/* ═══════════════════════════════════════════════════════════════════════════
   Dimensions Suite — unified app styles
   design-system.css (loaded from public/shared/) provides:
     tokens, reset, page-tabs, sidebars, panel chrome, buttons, toolbar,
     dropdowns, sliders, number inputs, collapsible, library grid,
     toast (.toast), modal, context menu, scrollbars, zoom controls.

   This file adds ONLY what is not in design-system.css:
     1.  App shell — tab bar + layout
     2.  Model Assembler — canvas, panels, part list, scene tree
     3.  Model Assembler — property panel, lathe, export
     4.  Model Assembler — HUD overlays (hint, multi-badge, vertex, view toggle)
     5.  Model Assembler — shape cards, drag-select
     6.  Shape Library   — (app-specific layout overrides)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Local token additions (not in design-system) ── */:root {
  --sel: color-mix(in srgb, var(--accent) 20%, transparent);
}

/* design-system resets html/body but sets font to --display.
   Model assembler wants --mono as the base font. Override here. */
html, body {
  font-family: var(--display);
}

/* ─────────────────────────────────────────────
   1. APP SHELL
   ─────────────────────────────────────────────
   design-system.css handles #page-tabs / .page-tab.
   ───────────────────────────────────────────── */
#unified-app {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

/* Tab content area fills remaining height below the 36px header */
.tab-view {
  flex: 1;
  min-height: 0;      /* critical — prevents flex children ignoring overflow */
  display: flex;
  overflow: hidden;
}

/* Each sub-app fills the full tab-view */
.tab-view > * {
  width: 100%;
  height: 100%;
  flex-shrink: 0;
}

/* ─────────────────────────────────────────────
   2. MODEL ASSEMBLER — PANELS + PART LIST
   ───────────────────────────────────────────── */
#app {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

#assembler-root {
  display: flex;
  width: 100%;
  height: calc(100% - var(--header-h));
  overflow: hidden;
  margin-top: var(--header-h);
}

/* Panels — design-system provides .sidebar / .sidebar-left / .sidebar-right.
   These #id rules keep backward-compat with existing JS that queries by id. */
#left-panel {
  width: 210px;
  min-width: 210px;
  background: var(--panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

#right-panel {
  width: 270px;
  min-width: 270px;
  background: var(--panel);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ── Add / select-all utility buttons (assembler-specific) ── */
.add-btn {
  margin: 6px 10px 2px;
  padding: 5px 8px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid var(--accent);
  color: var(--accent);
  font-family: var(--mono);
  font-size: 11px;
  cursor: pointer;
  border-radius: var(--r-sm);
  text-align: center;
  letter-spacing: 1px;
}

.add-btn:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
}

.select-all-btn {
  margin: 2px 10px 4px;
  padding: 4px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 11px;
  cursor: pointer;
  border-radius: var(--r-sm);
}

.select-all-btn:hover {
  border-color: var(--text-dim);
  color: var(--text);
}

/* ── Part list ── */
#part-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0;
}

.group-header {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 2px;
  color: var(--muted);
  padding: 10px 14px 3px;
  text-transform: uppercase;
}

.part-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px 5px 14px;
  font-size: 11px;
  cursor: pointer;
  border-left: 2px solid transparent;
  transition: background .1s, border-color .1s;
  user-select: none;
}

.part-item:hover {
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}

.part-item.selected {
  background: var(--sel);
  border-left-color: var(--accent);
}

.part-item.hidden {
  opacity: .35;
}

.part-item .dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .2);
  flex-shrink: 0;
}

.part-item .name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.part-item .ic-btn {
  width: 18px;
  height: 18px;
  opacity: .35;
  cursor: pointer;
  background: none;
  border: none;
  color: var(--text);
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
}

.part-item .ic-btn:hover {
  opacity: 1;
  color: var(--accent);
}

.part-item .del-btn {
  opacity: .25;
  color: var(--red);
}

.part-item .del-btn:hover {
  opacity: 1;
  color: var(--red);
}

.part-item.in-group {
  border-left-color: color-mix(in srgb, var(--accent2) 60%, transparent);
}

.part-item.in-group.selected {
  border-left-color: var(--accent2);
}

/* ─────────────────────────────────────────────
   3. MODEL ASSEMBLER — SCENE TREE
   ───────────────────────────────────────────── */
.parts-section-label {
  font-family: var(--display);
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--muted);
  padding: 6px 14px 3px;
  text-transform: uppercase;
}

.group-row {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px 0 6px;
  height: 30px;
  cursor: pointer;
  border-left: 2px solid color-mix(in srgb, var(--accent2) 50%, transparent);
  transition: background .1s;
  user-select: none;
}

.group-row:hover {
  background: color-mix(in srgb, var(--accent2) 9%, transparent);
}

.group-row-selected {
  background: color-mix(in srgb, var(--accent2) 13%, transparent);
  border-left-color: var(--accent2);
}

.group-chev {
  font-size: 10px;
  color: var(--muted);
  width: 12px;
  flex-shrink: 0;
  transition: color .15s;
}

.group-row:hover .group-chev {
  color: var(--accent2);
}

.group-icon {
  font-size: 12px;
  color: var(--accent2);
  flex-shrink: 0;
  opacity: .85;
}

.group-name {
  flex: 1;
  font-size: 12px;
  color: var(--fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.group-count {
  font-size: 10px;
  font-family: var(--mono);
  color: var(--muted);
  flex-shrink: 0;
  background: color-mix(in srgb, var(--accent2) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent2) 20%, transparent);
  border-radius: 8px;
  padding: 0 5px;
  min-width: 16px;
  text-align: center;
}

.part-item-nested {
  padding-left: 28px;
  border-left: 2px solid color-mix(in srgb, var(--accent2) 25%, transparent);
  background: color-mix(in srgb, var(--accent2) 3%, transparent);
}

.part-item-nested:hover {
  background: color-mix(in srgb, var(--accent2) 9%, transparent);
}

.part-item-nested.selected {
  background: var(--sel);
  border-left-color: var(--accent2);
}

.inline-rename {
  flex: 1;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid var(--accent);
  border-radius: 3px;
  color: var(--fg);
  font-size: 12px;
  font-family: var(--mono);
  padding: 1px 4px;
  outline: none;
  min-width: 0;
}

/* ─────────────────────────────────────────────
   4. MODEL ASSEMBLER — CANVAS + TOOLBAR
   ─────────────────────────────────────────────
   design-system.css provides .toolbar, .tool-btn, .tool-sep, .tool-label,
   .icon. These rules are the assembler-specific overrides / positioning.
   ───────────────────────────────────────────── */
#canvas-wrap {
  flex: 1;
  position: relative;
  overflow: hidden;
}

#canvas-wrap canvas {
  display: block;
  touch-action: none;
}

#toolbar {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);  /* Max width = canvas-wrap width (already excludes both sidebars) minus gutter */
  max-width: calc(100% - 24px);
  width: max-content;
  display: flex;
  flex-wrap: nowrap;
  gap: 2px;
  background: rgba(9, 9, 12, .92);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 1px;
  backdrop-filter: blur(8px);
  z-index: 100;  /* Single row — scroll horizontally when canvas is too narrow */
  overflow-x: auto;
  overflow-y: visible;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

#toolbar::-webkit-scrollbar {
  display: none;
}

.tool-btn {
  min-width: 36px;
  height: 46px;
  padding: 0 6px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: 17px;
  transition: all .15s;
  position: relative;
  flex-shrink: 0;
}

.tool-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, .05);
}

.tool-btn.active {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent)  12%, transparent);
}

.tool-btn.active-green {
  color: var(--green);
  border-color: var(--green);
  background: color-mix(in srgb, var(--green)   12%, transparent);
}

.tool-sep {
  width: 1px;
  align-self: stretch;
  background: var(--border);
  margin: 2px 0;
}

.tool-label {
  font-size: 11px;
  letter-spacing: 1px;
  font-family: var(--display);
}

