/* =========================================================================
   Trident Non-Planar Viewer — design tokens
   Precision-instrument / CNC-metrology aesthetic. Cool-slate dark palette
   (lifted off near-black so long sessions are easier on the eyes), systematic
   spacing, monospaced data. The --ok/--warn/--danger trio is reserved for
   safety states (probe limits, melt-ceiling, risk flags, clamp events) —
   nothing else may use them.
   ========================================================================= */
:root {
  --bg:#17191b;
  --surface:#1e2124;
  --line:#31363b;
  --ink:#e8eaed;
  --ink-muted:#9aa0a6;
  --accent:#2f6bff;

  --ok:#3fdca0;
  --warn:#ffb454;
  --danger:#ff5d5d;

  /* derived surface/accent shades used for hover/active states */
  --surface-raised:#262a2e;
  --accent-hover:#5a8aff;
  --ink-on-accent:#ffffff;

  /* recessed-well surface for editable controls -- deliberately darker than
     both --bg and --surface so inputs read as a well, not a flat swatch that
     happens to share the page background. */
  --input-bg:#121417;

  /* "Purple modifiers" accent -- reserved exclusively for the Point Edit
     Modifiers subsystem (entry button, modal chrome, per-modifier enabled
     state) so it reads as a distinct layer from the electric-blue --accent
     used everywhere else. Do not reuse elsewhere. */
  --accent-purple:#a78bfa;
  --accent-purple-hover:#c4b5fd;
  --ink-on-purple:#1b1030;

  --font-ui: "Inter", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;

  /* 4px base spacing scale */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-6:24px; --sp-8:32px;
  --radius:8px;
  --radius-lg:12px;

  /* legacy variable names — kept as aliases so designer.js/viewer.js, which
     read a handful of these via getComputedStyle(), keep resolving without
     needing their own edits (out of scope per the redesign brief). --input-bg
     used to be one of these aliases (= var(--bg)); it is now a real token in
     its own right (see above), declared once, not re-aliased here. */
  --txt: var(--ink);
  --muted: var(--ink-muted);
  --panel: var(--surface);
  --input-border: var(--line);
}

* { box-sizing:border-box; }

/* =========================================================================
   Focus-visible discipline (global). Mouse/touch activation must never
   leave a lingering outline; keyboard navigation must always show one.
   Specific components may override the visible treatment (e.g.
   .telemetry-toggle, .drow input/select use a border-color cue instead) --
   those overrides win on specificity and still satisfy the same contract.
   ========================================================================= */
a, button, .btn, [tabindex] { outline:none; }
a:focus-visible, button:focus-visible, .btn:focus-visible, [tabindex]:focus-visible {
  outline:2px solid var(--accent); outline-offset:2px;
}

/* =========================================================================
   Custom form controls — native OS checkbox/radio/range chrome doesn't
   match the theme, so these render as switches/dots/slim-track sliders.
   The Point Edit subsystem's purple override for its own checkboxes lives
   near its own section further down, not here.
   ========================================================================= */
input[type=checkbox], input[type=radio], input[type=range] { outline:none; }
input[type=checkbox]:focus-visible, input[type=radio]:focus-visible, input[type=range]:focus-visible {
  outline:2px solid var(--accent); outline-offset:2px;
}

