:root {
  /* Neutral greys and a cool accent, in the vein of a modern editor UI. The previous
     warm gold + serif scheme read as decorative; an editing tool should get out of the
     way of the picture it is showing. */
  --bg: #0c0c0e;
  --surface: #141417;
  --surface-2: #1e1e23;
  --surface-3: #2a2a31;
  --accent: #4d9dff;
  --accent-dim: rgba(77, 157, 255, 0.35);
  --text: #f2f3f5;
  --muted: rgba(242, 243, 245, 0.48);
  --success: #3ecf8e;
  --danger: #ff5f56;
  --border: rgba(242, 243, 245, 0.09);

  --font-display: var(--font-ui);
  --font-ui: 'Segoe UI Variable Display', -apple-system, 'Segoe UI', Roboto, Inter, sans-serif;
  --font-mono: ui-monospace, 'Cascadia Mono', Consolas, 'SF Mono', monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  overflow: hidden;
  user-select: none;
}

body {
  display: flex;
  flex-direction: column;
}

.hidden { display: none !important; }

button {
  font-family: inherit;
  cursor: pointer;
}

/* Topbar */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
  z-index: 20;
}

.topbar-left, .topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* An element whose colour is being derived rather than chosen. The swatch would
   otherwise show a stale colour that is not on screen. */
.element-colors label.is-auto input[type="color"] {
  opacity: 0.62;
  outline: 1px dashed rgba(242, 243, 245, 0.28);
  outline-offset: 2px;
}
.element-colors label.is-auto:hover input[type="color"] { opacity: 0.9; }
.element-colors label.is-auto > span {
  opacity: 0.5;
}
.element-colors label.is-auto > span::after {
  content: ' — auto';
  opacity: 0.75;
  font-style: italic;
}

/* Sub-heading inside the library. Deliberately quieter than .library-head so the
   hierarchy reads at a glance: one section, two kinds of thing. */
.library-sub {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--muted);
  opacity: 0.72;
  margin: 10px 0 4px;
}
.library-sub:first-of-type { margin-top: 4px; }
.library-sub-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.sub-help {
  border: 0;
  background: none;
  padding: 0;
  color: var(--muted);
  font: inherit;
  letter-spacing: 0.06em;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  transition: color 0.12s;
}
.sub-help:hover { color: var(--text); }

.guide-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(6, 6, 8, 0.62);
  backdrop-filter: blur(2px);
}
.guide {
  width: min(440px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
}
.guide-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--border);
}
.guide-top h2 { margin: 0; font-size: 14px; font-weight: 600; }
.guide-x {
  display: inline-flex;
  width: 22px; height: 22px;
  align-items: center; justify-content: center;
  border: 0; border-radius: 5px;
  background: none; color: var(--muted); cursor: pointer;
}
.guide-x svg { width: 13px; height: 13px; }
.guide-x:hover { color: var(--text); background: var(--surface-2); }
.guide-body {
  overflow-y: auto;
  padding: 4px 16px 16px;
  font-size: 12px;
  line-height: 1.55;
  color: rgba(242, 243, 245, 0.8);
}
.guide-body h3 {
  margin: 14px 0 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}
.guide-body p { margin: 0 0 6px; }
.guide-body b { color: var(--text); font-weight: 600; }
.guide-note { color: var(--muted); }

