* { box-sizing: border-box; }

:root {
  --bg: #f7f6f4;
  --surface: #ffffff;
  --text: #16150f;
  --text-dim: #8a887f;
  --accent: #f5b301;
  --border: #e8e6e0;
  --radius: 14px;
  --shadow: 0 6px 30px rgba(22, 21, 15, 0.08);
}

html, body {
  margin: 0;
  height: 100%;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  overflow: hidden;
}

body { display: flex; flex-direction: column; }

/* ---------- Top bar ---------- */

#topbar {
  height: 68px;
  flex: 0 0 auto;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 28px;
  position: relative;
  z-index: 10;
}

/* Kabinet Studio lockup (public/brand/logo/). font-size scales the whole
   lockup — mark, wordmark and the oak i-dot — so the app-bar size is set here
   and the brand's own styles in kabinet-logo.css stay untouched. */
.brand .kb-logo { font-size: 22px; }

.topbar-right { display: flex; align-items: center; gap: 26px; }

/* Language toggle: two bare flags, no chrome. The flags are already the most
   saturated thing in a very white topbar, so a pill or a chip around them just
   stacks rounded shapes — the selected state is carried by colour alone:
   the live language is in full colour, the other steps back to grey. */
.lang-switch { display: inline-flex; align-items: center; gap: 8px; }
.lang-switch .lang-opt {
  appearance: none;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  display: block;
  line-height: 0;
  border-radius: 3px;
  transition: transform 0.15s;
}
.lang-switch .lang-opt .flag {
  display: block;
  width: 26px;
  height: 18px;
  border-radius: 3px;
  /* the rounded corner is here, not a clipPath — see js/i18n/flags.js */
  overflow: hidden;
  /* both flags are white-heavy and would otherwise dissolve into the topbar */
  box-shadow: 0 0 0 0.5px rgba(22, 21, 15, 0.22);
  /* Desaturated, not dissolved: at lower opacity the Dutch tricolour's bands
     merge into one grey rectangle and stop reading as a flag at all. */
  filter: grayscale(1) opacity(0.55);
  transition: filter 0.18s;
}
.lang-switch .lang-opt:hover .flag { filter: none; }
.lang-switch .lang-opt:hover { transform: translateY(-1px); }
.lang-switch .lang-opt.active { cursor: default; }
.lang-switch .lang-opt.active .flag {
  filter: none;
  box-shadow: 0 0 0 0.5px rgba(22, 21, 15, 0.3), 0 1px 3px rgba(22, 21, 15, 0.18);
}
.lang-switch .lang-opt.active:hover { transform: none; }
.lang-switch .lang-opt:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .lang-switch .lang-opt,
  .lang-switch .lang-opt .flag { transition: none; }
  .lang-switch .lang-opt:hover { transform: none; }
}

.lang-select { font: 600 11px/1 inherit; padding: 4px 6px; border-radius: 6px; }

.dims {
  font-size: 13px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.price-block { display: flex; flex-direction: column; align-items: flex-end; }
.price-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-dim); }
.price { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }

.cta {
  background: var(--text);
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 12px 24px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.15s, background 0.15s;
}

.cta:hover { background: #000; transform: translateY(-1px); }

/* Heart / save */

#saved-wrap { position: relative; }

.icon-btn {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.icon-btn svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: var(--text);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

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

.icon-btn .count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 17px;
  height: 17px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--text);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  padding: 0 3px;
}

.icon-btn .count:empty { display: none; }

#saved-pop {
  position: absolute;
  top: 50px;
  right: 0;
  width: 290px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 10px 14px;
  display: none;
  z-index: 50;
}

#saved-pop.open { display: block; }

#saved-pop .empty { font-size: 12px; color: var(--text-dim); text-align: center; margin: 10px 0; }

.saved-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
}

.saved-row:last-child { border-bottom: none; }
.saved-row a { color: var(--text); text-decoration: none; flex: 1; }
.saved-row a:hover { text-decoration: underline; }
.saved-row span { color: var(--text-dim); font-variant-numeric: tabular-nums; }

.saved-row button {
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: 15px;
  cursor: pointer;
  padding: 0 2px;
}

.saved-row button:hover { color: var(--text); }

/* ---------- Stage ---------- */

#stage { position: relative; flex: 1; min-height: 0; }

#canvas { width: 100%; height: 100%; display: block; touch-action: none; }

/* Product title overlay — hidden: the step bar floats there instead.
   main.js still writes the generated title into it (and document.title). */

#product-title {
  display: none;
  position: absolute;
  top: 20px;
  left: 28px;
  pointer-events: none;
  max-width: 40%;
}

#product-title h1 {
  margin: 0;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

#product-title p {
  margin: 3px 0 0;
  font-size: 12.5px;
  color: var(--text-dim);
}

/* Constraint snackbar */

#snackbar {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow);
  padding: 10px 8px 10px 16px;
  font-size: 12.5px;
  display: flex;
  align-items: center;
  gap: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s, transform 0.25s;
  z-index: 5;
}

#snackbar.show { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }

#snackbar button {
  border: none;
  background: none;
  font-size: 16px;
  color: var(--text-dim);
  cursor: pointer;
  padding: 0 8px;
}

#snackbar button:hover { color: var(--text); }

/* Viewport tool strip (canvas edge next to the panel, like Tylko) */

#vp-tools {
  position: absolute;
  right: 392px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

#vp-tools button {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  box-shadow: var(--shadow);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s, background 0.15s;
}

#vp-tools button svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: var(--text);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

#vp-tools button:hover { border-color: var(--text); }
#vp-tools button.active { background: var(--text); border-color: var(--text); }
#vp-tools button.active svg { stroke: #fff; }

/* The palette icon's paint blobs are solid, not outlines — the shared rule
   above strokes everything and fills nothing. */
#tool-wall svg .dot { fill: var(--text); stroke: none; }
#tool-wall.active svg .dot { fill: #fff; }

/* ---------- Wall colour picker ---------- */

/* Anchored to the palette button by script (the tool column moves to the
   bottom-right corner on a phone), so only the look lives here. */
#wall-pop {
  position: absolute;
  width: 240px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px 12px;
  display: none;
  z-index: 20;
}

#wall-pop.open { display: block; }

#wall-pop h3 {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

#wall-presets {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  margin-bottom: 12px;
}

.wall-preset {
  height: 30px;
  border-radius: 8px;
  border: 1px solid rgba(22, 21, 15, 0.14);
  padding: 0;
  cursor: pointer;
  position: relative;
}

