:root {
  --topbar-h: 52px;          /* the top bar's height, kept by ui/topbar.js */
  --bg: #14161a;
  --panel: #1c1f26;
  --line: #2c313b;
  --text: #e7e9ee;
  --dim: #8d95a5;
  --accent: #4da3ff;
  --btn: #2a303a;       /* button fill — deliberately lighter than --panel so a
                           control reads as a control against the dark viewport */
  --btn-hi: #333a46;    /* button hover fill */
  --btn-line: #454d5c;  /* button edge, strong enough to see over the 3D scene */
}

* { box-sizing: border-box; }

/* An author `display` rule outranks the UA stylesheet's [hidden]{display:none},
   so any flex/grid panel here would ignore its own hidden attribute. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font: 13px/1.45 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
}

#viewport { position: fixed; inset: 0; }
#viewport canvas { display: block; }
/* Nothing imported yet (ui/plates.js): no view to orbit, no fields to set. */
body.no-part #viewport canvas { pointer-events: none; }

/* ---------- long-build spinner ---------- */
#spinner {
  position: fixed;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 14px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--dim);
  font-size: 12px;
  pointer-events: none;               /* informational only; never blocks the canvas */
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.4);
  /* Always laid out (never display:none) so the opacity transition fires every
     time; visibility keeps the idle spinner out of the a11y/hit tree. Toggling
     display:none instead skipped the fade unpredictably. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.5s ease, visibility 0s linear 0.5s;   /* fade out, then hide */
}
#spinner.show {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.5s ease, visibility 0s;               /* show immediately, fade in */
}
@media (prefers-reduced-motion: reduce) {
  #spinner, #spinner.show { transition: none; }
}
#spinner .ring {
  width: 15px; height: 15px;
  border: 2px solid var(--btn-line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { #spinner .ring { animation-duration: 2.4s; } }

/* ---------- top bar ---------- */

#topbar {
  position: fixed;
  z-index: 4;                /* above the empty-state scrim, or the controls read
                                as disabled on the one screen that must invite a click;
                                and above #right-rail (3), which the Export menu drops over */
  top: 0; left: 0; right: 0;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  background: linear-gradient(var(--bg), transparent);
  pointer-events: none;
  /* a window too narrow for one row wraps onto a second (and third) instead of
     pushing buttons off the right edge; ui/topbar.js measures the bar into
     --topbar-h so the rails and dropped panels start below it */
  flex-wrap: wrap;
  row-gap: 8px;
  justify-content: flex-end;   /* a wrapped row stays with the buttons on the right */
}
#topbar > * { pointer-events: auto; }
/* Narrower windows: shed words before anything wraps. GitHub and Ko-fi go to
   icons first (their titles and aria-labels still say what they are), then the
   brand text, the Build volume label and some slider width. */
@media (max-width: 1320px) {
  #topbar { gap: 10px; }
  #topbar .btn.gh .lbl, #topbar .btn.kofi .lbl { display: none; }
}
@media (max-width: 1140px) {
  #topbar .brand-text, #topbar .field .lbl { display: none; }
  #topbar #thr { width: 90px; }
}
/* wrapped onto a phone: the first row is the logo and the import controls, so the
   logo stays left of them rather than floating mid-row */
@media (max-width: 600px) {
  #topbar .brand { margin-right: auto; }
  /* Export can land first on its wrapped row: drop its menu rightward, not off
     the left edge */
  #export-menu { right: auto; left: 0; }
}
/* A phone: one row -- logo, Import, Add fins, Export, the ☰ button (and the error
   badge). Everything marked .more folds behind ☰ (ui/topbar.js toggles
   .more-open). Opened, the bar becomes a sheet under that row: the two fields as
   label | control rows, then Calibrate, Plugins and Language as full-width rows
   whose panels open in place (an accordion, not a popover off the sheet's edge),
   then GitHub and Ko-fi side by side. */
#topbar .more-btn { display: none; }
@media (max-width: 600px) {
  #topbar { gap: 8px; }
  #topbar .more-btn { display: inline-flex; align-items: center; justify-content: center; min-width: 40px; }
  #topbar .more-btn[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); }
  #topbar:not(.more-open) > .more { display: none; }
  #topbar > .more { order: 1; }

  #topbar.more-open {
    background: var(--panel);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .5);
    padding-bottom: 14px;
  }
  /* the first .more row opens the sheet: a hairline under the toolbar row */
  #topbar.more-open > .field.more:first-of-type {
    margin-top: 6px; padding-top: 14px; border-top: 1px solid var(--line);
  }
  /* label | control | value */
  #topbar.more-open > .field.more {
    flex: 1 0 100%;
    display: flex; align-items: center; gap: 10px;
    min-height: 40px; color: var(--dim);
  }
  #topbar.more-open > .field.more > span:first-child,
  #topbar.more-open > .field.more > .lbl { display: inline; flex: 0 0 96px; }
  #topbar.more-open #volume { flex: 1; margin-left: 0; min-height: 36px; }
  #topbar.more-open #thr { flex: 1; width: auto; margin: 0; }
  #topbar.more-open #thr-val { flex: 0 0 36px; text-align: right; }
  #topbar.more-open > .custom-vol { padding-left: 106px; gap: 6px; }
  #topbar.more-open > .custom-vol input { flex: 1; min-width: 0; min-height: 34px; }

  /* menu rows */
  #topbar.more-open > .calibrate-wrap,
  #topbar.more-open > .plugins-wrap,
  #topbar.more-open > .menu-wrap.more { flex: 1 0 100%; }
  #topbar.more-open > .calibrate-wrap { margin-top: 6px; padding-top: 14px; border-top: 1px solid var(--line); }
  #topbar.more-open > .more > .btn {
    display: block; width: 100%; min-height: 44px;
    padding: 0 14px; text-align: start; font-size: 15px;
  }
  /* their panels open in place under the row */
  #topbar.more-open > .more > .menu {
    position: static; width: auto; min-width: 0;
    max-height: 50vh; overflow-y: auto;
    margin-top: 6px; box-shadow: none;
  }
  #topbar.more-open > .more > .menu [role="menuitem"] { min-height: 40px; text-align: start; }

  /* links: two equal buttons, words back on */
  #topbar.more-open > .btn.more {
    flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center;
    gap: 6px; min-height: 44px; margin-top: 6px;
  }
  #topbar.more-open > .btn.more .lbl { display: inline; }
}
#topbar .spacer { flex: 1; pointer-events: none; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 650;
  letter-spacing: .02em;
  margin-right: 4px;
}
.brand-ver { font-size: 10px; font-weight: 400; letter-spacing: 0; color: var(--dim); margin-left: 2px; }
.brand-mark { height: 20px; width: auto; display: block; }