.asset-x, .bg-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 18px; height: 18px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--muted);
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.12s, color 0.12s, background 0.12s;
}
.asset-x svg, .bg-x svg { flex: 0 0 auto; width: 13px; height: 13px; stroke-width: 2.6; }
.asset-row:hover .asset-x, .bg-tile:hover .bg-x { opacity: 0.9; }
.asset-x:hover, .bg-x:hover { opacity: 1; color: #ff8a8a; background: rgba(255,120,120,0.14); }
.bg-x {
  position: absolute;
  top: 3px; right: 3px;
  background: rgba(6,8,12,0.72);
}

.bg-shelf {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
.bg-tile {
  position: relative;
  aspect-ratio: 200 / 112;
  border-radius: 5px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: #0b0d12 center/cover no-repeat;
  cursor: pointer;
  padding: 0;
}
.bg-tile:hover { border-color: var(--accent); }
.bg-tile.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.bg-tile span {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 3px 5px;
  font-size: 10px;
  text-align: left;
  color: #eef2f7;
  /* The clips are dark at the edges and light in the middle, so a flat caption bar
     is more legible here than text straight on the image. */
  background: linear-gradient(transparent, rgba(6,8,12,0.88));
}

.brand {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text);
  /* Sits apart from the actions rather than reading as another button. */
  padding-right: 12px;
  margin-right: 2px;
  border-right: 1px solid var(--border);
  user-select: none;
}
.brand-name { line-height: 1; }
.brand em { font-style: normal; color: var(--muted); font-weight: 500; }

.btn {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 13px;
}
.btn:hover { border-color: var(--accent-dim); color: var(--accent); }
/* A filled accent button must NOT inherit the outline button hover, which sets the
   TEXT to the accent colour — on an accent background that is blue on blue, which is
   what made the Export hover look broken. Filled buttons brighten instead. */
.btn-accent:hover {
  background: #6aaeff;
  color: #0b1220;
  border-color: transparent;
}
.btn-accent:active { background: #3d8ae6; }
.btn:disabled { opacity: 0.35; cursor: not-allowed; }
.btn-icon { padding: 6px 10px; }
.btn-accent {
  background: var(--accent);
  color: #0b1220;
  border: none;
  font-weight: 600;
  width: 100%;
  padding: 10px;
  margin-top: 14px;
  border-radius: 6px;
}
.btn-accent:disabled { background: var(--surface-2); color: var(--muted); }

.recent-wrap { position: relative; }
.recent-list {
  position: absolute;
  top: 32px;
  left: 0;
  min-width: 260px;
  max-height: 240px;
  overflow-y: auto;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  list-style: none;
  margin: 0;
  padding: 4px;
}
.recent-list li {
  padding: 6px 10px;
  font-size: 12px;
  border-radius: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.recent-list li:hover { background: var(--surface); color: var(--accent); }
.recent-list .empty { color: var(--muted); cursor: default; }

/* ---- Lucide icons (inlined; the CSP blocks any external asset host) ---- */
.icon {
  width: 16px;
  height: 16px;
  display: block;
  flex: 0 0 auto;
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.btn-icon .icon, .btn-round .icon, .btn-step .icon, .audio-x .icon { width: 15px; height: 15px; }
.btn-round .icon { width: 14px; height: 14px; }
/* Transport glyphs are conventionally solid, not outlined — an outline play button
   reads as "draw a triangle" rather than "play". The stroke has to go when filling:
   Lucide ships these with stroke-width 2, and filling on top of that inflates every
   shape by a pixel on each edge, which fattened the pause bars until they nearly
   merged and turned the stop square into a blob. */
#btn-play .icon, #btn-pause .icon, #btn-stop .icon {
  fill: currentColor;
  stroke: none;
}
/* The play triangle reads as visually off-centre when its bounding box is centred. */
#btn-play .icon { transform: translateX(1px); }
.audio-x { display: inline-flex; align-items: center; }

/* ---- Frame ratio: segmented toggle, first control in the bar ---- */
.segmented {
  display: flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 2px;
  gap: 2px;
}
.segment {
  background: none;
  border: 0;
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 5px 12px;
  border-radius: 4px;
}
.segment:hover { color: var(--text); }
.segment.active {
  background: var(--surface-3);
  color: var(--text);
  box-shadow: inset 0 0 0 1px rgba(242, 243, 245, 0.08);
}

/* ---- Sliders ----
   The knob is hidden until the control is hovered or focused, and the filled portion
   only takes the accent colour then. At rest the slider is a quiet line that states
   the value; it becomes an obviously grabbable control the moment you go for it. The
   fill has to be painted as a gradient because CSS cannot read an input's value — a
   --fill-pct custom property is kept in sync from JS. */
.slider {
  -webkit-appearance: none;
  appearance: none;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(
    to right,
    var(--fill-color, var(--text)) 0%,
    var(--fill-color, var(--text)) var(--fill-pct, 0%),
    var(--surface-3) var(--fill-pct, 0%),
    var(--surface-3) 100%
  );
  cursor: pointer;
}
.slider:hover, .slider:focus-visible { --fill-color: var(--accent); }
.slider:focus { outline: none; }
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--text);
  opacity: 0;
  transition: opacity 0.12s ease;
}
.slider:hover::-webkit-slider-thumb,
.slider:focus-visible::-webkit-slider-thumb,
.slider:active::-webkit-slider-thumb { opacity: 1; }

/* Collapsing the panel is what makes the 9:16 preview usable — it is nearly a third
   of the window, and while framing a shot you want the picture, not the numbers. */

/* ---- Three-column layout ----
   Left is everything you loaded, centre is the picture with the controls that frame it,
   right is what it is and how it looks. Time runs along the bottom. */
.assets {
  /* Wider than it was: the prompt box and its three actions now live here, and at
     210px the buttons wrapped one per line. */
  flex: 0 0 248px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow-y: auto;
}
/* One bar of actions. Equal widths so no single action reads as the primary one —
   which MIDI, audio and background all are, depending on what you are doing. */
.add-wrap { position: relative; }
.btn-add {
  width: 100%;
  justify-content: center;
  padding: 9px;
  font-size: 12px;
}

/* FIXED, not absolute. An absolutely positioned menu is clipped by .assets, which
   scrolls — see the note in the patch that introduced this. Placed from JS. */
.add-menu {
  position: fixed;
  z-index: 60;
  min-width: 210px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 5px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.55);
}
.add-menu .btn {
  width: 100%;
  justify-content: flex-start;
  border: 0;
  background: none;
  padding: 8px 10px;
  font-size: 12px;
}
.add-menu .btn span { display: inline; }
.add-menu .btn:hover { background: var(--surface); }

