/* Kitchen Configurator — design tokens
   Scandinavian/Japandi direction: warm minimalism, generous negative
   space, a single wood accent used sparingly. Hairline dividers instead
   of boxed/shadowed cards; restrained typographic weight reserved for the
   few numbers that matter (grand total, price deltas).

   Palette/type/radius are aligned to landing.html's own Kora Kitchens
   brand tokens (see that file's own :root — --clay/--sage/--font-head
   etc.) rather than kept as this file's original pre-rebrand values: the
   rebrand (commit af63f20) updated the marketing page's colours, fonts
   and favicon/theme-color but never touched this stylesheet, leaving the
   actual tool on an unrelated muted-brown accent and a plain system-serif
   fallback for its own headings. Base.html's favicon/theme-color were
   already Kora's, so the tool was already half-rebranded before this. */

:root {
  /* Palette — mapped 1:1 from landing.html's --bg/--surface/--raised/
     --text/--muted/--divider (all either identical or a few hex steps
     off already, since this file's original warm-cream direction was
     never far from the brand kit that came later). */
  --color-bg: #f7f3ec;
  --color-bg-panel: #efe7d8;
  --color-bg-raised: #fdfbf6;
  --color-text: #33302a;
  --color-text-muted: #6f6656;
  --color-text-faint: #b3aa9c;
  --color-border: #ddd2bd;
  --color-border-strong: #c9bb9f;

  /* Brand accent — Kora Kitchens clay/terracotta (landing.html's
     --clay/--clay-100/--clay-ink), not the old exposed_birch_ply-derived
     brown this token used before the rebrand. */
  --color-accent: #c67139;
  --color-accent-soft: #f3e6dc;
  --color-accent-ink: #201e1d;

  /* Kora sage — landing.html's --sage, close enough to this token's own
     prior value that only a small nudge was needed. */
  --color-positive: #7a8a5e;
  --color-negative: #a1583f;
  --color-negative-bg: #f6ece5;

  /* Type — same three families as landing.html's own stack: Inter for
     body/UI text, Figtree for small-caps/eyebrow labels, Caprasimo for
     display headings (all loaded from Google Fonts in base.html). */
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-strapline: "Figtree", var(--font-sans);
  --font-display: "Caprasimo", Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", serif;

  /* Spacing scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  /* Softened from the original 3px/6px to read closer to landing.html's
     own much rounder cards/buttons, without going as far as its 24-48px
     card radii — this file's rows/fields are far denser than a marketing
     page's sections and stay legible at a moderate radius, not a fully
     rounded one. Buttons (.btn, below) go further and match landing's
     fully-pill buttons directly, since that's the brand's most
     recognisable, safely-copyable shape regardless of surrounding
     density. */
  --radius: 8px;
  --radius-lg: 14px;

  --panel-left-width: 304px;
  --panel-right-width: 328px;
}

* {
  box-sizing: border-box;
}

html, body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-sans);
  font-weight: 500;
  margin: 0;
  color: var(--color-text);
}

a {
  color: inherit;
}

button, input, select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

/* --- App shell: three-panel layout ---------------------------------- */

.app-shell {
  display: grid;
  grid-template-columns: var(--panel-left-width) 1fr var(--panel-right-width);
  height: calc(100vh - 56px);
}

/* Function view (function-view-implementation-prompt.md) takes over the
   whole workspace: partials/workspace.html simply doesn't render
   .panel-left/.panel-right while session.center_view_mode == "function",
   and .panel-center-fullscreen (set on .panel-center in that same
   template) collapses this grid to one full-width column instead of
   panel-center just sitting alone in the grid's first (narrow) track.
   :has() rather than a class on #workspace itself, because every htmx
   swap targeting #workspace uses hx-swap="innerHTML" — the div's own
   class attribute is set once at page load in index.html and never
   touched again by any later swap. */
.app-shell:has(.panel-center-fullscreen) {
  grid-template-columns: 1fr;
}

/* --- Header bar --------------------------------------------------------- */

.app-header {
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  background: var(--color-bg-panel);
  border-bottom: 1px solid var(--color-border);
}

.header-group {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.header-brand {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text);
}

.header-project-name {
  font-size: 12px;
  color: var(--color-text-muted);
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--color-bg-raised);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  padding: 5px 12px;
  font-size: 12.5px;
  white-space: nowrap;
}

.status-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  font-size: 9px;
  color: #fff;
  background: var(--color-positive);
}

.status-badge-negative {
  background: var(--color-negative);
}

.status-text-idle {
  color: var(--color-text-faint);
}

.status-detail {
  color: var(--color-text-muted);
}

.status-pill .status-saving-label {
  display: none;
  color: var(--color-accent);
}

.status-pill.htmx-request .status-saving-label {
  display: inline;
}

.status-pill.htmx-request .status-badge,
.status-pill.htmx-request .status-text,
.status-pill.htmx-request .status-detail {
  display: none;
}

.header-toggle {
  display: inline-flex;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
  overflow: hidden;
}

.header-toggle-segment {
  padding: 5px 10px;
  font-size: 12.5px;
  color: var(--color-text-muted);
  text-decoration: none;
  cursor: pointer;
}

.header-toggle-segment.active {
  background: var(--color-accent);
  color: #faf7f1;
}

#undo-redo-controls {
  display: contents;
}

.checkpoint-picker {
  position: relative;
}

.checkpoint-picker summary {
  list-style: none;
  cursor: pointer;
}

.checkpoint-picker summary::-webkit-details-marker {
  display: none;
}

.checkpoint-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 10;
  width: 260px;
  background: var(--color-bg-raised);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

.checkpoint-save-form {
  display: flex;
  gap: var(--space-2);
}

.checkpoint-save-form input {
  flex: 1;
  min-width: 0;
  border: none;
  border-bottom: 1px solid var(--color-border-strong);
  background: transparent;
  padding: 4px 2px;
  font-size: 13px;
  color: var(--color-text);
  outline: none;
}

.checkpoint-list {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
}

.checkpoint-list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 6px 0;
  border-top: 1px solid var(--color-border);
  font-size: 12.5px;
}

.btn[disabled] {
  color: var(--color-text-faint);
  border-color: var(--color-border);
  background: transparent;
  cursor: not-allowed;
}

.panel {
  overflow-y: auto;
  min-width: 0;
}

.panel-left {
  border-right: 1px solid var(--color-border);
  background: var(--color-bg-panel);
  padding: var(--space-5) var(--space-4);
}