.btn {
  display: inline-block;
  padding: 5px 12px;
  /* A plain button floats over the near-black viewport, so --panel/--line (both
     within a few points of --bg) made it read as a faint dark smudge. Lift it
     off the scene: a lighter fill, a defined edge, and a drop shadow. */
  border: 1px solid var(--btn-line);
  border-radius: 6px;
  background: var(--btn);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  user-select: none;
}
.btn:hover { border-color: var(--accent); color: var(--accent); background: var(--btn-hi); }

/* Keyboard focus was invisible: no control showed a focus ring, so tabbing
   through the tool was untrackable. One visible ring for every focusable thing
   -- buttons, the select, the slider, the file label -- shown only for keyboard
   focus (:focus-visible) so a mouse click doesn't leave a lingering outline. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.btn:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}
/* A field that doesn't apply in this mode (Wide-face coverage in Full coverage). */
.fld.disabled { opacity: 0.45; }
.fld.disabled input { cursor: not-allowed; }
/* A disabled Undo/Redo shouldn't look clickable. */
.btn:disabled {
  opacity: 0.4;
  cursor: default;
  box-shadow: none;
}
.btn:disabled:hover { border-color: var(--btn-line); color: var(--text); background: var(--btn); }
/* Export menu: a small panel dropped under its button, items styled as rows. */
.menu-wrap { position: relative; }
.menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  min-width: 100%;
  padding: 4px;
  border: 1px solid var(--btn-line);
  border-radius: 6px;
  background: var(--btn);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}
.menu[hidden] { display: none; }
.menu button {
  padding: 5px 10px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.menu button:hover, .menu button:focus-visible { background: var(--btn-hi); color: var(--accent); outline: none; }
.menu button:disabled { opacity: 0.4; cursor: default; background: none; color: var(--text); }
/* Export menu's no-fins line, over the rows: wraps at a narrow width so the panel
   grows only a little while it shows. */
.menu-note { max-width: 200px; margin: 2px 6px 6px; color: var(--dim); font-size: 12px; line-height: 1.4; }
.menu-note[hidden] { display: none; }
.menu-note.warn { color: #ff6b6b; }
/* Plugins menu (web/ui/plugins.js): the same dropped panel, wide enough for a
   row per plugin with its install note; scrolls when the window is short. */
.plugins-wrap, .calibrate-wrap { position: relative; }
/* Calibrate menu (web/ui/calibrate.js) borrows this panel and its rows. */
.calibrate-intro { margin: 2px 4px 8px; color: var(--dim); font-size: 12px; line-height: 1.4; }
.plugins-panel {
  width: min(380px, calc(100vw - 32px));
  max-height: calc(100vh - var(--topbar-h) - 16px);   /* under a bar of any height */
  overflow-y: auto;
  padding: 8px 10px;
  gap: 2px;
  font-size: 13px;
  white-space: normal;
}
/* Narrow window: drop the panel from the window's right edge, not the button's, so
   it can't run off the left. */
@media (max-width: 600px) {
  .plugins-panel { position: fixed; top: var(--topbar-h); left: 16px; right: 16px; width: auto; min-width: 0; }
}
.plugins-computer { display: flex; align-items: center; gap: 8px; color: var(--dim); padding: 2px 0 6px; }
.plugins-computer select { flex: 1; }
.plugin { border-top: 1px solid var(--line); }
.plugin-head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 4px; cursor: pointer; list-style: none; border-radius: 6px;
}
.plugin-head::-webkit-details-marker { display: none; }
.plugin-head::before {      /* the open/closed caret */
  content: '▸'; color: var(--dim); width: 10px; flex: none;
  transition: transform 0.12s;
}
.plugin[open] > .plugin-head::before { transform: rotate(90deg); }
.plugin-head:hover, .plugin-head:focus-visible { background: var(--btn-hi); outline: none; }
.plugin-name { flex: 1; }
.plugin-name b { font-weight: 600; }
.plugin-v { color: var(--dim); font-size: 12px; font-variant-numeric: tabular-nums; }
.plugin-head .btn { text-decoration: none; font-size: 12px; }
.plugin-none { color: var(--dim); font-size: 12px; }
.plugin-body { padding: 0 4px 10px 22px; }
.plugin-needs, .plugin-ver { color: var(--dim); font-size: 12px; margin-top: 2px; }
.plugin-ver { font-variant-numeric: tabular-nums; }
.plugin-how { margin-top: 4px; font-size: 12px; line-height: 1.4; }
.plugin-note { margin-top: 4px; font-size: 12px; line-height: 1.4; color: #ffb492; }
.plugins-foot { margin: 6px 0 0; padding-top: 8px; border-top: 1px solid var(--line); color: var(--dim); font-size: 12px; }
.plugins-foot a { color: var(--accent); }
.btn.primary {
  border-color: var(--accent);
  color: #06121f;
  background: var(--accent);
  font-weight: 600;
}
.btn.primary:hover { background: #6cb5ff; color: #06121f; }

.field { color: var(--dim); }
.field:has(:disabled) { opacity: 0.45; }
.field select,
.plugins-computer select {
  margin-left: 6px;
  padding: 4px 8px;
  border: 1px solid var(--btn-line);
  border-radius: 6px;
  background: var(--btn);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  font: inherit;
}
.plugins-computer select { margin-left: 0; }   /* its label already has an 8px gap */

.badge {
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--dim);
  font-size: 12px;
}

/* ---------- left rail ---------- */

/* One scrolling column, pinned from under the top bar to the bottom of the screen,
   holds the stats card (the pose and view tools moved to the view toolbar, local
   issue 42). A window too short for the card scrolls the rail; the toolbar steps
   right of the card when it reaches down beside it. pointer-events are off on the
   rail so the empty space below the card still orbits the scene. */
#left-rail {
  position: fixed;
  top: var(--topbar-h); bottom: 14px; left: 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  overflow-y: auto;
  overflow-x: hidden;
  pointer-events: none;
  scrollbar-width: thin;
}
#left-rail > * { pointer-events: auto; }
.rail-spacer[hidden] { display: none; }

/* Right rail: same one-column-that-scrolls trick as #left-rail, for the fin
   options (top, grows DOWN as draw mode adds controls) and the status/receipt
   card (bottom). Independently-fixed, they used to grow into each other and off
   the bottom of the screen in draw mode; sharing a rail with a flex spacer keeps
   options up top, status pinned low, and scrolls the pair under a short window
   instead of overlapping. pointer-events off on the rail so the gap orbits the
   scene, back on for the cards. */
#right-rail {
  position: fixed;
  top: var(--topbar-h); bottom: 34px; right: 14px;   /* bottom clears the fps readout */
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  overflow-y: auto;
  overflow-x: hidden;
  pointer-events: none;
  scrollbar-width: thin;
}
#right-rail > * { pointer-events: auto; }
#right-rail > .rail-spacer { flex: 1 1 0; min-height: 0; pointer-events: none; }