.library {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.bg-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* Room between the rule and the heading under it; at 4px they were touching. */
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--border);
}
.bg-block .prompt-box { width: 100%; box-sizing: border-box; }
.bg-block .prompt-actions { display: flex; gap: 4px; }
.bg-block .prompt-actions .btn { flex: 1 1 0; justify-content: center; padding: 6px 4px; }
.bg-block .prompt-actions .btn span { font-size: 11px; }
.bg-block .btn-wide { width: 100%; justify-content: center; }
/* Section headings in the assets column share one rhythm: a clear gap ABOVE, tying
   the heading to what follows rather than to what came before, and a small gap below.
   Without it the headings sat against the block above them and read as its footer. */
.library-head {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 6px 0 2px;
}
/* The first heading has the panel edge above it, so it needs no extra room. */
.assets > *:first-child .library-head,
.library:first-of-type .library-head { margin-top: 0; }
.assets .audio-chip { max-width: none; flex-wrap: wrap; border-radius: 8px; }
.assets .audio-name { max-width: 100%; }

/* A recent file is something you can click, so it gets the affordances of one, and
   the loaded row is MARKED rather than hidden — knowing which of your recents is open
   is the reason to show them together. */
.asset-row.clickable { cursor: pointer; }
.asset-row.clickable:hover { background: var(--surface); border-color: var(--accent-dim); }
.asset-row.active { border-color: var(--accent-dim); background: var(--surface); }
.asset-divider { height: 1px; background: var(--border); margin: 3px 2px; }

/* Project actions sit at the TOP: opening or saving a project is what you do before
   and after the work, not something to hunt for at the bottom of the column. */
/* Project actions live in the top bar now; these rules only style them there. */
.topbar-left { display: flex; align-items: center; gap: 6px; }
.topbar-left .btn { font-size: 12px; }

/* Loaded assets, one row each. */
.asset-list { display: flex; flex-direction: column; gap: 5px; }
.asset-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 8px;
  border-radius: 6px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 11px;
  min-width: 0;
}
.asset-row .icon { width: 14px; height: 14px; color: var(--muted); }
.asset-row .asset-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.asset-row .asset-meta { color: var(--muted); font-family: var(--font-mono); font-size: 10px; }
.asset-empty { font-size: 11px; color: var(--muted); padding: 4px 2px; }