.wall-preset:hover { transform: translateY(-1px); }
/* Active paint: a ring outside the swatch, so the colour itself stays untouched. */
.wall-preset.active { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--text); }

.wall-free { display: grid; gap: 8px; }

#wall-sv, #wall-hue {
  display: block;
  width: 100%;
  border-radius: 8px;
  cursor: crosshair;
  touch-action: none; /* the drag is ours, not the page's */
}

#wall-sv { height: 112px; }
#wall-hue { height: 12px; }
#wall-sv:focus-visible, #wall-hue:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

.wall-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
}

#wall-swatch {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: 7px;
  border: 1px solid rgba(22, 21, 15, 0.14);
}

#wall-hex {
  flex: 1 1 auto;
  min-width: 0;
  font: 500 12px/1 inherit;
  font-variant-numeric: tabular-nums;
  text-transform: lowercase;
  padding: 7px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
}

#wall-hex:focus { outline: none; border-color: var(--text); }
#wall-hex.invalid { border-color: #d2564a; }

#wall-reset {
  flex: 0 0 auto;
  font: 500 12px/1 inherit;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text-dim);
  cursor: pointer;
}

#wall-reset:hover { border-color: var(--text); color: var(--text); }


#toast {
  position: absolute;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%) translateY(10px);
  background: var(--text);
  color: #fff;
  font-size: 13px;
  padding: 10px 20px;
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s, transform 0.25s;
}

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

/* ---------- Control panel ---------- */

#panel {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 352px;
  max-height: calc(100% - 40px);
  overflow-y: auto;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
}

/* The panel scrolls when a step overflows, but without visible chrome */
#panel { scrollbar-width: none; }
#panel::-webkit-scrollbar { display: none; }

.section { padding: 18px 22px 6px; }
.section + .section { border-top: 1px solid var(--border); }

.section h2 {
  margin: 0 0 14px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}

.control-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.control-row label { flex: 0 0 84px; font-size: 13.5px; font-weight: 500; }

.control-row input[type="range"] {
  flex: 1;
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
  height: 3px;
  border-radius: 2px;
  background: var(--border);
  outline: none;
}

.control-row input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--text);
  cursor: grab;
  transition: transform 0.1s;
}

.control-row input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); }

/* Slider with a value bubble riding the thumb */

.slider-wrap {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  height: 28px;
}

.slider-wrap .dot {
  position: absolute;
  top: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #cfccc3;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.slider-wrap .bubble {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  background: var(--surface);
  border: 1.5px solid var(--text);
  border-radius: 999px;
  padding: 4px 11px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  z-index: 1;
}

.slider-wrap.has-pill input[type="range"]::-webkit-slider-thumb { opacity: 0; }

.control-row .value {
  flex: 0 0 64px;
  text-align: right;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

/* Pill button groups */

.pills {
  display: flex;
  gap: 6px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.pill {
  flex: 1;
  min-width: 0;
  padding: 9px 10px;
  font-size: 12.5px;
  font-weight: 500;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s, background 0.15s;
}

.pill:hover { border-color: var(--text-dim); }
.pill.active { border-color: var(--text); background: var(--text); color: #fff; }

/* Depth: four numbers and "Op maat" share one row. Equal shares leave each
   ~54px — too narrow for the word, which then runs out of its own outline.
   Here a pill never shrinks below its label; the numbers split what is left. */
.depth-pills .pill { flex: 1 0 auto; }

/* Toggle switch */

.switch { position: relative; width: 42px; height: 24px; flex: 0 0 auto; }
.switch input { opacity: 0; width: 0; height: 0; }

.switch .track {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--border);
  transition: background 0.2s;
  cursor: pointer;
}

.switch .track::before {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  top: 3px;
  left: 3px;
  transition: transform 0.2s;
  box-shadow: 0 1px 3px rgba(0,0,0,0.25);
}

.switch input:checked + .track { background: var(--text); }
.switch input:checked + .track::before { transform: translateX(18px); }

.switch-row { justify-content: space-between; }
.switch-row label { flex: 1; }

.hint {
  font-size: 11.5px;
  color: var(--text-dim);
  line-height: 1.45;
  margin: 2px 0 14px;
}

/* Material swatches */

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

.swatch {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 2px solid var(--border);
  padding: 2px;
  background: var(--surface);
  cursor: pointer;
  position: relative;
  transition: border-color 0.15s, transform 0.1s;
}

.swatch:hover { transform: scale(1.08); }
.swatch.active { border-color: var(--text); }

.swatch .chip { width: 100%; height: 100%; border-radius: 50%; display: block; }

.swatch-name {
  font-size: 12px;
  color: var(--text-dim);
  margin: -6px 0 14px;
  min-height: 15px;
}

/* Advanced (thickness) collapsible */

details.advanced { padding: 0 22px 12px; border-top: 1px solid var(--border); }

details.advanced summary {
  padding: 14px 0 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  cursor: pointer;
  list-style: none;
}

details.advanced summary::after { content: " +"; }
details.advanced[open] summary::after { content: " –"; }

/* ---------- Cut list ---------- */

#cutlist-section { border-top: 1px solid var(--border); padding: 0 22px 14px; }

#cutlist-section summary {
  padding: 14px 0 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  cursor: pointer;
  list-style: none;
}

#cutlist-section summary::after { content: " +"; }
#cutlist-section[open] summary::after { content: " –"; }

#cutlist table { width: 100%; border-collapse: collapse; font-size: 11.5px; }

#cutlist th, #cutlist td {
  text-align: left;
  padding: 4px 6px 4px 0;
  border-bottom: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
}

#cutlist th { color: var(--text-dim); font-weight: 500; }
#cutlist td.num, #cutlist th.num { text-align: right; padding-right: 0; }