/* ---------- stats ---------- */

#stats {
  /* Fixed width so a long value (a filename, the receipt's sentence) wraps
     inside the panel instead of stretching the whole left column. Shrinks on
     very narrow viewports. */
  width: 272px;
  max-width: calc(100vw - 28px);
  box-sizing: border-box;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  backdrop-filter: blur(6px);
}
#stats dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 3px 16px;
  margin: 0;
}
#stats dt { color: var(--dim); }
#stats dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
  min-width: 0;
  overflow-wrap: anywhere;   /* a long filename wraps rather than overflowing */
}
#stats dd.warn { color: #ffb454; }
#stats dd.good { color: #6fcf8b; }
/* Reserve two lines for the analysis timing: it wraps to a second line once the
   fin/pad time is appended, and that one-line growth used to nudge everything below
   the panel down when fins were added. Always two lines high = no shift. */
#stats #s-time { display: block; min-height: 2.6em; line-height: 1.3; }
#stats .part-btns { display: flex; gap: 6px; margin-top: 10px; }
#stats hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 9px 0;
}

/* legend swatches, keyed to the shading colours in ui/part.js */
.sw {
  display: inline-block;
  width: 8px; height: 8px;
  margin-right: 6px;
  border-radius: 2px;
  vertical-align: baseline;
}
.sw-over { background: #ff5a4d; }
.sw-small { background: #ffb454; margin: 0 3px 0 0; }
.sw-bed  { background: #3f7fd0; }
.sw-fin  { background: #59d98e; }
.sw-pad  { background: #e8b64c; }

/* What the fins did and did not do. Wraps rather than truncating: every line
   here is a limitation the user needs before they commit to a print. */
.note {
  max-width: 26ch;
  margin: 0;
  color: var(--dim);
  font-size: 11px;
  line-height: 1.45;
}
.note:empty { display: none; }
.note.good { color: #6fcf8b; }   /* the "you don't need me" all-clear */

/* The receipt: the "what did this get me" payoff. The exact grams are the
   headline; the saving is the reason to bother. Set apart from the plain stats
   so the number reads as a result, not another row. */
#receipt {
  margin: 0;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-left: 3px solid #6fcf8b;
  border-radius: 6px;
  background: #171a20;
  display: flex;
  align-items: baseline;
  gap: 4px;
}
#receipt[hidden] { display: none; }
#r-grams {
  font-size: 20px;
  font-weight: 650;
  color: #6fcf8b;
  font-variant-numeric: tabular-nums;
}
#receipt .r-unit { color: var(--dim); font-size: 11px; }
/* The detail (methodology + the "20-45% less plastic" claim) lives behind this
   icon's tooltip -- long and repetitive to show every time, so it's opt-in. */
#receipt .r-info {
  margin-left: auto;
  align-self: center;
  width: 16px; height: 16px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--dim);
  font: italic 600 11px/1 Georgia, serif;
  cursor: help;
}
#receipt .r-info:hover { color: var(--text); border-color: var(--dim); }

/* "Something didn't get supported?" (ui/report.js): quiet links under the receipt,
   there when wanted, never louder than the result above them. */
.report-row { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 11px; }
.report-row[hidden] { display: none; }
.report-row .linkish {
  padding: 0; border: 0; background: none; color: var(--dim);
  font: inherit; text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.report-row .linkish:hover { color: var(--text); }

/* The (i) on the Fins row: same opt-in-detail idea as the receipt's, for the
   how-it-works / how-to-fix text that reads as a wall when it's always on. */
.note-info {
  margin-left: 5px;
  width: 15px; height: 15px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--dim);
  font: italic 600 10px/1 Georgia, serif;
  cursor: help;
  vertical-align: middle;
}
.note-info[hidden] { display: none; }
.note-info:hover { color: var(--text); border-color: var(--dim); }

/* Fin options, anchored under the toggle rather than inside the topbar, so
   turning fins on never moves another control. */
#fin-opts {
  width: 264px;
  max-width: calc(100vw - 28px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter: blur(6px);
}
#fin-opts .fld {
  display: flex;
  flex-direction: column;
  gap: 4px;
  color: var(--dim);
}
#fin-opts select,
.vb-pop .nav-pick select,
#fin-opts input[type=number],
#fin-opts input[type=search] {
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
}
#fin-opts input[type=number] { width: 72px; }
#fin-opts input[type=range] { width: 100%; margin: 0; accent-color: var(--accent); }
/* A one-control field: label left, control right, one line instead of two. */
/* Placement: three joined buttons, the chosen one filled, and a note under them. */
#fin-opts .mode-seg { display: flex; }
#fin-opts .mode-seg .btn { flex: 1 1 auto; padding: 5px 8px; font-size: 12px; white-space: nowrap; text-align: center;
  border-radius: 0; box-shadow: none; }
#fin-opts .mode-seg .btn + .btn { margin-left: -1px; }
#fin-opts .mode-seg .btn:first-child { border-radius: 6px 0 0 6px; }
#fin-opts .mode-seg .btn:last-child { border-radius: 0 6px 6px 0; }
#fin-opts .mode-seg .btn[aria-pressed="true"] { position: relative; border-color: var(--accent); background: var(--accent);
  color: #06121f; font-weight: 600; }