.stage-col {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.player-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 14px;
  background: var(--surface);
  border-top: 1px solid var(--border);
}
/* ---- Export clip range ---- */
.dropdown-sep {
  height: 1px;
  margin: 6px 0;
  background: var(--border);
}
.range-block { padding: 2px 10px 8px; }
.range-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text);
  padding: 4px 0;
  cursor: pointer;
}
.range-fields { display: grid; gap: 7px; margin-top: 6px; }
.range-fields.off { opacity: 0.4; pointer-events: none; }
.range-fields > label {
  display: grid;
  grid-template-columns: 34px 1fr;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--muted);
}
.range-fields input[type="text"] {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  padding: 4px 7px;
}
.range-actions, .range-presets { display: flex; gap: 5px; }
.range-actions .btn, .range-presets .btn { flex: 1; justify-content: center; }
.range-readout {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* Said once, quietly, where a control would otherwise appear to be broken. */
.section-note {
  margin: -2px 0 10px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--muted);
}
#bg-section.na .style-row,
#bg-section.na .mini-label,
#bg-section.na .prompt-box,
#bg-section.na .prompt-actions,
#bg-section.na .prompt-facts { opacity: 0.4; pointer-events: none; }

.res-readout {
  margin-left: auto;
  margin-right: 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* ---- Panel tabs ---- */
.panel-tabs {
  display: flex;
  gap: 2px;
  padding: 10px 14px 0;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 2;
}
.panel-tab {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 7px 12px;
}
.panel-tab:hover { color: var(--text); }
.panel-tab.active { color: var(--text); border-bottom-color: var(--accent); }

.style-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  font-size: 12px;
  color: var(--muted);
}
.style-row .slider { width: 120px; }
/* A row that shows its value gives up some track for the number. */
.style-row.has-val .slider { width: 92px; }
.style-val {
  min-width: 30px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text);
}
.style-row input[type="checkbox"] { accent-color: var(--accent); width: 15px; height: 15px; }
.panel-pane .swatches { margin: 4px 0 12px; }

/* Which picker is which. Without these the three swatches look interchangeable and
   it is not obvious that they run low register to high. */
.element-colors {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 10px;
  margin: 4px 0 2px;
}
.element-colors label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  color: var(--muted);
  cursor: pointer;
}
.element-colors input[type="color"] {
  flex: 0 0 auto;
  width: 26px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: none;
  cursor: pointer;
}
.element-colors input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.element-colors input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 3px; }
.stop-captions {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
}
.stop-captions span {
  width: 34px;
  text-align: center;
  font-size: 9.5px;
  color: var(--muted);
  letter-spacing: 0.04em;
}
.stop-captions span.off { opacity: 0.3; }

.custom-colors { display: flex; align-items: center; gap: 6px; margin: 4px 0 4px; }
.custom-colors input[type="color"] {
  width: 34px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: none;
  cursor: pointer;
}
.custom-colors input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.custom-colors input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 3px; }
.custom-colors input.off { opacity: 0.25; pointer-events: none; }
#stop-count { margin-left: auto; }
#stop-count .segment { padding: 4px 9px; font-size: 11px; }

/* The musical facts, shown where they are actually used: they are what the Suggest
   button turns into a prompt. In the Data tab the same numbers were decoration. */
.prompt-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 6px 0 8px;
}
.prompt-fact {
  font-size: 10.5px;
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 3px 9px;
  white-space: nowrap;
}
.prompt-fact b { color: var(--text); font-weight: 600; }

.prompt-box {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 12px;
  line-height: 1.45;
  padding: 8px 10px;
  resize: vertical;
}
.prompt-box:focus { outline: none; border-color: var(--accent-dim); }
.prompt-actions { display: flex; gap: 6px; margin-top: 6px; }
.prompt-actions .btn { flex: 1 1 0; justify-content: center; padding: 5px 6px; }
.btn-wide { width: 100%; justify-content: center; margin-top: 8px; }
/* Hidden, not removed: renderAssets reads its textContent to build the library row,
   so it is still the place the current background name lives — it just no longer
   prints that name a second time under the loader. */
.bg-name { display: none; }

/* ---- Timeline ---- */
.timeline-row {
  flex: 0 0 auto;
  padding: 0 16px;
  background: var(--surface);
  border-top: 1px solid var(--border);
}
#timeline-canvas {
  display: block;
  width: 100%;
  /* Ruler + MIDI + AUDIO came to 104; the camera lane and its gap add 23. */
  height: 127px;
  cursor: pointer;
  touch-action: none;
}