.panel-center {
  padding: var(--space-6) var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.panel-right {
  border-left: 1px solid var(--color-border);
  background: var(--color-bg-panel);
  padding: var(--space-5) var(--space-4);
  display: flex;
  flex-direction: column;
}

/* --- Section labels --------------------------------------------------- */

.section-label {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-3);
}

.hairline {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: var(--space-5) 0;
}

/* --- Run / cabinet tree (left panel) ---------------------------------- */

.run-block {
  margin-bottom: var(--space-6);
}

.run-header {
  all: unset;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-2) 0;
  cursor: pointer;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-2);
}

.run-header.selected {
  border-bottom-color: var(--color-accent);
}

.run-name {
  font-weight: 500;
  font-size: 13px;
}

.run-meta {
  font-size: 11px;
  color: var(--color-text-faint);
}

.cabinet-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.cabinet-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 6px var(--space-2);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--color-text-muted);
}

.cabinet-row:hover {
  background: var(--color-bg-raised);
}

.cabinet-row.selected {
  background: var(--color-bg-raised);
  color: var(--color-text);
  font-weight: 500;
}

.cabinet-row.selected::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--color-accent);
  margin-right: var(--space-2);
}

.cabinet-row-select {
  all: unset;
  cursor: pointer;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reorder-controls {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
}

.reorder-btn {
  all: unset;
  cursor: pointer;
  font-size: 11px;
  line-height: 1;
  padding: 2px 4px;
  border-radius: var(--radius);
  color: var(--color-text-muted);
}

.reorder-btn:hover:not(:disabled) {
  background: var(--color-bg-panel);
  color: var(--color-accent);
}

.reorder-btn:disabled {
  color: var(--color-text-faint);
  cursor: default;
}

.cabinet-row .cab-price {
  font-variant-numeric: tabular-nums;
  color: var(--color-text-faint);
  font-size: 12px;
  flex-shrink: 0;
}

.gap-row {
  padding: 0;
  margin: 2px 0;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius);
}

.gap-row.selected {
  border-color: var(--color-accent);
}

.gap-row-select {
  all: unset;
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 4px var(--space-2);
  cursor: pointer;
  font-size: 12px;
  color: var(--color-text-faint);
  font-style: italic;
}

.gap-row.selected .gap-row-select {
  color: var(--color-text);
}

.kitchen-toolbar {
  margin-bottom: var(--space-4);
}

.link-button {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-text-muted);
  font-size: 12px;
  cursor: pointer;
  text-decoration: none;
  border-bottom: 1px dotted var(--color-text-faint);
}

