/* Kagora — shares the ledzeppelin interface language (the sibling realtime DDP
   show tool) so the two apps read as ONE toolset:
     • MONO-FORWARD: one monospace face (Spline Sans Mono) drives both chrome
       and data — labels, buttons, numbers, headings.
     • Near-black matte panels with 1px HAIRLINE borders (no 2px frames, no
       shadows-as-structure); panels are slightly translucent so the canvas
       reads underneath.
     • ONE warm accent (#e8a35c) everywhere — selection/active state is shown
       by TEXT turning accent, not by fills or thick borders.
     • Dense spacing (Resolume-slim rows), uppercase micro-caps group headers.

   Theming stays token-driven: `:root` is the DARK (default) set, matching
   ledzeppelin; `[data-theme="light"]` is a warm-paper variant with the same
   structure. The canvas reads these same custom properties via
   getComputedStyle (see canvas.js _theme), so shell + stage switch together. */

/* Self-hosted variable fonts (latin subset, 300–700 from a single file each).
   Spline Sans Mono is THE interface face; Spline Sans stays declared as an
   optional body face but the tokens alias everything to mono (ledzeppelin's
   mono-forward rule). */
@font-face {
  font-family: 'Spline Sans';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/SplineSans-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/SplineSansMono-latin.woff2') format('woff2');
}

/* ---------- theme token sets ----------
   Most specific wins:
     1. :root[data-theme="dark"] / [="light"]  — explicit user toggle
     2. @media (prefers-color-scheme: light)    — OS choice when unset
     3. :root base                              — DARK default (ledzeppelin) */

:root {
  color-scheme: dark;

  /* Near-black matte (ledzeppelin). Panels slightly translucent over canvas. */
  --bg:          #08090b;
  --bg-surface:  #0e1014;
  --bg-elevated: #13141a;          /* opaque card/popover surface */
  --panel:       rgba(12, 13, 17, 0.94);  /* translucent panel over the canvas */
  --fg:          #f4f5f7;
  --fg-title:    #f4f5f7;
  --fg-muted:    #9aa2ac;
  --fg-disabled: #697079;
  --border-color:#262a32;          /* hairline */
  --line-2:      #3a3f49;          /* stronger hairline (inputs, popovers) */
  --field-bg:    #050608;          /* recessed input fill (darker than bg) */
  --accent:      #e8a35c;          /* THE warm accent */
  --accent-ink:  #08090b;          /* text/icon on an accent fill */
  --accent-soft: #241a12;          /* warm selected/active background */
  --radius:      3px;
  --border-w:    1px;

  /* Derived lines & surfaces (token-based so the canvas can mirror them) */
  --rule:  var(--border-w) solid var(--border-color);
  --rule-color: var(--border-color);
  --muted: var(--fg-muted);
  --soft:  #13151a;                /* quiet hover fill */
  --soft-2: #1c1f26;               /* stronger hover / active fill */

  /* Status colors — tuned for near-black */
  --ok:    #7bd88f;
  --warn:  #ff6b6b;
  --amber: #e8b27f;

  /* Canvas-specific tokens (read by canvas.js _theme via getComputedStyle).
     Signal/power keep their FUNCTIONAL hues (data vs power must read apart);
     only the stage surfaces collapse to the matte near-black. */
  --canvas-stage:    #0a0b0d;
  --canvas-checker:  rgba(255, 255, 255, 0.05);  /* pasteboard diamond tile (screen only) */
  --canvas-grid:     #16191e;
  --canvas-node-fill:#13141a;
  --canvas-port-ring:#08090b;
  --canvas-signal:   #4d8bff;      /* data / signal cable + data-out ports */
  --canvas-signal-in:#3fbf6a;      /* data-in ports */
  --canvas-power:    #e8694f;      /* power cable + power ports */

  /* Per-strip-card hue palette (calm, distinguishable; from calculed). */
  --strip-hue-1: #fde68a;  /* amber  */
  --strip-hue-2: #bfdbfe;  /* blue   */
  --strip-hue-3: #bbf7d0;  /* mint   */
  --strip-hue-4: #fbcfe8;  /* pink   */
  --strip-hue-5: #c7d2fe;  /* indigo */
  --strip-hue-6: #fed7aa;  /* peach  */
  --strip-hue-7: #d8b4fe;  /* violet */
  --strip-hue-8: #99f6e4;  /* teal   */

  /* Typography — MONO-FORWARD: every token aliases the one mono face. */
  --font:      'Spline Sans Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --font-sans: var(--font);
  --font-mono: var(--font);
  --fs-mini:  10px;
  --fs-small: 11px;
  --fs-base:  12px;
  --fs-large: 13px;
  --fs-icon:  14px;     /* icon-button glyphs (⚙ ◐ ↺ …) */
  --fs-xl:    17px;     /* BOM stat values */
  --fs-2xl:   19px;     /* BOM document title */
  --ls-caps:  .1em;     /* uppercase micro-caps tracking */
  --ls-ui:    .04em;

  /* Spacing scale — tightened (slim, instrument-dense). */
  --s-1: 2px;
  --s-2: 4px;
  --s-3: 8px;
  --s-4: 12px;
  --s-5: 16px;
  --s-6: 20px;
  --s-8: 28px;

  /* Radii — hairline aesthetic: tiny rounding only. */
  --r-sm: 2px;
  --r-md: var(--radius);

  /* Transitions */
  --t-fast: 0.1s;
  --t-base: 0.15s;

  /* Shell metrics */
  --sidebar-w: 300px;
  --row-h: 26px;      /* ONE control-row height — every label·control row (ledzeppelin rhythm) */
  --ctrl-h: 22px;     /* ONE control height inside a row (row-h minus breath) */
  --col-label: 96px;  /* fixed label column in horizontal form rows */
  --col-val: 48px;    /* value-box column on slider rows */

  /* Print page margin (used by the @media print Layout-only sheet). */
  --print-margin: 12mm;

  font: var(--fs-base)/1.45 var(--font);
  accent-color: var(--accent);
}

/* Warm-paper light set — same structure (hairlines, mono, one warm accent),
   inverted value. Two selectors share it: the explicit toggle and OS-light
   *only when the user hasn't chosen*. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg:          #f4f2ee;
  --bg-surface:  #fbfaf8;
  --bg-elevated: #ffffff;
  --panel:       rgba(251, 250, 248, 0.95);
  --fg:          #2b2926;
  --fg-title:    #1a1918;
  --fg-muted:    #5f5a54;          /* darkened for readable secondary text on paper */
  --fg-disabled: #a39c93;
  --border-color:#d2ccc2;          /* darker so hairlines actually read on off-white */
  --line-2:      #bdb6ab;
  --field-bg:    #e9e5dd;          /* recessed enough that inputs read as fields */
  --accent:      #b06f24;          /* the warm accent, darkened for paper */
  --accent-ink:  #ffffff;
  --accent-soft: #efe0cd;          /* visible selected/active fill */
  --soft:        #eceae5;
  --soft-2:      #e4e1da;

  --ok:    #16a34a;
  --warn:  #dc2626;
  --amber: #d97706;

  --canvas-stage:    #efece7;
  --canvas-checker:  rgba(0, 0, 0, 0.035);
  --canvas-grid:     #d8d3cc;
  --canvas-node-fill:#ffffff;
  --canvas-port-ring:#ffffff;
  --canvas-signal:   #2f6df6;
  --canvas-signal-in:#16a34a;
  --canvas-power:    #d9483b;
}

