/* VOID brand palette. Values taken from the brand asset sheet rather than
   sampled from the website, which runs a slightly different orange.

   The one place the sheet cannot be followed literally is destructive UI:
   every conventional danger red lands within ~20 degrees of the brand orange,
   so a red button and a primary button would read the same at a glance. Two
   things resolve it — destructive buttons separate by TREATMENT (a filled
   block, see button.danger) rather than hue, and --danger itself carries a
   pink cast so error text never reads as brand orange. */
:root {
  --bg: #000000;
  --panel: #0C0B0B;
  --panel-2: #151312;
  --border: #2B2724;
  --text: #D3CFC7;          /* bone */
  --text-dim: #6F695E;      /* taupe */
  --accent: #EB4927;
  --accent-strong: #FF6A47;
  --accent-2: #C43A1D;
  --periwinkle: #BFBDEE;    /* the sheet's secondary; used for partial states */
  --danger: #F2456B;
  --danger-ink: #FFFFFF;
  /* The brand is built from hard rectangles. Rounded corners were the single
     biggest thing making this read as a different product. */
  --radius: 2px;
  --radius-sm: 2px;
}

* { box-sizing: border-box; }

/* The `hidden` attribute must always win. Without this, any class on a
   hidden element that sets its own `display` (flex/grid/block/...) silently
   overrides the browser's default [hidden]{display:none} and the element
   stays visible — bit us twice already (.tab-panel, .no-reference). */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
}

h1, h2, h3, .brand, .wordmark {
  font-family: "Archivo", -apple-system, BlinkMacSystemFont, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

button, input, select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

button {
  background: transparent;
  border: 1.5px solid var(--border);
  color: var(--text);
  padding: 8px 16px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-weight: 600;
  transition: border-color 0.15s, background 0.15s, filter 0.15s;
}
button:hover { border-color: var(--accent); }
button.primary {
  background: linear-gradient(180deg, var(--accent-strong), var(--accent));
  border-color: var(--accent);
  color: #201503;
  font-weight: 700;
}
button.primary:hover { filter: brightness(1.06); }
button.danger {
  background: var(--accent); color: var(--danger-ink);
  border-color: var(--accent); font-weight: 700;
}
button.danger:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
button:disabled { opacity: 0.4; cursor: not-allowed; }
button.small { padding: 5px 12px; font-size: 12px; }

/* Non-pill utility buttons (compact toolbar controls) keep a tighter shape */
.canvas-toolbar button, .mode-group button { border-radius: var(--radius-sm); }

input[type="text"], input[type="number"], input[type="password"],
input[type="time"], input[type="date"] {
  background: var(--bg);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 7px 14px;
  width: 100%;
}
input[type="text"]:focus, input[type="number"]:focus, input[type="password"]:focus,
input[type="time"]:focus, input[type="date"]:focus { outline: none; border-color: var(--accent); }
/* Time and date fields draw their own guts — the digits, the separators, the
   picker glyph. Without color-scheme the browser paints that furniture for a
   light page and the field arrives white in the middle of a black one. */
input[type="time"], input[type="date"] {
  color-scheme: dark;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 12.5px;
}

label { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 4px; }

.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  flex-wrap: wrap;
}
.topbar .brand {
  font-weight: 700;
  letter-spacing: 0.01em;
  display: flex; align-items: center; gap: 9px;
  font-size: 17px;
  color: var(--text);
  white-space: nowrap;
}
.topbar .brand .dot {
  width: 11px; height: 11px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--accent-strong), var(--accent) 70%);
  box-shadow: 0 0 10px rgba(225, 92, 46, 0.6);
  flex-shrink: 0;
}
.app-version {
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
}
.topbar .spacer { flex: 1; }
.topbar input[type="text"] { max-width: 320px; }
.topbar button { white-space: nowrap; }

@media (max-width: 640px) {
  .topbar { gap: 10px; padding: 12px 14px; }
  .topbar .spacer { flex-basis: 100%; height: 0; order: 2; }
  .topbar input[type="text"] { order: 3; flex: 1 1 auto; max-width: none; min-width: 0; }
  .topbar .badge { order: 3; }
  .topbar button.primary { order: 1; margin-left: auto; }
}

.container { max-width: 1100px; margin: 0 auto; padding: 28px 20px 60px; }
.container h2 { font-size: 26px; margin-bottom: 4px; }
/* The Media page's grid is the widest, most content-dense thing this
   container ever holds — centering it left as much dead space on the left
   (next to the sidebar) as on the right, which reads as the whole library
   sitting oddly far from the nav it belongs to. Hugging the sidebar instead
   fixes that without changing the centered layout every other, lighter
   page (Projects, Orchestrator, ...) already uses on purpose. */
.container-media { margin-left: 0; max-width: 1560px; }

/* ---- Project list ---- */
.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
  margin-top: 20px;
}
.project-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.15s, transform 0.15s;
}
.project-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.project-card .thumb {
  aspect-ratio: 16/9;
  background: var(--panel-2) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); font-size: 12px;
}
.project-card .body { padding: 14px 16px; display: flex; flex-direction: column; gap: 6px; }
.project-card .name { font-weight: 700; font-family: "Archivo", sans-serif; }
.project-card .meta { color: var(--text-dim); font-size: 12px; }
.project-card .row { display: flex; justify-content: space-between; align-items: center; margin-top: 4px; }

/* .row itself was never actually given display:flex anywhere outside this
   one project-card scope -- every other call site across the app (settings,
   projector detail, spatial, this page's own sort control) passes
   justify-content/align-items/gap/flex-wrap in an inline style expecting
   flex layout, and all of them have been silently no-ops: a plain div has
   no justify-content, and gap does nothing in normal block flow. Confirmed
   live on the Projectors page's own Sort control, which rendered as a
   full-width block instead of a compact right-aligned row. This is the
   missing base rule; each call site's own inline style still controls
   direction/alignment/spacing on top of it. */
.row { display: flex; }

/* Dashboard's Recent Projects widget reuses .project-card/.project-grid but
   needs to stay compact next to the other panels — scoped here so the full
   Projects page's cards are untouched. */

.new-project-form {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px;
}
/* min-width keeps the field readable if the row wraps rather than letting
   flex:1 crush it toward zero first — seen on the Media page once its
   toolbar started sharing a row with the folder rail instead of spanning
   the full page width. */
.new-project-form input { flex: 1; min-width: 160px; max-width: 360px; }

/* The Devices heading's inline add-device form — smaller than an ordinary
   .new-project-form since it sits next to a page heading, not alone in its
   own card. .mode-group button's own padding (6px 14px) otherwise wins over
   button.small's (5px 12px) at equal specificity, since .mode-group button
   is declared later in this file — these overrides need their own, more
   specific selector rather than relying on .small doing it. */
.devices-add-form { gap: 6px; }
.devices-add-form input[type="text"] { flex: none; width: 170px; min-width: 0; padding: 5px 10px; font-size: 12.5px; }
.devices-add-form .mode-group button { padding: 4px 10px; font-size: 11.5px; }
.devices-add-form button.primary { padding: 5px 12px; font-size: 12px; }

.empty-state {
  color: var(--text-dim);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 40px;
  text-align: center;
  margin-top: 20px;
}

/* ---- Page tab switch (Exhibition Planner) ---- */
.page-tab-switch { margin: 4px 0 22px; }
.page-tab-switch button { padding: 8px 18px; }

/* ---- Projectors page: Devices / Groups sections ----
   These used to be two tabs, one hidden at a time — merged into a single
   scrollable page with both always visible, since a group is just another
   kind of projector and hiding it behind a click made it too easy to forget
   groups exist at all. Each section keeps its own heading in the tab's
   place instead. Groups sits right under the register-device form, above
   the individual device list, since a group is the thing most setups reach
   for first and shouldn't need scrolling past every single device to find. */
.page-section-heading { font-size: 20px; margin: 0 0 14px; }
#groups-tab, #devices-tab { margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--border); }

.cta-card {
  background: var(--panel); border: 1.5px dashed var(--border); border-radius: var(--radius);
  padding: 16px 20px;
}
button.cta-card-btn {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  font-family: "Archivo", sans-serif; font-size: 15px; color: var(--text);
  border: 1.5px dashed var(--border); cursor: pointer;
}
button.cta-card-btn:hover { border-color: var(--accent); border-style: solid; }
.cta-card-icon {
  display: flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  border-radius: 50%; background: var(--panel-2); font-size: 18px; flex-shrink: 0;
}

.device-list { display: flex; flex-direction: column; gap: 10px; }
.device-row {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px;
  background: var(--panel); border: 1.5px solid var(--border); border-radius: var(--radius);
  padding: 14px 18px; transition: border-color 0.15s;
}
.device-row:hover { border-color: var(--accent); }
.device-row-icon {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 44px; height: 44px; border-radius: var(--radius-sm); background: var(--panel-2); color: var(--text-dim);
}
.device-row-icon svg { width: 24px; height: 24px; stroke: currentColor; fill: none; }
.device-row-body { display: flex; flex-direction: column; gap: 3px; min-width: 160px; flex: 1; }
.device-row .group-testpattern-btn { flex-shrink: 0; }
.device-row-name {
  font-weight: 700; font-family: "Archivo", sans-serif; font-size: 15px; color: var(--text);
  display: flex; align-items: center; gap: 8px;
}
.device-row-status { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 12.5px; color: var(--text-dim); }
.device-row-meta { font-size: 12px; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Playback transport controls (device row, group row, global bar) ---- */
.transport-controls { display: flex; gap: 4px; flex-shrink: 0; }
.transport-controls button {
  width: 30px; height: 30px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; line-height: 1;
}

/* ---- Device power controls (sleep/wake/reboot) — the device row's own,
   distinct from playback transport above. Same horizontal row everywhere,
   including the projector detail page's header, where #device-header-power
   sits inline with Open Timeline/Present/Preview/Align (see
   templates/projector_detail.html) so the whole cluster wraps together as
   one row instead of an isolated stacked block of its own — a previous
   forced-vertical version here made that header disproportionately tall
   with an awkward gap next to it. */
.power-controls { display: flex; gap: 4px; flex-shrink: 0; }
/* The press-and-hold "reboot both" fill bar (see wirePowerButtons in
   projector-controls.js) — a plain absolutely-positioned bar behind the
   button's own label, animated via inline style (its duration has to
   match LONG_PRESS_MS exactly, which lives in JS) rather than a CSS
   transition class alone. z-index isn't needed: DOM order alone puts the
   label span after the fill span, so it paints on top without one. */
/* user-select/-webkit-touch-callout suppress the native "select text" /
   copy-paste callout a long-press on a touch screen normally triggers —
   confirmed live as the cause of the kill switch's hold gesture reading as
   "glitchy" on a phone (the OS's own long-press handling was fighting this
   button's own JS-driven one for the same gesture). touch-action:
   manipulation additionally tells the browser not to reserve this press
   for its own double-tap-zoom/pan gesture detection. */
.power-reboot-btn {
  position: relative; overflow: hidden;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  touch-action: manipulation;
}
.reboot-hold-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0%;
  background: var(--accent); opacity: 0.35; pointer-events: none;
}
.reboot-btn-label { position: relative; }
/* The single Power trigger's dropdown — Turn everything off/on plus the
   individual Sleep/Wake/Reboot actions (see powerControlsHtml/
   wirePowerButtons in projector-controls.js). Same visual language as
   .header-more-menu (absolute panel, same bg/border/shadow) but its own
   classes: .header-more-menu is shared by the group page's member-row
   overflow menus and canvas.html's Add menu, which don't share this one's
   open/close wiring (see closePowerDropdown's own comment on why that
   lives at module scope instead of per-row). */
.power-menu { position: relative; }
.power-menu-btn { display: inline-flex; align-items: center; gap: 4px; }
.power-caret { font-size: 10px; opacity: 0.7; }
.power-dropdown {
  position: fixed; z-index: 60; /* left/top/width set in positionPowerDropdown */
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 6px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); width: 260px; max-height: calc(100vh - 16px); overflow-y: auto;
}
.power-item {
  display: flex; align-items: flex-start; gap: 10px; width: 100%;
  padding: 8px; border-radius: 6px; text-align: left; background: none; border: none;
  color: var(--text); font-size: 13px; cursor: pointer;
}
.power-item:hover { background: var(--panel-3, rgba(255,255,255,0.06)); }
.power-item-ic { flex-shrink: 0; }
.power-item-text { display: flex; flex-direction: column; gap: 2px; }
.power-item-title { font-weight: 600; }
.power-item-sub { font-size: 11.5px; color: var(--text-dim); }
.power-dropdown-sep { height: 1px; background: var(--border); margin: 6px 2px; }
.power-item-sm { align-items: center; gap: 8px; font-weight: normal; position: relative; overflow: hidden; }
.play-status.state-playing { color: var(--accent); }
.play-status.state-paused { color: #e0b84a; }
.play-status.state-error { color: var(--danger); }
.play-status.state-aligning { color: #4ac0e0; }
.device-row-status .status-sep { opacity: 0.5; }
.device-stat.good { color: #4ec27a; }
.device-stat.warn { color: #e2a33c; }
.device-stat.bad  { color: var(--danger); }

/* ---- Editor ---- */
.editor-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 18px;
  padding: 18px;
  align-items: start;
}

@media (max-width: 900px) {
  .editor-layout { grid-template-columns: minmax(0, 1fr); padding: 12px; }
}

.canvas-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.canvas-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.canvas-toolbar .hint { color: var(--text-dim); font-size: 12px; }
.canvas-toolbar .spacer { flex: 1; }

/* Reusable segmented-control toggle (Warp/Mask, HDMI/Wireless, Internal/External, ...) */
.mode-group { display: flex; border: 1.5px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; padding: 2px; gap: 2px; }
.mode-group button { border: none; padding: 6px 14px; }
.mode-group button.active {
  background: linear-gradient(180deg, var(--accent-strong), var(--accent));
  color: #201503; font-weight: 700;
}

#canvas-stage {
  position: relative;
  /* Layers blend against each other and this stage's own background, never
     against the page behind it — without isolation a Screen or Add layer
     would light up the app's chrome. */
  isolation: isolate;
  background: #08070a repeating-conic-gradient(#100e10 0% 25%, #08070a 0% 50%) 0 0/20px 20px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  align-self: center;
  touch-action: none;
}
#canvas-stage img.reference { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.9; }
#canvas-stage .no-reference {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); text-align: center; padding: 20px; font-size: 13px;
}

.layer-media { will-change: transform; }

.handle {
  position: absolute;
  width: 16px; height: 16px;
  margin-left: -8px; margin-top: -8px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #100e10;
  cursor: grab;
  z-index: 50;
  box-shadow: 0 0 6px rgba(225, 92, 46, 0.5);
}
.handle:active { cursor: grabbing; }

/* ---- Timeline page (merged editor + player) ----
   Video-editor layout: media bin column on the left, program monitor above
   the clip timeline on the right. The whole thing is sized to the viewport
   so the timeline stays docked at the bottom (like a real NLE) instead of
   scrolling off with page content. */
.timeline-layout {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  position: relative;
  gap: 14px;
  padding: 14px;
  /* --topbar-h is measured and set by timeline.js — the topbar's real
     height varies (it wraps on narrow windows), and hardcoding it would
     push the docked timeline off the bottom of the screen whenever it did. */
  height: calc(100vh - var(--topbar-h, 60px));
  align-items: stretch;
}
/* Keep the present-target picker from stretching the topbar into extra
   rows, which would eat vertical space the timeline needs. */
#present-target-select { max-width: 200px; }
/* The media/setup drawer opens over the stage rather than taking a column,
   so the canvas keeps the whole width when it's closed. */
.timeline-sidebar {
  display: none; flex-direction: column; gap: 12px;
  overflow-y: auto;
  min-height: 0;
  position: absolute; left: 68px; top: 14px; bottom: 14px; width: 300px; z-index: 30;
  background: var(--bg); padding: 0 2px 0 0;
  box-shadow: 8px 0 28px rgba(0, 0, 0, 0.55);
}
.timeline-layout.drawer-open .timeline-sidebar { display: flex; }
.timeline-rail {
  display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px 0;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
}
.timeline-rail button {
  position: relative; width: 38px; padding: 8px 0; border: none; background: none; color: var(--text-dim);
  display: flex; flex-direction: column; align-items: center; gap: 2px; cursor: pointer; border-radius: 6px;
}
.timeline-rail button:hover { color: var(--text); background: rgba(255, 255, 255, 0.05); }
.timeline-layout.drawer-open .timeline-rail button { color: var(--accent-strong); background: rgba(255, 106, 61, 0.14); }
.rail-icon { font-size: 18px; line-height: 1; }
.rail-label { font-size: 9px; letter-spacing: 0.02em; }
.rail-dot { position: absolute; top: 5px; right: 5px; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.rail-dot[hidden] { display: none; }
.timeline-main {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  /* Without an explicit minmax(0,…) column, these grid items size to their
     CONTENT — so zooming the timeline in would stretch the whole panel (and
     the page) wider instead of scrolling inside .timeline-scroll. */
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  min-height: 0;
}
.program-monitor {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  display: flex; flex-direction: column; gap: 8px;
  min-height: 0;
  overflow: hidden;
}
.monitor-zoom-bar { display: flex; align-items: center; gap: 6px; align-self: flex-end; flex: 0 0 auto; }
.monitor-zoom-bar button {
  width: 26px; height: 26px; padding: 0; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); font-size: 15px; line-height: 1;
}
#mon-zoom-fit { width: auto; padding: 0 10px; font-size: 11px; }
#mon-zoom-label { font-size: 12px; color: var(--text-dim); min-width: 42px; text-align: center; }
/* Centres the canvas while it fits, and becomes pannable once zoomed past
   the monitor's bounds. */
.monitor-scroll {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  display: flex; align-items: center; justify-content: center;
}
.monitor-scroll > #canvas-stage { flex: 0 0 auto; margin: auto; }
.timeline-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  display: flex; flex-direction: column; gap: 10px;
  min-width: 0;
}
.transport-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.transport-bar .spacer { flex: 1; }
.transport-bar .clock { font-variant-numeric: tabular-nums; color: var(--text-dim); flex-shrink: 0; }
.timeline-zoom { display: flex; gap: 6px; }
.timeline-zoom button {
  width: 26px; height: 26px; padding: 0; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); font-size: 15px; line-height: 1;
}
#tl-zoom-fit { width: auto; padding: 0 10px; font-size: 11px; }

.timeline-scroll { overflow-x: auto; overflow-y: hidden; max-height: 260px; }
.timeline-inner { position: relative; min-width: 100%; }

.timeline-ruler {
  position: relative; height: 22px; border-bottom: 1px solid var(--border);
  cursor: text; touch-action: none; user-select: none;
}
.tl-tick { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--border); }
.tl-tick span { position: absolute; left: 4px; top: 3px; font-size: 10px; color: var(--text-dim); white-space: nowrap; }
.tl-tick-minor { top: auto; bottom: 0; height: 7px; border-left-color: var(--text-dim); opacity: 0.45; }

.timeline-tracks { position: relative; touch-action: none; }
.tl-clip {
  position: absolute;
  background: linear-gradient(180deg, rgba(225,92,46,0.85), rgba(225,92,46,0.6));
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  overflow: hidden;
  cursor: grab;
  display: flex; align-items: center;
  box-sizing: border-box;
}
.tl-clip.dragging { cursor: grabbing; z-index: 6; }
.tl-clip.selected { outline: 2px solid #fff; outline-offset: -2px; }
.tl-clip-label {
  padding: 0 8px; font-size: 11px; font-weight: 700; color: #201503;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none;
}
.tl-clip-trim {
  position: absolute; top: 0; bottom: 0; width: 10px;
  cursor: ew-resize; background: transparent;
}
/* One handle per end so a clip can be trimmed from the head as well as the
   tail — head trims move the start and shorten the clip, pinning the tail. */
.tl-trim-left { left: 0; border-top-left-radius: inherit; border-bottom-left-radius: inherit; }
.tl-trim-right { right: 0; border-top-right-radius: inherit; border-bottom-right-radius: inherit; }
.tl-clip:hover .tl-clip-trim { background: rgba(255,255,255,0.28); }
.tl-clip-trim:hover { background: rgba(255,255,255,0.5) !important; }
.timeline-playhead {
  position: absolute; top: 0; bottom: 0; width: 2px; background: #fff;
  pointer-events: none; z-index: 8;
}
.timeline-empty { margin: 0; }

@media (max-width: 900px) {
  .timeline-layout { grid-template-columns: minmax(0, 1fr); height: auto; padding: 12px; }
}

/* Clip controls live in the media drawer, under the media bin. Delete clip and
   Bring to front stay pinned to the bottom of the drawer while it scrolls. */
.timeline-sidebar #layer-props .props-footer {
  position: sticky; bottom: 0; background: var(--panel); padding: 8px 0 2px; margin-top: 8px;
  border-top: 1px solid var(--border);
}

/* Narrow screens keep the old stacked layout: no rail, and the drawer sits in
   the page flow. */
@media (max-width: 900px) {
  .timeline-rail { display: none; }
  .timeline-sidebar { display: flex; position: static; width: auto; box-shadow: none; padding: 0; }
  .timeline-sidebar #layer-props .props-footer { position: static; }
}


/* ---- Guided setup checklist (new projects, Timeline sidebar) ---- */
.setup-panel { border-color: var(--accent); }
.setup-panel h3 { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.setup-dismiss { padding: 0 8px; line-height: 1; font-size: 16px; }
.setup-steps { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.setup-step {
  display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 10px;
  padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-2);
}
/* The step the user should act on next is the only one that draws attention —
   done steps recede, later steps stay visibly "not yet" without being
   disabled, since nothing here hard-blocks working out of order. */
.setup-step.current { border-color: var(--accent); background: rgba(225, 92, 46, 0.07); }
.setup-step.todo { opacity: 0.78; }
/* A done step only needs its checkmark + title to still read as "finished,
   this is what happened" — repeating the full description below it (which
   only ever changes to report the done state anyway) was pure bulk once
   there's nothing left to act on, and it's most of what made an
   established project's checklist feel like it never goes away. */
.setup-step.done {
  opacity: 0.55; grid-template-columns: 24px minmax(0, 1fr); align-items: center;
  padding: 6px 10px;
}
.setup-step.done .setup-step-desc,
.setup-step.done .setup-step-actions { display: none; }
.setup-step-num {
  width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 800; border: 1.5px solid var(--border); color: var(--text-dim);
}
.setup-step.current .setup-step-num { background: var(--accent); border-color: var(--accent); color: #201503; }
.setup-step.done .setup-step-num { background: transparent; border-color: var(--accent); color: var(--accent); }
.setup-step-title { font-weight: 700; font-size: 13px; }
.setup-step-desc { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
.setup-step-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.setup-step-actions button, .setup-step-actions select { font-size: 11px; }
.setup-step-actions select { padding: 4px 6px; border-radius: var(--radius-sm); max-width: 100%; }
/* The group affordance is deliberately secondary to linking itself — it only
   becomes meaningful once more than one projector is on the project. */
.setup-secondary { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border); font-size: 11px; color: var(--text-dim); }

.sidebar { display: flex; flex-direction: column; gap: 14px; }
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.panel h3 {
  margin: 0 0 12px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-dim); font-family: "Archivo", sans-serif; font-weight: 800;
}

/* Collapses to a single quiet line once there's nothing cached (see the
   class toggle in media.js) — the full explanation of what "in use" means
   only earns a permanent paragraph once there's an actual list of files to
   read it against; until then it's a tooltip on the heading instead. */
.pi-panel.pi-panel-empty { padding-bottom: 12px; }
.pi-panel.pi-panel-empty .pi-panel-desc { display: none; }
.pi-panel.pi-panel-empty h3 { margin-bottom: 4px; cursor: help; }

.layer-list { display: flex; flex-direction: column; gap: 6px; max-height: 260px; overflow-y: auto; }
.layer-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: var(--radius-sm); border: 1px solid transparent;
  cursor: pointer;
}
.layer-row:hover { background: var(--panel-2); }
.layer-row.selected { background: var(--panel-2); border-color: var(--accent); }
.layer-row .icon { width: 22px; text-align: center; color: var(--text-dim); }
.layer-row .lname { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.layer-row .del { opacity: 0; }
.layer-row:hover .del { opacity: 1; }

.reorder-btns { display: flex; flex-direction: column; gap: 1px; flex-shrink: 0; }
.reorder-btns button {
  border: none; border-radius: var(--radius-sm); padding: 0; width: 16px; height: 12px;
  font-size: 8px; line-height: 1; display: flex; align-items: center; justify-content: center;
  color: var(--text-dim); background: transparent;
}
.reorder-btns button:hover:not(:disabled) { color: var(--accent); background: var(--panel); }
.reorder-btns button:disabled { opacity: 0.2; cursor: default; }

.upload-btn {
  display: block; text-align: center; border: 1.5px dashed var(--border); border-radius: var(--radius-sm);
  padding: 10px; color: var(--text-dim); cursor: pointer; margin-top: 10px; font-weight: 600;
}
.upload-btn:hover { border-color: var(--accent); color: var(--accent); }

.field-row { display: flex; gap: 8px; margin-bottom: 10px; }
.field-row > div { flex: 1; }
.checkbox-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.checkbox-row input { width: auto; }
.startup-pref-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); cursor: pointer; font-size: 13px; }
.startup-pref-row:last-child { border-bottom: none; }
.startup-pref-row input { width: auto; margin-top: 3px; flex-shrink: 0; }
.startup-pref-row span { color: var(--text-dim); }
.startup-pref-row strong { color: var(--text); }

.props-footer { display: flex; justify-content: space-between; margin-top: 10px; }

/* ---- Player ---- */
.player-shell { display: flex; flex-direction: column; height: 100vh; }
.player-stage-wrap {
  flex: 1; display: flex; align-items: center; justify-content: center; background: #000; overflow: hidden;
}
#player-stage { position: relative; background: #000; }
#player-stage img.reference { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; }
.player-controls {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px;
  background: var(--panel); border-top: 1px solid var(--border);
}
.player-controls .spacer { flex: 1; }
.player-controls .clock { font-variant-numeric: tabular-nums; color: var(--text-dim); flex-shrink: 0; }
.badge { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 4px 12px; font-size: 12px; color: var(--text-dim); }
.projector-badge { display: inline-flex; align-items: center; gap: 6px; width: fit-content; color: var(--text-dim); }
.projector-badge:hover { border-color: var(--accent); color: var(--text); text-decoration: none; }

@media (max-width: 640px) {
  .player-controls { flex-wrap: wrap; }
  .player-controls .badge { display: none; }
}

/* ---- Side nav ----
   A fixed-position column, independent of each page's own layout (.container,
   .editor-layout, .canvas-page-layout, .player-shell all have very different
   internal structures) — position:fixed sidesteps needing to touch any of
   them individually. body:has(.side-nav) below is what actually reserves
   the space for it, and works regardless of how deep .side-nav sits in a
   given template's markup (e.g. nested inside .player-shell). */
/* 224px, not 208px: the sidebar's own global-transport row (6 buttons at
   30px + 5 gaps at 4px = 200px) needs at least that much content width
   (width minus the side-nav's own 10px+10px padding) to stay on one row
   instead of wrapping the last button onto its own line. */
:root { --side-nav-w: 224px; }

body:has(.side-nav) { padding-left: var(--side-nav-w); }

.side-nav {
  position: fixed;
  left: 0; top: 0; bottom: 0;
  width: var(--side-nav-w);
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--panel);
  border-right: 1px solid var(--border);
  padding: 20px 10px calc(20px + env(safe-area-inset-bottom));
  overflow-y: auto;
}
.side-nav a {
  display: flex; flex-direction: row; align-items: center; gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
}
.side-nav a svg { width: 20px; height: 20px; stroke: currentColor; flex-shrink: 0; }
.side-nav a:hover:not(.active):not([aria-disabled="true"]) { background: var(--panel-2); color: var(--text); }
.side-nav a.active { color: var(--text); background: rgba(225, 92, 46, 0.16); }
.side-nav a.active svg { stroke: var(--accent); }
.side-nav a[aria-disabled="true"] { opacity: 0.35; pointer-events: none; }

/* ---- Side nav, phone-width ----
   Below this the fixed 208px column doesn't just look cramped, it pushes
   real content off the right edge of the screen entirely (no page here
   scrolls horizontally to compensate). Collapsed into a slide-in drawer
   instead: off-screen by default, toggled by a hamburger button
   side-nav-toggle.js injects into every page's .topbar (all of them share
   that one class name, so one script covers every template without
   touching each one's own markup beyond the single new <script> tag). */