.link-button:hover {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

/* --- Center panel: visuals + editor ------------------------------------ */

.visual-block {
  background: var(--color-bg-raised);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  padding: var(--space-5);
}

.visual-block-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.elevation-svg, .plan-svg {
  width: 100%;
  height: auto;
  display: block;
}

.cabinet-selection-outline {
  stroke: var(--color-accent);
  stroke-width: 12;
  pointer-events: none;
}

.cabinet-selection-caption {
  font-size: 34px;
  text-transform: uppercase;
  letter-spacing: 2px;
  fill: var(--color-accent);
  text-anchor: start;
}

.gap-label {
  font-size: 24px;
  text-transform: uppercase;
  letter-spacing: 1px;
  fill: var(--color-text-faint);
}

@keyframes cabinet-changed {
  0%   { fill-opacity: 1; }
  15%  { fill-opacity: 0.55; }
  100% { fill-opacity: 1; }
}

.cabinet.changed rect {
  animation: cabinet-changed 900ms ease-out;
}

/* height, not max-height: a max-height on a box whose own `height` is
   otherwise `auto` never becomes a *definite* height for percentage
   resolution purposes (CSS2.1 10.5) — only the box's own painted size
   gets capped. That mismatch was the actual bug here: the SVG child's
   old `height:100%` fell back to resolving against the parent's
   *content-driven* auto height instead, sizing itself purely from the
   available WIDTH and the run's own aspect ratio, with nothing capping
   the result against 46vh at all. For a wide/short run the aspect-ratio
   height always landed comfortably under 46vh regardless, so the bug
   was invisible — but a run whose width-driven height genuinely wanted
   to exceed 46vh (a wide panel and/or a tall/narrow run) rendered taller
   than this box's own painted (max-height-clamped) size, and the excess
   silently vanished under `overflow: hidden` — cutting off the bottom
   of the cabinet, worse the more the two disagreed. A real `height`
   here removes the ambiguity: the box is always exactly 46vh, full
   stop, whatever the content wants. */
.elevation-wrap {
  height: 46vh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Now that the wrap has a real height, size the SVG as a plain "contain"
   fit within it — max-width/max-height (not width/height) on both axes,
   auto/auto so the browser resolves whichever axis is more constraining
   and scales the other down from it via the intrinsic aspect ratio (the
   standard replaced-element sizing algorithm, CSS2.1 10.4). This can
   only ever shrink the run to fit inside the box — never overflow it —
   regardless of how wide/short or narrow/tall its own aspect ratio is,
   so `overflow: hidden` above is now just a defensive backstop, not
   something that should ever actually trigger. */
.elevation-wrap .elevation-svg {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}

.plan-thumb {
  max-width: 220px;
}

.plan-view-toggle {
  margin-bottom: var(--space-5);
}

.plan-view-toggle .plan-thumb-block {
  margin-top: var(--space-4);
}

.plan-run {
  fill: var(--color-accent-soft);
  fill-opacity: 0.5;
  stroke: var(--color-border-strong);
  stroke-width: 12;
}

.plan-run.selected {
  stroke: var(--color-accent);
  stroke-width: 18;
}

.plan-front-indicator {
  stroke: var(--color-text-faint);
  stroke-width: 10;
}

/* --- Left rail: "Style & finish" disclosure --------------------------- */

.rail-style {
  margin-bottom: var(--space-4);
}

.rail-style > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rail-style > summary::-webkit-details-marker {
  display: none;
}

.rail-style-heading {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.rail-style-heading::after {
  content: " \25B8"; /* ▸ */
  color: var(--color-text-faint);
}

.rail-style[open] .rail-style-heading::after {
  content: " \25BE"; /* ▾ */
}

.rail-style-values {
  font-size: 11px;
  line-height: 1.4;
  color: var(--color-text-faint);
}

/* Once expanded the fields themselves are the readout — drop the summary
   line so the header doesn't say the same thing twice. */
.rail-style[open] .rail-style-values {
  display: none;
}

.rail-style-fields {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.rail-style-subhead {
  margin: var(--space-2) 0 0;
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

/* --- Cabinet editor form ------------------------------------------------ */

.editor-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px 24px;
}

@media (max-width: 900px) {
  .editor-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Cabinet editor: one left-aligned column (Label, Cabinet type, Width,
   Depth, Front layout, then any front-layout-dependent fields, then
   Service void), rather than the old 4-up grid. */
.editor-grid-stacked,
.editor-grid-stacked.editor-grid {
  grid-template-columns: minmax(0, 340px);
  gap: 16px;
}

.overridable-field .override-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--color-text-muted);
  text-transform: none;
  letter-spacing: normal;
  cursor: pointer;
}

.overridable-field .override-toggle input {
  width: auto;
  margin: 0;
}

.overridable-field input[type="number"]:disabled {
  color: var(--color-text-faint);
  cursor: not-allowed;
}

.field-note {
  font-size: 11px;
  color: var(--color-text-faint);
  margin-top: 2px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field label {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.field input[type="text"],
.field input[type="password"],
.field input[type="number"],
.field select {
  border: none;
  border-bottom: 1px solid var(--color-border-strong);
  background: transparent;
  padding: 6px 2px;
  font-size: 14px;
  color: var(--color-text);
  outline: none;
  transition: border-color 0.15s ease;
}

.field input:focus,
.field select:focus {
  border-bottom-color: var(--color-accent);
}

.field-hint {
  font-size: 11px;
  color: var(--color-text-faint);
}

.field-error {
  font-size: 11px;
  color: var(--color-negative);
  margin-top: 2px;
}

.field-error:empty {
  margin-top: 0;
  display: none;
}

.field-input-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.field-input-row input,
.field-input-row select {
  flex: 1;
  min-width: 0;
}

.field-marker {
  font-size: 10px;
  white-space: nowrap;
  flex-shrink: 0;
}

.field-marker-negative {
  color: var(--color-negative);
}

.field-marker-positive {
  color: var(--color-positive);
  animation: field-saved-fade 2.5s ease forwards;
}

@keyframes field-saved-fade {
  0% { opacity: 1; }
  70% { opacity: 1; }
  100% { opacity: 0; }
}

/* Driven by the marker span's own class rather than a class on .field
   itself: the marker (and .field-error below) are the only parts of a
   field that ever get hx-swap-oob'd on a debounced commit now — the
   input/select and its .field wrapper are never touched, so a focused
   input's caret position survives its own commit landing. See
   partials/_macros.html's own module docstring. */
.field:has(.field-marker-positive) input,
.field:has(.field-marker-positive) select {
  border-bottom-color: var(--color-accent);
}

.field:has(.field-marker-negative) label {
  color: var(--color-negative);
}

.field:has(.field-marker-negative) input,
.field:has(.field-marker-negative) select {
  color: var(--color-negative);
  border-bottom-color: var(--color-negative);
  background: var(--color-negative-bg);
}

.field-correction {
  font-size: 11px;
  color: var(--color-negative);
  text-decoration: underline;
  border-bottom: none;
}

.field-correction:hover {
  color: var(--color-accent-ink);
}

/* Groups conditionally-visible fields (partials/_macros.html's
   dependent_fields()) without becoming a grid item itself, so its children
   keep participating directly in .editor-grid's own column layout. */
.dependent-fields {
  display: contents;
}

.field.overridden label::after {
  content: " · overridden";
  color: var(--color-accent);
  text-transform: none;
  letter-spacing: 0;
}

.error-banner {
  background: #f3e6de;
  color: var(--color-negative);
  border-left: 2px solid var(--color-negative);
  padding: var(--space-3) var(--space-4);
  font-size: 13px;
  border-radius: var(--radius);
  margin-bottom: var(--space-4);
}

/* --- Buttons ------------------------------------------------------------ */

/* Fully pill-shaped (999px), not --radius — landing.html's own .btn is
   the same shape regardless of everything else on that page being much
   more rounded than this one; a button reads as "the same button" across
   both surfaces this way even though the surrounding density doesn't
   match. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: 1px solid var(--color-border-strong);
  background: var(--color-bg-raised);
  color: var(--color-text);
  padding: 8px 16px;
  font-size: 13px;
  border-radius: 999px;
  cursor: pointer;
  text-decoration: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}

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

.btn-primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
  font-weight: 500;
}

.btn-primary:hover {
  background: var(--color-accent-ink);
  border-color: var(--color-accent-ink);
}

.btn-quiet {
  border: none;
  background: none;
  padding: 4px 8px;
  color: var(--color-text-muted);
}

.btn-quiet:hover {
  color: var(--color-negative);
}

.btn-row {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

/* --- Right panel: pricing ------------------------------------------------ */

.grand-total {
  margin-top: var(--space-2);
}

.grand-total .amount {
  font-size: 30px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  letter-spacing: -0.01em;
}

.pricing-recalc {
  font-size: 11px;
  color: var(--color-accent);
  opacity: 0;
  transition: opacity 0.15s ease;
  margin-left: var(--space-2);
}

.htmx-request .pricing-recalc {
  opacity: 1;
}

.pricing-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.rail-kitchen-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.toggle-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  text-transform: none;
  letter-spacing: normal;
}

.toggle-switch input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.toggle-switch-track {
  position: relative;
  display: inline-block;
  width: 34px;
  height: 18px;
  border-radius: 999px;
  background: var(--color-border-strong);
  transition: background 0.15s ease;
  flex-shrink: 0;
}

.toggle-switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--color-bg-raised);
  transition: transform 0.15s ease;
}

.toggle-switch input:checked + .toggle-switch-track {
  background: var(--color-accent);
}

.toggle-switch input:checked + .toggle-switch-track .toggle-switch-thumb {
  transform: translateX(16px);
}

.toggle-switch input:focus-visible + .toggle-switch-track {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.toggle-switch-label {
  font-size: 11px;
  color: var(--color-text-muted);
  min-width: 52px;
}

.cabinet-price-list {
  margin-top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.price-run-header {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-faint);
  margin-top: var(--space-3);
}

.price-run-header:first-child {
  margin-top: 0;
}

.cabinet-price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 5px 0;
  font-size: 12.5px;
  border-bottom: 1px solid var(--color-border);
}

.cabinet-price-row .name {
  color: var(--color-text-muted);
}

.cabinet-price-row .price {
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

.price-delta {
  font-size: 11px;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: 10px;
  margin-left: var(--space-2);
  /* Fades in only — holds indefinitely once shown, until that cabinet's
     (or the session's) next commit replaces or clears it. No fade-out:
     this used to expire on a fixed 2.6s timer regardless of whether the
     price it described was still current, which meant a delta could
     vanish while its cabinet was still sitting at that changed price, or
     equally could still be fading as a completely unrelated edit landed. */
  animation: delta-in 250ms ease;
}

.price-delta.positive {
  color: var(--color-negative);
  background: color-mix(in srgb, var(--color-negative) 12%, transparent);
}

.price-delta.negative {
  color: var(--color-positive);
  background: color-mix(in srgb, var(--color-positive) 14%, transparent);
}

@keyframes delta-in {
  0% { opacity: 0; transform: translateY(-2px); }
  100% { opacity: 1; transform: translateY(0); }
}

.session-baseline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.cost-breakdown {
  margin-top: var(--space-5);
  font-size: 12px;
  color: var(--color-text-muted);
}

.cost-breakdown-row {
  display: flex;
  justify-content: space-between;
  padding: 3px 0;
}

.transport-tier {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-faint);
}

/* --- Warnings (collapsible, unobtrusive) --------------------------------- */

.warnings {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.warnings summary {
  font-size: 11px;
  color: var(--color-text-faint);
  cursor: pointer;
  list-style: none;
}

.warnings summary::-webkit-details-marker {
  display: none;
}

.warnings summary::before {
  content: "⚠ ";
  opacity: 0.6;
}

.warnings ul {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-4);
  font-size: 11px;
  color: var(--color-text-faint);
}

.warnings li {
  margin-bottom: 4px;
}

/* --- Project settings drawer -------------------------------------------- */

.settings-toggle {
  margin-top: var(--space-5);
}

.settings-drawer {
  margin-top: var(--space-4);
  padding: var(--space-4);
  background: var(--color-bg-raised);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
}

.settings-drawer .field {
  margin-bottom: var(--space-3);
}

/* --- Generate panel ------------------------------------------------------ */

.generate-block {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

.generate-result {
  font-size: 12.5px;
  color: var(--color-text-muted);
  margin-top: var(--space-3);
}

.generate-result .path {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11.5px;
  color: var(--color-text);
}

.pipeline-log {
  margin-top: var(--space-3);
  max-height: 160px;
  overflow-y: auto;
  background: var(--color-text);
  color: var(--color-bg);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11px;
  padding: var(--space-3);
  border-radius: var(--radius);
  white-space: pre-wrap;
}

.pipeline-status {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}

.pipeline-status.done { color: var(--color-positive); }
.pipeline-status.error { color: var(--color-negative); }

@media (max-width: 1100px) {
  .app-shell {
    grid-template-columns: 220px 1fr 300px;
  }
}

/* --- Client view (task 25) ----------------------------------------------- */

.client-shell {
  max-width: 1480px;
  margin: 0 auto;
  padding: 44px 64px 52px;
  display: flex;
  flex-direction: column;
  gap: 44px;
}

.client-headline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
}

.client-headline h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 30px;
  margin-bottom: var(--space-2);
}

.client-headline-description {
  font-size: 15px;
  color: var(--color-text-muted);
  margin: 0;
}

.client-total {
  text-align: right;
  flex-shrink: 0;
}

.client-total .amount {
  font-size: 52px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.client-total-hint {
  font-size: 12px;
  color: var(--color-text-muted);
}

.client-elevation-block {
  padding: var(--space-4) var(--space-5);
}

.client-elevation-wrap {
  height: 320px;
  display: flex;
  justify-content: center;
  overflow: hidden;
}

/* Same "contain" idiom as .elevation-wrap's own override above — see its
   comment. This wrap's own height was already a real fixed 320px (not
   auto+max-height), so the old width:auto/height:100%/max-width:100%
   never actually cropped anything here — the replaced-element algorithm
   correctly re-derives both dimensions together when max-width binds.
   Aligned to max-width/max-height + auto/auto anyway, for the same
   "can only ever shrink to fit, never overflow" guarantee regardless of
   how this rule's own height ever changes in the future. */
.client-elevation-wrap .elevation-svg {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}

.client-columns {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: 56px;
}

.client-getting-list {
  margin-top: var(--space-3);
}

.client-getting-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  font-size: 13.5px;
  border-bottom: 1px solid var(--color-border);
}

.client-getting-width {
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.client-getting-price {
  font-variant-numeric: tabular-nums;
  min-width: 70px;
  text-align: right;
}

.client-materials {
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.client-material-row {
  display: grid;
  grid-template-columns: 34px 1fr;
  grid-template-rows: auto auto;
  column-gap: var(--space-3);
  align-items: center;
}

.client-swatch {
  width: 34px;
  height: 34px;
  border-radius: var(--radius);
  grid-row: 1 / 3;
  border: 1px solid var(--color-border-strong);
}

.client-material-name {
  font-size: 13.5px;
  grid-column: 2;
}

.client-material-role {
  font-size: 11.5px;
  color: var(--color-text-muted);
  grid-column: 2;
}

.client-options {
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.client-option-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-size: 13.5px;
}

.client-option-row.active {
  border-color: var(--color-accent);
}

.client-option-name {
  flex: 1;
}

.client-option-price {
  font-variant-numeric: tabular-nums;
}

.client-footer {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-4);
  font-size: 11.5px;
  color: var(--color-text-muted);
}

.client-footer p {
  margin: 0 0 var(--space-1);
}

@media print {
  .no-print {
    display: none;
  }

  body {
    background: #fff;
  }

  .client-shell {
    padding: 0;
    gap: var(--space-6);
  }

  .visual-block {
    border: none;
    background: none;
    padding: 0;
  }

  .client-getting-row {
    break-inside: avoid;
  }
}

/* --- Login gate ----------------------------------------------------------- */

.login-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg);
}