#fin-opts .mode-seg .btn:focus-visible { position: relative; z-index: 1; }   /* ring over the neighbour */
#fin-opts .mode-note { margin: 0; font-size: 11px; line-height: 1.35; color: var(--dim); }
#fin-opts .fld.row {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
#fin-opts .fld.row select { min-width: 0; max-width: 130px; }
/* Bed pad's Auto option names what it built ("Auto (Sure hold)"), which the
   130px cap cut off -- the one label that must be read whole. */
#fin-opts .fld.row select#bed-pad { max-width: 170px; }

/* Collapsible sections (issue #41). */
#fin-opts .sec { border-top: 1px solid var(--line); padding-top: 8px; }
#fin-opts .sec:first-child { border-top: 0; padding-top: 0; }
#fin-opts .sec > summary {
  display: flex;
  align-items: baseline;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
#fin-opts .sec > summary::-webkit-details-marker { display: none; }
#fin-opts .sec > summary::before {
  content: "";
  flex: none;
  width: 0; height: 0;
  align-self: center;
  border-left: 4px solid var(--dim);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.12s;
}
#fin-opts .sec[open] > summary::before { transform: rotate(90deg); }
#fin-opts .sec-title { color: var(--text); font-weight: 600; white-space: nowrap; }
#fin-opts label.sec-title { font-weight: 600; }
#fin-opts .sec-title .mut { font-weight: 400; color: var(--dim); }
#fin-opts .sec:not([open]) .sec-title .mut { display: none; }   /* room for the recap */
/* The one-line recap of a section's values, only while it's collapsed. */
#fin-opts .sec-sum {
  flex: 1;
  min-width: 0;
  text-align: right;
  color: var(--dim);
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#fin-opts .sec[open] > summary > .sec-sum { visibility: hidden; }
#fin-opts .sec > :not(summary) { margin-top: 8px; }
#fin-opts .sec[open] { display: flex; flex-direction: column; }
/* Printer row (local issue 42): one line with an icon and Edit in place of the
   fold arrow; it unfolds in place and the line keeps showing what's set. */
#fin-opts .printer > summary { align-items: center; gap: 6px; padding: 6px; margin: -2px -4px 0; border-radius: 6px;
  border: 1px solid transparent; background: color-mix(in srgb, #fff 4%, transparent); }
#fin-opts .printer > summary:hover { border-color: var(--btn-line); }
#fin-opts .printer > summary::before { display: none; }
#fin-opts .printer > summary .ico { width: 16px; height: 16px; color: var(--dim); flex: none; }
#fin-opts .printer-txt { flex: 1; min-width: 0; font-size: 11.5px; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#fin-opts .printer-edit { color: var(--dim); font-size: 12px; }
#fin-opts .printer > summary:hover .printer-edit { color: var(--accent); }
#fin-opts .printer:not([open]) .when-open, #fin-opts .printer[open] .when-shut { display: none; }
#fin-opts .printer[open] > :not(summary) { padding-inline-start: 22px; }
/* Settings search (ui/search.js): while a query is in, only matching rows show, and
   only the sections and groups that hold one. A section's own header stays as context. */
#fin-opts .opts-search input { width: 100%; box-sizing: border-box; }
#fin-opts .opts-search p { margin: 6px 0 0; font-size: 12px; }
#fin-opts.searching :is(.fld, label.check, button.btn:not([data-mode])):not(.hit):not(summary *):not(.fld *),
#fin-opts.searching .nohit { display: none !important; }   /* over .more .group's flex */
#fin-opts.searching .hit:not(summary *) { box-shadow: -10px 0 0 -6px var(--accent); }
/* More settings (local issue 42): headed groups that don't fold, a count of the
   changed settings on the line (shown open or shut), and a dot by each changed one. */
#fin-opts .more[open] > summary > .more-count, #fin-opts .more > summary > .more-count { visibility: visible; color: var(--accent); }
#fin-opts .more .group { display: flex; flex-direction: column; gap: 8px; }
#fin-opts .more .group + .group { margin-top: 14px; }
#fin-opts .more .group h5 { margin: 0; font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--dim); }
#fin-opts .more label.changed > span:first-of-type::after { content: ""; display: inline-block; width: 6px; height: 6px;
  margin-inline-start: 6px; vertical-align: middle; border-radius: 50%; background: var(--accent); }
.check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--dim);
  cursor: pointer;
}

/* Draw-mode controls, revealed under the placement select when Draw is live. */
#draw-controls {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
#draw-controls[hidden] { display: none; }
.draw-hint {
  margin: 0;
  max-width: 22ch;
  color: var(--dim);
  font-size: 11px;
  line-height: 1.5;
}
.draw-hint strong { color: var(--text); }
.draw-hint kbd {
  padding: 0 4px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg);
  font: inherit;
  font-size: 10px;
}
.draw-btns { display: flex; gap: 6px; }

/* No single control in the topbar may wrap its own text onto a second line (a
   two-line button makes the bar jump in height as its contents change); the bar
   as a whole wraps between controls on a narrow window instead. */
#topbar .btn,
#topbar .badge,
#topbar .field { white-space: nowrap; }

.field input[type=range] {
  width: 92px;
  margin: 0 6px;
  vertical-align: middle;
  accent-color: var(--accent);
}
.field output { color: var(--text); font-variant-numeric: tabular-nums; }

.custom-vol { display: inline-flex; align-items: center; gap: 4px; }
.custom-vol input {
  width: 4.2em;
  padding: 4px 6px;
  border: 1px solid var(--btn-line);
  border-radius: 6px;
  background: var(--btn);
  color: var(--text);
  font: inherit;
  font-variant-numeric: tabular-nums;
}

/* The view toolbar's popovers (ui/toolbar.js): rows of small controls, a dim hint
   line, the angle readout. */