/* ---- Backing audio track ---- */
.audio-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 14px;
  font-size: 12px;
  max-width: 300px;
}
.audio-name {
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 150px;
}
.audio-offset { display: flex; align-items: center; gap: 3px; color: var(--muted); font-size: 11px; }
.audio-offset input {
  width: 52px;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  text-align: right;
  padding: 0 2px;
}
.audio-offset input:focus { outline: none; border-bottom-color: var(--accent); }
.audio-offset input::-webkit-outer-spin-button,
.audio-offset input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.audio-status { font-size: 11px; color: var(--muted); white-space: nowrap; }
.audio-status.ok { color: var(--success); }
.audio-status.weak { color: var(--danger); }
.audio-x {
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  padding: 0 4px;
}
.audio-x:hover { color: var(--danger); }

/* ---- Zoom ---- */
.zoom-wrap { display: flex; align-items: center; gap: 7px; }
.zoom-label { font-size: 11px; color: var(--muted); letter-spacing: 0.04em; }
#zoom-slider { width: 84px; }
.zoom-val {
  background: none;
  border: 0;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  min-width: 34px;
  text-align: left;
  padding: 0;
}
.zoom-val:hover { color: var(--accent); }
.zoom-val.off { color: var(--accent); }

/* ---- Three colour schemes ---- */
.swatches { display: flex; gap: 6px; }
.cover-row { margin-top: 8px; }
.cover-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 4px 6px;
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: 11px;
}
.cover-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
}
.cover-chip .kf-x { opacity: 0.75; }

/* Sits with the transport, next to the playhead time it keys against. */
.kf-bar-btn { gap: 5px; }
.kf-del-btn { color: #ff8a8a; border-color: rgba(255,120,120,0.45); }
.kf-del-btn svg { stroke-width: 2.8; }
.kf-del-btn:hover { background: rgba(255,120,120,0.14); }
.kf-bar-btn:disabled { opacity: 0.4; cursor: default; }
.kf-bar-btn.has-keys { color: var(--accent); border-color: var(--accent-dim); }
.kf-bar-btn.has-keys svg { fill: var(--accent-dim); }

.kf-list { margin-top: 6px; }
.kf-empty { color: var(--muted); font-size: 11px; padding: 4px 0; }
.kf-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 4px;
  font-size: 11px;
  cursor: pointer;
}
.kf-row:hover { background: var(--surface-2); }
.kf-time { font-family: var(--font-mono); color: var(--text); }
.kf-detail { color: var(--muted); flex: 1; }
.kf-x {
  display: inline-flex;
  align-items: center; justify-content: center;
  /* The detail column is flex:1, so without this the button is crushed to a sliver. */
  flex: 0 0 auto;
  width: 18px; height: 18px;
  border: 0; border-radius: 4px;
  background: none; color: var(--muted); cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.12s, color 0.12s, background 0.12s;
}
.kf-x svg { flex: 0 0 auto; width: 13px; height: 13px; stroke-width: 2.6; }
.kf-row:hover .kf-x { opacity: 0.9; }
.kf-x:hover { opacity: 1; color: #ff8a8a; background: rgba(255,120,120,0.14); }

.style-select {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 4px 6px;
  font: inherit;
  font-size: 11px;
}
.style-select:focus { outline: none; border-color: var(--accent-dim); }

/* Reflection and Sky-on-floor have nothing to show once the floor is fully see-
   through, so they are disabled rather than left interactive and silently inert. */
.slider:disabled { opacity: 0.35; cursor: not-allowed; }
.style-row:has(.slider:disabled) > span:first-child { opacity: 0.5; }

.style-text {
  width: 100%;
  margin: 6px 0 0;
  padding: 6px 8px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  font: inherit;
  font-size: 12px;
}
.style-text::placeholder { color: var(--muted); }
.style-text:focus { outline: none; border-color: var(--accent-dim); }

.swatch {
  width: 30px;
  height: 24px;
  border-radius: 5px;
  border: 1px solid var(--border);
  background: linear-gradient(135deg, var(--a), var(--b));
  padding: 0;
}
.swatch-spectrum {
  background: linear-gradient(135deg, #4a6bff, #35d0c0, #f2e05a, #ff7a4a, #ff4a8d);
}
/* Reads as a slot rather than a fixed palette: it is whatever the Elements are set
   to right now, so it is drawn with a seam down the middle. */
.swatch-custom { position: relative; }
.swatch-custom::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(12, 12, 14, 0.35);
}
.swatch:hover { border-color: var(--muted); }
.swatch.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-dim);
}