.login-card {
  width: 320px;
  padding: var(--space-6);
  background: var(--color-bg-raised);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.login-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 22px;
  margin-bottom: var(--space-2);
}

/* --- Design-handoff Phase 5: tree run controls, insert affordance ------- */

.run-control-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: 4px var(--space-3) var(--space-2);
  font-size: 11px;
}

.run-position-readout {
  color: var(--color-text-faint);
  font-variant-numeric: tabular-nums;
}

.run-control-disclosure summary {
  cursor: pointer;
  list-style: none;
}

.run-control-disclosure summary::-webkit-details-marker {
  display: none;
}

.run-position-form,
.run-bulk-form {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.run-bulk-row {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
}

.tree-insert-slot {
  height: 0;
  overflow: visible;
  position: relative;
  list-style: none;
}

.tree-insert-affordance {
  display: none;
  width: 100%;
  border: none;
  background: none;
  color: var(--color-accent);
  font-size: 11px;
  cursor: pointer;
  padding: 2px 0;
  position: relative;
  top: -6px;
}

.tree-insert-slot:hover .tree-insert-affordance {
  display: block;
}

.rail-footer {
  margin-top: auto;
  padding-top: var(--space-3);
}

/* --- Design-handoff Phase 5: elevation header / dimension line --------- */

.elevation-header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.last-change {
  font-size: 11px;
  color: var(--color-accent);
}

.dimension-line {
  text-align: center;
  font-size: 11px;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-2);
}