/* Per-button label under the icon */
.tb-lbl {
  font-size: 11px;
  font-family: var(--display);
  white-space: nowrap;
  line-height: 1;
}

.icon {
  width: 16px;
  height: 16px;
  display: block;
  flex-shrink: 0;
}

/* Light mode toolbar */
[data-theme="light"] #toolbar {
  background: var(--panel, #f5f4f0);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: var(--border, #ccc9c0);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}

[data-theme="light"] .tool-btn {
  color: var(--muted, #7a786e);
  background: transparent;
  border-color: transparent;
}

[data-theme="light"] .tool-btn:hover {
  color: var(--text, #1c1b18);
  background: rgba(0, 0, 0, 0.06);
  border-color: var(--border, #ccc9c0);
}

[data-theme="light"] .tool-btn.active {
  color: var(--accent, #0891b2);
  border-color: var(--accent, #0891b2);
  background: color-mix(in srgb, var(--accent, #0891b2) 12%, transparent);
}

[data-theme="light"] .tool-sep {
  background: var(--border, #ccc9c0);
}

/* ─────────────────────────────────────────────
   5. MODEL ASSEMBLER — HUD OVERLAYS
   ───────────────────────────────────────────── */

/* Hint bar */
#hint {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  color: var(--muted);
  background: rgba(9, 9, 12, .8);
  padding: 4px 14px;
  border-radius: 20px;
  pointer-events: none;
  white-space: nowrap;
}

[data-theme="light"] #hint {
  background: rgba(240, 238, 234, 0.92);
  color: var(--muted, #7a786e);
  border: 1px solid var(--border, #ccc9c0);
}

/* Multi-select badge */
#multi-badge {
  position: absolute;
  top: 12px;
  right: 12px;
  background: var(--accent);
  color: var(--bg);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  padding: 3px 8px;
  border-radius: var(--r-sm);
  display: none;
}

/* View toggle */
#view-toggle {
  position: absolute;
  bottom: 14px;
  right: 14px;
  display: flex;
  gap: 3px;
  background: rgba(9, 9, 12, .88);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 3px;
  backdrop-filter: blur(6px);
}

.view-btn {
  padding: 4px 10px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  color: var(--muted);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  cursor: pointer;
  transition: all .12s;
  text-transform: uppercase;
}

.view-btn:hover {
  color: var(--text);
  background: rgba(255, 255, 255, .05);
}

.view-btn.active {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* Vertex mode overlay (legacy — kept for reference) */
#vertex-overlay {
  position: absolute;
  top: 70px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(10, 18, 12, .92);
  border: 1px solid var(--green);
  border-radius: var(--r-md);
  padding: 5px 12px;
  font-size: 11px;
  color: var(--green);
  pointer-events: auto;
  display: none;
  letter-spacing: 1px;
  font-family: var(--display);
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

[data-theme="light"] #vertex-overlay {
  background: var(--panel, #f5f4f0);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: var(--accent2, #16a34a);
  color: var(--accent2, #16a34a);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}

/* Edit Mesh overlay — below the main toolbar, horizontally centred, auto-width */
#mesh-edit-overlay {
  position: absolute;
  top: 70px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(10, 18, 12, .92);
  border: 1px solid var(--green, #4ade80);
  border-radius: var(--r-md);
  padding: 6px 12px;
  font-size: 11px;
  color: var(--green, #4ade80);
  pointer-events: auto;
  display: none;
  flex-direction: column;
  gap: 4px;
  letter-spacing: 1px;
  font-family: var(--display);
  white-space: nowrap;
  max-width: calc(100% - 40px);  /* Row 1 grew past a narrow viewport's width once OUTLINE joined FACE / EDGE /
       VTX. With nowrap and no overflow rule the extra buttons simply spilled out
       past max-width; scrolling keeps them reachable instead. MeshEditOverlay
       teleports its SELECT and help popups to <body> precisely so this clip can't
       swallow them. */
  overflow-x: auto;
  overflow-y: visible;
  z-index: 10;
  backdrop-filter: blur(8px);
}

/* The scrollbar itself would eat a row's worth of height on desktop, so hide it
   and leave the gesture (trackpad swipe / shift-wheel / touch drag) working. */
#mesh-edit-overlay {
  scrollbar-width: none;
}

#mesh-edit-overlay::-webkit-scrollbar {
  display: none;
}

#mesh-edit-overlay.active {
  display: flex;
}

[data-theme="light"] #mesh-edit-overlay {
  background: var(--panel, #f5f4f0);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-color: var(--accent2, #16a34a);
  color: var(--accent2, #16a34a);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}

.vtx-mode-btn {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--green) 30%, transparent);
  color: color-mix(in srgb, var(--green) 60%, transparent);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  padding: 3px 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: all .15s;
  text-transform: uppercase;
}

.vtx-mode-btn:hover {
  border-color: var(--green);
  color: var(--green);
}

.vtx-mode-btn.active {
  border-color: var(--green);
  color: var(--bg);
  background: var(--green);
}

/* Transform HUD measurement labels (CSS2DObject) */
.hud-sel-label {
  color: rgba(138, 180, 212, 0.88);
  font-family: var(--mono, monospace);
  font-size: 12px;
  font-weight: 400;
  white-space: nowrap;
  pointer-events: none;
  user-select: none;
  text-shadow: 0 0 5px rgba(0, 0, 0, 0.95), 0 0 2px rgba(0, 0, 0, 1);
  letter-spacing: 0.3px;
}

[data-theme="light"] .hud-sel-label {
  color: rgba(30, 90, 140, 0.90);
  text-shadow: 0 0 4px rgba(255, 255, 255, 0.8);
}

/* Drag-select */
#drag-select-box {
  border: 1px dashed var(--accent) !important;
}

/* ─────────────────────────────────────────────
   6. MODEL ASSEMBLER — PROPERTY PANEL
   ───────────────────────────────────────────── */
#prop-content {
  flex: 1;
  overflow-y: auto;
}

.prop-section {
  border-bottom: 1px solid var(--border);
  padding: 10px 14px;
}

.prop-section-title {
  font-family: var(--display);
  font-size: 13px;
  letter-spacing: 1px;
  color: var(--muted);
  text-transform: capitalize;
  margin-bottom: 0px;
  display: flex;
  align-items: center;
}

.prop-row {
  display: flex;
  align-items: center;
  margin-bottom: 7px;
  gap: 4px;
}

.prop-label {
  font-size: 11px;
  color: var(--muted);
  width: 36px;
  flex-shrink: 0;
  text-align: left;
}

/* num-input — design-system provides .number-input; this is the legacy alias */
.num-input {
  width: 52px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--display);
  font-size: 12px;
  padding: 4px 6px;
  border-radius: var(--r-sm);
  text-align: right;
}

.num-input:focus {
  outline: none;
  border-color: var(--accent);
}

.color-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.color-label {
  font-size: 11px;
  color: var(--muted);
  flex: 1;
}

input[type=color] {
  width: 32px;
  height: 24px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: none;
  cursor: pointer;
  padding: 1px;
}

.reset-btn {
  width: 14px;
  height: 18px;
  flex-shrink: 0;
  background: none;
  border: 1px solid transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 18px;
  border-radius: var(--r-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.reset-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.no-selection {
  padding: 20px 14px;
  font-size: 11px;
  color: var(--muted);
  text-align: center;
  line-height: 1.9;
}

.section-reset {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 0;
}

.section-reset:hover {
  color: var(--accent);
}

/* ─────────────────────────────────────────────
   7. MODEL ASSEMBLER — LATHE EDITOR
   ───────────────────────────────────────────── */
.lathe-canvas-wrap {
  position: relative;
  margin-top: 8px;
}

#lathe-canvas {
  background: #080c10;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  display: block;
  cursor: crosshair;
}

.lathe-btns {
  display: flex;
  gap: 5px;
  margin-top: 6px;
  flex-wrap: wrap;
}

.lathe-btn {
  flex: 1;
  min-width: 60px;
  padding: 5px 4px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1px;
  cursor: pointer;
  border-radius: var(--r-sm);
  text-transform: uppercase;
  text-align: center;
}

.lathe-btn:hover {
  border-color: var(--accent2);
  color: var(--accent2);
}

.lathe-seg-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}

/* ─────────────────────────────────────────────
   8. MODEL ASSEMBLER — EXPORT
   ───────────────────────────────────────────── */
/* ── Import / Export — one control pair ──────────────────────────────────────
   Export is injected by dimensions-export-panel.js and decorated in
   useAppBridge (_decorateExportBtn); Import is authored in RightPanel.vue.
   Both end up as .btn-action.io-btn, so height, padding, icon size and
   letterspacing all come from the design system and the two can't drift —
   only --btn-c differs. Colours are set by id so a vendor colour class on the
   export button (which would beat a plain .btn-green) can't outrank them.
   Sharing the padding with .forge-export-sec keeps the two the same width. */
#export-area,
.forge-export-sec {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px 10px;
}

/* Import sits directly under the export panel; its own top padding would
   double up on that panel's bottom padding. */
#export-area { padding-top: 0; }

/* Material glyphs ship without a fill, so they'd paint black on their own. */
.io-btn svg { fill: currentColor; }

#forge-export-panel-export { --btn-c: var(--green);   }
#import-btn                { --btn-c: var(--accent); }

/* .export-btn (and .export-btn.secondary) lived here until Import moved onto
   .btn-action. Nothing in the Vue or core source emits either class any more;
   they're kept only until the vendor scripts are confirmed clear of them. */
.export-btn {
  width: 100%;
  padding: 8px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 2px;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: var(--r-sm);
  transition: all .15s;
}

.export-btn:hover {
  background: color-mix(in srgb, var(--accent) 25%, transparent);
}

.export-btn.secondary {
  background: transparent;
  border-color: var(--border);
  color: var(--muted);
}

.export-btn.secondary:hover {
  border-color: var(--text-dim);
  color: var(--text);
}

/* Export modal — extends design-system .modal-* */
#export-modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .8);
  z-index: 100;
  align-items: center;
  justify-content: center;
}

#export-modal.open {
  display: flex;
}

#export-modal .modal-box {
  width: 700px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0;
  border-radius: var(--r-lg);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  font-family: var(--display);
  font-size: 13px;
  letter-spacing: 2px;
  color: var(--accent);
  flex-shrink: 0;
}

#export-code {
  flex: 1;
  overflow: auto;
  padding: 16px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.7;
  color: #a0c8b0;
  background: var(--bg);
  white-space: pre;
  tab-size: 2;
}

/* .modal-footer already defined in design-system; this adds export-specific padding */
#export-modal .modal-footer {
  padding: 10px 16px;
  border-top: 1px solid var(--border);
}