.vb-pop .vb-row { flex-basis: 100%; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.vb-pop .lbl { color: var(--dim); font-size: 12px; margin-right: 2px; }
.vb-pop .readout {
  flex-basis: 100%;
  margin: 0;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.vb-pop input[type=number] {
  width: 72px;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 12px;
  appearance: textfield;          /* Scale: a step would be a whole reload */
}
.vb-pop input[type=number]::-webkit-inner-spin-button,
.vb-pop input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.vb-pop .readout #rot-delta { color: var(--accent); margin-left: 8px; }
.vb-pop .hint {
  flex-basis: 100%;
  margin: 0;
  color: var(--dim);
  font-size: 12px;
}
.btn.sm { padding: 3px 9px; }

#suggest-note.good { color: #6fcf8b; }   /* the "best support is no support" win */
#suggest-list {
  flex-basis: 100%;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
/* One tight line per pose, so three suggestions cost ~90px, not ~280. */
.suggest-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  text-align: left;
  padding: 5px 8px;
  line-height: 1.3;
}
.suggest-row .sr-rank {
  flex: 0 0 auto;
  min-width: 22px;
  color: var(--accent);
  font-weight: 600;
  font-size: 11px;
}
.suggest-row .sr-line {
  flex: 1 1 auto;
  min-width: 0;                 /* let the row wrap inside the panel instead of spilling past its border */
  font-variant-numeric: tabular-nums;
  font-size: 11px;
}
.suggest-row.active { border-color: var(--accent); background: var(--btn-hi); }
.suggest-row.active .sr-rank::after { content: ' ✓'; }
.suggest-row.bad .sr-rank { color: var(--dim); }
/* the "best support is no support" win: a pose that needs nothing, or that keeps
   every hole/bore support-free. Celebrated, not buried as a gray "0 fins". */
.suggest-row .sr-badge {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .02em;
  color: #6fcf8b;
  border: 1px solid #6fcf8b66;
  border-radius: 3px;
  padding: 0 4px;
  margin-left: 6px;
  white-space: nowrap;
}
.suggest-row.free { border-color: #6fcf8b66; }
.suggest-row.free .sr-rank { color: #6fcf8b; }

/* ---------- automatic layer view ---------- */
.layers-toggle { color: var(--dim); font-size: 12px; }
.nav-pick { color: var(--dim); font-size: 12px; }
.vb-pop .nav-pick select { margin-left: 6px; padding: 2px 6px; font-size: 12px; }
/* View cube (#350, ui/viewcube.js): above the toolbar at its left end, out of the
   way of the popovers: one opening over it lifts it above (viewcube.js fit) */
#viewcube {
  position: absolute; left: 0; bottom: calc(100% + 8px);
  display: flex; align-items: flex-end; gap: 2px;
  pointer-events: none;
  transition: transform .18s ease;
}
@media (prefers-reduced-motion: reduce) { #viewcube { transition: none; } }
#viewcube.vc-off { visibility: hidden; }
#viewcube .vc-stage {
  position: relative; width: 116px; height: 116px;
  pointer-events: auto; cursor: pointer; touch-action: none; user-select: none;
}
#viewcube .vc-stage.dragging { cursor: grabbing; }
#viewcube .vc-cube {
  position: absolute; left: 50%; top: 50%; width: 0; height: 0;
  transform-style: preserve-3d;
}
#viewcube .vc-face {
  position: absolute; left: -32px; top: -32px; width: 64px; height: 64px;
  box-sizing: border-box;
  display: grid; grid-template: 13px 1fr 13px / 13px 1fr 13px;
  border: 1px solid color-mix(in srgb, #fff 22%, transparent);
  background: color-mix(in srgb, var(--btn) 88%, transparent);
  backface-visibility: hidden;
}
#viewcube .vc-cell { display: flex; align-items: center; justify-content: center; min-width: 0; }
#viewcube .vc-cell.hot { background: color-mix(in srgb, var(--accent) 55%, transparent); }
#viewcube .vc-cell span {
  color: var(--text); font-size: 11px; font-weight: 600; letter-spacing: .02em;
  white-space: nowrap; pointer-events: none;
}
#viewcube .vc-side { display: flex; flex-direction: column; align-items: center; gap: 4px; pointer-events: auto; }
#viewcube .vc-home {
  display: inline-flex; padding: 4px; border: 1px solid var(--glass-line); border-radius: 6px;
  background: var(--glass); color: var(--text); cursor: pointer;
}
#viewcube .vc-home:hover { color: var(--accent); }
#viewcube .vc-home .ico { width: 14px; height: 14px; }
#viewcube .vc-axes { width: 32px; height: 32px; overflow: visible; }
#viewcube .vc-axes text { font-size: 7px; font-weight: 700; }
/* Camera views (#350): opposite sides paired in columns -- Front/Back, Left/Right, Top/Bottom */
.vb-pop #cam-views { display: grid; grid-template-rows: auto auto; grid-auto-flow: column; gap: 4px; }

/* ---------- strength arrow (load direction) ---------- */
.vb-pop.loadfx .loadfx-lede { flex-basis: 100%; margin: 0 0 3px;
  font-size: 13px; color: var(--text); }
.vb-pop.loadfx .loadfx-lede strong { color: var(--accent); }
.vb-pop.loadfx .strength-pad { flex-basis: 100%; display: grid;
  grid-template-columns: repeat(2, 1fr); gap: 4px; margin: 2px 0; }