input[type=checkbox] {
  appearance:none; -webkit-appearance:none; margin:0; flex:none; vertical-align:middle;
  position:relative; width:34px; height:20px; border-radius:999px;
  background:var(--surface-raised); border:1px solid var(--line); cursor:pointer;
  transition:background .15s, border-color .15s;
}
input[type=checkbox]::after {
  content:''; position:absolute; top:1px; left:1px; width:16px; height:16px;
  border-radius:50%; background:var(--ink-muted); transition:transform .15s, background .15s;
}
input[type=checkbox]:hover:not(:disabled) { border-color:var(--accent); }
input[type=checkbox]:checked { background:var(--accent); border-color:var(--accent); }
input[type=checkbox]:checked::after { transform:translateX(14px); background:#fff; }
input[type=checkbox]:disabled { opacity:.4; cursor:default; }

input[type=radio] {
  appearance:none; -webkit-appearance:none; margin:0; flex:none; vertical-align:middle;
  position:relative; width:16px; height:16px; border-radius:50%;
  background:var(--surface-raised); border:1px solid var(--line); cursor:pointer;
  transition:border-color .15s;
}
input[type=radio]::after {
  content:''; position:absolute; top:3px; left:3px; width:8px; height:8px;
  border-radius:50%; background:var(--accent); transform:scale(0); transition:transform .1s;
}
input[type=radio]:hover:not(:disabled) { border-color:var(--accent); }
input[type=radio]:checked { border-color:var(--accent); }
input[type=radio]:checked::after { transform:scale(1); }

input[type=range] {
  appearance:none; -webkit-appearance:none; width:100%; height:16px; margin:0;
  background:transparent; cursor:pointer;
}
input[type=range]::-webkit-slider-runnable-track {
  height:4px; border-radius:999px; background:var(--surface-raised); border:1px solid var(--line);
}
input[type=range]::-moz-range-track {
  height:4px; border-radius:999px; background:var(--surface-raised); border:1px solid var(--line);
}
input[type=range]::-webkit-slider-thumb {
  appearance:none; -webkit-appearance:none; margin-top:-6px; box-sizing:border-box;
  width:16px; height:16px; border-radius:50%; background:var(--accent);
  border:2px solid var(--ink-on-accent); box-shadow:0 1px 3px rgba(0,0,0,.4); cursor:pointer;
}
input[type=range]::-moz-range-thumb {
  width:16px; height:16px; border-radius:50%; background:var(--accent); box-sizing:border-box;
  border:2px solid var(--ink-on-accent); box-shadow:0 1px 3px rgba(0,0,0,.4); cursor:pointer;
}
input[type=range]:disabled { opacity:.4; cursor:default; }

/* Native <select> chevron: was a barely-visible 10x6 glyph in flat #888 with
   no open/closed cue. Enlarged, higher-contrast on hover/focus, and extra
   right padding so the glyph has room. A native select cannot report "open"
   to CSS, so :focus is used as a faithful proxy (set exactly when the list
   opens) -- but the arrow lives in a background-image on a replaced element,
   which CSS transform cannot animate, so the "rotate 180deg on open" cue is
   approximated by swapping to a mirrored (upward) arrow of the same weight,
   which reads as the same flip without wrapping every select in the app in
   an extra element. */
select {
  appearance:none; -webkit-appearance:none; -moz-appearance:none;
  background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:6px; font-size:12px; padding:3px 24px 3px 5px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path fill='none' stroke='%23989898' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 2l5 5 5-5'/></svg>");
  background-repeat:no-repeat; background-position:right 8px center;
  transition:border-color .15s;
}
/* Hover/focus chevron is plain %23e8eaed, the literal value of --ink: a
   data-URI SVG's XML text can't reference a CSS custom property. */
select:hover {
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path fill='none' stroke='%23e8eaed' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 2l5 5 5-5'/></svg>");
}
select:focus {
  border-color:var(--accent); outline:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path fill='none' stroke='%23e8eaed' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 6l5 -5 5 5'/></svg>");
}

html,body {
  margin:0; height:100%;
  background:var(--bg); color:var(--ink);
  font-family:var(--font-ui);
  overflow:hidden;
}

/* Tabular, monospaced treatment for read-only machine/measured output only. */
.tm, .gen-report, .tl-read {
  font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;
}

/* Every editable control uses the UI sans, not mono -- inputs should read like
   the labels beside them. tabular-nums keeps typed numerals from jittering
   width without switching typeface. */
input, select, textarea {
  font-family:var(--font-ui);
  font-variant-numeric:tabular-nums;
}

/* Uppercase label / group-heading treatment. */
.group h2, .section-heading, .step-label, .cv-head, .legend-labels, .telemetry-title {
  text-transform:uppercase;
  letter-spacing:.08em;
  color:var(--ink-muted);
  font-family:var(--font-mono);
}

.app-shell { display:flex; height:100%; }
.canvas-wrap { flex:1; position:relative; min-width:0; }
canvas { display:block; }

/* ---- resizable splitter ---- */
.splitter { width:6px; flex:0 0 6px; background:var(--line); cursor:col-resize; transition:background .15s; }
.splitter:hover, .splitter.dragging { background:var(--accent); }

.panel { width:340px; flex:0 0 340px; background:var(--surface); border-left:1px solid var(--line);
  padding:0; overflow-y:auto; display:flex; flex-direction:column; height:100%; }
.panel-scroll { padding:var(--sp-4); overflow-y:auto; flex:1 1 auto; min-height:0; }
h1 { font-size:16px; font-weight:700; margin:0 0 var(--sp-1); letter-spacing:-0.01em; }
.sub { color:var(--ink-muted); font-size:12px; margin-bottom:var(--sp-4); }
.drop { border:1.5px dashed var(--line); border-radius:var(--radius-lg); padding:var(--sp-4); text-align:center;
  color:var(--ink-muted); font-size:13px; cursor:pointer; transition:.15s; }
.drop:hover, .drop.hot { border-color:var(--accent); color:var(--ink); background:var(--surface-raised); }
.stat { display:flex; justify-content:space-between; font-size:12.5px; padding:var(--sp-2) 0;
  border-bottom:1px solid var(--line); }
.stat span:first-child { color:var(--ink-muted); }
.stat .tm { color:var(--accent-hover); font-weight:600; }
.stat .tm.state-warn { color:var(--warn); }
.stat .tm.state-danger { color:var(--danger); }
.group { margin-top:var(--sp-4); }
.group h2 { font-size:11px; margin:0 0 var(--sp-2); font-weight:600; }
label.row { display:flex; align-items:center; justify-content:space-between; font-size:12.5px;
  padding:5px 0; cursor:pointer; }
label.radio-inline { cursor:pointer; }
.legend { height:10px; border-radius:5px; margin-top:6px;
  background:linear-gradient(90deg,#2b3a8f,#1f9e89,#cfe11f,#fca50a,#e0402f); }
.legend-labels { display:flex; justify-content:space-between; font-size:11px; margin-top:3px; }
.hint { font-size:11px; color:var(--ink-muted); margin-top:var(--sp-3); line-height:1.5; }
.hint.tight-top { margin-top:-2px; margin-bottom:2px; }

/* Section sub-heading (was inline-styled <h3> repeated throughout the panel). */
.section-heading { font-size:11px; font-weight:600; margin:0 0 var(--sp-2); }

/* ---- generic buttons: ids stay JS hooks only, styling lives in classes ---- */
.btn { border:1px solid var(--line); border-radius:8px; background:var(--surface-raised); color:var(--ink);
  font-size:12px; font-weight:600; cursor:pointer; transition:border-color .15s, color .15s, background .15s, opacity .15s, transform .1s ease, box-shadow .15s;
  padding:7px 10px; font-family:var(--font-ui); white-space:nowrap; box-shadow:0 1px 2px rgba(0,0,0,.3); }
.btn:hover:not(:disabled) { border-color:var(--accent); color:var(--accent-hover); transform:translateY(-1px); box-shadow:0 4px 10px rgba(0,0,0,.35); }
.btn:active:not(:disabled) { transform:translateY(0); box-shadow:0 1px 2px rgba(0,0,0,.3); }
.btn:disabled { opacity:.35; cursor:default; box-shadow:none; }
.btn:disabled:hover { border-color:var(--line); color:var(--ink); }
.btn-block { width:100%; display:block; }
.btn-sm { padding:4px 10px; font-size:11px; }
.btn-icon { padding:4px 7px; font-size:13px; line-height:1; }
.btn-primary { background:var(--accent); color:var(--ink-on-accent); border-color:var(--accent); font-weight:700; box-shadow:0 2px 8px rgba(47,107,255,.35); }
.btn-primary:hover:not(:disabled) { color:var(--ink-on-accent); opacity:.9; border-color:var(--accent); background:var(--accent-hover); box-shadow:0 4px 14px rgba(47,107,255,.45); }
.btn-primary:disabled { background:var(--surface-raised); color:var(--ink); border-color:var(--line); box-shadow:none; }

/* ---- overlay (drop-a-gcode card / canvas-centered messages) ---- */
.overlay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  pointer-events:none; }
.overlay .card { pointer-events:auto; background:rgba(30,33,36,.92); border:1px solid var(--line);
  border-radius:var(--radius-lg); padding:28px 34px; text-align:center; }
.card-title { font-size:14px; margin-bottom:6px; }

/* =========================================================================
   Machine readout strip — the timeline scrubber (sparkline + scrub bar)
   only. Slim, full-width, bottom of the canvas. Turn/revolution count gets
   visual primacy since per-turn playback stepping is this tool's signature
   interaction. Live telemetry lives in the floating card below instead.
   ========================================================================= */
.machine-readout { position:absolute; left:var(--sp-4); right:var(--sp-4); bottom:var(--sp-3);
  background:rgba(30,33,36,.94); border:1px solid var(--line); border-radius:var(--radius-lg);
  overflow:hidden; box-shadow:0 8px 24px rgba(0,0,0,.35); }

.spark { display:block; width:100%; height:26px; background:transparent; cursor:pointer; }

.timeline { height:42px; display:flex; align-items:center; gap:var(--sp-2); padding:0 var(--sp-3);
  border-top:1px solid var(--line); }
.timeline button { width:30px; height:26px; border:none; border-radius:6px; cursor:pointer;
  background:var(--accent); color:var(--ink-on-accent); font-size:12px; font-weight:700;
  transition:background .15s, transform .1s, box-shadow .15s; box-shadow:0 1px 2px rgba(0,0,0,.3); }
.timeline button:hover { transform:translateY(-1px); box-shadow:0 3px 8px rgba(47,107,255,.4); }
.timeline button:active { transform:translateY(0); }
.timeline input[type=range] { flex:1; cursor:pointer; }
.timeline select { background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:6px; padding:3px 24px 3px 4px; font-size:12px; }
.tl-read { font-size:12px; color:var(--ink-muted); min-width:150px; text-align:right; }
.tl-read .turn-count { color:var(--ink); font-weight:700; font-size:13px; }

/* =========================================================================
   Telemetry card — Orca-style floating overlay, anchored top-right of the
   canvas (mirrors .overlay .card's translucent-surface idiom). Collapsible
   via #telemetry-toggle; state persisted in localStorage.
   ========================================================================= */
/* Draft-preview badge -- top-left corner, so it never collides with the
   telemetry card (which owns top-right at any collapsed/expanded height). */
.draft-preview-label { position:absolute; top:16px; left:16px; z-index:5;
  padding:2px 8px; border-radius:4px; background:rgba(47,107,255,.18);
  color:var(--accent-hover); font-size:11px; font-family:var(--font-ui); pointer-events:none; }

.telemetry-card { position:absolute; top:16px; right:16px; width:190px; z-index:5;
  background:rgba(30,33,36,.92); border:1px solid var(--line); border-radius:var(--radius);
  overflow:hidden; box-shadow:0 8px 24px rgba(0,0,0,.35); }
.telemetry-header { display:flex; align-items:center; justify-content:space-between;
  padding:6px 8px; border-bottom:1px solid var(--line); }
.telemetry-title { font-size:9.5px; margin:0; }
.telemetry-toggle { background:none; border:none; color:var(--ink-muted); cursor:pointer;
  font-size:11px; padding:2px 5px; line-height:1; border-radius:4px; font-family:var(--font-ui);
  transition:color .15s, background-color .15s, transform .15s; }
.telemetry-toggle:hover { color:var(--ink); background:var(--surface-raised); }
.telemetry-toggle:focus-visible { outline:1px solid var(--accent); outline-offset:1px; }
.telemetry-card.collapsed .telemetry-toggle { transform:rotate(-90deg); }
.telemetry-card.collapsed .readout-telemetry { display:none; }

/* Live telemetry rows: label left, value right (--font-mono via .tm). */
.readout-telemetry { display:flex; flex-direction:column; padding:2px var(--sp-3) 4px; }
.readout-telemetry .stat { flex:none; display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-2); padding:4px 0; border-bottom:none; }
.readout-telemetry .stat span:first-child { font-size:9.5px; text-transform:uppercase; letter-spacing:.06em; }
.readout-telemetry .stat .tm { font-size:12.5px; }

/* ---- Design a vase panel ---- */
.drow { display:flex; align-items:center; justify-content:space-between; font-size:12px;
  padding:4px 0; gap:8px; }
.drow > span { color:var(--ink-muted); white-space:nowrap; transition:color .15s; }
.drow:hover > span:first-child { color:var(--ink); }
.drow input[type=number] { background-color:var(--input-bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px; padding:3px 5px; font-size:12px; width:92px; }
.drow select { background-color:var(--input-bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px; padding:3px 5px; font-size:12px; width:92px; }
.drow select { width:116px; padding-right:24px; }
.drow input:focus, .drow select:focus { border-color:var(--accent); outline:none; }
.colormode-select { width:110px; }

/* Controls whose own content needs more room than the default 92/116px track.
   Widths are measured against the widest option (or placeholder) each one has
   to display, so the text is not clipped: base style needs 110px, the pattern
   list 162px, and the nozzle-temp placeholder 98px. All still fit inline
   alongside their labels inside the 292px row.

   Selectors are qualified with .drow so they outrank the base `.drow select` /
   `.drow input[type=number]` tracks above -- a bare .mid-select loses to
   `.drow select` on specificity and silently does nothing. */
.drow select.mid-select  { width:134px; }
.drow select.wide-select { width:178px; }
.drow input.wide-input   { width:112px; }

/* Orca filament names reach 400px+ ("Creality Generic PLA Matte @..."), which no
   inline track in a 340px panel can hold. Give the row its own line so the
   select spans the full width, and keep a title tooltip for the few that still
   overrun -- a <select> cannot show a text-overflow ellipsis of its own. */
.drow.stacked { flex-direction:column; align-items:stretch; gap:var(--sp-1); }
.drow.stacked > span:first-child { white-space:normal; }
.drow.stacked select { width:100%; }
.drow input[type=range] { width:92px; }
.cv-editor { margin-top:var(--sp-3); background:var(--surface-raised); border:1px solid var(--line);
  border-radius:var(--radius-lg); padding:var(--sp-3); }
.cv-editor .cv-head { display:flex; align-items:center; justify-content:space-between;
  font-size:11px; margin-bottom:6px; }
.cv-editor .cv-head b { color:var(--ink); font-weight:600; text-transform:none; letter-spacing:0; font-family:var(--font-ui); }
.cv-editor .cv-reset { cursor:pointer; color:var(--accent-hover); font-size:11px; text-transform:none; letter-spacing:0; }
.cv-editor canvas { display:block; width:100%; border:1px solid var(--line); border-radius:6px;
  background:var(--bg); cursor:ns-resize; }
.cv-note { font-size:10.5px; color:var(--ink-muted); margin-top:6px; }

/* ---- parameter search (jump-to-control) ---- */
.param-search { position:relative; margin-bottom:var(--sp-3); }
/* Magnifying-glass icon lives in a background-image, so background-COLOR
   (and the other longhands) are set explicitly here, never the `background`
   shorthand -- the shorthand resets background-repeat/position to their
   initials, which is exactly what tiled a chevron SVG across a control
   elsewhere (see the .pm-field-input comment further down). The icon is
   plain #9aa0a6, the literal value of --ink-muted: a data-URI SVG's XML
   text can't reference a CSS custom property. Left padding on the input
   keeps typed text clear of the icon. */
.param-search input { width:100%; background-color:var(--input-bg); color:var(--ink);
  border:1px solid var(--line); border-radius:var(--radius); padding:7px 10px 7px 30px;
  font-size:12.5px; font-family:var(--font-ui);
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 16 16'><circle cx='7' cy='7' r='5' fill='none' stroke='%239aa0a6' stroke-width='1.6'/><line x1='11' y1='11' x2='15' y2='15' stroke='%239aa0a6' stroke-width='1.6' stroke-linecap='round'/></svg>");
  background-repeat:no-repeat; background-position:10px center; background-size:13px 13px; }
.param-search input:focus { border-color:var(--accent); outline:none; }
.param-search input::placeholder { color:var(--ink-muted); }
.param-search-results { position:absolute; left:0; right:0; top:calc(100% + 4px); z-index:8;
  background:var(--surface-raised); border:1px solid var(--line); border-radius:var(--radius);
  box-shadow:0 8px 24px rgba(0,0,0,.35); max-height:280px; overflow-y:auto; padding:4px; display:none; }
.param-search-results.open { display:block; }
.param-search-item { display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:6px 8px; border-radius:6px; cursor:pointer; font-size:12px; color:var(--ink); }
.param-search-item:hover, .param-search-item.active { background:var(--accent); color:var(--ink-on-accent); }
.param-search-item .psi-crumb { font-size:10px; color:var(--ink-muted); font-family:var(--font-mono);
  text-transform:uppercase; letter-spacing:.05em; white-space:nowrap; }
.param-search-item:hover .psi-crumb, .param-search-item.active .psi-crumb { color:var(--ink-on-accent); }

/* highlight pulse when jumping to a control */
@keyframes param-search-pulse { 0%,15% { background:rgba(47,107,255,.22); } 100% { background:transparent; } }
.param-search-hit { animation:param-search-pulse 1.3s ease-out; border-radius:6px; }

/* ---- conditional/dependent option blocks -- indented rail signals "these
   rows only apply because of a choice above" (star shape, texture pattern
   selected, blob/loop style chosen, STL mesh loaded). ---- */
#star-rows, #pattern-rows, #blob-rows, #loop-rows, #mesh-info {
  border-left:2px solid var(--line); padding-left:var(--sp-3); margin-left:2px;
}

/* ---- silhouette mode bar (Symmetrical / Unsymmetrical) ---- */
.sil-mode-bar { display:flex; gap:2px; padding:3px; background:var(--bg);
  border:1px solid var(--line); border-radius:6px; margin:6px 0; }
.sil-mode-btn { flex:1; background:none; border:none; border-radius:4px; color:var(--ink-muted);
  font-size:11px; font-weight:600; padding:5px 2px; cursor:pointer;
  transition:color .15s, background-color .15s; text-transform:uppercase; letter-spacing:.04em; }
.sil-mode-btn:hover { color:var(--ink); }
.sil-mode-btn.active { color:var(--ink-on-accent); background:var(--accent); }

/* ---- preset bar ---- */
.preset-bar { display:flex; gap:4px; align-items:center; margin-bottom:10px; }
.preset-bar select { flex:1 1 0; min-width:0; background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:6px; padding:4px 24px 4px 4px; font-size:11.5px; }
.preset-bar .btn { padding-left:8px; padding-right:8px; }
.preset-bar .btn-icon { padding-left:6px; padding-right:6px; }

/* ---- top-level mode bar (Design / G-code Viewer) ---- */
.mode-bar { display:flex; gap:2px; padding:3px; background:var(--surface-raised);
  border:1px solid var(--line); border-radius:var(--radius); margin-bottom:var(--sp-4); }
.mode-btn { flex:1; background:none; border:none; border-radius:6px; color:var(--ink-muted);
  font-size:12.5px; font-weight:600; padding:7px 2px; cursor:pointer;
  transition:color .15s, background-color .15s; text-transform:uppercase; letter-spacing:.05em; }
.mode-btn:hover { color:var(--ink); }
.mode-btn.active { color:var(--ink-on-accent); background:var(--accent); }

.mode-panel { display:none; }
.mode-panel.active { display:block; }

/* =========================================================================
   Machine bar -- persistent printer strip above the step wizard, visible in
   all four design steps (the printer is context for every step, not a
   Step-4-only choice). Ordinary --accent blue chrome, never --accent-purple
   (reserved for Point Edit). Must stay usable at the 340px sidebar floor --
   everything here wraps or ellipsizes rather than forcing horizontal scroll.
   ========================================================================= */
.mb-bar { background:var(--surface-raised); border:1px solid var(--line);
  border-radius:var(--radius-lg); padding:var(--sp-2) var(--sp-3); margin-bottom:var(--sp-3); }
.mb-row1 { display:flex; align-items:center; gap:6px; }
.mb-icon { flex:none; font-size:13px; color:var(--ink-muted); line-height:1; }
.mb-select { flex:1 1 auto; min-width:0; background-color:var(--input-bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px; font-size:12.5px; font-weight:600;
  padding-top:6px; padding-bottom:6px; padding-left:8px; }
.mb-summary { font-family:var(--font-mono); font-size:10.5px; color:var(--ink-muted);
  margin-top:5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mb-bar .pm-meta { margin-top:var(--sp-2); padding-top:var(--sp-2); }
.mb-add-btn { margin-top:var(--sp-2); }

/* ---- printer combobox -- the printer list grew to ~17 rows (14 printers +
   3 group headers, more once custom printers are added). A native <select>
   popup is OS-drawn: its size/placement can't be controlled from CSS, so on
   a short viewport the popup runs past the window bottom and the last row
   is clipped. This is a bounded, scrolling, keyboard-navigable listbox built
   on the same pattern as .param-search-results, opened/closed and
   positioned (including the upward "flip" when there's no room below) from
   designer.js's printer-combo module. Every other <select> in the app stays
   native -- only the printer list has the length problem. */
.mb-combo { position:relative; flex:1 1 auto; min-width:0; }
.mb-combo-btn { display:flex; align-items:center; justify-content:space-between; gap:6px;
  width:100%; background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:6px; font-size:12.5px; font-weight:600; font-family:var(--font-ui);
  padding:6px 8px; text-align:left; cursor:pointer; transition:border-color .15s; }
.mb-combo-btn:hover { border-color:var(--ink-muted); }
.mb-combo-btn:focus, .mb-combo-btn[aria-expanded="true"] { border-color:var(--accent); outline:none; }
#printer-combo-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Real element (not a background-image, unlike the native-select chevron
   below) so "rotate 180deg on genuine open state" is a literal transform,
   not an approximation swapped in on :focus. Sized/weighted to match the
   enlarged, higher-contrast native chevron used by every other select. */
/* The chevron is the affordance the user reported as "too small to be seen,
   neither has it been dropped down or not". It carries two jobs at rest --
   say "this opens", and say "it is currently closed" -- so it is sized and
   coloured to be legible without hovering, not just on hover. */
.mb-combo-chevron { flex:none; font-size:14px; line-height:1; color:var(--ink);
  opacity:.75; transition:transform .15s, opacity .15s; }
.mb-combo-btn:hover .mb-combo-chevron, .mb-combo-btn[aria-expanded="true"] .mb-combo-chevron { opacity:1; }
.mb-combo-btn[aria-expanded="true"] .mb-combo-chevron { transform:rotate(180deg); }
.mb-combo-list { position:absolute; left:0; right:0; z-index:9;
  background:var(--surface-raised); border:1px solid var(--line); border-radius:var(--radius);
  box-shadow:0 8px 24px rgba(0,0,0,.35); overflow-y:auto; padding:4px; display:none; }
.mb-combo-list.open { display:block; }
.mb-combo-group { padding:6px 8px 2px; font-size:10px; font-weight:600; color:var(--ink-muted);
  font-family:var(--font-mono); text-transform:uppercase; letter-spacing:.06em; }
.mb-combo-group:first-child { padding-top:4px; }
.mb-combo-option { display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:6px 8px; border-radius:6px; cursor:pointer; font-size:12px; color:var(--ink); }
.mb-combo-option:hover, .mb-combo-option.active { background:var(--accent); color:var(--ink-on-accent); }
.mb-combo-option[aria-selected="true"] { font-weight:600; }
.mb-combo-option[aria-selected="true"]::after { content:'\2713'; flex:none; font-size:11px; }

/* Switch-printer notice -- warn (values clamped) or ok (headroom grew).
   Dismissable (close button) and self-clears after a few seconds; never a
   window.alert. */
.mb-notice { margin-top:var(--sp-2); border-radius:6px; padding:6px 8px; font-size:11px;
  line-height:1.4; display:flex; align-items:flex-start; gap:6px; }
.mb-notice-warn { background:rgba(255,180,84,.10); border:1px solid var(--warn); color:var(--warn); }
.mb-notice-ok { background:rgba(63,220,160,.08); border:1px solid var(--ok); color:var(--ok); }
.mb-notice-msg { flex:1 1 auto; }
.mb-notice-close { flex:none; background:none; border:none; color:inherit; cursor:pointer;
  font-size:13px; line-height:1; padding:0 2px; font-family:var(--font-ui); }

/* ---- design wizard step bar ---- */
.step-bar { display:flex; gap:2px; border-bottom:1px solid var(--line); margin-bottom:10px; }
.step-item { flex:1; display:flex; flex-direction:column; align-items:center; gap:3px;
  background:none; border:none; color:var(--ink-muted); font-size:10.5px;
  padding:7px 2px 8px; cursor:pointer; border-bottom:2px solid transparent;
  transition:color .15s, border-color .15s; white-space:nowrap; }
.step-item:hover { color:var(--ink); }
.step-item:hover .step-num { border-color:var(--ink-muted); }
.step-num { display:flex; align-items:center; justify-content:center; width:17px; height:17px;
  border:1px solid var(--line); border-radius:50%; color:var(--ink-muted); font-size:10px;
  font-weight:600; line-height:1; font-family:var(--font-mono); transition:color .15s, border-color .15s, background .15s; }
.step-label { font-size:10px; transition:color .15s; }
.step-item.done { color:var(--ink); }
.step-item.done .step-num { border-color:var(--accent); color:var(--accent-hover); background:none; }
.step-item.active { color:var(--ink); border-bottom-color:var(--accent); font-weight:600; }
.step-item.active .step-num { border-color:var(--accent); background:var(--accent); color:var(--ink-on-accent); }
.step-item.active .step-label { color:var(--accent-hover); }

.step-panel { display:none; }
.step-panel.active { display:block; animation:step-fade-in .12s ease-out; }
@keyframes step-fade-in { from { opacity:0; } to { opacity:1; } }

/* ---- section separators inside step panels ---- */
.step-panel hr { border:none; border-top:1px solid var(--line); margin:var(--sp-3) 0 var(--sp-2); }

/* ---- step nav (Back / Next) -- pinned as a sibling of #panel-scroll (see
   #panel below), not the last child scrolling with it, so it stays reachable
   regardless of panel content height. flex:0 0 auto keeps it from shrinking
   inside #panel's column flex; the panel's own background so content
   scrolled underneath passes BEHIND it, not through it. Design-mode only --
   toggled the same way #mode-design/#mode-viewer are, see setAppMode(). ---- */
.step-nav { display:none; justify-content:space-between; gap:8px; flex:0 0 auto;
  padding:var(--sp-3) var(--sp-4); border-top:1px solid var(--line); background:var(--surface); }
.step-nav.active { display:flex; }
.step-nav .btn { flex:1; padding:7px 0; text-align:center; }

/* ---- import drop zone ---- */
.import-drop { border:1.5px dashed var(--line); border-radius:var(--radius-lg); padding:28px 14px; text-align:center;
  color:var(--ink-muted); font-size:12.5px; margin-top:8px; cursor:pointer; transition:.15s; }
.import-drop:hover, .import-drop.drag-over { border-color:var(--accent); color:var(--ink); background:var(--surface-raised); }
.mesh-info .drow > span:first-child { flex:0 0 auto; }
.mesh-name { color:var(--ink); font-size:11.5px; text-align:right; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; max-width:170px; }

/* ---- vase-compatibility verdict for an imported STL ---- */
.mesh-check { margin-top:8px; padding:8px 10px; border-radius:6px; font-size:11px; line-height:1.4; }
.mesh-check.warn { background:rgba(255,180,84,.10); border:1px solid var(--warn); }
.mesh-check.bad { background:rgba(255,93,93,.10); border:1px solid var(--danger); }
.mesh-check.info { background:rgba(47,107,255,.10); border:1px solid var(--accent); }
.mesh-check-head { display:flex; align-items:center; gap:6px; font-weight:600; }
.mesh-check.warn .mesh-check-head { color:var(--warn); }
.mesh-check.bad .mesh-check-head { color:var(--danger); }
.mesh-check.info .mesh-check-head { color:var(--accent); }
.mesh-check-head > span:first-child { flex:0 0 auto; font-size:12px; }
.mesh-check-notes { margin:6px 0 0; padding-left:16px; color:var(--ink-muted); }
.mesh-check-notes:empty { display:none; }
.mesh-check-notes li { margin-bottom:3px; }

/* ---- generate / download controls (inside step-generate) ---- */
.gen-status { font-size:11.5px; margin-top:7px; min-height:14px; color:var(--ink-muted); }
.gen-status.err { color:var(--danger); }
.gen-status.ok { color:var(--ok); }
.gen-report { margin-top:8px; max-height:200px; overflow:auto; font-size:10.5px; line-height:1.35;
  background:var(--bg); border:1px solid var(--line); border-radius:6px; padding:8px;
  white-space:pre; color:var(--ink); display:none; }

/* stale gcode: design changed since last generate — unmistakable warn state */
.btn.stale { outline:2px solid var(--warn); outline-offset:2px; }
/* stale primary button: one unambiguous amber signal, not amber ring + blue glow */
.btn-primary.stale { box-shadow:0 2px 8px rgba(255,180,84,.35); }
.btn-primary.stale:hover:not(:disabled) { box-shadow:0 4px 14px rgba(255,180,84,.45); }
.stale-badge { position:absolute; top:40px; left:50%; transform:translateX(-50%); z-index:6;
  padding:5px 12px; border-radius:6px; background:rgba(255,180,84,.14); border:1px solid var(--warn);
  color:var(--warn); font-size:11.5px; font-weight:600; pointer-events:none; display:none; }

/* ---- spacing utilities (4px base scale) ---- */
.mt-1 { margin-top:var(--sp-1); }
.mt-2 { margin-top:var(--sp-2); }
.mt-3 { margin-top:var(--sp-3); }
.mt-4 { margin-top:var(--sp-4); }
.mt-6 { margin-top:var(--sp-6); }

/* =========================================================================
   Point Edit Modifiers — "purple modifiers" (Clay Studio Pro convention):
   operate on the already-sliced wall polyline, not the source geometry.
   Entry button lives at the top of the Texture step; --accent-purple is
   reserved for this subsystem only (see :root comment above).
   ========================================================================= */
.pe-entry-btn { border-color:var(--accent-purple); color:var(--accent-purple); margin-bottom:0; }
.pe-entry-btn:hover:not(:disabled) { border-color:var(--accent-purple-hover); color:var(--accent-purple-hover);
  background:rgba(167,139,250,.10); }
.pe-entry-btn.pe-out-of-scope { opacity:.6; }
.pe-dot { display:inline-block; width:6px; height:6px; border-radius:50%; background:transparent;
  margin-right:6px; transition:background .15s, box-shadow .15s; vertical-align:middle; }
.pe-dot.active { background:var(--accent-purple); box-shadow:0 0 0 3px rgba(167,139,250,.22); }

.pe-modal { position:fixed; inset:0; z-index:50; display:flex; align-items:center; justify-content:center; }
.pe-modal-backdrop { position:absolute; inset:0; background:rgba(0,0,0,.6); }
.pe-modal-card { position:relative; width:min(640px, 92vw); max-height:86vh; display:flex; flex-direction:column;
  background:var(--surface); border:1px solid var(--accent-purple); border-radius:var(--radius-lg);
  /* Softened from rgba(0,0,0,.5): a pure-black shadow this heavy read harsher
     once --bg lifted off near-black -- .4 keeps the modal grounded without
     the seam looking like a hole cut in the page. */
  box-shadow:0 20px 60px rgba(0,0,0,.4); overflow:hidden; }
.pe-modal-header { display:flex; align-items:center; justify-content:space-between; padding:var(--sp-3) var(--sp-4);
  border-bottom:1px solid var(--line); flex:none; }
.pe-modal-header h2 { font-size:14px; margin:0; color:var(--ink); font-weight:600; }
.pe-modal-body { display:flex; flex:1 1 auto; min-height:0; }
.pe-rail { flex:0 0 172px; border-right:1px solid var(--line); padding:var(--sp-2); overflow-y:auto; }
.pe-rail-item { display:block; width:100%; text-align:left; background:none; border:none; color:var(--ink-muted);
  font-size:12px; font-weight:600; padding:8px 10px; border-radius:6px; cursor:pointer; margin-bottom:2px;
  transition:background .15s, color .15s; font-family:var(--font-ui); }
.pe-rail-item:hover { color:var(--ink); background:var(--surface-raised); }
.pe-rail-item.active { color:var(--accent-purple); background:rgba(167,139,250,.12); }
.pe-rail-item.enabled::after { content:''; display:inline-block; width:6px; height:6px; border-radius:50%;
  background:var(--accent-purple); margin-left:6px; vertical-align:middle; }
.pe-rail-sep { height:1px; background:var(--line); margin:var(--sp-2) 4px; }
.pe-panels { flex:1 1 auto; padding:var(--sp-4); overflow-y:auto; }
.pe-panel { display:none; }
.pe-panel.active { display:block; }
.pe-panel input[type=checkbox]:checked { background:var(--accent-purple); border-color:var(--accent-purple); }
.pe-panel .hint { margin:4px 0 var(--sp-3); }
.pe-modal-footer { display:flex; justify-content:space-between; gap:8px; padding:var(--sp-3) var(--sp-4);
  border-top:1px solid var(--line); flex:none; }
.pe-grid { display:grid; gap:3px; margin-top:var(--sp-2); }
.pe-grid input { width:100%; background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:4px; padding:4px 2px; font-size:10.5px; text-align:center; font-family:var(--font-ui); }
.pe-grid input:focus { border-color:var(--accent-purple); outline:none; }

/* =========================================================================
   Custom printer import/edit -- sidebar meta row + review modal. Ordinary
   --accent blue chrome (--accent-purple stays reserved for Point Edit).
   --ok/--warn/--danger are used here only for the safety report and the
   parsed/default/clamped source tags, which is exactly their reserved
   purpose (see :root comment above).
   ========================================================================= */
.pm-meta { margin-top:var(--sp-2); padding-top:var(--sp-2); border-top:1px solid var(--line); }
.pm-meta-row { display:flex; align-items:center; justify-content:space-between; gap:8px; }
.pm-meta-source { font-size:11px; color:var(--ink-muted); }
.pm-meta-actions { display:flex; gap:var(--sp-3); margin-top:4px; }
.pm-link { background:none; border:none; padding:0; margin:0; font-size:11px; font-weight:600;
  color:var(--accent-hover); cursor:pointer; font-family:var(--font-ui); }
.pm-link:hover { text-decoration:underline; }
.pm-link-danger { color:var(--ink-muted); }
.pm-link-danger:hover { color:var(--danger); }
.pm-link-confirm { color:var(--danger); }

.pm-modal { position:fixed; inset:0; z-index:50; display:flex; align-items:center; justify-content:center; }
.pm-modal-backdrop { position:absolute; inset:0; background:rgba(0,0,0,.6); }
.pm-modal-card { position:relative; width:min(760px, 94vw); max-height:88vh; display:flex; flex-direction:column;
  background:var(--surface); border:1px solid var(--accent); border-radius:var(--radius-lg);
  /* Softened from rgba(0,0,0,.5) for the same reason as .pe-modal-card above. */
  box-shadow:0 20px 60px rgba(0,0,0,.4); overflow:hidden; }
/* Horizontal padding is --sp-6 (24px), not --sp-4: this card is up to 760px
   wide and prose runs the full width, so 16px left the text visibly crowding
   the border. Header, body and footer share the inset so everything lines up
   on one vertical edge. Narrow viewports drop back to 16px below. */
.pm-modal-header { display:flex; align-items:center; justify-content:space-between;
  padding:var(--sp-3) var(--sp-6); border-bottom:1px solid var(--line); flex:none; }
.pm-modal-header h2 { font-size:14px; margin:0; color:var(--ink); font-weight:600; }
.pm-modal-body { flex:1 1 auto; min-height:0; overflow-y:auto; padding:var(--sp-4) var(--sp-6); }
.pm-modal-footer { display:flex; justify-content:flex-end; gap:8px;
  padding:var(--sp-3) var(--sp-6); border-top:1px solid var(--line); flex:none; }
@media (max-width: 560px) {
  .pm-modal-header, .pm-modal-footer { padding-left:var(--sp-4); padding-right:var(--sp-4); }
  .pm-modal-body { padding-left:var(--sp-4); padding-right:var(--sp-4); }
}

.pm-drop { margin-bottom:0; }
/* .hint.tight-top pulls a caption up by 2px, which is right under a form row
   but wrong under the drop zone: that has a visible dashed border, and the
   -2px dragged the text into it (measured gap: -2px, i.e. overlapping).
   Later in the file than .hint.tight-top and equal specificity, so this wins. */
.pm-drop + .hint { margin-top:var(--sp-3); }
.pm-error { margin-top:var(--sp-3); font-size:12px; color:var(--danger); }

.pm-review-head { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-bottom:var(--sp-3); }
.pm-name-input { flex:1 1 160px; min-width:0; background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:6px; padding:6px 8px; font-size:13px; }
.pm-name-input:focus { border-color:var(--accent); outline:none; }
.pm-badge { flex:0 0 auto; font-size:10.5px; font-family:var(--font-mono); text-transform:uppercase;
  letter-spacing:.06em; color:var(--ink-muted); background:var(--surface-raised); border:1px solid var(--line);
  border-radius:999px; padding:3px 9px; }

.pm-report { margin-bottom:var(--sp-4); border:1px solid var(--line); border-radius:var(--radius-lg);
  padding:var(--sp-3); background:var(--surface-raised); }
.pm-report-ok { font-size:12px; color:var(--ok); }
.pm-report-head { font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  color:var(--ink-muted); margin:var(--sp-2) 0 4px; }
.pm-report-head:first-child { margin-top:0; }
.pm-report-head-error { color:var(--danger); }
.pm-report-head-warn { color:var(--warn); }
/* Copies the whole report as plain text. Sits at the end of the list rather
   than in the heading: the heading is per-severity, the report is one thing. */
.pm-report-copy { margin-top:var(--sp-3); background:none; border:1px solid var(--line);
  color:var(--ink-muted); font-family:var(--font-ui); font-size:11px; padding:4px 10px;
  border-radius:999px; cursor:pointer; transition:color .15s, border-color .15s; }
.pm-report-copy:hover { color:var(--ink); border-color:var(--ink-muted); }
.pm-report-copy-done { color:var(--ok); border-color:var(--ok); }
.pm-issue { display:flex; flex-wrap:wrap; align-items:center; gap:8px; width:100%;
  border-left:2px solid var(--line); color:var(--ink); font-size:12px;
  line-height:1.4; padding:4px 0 4px 8px; margin-bottom:2px; }
.pm-issue-msg { flex:1 1 auto; text-align:left; background:none; border:none; padding:0; margin:0;
  color:var(--ink); font-size:12px; font-family:var(--font-ui); line-height:1.4; }
button.pm-issue-msg { cursor:pointer; }
button.pm-issue-msg:hover { text-decoration:underline; }
.pm-issue-error { border-left-color:var(--danger); }
.pm-issue-warn { border-left-color:var(--warn); }
.pm-quickfix { flex:0 0 auto; border:1px solid var(--accent); color:var(--accent-hover); background:none;
  border-radius:6px; padding:2px 8px; font-size:10.5px; font-weight:600; cursor:pointer; font-family:var(--font-ui); }
.pm-quickfix:hover { background:rgba(47,107,255,.12); }

.pm-group-head { font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.06em;
  color:var(--ink-muted); font-family:var(--font-mono); margin:var(--sp-3) 0 2px; }
.pm-group-head:first-child { margin-top:0; }
.pm-field-row { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:6px 10px;
  font-size:12px; padding:5px 0; border-bottom:1px solid var(--line); }
.pm-field-label { color:var(--ink-muted); flex:1 1 140px; min-width:0; }
.pm-field-control { display:flex; align-items:center; gap:6px; flex:0 0 auto; }
/* background-COLOR, never the `background` shorthand: this class is also worn
   by <select> (see select.pm-field-input below), and the shorthand resets
   background-repeat/position to their initials -- which tiled the chevron SVG
   across the whole control as a sawtooth pattern behind the value. */
.pm-field-input { background-color:var(--input-bg); color:var(--ink); border:1px solid var(--line);
  border-radius:6px; padding:3px 5px; font-size:12px; width:92px; }
.pm-field-input:focus { border-color:var(--accent); outline:none; }
select.pm-field-input { width:116px; padding-right:24px; }
.pm-tag { flex:0 0 auto; font-size:10px; font-family:var(--font-mono); text-transform:uppercase;
  letter-spacing:.04em; padding:2px 6px; border-radius:999px; color:var(--ink-muted);
  background:var(--surface-raised); border:1px solid var(--line); cursor:default; }
.pm-tag-default, .pm-tag-clamped { color:var(--warn); border-color:rgba(255,180,84,.4); }
.pm-tag-warn { color:var(--warn); border-color:rgba(255,180,84,.4); }
/* An override the user typed themselves: neither imported nor invented, so it
   reads in the neutral accent rather than the amber "we chose this" state. */
.pm-tag-edited { color:var(--accent); border-color:rgba(47,107,255,.45); }

.pm-gcode-block { margin-top:var(--sp-3); }
.pm-gcode-label { font-size:11px; color:var(--ink-muted); margin-bottom:4px; }
.pm-gcode-textarea { width:100%; box-sizing:border-box; resize:vertical; background-color:var(--input-bg); color:var(--ink);
  border:1px solid var(--line); border-radius:6px; padding:8px; font-family:var(--font-mono); font-size:11.5px;
  line-height:1.5; }
.pm-gcode-textarea:focus { border-color:var(--accent); outline:none; }

/* "We changed your G-code so it can actually print" is a safety-state notice
   (a start block with no heating at all is a guaranteed abort on the first
   extrusion move, not a stylistic nit), so --warn is the right token under
   the reserved-token rule -- same amber as .pm-stripped-* just below, for
   the same reason: something the user did not type is now in their G-code
   and they should look at it before printing. */
.pm-repaired { margin-top:var(--sp-3); }
.pm-repaired-toggle { width:100%; text-align:left; background:var(--surface-raised); border:1px solid var(--line);
  color:var(--ink); border-radius:6px; padding:6px 10px; font-size:11.5px; font-weight:600; cursor:pointer;
  font-family:var(--font-ui); }
.pm-repaired-toggle:hover { border-color:var(--warn); }
.pm-repaired-list { list-style:none; margin:6px 0 0; padding:0 0 0 10px; border-left:2px solid var(--warn);
  font-family:var(--font-mono); font-size:11px; color:var(--ink-muted); }
.pm-repaired-list li { padding:2px 0; }

.pm-stripped { margin-top:var(--sp-3); }
.pm-stripped-toggle { width:100%; text-align:left; background:var(--surface-raised); border:1px solid var(--line);
  color:var(--ink); border-radius:6px; padding:6px 10px; font-size:11.5px; font-weight:600; cursor:pointer;
  font-family:var(--font-ui); }
.pm-stripped-toggle:hover { border-color:var(--warn); }
.pm-stripped-list { list-style:none; margin:6px 0 0; padding:0 0 0 10px; border-left:2px solid var(--warn);
  font-family:var(--font-mono); font-size:11px; color:var(--ink-muted); }
.pm-stripped-list li { padding:2px 0; }
.pm-allow-raw { margin-top:var(--sp-2); font-size:12px; }

/* ---- clearance stage (stage 3): schematic diagram + z_amp_max's one field ---- */
.pm-clearance-svg { display:block; margin:10px auto 6px; width:100%; max-width:220px; height:auto; }
.pmc-hatch-line { stroke:var(--line); stroke-width:1; }
.pmc-fill { fill:url(#pm-hatch); stroke:var(--line); stroke-width:1; }
.pmc-surface { stroke:var(--ink-muted); stroke-width:1; stroke-dasharray:4 3; }
.pmc-hardware { fill:var(--surface-raised); stroke:var(--ink-muted); stroke-width:1; }
.pmc-tip { fill:var(--accent); }
.pmc-dim { stroke:var(--accent); stroke-width:1; fill:none; }
.pmc-label { font-family:var(--font-ui); font-size:8px; }
.pmc-label-accent { fill:var(--accent-hover); font-weight:600; }
.pmc-caption { margin-bottom:var(--sp-2); }

/* =========================================================================
   Printer-import modal -- prose hierarchy (all three pm-stage screens).
   Every .hint inside this modal used to render at the sitewide 11px whether
   it was a one-line field caption or a multi-line explanation running the
   full 760px card width, so a lede question, a diagram caption and a safety
   note all looked identical -- no reading hierarchy at all. Scoped to
   ".pm-stage .hint" rather than editing the bare .hint rule above, so
   field-level one-line hints elsewhere in the app (drow hints, mesh-info,
   etc.) are untouched. This one rule is what keeps stage-drop's "Supported
   formats..." line and stage-clearance's prose on the same scale -- there
   is nothing left to fix on stage-review, which carries no loose .hint
   prose of its own (its report/field rows already use the severity system
   below, on purpose).
   ========================================================================= */
.pm-stage .hint { font-size:13px; line-height:1.6; }

/* The sentence that opens a stage -- Stage C's "How far can the nozzle
   dip..." -- is promoted above the generic .hint treatment: larger,
   full-ink, so it reads as the question the rest of the stage answers,
   not just another line of muted caption text. */
.pm-lede { font-size:15px; line-height:1.6; color:var(--ink); margin:0 0 var(--sp-2); }

/* Diagram caption stays visibly secondary to the lede above it: smaller
   than even the raised .pm-stage .hint baseline. Specificity (3 classes)
   deliberately beats ".pm-stage .hint" (2 classes) so this wins regardless
   of source order in the file. */
.pm-stage .hint.pmc-caption { font-size:12px; line-height:1.5; }

/* The closing clearance note is a real hardware-collision risk flag, not
   body copy: it says the amplitude ceiling above is an unverified guess,
   and an over-large guess is exactly what lets the nozzle strike the fan
   shroud or probe body it was measured against. That makes it one of the
   legitimate --warn uses under the reserved-token rule (see :root comment
   -- --warn is for probe limits, melt ceiling, risk flags, clamp events).
   Boxed like the app's other warn idioms (.mesh-check.warn, .mb-notice-warn)
   so it reads as a flag to stop on, not another paragraph to skim past. */
.pm-caution { margin-top:var(--sp-3); padding:var(--sp-2) var(--sp-3);
  border-radius:6px; background:rgba(255,180,84,.10); border:1px solid var(--warn);
  color:var(--warn); font-size:12.5px; line-height:1.5; }

/* =========================================================================
   Collapsible sections — every h3.section-heading and .group h2 that owns
   a #sec-body-* container becomes a click/keyboard toggle (state persisted
   in localStorage under 'trident_sections' by designer.js). Purely additive:
   the toggle affordance rides on the existing heading element.
   ========================================================================= */
.sec-toggle { cursor:pointer; user-select:none; display:flex; align-items:center;
  justify-content:space-between; gap:var(--sp-2); border-radius:6px; margin-left:-4px;
  margin-right:-4px; padding:2px 4px; transition:background-color .15s; }
.sec-toggle:hover { background:var(--surface-raised); }
.sec-chevron { flex:none; font-size:8px; color:var(--ink-muted); transition:transform .15s; }
.sec-toggle.sec-closed .sec-chevron { transform:rotate(-90deg); }

/* ---- design panel header: active-summary chips + hint-density toggle ---- */
.design-panel-header { display:flex; align-items:flex-start; gap:var(--sp-2); margin-bottom:var(--sp-3); }
.active-summary { flex:1 1 auto; display:flex; flex-wrap:wrap; gap:5px; min-height:0; }
.active-summary:empty { display:none; }
.chip { background:var(--surface-raised); border:1px solid var(--line); color:var(--ink-muted);
  font-size:10.5px; font-family:var(--font-ui); border-radius:999px; padding:3px 9px; cursor:pointer;
  transition:border-color .15s, color .15s, background-color .15s; white-space:nowrap; }
.chip:hover { border-color:var(--accent); color:var(--ink); }
.hint-toggle { flex:none; background:var(--surface-raised); border:1px solid var(--line);
  color:var(--ink-muted); font-size:10.5px; font-weight:600; border-radius:6px; padding:5px 9px;
  cursor:pointer; font-family:var(--font-ui); transition:border-color .15s, color .15s; }
.hint-toggle:hover { border-color:var(--accent); color:var(--ink); }
.hint-toggle[aria-pressed="false"] { color:var(--ink-muted); opacity:.55; }

/* Hint-density toggle: when off, every .hint block in either panel is hidden. */
body.hints-hidden .hint { display:none; }

/* ---- generate step: two distinct output-path cards (task 2) ---- */
.output-card { background:var(--surface-raised); border:1px solid var(--line); border-radius:var(--radius-lg);
  padding:var(--sp-3); margin-top:var(--sp-4); }
.output-card .section-heading { margin-bottom:4px; }
.output-divider { height:1px; background:var(--line); margin:var(--sp-4) 0 0; }

/* =========================================================================
   Responsive floor — below 900px this is a "best on desktop" notice, not a
   real mobile layout. The primary interactions (cage-handle drag, curve
   editor drag, arrow-key per-turn playback) are hover/fine-motor/keyboard
   dependent and are not reworked for touch.
   ========================================================================= */
.mobile-note { display:none; }
@media (max-width:900px) {
  .app-shell { flex-direction:column; }
  .canvas-wrap { flex:1 1 40vh; min-height:240px; }
  .panel { width:auto; flex:1 1 auto; border-left:none; border-top:1px solid var(--line); }
  .splitter { display:none; }
  .mobile-note { display:block; font-size:11px; color:var(--warn); background:rgba(255,180,84,.10);
    border:1px solid var(--line); border-radius:6px; padding:8px 10px; margin-bottom:var(--sp-4); }
  .telemetry-card { top:8px; right:8px; width:150px; max-width:calc(100% - 16px); }
  .draft-preview-label { top:8px; left:8px; }
}

/* =========================================================================
   Parameter help tooltips (Orca-style hover panel) -- one reused floating
   panel, positioned/flipped in JS (see designer.js). Plain --surface/--line
   chrome, matching .hint / .pm-report rather than a new visual language.
   Deliberately uses neither --accent-purple (Point Edit only) nor the
   --ok/--warn/--danger trio (safety states only).
   ========================================================================= */
.param-tip {
  position:fixed; z-index:60; max-width:300px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
  padding:var(--sp-2) var(--sp-3);
  box-shadow:0 8px 24px rgba(0,0,0,.45);
  pointer-events:none;
  opacity:0; visibility:hidden; transition:opacity .12s ease;
}
.param-tip.open { opacity:1; visibility:visible; }
.param-tip-desc {
  font-family:var(--font-ui); font-size:12px; line-height:1.5; color:var(--ink);
}
.param-tip-meta {
  font-family:var(--font-mono); font-size:10.5px; line-height:1.5; color:var(--ink-muted);
  margin-top:6px; word-break:break-word;
}
.param-tip-meta:first-of-type { margin-top:8px; }