/* OS-light default — applies only when no explicit theme is set. An explicit
   [data-theme] always wins. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) {
    color-scheme: light;
    --bg:          #f4f2ee;
    --bg-surface:  #fbfaf8;
    --bg-elevated: #ffffff;
    --panel:       rgba(251, 250, 248, 0.95);
    --fg:          #2b2926;
    --fg-title:    #1a1918;
    --fg-muted:    #6b6660;
    --fg-disabled: #b3ada4;
    --border-color:#ddd8d0;
    --line-2:      #c6c0b6;
    --field-bg:    #efece7;
    --accent:      #b06f24;
    --accent-ink:  #ffffff;
    --accent-soft: #f3e5d3;
    --soft:        #eceae5;
    --soft-2:      #e4e1da;

    --ok:    #16a34a;
    --warn:  #dc2626;
    --amber: #d97706;

    --canvas-stage:    #efece7;
    --canvas-checker:  rgba(0, 0, 0, 0.035);
    --canvas-grid:     #d8d3cc;
    --canvas-node-fill:#ffffff;
    --canvas-port-ring:#ffffff;
    --canvas-signal:   #2f6df6;
    --canvas-signal-in:#16a34a;
    --canvas-power:    #d9483b;
  }
}

/* ═══════════ SHARED APP-SURFACE BACKBONE ═══════════
   This block is IDENTICAL (rule-for-rule; only token VALUES differ via :root)
   in LEDger (src/style.css) and ledzeppelin (src/ui/ui.css) — keep them in
   sync. It is what makes both feel like one rock-solid native tool instead of
   a web page: no stray text selection, no OS-blue highlights, no tap flashes,
   no ghost-drags, no rubber-band overscroll, no native widget chrome. */
* { box-sizing: border-box; }
input, select, button, textarea, output { font-family: inherit; }

html, body {
  margin: 0;
  height: 100%;
  /* The app is exactly one viewport; nothing scrolls the page itself (panels +
     canvas scroll internally) and nothing rubber-bands at the edges. */
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--bg);
  color: var(--fg);
  font: var(--fs-base)/1.45 var(--font);
  /* Arrow cursor over all chrome/labels (no I-beam on non-editable text);
     editable fields restore their own text cursor below. */
  cursor: default;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}
.muted { color: var(--muted); }
.small { font-size: var(--fs-small); }

/* App surface, not a document — no text selection anywhere except editable
   fields, and even there the highlight + caret are THEMED (accent), never the
   OS default blue. */
* { user-select: none; -webkit-user-select: none; }
input, textarea, [contenteditable] {
  user-select: text; -webkit-user-select: text;
  cursor: text;
  caret-color: var(--accent);
}
::selection { background: color-mix(in srgb, var(--accent) 30%, transparent); color: var(--fg); }

/* Nothing in the chrome is draggable as a ghost image. */
img, a, button, summary, label { -webkit-user-drag: none; user-drag: none; }

/* Hide scrollbars everywhere (still scrollable). */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Number fields read as clean data — no native spinner chrome. */
input[type=number] { -moz-appearance: textfield; appearance: textfield; }
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }

/* SINGLE WEIGHT: no bold anywhere — emphasis comes from color (bright vs
   muted vs accent), never from weight. */
strong, b { font-weight: 400; }

/* Keyboard focus: one quiet hairline ring on EVERY focusable — no browser
   blue, no rings on mouse clicks (focus-visible only). */
button:focus, select:focus, input[type=range]:focus, summary:focus { outline: none; }
button:focus-visible, select:focus-visible, input:focus-visible,
input[type=range]:focus-visible, summary:focus-visible, a:focus-visible {
  outline: 1px solid var(--line-2); outline-offset: 1px;
}
/* ═══════════ end shared backbone ═══════════ */

/* ---------- shell ---------- */
/* FULL-BLEED canvas (ledzeppelin): the stage fills the whole viewport and all
   chrome FLOATS over it — the former top bar is now two corner clusters
   (fixed-position chips), the right panels slide in over the canvas. */
#app {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "stage";
  height: 100vh;
  height: 100dvh; /* dynamic viewport unit: correct under a mobile browser's URL bar */
  width: 100vw;
  overflow: hidden;
}
/* 'h' hides all GUI to view the canvas full-screen (Layout view; app.js). */
body.gui-hidden #topbar,
body.gui-hidden #sidebar,
body.gui-hidden #toolrail { display: none !important; }
/* Non-Layout views (Library, BOM) hide the floating tool toolbar; it reappears
   in Layout with its tool state intact (it's only display-toggled). */
#app[data-view="library"] #toolrail,
#app[data-view="bom"] #toolrail { display: none; }

/* ---------- floating corner clusters (the former top bar) ---------- */
/* The #topbar element survives (all menu/button wiring is untouched) but it is
   no longer a bar: display:contents dissolves its box and the two child groups
   pin to their corners as matte CHIPS — ledzeppelin's #corner-controls idiom.
   BOTTOM-LEFT: document menus + render-mode/theme/settings toggles (menus open
   UPWARD). TOP-RIGHT: the view switch, directly above the sidebar. */
#topbar { display: contents; }
.topbar-actions,
.topbar-right {
  position: fixed;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--s-1);
  background: var(--panel);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  padding: 2px 4px;
}
.topbar-actions { left: 8px; bottom: 8px; }
.topbar-right { top: 8px; right: 8px; }
.topbar-divider {
  width: 1px;
  align-self: stretch;
  margin: 5px var(--s-1);
  background: var(--rule-color);
}

/* Menu popovers (File / Export / Tools / View) — quiet uppercase triggers,
   solid near-black popover with hairline (ledzeppelin #menu-pop). */
