/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font-family: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;
  font-size: 14px;
  line-height: 1.45;
  /* Millimetres and money line up down every column in this interface, so the
     figures are tabular everywhere rather than being switched on per rule. */
  font-variant-numeric: tabular-nums;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button { font: inherit; color: inherit; }

/* ---------- theme ---------- */
/* The piece stands in a room, and the panels are paper laid on its wall. That
   is the whole colour idea, and it decides the one unusual thing here: there is
   no brand hue. Every colour a customer sees on screen belongs either to a
   decor they picked or to whether something is valid. A blue accent would
   compete with the oak and the yellow front, and imply a colour that is not for
   sale — so a selected thing is filled with ink and carries more weight
   instead.

   `--accent` is kept as a name because forty rules already reach for it; it
   simply resolves to ink now. */
:root {
  color-scheme: light dark;

  --room: #ada69c;
  --paper: #f7f5f2;
  --paper-2: #ebe8e3;
  --rule: #d6d1ca;
  --ink: #22201d;
  --ink-dim: #736e67;

  --fault: #a6342a;
  --sound: #2f6b4f;

  /* Legacy names, remapped. */
  --bg: var(--room);
  --panel: var(--paper);
  --panel-2: var(--paper-2);
  --border: var(--rule);
  --line: var(--rule);
  --fg: var(--ink);
  --fg-dim: var(--ink-dim);
  --accent: var(--ink);
  --accent-2: var(--ink-dim);
  --accent-fg: var(--paper);
  --ghost-ok: var(--sound);
  --ghost-bad: var(--fault);

  /* What a selection is marked in, so the band you are editing and the board
     outlined in the model are plainly the same thing: the panels' selection
     blue, which the outline pass also draws in (render/mod.rs). */
  --pick: var(--cx-select);

  /* No drop shadows. Depth is paper against the room, not a glow under a card
     — a uniform shadow beneath every panel is the tell that turns a
     configurator into a template. */
  --shadow: none;
  --radius: 8px;
  --sidebar-w: 288px;
  --inspector-w: 320px;

  /* The context panels' roles (configurator redesign, HANDOFF.md "Design
     language"). Named for what they are for rather than what they look like,
     so the light set below and the dark set — the handoff's own values — are
     the same rules. The one hue is the selection: it is not a brand colour but
     a state, the thing you are editing, and it is the same blue in the panel
     and in the model. */
  --cx-bg: var(--paper);
  --cx-picker-bg: #fbfaf8;
  --cx-picker-border: var(--rule);
  --cx-picker-radius: 12px;
  --cx-field: #efece7;
  --cx-field-2: #f2efea;
  --cx-raised: #fdfcfa;
  --cx-raised-border: var(--rule);
  --cx-divider: #e3dfd9;
  --cx-control-border: #cfc9c1;
  --cx-control-border-2: #bdb6ad;
  --cx-text: var(--ink);
  --cx-text-2: #3f3b36;
  --cx-muted: var(--ink-dim);
  --cx-faint: #8f8981;
  /* A chosen segment or a pressed toggle: filled with ink, as everything
     selected in this interface already is. */
  --cx-active-bg: var(--ink);
  --cx-active-fg: var(--paper);
  --cx-select: #9edaff;
}

/* Dark is the option, not the default: this is furniture for a home, looked at
   in daylight, and the decors have to read true. */
@media (prefers-color-scheme: dark) {
  :root {
    --room: #2a2723;
    --paper: #1b1917;
    --paper-2: #232120;
    --rule: #3a3633;
    --ink: #f0ece6;
    --ink-dim: #9c958c;
    --fault: #e0776b;
    --sound: #7fc4a0;
    --accent-fg: #1b1917;

    --cx-bg: #1c1c1c;
    --cx-picker-bg: #1f1f1e;
    --cx-picker-border: #333330;
    --cx-field: #141414;
    --cx-field-2: #1a1a19;
    --cx-raised: #222220;
    --cx-raised-border: #333330;
    --cx-divider: #2a2a28;
    --cx-control-border: #3a3a37;
    --cx-control-border-2: #45453f;
    --cx-text: #ededea;
    --cx-text-2: #cfcfca;
    --cx-muted: #a3a3a0;
    --cx-faint: #8a8a87;
    --cx-active-bg: #efece6;
    --cx-active-fg: #1a1a1a;
    --cx-select: #9edaff;
  }
}

/* ---------- app shell ---------- */
/* Two columns: the sidebar and the view. The right panel floats over the
   view's right edge rather than taking a column, so selecting something never
   resizes the view and nothing in the scene jumps. */
.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  height: 100dvh;
  width: 100vw;
  overflow: hidden;
}
@media (min-width: 861px) {
  .panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: var(--inspector-w);
    z-index: 35;
    box-shadow: -8px 0 24px rgba(0, 0, 0, 0.18);
  }
  /* What sits at the view's right edge, and the bar centred in it, move to
     the space beside the panel. */
  .app--inspector-open .action-bar { left: calc((100% - var(--inspector-w)) / 2); }
}

.sidebar {
  background: var(--panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.sidebar__header {
  padding: 8px 10px 8px 8px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 4px;
}
.sidebar__history {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel-2);
  color: var(--fg-dim);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  transition: color 100ms, border-color 100ms;
}
.sidebar__history:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}
.sidebar__history:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}
.sidebar__title {
  margin: 0;
  flex: 1;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}
.sidebar__body {
  padding: 12px;
  overflow: auto;
  flex: 1 1 auto;
}

/* ---------- palette ---------- */
.sidebar__section-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--fg);
  padding-bottom: 4px;
}
* + .sidebar__section-label { margin-top: 14px; }
/* One notch smaller and un-bolded: nested under .sidebar__section-label,
   grouping the palette into Elements vs Structure. */
.sidebar__subsection-label {
  font-size: 11px;
  color: var(--fg-dim);
  padding: 4px 0 6px;
}
.palette {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 10px;
}
.palette + .sidebar__subsection-label {
  margin-top: 8px;
}

.palette-chip {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 8px 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  color: var(--fg);
  cursor: grab;
  user-select: none;
  touch-action: none;
}
.palette-chip:hover { border-color: var(--accent); }
.palette-chip:active,
.palette-chip[data-dragging="true"] {
  cursor: grabbing;
  border-color: var(--accent-2);
}
.palette-chip__icon { flex: none; }
.palette-chip__label {
  font-size: 12.5px;
  font-weight: 500;
}

/* ---------- canvas viewport ---------- */
.viewport {
  position: relative;
  /* Its own layer: what floats over the model — the nav cube, the overlays,
     the toast — stays inside it, so pop-outs over the viewport cover them
     rather than the cube showing through a picker. */
  isolation: isolate;
  /* Flat. A vertical gradient behind a product is stage lighting that is not
     there, and it tints the decors it sits behind. */
  background: var(--room);
  min-width: 0;
  min-height: 0;
  height: 100%;
  overflow: hidden;
}
.viewport__canvas {
  display: block;
  width: 100%;
  height: 100%;
  outline: none;
  touch-action: none;
}

.viewport__overlay {
  position: absolute;
  inset: 0;
  /* Over the room plan (z-index 20), so its messages show there too. Nothing
     in it takes the pointer but its own buttons. */
  z-index: 30;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 12px;
  /* toolbar is absolutely positioned within overlay */
}

/* ---------- under a front's material or handle row ---------- */
.pickgroup { display: flex; flex-direction: column; gap: 6px; }
.usage {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 28px;
}
.usage__line { font-size: 12px; color: var(--cx-faint); }
.usage__all {
  flex-shrink: 0;
  height: 28px;
  padding: 0 10px;
  border: 1px solid var(--cx-control-border);
  border-radius: 8px;
  background: transparent;
  color: var(--cx-text-2);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.usage__all:hover { border-color: var(--cx-control-border-2); color: var(--cx-text); }

/* ---------- Apply to ---------- */
.pop__scope {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex-shrink: 0;
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--cx-divider);
}
.pop__scope-label { font-size: 13px; color: var(--cx-muted); }
.pop__scope-caption { font-size: 12px; color: var(--cx-faint); }
.pop__scope-caption.is-live { color: var(--cx-text-2); }

/* ---------- a door's cover, on the model ---------- */
/* Drawn over the canvas in percent of it, from the door's face projected
   through the camera, so it tilts with the model. */