.vb-pop.loadfx .strength-pad .btn { padding: 5px 6px; font-size: 13px; }
.btn.sm.active { border-color: var(--accent); color: var(--accent); background: var(--btn-hi); }
.load-verdict {
  flex-basis: 100%;
  margin: 0;
  padding: 6px 9px;
  border-left: 3px solid var(--dim);
  border-radius: 3px;
  background: var(--btn);
  font-size: 12px;
  line-height: 1.35;
}
.load-verdict.good  { border-left-color: #6fcf8b; color: #cdeccf; }
.load-verdict.mixed { border-left-color: #ffb454; color: #f0dcc0; }
.load-verdict.poor  { border-left-color: #ff6b6b; color: #f2cfcf; }
.vb-pop.loadfx #load-suggest { flex-basis: 100%; margin-top: 2px; }

/* ---------- bottom-right status card ---------- */
#status {
  width: 264px;
  max-width: calc(100vw - 28px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 6px;
  pointer-events: none;          /* clicks pass through the empty gaps to the canvas */
  text-align: left;
}
#status[hidden] { display: none; }
#status > * { pointer-events: auto; }
/* Overhang warning: the tool isn't holding these up, so it's an amber caution, not
   a green all-clear. Card look sets it apart from the plainer .note lines above. */
.over-warn {
  margin: 0;
  padding: 6px 9px;
  border-left: 3px solid #ffb454;
  border-radius: 3px;
  background: var(--btn);
  color: #f0dcc0;
  font-size: 12px;
  line-height: 1.35;
}
.over-warn:empty { display: none; }
/* Island starts (ui/islands.js): unsupported like amber, but a start in midair
   fails outright, so it takes the red of the overhangs and their cones. */
.island-warn { border-left-color: #ff5a4d; color: #f3cdc8; }

/* ---------- view toolbar (ui/toolbar.js) ---------- */
/* Bottom-left of the 3D view: badges fill in from the left; the open badge's
   settings in a card above it, its left edge on the badge's, extending right
   (toolbar.js sets both lefts). Frosted glass so the scene shows through. Pointer
   events only on the pieces, so the space around them still orbits the scene. */
#viewbar {
  --glass: color-mix(in srgb, var(--panel) 52%, transparent);
  --glass-line: color-mix(in srgb, #fff 9%, transparent);
  position: fixed;
  left: 14px; bottom: 14px;
  z-index: 3;
  width: max-content;
  max-width: calc(100vw - 28px);
  pointer-events: none;
}
#viewbar > * { pointer-events: auto; }
.vb-badges, .vb-pop {
  display: flex;
  gap: 3px;
  padding: 3px;
  border: 1px solid var(--glass-line);
  border-radius: 9px;
  background: var(--glass);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 7%, transparent), 0 4px 18px rgb(0 0 0 / 0.28);
}
.vb-badges { align-items: center; overflow-x: auto; scrollbar-width: none; }
.vb-pop {
  position: absolute;
  bottom: calc(100% + 5px);
  left: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  width: max-content;
  max-width: min(20rem, calc(100vw - 28px));
  /* a short window (a phone on its side): scroll inside rather than run under the top bar */
  max-height: calc(100vh - var(--topbar-h) - 60px);
  overflow-y: auto;
}
.vb-pop.wide { max-width: min(22rem, calc(100vw - 28px)); }
/* The ring-drag angle (pose.js): a popover-shaped label that never takes a click,
   shown with .show and faded out after the drag. */
.vb-pop.vb-tip {
  padding: 4px 9px;
  gap: 8px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .4s ease, visibility 0s linear .4s;
}
.vb-pop.vb-tip.show { opacity: 1; visibility: visible; transition: none; }
/* The sway brace preview label (ui/walls.js, eased by ui/braceghost.js): beside the
   cursor, glass like the view bar's popovers, amber when the spot can't take a brace.
   It fades rather than pops, and a min-width keeps it from resizing as the text changes. */
#brace-tip {
  position: fixed;
  z-index: 4;
  min-width: 13rem;
  max-width: 18rem;
  padding: 4px 9px;
  border: 1px solid color-mix(in srgb, #fff 9%, transparent);
  border-radius: 9px;
  background: color-mix(in srgb, var(--panel) 72%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  backdrop-filter: blur(14px) saturate(160%);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .12s ease, color .12s ease, visibility 0s linear .12s;
}
#brace-tip.show { opacity: 1; visibility: visible; transition: opacity .12s ease, color .12s ease; }
#brace-tip.bad { color: #ffb454; }
/* any popover open (Suggest left open during a drag, Rotate opened mid-fade) wins */
#viewbar:has(.vb-pop:not([hidden]):not(.vb-tip)) #drag-angle { display: none; }
#drag-delta { color: var(--accent); font-weight: 600; }
#drag-delta:empty { display: none; }
#drag-now { color: var(--dim); }
.vb-sep { width: 1px; align-self: stretch; margin: 3px; background: var(--glass-line); }
.vb-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.vb-badge .ico { width: 14px; height: 14px; }
.vb-badge.icon { padding: 3px 6px; }
/* A phone: icons only (their titles still say what they are), so the whole bar fits. */
@media (max-width: 600px) {
  .vb-badge > span { display: none; }
  .vb-badge { padding: 4px 7px; }
}
.vb-badge:disabled { opacity: .35; cursor: default; }
.vb-badge:disabled:hover { background: none; color: var(--text); }
.vb-badge:hover { background: color-mix(in srgb, #fff 8%, transparent); color: var(--accent); }
.vb-badge.on { border-color: color-mix(in srgb, var(--accent) 70%, transparent); color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.vb-badge[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 85%, transparent); color: #06121f; }
.vb-pop .btn {
  padding: 2px 8px;
  font-size: 12px;
  border-color: var(--glass-line);
  background: color-mix(in srgb, var(--btn) 55%, transparent);
  box-shadow: none;
}
.vb-pop .btn:hover { border-color: var(--accent); }
.vb-pop .btn.active { border-color: var(--accent); color: var(--accent); }
.vb-pop .btn.primary { background: color-mix(in srgb, var(--accent) 85%, transparent); border-color: transparent; }
.vb-pop .seg { display: inline-flex; }
.vb-pop .seg .btn:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.vb-pop .seg .btn:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }

#fps {
  position: fixed;
  right: 14px; bottom: 12px;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* ---------- drop target ---------- */

#drop {
  position: fixed;
  inset: 0;
  /* centred in the space under the top bar, which grows on a narrow window */
  padding: var(--topbar-h) 16px 16px;
  display: grid;
  place-items: center;
  text-align: center;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  pointer-events: none;
}
#drop.hidden { display: none; }
#drop.armed { background: color-mix(in srgb, var(--accent) 12%, var(--bg) 70%); }

#drop .inner {
  padding: 30px 38px;
  border: 2px dashed var(--line);
  border-radius: 14px;
  max-width: 34rem;
  text-align: left;
  /* a short phone under a wrapped bar can't fit it all: the card scrolls (drops
     are caught on the window, io.js, so this doesn't block them) */
  max-height: 100%;
  overflow-y: auto;
  pointer-events: auto;
  box-sizing: border-box;
}
@media (max-width: 600px) {
  #drop .inner { padding: 18px 18px; }
}
#drop.armed .inner { border-color: var(--accent); }
#drop .big {
  margin: 0 0 10px;
  font-size: 22px;
  font-weight: 650;
  letter-spacing: -0.01em;
}
#drop .lede { margin: 0 0 16px; color: var(--text); }
#drop .steps {
  margin: 0 0 16px;
  padding-left: 1.25em;
  color: var(--dim);
}
#drop .steps li { margin-bottom: 4px; }
#drop .steps strong { color: var(--text); font-weight: 600; }
#drop .small { margin: 0; color: var(--dim); font-size: 12px; }
/* After the first part: no intro and no scrim, the bare plate with an Import button
   on it; a drag tints it as before. */
#drop .empty { display: none; }
#drop.compact { background: none; }
#drop.compact.armed { background: color-mix(in srgb, var(--accent) 12%, transparent); }
#drop.compact .inner { display: none; }
#drop.compact .empty {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 22px 30px;
  border: 2px dashed var(--line);
  border-radius: 14px;
  background: color-mix(in srgb, var(--panel) 70%, transparent);
  pointer-events: auto;
}
#drop.compact.armed .empty { border-color: var(--accent); }

/* ---------- Ko-fi support link ---------- */
/* Warm accent so the support link reads apart from the blue tool controls,
   without shouting — the tool is the gift, this is a soft "thanks" ask. */
/* Error badge: a red "!" at the end of the top bar, shown only after an error
   (the inline script in index.html). Its panel drops like the other menus. */
.btn.err-badge { border-color: #ff4d4d; color: #fff; background: #c62828; font-weight: 700; min-width: 32px; }
.btn.err-badge:hover { background: #ff4d4d; border-color: #ff4d4d; color: #fff; }
.err-panel { width: min(360px, calc(100vw - 32px)); padding: 10px 12px; gap: 8px; }
.err-panel .err-head { margin: 0; font-weight: 600; }
.err-panel pre { margin: 0; max-height: 40vh; overflow: auto; white-space: pre-wrap; word-break: break-word;
                 font-size: 11px; color: var(--dim); }
.err-panel .err-foot { margin: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px;
                       font-size: 12px; color: var(--dim); }
.err-panel .err-copy { flex: none; border: 1px solid var(--btn-line); background: var(--btn-hi); }
.noscript { position: fixed; inset: 40% 16px auto; z-index: 10; text-align: center; color: var(--text); }
/* "The 3D view can't draw" (ui/glfail.js): under the plate tabs (and the phone chip rows), above any panel. */
.glfail { position: fixed; inset: calc(var(--topbar-h) + var(--chips-h, 0px) + 48px) 16px auto; z-index: 30;
  max-width: 34em; margin: 0 auto; padding: 12px 16px; text-align: center;
  color: var(--text); background: var(--panel); border: 1px solid var(--accent);
  border-radius: 8px; box-shadow: 0 6px 24px rgb(0 0 0 / 0.5); }
.btn.gh { display: inline-flex; align-items: center; text-decoration: none; }
.btn.gh svg { flex: none; }
.btn.kofi { border-color: #ff8a5b; color: #ffb492; text-decoration: none; }
.btn.kofi:hover { border-color: #ff8a5b; color: #06121f; background: #ff8a5b; }
/* What's new: the card's changelog, written into index.html by web/changelog.js. The list
   scrolls past about five lines so the card keeps its size as releases pile up. */
#drop .whatsnew { margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--line); font-size: 12px; color: var(--dim); }
#drop .whatsnew h2 { margin: 0 0 6px; font-size: 13px; font-weight: 600; color: var(--text); }
#drop .whatsnew .ver { color: var(--dim); font-weight: 400; }
#drop .whatsnew .list {
  max-height: 7.5em; overflow-y: auto; overscroll-behavior: contain; margin: 0 0 6px;
  /* macOS hides the scrollbar: the last line fades so the list reads as more below */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5em), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 1.5em), transparent);
}
#drop .whatsnew .list.end { -webkit-mask-image: none; mask-image: none; }
#drop .whatsnew ul { margin: 0 0 4px; padding-left: 1.1em; }
#drop .whatsnew li { margin-bottom: 2px; }
#drop .whatsnew .rel { margin: 6px 0 2px; color: var(--text); font-weight: 600; }
#drop .whatsnew .rel span { color: var(--dim); font-weight: 400; }
#drop .whatsnew a { color: var(--accent); }
#drop .kofi-line { margin: 14px 0 0; color: var(--dim); font-size: 12px; }
#drop .kofi-line .kofi {
  pointer-events: auto;   /* the #drop scrim is click-through; the link must not be */
  color: #ff9e73;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, #ff8a5b 55%, transparent);
}
#drop .kofi-line .kofi:hover { color: #ffb492; border-bottom-color: #ff8a5b; }

