/* ============================================================
   BAP-16 — dark hardware UI
   Design system: one light source (above), one spacing ladder,
   one type scale, one control material, one accent semantic.
   ============================================================ */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: block;
  src: url("../fonts/inter-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
    U+2190-21BB, U+2212, U+2215, U+2669-266F;
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: block;
  src: url("../fonts/jetbrains-mono-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2190-21BB, U+2212;
}

/* The two optional pad legend faces. Only one is ever drawn with, so the
   other never leaves the server: a @font-face is only fetched once something
   on the page is actually set in it. */
@font-face {
  font-family: "Orbitron";
  font-style: normal;
  font-weight: 400 900;
  font-display: block;
  src: url("../fonts/orbitron-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2212;
}

@font-face {
  font-family: "Playfair Display";
  font-style: italic;
  font-weight: 400 900;
  font-display: block;
  src: url("../fonts/playfair-italic-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2212;
}

:root {
  /* ---- neutral ramp: even ~3.4 L* steps, cool cast ---- */
  --bg: #0b0d10;
  --panel: #141619;
  --panel-2: #1a1c1f;
  --line-soft: #212326;
  --pad-face: #282a2d;
  --line: #303235;
  --pad-face-hi: #37393c;
  --pad-edge: #08090b;
  --well: #0c0e11;

  /* ---- ink: every value clears 3.7:1 on its own ground ---- */
  --ink: #e8e6e1;
  --ink-dim: #a2a6ac;
  --ink-off: #83878f;
  --ink-faint: #7b7f85;   /* 4.50:1 on --panel */

  /* ---- accent + status ---- */
  --accent: #ff5a00;
  --accent-soft: #ff5a0033;
  --amber: #ffb454;
  --green: #7dd069;
  --red: #ff3b30;
  --teal: #3fd6b5;
  --violet: #c07aff;

  /* ---- LCD phosphor: one hue, descending lightness ---- */
  --lcd-hi: #a8e08c;
  --lcd-mid: #7fb26a;
  --lcd-lo: #5e8a4f;
  --lcd-dim: #3f6a35;

  /* ---- pad legends: engraved, never pure white ---- */
  --legend: #b3b0a8;
  --legend-dim: #8d9197;  /* 4.54:1 on --pad-face */
  --engrave: 0 -1px 1px rgba(0, 0, 0, 0.8), 0 1px 0 rgba(255, 255, 255, 0.07);
  --engrave-soft: 0 -1px 1px rgba(0, 0, 0, 0.6), 0 1px 0 rgba(255, 255, 255, 0.05);

  /* ---- spacing ladder (4px base) ---- */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;

  /* ---- type scale ---- */
  --fs-1: 9px;    /* Inter micro legend */
  --fs-2: 10px;   /* shared workhorse; mono-crisp (6.0px cell) */
  --fs-3: 11px;
  --fs-4: 12px;   /* pad names, help body */
  --fs-5: 15px;   /* LCD display; mono-crisp (9.0px cell) */

  /* ---- tracking: inverse to size ---- */
  --tr-title: 0.2em;
  --tr-09: 0.16em;
  --tr-10: 0.14em;
  --tr-11: 0.11em;
  --tr-12: 0.06em;
  --tr-15: 0.02em;
  --tr-num: 0;

  /* ---- radii: concentric (chassis 28 − 20 pad = 8… stepped) ---- */
  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-pad: 10px;
  --r-chassis: 28px;
  --radius: var(--r-pad);

  /* ---- control geometry ---- */
  --ctl-h-s: 20px;
  --ctl-h-m: 28px;
  --ctl-h-l: 40px;

  /* ---- motion ---- */
  --e-std: cubic-bezier(0.4, 0, 0.2, 1);
  --e-out: cubic-bezier(0.22, 1, 0.36, 1);
  --d-tint: 90ms;
  --d-press: 30ms;
  --d-release: 170ms;

  /* ---- control material: lit from above ---- */
  --ctl-bg: linear-gradient(180deg, #24262a 0%, #1c1e22 52%, #17191c 100%);
  --ctl-bg-hover: linear-gradient(180deg, #2b2e33 0%, #212429 52%, #1b1d21 100%);
  --ctl-bg-press: linear-gradient(180deg, #141619 0%, #1a1c20 100%);
  --ctl-edge: #08090b;
  --ctl-shadow:
    inset 0 1px 0 #ffffff16,
    inset 0 -1px 0 #ffffff06,
    inset 0 -4px 6px -4px #00000055,
    0 1px 2px #00000077,
    0 2px 5px -1px #00000055;
  --ctl-shadow-press:
    inset 0 2px 5px #000000aa,
    inset 0 -1px 0 #ffffff07;

  --font-ui: "Inter", "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "SF Mono", ui-monospace, Menlo, monospace;

  /* Fractal grain for moulded/anodised surfaces. Raw feTurbulence is unusable
     here: it carries random ALPHA, so most of the tile is transparent and an
     overlay blend of it against near-black moves the surface by well under one
     level in 255 — invisible on a UI this dark. So the filter flattens alpha to
     1, desaturates (colour speckle would tint the greys), and stretches the
     greys around 0.5, which is overlay's neutral point. Grain then reads at any
     strength the opacity asks for, and lightens/darkens in equal measure
     instead of just washing the surface out. */
  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='2.2' intercept='-0.6'/%3E%3CfeFuncG type='linear' slope='2.2' intercept='-0.6'/%3E%3CfeFuncB type='linear' slope='2.2' intercept='-0.6'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");

  /* Dashed "something goes in here" stitching for the drop wells, drawn as
     an SVG layer: a CSS dashed border can neither thicken past its hairline
     pattern nor open the gaps between dashes. The rect's stroke rides the
     viewport edge, so half of the 4px stroke is clipped and 2px shows; the
     8/9 dasharray is what gives the stitches their air. One colour per
     state, since url() cannot read a var() for its stroke. */
  --dash-line: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%23303235' stroke-width='4' stroke-dasharray='8 9'/%3E%3C/svg%3E");
  --dash-bright: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%237b7f85' stroke-width='4' stroke-dasharray='8 9'/%3E%3C/svg%3E");
  --dash-accent: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='%23ff5a00' stroke-width='4' stroke-dasharray='8 9'/%3E%3C/svg%3E");
  /* The same seam at button scale: one drop well is the size of eight of
     these, so the big well's stitch would put four dashes on a side. Finer
     thread (1.5px shown), and more of them. */
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* body must be at LEAST the viewport (the backdrop fills the screen on a short
   page) but is never capped by it. As `height`, it gave the column flex box a
   definite main size, and a flex item shrinks to fit one: the machine was
   squeezed to exactly 100vh while its panels overflowed it, so everything
   past the first screenful painted outside the chassis and the article below
   showed through the gaps between panels. */
html { height: 100%; }
body { min-height: 100%; }

/* Belt and braces on the same failure, because it is invisible on a desktop
   and disfiguring on a phone. Whatever a browser decides the page column's
   height is, neither the machine nor the article may be squeezed into it: a
   shrunk flex item keeps its box but not its contents, and the panels then
   spill out of the chassis and paint over the prose. Both take their height
   from what is in them, so the article always begins after the whole
   instrument. */
body > * { flex: 0 0 auto; }

body {
  background: var(--bg);
  background-image:
    radial-gradient(1500px 760px at 50% -22%, #101318 0%, #0d0f12 44%, transparent 66%),
    radial-gradient(circle at 1px 1px, #ffffff0f 0.6px, transparent 1.6px);
  background-size: auto, 26px 26px;
  color: var(--ink);
  font-family: var(--font-ui);
  /* only features this subset actually carries */
  font-feature-settings: "calt" 1;
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--s4);
  -webkit-user-select: none;
  user-select: none;
}

/* numerals never jitter */
.lcd, .mono-val, #bpm, .pad-num, .step-label-num, .pad-edit-name,
.lcd-pos, .sample-bpm, .slice-ghost, #slice-count, .pad-name {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
}

/* silkscreen has no leading: flush micro-type to its em box */
.panel-title, .help-grid h3, .lcd-status, .lcd-pos, #lcd-kit, #lcd-msg,
.pad-num, .pad-key, .pad-name, .opt label, .t-lab, .tog, .mini-btn, .mic-btn,
.seg button, .step-btn, #bpm, .mono-val, .step-label, .step-rename, .steps-hint,
.song-lane-label, .song-colnum, .choke-group label, .pe-layer-name,
.sample-bpm, .slice-ghost, .drop-main, .drop-sub, .chip-btn, .ms-btn, .pad-edit-name {
  line-height: 1;
}

/* ============ CHASSIS ============ */

#machine {
  width: min(1180px, 100%);
  container-type: inline-size;
  container-name: machine;
  position: relative;
  isolation: isolate;
  background: linear-gradient(180deg, #26282c 0%, #1e2024 6%, #1a1c20 52%, #131518 100%);
  border: 1px solid #07080a;
  border-radius: var(--r-chassis);
  box-shadow:
    inset 0 1px 0 #ffffff1c,
    inset 0 0 0 1px #ffffff05,
    inset 0 -2px 0 #00000099,
    0 1px 0 #ffffff0c,
    0 40px 90px -18px #000000d0,
    0 10px 26px -10px #000000aa;
  padding: var(--s5);
}

/* Anodised grain over the whole enclosure. One noise token serves every
   surface, always at its intrinsic 140px tile — scaling the SVG resamples it
   and smooths the very detail that makes it grain — so the materials are
   separated by STRENGTH instead: chassis here, softer in the milled pockets,
   strongest on the rubber a finger actually lands on.

   Every grain layer sits BEHIND its surface's contents (negative z-index
   inside an isolated parent), never as a film over the whole machine. Grain
   painted over the top belongs to the screen rather than to the object: it
   stays put while a pad travels down under a press, and the surface appears
   to swim beneath its own texture. Behind the contents, each control keeps
   only the grain of its own face, and that grain moves with it. */
#machine::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  opacity: 0.11;
  mix-blend-mode: overlay;
  background-image: var(--noise);
  background-size: 140px 140px;
}

/* ============ TOP BAR ============ */

#topbar {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr);
  gap: var(--s5);
  align-items: stretch;
  margin-bottom: var(--s5);
}

/* ---- LCD: a real backlit panel behind glass ---- */
.lcd {
  position: relative;
  z-index: 4;
  background: linear-gradient(180deg, #101c0e 0%, #0d170c 55%, #0a120a 100%);
  border: 1px solid #04060a;
  border-radius: var(--r-md);
  box-shadow:
    inset 0 0 0 1px #ffffff07,
    inset 0 2px 14px #000000cc,
    inset 0 0 44px #4f8f3f1c,
    0 1px 0 #ffffff0c,
    0 0 28px -8px #7dd06926;
  padding: var(--s3) var(--s4) 14px;
  font-family: var(--font-mono);
  color: var(--lcd-hi);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-width: 0;
  overflow: hidden;
}
/* subpixel matrix */
.lcd::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  mix-blend-mode: multiply;
  background:
    repeating-linear-gradient(90deg, #00000040 0 1px, #0000 1px 3px),
    repeating-linear-gradient(0deg, #00000026 0 1px, #0000 1px 3px);
}
/* glass sheen */
.lcd::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(104deg, #ffffff0d 0%, #ffffff04 20%, #0000 44%);
}
.lcd > * { position: relative; z-index: 1; }

.lcd-row1, .lcd-row2 {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s3);
}
#lcd-kit {
  font-size: var(--fs-5);
  font-weight: 700;
  letter-spacing: var(--tr-15);
  color: var(--lcd-hi);
  text-shadow: 0 0 10px #a8e08c55, 0 0 2px #a8e08c99;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  cursor: text; /* it is the project's name, and you can type over it */
  border-radius: 3px;
  padding: 1px 3px;
  margin-left: -3px;
  transition: background var(--d-tint) var(--e-std);
}
#lcd-kit:hover { background: #a8e08c14; }

/* typing over the project name: the field wears the phosphor, not the
   machine's usual well, so the LCD never looks like it grew a control */
.lcd-rename {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-5);
  font-weight: 700;
  letter-spacing: var(--tr-15);
  line-height: 1;
  color: var(--lcd-hi);
  background: #a8e08c14;
  border: 1px solid #a8e08c66;
  border-radius: 3px;
  padding: 1px 3px;
  margin-left: -3px;
  outline: none;
}
.lcd-status {
  font-size: var(--fs-2);
  letter-spacing: var(--tr-10);
  margin-right: calc(-1 * var(--tr-10)); /* cancel trailing tracking on the right rail */
  color: var(--lcd-lo);
  text-shadow: 0 0 6px #5e8a4f44;
  white-space: nowrap;
}
.lcd-status.rec { color: #ff6a5e; animation: blink 1s steps(2) infinite; }
#lcd-msg {
  font-size: var(--fs-3);
  color: var(--lcd-mid);
  text-shadow: 0 0 6px #7fb26a44;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lcd-pos {
  font-size: var(--fs-3);
  color: var(--lcd-lo);
  letter-spacing: var(--tr-num);
  text-shadow: 0 0 6px #5e8a4f44;
  white-space: nowrap;
}
.lcd-meter {
  position: relative;
  height: 4px;
  background: #0f1c0c;
  border-radius: 2px;
  margin-top: var(--s2);
  overflow: hidden;
  box-shadow: inset 0 1px 2px #000000aa;
}
/* The ramp spans the whole TRACK and the fill reveals it by clipping, so a
   level walks green -> amber -> red. (Sizing the gradient to the fill would
   mean a quiet signal painted the entire ramp into a few pixels.) */
#lcd-meter-fill {
  position: absolute;
  inset: 0;
  width: 100%;
  background: linear-gradient(90deg, #4f8f3f 0%, #8fd07a 62%, #a8e08c 82%, #ffd24a 92%, #ff6a5e 100%);
  clip-path: inset(0 calc(100% - var(--lvl, 0%)) 0 0);
  transition: clip-path 260ms var(--e-std);
}
#lcd-meter-fill.rising { transition-duration: 0ms; }
.lcd-meter::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(90deg, transparent 0 calc(12.5% - 1px), #0a0f0a99 calc(12.5% - 1px) 12.5%);
}
@keyframes blink { 50% { opacity: 0.35; } }

.top-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s3);
  padding-right: 17px; /* keep the LED off the chassis edge */
}

.power-led {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 33%, #f0ffe9 0%, #abec95 28%, #7dd069 62%, #3d7830 100%);
  box-shadow: 0 0 5px #7dd069cc, 0 0 13px #7dd06955, inset 0 0 2px #ffffffa0, 0 0 0 1px #00000088;
}

/* ============ LAYOUT ============ */

#deck {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr);
  gap: var(--s5);
  /* the pad module sizes to its own content; stretching it meant the right
     column's height (which changes when the sampler opens) punched a hole
     between the pads and the perform bar */
  align-items: start;
}

#control-zone {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  min-width: 0;
}