.cover-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.cover-svg polygon { fill: none; stroke: var(--cx-select); stroke-width: 2; stroke-dasharray: 6 4; }
.cover-svg__add { pointer-events: none; }
.cover-svg__add.is-hover { fill: color-mix(in srgb, var(--cx-select) 18%, transparent); }
.cover-svg__ghost { stroke-width: 2.5; }
.cover-add,
.cover-grip {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  cursor: pointer;
}
.cover-add {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--cx-select);
  border-radius: 50%;
  background: #1c1c1c;
  color: #ededea;
}
.cover-add svg { width: 12px; height: 12px; }
.cover-add:hover,
.cover-add:focus-visible { background: #2c2c2a; }
.cover-grip {
  width: 10px;
  height: 30px;
  padding: 0;
  border: 2px solid var(--cx-select);
  border-radius: 999px;
  background: #ffffff;
  cursor: ew-resize;
  touch-action: none;
}
.cover-grip--across { width: 30px; height: 10px; cursor: ns-resize; }
.cover-grip:focus-visible { outline: 2px solid var(--cx-select); outline-offset: 2px; }
.cover-grip.is-held { background: var(--cx-select); }
.cover-hint {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 12px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 999px;
  background: var(--cx-bg);
  color: var(--cx-muted);
  font-size: 12px;
  white-space: nowrap;
}
/* Bottom-left, under the toast when there is one. */
.cover-size {
  align-self: flex-start;
  margin-top: 8px;
  padding: 4px 10px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 999px;
  background: var(--cx-bg);
  color: var(--cx-muted);
  font-size: 12px;
}
.cover-size.is-live { border-color: var(--cx-select); color: var(--cx-text); }

/* ---------- projects ---------- */
/* The project's name in the sidebar header, which opens the list. */
.switcher {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  height: 48px;
  padding: 0 8px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--cx-muted);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.switcher:hover,
.switcher.is-open { background: var(--cx-divider); }
.switcher__text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.switcher__name {
  font-size: 15px;
  font-weight: 500;
  color: var(--cx-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.switcher__status { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--cx-faint); }

/* Hung under the header, reaching over the edge of the model. */
.projects {
  position: fixed;
  left: 10px;
  z-index: 47;
  width: 340px;
  max-width: calc(100vw - 20px);
  max-height: 60vh;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-picker-bg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.projects__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-shrink: 0;
  padding: 10px 10px 6px 16px;
}
.projects__title { margin: 0; font-size: 14px; font-weight: 500; color: var(--cx-text-2); }
.projects__new {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px 0 10px;
  border: 1px solid var(--cx-control-border);
  border-radius: 8px;
  background: transparent;
  color: var(--cx-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.projects__new svg { width: 14px; height: 14px; }
.projects__new:hover { border-color: var(--cx-control-border-2); }
.projects__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 4px 6px 6px;
  list-style: none;
}
.projects__foot {
  flex-shrink: 0;
  margin: 0;
  padding: 10px 16px 12px;
  border-top: 1px solid var(--cx-divider);
  font-size: 12px;
  color: var(--cx-faint);
}
.project {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px 4px 2px 2px;
  border-radius: 8px;
}
.project:hover { background: color-mix(in srgb, var(--cx-divider) 60%, transparent); }
.project.is-current { background: var(--cx-divider); }
.project__open,
.project__editing {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
  padding: 6px;
}
.project__open {
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.project__thumb {
  display: flex;
  flex-shrink: 0;
  width: 52px;
  height: 40px;
  box-sizing: border-box;
  padding: 4px;
  border-radius: 6px;
  background: var(--cx-field);
  overflow: hidden;
}
.project__art { width: 100%; height: 100%; display: block; }
.project__text { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.project__name,
.project__meta { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.project__name { font-size: 14px; color: var(--cx-text-2); }
.project.is-current .project__name { font-weight: 500; color: var(--cx-text); }
.project__meta { font-size: 12px; color: var(--cx-faint); }
/* On the open row and the hovered one; always reachable with Tab. */
.project__act {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--cx-muted);
  cursor: pointer;
  opacity: 0;
}
.project:hover .project__act,
.project.is-current .project__act,
.project__act:focus-visible { opacity: 1; }
.project__act:hover { color: var(--cx-text); }
.project__field {
  width: 100%;
  height: 30px;
  box-sizing: border-box;
  padding: 0 8px;
  border: 1px solid var(--cx-select);
  border-radius: 6px;
  background: var(--cx-field-2);
  color: var(--cx-text);
  font: inherit;
  font-size: 14px;
}
.project__field:focus { outline: none; }

/* ---------- toast ---------- */
/* Bottom-left, in the overlay's column — over the action bar where the two
   meet, rather than lifted clear of it. */
.toast-slot { align-self: flex-start; position: relative; z-index: 7; }
.toast-slot:empty { display: none; }
.toast {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 6px 6px 16px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  color: var(--cx-text);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  pointer-events: auto;
}
.toast__message { font-size: 14px; margin-right: 8px; }
.toast__undo {
  height: 32px;
  padding: 0 12px;
  border: 0;
  border-radius: 8px;
  background: var(--cx-raised);
  color: var(--cx-text);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.toast__undo:hover { background: var(--cx-control-border); }
.toast__dismiss {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--cx-muted);
  cursor: pointer;
}
.toast__dismiss:hover { color: var(--cx-text); }

/* ---------- view buttons ---------- */
/* Top-left, as far in as the nav cube is from its corner, in the cube's own
   colours: off, the face shade; a switch that is on, inverted, so it reads
   as held down. */
.view-buttons {
  position: absolute;
  top: 16px;
  left: 16px;
  display: flex;
  gap: 8px;
  pointer-events: all;
  z-index: 50;
}
/* The switch and the camera tools: each a dark tray of buttons. */
.view-switch,
.view-tools {
  display: flex;
  padding: 4px;
  gap: 2px;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  background: rgba(34,32,29,0.92);
}
.view-button {
  width: 38px;
  height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: #fff;
  cursor: pointer;
}
.view-button:hover { background: rgba(255,255,255,0.08); }
.view-button:focus-visible { outline: 2px solid #9edaff; outline-offset: 2px; }
.view-button--on,
.view-button--on:hover { background: #fff; color: rgba(34,32,29,1); }

/* ---------- viewport menu (mobile only) ---------- */
.viewport__menu {
  position: absolute;
  top: 12px;
  left: 12px;
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--fg);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  pointer-events: all;
  box-shadow: var(--shadow);
}

/* Dismisses the mobile sidebar drawer. */
.scrim {
  position: fixed;
  inset: 0;
  z-index: 35;
  background: rgba(0, 0, 0, 0.45);
}
/* The real file picker, opened by the "Open plan" button beside it. Hidden
   rather than removed: it is what actually holds the OS dialog, and a
   `display: none` input still opens on `.click()`. */
.sidebar__file {
  display: none;
}

/* ---------- drag chip (follows the cursor) ---------- */
.drag-ghost {
  position: fixed;
  pointer-events: none;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--fg);
  box-shadow: var(--shadow);
  transform: translate(-50%, -50%);
  z-index: 9999;
  opacity: 0.95;
  white-space: nowrap;
  max-width: 60vw;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Matches the red cell ghost in the viewport: the drop is allowed, but it
   produces a design that cannot be exported. */
/* The clear inside of what the pointer rests on, down and right of it so the
   pointer does not cover it. */
.inside-chip {
  position: fixed;
  pointer-events: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 12px;
  color: var(--fg);
  box-shadow: var(--shadow);
  z-index: 9998;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.drag-ghost--bad {
  border-color: var(--ghost-bad);
  color: var(--ghost-bad);
}

/* ---------- mobile ---------- */
/* Below this width neither panel can afford a column: the sidebar becomes a
   left slide-over and the inspector a bottom sheet, both over a full-bleed
   canvas. Camera control is touch-driven (see ui/canvas.rs `Gesture`). */
@media (max-width: 860px) {
  .app {
    grid-template-columns: minmax(0, 1fr);
  }

  .viewport__menu { display: flex; }
  /* Beside the menu button, which takes the corner on a phone. */
  .view-buttons { top: 16px; left: 68px; }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(320px, 85vw);
    z-index: 40;
    border-right: 1px solid var(--border);
    box-shadow: var(--shadow);
    transform: translateX(-100%);
    transition: transform 180ms ease;
  }
  .app--sidebar-open .sidebar { transform: none; }

  .panel {
    position: fixed;
    inset: auto 0 0 0;
    height: 45dvh;
    max-height: 85dvh;
    z-index: 45;
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -4px 24px rgba(0,0,0,0.35);
    animation: sheet-in 180ms ease;
    transition: height 160ms ease;
  }
  .panel--expanded { height: 85dvh; }
  /* While the grip is held the height is written inline every pointermove;
     transitioning it would make the sheet lag behind the finger. */
  .panel--dragging { transition: none; }
  .panel__grip { display: block; }

  /* The pop-out has no sidebar to sit beside, so it becomes a sheet of its
     own, over the panel that opened it. */
  .pop {
    position: fixed;
    inset: auto 8px 8px;
    top: auto !important;
    width: auto;
    max-height: 70dvh;
    z-index: 50;
  }

  /* Keep the cube clear of the sheet and give it a thumb-sized hit area. */

  /* Comfortable touch targets, and 16px inputs so iOS does not zoom on focus. */
  input,
  select { font-size: 16px; }
  .ghostbtn,
  .addbtn,
  .export-btn { min-height: 44px; }
  .band { min-height: 48px; }
}

@keyframes sheet-in {
  from { transform: translateY(100%); }
  to { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .panel,
  .pop { animation: none; }
  .sidebar { transition: none; }
}

/* ---------- cost estimate ---------- */
/* Board cost, under the carcass editor. The total reads first and largest;
   the per-material lines and the caveat are supporting detail. */
.cost {
  padding-bottom: 12px;
}
.cost__totals {
  padding-bottom: 6px;
}
.cost__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
/* What you actually pay leads; the area figure sits under it as context. */
.cost__total--lead .cost__total-value {
  font-size: 18px;
}
.cost__total-value--minor {
  font-size: 12px;
  font-weight: 500;
  color: var(--fg-dim);
}
.cost__total-label {
  font-size: 12px;
  color: var(--fg-dim);
}
.cost__total-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.cost__lines {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Name takes the slack, quantity and cost keep their own columns so the
   figures line up down the list however long the decor names get. */
.cost__line {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 11px;
  padding: 2px 0;
}
.cost__line-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cost__line-qty {
  flex-shrink: 0;
  color: var(--fg-dim);
}
.cost__line-cost {
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.cost__caveat {
  margin: 8px 0 0;
  font-size: 10px;
  line-height: 1.4;
  color: var(--fg-dim);
  opacity: 0.8;
}


/* ---------- the order rail ---------- */
/* The action that completes the job. It had no rules at all until now and came
   out as a bare browser button under the two blue ones — the only unstyled
   thing on screen was the one that earns the money.
   Full width because it is not one option among several. */
.sidebar__export {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--rule);
}
.export-btn {
  width: 100%;
  padding: 11px 16px;
  border: none;
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--paper);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 120ms ease;
}
.export-btn:hover:not(:disabled) {
  opacity: 0.88;
}
.export-btn:disabled {
  background: var(--paper-2);
  color: var(--ink-dim);
  cursor: not-allowed;
}
/* Why you cannot order yet. Reasons, not errors: the design is unfinished, and
   saying which part is the useful thing. */
.export-blockers {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--fault);
}
.export-blockers li {
  padding-left: 14px;
  text-indent: -14px;
}
.export-blockers li::before {
  content: "\2014\00a0";
}


/* ---------- price ---------- */
/* What you pay, said once and large. It was four numbers and a paragraph of
   nesting caveat; three of the numbers were about sheets of raw board, which is
   how the piece gets made rather than what anybody is buying. */
.price {
  padding: 4px 0 12px;
}
.price__amount {
  display: flex;
  align-items: baseline;
  gap: 3px;
  font-size: 34px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.price__currency {
  font-size: 20px;
  font-weight: 500;
  color: var(--ink-dim);
}
.price__note {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--ink-dim);
}

/* ---------- the lede ---------- */
/* One sentence saying how the thing works, where six palette chips and three
   headings used to be. */
.sidebar__lede {
  margin: 0 0 4px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-dim);
}

/* ---------- secondary actions ---------- */
.sidebar__secondary {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: none;
  color: var(--ink-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: border-color 100ms ease, color 100ms ease;
}
.sidebar__secondary:hover:not(:disabled) {
  border-color: var(--ink);
  color: var(--ink);
}
.sidebar__secondary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ---------- workshop (debug builds) ---------- */
/* Collapsed by default: it is reference, not something to read. */
.workshop {
  border-top: 1px solid var(--rule);
  padding-top: 8px;
}
.workshop__summary {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-dim);
  cursor: pointer;
  list-style: none;
}
.workshop__summary::-webkit-details-marker {
  display: none;
}
.workshop__summary::before {
  content: "+ ";
}
.workshop[open] .workshop__summary::before {
  content: "\2212\00a0";
}
.sidebar__dev {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
/* The bundle is the one of the three you reach for, and the only one whose
   name does not fit beside another. */
.sidebar__dev .sidebar__secondary:first-child {
  flex-basis: 100%;
}
.sidebar__dev .sidebar__secondary:not(:first-child) {
  flex: 1;
}
.sidebar__dev .sidebar__secondary {
  font-size: 12px;
  padding: 6px 8px;
}


/* ---------- the foot of the rail ---------- */
/* Price and the order button, pinned. What something costs and how to buy it
   are the two things that must never be scrolled away from — and pinning them
   also stops the column reading as half-empty, which it did once the palette
   left. */
.sidebar__foot {
  flex: 0 0 auto;
  padding: 12px 12px 14px;
  border-top: 1px solid var(--rule);
  background: var(--paper);
}
.sidebar__foot .price {
  padding-top: 0;
}

/* ---------- a drawer's box ---------- */
/* The height of the box behind the front, which the front's own height caps.
   At the top of its range it is not a number at all but "as tall as this front
   allows", which is what the word beside the field says. */
.boxrow { display: flex; align-items: center; gap: 8px; }
.boxrow__label { flex: 1; font-size: 13px; font-weight: 600; color: var(--fg); }
.boxrow__auto { font-size: 11px; color: var(--fg-dim); }
.boxrow__num {
  width: 64px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 4px 6px;
  font: inherit;
  font-size: 14px;
  color: var(--fg);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.boxrow__num:focus { outline: none; border-color: var(--accent); }
.boxrow__num:disabled { opacity: 0.4; }
.boxrow__unit { font-size: 11px; color: var(--fg-dim); }
.boxslider { width: 100%; margin: 4px 0 0; accent-color: var(--accent); }
.boxslider:disabled { opacity: 0.4; }
.boxends {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--fg-dim);
}

/* ═══════════════════════════════════════════════════════════════════════════
   The context panel
   ═══════════════════════════════════════════════════════════════════════════
   One panel per kind of thing selected, built from a handful of controls that
   look the same wherever they appear: a choice is always a connected row of
   options, a measurement is always a label, a number and a slider, and a
   catalogue part is always a row that opens a pop-out. */

.panel {
  background: var(--panel);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  font-variant-numeric: tabular-nums;
}
/* Bottom-sheet grab handle. Only meaningful on mobile, where the panel is a
   sheet rather than a column. */
.panel__grip {
  display: none;
  flex: 0 0 auto;
  align-self: center;
  width: 40px;
  height: 4px;
  margin: 8px 0 2px;
  border-radius: 999px;
  background: var(--border);
  cursor: grab;
  touch-action: none;
}
.panel__head {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  height: 56px;
  padding: 0 8px 0 16px;
  border-bottom: 1px solid var(--cx-divider);
}
/* The header's controls in the context panels' roles: an outlined toggle that
   fills when on, and quiet icons. */
.panel__head .ghostbtn { border-color: var(--cx-control-border); color: var(--cx-text); }
.panel__head .ghostbtn.is-on {
  background: var(--cx-active-bg);
  border-color: var(--cx-active-bg);
  color: var(--cx-active-fg);
}
.panel__head .iconbtn { color: var(--cx-muted); }
.panel__title {
  margin: 0;
  flex: 1;
  min-width: 0;
  font-size: 16px;
  font-weight: 500;
  color: var(--cx-text);
}
.panel__titles { flex: 1; min-width: 0; }
.panel__titles .panel__title {
  display: block;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.panel__sub {
  margin: 1px 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  color: var(--cx-muted);
}
.panel__sub:empty { display: none; }
/* Reads as the title until pointed at; a field once in it. */
.panel__name {
  margin: 0 0 0 -6px;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  font-family: inherit;
}
.panel__titles .panel__name { width: calc(100% + 6px); box-sizing: border-box; }
.panel__name:hover { border-color: var(--cx-control-border); }
.panel__name:focus {
  outline: none;
  border-color: var(--cx-active-bg);
  background: var(--cx-field);
}
.panel__body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
}

/* ---------- buttons ---------- */
/* An icon on its own, for the ones whose meaning is the picture: close,
   delete. Square, so a row of them lines up. */
.iconbtn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--fg-dim);
  cursor: pointer;
  transition: color 100ms, background 100ms;
}
.iconbtn:hover:not(:disabled) { color: var(--fg); background: var(--panel-2); }
.iconbtn:disabled { opacity: 0.35; cursor: default; }
/* The trash goes red on approach, and only there: it is the one button in the
   header that takes something away. */
.panel__head .iconbtn:first-of-type:hover:not(:disabled) { color: var(--ghost-bad); }

/* Outlined, not filled: these are available, not urged. */
.ghostbtn {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: border-color 100ms, background 100ms, color 100ms;
}
.ghostbtn:hover:not(:disabled) { border-color: var(--accent); }
.ghostbtn:disabled { opacity: 0.4; cursor: default; }
/* Pressed: the model is showing it open. Filled, like a chosen segment, so
   "on" means one thing throughout the panel. */
.ghostbtn.is-on { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.ghostbtn--bare { border-color: transparent; padding: 0 4px; color: var(--fg-dim); }
.ghostbtn--bare:hover:not(:disabled) { border-color: transparent; color: var(--ghost-bad); }

.icon { flex-shrink: 0; }

/* ---------- sections ---------- */
/* One rule between sections and nothing under a heading: two lines within a
   few pixels of each other read as a box, and the panel was full of boxes. */
.sec {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
}
.sec + .sec { border-top: 1px solid var(--border); }
.sec__title {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--fg);
}
.sec__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
.sec__titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sec__sub { font-size: 13px; color: var(--fg-dim); }

/* A control under its own name, for the ones too wide to share a line. */
.labelled { display: flex; flex-direction: column; gap: 6px; }
.labelled__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.labelled__label { font-size: 13px; color: var(--fg-dim); }
/* What is chosen, or a fact about the slot — "Matte stainless", "HDF, 3 mm". */
.labelled__aside { font-size: 13px; color: var(--fg); text-align: right; }

/* A control on the same line as its name. */
.line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.line__label { font-size: 13px; color: var(--fg-dim); }
.line__control { display: flex; align-items: center; gap: 6px; }
/* Nothing to choose: one value, stated. Deliberately not a disabled control —
   a greyed slider still looks clickable. */
.line__only { font-size: 14px; color: var(--fg); }

.stackrow { display: flex; flex-direction: column; gap: 6px; }
/* A fact worth keeping but not worth reading twice. */
.caption { margin: 0; font-size: 12px; color: var(--fg-dim); line-height: 1.4; }
/* Something that just happened, or what fits while you are typing. */
.says { margin: 0; font-size: 13px; color: var(--fg); line-height: 1.4; }
/* Something that is wrong now and will stay wrong until it is fixed. */
.alarm {
  margin: 0;
  padding: 8px 10px;
  border: 1px solid var(--ghost-bad);
  border-radius: 8px;
  background: color-mix(in srgb, var(--ghost-bad) 12%, transparent);
  color: var(--ghost-bad);
  font-size: 13px;
  line-height: 1.4;
}

/* ---------- a choice ---------- */
/* Two or three options as one connected control: the chosen one filled, the
   rest not. A radio group, not buttons that happen to sit together. */
.seg {
  display: grid;
  gap: 3px;
  padding: 3px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.seg__opt {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 0;
  height: 34px;
  padding: 0 6px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--fg-dim);
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
  transition: background 100ms, color 100ms;
}
.seg__opt:hover:not(:disabled):not(.is-on) { color: var(--fg); }
.seg__opt.is-on { background: var(--accent); color: var(--accent-fg); }
.seg__opt:disabled { opacity: 0.35; cursor: default; }
/* A label with a count under it. */
.seg__opt--two { flex-direction: column; gap: 0; height: 46px; padding: 0 4px; }
.seg__label { font-size: 13px; font-weight: 500; line-height: 1.2; }
.seg__sub { font-size: 11px; line-height: 1.3; color: var(--cx-faint); }
.seg__opt.is-on .seg__sub { color: inherit; opacity: 0.7; }
.seg__opt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---------- a measurement ---------- */
/* One pattern for every dimension in the app: the name on the left, the number
   on the right, the slider under both. The slider carries the limits — it
   cannot be dragged past them — so no caption has to repeat what they are. */
.dim { display: flex; flex-direction: column; gap: 6px; }
.dim__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.dim__label { font-size: 13px; color: var(--cx-muted); }
.dim__value { display: flex; align-items: center; gap: 6px; }
/* A word in front of the number, when the number is an outcome rather than a
   setting — a box height that is following the front it sits behind. */
.dim__lead { font-size: 12px; color: var(--fg-dim); }
.dim__num {
  width: 72px;
  box-sizing: border-box;
  height: 32px;
  padding: 0 8px;
  border: 1px solid var(--cx-control-border-2);
  border-radius: 6px;
  background: var(--cx-field-2);
  color: var(--cx-text);
  font: inherit;
  font-size: 15px;
  text-align: right;
}
.dim__num:focus { outline: none; border-color: var(--cx-active-bg); }
.dim__num:disabled { opacity: 0.4; }
.dim__unit { width: 22px; font-size: 13px; color: var(--cx-muted); }
.dim__range { width: 100%; margin: 0; accent-color: var(--cx-active-bg); }
.dim__range:disabled { opacity: 0.4; }
.dim__ends {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--fg-dim);
}
.dim__note { margin: 0; font-size: 12px; color: var(--fg-dim); line-height: 1.4; }

/* ---------- what is chosen from a catalogue ---------- */
/* The grid used to live here, which made a scrolling area inside a scrolling
   panel. The row says what is on it now; choosing happens in the pop-out. */
.pickrow {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 8px 12px 8px 8px;
  border: 1px solid var(--cx-raised-border);
  border-radius: 10px;
  background: var(--cx-raised);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 100ms;
}
.pickrow:hover { border-color: var(--accent); }
.pickrow__thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--paper-2);
}
/* A part is photographed against white and is wider than it is tall. */
.pickrow__thumb--wide { width: 52px; background: #ffffff; }
.pickrow__thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
.pickrow__text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.pickrow__name {
  font-size: 14px;
  font-weight: 500;
  color: var(--cx-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pickrow__sub { font-size: 12px; color: var(--cx-faint); }
.pickrow__action { flex-shrink: 0; font-size: 13px; color: var(--cx-muted); }
.pickrow:hover .pickrow__action { color: var(--cx-text); }
.pickrow__thumb .chip { border-color: var(--cx-control-border-2); }

/* A decor as a colour, or the photograph of one. */
.chip {
  display: block;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.chip img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Inside a segment, beside the word. */
.chip--sm { width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0; }

/* ---------- colours ---------- */
/* A dot needs no name for the colour it stands for, which is what lets the
   buckets be chosen for the eye rather than for the dictionary. */
.dots { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.dot {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background-size: cover;
  cursor: pointer;
  transition: transform 100ms, box-shadow 100ms;
}
.dot:hover { transform: scale(1.08); }
.dot.is-on {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--accent);
}
.dots--big .dot { width: 32px; height: 32px; }
/* The only colour it comes in: shown, because the axis is always named, but
   not offered as a choice. */
.dot.is-only { cursor: default; opacity: 0.85; }
.dot.is-only:hover { transform: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   The stack
   ═══════════════════════════════════════════════════════════════════════════
   Drawn to scale, in the cabinet's own orientation, and edited on the drawing:
   the number that sizes a drawer is *in* the drawer. A column of fields beside
   a picture of the same stack asks you to match one row to one band. */

.strip { display: flex; }
.strip--v { flex-direction: row; align-items: stretch; }
/* Rows carry their own height (in proportion, with a floor), so they do not
   stretch or shrink to fit the strip. */
.strip--v .band { flex: 0 0 auto; }
.strip--h { flex-direction: column; }
.strip__bands {
  flex: 1;
  display: flex;
  min-width: 0;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel-2);
}
.strip--v .strip__bands { flex-direction: column; }
.strip--h .strip__bands { flex-direction: row; height: 120px; }

.band {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 0;
  min-width: 0;
  padding: 0 8px 0 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  overflow: hidden;
  cursor: pointer;
}
/* A row of columns has no width to spare, so the same parts stack. */
.strip--h .band { flex-direction: column; justify-content: center; gap: 4px; padding: 6px 4px; }
.strip--h .band__note { display: none; }
/* Hatched: this one takes what is left rather than stating a size, so its
   number is an outcome. The dashed field and pill say the same thing up close.*/
.band.is-fill {
  background: repeating-linear-gradient(
    135deg, var(--panel) 0 6px, var(--panel-2) 6px 7px
  );
}
.band.is-fill .band__value,
.band.is-fill .band__mode { border-style: dashed; background: transparent; color: var(--fg-dim); }
/* Over its limits. Named by the banner under the stack; this is so you can see
   which one it means without reading. */
.band.is-bad { border-color: var(--ghost-bad); background: color-mix(in srgb, var(--ghost-bad) 14%, var(--panel)); }
.band.is-bad .band__value { color: var(--ghost-bad); }
/* The one being edited, in the colour the model outlines it with. */
.band.is-on { border-color: var(--pick); box-shadow: inset 0 0 0 1px var(--pick); }

.band__num { width: 10px; flex-shrink: 0; font-size: 12px; color: var(--fg-dim); }
/* Text rather than a number field: the value may be typed with a unit after
   it — "40%", "210mm" — which is how a drawer's sizing is changed without
   going to the menu for it. */
.band__value {
  width: 64px;
  box-sizing: border-box;
  flex-shrink: 0;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--fg);
  font: inherit;
  font-size: 15px;
  text-align: right;
}
.band__value:focus { outline: none; border-color: var(--accent); }
.strip--h .band__value { width: 100%; font-size: 13px; padding: 0 4px; }
.band__unit { flex-shrink: 0; font-size: 13px; color: var(--fg-dim); }
/* What a percentage works out to. The percentage is the instruction; this is
   the thing actually being decided. */
.band__note {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--fg-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.band__mode {
  margin-left: auto;
  flex-shrink: 0;
  max-width: 100%;
  height: 28px;
  padding: 0 6px 0 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--fg);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.strip--h .band__mode { margin-left: 0; }

/* The divider between two bands, as its own element: a border cannot be
   grabbed. Wider than it looks — the visible line is 3px but the hit area is
   the whole element, which is what makes it catchable with a finger. */
.band__grip {
  flex: 0 0 10px;
  position: relative;
  touch-action: none;
}
.strip--v .band__grip { cursor: row-resize; }
.strip--h .band__grip { cursor: col-resize; }
.band__grip::after {
  content: "";
  position: absolute;
  background: var(--border);
  border-radius: 2px;
}
.strip--v .band__grip::after {
  left: 42%; right: 42%; top: 50%; height: 3px; transform: translateY(-50%);
}
.strip--h .band__grip::after {
  top: 42%; bottom: 42%; left: 50%; width: 3px; transform: translateX(-50%);
}
.band__grip:hover::after,
.band__grip:focus-visible::after { background: var(--accent); }
.band__grip:focus-visible { outline: none; }
/* While dragging, the whole strip takes the cursor and stops selecting text —
   a drag that starts on the grip keeps moving over the bands. */
.strip__bands.is-dragging { user-select: none; }
.strip__bands.is-dragging .band__grip::after { background: var(--accent); }

.addbtn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: 40px;
  border: 1px dashed var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--fg-dim);
  font: inherit;
  cursor: pointer;
  transition: border-color 100ms, color 100ms;
}
.addbtn:hover:not(:disabled) { border-color: var(--accent); color: var(--fg); }
.addbtn:disabled { opacity: 0.4; cursor: default; }

/* Whatever the picked entry has beyond a size. Raised off the panel, because
   it is about one band rather than about the stack. */
.card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel-2);
}
.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.card__title { margin: 0; font-size: 14px; font-weight: 400; color: var(--fg-dim); }
.card__title b { font-weight: 500; color: var(--fg); margin-right: 4px; }

/* ═══════════════════════════════════════════════════════════════════════════
   Where a door's handle goes
   ═══════════════════════════════════════════════════════════════════════════
   The door at its real proportions, with the hinges drawn on the side they are
   actually on, and six places a handle can sit. Every spot shows a faint handle
   in the direction it would take, so the drawing is always a preview. */

/* The doors on their own, with no box round them; inset just enough for the
   hinges, which stand out past the outer edges. */
.doorview {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 3px;
  margin-bottom: 8px;
}
/* One outline, the same whatever the cabinet is and however many doors fill
   it — as the opening is the same with one door or two: a single door fills
   it, a pair splits it. Drawing the real proportions made a wide, short door a
   letterbox with six spots piled into it. */
.doorview__face {
  display: flex;
  gap: 4px;
  width: 100%;
  height: 180px;
}
.doorview__leaf {
  position: relative;
  flex: 1;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--panel);
}
/* On the outside edge, where the barrel would be. */
.doorview__hinge {
  position: absolute;
  left: -3px;
  width: 4px;
  height: 14px;
  border-radius: 1px;
  background: var(--fg-dim);
}
.doorview__hinge.is-right { left: auto; right: -3px; }