/* ---------- 3MF object picker ---------- */
/* A plate with several objects: choose which body to fin (or check several to
   merge). Sits above everything, including the drop scrim. */
#picker {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(0, 0, 0, 0.55);
}
.picker-panel {
  width: min(440px, 100%);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  padding: 18px 20px;
  border: 1px solid var(--btn-line);
  border-radius: 8px;
  background: var(--panel);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
.picker-panel h2 { margin: 0 0 4px; font-size: 15px; color: var(--text); }
.picker-sub { margin: 0 0 12px; font-size: 12px; color: var(--dim); line-height: 1.4; }
#picker-list {
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 6px;
}
#picker-list li { border-bottom: 1px solid var(--line); }
#picker-list li:last-child { border-bottom: 0; }
#picker-list label {
  display: flex;
  align-items: baseline;
  gap: 9px;
  padding: 9px 11px;
  cursor: pointer;
}
#picker-list label:hover { background: var(--btn); }
#picker-list input { margin: 0; accent-color: var(--accent); align-self: center; }
#picker-list .pk-name { flex: 1; color: var(--text); font-size: 13px; word-break: break-word; }
#picker-list .pk-meta { color: var(--dim); font-size: 11px; white-space: nowrap; }
.picker-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.picker-foot .note { margin: 0; font-size: 11px; color: var(--dim); }
.picker-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* Plate tabs (ui/plates.js): centred under the top bar, between the rails. */
#plates {
  position: fixed;
  z-index: 3;
  top: var(--topbar-h);
  left: 50%;
  transform: translateX(-50%);
  max-width: calc(100vw - 600px);
  min-width: 0;
  display: flex;
  gap: 4px;
  overflow-x: auto;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  backdrop-filter: blur(6px);
}
@media (max-width: 900px) { #plates { max-width: calc(100vw - 28px); top: auto; bottom: 64px; } }
#plates .plate { display: flex; flex: none; align-items: center; border-radius: 6px; }
#plates button {
  border: 0; background: none; color: var(--dim); font: inherit; font-size: 12px; cursor: pointer;
}
#plates .plate-tab {
  display: flex; flex-direction: column; align-items: flex-start;
  padding: 3px 4px 3px 8px; line-height: 1.25; border-radius: 6px 0 0 6px;
}
#plates .plate-tab[aria-pressed="true"] { color: var(--text); }
#plates .plate:has([aria-pressed="true"]) { background: var(--btn); }
#plates .plate-name { max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--dim); }
#plates .plate-x { padding: 2px 7px; font-size: 14px; border-radius: 0 6px 6px 0; }
#plates .plate-x:hover { color: #ff5a4d; }
#plates .plate-add { padding: 2px 10px; font-size: 16px; border-radius: 6px; }
#plates .plate-add:disabled { opacity: 0.4; cursor: default; }
#plates button:hover { color: var(--text); }
#plates .plate-all { padding: 2px 10px; border-left: 1px solid var(--line); border-radius: 0; }
#plates .plate-all[hidden] { display: none; }