.cutlist-totals { margin-top: 10px; font-size: 12px; color: var(--text-dim); }
.cutlist-note { margin-top: 8px; font-size: 10.5px; color: var(--text-dim); line-height: 1.4; }
/* Secondary/utility pill — distinct from the black header CTA (quote). */
.export-btn {
  width: 100%;
  margin-top: 14px;
  padding: 11px 16px;
  border: 1.5px solid var(--text);
  border-radius: 999px;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.15s, background 0.15s, color 0.15s;
}
.export-btn:hover { background: var(--text); color: #fff; transform: translateY(-1px); }
.export-note { margin-top: 8px; font-size: 10.5px; color: var(--text-dim); line-height: 1.4; }

#cutlist table.hardware { margin-top: 14px; }

/* ---------- Demo plate (Overzicht step) ---------- */
/* The only block that addresses the kastenmaker instead of the customer, so it
   speaks in a different voice rather than louder in the same one: an ink plate
   full-bleed across the panel — the maker's plate on the back of a cabinet —
   instead of one more tinted card among the controls. It is the first thing in
   the step, so the panel's own rounded corners clip its top edge and it reads
   as part of the panel, not as a banner dropped on top of it. */

.demo-plate {
  padding: 21px 22px 20px;
  background: var(--text);
  color: #f6f4ee;
}

.demo-plate h3 {
  margin: 0 0 10px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.015em;
}

/* Warm grey on ink: readable, but a clear step below the headline so the plate
   stays quiet next to the controls underneath it. */
.demo-plate p {
  margin: 0 0 9px;
  font-size: 11.5px;
  line-height: 1.65;
  color: rgba(246, 244, 238, 0.64);
}

.demo-plate p:last-of-type { margin-bottom: 16px; }

/* The one accent moment on the plate: the same solid-pill idiom as the topbar
   CTA, in the brand accent because here the accent IS the offer. */
.demo-plate-cta {
  display: inline-block;
  padding: 9px 16px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--text);
  font-size: 11.5px;
  font-weight: 700;
  text-decoration: none;
  transition: transform 0.15s, box-shadow 0.15s;
}

.demo-plate-cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 5px 16px rgba(245, 179, 1, 0.3);
}

.demo-plate-cta:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .demo-plate-cta { transition: none; }
  .demo-plate-cta:hover { transform: none; }
}

/* ---------- Section count stepper ---------- */

.stepper {
  display: flex;
  align-items: center;
  gap: 2px;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  padding: 2px;
}

.step-btn {
  width: 28px;
  height: 28px;
  border: none;
  background: none;
  border-radius: 50%;
  font-size: 15px;
  cursor: pointer;
  color: var(--text);
  line-height: 1;
}

.step-btn:hover:not(:disabled) { background: var(--bg); }
.step-btn:disabled { opacity: 0.3; cursor: default; }

.step-value {
  min-width: 26px;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.control-row .auto-pill { flex: 0 0 auto; padding: 7px 14px; }

.pill:disabled { opacity: 0.35; cursor: default; }
.pill:disabled:hover { border-color: var(--border); }

/* ---------- Section editor popover ---------- */

#section-pop {
  position: absolute;
  width: 264px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px 8px;
  display: none;
  z-index: 40;
}

#section-pop.open { display: block; }

.pop-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }
.pop-head strong { font-size: 13.5px; }
.pop-head span { font-size: 11.5px; color: var(--text-dim); flex: 1; }

.pop-close {
  border: none;
  background: none;
  font-size: 18px;
  color: var(--text-dim);
  cursor: pointer;
  padding: 0 2px;
  line-height: 1;
}

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

/* Element palette: engine-rendered cards, dragged onto the model */

.pop-drag-hint {
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.5;
  margin: -6px 0 10px;
}

.comp-palette {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  margin-bottom: 12px;
}

.comp-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 5px 4px 6px;
  border: 1.5px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  cursor: grab;
  touch-action: none;
  transition: border-color 0.15s, transform 0.1s;
}

.comp-card img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 7px;
  background: #f3f1ec;
  pointer-events: none;
}

.comp-card span { font-size: 9.5px; font-weight: 500; color: var(--text); text-align: center; }

/* "Plank set" is a generator, not a single piece — mark it as a different kind:
   an accent-tinted tile with a schematic icon instead of a photographic thumb. */
.comp-card.is-generator {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  background: color-mix(in srgb, var(--accent) 5%, var(--surface));
}
.comp-card .gen-icon {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 7px;
  background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  pointer-events: none;
  display: block;
}
.gen-icon .gi-plank { fill: color-mix(in srgb, var(--text) 78%, transparent); }
.gen-icon .gi-eq path { stroke: var(--accent); stroke-width: 1.5; stroke-linecap: round; fill: none; }

.comp-card:hover:not(:disabled) { border-color: var(--text-dim); transform: translateY(-1px); }
.comp-card.is-generator:hover:not(:disabled) { border-color: var(--accent); }
.comp-card:active:not(:disabled) { cursor: grabbing; }
.comp-card:disabled { opacity: 0.35; cursor: default; }

/* Click-place mode: cards are click targets, not drag handles. The armed card
   stays highlighted until you pick another, place off-target, or press Esc. */
body.place-click .comp-card:not(:disabled),
body.place-click .comp-card:not(:disabled):active { cursor: pointer; }
.comp-card.picked {
  border-color: var(--accent);
  background: rgba(245, 179, 1, 0.10);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* Placement-mode switch (Drag | Click) shown above each palette. A small
   segmented control; the active half fills to read as selected (like .pill.active). */
.place-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  margin: 0 0 10px;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
}
.place-toggle button {
  appearance: none;
  border: none;
  background: none;
  padding: 5px 15px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-dim);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.place-toggle button:hover { color: var(--text); }
.place-toggle button.on { background: var(--text); color: #fff; }

/* Mobile is drag-only (see main.js): there's no click-place mode to switch to,
   so the Drag|Click toggle is hidden — it would only add clutter to the sheet. */
body.is-touch .place-toggle { display: none; }

/* Drag carry: a floating model that rides under the cursor from the moment you
   grab, so it's clear you're carrying an element well before the pointer ever
   reaches the cabinet. Positioned in JS via left/top; the transform only
   lifts/tilts it, so movement stays instant. It hosts either a live 3D render
   of the bare element (.is-3d) or, as a fallback, a clone of the flat card. */
.drag-ghost {
  position: fixed;
  left: 0;
  top: 0;
  width: 66px;
  z-index: 10000;
  pointer-events: none;
  opacity: 0;
  transform: translate(10px, 10px) rotate(-4deg) scale(0.6);
  transform-origin: top left;
  transition: opacity 0.12s ease, transform 0.12s ease;
  filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.28));
  will-change: left, top;
}
.drag-ghost.on {
  opacity: 1;
  transform: translate(10px, 10px) rotate(-4deg) scale(1);
}
/* Over a valid compartment the landing preview shows the piece in place, so the
   carried model hides entirely — you only ever see one piece at a time. */
.drag-ghost.droppable {
  opacity: 0;
  transform: translate(10px, 10px) rotate(0deg) scale(0.9);
}
.drag-ghost .comp-card {
  margin: 0;
  cursor: grabbing;
}

/* Touch: center the carry horizontally over the finger (main.js lifts it above
   the fingertip via `top`). Desktop keeps the offset-to-the-right cursor
   placement above; on a phone there's no cursor, so it should sit on the touch
   point, not beside it. translate(-50%) against the top-left origin centres the
   card on left=clientX. */