.menu { position: relative; }
.menu > summary {
  list-style: none;
  cursor: pointer;
  font: var(--fs-small) var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  padding: 5px 9px;
  border-radius: var(--r-sm);
  color: var(--muted);
  user-select: none;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary:hover { color: var(--fg); }
.menu[open] > summary { color: var(--fg); background: var(--soft-2); }
.menu-pop {
  position: absolute;
  bottom: calc(100% + 4px);   /* the cluster sits bottom-left → menus open UP */
  left: 0;
  z-index: 60;
  min-width: 13rem;
  background: var(--bg-elevated);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
  padding: var(--s-1);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.menu-item {
  font: var(--fs-small) var(--font);
  text-align: left;
  padding: 6px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  white-space: nowrap;
}
.menu-item:hover { background: var(--soft-2); color: var(--accent); }
/* Active state (e.g. the current tool in the Tools menu): accent text + tick. */
.menu-item.active { color: var(--accent); }
.menu-item.active::before { content: "✓ "; }
.menu-sep { height: 1px; margin: var(--s-1) 0; background: var(--rule-color); }
/* A menu item with a trailing shortcut hint (e.g. View ▸ Fit · 0). */
.menu-item:has(.menu-key) { display: flex; justify-content: space-between; align-items: center; gap: var(--s-4); }
.menu-key {
  font: var(--fs-mini) var(--font); color: var(--fg-disabled);
  background: var(--field-bg); border: 1px solid var(--border-color);
  border-radius: var(--r-sm); padding: 0 5px; min-width: 14px; text-align: center;
}

/* ---------- keyboard-shortcuts overlay (ledzeppelin #shortcuts-overlay) ---------- */
#shortcuts-overlay {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
}
#shortcuts-overlay[hidden] { display: none; }
.shortcuts-card {
  background: var(--bg-elevated); border: 1px solid var(--line-2);
  border-radius: var(--radius); padding: 18px 20px;
  min-width: 340px; max-width: 90vw;
  box-shadow: 0 12px 40px color-mix(in srgb, var(--fg-title) 22%, transparent);
}
.shortcuts-title {
  font: var(--fs-mini) var(--font); letter-spacing: var(--ls-caps);
  text-transform: uppercase; color: var(--accent); margin-bottom: 14px;
  padding-bottom: 5px; border-bottom: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
.shortcuts-grid { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; align-items: center; }
.shortcuts-key {
  font: var(--fs-small) var(--font); color: var(--fg);
  background: var(--field-bg); border: 1px solid var(--border-color);
  border-radius: var(--r-sm); padding: 1px 7px; justify-self: start; white-space: nowrap;
}
.shortcuts-act { font: var(--fs-small) var(--font); color: var(--muted); }
/* Inline checkbox row inside a menu popover (e.g. the Export "Background"
   toggle). Reads like a menu item but stays put on click instead of acting. */
.menu-check {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-small);
  padding: 6px 10px;
  border-radius: var(--r-sm);
  cursor: pointer;
  white-space: nowrap;
  color: var(--fg);
}
.menu-check:hover { background: var(--soft-2); }
.menu-check input { cursor: pointer; margin: 0; }

/* Icon buttons (undo/redo/gear/close) — borderless quiet glyphs; accent on hover. */
.icon-btn {
  font: var(--fs-icon)/1 var(--font);
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color var(--t-fast), background var(--t-fast);
}
.icon-btn:hover:not(:disabled) { color: var(--accent); background: var(--soft); }
.icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }

/* View switcher — ledzeppelin section tabs: text-only, the active one is just
   BRIGHT/accent; no fills, no boxes. */
.view-switch {
  display: inline-flex;
  gap: 0;
}
.view-tab {
  font: var(--fs-small) var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  padding: 5px 10px;
  border: 0;
  background: transparent;
  color: var(--fg-disabled);
  cursor: pointer;
}
.view-tab:hover:not(:disabled):not(.active) { color: var(--muted); }
.view-tab.active { color: var(--accent); }
.view-tab:disabled { opacity: 0.4; cursor: not-allowed; }

.mode-tab {
  font: var(--fs-small) var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  padding: 5px 8px;
  border: 0;
  background: transparent;
  color: var(--fg-disabled);
  cursor: pointer;
  white-space: nowrap;
}
.mode-tab:hover:not(.active) { color: var(--muted); }
.mode-tab.active { color: var(--accent); }
/* Pulse-style toggle is disabled (greyed) while in schematic mode. */
#pulse-style:disabled { opacity: 0.35; cursor: not-allowed; }

/* ---------- floating tool toolbar ---------- */
/* Compact toolbar overlaid on the TOP-LEFT of the canvas. Matte panel with a
   hairline; collapses away when empty. */
#toolrail:empty { display: none; }
#toolrail {
  position: absolute;
  top: 48px;   /* below the floating top-left cluster */
  left: 10px;
  z-index: 5;
  background: var(--panel);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--s-1);
  padding: var(--s-1);
}

/* Problems panel — live validation list in the sidebar (errors first). Empty
   when the design is clean (renderProblems writes nothing). */
.problem-count {
  font: var(--fs-mini) var(--font);
  padding: 1px 6px; border-radius: 999px; margin-left: 4px;
  font-variant-numeric: tabular-nums;
}
.problem-count.err { background: color-mix(in srgb, var(--warn) 22%, transparent); color: var(--warn); }
.problem-count.warn { background: color-mix(in srgb, var(--amber) 22%, transparent); color: var(--amber); }
#panel-problems { display: flex; flex-direction: column; gap: 1px; }
.problem-row {
  display: flex; align-items: center; gap: var(--s-2); width: 100%;
  text-align: left; border: 0; background: none; cursor: pointer;
  padding: 4px 5px; border-radius: var(--r-sm); color: var(--muted);
  font-size: var(--fs-mini);
}
.problem-row:hover { background: var(--soft); color: var(--fg); }
.problem-dot { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; }
.problem-row.error .problem-dot { background: var(--warn); }
.problem-row.warn .problem-dot { background: var(--amber); }
.problem-msg { flex: 1 1 auto; }


/* ---------- center stage ---------- */
#stage-wrap {
  grid-area: stage;
  position: relative;
  overflow: hidden;
  min-height: 0; /* let the 1fr grid track shrink/fill instead of blowing out */
  min-width: 0;
  /* Canvas paints its own theme-aware background; this just matches behind it. */
  background: var(--canvas-stage);
}
#stage { display: block; width: 100%; height: 100%; cursor: default; }
#stage.panning { cursor: grabbing; }

/* First-run / empty-canvas overlay (toggled by ui.js syncEmptyState). The wrap is
   click-through so it never blocks canvas interaction; only the card itself takes
   pointer events. */
#stage-empty {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: var(--s-4);
  pointer-events: none;
}
#stage-empty[hidden] { display: none; }
.empty-card {
  pointer-events: auto;
  max-width: 30rem;
  text-align: center;
  background: var(--bg-elevated);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  padding: 28px 26px;
}
.empty-title {
  font-size: var(--fs-large); font-weight: 400; margin: 0 0 10px;
  text-transform: uppercase; letter-spacing: var(--ls-caps); color: var(--accent);
}
.empty-sub { color: var(--muted); line-height: 1.55; margin: 0 0 18px; font-size: var(--fs-small); }
.empty-cta {
  font: var(--fs-small) var(--font);
  text-transform: uppercase; letter-spacing: .06em;
  padding: 8px 16px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--field-bg); color: var(--fg);
  cursor: pointer;
}
.empty-cta:hover { color: var(--accent); border-color: var(--accent); }
.empty-hint { color: var(--fg-disabled); font-size: var(--fs-mini); line-height: 1.55; margin: 14px 0 0; }
.empty-hint strong { color: var(--muted); font-weight: 400; }