/* Forge export panel — .forge-export-sec's layout is shared with #export-area
   in section 8 (Import / Export), so the two stacks line up. */
#forge-export-panel .btn {
  opacity: 1 !important;
  cursor: pointer !important;
}

/* ─────────────────────────────────────────────
   9. MODEL ASSEMBLER — TOAST (id-based override)
   ─────────────────────────────────────────────
   design-system.css defines .toast (class). The assembler uses #toast (id).
   Keep both in sync here.
   ───────────────────────────────────────────── */
#toast {
  position: fixed;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--panel2);
  border: 1px solid var(--accent);
  color: var(--text);
  font-size: 11px;
  padding: 8px 18px;
  border-radius: 20px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s, transform .25s;
  z-index: 999;
  font-family: var(--mono);
  white-space: nowrap;
  max-width: calc(100vw - 32px);
}

#toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

#toast.error {
  border-color: var(--red);
  color: var(--red);
}

/* ─────────────────────────────────────────────
   10. MODEL ASSEMBLER — SCENE SWITCHER + SCENES PANEL
   ───────────────────────────────────────────── */
.scene-btn {
  flex: 1;
  min-width: 60px;
  padding: 4px 6px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .5px;
  cursor: pointer;
  border-radius: var(--r-sm);
  text-align: center;
  transition: all .12s;
  white-space: nowrap;
}

.scene-btn:hover {
  border-color: var(--accent2);
  color: var(--accent2);
}

.scene-btn.active {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

#scenes-section {
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  max-height: 340px;
  min-height: 0;
}

#scenes-header {
  display: flex;
  align-items: center;
  padding: 7px 14px 6px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}

#scenes-header-label {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 2px;
  color: var(--accent2);
  text-transform: uppercase;
  flex: 1;
}

#scenes-save-btn {
  padding: 2px 8px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid var(--accent);
  color: var(--accent);
  font-family: var(--display);
  font-size: 10px;
  letter-spacing: 1px;
  cursor: pointer;
  border-radius: var(--r-sm);
  white-space: nowrap;
  text-transform: uppercase;
  transition: background .12s;
}

#scenes-save-btn:hover {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

#scenes-save-btn.resave {
  border-color: var(--green);
  color: var(--green);
  background: color-mix(in srgb, var(--green) 10%, transparent);
}

#scenes-save-btn.resave:hover {
  background: color-mix(in srgb, var(--green) 22%, transparent);
}

#scenes-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0;
}

.scene-item {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 5px 8px 5px 12px;
  font-size: 11px;
  cursor: pointer;
  border-left: 2px solid transparent;
  transition: background .1s, border-color .1s;
  user-select: none;
}

.scene-item:hover {
  background: color-mix(in srgb, var(--accent2) 7%, transparent);
}

.scene-item.active-scene {
  border-left-color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 10%, transparent);
}

.scene-item-icon {
  font-size: 12px;
  flex-shrink: 0;
  opacity: .7;
}

.scene-item-info {
  flex: 1;
  min-width: 0;
}

.scene-item-name {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
  font-size: 11px;
}

.scene-item-meta {
  font-size: 10px;
  color: var(--muted);
  margin-top: 1px;
  display: block;
  letter-spacing: .3px;
}

.scene-item-name-input {
  flex: 1;
  background: color-mix(in srgb, var(--accent2) 10%, transparent);
  border: 1px solid var(--accent2);
  border-radius: 3px;
  color: var(--fg);
  font-size: 11px;
  font-family: var(--mono);
  padding: 1px 5px;
  outline: none;
  min-width: 0;
}

.scene-ic-btn {
  width: 18px;
  height: 18px;
  opacity: .3;
  cursor: pointer;
  background: none;
  border: none;
  color: var(--text);
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
  border-radius: 3px;
}

.scene-ic-btn:hover {
  opacity: 1;
  color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 12%, transparent);
}

.scene-ic-btn.del-btn:hover {
  opacity: 1;
  color: var(--red);
  background: color-mix(in srgb, var(--red) 12%, transparent);
}

.scenes-empty {
  padding: 10px 14px;
  font-size: 11px;
  color: var(--muted);
  font-style: italic;
  text-align: center;
}

#scenes-chevron {
  font-size: 10px;
  color: var(--muted);
  transition: transform .15s;
}

/* ─────────────────────────────────────────────
   11. MODEL ASSEMBLER — LIBRARY PANEL (embedded)
   ─────────────────────────────────────────────
   design-system.css defines .lib-section / .lib-grid / .lib-item etc.
   These are assembler-specific overrides for the embedded #lib-section panel.
   ───────────────────────────────────────────── */
#lib-section {
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  min-height: 80px;
  height: 50vh;
  max-height: 80vh;
  position: relative;
  overflow: hidden;
}

#lib-section .panel-title {
  font-size: 11px;
  padding: 7px 14px 6px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  cursor: ns-resize;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 6px;
}

#lib-section .panel-title::after {
  content: '⠿';
  color: var(--muted);
  font-size: 10px;
  margin-left: auto;
  opacity: .45;
  letter-spacing: -1px;
}

#lib-resize-handle {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 5px;
  cursor: ns-resize;
  z-index: 10;
  background: transparent;
}