/* ============ PANELS ============ */

.panel, #pad-zone {
  position: relative;
  isolation: isolate; /* keeps the grain layer above this pocket's own background */
  background: linear-gradient(180deg, #15171a 0%, #111316 100%);
  border: 1px solid #0b0c0f;
  border-radius: var(--r-lg);
  /* a pocket milled into the chassis: light from above falls INTO it, so the
     top inner edge is shadowed and the bottom inner edge catches the bounce.
     A top highlight here would read as a raised slab instead. */
  box-shadow:
    inset 0 2px 5px -2px #000000cc,
    inset 0 10px 22px -16px #000000e0,
    inset 0 -1px 0 #ffffff0c,
    0 1px 0 #ffffff0a,
    0 2px 6px -2px #00000066;
  padding: var(--s4);
}

/* A panel jumped to from somewhere else pulses once on arrival, so the
   answer is found rather than hunted for. The ring rides a ::before above
   the pocket's own inset shading — as a box-shadow on the panel itself it
   would have to restate the whole milled stack to animate. */
.panel.attn::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px var(--accent), 0 0 22px -4px #ff5a0066;
  animation: panel-attn 1s var(--e-out) forwards;
}
@keyframes panel-attn {
  0% { opacity: 0; }
  30% { opacity: 1; }
  100% { opacity: 0; }
}

/* the pockets are moulded too, on a finer grain than the chassis around
   them — without it a panel reads as a hole cut in the enclosure rather
   than a separate piece seated into it */
.panel::after, #pad-zone::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  opacity: 0.08;
  mix-blend-mode: overlay;
  background-image: var(--noise);
  background-size: 140px 140px;
}

#pad-zone {
  display: flex;
  flex-direction: column;
  /* 12px matches .panel-head's margin in every other panel, so the pad grid
     and the transport start at the same y instead of 16px apart */
  gap: var(--s3);
  justify-content: flex-start;
}
#pad-zone > .panel-head { margin-bottom: 0; }

.panel-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--s3);
}
.panel-title {
  font-size: var(--fs-2);
  font-weight: 700;
  letter-spacing: var(--tr-title);
  color: var(--ink-off);
  line-height: 12px;
}
.pad-edit-name {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  font-weight: 500;
  letter-spacing: var(--tr-num);
  color: var(--accent);
  margin-left: var(--s2);
}
#pattern-letter { color: var(--amber); }
#bank-letter { color: var(--accent); }

/* ============ PADS ============ */

#pad-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(4, 1fr);
  gap: var(--s3);
  /* rigid module: the bezel breathes, the pad block does not */
  flex: 0 0 auto;
  width: 100%;
  aspect-ratio: 1 / 1;
}