.nav-toggle-btn {
  display: none;
  width: 34px; height: 34px; padding: 0; flex-shrink: 0;
  align-items: center; justify-content: center;
  background: transparent; border-color: var(--border);
}
.nav-toggle-btn svg { width: 20px; height: 20px; stroke: currentColor; }
.side-nav-backdrop {
  display: none;
  position: fixed; inset: 0;
  background: rgba(8, 7, 8, 0.6);
  z-index: 29;
}
@media (max-width: 860px) {
  body:has(.side-nav) { padding-left: 0; }
  .nav-toggle-btn { display: flex; }
  .side-nav {
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 8px 0 24px rgba(0, 0, 0, 0.4);
    z-index: 31;
  }
  .side-nav.open { transform: translateX(0); }
  .side-nav-backdrop.open { display: block; }

  /* global-transport.js reparents .global-transport here (a direct child
     of <body>) at this same breakpoint, so it stays reachable without
     opening the drawer — see that script's own comment for why the move
     is necessary rather than just adding position:fixed in place. */
  /* --bottom-bar-h is the button/icon row's own height — set on body (an
     ancestor of both rules below, unlike .global-transport itself) so
     body's own reserved padding can share the exact same value instead of
     a second hardcoded number that could quietly drift out of sync. */
  body:has(.global-transport.bottom-bar) { --bottom-bar-h: 56px; --bottom-bar-status-h: 24px; }
  /* Joined strip of full-width cells with the status line UNDER it. On an
     iPhone the bottom edge is the home-indicator swipe zone (exit app /
     switch app), so the non-tappable status line takes that spot and the
     buttons sit clear above it. */
  .global-transport.bottom-bar {
    position: fixed; left: 0; right: 0; bottom: 0;
    z-index: 32; /* above the drawer (31) and its backdrop (29) */
    margin: 0; gap: 0; padding: 10px max(12px, env(safe-area-inset-right))
      calc(6px + env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
    background: var(--panel);
    border-top: 1px solid var(--border);
    border-radius: 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr) 48px;
    /* Lifts the bar visually off the page above it, so it reads as its
       own persistent surface rather than blending into whatever content
       happens to scroll underneath. */
    box-shadow: 0 -6px 16px rgba(0, 0, 0, 0.35);
  }
  .global-transport.bottom-bar > button {
    width: auto; max-width: none; height: var(--bottom-bar-h); font-size: 22px;
    border-radius: 0; margin-left: -1px; grid-row: 1;
  }
  .global-transport.bottom-bar > button:first-child { margin-left: 0; border-radius: 10px 0 0 10px; }
  .global-transport.bottom-bar > #transport-more-btn { grid-column: 5; border-radius: 0 10px 10px 0; font-size: 15px; }
  .global-transport.bottom-bar > #force-idle-btn,
  .global-transport.bottom-bar > #restart-app-btn { display: none; }
  .global-transport.bottom-bar > .nav-status {
    grid-column: 1 / -1; grid-row: 2; justify-content: center;
    height: var(--bottom-bar-status-h); margin-top: 6px; pointer-events: none;
  }
  /* Reserves room at the bottom of every page so real content never sits
     underneath the fixed bar above — :has() is already relied on for the
     same purpose by body:has(.side-nav) just above. */
  body:has(.global-transport.bottom-bar) {
    padding-bottom: calc(var(--bottom-bar-h) + var(--bottom-bar-status-h) + 22px + env(safe-area-inset-bottom));
  }
}

/* ---- Global transport controls (sidebar) ----
   Play/Pause/Restart/Stop/Force Idle every projector at once, plus the
   app-restart kill switch — not scoped to whichever page happens to be
   open, so these live in the sidebar rather than a page's own topbar
   (several of which, like Timeline's, are already packed with page-
   specific controls and have no room to spare). See global-transport.js,
   which wires the same buttons identically on every page. Same compact
   icon-square sizing as .transport-controls (the per-row play/stop/
   restart buttons) rather than the old full-width text buttons — a row of
   squares reads as one control cluster instead
   of a wall of buttons, and stays legible via each one's title tooltip. */
.global-transport {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--border);
}
.global-transport button {
  width: 30px; height: 30px; padding: 0; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; line-height: 1;
}

/* ---- Activity log (sidebar) ----
   A scrollable "what just happened" strip, pinned to the bottom of the side
   nav (margin-top: auto pushes it down the flex column, past the nav links
   and the transport buttons, to wherever the viewport's bottom actually is)
   rather than just sitting in document flow after them. See activity-log.js,
   loaded on every page the same way global-transport.js is. */
.activity-log {
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.activity-log-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.activity-log-title-clickable {
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  user-select: none;
}
.activity-log-title-clickable:hover { color: var(--text); }
.activity-log-toggle {
  font-size: 9px;
  transition: transform 0.15s ease;
}
.activity-log.collapsed .activity-log-toggle { transform: rotate(-90deg); }
.activity-log-list {
  height: 190px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: height 0.18s ease, opacity 0.18s ease, padding 0.18s ease, border-width 0.18s ease;
}
.activity-log.collapsed .activity-log-list {
  height: 0;
  opacity: 0;
  padding: 0 6px;
  border-width: 0;
  overflow: hidden;
}
.activity-log-row {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}
.activity-log-row:last-child { border-bottom: none; padding-bottom: 0; }
.activity-log-msg { font-size: 11.5px; color: var(--text); line-height: 1.3; }
.activity-log-actor { color: var(--text-dim); }
.activity-log-when { font-size: 10px; color: var(--text-dim); }
.activity-log-empty { font-size: 11.5px; color: var(--text-dim); }
.activity-log-full-link {
  display: block;
  font-size: 10.5px;
  color: var(--text-dim);
  text-align: right;
  margin-top: 6px;
  text-decoration: none;
}
.activity-log-full-link:hover { color: var(--accent); }
.activity-log.collapsed .activity-log-full-link { display: none; }

/* ---- Status dot ---- */
.status-dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--text-dim); flex-shrink: 0;
}
.status-dot.online { background: #5fcf7a; box-shadow: 0 0 8px rgba(95, 207, 122, 0.6); }
.status-dot.offline { background: var(--danger); box-shadow: 0 0 8px rgba(242, 69, 107, 0.45); }
/* Some, but not all, of a group is ready. Amber rather than green or red
   because it is neither: the show can start, but not on every wall. */
.status-dot.partial { background: var(--periwinkle); box-shadow: 0 0 8px rgba(191, 189, 238, 0.5); }

/* ---- Device detail header ---- */
.device-header { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
/* Pushes the transport/output cluster to the far right of the header row,
   opposite the device name. */
.device-header-actions { margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.device-header-transport { display: flex; align-items: center; gap: 10px; }
.device-header-buttons { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.device-header-unlinked { margin: 0; font-size: 11px; }
.device-header-actions button[disabled] { opacity: 0.45; cursor: not-allowed; }
.device-header-actions a[aria-disabled="true"] { pointer-events: none; }
@media (max-width: 760px) {
  /* Stacks under the name rather than squeezing beside it, so the buttons
     stay full-size and tappable instead of shrinking into each other. */
  .device-header { flex-wrap: wrap; }
  .device-header-actions { margin-left: 0; width: 100%; align-items: flex-start; }
  .device-header-buttons { justify-content: flex-start; }
}
.device-icon {
  width: 52px; height: 52px; border-radius: var(--radius-sm);
  background: var(--panel-2); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; color: var(--accent);
}
/* These icons are stroke-drawn and carry no stroke attribute of their own, so
   without this they paint nothing and the box reads as a missing image. */
.device-icon svg { width: 28px; height: 28px; stroke: currentColor; fill: none; }
.status-line { display: flex; align-items: center; gap: 7px; color: var(--text-dim); font-size: 13px; margin-top: 4px; }

/* ---- Tab strip ---- */
.tab-strip {
  display: flex; gap: 22px; border-bottom: 1px solid var(--border);
  margin-bottom: 20px; overflow-x: auto;
}
.tab-strip button {
  background: none; border: none; border-radius: 0;
  padding: 10px 2px 12px; color: var(--text-dim); font-weight: 700;
  white-space: nowrap; position: relative;
}
.tab-strip button:hover { border: none; color: var(--text); }
.tab-strip button.active { color: var(--text); }
.tab-strip button.active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--accent); border-radius: var(--radius-sm);
}

.tab-panel { display: flex; flex-direction: column; gap: 14px; max-width: 640px; }
/* About is a dashboard rather than a form: it fills the page instead of
   sitting in the narrow column the other form-style tabs use. */
.tab-panel[data-panel="about"], .tab-panel[data-panel="network"], .tab-panel[data-panel="settings"] { max-width: 1100px; }
.tab-panel.gs-settings[data-panel="settings"] { max-width: 760px; }
.about-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; }
.about-columns > .panel { margin: 0; }
@media (max-width: 860px) { .about-columns { grid-template-columns: minmax(0, 1fr); } }
.about-history-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.about-history-head h3 { margin: 0; }
.health-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 700px) { .health-grid { grid-template-columns: minmax(0, 1fr); } }
.health-card { background: var(--panel-2, rgba(255,255,255,0.03)); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; color: #4ec27a; }
.health-card[data-tone="warn"] { color: #e2a33c; }
.health-card[data-tone="bad"] { color: var(--danger); }
.health-top { display: flex; justify-content: space-between; align-items: baseline; font-size: 13px; }
.health-top span { color: var(--text-dim); }
.health-spark { display: block; width: 100%; height: 44px; margin: 6px 0 4px; }
.health-sub { font-size: 11px; color: var(--text-dim); min-height: 14px; }
.header-more { position: relative; display: inline-block; }
.header-more-menu { position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; padding: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); }
.header-more-menu .power-controls { flex-direction: column; align-items: stretch; }
.header-more-menu button { text-align: left; white-space: nowrap; }

.kv-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 13px;
}
.kv-row:last-child { border-bottom: none; }
.kv-row span:first-child { color: var(--text-dim); }
.mono { font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: 0.02em; }
/* Video Sync / Audio Sync's value, between their −/+ nudge buttons — click
   swaps it for a real number input so a specific value (say +250ms) can be
   typed directly instead of stepped to one nudge at a time. */
.offset-editable { cursor: pointer; border-radius: var(--radius-sm); padding: 2px 6px; }
.offset-editable:hover { background: var(--panel-2); }
.offset-edit-input { width: 64px; padding: 2px 4px; text-align: center; }

.hint-text { color: var(--text-dim); font-size: 12.5px; line-height: 1.5; margin: 0 0 12px; }

select {
  background: var(--bg); border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 14px; width: 100%;
}
select:focus { outline: none; border-color: var(--accent); }

input[type="range"] {
  width: 100%; accent-color: var(--accent); background: transparent;
}

.calib-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px; border-radius: var(--radius-sm); border: 1px solid var(--border);
  margin-bottom: 8px; background: var(--panel-2);
}
.calib-thumb { width: 56px; height: 40px; object-fit: cover; border-radius: var(--radius-sm); background: var(--bg); }
.calib-info { flex: 1; }
.calib-date { font-weight: 700; font-size: 13px; }

/* ---- Confirm dialog ---- */
.confirm-overlay {
  position: fixed; inset: 0; background: rgba(8, 7, 8, 0.6);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000; padding: 20px;
  opacity: 0; pointer-events: none; transition: opacity 0.12s;
}
.confirm-overlay.open { opacity: 1; pointer-events: auto; }
.confirm-box {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 22px; max-width: 380px; width: 100%;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  transform: translateY(6px); transition: transform 0.12s;
}
.confirm-overlay.open .confirm-box { transform: translateY(0); }
.confirm-message { margin: 0 0 18px; font-size: 14.5px; line-height: 1.5; color: var(--text); white-space: pre-line; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* In-place media preview (see openAssetPreview in media.js) — same
   overlay chrome as the confirm dialog above, sized for actually watching
   or listening to a file rather than a short confirmation message. */
.media-preview-box {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0; width: 100%; max-width: 900px; max-height: 88vh;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  transform: translateY(6px); transition: transform 0.12s;
  display: flex; flex-direction: column; overflow: hidden;
}
.media-preview-overlay.open .media-preview-box { transform: translateY(0); }
.media-preview-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.media-preview-name { font-weight: 700; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.media-preview-close {
  background: none; border: none; color: var(--text-dim); font-size: 22px; line-height: 1;
  cursor: pointer; padding: 2px 6px;
}
.media-preview-close:hover { color: var(--text); }
.media-preview-body {
  flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center;
  background: #000; padding: 0;
}
.media-preview-body video, .media-preview-body img { max-width: 100%; max-height: 80vh; display: block; }
.media-preview-body audio { width: 100%; max-width: 520px; margin: 0 20px 20px; }
.media-preview-audio-art {
  position: absolute; margin-top: -60px; color: var(--text-dim);
}
.media-preview-audio-art svg { width: 64px; height: 64px; }
.media-preview-body:has(audio) { flex-direction: column; padding: 60px 0 20px; position: relative; }

.confirm-actions button.danger.primary {
  background: linear-gradient(180deg, #ea6a72, var(--danger));
  border-color: var(--danger); color: #2a0a0c; font-weight: 700;
}
.confirm-box .prompt-input { margin-bottom: 18px; }

.confirm-box:has(.codec-options) { max-width: 460px; }
.codec-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.codec-option {
  display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius);
  font-size: 13px; line-height: 1.45; color: var(--text-dim);
}
.codec-option:has(input:checked) { border-color: var(--accent); color: var(--text); }
.codec-option input { margin-top: 3px; flex-shrink: 0; }
.codec-option strong { color: var(--text); }

.thumb.folder-thumb, .thumb.video-thumb, .thumb.audio-thumb {
  display: flex; align-items: center; justify-content: center; color: var(--text-dim);
}
.thumb.folder-thumb svg, .thumb.video-thumb svg, .thumb.audio-thumb svg { width: 34px; height: 34px; }
/* A real captured frame sits behind the play glyph (see media.js/
   asset-picker.js) — switch it to white with a drop shadow so it stays
   legible over any thumbnail brightness, instead of the muted placeholder
   color meant for an empty background. */
.thumb.video-thumb.has-thumbnail { color: #fff; }
.thumb.video-thumb.has-thumbnail svg { filter: drop-shadow(0 1px 5px rgba(0, 0, 0, 0.75)); }
/* Hover-to-preview (see wireVideoHoverPreview in media-thumbnail.js) — the
   video sits on top of the static thumbnail, invisible and not decoding
   anything (preload="none") until the pointer actually lingers over it. */
.thumb-video-preview {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; pointer-events: none; transition: opacity 0.15s; background: #000;
}
.thumb-video-preview.show { opacity: 1; }
.asset-name-input {
  background: transparent; border: none; padding: 0; font-weight: 700;
  font-family: "Archivo", sans-serif; color: var(--text); border-radius: var(--radius-sm);
}
.asset-name-input:hover, .asset-name-input:focus { background: var(--bg); padding: 2px 6px; margin: -2px -6px; outline: none; }

/* ---- Asset picker ---- */
.picker-box { max-width: 640px; max-height: 80vh; display: flex; flex-direction: column; }
.picker-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.picker-header .picker-close { border: none; padding: 2px 8px; font-size: 18px; line-height: 1; }
.picker-folders { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.picker-folders .folder-tab { padding: 5px 12px; }
.picker-folders .folder-tab.active { background: var(--accent); border-color: var(--accent); color: #201503; font-weight: 700; }
.picker-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px;
  overflow-y: auto; padding-right: 4px; min-height: 60px;
}
.picker-item { cursor: pointer; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--border); transition: border-color 0.15s; }
.picker-item:hover { border-color: var(--accent); }
.picker-thumb { position: relative; aspect-ratio: 4/3; background: var(--panel-2) center/cover no-repeat; display: flex; align-items: center; justify-content: center; color: var(--text-dim); }
.picker-thumb svg { width: 26px; height: 26px; }
.picker-thumb.video-thumb.has-thumbnail { color: #fff; }
.picker-thumb.video-thumb.has-thumbnail svg { filter: drop-shadow(0 1px 5px rgba(0, 0, 0, 0.75)); }
.picker-item-name { font-size: 11px; padding: 6px 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---- Keystone editor ---- */
#keystone-stage {
  position: relative; width: 340px; height: 200px;
  background: #08070a repeating-conic-gradient(#100e10 0% 25%, #08070a 0% 50%) 0 0/20px 20px;
  border-radius: var(--radius-sm); overflow: visible; touch-action: none;
}
#keystone-preview {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%; transform-origin: 0 0;
  background-color: rgba(225, 92, 46, 0.18);
  background-image:
    linear-gradient(rgba(225, 92, 46, 0.5) 1px, transparent 1px),
    linear-gradient(90deg, rgba(225, 92, 46, 0.5) 1px, transparent 1px);
  background-size: 20% 20%;
  border: 2px solid var(--accent);
}
/* Real deformed preview for mesh grids larger than 2x2 (a single CSS
   matrix3d can't represent a subdivided mesh) — shown instead of
   #keystone-preview, same footprint. */
#keystone-gl-preview {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  border-radius: var(--radius-sm);
}

/* ---- Mesh grid editor overlay (mesh-editor.js) ---- */
.mesh-grid-svg {
  position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; z-index: 40;
}
.mesh-grid-line { fill: none; stroke: var(--accent); stroke-width: 1.5; opacity: 0.85; }
.mesh-points-layer { position: absolute; inset: 0; }
.mesh-point {
  position: absolute;
  width: 14px; height: 14px;
  margin-left: -7px; margin-top: -7px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #100e10;
  cursor: grab;
  z-index: 50;
  box-shadow: 0 0 6px rgba(225, 92, 46, 0.5);
  touch-action: none;
}
.mesh-point:active { cursor: grabbing; }
.mesh-point.corner { border-radius: 2px; transform: rotate(45deg); }
.mesh-round { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.mesh-round-slider { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--text-dim); margin: 0; }
.mesh-round-slider input { width: 100%; }
.mesh-point.selected { background: #fff; box-shadow: 0 0 0 3px rgba(225, 92, 46, 0.55); }

.mesh-resolution-row { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.mesh-resolution-row .stepper { display: flex; align-items: center; gap: 6px; }
.mesh-resolution-row .stepper button { width: 28px; height: 28px; padding: 0; }
.mesh-resolution-row .stepper span { min-width: 42px; text-align: center; font-variant-numeric: tabular-nums; }

/* ---- Screen picker (multi-HDMI present flow) ---- */
.screen-list { display: flex; flex-direction: column; gap: 8px; }
.screen-row {
  display: flex; justify-content: space-between; align-items: center;
  border-radius: var(--radius-sm); padding: 12px 14px; text-align: left; width: 100%;
}
.screen-row:hover { border-color: var(--accent); }
.screen-row-name { font-weight: 700; }
.screen-row-meta { color: var(--text-dim); font-size: 12px; }

/* ---- Camera capture ---- */
.camera-box { max-width: 480px; width: 100%; }
.camera-device-select { margin-bottom: 0; width: 100%; }
/* Labelled so it reads as a deliberate choice ("which camera?") rather than
   an unexplained dropdown — it lists whatever the OS exposes, so a USB webcam
   and an iPhone on Continuity Camera both appear here by name. */
.camera-source-row { margin-bottom: 12px; }
.camera-source-row label { display: block; font-size: 11px; color: var(--text-dim); margin-bottom: 4px; }
.camera-source-row[hidden] { display: none; }
.camera-source-controls { display: flex; gap: 6px; align-items: center; }
.camera-source-controls .camera-device-select { flex: 1; min-width: 0; }
.camera-source-refresh { flex: 0 0 auto; padding: 6px 10px; font-size: 14px; line-height: 1; }
.camera-source-status { margin: 4px 0 0; font-size: 11px; }
.camera-source-help { margin: 6px 0 0; font-size: 11px; line-height: 1.45; }
.camera-preview-wrap {
  position: relative; width: 100%; aspect-ratio: 4/3; background: #000;
  border-radius: var(--radius-sm); overflow: hidden; margin-bottom: 14px;
}
.camera-video, .camera-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.camera-error { color: var(--danger); }

/* ---- Structured light calibration wizard ---- */
.calib-wizard-box { max-width: 520px; }
.calib-intro { margin-top: -8px; }
.calib-open-output-btn { margin-bottom: 12px; display: block; width: 100%; }
/* The result canvas is sized by the wrapper, NOT by its own pixel
   dimensions. It used to carry no box rules at all, so it laid out at its
   intrinsic size — which was harmless while that was a 480px analysis frame
   and broke the moment it became a full-resolution camera photo: a 4032px
   canvas inside a 520px dialog renders one corner of itself, clipped.
   `contain` keeps the aspect honest; see displayedRect() in
   structured-light-calibrate.js, which maps pointer coordinates through the
   same letterboxing. */
.calib-result-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  touch-action: none;
  cursor: crosshair;
}
.calib-progress { color: var(--accent); font-weight: 700; }

/* ---- Media library: play + multi-select ---- */
#select-mode-btn.active { background: var(--accent); border-color: var(--accent); color: #201503; font-weight: 700; }
.asset-card.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.thumb-wrap { position: relative; }
.thumb-wrap .play-btn {
  position: absolute; bottom: 8px; right: 8px; border-radius: 50%;
  width: 32px; height: 32px; padding: 0; display: flex; align-items: center; justify-content: center;
  font-size: 12px; opacity: 0; transition: opacity 0.15s;
}
.thumb-wrap:hover .play-btn { opacity: 1; }
.select-badge {
  position: absolute; top: 8px; left: 8px; min-width: 22px; height: 22px; padding: 0 5px;
  border-radius: var(--radius-sm); background: var(--accent); color: #201503; font-weight: 700; font-size: 12px;
  display: flex; align-items: center; justify-content: center;
}

/* ---- Canvas page (stitched multi-projector reference image) ---- */
.canvas-page-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: 18px;
  padding: 18px;
  align-items: start;
}
@media (max-width: 900px) {
  .canvas-page-layout { grid-template-columns: minmax(0, 1fr); padding: 12px; }
}
.canvas-page-intro { margin-top: -4px; }
#canvas-workspace-outer {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.canvas-zoom-bar { display: flex; align-items: center; gap: 6px; align-self: flex-end; }
.canvas-zoom-bar button {
  width: 26px; height: 26px; padding: 0; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); font-size: 15px; line-height: 1;
}
#zoom-fit-btn { width: auto; padding: 0 10px; font-size: 11px; }
#zoom-level-label { font-size: 12px; color: var(--text-dim); min-width: 42px; text-align: center; }
#canvas-workspace-scroll {
  max-width: 100%;
  overflow: auto;
}
#canvas-workspace {
  position: relative;
  background: #08070a repeating-conic-gradient(#100e10 0% 25%, #08070a 0% 50%) 0 0/20px 20px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  touch-action: none;
  margin: 0 auto;
}
.canvas-tile {
  position: absolute;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  cursor: grab;
  background: var(--panel-2);
}
.canvas-tile:hover, .canvas-tile.dragging { border-color: var(--accent); }
.canvas-tile.dragging { cursor: grabbing; z-index: 10; }
.canvas-tile img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.canvas-tile .tile-label {
  position: absolute; top: 0; left: 0; right: 0; padding: 3px 8px;
  background: rgba(19, 17, 19, 0.75); color: var(--text); font-size: 11px; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; pointer-events: none;
}
.canvas-tile .resize-handle {
  position: absolute; right: -10px; bottom: -10px; width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  cursor: nwse-resize; z-index: 5;
}
.canvas-tile .resize-handle::after {
  content: ""; width: 14px; height: 14px;
  background: var(--accent); border: 2px solid #100e10; border-radius: var(--radius-sm);
}
.unplaced-row, .no-photo-row {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}

/* Canvas-tile placeholder patterns — what a tile shows in place of a
   captured photo (see CANVAS_PLACEHOLDERS in canvas-page.js). Plain CSS
   gradients, not images: no upload, no generation step, and they scale to
   any tile size for free. .tile-label (the projector's name) already
   paints over these with its own solid backing, so legibility on top is
   unaffected regardless of which pattern is active. */
.canvas-tile.ph-stripes {
  background: repeating-linear-gradient(-45deg, var(--panel-2) 0 14px, var(--border) 14px 16px);
}
.canvas-tile.ph-dots {
  background-color: var(--panel-2);
  background-image: radial-gradient(var(--text-dim) 1.5px, transparent 1.5px);
  background-size: 18px 18px;
}
.canvas-tile.ph-gradient {
  background: linear-gradient(135deg, var(--accent-2), var(--panel-2) 55%, var(--periwinkle));
}
.canvas-tile.ph-grid {
  background-color: var(--panel-2);
  background-image:
    linear-gradient(var(--border) 1px, transparent 1px),
    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 20px 20px;
}

/* The swatch picker in the No Photo panel — same four patterns, shrunk
   down to plain little squares to choose between. */
.ph-swatches { display: flex; gap: 4px; }
.ph-swatch {
  width: 20px; height: 20px; padding: 0; border-radius: var(--radius-sm);
  border: 1.5px solid var(--border); cursor: pointer;
}
.ph-swatch.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ph-swatch.ph-stripes { background: repeating-linear-gradient(-45deg, var(--panel-2) 0 4px, var(--border) 4px 5px); }
.ph-swatch.ph-dots { background-color: var(--panel-2); background-image: radial-gradient(var(--text-dim) 1px, transparent 1px); background-size: 6px 6px; }
.ph-swatch.ph-gradient { background: linear-gradient(135deg, var(--accent-2), var(--panel-2) 55%, var(--periwinkle)); }
.ph-swatch.ph-grid {
  background-color: var(--panel-2);
  background-image: linear-gradient(var(--border) 1px, transparent 1px), linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 7px 7px;
}
.unplaced-row img {
  width: 44px; height: 30px; object-fit: cover; border-radius: var(--radius-sm); background: var(--bg);
}
.unplaced-row .lname, .no-photo-row .lname { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-badge:empty { display: none; }

/* Batch-select checkbox on a project card, sitting beside its own Delete
   button in the card's bottom row. Padded well past the input's own box so
   it's a comfortable click target rather than a 13px square. */
.card-select {
  display: inline-flex; align-items: center; justify-content: center;
  margin-left: auto; padding: 4px 6px; cursor: pointer; border-radius: var(--radius-sm);
}

/* The Timeline's topbar wrapped its Present control onto a second row at
   ordinary widths, and since the workspace below is sized to the remaining
   viewport height, every wrapped row came straight out of the timeline. The
   project-name field is the only elastic item, so letting it shrink (rather
   than holding a fixed width) keeps everything on one line. */
.timeline-topbar { gap: 12px; padding: 10px 16px; flex-wrap: nowrap; }
/* Shrinks to fill, but not below a usable width — with min-width:0 it
   collapsed to a ~30px sliver on narrower windows, which technically avoided
   wrapping but left the field unusable. Past this floor the media query below
   lets the bar wrap instead, which is the better trade. */
.timeline-topbar #project-name { flex: 1 1 120px; min-width: 110px; }
.timeline-topbar .brand { flex: 0 0 auto; }
.timeline-topbar .badge,
.timeline-topbar #present-target-select,
.timeline-topbar #present-btn { flex: 0 0 auto; }
@media (max-width: 700px) {
  /* Below this the row genuinely can't fit; wrapping is the lesser evil. */
  .timeline-topbar { flex-wrap: wrap; }
}
.motion-unit { font-size: 11px; color: var(--text-dim); margin-left: -8px; }

/* Media-bin rows: a small thumbnail makes clips distinguishable at a glance.
   Generated filenames are near-identical hashes, so the name alone forces
   you to read a dozen characters to tell two clips apart. */
.layer-row .layer-thumb {
  width: 34px; height: 22px; flex: 0 0 auto; border-radius: var(--radius-sm);
  object-fit: cover; background: var(--bg); border: 1px solid var(--border);
}

/* ---- Motion group (Premiere-style Effect Controls) ----
   Dense rows of label / value / reset, so a whole transform fits in the
   space the old stacked input fields used for two properties. */
.motion-group { margin-top: 14px; border-top: 1px solid var(--border); }
.motion-header {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 2px 8px; cursor: pointer; user-select: none;
}
.motion-twisty { font-size: 10px; color: var(--text-dim); transition: transform .15s ease; }
.motion-group.collapsed .motion-twisty { transform: rotate(-90deg); }
.motion-group.collapsed .motion-body { display: none; }

/* The Clip panel's own collapse, one level above the Timing/Motion/Mask
   sections it contains — same twisty, same interaction, applied to the
   whole panel instead of one of its groups. */
.panel-toggle-header { margin-bottom: 12px; }
.panel-toggle-header h3 { display: inline; }
#layer-props.collapsed .panel-toggle-header { margin-bottom: 0; }
#layer-props.collapsed .panel-toggle-header .motion-twisty { transform: rotate(-90deg); }
#layer-props.collapsed .panel-body { display: none; }
.motion-fx { font-style: italic; font-weight: 700; font-size: 12px; color: var(--text-dim); }
.motion-title { font-weight: 700; font-size: 13px; flex: 1; }

.motion-row {
  display: grid; grid-template-columns: 84px minmax(0, 1fr) 22px;
  align-items: center; gap: 8px; padding: 4px 2px;
}
.motion-label { font-size: 12px; color: var(--text-dim); }
.motion-values { display: flex; align-items: center; gap: 14px; min-width: 0; }
.motion-opacity { gap: 8px; }
.motion-opacity .prop-slider { flex: 1; min-width: 0; margin: 0; }
.motion-checkbox-row { padding-top: 2px; }
.motion-uniform { display: flex; align-items: center; gap: 6px; font-size: 12px; margin: 0; cursor: pointer; }
.motion-uniform input { width: auto; margin: 0; accent-color: var(--accent); }

/* A greyed row still shows its value (as Premiere does) rather than hiding
   it, so switching Uniform Scale off doesn't make the number appear from
   nowhere. */
.motion-row.disabled .motion-label,
.motion-row.disabled .scrub-value { opacity: 0.45; }
.motion-row.disabled .scrub-value { pointer-events: none; }

.motion-reset {
  background: none; border: none; padding: 0; cursor: pointer; line-height: 1;
  color: var(--text-dim); font-size: 14px; opacity: 0.6;
}
.motion-reset:hover { opacity: 1; color: var(--accent); }

/* The scrub-draggable numbers themselves. Blue-on-dark like an NLE, with a
   horizontal-resize cursor to advertise that they drag. */
.scrub-value {
  color: var(--accent); font-weight: 700; font-size: 12px;
  cursor: ew-resize; user-select: none; touch-action: none;
  padding: 1px 3px; border-radius: var(--radius-sm); min-width: 34px; display: inline-block;
  border-bottom: 1px dotted transparent;
}
.scrub-value:hover { border-bottom-color: var(--accent); }
.scrub-value[data-editing="1"] {
  cursor: text; user-select: text; background: var(--panel-2);
  outline: 1px solid var(--accent); border-bottom-color: transparent;
}

/* Range inputs in the clip properties panel. Sized to match the numeric
   fields beside them so the row doesn't look ragged. */
.prop-slider {
  width: 100%; margin: 6px 0 0; padding: 0; height: 20px;
  accent-color: var(--accent); background: transparent; cursor: pointer;
}
#layer-opacity-readout { color: var(--text); font-weight: 700; }

.props-subhead {
  margin: 14px 0 8px; padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-dim);
}
.card-select:hover { background: var(--panel-2); }