/* ---------- right sidebar (properties) — SLIDE-IN OVERLAY ----------
   A fixed panel pinned to the right edge that SLIDES in/out from the side.
   Slightly translucent (matte) so the canvas reads underneath. A grab handle
   on its left edge toggles it. */
#sidebar {
  position: fixed;
  top: 44px;   /* below the floating top-right cluster */
  right: 0;
  bottom: 0;
  width: min(var(--sidebar-w), 88vw);
  z-index: 10;
  border-left: var(--rule);
  background: var(--panel);
  /* overflow VISIBLE so the left-edge handle (which protrudes out of the box)
     isn't clipped; the inner #sidebar-scroll does the scrolling. */
  overflow: visible;
  display: flex;
  flex-direction: column;
  transform: translateX(0);
  transition: transform 0.22s ease;
}
#sidebar-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Sidebar section switch — ledzeppelin FOLDER-TAB idiom: the active tab is a
   filled block (accent text) that merges into the pane below (same surface), so
   "you're in this folder" reads as one connected surface, not just bright text. */
#side-tabs {
  flex: 0 0 auto;
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--border-color);
  background: var(--panel);
}
.side-tab {
  flex: 1 1 0;
  text-align: center;
  background: transparent;
  color: var(--fg-disabled);
  border: none;
  padding: 7px 0;
  font: var(--fs-small) var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  cursor: pointer;
  position: relative;
}
.side-tab:hover { color: var(--muted); }
.side-tab.active {
  color: var(--accent);
  background: var(--bg-surface);
  /* bleed 1px over the bottom border so the tab and its pane read as one slab */
  margin-bottom: -1px;
  padding-bottom: 8px;
}
/* The active pane sits on the same surface the active tab merged into. */
#pane-add, #pane-scene, #pane-item, #pane-setup { background: var(--bg-surface); }
/* Attention dot on a tab whose (hidden) pane has something to look at —
   e.g. validation problems while you're on Add/Item. */
.side-tab.has-attention::after {
  content: "●";
  position: absolute;
  top: 4px;
  margin-left: 3px;
  font-size: 7px;
  color: var(--warn);
}
.side-pane[hidden] { display: none; }
#app[data-sidebar="hidden"] #sidebar { transform: translateX(100%); }
/* Grab handle on the LEFT edge of the sidebar (the canvas-facing side). It's
   positioned just LEFT of the panel, so when the panel slides off-screen it stays
   poking out at the screen's right edge. */
#sidebar-handle {
  position: absolute;
  left: -24px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 72px;
  padding: 0;
  border: var(--rule);
  border-right: 0;
  border-radius: var(--r-sm) 0 0 var(--r-sm);
  background: var(--bg-elevated);
  color: var(--muted);
  cursor: pointer;
  font-size: var(--fs-small);
  display: flex;
  align-items: center;
  justify-content: center;
}
#sidebar-handle:hover { color: var(--accent); }
/* Hidden in the non-Layout views (the sidebar is Layout-only chrome). */
#app[data-view="library"] #sidebar,
#app[data-view="bom"] #sidebar { display: none; }
#layers-host  { flex: 0 0 auto; }
/* The whole sidebar scrolls (it carries Problems/Groups/Phases/Layers + the
   per-selection Inspector). */
#inspector { padding: var(--s-3) var(--s-4); }

.panel {
  padding: var(--s-3) var(--s-4);
  border-bottom: var(--rule);
}
/* Group headers — ledzeppelin tier: accent micro-caps over an accent-tinted rule. */
.panel h2,
#inspector h2 {
  margin: 0 0 var(--s-2);
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
  padding-bottom: 4px;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}

/* Collapsible panel group (Layers / Problems / Groups / Phases) — ledzeppelin
   .insp-sec: accent micro-caps header with a left disclosure triangle over an
   accent-tinted rule; the body shows when [open]. */
.panel-group { border-bottom: var(--rule); }
.panel-group-summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--s-3) var(--s-4) 5px;
  margin-bottom: 4px;
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
  user-select: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
.panel-group-summary:hover { color: var(--accent-text, var(--fg)); }
.panel-group-summary::-webkit-details-marker { display: none; }
/* Disclosure triangle (▸ rotates to ▾ when open), drawn as a CSS triangle. */
.panel-group-summary::before {
  content: ""; flex: 0 0 auto; width: 0; height: 0;
  border-left: 4px solid var(--accent);
  border-top: 3px solid transparent; border-bottom: 3px solid transparent;
  transition: transform .12s;
}
.panel-group[open] > .panel-group-summary::before { transform: rotate(90deg); }
.panel-group > .panel { border-top: none; border-bottom: 0; padding-top: 0; }
.panel-group:not([open]) > .panel { display: none; }

/* ---------- inventory (lists Library-designed types as placeable items) ---------- */
/* Folded kind groups (ledzeppelin section disclosure): just the kind + count
   until opened — the Add tab scans as a short menu, not a wall of options. */
.inv-group { margin-bottom: var(--s-1); }
.inv-group:last-of-type { margin-bottom: var(--s-2); }
.inv-group-title {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 5px 2px;
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--muted);
  margin: 0;
}
.inv-group-title::-webkit-details-marker { display: none; }
.inv-group-title::before { content: "▸"; color: var(--fg-disabled); font-size: 8px; }
.inv-group[open] > .inv-group-title::before { content: "▾"; }
.inv-group-title:hover { color: var(--accent); }
.inv-group[open] > .inv-group-title { color: var(--accent); }
.inv-group-count {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--fg-disabled);
}
.inv-group[open] { padding-bottom: var(--s-2); }
/* Footer link under the inventory → jumps to the Library view to design types. */
.inv-lib-link {
  display: block;
  width: 100%;
  margin-top: var(--s-2);
  padding: 5px 0;
  font: var(--fs-mini) var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  text-align: center;
  background: none;
  border: 1px dashed var(--border-color);
  border-radius: var(--radius);
  color: var(--muted);
  cursor: pointer;
}
.inv-lib-link:hover { color: var(--accent); border-color: var(--accent); }

/* ---------- palette strip-type rows ---------- */
/* Placeable rows: BORDERLESS (ledzeppelin) — structure comes from hairline
   separators, emphasis from text turning accent. "+" affordance at the right. */
.ptype {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  width: 100%;
  text-align: left;
  font: inherit;
  position: relative;
  padding: 5px 22px 5px 14px;
  margin: 0;
  border: none;
  border-top: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent);
  border-radius: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.ptype:first-of-type { border-top: none; }
