:root {
  --bg: #0f1419;
  --panel: #1a2332;
  --panel-2: #243044;
  --border: #2d3f56;
  --text: #e8eef6;
  --muted: #8fa3b8;
  --accent: #3b82f6;
  --accent-2: #22c55e;
  --warn: #f59e0b;
  --danger: #ef4444;
  --door: #a855f7;
  --strip: #06b6d4;
  --coping: #14b8a6;
  --tool-yellow: #facc15;
  --tool-indigo: #818cf8;
  --room-fill: rgba(59, 130, 246, 0.12);
  --hit: 48px;
  --radius: 10px;
  --shadow: 0 8px 24px rgba(0,0,0,.35);
  --canvas-bg: #0f1419;
  --header-bg: linear-gradient(180deg, #1c2738, #152032);
  --tabs-bg: #0d121a;
  font-family: "Segoe UI", system-ui, sans-serif;
}

/* Light theme ONLY — does not touch dark defaults */
html[data-theme="light"] {
  --bg: #f8fafc;
  --panel: #ffffff;
  --panel-2: #f1f5f9;
  --border: #cbd5e1;
  --text: #0f172a;
  --muted: #64748b;
  --accent: #2563eb;
  --accent-2: #16a34a;
  --warn: #d97706;
  --danger: #dc2626;
  --door: #7c3aed;
  --strip: #0891b2;
  --coping: #0d9488;
  --tool-yellow: #eab308;
  --tool-indigo: #4f46e5;
  --room-fill: rgba(37, 99, 235, 0.08);
  --shadow: 0 8px 24px rgba(15, 23, 42, 0.1);
  --canvas-bg: #ffffff;
  --header-bg: linear-gradient(180deg, #ffffff, #f1f5f9);
  --tabs-bg: #e2e8f0;
}

.app-header {
  background: var(--header-bg);
  border-bottom: 1px solid var(--border);
}

.tabs {
  background: var(--tabs-bg);
  border: 1px solid var(--border);
}

.tool-rail,
.props-panel {
  background: var(--panel);
  border-color: var(--border);
  color: var(--text);
}

.canvas-wrap,
#floorCanvas {
  background: var(--canvas-bg);
}

/* Inputs stay readable in both themes */
.props-panel input,
.props-panel select,
.props-panel textarea {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
}

.help-box {
  background: var(--panel-2);
  color: var(--muted);
  border: 1px solid var(--border);
}

/* 2026-09-05 — direct user request: "Make the theme circle small like the
   icons" — was 36px (bigger than the 30px icon buttons around it in the
   header); now matches that same 30px sizing. */
.theme-toggle {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--border);
  padding: 0;
  cursor: pointer;
  background: conic-gradient(from 90deg, #0f172a 0deg 180deg, #f8fafc 180deg 360deg);
  flex-shrink: 0;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

#app {
  display: grid;
  grid-template-rows: 56px 1fr 44px;
  height: 100%;
}

/* 2026-09-05 — direct user request: while a room's walls are actively
   being drawn (the wall-polygon-in-progress state — see canvas-engine.js's
   syncRoomDrawingUI()), both the top header and the floating toolbar
   ("both menus") disappear, and the bottom status bar goes with them, so
   the full canvas area is visible right down to where the status bar
   normally sits. The two side panels (Product/Service, tool rail,
   Properties) and the tabs bar are untouched — only the top/bottom
   "menus" the user pointed at hide.
   First attempt zeroed out #app's own header/footer grid tracks
   (grid-template-rows: 0 1fr 0) — that measurably broke layout instead:
   Chrome computed .workspace's own height as 0px despite its 1fr track
   correctly resolving to the full 900px (confirmed directly via
   getComputedStyle, not just visually), an actual grid-stretch quirk
   worth remembering rather than a typo. Fixed by leaving #app's row
   sizes alone entirely and instead making .workspace SPAN all 3 rows
   (grid-row: 1 / 4) while drawing — a spanning item gets the combined
   size of every track it spans regardless of what its hidden siblings'
   own tracks were sized for, which is the standard, reliable way to let
   one grid item take over the space vacated by display:none siblings. */
body.drawing-room .workspace {
  grid-row: 1 / 4;
}
body.drawing-room .app-header,
body.drawing-room .status-bar,
body.drawing-room .floating-toolbar {
  display: none !important;
}

/* Top chrome */
.app-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 12px;
  background: linear-gradient(180deg, #1c2738, #152032);
  border-bottom: 1px solid var(--border);
  z-index: 20;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 180px;
}

.brand-mark {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, #3b82f6, #8b5cf6);
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 14px;
}

.brand h1 {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .2px;
}

.brand small {
  display: block;
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
}

.tabs {
  display: flex;
  align-items: center;
  gap: 4px;
  background: #0d121a;
  padding: 4px;
  border-radius: 10px;
  border: 1px solid var(--border);
}

/* 2026-09-05 — direct user request: "Make th name of the Floorplan same
   text size as the floating [toolbar]" and separately "The cut list and
   quote should be much smaller text and close together no huge button -
   lets see if I can get everything in the top menu." .tab covers all
   three (the floorplan-name tab, Cut List, Quote), so shrinking it here
   tightens all of them together, matching the ~11px/tighter sizing
   already used for the floating toolbar's own buttons above. */
.tab {
  min-height: 30px;
  min-width: 64px;
  padding: 0 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-weight: 700;
  font-size: 11px;
  cursor: pointer;
}

/* 2026-09-06 — small floorplan icon ahead of each floorplan tab's real
   name (see canvas-engine.js's renderTabs()) — direct user request to
   turn the floorplan tab into "an image", balanced against still being
   able to tell Lounge/Kitchen/B3/etc apart when a project has several. */
.fp-tab-icon {
  font-size: 11px;
  opacity: .85;
  margin-right: 2px;
}

/* An icon-only tab (currently just Quote) needs to drop .tab's own
   min-width:64px — min-width always wins over .btn-icon's width:30px
   when the two conflict, so without this override an icon-only tab
   would still take up a full text-tab's width despite showing only a
   single glyph. */
.tab.btn-icon {
  min-width: 30px;
  padding: 0;
}

.tab.active {
  background: var(--accent);
  color: white;
  box-shadow: 0 2px 8px rgba(59,130,246,.4);
}

.header-actions {
  margin-left: auto;
  display: flex;
  gap: 4px;
  align-items: center;
  flex-wrap: wrap;
}

/* 2026-09-05 — direct user request: "menu items" (the top header's own
   action buttons, and the floating toolbar's buttons below) much tighter,
   smaller text, tighter box. Scoped to these two "menus" specifically
   rather than the global .btn, so tool-rail/props-panel/quote buttons
   elsewhere in the app (sized for touch, per the Properties panel's own
   "Tablet tips") are untouched. */
.header-actions .btn,
.floating-toolbar-body .btn {
  min-height: 30px;
  min-width: 30px;
  padding: 0 8px;
  font-size: 11px;
  border-radius: 6px;
  gap: 4px;
}

/* 2026-09-05 — keep icon-only buttons genuinely square with a
   legible-sized glyph even inside the floating toolbar / header, where
   the tighter ".btn" rule above (higher specificity, so it would
   otherwise win) sets a text-oriented 0 8px padding + 11px font that
   left icon glyphs small and off-center. */
.header-actions .btn-icon,
.floating-toolbar-body .btn-icon {
  padding: 0;
  font-size: 15px;
}

/* Icon-only header buttons (Undo/Redo/Settings/Underlay/Import/Export) —
   no text label, so no horizontal padding is needed; keep them square. */
.btn-icon {
  width: 30px;
  padding: 0;
  font-size: 15px;
  line-height: 1;
}

.btn {
  min-height: 40px;
  min-width: 40px;
  padding: 0 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.btn:active { transform: scale(.98); }
.btn.primary { background: var(--accent); border-color: #2563eb; color: #fff; }
.btn.success { background: #15803d; border-color: #16a34a; color: #fff; }
.btn.danger { background: #7f1d1d; border-color: #b91c1c; color: #fff; }
.btn.ghost { background: transparent; }
.btn:disabled { opacity: .45; cursor: not-allowed; }


/* Add to app.css or inside a <style> tag */
.field input[type="number"] {
  display: block;
  width: 100% !important;
  margin-bottom: 8px !important;
  box-sizing: border-box;
}

.field .btn.primary {
  display: block;
  width: 100% !important;
  margin-top: 0 !important;
}
/* Workspace */
.workspace { position: relative; overflow: hidden; }

.tab-panel {
  position: absolute;
  inset: 0;
  display: none;
}

.tab-panel.active { display: block; }

/* 2026-08-27 — the REAL bug behind "the Tarkett summary is missing/has never
   been there". It never was a missing-content bug in the Cut List report
   itself: groupJobsByProduct()'s per-vinyl Presorted Totals box AND the
   per-vinyl "Sorted by room" stack report (cut-list-engine.js) were both
   already rendering correctly for every vinyl, confirmed by the jsdom
   regression suite AND by inspecting the live #cutlistContent DOM directly
   in a real headless Chromium (116KB of HTML, all 8 rooms, both vinyls'
   "Presorted Totals" and "Sorted by room" boxes present).
   The user's own "Arkins_results.pdf" (a real File > Print > Save as PDF of
   the live app) proved something WAS wrong — but reproducing that exact
   flow (driving the real app.js/cut-list-engine.js through a real Chromium
   print pipeline, not just reading the DOM) showed the PDF truncates to a
   single page, cut off partway through the very FIRST room (Lounge) —
   before Kitchen/B3/Room 5/J1, let alone Tarkett. That matches the user's
   PDF byte-for-byte in shape.
   Root cause: html/body are `height:100%; overflow:hidden` and each
   .tab-panel is `position:absolute; inset:0` with its content wrapper set
   to `height:100%; overflow:auto` (index.html's cutlist tab). That's
   correct for the interactive on-screen scrolling view, but there was no
   @media print override anywhing in this file, so Chrome's print engine
   paginates the ACTUAL on-screen viewport box (one page's worth), not the
   full scrollHeight of the report — everything past the first screenful is
   silently clipped, with no visual indication in the exported PDF that
   anything was cut off. Every fix this session was real and correct; the
   user just had no way to see past page 1 whenever they printed/exported,
   which is exactly why they kept reporting the same vinyl's summary as
   "still missing" even after it was verified fixed and visible on-screen.
   Fix: when printing, un-pin the active tab panel from absolute
   positioning and let every ancestor's height resolve to its natural
   content height instead of being capped to the viewport, so Chrome's
   print pagination sees (and paginates across as many pages as needed) the
   FULL report, not just the first screenful. */
@media print {
  html, body {
    height: auto !important;
    overflow: visible !important;
  }
  #app {
    height: auto !important;
  }
  .workspace {
    overflow: visible !important;
  }
  .tab-panel {
    position: static !important;
  }
  .tab-panel.active {
    display: block !important;
  }
  /* The Cut List tab's own scrollable content wrapper (index.html) — this
     is the specific box that was clipping the report to one viewport. */
  #tab-cutlist > div {
    height: auto !important;
    overflow: visible !important;
  }
}

/* Floorplan layout – Product panel | Tool rail | Canvas | Props */
.floorplan-layout {
  display: grid;
  grid-template-columns: 240px 72px 1fr 300px;
  height: 100%;
  transition: grid-template-columns 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.floorplan-layout.panel-closed {
  grid-template-columns: 240px 72px 1fr 0px;
}

.floorplan-layout.product-closed {
  grid-template-columns: 0px 72px 1fr 300px;
}

.floorplan-layout.product-closed.panel-closed {
  grid-template-columns: 0px 72px 1fr 0px;
}

.tool-rail {
  background: var(--panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 6px;
  overflow-y: auto;
}

.tool-btn {
  width: 100%;
  min-height: 56px;
  border-radius: 10px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  line-height: 1.1;
}

.tool-btn .icon { font-size: 18px; }
.tool-btn.active {
  background: rgba(59,130,246,.18);
  border-color: var(--accent);
  color: #fff;
}

/* 2026-09-06 — direct user request: distinct, professional colors for
   specific tool-rail icons instead of every icon inheriting the same
   plain --muted gray. Each rule targets only that one tool's icon (or, for
   Scale/Void, the SVG replacing the old plain-text glyph so the color can
   apply to specific strokes rather than the whole character) — every other
   tool-rail icon is untouched until the rest of this redesign is done. */
.tool-btn[data-tool="divide"] .icon { color: var(--danger); }
.tool-btn[data-tool="void"] .icon svg { color: var(--danger); }
.tool-btn[data-tool="measure"] .icon svg { color: var(--tool-yellow); }
.tool-btn[data-tool="scale-underlay"] .icon svg { overflow: visible; }

/* Second batch of the same redesign — category colors for the rest of the
   rail, reusing this app's existing semantic vars wherever the category
   already has one (Door/--door, Strip/--strip, Coving/--coping,
   Trim/--warn) so a tool's rail icon matches its color everywhere else in
   the app (wall labels, chips, etc.), plus two new neutral "utility"
   colors (--tool-indigo for the two pan/select-adjacent tools) for tools
   with no existing semantic color. Select/Wall (the two primary drawing
   tools) get --accent to read as the "start here" pair. */
.tool-btn[data-tool="select"] .icon { color: var(--accent); }
.tool-btn[data-tool="wall"] .icon { color: var(--accent); }
.tool-btn[data-tool="door"] .icon svg { color: var(--door); }
.tool-btn[data-tool="strip"] .icon svg { color: var(--strip); }
.tool-btn[data-tool="coping"] .icon svg,
.tool-btn[data-tool="coping-section"] .icon svg { color: var(--coping); }
.tool-btn[data-tool="trim-wall"] .icon svg,
.tool-btn[data-tool="trim-room"] .icon svg,
.tool-btn[data-tool="trim-freeform"] .icon svg,
.tool-btn[data-tool="trim-void"] .icon svg { color: var(--warn); }
.tool-btn[data-tool="pin"] .icon svg { color: var(--danger); }
.tool-btn[data-tool="pan"] .icon svg,
.tool-btn[data-tool="marquee"] .icon { color: var(--tool-indigo); }

.canvas-wrap {
  position: relative;
  background:
    radial-gradient(circle at 20% 10%, rgba(59,130,246,.08), transparent 40%),
    #0b1017;
  overflow: hidden;
  min-height: 0;
  min-width: 0;
  height: 100%;
}

#floorCanvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: crosshair;
  touch-action: none;
}

/* NEW: TOGGLE PANEL BUTTON */
.product-toggle-btn {
  position: absolute;
  left: 0;
  top: 16px;
  width: 28px;
  height: 48px;
  z-index: 20;
  border: 1px solid var(--border);
  border-left: none;
  border-radius: 0 8px 8px 0;
  background: var(--panel);
  color: var(--muted);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.product-toggle-btn:hover {
  color: #fff;
  background: rgba(59,130,246,.25);
  border-color: var(--accent);
}
.floorplan-layout.product-closed .product-panel {
  overflow: hidden;
  border: none;
  padding: 0;
}
.panel-toggle-btn {
  position: absolute;
  right: 0;
  top: 16px;
  width: 28px;
  height: 48px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-right: none;
  border-radius: 8px 0 0 8px;
  color: var(--muted);
  cursor: pointer;
  z-index: 20;
  font-weight: bold;
  font-size: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
  box-shadow: -3px 0 10px rgba(0,0,0,0.15);
}

.panel-toggle-btn:hover {
  color: var(--text);
  background: var(--panel-2);
}

.chip {
  background: rgba(15,20,25,.88);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5px 11px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
  backdrop-filter: blur(6px);
  white-space: nowrap;
}

.chip.accent { border-color: var(--accent); color: #93c5fd; }
.chip.ok { border-color: var(--accent-2); color: #86efac; }

/* HUD chips now live in the status bar, right-justified, instead of
   floating over the canvas — frees up the whole top of the canvas for
   the Cutlines toolbar. */
.status-bar-hud {
  margin-left: auto;
  display: flex;
  gap: 6px;
  align-items: center;
  overflow-x: auto;
  max-width: 60%;
}
.status-bar-hud::-webkit-scrollbar { height: 4px; }
.status-bar-hud::-webkit-scrollbar-thumb { background: #334155; border-radius: 2px; }

/* 2026-09-05 — direct user request: this is "the other floating menu" —
   moved up flush under the top header (was 12px down, and stretched
   right:12px edge-to-edge — dropped that so it's a compact draggable
   panel with its own intrinsic width, not a full-width bar), made
   draggable via the head strip's grip, and given a chevron to collapse
   it down to just that head strip. See app.js's
   initFloatBarDragAndCollapse() for the drag/collapse wiring, which also
   persists position + collapsed state in localStorage. left/top below
   are just the pre-drag default — a drag overwrites them as inline
   styles once the user has moved it. */
.floating-toolbar {
  position: absolute;
  /* left starts past the pre-existing "«" Product-panel toggle button
     (.product-toggle-btn: left:0, width:28px, z-index:20 — higher than
     this toolbar's own z-index below) rather than at the canvas edge —
     confirmed via a real drag test that the two otherwise overlap, and
     the toggle button (being higher z-index) always wins the click,
     silently swallowing any attempt to drag the toolbar's head from its
     leftmost pixels. */
  left: 40px;
  top: 4px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: rgba(26,35,50,.95);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 6px;
  box-shadow: var(--shadow);
  z-index: 6;
  max-width: fit-content;
}

.floating-toolbar-head {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  cursor: move;
  user-select: none;
  padding: 0 2px;
  touch-action: none;
}
/* Deliberately NOT space-between: the head stretches to match the body's
   own (potentially wide, ~20-button) content width, and a first attempt
   using space-between put the chevron at the far right of that width —
   which measurably collided with the pre-existing Properties-panel
   toggle button (#btnTogglePanel) anchored to the canvas's right edge.
   Keeping grip+chevron adjacent on the left keeps both controls near
   where the toolbar actually starts, regardless of body width. */

/* 2026-09-06 — direct user request: a real "9 dots" grip icon (3x3 grid
   of dots), replacing the previous single ⠿ character — a font glyph
   that doesn't reliably render/read as a drag handle across platforms.
   Built from 9 actual elements (see index.html) so it's pixel-identical
   everywhere rather than dependent on a font's glyph coverage. */
.floating-toolbar-grip {
  display: inline-grid;
  grid-template-columns: repeat(3, 3px);
  grid-template-rows: repeat(3, 3px);
  gap: 2px;
  cursor: move;
  padding: 2px;
  flex-shrink: 0;
}
.grip-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--muted);
}

.floating-toolbar-chevron {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 11px;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
  min-height: 22px;
  min-width: 22px;
  line-height: 1;
}
.floating-toolbar-chevron:hover { background: rgba(255,255,255,.1); color: var(--text); }

.floating-toolbar-body {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-width: min(78vw, 900px);
  overflow-x: auto;
}
/* ROUND 4 follow-up (2026-09-06) — direct user request: "do not expand
   buttons like V and H etc keep them small." A wrapped flex row with
   only 1-2 items on a given line (V/H, the grain-group, either T-Seam
   button once shortened below) had nothing stopping the browser from
   giving those items a share of the row's leftover width — every button
   in this toolbar is meant to size to its own content, in every dock
   state, never grow to fill a row. flex:0 0 auto pins that globally
   rather than patching it dock-by-dock. */
.floating-toolbar-body > * {
  flex: 0 0 auto;
}

/* Collapsed: only the head strip (grip + chevron) shows. */
.floating-toolbar.collapsed .floating-toolbar-body { display: none; }
.floating-toolbar.collapsed { max-width: fit-content; }

/* 2026-09-05 — magnetic dock-to-edge (see initFloatBarDragAndCollapse in
   app.js). While dragging, getting close to the tool-rail's right edge or
   the Properties panel's left edge highlights the toolbar (the "magnet"
   about to grab it); once dropped there, it gets a steady accent border
   so it's clear it's now locked to that edge rather than just parked
   nearby. */
.floating-toolbar.dock-hover {
  box-shadow: 0 0 0 2px var(--accent), var(--shadow);
}
.floating-toolbar.docked {
  border-color: var(--accent);
}

/* 2026-09-06 — direct user request: the floating toolbar now docks into
   the TOP tab row by default (immediately right of the Quote icon), a
   third dock target alongside the existing tool-rail/props-panel edges.
   Actual left/top are set inline via JS (app.js's applyDockedPosition)
   since this sits in the header, above the toolbar's normal canvas-wrap
   containing block — position:fixed lets it do that regardless of where
   #floatBar lives in the DOM. Per the user's own phrasing ("have the 9
   dots to move it and the minimise in the same line as the items"), the
   grip+chevron sit inline with the button row here rather than stacked
   above it — achieved purely by switching the OUTER container to a row
   (the head and body are themselves already flex rows, so placing them
   side by side needs no DOM changes). */
.floating-toolbar.docked-top {
  position: fixed;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  max-width: none;
  z-index: 25; /* above the header's own controls it now sits beside */
  /* 2026-09-06 (4th round) — direct user report: "still not aligned - ...
     see where the box starts and duplicate the settings." Measured the
     real computed box model of #appTabs [data-tab="cutlist"] (the .tab
     class, app.css line ~203) versus this element live: the TAB is
     padding:0 10px, border:0, min-height:30px, box-sizing:border-box —
     giving it an exact 30px outer height with nothing eaten by padding
     or border. This element's base .floating-toolbar rule (further up
     this file) carries a 6px padding + 1px border on all sides, and this
     block was overriding padding to 4px 8px — meaning even after
     topDockTarget() aligned the two elements' TOP edges exactly (app.js),
     this box's own padding+border (2*(4px+1px) = 10px of vertical space)
     made its BOTTOM edge hang 10px lower than the tab's, the literal
     "still not aligned" being reported. Duplicating the tab's own values
     directly — border:0, padding matching the tab's 0-vertical/10px-
     horizontal — removes that 10px discrepancy entirely: both boxes are
     now exactly 30px tall with nothing but content inside them, so
     top-aligning them (already done in app.js) now ALSO bottom-aligns
     them, for real, without needing two different anchor formulas. */
  border: 0;
  padding: 0 10px;
  min-height: 30px;
}
.floating-toolbar.docked-top .floating-toolbar-head {
  padding: 0;
}
.floating-toolbar.docked-top .floating-toolbar-body {
  flex-wrap: nowrap;
  max-width: none;
  overflow-x: auto;
}

/* 2026-09-06 — direct user request: "When we drag on the left and the
   right and soft magnet lock left and right should show the menu icons
   vertically just like the [tool rail's own] menu items." Switches the
   button row to a column stack (still every existing button/id/handler
   unchanged — only the flex direction of their shared container).
   ROUND 4 (2026-09-06 follow-up) — this rule now covers docked-LEFT only;
   docked-RIGHT was redesigned below into a wide row-wrap panel instead of
   a squeezed single column (see the docked-right block further down). Two
   direct user fixes folded in here:
   - "if it runs oyut of space to show icons it must adda scroll bar just
     like the [tool rail's] Select (v) Wall (w) etc" — the tool rail
     itself just uses a plain overflow-y:auto (see .tool-rail above), so
     this matches that rather than inventing custom scrollbar chrome.
     (Previously this rule had a max-height with NO overflow-y — content
     past that height was silently clipped rather than scrollable, a real
     bug fixed here.)
   - "the 9 dot and hide arrow totally inline with the icons... I want to
     use as least amount of space as possible" — .floating-toolbar-head
     switches to flex-direction:column too when docked-left, so grip sits
     directly above the chevron sits directly above the first icon, one
     continuous vertical list with no separate "header block" feel,
     mirroring how the tool rail is just one plain list with no header. */
.floating-toolbar.docked-left .floating-toolbar-body {
  flex-direction: column;
  flex-wrap: nowrap;
  max-width: none;
  max-height: min(70vh, 640px);
  overflow-y: auto;
  overflow-x: hidden;
  /* 2026-09-06 (3rd round) — direct user report, confirmed by an actual
     screenshot of this exact dock state: "every button cannot keep
     expanding so the 'V', 'H' T+ and T- must be the small single boxes do
     not expand like all of the other icons." The ROUND 4 follow-up's
     `flex: 0 0 auto` rule (further up this file) only stops buttons
     growing along the MAIN axis of a wrapped row (fixed the right dock) —
     it does nothing for a COLUMN container's CROSS axis, where the
     default align-items:stretch was still forcing every text button
     without its own explicit width (V, H, T+, T−, the "Show cuts" row) to
     stretch to the full column width. flex-start makes every item size to
     its own content instead, matching how they already look in every
     other dock state ("like the default horizontal"). */
  align-items: flex-start;
}
.floating-toolbar.docked-left .floating-toolbar-head {
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0;
}
/* 2026-09-06 (4th round) — direct user report: "The NSEW on the vertical
   is showing the 4 together I don't want that have them on speperate
   lines." Confirmed live: in the left (vertical) dock, N/E/S/W render as
   one horizontal row (the grain-group's own internal row layout, used
   everywhere else this toolbar appears) sharing a single line of the
   column. Overridden here — ONLY for the vertical/left dock — to stack
   N, E, S, W as 4 separate column rows, matching how every other button
   in this dock already gets its own line. Left as a normal horizontal
   row everywhere else (top/right/bottom docks), since those weren't part
   of this complaint and a horizontal row reads fine with real width to
   spread into. */
.floating-toolbar.docked-left .grain-group {
  flex-direction: column;
  align-items: flex-start;
}

/* ROUND 4 (2026-09-06) — direct user request: "On the right hand side it
   is to lock in under the properties chron[header] and expand fully out
   to the right not just bunched up." Previously docked-right reused the
   exact same narrow single-column layout as docked-left, squeezed into
   the thin gap between the canvas and the Properties panel's left edge.
   This redesigns it to sit UNDER the Properties panel's own header
   (.props-header) and span that panel's full width — structurally now
   much closer to the top-dock's own row-wrap layout than to the left
   dock's narrow column (see applyDockedPosition('right') in app.js,
   which now sets position:fixed + an explicit width here, the same
   coordinate-space trick the top dock already uses to escape
   canvas-wrap's overflow:hidden). Because it now has real width to work
   with (the panel is ~300px), the body wraps icons across multiple rows
   instead of forcing everything into one cramped column — closer to
   "the default horizontal" the user asked the grip/chevron to always
   match regardless of orientation, since a wrapped row keeps them inline
   for free (no separate head/column treatment needed here, unlike
   docked-left above).
   Trade-off flagged for the user: because this sits UNDER the panel's
   header as a position:fixed overlay, it visually sits on top of
   whatever Properties fields happen to be scrolled to that spot in
   .props-body underneath, rather than pushing that content down. This is
   the same visual model the top dock already uses over the header, and
   the toolbar can still be collapsed (chevron) or dragged elsewhere if a
   field underneath needs to be seen.
   ROUND 4 follow-up (2026-09-06) — direct user report: the toolbar was
   visibly landing "into the properties section" (covering the Project
   Name / Primary Room Name fields more than expected). Two things
   compounded that: (1) the outer container was flex-direction:row, so
   the head strip sat BESIDE the body, stealing ~40px of the panel's own
   width away from the body's wrapping — head now sits ABOVE the body
   instead (outer switches to column; the head itself stays row so
   grip+chevron remain inline with each other, same "least space"
   principle as everywhere else), so the body gets the panel's full width
   to wrap into and needs fewer rows overall. (2) the two T-Seam buttons'
   long labels ("+T-Seam"/"−T-Seam") were each forcing their own full-
   width row — shortened to "T+"/"T−" in index.html, which lets them sit
   inline with their neighbours like every other button instead. Combined
   with the new flex:0 0 auto rule above (V/H no longer inflate to fill a
   row), this measurably shortens the panel's overall height, though with
   the Properties panel's own fields starting only ~15-20px below the
   header, some overlap with Project Name specifically is still likely
   whenever more than 2-3 rows of buttons are showing — worth the user's
   own look at the real result before calling this fully resolved. */
.floating-toolbar.docked-right {
  position: fixed;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 6px 8px;
  z-index: 25;
}
.floating-toolbar.docked-right .floating-toolbar-head {
  padding: 0;
  flex-shrink: 0;
}
.floating-toolbar.docked-right .floating-toolbar-body {
  flex-direction: row;
  flex-wrap: wrap;
  max-width: none;
  max-height: min(60vh, 480px);
  overflow-y: auto;
  overflow-x: hidden;
}

/* ROUND 4 (2026-09-06) — new 4th magnet-dock zone, direct user request:
   "at the bottom it is to magnet in on the bottom left." Unlike the top
   dock, canvas-wrap's own BOTTOM edge already lives inside its clipped
   coordinate space (nothing above it to escape), so — like left/right —
   this stays position:absolute and wrap-relative rather than needing the
   position:fixed trick. Row layout (matching the top dock's own
   "least space, everything inline" row) since it tucks into a corner
   rather than hugging a full vertical edge. */
.floating-toolbar.docked-bottom {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  max-width: none;
}
.floating-toolbar.docked-bottom .floating-toolbar-head {
  padding: 0;
}
.floating-toolbar.docked-bottom .floating-toolbar-body {
  flex-direction: row;
  flex-wrap: nowrap;
  max-width: min(78vw, 900px);
  overflow-x: auto;
}

/* Live cut-list preview — mirrors the Cut List tab's "blue bordered"
   diagram right on the Floorplan canvas, so you can see how a room's cuts
   will pack onto the roll before Applying / locking them into the report.
   Sits along the BOTTOM of the canvas so it never overlaps the HUD/toolbar
   at the top. */
.live-cut-preview {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
  max-height: 260px;
  overflow-y: auto;
  background: rgba(255,255,255,0.97);
  border: 2px solid var(--accent);
  border-radius: 12px;
  padding: 12px 40px 12px 12px;
  box-shadow: var(--shadow);
  z-index: 7;
  font-size: 12px;
}
.live-cut-preview::-webkit-scrollbar { width: 8px; }
.live-cut-preview::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
.live-cut-preview-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  min-width: 26px;
  min-height: 26px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(15,23,42,0.08);
  color: #334155;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 8;
}
.live-cut-preview-close:hover {
  background: rgba(239, 68, 68, 0.18);
  color: #ef4444;
}

.props-panel {
  background: var(--panel);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}

.props-header {
  padding: 14px 14px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.props-header h2 {
  font-size: 14px;
  font-weight: 700;
}

.props-header p {
  color: var(--muted);
  font-size: 12px;
  margin-top: 4px;
}

.props-body {
  padding: 12px;
  overflow-y: auto;
  flex: 1;
}

.field {
  margin-bottom: 12px;
  white-space: nowrap;
}

.field label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-bottom: 6px;
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 42px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #0f1621;
  color: var(--text);
  padding: 8px 10px;
  font-size: 14px;
}

.field textarea { min-height: 80px; resize: vertical; }

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.segmented {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
}

.seg-btn {
  min-height: 40px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #0f1621;
  color: var(--muted);
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
}

.seg-btn.active {
  background: rgba(59,130,246,.2);
  border-color: var(--accent);
  color: #fff;
}

.catalog-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.catalog-item {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 8px;
  background: #0f1621;
  text-align: center;
  cursor: pointer;
  min-height: 72px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
}

.catalog-item.active,
.catalog-item:hover {
  border-color: var(--accent);
  color: #fff;
  background: rgba(59,130,246,.12);
}

.catalog-item .sym {
  font-size: 18px;
  line-height: 1;
}

.help-box {
  background: #121a26;
  border: 1px dashed var(--border);
  border-radius: 10px;
  padding: 10px;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.45;
  white-space: normal;
}

.help-box strong { color: var(--text); }

.list-block {
  margin-top: 8px;
}

.list-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 6px;
  background: #0f1621;
  font-size: 12px;
  cursor: pointer;
}

.list-item.active { border-color: var(--accent); }
.list-item .meta { color: var(--muted); }

/* Status bar */
.status-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 14px;
  background: #0c1118;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
  overflow: hidden;
  white-space: nowrap;
}

.status-bar strong { color: var(--text); font-weight: 700; }

/* Toast */
.toast-host {
  position: fixed;
  right: 16px;
  bottom: 52px;
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.toast {
  min-width: 220px;
  max-width: 360px;
  background: #111827;
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: 10px;
  padding: 12px 14px;
  box-shadow: var(--shadow);
  font-size: 13px;
  animation: slideIn .2s ease;
}

.toast.ok { border-left-color: var(--accent-2); }
.toast.err { border-left-color: var(--danger); }

@keyframes slideIn {
  from { transform: translateX(20px); opacity: 0; }
  to { transform: none; opacity: 1; }
}

@media (max-width: 980px) {
  .floorplan-layout {
    grid-template-columns: 0px 64px 1fr;
  }
  .panel-toggle-btn {
      display: none;
  }
  .props-panel {
    position: absolute;
    right: 0;
    top: 0;
    bottom: 0;
    width: min(320px, 92vw);
    z-index: 15;
    transform: translateX(105%);
    transition: transform .2s ease;
    box-shadow: var(--shadow);
  }
  .props-panel.open { transform: none; }
  .brand { min-width: auto; }
  .brand small { display: none; }
  .tab { min-width: 84px; padding: 0 10px; }
}

/* 2026-09-05 — direct user request: "make the + and - (add remove
   floorplan) smaller and have the vertical under each other to save
   room use an icon". .tab-add-group stacks the two icon buttons one
   above the other (was: two full tab-height buttons sitting side by
   side, inline with the tabs themselves) so the pair together take up
   roughly one tab's worth of width instead of two. */
.tab-add-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  justify-content: center;
}
.tab-add {
  min-height: 14px;
  height: 14px;
  min-width: 22px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--muted);
  font-weight: 700;
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  overflow: visible;
}
.tab-add:hover {
  background: rgba(255,255,255,0.1);
  color: #fff;
}

/* Light theme form controls */
html[data-theme="light"] input,
html[data-theme="light"] select,
html[data-theme="light"] textarea {
  background: #ffffff !important;
  color: #0f172a !important;
  border: 1px solid #cbd5e1 !important;
}

html[data-theme="light"] input::placeholder {
  color: #94a3b8;
}

html[data-theme="light"] .catalog-item {
  background: #ffffff;
  color: #0f172a;
  border-color: #cbd5e1;
}

html[data-theme="light"] .catalog-item.active {
  background: #eff6ff;
  border-color: var(--accent);
}

html[data-theme="light"] .seg-btn {
  background: #ffffff;
  color: #0f172a;
  border: 1px solid #cbd5e1;
}

html[data-theme="light"] .seg-btn.active {
  background: var(--accent);
  color: #ffffff;
  border-color: var(--accent);
}

html[data-theme="light"] .help-box {
  background: #f8fafc;
  color: #475569;
  border: 1px solid #e2e8f0;
}

html[data-theme="light"] .btn:not(.primary):not(.danger) {
  background: #ffffff;
  color: #0f172a;
  border: 1px solid #cbd5e1;
}

html[data-theme="light"] .props-panel label {
  color: #475569;
}

/* Brand always light on the dark header bar */
.app-header .brand h1 {
  color: #f8fafc !important;
}

.app-header .brand small,
.app-header #brandProject {
  color: #94a3b8 !important;
}

/* HUD chips – dark pill, white text (readable in the status bar) */
.status-bar-hud .chip {
  background: rgba(15, 23, 42, 0.85) !important;
  color: #f8fafc !important;
  border: 1px solid rgba(255, 255, 255, 0.12);
}

.status-bar-hud .chip.accent {
  background: rgba(37, 99, 235, 0.9) !important;
  color: #ffffff !important;
}

.status-bar-hud .chip.ok {
  background: rgba(22, 163, 74, 0.9) !important;
  color: #ffffff !important;
}

/* 2026-09-05 — direct user request: "move the + and - sign off the
   moveable menu and make it a magnify icon + and - at the bottom menu to
   the left of the 1 px = 10 mm scale... as well as fit (use an icon)".
   Reuses the same #btnZoomIn/#btnZoomOut/#btnZoomFit ids the JS already
   wires up (app.js), just relocated + restyled as a compact pill that
   matches the HUD chips around it, sitting first in #statusBarHud so it
   lands immediately to the left of #hudScale. */
.zoom-controls {
  display: flex;
  align-items: center;
  gap: 2px;
  background: rgba(15, 23, 42, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  padding: 2px 6px;
  flex-shrink: 0;
}
.zoom-icon {
  font-size: 12px;
  line-height: 1;
  margin-right: 2px;
  opacity: .85;
}
.zoom-btn {
  min-height: 20px;
  height: 20px;
  min-width: 20px;
  width: 20px;
  padding: 0;
  font-size: 12px;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: #f8fafc;
}
.zoom-btn:hover { background: rgba(255,255,255,.15); }
#btnZoomFit { font-size: 13px; }


/* ═══════════════════════════════════════════════════
   PRODUCT PANEL (left) – Measure Square style
   ═══════════════════════════════════════════════════ */
.product-panel {
  background: var(--panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
  height: 100%;
}

.product-panel-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.product-panel-header h3 {
  font-size: 13px;
  font-weight: 700;
  flex: 1;
  white-space: nowrap;
  margin: 0;
}

.product-search {
  width: calc(100% - 16px);
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--text);
  font-size: 12px;
  margin: 6px 8px;
  box-sizing: border-box;
}

.product-tree {
  flex: 1;
  overflow-y: auto;
  padding: 4px 0 8px;
  font-size: 12px;
}

.prod-node { user-select: none; }

.prod-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding-right: 8px;
  cursor: pointer;
  border-radius: 4px;
  margin: 1px 4px;
}

.prod-row:hover {
  background: rgba(59, 130, 246, 0.12);
}

.prod-row.active {
  background: rgba(59, 130, 246, 0.22);
  outline: 1px solid var(--accent);
}

.prod-row.folder {
  font-weight: 600;
  color: var(--text);
}

.prod-row.leaf {
  font-weight: 500;
  color: var(--text);
}

.prod-chevron,
.prod-chevron-spacer {
  width: 14px;
  flex-shrink: 0;
  text-align: center;
  font-size: 10px;
  color: var(--muted);
}

.prod-swatch {
  width: 12px;
  height: 12px;
  border-radius: 2px;
  flex-shrink: 0;
  border: 1px solid rgba(0,0,0,0.25);
}

.prod-swatch-inline {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 4px;
  vertical-align: middle;
  border: 1px solid rgba(0,0,0,0.2);
}

.prod-label {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.prod-unit {
  color: var(--muted);
  font-weight: 400;
  font-size: 11px;
}

.project-items-section {
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  max-height: 200px;
  display: flex;
  flex-direction: column;
}

.project-items-header {
  padding: 6px 10px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--muted);
  background: var(--panel-2);
}

.project-items-list {
  overflow-y: auto;
  padding: 4px 0;
  font-size: 12px;
}

.project-item-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  min-height: 26px;
}