body.is-touch .drag-ghost { transform: translate(-50%, 0) rotate(-4deg) scale(0.6); }
body.is-touch .drag-ghost.on { transform: translate(-50%, 0) rotate(-4deg) scale(1); }
body.is-touch .drag-ghost.droppable { transform: translate(-50%, 0) rotate(0deg) scale(0.9); }

/* Live 3D carry model: a transparent square canvas, no card frame. It's centred
   right on the cursor (the piece sits under the pointer, as if held), and its
   drop-shadow falls on the element's own silhouette (the canvas is clear). */
.drag-ghost.is-3d {
  width: auto;
  transform-origin: center center;
  transform: translate(-50%, -50%) scale(0.72);
  filter: drop-shadow(0 12px 14px rgba(0, 0, 0, 0.32));
}
.drag-ghost.is-3d.on {
  transform: translate(-50%, -50%) scale(1);
}
.drag-ghost.is-3d.droppable {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.9);
}
.drag-ghost.is-3d canvas {
  display: block;
}
/* Reinforce "holding something" wherever the ghost travels. cursor inherits,
   so this only sets the default — the canvas's inline 'copy' over a valid
   compartment still wins. */
body.dragging-palette {
  cursor: grabbing;
}

/* Handle catalog (Handles step): pick-one cards, not drag sources */

.push-card {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  margin-bottom: 8px;
  border: 1.5px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s;
  font-size: 12px;
  font-weight: 500;
  color: var(--text);
}
.push-card svg { width: 26px; height: 26px; flex: none; fill: none; stroke: var(--text); stroke-width: 1.5; stroke-linecap: round; }
.push-card:hover { border-color: var(--text-dim); }
.push-card.active { border-color: var(--text); background: #faf9f7; }

.handle-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin-bottom: 14px;
}

/* One card per handle: a big photo on top, name/price and the handle's own
   colour chips below it. */
.handle-card {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 6px 6px 8px;
  border: 1.5px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s;
  text-align: left;
}
.handle-card:hover { border-color: var(--text-dim); }
.handle-card.active { border-color: var(--text); background: #faf9f7; }

/* Packshot with the mounted-on-furniture photo stacked on top; hovering or
   selecting crossfades to the furniture shot — both pictures, one cell. */
.handle-imgs {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 7px;
  overflow: hidden;
  background: #f3f1ec;
}
.handle-imgs img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}
/* The second shot fades in only while the pointer is on the photo itself —
   never pinned by selection, so a selected option still toggles on hover. */
.handle-imgs img.scene { opacity: 0; transition: opacity 0.18s; }
.handle-imgs:hover img.scene { opacity: 1; }

/* Name left, price right; the chip row spans the full width below them. */
.handle-info {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 5px 8px;
  padding: 0 2px;
}
.handle-card .handle-name { font-size: 12.5px; font-weight: 600; color: var(--text); }
.handle-card .handle-price { font-size: 11px; color: var(--text-dim); }

/* Per-handle steekmaat pills: only the hole spacings the shop sells. A pill
   or chip that doesn't exist with the currently shown counterpart dims. */
.handle-sizes { grid-column: 1 / -1; display: flex; gap: 5px; flex-wrap: wrap; }
.handle-size {
  padding: 2px 8px;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.12s;
}
.handle-size:hover { border-color: var(--text-dim); }
.handle-size.active { border-color: var(--text); background: #f3f1ec; }
.handle-size.off { opacity: 0.35; }

/* Per-handle colour chips: only the finishes this model is photographed in. */
.handle-fins { grid-column: 1 / -1; display: flex; gap: 7px; }
.handle-fin {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1.5px solid rgba(0, 0, 0, 0.18);
  box-shadow: inset 0 0 0 1.5px var(--surface);
  cursor: pointer;
  transition: transform 0.12s;
}
.handle-fin:hover { transform: scale(1.15); }
.handle-fin.active { border-color: var(--text); box-shadow: inset 0 0 0 2px var(--surface); }
.handle-fin.off { opacity: 0.35; }

/* Per-compartment extras as drag chips in the panel (LED / cable hole) */
.palette-flags {
  display: flex;
  gap: 6px;
  margin-bottom: 14px;
}

.palette-flags .flag-chip {
  flex: 1;
  padding: 8px 6px;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 12px;
  font-weight: 500;
  color: var(--text);
  cursor: grab;
  touch-action: none;
  transition: border-color 0.15s, transform 0.1s;
}

.palette-flags .flag-chip:hover { border-color: var(--text-dim); transform: translateY(-1px); }
.palette-flags .flag-chip:active { cursor: grabbing; }

.comp-clear {
  width: 100%;
  margin: 0 0 10px;
  padding: 8px;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  background: none;
  font-size: 11.5px;
  color: var(--text-dim);
  cursor: pointer;
}

.comp-clear:hover { border-color: #c96a5b; color: #c96a5b; }

/* Tiny action pill next to a selected element: icon-only controls (remove /
   duplicate / — for drawers — height). The height feedback itself is drawn in
   the 3D scene as gap dimension lines and an editable pill on the drawer. */
#comp-chip, #hole-chip, #front-chip {
  position: absolute;
  display: none;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: var(--shadow);
  z-index: 45;
  white-space: nowrap;
}

#comp-chip.open, #hole-chip.open, #front-chip.open { display: flex; }

#comp-chip button, #hole-chip button, #front-chip button {
  border: none;
  background: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  color: var(--text-dim);
  cursor: pointer;
}

#comp-chip button svg, #hole-chip button svg, #front-chip button svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

#comp-chip button:hover, #hole-chip button:hover, #front-chip button:hover { color: var(--text); background: #f2f0ea; }
#comp-chip button:focus, #hole-chip button:focus, #front-chip button:focus { outline: none; } /* transient chip — no lingering ring after a click */
#comp-chip .chip-del:hover, #hole-chip .chip-del:hover, #front-chip .chip-del:hover { color: #c96a5b; background: rgba(201, 106, 91, 0.1); }

/* Plank-group count stepper (− N +): shown only when a group is selected. The
   two round buttons reuse the chip button styling; the readout sits between. */
#comp-chip .chip-count { display: flex; align-items: center; gap: 0; }
#comp-chip .chip-count-val {
  min-width: 20px;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
#comp-chip .chip-count button:disabled { opacity: 0.35; cursor: default; background: none; }

/* Dimension stickers are 3D plane meshes on the glass pane (see stage.js), and
   they're edited in place on that pane — so there is no HTML input to style. */