/* Media toolbar sort control */
.sort-control { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-dim); margin: 0; }
.sort-control select { width: auto; padding: 7px 10px; font-size: 12.5px; }

/* Brief highlight when a required field was left empty — the create-folder
   button used to just silently do nothing, which read as "broken". */
@keyframes inputNudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-3px); }
  75% { transform: translateX(3px); }
}
.input-nudge { animation: inputNudge .18s ease 2; border-color: var(--danger) !important; }
.card-select input { width: 16px; height: 16px; cursor: pointer; accent-color: var(--accent); margin: 0; }
.project-card.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

.precise-row { margin-bottom: 14px; }
.precise-row:last-child { margin-bottom: 0; }
.precise-row-name { font-weight: 700; font-size: 13px; margin-bottom: 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.precise-row-fields { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.precise-row-fields label { font-size: 10px; color: var(--text-dim); display: block; }
.precise-row-fields input { width: 100%; padding: 4px 6px; font-size: 12px; border-radius: var(--radius-sm); }
.precise-row-warning {
  margin: 6px 0 0; padding: 6px 8px; border-radius: var(--radius-sm);
  background: rgba(120, 24, 24, 0.35); border: 1px solid var(--danger);
  color: var(--text); font-size: 11px; line-height: 1.4;
}
.precise-row-scale-fields { margin-top: 6px; }
.precise-row-scale-fields label { font-size: 10px; color: var(--text-dim); display: block; }
.precise-row-scale-fields input { width: 100%; padding: 4px 6px; font-size: 12px; border-radius: var(--radius-sm); margin-top: 2px; }


/* ---- Mask pen editor (overlays the program monitor) ---- */
.mask-overlay {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 20; cursor: crosshair; touch-action: none;
}
/* Dims outside the mask so the cut reads against the media rather than
   being a bare outline. evenodd + a huge outer rect inverts the fill. */
.mask-shade { fill: rgba(0,0,0,0.55); fill-rule: evenodd; pointer-events: none; }
.mask-outline { fill: none; stroke: var(--accent); stroke-width: 1.5; pointer-events: none; }
.mask-handle-arm { stroke: rgba(225,92,46,0.65); stroke-width: 1; pointer-events: none; }
.mask-handle { fill: #fff; stroke: var(--accent); stroke-width: 1.5; cursor: grab; }
.mask-anchor { fill: var(--accent); stroke: #100e10; stroke-width: 1.5; cursor: grab; }
.mask-anchor.smooth { fill: #fff; stroke: var(--accent); }
.mask-anchor.first { fill: #fff; stroke: var(--accent); stroke-width: 2.5; }
.mask-help { line-height: 1.6; margin: 6px 0 0; }

/* Media on the monitor is directly draggable (see stageDrag in timeline.js).
   pointer-events had been off so clicks fell through to the stage. */
#canvas-stage .layer-media { pointer-events: auto; cursor: grab; }
#canvas-stage img.reference { pointer-events: none; } /* the guide layer stays click-through */

/* ---------- Mask stack ---------- */
/* Several shapes per layer, each adding to or cutting out of the visible
   area — so the panel is a list, not a single set of controls. */
.mask-list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
.mask-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.02);
}
.mask-row.editing { border-color: var(--accent); background: rgba(225, 92, 46, 0.08); }
.mask-row .mask-name {
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: text;
}
.mask-row .mask-mode { font-size: 11px; padding: 1px 3px; }
.mask-row .mask-del { padding: 1px 6px; line-height: 1.2; }
.mask-tools { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Outlines of the OTHER shapes in the stack while one is being edited. */
.mask-context {
  fill: none;
  stroke: rgba(255, 255, 255, 0.4);
  stroke-width: 1;
  stroke-dasharray: 5 4;
  pointer-events: none;
}
/* The +/- badge that says what the next click will do, as the pen tool in
   Illustrator does with its cursor. */
.mask-cursor-badge {
  fill: #fff;
  font: 700 14px var(--font, system-ui);
  paint-order: stroke;
  stroke: #100e10;
  stroke-width: 3;
  pointer-events: none;
}

/* Project switcher in the Timeline top bar — sized so a long project name
   doesn't push the transport controls off the row. */
.timeline-topbar #project-switch {
  max-width: 190px;
  padding: 7px 8px;
  font-size: 13px;
}

/* ---------- Media library: rail + drag and drop ---------- */
.media-layout { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 22px; align-items: start; }
@media (max-width: 860px) { .media-layout { grid-template-columns: 1fr; } }

/* margin-top matches .new-project-form's own 16px so the rail's first entry
   lines up with the toolbar beside it, not the grid further down. */
.folder-rail { position: sticky; top: 16px; display: flex; flex-direction: column; gap: 2px; margin-top: 16px; }
.rail-head {
  color: var(--text-dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}
.rail-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  color: var(--text);
  text-decoration: none;
  font-size: 13px;
}
.rail-item:hover { background: rgba(255, 255, 255, 0.05); }
.rail-item.active { background: rgba(225, 92, 46, 0.14); border-color: rgba(225, 92, 46, 0.5); }
.rail-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-count { color: var(--text-dim); font-size: 11px; }

/* Shared highlight for anything accepting a drop — folder cards, rail
   entries, the Timeline's media bin. */
.drop-target,
.rail-item.drop-target,
.project-card.drop-target {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
  background: rgba(225, 92, 46, 0.12);
}
.asset-card.dragging { opacity: 0.45; }
.asset-card[draggable="true"] .thumb-wrap { cursor: grab; }

/* Import hint, shown only while files are actually being dragged over. */
.media-main { position: relative; min-height: 220px; }
.drop-hint {
  display: none;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--accent);
  border-radius: var(--radius-sm);
  background: rgba(225, 92, 46, 0.1);
  font-weight: 700;
  pointer-events: none;
  z-index: 5;
}
.media-main.file-drop-active .drop-hint { display: flex; }

/* Show-length control, sitting with the zoom buttons because both govern
   how much time the ruler covers. */
.show-duration { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.show-duration .sd-label { color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; font-size: 11px; }
/* Folded into .timeline-zoom (no separate labeled block, own tooltip
   instead — same as the zoom buttons right next to it) purely to save
   width: a whole extra bordered cluster in the transport bar was enough
   to wrap it onto a second row at ordinary window widths. */
#show-fps-select { width: auto; height: 26px; padding: 0 6px; font-size: 11px; margin-right: 4px; }

/* ---------- Track heads (V/A rows) ---------- */
/* Pinned to the left of the scrolling timeline, so the controls for a track
   stay put while its clips scroll past. */
.timeline-body { display: grid; grid-template-columns: 132px minmax(0, 1fr); align-items: start; }
.track-heads {
  border-right: 1px solid var(--border);
  user-select: none;
  overflow: hidden;
}
/* Matches .timeline-ruler's height so row 1 of the heads lines up with row 1
   of the tracks. */
.track-head-spacer { height: 22px; border-bottom: 1px solid var(--border); }
.track-head {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0 6px;
  box-sizing: border-box;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  font-size: 11px;
}
.track-head.selected { background: rgba(255, 255, 255, 0.05); }
.track-head .th-name {
  min-width: 22px;
  font-weight: 800;
  color: var(--text-dim);
  letter-spacing: 0.03em;
}
.th-btn {
  width: 22px;
  height: 22px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  font-size: 11px;
  line-height: 1;
  opacity: 0.45;
}
.th-btn.on { opacity: 1; }
.th-mute.on { background: rgba(225, 92, 46, 0.3); border-color: var(--accent); }
.th-vol { margin-left: auto; color: var(--text-dim); font-size: 11px; }

/* Audio rows read as sound, not picture — a different colour is the fastest
   way to tell which half of a linked clip you're grabbing. */
.tl-clip.tl-a {
  background: linear-gradient(180deg, rgba(86, 160, 214, 0.85), rgba(86, 160, 214, 0.55));
  border-color: #56a0d6;
}
.tl-clip.tl-a .tl-clip-label { color: #04121d; }
.tl-clip.off { opacity: 0.4; }
.tl-clip.locked { cursor: not-allowed; background-image: repeating-linear-gradient(45deg, rgba(0,0,0,0.18) 0 6px, transparent 6px 12px); }
.tl-clip.locked .tl-clip-trim { display: none; }
@keyframes tl-shake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }
.tl-clip.shake { animation: tl-shake 0.3s; }

/* Blend-mode picker in the Motion group. */
.blend-select { width: 100%; font-size: 12px; padding: 4px 6px; }
.blend-note { margin: -2px 0 4px; }

/* ---------- Spatial module ---------- */
.badge.beta { background: rgba(140, 110, 220, 0.22); border-color: rgba(140, 110, 220, 0.5); }
.badge.ok { background: rgba(80, 190, 120, 0.2); border-color: rgba(80, 190, 120, 0.55); }
.badge.danger { background: rgba(220, 80, 70, 0.2); border-color: rgba(220, 80, 70, 0.55); }
.badge.warn { background: rgba(226, 163, 60, 0.18); border-color: rgba(226, 163, 60, 0.55); color: #e2a33c; }

/* ---------- Changelog ---------- */
/* Shared by the full /changelog page and the automatic "what's new"
   popup (changelog-notice.js) — one card look for both, built from
   changelog-format.js's entryCardHtml(). */
.cl-head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 18px; }
.cl-head h2 { margin: 0 0 4px; }
.cl-filters { display: flex; gap: 2px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 2px; }
.cl-filters button { border: none; background: none; color: var(--text-dim); padding: 6px 14px; font-size: 12.5px; border-radius: 1px; cursor: pointer; }
.cl-filters button.active { background: var(--panel-2); color: var(--text); font-weight: 600; }
.cl-filters button:hover:not(.active) { color: var(--text); }
.cl-list { display: flex; flex-direction: column; gap: 10px; }
.cl-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; display: flex; gap: 16px; align-items: flex-start; justify-content: space-between; }
.cl-card-main { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; min-width: 0; }
.cl-card .badge { flex: none; text-transform: uppercase; letter-spacing: 0.04em; font-size: 10.5px; font-weight: 700; padding: 3px 9px; }
.cl-title { font-weight: 700; font-size: 14.5px; }
.cl-author { flex: none; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 10px; border: 1px solid var(--border); color: var(--text-dim); }
.cl-author-mohammed { color: #8ab4ff; border-color: rgba(138, 180, 255, 0.4); }
.cl-author-soz { color: #f0b36a; border-color: rgba(240, 179, 106, 0.4); }
.cl-desc { color: var(--text-dim); font-size: 13px; margin-top: 4px; flex-basis: 100%; }
.cl-date { flex: none; color: var(--text-dim); font-size: 12px; white-space: nowrap; font-family: 'JetBrains Mono', monospace; }
.cl-empty { color: var(--text-dim); text-align: center; padding: 40px 0; }
/* Inside the popup's narrower .confirm-box, not the full page — tighter
   padding and no per-card border so a short list doesn't look like a
   stack of separate boxes inside an already-boxed dialog. */
.cl-popup-list { display: flex; flex-direction: column; gap: 8px; max-height: 320px; overflow-y: auto; margin: 0 0 18px; }
.cl-popup-list .cl-card { border: none; padding: 10px 0; border-bottom: 1px solid var(--border); border-radius: 0; }
.cl-popup-list .cl-card:last-child { border-bottom: none; }

.spatial-toggle-panel { margin-top: 18px; }
.spatial-switch { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.spatial-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.spatial-switch-track {
  position: relative;
  width: 44px;
  height: 24px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid var(--border);
  flex: 0 0 auto;
  transition: background 0.15s ease;
}
.spatial-switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.15s ease;
}
.spatial-switch input:checked + .spatial-switch-track { background: var(--accent); }
.spatial-switch input:checked + .spatial-switch-track .spatial-switch-knob { transform: translateX(20px); }

/* Everything below the switch dims and stops responding while the workflow
   is off — the module is visibly present but inert. */
.spatial-disabled { opacity: 0.4; pointer-events: none; filter: saturate(0.4); }

.static-steps { margin: 0; padding-left: 18px; line-height: 1.7; }
.static-steps li { margin-bottom: 4px; }

.spatial-layout {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  gap: 16px;
  padding: 16px;
  align-items: start;
}
@media (max-width: 1000px) { .spatial-layout { grid-template-columns: minmax(0, 1fr); } }
.spatial-sidebar { display: flex; flex-direction: column; gap: 12px; }
.spatial-main { display: flex; flex-direction: column; gap: 12px; min-width: 0; }

.spatial-viewport-wrap {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #0a090c;
  /* min-height as well as the viewport-relative height: in an embedded or
     zero-height context vh collapses to nothing, and a 3D viewport with no
     area is indistinguishable from one that failed to start. */
  height: min(60vh, 560px);
  min-height: 360px;
}
#spatial-viewport { width: 100%; height: 100%; display: block; touch-action: none; cursor: grab; }
#spatial-viewport:active { cursor: grabbing; }
.sp-viewport-bar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px;
  background: rgba(10, 9, 12, 0.85);
  border-top: 1px solid var(--border);
  font-size: 11px;
}
.sp-toggle { display: flex; align-items: center; gap: 4px; color: var(--text-dim); }
.sp-viewport-empty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  text-align: center; color: var(--text-dim); pointer-events: none; padding: 20px;
}
.sp-viewport-empty strong { color: var(--text); font-size: 15px; }

.spatial-results { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; }
.sp-field { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 8px; font-size: 12px; }
.sp-field select { flex: 1; }
.sp-surface-list { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.sp-surface {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 5px 8px; border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 12px;
}
.sp-surface-name { font-weight: 700; }
.sp-progress { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.sp-progress-bar { flex: 1; height: 6px; border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.1); overflow: hidden; }
.sp-progress-bar span { display: block; height: 100%; background: var(--accent); width: 0; transition: width 0.2s ease; }
.sp-plan {
  margin: 8px 0 0; padding: 8px; max-height: 220px; overflow: auto;
  background: rgba(0, 0, 0, 0.35); border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: 11px; white-space: pre; color: var(--text-dim);
}

/* ---------- Media Library picker: import by drop ---------- */
.picker-box { position: relative; }
.picker-upload-btn {
  margin-left: auto;
  margin-right: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 12px;
}
.picker-upload-btn:hover { background: rgba(255, 255, 255, 0.06); }
.picker-status { margin: 6px 0 0; }
/* Shown only while files are actually being dragged over the dialog. */
.picker-drop-hint {
  display: none;
  position: absolute;
  inset: 10px;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--accent);
  border-radius: var(--radius-sm);
  background: rgba(225, 92, 46, 0.12);
  font-weight: 700;
  pointer-events: none;
  z-index: 3;
}
.picker-box.file-drop-active .picker-drop-hint { display: flex; }

/* ---------- Canvas scan history: which one is the reference ---------- */
.calib-row.is-reference { border-color: rgba(80, 190, 120, 0.55); background: rgba(80, 190, 120, 0.07); }
/* The active-reference card sits alone above the (optionally collapsed)
   history list — same is-reference tint, just a bigger thumbnail and
   padding so it visibly leads the panel instead of being one row among
   several with a badge as the only difference. */
.calib-reference-card { padding: 14px; margin-bottom: 14px; }
.calib-reference-card .calib-thumb { width: 88px; height: 60px; }
.calib-reference-card .calib-date { font-size: 14px; }
.calib-history { margin-top: 4px; }
.calib-history-label {
  font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-dim); margin: 0 0 8px; cursor: pointer;
}
.calib-history[open] .calib-history-label { margin-bottom: 10px; }
.calib-history .calib-row:last-child { margin-bottom: 0; }

/* ---- Calibration sub-tabs (Canvas Scans / Reference Snapshots) ----
   A lighter, narrower variant of .tab-strip for the nested split inside
   the Calibration tab — kept visually distinct from the page's own
   About/Calibration/Network/Settings tabs above it so the two levels
   don't read as the same control. */
.subtab-strip {
  display: flex; gap: 18px; border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}
.subtab-strip button {
  background: none; border: none; border-radius: 0;
  padding: 8px 2px 10px; color: var(--text-dim); font-size: 13px; font-weight: 600;
  white-space: nowrap; position: relative;
}
.subtab-strip button:hover { border: none; color: var(--text); }
.subtab-strip button.active { color: var(--text); }
.subtab-strip button.active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--accent); border-radius: var(--radius-sm);
}
.subtab-count { color: var(--text-dim); font-weight: 400; font-size: 12px; }
.subtab-panel[hidden] { display: none; }
.snap-ref-badge { margin-top: 3px; display: inline-block; font-size: 10px; }
.snap-mode-note { margin: 0 0 8px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Opt-in for applying a scan's measured warp to the projector. */
.calib-apply-warp { display: flex; align-items: flex-start; gap: 8px; margin: 8px 0 0; line-height: 1.5; }
.calib-apply-warp input { margin-top: 2px; flex: 0 0 auto; }

/* The mask point currently under the keyboard's control. */
.mask-anchor.selected { fill: #fff; stroke: var(--accent); stroke-width: 3; }

/* ---------- Video / audio split ---------- */
/* Video tracks occupy the upper half of the timeline and audio the lower,
   with the boundary drawn rather than implied — the same hard break Premiere
   puts between its V and A stacks. */
.tl-audio-band {
  position: absolute;
  left: 0;
  right: 0;
  background: rgba(86, 160, 214, 0.06);
  pointer-events: none;
  z-index: 0;
}
.tl-av-divider {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 2px solid var(--border);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  pointer-events: none;
  z-index: 3;
}
/* The heads column mirrors it, so the line reads across the whole timeline. */
.track-head-split {
  height: 0;
  border-top: 2px solid var(--border);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
}
.track-head-a { background: rgba(86, 160, 214, 0.06); }
.track-head-a .th-name { color: #7fb6e0; }

/* The mask shape buttons sit on one row and share the width evenly. */
.mask-shape-buttons { display: flex; gap: 6px; }
.mask-shape-buttons .small { flex: 1; padding: 4px 6px; font-size: 11px; }

/* ---------- Magic wand ---------- */
.mask-wand-row { display: flex; align-items: center; gap: 8px; }
.mask-wand-row .small { flex: 1; }
.wand-tol { color: var(--text-dim); font-size: 11px; white-space: nowrap; }
.wand-hint { margin: -2px 0 4px; }
/* An armed wand takes over the monitor, so the cursor has to say so. */
#canvas-stage.wand-armed { cursor: crosshair; }
#canvas-stage.wand-armed .layer-media { cursor: crosshair; }

/* A mask row is a click target, and the one being edited is unmistakable. */
.mask-row { cursor: pointer; transition: border-color 0.12s ease, background 0.12s ease; }
.mask-row:hover { background: rgba(255, 255, 255, 0.05); }
.mask-row.editing { box-shadow: inset 3px 0 0 var(--accent); }
.mask-row .mask-name { cursor: pointer; }
.mask-row.editing .mask-name { font-weight: 700; }
/* The controls inside a row keep their own meaning. */
.mask-row .mask-en,
.mask-row .mask-mode,
.mask-row .mask-del { cursor: default; }

/* ---------- Wireless players ---------- */
.players-heading { margin: 32px 0 10px; display: flex; align-items: baseline; gap: 10px; }
.players-heading .hint-text { margin: 0; font-weight: 400; }
.player-card { display: flex; flex-direction: column; gap: 6px; }
.player-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.player-head .hint-text { margin: 0; }
/* Presence is the first thing you look for on a show day. */
.player-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #6b6b72; flex: 0 0 auto;
}
.player-card.online .player-dot { background: #4ec27a; box-shadow: 0 0 8px rgba(78, 194, 122, 0.7); }
.player-meta { margin: 0; }
.player-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 2px; }
.player-actions label { display: flex; align-items: center; gap: 6px; margin: 0; }
.player-install-cmd {
  display: inline-block; margin-top: 8px; padding: 8px 10px;
  background: rgba(0, 0, 0, 0.4); border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px;
  user-select: all; word-break: break-all; text-align: left;
}

/* Camera source switch in the scan wizard. */
.calib-camera-where { display: flex; gap: 6px; margin-bottom: 8px; }
.calib-camera-where .small { flex: 1; }
.calib-camera-where .small.active { background: var(--accent); border-color: var(--accent); color: #201503; font-weight: 700; }
.calib-camera-where .small:disabled { opacity: 0.45; cursor: not-allowed; }

/* Live MJPEG preview from the player's camera, in the same box as the
   local <video> so switching sources doesn't move the layout. */
.camera-live {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; background: #000;
}

/* Elapsed clock during a scan — the only feedback that separates "still
   working" from "hung" while a projector flashes patterns at a wall. */
.calib-elapsed { margin: 2px 0 0; font-variant-numeric: tabular-nums; }
.calib-elapsed-time { color: var(--accent); font-weight: 700; }

/* ---------- Network tab ---------- */
.net-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 2px 18px; }
.net-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 12.5px; }
.net-label { color: var(--text-dim); }
.net-value { font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
.net-value.good { color: #4ec27a; }
.net-value.warn { color: #e2a33c; }
.net-value.bad  { color: var(--danger); }

/* Device onboarding progress. */
.ap-log {
  margin: 8px 0 0; padding: 8px 10px; max-height: 220px; overflow: auto;
  background: rgba(0,0,0,0.45); border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px;
  white-space: pre-wrap; color: var(--text-dim); line-height: 1.5;
}

/* Discovered devices, offered as a list to click instead of an IP to look up. */
.device-hit {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin-bottom: 4px; padding: 7px 10px; text-align: left;
  background: rgba(255,255,255,0.03); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text); cursor: pointer; font-size: 12px;
}
.device-hit:hover:not(:disabled) { border-color: var(--accent); }
.device-hit:disabled { opacity: 0.5; cursor: default; }
.device-hit .mono { font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; }

/* A registered device's row: same shell as .device-hit, but it is a <div>
   (not a button) because it holds its own interactive children — a
   projector picker and an Assign button — which cannot nest inside one. */
.device-hit-assign {
  display: flex; align-items: center; gap: 8px; width: 100%; flex-wrap: wrap;
  margin-bottom: 4px; padding: 7px 10px; text-align: left;
  background: rgba(255,255,255,0.03); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text); font-size: 12px;
}
.device-hit-assign .device-assign-select { flex: 1; min-width: 160px; font-size: 12px; padding: 5px 8px; }

/* Wi-Fi picker, inside the Network panel. */
.rule { border: none; border-top: 1px solid var(--border); margin: 16px 0 12px; }
.wifi-list { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }
.wifi-row {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.02); font-size: 12px;
}
.wifi-row.active { border-color: var(--accent); }
.wifi-bars { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; letter-spacing: 1px; opacity: 0.8; }
.wifi-ssid { flex: 1; font-weight: 500; }
.wifi-meta { color: var(--text-dim); font-size: 11px; }
.toggle-inline { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-dim); }

/* Why a projector is offline, next to the status itself — the question that
   follows "Offline" is always "why", and the answer is already known. */
.status-why { color: var(--text-dim); font-size: 11px; }