/* Export is the primary action in the bar, so it reads as a filled button rather
   than another outline control. Overrides the side panel's full-width .btn-accent. */
.btn-cta {
  width: auto;
  margin-top: 0;
  padding: 6px 16px;
  font-size: 12px;
}

/* ---- Tempo in BPM ---- */
.bpm-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* The stepper and the field are one control, so they share a height. Border-box so
   the border does not make the squares 2px taller than the number they sit beside. */
.btn-step {
  box-sizing: border-box;
  width: 26px;
  height: 26px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  /* Normal line-height leaves the glyph riding high in the box; 0 makes flex
     centring position it on its own optical centre instead. */
  line-height: 0;
}
.bpm-field {
  box-sizing: border-box;
  height: 26px;
  display: flex;
  /* Baseline alignment made the field size itself from the text and sit a pixel or
     two off the buttons either side. */
  align-items: center;
  gap: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0 8px;
}
.bpm-field input {
  width: 42px;
  background: none;
  border: 0;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 13px;
  text-align: right;
  padding: 0;
}
.bpm-field input:focus { outline: none; color: var(--accent); }
/* Number spinners crowd a field this narrow. */
.bpm-field input::-webkit-outer-spin-button,
.bpm-field input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.bpm-unit { font-size: 10px; color: var(--muted); letter-spacing: 0.06em; }
.bpm-mult {
  background: none;
  border: 0;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  min-width: 42px;
  text-align: left;
  padding: 0 2px;
}
.bpm-mult:hover { color: var(--accent); }
.bpm-mult.off { color: var(--accent); }

.menu-wrap { position: relative; }
.dropdown {
  position: absolute;
  top: 32px;
  right: 0;
  min-width: 190px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px;
  z-index: 40;
  display: flex;
  flex-direction: column;
}
.dropdown-item {
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  font-size: 12px;
  text-align: left;
  padding: 7px 10px;
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
}
.dropdown-item:hover { background: var(--surface); color: var(--accent); }

/* Recording indicator, floating over the stage. A pulsing dot and an elapsed-time
   counter used to be the whole of it -- neither one tells you how much of the export
   is actually DONE, so a slow start (or just a long export) reads as stuck rather
   than working. The track below turns the frame count the loop already has into an
   actual answer to "how much longer". */
.rec-badge {
  position: absolute;
  top: 58px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 220px;
  padding: 7px 12px;
  font-size: 12px;
  background: rgba(20, 8, 10, 0.92);
  border: 1px solid #7b2b34;
  border-radius: 14px;
}
.rec-badge-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.rec-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #e05263;
  animation: rec-pulse 1.1s ease-in-out infinite;
  flex-shrink: 0;
}
@keyframes rec-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
#rec-text { flex: 1; }
.rec-progress-track {
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}
.rec-progress-fill {
  height: 100%;
  width: 0%;
  background: #e05263;
  border-radius: 2px;
  /* Short transition rather than none: frames land in an irregular burst as the
     real-time pacing catches up or falls behind, and smoothing that slightly reads
     as steady progress instead of a jittery bar. */
  transition: width 0.15s linear;
}
.btn-small { padding: 2px 8px; font-size: 11px; }

/* Fixed export framing. The canvas box is constrained to the chosen ratio and
   centred, so what you see on screen is exactly what a 16:9 or 9:16 export will
   contain — the 3D camera reads its aspect straight off the canvas each frame. */
.stage.framed {
  display: flex;
  /* Bottom-aligned, not centred. Centring split the letterbox evenly, and the bar
     UNDER the picture reads as a gap below the keyboard because the keys meet it
     with a hard edge. Sitting the frame on the player bar puts all the slack above,
     where it blends into the sky it is already next to. */
  align-items: flex-end;
  justify-content: center;
  padding: 8px 8px 0;
}
/* Sized in JS by fitStageCanvas, not here. width/height:auto with max-* only ever
   SHRINKS a box: nothing grew the canvas, so it sat at the 300x150 intrinsic default
   of a <canvas> and aspect-ratio merely reshaped that. On a large display the picture
   was a postage stamp in the middle of an empty stage. */