/* --- Design-handoff Phase 5: estimate rail cost breakdown / sheet yield - */

.cabinet-price-row.selected .name {
  font-weight: 500;
  color: var(--color-text);
}

.cost-breakdown-detail {
  background: var(--color-bg-raised);
  border-radius: var(--radius-lg);
  padding: var(--space-2) var(--space-3);
  margin: 2px 0 var(--space-2);
}

.cost-breakdown-detail .cost-breakdown-row {
  font-size: 11.5px;
  color: var(--color-text-muted);
}

.sheet-yield-list .cost-breakdown-row {
  font-size: 11.5px;
  color: var(--color-text-muted);
}

.sheet-yield-disclosure > summary.sheet-yield-heading {
  list-style: none;
  cursor: pointer;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-3);
}

.sheet-yield-disclosure > summary.sheet-yield-heading::-webkit-details-marker {
  display: none;
}

.sheet-yield-disclosure > summary.sheet-yield-heading::after {
  content: " \25B8"; /* ▸ */
  color: var(--color-text-faint);
}

.sheet-yield-disclosure[open] > summary.sheet-yield-heading::after {
  content: " \25BE"; /* ▾ */
}

.before-generating {
  margin-top: auto;
  padding-top: var(--space-4);
}

.blocking-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.blocking-list li {
  font-size: 11.5px;
  color: var(--color-text-muted);
  padding-left: var(--space-3);
  position: relative;
}

.blocking-list li::before {
  content: "•";
  color: var(--color-accent);
  position: absolute;
  left: 0;
}

/* --- Design-handoff Phase 5: project settings page ----------------------- */

.settings-page {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.settings-page-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.settings-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.settings-group:last-child {
  border-bottom: none;
}

/* --- Setup wizard (section 2a) ------------------------------------------- */

.wizard-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}

.wizard-card {
  width: 440px;
  flex: none;
  background: var(--color-bg-panel);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.wizard-eyebrow {
  font-size: 10px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.wizard-heading {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.wizard-title {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 400;
}

.wizard-card-entry .wizard-title {
  font-size: 21px;
}

/* --- Start page (post-login landing fork) ------------------------------ */

.wizard-card-start {
  width: 720px;
}

.wizard-start-grid {
  gap: var(--space-5);
}

.wizard-start-tile {
  text-decoration: none;
}

.wizard-start-tile-image {
  height: 160px;
}

.wizard-start-tile-label {
  font-size: 15px;
  line-height: 1.4;
}

.wizard-start-tile:hover .wizard-start-tile-label {
  color: var(--color-accent);
}

/* --- Assistant page (Botpress webchat) ---------------------------------- */

.assistant-shell {
  max-width: 720px;
  margin: var(--space-8) auto;
  padding: 0 var(--space-5);
  text-align: center;
}

.assistant-intro .wizard-title {
  margin-bottom: var(--space-2);
}

/* Botpress mounts its webchat inside this element (embeddedChatId); it
   fills the box, so the box is what positions the chat — centred in the
   page, not pinned to a corner. */
.assistant-webchat {
  width: 100%;
  max-width: 460px;
  height: min(70vh, 640px);
  margin: var(--space-6) auto 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-bg-raised);
}

.wizard-subtitle {
  margin: 0;
  font-size: 13px;
  color: var(--color-text-muted);
  line-height: 1.6;
}

.wizard-entry-hero {
  height: 160px;
}

.wizard-entry-actions {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: stretch;
}

.wizard-entry-actions form {
  display: flex;
  justify-content: center;
}

.wizard-skip-link {
  font-size: 12.5px;
}

/* Placeholder for a photo this prototype has no asset pipeline for (see
   README's "Assets" section) — a plain labelled tile, same convention as
   the app's text-glyph icons elsewhere. */
.wizard-tile-image {
  position: relative;
  height: var(--tile-height, 96px);
  border-radius: var(--radius);
  background: var(--color-bg-raised);
  border: 1px solid var(--color-border);
  color: var(--color-text-faint);
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-2);
}

.wizard-step-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 11px;
}

.wizard-step-nav-item {
  color: var(--color-text-faint);
}

.wizard-step-nav-item.done {
  color: var(--color-text-muted);
  text-decoration: none;
  border-bottom: 1px dotted var(--color-text-faint);
}