/* Full-size look at a canvas scan, in place. */
.img-preview {
  position: fixed; inset: 0; z-index: 4000; display: flex; flex-direction: column;
  background: rgba(0,0,0,0.9); backdrop-filter: blur(3px);
}
.img-preview-bar {
  display: flex; align-items: center; gap: 14px; padding: 10px 16px;
  border-bottom: 1px solid var(--border); font-size: 12px; color: var(--text-dim);
  background: rgba(0,0,0,0.5);
}
.img-preview-caption { color: var(--text); font-weight: 500; }
.img-preview-dims { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; }
.img-preview-open { margin-left: auto; color: var(--accent); text-decoration: none; font-size: 12px; }
.img-preview-open + .img-preview-open { margin-left: 0; }
.img-preview-close {
  background: none; border: 1px solid var(--border); color: var(--text);
  border-radius: var(--radius-sm); width: 28px; height: 28px; cursor: pointer;
}
.img-preview-stage {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 20px; overflow: auto; min-height: 0;
}
.img-preview-stage img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  /* A checker ground: canvas scans have dark and transparent regions that
     vanish against a black backdrop. */
  background-image: linear-gradient(45deg, #1a1a1a 25%, transparent 25%),
                    linear-gradient(-45deg, #1a1a1a 25%, transparent 25%),
                    linear-gradient(45deg, transparent 75%, #1a1a1a 75%),
                    linear-gradient(-45deg, transparent 75%, #1a1a1a 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}
.calib-thumb.snap-preview { cursor: zoom-in; }

/* Wi-Fi password entry. */
.wifi-ask {
  position: fixed; inset: 0; z-index: 4100; display: flex;
  align-items: center; justify-content: center; background: rgba(0,0,0,0.75);
}
.wifi-ask-box {
  width: min(360px, 92vw); padding: 18px 20px; border-radius: var(--radius-sm);
  background: var(--panel, #141414); border: 1px solid var(--border);
}
.wifi-ask-box h4 { margin: 0 0 4px; font-size: 14px; }
.wifi-ask-input { width: 100%; margin-top: 10px; }
.wifi-ask-show { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 11px; color: var(--text-dim); }
.wifi-ask-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }

/* The device behind a wireless projector, inline in the status line. Styled
   as text rather than a form control: it sits among status words, and a full
   select chrome there would read as a settings widget rather than a fact
   about this projector. */
.header-player { display: inline-flex; align-items: center; gap: 6px; margin-left: 8px; }
.header-player-name {
  font-size: 12px; color: var(--text); max-width: 180px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.header-player-name.unassigned { color: var(--danger); }

/* ---------------------------------------------------------------------------
   Orchestrator
   The automation layer's control surface. Reads at two distances on purpose:
   an operator crossing the room needs the master state and the day bar, and an
   operator at the keyboard needs the schedule underneath it.
   --------------------------------------------------------------------------- */

/* The shared overlay is shown by class; without this the hidden attribute
   loses to its display:flex and the dialog is never really closed. */
.confirm-overlay[hidden] { display: none; }

.orch-master { display: flex; align-items: center; gap: 12px; }
.orch-master-state {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
}
.orch-master-state.is-on { color: var(--accent-strong); }
.orch-master-state.is-warn { color: var(--periwinkle); }
.orch-master-state.is-off, .orch-master-state.is-dim { color: var(--text-dim); }

.orch-container { padding-bottom: 60px; }
.orch-panel { padding: 18px 20px; margin-bottom: 16px; }
.orch-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.orch-panel-head h3 { margin: 0; font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
.orch-clock { color: var(--text-dim); font-size: 12px; }

.orch-override {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--panel-2); border: 1px solid var(--periwinkle);
  border-radius: var(--radius); padding: 12px 16px; margin-bottom: 16px;
  color: var(--text); font-size: 13.5px;
}
.orch-override strong { color: var(--periwinkle); }

.orch-now {
  display: grid; grid-template-columns: 1fr 240px; gap: 24px;
  padding: 20px; margin-bottom: 14px;
}
.orch-now-label {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 9.5px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--text-dim); margin-bottom: 8px;
}
.orch-now-title { font-size: 26px; font-weight: 700; line-height: 1.15; }
.orch-next-title { font-size: 16px; font-weight: 600; line-height: 1.2; }
.orch-now-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 7px; }
.orch-progress {
  height: 3px; background: var(--panel-2); border-radius: var(--radius-sm);
  margin-top: 16px; overflow: hidden;
}
.orch-progress-bar { height: 100%; background: var(--accent); transition: width 0.4s linear; }
.orch-next { border-left: 1px solid var(--border); padding-left: 24px; }

.orch-transport { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }

/* The day, drawn to scale. */
.orch-timeline { position: relative; }
.orch-timeline-track {
  position: relative; height: 46px;
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.orch-block {
  position: absolute; top: 0; bottom: 0;
  background: var(--accent-2); border-right: 1px solid var(--bg);
  display: flex; align-items: center; padding: 0 8px; overflow: hidden;
}
.orch-block span {
  font-size: 11px; font-weight: 600; color: var(--danger-ink);
  white-space: nowrap; text-overflow: ellipsis; overflow: hidden;
}
.orch-block.is-now { background: var(--accent-strong); }
.orch-block.is-seq { background-image: repeating-linear-gradient(
  135deg, rgba(0,0,0,0.16) 0 6px, transparent 6px 12px); }
/* A broken reference is never a colour variation on a working one — it is a
   different state, and it has to look like a fault. */
.orch-block.is-broken { background: var(--panel); border: 1px dashed var(--danger); }
.orch-block.is-broken span { color: var(--danger); }
.orch-playhead {
  position: absolute; top: -3px; bottom: -3px; width: 2px;
  background: var(--periwinkle); box-shadow: 0 0 8px rgba(191, 189, 238, 0.7);
}
.orch-timeline-ruler { position: relative; height: 16px; margin-top: 5px; }
.orch-tick {
  position: absolute; transform: translateX(-50%);
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 9.5px; color: var(--text-dim); letter-spacing: 0.06em;
}
.orch-timeline-empty { color: var(--text-dim); font-size: 13px; margin-top: 12px; }

.orch-events { display: flex; flex-direction: column; gap: 6px; }
.orch-event {
  display: flex; align-items: center; gap: 12px;
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 14px;
}
.orch-event.is-now { border-color: var(--accent); }
.orch-event.is-off { opacity: 0.5; }
.orch-event.is-broken { border-color: var(--danger); }
.orch-event-main { flex: 1; min-width: 0; }
.orch-event-name { font-size: 14px; font-weight: 600; }
.orch-event-meta { font-size: 11px; color: var(--text-dim); margin-top: 3px; }
.orch-event-project { font-size: 10.5px; color: var(--text-dim); margin-top: 2px; opacity: 0.75; }
.orch-event-problem { font-size: 11.5px; color: var(--danger); margin-top: 3px; }
.orch-event-actions { display: flex; gap: 6px; }
.orch-event-toggle { display: flex; align-items: center; }
/* .th-btn is tuned for the timeline's track heads, where it sits at 45%
   opacity against a busy strip. Reordering the schedule is a primary action on
   this page, so the arrows are given their own weight rather than inheriting
   one meant for somewhere else. */
.orch-event .reorder-btns { display: flex; flex-direction: column; gap: 3px; flex: none; }
.orch-event .reorder-btns .th-btn {
  width: 20px; height: 16px; opacity: 1;
  border: 1px solid var(--border); background: var(--panel);
  color: var(--text-dim); font-size: 8px; cursor: pointer;
}
.orch-event .reorder-btns .th-btn:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.orch-event .reorder-btns .th-btn:disabled { opacity: 0.25; cursor: default; }
.orch-event-toggle input {
  width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: var(--accent);
}

.orch-outputs { display: flex; flex-direction: column; gap: 6px; }
.orch-output {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 4px; border-bottom: 1px solid var(--border);
}
.orch-output:last-child { border-bottom: none; }
.orch-output-main { flex: 1; min-width: 0; }
.orch-output-name { font-size: 13.5px; font-weight: 600; }
.orch-output-meta { font-size: 10.5px; color: var(--text-dim); margin-top: 2px; }
.orch-output-project { font-size: 12px; color: var(--text); }
.orch-output-project .muted { color: var(--text-dim); }

.orch-settings {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 14px;
}
.orch-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.orch-field > span {
  font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-dim);
}
.orch-field > span em { text-transform: none; letter-spacing: 0; font-style: normal; opacity: 0.7; }
.orch-time-presets { align-self: flex-start; flex-wrap: wrap; }
.orch-days { display: flex; gap: 6px; flex-wrap: wrap; }
.orch-day {
  display: flex; align-items: center; gap: 5px;
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 5px 8px; font-size: 11.5px; cursor: pointer;
}
.orch-day:hover { border-color: var(--accent); }

.orch-dialog { max-width: 460px; }
.orch-dialog h3 { margin: 0 0 16px; font-size: 15px; }
.orch-dialog-error {
  color: var(--danger); font-size: 12.5px; margin-bottom: 12px;
  border-left: 2px solid var(--danger); padding-left: 10px;
}

.orch-log { display: flex; flex-direction: column; gap: 2px; max-height: 320px; overflow-y: auto; }
.orch-log-row { display: flex; gap: 12px; font-size: 12px; padding: 4px 0; align-items: baseline; }
.orch-log-time { color: var(--text-dim); font-size: 10.5px; flex: none; width: 42px; }
.orch-log-kind {
  color: var(--text-dim); font-size: 9.5px; letter-spacing: 0.14em;
  text-transform: uppercase; flex: none; width: 78px;
}
.orch-log-msg { color: var(--text); }
.orch-log-row.is-warn .orch-log-msg { color: var(--periwinkle); }
.orch-log-row.is-error .orch-log-msg { color: var(--danger); }
.orch-log-row.is-error .orch-log-kind { color: var(--danger); }

@media (max-width: 720px) {
  .orch-now { grid-template-columns: 1fr; }
  .orch-next { border-left: none; border-top: 1px solid var(--border); padding-left: 0; padding-top: 16px; }
}

/* A consequence worth reading, not worth a dialog: shown when claiming the
   projectors stopped another show. Lifted out of the topbar's flex row — that
   row is already full, and a message that arrives truncated to "Sto…" is worse
   than no message. */
.present-note {
  position: fixed; top: 52px; right: 16px; z-index: 900;
  background: var(--panel-2); border: 1px solid var(--periwinkle);
  border-radius: var(--radius); padding: 7px 12px;
  font-size: 11.5px; color: var(--periwinkle); max-width: min(420px, 70vw);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
/* "Showing X" is the live fact on a projector row, next to the projects that
   are merely built for it — it earns the accent, the membership list does not. */
.now-showing { color: var(--accent); }

/* A projector's projects: several at once, one of them possibly live. */
.linked-projects { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.linked-project-row {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--border); border-radius: var(--radius); padding: 7px 12px;
}
.linked-project-row a { flex: 1; font-size: 13px; }
.linked-project-row .now-showing { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; }

/* ---------------------------------------------------------------------------
   Group page
   Same skeleton as a projector's own page — header, tab strip, panels — so the
   two read as the same kind of object. Only the group-specific pieces are new.
   --------------------------------------------------------------------------- */

.group-container { max-width: 1500px; }

.group-control-row {
  display: grid; grid-template-columns: 90px 1fr 52px;
  align-items: center; gap: 12px; padding: 9px 0;
  border-bottom: 1px solid var(--border);
}
.group-control-row:last-of-type { border-bottom: none; }
.group-control-row label { margin: 0; font-size: 12.5px; color: var(--text); }
.group-control-row input[type="range"] { width: 100%; accent-color: var(--accent); }
.group-control-row input[type="range"]:disabled { opacity: 0.35; cursor: not-allowed; }
.group-power button:disabled { opacity: 0.4; cursor: not-allowed; }
.group-control-value { font-size: 11.5px; color: var(--text-dim); text-align: right; }
.group-power { display: flex; gap: 6px; }

.group-member-row { align-items: center; }
.group-member-row .device-row-name a { font-weight: 700; }
.group-member-row .device-row-name a:hover { color: var(--accent); }
.group-member-actions { display: flex; gap: 6px; align-items: center; }

/* The canvas tab keeps its own two-column layout, but inside a tab panel it no
   longer needs the full-page margins it had as a standalone page. */
/* .tab-panel caps at 640px for the form-style panels (About, Settings) — right
   for a list of fields, wrong for the Canvas tab's workspace-plus-sidebar
   layout, which was designed to use the full page width when this was its own
   standalone page. Lifting the cap here is what gives the workspace and the
   Precise Placement fields room again. */
.tab-panel[data-panel="canvas"] { max-width: none; }
.tab-panel .canvas-page-layout { margin: 0; }
.group-canvas-toolbar { display: flex; gap: 8px; margin: 0 0 12px; }
/* The group header carries one more button than a projector's does; letting
   the badges shrink before the buttons wrap keeps it on one row for longer. */
.group-container .device-header .badge { white-space: nowrap; }
.group-container .device-header > div:nth-child(2) { min-width: 0; }

/* Media Bin rows: a real still, with the clip playing on hover. Matches the
   Media Library grid — a row that is blank until hovered gives you nothing to
   scan, which is the whole job of a thumbnail. */
.layer-thumb-wrap {
  position: relative; width: 34px; height: 22px; flex: 0 0 auto;
  border-radius: var(--radius-sm); overflow: hidden; background: var(--bg);
}
.layer-thumb-wrap .layer-thumb { display: block; width: 100%; height: 100%; }
.layer-thumb-preview {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 0.15s;
  pointer-events: none; background: var(--bg);
}
.layer-thumb-preview.show { opacity: 1; }
.layer-thumb-pending {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; font-size: 11px;
}
.layer-thumb-pending .icon { width: auto; }

/* ---- Upload progress ----
   Lives inside the sidebar, right under the nav links, rather than in
   whichever panel started the transfer: media can be added from the
   Editor, the Media Library and a group's canvas, a Show can render from
   the Timeline or the Shows page, and a device sync isn't started from any
   page at all — one fixed spot that doesn't depend on which page happens
   to be open is what makes it findable regardless (see global-progress.js,
   which is what keeps it live everywhere, not just the page that started
   the work). */
.upload-progress {
  margin: 10px 0 0;
  width: 100%;
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--radius);
  max-height: 0; overflow: hidden;
  opacity: 0;
  transition: opacity 0.18s, max-height 0.22s ease;
  /* .side-nav is a flex column, and this panel's own `overflow: hidden`
     (needed for the open/close max-height animation) resets its flex
     automatic-minimum-size to 0 per spec — without flex-shrink: 0, a
     sidebar whose total content (this panel + nav links + activity log)
     exceeds the viewport gets this panel silently squashed below its own
     row content and clipped, rather than just letting .side-nav's existing
     overflow-y: auto take over and scroll. */
  flex-shrink: 0;
}
.upload-progress.open { opacity: 1; max-height: 420px; }
.upload-progress.is-done { border-color: var(--border); }
/* Fallback for a page with no sidebar to sit inside (none today, but
   ensurePanel() in upload-progress.js reaches for this if .side-nav is
   ever missing) — the old floating-toast placement. */
.upload-progress.upload-progress-fallback {
  position: fixed; right: 18px; bottom: 18px; z-index: 1200;
  margin: 0; width: min(340px, calc(100vw - 36px));
  max-height: none; overflow: visible;
  transform: translateY(8px); pointer-events: none;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
}
.upload-progress.upload-progress-fallback.open { transform: translateY(0); pointer-events: auto; }
.upload-progress-head {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 13px 16px; border-bottom: 1px solid var(--border);
}
.upload-progress-title {
  font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-dim);
}
.upload-progress-count {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 11.5px; color: var(--text-dim);
}
.upload-progress-list { max-height: 320px; overflow-y: auto; padding: 8px 16px 14px; }

.upload-row { padding: 12px 0; }
.upload-row + .upload-row { border-top: 1px solid var(--border); }
.upload-row-name {
  font-size: 13px; line-height: 1.4; color: var(--text);
  white-space: normal; overflow-wrap: break-word; word-break: break-word;
  margin-bottom: 6px;
}
.upload-row-meta {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px;
}
.upload-row-pct { font-size: 11.5px; color: var(--text-dim); flex: none; }
.upload-row-cancel {
  border: none; background: transparent; color: var(--text-dim);
  padding: 0 2px; font-size: 16px; line-height: 1; cursor: pointer;
}
.upload-row-cancel:hover { color: var(--danger); }
.side-nav .upload-row a { display: inline; padding: 0; gap: 0; font-size: inherit; font-weight: inherit; background: none; border-radius: 0; }
.upload-row-link { color: inherit; text-decoration: none; }
.upload-row-link:hover { text-decoration: underline; }
.upload-row-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 6px; min-height: 0; }
.upload-row-sub:empty { display: none; }
.upload-row-sub a { color: var(--accent); }
.upload-row-sub.is-failed-text { color: var(--danger); }
.upload-row-actions { display: flex; gap: 8px; margin-top: 8px; }
.upload-row-actions button { font-size: 11.5px; padding: 3px 10px; }
.upload-row-track {
  height: 5px; background: var(--panel-2); border-radius: 3px; overflow: hidden;
}
.upload-row-bar {
  height: 100%; min-width: 4px; width: 0%; background: var(--accent);
  border-radius: 3px; transition: width 0.15s linear;
}
/* A device sync gets a distinct hue from a render/upload while it's still
   active — both kinds can sit in the same panel at once (see upload-
   progress.js's HEAD_WORDING "mixed" case, exactly the render+sync
   combination this differentiates), so the fill color alone should say
   which phase a row is in without having to read its name. Render and
   upload keep the default accent: both mean "this machine is doing the
   work"; periwinkle is the sheet's existing secondary/partial-state color,
   fitting for "copying to a device over the network" specifically. Placed
   before the done/failed rules below so a finished sync still turns the
   same green/red as everything else, rather than staying periwinkle. */
.upload-row[data-kind="sync"] .upload-row-bar,
.upload-row[data-kind="push"] .upload-row-bar,
.upload-row[data-kind="power"] .upload-row-bar { background: var(--periwinkle); }
.upload-row.is-done .upload-row-bar { background: #5fcf7a; }
.upload-row.is-failed .upload-row-bar { background: var(--danger); width: 100% !important; }
.upload-row.is-failed .upload-row-pct { color: var(--danger); }
/* Some bodies are not length-computable, so there is no honest percentage to
   show — a travelling sliver says "working" without inventing a number. */
.upload-row.indeterminate .upload-row-bar {
  width: 35% !important; animation: upload-slide 1.1s ease-in-out infinite;
}
@keyframes upload-slide {
  0% { margin-left: -35%; } 100% { margin-left: 100%; }
}

/* ---------- Shows: per-file render/sync progress bar ----------
   Was the dedicated Encoder tab's own card language (one card per
   projector's file); that tab is gone — the same live elapsed/remaining
   and device-sync progress now renders directly under each file badge in
   the Shows list itself (see shows.js's renderProgressBarHtml/
   syncProgressBarHtml), so this is just the bar/track sizing now. */
/* align-self:stretch, not the parent's own align-items:flex-start (its
   badge/actions row above genuinely wants to stay content-width) — this
   is the one child in that column that should span the full width of
   whatever the badge ends up being, not shrink to its own bar's
   min-width like a bare block would inside an inline-flex column. */
.show-progress-track { height: 6px; width: 100%; align-self: stretch; }
/* upload-slide is the sidebar panel's own indeterminate animation (see
   .upload-row.indeterminate .upload-row-bar), reused here directly rather
   than through that selector — it requires a .upload-row ANCESTOR, which
   a Shows-list row never has, so without this rule of its own the bar sat
   at a static few-pixel sliver: no width, no animation, nothing to look
   at. */
.show-progress-track.indeterminate .upload-row-bar {
  width: 35% !important; animation: upload-slide 1.1s ease-in-out infinite;
}

/* ---------- Shows: file preview / download ---------- */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--panel-2); color: var(--text-dim);
  font-size: 11px; cursor: pointer; text-decoration: none; line-height: 1;
}
.icon-btn:hover { color: var(--text); border-color: var(--accent); }

.show-preview-overlay {
  position: fixed; inset: 0; background: rgba(8, 7, 8, 0.75);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000; padding: 20px;
  opacity: 0; pointer-events: none; transition: opacity 0.12s;
}
.show-preview-overlay.open { opacity: 1; pointer-events: auto; }
.show-preview-box {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px; max-width: 90vw; width: 960px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  transform: translateY(6px); transition: transform 0.12s;
}
.show-preview-overlay.open .show-preview-box { transform: translateY(0); }
.show-preview-head { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.show-preview-title { flex: 1; font-weight: 700; font-size: 14px; }
.show-preview-close {
  border: none; background: transparent; color: var(--text-dim);
  font-size: 20px; cursor: pointer; line-height: 1; padding: 0 4px;
}
.show-preview-close:hover { color: var(--text); }
.show-preview-box video {
  width: 100%; max-height: 70vh; border-radius: var(--radius-sm); background: #000; display: block;
}

/* ---------- Dashboard ---------- */
/* Three bands top to bottom — Create, Control, Automate — rather than a
   rigid 3-column split, since the six widgets the dashboard covers don't
   divide evenly across those three concepts (2/3/1). Each band gets its
   own responsive grid instead. */
.dash-container { padding-bottom: 60px; }
.dash-section { margin-top: 32px; }
.dash-section:first-of-type { margin-top: 20px; }
.dash-section-title {
  font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--accent); font-family: "Archivo", sans-serif; font-weight: 800;
  margin: 0 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.dash-subhead {
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim);
  margin: 20px 0 10px; font-weight: 800;
}

.dash-quick-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 20px; }
.dash-quick-actions .new-project-form { margin: 0; flex-wrap: nowrap; }
.dash-action-links { display: flex; flex-wrap: wrap; gap: 8px; }

.dash-control-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; align-items: start;
}

.dash-stat-row { display: flex; gap: 10px; margin-bottom: 14px; }
.dash-stat {
  flex: 1; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; display: flex; flex-direction: column; gap: 2px;
}
.dash-stat-value { font-family: "Archivo", sans-serif; font-weight: 800; font-size: 22px; line-height: 1.1; }
.dash-stat-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; }
.dash-server-meta { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }

.dash-alerts-list { display: flex; flex-direction: column; gap: 6px; }
.dash-alert {
  display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; line-height: 1.4;
  padding: 8px 10px; border-radius: var(--radius-sm); background: rgba(242, 69, 107, 0.08);
  border: 1px solid rgba(242, 69, 107, 0.25); color: var(--text);
}
.dash-alert.warn { background: rgba(224, 184, 74, 0.08); border-color: rgba(224, 184, 74, 0.3); }
.dash-alert a { color: inherit; text-decoration: underline; }

.dash-now-playing-row, .dash-device-row {
  display: flex; align-items: center; gap: 10px; padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.dash-now-playing-row:last-child, .dash-device-row:last-child { border-bottom: none; }
.dash-np-info, .dash-dev-info { flex: 1; min-width: 0; }
.dash-np-name, .dash-dev-name {
  font-weight: 700; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-np-sub, .dash-dev-sub { font-size: 11.5px; color: var(--text-dim); margin-top: 1px; }
.dash-np-controls { display: flex; gap: 6px; flex-shrink: 0; }
.dash-np-controls button {
  width: 28px; height: 28px; padding: 0; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); font-size: 13px; line-height: 1;
}

.dash-device-list { display: flex; flex-direction: column; }
.dash-dev-status { flex-shrink: 0; font-size: 11.5px; text-align: right; }

.dash-orch-header { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.dash-orch-slot {
  display: flex; align-items: baseline; gap: 10px; padding: 8px 10px; border-radius: var(--radius-sm);
  background: var(--panel-2); border: 1px solid var(--border); margin-bottom: 8px; font-size: 13px;
}
.dash-orch-slot-tag {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim);
  flex-shrink: 0; width: 46px;
}
.dash-orch-slot.current .dash-orch-slot-tag { color: var(--accent); }
.dash-orch-slot-time { color: var(--text-dim); font-size: 11.5px; flex-shrink: 0; }

/* ---------- Clients ---------- */

/* The Pipeline/Clients/Tasks/Analytics sub-nav — real pages (bookmarkable,
   back-button-correct), not a JS tab toggle, so it's a row of <a> tags
   riding the existing .mode-group/.page-tab-switch segmented-control look
   rather than that pattern's usual <button>s. */
.mode-group a, .page-tab-switch a {
  border: none; padding: 6px 14px; text-decoration: none; color: var(--text);
  display: inline-flex; align-items: center; border-radius: var(--radius-sm);
}
.mode-group a.active {
  background: linear-gradient(180deg, var(--accent-strong), var(--accent));
  color: #201503; font-weight: 700;
}

#opp-form textarea {
  background: var(--bg); border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  padding: 7px 14px; width: 100%; font-family: inherit; font-size: inherit; color: inherit; resize: vertical;
}
#opp-form textarea:focus { outline: none; border-color: var(--accent); }

.temp-badge { font-size: 10px; padding: 2px 7px; border-radius: 999px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.temp-badge.hot { background: rgba(242, 69, 107, 0.16); color: #ff8fa3; }
.temp-badge.warm { background: rgba(224, 184, 74, 0.16); color: #e0b84a; }
.temp-badge.cold { background: rgba(74, 192, 224, 0.16); color: #6fd3f0; }

/* Kanban board */
.kanban-board {
  display: flex; gap: 12px; overflow-x: auto; padding-bottom: 12px; align-items: flex-start;
}
.kanban-column {
  flex: 0 0 260px; width: 260px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); display: flex; flex-direction: column; max-height: calc(100vh - 220px);
}
.kanban-column.drag-over { border-color: var(--accent); background: rgba(225, 92, 46, 0.05); }
.kanban-column-header {
  padding: 10px 12px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px;
  flex-shrink: 0;
}
.kanban-column-title { font-weight: 800; font-size: 12.5px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kanban-column-count { font-size: 11px; color: var(--text-dim); background: var(--panel-2); border-radius: 999px; padding: 1px 8px; }
.kanban-column-value { font-size: 10.5px; color: var(--text-dim); padding: 0 12px 8px; flex-shrink: 0; }
.kanban-cards { flex: 1; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 8px; min-height: 40px; }
.kanban-add-btn { margin: 0 8px 8px; }

.opp-card {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px; cursor: grab; display: flex; flex-direction: column; gap: 6px;
}
.opp-card:hover { border-color: var(--accent); }
.opp-card.dragging { opacity: 0.4; }
.opp-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 6px; }
.opp-card-title { font-weight: 700; font-size: 13px; line-height: 1.3; }
.opp-card-client { font-size: 11.5px; color: var(--text-dim); }
.opp-card-meta { display: flex; flex-wrap: wrap; gap: 6px; font-size: 11px; color: var(--text-dim); }
.opp-card-value { color: var(--accent); font-weight: 700; font-size: 12.5px; }
.opp-card-footer { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-top: 2px; }
.opp-card-close-date { font-size: 10.5px; color: var(--text-dim); }
.opp-card.won-convert { border-color: var(--accent); background: rgba(225, 92, 46, 0.07); }
.opp-card-convert-btn { width: 100%; margin-top: 2px; }

/* Client list */
.client-row {
  display: flex; align-items: center; gap: 14px; padding: 12px 14px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--panel); margin-bottom: 8px; text-decoration: none; color: var(--text);
}
.client-row:hover { border-color: var(--accent); }
.client-row-name { font-weight: 700; flex: 1; min-width: 0; }
.client-row-loc { color: var(--text-dim); font-size: 12.5px; flex-shrink: 0; }
.client-row-stat { font-size: 12px; color: var(--text-dim); flex-shrink: 0; text-align: right; min-width: 90px; }
.client-row-stat strong { color: var(--text); }

/* Client profile */
.client-detail-grid { display: grid; grid-template-columns: 300px 1fr; gap: 16px; align-items: start; }
@media (max-width: 900px) { .client-detail-grid { grid-template-columns: 1fr; } }
.contact-row { display: flex; flex-direction: column; gap: 2px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.contact-row:last-child { border-bottom: none; }
.contact-row-name { font-weight: 700; font-size: 13px; }
.contact-row-meta { font-size: 12px; color: var(--text-dim); }
.contact-primary-badge { font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--accent); font-weight: 700; margin-left: 6px; }

.timeline-entry { display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.timeline-entry:last-child { border-bottom: none; }
.timeline-entry-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-dim); margin-top: 5px; flex-shrink: 0; }
.timeline-entry.kind-system .timeline-entry-dot { background: var(--accent); }
.timeline-entry-body { flex: 1; }
.timeline-entry-time { color: var(--text-dim); font-size: 11px; margin-top: 1px; }

.mini-opp-row {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); margin-bottom: 6px; text-decoration: none; color: var(--text);
}
.mini-opp-row:hover { border-color: var(--accent); }
.mini-opp-row-title { flex: 1; font-weight: 700; font-size: 12.5px; }
.mini-opp-row-stage { font-size: 10.5px; color: var(--text-dim); background: var(--panel-2); border-radius: 999px; padding: 2px 8px; }