.ptype::after {
  content: "+";
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--fg-disabled);
  font-size: var(--fs-large);
}
.ptype:hover .ptype-name, .ptype:hover::after { color: var(--accent); }
.ptype.active .ptype-name { color: var(--accent); }
.ptype-name { font-size: var(--fs-small); color: var(--fg); }
.ptype-meta { font-size: var(--fs-mini); color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---------- layers panel rows ---------- */
.layer-row {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  padding: 1px 4px;
  min-height: var(--row-h);
  margin-bottom: 0;
  cursor: pointer;
}
.layer-row:hover { background: var(--soft); }
/* Active = accent NAME only (text-state language — no fill, no border). */
.layer-row.active .layer-name { color: var(--accent); }
/* Name is a click-to-select span (the row selects); the swapped-in rename input
   inherits the same box. */
.layer-row .layer-name {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  font-size: var(--fs-small);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--fg);
  padding: 2px 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
input.layer-name { cursor: text; }
input.layer-name:focus { outline: none; border-color: var(--line-2); background: var(--field-bg); }
.layer-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: var(--fs-small);
  line-height: 1;
  padding: 2px 3px;
  border-radius: var(--r-sm);
  color: var(--muted);
}
.layer-btn:hover { color: var(--fg); background: var(--soft-2); }
.layer-btn.layer-active.on { color: var(--accent); }
/* Lock: ONE glyph, state = color (faint when unlocked, accent when locked). */
.layer-btn.layer-lock { color: var(--fg-disabled); }
.layer-btn.layer-lock.on { color: var(--accent); }
.layer-btn.layer-up, .layer-btn.layer-down { color: var(--fg-disabled); font-size: var(--fs-mini); }
.layer-btn.layer-up:hover, .layer-btn.layer-down:hover { color: var(--fg); }
.layer-btn.layer-del { color: var(--fg-disabled); }
.layer-btn.layer-del:hover { color: var(--warn); }
.layer-btn:disabled { opacity: 0.3; cursor: default; }
.layer-swatch {
  width: 16px;
  height: 16px;
  padding: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  cursor: pointer;
  flex: 0 0 auto;
  background: transparent;
}

/* ---------- groups panel (mirrors layers) ---------- */
/* Mains phases panel rows: leg name + load readout; amps go red over 16 A. */
.phase-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s-1); padding: 0 4px; min-height: calc(var(--row-h) - 6px); font-size: var(--fs-mini); }
.phase-name { color: var(--fg); }
.phase-meta { color: var(--muted); font-variant-numeric: tabular-nums; }
.phase-amps.over { color: var(--warn); }

.group-row {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  padding: 1px 4px;
  min-height: var(--row-h);
  margin-bottom: 0;
  cursor: pointer;
}
.group-row:hover { background: var(--soft); }
.group-row.active .group-name,
.group-row.selected .group-name { color: var(--accent); }
.group-row .group-name {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  font-size: var(--fs-small);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--fg);
  padding: 2px 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
input.group-name { cursor: text; }
input.group-name:focus { outline: none; border-color: var(--line-2); background: var(--field-bg); }
.group-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: var(--fs-small);
  line-height: 1;
  padding: 2px 3px;
  border-radius: var(--r-sm);
  color: var(--muted);
}
.group-btn:hover { color: var(--fg); background: var(--soft-2); }
.group-btn.group-ungroup { color: var(--fg-disabled); flex: 0 0 auto; font-size: var(--fs-mini); }
.group-btn.group-ungroup:hover { color: var(--warn); }
.group-swatch {
  width: 16px;
  height: 16px;
  padding: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  cursor: pointer;
  flex: 0 0 auto;
  background: transparent;
}

/* ---------- controller output rows ---------- */
.out-row { display: flex; align-items: center; gap: var(--s-1); margin-bottom: 4px; }
.out-row input { flex: 1 1 auto; width: 100%; }
/* read-only output readout in the main inspector (no editable input) */
.out-row.readonly { justify-content: space-between; }
.out-row.readonly .out-flag { flex: 0 0 auto; }
.ctrl-out-readout { margin: 4px 0 6px; }
/* Editable per-output budget rows inside the Library's aligned table. Grid
   layout so id | budget input | wired-flag line up with the .out-head columns. */
.ctrl-out-table .out-row {
  display: grid;
  grid-template-columns: 5rem 1fr auto;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: 4px;
}
.ctrl-out-table .out-row input { width: 100%; }
.ctrl-out-table .out-flag { text-align: right; justify-self: end; }

/* Structured wiring: per-output daisy-chain builder in the controller inspector */
.ctrl-out-wiring { margin: 4px 0 6px; }
.chain-output { margin-bottom: 8px; padding-bottom: 7px; border-bottom: 1px solid color-mix(in srgb, var(--border-color) 60%, transparent); }
.chain-output:last-child { border-bottom: 0; }
.chain-output .out-row { justify-content: space-between; }
.chain-list { margin: 4px 0; padding-left: 6px; }
.chain-strip { display: flex; align-items: center; justify-content: space-between; gap: var(--s-1); padding: 2px 0; }
.chain-rm {
  flex: 0 0 auto; border: 0; background: none; cursor: pointer;
  color: var(--fg-disabled); font-size: var(--fs-mini); line-height: 1; padding: 2px 4px; border-radius: var(--r-sm);
}
.chain-rm:hover { color: var(--warn); background: color-mix(in srgb, var(--warn) 12%, transparent); }
.chain-add { width: 100%; }

/* ---------- BOM view (own destination, like Library) ---------- */
/* Occupies the whole content area (stage + sidebar columns) when active. Hidden
   in Layout/Library; shown when #app[data-view="bom"]. Driven by #app[data-view]. */
#bom-view {
  grid-column: 1 / -1;
  grid-row: 1;
  display: none;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  background: var(--bg);
  padding-top: 44px;   /* clear the floating top clusters */
}
#app[data-view="bom"] #bom-view { display: flex; }
#app[data-view="bom"] #stage-wrap,
#app[data-view="bom"] #sidebar { display: none; }
.bom-view-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-5);
  border-bottom: var(--rule);
  flex: 0 0 auto;
}
.bom-view-head h2 {
  margin: 0;
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
}
.bom-view-actions { display: flex; gap: var(--s-2); }
#bom {
  overflow-y: auto;
  flex: 1 1 auto;
  padding: var(--s-6) var(--s-5) var(--s-8);
}

/* The materials document — a centered, max-width page that reads as a printed
   spec sheet rather than a panel widget. */
.bom-doc {
  max-width: 760px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}
.bom-doc-title { margin: 0; }
.bom-doc-title h1 {
  margin: 0 0 var(--s-1);
  font-size: var(--fs-2xl);
  font-weight: 400;
  color: var(--fg-title);
  letter-spacing: var(--ls-ui);
}
.bom-doc-sub {
  margin: 0;
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--muted);
}

/* Summary block: labelled stat cells in a responsive grid. */
.bom-summary {
  display: grid;
  /* 9 stat cells → a 3-wide grid fills exactly 3 rows with no empty cells. */
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--rule-color);
  border: var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}
