:root {
  --bg: #fff;
  --fg: #000;
  --line: #000;
  --muted: #666;
  --checker-a: #fff;
  --checker-b: #dcdcdc;

  /* ---- inky monochrome design tokens (panels / inspector / colour) ----
     Edgy + boxy: pure-black structure and active fills, SHARP corners. One
     ink, one height. Active = black-on-white; hover punches to black. */
  --ink: #000;             /* active fills + strong text — deep pure black */
  --ink-2: #000;           /* ink hover */
  --label: #555;           /* field labels / section titles */
  --hair: #000;            /* component borders (pure black, boxy) */
  --hair-2: #000;          /* dividers */
  --surface: #f0f0f0;      /* subtle hover tint on fields */
  --field-h: 26px;         /* one control height everywhere in a panel */
  --r: 0;                  /* sharp corners — no rounding */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--fg);
  font-family: "IBM Plex Sans", "Helvetica Neue", Arial, sans-serif;
}

button, select, input, a { font: inherit; }

.app {
  height: 100vh;
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: 10px;
  padding: 10px;
}

.topbar, .control-bar, .panel, .library, .status-bar {
  border: 1px solid var(--line);
  background: var(--bg);
}

.topbar, .status-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  flex-wrap: wrap;
}

.topbar > * { min-width: 0; }

.label {
  font-size: 11px;
  letter-spacing: 0.12em;
}

.topbar h1 {
  margin: 2px 0 0;
  font-size: 28px;
  line-height: 1;
}


.drop-button, .control-bar button, .ghost-button, .ghost-link, select {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg);
  padding: 10px 12px;
  text-decoration: none;
}

button:not(:disabled),
a.ghost-link,
select {
  cursor: pointer;
}

button:disabled {
  opacity: 0.45;
  cursor: default;
}

.drop-button.drag {
  background: #000;
  color: #fff;
}

.library {
  min-height: 0;
  display: grid;
  grid-template-rows: auto 1fr;
}

.control-bar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
  padding: 10px;
  align-items: center;
}

.control-bar > select,
.control-bar > button,
.control-bar > .slider-wrap {
  min-width: 0;
}

.slider-wrap {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px;
  align-items: center;
  border: 1px solid var(--line);
  padding: 0 10px;
  height: 42px;
  min-width: 0;
}

.main-grid {
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(200px, 1fr) minmax(0, 2fr) minmax(0, 2fr);
  gap: 10px;
}

.panel {
  min-height: 0;
  display: grid;
  grid-template-rows: auto 1fr;
}

/* Preview panels are a flex column: head (top chin) and foot (bottom chin)
   stay fixed-height, the frame in between grows to fill. */
.panel.preview-panel,
.stage-wrap.preview-panel {
  display: flex;
  flex-direction: column;
}