/* Tasks */
.task-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--panel); margin-bottom: 6px;
}
.task-row.overdue { border-color: var(--danger); background: rgba(242, 69, 107, 0.06); }
.task-row.done { opacity: 0.55; }
.task-row-title { flex: 1; min-width: 0; font-weight: 700; font-size: 13px; }
.task-row-sub { font-size: 11.5px; color: var(--text-dim); }
.priority-badge { font-size: 10px; padding: 2px 7px; border-radius: 999px; font-weight: 700; text-transform: uppercase; }
.priority-badge.high { background: rgba(242, 69, 107, 0.16); color: #ff8fa3; }
.priority-badge.normal { background: var(--panel-2); color: var(--text-dim); }
.priority-badge.low { background: var(--panel-2); color: var(--text-dim); }

/* Analytics */
.analytics-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px; }
.analytics-stat {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px;
}
.analytics-stat-value { font-family: "Archivo", sans-serif; font-weight: 800; font-size: 26px; line-height: 1.1; }
.analytics-stat-label { font-size: 11.5px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 4px; }
.stage-bar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; font-size: 12.5px; }
.stage-bar-label { width: 110px; flex-shrink: 0; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stage-bar-track { flex: 1; height: 8px; background: var(--panel-2); border-radius: 999px; overflow: hidden; }
.stage-bar-fill { height: 100%; background: linear-gradient(90deg, var(--accent-strong), var(--accent)); }
.stage-bar-value { width: 90px; flex-shrink: 0; text-align: right; color: var(--text-dim); font-size: 11.5px; }

/* ---------- Finance ---------- */

.fin-status-badge { font-size: 10px; padding: 2px 8px; border-radius: 999px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.fin-status-badge.draft { background: var(--panel-2); color: var(--text-dim); }
.fin-status-badge.sent, .fin-status-badge.viewed { background: rgba(74, 192, 224, 0.16); color: #6fd3f0; }
.fin-status-badge.accepted, .fin-status-badge.paid { background: rgba(95, 207, 122, 0.16); color: #5fcf7a; }
.fin-status-badge.partially_paid { background: rgba(224, 184, 74, 0.16); color: #e0b84a; }
.fin-status-badge.overdue, .fin-status-badge.rejected { background: rgba(242, 69, 107, 0.16); color: #ff8fa3; }
.fin-status-badge.void, .fin-status-badge.expired { background: var(--panel-2); color: var(--text-dim); text-decoration: line-through; }

.fin-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.fin-table th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); padding: 6px 8px; border-bottom: 1px solid var(--border); }
.fin-table td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.fin-table tr:last-child td { border-bottom: none; }
.fin-table td.num, .fin-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.fin-table a { color: inherit; }
.fin-row-link:hover { background: var(--panel-2); cursor: pointer; }

.fin-list-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
.fin-list-toolbar .spacer { flex: 1; }

.fin-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 20px; }
.fin-stat { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; }
.fin-stat-value { font-family: "Archivo", sans-serif; font-weight: 800; font-size: 24px; line-height: 1.1; }
.fin-stat-value.positive { color: #5fcf7a; }
.fin-stat-value.negative { color: var(--danger); }
.fin-stat-label { font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 4px; }
.fin-stat-sub { font-size: 11px; color: var(--text-dim); margin-top: 2px; }

/* Document builder (Quote/Invoice edit form) — line items + running totals */
.fin-items-table input { width: 100%; padding: 5px 8px; }
.fin-items-table .qty-col { width: 70px; }
.fin-items-table .price-col { width: 110px; }
.fin-items-table .total-col { width: 100px; text-align: right; font-variant-numeric: tabular-nums; }
.fin-totals { margin-left: auto; width: 280px; display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.fin-totals-row { display: flex; justify-content: space-between; padding: 2px 0; }
.fin-totals-row.grand { border-top: 1px solid var(--border); margin-top: 4px; padding-top: 8px; font-weight: 800; font-size: 16px; }
.fin-schedule-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.fin-schedule-row input[type="text"] { flex: 1; }
.fin-schedule-row input[type="number"] { width: 90px; }
.fin-schedule-row input[type="date"] { width: 150px; }

/* Printable document view (Quote/Invoice detail) — same content, styled to
   double as the PDF: print it via the browser's own Print > Save as PDF,
   no server-side PDF library needed. See @media print below for what
   changes when that dialog actually opens. */
.fin-doc { max-width: 800px; margin: 0 auto; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 40px; }
.fin-doc-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px; }
.fin-doc-brand { font-family: "Archivo", sans-serif; font-weight: 800; font-size: 20px; }
.fin-doc-brand .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-right: 8px; }
.fin-doc-title { text-align: right; }
.fin-doc-title h2 { margin: 0; font-size: 22px; text-transform: uppercase; letter-spacing: 0.04em; }
.fin-doc-title .fin-doc-number { color: var(--text-dim); font-size: 13px; margin-top: 2px; }
.fin-doc-parties { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 28px; }
.fin-doc-parties h4 { margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
.fin-doc-meta { display: flex; gap: 24px; margin-bottom: 24px; font-size: 12.5px; flex-wrap: wrap; }
.fin-doc-meta div span { display: block; color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; }
.fin-doc-section { margin-top: 24px; }
.fin-doc-section h4 { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); margin-bottom: 6px; }
.fin-doc-actions { max-width: 800px; margin: 0 auto 16px; display: flex; gap: 8px; justify-content: flex-end; }

@media print {
  .side-nav, .topbar, .fin-doc-actions, .activity-log { display: none !important; }
  body, .container { background: #fff !important; color: #000 !important; margin: 0 !important; padding: 0 !important; }
  .fin-doc { border: none; box-shadow: none; max-width: 100%; color: #000; background: #fff; }
  .fin-doc-brand .dot { background: #000; }
  .fin-table td, .fin-table th { border-color: #ccc; color: #000; }
}

.topbar-access { color: var(--text-dim); font-size: 12px; white-space: nowrap; }
.topbar-access.soon { color: #e2a33c; }

/* Inventory */
.inv-add { display: grid; grid-template-columns: minmax(200px, 2fr) 150px auto minmax(140px, 1fr) auto; gap: 8px; margin: 0 0 16px; align-items: center; }
.inv-add input, .inv-add select { width: 100%; }
.inv-qty-wrap { display: flex; align-items: center; gap: 4px; }
.inv-qty-wrap::before { content: "×"; color: var(--text-dim); margin-right: 2px; }
.inv-qty-wrap input { width: 64px; text-align: center; font-family: "JetBrains Mono", monospace; -moz-appearance: textfield; }
.inv-qty-wrap input::-webkit-outer-spin-button, .inv-qty-wrap input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.inv-qty-wrap button { padding: 4px 9px; }
.inv-group { margin-bottom: 18px; }
.inv-group h3 { margin: 0 0 6px; font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
.inv-row { display: grid; grid-template-columns: 1fr 70px minmax(120px, 1fr) 140px auto; gap: 10px; align-items: center; padding: 9px 0; border-top: 1px solid var(--border); font-size: 13.5px; }
.inv-row:first-of-type { border-top: none; }
.inv-row .name { font-weight: 600; }
.inv-row .sub { color: var(--text-dim); font-size: 12px; font-weight: 400; }
.inv-row .qty { font-family: "JetBrains Mono", monospace; text-align: right; }
.inv-row .loc { color: var(--text-dim); }
.inv-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #666; margin-right: 6px; vertical-align: 1px; }
.inv-dot.on { background: #50be78; }
.inv-dot.off { background: #e05252; }
.inv-row .dev { font-size: 12px; color: var(--text-dim); white-space: nowrap; }
@media (max-width: 760px) {
  .inv-add { grid-template-columns: 1fr 1fr; }
  .inv-add input[type="text"]:first-child { grid-column: 1 / -1; }
  .inv-row { grid-template-columns: 1fr 60px auto; }
  .inv-row .loc, .inv-row .dev { grid-column: 1 / -1; }
}

/* Finance → Business model */
.bm-head { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; margin: 0 0 12px; flex-wrap: wrap; }
.bm-head ~ .panel { margin-bottom: 16px; }
.bm-canvas { display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 8px; grid-template-areas:
  "partners partners activities activities value value relationships relationships segments segments"
  "partners partners resources resources value value channels channels segments segments"
  "costs costs costs costs costs revenue revenue revenue revenue revenue"; }
.bm-cell { display: flex; flex-direction: column; gap: 4px; background: var(--panel-2, #151313); border: 1px solid var(--border); padding: 8px 10px; min-width: 0; }
.bm-cell > span { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim); }
.bm-cell textarea { flex: 1; min-height: 96px; width: 100%; resize: vertical; background: transparent; border: none; color: var(--text); font: inherit; font-size: 13px; line-height: 1.45; padding: 0; outline: none; }
.bm-cell:focus-within { border-color: var(--accent); }
.a-partners { grid-area: partners; } .a-activities { grid-area: activities; } .a-value { grid-area: value; }
.a-relationships { grid-area: relationships; } .a-segments { grid-area: segments; } .a-resources { grid-area: resources; }
.a-channels { grid-area: channels; } .a-costs { grid-area: costs; } .a-revenue { grid-area: revenue; }
.bm-table-wrap { overflow-x: auto; }
.bm-table input { width: 100%; min-width: 64px; font-size: 13px; }
.bm-table td.num input { text-align: right; max-width: 96px; margin-left: auto; display: block; }
.bm-table th.num, .bm-table td.num { text-align: right; }
.bm-fixed-row { display: grid; grid-template-columns: 1fr 140px auto; gap: 8px; margin: 0 0 6px; align-items: center; }
.bm-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.bm-assume { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px 14px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.bm-assume label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-dim); }
.bm-assume input { width: 100%; }
.bm-scen { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; margin: 0 0 14px; }
.bm-scen .bm-empty { grid-column: 1 / -1; margin: 0; }
.bm-sc { border: 1px solid var(--border); border-top: 3px solid var(--c); padding: 10px 12px; background: var(--panel-2, #151313); }
.bm-sc-h { font-size: 12px; font-weight: 600; margin: 0 0 6px; color: var(--c); }
.bm-kv { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; padding: 3px 0; color: var(--text-dim); }
.bm-kv strong { color: var(--text); font-weight: 500; text-align: right; }
.bm-kv.big { font-size: 14px; border-top: 1px solid var(--border); margin-top: 4px; padding-top: 6px; }
.pos { color: #5fcf7a !important; } .neg { color: #e05252 !important; }
.bm-chart-wrap { width: 100%; margin: 4px 0 6px; }
.bm-chart-wrap canvas { width: 100%; display: block; }
.bm-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; margin: 0 0 12px; align-items: center; }
.bm-legend span::before { content: ""; display: inline-block; width: 14px; height: 3px; margin-right: 6px; vertical-align: 3px; background: currentColor; }
.bm-legend .lo { color: #e2a33c; } .bm-legend .ba { color: #e0613f; } .bm-legend .hi { color: #5fcf7a; }
.bm-legend .hint-text::before { display: none; }
.bm-tabs { display: flex; gap: 6px; margin: 4px 0 8px; }
.bm-tabs button { background: none; border: 1px solid var(--border); color: var(--text-dim); padding: 4px 12px; font-size: 12px; cursor: pointer; }
.bm-tabs button.active { border-color: var(--accent); color: var(--text); }
.bm-compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; }
.bm-cmp { border: 1px solid var(--border); padding: 8px 12px; display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--text-dim); }
.bm-cmp strong { font-size: 17px; font-weight: 500; color: var(--text); }
.bm-cmp em { font-style: normal; }
@media (max-width: 860px) {
  .bm-canvas { grid-template-columns: 1fr 1fr; grid-template-areas: "value value" "segments relationships" "channels activities" "resources partners" "revenue costs"; }
}

/* Finance → Recurring, Budgets, Cash flow, Export */
.rc-off td { opacity: .55; }
.bg-table th, .bg-table td { white-space: nowrap; }
.bg-bar { height: 4px; background: var(--border); margin-top: 4px; width: 100%; min-width: 80px; }
.bg-bar i { display: block; height: 100%; background: #5fcf7a; }
.bg-bar i.over { background: #e05252; }
.cf-h { margin: 22px 0 6px; color: var(--text-dim); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; }
.ex-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 9px 0; border-top: 1px solid var(--border); }
.ex-row:first-child { border-top: none; }
.ex-btn { display: inline-block; background: var(--accent); color: #1a0b06; font-weight: 600; padding: 9px 16px; text-decoration: none; font-size: 13px; }
.ex-btn.small { background: none; border: 1px solid var(--border); color: var(--text); padding: 5px 12px; font-weight: 400; }
.ex-btn:hover { filter: brightness(1.1); }

/* Finance → Settings and Clients */
.fin-settings { max-width: 860px; }
.fin-settings .panel { margin-bottom: 14px; }
.fin-set-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; }
.fin-set-grid label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--text-dim); }
.fin-set-grid label.wide { grid-column: 1 / -1; }
.fin-set-grid input, .fin-set-grid select, .fin-set-grid textarea { width: 100%; color: var(--text); font-size: 13.5px; }
.fin-set-grid textarea { background: var(--bg); border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 7px 14px; font-family: inherit; resize: vertical; }
.fin-rate-field { display: flex; align-items: center; gap: 8px; }
.fin-rate-field input { max-width: 140px; }
.fin-rate-field span { color: var(--text-dim); font-size: 13px; }
.fin-logo-row { display: flex; gap: 16px; align-items: center; margin-top: 14px; }
.fin-logo-box { width: 160px; height: 72px; border: 1px dashed var(--border); border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; background: #fff; overflow: hidden; }
.fin-logo-box img { max-width: 100%; max-height: 100%; object-fit: contain; }
.fin-set-actions { display: flex; justify-content: flex-end; align-items: center; gap: 12px; }
.fin-owed { color: var(--warn, #e2a33c); font-weight: 600; }
.fin-row-actions { white-space: nowrap; text-align: right; }
.fin-row-actions button + button { margin-left: 6px; }
@media (max-width: 720px) { .fin-set-grid { grid-template-columns: 1fr; } }

/* Receipt scanning on the Expenses page (finance-expenses.js) */
.fin-scan-btn svg { width: 16px; height: 16px; vertical-align: -3px; margin-right: 7px; }
.scan-note { background: rgba(226, 163, 60, 0.12); border: 1px solid rgba(226, 163, 60, 0.5); border-radius: var(--radius-sm); padding: 8px 12px; font-size: 12.5px; line-height: 1.45; margin: 0 0 12px; }
.scan-note.high { background: rgba(80, 190, 120, 0.12); border-color: rgba(80, 190, 120, 0.5); }
.scan-note.low { background: rgba(220, 80, 70, 0.12); border-color: rgba(220, 80, 70, 0.5); }
.scan-attached { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.scan-attached img { width: 44px; height: 44px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border); }
.scan-pulse { animation: scan-pulse 0.9s ease-in-out 3; }
@keyframes scan-pulse { 50% { transform: scale(1.06); } }
@media (max-width: 640px) {
  /* On a phone the expense form is the whole screen, one field per line. */
  .confirm-overlay:has(.fin-form) { align-items: flex-start; padding: 0; overflow-y: auto; }
  .confirm-overlay .fin-form { max-width: none !important; border-radius: 0; min-height: 100%; padding: 18px 16px 32px; }
  .fin-form .field-row { flex-direction: column; gap: 10px !important; }
  .fin-list-toolbar #scan-receipt-btn { width: 100%; order: -1; padding: 14px; font-size: 15px; }
  .fin-list-toolbar #scan-receipt-btn svg { width: 20px; height: 20px; vertical-align: -5px; }
  #expenses-table th:nth-child(n+3):nth-child(-n+7), #expenses-table td:nth-child(n+3):nth-child(-n+7) { display: none; }
}

/* Media Processing's "Add media" composer — one screen: source, destination
   and quality on the left, a live result summary and the Process button on
   the right, everything less common under Advanced. */
.mp-composer { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.mp-advanced { grid-column: 1 / -1; }
.mp-advanced summary { cursor: pointer; color: var(--text-dim); font-size: 13px; }
.mp-block { margin-bottom: 16px; }
.mp-label { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); margin: 0 0 6px; }
.mp-dim { color: var(--text-dim); font-size: 12px; margin: 6px 0 0; }
.mp-drop { border: 2px dashed var(--border); border-radius: 6px; padding: 14px; color: var(--text-dim); transition: border-color .15s, color .15s; }
.mp-result { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; }
.mp-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 7px 0; border-top: 1px solid var(--border); font-size: 13px; }
.mp-row:first-child { border-top: none; }
.mp-row span:first-child { color: var(--text-dim); }
.mp-load-bar { width: 96px; height: 6px; background: var(--panel-2); border-radius: 3px; overflow: hidden; display: inline-block; vertical-align: middle; margin-right: 8px; }
.mp-load-bar i { display: block; height: 100%; }
.mp-note { font-size: 12px; color: var(--text-dim); margin: 8px 0 0; }
.mp-note.warn { color: #e0b84a; }
@media (max-width: 760px) { .mp-composer { grid-template-columns: minmax(0, 1fr); } }

/* ---- Settings: category nav + one group at a time ---- */
.set-layout { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 20px; align-items: start; }
@media (max-width: 760px) { .set-layout { grid-template-columns: 1fr; } }
.set-nav { display: flex; flex-direction: column; gap: 2px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel); padding: 6px; position: sticky; top: 12px; }
.set-nav button { border: none; background: none; color: var(--text-dim); text-align: left; padding: 9px 12px; border-radius: 6px; font-size: 13px; cursor: pointer; }
.set-nav button:hover { color: var(--text); background: rgba(255, 255, 255, 0.04); }
.set-nav button.active { background: rgba(255, 106, 61, 0.14); color: #ff8a5c; font-weight: 600; }
.set-nav button[hidden] { display: none; }
@media (max-width: 760px) { .set-nav { flex-direction: row; overflow-x: auto; position: static; } .set-nav button { white-space: nowrap; } }
.set-cat[hidden] { display: none; }
.set-cat .panel { margin-bottom: 16px; }
.set-cap-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.set-cap-row label { margin: 0; }
.set-cap-row input { width: 120px; }
.set-unit { color: var(--text-dim); font-size: 13px; }
.set-download { display: inline-block; padding: 5px 12px; font-size: 12px; font-weight: 600; border: 1.5px solid var(--border); border-radius: var(--radius-sm); color: var(--text); text-decoration: none; }
.set-download:hover { border-color: var(--accent); }
.set-details { margin-top: 12px; }
.set-details summary { cursor: pointer; color: var(--text-dim); font-size: 12.5px; }
.startup-pref-row { border-radius: 6px; padding-left: 8px; padding-right: 8px; }
.startup-pref-row input { accent-color: var(--accent); }
.startup-pref-row.selected { background: rgba(255, 106, 61, 0.08); }
.set-savebar { position: sticky; bottom: 12px; display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; padding: 10px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-2); box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5); font-size: 13px; color: #e2a33c; }
.set-savebar[hidden] { display: none; }
.set-savebar-actions { display: flex; gap: 8px; }

/* ---- Media library: collections, quiet cards, storage strip ---- */
.folder-mosaic { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 2px; width: 100%; height: 100%; }
.folder-mosaic span { background: var(--panel-2) center/cover no-repeat; }
.project-card .thumb.folder-thumb { overflow: hidden; }
/* Card actions (folder, batch tick, Delete) only show on hover or focus — ten
   always-lit Delete buttons made the grid read as a wall of warnings. Touch
   screens have no hover, so they keep everything visible. */
@media (hover: hover) {
  .asset-card .body .row { opacity: 0; transition: opacity 0.12s; }
  .asset-card:hover .body .row, .asset-card:focus-within .body .row, .asset-card.selected .body .row { opacity: 1; }
}
.media-dropzone {
  display: flex; align-items: center; justify-content: center; margin-top: 22px; padding: 14px;
  border: 1.5px dashed var(--border); border-radius: var(--radius-sm); color: var(--text-dim);
  font-size: 13px; cursor: pointer;
}
.media-dropzone:hover { border-color: var(--accent); color: var(--text); }
.media-storage {
  display: flex; align-items: flex-end; gap: 26px; flex-wrap: wrap; margin-top: 20px; padding: 12px 16px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel);
}
.ms-cell { min-width: 140px; }
.ms-cell.off { opacity: 0.55; }
.ms-label { color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px; }
.ms-value { font-size: 13px; }
.ms-value.small { font-size: 11.5px; color: var(--text-dim); margin-top: 4px; }
.ms-bar { height: 5px; width: 160px; border-radius: 3px; background: var(--panel-2); overflow: hidden; }
.ms-bar span { display: block; height: 100%; background: var(--text-dim); }
.ms-link { margin-left: auto; color: var(--accent-strong); font-size: 12.5px; text-decoration: none; }
.media-cached { margin-top: 14px; }
.media-cached > summary { cursor: pointer; color: var(--text-dim); font-size: 12.5px; }

/* ---- Group canvas: selection, joins, snapping, status ---- */
.canvas-tile.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* A tile that's part of a multi-selection (shift/cmd-click, see
   multiSelectedIds in canvas-page.js) — distinct dashed outline so a group
   of several reads differently from the one single-selected tile driving
   the precise-inputs panel, the same distinction routing.js's own
   rt-multi-selected already draws for the Output Nodes graph. */
.canvas-tile.multi-selected { border-color: var(--periwinkle); box-shadow: 0 0 0 1px var(--periwinkle); }
.canvas-tile.multi-selected.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 0 3px var(--periwinkle); }
.join-badge {
  position: absolute; transform: translateX(-50%); z-index: 6; pointer-events: none;
  padding: 1px 8px; border-radius: 999px; font-size: 10px; white-space: nowrap;
  background: rgba(18, 51, 47, 0.92); border: 1px solid #2fd6d0; color: #2fd6d0;
}
.join-badge.warn { background: rgba(42, 34, 16, 0.92); border-color: #8a6a1e; color: #e2a33c; }
.join-badge.horizontal { transform: translateY(-50%); }
.snap-guide { position: absolute; pointer-events: none; z-index: 7; border: 0 dashed #2fd6d0; }
.snap-guide.x { top: 0; bottom: 0; border-left-width: 1px; }
.snap-guide.y { left: 0; right: 0; border-top-width: 1px; }
#snap-toggle-btn.active { border-color: var(--accent); color: var(--accent-strong); }
.canvas-status { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; padding: 8px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel); font-size: 12.5px; }
.canvas-status[hidden] { display: none; }
.join-tone.ok { color: #6fd08a; }
.join-tone.warn { color: #e2a33c; }
.precise-row-next { font-size: 12px; margin: -2px 0 8px; }
.canvas-joins { margin-top: 12px; }
.canvas-joins[hidden] { display: none; }
.joins-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.joins-table th { text-align: left; color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; padding: 4px 8px 6px 0; font-weight: 600; }
.joins-table td { padding: 7px 8px 7px 0; border-top: 1px solid var(--border); }

/* ---- Side nav: groups, badges, control menu, icon rail ---- */
.side-nav a { position: relative; }
.nav-group { display: flex; flex-direction: column; }
.nav-group[hidden] { display: none; }
.nav-group + .nav-group { margin-top: 6px; }
.nav-group-head {
  display: flex; align-items: center; gap: 6px; width: 100%; padding: 8px 12px 4px;
  background: none; border: none; color: var(--text-dim); cursor: pointer; text-align: left;
  font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
}
.nav-group-head:hover { color: var(--text); }
.nav-caret { display: inline-block; font-size: 9px; transition: transform 0.12s; }
.nav-group.collapsed .nav-caret { transform: rotate(-90deg); }
.nav-group-body { display: flex; flex-direction: column; gap: 2px; }
.nav-group.collapsed .nav-group-body { display: none; }
.nav-group-alert { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); margin-left: 2px; }
.nav-group-alert[hidden] { display: none; }
.nav-badge {
  margin-left: auto; font-size: 10px; font-weight: 700; padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text-dim); white-space: nowrap;
}
.nav-badge[hidden] { display: none; }
.nav-badge[data-tone="ok"] { color: #6fd08a; border-color: #2f5a3a; }
.nav-badge[data-tone="warn"] { color: #e2a33c; border-color: #6b5a1e; }
.nav-badge[data-tone="bad"] { color: var(--danger); border-color: #7a2f2b; }
.nav-badge[data-tone="accent"] { color: #fff; background: var(--accent); border-color: var(--accent); }
.nav-status { display: flex; align-items: center; gap: 8px; margin-top: 8px; padding: 0 4px; font-size: 11.5px; color: var(--text-dim); }
.nav-status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-dim); flex: none; }
.nav-status[data-tone="ok"] { color: #6fd08a; }
.nav-status[data-tone="ok"] .nav-status-dot { background: #6fd08a; }
.nav-status[data-tone="warn"] { color: #e2a33c; }
.nav-status[data-tone="warn"] .nav-status-dot { background: #e2a33c; }
.nav-collapse-btn {
  display: flex; align-items: center; gap: 8px; margin-top: 10px; padding: 6px 8px;
  background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 12px; font-weight: 600;
  border-radius: var(--radius-sm);
}
.nav-collapse-btn:hover { color: var(--text); background: var(--panel-2); }
@media (max-width: 860px) { .nav-collapse-btn { display: none; } }

.transport-more-pop {
  position: fixed; z-index: 60; min-width: 250px; padding: 6px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
}
.transport-more-pop[hidden] { display: none; }
.tmp-row { display: flex; align-items: center; gap: 10px; padding: 5px 6px; font-size: 12.5px; color: var(--text); }
.tmp-row:has(button[hidden]) { display: none; }
.global-transport .transport-more-pop button, .transport-more-pop button {
  flex: none; width: 30px; height: 30px; max-width: none; font-size: 13px; line-height: 1;
  /* These live in a body-level popup, not inside .global-transport, so they
     don't inherit that row's centering — without this the default button
     padding (5px 12px) leaves ~6px inside a 30px square and the icon spills
     off-centre. */
  display: flex; align-items: center; justify-content: center; padding: 0;
}

/* Icon rail — wide screens only; the phone drawer stays as it was. */
@media (min-width: 861px) {
  body.nav-collapsed { --side-nav-w: 68px; }
  body.nav-collapsed .side-nav { padding-left: 8px; padding-right: 8px; align-items: stretch; }
  body.nav-collapsed .nav-label,
  body.nav-collapsed .nav-group-label,
  body.nav-collapsed .nav-caret,
  body.nav-collapsed .nav-status,
  body.nav-collapsed .nav-collapse-label,
  body.nav-collapsed .activity-log { display: none; }
  body.nav-collapsed .side-nav a { justify-content: center; padding: 10px 0; gap: 0; }
  body.nav-collapsed .nav-group-head { height: 1px; padding: 0; margin: 8px 6px; border-top: 1px solid var(--border); cursor: default; }
  body.nav-collapsed .nav-group-alert { display: none; }
  body.nav-collapsed .nav-group.collapsed .nav-group-body { display: flex; }
  body.nav-collapsed .nav-badge { position: absolute; top: 5px; right: 8px; width: 9px; height: 9px; padding: 0; font-size: 0; }
  body.nav-collapsed .nav-badge[data-tone="ok"] { background: #6fd08a; border-color: #6fd08a; }
  body.nav-collapsed .nav-badge[data-tone="warn"] { background: #e2a33c; border-color: #e2a33c; }
  body.nav-collapsed .nav-badge[data-tone="bad"] { background: var(--danger); border-color: var(--danger); }
  body.nav-collapsed .global-transport { flex-direction: column; align-items: center; gap: 6px; }
  body.nav-collapsed .nav-collapse-btn { justify-content: center; margin-top: auto; }
  /* The transfer panel has no room in a 68px rail; float it beside it. */
  body.nav-collapsed .side-nav .upload-progress { position: fixed; left: calc(var(--side-nav-w) + 8px); bottom: 12px; width: 260px; z-index: 40; }
}

/* ---- Dashboard: hero + quiet panels ---- */
.dash-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.dash-headline { font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }
.dash-headline.ok { color: #6fd08a; }
.dash-headline.warn { color: #e2a33c; }
.dash-banner { margin-bottom: 14px; padding: 8px 14px; border: 1px solid #6b5a1e; border-radius: var(--radius-sm); background: rgba(226, 163, 60, 0.08); font-size: 13px; }
.dash-banner[hidden] { display: none; }
.dash-banner-row { padding: 3px 0; color: #e2a33c; }
.dash-banner-row a { color: var(--accent-strong); }

.dash-hero {
  display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 14px 24px; margin-bottom: 16px; padding: 18px 20px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--panel);
}
.dash-hero.cued { border-color: #6b5a1e; }
.dash-hero.problem { border-color: #7a2f2b; }
.dash-hero.idle { grid-template-columns: 1fr; }
@media (max-width: 860px) { .dash-hero { grid-template-columns: 1fr; } }
.dash-kicker { color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 8px; }
.dash-hero-title { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dash-hero-title h3 { margin: 0; font-size: 22px; }
.dash-pill, .dash-tag, .dash-chip { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 11px; border: 1px solid var(--border); color: var(--text-dim); white-space: nowrap; }
.dash-pill.ok, .dash-chip.ok, .dash-tag.ok { color: #6fd08a; border-color: #2f5a3a; }
.dash-pill.warn, .dash-chip.warn, .dash-tag.warn { color: #e2a33c; border-color: #6b5a1e; }
.dash-progress { margin: 16px 0 6px; max-width: 560px; }
.dash-progress-bar { height: 8px; border-radius: 4px; background: var(--panel-2); overflow: hidden; }
.dash-progress-fill { display: block; height: 100%; width: 0; background: var(--accent); border-radius: 4px; }
.dash-progress-meta { display: flex; justify-content: space-between; gap: 12px; margin-top: 6px; font-size: 11.5px; color: var(--text-dim); }
.dash-hero-controls { display: flex; gap: 8px; align-items: center; margin-top: 14px; flex-wrap: wrap; }
.dash-play-big { padding: 10px 22px; font-size: 14px; }
.dash-wall .dash-kicker { margin-bottom: 6px; }
.dash-wall-map { position: relative; width: 100%; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); overflow: hidden; }
.dash-wall-block { position: absolute; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 1px solid; font-size: 11px; color: #fff; text-decoration: none; }
.dash-wall-block.tone-0 { background: rgba(47, 214, 208, 0.16); border-color: #2fd6d0; }
.dash-wall-block.tone-1 { background: rgba(181, 140, 255, 0.16); border-color: #b58cff; }
.dash-wall-block:hover { filter: brightness(1.4); }
.dash-strip { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; }
.dash-proj { display: flex; align-items: flex-start; gap: 10px; padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg); font-size: 13px; }
.dash-proj a { color: var(--text); font-weight: 600; text-decoration: none; }
.dash-proj.off { opacity: 0.75; }
.dash-proj-sub { color: var(--text-dim); font-size: 11.5px; margin-top: 1px; }
.dash-problem { grid-column: 1 / -1; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 10px 14px; border: 1px solid #7a2f2b; border-radius: var(--radius-sm); background: rgba(200, 60, 50, 0.08); font-size: 12.5px; }
.dash-problem strong { color: var(--danger); }
.dash-problem span { color: var(--text-dim); }
.dash-idle-pick { display: flex; gap: 8px; margin-top: 14px; max-width: 520px; }
.dash-idle-pick select { flex: 1; }
.dash-tool-pick { margin-top: 16px; max-width: 520px; }
.dash-link { color: var(--accent-strong); font-size: 12.5px; text-decoration: none; }
.dash-link:hover { text-decoration: underline; }

.dash-mid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin-bottom: 20px; }
@media (max-width: 980px) { .dash-mid { grid-template-columns: 1fr; } }
.dash-mid .panel { margin: 0; }
.dash-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.dash-panel-head h3 { margin: 0; }
.dash-reach { font-size: 11.5px; color: #6fd08a; display: flex; align-items: center; gap: 6px; }
.dash-bar-row { display: grid; grid-template-columns: 38px minmax(0, 1fr) 70px; align-items: center; gap: 10px; padding: 6px 0; font-size: 12.5px; }
.dash-bar-label { color: var(--text-dim); }
.dash-bar { height: 7px; border-radius: 4px; background: var(--panel-2); overflow: hidden; }
.dash-bar span { display: block; height: 100%; border-radius: 4px; }
.dash-bar .ok { background: #6fd08a; }
.dash-bar .warn { background: #e2a33c; }
.dash-bar .bad { background: var(--danger); }
.dash-bar-val { text-align: right; color: var(--text-dim); font-size: 11.5px; }
.dash-act-row { padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.dash-act-row:last-of-type { border-bottom: none; }
.dash-act-sub { color: var(--text-dim); font-size: 11px; margin-top: 1px; }
.dash-slot-tag { color: var(--accent-strong); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; margin-right: 4px; }
.dash-orch-actions { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 12px; }


.dash-more { margin-top: 10px; }
.dash-more > summary { cursor: pointer; color: var(--text-dim); font-size: 12px; }
.dash-more[open] > summary { margin-bottom: 8px; }
.dash-kv { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; border-bottom: 1px solid var(--border); font-size: 12px; }
.dash-kv > span:first-child { color: var(--text-dim); }
.dash-kv > span:last-child { text-align: right; }
.warn-text { color: #e2a33c; }
.dash-store-row { display: grid; grid-template-columns: minmax(0, 1fr) 70px 62px; align-items: center; gap: 8px; padding: 3px 0; font-size: 12px; }
.dash-store-bar { height: 5px; border-radius: 3px; background: var(--panel-2); overflow: hidden; }
.dash-store-bar span { display: block; height: 100%; background: var(--text-dim); border-radius: 3px; }

/* ---- Projects page: cards with status, hover actions, selection bar ---- */
.proj-container { max-width: 1560px; margin-left: 0; }
.proj-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
.proj-sub { color: var(--text-dim); font-size: 12.5px; margin-top: 2px; }
.proj-tools { display: flex; align-items: center; gap: 10px; }
.proj-tools input[type="search"] { width: 240px; }
.proj-newwrap { position: relative; }
.proj-newpop {
  position: absolute; right: 0; top: calc(100% + 8px); width: 340px; z-index: 40; padding: 14px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 34px rgba(0, 0, 0, 0.55);
}
.proj-newpop[hidden] { display: none; }
.proj-newpop input[type="text"] { width: 100%; }
.proj-kicker { color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; margin: 12px 0 6px; }
.proj-start-pane { margin-top: 8px; max-height: 150px; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; }
.proj-start-pane[hidden] { display: none; }
.proj-check { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.proj-check input { width: auto; }
.proj-newpop-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.proj-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.proj-chip { padding: 4px 14px; border-radius: 999px; font-size: 12px; background: none; border: 1px solid var(--border); color: var(--text-dim); cursor: pointer; }
.proj-chip:hover { color: var(--text); }
.proj-chip.active { background: var(--panel-2); color: var(--text); border-color: var(--text-dim); }
.proj-chip[hidden] { display: none; }
.proj-sort { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); margin: 0; }
.proj-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.proj-card { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; transition: border-color 0.15s; }
.proj-card:hover { border-color: var(--text-dim); }
.proj-card.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.proj-thumb { position: relative; aspect-ratio: 16 / 9; background: var(--panel-2) center / cover no-repeat; color: var(--text-dim); font-size: 12px; }
.proj-thumb-link { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: inherit; text-decoration: none; }
.proj-tick { position: absolute; top: 8px; left: 8px; z-index: 3; margin: 0; padding: 3px; border-radius: 6px; background: rgba(0, 0, 0, 0.55); }
.proj-tick input { width: 16px; height: 16px; margin: 0; display: block; accent-color: var(--accent); }
.proj-tag { position: absolute; top: 8px; left: 42px; z-index: 2; padding: 2px 9px; border-radius: 999px; font-size: 10.5px; background: rgba(0, 0, 0, 0.62); color: #b58cff; pointer-events: none; }
.proj-tag.archived { left: auto; right: 44px; color: var(--text-dim); }
.proj-state { position: absolute; top: 8px; right: 44px; z-index: 2; padding: 2px 9px; border-radius: 999px; font-size: 10.5px; background: rgba(0, 0, 0, 0.62); pointer-events: none; }
.proj-state.cued { color: #e2a33c; }
.proj-state.playing { color: #6fd08a; }
.proj-menu-btn { position: absolute; top: 6px; right: 8px; z-index: 3; width: 28px; height: 26px; padding: 0; border: none; background: rgba(0, 0, 0, 0.55); color: #fff; border-radius: 6px; font-size: 16px; line-height: 1; cursor: pointer; }
.proj-menu { position: absolute; top: 36px; right: 8px; z-index: 5; min-width: 130px; padding: 4px; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55); display: flex; flex-direction: column; }
.proj-menu[hidden] { display: none; }
.proj-menu button { text-align: left; border: none; background: none; color: var(--text); padding: 7px 10px; border-radius: 5px; font-size: 12.5px; cursor: pointer; }
.proj-menu button:hover { background: var(--panel-2); }
.proj-menu .danger-text { color: var(--danger); }
.proj-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 3px; }
.proj-name { font-family: "Archivo", sans-serif; font-weight: 700; font-size: 15px; color: var(--text); text-decoration: none; }
.proj-name:hover { color: var(--accent-strong); }
.proj-rename { font-weight: 700; font-size: 15px; padding: 2px 6px; }
.proj-meta { color: var(--text-dim); font-size: 12px; }
.proj-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.proj-pchip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px; border: 1px solid var(--border); border-radius: 999px; font-size: 11.5px; color: var(--text-dim); text-decoration: none; }
.proj-pchip:hover { color: var(--text); border-color: var(--text-dim); }
.proj-next { margin-top: 6px; font-size: 12px; color: #e2a33c; text-decoration: none; }
.proj-next:hover { text-decoration: underline; }
.proj-actions { display: flex; gap: 8px; margin-top: 10px; }
.proj-btn { padding: 5px 12px; font-size: 12px; font-weight: 600; border: 1.5px solid var(--border); border-radius: var(--radius-sm); color: var(--text); text-decoration: none; }
.proj-btn:hover { border-color: var(--accent); }
.proj-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
/* The action row only appears on hover or focus — three buttons per card made
   the grid busy — but touch screens have no hover, so they keep it. */
@media (hover: hover) {
  .proj-actions { opacity: 0; transition: opacity 0.12s; }
  .proj-card:hover .proj-actions, .proj-card:focus-within .proj-actions, .proj-card.selected .proj-actions { opacity: 1; }
  .proj-tick { opacity: 0; transition: opacity 0.12s; }
  .proj-card:hover .proj-tick, .proj-card.selected .proj-tick, .proj-card:focus-within .proj-tick { opacity: 1; }
}
.proj-selbar {
  position: sticky; bottom: 14px; display: flex; align-items: center; gap: 10px; margin-top: 18px; padding: 10px 16px;
  background: var(--panel-2); border: 1px solid var(--accent); border-radius: 10px; box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55); z-index: 20;
}
.proj-selbar[hidden] { display: none; }

/* ---- Orchestrator: status, control dock, timeline, events ---- */
.orch-container { max-width: 1560px; margin-left: 0; }
.orch-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
.orch-sub { color: var(--text-dim); margin: 2px 0 0; }
.orch-status {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(220px, 300px); gap: 24px; padding: 18px 22px; margin-bottom: 12px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--panel);
}
@media (max-width: 800px) { .orch-status { grid-template-columns: 1fr; } }
.orch-status[data-state="running"] { border-color: #2f5a3a; }
.orch-status[data-state="manual"] { border-color: #6a5aa8; }
.orch-kicker { color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 8px; }
.orch-status[data-state="running"] #status-kicker { color: #6fd08a; }
.orch-status[data-state="manual"] #status-kicker { color: var(--periwinkle); }
.orch-next-title { font-size: 18px; font-weight: 700; }
.orch-now-sub.warn { color: #e2a33c; }
.orch-progress { max-width: 560px; margin-top: 12px; }
.orch-progress[hidden] { display: none; }
.orch-resume { margin-top: 14px; padding: 10px 20px; }
.orch-resume[hidden] { display: none; }

.orch-dock {
  position: sticky; top: 0; z-index: 12; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 10px 16px; margin-bottom: 16px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--panel);
}
.orch-dock-label { color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.1em; }
.orch-dock-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.orch-dock-hint { margin-left: auto; color: var(--text-dim); font-size: 11.5px; }
.danger-outline { border-color: #8a3a34; color: var(--danger); }
.orch-mode button.active { background: var(--panel-2); color: var(--text); }
.orch-mode[data-locked] button { opacity: 0.5; }

.orch-pop {
  position: fixed; z-index: 60; width: 280px; max-height: 340px; overflow-y: auto; padding: 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
}
.orch-pop[hidden] { display: none; }
.orch-pop input { width: 100%; margin-bottom: 6px; }
.orch-pop button { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%; text-align: left; border: none; background: none; color: var(--text); padding: 8px 10px; border-radius: 6px; font-size: 13px; cursor: pointer; }
.orch-pop button:hover { background: var(--panel-2); }
.orch-pop button span { color: var(--text-dim); font-size: 11px; font-weight: 400; }
.orch-pop .danger-text { color: var(--danger); }
.orch-pop-h { color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; padding: 8px 10px 4px; }
.orch-pop-empty { color: var(--text-dim); font-size: 12px; padding: 8px 10px; }

.orch-timeline-track { cursor: crosshair; min-height: 52px; }
.orch-block { cursor: pointer; }
.orch-playhead span { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); font-size: 9px; padding: 1px 7px; border-radius: 999px; background: var(--accent); color: #fff; white-space: nowrap; }
.orch-timeline-note { color: var(--text-dim); font-size: 12px; margin-top: 10px; }
.orch-event-check { flex: none; width: 240px; font-size: 12px; color: var(--text-dim); }
.orch-event-check.ok { color: #6fd08a; }
.orch-event-check.bad { color: #e2a33c; }
@media (max-width: 900px) { .orch-event-check { width: auto; } }
.orch-output-actions { display: flex; gap: 6px; }
.orch-log-filter { width: auto; }
.orch-log-time { width: 110px; }

/* ---- Finance: condensed navigation and the by-project overview ---- */
.fin-navrow { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 4px 0 8px; }
.fin-navrow .page-tab-switch { margin: 0; }
.fin-new { position: relative; margin-left: auto; }
.fin-new-pop { position: absolute; right: 0; top: calc(100% + 6px); z-index: 40; min-width: 220px; padding: 6px; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 30px rgba(0, 0, 0, 0.55); display: flex; flex-direction: column; }
.fin-new-pop[hidden] { display: none; }
.fin-new-pop a { padding: 8px 12px; border-radius: 6px; color: var(--text); text-decoration: none; font-size: 13px; }
.fin-new-pop a:hover { background: var(--panel-2); }
.fin-subnav { display: flex; gap: 6px; margin: 0 0 18px; }
.fin-subnav[hidden] { display: none; }
.fin-subnav a { padding: 5px 14px; border-radius: 999px; border: 1px solid var(--border); font-size: 12px; color: var(--text-dim); text-decoration: none; }
.fin-subnav a:hover { color: var(--text); }
.fin-subnav a.active { background: var(--panel-2); color: var(--text); border-color: var(--text-dim); }

.fin-periods { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0 16px; }
.fin-chip { padding: 4px 14px; border-radius: 999px; border: 1px solid var(--border); background: none; color: var(--text-dim); font-size: 12px; cursor: pointer; }
.fin-chip:hover { color: var(--text); }
.fin-chip.active { background: var(--panel-2); color: var(--text); border-color: var(--text-dim); }
.fin-custom { display: inline-flex; align-items: center; gap: 8px; }
.fin-custom[hidden] { display: none; }
.fin-custom input { width: auto; }
.fin-periods .spacer { flex: 1; }

.fin-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 16px; }
@media (max-width: 800px) { .fin-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.fin-kpi { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 18px; }
.fin-kpi-label { color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em; }
.fin-kpi-value { font-family: "Archivo", sans-serif; font-weight: 800; font-size: 26px; line-height: 1.15; margin-top: 6px; }
.fin-kpi-value.positive { color: #5fcf7a; }
.fin-kpi-value.negative { color: #ff7a70; }
.fin-kpi-sub { color: var(--text-dim); font-size: 11.5px; margin-top: 3px; min-height: 14px; }
.fin-onboarding[hidden] { display: none; }
.fin-onboarding a { margin-right: 8px; }

.fin-cols { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(260px, 1fr); gap: 16px; align-items: start; margin-bottom: 16px; }
@media (max-width: 900px) { .fin-cols { grid-template-columns: 1fr; } }
.fin-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.fin-panel-head h3 { margin: 0; }
.fin-groupby { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); margin: 0; }
.fin-group-head, .fin-group-line { display: grid; grid-template-columns: minmax(0, 1.6fr) repeat(4, minmax(64px, 1fr)); gap: 10px; align-items: baseline; }
.fin-group-head { color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; padding: 0 0 6px; border-bottom: 1px solid var(--border); }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.fin-group-row { padding: 10px 0 8px; border-bottom: 1px solid var(--border); cursor: pointer; }
.fin-group-row:hover .fin-group-name strong { color: var(--accent-strong); }
.fin-group-name { display: flex; flex-direction: column; min-width: 0; }
.fin-group-name small { color: var(--text-dim); font-size: 11px; }
.fin-group-line .neg { color: #ff7a70; }
.fin-margin.good { color: #6fd08a; }
.fin-margin.mid { color: #e2a33c; }
.fin-margin.low { color: #ff7a70; }
.fin-bar { height: 4px; border-radius: 2px; background: var(--panel-2); margin-top: 7px; overflow: hidden; }
.fin-bar span { display: block; height: 100%; border-radius: 2px; background: var(--text-dim); }
.fin-bar span.good { background: #6fd08a; }
.fin-bar span.mid { background: #e2a33c; }
.fin-bar span.low { background: #ff7a70; }
.fin-group-detail { margin-top: 10px; padding: 10px 12px; background: var(--bg); border-radius: var(--radius-sm); cursor: default; }
.fin-group-detail[hidden] { display: none; }
.fin-detail-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 700px) { .fin-detail-cols { grid-template-columns: 1fr; } }
.fin-detail-h { color: var(--text-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 6px; }
.fin-detail-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 10px; padding: 4px 0; font-size: 12.5px; color: var(--text); text-decoration: none; }

.fin-rail { display: flex; flex-direction: column; gap: 16px; }
.fin-rail .panel { margin: 0; }
.fin-count { background: var(--accent); color: #fff; border-radius: 999px; padding: 0 8px; font-size: 11px; font-weight: 700; }
.fin-count:empty { display: none; }
.fin-attn-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.fin-attn-row:last-child { border-bottom: none; }
.fin-attn-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex: none; background: #e2a33c; }
.fin-attn-row.bad .fin-attn-dot { background: #ff7a70; }
.fin-attn-text { flex: 1; min-width: 0; }
.fin-attn-text small { display: block; color: var(--text-dim); font-size: 11.5px; }
.fin-attn-act { color: var(--accent-strong); text-decoration: none; font-size: 12px; white-space: nowrap; }
.fin-kv { display: flex; justify-content: space-between; padding: 7px 0; font-size: 13px; border-bottom: 1px solid var(--border); }
.fin-kv:last-child { border-bottom: none; }
.fin-kv span { color: var(--text-dim); }
.fin-recent > summary { cursor: pointer; color: var(--text-dim); font-size: 13px; }
.fin-recent[open] > summary { margin-bottom: 10px; }
.fin-toggle { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); margin-bottom: 8px; }
.fin-toggle input { width: auto; }

/* ---- Keystone / Mesh Warp: preview with an inspector ---- */
.mesh-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.mesh-saved { font-size: 12px; color: #6fd08a; min-height: 16px; }
.mesh-layout { display: grid; grid-template-columns: minmax(0, 1fr) 250px; gap: 20px; align-items: start; }
@media (max-width: 900px) { .mesh-layout { grid-template-columns: 1fr; } }
.mesh-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.mesh-toolbar-gap { flex: 1; }
.mesh-toolbar button:disabled { opacity: 0.4; }
.mesh-keys { text-align: center; margin: 10px 0 0; }
.mesh-inspector { display: flex; flex-direction: column; gap: 14px; }
.mesh-section { padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-2); }
.mesh-label { color: var(--text-dim); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px; }
.mesh-grid-steppers { display: grid; grid-template-columns: auto 1fr; gap: 8px 12px; align-items: center; }
.mesh-grid-steppers .stepper { display: flex; align-items: center; gap: 6px; }
.mesh-grid-steppers .stepper button { width: 28px; height: 28px; padding: 0; }
.mesh-grid-steppers .stepper span { min-width: 24px; text-align: center; font-variant-numeric: tabular-nums; }
.mesh-perf { margin-top: 12px; padding: 7px 10px; border: 1px solid #6b5a1e; border-radius: var(--radius-sm); background: rgba(226, 163, 60, 0.08); color: #e2a33c; font-size: 11.5px; }
.mesh-perf[hidden] { display: none; }
.mesh-xy { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px; }
.mesh-xy label { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--text-dim); margin: 0; }
.mesh-xy input { width: 100%; padding: 5px 8px; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.mesh-row-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.mesh-row-buttons select { width: 100%; }
#keystone-preview.picture {
  background-image: var(--scenic); background-size: 100% 100%; background-color: transparent; border-width: 2px;
}


/* ---------- Network tab ---------- */
.net-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.net-head h3 { margin: 0; }
.net-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; align-items: start; margin-bottom: 12px; }
@media (max-width: 860px) { .net-cols { grid-template-columns: minmax(0, 1fr); } }
.net-card { background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
.net-card h4 { margin: 0 0 8px; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); font-weight: 600; }
.net-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.net-card-head h4 { margin: 0; }
.net-list .net-row:last-child { border-bottom: none; }
.net-hint { font-weight: 400; font-size: 11px; opacity: 0.85; }
.net-copy { background: none; border: none; padding: 0 0 0 6px; margin: 0; color: var(--text-dim); cursor: pointer; font-size: 12px; line-height: 1; }
.net-copy:hover { color: var(--text); }
#wifi-note:empty { display: none; }

.net-path { display: flex; align-items: stretch; margin-bottom: 12px; }
.path-node { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 10px 8px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; text-align: center; }
.path-node svg { color: var(--text-dim); }
.path-node b { font-size: 13px; font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.path-node span { font-size: 11px; color: var(--text-dim); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.path-link { flex: 0 0 34%; min-width: 120px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; padding: 0 8px; font-size: 11px; color: var(--text-dim); text-align: center; }
.path-link i { display: block; width: 100%; height: 0; border-top: 2px solid var(--border-strong, #5a524d); }
.path-link.good { color: #4ec27a; } .path-link.good i { border-color: #4ec27a; }
.path-link.warn { color: #e2a33c; } .path-link.warn i { border-color: #e2a33c; }
.path-link.bad { color: var(--danger); } .path-link.bad i { border-color: var(--danger); border-top-style: dashed; }
@media (max-width: 560px) { .path-link { min-width: 90px; } }

.net-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 700px) { .net-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.net-tiles:empty { display: none; }
.net-tile { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 2px; }
.net-tile p { margin: 0; font-size: 11px; color: var(--text-dim); }
.net-tile b { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.net-tile b.good { color: #4ec27a; } .net-tile b.warn { color: #e2a33c; } .net-tile b.bad { color: var(--danger); }
.net-tile span { font-size: 11px; color: var(--text-dim); }

.switch { position: relative; display: inline-block; width: 34px; height: 18px; flex-shrink: 0; cursor: pointer; }
.switch input { position: absolute; opacity: 0; inset: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch-track { position: absolute; inset: 0; border-radius: 99px; background: var(--border-strong, #5a524d); transition: background 0.15s; pointer-events: none; }
.switch-track::after { content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; transition: transform 0.15s; }
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }


/* ---------- Settings tab ---------- */
.ready-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.ready-title { font-size: 14px; font-weight: 600; }
.ready-pill { font-size: 12px; padding: 2px 10px; border-radius: 99px; border: 1px solid var(--border); }
.ready-pill.good { color: #4ec27a; border-color: rgba(78,194,122,0.4); }
.ready-pill.warn { color: #e2a33c; border-color: rgba(226,163,60,0.4); }
.ready-bar { height: 4px; border-radius: 2px; background: var(--border); margin-bottom: 10px; overflow: hidden; }
.ready-bar i { display: block; height: 100%; background: var(--accent); transition: width 0.2s; }
.ready-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 2px 18px; }
.ready-item { display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 0; min-height: 30px; }
.ready-mark { width: 16px; text-align: center; font-weight: 700; flex-shrink: 0; }
.ready-item.ok .ready-mark { color: #4ec27a; }
.ready-item.todo .ready-mark { color: #e2a33c; }
.ready-text { flex: 1; }
.ready-item.ok .ready-text { color: var(--text-dim); }

.settings-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; align-items: start; }
@media (max-width: 860px) { .settings-grid { grid-template-columns: minmax(0, 1fr); } }
.settings-list { padding-top: 2px; padding-bottom: 2px; }
.srow { border-bottom: 1px solid var(--border); }
.srow:last-child { border-bottom: none; }
.srow-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; background: none; border: none; color: var(--text); font: inherit; font-size: 13px; padding: 11px 2px; text-align: left; cursor: pointer; border-radius: 0; }
.srow-head.static { cursor: default; }
.srow-head:not(.static):hover .srow-chev { color: var(--text); }
.srow-val { display: inline-flex; align-items: center; gap: 8px; color: var(--text-dim); font-size: 12px; text-align: right; min-width: 0; }
.srow-chev { font-style: normal; transition: transform 0.15s; display: inline-block; }
.srow.open > .srow-head .srow-chev { transform: rotate(90deg); }
.srow-body { padding: 0 2px 14px; }
.srow-sub { margin: 16px 0 6px; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); font-weight: 600; }
.danger-text { color: var(--danger); }
.row-pill { font-size: 11px; padding: 1px 8px; border-radius: 99px; border: 1px solid var(--border); }
.row-pill.good { color: #4ec27a; border-color: rgba(78,194,122,0.4); }
.row-pill.bad { color: var(--danger); border-color: rgba(220,80,80,0.4); }

.pref-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pref-chip { cursor: pointer; margin: 0; }
.pref-chip input { position: absolute; opacity: 0; pointer-events: none; }
.pref-chip span { display: inline-block; font-size: 12px; padding: 4px 11px; border-radius: 99px; border: 1px solid var(--border); background: var(--panel); color: var(--text-dim); }
.pref-chip:hover span { color: var(--text); }
.pref-chip.active span { background: rgba(225,92,46,0.16); border-color: var(--accent); color: var(--text); }
.pref-chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

.seg-group { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.seg-group button { border: none; border-radius: 0; margin: 0; }
.seg-group button + button { border-left: 1px solid var(--border); }
.seg-group button.active { background: var(--accent); color: #fff; }

.out-stage { position: relative; background: var(--panel); border: 1px solid var(--border); border-radius: 6px; width: 100%; overflow: hidden; }
.out-ph { position: absolute; border: 1.5px solid var(--accent); background: rgba(225,92,46,0.16); border-radius: 3px; cursor: move; touch-action: none; }
.out-dim, .out-hint { position: absolute; bottom: 6px; font-size: 11px; color: var(--text-dim); pointer-events: none; }
.out-dim { left: 8px; } .out-hint { right: 8px; }
.out-readout { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; min-height: 4px; font-size: 11px; color: var(--text-dim); }
.out-readout:empty { display: none; }
.out-audio { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 13px; padding: 4px 0; }


/* ---------- Group About tab ---------- */
.tab-panel.gs-about { max-width: 1100px; }
.gs-banner { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.gs-title { font-size: 16px; font-weight: 700; }
.gs-title.good { color: #4ec27a; } .gs-title.warn { color: #e2a33c; } .gs-title.bad { color: var(--danger); }
.gs-sub { font-size: 12.5px; color: var(--text-dim); margin-top: 3px; }
.gs-banner-actions { display: flex; gap: 8px; }
.gs-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.gs-head h3 { margin: 0; }
.gs-head h3 .mono { font-size: 12px; font-weight: 400; color: var(--text-dim); margin-left: 8px; }
.gs-link { font-size: 12px; color: var(--accent); }
.gs-wall { position: relative; width: 100%; background: var(--panel); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.gs-tile { position: absolute; box-sizing: border-box; display: flex; flex-direction: column; justify-content: space-between; padding: 8px 10px; border: 1px solid var(--border-strong, #5a524d); background: var(--panel-2); color: var(--text); text-decoration: none; overflow: hidden; }
.gs-tile:hover { border-color: var(--accent); }
.gs-tile.offline { opacity: 0.65; }
.gs-tile-name { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; }
.gs-tile-sub { font-size: 11px; color: var(--text-dim); }
.gs-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.gs-table th { text-align: left; font-weight: 500; color: var(--text-dim); padding: 6px 8px; border-bottom: 1px solid var(--border); }
.gs-table td { padding: 9px 8px; border-bottom: 1px solid rgba(255,255,255,0.05); font-variant-numeric: tabular-nums; }
.gs-table tr:last-child td { border-bottom: none; }
.gs-table a { color: var(--text); font-weight: 500; }


/* ---------- Group Members tab ---------- */
.gm-scroll { overflow-x: auto; }
.gm-table th:last-child, .gm-table td:last-child { text-align: right; }
.gm-name a { color: var(--text); font-weight: 600; margin-right: 6px; }
.gm-also { font-size: 11px; color: var(--text-dim); margin-top: 2px; font-weight: 400; }
.gm-status { white-space: nowrap; }
.gm-mini { position: relative; width: 96px; background: var(--panel); border: 1px solid var(--border-strong, #5a524d); border-radius: 3px; }
.gm-mini i { position: absolute; box-sizing: border-box; background: rgba(225,92,46,0.22); border: 1px solid var(--accent); border-radius: 2px; }
.gm-transport { display: flex; align-items: center; gap: 8px; }
.gm-transport .transport-controls { display: inline-flex; gap: 4px; }
.gm-more .header-more-menu { display: flex; flex-direction: column; gap: 4px; min-width: 190px; text-align: left; }
.gm-more .header-more-menu a { display: block; }
.gm-more .header-more-menu button { width: 100%; text-align: left; }
.gm-add-menu { display: flex; flex-direction: column; gap: 4px; min-width: 240px; }
.gm-add-menu button { text-align: left; white-space: normal; }
.gm-add-note { color: var(--text-dim); font-size: 11px; }
.gm-name a { white-space: nowrap; }
.gm-also { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gm-status .play-status { font-size: 11px; margin-top: 2px; }
.gm-transport button { white-space: nowrap; }
.gm-mini { width: 80px; }
.gm-mini { width: 68px; }
.gm-also { max-width: 150px; }
.gm-table th, .gm-table td { padding-left: 6px; padding-right: 6px; }
.row-pill.warn { color: #e2a33c; border-color: rgba(226,163,60,0.4); }


/* The side nav is rebuilt into groups by side-nav-groups.js after the page
   loads. Hold it invisible until that has happened so the flat, ungrouped
   order never flashes and then shuffles. The animation is a safety net: if
   the script never runs, the nav still appears after a moment instead of
   staying blank. */
.side-nav:not(.nav-ready) { opacity: 0; animation: nav-fallback 0.01s linear 0.8s forwards; }
@keyframes nav-fallback { to { opacity: 1; } }


/* A page's own actions, tucked under its entry in the side nav. */
.nav-subactions { display: flex; flex-direction: column; gap: 6px; margin: 2px 0 8px 22px; padding-left: 10px; border-left: 1px solid var(--border); }
.nav-subactions button { width: 100%; margin: 0 !important; text-align: left; white-space: normal; line-height: 1.25; }
.nav-subactions .badge { margin: 0 !important; font-size: 11px; white-space: normal; }
/* The icon-only rail has no room for text buttons; expand the sidebar to use them. */
body.nav-collapsed .nav-subactions { display: none; }


/* ---------- Shows: Now playing ---------- */
#shows-now:not(:empty) { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }
.now-card { background: var(--panel); border: 1px solid rgba(225,92,46,0.5); border-radius: 10px; padding: 12px 16px; }
.now-top { display: flex; align-items: center; gap: 14px; }
.now-text { flex: 1; min-width: 0; }
.now-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
.now-title strong { font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.now-pill { flex-shrink: 0; font-size: 11px; padding: 2px 9px; border-radius: 99px; background: rgba(225,92,46,0.18); color: var(--accent); border: 1px solid rgba(225,92,46,0.4); }
.now-pill.cued { background: rgba(226,163,60,0.15); color: #e2a33c; border-color: rgba(226,163,60,0.4); }
.now-meta { margin: 3px 0 0; }
.now-loop { white-space: nowrap; }
.now-track { height: 4px; border-radius: 2px; background: var(--border); margin: 10px 0 6px; overflow: hidden; }
.now-track i { display: block; height: 100%; background: var(--accent); transition: width 1s linear; }
.now-pos { margin: 0; }
.now-card.idle { border-color: var(--border); }
.now-pill.idle { background: var(--panel-2); color: var(--text-dim); border-color: var(--border); }


/* ---------- Render Show ---------- */
.render-queued-toast { position: fixed; right: 20px; bottom: 20px; z-index: 200; max-width: 380px; display: flex; flex-direction: column; gap: 4px; padding: 12px 36px 12px 14px; background: var(--panel-2); border: 1px solid var(--accent); border-radius: 10px; font-size: 13px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); }
.render-queued-toast span { color: var(--text-dim); }
.render-queued-toast a { color: var(--accent); font-weight: 600; }
.render-queued-toast button { position: absolute; top: 6px; right: 8px; background: none; border: none; color: var(--text-dim); font-size: 18px; cursor: pointer; padding: 0 4px; }


/* Loop / Autoplay, stacked, beside the Play/Cue button. */
.play-opts { display: inline-flex; flex-direction: column; gap: 3px; margin-right: 8px; vertical-align: middle; }
.play-opts label { display: inline-flex; align-items: center; gap: 4px; margin: 0; white-space: nowrap; cursor: pointer; }


/* Orchestrator: the schedule's clock isn't the viewer's. */
.orch-tz-note { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; padding: 10px 14px; border: 1px solid rgba(226,163,60,0.5); border-radius: 8px; background: rgba(226,163,60,0.08); color: #e2a33c; font-size: 13px; }
.orch-hint { display: block; margin-top: 4px; font-size: 11.5px; color: var(--text-dim); }

/* Canvas Scan result: flat vs curved fit comparison (structured-light-calibrate.js) */
.calib-fit-compare { margin: 10px 0; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel-2); }
.calib-fit-title { margin: 0 0 8px; font-size: 13px; font-weight: 600; }
.calib-fit-row {
  display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; width: 100%; text-align: left;
  padding: 8px 10px; margin-bottom: 6px; background: var(--panel); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
}
.calib-fit-row.active { border-color: var(--accent); }
.calib-fit-row:disabled { opacity: 0.45; cursor: not-allowed; }
.calib-fit-name { font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.calib-fit-miss { font-size: 12px; color: var(--text-dim); }
.calib-fit-bar { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: var(--border); overflow: hidden; }
.calib-fit-bar i { display: block; height: 100%; background: var(--accent); }
.calib-fit-badge { font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 4px; background: rgba(58, 168, 143, 0.18); color: #3aa88f; }
.calib-fit-coverage { color: #e0a13a; }

/* Sidebar transport, expanded desktop: Play/Pause/Restart/menu as one joined
   strip, Stop as its own full-width labelled bar beneath, status under that.
   Scoped away from the phone bar (its own rules above) and the collapsed
   icon rail, which keeps the original small column of squares. */
@media (min-width: 861px) {
  body:not(.nav-collapsed) .global-transport {
    display: grid; grid-template-columns: 1fr 1fr 1fr 34px; gap: 0;
  }
  body:not(.nav-collapsed) .global-transport > button {
    width: auto; height: 46px; font-size: 17px; border-radius: 0; margin-left: -1px; grid-row: 1;
  }
  body:not(.nav-collapsed) .global-transport > button:hover,
  body:not(.nav-collapsed) .global-transport > button:focus-visible { position: relative; z-index: 1; }
  body:not(.nav-collapsed) .global-transport > #play-all-btn { margin-left: 0; border-radius: 8px 0 0 0; }
  body:not(.nav-collapsed) .global-transport > #transport-more-btn { grid-column: 4; border-radius: 0 8px 0 0; font-size: 12px; }
  body:not(.nav-collapsed) .global-transport > #stop-all-btn {
    grid-column: 1 / -1; grid-row: 2; margin: -1px 0 0; height: 40px; border-radius: 0 0 8px 8px; font-size: 15px; gap: 8px;
  }
  body:not(.nav-collapsed) .global-transport > #stop-all-btn::after { content: "Stop all"; font-size: 12.5px; font-weight: 600; }
  body:not(.nav-collapsed) .global-transport > #force-idle-btn,
  body:not(.nav-collapsed) .global-transport > #restart-app-btn { display: none; }
  body:not(.nav-collapsed) .global-transport > .nav-status { grid-column: 1 / -1; grid-row: 3; margin-top: 10px; }
}

/* Projects list: compact tiles by default (about a quarter of the old card
   area), and a one-row-per-project list view. Same markup for both — only
   these rules differ. Actions/chips/second meta line are dropped from the
   tile; the whole thumbnail and the name are the way in. */
.proj-viewtoggle { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.proj-viewtoggle button { display: flex; align-items: center; justify-content: center; width: 32px; height: 28px; padding: 0; border: none; border-radius: 0; background: none; color: var(--text-dim); cursor: pointer; }
.proj-viewtoggle button + button { border-left: 1px solid var(--border); }
.proj-viewtoggle button svg { width: 16px; height: 16px; }
.proj-viewtoggle button.active { background: var(--panel-2); color: var(--text); }
.proj-thumb-link { text-align: center; }
.proj-grid .proj-body { padding: 7px 9px 9px; gap: 1px; }
.proj-grid .proj-name, .proj-grid .proj-meta, .proj-grid .proj-next { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.proj-grid .proj-name { font-size: 12.5px; }
.proj-grid .proj-meta { font-size: 11px; }
.proj-grid .proj-next { font-size: 10.5px; margin-top: 2px; }
.proj-grid .proj-meta2, .proj-grid .proj-chips, .proj-grid .proj-actions { display: none; }
.proj-grid .proj-rename { font-size: 12.5px; width: 100%; }
.proj-grid .proj-tick { top: 5px; left: 5px; }
.proj-grid .proj-menu-btn { top: 5px; right: 5px; width: 24px; height: 22px; font-size: 14px; }
.proj-grid .proj-menu { top: 30px; right: 5px; }
.proj-grid .proj-tag, .proj-grid .proj-state { top: auto; bottom: 5px; font-size: 9.5px; padding: 1px 7px; }
.proj-grid .proj-tag { left: 5px; }
.proj-grid .proj-state, .proj-grid .proj-tag.archived { left: auto; right: 5px; }

.proj-grid.view-list { grid-template-columns: 1fr; gap: 6px; }
.view-list .proj-card { flex-direction: row; align-items: center; }
.view-list .proj-thumb { width: 116px; flex: none; }
.view-list .proj-body { flex: 1; min-width: 0; text-align: left; padding: 8px 14px; display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 2px 14px; }
.view-list .proj-name { flex: 1 0 100%; font-size: 14px; }
.view-list .proj-meta2, .view-list .proj-chips { display: flex; margin-top: 0; }
.view-list .proj-chips { gap: 5px; }
.view-list .proj-next { margin-top: 0; font-size: 11.5px; }
.view-list .proj-actions { display: flex; margin: 0 0 0 auto; gap: 6px; }
.view-list .proj-btn { padding: 3px 10px; }
@media (max-width: 600px) {
  .view-list .proj-thumb { width: 92px; }
  .view-list .proj-meta2, .view-list .proj-chips, .view-list .proj-actions { display: none; }
}

/* Dashboard storage rows: expandable per-row cleanup (dashboard.js) */
.dash-store-row.clickable { cursor: pointer; border-radius: 6px; }
.dash-store-row.clickable:hover { background: var(--panel-2); }
.dash-store-caret { display: inline-block; width: 12px; color: var(--text-dim); font-size: 10px; }
.dash-store-panel { margin: 4px 0 8px 12px; padding: 4px 10px; border-left: 2px solid var(--border); font-size: 12px; }
.dash-clean-row, .dash-clean-item { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.dash-clean-row:last-child, .dash-clean-item:last-child { border-bottom: none; }
.dash-clean-row.dim { color: var(--text-dim); }
.dash-clean-row small, .dash-clean-item small { display: block; color: var(--text-dim); font-size: 11px; }
.dash-clean-item > span:first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.dash-clean-badge { font-size: 10.5px; padding: 1px 8px; border-radius: 999px; background: var(--panel-2); color: #e2a33c; white-space: nowrap; }
.dash-clean-row select { width: auto; margin: 0 2px; }

/* The kill switch sits on a red button, where the accent-coloured hold fill was
   the same colour as its own background and so invisible. */
#restart-app-btn .reboot-hold-fill { background: rgba(255, 255, 255, 0.7); opacity: 1; }

/* Settings → Idle screen */
.idle-layout { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 22px; align-items: start; }
@media (max-width: 900px) { .idle-layout { grid-template-columns: 1fr; } }
.idle-preview-wrap { position: sticky; top: 12px; }
.idle-preview { position: relative; aspect-ratio: 16 / 9; background: #000; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.idle-preview-inner { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; transform-origin: 0 0; }
.idle-preview-tag { text-align: center; margin-top: 8px; font-size: 12px; color: var(--text-dim); min-height: 16px; }
.idle-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 12px; }
.idle-label { width: 96px; flex: none; margin: 0; font-size: 12.5px; color: var(--text-dim); }
.idle-row input[type="range"] { flex: 1; min-width: 120px; margin: 0; }
.idle-row input[type="text"] { flex: 1; min-width: 160px; }
.idle-val { width: 56px; text-align: right; font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.idle-check { gap: 8px; font-size: 13px; }
.idle-check input { width: auto; margin: 0; }
.idle-swatches { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.idle-swatch { position: relative; display: inline-flex; }
.idle-swatch input[type="color"] { width: 34px; height: 34px; padding: 2px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel-2); cursor: pointer; }
.idle-swatch-x { position: absolute; top: -6px; right: -6px; width: 16px; height: 16px; padding: 0; border-radius: 50%; border: 1px solid var(--border); background: var(--panel); color: var(--text-dim); font-size: 11px; line-height: 1; cursor: pointer; }
.idle-swatch-x:disabled { display: none; }
.idle-warn { color: #e2a33c; margin: 0 0 12px; }
.idle-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }

/* Idle screen editor v2: accordion, scope bar, preview tools */
.idle-topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.idle-topright { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.idle-topright input[type="search"] { width: 180px; }
.idle-wall, .idle-load { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.idle-wall select { width: auto; min-width: 130px; }
.idle-meter { display: inline-flex; gap: 3px; }
.idle-meter i { width: 16px; height: 6px; border-radius: 2px; background: var(--panel-2); }
.idle-meter i.on-ok { background: #6fd08a; }
.idle-meter i.on-warn { background: #e2a33c; }
.idle-meter i.on-bad { background: #e05252; }
.idle-controls { min-width: 0; }
.idle-sec { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 8px; background: var(--panel); }
.idle-sec > summary { cursor: pointer; padding: 9px 12px; display: flex; justify-content: space-between; align-items: center; list-style: none; font-weight: 600; font-size: 13px; }
.idle-sec > summary::-webkit-details-marker { display: none; }
.idle-sec > summary .idle-count { color: var(--text-dim); font-weight: 400; font-size: 11.5px; }
.idle-sec > summary::after { content: "\25B8"; color: var(--text-dim); margin-left: 8px; }
.idle-sec[open] > summary::after { content: "\25BE"; }
.idle-sec-body { padding: 4px 12px 10px; }
.idle-sec[hidden] { display: none; }
.idle-row[hidden] { display: none; }
.idle-row .mode-group { flex-wrap: wrap; }
.idle-row input[type="color"] { width: 34px; height: 30px; padding: 2px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-2); }
.idle-row input[type="time"] { width: auto; }
.idle-looks { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 0 0 12px; }
.idle-look { border: 1px solid var(--border); border-radius: 8px; padding: 8px 6px; text-align: center; background: var(--panel-2); color: var(--text-dim); cursor: pointer; font-size: 12px; }
.idle-look.active { border-color: var(--accent); color: var(--text); }
.idle-look i { display: inline-block; width: 16px; height: 16px; border-radius: 4px; margin: 0 2px 4px; }
.idle-pal-row { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; }
.idle-pal-row input[type="range"] { flex: 1; min-width: 80px; margin: 0; }
.idle-pal-share { width: 40px; text-align: right; font-size: 12px; color: var(--text-dim); }
.idle-pal-x { width: 22px; height: 22px; padding: 0; border-radius: 50%; border: 1px solid var(--border); background: var(--panel); color: var(--text-dim); cursor: pointer; }
.idle-pal-x:disabled { opacity: 0.3; cursor: default; }
.idle-note { font-size: 11.5px; color: var(--text-dim); margin: 2px 0 8px; }

/* Mesh editor: on-screen nudge pad (for phones and tablets). Stacked under
   the Columns/Rows steppers rather than beside them — the inspector column
   is 250px (.mesh-layout), which after .mesh-section's own padding leaves
   ~222px, less than the ~284px the two need side by side; squeezed into a
   flex row, the steppers' fixed-width +/- buttons overflowed straight
   through the nudge pad instead of wrapping or shrinking. */
.mesh-grid-row { display: flex; flex-direction: column; gap: 10px; }
.mesh-grid-row .mesh-grid-steppers { gap: 8px 8px; }
.mesh-grid-row .stepper span { min-width: 26px; }
.mesh-nudge { align-self: center; }
.mesh-nudge-label { font-size: 10.5px; color: var(--text-dim); text-align: center; margin-bottom: 4px; letter-spacing: 0.04em; }
.mesh-nudge-pad { display: grid; grid-template-columns: repeat(3, 34px); grid-template-rows: repeat(3, 34px); gap: 3px; }
.mesh-nudge-pad button { padding: 0; width: 34px; height: 34px; font-size: 12px; line-height: 1; display: flex; align-items: center; justify-content: center;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: manipulation; }
.mesh-nudge-pad .mesh-nudge-step { font-size: 10px; font-weight: 600; color: var(--text-dim); }

/* Pairing code chip on a device waiting to be linked */
.pair-chip { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 12px; letter-spacing: 0.2em; padding: 2px 9px; border-radius: 6px; background: rgba(226, 163, 60, 0.14); color: #e2a33c; }

/* Projector calibration history */
.calib-history-list { display: flex; flex-direction: column; }
.calib-hist-row { display: flex; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.calib-hist-row:last-child { border-bottom: none; }
.calib-hist-thumb { width: 72px; height: 42px; flex: none; border: 1px solid var(--border); border-radius: 6px; background: #000; }
.calib-hist-main { flex: 1; min-width: 160px; }
.calib-hist-title { font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.calib-hist-meta { font-size: 11.5px; color: var(--text-dim); margin-top: 2px; }
.calib-hist-badge { font-size: 10.5px; font-weight: 600; padding: 1px 8px; border-radius: 999px; background: var(--panel-2); color: var(--text-dim); }
.calib-hist-badge.now { background: rgba(58, 168, 143, 0.18); color: #3aa88f; }
.calib-hist-badge.pin { background: rgba(226, 163, 60, 0.16); color: #e2a33c; }
.calib-hist-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.calib-hist-more { margin-top: 8px; }

/* Add a device: prepare an SD card */
.fb-form { display: flex; flex-direction: column; gap: 10px; }
.fb-row { display: flex; gap: 10px; flex-wrap: wrap; }
.fb-row label { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--text-dim); margin: 0; flex: 1; min-width: 150px; }
.fb-row input { width: 100%; }
.fb-adv { border-top: 1px solid var(--border); padding-top: 8px; }
.fb-adv > summary { cursor: pointer; font-size: 12px; color: var(--text-dim); margin-bottom: 8px; }

/* Live performance panel on the projector page */
.perf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; margin: 8px 0; }
.perf-cell { display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; border: 1px solid var(--border); border-left-width: 3px; border-radius: 6px; background: var(--surface, transparent); }
.perf-cell .perf-label { font-size: 11px; color: var(--text-dim); }
.perf-cell .perf-value { font-family: "JetBrains Mono", monospace; font-size: 15px; font-weight: 600; }
.perf-cell.ok { border-left-color: #4fbf7a; }
.perf-cell.warn { border-left-color: #e2a33c; }
.perf-cell.bad { border-left-color: #e05252; }

/* Add a device — method cards (see templates/projectors.html) */
.method-cards { display: flex; gap: 10px; margin-bottom: 4px; }
.method-card {
  flex: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  text-align: left; padding: 12px 14px; background: var(--panel-2); border: 1.5px solid var(--border);
  border-radius: var(--radius-sm); cursor: pointer; font-family: inherit; color: var(--text);
}
.method-card:hover { border-color: var(--text-dim); }
.method-card.active { border-color: var(--accent); background: rgba(235, 73, 39, 0.08); }
.method-card-icon {
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); background: var(--panel); border: 1px solid var(--border); color: var(--accent);
}
.method-card.active .method-card-icon { background: var(--accent); color: #fff; border-color: var(--accent); }
.method-card-icon svg { width: 14px; height: 14px; }
.method-card-title { font-size: 12.5px; font-weight: 700; color: var(--text); }
.method-card-desc { font-size: 11px; color: var(--text-dim); line-height: 1.4; }
.method-panel { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }

/* Per-device file management, expandable from the Device Media Cache panel
   in Settings (see loadMediaCacheDevices/loadDeviceFiles in settings.js). */
.cache-files-toggle { margin-top: 8px; }
.cache-files-toggle summary {
  cursor: pointer; font-size: 12px; color: var(--text-dim); list-style: none;
}
.cache-files-toggle summary:hover { color: var(--text); }
.cache-files-toggle summary::-webkit-details-marker { display: none; }
.cache-files-toggle summary::before { content: "▸ "; }
.cache-files-toggle[open] summary::before { content: "▾ "; }
.cache-file-row {
  display: flex; align-items: center; gap: 10px; padding: 7px 0;
  border-top: 1px solid var(--border);
}

/* ---- Per-user UI preferences (see role-nav.js's applyUiPrefs) ---- */

/* Density: a lighter touch than a full app-wide retrofit — scoped to the
   two most content-dense, most-scanned lists (the Projectors device rows
   and the Projects grid cards), which is where a tighter view actually
   earns back screen space. */
body.density-compact .device-row { padding: 8px 12px; gap: 6px 12px; }
body.density-compact .device-row-icon { width: 32px; height: 32px; }
body.density-compact .device-row-icon svg { width: 18px; height: 18px; }
body.density-compact .device-row-name { font-size: 13px; }
body.density-compact .device-row-body { gap: 1px; min-width: 120px; }
body.density-compact .project-card:hover { transform: none; }
body.density-compact .project-card .body { padding: 8px 10px; }

/* Dashboard widget visibility */
body.hide-widget-server #dash-server-panel { display: none; }
body.hide-widget-activity #dash-activity-panel { display: none; }
body.hide-widget-automation #dash-orch-panel { display: none; }

/* Projector-row stat visibility (see data-*-for spans in projectors.js).
   Each stat's own preceding "·" separator is a plain sibling span with no
   attribute of its own to target directly — :has() reaches it from the
   stat that follows it instead, so hiding a stat doesn't leave a dangling
   "· ·" behind where it used to sit. */
body.hide-row-conn [data-conn-for],
body.hide-row-conn .status-sep:has(+ [data-conn-for]) { display: none; }
body.hide-row-temp [data-temp-for],
body.hide-row-temp .status-sep:has(+ [data-temp-for]) { display: none; }
body.hide-row-ram [data-ram-for],
body.hide-row-ram .status-sep:has(+ [data-ram-for]) { display: none; }
body.hide-row-cpu [data-cpu-for],
body.hide-row-cpu .status-sep:has(+ [data-cpu-for]) { display: none; }

/* ---- Settings: Personalization panel ---- */
.pz-row { display: flex; align-items: center; gap: 16px; margin: 10px 0; }
.pz-label { font-size: 13px; color: var(--text-dim); min-width: 110px; }
.pz-swatches { display: flex; gap: 8px; }
.pz-swatch {
  width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent;
  cursor: pointer; padding: 0;
}
.pz-swatch.active { border-color: var(--text); box-shadow: 0 0 0 2px var(--panel), 0 0 0 3px var(--text); }
.pz-checks { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.pz-checks label { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); cursor: pointer; }
.pz-checks input { width: auto; margin: 0; }

/* ---- Keyboard shortcuts (shortcuts.js): the "?" panel and the Cmd/Ctrl+K
   page jumper. Both reuse the confirm-overlay chrome so they look and close
   like every other dialog in the app. ---- */
.sc-box { max-width: 760px; max-height: calc(100vh - 40px); overflow-y: auto; padding: 20px 24px 24px; }
.sc-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.sc-head h3 { margin: 0; font-size: 15px; font-weight: 800; color: #fff; }
.sc-close { font-size: 16px; line-height: 1; padding: 4px 9px; }
.sc-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px; }
.sc-cols h4 {
  margin: 14px 0 4px; font-size: 10px; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-dim);
}
.sc-cols h4:first-child { margin-top: 0; }
.sc-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 7px 2px; border-bottom: 1px solid var(--border); font-size: 12.5px; color: var(--text);
}
.sc-row:last-child { border-bottom: none; }
.sc-keys { display: flex; align-items: center; gap: 4px; flex: none; color: var(--text-dim); font-size: 11.5px; }
.sc-overlay kbd {
  font-family: inherit; font-size: 11px; font-weight: 700; color: var(--text);
  background: var(--panel-2); border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: 3px; padding: 2px 7px; min-width: 22px; text-align: center;
}
@media (max-width: 640px) { .sc-cols { grid-template-columns: minmax(0, 1fr); gap: 0; } }

.sc-palette-overlay { align-items: flex-start; padding-top: 14vh; }
.sc-palette { max-width: 440px; padding: 0; overflow: hidden; }
.sc-palette-input {
  width: 100%; border: none; border-bottom: 1px solid var(--border); border-radius: 0;
  background: transparent; padding: 14px 16px; font-size: 14px;
}
.sc-palette-input:focus { outline: none; border-color: var(--accent); }
.sc-palette-list { max-height: 50vh; overflow-y: auto; padding: 6px; }
.sc-palette-item {
  display: flex; align-items: center; gap: 12px; padding: 9px 10px; border-radius: var(--radius-sm);
  color: var(--text-dim); font-size: 13px; font-weight: 700; cursor: pointer;
}
.sc-palette-item svg { width: 18px; height: 18px; stroke: currentColor; flex-shrink: 0; }
.sc-palette-item:hover { background: var(--panel-2); color: var(--text); }
.sc-palette-item.active { background: rgba(225, 92, 46, 0.16); color: var(--text); }
.sc-palette-item.active svg { stroke: var(--accent); }
.sc-palette-empty { padding: 12px 10px; font-size: 12.5px; color: var(--text-dim); }

/* ---- Personal side-nav layout (side-nav-groups.js + nav-editor.js) ---- */
.nav-pinned > .nav-group-head { cursor: default; }
.nav-pinned > .nav-group-head:hover { color: var(--text-dim); }
.side-nav .nav-group.nav-more { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--border); }
.nav-more-count { margin-left: auto; letter-spacing: 0; }
.nav-more-dots { display: none; }
/* Flat list: same order, no group headings — only More keeps its fold. */
.side-nav.nav-flat .nav-group:not(.nav-more) > .nav-group-head { display: none; }
.side-nav.nav-flat .nav-group + .nav-group:not(.nav-more) { margin-top: 0; }
.side-nav.nav-flat .nav-pinned:not([hidden]) { padding-bottom: 6px; margin-bottom: 4px; border-bottom: 1px solid var(--border); }

.nav-foot { display: flex; align-items: center; justify-content: space-between; gap: 4px; margin-top: 10px; }
.nav-foot .nav-collapse-btn { margin-top: 0; }
.nav-edit-btn {
  display: flex; align-items: center; gap: 6px; padding: 6px 8px; margin-left: auto;
  background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 12px; font-weight: 600;
  border-radius: var(--radius-sm);
}
.nav-edit-btn:hover { color: var(--text); background: var(--panel-2); border: none; }
.nav-edit-btn svg { width: 13px; height: 13px; stroke: currentColor; }

@media (min-width: 861px) {
  body.nav-collapsed .nav-pinned > .nav-group-head { display: none; }
  body.nav-collapsed .side-nav .nav-group.nav-more { border-top: none; padding-top: 0; margin-top: 6px; }
  /* In the rail, More is a "···" button that still folds — the rule above
     that forces every folded group open there would otherwise undo tucking. */
  body.nav-collapsed .nav-more > .nav-group-head {
    height: auto; margin: 0; padding: 8px 0; justify-content: center; cursor: pointer; position: relative;
  }
  body.nav-collapsed .nav-more .nav-more-dots { display: inline; font-size: 15px; letter-spacing: 1px; }
  body.nav-collapsed .nav-more .nav-more-count { display: none; }
  body.nav-collapsed .nav-more .nav-group-alert:not([hidden]) { display: block; position: absolute; top: 6px; right: 12px; }
  body.nav-collapsed .side-nav .nav-group.nav-more.collapsed .nav-group-body { display: none; }
  body.nav-collapsed .nav-foot { margin-top: auto; flex-direction: column; }
  body.nav-collapsed .nav-foot .nav-collapse-btn { margin-top: 0; }
  body.nav-collapsed .nav-edit-btn { margin-left: 0; }
  body.nav-collapsed .nav-edit-label { display: none; }
}

/* The editor sits to the right of the menu, not over it, so the menu can
   act as its live preview. */
@media (min-width: 861px) {
  .nve-overlay { left: var(--side-nav-w); background: rgba(8, 7, 8, 0.45); }
}
.nve-box { max-width: 680px; max-height: calc(100vh - 40px); overflow-y: auto; padding: 20px 22px 18px; }
.nve-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
.nve-top h3 { margin: 0 0 4px; font-size: 15px; font-weight: 800; color: #fff; }
.nve-note { margin: 0; font-size: 12px; color: var(--text-dim); line-height: 1.5; }
.nve-controls { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 14px 24px; margin-bottom: 14px; }
.nve-ctl-lbl { font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 6px; }
.nve-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.nve-preset.on { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }
.nve-custom { font-size: 12px; font-weight: 600; padding: 6px 12px; border: 1px dashed var(--accent); border-radius: var(--radius-sm); }
.nve-custom[hidden] { display: none; }
.nve-style { display: flex; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 2px; background: var(--bg); }
.nve-style button { border: none; background: transparent; color: var(--text-dim); padding: 5px 11px; }
.nve-style button:hover { color: var(--text); border: none; }
.nve-style button.on { background: var(--accent); color: #fff; font-weight: 700; }
.nve-body { display: flex; flex-direction: column; gap: 10px; }
.nve-zone { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 10px 10px; }
.nve-zone-tucked { border-style: dashed; }
.nve-zone-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; padding: 0 2px; }
.nve-zone-title { flex: none; font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text); }
.nve-zone-sub { flex: 1; font-size: 11.5px; color: var(--text-dim); line-height: 1.45; }
.nve-fold { flex: none; margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--text-dim); cursor: pointer; }
.nve-fold input { margin: 0; accent-color: var(--accent); }
.nve-list { display: flex; flex-direction: column; gap: 4px; min-height: 34px; border-radius: var(--radius-sm); }
.nve-list.drop-target { outline: 1px dashed var(--accent); outline-offset: 2px; }
.nve-row {
  position: relative; display: flex; align-items: center; gap: 10px; min-height: 36px; padding: 0 4px 0 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: grab;
}
.nve-row:hover { border-color: #4a433d; }
.nve-row.dragging { opacity: 0.3; }
.nve-row.drop-before::before, .nve-row.drop-after::after {
  content: ""; position: absolute; left: -1px; right: -1px; height: 2px; background: var(--accent);
}
.nve-row.drop-before::before { top: -4px; }
.nve-row.drop-after::after { bottom: -4px; }
.nve-row.locked { cursor: default; border-style: dashed; }
.nve-row.locked .nve-label, .nve-row.locked .nve-icon, .nve-row.locked .nve-grip { color: var(--text-dim); opacity: 0.6; }
.nve-grip { width: 12px; flex: none; color: var(--text-dim); font-size: 13px; user-select: none; }
.nve-icon { display: flex; width: 18px; height: 18px; flex: none; color: var(--text); pointer-events: none; }
.nve-icon svg { width: 100%; height: 100%; stroke: currentColor; }
.nve-meta { display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; }
.nve-label { font-size: 13px; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nve-tag {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap;
  color: var(--accent-strong); border: 1px solid var(--accent); padding: 1px 6px; border-radius: var(--radius-sm);
}
.nve-kbd {
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 10px; color: var(--text-dim);
  border: 1px solid var(--border); border-bottom-width: 2px; padding: 0 5px; border-radius: 2px;
}
.nve-actions { display: flex; align-items: center; gap: 2px; margin-left: auto; flex: none; }
.nve-btn {
  width: 28px; height: 28px; padding: 0; display: flex; align-items: center; justify-content: center;
  background: none; border: 1px solid transparent; color: #57514a;
}
.nve-btn:hover { color: var(--text); background: var(--panel-2); border-color: var(--border); }
.nve-btn svg { width: 16px; height: 16px; }
.nve-btn.nve-home.on { color: var(--accent-strong); }
.nve-btn.nve-star.on { color: #e2a33c; }
.nve-btn.nve-star.on svg { fill: currentColor; }
.nve-btn.nve-badge.on { color: var(--text); }
.nve-btn.nve-tuck.on { color: var(--periwinkle); }
.nve-spacer { width: 28px; height: 28px; flex: none; }
.nve-lock { display: flex; align-items: center; gap: 5px; padding-right: 6px; font-size: 11px; color: var(--text-dim); }
.nve-lock svg { width: 12px; height: 12px; }
.nve-empty { display: flex; align-items: center; min-height: 34px; padding: 0 12px; font-size: 12px; color: var(--text-dim); border: 1px dashed var(--border); border-radius: var(--radius-sm); }
.nve-status { margin: 12px 0 0; min-height: 1em; font-size: 12px; color: var(--text-dim); }
.nve-status.err { color: var(--danger); }

/* Display rotation control on a projector's Paired device panel. */
.device-rotation { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.device-rotation-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 6px; }

/* ---- New-canvas-size dialog (canvas-size-dialog.js): a layout picker plus
   a live to-scale diagram, built from a group's real projectors. ---- */
.ncs-box { max-width: 980px; width: 100%; max-height: calc(100vh - 40px); overflow-y: auto; padding: 20px 22px 18px; }
/* The same box, embedded in a page instead of popped up over it — see the
   Canvas tab's own "Resize & arrange" section (mountCanvasSizePicker). No
   viewport-height clamp (it's not fighting a modal's own scroll), and the
   .confirm-box look it otherwise borrows (background/border/shadow)
   recreated plainly instead, since .confirm-box itself is modal chrome. */
.ncs-inline { max-height: none; overflow: visible; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }
.ncs-head-row { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.ncs-title { margin: 0; font-size: 15px; font-weight: 700; color: var(--text); }
.ncs-group { font-size: 12px; color: var(--text-dim); }
.ncs-sec-lbl { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); }

.ncs-lay { margin-top: 16px; display: flex; flex-direction: column; gap: 8px; }
.ncs-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.ncs-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 10px;
  background: var(--panel-2); border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  font-weight: 400; text-align: left;
}
.ncs-card:hover:not(:disabled) { border-color: var(--accent); }
.ncs-card.on { border-color: var(--accent); background: rgba(225, 92, 46, 0.1); }
.ncs-card:disabled { opacity: 0.45; cursor: not-allowed; }
.ncs-card-mini { position: relative; background: var(--bg); border: 1px solid var(--border); flex: none; }
.ncs-mini-t { position: absolute; background: var(--periwinkle); opacity: 0.7; border: 1px solid rgba(0, 0, 0, 0.4); box-sizing: border-box; }
.ncs-card-name { font-size: 12.5px; font-weight: 700; color: var(--text); }
.ncs-card-size { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 11px; color: var(--text-dim); }
.ncs-cols { display: flex; align-items: center; gap: 10px; }

.ncs-stage-wrap { margin-top: 12px; }
.ncs-stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: visible;
}
.ncs-arr { position: relative; }
.ncs-tile {
  position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  background: var(--panel-2); border: 1.5px solid var(--border); box-sizing: border-box; cursor: pointer; text-align: center; padding: 4px;
}
.ncs-tile:hover { border-color: var(--accent-strong); }
.ncs-tile.sel { border-color: var(--accent); background: rgba(225, 92, 46, 0.12); z-index: 2; }
.ncs-tile.warn { border-style: dashed; border-color: #e2a33c; }
.ncs-tile-name { font-size: 12px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.ncs-tile-size { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 11px; color: var(--text-dim); }
.ncs-tile.xs .ncs-tile-name { font-size: 10px; }

.ncs-tb {
  position: absolute; z-index: 5; display: flex; align-items: center; gap: 6px; padding: 6px 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
.ncs-tb-name { flex: 1; min-width: 0; font-size: 12px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ncs-tb-btn, .ncs-tb-chip {
  flex: none; display: flex; align-items: center; justify-content: center; gap: 4px; height: 30px; padding: 0 8px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text);
}
.ncs-tb-btn { width: 30px; padding: 0; }
.ncs-tb-btn:hover:not(:disabled), .ncs-tb-chip:hover { border-color: var(--accent); }
.ncs-tb-btn svg, .ncs-tb-chip svg { width: 15px; height: 15px; }
.ncs-tb-num { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-dim); }
.ncs-tb-num input { width: 52px; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 5px 6px; text-align: center; }
.ncs-tb-x { color: var(--text-dim); }

.ncs-add {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--panel-2); border: 1.5px dashed var(--border); border-radius: var(--radius-sm); color: var(--text-dim);
  font-weight: 600; font-size: 12px;
}
.ncs-add svg { width: 14px; height: 14px; flex: none; }
.ncs-add:hover { border-color: var(--accent); color: var(--text); }
.ncs-add-r { position: absolute; right: -46px; top: 0; bottom: 0; width: 38px; flex-direction: column; writing-mode: vertical-rl; }
.ncs-add-b { width: 100%; height: 36px; margin-top: 8px; }

.ncs-bulk { margin-top: 16px; display: flex; align-items: flex-start; flex-wrap: wrap; gap: 24px; row-gap: 10px; }
.ncs-bulk-grp { display: flex; align-items: center; gap: 8px; }
.ncs-mixed { min-width: 42px; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--periwinkle); }
.ncs-mixed.off { visibility: hidden; }
.ncs-bulk-grp .mode-group { border-radius: var(--radius-sm); }
.ncs-bulk-grp .mode-group button { min-height: 34px; }

.ncs-or-list { flex-basis: 100%; display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.ncs-or-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 30px; }
.ncs-or-name { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.seg-sm button { padding: 0 9px; min-height: 30px; }
.seg-sm svg { width: 14px; height: 14px; }

.ncs-sum { margin-top: 16px; display: flex; flex-direction: column; gap: 6px; }
.ncs-sum-line { margin: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.ncs-sum-size { font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 17px; font-weight: 700; color: var(--text); }
.ncs-sum-dim { font-size: 12.5px; color: var(--text-dim); }
.ncs-note { margin: 0; display: flex; align-items: flex-start; gap: 6px; font-size: 12px; color: var(--text-dim); line-height: 1.5; }
.ncs-note.warn { color: #e2a33c; border: 1px solid #6b5a1e; background: rgba(226, 163, 60, 0.06); border-radius: var(--radius-sm); padding: 5px 9px; }
.ncs-note svg { width: 14px; height: 14px; flex: none; margin-top: 2px; }
.ncs-place { display: flex; flex-direction: column; gap: 2px; font-size: 12.5px; font-weight: 600; color: var(--text); cursor: pointer; }
.ncs-place-row { display: flex; align-items: center; gap: 7px; }
.ncs-place input { margin: 0; accent-color: var(--accent); }
.ncs-place .hint-text { margin: 0 0 0 21px; font-weight: 400; }

@media (max-width: 860px) {
  .ncs-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ncs-add-r { display: none; }
}
.ncs-card-tag {
  display: inline-block; margin-left: 5px; font-size: 9px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent-strong); border: 1px solid var(--accent); border-radius: var(--radius-sm); padding: 1px 5px; vertical-align: middle;
}
.ncs-box .step { display: flex; align-items: center; gap: 0; border: 1.5px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.ncs-box .step-btn {
  display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0;
  background: var(--panel-2); border: none; border-radius: 0; color: var(--text);
}
.ncs-box .step-btn:hover:not(:disabled) { border-color: transparent; background: rgba(225, 92, 46, 0.16); color: var(--accent-strong); }
.ncs-box .step-btn svg { width: 14px; height: 14px; }
.ncs-box .step-val { min-width: 28px; text-align: center; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 13px; font-weight: 700; color: var(--text); }

/* "Current" size, next to the dialog title — the canvas size actually set,
   visible before anything here is changed. */
.ncs-current { display: flex; align-items: center; gap: 6px; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 12.5px; color: var(--text-dim); }

/* The info line above the diagram: a hover preview of what an Add button
   would do, a leftover note from the last action, or the plain click hint. */
.ncs-info { margin: 10px 0 0; font-size: 12.5px; color: var(--text-dim); min-height: 1.5em; }
.ncs-info.hover { color: var(--accent-strong); font-weight: 600; }
.ncs-info.note { color: var(--periwinkle); }

/* A tile too small for its own name/size falls back to a lettered badge
   (see .ncs-key below, which maps each letter back to its tile). */
.ncs-tile-num {
  position: absolute; top: 3px; left: 3px; display: flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 10px; font-weight: 700;
  color: var(--accent-strong); border: 1px solid rgba(225, 92, 46, 0.55); border-radius: var(--radius-sm);
}
.ncs-key { list-style: none; margin: 8px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 4px 8px; }
.ncs-key-item {
  display: flex; align-items: center; gap: 7px; width: 100%; min-width: 0; height: 32px; padding: 0 8px;
  background: transparent; border: 1.5px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; text-align: left;
}
.ncs-key-item:hover { border-color: var(--accent-strong); }
.ncs-key-item.on { border-color: var(--accent); background: rgba(225, 92, 46, 0.1); }
.ncs-key-num {
  flex: none; display: flex; align-items: center; justify-content: center; width: 16px; height: 16px;
  font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 10px; font-weight: 700;
  color: var(--accent-strong); border: 1px solid rgba(225, 92, 46, 0.55); border-radius: var(--radius-sm);
}
.ncs-key-name { min-width: 0; font-size: 12px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ncs-key-size { flex: none; margin-left: auto; font-family: "JetBrains Mono", ui-monospace, monospace; font-size: 11px; color: var(--text-dim); }

/* A freshly-added tile gets a brief highlight so it's easy to spot among
   several, instead of needing to be hunted for by its name alone. */
.ncs-tile.fresh { animation: ncs-tile-in 0.9s ease-out; }
@keyframes ncs-tile-in {
  from { border-color: var(--accent-strong); box-shadow: inset 0 0 0 400px rgba(225, 92, 46, 0.22); }
}

/* A brief confirmation after Create, on the page behind the dialog (which is
   already closed by the time this shows) — see showCanvasSizeToast. */
.ncs-toast {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%) translateY(12px);
  display: flex; align-items: center; gap: 10px; padding: 10px 16px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6); font-size: 13px; color: var(--text); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.2s, transform 0.2s; z-index: 200;
}
.ncs-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.ncs-toast svg { width: 16px; height: 16px; flex: none; color: var(--periwinkle); }

/* Archive-on-own-camera control (Paired device panel), same spacing as
   .device-rotation right above it. */
.device-archive { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }

/* ---------- Archive health (Archive page sidebar + checklist) ---------- */
.archive-exhibition-label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-dim); margin: 0 0 8px; cursor: pointer; }
/* The Archive's side panel (templates/archive.html): labelled sections, switches, list rows. */
.arc-panel { gap: 0; }
.arc-lab { font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin: 14px 0 6px; }
.arc-lab:first-child { margin-top: 2px; }
.arc-rowlink { display: flex !important; align-items: center; justify-content: space-between; gap: 8px; width: 100%; background: none !important; border: none !important; border-top: 1px solid var(--border) !important; border-radius: 0 !important; padding: 8px 2px !important; font-size: 12.5px; color: var(--text); cursor: pointer; }
.arc-rowlink:hover { color: var(--accent); }
.arc-rowlink .rl-ic { width: 14px; height: 14px; color: var(--text-dim); flex: none; }
.arc-rowlink.arc-hot, .arc-rowlink.arc-hot .rl-ic { color: var(--accent); }
.arc-switch, .arc-input { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; color: var(--text); margin: 0 0 8px; cursor: pointer; }
.arc-switch small { color: var(--text-dim); font-size: 11px; margin-left: 4px; }
.arc-input select { max-width: 150px; font-size: 12px; padding: 4px 6px; }
.arc-switch input[type="checkbox"] { appearance: none; -webkit-appearance: none; flex: none; width: 30px; height: 17px; border-radius: 9px; background: var(--border); border: none; position: relative; cursor: pointer; transition: background .12s; margin: 0; padding: 0; }
.arc-switch input[type="checkbox"]::after { content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: #bdb7b3; transition: left .12s; }
.arc-switch input[type="checkbox"]:checked { background: var(--accent); }
.arc-switch input[type="checkbox"]:checked::after { left: 15px; background: #fff; }
.arc-lock-note { font-size: 11.5px; color: #e2a33c; background: rgba(226, 163, 60, 0.1); border: 1px solid rgba(226, 163, 60, 0.4); padding: 6px 8px; margin: 0 0 8px; }
.arc-rowlink:disabled, .arc-switch input:disabled, .arc-input select:disabled { opacity: .45; cursor: not-allowed; }
.arc-rowlink:disabled:hover { color: var(--text); }
.arc-input[hidden] { display: none; }
.role-locked { opacity: .45; cursor: not-allowed; }
/* The show desk */
.dk-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; }
.dk-big { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 0 0 18px; }
.dk-btn { font-size: 16px; font-weight: 600; padding: 18px 12px; background: var(--panel-2, #151313); border: 1px solid var(--border); color: var(--text); cursor: pointer; }
.dk-btn:hover:not(:disabled) { border-color: var(--accent); }
.dk-btn.go { background: var(--accent); border-color: var(--accent); color: #1a0b06; }
.dk-btn.stop { border-color: #e05252; color: #f0a0a0; }
.dk-h { margin: 0 0 8px; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim); }
.dk-walls { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 10px; margin: 0 0 18px; }
.dk-wall { border: 1px solid var(--border); border-left: 4px solid var(--border); padding: 10px 12px; background: var(--panel-2, #151313); }
.dk-wall.ok { border-left-color: #5fcf7a; } .dk-wall.warn { border-left-color: #e2a33c; } .dk-wall.bad { border-left-color: #e05252; }
.dk-wall-h { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; }
.dk-state { margin-left: auto; font-size: 12px; color: var(--text-dim); }
.dk-dot { width: 9px; height: 9px; border-radius: 50%; background: #777; flex: none; }
.dk-dot.ok { background: #5fcf7a; } .dk-dot.warn { background: #e2a33c; } .dk-dot.bad { background: #e05252; }
.dk-wall-b { display: flex; gap: 6px; flex-wrap: wrap; }
.dk-two { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.dk-row { display: flex; align-items: center; gap: 10px; justify-content: space-between; margin: 0 0 8px; font-size: 13px; }

#viewing-as-bar { position: sticky; top: 0; z-index: 3000; display: flex; justify-content: center; align-items: center; gap: 14px; padding: 6px 12px; background: #e2a33c; color: #1a1205; font-size: 13px; }
#viewing-as-bar button { background: #1a1205; color: #fff; border: none; padding: 4px 12px; cursor: pointer; font-size: 12px; }
.arc-pair { display: flex; gap: 6px; margin: 0 0 4px; }
.arc-pair button { flex: 1; margin: 0 !important; text-align: center !important; }
.archive-health { margin: 0; display: flex; flex-direction: column; gap: 2px; }
.archive-health .ah-row { display: flex; align-items: center; gap: 7px; font-size: 12.5px; line-height: 1.35; padding: 3px 0; }
.archive-health .ah-row strong { font-weight: 600; }
.archive-health .ah-issue { color: var(--text-dim); margin-left: auto; font-size: 11.5px; text-align: right; }
.ah-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--text-dim); }
.ah-dot.ok { background: #5fcf7a; }
.ah-dot.warn { background: #e2a33c; }
.ah-dot.fail { background: #e2456b; }
.ah-dot.stopped { background: var(--text-dim); opacity: 0.5; }
.archive-checklist { list-style: none; margin: 0 0 16px; padding: 0; max-height: 360px; overflow-y: auto; }
.archive-checklist li { display: flex; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.archive-checklist li:last-child { border-bottom: none; }
.archive-checklist .ah-detail { color: var(--text-dim); font-size: 12px; }

/* ---------- Output mask editor (output-mask-panel.js) ---------- */
/* The stage the shapes are drawn over: sits above whatever it masks, passes
   clicks through until a pen session or the wand needs them. */
.omask-host { position: absolute; inset: 0; z-index: 15; pointer-events: none; }
.omask-host .mask-overlay { pointer-events: auto; }
.omask-host.wand-armed { pointer-events: auto; cursor: crosshair; }
.omask { display: flex; flex-direction: column; gap: 8px; }
.omask-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.omask-hint { margin: 0; font-size: 12px; }
.omask-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.omask-tol { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-dim); margin-left: 6px; }
.omask-tol input { width: 54px; padding: 3px 5px; font-size: 12px; }
.omask-wandhint { margin: 0; font-size: 12px; }
.omask .mask-list, .omask-list { display: flex; flex-direction: column; gap: 4px; }
.omask .mask-row { grid-template-columns: auto minmax(0, 1fr) auto auto auto auto; }
.omask-feather { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-dim); }
.omask-feather input { width: 54px; padding: 2px 4px; font-size: 12px; }
.omask-tools { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.omask-count { font-size: 12px; color: var(--text-dim); min-width: 70px; }
.omask-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-top: 4px; border-top: 1px solid var(--border); }
.omask-preview { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; margin-right: auto; cursor: pointer; }
.omask-status { font-size: 12px; color: var(--text-dim); }
.proj-mask-stagewrap { margin: 8px 0 0; display: flex; justify-content: center; }
.proj-mask-stage { position: relative; background: #08070a; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; max-width: 100%; }
.proj-mask-stage img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: fill; opacity: 0.55; pointer-events: none; }
.archive-version { font-size: 11px; line-height: 1.4; color: var(--text-dim); margin: 0 0 8px; word-break: break-word; }
.archive-version strong { color: var(--text); font-family: 'JetBrains Mono', monospace; }
.cl-more { flex-basis: 100%; margin-top: 6px; }
.cl-more summary { cursor: pointer; font-size: 12px; color: var(--accent); width: fit-content; }
.cl-body { white-space: pre-wrap; font-size: 12.5px; line-height: 1.5; color: var(--text-dim); margin: 8px 0 6px; max-width: 70ch; }
.cl-files { list-style: none; margin: 6px 0 2px; padding: 0; display: flex; flex-direction: column; gap: 2px; font-family: 'JetBrains Mono', monospace; font-size: 11.5px; }
.cl-files li { display: flex; justify-content: space-between; gap: 14px; }
.cl-path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-dim); }
.cl-stat { flex: none; color: var(--text-dim); }
.cl-add { color: #5fcf7a; } .cl-del { color: #e2456b; }
.cl-more-note { color: var(--text-dim); }
.cl-popup-list .cl-more { margin-top: 4px; }
.cl-diff { margin: 6px 0 2px; padding: 8px 10px; background: rgba(255,255,255,0.04); border-radius: 6px; font-family: 'JetBrains Mono', monospace; font-size: 11.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-all; }

/* ---- Routing: Live apps panel (live-apps-panel.js) ---- */
#live-apps-panel { margin-bottom: 16px; }
.la-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.la-new > summary { cursor: pointer; color: var(--text-dim); font-size: 13px; margin: 6px 0; }
.la-form { max-width: 720px; padding-top: 6px; }
.la-row { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; font-size: 13px; }
.la-lab { width: 96px; flex-shrink: 0; color: var(--text-dim); }
.la-row select { flex: 0 1 320px; }
.la-row input[type="range"] { flex: 1; max-width: 360px; }
.la-val { width: 64px; text-align: right; }
.la-note { margin: -4px 0 10px 106px; }
.la-pills { display: flex; flex-wrap: wrap; gap: 4px; }
.la-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 6px; }
.la-error { color: var(--danger); font-size: 12.5px; margin-right: auto; }
.la-session { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 12px; }
.la-session-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.la-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-bottom: 12px; }
.la-metrics > div { background: var(--panel-2); border-radius: var(--radius-sm); padding: 8px 10px; display: flex; flex-direction: column; gap: 2px; }
.la-metrics span { font-size: 11.5px; color: var(--text-dim); }
.la-metrics b { font-size: 16px; font-weight: 500; }
.la-table { width: 100%; border-collapse: collapse; font-size: 12.5px; margin-bottom: 8px; }
.la-table th, .la-table td { text-align: left; padding: 6px 4px; border-top: 1px solid var(--border); font-weight: 400; }
.la-table th { color: var(--text-dim); font-size: 11.5px; border-top: none; }
.la-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; }
.la-tag { font-size: 11px; color: var(--accent); margin-left: 4px; }
@media (max-width: 720px) {
  .la-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .la-note { margin-left: 0; }
  .la-table { display: block; overflow-x: auto; }
}
.la-actions select { width: auto; flex: 0 0 auto; }

/* ---- Dashboard: yesterday on the walls (templates/index.html, dashboard-yesterday.js) ---- */
.yday { margin: 0 0 16px; padding: 12px 14px; }
.yday-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 0 0 8px; }
.yday-head strong { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; }
.yday-head .yday-link { margin-left: auto; font-size: 12px; color: var(--accent-strong); text-decoration: none; }
.yday-body { display: grid; grid-template-columns: auto 1fr; gap: 10px 22px; align-items: start; }
.yday-stats { display: flex; gap: 18px; flex-wrap: wrap; }
.yday-stat .k { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.06em; }
.yday-stat .v { font-size: 22px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1.1; }
.yday-stat .s { font-size: 11px; color: var(--text-dim); }
.yday-stat .v.ok { color: #6FAF8B; } .yday-stat .v.warnc { color: #E2A33C; } .yday-stat .v.bad { color: var(--danger); }
.yday-walls { display: grid; gap: 4px; min-width: 0; }
.yday-wall { display: grid; grid-template-columns: 90px 1fr 48px; gap: 8px; align-items: center; font-size: 11px; color: var(--text-dim); }
.yday-wall .pct { text-align: right; font-family: "JetBrains Mono", monospace; font-variant-numeric: tabular-nums; color: var(--text); }
.yday-bar { display: flex; height: 9px; background: var(--panel-2); border: 1px solid var(--border); overflow: hidden; }
.yday-bar i { display: block; height: 100%; }
.yday-bar .st-content { background: #6FAF8B; } .yday-bar .st-offline { background: var(--danger); } .yday-bar .st-blank { background: #E2A33C; } .yday-bar .st-hands_down { background: var(--periwinkle); } .yday-bar .st-stutter { background: #8FB3C9; } .yday-bar .st-gap { background: transparent; }
.yday-incidents { margin: 8px 0 0; font-size: 12px; }
@media (max-width: 860px) { .yday-body { grid-template-columns: 1fr; } }

/* Project cost / operating expense tag (Finance → Expenses, Recurring) */
.cost-tag { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; padding: 1px 7px; border: 1px solid var(--border); color: var(--text-dim); }
.cost-tag.project { color: var(--accent, #e8a33d); border-color: currentColor; }

.inv-books { font-size: 13px; margin: 2px 0 8px; }
.inv-books-form { display: flex; gap: 8px; align-items: center; }
.inv-books-form select { flex: 1; }

/* Wall alert bar (static/js/alert-banner.js) */
.wall-alert-bar { position: sticky; top: 0; z-index: 900; display: flex; align-items: center; gap: 14px; padding: 9px 16px; background: #b3261e; color: #fff; font-size: 13px; }
.wall-alert-bar[hidden] { display: none; }
.wall-alert-bar .wall-alert-text { flex: 1; }
.wall-alert-bar a { color: #fff; text-decoration: underline; white-space: nowrap; }
.wall-alert-bar button { background: transparent; border: 0; color: #fff; font-size: 20px; line-height: 1; cursor: pointer; padding: 0 4px; }

/* Pre-show check (Show desk) */
.dk-check { margin: 0 0 18px; }
.dk-check-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dk-check-result { font-weight: 700; }
.dk-check-result.ok { color: #3aa88f; }
.dk-check-result.bad { color: #ff8fa3; }
.dk-check-list { margin-top: 10px; display: grid; gap: 6px; }
.dk-check-item { display: grid; grid-template-columns: 18px 150px 1fr; gap: 10px; align-items: baseline; font-size: 13px; padding: 6px 10px; border: 1px solid var(--border); background: var(--panel); }
.dk-check-item .mark { font-weight: 800; }
.dk-check-item.ok .mark { color: #3aa88f; }
.dk-check-item.warn .mark { color: #e2a33c; }
.dk-check-item.fail .mark { color: #ff8fa3; }
.dk-check-item.note .mark { color: var(--text-dim); }
.dk-check-item .t { font-weight: 600; }
.dk-check-item .d { color: var(--text-dim); }
@media (max-width: 600px) { .dk-check-item { grid-template-columns: 18px 1fr; } .dk-check-item .d { grid-column: 2; } }

.yday-pick { margin-left: 6px; }
.yday-pick button { padding: 2px 10px; font-size: 11.5px; }

/* Gestures page (templates/gestures.html, static/js/gestures.js) */
.gx-wrap { max-width: 1280px; }
.gx-head { display: flex; align-items: flex-start; gap: 16px; margin: 0 0 16px; }
.gx-grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 16px; align-items: start; }
@media (max-width: 1320px) { .gx-grid { grid-template-columns: 1fr; } .gx-side { position: static; } }
#gx-matrix { min-width: 560px; }
.gx-sec { margin: 0 0 14px; }
.gx-sec-h { display: flex; align-items: baseline; gap: 12px; margin: 0 0 10px; flex-wrap: wrap; }
.gx-sec-h h3 { margin: 0; font-size: 14px; }
.gx-scroll { overflow-x: auto; }
.gx-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.gx-table th { text-align: left; font-weight: 600; font-size: 12px; color: var(--text-dim); padding: 6px 10px 6px 0; border-bottom: 1px solid var(--border); white-space: nowrap; }
.gx-table td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--border); vertical-align: middle; }
.gx-g { font-weight: 600; }
.gx-how { font-size: 11.5px; color: var(--text-dim); max-width: 280px; }
.gx-fixed { color: var(--text-dim); font-size: 12.5px; }
.gx-lock, .gx-chip { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--text-dim); border: 1px dashed var(--border); padding: 0 5px; margin-left: 4px; font-weight: 500; }
.gx-cell { display: flex; align-items: center; gap: 8px; }
.gx-cell select { width: 100%; max-width: 190px; font-size: 12.5px; padding: 4px 6px; }
.gx-switch input { appearance: none; -webkit-appearance: none; width: 30px; height: 17px; border-radius: 9px; background: var(--border); position: relative; cursor: pointer; margin: 0; flex: none; transition: background .12s; }
.gx-switch input::after { content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: #bdb7b3; transition: left .12s; }
.gx-switch input:checked { background: var(--accent); }
.gx-switch input:checked::after { left: 15px; background: #fff; }
.gx-profiles { display: flex; flex-wrap: wrap; border: 1px solid var(--border); width: fit-content; max-width: 100%; margin: 0 0 8px; }
.gx-profiles button { background: none; border: 0; border-right: 1px solid var(--border); color: var(--text-dim); font-size: 12.5px; padding: 7px 14px; cursor: pointer; }
.gx-profiles button:last-child { border-right: 0; }
.gx-profiles button.on { background: var(--text); color: var(--bg); }
.gx-profiles .gx-new { color: var(--text-dim); }
.gx-profile-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 0 0 12px; }
.gx-pg { border-top: 1px solid var(--border); }
.gx-pg summary { cursor: pointer; padding: 9px 2px; font-size: 12.5px; font-weight: 600; list-style: none; }
.gx-pg summary::-webkit-details-marker { display: none; }
.gx-pg-body { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 4px 22px; padding: 0 2px 12px; }
.gx-par-h { display: flex; align-items: center; gap: 6px; margin: 0 0 4px; }
.gx-par-l { flex: 1; font-size: 12.5px; }
.gx-par-v { font-size: 11px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.gx-par-hint { font-size: 11px; color: var(--text-dim); margin: 3px 0 9px; line-height: 1.35; }
.gx-par input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 16px; background: transparent; margin: 0; cursor: pointer; --pct: 50%; }
.gx-par input[type="range"]::-webkit-slider-runnable-track { height: 3px; background: linear-gradient(to right, var(--accent) var(--pct), var(--border) var(--pct)); border-radius: 2px; }
.gx-par input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 13px; height: 13px; margin-top: -5px; border-radius: 50%; background: var(--text); border: 0; }
.gx-par input[type="range"]:disabled { opacity: .5; cursor: default; }
.gx-walls-t td { padding-right: 14px; }
.gx-side { position: sticky; top: 12px; }
.gx-lab { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-dim); margin: 0 0 8px; }
.gx-lab select { flex: 1; }
.gx-cam { position: relative; background: #14110f; border: 1px solid var(--border); }
.gx-cam canvas { display: block; width: 100%; max-width: 420px; height: auto; margin: 0 auto; }
.gx-cam-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; padding: 14px; font-size: 12px; color: #bdb7b3; }
.gx-meters { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 10px 0 6px; font-size: 11px; color: var(--text-dim); }
.gx-bar { height: 5px; background: var(--border); margin-top: 3px; position: relative; }
.gx-bar i { position: absolute; left: 0; top: 0; bottom: 0; background: var(--accent); width: 0; transition: width .12s; }
.gx-now { font-size: 12.5px; min-height: 18px; }
.gx-events { font-size: 12px; color: var(--text-dim); margin-top: 6px; min-height: 20px; }
.gx-events div { padding: 2px 0; border-top: 1px solid var(--border); }

.dk-showing { font-size: 12px; color: var(--text); margin: 0 0 8px; min-height: 16px; }
.dk-snap { display: none; width: 100%; height: auto; margin: 0 0 8px; border: 1px solid var(--border); background: #000; }
.dk-snap.on { display: block; }

.dk-phone-tip { display: none; margin-top: 14px; }
@media (max-width: 700px) { .dk-phone-tip { display: block; } .dk-big { grid-template-columns: repeat(2, 1fr); } #dk-idle { grid-column: 1 / -1; } .dk-wall-b button { min-height: 38px; } }

.fx-pals button:not(.small)[data-v="custom"] { display: flex; }
.fx-pals button[hidden] { display: none; }
.fx-photo-btn { margin-top: 8px; }

.inv-book-form { display: grid; grid-template-columns: minmax(0, 1.6fr) 1fr 1fr 64px auto; gap: 6px; margin: 4px 0 0; align-items: center; }
.inv-book-form select, .inv-book-form input { width: 100%; font-size: 12px; padding: 4px 6px; }
.inv-booking { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: 13px; padding: 3px 0; border-bottom: 1px solid var(--border); }
@media print { #lo-panel .fin-list-toolbar { display: none !important; } #lo-panel { color: #000; } #lo-table td, #lo-table th { color: #000; border-color: #ccc; } .confirm-overlay { display: none !important; } }

/* Time page */
.tm-form { display: grid; grid-template-columns: minmax(160px, 1.4fr) 150px 100px minmax(140px, 2fr) auto; gap: 8px; align-items: center; }
.tm-form input, .tm-form select { width: 100%; }
.tm-timer { display: flex; align-items: center; gap: 10px; margin: 10px 0 0; flex-wrap: wrap; }
.tm-cols { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 14px; align-items: start; }
@media (max-width: 900px) { .tm-cols { grid-template-columns: 1fr; } .tm-form { grid-template-columns: 1fr 1fr; } }
.tm-h { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 8px; flex-wrap: wrap; }
.tm-sub { font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); margin: 8px 0 6px; }
.tm-bar { display: grid; grid-template-columns: minmax(80px, 1.2fr) 1.5fr auto; gap: 8px; align-items: center; font-size: 13px; padding: 3px 0; }
.tm-bar i { display: block; height: 6px; background: var(--border); position: relative; }
.tm-bar i::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--w); background: var(--accent); }
.tm-bar b { font-weight: 500; font-variant-numeric: tabular-nums; }

/* Project files dialog */
.pf-box { max-width: 560px; }
.pf-list { max-height: 42vh; overflow: auto; margin-bottom: 10px; }
.pf-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 10px; padding: 7px 0; border-bottom: 1px solid var(--border); align-items: center; }
.pf-row a { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-row .hint-text { grid-column: 1; }
.pf-row button { grid-column: 2; grid-row: 1 / span 2; }
.pf-add { display: grid; grid-template-columns: 1fr; gap: 6px; margin-bottom: 6px; }

/* Search palette (Cmd/Ctrl+K) */
.sp-overlay { position: fixed; inset: 0; background: rgba(8, 7, 8, 0.6); z-index: 1100; display: flex; justify-content: center; align-items: flex-start; padding: 12vh 16px 0; }
.sp-box { width: min(620px, 100%); background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); overflow: hidden; }
.sp-box input { width: 100%; box-sizing: border-box; border: 0; border-bottom: 1px solid var(--border); background: transparent; color: var(--text); padding: 14px 16px; font-size: 15px; outline: none; }
.sp-list { max-height: 50vh; overflow: auto; }
.sp-row { display: grid; grid-template-columns: 78px minmax(0, 1fr) auto; gap: 10px; align-items: center; padding: 9px 16px; color: var(--text); text-decoration: none; font-size: 14px; }
.sp-row.sel, .sp-row:hover { background: var(--border); }
.sp-kind { font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-dim); }
.sp-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-sub { font-size: 12px; color: var(--text-dim); }
.sp-empty { padding: 18px 16px; color: var(--text-dim); font-size: 13px; }