.pop-param {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.pop-param:empty { display: none; }
.pop-param label, .pop-doors > label, .pop-row > label { font-size: 13px; font-weight: 500; }
.pop-doors > label { display: block; margin-bottom: 8px; }
.pop-doors .pills { margin-bottom: 8px; }
/* door pills keep their natural width and wrap instead of squeezing */
.pop-doors .pill { flex: 1 0 auto; }

.pop-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 10px;
}

.pop-flags .pill { flex: 1; padding: 8px 6px; font-size: 12px; }

/* ---------- Dimensions card (shell step) ---------- */

.dim-input {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 1.5px solid var(--border);
  border-radius: 999px;
  padding: 5px 12px;
}

.dim-input input {
  width: 52px;
  border: none;
  outline: none;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: right;
  background: none;
  color: var(--text);
  -moz-appearance: textfield;
}

.dim-input input::-webkit-outer-spin-button,
.dim-input input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.dim-input span { font-size: 11.5px; color: var(--text-dim); }

/* How the cabinet meets the wall skirting: a two-way radio choice (base
   "none") or a computed verdict note (plinth/legs clear the board — or not) */

.fit-opts {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 2px 0 10px;
}

.fit-opt {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  text-align: left;
  padding: 9px 11px;
  border: 1.5px solid var(--border);
  border-radius: 12px;
  background: #fff;
  cursor: pointer;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  transition: border-color 0.12s, background 0.12s;
}

.fit-opt small {
  display: block;
  margin-top: 1px;
  font-size: 11px;
  font-weight: 400;
  color: var(--text-dim);
}

.fit-opt .dot {
  flex: none;
  position: relative;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  border: 2px solid #cbc8be;
  border-radius: 50%;
  background: #fff;
  transition: border-color 0.12s;
}

.fit-opt.active { border-color: var(--accent); background: rgba(245, 179, 1, 0.06); }
.fit-opt.active .dot { border-color: var(--accent); }
.fit-opt.active .dot::after {
  content: "";
  position: absolute;
  inset: 2.5px;
  border-radius: 50%;
  background: var(--accent);
}

.fit-note {
  margin: 2px 0 10px;
  padding: 9px 11px;
  border-radius: 10px;
  font-size: 11.5px;
  line-height: 1.5;
}

.fit-note.ok { background: #edf2e8; color: #44603c; }
.fit-note.warn { background: #f8eed6; color: #7c5c1b; }

/* Panel versions of the same blocks sit inside a .section */
.section .fit-opts { margin-top: 6px; }
.section .fit-note { margin-top: 6px; }

.pop-continue {
  width: 100%;
  margin: 6px 0 8px;
}

/* Info tooltip (measuring tips) */

.info-tip {
  position: relative;
  display: inline-block;
  margin-left: 4px;
  color: var(--text-dim);
  cursor: help;
  font-style: normal;
}

.info-tip .tip-pop {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: 250px;
  background: var(--text);
  color: #f6f5f2;
  font-size: 11.5px;
  font-weight: 400;
  line-height: 1.55;
  padding: 12px 14px;
  border-radius: 10px;
  z-index: 80;
  pointer-events: none;
}

.info-tip:hover .tip-pop, .info-tip:focus .tip-pop { display: block; }

/* ---------- Step progression bar (floats over the room, top-left) ----------
   One connected pill; each step is a chevron segment (arrow point on the
   right, notch on the left) so the row reads as flowing progress. */

#steps {
  position: absolute;
  top: 10px;
  left: 20px;
  z-index: 5;
  display: flex;
  flex-wrap: nowrap;
  /* clip-path eats per-segment box-shadows; a drop-shadow on the group
     traces the chevron outlines instead, lifting them off the room */
  filter: drop-shadow(0 2px 4px rgba(22, 21, 15, 0.18));
  /* stay clear of the config panel on the right */
  max-width: calc(100% - 430px);
}

.step-tab {
  --pt: 11px; /* chevron point depth */
  flex: 0 0 auto;
  width: 112px; /* fixed: fits the widest label ("✓ MATERIAL") so it never shifts */
  border: none;
  background: #ffffff;
  font-family: inherit;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #77756c;
  padding: 9px 16px 10px;
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
  transition: background 0.15s, color 0.15s;
  clip-path: polygon(0 0, calc(100% - var(--pt)) 0, 100% 50%,
      calc(100% - var(--pt)) 100%, 0 100%, var(--pt) 50%);
}

/* overlap so each point nests into the next notch, leaving a thin divider */
.step-tab + .step-tab { margin-left: -8px; }

.step-tab:first-child {
  border-radius: 8px 0 0 8px;
  clip-path: polygon(0 0, calc(100% - var(--pt)) 0, 100% 50%,
      calc(100% - var(--pt)) 100%, 0 100%);
}

.step-tab:last-child {
  border-radius: 0 8px 8px 0;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, var(--pt) 50%);
}

.step-tab:hover { color: var(--text); background: #f2f0ea; }

/* current step: soft near-black (a touch lighter than pure --text) */
.step-tab.active {
  color: var(--surface);
  background: #2d2b25;
}

/* finished steps turn light green */
.step-tab.done {
  color: #33622f;
  background: #d3e9c9;
}

.step-tab .tick { display: none; margin-right: 3px; font-size: 9px; }
.step-tab.done .tick { display: inline; }

#step-next-wrap { padding: 14px 22px 16px; border-top: 1px solid var(--border); }
#step-next { width: 100%; }

.swatch-head {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-dim);
  margin: 2px 0 8px;
}

.auto-swatch .chip {
  background: var(--bg);
  color: var(--text-dim);
  font-size: 15px;
  font-weight: 600;
  line-height: 36px;
  text-align: center;
}

.back-pills .pill { flex: 0 0 auto; padding: 8px 16px; }

/* ---------- Placement chooser (Type step) ---------- */

#type-pop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  align-items: flex-start;
  justify-content: center;
  padding: 9vh 24px 24px;
  background: rgba(22, 21, 15, 0.38);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

#type-pop.open { display: flex; }

.type-card {
  position: relative;
  width: min(880px, 100%);
  background: var(--surface);
  border-radius: 20px;
  box-shadow: 0 28px 90px rgba(22, 21, 15, 0.35);
  padding: 30px 34px;
  animation: type-in 0.28s ease;
}

@keyframes type-in {
  from { opacity: 0; transform: translateY(14px); }
}

.type-card > .pop-close { position: absolute; top: 16px; right: 18px; font-size: 22px; }