#lib-resize-handle:hover, #lib-resize-handle.dragging {
  background: color-mix(in srgb, var(--accent) 30%, transparent);
}

#lib-grid {
  flex: 1;
  overflow-y: auto;
  padding: 6px 8px;
  gap: 5px;
  align-content: start;
}

#lib-grid .lib-item {
  padding: 3px 4px 4px;
  min-height: 82px;
  height: 82px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

#lib-grid .lib-item canvas {
  width: 100%;
  height: 52px;
  min-height: 52px;
  flex-shrink: 0;
  display: block;
  object-fit: contain;
}

#lib-grid .lib-item-footer {
  padding-top: 2px;
  flex-shrink: 0;
}

#lib-grid .lib-item-name {
  font-size: 10px;
  line-height: 1.2;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
  color: var(--text);
  margin-top: 2px;
}

#lib-grid .lib-source-badge {
  font-size: 10px;
  padding: 1px 4px;
  margin-top: 2px;
  flex-shrink: 0;
}

/* Light mode */
[data-theme="light"] #lib-grid .lib-item {
  background: var(--panel2, #ebe9e4);
  border-color: var(--border, #ccc9c0);
}

[data-theme="light"] #lib-grid .lib-item:hover {
  border-color: var(--accent, #0891b2);
}

[data-theme="light"] #lib-grid .lib-item canvas {
  background: #d8d5ce;
}

[data-theme="light"] #lib-grid .lib-item-name {
  color: var(--text, #1c1b18);
}

[data-theme="light"] #lib-grid .lib-source-badge {
  background: rgba(8, 145, 178, 0.10);
  color: var(--accent, #0891b2);
  border-color: rgba(8, 145, 178, 0.20);
}

#lib-filter-bar {
  display: flex;
  padding: 4px 8px 3px;
  flex-shrink: 0;
  border-bottom: 1px solid var(--border);
}

#lib-filter-bar select {
  width: 100%;
  background: var(--panel3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .5px;
  padding: 3px 6px;
  cursor: pointer;
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns=!string!width=!string!height=!string!%3E%3Cpath d=!string!fill=!string!/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 7px center;
  padding-right: 22px;
}

#lib-filter-bar select:focus {
  border-color: var(--accent);
}

.lib-empty {
  font-size: 11px;
  color: var(--muted);
  padding: 10px 14px;
  font-style: italic;
}

/* ─────────────────────────────────────────────
   12. MODEL ASSEMBLER — SHAPE CARDS
   ───────────────────────────────────────────── */
.shape-grid-inline {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
  margin-bottom: 2px;
}

.shape-card-sm {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 6px 2px 5px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: grab;
  font-size: 13px;
  text-align: center;
  transition: all .12s;
  user-select: none;
  line-height: 1;
}

.shape-card-sm span {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 0px;
  color: var(--muted);
  line-height: 1;
  max-width: 50px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shape-card-sm:hover {
  border-color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 8%, transparent);
}

.shape-card-sm:hover span {
  color: var(--accent2);
}

.shape-card-sm.drag-active {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  cursor: grabbing;
}

/* Negative shape cutter cards — purple tint */
.neg-card:hover {
  border-color: var(--purple) !important;
  background: color-mix(in srgb, var(--purple) 10%, transparent) !important;
}

.neg-card:hover span {
  color: var(--purple) !important;
}

#canvas-wrap.drag-over-canvas::after, #canvas-wrap.drag-over-file::after {
  position: absolute;
  inset: 0;
  border: 2px dashed var(--accent);
  border-radius: var(--r-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--display);
  font-size: 13px;
  letter-spacing: 3px;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 4%, transparent);
  pointer-events: none;
  z-index: 99;
}

/* Shape card dragged out of the panel. */
#canvas-wrap.drag-over-canvas::after {
  content: 'http://www.w3.org/2000/svg';
}

/* Model file dragged in from the desktop. Different wording and a different
   colour, because the two gestures have very different consequences — one adds
   a primitive, the other pulls in a file that may take a moment to parse. */
#canvas-wrap.drag-over-file::after {
  content: '10';
  border-color: var(--accent2);
  color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 5%, transparent);
}

/* ═══════════════════════════════════════════════════════════════════════════
   13. SHAPE LIBRARY — merged from shape-library.css
   Standalone-app rules (body overflow, #app root, margin-top, 100vh heights)
   are removed — ShapeLibrary now lives inside .tab-view which already
   provides the correct dimensions. Everything else is verbatim.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Layout ── */
#shape-library-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  margin-top: var(--header-h);  /* no margin-top / 100vh — parent .tab-view owns sizing */
}

#lib-main {
  flex: 1;
  display: flex;
  overflow: hidden;
}

/* ── 2. Toolbar ── */
#lib-toolbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  padding: 0 16px;
  height: 44px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  gap: 0;
}

#lib-toolbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}

.tb-label {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--muted);
  flex-shrink: 0;
}

.tb-sep {
  width: 1px;
  height: 20px;
  background: var(--border);
  margin: 0 10px;
  flex-shrink: 0;
}

#lib-search-wrap {
  position: relative;
  flex-shrink: 0;
}

#lib-search-icon {
  position: absolute;
  left: 8px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--muted);
  pointer-events: none;
}

#lib-search {
  width: 200px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text);
  font-family: var(--display);
  font-size: 11px;
  padding: 5px 10px 5px 28px;
  outline: none;
  transition: border-color .15s;
}

#lib-search:focus {
  border-color: var(--accent);
}

#sort-select {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text-dim);
  font-family: var(--display);
  font-size: 11px;
  padding: 4px 8px;
  cursor: pointer;
  outline: none;
  transition: border-color .15s;
}

#sort-select:focus {
  border-color: var(--accent);
}

#view-btns {
  display: flex;
  gap: 3px;
  flex-shrink: 0;
}

.view-toggle-btn {
  padding: 4px 8px;
  font-size: 13px;
  line-height: 1;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: all .12s;
}

.view-toggle-btn:hover {
  color: var(--text);
  border-color: var(--border-hi);
}

.view-toggle-btn.active {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.tb-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  border-radius: var(--r-sm);
  cursor: pointer;
  border: 1px solid;
  background: transparent;
  transition: all .15s;
  white-space: nowrap;
}

.tb-btn-ghost {
  border-color: var(--border);
  color: var(--muted);
}

.tb-btn-ghost:hover {
  border-color: var(--text-dim);
  color: var(--text);
  background: rgba(255, 255, 255, .03);
}

.tb-btn-accent {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.tb-btn-accent:hover {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.tb-btn-accent2 {
  border-color: var(--accent2);
  color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 6%, transparent);
}

.tb-btn-accent2:hover {
  background: color-mix(in srgb, var(--accent2) 14%, transparent);
}

.tb-btn-danger {
  border-color: var(--red);
  color: var(--red);
  background: color-mix(in srgb, var(--red) 6%, transparent);
}

.tb-btn-danger:hover {
  background: color-mix(in srgb, var(--red) 14%, transparent);
}

.tb-btn-sm {
  padding: 3px 9px;
  font-size: 11px;
}

.tb-btn:disabled {
  opacity: .3;
  cursor: default;
  pointer-events: none;
}

/* ── 3. Groups Sidebar ── */
#groups-sidebar {
  width: 210px;
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--panel);
}

#groups-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

#groups-header-title {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
}

#add-group-btn {
  font-size: 17px;
  line-height: 1;
  color: var(--accent);
  background: none;
  border: none;
  cursor: pointer;
  opacity: .7;
  transition: opacity .12s;
  padding: 0 2px;
}

#add-group-btn:hover {
  opacity: 1;
}

#groups-list {
  flex: 1;
  overflow-y: auto;
  padding: 5px 0;
}

.group-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  cursor: pointer;
  transition: background .1s;
  position: relative;
  user-select: none;
  min-height: 32px;
}

.group-item:hover {
  background: rgba(255, 255, 255, .03);
}