/* ============================ Editor reframe ============================ */
:root { --accent: #1b73e8; }

.app.editor { grid-template-rows: auto 1fr auto; }

/* File icon (left) · Edit/Process view-swap (right). Undo/redo live by the
   viewport BG button; help/collapse live in the ready bar. */
.editor-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.doc-actions-left { justify-self: start; }
.brand-logo { height: 22px; width: auto; display: block; flex: 0 0 auto; }
/* document name, centred in the header (moved out of the stage so the canvas gets the
   full height). flex:1 between the File icon and the view-swap → centred + truncates. */
.doc-title { flex: 1 1 auto; min-width: 0; text-align: center; font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-sel { color: var(--accent); font-weight: 600; }
.brand { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.brand-name { font-weight: 600; letter-spacing: 0.02em; }
.brand-sub { color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-actions { flex-wrap: wrap; justify-content: flex-end; }
.bar-sep { width: 1px; align-self: stretch; background: var(--line); opacity: 0.4; margin: 0 2px; }

.editor-grid {
  min-height: 0;
  display: grid;
  /* toolstrip · stage · actionbar · rightdock (history + layers) */
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  /* no gap between the toolstrip / stage / action bar: they butt together so their
     borders form one continuous picture frame around the canvas. The dock is set
     apart with its own margin (below). */
  gap: 0;
}
/* The grid template (which dock columns exist) is driven by JS in the Dockable-panels
   module — leftdock·toolstrip·stage·actionbar·rightdock, with each side dock's `auto`
   track added only when that dock is shown. Empty/folded docks are set display:none. */
.leftdock { display: none; }   /* default until JS shows it / a panel docks here */

/* ---- Manage screen (Edit/Manage swap) ----
   The A-side surface. Lives in the same grid cell as .editor-grid; display toggles by
   the .app.manage class (a display:none element is not a grid item, so auto-placement
   just works — header→row1, whichever grid is shown→1fr row2, footer→row3). manage.js
   reparents Library / Processor / Jobs into here, so they get room the 270px dock can't. */
.manage-grid { display: none; min-height: 0; }
.app.manage .editor-grid { display: none; }
.app.manage .manage-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 360px);
  grid-template-rows: minmax(0, 1fr) minmax(120px, 0.7fr) minmax(96px, 0.45fr);
  gap: 10px;
}
/* Library = the big browse pane (full-height left); right column stacks Processor (the active
   image's pipeline) · Info (inspect the selection) · Jobs (the queue). */
.manage-grid .rail-section.library { grid-column: 1; grid-row: 1 / span 3; }
.manage-grid .rail-section.processor { grid-column: 2; grid-row: 1; }
.manage-grid .rail-section.info { grid-column: 2; grid-row: 2; }
.manage-grid .rail-section.jobs { grid-column: 2; grid-row: 3; }
.manage-grid .rail-section.info .fp-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
.manage-grid .rail-section .panel-x { display: none; }   /* a Manage citizen has no close button */
.info-empty { padding: 14px 12px; color: var(--muted); font-size: 12px; line-height: 1.5; }
/* In the dock the sections share the dock's frame; standalone in the grid they need
   their own border/surface + rounded corners, and must clip their own scroll. */
.manage-grid .rail-section {
  border: 1px solid var(--line); background: var(--bg); border-radius: var(--r);
  min-height: 0; min-width: 0; overflow: hidden; flex: 1 1 auto;
}
/* Borrowed panels are never collapsed/floated here — neutralise the dock's collapse caret. */
.manage-grid .rail-section.collapsed { flex: 1 1 auto; }
.manage-grid .rail-section .caret { display: none; }

.toolstrip {
  border: 1px solid var(--line);
  background: var(--bg);
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px;
  flex: 0 0 auto;
  justify-content: center;   /* swatches moved to the canvas → tools centre in the bar */
}
/* THE shared button object — every toolbar (tools, arrange, actions, viewport)
   inherits this one class so they all match. Primary tools opt into a larger box. */
.tool-button {
  position: relative;
  width: 30px; height: 30px;
  border: 1px solid var(--line);
  background: var(--bg); color: var(--fg);
  font-size: 14px; line-height: 1; cursor: pointer; white-space: nowrap;
  display: grid; place-items: center;
}
.tool-button.active { background: #000; color: #fff; }
/* one consistent hover / disabled / toggle-on treatment for every bar */
.tool-button:hover:not(:disabled):not(.active):not(.on) { background: var(--accent); color: #fff; border-color: var(--accent); }
.tool-button:hover:not(:disabled)[data-key]::after { color: rgba(255, 255, 255, 0.8); }
.tool-button:disabled { opacity: 0.3; cursor: default; }
.tool-button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.tool-button.on[data-key]::after { color: rgba(255, 255, 255, 0.8); }
/* right-side object action bar — mirrors the left toolstrip (clipboard / boolean /
   transform actions for the selection). Buttons are the same square objects as the
   tools, and as action buttons they get a hover + disabled state. */
.actionbar {
  border: 1px solid var(--line); background: var(--bg);
  display: flex; flex-direction: column; gap: 6px; padding: 6px; flex: 0 0 auto;
  justify-content: center;
}
/* V and A are the two primary tools; the creation tools below read as subcontrols */
.tool-button.tool-primary { width: 34px; height: 34px; font-size: 17px; }
/* shortcut badge: a tiny letter in the bottom-right corner (tool buttons,
   viewport controls, the fill/stroke swap — one consistent kind of object) */
.tool-button[data-key]::after, .swatch-swap[data-key]::after,
.icon-btn[data-key]::after, .view-btn[data-key]::after {
  content: attr(data-key);
  position: absolute; right: 1px; bottom: 0;
  font-size: 8px; line-height: 1; font-weight: 600;
  color: var(--muted); pointer-events: none; letter-spacing: 0;
}
.tool-button.active[data-key]::after { color: rgba(255, 255, 255, 0.75); }
/* viewport controls now use the shared .tool-button object (see above) */
.vp-sep { width: 1px; align-self: stretch; margin: 2px 3px; background: var(--line); }
.swatch-swap[data-key]::after { right: -6px; bottom: -1px; }
/* header icon box (File menu) — same bordered-box object, square */
.icon-btn {
  position: relative; box-sizing: border-box; width: 32px; height: 30px;
  border: 1px solid var(--line); background: var(--bg); color: var(--fg);
  font: inherit; font-size: 16px; line-height: 1; cursor: pointer; display: grid; place-items: center;
}
.icon-btn:hover, .menu.open .icon-btn { background: var(--accent); color: #fff; border-color: var(--accent); }
/* text variant for header dropdown triggers (Layout ▾) */
.icon-btn.hdr-text { width: auto; padding: 0 10px; font-size: 13px; }
.icon-btn.hdr-text.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.menu.open .icon-btn[data-key]::after, .icon-btn:hover[data-key]::after { color: rgba(255, 255, 255, 0.8); }
/* Ableton-style view swap: two icon boxes, the active view highlighted. Lifted
   above the modal (z 50) so you can swap back to Edit while Process is open. */
.view-swap { display: inline-flex; gap: 0; position: relative; z-index: 60; }
/* Cloud build: no Manage station (its Library/Processor/Jobs panels don't exist); show the
   download-the-desktop-app CTA instead. `.cloud` is set on <html> by env.js. */
.get-desktop { display: none; }
.cloud .view-swap { display: none; }
.cloud .get-desktop { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; margin-left: 4px;
  font-size: 12px; font-weight: 600; color: var(--fg); background: var(--panel); border: 1px solid var(--hair);
  border-radius: var(--r); text-decoration: none; white-space: nowrap; }
.cloud .get-desktop:hover { border-color: var(--ink); background: var(--bg); }
.view-btn {
  position: relative; box-sizing: border-box; min-width: 34px; height: 30px;
  padding: 0 10px; border: 1px solid var(--line); background: var(--bg); color: var(--fg);
  font: inherit; font-size: 16px; line-height: 1; cursor: pointer; display: grid; place-items: center;
}
.view-btn + .view-btn { border-left: 0; }
.view-btn:hover:not(.active) { background: var(--hover, #f0f0f0); }
.view-btn.active { background: #000; color: #fff; border-color: #000; }
.view-btn.active[data-key]::after { color: rgba(255, 255, 255, 0.7); }
.view-count {
  position: absolute; top: -5px; right: -5px; min-width: 14px; height: 14px; padding: 0 3px;
  box-sizing: border-box; background: var(--accent); color: #fff; border-radius: 7px;
  font-size: 9px; font-weight: 700; line-height: 14px; text-align: center;
}
.view-count:empty { display: none; }
/* Edit ⇄ Manage tabs: text-labelled segmented control (the icon .view-btn is for the
   old swap). The active station reads inked; click the other to cross the seam. */
.view-tab {
  box-sizing: border-box; height: 30px; padding: 0 14px; border: 1px solid var(--line);
  background: var(--bg); color: var(--muted); font: inherit; font-size: 12px; font-weight: 600;
  letter-spacing: 0.02em; line-height: 1; cursor: pointer;
}
.view-tab + .view-tab { border-left: 0; }
.view-tab:first-child { border-radius: var(--r) 0 0 var(--r); }
.view-tab:last-child { border-radius: 0 var(--r) var(--r) 0; }
.view-tab:hover:not(.active) { background: var(--surface); color: var(--ink); }
.view-tab.active { background: var(--ink); color: #fff; border-color: var(--ink); }
.tool-sep { height: 1px; background: var(--line); margin: 2px 4px; align-self: stretch; }
/* Fill (primary) / stroke (secondary) swatches — float in the canvas's inner
   bottom-right corner (over the stage body, above the rulers). */
.tool-spacer { flex: 1 1 auto; }
.tool-swatches {
  position: absolute; right: 12px; bottom: 12px; z-index: 8;
  width: 40px; height: 48px; padding: 0;
  pointer-events: auto;
}
.swatch { position: absolute; width: 22px; height: 22px; padding: 0; cursor: pointer; border: 1px solid #777; box-shadow: 0 0 0 1px #fff; }
.swatch-fill { left: 0; top: 0; z-index: 2; }
.swatch-stroke { right: 0; top: 13px; z-index: 1; }
.swatch.none { background: #fff; }
.swatch.none::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top right, transparent 44%, #e0245e 45%, #e0245e 55%, transparent 56%); }
.swatch-swap { position: absolute; left: 0; bottom: 3px; width: 16px; height: 14px; border: 0; background: none; padding: 0; cursor: pointer; font-size: 11px; line-height: 1; color: var(--muted); }
.swatch-swap:hover { color: #000; }

/* right dock: Inspector (properties) above, Layers below — resizable + collapsible */
.rightdock {
  position: relative;
  border: 1px solid var(--line);
  background: var(--bg);
  margin-left: 10px;   /* set apart from the picture-frame (which has no gaps) */
  width: 270px;
  min-height: 0;
  overflow: hidden;   /* backstop: a mis-sized section can never spill the dock off-screen */
  display: flex;
  flex-direction: column;
}
.dock-resizer {
  position: absolute; left: -5px; top: 0; bottom: 0; width: 9px;
  cursor: ew-resize; z-index: 6; touch-action: none;
}
.dock-resizer:hover { background: var(--accent); opacity: 0.3; }
.menu-count { color: var(--muted); font-size: 11px; margin-left: 2px; }
.rail-section { display: flex; flex-direction: column; min-height: 0; }

/* Left dock — mirror of the right dock (margin + resizer on the opposite edge). */
.leftdock {
  position: relative; border: 1px solid var(--line); background: var(--bg);
  margin-right: 10px; width: 270px; min-height: 0; overflow: hidden; flex-direction: column;
}
.dock-resizer-l { left: auto; right: -5px; }
/* The Properties panel reuses .context-panel for inspector-style selectors, but as a
   docked rail-section it must fill the column (not the 248px fixed float width). */
.rail-section.context-panel { width: auto; border: 0; box-shadow: none; }
/* The × is never shown: docked panels aren't "closed", and a floating panel re-docks by
   dragging its header back (no close button to accidentally lose a panel). It stays in the
   DOM purely as the header receiver's tail anchor. */
.leftdock .panel-x, .rightdock .panel-x, .dock-window .panel-x { display: none; }
.dock-window .rail-section.collapsed .panel-actions { display: flex; }   /* keep the header actions reachable on a collapsed float */
/* Every panel header action area is a customize-layout drop RECEIVER. All header buttons
   are one consistent size (one rule, every header) — no placeholder/blank-slot box; an
   empty header just shows the outlined drop area while customising. Capped at 3 tiles. */
/* panel-header receiver buttons read as the same soft hairline objects as the
   inspector icon buttons (not hard black boxes that hover blue) */
.section-head .panel-actions .tool-button { width: 26px; height: 26px; font-size: 12px; border: 1px solid var(--hair); border-radius: var(--r); background: #fff; color: var(--ink); }
.section-head .panel-actions .tool-button:hover:not(:disabled):not(.active):not(.on) { background: var(--surface); color: var(--ink); border-color: var(--hair-2); }
.section-head .panel-actions .tool-button.active, .section-head .panel-actions .tool-button.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.app.editor.customizing .section-head .panel-actions { min-width: 26px; min-height: 26px; outline: 1px dashed var(--ink); outline-offset: -1px; background: var(--surface); border-radius: var(--r); }
/* Shelf: closed/unused panels parked as squares in the top-right header. Click to reopen,
   right-click for a tiny menu. Empty → hidden so it takes no space. */
.panel-shelf { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.panel-shelf:empty { display: none; }
.shelf-sq { width: 26px; height: 26px; font-size: 13px; line-height: 1; border: 1px solid var(--hair); border-radius: var(--r); background: #fff; color: var(--ink); cursor: pointer; display: grid; place-items: center; padding: 0; }
.shelf-sq:hover { background: var(--surface); border-color: var(--hair-2); }
.shelf-sq.dimmed { opacity: 0.5; }   /* a shelved contextual panel that isn't currently relevant */
/* Auto-parked (contextual/idle) square: dashed + muted, to read as "available, nothing to
   act on yet" — distinct from a solid square (a panel you closed by hand). */
.shelf-sq.auto { border-style: dashed; color: var(--muted, #8a8f98); }
.shelf-sq.auto:hover { color: var(--ink); }
/* Colour panel: the picker editor embedded in a panel body (no backdrop/window chrome). */
.color .fp-body { padding: 0; }
.cp-window.cp-embedded { position: static; width: auto; max-width: none; border: 0; box-shadow: none; padding: 8px; gap: 7px; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* When the panel is too short the picker SCROLLS — its fixed rows (field, alpha bar,
   swatches) keep their size instead of collapsing the transparency bar to nothing. */
.cp-window.cp-embedded > * { flex-shrink: 0; }
.cp-window.cp-embedded .cp-head { display: none; }   /* the panel header already titles it */
/* drag any panel's header to detach/float or to re-dock + reorder (no detach button) */
.leftdock .section-head, .rightdock .section-head, .dock-window .section-head { cursor: grab; }
/* A floating, draggable panel window (History / Layers / Properties popped out). */
.dock-window {
  position: fixed; z-index: 80; min-width: 190px; min-height: 120px;
  display: flex; flex-direction: column; background: var(--bg);
  border: 1px solid var(--line); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.22);
  overflow: hidden;
}
/* All-sides resize handles (replaces the single bottom-right `resize:both`). Thin
   inside-edge strips + slightly bigger corners; sit above content so the very edge
   always grabs. */
.dock-rs { position: absolute; z-index: 6; }
.dock-rs-n { top: 0; left: 0; right: 0; height: 5px; cursor: ns-resize; }
.dock-rs-s { bottom: 0; left: 0; right: 0; height: 5px; cursor: ns-resize; }
.dock-rs-e { right: 0; top: 0; bottom: 0; width: 5px; cursor: ew-resize; }
.dock-rs-w { left: 0; top: 0; bottom: 0; width: 5px; cursor: ew-resize; }
.dock-rs-ne, .dock-rs-nw, .dock-rs-se, .dock-rs-sw { width: 10px; height: 10px; z-index: 7; }
.dock-rs-ne { top: 0; right: 0; cursor: nesw-resize; }
.dock-rs-nw { top: 0; left: 0; cursor: nwse-resize; }
.dock-rs-se { bottom: 0; right: 0; cursor: nwse-resize; }
.dock-rs-sw { bottom: 0; left: 0; cursor: nesw-resize; }
.dock-window > .rail-section { flex: 1 1 auto; min-height: 0; }
.dock-window > .rail-section > .section-body,
.dock-window > .rail-section .layers-list { flex: 1 1 auto; }
.dock-window.dragging { opacity: 0.92; cursor: grabbing; }
/* A collapsed floating panel hugs its header — drop the window's min-height + resize
   grabber so there's no empty chin below the folded header. */
.dock-window:has(> .rail-section.collapsed) { min-height: 0; resize: none; }
.dock-window.float-panel { z-index: 1000; }   /* Properties window above the docks */
/* Properties body fills its section (window or dock), not the 248px float max-height. */
.dock-window .fp-body, .leftdock .properties .fp-body, .rightdock .properties .fp-body,
.leftdock .color .fp-body, .rightdock .color .fp-body { max-height: none; flex: 1 1 auto; }
/* Drop indicator shown over a dock zone while dragging a window onto it. */
.dock-droplines { position: fixed; z-index: 1100; pointer-events: none;
  border: 2px solid var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); border-radius: 2px; }
/* insertion line between / below panels (vs. the full-zone box for an empty dock) */
.dock-droplines.line { border: 0; background: var(--accent); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6); }
.dock-droplines.hidden { display: none; }

/* ---- Locking-bezel groups: snapped floating panels tiled in one container ----
   The group behaves like a floating mini-dock — drag a member header to move it all,
   the 8-way handles scale it, and the bezel between members resizes / detaches. */
.dock-group {
  position: fixed; z-index: 80; min-width: 220px; min-height: 140px;
  display: flex; flex-direction: row; background: var(--bg);
  border: 1px solid var(--line); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.22); overflow: hidden;
}
.dock-group.col { flex-direction: column; }
.dock-group.dragging { opacity: 0.92; cursor: grabbing; }
.dock-group > .rail-section { min-width: 0; min-height: 0; overflow: hidden; }
/* An EXPANDED grouped member can't shrink below a usable size — a near-zero fraction
   otherwise leaves a thin empty "ghost" strip you can't grab back (collapsed members
   still hug their header via flex:0 0 auto, so this only floors live ones). */
.dock-group:not(.col) > .rail-section:not(.collapsed) { min-width: 130px; }
.dock-group.col > .rail-section:not(.collapsed) { min-height: 96px; }
.dock-group > .rail-section > .section-body, .dock-group > .rail-section .layers-list,
.dock-group .fp-body { flex: 1 1 auto; min-height: 0; max-height: none; }
.dock-group > .rail-section .panel-x { display: none; }   /* × redocks → splits; use the bezel */
/* the bezel = draggable seam between two members (double-click to detach) */
.dock-bezel { flex: 0 0 6px; align-self: stretch; background: var(--line); cursor: col-resize; position: relative; z-index: 5; }
.dock-group.col > .dock-bezel { cursor: row-resize; }
.dock-bezel:hover { background: var(--accent); }
/* snap preview line shown while a dragged panel is about to lock to an edge */
.dock-snapline { position: fixed; z-index: 1100; pointer-events: none; background: var(--accent); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6); }
.dock-snapline.hidden { display: none; }

/* the side-panel fold toggle reads as "active" when panels are hidden */
.bar-btn.on { background: var(--accent); color: #fff; border-color: var(--accent); }

.stage-wrap { min-height: 0; position: relative; }
.stage-head { gap: 10px; }
/* arrange/structure toolbar across the top of the canvas (reorder/group/cleanup …) */
/* top arrange bar — the top run of the picture frame: same fill as the side bars,
   bordered top (outer edge) and bottom (inner edge against the canvas). */
.stage-toolbar { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap; padding: 5px 8px; background: var(--bg); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.tool-vsep { width: 1px; align-self: stretch; margin: 3px 4px; background: var(--line); }
/* body holds the rulers overlay + the preview frame (so rulers don't cover the toolbar) */
.stage-body { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* Illustrator-style measurement rulers (canvas-drawn, synced to zoom/pan) overlaid on
   the top + left edges of the stage. pointer-events:none so the canvas stays clickable. */
.rulers { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
.rulers[hidden] { display: none; }
.ruler { position: absolute; background: var(--bg); pointer-events: auto; }
.ruler-h { cursor: ns-resize; }
.ruler-v { cursor: ew-resize; }
/* explicit width/height: an absolutely-positioned <canvas> (replaced element) uses its
   intrinsic size with left+right:auto, so it won't stretch — size it directly. */
.ruler-h { top: 0; left: 18px; width: calc(100% - 18px); height: 18px; border-bottom: 1px solid var(--line); }
.ruler-v { top: 18px; left: 0; width: 18px; height: calc(100% - 18px); border-right: 1px solid var(--line); }
.ruler-corner { position: absolute; top: 0; left: 0; width: 18px; height: 18px; background: var(--bg); border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); z-index: 1; }

.inspector {
  background: var(--bg);
  min-height: 0;
  display: grid;
  grid-template-rows: auto 1fr;
  flex: 1 1 50%;
}
/* Docked panels share the column height equally (panels are reorderable now, so there's
   no single fixed History/Layers split + drag handle). Collapsed panels shrink to head. */
.leftdock .rail-section, .rightdock .rail-section { flex: 1 1 0; min-height: 96px; height: auto; }
.leftdock .rail-section.collapsed, .rightdock .rail-section.collapsed { flex: 0 0 auto; min-height: 0; height: auto; }
.dock-vresizer { display: none; }   /* old fixed handle — superseded by per-pair .dock-vsep */
/* Draggable handle between two stacked docked panels (resize up/down). */
.dock-vsep { flex: 0 0 6px; cursor: row-resize; touch-action: none; background: var(--bg); border-top: 1px solid var(--hair); }
.dock-vsep:hover { background: var(--ink); opacity: 0.18; }
.history-list { min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
.history-row { text-align: left; padding: 5px 12px; font: inherit; font-size: 12px; line-height: 1.4; background: none; border: 0; border-left: 2px solid transparent; color: #1d1d1f; cursor: pointer; }
.history-row:hover { background: #f2f2f2; }
.history-row.current { background: #000; color: #fff; }
.history-row.future { color: #9b9b9b; }
.inspector-body { min-height: 0; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 14px; }

/* ============================================================================
   Inspector design system — one inky, hairline-drawn component vocabulary used
   by every panel (Object / Artboard / Colour). Active states are ink-on-white;
   structure is hairlines; one --field-h height; one --r radius. No hard black
   boxes, no blue. See also the shared field/seg/btn rules below.
   ============================================================================ */
/* Raster panel "Process" section: the pipeline stages docked into Properties for a
   selected <image>. .hv-raster-hidden hides the source while a live trace previews
   on top of it (the canvas reads as the vector). */
.inline-svg .hv-raster-hidden { display: none; }
.raster-tools { display: flex; flex-direction: column; gap: 12px; margin-top: 18px; }
.rt-heading {
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  color: var(--label); padding-bottom: 5px; border-bottom: 1px solid var(--ink);
}
.rt-stage { display: flex; flex-direction: column; gap: 6px; }
.rt-stage-head { font-size: 11px; font-weight: 600; color: var(--ink); opacity: 0.85; }
.rt-stage-body { display: flex; flex-direction: column; gap: 6px; }
.rt-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.rt-actions .primary-button, .rt-actions .ghost-button { font-size: 12px; padding: 6px 10px; flex: 1 1 auto; }
.rt-stage + .rt-stage { padding-top: 10px; border-top: 1px dashed var(--hair, rgba(0,0,0,0.12)); }

.insp-group { display: flex; flex-direction: column; gap: 6px; }
/* Sections are siblings inside one wrap div, so they need an explicit gap — without
   this they collide (only the title's margin separated them). Tight rows WITHIN a
   section, a clear break BETWEEN sections. */
.insp-group + .insp-group { margin-top: 18px; }
.insp-title {
  font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  color: var(--label); padding-bottom: 5px; border-bottom: 1px solid var(--ink); margin: 0 0 2px;
}
/* Collapsible group headers: click the title to fold. The caret is a ::before pseudo (NOT a
   child node) so .insp-title.textContent stays equal to the group name. */
.insp-title.is-collapsible { position: relative; padding-left: 13px; cursor: pointer; user-select: none; }
.insp-title.is-collapsible:hover { color: var(--ink); }
.insp-title.is-collapsible::before { content: "\25BE"; position: absolute; left: 0; top: 0; font-size: 8px; letter-spacing: 0; color: var(--label); }
.insp-group.collapsed > .insp-title.is-collapsible::before { content: "\25B8"; }
.insp-group.collapsed > :not(.insp-title) { display: none; }
.insp-group.collapsed > .insp-title { margin-bottom: 0; border-bottom-color: var(--hair); }
.insp-row { display: grid; grid-template-columns: 54px minmax(0, 1fr); gap: 8px; align-items: center; min-height: var(--field-h); }
.insp-row > span { font-size: 11.5px; color: var(--label); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* scrubbable number label — drag horizontally to change the value */
.insp-row > span.scrub { cursor: ew-resize; user-select: none; -webkit-user-select: none; touch-action: none; }
.insp-row > span.scrub:hover { color: var(--ink); text-decoration: underline dotted; text-underline-offset: 2px; }

/* Paired number fields (X|Y, W|H, Rotate|Corner) — two label+input pairs share one row
   so short numeric values stop wasting half the panel width. */
.insp-row.insp-pair { grid-template-columns: 1fr 1fr; gap: 6px; }
.insp-field { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 7px; align-items: center; min-width: 0; }
.insp-field > span { font-size: 11.5px; color: var(--label); text-align: left; white-space: nowrap; }
.insp-field > span.scrub { cursor: ew-resize; user-select: none; -webkit-user-select: none; touch-action: none; }
.insp-field > span.scrub:hover { color: var(--ink); text-decoration: underline dotted; text-underline-offset: 2px; }
.insp-field.is-disabled { opacity: 0.38; }
.insp-field.is-disabled input { cursor: not-allowed; }

/* Small inline note under inspector rows (e.g. area-text overflow warning). */
.insp-note { font-size: 11px; line-height: 1.35; color: var(--label); }
.insp-note-warn { color: #d2691e; }

/* No native number spinners — the drag-to-scrub label replaces them. */
.insp-row input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.insp-row input[type="number"]::-webkit-outer-spin-button,
.insp-row input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* text / number / select field — the canonical input */
.insp-row input[type="number"], .insp-row input[type="text"], .insp-row select {
  width: 100%; height: var(--field-h); min-width: 0; border: 1px solid var(--hair); border-radius: var(--r);
  background: #fff; color: var(--ink); padding: 0 8px; font-size: 12px; font-variant-numeric: tabular-nums; line-height: var(--field-h);
}
.insp-row input[type="number"]:hover, .insp-row input[type="text"]:hover, .insp-row select:hover { background: var(--surface); }
.insp-row input[type="number"]:focus, .insp-row input[type="text"]:focus, .insp-row select:focus { outline: none; border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); background: #fff; }
.insp-row select { cursor: pointer; padding-right: 4px; }
.insp-row input[type="color"] { width: 100%; height: var(--field-h); padding: 0; border: 1px solid var(--hair); border-radius: var(--r); background: #fff; cursor: pointer; }
.insp-empty { color: var(--muted); font-size: 12.5px; line-height: 1.5; }

/* compact preset buttons (artboard size presets) */
.insp-preset-btns { display: flex; flex-wrap: wrap; gap: 5px; min-width: 0; }
.insp-preset-btns .ghost-button {
  padding: 0 9px; height: var(--field-h); font-size: 11px; color: var(--ink); border: 1px solid var(--hair);
  border-radius: var(--r); background: #fff; font-variant-numeric: tabular-nums;
}
.insp-preset-btns .ghost-button:hover { background: var(--ink); color: #fff; }

/* row of square icon buttons (align / arrange / flip / orient) */
.insp-btns { display: flex; gap: 5px; min-width: 0; align-items: center; }
.insp-iconbtn {
  flex: 0 0 auto; width: var(--field-h); height: var(--field-h); display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--hair); background: #fff; color: var(--ink); cursor: pointer; border-radius: var(--r); padding: 0;
}
.insp-iconbtn:hover:not(:disabled) { background: var(--ink); color: #fff; border-color: var(--ink); }
.insp-iconbtn:disabled { opacity: 0.3; cursor: default; }
.insp-iconbtn.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.insp-iconbtn svg { display: block; pointer-events: none; }
.insp-ratio { margin-left: auto; font-size: 11px; color: var(--label); font-variant-numeric: tabular-nums; }

/* bottom chin of the Properties panel — pinned align-to-artboard bar (never scrolls
   away). 6 equal-width ink buttons spanning the panel. */
.insp-foot { flex: 0 0 auto; border-top: 1px solid var(--ink); padding: 7px 10px; }
.insp-foot:empty { display: none; }
.insp-alignbar { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; }
.insp-alignbar .insp-iconbtn { width: 100%; }

/* inspector swatch (opens the colour picker) */
.insp-swatch {
  width: 100%; height: var(--field-h); padding: 0; border: 1px solid var(--hair); border-radius: var(--r); cursor: pointer;
  background: #000; box-shadow: inset 0 0 0 1px #fff;
}
.insp-swatch.none { background: #fff; background-image: linear-gradient(to top right, transparent 45%, var(--ink) 46%, var(--ink) 54%, transparent 55%); }
.insp-swatch.mixed { background-image: repeating-linear-gradient(45deg, rgba(127,127,127,0.5) 0 5px, rgba(127,127,127,0.15) 5px 10px); box-shadow: inset 0 0 0 1px #fff; }
.insp-slider-val.mixed { font-style: italic; opacity: 0.7; }

/* segmented control (cap / join / dash presets) — one connected hairline pill */
.insp-seg { display: flex; height: var(--field-h); border: 1px solid var(--hair); border-radius: var(--r); overflow: hidden; }
.insp-seg-btn {
  flex: 1 1 0; border: 0; border-left: 1px solid var(--hair); background: #fff; color: var(--ink);
  padding: 0; font-size: 12px; line-height: 1; cursor: pointer; min-width: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.insp-seg-btn:first-child { border-left: 0; }
.insp-seg-btn:hover:not(.active) { background: var(--surface); }
.insp-seg-btn svg { display: block; pointer-events: none; }
.insp-seg-btn.active { background: var(--ink); color: #fff; }

/* dash editor: presets row + dash/gap length sliders */
.insp-stroke-style { display: flex; flex-direction: column; gap: 7px; }
.insp-dash-sliders { display: flex; flex-direction: column; gap: 5px; }
.insp-dash-srow { display: grid; grid-template-columns: 30px minmax(0, 1fr) 22px; align-items: center; gap: 8px; }
.insp-dash-srow > span { font-size: 11px; color: var(--label); }
.insp-dash-srow > .insp-dash-val { text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; }
.insp-dash-srow.insp-disabled .insp-dash-val { color: var(--label); }

/* a control that doesn't apply yet (cap/join with no stroke, miter on a non-miter join) */
.insp-row.insp-disabled, .insp-disabled { opacity: 0.4; pointer-events: none; }
.insp-slider { display: flex; align-items: center; gap: 9px; min-height: var(--field-h); }
.insp-slider-val { font-size: 11.5px; color: var(--label); min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }

/* monochrome range slider — a black SQUARE thumb (white-bordered so it reads on
   the black track). No native blue, no round corners. */
.insp-slider input[type="range"], .insp-dash-srow input[type="range"] { flex: 1 1 0; width: 100%; min-width: 0; -webkit-appearance: none; appearance: none; background: transparent; height: 16px; cursor: pointer; }
.insp-slider input[type="range"]::-webkit-slider-runnable-track, .insp-dash-srow input[type="range"]::-webkit-slider-runnable-track { height: 2px; background: var(--ink); }
.insp-slider input[type="range"]::-moz-range-track, .insp-dash-srow input[type="range"]::-moz-range-track { height: 2px; background: var(--ink); }
.insp-slider input[type="range"]::-webkit-slider-thumb, .insp-dash-srow input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 12px; height: 12px; margin-top: -5px; border-radius: 0; background: var(--ink); border: 2px solid #fff; box-shadow: 0 0 0 1px var(--ink); }
.insp-slider input[type="range"]::-moz-range-thumb, .insp-dash-srow input[type="range"]::-moz-range-thumb { width: 12px; height: 12px; border-radius: 0; background: var(--ink); border: 2px solid #fff; box-shadow: 0 0 0 1px var(--ink); }
.insp-disabled input[type="range"]::-webkit-slider-track { background: #bbb; }
.insp-disabled input[type="range"]::-webkit-slider-thumb { background: #bbb; box-shadow: 0 0 0 1px #bbb; }

/* checkbox — small ink-filled SQUARE box (no native blue) */
.insp-row input[type="checkbox"] { -webkit-appearance: none; appearance: none; width: 15px; height: 15px; border: 1px solid var(--ink); border-radius: var(--r); background: #fff; cursor: pointer; position: relative; margin: 0; }
.insp-row input[type="checkbox"]:hover { background: var(--surface); }
.insp-row input[type="checkbox"]:checked { background: var(--ink); border-color: var(--ink); }
.insp-row input[type="checkbox"]:checked::after { content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }

/* active fill/stroke swatch in the toolstrip (Illustrator's X focus) */
.swatch.active { outline: 2px solid var(--accent); outline-offset: 1px; z-index: 3; }

/* ---------- unified colour picker ---------- */
.cp-backdrop {
  position: fixed; inset: 0; z-index: 2000; display: grid; place-items: center;
  background: transparent;   /* movable floating panel — don't dim the canvas */
}
.cp-window {
  width: 300px; max-width: calc(100vw - 24px); box-sizing: border-box;
  background: var(--bg); color: var(--fg); border: 1px solid var(--hair-2); border-radius: var(--r);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.22); padding: 10px; display: flex; flex-direction: column; gap: 8px;
  font-size: 12px; overflow: hidden;
}
.cp-head { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.cp-body { display: flex; gap: 8px; min-width: 0; }
/* field flexes to fill the row so field + hue + preview never overflow the window */
.cp-field { position: relative; flex: 1 1 auto; width: auto; min-width: 0; height: 128px; cursor: crosshair; border: 1px solid var(--hair-2); border-radius: var(--r); overflow: hidden; }
.cp-field-sat { position: absolute; inset: 0; background: linear-gradient(to right, #fff, rgba(255,255,255,0)); }
.cp-field-val { position: absolute; inset: 0; background: linear-gradient(to top, #000, rgba(0,0,0,0)); }
.cp-field-thumb {
  position: absolute; width: 12px; height: 12px; border: 2px solid #fff; border-radius: 50%;
  transform: translate(-50%, -50%); box-shadow: 0 0 0 1px #000; pointer-events: none;
}
.cp-hue { position: relative; width: 14px; height: 128px; cursor: ns-resize; border: 1px solid var(--hair-2); border-radius: var(--r); flex: 0 0 auto;
  background: linear-gradient(to bottom, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); }
.cp-hue-thumb { position: absolute; left: -2px; right: -2px; height: 4px; background: #fff; border: 1px solid #000; transform: translateY(-50%); pointer-events: none; }
/* side column: fill/stroke targets (duo) or a solo live preview (single) */
.cp-side { flex: 0 0 44px; display: flex; flex-direction: column; gap: 5px; }
.cp-side .cp-target { flex: 1 1 0; position: relative; padding: 0; border: 1px solid var(--hair-2); border-radius: var(--r); cursor: default; }
.cp-side.duo .cp-target { cursor: pointer; }
.cp-side.duo .cp-target.active { border-color: var(--ink); box-shadow: 0 0 0 2px var(--ink); z-index: 1; }
.cp-target > .cp-target-lab {
  position: absolute; top: 2px; left: 2px; font-size: 9px; line-height: 1; padding: 1px 3px;
  background: rgba(255, 255, 255, 0.82); color: #000; pointer-events: none; letter-spacing: 0.02em;
}
.cp-alpha { position: relative; height: 13px; cursor: ew-resize; border: 1px solid var(--hair-2); border-radius: var(--r); overflow: hidden; }
.cp-alpha-track { position: absolute; inset: 0; }
.cp-alpha-thumb { position: absolute; top: -2px; bottom: -2px; width: 4px; background: #fff; border: 1px solid #000; transform: translateX(-50%); pointer-events: none; }
/* numeric fields: two tidy columns (RGB | HSB) with right-aligned values, hex + alpha
   span the full width. Each row is a fixed label cell + input so everything lines up. */
/* colour-model tabs (RGB / HSL / HSB) — one connected hairline pill, ink active */
.cp-models { display: flex; flex-direction: column; gap: 7px; }
.cp-tabs { display: flex; height: var(--field-h); border: 1px solid var(--hair); border-radius: var(--r); overflow: hidden; }
.cp-tab { flex: 1 1 0; border: 0; border-left: 1px solid var(--hair); background: #fff; color: var(--label); font: inherit; font-size: 11px; letter-spacing: 0.05em; cursor: pointer; }
.cp-tab:first-child { border-left: 0; }
.cp-tab:hover:not(.active) { background: var(--surface); color: var(--ink); }
.cp-tab.active { background: var(--ink); color: #fff; }
.cp-fields { display: flex; flex-direction: column; gap: 5px; }
.cp-triple { display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px 8px; }
.cp-inp { display: grid; grid-template-columns: 14px minmax(0, 1fr); align-items: center; gap: 5px; font-size: 11px; color: var(--label); min-width: 0; cursor: ew-resize; user-select: none; -webkit-user-select: none; touch-action: none; }
.cp-inp input { cursor: text; min-width: 0; height: var(--field-h); border: 1px solid var(--hair); border-radius: var(--r); background: #fff; color: var(--ink); padding: 0 7px; font: inherit; font-variant-numeric: tabular-nums; text-align: right; }
.cp-inp input:hover { border-color: var(--hair-2); }
.cp-inp input:focus { outline: none; border-color: var(--ink); }
.cp-hex input { text-align: left; letter-spacing: 0.06em; }
/* recent-colours strip (auto-tracked, above the saved palette) */
.cp-recent { display: flex; flex-direction: column; gap: 4px; }
.cp-strip-lab { font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--label); font-weight: 600; }
.cp-recent-row { display: grid; grid-template-columns: repeat(12, 1fr); gap: 4px; }
/* gradient editor (Epic G): paint-type tabs + a draggable stop strip */
.cp-gradient { display: flex; flex-direction: column; gap: 6px; }
.cp-paint-types { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.cp-ptype { padding: 4px 0; font-size: 10.5px; border: 1px solid var(--hair-2); border-radius: var(--r); background: #fff; color: var(--label); cursor: pointer; }
.cp-ptype:hover { border-color: var(--ink); color: var(--ink); }
.cp-ptype.active { background: var(--ink); color: #fff; border-color: var(--ink); }
.cp-grad-strip { position: relative; height: 18px; border: 1px solid var(--hair-2); border-radius: var(--r); cursor: copy; }
.cp-grad-stop { position: absolute; top: 50%; width: 12px; height: 12px; transform: translate(-50%, -50%); border: 2px solid #fff; border-radius: 50%; box-shadow: 0 0 0 1px var(--ink); cursor: grab; }
.cp-grad-stop.active { box-shadow: 0 0 0 2px var(--ink); width: 14px; height: 14px; }
.cp-swatches { display: grid; grid-template-columns: repeat(10, 1fr); gap: 4px; }
.cp-sw { width: 100%; aspect-ratio: 1 / 1; padding: 0; border: 1px solid var(--hair-2); border-radius: var(--r); cursor: pointer; }
.cp-sw:hover { box-shadow: 0 0 0 1px var(--ink); }
.cp-sw-add { background: #fff; color: var(--label); font-size: 14px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; border-color: var(--hair); }
.cp-sw-add:hover { color: var(--ink); border-color: var(--ink); box-shadow: none; }
/* eyedropper sits in the swatch row as a clearly-bordered icon (first item) */
.cp-eyedrop { background: #fff; color: var(--ink); display: inline-flex; align-items: center; justify-content: center; margin-right: 2px; border-color: var(--hair); }
.cp-eyedrop:hover { background: var(--ink); color: #fff; border-color: var(--ink); box-shadow: none; }
.cp-actions { display: flex; align-items: center; gap: 6px; }
.cp-spacer { flex: 1 1 auto; }

/* selection overlay — lives in the stage svg but is stripped from saved output */
.hv-sel-box { fill: none; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; pointer-events: none; }
.inline-svg.hv-pickable { pointer-events: auto; }

/* the workspace name duplicated the brand — the source path lives in the rail */
.brand-sub, .brand-path { display: none; }

/* layers panel */
.rail-section.layers { flex: 1 1 auto; min-height: 120px; }
/* In a side dock the Library hugs its content (so it doesn't starve Layers); scope this
   to the docks ONLY. When floated/grouped the section must FILL its window so the list
   scrolls inside it (see .dock-window > .rail-section) — otherwise a detached Library
   grew to full content height, lost its scroll, and ran out of bounds. */
.leftdock .rail-section.library, .rightdock .rail-section.library { flex: 0 0 auto; }
.layers-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
.layer-row {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 8px; border-bottom: 1px solid var(--line);
  cursor: pointer; font-size: 13px; background: var(--bg);
}
.layer-row:hover { background: #f2f2f2; }
.layer-row.active { background: #000; color: #fff; }
.layer-row.dragging { opacity: 0.4; }
/* drag-drop affordances: nest INTO a group (outline+tint), or a "magnet" line BETWEEN
   rows for reorder (before/after). The crevice the pointer is in picks which. */
.layer-row.drop-into { outline: 1px solid var(--accent); outline-offset: -1px; background: color-mix(in srgb, var(--accent) 14%, var(--bg)); }
.layer-row.drop-into.active { background: color-mix(in srgb, var(--accent) 40%, #000); }
.layer-row.drop-before { box-shadow: inset 0 2px 0 0 var(--accent); }
.layer-row.drop-after { box-shadow: inset 0 -2px 0 0 var(--accent); }
.layers-list.drop-root { box-shadow: inset 0 0 0 2px var(--accent); }
/* group expand/collapse twisty (leaf rows render an invisible spacer to stay aligned) */
.layer-twist {
  flex: 0 0 12px; width: 12px; padding: 0; border: 0; background: none; color: var(--muted);
  font-size: 9px; line-height: 1; cursor: pointer; text-align: center;
}
.layer-twist.leaf { visibility: hidden; cursor: default; }
.layer-row.active .layer-twist { color: rgba(255, 255, 255, 0.85); }
.layer-row.is-group { font-weight: 600; }
.layer-swatch.group { background: none; border: 0; display: inline-grid; place-items: center; color: var(--muted); font-size: 12px; }
.layer-row.active .layer-swatch.group { color: #fff; }
/* Artboard pinned at the bottom of the stack, like a background layer. */
.layer-row.artboard-row { border-bottom: none; color: var(--muted); font-style: italic; }
.layer-row.artboard-row.active { color: #fff; }
.layer-swatch {
  flex: 0 0 auto; width: 14px; height: 14px; border: 1px solid var(--line);
  background-image:
    linear-gradient(45deg, #ccc 25%, transparent 25%), linear-gradient(-45deg, #ccc 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ccc 75%), linear-gradient(-45deg, transparent 75%, #ccc 75%);
  background-size: 8px 8px; background-position: 0 0, 0 4px, 4px -4px, -4px 0;
}
/* Raster rows show a live image thumbnail (background-image set inline). */
.layer-swatch.raster { background-repeat: no-repeat; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15); display: inline-grid; place-items: center; }
.layer-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.layer-btn {
  flex: 0 0 auto; width: 20px; height: 20px; padding: 0;
  border: 1px solid transparent; background: transparent; color: inherit;
  font: inherit; line-height: 1; cursor: pointer; text-align: center;
}
.layer-btn:hover { border-color: currentColor; }
.layer-btn.on { font-weight: 700; }
.layer-rename { flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); background: var(--bg); color: var(--fg); font: inherit; padding: 2px 4px; }

/* collapsible rail sections (accordion): caret + label on the left, any header
   buttons (History undo/redo) on the right. The layer arrange controls moved to the
   top-of-canvas toolbar, so the headers have their labels back. */
.section-head { cursor: pointer; user-select: none; gap: 6px; padding: 7px 8px; }
.section-head .caret { flex: 0 0 auto; font-size: 10px; color: var(--muted); transition: transform 0.12s; }
.section-head .sec-label { flex: 1 1 auto; font-size: 12px; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* a small count badge next to the History / Layers titles (pushes any header buttons right) */
.section-head .sec-label:has(+ .sec-count) { flex: 0 1 auto; }
.section-head .sec-count { flex: 0 0 auto; margin: 0 auto 0 6px; color: var(--muted); font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.section-head .sec-count:empty { display: none; }
/* Artboard pinned to the bottom of the Layers panel (a chin that never scrolls away) */
.layers-foot { flex: 0 0 auto; border-top: 1px solid var(--hair); }
.layers-foot:empty { display: none; }
.rail-section.collapsed .layers-foot { display: none; }
.rail-section.collapsed .insp-foot { display: none; }   /* hide the align chin when the panel is folded */
.rail-section.collapsed .library-chin { display: none; }   /* hide the filter/C·R·V chin when the library is folded */
.rail-section.collapsed .section-head .caret { transform: rotate(-90deg); }
.rail-section.collapsed .section-body { display: none; }
/* Dock-aware header: the caret sits on the OUTER (screen) edge and, when collapsed,
   points toward the canvas; the label hugs the INNER edge. Mirrored per dock side. */
.leftdock .section-head, .rightdock .section-head { justify-content: flex-start; }
/* RIGHT dock → label/count left, actions + caret pushed to the right edge, caret last. */
.rightdock .section-head .sec-label { order: 1; }
.rightdock .section-head .sec-count { order: 2; margin: 0; }
.rightdock .section-head .panel-actions { order: 3; margin-left: auto; }
.rightdock .section-head .caret { order: 4; margin-left: 6px; }
.rightdock .rail-section.collapsed .section-head .caret { transform: rotate(90deg); }   /* ◄ points inward (actions stay visible + carry the auto-margin) */
/* LEFT dock → caret on the left edge, label grows + right-aligns so its text hugs the
   inner edge, count trailing it. */
.leftdock .section-head .caret { order: 1; }
.leftdock .section-head .panel-actions { order: 2; }
.leftdock .section-head .sec-label { order: 3; flex: 1 1 auto; text-align: right; }
.leftdock .section-head .sec-count { order: 4; margin: 0 0 0 4px; }
.leftdock .rail-section.collapsed .section-head .caret { transform: rotate(-90deg); }   /* ► points inward */
/* collapsed keeps the header action buttons reachable (single row, no wrap) */
.rail-section.collapsed .panel-actions { display: flex; flex-wrap: nowrap; }
.rail-section.collapsed { flex: 0 0 auto; min-height: 0; }   /* must not reserve flex/min space */

@media (max-width: 900px) {
  .editor-grid { grid-template-columns: auto minmax(0, 1fr) auto; }
  .rightdock { display: none; }
}

.panel-head, .panel-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  min-width: 0;
}

.panel-head {
  justify-content: space-between;
  padding: 9px 11px;
  border-bottom: 1px solid var(--hair);
  flex: 0 0 auto;
}

.panel-head > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* rail-header action buttons (Layers reorder/group/cleanup/rename/delete, History
   undo/redo) — the same square objects as the tools so the headers read consistently. */
.panel-actions { gap: 4px; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }

/* bottom viewport bar — the bottom run of the picture frame: bordered top (inner
   edge against the canvas) and bottom (outer edge), same fill as the side bars. */
.panel-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 12px;
  background: var(--bg);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.preview-frame {
  min-height: 0;
  flex: 1 1 auto;
  padding: 10px;
  overflow: hidden;
}

.empty-frame {
  display: grid;
  place-items: center;
  color: var(--muted);
}

.checker {
  width: 100%;
  height: 100%;
  min-height: 0;
}

.viewport-shell.bg-checker,
.gallery-thumb {
  background:
    linear-gradient(45deg, var(--checker-b) 25%, transparent 25%),
    linear-gradient(-45deg, var(--checker-b) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--checker-b) 75%),
    linear-gradient(-45deg, transparent 75%, var(--checker-b) 75%);
  background-size: 24px 24px;
  background-position: 0 0, 0 12px, 12px -12px, -12px 0;
}

.viewport-shell.bg-white { background: #ffffff; }
.viewport-shell.bg-black { background: #000000; }
.viewport-shell.bg-dark  { background: #1c1c1c; }

.viewport-shell {
  position: relative;
  overflow: hidden;
  cursor: default;   /* arrow by default; tools override (crosshair), Hand only with Space */
  display: flex;
  align-items: center;
  justify-content: center;
}

.viewport-content {
  transform-origin: center center;
  /* will-change is toggled on in JS only during active pan/zoom and cleared at
     rest — a permanent GPU layer caches a bitmap that scales blurry on zoom. */
  width: max-content;
  height: max-content;
}

.viewport-content img,
.viewport-content svg {
  display: block;
  max-width: none;
  max-height: none;
  user-select: none;
  pointer-events: none;
}

.inline-svg {
  /* The artboard reads as an obvious sheet floating on the checker pasteboard.
     overflow stays visible so artwork can spill onto the pasteboard. The white
     background shows through wherever the artboard fill is "none". */
  overflow: visible;
  background: #ffffff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.20), 0 0 0 1px rgba(0, 0, 0, 0.12);
}
/* artboard fill = none → show a checker so the sheet reads as transparent (the white
   background otherwise made a transparent artboard look solid white). */
.inline-svg.transparent-board {
  background:
    linear-gradient(45deg, #dadada 25%, transparent 25%), linear-gradient(-45deg, #dadada 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #dadada 75%), linear-gradient(-45deg, transparent 75%, #dadada 75%), #fff;
  background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}

/* Per-tool canvas cursors (set via .stage-wrap[data-tool]). */
.stage-wrap[data-tool="marquee"] .viewport-shell, .stage-wrap[data-tool="marquee"] .inline-svg,
.stage-wrap[data-tool="pen"] .viewport-shell,     .stage-wrap[data-tool="pen"] .inline-svg,
.stage-wrap[data-tool="curvature"] .viewport-shell, .stage-wrap[data-tool="curvature"] .inline-svg,
.stage-wrap[data-tool="rect"] .viewport-shell,    .stage-wrap[data-tool="rect"] .inline-svg,
.stage-wrap[data-tool="ellipse"] .viewport-shell, .stage-wrap[data-tool="ellipse"] .inline-svg,
.stage-wrap[data-tool="line"] .viewport-shell,    .stage-wrap[data-tool="line"] .inline-svg { cursor: crosshair; }
.stage-wrap[data-tool="text"] .viewport-shell,    .stage-wrap[data-tool="text"] .inline-svg { cursor: text; }
/* Select / Transform: move cursor over a pickable object, arrow on empty canvas. */
.stage-wrap[data-tool="select"] .viewport-shell, .stage-wrap[data-tool="node"] .viewport-shell,
.stage-wrap[data-tool="transform"] .viewport-shell { cursor: default; }
.stage-wrap[data-tool="select"] .inline-svg [data-hv-id],
.stage-wrap[data-tool="transform"] .inline-svg [data-hv-id] { cursor: move; }
.inline-svg [data-hv-locked="1"] { cursor: default; }
/* Pen close-path affordance (hovering the first anchor). */
.stage-wrap.pen-close .viewport-shell, .stage-wrap.pen-close .inline-svg { cursor: pointer; }
.stage-wrap.pen-tempsel .viewport-shell, .stage-wrap.pen-tempsel .inline-svg { cursor: default; }
/* Pen auto add/delete affordances over an existing path */
.stage-wrap.pen-add .viewport-shell, .stage-wrap.pen-add .inline-svg { cursor: copy; }
.stage-wrap.pen-del .viewport-shell, .stage-wrap.pen-del .inline-svg { cursor: pointer; }
.hv-pen-add { fill: #16a34a; stroke: #fff; stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-pen-del { fill: none; stroke: #dc2626; stroke-width: 1.5; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-pen-cont { fill: none; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; pointer-events: none; }

/* Spacebar = temporary Hand tool: grab cursor while held (Illustrator). */
.stage-wrap.space-pan,
.stage-wrap.space-pan .viewport-shell,
.stage-wrap.space-pan .inline-svg { cursor: grab; }
.stage-wrap.space-pan:active,
.stage-wrap.space-pan .viewport-shell:active { cursor: grabbing; }

/* In-viewport point-editing handles (re-enable pointer events the svg disables) */
.hv-handle {
  fill: #fff;
  stroke: #000;
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
  pointer-events: all;
  cursor: grab;
}
.hv-handle:hover { fill: #000; }
.hv-handle.dragging { fill: #000; cursor: grabbing; }
/* Node-tool path anchors (square) + draggable bezier direction handles (round) */
.hv-node-anchor { fill: #fff; stroke: var(--accent); stroke-width: 1; }
.hv-node-anchor:hover { fill: var(--accent); }
.hv-node-anchor.selected { fill: var(--accent); stroke: #fff; stroke-width: 1.5; }
.hv-node-handle-line { stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; opacity: 0.55; }
.hv-node-handle { fill: var(--accent); stroke: #fff; stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: all; cursor: grab; }
.hv-node-handle:hover { fill: #000; }
.hv-node-handle.dragging { fill: #000; cursor: grabbing; }
.hv-whandles { pointer-events: none; }
.hv-whandle { fill: #fff; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; }
.stage-wrap[data-tool="width"] svg { cursor: crosshair; }
/* Epic B path-construction tools: live drag trail + cursors */
.hv-btrail { fill: none; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-btrail-knife { stroke: #e23; stroke-dasharray: 4 3; }
.hv-btrail-erase { stroke: #888; stroke-width: 2; opacity: 0.7; }
.hv-btrail-build { stroke: var(--accent); stroke-width: 2.5; opacity: 0.6; }
.stage-wrap[data-tool="shapebuilder"] svg, .stage-wrap[data-tool="knife"] svg, .stage-wrap[data-tool="eraser"] svg { cursor: crosshair; }
.stage-wrap[data-tool="scissors"] svg { cursor: crosshair; }
/* Epic I — Isolation mode: dim + disable everything outside the isolated group */
svg.hv-iso > [data-hv-id]:not(.hv-iso-keep) { opacity: 0.25; pointer-events: none; }
svg.hv-iso > .hv-ablayer, svg.hv-iso > rect.hv-artboard { opacity: 0.4; }
.hv-iso-crumb { position: absolute; top: 8px; left: 8px; z-index: 7; display: flex; align-items: center; gap: 8px;
  background: var(--panel, #fff); border: 1px solid var(--hair); border-radius: var(--r); padding: 3px 8px 3px 4px; box-shadow: 0 1px 4px rgba(0,0,0,0.12); }
.hv-iso-back { font: inherit; font-size: 12px; line-height: 1; cursor: pointer; border: 1px solid var(--hair); border-radius: 3px; background: #fff; color: var(--ink); padding: 3px 7px; }
.hv-iso-back:hover { border-color: var(--accent); color: var(--accent); }
.hv-iso-name { font-size: 12px; color: var(--muted); }
/* Epic C — Recolor swatch grid. `flex: 0 0 20px` + min/max + align-self pin each swatch to a
   fixed 20×20 box so no flex stretch/shrink (from any ancestor) can collapse them into thin tall bars. */
.insp-recolor-grid { display: flex; flex-wrap: wrap; align-items: flex-start; align-content: flex-start; gap: 4px; }
.insp-recolor-sw { flex: 0 0 20px; box-sizing: border-box; width: 20px; height: 20px; min-width: 20px; max-width: 20px; min-height: 20px; max-height: 20px; align-self: center; padding: 0; border: 1px solid var(--hair); border-radius: 3px; cursor: pointer; }
.insp-recolor-sw:hover { outline: 1px solid var(--accent); }
/* Recolor mode in the dock Colour panel: a back/Done bar above the solo picker. */
.cp-recolor-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.cp-recolor-bar .cp-recolor-lab { font-size: 11px; color: var(--label); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-recolor-done { flex: 0 0 auto; }
/* Pen-tool anchor markers (while a path is being drawn) */
.hv-pen-anchor { fill: #fff; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-pen-anchor.first { fill: var(--accent); }
.hv-pen-anchor.close { fill: var(--accent); stroke: #fff; stroke-width: 1.5; }
/* Read-only anchor dots of the selected path (pen tool) — shows where +/− will act */
.hv-pen-point { fill: #fff; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
/* Pen direction handles (bezier tangents shown while drawing) */
.hv-pen-handle-line { stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; opacity: 0.6; }
.hv-pen-handle { fill: var(--accent); stroke: #fff; stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
/* Transform-tool bounding box + scale handles */
.hv-xform-box { fill: none; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
/* before/after: dashed ghost of the original bounds shown during a scale */
.hv-xform-ghost { fill: none; stroke: var(--muted); stroke-width: 1; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; pointer-events: none; opacity: 0.7; }
/* rotation handles — visible circles set outside the corners; subtle in scale mode,
   emphasized in rotate mode. (cursor 'grab' as a stand-in for a rotate cursor) */
.hv-xform-rot { fill: #fff; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: all; cursor: grab; opacity: 0.45; }
.hv-xform-rot:hover { opacity: 1; fill: var(--accent); }
.hv-xform-rotate .hv-xform-rot { opacity: 1; }
.hv-xform-handle { fill: #fff; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: all; }
.hv-xform-handle:hover { fill: var(--accent); }
.hv-xform-handle.dragging { fill: var(--accent); }
/* on-canvas gradient editor (G.4): a direction line/ring + draggable endpoint handles */
.hv-gradedit-line, .hv-gradedit-ring { fill: none; stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-gradedit-ring { opacity: 0.7; }
.hv-gradedit-handle { fill: #fff; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; pointer-events: all; cursor: grab; }
.hv-gradedit-handle:hover { fill: var(--accent); }
.hv-xform-nw, .hv-xform-se { cursor: nwse-resize; }
.hv-xform-ne, .hv-xform-sw { cursor: nesw-resize; }
.hv-xform-n,  .hv-xform-s  { cursor: ns-resize; }
.hv-xform-e,  .hv-xform-w  { cursor: ew-resize; }
/* Drag-select marquee / lasso */
.hv-marquee { fill: rgba(0, 122, 255, 0.12); stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-guide { stroke: #ff2d92; stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
/* Persistent ruler guides: a thin cyan line plus a wide transparent hit target. */
.hv-guideobj { stroke: #18b6c9; stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-guidehit { stroke: transparent; stroke-width: 9; vector-effect: non-scaling-stroke; pointer-events: stroke; }
/* Extra-artboard chrome frames + labels (Epic A) — visual only, stripped on export. */
.hv-abframe { fill: none; stroke: #4a90d9; stroke-width: 1.5; stroke-dasharray: 5 3; vector-effect: non-scaling-stroke; pointer-events: none; }
.hv-ablabel { fill: #4a90d9; font: 12px var(--ui-font, system-ui, sans-serif); pointer-events: none; }
.insp-ab-row .insp-ab-name { flex: 1; min-width: 0; }
input.insp-ab-name { background: var(--field-bg, #2a2a2e); color: inherit; border: 1px solid var(--border, #3a3a3e); border-radius: 4px; padding: 2px 6px; }
.xform-readout { position: fixed; z-index: 1100; background: rgba(0, 0, 0, 0.82); color: #fff; font: 11px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 3px 7px; pointer-events: none; white-space: nowrap; }
.xform-readout[hidden] { display: none; }

/* Dropdown menus in the panel heads */
.menu {
  position: relative;
  display: inline-flex;
}
.menu[hidden] { display: none; }

.menu-list {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;          /* open rightward from the trigger (keeps left-edge menus in bounds) */
  right: auto;
  z-index: 40;
  min-width: 190px;
  background: var(--bg);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 4px;
  gap: 2px;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.12);
}
.menu-list[hidden] { display: none; }
/* Right-edge menus (the Layers kebab) open leftward so they stay in bounds. */
.menu[data-menu="layers"] .menu-list { left: auto; right: 0; }
/* right-click context menu reuses .menu-list styling but is viewport-positioned */
.context-menu { position: fixed; top: auto; right: auto; z-index: 1000; max-height: 80vh; overflow-y: auto; }
/* Object context panel = style editors (canvas panel adds an actions grid). overflow-x
   hidden kills the stray horizontal scrollbar (overflow-y:auto otherwise computes
   overflow-x:auto); min-width:0 lets the rows shrink to the panel width. */
.context-panel { width: 248px; padding: 0; overflow-x: hidden; background: var(--bg); border: 1px solid var(--line); box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.12); display: flex; flex-direction: column; }
.context-panel .ctx-head { font-size: 12px; font-weight: 600; letter-spacing: 0.02em; padding: 8px 12px; border-bottom: 1px solid var(--line); }
.context-panel .ctx-style { padding: 12px; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.context-panel .ctx-style > div { min-width: 0; }
/* persistent floating Properties window: a draggable titlebar over a live body */
.float-panel { position: fixed; z-index: 1000; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.28); }
.float-panel[hidden] { display: none; }
.fp-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 5px 6px 5px 12px; border-bottom: 1px solid var(--line); cursor: move; user-select: none; -webkit-user-select: none; touch-action: none; }
.fp-title { font-size: 12px; font-weight: 600; letter-spacing: 0.02em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fp-close { flex: 0 0 auto; width: 22px; height: 22px; padding: 0; border: 1px solid transparent; background: none; color: var(--muted); font-size: 16px; line-height: 1; cursor: pointer; display: grid; place-items: center; }
.fp-close:hover { color: #fff; background: var(--accent); border-color: var(--accent); }
.fp-body { padding: 11px 12px; display: flex; flex-direction: column; gap: 15px; min-width: 0; overflow-y: auto; max-height: calc(100vh - 130px); }
.fp-body > div { min-width: 0; }
.context-panel .ctx-actions { border-top: 1px solid var(--line); padding: 6px; display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px; }
.grid-item { aspect-ratio: 1 / 1; display: grid; place-items: center; border: 1px solid var(--line); background: var(--bg); color: var(--fg); font-size: 15px; line-height: 1; cursor: pointer; padding: 0; }
.grid-item:hover { background: #f2f2f2; }
.grid-item:disabled { opacity: 0.3; cursor: default; }
.grid-item.text { font-size: 8px; padding: 2px; text-align: center; overflow: hidden; }
.grid-sep { grid-column: 1 / -1; height: 1px; background: var(--line); margin: 1px 0; }

.menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  background: var(--bg);
  color: var(--fg);
  padding: 7px 9px;
  font: inherit;
  white-space: nowrap;
}
.menu-item:not(:disabled):hover { border-color: var(--line); }
.menu-item:disabled { opacity: 0.4; cursor: default; }
.menu-item.checked { font-weight: 600; }
.menu-check {
  width: 12px;
  flex: 0 0 12px;
  text-align: center;
}
.menu-label { flex: 1 1 auto; }
.menu-item.checked .menu-check { color: var(--accent); }
/* "edited" pill: the live arrangement diverges from the selected baseline */
.menu-badge { flex: 0 0 auto; margin-left: 6px; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase; color: var(--accent); border: 1px solid var(--accent); border-radius: 3px; padding: 0 4px; line-height: 15px; }
/* manageable menu row (Layout profiles): label + inline rename / delete buttons.
   The label carries the same real .menu-check gutter as a plain item (no faked indent). */
.menu-row { padding: 2px 6px 2px 9px; gap: 2px; }
.menu-row .menu-rowlabel { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; min-width: 0; text-align: left; padding: 5px 5px 5px 9px; border: 1px solid transparent; background: var(--bg); color: var(--fg); font: inherit; cursor: pointer; white-space: nowrap; }
.menu-row .menu-rowlabel.checked { font-weight: 600; }
.menu-row .menu-rowlabel.checked .menu-check { color: var(--accent); }
.menu-row .menu-rowlabel .menu-label { overflow: hidden; text-overflow: ellipsis; }
.menu-row .menu-rowlabel:hover { border-color: var(--line); }
.menu-row .menu-rowbtn { flex: 0 0 auto; width: 24px; height: 26px; border: 1px solid transparent; background: var(--bg); color: var(--muted); font-size: 12px; cursor: pointer; }
.menu-row .menu-rowbtn:hover { color: #fff; background: var(--accent); border-color: var(--accent); }
.menu-sep {
  height: 1px;
  background: var(--line);
  opacity: 0.4;
  margin: 3px 2px;
}

.status-bar {
  min-height: 48px;
}

.force-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  user-select: none;
  padding: 0 6px;
  cursor: pointer;
  color: var(--fg-muted, #c9c9c9);
}

.force-toggle input {
  accent-color: #f0a020;
  cursor: pointer;
}

.badge {
  display: inline-block;
  min-width: 18px;
  padding: 0 6px;
  margin-left: 4px;
  font-size: 11px;
  line-height: 16px;
  border-radius: 8px;
  background: #444;
  color: #fff;
  text-align: center;
}

.badge.badge-busy { background: #2a7adf; }
.badge.badge-fail { background: #c0382b; }

/* Processor panel — the pipeline as a vertical flow rail (stacked stage cards with
   flow connectors, drag to reorder, expand for settings). */
.processor-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 8px; }
.proc-rail { display: flex; flex-direction: column; gap: 10px; }
.proc-target { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); min-width: 0; }
.proc-target-ic { flex: 0 0 auto; }
/* The currently-loaded image: a real thumbnail makes "what am I processing" unmistakable. */
.proc-target-thumb { flex: 0 0 auto; width: 34px; height: 34px; object-fit: cover; border: 1px solid var(--line); border-radius: var(--r);
  background: repeating-conic-gradient(#e9e9e9 0% 25%, #fff 0% 50%) 50% / 12px 12px; }
.proc-target-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; color: var(--ink); font-weight: 600; font-size: 13px; }
.proc-target.batch .proc-target-name { color: var(--muted); }
.proc-target.batch .proc-target-name { font-style: italic; }
.proc-target-swap { flex: 0 0 auto; height: 22px; min-width: 26px; font-size: 12px; }
.proc-target-swap.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.proc-stages { display: flex; flex-direction: column; gap: 14px; }
.proc-stage { position: relative; border: 1px solid var(--hair); border-radius: var(--r); background: #fff; }
.proc-stage.off { opacity: 0.55; }
.proc-stage.dragging { opacity: 0.4; }
.proc-stage.expanded { border-color: var(--hair-2); }
/* flow connector: a short vertical tick rising into each card after the first */
.proc-stage:not(:first-child)::before { content: ""; position: absolute; left: 22px; top: -14px; width: 2px; height: 14px; background: var(--hair-2); }
.proc-stage-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; }
.proc-grip { cursor: grab; color: var(--muted); user-select: none; font-size: 12px; line-height: 1; }
.proc-stage-title { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 1px; background: none; border: 0; padding: 0; cursor: pointer; text-align: left; }
.proc-stage-title .stage-name { font-weight: 600; font-size: 12.5px; color: var(--ink); }
.proc-stage-title .stage-note { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.proc-stage .stage-caret { color: var(--muted); flex: 0 0 auto; }
.proc-stage .pipeline-detail-body { border-top: 1px solid var(--hair); padding: 8px 10px; margin: 0; }
/* Standard chin: pinned below the scrolling stage list, ink top-border (matches
   .library-chin / .insp-foot). Run is pushed to the right edge. */
.processor-chin { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; border-top: 1px solid var(--ink); padding: 5px 8px; }
.processor-chin:empty { display: none; }
/* Run — one inky chin control (black-on-white, hover punches to black). Compact, not the
   divergent blue primary-button. */
.processor-chin .proc-run {
  margin-left: auto; height: 24px; padding: 0 12px; font-size: 11.5px; font-weight: 600;
  border: 1px solid var(--ink); background: #fff; color: var(--ink); cursor: pointer; border-radius: var(--r);
}
.processor-chin .proc-run:hover:not(:disabled) { background: var(--ink); color: #fff; }
.processor-chin .proc-run:disabled { opacity: 0.4; cursor: default; }
.rail-section.collapsed .processor-chin { display: none; }
/* Contextual dim: nothing to process (no raster selected, not batch) — muted, but the
   panel stays put. It un-dims + reveals the moment a raster becomes the subject. */
.rail-section.processor.dimmed .processor-body, .rail-section.processor.dimmed .processor-chin { opacity: 0.45; }
.rail-section.processor.dimmed .sec-label::after { content: " · idle"; color: var(--muted); font-weight: 400; }
.proc-foot-hint { font-size: 11px; color: var(--muted); }
.proc-foot-load { font: inherit; font-size: 11px; height: 24px; padding: 0 10px; border: 1px solid var(--ink); background: #fff; color: var(--ink); cursor: pointer; border-radius: var(--r); }
.proc-foot-load:hover { background: var(--ink); color: #fff; }
/* Auto-pipeline surface (#50): the analyzer's read + suggested compose, above the strip. */
.proc-auto { display: flex; flex-direction: column; gap: 8px; padding: 9px 10px; border: 1px solid var(--hair-2); border-radius: var(--r); background: var(--paper, #fafafa); }
.proc-auto.muted { color: var(--muted); font-size: 11.5px; font-style: italic; }
.proc-auto-head { display: flex; align-items: center; gap: 7px; min-width: 0; }
.proc-auto-badge { flex: 0 0 auto; font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; padding: 1px 6px; border-radius: 9px; background: var(--ink); color: #fff; }
.proc-auto-sum { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; }
.proc-auto-steps, .proc-auto-offered { display: flex; flex-direction: column; gap: 5px; }
.proc-auto-none { font-size: 11.5px; color: var(--muted); }
.proc-plan-step { display: flex; align-items: baseline; gap: 6px; font-size: 11.5px; min-width: 0; }
.proc-plan-cap { flex: 0 0 auto; font-weight: 600; color: var(--ink); }
.proc-plan-why { flex: 1 1 auto; color: var(--muted); overflow: hidden; text-overflow: ellipsis; }
.proc-plan-badge.needs { flex: 0 0 auto; font-size: 10px; padding: 0 5px; border: 1px solid var(--hair-2); border-radius: 8px; color: var(--muted); white-space: nowrap; }
.proc-plan-add { flex: 0 0 auto; font: inherit; font-size: 10.5px; height: 19px; padding: 0 8px; border: 1px solid var(--ink); background: #fff; color: var(--ink); cursor: pointer; border-radius: var(--r); }
.proc-plan-add:hover { background: var(--ink); color: #fff; }
.proc-auto-apply { align-self: flex-start; height: 24px; padding: 0 14px; font-size: 11.5px; font-weight: 600; border: 1px solid var(--ink); background: var(--ink); color: #fff; cursor: pointer; border-radius: var(--r); }
.proc-auto-apply:hover:not(:disabled) { opacity: 0.85; }
.proc-auto-apply:disabled { opacity: 0.4; cursor: default; }
.proc-auto-offered { border-top: 1px dashed var(--hair-2); padding-top: 7px; }
.proc-auto-offered-lbl { font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }

/* Intent-first stage controls (#49): Outcome picker → Auto badge → collapsed Advanced. */
.intent-auto { display: flex; align-items: center; gap: 6px; margin: 4px 0 2px; font-size: 11.5px; flex-wrap: wrap; }
.intent-auto.muted { color: var(--muted); font-style: italic; }
.intent-auto-tag { flex: 0 0 auto; font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; padding: 1px 6px; border-radius: 9px; background: var(--ink); color: #fff; }
.intent-auto-model { color: var(--ink); font-weight: 600; }
.intent-auto-need { flex: 0 0 auto; font: inherit; font-size: 10.5px; padding: 0 7px; height: 19px; border: 1px solid var(--hair-2); border-radius: 8px; background: #fff; color: var(--muted); cursor: pointer; }
.intent-auto-need:hover { background: var(--ink); color: #fff; }
.stage-adv-toggle { display: block; width: 100%; text-align: left; margin-top: 8px; padding: 4px 0; font: inherit; font-size: 11.5px; font-weight: 600; color: var(--muted); background: none; border: 0; border-top: 1px solid var(--hair); cursor: pointer; }
.stage-adv-toggle:hover { color: var(--ink); }
.stage-adv { padding-top: 4px; }

/* Settings → AI models & tools: registry-driven capability/model inventory (#51) */
.cap-group { border: 1px solid var(--hair); border-radius: var(--r); padding: 8px 10px; margin: 8px 0; }
.cap-group-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.cap-group-title { font-weight: 600; color: var(--ink); font-size: 12.5px; }
.cap-group-count { color: var(--muted); font-size: 11px; }
.cap-model { display: flex; align-items: baseline; gap: 8px; padding: 2px 0; font-size: 11.5px; }
.cap-model-name { color: var(--ink); flex: 0 0 auto; }
.cap-model-meta { color: var(--muted); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cap-model-state { flex: 0 0 auto; font-size: 10px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; padding: 1px 6px; border-radius: 9px; }
.cap-model-state.is-ready { background: var(--ink); color: #fff; }
.cap-model-state.is-need { border: 1px solid var(--hair-2); color: var(--muted); }
.cap-model-state.is-soon { color: var(--muted); font-style: italic; text-transform: none; font-weight: 500; letter-spacing: 0; }
.cap-group-action { display: flex; align-items: center; gap: 8px; margin-top: 6px; }

/* Object-removal (LaMa) cleanup overlay + launcher (#56) */
.proc-cleanup-btn { display: block; width: 100%; margin: 6px 0 2px; padding: 7px 10px; font: inherit; font-size: 12px; font-weight: 600;
  text-align: left; color: var(--ink); background: #fff; border: 1px solid var(--hair); border-radius: var(--r); cursor: pointer; }
.proc-cleanup-btn:hover { background: var(--surface); border-color: var(--hair-2); }
.cleanup-overlay { position: fixed; inset: 0; z-index: 9000; background: rgba(0,0,0,0.18); }
.cleanup-canvas { position: fixed; cursor: crosshair; touch-action: none; outline: 1px dashed var(--ink); outline-offset: 0; }
.cleanup-bar { position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; background: #fff; border: 1px solid var(--hair); border-radius: var(--r); box-shadow: 0 4px 20px rgba(0,0,0,0.25); }
.cleanup-hint { font-size: 12px; color: var(--muted); }
.cleanup-bar input[type="range"] { width: 120px; }

/* Degradation-fix row (#58): compact denoise / de-JPEG / deblur buttons */
.proc-fix-row { display: flex; gap: 6px; margin: 2px 0 4px; }
.proc-fix-btn { flex: 1 1 0; padding: 6px 4px; font: inherit; font-size: 11px; font-weight: 600; color: var(--ink);
  background: #fff; border: 1px solid var(--hair); border-radius: var(--r); cursor: pointer; }
.proc-fix-btn:hover { background: var(--surface); border-color: var(--hair-2); }
.stage-restore-note { margin: 2px 0; font-size: 11.5px; color: var(--muted); line-height: 1.4; }

/* compact Properties "Process" pointer (the pipeline lives in the Processor panel now) */
.raster-tools-compact { display: flex; flex-direction: column; gap: 8px; }

.jobs-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px 0;
}

.jobs-empty {
  padding: 18px;
  text-align: center;
  color: var(--fg-muted, #888);
}

.job-row {
  display: grid;
  grid-template-columns: 84px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  background: var(--bg);
}

.job-row .job-status {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: center;
  padding: 4px 0;
  background: #eee;
  color: #555;
}

.job-row.queued .job-status { background: #e3edff; color: #1b4f9b; }
.job-row.running .job-status { background: var(--accent); color: #fff; }
.job-row.done .job-status { background: #dff3e3; color: #1e6b34; }
.job-row.failed .job-status { background: #fde3df; color: #9b2c20; }
.job-row.cancelled .job-status { background: #eee; color: #666; }

.job-row .job-meta { min-width: 0; }
.job-row .job-summary {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.job-row .job-log {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.job-row .job-actions {
  display: flex;
  gap: 6px;
}
.job-row .job-actions .ghost-button {
  padding: 4px 8px;
  font-size: 12px;
}

.job-progress {
  position: relative;
  height: 14px;
  background: #e6e6e6;
  overflow: hidden;
  margin: 4px 0 2px;
}
.job-progress-bar {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--accent);
  transition: width 150ms ease;
}
.job-progress-label {
  position: relative;
  display: block;
  text-align: center;
  font-size: 10px;
  color: #fff;
  line-height: 14px;
  text-shadow: 0 0 2px rgba(0,0,0,0.5);
}
.job-outputs {
  font-size: 11px;
  color: #1e6b34;
  margin-top: 2px;
}

#status-text {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#status-text.status-error {
  color: #c0392b;
  font-weight: 600;
}

.form-hint.status-error {
  color: #ff7878;
}

#status-text.status-clickable {
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

#status-text.status-clickable:hover {
  color: #ffb070;
}

.topbar-info {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.output-panel {
  grid-template-rows: auto auto 1fr;
}

.output-picker {
  display: flex;
  gap: 6px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  overflow-y: hidden;
}

.output-pick {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg);
  padding: 4px 8px;
  font: inherit;
  cursor: pointer;
  white-space: nowrap;
  flex: 0 0 auto;
}

.output-pick.active {
  background: #000;
  color: #fff;
}

.modal-root {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 50;
}

.modal-root[hidden] { display: none; }

.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.7);
}

.modal-window {
  position: relative;
  background: var(--bg);
  border: 1px solid var(--line);
  width: min(960px, 92vw);
  max-height: 88vh;
  display: grid;
  grid-template-rows: auto 1fr;
}
.modal-window.modal-narrow { width: min(460px, 92vw); }

/* ---- Pipeline stage cards (Processor dock panel) ---- */
.gallery-cell.processed .gallery-caption { color: var(--accent); }

/* stage toggle checkbox (name/note/caret are styled by .proc-stage-* above) */
.stage-toggle { flex: 0 0 auto; width: 15px; height: 15px; cursor: pointer; }

/* expanded stage settings — dense form grid (label -> control -> tiny hint) */
.pipeline-detail-body.form { padding: 11px 13px; display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); gap: 10px 14px; align-content: start; }
.pipeline-detail-body .form-row { display: flex; flex-direction: column; align-items: stretch; gap: 3px; }
.pipeline-detail-body .form-label { font-size: 11px; font-weight: 600; color: var(--muted); letter-spacing: 0.02em; }
.pipeline-detail-body .form-hint { font-size: 10.5px; line-height: 1.3; color: var(--muted); opacity: 0.8;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-clamp: 2; overflow: hidden; }
.pipeline-detail-body select, .pipeline-detail-body .form-input { width: 100%; box-sizing: border-box; padding: 5px 7px; }
.pipeline-detail-body .form-range { grid-template-columns: 1fr 30px; gap: 9px; }
.pipeline-detail-body .form-range output { font-size: 11.5px; color: var(--label); }
.pipeline-detail-body .form-section { grid-column: 1 / -1; margin-top: 2px; }
.pipeline-detail-body .form-actions { grid-column: 1 / -1; }

/* ---- Library dock panel: the gallery as a first-class panel object ----
   Lives in the rail (narrow), so the grid packs tighter than the Process view's. */
/* body flexes + scrolls; the chin below stays pinned (also fixes detached scroll). */
.library-list { flex: 1 1 auto; display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; padding: 8px; }
.library-list .gallery-grid { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 6px; }
.library-list .gallery-caption { padding: 4px 6px; font-size: 11px; }
.gallery-thumb-proj { font-size: 30px; color: var(--muted); }   /* .hv project tile (no image) */
.gallery-thumb-loading { background: var(--surface); }            /* SVG thumb being rasterised */

/* Library chin — a standard chin (ink top-border) with filter (~2/3) + C/R/V toggle. */
.library-chin { flex: 0 0 auto; display: flex; gap: 8px; align-items: center; border-top: 1px solid var(--ink); padding: 7px 8px; }
.library-chin .library-filter { flex: 1 1 auto; min-width: 0; box-sizing: border-box; }
.lib-mode-btns, .lib-sort { flex: 0 0 auto; display: flex; }
.lib-mode, .lib-sort-dir, .lib-sort-key { width: 24px; height: var(--field-h); border: 1px solid var(--hair); border-left-width: 0; background: #fff; color: var(--ink); font: inherit; font-size: 11px; font-weight: 600; cursor: pointer; }
.lib-mode:first-child, .lib-sort > :first-child { border-left-width: 1px; }
.lib-mode:hover:not(.active), .lib-sort-dir:hover:not(.active), .lib-sort-key:hover { background: var(--surface); }
.lib-mode.active, .lib-sort-dir.active { background: var(--ink); color: #fff; border-color: var(--ink); }

/* invisible-scroll-with-hint: a tile bar scrolls when crowded (no visible scrollbar);
   a soft edge fade appears ONLY while it overflows (toggled by observeOverflow), so a
   bar that fits is never clipped. Reused by panel-header tile bars + viewport strips. */
.tile-scroll-x { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; min-width: 0; }
.tile-scroll-y { overflow-y: auto; overflow-x: hidden; scrollbar-width: none; min-height: 0; }
.tile-scroll-x::-webkit-scrollbar, .tile-scroll-y::-webkit-scrollbar { width: 0; height: 0; display: none; }
.tile-scroll-x.is-overflowing { -webkit-mask-image: linear-gradient(to right, transparent 0, #000 8px, #000 calc(100% - 8px), transparent 100%); mask-image: linear-gradient(to right, transparent 0, #000 8px, #000 calc(100% - 8px), transparent 100%); }
.tile-scroll-y.is-overflowing { -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 8px, #000 calc(100% - 8px), transparent 100%); mask-image: linear-gradient(to bottom, transparent 0, #000 8px, #000 calc(100% - 8px), transparent 100%); }

.primary-button {
  border: 1px solid var(--accent); background: var(--accent); color: #fff;
  padding: 7px 14px; font: inherit; cursor: pointer; font-weight: 600;
}
.primary-button:hover { filter: brightness(1.08); }

/* ---- chin (footer) live progress ---- */
.status-bar { gap: 12px; }
#status-text { flex: 0 1 auto; }
.status-progress { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; min-width: 0; }
.status-progress[hidden] { display: none; }
.status-progress-track { flex: 1 1 auto; height: 6px; background: #e6e6e6; overflow: hidden; min-width: 60px; }
.status-progress-bar { height: 100%; width: 0; background: var(--accent); transition: width 0.2s; }
.status-progress-bar.indeterminate { width: 40% !important; animation: hv-indet 1.1s ease-in-out infinite; }
.status-progress-label { flex: 0 0 auto; font-size: 12px; color: var(--muted); white-space: nowrap; }
@keyframes hv-indet { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }

.modal-search {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg);
  padding: 6px 10px;
  font: inherit;
  min-width: 160px;
}

.modal-body {
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  gap: 10px;
}

.gallery-cell {
  border: 1px solid var(--line);
  background: var(--bg);
  display: grid;
  grid-template-rows: auto auto auto;   /* square thumb governs its own height */
  text-align: left;
  padding: 0;
  font: inherit;
  color: var(--fg);
  min-width: 0;                          /* let the track shrink — don't widen the modal */
  overflow: hidden;
}

/* The active (currently-loaded) tile must stand out among 50 — accent ring + tinted frame +
   a corner pip, not a thin black outline that's lost in the grid. */
.gallery-cell.active {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.gallery-cell.active .gallery-caption { background: var(--accent); color: #fff; font-weight: 600; }
.gallery-cell.active .gallery-thumb::after {
  content: "●"; position: absolute; top: 4px; right: 5px; font-size: 11px; line-height: 1;
  color: var(--accent); text-shadow: 0 0 0 1px #fff, 0 0 3px #fff;
}
.gallery-thumb { position: relative; }

.gallery-thumb {
  width: 100%;
  aspect-ratio: 1 / 1;
  background-size: 18px 18px;
  background-position: 0 0, 0 9px, 9px -9px, -9px 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  min-width: 0;
}

.gallery-thumb-button {
  all: unset;
  display: block;
  cursor: pointer;
  width: 100%;
}

.gallery-thumb-button:focus-visible {
  outline: 2px solid #000;
  outline-offset: -2px;
}

/* compact icon actions — five fit a narrow cell without clipping. min-width:0 lets
   this grid item shrink to the (auto-fill floor) track so the flex:1 icons share it
   instead of overflowing the cell when the gallery is wide (many narrow columns). */
.gallery-actions {
  display: flex;
  gap: 2px;
  padding: 4px;
  border-top: 1px solid var(--line);
  background: var(--bg);
  min-width: 0;
}

.gallery-act {
  flex: 1 1 0;
  min-width: 0;
  height: 24px;
  border: 1px solid transparent;
  border-radius: 3px;
  background: none;
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.gallery-act:hover { background: var(--accent); color: #fff; }

/* the entire object, centred and scaled to fit the square — never cropped */
.gallery-thumb img,
.gallery-thumb svg {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}

.gallery-caption {
  border-top: 1px solid var(--line);
  padding: 6px 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
}

.gallery-empty {
  color: var(--muted);
  padding: 24px;
  text-align: center;
}

.form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.form-section {
  letter-spacing: 0.12em;
  font-size: 11px;
  text-transform: uppercase;
  border-bottom: 1px solid var(--line);
  padding-bottom: 4px;
  margin-top: 6px;
}
/* Brief highlight when Settings is deep-linked to a section (e.g. a stage routing here to
   install a missing tool), so the user lands exactly where the action is. */
.form-section.settings-focus { animation: settings-focus-pulse 1.5s ease-out; border-radius: 4px; }
@keyframes settings-focus-pulse {
  0%, 30% { background: var(--accent, #2f7fe0); color: #fff; box-shadow: 0 0 0 4px var(--accent, #2f7fe0); }
  100% { background: transparent; box-shadow: 0 0 0 0 transparent; }
}
/* A stage whose tool is missing: the "set up in Settings" affordance stacks the note + button. */
.rt-tool-setup { flex-direction: column; align-items: stretch; gap: 6px; }

.form-row {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
}

.form-row .form-hint {
  grid-column: 2 / -1;
  font-size: 11px;
  color: var(--muted);
}

.form-label {
  font-size: 12px;
}

.form-range {
  display: grid;
  grid-template-columns: 1fr 44px;
  gap: 8px;
  align-items: center;
}

.form-range input[type="range"] {
  width: 100%;
}

.form-range output {
  font-variant-numeric: tabular-nums;
  text-align: right;
  font-size: 12px;
}

.form-input,
.form select {
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg);
  padding: 6px 8px;
  font: inherit;
  min-width: 0;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

/* Export modal: a preview of what's being rendered, on a checker so transparency reads. */
.export-preview {
  display: flex; align-items: center; justify-content: center;
  max-height: 220px; padding: 8px; margin-bottom: 8px;
  border: 1px solid var(--border); border-radius: 8px;
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #e8e8ec 25%, transparent 25%), linear-gradient(-45deg, #e8e8ec 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #e8e8ec 75%), linear-gradient(-45deg, transparent 75%, #e8e8ec 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
.export-preview img { max-width: 100%; max-height: 204px; object-fit: contain; display: block; }

.about-block { font-size: 12px; color: var(--muted); display: flex; flex-direction: column; gap: 4px; }
.about-name { font-size: 14px; color: var(--fg); font-weight: 600; }
.about-ver { color: var(--muted); font-weight: 400; font-size: 12px; }

.info-grid {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: 4px 12px;
  font-size: 12px;
}

.info-key {
  color: var(--muted);
}

.info-val {
  word-break: break-all;
}

/* ---- Image Info panel: a preview header + the file actions that used to crowd
   the gallery thumbnail (copy name / path / reveal / open) + the primary Load. ---- */
.info-modal { gap: 14px; }
/* Info lives in a (narrower) dock panel now — stack the header + tighten the grid. */
.rail-section.info .info-head { flex-direction: column; align-items: stretch; }
.rail-section.info .info-preview { width: 100%; height: 132px; }
.rail-section.info .info-grid { grid-template-columns: 88px minmax(0, 1fr); }
.rail-section.info .info-actions { gap: 5px; }
.info-head { display: flex; gap: 14px; align-items: flex-start; }
.info-preview {
  flex: 0 0 auto; width: 112px; height: 112px; border: 1px solid var(--hair);
  background: var(--surface); display: grid; place-items: center; overflow: hidden;
}
.info-preview img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.info-preview-proj { font-size: 56px; color: var(--muted); }
/* canvas highlights while a library cell is dragged over it */
.lib-drop-over { outline: 2px dashed var(--accent); outline-offset: -6px; }
.info-headmeta { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.info-name { font-weight: 600; font-size: 14px; word-break: break-all; line-height: 1.25; }
.info-sub { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* A standard icon chin: one uniform row of bordered tool-buttons (same family as
   the arrange / viewport toolbars), wrapping cleanly on a narrow panel instead of
   the old ragged mix of wide text buttons. Each action is icon-only with a title. */
.info-actions { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin-top: 4px; }
/* The emphasised action (Load into canvas / Open project) reads as a SOLID ink
   fill — same 30px footprint as its peers, just filled; hover inverts. */
.info-actions .info-primary { border-color: var(--ink); background: var(--ink); color: #fff; }
.info-actions .info-primary:hover:not(:disabled) { background: #fff; color: var(--ink); border-color: var(--ink); }
/* The raster/vector live-preview Keep/Revert row keeps its filled primary CTA. */
.rt-actions .primary-button { border-color: var(--ink); background: var(--ink); color: #fff; font-weight: 600; }
.rt-actions .primary-button:hover { filter: none; background: #fff; color: var(--ink); }
/* Delete is pushed to the trailing edge — muted until hover, loud once armed
   (first click arms → ‼, second within the open panel confirms). */
.info-actions .danger-button { margin-left: auto; color: #b42318; }
.info-actions .danger-button:hover:not(:disabled) { background: #b42318; color: #fff; border-color: #b42318; }
.info-actions .danger-button.danger-armed { background: #b42318; color: #fff; border-color: #b42318; font-weight: 700; }

/* Name + Path are click-to-copy (the explicit Copy buttons are gone). */
.info-copy { border: 0; background: none; padding: 0; margin: 0; font: inherit; color: inherit; text-align: left; cursor: copy; word-break: break-all; }
.info-copy:hover { color: var(--accent); }
.info-name.info-copy { font-size: 14px; font-weight: 600; line-height: 1.25; }

/* Floating in-app prompt (replaces window.prompt for renames / save-as). */
.hv-float-input { z-index: 1200; min-width: 230px; max-width: 280px; border: 1px solid var(--ink); background: var(--bg); box-shadow: 0 3px 12px rgba(0, 0, 0, 0.3); padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.hv-float-label { font-size: 11px; color: var(--muted); letter-spacing: 0.02em; }
.hv-float-input input { border: 1px solid var(--hair); background: #fff; color: var(--fg); padding: 5px 8px; font: inherit; font-size: 13px; }
.hv-float-input input:focus { outline: none; border-color: var(--ink); }

/* Live text editor overlay (the text tool). The WRAPPER is a 0×0 anchor at the viewport
   origin carrying the stage's screen CTM as a CSS matrix, so the inner editable can use
   plain local user-units and still land pixel-exact under zoom/pan/rotate. pointer-events
   pass through the wrapper (only the text box itself is interactive) so a click on bare
   canvas commits + starts the next action. The matching <text> is hidden while editing. */
.hv-text-overlay-wrap { position: fixed; left: 0; top: 0; z-index: 1200; transform-origin: 0 0; pointer-events: none; }
.hv-text-overlay {
  position: absolute; pointer-events: auto; display: inline-block;
  white-space: pre; outline: none; border: 0; margin: 0; padding: 0;
  background: transparent; caret-color: currentColor; cursor: text;
  min-width: 1px; min-height: 1em;
}
text.hv-text-editing { visibility: hidden; }
/* The drag-to-create rubber-band for an AREA text box (lives in the overlay layer). */
.hv-textbox-preview { fill: none; stroke: var(--accent); stroke-width: 1; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; pointer-events: none; }

/* Font picker: the Text inspector's Font row is a button (current family, shown in its own
   face) that opens a searchable Google-Fonts browser popover. */
.font-pick { width: 100%; text-align: left; padding: 5px 8px; border: 1px solid var(--hair); background: #fff; color: var(--fg); font: inherit; font-size: 13px; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.font-pick:hover { border-color: var(--ink); }
.insp-action { width: 100%; padding: 5px 8px; border: 1px solid var(--hair); background: var(--bg); color: var(--fg); font: inherit; font-size: 12px; cursor: pointer; }
.insp-action:hover { border-color: var(--ink); }
/* "Actions ▾" — the object-commands menu trigger at the top of the Properties panel. */
.insp-actions-btn { width: 100%; padding: 6px 8px; margin-bottom: 14px; border: 1px solid var(--hair); border-radius: var(--r); background: var(--bg); color: var(--fg); font: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0.02em; cursor: pointer; }
.insp-actions-btn:hover { border-color: var(--ink); background: var(--panel); }

.font-browser { position: fixed; z-index: 1300; width: 304px; max-height: 384px; display: flex; flex-direction: column;
  border: 1px solid var(--ink); background: var(--bg); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.28); }
.font-search { margin: 8px 8px 6px; padding: 6px 8px; border: 1px solid var(--hair); background: #fff; color: var(--fg); font: inherit; font-size: 13px; }
.font-search:focus { outline: none; border-color: var(--ink); }
.font-srcbar { display: flex; gap: 4px; padding: 0 8px 8px; flex-wrap: wrap; border-bottom: 1px solid var(--hair); }
.font-chip { padding: 3px 9px; border: 1px solid var(--hair); background: var(--bg); color: var(--muted); font: inherit; font-size: 11px; cursor: pointer; border-radius: 999px; }
.font-chip:hover { border-color: var(--ink); color: var(--fg); }
.font-chip.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.font-row-src { font-size: 9px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); flex: 0 0 auto; border: 1px solid var(--hair); border-radius: 3px; padding: 1px 5px; }
.font-row.active .font-row-src { color: rgba(255, 255, 255, 0.85); border-color: rgba(255, 255, 255, 0.4); }
.font-results { overflow-y: auto; padding: 0 4px 6px; }
.font-sec { font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); padding: 6px 8px 2px; position: sticky; top: 0; background: var(--bg); }
.font-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; width: 100%; text-align: left; padding: 6px 8px; border: 0; background: transparent; color: var(--fg); font: inherit; font-size: 15px; cursor: pointer; border-radius: 3px; }
.font-row:hover { background: var(--hover, rgba(0, 0, 0, 0.05)); }
.font-row.active { background: var(--accent); color: #fff; }
.font-row.loading { opacity: 0.5; cursor: progress; }
.font-row.err { color: #c0392b; }
.font-row-cat { font-size: 10px; color: var(--muted); flex: 0 0 auto; }
.font-row.active .font-row-cat { color: rgba(255, 255, 255, 0.8); }
.font-empty { padding: 12px 8px; color: var(--muted); font-size: 12px; }

/* Floating tag-style rename editor (replaces window.prompt). */
.hv-rename-pop {
  z-index: 1200; min-width: 150px; max-width: 220px;
  border: 1px solid var(--ink); background: var(--bg); color: var(--fg);
  padding: 4px 8px; font: inherit; font-size: 13px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.28);
}

/* Colour recents are a standard chin — identical treatment to the Object panel's
   align chin (.insp-foot): ink top-border, 7px 10px, flush + full-width. margin-top:
   auto pins it to the bottom; the negative margins bleed past the embedded picker's
   8px body padding so the border spans edge-to-edge and the chin sits flush. */
.cp-chin { margin-top: auto; border-top: 1px solid var(--ink); padding: 7px 10px; }
/* Embedded (docked Colour panel): margin-top:auto (via the `auto` top in the shorthand)
   already pins the recents chin to the panel's bottom when it's short — the cp-window is a
   flex column. We deliberately do NOT position:sticky it: when the panel overflows and
   scrolls, a sticky bottom strip rides up and covers the .cp-actions row (None/OK/Cancel)
   sitting just above it, making those buttons unclickable. In-flow keeps recents below the
   actions, so both stay reachable. Negative margins bleed past the 8px body padding so the
   border spans edge-to-edge; opaque bg keeps it clean. */
.cp-embedded .cp-chin { margin: auto -8px -8px; background: var(--bg); }

/* Keyboard-shortcuts modal: section headers span the grid; key column auto-fits. */
.shortcut-grid { grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; }
.shortcut-grid .info-key { white-space: nowrap; color: var(--fg); }
.shortcut-grid .info-val { word-break: normal; }
.shortcut-section {
  grid-column: 1 / -1;
  margin-top: 12px;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding-bottom: 3px;
}
.shortcut-section:first-child { margin-top: 0; }

.panel-focused {
  box-shadow: inset 0 0 0 1px var(--line);
}

/* status-bar (ready bar) tool cluster: undo / redo / help / collapse — one
   consistent square-box object, same size as the toolstrip & viewport buttons. */
.status-tools { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.bar-sep { width: 1px; align-self: stretch; margin: 4px 2px; background: var(--line); }
.bar-btn {
  position: relative; box-sizing: border-box; width: 30px; height: 30px;
  border: 1px solid var(--line); background: var(--bg); color: var(--fg);
  font: inherit; font-size: 15px; line-height: 1; cursor: pointer; display: grid; place-items: center;
}
.bar-btn:hover:not(:disabled) { background: var(--accent); color: #fff; border-color: var(--accent); }
.bar-btn:disabled { opacity: 0.35; cursor: default; }
.bar-btn[data-key]::after {
  content: attr(data-key);
  position: absolute; right: 1px; bottom: 0;
  font-size: 8px; line-height: 1; font-weight: 600;
  color: var(--muted); pointer-events: none;
}
.bar-btn:hover:not(:disabled)[data-key]::after { color: rgba(255, 255, 255, 0.8); }
.bar-btn.bar-text { width: auto; padding: 0 10px; font-size: 12px; }
.bar-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* ---- Customize-layout mode: drag toolbar tiles between the picture-frame bars ---- */
.app.editor.customizing .toolstrip,
.app.editor.customizing .stage-toolbar,
.app.editor.customizing .actionbar,
.app.editor.customizing .viewport-controls { outline: 1px dashed var(--accent); outline-offset: -1px; background: color-mix(in srgb, var(--accent) 6%, var(--bg)); }
/* the bottom bar's viewport cluster is centered inside a wider .panel-foot, so tint the
   whole foot too — otherwise only the button group turns blue and the bar reads white. */
.app.editor.customizing .panel-foot { background: color-mix(in srgb, var(--accent) 6%, var(--bg)); }
/* the Layers artboard chin reads as part of the customizable frame too */
.app.editor.customizing .layers-foot { outline: 1px dashed var(--accent); outline-offset: -1px; background: color-mix(in srgb, var(--accent) 6%, var(--bg)); }
/* tiles become draggable handles; their normal click is suppressed while customizing */
.app.editor.customizing .tool-button { cursor: grab; }
.app.editor.customizing .tool-button:active { cursor: grabbing; }
.app.editor.customizing .tool-button.dragging { opacity: 0.4; outline: 1px solid var(--accent); }
/* disabled tiles stay draggable in customize mode (you're arranging, not using them) */
.app.editor.customizing .tool-button:disabled { opacity: 1; cursor: grab; }
/* dividers become visible, grabbable chips while customizing (drag to move,
   right-click a bar to add one / a divider to remove it) */
.app.editor.customizing .tool-sep,
.app.editor.customizing .tool-vsep,
.app.editor.customizing .vp-sep { background: var(--accent); opacity: 0.55; cursor: grab; border-radius: 2px; }
.app.editor.customizing .tool-sep { height: 4px; margin: 2px 4px; }
.app.editor.customizing .tool-vsep,
.app.editor.customizing .vp-sep { width: 4px; margin: 3px 2px; }
.app.editor.customizing .tool-sep.dragging,
.app.editor.customizing .tool-vsep.dragging,
.app.editor.customizing .vp-sep.dragging { opacity: 1; }

@media (max-width: 700px) {
  .form-row {
    grid-template-columns: 1fr;
  }
  .form-row .form-hint {
    grid-column: 1;
  }
}

@media (max-width: 1100px) {
  .main-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: minmax(160px, 220px) minmax(0, 1fr);
  }

  .library {
    grid-column: 1 / -1;
  }

}

@media (max-width: 700px) {
  .main-grid {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(160px, 200px) minmax(0, 1fr) minmax(0, 1fr);
  }

  .library {
    grid-column: auto;
  }
}

/* ===================== App-window mode (standalone / WCO) =====================
   Active only when launched via launch.sh as a Chromium app window (the JS adds
   `.app-window`, and `.wco` when the Window-Controls-Overlay is live). Window
   min/max/close are left to the native window manager — no custom buttons. Gated
   so the normal in-browser layout is completely unaffected.

   In app-window mode the header doubles as the OS titlebar: empty header areas
   drag the window; interactive controls opt out. */
.app-window .topbar {
  -webkit-app-region: drag;
  app-region: drag;
}
.app-window .topbar button,
.app-window .topbar a,
.app-window .topbar select,
.app-window .topbar input,
.app-window .topbar .menu,
.app-window .topbar .menu-list {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

/* When the Window-Controls-Overlay is active, the native min/max/close buttons
   sit in a reserved corner; `--wco-*-inset` (set from JS via getTitlebarAreaRect)
   keep our header content clear of them. */
.app-window.wco .topbar {
  padding-left: calc(12px + var(--wco-left-inset, 0px));
  padding-right: calc(12px + var(--wco-right-inset, 0px));
}