.wizard-step-nav-item.done:hover {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

.wizard-step-nav-item.current {
  color: var(--color-text);
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.wizard-step-nav-sep {
  color: var(--color-text-faint);
}

.wizard-tile-grid {
  display: grid;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  border: none;
}

.wizard-tile-grid-grid2 {
  grid-template-columns: repeat(2, 1fr);
}

.wizard-tile-grid-grid3 {
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}

.wizard-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
}

.wizard-tile .wizard-tile-image {
  width: 100%;
}

.wizard-tile.selected .wizard-tile-image {
  border: 2px solid var(--color-accent);
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.wizard-tile-check {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-accent);
  color: #faf7f1;
  font-size: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wizard-tile-label {
  font-size: 12.5px;
  color: var(--color-text-muted);
}

.wizard-tile.selected .wizard-tile-label {
  color: var(--color-text);
  font-weight: 500;
}

.wizard-tile-sublabel {
  font-size: 11.5px;
  color: var(--color-text-muted);
}

.wizard-tile-description {
  font-size: 11px;
  color: var(--color-text-muted);
  line-height: 1.4;
}

.wizard-tile-delta {
  font-size: 11px;
  color: var(--color-text-faint);
  font-variant-numeric: tabular-nums;
}

.wizard-tile.selected .wizard-tile-delta {
  color: var(--color-accent);
}

/* Mobile-only (<400px) collapsed caption, see the media query below —
   hidden by default; the query both hides the per-tile description and
   reveals this instead. */
.wizard-tile-caption {
  display: none;
  margin: 0;
  font-size: 12px;
  color: var(--color-text-muted);
}

.wizard-measurement-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.wizard-measurement-row {
  display: flex;
  gap: var(--space-2);
}

.wizard-measurement-row input {
  flex: 1;
  min-width: 0;
}

.wizard-price-footer {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: 13px;
}

.wizard-price-label {
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.wizard-price-value {
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.wizard-breakdown summary {
  font-size: 12px;
}

.wizard-breakdown-rows {
  margin-top: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.wizard-breakdown-row {
  display: flex;
  justify-content: space-between;
  font-size: 11.5px;
  color: var(--color-text-muted);
}

.wizard-breakdown-row span:last-child {
  font-variant-numeric: tabular-nums;
}

.wizard-footer {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.wizard-footer form {
  display: contents;
}

.wizard-link-disabled {
  color: var(--color-text-faint);
  border-bottom-color: transparent;
  cursor: default;
}

/* --- Shape & size step (design_handoff_kora_journey/, screen 1d) -------- */

.wizard-card-shape {
  width: 720px;
}

.shape-diagram {
  display: block;
}

.shape-diagram-sm {
  width: 40px;
  height: 30px;
  flex: none;
}

.shape-diagram-lg {
  width: 100%;
  max-width: 280px;
  height: auto;
}

.wizard-shape-picker {
  display: grid;
  grid-template-columns: 250px 1fr;
  gap: var(--space-5);
  align-items: start;
}

.wizard-shape-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.wizard-shape-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  background: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
}

.wizard-shape-row.selected {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}

.wizard-shape-preview {
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}

.wizard-run-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: 100%;
}

.wizard-run-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.wizard-run-field label {
  font-size: 12.5px;
  color: var(--color-text-muted);
}

.wizard-run-field-row {
  display: flex;
  gap: var(--space-2);
}

.wizard-run-field-row input {
  flex: 1;
  min-width: 0;
}

.wizard-run-field.is-typical .wizard-run-field-row input {
  border-style: dashed;
  color: var(--color-text-muted);
}

.wizard-run-field-chip {
  align-self: flex-start;
  background: none;
  border: none;
  padding: 0;
  font-size: 11px;
  color: var(--color-text-faint);
  cursor: pointer;
  text-decoration: underline;
  text-align: left;
  font-family: inherit;
}

/* --- Handoff summary step (screen 1g) ----------------------------------- */

.wizard-summary-rows {
  display: flex;
  flex-direction: column;
}

.wizard-summary-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px dashed var(--color-border);
}

.wizard-summary-row:last-child {
  border-bottom: none;
}

.wizard-summary-label {
  width: 110px;
  flex: none;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-faint);
}

.wizard-summary-value {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 13px;
}

.wizard-summary-assumed {
  color: var(--color-text-muted);
  font-size: 12px;
}

/* --- Options beyond the standard step (design_handoff_options_beyond_
   standard/, screen 1) — also reused by settings_panel.html's own group
   via the same option_row() macro, single-column there instead of
   two-column. -------------------------------------------------------- */

.wizard-card-options {
  width: 760px;
}

.wizard-options-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 260px);
  gap: var(--space-6);
  align-items: start;
}

.wizard-options-group-label {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-faint);
  margin: var(--space-4) 0 var(--space-2);
}

.wizard-options-group-label:first-child {
  margin-top: 0;
}

.wizard-option-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-bg-panel);
  cursor: pointer;
  margin-bottom: var(--space-2);
}

/* The real checkbox stays in the tab order and reachable by screen
   readers — visually hidden (not display:none, which would drop it from
   the tab order in some browsers), with .wizard-option-checkbox (a plain
   sibling span) as its custom visual. */
.wizard-option-row input[type="checkbox"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.wizard-option-checkbox {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  border-radius: 4px;
  border: 1.5px solid var(--color-border-strong);
  background: var(--color-bg-raised);
  display: flex;
  align-items: center;
  justify-content: center;
  color: transparent;
}

.wizard-option-row input[type="checkbox"]:checked + .wizard-option-checkbox {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

.wizard-option-row-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.wizard-option-row-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text);
}

.wizard-option-tag {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 500;
  background: var(--color-accent-soft);
  color: var(--color-accent-ink);
}

.wizard-option-row-description {
  font-size: 12.5px;
  color: var(--color-text-muted);
  line-height: 1.45;
}

.wizard-options-summary {
  position: sticky;
  top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.wizard-options-summary-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--color-bg-raised);
}

.wizard-options-price-note {
  border: 1px solid var(--color-accent-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--color-bg-raised);
}

.wizard-options-unlocked-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: 12.5px;
  line-height: 1.45;
}

/* Back + Continue grouped on the left, Skip pushed to the far right —
   deliberately not .wizard-footer's own justify-content:space-between
   (that's built for exactly two items; a 3rd item here would just land
   evenly spaced in the middle rather than grouped with the other two). */
.wizard-options-actions {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.wizard-options-skip {
  margin-left: auto;
}

@media (max-width: 640px) {
  .wizard-page {
    padding: 0;
    align-items: stretch;
  }

  .wizard-card {
    width: 100%;
    min-height: 100vh;
    border: none;
    border-radius: 0;
    padding: var(--space-5) var(--space-4);
  }

  /* Both tile grids stay three-across at every breakpoint down to 375px
     (repeat(3, minmax(0,1fr)) — plain 1fr would let the image tiles force
     the tracks open and overflow the card, per README's mobile spec). */
  .wizard-tile-grid-grid3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .wizard-shape-picker {
    grid-template-columns: 1fr;
  }

  .wizard-options-layout {
    grid-template-columns: 1fr;
  }

  .wizard-options-summary {
    position: static;
  }

  /* The step-nav moves into a slim header sticky to the viewport top
     rather than floating inside the card — needed once a tall tile grid
     pushes the rest of the screen below the fold. The negative margins
     pull it flush to the card's own edges (undoing the card's own padding
     just above) so it reads as a full-bleed bar, not an inset one. No
     scrolling ancestor sits between this and the viewport on mobile (the
     card itself no longer scrolls independently), so a plain
     `position: sticky` is enough — no JS. */
  .wizard-step-nav {
    position: sticky;
    top: 0;
    z-index: 2;
    margin: calc(-1 * var(--space-5)) calc(-1 * var(--space-4)) 0;
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-panel);
    border-bottom: 1px solid var(--color-border);
  }

  /* The price footer + back/skip footer share one sticky region at the
     viewport bottom (README: "share one sticky footer... the workspace
     CTA stays inside the scroll so the footer never carries two
     actions") instead of sitting at `margin-top: auto` inside a now-
     unbounded-height card — same full-bleed trick as the step-nav above,
     plus safe-area padding so it clears a device's home indicator. The
     price line keeps its own top border (unchanged from desktop); only
     the group as a whole gets the sticky/full-bleed treatment. */
  .wizard-sticky-footer {
    position: sticky;
    bottom: 0;
    margin: var(--space-5) calc(-1 * var(--space-4)) calc(-1 * var(--space-5));
    padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
    background: var(--color-bg-panel);
    border-top: 1px solid var(--color-border);
  }

  .wizard-price-footer {
    border-top: none;
    padding-top: 0;
  }

  /* Type steps up slightly from desktop, and controls take more vertical
     padding, per README's mobile spec. */
  .wizard-title {
    font-size: 21px;
  }

  .wizard-subtitle,
  .wizard-tile-label,
  .wizard-measurement-row input {
    font-size: 13.5px;
  }

  .btn,
  .wizard-measurement-row input {
    padding-top: 15px;
    padding-bottom: 15px;
  }
}