@media (max-width: 600px) {
  .bom-summary { grid-template-columns: repeat(2, 1fr); }
}
.bom-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  background: var(--bg-surface);
}
.bom-stat-label {
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--fg-disabled);
}
.bom-stat-value {
  font-size: var(--fs-xl);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--fg-title);
  line-height: 1.1;
}
.bom-stat-sub {
  font-size: var(--fs-mini);
  color: var(--muted);
}
.bom-stat.warn .bom-stat-value { color: var(--amber); }
.bom-stat.warn .bom-stat-label { color: var(--amber); }

/* Each line-item group: an accent micro-caps section header over the table. */
.bom-doc-section { margin: 0; }
.bom-section-head {
  margin: 0 0 var(--s-2);
  padding-bottom: var(--s-1);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
}

/* Instrument table — mono headers + data, tabular-nums on numeric cols. */
.bom-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
}
.bom-table th {
  text-align: left;
  font-weight: 400;
  color: var(--fg-disabled);
  padding: 5px 14px 5px 0;
  border-bottom: var(--rule);
  text-transform: uppercase;
  font-size: var(--fs-mini);
  letter-spacing: var(--ls-caps);
  white-space: nowrap;
}
.bom-table td {
  padding: 5px 14px 5px 0;
  vertical-align: baseline;
  border-bottom: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent);
  white-space: nowrap;
  color: var(--muted);
}
.bom-table tbody td:first-child { color: var(--fg); }
.bom-table th.num, .bom-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.bom-table th:last-child, .bom-table td:last-child { padding-right: 0; }
.bom-table tbody tr:last-child td { border-bottom: 0; }
.bom-table tfoot td {
  padding: 7px 14px 0 0;
  border-top: var(--rule);
  border-bottom: 0;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}
.bom-table tfoot td:last-child { padding-right: 0; }

/* Stock check: needed vs available with an over/under flag. */
.bom-stock {
  border: var(--rule);
  border-radius: var(--radius);
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-small);
  background: var(--bg-surface);
}
.bom-stock-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 4px 0;
}
.bom-stock-row .num { font-variant-numeric: tabular-nums; }
.bom-stock-label { color: var(--muted); }
.bom-stock-flag {
  border-top: var(--rule);
  margin-top: 3px;
  padding-top: 7px;
}
.bom-stock.over { border-color: var(--warn); }
.bom-stock.over .bom-stock-flag,
.bom-stock.over .bom-stock-flag .num { color: var(--warn); }
.bom-stock.ok .bom-stock-flag,
.bom-stock.ok .bom-stock-flag .num { color: var(--ok); }

/* ---------- system power reconciliation (placed PSU vs draw) ---------- */
/* Power budget — the four power figures as one reconciled chain (LED draw →
   recommended supply → placed PSUs → coverage → mains load). Each row: label ·
   value (right-aligned tabular) · note. */
.bom-budget {
  border: var(--rule);
  border-radius: var(--radius);
  background: var(--bg-surface);
  overflow: hidden;
}
.bom-budget-row {
  display: grid;
  grid-template-columns: 9rem auto 1fr;
  align-items: baseline;
  gap: var(--s-2) var(--s-4);
  padding: 6px var(--s-4);
  font-size: var(--fs-small);
  border-top: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent);
}
.bom-budget-row:first-child { border-top: none; }
.bom-budget-label { color: var(--fg); }
.bom-budget-val { font-variant-numeric: tabular-nums; color: var(--fg-title); text-align: right; }
.bom-budget-note { color: var(--muted); font-size: var(--fs-mini); justify-self: start; }
/* The coverage verdict row gets the ok/over emphasis. */
.bom-budget-row.ok { background: color-mix(in srgb, var(--ok) 7%, transparent); }
.bom-budget-row.ok .bom-budget-label, .bom-budget-row.ok .bom-budget-val { color: var(--ok); }
.bom-budget-row.over { background: color-mix(in srgb, var(--warn) 8%, transparent); }
.bom-budget-row.over .bom-budget-label, .bom-budget-row.over .bom-budget-val { color: var(--warn); }

/* ---------- inspector form ---------- */
/* Collapsible section — ledzeppelin .insp-sec: an accent-caps header with a
   ▸/▾ disclosure triangle over an accent-tinted rule; the body folds away.
   makeSectionsCollapsible() wraps each h3.sub at render time. */
.insp-sec { margin: 0; }
.insp-sec-head {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 7px 2px 5px; margin-top: var(--s-2);
  background: none; border: none; border-radius: 0; cursor: pointer;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
.insp-sec:first-child .insp-sec-head { margin-top: 0; }
.insp-tri {
  width: 0; height: 0; flex: 0 0 auto;
  border-left: 4px solid var(--accent);
  border-top: 3px solid transparent; border-bottom: 3px solid transparent;
  transition: transform .12s;
}
.insp-sec.is-open .insp-tri { transform: rotate(90deg); }
.insp-sec-body { display: none; padding: var(--s-2) 0 var(--s-3); }
.insp-sec.is-open .insp-sec-body { display: block; }
/* The h3.sub now lives INSIDE the head button: bare accent caps (the rule +
   triangle come from the wrapper). The standalone rules below still apply to
   any h3.sub that wasn't wrapped (defensive). */
#inspector h3.sub,
.dialog-body h3.sub {
  margin: 0; padding: 0;
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
}
.insp-sec-head:hover h3.sub { color: var(--accent-text, var(--fg-title)); }
/* A faint SUB-label tier (no rule, no accent) for quiet inline group labels —
   the second ledzeppelin header tier. */
.sublabel {
  font: var(--fs-mini)/1.2 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--fg-disabled);
  margin: var(--s-3) 0 var(--s-1);
}
/* A faint SUB-label tier (no rule, no accent) for quiet inline group labels —
   the second ledzeppelin header tier. */
.sublabel {
  font: var(--fs-mini)/1.2 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--fg-disabled);
  margin: var(--s-3) 0 var(--s-1);
}
/* HORIZONTAL row module (ledzeppelin .fx-field): label in a fixed LEFT column,
   control fills the rest, one shared row height. The label is the <label>'s
   bare text node — grid places anonymous text in column 1 automatically, so
   the `<label class="fld">Name <input/></label>` markup stays untouched. */
.fld {
  display: grid;
  grid-template-columns: var(--col-label) 1fr;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  min-height: var(--row-h);
  margin-bottom: 1px;
  font-size: var(--fs-small);
  color: var(--muted);
}
/* Grid auto-placement does the work: the bare label text becomes an anonymous
   item in column 1, the control lands in column 2. Tall controls (textarea)
   just grow the row. */
.fld > textarea { grid-column: 2; }
.fld input, .fld select, .sel {
  font: var(--fs-base) var(--font);
  color: var(--fg);
  padding: 4px 7px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: var(--field-bg);
}
.fld input:focus, .fld select:focus, .sel:focus {
  outline: none;
  border-color: var(--accent);
}
.fld input[type="number"] { font-variant-numeric: tabular-nums; }

/* Value-box + slider param row (ledzeppelin .ly-row): label · recessed value
   box (right-aligned number) · accent-fill slider. Three columns; the value box
   is fixed-width, the slider fills. wireSliderRows() keeps them synced. */