.type-card h2 { margin: 0 0 6px; font-size: 21px; letter-spacing: -0.01em; }

.type-intro { margin: 0 0 22px; font-size: 13.5px; color: var(--text-dim); }

.type-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }

.type-opt {
  border: 1.5px solid var(--border);
  background: var(--surface);
  border-radius: 14px;
  padding: 9px 9px 12px;
  font-family: inherit;
  /* iOS Safari paints unstyled button text system-blue — say it explicitly. */
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}

.type-opt:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(22, 21, 15, 0.13); border-color: #d6d3ca; }
.type-opt.active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

.type-opt img {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 9px;
  background: #f3f1ec;
}

.type-opt strong { display: block; margin: 11px 2px 4px; font-size: 13.5px; }
.type-opt span { display: block; margin: 0 2px; font-size: 11.5px; line-height: 1.5; color: var(--text-dim); }

.type-back {
  border: none;
  background: none;
  font-family: inherit;
  font-size: 12.5px;
  color: var(--text-dim);
  cursor: pointer;
  padding: 0;
  margin: 0 0 14px;
}

.type-back:hover { color: var(--text); }

.type-sub-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; }

.type-opt.sub { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 20px 12px 16px; }
.type-opt.sub strong { margin: 0; font-size: 13px; }
.type-opt.sub.active { background: rgba(245, 179, 1, 0.07); }

/* Sloped sides are multi-select: a checkbox on every card signals that up
   front and carries the state, so these drop the single-select accent ring. */
.type-opt.toggle { position: relative; }
.type-opt.toggle .check {
  position: absolute;
  top: 12px;
  left: 12px;
  width: 20px;
  height: 20px;
  box-sizing: border-box;
  border: 2px solid #cbc8be;
  border-radius: 6px;
  background: #fff;
  transition: background 0.12s, border-color 0.12s;
}
.type-opt.toggle .check::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 2px;
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg);
  opacity: 0;
}
.type-opt.toggle.active {
  border-color: #d6d3ca; /* neutral — the checkbox, not an outline, marks selection */
  box-shadow: none;
  background: #f6f4ee;
}
.type-opt.toggle.active .check { background: var(--accent); border-color: var(--accent); }
.type-opt.toggle.active .check::after { opacity: 1; }