/* The drawers question's three per-tile descriptions collapse into one
   live caption below the grid, showing only the selected option — the
   tiles themselves keep their name and £ delta regardless. README:
   "Per-tile descriptions collapse below 400px" — deliberately its own,
   narrower breakpoint than the general 640px mobile layout above (the
   size question's short "around 3.6m" captions stay in place at every
   width, so this only ever touches .wizard-tile-description/
   .wizard-tile-caption, both drawers-screen-only). */
@media (max-width: 400px) {
  .wizard-tile-description {
    display: none;
  }

  .wizard-tile-caption {
    display: block;
  }
}

/* Minimum 44x44 tap target on every wizard tile, at every breakpoint — the
   label text is already inside the same <button> as the image, so the tap
   target already includes it; this just guarantees the button itself is
   never shorter than that regardless of a tile's own image height. */
.wizard-tile {
  min-height: 44px;
}

/* --- 3D preview overlay ----------------------------------------------- */

.preview3d-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: rgba(52, 48, 42, 0.28);
}

.preview3d-overlay[hidden] {
  display: none;
}

.preview3d-panel {
  display: flex;
  flex-direction: column;
  width: min(1400px, 94vw);
  height: min(900px, 90vh);
  background: var(--color-bg-raised);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.22);
  overflow: hidden;
}

.preview3d-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  flex: none;
}

.preview3d-bar .section-label {
  margin: 0;
}

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

.preview3d-canvas {
  position: absolute;
  inset: 0;
}

.preview3d-canvas canvas {
  display: block;
}

.preview3d-loading,
.preview3d-error {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: var(--space-3) var(--space-5);
  background: var(--color-bg-raised);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--color-text-muted);
  max-width: 80%;
  text-align: center;
}

.preview3d-error {
  color: var(--color-negative);
  border-color: var(--color-negative);
}

/* --- Function view (function-view-implementation-prompt.md) -----------
   Additive only — every value below is one of this file's own existing
   tokens (--color-*, --space-*, --radius*); no new colour/font/radius
   constants. Structural classes (.visual-block, .section-label, .field,
   .cabinet-list/.cabinet-row, .btn, .link-button, .hairline) are reused
   verbatim from the rest of the app, not redefined here. */

.function-view-mode-toggle {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

/* Function view takes over the whole workspace grid (panel-center-
   fullscreen, see .app-shell in this file) for room to work with anchors/
   items and the plan — capped and centred rather than left to stretch
   edge-to-edge on a wide monitor, where the 1fr/1.6fr/1fr columns below
   would otherwise grow uncomfortably wide for their actual content. */
.function-view-block {
  max-width: 1600px;
  margin: 0 auto;
}

.function-view-columns {
  display: grid;
  grid-template-columns: 1fr 1.6fr 1fr;
  gap: var(--space-6);
  align-items: start;
}

/* Anchor row (function_view.html): badge + name + right-aligned status,
   all on one line — mirrors the design handoff's own mockup (function-
   view-plan.dc.html's anchor rows: a small circular initial badge, the
   label, then the source pushed to the row's far edge). */
.function-view-anchor-button {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

.function-view-anchor-badge {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid var(--color-border-strong);
  font-size: 10px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
}

.cabinet-row.selected .function-view-anchor-badge {
  border-color: var(--color-accent);
  color: var(--color-accent-ink);
}

.function-view-anchor-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.function-view-row-status {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 11px;
  color: var(--color-text-faint);
  font-weight: 400;
}

/* Item row (function_view.html): icon + label on one line, "where" it's
   placed indented underneath on a second line — mirrors the design
   handoff's own mockup (function-view-plan.dc.html's item rows: a
   Phosphor icon + label, then a smaller "where" line indented past the
   icon's own width). */
.function-view-item-button {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

.function-view-item-main {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.function-view-item-main i {
  font-size: 15px;
  opacity: 0.85;
  width: 15px;
  text-align: center;
}

.function-view-item-where {
  font-size: 11px;
  color: var(--color-text-faint);
  padding-left: calc(15px + var(--space-2));
}

.function-view-plan-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}

.function-view-plan-hint {
  font-size: 12px;
  color: var(--color-text-faint);
}

.function-view-plan-header-unit {
  flex-shrink: 0;
  font-size: 12px;
  color: var(--color-text-faint);
}

.function-view-plan-wrap {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  background: var(--color-bg-raised);
}

/* Set by cabinet_workspace.html/function_view.html whenever an anchor is
   armed — a hook for app.js's own click-to-place listener and a visual
   cue that clicking the plan background will now drop a marker there. */
.function-view-plan-wrap[data-armed-anchor]:not([data-armed-anchor=""]) {
  cursor: crosshair;
}

.function-view-cabinet-fronts {
  margin-top: var(--space-5);
}

.function-view-fronts-header {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.function-view-slot-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.function-view-slot-row label {
  text-transform: none;
  letter-spacing: normal;
  font-size: 12.5px;
  color: var(--color-text-muted);
}

/* One bordered card holding the overall score + zone bars — mirrors the
   design handoff's own mockup (function-view-plan.dc.html's score card):
   a plain "{{score}} / 100 · {{rating}}" line, not a colour-coded badge
   pill (the zone bars below it already carry the good/workable/poor
   colour coding). */
.function-view-score-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--color-bg-raised);
}

.function-view-overall {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.function-view-overall-score {
  font-size: 32px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--color-text);
}

.function-view-overall-rating {
  font-size: 13px;
  color: var(--color-text-muted);
}

.function-view-zone-field {
  margin-bottom: var(--space-2);
}

.function-view-zone-bar-track {
  background: var(--color-bg-panel);
  border-radius: var(--radius);
  height: 8px;
  overflow: hidden;
}

.function-view-zone-bar-fill {
  height: 100%;
  transition: width 0.15s ease;
}

.function-view-zone-bar-good {
  background: var(--color-positive);
}

.function-view-zone-bar-workable {
  background: var(--color-accent);
}

.function-view-zone-bar-poor {
  background: var(--color-negative);
}

.function-view-finding-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: var(--space-2);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--color-text-muted);
}