.project-item-row:hover {
  background: rgba(59, 130, 246, 0.08);
}

.project-item-name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.project-item-usage {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 11px;
  flex-shrink: 0;
}

.project-items-empty {
  padding: 12px 10px;
  color: var(--muted);
  font-size: 12px;
  text-align: center;
}

#hudActiveProduct {
  max-width: 130px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 1100px) {
  .floorplan-layout {
    grid-template-columns: 200px 64px 1fr 280px;
  }
}


/* Project Items – per-room headings */
.project-items-room-head {
  padding: 6px 10px 2px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--accent);
  margin-top: 4px;
}
.project-items-category-head {
  padding: 4px 10px 2px 18px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--muted);
  margin-top: 2px;
}
.project-items-total-head {
  border-top: 1px solid var(--border);
  margin-top: 8px;
  padding-top: 8px;
  color: var(--text);
}
.project-item-total {
  font-weight: 600;
}
.project-item-total .project-item-usage {
  color: var(--text);
  font-weight: 700;
}


/* Project Items resize handle */
.project-items-section {
  min-height: 100px;
  height: 200px;
  max-height: 55%;
  resize: none;
}
.project-items-header {
  cursor: ns-resize;
  user-select: none;
}
.project-items-resize {
  position: absolute;
  left: 0;
  right: 0;
  top: -4px;
  height: 8px;
  cursor: ns-resize;
}
.project-items-resize::after {
  content: '';
  display: block;
  width: 36px;
  height: 3px;
  margin: 2px auto 0;
  border-radius: 2px;
  background: var(--border);
}
.project-items-header:hover .project-items-resize::after {
  background: var(--accent);
}