.diag { width: 84px; height: 66px; }
.diag rect, .diag path, .diag line { fill: none; stroke: #77756c; stroke-width: 1.6; }
.diag .solid { fill: #ccc9bf; stroke: none; }
.diag .roof { stroke: #b8b5aa; stroke-dasharray: 4 3; }

.type-continue { margin-top: 20px; width: 100%; }
.type-continue:disabled { opacity: 0.4; cursor: default; transform: none; }

/* placement chip at the top of the dimensions card */
.type-chip {
  display: inline-block;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  border-radius: 999px;
  padding: 5px 12px;
  cursor: pointer;
  margin: 0 0 10px;
}

.type-chip:hover { border-color: #cfccc2; background: #f2f0ea; }

@media (max-width: 860px) {
  .type-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Confirm dialog (Start over) ---------- */

/* Same card + scrim as the placement chooser, sized down: it guards one
   irreversible action, not a step. The destructive choice is the quiet red
   one; the safe default carries the weight (the filled pill used everywhere
   else for "go on"). */
#confirm-pop {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(22, 21, 15, 0.38);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

#confirm-pop.open { display: flex; }

.confirm-card {
  width: min(340px, 100%);
  background: var(--surface);
  border-radius: 18px;
  box-shadow: 0 28px 90px rgba(22, 21, 15, 0.35);
  padding: 20px 22px 18px;
  animation: type-in 0.28s ease;
}

.confirm-card h2 {
  margin: 0 0 6px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.confirm-card p {
  margin: 0 0 16px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-dim);
  text-wrap: balance; /* two even lines, not a one-word orphan */
}

.confirm-actions { display: flex; gap: 8px; justify-content: flex-end; }

.confirm-btn {
  padding: 8px 16px;
  border: 1.5px solid transparent;
  border-radius: 999px;
  background: transparent;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.15s;
}

#confirm-cancel {
  border-color: var(--text);
  background: var(--text);
  color: #fff;
}

#confirm-cancel:hover { transform: translateY(-1px); }

.confirm-btn:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

.confirm-btn.danger { border-color: var(--border); color: #d70015; }
.confirm-btn.danger:hover { border-color: #d70015; background: rgba(215, 0, 21, 0.06); }

body.is-touch .confirm-btn { min-height: 44px; }

/* ---------- Thumbnail mode (?thumb=…): bare canvas for pre-renders ---------- */

body.thumb #topbar, body.thumb #panel, body.thumb #steps, body.thumb #vp-tools,
body.thumb #product-title, body.thumb #snackbar, body.thumb #toast, body.thumb #type-pop,
body.thumb #confirm-pop {
  display: none !important;
}

/* ===================== Mobile compact shell (is-compact) ===================== */
:root { --sheet-peek: 76px; --safe-b: 0px; --safe-t: 0px; }
@supports (padding: env(safe-area-inset-bottom)) {
  :root { --safe-b: env(safe-area-inset-bottom); --safe-t: env(safe-area-inset-top); }
}

/* Topbar: one tidy row — brand + inline price + CTA. A FIXED height breaks under
   viewport-fit=cover: --safe-t (the Dynamic Island inset) is padded INTO a
   border-box height and the bar overflows/swells. Size to content instead and
   let the safe-area inset sit above a compact content row. */
body.is-compact #topbar {
  height: auto;
  min-height: 46px;
  padding: calc(var(--safe-t) + 7px) 14px 7px;
  align-items: center;
}
body.is-compact .brand { display: flex; align-items: center; min-width: 0; }
/* Minimal: no dims and no price label on a phone. */
body.is-compact #dims-badge,
body.is-compact .price-label { display: none; }
/* Phone: price, heart, language toggle and a Dutch-length CTA already fill the
   bar, and the lockup — unlike the brand text it replaced — cannot ellipsise;
   it would paint straight over them. Drop to the mark alone, which the brand
   allows as a standalone form (public/brand/logo/README.md: minimum mark 16px),
   and keep it from being squeezed by the row beside it. */
body.is-compact .brand { flex: 0 0 auto; }
body.is-compact .brand .kb-logo { font-size: 19px; }
body.is-compact .kb-word,
body.is-compact .kb-studio { display: none; }
body.is-compact .topbar-right { gap: 12px; }
/* Text expansion: Dutch runs ~20% longer than English ("Offerte aanvragen" vs
   "Request quote"), which on a 420px phone wrapped the CTA onto two lines and
   squeezed the brand into an ellipsis. Keep the CTA on one line and let it
   shrink instead, and let the language toggle give up its gap first. */
body.is-compact #quote-btn {
  white-space: nowrap;
  flex: 0 1 auto;
  min-width: 0;
  padding-left: 12px;
  padding-right: 12px;
  font-size: 12px;
}
/* Phone: the topbar is tight (Dutch runs ~20% longer than English), so the
   toggle gives up a few px before the brand or the CTA has to. */
body.is-compact .lang-switch { gap: 6px; }
body.is-compact .lang-switch .lang-opt .flag { width: 21px; height: 15px; }
/* Price inline: the stacked label is hidden, so keep the value on one line and
   stop it being squeezed into a €/value wrap. */
body.is-compact .price-block { flex: 0 0 auto; }
body.is-compact .price { font-size: 16px; white-space: nowrap; }
body.is-compact .cta { padding: 9px 16px; font-size: 13px; }
/* Save heart: drop the circle chrome on a phone — just the glyph reads lighter
   and lets the bar feel thinner. Keep a finger-sized hit box. */
body.is-compact .icon-btn { border: none; background: none; width: 40px; height: 40px; }

/* Steps: five chevrons → a centered dot-progress strip */
body.is-compact #steps {
  top: calc(8px + var(--safe-t)); left: 0; right: 0;
  max-width: none; justify-content: center; gap: 8px; filter: none;
}
body.is-compact .step-tab {
  width: 9px; height: 9px; min-width: 0; padding: 0; margin: 0;
  font-size: 0; letter-spacing: 0; border-radius: 50%;
  background: #cfccc4; clip-path: none; transition: background 0.2s;
}
body.is-compact .step-tab.active { background: #2d2b25; }
body.is-compact .step-tab.done   { background: #9cc78e; }
body.is-compact .step-tab .tick  { display: none; }

/* Panel → full-width bottom sheet. Task 4 toggles .detent-half/.detent-full. */
body.is-compact #panel {
  position: fixed; inset: auto 0 0 0; top: auto;
  width: auto; height: 100dvh; max-height: none; overflow: hidden;
  border-radius: 16px 16px 0 0;
  transform: translateY(calc(100% - var(--sheet-peek) - var(--safe-b)));
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  padding-bottom: var(--safe-b);
  z-index: 20;
}
/* Panel height == viewport height (100dvh above) so its bottom-anchored
   natural top edge sits at y=0 — translateY(y) then lands the top edge at
   exactly y, matching bottom-sheet.js's from-top detent math (which computes
   in window.innerHeight pixels, same frame dvh tracks). */
/* 'half' is the low working height: sheet rests below mid-screen (covers ~45%)
   so the cabinet stays visible above while editing. Split Size steps fit here. */
body.is-compact #panel.detent-half { transform: translateY(55dvh); }
body.is-compact #panel.detent-full { transform: translateY(10dvh); }
/* the sheet's own body scrolls once expanded (bounded to the visible area) */
body.is-compact #panel.detent-half #controls { overflow-y: auto; max-height: 40dvh; }
body.is-compact #panel.detent-full #controls { overflow-y: auto; max-height: 82dvh; }
/* The scroll region's bottom runs off-screen behind the phone's browser chrome,
   so the last rows of a step can't be scrolled into view. Trailing whitespace
   gives every step room to scroll its final control clear of the bottom edge. */
body.is-compact #controls { padding-bottom: calc(var(--safe-b) + 11dvh); }

body.is-compact .sheet-grab {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  height: 24px; cursor: grab; touch-action: none; background: var(--surface);
  border-radius: 16px 16px 0 0;
}
body.is-compact .sheet-grab::after {
  content: ''; width: 40px; height: 4px; border-radius: 2px; background: var(--border);
}
body:not(.is-compact) .sheet-grab { display: none; }

body.is-compact .sheet-peekbar {
  display: flex; align-items: center; gap: 8px;
  padding: 0 12px 10px; min-height: 40px;
  touch-action: none; /* the whole header drags the sheet — don't let it scroll */
}
/* Back + Next: a matched secondary/primary pair from the same design language —
   identical 40px height, pill radius and 1px stroke. Back (top-left, replacing
   the "· change" chip) is the outlined chevron; Next is the filled action with a
   mirrored forward chevron. */
body.is-compact .sheet-peekbar .pk-back {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); color: var(--text);
  font-size: 22px; line-height: 1;
}
body.is-compact .sheet-peekbar .pk-step { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-dim); }
body.is-compact .sheet-peekbar .pk-next {
  display: flex; align-items: center; gap: 5px;
  margin-left: auto; height: 40px; padding: 0 20px;
  border: 1px solid var(--text); border-radius: 999px;
  background: var(--text); color: #fff; font-weight: 600; font-size: 14px;
}
body.is-compact .sheet-peekbar .pk-next::after { content: '\203A'; font-size: 18px; line-height: 1; }
body:not(.is-compact) .sheet-peekbar { display: none; }

/* The in-panel placement chip ("Free-standing · change") is redundant on a
   phone — the peek-bar back arrow returns to the placement chooser instead. */
body.is-compact .type-chip { display: none; }

/* Avoid a double "Next": the peek-bar carries the persistent Next on compact,
   so hide the in-panel step-next. Hide the WRAPPER (not the #step-next button):
   syncPeek reads the button's own display to know if a next step exists. */
body.is-compact #step-next-wrap { display: none; }

/* Handles: the phone sheet is wide enough for two cards per row (desktop's
   narrow side panel keeps the single column). */
body.is-compact .handle-grid { grid-template-columns: 1fr 1fr; }

/* Viewport tools: off-screen right → thumb-reachable FAB column */
body.is-compact #vp-tools {
  right: 12px; left: auto; top: auto;
  bottom: calc(var(--sheet-peek) + var(--safe-b) + 14px);
  transform: none; flex-direction: column; gap: 8px; z-index: 15;
}

/* Placement chooser → bottom sheet. The desktop card is a 4-up gallery of
   square renders; stacked one-per-row on a phone that's four 350px images, so
   the heading scrolls off the top of a fixed overlay and can't be reached.
   Phone layout is a LIST instead: thumbnail left, name + blurb right, all four
   visible at once, heading pinned and only the list scrolling. */