.fld.range { grid-template-columns: var(--col-label) var(--col-val) 1fr; }
.fld.range .range-val {
  grid-column: 2;
  width: 100%;
  text-align: right;
  padding: 0 6px;
  height: var(--ctrl-h);
  font-variant-numeric: tabular-nums;
}
.fld.range .range-slider {
  grid-column: 3;
  align-self: center;
  width: 100%;
  background: transparent;
  border: none;
  padding: 0;
}
/* Library "View product ↗" link — a quiet inline anchor under the model fields. */
.lib-product-link {
  display: inline-block;
  margin-bottom: var(--s-2);
  font-size: var(--fs-small);
  color: var(--accent);
  text-decoration: none;
}
.lib-product-link:hover { text-decoration: underline; }
.fld-check {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-2);
  font-size: var(--fs-small);
  color: inherit;
  cursor: pointer;
}
.fld-check input { width: auto; margin: 0; }
.fld input[type="color"] { padding: 2px; height: 26px; cursor: pointer; }

/* Sliders — ledzeppelin .ly-row range: a thin track with an accent fill (left of
   the thumb) and a small round accent thumb; no native chrome. Firefox uses
   ::-moz-range-progress for the fill; WebKit uses a value-based gradient driven
   by --fill (set in JS where available, else a centered default). */
input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 14px; padding: 0; border: 0; margin: 0;
  background: transparent; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: var(--r-sm);
  background: linear-gradient(to right, var(--accent) var(--fill, 50%), var(--line-2) var(--fill, 50%));
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 12px; height: 12px; margin-top: -4px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg-surface);
}
input[type="range"]::-moz-range-track { height: 4px; border-radius: var(--r-sm); background: var(--line-2); }
input[type="range"]::-moz-range-progress { height: 4px; border-radius: var(--r-sm); background: var(--accent); }
input[type="range"]::-moz-range-thumb { width: 12px; height: 12px; border: 2px solid var(--bg-surface); border-radius: 50%; background: var(--accent); }

.btn-row { display: flex; gap: var(--s-2); margin-top: var(--s-2); }
.btn-row.wrap { flex-wrap: wrap; }
/* Buttons — ledzeppelin .ctrl-btn: uppercase hairline, accent text on hover. */
.btn {
  font: var(--fs-small) var(--font);
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: 5px 10px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: var(--field-bg);
  color: var(--fg);
  cursor: pointer;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.btn:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn.ghost { background: transparent; border-color: var(--border-color); color: var(--muted); }
.btn.ghost:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.btn.mini { padding: 3px 7px; font-size: var(--fs-mini); }
/* Panel action buttons span the full sidebar width (cleaner than hugging text).
   flex-basis 100% in their wrap rows stacks each onto its own full-width line. */
#group-create,
#phase-balance,
#layer-add,
#layer-move-sel,
#layer-auto { flex: 1 1 100%; }

/* Custom checkbox to match the matte theme (ledzeppelin). */
input[type=checkbox] {
  appearance: none; -webkit-appearance: none; margin: 0;
  width: 13px; height: 13px; flex: 0 0 auto; vertical-align: middle;
  border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--field-bg);
  cursor: pointer; position: relative;
}
input[type=checkbox]:checked { background: var(--accent); border-color: var(--accent); }
input[type=checkbox]:checked::after {
  content: ''; position: absolute; left: 4px; top: 1px; width: 3px; height: 6px;
  border: solid var(--accent-ink); border-width: 0 2px 2px 0; transform: rotate(45deg);
}

/* (Focus-ring handling lives in the shared backbone block at the top.) */

/* ---------- electrical warnings ---------- */
.warn-line {
  font-size: var(--fs-small);
  color: var(--amber);
  background: color-mix(in srgb, var(--amber) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent);
  border-radius: var(--radius);
  padding: 5px 7px;
  margin: var(--s-1) 0;
}
.ok-line {
  font-size: var(--fs-small);
  color: var(--ok);
  background: color-mix(in srgb, var(--ok) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--ok) 30%, transparent);
  border-radius: var(--radius);
  padding: 5px 7px;
  margin: var(--s-1) 0;
}
.out-flag { font-size: var(--fs-mini); flex: 0 0 auto; }
.out-flag.over { color: var(--warn); }
.out-flag.ok { color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---------- settings (the sidebar SETUP pane — no modal) ---------- */
/* #settings-body keeps the .dialog-body class so renderSettings' markup lands
   unchanged; it now lives inside a .panel in the Setup pane. */
.dialog-body { padding: 0; display: grid; gap: var(--s-2); }
.dialog-body .settings-section + .settings-section {
  margin-top: var(--s-2);
  padding-top: var(--s-2);
  border-top: var(--rule);
}
.dialog-body .settings-section h3.sub:first-child { margin-top: 0; }
.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--s-2) var(--s-4);
}

/* ---------- library view (fixture editor) ---------- */
/* The library occupies the whole content area (stage + sidebar cols) when
   active. Driven by #app[data-view]. */
#library-view {
  grid-column: 1 / -1;
  grid-row: 1;
  display: none;
  /* Editor LEFT, type list RIGHT — mirrors the Layout view (canvas left,
     panels in a right sidebar), so the two views read as one shell. */
  grid-template-columns: 1fr 19rem;
  min-height: 0;
  overflow: hidden;
  background: var(--bg);
  padding-top: 44px;   /* clear the floating top clusters */
}
#app[data-view="library"] #library-view { display: grid; }
#app[data-view="library"] #stage-wrap,
#app[data-view="library"] #sidebar { display: none; }

#library-list {
  grid-column: 2;
  grid-row: 1;
  border-left: var(--rule);
  overflow-y: auto;
  padding: 0;
  display: flex;
  flex-direction: column;
}
#library-editor {
  grid-column: 1;
  grid-row: 1;
  overflow-y: auto;
  padding: var(--s-6) var(--s-6) var(--s-8);
  min-width: 0;
}

/* List header: title + the grouped "New …" action row (sticky at the top). */
.lib-list-head {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--s-3) var(--s-4);
  border-bottom: var(--rule);
  background: var(--bg);
}
.lib-list-title {
  margin: 0 0 var(--s-2);
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
}
.lib-groups { padding: var(--s-3) var(--s-4) var(--s-5); }

.lib-group { margin-bottom: var(--s-5); }
.lib-group:last-child { margin-bottom: 0; }
/* Section header: the section name + count on the left, its own "+ Add" button
   on the right. Each section thus owns the affordance for adding that kind. */
.lib-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  margin: 0 0 var(--s-2);
  padding-bottom: var(--s-1);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
.lib-group-title {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font: var(--fs-mini)/1 var(--font);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
  margin: 0;
}
.lib-group-add { flex: none; white-space: nowrap; }
.lib-group-count {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  background: var(--soft-2);
  border-radius: 999px;
  padding: 1px 6px;
  font-size: var(--fs-mini);
}
.lib-group-empty { margin: 0; }
/* Library list rows — borderless, hairline-separated; selection = accent TEXT. */
.lib-cards { display: flex; flex-direction: column; gap: 0; }
.lib-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  text-align: left;
  font: inherit;
  padding: var(--s-2) var(--s-1);
  border: none;
  border-top: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent);
  border-radius: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.lib-item:first-child { border-top: none; }