/* Project item remove (trash) */
.project-item-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.project-item-trash {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  margin-left: 2px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: var(--text-muted, #94a3b8);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.55;
}
.project-item-row:hover .project-item-trash {
  opacity: 1;
}
.project-item-trash:hover {
  background: rgba(239, 68, 68, 0.18);
  color: #ef4444;
  opacity: 1;
}


/* Region anchor compass (N/E/S/W) */
.region-compass {
  display: grid;
  grid-template-columns: 36px 36px 36px;
  grid-template-rows: 36px 36px 36px;
  gap: 4px;
  width: max-content;
  margin: 8px auto;
  user-select: none;
}
.compass-btn {
  width: 36px;
  height: 36px;
  border: 1px solid rgba(148, 163, 184, 0.35);
  border-radius: 8px;
  background: rgba(30, 41, 59, 0.9);
  color: #f8fafc;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
.compass-btn:hover {
  background: rgba(56, 189, 248, 0.25);
  border-color: #38bdf8;
  color: #e0f2fe;
}
.compass-btn:active {
  background: rgba(56, 189, 248, 0.45);
}
.compass-n { grid-column: 2; grid-row: 1; }
.compass-w { grid-column: 1; grid-row: 2; }
.compass-e { grid-column: 3; grid-row: 2; }
.compass-s { grid-column: 2; grid-row: 3; }
.compass-hub {
  grid-column: 2;
  grid-row: 2;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(148, 163, 184, 0.25);
  background: rgba(15, 23, 42, 0.6);
}


.region-gap-grid {
  display: grid;
  grid-template-columns: 56px 72px;
  gap: 6px 8px;
  align-items: center;
  margin: 8px 0;
}
.region-gap-grid label {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-align: left;
  opacity: 0.9;
}
.region-gap-grid input[type="number"] {
  width: 72px;
  min-width: 72px;
  max-width: 72px;
  padding: 4px 6px;
  font-size: 12px;
}
.compass-btn.active {
  background: rgba(56, 189, 248, 0.45);
  border-color: #38bdf8;
  color: #fff;
  box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.35);
}


/* Settings gear + modal */
.settings-gear {
  min-width: 36px;
  font-size: 18px;
  padding: 0 10px;
}
.settings-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.55);
  z-index: 2000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.settings-modal {
  width: min(560px, 100%);
  max-height: min(90vh, 820px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 20px 50px rgba(0,0,0,.45);
}
/* 2026-09-01 — the password prompt shown in place of the full Settings
   modal (see settings.js's showPasswordPrompt) reuses .settings-modal but
   is much shorter content, so it gets its own narrower max-width rather
   than stretching to the full 560px settings width. */
.settings-modal-narrow { width: min(360px, 100%); }
.settings-pw-error {
  color: var(--danger, #ef4444);
  font-size: 12px;
  margin-top: 8px;
}
.settings-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.settings-header h2 {
  margin: 0;
  font-size: 16px;
}
.settings-body {
  padding: 12px 16px 20px;
  overflow-y: auto;
  flex: 1;
}
.settings-sec {
  margin-bottom: 18px;
}
.settings-sec h3 {
  margin: 0 0 8px;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.85;
}
.settings-sec label {
  display: block;
  font-size: 12px;
  margin: 6px 0 4px;
  opacity: 0.9;
}
.settings-sec select,
.set-inp {
  width: 100%;
  max-width: 100%;
  background: var(--input-bg, #0f1621);
  color: inherit;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 13px;
}
.set-hint {
  font-size: 12px;
  opacity: 0.7;
  margin: 0 0 8px;
}
/* 2026-09-01 — Billing details textareas (Settings > Quoting) reuse
   .set-inp for width/border/background but need their own resize + font
   behaviour, since that base rule was written for <input>/<select> only. */
.settings-sec textarea.set-inp {
  resize: vertical;
  font-family: inherit;
  line-height: 1.4;
}
.set-table-wrap {
  max-height: 220px;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.set-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.set-table th,
.set-table td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.set-table th {
  position: sticky;
  top: 0;
  background: var(--panel);
  z-index: 1;
}
.set-table .set-inp {
  padding: 4px 6px;
  font-size: 12px;
}
/* 2026-09-05 — Settings > Colours tab: a colour swatch + its hex value
   sitting side by side need fixed widths, not the table's usual
   full-width .set-inp default. */
.set-colour-swatch-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
}
.set-table .set-colour-swatch {
  width: 36px;
  height: 28px;
  flex: 0 0 auto;
  padding: 2px;
  cursor: pointer;
}
.set-table .set-colour-hex {
  width: 84px;
  flex: 0 0 auto;
  font-family: monospace;
  text-transform: uppercase;
}
.set-colour-cat {
  opacity: 0.7;
  text-transform: capitalize;
}
.settings-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
}

/* 2026-09-01 — Settings tab bar (General / Quoting / Vinyl & Cutting),
   added when the Quoting price lists made one long scrolling modal body
   impractical. .settings-tabbtn reuses .btn's own sizing/border so it
   matches every other button in the modal; only the active/inactive
   background differs, mirroring .tab/.tab.active's own pattern on the
   main app header without literally sharing that class (this bar is
   rebuilt fresh every openModal() call — see settings.js's wireModal). */
.settings-tabs {
  display: flex;
  gap: 6px;
  padding: 0 16px 10px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.settings-tabbtn.active {
  background: var(--accent);
  border-color: #2563eb;
  color: #fff;
}
.settings-tabpage { display: none; }
.settings-tabpage.active { display: block; }


/* Cut line toolbar on floorplan — above Fit/Snap bar, not under it */
.cut-toolbar {
  position: fixed;
  bottom: 72px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2000;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: rgba(15, 23, 42, 0.98);
  border: 1px solid #f97316;
  border-radius: 12px;
  box-shadow: 0 10px 28px rgba(0,0,0,0.55);
  max-width: min(920px, 96vw);
}
.cut-toolbar .btn {
  font-size: 13px;
  padding: 7px 12px;
  border-radius: 7px;
  border: 1px solid #475569;
  background: #1e293b;
  color: #f1f5f9;
  cursor: pointer;
  white-space: nowrap;
}
.cut-toolbar .btn:hover { background: #334155; }
.cut-toolbar .btn.active,
.cut-toolbar .btn.primary.active {
  background: #f97316;
  border-color: #fb923c;
  color: #0f172a;
  font-weight: 700;
}
.cut-toolbar .btn.primary {
  background: #c2410c;
  border-color: #f97316;
  color: #fff;
}
.cut-toolbar-label {
  font-size: 13px;
  font-weight: 800;
  color: #fb923c;
  margin-right: 4px;
}
.cut-toolbar-hint {
  font-size: 11px;
  color: #cbd5e1;
  margin-left: 4px;
}

#btnToggleCutlines.active {
  background: #c2410c;
  border-color: #f97316;
  color: #fff;
  font-weight: 700;
}
#hudCutlines.active {
  background: rgba(249, 115, 22, 0.25);
  border-color: #f97316;
  color: #fdba74;
}

#btnCutVert.active, #btnCutHoriz.active, #btnCutEdit.active {
  background: #c2410c;
  border-color: #f97316;
  color: #fff;
  font-weight: 700;
}

/* T-bar toolbar buttons — same active-state treatment as the cut-line
   buttons above (#btnCutVert / #btnCutHoriz / #btnCutEdit). These live in
   the .floating-toolbar / #floatBar container, NOT .cut-toolbar, so the
   generic ".cut-toolbar .btn.active" rule never matched them — that
   mismatch is why +T-Bar / -T-Bar never visually highlighted when armed
   even though the JS was already toggling the "active" class correctly. */
#btnTBarAdd.active, #btnTBarDel.active {
  background: #c2410c;
  border-color: #f97316;
  color: #fff;
  font-weight: 700;
}