.group-item.active {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.group-item.active .group-name {
  color: var(--accent);
}

.group-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.group-name {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .9px;
  text-transform: uppercase;
  color: var(--text-dim);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.group-count {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  background: color-mix(in srgb, var(--text) 7%, transparent);
  border-radius: 10px;
  padding: 1px 6px;
  flex-shrink: 0;
}

.group-actions {
  display: none;
  gap: 2px;
  position: absolute;
  right: 5px;
  top: 50%;
  transform: translateY(-50%);
}

.group-item:hover .group-actions {
  display: flex;
}

.group-action-btn {
  font-size: 11px;
  padding: 2px 4px;
  background: var(--panel3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--muted);
  line-height: 1;
  transition: all .1s;
}

.group-action-btn:hover {
  color: var(--text);
  border-color: var(--accent);
}

.group-action-btn.del:hover {
  color: var(--red);
  border-color: var(--red);
}

.group-inline-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--display);
  font-size: 11px;
  padding: 1px 5px;
  outline: none;
  flex: 1;
  min-width: 0;
}

#modal-group-parent-info {
  display: none;
  font-size: 11px;
  color: var(--accent);
  margin-bottom: 10px;
  padding: 4px 8px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-radius: var(--r-sm);
  letter-spacing: .5px;
}

/* ── 4. Content + Selection Bar ── */
#lib-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg);
}

#selection-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  flex-shrink: 0;
  transition: background .2s;
}

#selection-bar.has-selection {
  background: color-mix(in srgb, var(--accent2) 6%, var(--panel));
}

#sel-count {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  min-width: 90px;
  transition: color .15s;
}

#selection-bar.has-selection #sel-count {
  color: var(--accent2);
}

#sel-actions {
  display: flex;
  gap: 6px;
  margin-left: 4px;
}

#sel-clear {
  background: none;
  border: none;
  color: var(--muted);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s, color .15s;
}

#selection-bar.has-selection #sel-clear {
  opacity: 1;
  pointer-events: auto;
}

#sel-clear:hover {
  color: var(--text);
}

#sel-actions .tb-btn:disabled {
  opacity: .3;
  cursor: default;
  pointer-events: none;
}

/* Import / Export pinned to the right of the same row as the bulk actions */
#sel-io {
  display: flex;
  gap: 8px;
  margin-left: auto;
  flex-shrink: 0;
}

.group-sel-all-btn {
  margin-left: auto;
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--muted);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .5px;
  padding: 2px 8px;
  cursor: pointer;
  transition: all .12s;
  white-space: nowrap;
  flex-shrink: 0;
  opacity: 0;
  pointer-events: none;
}

.grid-group-label:hover .group-sel-all-btn, .group-sel-all-btn.active {
  opacity: 1;
  pointer-events: auto;
}

.group-sel-all-btn:hover {
  border-color: var(--accent2);
  color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 8%, transparent);
}

.group-sel-all-btn.active {
  border-color: var(--accent2);
  color: var(--accent2);
}

#grid-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
}

/* ── 5. Grid / List + Group Sections ── */
.grid-group-section {
  margin-bottom: 24px;
}

.grid-group-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}

.grid-group-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.grid-group-name {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
}

.grid-group-n {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  margin-left: auto;
}

.item-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 8px;
}

.item-grid.list-view {
  grid-template-columns: 1fr;
  gap: 3px;
}

/* ── 6. Shape Item Card ── */
.slm-item {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--panel);
  cursor: pointer;
  transition: border-color .12s, background .12s, box-shadow .12s;
  position: relative;
  overflow: hidden;
  user-select: none;
}

.slm-item:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 4%, var(--panel));
}

.slm-item.selected {
  border-color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 6%, var(--panel));
  box-shadow: 0 0 0 1px var(--accent2) inset;
}

.item-grid.list-view .slm-item {
  display: flex;
  align-items: center;
  padding: 6px 8px;
}

.slm-check {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 10;
  width: 20px;
  height: 20px;
  border: 1.5px solid var(--border-hi);
  border-radius: 3px;
  background: var(--panel2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  transition: all .1s;
  pointer-events: all;
  cursor: pointer;
}

.slm-check:hover {
  border-color: var(--accent2);
  background: color-mix(in srgb, var(--accent2) 15%, var(--panel2));
}

.slm-item.selected .slm-check {
  background: var(--accent2);
  border-color: var(--accent2);
  color: var(--bg);
}

.slm-thumb {
  width: 100%;
  aspect-ratio: 16 / 9;
  display: block;
  background: #080a10;
  cursor: zoom-in;
}

.item-grid.list-view .slm-thumb {
  width: 72px;
  aspect-ratio: unset;
  height: 40px;
  flex-shrink: 0;
  border-radius: var(--r-sm);
}

.slm-body {
  padding: 6px 8px 7px;
}

.item-grid.list-view .slm-body {
  padding: 0 8px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
}

.slm-name-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
}

.slm-name {
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
}

.slm-item:hover .slm-name {
  color: var(--text);
}

.slm-edit-name-btn {
  opacity: 0;
  font-size: 11px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--accent);
  padding: 0 2px;
  line-height: 1;
  transition: opacity .1s;
  flex-shrink: 0;
}

.slm-item:hover .slm-edit-name-btn {
  opacity: .7;
}

.slm-edit-name-btn:hover {
  opacity: 1 !important;
}

.slm-name-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--display);
  font-size: 11px;
  padding: 2px 5px;
  outline: none;
}

.slm-meta {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.item-grid.list-view .slm-meta {
  margin-top: 0;
  margin-left: auto;
  flex-shrink: 0;
}

.slm-badge {
  display: inline-block;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--accent2) 15%, transparent);
  color: var(--accent2);
  margin-left: 4px;
}

.slm-more-btn {
  position: absolute;
  top: 5px;
  right: 5px;
  padding: 2px 5px;
  font-size: 13px;
  line-height: 1;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
  color: var(--muted);
  opacity: 0;
  transition: opacity .1s, color .1s;
  z-index: 2;
}

.slm-item:hover .slm-more-btn {
  opacity: 1;
}

.slm-more-btn:hover {
  color: var(--text);
}

/* ── 7. Drag & Drop ── */
body.is-dragging .slm-item {
  cursor: grabbing;
}

.group-item.drag-target {
  background: color-mix(in srgb, var(--accent) 14%, transparent) !important;
  border-radius: var(--r-sm);
  outline: 1px dashed var(--accent);
  outline-offset: -2px;
}

.group-item.drag-target .group-name {
  color: var(--accent);
}

#slm-drag-ghost {
  position: fixed;
  top: -100px;
  left: 0;
  background: var(--panel2);
  border: 1px solid var(--accent);
  border-radius: 20px;
  color: var(--accent);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 5px 14px;
  pointer-events: none;
  z-index: 9999;
  white-space: nowrap;
}

/* ── 8. Empty State ── */
#lib-empty {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  gap: 12px;
  padding: 60px 20px;
  text-align: center;
}

#lib-empty.visible {
  display: flex;
}

#lib-empty-icon {
  font-size: 40px;
  opacity: .3;
}

#lib-empty-title {
  font-family: var(--display);
  font-size: 13px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-dim);
}

#lib-empty-desc {
  font-size: 11px;
  color: var(--muted);
  max-width: 280px;
  line-height: 1.7;
}

#lib-empty-desc a {
  color: var(--accent);
}

/* ── 9. Context Menu ── */
#slm-ctx {
  position: fixed;
  z-index: 910;
  background: var(--panel2);
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  min-width: 190px;
  box-shadow: 0 8px 32px color-mix(in srgb, var(--bg) 70%, transparent);
  overflow: hidden;
}

.ctx-name-header {
  padding: 8px 14px 6px;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--accent);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#slm-ctx .ctx-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  cursor: pointer;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
  transition: background .1s;
}

#slm-ctx .ctx-item:hover {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--text);
}

#slm-ctx .ctx-item.ctx-danger {
  color: var(--red);
}