.stage.framed #scene-canvas {
  position: relative;
  inset: auto;
  box-shadow: 0 0 0 1px var(--border);
}

.speed-wrap, .colorby-wrap {
  font-size: 12px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 6px;
}

.theme-dots {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 4px;
}
.theme-dot {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--dot-color);
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
}
.theme-dot:hover { transform: scale(1.15); }
.theme-dot.active { border-color: var(--text); }
select, input[type="range"] { accent-color: var(--accent); }
select {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 6px;
  font-size: 12px;
}

/* Spotify-style: an engaged toggle is marked by colour, not by a border. */
.btn-loop-active { color: var(--accent) !important; }

/* Layout */
.layout {
  flex: 1 1 auto;
  display: flex;
  min-height: 0;
}

.stage {
  flex: 1 1 auto;
  position: relative;
  background: #06060a;
  min-width: 0;
  overflow: hidden;
  /* Lets .empty-state size itself off the stage's own on-screen width (cqw) rather
     than a fixed px value or the viewport -- so the drop-zone prompt is naturally
     smaller on a narrow 9:16 frame and unchanged on a wide 16:9 one, without tracking
     the aspect toggle in JS. */
  container-type: inline-size;
}

#scene-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.empty-state {
  /* width/height set in JS (fitStageCanvas) to match #scene-canvas's own box exactly
     -- inset:0 used to cover the whole (unframed-width) stage, so on a 9:16 frame the
     prompt sat far wider than the actual picture and its font-size had no narrower box
     to respond to. Positioned the same way the canvas is (bottom-anchored, centred)
     rather than inset:0, now that it carries an explicit size instead of filling the
     stage. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-family: var(--font-ui);
  pointer-events: none;
  /* Its own box (matching the canvas) is now what cqw below measures against. */
  container-type: inline-size;
}
.empty-state p {
  margin: 0;
  padding: 0 24px;
  text-align: center;
  /* 18px on a 16:9 frame (unchanged, since 3.4cqw exceeds it there and clamps to the
     max); scales down on a narrower one, which is what a 9:16 frame is -- roughly half
     the width at the same height, so the same fixed 18px read as oversized against it. */
  font-size: clamp(11px, 3.4cqw, 18px);
}

.drop-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(6, 6, 8, 0.85);
  border: 2px dashed var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.drop-overlay-text {
  font-family: var(--font-display);
  font-size: 28px;
  color: var(--accent);
}

/* Panel */
.panel {
  flex: 0 0 320px;
  background: var(--surface);
  border-left: 1px solid var(--border);
  overflow-y: auto;
  /* No padding on the SCROLLER. A sticky child with top:0 sticks below the padding
     box, so a padded scroll container leaves a strip above the stuck element that
     scrolled content shows through — which is what made the tabs look like they were
     floating over the text. The panes carry the padding instead. */
  padding: 0;
}
/* Breathing room under the tab bar. The first heading sat right against it,
   which made the tabs read as part of the section rather than above it. */
.panel-pane { padding: 20px 16px 16px; }
.panel-section { margin-bottom: 24px; }
.panel-section h2 {
  font-family: var(--font-display);
  font-size: 14px;
  /* Headings are not interactive. Blue means "you can do something with this". */
  color: var(--text);
  margin: 0 0 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  font-size: 11px;
  opacity: 0.85;
}

.stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 14px;
}
.stat-grid > div { display: flex; flex-direction: column; gap: 2px; }
/* A track title is a name, not a number: it gets the whole row rather than being
   folded into a half-width column beside the note count. */
.stat-grid > div.stat-wide { grid-column: 1 / -1; }
.stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); }
.stat-value { font-family: var(--font-mono); font-size: 14px; }

.mini-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  margin-bottom: 6px;
}
.mini-label-hint {
  text-transform: none;
  letter-spacing: normal;
  opacity: 0.7;
}

.key-character-wrap {
  margin-bottom: 14px;
  padding: 10px 12px;
  background: var(--surface-2);
  border-left: 2px solid var(--border);
  border-radius: 4px;
}
.key-character-label {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--text);
  margin-bottom: 4px;
}
.key-character-desc {
  margin: 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--muted);
}