/* The hit target: a thumb-sized square over the handle it picks, and nothing
   to look at until you approach it. */
.spot {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  transform: translate(-50%, -50%);
  cursor: pointer;
}
.spot:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); }
.spot.is-on { background: color-mix(in srgb, var(--fg) 14%, transparent); }
/* The handle itself, drawn at its real size against the board and placed by
   the same function that places it on the cabinet. */
/* A handle, at a size that reads rather than at the one it is. Which way it
   lies is the thing this has to show. */
.spot__bar {
  display: block;
  width: 24px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
}
.spot__bar--up { width: 4px; height: 24px; }
.spot.is-on .spot__bar { background: var(--fg); }

/* ---------- a rail's finish ---------- */
/* The tube itself, in the colour it renders, rather than three words for three
   metals. */
.finishes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.finish {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  padding: 14px 10px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel-2);
  color: var(--fg-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.finish.is-on { border-color: var(--accent); color: var(--fg); }
/* Oval, like the tube: 30 × 15 mm in section. */
.finish__tube { display: block; height: 10px; border-radius: 5px; }
.finish__name { text-align: left; }

/* ═══════════════════════════════════════════════════════════════════════════
   The pop-outs
   ═══════════════════════════════════════════════════════════════════════════
   Beside the panel, over the edge of the model — not on top of it, and with no
   dimmed backdrop: choosing a handle and then nudging its size is one job, so
   the panel stays live while this is open. */

.pop {
  position: fixed;
  right: calc(var(--inspector-w) + 10px);
  width: 360px;
  max-height: min(620px, calc(100dvh - 24px));
  z-index: 46;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.28);
  overflow: hidden;
  animation: pop-in 120ms ease;
}
/* The bottom corners, each drawn as two short strokes, the usual sign for a
   corner that drags. */