.pad {
  position: relative;
  --pad-glow: var(--accent);
  border-radius: var(--r-pad);
  border: 1px solid var(--pad-edge);
  /* domed silicone: point-lit from above, bounce along the bottom lip */
  background:
    radial-gradient(125% 95% at 50% 4%, var(--pad-face-hi) 0%, #2e3137 38%, #0000 70%),
    linear-gradient(180deg, #2e3137 0%, var(--pad-face) 55%, #1d1f23 100%);
  box-shadow:
    inset 0 1px 0 #ffffff1e,
    inset 0 -1px 0 #ffffff0a,
    inset 0 6px 12px -10px #000000c0,
    inset 0 -7px 11px -6px #00000066,
    0 1px 1px #000000a0,
    0 5px 11px -2px #00000077,
    0 14px 26px -10px #00000055;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: var(--s3);
  transition:
    transform var(--d-release) var(--e-out),
    box-shadow var(--d-release) var(--e-out),
    border-color var(--d-tint) var(--e-std);
  touch-action: none;
  overflow: hidden;
}

/* moulded-rubber micro-texture — the tightest grain on the machine, because
   a rubber pad is the one surface your eye gets close to */
.pad::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-image: var(--noise);
  /* intrinsic tile size: scaling the SVG resamples it and softens the grain */
  background-size: 140px 140px;
  opacity: 0.15;
  mix-blend-mode: overlay;
  pointer-events: none;
}

/* hover: the light catches the whole dome a little more, not just the keycap.
   A sheen laid over the face rather than a new background, so it works the
   same on a grey pad and a saturated one, and leaves the strike and select
   states - which own the box-shadow outright - completely alone. */
.pad::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(125% 95% at 50% 0%, #ffffff33 0%, #ffffff1a 45%, #ffffff08 78%);
  /* the rim catches it too - a ring drawn on this layer, so the strike and
     select states keep their own box-shadow untouched */
  box-shadow: inset 0 0 0 1px #ffffff26;
  opacity: 0;
  transition: opacity var(--d-tint) var(--e-std);
  pointer-events: none;
}
.pad:hover::before { opacity: 1; }

.pad:active, .pad.hit {
  transform: translateY(2px) scale(0.985);
  transition-duration: var(--d-press);
}

/* selected for editing — persistent, distinct from the strike */
.pad.sel {
  border-color: #ff5a0066;
  box-shadow:
    inset 0 1px 0 #ffffff1e,
    inset 0 -1px 0 #ffffff0a,
    inset 0 6px 12px -10px #000000c0,
    inset 0 0 0 1px #ff5a0033,
    0 0 14px -6px #ff5a0066,
    0 5px 11px -2px #00000077;
}
.pad.sel .pad-num { color: var(--accent); text-shadow: none; }

/* an empty pad is the same moulded object, just unlit — keep the dome and
   only take away the finish, never the physics */
/* Struck: the light is edge-lit. The rim itself is the emitter and the
   glow falls inward from it, so the pad reads as a ring lighting up
   rather than a lamp switched on behind the middle of the face. */
.pad.hit {
  outline: none;
  border-color: color-mix(in oklab, var(--pad-glow) calc(45% + var(--hv, 1) * 55%), var(--pad-edge));
  /* the face keeps its own dome and grain — a struck pad is the same
     object, lit from its rim, not a different surface */
  box-shadow:
    /* the ring, at full strength */
    inset 0 0 0 1px color-mix(in oklab, var(--pad-glow) calc(55% + var(--hv, 1) * 45%), transparent),
    /* its light bleeding inward off the rim, never reaching the centre */
    inset 0 0 calc(6px + var(--hv, 1) * 10px) calc(-2px) color-mix(in oklab, var(--pad-glow) calc(30% + var(--hv, 1) * 45%), transparent),
    inset 0 1px 0 #ffffff1e,
    /* and spilling outward onto the bezel */
    0 0 0 1px color-mix(in oklab, var(--pad-glow) 30%, transparent),
    0 0 calc(14px + var(--hv, 1) * 16px) calc(-6px) color-mix(in oklab, var(--pad-glow) calc(40% + var(--hv, 1) * 45%), transparent),
    0 1px 4px #000000aa;
}

/* ---- a coloured pad ----
   The face is rebuilt from the chosen colour rather than tinted over the
   grey one, so the dome, the rim light and the strike glow all belong to the
   same object. --pc-ink is picked in JS by luminance: the label has to stay
   legible on a lemon-yellow pad as well as a navy one. */
.pad.tinted {
  --pad-glow: var(--pc);
  /* The legend is cut into the pad, so the groove's walls are the pad's own
     colour rather than absolute black and white, which would only muddy a
     saturated face. Mixing from --pc carves lemon-yellow and navy alike.

     The weighting is what sells the direction, and it is not symmetric: the
     shadow under the top lip does the work and the lit bottom wall is barely
     there — the same ratio --engrave uses on the grey pads (80% black above,
     7% white below). Balanced the other way, a bright line under a glyph
     reads as the lit edge of something sitting ON the pad, and the whole
     legend floats instead of sinking. */
  --pc-engrave:
    0 -1px 1px color-mix(in oklab, var(--pc) 45%, #000),
    0  1px 0   color-mix(in oklab, var(--pc) 85%, #fff);
  border-color: color-mix(in oklab, var(--pc) 30%, #08090b);
  background:
    radial-gradient(125% 95% at 50% 4%,
      color-mix(in oklab, var(--pc) 72%, #fff) 0%,
      color-mix(in oklab, var(--pc) 92%, #fff) 38%,
      #0000 70%),
    linear-gradient(180deg,
      color-mix(in oklab, var(--pc) 86%, #fff) 0%,
      var(--pc) 55%,
      color-mix(in oklab, var(--pc) 72%, #000) 100%);
}
/* Undiluted ink reads as something printed ON the pad. What you see in a
   groove is the pad's own material in shadow, so bend the ink a fifth of the
   way back toward the face — enough to belong to it. Every colour a kit
   ships stays between 5:1 and 10.6:1 against its own face; only a mid-tone
   picked by hand near inkFor()'s crossover drops toward 3.4:1, where the
   pad's own light-to-dark dome is already the limiting factor. */
.pad.tinted {
  --pc-ink-base: color-mix(in oklab, var(--pc-ink) 80%, var(--pc));
}
.pad.tinted .pad-key {
  color: var(--pc-ink-base);
  text-shadow: var(--pc-engrave);
}
/* The bevel belongs INSIDE the glyph. A shadow above the letters is a shadow
   cast onto the pad — it reads as something hovering over the face, which is
   the opposite of engraved. What a groove actually shows is the glyph's own
   body turning through the light: dark where the wall faces away at the top
   left, coming back up toward the pad's colour at the bottom right. A
   text-shadow can only ever paint behind a glyph, never within one, so the
   bevel has to be the fill itself — a gradient clipped to the text. */
.pad.tinted .pad-name,
.pad.tinted .pad-num {
  background-image: linear-gradient(146deg,
    color-mix(in oklab, var(--pc-ink-base) 58%, #000) 0%,
    color-mix(in oklab, var(--pc-ink-base) 84%, #000) 26%,
    var(--pc-ink-base) 54%,
    color-mix(in oklab, var(--pc-ink-base) 72%, var(--pc)) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  /* a shadow would show straight through the transparent fill */
  text-shadow: none;
}
.pad.tinted .pad-key { border-color: color-mix(in oklab, var(--pc-ink) 22%, transparent); }
/* the lens is tinted even at rest, so a glance at the dark pad still tells you
   which colour it will flash; struck, it burns to a white-hot core of its own
   colour exactly as the orange one does */
.pad.tinted .pad-led {
  background: radial-gradient(circle at 50% 30%,
    color-mix(in oklab, var(--pc) 55%, #303339) 0%,
    color-mix(in oklab, var(--pc) 40%, #212429) 55%,
    color-mix(in oklab, var(--pc) 25%, #131518) 100%);
}
.pad.tinted.hit .pad-led {
  background: radial-gradient(circle at 50% 33%,
    #fff 0%,
    color-mix(in oklab, var(--pc) 55%, #fff) 30%,
    var(--pc) 66%,
    color-mix(in oklab, var(--pc) 70%, #000) 100%);
  box-shadow:
    0 0 calc(3px + var(--hv, 1) * 8px) color-mix(in oklab, var(--pc) 82%, transparent),
    0 0 calc(8px + var(--hv, 1) * 12px) color-mix(in oklab, var(--pc) 40%, transparent),
    inset 0 0 2px #ffffffb0;
}
/* a strike lights the rim, not the face — the legend stays cut into it */
/* A strike lights the rim, not the face. The legend's fill is clipped to the
   glyph, so a text-shadow behind it — the accent flare .pad.hit adds — has
   nothing opaque to hide under and flares straight THROUGH the letterforms:
   the name buzzes and lifts off the pad on every hit. The engraving holds
   still and the rim does the talking. */
.pad.tinted.hit .pad-name,
.pad.tinted.hit .pad-num { text-shadow: none; }
.pad.tinted.sel .pad-num { color: var(--pc-ink); }


.pad.empty {
  background:
    radial-gradient(125% 95% at 50% 4%, #2b2e33 0%, #232529 38%, #0000 70%),
    linear-gradient(180deg, #232529 0%, #1c1e22 55%, #17191c 100%);
  border-color: #0a0b0e;
  box-shadow:
    inset 0 1px 0 #ffffff10,
    inset 0 -1px 0 #ffffff06,
    inset 0 6px 12px -10px #000000c0,
    0 1px 1px #00000088,
    0 3px 7px -2px #00000055;
}
.pad.empty .pad-name { color: #7a7e85; text-shadow: var(--engrave-soft); }

.pad-top { display: flex; justify-content: space-between; align-items: center; height: 14px; flex: 0 0 auto; }
.pad-num {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-num);
  color: var(--legend-dim);
  text-shadow: var(--engrave-soft);
}
.pad-led {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  /* a dead LED behind a lens is DARKER than the surface around it; only the
     top of the lens catches any light */
  background: radial-gradient(circle at 50% 30%, #303339 0%, #212429 55%, #131518 100%);
  box-shadow: inset 0 1px 1px #ffffff14, inset 0 -1px 1px #00000099, 0 0 0 1px #00000066;
  /* snap on, decay out */
  transition: background 220ms var(--e-out), box-shadow 220ms var(--e-out);
}
.pad.hit .pad-led {
  background: radial-gradient(circle at 50% 33%, #fff4ec 0%, #ffa864 30%, #ff5a00 66%, #b83800 100%);
  box-shadow:
    0 0 calc(3px + var(--hv, 1) * 8px) #ff5a00d0,
    0 0 calc(8px + var(--hv, 1) * 12px) #ff5a0066,
    inset 0 0 2px #ffffffb0;
  transition-duration: 0ms;
}

.pad-name {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* .pad is a <button>, and a button does not inherit the page font - without
     this the legend falls back to the UA's Arial, which is not the machine's
     face and is not what FILE > PREFS offers */
  font-family: var(--font-ui);
  font-size: var(--fs-4);
  font-weight: 600;
  letter-spacing: var(--tr-12);
  padding-left: var(--tr-12); /* cancel trailing tracking on a centred line */
  /* engraved into the rubber, lit from above */
  color: var(--legend);
  text-shadow: var(--engrave);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.pad.hit .pad-name {
  color: #efece5;
  /* a strike lights the legend, it does not lift it out of the rubber: the
     shadow stays above the glyph like every other engraving here. Below it,
     the name popped up off the face for as long as the pad was lit. */
  text-shadow: 0 0 10px #ff5a0055, var(--engrave);
}

/* ---- the pad legend face (FILE > PREFS) ----
   The sixteen names are the one legend on this machine allowed a voice of its
   own; the numbers, keycaps and panel titles stay silkscreened mono whatever
   is chosen here. The face rides on <html>, so switching it is one attribute
   rather than sixteen restyled nodes. Each of these carries its own size and
   tracking: a serif italic and a squared display face do not read at the same
   setting, and matching their sizes would only match one of them. */
:root[data-pad-font="robot"] .pad-name {
  font-family: "Orbitron", var(--font-mono);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.06em;
  padding-left: 0.06em;
  line-height: 1.25;
}
:root[data-pad-font="nameplate"] .pad-name {
  font-family: "Playfair Display", Georgia, serif;
  font-style: italic;
  font-weight: 600;
  font-size: 22px;
  letter-spacing: 0;
  padding-left: 0;
  line-height: 1.25;
}

.pad-key {
  align-self: flex-start;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 15px;
  margin-left: -5px; /* glyph aligns with .pad-num's left rail */
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  font-weight: 600;
  letter-spacing: var(--tr-num);
  color: var(--legend-dim);
  text-shadow: var(--engrave-soft);
  border: 1px solid #ffffff0a;
  border-radius: var(--r-sm);
  padding: 0 5px;
  background: #00000038;
  box-shadow: inset 0 1px 2px #00000066, 0 1px 0 #ffffff08;
  transition: color var(--d-tint) var(--e-std), border-color var(--d-tint) var(--e-std);
}
/* the keycap does not answer hover on its own - the whole face does */

.pad.drop-target {
  outline: 1px dashed var(--accent);
  outline-offset: -2px;
  border-color: #ff5a0066;
  box-shadow: inset 0 0 22px -6px #ff5a0055, 0 0 18px -6px #ff5a0055;
}

/* ============ PERFORM BAR ============ */

.perform-bar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
}
.perform-bar > .perf-repeat { margin-left: var(--s2); }
.perform-bar > #btn-full { margin-left: auto; }
.perf-label {
  font-size: var(--fs-1);
  font-weight: 700;
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
}
.perf-repeat { display: flex; gap: var(--s2); align-items: center; }
#repeat-seg button, #grid-seg button,
#mel-view button, #mel-scale button, #mel-voicing button { width: auto; padding: 0 7px; font-size: var(--fs-2); }
#mel-algo button { width: 30px; font-size: var(--fs-2); }
#repeat-seg.dim { opacity: 0.45; }

.bank-seg button { width: 26px; position: relative; }
.bank-seg button.filled::after,
.pattern-seg button.has-events::after {
  content: "";
  position: absolute;
  bottom: 3px;
  left: 50%;
  transform: translateX(-50%);
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--ink-faint);
}
.bank-seg button.on.filled::after { background: var(--accent); }
.pattern-seg button.on.has-events::after { background: var(--amber); }

/* ============ UNIFIED CONTROL MATERIAL ============ */

.t-btn, .tog, .mini-btn, .seg button, .chip-btn,
.ms-btn, .step-btn, .mic-btn, .stepper, .eject-btn, .swap-btn, .steps-play {
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--ink-off);
  background: var(--ctl-bg);
  border: 1px solid var(--ctl-edge);
  border-radius: var(--r-md);
  box-shadow: var(--ctl-shadow);
  cursor: pointer;
  transition:
    color var(--d-tint) var(--e-std),
    background var(--d-tint) var(--e-std),
    border-color var(--d-tint) var(--e-std),
    box-shadow var(--d-release) var(--e-out),
    transform var(--d-release) var(--e-out);
}

.t-btn:hover:not(:disabled), .tog:hover:not(:disabled), .mini-btn:hover:not(:disabled),
.seg button:hover:not(:disabled), .chip-btn:hover:not(:disabled),
.ms-btn:hover, .step-btn:hover, .mic-btn:hover:not(:disabled),
.eject-btn:hover:not(:disabled), .swap-btn:hover:not(:disabled), .steps-play:hover:not(:disabled) {
  color: var(--ink);
  background: var(--ctl-bg-hover);
}

.t-btn:active:not(:disabled), .tog:active:not(:disabled), .mini-btn:active:not(:disabled),
.seg button:active:not(:disabled), .chip-btn:active:not(:disabled),
.ms-btn:active, .step-btn:active, .mic-btn:active:not(:disabled),
.eject-btn:active:not(:disabled), .swap-btn:active:not(:disabled), .steps-play:active:not(:disabled) {
  transform: translateY(1px);
  background: var(--ctl-bg-press);
  box-shadow: var(--ctl-shadow-press);
  transition-duration: var(--d-press);
}

/* segments and steppers are one machined block, not stacked buttons */
.seg, .stepper {
  display: inline-flex;   /* hug the buttons: no empty tail inside the capsule */
  align-items: stretch;
  width: fit-content;
  height: var(--ctl-h-m);
  /* never overflow:hidden here — it would clip the children's focus rings */
  background: var(--ctl-bg);
  border: 1px solid var(--ctl-edge);
  border-radius: var(--r-md);
  box-shadow: var(--ctl-shadow);
  overflow: visible;
}
.seg button, .stepper .step-btn {
  background: none;
  border: none;
  box-shadow: none;
  border-radius: 0;
}
.stepper #bpm {
  background: none;
  border: none;
  border-radius: 0;
  /* seams, to match the inset dividers .seg draws between its chips */
  box-shadow: inset 1px 0 0 var(--line-soft), inset -1px 0 0 var(--line-soft);
}
.seg button:first-child { border-radius: var(--r-md) 0 0 var(--r-md); }
.seg button:last-child { border-radius: 0 var(--r-md) var(--r-md) 0; }
.stepper > :first-child { border-radius: var(--r-md) 0 0 var(--r-md); }
.stepper > :last-child { border-radius: 0 var(--r-md) var(--r-md) 0; }
.seg button:hover:not(.on), .stepper .step-btn:hover { background: #ffffff08; }
/* the capsule is one machined block: its chips darken on press, they never
   translate, or a lit chip would slide inside a frame that cannot move */

.seg button {
  position: relative;
  width: 30px;
  /* the block owns the height; the buttons fill it, so .seg matches .stepper
     and .tog exactly instead of standing 2px proud of them */
  height: calc(var(--ctl-h-m) - 2px);
  color: var(--ink-off);
  font-size: var(--fs-2);
}
/* inset dividers that vanish around a lit chip */
.seg button:not(:last-child)::before {
  content: "";
  position: absolute;
  right: 0;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--line-soft);
  transition: opacity var(--d-tint) var(--e-std);
}
.seg button.on::before, .seg button:has(+ .on)::before { opacity: 0; }

/* ---- ONE on-recipe: colour is the only variable ---- */
/* STEM SEP and EVOLVE do the most surprising things in the app and are the
   two controls nobody finds on their own, so they wear their own colours at
   rest - dressed to be tried, not lit to report a state. Placed before the
   .on line so a running split still takes the amber. */
.mini-btn.teal { --st: var(--teal); }
.mini-btn.violet { --st: var(--violet); }
.tog.on, .mini-btn.on { --st: var(--amber); }
.seg button.on, .chip-btn.on, .mini-btn.accent { --st: var(--accent); }
/* PATTERN and BANK are both A-B-C-D in an identical shell, so they must not
   share a lit colour: banks stay accent, patterns are amber like the
   PATTERN letter in the step-editor head. */
.pattern-seg button.on { --st: var(--amber); }
/* the letters carry as well as select: a press that travels clones the whole
   pattern onto the letter it lands on, so the browser may not take the
   gesture for a scroll before it becomes a drag */
.pattern-seg button, .song-lane-label[data-pat] { touch-action: none; }
/* a queued switch is armed, not active: outline it and pulse, don't fill it */
.pattern-seg button.queued {
  color: var(--amber);
  box-shadow: inset 0 0 0 1px #ffb45499;
  animation: blink 0.7s steps(2) infinite;
}
.t-btn.rec.armed, .t-btn.rec.recording, .mini-btn.confirm, .mic-btn.recording { --st: var(--red); }
.t-btn.playing { --st: var(--green); }

.tog.on, .mini-btn.on, .seg button.on, .chip-btn.on, .mini-btn.accent,
.mini-btn.teal, .mini-btn.violet,
.t-btn.rec.armed, .t-btn.rec.recording, .t-btn.playing, .mini-btn.confirm, .mic-btn.recording,
.steps-play.playing {
  color: color-mix(in oklab, var(--st) 62%, #fff);
  border-color: color-mix(in oklab, var(--st) 58%, transparent);
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--st) 26%, #1d1f23) 0%,
    color-mix(in oklab, var(--st) 14%, #16181b) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, var(--st) 30%, transparent),
    inset 0 0 13px color-mix(in oklab, var(--st) 18%, transparent),
    0 0 13px -3px color-mix(in oklab, var(--st) 55%, transparent);
  text-shadow: 0 0 7px color-mix(in oklab, var(--st) 60%, transparent);
}
/* Segments sit inside a machined block, so their lit state is a recessed
   backlight rather than a raised cap — same recipe, one extra inset. */
.seg button.on {
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, var(--st) 30%, transparent),
    inset 0 0 13px color-mix(in oklab, var(--st) 22%, transparent),
    inset 0 0 0 1px color-mix(in oklab, var(--st) 22%, transparent);
}
/* Lit controls need the same feedback as unlit ones: brighten on hover,
   sink on press. Without this every "on" control feels dead to the touch. */
.tog.on:hover, .mini-btn.on:hover, .seg button.on:hover, .chip-btn.on:hover,
.mini-btn.teal:hover:not(:disabled), .mini-btn.violet:hover:not(:disabled),
.mini-btn.accent:hover, .t-btn.playing:hover, .t-btn.rec.recording:hover,
.t-btn.rec.armed:hover, .mic-btn.recording:hover, .steps-play.playing:hover {
  color: color-mix(in oklab, var(--st) 35%, #fff);
  border-color: color-mix(in oklab, var(--st) 80%, transparent);
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--st) 34%, #1d1f23) 0%,
    color-mix(in oklab, var(--st) 20%, #16181b) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, var(--st) 38%, transparent),
    inset 0 0 15px color-mix(in oklab, var(--st) 24%, transparent),
    0 0 17px -3px color-mix(in oklab, var(--st) 68%, transparent);
}
.tog.on:active, .mini-btn.on:active, .seg button.on:active, .chip-btn.on:active,
.mini-btn.teal:active:not(:disabled), .mini-btn.violet:active:not(:disabled),
.mini-btn.accent:active, .t-btn.playing:active, .t-btn.rec:active, .mic-btn.recording:active {
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 6px #000000aa,
    inset 0 0 12px color-mix(in oklab, var(--st) 18%, transparent);
  transition-duration: var(--d-press);
}

/* chips darken in place: the capsule around them cannot move, so a chip
   that translates would slide past its own frame */
.seg button:active:not(:disabled),
.seg button.on:active:not(:disabled),
.stepper .step-btn:active:not(:disabled) {
  transform: none;
  background: #00000055;
  box-shadow: inset 0 2px 5px #000000aa;
}

.mini-btn.confirm { animation: blink 0.7s steps(2) infinite; }

/* drawn icons (undo / redo) — the glyphs are not in the shipped subset */
.icon-btn { display: inline-grid; place-items: center; padding: 0; width: 30px; }
.icon-btn svg { width: 15px; height: 15px; display: block; }
.icon-btn svg path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.head-right { display: flex; align-items: center; gap: var(--s2); }

/* mirrors the LCD line when the top bar is hidden */
.status-compact {
  display: none;
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-10);
  line-height: 1.35;
  color: var(--lcd-mid);
  padding: var(--s2) 10px;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, #101c0e 0%, #0a120a 100%);
  border: 1px solid #04060a;
  box-shadow: inset 0 1px 6px #000000aa, inset 0 0 22px #4f8f3f14;
  text-shadow: 0 0 6px #7fb26a44;
}
/* this only exists when the top bar is gone */
#btn-help-compact { display: none; width: 24px; height: 24px; font-size: var(--fs-2); }
/* The LCD's big line names the PROJECT, so the kit is named here instead -
   at every width, or a desktop player would never learn what is loaded. */
.kit-name-compact {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-10);
  color: var(--lcd-mid);
}
.t-btn.rec.recording .ico-rec { box-shadow: 0 0 10px var(--red); animation: blink 1s steps(2) infinite; }

/* ============ TRANSPORT ============ */

.transport {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s2);
  margin-bottom: var(--s4);
}
/* DUB/TAKE: a vertical record-mode switch welded onto REC — one machined
   block with a single seam line between them, no gap. DUB layers every
   pass; TAKE keeps only the latest pass on the pads being played. */
.rec-block {
  display: flex;
  align-items: stretch;
}
.rec-block .t-btn {
  flex: 1;
  border-radius: var(--r-md) 0 0 var(--r-md);
  border-right: none; /* the seg's left border is the seam */
}
/* Doubled class on purpose: this switch takes its height from the block it is
   welded to, and the coarse-pointer rules below give every other .seg a fixed
   34px. At plain .seg specificity that fixed height won on touch, leaving the
   switch standing 6px short of REC with its two chips squeezed to 16px. */
.seg.recmode-seg {
  flex-direction: column;
  height: auto;
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
.seg.recmode-seg button {
  flex: 1;
  width: auto;
  height: auto;
  padding: 0 9px;
  font-size: var(--fs-1);
}
.recmode-seg button:first-child { border-radius: 0 var(--r-md) 0 0; }
.recmode-seg button:last-child { border-radius: 0 0 var(--r-md) 0; }
/* the divider runs horizontally here, not down the right edge */
.recmode-seg button:not(:last-child)::before {
  right: 6px;
  left: 6px;
  top: auto;
  bottom: 0;
  width: auto;
  height: 1px;
}
.t-btn {
  padding: 8px 4px 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: var(--ctl-h-l);
}
.t-btn .t-lab {
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  padding-left: var(--tr-09); /* re-centre against trailing tracking */
  flex: 0 0 auto;
}
/* every icon occupies the same 13px box and none of them may be squashed by
   the flex column, so all three labels sit on one baseline */
.t-ico { width: 13px; height: 13px; display: block; flex: 0 0 13px; }
.ico-rec {
  width: 10px; height: 10px;
  margin: 1.5px auto;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}

/* PLAY is the primary action — it should not look like its neighbours */
/* class-scoped so the shared lit recipe still wins when PLAY is playing */
.t-btn#btn-play:not(.playing) { color: var(--ink-dim); }
#btn-play .ico-play { transform: translateX(1.83px) scale(1.08); transform-origin: center; }
.t-btn#btn-play:not(.playing):hover { color: var(--green); border-color: #7dd06955; }
#btn-rec:not(.recording):not(.armed):hover { color: var(--red); border-color: #ff3b3055; }
/* REC and DUB/TAKE are one welded control, so REC's hover ring and its
   recording glow must wrap the whole block, not stop at the seam */
.rec-block:has(#btn-rec:not(.recording):not(.armed):hover) .recmode-seg {
  border-color: #ff3b3055;
}
.rec-block:has(.rec.recording) .recmode-seg,
.rec-block:has(.rec.armed) .recmode-seg {
  border-color: color-mix(in oklab, var(--red) 58%, transparent);
  box-shadow: 0 0 13px -3px color-mix(in oklab, var(--red) 55%, transparent);
}
.ico-play {
  width: 0; height: 0;
  margin: 0 auto;
  border-left: 11px solid currentColor;
  border-top: 6.5px solid transparent;
  border-bottom: 6.5px solid transparent;
  background: none;
  transform: translateX(1.83px); /* optical centre = centroid, not bounding box */
}
.ico-clear { position: relative; }
.ico-clear::before, .ico-clear::after {
  content: "";
  position: absolute;
  left: 5.75px;
  top: 0.5px;
  width: 1.5px;
  height: 12px;
  background: currentColor;
  border-radius: 1px;
}
.ico-clear::before { transform: rotate(45deg); }
.ico-clear::after { transform: rotate(-45deg); }

/* ============ CONTROL ROWS ============ */

.loop-opts {
  display: grid;
  /* capsules hug their content now, so let the tracks follow them: the
     three labelled groups then sit on one even gutter */
  grid-template-columns: auto auto minmax(0, 1fr);
  gap: var(--s4) var(--s4);
  align-items: end;
}
/* row 2 needs its own tracks: QUANTIZE (with its strength stepper) is wide,
   so it takes a full line and the toggles get the next one */
.loop-opts.row2 {
  margin-top: var(--s4);
  grid-template-columns: minmax(0, 1fr) auto;
}
.loop-opts.row2 > .opt:first-child { grid-column: 1 / -1; }

.opt { display: flex; flex-direction: column; gap: 7px; min-width: 0; align-items: flex-start; }
/* capsules hug their buttons instead of stretching to the track width */
.opt > .seg, .opt > .stepper { align-self: flex-start; }
.opt label, .opt > label {
  font-size: var(--fs-1);
  font-weight: 700;
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
}

.stepper { height: var(--ctl-h-m); }
.step-btn {
  width: 26px;
  height: calc(var(--ctl-h-m) - 2px);
  color: var(--ink-off);
  font-size: 0;
  position: relative;
}
.step-btn::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 9px; height: 1.5px;
  background: currentColor;
  border-radius: 1px;
  transform: translate(-50%, -50%);
}
/* the bar across is drawn for every step button; .plus adds the upright, so a
   new stepper gets a real + without another id being wired in here */
.step-btn.plus::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 1.5px; height: 9px;
  background: currentColor;
  border-radius: 1px;
  transform: translate(-50%, -50%);
}
.step-btn:hover { color: var(--accent); }
#bpm {
  width: 44px;
  height: calc(var(--ctl-h-m) - 2px);
  /* seams live in the .stepper #bpm rule below: at (1,0,0) this selector
     loses to the shared reset, so setting them here would be dead code */
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--fs-3);
  letter-spacing: var(--tr-num);
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}
#bpm::-webkit-inner-spin-button { display: none; }
#bpm:focus { outline: none; }

.quant-row { display: flex; gap: var(--s2); align-items: center; min-width: 0; }

.toggles { flex-direction: row; align-items: center; gap: 6px; flex-wrap: wrap; }
.tog {
  height: var(--ctl-h-m);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  padding: 0 10px 0 calc(10px + var(--tr-09));
}
#btn-undo, #btn-redo { font-size: var(--fs-3); padding: 0 8px; letter-spacing: 0; }
/* strength rides the QUANTIZE row it acts on, pushed to the row's far end -
   whole tens on a stepper, not a slider squeezed into leftover width */
.str-lab {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  font-weight: 700;
  letter-spacing: var(--tr-09);
  line-height: 1;
  color: var(--ink-faint);
}
.str-val { min-width: 26px; } /* room for the three digits of 100 */

/* loop progress: a recessed well */
.loop-bar {
  position: relative;
  /* tall enough for the coloured miniature to actually read as colour */
  height: 12px;
  border-radius: var(--r-sm);
  background: var(--well);
  border: 1px solid #05070a;
  box-shadow: inset 0 1px 3px #000000aa, inset 0 -1px 0 #ffffff0a, 0 1px 0 #ffffff09;
  margin-top: var(--s4);
  overflow: hidden;
}
/* glass over the whole strip: a highlight across the top, a shade at the
   bottom. It lifts the marks off the well without washing their colour out */
.loop-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  background: linear-gradient(180deg, #ffffff26 0%, #ffffff0d 42%, #ffffff00 62%, #0000002e 100%);
}
#loop-progress {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: linear-gradient(90deg, #ff5a0088, var(--accent));
  box-shadow: 0 0 10px -2px #ff5a0088;
}
/* miniature of the pattern: one mark per hit, above the fill so the playhead
   sweeps under it and lit hits read against the orange */
#loop-notes { position: absolute; inset: 0; pointer-events: none; }
#loop-notes i {
  position: absolute;
  top: 1px;
  bottom: 1px;
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--ink);
  mix-blend-mode: overlay;
  z-index: 1;
}
/* the footprint of a sample that keeps sounding past its hit: an orange
   smear under the marks, dying off the way the sound does */
#loop-notes i.tail {
  margin-left: 0;
  background: linear-gradient(90deg, #ffb45466, #ffb45408);
  mix-blend-mode: normal;
  z-index: 0;
}
/* beat lines, with the bar lines standing clear of them - z-index puts the
   grid above the coloured marks so the ruler stays readable over a busy bar */
#loop-ticks { position: absolute; inset: 0; display: flex; z-index: 2; pointer-events: none; }
#loop-ticks span { flex: 1; border-right: 1px solid #ffffff26; }
/* a bar line has to survive landing on a bright mark, so it carries its own
   dark edge and reads against colour as well as against the empty well */
#loop-ticks span.bar {
  border-right: 1px solid #ffffff8c;
  box-shadow: 1px 0 0 #000000a6, -1px 0 0 #00000073;
}
/* the loop's own end is the bar's edge, not a line to draw */
#loop-ticks span:last-child { border-right: none; box-shadow: none; }

/* ---- song card ----
   The song is a grid the shape of the pattern card: a row per pattern, a
   column per step of the song. Cells wear the pattern amber; the dashed
   last column is where the song grows. */
#panel-song { margin-top: var(--s5); padding: var(--s4); }
#panel-song .panel-head { align-items: center; margin-bottom: var(--s4); gap: var(--s3); }
#panel-song .steps-hint {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.song-grid {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 2px;
  scrollbar-width: thin;
  scrollbar-color: #3b4048 var(--well);
}

/* the same lane anatomy as the pattern card's .step-row - M/S, name, cells,
   on the same column widths and gap, so the two grids read as one family */
.song-row {
  --lane: var(--amber);
  display: grid;
  grid-template-columns: 42px 96px minmax(0, 1fr);
  --lane-gap: var(--s3);
  gap: var(--lane-gap);
  align-items: stretch;
}
.song-row.head { margin-bottom: 1px; }
.song-row.inaudible .song-cells { opacity: 0.35; }

.song-ms { display: flex; gap: var(--s1); align-items: center; }

/* the y-axis: PATTERN A…F, each label also the handle that picks the pattern */
.song-lane-label {
  background: none;
  border: none;
  box-shadow: none;
  color: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  text-align: left;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 5px;
  transition: color var(--d-tint) var(--e-std);
}
.song-lane-label:hover { color: var(--ink); }
/* each pattern letter wears its row's colour */
.song-lane-label b {
  font-weight: 500;
  color: var(--lane);
  letter-spacing: var(--tr-num);
}
/* the pattern on the editing bench right now */
.song-lane-label.current b { text-shadow: 0 0 7px color-mix(in oklab, var(--lane) 60%, transparent); }
.song-lane-label.current { color: var(--ink-off); }
/* a pattern that holds hits marks itself, like the A–D buttons do */
.song-lane-label.has-events::after {
  content: "";
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--ink-faint);
}

/* the same 3px seam as .step-cells, so the two grids share a rhythm */
.song-cells { display: flex; gap: 3px; min-width: 0; }
/* a song cell is the size a step cell has in a 1-bar pattern: 1/16 of the
   track, minus what that grid spends on seams and beat margins (15×3px +
   3×6px = 63px), so the two cards' cells measure the same. The floor keeps
   a fingertip's target when the track gets narrow. */
.song-colnum {
  flex: none;
  width: calc((100% - 63px) / 16);
  min-width: 26px;
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.song-colnum.now { color: var(--amber); text-shadow: 0 0 6px #ffb45466; }

/* the step cell's exact socket and lit recipe, in the row's own colour */
.song-cell {
  flex: none;
  width: calc((100% - 63px) / 16);
  min-width: 26px;
  height: 20px;
  border-radius: var(--r-sm);
  background: linear-gradient(180deg, #17191c 0%, #1c1e22 100%);
  border: 1px solid #0c0d10;
  box-shadow: inset 0 1px 2px #00000088, 0 1px 0 #ffffff08;
  cursor: pointer;
  transition:
    background 80ms var(--e-std),
    border-color 80ms var(--e-std),
    box-shadow 80ms var(--e-std);
}
.song-cell:hover, .song-cell.on:hover { border-color: var(--ink-dim); }
.song-cell.on {
  background: color-mix(in oklab, var(--lane) 90%, #241a13);
  border-color: color-mix(in oklab, var(--lane) 90%, #24262b);
  box-shadow: 0 0 8px color-mix(in oklab, var(--lane) 40%, transparent);
}
/* the growth column: an outline of the step it would become */
.song-cell.add {
  background: none;
  border-style: dashed;
  border-color: #ffffff16;
  box-shadow: none;
}
.song-cell.add:hover { border-color: color-mix(in oklab, var(--lane) 55%, transparent); }
/* the column sounding right now */
.song-cell.now {
  border-color: #ffffff40;
  box-shadow: inset 0 0 0 1px #ffffff28;
  transition-duration: 0ms;
}
.song-cell.on.now {
  border-color: color-mix(in oklab, var(--lane) 40%, #fff);
  box-shadow:
    0 0 12px -1px color-mix(in oklab, var(--lane) 80%, transparent),
    inset 0 1px 0 #ffffff55;
  transition-duration: 0ms;
}

/* ============ SLIDERS ============ */

input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 22px;
  background: none;
  cursor: pointer;
  --thumb: 13px;
  /* thumb centre travels (100% - thumb); match the fill to it exactly */
  --pos: calc(var(--thumb) / 2 + var(--fill, 0) * (100% - var(--thumb)));
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  /* neutral by default — orange is reserved for "engaged right now" */
  background: linear-gradient(90deg, #71767e 0 var(--pos), var(--well) var(--pos) 100%);
  box-shadow: inset 0 1px 2px #000000aa, inset 0 -1px 0 #ffffff0a;
}
input[type="range"]:hover::-webkit-slider-runnable-track,
input[type="range"]:active::-webkit-slider-runnable-track,
input[type="range"]:focus-visible::-webkit-slider-runnable-track {
  background: linear-gradient(90deg, var(--accent) 0 var(--pos), var(--well) var(--pos) 100%);
}
input[type="range"]::-moz-range-track {
  height: 4px; border-radius: 2px; background: var(--well);
  box-shadow: inset 0 1px 2px #000000aa;
}
input[type="range"]::-moz-range-progress { height: 4px; border-radius: 2px; background: #71767e; }
input[type="range"]:hover::-moz-range-progress,
input[type="range"]:active::-moz-range-progress { background: var(--accent); }

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: var(--thumb);
  height: var(--thumb);
  margin-top: calc((4px - var(--thumb)) / 2);
  border-radius: 50%;
  /* moulded cap: specular at 26%, terminator, bounce */
  background: radial-gradient(circle at 50% 26%, #ffffff 0%, #e4e1db 30%, #b6b3ad 62%, #7c7a75 88%, #5d5b57 100%);
  border: 1px solid #06070a;
  box-shadow: 0 1px 2px #000000cc, 0 3px 7px -1px #00000088, inset 0 -1px 1px #00000055;
  transition: transform 120ms var(--e-out), box-shadow 120ms var(--e-out);
}
input[type="range"]::-moz-range-thumb {
  width: calc(var(--thumb) - 2px);
  height: calc(var(--thumb) - 2px);
  border-radius: 50%;
  border: 1px solid #06070a;
  background: radial-gradient(circle at 50% 26%, #ffffff 0%, #e4e1db 30%, #b6b3ad 62%, #7c7a75 88%, #5d5b57 100%);
  box-shadow: 0 1px 2px #000000cc, 0 3px 7px -1px #00000088;
}
input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.12); }
input[type="range"]:active::-webkit-slider-thumb {
  transform: scale(1.02);
  box-shadow: 0 1px 2px #000000aa, 0 0 0 4px var(--accent-soft);
}
input[type="range"]:focus { outline: none; }

.slider-opt { flex-direction: row; align-items: center; gap: var(--s2); }
.slider-opt label { min-width: 42px; }
.slider-opt.grow { flex: 1; }
.mono-val {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-num);
  /* a value at rest is just a number; it only earns colour once it has been
     moved away from its default (JS adds .changed) */
  color: var(--ink-off);
  min-width: 18px;
  text-align: right;
  transition: color var(--d-tint) var(--e-std);
}
.mono-val.changed { color: var(--amber); }

/* ============ SAMPLER ============ */

.sampler-idle { display: flex; gap: var(--s2); align-items: stretch; }
.sampler-idle .drop-zone { flex: 1; }

.drop-zone {
  position: relative;
  border: none;
  border-radius: var(--r-md);
  /* a milled well, not a flat wash — it is the largest surface in the
     right column when no sample is loaded */
  background: linear-gradient(180deg, #0e1013 0%, #121417 100%);
  box-shadow: inset 0 3px 8px -3px #000000cc, inset 0 1px 0 #00000080, inset 0 -1px 0 #ffffff0c;
  transition: background var(--d-tint) var(--e-std), box-shadow var(--d-tint) var(--e-std);
  cursor: pointer;
}
/* the stitching rides a ::before ABOVE the well's inset shadows — as a
   background layer the top run of dashes sat under the drop shadow and
   read thinner than the bottom. The EVOLVE socket shares the seam, in the
   finer thread its size asks for. */
.drop-zone::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--dash-line) center / 100% 100% no-repeat;
  pointer-events: none;
}
.drop-zone:hover {
  box-shadow: inset 0 3px 8px -3px #000000cc, inset 0 1px 0 #00000080, inset 0 -1px 0 #ffffff0c, inset 0 0 0 1px #ffffff12;
}
.drop-zone:hover::before { background-image: var(--dash-bright); }
.drop-zone.over {
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 2px var(--accent), 0 0 24px #ff5a0033;
}
.drop-zone.over::before { background-image: var(--dash-accent); }
.drop-zone.over .drop-ico { color: var(--accent); transform: translateY(2px); }
#file-input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.drop-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: var(--s5) var(--s3);
  pointer-events: none;
}
.drop-ico { width: 20px; height: 20px; color: var(--ink-faint); transition: color 150ms, transform 150ms; }
/* the sampler speaks the same silkscreen language as every other label */
.drop-main {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  font-weight: 600;
  letter-spacing: var(--tr-10);
  padding-left: var(--tr-10);
  color: var(--ink);
}
.drop-sub {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  padding-left: var(--tr-09);
  color: var(--ink-faint);
}

/* ---- stem separation ---- */

/* the sampler lights up when a stem is dragged over it */
#panel-chop.stem-target {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px #ff5a0044, 0 0 24px -6px #ff5a0055;
}

.stems-progress { display: flex; flex-direction: column; gap: var(--s2); }
.stems-stage {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s2);
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-off);
}
.stems-bar {
  height: 6px;
  border-radius: 3px;
  background: #00000066;
  box-shadow: inset 0 1px 3px -1px #000000cc;
  overflow: hidden;
}
#stems-bar-fill, #evo-bar-fill {
  height: 100%;
  width: 0;
  border-radius: 3px;
  background: linear-gradient(90deg, #ff8a3c, var(--accent));
  box-shadow: 0 0 8px -1px #ff5a00aa;
  transition: width 200ms var(--e-std);
}
.stems-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
  min-height: 1em;
}

.stems-list { display: flex; flex-direction: column; gap: var(--s1); }
.stems-list:not(:empty) { margin-top: var(--s3); }
.stem-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: linear-gradient(180deg, #16181d 0%, #101216 100%);
  cursor: grab;
  /* the row is a drag handle, not text: no selection, no touch scrolling */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: border-color var(--d-tint) var(--e-std), background var(--d-tint) var(--e-std);
}
.stem-row:hover { border-color: var(--line); background: linear-gradient(180deg, #1b1e24 0%, #131519 100%); }
.stem-row.dragging { opacity: 0.5; cursor: grabbing; }
.stem-name {
  flex: 1;
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  font-weight: 600;
  letter-spacing: var(--tr-10);
  text-transform: uppercase;
  color: var(--ink);
}
.stem-size { color: var(--ink-faint); }
/* the WAV link is a control, not prose */
.stem-row .mini-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}
.stems-hint {
  margin-top: var(--s2);
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
  text-align: center;
}

/* ---- evolver ---- */

/* EVOLVE is a socket first and a button second: the same milled well as DROP
   AUDIO, stitched with the same dashed border, sized exactly like the buttons
   beside it. The seam is an invitation to drop one cut on it, which is the
   better raw material; clicking breeds from the whole sample. */
/* every slice-drop socket lights up the same way when a slice is carried
   over it: AUTO, EVOLVE, STEM SEP, the swap tray and the open evolve card.
   Outline and filter on purpose - no other button rule touches either, so
   the ring survives the tinted and hover recipes at any specificity. */
.evo-target {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  filter: drop-shadow(0 0 9px #ff5a0055);
}

/* ancestry: the family tree flattened into a breadcrumb strip. It grows
   rightward forever, so it scrolls sideways instead of stacking. */
.evo-tree {
  display: flex;
  gap: var(--s1);
  overflow-x: auto;
  padding-bottom: var(--s1);
  margin-bottom: var(--s2);
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.evo-chip {
  flex: none;
  height: var(--ctl-h-s);
  padding: 0 var(--s2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--ctl-bg);
  box-shadow: var(--ctl-shadow);
  color: var(--ink-off);
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  font-weight: 600;
  letter-spacing: var(--tr-09);
  line-height: 1;
  cursor: pointer;
  transition: color var(--d-tint) var(--e-std), border-color var(--d-tint) var(--e-std);
}
.evo-chip:hover { color: var(--ink); border-color: var(--line); }
.evo-chip.on {
  color: var(--accent);
  border-color: color-mix(in oklab, var(--accent) 55%, transparent);
  box-shadow: var(--ctl-shadow), inset 0 0 8px color-mix(in oklab, var(--accent) 14%, transparent);
}
.evo-chip.src { color: var(--lcd-mid); }

/* the litter: 16 children, each a small phosphor waveform */
.evo-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s2);
}
.evo-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 62px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--well);
  box-shadow: inset 0 1px 4px -1px #000000aa;
  overflow: hidden;
  cursor: grab;
  /* a cell is a drag handle, not text: no selection, no touch scrolling */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: border-color var(--d-tint) var(--e-std), box-shadow var(--d-tint) var(--e-std);
}
.evo-cell:hover { border-color: var(--line); }
.evo-cell.sel {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px #ff5a0044, 0 0 14px -5px #ff5a0088;
}
.evo-cell.empty { opacity: 0.22; pointer-events: none; }
.evo-cell.dragging { opacity: 0.5; cursor: grabbing; }

/* while a litter renders the empty cells breathe - a subtle pulse of the
   well's own background, the whole grid inhaling as one */
.evo-cell.brewing {
  pointer-events: none;
  animation: evo-breathe 1.2s var(--e-std) infinite;
}
/* four levels off the well at the top of the breath: enough that the grid
   is plainly alive, little enough that it never pulls the eye off the
   waveforms landing in it */
@keyframes evo-breathe {
  0%, 100% { background-color: var(--well); }
  50% { background-color: #101317; }
}
@media (prefers-reduced-motion: reduce) {
  .evo-cell.brewing { animation: none; background-color: #0e1014; }
}
.evo-cell canvas { flex: 1; width: 100%; min-height: 0; }
.evo-tag {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s1);
  padding: 2px 4px 3px;
  border-top: 1px solid var(--line-soft);
  background: #00000044;
  font-family: var(--font-mono);
  /* a step under --fs-1: sixteen cells share one card, and at 9px the
     strategy names lost their tails to the ellipsis */
  font-size: 8px;
  letter-spacing: var(--tr-12);
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  /* one line's worth even when the spans are empty, so the skeleton and
     the loaded cell keep the same geometry */
  min-height: 14px;
}
.evo-name { color: var(--ink-off); font-weight: 600; }
.evo-cell.sel .evo-name { color: var(--accent); }
/* the near-copies wear a mark: they are the way back to the parent's sound */
.evo-cell.kin .evo-name::before { content: "◆ "; color: var(--amber); }
.evo-dur { color: var(--ink-faint); }
.evo-strat {
  margin-left: auto;
  color: var(--amber);
  opacity: 0.75;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* narrow cells: the strategy says more than the seconds do */
@container machine (max-width: 620px) {
  .evo-dur { display: none; }
}

.evo-controls {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s3);
}
.evo-controls #btn-evo-breed { flex: 1; }
.evo-progress { margin-top: var(--s2); }

.mic-btn {
  width: 56px;
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  padding-left: var(--tr-09);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
}
/* sits beside MIC and reads as another input source, just a wider label */
.tab-btn {
  width: 74px;
  line-height: 1.25;
  text-align: center;
}
.tab-ico { width: 17px; height: 17px; color: var(--ink-dim); }
.tab-btn:hover:not(:disabled) .tab-ico { color: var(--accent); }

/* the explainer speaks the same way HOW TO PLAY does: prose in --ink-dim,
   inline terms set in the mono face, silkscreen headings */
.tabsampler-body p { font-size: var(--fs-4); line-height: 20px; color: var(--ink-dim); }
.tabsampler-body h3 {
  font-size: var(--fs-2);
  font-weight: 700;
  letter-spacing: var(--tr-title);
  color: var(--ink-off);
  margin: var(--s4) 0 var(--s2);
}
.tabsampler-body b {
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--fs-3);
}

/* numerals in the mono face, like every other numbered thing on the machine */
.ts-steps { list-style: none; counter-reset: ts; display: grid; gap: 7px; }
/* the numeral sits in a gutter; the copy stays ordinary inline flow so <b>
   terms run with the sentence instead of becoming their own column */
.ts-steps li {
  counter-increment: ts;
  position: relative;
  padding-left: 26px;
  font-size: var(--fs-4);
  line-height: 20px;
  color: var(--ink-dim);
}
.ts-steps li::before {
  content: counter(ts, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0;
  line-height: 20px;
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* the primary action, cut like SEND TO PADS: full width, lit accent */
.ts-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 32px;
  margin-top: var(--s5);
  border-radius: var(--r-md);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-2);
  letter-spacing: 0.18em;
  padding-left: 0.18em;
  text-decoration: none;
  cursor: pointer;
  --st: var(--accent);
  color: color-mix(in oklab, var(--st) 62%, #fff);
  border: 1px solid color-mix(in oklab, var(--st) 58%, transparent);
  background: linear-gradient(180deg,
    color-mix(in oklab, var(--st) 24%, #1a1c20) 0%,
    color-mix(in oklab, var(--st) 14%, #131519) 100%);
  box-shadow: var(--ctl-shadow), 0 0 14px -6px var(--st);
  transition: color var(--d-tint) var(--e-std), background var(--d-tint) var(--e-std),
    border-color var(--d-tint) var(--e-std), box-shadow var(--d-release) var(--e-out),
    transform var(--d-release) var(--e-out);
}
.ts-cta:hover {
  color: #fff;
  border-color: var(--st);
  box-shadow: var(--ctl-shadow), 0 0 20px -6px var(--st);
}
.ts-cta:active { transform: translateY(1px); box-shadow: var(--ctl-shadow-press); }
/* the two calls to action share a row: install the bridge, or go looking for
   something to put through it */
.ts-ctas { display: flex; gap: var(--s3); }
.ts-ctas .ts-cta { flex: 1; margin-top: var(--s5); }
.ts-dig { --st: var(--amber); }
.ts-dig-query {
  margin-top: var(--s3);
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-09);
  color: var(--amber);
}

.ts-note { font-size: var(--fs-2) !important; color: var(--ink-faint) !important; margin-top: var(--s2); }
#tabsampler-overlay .panel-head { align-items: baseline; }
#btn-tabsampler-close { width: var(--ctl-h-s); height: var(--ctl-h-s); font-size: var(--fs-3); font-weight: 300; }

.mic-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: radial-gradient(circle at 50% 33%, #6a6e75 0%, #4a4d53 60%, #24262a 100%);
  transition: all var(--d-tint);
}
.mic-btn.recording .mic-dot {
  background: radial-gradient(circle at 50% 33%, #fff0ee 0%, #ff7a70 32%, #ff3b30 70%, #a02018 100%);
  box-shadow: 0 0 8px var(--red);
  animation: blink 1s steps(2) infinite;
}

.chop-editor { display: flex; flex-direction: column; gap: var(--s3); }
.hidden { display: none !important; }

/* the waveform is the second screen — same material as the LCD */
#waveform {
  width: 100%;
  height: 92px;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, #0e1a10 0%, #0a120a 100%);
  border: 1px solid #04060a;
  box-shadow:
    inset 0 0 0 1px #ffffff06,
    inset 0 2px 12px #000000cc,
    inset 0 0 34px #4f8f3f16,
    0 1px 0 #ffffff0c;
  cursor: grab;
  touch-action: none;
  display: block;
}
.chop-hint {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: 0;
  color: var(--ink-off);
  margin-top: -6px;
}
.chop-hint kbd {
  font-family: var(--font-mono);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 3px;
  color: var(--ink-off);
  background: #00000033;
}

.chop-controls { display: flex; flex-direction: column; gap: var(--s3); }
.chop-info-row { display: flex; gap: var(--s2); align-items: center; }

/* The slice count sits in the card's head, beside eject. Down in the controls
   row it was a range track wedged between the tempo readout and the fit
   buttons - the first thing a narrow column stole width from, until the whole
   2-16 sweep lived in a dozen pixels. A stepper has a floor it cannot be
   pushed below, and whole slices were never a sliding gesture anyway. */
.chop-slices { display: flex; align-items: center; gap: var(--s2); }
.chop-slices-lab {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  font-weight: 700;
  letter-spacing: var(--tr-09);
  line-height: 1;
  color: var(--ink-faint);
}
.slices-stepper { height: var(--ctl-h-s); }
.slices-stepper .step-btn { width: 20px; height: calc(var(--ctl-h-s) - 2px); }

.sample-bpm {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-10);
  color: var(--amber);
  white-space: nowrap;
}
/* eject is the way out of the sampler, not one of the things you do inside
   it — so it is the machine's standard control cap, sized to the slice
   stepper beside it, and stays quiet until you reach for it */
.eject-btn, .swap-btn {
  display: grid;
  place-items: center;
  width: 24px;
  height: var(--ctl-h-s);
  padding: 0;
}
.eject-btn svg, .swap-btn svg { width: 13px; height: 13px; display: block; }
/* the two caps are the same size and sit together: EJECT takes the sample
   out and warns in red, SWAP brings the next one in and invites in accent */
.eject-btn:hover:not(:disabled) { color: var(--red); border-color: #ff3b3055; }
.swap-btn:hover:not(:disabled) { color: var(--accent); border-color: #ff5a0055; }

/* 4 modifiers on one row, the primary action full width beneath — no holes */
/* row-gap matches .chop-controls' own gap so SEND TO PADS sits as far below the
   modifier row as the row sits below the slice controls; the columns stay tight */
.chop-btns { display: grid; grid-template-columns: repeat(4, 1fr); column-gap: 6px; row-gap: var(--s3); }
.chop-btns .accent { grid-column: 1 / -1; height: 32px; font-size: var(--fs-2); letter-spacing: 0.18em; }
.chop-btns .mini-btn { flex: none; min-width: 0; padding: 0 6px 0 calc(6px + var(--tr-09)); }
/* ---- SYNC overlay: recording timing, measured to this player ---- */
.sync-body { display: flex; flex-direction: column; gap: var(--s4); padding-top: var(--s3); }
.sync-copy { color: var(--ink-off); font-size: var(--fs-4); line-height: 1.55; max-width: 62ch; }
.sync-readouts { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3); }
.sync-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--s3);
  background: var(--well);
  border: 1px solid #05070a;
  border-radius: var(--r-md);
  box-shadow: inset 0 1px 3px #00000073;
}
.sync-num { font-family: var(--font-mono); font-size: var(--fs-5); color: var(--amber); }
.sync-lab { font-size: var(--fs-1); letter-spacing: var(--tr-10); color: var(--ink-off); }
.sync-test { display: flex; align-items: center; gap: var(--s3); }
.sync-test .mini-btn { flex: none; padding: 0 14px; }
.sync-tap {
  flex: 1;
  height: 64px;
  font-family: var(--font-mono);
  font-size: var(--fs-5);
  letter-spacing: 0.3em;
  color: var(--ink);
  background: var(--ctl-bg);
  border: 1px solid var(--ctl-edge);
  border-radius: var(--r-md);
  box-shadow: var(--ctl-shadow);
  cursor: pointer;
}
.sync-tap:active { background: var(--ctl-bg-press); box-shadow: var(--ctl-shadow-press); transform: translateY(1px); }
.sync-status { font-family: var(--font-mono); font-size: var(--fs-2); letter-spacing: var(--tr-09); color: var(--ink-off); min-height: 1.4em; }

/* ============ PREFERENCES ============ */

.prefs-body { display: flex; flex-direction: column; gap: var(--s4); padding-top: var(--s3); }
.pref-group-lab {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
}
.pref-faces { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3); }

/* each choice is a pad, so the sample is shown on the surface it will be
   engraved into rather than on a form control */
.pref-face {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--s2);
  padding: var(--s3);
  cursor: pointer;
  color: inherit;
  border: 1px solid var(--pad-edge);
  border-radius: var(--r-pad);
  background:
    radial-gradient(125% 95% at 50% 4%, var(--pad-face-hi) 0%, #2e3137 38%, #0000 70%),
    linear-gradient(180deg, #2e3137 0%, var(--pad-face) 55%, #1d1f23 100%);
  box-shadow:
    inset 0 1px 0 #ffffff1e,
    inset 0 6px 12px -10px #000000c0,
    0 1px 1px #000000a0,
    0 4px 9px -3px #00000077;
  transition: border-color var(--d-tint) var(--e-std), box-shadow var(--d-tint) var(--e-std);
}
.pref-face:hover { border-color: #ffffff1f; }
.pref-face.on {
  border-color: #ff5a0077;
  box-shadow:
    inset 0 1px 0 #ffffff1e,
    inset 0 0 0 1px #ff5a0033,
    0 1px 1px #000000a0,
    0 0 16px -4px #ff5a0055;
}
.pref-sample {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  text-align: center;
  color: var(--legend);
  text-shadow: var(--engrave);
  overflow: hidden;
}
.pref-face.on .pref-sample { color: #d6d2c9; }
/* the sample always wears the face it selects, whatever is currently set */
.pref-sample[data-face="robot"] {
  font-family: "Orbitron", var(--font-mono);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.06em;
}
.pref-sample[data-face="nameplate"] {
  font-family: "Playfair Display", Georgia, serif;
  font-style: italic;
  font-weight: 600;
  font-size: 21px;
}
.pref-sample[data-face="inter"] {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: var(--tr-12);
}
.pref-name {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-09);
  color: var(--legend-dim);
  text-shadow: var(--engrave-soft);
}
.pref-face.on .pref-name { color: var(--accent); text-shadow: none; }
.pref-note { font-size: var(--fs-1); letter-spacing: var(--tr-11); color: var(--ink-faint); }

@container machine (max-width: 620px) {
  .pref-faces { grid-template-columns: 1fr; }
}

/* the metronome button ticks with its click - the whole face lights, not
   just the lettering, and the downbeat hits harder so the bar reads at a
   glance. Keyframes outrank the base and .on rules, so it flashes either
   way and settles back into whichever state it was in. */
@keyframes metro-tick {
  0% { background: #ffb45408; border-color: #ffb4541c; }
  100% { background: var(--ctl-bg); border-color: var(--ctl-edge); }
}
@keyframes metro-tick-down {
  0% { background: #ffb45412; border-color: #ffb4542e; }
  100% { background: var(--ctl-bg); border-color: var(--ctl-edge); }
}
/* a breath on the surface, not a lamp: short, dim, gone before the next beat */
#btn-metro.tick { animation: metro-tick 0.11s ease-out; }
#btn-metro.tick-down { animation: metro-tick-down 0.16s ease-out; }

/* FIT splits like REC: the button acts, the chips stacked beside it pick
   the flavour — same horizontal-divider treatment as DUB/TAKE */
/* the FIT block runs wider than MATCH - it carries the mode chips too */
.fit-block { display: flex; align-items: stretch; flex: 1.9; }
.fit-block .mini-btn { flex: 1; border-radius: var(--r-md) 0 0 var(--r-md); border-right: none; }
.fitmode-seg {
  flex-direction: column;
  height: auto;
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
.fitmode-seg button {
  flex: 1;
  width: auto;
  height: auto;
  padding: 0 6px;
  font-size: 8px;
  line-height: 1;
}
.fitmode-seg button:first-child { border-radius: 0 var(--r-md) 0 0; }
.fitmode-seg button:last-child { border-radius: 0 0 var(--r-md) 0; }
/* pressing FIT presses the whole machined block - the chips ride along */
.fit-block:has(> .mini-btn:active:not(:disabled)) .fitmode-seg {
  transform: translateY(1px);
  background: var(--ctl-bg-press);
  box-shadow: var(--ctl-shadow-press);
}
/* the divider runs horizontally here, not down the right edge */
.fitmode-seg button:not(:last-child)::before {
  right: 5px;
  left: 5px;
  top: auto;
  bottom: 0;
  width: auto;
  height: 1px;
}

.mini-btn {
  height: var(--ctl-h-m);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  padding: 0 8px 0 calc(8px + var(--tr-09));
  white-space: nowrap;
  flex: 1;
}

/* ============ PAD EDIT ============ */

#panel-pad.disabled .panel-title,
#panel-pad.disabled .pad-edit-name { color: var(--ink-faint); }
#panel-pad.disabled .pad-edit-grid,
#panel-pad.disabled .pad-edit-toggles,
#panel-pad.disabled .pe-layer-row { opacity: 0.55; pointer-events: none; }
/* the children are already :disabled; without this the two fades multiply
   (0.55 x 0.3 = 0.165) and the panel becomes unreadable rather than muted */
#panel-pad.disabled button:disabled,
#panel-pad.disabled input:disabled { opacity: 1; filter: none; }

/* the PAD title's name half is a rename target, the slot half is not */
.pe-name { cursor: pointer; }
.pe-name:hover { color: var(--amber); }

/* ---- pad colour picker ----
   The app's own chips rather than the OS dialog: a native picker is another
   piece of software wearing another skin, and twelve chosen colours suit
   this UI better than sixteen million. */
.color-group { position: relative; display: inline-flex; }
#pe-color { display: inline-flex; align-items: center; gap: 6px; }
.pe-color-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  /* unset reads as an empty socket, not as a colour that happens to be grey */
  background: #2b2e33;
  border: 1px solid #ffffff24;
  box-shadow: inset 0 1px 0 #ffffff20;
  flex: none;
}
/* a raised module lifted off the panel, built from the same recipe as the
   chassis: a lit top edge, a hairline ring, a dark underside, and its own
   shadow falling on the panel below */
.color-pop {
  position: absolute;
  top: calc(100% + 7px);
  left: 0;
  z-index: 20;
  isolation: isolate;   /* keeps its grain layer above its own background */
  padding: var(--s3);
  border-radius: var(--r-md);
  background: linear-gradient(180deg, #23262b 0%, #1a1c20 55%, #141619 100%);
  border: 1px solid #07080a;
  box-shadow:
    inset 0 1px 0 #ffffff1c,
    inset 0 0 0 1px #ffffff05,
    inset 0 -2px 0 #00000099,
    0 18px 40px -12px #000000e0,
    0 6px 16px -6px #000000b0;
}
/* the same moulded grain the panels and pads carry, behind the chips so it
   textures the surface without touching their edges */
.color-pop::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: -1;
  opacity: 0.09;
  mix-blend-mode: overlay;
  background-image: var(--noise);
  background-size: 140px 140px;
}
.color-pop.hidden { display: none; }
/* the popover is wider than the button, so let it grow left as well as right
   rather than pushing past the panel edge */
.color-pop { left: 50%; transform: translateX(-50%); }
/* while picking, the whole panel rises: see setColorPop() for why it has to
   be the panel and not the popover */
#panel-pad.picking { z-index: 30; }
.color-swatches {
  display: grid;
  /* exact track widths, never 1fr: an 18px chip inside an 18.17px fr-track
     starts on a fractional pixel, and the browser then anti-aliases both
     straight edges into the gap. That soft ramp is what reads as smudge. */
  grid-template-columns: repeat(12, 20px);
  /* pitch matters as much as the origin: chip + gap must be a whole number of
     DEVICE pixels or every chip after the first drifts off the grid again.
     25 x 1.8 = 45, and it stays whole at 1x and 2x too. */
  gap: 5px;
}
/* A chip is pure colour and nothing else: no border, no bevel, no shadow, not
   even a hairline. A translucent inset edge sits on top of the anti-aliased
   boundary the chip already has, and the two together ramp the colour into
   the gap over three pixels instead of stopping at one. The dark gap between
   chips is all the separation a flat block of colour needs. */
.color-swatches button {
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: var(--sw);
  box-shadow: none;
  cursor: pointer;
  transition: box-shadow var(--d-tint) var(--e-std);
}
/* hover reads as an inset ring rather than a scale: growing a 20px chip puts
   its edges back on fractional pixels, which is the blur all over again */
.color-swatches button:hover {
  box-shadow: inset 0 0 0 2px #ffffffcc;
}
/* the chosen one gets a ring OUTSIDE itself, with a gap of popover behind it,
   so it reads on a white chip and a near-black one alike */
.color-swatches button.on {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  box-shadow: none;
}
.color-clear {
  width: 100%;
  margin-top: var(--s2);
  height: var(--ctl-h-s);
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-off);
  background: var(--ctl-bg);
  border: 1px solid var(--ctl-edge);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.color-clear:hover { color: var(--ink); background: var(--ctl-bg-hover); }

.pad-edit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s1) var(--s4); }
.pad-edit-grid .slider-opt label { min-width: 30px; font-size: var(--fs-1); }
.pad-edit-grid .mono-val { min-width: 26px; }
.pad-edit-grid input[type="range"] { height: var(--ctl-h-s); }

.pad-edit-toggles { display: flex; gap: var(--s2); align-items: center; margin-top: var(--s3); flex-wrap: wrap; }
.choke-group { display: flex; gap: 6px; align-items: center; margin-left: auto; }
.choke-group label {
  font-size: var(--fs-1);
  font-weight: 700;
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
}
#pe-choke button { width: auto; padding: 0 7px; }

.pe-layer-row {
  display: flex;
  gap: var(--s2);
  align-items: center;
  margin-top: var(--s3);
  padding: var(--s2);
  border: 1px solid var(--line-soft);
  background: #00000022;
  border-radius: var(--r-md);
}
.pe-layer-name {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--amber);
}
.pe-layer-row .mini-btn { flex: 0 0 auto; width: 24px; padding: 0; }

/* ============ OUTPUT ============ */

/* 6 exports in two clean rows of three; the destructive NEW sits apart on
   its own full-width row so it can never be hit by muscle memory */
/* ---- FILE menu: the old OUTPUT card, folded into the top bar ---- */
.file-wrap { position: relative; }
.file-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 60;
  min-width: 172px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  /* lifted off the rack the way the overlay cards are: a dark outer edge to
     separate it from whatever it covers, a lit top lip and a whisper of a
     rim inside that, so the panel reads as a raised object rather than a
     hole cut in the page. Its face sits a few steps up the neutral ramp from
     the rack behind it — matched to the rack it only read as a shadow. */
  background: linear-gradient(180deg, #2b2d32 0%, #232529 100%);
  border: 1px solid #0b0c0f;
  border-radius: var(--r-md);
  box-shadow:
    inset 0 1px 0 #ffffff17,
    inset 0 0 0 1px #ffffff08,
    0 14px 34px -10px #000000e6,
    0 2px 10px #00000080;
}
.file-item {
  position: relative;
  display: block;
  text-align: left;
  padding: 8px 12px;
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-10);
  color: var(--ink-off);
  background: none;
  border: none;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.file-item:hover:not(:disabled) { background: #ffffff12; color: var(--ink); }
.file-item:disabled { opacity: 0.4; cursor: default; }
/* a groove in the lifted face, so it needs a lit under-lip like every other
   inset seam on the machine */
.file-sep { height: 1px; margin: 4px 4px; background: #00000059; box-shadow: 0 1px 0 #ffffff0d; }
/* LOAD hides a file input inside itself; clip it to the button's own radius
   without clip-path, which would also clip the focus ring outside the box */
#btn-load { overflow: hidden; }
#btn-load:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
#project-input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
/* the destructive one leads the drawer, but never dressed as a routine
   action: it stays the dimmest item until you reach for it, then goes red */
#btn-new { color: var(--ink-faint); }
#btn-new:hover { color: var(--red); background: #ff3b300f; }
#btn-new.confirm { color: var(--red); animation: blink 0.7s steps(2) infinite; }

.chip-btn {
  width: var(--ctl-h-m);
  height: var(--ctl-h-m);
  border-radius: 50%;
  color: var(--ink-off);
  font-size: var(--fs-3);
  display: grid;
  place-items: center;
}
.chip-btn.wide {
  width: auto;
  padding: 0 10px 0 calc(10px + var(--tr-09));
  border-radius: 999px;
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
}

/* ============ STEP EDITOR ============ */

/* same 16px inset as every other panel, so its content rails line up with
   the pad grid's rails instead of missing them by 4px */
#panel-steps { margin-top: var(--s5); padding: var(--s4); }
/* title | tools | play + pattern + length + CLEAR, pushed to the far edge.
   The slack lives in the last column rather than in a spacer of its own, so
   .head-right has to sit at the end of it instead of being stretched across
   it — left stretched, CLEAR grew to fill the whole rest of the row. */
#panel-steps .panel-head {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s4);
}
#panel-steps .panel-head > .head-right { justify-self: end; }
/* the hint is the only expendable thing here: it clips rather than pushing the
   controls onto a second row */
#panel-steps .steps-hint {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-align: right;
}
/* The step editor's own transport: the same control as PLAY in LOOP, shrunk
   to the head's furniture so it reads as part of the card rather than a
   second, competing transport. It lights green from the same state update. */
.steps-play {
  display: grid;
  place-items: center;
  width: 32px;
  /* the same height as the pattern letters and CLEAR beside it */
  height: var(--ctl-h-m);
  padding: 0;
  color: var(--ink-dim);
}
.steps-play .ico-play {
  border-left-width: 9px;
  border-top-width: 5.5px;
  border-bottom-width: 5.5px;
}
.steps-play:hover:not(:disabled) { color: var(--green); border-color: #7dd06955; }
.steps-play.playing { --st: var(--green); }

#pattern-seg-steps button { width: 24px; }
/* .seg buttons are a fixed 30px for single letters; these carry words */
.tool-seg button {
  width: auto;
  padding: 0 8px 0 calc(8px + var(--tr-09));
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
}

.bars-stepper { height: var(--ctl-h-s); }
.bars-stepper .step-btn { width: 20px; height: calc(var(--ctl-h-s) - 2px); }
.bars-val, .slices-val, .str-val {
  /* a flex child of the capsule: centre the digit in its own box rather than
     letting it sit on the stretched box's baseline */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  box-shadow: inset 1px 0 0 var(--line-soft), inset -1px 0 0 var(--line-soft);
}

/* ============ MELODY ============ */

.mel-opts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
  align-items: flex-end;
}
/* every control in the card is one height, so the labels above them share a
   baseline whichever mix of segs and steppers ends up on a row */
.mel-stepper .step-btn { width: 22px; }
.mel-val { min-width: 22px; font-size: var(--fs-2); }
.mel-val.wide { min-width: 34px; }
.mel-root-row { display: flex; gap: var(--s2); align-items: stretch; }
.mel-root-row .mini-btn { height: var(--ctl-h-m); }

/* how a note sits against the last chord: in it, safe near it, or rubbing it */
/* the melody root opts back out of the engraved fill: it has to read as amber,
   and a clipped gradient cannot be recoloured by `color` alone */
.pad.mel-ct .pad-name {
  background-image: none;
  -webkit-text-fill-color: currentColor;
  color: var(--amber);
  text-shadow: 0 0 8px #ffb45433, var(--engrave-soft);
}
.pad.mel-tn .pad-name { color: var(--legend-dim); }
/* the key's root degrees anchor the grid */
.pad.mel-root .pad-num { color: var(--accent); text-shadow: none; }

/* the box you drag to select (the selection styling itself lives further down,
   after .step-cell.on, which sets its own box-shadow) */
.step-marquee {
  position: absolute;
  z-index: 6;
  pointer-events: none;
  border: 1px solid var(--amber);
  background: #ffb45422;
  border-radius: 2px;
}
#step-grid { position: relative; }
#step-grid[data-tool="select"] .step-cell { cursor: crosshair; }
#step-grid[data-tool="move"] .step-cell.on { cursor: grab; }
#step-grid[data-tool="dupe"] .step-cell.on { cursor: copy; }
#step-grid[data-tool="vel"] .step-cell { cursor: ns-resize; }

.steps-hint {
  justify-self: start;
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  /* running lowercase copy, not a silkscreen label: no display tracking */
  letter-spacing: 0;
  color: var(--ink-off);
}

.step-grid {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 2px;
  /* Firefox takes only these two; the ::-webkit rules below are for the rest.
     Both are needed — neither engine understands the other's syntax. */
  scrollbar-width: thin;
  scrollbar-color: #3b4048 var(--well);
}
.step-grid::-webkit-scrollbar, .song-grid::-webkit-scrollbar { height: 10px; }
.step-grid::-webkit-scrollbar-track, .song-grid::-webkit-scrollbar-track {
  background: var(--well);
  border-radius: var(--r-sm);
  box-shadow: inset 0 1px 2px #000000aa;
}
.step-grid::-webkit-scrollbar-thumb, .song-grid::-webkit-scrollbar-thumb {
  /* the same machined block every control in here is made of */
  background: linear-gradient(180deg, #2f333a 0%, #24272c 100%);
  border: 1px solid #0c0d10;
  border-radius: var(--r-sm);
}
.step-grid::-webkit-scrollbar-thumb:hover, .song-grid::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #3a3f47 0%, #2c3036 100%);
}

.step-row {
  --lane: var(--accent);
  display: grid;
  grid-template-columns: 42px 96px minmax(0, 1fr);
  /* one source of truth: the gap is also how far the sticky names must paint
     to cover it, so the two can never drift apart across breakpoints */
  --lane-gap: var(--s3);
  gap: var(--lane-gap);
  align-items: stretch;
}
.step-row.inaudible .step-cells { opacity: 0.35; }

/* The names stay put while the lanes scroll. Only the two sticky CELLS carry
   a background, though, and a grid's gaps belong to no cell at all — so the
   lanes used to slide through the bare slots either side of the name, which
   is what made the strip read as text floating over the pattern rather than
   a fixed column. A hard shadow the exact width of the gap closes it, and a
   soft one past the edge puts the lanes visibly underneath. */
.step-row > .step-ms,
.step-row > .step-label,
.song-row > .song-ms,
.song-row > .song-lane-label {
  position: sticky;
  z-index: 2;
  background: var(--panel);
  box-shadow: var(--lane-gap) 0 0 0 var(--panel);
}
.step-row > .step-ms, .song-row > .song-ms { left: 0; }
.step-row > .step-label,
.song-row > .song-lane-label {
  left: 54px;
  box-shadow:
    var(--lane-gap) 0 0 0 var(--panel),
    calc(var(--lane-gap) + 6px) 0 7px -6px #000000b0;
}

.step-ms { display: flex; gap: var(--s1); align-items: center; }
.ms-btn {
  width: 17px;
  height: 17px;
  border-radius: var(--r-sm);
  font-size: var(--fs-1);
  font-weight: 600;
  padding: 0;
  display: grid;
  place-items: center;
  color: var(--ink-faint);
  box-shadow: none;
  background: #17181c;
  border-color: var(--line-soft);
  /* a press here can become a sweep down the column, so the gesture is ours */
  touch-action: none;
}
.ms-btn.m.on { color: #16130f; background: var(--red); border-color: var(--red); box-shadow: 0 0 6px #ff3b3055; }
.ms-btn.s.on { color: #16130f; background: var(--amber); border-color: var(--amber); box-shadow: 0 0 6px #ffb45455; }

/* Two targets in one control: the number opens the pad, the name renames it.
   So the label is a flex row that fills the lane's full height — as a
   text-sized box (10px tall, with the number an inline span clipped by
   overflow) the number was a sliver you had to hit dead centre, and every
   near miss fell through to the rename. */
.step-label {
  background: none;
  border: none;
  box-shadow: none;
  color: var(--ink-off);
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  text-align: left;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  transition: color var(--d-tint) var(--e-std);
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 5px;
}
/* colour only: the sticky lane backing lives on .step-row > .step-label and
   must survive the hover, or the pattern slides through the name column */
.step-label:hover { color: var(--ink); }
.step-label-num {
  flex: none;
  align-self: stretch;
  display: flex;
  align-items: center;
  padding: 0 3px;
  color: var(--ink-faint);
  letter-spacing: var(--tr-num);
  border-radius: 3px;
  /* the number carries the lane to another pattern, and that drag runs in
     both axes - the browser may not take the gesture for scrolling */
  touch-action: none;
  cursor: grab;
  transition: color var(--d-tint) var(--e-std), background var(--d-tint) var(--e-std);
}
.step-label-num:hover { color: var(--accent); background: var(--accent-soft); }
/* The number is the pad's own handle, so on a coloured pad it wears that
   pad's colour. An uncoloured pad keeps the silkscreen grey, the same as the
   rest of the machine's legends.

   The palette runs dark enough (down to 26% lightness) that a raw pad colour
   can vanish into the panel at this size, so the lightness is floored rather
   than the colour being mixed towards white - a wash would drain the bright
   hues of the very saturation that makes them recognisable as their pad.
   The color-mix above is the fallback where relative colour is unsupported. */
.step-row.lane-colored .step-label-num {
  color: color-mix(in oklab, var(--lane) 78%, #fff);
  color: oklch(from var(--lane) clamp(0.74, l, 0.92) c h);
}
.step-row.lane-colored .step-label-num:hover {
  color: #fff;
  background: color-mix(in oklab, var(--lane) 26%, transparent);
}
.step-label-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.step-rename {
  width: 68px;
  background: var(--well);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  padding: 1px 4px;
  outline: none;
  text-transform: uppercase;
}

.step-cells { display: flex; gap: 3px; min-width: 0; }

.step-cell {
  flex: 1;
  min-width: 10px;
  height: 20px;
  border-radius: var(--r-sm);
  /* a step is a recessed socket in the panel, lit from above like everything
     else; unlit cells must read as holes, not as painted rectangles */
  background: linear-gradient(180deg, #17191c 0%, #1c1e22 100%);
  border: 1px solid #0c0d10;
  box-shadow: inset 0 1px 2px #00000088, 0 1px 0 #ffffff08;
  cursor: pointer;
  transition:
    background 80ms var(--e-std),
    border-color 80ms var(--e-std),
    box-shadow 80ms var(--e-std);
  touch-action: none;
}
.step-cell.beat-a { background: linear-gradient(180deg, #1b1d21 0%, #202226 100%); }
.step-cell.beat-start { margin-left: 6px; }
.step-cell.bar-start { margin-left: 18px; }
/* a sample still sounding through this step: a faint orange wash, strongest
   just after the hit and fading with the sound (--tv 1 -> 0) */
.step-cell.tail {
  background: color-mix(in oklab, var(--lane) calc(6% + var(--tv, 0.5) * 14%), #1a1b1f);
  border-color: color-mix(in oklab, var(--lane) calc(10% + var(--tv, 0.5) * 20%), #0c0d10);
}
/* must out-specify .step-cell.on, or only empty cells respond to hover */
.step-cell:hover, .step-cell.on:hover { border-color: var(--ink-dim); }

/* velocity is legible across the whole range (oklab keeps mids bright) */
.step-cell.on {
  background: color-mix(in oklab, var(--lane) calc(28% + var(--vel, 0.8) * 72%), #241a13);
  border-color: color-mix(in oklab, var(--lane) calc(45% + var(--vel, 0.8) * 55%), #24262b);
  box-shadow: 0 0 calc(var(--vel, 0.8) * 9px) color-mix(in oklab, var(--lane) calc(var(--vel, 0.8) * 45%), transparent);
}
.step-cell.ph {
  border-color: #ffffff40;
  box-shadow: inset 0 0 0 1px #ffffff28;
  transition-duration: 0ms;
}
.step-cell.on.ph {
  background: color-mix(in oklab, var(--amber) calc(28% + var(--vel, 0.8) * 72%), #241a13);
  border-color: var(--amber);
  box-shadow: 0 0 calc(4px + var(--vel, 0.8) * 10px) #ffb45455;
  transition-duration: 0ms;
}

/* a hit belonging to another pattern stacked on this song step: visible so
   you can play against it, outlined so it cannot be mistaken for your own -
   it is edited on its own pattern, not here */
.step-cell.ghost {
  background: color-mix(in oklab, var(--amber) calc(4% + var(--gv, 0.6) * 10%), #17191c);
  border-color: #ffb45438;
  box-shadow: inset 0 0 0 1px #ffb45426, inset 0 1px 2px #00000055;
}

/* ---- selection ----
   These have to sit after .step-cell.on and out-specify it, or a lit cell's own
   box-shadow wins and a selected hit shows no ring at all. The region wash is an
   inset flood rather than a background, so it reads over the empty socket
   gradient and the lit velocity colour alike. */
#step-grid .step-cell.in-sel {
  box-shadow: inset 0 0 0 999px #ffb4541c, inset 0 1px 2px #00000088;
}
#step-grid .step-cell.on.in-sel {
  box-shadow:
    inset 0 0 0 999px #ffb4541c,
    0 0 calc(var(--vel, 0.8) * 9px) color-mix(in oklab, var(--lane) calc(var(--vel, 0.8) * 45%), transparent);
}
#step-grid .step-cell.sel,
#step-grid .step-cell.on.sel {
  border-color: var(--amber);
  box-shadow:
    inset 0 0 0 1px var(--amber),
    inset 0 0 0 999px #ffb4541c,
    0 0 7px -1px #ffb45499;
}

/* ---- where that hit would have landed ----
   A pad struck against a running loop flashes the cell the recorder would
   have written it to. It has to read as a measurement rather than as a hit:
   no fill, just a ring that blows out and fades, so it is never confused
   with a real step (.on), the playhead (.ph) or another pattern's hit
   (.ghost). Sits after the selection rules because those out-specify .on,
   and the static reduced-motion fallback has to beat them too. */
@keyframes step-landing {
  0%   { box-shadow: inset 0 0 0 2px var(--accent), 0 0 14px -3px #ff5a00cc; }
  70%  { box-shadow: inset 0 0 0 2px #ff5a0088, 0 0 10px -3px #ff5a0044; }
  100% { box-shadow: inset 0 0 0 2px #ff5a0000, 0 0 10px -3px #ff5a0000; }
}
.step-cell.landing { animation: step-landing 450ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  .step-cell.landing { animation: none; box-shadow: inset 0 0 0 2px var(--accent); }
}

/* ============ OVERLAY ============ */

.overlay {
  position: fixed;
  inset: 0;
  background: #000000bb;
  backdrop-filter: blur(4px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: var(--s4);
  z-index: 50;
  overflow-y: auto;
}
.overlay-card {
  margin-top: min(12vh, 96px);
  width: min(680px, 92vw);
  background: linear-gradient(180deg, #16181c 0%, #131518 100%);
  border: 1px solid #0b0c0f;
  border-radius: var(--r-lg);
  padding: var(--s5);
  box-shadow: 0 40px 90px -18px #000000e0, inset 0 1px 0 #ffffff0e;
}
#help-overlay .panel-head { align-items: baseline; }
#btn-help-close { width: var(--ctl-h-s); height: var(--ctl-h-s); font-size: var(--fs-3); font-weight: 300; }
.help-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5) var(--s6); }
.help-grid h3 {
  font-size: var(--fs-2);
  font-weight: 700;
  letter-spacing: var(--tr-title);
  color: var(--ink-off);
  margin-bottom: 7px;
}
.help-grid p { font-size: var(--fs-4); line-height: 20px; color: var(--ink-dim); }
.help-grid b {
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--fs-3);
}

/* ============ GROOVE BROWSER ============ */

.overlay-card.wide { width: min(920px, 94vw); }
.chip-btn.accent-chip {
  color: var(--accent);
  border-color: #ff5a0055;
  background: linear-gradient(180deg, #2a1c14 0%, #1e1610 100%);
}
.chip-btn.accent-chip:hover { color: #ffb086; border-color: var(--accent); }

.groove-list { max-height: min(64vh, 600px); overflow-y: auto; padding-right: var(--s1); }
.groove-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  gap: var(--s2);
}
.groove-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); width: 100%; }
.groove-fam {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
  white-space: nowrap;
}
.groove-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  min-height: 118px;
  text-align: left;
  padding: var(--s3);
  background: var(--ctl-bg);
  border: 1px solid var(--ctl-edge);
  border-radius: var(--r-md);
  box-shadow: var(--ctl-shadow);
  cursor: pointer;
  transition:
    background var(--d-tint) var(--e-std),
    border-color var(--d-tint) var(--e-std),
    transform var(--d-release) var(--e-out);
}
.groove-card:hover {
  background: var(--ctl-bg-hover);
  border-color: #ff5a0055;
  transform: translateY(-1px);
}
.groove-card:active { transform: translateY(1px); box-shadow: var(--ctl-shadow-press); }
.groove-card.on { border-color: var(--accent); }
.groove-name {
  font-family: var(--font-mono);
  font-size: var(--fs-3);
  font-weight: 600;
  letter-spacing: var(--tr-11);
  color: var(--ink);
}
.groove-blurb {
  font-size: var(--fs-4);
  line-height: 1.4;
  color: var(--ink-dim);
  text-transform: none;
}
.groove-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-1);
  letter-spacing: var(--tr-09);
  color: var(--ink-faint);
  margin-top: auto;
  padding-top: 3px;
}
.groove-empty { font-family: var(--font-mono); font-size: var(--fs-2); color: var(--ink-faint); }

@container machine (max-width: 620px) {
  .groove-cards { grid-template-columns: 1fr; }
}

.slice-ghost {
  position: fixed;
  z-index: 100;
  transform: translate(-50%, -130%);
  background: var(--accent);
  color: #16130f;
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  font-weight: 700;
  letter-spacing: var(--tr-10);
  padding: 5px 9px 5px calc(9px + var(--tr-10));
  border-radius: 6px;
  pointer-events: none;
  box-shadow: 0 8px 20px #000000aa;
}
.slice-ghost.layer { background: var(--amber); }

/* ============ FOCUS & DISABLED ============ */

:where(button, .pad, [tabindex], input, canvas#waveform):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  position: relative;
  z-index: 5;
}
.pad:focus-visible { outline-offset: 3px; }
#bpm:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; color: var(--amber); }
.step-cell:focus-visible { outline-offset: 1px; }

button:disabled, input:disabled {
  opacity: 0.3;
  filter: saturate(0.25);
  cursor: not-allowed;
  box-shadow: none;
}
button:disabled:hover {
  color: var(--ink-off);
  background: var(--ctl-bg);
}

/* ============================================================
   PAGE CONTENT (below the instrument)
   ============================================================ */

.seo {
  width: min(1180px, 100%);
  margin: var(--s6) auto 0;
  padding: 0 var(--s5) var(--s6);
  color: var(--ink-dim);
  font-size: 14px;
  line-height: 1.65;
  -webkit-user-select: text;
  user-select: text;
}
.seo h1 {
  font-size: 26px;
  font-weight: 800;
  letter-spacing: -0.012em;
  line-height: 1.22;
  color: var(--ink);
  margin-bottom: var(--s3);
  max-width: 22ch;
}
.seo h2 {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.004em;
  line-height: 1.3;   /* headings must not inherit body leading */
  color: var(--ink);
  margin: var(--s6) 0 var(--s2);
}
.seo h3 {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink);
  margin-bottom: 2px;
}
.seo p { margin-bottom: var(--s3); max-width: 72ch; }
.seo .lede { font-size: 15px; color: var(--ink); max-width: 68ch; }
.seo b { color: var(--ink); font-family: var(--font-mono); font-size: 13px; font-weight: 600; }
.seo a { color: var(--ink); text-decoration-color: #ffffff33; text-underline-offset: 3px; }
.seo a:hover { color: var(--accent); text-decoration-color: var(--accent); }

.crumbs { margin-bottom: var(--s3); }
.crumbs ul { display: flex; flex-wrap: wrap; gap: var(--s2); list-style: none; }
.crumbs li { font-family: var(--font-mono); font-size: var(--fs-2); color: var(--ink-faint); letter-spacing: var(--tr-10); }
.crumbs li + li::before { content: "/"; margin-right: var(--s2); color: #3f4249; }
.crumbs a { color: var(--ink-faint); text-decoration: none; }
.crumbs a:hover { color: var(--accent); }

.meta-line {
  font-family: var(--font-mono);
  font-size: var(--fs-2);
  letter-spacing: var(--tr-10);
  color: var(--ink-off);
}
.meta-line span { color: var(--ink-faint); }

.feature-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5) var(--s6); margin-top: var(--s4); }
.feature-cols p { margin-bottom: 0; }

.tips { padding-left: 1.25em; margin-bottom: var(--s3); }
.tips li { margin-bottom: var(--s2); max-width: 72ch; }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--s2);
  list-style: none;
  margin: var(--s3) 0 var(--s4);
}
.card-grid a {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: var(--s3);
  background: var(--ctl-bg);
  border: 1px solid var(--ctl-edge);
  border-radius: var(--r-md);
  box-shadow: var(--ctl-shadow);
  text-decoration: none;
  transition: background var(--d-tint) var(--e-std), border-color var(--d-tint) var(--e-std);
}
.card-grid a:hover { background: var(--ctl-bg-hover); border-color: #ff5a0055; color: var(--ink); }
.card-name { font-size: 13px; font-weight: 600; color: var(--ink); }
.card-bpm { font-family: var(--font-mono); font-size: var(--fs-2); letter-spacing: var(--tr-10); color: var(--ink-faint); }

.faq h3 { margin-top: var(--s3); }
.faq p { margin-bottom: var(--s2); }

.disclaimer {
  margin-top: var(--s6);
  padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
  font-size: 12px;
  color: var(--ink-faint);
  max-width: 72ch;
}

/* who made it, under the small print and quieter than it */
.colophon {
  margin-top: var(--s2);
  font-size: 12px;
  color: var(--ink-faint);
}
.colophon a { color: var(--ink-dim); text-decoration: none; border-bottom: 1px solid var(--line-soft); }
.colophon a:hover { color: var(--accent); border-bottom-color: var(--accent); }

@media (max-width: 640px) {
  .seo { padding: 0 var(--s4) var(--s5); margin-top: var(--s5); }
  .seo h1 { font-size: 22px; max-width: none; }
  .feature-cols { grid-template-columns: 1fr; gap: var(--s4); }
}

/* ============================================================
   RESPONSIVE
   Internal layout responds to the MACHINE's own width via
   container queries; media queries handle only the page shell.
   ============================================================ */

@media (max-width: 900px) { body { padding: 10px; } }

@media (max-width: 640px) {
  body {
    padding: 0;
    background-image: radial-gradient(900px 500px at 50% -10%, #101318 0%, transparent 60%);
  }
  #machine {
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 10px 10px 14px;
    min-height: 100vh;
  }
}

@container machine (max-width: 900px) {
  /* minmax(0,…) so the column may shrink past its content's min-content
     width; plain 1fr lets a wide child push the whole deck out */
  #deck, #topbar { grid-template-columns: minmax(0, 1fr); }
  #pad-zone, #control-zone, .panel { min-width: 0; }
  .top-right { padding-right: 0; }
}

/* Tight: drop the chrome a phone does not need */
@container machine (max-width: 620px) {
  #topbar { display: none; }
  /* the top bar carried the only help trigger and the only feedback
     surface, so both reappear inside the pad module */
  #btn-help-compact { display: grid; }
  .status-compact { display: block; }

  #deck { gap: var(--s3); }
  #pad-zone { padding: var(--s3); gap: var(--s3); }
  #pad-grid { gap: var(--s2); }
  .panel { padding: var(--s3); }

  .loop-opts { grid-template-columns: 1fr 1fr; gap: var(--s3); }
  .loop-opts.row2 { grid-template-columns: 1fr; }
  .loop-opts.row2 > .opt:first-child { grid-column: auto; }
  .transport { gap: 6px; }
  .pad-edit-grid { grid-template-columns: 1fr; gap: 2px; }
  .pad-edit-toggles { flex-wrap: wrap; }
  .choke-group { margin-left: 0; }
  .sampler-idle { flex-direction: column; }
  .mic-btn { width: 100%; flex-direction: row; gap: var(--s2); padding: 9px 0; }
  .help-grid { grid-template-columns: 1fr; gap: var(--s3); }
  .chop-info-row { flex-wrap: wrap; }
  #panel-steps, #panel-song { margin-top: var(--s3); padding: var(--s3); }
  .steps-hint { display: none; }
  /* The tool segment and the pattern block are both machined capsules — they
     size to their labels and cannot shrink. Held on one row they set a 471px
     floor under the whole page, which is wider than a phone: the browser then
     scales the entire instrument down to fit. Let them wrap onto as many rows
     as they need and the machine's floor becomes the widest single capsule. */
  #panel-steps .panel-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s3);
  }
  #panel-steps .panel-head > .head-right {
    margin-left: auto;
    /* on the narrowest phones the play button, A-D, the length stepper and
       CLEAR do not make one line either */
    flex-wrap: wrap;
    justify-content: flex-end;
  }
}

/* Very tight: shrink the furniture so the pads stay playable */
@container machine (max-width: 440px) {
  #pad-grid { gap: 6px; }
  .pad { padding: 7px 8px; border-radius: var(--r-md); }
  /* centred ellipsis clips BOTH ends silently, so wrap instead of truncate */
  .pad-name {
    font-size: var(--fs-2);
    white-space: normal;
    line-height: 1.18;
    overflow-wrap: anywhere;
    text-overflow: clip;
  }
  /* the optional faces set their own size above, so they need their own
     tightening here - and not the same one: Orbitron is the widest of the
     three at a given size, the serif the narrowest per unit of x-height */
  :root[data-pad-font="robot"] .pad-name {
    font-size: 9px;
    letter-spacing: 0.03em;
    padding-left: 0.03em;
    line-height: 1.18;
  }
  :root[data-pad-font="nameplate"] .pad-name {
    font-size: 14px;
    line-height: 1.18;
  }
  .pad-key { display: none; }
  /* the LED wears the same 6px it has on a full-size pad, which on these
     small ones reads as a knob - keep it a pinprick */
  .pad-led { width: 4px; height: 4px; }
  .perform-bar { gap: 6px; }
  .perform-bar > #btn-full { margin-left: 0; }
  #repeat-seg, #grid-seg { display: none; }
}

@container machine (max-width: 760px) {
  .step-row, .song-row { grid-template-columns: 38px 78px minmax(0, 1fr); --lane-gap: 6px; }
  .step-row > .step-label, .song-row > .song-lane-label { left: 44px; }
  .step-cell { min-width: 13px; }
}

@container machine (max-width: 560px) {
  .step-row, .song-row { grid-template-columns: 38px 62px minmax(0, 1fr); --lane-gap: 5px; }
  .step-row > .step-label, .song-row > .song-lane-label { left: 43px; }
  .step-label, .song-lane-label { font-size: var(--fs-1); }
  .step-cell { min-width: 11px; }
  .step-cell.bar-start { margin-left: 10px; }
  /* the word goes, the letter stays - the letter is the information */
  .song-lane-label span { display: none; }
  /* a window this narrow is a phone-shaped view whatever the pointer is -
     keycap letters are desk furniture and the pads need the room */
  .pad-key { display: none; }
}

/* ---- touch: fingers need bigger targets ---- */

/* An instrument you play with your fingers cannot also be a document you
   pinch and double-tap: a two-finger roll on the pads would zoom the page,
   and a fast double hit would zoom it too. Allowing only pan leaves scrolling
   intact and takes the browser's own gestures off the table. iOS ignores
   user-scalable in the viewport tag, so this is what actually holds. */
html { touch-action: pan-x pan-y; }
/* Safari's own pinch events sit outside touch-action; app.js refuses them. */

@media (pointer: coarse) {
  /* grow the CAPSULE as well as its children, or the block stays 28px while
     its buttons grow and every label falls off the capsule's centreline */
  .seg, .stepper { height: 34px; }
  .seg button, #bpm, .step-btn { height: calc(34px - 2px); }
  .tog, .mini-btn { min-height: 34px; }
  .chip-btn { width: 34px; height: 34px; }
  /* the compact chip is the ONLY help trigger on a phone — it must be a
     real target, not a 22px dot */
  #btn-help-compact { width: 34px; height: 34px; font-size: var(--fs-3); }
  /* the grid must be pannable; .step-cell blocks touch so it can drag
     velocity, so give the scroller itself the horizontal gesture */
  .step-grid { touch-action: pan-x pan-y; }
  /* touch-action intersects with ancestors, so a cell set to `none` would
     veto the scroller above it; keep the vertical velocity drag by taking
     only the horizontal axis away from the cell */
  .step-cell { touch-action: pan-x; }
  /* DRAW is the tool you land in without choosing it, so on a touch screen
     it must not own the swipe that scrolls the page - a finger crossing the
     grid on its way somewhere else was leaving hits behind. Both pans go
     back to the browser here and a clean tap is the only thing that draws.
     The tools you go and pick - VEL's velocity drag, SELECT's marquee,
     MOVE and DUPE - keep the gestures that are their whole point. */
  #step-grid[data-tool="draw"] .step-cell { touch-action: pan-x pan-y; }
  /* same bargain as .step-cell: the vertical axis is the mute sweep, the
     horizontal one still belongs to the scroller underneath */
  .ms-btn { width: 26px; height: 26px; font-size: var(--fs-2); touch-action: pan-x; }
  .step-cell { height: 24px; }
  input[type="range"] { height: 30px; --thumb: 18px; }
  .pad-key { display: none; }
  /* song cells under a fingertip: a wider floor, the step cell's height */
  .song-cell, .song-colnum { min-width: 30px; }
  .song-cell { height: 24px; }
  /* REC/PLAY/CLEAR carry the performance, and the record-mode switch splits
     its half of the block between two chips: give the row the height that
     leaves both halves catchable */
  .transport { --ctl-h-l: 48px; }
  /* nothing on the machine may hand the browser a text-selection or a
     callout menu when a finger rests on it */
  #machine { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  #machine input, #machine textarea { -webkit-user-select: text; user-select: text; }
  /* the pad-edit sliders carry a tighter height so eight of them fit the
     two-column grid; under a finger they need the same reach as the rest */
  .pad-edit-grid input[type="range"] { height: 30px; }
  .mic-btn { min-height: 34px; }
  /* single letters in a capsule: 20-24px wide is a miss, and the steppers
     either side of a value are the smallest things on the machine */
  #pattern-seg-steps button { width: 30px; }
  .bars-stepper, .slices-stepper, .eject-btn, .swap-btn { height: 30px; }
  .bars-stepper .step-btn, .slices-stepper .step-btn { width: 26px; height: 28px; }
  .eject-btn, .swap-btn { width: 30px; }
  /* the choke row was the last control still built for a mouse pointer */
  #pe-choke button { min-width: 30px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ============ LIVE CODE PANEL (Strudel) ============ */

#panel-code { margin-top: var(--s4); }
.code-status {
  font-size: var(--fs-2);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--green);
  margin-left: var(--s2);
  text-transform: none;
}
.code-status.err { color: var(--accent); }
.code-view {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  line-height: 1.55;
  background: var(--well);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  max-height: 380px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--ink-off);
  user-select: text;
}
.code-view .c-com { color: #5b6068; }
.code-view .c-str { color: #9fb877; }
.code-slot {
  color: var(--ink);
  background: #ffffff0a;
  border-bottom: 1px dashed #ffb45466;
  border-radius: 3px;
  padding: 0 1px;
  outline: none;
}
.code-slot:hover { background: #ffffff12; }
.code-slot:focus { background: #ffb4541a; border-bottom-color: var(--amber); }
.code-slot.err { border-bottom: 1px solid var(--accent); background: #ff5a001a; }
.code-slot[data-empty]::after {
  content: '·fx';
  color: #ffb45466;
  font-style: italic;
}
.code-slot.master[data-empty]::after { content: '·master fx'; }
.code-slot.src { color: var(--amber); }
