:root {
  --bg: #161914;
  --bg2: #1e2219;
  --panel: #232820;
  --panel2: #2b3126;
  --line: #3a4233;
  --text: #cdd6c2;
  --dim: #8a9480;
  --lime: #a8d024;
  --lime2: #d3f05a;
  --amber: #e0a63c;
  --red: #e05a4c;
  --cyan: #57c8d8;
  --sel: #f0e040;

  /* Left-hand column widths, shared by the panels below AND by the topbar,
     which parks the transport exactly on the canvas origin (see .bar-zone.lead).
     Both panels are border-box, so the canvas starts at the sum of these two.
     Change a width here and the topbar follows; hardcode one in either place
     and the alignment breaks silently the next time the sidebar is resized. */
  --sidebar-w: 280px;
  --lanestrip-w: 194px;
  --bar-pad-x: 16px;
  --bar-gap: 18px;
  /* Height of EVERY control in the topbar. One number, no exceptions. */
  --ctl-h: 38px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, sans-serif;
  font-size: 13px;
  display: flex;
  flex-direction: column;
  user-select: none;
}

/* ---------- top bar ---------- */
/* THREE ZONES (see index.html): `lead` = the brand, `main` = the working
   cluster (transport, follow pin, clock, BPM, snap), `tail` = env chips then
   the gear/File/exports.
   `main` is NOT centred in the window -- it is aligned to the TIMELINE. The
   lead zone is sized so that `main` begins exactly where the canvas does, which
   puts the transport on the bar-1 gridline and makes the topbar read as a
   header over the timeline rather than a strip floating above it. That is why
   the width is computed from the same variables the panels use instead of being
   a magic number: --sidebar-w + --lanestrip-w is the canvas origin (both panels
   are border-box), minus the bar's own left padding and the gap that follows
   the lead zone, both of which sit inside that distance.
   The zone is also the wrap unit. #topbar still wraps, but a whole zone moves
   rather than a group splitting down its middle -- and the reserved readout
   widths below are what stop it wrapping mid-mix in the first place. */
#topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--bar-gap);
  row-gap: 6px;
  padding: 10px var(--bar-pad-x);
  background: linear-gradient(#2b3126, #1e2219);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.bar-zone { display: flex; flex-wrap: nowrap; align-items: center; gap: 16px; }
.bar-zone.lead {
  flex: 0 0 calc(var(--sidebar-w) + var(--lanestrip-w) - var(--bar-pad-x) - var(--bar-gap));
}
.bar-zone.main { flex: 0 0 auto; }
/* NO `min-width: 0` here, deliberately. The default `min-width: auto` floors the
   zone at its min-content width, and that floor is doing two jobs: a squeezed
   tail stops shrinking instead of letting its buttons overflow the box, and the
   flex line-breaking step sizes items by that same floor -- which is what makes
   a too-narrow window wrap a whole zone rather than overflow the bar and clip
   the exports off the right edge. Zeroing it silently loses both. */
.bar-zone.tail { flex: 1 1 0; justify-content: flex-end; }
/* Keeps that min-content floor honest: without it "Export WAV" counts as
   min-content "Export" + "WAV" on two lines, so the bar would rather make a
   button two rows tall than wrap the zone. */
#topbar button, #topbar .group label { white-space: nowrap; }
/* The tier boundary, drawn: past this line everything is set-once or rare.
   Same primitive as .insp-sep. */
.bar-sep { flex: none; width: 1px; height: 24px; background: var(--line); }

/* ONE height for every control in the bar, --ctl-h, with NO exceptions -- env
   chips and the follow pin included. Mixed heights read as misalignment, not as
   hierarchy: the eye catches the ragged baseline long before it infers that the
   taller thing is more important, so a "size step for emphasis" just makes the
   row look broken. An earlier pass gave the transport its own taller height for
   exactly that reason and it had to come back out.
   Emphasis is carried by WIDTH and type size instead (see .transport below),
   which buys prominence without disturbing the row. */
#topbar button,
#topbar input[type="number"],
#topbar select,
#topbar #timeDisplay { min-height: var(--ctl-h); }
#topbar button:not(.envchip) { padding: 6px 12px; }
#topbar input[type="number"], #topbar select { padding: 5px 8px; }
/* The primary control: wider and set larger, same height as everything else. */
#topbar .transport button { min-width: 52px; font-size: 17px; padding: 4px 10px; }
.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--text);
}
.brand-mark { flex: none; width: 25px; height: 25px; display: block; }
.brand .mix { color: var(--lime); }
/* The logo fades the top of the "L" from lime down into the text colour - the
   green seeping over from "Mix". background-clip:text is Chrome/Edge only,
   which is all this app targets; without it the letter just renders as text. */