body.is-compact #type-pop { padding: 0; align-items: flex-end; }
body.is-compact .type-card {
  display: flex;
  flex-direction: column;
  max-height: 92dvh;
  border-radius: 18px 18px 0 0;
  padding: 22px 16px calc(18px + var(--safe-b));
}
body.is-compact .type-card > .pop-close { top: 12px; right: 14px; }
body.is-compact .type-card h2 { font-size: 19px; padding-right: 36px; }
/* flex column stretches its children: keep the back link at its own width so
   the button's centered label doesn't drift to the middle of the sheet */
body.is-compact .type-back { align-self: flex-start; }
body.is-compact .type-intro { margin-bottom: 14px; }

body.is-compact .type-grid {
  grid-template-columns: 1fr;
  gap: 8px;
  /* the list is the only scroller inside the sheet (min-height:0 lets a flex
     child actually shrink); the 2px gutter keeps the active ring from clipping */
  min-height: 0;
  overflow-y: auto;
  margin: -2px;
  padding: 2px;
}
body.is-compact .type-opt {
  display: grid;
  grid-template-columns: 92px 1fr;
  align-items: center;
  gap: 13px;
  padding: 8px;
}
body.is-compact .type-opt img { aspect-ratio: 1; border-radius: 10px; }
body.is-compact .type-txt { min-width: 0; }
body.is-compact .type-opt strong { margin: 0 0 3px; font-size: 14.5px; }
body.is-compact .type-opt span { margin: 0; font-size: 12px; line-height: 1.35; }

/* Follow-up panes (wall side / roof slopes): same list treatment */
body.is-compact .type-sub-grid { grid-template-columns: 1fr; gap: 8px; }
body.is-compact .type-opt.sub {
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 14px;
  padding: 8px 14px;
}
body.is-compact .type-opt.sub .diag { flex: 0 0 auto; width: 68px; height: 54px; }
body.is-compact .type-opt.sub strong { font-size: 14.5px; }
body.is-compact .type-opt.toggle { padding-left: 50px; }
body.is-compact .type-opt.toggle .check { top: 50%; left: 14px; transform: translateY(-50%); }
body.is-compact .type-continue { margin-top: 14px; }

/* Touch: no hover — show the handle's in-context photo by default */
body.is-touch .handle-imgs img.scene { opacity: 1; }
/* Touch: tapped info-tip toggles its tooltip open (see the is-touch click handler) */
.info-tip.tip-open .tip-pop { display: block; }

/* ===================== Touch targets (is-touch) ===================== */
/* Section-count steppers: 28px → 44px */
body.is-touch .step-btn { width: 44px; height: 44px; font-size: 18px; }

/* Floating action chips (comp/hole/front): 30px → 44px, icons a touch bigger */
body.is-touch #comp-chip button,
body.is-touch #hole-chip button,
body.is-touch #front-chip button { width: 44px; height: 44px; }
body.is-touch #comp-chip button svg,
body.is-touch #hole-chip button svg,
body.is-touch #front-chip button svg { width: 22px; height: 22px; }

/* Close buttons (popover ×, snackbar ×): give them a real 44px box */
body.is-touch .pop-close { padding: 10px; font-size: 22px; }
body.is-touch #snackbar button { min-width: 44px; min-height: 44px; }

/* Handle size (steekmaat) pills: ~18px tall → ~40px, still pill-shaped */
body.is-touch .handle-size { padding: 11px 16px; font-size: 13px; }

/* Handle colour chips: 20px, gap 7px → 32px, gap 12px (visual enlarge; a 44px
   invisible overlay would overlap neighbours). 32px + gap is comfortably tappable. */
body.is-touch .handle-fins { gap: 12px; }
body.is-touch .handle-fin { width: 32px; height: 32px; }

/* Dimension slider: 3px track / 20px thumb → grabbable on touch */
body.is-touch .control-row input[type="range"] { height: 12px; border-radius: 6px; }
body.is-touch .control-row input[type="range"]::-webkit-slider-thumb {
  width: 30px; height: 30px;
}
body.is-touch .control-row input[type="range"]::-moz-range-thumb {
  width: 30px; height: 30px; border: 2px solid var(--text); background: var(--surface); border-radius: 50%;
}
/* keep the has-pill variant hiding the thumb (a number pill overlays it) */
body.is-touch .slider-wrap.has-pill input[type="range"]::-webkit-slider-thumb { opacity: 0; }

/* iOS Safari auto-zooms into any focused input whose font is < 16px and does NOT
   zoom back out — that's what left the view cropped/enlarged after editing a
   size. Keep touch inputs at 16px so the zoom never triggers (pinch-zoom stays
   available). The dim fields get a little more width for the larger digits. */
body.is-touch input:not([type="range"]):not([type="checkbox"]):not([type="radio"]) {
  font-size: 16px;
}
body.is-touch .dim-input input { width: 64px; }


/* ============== Wide compact sheet (is-roomy — tablet held upright) ==============
   Same bottom sheet as the phone, but ~1024px across instead of ~390. The phone's
   rules were written against that narrow measure, so left alone they stretch: a
   fixed 3-column palette turns 110px cards into 320px ones, and body copy runs
   the full width of the slab. Cap the content to a readable measure and let the
   card grids fill it by card SIZE rather than by a fixed column count. */
body.is-compact.is-roomy #controls > * {
  max-width: 720px;
  margin-inline: auto;
}
/* The peek-bar spans the slab (it is chrome, not content); its contents align
   to the same measure so "Verder" doesn't drift out to the far corner. */
body.is-compact.is-roomy .sheet-peekbar {
  max-width: 720px;
  margin-inline: auto;
  width: 100%;
}
/* Fill the measure by card width, so the cards stay the size a thumb expects
   and the columns grow instead. 720px measure → 5 columns of ~135px. */
body.is-compact.is-roomy .comp-palette {
  grid-template-columns: repeat(auto-fill, minmax(126px, 1fr));
  gap: 8px;
}
body.is-compact.is-roomy .comp-card span { font-size: 11px; }
body.is-compact.is-roomy .handle-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
body.is-compact.is-roomy .type-grid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
/* Scroll region matched to the shallower roomy detents (bottom-sheet.js:
   half 62dvh, full 34dvh) less the sticky grab + peek-bar header. */
body.is-compact.is-roomy #panel.detent-half #controls { max-height: 33dvh; }
body.is-compact.is-roomy #panel.detent-full #controls { max-height: 58dvh; }
/* The phone's generous trailing scroll slack exists because a phone's browser
   chrome eats the bottom of the sheet. A tablet in portrait has no bottom bar
   and lays each step out in far fewer rows, so that much slack is just void. */
body.is-compact.is-roomy #controls { padding-bottom: calc(var(--safe-b) + 4dvh); }