#slm-ctx .ctx-item.ctx-danger:hover {
  background: color-mix(in srgb, var(--red) 8%, transparent);
}

.ctx-sep {
  height: 1px;
  background: var(--border);
  margin: 3px 0;
}

/* ── 10. Modals ── */
.slm-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: color-mix(in srgb, var(--bg) 65%, transparent);
  align-items: center;
  justify-content: center;
}

.slm-overlay.open {
  display: flex;
}

.slm-modal {
  background: var(--panel2);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-lg);
  padding: 24px 28px;
  min-width: 340px;
  max-width: 480px;
  box-shadow: 0 16px 48px color-mix(in srgb, var(--bg) 70%, transparent);
}

.slm-modal-title {
  font-family: var(--display);
  font-size: 13px;
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-bottom: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.slm-modal-title.accent {
  color: var(--accent);
}

.slm-modal-title.danger {
  color: var(--red);
}

.slm-modal-title.green {
  color: var(--green);
}

.slm-modal-body {
  font-size: 11px;
  color: var(--muted);
  line-height: 1.7;
  margin-bottom: 18px;
}

.slm-modal-body strong {
  color: var(--text);
}

.slm-modal-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--display);
  font-size: 12px;
  padding: 7px 10px;
  outline: none;
  margin-bottom: 16px;
  transition: border-color .15s;
}

.slm-modal-input:focus {
  border-color: var(--accent);
}

.slm-modal-select {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--display);
  font-size: 11px;
  padding: 7px 10px;
  outline: none;
  margin-bottom: 16px;
  cursor: pointer;
}

.slm-modal-select:focus {
  border-color: var(--accent);
}

.slm-modal-row {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  align-items: center;
}

.color-swatches {
  display: flex;
  gap: 7px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.color-swatch {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  cursor: pointer;
  border: 2px solid color-mix(in srgb, currentColor 0%, var(--border-hi));
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25);
  transition: border-color .1s, transform .1s, box-shadow .1s;
  position: relative;
}

.color-swatch:hover {
  transform: scale(1.1);
}

.color-swatch.active {
  border-color: var(--text);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25), 0 0 0 2px var(--panel2), 0 0 0 4px var(--text);
  transform: scale(1.15);
}

.color-swatch[data-color='6'] {
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15), 0 0 0 1px var(--border-hi);
}

.export-option-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

.export-option-list label {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 11px;
  color: var(--text-dim);
}

.export-option-list input[type=radio] {
  accent-color: var(--accent);
}

/* ── 11. Import Progress ── */
#import-drop-zone {
  border: 1.5px dashed var(--border-hi);
  border-radius: var(--r-md);
  padding: 28px 16px;
  text-align: center;
  cursor: pointer;
  margin-bottom: 14px;
  font-family: var(--display);
  font-size: 11px;
  color: var(--muted);
  transition: background .15s, border-color .15s;
}

#import-drop-zone:hover, #import-drop-zone.drag-active {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}

#import-progress {
  margin-bottom: 14px;
}

#import-status {
  font-family: var(--display);
  font-size: 11px;
  color: var(--text-dim);
}

.prog-bar-wrap {
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  overflow: hidden;
  margin-top: 8px;
}

.prog-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
  transition: width .2s;
}

#import-log {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  max-height: 120px;
  overflow-y: auto;
  margin-top: 8px;
  line-height: 1.6;
}

.import-step {
  margin-bottom: 16px;
}

.import-step-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 8px;
}

.import-step-label strong {
  color: var(--text);
  font-weight: 600;
}

.import-step-num {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--bg);
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.import-file-list {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  padding: 5px 8px;
  background: var(--bg);
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  min-height: 28px;
  line-height: 1.5;
  word-break: break-all;
  margin-top: 6px;
}

#import-needs-tex {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  background: color-mix(in srgb, var(--color-yellow, #fde047) 7%, var(--panel3));
  border: 1px solid color-mix(in srgb, var(--color-yellow, #fde047) 25%, transparent);
  border-radius: var(--r-md);
  padding: 8px 12px;
  margin-bottom: 10px;
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.6;
}

#import-needs-tex strong {
  color: var(--text);
}

#import-go-btn:disabled {
  opacity: .35;
  cursor: default;
  pointer-events: none;
}

#import-group-section .import-step-num {
  background: var(--purple);
}

/* ── 12. Toast ── */
#slm-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(12px);
  background: var(--panel2);
  border: 1px solid var(--accent);
  color: var(--text);
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .5px;
  padding: 7px 18px;
  border-radius: 20px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .22s, transform .22s;
  z-index: 1100;
  white-space: nowrap;
}

#slm-toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

#slm-toast.error {
  border-color: var(--red);
  color: var(--red);
}

/* ── 3D Preview Modal ── */
.preview-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1200;
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.preview-overlay.open {
  display: flex;
}

.preview-box {
  display: flex;
  flex-direction: column;
  background: var(--panel2);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 24px 64px color-mix(in srgb, var(--bg) 70%, transparent);
  width: min(900px, calc(100vw - 48px));
  height: min(600px, calc(100vh - 48px));
}

.preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  flex-shrink: 0;
}

.preview-title-text {
  font-family: var(--display);
  font-size: 12px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--accent);
}

.preview-close-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--muted);
  cursor: pointer;
  padding: 3px 9px;
  font-size: 12px;
  transition: all .12s;
}

.preview-close-btn:hover {
  border-color: var(--red);
  color: var(--red);
}

.preview-canvas {
  flex: 1;
  width: 100%;
  height: 100%;
  display: block;
  background: #0a0c14;
  cursor: grab;
  outline: none;
}

.preview-canvas:active {
  cursor: grabbing;
}

.preview-hint {
  flex-shrink: 0;
  text-align: center;
  font-family: var(--display);
  font-size: 11px;
  letter-spacing: .5px;
  color: var(--muted);
  padding: 7px 16px;
  background: var(--panel);
  border-top: 1px solid var(--border);
}

/* ─────────────────────────────────────────────
   13. MOBILE
   ─────────────────────────────────────────────
   Append this block to the END of style.css (the shape-library stylesheet).
   If you already pasted an earlier version of this section, REPLACE it with
   this one.

   These rules are keyed off `body.is-mobile`, which useIsMobile() toggles at
   the 768px breakpoint. Keying off the class (rather than a hard-coded @media
   query) keeps the CSS and the JS `isMobile` ref using a single source of truth,
   so the toolbar markup and the layout never disagree about what "mobile" is.
   ───────────────────────────────────────────── */

/* Hide the groups sidebar — its job is taken over by the toolbar dropdown
   (GroupsDropdown.vue). Content then fills the full width. */
body.is-mobile #groups-sidebar {
  display: none;
}

/* ── Toolbar: two rows on mobile ──────────────────────────────────────────────
   Row 1: groups dropdown + search + sort + view toggles
   Row 2: import + export (forced onto their own line so they can't overlap the
   view toggles on narrow screens). */
body.is-mobile #lib-toolbar {
  flex-wrap: wrap;
  height: auto;
  min-height: 44px;
  padding: 6px 8px;
  gap: 6px;
  row-gap: 6px;
  align-content: center;
}

body.is-mobile #lib-toolbar-left {
  gap: 6px;
  flex: 0 1 auto;
  min-width: 0;
}

body.is-mobile .tb-sep {
  margin: 0 4px;
}

/* The groups dropdown absorbs spare width on row 1. */
body.is-mobile #lib-toolbar > .groups-dd {
  flex: 1 1 auto;
  min-width: 0;
}

/* While the search is expanded the left zone fills row 1 (dropdown + right
   group are both hidden via v-if), so it stays a single row. */
body.is-mobile #lib-toolbar.tb-search-open #lib-toolbar-left {
  flex: 1 1 auto;
}

/* Keep the sort select from eating the row. */
body.is-mobile #sort-select {
  max-width: 120px;
}

/* ── Collapsed search / generic toolbar icon button ── */
.tb-icon-btn {
  flex-shrink: 0;
  width: 30px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--muted);
  cursor: pointer;
  transition: border-color .15s, color .15s;
}