.pop__grip {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 1;
  width: 18px;
  height: 18px;
  cursor: nesw-resize;
  touch-action: none;
  background:
    linear-gradient(45deg, transparent 0 18%, var(--cx-faint) 18% 25%, transparent 25% 38%,
      var(--cx-faint) 38% 45%, transparent 45%);
  opacity: 0.6;
}
.pop__grip:hover { opacity: 1; }
.pop__grip--right {
  left: auto;
  right: 0;
  cursor: nwse-resize;
  background:
    linear-gradient(-45deg, transparent 0 18%, var(--cx-faint) 18% 25%, transparent 25% 38%,
      var(--cx-faint) 38% 45%, transparent 45%);
}
@keyframes pop-in {
  from { opacity: 0; transform: translateX(6px); }
  to { opacity: 1; transform: none; }
}
.pop__head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  height: 56px;
  padding: 0 8px 0 16px;
  border-bottom: 1px solid var(--border);
}
/* The handle the pop-out is moved by. */
.pop__head { cursor: grab; user-select: none; touch-action: none; }
.pop__head:active { cursor: grabbing; }
.pop__titles { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.pop__title { margin: 0; font-size: 15px; font-weight: 500; color: var(--fg); }
/* What is on it now, so the pop-out says what it is about to replace. */
.pop__sub {
  font-size: 12px;
  color: var(--fg-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pop__filters {
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex-shrink: 0;
  padding: 16px;
}
/* The two dropdowns side by side. Search, when it is there at all, is a
   debug-only line above them and gets the full width. */
.pop__row { display: flex; gap: 8px; }
.pop__select {
  flex: 1;
  min-width: 0;
  height: 36px;
  padding: 0 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-2);
  color: var(--fg);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.pop__select--wide { width: 100%; }
.pop__find { position: relative; display: flex; align-items: center; }
.pop__find .icon { position: absolute; left: 12px; color: var(--fg-dim); pointer-events: none; }
.pop__find-input {
  width: 100%;
  box-sizing: border-box;
  height: 36px;
  padding: 0 12px 0 36px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-2);
  color: var(--fg);
  font: inherit;
}
.pop__find-input:focus { outline: none; border-color: var(--accent); }

/* One scroll area, and it is this one. */
.pop__grid {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  padding: 4px 16px 16px;
}
/* As many columns as the width takes, so a widened pop-out shows more at a
   time rather than bigger swatches. Four at the usual width. */
.pop__grid--decors { grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 14px 10px; }
/* The pop-out's one scroll area, holding "In this design" above the
   catalogue. Grids inside it do not scroll on their own. */
.pop__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 16px 16px;
}
.pop__scroll .pop__grid { overflow: visible; padding: 0; }
.pop__section { margin: 4px 0 10px; font-size: 13px; font-weight: 500; color: var(--cx-text-2); }
.pop__grid + .pop__section,
.pop__used + .pop__section,
.pop__grid + .pop__section-row,
.pop__used + .pop__section-row { margin-top: 22px; }
/* A catalogue's heading, with the price sort on the right of it. */
.pop__section-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 4px 0 10px;
}
.pop__section-row .pop__section { margin: 0; }
.pricesort { display: flex; align-items: center; gap: 6px; }
.pricesort__label { font-size: 12px; color: var(--cx-faint); }
.pricesort__seg {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--cx-control-border);
  border-radius: 8px;
  background: var(--cx-field);
}
.pricesort__opt {
  height: 26px;
  padding: 0 8px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--cx-text-2);
  font: inherit;
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
}
.pricesort__opt.is-on { background: var(--cx-active-bg); color: var(--cx-active-fg); }