.lib-item:hover .lib-item-name { color: var(--accent); }
.lib-item.active .lib-item-name { color: var(--accent); }
.lib-item-name { font-size: var(--fs-small); color: var(--fg); }
.lib-item-meta { font-size: var(--fs-mini); color: var(--muted); font-variant-numeric: tabular-nums; }

/* Editor pane — a constrained, well-spaced document, CENTERED within the pane. */
.lib-editor-doc { max-width: 38rem; margin-inline: auto; }
.lib-editor-head { margin: 0 0 var(--s-5); }
.lib-editor-block + .lib-editor-block { margin-top: var(--s-6); }
.lib-editor-block .sub:first-child { margin-top: 0; }
.lib-editor-foot {
  margin-top: var(--s-6);
  padding-top: var(--s-4);
  border-top: var(--rule);
}
#library-editor h2 { margin: 0; font-size: var(--fs-large); font-weight: 400; color: var(--fg-title); letter-spacing: var(--ls-ui); }
#library-editor .lib-kind {
  display: block;
  margin-bottom: 3px;
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--accent);
  font-weight: 400;
}
#library-editor .lib-empty { color: var(--muted); }
.lib-derived {
  margin: var(--s-3) 0 0;
  padding: var(--s-3);
  border: var(--rule);
  border-radius: var(--radius);
  background: var(--bg-surface);
  font-size: var(--fs-small);
  font-variant-numeric: tabular-nums;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 4px var(--s-3);
  color: var(--muted);
}
/* Allow long labels to wrap within their cell instead of overflowing into the
   next column. */
.lib-derived span { margin-right: 0; white-space: normal; overflow-wrap: anywhere; }
.lib-fields { max-width: none; }

/* Per-output budget table (controller types) — aligned grid with a header. */
.ctrl-out-table { margin-top: var(--s-2); }
.out-head {
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--fg-disabled);
  padding-bottom: var(--s-1);
  margin-bottom: var(--s-1);
  border-bottom: var(--rule);
}
.out-head span:last-child { text-align: right; }
.out-id {
  font-size: var(--fs-small);
  color: var(--muted);
}

/* ports / channels editor — a tidy aligned table with a header row. */
.port-table { display: flex; flex-direction: column; gap: 4px; }
.port-row {
  display: grid;
  grid-template-columns: 1.6fr 6rem 1fr 2rem;
  gap: var(--s-2);
  align-items: center;
}
.port-head {
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--fg-disabled);
  padding-bottom: var(--s-1);
  margin-bottom: 1px;
  border-bottom: var(--rule);
}
.port-head span { padding-left: 1px; }
/* Match the recessed field styling used by .fld inputs so the channel editor
   reads as part of the same instrument. */
.port-row input, .port-row select {
  width: 100%;
  font: var(--fs-base) var(--font);
  color: var(--fg);
  padding: 4px 7px;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: var(--field-bg);
}
.port-row input:focus, .port-row select:focus {
  outline: none;
  border-color: var(--accent);
}
.port-row [data-pf="del"] { justify-self: center; }

/* per-type cable color list (settings) */
.cable-color-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: 4px;
  font-size: var(--fs-small);
}
.cable-color-row .cc-name { flex: 1 1 auto; color: var(--muted); }
.cable-color-row input[type="color"] { width: 2.4rem; height: 24px; padding: 2px; cursor: pointer; background: var(--field-bg); border: 1px solid var(--line-2); border-radius: var(--r-sm); }

/* ---------- double-click add palette (TouchDesigner "OP Create") ---------- */
#add-palette {
  position: fixed;
  z-index: 70;
  width: 240px;
  background: var(--bg-elevated);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.5);
  padding: var(--s-2);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
#add-palette-q {
  width: 100%;
  font: var(--fs-base) var(--font);
  color: var(--fg);
  background: var(--field-bg);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  padding: 4px 7px;
}
#add-palette-q:focus { outline: none; border-color: var(--accent); }
.add-palette-list { max-height: 300px; overflow-y: auto; display: flex; flex-direction: column; }
.add-palette-group {
  margin: var(--s-2) 0 1px;
  font-size: var(--fs-mini);
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
  color: var(--fg-disabled);
}
.add-palette-group:first-child { margin-top: 0; }
.add-palette-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-2);
  text-align: left;
  font: var(--fs-small) var(--font);
  padding: 4px 5px;
  background: none;
  border: none;
  border-radius: var(--r-sm);
  color: var(--fg);
  cursor: pointer;
}
.add-palette-item:hover { background: var(--soft-2); color: var(--accent); }
.add-palette-meta { font-size: var(--fs-mini); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.add-palette-empty { margin: var(--s-2) 0; font-size: var(--fs-mini); color: var(--fg-disabled); text-align: center; }

/* ---------- toast ---------- */
/* A quiet near-black card with a hairline; the KIND tints the text, not the
   whole card (matte, not a colored pill). */
#toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%) translateY(8px);
  background: var(--bg-elevated);
  color: var(--fg);
  border: 1px solid var(--line-2);
  padding: 7px 14px;
  border-radius: var(--radius);
  font-size: var(--fs-small);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, transform 0.18s;
  z-index: 80;
  max-width: 60vw;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
#toast[data-kind="error"] { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
#toast[data-kind="warn"]  { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); }
#toast[data-kind="ok"]    { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }

/* ---------- print: Layout-only ----------
   When the Layout view is active, Cmd/Ctrl+P (and the Export ▸ Print/PDF action)
   should print ONLY the Layout canvas + its on-canvas legend — no top bar, no
   right sidebar, no floating tool rail, no other chrome. */
@media print {
  @page { margin: var(--print-margin); }

  /* Reset the app grid so the stage can take the full printable page. */
  #app[data-view="layout"] {
    display: block;
    height: auto;
    width: auto;
  }
  /* Hide every piece of chrome around the Layout. */
  #app[data-view="layout"] #topbar,
  #app[data-view="layout"] #sidebar,
  #app[data-view="layout"] #toolrail,
  #app[data-view="layout"] #library-view,
  #app[data-view="layout"] #bom-view,
  #toast {
    display: none !important;
  }
  /* Let the stage flow on the page (no fixed grid box / no clipping overflow). */
  #app[data-view="layout"] #stage-wrap {
    position: static;
    overflow: visible;
    background: transparent;
    width: auto;
    height: auto;
  }
  /* Scale the canvas raster to the page width, keeping its aspect (height:auto)
     and never exceeding the printable height, so the whole Layout fits. */
  #app[data-view="layout"] #stage {
    width: 100% !important;
    height: auto !important;
    max-height: 100vh;
    object-fit: contain;
  }
}