.tb-icon-btn:hover {
  color: var(--text);
  border-color: var(--border-hi);
}

/* Accent state signals an active search filter while collapsed. */
.tb-icon-btn.has-value {
  color: var(--accent);
  border-color: var(--accent);
}

/* ── Expanded mobile search fills the toolbar ── */
body.is-mobile #lib-search-wrap.search-expanded {
  flex: 1;
  width: 100%;
  display: flex;
  align-items: center;
}

body.is-mobile #lib-search-wrap.search-expanded #lib-search {
  width: 100%;
  flex: 1;
  padding-right: 30px;   /* room for the close button */
}

.tb-search-close {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--muted);
  font-size: 12px;
  line-height: 1;
  padding: 4px;
  cursor: pointer;
}

.tb-search-close:hover {
  color: var(--text);
}

/* ── Icon-only buttons on mobile (Import/Export + selection bar Move/Delete) ──
   Both reuse the .tb-btn-glyph / .tb-btn-text split: the text is hidden and
   only the leading glyph shows. */
body.is-mobile #sel-io .tb-btn, body.is-mobile #sel-actions .tb-btn {
  gap: 0;
  min-width: 34px;
  justify-content: center;
}

body.is-mobile #sel-io .tb-btn {
  padding: 5px 10px;
}

body.is-mobile #sel-io .tb-btn .tb-btn-text, body.is-mobile #sel-actions .tb-btn .tb-btn-text {
  display: none;
}

body.is-mobile #sel-io .tb-btn .tb-btn-glyph {
  font-size: 14px;
}

body.is-mobile #sel-actions .tb-btn .tb-btn-glyph {
  font-size: 13px;
}

/* Reclaim row width on mobile so Import/Export fit beside Move/Delete:
   drop the count's reserved min-width, and remove the hidden Clear button's
   footprint when nothing is selected (it only needs space once it appears). */
body.is-mobile #sel-count {
  min-width: 0;
}

body.is-mobile #selection-bar:not(.has-selection) #sel-clear {
  display: none;
}

/* ── Finger-friendly range sliders ────────────────────────────────────────────
   Native range inputs draw a ~12px thumb, and the pivot rows shrink the track
   to an inline height:3px — both far too small to grab on a touch screen. On
   mobile we take over the appearance to give every slider a tall hit area and a
   large round thumb, while keeping the visible track thin. `!important` on the
   height is needed to beat the inline height:3px on the pivot sliders.
   `touch-action: pan-y` lets a vertical drag still scroll the panel while a
   horizontal drag adjusts the value. */
body.is-mobile input[type=range] {
  -webkit-appearance: none;
  appearance: none;
  height: 30px !important;
  min-height: 30px !important;
  background: transparent;
  cursor: pointer;
  touch-action: pan-y;
  max-width: 110px;
}

body.is-mobile input[type=range]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--border);
}

body.is-mobile input[type=range]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--border);
}

body.is-mobile input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 24px;
  margin-top: -10px;            /* center the 24px thumb over the 4px track */
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--panel, #1a1a2e);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

body.is-mobile input[type=range]::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border: 2px solid var(--panel, #1a1a2e);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

body.is-mobile input[type=range]:active::-webkit-slider-thumb {
  background: var(--accent2, var(--accent));
}

body.is-mobile input[type=range]:active::-moz-range-thumb {
  background: var(--accent2, var(--accent));
}

@media (max-width:768px) {
  :root {
    --header-h: 48px;
    --tool-footer-h: calc(56px + env(safe-area-inset-bottom, 0px));
    --mobile-chips-h: 50px;   /* keep in sync with .mpd { --chips-h } */
  }

  /* ── Toast clears the mobile dock ─────────────────────────────────────────
     The bottom chrome is the tool footer (--tool-footer-h) with the
     MobilePanelDock chip strip (--mobile-chips-h) stacked on top of it. The
     desktop `bottom: 40px` puts the toast inside that chrome, and its
     z-index: 999 sits below the chips (10010) and drawer (10005), so it was
     rendering behind the dock. Lift it clear and raise it above both.
     Messages also wrap here — "2 shapes duplicated" overflows a 390px
     viewport with nowrap. */
  #toast {
    bottom: calc(var(--tool-footer-h) + var(--mobile-chips-h) + 12px);
    z-index: 10020;
    white-space: normal;
    text-align: center;
  }

  .more-menu {
    max-height: 72vh;
    overflow-y: auto;
  }

  #left-panel, #right-panel {
    display: none !important;
  }

  #toolbar {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    transform: none;
    width: auto;
    max-width: none;
    height: var(--tool-footer-h);
    padding: 0 4px env(safe-area-inset-bottom, 0px);
    align-items: center;          /* center the 46px buttons in the bar */
    border: none;
    border-top: 1px solid var(--border);
    border-radius: 0;
  }

  /* ── Properties drawer open ───────────────────────────────────────────────
     MobilePanelDock adds body.ma-props-drawer while the Properties panel is up.
     The drawer takes the bottom half of the screen and the chip strip + tool
     footer are hidden, so the viewport must shrink into the top half — no
     overlap. Resizing #canvas-wrap fires the renderer's ResizeObserver, which
     re-derives the camera aspect; MobilePanelDock then zoom-to-fits the
     selection into that top-half viewport. */
  body.ma-props-drawer #toolbar {
    display: none !important;
  }

  /* Footer + chips are hidden in this mode; a 50dvh sheet is pinned to the
     bottom instead, so the toast rides above that. */
  body.ma-props-drawer #toast {
    bottom: calc(50dvh + 12px);
  }

  body.ma-props-drawer #canvas-wrap {
    flex: 0 0 auto;
    height: calc(50vh - var(--header-h));
    height: calc(50dvh - var(--header-h));
  }

  /* ── Drilled property section fills the drawer ────────────────────────────
     MobilePanelDock adds body.ma-section-focus and tags the drilled section
     with .mpd-drilled while isolating everything else. Stretch the (teleported)
     content chain so that one section fills the drawer height with its own
     scroll. The body selector is generic (`.prop-section-title + *`) because the
     injected parametric panels use a plain <div> body, not `.section-body`.

     The body is a COLUMN. Without an explicit flex-direction it defaults to
     `row`, which lays the parametric panels' slider rows out side by side
     (label · slider · value of one row running into the next). And it must not
     carry a max-height — `flex: 1 1 auto` + `min-height: 0` is what makes it
     fill the drawer; a fixed cap clips it to a single visible row. */
  body.ma-section-focus #mobile-drawer-body {
    display: flex;
    flex-direction: column;
  }

  body.ma-section-focus #mobile-drawer-body .rp-tab-pane {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  body.ma-section-focus #mobile-drawer-body #prop-content {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  body.ma-section-focus #mobile-drawer-body .prop-section.mpd-drilled {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  body.ma-section-focus #mobile-drawer-body .prop-section.mpd-drilled > .prop-section-title + * {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }

}

/* ─────────────────────────────────────────────
 MATERIAL LIBRARY — fill-height fix
 ─────────────────────────────────────────────
 #material-library-body lives inside an anonymous tab wrapper
 (.tab-view > div), which is display:block. Its scoped rule sizes it with
 flex:1, but flex:1 does nothing inside a block parent, so the whole library
 collapses to content height and the empty state looks truncated mid-screen.
 Nothing else sets `height` on this id, so this plain-id rule wins for height
 even against the higher-specificity scoped rule — no need to touch the .vue.
 Mirrors how #assembler-root sizes itself; fallback guards a missing token. */
#material-library-body {
  height: calc(100vh - var(--header-h, 36px));
  height: calc(100dvh - var(--header-h, 36px));
}