.palette-wrap, .sections-wrap { margin-bottom: 14px; }

.color-palette {
  display: flex;
  gap: 6px;
}
.color-swatch {
  flex: 1 1 0;
  height: 32px;
  border-radius: 5px;
  cursor: pointer;
  border: 1px solid rgba(0,0,0,0.3);
  transition: transform 0.1s ease;
}
.color-swatch:hover { transform: translateY(-2px); }
.color-swatch.active { outline: 2px solid var(--text); outline-offset: 1px; }

.section-list {
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.section-card {
  background: var(--surface-2);
  border-radius: 5px;
  padding: 6px 8px;
  cursor: pointer;
  border: 1px solid transparent;
}
.section-card:hover { border-color: var(--accent-dim); }
.section-card-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted);
  margin-bottom: 4px;
}
.section-card-bar {
  height: 4px;
  border-radius: 2px;
  background: rgba(240,236,228,0.08);
  margin-bottom: 5px;
  overflow: hidden;
}
.section-card-bar-fill {
  height: 100%;
  background: var(--accent);
}
.section-card-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.section-card-tags .mood-tag { font-size: 9px; padding: 2px 6px; }

.panel-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 14px;
}
.panel-actions .btn-accent { margin-top: 0; }
.chord-block {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 10px;
  color: #060608;
  cursor: pointer;
  border-right: 1px solid rgba(0,0,0,0.25);
  overflow: hidden;
  white-space: nowrap;
}
.chord-block:hover { filter: brightness(1.2); }


.mood-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.mood-tag {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
}

.raw-json-wrap summary {
  cursor: pointer;
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 6px;
}
.raw-json {
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.5;
  background: var(--surface-2);
  border-radius: 6px;
  padding: 10px;
  max-height: 260px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--muted);
  user-select: text;
}

/* Transport */
.transport {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: var(--surface);
  border-top: 1px solid var(--border);
}
/* Transport hierarchy: exactly one filled primary, everything else a bare glyph.
   Three equally-weighted outlined circles gave the eye nothing to land on; a single
   solid disc reads as "the button" from across the room. */
.btn-primary {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 0;
  background: var(--text);
  color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  transition: transform 0.12s ease, background 0.12s ease;
}
.btn-primary:hover { transform: scale(1.07); }
.btn-primary:active { transform: scale(0.97); }
.btn-primary:disabled { background: var(--surface-3); color: var(--muted); transform: none; cursor: not-allowed; }
.btn-primary .icon { width: 16px; height: 16px; fill: currentColor; stroke: none; }
/* Only ever one of the two glyphs is present. */
.btn-primary .icon-pause, .btn-primary.is-playing .icon-play { display: none; }
.btn-primary.is-playing .icon-pause { display: block; }
/* Optical centring: a triangle's visual mass sits left of its bounding box. */
.btn-primary .icon-play { transform: translateX(1.5px); }

.btn-ghost {
  background: none;
  border: 0;
  padding: 6px;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}
.btn-ghost:hover { color: var(--text); }
.btn-ghost .icon { width: 16px; height: 16px; }
#btn-stop .icon { fill: currentColor; stroke: none; width: 13px; height: 13px; }

.progress-wrap { flex: 1 1 auto; }
.progress-track {
  position: relative;
  height: 6px;
  border-radius: 3px;
  background: var(--surface-2);
  cursor: pointer;
}
.progress-fill {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 0%;
  background: var(--accent);
  border-radius: 3px;
}
.progress-handle {
  position: absolute;
  top: 50%;
  left: 0%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 8px var(--accent-dim);
}

/* Equal flex-basis on both sides is what keeps the centre group centred regardless of
   how wide the tempo controls or the volume slider happen to be. */
.transport-side {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}
.transport-right { justify-content: flex-end; }
.transport-center {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
}
.time-display {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  min-width: 100px;
  text-align: right;
}

.volume-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
}
/* Speaker glyph reflects the level: only one of the three is ever shown. */
#btn-mute .icon { display: none; }
#btn-mute[data-level="high"] .icon-vol-high,
#btn-mute[data-level="low"] .icon-vol-low,
#btn-mute[data-level="off"] .icon-vol-off { display: block; }