.brand .bleed {
  background: linear-gradient(180deg, var(--lime2) 0%, var(--lime) 30%, var(--text) 68%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
/* Groups no longer need margin-left:auto to reach the right edge -- the `.end`
   zone puts them there, and an auto margin inside it would spread the zone's
   own contents apart instead of keeping them as one cluster. */
.group { display: flex; align-items: center; gap: 8px; }
.group label { color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
.group .unit { color: var(--dim); font: 10px Consolas, monospace; margin-left: -3px; }
/* Out-specifies the bar-wide #topbar select padding above, which would
   otherwise eat into this deliberately narrow field. */
#popSettings .auto-gain-group select { width: 52px; padding-inline: 4px; }
#btnAutoGain { font-size: 11px; letter-spacing: .03em; }
#btnAutoGain.active { background: var(--amber); color: #17130b; border-color: #f1c369; }

button {
  background: var(--panel2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 5px 10px;
  font-size: 13px;
  cursor: pointer;
}
button:hover { background: #39412f; border-color: var(--lime); }
/* Envelope chips opt OUT: .active on a chip means "the one you're editing" and
   must keep the chip's own ENV_COLORS fill. The per-param .on rules below do
   out-specify this one, but only by accident of selector weight -- saying it
   here means a future tweak to either side cannot quietly turn every active
   chip lime. */
button.active:not(.envchip) { background: var(--lime); color: #10130c; border-color: var(--lime2); }
button.danger:hover { background: #5a2620; border-color: var(--red); }
#btnKeyLock.active { background: var(--cyan); color: #0b1417; border-color: #8fe0ec; }
#btnPlay.playing { background: var(--lime); color: #10130c; }
/* Follow is a view latch, not a transport action, so it is an icon toggle
   rather than a labelled button competing with the transport for attention.
   The dim state is the load-bearing half: scrolling the timeline unpins the
   view silently, and this is the only thing on screen that says so.
   Two ids, because the bar-wide `#topbar button:not(.envchip)` padding rule is
   (1 id, 1 class, 1 type) and would otherwise win and re-widen the icon. */
#topbar #btnFollow { padding: 6px 9px; font-size: 14px; line-height: 1; opacity: .45; filter: grayscale(1); }
#topbar #btnFollow.on { opacity: 1; filter: none; background: #2b3a3f; border-color: #4d6a72; }

/* Envelope chips — the only envelope control. Colours match ENV_COLORS in
   timeline.js. Three states: dark = hidden, lit = drawn as a dashed ghost,
   lit + lime ring = the one you're editing. "Editing" is deliberately a ring
   ON TOP of the lit colour rather than a different colour of its own: the
   edited envelope IS drawn, so it must read as lit, and the ring says it is a
   step beyond showing. The gap leaves room for that ring, which is drawn
   outside the border box.
   They take the shared --ctl-h like everything else in the bar; what still says
   "chip" is the narrow width, the small bold mono letter and the colour, none
   of which need a shorter box to work. */
#envShow { display: inline-flex; gap: 6px; }
button.envchip {
  padding: 5px 10px;
  font: 700 11px Consolas, monospace;
  border-radius: 3px;
  color: var(--dim);
  background: #10130c;
}
button.envchip[data-p="gain"].on { background: #ffffff; color: #10130c; border-color: #ffffff; }
button.envchip[data-p="low"].on  { background: #ff6a58; color: #10130c; border-color: #ff6a58; }
button.envchip[data-p="mid"].on  { background: #ffc04d; color: #10130c; border-color: #ffc04d; }
button.envchip[data-p="high"].on { background: #6ad8e8; color: #10130c; border-color: #6ad8e8; }
button.envchip[data-p="filter"].on { background: #7ee787; color: #10130c; border-color: #7ee787; }
button.envchip[data-p="echo"].on { background: #c98bff; color: #10130c; border-color: #c98bff; }
button.envchip:not(.active):hover { border-color: var(--lime2); }
button.envchip.active { box-shadow: 0 0 0 2px var(--lime); cursor: default; }

input[type="number"], select {
  background: #10130c;
  color: var(--lime2);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 4px 6px;
  font-size: 13px;
  font-family: Consolas, monospace;
  width: 70px;
}
select { width: auto; color: var(--text); }

/* The working cluster's anchor, so it is a step larger than the controls around
   it. The reserved widths below are in `ch`, so they scale with this size and
   keep reserving exactly the same number of digits. */
#timeDisplay {
  background: #10130c;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 5px 12px;
  font-family: Consolas, monospace;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 14px;
}
/* Every field reserves the widest value it can ever show. These spans are
   rewritten every frame by updateTimeDisplay(); left to size themselves they
   grow with the playhead ("1.1" -> "1280.3"), and one extra character was
   enough to tip #topbar (flex-wrap: wrap) onto a second row MID-MIX, shifting
   every control and the canvas down under the cursor. Reserved widths mean the
   bar is the same size at 0:00 as at 40:00.
   Sitting inside `.bar-zone.main` adds a second reason: that zone is `flex: 0 0
   auto`, so a box that grew by a character would widen the zone and push Master
   BPM and Snap rightwards every time the clock ticked past a digit. (It cannot
   move the transport, which is upstream of it and pinned by `.lead`.)
   Right-aligned so the digits sit in a stable column. */
#timeDisplay > span { display: inline-block; text-align: right; }
#tdBars  { color: var(--lime2); min-width: 6ch; }  /* up to "1280.3" */
#tdClock { color: var(--dim);   min-width: 7ch; }  /* up to "99:59.9" */
#tdTotal { color: var(--dim); opacity: .65; min-width: 9ch; } /* "/ 41:07.2" */

/* ---------- popovers ---------- */
/* ONE primitive, used by both the gear (set-once mix settings) and File. The
   app had no menu pattern before these two, and two bespoke ones would drift.
   Anchored to .pop-wrap so it hangs under its own trigger; shown by toggling
   [hidden], never by editing style.display, so app.js only ever flips el.hidden. */
.pop-wrap { position: relative; display: inline-flex; }
.pop {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 190px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--panel2);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, .55);
}
.pop[hidden] { display: none; }
.pop-row { display: flex; align-items: center; gap: 6px; justify-content: space-between; }
.pop-row label { color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: .5px; }
.pop-sep { height: 1px; background: var(--line); }
/* The File menu is a list of actions, so its children fill the width and read
   top-to-bottom rather than sitting in labelled rows. */
.pop-menu { min-width: 150px; gap: 4px; }
.pop-menu button { text-align: left; width: 100%; }
.pop-trigger { position: relative; }
.pop-trigger[aria-expanded="true"] { border-color: var(--lime2); color: var(--text); }
/* Tucking key lock / Auto Gain behind the gear must not make a non-default
   setup invisible: the dot marks "something in here is switched off". */
.pop-dot {
  position: absolute;
  top: -2px; right: -2px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--amber);
}
.pop-dot[hidden] { display: none; }

/* ---------- inspector ---------- */
#inspector {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  background: var(--bg2);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
#inspector.hidden { display: none; }
#inspector label { color: var(--dim); font-size: 11px; }
#inspector input { width: 52px; }
#inspector #trackGainIn { width: 58px; }
.gain-unit, #trackGainMode { color: var(--dim); font: 10px Consolas, monospace; }
#trackGainMode.auto { color: var(--amber); }
#trackGainMode.manual { color: var(--cyan); }
.insp-name { color: var(--sel); font-weight: 600; }
.insp-sep { width: 1px; height: 18px; background: var(--line); margin: 0 6px; }

/* ---------- main ---------- */
#main { display: flex; flex: 1 1 auto; min-height: 0; }

#sidebar {
  width: var(--sidebar-w);
  flex: 0 0 auto;
  background: var(--bg2);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 10px;
  color: var(--dim);
  letter-spacing: 1px;
  font-size: 11px;
  border-bottom: 1px solid var(--line);
}
#library { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.lib-empty {
  color: var(--dim);
  text-align: center;
  padding: 40px 10px;
  border: 1px dashed var(--line);
  margin: 12px;
  border-radius: 6px;
  line-height: 1.7;
}
.lib-item {
  padding: 7px 10px;
  border-bottom: 1px solid #262c20;
  cursor: grab;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.lib-item:hover { background: var(--panel); }
.lib-item .lib-title { display: flex; align-items: center; gap: 6px; min-width: 0; }
.lib-item .t { flex: 1; min-width: 0; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lib-item .track-remove {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--dim);
  padding: 0 3px;
  font-size: 17px;
  line-height: 16px;
  cursor: pointer;
}
.lib-item .track-remove:hover { color: #ff786e; background: transparent; }
.lib-item .m { display: flex; flex-wrap: wrap; column-gap: 8px; row-gap: 2px; font-family: Consolas, monospace; font-size: 12px; color: var(--dim); align-items: center; }
.lib-item .bpm { color: var(--lime2); }
.lib-item .keybadge {
  padding: 0 6px;
  border-radius: 8px;
  color: #10130c;
  font-weight: 700;
  font-size: 11px;
}
.lib-item .compat { color: var(--sel); font-size: 11px; }
.lib-item.analyzing { opacity: 0.6; }
/* loaded from a project but still waiting for its audio file: the one library
   row you can't drag, so it advertises the click that fixes it */
.lib-item.missing { opacity: 1; cursor: pointer; border-left: 3px solid var(--amber); }
.lib-item.missing:hover { background: #35301f; }
.lib-item .needsfile { color: var(--amber); font-size: 11px; }
.lib-item .prog {
  height: 3px;
  background: #10130c;
  border-radius: 2px;
  overflow: hidden;
}
.lib-item .prog > div { height: 100%; background: var(--lime); width: 0%; }
/* key lock: cyan for "pitch held", amber for "pitch is being dragged" */
.lib-item .klock { color: var(--cyan); font-size: 11px; }
.lib-item .klock.pend { color: var(--dim); }
.lib-item .pitchoff { color: var(--amber); font-size: 11px; }
.lib-item .norm { color: var(--sel); font-size: 11px; }
.lib-item .norm.manual { color: var(--cyan); }
.lib-item .norm.off { color: var(--dim); }
.lib-item .prog.stretch > div { background: var(--cyan); }

/* ---------- timeline ---------- */
#timelineArea { flex: 1 1 auto; display: flex; min-width: 0; min-height: 0; }
#laneStrips {
  width: var(--lanestrip-w);
  flex: 0 0 auto;
  background: var(--bg2);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
}
.ruler-spacer { flex: 0 0 auto; border-bottom: 1px solid var(--line); }
.lane-strip {
  /* stretch to exactly 1/4 of the timeline height so strips line up with canvas lanes */
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
  box-sizing: border-box;
  border-bottom: 1px solid var(--line);
  padding: 2px 8px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  justify-content: flex-start;
}
.lane-strip .row { display: flex; align-items: center; gap: 6px; min-height: 0; }
/* The six faders own whatever height is left between the lane head and the
   meter and spread evenly through it, so the slack shows up BETWEEN the rows
   instead of pooling as dead space above the meter. min-height keeps them
   legible when the window is short enough that there is no slack to spread. */
.lane-strip .fader-bank {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
}
.lane-strip .fader-bank .row { flex: 0 0 auto; min-height: 16px; }
.lane-strip .lname { color: var(--dim); font-size: 11px; font-weight: 700; }
.lane-strip .lane-head { flex: 0 0 auto; min-height: 18px; }
.lane-strip .lane-level-db { margin-left: auto; color: var(--dim); font: 10px Consolas, monospace; }
.lane-strip button.mute {
  padding: 0 6px; font-size: 10px; line-height: 16px; border-radius: 3px;
}
.lane-strip button.mute.on { background: var(--amber); color: #10130c; }
.lane-strip label { font-size: 10px; color: var(--dim); width: 12px; font-family: Consolas, monospace; }
/* Faders are hand-drawn (not accent-color) because the EQ track must fill
   from its CENTRE outwards: --a/--b are the fill edges, set by paintFader(). */
.lane-strip input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  min-width: 0;
  height: 12px;
  background: transparent;
  cursor: pointer;
}
.lane-strip input[type="range"]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background:
    linear-gradient(90deg, transparent 0 var(--a, 0%), var(--c) var(--a, 0%) var(--b, 0%), transparent var(--b, 0%) 100%),
    var(--panel2);
}
/* EQ also gets a centre-detent tick under the fill, marking 0 dB */
.lane-strip input.eq::-webkit-slider-runnable-track {
  background:
    linear-gradient(90deg, transparent 0 var(--a, 50%), var(--c) var(--a, 50%) var(--b, 50%), transparent var(--b, 50%) 100%),
    linear-gradient(90deg, transparent calc(50% - 1px), var(--dim) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)),
    var(--panel2);
}
.lane-strip input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 8px;
  height: 12px;
  margin-top: -3px;
  border-radius: 2px;
  background: var(--c);
  border: 1px solid #10130c;
}
.lane-strip input.vol { --c: var(--lime); }
.lane-strip input.eq { --c: var(--amber); }
.lane-strip input.filter { --c: #7ee787; }
.lane-strip input.echo { --c: #c98bff; }
.lane-strip .rdout {
  flex: 0 0 auto;
  width: 42px;
  text-align: right;
  font: 10px Consolas, monospace;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.lane-strip .rdout.neutral { color: var(--dim); }
.lane-meter-wrap {
  flex: 0 0 auto;
  margin-top: auto;
  padding: 4px 0 2px;
  border-top: 1px solid #30372b;
}
.lane-meter-labels {
  position: relative;
  height: 9px;
  color: #65705d;
  font: 8px Consolas, monospace;
  line-height: 9px;
}
.lane-meter-labels span { position: absolute; transform: translateX(-50%); }
.lane-meter-labels .db-min { left: 0; transform: none; }
.lane-meter-labels .db-18 { left: 58.8%; }
.lane-meter-labels .db-6 { left: 82.4%; }
.lane-meter-labels .db-zero { left: 94.1%; }
.lane-meter {
  display: block;
  width: 100%;
  height: 14px;
  background: #10130c;
  border: 1px solid var(--line);
  border-radius: 3px;
}

#timelineWrap { flex: 1 1 auto; position: relative; min-width: 0; overflow: hidden; }
#timelineCanvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* ---------- status bar ---------- */
#statusbar {
  flex: 0 0 auto;
  padding: 4px 12px;
  background: var(--bg2);
  border-top: 1px solid var(--line);
  color: var(--dim);
  font-size: 12px;
  /* Deliberately one line: a wrapping status bar would resize the canvas
     mid-mix. The ellipsis makes truncation visible instead of silent, and
     setStatus() mirrors every message into the title so hover shows the rest --
     the relink flow writes the longest messages in the app. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-overflow: ellipsis;
}