/* 2026-09-06 (3rd round) — direct user report: "the 'N','E','S','W' ...
   not bunched together." The group's internal 2px gap was noticeably
   tighter than the 6px gap used everywhere else between buttons in this
   toolbar, so N/E/S/W read as one merged pill rather than 4 distinct
   buttons — most visible in the right dock's narrower wrapped rows.
   Matched to the standard 6px gap so it looks like 4 ordinary buttons
   "like the horizontal" everywhere else, not a cramped sub-widget. */
.grain-group { display: inline-flex; gap: 6px; }
.grain-btn { min-width: 28px; font-weight: 700; }
.grain-btn.active {
  background: #0369a1 !important;
  border-color: #38bdf8 !important;
  color: #fff !important;
}

.grain-btn.grain-disabled {
  opacity: 0.45;
  filter: grayscale(0.6);
  /* still clickable so user can switch mode via E/W or N/S */
  pointer-events: auto;
  cursor: pointer;
}
#btnApplyCuts {
  background: #15803d !important;
  border-color: #22c55e !important;
  color: #fff !important;
  font-weight: 700;
}
#btnApplyCuts:hover {
  background: #16a34a !important;
}

/* ============================================================
   Quote tab — Materials/Labour tables (2026-09-01 redesign)
   Direct user request, attached a "modern invoice" template as a style
   reference: too much visual gap between items in that reference, its own
   + button sat under the last row instead of at the top of the section,
   and its rows had a clean read-only look by default with a proper
   Edit/Save/Delete icon workflow. Ported that interaction pattern onto
   this app's OWN already-working Quote tab (quote-engine.js's
   renderSectionTable/app.js's data-quote-* wiring) rather than adopting
   the reference's dark theme — the Quote tab has always deliberately
   stayed a plain light "document" regardless of the app's own dark/light
   toggle (see quote-engine.js's own header comment), and that's the
   correct look to keep, just tightened and re-skinned.
   A row defaults to a clean, read-only-looking display (line._editing
   falsy — see quote-engine.js/app.js) with an Edit (pencil) button; click
   it to reveal live inputs and a Save (check) button. Delete (minus) is
   always present. Every input still fires the exact same data-quote-idx/
   data-quote-field events app.js already listens for — this is a purely
   visual lock, not a second data path. ============================ */