/* The leg picker's line above the cost: how many the cabinet stands on. */
/* A line of its own between the head and the scope row, in the same 16px
   margin as everything else in the pop-out. */
.pop__needs { margin: 0; padding: 14px 16px 0; font-size: 13px; color: var(--cx-text-2); }
/* Shape filters as chips: few enough to show all at once. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip-btn {
  height: 28px;
  padding: 0 12px;
  border: 1px solid var(--cx-control-border);
  border-radius: 999px;
  background: transparent;
  color: var(--cx-text-2);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.chip-btn.is-on { border-color: var(--cx-active-bg); background: var(--cx-active-bg); color: var(--cx-active-fg); }
/* A leg stands on the tile's bottom edge, so heights compare at a glance. */
.part__shot--stand { align-items: flex-end; box-sizing: border-box; padding-bottom: 10px; }
.part__shot--stand .part__img { max-height: 80%; object-position: bottom; }
.used__shot--leg { width: 52px; height: 52px; }

/* Prices: a step lighter than the name they are under. */
.decor__price { font-size: 11px; line-height: 1.35; color: var(--cx-muted); }
.part__price { font-size: 12px; line-height: 1.3; color: var(--cx-text-2); }
.used__aside { display: flex; flex-direction: column; align-items: flex-end; flex-shrink: 0; }
.used__price { font-size: 12px; color: var(--cx-text-2); white-space: nowrap; }