/* Undo for a deleted plate / removed model (ui/plates.js): bottom centre, over the view. */
#toast {
  position: fixed;
  z-index: 5;
  left: 50%; bottom: 70px;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px;
  padding: 8px 10px 8px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  box-shadow: 0 6px 24px rgb(0 0 0 / .35);
}
#toast[hidden] { display: none; }

/* Models that collide (ui/arrange.js): under the plate tabs, red like the cells. */
#clash-banner {
  position: fixed;
  z-index: 3;                /* under the top bar's menus */
  left: 50%; top: calc(var(--topbar-h) + 44px);
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px;
  padding: 6px 8px 6px 14px;
  border: 1px solid #ff6b6b;
  border-radius: 8px;
  background: var(--panel);
  color: #ff8a8a;
  font-size: 13px;
  box-shadow: 0 6px 24px rgb(0 0 0 / .35);
}
#clash-banner[hidden] { display: none; }
@media (max-width: 900px) { #toast { bottom: 120px; } }

/* Tip cards (ui/tips.js): a setting's own title text in a card, under a small
   diagram where it has one. The diagrams use the scene's colours: part, supports,
   overhang. A card without one is only as wide as its text. */
.tip-card {
  position: fixed; z-index: 60; width: 260px; max-width: calc(100vw - 32px);
  box-sizing: border-box; padding: 10px 12px; pointer-events: none;
  background: var(--panel); border: 1px solid var(--btn-line); border-radius: 8px;
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.45); color: var(--text);
}
.tip-card.plain { width: max-content; max-width: min(260px, calc(100vw - 32px)); }
.tip-card svg { display: block; width: 100%; height: auto; margin-bottom: 8px;
  background: var(--bg); border-radius: 4px; }
.tip-card p { margin: 0; font-size: 12px; line-height: 1.45; }
.tp-bed { stroke: #5b6472; stroke-width: 2; }
.tp-part { fill: #8fa3c8; }
.tp-sup { fill: #3fae6a; }
.tp-over { stroke: #e05a4f; stroke-width: 3; }
.tp-tine { stroke: #7be0a0; stroke-width: 2; }
.tp-dim, .tp-dim-head { stroke: var(--accent); stroke-width: 1.5; fill: none; }
.tp-mark { stroke: var(--dim); stroke-width: 1; stroke-dasharray: 4 3; }
.tp-tool { fill: #5b6472; }
.tp-layer { stroke: var(--bg); stroke-width: 1; }
.tp-bead { fill: #3fae6a; stroke: var(--bg); stroke-width: 1; }
.tp-sup2 { fill: #2b8a52; }
.tp-iface { fill: #a070e0; }
.tp-hole { fill: var(--bg); }
.tp-hand { fill: color-mix(in srgb, var(--accent) 35%, transparent); stroke: var(--accent); stroke-width: 1.5; }
.tp-ghost { fill: none; stroke: var(--dim); stroke-width: 1.5; stroke-dasharray: 4 3; }
.tp-picked { fill: #3fae6a; stroke: var(--accent); stroke-width: 2.5; }
.tp-x { stroke: #e05a4f; stroke-width: 3; fill: none; }
.tp-cursor { fill: #fff; stroke: #111; stroke-width: 1; }
.tp-amber { stroke: #f0a830; stroke-width: 4; }
.tp-cone { fill: #e05a4f; }
.tp-plan { fill: #8fa3c8; opacity: 0.45; }
.tp-band { stroke: #3fae6a; stroke-width: 5; }

/* ---------- phone: a part on the plate ---------- */

/* Phone (ui/phone.js): the stats card and the fin settings fold behind two chips
   under the top bar, one open at a time; the plate tabs sit right of the chips
   instead of over the toolbar; the status/receipt card sits just above the view
   toolbar; the fps readout goes. The rails start under the chip row. */
#phone-chips { display: none; }
@media (max-width: 600px) {
  :root { --chips-h: 44px; }
  #phone-chips:not([hidden]) {
    position: fixed; z-index: 3;
    top: var(--topbar-h); left: 14px;
    display: flex; gap: 6px;
  }
  #phone-chips .chip { min-height: 36px; padding: 0 12px; }
  #phone-chips .chip[hidden] { display: none; }
  #phone-chips .chip[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }

  body:not(.show-stats) #stats { display: none; }
  body:not(.show-opts) #fin-opts { display: none; }
  #left-rail { top: calc(var(--topbar-h) + var(--chips-h)); right: 14px; }
  #stats { width: auto; max-width: none; align-self: stretch; }
  #right-rail { top: calc(var(--topbar-h) + var(--chips-h)); left: 14px; bottom: 64px; }
  #fin-opts { width: auto; max-width: none; align-self: stretch; }
  #status { width: auto; max-width: none; align-self: stretch; }
  #plates {
    top: var(--topbar-h); bottom: auto; left: auto; right: 14px;
    transform: none; max-width: calc(100vw - 230px);
  }
  body.plates-below { --chips-h: 88px; }
  body.plates-below #plates { top: calc(var(--topbar-h) + 44px); left: 14px; right: auto; max-width: calc(100vw - 28px); }
  #fps { display: none; }
  /* the view toolbar's popovers: buttons a fingertip can hit */
  .vb-pop .btn.sm { min-height: 36px; padding: 0 12px; }
}