.quote-section {
  margin-bottom: 18px;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  overflow: hidden;
}

.quote-section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  background: #f8fafc;
  border-bottom: 1px solid #e2e8f0;
}

.quote-section-head h3 {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  color: #0f172a;
  letter-spacing: 0.2px;
}

.quote-table-wrap { overflow-x: auto; }

.quote-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  color: #1e293b;
}

.quote-table-head th {
  text-align: left;
  padding: 6px 10px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: #64748b;
  border-bottom: 1px solid #e2e8f0;
  font-weight: 600;
}

.quote-td {
  padding: 5px 10px;
  border-bottom: 1px solid #f1f5f9;
  vertical-align: middle;
}

.quote-row:last-of-type .quote-td { border-bottom: none; }
.quote-row:hover { background: #f8fafc; }

/* 2026-09-01 — direct user request: the "$" in front of the Rate input
   was wrapping onto its own line above the number, throwing that row's
   height (and its visual alignment with the Total column) off from every
   other row. white-space:nowrap keeps the "$" and its input on one line
   no matter how the column itself sizes; the column can grow to fit
   rather than wrapping. */
/* 2026-09-01 — widened further after a follow-up screenshot showed a
   two-decimal Rate value ("11.56") getting its last digit clipped: an
   <input> is a replaced element with a fixed box, so once its own inline
   width plus the "$" prefix exceeded the column's old 78px, the excess
   just got clipped rather than reflowing — nowrap alone (added just
   above, previous round) fixed the two-line wrap but not this clipping.
   Both the column and the Qty/Rate <input> widths themselves (see
   quote-engine.js) are now wide enough for real rate values like
   "$999.99" without truncating. */
.quote-td-num, .quote-table-head th.quote-td-num { text-align: right; width: 104px; white-space: nowrap; }
.quote-td-unit, .quote-table-head th.quote-td-unit { text-align: center; width: 68px; }
.quote-td-total, .quote-table-head th.quote-td-total { text-align: right; width: 90px; font-variant-numeric: tabular-nums; }
.quote-td-actions, .quote-table-head th.quote-td-actions { text-align: center; width: 64px; white-space: nowrap; }

.quote-item-wrap { display: flex; align-items: center; gap: 6px; min-width: 160px; }
.quote-item-name { flex: 1; min-width: 0; }

.quote-tag {
  flex: none;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  padding: 1px 6px;
  border-radius: 999px;
}
.quote-tag-auto { color: #64748b; background: #f1f5f9; }
.quote-tag-manual { color: #2563eb; background: #eff6ff; }

.quote-cell-input {
  border: 1px solid #cbd5e1;
  background: #fff;
  color: #1e293b;
  border-radius: 5px;
  padding: 3px 6px;
  font-size: 13px;
  font-family: inherit;
}
.quote-cell-input:focus { outline: none; border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.15); }

/* Locked (default/read-only-looking) state — same "looks like plain text,
   isn't interactive" treatment as the reference template's row-readonly
   class, applied to whichever cells this app already makes editable. */
.quote-cell-input.quote-locked {
  border-color: transparent;
  background: transparent;
  color: #1e293b;
  pointer-events: none;
  padding: 3px 2px;
}
select.quote-cell-input.quote-locked { -webkit-appearance: none; appearance: none; }
.quote-row-locked .quote-td-num,
.quote-row-locked .quote-td-unit { color: #1e293b; }

.quote-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid transparent;
  background: transparent;
  cursor: pointer;
  padding: 0;
  margin: 0 1px;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.quote-icon-edit { color: #2563eb; border-color: #bfdbfe; }
.quote-icon-edit:hover { background: #2563eb; color: #fff; }
.quote-icon-save { color: #16a34a; border-color: #bbf7d0; }
.quote-icon-save:hover { background: #16a34a; color: #fff; }
.quote-icon-delete { color: #dc2626; border-color: #fecaca; }
.quote-icon-delete:hover { background: #dc2626; color: #fff; }
.quote-icon-add {
  width: 26px; height: 26px;
  color: #fff;
  background: #2563eb;
  border-color: #2563eb;
}
.quote-icon-add:hover { background: #1d4ed8; }

.quote-table-foot { background: #f8fafc; font-weight: 600; }
.quote-td-footlabel { text-align: right; color: #334155; }
.quote-empty { color: #94a3b8; padding: 14px 10px; text-align: center; }
/* ═══════════════════════════════════════════════════════════════════════
   SETTINGS — LANDSCAPE / RESIZABLE WINDOW (2026-09-11)
   The full Settings window opens at the width of the live canvas area.
   settings.js supplies the exact runtime left/top/max dimensions because
   Product / Service and Properties panels can open/close dynamically.
   The password prompt keeps its original compact layout.
   ═══════════════════════════════════════════════════════════════════════ */
.settings-modal:not(.settings-modal-narrow) {
  box-sizing: border-box;
  width: min(1180px, calc(100vw - 24px));
  height: min(720px, calc(100vh - 100px));
  min-width: min(680px, calc(100vw - 24px));
  min-height: min(420px, calc(100vh - 100px));
  resize: both;
  overflow: hidden;
  position: relative;
}

/* Visible bottom-right resize cue. Chrome still supplies the real native
   resize hit-target; this merely makes it obvious where to drag. */
.settings-modal:not(.settings-modal-narrow)::after {
  content: "";
  position: absolute;
  right: 4px;
  bottom: 4px;
  width: 16px;
  height: 16px;
  pointer-events: none;
  opacity: .65;
  background:
    linear-gradient(
      135deg,
      transparent 0 42%,
      var(--muted) 43% 49%,
      transparent 50% 62%,
      var(--muted) 63% 69%,
      transparent 70% 82%,
      var(--muted) 83% 89%,
      transparent 90%
    );
}

.settings-modal:not(.settings-modal-narrow) .settings-body {
  min-height: 0;
  min-width: 0;
  overflow: auto;
}

/* Landscape width is particularly useful for the wider pricing/bundler
   tables. Do not squeeze their columns to the old portrait-modal width. */
.settings-modal:not(.settings-modal-narrow) .set-table-wrap {
  max-width: 100%;
}

.settings-modal:not(.settings-modal-narrow) .settings-tabs {
  flex: 0 0 auto;
}

.settings-modal:not(.settings-modal-narrow) .settings-header,
.settings-modal:not(.settings-modal-narrow) .settings-footer {
  flex: 0 0 auto;
}

/* On genuinely narrow screens native resize is counterproductive; fall
   back to the normal responsive modal. Desktop/tablet retains resize. */
@media (max-width: 760px) {
  .settings-modal:not(.settings-modal-narrow) {
    position: relative !important;
    left: auto !important;
    top: auto !important;
    width: 100% !important;
    height: min(90vh, 760px) !important;
    min-width: 0 !important;
    min-height: 0 !important;
    max-width: 100% !important;
    max-height: 90vh !important;
    resize: none;
  }
}