.function-view-finding-row i {
  flex-shrink: 0;
  font-size: 14px;
  margin-top: 1px;
}

.function-view-finding-row:hover {
  background: var(--color-bg-panel);
  color: var(--color-text);
}

.function-view-finding-fail {
  color: var(--color-negative);
}

.function-view-finding-warn {
  color: var(--color-accent-ink);
}

/* --- Function view v2 (Kitchen function and workflow analysis (3)) -----
   First-run explainer, guided setup, the on-demand report launcher and
   its full-width report section. Additive only, existing tokens only —
   same rule as the rest of this file's own Function view section. */

.function-view-intro-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-raised);
  padding: var(--space-4);
  margin-bottom: var(--space-5);
}

.function-view-intro-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.function-view-setup-card {
  border: 1px solid var(--color-accent);
  border-radius: var(--radius);
  background: var(--color-accent-soft);
  padding: var(--space-3);
  margin-bottom: var(--space-5);
}

.function-view-setup-eyebrow {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent-ink);
}

.function-view-setup-title {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--color-text);
  margin-top: 2px;
}

.function-view-setup-actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.function-view-row-dimmed {
  opacity: 0.45;
}

.function-view-row-dimmed .cabinet-row-select {
  cursor: default;
}

.function-view-reopen-link {
  margin-left: var(--space-2);
  font-size: 11px;
  text-transform: none;
  letter-spacing: normal;
}

.function-view-report-launcher {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg-raised);
  padding: var(--space-4);
}

.function-view-report-launcher-notrun .btn {
  margin-top: var(--space-3);
  width: 100%;
}

.function-view-report-stale-note {
  font-size: 11px;
  color: var(--color-accent-ink);
  margin-top: var(--space-2);
}

.function-view-report-launcher-actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.function-view-report-section {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

.function-view-report-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-5);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
}

.function-view-report-zones {
  display: flex;
  gap: var(--space-5);
  flex-wrap: wrap;
}

.function-view-report-zones .function-view-zone-field {
  min-width: 100px;
  margin-bottom: 0;
}

.function-view-report-body {
  display: grid;
  grid-template-columns: minmax(240px, 320px) 1fr;
  gap: var(--space-6);
  align-items: start;
}

.function-view-scenarios-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(240px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-3);
}

.function-view-scenario-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg-raised);
  padding: var(--space-3);
}

.function-view-scenario-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 13px;
  color: var(--color-text);
}

.function-view-scenario-plan {
  margin-top: var(--space-3);
}

.function-view-scenario-stat {
  font-size: 12px;
  color: var(--color-text-muted);
  margin-top: var(--space-2);
}

.function-view-scenario-stat-incomplete {
  color: var(--color-accent-ink);
}

@media (max-width: 900px) {
  .function-view-columns {
    grid-template-columns: 1fr;
  }

  .function-view-report-body {
    grid-template-columns: 1fr;
  }

  .function-view-scenarios-grid {
    grid-template-columns: 1fr;
  }
}

/* --- Plan SVG (webapp/svg_render.py's render_plan()) -------------------
   Previously dead code with zero real callers, so these elements had no
   styling at all before the Function view started using it — default
   SVG fill (solid black) would be unusable. */

.plan-cabinet {
  fill: var(--color-bg-raised);
  stroke: var(--color-border-strong);
  stroke-width: 12;
  cursor: pointer;
}

.plan-cabinet:hover {
  stroke: var(--color-accent);
}

.plan-cabinet.selected {
  stroke: var(--color-accent);
  stroke-width: 18;
}

.plan-gap {
  stroke: var(--color-border-strong);
  stroke-width: 12;
  stroke-dasharray: 30 20;
  cursor: pointer;
}

.plan-gap.selected {
  stroke: var(--color-accent);
  stroke-width: 18;
}

.plan-front-indicator {
  stroke: var(--color-text-faint);
  stroke-width: 8;
}

/* The row-spanning "rail" line along each run's front edge — see
   render_plan()'s own rails/rail_defs (its stroke is a per-run
   <linearGradient>, already fading at both ends, so only stroke-width is
   set here). This is what "lines appear between the cabinets in plan
   view" refers to (function-view-plan.dc.html's own two row-divider
   lines) — a single line completing the run, not a border added to every
   individual cabinet (each already has its own polygon stroke). */
.plan-row-rail {
  stroke-width: 6;
}

.plan-entry-label {
  font-size: 28px;
  fill: var(--color-text-muted);
}

.plan-anchor-marker {
  stroke: var(--color-bg-raised);
  stroke-width: 8;
}

.plan-anchor-label {
  font-size: 26px;
  font-weight: 600;
  fill: var(--color-accent-ink);
}

.plan-measure-label {
  font-size: 24px;
  font-weight: 600;
}

/* --- Scenario mini-plan SVG (webapp/svg_render.py's render_scenario_
   plan()) — a small, non-interactive illustration inside each scenario
   card, styled the same additive/token-only way as .plan-* above. */

.scenario-plan-entry {
  fill: var(--color-bg-panel);
  stroke: var(--color-border);
  stroke-width: 10;
}

.scenario-plan-line {
  stroke: var(--color-accent);
  stroke-width: 10;
  stroke-dasharray: 20 14;
}

.scenario-plan-stop {
  fill: var(--color-accent);
  stroke: var(--color-bg-raised);
  stroke-width: 8;
}

.scenario-plan-stop-number {
  font-size: 60px;
  font-weight: 600;
  fill: #fff;
}

/* --- Pricing admin (webapp/templates/partials/pricing_admin_panel.html) -
   The admin Pricing view's per-cabinet-type cards — additive only,
   existing tokens only, same discipline as every other section in this
   file. No dedicated amber/warn token exists in this codebase (see
   svg_render.py's own WARN_LINE_COLOR comment) — --color-accent is the
   established stand-in, used here for the amber RAG pill too. */

.pricing-type-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

.pricing-type-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

.pricing-type-name {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--color-text);
}

.pricing-comparators {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-3) 0;
}

.rag-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 12px;
  padding: 3px 10px;
  border-radius: 999px;
  color: #fff;
}

.rag-pill-green {
  background: var(--color-positive);
}

.rag-pill-amber {
  background: var(--color-accent);
}

.rag-pill-red {
  background: var(--color-negative);
}

.pricing-match-button {
  color: inherit;
  text-decoration: underline;
  font-size: 11px;
}

.pricing-margin-field {
  margin-top: var(--space-3);
}

.pricing-margin-field input[type="range"] {
  width: 100%;
  accent-color: var(--color-accent);
}
