/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2026 the tapey-1 contributors */

/* Where each font file comes from, and its licence: fonts/sources.json. */
@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo.woff2") format("woff2");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: block;
}
@font-face {
  font-family: "Plex Mono";
  src: url("fonts/plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-display: block;
}
@font-face {
  font-family: "Plex Mono";
  src: url("fonts/plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-display: block;
}

:root {
  /* The whole unit is measured in cells: the grid (16 by 6 as it comes; layouts.js
     sets another layout's), plus the edge along the right, or along the bottom. */
  --cols: 16;
  --rows: 6;
  --edge-x: 0.915;
  --edge-y: 0.343;
  /* Room for what is not the panel: the undo bar above it, the bar under it, the gaps. */
  --chrome-y: 108px;
  /* Room the skin editor and remap mode take from the panel: a bar along the top, and
     the tray under the panel (or beside it). */
  --room-top: 0px;
  --room-y: 0px;
  --room-x: 0px;
  /* The window the panel fits in: the browser's, or on a phone held upright with the
     panel turned sideways, the turned frame (mobile.js sets both). */
  --vw: 100vw;
  --vh: 100vh;
  --c: min(calc((var(--vw) - 32px - var(--room-x)) / (var(--cols) + var(--edge-x))),
    calc((var(--vh) - var(--chrome-y) - var(--room-top) - var(--room-y)) / (var(--rows) + var(--edge-y))));
  /* The seam between two cells; half of it runs round the grid's border. */
  --seam: max(1.5px, calc(var(--c) * 0.0286));
  /* A cap or a key sits this far inside its cell. */
  --inset: calc(var(--c) * 0.0714);

  --desk: #1b1d20;
  --desk-lift: #24272c;

  --blue: #2f80ea;
  --green: #13923f;
  --orange: #ec4610;
  --red: #f0412b;
  --knob-blue: #58a0f3;
  --knob-green: #3fd16a;
  --knob-white: #f7f7f5;
  --knob-orange: #ff6420;

  --text: #d9dce0;
  --text-soft: #8f959d;
  --text-faint: #737982;
  --panel: #222529;
  --panel-line: #33373d;

  --sans: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Plex Mono", ui-monospace, Menlo, Consolas, monospace;
}

/* Skins: every colour of the unit itself. Light is the default; a skin is one
   more block of these, picked with data-skin on the page's root element. */
:root,
:root[data-skin="light"] {
  --body-top: #e6e8eb;
  --body-bottom: #d4d8dc;
  --body-shine: rgba(255, 255, 255, 0.55);
  --body-edge: inset 0 1px 0 rgba(255, 255, 255, 0.9), inset 0 -2px 0 rgba(0, 0, 0, 0.08), 0 1px 0 #8f959c;
  --seam-color: #757b82;
  --tile-hi: #eef0f2;
  --tile-lo: #e0e3e6;
  --tile-edge: inset 0 1px 0 rgba(255, 255, 255, 0.85), inset 0 -1px 0 rgba(0, 0, 0, 0.035);
  --cap-face: radial-gradient(120% 100% at 50% 28%, #fbfbfc, #f1f2f4 55%, #e5e8eb);
  --cap-face-down: radial-gradient(120% 100% at 50% 32%, #f1f2f4, #e7e9ec 55%, #dadde1);
  --cap-lift: inset 0 1px 0 rgba(255, 255, 255, 0.9), inset 0 -2px 4px rgba(35, 40, 50, 0.05),
    0 1px 1px rgba(35, 40, 50, 0.12), 0 calc(var(--c) * 0.043) calc(var(--c) * 0.1) rgba(35, 40, 50, 0.08);
  --cap-pressed: inset 0 1px 2px rgba(35, 40, 50, 0.1), 0 1px 1px rgba(35, 40, 50, 0.14);
  --white-face: linear-gradient(180deg, #fbfbfc, #f2f3f5 78%, #e6e9ec);
  --white-face-down: linear-gradient(180deg, #eef0f2, #e6e8eb 78%, #dadde1);
  --black-face: linear-gradient(180deg, #45494f, #2c2f34);
  --black-face-down: linear-gradient(180deg, #383b40, #24262a);
  --black-lift: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -2px 4px rgba(0, 0, 0, 0.25),
    0 1px 1px rgba(35, 40, 50, 0.2), 0 calc(var(--c) * 0.043) calc(var(--c) * 0.1) rgba(35, 40, 50, 0.12);
  --ink: #3a3f47;
  --ink-soft: #8f959d;
  --icon-blue: var(--blue);
  --icon-green: var(--green);
  --icon-orange: var(--orange);
  --speaker-ink: #565c63;
  --led-on: #ec4610;
  --led-off: #ccd1d6;
  --model-ink: #9aa0a8;
  --ring-face: radial-gradient(circle at 50% 40%, #f1f3f5, #dfe3e7 70%, #cfd4d9);
  --ring-edge: inset 0 0 0 calc(var(--c) * 0.03) #5d636b, inset 0 0 0 calc(var(--c) * 0.05) #c9ced4,
    inset 0 calc(var(--c) * 0.05) calc(var(--c) * 0.12) rgba(255, 255, 255, 0.7),
    0 calc(var(--c) * 0.02) calc(var(--c) * 0.04) rgba(255, 255, 255, 0.6);
  --enc-led-off: #c4c9cf;
}
:root[data-skin="dark"] {
  --body-top: #3b3e43;
  --body-bottom: #2b2e32;
  --body-shine: rgba(255, 255, 255, 0.06);
  --body-edge: inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 #0e0f11;
  --seam-color: #16181b;
  --tile-hi: #45494f;
  --tile-lo: #393c41;
  --tile-edge: inset 0 1px 0 rgba(255, 255, 255, 0.07), inset 0 -1px 0 rgba(0, 0, 0, 0.18);
  --cap-face: radial-gradient(120% 100% at 50% 28%, #5a5e65, #4c5056 55%, #404349);
  --cap-face-down: radial-gradient(120% 100% at 50% 32%, #4e5258, #43464c 55%, #383b40);
  --cap-lift: inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 -2px 4px rgba(0, 0, 0, 0.2),
    0 1px 1px rgba(0, 0, 0, 0.35), 0 calc(var(--c) * 0.043) calc(var(--c) * 0.1) rgba(0, 0, 0, 0.25);
  --cap-pressed: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 1px rgba(0, 0, 0, 0.3);
  --white-face: linear-gradient(180deg, #595d64, #4b4f55 78%, #404349);
  --white-face-down: linear-gradient(180deg, #4d5157, #42454b 78%, #383b40);
  --black-face: linear-gradient(180deg, #1b1c1f, #0b0c0d);
  --black-face-down: linear-gradient(180deg, #131416, #060607);
  --black-lift: inset 0 1px 0 rgba(255, 255, 255, 0.14), inset 0 0 0 1px rgba(255, 255, 255, 0.05),
    inset 0 -3px 5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.4), 0 calc(var(--c) * 0.043) calc(var(--c) * 0.1) rgba(0, 0, 0, 0.3);
  --ink: #e2e4e8;
  --ink-soft: #9aa0a8;
  --icon-blue: #5c9df2;
  --icon-green: #37b866;
  --icon-orange: #f45a22;
  --speaker-ink: #17191c;
  --led-on: #ec4610;
  --led-off: #2a2d31;
  --model-ink: #7c828a;
  /* The ring of the light skin, in dark: a shallow well a shade below the tile. */
  --ring-face: radial-gradient(circle at 50% 40%, #34373c, #2c2f33 70%, #26292d);
  --ring-edge: inset 0 0 0 calc(var(--c) * 0.03) #111214, inset 0 0 0 calc(var(--c) * 0.05) #3c4045,
    inset 0 calc(var(--c) * 0.05) calc(var(--c) * 0.12) rgba(255, 255, 255, 0.05),
    0 calc(var(--c) * 0.02) calc(var(--c) * 0.04) rgba(255, 255, 255, 0.07);
  --enc-led-off: #24272b;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--desk);
  color: var(--text);
  font-family: var(--sans);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

#app { height: 100%; }
/* The icons' drawings, kept out of the layout. */
.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ------------------------------------------------------------------ stage */

.stage {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: calc(12px + var(--room-top)) calc(16px + var(--room-x)) 12px 16px;
  /* A soft pool of light on the desk, behind the unit. */
  background: radial-gradient(ellipse 70% 60% at 50% 46%, var(--desk-lift), var(--desk) 72%);
}

.unit {
  position: relative;
  flex: none;
  width: calc(var(--c) * (var(--cols) + var(--edge-x)));
  height: calc(var(--c) * (var(--rows) + var(--edge-y)));
  border-radius: calc(var(--c) * 0.314);
  background:
    linear-gradient(180deg, var(--body-shine), rgba(255, 255, 255, 0) 6%),
    linear-gradient(180deg, var(--body-top), var(--body-bottom));
  box-shadow:
    var(--body-edge),
    0 calc(var(--c) * 0.05) calc(var(--c) * 0.04) rgba(0, 0, 0, 0.45),
    0 calc(var(--c) * 0.3) calc(var(--c) * 0.6) rgba(0, 0, 0, 0.45);
  touch-action: none;
}

/* The grid's floor: the seam colour shows between the cells. */
.grid {
  position: absolute;
  left: calc(var(--c) * 0.171);
  top: calc(var(--c) * 0.171);
  width: calc(var(--c) * var(--cols));
  height: calc(var(--c) * var(--rows));
  background: var(--seam-color);
  border-radius: calc(var(--c) * 0.086);
}

.tile {
  --w: 1;
  --h: 1;
  position: absolute;
  left: calc(var(--x) * var(--c) + var(--seam) / 2);
  top: calc(var(--y) * var(--c) + var(--seam) / 2);
  width: calc(var(--w) * var(--c) - var(--seam));
  height: calc(var(--h) * var(--c) - var(--seam));
  border-radius: calc(var(--c) * 0.043);
  background: linear-gradient(165deg, var(--tile-hi), var(--tile-lo));
  box-shadow: var(--tile-edge);
}

/* ------------------------------------------------------------------- keys */

.key {
  display: grid;
  place-items: center;
  cursor: pointer;
}

/* One round cap, nearly as wide as its cell. */
.cap {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: calc(100% - 2 * var(--inset));
  aspect-ratio: 1;
  border-radius: 50%;
  color: var(--ink);
  background: var(--cap-face);
  box-shadow: var(--cap-lift);
  transition: transform 60ms ease-out, box-shadow 60ms ease-out, background 60ms;
}
.key.down .cap {
  transform: translateY(calc(var(--c) * 0.012)) scale(0.985);
  background: var(--cap-face-down);
  box-shadow: var(--cap-pressed);
}

/* The light in a cell's top right corner, on the keys that show a state. */
.led {
  position: absolute;
  right: calc(var(--c) * 0.057);
  top: calc(var(--c) * 0.057);
  width: calc(var(--c) * 0.071);
  height: calc(var(--c) * 0.071);
  border-radius: 50%;
  background: var(--led-off);
  box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
  transition: background 90ms, box-shadow 90ms;
  pointer-events: none;
}
.key.on .led {
  background: var(--led-on);
  box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15), 0 0 calc(var(--c) * 0.086) var(--led-on);
}
/* The mic key while sampling: its light blinks while armed, waiting for a key, and
   burns red while it records. */
.key.mic-armed .led { animation: mic-armed 0.9s steps(1, end) infinite; }
.key.mic-recording .led { background: #ff2b33; box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15), 0 0 calc(var(--c) * 0.13) #ff2b33; }
@keyframes mic-armed { 50% { background: var(--led-off); box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15); } }

.ico {
  width: calc(var(--c) * 0.43);
  height: calc(var(--c) * 0.43);
  fill: none;
  stroke: currentColor;
  stroke-width: 0.85;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}
.blue { color: var(--icon-blue); }
.green { color: var(--icon-green); }
.orange { color: var(--icon-orange); }

.num {
  font-family: var(--sans);
  line-height: 1;
  color: var(--ink);
}
.tkey .num { font-size: calc(var(--c) * 0.457); font-weight: 100; }
.skey .num { font-size: calc(var(--c) * 0.3); font-weight: 200; }
.legend {
  margin-top: calc(var(--c) * 0.057);
  font-size: calc(var(--c) * 0.143);
  font-weight: 400;
  line-height: 1;
  color: var(--ink-soft);
}
.legend.green { color: var(--icon-green); }
.trick {
  width: calc(var(--c) * 0.3);
  height: calc(var(--c) * 0.125);
  margin-top: calc(var(--c) * 0.05);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}

/* Record: the one orange cap on the unit. */
.rec .cap {
  background: radial-gradient(120% 100% at 40% 28%, #ff8a5c, #ec4610 55%, #d93d0a);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.4),
    inset 0 -2px 4px rgba(120, 30, 0, 0.2),
    0 1px 1px rgba(120, 40, 20, 0.25),
    0 calc(var(--c) * 0.043) calc(var(--c) * 0.1) rgba(120, 40, 20, 0.16);
}
.rec.down .cap {
  background: radial-gradient(120% 100% at 40% 32%, #f57a48, #e0400c 55%, #c93606);
  box-shadow: inset 0 1px 2px rgba(120, 30, 0, 0.3), 0 1px 1px rgba(120, 40, 20, 0.25);
}
.rec-dot {
  width: calc(var(--c) * 0.197);
  height: calc(var(--c) * 0.197);
  border-radius: 50%;
  background: #fff;
}

/* ---------------------------------------------------------------- speaker */

.speaker { display: grid; place-items: center; }
.grille { width: calc(min(var(--w), var(--h)) * var(--c) * 0.886); height: calc(min(var(--w), var(--h)) * var(--c) * 0.886); }
.grille circle, .grille-wide circle { fill: var(--speaker-ink); }
.grille-wide { display: none; }
/* A speaker wider than tall: the capsule of holes, two thirds of its width. */
.speaker.wide .grille { display: none; }
.speaker.wide .grille-wide { display: block; width: calc(var(--w) * var(--c) * 0.62); height: calc(var(--w) * var(--c) * 0.62 / 3); }

/* ----------------------------------------------------------------- volume */

/* The volume: the white knob's cap on its own, with no ring. */
.volume { display: grid; place-items: center; cursor: ns-resize; }
.vol-knob {
  position: relative;
  width: calc(var(--c) * 0.7);
  height: calc(var(--c) * 0.7);
  border-radius: 50%;
  background: radial-gradient(circle at 40% 32%, #ffffff, #f2f2f0 55%, #dcdedf);
  box-shadow:
    inset 0 calc(var(--c) * -0.03) calc(var(--c) * 0.05) rgba(0, 0, 0, 0.18),
    inset 0 calc(var(--c) * 0.02) calc(var(--c) * 0.03) rgba(255, 255, 255, 0.45),
    0 calc(var(--c) * 0.05) calc(var(--c) * 0.07) rgba(30, 35, 45, 0.35),
    0 calc(var(--c) * 0.012) calc(var(--c) * 0.016) rgba(30, 35, 45, 0.3);
}
/* Only the slot turns; the light on the cap stays put. */
.vol-knob .enc-slot { transform: rotate(var(--turn, 0deg)); }

/* ---------------------------------------------------------------- encoders */

.enc { display: grid; place-items: center; cursor: ns-resize; }
.enc-ring {
  position: relative;
  display: grid;
  place-items: center;
  width: calc(var(--c) * 1.26);
  height: calc(var(--c) * 1.26);
  border-radius: 50%;
  background: var(--ring-face);
  box-shadow: var(--ring-edge);
}
.enc-cap {
  position: relative;
  width: calc(var(--c) * 0.7);
  height: calc(var(--c) * 0.7);
  border-radius: 50%;
  background: var(--knob, #ccc);
  box-shadow:
    inset 0 calc(var(--c) * -0.03) calc(var(--c) * 0.05) rgba(0, 0, 0, 0.18),
    inset 0 calc(var(--c) * 0.02) calc(var(--c) * 0.03) rgba(255, 255, 255, 0.45),
    0 calc(var(--c) * 0.05) calc(var(--c) * 0.07) rgba(30, 35, 45, 0.35),
    0 calc(var(--c) * 0.012) calc(var(--c) * 0.016) rgba(30, 35, 45, 0.3);
}
/* The slot across the cap shows how far the knob has turned. */
.enc-slot {
  position: absolute;
  left: 50%;
  top: 14%;
  width: calc(var(--c) * 0.16);
  height: 72%;
  margin-left: calc(var(--c) * -0.08);
  border-radius: calc(var(--c) * 0.08);
  box-shadow:
    inset 0 calc(var(--c) * 0.012) calc(var(--c) * 0.02) rgba(0, 0, 0, 0.28),
    0 1px 0 rgba(255, 255, 255, 0.35);
  background: rgba(0, 0, 0, 0.05);
  will-change: transform;
}
.enc.held .enc-cap {
  box-shadow:
    inset 0 calc(var(--c) * -0.03) calc(var(--c) * 0.05) rgba(0, 0, 0, 0.18),
    inset 0 calc(var(--c) * 0.02) calc(var(--c) * 0.03) rgba(255, 255, 255, 0.45),
    0 calc(var(--c) * 0.03) calc(var(--c) * 0.045) rgba(30, 35, 45, 0.38),
    0 calc(var(--c) * 0.008) calc(var(--c) * 0.012) rgba(30, 35, 45, 0.3);
}
.enc[data-knob="blue"] { --knob: var(--knob-blue); }
.enc[data-knob="green"] { --knob: var(--knob-green); }
.enc[data-knob="white"] { --knob: var(--knob-white); }
.enc[data-knob="orange"] { --knob: var(--knob-orange); }
.enc[data-knob="white"] .enc-cap {
  background: radial-gradient(circle at 40% 32%, #ffffff, #f2f2f0 55%, #dcdedf);
}
/* A small lamp: the knob the up and down arrows turn. */
.enc-led {
  position: absolute;
  right: calc(var(--c) * 0.16);
  top: calc(var(--c) * 0.16);
  width: calc(var(--c) * 0.06);
  height: calc(var(--c) * 0.06);
  border-radius: 50%;
  background: var(--enc-led-off);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25);
}
.enc.last .enc-led {
  background: var(--knob);
  box-shadow: 0 0 calc(var(--c) * 0.06) var(--knob), inset 0 1px 1px rgba(255, 255, 255, 0.4);
}
.enc[data-knob="white"].last .enc-led { background: #fff; box-shadow: 0 0 calc(var(--c) * 0.05) rgba(0, 0, 0, 0.35); }

/* ----------------------------------------------------------------- screen */

.screen {
  background: #060607;
  box-shadow: inset 0 0 0 1px #000, inset 0 1px 2px rgba(0, 0, 0, 0.8);
  overflow: hidden;
}
.glass {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}
/* The canvas is exactly 2:1, as large as fits inside the glass. */
#display {
  display: block;
  width: calc(min(var(--w), 2 * var(--h)) * var(--c) * 0.965);
  height: calc(min(var(--w), 2 * var(--h)) * var(--c) * 0.4825);
  background: #000;
}
.glass::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* A faint reflection on the glass, kept light so the blacks stay black. */
  background: linear-gradient(118deg, rgba(255, 255, 255, 0.05) 0, rgba(255, 255, 255, 0.03) 31%, rgba(255, 255, 255, 0) 31.2%);
}

/* ----------------------------------------------------- the musical keyboard */

/* The keyboard is in parts (app.js, setKeyboard). One part is as many white keys
   as it holds, each --kw cells wide, under a row of black keys: three rows tall in
   all, or as tall as its white keys when the black keys are inset among them. */
.keyboard {
  position: absolute;
  left: calc(var(--x, 0) * var(--c));
  top: calc(var(--y, 0) * var(--c));
  width: calc(var(--w, 0) * var(--c));
  height: calc(var(--h, 0) * var(--c));
}
.kb-part {
  position: absolute;
  left: calc(var(--x) * var(--c));
  top: calc(var(--y) * var(--c));
  width: calc(var(--c) * var(--kw) * var(--n));
  height: calc(var(--c) * var(--kh));
}
/* A cell of the black-key row: --a is where it starts and --b how wide it is,
   in cells; the row is half a cell out of step with the white keys. */
.btile {
  --y: 0;
  left: calc(var(--a) * var(--c) + var(--seam) / 2);
  width: calc(var(--b) * var(--c) - var(--seam));
  display: grid;
  place-items: center;
}
.bk {
  position: relative;
  width: calc(var(--c) - var(--seam) - 2 * var(--inset));
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--black-face);
  box-shadow: var(--black-lift);
  cursor: pointer;
  transition: transform 50ms ease-out;
}
.bk.down { transform: translateY(calc(var(--c) * 0.012)) scale(0.97); background: var(--black-face-down); }
/* The black key after the last white key: the left half of a round key, cut flat
   at the keyboard's edge. */
.bk.half {
  justify-self: end;
  width: calc((var(--c) - var(--seam) - 2 * var(--inset)) / 2);
  height: calc(var(--c) - var(--seam) - 2 * var(--inset));
  aspect-ratio: auto;
  border-radius: 999px 0 0 999px;
}
.bk.half.down { transform: translateY(calc(var(--c) * 0.012)) scaleY(0.97); transform-origin: right center; }

.wk { display: grid; place-items: center; cursor: pointer; }
.wk .bar {
  position: relative;
  width: calc(100% - 2 * var(--inset));
  height: calc(100% - 2 * var(--inset));
  border-radius: 999px;
  background: var(--white-face);
  box-shadow: var(--cap-lift);
  transition: transform 50ms ease-out, box-shadow 50ms ease-out;
}
/* Black keys inset: each sits at the top of the line between two white keys, whose
   bars start below it. */
.kb-part.inset .wk .bar { height: calc(100% - 2 * var(--inset) - var(--c) * 0.5); margin-top: calc(var(--c) * 0.5); }
.bslot {
  position: absolute;
  z-index: 1;
  left: calc(var(--a) * var(--c) - var(--c) * 0.27);
  top: calc(var(--c) * 0.06);
  width: calc(var(--c) * 0.54);
  height: calc(var(--c) * 0.54);
  display: grid;
  place-items: center;
}
.kb-part.inset .bk { width: 100%; }
.bslot.half { width: calc(var(--c) * 0.27); }
.kb-part.inset .bk.half { width: 100%; height: 100%; }
.wk.down .bar {
  transform: translateY(calc(var(--c) * 0.012));
  background: var(--white-face-down);
  box-shadow: var(--cap-pressed);
}

/* A dot on each key that sounds. */
.bk.sounding::after,
.wk.sounding .bar::after {
  content: "";
  position: absolute;
  left: 50%;
  width: calc(var(--c) * 0.09);
  height: calc(var(--c) * 0.09);
  margin-left: calc(var(--c) * -0.045);
  border-radius: 50%;
  background: var(--play-color, var(--blue));
  box-shadow: 0 0 calc(var(--c) * 0.08) var(--play-color, var(--blue));
}
.bk.sounding::after { top: 50%; margin-top: calc(var(--c) * -0.045); }
.wk.sounding .bar::after { bottom: calc(var(--c) * 0.2); }
#app[data-instrument="drum"] { --play-color: var(--knob-green); }
#app[data-instrument="synth"] { --play-color: var(--knob-blue); }

/* ------------------------------------------------------------ edge of the body */

.edge {
  position: absolute;
  top: calc(var(--c) * 0.171);
  bottom: calc(var(--c) * 0.171);
  left: calc(var(--c) * (var(--cols) + 0.171));
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: calc(var(--c) * 2.14) 0 calc(var(--c) * 0.12);
}
.leds { display: flex; flex-direction: column; gap: calc(var(--c) * 0.086); }
.leds i {
  width: calc(var(--c) * 0.071);
  height: calc(var(--c) * 0.071);
  border-radius: 50%;
  background: var(--led-off);
  box-shadow: inset 0 0 0 0.5px rgba(0, 0, 0, 0.15);
}
.leds i.on { background: var(--knob-green); box-shadow: 0 0 calc(var(--c) * 0.05) var(--knob-green); }
.leds i.hot { background: var(--knob-orange); box-shadow: 0 0 calc(var(--c) * 0.05) var(--knob-orange); }
.model {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-size: calc(var(--c) * 0.186);
  font-weight: 400;
  letter-spacing: calc(var(--c) * 0.043);
  color: var(--model-ink);
}

/* ------------------------------------------------------------ undo bar */

/* Undo and redo, above the panel (undo.js). Slim, on the desk like the bar under the panel. */
.undo-bar {
  flex: none;
  width: calc(var(--c) * (var(--cols) + var(--edge-x)));
  height: 24px;
  margin-bottom: -8px;
  display: flex;
  align-items: center;
  gap: 2px;
  font-size: 12.5px;
}
.undo-bar .bar-btn { height: 24px; padding: 0 9px 0 6px; }
.undo-bar .bar-btn.off { color: var(--text-faint); opacity: 0.55; cursor: default; }
.undo-bar .bar-btn.off:hover { background: transparent; color: var(--text-faint); }
.undo-bar .bar-btn:not(.off):active { background: #2d3036; }
.undo-bubble { max-width: min(360px, calc(var(--vw) - 16px)); }
/* The vertical skin's bars show icons only; this one has room for its two words. */
:root[data-layout="vertical"] .undo-bar .bar-btn { font-size: 12.5px; gap: 6px; padding: 0 9px 0 6px; }

/* The project (project.js), at the bar's right end: its name, a dot while it has
   unsaved changes, Open, and Save, which has a little more weight than the rest. */
.project-bar { margin-left: auto; display: flex; align-items: center; gap: 2px; min-width: 0; }
.project-bar[hidden] { display: none; }
.project-name {
  min-width: 0;
  max-width: 220px;
  margin-right: 6px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--text-faint);
  font-weight: 500;
  cursor: default;
}
.project-name.dirty::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 6px;
  vertical-align: 1px;
  border-radius: 50%;
  background: var(--knob-orange);
}
.undo-bar .bar-btn.save { color: var(--text); border-color: #3a3e45; background: #23262b; }
.undo-bar .bar-btn.save:hover { background: #2c3036; }
.undo-bar .bar-btn.busy { opacity: 0.55; cursor: progress; }
:root[data-layout="vertical"] .project-name { max-width: 96px; margin-right: 2px; }
@media (max-width: 380px) {
  :root[data-layout="vertical"] .project-name { display: none; }
}

/* A question before something that cannot be taken back (project.js): in the panel's skin. */
.ask-overlay { z-index: 40; }
.sheet.ask {
  width: min(460px, 100%);
  padding: 20px 22px 18px;
  border-color: var(--sheet-line);
  background: var(--sheet-bg);
  color: var(--sheet-ink);
  box-shadow: var(--sheet-shadow);
}
.sheet.ask h2 { margin: 0 0 8px; font-size: 17px; font-weight: 650; letter-spacing: -0.01em; color: var(--sheet-ink); }
.sheet.ask p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--sheet-ink-soft); }
.ask-buttons { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.ask-buttons .btn { border-color: var(--sheet-line); background: var(--sheet-card); color: var(--sheet-ink); }
.ask-buttons .btn:hover { filter: brightness(0.97); }
.ask-buttons .btn.primary { background: var(--knob-orange); border-color: var(--knob-orange); color: #1b0d05; }
.ask-buttons .btn.danger { background: #c9362b; border-color: #c9362b; color: #fff; }
.ask-buttons .btn.danger:hover { background: #b02c22; filter: none; }
.ask-buttons .btn:first-child { margin-right: auto; }
@media (max-width: 420px) {
  .ask-buttons { flex-direction: column-reverse; align-items: stretch; }
  .ask-buttons .btn:first-child { margin-right: 0; }
}
/* Settings, Start over: red, but only a line until it is pressed. */
.btn.danger-line { border-color: #7a3530; color: #ff8f84; background: transparent; }
.btn.danger-line:hover { background: rgba(201, 54, 43, 0.16); }

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

.status-bar {
  flex: none;
  width: calc(var(--c) * (var(--cols) + var(--edge-x)));
  height: 28px;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12.5px;
  color: var(--text-soft);
}
.bar-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px 0 7px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--text-soft);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
}
.bar-btn:hover { color: var(--text); background: #26292e; }
.bar-btn:focus-visible { outline: 2px solid var(--knob-blue); outline-offset: 1px; }
.bar-btn[aria-expanded="true"] { color: var(--text); background: #2a2d33; }
/* Help: the key that lives in the bar, pressed and held like a key. */
.bar-key { touch-action: none; }
.bar-key.down { color: var(--text); background: #2d3036; }
.bar-key.on { color: var(--text); }
.bar-key.on .bar-ico { color: var(--led-on); }
.bar-ico {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.status {
  flex: 1;
  min-width: 0;
  margin: 0 0 0 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-faint);
}
.status.note { color: var(--text-soft); }
.status.error { color: #ff7a66; }
.meta {
  flex: none;
  margin: 0;
  display: flex;
  gap: 14px;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--text-faint);
}
.meta .warn { color: #f0a35c; }

/* ----------------------------------------------------------------- drawer */

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(360px, 92vw);
  padding: 14px 22px 22px;
  overflow-y: auto;
  /* A shade along the top or bottom edge while there is more to scroll to,
     since macOS shows no scroll bar until you scroll. The two covers, in the
     panel's colour (34, 37, 41), move with the content and hide each shade at
     its own end. */
  background:
    linear-gradient(var(--panel) 30%, rgba(34, 37, 41, 0)) center top / 100% 36px no-repeat local,
    linear-gradient(rgba(34, 37, 41, 0), var(--panel) 70%) center bottom / 100% 36px no-repeat local,
    linear-gradient(rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0)) center top / 100% 16px no-repeat scroll,
    linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.55)) center bottom / 100% 16px no-repeat scroll,
    var(--panel);
  border-left: 1px solid var(--panel-line);
  box-shadow: -18px 0 40px rgba(0, 0, 0, 0.45);
  transform: translateX(105%);
  visibility: hidden;
  transition: transform 180ms ease-out, visibility 0s linear 180ms;
  -webkit-user-select: text;
  user-select: text;
  z-index: 10;
}
.drawer.open {
  transform: none;
  visibility: visible;
  transition: transform 180ms ease-out;
}
.drawer-head, .sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}
.sheet-head { margin-bottom: 14px; }
.drawer h2, .sheet h2 {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  color: #eef0f2;
}
.close {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-soft);
  cursor: pointer;
}
.close:hover { background: #2d3036; color: var(--text); }
.close:focus-visible { outline: 2px solid var(--knob-blue); }
.close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }

.field { padding: 12px 0; border-top: 1px solid var(--panel-line); }
.field > label, .field > .label {
  display: block;
  margin-bottom: 7px;
  font-size: 13px;
  font-weight: 600;
  color: #e4e6e9;
}
.field select {
  width: 100%;
  height: 32px;
  padding: 0 30px 0 10px;
  border: 1px solid #3b3f46;
  border-radius: 6px;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5' fill='none' stroke='%238f959d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 10px center / 12px 12px,
    #1a1c1f;
  color: var(--text);
  font: 13px var(--sans);
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}
.field select:focus-visible { outline: 2px solid var(--knob-blue); outline-offset: 1px; }
.hint { margin: 8px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--text-soft); }
.hint:empty { display: none; }
.hint.result { color: var(--knob-green); word-break: break-all; }
.hint.result.bad { color: #ff7a66; }
.path { font-family: var(--mono); font-size: 11.5px; color: var(--text); word-break: break-all; }
.choices { display: flex; flex-direction: column; gap: 6px; }
.choice {
  display: grid;
  grid-template-columns: 16px 1fr;
  gap: 1px 10px;
  align-items: start;
  padding: 6px 10px 7px;
  border: 1px solid #33373d;
  border-radius: 7px;
  cursor: pointer;
}
.choice:hover { border-color: #4a4f57; }
.choice input { margin: 2px 0 0; accent-color: var(--knob-blue); }
.choice b { font-size: 13px; font-weight: 600; color: var(--text); }
.choice span { grid-column: 2; font-size: 12px; line-height: 1.35; color: var(--text-soft); }
.choice:has(input:checked) { border-color: var(--knob-blue); background: rgba(88, 160, 243, 0.08); }
.row { display: flex; flex-wrap: wrap; gap: 8px; }
.btn {
  height: 32px;
  padding: 0 14px;
  border: 1px solid #3f444b;
  border-radius: 6px;
  background: #2a2d32;
  color: var(--text);
  font: 500 13px var(--sans);
  cursor: pointer;
}
.btn:hover { background: #32363c; }
.btn:focus-visible { outline: 2px solid var(--knob-blue); outline-offset: 1px; }
.btn.primary { background: var(--knob-orange); border-color: var(--knob-orange); color: #1b0d05; }
.btn.primary:hover { background: #ff7838; }
.btn:disabled { opacity: 0.5; cursor: default; }
/* The panel's own preferences: a name and a row of choices on one line. */
.pref { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; }
.pref:first-of-type { margin-top: 0; }
.pref-name { font-size: 13px; color: var(--text-soft); }
.seg { display: inline-flex; padding: 2px; border: 1px solid #3b3f46; border-radius: 7px; background: #1a1c1f; }
.seg label { position: relative; cursor: pointer; }
.seg input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
.seg span { display: block; min-width: 46px; padding: 4px 10px; border-radius: 5px; text-align: center; font-size: 12.5px; font-weight: 500; color: var(--text-soft); }
.seg label:hover span { color: var(--text); }
.seg input:checked + span { background: #34383e; color: #eef0f2; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.seg input:focus-visible + span { outline: 2px solid var(--knob-blue); outline-offset: 1px; }
/* A preference with many choices (the skin): a list on the same line. */
.field select.pref-select { width: auto; min-width: 150px; max-width: 60%; height: 30px; }
.about { margin: 16px 0 0; font-size: 12px; color: var(--text-faint); }

/* ---------------------------------------------------------- keys overlay */

.overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(12, 13, 15, 0.72);
  opacity: 0;
  visibility: hidden;
  transition: opacity 140ms, visibility 0s linear 140ms;
  z-index: 20;
}
/* A sheet fading out lets the panel be touched at once. */
.overlay:not(.open) { pointer-events: none; }
.overlay.open { opacity: 1; visibility: visible; transition: opacity 140ms; }
.sheet {
  width: min(900px, 100%);
  max-height: 100%;
  overflow: auto;
  padding: 18px 22px 18px;
  border: 1px solid var(--panel-line);
  border-radius: 12px;
  background: var(--panel);
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5);
}
.sheet-note { margin: 14px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--text-soft); }
/* A laptop keyboard: every row is fifteen key widths, in quarter-key columns. */
.kmap {
  display: grid;
  grid-template-columns: repeat(60, minmax(0, 1fr));
  row-gap: 5px;
}
.kcap {
  min-width: 0;
  min-height: 44px;
  margin: 0 2.5px;
  padding: 5px 7px 6px;
  border: 1px solid #383c43;
  border-radius: 7px;
  background: #2a2d32;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
  transition: background 60ms, border-color 60ms;
}
.kcap.idle { background: transparent; border-style: dashed; border-color: #30343a; }
.kcap.gap { background: transparent; border-color: transparent; }
.kcap .kk { font: 500 10.5px var(--mono); color: var(--text-faint); white-space: nowrap; }
.kcap .kj { font-size: 11px; font-weight: 600; font-stretch: 76%; line-height: 1.15; color: var(--text); -webkit-hyphens: auto; hyphens: auto; }
.kcap.note { background: #e9ebee; border-color: #e9ebee; }
.kcap.note .kk { color: #7c828a; }
.kcap.note .kj { color: #1d2024; }
.kcap.sharp { background: #111214; border-color: #111214; }
.kcap.sharp .kj { color: #f0f1f3; }
.kcap.blue .kj { color: var(--knob-blue); }
.kcap.green .kj { color: var(--knob-green); }
.kcap.orange .kj { color: var(--knob-orange); }
/* The keys you press light up while the map is open. */
.kcap.pressed { border-color: var(--knob-blue); box-shadow: inset 0 0 0 1px var(--knob-blue); }
.kcap.note.pressed { background: #cfe2fb; }

/* ------------------------------------------------------- layouts (layouts.js) */

/* A plain tile where nothing sits, so the grid is whole. */
.tile.blank { pointer-events: none; }
.tile[hidden] { display: none; }
/* A gap: the body shows through, a break between two parts of the grid. Its
   gradient is the body's, placed where the gap sits on it. */
.tile.blank.gap {
  left: calc(var(--c) * -0.171);
  width: calc(var(--c) * (var(--cols) + 0.342));
  border-radius: 0;
  background: linear-gradient(180deg, var(--body-top), var(--body-bottom)) 0 calc(var(--c) * (-0.171 - var(--y)) - var(--seam) / 2) /
    100% calc(var(--c) * (var(--rows) + var(--edge-y))) no-repeat;
  box-shadow: inset 0 calc(var(--seam) * -0.5) 0 rgba(255, 255, 255, 0.14), inset 0 calc(var(--seam) * 0.5) 0 rgba(0, 0, 0, 0.06);
}
/* A key wider than its row keeps a round cap as tall as the row; one as large as a
   knob gets a cap to match (layouts.js sets both). */
.key.wide .cap { width: calc(var(--h) * var(--c) - var(--seam) - 2 * var(--inset)); }
.key.big .cap { width: calc(var(--c) * 1.32); }
.tkey.big .num { font-size: calc(var(--c) * 0.78); }
/* A tile turned a quarter or more (layouts.js): its box is the size it has upright,
   turned about its middle. A knob or the volume smaller or larger than it comes
   keeps its round cap in step with its tile. */
.grid > .tile[data-tile],
.grid > .keyboard { rotate: var(--tile-turn, 0deg); }
.enc.small .enc-ring { scale: calc(0.1 + min(var(--w), var(--h)) * 0.45); }
.volume .vol-knob { scale: min(var(--w), var(--h)); }

/* Vertical: the edge runs along the bottom, its lamps in a row and the name level. */
:root[data-layout="vertical"] .edge {
  top: calc(var(--c) * (var(--rows) + 0.171));
  left: calc(var(--c) * 0.171);
  right: calc(var(--c) * 0.171);
  bottom: 0;
  flex-direction: row;
  padding: 0 calc(var(--c) * 0.2);
}
:root[data-layout="vertical"] .leds { flex-direction: row; }
:root[data-layout="vertical"] .model { writing-mode: horizontal-tb; transform: none; }
:root[data-layout="vertical"] .status-bar { flex-wrap: wrap; height: auto; row-gap: 0; }
:root[data-layout="vertical"] .status { order: 10; flex-basis: 100%; margin: 2px 0 0 8px; }
:root[data-layout="vertical"] .meta { margin-left: auto; gap: 10px; }
/* Narrow: the bar's buttons show their icons; each says what it is when hovered. */
:root[data-layout="vertical"] .bar-btn { font-size: 0; gap: 0; padding: 0 8px; }
/* As narrow as a phone: the bar keeps to one line, and says dropouts only when there are some. */
@media (max-width: 360px) {
  :root[data-layout="vertical"] .meta { gap: 6px; }
  :root[data-layout="vertical"] .meta span:nth-child(2):not(.warn) { display: none; }
  :root[data-layout="vertical"] .bar-btn { padding: 0 6px; }
}

/* Editing: every tile that can move shows it, and a lifted tile follows the pointer. */
:root.editing-layout .unit { cursor: grab; }
:root.editing-layout .grid .tile[data-tile]::after {
  content: "";
  position: absolute;
  inset: calc(var(--c) * 0.035);
  border: 1.5px dashed color-mix(in srgb, var(--knob-blue) 55%, transparent);
  border-radius: calc(var(--c) * 0.06);
  pointer-events: none;
}
:root.editing-layout .grid .tile[data-tile] { animation: tile-wait 1.6s ease-in-out infinite; }
:root.editing-layout .grid .tile[data-tile].can-swap::after { border-style: solid; border-color: color-mix(in srgb, var(--knob-blue) 70%, transparent); }
:root.editing-layout .grid .tile.swap-here::after { border: 2.5px solid var(--knob-blue); background: color-mix(in srgb, var(--knob-blue) 16%, transparent); }
:root.editing-layout .grid .tile.lifted {
  z-index: 5;
  cursor: grabbing;
  animation: none;
  box-shadow: var(--tile-edge), 0 calc(var(--c) * 0.2) calc(var(--c) * 0.4) rgba(0, 0, 0, 0.35);
  opacity: 0.92;
}
:root.editing-layout .grid .tile.lifted::after { border-color: var(--knob-blue); border-style: solid; }
@keyframes tile-wait { 50% { filter: brightness(0.97); } }

/* The bar over the panel while editing the layout or learning MIDI. */
.float-bar {
  position: fixed;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(var(--vw) - 24px);
  padding: 7px 8px 7px 14px;
  border: 1px solid var(--panel-line);
  border-radius: 12px;
  background: rgba(30, 32, 36, 0.94);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.45);
  font-size: 13px;
  color: var(--text);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.float-bar[hidden] { display: none; }
.float-bar .btn { height: 28px; padding: 0 12px; }
.float-text { flex: 1; min-width: 0; line-height: 1.35; }
.float-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--knob-orange); box-shadow: 0 0 8px var(--knob-orange); }
.float-dot.blue { background: var(--knob-blue); box-shadow: 0 0 8px var(--knob-blue); }

/* A file held over the page. */
.drop-hint {
  position: fixed;
  inset: 10px;
  z-index: 40;
  display: grid;
  place-items: center;
  border: 2px dashed var(--knob-blue);
  border-radius: 18px;
  background: rgba(20, 30, 45, 0.55);
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 120ms;
}
.drop-hint span { padding: 10px 18px; border-radius: 10px; background: rgba(15, 17, 20, 0.85); }
:root.dropping .drop-hint { opacity: 1; visibility: visible; }

.pref-row { margin-top: 10px; }
.row.tight { gap: 6px; }
.row.tight .btn { height: 30px; padding: 0 11px; }
/* Settings, Files: the three exports share one row, which the drawer's width only just holds. */
.row.exports .btn { flex: 1 1 auto; padding: 0 6px; font-size: 12.5px; }
.row.exports + .row { margin-top: 6px; }
.btn[aria-pressed="true"] { border-color: var(--knob-blue); background: rgba(88, 160, 243, 0.16); color: #eef0f2; }

/* ------------------------------------------------- the keys sheet (keys.js) */

.sheet-head .tabs { margin: 0 auto 0 18px; }
.tabs { display: inline-flex; padding: 2px; border: 1px solid #3b3f46; border-radius: 8px; background: #1a1c1f; }
.tabs button {
  padding: 5px 14px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-soft);
  font: 500 12.5px var(--sans);
  cursor: pointer;
}
.tabs button:hover { color: var(--text); }
.tabs button[aria-selected="true"] { background: #34383e; color: #eef0f2; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.remap-hint { margin: 0; font-size: 12.5px; color: var(--text-soft); }
.keys-foot { display: flex; align-items: center; gap: 14px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--panel-line); }
.keys-foot .btn { margin-left: auto; }
.bindings { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.binding {
  display: grid;
  grid-template-columns: 112px 20px 1fr auto 30px;
  align-items: center;
  gap: 10px;
  padding: 6px 6px 6px 12px;
  border: 1px solid #33373d;
  border-radius: 8px;
  background: #26292e;
  font-size: 13px;
}
.binding.fresh { border-color: var(--knob-blue); animation: fresh 1.6s ease-out; }
@keyframes fresh { from { background: rgba(88, 160, 243, 0.2); } }
.binding-from { font: 500 12px var(--mono); color: var(--text); }
.binding-arrow { width: 18px; height: 18px; fill: none; stroke: var(--text-faint); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.binding-to { font-weight: 600; color: var(--text); }
.binding-to.t-blue { color: var(--knob-blue); }
.binding-to.t-green { color: var(--knob-green); }
.binding-to.t-orange { color: var(--knob-orange); }
.binding-mode {
  height: 28px;
  padding: 0 26px 0 9px;
  border: 1px solid #3b3f46;
  border-radius: 6px;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5' fill='none' stroke='%238f959d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 8px center / 11px 11px,
    #1a1c1f;
  color: var(--text-soft);
  font: 12px var(--sans);
  -webkit-appearance: none;
  appearance: none;
}
.binding-mode-none { display: block; }
.binding-empty { padding: 18px; border: 1px dashed #383c43; border-radius: 8px; font-size: 12.5px; line-height: 1.5; color: var(--text-soft); text-align: center; }

/* ------------------------------------------------ the library (library.js) */

/* The library wears the panel's skin: light on the light unit, dark on the dark. */
:root,
:root[data-skin="light"] {
  --sheet-bg: #f2f3f5;
  --sheet-card: #ffffff;
  --sheet-line: #e0e3e7;
  --sheet-ink: #1f2328;
  --sheet-ink-soft: #59606a;
  --sheet-ink-faint: #8a9099;
  --sheet-field: #ffffff;
  --sheet-chip: #e6e8eb;
  --sheet-shadow: 0 30px 80px rgba(0, 0, 0, 0.4);
  --card-shadow: 0 1px 2px rgba(20, 24, 30, 0.05), 0 4px 14px rgba(20, 24, 30, 0.05);
  --card-lift: 0 2px 4px rgba(20, 24, 30, 0.06), 0 14px 30px rgba(20, 24, 30, 0.1);
  --art-base: #ffffff;
  --kind-synth: #2f80ea;
  --kind-drum: #13923f;
  --kind-sample: #d63b57;
  --kind-skin: #e8591a;
  --in-use: #13923f;
}
:root[data-skin="dark"] {
  --sheet-bg: #222427;
  --sheet-card: #2c2f33;
  --sheet-line: #3a3e44;
  --sheet-ink: #eceef1;
  --sheet-ink-soft: #a7adb5;
  --sheet-ink-faint: #7d838c;
  --sheet-field: #1b1d20;
  --sheet-chip: #383b41;
  --sheet-shadow: 0 30px 80px rgba(0, 0, 0, 0.65);
  --card-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  --card-lift: 0 2px 4px rgba(0, 0, 0, 0.3), 0 14px 30px rgba(0, 0, 0, 0.35);
  --art-base: #26292d;
  --kind-synth: #5c9df2;
  --kind-drum: #37b866;
  --kind-sample: #f0627a;
  --kind-skin: #f47a3e;
  --in-use: #3fc073;
}
.sheet.library {
  width: min(1080px, 100%);
  height: min(860px, 100%);
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
  border-color: var(--sheet-line);
  border-radius: 16px;
  background: var(--sheet-bg);
  color: var(--sheet-ink);
  box-shadow: var(--sheet-shadow);
}
.lib-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 24px 28px 6px; }
.sheet.library h2 { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; color: var(--sheet-ink); }
.lib-sub { margin: 5px 0 0; max-width: 620px; font-size: 13.5px; line-height: 1.45; color: var(--sheet-ink-soft); }
.sheet.library .close { color: var(--sheet-ink-soft); }
.sheet.library .close:hover { background: var(--sheet-chip); color: var(--sheet-ink); }
.lib-bar { display: flex; align-items: center; gap: 12px; padding: 14px 28px 14px; border-bottom: 1px solid var(--sheet-line); }
.lib-filters { display: flex; gap: 6px; flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.lib-filters::-webkit-scrollbar { display: none; }
.lib-filters button {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 32px;
  padding: 0 13px;
  border: 1px solid var(--sheet-line);
  border-radius: 999px;
  background: var(--sheet-card);
  color: var(--sheet-ink-soft);
  font: 550 13px var(--sans);
  cursor: pointer;
  transition: background 120ms, color 120ms, border-color 120ms;
}
.lib-filters button:hover { color: var(--sheet-ink); border-color: var(--sheet-ink-faint); }
.lib-filters button i { width: 8px; height: 8px; border-radius: 50%; background: var(--k); }
.lib-filters button[aria-pressed="true"] { background: var(--sheet-ink); border-color: var(--sheet-ink); color: var(--sheet-bg); }
.lib-count { font: 500 11px var(--mono); opacity: 0.6; }
.lib-count:empty { display: none; }
.k-synth { --k: var(--kind-synth); }
.k-drum { --k: var(--kind-drum); }
.k-sample { --k: var(--kind-sample); }
.k-skin { --k: var(--kind-skin); }
.lib-search {
  flex: none;
  display: flex;
  align-items: center;
  gap: 7px;
  width: 210px;
  height: 34px;
  padding: 0 11px;
  border: 1px solid var(--sheet-line);
  border-radius: 10px;
  background: var(--sheet-field);
  color: var(--sheet-ink-faint);
}
.lib-search:focus-within { border-color: var(--kind-synth); box-shadow: 0 0 0 3px color-mix(in srgb, var(--kind-synth) 20%, transparent); }
.lib-search svg { flex: none; width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.lib-search input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--sheet-ink); font: 13.5px var(--sans); }
.lib-search input::placeholder { color: var(--sheet-ink-faint); }
.lib-list { flex: 1; min-height: 0; overflow-y: auto; list-style: none; margin: 0; padding: 6px 28px 28px; scroll-behavior: smooth; }
.lib-section { margin-top: 22px; }
.lib-shead { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; margin: 0 2px 12px; }
.lib-shead h3 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -0.015em; color: var(--sheet-ink); }
.lib-shead span { font-size: 12.5px; color: var(--sheet-ink-faint); }
.lib-more {
  margin-left: auto;
  border: 0;
  background: transparent;
  color: var(--kind-synth);
  font: 600 13px var(--sans);
  cursor: pointer;
}
.lib-more:hover { text-decoration: underline; }
.lib-none { margin: 0 2px; padding: 16px 18px; border: 1px dashed var(--sheet-line); border-radius: 12px; font-size: 13px; color: var(--sheet-ink-faint); }
.lib-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 16px; }
.lib-grid.feature { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }

.lib-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--sheet-line);
  border-radius: 14px;
  background: var(--sheet-card);
  box-shadow: var(--card-shadow);
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}
.lib-card:hover { transform: translateY(-2px); box-shadow: var(--card-lift); }
.lib-card.busy { opacity: 0.65; pointer-events: none; }
.lib-card.in-use { border-color: color-mix(in srgb, var(--in-use) 55%, var(--sheet-line)); }
.lib-art {
  position: relative;
  aspect-ratio: 2 / 1;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 85% 0%, color-mix(in srgb, var(--k) 16%, transparent), transparent 60%),
    linear-gradient(170deg, color-mix(in srgb, var(--k) 12%, var(--art-base)), color-mix(in srgb, var(--k) 4%, var(--art-base)));
  color: var(--k);
}
.feature .lib-art { aspect-ratio: 16 / 9; }
.art-pic, .art-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.art-pic { opacity: 0; transition: opacity 260ms ease; }
.lib-art.drawn .art-pic { opacity: 1; }
.art-ph { position: absolute; left: 50%; top: 50%; width: 36px; height: 36px; margin: -18px 0 0 -18px; fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round; opacity: 0.3; transition: opacity 200ms; }
.lib-art.drawn .art-ph { opacity: 0; }
.art-wave { fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.art-mid { fill: none; stroke: currentColor; stroke-width: 1; stroke-dasharray: 2 4; opacity: 0.22; vector-effect: non-scaling-stroke; }
.art-env-fill { fill: currentColor; opacity: 0.1; }
.art-env { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linejoin: round; opacity: 0.5; vector-effect: non-scaling-stroke; }
.art-hit { fill: currentColor; }
.art-bars { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; vector-effect: non-scaling-stroke; opacity: 0.9; }
.art-hits {
  position: absolute;
  left: 8px;
  right: calc(8px + 21.7%);
  bottom: 9px;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  font: 500 9.5px var(--mono);
  text-align: center;
  color: color-mix(in srgb, var(--k) 70%, var(--sheet-ink));
}
.art-hits span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 2px; }
.art-top { position: absolute; top: 10px; left: 10px; right: 10px; display: flex; gap: 5px; overflow: hidden; }
.art-top > * { flex: none; }
.in-use .art-top { right: 96px; }
.in-use .art-chip { display: none; }
.art-kind, .art-chip, .art-use {
  padding: 3px 8px;
  border-radius: 999px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  line-height: 1.25;
}
.art-kind { background: color-mix(in srgb, var(--k) 16%, transparent); color: var(--k); }
.art-chip { background: color-mix(in srgb, var(--sheet-card) 70%, transparent); color: var(--sheet-ink-soft); letter-spacing: 0.04em; }
.art-use {
  position: absolute;
  top: 10px;
  right: 10px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--in-use);
  color: #fff;
}
.art-use::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: #fff; }
.lib-play {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--k);
  color: #fff;
  box-shadow: 0 4px 12px color-mix(in srgb, var(--k) 45%, transparent);
  cursor: pointer;
  transition: transform 120ms ease, filter 120ms;
}
.lib-play:hover { transform: scale(1.07); filter: brightness(1.08); }
.lib-play svg { width: 16px; height: 16px; fill: currentColor; }
.lib-play .i-stop, .lib-play.on .i-play { display: none; }
.lib-play.on .i-stop { display: inline; }
.lib-play.on { animation: lib-pulse 1.2s ease-in-out infinite; }
@keyframes lib-pulse { 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--k) 22%, transparent); } }
.lib-play:focus-visible, .lib-btn:focus-visible, .lib-filters button:focus-visible { outline: 2px solid var(--kind-synth); outline-offset: 2px; }

/* A skin's picture: the panel in small, in the panel's own colours. */
.sk-body { fill: var(--body-top); stroke: color-mix(in srgb, var(--sheet-ink) 14%, transparent); stroke-width: 0.8; }
.sk-tile, .sk-key { fill: var(--tile-hi); stroke: color-mix(in srgb, var(--sheet-ink) 10%, transparent); stroke-width: 0.6; }
.sk-key { fill: color-mix(in srgb, var(--tile-hi) 60%, #fff); }
:root[data-skin="dark"] .sk-key { fill: color-mix(in srgb, var(--tile-hi) 80%, #fff); }
.sk-speaker { fill: color-mix(in srgb, var(--tile-lo) 85%, var(--sheet-ink)); }
.sk-screen { fill: #121417; }
.sk-wkey { fill: #fbfbfc; stroke: rgba(0, 0, 0, 0.16); stroke-width: 0.5; }
.sk-knob { stroke: rgba(0, 0, 0, 0.12); stroke-width: 0.5; }
.sk-blue { fill: var(--knob-blue); }
.sk-green { fill: var(--knob-green); }
.sk-white.sk-knob, .sk-knob.sk-white { fill: #f4f5f6; }
.sk-orange { fill: var(--knob-orange); }
.sk-moved { fill: var(--kind-skin); stroke: none; }

.lib-body { flex: 1; display: flex; flex-direction: column; gap: 3px; padding: 12px 14px 14px; min-width: 0; }
.lib-title { display: flex; align-items: center; gap: 6px; min-width: 0; }
.lib-name { font-size: 15px; font-weight: 650; letter-spacing: -0.01em; color: var(--sheet-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feature .lib-name { font-size: 17px; }
.lib-mark { flex: none; display: inline-grid; place-items: center; width: 16px; height: 16px; border-radius: 50%; background: var(--in-use); color: #fff; }
.lib-mark svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.lib-by { font-size: 11.5px; color: var(--sheet-ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lib-desc {
  margin: 4px 0 2px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--sheet-ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.feature .lib-desc { -webkit-line-clamp: 3; font-size: 13px; }
.lib-spec { display: flex; flex-wrap: wrap; gap: 3px 9px; margin-top: 4px; font: 400 10.5px/1.5 var(--mono); color: var(--sheet-ink-faint); }
.lib-spec:empty { display: none; }
.lib-spec b { font-weight: 600; color: var(--k); }
.lib-spec i { font-style: normal; color: var(--sheet-ink-soft); }
.lib-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.lib-tag { padding: 1px 7px; border-radius: 5px; background: var(--sheet-chip); font-size: 10.5px; color: var(--sheet-ink-soft); }
.lib-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: auto; padding-top: 10px; }
.lib-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 13px;
  border: 1px solid var(--sheet-line);
  border-radius: 8px;
  background: var(--sheet-field);
  color: var(--sheet-ink);
  font: 600 12.5px var(--sans);
  cursor: pointer;
  white-space: nowrap;
  transition: filter 120ms, border-color 120ms;
}
.lib-btn:hover { border-color: var(--sheet-ink-faint); }
.lib-btn.primary { border-color: var(--sheet-ink); background: var(--sheet-ink); color: var(--sheet-bg); }
.lib-btn.primary:hover { filter: brightness(1.2); }
.lib-btn.done { border-color: transparent; background: color-mix(in srgb, var(--in-use) 14%, transparent); color: var(--in-use); }
.lib-btn.done svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.lib-btn.quiet { border-color: transparent; background: transparent; color: var(--sheet-ink-faint); font-weight: 500; padding: 0 6px; }
.lib-btn.quiet:hover { color: var(--kind-sample); }
.lib-empty { padding: 60px 20px; text-align: center; font-size: 13.5px; color: var(--sheet-ink-faint); }
.lib-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 28px 13px;
  border-top: 1px solid var(--sheet-line);
  font-size: 12px;
  color: var(--sheet-ink-faint);
}
.lib-foot a { flex: none; color: var(--sheet-ink-soft); font-weight: 600; text-decoration: none; cursor: pointer; }
.lib-foot a:hover { color: var(--sheet-ink); text-decoration: underline; }
.pref-select { width: auto !important; min-width: 160px; max-width: 60%; }
@media (max-width: 640px) {
  #library-overlay { padding: 0; }
  .sheet.library { width: 100%; height: 100%; border-radius: 0; border: 0; }
  .lib-head { padding: 18px 16px 2px; }
  .sheet.library h2 { font-size: 23px; }
  .lib-sub { font-size: 12.5px; }
  .lib-bar { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px 16px 12px; }
  .lib-search { width: auto; }
  .lib-list { padding: 0 16px 22px; }
  .lib-section { margin-top: 18px; }
  .lib-shead h3 { font-size: 17px; }
  .lib-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .lib-grid.feature { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; gap: 12px; margin: 0 -16px; padding: 2px 16px 6px; scrollbar-width: none; }
  .lib-grid.feature::-webkit-scrollbar { display: none; }
  .lib-grid.feature .lib-card { flex: none; width: 78%; scroll-snap-align: center; }
  .lib-body { padding: 10px 11px 11px; }
  .lib-name { font-size: 14px; }
  .lib-desc { font-size: 12px; }
  .lib-btn { flex: 1 1 auto; justify-content: center; height: 30px; padding: 0 8px; font-size: 12px; }
  .lib-play { width: 30px; height: 30px; right: 8px; bottom: 8px; }
  .art-hits { font-size: 8.5px; }
  .lib-foot { padding: 10px 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .drawer, .overlay, .cap, .led, .bk, .wk .bar, .kcap { transition: none; }
  :root.editing-layout .grid .tile[data-tile], :root.editing-layout .grid > .tile, :root.editing-layout .keyboard,
  .remap-pick .cap, .remap-pick .enc-ring, .remap-pick .vol-knob, .wk.remap-pick .bar, .bk.remap-pick { animation: none; transition: none; }
}

/* --------------------------------------------------- hints on hover (hints.js) */

/* The screen's own speech bubble, off the screen: black, a thin white line round
   it, and a tail pointing at the key (--tail from the bubble's left). */
.hint-bubble {
  --line: #f2f4f6;
  position: fixed;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(330px, calc(var(--vw) - 16px));
  padding: 8px 12px 9px;
  border: 1.2px solid var(--line);
  border-radius: 9px;
  background: #000;
  color: var(--line);
  font: 500 12.5px/1.35 var(--sans);
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 110ms ease-out, transform 110ms ease-out;
}
.hint-bubble.open { opacity: 1; transform: none; }
.hint-bubble.below { transform: translateY(-3px); }
.hint-bubble.below.open { transform: none; }
.hint-bubble::before,
.hint-bubble::after {
  content: "";
  position: absolute;
  left: calc(var(--tail, 24px) - 7px);
  top: 100%;
  border: 7px solid transparent;
  border-bottom: 0;
  border-top-color: var(--line);
}
.hint-bubble::after { margin-top: -1.6px; border-top-color: #000; }
.hint-bubble.below::before,
.hint-bubble.below::after { top: auto; bottom: 100%; border: 7px solid transparent; border-top: 0; border-bottom-color: var(--line); }
.hint-bubble.below::after { margin-bottom: -1.6px; border-bottom-color: #000; }
.hb-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.hb-body b { font-weight: 600; font-size: 12.5px; }
.hb-body span { color: #c5cad0; }
.hb-body .hb-shift { margin-top: 2px; font-size: 11.5px; color: var(--text-soft); }
/* The small drawings: keys as rings, the lit one filled in its colour. */
.hb-art { flex: none; overflow: visible; }
.hb-art .cap { fill: none; stroke: #5a6068; stroke-width: 1.1; }
.hb-art .cap.on { stroke: currentColor; fill: currentColor; fill-opacity: 0.18; }
.hb-art .ico { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.hb-art .dim { color: #6a7078; }
.hb-art .blue, .hb-art .cap.blue { color: #4f9dff; }
.hb-art .green, .hb-art .cap.green { color: #3fdc7e; }
.hb-art .orange, .hb-art .cap.orange { color: #ff6a2b; }
.hb-art .white, .hb-art .cap.white { color: #f2f4f6; }
.hb-art .lbl { fill: #8e949c; font: 600 7.5px var(--sans); text-anchor: middle; }
.hb-art .lbl.small { font-size: 6.5px; text-anchor: start; }
.hb-art .lbl.on { fill: #f2f4f6; }
.hb-art .track { stroke: #3a3f46; stroke-width: 1; }
.hb-art .track.on { stroke: #8e949c; }
.hb-art .take { stroke: #6a7078; stroke-width: 3; stroke-linecap: round; }
.hb-art .take.on { stroke: #ff6a2b; }
.hb-art .trick { fill: none; stroke: currentColor; stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round; }
.hb-art .knob { stroke: none; }
.hb-art .knob.blue { fill: #58a0f3; }
.hb-art .knob.green { fill: #3fd16a; }
.hb-art .knob.white { fill: #f7f7f5; }
.hb-art .knob.orange { fill: #ff6420; }
.hb-art .slot { stroke: rgba(0, 0, 0, 0.45); stroke-width: 3; stroke-linecap: round; }
@media (prefers-reduced-motion: reduce) { .hint-bubble { transition: none; } }

/* ------------------------------------------------ the skin editor (layouts.js) */

:root.editing-layout { --room-top: 56px; --room-y: 142px; }
/* The vertical skin keeps its tray beside it, where the window has room for one. */
@media (min-width: 620px) {
  :root.editing-layout[data-layout="vertical"] { --room-y: 0px; --room-x: 244px; }
}
/* Nothing in the bar works while editing: the panel is a layout, not an instrument. */
:root.editing-layout .status-bar { opacity: 0.35; pointer-events: none; }

/* Tiles glide to where they go; the one held follows the pointer. */
:root.editing-layout .grid > .tile[data-tile],
:root.editing-layout .keyboard { transition: left 170ms ease, top 170ms ease, opacity 170ms; }
:root.editing-layout .grid .tile.lifted,
:root.editing-layout .keyboard.lifted { transition: none; }
:root.editing-layout .keyboard { cursor: grab; }
:root.editing-layout .keyboard::after {
  content: "";
  position: absolute;
  inset: calc(var(--c) * 0.035);
  z-index: 2;
  border: 1.5px dashed color-mix(in srgb, var(--knob-blue) 55%, transparent);
  border-radius: calc(var(--c) * 0.06);
  pointer-events: none;
}
:root.editing-layout .keyboard.lifted {
  z-index: 6;
  cursor: grabbing;
  opacity: 0.92;
  filter: drop-shadow(0 calc(var(--c) * 0.2) calc(var(--c) * 0.3) rgba(0, 0, 0, 0.35));
}
:root.editing-layout .keyboard.lifted::after { border-style: solid; border-color: var(--knob-blue); }
:root.editing-layout .grid .tile.lifted { z-index: 6; }
/* A tile making room for the one held, and one that would go to the tray. */
:root.editing-layout .grid .tile.making-room::after,
:root.editing-layout .keyboard.making-room::after { border-style: solid; border-color: color-mix(in srgb, var(--knob-blue) 80%, transparent); }
:root.editing-layout .grid .tile.to-tray,
:root.editing-layout .keyboard.to-tray { opacity: 0.45; }
:root.editing-layout .grid .tile.to-tray::after,
:root.editing-layout .keyboard.to-tray::after { border: 2px solid var(--knob-orange); }
/* An empty cell: a plain tile, which shows as a slot while editing. */
:root.editing-layout .tile.blank.filler {
  background: color-mix(in srgb, var(--tile-lo) 82%, var(--seam-color));
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
}
:root.editing-layout .tile.blank.filler::after {
  content: "";
  position: absolute;
  inset: calc(var(--c) * 0.1);
  border: 1.5px dashed color-mix(in srgb, var(--ink-soft) 45%, transparent);
  border-radius: calc(var(--c) * 0.1);
}
/* Where the tile held will land. */
.drop-spot {
  position: absolute;
  z-index: 5;
  left: calc(var(--x) * var(--c) + var(--seam) / 2);
  top: calc(var(--y) * var(--c) + var(--seam) / 2);
  width: calc(var(--w) * var(--c) - var(--seam));
  height: calc(var(--h) * var(--c) - var(--seam));
  border: 2px dashed var(--knob-blue);
  border-radius: calc(var(--c) * 0.08);
  background: color-mix(in srgb, var(--knob-blue) 16%, transparent);
  pointer-events: none;
  transition: left 90ms ease-out, top 90ms ease-out;
}
.drop-spot.warn { border-color: var(--knob-orange); background: color-mix(in srgb, var(--knob-orange) 14%, transparent); }
.drop-spot[hidden] { display: none; }

/* The tile picked: a handle at its corner resizes it, and two buttons turn it. */
:root.editing-layout .grid .tile.picked::after,
:root.editing-layout .keyboard.picked::after { border: 2px solid var(--knob-blue); }
.tile-tools {
  position: absolute;
  z-index: 7;
  left: calc(var(--x) * var(--c));
  top: calc(var(--y) * var(--c));
  width: calc(var(--w) * var(--c));
  height: calc(var(--h) * var(--c));
  pointer-events: none;
}
.tile-tools[hidden] { display: none; }
.tile-tools > * { pointer-events: auto; }
.tile-tools { --tt: 26px; }
/* Its size, under it; the turn buttons side by side above it, or under it (under the
   corner handle) for a tile in the top row, so they stay on the panel. */
.tt-size {
  position: absolute;
  left: 50%;
  top: calc(100% + 6px);
  translate: -50% 0;
  padding: 2px 7px;
  border-radius: 6px;
  background: #23262b;
  color: #eef0f2;
  font: 600 11px/16px var(--sans);
  white-space: nowrap;
  pointer-events: none;
}
.tt-turns {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 6px);
  translate: -50% 0;
  display: flex;
  gap: 4px;
  pointer-events: none;
}
.tile-tools.low .tt-turns { bottom: auto; top: calc(100% + var(--tt) * 0.45 + 4px); }
.tile-tools.low .tt-size { top: calc(100% + var(--tt) * 1.45 + 12px); }
.tt-turn {
  flex: none;
  width: var(--tt);
  height: var(--tt);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--knob-blue);
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  cursor: pointer;
  pointer-events: auto;
}
.tt-turn[hidden] { display: none; }
.tt-turn svg { width: 58%; height: 58%; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.tt-grip {
  position: absolute;
  right: calc(var(--tt) * -0.45);
  bottom: calc(var(--tt) * -0.45);
  width: calc(var(--tt) * 0.85);
  height: calc(var(--tt) * 0.85);
  border: 3px solid #fff;
  border-radius: 50%;
  background: var(--knob-blue);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  cursor: nwse-resize;
  touch-action: none;
}
/* Larger to touch on a phone. */
@media (pointer: coarse) { .tile-tools { --tt: 34px; } }

/* The panel's edges while editing: drag them to add or take away columns and rows. */
.grid-grip { display: none; position: absolute; z-index: 8; touch-action: none; }
:root.editing-layout .grid-grip { display: block; }
.grid-grip.right {
  top: calc(var(--c) * 0.171);
  height: calc(var(--c) * var(--rows));
  left: calc(var(--c) * (var(--cols) + 0.171) - 7px);
  width: 14px;
  cursor: ew-resize;
}
.grid-grip.bottom {
  left: calc(var(--c) * 0.171);
  width: calc(var(--c) * var(--cols));
  top: calc(var(--c) * (var(--rows) + 0.171) - 7px);
  height: 14px;
  cursor: ns-resize;
}
.grid-grip.corner {
  left: calc(var(--c) * (var(--cols) + 0.171) - 11px);
  top: calc(var(--c) * (var(--rows) + 0.171) - 11px);
  width: 22px;
  height: 22px;
  cursor: nwse-resize;
}
.grid-grip.right::after,
.grid-grip.bottom::after {
  content: "";
  position: absolute;
  border-radius: 3px;
  background: color-mix(in srgb, var(--knob-blue) 70%, transparent);
}
.grid-grip.right::after { left: 5px; top: 50%; width: 4px; height: 44px; margin-top: -22px; }
.grid-grip.bottom::after { top: 5px; left: 50%; height: 4px; width: 44px; margin-left: -22px; }
.grid-grip.corner::after {
  content: "";
  position: absolute;
  inset: 4px;
  border: 3px solid #fff;
  border-radius: 50%;
  background: var(--knob-blue);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.grid-grip:hover::after { background: var(--knob-blue); }
@media (pointer: coarse) {
  .grid-grip.right { width: 26px; left: calc(var(--c) * (var(--cols) + 0.171) - 13px); }
  .grid-grip.bottom { height: 26px; top: calc(var(--c) * (var(--rows) + 0.171) - 13px); }
  .grid-grip.right::after { left: 11px; }
  .grid-grip.bottom::after { top: 11px; }
}

/* The small pencil on the panel's edge: it opens the skin editor. */
.panel-edit {
  position: absolute;
  z-index: 3;
  right: calc(var(--c) * 0.17);
  top: calc(var(--c) * 0.36);
  width: calc(var(--c) * 0.42);
  height: calc(var(--c) * 0.42);
  min-width: 20px;
  min-height: 20px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--model-ink);
  opacity: 0.55;
  cursor: pointer;
  transition: opacity 120ms, background 120ms;
}
.panel-edit:hover { opacity: 1; background: color-mix(in srgb, var(--model-ink) 16%, transparent); }
.panel-edit svg { width: 62%; height: 62%; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
:root[data-layout="vertical"] .panel-edit { top: auto; right: auto; left: 50%; translate: -50% 0; bottom: calc(var(--c) * 0.2); }
:root.editing-layout .panel-edit { display: none; }

/* The bar along the top while editing. */
.skin-bar { gap: 10px; padding: 6px 7px 6px 14px; }
.sb-title { flex: none; font-weight: 600; font-size: 13px; color: #eef0f2; }
.sb-menu-wrap { position: relative; flex: none; }
.sb-skin {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 220px;
  height: 30px;
  padding: 0 8px 0 11px;
  border: 1px solid #454a52;
  border-radius: 8px;
  background: #2b2e33;
  color: #eef0f2;
  font: 600 12.5px var(--sans);
  cursor: pointer;
}
.sb-skin span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-skin:hover, .sb-skin[aria-expanded="true"] { border-color: var(--knob-blue); background: #31353b; }
.sb-skin svg { flex: none; width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: 0.8; }
.sb-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 2;
  width: 270px;
  padding: 6px;
  border: 1px solid var(--panel-line);
  border-radius: 11px;
  background: #24272b;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
}
.sb-menu[hidden] { display: none; }
.sb-menu-head { margin: 4px 8px 6px; font-size: 11.5px; line-height: 1.4; color: var(--text-faint); }
.sb-menu > button {
  display: flex;
  width: 100%;
  align-items: center;
  height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text);
  font: 500 13px var(--sans);
  text-align: left;
  cursor: pointer;
}
.sb-menu > button:hover:not(:disabled) { background: #31353b; }
.sb-menu > button:disabled { color: var(--text-faint); cursor: default; }
.sb-menu > button[hidden] { display: none; }
.sb-menu > button.current::after { content: "on now"; margin-left: auto; font-size: 11px; color: var(--text-faint); }
.sb-menu > button.danger { color: #ff8f7a; }
.sb-menu hr { margin: 5px 6px; border: 0; border-top: 1px solid var(--panel-line); }
.sb-name-form { display: flex; flex-direction: column; gap: 7px; margin: 2px 4px 6px; padding: 8px; border-radius: 8px; background: #1b1d20; }
.sb-name-form[hidden] { display: none; }
.sb-name-form input {
  height: 32px;
  padding: 0 10px;
  border: 1px solid #454a52;
  border-radius: 7px;
  background: #121315;
  color: #eef0f2;
  font: 500 13px var(--sans);
  -webkit-user-select: text;
  user-select: text;
}
.sb-name-form input:focus { outline: none; border-color: var(--knob-blue); }
.sb-name-form .btn { height: 28px; }
.skin-bar { width: min(980px, calc(var(--vw) - 24px)); }
.skin-bar .float-text { flex: 1 1 240px; color: var(--text-soft); font-size: 12.5px; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.skin-bar .float-text.warn, .rb-text.warn { color: #ffb08a; }
.skin-bar .btn svg { width: 15px; height: 15px; margin: 0 5px -3px -2px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 760px) {
  .skin-bar .float-text:not(.warn) { display: none; }
  .skin-bar .sb-title { display: none; }
}

/* The tray: under the panel, or beside the vertical skin. */
.skin-tray {
  flex: none;
  width: calc(var(--c) * (var(--cols) + var(--edge-x)));
  min-width: min(560px, calc(var(--vw) - 32px));
  height: 128px;
  display: flex;
  flex-direction: column;
  padding: 8px 12px 10px;
  border: 1.5px dashed #3d424a;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.025);
  transition: border-color 120ms, background 120ms;
}
.skin-tray[hidden] { display: none; }
.skin-tray.over { border-color: var(--knob-blue); background: rgba(88, 160, 243, 0.1); }
.skin-tray.nudge { animation: tray-nudge 420ms ease-in-out; border-color: var(--knob-orange); }
@keyframes tray-nudge { 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }
.tray-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 6px; }
.tray-title { flex: none; font-size: 12px; font-weight: 600; color: var(--text); letter-spacing: 0.02em; }
.tray-title b { margin-left: 4px; padding: 0 6px; border-radius: 999px; background: var(--knob-orange); color: #1b0d05; font-size: 11px; }
.tray-title b:empty { display: none; }
.tray-hint { min-width: 0; font-size: 11.5px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tray-list { flex: 1; min-height: 0; display: flex; align-items: flex-start; gap: 14px; overflow-x: auto; overflow-y: hidden; padding: 2px 2px 0; }
.skin-tray.empty .tray-list::before {
  content: "Empty";
  margin: auto;
  font-size: 12px;
  color: #4b5058;
}
.tray-item { flex: none; display: flex; flex-direction: column; align-items: center; gap: 4px; cursor: grab; touch-action: none; }
.tray-box {
  position: relative;
  border-radius: calc(var(--c) * 0.12);
  background: var(--seam-color);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
}
.tray-box > * { pointer-events: none; }
.tray-box canvas { display: none; }
.tray-label { max-width: 120px; font-size: 11px; color: var(--text-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tray-item:hover .tray-box { outline: 2px solid var(--knob-blue); outline-offset: 2px; }
@media (min-width: 620px) {
  :root[data-layout="vertical"] .skin-tray {
    position: fixed;
    top: 70px;
    right: 16px;
    bottom: 54px;
    width: 220px;
    min-width: 0;
    height: auto;
  }
  :root[data-layout="vertical"] .tray-head { flex-direction: column; gap: 4px; }
  :root[data-layout="vertical"] .tray-hint { white-space: normal; }
  :root[data-layout="vertical"] .tray-list { flex-direction: column; align-items: center; overflow-x: hidden; overflow-y: auto; padding-top: 8px; }
}

/* ------------------------------------------------- remap mode (keys.js) */

:root.remapping { --room-top: 52px; }
@media (max-width: 860px) { :root.remapping { --room-top: 96px; } }
.remap-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  min-height: 52px;
  padding: 8px 12px 8px 18px;
  border-bottom: 1px solid rgba(88, 160, 243, 0.35);
  background: linear-gradient(180deg, #1c2533, #181e27);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  font-size: 13px;
  color: var(--text);
}
.remap-banner[hidden] { display: none; }
.rb-title { flex: none; font-weight: 700; letter-spacing: 0.01em; color: #eef4ff; }
.rb-tabs { flex: none; }
.rb-text { flex: 1 1 260px; min-width: 0; line-height: 1.35; color: #c9d1dc; }
.rb-text.busy { color: #8fc0ff; }
.rb-text.good { color: #7fe3a0; }
.rb-heard {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 10px;
  border: 1px solid #34404f;
  border-radius: 999px;
  background: #121821;
  font: 500 12px var(--mono);
  color: #c9d1dc;
}
.rb-heard[hidden] { display: none; }
.rb-heard i { width: 7px; height: 7px; border-radius: 50%; background: #3a4552; transition: background 300ms, box-shadow 300ms; }
.rb-heard.live i { background: var(--knob-green); box-shadow: 0 0 8px var(--knob-green); transition: none; }
.rb-heard span { color: var(--text-faint); }
.remap-banner .btn { height: 30px; padding: 0 13px; }
#btn-remap[aria-pressed="true"] { color: var(--text); background: rgba(88, 160, 243, 0.2); }
/* Settings, Keys and Library wait; help can be picked. */
:root.remapping .status-bar .bar-btn:not(.bar-key):not(#btn-remap) { opacity: 0.35; pointer-events: none; }

/* The controls that can be picked, the one picked, and one just changed. */
:root.remapping .unit .key, :root.remapping .unit .enc, :root.remapping .unit .volume,
:root.remapping .wk, :root.remapping .bk { cursor: pointer; }
:root.remapping .key:hover .cap, :root.remapping .wk:hover .bar, :root.remapping .bk:hover,
:root.remapping .enc:hover .enc-ring, :root.remapping .volume:hover .vol-knob {
  box-shadow: var(--cap-lift), 0 0 0 calc(var(--c) * 0.025) color-mix(in srgb, var(--knob-blue) 55%, transparent);
}
:root[data-remap-tab="midi"] .enc:hover .enc-ring, :root[data-remap-tab="midi"] .volume:hover .vol-knob { box-shadow: 0 0 0 calc(var(--c) * 0.03) color-mix(in srgb, var(--knob-blue) 55%, transparent); }
.remap-pick .cap, .wk.remap-pick .bar, .bk.remap-pick, .remap-pick .enc-ring, .remap-pick .vol-knob { animation: pick-wait 1.1s ease-in-out infinite; }
@keyframes pick-wait {
  0%, 100% { box-shadow: 0 0 0 calc(var(--c) * 0.035) var(--knob-blue), 0 0 calc(var(--c) * 0.2) color-mix(in srgb, var(--knob-blue) 60%, transparent); }
  50% { box-shadow: 0 0 0 calc(var(--c) * 0.06) color-mix(in srgb, var(--knob-blue) 70%, transparent), 0 0 calc(var(--c) * 0.3) color-mix(in srgb, var(--knob-blue) 40%, transparent); }
}
.bar-key.remap-pick { outline: 2px solid var(--knob-blue); outline-offset: 1px; }
.remap-flash .cap, .wk.remap-flash .bar, .bk.remap-flash, .remap-flash .enc-ring, .remap-flash .vol-knob { animation: picked 0.9s ease-out; }
.bar-key.remap-flash { animation: picked-bar 0.9s ease-out; }
@keyframes picked { from { box-shadow: 0 0 0 calc(var(--c) * 0.07) var(--knob-green), 0 0 calc(var(--c) * 0.35) var(--knob-green); } }
@keyframes picked-bar { from { background: rgba(63, 209, 106, 0.35); } }

/* A MIDI binding on its control, while remap mode shows MIDI. */
.mchip {
  position: absolute;
  z-index: 3;
  top: calc(var(--c) * 0.04);
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: max(15px, calc(var(--c) * 0.19));
  padding: 0 2px 0 6px;
  border-radius: 999px;
  background: var(--knob-blue);
  color: #fff;
  font: 600 max(9px, calc(var(--c) * 0.105)) / 1 var(--mono);
  white-space: nowrap;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.bk .mchip { top: auto; bottom: 100%; margin-bottom: 2px; }
.mchip-x {
  display: grid;
  place-items: center;
  width: max(12px, calc(var(--c) * 0.15));
  height: max(12px, calc(var(--c) * 0.15));
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.22);
  color: #fff;
  cursor: pointer;
}
.mchip-x:hover { background: rgba(0, 0, 0, 0.45); }
.mchip-x svg { width: 75%; height: 75%; fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; }
.mchip.chip-hit { animation: chip-hit 0.6s ease-out; }
@keyframes chip-hit { from { background: var(--knob-green); box-shadow: 0 0 calc(var(--c) * 0.2) var(--knob-green); } }

/* ---------------------------------------------------------- key letters (app.js) */

/* The computer key that plays each key: small and faint at the bottom of its cap. */
.kl {
  position: absolute;
  left: 50%;
  bottom: calc(var(--c) * 0.07);
  transform: translateX(-50%);
  font: 600 calc(var(--c) * 0.118) / 1 var(--mono);
  color: var(--ink-soft);
  opacity: 0.7;
  white-space: nowrap;
  pointer-events: none;
}
.skey .cap { padding-bottom: calc(var(--c) * 0.09); }
:root.no-letters:not([data-remap-tab="keys"]) .skey .cap { padding-bottom: 0; }
.kl.word { font-size: calc(var(--c) * 0.088); letter-spacing: 0.02em; }
.wk .kl { bottom: calc(var(--c) * 0.075); }
.bk .kl { bottom: calc(var(--c) * 0.1); color: rgba(255, 255, 255, 0.55); }
:root .key.rec .kl { color: rgba(255, 255, 255, 0.8); opacity: 1; }
:root[data-skin="dark"] .kl { opacity: 0.62; }
:root[data-skin="dark"] .bk .kl { color: rgba(255, 255, 255, 0.45); }
:root.no-letters .kl, :root.keys-away .kl { display: none; }
/* Remap mode shows every key's letter, as a badge. */
:root[data-remap-tab="keys"] .kl {
  display: block;
  padding: calc(var(--c) * 0.018) calc(var(--c) * 0.04);
  border-radius: calc(var(--c) * 0.05);
  background: color-mix(in srgb, var(--knob-blue) 22%, transparent);
  color: var(--ink);
  opacity: 1;
}
:root[data-remap-tab="keys"] .bk .kl { color: #fff; background: color-mix(in srgb, var(--knob-blue) 55%, transparent); }
:root[data-remap-tab="midi"] .kl { opacity: 0.35; }