/* A handle the design already wears: its whole setup on one row. */
.pop__used { display: flex; flex-direction: column; gap: 8px; }
.used {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  padding: 6px 12px 6px 6px;
  border: 1px solid var(--cx-raised-border);
  border-radius: 10px;
  background: var(--cx-raised);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.used:hover { border-color: var(--cx-control-border-2); }
.used.is-on { border-color: var(--cx-select); }
.used__shot {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 64px;
  height: 44px;
  border-radius: 6px;
  overflow: hidden;
  background: #ffffff;
}
.used__text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.used__name { font-size: 14px; font-weight: 500; color: var(--cx-text); }
.used__desc { font-size: 12px; color: var(--cx-muted); }
.used__place { flex-shrink: 0; font-size: 12px; color: var(--cx-faint); white-space: nowrap; }
.pop__grid--parts { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px 12px; }

/* A decor is chosen by look, so the name is a caption under the swatch rather
   than a tooltip: four columns is wide enough to tell two oaks apart. */
.decor {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.decor__swatch {
  display: block;
  width: 100%;
  height: 52px;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.decor__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.decor__name { font-size: 12px; line-height: 1.3; color: var(--fg-dim); }
.decor__text { display: flex; flex-direction: column; }
.decor__place { font-size: 11px; line-height: 1.3; color: var(--cx-faint); }
.decor.is-on .decor__swatch { outline: 2px solid var(--accent); outline-offset: 2px; }
.decor.is-on .decor__name { color: var(--fg); }

/* A part is chosen by its photograph, with the range of sizes it is made in
   underneath: the sizes are what the list collapsed, so it says so. */
/* The lines under the photograph sit together, as they do under a decor's
   swatch: only the photograph is set apart from them. */
.part {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.part:disabled { opacity: 0.35; cursor: default; }
.part__shot {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 84px;
  margin-bottom: 6px;
  border-radius: 8px;
  background: #ffffff;
  overflow: hidden;
}
.part__img { max-width: 88%; max-height: 88%; object-fit: contain; display: block; }
.part__name {
  font-size: 13px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.part__sub { font-size: 12px; line-height: 1.3; color: var(--fg-dim); }
.part.is-on .part__shot { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Spinners off: they are 9px of hit area on a number you type or drag, and
   they push the digits left every time the pointer passes over. */
.dim__num::-webkit-outer-spin-button,
.dim__num::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}
.dim__num { appearance: textfield; }

/* Debug builds: GPU time and separate text buttons for the rendering path
   and shadow-map view, in the corner of the model. */
.frame-time {
  position: absolute;
  left: 12px;
  bottom: 12px;
  font: inherit;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: #000;
  pointer-events: auto;
}
.frame-time__button {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.frame-time__button:hover { text-decoration: underline; }
.frame-time__button:focus-visible { outline: 1px solid currentColor; outline-offset: 2px; }

/* ---------- 3D view / room plan switch ---------- */
/* Two segments in the view buttons' own dark pill, the chosen one inverted
   as a pressed view button is. */
.view-switch__option {
  height: 36px;
  padding: 0 14px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: #fff;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.view-switch__option--on { background: #fff; color: rgba(34,32,29,1); }
.view-switch__option:focus-visible { outline: 2px solid #9edaff; outline-offset: 2px; }

/* ---------- room plan ---------- */
/* Over the canvas, under the view buttons and the toast. Walls are ink on
   the room's own grey, the floor paper; what is picked or being moved is the
   selection blue, as it is in the model. */
.room-plan {
  position: absolute;
  inset: 0;
  z-index: 20;
  overflow: hidden;
  background: var(--room);
  outline: none;
  touch-action: none;
}
.room-plan__svg { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; }
.plan-floor { fill: var(--paper); fill-opacity: 0.55; }
.plan-cabinet { fill: var(--ink); fill-opacity: 0.12; stroke: var(--ink-dim); stroke-width: 1; }
.plan-cabinet--hanging { fill-opacity: 0.05; stroke-dasharray: 5 4; }
.plan-cabinet--misfit { stroke: var(--fault); stroke-width: 2; stroke-dasharray: 6 4; }
/* "Measure it at several points": dashed, in the selection blue, so it reads
   as a suggestion about the drawing rather than part of it. */
.plan-measure-hint {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 10px;
  border: 1px dashed var(--pick);
  border-radius: 16px;
  background: color-mix(in srgb, var(--pick) 14%, var(--cx-bg));
  color: var(--cx-text);
  font: inherit;
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
}
.plan-measure-hint--ceiling { transform: none; top: 80px; right: 28px; left: auto; }
.plan-measure-hint:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; }
.plan-wall__band { stroke: var(--ink); stroke-linecap: butt; }
.plan-wall__corner { fill: var(--ink); }
.plan-wall--picked .plan-wall__band { stroke: var(--pick); }
.plan-wall__open { stroke: var(--ink-dim); stroke-width: 2; stroke-dasharray: 8 6; }
.plan-wall--picked .plan-wall__open { stroke: var(--pick); }
.plan-wall__hit { stroke: transparent; stroke-width: 30; cursor: grab; }
.plan-dim { stroke: var(--ink-dim); stroke-width: 1; }
/* A window and a door alike, in paper: the blue a window was drawn in was
   all but the picked wall's. A door is told by its swing. */
.plan-opening__band { stroke: var(--paper); stroke-linecap: butt; }
.plan-opening--picked .plan-opening__band { stroke: var(--pick); }
/* One being dragged in from the sidebar: where it would land, see-through. */
.plan-opening--ghost { opacity: 0.55; pointer-events: none; }
.plan-opening--ghost .plan-opening__band { stroke: var(--pick); stroke-dasharray: 6 4; }
.plan-opening__swing { fill: none; stroke: var(--ink-dim); stroke-width: 1; stroke-dasharray: 5 4; }
.plan-opening__hit { stroke: transparent; stroke-width: 30; cursor: grab; }

.plan-pill,
.plan-letter {
  position: absolute;
  transform: translate(-50%, -50%);
  font: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
/* A number on the plan: in the plan's own colour, so it reads as a break in
   the dimension line, as on a drawing. */
.plan-pill {
  height: 22px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: var(--room);
  color: var(--ink);
  font-size: 12px;
  font-weight: 500;
}
.plan-pill:hover { border-color: #5ea1ff; }
.plan-pill--lit { background: #1d2a3a; border-color: #5ea1ff; color: #ededea; }
.plan-letter {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font-size: 12px;
  font-weight: 600;
}
.plan-letter--picked { background: var(--pick); color: #1b1917; }
.plan-pill:focus-visible,
.plan-letter:focus-visible,
.plan-chip:focus-visible,
.plan-toolbar__button:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; }

.plan-toolbar {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  gap: 2px;
  padding: 4px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
.plan-toolbar__button {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--cx-text);
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
}
.plan-toolbar__button:hover { background: var(--cx-field); }

.plan-editor {
  position: absolute;
  transform: translate(-50%, -50%);
  z-index: 5;
}
.plan-editor--ceiling { transform: none; top: 76px; right: 28px; left: auto; }
.measure {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 260px;
  padding: 12px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  color: var(--cx-text);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
.measure__pair { display: flex; gap: 8px; }
.measure__field { display: flex; flex: 1; align-items: center; gap: 6px; font-size: 13px; color: var(--cx-muted); }
.measure__pair .measure__field { flex-direction: column; align-items: stretch; gap: 4px; }
.measure__field input {
  flex: 1;
  min-width: 0;
  width: 100%;
  box-sizing: border-box;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--cx-control-border);
  border-radius: 8px;
  background: var(--cx-field);
  color: var(--cx-text);
  font: inherit;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.measure__field input:focus { outline: none; border-color: var(--pick); }
.measure__tip { margin: 0; font-size: 12px; line-height: 1.4; color: var(--cx-muted); }
.measure__link {
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--cx-text);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.measure__error { margin: 0; font-size: 12px; color: var(--fault); }

.plan-corner {
  position: absolute;
  top: 28px;
  right: 28px;
  display: flex;
  gap: 8px;
}
.plan-shape { position: relative; }
.plan-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 14px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  color: var(--cx-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.plan-chip strong { font-weight: 600; font-variant-numeric: tabular-nums; }
.plan-chip .shape-icon { width: 28px; height: 22px; }
.plan-shape__menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  min-width: 220px;
  padding: 4px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
.plan-shape__item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--cx-text);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.plan-shape__item:hover { background: var(--cx-field); }
.plan-shape__item--on { background: var(--cx-active-bg); color: var(--cx-active-fg); }
.plan-shape__item .shape-icon { width: 30px; height: 24px; }

.plan-refusal {
  position: absolute;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 6px 6px 16px;
  border: 1px solid var(--fault);
  border-radius: 12px;
  background: var(--cx-bg);
  color: var(--cx-text);
  font-size: 14px;
}

/* ---------- where will it go ---------- */
.room-start {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  width: min(560px, calc(100% - 32px));
  padding: 24px;
  border: 1px solid var(--cx-picker-border);
  border-radius: var(--cx-picker-radius);
  background: var(--cx-bg);
  color: var(--cx-text);
  text-align: center;
}
.room-start__title { margin: 0 0 16px; font-size: 20px; font-weight: 600; }
.room-start__shapes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.room-start__shape {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-height: 96px;
  padding: 14px 8px;
  border: 1px solid var(--cx-control-border);
  border-radius: 10px;
  background: var(--cx-raised);
  color: var(--cx-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.room-start__shape:hover { border-color: var(--ink); }
.room-start__shape:focus-visible,
.room-start__skip:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; }
.room-start__skip {
  margin-top: 16px;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid var(--cx-control-border);
  border-radius: 10px;
  background: transparent;
  color: var(--cx-text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
@media (max-width: 560px) {
  .room-start__shapes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .plan-corner { top: auto; bottom: 16px; right: 16px; }
  .plan-editor--ceiling { top: auto; bottom: 72px; right: 16px; }
}

/* ---------- placing a cabinet ---------- */
/* Round 44 px handles in ink, the toolbar and gap pills as on the plan. */
.placement { position: absolute; inset: 0; pointer-events: none; }
/* The dimension lines the gaps measure: white, on a dark halo so they read
   on a pale wall as on a dark floor. In percent of the viewport, so the
   strokes are kept from scaling with it. */
.placement__dims { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.placement__dims line { vector-effect: non-scaling-stroke; stroke-linecap: round; }
.placement__dim-halo { stroke: rgba(34, 32, 29, 0.55); stroke-width: 3.5; }
.placement__dim { stroke: #fff; stroke-width: 1.5; }
/* A measurement: a small number on its line, a break in it rather than a
   button on it. Its touch area reaches past the capsule. */
.placement__gap {
  position: absolute;
  transform: translate(-50%, -50%);
  height: 22px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: rgba(22, 22, 21, 0.78);
  color: #ededea;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  line-height: 20px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  pointer-events: auto;
}
.placement__gap::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100%, 44px);
  height: 36px;
  transform: translate(-50%, -50%);
}
.placement__gap:hover,
.placement__gap:focus-visible { outline: none; border-color: #5ea1ff; }
.placement__gap--live,
.placement__gap--live:hover { background: #1d2a3a; border-color: #5ea1ff; cursor: default; }
.placement__gap:disabled:not(.placement__gap--live) { cursor: default; }
/* A grip on a tick where the line meets the cabinet's own face: a small
   white dot ringed in the selection blue, its touch area wider. Dragged,
   that face moves. */
.placement__grip {
  position: absolute;
  width: 12px;
  height: 12px;
  transform: translate(-50%, -50%);
  border: 2px solid #5ea1ff;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  pointer-events: auto;
  touch-action: none;
}
.placement__grip::before {
  content: "";
  position: absolute;
  inset: -14px;
  border-radius: 50%;
}
.placement__grip:hover,
.placement__grip--held { background: #5ea1ff; border-color: #fff; }

/* What can be done to what is selected: always at the bottom middle of the
   view, never over the thing itself. */
.action-bar {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  gap: 2px;
  padding: 4px;
  border: 1px solid #3a3a37;
  border-radius: 14px;
  background: rgba(28, 28, 28, 0.96);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
  pointer-events: auto;
}
.action-bar__button {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 44px;
  padding: 0 12px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: #ededea;
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
}
.action-bar__button:hover { background: rgba(255, 255, 255, 0.08); }
.action-bar__button:disabled { opacity: 0.4; cursor: default; }
.action-bar__button:disabled:hover { background: transparent; }
.action-bar__button:focus-visible { outline: 2px solid #5ea1ff; outline-offset: -2px; }
/* The suggestion to fit a piece: first in the bar, tinted blue, with an ×
   that turns it down for good. */
.action-bar__suggest {
  display: flex;
  align-items: center;
  border: 1px solid #3d6aa8;
  border-radius: 10px;
  background: #1d2a3a;
}
.action-bar__dismiss {
  display: grid;
  place-items: center;
  width: 32px;
  height: 44px;
  border: 0;
  background: transparent;
  color: #a9b8cc;
  cursor: pointer;
}
.action-bar__dismiss:hover { color: #fff; }
.action-bar__dismiss svg { width: 12px; height: 12px; }
/* A side built in: no gap to type, the filler closes it. */
/* Messages step up over the bar while it shows. */
.placement__typing {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  width: 160px;
  padding: 8px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  color: var(--cx-muted);
  font-size: 13px;
  pointer-events: auto;
}
.placement__typing input {
  flex: 1;
  min-width: 0;
  height: 32px;
  padding: 0 8px;
  border: 1px solid var(--pick);
  border-radius: 8px;
  background: var(--cx-field);
  color: var(--cx-text);
  font: inherit;
  font-variant-numeric: tabular-nums;
}
.placement__typing .measure__error { flex-basis: 100%; }

/* ---------- the room tiles ---------- */
.room-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-bottom: 8px; }
.room-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 12px 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.room-tile:hover { border-color: var(--accent); }
.room-tile:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; }
/* Follows the finger while a tile is dragged into the room. */
.room-drag-label {
  position: fixed;
  z-index: 200;
  padding: 6px 10px;
  border: 1px dashed var(--pick);
  border-radius: 10px;
  background: var(--cx-bg);
  color: var(--cx-text);
  font-size: 12px;
  white-space: nowrap;
  pointer-events: none;
}

/* ---------- a window or door selected in 3D ---------- */
/* A door's swing on the floor: the quarter its leaf sweeps, dashed round. */
.door-swings { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.door-swings * { vector-effect: non-scaling-stroke; stroke-linecap: round; stroke-linejoin: round; }
.door-swing__area { fill: rgba(255, 255, 255, 0.22); stroke: none; }
.door-swing__arc { fill: none; stroke: rgba(34, 32, 29, 0.55); stroke-width: 1.5; stroke-dasharray: 5 4; }
.door-swing__leaf { stroke: rgba(34, 32, 29, 0.55); stroke-width: 1.5; }

/* ---------- room colours ---------- */
.rc-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.rc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px 0 8px;
  border: 1px solid var(--cx-control-border);
  border-radius: 15px;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.rc-chip.is-on { border-color: #5ea1ff; background: color-mix(in srgb, #5ea1ff 14%, transparent); }
.rc-chip__dot { width: 12px; height: 12px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); }
.rc-swatches { display: grid; grid-template-columns: repeat(6, 36px); justify-content: space-between; gap: 10px 0; margin: 4px 0 8px; }
.rc-swatch {
  position: relative;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.18);
  cursor: pointer;
}
.rc-swatch.is-on { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.18), 0 0 0 2px var(--panel), 0 0 0 4px #ededea; }
.rc-swatch:focus-visible { outline: 2px solid #5ea1ff; outline-offset: 3px; }
.rc-swatch--custom {
  background: conic-gradient(#f66, #fd5, #6d6, #5cf, #86f, #f6c, #f66);
  overflow: hidden;
}
.rc-swatch--custom input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.rc-row { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.rc-label { display: block; margin-top: 14px; margin-bottom: 4px; font-size: 13px; color: var(--fg-dim); }
.rc-row .rc-label { margin: 0; }

/* ---------- a divided space's sizes ---------- */
/* Each space's number on the model, as the cabinet's gaps are; a share of
   what is left greyed, as nobody typed it, and a percentage marked. */
.tracks__size--fill { color: #8f8f8a; }
.tracks__size { cursor: pointer; }
.tracks__typing { width: 176px; }
.tracks__modes { display: flex; gap: 2px; flex-basis: 100%; padding: 2px; border-radius: 8px; background: var(--cx-field); }
.tracks__mode {
  flex: 1;
  height: 26px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--cx-muted);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.tracks__mode--on { background: var(--cx-bg); color: var(--cx-text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); }
/* A + to add a board down a space, a − to take one out: the cover's +, smaller. */
.tracks__add,
.tracks__remove { width: 24px; height: 24px; }
.tracks__remove { border-color: #8f8f8a; }

/* ---------- built-in fit ---------- */
/* The row in the cabinet panel, and the page it opens. Error red as the
   handoff has it: #f07a6a on dark. */
.fitrow {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel-2);
  color: var(--fg);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  text-align: left;
}
.fitrow:hover { border-color: var(--cx-control-border-2); }
.fitrow__label { display: flex; align-items: center; gap: 8px; flex: none; }
.fitrow__value { flex: 1; text-align: right; color: var(--fg); }
.fitrow__value.is-off { color: var(--fg-dim); }
.fitrow__missing { color: #f07a6a; white-space: nowrap; }
.fitpage { display: flex; flex-direction: column; }
.fitpage__back {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 14px 16px;
  border: 0;
  border-bottom: 1px solid var(--cx-divider);
  background: none;
  color: var(--cx-text);
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}
.fitpage__back:hover { background: var(--cx-raised); }
.fitpage__back svg { color: var(--cx-muted); }
.fitpage__remove {
  width: 100%;
  height: 40px;
  border: 1px solid var(--cx-control-border);
  border-radius: 10px;
  background: none;
  color: var(--cx-text);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.fitpage__remove:hover { background: var(--cx-raised); }
.fitpage__note { margin: 8px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--fg-dim); }
.fitpage__head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.fitpage__count { font-size: 12.5px; color: #8fb8ee; }
.fitpage__count.is-missing { color: #f07a6a; }
.fitchips { display: flex; flex-wrap: wrap; gap: 6px; }
.fitchip {
  padding: 7px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--fg-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.fitchip.is-on { border-color: #3d6aa8; background: #1d2a3a; color: #fff; }
.fitcheck { border: 1px solid var(--border); border-radius: 10px; margin-top: 8px; }
.fitcheck.is-open { border-color: var(--cx-control-border-2); }
.fitcheck__head {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  background: none;
  color: var(--fg);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.fitcheck__ring {
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  border: 1.5px solid #f07a6a;
  border-radius: 50%;
  font-size: 11px;
  color: #fff;
}
.fitcheck__ring.is-done { border-color: #3d6aa8; background: #3d6aa8; }
.fitcheck__title { font-size: 13px; }
.fitcheck__sum { font-size: 12px; color: var(--fg-dim); }
.fitcheck__body { padding: 0 12px 12px; }
.fitcheck__fields { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.fitcheck__fields label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--fg-dim); }
.fitcheck__fields label > span:first-child { flex: 1; }

/* Order held up by a built-in piece's missing measurements. */
.fit-blocked {
  display: flex;
  gap: 8px;
  margin: 8px 0 0;
  padding: 10px 12px;
  border: 1px solid #8a3a33;
  border-radius: 10px;
  background: #2e1c1a;
  color: #ffc9c0;
  font-size: 12.5px;
  line-height: 1.4;
}
.fit-blocked p { margin: 0; }
/* At most a third of the window; past that it scrolls. */
.order-errors { max-height: 33vh; overflow-y: auto; flex-shrink: 0; }
.order-errors:empty { display: none; }
.fit-blocked svg { flex: none; margin-top: 1px; }
.fit-blocked__link {
  margin-top: 4px;
  padding: 0;
  border: 0;
  background: none;
  color: #f07a6a;
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* In the way, but not stopping the order. */
.fit-warning {
  display: flex;
  gap: 8px;
  margin: 8px 0 0;
  padding: 10px 12px;
  border: 1px solid #8a6a2a;
  border-radius: 10px;
  background: #2e281a;
  color: #ffe2a8;
  font-size: 12.5px;
  line-height: 1.4;
}
.fit-warning p { margin: 0; }
.fit-warning svg { flex: none; margin-top: 1px; }
.fit-warning__link {
  margin-top: 4px;
  padding: 0;
  border: 0;
  background: none;
  color: #e0a84a;
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* Needed for a built-in fit and not measured: the plan's wall label and its
   ceiling and skirting chips, in the error red. */
.plan-pill--needed, .plan-chip--needed { border-color: #f07a6a !important; background: #2e1c1a !important; color: #ffc9c0 !important; }
.dim__num.is-missing { border-color: #f07a6a; }
/* The skirting panel, top right like the shape menu. */
.plan-skirting {
  position: absolute;
  top: 72px;
  right: 16px;
  z-index: 6;
  width: 340px;
  padding: 12px 14px;
  border: 1px solid var(--cx-picker-border);
  border-radius: 12px;
  background: var(--cx-bg);
  color: var(--cx-text);
  font-size: 13px;
}
.plan-skirting__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.price__estimate { margin-left: 8px; font-size: 13px; font-weight: 400; color: var(--ink-dim); }
/* Back to the fillers following the fronts. */
.decor-reset {
  align-self: flex-start;
  margin-top: 6px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--cx-text);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}
.placement__gap--fitted { background: #2b5ea8; color: #fff; pointer-events: none; }

/* ---------- the waitlist, while ordering is not open ---------- */
/* Joined: the order button stays where it was, outlined, and says so. */
.export-btn--joined {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink);
}
.export-btn--joined svg { color: var(--sound); }
.order-joined-note { margin: 0; font-size: 12px; color: var(--ink-dim); }

.order-backdrop {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: rgba(10, 10, 9, 0.62);
}
/* Centred on the 3D view, right of the sidebar. */
.order-dialog {
  position: fixed;
  z-index: 901;
  top: 50%;
  left: calc(var(--sidebar-w) + (100vw - var(--sidebar-w)) / 2);
  transform: translate(-50%, -50%);
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 28px;
  border-radius: 18px;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.35);
}
@media (max-width: 900px) {
  .order-dialog { left: 50%; }
}
.order-dialog__close {
  position: absolute;
  right: 12px;
  top: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--ink-dim);
  cursor: pointer;
}
.order-dialog__close:hover { background: var(--paper-2); color: var(--ink); }

.order-piece {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px;
  margin-right: 36px;
  border-radius: 12px;
  background: var(--paper-2);
}
.order-piece__thumb {
  flex-shrink: 0;
  width: 72px;
  height: 72px;
  border-radius: 8px;
  background: var(--paper);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.order-piece__thumb svg { width: 100%; height: 100%; }
.order-piece__text { display: flex; flex-direction: column; gap: 2px; flex-grow: 1; min-width: 0; }
.order-piece__name { font-size: 15px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.order-piece__size { font-size: 13px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.order-piece__saved { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--sound); }
.order-piece__price { font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }

.order-dialog__head { display: flex; flex-direction: column; gap: 8px; }
.order-dialog__head h2 { margin: 0; font-size: 26px; line-height: 1.2; font-weight: 600; }
.order-dialog__head p { margin: 0; font-size: 15px; line-height: 1.45; color: var(--ink-dim); }
.order-dialog__done { display: flex; align-items: center; gap: 10px; }
.order-dialog__done svg { flex-shrink: 0; color: var(--sound); }

.order-form { display: flex; flex-direction: column; gap: 10px; }
.order-form > label:first-child { font-size: 13px; color: var(--ink-dim); }
.order-form input[type="email"] {
  height: 48px;
  box-sizing: border-box;
  padding: 0 14px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--cx-field);
  color: var(--ink);
  font: inherit;
  font-size: 16px;
}
.order-form input[type="email"]:focus-visible { outline: 2px solid var(--ink); outline-offset: -1px; }
.order-form input[aria-invalid="true"] { border-color: var(--fault); }
.order-form__error { font-size: 13px; color: var(--fault); }
.order-form__check { display: flex; align-items: flex-start; gap: 10px; font-size: 14px; cursor: pointer; }
.order-form__check input { width: 18px; height: 18px; margin: 1px 0 0; flex-shrink: 0; accent-color: var(--ink); }
.order-form .export-btn { margin-top: 4px; min-height: 48px; }
.order-form__note { font-size: 12px; color: var(--ink-dim); }
.order-form__note a { color: var(--ink); }

.order-ask { display: flex; flex-direction: column; gap: 10px; padding-top: 16px; border-top: 1px solid var(--rule); font-size: 14px; color: var(--ink-dim); }
.order-ask__chips { display: flex; flex-wrap: wrap; gap: 8px; }
.order-ask__chips button {
  height: 40px;
  padding: 0 14px;
  border: 1px solid var(--rule);
  border-radius: 20px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.order-ask__chips button:hover { background: var(--paper-2); }
.order-ask__thanks { font-size: 14px; color: var(--ink-dim); }
.order-dialog__back {
  height: 48px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}
.order-dialog__back:hover { background: var(--paper-2); }

/* HR | EN, in the sidebar's header. */
.lang-switch { display: flex; gap: 2px; margin-left: 4px; }
.lang-switch button {
  padding: 4px 6px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-dim);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.lang-switch button:hover { color: var(--ink); background: var(--paper-2); }
.lang-switch button[aria-pressed="true"] { color: var(--ink); background: var(--paper-2); }
