/* ════════════════════════════════════════════════════
   LiteSpec Shared UI Components
   Standardised component classes used across Dashboard,
   Engineering Hub, Framing Hub, and all module pages.
   Loaded globally via base.html.
   ════════════════════════════════════════════════════ */

/* ── Global type scale ──────────────────────────────
   All font-size values in shared components should
   reference these variables for consistency.
   ──────────────────────────────────────────────────── */
:root {
  /* Type scale */
  --fs-2xs:  8px;   /* micro labels, badge text */
  --fs-xs:   9px;   /* tags, codes, var(--font-sans, 'Inter', sans-serif) ids */
  --fs-sm:  10px;   /* column headings, caps labels */
  --fs-base: 11px;  /* primary body text in dense panels */
  --fs-md:  12px;   /* list row text, general content */
  --fs-lg:  12px;   /* section titles, sidebar heads */
  --fs-xl:  14px;   /* panel headers, card titles */
  --fs-2xl: 16px;   /* modal / page headings */
  --fs-3xl: 20px;   /* hero / display text */

  /* Dashboard theme */
  --brand:         #8e75da;
  --brand-light:   #e8e3f5;
  --brand-mid:     #a78bfa;
  --brand-dark:    #120d2b;
  --bg:            #f5f3ff;
  --surface:       #ffffff;
  --surface-2:     #f8f7ff;
  --border:        rgba(142,117,218,0.12);
  --border-strong: rgba(142,117,218,0.25);
  --text-primary:  #17152e;
  --text-secondary:#6b6886;
  --text-tertiary: #a8a5be;
  --success:       #059669;
  --success-bg:    var(--pass-bg);
  --success-border:var(--pass-border);
  --warning:       #92400e;
  --warning-bg:    var(--warn-bg);
  --warning-border:var(--warn-border);
  --danger:        #991b1b;
  --danger-bg:     var(--fail-bg);
  --danger-border: var(--fail-border);
  --info:          #1e40af;
  --neutral:       #6b7280;
  --sidebar-w:     280px;
  --topbar-h:      56px;
  --radius:        12px;
  --radius-sm:     8px;
  /* Topbar control tokens */
  --tb-h:          30px;
  --tb-h-sm:       26px;
  --tb-radius:     6px;
  --tb-font:       12px;
  --tb-font-label: 10px;
  --tb-gap:        8px;
  --tb-pad-x:      12px;
  --tb-pad-y:      5px;
}

/* ── Library hub page ───────────────────────────────
   Centred card grid for the Engineering Libraries hub.
   Replaces per-page inline <style>; lives on the app shell.
   ──────────────────────────────────────────────────── */
.lib-hub {
  max-width: 900px;
  margin: 0 auto;
  padding: 24px 24px 40px;
}

/* ═══════════════════════════════════════════════════
   UNIVERSAL CARD COMPONENT — app-cardbox
   ═══════════════════════════════════════════════════
   
   Single source of truth for all card implementations
   across LiteSpec. Consolidates all existing card
   patterns into a unified, extensible component system.
   
   Base: light interactive card
   Modifiers:
   - --dark: premium dark variant (login/reg card style)
   - --compact: reduced padding
   - --interactive: hover elevation effects
   - --elevated: strong shadow
   - --flat: no shadow/border
   ═══════════════════════════════════════════════════ */

/* ── Base card style ── */
.app-cardbox {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 18px 12px;
  transition: box-shadow .2s, border-color .2s, transform .18s, background .15s;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}

/* ── Dark variant — premium card (login/registration) ── */
.app-cardbox--dark {
  background: #271357;
  border: 1px solid var(--c-border-dedede);
  border-radius: 20px;
  padding: 2.5rem 2rem 2rem;
  color: #fff;
  cursor: pointer;
}

.app-cardbox--dark:hover {
  box-shadow: 0 6px 22px rgba(120, 80, 200, 0.15);
  border-color: var(--brand-mid);
  transform: translateY(-2px);
}

.app-cardbox--dark h1,
.app-cardbox--dark h2,
.app-cardbox--dark h3,
.app-cardbox--dark h4,
.app-cardbox--dark h5,
.app-cardbox--dark h6 {
  color: #fff;
}

.app-cardbox--dark label,
.app-cardbox--dark .label-text {
  color: rgba(255, 255, 255, 0.9);
}

/* ── Compact variant ── */
.app-cardbox--compact {
  padding: 12px 8px;
}

/* ── Interactive variant — hover effects ── */
.app-cardbox--interactive {
  cursor: pointer;
}

.app-cardbox--interactive:hover {
  box-shadow: 0 6px 22px rgba(120, 80, 200, 0.13);
  border-color: var(--accent-line);
  transform: translateY(-2px);
}

/* ── Elevated variant — strong shadow ── */
.app-cardbox--elevated {
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
}

.app-cardbox--elevated:hover {
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.16);
}

/* ── Flat variant — minimal styling ── */
.app-cardbox--flat {
  border: none;
  box-shadow: none;
  background: transparent;
}

/* ── Disabled state ── */
.app-cardbox.is-disabled,
.app-cardbox--disabled {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* ── Grid container for square cards ── */
.app-cardbox--square {
  aspect-ratio: 1;
  width: 100%;
  text-align: center;
  align-items: center;
  justify-content: center;
  gap: 10px;
  cursor: pointer;
}

.app-cardbox--square:hover {
  box-shadow: 0 6px 22px rgba(120, 80, 200, 0.13);
  border-color: var(--brand-mid);
  transform: translateY(-2px);
}

/* ── List container for management/admin hubs ── */
.lib-hub-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 24px;
}

/* ── Grouped hub layout (engineering landing) ── */
.eng-hub-group {
  margin-bottom: 28px;
}

/* Custom monoline tool icons (SVG sprite, stroke = currentColor) */
.eng-ico {
  width: 26px;
  height: 26px;
  display: block;
  color: inherit;
}

/* ── Row variant — horizontal list item (management hubs) ── */
.app-cardbox--row {
  flex-direction: row;
  align-items: center;
  gap: 16px;
  padding: 14px 18px;
  width: 100%;
  text-align: left;
  cursor: pointer;
}

.app-cardbox--row:hover {
  box-shadow: 0 4px 16px rgba(120, 80, 200, 0.10);
  border-color: var(--brand-mid);
  transform: translateY(-1px);
}

.app-cardbox--row .app-cardbox-icon {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  font-size: 18px;
}

.app-cardbox--row .app-cardbox-row-text {
  flex: 1;
  min-width: 0;
}

.app-cardbox--row .app-cardbox-desc {
  margin-top: 2px;
}

.app-cardbox--row .app-cardbox-row-chevron {
  color: var(--text-faint);
  font-size: 14px;
  flex-shrink: 0;
}

/* ── Tile variant — compact dashboard tiles (120x120) ── */
.app-cardbox--tile {
  width: 120px;
  height: 120px;
  flex: 0 0 auto;
  padding: 12px;
  text-align: center;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: pointer;
}

.app-cardbox--tile:hover {
  box-shadow: 0 4px 16px rgba(120, 80, 200, 0.12);
  border-color: var(--accent-line);
  transform: translateY(-1px);
}

/* ── App variant — dashboard Applications cards ──
   The dashboard used icon-only 120x120 tiles, which assumed you already knew
   what each product did. This variant carries a description, so the grid reads
   as a product menu. Sibling of --eng-tile (which centres 80px hub artwork);
   this one leads with a small left-aligned icon chip. */
.app-appgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 12px;
}

.app-cardbox--app {
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  padding: 18px;
  gap: 0;
  min-height: 136px;
  cursor: pointer;
}

.app-cardbox--app:hover {
  box-shadow: 0 4px 16px rgba(120, 80, 200, 0.12);
  border-color: var(--accent-line);
  transform: translateY(-1px);
}

.app-cardbox--app .app-cardbox-icon {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--brand-light);
  margin-bottom: 12px;
}

.app-cardbox--app .app-cardbox-icon i {
  font-size: 18px;
  color: var(--brand);
}

.app-cardbox--app .app-cardbox-label {
  font-size: 15px;
  font-weight: 600;
}

.app-cardbox--app .app-cardbox-desc {
  margin-top: 5px;
  font-size: 12.5px;
  line-height: 1.45;
  /* Two lines keeps every card the same height whatever the copy length. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Live count under a card — turns the launcher into a status board. */
.app-cardbox-stat {
  margin-top: auto;
  padding-top: 10px;
  font-size: 11px;
  color: var(--text-tertiary);
  letter-spacing: 0.2px;
}

/* Walls & Ceilings is the front door that chains the other four, so it keeps
   the purple hub treatment rather than reading as a fifth peer. */
.app-cardbox--app-hub {
  background: #2e1065;
  border-color: #2e1065;
  color: #fff;
  position: relative;
}

.app-cardbox--app-hub:hover {
  background: #3b0f7a;
  border-color: #6d28d9;
  box-shadow: 0 6px 22px rgba(46, 16, 101, 0.28);
}

.app-cardbox--app-hub .app-cardbox-icon {
  background: rgba(255, 255, 255, 0.12);
}

.app-cardbox--app-hub .app-cardbox-icon i,
.app-cardbox--app-hub .app-cardbox-label {
  color: #fff;
}

.app-cardbox--app-hub .app-cardbox-desc {
  color: rgba(255, 255, 255, 0.72);
}

.app-cardbox--app-hub .app-hub-badge {
  position: absolute;
  top: 10px;
  right: 10px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: 20px;
  background: #fbbf24;
  color: #422006;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.app-cardbox--app-hub .app-hub-badge i {
  font-size: 8px;
}

/* Staggered entrance, matching the stat row above. */
.app-cardbox--app {
  animation: appFadeUp 0.35s ease both;
}

.app-cardbox--app:nth-child(1) { animation-delay: 0.10s; }
.app-cardbox--app:nth-child(2) { animation-delay: 0.13s; }
.app-cardbox--app:nth-child(3) { animation-delay: 0.16s; }
.app-cardbox--app:nth-child(4) { animation-delay: 0.19s; }
.app-cardbox--app:nth-child(5) { animation-delay: 0.22s; }

/* ── Grid container for the Engineering hub ── */
.eng-hub-tilegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
  margin-bottom: 24px;
}

/* ── Eng-tile variant — bigger icon, vertical grid tile ── */
.app-cardbox--eng-tile {
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  padding: 20px 18px;
  gap: 16px;
  cursor: pointer;
  height: 100%;
}

.app-cardbox--eng-tile:hover {
  box-shadow: 0 4px 16px rgba(120, 80, 200, 0.10);
  border-color: var(--brand-mid);
  transform: translateY(-1px);
}

.app-cardbox--eng-tile .app-cardbox-icon {
  width: 80px;
  height: 80px;
  padding: 6px;
  align-self: center;
}

.app-cardbox--eng-tile .eng-ico {
  width: 100%;
  height: 100%;
  max-width: 68px;
  max-height: 68px;
  object-fit: contain;
}

.app-cardbox--eng-tile .app-cardbox-row-text {
  width: 100%;
}

.app-cardbox--eng-tile .app-cardbox-label {
  font-size: 14px;
}

.app-cardbox--eng-tile .app-cardbox-desc {
  margin-top: 4px;
}

/* ── Diagonal "coming soon" ribbon — corner banner on eng-tile cards ── */
.app-cardbox-ribbon {
  position: absolute;
  top: 16px;
  right: -34px;
  width: 130px;
  padding: 3px 0;
  transform: rotate(45deg);
  background: var(--neutral-bg);
  color: var(--text-faint);
  font-size: 10px;
  font-weight: 700;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: .02em;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  pointer-events: none;
}

/* ── Hub tile — dashboard app selector (solid dark) ── */
.app-cardbox--hub {
  background: #271357;
  border-color: var(--c-border-dedede);
  color: #fff;
  position: relative;
  overflow: visible;
}

.app-cardbox--hub:hover {
  background: #3a2465;
  border-color: var(--brand-mid);
  transform: translateY(-2px);
  box-shadow: 0 6px 22px rgba(120, 80, 200, 0.15);
}

/* ────────────────────────────────────────────────
   Content utility classes for card children
   ──────────────────────────────────────────────── */

.app-cardbox-header {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-strong);
  margin-bottom: 0.5rem;
}

.app-cardbox-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.app-cardbox-footer {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  display: flex;
  gap: 8px;
}

.app-cardbox-icon {
  width: 56px;
  height: 56px;
  border-radius: 14px;
  background: linear-gradient(135deg, var(--surface-accent), var(--c-bg-ddd6f4));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: #664d9e;
  flex-shrink: 0;
}

.app-cardbox-label {
  font-weight: 700;
  font-size: 14px;
  color: var(--text-strong);
  line-height: 1.3;
}

.app-cardbox-desc {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.45;
}

/* Dark variant text colors */
.app-cardbox--dark .app-cardbox-label {
  color: #fff;
}

.app-cardbox--dark .app-cardbox-desc {
  color: rgba(255, 255, 255, 0.75);
}

/* Tile icon variant (smaller for 120x120 tiles) */
.app-cardbox--tile .app-cardbox-icon {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--brand-light);
  font-size: 18px;
}

.app-cardbox--tile .app-cardbox-icon i {
  color: var(--brand);
}

/* Hub tile icon — light background */
.app-cardbox--hub .app-cardbox-icon {
  background: rgba(255, 255, 255, 0.12);
}

.app-cardbox--hub .app-cardbox-icon i {
  color: #fff;
}

/* Tile label variant (smaller font) */
.app-cardbox--tile .app-cardbox-label {
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 400;
  line-height: 1.2;
}

/* Hub tile label — white text */
.app-cardbox--hub .app-cardbox-label {
  color: #fff;
}

/* Badge on hub tiles */
.app-cardbox--hub .app-hub-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  background: #FFD24D;
  color: var(--c-text-24170a);
  padding: 2px 6px;
  border-radius: 10px;
  font-weight: 700;
  font-size: 11px;
  line-height: 1;
  display: inline-flex;
}

/* ────────────────────────────────────────────────
   Badge system for cards
   ──────────────────────────────────────────────── */

.app-cardbox-badge {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  font-weight: 600;
  border-radius: 99px;
  padding: 2px 9px;
  border: 1px solid var(--line);
  background: var(--neutral-bg);
  color: var(--text-faint);
  white-space: nowrap;
}

.app-cardbox-badge--purple {
  background: var(--surface-accent);
  color: var(--accent-text-strong);
  border-color: var(--c-border-ddd6f4);
}

.app-cardbox-badge--amber {
  background: var(--warn-bg);
  color: #b45309;
  border-color: var(--c-border-fde68a);
}

.app-cardbox-badge--green {
  background: var(--c-bg-dcfce7);
  color: #15803d;
  border-color: var(--c-border-bbf7d0);
}

/* Enhanced shimmer overlay */

/* ── Responsive ── */

/* ── Builder Panel Drag Dividers ────────────────────────────────────────
   Shared resizable dividers used in SmartSpec System Builder,
   Section Builder, and any future builder module.

   Usage:
     Vertical (col-resize):  <div class="ls-divider-col" title="Drag to resize"></div>
     Horizontal (row-resize): <div class="ls-divider-row" title="Drag to resize"></div>
   ────────────────────────────────────────────────────────────────────── */

/* Shared base */
.ls-divider-col,
.ls-divider-row {
  flex-shrink: 0;
  background: var(--c-bg-e5e7eb);
  display: flex; align-items: center; justify-content: center;
  transition: background .1s;
  position: relative; z-index: 5;
}
.ls-divider-col:hover, .ls-divider-col.dragging,
.ls-divider-row:hover, .ls-divider-row.dragging { background: #7c3aed; }

/* Vertical divider (col-resize — splits left/right panels) */
.ls-divider-col {
  width: 6px;
  cursor: col-resize;
}
.ls-divider-col::after {
  content: '';
  display: block; width: 2px; height: 28px;
  border-radius: 2px;
  background: rgba(0,0,0,.15);
}

/* Horizontal divider (row-resize — splits top/bottom panels) */
.ls-divider-row {
  height: 6px;
  cursor: row-resize;
}
.ls-divider-row::after {
  content: '';
  display: block; height: 2px; width: 28px;
  border-radius: 2px;
  background: rgba(0,0,0,.15);
}

/* ════════════════════════════════════════════════════
   Delete icon button — ss-wt-del-btn
   18x18px | rounded 4px | red icon | reveal on parent hover
   ════════════════════════════════════════════════════ */
.ss-wt-del-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  color: #f87171;
  cursor: pointer;
  font-size: 10px;
  background: transparent;
  border: none;
  transition: background .12s, color .12s, transform .1s;
}
.ss-wt-del-btn:hover {
  background: var(--fail-bg);
  color: var(--danger-text);
  transform: scale(1.15);
}
.ss-wt-del-btn i { pointer-events: none; }

/* ════════════════════════════════════════════════════
   Primary data table — primary-table-wrap / primary-table
   Universal table component. Visual reference: inspection panel line-list.
   Used site-wide: Projects, Catalogue, Engineering libraries.
   ════════════════════════════════════════════════════ */
/* ── Shared table design tokens — single source of truth for ALL tables.
   primary-table and every legacy table class (ft-, insp-, flr-, cap-,
   litespec-, ai-matches-, #systemTable …) consume these so the whole
   app shares one consistent table look. ── */
:root {
  --tbl-font:          13px;   /* body cell text */
  --tbl-head-font:     13px;   /* header text */
  --tbl-head-bg:       #f5f3ff;
  --tbl-head-color:    #1f2937;
  --tbl-head-border:   #ddd6fe;
  --tbl-head-pad:      10px 14px;
  --tbl-cell-pad:      8px 14px;
  --tbl-cell-color:    #374151;
  --tbl-border:        #f3f4f6;
  --tbl-row-hover:     #f5f3ff;
  --tbl-row-selected:  #ede9fe;
  --tbl-head-hover-bg: #ede9fe;
}
.primary-table-wrap {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow-x: auto;
  overflow-y: hidden;
}
/* Auto-width variant — table shrinks to content rather than stretching full width */
.primary-table-wrap--auto {
  width: fit-content;
  max-width: 100%;
}
.primary-table {
  width: 100%;
  table-layout: auto;
  border-collapse: collapse;
  font-size: var(--tbl-font);
  font-family: inherit;
}
.primary-table thead th {
  position: sticky;
  top: 0;
  background: var(--tbl-head-bg);
  padding: var(--tbl-head-pad);
  text-align: left;
  font-size: var(--tbl-head-font);
  font-weight: 600;
  color: var(--tbl-head-color);
  border-bottom: 1px solid var(--tbl-head-border);
  border-right: none;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.primary-table thead th:last-child { cursor: default; }
.primary-table thead th:hover:not(:last-child) {
  background: var(--tbl-head-hover-bg);
  color: var(--tbl-head-color);
}
.primary-table tbody tr {
  border-bottom: 1px solid var(--tbl-border);
  cursor: pointer;
  transition: background .1s;
}
.primary-table tbody tr:last-child { border-bottom: none; }
.primary-table tbody tr:hover { background: var(--tbl-row-hover); }
.primary-table tbody tr.selected { background: var(--tbl-row-selected); }
.primary-table td {
  padding: var(--tbl-cell-pad);
  vertical-align: middle;
  border-right: 1px solid var(--tbl-border);
  color: var(--tbl-cell-color);
  font-size: var(--tbl-font);
}
.primary-table td:last-child { border-right: none; }
/* Alignment helpers */
.primary-table td.td-num,
.primary-table th.th-num { text-align: right; }
.primary-table td.td-num { font-variant-numeric: tabular-nums; }
.primary-table td.td-center,
.primary-table th.th-center { text-align: center; }
/* Shrink-to-fit: action buttons, numeric IDs, short codes — column hugs its content */
.primary-table th.th-shrink,
.primary-table td.td-shrink {
  white-space: nowrap;
}
/* Descriptive columns (name/description) — also content-sized, never forced to stretch.
   Class kept for semantic intent; widths are content-driven under table-layout: auto. */
.primary-table th.th-grow,
.primary-table td.td-grow {
  width: auto;
}

/* Column resize handle — injected by ws-table-utils.js */
.ws-col-resizer {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  width: 5px;
  cursor: col-resize;
  z-index: 1;
  background: transparent;
  transition: background .1s;
}
.ws-col-resizer:hover,
.ws-col-resizer:active {
  background: #a78bfa;
  opacity: 0.5;
}

/* Library workspace content wrapper */
.lib-workspace-wrap {
  padding: 1.75rem 1.5rem;
}

/* ════════════════════════════════════════════════════
   Shared library detail panel classes
   Used by Engineering side panels (Material, Section, Fixing, Connector, Load Case).
   ════════════════════════════════════════════════════ */
.detail-row { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid var(--line-faint); font-size: 12px; }
.detail-label { color: var(--text-body-2); font-weight: 500; }
.detail-val { font-weight: 600; color: var(--text-strong); }
.detail-group { margin-bottom: 16px; }
.detail-group h4 { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: #7c5fb3; margin: 0 0 6px; }

/* Shared capacity / component sub-table — Fixing, Connector, Load Case detail panels */
.cap-table { width: 100%; border-collapse: collapse; font-size: 12px; margin-top: 6px; }
.cap-table th { background: var(--tbl-head-bg); padding: 6px 10px; text-align: left; font-size: 12px; font-weight: 600; color: var(--tbl-head-color); border-bottom: 1px solid var(--tbl-head-border); }
.cap-table td { padding: 5px 10px; font-size: 12px; border-bottom: 1px solid var(--tbl-border); color: var(--tbl-cell-color); }
/* .cap-table is the compact primary-table (same cell padding and 12px text) for
   sub-tables inside side panels; th-num / td-num right-align numeric columns. */
.cap-table th.th-num, .cap-table td.td-num { text-align: right; font-variant-numeric: tabular-nums; }

/* ΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉ
   Generic icon button ΓÇö pl-icon-btn
   20├ù20px | rounded 4px | gray bg | state variants
   ΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉ */
.pl-icon-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  border: none;
  cursor: pointer;
  font-size: 10px;
  background: transparent;
  color: var(--c-text-4e4e4e);
  transition: background .12s, color .12s, transform .1s;
}
.pl-icon-btn:hover { background: #d4d4d4; transform: scale(1.1); }
.pl-icon-btn:disabled { opacity: .4; cursor: default; pointer-events: none; }
.pl-icon-btn.dirty { border-color: #764ba2; background: var(--surface-accent); color: #764ba2; }
.pl-icon-btn.saved { border-color: #10b981; background: var(--c-bg-dcfce7); color: #10b981; }
.pl-icon-btn i { pointer-events: none; }

/* ΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉ
   Project select dropdown ΓÇö ls-select-wrap / ls-select
   Sidebar project picker with FA folder icon.
   Usage:
     <div class="ls-select-wrap">
       <i class="fas fa-folder ls-select-icon"></i>
       <select class="ls-select" onchange="...">...</select>
     </div>
   ΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉ */
.ls-select-wrap {
  position: relative;
  display: flex;
  align-items: center;
  padding: 10px 17px;
  flex-shrink: 0;
}
.ls-select-icon {
  position: absolute;
  left: 26px;
  font-size: 12px;
  color: #7354af;
  pointer-events: none;
  z-index: 1;
}
.ls-select {
  width: 100%;
  height: 37px;
  padding: 0 28px 0 28px;
  border: 0.2px solid var(--c-border-dedede);
  border-radius: 6px;
  font-size: 12px;
  color: #6d6d6d;
  background: var(--surface);
  outline: none;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'><path fill='%236d6d6d' d='M0 0l5 6 5-6z'/></svg>");
  background-repeat: no-repeat;
  background-position: calc(100% - 10px) 50%;
  background-size: 8px 5px;
  box-sizing: border-box;
  transition: border-color .15s;
}
.ls-select:focus { border-color: #7354af; }
input.ls-select, textarea.ls-select { background-image: none; cursor: text; }

/* ΓöÇΓöÇ Shared search bar (catalogue + projects) ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
   Exact same styles as catalog/product_library_content.html inline CSS.
   Loaded globally so projects page can reuse without duplicating.
   ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */
.pl-search-bar {
  background: var(--c-bg-f5f5f5);
  border: 0.5px solid var(--c-border-d2d2d2);
  border-radius: 10px;
  box-shadow: 0px 4px 4px 0px rgba(0,0,0,0.25);
  overflow: hidden;
  margin-bottom: 0;
}
.pl-search-bar-hdr {
  background: var(--c-bg-dddddd);
  height: 40px;
  display: flex;
  align-items: center;
  padding: 0 21px;
}
.pl-search-bar-hdr-title {
  color: var(--c-text-4e4e4e); font-weight: 700; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
}
.pl-search-bar-hdr-end { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.pl-search-bar-body {
  padding: 8px 20px 14px;
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}
.pl-sb-field { display: flex; flex-direction: column; gap: 4px; }
.pl-sb-field-label { font-size: 12px; font-weight: 700; color: var(--c-text-4e4e4e); white-space: nowrap; }
.pl-search-input-wrap { position: relative; display: inline-block; width: 251px; }
.pl-search-input-wrap i {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  color: #f5f3ff; font-size: 12px; pointer-events: none; z-index: 1;
}
.pl-search-input-wrap input {
  width: 100%; height: 32px;
  border: 0.5px solid #6d6d6d; border-radius: 6px;
  padding: 0 10px 0 32px;
  font-size: 12px; color: var(--c-text-4e4e4e); background: var(--surface);
  outline: none; box-sizing: border-box;
}
.pl-search-input-wrap input:focus { border-color: var(--brand-mid); }
.pl-sb-select-wrap { position: relative; }
.pl-sb-select-wrap i {
  position: absolute; right: 9px; top: 50%; transform: translateY(-50%);
  color: var(--c-text-4e4e4e); font-size: 10px; pointer-events: none;
}
.pl-filter-select {
  width: 149px; height: 32px;
  border: 0.5px solid #6d6d6d; border-radius: 6px;
  padding: 0 26px 0 10px;
  font-size: 12px; color: var(--c-text-4e4e4e); background: var(--surface);
  outline: none; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  box-sizing: border-box;
}
.pl-filter-select:focus { border-color: var(--brand-mid); }
.pl-count-badge {
  font-size: 12px; color: var(--c-text-4e4e4e); padding: 0 10px;
  background: var(--surface); border-radius: 6px; height: 25px;
  display: inline-flex; align-items: center; white-space: nowrap;
}
.pl-add-btn {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0 14px; border: none; border-radius: 7px;
  background: #764ba2; color: #fff; font-size: 12px; font-weight: 600;
  cursor: pointer; transition: background .15s; white-space: nowrap;
  height: 32px; text-decoration: none;
}
.pl-add-btn:hover { background: #5b4282; color: #fff; }

/* ════════════════════════════════════════════════════
   Framing Table (ft-)
   Shared by Stud Wall Framing and all future framing modules.
   ════════════════════════════════════════════════════ */

/* ── Page shell ──────────────────────────────────── */
#ftMatrixWrap { flex:1; overflow:auto; padding:16px 20px 24px; }

/* ════════════════════════════════════════════════════
   Split-workspace shell — universal, module-agnostic.
   Use for any module that pairs a slide-out side panel
   (e.g. .detail-panel "Design Assumptions") with a
   results canvas, inside .app-workspace's content block:

     <div class="ws-split">
       <div class="detail-panel" id="...">...</div>   <!-- optional side panel -->
       <div class="ws-split-main">
         <div class="ws-canvas">...results...</div>
       </div>
     </div>

   .ws-split       — flex row, fills the workspace, clips overflow
   .ws-split-main  — the non-panel column; owns its own scroll
   .ws-canvas      — padded, tinted content pane (was ftMain+ftContent)
   Skip .ws-split/.ws-split-main entirely for single-pane pages —
   just drop .ws-canvas straight into the content block (see
   ceiling_stud.html).
   ════════════════════════════════════════════════════ */
.ws-split      { display:flex; flex-direction:row; flex:1; overflow:hidden; }
.ws-canvas     { flex:1; background:var(--c-bg-fafafa); padding:16px; display:flex; flex-direction:column; gap:12px; }

/* ── Input panel topbar ──────────────────────────────
   Sits as a sibling element directly after .app-topbar-bar
   inside {% block dbs_topbar %}. Follows sq-stats-bar
   visual language: flat, white, border-bottom.
   ─────────────────────────────────────────────────── */
.app-topbar-panel {
  display:flex;
  align-items:stretch;
  flex-shrink:0;
  min-height:48px;
  background:var(--surface,var(--surface));
  border-bottom:1px solid var(--border,rgba(142,117,218,.12));
  padding:0 0.5rem;
  /* Zones wrap onto their own row instead of running past the edge (the
     catalogue and engineering library bars lost Export / Status at 900-1366). */
  flex-wrap:wrap;
  min-width:0;
}
.ft-filter-inner {
  display:flex;
  align-items:stretch;
  flex:1;
}
.ft-filter-section {
  display:flex;
  flex-direction:column;
  justify-content:center;
  gap:5px;
  padding:8px 16px;
  min-width:0;
  flex:1;
}
.ft-filter-section--grow { flex:1; }
.ft-filter-section-header {
  display:flex;
  flex-direction:column;
  gap:1px;
  line-height:1;
}
.ft-filter-section-title {
  font-size: var(--tbl-head-font, 12px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--tbl-head-color, var(--text-muted));
}
.ft-filter-section-desc {
  font-size: var(--tbl-head-font, 12px);
  color: var(--tbl-head-color, var(--text-muted));
  font-weight: 400;
  opacity: .7;
}
.ft-filter-tags {
  display:flex;
  align-items:center;
  gap:6px;
  flex-wrap:wrap;
}
.ft-filter-divider {
  width:1px;
  background:var(--border,rgba(142,117,218,.12));
  flex-shrink:0;
  align-self:stretch;
  margin:8px 0;
}

/* ── Topbar zones (scope · filters · actions) ─────────
   The panel is three semantic zones, not a flat rail:
   scope is a connected context cluster on the left,
   filters grows to fill the middle, actions anchors
   hard right via margin-left:auto (robust even when the
   filters zone is empty). Replaces the old "only the
   grow section pushes actions right" mechanism.
   ─────────────────────────────────────────────────── */
.tb-zone { display:flex; flex-wrap:wrap; align-items:stretch; row-gap:6px; min-width:0; }
.tb-zone--filters { flex:1 1 auto; min-width:0; }
.tb-zone--actions {
  margin-left:auto;
  align-items:center;
}
/* No rules between fields. Every divider the panel drew — full-height at
   first, then the thinner internal ones — fenced each control into its own
   cell, which is what made the bar read as a spreadsheet header row rather
   than a toolbar. The zones are already separated by whitespace (actions is
   pushed right by margin-left:auto), so the ink bought nothing. Spacing does
   the grouping instead: .app-field-group's 18px of side padding against its
   own 8px internal gap, so a label always sits nearer its own control than
   the neighbouring one. */

/* ── Settings cluster ─────────────────────────────────
   Module tools (Design Assumptions, Add custom system) immediately left of
   the primary Run cluster. Tighter than the zone's field spacing so the two
   read as one right-hand group rather than two unrelated islands.
   ─────────────────────────────────────────────────── */
.tb-settings { display:flex; align-items:center; gap:6px; }

/* ── Labelled field group ─────────────────────────────
   One bordered capsule: a muted label prefix + a bare control.
     .tb-field
       .tb-field__label   (icon + word, tinted, hairline to the right)
       .app-select--bare  (the control, border/background stripped)

   Exists because the Project and Loading pickers are two visually identical
   selects sitting side by side — both filled with project-ish names, told
   apart only by a cryptic 12px icon. An in-capsule label names each one
   without resurrecting the shouting standalone labels: the word is part of
   the control, lowercase-quiet, and cannot drift away from it.
   ─────────────────────────────────────────────────── */
.tb-field {
  display: inline-flex;
  align-items: stretch;
  height: var(--tb-h, 30px);
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface));
  overflow: hidden;
  transition: border-color .12s, box-shadow .12s;
}
.tb-field:hover        { border-color: var(--brand-mid, var(--brand-mid)); }
.tb-field:focus-within {
  border-color: var(--brand, #8e75da);
  box-shadow: 0 0 0 2px rgba(142,117,218,.18);
}
/* The wrapper carries the disabled look — the select inside is bare, so its
   own :disabled dimming would only fade the text, not the capsule. */
.tb-field:has(> select:disabled) { opacity: .55; }
.tb-field__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 9px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-secondary, var(--text-muted));
  background: var(--neutral-bg);
  border-right: 1px solid var(--border, rgba(142,117,218,.12));
  white-space: nowrap;
  user-select: none;
}
.tb-field__label i { font-size: 11px; color: var(--brand, var(--brand)); }
/* Bare select for use inside .tb-field: the capsule owns border, focus ring
   and disabled look; the select keeps only its text and caret. Declared
   after .app-select so the modifier wins at equal specificity. */
.app-select--bare,
.app-select.app-select--bare {
  height: 100%;
  border: none;
  border-radius: 0;
  background-color: transparent;
  outline: none;
  box-shadow: none;
}
.app-select--bare:focus-visible { outline: none; }
[data-theme="dark"] .tb-field { background:#1e293b; border-color:#334155; }
[data-theme="dark"] .tb-field__label { background:#0f172a; border-right-color:#334155; color:#94a3b8; }

/* ── Stacked (two-row) topbar ─────────────────────────
   Opt-in via `stacked` on the include. The panel wraps so
   the filter inputs (scope + filters zones) sit on the top
   row and the actions zone drops to its own full-width row
   below, becoming a dedicated buttons rail. Keeps the same
   sections/config — only the flow changes.
   ─────────────────────────────────────────────────── */
.app-topbar-panel--stacked { flex-wrap:wrap; }
.app-topbar-panel--stacked .tb-zone--actions {
  flex-basis:100%;
  margin-left:0;
  border-left:none;
  border-top:1px solid var(--border,rgba(142,117,218,.12));
  justify-content:flex-start;
}
/* Cap topbar select width so a long option list (e.g. many category or
   supplier names) can't blow the control out to its widest option. */
.app-topbar-panel .tb-zone .app-select { max-width:200px; }

/* ── Topbar control tokens ───────────────────────────
   Shared by all module topbar partials. Replaces
   inline styles so all modules render identically.
   ─────────────────────────────────────────────────── */

/* Auto-saved draft badge — use app-badge--info instead */
.ft-badge--draft {
  display: none;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: var(--tb-radius, 6px);
  border: 1px solid var(--info-border, var(--info-border));
  background: var(--info-bg, var(--info-bg));
  color: var(--info, var(--info-fg));
  font-size: var(--tb-font, 12px);
  font-weight: 600;
}

/* ── Calculation status pill ─────────────────────────────────────────
   Reports calc lifecycle only — it is NOT an action. Saving belongs to the
   Save button in the same cluster; a pill that morphed into a second Save
   was the source of the duplicate-Save bug, so there is deliberately no
   --save state here. Universal — usable by any precompute module.
   States: --progress (calculating) · --warn (blocked) · --busy (saving)
   · --saved / --ok (success flash) · --error.
   Height is pinned to the shared control height so the pill lines up with
   the Run/Save buttons beside it instead of floating mid-cluster. */
.calc-status {
  display:inline-flex;
  align-items:center;
  gap:6px;
  height:var(--tb-h, 30px);
  font-size:12px;
  font-weight:600;
  line-height:1;
  border:1px solid transparent;
  border-radius:var(--tb-radius, 6px);
  padding:0 12px;
  white-space:nowrap;
  box-sizing:border-box;   /* borders inside --tb-h, matching .app-btn */
}
.calc-status--progress { color:var(--accent-text-strong); background:var(--surface-accent); border-color:var(--accent-line); }
.calc-status--warn     { color:var(--warn-fg); background:var(--warn-bg); border-color:var(--warn-border); }
.calc-status--error { color:var(--danger-text-2); background:var(--fail-bg); border-color:var(--fail-border); }
.calc-status--ok    { color:#fff; background:#16a34a; border-color:#16a34a; cursor:default; }
/* ── Status strip variant ─────────────────────────────
   The component renders the calc status as a full-width row directly under
   the rail (see input_panel_topbar.html) instead of a pill inside the
   button cluster. Layout keys on the attribute, not a class: _statusBase
   rewrites className on every state change ('calc-status calc-status--warn'),
   and an attribute is the only hook that survives that. Colours still come
   from the calc-status--* state classes above; these rules only flatten the
   pill geometry into a strip. Declared after the variants so the tie on
   specificity goes to the strip. */
[data-ta-statusbar] {
  width: 100%;
  height: auto;
  align-items: center;
  border: none;
  border-radius: 0;
  padding: 6px 17px;   /* text aligns with the scope zone's first capsule */
}
/* Determinate progress bar inside the pill ("12 / 42" + filling track).
   Driven by TopbarActions.setProgress(done, total). */
.calc-progress {
  display:inline-block;
  width:56px;
  height:4px;
  border-radius:2px;
  background:var(--surface-accent-2);
  overflow:hidden;
  vertical-align:middle;
  margin-left:4px;
}
.calc-progress-fill {
  display:block;
  height:100%;
  width:0;
  border-radius:2px;
  background:#6d28d9;
  transition:width .3s ease;
}

/* ── Wall type filter tags ───────────────────────── */
.ft-type-tag {
  display:inline-flex; align-items:center; gap:5px;
  font-size:12px; font-weight:600; padding:5px 12px; border-radius:20px; cursor:pointer;
  border:1px solid var(--line); background:var(--surface-sunken); color:var(--text-muted);
  transition:background .15s, color .15s, border-color .15s;
  white-space:nowrap;
}
.ft-type-tag:hover { background:var(--c-bg-f3f0ff); border-color:var(--accent-line); color:var(--accent-text-strong); }
.ft-type-tag.active { background:var(--surface-accent); border-color:var(--brand-mid); color:var(--accent-text-strong); font-weight:700; }

/* ── View mode toggle ────────────────────────────── */
.ft-view-toggle { display:flex; border:1px solid var(--line); border-radius:8px; overflow:hidden; width:fit-content; }
.ft-view-btn {
  background:var(--surface); border:none; cursor:pointer; font-size:12px; font-weight:600;
  color:var(--text-muted); padding:6px 14px; display:flex; align-items:center; gap:6px;
  transition:background .15s, color .15s; white-space:nowrap;
}
.ft-view-btn + .ft-view-btn { border-left:1px solid var(--line); }
.ft-view-btn.active { background:var(--surface-accent); color:var(--accent-text); }
.ft-view-btn:hover:not(.active) { background:var(--c-bg-f5f0ff); color:var(--accent-text); }

/* ════════════════════════════════════════════════════════════════
   UNIVERSAL APP-TOPBAR-PANEL DESIGN SYSTEM
   ════════════════════════════════════════════════════════════════
   Canonical UI classes for every control inside .app-topbar-panel
   across all LiteSpec modules (Catalog, Engineering, Libraries,
   Projects, SmartSpec, SmartQuote, Reviews, Admin).

   Naming:  app-{component}  /  app-{component}--{variant}
   Source:  this block in static/css/components.css

   Deprecated class → Universal replacement mapping:
     ft-ctrl-btn             → app-btn
     ft-ctrl-btn--primary    → app-btn-primary
     ft-ctrl-btn--icon       → app-btn-icon
     ft-filter-select        → app-select
     tb-btn                  → app-btn
     tb-btn.primary          → app-btn-primary
     tb-select / tb-input    → app-select / app-input
     status-pill--on/--off   → app-badge--success / app-badge--danger
     status-badge .pass/fail → app-badge--success / app-badge--danger
     lib-badge-archived      → app-badge--warning
     pass-pill / fail-pill   → app-badge--success / app-badge--danger
     ls-badge-uls / ls-badge-sls → app-badge--danger / app-badge--info
     ft-filter-section-title → app-label
     ft-filter-section-desc  → app-caption
     ft-filter-section       → app-field-group
   ════════════════════════════════════════════════════════════════ */

/* ── app-badge ────────────────────────────────────────────────────
   Universal status/tag badge. Replaces all pill/badge variants.
   ─────────────────────────────────────────────────────────────── */
.app-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--tb-font-label, 10px);
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 99px;
  white-space: nowrap;
  line-height: 1.4;
  border: 1px solid var(--neutral-border, var(--neutral-border));
  background: var(--neutral-bg);
  color: var(--neutral-fg, var(--text-muted));
}
/* Soft status palette — one source: --pass/--warn/--fail/--info/--neutral
   tokens in tokens.css. --pass is the same palette as --success etc. */
.app-badge--success, .app-badge--pass {
  background:   var(--pass-bg,     var(--pass-bg));
  color:        var(--pass-fg,     var(--pass-fg));
  border-color: var(--pass-border, var(--pass-border));
}
.app-badge--warning, .app-badge--warn {
  background:   var(--warn-bg,     var(--warn-bg));
  color:        var(--warn-fg,     var(--warn-fg));
  border-color: var(--warn-border, var(--warn-border));
}
.app-badge--danger, .app-badge--fail {
  background:   var(--fail-bg,     var(--fail-bg));
  color:        var(--fail-fg,     var(--fail-fg));
  border-color: var(--fail-border, var(--fail-border));
}
.app-badge--info {
  background:   var(--info-bg, var(--info-bg));
  color:        var(--info-fg,          var(--info-fg));
  border-color: var(--info-border, var(--info-border));
}
.app-badge--neutral {
  background:   var(--neutral-bg,      var(--neutral-bg));
  color:        var(--neutral-fg,      var(--text-muted));
  border-color: var(--neutral-border,  var(--neutral-border));
}
.app-badge--primary {
  background:   var(--brand-light,     var(--brand-light));
  color:        var(--accent-text-deep);
  border-color: var(--brand-mid,       var(--brand-mid));
}

/* ── app-topbar-dd ────────────────────────────────────────────────
   Dropdown anchored inside .app-topbar-panel sections.
   Structure:
     .app-topbar-dd
       button.app-topbar-dd__trigger  (app-btn variant)
       .app-topbar-dd__menu
         .app-topbar-dd__item
           .app-topbar-dd__item-main  (click = primary action)
           .app-topbar-dd__actions
             button.app-topbar-dd__act-btn
             button.app-topbar-dd__act-btn--danger
   ─────────────────────────────────────────────────────────────── */
.app-topbar-dd { position: relative; }

.app-topbar-dd__menu {
  position:   absolute;
  top:        calc(100% + 4px);
  left:       0;
  min-width:  260px;
  max-height: 320px;
  overflow-y: auto;
  background: var(--surface, var(--surface));
  border:     1px solid var(--border, var(--line));
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0,0,0,.12);
  z-index:    9999;
  display:    none;
}
.app-topbar-dd__menu.is-open { display: block; }
/* .tb-zone--actions is pinned to the far right edge of the page
   (margin-left:auto). A menu that grows rightward from its trigger's left
   edge (the base rule above) runs off the viewport there — the Saved dropdown
   on every module topbar had this bug. Anchor to the trigger's right edge
   instead, so the menu grows back into the toolbar. Matches the existing
   .ta-mode-menu treatment; covers Load, Columns and pricelist dropdowns alike
   since all three live in the actions zone. */
.tb-zone--actions .app-topbar-dd__menu { left: auto; right: 0; }
[data-theme="dark"] .app-topbar-dd__menu {
  background:   #1e293b;
  border-color: #334155;
}

.app-topbar-dd__empty {
  padding:    14px 12px;
  font-size:  12px;
  color:      var(--text-faint);
  text-align: center;
}

.app-topbar-dd__item {
  display:       flex;
  align-items:   center;
  border-bottom: 1px solid var(--line-faint);
}
[data-theme="dark"] .app-topbar-dd__item { border-bottom-color: #334155; }
.app-topbar-dd__item:last-child { border-bottom: none; }

.app-topbar-dd__item-main {
  flex:           1;
  min-width:      0;
  padding:        8px 10px;
  cursor:         pointer;
  display:        flex;
  flex-direction: column;
  gap:            2px;
  background:     none;
  border:         none;
  text-align:     left;
}
.app-topbar-dd__item-main:hover { background: rgba(0,0,0,.04); }
[data-theme="dark"] .app-topbar-dd__item-main:hover { background: rgba(255,255,255,.06); }

.app-topbar-dd__item-name {
  font-size:     12px;
  font-weight:   600;
  color:         var(--text-ink);
  white-space:   nowrap;
  overflow:      hidden;
  text-overflow: ellipsis;
}
[data-theme="dark"] .app-topbar-dd__item-name { color: #f1f5f9; }

.app-topbar-dd__item-meta {
  font-size: 10px;
  color:     var(--text-faint);
}

.app-topbar-dd__actions {
  display:     flex;
  align-items: stretch;
  flex-shrink: 0;
  border-left: 1px solid var(--line-faint);
}
[data-theme="dark"] .app-topbar-dd__actions { border-left-color: #334155; }

.app-topbar-dd__act-btn {
  display:         flex;
  align-items:     center;
  justify-content: center;
  width:           30px;
  background:      none;
  border:          none;
  border-right:    1px solid var(--line-faint);
  cursor:          pointer;
  color:           var(--text-muted);
  font-size:       11px;
  transition:      background .15s, color .15s;
}
[data-theme="dark"] .app-topbar-dd__act-btn { border-right-color: #334155; color: #94a3b8; }
.app-topbar-dd__act-btn:last-child { border-right: none; }
.app-topbar-dd__act-btn:hover { background: rgba(0,0,0,.06); color: var(--text-ink); }
[data-theme="dark"] .app-topbar-dd__act-btn:hover { background: rgba(255,255,255,.08); color: #f1f5f9; }

.app-topbar-dd__act-btn--danger:hover { background: var(--fail-bg-soft); color: #ef4444; }
[data-theme="dark"] .app-topbar-dd__act-btn--danger:hover { background: rgba(239,68,68,.15); color: #f87171; }

/* Load action — labelled (not icon-only) and accented so it reads as the primary action */
.app-topbar-dd__act-btn--load {
  width: auto; gap: 5px; padding: 0 10px; font-weight: 600;
  color: var(--accent-text); white-space: nowrap;
}
.app-topbar-dd__act-btn--load:hover { background: var(--c-bg-f3effb); color: var(--accent-text-strong); }
[data-theme="dark"] .app-topbar-dd__act-btn--load { color: #c4b5fd; }
[data-theme="dark"] .app-topbar-dd__act-btn--load:hover { background: rgba(124,58,237,.18); color: #ddd6fe; }

/* ── Topbar Actions (TopbarActions) ───────────────────────────────
   Universal Run / Save / Load cluster rendered by
   components/input_panel_topbar/_actions.html.
   ─────────────────────────────────────────────────────────────── */
.ta-cluster { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Run is the hero of the actions zone: bolder, wider, unmistakably primary,
   so it never reads as a peer of Save. Save/Saved stay app-btn-secondary.
   Hero-ness comes from colour, weight and width — NOT height: every control
   in the cluster shares --tb-h so the row has one baseline. */
.ta-cluster .ta-run--hero { padding: 0 18px; font-size: 13px; font-weight: 700; }

/* Live count on the always-visible Saved dropdown. */
.ta-load-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; margin-left: 2px;
  border-radius: 9px; font-size: 11px; font-weight: 700; line-height: 1;
  background: var(--brand-light, var(--brand-light)); color: var(--brand, var(--brand));
}
[data-theme="dark"] .ta-load-count { background: rgba(124,58,237,.28); color: #ddd6fe; }

/* Split Run button: main action + caret, fused into one pill. */
.ta-split { display: inline-flex; align-items: stretch; }
/* Square the joint only when a caret actually follows — a modeless module
   renders Run alone inside the split, and unconditional squaring left it
   half-rounded, visibly different from every sibling module's Run. */
.ta-split .ta-run:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.ta-mode { position: relative; display: inline-flex; }
.ta-split .ta-caret {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
  border-left: 1px solid rgba(255,255,255,.35);
  padding: 0 8px;
}

/* Mode menu reuses .app-topbar-dd__menu positioning; just narrower. */
.ta-mode-menu { min-width: 160px; right: 0; left: auto; padding: 4px; }
.ta-mode-item {
  display: flex; align-items: center; width: 100%;
  padding: 7px 10px; border: none; background: none; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--text-ink);
  border-radius: 5px; text-align: left;
}
.ta-mode-item:hover { background: rgba(0,0,0,.05); }
.ta-mode-item.is-active { background: rgba(124,58,237,.12); color: var(--accent-text-strong); }
[data-theme="dark"] .ta-mode-item { color: #f1f5f9; }
[data-theme="dark"] .ta-mode-item:hover { background: rgba(255,255,255,.07); }
[data-theme="dark"] .ta-mode-item.is-active { background: rgba(124,58,237,.28); color: #ddd6fe; }

/* ── app-btn ──────────────────────────────────────────────────────
   Universal action button. Replaces: ft-ctrl-btn, tb-btn,
   libws-btn, lib-restore-btn.
   ─────────────────────────────────────────────────────────────── */
.app-btn[hidden] { display: none; }
.app-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--tb-gap, 6px);
  height: var(--tb-h, 30px);
  padding: 0 var(--tb-pad-x, 12px);
  border: 1px solid var(--border-strong, rgba(142,117,218,.25));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface));
  color: var(--text-secondary, var(--text-secondary));
  font-size: var(--tb-font, 12px);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background .12s, border-color .12s, color .12s;
  box-sizing: border-box;
  flex-shrink: 0;
}
.app-btn:hover {
  background:   var(--brand-light, var(--brand-light));
  border-color: var(--brand-mid,   var(--brand-mid));
  color:        var(--brand,       var(--brand));
}
.app-btn:focus-visible {
  outline: 2px solid var(--brand-mid, var(--brand-mid));
  outline-offset: 2px;
}
.app-btn:disabled,
.app-btn[disabled] { opacity: .5; cursor: not-allowed; pointer-events: none; }

/* Primary — filled brand colour */
.app-btn-primary {
  background:   var(--brand, #8e75da);
  border-color: var(--brand, #8e75da);
  color: #fff;
}
.app-btn-primary:hover  { background: #7c3aed; border-color: #7c3aed; color: #fff; }
.app-btn-primary:active { background: #6d28d9; border-color: #6d28d9; }

/* Secondary — default; kept as alias for clarity */
.app-btn-secondary {
  background:   var(--surface, var(--surface));
  border-color: var(--border-strong, rgba(142,117,218,.25));
  color:        var(--text-secondary, var(--text-secondary));
}
.app-btn-secondary:hover {
  background:   var(--brand-light, var(--brand-light));
  border-color: var(--brand-mid,   var(--brand-mid));
  color:        var(--brand,       var(--brand));
}

/* Ghost — borderless at rest */
.app-btn-ghost {
  border-color: transparent;
  background: transparent;
  color: var(--text-secondary, var(--text-secondary));
}
.app-btn-ghost:hover {
  background:   var(--brand-light, var(--brand-light));
  border-color: var(--brand-mid,   var(--brand-mid));
  color:        var(--brand,       var(--brand));
}

/* Icon button — square, icon only */
.app-btn-icon {
  width: var(--tb-h, 30px);
  height: var(--tb-h, 30px);
  padding: 0;
  justify-content: center;
  flex-shrink: 0;
}
/* Brand-tinted glyph — for icon buttons that are an accent rather than a
   neutral control (e.g. the topbar Design Assumptions button). Carries the
   colour the retired .ft-ctrl-btn--icon alias used to supply. */
.app-btn-icon--brand { color: var(--brand, var(--brand)); }
/* Destructive glyph — stays neutral until hovered, so a row of icon buttons
   does not read as a wall of red, but the intent is unmistakable on approach.
   Pairs with .app-btn-icon, not with the filled .app-btn-danger. */
.app-btn-icon--danger:hover {
  color: var(--danger-text);
  border-color: var(--c-border-fecaca);
  background: var(--fail-bg-soft);
}

/* ── Semantic filled variants — pair with .app-btn ────────────────
   Use for the affirmative action in a modal / toolbar:
     app-btn-danger   → delete / destructive
     app-btn-warning  → caution / proceed-with-care
     app-btn-success  → update / save / confirm-positive
   (primary = brand default; secondary = neutral cancel.)
   ─────────────────────────────────────────────────────────────── */
.app-btn-danger {
  background: #dc2626; border-color: #dc2626; color: #fff;
}
.app-btn-danger:hover  { background: #ef4444; border-color: #ef4444; color: #fff; }
.app-btn-danger:active { background: #b91c1c; border-color: #b91c1c; }

.app-btn-warning {
  background: #d97706; border-color: #d97706; color: #fff;
}
.app-btn-warning:hover  { background: #f59e0b; border-color: #f59e0b; color: #fff; }
.app-btn-warning:active { background: #b45309; border-color: #b45309; }

.app-btn-success {
  background: #059669; border-color: #059669; color: #fff;
}
.app-btn-success:hover  { background: #10b981; border-color: #10b981; color: #fff; }
.app-btn-success:active { background: #047857; border-color: #047857; }

/* Small button - 24px. Pair with .app-btn (and any tone). */
.app-btn-sm {
  height: 24px;
  padding: 0 8px;
  gap: 5px;
  font-size: 11px;
}
.app-btn-sm.app-btn-icon { width: 24px; padding: 0; }
.app-btn-block { display: flex; width: 100%; justify-content: center; }

/* Large button - 40px, for the single call to action on a page (account pages,
   upgrade, landing forms). Pair with .app-btn and a tone. */
.app-btn-lg {
  height: 40px;
  padding: 0 20px;
  gap: 8px;
  font-size: 14px;
}
.app-btn-lg.app-btn-icon { width: 40px; padding: 0; }

/* ── Legacy button aliases ───────────────────────────────────────
   Only the classes that still appear in markup are kept (checked 14 Oct):
     btn, btn-sm, btn-outline-primary / -secondary / -danger
   They are used by the Django admin change lists (catalog product and system
   lists keep their admin buttons on purpose) and by the calculators page.
   New markup: class="app-btn app-btn-primary", never btn-*.
   Retired with zero uses: btn-primary/-secondary/-danger/-danger-soft/
   -danger-ghost/-ghost/-soft/-success/-warn/-warning, btn-icon*, btn-close,
   btn-lg, dp-btn, ft-ctrl-btn, ft-filter-select, card-default/-elevated/
   -flat/-compact/-detail/-list, status-pill, form-input.
   ─────────────────────────────────────────────────────────────── */
.btn, .btn-outline-primary, .btn-outline-secondary, .btn-outline-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--tb-gap, 6px);
  height: var(--tb-h, 30px);
  padding: 0 var(--tb-pad-x, 12px);
  border: 1px solid var(--border-strong, rgba(142,117,218,.25));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface));
  color: var(--text-secondary, var(--text-secondary));
  font-family: inherit;
  font-size: var(--tb-font, 12px);
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  box-shadow: none;
  transform: none;
  transition: background .12s, border-color .12s, color .12s;
  box-sizing: border-box;
  flex-shrink: 0;
}
.btn:hover, .btn-outline-primary:hover, .btn-outline-secondary:hover {
  background: var(--brand-light, var(--brand-light));
  border-color: var(--brand-mid, var(--brand-mid));
  color: var(--brand, var(--brand));
  text-decoration: none;
  box-shadow: none;
  transform: none;
}
.btn:focus-visible, .btn-outline-primary:focus-visible, .btn-outline-secondary:focus-visible,
.btn-outline-danger:focus-visible {
  outline: 2px solid var(--brand-mid, var(--brand-mid));
  outline-offset: 2px;
}
.btn:disabled, .btn[disabled], .btn-outline-primary:disabled, .btn-outline-secondary:disabled {
  opacity: .5; cursor: not-allowed; pointer-events: none;
}
.btn[hidden] { display: none; }

.btn-outline-primary { background: transparent; border-color: var(--brand-mid, var(--brand-mid)); color: var(--brand, var(--brand)); }
.btn-outline-primary:hover { background: var(--brand-light, var(--brand-light)); }
.btn-outline-secondary { background: transparent; }
.btn-outline-danger { background: transparent; border-color: var(--fail-border, var(--fail-border)); color: var(--fail-fg, var(--fail-fg)); }
.btn-outline-danger:hover { background: #dc2626; border-color: #dc2626; color: #fff; text-decoration: none; }
.btn-sm { height: 24px; padding: 0 8px; gap: 5px; font-size: 11px; }

/* ════════════════════════════════════════════════════════════════
   app-modal — UNIVERSAL modal / dialog system
   One shell, two layouts:
     • Panel   (.app-modal)           header + scrolling body + footer
     • Confirm (.app-modal-confirm)   icon + title + message + centred actions
   Tones colour the confirm icon: primary / danger / warning / success / info.
   JS helper:  AppModal.confirm() / .prompt() / .alert()  (defined in base.html)
   Footer buttons use .app-btn + a semantic variant (app-btn-danger…).
   ════════════════════════════════════════════════════════════════ */
.app-modal-overlay {
  position: fixed; inset: 0; z-index: 10050;
  display: none; align-items: center; justify-content: center;
  padding: 16px;
  background: rgba(17, 24, 39, .55);
}
.app-modal-overlay.open { display: flex; }

.app-modal {
  background: var(--surface, var(--surface));
  border: 1px solid var(--neutral-border, var(--line));
  border-radius: 14px;
  width: 100%; max-width: 460px;
  max-height: 88vh;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .30);
  overflow: hidden;
}
.app-modal-sm { max-width: 380px; }
.app-modal-lg { max-width: 680px; }

/* Header */
.app-modal-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px;
  background: var(--tbl-head-bg, var(--surface-tint));
  border-bottom: 1px solid var(--tbl-head-border, var(--accent-line-soft));
  flex-shrink: 0;
}
.app-modal-title {
  margin: 0; flex: 1;
  font-size: 15px; font-weight: 700; color: var(--text-primary, var(--text-strong));
  display: flex; align-items: center; gap: 8px;
}
.app-modal-title i { color: var(--brand, var(--accent-text)); }
.app-modal-close {
  background: none; border: none; cursor: pointer; line-height: 1;
  font-size: 16px; color: var(--text-tertiary, var(--text-faint));
  padding: 4px; border-radius: 6px;
}
.app-modal-close:hover { color: #ef4444; background: rgba(239, 68, 68, .08); }

/* Body / footer */
.app-modal-body { padding: 16px 18px; overflow-y: auto; }
.app-modal-foot {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 12px 18px;
  border-top: 1px solid var(--neutral-border, var(--line));
  flex-shrink: 0;
}
.app-modal-foot-center { justify-content: center; }

/* ── Confirm layout ── */
.app-modal-confirm { max-width: 380px; text-align: center; }
.app-modal-confirm .app-modal-body { padding: 24px 22px 4px; }
.app-modal-icon {
  width: 52px; height: 52px; margin: 0 auto 14px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px;
  background: var(--brand-light, var(--surface-accent)); color: var(--brand, var(--accent-text));
}
.app-modal-ctitle { font-size: 17px; font-weight: 700; color: var(--text-primary, var(--text-strong)); }
.app-modal-cmsg { font-size: 13px; color: var(--text-secondary, var(--text-muted)); margin-top: 8px; line-height: 1.5; }
.app-modal-cmsg:empty { display: none; }
.app-modal-confirm .app-modal-foot { border-top: none; justify-content: center; padding: 18px 22px 20px; }
.app-modal-confirm .app-modal-foot .app-btn { min-width: 120px; justify-content: center; height: 38px; }
/* Three-action confirm (Cancel / alt / OK): widen so buttons don't squeeze. */
.app-modal-confirm.app-modal-3btn { max-width: 460px; }
.app-modal-confirm.app-modal-3btn .app-modal-foot { gap: 10px; padding-left: 18px; padding-right: 18px; }
.app-modal-confirm.app-modal-3btn .app-modal-foot .app-btn { min-width: 0; flex: 1; padding-left: 8px; padding-right: 8px; white-space: nowrap; }

/* Tone → confirm-icon colours */
.app-modal-tone-danger  .app-modal-icon { background: var(--fail-bg); color: var(--danger-text); }
.app-modal-tone-warning .app-modal-icon { background: var(--warn-bg); color: #d97706; }
.app-modal-tone-success .app-modal-icon { background: var(--pass-bg); color: #059669; }
.app-modal-tone-info    .app-modal-icon { background: var(--info-bg); color: #2563eb; }

/* Inline prompt() field + validation error */
.app-modal-field { text-align: left; margin-top: 16px; }
.app-modal-field label {
  display: block; margin-bottom: 6px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-secondary, var(--text-muted));
}
.app-modal-field .app-input { width: 100%; height: 34px; }
.app-modal-error { margin-top: 10px; font-size: 12px; color: var(--danger-text-2); text-align: left; }
.app-modal-error:empty { display: none; }

/* Multi-field dialog: .app-modal-form inside .app-modal-body. One row per
   field, label left (stacks above the control on a phone). Replaces the
   catalogue's cat-dialog. Footer: .app-modal-foot with .app-btn buttons. */
.app-modal-form { display: flex; flex-direction: column; gap: 12px; }
.app-modal-form-row {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 8px 12px;
  align-items: center;
}
.app-modal-form-row--top { align-items: start; }
.app-modal-form-row--top > .app-modal-form-label { padding-top: 7px; }
.app-modal-form-label {
  font-size: var(--tb-font, 12px);
  font-weight: 600;
  color: var(--text-secondary, var(--text-muted));
}
.app-modal-form-row > .app-input,
.app-modal-form-row > .app-select,
.app-modal-form-row > .app-textarea { width: 100%; min-width: 0; }
.app-modal-form-msg {
  margin: 0;
  min-height: 1em;
  font-size: 12px;
  color: var(--text-secondary, var(--text-muted));
}
@media (max-width: 479.98px) {
  .app-modal-form-row { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .app-modal-form-row--top > .app-modal-form-label { padding-top: 0; }
}


/* ── AIDetect (ai-detect-ui.js) ───────────────────────────────────
   Shared chrome for every AI Detect surface: toast, model pills,
   dialog scaffold. Replaces the per-template pill/toast/dialog CSS
   in project_systems.html, system_builder.html and _ai_detect.html. */
/* .aid-toast — retired: AIDetect.toast delegates to AppToast (.app-toast). */

.aid-model-pill { display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: 1px; padding: 7px 12px; margin: 0 6px 6px 0; border: 1px solid #ddd;
  border-radius: 8px; background: #fafafa; cursor: pointer; text-align: left; }
.aid-model-pill--on { border-color: #0a66c2; background: var(--c-bg-e8f1fb); }
.aid-mp-name { font-size: 12.5px; font-weight: 700; color: var(--text-strong); }
.aid-mp-note { font-size: 10.5px; color: var(--text-muted); }
.aid-mp-cost { font-size: 10.5px; font-weight: 700; color: #0a7d28; margin-top: 2px; }
.aid-model-pill--on .aid-mp-name { color: #0a66c2; }

.aid-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.45);
  z-index: 99999; display: flex; align-items: center; justify-content: center; }
.aid-card { background: var(--surface); border-radius: 10px; max-width: 520px; width: 92%;
  max-height: 84vh; display: flex; flex-direction: column;
  box-shadow: 0 12px 40px rgba(0,0,0,.3); color: var(--c-text-222222); }
.aid-card--wide { max-width: 760px; }
.aid-card-head { display: flex; align-items: center; gap: 8px;
  padding: 14px 18px 10px; font-size: 16px; font-weight: 700; }
.aid-card-head i { color: #0a66c2; }
.aid-card-head span { flex: 1; }
.aid-card-x { border: none; background: none; font-size: 22px; line-height: 1;
  color: var(--text-faint); cursor: pointer; padding: 0 2px; }
.aid-card-x:hover { color: var(--text-body); }
.aid-card-body { padding: 4px 18px 18px; overflow-y: auto; }

/* ── AI Detect dialog content ─────────────────────────────────────
   Shared by every AI dialog body (project AI Scan, and any surface
   that renders rows inside AIDetect.dialog). These were 30+ inline
   styles in ai-scan-plan.js, which is why the same "note", "row" and
   "chip" looked slightly different here than in the canvas dialog.
   Buttons and badges are NOT redefined — use .app-btn / .app-badge.
   ─────────────────────────────────────────────────────────────── */
.aid-lede { margin: 0 0 10px; font-size: 12.5px; color: var(--c-text-555555); }
.aid-note { margin: 0 0 12px; font-size: 13px; color: var(--c-text-555555); }
.aid-note--warn  { color: #b35900; }
.aid-note--error { color: #b00020; }

.aid-field { margin-bottom: 10px; }
.aid-field-label { font-size: 11px; font-weight: 700; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 4px; }

.aid-row { display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 0; border-top: 1px solid var(--c-border-eeeeee); cursor: pointer; }
.aid-row--off { opacity: .65; cursor: default; }
.aid-row-icon { width: 22px; text-align: center; color: var(--accent-text-strong); flex-shrink: 0; }
.aid-row-main { flex: 1; min-width: 0; }
.aid-row-title { font-size: 13px; font-weight: 600; color: var(--text-strong); }
.aid-row-sub { font-size: 11.5px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aid-row-chips { margin-top: 3px; }
.aid-row-state { font-size: 11px; margin-top: 3px; }
.aid-row-cost { text-align: right; flex-shrink: 0;
  font-size: 11.5px; color: #0a7d28; font-weight: 700; }
.aid-cb { margin-top: 3px; flex-shrink: 0; }

.aid-chip { display: inline-block; padding: 1px 6px; margin-right: 4px;
  border-radius: 99px; background: var(--c-bg-eef2ff); color: #4338ca; font-size: 10.5px; }

.aid-link { font-size: 11px; color: #0a66c2; }

/* Run state of one row — the four colours were a JS lookup table. */
.aid-state--ok      { color: #059669; }
.aid-state--fail    { color: var(--danger-text-2); }
.aid-state--running { color: #0a66c2; }
.aid-state--queued,
.aid-state--idle    { color: var(--text-faint); }
.aid-state--done    { color: #059669; }

.aid-foot { margin-top: 14px; display: flex; align-items: center; gap: 12px; }
.aid-foot-total { flex: 1; font-size: 12.5px; color: var(--text-body); }
.aid-foot-total b { color: inherit; }
.aid-money { color: #0a7d28; }

/* Running state — AIDetect.running(). */
.aid-progress { display: flex; align-items: center; gap: 12px;
  padding: 6px 0 12px; }
.aid-progress-icon { font-size: 20px; color: var(--accent-text-strong); flex-shrink: 0; }
.aid-progress-main { flex: 1; min-width: 0; }
.aid-progress-title { font-size: 13.5px; font-weight: 600; color: var(--text-strong); }
.aid-progress-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.aid-progress-time { font-size: 12px; color: var(--text-faint);
  font-variant-numeric: tabular-nums; flex-shrink: 0; }

/* Post-run review panel (schedule rows before they are saved). */
.aid-review { margin-top: 16px; border-top: 2px solid var(--c-border-ede9fe); padding-top: 12px; }
.aid-review-title { font-size: 13px; font-weight: 700; color: var(--accent-text-strong); margin-bottom: 6px; }
.aid-review-sub { font-size: 11.5px; color: var(--text-muted); margin-bottom: 8px; }
.aid-review-list { max-height: 190px; overflow-y: auto; font-size: 12px; }
.aid-review-row { display: flex; gap: 8px; padding: 3px 0; border-bottom: 1px solid var(--line-faint); }
.aid-rr-tag  { width: 46px; }
.aid-rr-kind { width: 64px; color: var(--text-muted); }
.aid-rr-name { flex: 1; color: var(--text-body); }
.aid-rr-size { color: var(--text-muted); }
.aid-review-foot { margin-top: 10px; text-align: right; }

/* ── app-input ────────────────────────────────────────────────────
   Universal text / number input. Replaces: tb-input.
   ─────────────────────────────────────────────────────────────── */
.app-input,
.form-control {
  height: var(--tb-h, 30px);
  padding: 0 var(--tb-pad-x, 12px);
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface));
  color: var(--text-primary, var(--text-body));
  font-size: var(--tb-font, 12px);
  outline: none;
  transition: border-color .12s;
  box-sizing: border-box;
  min-width: 0;
}
.app-input::placeholder, .form-control::placeholder { color: var(--text-tertiary, var(--text-faint)); }
.app-input:hover, .form-control:hover { border-color: var(--brand-mid, var(--brand-mid)); }
.app-input:focus, .form-control:focus { border-color: var(--brand,     #8e75da); }
.app-input:disabled, .form-control:disabled { opacity: .5; cursor: not-allowed; background: var(--neutral-bg); }

/* ── app-input[type=file] ─────────────────────────────────────────
   The container's border/radius/height come from .app-input above;
   this reskins the "choose file" button and filename text the browser
   draws itself, since neither inherits page CSS by default. ─────── */
.app-input[type="file"] {
  display: inline-flex;
  align-items: center;
  padding: 0 12px 0 4px;
  color: var(--text-secondary, var(--text-secondary));
  cursor: pointer;
}
.app-input[type="file"]::file-selector-button,
.app-input[type="file"]::-webkit-file-upload-button {
  height: 22px;
  margin-right: 10px;
  padding: 0 10px;
  border: 1px solid var(--border-strong, rgba(142,117,218,.25));
  border-radius: 4px;
  background: var(--brand-light, var(--brand-light));
  color: var(--brand, var(--brand));
  font-size: var(--tb-font, 12px);
  font-weight: 600;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.app-input[type="file"]:hover::file-selector-button,
.app-input[type="file"]:hover::-webkit-file-upload-button {
  background: var(--brand, #8e75da);
  border-color: var(--brand, #8e75da);
  color: #fff;
}
.app-input[type="file"]:disabled::file-selector-button,
.app-input[type="file"]:disabled::-webkit-file-upload-button { cursor: not-allowed; }

/* ── app-select ───────────────────────────────────────────────────
   Universal <select>. Replaces: ft-filter-select, tb-select.
   ─────────────────────────────────────────────────────────────── */
.app-select,
.form-select,
select.form-control {
  height: var(--tb-h, 30px);
  padding: 0 28px 0 10px;
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface))
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'><path fill='%238e75da' d='M0 0l5 6 5-6z'/></svg>")
    no-repeat right 10px center / 8px 5px;
  appearance: none;
  color: var(--text-primary, var(--text-body));
  font-size: var(--tb-font, 12px);
  outline: none;
  transition: border-color .12s;
  box-sizing: border-box;
  cursor: pointer;
  min-width: 120px;
}
.app-select:hover, .form-select:hover, select.form-control:hover { border-color: var(--brand-mid, var(--brand-mid)); }
.app-select:focus, .form-select:focus, select.form-control:focus { border-color: var(--brand,     #8e75da); }
.app-select:disabled, .form-select:disabled, select.form-control:disabled { opacity: .5; cursor: not-allowed; }
/* `outline:none` above also removes the keyboard focus ring, which a border
   tint alone does not replace — it is invisible against a hover. Put a real
   ring back for keyboard users only, so mouse focus stays quiet. */
.app-select:focus-visible, .form-select:focus-visible, select.form-control:focus-visible {
  outline: 2px solid var(--brand, #8e75da);
  outline-offset: 1px;
}

/* ── Dropdown picker — the open list ──────────────────────────────
   Universal: applies to every <select> in the app, whatever class it
   carries (.app-select, .param-select, .flr-tbl-sel, select.app-input,
   or none at all). They all open the same list, so the list is defined
   once, here.

   The closed control has always been themed; the list it opened was not,
   because the popup is drawn by the OS and CSS cannot reach it — hence
   the system font, square corners and blue highlight.

   Two layers, in order of what a browser supports:

   1. `option` colours. Honoured by Chromium's native popup and ignored
      elsewhere, so the list at least stops being white-on-blue. The dark
      theme's `color-scheme: dark` (tokens.css) already flips the popup
      chrome to match.
   2. `appearance: base-select`. Moves the picker into the page where it
      can be styled properly, matching .app-topbar-dd__menu so the app has
      one menu language. Chrome 135+ today; browsers without it keep the
      native popup and layer 1. Progressive enhancement — nothing here is
      required for a select to work.
   ─────────────────────────────────────────────────────────────── */
/* Baseline for every <select>, so a dropdown looks like a LiteSpec dropdown
   whether or not someone remembered to add .app-select. Deliberately low
   specificity (one element selector): .app-select, .param-select and the rest
   still win on the properties they set, so this only fills gaps — it does not
   re-style controls that already made a choice. Sizing is left out for the
   same reason; dense table selects set their own. */
select {
  appearance: none;
  padding: 0 28px 0 10px;
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface))
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'><path fill='%238e75da' d='M0 0l5 6 5-6z'/></svg>")
    no-repeat right 10px center / 8px 5px;
  color: var(--text-primary, var(--text-body));
  font-family: inherit;
  font-size: var(--tb-font, 12px);
  cursor: pointer;
  transition: border-color .12s;
}
select:hover { border-color: var(--brand-mid, var(--brand-mid)); }
select:focus-visible { outline: 2px solid var(--brand, #8e75da); outline-offset: 1px; }
select:disabled { opacity: .5; cursor: not-allowed; }

select option {
  background-color: var(--surface, var(--surface));
  color: var(--text-primary, var(--text-body));
}
[data-theme="dark"] select option {
  background-color: #1e293b;
  color: #f1f5f9;
}

@supports (appearance: base-select) {
  /* Every class here already sets `appearance`, so naming them is not
     redundant — a bare `select` rule loses to them on specificity and the
     picker silently stays native. That is exactly the bug this block exists
     to fix, so the list must cover every select-styling class in the app. */
  select,
  .app-select,
  select.app-input,
  .param-select,
  .flr-tbl-sel { appearance: base-select; }

  /* We paint our own caret as a background image; drop the one base-select
     supplies so the two don't stack. */
  select::picker-icon,
  .app-select::picker-icon,
  select.app-input::picker-icon,
  .param-select::picker-icon,
  .flr-tbl-sel::picker-icon { display: none; }

  /* base-select renders the closed control as a real button, so a long value
     wrapped to a second line and spilled past the fixed height — the native
     control clipped it. `nowrap` puts the clipping back.

     Clipped, not ellipsised, and that is as far as CSS goes here: Chrome
     forces overflow:visible on a base-select <select> (the picker has to
     escape the button box) and text-overflow needs a clipping box, so it
     can never fire on the select itself. `::selectedcontent` is not a
     selectable pseudo-element either. The only route to a real ellipsis is
     authoring <button><selectedcontent></selectedcontent></button> inside
     every select, which browsers without the customizable-select parser
     hoist back out as a stray button next to the control. Clipped beats
     broken. */
  select { white-space: nowrap; }
  /* Honoured only by selects that do author the button/selectedcontent
     pattern; harmless for the rest. */
  select selectedcontent {
    display: block;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  select::picker(select) {
    font-family: inherit;
    appearance: base-select;
    background: var(--surface, var(--surface));
    border: 1px solid var(--border, var(--line));
    border-radius: 8px;
    box-shadow: 0 6px 20px rgba(0,0,0,.12);
    padding: 4px;
    /* At least as wide as the control, free to grow past it. The closed
       control truncates; the list is where you actually read the value, so
       it should not truncate too. */
    min-width: anchor-size(width);
    width: max-content;
    max-width: min(520px, 90vw);
    max-height: 320px;
    overflow: auto;
    /* No entrance transition here on purpose. Animating in would mean
       defaulting the picker to opacity:0 and relying on :popover-open to
       raise it — and if that selector ever fails to match, every dropdown in
       the app opens invisible. Not worth 120ms of polish. */
  }

  select option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: 6px;
    font-size: var(--tb-font, 12px);
    line-height: 1.4;
    color: var(--text-primary, var(--text-body));
    background: transparent;
    cursor: pointer;
    white-space: nowrap;
  }
  select option:hover,
  select option:focus {
    background: var(--brand-light, var(--brand-light));
    color: var(--accent-text-deep);
    outline: none;
  }
  select option:checked {
    background: var(--brand-light, var(--brand-light));
    color: var(--accent-text-deep);
    font-weight: 600;
  }
  /* The tick marks the current value; push it to the trailing edge so the
     labels stay left-aligned whether or not a row carries one. */
  select option::checkmark {
    order: 1;
    margin-left: auto;
    color: var(--brand, var(--brand));
  }
  select option:disabled { opacity: .5; cursor: not-allowed; }

  [data-theme="dark"] select::picker(select) {
    background: #1e293b;
    border-color: #334155;
    box-shadow: 0 6px 20px rgba(0,0,0,.4);
  }
  [data-theme="dark"] select option { color: #f1f5f9; background: transparent; }
  [data-theme="dark"] select option:hover,
  [data-theme="dark"] select option:focus,
  [data-theme="dark"] select option:checked {
    background: rgba(124,58,237,.22);
    color: #ddd6fe;
  }
}

/* ── app-search ───────────────────────────────────────────────────
   Search input with leading icon. Replaces: _search.html inline.
   ─────────────────────────────────────────────────────────────── */
.app-search {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 100%;
}
.app-search__icon {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-tertiary, var(--text-faint));
  font-size: 12px;
  pointer-events: none;
}
.app-search__input {
  height: var(--tb-h, 30px);
  padding: 0 10px 0 30px;
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface));
  color: var(--text-primary, var(--text-body));
  font-size: var(--tb-font, 12px);
  outline: none;
  transition: border-color .12s;
  box-sizing: border-box;
  width: 100%;
}
.app-search__input::placeholder { color: var(--text-tertiary, var(--text-faint)); }
.app-search__input:hover  { border-color: var(--brand-mid, var(--brand-mid)); }
.app-search__input:focus  { border-color: var(--brand,     #8e75da); }

/* ── app-checkbox / app-radio ────────────────────────────────────
   Universal checkbox/radio label. Replaces: _checkbox inline.
   ─────────────────────────────────────────────────────────────── */
.app-checkbox,
.app-radio {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--tb-font, 12px);
  font-weight: 600;
  color: var(--text-primary, var(--text-body));
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  line-height: 1.3;
}
.app-checkbox input[type="checkbox"],
.app-radio    input[type="radio"] {
  width: 14px;
  height: 14px;
  accent-color: var(--brand, #8e75da);
  cursor: pointer;
  flex-shrink: 0;
}

/* ── app-toggle ───────────────────────────────────────────────────
   CSS-only toggle switch (pill). Mark up:
   <label class="app-toggle">
     <input type="checkbox" hidden>
     <span class="app-toggle__track"><span class="app-toggle__thumb"></span></span>
     <span>Label text</span>
   </label>
   ─────────────────────────────────────────────────────────────── */
.app-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: var(--tb-font, 12px);
  font-weight: 600;
  color: var(--text-secondary, var(--text-secondary));
  user-select: none;
}
.app-toggle input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
.app-toggle__track {
  position: relative;
  width: 36px;
  height: 20px;
  border-radius: 10px;
  background: var(--neutral-border, #d1d5db);
  transition: background .15s;
  flex-shrink: 0;
}
.app-toggle__thumb {
  position: absolute;
  top: 3px; left: 3px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0,0,0,.2);
  transition: left .15s;
}
.app-toggle input:checked + .app-toggle__track { background: var(--brand, #8e75da); }
.app-toggle input:checked + .app-toggle__track .app-toggle__thumb { left: 19px; }

/* ── app-label / app-caption / app-field-group ───────────────────
   Typography and layout for topbar control groups.
   Replaces: ft-filter-section, ft-filter-section-title,
             ft-filter-section-desc, tb-label.
   ─────────────────────────────────────────────────────────────── */
.app-label {
  font-size: var(--tbl-head-font, 12px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--tbl-head-color, var(--text-muted));
  white-space: nowrap;
  line-height: 1;
}
.app-caption {
  font-size: var(--tbl-head-font, 12px);
  color: var(--tbl-head-color, var(--text-muted));
  font-weight: 400;
  opacity: .7;
  line-height: 1;
}
/* Single-row toolbar field: icon/label + control, vertically centred within
   the fixed-height .app-topbar-panel — replaces the old two-line
   title-over-description stack (each field used to carry its own header
   block, which is what made the bar read as a stack of form fields rather
   than one toolbar). The dropped description text moves to a `title=`
   tooltip on the control itself in each partial, so nothing is lost, just
   deferred to hover. */
.app-field-group {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Must stay wider than the 8px internal gap above, or a label sits as
     close to the previous field's control as to its own — the dividers
     used to mask that, and nothing masks it now. */
  padding: 0 9px;
  min-width: 0;
  flex: 1;
}
.app-field-group--shrink { flex: 0 1 auto; }
.app-field-group--grow   { flex: 1; }
/* Kept only for the two hand-rolled panels that never adopted this
   component (connection_engine, loading_sidebar) and still render the old
   stacked title+description header. Not used by any input_panel_topbar
   partial any more. */
.app-field-group__header {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.app-field-group__icon { font-size: 14px; color: var(--brand, var(--brand)); flex-shrink: 0; }

/* ── Shared sidebar list primitives (used by SmartSpec, WSF, etc.) ── */

.ss-viewall { margin-left: auto; font-size: var(--fs-md); font-weight: 600; color: rgba(0, 0, 0, 0.55); text-decoration: none; cursor: pointer; }
[data-theme="dark"] .ss-viewall { color: rgba(255, 255, 255, 0.55); }
.ss-viewall:hover { text-decoration: underline; }

.ss-list-item {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 12px;
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--secondary-sidebar-text);
  cursor: pointer;
  transition: background 0.12s;
  border-bottom: 1px solid var(--secondary-sidebar-border);
}
.ss-list-item:last-child { border-bottom: none; }
.ss-list-item:hover { background: rgba(0, 0, 0, 0.04); }
[data-theme="dark"] .ss-list-item:hover { background: rgba(255, 255, 255, 0.06); }
.ss-list-item.active { background: rgba(0, 0, 0, 0.08); font-weight: 600; }
[data-theme="dark"] .ss-list-item.active { background: rgba(255, 255, 255, 0.1); }
.ss-list-empty { font-size: var(--fs-md); color: rgba(0, 0, 0, 0.4); text-align: center; padding: 12px 10px; }
[data-theme="dark"] .ss-list-empty { color: rgba(255, 255, 255, 0.4); }

/* Dark sidebar variant (WSF) */
.ss-list-item--dark {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 12px;
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--secondary-sidebar-text);
  cursor: pointer;
  background: transparent;
  border-bottom: 1px solid var(--secondary-sidebar-border);
  transition: background 0.12s;
}
.ss-list-item--dark:hover { background: rgba(0, 0, 0, 0.04); }
[data-theme="dark"] .ss-list-item--dark:hover { background: rgba(255, 255, 255, 0.06); }
.ss-list-item--dark.is-active { background: rgba(0, 0, 0, 0.08); font-weight: 600; }
[data-theme="dark"] .ss-list-item--dark.is-active { background: rgba(255, 255, 255, 0.1); }
.ss-list-item--dark .ss-wt-del-btn { opacity: 0; transition: opacity .15s; }
.ss-list-item--dark:hover .ss-wt-del-btn { opacity: 1; }
.ss-list-item--dark .ss-wt-edit-btn { opacity: 0; }
.ss-list-item--dark:hover .ss-wt-edit-btn,
.ss-list-item--dark.is-active .ss-wt-edit-btn,
.ss-list-item--dark.is-active .ss-wt-del-btn { opacity: 1; }

/* Spinning load icon */

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.ss-wt-del-btn { opacity: 0; transition: opacity .15s; }
.ss-list-item:hover .ss-wt-del-btn { opacity: 1; }
.ss-wt-del-btn:hover { background: rgba(220, 38, 38, 0.08); }
[data-theme="dark"] .ss-wt-del-btn:hover { background: rgba(220, 38, 38, 0.15); }

/* ════════════════════════════════════════════════════
   Dark sidebar — SmartSpec utility classes
   Matches the WSF dark sidebar design system (ms-*)
   ════════════════════════════════════════════════════ */

/* Scrollable list container inside dark sidebar */
.ms-list-scroll {
  overflow-y: auto;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  flex-shrink: 0;
}
.ms-list-scroll--sm  { max-height: 140px; }
.ms-list-scroll--lg  { max-height: 280px; }
#sq-drawing-list { max-height: 550px; overflow: hidden; border-bottom: 1px solid var(--secondary-sidebar-border); }

/* Item name — truncating span */
.ms-item-name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Icon colour variants inside ss-list-item--dark */
.ss-li-ico { font-size: 9px; flex-shrink: 0; }
.ss-li-ico--muted  { color: rgba(0, 0, 0, 0.5); }
[data-theme="dark"] .ss-li-ico--muted { color: #94a3b8; }
.ss-li-ico--blue   { color: #2563eb; }
[data-theme="dark"] .ss-li-ico--blue { color: #60a5fa; }
.ss-li-ico--purple { color: var(--accent-text); font-size: 5px; }
[data-theme="dark"] .ss-li-ico--purple { color: #a78bfa; }
.ss-li-ico--lilac  { color: #a78bfa; font-size: 6px; }
[data-theme="dark"] .ss-li-ico--lilac { color: #c4b5fd; }

/* Count badge inside ms-section-header */
.ms-sec-count {
  font-size: 9px;
  font-weight: 500;
  color: #4a5070;
  margin-left: 4px;
}

/* Mini add button inside ms-section-header */
.ms-add-btn {
  margin-left: auto;
  background: rgba(108,92,231,0.15);
  border: 1px solid rgba(108,92,231,0.3);
  color: #a78bfa;
  border-radius: 4px;
  width: 18px;
  height: 18px;
  font-size: 9px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background .12s, border-color .12s, color .12s;
}
.ms-add-btn:hover { background: #6c5ce7; color: #fff; border-color: #6c5ce7; }

/* "View all" link — bottom of a dark list section */
.ms-viewall {
  display: block;
  text-align: center;
  font-size: 9px;
  font-weight: 500;
  color: #6c5ce7;
  padding: 5px 8px 6px;
  text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,0.04);
  background: rgba(108,92,231,0.05);
  flex-shrink: 0;
  transition: background .12s, color .12s;
}
.ms-viewall:hover { background: rgba(108,92,231,0.12); color: #a78bfa; text-decoration: none; }

/* Smartspec sidebar: slightly larger, more readable "View all" link */
.ms-sidebar .ms-viewall {
  font-size: 12px;
  padding: 8px 10px;
  color: #6c5ce7;
  background: transparent;
  border-bottom: 1px solid rgba(255,255,255,0.02);
}

/* Empty state inside dark list */
.ms-list-empty {
  font-size: 10px;
  color: #4a5070;
  text-align: center;
  padding: 8px 10px;
  margin: 0;
}

/* Gray system badge variant */
.ss-sys-badge--gray { background: #6d6d6d; }

/* Drawings section — hidden until project selected */
.ms-drawings-wrap { display: none; flex-shrink: 0; }
.ms-drawings-list {
  padding: 0 8px 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Project AI systems section — hidden until project selected */
.ms-project-section { display: none; flex-shrink: 0; }

/* Edit + delete action buttons group inside ss-list-item */
.ss-wt-action-btns {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}
.ss-wt-edit-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  color: var(--accent-text);
  cursor: pointer;
  font-size: 10px;
  background: transparent;
  border: none;
  opacity: 0;
  transition: background .12s, opacity .15s;
}
.ss-wt-edit-btn:hover { background: var(--surface-accent); }
/* Show both buttons only on hover OR when item is active */
.ss-list-item:hover .ss-wt-edit-btn,
.ss-list-item.active .ss-wt-edit-btn { opacity: 1; }
.ss-list-item.active .ss-wt-del-btn { opacity: 1; }
.ss-sys-badge {
  flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  height: 15px; padding: 0 6px; border-radius: 2px;
  font-size: var(--fs-2xs); font-weight: 400; color: #fff; white-space: nowrap;
  max-width: 110px; overflow: hidden; text-overflow: ellipsis;
}
.ss-cardbox {
  margin: 0 17px 8px; background: var(--surface);
  border: 0.5px solid var(--c-border-d2d2d2); border-radius: 6px; overflow: hidden; flex-shrink: 0;
}

/* ── Slide-out panel (collapses to width:0, expands on .open) ── */
.detail-panel {
  width: 0; flex-shrink: 0; overflow: hidden;
  transition: width .22s cubic-bezier(.4,0,.2,1);
  background: var(--surface);
  box-shadow: 4px 0 12px rgba(0,0,0,.10);
  display: flex; flex-direction: column; position: relative; z-index: 10;
}
.detail-panel.open { width: var(--detail-panel-width, 300px); }
.detail-panel-body {
  flex: 1; overflow-y: auto; padding: 2px 0;
}

/* System library pagination background */
#sb-lib-pagination {
  background: var(--c-bg-f2f4f8);
}

/* Centralized SmartSpec detail-panel overrides moved from smartspec.css */
/* Apply 500px width and header styling for SmartSpec panels */
#sb-lib-list,
.detail-panel-body.system-library {
  width: 100%;
  box-sizing: border-box;
}

/* Row-style choice cards — New System panel */
.sb-new-row-card {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border: 1.5px solid var(--c-border-e9e9f0); border-radius: 10px;
  background: var(--surface); cursor: pointer; text-align: left; width: 100%;
  box-sizing: border-box; transition: border-color .15s, background .15s, box-shadow .15s;
}
.sb-new-row-card:hover { border-color: var(--brand-mid); background: var(--surface-tint-2); box-shadow: 0 2px 8px rgba(124,58,237,.08); }
.sb-row-card-icon {
  width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 16px;
}
.sb-row-card-icon--lib { background: var(--info-bg-soft); color: #2563eb; }
.sb-row-card-body { flex: 1; min-width: 0; }
.sb-row-card-title { font-size: 12.5px; font-weight: 700; color: var(--c-text-1a1b2e); }
.sb-row-card-desc { font-size: 10.5px; color: var(--text-faint); margin-top: 1px; line-height: 1.35; }
.sb-row-card-chip { font-size: 9px; font-weight: 600; padding: 2px 7px; border-radius: 99px; background: var(--neutral-bg); color: var(--text-muted); border: 1px solid var(--line); }
.sb-row-card-arrow { font-size: 10px; color: #d1d5db; flex-shrink: 0; transition: color .15s, transform .15s; }
.sb-new-row-card:hover .sb-row-card-arrow { color: var(--accent-text); transform: translateX(2px); }

/* System info rows — 2-column grid */
.sb-sysinfo-val,
.sb-perf-val    { font-size: 11px; font-weight: 600; color: var(--c-text-4e4e4e); }
.sb-sysinfo-val strong,
.sb-perf-val strong { font-weight: 700; }
/* supplier brand colour applied via inline style */
.sb-sysinfo-thermal strong { background: #53a24f; color: #fff; padding: 1px 7px; border-radius: 2px; font-size: 11px; font-weight: 700; }

/* Perf value colours */
.sb-perf-val.frl   { color: var(--danger-text); }
.sb-perf-val.rw    { color: #2563eb; }
.sb-perf-val.width { color: #0891b2; }
.sb-perf-val.stud  { color: #6d6d6d; }
.sb-perf-empty { color: var(--text-faint); font-weight: 400; font-size: 11px; }

/* Info cell — labelled input field with lavender styling */
@keyframes sb-input-shake {
  0%,100% { transform: translateX(0); }
  25%      { transform: translateX(-4px); }
  75%      { transform: translateX(4px); }
}
.sys-name { font-size: var(--fs-md); font-weight:700; color:var(--text-strong); }
.sys-frl  { font-size:9px; font-weight:700; color:var(--danger-text); background:var(--fail-bg); border-radius:3px; padding:1px 5px; }
.sys-src  { font-size:9px; color:#a78bda; }

/* ── Result cells ────────────────────────────────── */
.cell-util { display:inline-block; padding:2px 8px; border-radius:99px; font-size:12px; font-weight:800; }
.cell-util.pass { background:var(--pass-bg,var(--pass-bg)); color:var(--pass-fg,var(--pass-fg)); }
.cell-util.warn { background:var(--warn-bg,var(--warn-bg)); color:var(--warn-fg,var(--warn-fg)); }
.cell-util.fail { background:var(--fail-bg,var(--fail-bg)); color:var(--fail-fg,var(--fail-fg)); }
.cell-st { font-size:10px; font-weight:700; }
.cell-st.pass { color:#059669; }
.cell-st.warn { color:#d97706; }
.cell-st.fail { color:#ef4444; }

/* ── Add custom / delete rows ────────────────────── */
.row-del { visibility:hidden; background:none; border:none; color:#f87171; cursor:pointer; padding:3px 5px; font-size:11px; border-radius:4px; }
tr:hover .row-del { visibility:visible; }

/* ── Grouped framing rows ────────────────────────── */
.ft-col-header {
  display:flex; align-items:center; background:var(--c-bg-f9f6fe);
  border-bottom:0.2px solid var(--c-border-c2c2c2); font-size:12px; font-weight:500; color:var(--c-text-4e4e4e);
}
.ft-col-header .ft-group-strip-hdr { width:28px; min-width:28px; flex-shrink:0; display:flex; align-items:center; justify-content:center; padding:0; }
.ft-col-header .ft-cell { color:var(--c-text-4e4e4e); }
.ft-group { border-bottom:1px solid var(--c-border-c8bcee); border:none; border-bottom:1px solid var(--c-border-c8bcee); border-radius:0; overflow:hidden; background:var(--surface); box-shadow:none; }
.ft-group:last-of-type { border-bottom:none; }
.ft-group-header { display:flex; flex-direction:column; border-bottom:1px solid var(--c-border-c8bcee); background:var(--c-bg-f9f6ff); border-radius:0; color:#7154aa; padding:0; position:relative; }
.ft-group-nametag { padding:10px 14px; display:flex; align-items:center; justify-content:flex-start; width:100%; box-sizing:border-box; gap:8px; min-height:44px; }
.ft-group-nametag-badge { background:var(--surface-accent); color:var(--accent-text-strong); font-size:12px; font-weight:700; padding:3px 10px; border-radius:20px; border:1px solid var(--accent-line); letter-spacing:0.02em; }
.ft-group-body { display:grid; grid-template-columns: 140px 200px 200px 1fr 1fr; align-items:flex-start; background:var(--surface); gap:12px; padding:6px 14px 14px 14px; position:relative; }
.ft-group-left { display:flex; flex-direction:column; }
.ft-group-thumbarea { flex:1; display:flex; align-items:center; justify-content:center; }
.ft-group-thumb { width:100%; background:var(--c-bg-f9f6ff); display:flex; align-items:center; justify-content:center; overflow:hidden; border-radius:4px; min-height:80px; border:1px solid var(--c-border-e5d9ff); }
.ft-group-right { display:contents; }
.ft-group-meta { font-size:12px; color:var(--text-body); display:contents; }
.meta-box { background:transparent; padding:0 12px; border-radius:0; border:none; border-right:1px solid var(--c-border-e5d9ff); }
.meta-box:last-child { border-right:none; grid-column: span 2; }
.meta-box-title { font-weight:700; color:#7154aa; font-size:11px; text-transform:uppercase; letter-spacing:.08em; margin-bottom:10px; display:block; }
.meta-box-field { display:flex; flex-direction:row; justify-content:space-between; align-items:center; gap:16px; margin-bottom:6px; font-size:11px; }
.meta-box-field:last-child { margin-bottom:0; }
.meta-box-label { font-weight:700; color:#5b5b7f; font-size:11px; white-space:nowrap; text-transform:capitalize; text-align:left; }
.meta-box-value { color:var(--text-body); font-size:11px; font-weight:500; text-align:right; flex-shrink:0; }
.ft-floor-rows { display:table; border-collapse:collapse; width:auto; }
.ft-floor-rows.collapsed { display:none; }
.ft-col-header { display:table-row; background:var(--c-bg-f9f6fe); border-bottom:1px solid var(--c-border-e5d9ff); font-size:11px; font-weight:600; color:var(--accent-text-strong); position:sticky; top:0; z-index:3; }
.ft-floor-row { display:table-row; border-bottom:1px solid var(--c-border-f3e8ff); cursor:pointer; transition:background .12s; }
.ft-floor-row:last-child { border-bottom:none; }
.ft-floor-row:hover .ft-cell,
.ft-floor-row:hover .ft-group-strip,
.ft-col-header:hover .ft-cell { background:var(--c-bg-faf8ff); }
.ft-floor-row:hover .ft-group-strip { filter:brightness(1.05); }
.ft-floor-row.selected .ft-cell { background:var(--surface-accent); }
.ft-group.ft-group-alt .ft-floor-row .ft-cell { background:var(--surface); }
.ft-group.ft-group-alt .ft-floor-row:hover .ft-cell { background:var(--c-bg-faf8ff); }
.ft-group.ft-group-alt .ft-floor-row.selected .ft-cell { background:var(--surface-accent); }
.ft-group-strip { display:table-cell; width:28px; vertical-align:middle; text-align:center; background:#7c3aed; padding:0; }
.ft-group-strip span { writing-mode:vertical-rl; transform:rotate(180deg); font-size:8px; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:#fff; }
.ft-group-strip.ext      { background:#059669; }
.ft-group-strip.party    { background:#d97706; }
.ft-group-strip.fire-ext,
.ft-group-strip.fire-int { background:#dc2626; }
.ft-group-strip.frw      { background:#7c3aed; }
.ft-col-header .ft-group-strip-hdr { display:table-cell; width:28px; vertical-align:middle; text-align:center; padding:0; }
.ft-level-circle { width:26px; height:26px; border-radius:50%; background:#fbbf24; color:#fff; font-size:12px; font-weight:700; display:inline-flex; align-items:center; justify-content:center; box-shadow:0 1px 3px rgba(251,191,36,0.3); font-family: var(--font-sans, 'Inter', sans-serif); }
.ft-level-circle.ext      { background:#059669; }
.ft-level-circle.party    { background:#d97706; }
.ft-level-circle.fire-ext,
.ft-level-circle.fire-int { background:#dc2626; }
.ft-cell         { display:table-cell; padding:8px 10px; font-size:12px; color:var(--c-text-4e4e4e); font-weight:500; white-space:nowrap; vertical-align:middle; background:var(--surface); border-right:1px solid var(--c-border-e5d9ff); font-family: var(--font-sans, 'Inter', sans-serif); }
.ft-cell:last-child { border-right:none; }
.ft-col-header .ft-cell { border-right:1px solid var(--c-border-c2b8e8); }
.ft-col-header .ft-cell:last-child { border-right:none; }
.ft-cell-level   { text-align:center; }

/* ── Framing Table per-system card ───────────────────────────────
   Shared by Stud Wall Framing and the ceiling framing tools. ── */
.ft-level { font-size:12px; font-weight:600; color:var(--text-body); white-space:nowrap; }

/* ── Detail modal ────────────────────────────────── */
#ftDetailPanel { position:fixed; inset:0; background:rgba(18,13,43,.5); z-index:800; display:none; align-items:center; justify-content:center; }
#ftDetailPanel.open { display:flex; }
.dp-modal { background:var(--surface); border-radius:12px; width:1100px; max-width:96vw; max-height:88vh; overflow:hidden; display:flex; flex-direction:column; box-shadow:0 24px 60px rgba(0,0,0,.25); }
.dp-head { display:flex; align-items:center; justify-content:space-between; padding:12px 16px; background:var(--c-bg-eee8f9); border-bottom:1px solid var(--c-border-d8ccee); flex-shrink:0; }
.dp-head span { font-weight:700; font-size:12px; color:#5b4282; }
.dp-close { background:none; border:none; cursor:pointer; color:var(--text-faint); font-size:14px; padding:0; }
.dp-body { overflow-y:auto; flex:1; display:grid; grid-template-columns:1fr 2fr; }
.dp-col { padding:14px 16px; }
.dp-col + .dp-col { border-left:1px solid var(--line-faint); }
.dp-group { margin-bottom:14px; }
.dp-group h4 { font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.07em; color:#7c5fb3; margin:0 0 6px; }
.dp-row { display:flex; justify-content:space-between; padding:4px 0; border-bottom:1px solid var(--line-faint); font-size:12px; }
.dp-lbl { color:var(--text-muted); }
.dp-val { font-weight:600; color:var(--text-strong); }

/* ── Universal detail-modal building blocks (WSF + any analysis modal) ──
   Use these classes instead of inline styles so every mode renders identically. */
/* Overview banner — full-width, spans both columns */
.dp-overview { grid-column:1/-1; display:flex; gap:14px; padding:14px 16px; background:var(--c-bg-faf8ff); border-bottom:1px solid var(--c-border-ede9fe); align-items:flex-start; }
.dp-overview-svg { flex-shrink:0; width:110px; border:1px solid var(--c-border-e0d4f7); border-radius:8px; overflow:hidden; background:var(--surface); }
.dp-overview-main { flex:1; min-width:0; }
.dp-overview-title { font-size:13px; font-weight:700; color:#3b1f6f; margin-bottom:6px; }
.dp-badges { display:flex; flex-wrap:wrap; gap:5px; margin-bottom:8px; }
.dp-meta { font-size:11px; color:var(--text-muted); margin-bottom:2px; }
.dp-meta b { font-weight:600; color:var(--text-body); }
/* Full-width section (e.g. Components) + ONE unified section heading style */
.dp-section-title,
.dp-group h4 { font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.07em; color:#7354af; margin:0 0 6px; }
/* Value variants */
.dp-val--right { text-align:right; max-width:55%; word-break:break-word; }
.dp-was { color:var(--text-faint); font-size:10px; font-weight:400; } /* the "(was …)" original hint */
/* Status text */
.dp-status { font-weight:700; }
.dp-status.pass { color:#059669; }
.dp-status.warn { color:#d97706; }
.dp-status.fail { color:#ef4444; }
.dp-status.err  { color:#f97316; }
/* Placeholders */
.dp-empty { color:var(--text-faint); font-size:12px; margin:0; }
.dp-empty.error { color:#ef4444; }
/* Footer action buttons */
.dp-actions { margin-top:14px; display:flex; flex-direction:column; gap:8px; }
.dp-action { width:100%; padding:8px 12px; border-radius:7px; font-weight:600; font-size:12px; cursor:pointer; display:flex; align-items:center; justify-content:center; gap:6px; border:1px solid transparent; }
.dp-action:hover { filter:brightness(.97); }

/* ── Detail modal tabs (matrix cell modal — Input / Output / Diagrams) ──
   Shared by every framing module (wall stud, ceilings, battens, soffit) so
   the cell modal looks identical across the suite. */
.dp-tab { border:none; background:none; cursor:pointer; padding:10px 14px 8px; font-size:12px; font-weight:600; color:#8b7fa8; border-bottom:2px solid transparent; margin-bottom:-1px; }
.dp-tab:hover { color:#5b4282; }
.dp-tab.active { color:#5b4282; border-bottom-color:#7c3aed; }

/* Limit-state group header + not-applicable combination rows */

/* ── Ceiling Picker Modal ───────────────────────── */
.cp-modal-overlay { display:none; position:fixed; inset:0; background:rgba(18,13,43,.55); z-index:9000; align-items:center; justify-content:center; }
.cp-modal-overlay.open { display:flex; }
/* Right-aligned footer for .dp-modal dialogs (Cancel / primary action). */
.cp-modal { background:var(--surface); border-radius:6px; width:520px; max-width:95vw; box-shadow:0 20px 60px rgba(0,0,0,.25); overflow:hidden; }

/* ── Modal ───────────────────────────────────────── */
.modal-field label { display:block; font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:#7c5fb3; margin-bottom:4px; }
.modal-field input, .modal-field select { width:100%; padding:6px 8px; font-size:12px; border:1px solid var(--c-border-ddd6f0); border-radius:6px; background:var(--surface); color:var(--text-body); box-sizing:border-box; }
.modal-field input:focus, .modal-field select:focus { outline:none; border-color:#a78bdb; }

/* ── Wall diagram ────────────────────────────────── */
.ft-diagram-clip { width:72px; overflow:hidden; flex-shrink:0; border-radius:5px; border:1px solid var(--c-border-ede9fe); background:var(--c-bg-faf8ff); }
.ft-diagram-inner { zoom:0.46; width:156px; line-height:0; }
#cmPreviewBox { width:80px; height:120px; overflow:hidden; position:relative; flex-shrink:0; border:1px solid var(--line); border-radius:6px; background:var(--c-bg-f9f7ff); display:flex; align-items:center; justify-content:center; }

/* ── Spinner ─────────────────────────────────────── */
.ft-spinner { display:inline-block; width:14px; height:14px; border:2px solid rgba(124,58,237,.3); border-top-color:#7c3aed; border-radius:50%; animation:spin .6s linear infinite; vertical-align:middle; }

/* ══════════════════════════════════════════════════════════════
   Workspace Selector Sidebar — light theme  (.ms-*)
   Mirrors the dark .ws-sb-* system in workspace_selector.html
   but uses light colours for in-module secondary sidebars.
   Usage: wrap content in .ms-sidebar and use child classes below.
   ══════════════════════════════════════════════════════════════ */

/* Outer container — fills the secondary sidebar shell */
.ms-sidebar {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  background: var(--secondary-sidebar-bg);
  color: var(--secondary-sidebar-text);
}

/* ── Header ── */
.ms-back {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 11px;
  padding: 6px 10px;
  width: 100%;
  box-sizing: border-box;
  border-radius: 7px;
  border: 1px solid var(--c-border-ddd6f4);
  font-size: 11px;
  font-weight: 600;
  color: #64748b;
  background: rgba(255,255,255,.8);
  cursor: pointer;
  text-decoration: none;
  transition: background .14s, color .14s, border-color .14s;
}
.ms-back:hover { background: var(--surface); color: var(--accent-text-strong); border-color: var(--brand-mid); }

/* -- Unified Header with Back Button -- */
.ms-unified-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  flex-shrink: 0;
  background: var(--secondary-sidebar-bg);
  border-bottom: 1px solid var(--secondary-sidebar-border);
}

/* -- Section header bar -- */
.ms-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.55);
  padding: 10px 12px 6px;
  flex-shrink: 0;
  border-bottom: 1px solid var(--secondary-sidebar-border);
}
[data-theme="dark"] .ms-section-header {
  color: rgba(255, 255, 255, 0.55);
}

/* -- Dark form helpers -- */
.ms-sidebar .ls-select { background: var(--secondary-sidebar-bg); color: var(--secondary-sidebar-text); border-color: var(--secondary-sidebar-border); }
.ms-sidebar .ls-select-icon { color: rgba(0, 0, 0, 0.5); }
[data-theme="dark"] .ms-sidebar .ls-select-icon { color: rgba(255, 255, 255, 0.5); }

.ms-back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 7px;
  border: 1px solid var(--secondary-sidebar-border);
  background: transparent;
  color: var(--secondary-sidebar-text);
  text-decoration: none;
  font-size: 12px;
  font-weight: 600;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  white-space: nowrap;
  opacity: 0.7;
}

.ms-back-link:hover {
  opacity: 1;
  background: rgba(108, 92, 231, 0.08);
  border-color: var(--brand-mid);
  color: var(--accent-text);
}

[data-theme="dark"] .ms-back-link {
  color: rgba(229, 231, 235, 0.7);
}

[data-theme="dark"] .ms-back-link:hover {
  color: #a78bfa;
  background: rgba(167, 139, 250, 0.1);
  border-color: rgba(167, 139, 250, 0.4);
  opacity: 1;
}

.ms-back-icon {
  font-size: 12px;
}

.ms-title-center {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ms-title-text {
  margin: 0;
  color: var(--secondary-sidebar-text);
  font-size: 14px;
  font-weight: 500;
}

.ms-collapse-btn {
  background: transparent;
  border: 1px solid var(--secondary-sidebar-border);
  border-radius: 6px;
  padding: 6px 10px;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s;
  color: rgba(0, 0, 0, 0.6);
}

.ms-collapse-btn:hover {
  background: rgba(0, 0, 0, 0.05);
  color: rgba(0, 0, 0, 0.8);
}

[data-theme="dark"] .ms-collapse-btn {
  color: rgba(229, 231, 235, 0.6);
}

[data-theme="dark"] .ms-collapse-btn:hover {
  background: rgba(255, 255, 255, 0.05);
  color: rgba(229, 231, 235, 0.8);
}

.ms-collapse-icon {
  font-size: 14px;
}

/* ── Project mini card ── */
.ms-proj {
  padding: 12px 14px;
  flex-shrink: 0;
  background: #141720;
  border-bottom: 1px solid #334155;
}

/* Status badges */
.ms-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 99px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
}

/* ── Module nav list ── */

.ms-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  border-radius: 9px;
  cursor: pointer;
  margin-bottom: 2px;
  border: 1px solid transparent;
  text-decoration: none;
  transition: background .13s, border-color .13s;
}
.ms-item:hover { background: rgba(0,0,0,0.04); border-color: rgba(108,92,231,0.2); }
.ms-item.active { background: rgba(108,92,231,0.1); border: 1px solid rgba(108,92,231,0.3); box-shadow: none; }
[data-theme="dark"] .ms-item:hover { background: rgba(255,255,255,0.04); }
[data-theme="dark"] .ms-item.active { background: #3a2557; border-color: rgba(108,92,231,0.4); }

.ms-ico {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: 8px;
  background: #6c5ce7;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 12px;
  transition: background .13s, color .13s;
}
.ms-item.active .ms-ico {
  background: #6c5ce7;
  color: #fff;
  border-color: transparent;
  box-shadow: none;
}

/* ── Analysis / module-specific section ── */
.ms-section {
  border-top: 1px solid #334155;
  flex-shrink: 0;
  padding: 10px 12px;
}
.ms-section-lbl {
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.55);
  margin-bottom: 0;
  display: flex;
  align-items: center;
  gap: 4px;
}
[data-theme="dark"] .ms-section-lbl {
  color: rgba(255, 255, 255, 0.55);
}

/* ── Structural Actions: Floors panel (.flr-*) ── */
#floor-panel {
  --detail-panel-width: 420px;
  box-shadow: 4px 0 12px rgba(0,0,0,.10);
}

#floor-list-viewer {
  flex: 1;
  overflow-y: auto;
  padding: 0;
  display: block;
  margin-top: 32px;
}

.flr-empty {
  color: var(--text-muted);
  font-size: 12px;
  margin: 0;
  padding: 12px;
}

/* ════════════════════════════════════════════════════
   App Layout — CSS Grid shell
   app-layout > app-sidebar | app-topbar
                            | app-workspace
   ════════════════════════════════════════════════════ */

.app-layout {
  display: grid;
  height: 100vh;
  overflow: hidden;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  transition: grid-template-columns 0.2s ease;
}

/* Sidebar collapsed — grid column shrinks, sidebar content clips */
.app-layout:has(.app-sidebar--collapsed) {
  grid-template-columns: 56px minmax(0, 1fr);
}

/* Embedded mode (workspace iframes): no global nav column at all. The topbar +
   workspace default to grid-column 2 (column 1 is normally the nav); with the
   nav gone they must move to column 1 or a phantom gutter appears. */
.app-layout--embedded { grid-template-columns: minmax(0, 1fr); }
.app-layout--embedded .app-topbar,
.app-layout--embedded .app-workspace { grid-column: 1; }
/* When a module still ships a secondary slide-panel (e.g. stud ceiling), keep
   it as the left column and push the content right. */
.app-layout--embedded:has(> .slide-panel) {
  grid-template-columns: var(--secondary-sidebar-width) minmax(0, 1fr);
}
.app-layout--embedded:has(> .slide-panel) .app-topbar,
.app-layout--embedded:has(> .slide-panel) .app-workspace { grid-column: 2; }
.app-layout--embedded .slide-panel { grid-column: 1; }

/* Workspace embed with chrome=0 (core/embed.py, base.html .embed-bare): the
   workspace ribbon and header already carry the page title, back link and
   project name, so the module's own copies go. Its working UI is untouched. */
.embed-bare .page-header,
.embed-bare .page-back-btn,
.embed-bare .app-topbar-bar,
.embed-bare .app-topbar--tool { display: none; }
/* ...and so does a topbar panel left with nothing visible in it (Loading's panel
   holds only a project picker the workspace hides, plus buttons the ribbon now
   carries): it was an empty 48px strip under the ribbon. Hidden controls stay in
   the DOM, so a page that reads them still works. A panel with any visible
   control keeps its row. */
.embed-bare .app-topbar-panel:not(:has(:is(button, a, input, select, textarea, .app-btn):not(.eng-hidden, .eng-hidden *))) { display: none; }

/* Slide panel present — add a centre column */
.app-layout:has(> .slide-panel) {
  grid-template-columns: var(--sidebar-w) var(--secondary-sidebar-width) minmax(0, 1fr);
}
.app-layout:has(> .slide-panel) .app-topbar { grid-column: 3; }
.app-layout:has(> .slide-panel) .app-workspace { grid-column: 3; }
.slide-panel { grid-column: 2; grid-row: 1 / -1; }

/* Collapsed sidebar + slide panel */
.app-layout:has(.app-sidebar--collapsed):has(> .slide-panel) {
  grid-template-columns: 56px var(--secondary-sidebar-width) minmax(0, 1fr);
}

/* App sidebar — spans full height in column 1 */
.app-sidebar {
  grid-column: 1;
  grid-row: 1 / -1;
  background: var(--c-bg-f8f7f7);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  padding: 0 0 1rem;
  position: relative;
  z-index: 10;
  overflow: hidden;
}

/* App topbar — header row in column 2, wraps app-topbar-bar + optional app-topbar-panel */
.app-topbar {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  min-width: 0;
}

/* App workspace — scrollable content area in column 2 row 2 */
.app-workspace {
  grid-column: 2;
  grid-row: 2;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Right-rail variant — place as last child of .app-layout, fill with .ms-sidebar */
.app-sidebar--right {
  border-right: none;
  border-left: 1px solid var(--border);
  padding: 0;
  z-index: 9;
}

/* ── Collapsed state — grid-template-columns handles the width ── */
.app-sidebar--collapsed {
  /* width controlled by .app-layout:has(.app-sidebar--collapsed) */
}

.app-sidebar--collapsed .app-sidebar-logo {
  padding: 0.5rem 0 0.75rem;
  cursor: pointer;
}

.app-sidebar--collapsed .app-sidebar-logo img {
  width: 32px;
}

.app-sidebar-logo-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.app-sidebar--collapsed .app-sidebar-logo-text,
.app-sidebar--collapsed .app-sidebar-collapse-btn,
.app-sidebar--collapsed .app-nav-lbl,
.app-sidebar--collapsed .app-nav-badge {
  display: none;
}

/* Collapsed: a group label becomes a thin divider so the icon groups stay apart. */
.app-sidebar--collapsed .app-nav-sec {
  height: 0;
  margin: 6px 8px;
  padding: 0;
  overflow: hidden;
  font-size: 0;
  border-top: 1px solid var(--border);
}
.app-sidebar--collapsed .app-nav-sec:first-child {
  display: none;
}

/* Locked (Pro) items keep a small dot on the icon when the PRO tag is hidden. */
.app-sidebar--collapsed .app-nav-item--locked::after {
  content: "";
  position: absolute;
  top: 6px;
  right: 8px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand);
  border: 1.5px solid var(--c-border-f8f7f7);
}

.app-sidebar--collapsed .app-nav {
  padding: 0.5rem 6px;
}

.app-sidebar--collapsed .app-nav-item {
  padding: 9px;
  justify-content: center;
  gap: 0;
}

.app-sidebar--collapsed .app-nav-item i {
  width: auto;
}

.app-sidebar--collapsed .app-sidebar-footer {
  padding: 0.4rem;
}

.app-sidebar--collapsed .app-user {
  padding: 6px;
  justify-content: center;
  gap: 0;
}

/* ── Sidebar head: website (home) button at the left, collapse toggle at the right ── */
.app-sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px 0;
}
.app-sidebar--collapsed .app-sidebar-head {
  justify-content: center;
  padding: 10px 0 0;
}

.app-sidebar-home,
.app-sidebar-collapse-btn {
  width: 32px;
  height: 32px;
  box-sizing: border-box;
  background: var(--surface, var(--surface));
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.app-sidebar-home:hover {
  background: var(--brand-light);
  color: var(--brand);
  border-color: var(--brand);
}
.app-sidebar-home:focus-visible,
.app-sidebar-collapse-btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.app-sidebar-collapse-btn:hover {
  background: var(--brand-light);
  color: var(--brand);
  border-color: var(--brand);
}

/* ════════════════════════════════════════════════════
   Sliding secondary panel (slide-panel)
   Used in dbs layouts where the ms-sidebar slides in
   alongside the main dbs-side nav.
   ════════════════════════════════════════════════════ */
.slide-panel {
  width: var(--sidebar-w);
  flex-shrink: 0;
  overflow: hidden;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  transition: width 0.22s ease, opacity 0.22s ease;
  opacity: 1;
  background: var(--secondary-sidebar-bg, var(--c-bg-f8f7fa));
}

.slide-panel--closed {
  width: 0;
  opacity: 0;
  border-right: none;
}

/* ── Slide panel inner shell (secondary_sidebar.html) ── */
.sp-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.app-sidebar-logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0.25rem 1rem 1rem;
  border-bottom: 1px solid var(--border);
}

.app-sidebar-logo img {
  width: 80px;
  height: auto;
  display: block;
  filter: drop-shadow(0 8px 16px rgba(18, 13, 41, 0.18));
}

.topbar-title {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 29px;
  font-weight: 500;
  color: var(--text-primary);
  margin-top: 10px;
  text-align: center;
  letter-spacing: 0.12em;
  text-shadow: 0px 3px 4px rgba(0, 0, 0, 0.1);
}

.topbar-lite {
  color: #8b7dba;
  text-shadow: 0px 3px 4px rgba(0, 0, 0, 0.1);
}

.topbar-spec {
  color: var(--text-primary);
  margin-left: 0px;
  text-shadow: 0px 3px 4px rgba(0, 0, 0, 0.1);
}

.topbar-subtitle {
  font-size: 9px;
  font-family: 'Inter', sans-serif;
  color: #2e1860;
  opacity: 1;
  letter-spacing: 0.05em;
  margin-top: 6px;
  text-transform: uppercase;
  font-weight: 400;
  text-shadow: 0px 3px 4px rgba(0, 0, 0, 0.1);
}

.app-nav {
  flex: 1;
  padding: 1rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
}

.app-nav-sec {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.5rem 0.5rem 0.25rem;
  margin-top: 0.5rem;
}

.app-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 13.5px;
  font-weight: 400;
  transition: background 0.12s, color 0.12s;
  text-decoration: none;
  position: relative;
}

.app-nav-item:hover {
  background: var(--brand-light);
  color: var(--brand);
}

.app-nav-item.active {
  background: var(--brand-light);
  color: var(--brand);
  font-weight: 500;
}

.app-nav-item i {
  font-size: 16px;
  flex-shrink: 0;
  width: 18px;
  text-align: center;
}

.app-nav-badge {
  margin-left: auto;
  background: var(--brand);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 20px;
}

/* DBS Topbar */
.app-topbar-bar {
  height: var(--topbar-h);
  background: var(--c-bg-f8f7f7);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 1.75rem;
  gap: 12px;
  flex-shrink: 0;
}

.app-topbar-title {
  font-size: 15px;
  font-weight: 500;
  color: var(--text-primary);
  flex: 1;
  /* A long title (plus its subtitle) wraps to at most two lines inside the fixed
     topbar height and is cut with an ellipsis; the full text is the title attribute. */
  min-width: 0;
  line-height: 1.25;
  overflow: hidden;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.app-topbar-sub {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-left: 6px;
  font-weight: 400;
}

.app-topbar-kbd {
  margin-left: auto;
  font-size: 10px;
  color: var(--text-tertiary);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  padding: 1px 5px;
  border-radius: 4px;
}

.app-icon-btn {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text-secondary);
  transition: background 0.12s, border-color 0.12s;
  position: relative;
}

.app-icon-btn:hover {
  background: var(--bg);
  border-color: var(--border-strong);
}

.app-icon-btn i {
  font-size: 16px;
}

/* Live unread badge for the app-shell notification bell. Hidden when the
   reviews:notification_count endpoint returns an empty string (zero unread). */
.app-notif-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 999px;
  background: #ef4444;
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--surface);
}

/* Bell dropdown: panel, empty state and the "Open inbox" footer link. */
.app-notif { position: relative; flex-shrink: 0; }
.app-notif-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  width: 320px;
  max-width: calc(100vw - 24px);
  background: var(--surface, var(--surface));
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .12);
  overflow: hidden;
  z-index: 200;
}
.app-notif-list { max-height: 360px; overflow-y: auto; }
.app-notif-empty {
  padding: 32px 16px;
  text-align: center;
  font-size: 13px;
  color: var(--text-secondary);
}
.app-notif-empty i { display: block; margin-bottom: 8px; font-size: 22px; }
.app-notif-foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  font-weight: 600;
  color: var(--brand);
  text-decoration: none;
}
.app-notif-foot:hover { background: var(--brand-light); }
.app-notif-foot:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.app-notif-badge:empty { display: none; }
.app-notif-badge:not(:empty) { display: flex; }

.app-btn-new {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--brand);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 0 14px;
  height: 34px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s;
  font-family: inherit;
  text-decoration: none;
  letter-spacing: -0.1px;
}

.app-btn-new:hover {
  background: var(--brand-dark);
  color: #fff;
}

.app-btn-new i {
  font-size: 14px;
}

/* ── Sidebar Footer & User trigger ── */
.app-sidebar-footer {
  padding: 0.6rem 0.75rem 0.9rem;
  border-top: 1px solid var(--border);
  position: relative; /* anchor for .app-user-menu */
}

.app-user {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.12s;
  text-decoration: none;
  /* button resets */
  background: none;
  border: none;
  width: 100%;
  font-family: inherit;
  text-align: left;
}

.app-user:hover {
  background: var(--surface-2);
}

.app-user-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.app-user-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-user-company {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-user-role {
  font-size: 10px;
  color: var(--text-tertiary);
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.app-user i {
  margin-left: auto;
  color: var(--text-tertiary);
  font-size: 15px;
}

/* ── Sidebar user popup menu ── */
.app-user-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 -4px 20px rgba(0, 0, 0, .10), 0 2px 8px rgba(0, 0, 0, .06);
  overflow: hidden;
  z-index: 200;
}

/* Hide popup when sidebar is collapsed */
.app-sidebar--collapsed .app-user-menu {
  display: none !important;
}

.app-user-menu-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px 12px;
  background: var(--surface-2);
}

.app-user-menu-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  letter-spacing: 0.04em;
}

.app-user-menu-info {
  flex: 1;
  min-width: 0;
}

.app-user-menu-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-user-menu-company {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 1px;
}

.app-user-menu-email {
  font-size: 11px;
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 1px;
}

.app-user-menu-badge {
  display: inline-block;
  margin-top: 5px;
  padding: 2px 7px;
  border-radius: 20px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.03em;
}

.app-user-menu-badge--admin {
  background: var(--warn-bg);
  color: var(--warn-fg);
}

.app-user-menu-badge--staff {
  background: var(--surface-accent);
  color: var(--accent-text-deep);
}

.app-user-menu-badge--user {
  background: var(--surface-2);
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

.app-user-menu-divider {
  height: 1px;
  background: var(--border);
}

.app-user-menu-items {
  padding: 6px;
}

.app-user-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 13px;
  color: var(--text-secondary);
  text-decoration: none;
  transition: background 0.12s, color 0.12s;
  cursor: pointer;
  width: 100%;
  border: none;
  background: transparent;
  font-family: inherit;
  text-align: left;
}

.app-user-menu-item i {
  width: 16px;
  text-align: center;
  font-size: 13px;
  color: var(--text-tertiary);
  flex-shrink: 0;
  transition: color 0.12s;
}

.app-user-menu-item:hover {
  background: var(--surface-2);
  color: var(--text-primary);
}

.app-user-menu-item:hover i {
  color: var(--brand);
}

.app-user-menu-item--danger {
  color: var(--danger-text);
}

.app-user-menu-item--danger i {
  color: var(--danger-text);
}

.app-user-menu-item--danger:hover {
  background: var(--fail-bg-soft);
  color: var(--danger-text-2);
}

.app-user-menu-item--danger:hover i {
  color: var(--danger-text-2);
}

/* Scrollable inner content area (used by pages that need padding + scroll) */
.app-workspace-inner {
  flex: 1;
  overflow-y: auto;
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.app-workspace-inner::-webkit-scrollbar {
  width: 5px;
}

.app-workspace-inner::-webkit-scrollbar-track {
  background: transparent;
}

.app-workspace-inner::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 10px;
}

/* DBS Greeting */
.app-greeting {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
}

.app-greeting h1 {
  font-size: 22px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.5px;
}

.app-greeting p {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 3px;
}

.app-date-pill {
  font-size: 11px;
  color: var(--text-tertiary);
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 4px 10px;
  border-radius: 20px;
  white-space: nowrap;
}

/* DBS Stats */
.app-stats {
  display: grid;
  /* auto-fit, not repeat(5, 1fr): five quota cards fit across a desktop and
     reflow to 3+2 then 2+2+1 without a media query per breakpoint. */
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
}

.app-stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem 1rem;
  position: relative;
  overflow: hidden;
  transition: border-color 0.15s;
}

.app-stat:hover {
  border-color: var(--border-strong);
}

.app-stat::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--brand-light);
}

.app-stat-icon {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: var(--brand-light);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.75rem;
}

.app-stat-icon i {
  color: var(--brand);
  font-size: 15px;
}

.app-stat-lbl {
  font-size: 11px;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
}

.app-stat-val {
  font-size: 26px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.8px;
  line-height: 1;
}

.app-stat-val span {
  font-size: 14px;
  color: var(--text-tertiary);
  font-weight: 400;
}

.app-stat-sub {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 4px;
}

.app-stat-bar {
  height: 3px;
  background: var(--bg);
  border-radius: 2px;
  margin-top: 12px;
}

.app-stat-fill {
  height: 100%;
  border-radius: 2px;
  background: var(--brand);
  transition: width 0.6s ease;
}

/* A spent allowance should be readable at a glance, not just a full bar. */
.app-stat--maxed {
  border-color: var(--danger-border, var(--c-border-fecaca));
}

.app-stat--maxed::after {
  background: var(--danger, #ef4444);
}

.app-stat--maxed .app-stat-fill {
  background: var(--danger, #ef4444);
}

.app-stat--maxed .app-stat-sub {
  color: var(--danger, #ef4444);
}

/* DBS Section Header */
.app-sec-hd {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.app-sec-hd h2 {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
}

.app-viewall {
  font-size: 12px;
  color: var(--brand);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 3px;
}

.app-viewall:hover {
  opacity: 0.75;
}

/* DBS App Tiles */

.app-tile-icon {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--brand-light);
  display: flex;
  align-items: center;
  justify-content: center;
}

.app-tile-icon i {
  color: var(--brand);
  font-size: 18px;
}

.app-tile-name {
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 400;
  line-height: 1.2;
}

/* DBS Bottom Panels */
.app-panels {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr;
  gap: 14px;
}

.app-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.app-panel-hd {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 13px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.app-panel-hd h3 {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
}

/* DBS Project Row */
.app-proj-row {
  display: flex;
  align-items: center;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.1s;
  gap: 10px;
  text-decoration: none;
}

.app-proj-row:last-child {
  border-bottom: none;
}

.app-proj-row:hover {
  background: var(--surface-2);
}

.app-proj-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand);
  flex-shrink: 0;
  margin-top: 1px;
}

.app-proj-info {
  flex: 1;
  min-width: 0;
}

.app-proj-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-proj-loc {
  font-size: 11px;
  color: var(--text-tertiary);
  margin-top: 1px;
}

.app-proj-meta {
  text-align: right;
  flex-shrink: 0;
}

.app-proj-date {
  font-size: 11px;
  color: var(--text-tertiary);
}

.app-proj-state {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  font-weight: 500;
  padding: 2px 7px;
  border-radius: 20px;
  margin-top: 3px;
}

.app-proj-state.active {
  background: var(--success-bg);
  color: var(--success);
}

/* Completed / on-hold projects read differently from live ones — the row used
   to say "Active" for every status regardless. */
.app-proj-state.done {
  background: var(--brand-light);
  color: var(--brand);
}

.app-proj-state.held {
  background: var(--neutral-bg);
  color: var(--text-muted);
}

.app-panel-cta {
  padding: 12px 16px;
  border-top: 1px solid var(--border);
}

.app-btn-inline {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--brand);
  border: 1px solid var(--border-strong);
  padding: 5px 12px;
  border-radius: 7px;
  cursor: pointer;
  background: transparent;
  font-family: inherit;
  transition: background 0.12s;
  font-weight: 500;
  text-decoration: none;
}

.app-btn-inline:hover {
  background: var(--brand-light);
  color: var(--brand);
}

.app-btn-inline i {
  font-size: 13px;
}

/* DBS Empty State */
.app-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.5rem;
  text-align: center;
  gap: 8px;
}

.app-empty-icon {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 4px;
}

.app-empty-icon i {
  font-size: 20px;
  color: var(--text-tertiary);
}

.app-empty p {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* DBS Inbox Row */
.app-inbox-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.1s;
}

.app-inbox-row:last-child {
  border-bottom: none;
}

.app-inbox-row:hover {
  background: var(--surface-2);
}

/* ═══════════════════════════════════════════════════════════
   SmartQuote — Session Setup Modal  (.sq-sm-*)
   ═══════════════════════════════════════════════════════════ */

/* Backdrop */
.sq-sm-backdrop {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(22, 10, 50, 0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 8px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.22s ease;
}
.sq-sm-backdrop.sq-sm-open {
  opacity: 1; pointer-events: all;
}

/* Header */
.sq-sm-hd {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px 16px;
  border-bottom: 1px solid var(--c-border-ede9fe);
  flex-shrink: 0;
}
.sq-sm-hd-left { display: flex; align-items: center; gap: 12px; }
.sq-sm-hd-icon {
  width: 40px; height: 40px; border-radius: 10px;
  background: linear-gradient(135deg, #7354af, #a78bfa);
  color: #fff; font-size: 17px;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.sq-sm-hd-title { font-size: 16px; font-weight: 700; color: #2d1b69; line-height: 1.2; }
.sq-sm-hd-sub   { font-size: 12px; color: #7c6aaa; margin-top: 2px; }
.sq-sm-close-btn {
  width: 34px; height: 34px; border-radius: 8px; border: none;
  background: var(--surface-tint); color: #7c6aaa; font-size: 18px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background 0.15s, color 0.15s;
}
.sq-sm-close-btn:hover { background: var(--surface-accent); color: var(--accent-text-deep); }

/* Step panels inside the modal body — animated show/hide */

/* ─── end SmartQuote Session Setup Modal ─── */

/* ════════════════════════════════════════════════════
   SmartQuote Landing — Floor-anchored workflow
   Floors & Sessions table, Final BOQ List, compact picker
   dialogs. Reuses primary-table, insp-icon-btn, app-btn,
   app-badge, and the .sq-sm-backdrop overlay.
   ════════════════════════════════════════════════════ */
.sq-l-wrap { padding: 1.5rem 1.5rem 3rem; display: flex; flex-direction: column; gap: 1.5rem; }
.sq-l-section-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 10px;
}
.sq-l-section-title {
  font-size: 14px; font-weight: 700; color: #2d1b69;
  display: flex; align-items: center; gap: 8px;
}
.sq-l-section-title i { color: var(--brand); font-size: 13px; }

/* Floor → session expand caret (floor rows are primary-table rows) */
.sq-l-caret {
  background: none; border: none; cursor: pointer; color: var(--accent-text);
  width: 22px; height: 22px; border-radius: 5px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; transition: background .12s, transform .15s;
}
.sq-l-caret:hover { background: var(--surface-accent); }
.sq-l-caret.open { transform: rotate(90deg); }

/* ── Floors & Sessions: collapsible cards ────────────────────────────────
   Replaces the old table. A floor and a session share no columns, so the
   session row had to span the whole grid and lay itself out with flexbox
   anyway. As cards, a session can carry its own detail without negotiating
   with the floor's columns. (The BOQ stays a real table.) */
.sq-fc-list { display: flex; flex-direction: column; gap: 8px; }
.sq-fc {
  background: var(--surface); border: 1px solid var(--c-border-ece8f5); border-radius: 12px;
  overflow: hidden; transition: border-color .12s, box-shadow .12s;
}
.sq-fc:hover { border-color: var(--c-border-ddd4f3); }
.sq-fc.open { border-color: var(--c-border-d6cbef); box-shadow: 0 1px 3px rgba(76, 46, 138, .06); }
.sq-fc-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; cursor: pointer; user-select: none;
}
.sq-fc-head:hover { background: var(--surface-tint-2); }
.sq-fc-title { font-size: 13.5px; font-weight: 600; color: var(--text-strong); flex-shrink: 0; }
.sq-fc-meta { font-size: 12px; color: #9189a8; flex-shrink: 0; }
/* Chips take the slack so the count and action stay right-aligned. */
.sq-fc-chips { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; gap: 5px; }
.sq-fc-act { flex-shrink: 0; }
.sq-fc-body { display: none; border-top: 1px solid var(--c-border-f1edfa); }
.sq-fc.open .sq-fc-body { display: block; }
.sq-fc-empty {
  padding: 10px 14px 10px 40px; font-size: 12px; color: var(--text-faint); font-style: italic;
}

/* Session card inside a floor */
.sq-sc {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px 8px 40px; background: var(--surface-tint-2);
  border-bottom: 1px solid var(--c-border-f1edfa);
}
.sq-sc:last-child { border-bottom: none; }
.sq-sc:hover { background: var(--c-bg-f5f2ff); }
.sq-sc-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sq-sc-meta { font-size: 11px; color: #9189a8; white-space: nowrap; }

/* Session actions. The primary action carries a word — a row of five bare
   glyphs gives no clue which one opens the drawing. The rest stay icons,
   grouped by consequence: edit-ish in the middle, destructive past a rule. */
.sq-sc-open { height: 26px; padding: 0 10px; font-size: 12px; }
.sq-sc-sep {
  width: 1px; height: 16px; background: var(--c-bg-e5e0f2); flex-shrink: 0; margin: 0 2px;
}
/* Already done / already in the BOQ: state shown on the control itself, so the
   row says what has happened rather than only what you may do. */
.sq-l-actions .app-btn-icon.is-done { color: #10b981; opacity: 1; }
.sq-l-actions .app-btn-icon.is-done:disabled { pointer-events: none; }
.sq-l-actions .app-btn-icon.is-on { color: var(--accent-text); }

/* Referenceable short session id (var(--font-sans, 'Inter', sans-serif)) — replaces the verbose name/badge */
.sq-l-session-id {
  font-family: var(--font-sans, 'Inter', sans-serif);
  font-size: 12.5px; font-weight: 700; letter-spacing: .03em;
  color: #4338ca; text-decoration: none; white-space: nowrap;
}
.sq-l-session-id:hover { text-decoration: underline; }
.sq-l-session-meta { font-size: 11px; color: #9189a8; white-space: nowrap; }
.sq-l-mod-ico { color: var(--brand); font-size: 11px; }
.sq-l-ico-pdf { color: var(--danger-text); }
.sq-l-ico-floor { color: var(--brand); }
.sq-l-actions { display: flex; align-items: center; gap: 5px; flex-shrink: 0; }

/* Floor component-confirmation zone — a clearly bounded panel, set apart from the
   sessions above it by a divider, inset margin and a faint tint. */
.sq-l-floorcomp-wrap {
  margin: 8px 14px 12px;
  padding: 12px 14px 14px;
  border: 1px solid var(--c-border-e7e0f5);
  border-top: 2px solid var(--c-border-d9cff0);
  border-radius: 10px;
  background: var(--c-bg-faf8fe);
}
.sq-floor-comp { display: flex; flex-direction: column; gap: 14px; }
.sq-fc-title {
  font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-muted); display: flex; align-items: center; gap: 6px;
}
.sq-fc-title i { color: var(--brand); }
.sq-fc-module {
  border: 1px solid var(--c-border-ece8f5); border-radius: 10px; padding: 10px 12px; background: var(--surface);
}
.sq-fc-module-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 10px; flex-wrap: wrap;
}
.sq-fc-module-name { font-size: 13px; font-weight: 700; color: #2d1b69; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.sq-fc-module-name > i { color: var(--brand); font-size: 12px; }
.sq-fc-calc { font-size: 12px; font-weight: 500; color: #7c6aaa; }

/* Staged lock controls: framing → components → session */
.sq-fc-locksteps { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sq-fc-stage {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 700; padding: 4px 9px; border-radius: 6px;
}
.sq-fc-stage i { font-size: 10px; }
.sq-fc-stage--done { background: var(--c-bg-ecfdf5); color: #047857; border: 1px solid var(--pass-border); }
.sq-fc-unlock { font-size: 11px; }
/* Locked stages render read-only. Framing-only lock freezes just the framing
   inputs; a components lock (which implies framing locked) freezes the whole table. */
.sq-fc-flocked .wsf-manual,
.sq-fc-flocked .wsf-fdetails { opacity: .6; pointer-events: none; }
.sq-fc-clocked .wsf-list { opacity: .65; pointer-events: none; }
/* ── Landing polish: Floors & Sessions table ─────────────────────────────── */
/* Floor name stays neutral; readiness is shown with a subtle left accent + row
   hover rather than recolouring the whole label green. */
/* Framing chips: softer fill, comfortable inline rhythm. */
.sq-floor-fr { background: var(--c-bg-f1eefb); color: #5b4596; font-weight: 600; }
.sq-floor-fr i { opacity: .8; }

/* Row actions inherit the universal .app-btn-icon; only the hover tint is
   landing-specific, so the canvas inspector buttons are untouched. */
.sq-l-actions .app-btn-icon:hover { background: var(--c-bg-f1eefb); color: #5b4596; }

/* Modules with no saved framing collapse to a single compact line (dashed, muted)
   instead of a tall empty panel. */
.sq-fc-module--empty { padding: 8px 12px; background: var(--c-bg-fbfafd); border-style: dashed; }
.sq-fc-module--empty .sq-fc-module-head { margin-bottom: 0; }
.sq-fc-nocalc-tag { font-size: 11px; font-weight: 600; color: #ada4c2; display: inline-flex; align-items: center; gap: 5px; }
.sq-fc-nocalc-tag i { font-size: 10px; opacity: .7; }

/* Compact picker dialog (drops into .sq-sm-backdrop instead of .sq-sm-dialog) */
.sq-l-dialog {
  background: var(--surface); border-radius: 14px;
  width: 96vw; max-width: 540px; max-height: 86vh;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 70px rgba(22,10,50,.28);
  overflow: hidden;
  transform: translateY(24px) scale(.97);
  transition: transform .28s cubic-bezier(.34,1.56,.64,1);
}
.sq-sm-backdrop.sq-sm-open .sq-l-dialog { transform: translateY(0) scale(1); }
.sq-l-dialog--wide { max-width: 760px; }
.sq-l-dialog-body { padding: 16px 20px; overflow-y: auto; }
.sq-l-field .app-select { width: 100%; }
.sq-l-dialog-foot {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 20px; border-top: 1px solid var(--c-border-ede9fe); flex-shrink: 0;
}
.sq-l-field { margin-bottom: 14px; }
.sq-l-field > label {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 5px;
}

/* Option list (PDF / floor / calc pickers) */
.sq-l-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border: 1px solid var(--line); border-radius: 8px;
  cursor: pointer; margin-bottom: 6px; transition: border-color .12s, background .12s;
}
.sq-l-opt:hover { border-color: var(--brand-mid); background: var(--surface-tint); }
.sq-l-opt.selected { border-color: #7c3aed; background: var(--surface-accent); }
.sq-l-opt.checked  { border-color: #7c3aed; background: var(--surface-accent); }
.sq-l-opt-name { font-size: 13px; font-weight: 600; color: var(--text-strong); }
.sq-l-opt-sub  { font-size: 11px; color: var(--text-faint); margin-top: 1px; }
/* PDF group heading */
.sq-l-pdf-group { margin-bottom: 4px; }
.sq-l-pdf-group-head {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-muted); padding: 6px 2px 4px;
}
/* Selected PDF chips (multi-select summary row) */
.sq-pdf-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; min-height: 0; }
.sq-pdf-chips:empty { display: none; margin: 0; }
.sq-pdf-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 600; padding: 3px 8px;
  background: var(--surface-accent); color: var(--accent-text-deep); border-radius: 12px;
  border: 1px solid var(--accent-line);
}
.sq-pdf-chip i { font-size: 10px; opacity: .7; }
/* Footer hint in dialogs */
.sq-l-foot-hint { flex: 1; font-size: 12px; color: var(--text-faint); }
.sq-l-empty {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 34px 20px; text-align: center; color: var(--text-faint); font-size: 13px;
}
.sq-l-empty i { font-size: 26px; opacity: .35; }
/* Final BOQ: the session is identified by its id, not its plan-derived name */
.sq-boq-uid {
  font-family: var(--font-sans, 'Inter', sans-serif);
  font-size: 11px; color: var(--text-muted); white-space: nowrap;
}
/* The class's explicit display would otherwise win over the [hidden] UA rule,
   leaving the intro (and its padding gap) on screen after a selection is made. */
.sq-l-empty[hidden] { display: none; }

/* ── Engineering data-source cards (replaces the old 3-column modules table) ──
   One card per engineering module: icon + name, the saved-framing source select,
   and a status line. Scannable and clearly tied to the active structural link. */
.sq-mod-loading { padding: 14px; font-size: 12px; color: var(--text-faint); font-style: italic; }
.sq-mod-card {
  border: 1px solid var(--c-border-ece8f5); border-radius: 12px; background: var(--surface);
  padding: 14px; display: flex; flex-direction: column; gap: 11px;
}
.sq-mod-card-head { display: flex; align-items: center; gap: 9px; }
.sq-mod-card-ico {
  width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--c-bg-f3effb); color: var(--accent-text); font-size: 13px;
}
.sq-mod-card-name { font-size: 13px; font-weight: 700; color: #2d1b69; }
.sq-mod-card-field label {
  display: block; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 4px;
}
.sq-mod-card-field .app-select { width: 100%; }
.sq-mod-card-head .app-badge,
.sq-mod-card-head .sq-l-session-meta { margin-left: auto; }

/* ── Landing Zone 1 — workspace selection (Project → Loading) ─────────────── */
.sq-ws {
  display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap;
  background: var(--c-bg-faf9fe); border: 1px solid var(--c-border-ece8f5); border-radius: 12px;
  padding: 14px 16px; margin-bottom: 18px;
}
.sq-ws-step { display: flex; align-items: flex-end; gap: 10px; }
.sq-ws-field { display: flex; flex-direction: column; gap: 4px; min-width: 220px; }
.sq-ws-field label {
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
}
.sq-ws-field .app-select { min-width: 220px; }

/* ── Landing Zone 2 — framing assignment (systems strip + floor checklist) ─── */
.sq-mod-sys-strip { display: flex; flex-wrap: wrap; gap: 5px; }
.sq-sys-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 11px;
  background: var(--neutral-bg); color: var(--text-body-2); border: 1px solid var(--line);
}
.sq-sys-chip em { font-style: normal; font-weight: 700; opacity: .8; }
.sq-sys-chip.sq-sys-designed { background: var(--c-bg-ecfdf5); color: #047857; border-color: var(--pass-border); }
.sq-sys-chip.sq-sys-manual   { background: var(--c-bg-fff7ed); color: #b45309; border-color: var(--c-border-fed7aa); }
.sq-sys-chip.sq-sys-partial  { background: var(--info-bg-soft); color: #1d4ed8; border-color: var(--c-border-bfdbfe); }

.sq-mod-floor-head {
  display: flex; align-items: center; justify-content: space-between;
  margin: 2px 0 5px; gap: 8px;
}
.sq-mod-checkall {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 700; color: var(--text-body-2); cursor: pointer;
}
.sq-mod-floor-list {
  max-height: 230px; overflow-y: auto; border: 1px solid var(--c-border-f0eef7);
  border-radius: 8px; padding: 3px;
}
.sq-mod-floor-row {
  display: flex; align-items: center; gap: 8px; padding: 4px 6px;
  border-radius: 6px; cursor: pointer; font-size: 12px;
}
.sq-mod-floor-row:hover { background: var(--c-bg-faf9fe); }
.sq-floor-lbl { flex: 1; color: var(--text-body); font-weight: 600; }
.sq-floor-tag {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 4px;
  text-transform: uppercase; letter-spacing: .03em;
}
.sq-floor-tag.sq-floor-data   { background: var(--pass-bg); color: var(--pass-fg); }
.sq-floor-tag.sq-floor-manual { background: var(--warn-bg); color: var(--warn-fg); }
.sq-floor-cur {
  font-size: 10px; color: var(--accent-text-strong); background: var(--c-bg-f3effb);
  padding: 1px 6px; border-radius: 10px; max-width: 110px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sq-mod-apply { display: flex; gap: 8px; }
.sq-mod-apply[hidden] { display: none; }

/* ── Landing Zone 3 — compact floors table + per-floor framing badge ──────── */
.sq-floor-fr {
  display: inline-flex; align-items: center; gap: 4px; margin-left: 8px;
  font-size: 10px; font-weight: 600; padding: 1px 7px; border-radius: 10px;
  background: var(--c-bg-f3effb); color: var(--accent-text-strong); vertical-align: middle;
}
.sq-floor-fr--none { background: var(--fail-bg-soft); color: var(--danger-text-2); }

/* ── WSF component picker (shared) ──────────────────────────────────────────
   Floor × component confirmation tables + the per-cell product picker. Lives
   here (not page-local) so the Modules-table sub-row and any other consumer
   can render it. Pairs with the universal .ls-tbl table classes. ─────────── */

.wsq-orient-toggle { display: flex; gap: 0; }
.wsq-orient-btn {
  font-size: 10px; font-weight: 700; padding: 2px 7px; border: 1px solid var(--accent-line);
  background: var(--surface-tint); color: var(--accent-text-strong); cursor: pointer; line-height: 1.4; transition: background .1s;
}
.wsq-orient-btn:first-child { border-radius: 4px 0 0 4px; }
.wsq-orient-btn:last-child  { border-radius: 0 4px 4px 0; border-left: none; }
.wsq-orient-active { background: #7c3aed; color: #fff; border-color: #6d28d9; }
.wsq-orient-lbl { font-size: 9px; color: var(--text-faint); margin-top: 3px; text-align: center; white-space: nowrap; }

.wsq-cell-actions { display: flex; align-items: center; gap: 5px; margin-top: 3px; }
.wsq-pick-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 4px; border: 1px solid var(--c-border-e5d9ff);
  background: var(--surface-tint); color: var(--accent-text); cursor: pointer; flex-shrink: 0; padding: 0;
  transition: background .15s, border-color .15s;
}
.wsq-pick-icon:hover { background: var(--surface-accent); border-color: var(--brand-mid); }

.wsq-picker-opt {
  display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 8px; cursor: pointer; margin-bottom: 6px;
  transition: border-color .15s, background .15s; user-select: none;
}
.wsq-picker-opt:hover { border-color: #7c3aed; background: var(--surface-tint); }
.wsq-picker-opt.wsq-picker-selected { border-color: #7c3aed; background: var(--surface-accent); }
.wsq-picker-opt input[type=radio] { accent-color: #7c3aed; margin-top: 3px; flex-shrink: 0; }
.wsq-picker-opt-body { flex: 1; min-width: 0; }
.wsq-picker-opt-name { font-size: 12px; font-weight: 600; color: var(--text-ink); }
.wsq-picker-opt-dim  { font-size: 11px; color: var(--accent-text); margin-top: 1px; }
.wsq-picker-opt-code { font-size: 9px; color: var(--text-faint); font-family: var(--font-sans, 'Inter', sans-serif); margin-top: 2px; }

/* ── WSF systems — collapsed summary rows + slide-over edit drawer ──────────
   List shows one read-only row per wall system; editing happens in a right-hand
   drawer (master–detail). The editable block stays in the DOM (hidden / moved
   into the drawer) so Confirm still gathers every cell. No inline CSS. ─────── */
.wsf-list { display: flex; flex-direction: column; gap: 8px; }

/* Summary row */
.wsf-sys-row {
  display: flex; align-items: center; gap: 14px;
  padding: 11px 14px;
  background: var(--surface);
  border: 1px solid var(--c-border-e9e4f5);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.wsf-sys-row:hover { border-color: var(--c-border-c9bbef); box-shadow: 0 2px 10px rgba(124,58,237,.07); }
.wsf-row-main { display: flex; align-items: center; gap: 9px; flex-shrink: 0; }
.wsf-sys-strip { width: 4px; height: 22px; border-radius: 3px; background: #7c3aed; flex-shrink: 0; }
.wsf-sys-strip.ext      { background: #2563eb; }
.wsf-sys-strip.party    { background: #d97706; }
.wsf-sys-strip.fire-ext,
.wsf-sys-strip.fire-int { background: #dc2626; }
.wsf-sys-strip.frw      { background: #db2777; }
.wsf-sys-name { font-size: 14px; font-weight: 800; color: var(--c-text-312e81); white-space: nowrap; }
.wsf-sys-type { font-size: 10px; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: .05em; }

.wsf-row-specs { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; flex: 1; min-width: 0; }
.wsf-spec {
  font-size: 11px; color: var(--text-muted);
  background: var(--c-bg-f6f4fb); border: 1px solid var(--c-border-efeafa); border-radius: 6px;
  padding: 2px 9px; white-space: nowrap;
}
.wsf-spec strong { color: var(--c-text-312e81); font-weight: 700; }
.wsf-spec-muted { background: transparent; border: none; color: var(--text-faint); }
.wsf-spec-manual { color: var(--warn-fg); background: var(--c-bg-fffbeb); border-color: var(--c-border-fde68a); font-weight: 700; }
.wsf-spec-manual i { font-size: 9px; margin-right: 3px; }

.wsf-row-edit {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  font-size: 12px; font-weight: 600; color: var(--accent-text-strong);
  background: var(--c-bg-f1ecfe); border: 1px solid var(--accent-line-soft); border-radius: 8px;
  padding: 6px 12px; cursor: pointer; white-space: nowrap;
  transition: background .12s, border-color .12s;
}
.wsf-row-edit:hover { background: var(--c-bg-e6defb); border-color: var(--accent-line); }
.wsf-row-edit .fa-chevron-right { font-size: 9px; opacity: .7; }

/* ── Edit drawer (slide-over) ── */
.wsf-drawer-scrim {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(17,12,40,.38);
  opacity: 0; transition: opacity .2s;
}
.wsf-drawer-scrim.wsf-drawer-on { opacity: 1; }
.wsf-drawer {
  position: fixed; top: 0; right: 0; z-index: 1201;
  height: 100vh; width: min(520px, 94vw);
  background: var(--surface);
  display: flex; flex-direction: column;
  box-shadow: -12px 0 40px rgba(17,12,40,.18);
  transform: translateX(100%); transition: transform .22s ease;
}
.wsf-drawer.wsf-drawer-on { transform: translateX(0); }
.wsf-drawer[hidden], .wsf-drawer-scrim[hidden] { display: none; }
.wsf-drawer-hd {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--c-border-efeafa);
}
.wsf-drawer-title { font-size: 17px; font-weight: 800; color: var(--c-text-312e81); }
.wsf-drawer-sub { font-size: 12px; color: var(--text-faint); margin-top: 2px; }
.wsf-drawer-close {
  width: 30px; height: 30px; flex-shrink: 0;
  border: none; background: var(--c-bg-f3f0fb); border-radius: 8px;
  color: var(--text-muted); cursor: pointer; font-size: 15px;
}
.wsf-drawer-close:hover { background: var(--c-bg-e9e3f8); }
.wsf-drawer-body { flex: 1; overflow-y: auto; padding: 18px 20px; }
.wsf-drawer-foot { padding: 12px 20px; border-top: 1px solid var(--c-border-efeafa); display: flex; justify-content: flex-end; }

.wsf-drawer-specs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.wsf-drawer-note {
  flex: 1 1 100%;
  display: flex; align-items: center; gap: 8px;
  font-size: 11.5px; font-weight: 600;
  border-radius: 8px; padding: 8px 12px; margin-bottom: 2px;
}
.wsf-drawer-note--warn { color: var(--warn-fg); background: var(--c-bg-fffbeb); border: 1px solid var(--c-border-fde68a); }
.wsf-drawer-note--fail { color: var(--fail-fg); background: var(--fail-bg-soft); border: 1px solid var(--c-border-fecaca); }

/* Save gated until all WSF cells finish calculating */
#btnWsfSave:disabled { opacity: .45; cursor: not-allowed; box-shadow: none; filter: grayscale(.25); }
.wsf-drawer-chip {
  display: flex; flex-direction: column; gap: 1px;
  background: var(--c-bg-f8f7fc); border: 1px solid var(--c-border-ece8f6); border-radius: 8px; padding: 6px 12px; min-width: 78px;
}
.wsf-drawer-chip-l { font-size: 9px; font-weight: 700; color: var(--text-faint); text-transform: uppercase; letter-spacing: .05em; }
.wsf-drawer-chip-v { font-size: 13px; font-weight: 700; color: var(--c-text-312e81); }

.wsf-drawer-sec { margin-bottom: 18px; }
.wsf-drawer-sectitle { font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 8px; }
.wsf-drawer-diagram {
  background: var(--c-bg-fafafe); border: 1px solid var(--c-border-ece8f6); border-radius: 10px;
  padding: 18px; display: flex; justify-content: center;
}
.wsf-drawer-diagram img, .wsf-drawer-diagram svg { cursor: default !important; }
.wsf-d-stack { display: flex; flex-direction: column; align-items: center; gap: 0; }

/* Framing details — connections + anchors captured into the saved framing */
.wsf-fdetails {
  background: var(--c-bg-fbfaff); border: 1px solid var(--c-border-ece8f6); border-radius: 10px;
  padding: 12px 14px; margin-bottom: 16px;
}
.wsf-fd-title {
  font-size: 11px; font-weight: 700; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .05em; margin-bottom: 10px;
}
.wsf-fd-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px;
}
.wsf-fd-field {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #78716c;
}
.wsf-fd-field select {
  font-size: 12px; font-weight: 500; color: var(--text-strong);
  border: 1px solid var(--line); border-radius: 7px; padding: 6px 8px; background: var(--surface);
  font-family: inherit; text-transform: none; letter-spacing: 0; cursor: pointer;
}
.wsf-fd-field select:focus { outline: none; border-color: #7c3aed; box-shadow: 0 0 0 2px rgba(124,58,237,.12); }

/* Manual framing entry (floors with no valid design) */
.wsf-manual {
  background: var(--c-bg-fffbeb); border: 1px solid var(--c-border-fde68a); border-radius: 10px;
  padding: 12px 14px; margin-bottom: 16px;
}
.wsf-manual-hd { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.wsf-manual-tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--warn-fg); background: var(--warn-bg); border: 1px solid var(--warn-border); border-radius: 6px; padding: 3px 8px;
}
.wsf-manual-hint { font-size: 11px; color: var(--warn-fg); }
.wsf-manual-fields { display: flex; flex-wrap: wrap; gap: 12px; }
.wsf-manual-field {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #78716c;
  flex: 1 1 130px;
}
.wsf-manual-field input {
  font-size: 13px; font-weight: 600; color: var(--text-strong);
  border: 1px solid #e7c98a; border-radius: 7px; padding: 7px 9px; background: var(--surface);
  font-family: inherit; text-transform: none; letter-spacing: 0;
}
.wsf-manual-field input:focus { outline: none; border-color: #d97706; box-shadow: 0 0 0 2px rgba(217,119,6,.14); }

/* Component line — role | product select | sku */
.wsf-comp {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px;
  background: var(--c-bg-fdfcff);
  border: 1px solid var(--c-border-eee9f7);
  border-radius: 8px;
  min-width: 0;
}
.wsf-comp-role {
  flex: 0 0 66px;
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint);
}
.wsf-comp-role.ct-lining     { color: #4338ca; }
.wsf-comp-role.ct-framing    { color: var(--accent-text-strong); }
.wsf-comp-role.ct-track      { color: #1d4ed8; }
.wsf-comp-role.ct-nogging    { color: #b45309; }
.wsf-comp-role.ct-insulation { color: #0d9488; }
.wsf-comp-role.ct-anchor,
.wsf-comp-role.ct-other      { color: var(--text-muted); }
.wsf-comp-sel {
  flex: 1; min-width: 0;
  font-size: 12px; color: var(--text-body);
  border: 1px solid var(--line); border-radius: 7px;
  padding: 6px 9px; background: var(--surface); cursor: pointer; appearance: auto;
  transition: border-color .12s, box-shadow .12s;
}
.wsf-comp-sel:hover  { border-color: var(--accent-line); }
.wsf-comp-sel:focus  { outline: none; border-color: #7c3aed; box-shadow: 0 0 0 2px rgba(124,58,237,.12); }
.wsf-comp-sku  { flex: 0 0 auto; font-size: 10px; color: var(--text-faint); font-family: var(--font-sans, 'Inter', sans-serif); }
.wsf-comp-name { flex: 1; min-width: 0; font-size: 12px; font-weight: 600; color: var(--text-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wsf-comp-nosku { flex: 0 0 auto; font-size: 10px; color: #d1d5db; font-style: italic; }

/* ════════════════════════════════════════════════════
   Vertical Toolbar  (AutoCAD-style floating toolbar)
   Shared by SmartQuote drawing interface and SmartSpec.
   ════════════════════════════════════════════════════ */

.toolbar-section {
    width: 100%;
    border-bottom: 0.5px solid var(--c-border-d4d2cf);
    padding: 3px 0;
}
.toolbar-section:last-child { border-bottom: none; }

.tool-btn {
    width: 44px;
    height: 44px;
    margin: 2px auto;
    background: var(--surface);
    border: 1px solid var(--c-border-e8e6e1);
    border-radius: 6px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.2s ease;
    color: var(--c-text-444441);
    box-shadow: none;
    position: relative;
    overflow: hidden;
    font-size: 11px;
}
.tool-btn::before {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 100%; height: 100%;
    background: none;
    opacity: 0;
    transition: opacity 0.2s;
}
.tool-btn:hover {
    background: var(--c-bg-e9e7e2);
    border-color: var(--c-border-d4d2cf);
    color: var(--c-text-2d2d2b);
    transform: none;
    box-shadow: none;
}
.tool-btn:hover::before { opacity: 0; }
.tool-btn.active {
    background: #0078d4;
    border-color: #0078d4;
    color: white;
    box-shadow: none;
    transform: none;
}
.tool-btn.active::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 80%;
    height: 0px;
    background: transparent;
    box-shadow: none;
}
.tool-btn i {
    font-size: 20px;
    margin-bottom: 2px;
    color: var(--c-text-444441);
}
.tool-btn span {
    font-size: 8px;
    text-align: center;
    font-weight: 500;
    color: var(--c-text-444441);
}
.tool-btn.active i,
.tool-btn.active span { color: white; }

/* ─── end Vertical Toolbar ─── */

/* ════════════════════════════════════════════════════
   Inspector Panel  (bottom drawer)
   Shared by SmartQuote drawing interface and SmartSpec.
   ════════════════════════════════════════════════════ */

.insp-panel {
    flex-shrink: 0;
    background: var(--surface);
    border-top: 2px solid var(--c-border-e2d9f3);
    display: flex;
    flex-direction: column;
    height: auto;          /* size to content (header + rows + footer) … */
    min-height: 38px;
    max-height: 50vh;      /* … capped at half the viewport, then the table scrolls */
    transition: height 0.05s linear;
    position: relative;
    z-index: 10;
}
.insp-panel.resizing { transition: none; }
.insp-panel.collapsed { height: 38px; }

/* ── Side-by-side layout: the SmartSpec inspector is a LEFT pane, not a bottom
      drawer. Scoped to the #side-panel id so shared .insp-panel users
      (residential IFC, SmartQuote) keep the drawer behaviour. ── */
#side-panel {
  order: -1;                 /* sit left of the 3D viewer */
  width: 420px; min-width: 220px; max-width: 60%;
  height: auto; min-height: 0; max-height: none;   /* stretch to full column height */
  align-self: stretch;
  border-top: none;
  border-right: 2px solid var(--c-border-e2d9f3);
}
/* min-width:220px above always wins over a smaller width, so collapsed must
   override it too or the panel stays pinned at 220px with everything hidden
   inside — a blank gap instead of a slim 40px rail. */
#side-panel.collapsed { width: 40px; min-width: 40px; max-width: 40px; height: auto; }
#side-panel.collapsed .insp-tabs,
#side-panel.collapsed .insp-actions { display: none; }
#side-panel.collapsed .insp-header { padding: 0; justify-content: center; }
/* Collapsed, the rail IS the toggle: stretch the tinted header over the whole
   strip and centre the chevron in it, instead of a 38px cap of colour above a
   white void with the chevron stranded at the top. */
#side-panel.collapsed { background: var(--tbl-head-bg, var(--surface-tint)); }
#side-panel.collapsed .insp-header {
  flex: 1 1 auto; height: auto; align-items: center; border-bottom: none;
}
/* Resize handle moves to the RIGHT edge (horizontal drag). */
#side-panel .insp-resize-handle {
  top: 0; bottom: 0; left: auto; right: -4px;
  width: 8px; height: auto; cursor: ew-resize;
}
#side-panel .insp-resize-handle::after { width: 3px; height: 40px; }

/* ── Compact 2-line card layout for the SmartSpec inspector list ──────────────
   The wide multi-column table can't fit the narrow left pane, so each system
   renders as a compact card: line 1 = identity (name + supplier/code + actions),
   line 2 = flags + performance, line 3 = notes. This is pure CSS over the
   existing table markup (walls & ceilings, SSR + JS renderers), so every row
   handler, data-attr and the component-expansion sub-rows keep working. */
#side-panel .insp-table,
#side-panel .insp-table > thead,
#side-panel .insp-table > tbody { display: block; width: 100%; }
#side-panel .insp-table > thead { display: none; }   /* columns no longer align */
#side-panel .insp-table-wrap { overflow-x: hidden; }  /* no more sideways scroll */

#side-panel .insp-table tr.line-item {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: 6px; row-gap: 2px;
  padding: 6px 11px; border-bottom: 1px solid var(--c-border-f0f1f5);
  transition: background .12s;
}
#side-panel .insp-table tr.line-item:hover { background: var(--c-bg-faf9fe); }
#side-panel .insp-table tr.line-item.selected { background: var(--c-bg-f3f0fd); }
#sb-walls-table tr.line-item.selected { box-shadow: inset 3px 0 0 #7c3aed; }
#sb-ceilings-table tr.line-item.selected { box-shadow: inset 3px 0 0 #0369a1; }
/* width:auto overrides the base ".insp-table td { width:1% }" shrink trick, which
   otherwise collapses every flex-item cell to a few px and overflows the text. */
#side-panel .insp-table tr.line-item > td { padding: 0; border: none; font-size: 12px; width: auto; }

/* line 1 — system identity */
#side-panel tr.line-item > td:has(.insp-sys-dot) { order: 1; white-space: nowrap; }
/* The colour square marked wall (purple) vs ceiling (blue) — redundant now that
   each lives in its own tab, so hide it and keep just the expand toggle. */
#side-panel .insp-sys-dot { display: none; }
#side-panel tr.line-item > td:has(.insp-name-input) { order: 2; flex: 0 1 auto; min-width: 0; }
#side-panel tr.line-item > td:has(.insp-name-input) + td { order: 3; }        /* supplier badge */
#side-panel tr.line-item > td:has(.insp-name-input) + td + td { order: 4; }    /* code + modified pill */
#side-panel tr.line-item > td:has(.insp-td-actions) { order: 5; margin-left: auto; }

/* Forced break after the identity line: repurpose the always-empty "Other" perf
   cell (walls col 10, ceilings col 9) as a zero-height full-width spacer. */
#sb-walls-table tr.line-item > td.insp-perf-cell:nth-child(10),
#sb-ceilings-table tr.line-item > td.insp-perf-cell:nth-child(9) {
  order: 6; flex: 0 0 100%; height: 0; padding: 0; margin: 0; overflow: hidden; visibility: hidden;
}

/* line 2 — flags + performance, rendered as labelled pill-chips (the header row
   is gone, so every value names itself). */
#side-panel tr.line-item > td.insp-flag-cell {
  order: 12; display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 8px 1px 9px; border-radius: 999px;
  background: var(--c-bg-f4f5f8); border: 1px solid var(--c-border-e9ebf0);
  font-size: 9.5px; letter-spacing: .04em; text-transform: uppercase; font-weight: 700; color: #9aa2b1;
}
#side-panel tr.line-item > td.insp-flag-cell--active { order: 11; }
/* checked flags read as "on" with an accent tint */
#side-panel tr.line-item > td.insp-flag-cell:has(.insp-flag:checked) {
  background: var(--c-bg-f0ebfe); border-color: var(--c-border-dccdfb); color: var(--accent-text-strong);
}
#side-panel .insp-flag { accent-color: #7c3aed; width: 13px; height: 13px; margin: 0; cursor: pointer; }

/* Active flag reads as a switch, not a checkbox — a plain tick is easy to
   mistake for "this row is selected" rather than "excluded from reports and
   calculations when off", which is a much bigger effect than Wet/Sealed. */
#side-panel .insp-flag--switch {
  appearance: none; -webkit-appearance: none; accent-color: unset;
  width: 26px; height: 14px; border-radius: 999px; position: relative;
  background: #cbd1dc; border: none; margin: 0; cursor: pointer;
  transition: background .15s ease; flex-shrink: 0;
}
#side-panel .insp-flag--switch::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 10px; height: 10px; border-radius: 50%; background: var(--surface);
  box-shadow: 0 1px 2px rgba(0,0,0,.3); transition: left .15s ease;
}
#side-panel .insp-flag--switch:checked { background: #7c3aed; }
#side-panel .insp-flag--switch:checked::after { left: 14px; }

#side-panel tr.line-item > td.insp-perf-cell {
  order: 15; display: inline-flex; align-items: baseline; gap: 4px;
  padding: 1px 8px; border-radius: 6px; background: var(--c-bg-eef2f7);
  font-size: 10.5px; font-weight: 600; color: var(--c-text-334155); white-space: nowrap;
}
/* Hide empty (grey dash) and voided (amber dash) performance cells — the MODIFIED
   pill already explains why a modified system carries no ratings. */
#side-panel tr.line-item > td.insp-perf-cell:has(> span[style*="9ca3af"]),
#side-panel tr.line-item > td.insp-perf-cell:has(> .insp-perf-void) { display: none; }

/* Inline labels for the flags + the values that don't name themselves (FRL, width). */
#side-panel tr.line-item > td.insp-flag-cell--active::before { content: "Active"; }
#side-panel tr.line-item > td.insp-flag-cell[data-sealed-cell]::before { content: "Sealed"; }
#side-panel tr.line-item > td.insp-flag-cell:not(.insp-flag-cell--active):not([data-sealed-cell])::before { content: "Wet"; }
#side-panel tr.line-item > td.insp-perf-cell::before { color: #94a3b8; font-weight: 700; font-size: 9px; text-transform: uppercase; letter-spacing: .03em; }
#sb-walls-table tr.line-item > td.insp-perf-cell:nth-child(6)::before,
#sb-ceilings-table tr.line-item > td.insp-perf-cell:nth-child(6)::before { content: "FRL"; }
#sb-walls-table tr.line-item > td.insp-perf-cell:nth-child(9)::before { content: "W"; }

/* notes — always its own full-width third row. */
#side-panel tr.line-item > td:has(.insp-notes-input) { order: 30; flex: 1 1 100%; min-width: 0; }
/* Empty notes stay low-profile (italic, dim) so they don't read as filled. */
#side-panel .insp-notes-input:placeholder-shown:not(:focus) { opacity: .5; font-style: italic; }

/* Cleaner, lighter inline inputs so the card reads like a card, not a form. */
#side-panel .insp-name-input {
  font-weight: 600; font-size: 12.5px; box-sizing: border-box;
  /* size the input to its text so the supplier/code sit right beside the tag
     (field-sizing: Chromium 123+; falls back to the default input width). */
  field-sizing: content; width: auto; min-width: 46px; max-width: 200px;
  border: 1px solid transparent; background: transparent; padding: 2px 5px; border-radius: 5px;
}
#side-panel .insp-notes-input {
  font-size: 11px; width: 100%; min-width: 0; box-sizing: border-box;
  border: 1px solid transparent; background: transparent; padding: 2px 5px; border-radius: 5px;
}
#side-panel .insp-name-input:hover,
#side-panel .insp-notes-input:hover { border-color: var(--c-border-e2e8f0); }
#side-panel .insp-name-input:focus,
#side-panel .insp-notes-input:focus { border-color: var(--accent-line); background: var(--surface); outline: none; }

/* Inline add-row (ghost): keep its hint visible instead of letting the name grow. */
#side-panel tr.line-item[data-wt-pk="new"] > td:has(.insp-name-input) { flex: 0 0 auto; min-width: 92px; }
#side-panel tr.line-item[data-wt-pk="new"] > td[colspan] { order: 4; flex: 1 1 auto; }

/* Component-expansion sub-rows: a clean indented list, one component per line —
   [layer chip] [supplier] [product name, grows + truncates] [code]. The old
   "LAYER SUPPLIER PRODUCT CODE" header cramped together, so it's dropped. */
#side-panel .insp-comp-header { display: none; }
#side-panel .insp-comp-row {
  display: flex; align-items: center; gap: 8px;
  padding: 3px 11px 3px 30px; font-size: 11px;
  background: var(--c-bg-fbfbfe); border-bottom: 1px solid var(--c-border-f3f2f9);
}
#side-panel .insp-comp-row:hover { background: var(--c-bg-f6f4fd); }
/* reset the base ".insp-table td { width:1% }" shrink trick that jammed the cells */
#side-panel .insp-comp-row > td { width: auto; padding: 0; border: none; }
#side-panel .insp-comp-row > td:first-child,          /* empty lead cell   */
#side-panel .insp-comp-row > td[colspan] { display: none; }   /* empty tail cell */
#side-panel .insp-comp-row > td:nth-child(2) { flex: 0 0 auto; }   /* layer chip */
#side-panel .insp-comp-row .insp-comp-supplier {
  flex: 0 0 auto; color: #94a3b8; font-size: 10px; font-weight: 500;
}
#side-panel .insp-comp-row .insp-comp-name {
  flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-weight: 600; color: var(--c-text-334155);
}
#side-panel .insp-comp-row .insp-comp-code {
  flex: 0 0 auto; margin-left: auto; padding-left: 6px; color: #9aa2b1; font-size: 10px;
}

.insp-resize-handle {
    position: absolute;
    top: -4px;
    left: 0; right: 0;
    height: 8px;
    cursor: ns-resize;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: center;
}
.insp-resize-handle::after {
    content: '';
    display: block;
    width: 40px;
    height: 3px;
    border-radius: 2px;
    background: var(--c-bg-d8b4fe);
    opacity: 0;
    transition: opacity 0.15s;
}
.insp-resize-handle:hover::after,
.insp-panel.resizing .insp-resize-handle::after { opacity: 1; }

.insp-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    padding: 0 10px;
    height: 38px;
    background: var(--tbl-head-bg, var(--surface-tint));
    border-bottom: 1px solid var(--tbl-head-border, var(--accent-line-soft));
    gap: 0;
}
.insp-tabs { display: flex; align-items: stretch; gap: 0; flex: 1; height: 100%; }
.insp-tab {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 0 14px; font-size: 12px; font-weight: 600; color: var(--text-muted);
    background: none; border: none; border-bottom: 2px solid transparent;
    cursor: pointer; white-space: nowrap; height: 100%;
    transition: color 0.12s, border-color 0.12s;
}
.insp-tab:hover { color: var(--text-body); }
.insp-tab.active { color: var(--accent-text-strong); border-bottom-color: #6d28d9; }
.insp-tab-badge {
    background: #6d28d9; color: #fff; border-radius: 99px;
    font-size: 10px; font-weight: 700; padding: 1px 6px;
    min-width: 18px; text-align: center;
}
/* ── Tooltip for [data-tip] ──
   Body-level + fixed: the inspector's sticky headers sit inside an
   overflow:auto wrapper that clips anything in flow (which is why the native
   title never showed). Positioned by sbTipShow. */
.sb-tip {
  display: none; position: fixed; z-index: 9700; max-width: 260px;
  padding: 6px 9px; border-radius: 6px; pointer-events: none;
  background: #1f2937; color: #fff; font-size: 11.5px; line-height: 1.45;
  box-shadow: 0 6px 20px rgba(15,10,30,.22); white-space: normal;
}

/* ── Design-classification flag columns (Active / Wet / Sealed) ── */
.insp-flag-th { width: 52px; text-align: center; }
.insp-flag-cell { text-align: center; white-space: nowrap; }
.insp-flag {
  width: 14px; height: 14px; margin: 0; cursor: pointer; accent-color: #7c3aed;
  vertical-align: middle;
}
/* The chip sits under the tick in its own cell, so it must not inherit the
   inline spacing the code-column version uses. */
.insp-flag-cell .insp-mod-flag { display: inline-block; margin: 2px 0 0; }

/* Not active → excluded from reports, certification and calculations. Grey the
   row so that reads at a glance, but keep the Active tick and the row actions
   at full strength: you still need to click them to bring it back. */
.insp-table tbody tr.insp-row-inactive > td { opacity: .4; }
.insp-table tbody tr.insp-row-inactive > td.insp-flag-cell--active,
.insp-table tbody tr.insp-row-inactive > td:last-child { opacity: 1; }
.insp-table tbody tr.insp-row-inactive .insp-name-input { text-decoration: line-through; }

/* ── "Modified from the tested system" markers ──
   The build-up no longer matches the tested specimen, so its FRL / Rw / R-value
   are withheld rather than shown against an assembly they weren't tested on. */
.insp-mod-flag {
  margin-left: 6px; padding: 1px 5px; border-radius: 99px; vertical-align: middle;
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: #b45309; background: var(--warn-bg); border: 1px solid var(--c-border-fde68a); cursor: help;
}
.insp-perf-void { color: #d97706; cursor: help; border-bottom: 1px dotted #fbbf24; }

.insp-actions { display: flex; align-items: center; gap: 6px; margin-left: 12px; }
.insp-add-dropdown {
  position: absolute; top: calc(100% + 5px); right: 0;
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0,0,0,.12); min-width: 162px;
  z-index: 200; display: none; flex-direction: column; overflow: hidden;
}
.insp-add-dropdown.open { display: flex; }
.insp-add-dropdown button {
  background: none; border: none; padding: 9px 13px;
  text-align: left; font-size: 12px; color: var(--text-body);
  cursor: pointer; display: flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
.insp-add-dropdown button:hover { background: var(--c-bg-f3f0ff); color: var(--accent-text-strong); }
.insp-add-dropdown button i { color: var(--accent-text); font-size: 11px; width: 14px; text-align: center; }
.insp-action-btn {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 4px 10px; font-size: 11px; font-weight: 600;
    border-radius: 6px; border: 1px solid; cursor: pointer;
    transition: opacity 0.15s; white-space: nowrap; font-family: inherit;
}
.insp-action-btn:hover { opacity: 0.8; }
.insp-action-btn--danger  { background: var(--fail-bg-soft); color: var(--danger-text); border-color: var(--c-border-fecaca); }
.insp-action-btn--primary { background: var(--surface-accent); color: var(--accent-text-strong); border-color: var(--accent-line); }
.insp-action-btn--ifc     { background: var(--c-bg-eef2ff); color: #4338ca; border-color: var(--c-border-c7d2fe); }
.insp-action-btn--ifc:disabled { opacity: 0.45; cursor: not-allowed; }
.insp-action-btn--bom     { background: var(--c-bg-f0fdf4); color: #15803d; border-color: var(--c-border-bbf7d0); text-decoration: none; }
.insp-actions-sep         { width: 1px; height: 20px; background: var(--c-bg-e5e7eb); flex-shrink: 0; margin: 0 2px; }
.insp-toggle-btn {
    width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
    background: none; border: 1px solid var(--line); border-radius: 5px;
    cursor: pointer; color: var(--text-muted); font-size: 10px; transition: background 0.12s; margin-left: 4px;
}
.insp-toggle-btn:hover { background: var(--neutral-bg); }

/* ── Inspection-panel type sub-tabs ── */
.insp-subtabs {
  flex-shrink: 0; display: flex; align-items: center; gap: 4px;
  padding: 6px 10px; background: var(--c-bg-fbfaff); border-bottom: 1px solid var(--c-border-ece8f5);
  overflow-x: auto; white-space: nowrap;
}
.insp-panel.collapsed .insp-subtabs { display: none; }
.insp-subtab {
  flex-shrink: 0; padding: 4px 11px; font-size: 11px; font-weight: 600;
  color: var(--text-muted); background: var(--surface); border: 1px solid var(--line); border-radius: 99px;
  cursor: pointer; font-family: inherit; transition: all .12s; white-space: nowrap;
}
.insp-subtab:hover { color: var(--accent-text-strong); border-color: var(--accent-line); background: var(--surface-tint); }
.insp-subtab.active { color: #fff; background: #6d28d9; border-color: #6d28d9; }
.insp-subtab i { font-size: 10px; margin-right: 3px; opacity: .8; }

/* ── Search box + filter controls (system library overlay) ── */
.sb-finder-search { position: relative; flex: 1 1 220px; min-width: 180px; max-width: 300px; }
.sb-finder-search i {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  color: var(--text-faint); font-size: 12px; pointer-events: none;
}
.sb-finder-search input {
  width: 100%; height: 30px; padding: 0 10px 0 30px; font-size: 12px;
  border: 1px solid var(--neutral-border); border-radius: 6px; outline: none; font-family: inherit;
}
.sb-finder-search input:focus { border-color: var(--brand-mid); }
.sb-ff-group { display: flex; align-items: center; gap: 4px; }
.sb-ff-lbl { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--text-faint); margin-right: 1px; }
.sb-ff-clear {
  width: 26px; height: 26px; border: 1px solid var(--line); border-radius: 6px; background: var(--surface);
  color: var(--text-faint); cursor: pointer; font-size: 11px; margin-left: auto;
}
.sb-ff-clear:hover { background: var(--fail-bg-soft); color: var(--danger-text); border-color: var(--c-border-fecaca); }

/* ── Template picker ("which template?" behind the inspector Template button) ── */
.sb-tpl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.sb-tpl-group {
  grid-column: 1 / -1; font-size: 9.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--text-faint); padding: 8px 2px 2px;
}
.sb-tpl-card {
  display: flex; align-items: center; gap: 9px; text-align: left;
  padding: 11px 12px; font-size: 12.5px; font-weight: 600; font-family: inherit;
  color: var(--text-body); background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  cursor: pointer; transition: all .12s;
}
.sb-tpl-card:hover { color: var(--accent-text-strong); border-color: var(--accent-line); background: var(--surface-tint); }
.sb-tpl-card i { color: var(--accent-text); font-size: 13px; width: 15px; text-align: center; flex-shrink: 0; }
.sb-tpl-card span { overflow: hidden; text-overflow: ellipsis; }
.sb-tpl-card--blank { border-style: dashed; }

/* ── Components column: layer editor above, builder bar pinned at its foot.
      Revealed by #sb-build-area.building; hidden (zero width) otherwise. ── */
#sb-left-col { display: none; }
#sb-build-area.building #sb-left-col {
  display: flex; flex-direction: column; min-height: 0;
  width: 320px; flex-shrink: 0;
  border-right: 1px solid var(--c-border-ece8f5); background: var(--c-bg-fbfafe);
}

/* ── Builder bar (foot of the components column while building) ── */
#sb-builder-bar {
  flex-shrink: 0; display: flex; flex-direction: column; gap: 7px;
  padding: 10px 12px; background: var(--c-bg-faf8ff); border-top: 1px solid var(--c-border-ede9fe);
}
.sb-bb-row { display: flex; align-items: center; gap: 8px; }
.sb-bb-row--btns > * { flex: 1; justify-content: center; }
.sb-bb-badge {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--accent-text-strong); background: var(--surface-accent); border-radius: 99px; padding: 3px 9px; white-space: nowrap;
}
#sb-bb-name {
  flex: 1; min-width: 0; height: 30px; padding: 0 10px; font-size: 13px;
  border: 1px solid var(--neutral-border); border-radius: 6px; outline: none; font-family: inherit;
}
#sb-bb-name:focus { border-color: var(--brand-mid); }
.sb-bb-cancel {
  height: 30px; padding: 0 12px; font-size: 12px; font-weight: 600; font-family: inherit;
  color: var(--text-muted); background: var(--surface); border: 1px solid var(--line); border-radius: 6px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.sb-bb-cancel:hover { background: var(--surface-sunken); }
.sb-bb-save { height: 30px; padding: 0 14px; font-size: 12px; }
.sb-bb-save:disabled { opacity: .5; cursor: not-allowed; }

/* ── Interactive 2D layer editor (Section view) ── */
/* Scrolls inside the components column; the builder bar below it stays put. */
#sb-layer-stack {
  flex: 1; min-height: 0; overflow-y: auto; padding: 10px 12px 24px;
}
/* Tri-pane overlay: Perspective (left) | Section (top-right) / Plan (bottom-right).
   Left split at 56% must match _TRI_LEFT_FRAC in the renderer. */
/* Tri-pane = three real bordered container cells overlaid on the single canvas.
   The 6px gutter (--sb-tri-gut) + 56%/50% split MUST match the inset viewports in
   _renderTriple and _TRI_LEFT_FRAC in the renderer. pointer-events:none so the canvas
   underneath still receives mouse input (routed per pane by _triPaneAt). */
#sb-tri-labels {
  position: absolute; inset: 0; pointer-events: none; --sb-tri-gut: 6px;
  user-select: none; -webkit-user-select: none;   /* no blue text-select on drag */
}
.sb-tri-pane {
  position: absolute; pointer-events: none; z-index: 4;
  border: 1px solid var(--c-border-cbd5e1); border-radius: 0; background: transparent;
  transition: border-color .1s ease;
}
/* Active viewport (cursor is over it): crisp accent edge, no shadow/radius */
.sb-tri-pane.is-hovered { border-color: #7c3aed; box-shadow: inset 0 0 0 1px #7c3aed; }
:root[data-theme="dark"] .sb-tri-pane { border-color: #3a4150; }
:root[data-theme="dark"] .sb-tri-pane.is-hovered { border-color: #a78bfa; box-shadow: inset 0 0 0 1px #a78bfa; }
/* Perspective on top (full width); Section + Plan share a smaller bottom row. */
.sb-tri-pane--persp {
  left: var(--sb-tri-gut); right: var(--sb-tri-gut); top: var(--sb-tri-gut);
  bottom: calc(38% + var(--sb-tri-gut));
}
.sb-tri-pane--section {
  left: var(--sb-tri-gut); top: calc(62% + var(--sb-tri-gut));
  right: calc(50% + var(--sb-tri-gut)); bottom: var(--sb-tri-gut);
}
.sb-tri-pane--plan {
  left: calc(50% + var(--sb-tri-gut)); top: calc(62% + var(--sb-tri-gut));
  right: var(--sb-tri-gut); bottom: var(--sb-tri-gut);
}
.sb-tri-lbl {
  position: absolute; left: 10px; top: 7px; pointer-events: none;
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #94a3b8;
}
.sb-ls-empty-msg { text-align: center; color: var(--text-faint); font-size: 12px; padding: 28px 0; }
.sb-layer-strip {
  display: flex; align-items: stretch; gap: 0; overflow: hidden;
  background: var(--surface); border: 1px solid var(--c-border-e9e5f5); border-radius: 9px; cursor: pointer;
  transition: border-color .12s, box-shadow .12s;
}
.sb-layer-strip:hover { border-color: var(--accent-line); box-shadow: 0 1px 6px rgba(124,58,237,.08); }
.sb-layer-strip.dragging { opacity: .45; border-style: dashed; }
/* Selected layer (clicked in the list or picked in the 3D) — accent edge that
   matches the viewport highlight. */
.sb-layer-strip.ring-3d-highlight {
  border-color: #7c3aed; background: var(--surface-tint);
  box-shadow: 0 0 0 1px #7c3aed, 0 1px 8px rgba(124,58,237,.16);
}
:root[data-theme="dark"] .sb-layer-strip.ring-3d-highlight {
  border-color: #a78bfa; background: rgba(124,58,237,.14);
  box-shadow: 0 0 0 1px #a78bfa;
}
/* ── Right-click menu (component cards + inspector rows) ── */
.sb-ctx-menu {
  display: none; position: fixed; z-index: 9600; min-width: 178px; padding: 4px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 8px 26px rgba(15,10,30,.16);
}
.sb-ctx-menu button {
  display: flex; align-items: center; gap: 9px; width: 100%; padding: 7px 10px;
  background: none; border: none; border-radius: 5px; cursor: pointer;
  font-family: inherit; font-size: 12px; color: var(--text-body); text-align: left; white-space: nowrap;
}
.sb-ctx-menu button:hover { background: var(--surface-tint); color: var(--accent-text-strong); }
.sb-ctx-menu button i { width: 14px; text-align: center; opacity: .75; font-size: 11px; }
.sb-ctx-menu button.danger { color: var(--danger-text-2); }
.sb-ctx-menu button.danger:hover { background: var(--fail-bg-soft); }
.sb-ctx-sep { height: 1px; margin: 4px 6px; background: var(--c-bg-f1f5f9); }

/* Where the dragged card will land. Inset bars, because .sb-layer-strip clips
   overflow; declared last so they win over the hover / highlight box-shadows. */
.sb-layer-strip.drop-above { box-shadow: inset 0 3px 0 0 #7c3aed; border-color: var(--accent-line); }
.sb-layer-strip.drop-below { box-shadow: inset 0 -3px 0 0 #7c3aed; border-color: var(--accent-line); }

/* ── 3D component leader tags (system builder viewport) ─────────────────── */
#sb-tag-overlay { position: absolute; inset: 0; pointer-events: none; z-index: 6; display: none; }
#sb-tag-overlay svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
#sb-tag-overlay svg line { stroke: #94a3b8; stroke-width: 1; }
#sb-tag-overlay svg line.sel { stroke: #7c3aed; stroke-width: 1.4; }
.sb-tag-chip {
  position: absolute; pointer-events: auto; cursor: pointer; user-select: none;
  font-size: 10px; font-weight: 600; line-height: 17px; white-space: nowrap;
  color: var(--c-text-475569); background: rgba(255,255,255,.94);
  border: 1px solid var(--c-border-cbd5e1); border-radius: 3px; padding: 0 6px;
}
.sb-tag-chip:hover { border-color: #7c3aed; color: var(--accent-text-deep); }
.sb-tag-chip.is-selected { background: #7c3aed; border-color: #7c3aed; color: #fff; }
.sb-tag-chip.is-off { opacity: .45; text-decoration: line-through; }
/* ── 3D viewport tools: measure overlay + tool sliders ──────────────────── */
#sb-measure-overlay { position: absolute; inset: 0; pointer-events: none; z-index: 7; display: none; }
#sb-measure-overlay svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
#sb-measure-overlay svg line { stroke: #0ea5e9; stroke-width: 1.5; }
#sb-measure-overlay svg circle { fill: #0ea5e9; stroke: #fff; stroke-width: 1; }
.sb-measure-lbl {
  position: absolute; transform: translate(-50%, -135%); pointer-events: none;
  font-size: 10.5px; font-weight: 700; color: #0c4a6e; white-space: nowrap;
  background: rgba(224,242,254,.95); border: 1px solid #7dd3fc; border-radius: 3px; padding: 1px 7px;
}
.sb-measure-lbl span { font-weight: 500; color: #0369a1; }
/* Bottom-left so they never collide with the wrapping tool row up top. */
#sb-3d-sliders { position: absolute; bottom: 8px; left: 8px; display: flex; flex-direction: column; gap: 4px; z-index: 10; }
#sb-3d-sliders > div {
  display: flex; align-items: center; gap: 7px; padding: 3px 9px;
  background: rgba(255,255,255,.94); border: 1px solid var(--neutral-border); border-radius: 5px;
  font-size: 10px; color: var(--accent-text-deep);
}
#sb-3d-sliders input[type="range"] { width: 130px; height: 4px; accent-color: #7c3aed; }
.sb-ls-bar { width: 4px; flex-shrink: 0; align-self: stretch; }
.sb-ls-handle { display: flex; align-items: center; color: #cbd5e1; cursor: grab; font-size: 12px; padding: 0 4px 0 8px; flex-shrink: 0; }
.sb-ls-handle:active { cursor: grabbing; }
.sb-ls-body { flex: 1; min-width: 0; padding: 7px 4px 7px 0; display: flex; flex-direction: column; gap: 1px; justify-content: center; }
.sb-ls-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sb-ls-typetag {
  font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: #94a3b8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sb-ls-supplier { font-size: 10.5px; font-weight: 700; color: var(--accent-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 50%; flex-shrink: 0; }
.sb-ls-pname { font-size: 13px; font-weight: 500; color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-ls-code { font-size: 10.5px; color: #64748b; font-family: var(--font-sans, 'Inter', sans-serif); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-ls-body .sb-ls-empty { color: #a78bfa; font-style: italic; font-size: 13px; }
.sb-ls-x {
  flex-shrink: 0; width: 30px; align-self: stretch; border: none; background: none; color: #cbd5e1;
  cursor: pointer; font-size: 12px;
}
.sb-ls-x:hover { background: var(--fail-bg-soft); color: var(--danger-text); }
/* Insert zone between strips */
/* Add-a-layer affordance between cards. It used to be opacity:0 until hovered,
   which made it undiscoverable — you had to know it was there. Keep it visible
   at rest and let hover promote it. */
.sb-ls-insert {
  position: relative; height: 20px; display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.sb-ls-insert > span {
  position: relative; z-index: 1;
  width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 9px; color: #8b5cf6; background: var(--surface); border: 1px solid var(--accent-line-soft);
  opacity: .75; transition: opacity .12s, background .12s, color .12s, border-color .12s, transform .12s;
}
.sb-ls-insert:hover > span {
  opacity: 1; color: #fff; background: #7c3aed; border-color: #7c3aed; transform: scale(1.12);
}
.sb-ls-insert::before {
  content: ''; position: absolute; left: 10px; right: 10px; top: 50%; height: 1px; background: var(--surface-accent);
}
.sb-ls-insert:hover::before { background: var(--c-bg-c4b5fd); }
.sb-ls-typepicker {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%); z-index: 50; margin-top: 2px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.14);
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px; padding: 6px; min-width: 220px;
}
.sb-ls-typepicker button {
  text-align: left; font-size: 11.5px; padding: 6px 9px; border: none; background: none; border-radius: 5px;
  color: var(--text-body); cursor: pointer; font-family: inherit; white-space: nowrap;
}
.sb-ls-typepicker button:hover { background: var(--c-bg-f3f0ff); color: var(--accent-text-strong); }
.sb-ls-tp-group {
  grid-column: 1 / -1; font-size: 9.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--text-faint); padding: 7px 9px 3px; margin-top: 2px;
  border-top: 1px solid var(--c-border-f1f5f9);
}
.sb-ls-tp-group:first-child { border-top: none; margin-top: 0; }
/* Pre-rendered 3D thumbnail (SystemThumbnail) — plain img, no client render */
.sb-thumb-img { width: 100%; height: 100%; object-fit: contain; border-radius: 4px; }
/* The system-info modal reuses the shared .dp-* "Universal detail-modal building
   blocks" + .cp-modal-overlay (see "Detail modal" section above) — no bespoke CSS. */

/* ── Library browser overlay (categorised catalog, card grid) ── */
.sb-lib-shell {
  width: min(1280px, calc(100vw - 48px)); height: calc(100vh - 64px);
  background: var(--surface); border-radius: 12px; overflow: hidden;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 64px rgba(30, 20, 70, .35);
}
.sb-lib-head {
  flex-shrink: 0; display: flex; align-items: center; gap: 12px;
  padding: 11px 14px; border-bottom: 1px solid var(--c-border-f1eefb); background: var(--c-bg-fbfaff);
}
.sb-lib-title { font-size: 14px; font-weight: 700; color: var(--accent-text-ink); white-space: nowrap; }
.sb-lib-title i { margin-right: 6px; color: var(--accent-text); }
.sb-lib-search { flex: 0 1 340px; }
.sb-lib-count { margin-left: auto; font-size: 12px; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
/* Filter bar — its own row so controls never crowd the title/search */
.sb-lib-filters {
  flex-shrink: 0; display: flex; align-items: center; gap: 6px 10px; flex-wrap: wrap;
  padding: 8px 14px; border-bottom: 1px solid var(--c-border-ece8f5); background: var(--surface);
}
.sb-lib-filters .sb-ff,
.sb-lib-filters .sb-ff-num {
  height: 26px; font-size: 11px; border: 1px solid var(--neutral-border); border-radius: 6px;
  background: var(--surface); color: var(--text-body); padding: 0 6px; outline: none; font-family: inherit;
}
.sb-lib-filters .sb-ff { cursor: pointer; max-width: 190px; }
.sb-lib-filters .sb-ff:focus,
.sb-lib-filters .sb-ff-num:focus { border-color: var(--brand-mid); }
.sb-lib-filters .sb-ff-num { width: 56px; }
.sb-lib-body { flex: 1 1 auto; min-height: 0; display: flex; }
.sb-lib-tree {
  flex: 0 0 240px; overflow-y: auto; border-right: 1px solid var(--c-border-ece8f5);
  background: var(--c-bg-fbfaff); padding: 10px 8px 16px;
}
.sb-lib-tree-section {
  font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: #a78bfa; padding: 10px 8px 4px;
}
.sb-lib-node {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 5px 8px; border: none; background: none; border-radius: 6px;
  font-size: 12px; font-weight: 600; color: var(--text-body); cursor: pointer;
  font-family: inherit; text-align: left; transition: background .1s;
}
.sb-lib-node:hover { background: var(--surface-tint); color: var(--accent-text-strong); }
.sb-lib-node.active { background: var(--surface-accent); color: var(--accent-text-deep); }
.sb-lib-node--leaf { padding-left: 22px; font-weight: 500; }
.sb-lib-node-lbl { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-lib-node-n { flex-shrink: 0; font-size: 10.5px; font-weight: 600; color: var(--text-faint); }
.sb-lib-node.active .sb-lib-node-n { color: var(--accent-text); }
.sb-lib-grid-wrap { flex: 1 1 auto; min-width: 0; overflow-y: auto; padding: 14px; }
.sb-lib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px; }
.sb-lib-card {
  position: relative; display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
  cursor: pointer; transition: border-color .12s, box-shadow .12s;
}
.sb-lib-card:hover { border-color: var(--accent-line); box-shadow: 0 4px 14px rgba(109, 40, 217, .10); }
.sb-lib-card-img {
  position: relative; /* anchors the absolute clip wrapper sbDrawThumb injects */
  height: 130px; background: var(--c-bg-f0f4f8); display: flex; align-items: center; justify-content: center;
  border-bottom: 1px solid var(--c-border-f1f5f9);
}
.sb-lib-card-img img { width: 100%; height: 100%; object-fit: cover; }
/* Server-rendered SVG sections are portrait; cover would crop the outer linings */
.sb-lib-card-img img[src$=".svg"] { object-fit: contain; padding: 4px 0; }
.sb-lib-card-img i { font-size: 26px; color: #d8b4fe; opacity: .4; }
/* Component legend under the 2D cross-section thumbnail */
.sb-thumb-legend {
  flex-shrink: 0; padding: 3px 7px 4px; background: rgba(255, 255, 255, .92);
  border-top: 1px solid var(--c-border-eef2f7); display: flex; flex-direction: column; gap: 1px;
  text-align: left;
}
.sb-tlg-row {
  font-size: 9px; line-height: 1.3; color: var(--c-text-475569);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sb-tlg-row.sb-tlg-frame { color: var(--accent-text-strong); font-weight: 600; }
.sb-tlg-row.sb-tlg-more { color: #94a3b8; font-style: italic; }
.sb-tlg-n { color: #94a3b8; font-weight: 600; margin-right: 3px; }
.sb-lib-card-body { padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 5px; }
.sb-lib-card-code { font-size: 12.5px; font-weight: 700; color: var(--text-ink); }
.sb-lib-card-sup { font-size: 11px; font-weight: 600; }
.sb-lib-card-badges { display: flex; flex-wrap: wrap; gap: 4px; }
.sb-lib-card-add {
  position: absolute; top: 8px; right: 8px; padding: 3px 10px; font-size: 11px;
  opacity: 0; transition: opacity .1s;
}
.sb-lib-card:hover .sb-lib-card-add { opacity: 1; }
.sb-lib-grid-msg { grid-column: 1 / -1; padding: 40px 16px; text-align: center; color: var(--text-faint); font-size: 12.5px; }
.sb-lib-grid-msg--err { color: #ef4444; }

.insp-body { flex: 1; overflow: hidden; display: flex; flex-direction: column; }
.insp-panel.collapsed .insp-body,
.insp-panel.collapsed .insp-footer { display: none; }

.insp-tab-content { display: none; flex: 1; overflow: hidden; flex-direction: column; }
.insp-tab-content.active { display: flex; }
.insp-table-wrap { flex: 1; overflow: auto; }
.insp-table { width: 100%; border-collapse: collapse; font-size: var(--tbl-font); }
.insp-table thead th {
    position: sticky; top: 0; background: var(--tbl-head-bg);
    font-size: var(--tbl-head-font); font-weight: 600; letter-spacing: normal; text-transform: none; color: var(--tbl-head-color);
    padding: var(--tbl-head-pad); border-bottom: 1px solid var(--tbl-head-border);
    white-space: nowrap; text-align: left;
}
.insp-table tbody tr { border-bottom: 1px solid var(--tbl-border); transition: background 0.1s; cursor: pointer; }
.insp-table tbody tr:hover { background: var(--tbl-row-hover); }
/* The row whose system is loaded in the builder. The tint alone reads as hover,
   so anchor it with an accent bar and keep it while hovering other rows. */
.insp-table tbody tr.selected,
.insp-table tbody tr.selected:hover { background: var(--tbl-row-selected); }
.insp-table tbody tr.selected { box-shadow: inset 3px 0 0 #7c3aed; }
.insp-table tbody tr.selected .insp-name-input { font-weight: 700; color: var(--accent-text-deep); }
.insp-table td { padding: var(--tbl-cell-pad); color: var(--tbl-cell-color); vertical-align: middle; white-space: nowrap; width: 1%; }
.insp-table th:last-child,
.insp-table td:last-child { width: auto; text-align: right; }
.insp-sys-dot { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex-shrink: 0; }
.insp-type-chip {
    display: inline-block; padding: 1px 6px; border-radius: 4px;
    font-size: 10px; font-weight: 600; text-transform: capitalize;
}
.insp-type-chip.wall      { background: var(--surface-accent); color: var(--accent-text-strong); }
.insp-type-chip.ceiling   { background: var(--c-bg-e0f2fe); color: #0369a1; }
.insp-type-chip.reference { background: var(--neutral-bg); color: var(--text-muted); }
.insp-td-num { font-variant-numeric: tabular-nums; color: var(--text-body); }
.insp-td-actions { display: flex; gap: 4px; justify-content: flex-end; }
.insp-icon-btn {
    width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
    border-radius: 4px; border: none; cursor: pointer; font-size: 10px; transition: background 0.12s;
}
.insp-icon-btn.view { background: var(--neutral-bg); color: var(--c-text-555555); }
.insp-icon-btn.view:hover { background: var(--c-bg-e5e7eb); }
.insp-icon-btn.dup { background: var(--surface-tint); color: var(--accent-text-strong); }
.insp-icon-btn.dup:hover { background: var(--surface-accent); }
.insp-icon-btn.del { background: var(--fail-bg); color: var(--danger-text); }
.insp-icon-btn.del:hover { background: var(--c-bg-fecaca); }
.insp-row-btns { display: flex; gap: 4px; }
.insp-row-edit-btns { display: none; gap: 4px; }
.insp-icon-btn.save { background: var(--pass-bg); color: #059669; }
.insp-icon-btn.save:hover:not(:disabled) { background: #a7f3d0; }
.insp-icon-btn.save:disabled { background: var(--neutral-bg); color: #d1d5db; cursor: not-allowed; }
.insp-icon-btn.disc { background: var(--fail-bg); color: var(--danger-text); }
.insp-icon-btn.disc:hover { background: var(--c-bg-fecaca); }
.insp-icon-btn.chg { background: var(--surface-accent); color: var(--accent-text); }
.insp-icon-btn.chg:hover { background: var(--surface-accent-2); }
.insp-empty {
    display: flex; flex-direction: column; align-items: center;
    justify-content: center; padding: 30px; color: var(--text-faint); font-size: 12px; gap: 8px;
}
.insp-footer {
    flex-shrink: 0; display: flex; align-items: center; gap: 8px;
    padding: 5px 14px; background: var(--c-bg-faf9fc); border-top: 1px solid var(--c-border-ece8f5);
    font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden;
}
/* SmartSpec finder footer — compact (half height) */
#insp-footer { padding: 2px 12px; font-size: 10.5px; line-height: 1.3; min-height: 0; }
#insp-footer i { font-size: 9px; }
.insp-foot-div { color: #d1d5db; }
.insp-foot-val { font-weight: 600; color: var(--text-body); }
.insp-foot-hint { margin-left: auto; color: var(--text-faint); font-style: italic; }
/* Compact footer for the SmartSpec finder panel */
#insp-footer { padding: 2px 12px; gap: 5px; font-size: 10px; line-height: 1.4; min-height: 20px; }
#insp-footer .insp-foot-val { font-size: 11px; }

/* Systems tab */
.insp-sec-label {
    font-size: 10px; font-weight: 700; letter-spacing: 0.05em; color: var(--text-muted);
    padding: 6px 12px 2px; display: flex; align-items: center; gap: 4px;
}

/* Component expand rows — chevron rotates 90° (right → down) when expanded,
   same pattern as .sq-l-caret elsewhere in the app. */
.insp-expand-btn {
    background: none; border: none; border-radius: 5px;
    width: 18px; height: 18px; font-size: 10px; line-height: 1;
    cursor: pointer; color: var(--accent-text); display: inline-flex;
    align-items: center; justify-content: center; padding: 0;
    margin-left: 4px; flex-shrink: 0;
    transition: background .12s, transform .15s;
}
.insp-expand-btn:hover { background: var(--surface-accent); }
.insp-expand-btn.open { transform: rotate(90deg); }
.insp-expand-btn:disabled { cursor: default; opacity: .6; }
.insp-comp-row { display: none; background: var(--c-bg-f9f8ff); }
.insp-comp-row.visible { display: table-row; }
.insp-comp-header { display: table-row; background: var(--c-bg-f3f0fd); }
.insp-comp-header td {
    padding: 3px 10px 3px 22px;
    font-size: 9px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-faint); border-bottom: 1px solid var(--c-border-e9e7fc);
}

/* Clickable system dot → opens a colour-swatch popover */
.insp-sys-dot { cursor: pointer; }
.insp-sys-dot:hover { outline: 2px solid rgba(0,0,0,.15); outline-offset: 1px; }
.insp-color-pop {
    position: fixed; z-index: 10001;
    display: flex; flex-wrap: wrap; gap: 4px; width: 132px;
    padding: 6px; background: var(--surface);
    border: 1px solid var(--c-border-d4d2cf); border-radius: 8px;
    box-shadow: 0 4px 14px rgba(0,0,0,.18);
}
.insp-color-pop .icp-sw {
    width: 20px; height: 20px; border-radius: 5px;
    border: 1px solid rgba(0,0,0,.15); cursor: pointer; padding: 0;
}
.insp-color-pop .icp-sw:hover { transform: scale(1.15); }
.insp-color-pop .icp-reset {
    width: 20px; height: 20px; border-radius: 5px; cursor: pointer;
    border: 1px solid var(--line); background: var(--neutral-bg); color: var(--text-muted); font-size: 10px;
}
.insp-color-pop .icp-reset:hover { background: var(--c-bg-e5e7eb); }

/* Count types panel (Counts tab) */
.insp-counts-body { padding: 8px 10px; }
.cnt-add-btn {
    border: 1px solid var(--line); background: var(--neutral-bg); color: var(--text-body);
    font-size: 10px; font-weight: 600; border-radius: 5px; padding: 2px 8px; cursor: pointer;
}
.cnt-add-btn:hover { background: var(--surface-accent); color: var(--accent-text-strong); }
.cnt-list { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.cnt-empty { font-size: 11px; color: var(--text-faint); padding: 8px 2px; }
.cnt-row {
    display: flex; align-items: center; gap: 8px;
    padding: 6px 8px; border: 1px solid var(--c-border-ececec); border-radius: 6px; cursor: pointer;
}
.cnt-row:hover { background: var(--surface-tint-2); }
.cnt-row.active { border-color: #7c3aed; background: var(--surface-tint); box-shadow: inset 3px 0 0 #7c3aed; }
.cnt-row .cnt-swatch { width: 14px; height: 14px; border-radius: 50%; border: 1px solid rgba(0,0,0,.15); }
.cnt-row .cnt-name { font-size: 12px; font-weight: 600; color: var(--text-body); flex: 1; }
.cnt-row .cnt-sku {
    border: 1px dashed var(--c-border-cbd5e1); background: var(--surface); color: #94a3b8;
    font-size: 9px; border-radius: 4px; padding: 1px 6px; cursor: pointer; white-space: nowrap;
}
.cnt-row .cnt-sku.linked { border-style: solid; border-color: #16a34a; color: #16a34a; background: var(--c-bg-f0fdf4); }
.cnt-row .cnt-sku:hover { border-color: #7c3aed; color: var(--accent-text); }
.cnt-row .cnt-tally { font-size: 13px; font-weight: 700; color: var(--text-ink); min-width: 22px; text-align: right; }
.cnt-row .cnt-del { border: none; background: none; color: var(--text-faint); cursor: pointer; font-size: 11px; }
.cnt-row .cnt-del:hover { color: var(--danger-text); }

/* Hidden line/area row (canvas visibility off) */
.line-item.row-hidden { opacity: .45; }
.line-item.row-hidden .insp-sys-name { text-decoration: line-through; }

/* Row highlight when a system is selected in the Systems tab */
.line-item.insp-row-highlight { background: var(--info-bg) !important; box-shadow: inset 3px 0 0 #2563eb; }
/* Sub-header row labelling the expanded component columns */
.insp-comp-head td {
    font-size: 9px; font-weight: 700; color: var(--text-faint);
    text-transform: uppercase; letter-spacing: .04em;
    background: var(--c-bg-f1eefc); border-bottom: 1px solid var(--c-border-e9e7fc);
}
.insp-comp-row td {
    padding: 3px 10px 3px 22px;
    font-size: 11px; color: var(--text-muted);
    border-bottom: 1px dashed var(--c-border-e9e7fc);
}
.insp-comp-name { font-weight: 600; color: var(--text-body); }
.insp-comp-supplier { color: var(--text-muted); }
.insp-comp-code { font-family: var(--font-sans, 'Inter', sans-serif); font-size: 10px; color: #6366f1; white-space: nowrap; }
.insp-comp-tag {
    display: inline-block;
    padding: 1px 7px; border-radius: 999px;
    background: var(--surface-accent); color: var(--accent-text-strong);
    font-size: 9px; font-weight: 600; text-transform: uppercase;
    letter-spacing: .03em; white-space: nowrap;
}
.insp-comp-layer {
    font-size: 10px; color: #a5b4fc;
    margin-left: 5px; font-family: var(--font-sans, 'Inter', sans-serif);
}
.insp-comp-price { color: #059669; font-weight: 500; }

/* InspTextStrong (cell-strong) — canonical inspection typography: weight 600, body-dark.
   Use on labels, values, names and any strong text inside inspection panels. */

/* InspInput — universal inline editable field for inspection panels */
.insp-name-input {
    border: none; outline: none; background: transparent;
    font-size: 12px; font-weight: 600; color: var(--text-strong);
    width: 100%; min-width: 60px;
    padding: 1px 3px; border-radius: 3px;
    transition: background .15s;
}
.insp-name-input:hover,
.insp-name-input:focus { background: var(--c-bg-faf5ff); }

.insp-notes-input {
    border: none; outline: none; background: transparent;
    font-size: 11px; color: var(--text-muted); width: 100%; min-width: 180px;
    padding: 1px 3px; border-radius: 3px;
    transition: background .15s;
}
.insp-notes-input:hover,
.insp-notes-input:focus { background: var(--surface-sunken); }

/* InspPerfCell — performance / spec data cell in inspection table rows */
.insp-perf-cell { font-size: 11px; color: var(--text-body); white-space: nowrap; vertical-align: middle; padding: 0 8px; }

/* ─── end Inspector Panel ─── */

.app-inbox-av {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--brand-light);
  color: var(--brand);
  font-size: 10px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 1px;
}

.app-inbox-body {
  flex: 1;
  min-width: 0;
}

.app-inbox-from {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
}

.app-inbox-subject {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-inbox-time {
  font-size: 10px;
  color: var(--text-tertiary);
  flex-shrink: 0;
  margin-top: 2px;
}

.app-unread {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brand);
  flex-shrink: 0;
  margin-top: 6px;
}

/* DBS Animations */
@keyframes appFadeUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.app-stat {
  animation: appFadeUp 0.35s ease both;
}

.app-stat:nth-child(1) {
  animation-delay: 0.05s;
}

.app-stat:nth-child(2) {
  animation-delay: 0.1s;
}

.app-stat:nth-child(3) {
  animation-delay: 0.15s;
}

.app-stat:nth-child(4) {
  animation-delay: 0.2s;
}

/* Preserve animations on consolidated app-cardbox--tile */
.app-cardbox--tile {
  animation: appFadeUp 0.35s ease both;
}

.app-cardbox--tile:nth-child(1) {
  animation-delay: 0.1s;
}

.app-cardbox--tile:nth-child(2) {
  animation-delay: 0.13s;
}

.app-cardbox--tile:nth-child(3) {
  animation-delay: 0.16s;
}

.app-cardbox--tile:nth-child(4) {
  animation-delay: 0.19s;
}

.app-cardbox--tile:nth-child(5) {
  animation-delay: 0.22s;
}

.app-cardbox--tile:nth-child(6) {
  animation-delay: 0.25s;
}

.app-cardbox--tile:nth-child(7) {
  animation-delay: 0.28s;
}

.app-panel {
  animation: appFadeUp 0.4s ease both;
}

.app-panel:nth-child(1) {
  animation-delay: 0.2s;
}

.app-panel:nth-child(2) {
  animation-delay: 0.25s;
}

.app-panel:nth-child(3) {
  animation-delay: 0.3s;
}

/* DBS Responsive */

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

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

/* Phone: the sidebar is an off-canvas drawer. Closed by default; the toggle
   (.app-nav-toggle) adds .app-layout--nav-open, the overlay closes it. The
   grid collapses to one column so the page uses the full width. The
   !important flags beat the :has() column rules above, which set a 280px
   first column even though the sidebar no longer sits in it. */
@media (max-width: 767.98px) {
  .app-layout {
    grid-template-columns: minmax(0, 1fr) !important;
  }
  .app-layout > .app-topbar,
  .app-layout > .app-workspace {
    grid-column: 1 !important;
  }
  .app-layout > .app-topbar .app-topbar-bar {
    padding-left: 52px;
  }
  .app-sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(var(--sidebar-w), 85vw);
    z-index: 60;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.2s ease, visibility 0s linear 0.2s;
    box-shadow: none;
  }
  .app-layout--nav-open .app-sidebar {
    transform: none;
    visibility: visible;
    transition: transform 0.2s ease;
    box-shadow: 0 0 24px rgba(0, 0, 0, 0.25);
  }
  .app-sidebar-collapse-btn {
    display: none;
  }
  .app-layout > .app-nav-toggle {
    display: inline-flex;
  }
  .app-layout--nav-open .app-nav-toggle {
    display: none;
  }
  .app-layout--nav-open .app-nav-overlay {
    display: block;
  }
}

/* Drawer toggle + overlay (phone only; hidden on desktop) */
.app-nav-toggle {
  display: none;
  position: fixed;
  top: 8px;
  left: 10px;
  z-index: 55;
  width: 36px;
  height: 36px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-primary);
  cursor: pointer;
}
.app-nav-toggle:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}
.app-nav-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 58;
  background: rgba(0, 0, 0, 0.45);
}

/* Sidebar groups and plan locks */
[x-cloak] { display: none !important; }
.app-nav-sub {
  padding-left: 28px;
  font-size: 13px;
}
.app-nav-group-btn {
  width: 100%;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
}
.app-nav-chev {
  margin-left: auto;
  font-size: 11px !important;
  transition: transform 0.15s;
}
.app-nav-chev--open {
  transform: rotate(180deg);
}
.app-nav-item .app-nav-lock {
  margin-left: auto;
  font-size: 11px;
  width: auto;
  color: var(--text-tertiary);
}
.app-sidebar--collapsed .app-nav-sub {
  padding-left: 10px;
}
/* PRO tag on a locked sidebar item (a small outlined pill, like the design). */
.app-nav-item .app-nav-pro {
  margin-left: auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.5;
  padding: 0 7px;
  border: 1px solid var(--c-border-cfc6ea);
  border-radius: 99px;
  color: #4b2f96;
  background: var(--surface, var(--surface));
}

.flr-row-invalid {
  background: var(--fail-bg) !important;
}

.flr-col-name { text-align: left; width: 16%; }

.flr-col-rl,
.flr-col-height,
.flr-col-thk {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.flr-col-rl { width: 12%; }
.flr-col-height { width: 13%; }
.flr-col-type { text-align: left; width: 33%; }
.flr-col-thk { width: 12%; }

.flr-col-actions {
  text-align: center;
  width: 14%;
}

.flr-calc-value {
  padding: 8px;
  font-size: 10px;
  color: #64748b;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  height: 34px;
  box-sizing: border-box;
}

.flr-tbl-in {
  width: 100%;
  height: 34px;
  padding: 4px 8px;
  border: none;
  border-radius: 0;
  font-size: 10px;
  color: var(--text-strong);
  background: var(--surface);
  box-sizing: border-box;
}

.flr-col-rl .flr-tbl-in,
.flr-col-thk .flr-tbl-in {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.flr-tbl-in:focus {
  outline: none;
  box-shadow: inset 0 0 0 1px #cbd5e1;
}

.flr-tbl-sel {
  width: 100%;
  height: 34px;
  padding: 4px 18px 4px 8px;
  border: none;
  border-radius: 0;
  font-size: 10px;
  color: var(--text-strong);
  background: var(--surface);
  box-sizing: border-box;
}

.flr-tbl-sel:focus {
  outline: none;
  box-shadow: inset 0 0 0 1px #cbd5e1;
}

/* Footer action under the floors table. Aligned to the table's cell padding so
   it reads as the last row rather than a detached strip — and sized to its own
   label: the table is as wide as the pressure columns need, so a full-width
   button rendered as a metre of empty chrome. */
.flr-add-wrap {
  padding: 8px 14px;   /* 14px = the x half of --tbl-cell-pad */
  border-top: 1px solid var(--tbl-border, var(--line-faint));
}

.flr-add-btn.app-btn {
  color: var(--brand, var(--brand));
  border-color: var(--brand-mid, var(--brand-mid));
}
/* ═══════════════════════════════════════════════════════════ */

/* ── Drawing File Uploader (dfu-*) ────────────────────────
   Reusable drop-zone + per-file type assignment.
   Used via {% include "includes/drawing_uploader.html" %}
   across Projects, SmartQuote, SmartSpec, Reviews, etc.
   ──────────────────────────────────────────────────────── */
.dfu-zone {
  border: 2px dashed var(--accent-line);
  border-radius: 10px;
  padding: 24px 20px 20px;
  text-align: center;
  cursor: pointer;
  background: var(--c-bg-fdfaff);
  transition: border-color .15s, background .15s;
  outline: none;
  position: relative;
}
.dfu-zone:hover,
.dfu-zone:focus { border-color: #8e75da; background: var(--c-bg-f5f0ff); }
.dfu-zone.drag-over { border-color: #8e75da; background: var(--surface-accent); }
.dfu-zone.has-files { border-style: solid; border-color: var(--accent-line); background: var(--c-bg-faf8ff); padding: 14px 20px; }
.dfu-zone-icon { font-size: 24px; color: #c4b5fd; margin-bottom: 6px; }
.dfu-zone.has-files .dfu-zone-icon { font-size: 18px; }
.dfu-zone-text { font-size: 12px; color: var(--text-body-2); }
.dfu-zone-text strong { color: var(--brand); }
.dfu-browse-link { color: var(--brand); font-weight: 600; text-decoration: underline; cursor: pointer; }
.dfu-zone-hint { font-size: 13px; color: var(--text-faint); margin-top: 4px; }
.dfu-error { color: var(--danger-text); font-size: 12px; margin-top: 6px; }

/* File list */
.dfu-list { margin-top: 10px; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.dfu-list-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px 12px; background: var(--surface-sunken);
  border-bottom: 1px solid var(--line); font-size: 11.5px; color: var(--text-muted); font-weight: 500;
}
.dfu-list-clear {
  font-size: 11px; color: var(--text-faint); background: none; border: none; cursor: pointer;
  padding: 2px 6px; border-radius: 4px; transition: color .1s, background .1s;
}
.dfu-list-clear:hover { color: var(--danger-text); background: var(--fail-bg-soft); }
.dfu-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; font-size: 12px; color: var(--text-body);
  border-bottom: 1px solid var(--line-faint); background: var(--surface);
}
.dfu-item:last-child { border-bottom: none; }
.dfu-item-ext {
  font-size: 9px; font-weight: 700; color: var(--brand);
  background: var(--surface-accent); border-radius: 4px; padding: 2px 5px;
  text-transform: uppercase; flex-shrink: 0; letter-spacing: .04em; min-width: 30px; text-align: center;
}
.dfu-item-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.dfu-item-type {
  flex-shrink: 0; font-size: 11.5px;
  border: 1px solid var(--line); border-radius: 6px;
  padding: 3px 6px; color: var(--text-body); background: var(--surface-sunken);
  cursor: pointer; min-width: 140px;
}
.dfu-item-type:focus { outline: 2px solid #8e75da; outline-offset: 1px; }
.dfu-item-dir {
  flex-shrink: 0; font-size: 11.5px;
  border: 1px solid var(--line); border-radius: 6px;
  padding: 3px 6px; color: var(--text-body); background: var(--surface-sunken);
  cursor: pointer; min-width: 100px;
}
.dfu-item-dir:focus { outline: 2px solid #8e75da; outline-offset: 1px; }
.dfu-item-size { font-size: 11px; color: var(--text-faint); flex-shrink: 0; min-width: 42px; text-align: right; }
.dfu-item-rm {
  background: none; border: none; color: #d1d5db; cursor: pointer;
  padding: 3px 6px; font-size: 12px; flex-shrink: 0; line-height: 1;
  border-radius: 4px; transition: color .1s, background .1s;
}
.dfu-item-rm:hover { color: var(--danger-text); background: var(--fail-bg-soft); }
.dfu-item-warn { color: #d97706; font-size: 11px; flex-shrink: 0; }

/* ============================================================================
   Library workspace shared widgets — import modal, audit history, archive
   (used via static/js/library_workspace.js across all Engineering Libraries)
   ==========================================================================*/

/* Import modal */
.libws-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(17, 24, 39, .45);
  display: none; align-items: center; justify-content: center;
}
.libws-overlay.open { display: flex; }
.libws-modal {
  background: var(--surface); border-radius: 12px; width: 440px; max-width: 92vw;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .25); overflow: hidden;
  display: flex; flex-direction: column;
}
.libws-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; background: var(--c-bg-ede9fc); border-bottom: 1px solid var(--c-border-d8ccee);
  font-weight: 700; font-size: 13px; color: #5b4282; text-transform: capitalize;
}
.libws-x {
  background: none; border: none; font-size: 20px; line-height: 1;
  color: var(--text-muted); cursor: pointer;
}
.libws-modal-body { padding: 16px; }
.libws-modal-foot {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 16px; border-top: 1px solid var(--c-border-eef0f3); background: var(--c-bg-fafafe);
}
.libws-hint { font-size: 12px; color: var(--text-muted); margin: 0 0 12px; line-height: 1.5; }
.libws-file { width: 100%; font-size: 12px; margin-bottom: 10px; }
.libws-btn {
  padding: 6px 14px; font-size: 12px; font-weight: 600; border-radius: 6px;
  border: 1px solid var(--c-border-d8ccee); background: var(--surface); color: #5b4282; cursor: pointer;
}
.libws-btn-primary { background: #7c5fb3; color: #fff; border-color: #7c5fb3; }
.libws-ok { font-size: 12px; color: var(--pass-fg); background: var(--pass-bg); padding: 8px 10px; border-radius: 6px; }
.libws-err { font-size: 12px; color: var(--fail-fg); background: var(--fail-bg); padding: 8px 10px; border-radius: 6px; }
.libws-errlist { font-size: 11px; color: var(--fail-fg); margin: 8px 0 0; padding-left: 18px; max-height: 160px; overflow-y: auto; }

/* Audit history timeline */
.libws-hist-empty { font-size: 12px; color: var(--text-faint); padding: 8px 0; }
.libws-hist-item { border-left: 2px solid var(--c-border-ddd6f4); padding: 6px 0 6px 10px; margin-bottom: 6px; }
.libws-hist-head { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.libws-hist-type { font-weight: 700; padding: 1px 7px; border-radius: 99px; font-size: 10px; }
.libws-hist-created { background: var(--pass-bg); color: var(--pass-fg); }
.libws-hist-changed { background: var(--info-bg); color: var(--info-fg); }
.libws-hist-deleted { background: var(--fail-bg); color: var(--fail-fg); }
.libws-hist-when { color: var(--text-body); }
.libws-hist-who { color: var(--text-faint); margin-left: auto; }
.libws-hist-change { font-size: 11px; color: var(--text-body-2); padding: 2px 0 0 2px; }

/* Archived row + restore button
   lib-badge-archived is kept for compat; new code → app-badge--warning */
.lib-row-archived { opacity: .6; }

/* ════════════════════════════════════════════════════
   Minimal table input — tbl-input
   For inputs embedded directly in table cells.
   Transparent background + underline-only at rest;
   subtle highlight on hover/focus. No bordered-box look.
   ════════════════════════════════════════════════════ */
.tbl-input {
  width: 100%;
  border: none;
  border-bottom: 1px solid transparent;
  border-radius: 0;
  background: transparent;
  padding: 2px 2px;
  font-size: var(--fs-md, 12px);
  font-family: inherit;
  color: var(--text-body);
  outline: none;
  transition: border-color .15s, background .15s;
  box-sizing: border-box;
}
.tbl-input:hover  { border-bottom-color: var(--neutral-border); }
.tbl-input:focus  { border-bottom-color: #7c3aed; background: rgba(124,58,237,.03); }

/* ════════════════════════════════════════════════════
   Detail panel input — detail-panel-input
   For inputs inside right-rail edit panels / modals.
   Replaces repeated inline style="border:1px solid #d8ccee;
   border-radius:5px;padding:3px 7px;font-size:12px;min-width:0;"
   ════════════════════════════════════════════════════ */
.detail-panel-input {
  /* Side-panel layout S-A: this is app-input at the panel's 30px height. */
  flex: 1;
  height: var(--tb-h, 30px);
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  padding: 0 10px;
  font-size: var(--tb-font, 12px);
  font-family: inherit;
  min-width: 0;
  background: var(--surface, var(--surface));
  color: var(--text-primary, var(--text-body));
  outline: none;
  transition: border-color .12s;
  box-sizing: border-box;
}
.detail-panel-input::placeholder { color: var(--text-tertiary, var(--text-faint)); }
.detail-panel-input:hover { border-color: var(--brand-mid, var(--brand-mid)); }
.detail-panel-input:focus { border-color: var(--brand, #8e75da); }
.detail-panel-input:disabled { opacity: .5; cursor: not-allowed; background: var(--neutral-bg); }
/* Numbers are right-aligned; units live in the label, e.g. "BMT (mm)". */
.detail-panel-input[type="number"],
.detail-panel-input--num { text-align: right; font-variant-numeric: tabular-nums; }
select.detail-panel-input { padding-right: 24px; }

/* ════════════════════════════════════════════════════
   Supplier plain label — pl-supplier-label
   Replaces pill/badge supplier display with plain branded text.
   Uses --supplier-color CSS variable set by JS for
   per-supplier brand colour without a badge background.
   ════════════════════════════════════════════════════ */
.pl-supplier-label {
  font-size: var(--fs-base, 11px);
  font-weight: 600;
  color: var(--supplier-color, var(--brand, var(--brand)));
  white-space: nowrap;
  letter-spacing: .01em;
}

/* ════════════════════════════════════════════════════
   litespec-table — compact key/value detail table
   Used in project workspace overview panels.
   ════════════════════════════════════════════════════ */
.litespec-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--tbl-font);
}
.litespec-table th,
.litespec-table td {
  padding: var(--tbl-cell-pad);
  vertical-align: top;
  border-bottom: 1px solid var(--tbl-border);
  text-align: left;
}
.litespec-table th {
  width: 140px;
  font-weight: 700;
  color: var(--tbl-head-color);
  font-size: var(--tbl-head-font);
  text-transform: uppercase;
  letter-spacing: .04em;
  white-space: nowrap;
}
.litespec-table td { color: var(--tbl-cell-color); }
.litespec-table tr:last-child th,
.litespec-table tr:last-child td { border-bottom: none; }

/* ════════════════════════════════════════════════════════════════════
   WORKSPACE DESIGN SYSTEM (WDS)
   Single source of truth for the modern workspace standard:
   tokens → cards → page header / back nav → detail panel → tables →
   list views → icon system → empty / loading states.
   All modules consume these classes; no module-specific re-implementations.
   ════════════════════════════════════════════════════════════════════ */

:root {
  /* Elevation scale — neutral, soft (Linear / Stripe style) */
  --shadow-xs: 0 1px 2px rgba(16, 24, 40, .05);
  --shadow-sm: 0 1px 3px rgba(16, 24, 40, .07), 0 1px 2px rgba(16, 24, 40, .04);
  --shadow-md: 0 4px 12px rgba(16, 24, 40, .08);
  --shadow-lg: 0 12px 32px rgba(16, 24, 40, .14);

  /* Spacing rhythm */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;

  /* Surface + border (neutral language; brand reserved for accents) */
  --wds-border:        #e5e7eb;
  --wds-border-subtle: #f3f4f6;
  --wds-surface:       #ffffff;
  --wds-surface-2:     #f9fafb;
  --wds-radius:        8px;
  --wds-radius-lg:     12px;

  /* Semantic icon / action colours */
  --icon-primary: #7c5fb3;
  --icon-success: #059669;
  --icon-warning: #d97706;
  --icon-danger:  #dc2626;
  --icon-info:    #2563eb;
  --icon-muted:   #9ca3af;
}

/* ── Workspace page container ─────────────────────────
   Standard scroll + width treatment for app-workspace
   content. Replaces ad-hoc container/mx-auto wrappers.
   .ws-page          — fluid, consistent padding rhythm
   .ws-page--narrow  — centred reading width (hubs, forms)
   .ws-page--wide    — data-dense tables
   ─────────────────────────────────────────────────── */
.ws-page {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-5) var(--space-6) var(--space-8);
}
.ws-page--narrow { max-width: 920px; }
.ws-page--wide   { max-width: none; }

/* The workspace itself owns scrolling — identical across modules */
.app-workspace { overflow-y: auto; background: var(--wds-surface-2); }
.app-workspace > .ws-page { flex-shrink: 0; }

/* ════════════════════════════════════════════════════
   Universal Page Header + Back Navigation
   breadcrumb / back / title / subtitle / actions —
   rendered by templates/components/page_header.html
   ════════════════════════════════════════════════════ */
.page-header {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.page-header-main { flex: 1; min-width: 0; }

.page-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: var(--fs-md, 12px);
  color: var(--icon-muted);
  margin: 0 0 4px;
}
.page-breadcrumb a {
  color: var(--text-muted);
  text-decoration: none;
  transition: color .12s;
}
.page-breadcrumb a:hover { color: var(--icon-primary); }
.page-breadcrumb .crumb-sep { font-size: 9px; color: #d1d5db; }
.page-breadcrumb .crumb-current { color: var(--text-body); font-weight: 500; }

.page-title {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 18px;
  font-weight: 700;
  color: var(--text-primary);
  margin: 0;
  line-height: 1.25;
}
.page-title-icon {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: linear-gradient(135deg, var(--surface-accent), var(--c-bg-ddd6f4));
  color: var(--icon-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  flex-shrink: 0;
}
.page-subtitle {
  font-size: var(--fs-md, 12px);
  color: var(--text-muted);
  margin: 3px 0 0;
}

.page-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  padding-top: 2px;
}

/* PageBackButton — universal back navigation (no history-only hacks) */
.page-back-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  min-width: 32px;
  border-radius: var(--wds-radius);
  border: 1px solid var(--wds-border);
  background: var(--wds-surface);
  color: var(--text-muted);
  font-size: 13px;
  text-decoration: none;
  cursor: pointer;
  flex-shrink: 0;
  margin-top: 1px;
  transition: background .12s, color .12s, border-color .12s, transform .12s;
}
.page-back-btn:hover {
  background: var(--surface-tint);
  border-color: var(--c-border-ddd6f4);
  color: var(--icon-primary);
}
.page-back-btn:hover i { transform: translateX(-1px); }
.page-back-btn i { transition: transform .15s ease; }
.page-back-btn:focus-visible { outline: 2px solid var(--icon-primary); outline-offset: 1px; }

/* ════════════════════════════════════════════════════
   Shared right-rail detail panel — detail-panel
   Replaces per-template #xxDetailPanel <style> blocks
   across all library / catalog manage workspaces.
   ════════════════════════════════════════════════════ */
.detail-panel {
  position: fixed;
  top: var(--topbar-offset, 125px);
  right: 0;
  bottom: 0;
  width: 400px;
  max-width: 94vw;
  background: var(--wds-surface);
  border-left: 1px solid var(--wds-border);
  box-shadow: -4px 0 20px rgba(0, 0, 0, .08);
  z-index: 200;
  transform: translateX(100%);
  transition: transform .2s ease;
  display: flex;
  flex-direction: column;
}
.detail-panel.open { transform: translateX(0); }
.detail-panel--sm { width: 380px; }
.detail-panel--md { width: 420px; }

/* Catalog manage right-rail panel — match library detail panels
   (full height + fixed width). ID selector overrides the conflicting
   .detail-panel.open width rule from the engineering slide-out panel. */
#pmDetailPanel {
  top: 0;
  right: 0;
  bottom: 0;
  width: 420px;
  max-width: 94vw;
  z-index: 9000;
  overflow-y: auto;
}
#pmDetailPanel.open { width: 420px; }
#pmDetailPanel.detail-panel--sm,
#pmDetailPanel.detail-panel--sm.open { width: 380px; }

.detail-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 12px var(--space-4);
  background: var(--c-bg-ede9fc);
  border-bottom: 1px solid var(--c-border-d8ccee);
  flex-shrink: 0;
}
.detail-panel-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
  font-size: 13px;
  color: #5b4282;
}
.detail-panel-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.detail-panel-body {
  padding: var(--space-4);
  flex: 1;
  overflow-y: auto;
}

/* Side-panel footer (layout S-A): Save / Cancel, right-aligned, top border. */
.detail-panel-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px var(--space-4, 16px);
  border-top: 1px solid var(--neutral-border, var(--line));
  background: var(--surface, var(--surface));
  flex-shrink: 0;
}
.detail-panel-foot[hidden] { display: none; }
/* Close button in the panel head — an app-btn app-btn-ghost app-btn-icon in a panel. */
.detail-panel-close { margin-left: auto; }

/* Fixed-width label variant for detail-panel edit forms */
.detail-label--fixed { flex: 0 0 120px; color: var(--text-body-2); font-weight: 500; }
.detail-row--center { align-items: center; }
.detail-row--top { align-items: flex-start; }
.detail-hint { font-size: 10.5px; color: var(--icon-muted); margin-top: 4px; padding: 2px 0; }
.detail-code {
  font-family: var(--font-sans, 'Inter', sans-serif);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-body);
}
.detail-checkbox { width: 16px; height: 16px; }
.detail-color-input {
  width: 34px;
  height: 28px;
  border: 1px solid var(--c-border-d8ccee);
  border-radius: 5px;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
}
.detail-panel-input--mono { font-family: var(--font-sans, 'Inter', sans-serif); margin-left: 6px; }
.detail-form-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
  justify-content: flex-end;
}
.detail-add-row { margin-top: 8px; }
/* Inline colour swatch (dynamic background set via style attr) */
.color-swatch {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, .15);
  vertical-align: middle;
  margin-right: 6px;
}
.detail-unit { color: var(--icon-muted); font-weight: 400; font-size: 10px; }
.detail-empty { color: #d1d5db; }
.detail-note { font-size: var(--fs-md, 12px); color: var(--text-body); padding: 4px 0; }
.detail-panel-textarea {
  width: 100%;
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  padding: 6px 10px;
  font-size: var(--tb-font, 12px);
  font-family: inherit;
  resize: vertical;
  box-sizing: border-box;
  transition: border-color .12s;
}
.detail-panel-textarea:hover { border-color: var(--brand-mid, var(--brand-mid)); }
.detail-panel-textarea:focus { outline: none; border-color: var(--brand, #8e75da); }

/* ════════════════════════════════════════════════════
   Detail panel expand / maximize
   Works on all right-rail detail panel variants:
   #matDetailPanel, #sectionDetailPanel, #fixDetailPanel,
   #conDetailPanel, #lcDetailPanel, #pmDetailPanel,
   and .detail-panel (WDS catalog manage panels).
   ════════════════════════════════════════════════════ */
.dp-panel-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  z-index: 9998;
}
.dp-panel-backdrop.active { display: block; }

/* Expanded — wider rail */
#matDetailPanel.dp-expanded,
#sectionDetailPanel.dp-expanded,
#fixDetailPanel.dp-expanded,
#conDetailPanel.dp-expanded,
#lcDetailPanel.dp-expanded,
#pl-detail-panel.dp-expanded { width: 640px; }

#pmDetailPanel.dp-expanded,
.detail-panel.dp-expanded { width: 640px; max-width: 94vw; }

/* Maximized — covers full viewport */
#matDetailPanel.dp-maximized,
#sectionDetailPanel.dp-maximized,
#fixDetailPanel.dp-maximized,
#conDetailPanel.dp-maximized,
#lcDetailPanel.dp-maximized,
#pl-detail-panel.dp-maximized,
#pmDetailPanel.dp-maximized,
.detail-panel.dp-maximized {
  width: 100vw !important;
  max-width: 100vw !important;
  top: 0 !important;
  z-index: 9999;
}

/* ════════════════════════════════════════════════════
   Refactored Detail Panels (Material, Section, Fixing, Connector, Load Case)
   Full-height right slide-in structure matching pl-detail-panel
   ════════════════════════════════════════════════════ */

/* ── Material Detail Panel ── */
#matDetailPanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: -4px 0 20px rgba(0,0,0,.08);
  overflow-y: auto; z-index: 9000; transform: translateX(100%);
  transition: transform .2s ease; display: flex; flex-direction: column;
}
#matDetailPanel.open { transform: translateX(0); }

/* ── Section Detail Panel ── */
#sectionDetailPanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: -4px 0 20px rgba(0,0,0,.08);
  overflow-y: auto; z-index: 9000; transform: translateX(100%);
  transition: transform .2s ease; display: flex; flex-direction: column;
}
#sectionDetailPanel.open { transform: translateX(0); }

/* ── Fixing Detail Panel ── */
#fixDetailPanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: -4px 0 20px rgba(0,0,0,.08);
  overflow-y: auto; z-index: 9000; transform: translateX(100%);
  transition: transform .2s ease; display: flex; flex-direction: column;
}
#fixDetailPanel.open { transform: translateX(0); }

/* ── Connector Detail Panel ── */
#conDetailPanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: -4px 0 20px rgba(0,0,0,.08);
  overflow-y: auto; z-index: 9000; transform: translateX(100%);
  transition: transform .2s ease; display: flex; flex-direction: column;
}
#conDetailPanel.open { transform: translateX(0); }

/* ── Load Case Detail Panel ── */
#lcDetailPanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px;
  background: var(--surface); border-left: 1px solid var(--line);
  box-shadow: -4px 0 20px rgba(0,0,0,.08);
  overflow-y: auto; z-index: 9000; transform: translateX(100%);
  transition: transform .2s ease; display: flex; flex-direction: column;
}
#lcDetailPanel.open { transform: translateX(0); }

/* ════════════════════════════════════════════════════
   Data table extensions — density modes + states
   primary-table is the universal grid; default density
   is Comfortable. Libraries/catalog opt into Compact.
   ════════════════════════════════════════════════════ */
/* Compact — data-dense reference tables (still readable: only ~25% tighter) */
.primary-table--compact thead th { padding: 6px 10px; font-size: 12px; }
.primary-table--compact td       { padding: 5px 10px; font-size: 12px; white-space: nowrap; }
/* Dense — maximum rows on screen (calc grids, large catalogues) */
.primary-table--dense thead th { padding: 4px 9px; font-size: 11px; }
.primary-table--dense td       { padding: 4px 9px; font-size: 12px; white-space: nowrap; }
/* Spacious — content-led browsing */
.primary-table--spacious thead th { padding: 12px 16px; }
.primary-table--spacious td       { padding: 12px 16px; }

/* Sticky header polish — subtle separation while scrolling */
.primary-table thead th { z-index: 2; box-shadow: 0 1px 0 var(--wds-border); }

/* Optional zebra striping */
.primary-table--striped tbody tr:nth-child(even) { background: var(--c-bg-fcfcfe); }
.primary-table--striped tbody tr:hover { background: var(--surface-tint-2); }

/* Table state cells — Loading / empty / error rows */
.cell-state {
  text-align: center;
  padding: 30px 10px !important;
  color: var(--icon-muted);
  font-size: var(--fs-md, 12px);
}
.cell-state--error { color: var(--icon-danger); }
.cell-strong { font-weight: 600; color: var(--text-body); }
.cell-mono { font-family: var(--font-sans, 'Inter', sans-serif); font-size: 12px; }
.cell-muted { color: var(--text-muted); font-size: 11px; }
.td-nowrap { white-space: nowrap; }
.txt-warn-xs { color: #b45309; font-size: 10px; font-weight: 600; }
.u-mr-6 { margin-right: 6px; }

/* Status pill — alias of app-badge (see app-badge; --on = success, --off = danger). */

/* ════════════════════════════════════════════════════
   List view infrastructure — ws-view-toggle + lv-row
   Card ⇄ List switching for catalog / libraries.
   ════════════════════════════════════════════════════ */
.ws-view-toggle {
  display: inline-flex;
  border: 1px solid var(--wds-border);
  border-radius: var(--wds-radius);
  overflow: hidden;
  background: var(--wds-surface);
}
.ws-view-toggle button {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px;
  font-size: 11.5px;
  font-weight: 600;
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background .12s, color .12s;
}
.ws-view-toggle button + button { border-left: 1px solid var(--wds-border); }
.ws-view-toggle button:hover { background: var(--wds-surface-2); }
.ws-view-toggle button.active,
.ws-view-toggle button[aria-pressed="true"] { background: var(--c-bg-ede9fc); color: #5b4282; }
.ws-view-toggle button:focus-visible { outline: 2px solid var(--brand-mid, var(--brand-mid)); outline-offset: -2px; }

/* Segmented control (app-seg): the generic form of ws-view-toggle. One choice
   of a few, always visible. Buttons carry .active / .is-active or
   aria-pressed="true" / aria-selected="true". Height follows --tb-h. */
.app-seg {
  display: inline-flex;
  height: var(--tb-h, 30px);
  border: 1px solid var(--wds-border);
  border-radius: var(--wds-radius);
  overflow: hidden;
  background: var(--wds-surface);
  box-sizing: border-box;
  flex-shrink: 0;
}
.app-seg > button {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 11px;
  font-size: 11.5px;
  font-weight: 600;
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background .12s, color .12s;
}
.app-seg > button + button { border-left: 1px solid var(--wds-border); }
.app-seg > button:hover { background: var(--wds-surface-2); }
.app-seg > button.active,
.app-seg > button.is-active,
.app-seg > button[aria-pressed="true"],
.app-seg > button[aria-selected="true"] { background: var(--c-bg-ede9fc); color: #5b4282; }
.app-seg > button:disabled { opacity: .5; cursor: not-allowed; }
.app-seg > button:focus-visible { outline: 2px solid var(--brand-mid, var(--brand-mid)); outline-offset: -2px; }
/* Hidden until script shows it: markup ships class="is-hidden" and JS removes
   the class (or toggles it). Beats component display rules, unlike [hidden]. */
.is-hidden { display: none !important; }

/* Phone topbar wrap (was an inline <style> in components/_app_topbar.html):
   the panel zones (search, filters, page actions) are single-row flex boxes,
   so under 768px each zone wraps instead of running off the edge. */
@media (max-width: 767.98px) {
  .app-topbar-panel { max-width: 100%; }
  .app-topbar-panel .tb-zone { flex-wrap: wrap; row-gap: 6px; min-width: 0; }
  .app-topbar-panel .tb-zone > * { max-width: 100%; }
}

/* ════════════════════════════════════════════════════
   Icon system — semantic colours + micro-animations
   Action icon mapping (use everywhere):
     add fa-plus | edit fa-pencil-alt | delete fa-trash-can |
     archive fa-box-archive | copy fa-copy | duplicate fa-clone |
     export fa-file-export | import fa-file-import |
     download fa-download | upload fa-upload | view fa-eye |
     search fa-magnifying-glass | settings fa-gear |
     refresh fa-rotate-right | save fa-floppy-disk |
     close fa-xmark
   ════════════════════════════════════════════════════ */
.ic-primary { color: var(--icon-primary); }
.ic-success { color: var(--icon-success); }
.ic-info    { color: var(--icon-info); }
.ic-muted   { color: var(--icon-muted); }

/* Accessible icon button — 28px visual, ≥32px touch target */
.ws-icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  min-width: 28px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  font-size: 12px;
  cursor: pointer;
  transition: background .12s, color .12s;
}
.ws-icon-btn::after { content: ''; position: absolute; inset: -3px; }
.ws-icon-btn:hover { background: var(--c-bg-f3f0fa); color: var(--icon-primary); }
.ws-icon-btn:focus-visible { outline: 2px solid var(--icon-primary); outline-offset: 1px; }

/* ════════════════════════════════════════════════════
   Child-record editor widgets — child-btn / child-form
   Inline sub-tables inside detail panels (capacity sets,
   hole patterns, components…). Shared by all libraries.
   ════════════════════════════════════════════════════ */
.child-btn {
  padding: 2px 7px;
  font-size: 10px;
  font-weight: 600;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: filter .12s;
}
.child-btn:hover { filter: brightness(.95); }
.child-btn.edit    { background: var(--c-bg-ede9fc); color: #5b4282; }
.child-btn.del     { background: var(--fail-bg); color: var(--danger-text-2); }
.child-btn.add     { background: var(--icon-success); color: #fff; padding: 3px 10px; font-size: 11px; }
.child-btn.neutral { background: var(--c-bg-e5e7eb); color: var(--text-body); }

.child-form {
  background: var(--wds-surface-2);
  border: 1px solid var(--wds-border);
  border-radius: var(--wds-radius);
  padding: 10px;
  margin-top: 8px;
}
.child-form .cf-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.child-form label { display: block; font-size: 10px; color: var(--text-muted); font-weight: 600; margin-bottom: 1px; }
.child-form input,
.child-form select {
  width: 100%;
  border: 1px solid var(--c-border-d8ccee);
  border-radius: 5px;
  padding: 3px 6px;
  font-size: 11px;
  box-sizing: border-box;
}
.child-form .cf-full { grid-column: 1 / -1; }

/* ════════════════════════════════════════════════════
   Empty / loading states — shared across all modules
   ════════════════════════════════════════════════════ */
.ws-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-5);
  text-align: center;
}
.ws-empty i { font-size: 26px; color: #d1d5db; }
.ws-empty-title { font-size: var(--fs-xl, 14px); font-weight: 600; color: var(--text-body); margin: 0; }
.ws-empty-sub { font-size: var(--fs-md, 12px); color: var(--icon-muted); margin: 0; }
.ws-empty .ws-empty-action { margin-top: var(--space-2); }

.ws-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-5);
  color: var(--icon-muted);
  font-size: var(--fs-md, 12px);
}
.ws-loading i { animation: spin .8s linear infinite; }

/* ════════════════════════════════════════════════════════════════
   AI-DETECTED SYSTEMS  (.ds-*)
   The SmartSpec "detected systems + catalog matching" cards rendered by
   renderDetectedSystems() in smartspec.js. Built on WDS tokens + app-badge.
   Theme accent switches wall (brand) ↔ ceiling (info) via .ds-scan--ceiling,
   which only re-points the --ds-* custom properties.
   ════════════════════════════════════════════════════════════════ */
.ds-scan {
  /* Accent palette — wall (brand violet) by default */
  --ds-accent:      var(--brand, #8e75da);
  --ds-accent-deep: #6d28d9;
  --ds-tint:        #faf8ff;
  --ds-tint-2:      #f5f3ff;
  --ds-head-bg:     #f8f5ff;
  --ds-line:        #ede9fe;

  background: var(--wds-surface, var(--surface));
  border: 1px solid var(--wds-border, var(--line));
  border-radius: var(--wds-radius-lg, 12px);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
  margin-bottom: var(--space-5, 20px);
}
.ds-scan--ceiling {
  --ds-accent:      #0891b2;
  --ds-accent-deep: #0e7490;
  --ds-tint:        #f0f9ff;
  --ds-tint-2:      #ecfeff;
  --ds-head-bg:     #f0f9ff;
  --ds-line:        #bae6fd;
}

/* ── Scan header bar ── */
.ds-scan__head {
  display: flex;
  align-items: center;
  gap: var(--space-3, 12px);
  padding: var(--space-3, 12px) var(--space-4, 16px);
  background: var(--ds-head-bg);
  border-bottom: 1px solid var(--ds-line);
}
.ds-scan__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  flex-shrink: 0;
  border-radius: 7px;
  color: #fff; font-size: .8rem;
  background: linear-gradient(135deg, var(--ds-accent), var(--ds-accent-deep));
}
.ds-scan__titles { flex: 1; min-width: 0; }
.ds-scan__title {
  font-size: .82rem; font-weight: 700; color: var(--c-text-1e293b);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ds-scan__file {
  font-size: .68rem; color: #94a3b8; margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ds-scan__del { color: var(--icon-danger, var(--danger-text)); }
.ds-scan__del:hover {
  background: var(--c-bg-fff1f2); border-color: var(--c-border-fecdd3); color: #e11d48;
}

/* ── Cards grid ── */
.ds-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-3, 12px);
  padding: var(--space-3, 12px) var(--space-4, 16px) var(--space-4, 16px);
}

/* ── Per-type card ── */
.ds-card {
  display: flex;
  flex-direction: column;
  min-height: 280px;
  background: var(--ds-tint);
  border: 1px solid var(--ds-line);
  border-radius: var(--wds-radius, 10px);
  overflow: hidden;
  transition: box-shadow .15s ease, border-color .15s ease, transform .15s ease;
}
.ds-card:hover {
  border-color: var(--ds-accent);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.ds-card__head {
  flex-shrink: 0;
  padding: 11px 14px 7px;
  border-bottom: 1px solid var(--wds-border-subtle, var(--c-border-f1f5f9));
}
.ds-card__code {
  font-size: 1rem; font-weight: 700; color: var(--c-text-1e293b);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ds-card__row {
  flex-shrink: 0;
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 6px;
  padding: 6px 14px;
  min-height: 32px;
  border-bottom: 1px solid var(--ds-line);
}
.ds-card__supplier {
  font-size: .68rem; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 90px;
}
.ds-chip-code {
  font-size: .68rem; font-weight: 600;
  font-family: var(--font-sans, 'Inter', sans-serif);
  color: #6366f1; background: var(--c-bg-eef2ff);
  padding: 2px 7px; border-radius: 4px; white-space: nowrap;
}
.ds-chip-ai {
  font-size: .68rem; font-weight: 600;
  color: var(--accent-text); background: var(--brand-light, var(--surface-accent));
  padding: 2px 7px; border-radius: 4px; white-space: nowrap;
}

/* Fire / acoustic tags reuse app-badge; thin variants here */
.ds-tag-fire {
  font-size: .68rem; background: var(--fail-bg-soft); color: var(--danger-text);
  padding: 2px 7px; border-radius: 99px; white-space: nowrap;
}
.ds-tag-acoustic {
  font-size: .68rem; background: var(--info-bg-soft); color: #2563eb;
  padding: 2px 7px; border-radius: 99px; white-space: nowrap;
}

/* ── Matched-catalog block (the "suggestions") ── */
.ds-card__matches {
  flex-shrink: 0;
  padding: 6px 14px 8px;
  border-bottom: 1px solid var(--ds-line);
}
.ds-card__matches-label {
  display: flex; align-items: center; gap: 4px;
  font-size: .6rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ds-accent); margin-bottom: 4px;
}
.ds-match-list { display: flex; flex-wrap: wrap; gap: 4px; }
.ds-match-pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .68rem; font-family: var(--font-sans, 'Inter', sans-serif);
  background: var(--ds-tint-2);
  border: 1px solid var(--ds-line);
  color: var(--ds-accent-deep);
  padding: 2px 8px; border-radius: 5px;
  cursor: pointer; white-space: nowrap;
  transition: background .12s, border-color .12s;
}
.ds-match-pill:hover {
  background: var(--brand-light, var(--surface-accent));
  border-color: var(--ds-accent);
}
.ds-match-pill .ds-match-size {
  font-size: .6rem; font-family: var(--font-sans, 'Inter', sans-serif); color: #8b5cf6;
}
.ds-match-empty {
  font-size: .64rem; color: #b8a8d8; font-style: italic;
}

/* ── Visualization (SVG + layer labels) ── */
.ds-card__viz {
  flex: 1;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px;
  background: var(--ds-tint-2);
}
.ds-card__labels {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column;
  align-items: flex-end; justify-content: center;
  gap: 1px; overflow: hidden;
}
.ds-card__label {
  font-size: .62rem; color: var(--c-text-475569); line-height: 1.35;
  text-align: right; max-width: 160px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ds-card__svg { flex-shrink: 0; }

/* ── Footer actions ── */
.ds-card__foot {
  flex-shrink: 0;
  display: flex; justify-content: flex-end; gap: 6px;
  padding: 8px 14px;
  border-top: 1px solid var(--ds-line);
}
/* compact buttons inside cards */
.ds-card__foot .app-btn { height: 28px; padding: 0 11px; font-size: .72rem; }

/* ════════════════════════════════════════════════════════════════
   Project picker modal  (shared: SmartSpec, Structural Actions)
   Matches the clean "confirm" modal aesthetic: edge-to-edge colour
   banner header + icon + close button, neat fixed-size card grid.
   ════════════════════════════════════════════════════════════════ */
.projpick-overlay {
  display: none;
  position: fixed; inset: 0; z-index: 9998;
  background: rgba(15, 10, 40, .55);
  backdrop-filter: blur(4px);
  align-items: center; justify-content: center;
  padding: 24px 12px;
}
.projpick-overlay.show { display: flex; }

.projpick-box {
  background: var(--surface); border-radius: 12px;
  width: 560px; max-width: 95vw;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .28);
  overflow: hidden;
  animation: projpickIn .2s ease-out;
}
@keyframes projpickIn {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.projpick-header {
  display: flex; align-items: center; gap: 12px;
  padding: 18px 20px;
  background: linear-gradient(135deg, var(--surface-tint), var(--c-bg-faf5ff));
  border-bottom: 1px solid var(--c-border-e9d5ff);
}
.projpick-icon {
  width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0;
  background: linear-gradient(135deg, #7c3aed, #6d28d9); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 17px;
}
.projpick-titles { flex: 1; min-width: 0; }
.projpick-title  { font-size: 15px; font-weight: 700; color: var(--accent-text-ink); line-height: 1.2; }
.projpick-sub    { font-size: 12px; color: var(--accent-text); margin-top: 2px; }
.projpick-close {
  background: rgba(124, 58, 237, .1); border: none; color: var(--accent-text);
  width: 28px; height: 28px; border-radius: 7px; cursor: pointer; font-size: 14px;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  transition: background .15s;
}
.projpick-close:hover { background: rgba(124, 58, 237, .2); }

.projpick-body {
  padding: 18px 20px 20px;
  max-height: 60vh; overflow-y: auto;
}
.projpick-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.projpick-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 7px;
  padding: 14px; height: 108px; box-sizing: border-box;
  background: var(--surface-sunken); border: 1.5px solid var(--line); border-radius: 10px;
  cursor: pointer; text-align: left; transition: all .15s ease;
}
.projpick-card:hover {
  background: var(--surface-tint); border-color: var(--brand-mid);
  box-shadow: 0 4px 14px rgba(109, 40, 217, .12); transform: translateY(-1px);
}
.projpick-card:active { transform: translateY(0); }
.projpick-card-ico  { font-size: 20px; color: var(--accent-text); }
.projpick-card--standalone .projpick-card-ico { color: var(--text-faint); }
.projpick-card-name { font-size: 13px; font-weight: 600; color: var(--text-strong); line-height: 1.3; word-break: break-word; }
.projpick-card-desc { font-size: 11px; color: var(--text-faint); margin-top: auto; }

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

/* ── Settings-area sub-navigation ─────────────────────────────────────────── */
.settings-subnav {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin: 4px 0 22px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.settings-subnav-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: 9px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  transition: background .15s, color .15s;
}
.settings-subnav-item i { font-size: 13px; opacity: .85; }
.settings-subnav-item:hover { background: var(--c-bg-f3f0fb); color: var(--accent-text-ink); }
.settings-subnav-item.active { background: var(--surface-accent); color: var(--accent-text-strong); }

/* ─────────────────────────────────────────────────────────────────────────
   Project work-tree ("Manage" tab in the workspace hub)
   Renders inside .wsh-stage-host; reuses .app-btn* and .dp-status colours.
   ───────────────────────────────────────────────────────────────────────── */
.worktree { max-width: 960px; margin: 0 auto; }
/* Modal variant: fill the detail modal width, action buttons right-aligned. */
.worktree--modal { max-width: none; margin: 0; }
.worktree-head--compact { margin-bottom: 12px; justify-content: flex-end; }
.worktree-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 18px; }
.worktree-head-text { min-width: 0; flex: 1; }
.worktree-title { display: flex; align-items: center; gap: 10px; margin: 0;
  font-size: 1.15rem; font-weight: 600; color: var(--text-primary); letter-spacing: -.01em; }
.worktree-title i { color: var(--brand); }
.worktree-sub { margin: 4px 0 0; font-size: .82rem; color: var(--text-secondary); }
.worktree-head-actions { display: flex; gap: 8px; flex-shrink: 0; }
.worktree-loading, .worktree-error { padding: 40px; text-align: center; color: var(--text-secondary); }
.worktree-error { color: var(--danger-text-2); }

.wt-branch { border: 1px solid var(--c-border-e6e3f0); border-radius: 12px; background: var(--surface);
  margin-bottom: 12px; overflow: hidden; }
.wt-branch-head { display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 12px 14px; border: 0; background: var(--c-bg-faf9fe); cursor: pointer;
  font-family: inherit; text-align: left; color: var(--text-primary); transition: background .15s; }
.wt-branch-head:hover { background: var(--c-bg-f3f0fb); }
.wt-branch-chev { font-size: .72rem; color: var(--text-tertiary); transition: transform .18s; }
.wt-branch:not(.is-open) .wt-branch-chev { transform: rotate(-90deg); }
.wt-branch-icon { font-size: .9rem; color: var(--brand); width: 18px; text-align: center; }
.wt-branch-label { flex: 1; font-size: .9rem; font-weight: 600; }
.wt-branch-count { min-width: 22px; padding: 1px 8px; border-radius: 999px;
  background: var(--surface-accent); color: var(--accent-text-strong); font-size: .74rem; font-weight: 600; text-align: center; }
.wt-branch-nodes { display: block; }
.wt-branch:not(.is-open) .wt-branch-nodes { display: none; }
.wt-empty { padding: 12px 16px 14px 42px; font-size: .8rem; color: var(--text-tertiary); }

.wt-node { border-top: 1px solid var(--c-border-f1eff8); }
.wt-node-row { display: flex; align-items: center; gap: 10px; padding: 9px 14px 9px 16px; }
.wt-node--child .wt-node-row { padding-left: 42px; background: var(--c-bg-fcfbff); }
.wt-node-row:hover { background: var(--c-bg-f7f5fd); }
.wt-node-chev { font-size: .68rem; color: var(--text-tertiary); cursor: pointer; width: 14px;
  text-align: center; transition: transform .18s; }
.wt-node:not(.is-open) > .wt-node-row .wt-node-chev { transform: rotate(-90deg); }
.wt-node-chev-sp { display: inline-block; width: 14px; }
.wt-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: #cbd5e1; flex-shrink: 0; }
.wt-status { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: currentColor; flex-shrink: 0; }
.wt-node-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* Report / takeoff / calculation names are long and their distinguishing part
   is often at the END ("…-18/07/2026", "…-GROUND FLOOR"), so a single
   ellipsised line hid exactly what tells two rows apart. Wrap to two lines and
   break inside unspaced names; the title attribute covers the rare third line. */
.wt-node-label { font-size: .86rem; font-weight: 600; color: var(--text-primary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere; line-height: 1.3; }
.wt-node-sub { font-size: .72rem; color: var(--text-tertiary); }
.wt-children { display: block; }
.wt-node:not(.is-open) > .wt-children { display: none; }

.wt-node-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.wt-ic { width: 30px; height: 30px; border: 0; border-radius: 8px; background: transparent;
  color: var(--text-secondary); cursor: pointer; font-size: .8rem; display: inline-flex;
  align-items: center; justify-content: center; transition: background .15s, color .15s; }
.wt-ic:hover { background: var(--surface-accent); color: var(--accent-text-strong); }
.wt-ic.del:hover { background: var(--fail-bg); color: var(--danger-text); }
.wt-toggle { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  border: 1px solid transparent; border-radius: 999px; cursor: pointer;
  font-family: inherit; font-size: .72rem; font-weight: 600; transition: .15s; }
.wt-toggle.is-on  { background: var(--c-bg-dcfce7); color: #15803d; border-color: var(--c-border-bbf7d0); }
.wt-toggle.is-on:hover { background: var(--c-bg-bbf7d0); }
.wt-toggle.is-off { background: var(--neutral-bg); color: var(--text-tertiary); border-color: var(--neutral-border); }
.wt-toggle.is-off:hover { background: var(--neutral-border); color: var(--text-secondary); }
.wt-toggle i { font-size: .82rem; }

/* ════════════════════════════════════════════════════
   Workspace hub (projects/workspace/hub.html)
   The hub rides the standard app shell, so the global .app-sidebar and
   .app-topbar are base.html's. This block owns only the stage host, the
   Overview dependency graph (.wsg-*) and the topbar stage pills.
   ════════════════════════════════════════════════════ */
@view-transition { navigation: auto; }

/* ── Stage host + panels ── */
.wsh-stage-host { position: relative; flex: 1; min-height: 0; background: var(--c-bg-f4f5f8); }
.wsh-panel { position: absolute; inset: 0; display: none; overflow: auto; }
.wsh-panel.is-active { display: block; }
.wsh-panel:not(.wsh-panel--frame) { padding: 20px 24px; }
.wsh-panel--frame { overflow: hidden; padding: 0; }
.wsh-frame { width: 100%; height: 100%; border: 0; display: block; background: var(--surface); }
.wsh-loading, .wsh-error { padding: 48px; text-align: center; color: #64748b; }
.wsh-error { color: var(--danger-text-2); }

/* ── Overview: the stage dependency graph ──
   Fixed layout, no pan/zoom/drag: seven nodes fit any sane viewport, so an
   interactive canvas would cost UX and buy nothing. Cards sit over a single
   SVG edge layer; an amber dashed edge means the upstream changed after the
   downstream last ran (projects/workflow.py stage_edges). */
.wsh-panel--overview { padding: 22px 26px 28px; }

/* Two columns: the graph says what state each step is in, the rail (the former
   Manage tab) is where you act on it. Kept on one screen so diagnosing and
   fixing are not two different places. The rail collapses, and auto-stacks
   underneath once the graph beside it would fall below MIN_SCALE (hub.html). */
.wsg-overview {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 22px;
  align-items: start;
}
.wsg-overview.is-stacked,
.wsg-overview.is-rail-hidden { grid-template-columns: minmax(0, 1fr); }
.wsg-overview.is-rail-hidden .wsg-rail { display: none; }
.wsg-graph { min-width: 0; }
.wsg-rail { min-width: 0; }
.wsg-overview.is-stacked .wsg-rail { border-top: 1px solid var(--border); padding-top: 20px; }

.wsg-rail-toggle {
  position: absolute; top: -4px; right: 0; z-index: 2;
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px;
  border: 1px solid var(--border); border-radius: 99px; background: var(--surface);
  color: var(--text-secondary); font-size: .72rem; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.wsg-rail-toggle:hover { background: var(--brand-light); color: var(--brand); border-color: var(--brand); }
.wsg-overview.is-stacked .wsg-rail-toggle { display: none; }

.wsg-head { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; padding-right: 120px; }
.wsg-head-label { font-size: .78rem; color: var(--text-secondary); white-space: nowrap; }
.wsg-progress { width: 180px; height: 6px; background: var(--brand-light); border-radius: 99px; overflow: hidden; }
.wsg-progress-bar { height: 100%; background: var(--brand); transition: width .4s ease; }
.wsg-head-warn {
  font-size: .74rem; font-weight: 600; color: #b45309; background: var(--warning-bg);
  border: 1px solid var(--warning-border); border-radius: 99px; padding: 3px 11px;
}

/* Viewport clips the scaled canvas so a narrow window shrinks the graph
   instead of growing a horizontal scrollbar. */
.wsg-viewport { overflow: hidden; }
.wsg-canvas {
  position: relative; transform-origin: top left;
  --wsg-edge: rgba(142, 117, 218, .38);
  --wsg-edge-stale: #d97706;
}
.wsg-edges { position: absolute; top: 0; left: 0; pointer-events: none; }

.wsg-node {
  position: absolute; width: 168px; height: 98px; box-sizing: border-box;
  display: flex; flex-direction: column; gap: 4px; text-align: left;
  padding: 10px 12px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); cursor: pointer; font-family: inherit;
  transition: border-color .12s, box-shadow .12s;
}
.wsg-node:hover { border-color: var(--brand); box-shadow: 0 2px 10px rgba(142, 117, 218, .18); }
.wsg-node--stale { border-color: var(--warning-border); background: var(--c-bg-fffdf7); }
.wsg-node--empty { background: var(--neutral-bg); }
.wsg-node-title {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: .82rem; font-weight: 600; color: var(--text-primary);
}
.wsg-node--empty .wsg-node-title { color: var(--text-secondary); }

.wsg-check {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  font-size: .68rem; color: var(--text-tertiary); line-height: 1.3;
}
.wsg-check span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wsg-check.is-done { color: var(--text-secondary); }
.wsg-check i { font-size: .58rem; flex-shrink: 0; color: var(--text-tertiary); }
.wsg-check.is-done i { color: var(--success); }

.wsg-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; display: inline-block; }
.wsg-dot--current { background: var(--success); }
.wsg-dot--stale { background: #d97706; }
.wsg-dot--empty { background: var(--neutral-border); }

.wsg-legend {
  display: flex; gap: 18px; flex-wrap: wrap; margin-top: 22px;
  font-size: .72rem; color: var(--text-secondary);
}
.wsg-legend span { display: inline-flex; align-items: center; gap: 6px; }
.wsg-legend-edge::before {
  content: ''; display: inline-block; width: 22px; height: 0;
  border-top: 2px dashed #d97706;
}

/* ── Topbar stage pills (components/input_panel_topbar/_stage_pills.html) ── */
.wsg-pills { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wsg-pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px;
  border: 1px solid var(--border); border-radius: 99px; background: var(--surface);
  color: var(--text-secondary); font-family: inherit; font-size: .74rem;
  cursor: pointer; white-space: nowrap;
  transition: background .12s, color .12s, border-color .12s;
}
.wsg-pill:hover { background: var(--brand-light); color: var(--brand); }
.wsg-pill.is-current {
  background: var(--brand-light); border-color: var(--brand);
  color: var(--brand); font-weight: 600;
}
.wsg-pill.is-stale { border-color: var(--warning-border); }
.wsg-pill i { font-size: .7rem; }

/* ─────────────────────────────────────────────────────────────────────────
   APPEND-ONLY (Phase 7 worktree change hub). Two-column Manage tab: tree on
   the left, change-impact preview panel on the right. Reuses .dp-status colours
   and .app-btn* / .app-input; only the layout + impact/editor chrome is new.
   (Concurrent agents may append further blocks below this one.)
   ───────────────────────────────────────────────────────────────────────── */
.worktree--hub { max-width: 1180px; }
.worktree-main { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
@media (max-width: 900px) { .worktree-main { grid-template-columns: 1fr; } }

/* ── Rail mode: the same fragment inside the hub Overview's right column ──
   Scoped by ancestor rather than a modifier class so the fragment stays
   byte-identical for its other host (the Projects-list modal). The tree's own
   two columns collapse to one and the impact preview stacks beneath it. */
.wsg-rail .worktree--hub { max-width: none; }
.wsg-rail .worktree-main { grid-template-columns: minmax(0, 1fr); gap: 14px; }
.wsg-rail .worktree-head { flex-direction: column; align-items: stretch; gap: 10px; margin-bottom: 14px; }
.wsg-rail .worktree-title { font-size: .92rem; }
.wsg-rail .worktree-sub { font-size: .74rem; }
.wsg-rail .worktree-head-actions .app-btn { flex: 1; justify-content: center; }
/* Bound the tree so a long project scrolls inside the rail instead of pushing
   the graph off the top of the page. */
.wsg-rail .worktree-body { max-height: 46vh; overflow: auto; }
.wsg-rail .worktree-impact { position: static; }

/* Stacked (narrow viewport): the rail spans the full width under the graph, so
   give the tree its own two columns back and drop the height cap. */
.wsg-overview.is-stacked .wsg-rail .worktree-main { grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; }
.wsg-overview.is-stacked .wsg-rail .worktree-head { flex-direction: row; align-items: flex-start; }
.wsg-overview.is-stacked .wsg-rail .worktree-head-actions .app-btn { flex: none; }
.wsg-overview.is-stacked .wsg-rail .worktree-body { max-height: none; overflow: visible; }
.wsg-overview.is-stacked .wsg-rail .worktree-impact { position: sticky; top: 8px; }
@media (max-width: 900px) {
  .wsg-overview.is-stacked .wsg-rail .worktree-main { grid-template-columns: minmax(0, 1fr); }
}

.wt-node-row { cursor: pointer; }
.wt-node.is-selected > .wt-node-row { background: var(--surface-accent); box-shadow: inset 3px 0 0 #8e75da; }
.wt-node.is-selected > .wt-node-row .wt-node-label { color: var(--accent-text-deep); }

/* ── Impact preview panel ── */
.worktree-impact { position: sticky; top: 8px; border: 1px solid var(--c-border-e6e3f0); border-radius: 12px;
  background: var(--surface); overflow: hidden; min-height: 220px; }
.wt-impact-empty { padding: 40px 24px; text-align: center; color: var(--text-tertiary); }
.wt-impact-empty i { font-size: 1.6rem; color: #c7bdf0; margin-bottom: 10px; display: block; }
.wt-impact-empty p { margin: 0; font-size: .82rem; }
.wt-impact-head { padding: 14px 16px; border-bottom: 1px solid var(--c-border-f1eff8); background: var(--c-bg-faf9fe); }
.wt-impact-title { font-size: .92rem; font-weight: 600; color: var(--text-primary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere; line-height: 1.3; }
.wt-impact-type { font-size: .68rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-tertiary); margin-top: 2px; }
.wt-impact-body { padding: 12px 16px; }
.wt-impact-note { font-size: .8rem; color: var(--text-secondary); line-height: 1.5; }
.wt-impact-group { margin-bottom: 14px; }
.wt-impact-group:last-child { margin-bottom: 0; }
.wt-impact-group-head { display: flex; align-items: center; gap: 8px; font-size: .74rem;
  font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--c-text-475569); margin-bottom: 7px; }
.wt-impact-group-head .dp-status { font-size: .72rem; }
.wt-impact-gcount { min-width: 20px; padding: 0 7px; border-radius: 999px; background: var(--neutral-bg);
  color: var(--text-secondary); font-size: .7rem; font-weight: 600; text-align: center; }
.wt-impact-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.wt-impact-list li { display: flex; flex-direction: column; gap: 2px;
  padding: 8px 10px; border: 1px solid var(--c-border-f1eff8); border-radius: 9px; background: var(--c-bg-fcfbff); }
.wt-impact-label { font-size: .82rem; font-weight: 600; color: var(--text-primary); }
.wt-impact-detail { font-size: .74rem; color: var(--text-secondary); line-height: 1.4; }

/* ── Inline opening-sweep editor (wall types) ── */
.wt-impact-foot { padding: 12px 16px 16px; border-top: 1px solid var(--c-border-f1eff8); }
.wt-osweep-head { display: flex; align-items: center; justify-content: space-between;
  font-size: .78rem; font-weight: 600; color: var(--text-primary); margin-bottom: 10px; }
.wt-osweep-head i { color: var(--brand); margin-right: 4px; }
.wt-osweep-toggle { display: inline-flex; align-items: center; gap: 6px; font-weight: 600;
  font-size: .74rem; color: var(--c-text-475569); cursor: pointer; }
.wt-osweep-fields { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.wt-osweep-fields label { display: flex; flex-direction: column; gap: 3px;
  font-size: .72rem; font-weight: 600; color: var(--text-secondary); }
.wt-osweep-fields .app-input { font-size: .8rem; }
.wt-osweep-actions { display: flex; }
.wt-osweep-actions .app-btn { flex: 1; justify-content: center; }
.wt-osweep-hint { margin: 8px 0 0; font-size: .7rem; color: var(--text-tertiary); }

/* ── Framing verification state (.cert-state) ───────────────────────────────
   Shown wherever framing-derived numbers are presented or gated: the BOM, the
   report and the certification step. One definition so those surfaces cannot
   drift into describing the same state differently. */
.cert-state {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 12px 14px; border-radius: 10px;
    border: 1px solid transparent; font-size: 13px; line-height: 1.5;
    margin-bottom: 14px;
}
.cert-state i { margin-top: 2px; flex-shrink: 0; }
.cert-state strong { display: block; margin-bottom: 2px; }
.cert-state--verified   { background: var(--c-bg-ecfdf5); border-color: var(--pass-border); color: var(--pass-fg); }
.cert-state--overridden { background: var(--c-bg-fffbeb); border-color: var(--c-border-fde68a); color: var(--warn-fg); }
.cert-state--unverified { background: var(--fail-bg-soft); border-color: var(--c-border-fecaca); color: var(--fail-fg); }
/* Exactly which values departed from the design — a bare "was overridden" is
   not enough to act on. */
.cert-ovr { margin: 6px 0 0; padding-left: 18px; font-size: 12px; opacity: .9; }
.cert-ovr li { margin: 1px 0; }
/* A blocked action stays visible and explains itself rather than disappearing,
   so the step does not look broken or absent. */
.wsh-stage-cta.is-disabled { opacity: .55; cursor: not-allowed; pointer-events: none; }

/* ── Session boot / navigation loader (.sql-*) ──────────────────────────────
   Shared by the SmartQuote landing page (shown while navigating into a
   session) and the drawing canvas (shown until the plan is usable), so the
   wait looks like one continuous step rather than two different screens. */
.sql-overlay {
    position: fixed; inset: 0; z-index: 10060;
    background: rgba(8,12,20,.72);
    display: flex; align-items: center; justify-content: center;
}
.sql-overlay[hidden] { display: none; }
.sql-dialog {
    width: 300px; max-width: calc(100vw - 32px);
    background: var(--sqd-bg, #1e293b); border: 1px solid var(--sqd-border, #334155);
    border-radius: 14px; padding: 26px 22px; text-align: center;
    box-shadow: 0 24px 70px rgba(0,0,0,.6);
}
.sql-spinner {
    width: 34px; height: 34px; margin: 0 auto 14px;
    border: 3px solid rgba(148,163,184,.28);
    border-top-color: #7c3aed;
    border-radius: 50%;
    animation: sql-spin .8s linear infinite;
}
/* sql-spin: legacy name still referenced by smartquote/drawing_interface.html;
   drop once that template adopts .app-spinner. Every other spinner uses `spin`. */
@keyframes sql-spin { to { transform: rotate(360deg); } }
.sql-title { color: #e2e8f0; font-size: 13px; font-weight: 600; }
.sql-sub { color: #94a3b8; font-size: 11px; margin-top: 5px; }

/* ── Reports & Certifications list (.rpt-*) ─────────────────────────────────
   A generated report is a certification record, not just a file: several
   issues of the same project routinely land on the same day, so the row has
   to carry the provenance that tells them apart — revision, exact time,
   author, document reference, and the categories that actually went into the
   PDF. Without that the list reads as three identical rows. */
.rpt-row {
    display: flex; align-items: flex-start; gap: 14px;
    padding: 14px 20px;
    transition: background .12s ease;
}
.rpt-row:hover { background: var(--c-bg-fafafa); }
.rpt-row + .rpt-row { border-top: 1px solid var(--c-border-eef0f4); }

/* Fixed width so the titles line up down the column regardless of Rev digits. */
.rpt-rev {
    flex-shrink: 0; width: 50px; margin-top: 1px;
    text-align: center;
    font-size: 10px; font-weight: 700; letter-spacing: .03em;
    color: var(--accent-text-strong); background: var(--c-bg-f5f2ff); border: 1px solid var(--c-border-e2d9fb);
    border-radius: 6px; padding: 4px 0;
}
.rpt-rev--stale { color: var(--text-faint); background: var(--surface-sunken); border-color: var(--line); }

.rpt-body { flex: 1; min-width: 0; }
.rpt-title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rpt-title { font-size: 13px; font-weight: 600; color: var(--text-ink); margin: 0; }

/* Status: dot + word, sitting with the title rather than buried in the meta
   line, because it gates whether Download is even there. */
.rpt-status { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; }
.rpt-status i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.rpt-status--ready { color: #059669; }
.rpt-status--busy  { color: #2563eb; }
.rpt-status--busy i { animation: rpt-pulse 1.2s ease-in-out infinite; }
.rpt-status--error { color: var(--danger-text); }
.rpt-status--draft { color: var(--text-faint); }
@keyframes rpt-pulse { 50% { opacity: .3; } }

/* Bullet separators, so the provenance reads as one line of facts instead of
   four unrelated columns. */
.rpt-meta {
    display: flex; align-items: center; flex-wrap: wrap;
    gap: 0 8px; margin-top: 5px;
    font-size: 11px; color: var(--text-muted);
}
.rpt-meta > * + *::before { content: "·"; margin-right: 8px; color: #d1d5db; }
.rpt-meta time { font-variant-numeric: tabular-nums; }
.rpt-ref {
    font-family: var(--font-sans, 'Inter', sans-serif);
    font-size: 10px; color: var(--text-faint); letter-spacing: .02em;
}
/* The revision's own description — the reason this issue exists, so it carries
   the weight of the meta line rather than sitting at its grey default. */
.rpt-revnote { color: var(--text-body-2); font-weight: 600; }
/* Issued = downloaded, which is what froze the revision. Stated on the row so
   the next generate bumping the number is never a surprise. */
.rpt-issued { display: inline-flex; align-items: center; gap: 4px; color: var(--accent-text-strong); font-weight: 600; }
.rpt-issued i { font-size: 9px; }
.rpt-unissued { color: var(--text-faint); font-style: italic; }

.rpt-cats { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
.rpt-cat {
    font-size: 10px; font-weight: 600; line-height: 1.6;
    padding: 1px 7px; border-radius: 4px;
    background: var(--neutral-bg); color: var(--text-body-2); border: 1px solid var(--c-border-e8eaee);
}
.rpt-cat--pricing { background: var(--c-bg-ecfdf5); color: #047857; border-color: var(--pass-border); }

.rpt-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.rpt-err { margin: 6px 0 0; font-size: 11px; color: var(--danger-text-2); }

/* ════════════════════════════════════════════════════════════════
   UI KIT - shared components added 13 Oct (reference: /console/ui-kit/,
   docs/workspace-design-system.md). Buttons, badges, modal, inputs and
   tables are defined where they always were; this block holds the rest.
   ════════════════════════════════════════════════════════════════ */

/* ── app-toast (window.AppToast, templates/base.html) ───────────
   Light style: white surface, tone-coloured border + text, top-right
   stack, aria-live polite. Replaces .toast-*, .aid-toast and the
   per-page showToast variants.
   ─────────────────────────────────────────────────────────────── */
.app-toast-stack {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 100002;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(380px, calc(100vw - 32px));
  pointer-events: none;
}
.app-toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 12px 11px 14px;
  border-radius: 12px;
  border: 1px solid var(--neutral-border, #d1d5db);
  background: var(--surface, #fff);
  color: var(--text-primary, #374151);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
  opacity: 0;
  transform: translateX(12px);
  transition: opacity .2s ease, transform .2s ease;
}
.app-toast.is-visible { opacity: 1; transform: none; }
.app-toast__icon { flex-shrink: 0; margin-top: 2px; }
.app-toast__msg { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.app-toast__close {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: inherit;
  opacity: .55;
  cursor: pointer;
  font-size: 12px;
}
.app-toast__close:hover { opacity: 1; background: rgba(0, 0, 0, .06); }
.app-toast__close:focus-visible { outline: 2px solid var(--brand-mid, #a78bfa); outline-offset: 1px; opacity: 1; }
.app-toast--success { border-color: var(--pass-border, #a7f3d0); color: var(--pass-fg, #065f46); }
.app-toast--error   { border-color: var(--fail-border, #fca5a5); color: var(--fail-fg, #991b1b); }
.app-toast--warning { border-color: var(--warn-border, #fcd34d); color: var(--warn-fg, #92400e); }
.app-toast--info    { border-color: var(--info-border, #93c5fd); color: var(--info-fg, #1e40af); }
:root[data-theme="dark"] .app-toast { background: #1f2937; }
@media (prefers-reduced-motion: reduce) { .app-toast { transition: none; transform: none; } }

/* ── app-textarea / app-checkbox / app-radio / app-field ─────────
   Siblings of app-input / app-select (30px, 6px radius, 12px text).
   ─────────────────────────────────────────────────────────────── */
.app-textarea {
  display: block;
  width: 100%;
  min-height: 72px;
  padding: 6px var(--tb-pad-x, 12px);
  border: 1px solid var(--neutral-border, var(--neutral-border));
  border-radius: var(--tb-radius, 6px);
  background: var(--surface, var(--surface));
  color: var(--text-primary, var(--text-body));
  font: inherit;
  font-size: var(--tb-font, 12px);
  line-height: 1.5;
  resize: vertical;
  outline: none;
  transition: border-color .12s;
  box-sizing: border-box;
}
textarea.form-control { height: auto; min-height: 72px; padding-top: 6px; padding-bottom: 6px; line-height: 1.5; resize: vertical; }
.app-textarea::placeholder { color: var(--text-tertiary, var(--text-faint)); }
.app-textarea:hover { border-color: var(--brand-mid, var(--brand-mid)); }
.app-textarea:focus { border-color: var(--brand, #8e75da); }
.app-textarea:disabled { opacity: .5; cursor: not-allowed; background: var(--neutral-bg); }

.app-checkbox,
.app-radio {
  appearance: none;
  -webkit-appearance: none;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin: 0;
  border: 1.5px solid var(--neutral-border, var(--neutral-border));
  background: var(--surface, var(--surface)) center / 10px 10px no-repeat;
  cursor: pointer;
  vertical-align: middle;
  transition: background-color .12s, border-color .12s;
}
.app-checkbox { border-radius: 4px; }
.app-radio    { border-radius: 50%; }
.app-checkbox:hover, .app-radio:hover { border-color: var(--brand-mid, var(--brand-mid)); }
.app-checkbox:checked {
  background-color: var(--brand, #8e75da);
  border-color: var(--brand, #8e75da);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'><path d='M1.5 5.2l2.4 2.4 4.6-5' fill='none' stroke='white' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}
.app-radio:checked {
  border-color: var(--brand, #8e75da);
  background-image: radial-gradient(circle, var(--brand, #8e75da) 0 3px, transparent 3.5px);
}
.app-checkbox:focus-visible, .app-radio:focus-visible { outline: 2px solid var(--brand-mid, var(--brand-mid)); outline-offset: 2px; }
.app-checkbox:disabled, .app-radio:disabled { opacity: .5; cursor: not-allowed; }
/* Label wrapper for a checkbox / radio + text */
.app-check { display: inline-flex; align-items: center; gap: 8px; font-size: var(--tb-font, 12px); color: var(--text-primary, var(--text-body)); cursor: pointer; }

/* app-field: label above the control, optional hint, optional error.
   --row = layout S-A (label left in a 120px column, control fills the rest). */
.app-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.app-field__label { font-size: var(--tb-font, 12px); font-weight: 500; color: var(--text-body-2); }
.app-field__req { color: var(--fail-fg, var(--fail-fg)); margin-left: 2px; }
.app-field__hint { font-size: 11px; color: var(--neutral-fg, var(--text-muted)); }
.app-field__error { font-size: 11px; font-weight: 500; color: var(--fail-fg, var(--fail-fg)); }
.app-field .app-input, .app-field .app-select, .app-field .app-textarea { width: 100%; }
.app-field.has-error .app-input,
.app-field.has-error .app-select,
.app-field.has-error .app-textarea,
.app-input[aria-invalid="true"],
.app-select[aria-invalid="true"],
.app-textarea[aria-invalid="true"] { border-color: #dc2626; }
.app-field--row { display: grid; grid-template-columns: 120px minmax(0, 1fr); align-items: center; column-gap: 12px; row-gap: 2px; }
.app-field--row .app-field__hint, .app-field--row .app-field__error { grid-column: 2; }
.form-control, .form-select { display: block; width: 100%; }

/* ── app-tabs: underline tabs for page sections ──────────────────
   Use for sections of one page; use .ws-view-toggle (segmented) to
   switch how the same data is shown (card / list).
   ─────────────────────────────────────────────────────────────── */
.app-tabs {
  display: flex;
  gap: 20px;
  border-bottom: 1px solid var(--neutral-border, var(--line));
  overflow-x: auto;
  scrollbar-width: none;
}
.app-tabs::-webkit-scrollbar { display: none; }
.app-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 35px;
  padding: 0 2px;
  margin-bottom: -1px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--neutral-fg, var(--text-muted));
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  flex-shrink: 0;
  transition: color .12s, border-color .12s;
}
.app-tab:hover { color: var(--text-body-2); text-decoration: none; }
.app-tab:focus-visible { outline: 2px solid var(--brand-mid, var(--brand-mid)); outline-offset: -2px; border-radius: 4px; }
.app-tab.is-active,
.app-tab[aria-selected="true"],
.app-tab[aria-current="page"] { color: var(--accent-text); border-bottom-color: #7c3aed; }
.app-tab:disabled { opacity: .5; cursor: not-allowed; }

/* ── app-spinner: the one ring spinner ───────────────────────────
   Brand-coloured ring; sm = 14px, md (default) = 20px. Uses the single
   @keyframes spin above. Aliases: .spinner, .ft-spinner (= sm).
   ─────────────────────────────────────────────────────────────── */
.app-spinner,
.spinner,
.ft-spinner {
  display: inline-block;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border: 2px solid var(--brand-light, var(--c-border-e8e3f5));
  border-top-color: var(--brand, #8e75da);
  border-radius: 50%;
  vertical-align: middle;
  box-sizing: border-box;
  animation: spin .7s linear infinite;
}
.app-spinner--sm, .ft-spinner { width: 14px; height: 14px; }
@media (prefers-reduced-motion: reduce) { .app-spinner, .spinner, .ft-spinner { animation-duration: 1.6s; } }

/* ── Cards ───────────────────────────────────────────────────────
   Canonical: .app-cardbox (+ --compact, --interactive, --elevated, --flat).
   DEPRECATED, still working: .card, .card-header/-body/-footer (tailwind
   layer). The card-default / -elevated / -flat / -compact / -detail / -list
   family was deleted on 14 Oct (zero uses). Do not use .card in new markup.
   ─────────────────────────────────────────────────────────────── */

/* ── Side panel layout S-A (reference markup in /console/ui-kit/) ──
   head: title + close icon button | body: .detail-row rows with a 120px
   .detail-label--fixed and a 30px .detail-panel-input | foot: .detail-panel-foot
   (Save primary, Cancel secondary, right-aligned, top border).
   ─────────────────────────────────────────────────────────────── */
.detail-panel .detail-row,
.detail-panel-body .detail-row { align-items: center; gap: 12px; }
.detail-panel .detail-row--top,
.detail-panel-body .detail-row--top { align-items: flex-start; }
.detail-panel-head .detail-panel-close { flex-shrink: 0; }

/* ── Forced states (reference page only) ─────────────────────────
   .is-hover / .is-focus mirror :hover / :focus so /console/ui-kit/ can
   show them without a mouse. Do not use in product markup.
   ─────────────────────────────────────────────────────────────── */
.app-btn.is-hover { background: var(--brand-light, var(--brand-light)); border-color: var(--brand-mid, var(--brand-mid)); color: var(--brand, var(--brand)); }
.app-btn-primary.is-hover { background: #7c3aed; border-color: #7c3aed; color: #fff; }
.app-btn-danger.is-hover  { background: #ef4444; border-color: #ef4444; color: #fff; }
.app-btn-warning.is-hover { background: #f59e0b; border-color: #f59e0b; color: #fff; }
.app-btn-success.is-hover { background: #10b981; border-color: #10b981; color: #fff; }
.app-btn-icon--danger.is-hover { color: var(--danger-text); border-color: var(--c-border-fecaca); background: var(--fail-bg-soft); }
.app-input.is-hover, .app-select.is-hover, .app-textarea.is-hover { border-color: var(--brand-mid, var(--brand-mid)); }
.app-input.is-focus, .app-select.is-focus, .app-textarea.is-focus { border-color: var(--brand, #8e75da); }

/* ═══════════════════════════════════════════════════════════════════════
   SQ kit: SmartQuote drawing stage (light only). See docs/workspace-design-system.md
   "SQ kit". Corner group, floor pill, menu, segment, rail button, command line,
   canvas chips, action bar.
   ═══════════════════════════════════════════════════════════════════════ */
:root {
  --sq-rail-w: 44px;
  --sq-tool: 34px;
  --sq-corner-pad: 3px;
  --sq-cmd-h: 36px;
  --sq-wall: var(--brand, #7c3aed);
  --sq-wall-unpriced: #d98a1c;
  --sq-area: rgba(124, 58, 237, .12);
  --sq-tag-bg: rgba(124, 58, 237, .10);
  --sq-ref: #9c98ae;
  --sq-opening: #d98a1c;
  --sq-surface: #ffffff;
  --sq-line: #e3e0ee;
  --sq-ink: #1b1830;
  --sq-ink-2: #4a4660;
  --sq-ink-3: #6b6880;
  --sq-hover: #f3f0fb;
  --sq-on-bg: #ede9fe;
  --sq-on-ink: #5b21b6;
  --sq-on-line: #ddd6fe;
}
.sq-corner { display: inline-flex; align-items: center; gap: 2px; padding: var(--sq-corner-pad); background: var(--sq-surface);
  border: 1px solid var(--sq-line); border-radius: 8px; box-shadow: 0 1px 2px rgba(27, 24, 48, .06); max-width: 100%; }
.sq-corner__btn { font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; height: var(--tb-h, 30px); min-width: var(--tb-h, 30px);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 6px; white-space: nowrap;
  border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--sq-ink-2); }
.sq-corner__btn:hover:not(:disabled) { background: var(--sq-hover); color: var(--sq-ink); }
.sq-corner__btn.is-on, .sq-corner__btn.active { background: var(--sq-on-bg); color: var(--sq-on-ink); border-color: var(--sq-on-line); }
.sq-corner__btn:disabled { color: #c4bdd9; cursor: default; }
.sq-corner__btn:focus-visible, .sq-pill:focus-visible, .sq-seg__btn:focus-visible { outline: 2px solid var(--brand, #7c3aed); outline-offset: 1px; }
.sq-corner__btn svg { width: 16px; height: 16px; flex: 0 0 16px; }
.sq-corner__btn--warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-border); }
.sq-corner__div { width: 1px; height: 20px; background: var(--sq-line); margin: 0 3px; flex: 0 0 1px; }
.sq-pill { font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; height: var(--tb-h, 30px); display: inline-flex; align-items: center;
  gap: 8px; padding: 0 10px; white-space: nowrap; background: var(--sq-surface); color: var(--sq-ink); border: 1px solid var(--sq-on-line);
  border-radius: 6px; box-shadow: 0 1px 2px rgba(27, 24, 48, .06); text-decoration: none; }
.sq-pill:hover { background: var(--sq-hover); color: var(--sq-ink); }
.sq-pill small { font-weight: 500; color: var(--sq-ink-3); font-size: 12px; }
.sq-pill.is-unset { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-fg); }
.sq-menu { position: absolute; z-index: 60; top: calc(100% + 4px); left: 0; min-width: 220px; padding: 4px; background: var(--sq-surface);
  border: 1px solid var(--sq-line); border-radius: 8px; box-shadow: 0 8px 24px rgba(35, 29, 69, .16); }
.sq-menu[hidden] { display: none; }
.sq-menu__item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 10px; font: inherit; font-size: 12px; font-weight: 500;
  color: var(--sq-ink); text-decoration: none; background: transparent; border: 0; border-radius: 6px; cursor: pointer; text-align: left; }
.sq-menu__item:hover, .sq-menu__item:focus-visible { background: var(--sq-hover); color: var(--sq-ink); outline: none; }
.sq-menu__item i { width: 14px; color: var(--sq-ink-3); }
.sq-seg { display: inline-flex; height: var(--tb-h, 30px); box-sizing: border-box; overflow: hidden; background: var(--sq-surface);
  border: 1px solid var(--neutral-border); border-radius: 6px; }
.sq-seg__btn { font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; padding: 0 11px;
  color: var(--sq-ink-2); background: var(--sq-surface); border: 0; border-right: 1px solid var(--sq-line); }
.sq-seg__btn:last-child { border-right: 0; }
.sq-seg__btn:hover:not(.active) { background: var(--sq-hover); }
.sq-seg__btn.active { background: var(--sq-on-bg); color: var(--sq-on-ink); }
.sq-seg__btn svg { width: 14px; height: 14px; }
.sq-cmd { display: flex; align-items: center; gap: 8px; height: var(--sq-cmd-h); padding: 0 10px; background: var(--sq-surface); border: 1px solid var(--sq-line);
  border-radius: 8px; box-shadow: 0 4px 16px rgba(35, 29, 69, .12); box-sizing: border-box; }
.sq-cmd__prompt { font-size: 11.5px; color: var(--sq-ink-2); background: var(--surface-tint); border: 1px solid var(--sq-on-line); border-radius: 6px; padding: 4px 8px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; max-width: 55%; }
.sq-cmd__prompt.is-error { background: var(--fail-bg); border-color: var(--fail-border); color: var(--fail-fg); }
.sq-cmd__entry { position: relative; flex: 1; min-width: 120px; display: flex; align-items: center; }
.sq-cmd__caret { position: absolute; left: 9px; font: 700 12px var(--font-sans, 'Inter', sans-serif); color: var(--brand, var(--accent-text)); }
.sq-cmd__input { flex: 1; min-width: 0; height: 26px; padding: 0 8px 0 26px; font: 12px var(--font-sans, 'Inter', sans-serif); color: var(--sq-ink);
  background: var(--surface); border: 1px solid var(--neutral-border); border-radius: 6px; text-transform: uppercase; }
.sq-cmd__input::placeholder { color: var(--sq-ref); text-transform: none; }
.sq-cmd__input:focus { outline: 2px solid var(--brand, #7c3aed); outline-offset: 1px; border-color: var(--brand, #7c3aed); }
.sq-chip-dim { font: 600 10.5px var(--font-sans, 'Inter', sans-serif); color: var(--sq-on-ink); background: rgba(255, 255, 255, .92);
  border: 1px solid var(--sq-on-line); border-radius: 4px; padding: 1px 5px; white-space: nowrap; }
.sq-chip-tag { font-size: 10.5px; font-weight: 700; color: var(--sq-on-ink); background: var(--sq-tag-bg); border-radius: 4px; padding: 1px 6px; white-space: nowrap; }
.sq-actionbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px; background: var(--sq-surface);
  border: 1px solid var(--sq-line); border-radius: 8px; box-shadow: 0 1px 2px rgba(27, 24, 48, .06); }
.sq-actionbar[hidden] { display: none; }
.sq-actionbar__grp { display: inline-flex; align-items: center; gap: 4px; padding-right: 6px; margin-right: 2px; border-right: 1px solid var(--sq-line); }
.sq-actionbar__grp:last-child { border-right: 0; padding-right: 0; margin-right: 0; }
.sq-actionbar__lbl { font-size: 10.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--sq-ink-3); padding: 0 4px; }
.wsr-cmd--danger svg { color: var(--danger-text); }
.sq-menu__item.is-current { background: var(--sq-on-bg); color: var(--sq-on-ink); font-weight: 600; }
.sq-menu__sep { height: 1px; margin: 4px 2px; background: var(--sq-line); }

/* ── AI modal (P4): one dialog for every AI run — rows, effort, cost footer ── */
.ai-modal { max-width: 640px; }
.ai-modal-body { display: flex; flex-direction: column; gap: 14px; }
.ai-modal-rows { display: flex; flex-direction: column; gap: 8px; }
.ai-modal-row { border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; background: var(--surface); }
.ai-modal-row--off { background: var(--surface-sunken); }
.ai-modal-row--off .ai-modal-row-label { color: var(--text-secondary, var(--text-muted)); }
.ai-modal-row-main { display: flex; align-items: center; gap: 10px; min-height: 24px; }
.ai-modal-check { width: 16px; height: 16px; flex: 0 0 auto; accent-color: var(--primary, #2563eb); }
.ai-modal-row-label { flex: 1 1 auto; font-size: 14px; font-weight: 600; color: var(--text-primary, var(--text-strong)); }
.ai-modal-row-cost { font-size: 12px; color: var(--text-secondary, var(--text-muted)); white-space: nowrap; font-variant-numeric: tabular-nums; }
.ai-modal-badge { flex: 0 0 auto; }
.ai-modal-row-meta, .ai-modal-row-reason, .ai-modal-row-progress, .ai-modal-row-summary, .ai-modal-row-error { font-size: 12.5px; line-height: 1.45; margin-top: 6px; color: var(--text-secondary, var(--text-muted)); }
.ai-modal-row-meta:empty, .ai-modal-row-progress:empty, .ai-modal-row-opts:empty, .ai-modal-row-result:empty { display: none; }
.ai-modal-row-reason { color: var(--text-primary, var(--text-strong)); }
.ai-modal-row-error { color: var(--danger-text-2); }
.ai-modal-row-summary { color: var(--text-primary, var(--text-strong)); }
.ai-modal-link { color: var(--primary, #2563eb); text-decoration: underline; cursor: pointer; }
.ai-modal-row-opts { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
.ai-modal-row-result { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.ai-modal-row-result .ai-modal-table { width: 100%; }
.ai-modal-table { display: block; max-width: 100%; overflow-x: auto; }
.ai-modal-table th[scope="row"] { text-align: left; font-weight: 600; white-space: nowrap; }
/* Loading detect result: source lines + "Check these first" (shared warn tokens, light and dark) */
.ai-modal-src { font-size: 12px; line-height: 1.4; margin-top: 2px; color: var(--text-secondary, var(--text-muted)); font-weight: 400; }
.ai-modal-src b { color: var(--text-primary, var(--text-strong)); font-weight: 600; }
.ai-modal-srcs { width: 100%; }
.ai-modal-checkfirst { width: 100%; box-sizing: border-box; padding: 10px 12px; border: 1px solid var(--warn-border, var(--warn-border)); background: var(--warn-bg, var(--warn-bg)); border-radius: 8px; color: var(--text-primary, var(--text-strong)); }
.ai-modal-checkfirst-title { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--warn-fg, var(--warn-fg)); margin-bottom: 4px; }
.ai-modal-checkfirst ul { margin: 0; padding: 0; list-style: none; font-size: 13px; }
.ai-modal-checkfirst > ul > li { padding: 5px 0; border-top: 1px solid var(--warn-border, var(--warn-border)); }
.ai-modal-checkfirst > ul > li:first-child { border-top: 0; }
.ai-modal-checkfirst ul.ai-modal-cand { margin: 2px 0 0 14px; list-style: disc; font-size: 12px; color: var(--text-secondary, var(--text-muted)); }
.ai-modal-table td .app-badge { margin-left: 4px; }
@media (max-width: 560px) {
  /* Phone: stack label over value so long values and source lines wrap instead of scrolling sideways */
  .ai-modal-table--kv tbody, .ai-modal-table--kv tr, .ai-modal-table--kv th, .ai-modal-table--kv td { display: block; width: 100%; box-sizing: border-box; }
  .ai-modal-table--kv th[scope="row"] { white-space: normal; padding-bottom: 0; border-bottom: 0; }
  .ai-modal-table--kv td { padding-top: 2px; white-space: normal; max-width: none; overflow-wrap: anywhere; }
}
.ai-modal-opt { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ai-modal-opt-label { font-size: 12px; font-weight: 600; color: var(--text-secondary, var(--text-muted)); min-width: 72px; }
.ai-modal-opt .app-input, .ai-modal-opt .app-select { height: var(--tb-h, 32px); max-width: 260px; }
.ai-modal-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.ai-modal-pill { font-size: 12px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--text-primary, var(--text-strong)); cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.ai-modal-pill.is-on { background: var(--primary, #2563eb); border-color: var(--primary, #2563eb); color: #fff; }
.ai-modal-pill.is-on .ai-modal-elapsed { color: rgba(255, 255, 255, 0.8); }
.ai-modal-pill:focus-visible, .ai-modal-pill:focus-within { outline: 2px solid var(--primary, #2563eb); outline-offset: 2px; }
.ai-modal-effort[hidden] { display: none; }
.ai-modal-effort { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ai-modal-effort-label { font-size: 12px; font-weight: 600; color: var(--text-secondary, var(--text-muted)); }
.ai-modal-sum { font-size: 13px; line-height: 1.5; color: var(--text-secondary, var(--text-muted)); border-top: 1px solid var(--line); padding: 10px 18px 0; flex: 0 0 auto; max-height: 30vh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.ai-modal-sum:empty { display: none; }
.ai-modal-sum strong { color: var(--text-primary, var(--text-strong)); }
.ai-modal-sum-warn { color: #b45309; }
.ai-modal-elapsed { color: var(--text-secondary, var(--text-muted)); }
.ai-modal-overlay .app-modal-dark { background: #1f2430; color: #e5e7eb; }
.ai-modal-overlay .app-modal-dark .ai-modal-row { background: #2a3140; border-color: #3b4252; }
.ai-modal-overlay .app-modal-dark .ai-modal-row--off { background: #232936; }
.ai-modal-overlay .app-modal-dark .ai-modal-row-label, .ai-modal-overlay .app-modal-dark .ai-modal-row-reason,
.ai-modal-overlay .app-modal-dark .ai-modal-row-summary, .ai-modal-overlay .app-modal-dark .ai-modal-sum strong,
.ai-modal-overlay .app-modal-dark .app-modal-title { color: #f3f4f6; }
.ai-modal-overlay .app-modal-dark .ai-modal-pill { background: #2a3140; color: #e5e7eb; border-color: #3b4252; }
.ai-modal-overlay .app-modal-dark .ai-modal-pill.is-on { background: var(--primary, #2563eb); border-color: var(--primary, #2563eb); }
.ai-modal-overlay .app-modal-dark .ai-modal-sum, .ai-modal-overlay .app-modal-dark .app-modal-head,
.ai-modal-overlay .app-modal-dark .app-modal-foot { border-color: #3b4252; }
@media (max-width: 640px) {
  .ai-modal-overlay { align-items: flex-end; }
  .ai-modal { max-width: 100%; width: 100%; max-height: 92vh; border-radius: 14px 14px 0 0; }
  .ai-modal-row-main { flex-wrap: wrap; }
  .ai-modal-row-cost { flex-basis: 100%; padding-left: 26px; }
  .ai-modal-foot { flex-wrap: wrap; }
  .ai-modal-foot .app-btn { flex: 1 1 auto; }
}

/* A POST form (logout, return to admin) that has to look like a link or a menu row.
   :where() keeps specificity at zero so the host class (app-user-menu-item, ...) wins. */
.app-inline-form { display: contents; }
.app-inline-form--end { display: block; margin-left: auto; }
:where(.app-linkbtn) {
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  cursor: pointer; text-align: left;
}
.app-user-menu-item.app-linkbtn,
.wsr-acct__out.app-linkbtn { width: 100%; }
.app-linkbtn--banner { color: #fff; text-decoration: underline; font-weight: 600; }

/* ── Dark theme: Tailwind neutral utilities ──────────────────────
   A handful of templates still use Tailwind's neutral palette. Those utilities
   are fixed-value, so dark mode remaps them here to the theme tokens
   (tokens.css). Screen only: print stays light.
   ─────────────────────────────────────────────────────────────── */
@media screen {
  /* Tailwind's base sets body text to fixed gray-800, which text with no colour of its own inherits. */
  :root[data-theme="dark"] body            { color: var(--text-primary); }
  :root[data-theme="dark"] .bg-white       { background-color: var(--surface); }
  :root[data-theme="dark"] .bg-gray-50     { background-color: var(--surface-sunken); }
  :root[data-theme="dark"] .bg-gray-100    { background-color: var(--neutral-bg); }
  :root[data-theme="dark"] .text-gray-400  { color: var(--text-faint); }
  :root[data-theme="dark"] .text-gray-500,
  :root[data-theme="dark"] .text-gray-600  { color: var(--text-muted); }
  :root[data-theme="dark"] .text-gray-700,
  :root[data-theme="dark"] .text-gray-800  { color: var(--text-body); }
  :root[data-theme="dark"] .text-gray-900  { color: var(--text-strong); }
  :root[data-theme="dark"] .border-gray-200,
  :root[data-theme="dark"] .border-gray-300 { border-color: var(--line); }
  :root[data-theme="dark"] .from-gray-100  { --tw-gradient-from: var(--surface-sunken); --tw-gradient-to: transparent; }
  :root[data-theme="dark"] .to-indigo-50   { --tw-gradient-to: var(--surface-tint); }
}

/* ── Dark theme: leftovers found in browser review ──────────────
   Glass panels (translucent white) take the surface colour; dark-purple text
   that sat on light grounds takes the lightened accent / strong text tokens.
   Screen only: print stays light.
   ─────────────────────────────────────────────────────────────── */
@media screen {
  :root[data-theme="dark"] .la-card,
  :root[data-theme="dark"] .ls3-site-tag,
  :root[data-theme="dark"] .ls3-wind-tag,
  :root[data-theme="dark"] .ls3-dim,
  :root[data-theme="dark"] .ls3-tip,
  :root[data-theme="dark"] .ls3-toolbar,
  :root[data-theme="dark"] .ls3-legend,
  :root[data-theme="dark"] .la-site-map-tag,
  :root[data-theme="dark"] .sw-label div,
  :root[data-theme="dark"] .sw-mini-label div,
  :root[data-theme="dark"] #la-wrap .insp-panel.la-floors,
  :root[data-theme="dark"] body.ss-stage #ss-dock { background: color-mix(in srgb, var(--surface) 92%, transparent); }
  :root[data-theme="dark"] #la-wrap .la-floors .insp-header,
  :root[data-theme="dark"] #ss-dock .ss-dock-head,
  :root[data-theme="dark"] .ss-dock-head { background: color-mix(in srgb, var(--surface-sunken) 92%, transparent); }
  :root[data-theme="dark"] .la-adv-sub { color: var(--c-text-3b3561); }
  :root[data-theme="dark"] .flr-add-btn.app-btn-secondary { color: var(--accent-text); }
  :root[data-theme="dark"] .ls3-dim { color: var(--accent-text-strong); }
  :root[data-theme="dark"] .ss-dock-empty a,
  :root[data-theme="dark"] .ss-dock-empty button.ss-link { color: var(--accent-text-strong); }
  :root[data-theme="dark"] .app-seg > button.active,
  :root[data-theme="dark"] .app-seg > button.is-active,
  :root[data-theme="dark"] .app-seg > button[aria-pressed="true"],
  :root[data-theme="dark"] .app-seg > button[aria-selected="true"],
  :root[data-theme="dark"] .ws-view-toggle button[aria-pressed="true"],
  :root[data-theme="dark"] .dp-tab.active,
  :root[data-theme="dark"] .dp-tab:hover,
  :root[data-theme="dark"] .detail-panel-title { color: var(--accent-text-ink); }
  :root[data-theme="dark"] .sq-sm-hd-title,
  :root[data-theme="dark"] .sq-l-section-title,
  :root[data-theme="dark"] .sq-fc-module-name,
  :root[data-theme="dark"] .sq-mod-card-name,
  :root[data-theme="dark"] .dp-overview-title { color: var(--text-strong); }
  :root[data-theme="dark"] .sq-sm-hd-sub,
  :root[data-theme="dark"] .sq-sm-close-btn,
  :root[data-theme="dark"] .sq-fc-calc { color: var(--text-muted); }
  :root[data-theme="dark"] .sq-floor-fr,
  :root[data-theme="dark"] .sq-l-actions .app-btn-icon:hover { color: var(--accent-text-strong); }
  :root[data-theme="dark"] .page-btn.current { color: var(--surface); }
  :root[data-theme="dark"] #grid .tabulator-table { background: var(--surface); }
  /* Admin shell: --wc-accent is a dark fill in light mode; text and icons drawn in it take the lightened accent. */
  :root[data-theme="dark"] .adm-sec__link,
  :root[data-theme="dark"] .adm-tab__ico,
  :root[data-theme="dark"] .adm-pagebtn i,
  :root[data-theme="dark"] .adm .wsr-cmd i:not(.adm-caret) { color: var(--accent-text); }
}

/* ── Dark theme: contrast fixes for brand-coloured controls ──────
   --brand (#8e75da) is a fill in light mode; on dark surfaces it is also used
   as text, so text uses lighten and fills darken to keep WCAG AA (4.5:1).
   ─────────────────────────────────────────────────────────────── */
@media screen {
  :root[data-theme="dark"] .app-nav-item:hover,
  :root[data-theme="dark"] .app-nav-item.active { color: var(--accent-text-deep); }
  :root[data-theme="dark"] .app-btn-primary,
  :root[data-theme="dark"] .app-user-avatar { background: #6d4fc9; color: #fff; }
  :root[data-theme="dark"] .app-btn-primary:hover { background: #7c5fd6; }
  /* Pager active page: --accent is 4.2:1 against white in dark; darken it to pass AA. */
  :root[data-theme="dark"] .pagination-buttons .page-number.active,
  :root[data-theme="dark"] .ws-page-active { background: color-mix(in srgb, var(--accent) 80%, black) !important; border-color: color-mix(in srgb, var(--accent) 80%, black) !important; }
  :root[data-theme="dark"] .topbar-subtitle { color: var(--text-secondary); }
  :root[data-theme="dark"] a:focus-visible,
  :root[data-theme="dark"] button:focus-visible,
  :root[data-theme="dark"] input:focus-visible,
  :root[data-theme="dark"] select:focus-visible,
  :root[data-theme="dark"] textarea:focus-visible { outline-color: #c4b5fd; }
}

/* ── Dark theme: page-level text and surface fixes ───────────────
   Colours the page stylesheets set outside the shared tokens (ribbon, calculators,
   SmartSpec canvas chrome, dashboard states). Screen only.
   ─────────────────────────────────────────────────────────────── */
@media screen {
  :root[data-theme="dark"] .wsr-tabrow { background: var(--surface-sunken); }
  :root[data-theme="dark"] .wsr-tab__txt,
  :root[data-theme="dark"] .wsr-crumb__sep { color: var(--text-faint); }
  :root[data-theme="dark"] .wsr-switch__all,
  :root[data-theme="dark"] .adm-role,
  :root[data-theme="dark"] .app-viewall,
  :root[data-theme="dark"] .pl-detail-title,
  :root[data-theme="dark"] .calcs-upgrade { color: var(--accent-text-deep); }
  :root[data-theme="dark"] .calcs h1,
  :root[data-theme="dark"] .calcs h2,
  :root[data-theme="dark"] .calcs h3 { color: var(--text-primary); }
  :root[data-theme="dark"] .sq-sm-hd-title,
  :root[data-theme="dark"] #sqStartSrcPlan.active { color: var(--text-primary); }
  :root[data-theme="dark"] .app-stat-sub { color: var(--fail-fg); }
  :root[data-theme="dark"] .app-proj-state.active { color: var(--pass-fg); }
  :root[data-theme="dark"] .app-nav-badge { background: #6d4fc9; color: #fff; }
  :root[data-theme="dark"] .ss-panel,
  :root[data-theme="dark"] .sb-topbar-project-wrap,
  :root[data-theme="dark"] .ls3-toolbar,
  :root[data-theme="dark"] #ss-layers-card,
  :root[data-theme="dark"] #ss-dock { background: var(--surface); color: var(--text-body); }
  :root[data-theme="dark"] .ss-dock-head { background: var(--surface-sunken); }
  :root[data-theme="dark"] .ss-dock-title { color: var(--text-primary); }
  :root[data-theme="dark"] .ss-empty,
  :root[data-theme="dark"] .ss-muted { color: var(--text-muted); }
}

/* ── Filter row and row actions (UI kit, promoted from the admin shell) ──
   Markup: components/_filter_row.html and components/_row_actions.html.
   Behaviour: static/js/app_row_menu.js (delegated, survives innerHTML swaps).
   Colours read --app-pop-* hooks that default to the theme tokens; the admin
   shell maps them to its --wc-* tokens (admin_shell.css), so it looks as before.
   ─────────────────────────────────────────────────────────────── */
.app-sr-only, .wsr-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Filter row. Controls share --tb-h-sm. */
.adm-filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 16px;
  background: var(--app-pop-bg, var(--surface)); border-bottom: 1px solid var(--app-pop-line, var(--line));
}
.adm-filters .adm-ctl, .adm-filters .app-btn { height: var(--tb-h-sm, 26px); min-height: 0; font-size: .75rem; box-sizing: border-box; }
.adm-filters__search { position: relative; display: block; flex: 0 1 260px; min-width: 160px; }
.adm-filters__search i { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); font-size: .6875rem; color: var(--app-pop-label, var(--text-muted)); pointer-events: none; }
.adm-filters__search input { width: 100%; padding-left: 26px; }
.adm-filters__selects { display: flex; flex-wrap: wrap; gap: 8px; }
.adm-filters__sheet { display: contents; }
.adm-filters__open, .adm-sheet__head { display: none; }
.adm-filters__n { min-width: 16px; height: 16px; border-radius: 8px; padding: 0 4px; font-size: .625rem; line-height: 16px; text-align: center; background: var(--app-pop-accent, var(--accent)); color: var(--app-pop-on-accent, var(--surface)); }
.adm-filters__count { margin-left: auto; font-size: .75rem; color: var(--app-pop-label, var(--text-muted)); white-space: nowrap; }
.adm-chip {
  font: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; height: var(--tb-h-sm, 26px); padding: 0 9px;
  border-radius: 999px; border: 1px solid var(--app-pop-chip-line, var(--accent-line-soft)); background: var(--app-pop-hover, var(--surface-accent)); color: var(--app-pop-ink, var(--text)); font-size: .75rem;
}
.adm-chip i { font-size: .625rem; color: var(--app-pop-label, var(--text-muted)); }
.adm-chip:focus-visible, .adm-filters__open:focus-visible { outline: 2px solid var(--app-pop-accent, var(--accent)); outline-offset: 2px; }

/* Row actions: optional Open + Edit, then a More menu (fixed-position so tables never clip it). */
.adm-ra { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.adm-ra .app-btn { height: var(--tb-h-sm, 26px); min-height: 0; font-size: .75rem; text-decoration: none; }
.adm-ra .app-btn-icon { width: var(--tb-h-sm, 26px); padding: 0; }
.adm-more { position: relative; display: inline-block; }
.adm-more__menu {
  position: fixed; z-index: 60; min-width: 220px; max-width: calc(100vw - 16px); padding: 6px; display: flex; flex-direction: column;
  background: var(--app-pop-bg, var(--surface-raised)); border: 1px solid var(--app-pop-line, var(--line)); border-radius: 12px;
  box-shadow: var(--app-pop-shadow, var(--shadow-lg));
}
.adm-more__menu[hidden] { display: none; }
.adm-more__form { margin: 0; }
.adm-more__item {
  font: inherit; cursor: pointer; width: 100%; display: flex; align-items: center; gap: 9px; padding: 8px 10px; border: 0; border-radius: 8px;
  background: transparent; color: var(--app-pop-ink, var(--text)); font-size: .8125rem; text-align: left; text-decoration: none;
}
.adm-more__item i { width: 16px; text-align: center; color: var(--app-pop-label, var(--text-muted)); }
.adm-more__item:hover, .adm-more__item:focus-visible { background: var(--app-pop-hover, var(--surface-accent)); outline: none; }
.adm-more__item:disabled { opacity: .6; cursor: default; }
.adm-more__item.is-danger, .adm-more__item.is-danger i { color: var(--app-pop-danger, var(--danger-text-2)); }
.adm-more__sep { border: 0; border-top: 1px solid var(--app-pop-divider, var(--line-faint)); margin: 4px 0; }
.adm-sheet__backdrop { position: fixed; inset: 0; z-index: 55; background: var(--app-pop-scrim, rgba(20, 16, 40, .4)); }
.adm-sheet__backdrop[hidden] { display: none; }

/* Phone: filters fold into a bottom sheet; the More menu is a bottom sheet. */
@media (max-width: 767px) {
  .adm-filters { padding: 8px 16px; }
  .adm-filters__search { flex: 1 1 140px; }
  .adm-filters__open { display: inline-flex; align-items: center; gap: 6px; }
  .adm-filters__sheet {
    display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 60; padding: 14px 16px 20px; max-height: 80vh; overflow-y: auto;
    background: var(--app-pop-bg, var(--surface-raised)); border-radius: 16px 16px 0 0; box-shadow: var(--app-pop-sheet-shadow, var(--shadow-lg));
  }
  .adm-filters.is-open .adm-filters__sheet { display: block; }
  .adm-sheet__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
  .adm-filters__selects { flex-direction: column; }
  .adm-filters__sel select { width: 100%; height: 36px; }
  .adm-filters__count { margin-left: 0; width: 100%; }
  .adm-ra .app-btn { height: 36px; }
  .adm-ra .app-btn-icon { width: 36px; }
  .adm-more__menu { left: 0 !important; right: 0; top: auto !important; bottom: 0; max-width: none; border-radius: 16px 16px 0 0; padding: 10px 12px 18px; }
  .adm-more__item { padding: 12px 12px; font-size: .9375rem; }
}

/* Compiled Tailwind gives every primary-table row hover a fixed light #ede9fe cell
   background; under the dark theme the cells take the table hover token. */
@media screen {
  :root[data-theme="dark"] .primary-table tbody tr:hover td { background: var(--tbl-row-hover); }
}

/* ── Inter everywhere: former monospace elements ───────────────────────────
   Inter replaces every monospace face. Digits keep their column alignment with tabular figures;
   codes, SKUs and secrets also get the slashed zero so 0 and O stay distinct. */
.td-num, .th-num, input[type="number"],
.cell-mono, .fk-cell-mono, .fk-card__meta, .fk-matrix__h, .fk-cell, .fk-ratio__value, .fk-check__val, .fk-opt__label, .fk-opt__ur,
.fu-grid th.fu-grid__stud, .fu-opt, .fu-pick__label, .fu-pick__facts dd,
.sq-l-session-id, .sq-boq-uid, .sq-cmd__input, .sq-chip-dim, .rpt-ref, .fr-banner-detail, .detail-panel-input--num,
.bs-res-table td.td-num, .bs-check-table td.td-num, .bs-xsec-na-lbl, .sb-comp-lc-tag, .ds-match-pill {
  font-variant-numeric: tabular-nums;
}
.acct-mono, .acct-secret, .acct-otp-input, .acct-codes code,
.sq-bom-sku, .wsq-picker-opt-code, .wsq-sku-note, .wsf-comp-sku, .sb-ls-code, .sb-comp-code, .ep-result-sku,
.insp-comp-code, .insp-comp-layer, .insp-comp-sku, .bm-sku, .grp-comp-sku, .sqg-sku, .detail-code, .detail-panel-input--mono,
.ds-chip-code, .ai-wt-code, .ai-wt-mc-code, .ai-tbl-code, .ai-review-link b, .tb-log, .import-log, .imp-log {
  font-variant-numeric: tabular-nums slashed-zero;
}