/* ─────────────────────────────────────────────
 MOBILE — shared modal dialogs
 ─────────────────────────────────────────────
 The small dialogs (Rename / Delete / Export / Group / Import) all use the
 global `.slm-modal` shell with an inline `min-width` (320–400px). On narrow
 phones that inline width can exceed the viewport and cause horizontal
 overflow, so on mobile we neutralise it (needs !important to beat the inline
 style), cap the width to the viewport, and let tall dialogs scroll inside.

 Keyed off `body.is-mobile` — the same source of truth used elsewhere — and
 scoped to `.slm-modal` only, so it never touches the full-screen shells used
 by MatCreateModal (.mat-create-shell) or ImgTexModal (.itm-shell).
 ───────────────────────────────────────────── */
body.is-mobile .slm-modal {
  min-width: 0 !important;
  width: min(92vw, 440px);
  max-height: 88dvh;
  overflow-y: auto;
  padding: 20px 18px;
}

/* Give the footer's Cancel / confirm buttons roomier tap targets. */
body.is-mobile .slm-footer {
  gap: 8px;
}

body.is-mobile .slm-footer .tb-btn {
  padding: 8px 14px;
}

/* ═══════════════════════════════════════════════════════════════════════════
 LIGHT-MODE CONTRAST FIXES
 ───────────────────────────────────────────────────────────────────────────
 applyTheme() (canvas-settings.js) remaps --text/--text2/--accent/--muted for
 light mode but leaves --text-dim, --green, --purple and --red at their bright
 dark-mode values, so they render as low-contrast text on the pale surfaces.
 Re-map them here (html[data-theme="light"] beats :root on specificity, so it
 wins no matter the load order), then patch the handful of components that
 hardcode dark colours in their own (scoped) styles.

 These selectors lead with `html[data-theme="light"]` on purpose: the extra
 element bumps specificity to (1,x,y) which clears the scoped `[data-v-*]`
 rules and the JS-set inline styles they need to override.
 ═══════════════════════════════════════════════════════════════════════════ */

/* ── Leftover tokens the theme switch never remapped ───────────────────────
 Two different override mechanisms are in play, and which one a token needs
 depends on whether applyTheme() touches it:

   • NOT set by applyTheme → a plain declaration is enough.
     html[data-theme="light"] scores (0,1,1); the :root block in
     design-system.css scores (0,1,0), so this wins on specificity regardless
     of which file loads first.  → --text-dim, --text-strong, --green,
     --purple, --red

   • SET by applyTheme → needs !important.
     applyTheme() writes --bg/--panel*/--border*/--text/--text2/--muted/
     --accent*/--btn-*/--shadow* as INLINE styles on <html>
     (root.style.setProperty). An inline declaration outranks every stylesheet
     rule no matter the selector, so the ONLY way to beat it from here is an
     !important declaration.  → --muted

 Light-mode contrast ladder this produces, measured on --panel (#f5f4f0) and
 the darkest surface --panel3 (#dddad4):

   --text        #1c1b18   15.7 / 12.4
   --text2       #3a3830   10.7 /  8.4
   --text-dim    #4a4840    8.3 /  6.6
   --muted       #5f5d55    6.0 /  4.7   (was 4.0 / 3.2 — under the 4.5 floor)
 ───────────────────────────────────────────────────────────── */
html[data-theme="light"] {
  /* Text ladder. Warm-neutral to match --text (#1c1b18) / --text2 (#3a3830);
     a pure grey like #3a3a3a reads cold next to them and, at 10.3:1, sits so
     close to --text2 that the dim/secondary distinction disappears. */
  --text-dim:    #4a4840;   /* was #a2aabb — 2.1:1. Panel labels, section titles, help */
  --text-strong: #14130f;   /* was #dadada. NOTE: nothing reads this token yet — see below */

  /* --muted is pinned inline by applyTheme, hence !important. 76 rules read it. */
  --muted:       #5f5d55 !important;   /* was #7a786e — 4.0:1 on --panel, 3.2:1 on --panel3 */

  /* Accent tokens applyTheme skips entirely. */
  --green:       #15803d;   /* was #4ade80 — 1.6:1. .btn.green, param titles, values */
  --purple:      #7e22ce;   /* was #c084fc — 2.4:1. purple titles / Import */
  --red:         #dc2626;   /* was #f87171 — 2.5:1 */
}

/* --text-strong is declared in design-system.css but no rule in the codebase
   consumes it, so overriding it changes nothing on screen today. Kept above so
   the token is correct once something uses it. Note the direction flips per
   theme: "strong" means LIGHTER than --text on dark (#dadada vs #c9cde4) and
   DARKER than --text on light (#14130f vs #1c1b18). A value like #2a2a2a is
   lighter than --text here, i.e. weaker than the text it is meant to
   emphasise. */

/* ── Material sidebar headings + help text ─────────────────────────────────────
 .mat-section-title and .hk-desc both read --text-dim (fixed above). .hk-sec-note
 uses --muted, which the token block now handles via !important; this rule pushes
 that one note a step darker still (--text2) because it sits on --panel3. */
html[data-theme="light"] .hk-sec-note {
  color: var(--text2, #3a3830);
}

/* ── Zoom strip (#viewport-zoom-controls) — hardcoded dark rgba() background ──── */
html[data-theme="light"] #viewport-zoom-controls {
  background: var(--panel, #f5f4f0);
  border-color: var(--border, #ccc9c0);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}

html[data-theme="light"] #viewport-zoom-controls .vpc-btn {
  color: var(--text2, #3a3830);
  border-color: var(--border, #ccc9c0);
}

html[data-theme="light"] #viewport-zoom-controls .vpc-btn:hover {
  color: var(--accent, #0891b2);
  border-color: var(--accent, #0891b2);
  background: rgba(0, 0, 0, 0.05);
}

/* ── Pan gizmo (#viewport-pan-gizmo) — the "dark circle" is a dark radial bg ──── */
html[data-theme="light"] #viewport-pan-gizmo {
  background: radial-gradient(ellipse at 50% 52%, rgba(255, 255, 255, 0.55) 25%, rgba(255, 255, 255, 0.18) 60%, transparent 100%);
}

/* Centre reset sphere is filled #1c2230 via a presentation attribute; a CSS
 `fill` wins over that. Lighten it so it reads on the pale gizmo. */
html[data-theme="light"] #viewport-pan-gizmo .gc circle:first-child {
  fill: #e6e3dc;
  stroke: #8a8578;
}

/* ── "Buy me a coffee" — pale-yellow image vanishes into the pale header ──────── */
html[data-theme="light"] #page-tabs a[href*="buymeacoffee"] {
  display: inline-flex;
  align-items: center;
  background: #ffffff;
  border: 1px solid var(--border2, #b8b4aa);
  border-radius: 6px;
  padding: 1px 4px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}

/* ── "Connect save folder" reconnect banner — hardcoded #fbbf24 amber ────────── */
html[data-theme="light"] .lib-reconnect-banner {
  background: rgba(180, 83, 9, 0.10);
}

html[data-theme="light"] .lib-reconnect-label, html[data-theme="light"] .lib-reconnect-btn {
  color: #92400e;
}

html[data-theme="light"] .lib-reconnect-btn {
  border-color: rgba(146, 64, 14, 0.5);
  background: rgba(180, 83, 9, 0.10);
}

html[data-theme="light"] .lib-reconnect-btn:hover:not(:disabled) {
  background: rgba(180, 83, 9, 0.20);
  border-color: rgba(146, 64, 14, 0.8);
}

/* ── Disabled buttons — 0.38 opacity is invisible on a light bg ───────────────── */
html[data-theme="light"] .lib-add-btn:disabled {
  opacity: 0.6;
}

/* ── Import / Export buttons ───────────────────────────────────────────────────
 Both read --btn-c and derive their fill and border from it, so one variable per
 button is the whole override — no !important needed now that the old inline
 purple is gone. --green and --accent2 are tuned for the dark panel; on the pale
 one they need the 700-weight versions to clear contrast. */
html[data-theme="light"] #forge-export-panel-export { --btn-c: #15803d; }
html[data-theme="light"] #import-btn                { --btn-c: #c2410c; }

