* { box-sizing: border-box; }

/* Super-admin gating (see js/auth/admin.js). auth-gate.js adds `is-admin` to
   <html> when the signed-in email is on ADMIN_EMAILS. Any element marked
   data-admin-only is then hidden from non-admins. */
html:not(.is-admin) [data-admin-only] { display: none !important; }
/* Account-tier gating (see js/auth/account-tier.js). markTier() adds
   `tier-allows-<t>` to <html> for every tier at or below the active rank
   (free < pro < max < admin). Mark a feature element data-needs-pro /
   data-needs-max / data-needs-admin and it is hidden for users below that
   tier. Nothing is gated yet — feature locks are TBD. */
html:not(.tier-allows-pro)   [data-needs-pro],
html:not(.tier-allows-max)   [data-needs-max],
html:not(.tier-allows-admin) [data-needs-admin] { display: none !important; }
/* P5 — typography pass. Specialist tools use a NAMED display sans, not
   the OS default (system-ui resolves to Segoe UI on Windows / SF on
   Mac, both of which read as "AI-generated landing page" against a
   pro-AV simulator). Inter Tight is the same family Treble uses;
   JetBrains Mono is the de-facto DAW/CAD numeric face. Numerics get
   tabular-nums by default so digits don't dance when SPL / RT60 /
   distance values update mid-session. */
html, body {
  margin: 0;
  height: 100%;
  /* App is viewport-bound — every Lab fits 100vh by design (3D
     viewport, SpeakerLAB workbench, DeviceLAB rack browser). The body
     should never scroll; if a Lab needs scrolling it does so inside
     its own container so the rails / glass header stay pinned. */
  overflow: hidden;
  font-family: "Inter Tight", "IBM Plex Sans", -apple-system, system-ui, sans-serif;
  font-feature-settings: "tnum" 1, "cv11" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* P5.c — JetBrains Mono on every numeric readout (live SPL / STI in
   walk mode, the SPL legend tick labels, the 2D legend, the results
   panel metric values, the precision-render headline number, the
   listener-row XYZ display). DAW / CAD convention — instrument-class
   tools use a mono face for digits so column alignment is rock-steady
   when values update mid-frame. The `tabular-nums` already comes via
   the body's font-feature-settings; this just swaps the face. */
.walk-spl,
.walk-spl-big,
.spl-legend-3d,
.spl-legend-3d .legend-tick-label,
.vp-legend.spl-legend-v,
.vp-legend.spl-legend-v .spl-legend-tick-label,
.summary,
.metric-value,
.metric-num,
.precision-headline-num,
.tile-value,
.pr-mono,
.sv-cat-summary {
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

#app-header {
  /* P10 — glass header. Position fixed at viewport top so the 3D /
     2D scene extends BEHIND it; backdrop-filter blurs the scene
     visibly through the header. Same visual family as rail panels
     and floating segment / corner buttons. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 44px;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 12px;
  z-index: 30;     /* above floating segment / rails / panels */
  background: rgba(20, 22, 28, 0.40);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
@supports not ((backdrop-filter: blur(18px)) or (-webkit-backdrop-filter: blur(18px))) {
  #app-header { background: rgba(20, 22, 28, 0.65); }
}
#app-header .app-brand {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  white-space: nowrap;
}
#app-header .brand-text {
  /* P5 — dropped uppercase + 0.12em letter-spacing (Maya's "2018
     startup tell" call). Mixed-case + tighter spacing reads as a
     specialist tool brand, not a SaaS landing page. */
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--fg-strong, #e5e7eb);
}
#app-header .project-name {
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--fg-strong, #e5e7eb);
  padding: 0.18rem 0.55rem;
  border-radius: 4px;
  background: rgba(74, 163, 255, 0.12);
  border: 1px solid rgba(74, 163, 255, 0.32);
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
}
#app-header .project-name[hidden] { display: none; }
#app-header .lab-nav {
  display: flex;
  gap: 4px;
  align-items: stretch;
}
/* Header-action buttons — Save / Load / Share / Print on the far right.
   Stay compact so they don't squeeze the nav at narrow widths. */
#app-header .header-actions {
  display: flex;
  gap: 0.3rem;
  margin-left: auto;
  align-items: center;
}
#app-header .header-actions button {
  /* P5 — file-level actions visually demoted. Smaller padding, no
     border by default, muted colour. Hover lifts to a subtle bg
     so they're discoverable but never compete with the lab tabs.
     This separates "where am I" (lab nav, primary) from "save this"
     (file actions, secondary) the way EASE / ArrayCalc do. */
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  padding: 4px 10px;
  font-size: 0.74rem;
  border-radius: var(--radius);
  cursor: pointer;
  white-space: nowrap;
  font: inherit;
  transition: background-color 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
}
#app-header .header-actions button:hover {
  border-color: var(--border);
  color: var(--fg-strong, #fff);
  background: rgba(255, 255, 255, 0.04);
}
/* Unsaved-changes state on the cloud Save button — accent colour + the "•" in
   the label signal that work isn't yet in the user's account. */
#app-header .header-actions button.btn-save-dirty {
  color: var(--accent);
}
#app-header .header-actions button.btn-save-dirty:hover {
  color: var(--accent-hover, #6ab4ff);
  border-color: var(--accent);
}
/* Print button gated state — no fresh precision render → muted look so
   the user sees Print is blocked BEFORE clicking. Clicking still
   surfaces a status message explaining what to do (panel-room.js). */
#app-header .header-actions button.btn-print-blocked {
  opacity: 0.45;
  cursor: not-allowed;
  position: relative;       /* anchor for the ::after hover tooltip below */
}
#app-header .header-actions button.btn-print-blocked:hover {
  background: transparent;
  border-color: var(--border-soft, rgba(255,255,255,0.06));
  color: var(--fg-mid, #8a929c);
}
/* Custom hover tooltip — appears immediately (no OS 1 s delay), styled
   to match the app, anchored below the button. data-block-reason is
   set by syncPrintBtnState() with an action-oriented sentence telling
   the user exactly which panel to open and what to click. Only renders
   when blocked + on hover; never on the active button. */
#app-header .header-actions button.btn-print-blocked[data-block-reason]:hover::after {
  content: attr(data-block-reason);
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 1000;
  width: max-content;
  max-width: 320px;
  padding: 8px 12px;
  background: rgba(20, 24, 32, 0.96);
  color: var(--fg, #e6edf3);
  border: 1px solid rgba(74, 163, 255, 0.55);
  border-radius: 4px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  font-family: 'Inter Tight', system-ui, sans-serif;
  font-size: 11.5px;
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  letter-spacing: 0;
  white-space: normal;
  pointer-events: none;
  opacity: 1;
  cursor: default;
}
/* Small arrow notch on the tooltip pointing up to the button. */
#app-header .header-actions button.btn-print-blocked[data-block-reason]:hover::before {
  content: '';
  position: absolute;
  top: calc(100% + 2px);
  right: 18px;
  z-index: 1001;
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 6px solid rgba(74, 163, 255, 0.55);
  pointer-events: none;
}
/* Reset button — same shape as the others but visually distinct so
   the destructive action signals itself. Compact, icon-only;
   confirmation dialog is the real safeguard. */
#app-header .btn-reset {
  padding: 0.32rem 0.55rem;
  min-width: 30px;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--muted);
}
#app-header .btn-reset:hover {
  background: rgba(229, 68, 68, 0.18);
  border-color: rgba(229, 68, 68, 0.55);
  color: #fca5a5;
}
#app-header .lab-tab {
  /* P5 — square chrome, no rounded pill. Active state is a subtle
     inset (lighter background + 1 px inset shadow) instead of a solid
     blue fill — instrument-like, less SaaS-tab feel. Saturated colour
     stays reserved for data-vis. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 1px;
  padding: 5px 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill, 0);
  background: transparent;
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease-out, color 120ms ease-out, border-color 120ms ease-out;
}
#app-header .lab-tab:hover:not(.disabled):not(.active) {
  color: var(--fg);
  background: rgba(255, 255, 255, 0.03);
}
#app-header .lab-tab.active {
  background: rgba(255, 255, 255, 0.06);
  color: var(--fg-strong, #fff);
  border-color: var(--border);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}
#app-header .lab-tab.active .lab-tab-sub { color: rgba(255, 255, 255, 0.5); }
#app-header .lab-tab.disabled {
  cursor: not-allowed;
  opacity: 0.45;
}
#app-header .lab-tab .lab-tab-label {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1.1;
}
#app-header .lab-tab .lab-tab-sub {
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1;
}

/* SPA route containers — only the active one is visible. The router
   toggles `.active` on `.lab-route[data-route="<id>"]`. Inactive Labs
   stay in the DOM (display:none), preserving their in-memory state +
   event listeners so route changes are instant DOM swaps, not
   re-mounts. RoomLAB triggers a window resize event when becoming
   active again so the Three.js renderer picks up any size changes
   that happened while it was hidden. */
.lab-route { display: none; }
.lab-route.active { display: block; }

/* Layout (P7 — full-bleed). Viewport spans 100 % of the width below
   the header; rail icons float as ABSOLUTE overlays on each edge with
   their own glass backgrounds, no longer occupying grid columns. The
   3D / 2D preview uses every pixel between the screen edges. Panels
   still slide out from the rail's old anchor (56 px from edge). */
#app-root {
  /* P10 — full viewport height; content extends BEHIND the now-fixed
     glass header. Floating elements inside (segment, rails, panels)
     have offsets bumped by ~44 px below to clear the header. */
  display: grid;
  grid-template-columns: 1fr;
  height: 100vh;
  position: relative;
}

/* Fullscreen mode — hides side panels AND the top header AND the
   rails so the viewport occupies the entire fullscreen rect. */
html.is-fullscreen .rail,
html.is-fullscreen #panel-left,
html.is-fullscreen #panel-right,
html.is-fullscreen #app-header {
  display: none;
}
html.is-fullscreen #app-root {
  grid-template-columns: 1fr;
  height: 100vh;
}

/* ---------- Icon rails (P7 floating) ----------
   The rail STRIP is gone — no dark vertical bar competing with the
   viewport. Each icon is its own floating glass pill, stacked
   vertically in a column anchored to the screen edge. Modern pro-app
   pattern (Figma toolbox, Linear sidebar, Treble action column). */
.rail {
  /* P10 — top: 104 clears the glass header (44) + the floating mode
     segment (now at 56) + the segment height (~36) + small gap. */
  position: absolute;
  top: 104px;
  bottom: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 4px;
  gap: 6px;
  background: transparent;
  border: none;
  z-index: 10;
  pointer-events: none;       /* gaps don't intercept viewport drags */
}
.rail-left  { left: 0; }
.rail-right { right: 0; }

.rail-icon {
  pointer-events: auto;
  width: 40px;
  height: 40px;
  /* Each icon is its own glass pill so it stays readable over any
     heatmap colour or 3D scene. Backdrop-filter same family as the
     panel surfaces; falls back to opaque slate on browsers without. */
  background: rgba(20, 22, 28, 0.40);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: var(--radius);
  cursor: pointer;
  display: grid;
  place-items: center;
  color: var(--muted);
  position: relative;
  -webkit-tap-highlight-color: transparent;
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .rail-icon { background: rgba(20, 22, 28, 0.65); }
}
.rail-icon svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.rail-icon:hover {
  color: var(--fg);
  background: rgba(40, 50, 65, 0.78);
  border-color: rgba(255, 255, 255, 0.18);
}
.rail-icon:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rail-icon.is-active {
  color: var(--fg-strong);
  background: rgba(50, 70, 100, 0.85);
  border-color: var(--accent);
}

/* Precision-render rail icon — red frozen-glass treatment so it
   visually pairs with the red glass Render button inside the panel
   it opens. Same backdrop-filter blur as the other rail icons so
   it still reads as a member of the rail family, just tinted red. */
.rail-icon[data-panel="precision"] {
  background:
    linear-gradient(180deg, rgba(255, 90, 90, 0.16) 0%, rgba(170, 30, 30, 0.34) 100%),
    rgba(140, 30, 30, 0.30);
  border-color: rgba(255, 110, 110, 0.32);
  color: rgba(255, 200, 200, 0.85);
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .rail-icon[data-panel="precision"] { background: rgba(140, 30, 30, 0.55); }
}
.rail-icon[data-panel="precision"]:hover {
  background:
    linear-gradient(180deg, rgba(255, 120, 120, 0.26) 0%, rgba(200, 40, 40, 0.50) 100%),
    rgba(170, 35, 35, 0.45);
  border-color: rgba(255, 140, 140, 0.55);
  color: #fff5f5;
}
.rail-icon[data-panel="precision"].is-active {
  background:
    linear-gradient(180deg, rgba(255, 130, 130, 0.32) 0%, rgba(210, 45, 45, 0.62) 100%),
    rgba(180, 40, 40, 0.55);
  border-color: rgba(255, 150, 150, 0.85);
  color: #fff;
}

/* Hover label — small floating tooltip slides 8 px from the rail. */
.rail-icon::after {
  content: attr(aria-label);
  position: absolute;
  top: 50%;
  transform: translateY(-50%) translateX(0);
  background: rgba(20, 22, 28, 0.65);
  color: var(--fg);
  font-size: 0.72rem;
  white-space: nowrap;
  padding: 4px 8px;
  border-radius: 3px;
  border: 1px solid var(--border);
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease 80ms, transform 120ms ease 80ms;
  z-index: 20;
}
.rail-left  .rail-icon::after { left:  64px; }
.rail-right .rail-icon::after { right: 64px; }
.rail-icon:hover::after,
.rail-icon:focus-visible::after {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

/* ---------- Side-panel overlays (P2: animated + frosted glass) ----
   Visibility is driven by transform + opacity (NOT display: none) so
   transitions can run on toggle. Initial state is off-screen + invis-
   ible + non-interactive; `data-rail-<side>` flips that to on-screen.
   * Open  → 320 ms slide with cubic-bezier(0.34, 1.56, 0.64, 1) bounce
   * Close → 220 ms ease-in (snappier; "get out of the way")
   * Glass → backdrop-filter blur + saturate, with @supports fallback
   * Mobile → blur radius cap at 12 px (Viktor §4 perf constraint)
   * Reduced motion → opacity-only fade, no transform, no bounce
   * will-change: transform applied ONLY during animation (the
     `.is-animating` class set by rail-system.js for ~350 ms)
*/
.rail-panel {
  position: fixed;
  top: 44px;          /* sits flush with the trimmed header */
  bottom: 0;
  width: 360px;
  background: rgba(20, 24, 32, 0.65);   /* fallback when no backdrop-filter */
  border: 1px solid var(--border);
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--space-3) var(--space-4);
  z-index: 11;
  opacity: 0;
  pointer-events: none;
  transition: transform 220ms cubic-bezier(0.4, 0, 1, 1),
              opacity 180ms linear;
}
.rail-panel-left  { left:  56px; border-left:  none; transform: translateX(-100%); }
.rail-panel-right { right: 56px; border-right: none; transform: translateX(100%); }

/* Glass surface — only when supported AND the FPS-watchdog has not
   degraded to opaque mode (`html.is-glass-fallback`). */
@supports (backdrop-filter: blur(18px)) or (-webkit-backdrop-filter: blur(18px)) {
  html:not(.is-glass-fallback) .rail-panel {
    background: rgba(20, 24, 32, 0.45);
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
  }
}

/* Mobile / small-tablet: cap blur radius at 12 px (Viktor §4) and
   drop saturate to 130% (mobile Safari ignores past ~130%). */
@media (max-width: 768px) {
  @supports (backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px)) {
    html:not(.is-glass-fallback) .rail-panel {
      backdrop-filter: blur(12px) saturate(130%);
      -webkit-backdrop-filter: blur(12px) saturate(130%);
    }
  }
}

/* Open state — applied when <html> carries data-rail-<side>. Bounce
   curve overshoots ~8 % (~3 px) before settling; opacity fades in
   during the first 180 ms so the bounce lands on a fully-opaque
   element. */
html[data-rail-left]  #panel-left,
html[data-rail-right] #panel-right {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(0);
  transition: transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1),
              opacity 180ms linear;
}

/* will-change scoped to the animation window so the panel doesn't
   permanently pin a GPU layer (Viktor's §4 constraint #1). The
   .is-animating class is added by rail-system.js on every toggle and
   removed ~350 ms later. */
.rail-panel.is-animating {
  will-change: transform, opacity;
}

/* Reduced-motion: replace slide + bounce with a 120 ms opacity fade.
   No transform animation means no GPU compositing concern, and no
   peripheral motion for users with vestibular sensitivity. */
@media (prefers-reduced-motion: reduce) {
  .rail-panel,
  html[data-rail-left]  #panel-left,
  html[data-rail-right] #panel-right {
    transform: none;
    transition: opacity 120ms linear;
  }
}

/* No-transition guard for initial page load: prevents a slide-in
   "flash" if sessionStorage had a panel open from the previous
   session. The class is removed by rail-system.js after first paint. */
html.no-transitions-yet .rail-panel {
  transition: none !important;
}

/* Walk-mode: rails dim but stay interactive (Hannes §3 + §8). The
   user is focused on the avatar; rail icons should be reachable but
   not visually compete. */
html.is-walkmode .rail {
  opacity: 0.45;
  transition: opacity 200ms ease;
}
html.is-walkmode .rail:hover,
html.is-walkmode .rail:focus-within {
  opacity: 1;
}

/* ---------- Panel-internal accordions (P3) -----------
   Styled <details>/<summary> for collapsible sub-sections inside an
   open panel. No JS needed — <details> is native HTML. We strip the
   default browser disclosure triangle and replace with a chevron
   that rotates on open. */
.rail-panel details.acc {
  margin: 6px 0 8px;
  border-top: 1px solid var(--border);
  padding-top: 6px;
}
.rail-panel details.acc:first-of-type { border-top: none; padding-top: 0; }
.rail-panel details.acc > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--fg);
  text-transform: uppercase;
  user-select: none;
  -webkit-user-select: none;
}
.rail-panel details.acc > summary::-webkit-details-marker { display: none; }
.rail-panel details.acc > summary::before {
  content: '';
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(-45deg) translate(-2px, 0);
  transition: transform 180ms ease, border-color 180ms ease;
  flex: 0 0 8px;
}
.rail-panel details.acc[open] > summary::before {
  transform: rotate(45deg) translate(-2px, -2px);
  border-color: var(--fg-strong);
}
.rail-panel details.acc > summary:hover::before { border-color: var(--fg); }
.rail-panel details.acc > summary .acc-count {
  margin-left: auto;
  color: var(--muted);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  text-transform: none;
  font-size: 0.72rem;
}
.rail-panel details.acc > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}
.rail-panel details.acc[open] > summary {
  color: var(--fg-strong);
}

/* Hide every section inside the panel by default; show only the one
   matching <html data-rail-<side>="…">. */
.rail-panel > section { display: none; }
/* RoomLAB */
html[data-rail-left="room"]      #panel-room,
html[data-rail-left="sources"]   #panel-sources,
html[data-rail-left="listeners"] #panel-listeners,
html[data-rail-left="zones"]     #panel-zones,
html[data-rail-left="treatments"] #panel-treatments,
html[data-rail-left="furniture"]  #panel-furniture,
html[data-rail-left="structure"]  #panel-structure,
html[data-rail-left="ambient"]   #panel-ambient,
html[data-rail-left="library"]   #panel-library,
html[data-rail-left="outdoor"]   #panel-outdoor,
html[data-rail-right="results"]     #panel-results,
html[data-rail-right="author-note"] #panel-author-note,
html[data-rail-right="precision"]   #panel-precision,
/* SpeakerLAB (P4 + P4.6) */
html[data-rail-left="catalogue"]  #panel-catalogue,
html[data-rail-left="import"]     #panel-import,
html[data-rail-right="specs"]     #panel-specs,
html[data-rail-right="frequency"] #panel-frequency,
html[data-rail-right="polar"]     #panel-polar,
html[data-rail-right="waterfall"] #panel-waterfall,
/* DeviceLAB (P4) */
html[data-rail-left="rackbrowser"]   #panel-rackbrowser,
html[data-rail-left="amplifiers"]    #panel-amplifiers,
html[data-rail-right="signalflow"]   #panel-signalflow,
html[data-rail-right="bom"]          #panel-bom,
/* SurfaceLAB (Lab #4) — seven left-rail icons by mechanism, four
   right-rail panels (specs / absorption / polar diffusion / construction).
   `specs` reuses the SpeakerLAB selector. */
html[data-rail-left="absorbers"]        #panel-absorbers,
html[data-rail-left="bass"]             #panel-bass,
html[data-rail-left="diffusers"]        #panel-diffusers,
html[data-rail-left="ceiling"]          #panel-ceiling,
html[data-rail-left="surfaces"]         #panel-surfaces,
html[data-rail-left="openings"]         #panel-openings,
html[data-rail-left="systems"]          #panel-systems,
html[data-rail-right="absorption"]      #panel-absorption,
html[data-rail-right="polardiffusion"]  #panel-polardiffusion,
html[data-rail-right="construction"]    #panel-construction,
/* WallLAB (Lab #5) — left: physics-mode (over-wall toggle); right: method. */
html[data-rail-left="physics"]          #panel-physics,
html[data-rail-right="method"]          #panel-method {
  display: block;
}
#viewport {
  position: relative;
  background: var(--bg-viewport);
  color: #9aa;
  overflow: hidden;
  /* P8 — viewport is a single-row container now; the previous
     `grid-template-rows: auto 1fr` reserved a row for the .viewport-
     tabs toolbar which has been replaced by floating overlays. */
  display: block;
}
/* P8 — Maya's 3-cluster top layout. The flat .viewport-tabs row is
   replaced by three floating overlays: a persistent segmented control
   for the most-used action (view mode), a corner cluster for the
   single-tap fullscreen + more-menu trigger, and a slide-down panel
   for low-frequency layer / tool toggles + keyboard shortcuts. */

/* Persistent segmented control — top-centre. ALWAYS visible; one click
   to switch views. Glass pill same family as rail icons, but
   horizontal and grouping the three radio options inside. */
.vp-mode-segment {
  position: absolute;
  top: 56px;       /* below the 44 px glass header + 12 px breathing */
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  background: rgba(20, 22, 28, 0.45);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  padding: 3px;
  gap: 2px;
  z-index: 10;
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .vp-mode-segment { background: rgba(20, 22, 28, 0.65); }
}
.vp-tab {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  padding: 5px 14px;
  min-width: 54px;
  border-radius: calc(var(--radius) - 1px);
  cursor: pointer;
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  text-align: center;
  transition: background-color 120ms ease, color 120ms ease;
}
.vp-tab:hover:not(.active) { color: var(--fg); background: rgba(255, 255, 255, 0.04); }
.vp-tab.active {
  background: rgba(255, 255, 255, 0.08);
  color: var(--fg-strong, #fff);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}

/* Corner buttons — top-right. Same glass-pill family as rail icons. */
.vp-corner-btn {
  position: absolute;
  top: 56px;
  width: 40px;
  height: 40px;
  background: rgba(20, 22, 28, 0.40);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 1rem;
  cursor: pointer;
  z-index: 10;
  display: grid;
  place-items: center;
  transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .vp-corner-btn { background: rgba(20, 22, 28, 0.65); }
}
.vp-corner-btn:hover {
  color: var(--fg-strong, #fff);
  background: rgba(40, 50, 65, 0.78);
  border-color: rgba(255, 255, 255, 0.18);
}
/* AutoCAD-style preset-view cluster — top-right of the 3D viewport,
   inside the scene area (not in the side rail). 3×2 grid: T/F/Bk on
   top row, L/R/Iso on bottom row. Same glass-pill family as the
   centre mode segment so the two clusters visually rhyme. Visibility
   is gated by JS (main.js sets a `hidden` attribute) when the
   viewport is in 2D or Walk mode — those views don't have an orbit
   camera to retarget. The 16-px clearance from the right edge avoids
   crowding the right rail's icons (which sit at right:0, 40 px wide). */
.vp-view-cube {
  position: absolute;
  top: 56px;                    /* aligned with the mode segment */
  right: 56px;                  /* rail icons are 40 px wide at right:0; +16 px gap */
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 32px;
  gap: 2px;
  padding: 3px;
  background: rgba(20, 22, 28, 0.45);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  z-index: 10;
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .vp-view-cube { background: rgba(20, 22, 28, 0.65); }
}
.vp-view-cube[hidden] { display: none; }
.vp-view-btn {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  padding: 2px 6px;
  min-width: 44px;
  border-radius: calc(var(--radius) - 1px);
  cursor: pointer;
  font: inherit;
  font-size: 0.66rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  transition: background-color 120ms ease, color 120ms ease;
}
.vp-view-btn:hover:not(.is-active) {
  color: var(--fg);
  background: rgba(255, 255, 255, 0.04);
}
.vp-view-btn.is-active {
  background: rgba(255, 255, 255, 0.08);
  color: var(--fg-strong, #fff);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3),
    inset 0 0 0 1px rgba(120, 160, 220, 0.45);
}
.vp-view-glyph {
  font-size: 0.85rem;
  line-height: 1;
  opacity: 0.85;
}
.vp-view-label {
  font-size: 0.66rem;
  line-height: 1;
}
/* Report-cam lock toggle nested inside the Iso button. Small, low-key —
   it's a power-user affordance, not a primary control. Clicking it
   toggles whether the print report's 3D cover uses the fixed Iso view
   (locked) or the user's current orbit angle (unlocked). stopPropagation
   in JS keeps the click from also firing the iso preset snap. */
.vp-iso-lock {
  font-size: 0.62rem;
  line-height: 1;
  margin-left: 1px;
  opacity: 0.55;
  cursor: pointer;
  user-select: none;
  border-radius: 3px;
  padding: 0 1px;
  transition: opacity 120ms ease, background-color 120ms ease;
}
.vp-iso-lock:hover,
.vp-iso-lock:focus-visible {
  opacity: 1;
  background: rgba(255, 255, 255, 0.12);
  outline: none;
}
/* Unlocked reads as the "live" / active state — brighten it so the user
   notices the report will use a non-default angle. */
.vp-iso-lock.is-unlocked { opacity: 0.95; }
/* On narrow viewports collapse to glyph-only so the cluster doesn't
   eat horizontal space from the central column. */
@media (max-width: 900px) {
  .vp-view-cube { right: 48px; }
  .vp-view-btn { min-width: 30px; padding: 2px 3px; }
  .vp-view-label { display: none; }
  .vp-view-glyph { font-size: 0.95rem; }
}

/* Fullscreen + more-menu BOTH live INSIDE the segmented control now
   (2D / 3D / Walk / ⛶ / ⋯). Both inherit .vp-tab dimensions; just
   size the glyph slightly larger so icons match the visual weight
   of the text labels. */
.vp-mode-segment .vp-fullscreen-btn,
.vp-mode-segment .vp-more-trigger {
  min-width: 40px;
  font-size: 1rem;
  padding-inline: 8px;
}
.vp-mode-segment .vp-more-trigger {
  font-size: 1.2rem;          /* ⋯ glyph is small in default size; bump it */
  line-height: 0.6;            /* nudge the dots vertically into centre */
  padding-bottom: 4px;
}
/* Active "more menu open" state on the segment trigger — same accent
   treatment as the rail icons when a panel is open, so the affordance
   stays consistent across the app. */
.vp-mode-segment .vp-more-trigger[aria-expanded="true"] {
  color: var(--fg-strong, #fff);
  background: rgba(50, 70, 100, 0.85);
  border-color: var(--accent);
}

/* --- 2D grid/snap selector: green ▾ on the 2D tab + drop-down menu --- */
.vp-tab-2d { position: relative; }
.vp-grid-arrow {
  position: absolute;
  right: 1px;
  bottom: -2px;
  font-size: 0.95rem;             /* larger, clearly tappable */
  line-height: 1;
  color: #34c759;                 /* green affordance */
  opacity: 0.95;
  cursor: pointer;
  padding: 0 2px;
  border-radius: 3px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  transition: opacity 120ms ease, background-color 120ms ease;
}
.vp-grid-arrow:hover,
.vp-grid-arrow:focus-visible { opacity: 1; background: rgba(52, 199, 89, 0.18); outline: none; }
.vp-grid-arrow[aria-expanded="true"] { background: rgba(52, 199, 89, 0.25); opacity: 1; }
.vp-grid-menu {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  min-width: 96px;
  background: rgba(20, 22, 28, 0.96);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius);
  padding: 4px;
  z-index: 20;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
}
.vp-grid-menu[hidden] { display: none; }
.vp-grid-menu-title {
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding: 3px 8px 5px;
}
.vp-grid-opt {
  display: flex;
  align-items: center;
  width: 100%;
  background: transparent;
  border: 0;
  color: var(--fg, #e8eaed);
  font: inherit;
  font-size: 0.74rem;
  text-align: left;
  padding: 5px 8px;
  border-radius: calc(var(--radius) - 2px);
  cursor: pointer;
}
.vp-grid-opt:hover { background: rgba(255, 255, 255, 0.06); }
.vp-grid-opt[aria-checked="true"] { color: #34c759; font-weight: 600; }
.vp-grid-opt[aria-checked="true"]::before { content: "✓ "; }
.vp-grid-opt[aria-checked="false"]::before { content: ""; padding-left: 0.9em; }
/* Show-grid toggle (checkbox-style) at the top of the grid dropdown. */
.vp-grid-toggle {
  display: flex;
  align-items: center;
  width: 100%;
  background: transparent;
  border: 0;
  color: var(--fg, #e8eaed);
  font: inherit;
  font-size: 0.74rem;
  text-align: left;
  padding: 5px 8px;
  border-radius: calc(var(--radius) - 2px);
  cursor: pointer;
}
.vp-grid-toggle::before {
  content: "";
  display: inline-block;
  width: 12px; height: 12px;
  margin-right: 8px;
  border: 1px solid #5b6573;
  border-radius: 3px;
  flex: none;
}
.vp-grid-toggle:hover { background: rgba(255, 255, 255, 0.06); }
.vp-grid-toggle.is-on { color: #34c759; }
.vp-grid-toggle.is-on::before {
  background: #34c759;
  border-color: #34c759;
  box-shadow: inset 0 0 0 2px #161b22;
}
.vp-grid-sep { height: 1px; background: rgba(255, 255, 255, 0.08); margin: 4px 2px; }
/* Green arrow reads "active" when the grid is shown. */
.vp-grid-arrow.is-grid-on { opacity: 1; }

/* Slide-down "more" panel anchored below the centred segmented
   control (2D/3D/Walk/⛶/⋯). Same animation grammar as the rail
   panels — translateY-from-above with bounce on open, fade-in on
   opacity. */
.vp-more-panel {
  position: absolute;
  top: 104px;     /* below segment (56) + segment height (~40) + gap (8) */
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  width: 280px;
  background: rgba(20, 22, 28, 0.45);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-lg);
  padding: 12px 14px 10px;
  z-index: 11;
  opacity: 0;
  pointer-events: none;
  transition: transform 220ms cubic-bezier(0.4, 0, 1, 1), opacity 180ms linear;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}
@supports not ((backdrop-filter: blur(18px)) or (-webkit-backdrop-filter: blur(18px))) {
  .vp-more-panel { background: rgba(20, 22, 28, 0.70); }
}
.vp-more-panel[hidden] { display: none; }
html[data-more-menu] .vp-more-panel {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
  transition: transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 180ms linear;
}
@media (prefers-reduced-motion: reduce) {
  .vp-more-panel,
  html[data-more-menu] .vp-more-panel {
    transform: translateX(-50%);
    transition: opacity 120ms linear;
  }
}
.vp-more-section { margin-bottom: 10px; }
.vp-more-section h4 {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 6px;
}
.vp-more-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}
.vp-more-footer {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  margin-top: 4px;
  padding-top: 8px;
}
.vp-more-footer button {
  background: transparent;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.74rem;
  cursor: pointer;
  padding: 4px 6px;
  text-align: left;
  width: 100%;
}
.vp-more-footer button:hover { color: var(--fg-strong, #fff); }

/* "Nymphysics Engine" section — catalogue labs (SurfaceLAB / WallLAB /
   FurnitureLAB) moved off the top bar into the "⋯" menu at v=852. Each
   row is a full-width SPA router link: bold name + muted one-line
   sublabel. Reuses the panel's two-tone (ink/muted) + accent language
   so it sits flush with the Layers / Tools sections above it. */
.vp-more-labs {
  display: grid;
  gap: 4px;
}
.vp-more-lab {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 8px;
  border: 1px solid #2a2f38;
  border-radius: 4px;
  text-decoration: none;
  color: #89929d;
  transition: color 140ms ease-out, border-color 140ms ease-out, background-color 140ms ease-out;
}
.vp-more-lab:hover {
  color: #fff;
  border-color: #4a515b;
  background: rgba(255, 255, 255, 0.04);
}
.vp-more-lab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.vp-more-lab-name {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: inherit;
}
.vp-more-lab-sub {
  font-size: 0.7rem;
  color: var(--muted-dim, #5a616c);
}
.vp-more-lab:hover .vp-more-lab-sub { color: var(--muted, #8a929d); }
@media (prefers-reduced-motion: reduce) {
  .vp-more-lab { transition: none; }
}

.vp-toggle {
  background: transparent;
  border: 1px solid #2a2f38;
  color: #89929d;
  padding: 0.3rem 0.55rem;
  border-radius: 4px;
  cursor: pointer;
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.vp-toggle:hover:not(:disabled) { color: #fff; border-color: #4a515b; }
.vp-toggle.active { background: #3a5a8a; color: #fff; border-color: #5a7ab0; }
.vp-toggle:not(.active)::before { content: '○ '; opacity: 0.6; }
.vp-toggle.active::before { content: '● '; }
.vp-toggle:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  color: #5a616a;
  border-color: #242830;
}
.vp-toggle:disabled::before { opacity: 0.25; }

/* Heatmap octave-band selector — horizontal row of 7 pills under the
   Layers toggle grid in the "•••" panel. Mirrors a graphic-EQ band row
   (engineers think "the 4k", not "position 6 of 7"). Driven by
   js/labs/roomlab/main.js → setHeatmapFrequency() in scene.js. */
.vp-freq-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
  margin-top: 8px;
  padding: 4px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
  position: relative;
}
/* Faint baseline that ties the 7 pills into one continuum — reads as
   an octave-band ribbon, not 7 unrelated buttons. */
.vp-freq-row::after {
  content: '';
  position: absolute;
  left: 8px; right: 8px; bottom: 3px;
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0%,
    rgba(90, 122, 176, 0.25) 15%,
    rgba(90, 122, 176, 0.45) 50%,
    rgba(90, 122, 176, 0.25) 85%,
    transparent 100%);
  pointer-events: none;
}
.vp-freq-pill {
  position: relative;
  background: transparent;
  border: 1px solid transparent;
  color: #89929d;
  padding: 5px 0;
  font: inherit;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  cursor: pointer;
  border-radius: 4px;
  text-align: center;
  transition: color 180ms ease-out, background-color 200ms ease-out,
              border-color 200ms ease-out, transform 120ms ease-out;
}
.vp-freq-pill:hover:not(:disabled):not(.active) {
  color: #c5ccd4;
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.08);
}
.vp-freq-pill:active:not(:disabled) {
  transform: scale(0.96);
}
.vp-freq-pill.active {
  color: #fff;
  background: #3a5a8a;
  border-color: #5a7ab0;
  box-shadow: 0 0 0 1px rgba(90, 122, 176, 0.35), 0 2px 6px rgba(58, 90, 138, 0.25);
}
.vp-freq-pill:focus-visible {
  outline: 2px solid var(--accent, #5a7ab0);
  outline-offset: 2px;
  z-index: 1;
}
.vp-freq-pill:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  color: #5a616a;
}
.vp-freq-row[aria-disabled="true"]::after {
  opacity: 0.3;
}
@media (prefers-reduced-motion: reduce) {
  .vp-freq-pill { transition: none; }
  .vp-freq-pill:active:not(:disabled) { transform: none; }
}

/* Heatmap resolution selector — a label + 3 pills sharing the octave-band
   pill look so the more-panel reads as one control family. */
.vp-res-row {
  display: grid;
  grid-template-columns: auto 1fr 1fr 1fr;
  align-items: center;
  gap: 3px;
  margin-top: 6px;
  padding: 4px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 6px;
}
.vp-res-label {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #7d8893;
  padding: 0 8px 0 4px;
}
.vp-res-pill {
  background: transparent;
  border: 1px solid transparent;
  color: #89929d;
  padding: 5px 0;
  font: inherit;
  font-size: 0.72rem;
  cursor: pointer;
  border-radius: 4px;
  text-align: center;
  transition: color 180ms ease-out, background-color 200ms ease-out,
              border-color 200ms ease-out, transform 120ms ease-out;
}
.vp-res-pill:hover:not(.active) {
  color: #c5ccd4;
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.08);
}
.vp-res-pill:active { transform: scale(0.96); }
.vp-res-pill.active {
  color: #fff;
  background: #3a5a8a;
  border-color: #5a7ab0;
  box-shadow: 0 0 0 1px rgba(90, 122, 176, 0.35), 0 2px 6px rgba(58, 90, 138, 0.25);
}
.vp-res-pill:focus-visible {
  outline: 2px solid var(--accent, #5a7ab0);
  outline-offset: 2px;
  z-index: 1;
}
@media (prefers-reduced-motion: reduce) {
  .vp-res-pill { transition: none; }
  .vp-res-pill:active { transform: none; }
}

.vp-help-btn { min-width: 28px; padding: 0.3rem 0.5rem; font-weight: 600; }
.vp-help-btn::before { content: '' !important; }

/* Toolbar overflow handling. Container queries on .viewport-tabs measure
   the actual toolbar width (not viewport width), so the row reacts to
   the central column's real space — independent of left/right panel
   widths. The Probe / Rays / Aim-lines tools collapse to icon-only when
   the toolbar gets narrow. The ? help button stays icon-only at all
   widths (already iconic). Layer toggles keep their labels because the
   metric names ("Heatmap", "STIPA", "Reverb field") don't abbreviate
   well visually. */
.viewport-tabs { container-type: inline-size; }

.vp-toggle .vp-icon { display: none; }
.vp-toggle .vp-label { display: inline; }

@container (max-width: 1150px) {
  .vp-group-tools .vp-toggle .vp-label { display: none; }
  .vp-group-tools .vp-toggle .vp-icon { display: inline; font-weight: 700; font-size: 0.95rem; line-height: 1; }
  .vp-group-tools .vp-toggle { min-width: 32px; padding: 0.3rem 0.45rem; }
  /* Drop the ○/● bullet in icon-only mode — the active background
     already conveys on/off state and the bullet crowds the icon. */
  .vp-group-tools .vp-toggle::before { content: '' !important; }
}
@container (max-width: 920px) {
  .vp-tab { min-width: 0; padding: 0.3rem 0.45rem; }
  .vp-group-layers { margin-left: 8px; padding-left: 8px; }
  .vp-group-tools  { padding-left: 8px; }
}

/* ? cheat-sheet modal overlay. Opens via the ? key or the ? button in
   the viewport. Dim background, centered card, two columns of shortcuts. */
#help-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 10, 14, 0.78);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  animation: helpFadeIn 120ms ease-out;
}
@keyframes helpFadeIn { from { opacity: 0; } to { opacity: 1; } }
#help-overlay[hidden] { display: none !important; }
.help-modal {
  background: #1a1d22;
  border: 1px solid #3a4048;
  border-radius: 8px;
  box-shadow: 0 24px 80px rgba(0,0,0,0.6);
  min-width: 560px;
  max-width: 760px;
  padding: 0;
  color: #dde;
}
.help-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px 12px;
  border-bottom: 1px solid #2a2f38;
}
.help-head h2 {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: #fff;
  margin: 0;
}
.btn-close {
  background: transparent;
  border: none;
  color: #89929d;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 10px;
  border-radius: 4px;
}
.btn-close:hover { color: #fff; background: #2a2f38; }
.help-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  padding: 18px 24px;
}
.help-col h3 {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: #89929d;
  margin: 0 0 8px;
  border: none;
}
.help-col h3:not(:first-child) { margin-top: 18px; }
.help-keys {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 16px;
  margin: 0;
}
.help-keys dt {
  font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
  font-size: 11px;
  color: #fff;
  background: #2a2f38;
  padding: 2px 7px;
  border-radius: 3px;
  border: 1px solid #3a4048;
  white-space: nowrap;
  justify-self: start;
  align-self: center;
}
.help-keys dd {
  margin: 0;
  color: #cfd3d9;
  font-size: 13px;
  align-self: center;
}
.help-foot {
  padding: 10px 20px 14px;
  border-top: 1px solid #2a2f38;
  color: #89929d;
  font-size: 11px;
  line-height: 1.55;
}
.help-foot strong { color: #cfd3d9; font-weight: 500; }
.btn-show-welcome {
  display: inline-block;
  margin-right: 12px;
  padding: 5px 11px;
  background: rgba(116, 208, 255, 0.12);
  border: 1px solid rgba(116, 208, 255, 0.4);
  border-radius: 4px;
  color: #74d0ff;
  font-family: inherit;
  font-size: 11px;
  cursor: pointer;
  letter-spacing: 0.02em;
}
.btn-show-welcome:hover {
  background: rgba(116, 208, 255, 0.22);
  border-color: #74d0ff;
}

/* ====================================================================
   Speaker detail view — full workbench replaces the 3D viewport when
   the user opens a loudspeaker's spec card.
   ==================================================================== */
#view-speaker {
  /* SpeakerLAB now mirrors the RoomLAB pattern: 3D preview fills the
     full viewport edge-to-edge; rails + title block float ON TOP as
     glass overlays. No padding here so the 3D canvas can use 100% of
     the window — that's also what makes the rail-panel's
     backdrop-filter blur visible (it has the 3D scene to blur). */
  padding: 0;
  background: var(--bg-app);
  overflow: hidden;
  height: 100vh;
  position: relative;          /* for absolute children (.sv-head, .sv-3d-caption) */
}
.speaker-view {
  display: grid;
  grid-template-columns: 1fr;       /* P4.5 — catalogue moved to left-rail panel; centre is single column */
  gap: 0;
  color: var(--fg);
  height: 100%;
}

/* When the catalogue list is relocated into the left-rail panel,
   strip its old sidebar chrome (border, background, max-height,
   sidebar padding) so it renders flush inside the rail panel.
   Same for DeviceLAB's rack columns. */
#panel-catalogue .sv-catalog {
  padding: 0;
  border-right: none;
  background: transparent;
  max-height: none;
  overflow: visible;
}
#panel-rackbrowser .rack-col-left,
#panel-bom .rack-col-right {
  padding: 0;
  border: none;
  background: transparent;
  width: auto;
  min-width: 0;
}

/* SurfaceLAB wrapper — Lab #4. Same shell as SpeakerLAB so navigation
   feels consistent; the centre column hosts a 3D preview of either a
   textured surface panel (plain finishes) or a procedural treatment
   product (QRDs, traps, absorbers). */
#surfacelab-root {
  height: 100vh;
  background: var(--bg-app);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr;
  position: relative;
}
#surfacelab-root #view-surface {
  height: 100%;
}
.surfacelab-view {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  color: var(--fg);
  height: 100%;
}

/* ── SurfaceLAB facet switch (Absorption / Isolation) ──────────────────
   SurfaceLAB hosts two facets in one route. #route-surface carries
   data-surface-facet="absorption|isolation"; everything tagged
   [data-facet] inside it shows only when its facet is active. This hides
   the inactive facet's rails (so its icons aren't clickable), panels, and
   centre view. The rail-system (js/ui/rail-system.js) is untouched — a
   display:none rail simply has no clickable icons.
   EXCEPT the facet SWITCH tabs (.surface-facet-tab) — they also carry a
   data-facet (their target), but they must ALWAYS stay visible or the user
   can never reach the inactive facet (the v=854 "where did WallLAB go?" bug:
   on the default Absorption facet the Isolation tab was being hidden). */
#route-surface[data-surface-facet="absorption"] [data-facet="isolation"]:not(.surface-facet-tab),
#route-surface[data-surface-facet="isolation"] [data-facet="absorption"]:not(.surface-facet-tab) {
  display: none !important;
}
/* The isolation centre view (#view-wall) reuses .walllab-view styling.
   When it's the active facet it must fill the route like #view-surface
   does; when hidden the [data-facet] rule above wins. */
#route-surface[data-surface-facet="isolation"] #view-surface { display: none; }
/* Isolation view scroll-pads its own header below the fixed app header;
   add a little extra so the floating facet bar doesn't overlap its h1. */
#route-surface[data-surface-facet="isolation"] #view-wall {
  padding-top: calc(44px + var(--space-4) + 44px);
}

/* Facet bar — route-level floating control, top-centre, above both views
   and clear of the rails. Sits in the same band as .sv-head. */
.surface-facet-bar {
  position: absolute;
  top: 56px;           /* clear the 44px fixed header + gap, matches .sv-head */
  left: 64px;          /* clear the left rail */
  right: 64px;         /* clear the right rail */
  z-index: 6;          /* above .sv-head glass (5), below rails (10) */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  pointer-events: none;   /* let clicks fall through the empty bar area… */
}
.surface-facet-switch {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: rgba(20, 22, 28, 0.62);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  flex: 0 0 auto;
  pointer-events: auto;   /* …but the switch itself is clickable */
}
.surface-facet-tab {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: calc(var(--radius) - 2px);
  padding: 4px 11px;
  cursor: pointer;
  white-space: nowrap;
}
.surface-facet-tab:hover { color: var(--fg); }
.surface-facet-tab:focus-visible {
  outline: 2px solid var(--accent, #74d0ff);
  outline-offset: 1px;
}
/* Active state signalled by fill + a clear border, NOT colour alone
   (Maya: must read on a calibrated / colour-blind display). */
.surface-facet-tab.is-active {
  color: var(--fg-strong, #fff);
  background: rgba(116, 208, 255, 0.16);
  border-color: rgba(116, 208, 255, 0.45);
}
.surface-facet-beta {
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--data-amber);
  background: rgba(255, 180, 84, 0.14);
  border: 1px solid rgba(255, 180, 84, 0.4);
  border-radius: 3px;
  padding: 1px 4px;
}
/* Orienting caption (Lin) — one line under the switch distinguishing the
   two facets. Centred in the floating bar; a glass pill for legibility
   over the 3D canvas. */
.surface-facet-caption {
  margin: 0;
  max-width: 60ch;
  text-align: center;
  font-size: 0.7rem;
  line-height: 1.3;
  color: var(--muted);
  font-style: italic;
  padding: 3px 10px;
  background: rgba(20, 22, 28, 0.55);
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--radius);
  pointer-events: none;
}
/* All four right-rail panels in SurfaceLAB follow SpeakerLAB's rules
   for blue accent + tight spacing. The kind-aware spec card content
   is rendered into #panel-specs by surface-detail.js. */
#view-surface {
  padding: 0;
  background: var(--bg-app);
  overflow: hidden;
  height: 100vh;
  position: relative;
}

/* SpeakerLAB-page wrapper — when this view runs as the whole page
   (speakers.html) instead of a RoomLAB viewport tab, it needs to fill
   the body height below the header nav. */
.lab-speakerlab body, body.lab-speakerlab { height: 100vh; }
#speakerlab-root {
  height: 100vh;       /* P10 — content extends behind glass header */
  background: var(--bg-app);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr;
  position: relative;
}
#speakerlab-root #view-speaker {
  height: 100%;
}
.speakerlab-view {
  /* Same as #view-speaker children — kept as a class hook in case the
     SpeakerLAB-only layout diverges later (e.g., comparison pane). */
}
.sv-main {
  padding: 1rem 1.25rem 2rem;
  overflow: auto;
  min-width: 0;
}
.sv-catalog {
  padding: 0.8rem 0.7rem;
  border-right: 1px solid var(--border);
  background: rgba(0, 0, 0, 0.15);
  overflow: auto;
  max-height: 100%;
}
.sv-cat-filter {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.7rem;
}
.sv-cat-filter select {
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0;
  text-transform: none;
  color: var(--fg);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.32rem 0.45rem;
  cursor: pointer;
}
.sv-cat-filter select:hover {
  border-color: var(--border-strong);
}
.sv-cat-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.55rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--border);
}
.sv-cat-count {
  font-size: 0.68rem;
  background: var(--bg-elev);
  padding: 0.08rem 0.38rem;
  border-radius: 8px;
  color: var(--fg);
  letter-spacing: 0;
}
.sv-cat-card {
  padding: 0.5rem 0.6rem;
  margin-bottom: 0.38rem;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.sv-cat-card:hover {
  border-color: var(--border-strong);
  background: rgba(255, 255, 255, 0.04);
}
.sv-cat-card.active {
  border-color: #74d0ff;
  background: rgba(116, 208, 255, 0.1);
}
.sv-cat-brand {
  font-size: 0.62rem;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.sv-cat-model {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--fg-strong);
  margin-top: 0.1rem;
  line-height: 1.3;
}
.sv-cat-kind {
  display: inline-block;
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.04rem 0.4rem;
  margin-top: 0.3rem;
}
.sv-cat-summary {
  font-size: 0.7rem;
  color: var(--muted);
  margin-top: 0.25rem;
  font-variant-numeric: tabular-nums;
}
.sv-cat-empty {
  padding: 0.8rem 0.3rem;
  font-size: 0.73rem;
  color: var(--muted);
  font-style: italic;
}
.sv-designer-note {
  margin: -0.5rem 0 1.4rem;
  padding: 0.55rem 0.7rem;
  font-size: 0.76rem;
  line-height: 1.55;
  color: var(--muted);
  background: rgba(255, 204, 90, 0.06);
  border-left: 2px solid rgba(255, 204, 90, 0.4);
  border-radius: 2px;
  font-style: italic;
}

/* Animated 3D cabinet preview — viewport-bound, edge-to-edge. Flat
   dark background matching the app palette so the canvas reads as
   "the 3D scene fills the window," same idiom as RoomLAB's viewport. */
.sv-3d-stage {
  margin: 0;
  padding: 0;
  background: var(--bg-app);
  border: none;
  border-radius: 0;
  text-align: center;
  position: absolute;
  inset: 0;
  height: 100%;
  display: block;
  overflow: hidden;
}
.sv-3d-stage canvas {
  display: block;
  /* Fill the entire stage (which fills the viewport). No aspect-ratio
     lock — Three.js's camera adjusts to whatever shape the viewport is,
     same as RoomLAB's 3D viewport. The renderer's pixel buffer is
     resized to match clientWidth/clientHeight in the animate loop
     (speaker-3d-preview.js). */
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: none;
}
/* The centre column fills the viewport; .sv-body fills the centre
   so the 3D preview can claim every pixel. Title block (.sv-head) is
   pulled out of normal flow into an absolute-positioned glass overlay
   below — that lets the canvas reach the top edge instead of being
   pushed down. */
/* Centre-column layout for SpeakerLAB AND SurfaceLAB — both Labs use
   the .speaker-view → .sv-main → .sv-head + .sv-body structure with a
   full-viewport 3D preview and a floating glass title bar. SurfaceLAB
   was originally missing these rules (falling through to the generic
   .sv-head), so the title rendered behind the glass header. */
#view-speaker .sv-main,
#view-surface .sv-main {
  position: absolute;
  inset: 0;
  height: 100%;
  padding: 0;
  box-sizing: border-box;
  overflow: hidden;
}
#view-speaker .sv-body,
#view-surface .sv-body {
  position: absolute;
  inset: 0;
  height: 100%;
  overflow: hidden;
}

/* Title + actions bar — floats over the 3D preview at the top, inside
   the rail clearance gutters so it doesn't collide with the icon
   rails. Glass panel matching the rail-panel + segment family. */
#view-speaker .sv-head,
#view-surface .sv-head {
  position: absolute;
  top: 56px;          /* clear the 44 px fixed glass header + 12 px gap */
  left: 64px;          /* clear the left rail (40 px icon + 24 px gap) */
  right: 64px;         /* clear the right rail */
  z-index: 5;          /* above canvas (0), below rails (10) and header (30) */
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  padding: 0.6rem 0.9rem;
  background: rgba(20, 22, 28, 0.40);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: var(--radius-lg);
  margin-bottom: 0;
  border-bottom: none;
  pointer-events: auto;
}
@supports not ((backdrop-filter: blur(18px)) or (-webkit-backdrop-filter: blur(18px))) {
  #view-speaker .sv-head,
  #view-surface .sv-head { background: rgba(20, 22, 28, 0.65); }
}
/* SurfaceLAB: the material-identity card used to sit top-left and overlapped
   the centred 3D preview (made worse by the facet bar also living up top).
   Move it to the bottom-LEFT corner — clear of the facet switch (top-centre),
   the rotate caption (bottom-centre) and the wood disc (which starts well to
   the right) — so the model reads unobstructed. Compact card; the material
   title + its trust chips stack vertically. */
#route-surface #view-surface .sv-head {
  top: auto;
  bottom: 4.5rem;
  left: 64px;
  right: auto;
  max-width: min(340px, 42vw);
  flex-direction: column;
  align-items: flex-start;
  gap: 0.45rem;
}
#route-surface #view-surface .sv-head .sv-title { flex: 0 0 auto; }

/* SpeakerLAB: same treatment as SurfaceLAB — the speaker-identity card
   (brand / model / description note) moves to the bottom-LEFT corner so
   the 3D speaker preview reads unobstructed. The Import action moved to
   the left rail (#panel-import), so the head now carries only the title;
   no .sv-actions to keep on the right. */
#route-speaker #view-speaker .sv-head {
  top: auto;
  bottom: 4.5rem;
  left: 64px;
  right: auto;
  max-width: min(340px, 42vw);
  flex-direction: column;
  align-items: flex-start;
  gap: 0.45rem;
}
#route-speaker #view-speaker .sv-head .sv-title { flex: 0 0 auto; }

/* Defensive — ensure the centre column shows ONLY the 3D preview,
   regardless of whether the JS DOM-relocation in speaker-detail.js
   fires successfully. Spec sections / catalogue / charts live in
   rail panels (P4.6); if any happen to remain in #view-speaker
   (timing race, error before relocation, etc.) we hide them here.
   This rule is a no-op when relocation works because the hidden
   selectors target descendants of #view-speaker — once relocated
   they're descendants of the rail panel, not #view-speaker. */
#view-speaker .sv-catalog,
#view-speaker .sv-grid,
#view-speaker .sv-body > .sv-section,
#view-speaker .sv-designer-note {
  display: none;
}
.sv-3d-stage canvas:active { cursor: grabbing; }
/* Caption pinned to the bottom of the stage so the canvas can claim
   the full vertical space; small glass pill for legibility against
   any speaker model. */
.sv-3d-caption {
  position: absolute;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.7rem;
  color: var(--muted);
  font-style: italic;
  padding: 4px 10px;
  background: rgba(20, 22, 28, 0.40);
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--radius);
  white-space: nowrap;
  max-width: calc(100% - 160px);
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  z-index: 5;
}
@supports not ((backdrop-filter: blur(8px)) or (-webkit-backdrop-filter: blur(8px))) {
  .sv-3d-caption { background: rgba(20, 22, 28, 0.65); }
}
.sv-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 0.9rem;
  padding-bottom: 0.8rem;
  border-bottom: 1px solid var(--border);
}
.sv-title { flex: 1; }
.sv-brand {
  font-size: 0.75rem;
  color: var(--muted);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.sv-model {
  font-size: 1.35rem;
  font-weight: 500;
  color: var(--fg-strong);
  letter-spacing: 0.01em;
  margin-top: 0.15rem;
}
.sv-note {
  font-size: 0.75rem;
  color: var(--muted);
  margin-top: 0.3rem;
  font-style: italic;
}
.sv-actions { display: flex; gap: 0.4rem; }
.btn-import-spec {
  padding: 0.4rem 0.75rem;
  background: rgba(116, 208, 255, 0.1);
  border: 1px dashed rgba(116, 208, 255, 0.45);
  color: #74d0ff;
  border-radius: var(--radius);
  font-size: 0.76rem;
  cursor: pointer;
  font-family: inherit;
}
.btn-import-spec:hover {
  background: rgba(116, 208, 255, 0.2);
  border-color: #74d0ff;
}
.sv-import-status {
  padding: 0.5rem 0.7rem;
  margin-bottom: 0.85rem;
  font-size: 0.76rem;
  line-height: 1.5;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--muted);
  border-left: 2px solid var(--border);
}
.sv-import-status.ok  { color: var(--data-green); border-left-color: var(--data-green); background: rgba(163, 217, 119, 0.08); }
.sv-import-status.err { color: var(--data-red);   border-left-color: var(--data-red);   background: rgba(255, 107, 107, 0.08); }
.sv-import-status.info{ color: #74d0ff;          border-left-color: #74d0ff;          background: rgba(116, 208, 255, 0.06); }
.sv-err-pre {
  margin: 0;
  font-family: inherit;
  font-size: 0.74rem;
  white-space: pre-wrap;
  color: inherit;
}

/* Import panel (left rail) — the import action button + supported-format
   guide, relocated here from the centre title bar / empty state. */
.sv-import-intro {
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0 0 0.75rem;
}
#panel-import .btn-import-spec {
  display: block;
  width: 100%;
  text-align: center;
  margin-bottom: 0.75rem;
}
.sv-import-formats {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0 0;
}
.sv-import-formats li {
  font-size: 0.76rem;
  color: var(--muted);
  line-height: 1.45;
  padding: 0.3rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.sv-import-formats li:last-child { border-bottom: 0; }
.sv-import-formats strong { color: var(--fg-strong); font-weight: 600; }

/* Secondary (export) + admin (publish) variants of the import button. */
#panel-import .btn-import-spec.sv-secondary {
  background: rgba(255, 255, 255, 0.04);
  border-style: solid;
  border-color: var(--border);
  color: var(--fg);
}
#panel-import .btn-import-spec.sv-secondary:hover { border-color: var(--fg); background: rgba(255, 255, 255, 0.08); }
#panel-import .btn-import-spec.sv-admin {
  background: rgba(163, 217, 119, 0.10);
  border-style: solid;
  border-color: rgba(163, 217, 119, 0.45);
  color: var(--data-green);
}
#panel-import .btn-import-spec.sv-admin:hover { border-color: var(--data-green); background: rgba(163, 217, 119, 0.18); }

/* DeviceLAB import-panel catalogue list — per-row export (⤓) / publish (☁). */
.dv-device-list { display: flex; flex-direction: column; gap: 2px; margin: 0 0 0.75rem; max-height: 40vh; overflow-y: auto; }
.dv-device-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elev);
}
.dv-device-name { font-size: 0.82rem; color: var(--fg-strong); }
.dv-device-spec { font-size: 0.72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.dv-device-acts { display: flex; gap: 4px; flex: 0 0 auto; }
.dv-row-btn {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius); background: rgba(255, 255, 255, 0.04);
  color: var(--fg); cursor: pointer; font-size: 0.9rem; font-family: inherit;
}
.dv-row-btn:hover { border-color: var(--fg); background: rgba(255, 255, 255, 0.08); }

/* "What is a .auralab file?" explainer — sits at the bottom of both Import
   panels using the shared rail-panel accordion (details.acc). */
.auralab-explainer { margin-top: 0.4rem; }
.auralab-explainer-body { font-size: 0.78rem; color: var(--muted); line-height: 1.5; padding-top: 0.3rem; }
.auralab-explainer-body p { margin: 0 0 0.6rem; }
.auralab-explainer-body p:last-child { margin-bottom: 0; }
.auralab-explainer-body strong { color: var(--fg-strong); font-weight: 600; }
.auralab-explainer-body em { font-style: italic; color: var(--fg); }

.sv-empty {
  /* Clear the floating glass head bar (~ top: 56 + 60 px tall + gap)
     and the left rail icon column. .sv-body is now inset:0 so the
     empty-state content needs its own viewport-clearance padding. */
  padding: 140px 80px 24px 80px;
  text-align: left;
  color: var(--muted);
  max-width: 720px;
  line-height: 1.55;
}
.sv-empty h3 { color: var(--fg-strong); font-weight: 500; margin: 0 0 0.5rem; }
.sv-empty ul { padding-left: 1.2rem; margin: 0.5rem 0 0; }
.sv-empty li { margin: 0.25rem 0; font-size: 0.78rem; }

.sv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.8rem;
  margin-bottom: 1.4rem;
}
.sv-card {
  padding: 0.8rem 0.9rem;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.sv-card h4 {
  margin: 0 0 0.55rem;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}
.sv-spec-row {
  display: flex;
  justify-content: space-between;
  padding: 0.22rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
  font-size: 0.78rem;
}
.sv-spec-row:last-child { border-bottom: 0; }
.sv-spec-label { color: var(--muted); }
.sv-spec-val { color: var(--fg-strong); font-variant-numeric: tabular-nums; }

.sv-section {
  margin-bottom: 1.4rem;
  padding: 0.9rem 1rem 1rem;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.sv-section h4 {
  margin: 0 0 0.65rem;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--fg-strong);
}
.sv-section canvas { display: block; max-width: 100%; }
#sv-fr { background: rgba(0, 0, 0, 0.22); border-radius: 4px; width: 100%; }
.sv-caption { font-size: 0.7rem; margin-top: 0.3rem; color: var(--muted); }

.sv-polar-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.sv-polar-freqs { display: flex; gap: 0.2rem; flex-wrap: wrap; }
.sv-freq-btn {
  padding: 0.2rem 0.55rem;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 3px;
  font-size: 0.7rem;
  font-family: inherit;
  cursor: pointer;
}
.sv-freq-btn:hover { color: var(--fg-strong); border-color: var(--border-strong); }
.sv-freq-btn.active {
  background: rgba(255, 204, 90, 0.12);
  border-color: #ffcc5a;
  color: #ffcc5a;
}
.sv-polar-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
}
.sv-polar-col { text-align: center; }
.sv-polar-label {
  font-size: 0.72rem;
  color: var(--muted);
  margin-bottom: 0.25rem;
  letter-spacing: 0.04em;
}
.sv-polar-col canvas { background: rgba(0, 0, 0, 0.28); border-radius: 50%; }

.sv-flags { display: grid; gap: 0.4rem; }
.sv-flag {
  padding: 0.5rem 0.65rem;
  font-size: 0.76rem;
  line-height: 1.5;
  border-radius: 3px;
  border-left: 2px solid var(--border);
  background: rgba(255, 255, 255, 0.03);
  color: var(--fg);
}
.sv-flag-good { border-left-color: var(--data-green); color: var(--data-green); background: rgba(163, 217, 119, 0.07); }
.sv-flag-info { border-left-color: #74d0ff; color: #74d0ff; background: rgba(116, 208, 255, 0.06); }
.sv-flag-warn { border-left-color: var(--data-amber); color: var(--data-amber); background: rgba(255, 180, 84, 0.07); }
.sv-flag-bad  { border-left-color: var(--data-red); color: var(--data-red); background: rgba(255, 107, 107, 0.07); }

.src-head-actions { display: flex; gap: 0.2rem; align-items: center; }
.btn-spec {
  width: 1.6rem; height: 1.6rem;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  background: rgba(116, 208, 255, 0.12);
  border: 1px solid rgba(116, 208, 255, 0.4);
  color: #74d0ff;
  border-radius: 4px;
  font-size: 0.85rem;
  cursor: pointer;
  line-height: 1;
}
.btn-spec:hover { background: rgba(116, 208, 255, 0.22); border-color: #74d0ff; }

/* Walkthrough mode — control-hints overlay on top-left of the 3D canvas. */
.walk-hint {
  position: absolute;
  top: 16px; left: 16px;
  background: rgba(20, 22, 28, 0.88);
  color: #dde;
  padding: 10px 14px;
  border-radius: 6px;
  font-size: 11px;
  line-height: 1.7;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 1px solid #3a4048;
  pointer-events: none;
  z-index: 5;
}
.walk-hint strong {
  font-size: 12px;
  color: #fff;
  letter-spacing: 0.05em;
  margin-bottom: 4px;
}
.walk-hint.hidden { display: none; }

/* ---------- Walk-mode touch controls (tablet / mobile) -------------
   Mobile-Legends-style HUD: virtual joystick bottom-left for move;
   action button cluster bottom-right for jump / run / crouch / sit;
   small rotate buttons beside the joystick for arrow-key turns. All
   buttons dispatch synthetic keyboard events into document so the
   ThirdPersonController handles them with no special path. Visible
   only when the walk-touch container's `hidden` attribute is removed
   by scene.js's setWalkthroughMode(true). */
.walk-touch {
  position: absolute;
  inset: 0;
  pointer-events: none;             /* gaps don't intercept camera drag */
  z-index: 8;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
.walk-touch[hidden] { display: none; }

/* Walk-mode interaction prompt — centered toast ("Press E to open …"). */
.walk-prompt {
  position: absolute;
  left: 50%;
  bottom: 20%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 8px 8px;
  background: rgba(15, 20, 28, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  pointer-events: none;
  z-index: 30;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.45);
}
.walk-prompt[hidden] { display: none; }
.walk-prompt-key {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  background: #fff;
  color: #15191f;
  border-radius: 6px;
  font-weight: 700;
  font-size: 13px;
  font-family: var(--font-mono, monospace);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}

.walk-joystick {
  pointer-events: auto;
  position: absolute;
  bottom: 32px;
  left: 32px;
  width: 140px;
  height: 140px;
  display: grid;
  place-items: center;
  touch-action: none;
}
.walk-joystick-base {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(40, 44, 52, 0.55) 0%, rgba(20, 22, 28, 0.40) 65%, rgba(20, 22, 28, 0.0) 100%);
  border: 2px solid rgba(192, 196, 208, 0.35);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.4);
}
.walk-joystick-nub {
  position: absolute;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: linear-gradient(180deg, #3b4250 0%, #232830 100%);
  border: 2px solid #c0c4d0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  transform: translate(0, 0);
  transition: transform 0.05s linear;
}
.walk-joystick.is-active .walk-joystick-nub { transition: none; }

.walk-rotate-buttons {
  pointer-events: auto;
  position: absolute;
  bottom: 30px;
  left: 196px;            /* sits to the right of the joystick */
  display: flex;
  gap: 8px;
}
.walk-rot-btn {
  width: 52px;
  height: 52px;
  font-size: 1.2rem;
  border-radius: 50%;
  border: 2px solid rgba(192, 196, 208, 0.5);
  background: rgba(20, 22, 28, 0.7);
  color: #c0c4d0;
  display: grid;
  place-items: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.walk-rot-btn.is-pressed { background: rgba(74, 163, 255, 0.4); border-color: #4aa3ff; }

.walk-actions {
  pointer-events: auto;
  position: absolute;
  bottom: 32px;
  right: 32px;
  display: grid;
  grid-template-columns: 80px 80px;
  gap: 10px;
  /* Above the SPL legend (z-index 10). The legend's bottom-right
     position overlapped the SIT button (bottom-right of this 2×2
     grid) and was hiding it. The legend can show through the gap
     between buttons; touching a button still works. */
  z-index: 12;
}
.walk-rotate-buttons { z-index: 12; }
.walk-btn {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  border: 2px solid rgba(192, 196, 208, 0.5);
  background: radial-gradient(circle, rgba(45, 52, 65, 0.85) 0%, rgba(20, 22, 28, 0.85) 80%);
  color: #ffffff;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  display: grid;
  place-items: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.walk-btn span { line-height: 1; }
.walk-btn.is-pressed {
  background: radial-gradient(circle, #4aa3ff 0%, #1f5faa 80%);
  border-color: #ffffff;
  transform: scale(0.96);
  box-shadow: 0 2px 6px rgba(74, 163, 255, 0.5);
}
.walk-btn-jump   { border-color: rgba(74, 163, 255, 0.7); }
.walk-btn-run    { border-color: rgba(255, 200, 70, 0.7); }
.walk-btn-crouch { border-color: rgba(120, 220, 120, 0.7); }
.walk-btn-sit    { border-color: rgba(200, 130, 240, 0.7); }

/* Hide the touch HUD on coarse-pointer-OFF (desktop) UNLESS the user is
   in a fullscreen viewport on a small screen — keeps desktop clean. */
@media (hover: hover) and (pointer: fine) {
  .walk-touch:not(.force-show) {
    /* desktop: still visible but slightly translucent so it's clearly
       a "touch HUD" and doesn't dominate the keyboard-driven view. */
    opacity: 0.55;
  }
  .walk-touch:hover { opacity: 1; }
}

/* Walkthrough live SPL readout — top-right of the 3D canvas, updates every
   frame with the avatar's ear position + current pose + total SPL. */
.walk-spl {
  /* P10 — sit below the corner buttons (which now sit at top: 56
     after the glass header bumped). Top: 104 = corner btn top (56)
     + height (40) + 8 gap. */
  position: absolute;
  top: 104px; right: 16px;
  background: rgba(20, 22, 28, 0.65);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-pop);
  border-radius: var(--radius-lg);
  padding: 10px 14px;
  color: var(--fg);
  font-size: 11px;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  z-index: 6;
  min-width: 170px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.walk-spl.hidden { display: none; }
.walk-spl .walk-spl-big {
  font-size: 1.9rem;
  font-weight: 300;
  letter-spacing: -0.01em;
  color: var(--fg-strong);
  line-height: 1;
  margin-bottom: 4px;
}
.walk-spl .walk-spl-sti {
  display: grid;
  grid-template-columns: 38px 1fr;
  gap: 8px;
  align-items: baseline;
  padding: 3px 0 5px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  margin-bottom: 2px;
}
.walk-spl .walk-spl-sti .walk-spl-val {
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.02em;
}
.walk-spl .sti-excellent { color: var(--data-green); }
.walk-spl .sti-good      { color: var(--data-green); }
.walk-spl .sti-fair      { color: var(--data-amber); }
.walk-spl .sti-poor      { color: #ff8c3c; }
.walk-spl .sti-bad       { color: var(--data-red); }
.walk-spl .walk-spl-row {
  display: grid;
  grid-template-columns: 38px 1fr;
  gap: 8px;
  align-items: baseline;
}
.walk-spl .walk-spl-label {
  color: var(--muted-dim);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.walk-spl .walk-spl-val {
  color: var(--fg);
  font-size: 11px;
}

/* Probe tool hover readout — floats next to the mouse over the 3D canvas. */
.probe-tooltip {
  position: absolute;
  pointer-events: none;
  z-index: 12;
  background: rgba(16, 18, 24, 0.92);
  border: 1px solid var(--accent);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.6);
  border-radius: var(--radius-lg);
  padding: 7px 10px 8px;
  color: var(--fg);
  font-size: 11px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 130px;
}
.probe-tooltip.hidden { display: none; }
.probe-tooltip .probe-spl {
  font-size: 1.25rem;
  font-weight: 300;
  letter-spacing: -0.01em;
  color: var(--fg-strong);
  margin-bottom: 2px;
}
.probe-tooltip .probe-xyz {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
  color: var(--muted);
  font-size: 10px;
}
.probe-tooltip .probe-note {
  margin-top: 2px;
  color: var(--muted-dim);
  font-size: 9.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.probe-tooltip .probe-fr-wrap {
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}
.probe-tooltip .probe-fr-chart {
  display: block;
  width: 200px;
  height: 90px;
  background: rgba(8, 12, 18, 0.55);
  border: 1px solid rgba(90, 122, 176, 0.25);
  border-radius: 3px;
}
.probe-tooltip .probe-fr-label {
  margin-top: 3px;
  color: var(--muted-dim);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.viewport-view {
  position: relative;
  overflow: hidden;
  /* P8 — viewport-view fills the whole viewport now that the toolbar
     row is gone. Without explicit height the canvas inside collapses. */
  height: 100%;
}
.viewport-view[hidden] { display: none; }
#view-3d { background: #0e1116; position: relative; }
#view-3d canvas { display: block; }

/* Vertical SPL/STI legend overlay anchored to the bottom-right of the 3D
   viewport. Sits inboard so it doesn't visually collide with the right
   Results panel border. Has three vertical sections:
   1) title (SPL / STI)
   2) gradient bar with absolute-positioned tick marks + value labels
   3) data-range footer showing the current heatmap min..max
   The fixed-position ticks are the "scale" — they communicate the absolute
   rating bands (STI: IEC boundaries 0.30/0.45/0.60/0.75) or numeric grid
   (SPL: 5/10 dB ticks across the data range), independent of the data span. */
.spl-legend-3d {
  position: absolute;
  bottom: 24px;
  right: 28px;
  background: rgba(20, 22, 28, 0.85);
  padding: 10px 10px 8px;
  border-radius: 6px;
  color: #ccd;
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 11px;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  min-width: 64px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  pointer-events: none;
  user-select: none;
}
.spl-legend-3d.hidden { display: none; }

/* Walk-mode: move the colour-ramp reference legend to the top-right
   (below the live SPL/STI readout) so it doesn't sit underneath the
   RUN / SIT touch buttons. The buttons are partly translucent and the
   legend's gradient was bleeding through. Live SPL + colour reference
   stay vertically stacked at top-right; touch HUD owns bottom-right. */
html.is-walkmode .spl-legend-3d {
  top: 324px;          /* walk-spl now at 104 + ~200 tall + 20 gap */
  bottom: auto;
  right: 16px;
}
.spl-legend-3d .legend-title {
  font-weight: 700;
  font-size: 10px;
  color: #aab;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
}
.spl-legend-3d .legend-scale {
  position: relative;
  display: flex;
  align-items: stretch;
  height: 180px;
}
.spl-legend-3d .legend-bar {
  width: 14px;
  height: 100%;
  border-radius: 3px;
  /* Background image is set inline by JS based on metric (SPL ramp vs
     STI IEC palette). */
  border: 1px solid rgba(255, 255, 255, 0.2);
  position: relative;
}
/* Data-extent bracket (Phase 11a, 2026-05-25).
   The colour ramp is fixed-domain; the bar caption ends are the ramp
   ends. This translucent band marks the actual data extent INSIDE the
   bar so the reader can see where the values actually live without
   confusing the colour-to-value mapping. */
.spl-legend-3d .legend-bar .legend-data-bracket {
  position: absolute;
  left: -2px;
  right: -2px;
  border: 1px solid rgba(255, 255, 255, 0.65);
  border-radius: 2px;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}
.spl-legend-3d .legend-data-caption {
  font-size: 9px;
  color: #8a8f99;
  font-variant-numeric: tabular-nums;
  text-align: center;
  margin-top: 1px;
  letter-spacing: 0.02em;
}
.spl-legend-3d .legend-ticks {
  position: relative;
  width: 36px;
  margin-left: 4px;
}
.spl-legend-3d .legend-tick {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  transform: translateY(50%);   /* anchor on the value line */
  pointer-events: none;
}
.spl-legend-3d .legend-tick-line {
  width: 6px;
  height: 1px;
  background: #ccd;
  opacity: 0.85;
  flex: 0 0 auto;
}
/* Minor (unlabeled) graduation — shorter line, lower contrast, no
   label. Renders as ruler-style sub-ticks between the labeled ones. */
.spl-legend-3d .legend-tick.minor .legend-tick-line {
  width: 3px;
  opacity: 0.40;
}
.spl-legend-3d .legend-tick-label {
  margin-left: 4px;
  padding: 1px 4px;
  font-size: 10px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #dde;
  background: rgba(20, 22, 28, 0.78);
  border-radius: 3px;
  line-height: 1.2;
  white-space: nowrap;
}
.spl-legend-3d .legend-range {
  display: flex;
  justify-content: center;
  gap: 4px;
  font-size: 10px;
  color: #8a8f99;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.spl-legend-3d .legend-sep { opacity: 0.6; }

section h2 {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin: 1.25rem 0 0.4rem;
  padding-bottom: 0.3rem;
  color: var(--muted);
  font-weight: 600;
  border-bottom: 1px solid var(--border);
}
section:first-of-type h2 { margin-top: 0.3rem; }

/* Collapsible sidebar sections. The whole <h2> is the click target;
   chevron rendered via ::before so it doesn't get wiped by the panel's
   own innerHTML re-renders. Collapsed state hides the body via display
   none — variable-height bodies (Sources can be 80 px or 1500 px) make
   max-height transitions either snap or feel janky, so we skip the
   animation. The chevron rotation IS the affordance. */
.panel-collapsible {
  margin-bottom: 4px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.panel-collapsible:last-of-type { border-bottom: 0; padding-bottom: 0; }
.panel-collapsible h2 { border-bottom: 0; }
.panel-collapsible h2.panel-toggle-h2 {
  display: flex;
  align-items: center;
  gap: 6px;
  user-select: none;
  border-radius: 3px;
  padding: 0.3rem 0.3rem;
  margin: 0.5rem 0 0.4rem;
  transition: background-color 120ms ease-out;
}
.panel-collapsible h2.panel-toggle-h2::before {
  content: '▾';
  font-size: 0.65rem;
  width: 10px;
  color: var(--muted);
  transition: transform 150ms ease-out;
}
.panel-collapsible.collapsed h2.panel-toggle-h2::before { transform: rotate(-90deg); }
.panel-collapsible h2.panel-toggle-h2:hover { background: var(--bg-elev); color: var(--fg); }
.panel-collapsible h2.panel-toggle-h2:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: -1px;
}
.panel-collapsible.collapsed .panel-body { display: none; }
.panel-body { /* default expanded layout — nothing to do */ }
section h3 {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 1rem 0 0.35rem;
  color: var(--muted);
  font-weight: 600;
}

/* Surface-group headings inside the Surfaces section (Floor / Walls /
   Ceiling). Direct children of #surface-materials only — the
   enclosure / surau / shared-wall section headers wrap their h4 in
   .enclosure-section-header, so they're untouched. Each surface's
   controls sit together under one of these; never interleaved. The
   left accent rule + tighter weight reads as a group label, one notch
   below the uppercase <h3> eyebrow above. */
#surface-materials > h4 {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--fg);
  margin: 0.85rem 0 0.4rem;
  padding-left: 0.5rem;
  border-left: 2px solid var(--accent, #4a9eff);
}
#surface-materials > h4:first-child { margin-top: 0.3rem; }

.field-group { display: grid; gap: 0.45rem; }
.field-group label {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 0.35rem;
  align-items: center;
  font-size: 0.82rem;
  color: var(--fg);
}
.field-group .unit { color: var(--muted); font-size: 0.72rem; font-variant-numeric: tabular-nums; }

/* Outdoor field panel (Phase 5). Toggle + field-size + ISO 9613-1 T/RH. */
.outdoor-toggle {
  display: flex !important;
  align-items: center;
  gap: 0.5rem;
  grid-template-columns: none !important;
  cursor: pointer;
  font-weight: 600;
}
.outdoor-toggle input { width: auto; }
.outdoor-lede {
  margin: 0;
  line-height: 1.45;
  color: var(--muted);
  font-size: 0.72rem;
}

/* Field controls dim when outdoor is off. The inputs themselves carry the
   real `disabled` attribute (set in JS) — that, not pointer-events, is what
   takes them out of the tab order and tells AT they're inert. Keep them
   visible (no pointer-events:none) so a screen reader can still announce
   "disabled" on focus traversal. */
#outdoor-controls { margin-top: 0.85rem; display: grid; gap: 0.85rem; }
#outdoor-controls.is-disabled { opacity: 0.5; }
#outdoor-controls input:disabled { cursor: not-allowed; color: var(--muted-dim); }

.outdoor-size-group { gap: 0.4rem; }
.outdoor-size-range { width: 100%; accent-color: var(--accent); cursor: pointer; }
.outdoor-size-range:disabled { cursor: not-allowed; }
.outdoor-size-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
}
#outdoor-size-readout {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.outdoor-airrow { gap: 0.45rem; }
.outdoor-section-label {
  font-size: 0.62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.outdoor-air-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
.outdoor-air-pair label { grid-template-columns: 1fr; gap: 0.25rem; align-items: start; }
.outdoor-inline { display: inline-flex; align-items: center; gap: 0.3rem; }
.outdoor-inline input { width: 3.6rem; }
.outdoor-air-hint { margin: 0; line-height: 1.4; color: var(--muted); font-size: 0.7rem; }

.outdoor-note {
  margin: 0;
  padding: 0.5rem 0.6rem;
  line-height: 1.5;
  color: var(--muted);
  font-size: 0.72rem;
  background: rgba(255, 255, 255, 0.03);
  border-left: 3px solid var(--data-amber);
  border-radius: 0 2px 2px 0;
}
.outdoor-note-head {
  display: block;
  font-weight: 600;
  color: var(--data-amber);
  margin-bottom: 0.2rem;
}

/* Fit-field row (Phase 11b). Explicit camera affordance — enabling outdoor
   no longer auto-snaps the camera, so this button is how the user opts in
   to seeing the whole field at once. Hinted accent on first-of-session
   enable clears on click or after 6 s. Modelled on .btn-show-welcome
   (small, low-weight, accent outline). */
.outdoor-fit-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35rem;
}
.btn-outdoor-fit {
  display: inline-block;
  padding: 5px 11px;
  background: rgba(116, 208, 255, 0.10);
  border: 1px solid rgba(116, 208, 255, 0.35);
  border-radius: 4px;
  color: #74d0ff;
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
}
.btn-outdoor-fit:hover {
  background: rgba(116, 208, 255, 0.22);
  border-color: #74d0ff;
}
.btn-outdoor-fit:active {
  background: rgba(116, 208, 255, 0.30);
}
.btn-outdoor-fit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.outdoor-fit-hint {
  margin: 0;
  line-height: 1.4;
  color: var(--muted);
  font-size: 0.7rem;
}
/* First-of-session pulse — soft accent ring + gentle ring beat so the
   button reads as "the thing to click" without becoming a distraction.
   Honours prefers-reduced-motion (animation suppressed; outline stays). */
.outdoor-fit-row-hinted .btn-outdoor-fit {
  border-color: #74d0ff;
  box-shadow: 0 0 0 3px rgba(116, 208, 255, 0.18);
  animation: outdoor-fit-pulse 1.6s ease-in-out 2;
}
@keyframes outdoor-fit-pulse {
  0%, 100% { box-shadow: 0 0 0 3px rgba(116, 208, 255, 0.18); }
  50%      { box-shadow: 0 0 0 6px rgba(116, 208, 255, 0.10); }
}
@media (prefers-reduced-motion: reduce) {
  .outdoor-fit-row-hinted .btn-outdoor-fit { animation: none; }
}

.field-group .rated-cap {
  color: var(--muted);
  font-size: 0.68rem;
  font-style: italic;
  margin-left: 0.35rem;
  white-space: nowrap;
}
.field-group input,
.field-group select {
  padding: 0.32rem 0.45rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
  background: var(--bg-elev);
  color: var(--fg);
  min-width: 0;          /* allow shrink below intrinsic <select> width */
}
/* Model / Group rows: the label grid is `1fr auto auto`, which sizes a
   <select> to its widest option (long speaker-model names, "Group A") and
   pushes it off the panel's right edge. For any label that holds a select,
   give the select a flexible, shrinkable column so the dropdown fills the
   row and clips instead of overflowing. (Labels with inputs + units keep the
   default grid since `:has` only matches select-bearing labels.) */
.field-group label:has(select) {
  grid-template-columns: auto minmax(0, 1fr);
}
.field-group label > select {
  width: 100%;
}
.field-group input:focus,
.field-group select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.18);
}

/* Active room name — eyebrow + tinted card. Anchors the panel:
   answers "which room am I in?" before any picker is touched.
   Spacing matches .picker-row (0.4rem) so the gap to PRESETS
   matches the PRESETS→TEMPLATES gap. Maya 2026-05-18. */
.field-group.room-name-row {
  position: relative;
  margin-bottom: 0.4rem;
  padding: 0.5rem 0.6rem 0.55rem 0.75rem;
  background: linear-gradient(
    180deg,
    rgba(74, 163, 255, 0.10) 0%,
    rgba(74, 163, 255, 0.04) 100%
  );
  border: 1px solid rgba(74, 163, 255, 0.22);
  border-left: 2px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: 0 0 0 1px rgba(74, 163, 255, 0.04) inset;
}
.field-group.room-name-row .room-eyebrow {
  display: block;
  font-size: 0.62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  color: var(--accent);
  margin-bottom: 0.3rem;
}
.field-group.room-name-row label {
  grid-template-columns: 1fr;
  gap: 0.25rem;
  font-size: 0.7rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.field-group.room-name-row input {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--fg-strong, #e5e7eb);
  background: rgba(12, 16, 22, 0.55);
  border-color: rgba(74, 163, 255, 0.28);
}
.field-group.room-name-row input:focus {
  background: var(--bg-elev);
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.22);
}
.field-group.room-name-row input::placeholder {
  color: var(--muted);
  font-weight: 400;
}

/* Author's note — per-room engineer commentary that prints on the
   report cover. Sits at the bottom of the Room panel after Surface
   materials. Textarea + live char counter, 240-char hard cap.
   Maya 2026-05-20. */
.field-group.author-note-row {
  gap: 0.3rem;
}
.field-group.author-note-row .author-note-label {
  grid-template-columns: 1fr;
  gap: 0.3rem;
  font-size: 0.7rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.author-note-textarea {
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.82rem;
  line-height: 1.45;
  background: var(--bg-elev);
  color: var(--fg);
  resize: vertical;
  min-height: 4.2rem;
  max-height: 9rem;
  width: 100%;
  box-sizing: border-box;
}
.author-note-textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.18);
}
.author-note-textarea::placeholder {
  color: var(--muted);
}
.author-note-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 0.68rem;
  color: var(--muted);
}
.author-note-hint {
  font-style: italic;
}
.author-note-counter {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.author-note-counter.is-warn {
  color: #d98a26;          /* amber — within 20 of cap */
  font-weight: 600;
}
.author-note-counter.is-over {
  color: #d44a3a;          /* red — at cap */
  font-weight: 600;
}

.preset-row { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.preset-row button,
.picker-buttons button {
  padding: 0.32rem 0.6rem;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--muted);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.74rem;
  font-family: inherit;
  letter-spacing: 0.02em;
}
.preset-row button:hover,
.picker-buttons button:hover {
  color: var(--fg-strong);
  background: var(--border);
  border-color: var(--border-strong);
}

/* Custom-draw entry button — accent treatment so the "draw your own
   room" entry point reads as a deliberate action, not a third class
   of preset / template. */
.btn-custom-draw {
  padding: 0.32rem 0.7rem;
  border: 1px dashed rgba(116, 208, 255, 0.5);
  background: rgba(116, 208, 255, 0.08);
  color: var(--data-cyan, #74d0ff);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.74rem;
  font-family: inherit;
  letter-spacing: 0.02em;
}
.btn-custom-draw:hover {
  background: rgba(116, 208, 255, 0.18);
  border-color: var(--data-cyan, #74d0ff);
  color: #cfe7ff;
}

/* Saved-custom-room chips — wrap-flow row alongside the Draw button.
   Each chip is `<label> <×>`. Active chip gets the accent fill so the
   user knows which saved room they're currently editing. */
.custom-saved-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  width: 100%;
  margin-top: 0.25rem;
}
.custom-saved-row:empty { display: none; }
.custom-chip {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border);
  background: var(--bg-elev);
  border-radius: var(--radius);
  overflow: hidden;
  font-size: 0.72rem;
  letter-spacing: 0.01em;
}
.custom-chip:hover { border-color: #4a515b; }
.custom-chip.active {
  border-color: var(--accent);
  background: rgba(74, 163, 255, 0.16);
}
.custom-chip-load {
  background: transparent;
  border: 0;
  color: var(--fg-strong, #e5e7eb);
  padding: 0.28rem 0.55rem;
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.custom-chip-delete {
  background: transparent;
  border: 0;
  border-left: 1px solid var(--border);
  color: var(--muted);
  padding: 0 0.45rem;
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  line-height: 1;
}
.custom-chip-delete:hover {
  background: rgba(229, 68, 68, 0.18);
  color: #fca5a5;
}

/* ── Saved Rooms library tree (below the Custom row) ──────────────────────
   Maya spec 2026-06-14. Replaces the saved-room dropdown + project-filtered
   recents chips with ONE full-library projects→rooms tree. Reads the whole
   library (never filtered by the active project). The ACTIVE room (the one
   💾 Save overwrites) is marked four ways — ● glyph, cyan left-border, bold
   name, tinted row — so it survives a dim display. Extends the .custom-chip
   accent language + the --data-cyan / --border / --bg-elev tokens. */
.saved-rooms-tree {
  width: 100%;
  margin-top: 0.4rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev);
  overflow: hidden;
}
.srt-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 0.5rem;
  padding: 0.35rem 0.55rem;
  border-bottom: 1px solid var(--border);
}
.srt-title {
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--muted);
}
.srt-count { font-size: 0.62rem; color: var(--muted); white-space: nowrap; }
.srt-empty {
  padding: 0.55rem; font-size: 0.72rem; color: var(--muted); line-height: 1.55;
}
.srt-body { max-height: 40vh; overflow-y: auto; }
.srt-body::-webkit-scrollbar { width: 8px; }
.srt-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* Project group header (collapsible) */
.srt-proj-head {
  display: flex; align-items: center; gap: 0.3rem;
  height: 32px; padding: 0 0.45rem; cursor: pointer; user-select: none;
}
.srt-proj-head:hover { background: rgba(255, 255, 255, 0.03); }
.srt-proj-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.srt-chevron {
  flex: none; width: 12px; font-size: 0.7rem; color: var(--muted);
  transition: transform 150ms ease-out;
}
.srt-project.collapsed .srt-chevron { transform: rotate(-90deg); }
.srt-proj-name {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.74rem; font-weight: 600; color: var(--fg-strong, #e5e7eb);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.srt-proj-name.unfiled { color: var(--muted); font-style: italic; font-weight: 500; }
.srt-proj-count { flex: none; font-size: 0.66rem; color: var(--muted); }
.srt-proj-menu {
  flex: none; background: transparent; border: 0; color: var(--muted);
  font-size: 0.9rem; line-height: 1; padding: 0 0.25rem; cursor: pointer;
  opacity: 0; transition: opacity 120ms;
}
.srt-proj-head:hover .srt-proj-menu,
.srt-proj-head:focus-within .srt-proj-menu { opacity: 1; }
.srt-proj-menu:hover { color: var(--fg-strong, #fff); }

/* Rooms list (collapse) */
.srt-project.collapsed .srt-rooms { display: none; }

/* Room row */
.srt-room {
  display: flex; align-items: center; gap: 0.2rem;
  height: 28px; padding: 0 0.4rem 0 0.5rem;
  margin-left: 22px;                 /* indent under the project name, not the chevron */
  border-left: 2px solid transparent;
  cursor: pointer; user-select: none;
}
.srt-room:hover { background: rgba(255, 255, 255, 0.03); }
.srt-room:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.srt-room.active {
  border-left-color: var(--data-cyan, #74d0ff);
  background: rgba(116, 208, 255, 0.08);
}
.srt-glyph {
  flex: none; width: 16px; text-align: center;
  font-size: 0.7rem; color: var(--data-cyan, #74d0ff);
}
.srt-room.dirty .srt-glyph { color: var(--data-amber, #ffb454); }
.srt-room-name {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.72rem; color: var(--fg, #e6edf3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.srt-room.active .srt-room-name { color: var(--fg-strong, #fff); }
.srt-dirty { flex: none; color: var(--data-amber, #ffb454); font-weight: 700; }
.srt-room-del {
  flex: none; background: transparent; border: 0; color: var(--muted);
  font-size: 0.95rem; line-height: 1; padding: 0 0.3rem; cursor: pointer;
  opacity: 0; transition: opacity 120ms;
}
.srt-room:hover .srt-room-del,
.srt-room:focus-within .srt-room-del { opacity: 1; }
.srt-room-del:hover { color: #fca5a5; }

/* Inline project rename */
.srt-rename-input {
  flex: 1 1 auto; min-width: 0;
  font: inherit; font-size: 0.74rem; font-weight: 600;
  color: var(--fg-strong, #fff);
  background: var(--bg, #11151b);
  border: 1px solid var(--accent);
  border-radius: 3px; padding: 1px 4px;
}

/* Project ⋯ popover menu (anchored, body-level) */
.srt-menu {
  position: fixed; z-index: 1000;
  min-width: 170px;
  background: var(--bg-elev, #1a1f26);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.42);
  padding: 4px;
}
.srt-menu-item {
  display: block; width: 100%; text-align: left;
  background: transparent; border: 0; color: var(--fg, #e6edf3);
  font: inherit; font-size: 0.74rem; padding: 0.4rem 0.5rem;
  border-radius: 4px; cursor: pointer;
}
.srt-menu-item:hover { background: rgba(255, 255, 255, 0.06); }
.srt-menu-danger { color: #f0a0a0; }
.srt-menu-danger:hover { background: rgba(229, 68, 68, 0.16); color: #fca5a5; }

/* Touch: never hide an affordance behind hover on a finger */
@media (hover: none) {
  .srt-proj-menu, .srt-room-del { opacity: 0.6; }
}

/* ── Admin read-only scene viewer (R3) — Maya spec 2026-06-14 ─────────────
   AccountLAB "Saved work" list + the read-only MODE banner + the editing-rail
   lock. Amber (--data-amber) = "borrowed context, careful" — deliberately NOT
   error-red (the admin is doing a legitimate thing). */

/* Surface 1 — AccountLAB saved-work list (inside the expanded detail cell) */
.al-saved {
  margin-top: var(--space-3);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}
.al-saved-proj-head {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
  color: var(--muted); text-transform: uppercase;
  margin: var(--space-3) 0 var(--space-1);
}
.al-saved-proj-head.unfiled { font-style: italic; text-transform: none; letter-spacing: 0; }
.al-saved-room {
  display: grid;
  grid-template-columns: 18px minmax(120px, 1.6fr) minmax(150px, 1.4fr) 92px 64px;
  align-items: center; gap: var(--space-3);
  height: 34px; padding: 0 var(--space-2);
  border-bottom: 1px solid var(--border);
}
.al-saved-name {
  color: var(--fg-strong, #e5e7eb); font-size: 0.82rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.al-saved-meta {
  color: var(--muted); font-size: 0.74rem;
  font-family: var(--mono, ui-monospace, monospace);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.al-saved-date { color: var(--muted); font-size: 0.74rem; }
.al-saved-view {
  font: 600 0.72rem/1 inherit; padding: 5px 12px;
  border: 1px solid var(--accent); color: var(--accent);
  background: transparent; border-radius: var(--radius); cursor: pointer;
}
.al-saved-view:hover { background: rgba(74, 163, 255, 0.1); }
.al-saved-view:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.srt-error { color: var(--data-amber, #ffb454); }
/* In-place "reload this user's saved work" — every expand re-fetches, this is
   the manual nudge so the admin can re-pull without collapsing the row. */
.al-saved-refresh {
  margin-left: auto; flex: none;
  background: transparent; border: 1px solid var(--border); color: var(--muted);
  font-size: 0.82rem; line-height: 1; padding: 2px 8px;
  border-radius: var(--radius); cursor: pointer;
}
.al-saved-refresh:hover { color: var(--fg-strong, #fff); border-color: var(--accent); }
.al-saved-refresh:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── AccountLAB activity sparkline (P2) + admin actions (P4) — Maya spec ──── */
.al-activity, .al-admin {
  margin-top: var(--space-3);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}
.al-spark-wrap { margin: 6px 0 3px; }
.al-spark { display: block; width: 100%; height: auto; }
.al-spark-axis { display: flex; justify-content: space-between; font-size: 0.62rem; color: var(--muted); }

.al-admin-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin: 4px 0; }
.al-admin-plan { display: inline-flex; align-items: center; gap: 8px; font-size: 0.74rem; color: var(--muted); }
.al-admin-plan-select {
  font: inherit; font-size: 0.78rem; background: var(--bg-elev); color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--radius); padding: 3px 8px; cursor: pointer;
}
.al-admin-planro { font-size: 0.76rem; color: var(--muted); }
.al-admin-apply, .al-admin-suspend, .al-admin-resume {
  font: 600 0.72rem/1 inherit; padding: 6px 12px; border-radius: var(--radius);
  cursor: pointer; background: transparent; border: 1px solid var(--border); color: var(--fg);
}
.al-admin-apply { border-color: var(--accent); color: var(--accent); }
.al-admin-apply:hover { background: rgba(74, 163, 255, 0.1); }
.al-admin-suspend { margin-left: auto; border-color: var(--data-amber, #ffb454); color: var(--data-amber, #ffb454); }
.al-admin-suspend:hover { background: rgba(255, 180, 84, 0.12); }
.al-admin-resume { margin-left: auto; border-color: var(--data-green, #a3d977); color: var(--data-green, #a3d977); }
.al-admin-resume:hover { background: rgba(163, 217, 119, 0.12); }
.al-admin-bar[aria-busy="true"] button, .al-admin-bar[aria-busy="true"] select { opacity: 0.6; cursor: progress; }
.al-admin-apply:focus-visible, .al-admin-suspend:focus-visible, .al-admin-resume:focus-visible,
.al-admin-plan-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.al-admin-self { font-size: 0.76rem; color: var(--muted); font-style: italic; }
.al-admin-msg { font-size: 0.74rem; margin: 4px 0 0; }
.al-admin-msg[hidden] { display: none; }
.al-admin-msg-ok { color: var(--data-green, #a3d977); }
.al-admin-msg-err { color: #fca5a5; }
.al-adminlog-row {
  display: grid; grid-template-columns: 16px 1fr auto; align-items: center; gap: 8px;
  height: 26px; font-size: 0.72rem; color: var(--muted); border-bottom: 1px solid var(--border);
}
.al-adminlog-text { color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Surface 2 — read-only mode banner. Absolute strip across the very top of the
   viewport. It sits at the very top (height 44px = the app header's height) and
   stacks ABOVE the fixed glass header (#app-header is z-index:30) so it cleanly
   REPLACES the top bar while viewing — otherwise the header (Save/Print/user)
   overlaps and intercepts clicks on the Exit button (user-reported). The
   floating 2D/3D/Walk + camera-cube clusters start at top:56px, so they clear
   the 44px bar and stay usable for inspection. Opaque so the header behind it
   doesn't bleed through. */
.ro-banner {
  position: absolute; top: 0; left: 0; right: 0; z-index: 40;
  display: flex; align-items: center; gap: var(--space-3);
  height: 44px; padding: 0 var(--space-3);
  background: rgba(38, 30, 16, 0.98);
  border-bottom: 1px solid var(--data-amber, #ffb454);
  color: var(--fg-strong, #fff); font-size: 0.8rem;
}
.ro-tag {
  flex: none; font-weight: 700; font-size: 0.7rem; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--data-amber, #ffb454);
  border-left: 3px solid var(--data-amber, #ffb454); padding-left: 0.5rem;
}
.ro-who { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg); }
.ro-email { color: var(--fg-strong, #fff); }
.ro-note { flex: none; color: var(--muted); font-size: 0.74rem; }
.ro-exit {
  margin-left: auto; flex: none;
  background: var(--data-amber, #ffb454); color: #1a1206;
  font-weight: 600; font-size: 0.76rem;
  border: 0; border-radius: var(--radius); padding: 5px 14px; cursor: pointer;
}
.ro-exit:hover { background: #ffc06e; }
.ro-exit:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (max-width: 640px) { .ro-note { display: none; } }

/* Surface 3 — editing-rail lock. `inert` (set in JS) does the enforcement;
   this is the VISUAL "intentionally locked" cue. No position change — the rail
   panel is position:fixed and must stay that way. */
.ro-locked {
  filter: saturate(0.55) brightness(0.9);
  opacity: 0.7;
  border-left: 2px solid var(--data-amber, #ffb454);
  cursor: not-allowed;
}

/* Picker row: small left-aligned label ("Presets" / "Templates") + a
   wrap-flow row of buttons. Used in #panel-room to split the unified
   picker into signature-Presets vs parametric-Templates. */
.picker-row {
  display: grid;
  grid-template-columns: 70px 1fr;
  gap: 0.4rem;
  align-items: start;
  margin-bottom: 0.4rem;
}
.picker-label {
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  padding-top: 0.4rem;
  cursor: help;
}
.picker-buttons { display: flex; gap: 0.3rem; flex-wrap: wrap; }

/* Modern dropdown for the Presets + Templates picker rows. Replaces
   the old wrap-flow button row (which got cluttered once the preset
   list grew past 4 entries). Native <select> with custom chrome:
   thin border, accent focus ring, dark theme background that matches
   the panel, and a chevron rendered via SVG data-URI so we don't
   ship the OS default arrow. */
.picker-dropdown {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 100%;
  padding: 0.38rem 1.8rem 0.38rem 0.6rem;
  border: 1px solid var(--border);
  background-color: var(--bg-elev);
  color: var(--fg-strong, #e5e7eb);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.78rem;
  font-family: inherit;
  letter-spacing: 0.02em;
  line-height: 1.3;
  /* Custom chevron arrow rendered as inline SVG so it matches the
     theme palette. Positioned right-side, vertically centred. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%2390969d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='3,4.5 6,7.5 9,4.5'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.55rem center;
  background-size: 0.75rem;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, color 0.12s ease;
}
.picker-dropdown:hover {
  border-color: var(--border-strong, #4a515b);
  color: var(--fg-strong, #e5e7eb);
}
.picker-dropdown:focus {
  outline: none;
  border-color: var(--accent, #4aa3ff);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.18);
}
.picker-dropdown:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
/* Native <option> styling in dark panels — keep the option background
   the same as the dropdown so the menu doesn't flash white in dark
   theme on Chromium / Firefox. The OS-native popup mostly ignores
   these on macOS but it's a no-op cost. */
.picker-dropdown option {
  background-color: var(--bg-elev, #1c2028);
  color: var(--fg-strong, #e5e7eb);
}
.picker-dropdown option:first-child {
  /* Placeholder "— Choose a ... —" row reads muted so it doesn't look
     like a real selection. */
  color: var(--muted);
  font-style: italic;
}

/* Save / Load / Share / Print live in the top header now (see
   #app-header .header-actions). The .room-head wrapper is gone
   from panel-room.js's template; this file kept the .room-head
   collapsible-h2 override below the panel-collapsible block. */

/* Transient toast — used for "link copied" / "loaded shared scene"
   acks where the import-status banner would be too sticky. Bottom-
   centre, fades in/out, click anywhere on the toast to dismiss. */
.rl-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  padding: 0.6rem 1.1rem;
  background: rgba(20, 24, 30, 0.96);
  color: var(--fg-strong, #e5e7eb);
  border: 1px solid var(--border-strong, #2a2f38);
  border-radius: 6px;
  font-size: 0.78rem;
  font-family: inherit;
  letter-spacing: 0.02em;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
  z-index: 10000;
  opacity: 0;
  transition: opacity 200ms ease-out, transform 200ms ease-out;
  cursor: default;
  white-space: pre-wrap;
}
.rl-toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.rl-toast-err {
  border-color: rgba(229, 68, 68, 0.5);
  color: #fca5a5;
}
/* Action-toast: a sticky toast with an inline CTA link. Used for the
   DeviceLAB → RoomLAB handoff after "Place in room" succeeds. */
.rl-toast-handoff {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.55rem 0.7rem 0.55rem 1.1rem;
}
.rl-toast-handoff .rl-toast-action {
  display: inline-block;
  padding: 0.35rem 0.75rem;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  border-radius: 4px;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-size: 0.78rem;
  white-space: nowrap;
  transition: background-color 120ms ease-out;
}
.rl-toast-handoff .rl-toast-action:hover {
  background: #2c6dba;
}

/* Persistent "Open / View in RoomLAB" button at the top of the
   DeviceLAB System overview column. Cheaper than a full toolbar —
   just a clear cross-Lab handoff link the user can find at any time. */
.rack-go-roomlab {
  display: block;
  text-align: center;
  padding: 0.5rem 0.7rem;
  margin-bottom: 0.55rem;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  border-radius: 5px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: background-color 120ms ease-out;
}
.rack-go-roomlab:hover { background: #2c6dba; }

.import-row { margin-bottom: 0.5rem; }
.btn-import {
  width: 100%;
  padding: 0.4rem 0.6rem;
  background: rgba(116, 208, 255, 0.08);
  border: 1px dashed rgba(116, 208, 255, 0.4);
  color: var(--data-cyan, #74d0ff);
  border-radius: var(--radius);
  font-size: 0.76rem;
  font-family: inherit;
  cursor: pointer;
  letter-spacing: 0.02em;
}
.btn-import:hover {
  background: rgba(116, 208, 255, 0.14);
  border-color: var(--data-cyan, #74d0ff);
}
.import-status {
  margin: 0 0 0.55rem 0;
  padding: 0.4rem 0.55rem;
  font-size: 0.72rem;
  line-height: 1.45;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--muted);
  border-left: 2px solid var(--border);
}
.import-status.ok {
  color: var(--data-green, #a3d977);
  border-left-color: var(--data-green, #a3d977);
  background: rgba(163, 217, 119, 0.08);
}
.import-status.err {
  color: var(--data-red, #ff6b6b);
  border-left-color: var(--data-red, #ff6b6b);
  background: rgba(255, 107, 107, 0.08);
}

.summary { padding: 0.35rem 0 0.6rem; }
.summary .big {
  font-size: 2.9rem;
  font-weight: 300;
  line-height: 1.0;
  letter-spacing: -0.015em;
  color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
}
.summary .big .unit { font-size: 1rem; font-weight: 300; color: var(--muted); margin-left: 0.25rem; }
.summary .sub { color: var(--muted); font-size: 0.74rem; margin-top: 0.2rem; }
.summary .sub.meta { margin-top: 0.5rem; font-variant-numeric: tabular-nums; }
.summary .rating {
  margin-top: 0.6rem;
  font-size: 0.78rem;
  padding: 0.35rem 0.6rem;
  border-radius: 0 var(--radius) var(--radius) 0;
  border-left: 3px solid currentColor;
  display: inline-block;
  font-weight: 500;
}

.good { color: var(--data-green); }
.ok   { color: var(--data-amber); }
.warn { color: #ff8c3c; }
.bad  { color: var(--data-red); }
.rating.good { background: rgba(163, 217, 119, 0.12); }
.rating.ok   { background: rgba(255, 180, 84, 0.12); }
.rating.warn { background: rgba(255, 140, 60, 0.14); }
.rating.bad  { background: rgba(255, 107, 107, 0.14); }

#rt60-table,
#spl-table,
#zones-table,
.lr-breakdown {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
  margin-top: 0.3rem;
}
#rt60-table td, #spl-table td, #zones-table td, .lr-breakdown td,
.summary .big, .listener-results .big-num {
  font-variant-numeric: tabular-nums;
}
#rt60-table th, #rt60-table td { padding: 0.3rem 0.35rem; text-align: right; border-bottom: 1px solid var(--border); }
#rt60-table th:first-child, #rt60-table td:first-child { text-align: left; }
#rt60-table th, #spl-table th, #zones-table th {
  font-weight: 600;
  color: var(--muted);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--border-strong);
  padding: 0.35rem 0.4rem;
}
#rt60-table tr:hover td,
#spl-table tr:hover td,
#zones-table tr:hover td { background: rgba(255, 255, 255, 0.03); }

.precision-cell {
  color: var(--data-cyan, #74d0ff);
  font-weight: 500;
}
.precision-cell.stale,
#rt60-table th.stale { color: var(--muted); opacity: 0.6; }
.precision-cmp {
  margin-top: 0.5rem;
  padding: 0.4rem 0.55rem;
  font-size: 0.78rem;
  color: var(--data-cyan, #74d0ff);
  background: rgba(116, 208, 255, 0.08);
  border-left: 2px solid var(--data-cyan, #74d0ff);
  border-radius: 2px;
}
.precision-cmp.stale {
  color: var(--muted);
  background: rgba(255, 255, 255, 0.03);
  border-left-color: var(--muted);
}
.precision-cmp em { font-style: italic; color: var(--data-amber); }

.ambient-summary {
  margin: 0.5rem 0;
  padding: 0.55rem 0.7rem;
  background: rgba(255, 255, 255, 0.03);
  border-left: 3px solid var(--border-strong);
  border-radius: 2px;
}
.ambient-dba {
  font-size: 1.45rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.ambient-dba .unit { font-size: 0.75rem; font-weight: 400; color: var(--muted); }
.ambient-desc {
  margin-top: 0.4rem;
  font-size: 0.72rem;
  color: var(--muted);
  line-height: 1.45;
  font-style: italic;
}
#panel-ambient details { margin-top: 0.35rem; }
#panel-ambient summary {
  cursor: pointer;
  font-size: 0.76rem;
  color: var(--muted);
  padding: 0.25rem 0;
  user-select: none;
}
.ambient-bands {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.35rem 0.5rem;
  margin-top: 0.4rem;
  padding: 0.45rem;
  background: rgba(0, 0, 0, 0.18);
  border-radius: 4px;
}
.ambient-band {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.76rem;
}
.ambient-band-hz {
  min-width: 2.3rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.ambient-band input {
  width: 3.2rem;
  padding: 0.18rem 0.25rem;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

.hint {
  margin-top: 1rem;
  font-size: 0.72rem;
  color: var(--muted);
  line-height: 1.55;
  padding: 0.6rem 0.75rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

/* P8 — 2D layout uses a grid with svg in the centre and the SPL/STI
   legend in a fixed right column. Bottom of the viewport is freed up
   for the plan to fill vertically. Padding clears the floating
   chrome on every edge: top — 2D/3D/Walk segmented control + corner
   buttons, sides — left + right floating rail icons. */
.viewport-2d {
  width: 100%;
  height: 100%;
  padding: 104px 60px 0.6rem;       /* top — clear glass header + mode segment */
  /* relative so the .vp-north-arrow absolute child anchors here */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "header header"
    "svg    legend"
    "note   note";
  gap: 0.4rem 1rem;
  color: #ccd;
}
.vp-header {
  grid-area: header;
  text-align: center;
  color: #dde;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}
.viewport-2d > svg {
  grid-area: svg;
  width: 100%;
  height: 100%;
  display: block;
  min-height: 0;
}
/* North arrow overlay — absolutely positioned at the top-right of the
   2D viewport so it doesn't move with the room or scale with wheel
   zoom. Fixed CSS pixel size for consistent legibility regardless of
   room dimensions. The arrow points UP (page-top) which corresponds
   to world +Y (north) after the v=459 Y-axis math-convention flip. */
.viewport-2d > .vp-north-arrow {
  position: absolute;
  top: 116px;            /* clears the glass header (104px) + 12px breathing room */
  right: 70px;           /* clears the 60px right padding + 10px gutter */
  width: 28px;
  height: 38px;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
  z-index: 10;
}
.viewport-2d > .vp-north-arrow svg {
  width: 100%;
  height: 70%;
  display: block;
}
.viewport-2d > .vp-north-arrow span {
  font-size: 10px;
  font-weight: 600;
  color: #cfd3d9;
  text-shadow:
    -1px -1px 0 #0a0c10, 1px -1px 0 #0a0c10,
    -1px  1px 0 #0a0c10, 1px  1px 0 #0a0c10;
  line-height: 1;
  margin-top: 1px;
}

.viewport-2d > .vp-legend {
  grid-area: legend;
  align-self: center;
  justify-self: end;
}
.viewport-2d > .vp-note {
  grid-area: note;
  text-align: center;
  font-size: 0.72rem;
  color: #667;
}
.vp-lbl { font-family: "Inter Tight", system-ui, -apple-system, sans-serif; }
/* Maya v9 audit §4 hierarchy. Wall direction tags are quiet (small
   uppercase, muted) — direction matters for orientation, the
   material no longer competes for attention. */
.vp-lbl-wall   { font-size: 9px; fill: #5a6677; font-weight: 600; letter-spacing: 0.12em; }
/* Per-wall material name — rides on the INSIDE face of each wall
   trapezoid (Phase 7 Commit 4). Smaller than direction tags so the
   contractor's eye lands on the wall fill pattern first, the name
   second. Letter-spacing reset to 0 — these are real names, not
   shouty all-caps labels. */
.vp-lbl-wall-mat { font-size: 8px; fill: #2a2f3a; font-weight: 500; letter-spacing: 0; }
.vp-lbl-center { font-size: 12px; fill: #8a8e99; }
.vp-lbl-dim    { font-size: 11px; fill: #7a8090; letter-spacing: 0.01em; font-variant-numeric: tabular-nums; }
/* Meta strip — lives BELOW the SVG (not inside) so wheel-zoom of the
   SVG viewBox doesn't scale it. Same visual treatment as the old
   in-SVG vp-lbl-dim text. */
.vp-meta-strip {
  font-size: 11px;
  color: #7a8090;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  text-align: center;
  margin-top: 4px;
  padding: 2px 8px;
  line-height: 1.3;
}
.vp-lbl-empty       { font-size: 13px; fill: #5a6677; }
.vp-lbl-empty-hint  { font-size: 11px; fill: #4a5260; }
/* Default (non-SPL) legend — vertical now since it sits in the right
   column. Horizontal flex-wrap doesn't make sense in a narrow column. */
.vp-legend {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: 0.72rem;
  color: #99a;
}
.vp-legend .legend-item { display: inline-flex; align-items: center; gap: 0.3rem; }
.vp-legend .swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; }
.vp-note { text-align: center; font-size: 0.72rem; color: #667; }

.phase-placeholder {
  padding: 0.6rem 0.75rem;
  font-size: 0.74rem;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  line-height: 1.55;
}
.phase-placeholder strong { color: var(--fg-strong); font-weight: 600; }
.phase-placeholder em { color: var(--data-cyan, #74d0ff); font-style: normal; }

/* Glossary hover terms — dotted underline tells users hover will show a
   definition. Built-in `title` attribute is used so it works without JS
   on the tooltip side. */
.gloss-term {
  text-decoration: underline dotted rgba(255, 255, 255, 0.35);
  text-underline-offset: 2px;
  cursor: help;
}
.gloss-term:hover { text-decoration-color: var(--data-cyan, #74d0ff); }

/* Sabine / Eyring / mid α decision hint in Results panel */
.rt60-rec {
  margin: 0.3rem 0 0.5rem;
  padding: 0.42rem 0.6rem;
  font-size: 0.74rem;
  line-height: 1.45;
  border-radius: 3px;
  border-left: 2px solid var(--border-strong);
  background: rgba(255, 255, 255, 0.03);
  color: var(--muted);
}
.rt60-rec strong { color: var(--fg-strong); font-weight: 600; }
.rt60-rec.rec-eyring { border-left-color: var(--data-amber); }
.rt60-rec.rec-sabine { border-left-color: var(--data-green); }
.rt60-rec.rec-mid    { border-left-color: var(--data-cyan, #74d0ff); }

/* Welcome / onboarding card — overlays the viewport on first load only.
   Positioned top-right so it doesn't block the primary 3D scene, and
   dismisses sticky via localStorage. */
#welcome-card {
  position: absolute;
  top: 50px;
  right: 16px;
  width: 340px;
  max-width: calc(100vw - 32px);
  padding: 1rem 1.1rem 0.85rem;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
  z-index: 90;
  color: var(--fg);
  font-size: 0.8rem;
  line-height: 1.5;
}
#welcome-card h3 {
  margin: 0 0 0.35rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--fg-strong);
  letter-spacing: 0.01em;
}
#welcome-card .welcome-sub {
  margin: 0 0 0.75rem;
  color: var(--muted);
  font-size: 0.74rem;
}
#welcome-close {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  width: 1.5rem; height: 1.5rem;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--muted);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 50%;
}
#welcome-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--fg-strong); }
.welcome-steps {
  list-style: none;
  padding: 0;
  margin: 0 0 0.5rem;
  display: grid;
  gap: 0.45rem;
}
.welcome-steps li {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: 0.55rem;
  align-items: start;
}
.welcome-step-num {
  width: 1.4rem; height: 1.4rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--data-cyan, #74d0ff);
  color: #051018;
  font-weight: 700;
  font-size: 0.78rem;
}
.welcome-steps strong { color: var(--fg-strong); font-weight: 600; font-size: 0.82rem; }
.welcome-hint { color: var(--muted); font-size: 0.73rem; }
.welcome-foot {
  margin-top: 0.25rem;
  padding-top: 0.55rem;
  border-top: 1px solid var(--border);
  font-size: 0.7rem;
  color: var(--muted);
}
.welcome-foot kbd {
  background: var(--border);
  padding: 1px 5px;
  border-radius: 3px;
  font-family: inherit;
  font-size: 0.72rem;
  color: var(--fg-strong);
}

/* DeviceLAB-page wrapper — same shape as SpeakerLAB. The
   #view-rack container fills the viewport area below the header
   nav; the .rack-builder grid does the three-column layout. */
#devicelab-root {
  height: 100vh;       /* P10 — content extends behind glass header */
  background: var(--bg-app);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr;
  position: relative;
}
#devicelab-root #view-rack {
  height: 100%;
  display: flex;
  flex-direction: column;
  /* v=695 — full-bleed preview matches RoomLAB's #viewport: only top
     padding to clear the glass header; sides + bottom hug the screen
     edge so the 3D preview goes edge-to-edge behind the floating rail
     icons. (Old padding-64-each-side reserved gutters for the rails
     when the layout was a 3-column grid; the centre column is now the
     only thing in the grid so the gutters were just dead space.)
  */
  padding: 56px 0 0 0;
  box-sizing: border-box;
}
#devicelab-root #view-rack .rack-builder { flex: 1; min-height: 0; }

/* Context banner — "Editing racks for: <room>". Reminds the user
   which RoomLAB scene's rackSystem they're modifying. Without this
   DeviceLAB feels disconnected from RoomLAB — the user cannot tell
   whether their edits will land in the hifi room they were just in
   or some other scene. */
.rack-builder-ctx {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.55rem 0.9rem;
  background: rgba(74, 163, 255, 0.08);
  border-bottom: 1px solid rgba(74, 163, 255, 0.2);
  font-size: 0.78rem;
  flex-shrink: 0;
}
.rack-ctx-label {
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.66rem;
}
.rack-ctx-name {
  color: var(--fg-strong, #e5e7eb);
  font-weight: 600;
}
.rack-ctx-meta {
  color: var(--muted);
  font-size: 0.72rem;
  flex: 1;
}
.rack-ctx-back {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.74rem;
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--accent);
  border-radius: 4px;
  transition: background-color 120ms ease-out;
}
.rack-ctx-back:hover { background: rgba(74, 163, 255, 0.18); }

/* PA Rack Builder — three-column layout. */
.rack-builder {
  /* P4.5 — left + right columns moved into rail panels; centre column
     now fills the viewport. CSS:has() degrades cleanly: when the
     side columns are missing, grid-template-columns collapses to a
     single 1fr track. */
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  height: 100%;
  padding: 0.6rem;
  box-sizing: border-box;
  overflow: hidden;
}
.rack-col-left, .rack-col-mid, .rack-col-right {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.6rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.rack-builder h3 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin: 0.2rem 0 0.3rem 0;
}
.rack-frame-list, .rack-amp-list, .rack-system-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.rack-frame-tile, .rack-amp-tile {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.45rem 0.55rem;
  background: var(--bg-elev-2, #1c1f24);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--fg-strong);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.78rem;
}
.rack-frame-tile:hover, .rack-amp-tile:hover {
  border-color: var(--border-strong);
  background: var(--border);
}
.rack-frame-tile strong, .rack-amp-tile strong {
  font-size: 0.84rem;
  color: var(--fg-strong);
}
.rack-amp-tile-spec, .rack-amp-tile-cat {
  font-size: 0.7rem;
  color: var(--muted);
}
.rack-amp-tile:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.rack-amp-filter input {
  width: 100%;
  padding: 0.35rem 0.5rem;
  background: var(--bg-elev-2, #1c1f24);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--fg-strong);
  font-family: inherit;
  font-size: 0.78rem;
}
.rack-mid-header {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.2rem 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.4rem;
}
.rack-mid-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--fg-strong);
}
.rack-mid-summary {
  font-size: 0.74rem;
  color: var(--muted);
}
.rack-preview {
  flex: 1 1 auto;
  min-height: 240px;
  background: #222934;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  position: relative;
}
.rack-preview canvas {
  display: block;
  width: 100%;
  height: 100%;
}
/* v=694 — DeviceLAB now uses a full-bleed 3D preview (like RoomLAB's
   3D viewport) with overlay controls instead of stacked rows. */
#devicelab-root #view-rack .rack-builder {
  display: block;
  height: 100%;
  padding: 0;
  overflow: hidden;
}
#devicelab-root #view-rack .rack-col-mid {
  height: 100%;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}
#devicelab-root #view-rack .rack-preview {
  border: 0;
  border-radius: 0;
  height: 100%;
}
/* Top overlay row above the preview — context + door tabs + title.
   Pointer-events: none on the row, re-enabled on individual children
   so the underlying OrbitControls still catches drag-rotate over the
   gaps between controls. */
.rack-vp-overlay-top {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 0.85rem;
  pointer-events: none;
  z-index: 10;
}
.rack-vp-overlay-top > * { pointer-events: auto; }

/* Small floating context chip (top-left of viewport). Replaces the
   full-width banner from v=693. */
.rack-vp-ctx {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.32rem 0.65rem;
  background: rgba(20, 22, 28, 0.55);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  font-size: 0.72rem;
  color: var(--fg);
  max-width: 38ch;
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .rack-vp-ctx { background: rgba(20, 22, 28, 0.78); }
}
.rack-vp-ctx-label {
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.62rem;
}
.rack-vp-ctx-name {
  color: var(--fg-strong, #e5e7eb);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rack-vp-ctx-back {
  margin-left: 0.4rem;
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.7rem;
  white-space: nowrap;
}
.rack-vp-ctx-back:hover { text-decoration: underline; }

/* Door-toggle segmented control (top-centre) — same glass-pill family
   as RoomLAB's 2D/3D/Walk vp-mode-segment (css/main.css ~645). When the
   current rack isn't enclosed the two buttons stay display:none, so the
   segment shrinks to nothing and the centre is left clean. */
.rack-vp-door-segment {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: rgba(20, 22, 28, 0.45);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .rack-vp-door-segment { background: rgba(20, 22, 28, 0.65); }
}
.rack-vp-door-tab {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  padding: 5px 14px;
  min-width: 84px;
  border-radius: calc(var(--radius) - 1px);
  cursor: pointer;
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  text-align: center;
  transition: background-color 120ms ease, color 120ms ease;
}
.rack-vp-door-tab:hover { color: var(--fg-strong, #fff); background: rgba(255, 255, 255, 0.04); }

/* Title cluster (top-right) — rack model name + summary. Compact so
   it doesn't crowd the door pill. */
.rack-vp-title {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.05rem;
  padding: 0.32rem 0.65rem;
  background: rgba(20, 22, 28, 0.55);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  max-width: 36ch;
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .rack-vp-title { background: rgba(20, 22, 28, 0.78); }
}
.rack-vp-title .rack-mid-title {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--fg-strong, #e5e7eb);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 34ch;
}
.rack-vp-title .rack-mid-summary {
  font-size: 0.68rem;
  color: var(--muted);
}

/* Slot-list / current-rack section inside the BOM rail panel. */
#panel-bom .rack-slot-list-wrap h3 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin: 0.6rem 0 0.3rem 0;
}
.rack-slot-list {
  flex: 0 0 auto;
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.rack-slot {
  display: grid;
  grid-template-columns: 60px 1fr auto;
  gap: 0.5rem;
  align-items: center;
  padding: 0.4rem 0.5rem;
  background: var(--bg-elev-2, #1c1f24);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.rack-slot-u {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.78rem;
  color: var(--muted);
  text-align: center;
}
.rack-slot-body {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.rack-slot-body strong { font-size: 0.82rem; color: var(--fg-strong); }
.rack-slot-body span { font-size: 0.7rem; color: var(--muted); }
.rack-slot-remove {
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 4px;
  width: 24px;
  height: 24px;
  cursor: pointer;
  font-size: 0.95rem;
}
.rack-slot-remove:hover { border-color: #d93a3a; color: #d93a3a; }
.rack-mid-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-top: 0.4rem;
  border-top: 1px solid var(--border);
}
.rack-target-label {
  font-size: 0.72rem;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.rack-target-room {
  flex: 1 1 220px;
  padding: 0.42rem 0.55rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--fg);
  font: inherit;
  font-size: 0.78rem;
}
.rack-target-room:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.18);
}
.rack-action {
  flex: 1;
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--bg-elev-2, #1c1f24);
  color: var(--fg-strong);
  font-family: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}
.rack-action-place {
  background: rgba(74, 163, 255, 0.18);
  border-color: rgba(74, 163, 255, 0.5);
  color: #aac8ff;
}
.rack-action-place:hover:not(:disabled) {
  background: rgba(74, 163, 255, 0.28);
  border-color: #4aa3ff;
}
.rack-action:disabled { opacity: 0.4; cursor: not-allowed; }
.rack-system-item {
  position: relative;
  padding: 0.5rem 0.6rem;
  background: var(--bg-elev-2, #1c1f24);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.78rem;
}
.rack-system-item .pr-mute { font-size: 0.7rem; color: var(--muted); }
.rack-system-remove {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 4px;
  padding: 0.1rem 0.4rem;
  cursor: pointer;
  font-size: 0.7rem;
  font-family: inherit;
}
.rack-system-remove:hover { border-color: #d93a3a; color: #d93a3a; }
.rack-system-edit {
  position: absolute;
  top: 0.4rem;
  right: 3.5rem;     /* sit to the LEFT of .rack-system-remove */
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 4px;
  padding: 0.1rem 0.4rem;
  cursor: pointer;
  font-size: 0.7rem;
  font-family: inherit;
}
.rack-system-edit:hover { border-color: var(--accent); color: var(--accent); }

/* 2D-viewport placed-rack interactivity (v=696). Same cursor + hover
   pattern as r2d-furniture so drag-to-move + right-click read as
   first-class affordances. */
.r2d-rack { cursor: pointer; }
.r2d-rack:hover { filter: brightness(1.18); }
.rack-door-toggle {
  margin-top: 0.35rem;
  background: none;
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 4px;
  padding: 0.18rem 0.5rem;
  cursor: pointer;
  font-size: 0.72rem;
  font-family: inherit;
}
.rack-door-toggle:hover { border-color: var(--accent); color: var(--accent); }
.rack-empty {
  font-size: 0.74rem;
  color: var(--muted);
  font-style: italic;
  margin: 0.3rem 0;
}

/* Master EQ — 10-band graphic EQ at top of Sources panel. Vertical sliders,
   compact so 10 fit in the left-rail width. Bypass + Flatten in header. */
.eq-section {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.55rem 0.65rem 0.6rem;
  margin-bottom: 0.55rem;
}
.eq-section.bypassed { opacity: 0.72; }
.eq-header {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 0.45rem;
}
.eq-title {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--fg-strong);
  letter-spacing: 0.02em;
}
.eq-sub {
  font-size: 0.65rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  flex: 1;
}
.eq-bypass, .eq-flatten {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 3px 8px;
  font: inherit;
  font-size: 0.7rem;
  border-radius: 3px;
  cursor: pointer;
  letter-spacing: 0.04em;
}
.eq-bypass:hover, .eq-flatten:hover { color: var(--fg); border-color: var(--border-strong); }
.eq-bypass.on {
  background: #3a5a8a;
  color: #fff;
  border-color: #5a7ab0;
  font-weight: 600;
}
.eq-sliders {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 4px;
  align-items: end;
}
.eq-band {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
}
.eq-band-readout {
  font-size: 0.6rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  min-height: 11px;
  line-height: 1;
}
/* Vertical-oriented range slider. `appearance: slider-vertical` is
   non-standard and Chrome deprecated it; the standard replacement is
   `writing-mode: vertical-lr; direction: rtl` which flips the track to
   go bottom-up (Chrome 124+ honours this; Firefox + Safari have always
   done so). Matches the EQ widget's graphic-EQ gesture of "push up
   for boost". */
.eq-slider {
  writing-mode: vertical-lr;
  direction: rtl;
  width: 16px;
  height: 100px;
  padding: 0;
  margin: 0;
  accent-color: #5a7ab0;
  cursor: pointer;
}
.eq-slider:disabled { cursor: not-allowed; opacity: 0.5; }
.eq-band-label {
  font-size: 0.58rem;
  color: var(--muted-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.source-card,
.listener-card,
.zone-card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 0.65rem 0.8rem;
  margin-bottom: 0.55rem;
}

/* Flash pulse for the source card targeted by a 3D click-on-speaker. Warm
   amber outline + soft inner glow fade across ~1.3 s so the user sees
   exactly which card matches the cabinet they just tapped. */
.source-card.source-card-flash,
#panel-treatments .treatment-card.treatment-card-flash {
  animation: sourceCardFlash 1.3s ease-out;
}
@keyframes sourceCardFlash {
  0% {
    outline: 2px solid #ffcc55;
    outline-offset: -1px;
    box-shadow: 0 0 0 0 rgba(255, 204, 85, 0.7), var(--shadow-card);
    background: rgba(255, 204, 85, 0.12);
  }
  70% {
    outline: 2px solid #ffcc55;
    outline-offset: -1px;
    box-shadow: 0 0 0 8px rgba(255, 204, 85, 0), var(--shadow-card);
    background: rgba(255, 204, 85, 0.06);
  }
  100% {
    outline: 2px solid transparent;
    outline-offset: -1px;
    box-shadow: 0 0 0 0 rgba(255, 204, 85, 0), var(--shadow-card);
    background: var(--bg-elev);
  }
}

/* Persistent selection — applied to the card whose source is currently
   selected in the 2D viewport (click on the speaker icon there). Distinct
   from the transient .source-card-flash pulse — this one stays on until
   another source is clicked. Cyan accent matches the SVG halo ring. */
.source-card.source-card-selected {
  outline: 1.5px solid #74d0ff;
  outline-offset: -1px;
  box-shadow:
    0 0 0 1px rgba(116, 208, 255, 0.35),
    0 0 16px -4px rgba(116, 208, 255, 0.45),
    var(--shadow-card);
  background: linear-gradient(180deg, rgba(116, 208, 255, 0.05), rgba(116, 208, 255, 0.015));
}

/* 2D viewport — speaker icon interaction states.
   The SVG <g class="r2d-source"> wraps every speaker icon with the
   transform="translate(sx, sy)" set inline by the renderer. We add
   visual feedback via class toggles + CSS transitions. */
.r2d-source { cursor: grab; transition: filter 120ms ease, opacity 120ms ease; }
.r2d-source:hover .r2d-spk-poly { filter: brightness(1.18); }
.r2d-source-selected .r2d-spk-poly { stroke: #ffd24a; stroke-width: 2.2; }
.r2d-source-dragging {
  cursor: grabbing;
  filter: drop-shadow(0 0 8px rgba(255, 200, 60, 0.7));
}
.r2d-source-dragging .r2d-spk-poly,
.r2d-source-dragging .r2d-spk-dot {
  fill: #ffd24a !important;
  stroke: #b48000 !important;
}

/* 2D listener icon — same interaction states as the speakers, mirrored
   for the listener circle + label group. Selected state keeps the
   existing yellow fill (from renderListenersSVG) so it's distinct
   from the cyan speaker-selected ring; dragging adds the 2x scale
   + glow + amber tint. */
.r2d-listener { cursor: grab; transition: filter 120ms ease; }
.r2d-listener:hover .r2d-lst-dot { filter: brightness(1.18); }
.r2d-listener-dragging {
  cursor: grabbing;
  filter: drop-shadow(0 0 8px rgba(255, 200, 60, 0.7));
}
.r2d-listener-dragging .r2d-lst-dot {
  fill: #ffd24a !important;
  stroke: #b48000 !important;
}

/* Room-corner vertex handles — click to select, drag to reshape the
   room polygon. Default state is a subtle white dot; selected vertex
   gets a cyan ring; adjacent vertices (sharing an edge with the
   selected) get a smaller cyan tint so the user sees which edges
   they're about to edit. Drag scales the group 2x via SVG transform. */
.r2d-vertex { cursor: pointer; transition: filter 120ms ease; }
.r2d-vertex .r2d-vertex-hit  { fill: transparent; pointer-events: all; }
.r2d-vertex .r2d-vertex-dot  {
  fill: #d6dde6;
  stroke: #13161c;
  stroke-width: 1.4;
  transition: fill 120ms ease, stroke 120ms ease;
}
.r2d-vertex:hover .r2d-vertex-dot { fill: #74d0ff; stroke: #0a3856; }
.r2d-vertex-adjacent .r2d-vertex-dot {
  fill: #74d0ff;
  stroke: #0a3856;
  opacity: 0.8;
}
.r2d-vertex-selected .r2d-vertex-dot {
  fill: #74d0ff;
  stroke: #ffffff;
  stroke-width: 2;
}
.r2d-vertex-dragging {
  cursor: grabbing;
  filter: drop-shadow(0 0 8px rgba(116, 208, 255, 0.85));
}
.r2d-vertex-dragging .r2d-vertex-dot {
  fill: #ffd24a !important;
  stroke: #b48000 !important;
}
/* Adjacent-edge overlay — drawn underneath the handles so the dots
   sit on top. Cyan dash so it's distinguishable from the warm room
   outline rendered by renderRoomOutline. */
.r2d-vertex-edge {
  stroke: #74d0ff;
  stroke-width: 2.5;
  stroke-dasharray: 6 4;
  fill: none;
  pointer-events: none;
  opacity: 0.85;
}

/* Live coordinate labels shown next to every vertex while ANY vertex
   is being dragged. Read-only neighbours dim; the dragged vertex's
   label brightens so the user sees the changing value pop. */
.r2d-vertex-coord {
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 500;
  fill: #cfd6e0;
  pointer-events: none;
  paint-order: stroke;
  stroke: rgba(8, 10, 14, 0.85);
  stroke-width: 3;
  stroke-linejoin: round;
}
.r2d-vertex-coord-active {
  fill: #ffd24a;
  font-weight: 600;
}


/* Right-click context menu opened on a 2D speaker. Glass card matching
   the rail aesthetic so it feels native to the floor-plan canvas. */
.r2d-ctx-menu {
  position: fixed;
  z-index: 9000;
  min-width: 200px;
  padding: 6px;
  background: rgba(20, 22, 28, 0.94);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 10px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 14px 40px -8px rgba(0, 0, 0, 0.55);
  color: #e6ebf2;
  font-family: "Inter Tight", "IBM Plex Sans", -apple-system, system-ui, sans-serif;
  font-size: 13px;
  user-select: none;
  animation: r2dCtxMenuIn 140ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
@keyframes r2dCtxMenuIn {
  from { opacity: 0; transform: translateY(-4px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0)    scale(1);    }
}
.r2d-ctx-header {
  padding: 6px 10px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #8a94a3;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  margin-bottom: 4px;
}
.r2d-ctx-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  background: transparent;
  border: none;
  border-radius: 6px;
  color: #e6ebf2;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background-color 120ms ease;
}
.r2d-ctx-item:hover,
.r2d-ctx-item:focus-visible {
  background: rgba(116, 208, 255, 0.16);
  outline: none;
}
/* Disabled context-menu item (e.g. "Delete node" at the 3-vertex floor) —
   greyed and non-clickable, matching the app's disabled-control affordance. */
.r2d-ctx-item.is-disabled,
.r2d-ctx-item:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}
.r2d-ctx-item.is-disabled:hover,
.r2d-ctx-item:disabled:hover {
  background: transparent;
}
.r2d-ctx-glyph {
  font-size: 14px;
  color: #74d0ff;
  width: 18px;
  text-align: center;
}
.r2d-ctx-hint {
  margin-left: auto;
  font-size: 11px;
  color: #6b7480;
}

/* Surface row pulse — fired when the user clicks a wall / floor / ceiling
   in the 3D viewport. The matching <label> in the Room panel pulses an
   amber outline so the user can see which row their click mapped to,
   then the native dropdown opens via .focus() so picking a material is
   one click away. Mirrors source-card-flash but stripped to outline-only
   because the row is short and a background flash would obscure the
   <select>'s current value. */
.surface-pulse {
  animation: surfacePulse 1.4s ease-out;
  border-radius: 4px;
}
@keyframes surfacePulse {
  0%   { outline: 2px solid #ffd000; outline-offset: 2px; background: rgba(255, 208, 0, 0.10); }
  70%  { outline: 2px solid #ffd000; outline-offset: 2px; background: rgba(255, 208, 0, 0.04); }
  100% { outline: 2px solid transparent; outline-offset: 2px; background: transparent; }
}

/* Wall row + per-wall openings editor (PR2). Each wall slot shows a
   material picker plus an inline list of doors and windows on that
   wall. Walls without openings just show the picker; the openings
   block appears below it once the user clicks "+ Door" or "+ Window". */
.wall-row { display: flex; flex-direction: column; gap: 4px; padding: 2px 0; min-width: 0; }
.wall-mat-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* Long material names ("Heavy plate glass with damping…") would otherwise
   stretch the select to its intrinsic content width and blow out the sidebar.
   Clamp the select to the row's available space and let the option text
   ellipsis inside the closed control. */
.wall-mat-row > select { flex: 1 1 auto; min-width: 0; max-width: 100%; }
/* In-row inline hint, surfaced under a material picker when the chosen
   material has a non-obvious physical consequence. Today only the
   ceiling + open-air gotcha uses it (a removed roof raises interior SPL
   when sources sit above the roof line — gallery horns, minaret PA).
   Quiet by default; the .mat-row-hint-warn variant tints the leading
   accent border in amber so the eye catches it. */
.mat-row-hint {
  font-size: 11px;
  line-height: 1.35;
  color: var(--muted);
  padding: 4px 6px;
  margin-left: 10px;
  border-left: 2px solid var(--border);
}
.mat-row-hint-warn { border-left-color: var(--data-amber); color: var(--fg); }
.openings-block {
  margin-left: 10px;
  padding: 4px 6px;
  border-left: 2px solid var(--border, #2a2f38);
  display: flex; flex-direction: column; gap: 4px;
  min-width: 0;
}
/* Per-wall thickness row — Phase 7 C3. Sits between the material select
   and the openings sub-block so it reads as "this wall's thickness" right
   next to "+ Door" / "+ Window". Compact single line; numeric input
   constrained to a fixed width so a 3-digit value doesn't push the unit
   suffix off the right edge of the sidebar. */
.wall-thickness-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: 10px;
  padding: 2px 6px;
  font-size: 11px;
  min-width: 0;
}
.wall-thickness-label { color: var(--text-muted, #8a93a3); flex: 0 0 auto; }
.wall-thickness-input {
  width: 56px;
  padding: 1px 4px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  background: var(--bg-elev, #14181f);
  border: 1px solid var(--border, #2a2f38);
  color: var(--text, #cfd6e0);
  border-radius: 3px;
}
.wall-thickness-unit { color: var(--text-muted, #8a93a3); flex: 0 0 auto; }
.openings-hdr { display: flex; align-items: center; gap: 6px; min-width: 0; }
.openings-summary { font-size: 11px; color: var(--text-muted, #8a93a3); flex: 1; min-width: 0; }
/* "+ Door" / "+ Window" sit on their own row below the summary so a wide
   material dropdown above can't push them off the right edge of the sidebar.
   Compact, left-aligned, same button styling as before. */
.openings-actions { display: flex; gap: 6px; flex-wrap: wrap; min-width: 0; }
.btn-add-opening {
  font-size: 11px; padding: 2px 8px; border-radius: 3px;
  background: var(--bg-elev, #1c2028); border: 1px solid var(--border, #2a2f38);
  color: var(--text-muted, #cfd6e0); cursor: pointer;
}
.btn-add-opening:hover { background: var(--bg-hover, #252b35); color: var(--text, #e6edf3); }
.opening-row {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: 4px 6px;
  align-items: center;
  padding: 4px 6px;
  background: var(--bg-elev, #14181f);
  border-radius: 3px;
  font-size: 11px;
}
.opening-kind { font-weight: 600; color: var(--text, #cfd6e0); }
.opening-state { font-size: 11px; padding: 1px 4px; }
.opening-mat { font-size: 11px; padding: 1px 4px; min-width: 0; }
.btn-delete-opening {
  width: 18px; height: 18px; padding: 0;
  border-radius: 3px;
  background: transparent; border: 1px solid var(--border, #2a2f38);
  color: var(--text-muted, #8a93a3); cursor: pointer;
  font-size: 13px; line-height: 1;
}
.btn-delete-opening:hover { background: rgba(220, 70, 70, 0.18); color: #f59999; border-color: #884040; }
.opening-dims {
  grid-column: 1 / -1;
  display: flex; gap: 4px; flex-wrap: wrap;
  padding-top: 2px;
}
.opening-dims label { display: flex; align-items: center; gap: 3px; font-size: 10px; color: var(--text-muted, #8a93a3); }
.opening-dims input { width: 50px; font-size: 11px; padding: 1px 3px; }

/* "New custom room" modal — replaces the back-to-back window.prompt()
   pair. Shows the existing project list (if any) so the user can attach
   the new room to a project they already have, plus a "+ New project"
   row that defaults to checked. Centred, focus-trapped, Esc-closable. */
.rl-modal-overlay {
  position: fixed; inset: 0;
  background: rgba(8, 11, 16, 0.72);
  display: flex; align-items: center; justify-content: center;
  z-index: 9999;
  animation: rlModalFade 160ms ease-out;
}
@keyframes rlModalFade { from { opacity: 0; } to { opacity: 1; } }
.rl-modal {
  background: var(--bg-elev, #1c2028);
  border: 1px solid var(--border, #2a2f38);
  border-radius: 6px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  padding: 18px 20px 16px;
  min-width: 360px; max-width: 480px;
  color: var(--text, #e6edf3);
}
.rl-modal h3 { margin: 0 0 12px; font-size: 14px; font-weight: 600; }
.rl-modal-section { margin-bottom: 12px; }
.rl-modal-label {
  display: block; font-size: 11px;
  color: var(--text-muted, #8a93a3); margin-bottom: 6px;
  text-transform: uppercase; letter-spacing: 0.4px;
}
.rl-modal-projects {
  max-height: 180px; overflow-y: auto;
  border: 1px solid var(--border, #2a2f38); border-radius: 4px;
  padding: 4px 0;
  margin-bottom: 8px;
}
.rl-modal-radio-row {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px; cursor: pointer; font-size: 12px;
}
.rl-modal-radio-row:hover { background: rgba(74, 168, 255, 0.08); }
.rl-modal-radio-row input { margin: 0; }
.rl-modal-radio-text { flex: 1; }
.rl-modal-count { color: var(--text-muted, #8a93a3); font-size: 11px; margin-left: 6px; }
.rl-modal-input {
  width: 100%; box-sizing: border-box;
  background: var(--bg, #14181f);
  border: 1px solid var(--border, #2a2f38); border-radius: 3px;
  color: var(--text, #e6edf3);
  padding: 7px 9px; font-size: 12px;
  transition: border-color 120ms ease, opacity 120ms ease;
}
.rl-modal-input:focus { outline: none; border-color: #4a8ff0; }
.rl-modal-input:disabled { background: rgba(20, 24, 31, 0.5); cursor: not-allowed; }
.rl-modal-actions {
  display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px;
}
.rl-modal-cancel, .rl-modal-confirm {
  font-size: 12px; padding: 6px 14px; border-radius: 3px; cursor: pointer;
  border: 1px solid var(--border, #2a2f38);
}
.rl-modal-cancel {
  background: transparent; color: var(--text-muted, #8a93a3);
}
.rl-modal-cancel:hover { background: rgba(255, 255, 255, 0.04); color: var(--text, #e6edf3); }
.rl-modal-confirm {
  background: #4a8ff0; color: white; border-color: #4a8ff0;
}
.rl-modal-confirm:hover { background: #6aa8ff; border-color: #6aa8ff; }
.rl-modal-confirm:disabled {
  background: var(--bg, #14181f); color: var(--text-muted, #8a93a3);
  border-color: var(--border, #2a2f38); cursor: not-allowed;
}

/* Room Capture — mode picker (Draw it / From a photo). Mirrors .rl-modal. */
.rl-modal-sub {
  font-size: 12px; color: var(--text-muted, #8a93a3); margin: 2px 0 12px;
}
.rl-capture-modes {
  display: flex; flex-direction: column; gap: 8px;
}
.rl-capture-mode {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  text-align: left; padding: 12px 14px; min-height: 44px; border-radius: 4px;
  cursor: pointer;
  background: var(--bg, #14181f); color: var(--text, #e6edf3);
  border: 1px solid var(--border, #2a2f38); width: 100%;
  transition: border-color 140ms ease-out, background 140ms ease-out;
}
.rl-capture-mode:hover { border-color: #4a8ff0; background: rgba(74, 143, 240, 0.08); }
.rl-capture-mode:focus-visible {
  outline: 2px solid #6aa8ff; outline-offset: 2px; border-color: #4a8ff0;
}
.rl-capture-mode-label { font-size: 13px; font-weight: 600; }
/* Hint contrast lifted to ≥4.5:1 on the modal bg — #9aa6b6 over #14181f. */
.rl-capture-mode-hint { font-size: 11px; line-height: 1.35; color: #9aa6b6; }

/* Place-Saved-Room placement HUD — floats over the 3D viewport while
   the user is positioning a sub-room. Positioned bottom-centre so it
   doesn't fight the side panel or the SPL legend in the bottom-right. */
.rl-placement-hud {
  position: fixed; left: 50%; bottom: 24px;
  transform: translateX(-50%);
  background: rgba(20, 24, 31, 0.92);
  border: 1px solid rgba(127, 199, 255, 0.5);
  color: #cfd6e0; font-size: 12px; font-weight: 500;
  padding: 8px 16px; border-radius: 4px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  z-index: 9998; pointer-events: none;
  opacity: 0; transition: opacity 140ms ease-out;
}
.rl-placement-hud.show { opacity: 1; }

/* Header project slot — the "active project" pill next to RoomLAB Suite.
   Becomes a dropdown when 2+ projects exist. Native <details> handles
   open/close + outside-click / Esc-close for free. */
.project-slot {
  display: inline-flex; align-items: center;
  margin-left: 10px;
  font-size: 12px;
}
.project-slot .project-name {
  font-weight: 600; color: #cfd6e0;
  background: rgba(74, 143, 240, 0.12);
  padding: 3px 9px; border-radius: 999px;
  border: 1px solid rgba(74, 143, 240, 0.26);
}
.project-dd { position: relative; }
.project-dd-summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
}
.project-dd-summary::-webkit-details-marker { display: none; }
.project-dd-arrow { font-size: 9px; color: var(--text-muted, #8a93a3); }
.project-dd[open] .project-dd-arrow { transform: rotate(180deg); }
.project-dd-menu {
  position: absolute; top: calc(100% + 6px); left: 0;
  background: var(--bg-elev, #1c2028);
  border: 1px solid var(--border, #2a2f38); border-radius: 4px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  min-width: 220px; max-height: 300px; overflow-y: auto;
  padding: 4px 0; z-index: 100;
}
.project-dd-item {
  display: flex; width: 100%; align-items: center; justify-content: space-between;
  background: transparent; border: none; color: var(--text, #cfd6e0);
  padding: 7px 12px; font-size: 12px; cursor: pointer; text-align: left;
}
.project-dd-item:hover { background: rgba(74, 143, 240, 0.10); }
.project-dd-item.active { background: rgba(74, 143, 240, 0.18); color: #fff; }
.project-dd-item.active::after { content: '✓'; color: #4a8ff0; margin-left: 8px; }
.project-dd-name { font-weight: 500; }
.project-dd-count { color: var(--text-muted, #8a93a3); font-size: 11px; }

/* Saved-rooms chip-row banner — labels which project's rooms the chips
   belong to so the user understands the row is filtered to the active
   project, not showing every saved room ever. */
.custom-saved-banner {
  display: inline-block;
  font-size: 11px; color: var(--text-muted, #8a93a3);
  font-weight: 600; letter-spacing: 0.4px;
  margin-right: 6px; padding: 3px 0;
  text-transform: uppercase;
}

/* Sub-structure detail panel — appears below the chip row when a placed
   sub-room is selected (3D click or chip click). Lets the user type
   exact X / Y / Z / rotation values, plus delete-as-whole and break-to-
   merge actions. Hidden when nothing is selected. */
.sub-structure-detail {
  margin: 6px 0 4px;
  padding: 8px 10px;
  background: var(--bg-elev, #14181f);
  border: 1px solid var(--border, #2a2f38);
  border-left: 3px solid #4a8ff0;
  border-radius: 4px;
  font-size: 12px;
}
.sub-detail-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 6px;
}
.sub-detail-title {
  font-weight: 600; color: var(--text, #e6edf3);
}
.sub-detail-close {
  width: 18px; height: 18px; padding: 0;
  background: transparent; border: 1px solid var(--border, #2a2f38);
  border-radius: 3px;
  color: var(--text-muted, #8a93a3); cursor: pointer;
  font-size: 13px; line-height: 1;
}
.sub-detail-close:hover {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text, #e6edf3);
}
.sub-detail-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 4px 8px;
  margin-bottom: 8px;
}
.sub-detail-grid label {
  display: flex; align-items: center; gap: 4px;
  font-size: 11px; color: var(--text-muted, #cfd6e0);
}
.sub-detail-grid input {
  width: 60px;
  background: var(--bg, #14181f);
  border: 1px solid var(--border, #2a2f38);
  border-radius: 3px;
  color: var(--text, #e6edf3);
  padding: 3px 6px; font-size: 12px;
}
.sub-detail-grid input:focus { outline: none; border-color: #4a8ff0; }
.sub-detail-grid .unit { color: var(--text-muted, #8a93a3); font-size: 10px; }
.sub-detail-actions {
  display: flex; gap: 6px; margin-top: 4px;
}
.sub-detail-break, .sub-detail-delete {
  font-size: 11px; padding: 5px 10px; border-radius: 3px; cursor: pointer;
  background: var(--bg, #14181f);
  border: 1px solid var(--border, #2a2f38);
  color: var(--text, #cfd6e0);
}
.sub-detail-break:hover {
  background: rgba(74, 143, 240, 0.16); border-color: #4a8ff0; color: #fff;
}
.sub-detail-delete:hover {
  background: rgba(220, 70, 70, 0.16); border-color: #884040; color: #f59999;
}
.sub-detail-hint {
  margin-top: 6px; font-size: 10px; color: var(--text-muted, #8a93a3);
  font-style: italic;
}

/* Zone groups — collapsible sections keyed on the first word of a zone's
   label. Arena preset yields 4 groups: Court · Concourse · Lower · Upper.
   Group header shows count + avg occupancy + bulk-edit controls. */
.zone-group {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-elev-2, #1c1f24);
  margin-bottom: 0.55rem;
  overflow: hidden;
}
.zone-group-head {
  display: grid;
  grid-template-columns: 16px auto auto 1fr;
  align-items: center;
  gap: 8px;
  padding: 0.55rem 0.75rem;
  cursor: pointer;
  user-select: none;
  background: transparent;
  transition: background 100ms;
}
.zone-group-head:hover { background: rgba(255,255,255,0.03); }
.zg-caret {
  color: var(--muted);
  font-size: 9px;
  line-height: 1;
  text-align: center;
}
.zg-name {
  color: var(--fg-strong);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}
.zg-count {
  color: var(--fg);
  background: #2a2f38;
  border-radius: 10px;
  padding: 0 8px;
  font-size: 0.7rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  min-width: 22px;
  text-align: center;
}
.zg-summary {
  color: var(--muted);
  font-size: 0.7rem;
  justify-self: end;
  font-variant-numeric: tabular-nums;
}
.zone-group.collapsed .zone-group-bulk,
.zone-group.collapsed .zone-group-body { display: none; }
.zone-group-bulk {
  padding: 0.5rem 0.75rem;
  background: rgba(0,0,0,0.22);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.zone-group-bulk label {
  font-size: 0.7rem;
  color: var(--muted);
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0.5rem;
}
.zone-group-bulk .bulk-occ { width: 100%; }
.zone-group-bulk .bulk-occ-readout { min-width: 36px; text-align: right; }
.zone-group-bulk select { width: 100%; }
.zone-group-body {
  padding: 0.4rem 0.55rem 0.55rem;
}

/* Compact zone row (collapsed state) — one-line summary. Click to expand. */
.zone-card.compact {
  display: grid;
  grid-template-columns: 1fr auto 24px;
  align-items: center;
  gap: 8px;
  padding: 0.4rem 0.6rem;
  margin-bottom: 0.3rem;
  cursor: pointer;
  transition: background 100ms;
}
.zone-card.compact:hover { background: rgba(255,255,255,0.025); }
.zone-card.compact .zc-label {
  color: var(--fg);
  font-size: 0.78rem;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.zone-card.compact .zc-meta {
  color: var(--muted);
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.zone-card.compact .btn-select {
  width: 24px;
  height: 24px;
  padding: 0;
  font-size: 0.85rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.zone-card.compact.selected {
  background: rgba(91, 126, 220, 0.08);
}
.source-card .source-header {
  font-size: 0.78rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
  color: var(--fg-strong);
  display: flex;
  justify-content: space-between;
  align-items: center;
  letter-spacing: 0.02em;
}
.source-card.line-array-card { border-left: 3px solid var(--accent); }
.source-card .splay-label { display: block; font-size: 0.72rem; margin-top: 0.4rem; color: var(--muted); }
.source-card .splay-label input { width: 100%; margin-top: 0.22rem; }
.source-card .splay-preview {
  font-size: 0.7rem;
  color: var(--muted);
  margin: 0.3rem 0 0.4rem;
  line-height: 1.45;
  font-variant-numeric: tabular-nums;
}
.source-actions { display: flex; gap: 0.4rem; }
.source-actions .btn-add { flex: 1; margin: 0; }
.btn-add {
  display: block;
  width: 100%;
  padding: 0.45rem;
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  margin-top: 0.35rem;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.btn-add:hover {
  background: var(--accent-dim);
  color: var(--fg-strong);
  border-color: var(--accent);
}
.btn-remove {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  width: 22px;
  height: 22px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  font-family: inherit;
  padding: 0;
  display: grid;
  place-items: center;
}
.btn-remove:hover {
  background: rgba(255, 107, 107, 0.12);
  color: var(--data-red);
  border-color: var(--data-red);
}
.viewport-loading {
  display: grid;
  place-items: center;
  height: 100%;
  color: #99a;
  font-size: 0.85rem;
}
.avatar-loading-overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(20, 24, 32, 0.85);
  color: #cdd2db;
  font-size: 0.9rem;
  font-weight: 500;
  padding: 10px 18px;
  border-radius: 6px;
  border: 1px solid rgba(120, 140, 160, 0.25);
  pointer-events: none;
  z-index: 10;
  letter-spacing: 0.02em;
}
.avatar-loading-overlay::before {
  content: '';
  display: inline-block;
  width: 10px;
  height: 10px;
  margin-right: 8px;
  border: 2px solid rgba(120, 140, 160, 0.3);
  border-top-color: #4aa3ff;
  border-radius: 50%;
  vertical-align: -2px;
  animation: avatar-loading-spin 0.8s linear infinite;
}
@keyframes avatar-loading-spin {
  to { transform: rotate(360deg); }
}

.listener-card.selected {
  border-color: var(--data-amber);
  background: rgba(255, 180, 84, 0.06);
  box-shadow: 0 0 0 1px var(--data-amber), var(--shadow-card);
}
.listener-derived, .note-small { font-size: 0.72rem; color: var(--muted); margin: 0.25rem 0; }
.btn-select {
  width: 100%;
  padding: 0.32rem;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  font-size: 0.72rem;
  color: var(--muted);
  margin-top: 0.35rem;
  letter-spacing: 0.03em;
}
.btn-select:hover {
  background: var(--border);
  color: var(--fg-strong);
  border-color: var(--border-strong);
}
.btn-select.active {
  background: rgba(255, 180, 84, 0.15);
  border-color: var(--data-amber);
  color: var(--data-amber);
  font-weight: 600;
}

.vp-lbl-listener { font-size: 9px; fill: #13161c; font-weight: 700; }
.vp-lbl-minaret  { font-size: 11px; fill: #1a1a1a; font-weight: 600; pointer-events: none; }
.vp-lbl-listener-metrics {
  font-size: 8.5px;
  font-weight: 600;
  fill: #e8ecf2;
  paint-order: stroke;
  stroke: #13161c;
  stroke-width: 2.4px;
  stroke-linejoin: round;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.listener-results {
  padding: 0.55rem 0.7rem;
  background: rgba(255, 180, 84, 0.08);
  border: 1px solid rgba(255, 180, 84, 0.35);
  border-radius: var(--radius-lg);
  margin-bottom: 0.7rem;
  box-shadow: var(--shadow-card);
}
.listener-results .lr-title { font-size: 0.82rem; color: var(--fg-strong); }
.listener-results .lr-sub { font-size: 0.72rem; color: var(--muted); margin-top: 0.2rem; }
.listener-results .lr-note {
  margin-top: 0.45rem;
  padding: 0.4rem 0.55rem;
  background: rgba(255, 140, 60, 0.12);
  border: 1px solid rgba(255, 140, 60, 0.3);
  border-radius: var(--radius);
  color: #ffb48c;
  font-size: 0.72rem;
  line-height: 1.45;
}
.btn-draw {
  display: block;
  width: 100%;
  padding: 0.5rem;
  background: var(--accent);
  color: var(--fg-strong);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  margin-top: 0.45rem;
  transition: background 0.12s;
}
.btn-draw:hover { background: var(--accent-hover); }

.draw-mode {
  background: #0e1116;
  color: #ccd;
  /* Flex column overrides .viewport-2d's grid-template-areas (which
     declares 3 rows: header/svg+legend/note) cleanly. Mixing the
     two grid templates collapsed the canvas to a thin strip because
     the row count and area names didn't match. */
  display: flex;
  flex-direction: column;
  grid-template-areas: none;     /* defensive: cancel the inherited template */
}
.draw-mode > .draw-toolbar { flex: 0 0 auto; }
.draw-mode > .draw-canvas { flex: 1 1 auto; min-height: 0; }
.draw-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.5rem 0.75rem;
  background: #13161c;
  border-bottom: 1px solid #2a2f38;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.draw-hint { color: #cfd6e0; font-size: 0.78rem; line-height: 1.2; }
.draw-hint-ready { color: #ffd000; font-weight: 500; }
.draw-canvas svg:focus { outline: none; }     /* keyboard-focused SVG shouldn't gain a browser default outline */

/* Coordinate input row — appears in the draw toolbar after the first
   vertex is placed. CAD-style "Next point (rel)" prompt so the user
   can type relative coords instead of (or alongside) clicking. */
.draw-coord-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.75rem;
  background: #15191f;
  border-bottom: 1px solid #2a2f38;
  font-size: 0.76rem;
  color: #cfd6e0;
  flex-wrap: wrap;
}
.draw-coord-row label {
  color: var(--muted);
  font-weight: 500;
  letter-spacing: 0.01em;
}
.draw-coord-row input {
  padding: 0.32rem 0.6rem;
  border: 1px solid #2a2f38;
  background: #0d1015;
  color: var(--fg-strong, #e5e7eb);
  border-radius: 4px;
  font: inherit;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  width: 9rem;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.draw-coord-row input:focus {
  outline: none;
  border-color: var(--accent, #4aa3ff);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.18);
}
.draw-coord-row input.draw-coord-err {
  border-color: #ef4444;
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.20);
}
.draw-coord-row button {
  padding: 0.28rem 0.7rem;
  border: 1px solid #2a2f38;
  background: #1a1f27;
  color: #cde;
  border-radius: 4px;
  cursor: pointer;
  font: inherit;
  font-size: 0.76rem;
}
.draw-coord-row button:hover { background: #2a2f38; color: #fff; }
.draw-coord-row button kbd {
  display: inline-block;
  margin-left: 4px;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 3px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.62rem;
  color: #cfd3d9;
}
.draw-coord-help {
  color: var(--muted);
  font-size: 0.72rem;
  font-style: italic;
}

/* ---------------------------------------------------------------
   Floating coord-entry panel — CAD-style "next point (x, y)" input
   that follows the cursor while the user is drawing a custom room.
   Replaces the toolbar coord row; mounted on #viewport (NOT
   #view-2d, whose innerHTML is rewritten per frame and would
   destroy the input + typing focus). Position math anchors to the
   .draw-canvas inside #view-2d so auto-flip respects the actual
   drawing region, not the surrounding chrome.
   --------------------------------------------------------------- */
/* Centered modal-style prompt — shown after a custom-room loop closes
   so the user can set the ceiling height with one keystroke. Lives on
   #viewport (absolute-positioned) and centers via translate(-50%,-50%). */
.draw-height-prompt {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 50;
  min-width: 220px;
  background: var(--bg-elev, #1c2028);
  border: 1px solid var(--accent, #4aa3ff);
  border-radius: 8px;
  padding: 14px 16px 12px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6),
              0 0 0 4px rgba(74, 163, 255, 0.10);
  text-align: center;
  animation: drawHeightPromptIn 160ms ease-out;
}
@keyframes drawHeightPromptIn {
  from { opacity: 0; transform: translate(-50%, -52%) scale(0.96); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.draw-height-prompt-title {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--fg-strong, #e5e7eb);
  letter-spacing: 0.02em;
  margin-bottom: 8px;
}
.draw-height-prompt-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-bottom: 8px;
}
.draw-height-prompt-row input {
  width: 6ch;
  padding: 5px 7px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--fg-strong, #e5e7eb);
  background: #0d1015;
  border: 1px solid var(--border, #2a2f38);
  border-radius: 4px;
  text-align: right;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.draw-height-prompt-row input:focus {
  outline: none;
  border-color: var(--accent, #4aa3ff);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.20);
}
.draw-height-prompt-row input.draw-height-prompt-err {
  border-color: #ef4444;
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.25);
  animation: drawHeightPromptShake 360ms ease-in-out;
}
@keyframes drawHeightPromptShake {
  0%, 100% { transform: translateX(0); }
  20%, 60% { transform: translateX(-3px); }
  40%, 80% { transform: translateX(3px); }
}
.draw-height-prompt-unit {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.82rem;
  color: var(--muted, #8a8e99);
}
.draw-height-prompt-hint {
  font-size: 0.68rem;
  color: var(--muted, #8a8e99);
  letter-spacing: 0.01em;
}
.draw-height-prompt-hint kbd {
  display: inline-block;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 3px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.62rem;
  color: #cfd3d9;
}
.draw-height-prompt-sep { opacity: 0.4; margin: 0 4px; }

.draw-float-coord {
  position: absolute;
  z-index: 40;
  /* Wrapper itself is click-through — only the inputs catch clicks.
     This lets the user mouse-click to place a vertex in the area the
     panel happens to overlap without first having to mouse-out of the
     panel chrome. A classic CAD-tool affordance. */
  pointer-events: none;
  background: var(--bg-elev, #1c2028);
  border: 1px solid var(--border, #2a2f38);
  border-radius: 6px;
  padding: 6px 8px 5px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45),
              0 1px 2px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Make the panel feel weightless — no movement transition on
     left/top (would lag the cursor), only a fade-in on first paint. */
  animation: drawFloatCoordIn 120ms ease-out;
}
@keyframes drawFloatCoordIn {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}
.draw-float-coord-row {
  display: flex;
  align-items: center;
  gap: 4px;
}
.draw-float-coord-label {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.72rem;
  color: var(--muted, #8a8e99);
  user-select: none;
}
.draw-float-coord-input {
  /* Inputs DO catch clicks (wrapper is click-through) so the user can
     focus the field with the mouse if they prefer. */
  pointer-events: auto;
  width: 5ch;
  padding: 3px 5px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--fg-strong, #e5e7eb);
  background: #0d1015;
  border: 1px solid var(--border, #2a2f38);
  border-radius: 3px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.draw-float-coord-input:focus {
  outline: none;
  border-color: var(--accent, #4aa3ff);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.20);
}
.draw-float-coord-unit {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.68rem;
  color: var(--muted, #8a8e99);
  margin-right: 4px;
  user-select: none;
}
.draw-float-coord-input.draw-float-coord-err {
  border-color: #ef4444;
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.22);
  animation: drawFloatCoordShake 220ms ease-in-out;
}
@keyframes drawFloatCoordShake {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-2px); }
  75%      { transform: translateX(2px); }
}
/* Typed coords would land within the close-radius — flag both fields
   green so the user knows pressing Enter here will CLOSE the loop,
   not place a new vertex on top of the origin. */
.draw-float-coord-input.draw-float-coord-input-close {
  border-color: #1faa5e;
  box-shadow: 0 0 0 2px rgba(31, 170, 94, 0.22);
  background: #0c1812;
}
.draw-float-coord-hint {
  font-size: 0.66rem;
  color: var(--muted, #8a8e99);
  letter-spacing: 0.01em;
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}
.draw-float-coord-hint kbd {
  display: inline-block;
  padding: 0 4px;
  min-width: 14px;
  text-align: center;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 3px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.60rem;
  color: #cfd6e0;
  line-height: 1.4;
}
.draw-float-coord-sep {
  color: #3a4150;
  margin: 0 1px;
}
/* Cursor is in the close-radius — entire panel shifts to a green
   "click to close" affordance. The hint text is replaced via the
   ::before pseudo so we don't have to touch the JS template; the
   inputs stay usable (a typed pair still commits via Enter). */
.draw-float-coord.draw-float-coord-ready-close {
  border-color: #1faa5e;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45),
              0 0 0 2px rgba(31, 170, 94, 0.30);
}
.draw-float-coord.draw-float-coord-ready-close .draw-float-coord-hint {
  color: #6ee0a0;
}
.draw-float-coord.draw-float-coord-ready-close .draw-float-coord-hint::before {
  content: "click to close the room  ";
  color: #6ee0a0;
  font-weight: 500;
  letter-spacing: 0.02em;
  margin-right: 6px;
}
.draw-actions { display: flex; gap: 0.3rem; }
.draw-actions button {
  padding: 0.28rem 0.7rem;
  border: 1px solid #2a2f38;
  background: #1a1f27;
  color: #cde;
  border-radius: 4px;
  cursor: pointer;
  font: inherit;
  font-size: 0.76rem;
}
.draw-actions button:hover:not(:disabled) { background: #2a2f38; color: #fff; }
/* Inline keyboard-shortcut chips inside the draw-action buttons. */
.draw-actions button kbd {
  display: inline-block;
  margin-left: 4px;
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 3px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.62rem;
  font-weight: 500;
  color: #cfd6e0;
  letter-spacing: 0.02em;
  text-transform: none;
}
.draw-actions button:disabled kbd { opacity: 0.5; }
.draw-actions button:disabled { opacity: 0.4; cursor: not-allowed; }
.draw-actions #btn-draw-finish { background: #0a7b3e; border-color: #0a7b3e; color: #fff; }
.draw-actions #btn-draw-finish:hover:not(:disabled) { background: #0c9249; }
.draw-actions #btn-draw-cancel { background: transparent; color: #c04a10; border-color: #c04a10; }
.draw-canvas { overflow: hidden; }
.draw-canvas svg { width: 100%; height: 100%; cursor: crosshair; display: block; }

.vertex-list { display: grid; gap: 0.3rem; margin-top: 0.3rem; }
.vertex-row {
  display: grid;
  grid-template-columns: auto 1fr 1fr auto;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.76rem;
}
.vertex-row .vertex-idx {
  background: #4a8ff0;
  color: #fff;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  font-size: 0.7rem;
  font-weight: 600;
}
.vertex-row label {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.15rem;
}
.vertex-row input {
  padding: 0.25rem 0.35rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  background: var(--bg-elev);
  color: var(--fg);
  min-width: 0;
}

.vp-lbl-edge { font-size: 10px; fill: #fff; font-weight: 600; }
.vp-lbl-zone { font-size: 13px; font-weight: 700; paint-order: stroke; stroke: #13161c; stroke-width: 3px; }
.vp-lbl-zone-sub { font-size: 10px; font-weight: 500; fill: #cce; paint-order: stroke; stroke: #13161c; stroke-width: 2px; }

.zone-card.selected {
  background: rgba(168, 132, 240, 0.08);
  border-color: rgba(168, 132, 240, 0.45);
}
.zone-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 0.35rem; margin-top: 0.4rem; }
.btn-redraw {
  padding: 0.32rem;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  font-size: 0.72rem;
  color: var(--muted);
  letter-spacing: 0.03em;
}
.btn-redraw:hover {
  background: var(--border);
  color: var(--fg-strong);
  border-color: var(--border-strong);
}
.derived { color: var(--muted); font-size: 0.72rem; font-variant-numeric: tabular-nums; }

#zones-table th, #zones-table td { padding: 0.35rem; border-bottom: 1px solid var(--border); text-align: right; }
#zones-table th:first-child, #zones-table td:first-child { text-align: left; }
#zones-table .sub { color: var(--muted); font-size: 0.68rem; }

.group-badge {
  display: inline-block;
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 0.05rem 0.4rem;
  border-radius: 3px;
  vertical-align: middle;
  margin-left: 0.2rem;
  letter-spacing: 0.04em;
}
.lr-breakdown .group-row td {
  background: rgba(255, 255, 255, 0.04);
  border-top: 2px solid var(--border-strong);
  font-size: 0.78rem;
  color: var(--fg-strong);
}

.badge-warn {
  display: inline-block;
  background: #ff5a3c;
  color: #fff;
  font-size: 0.66rem;
  font-weight: 600;
  padding: 0.05rem 0.35rem;
  border-radius: 3px;
  vertical-align: middle;
  margin-left: 0.3rem;
  letter-spacing: 0.02em;
}
.listener-results .lr-total { margin-top: 0.5rem; }
.listener-results .big-num {
  font-size: 2.1rem;
  font-weight: 300;
  color: var(--data-amber);
  letter-spacing: -0.01em;
}
.listener-results .sub { font-size: 0.72rem; color: var(--muted); }
.lr-breakdown th {
  text-align: left;
  font-weight: 600;
  color: var(--muted);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.3rem 0.35rem;
  border-bottom: 1px solid var(--border-strong);
}
.lr-breakdown td { padding: 0.25rem 0.35rem; border-bottom: 1px solid var(--border); color: var(--fg); }
.lr-breakdown td:nth-child(2), .lr-breakdown td:nth-child(3) { text-align: right; }
.source-row { display: grid; gap: 0.3rem; margin-bottom: 0.4rem; }
.source-row.triplet { grid-template-columns: 1fr 1fr 1fr; }
.source-row.duo     { grid-template-columns: 1fr 1fr; }
.source-row label {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.2rem;
  font-size: 0.72rem;
  color: var(--muted);
}
.source-row label input {
  padding: 0.28rem 0.35rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  background: var(--bg-elev);
  color: var(--fg);
  min-width: 0;
}
.source-row label input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(74, 163, 255, 0.18);
}
.source-row label .unit { color: var(--muted); font-size: 0.68rem; }

#spl-table th, #spl-table td { padding: 0.3rem 0.4rem; border-bottom: 1px solid var(--border); }
#spl-table th { text-align: left; font-weight: 600; color: var(--muted); font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.08em; }
#spl-table td { text-align: right; }

.vp-lbl-spk { font-size: 11px; fill: #e8ecf2; font-weight: 600; }
.vp-legend.spl-legend {
  align-items: center;
  gap: 0.45rem;
  justify-content: center;
}
.vp-legend.spl-legend .legend-bar {
  width: 180px;
  height: 10px;
  border-radius: 2px;
  background: linear-gradient(to right, #1428b4, #008ce6, #1edc50, #ffd700, #f01e1e);
}
.vp-legend.spl-legend .legend-range { font-size: 0.7rem; color: #99a; }
.vp-legend.spl-legend .legend-label { font-size: 0.72rem; color: #c0c4d0; font-weight: 500; }

/* Vertical SPL legend (Sofia v1) — single source of truth with the
   3D viewport and the print-report heatmap legend. Width 56 px, 14 px
   colour swatch, 4 px tick, 6 px gap, 10 px label. Numeric ticks come
   from computeTicks() so the same data shows the same scale across
   2D, 3D, and PDF. */
/* Maya v9 audit §1 — header ABOVE the bar (not orphaned beside it),
   left-aligned to bar's left edge. Footnote underneath gives the dB
   its physical reference. Tick labels right-aligned in a 50 px
   column so digits column-align (95 dB / 100 dB / 105 dB). */
.vp-legend.spl-legend-v {
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 6px 4px 4px;
}
.vp-legend.spl-legend-v .legend-header {
  font-size: 0.66rem;
  color: var(--muted);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0;
  white-space: nowrap;
}
.vp-legend.spl-legend-v .spl-legend-stage {
  position: relative;
  display: flex;
  align-items: stretch;
  height: 180px;
}
.vp-legend.spl-legend-v .legend-bar {
  width: 14px;
  height: 100%;
  border-radius: 2px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: linear-gradient(to top, #1428b4 0%, #008ce6 25%, #1edc50 50%, #ffd700 75%, #f01e1e 100%);
  position: relative;
}
/* Data-extent bracket (Phase 11a, 2026-05-25).
   See identical block under .spl-legend-3d in this file. */
.vp-legend.spl-legend-v .legend-bar .spl-legend-data-bracket {
  position: absolute;
  left: -2px;
  right: -2px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 2px;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35);
}
.vp-legend.spl-legend-v .spl-legend-data-caption {
  font-size: 0.6rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
/* Low-frequency modal / statistical disclosure caption under the SPL legend.
   Amber to read as an advisory, wraps to a couple of short lines. */
.vp-legend.spl-legend-v .spl-legend-lf-caption {
  display: block;
  max-width: 150px;
  margin-top: 3px;
  font-size: 0.56rem;
  line-height: 1.25;
  color: var(--data-amber, #ffb454);
  letter-spacing: 0.01em;
}
.vp-legend.spl-legend-v .spl-legend-ticks {
  position: relative;
  width: 50px;
  margin-left: 6px;
  height: 100%;
}
.vp-legend.spl-legend-v .spl-legend-tick {
  position: absolute;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.vp-legend.spl-legend-v .spl-legend-tick-line {
  width: 4px;
  height: 1px;
  background: var(--muted);
  flex: 0 0 4px;
}
/* Minor (unlabeled) graduation — shorter line, lower contrast, no
   label slot needed. Renders as ruler-style sub-ticks. */
.vp-legend.spl-legend-v .spl-legend-tick.minor .spl-legend-tick-line {
  width: 2px;
  flex: 0 0 2px;
  opacity: 0.45;
}
.vp-legend.spl-legend-v .spl-legend-tick-label {
  font-size: 0.66rem;
  color: var(--fg);
  line-height: 1;
}
.vp-legend.spl-legend-v .legend-footnote {
  font-size: 0.6rem;
  color: var(--muted);
  letter-spacing: 0.02em;
  margin: 0;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ */
/* Phase E — Precision Render panel                                   */
/* ------------------------------------------------------------------ */
#panel-precision {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
#panel-precision h2 {
  color: var(--fg-strong);
  font-size: 0.88rem;
  letter-spacing: 0.05em;
  margin: 0 0 var(--space-2);
}
.precision-intro {
  font-size: 0.72rem;
  color: var(--muted);
  line-height: 1.45;
  margin-bottom: var(--space-2);
}
.precision-controls {
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
  margin-bottom: var(--space-2);
}
.precision-controls .rays-label {
  flex: 1;
  font-size: 0.7rem;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.precision-controls input[type=number] {
  width: 100%;
  padding: 0.35rem 0.5rem;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  color: var(--fg);
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}
.btn-render, .btn-cancel {
  padding: 0.45rem 0.9rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  letter-spacing: 0.03em;
}
/* Precision render button — red glass treatment. Uses a translucent
   red fill + subtle inner highlight so it reads as a 'heavy action'
   button (precision render is a 1-10 s blocking compute). Hover
   deepens the red and lifts the highlight. */
.btn-render {
  background:
    linear-gradient(180deg, rgba(255, 90, 90, 0.18) 0%, rgba(170, 30, 30, 0.42) 100%),
    rgba(140, 30, 30, 0.55);
  color: #fff5f5;
  border-color: rgba(255, 110, 110, 0.55);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18),
    0 1px 2px rgba(140, 30, 30, 0.25);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
  transition: background 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
}
.btn-render:hover {
  background:
    linear-gradient(180deg, rgba(255, 120, 120, 0.28) 0%, rgba(200, 40, 40, 0.55) 100%),
    rgba(170, 35, 35, 0.65);
  border-color: rgba(255, 140, 140, 0.75);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.28),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18),
    0 2px 4px rgba(170, 35, 35, 0.32);
}
.btn-render:active {
  background:
    linear-gradient(180deg, rgba(180, 35, 35, 0.55) 0%, rgba(140, 25, 25, 0.62) 100%),
    rgba(120, 25, 25, 0.7);
  box-shadow:
    inset 0 2px 4px rgba(0, 0, 0, 0.32),
    0 0 0 rgba(0, 0, 0, 0);
}
.btn-cancel {
  background: transparent;
  color: #ff8080;
  border-color: #aa4040;
}
.btn-cancel:hover { background: rgba(200, 64, 64, 0.15); }
.btn-link {
  background: transparent;
  border: none;
  color: var(--accent);
  padding: 2px 6px;
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
  text-decoration: underline;
}
.btn-link:hover { color: #7ab9ff; }

.precision-progress {
  margin: var(--space-2) 0;
}
.precision-progress .progress-bar {
  height: 4px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 4px;
  overflow: hidden;
}
.precision-progress .progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #3a5a8a 0%, #5a7ab0 100%);
  transition: width 120ms ease-out;
}
.precision-progress .progress-text {
  margin-top: 4px;
  font-size: 0.7rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.precision-stale {
  margin: var(--space-2) 0;
  padding: 0.55rem 0.7rem;
  background: rgba(230, 120, 40, 0.08);
  border: 1px solid rgba(230, 120, 40, 0.30);
  border-radius: var(--radius);
  font-size: 0.72rem;
  color: #e67828;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.precision-results {
  margin-top: var(--space-2);
}
.precision-listener-line {
  font-size: 0.72rem;
  color: var(--muted);
  margin-bottom: 0.4rem;
}
.precision-listener-line strong { color: var(--fg); }

.precision-big-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.big-metric {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.55rem 0.7rem;
  text-align: center;
}
.big-metric .big-val {
  font-size: 1.8rem;
  font-weight: 300;
  letter-spacing: -0.01em;
  color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.big-metric .big-sub {
  font-size: 0.68rem;
  color: var(--muted);
  margin-top: 2px;
  letter-spacing: 0.04em;
}
.sti-excellent { color: var(--data-green) !important; }
.sti-good      { color: var(--data-green) !important; }
.sti-fair      { color: var(--data-amber) !important; }
.sti-poor      { color: #ff8c3c !important; }
.sti-bad       { color: var(--data-red) !important; }

.precision-table {
  width: 100%;
  font-size: 0.74rem;
  margin-bottom: 0.6rem;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}
.precision-table th {
  color: var(--muted);
  font-weight: 500;
  text-align: left;
  padding: 3px 6px 3px 0;
  width: 18%;
}
.precision-table td {
  color: var(--fg);
  padding: 3px 12px 3px 0;
  text-align: right;
  width: 32%;
}

.precision-per-band {
  margin: 0.5rem 0;
  font-size: 0.7rem;
}
.precision-per-band summary {
  color: var(--muted);
  cursor: pointer;
  padding: 4px 0;
  list-style: revert;
}
.precision-per-band summary:hover { color: var(--fg); }
.precision-per-band .band-table {
  width: 100%;
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
  border-collapse: collapse;
}
.precision-per-band .band-table th,
.precision-per-band .band-table td {
  padding: 2px 4px;
  text-align: right;
  font-size: 0.68rem;
}
.precision-per-band .band-table thead th {
  color: var(--muted-dim);
  border-bottom: 1px solid var(--border);
  font-weight: 500;
}
.precision-per-band .band-table tbody th {
  text-align: left;
  color: var(--muted);
  font-weight: 500;
}
.precision-per-band .band-table tbody td { color: var(--fg); }

.precision-meta {
  font-size: 0.66rem;
  color: var(--muted-dim);
  font-variant-numeric: tabular-nums;
  margin-top: 0.5rem;
}

.precision-echogram-wrap {
  margin-top: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.precision-echogram-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.precision-echogram-title {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.2px;
  color: var(--fg, #e6edf3);
}
.precision-echogram-sub {
  font-size: 10.5px;
  color: var(--fg-mid, #8a929c);
  font-style: italic;
}
.precision-echogram-zoom {
  display: block;
  width: 100%;
  padding: 0;
  margin: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: zoom-in;
  position: relative;
  overflow: hidden;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.precision-echogram-zoom:hover {
  border-color: rgba(74, 163, 255, 0.55);
  box-shadow: 0 0 0 1px rgba(74, 163, 255, 0.25);
}
.precision-echogram-zoom:focus-visible {
  outline: 2px solid var(--accent, #4aa3ff);
  outline-offset: 2px;
}
.precision-echogram-zoom::after {
  content: '⤢ enlarge';
  position: absolute;
  top: 6px;
  left: 8px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: rgba(255, 255, 255, 0.55);
  background: rgba(15, 18, 24, 0.62);
  padding: 2px 6px;
  border-radius: 3px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 140ms ease;
}
.precision-echogram-zoom:hover::after { opacity: 1; }
.precision-echogram {
  display: block;
  width: 100%;
  height: auto;
  background: #0a0d12;
}

/* Echogram modal — large preview when the mini view is clicked. */
.echogram-modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 12000;
  background: rgba(8, 10, 14, 0.78);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: echogramModalIn 180ms ease-out;
}
@keyframes echogramModalIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.echogram-modal-card {
  background: rgba(20, 24, 32, 0.96);
  border: 1px solid rgba(74, 163, 255, 0.45);
  border-radius: 8px;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
  max-width: 980px;
  width: 100%;
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
  overflow: auto;
}
.echogram-modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.echogram-modal-title {
  font-family: 'Inter Tight', system-ui, sans-serif;
  font-size: 14px;
  font-weight: 600;
  color: var(--fg, #e6edf3);
  line-height: 1.3;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.echogram-modal-sub {
  font-size: 11px;
  font-weight: 400;
  font-style: italic;
  color: var(--fg-mid, #8a929c);
}
.echogram-modal-close {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--fg, #e6edf3);
  font-size: 18px;
  line-height: 1;
  width: 28px;
  height: 28px;
  border-radius: 4px;
  cursor: pointer;
  flex: 0 0 auto;
}
.echogram-modal-close:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.25);
}
.echogram-modal-canvas {
  display: block;
  width: 100%;
  height: auto;
  background: #0a0d12;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 4px;
}
.echogram-modal-foot {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--fg-mid, #b9bfc8);
}

.precision-audition {
  margin-top: var(--space-3);
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: rgba(20, 24, 32, 0.4);
}
.audition-advisory {
  font-size: 11px;
  color: var(--muted-dim, #8a93a3);
  margin-bottom: 6px;
  line-height: 1.4;
}
.audition-schroeder {
  font-size: 10.5px;
  color: var(--muted-dim, #8a93a3);
  font-style: italic;
  margin-bottom: 6px;
  padding: 6px 8px;
  background: rgba(74, 163, 255, 0.06);
  border-left: 2px solid rgba(74, 163, 255, 0.4);
  border-radius: 3px;
  line-height: 1.4;
}

.treatment-preset-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 6px 0 10px 0;
  padding: 6px 8px;
  background: rgba(74, 163, 255, 0.04);
  border-radius: 4px;
}
.treatment-preset-label {
  font-size: 11px;
  color: var(--muted-dim, #8a93a3);
  white-space: nowrap;
}
.treatment-preset-select {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  padding: 3px 6px;
  background: var(--bg-elev, #1c2028);
  border: 1px solid var(--border, #2a2f38);
  border-radius: 3px;
  color: #cdd2db;
}
.audition-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.audition-sample-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  font-size: 12px;
}
.audition-sample-row[hidden] { display: none; }
.audition-sample-label {
  color: var(--text-muted, #8a8f99);
  font-weight: 500;
}
.audition-sample-select {
  background: var(--bg-elev, #1c2028);
  color: var(--text, #cdd2db);
  border: 1px solid var(--border, #2c313a);
  border-radius: 4px;
  padding: 3px 6px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
}
.audition-sample-select:hover { border-color: var(--accent, #4a9eff); }
.btn-audition {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--bg-elev, #1c2028);
  border: 1px solid var(--border, #2a2f38);
  border-radius: 4px;
  color: #cdd2db;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.btn-audition:hover:not(:disabled) {
  background: rgba(74, 163, 255, 0.12);
  border-color: rgba(74, 163, 255, 0.4);
}
.btn-audition:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.audition-icon {
  font-size: 11px;
  color: #4aa3ff;
}
.audition-state {
  font-size: 11px;
  color: var(--muted-dim, #8a93a3);
  font-style: italic;
}

.precision-error {
  margin-top: var(--space-2);
  padding: 0.5rem 0.7rem;
  background: rgba(255, 80, 80, 0.08);
  border: 1px solid rgba(255, 80, 80, 0.30);
  color: #ff8080;
  font-size: 0.72rem;
  border-radius: var(--radius);
}

.precision-hint {
  font-size: 0.75rem;
  color: var(--muted);
  padding: var(--space-2);
  font-style: italic;
}

/* ====================================================================
   SurfaceLAB — Lab #4
   ==================================================================== */

/* Per-rail catalogue list — one list per panel (seven panels, one per
   mechanism). Each rail icon opens a separate panel; previously a
   single Library panel held a 5-deep accordion which was too long to
   scroll. */
.surface-panel-count {
  font-size: 0.66rem;
  color: var(--muted);
  font-weight: 400;
  margin-left: 6px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
}
.surface-empty {
  padding: 16px 12px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px dashed rgba(255, 255, 255, 0.06);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 0.78rem;
}
.surface-empty p { margin: 0; }
.surface-empty-sub {
  font-size: 0.7rem;
  font-style: italic;
  margin-top: 6px !important;
  color: var(--muted);
}
.surface-cat-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 4px 0 8px;
}
.surface-cat-row {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 6px 8px;
  text-align: left;
  cursor: pointer;
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 6px;
  font: inherit;
  color: var(--fg);
  transition: background-color 100ms ease, border-color 100ms ease;
}
.surface-cat-row:hover {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.06);
}
.surface-cat-row.active {
  background: rgba(116, 208, 255, 0.10);
  border-color: rgba(116, 208, 255, 0.35);
}
.surface-cat-mfr {
  font-size: 0.62rem;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  grid-column: 1 / 4;
  margin-bottom: -2px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
/* Compact floor/wall/ceiling applicability badge in a catalogue row. */
.surface-cat-use {
  font-size: 0.6rem;
  color: var(--muted);
  letter-spacing: 0.08em;
  opacity: 0.8;
  flex: 0 0 auto;
}
.surface-cat-name {
  font-size: 0.78rem;
  color: var(--fg);
  grid-column: 1;
}
.surface-cat-headline {
  font-size: 0.72rem;
  color: #74d0ff;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  grid-column: 2;
}
.surface-cat-flag {
  color: #ffaa55;
  font-size: 0.85rem;
  grid-column: 3;
}
.surface-cat-flag-high {
  color: #ff7070;
}

/* Floor/wall/ceiling applicability chips under the title-bar description. */
.surface-use-chips {
  margin-top: 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.surface-use-chip {
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(116, 208, 255, 0.12);
  border: 1px solid rgba(116, 208, 255, 0.30);
  color: #aee0ff;
}
.surface-use-chip-none {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.10);
  color: var(--muted);
}
.surface-use-none {
  color: var(--muted);
  font-style: italic;
}

/* Title bar headline figure — appended to the right of the title block
   (a glass overlay floats over the 3D canvas). */
.surface-headline {
  margin-top: 4px;
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.surface-h-big {
  font-size: 1.5rem;
  font-weight: 300;
  color: #74d0ff;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  letter-spacing: -0.01em;
}
.surface-h-unit {
  font-size: 0.7rem;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.surface-trust-chips {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: flex-start;
}
.surface-flag-chip {
  font-size: 0.62rem;
  padding: 2px 6px;
  border-radius: 9999px;
  background: rgba(255, 170, 85, 0.14);
  color: #ffc888;
  border: 1px solid rgba(255, 170, 85, 0.4);
  cursor: help;
}
.surface-flag-chip.surface-flag-high {
  background: rgba(255, 100, 100, 0.18);
  color: #ff9090;
  border-color: rgba(255, 100, 100, 0.5);
}

/* Right-rail panels — Specs, Absorption, Polar, Cross-section */
#panel-specs .surface-headline-block {
  padding: 10px 0 14px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}
#panel-specs .surface-headline-value {
  font-family: "JetBrains Mono", ui-monospace, monospace;
}
#panel-specs .surface-headline-value .surface-h-big {
  font-size: 2.4rem;
  color: var(--fg-strong, #fff);
  display: inline-block;
  margin-right: 6px;
}
.surface-flag-block {
  margin: 0 0 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.surface-flag-row {
  display: flex;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--radius);
  background: rgba(255, 170, 85, 0.08);
  border-left: 2px solid #ffaa55;
  font-size: 0.74rem;
  line-height: 1.4;
  color: var(--fg);
}
.surface-flag-row.surface-flag-high {
  background: rgba(255, 100, 100, 0.10);
  border-left-color: #ff7070;
}
.surface-flag-icon { color: #ffaa55; flex: 0 0 auto; }
.surface-flag-row.surface-flag-high .surface-flag-icon { color: #ff7070; }
.surface-flag-msg { color: var(--muted); }

.surface-spec-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
  margin-bottom: 12px;
}
.surface-spec-table tr {
  border-bottom: 1px dotted rgba(255, 255, 255, 0.06);
}
.surface-spec-table th {
  text-align: left;
  font-weight: 400;
  color: var(--muted);
  padding: 4px 0;
  width: 50%;
}
.surface-spec-table td {
  padding: 4px 0;
  text-align: right;
  color: var(--fg);
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.74rem;
}
.surface-cite {
  font-size: 0.66rem;
  color: var(--muted);
  font-style: italic;
  margin-top: 8px;
  line-height: 1.4;
}
.surface-note {
  font-size: 0.74rem;
  color: var(--muted);
  margin: 0 0 10px;
  line-height: 1.4;
}

/* Charts — alpha + diffusion + polar */
.surface-alpha-chart,
.surface-diff-chart,
.surface-polar-chart {
  width: 100%;
  height: auto;
  display: block;
  margin-bottom: 10px;
}
.surface-nrc-stripe {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 10px;
  background: rgba(116, 208, 255, 0.06);
  border-radius: var(--radius);
  border-left: 2px solid #74d0ff;
}
.surface-nrc-label {
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  text-transform: uppercase;
}
.surface-nrc-value {
  font-size: 1.2rem;
  font-weight: 300;
  color: #74d0ff;
  font-family: "JetBrains Mono", ui-monospace, monospace;
}
.surface-nrc-note {
  font-size: 0.66rem;
  color: var(--muted);
  font-style: italic;
}

/* Cross-section / construction layers */
.surface-layers {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.surface-layer {
  display: grid;
  grid-template-columns: 18px 1fr;
  gap: 8px;
  padding: 6px 8px;
  background: rgba(255, 255, 255, 0.02);
  border-radius: var(--radius);
  border-left: 2px solid rgba(255, 255, 255, 0.08);
  align-items: start;
}
.surface-layer-color {
  width: 14px;
  height: 14px;
  margin-top: 2px;
  border-radius: 2px;
  border: 1px solid rgba(255, 255, 255, 0.15);
}
.surface-layer-name {
  font-size: 0.78rem;
  color: var(--fg);
  font-weight: 500;
}
.surface-layer-role {
  font-size: 0.7rem;
  color: var(--muted);
  font-style: italic;
  margin-top: 2px;
  line-height: 1.4;
}

/* SurfaceLAB — Library search input */
.surface-search-wrap {
  margin: 6px 0 10px;
}
.surface-search-input {
  width: 100%;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius);
  padding: 6px 10px;
  font: inherit;
  font-size: 0.78rem;
  color: var(--fg);
  outline: none;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.surface-search-input::placeholder { color: var(--muted); }
.surface-search-input:focus {
  border-color: rgba(116, 208, 255, 0.45);
  background: rgba(116, 208, 255, 0.06);
}

/* SurfaceLAB — Filter panel (chip cloud) */
.surface-filter-group {
  margin-bottom: 14px;
}
.surface-filter-group h3 {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 6px;
}
.surface-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.surface-filter-chip {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 9999px;
  padding: 4px 10px;
  font: inherit;
  font-size: 0.7rem;
  color: var(--fg);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 100ms ease, border-color 100ms ease;
}
.surface-filter-chip:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.16);
}
.surface-filter-chip.active {
  background: rgba(116, 208, 255, 0.18);
  border-color: rgba(116, 208, 255, 0.5);
  color: #cfe8ff;
}
.surface-filter-actions {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.surface-filter-clear {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  padding: 6px 12px;
  font: inherit;
  font-size: 0.72rem;
  color: var(--muted);
  cursor: pointer;
  width: 100%;
  transition: color 100ms ease, border-color 100ms ease;
}
.surface-filter-clear:hover {
  color: var(--fg);
  border-color: rgba(255, 255, 255, 0.2);
}

/* ====================================================================
   Terms-of-use modal — Sofia spec, Lin copy. Mounted from js/main.js
   on every page load. Heavier glass than the rail/header chrome
   (28px blur vs 18px) so the modal feels like an interruption worth
   reading, not another floating panel.
   ==================================================================== */

.terms-modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 10000;                /* above everything: header (30), rails (10), modal (10000) */
  background: rgba(8, 10, 14, 0.62);
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  animation: terms-scrim-in 240ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@supports not ((backdrop-filter: blur(8px)) or (-webkit-backdrop-filter: blur(8px))) {
  .terms-modal-scrim { background: rgba(8, 10, 14, 0.92); }
}
.terms-modal-scrim.terms-modal-exit {
  animation: terms-scrim-out 300ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes terms-scrim-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes terms-scrim-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

.terms-card {
  position: relative;
  width: min(860px, calc(100vw - 48px));
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  padding: 30px 44px 24px;
  border-radius: 18px;
  background: rgba(20, 22, 28, 0.58);
  backdrop-filter: blur(28px) saturate(160%);
  -webkit-backdrop-filter: blur(28px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.09);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    inset 0 -1px 0 rgba(0, 0, 0, 0.25),
    0 30px 80px -20px rgba(0, 0, 0, 0.55);
  color: #f1f4f8;
  font-family: "Inter Tight", "IBM Plex Sans", -apple-system, system-ui, sans-serif;
  transform: translateY(-2%);     /* Sofia §1 — 2% above optical centre */
  transition: opacity 280ms cubic-bezier(0.4, 0, 0.2, 1),
              transform 280ms cubic-bezier(0.4, 0, 0.2, 1),
              max-height 240ms cubic-bezier(0.4, 0, 0.2, 1);
}
@supports not ((backdrop-filter: blur(28px)) or (-webkit-backdrop-filter: blur(28px))) {
  .terms-card { background: rgba(20, 22, 28, 0.96); }
}
.terms-card.terms-card-fade-out {
  opacity: 0;
  transform: translateY(calc(-2% - 6px));
}
.terms-card.terms-card-relax {
  max-height: 460px;              /* compresses to the acknowledgement card (taller now — holds 5-row fingerprint grid) */
  opacity: 1;
  transform: translateY(-2%);
}

.terms-heading {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.30;
  letter-spacing: -0.01em;
  color: #f1f4f8;
  margin: 0 0 12px 0;
}
.terms-divider {
  height: 1px;
  background: rgba(255, 255, 255, 0.06);
  margin: 0 0 14px 0;
}
.terms-bullets {
  list-style: none;
  padding: 0;
  margin: 0 0 20px 0;
  /* 2-column layout on the wide card. The 4 bullets read as 2 rows
     of 2, which fits without scrolling on a standard laptop viewport
     while keeping each line at a comfortable ~60-70 char measure. */
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 32px;
  row-gap: 16px;
}
.terms-bullets li {
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.50;
  color: #aab3bf;
  margin: 0;
}
.terms-bullets li strong {
  font-weight: 600;
  color: #e6ebf2;
  display: block;
  margin-bottom: 4px;
}
.terms-callout {
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: #8a94a3;
  margin-top: 6px;
  line-height: 1.45;
}

/* Operator-name input row — sits between the bullet list and the
   accept button. Required field; accept stays disabled until this
   is non-empty AND the countdown has finished. Glass aesthetic
   matches the surrounding card. */
.terms-operator-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 0 16px 0;
}
.terms-operator-label {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #cfd6e0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.terms-operator-hint {
  font-size: 11px;
  font-weight: 400;
  color: #7e8693;
  letter-spacing: 0;
}
.terms-operator-input {
  width: 100%;
  height: 40px;
  padding: 0 14px;
  border-radius: 8px;
  background: rgba(8, 10, 14, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.10);
  color: #f1f4f8;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.005em;
  outline: none;
  transition: background-color 180ms ease, border-color 180ms ease, box-shadow 180ms ease;
}
.terms-operator-input::placeholder {
  color: #6b7280;
  font-weight: 400;
}
.terms-operator-input:hover {
  border-color: rgba(255, 255, 255, 0.18);
  background: rgba(8, 10, 14, 0.55);
}
.terms-operator-input:focus {
  border-color: rgba(116, 208, 255, 0.55);
  background: rgba(8, 10, 14, 0.60);
  box-shadow: 0 0 0 3px rgba(116, 208, 255, 0.15);
}

/* Accept button — countdown disabled state + activated state */
.terms-btn {
  position: relative;
  width: 100%;
  height: 48px;
  border-radius: 10px;
  border: 1px solid rgba(116, 208, 255, 0.22);
  background: rgba(116, 208, 255, 0.10);
  color: rgba(241, 244, 248, 0.55);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: not-allowed;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  transition: background-color 200ms ease, color 200ms ease, border-color 200ms ease;
}
.terms-btn-counter {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 13px;
  font-weight: 500;
  opacity: 0.85;
}
.terms-btn-underline {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: rgba(116, 208, 255, 0.85);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 4000ms linear, opacity 320ms ease, background 320ms ease;
}
/* Auto-trigger the underline fill on mount */
.terms-modal-scrim .terms-btn-underline {
  transform: scaleX(1);
}
.terms-btn-no-anim .terms-btn-underline {
  transition: none;
  transform: scaleX(0);
  opacity: 0;
}
.terms-btn.terms-btn-enabled {
  background: rgba(116, 208, 255, 0.18);
  border-color: rgba(116, 208, 255, 0.55);
  color: #e6f5ff;
  cursor: pointer;
  animation: terms-btn-pulse 320ms ease-out;
}
.terms-btn.terms-btn-enabled .terms-btn-underline {
  height: 1px;
  background: rgba(116, 208, 255, 0.45);
}
@keyframes terms-btn-pulse {
  0%   { box-shadow: 0 0 0 1px rgba(116,208,255,0.65), 0 0 36px -2px rgba(116,208,255,0.55); }
  100% { box-shadow: 0 0 0 1px rgba(116,208,255,0.35), 0 0 24px -4px rgba(116,208,255,0.28); }
}
.terms-btn.terms-btn-enabled:hover {
  background: rgba(116, 208, 255, 0.28);
  border-color: rgba(116, 208, 255, 0.7);
}
.terms-btn:focus-visible {
  outline: 2px solid #74d0ff;
  outline-offset: 3px;
}

/* Post-acceptance acknowledgement card */
.terms-ack-block {
  padding: 8px 0 4px;
}
/* Inline brand mark — used ONLY for the reduced-motion path. Stays
   inside the card, fades with the card. Normal motion uses the
   body-level .terms-hero-mark overlay instead (see below). */
.terms-ack-mark {
  display: block;
  width: 88px;
  height: 88px;
  margin: 0 auto 14px;
}
.terms-ack-mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
@media (max-width: 480px) {
  .terms-ack-mark { width: 72px; height: 72px; margin-bottom: 10px; }
}

/* Hero brand mark — body-level overlay independent of the terms scrim.
   Spawned at the start of the ack-display window, persists after the
   card + scrim dismiss, plays its full animation cycle on a transparent
   overlay while the workbench reveals behind. Pointer-events: none so
   the workbench is interactive even while the swirl is still on screen.

   Two visual states:
   - Default: positioned above the card (centre-x, ~220px above centre-y),
     160 × 160. Hero plays alongside the card during ack display.
   - .terms-hero-mark-centred: card has exited; hero promotes to screen
     centre at 240 × 240 with a smooth scale+slide. */
.terms-hero-mark {
  position: fixed;
  top: 50%;
  left: 50%;
  /* Phase 1 — hero sits above the card so they don't overlap. */
  transform: translate(-50%, calc(-50% - 220px)) scale(0.96);
  width: 160px;
  height: 160px;
  z-index: 10001;          /* above terms scrim's 10000 */
  pointer-events: none;
  opacity: 0;
  transition:
    opacity 320ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 480ms cubic-bezier(0.16, 1, 0.3, 1),
    width 480ms cubic-bezier(0.16, 1, 0.3, 1),
    height 480ms cubic-bezier(0.16, 1, 0.3, 1);
}
.terms-hero-mark-visible {
  opacity: 1;
  transform: translate(-50%, calc(-50% - 220px)) scale(1);
}
.terms-hero-mark-centred {
  /* Phase 2 — card has exited; hero takes screen centre at full size. */
  width: 240px;
  height: 240px;
  transform: translate(-50%, -50%) scale(1);
}
.terms-hero-mark-exit {
  /* Phase 3 — fade away while staying centred. */
  opacity: 0;
  transition: opacity 400ms ease;
}
.terms-hero-mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
@media (max-width: 480px) {
  /* Phone — pull the hero closer to the card and clamp the hero size so
     it doesn't clip on short viewports. */
  .terms-hero-mark,
  .terms-hero-mark-visible {
    width: 120px;
    height: 120px;
    transform: translate(-50%, calc(-50% - 180px)) scale(1);
  }
  .terms-hero-mark-centred {
    width: 180px;
    height: 180px;
    transform: translate(-50%, -50%) scale(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  /* Belt-and-braces — JS already gates the hero behind reducedMotion, but
     if the hero is ever spawned by mistake we kill its transitions so it
     doesn't surprise a motion-sensitive user. */
  .terms-hero-mark {
    transition: opacity 200ms ease;
  }
}

.terms-ack-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 14px;
}
.terms-ack-dot {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #74d0ff;
  opacity: 0.85;
  margin-top: 4px;
  animation: terms-ack-pulse 600ms cubic-bezier(0.4, 0, 0.2, 1);
  animation-delay: calc(var(--terms-ack-delay, 0ms) + 220ms);
}
@keyframes terms-ack-pulse {
  0%   { transform: scale(1);    opacity: 0.85; }
  50%  { transform: scale(1.25); opacity: 1; }
  100% { transform: scale(1);    opacity: 0.85; }
}
.terms-ack-line {
  font-size: 15px;
  line-height: 1.55;
  color: #d8dde5;
  display: block;
  margin-bottom: 10px;
}
.terms-ack-ts {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-weight: 500;
  color: #74d0ff;
}
.terms-ack-stagger {
  opacity: 0;
  transform: translateY(8px);
  animation: terms-ack-in 320ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
  animation-delay: var(--terms-ack-delay, 0ms);
}
@keyframes terms-ack-in {
  to { opacity: 1; transform: translateY(0); }
}

/* Acknowledgement fingerprint grid — 5 key/value rows (Operator,
   Public IP, Browser, Timezone, Accepted). Two-column grid: label
   in muted small-caps, value in monospace for visual scan. */
.terms-ack-kv-grid {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 6px 18px;
  margin: 6px 0 14px 0;
  padding: 12px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.terms-ack-kv {
  display: contents;
}
.terms-ack-kv .terms-ack-k {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #7e8693;
  align-self: center;
}
.terms-ack-kv .terms-ack-v {
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  font-weight: 500;
  color: #e6ebf2;
  word-break: break-word;
  align-self: center;
}
.terms-ack-tail {
  font-size: 13px;
  color: #8a94a3;
  font-style: italic;
}

/* Tablet / narrow desktop — collapse to single-column bullets so each
   item gets full reading width rather than two cramped columns. */
@media (max-width: 720px) {
  .terms-bullets {
    grid-template-columns: 1fr;
    row-gap: 14px;
  }
}
/* Mobile — narrower card, smaller bullet font, taller touch target */
@media (max-width: 480px) {
  .terms-card {
    width: calc(100vw - 24px);
    padding: 24px 22px 20px;
    transform: translateY(0);
  }
  .terms-card.terms-card-fade-out { transform: translateY(-6px); }
  .terms-card.terms-card-relax { transform: translateY(0); }
  .terms-heading { font-size: 18px; }
  .terms-bullets li { font-size: 13px; }
  .terms-callout { font-size: 11px; }
  .terms-btn { height: 52px; }
  .terms-ack-kv-grid {
    grid-template-columns: 88px 1fr;
    gap: 5px 12px;
  }
  .terms-ack-kv .terms-ack-v { font-size: 12px; }
  .terms-operator-label {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }
}

/* Reduced motion — kill stagger / typewriter / pulses; keep the
   countdown numeric (it's a legal control, not motion flourish). */
@media (prefers-reduced-motion: reduce) {
  .terms-modal-scrim,
  .terms-modal-scrim.terms-modal-exit { animation-duration: 120ms; }
  .terms-card,
  .terms-card.terms-card-fade-out,
  .terms-card.terms-card-relax { transition: none; }
  .terms-btn-underline { transition: none; transform: scaleX(0); opacity: 0; }
  .terms-ack-dot { animation: none; }
  .terms-ack-stagger {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

/* ====================================================================
   Lab loading overlay — js/shared/lab-loading-overlay.js
   Fires on every route transition. Lighter glass than the terms modal
   (blur 14px vs the terms' 8px scrim+heavy-card combo) because this
   is a 200-1500 ms transient, not a hold-for-attention dialogue. Sits
   at z-index 9500 so the terms modal at 10000 still wins if both are
   somehow live at the same time.
   ==================================================================== */
.lab-loading-scrim {
  position: fixed;
  inset: 0;
  z-index: 9500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 10, 14, 0.42);
  backdrop-filter: blur(14px) saturate(130%);
  -webkit-backdrop-filter: blur(14px) saturate(130%);
  opacity: 0;
  pointer-events: none;     /* hidden state — clicks pass through */
  transition: opacity 200ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
@supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
  .lab-loading-scrim { background: rgba(8, 10, 14, 0.78); }
}
.lab-loading-scrim.is-visible {
  opacity: 1;
  pointer-events: auto;     /* eat stray clicks on the stale lab UI */
}
.lab-loading-scrim.is-exiting {
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms cubic-bezier(0.4, 0, 0.2, 1);
}
.lab-loading-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  min-width: 220px;
  background: rgba(20, 24, 32, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
  transform: scale(0.96);
  transition: transform 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.lab-loading-scrim.is-visible .lab-loading-card { transform: scale(1); }
.lab-loading-indicator {
  position: relative;
  width: 28px;
  height: 28px;
}
.lab-loading-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid rgba(74, 163, 255, 0.18);   /* track — dim accent */
  border-top-color: var(--accent);              /* sweep — bright accent */
  animation: lab-loading-spin 900ms linear infinite;
}
.lab-loading-ring-2 {
  inset: 5px;
  border-color: rgba(74, 163, 255, 0.10);
  border-top-color: rgba(74, 163, 255, 0.55);
  animation-duration: 1400ms;
  animation-direction: reverse;
}
.lab-loading-label {
  font-family: 'Inter Tight', system-ui, sans-serif;
  font-size: 13px;
  font-weight: 500;
  color: var(--fg);
  letter-spacing: 0.2px;
}
@keyframes lab-loading-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .lab-loading-scrim,
  .lab-loading-scrim.is-exiting { transition-duration: 120ms; }
  .lab-loading-card { transition: none; transform: none; }
  .lab-loading-ring,
  .lab-loading-ring-2 { animation: none; border-top-color: rgba(74, 163, 255, 0.18); }
}

/* ====================================================================
   Treatments panel — js/ui/panel-treatments.js
   Default panel typography sized for short labels overflows with long
   product names + dense description text. Scope tighter sizes locally
   and clamp descriptions to 2 lines. Catalogue is the bulk of the
   panel — that's where the air comes out.
   ==================================================================== */
#panel-treatments .treatments-disclaimer {
  font-size: 11.5px;
  line-height: 1.4;
  padding: 6px 9px;
  margin-bottom: 8px;
  background: rgba(255, 200, 80, 0.06);
  border-left: 2px solid rgba(255, 200, 80, 0.55);
  color: var(--fg-mid, #b9bfc8);
}
#panel-treatments .treatments-disclaimer strong { color: var(--fg, #e6edf3); }

#panel-treatments .treatments-armed {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 9px;
  margin-bottom: 10px;
  background: rgba(74, 163, 255, 0.10);
  border: 1px solid rgba(74, 163, 255, 0.45);
  border-radius: 4px;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--fg, #e6edf3);
}
#panel-treatments .treatments-armed-icon {
  font-size: 14px; color: var(--accent, #4aa3ff);
  flex: 0 0 auto;
}
#panel-treatments .treatments-armed-text { flex: 1; }
#panel-treatments .treatments-armed-text strong { color: var(--fg-strong, #fff); }
#panel-treatments .treatments-armed-cancel {
  flex: 0 0 auto;
  padding: 3px 8px;
  font-size: 11px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.18);
  color: var(--fg-mid, #b9bfc8);
  border-radius: 3px;
  cursor: pointer;
}
#panel-treatments .treatments-armed-cancel:hover {
  background: rgba(255,255,255,0.06);
  color: var(--fg, #e6edf3);
}

#panel-treatments .treatments-h3 {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--fg-mid, #8a929c);
  margin: 12px 0 6px;
}

#panel-treatments .treatments-empty {
  font-size: 11.5px;
  line-height: 1.4;
  padding: 6px 9px;
  color: var(--fg-mid, #8a929c);
}

#panel-treatments .treatments-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
#panel-treatments .treatments-tab {
  flex: 1 1 0;
  min-width: 0;
  padding: 4px 6px;
  font-size: 11px;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--fg-mid, #b9bfc8);
  border-radius: 3px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#panel-treatments .treatments-tab:hover {
  background: rgba(255,255,255,0.04);
  color: var(--fg, #e6edf3);
}
#panel-treatments .treatments-tab.is-active {
  background: rgba(74, 163, 255, 0.14);
  border-color: rgba(74, 163, 255, 0.55);
  color: var(--accent, #4aa3ff);
}

#panel-treatments .treatments-mfr {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--fg-mid, #8a929c);
  margin: 8px 0 4px;
  padding-bottom: 2px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
}

#panel-treatments .treatments-products {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
#panel-treatments .treatment-product-card {
  padding: 6px 8px;
  background: rgba(255,255,255,0.025);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 3px;
  font-size: 11.5px;
  line-height: 1.35;
}
#panel-treatments .tp-head {
  display: flex;
  justify-content: space-between;
  gap: 6px;
  align-items: baseline;
}
#panel-treatments .tp-name {
  font-size: 12px;
  font-weight: 600;
  color: var(--fg, #e6edf3);
  line-height: 1.25;
  /* Long product names — wrap, don't ellipsis (user needs to read full name) */
  word-break: break-word;
}
#panel-treatments .tp-tier {
  font-size: 10.5px;
  color: var(--fg-mid, #8a929c);
  flex: 0 0 auto;
  letter-spacing: 0.5px;
}
#panel-treatments .tp-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-top: 3px;
  font-size: 10.5px;
  color: var(--fg-mid, #8a929c);
  font-variant-numeric: tabular-nums;
}
#panel-treatments .tp-desc {
  margin: 4px 0 0;
  font-size: 11px;
  line-height: 1.35;
  color: var(--fg-mid, #b9bfc8);
  /* Clamp long datasheet descriptions to 2 lines */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
#panel-treatments .treatment-product-card:hover .tp-desc {
  -webkit-line-clamp: unset;        /* full text on hover */
  overflow: visible;
}
#panel-treatments .tp-place {
  width: 100%;
  margin-top: 6px;
  padding: 4px 8px;
  font-size: 11.5px;
}
#panel-treatments .treatments-armed ~ .treatments-catalogue .tp-place {
  opacity: 0.55;     /* dim other Place buttons while one is armed — still clickable to re-arm */
}

/* Per-card ΔRT60 ticker chip — PR-3, treatments physics integration.
   Maya UX call: must be at-a-glance, signed, never compete with the
   primary label for visual weight. Colour-coded by sign so a scan
   of the placed-list reads as "how is my room responding". */
#panel-treatments .treatment-delta-rt60 {
  display: inline-block;
  padding: 1px 6px;
  font-size: 10.5px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,0.10);
  background: rgba(255,255,255,0.04);
  color: var(--fg-mid, #b9bfc8);
  flex: 0 0 auto;
  white-space: nowrap;
}
#panel-treatments .treatment-delta-rt60.negative {
  /* Absorber doing its job — shortens RT60. */
  background: rgba(70, 200, 130, 0.10);
  border-color: rgba(70, 200, 130, 0.45);
  color: #6fd8a4;
}
#panel-treatments .treatment-delta-rt60.positive {
  /* Rare: panel α < wall material α (e.g. a glass diffuser on bare gypsum
     at LF). User should see this — it's actually LENGTHENING RT60 a touch. */
  background: rgba(255, 170, 60, 0.10);
  border-color: rgba(255, 170, 60, 0.45);
  color: #ffb874;
}
#panel-treatments .treatment-delta-rt60.zero,
#panel-treatments .treatment-delta-rt60.unknown {
  /* Tiny-effect or "catalogue still loading" — neutral. */
  opacity: 0.7;
}

/* Clamped badge — panel area exceeded the wall budget, physics
   reduced its effective area to fit. Visually similar warning weight
   to the "outside" / "through wall" badges in the Results panel. */
#panel-treatments .treatment-clamped-badge {
  display: inline-block;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  line-height: 1.4;
  border-radius: 3px;
  background: rgba(255, 165, 70, 0.14);
  border: 1px solid rgba(255, 165, 70, 0.55);
  color: #ffb874;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Precision tab banner — physics integration is RT60/STI only, not the
   ray tracer (deferred to v3 per Hannes' scoping doc). Banner sits at
   the top of #panel-precision so the user never thinks Precision T30
   is reading the panel α catalogue. */
#panel-precision .precision-v3-pending {
  font-size: 11.5px;
  line-height: 1.4;
  padding: 6px 9px;
  margin: 0 0 10px;
  background: rgba(255, 200, 80, 0.06);
  border-left: 2px solid rgba(255, 200, 80, 0.55);
  color: var(--fg-mid, #b9bfc8);
}
#panel-precision .precision-v3-pending strong { color: var(--fg, #e6edf3); }

/* v3 live banner — same layout as the v3-pending warning but with a
   positive (sea-foam) accent now that treatment scattering is wired
   in. Kept side-by-side with .precision-v3-pending so a re-roll
   between live and pending only needs the class swap in
   panel-precision.js. */
#panel-precision .precision-v3-live {
  font-size: 11.5px;
  line-height: 1.4;
  padding: 6px 9px;
  margin: 0 0 10px;
  background: rgba(120, 220, 170, 0.06);
  border-left: 2px solid rgba(120, 220, 170, 0.55);
  color: var(--fg-mid, #b9bfc8);
}
#panel-precision .precision-v3-live strong { color: var(--fg, #e6edf3); }

/* ===================================================================
   Isolation facet (was WallLAB, folded into SurfaceLAB v=852+). The
   simulator now renders into #view-wall INSIDE #route-surface, shown
   when data-surface-facet="isolation". The .walllab-view / .wall-*
   rules below still drive the simulator's own layout + glass header;
   the facet show/hide lives up in the SurfaceLAB facet block.
   BETA chip uses the calm amber data accent, never red, per Maya.
   #walllab-root no longer exists (route folded) — rule kept inert for
   any third-party embed that still mounts a standalone walllab root. */
#walllab-root {
  height: 100vh;          /* content extends behind the glass header */
  background: var(--bg);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr;
  position: relative;
}
/* Centre view — the simulator. Scrolls INTERNALLY only if a very short
   viewport can't fit it; on normal heights everything fits without scroll.
   Top padding clears the 44px fixed header. */
.walllab-view {
  height: 100vh;
  overflow-y: auto;
  padding: calc(44px + var(--space-4)) var(--space-4) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  align-items: center;
}
.wall-workbench {
  width: 100%;
  max-width: 720px;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.wall-head h1 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 22px;
  color: var(--fg-strong);
  margin: 0 0 var(--space-1);
}
.wall-head .wall-sub {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
  max-width: 56ch;
}
.wall-beta-chip {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--data-amber);
  background: rgba(255, 180, 84, 0.12);
  border: 1px solid rgba(255, 180, 84, 0.4);
  border-radius: var(--radius);
  padding: 2px 6px;
}
.wall-card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
}
.wall-card h2 {
  font-size: 15px;
  color: var(--fg-strong);
  margin: 0 0 var(--space-2);
}
/* Toggle lives inside the left "Physics mode" rail panel (narrow). Name +
   switch on one row, description + status stacked below. */
.wall-toggle-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.wall-toggle-divider {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--border);
}
.wall-toggle-name {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: 13px;
  font-weight: 600;
  color: var(--fg-strong);
}
.wall-toggle-desc {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.55;
  margin: 0;
}
.wall-toggle-desc em { color: var(--fg); font-style: italic; }
.wall-validation {
  display: inline-block;
  margin-top: var(--space-1);
  color: var(--data-amber);
  font-size: 11.5px;
}

/* Switch — real button[role=switch]; on/off by position + text, never
   colour alone (deuteranopia). */
.wall-switch {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
}
.wall-switch-track {
  width: 40px;
  height: 22px;
  border-radius: 11px;
  background: var(--border-strong);
  border: 1px solid var(--border-strong);
  position: relative;
  transition: background 0.15s ease;
}
.wall-switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 0.18s ease, background 0.15s ease;
}
.wall-switch[aria-checked="true"] .wall-switch-track { background: var(--accent-dim); }
.wall-switch[aria-checked="true"] .wall-switch-thumb { transform: translateX(18px); background: var(--accent); }
.wall-switch-state {
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
  min-width: 22px;
  text-align: left;
}
.wall-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius); }

.wall-toggle-status {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
}
.wall-toggle-status strong { color: var(--fg); font-weight: 600; }
.wall-status-auto { color: var(--data-amber); font-weight: 600; }
.wall-status-warn { color: var(--data-red); }

.wall-reload-banner {
  margin-top: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: rgba(74, 163, 255, 0.08);
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  font-size: 12.5px;
  color: var(--fg);
}
.wall-reload-btn {
  flex: 0 0 auto;
  background: var(--accent);
  color: #06121f;
  border: none;
  border-radius: var(--radius);
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.wall-reload-btn:hover { background: var(--accent-hover); }
.wall-reload-btn:focus-visible { outline: 2px solid var(--fg-strong); outline-offset: 2px; }

.wall-sim-placeholder .phase-placeholder,
.wall-sim .phase-placeholder {
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.55;
  margin: 0;
}

/* --- Simulator controls --- */
.wall-sim-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.wall-ctrl {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.wall-ctrl select {
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 5px 8px;
  font-size: 13px;
  text-transform: none;
  letter-spacing: 0;
  min-width: 150px;
}
.wall-ctrl-slider { flex: 1 1 220px; min-width: 200px; }
.wall-ctrl input[type="range"] { width: 100%; accent-color: var(--accent); margin: 6px 0 0; }
.wall-thick-val { font-size: 13px; color: var(--fg); text-transform: none; letter-spacing: 0; font-variant-numeric: tabular-nums; }
.wall-ctrl select:focus-visible,
.wall-ctrl input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --- Results: summary + plot --- */
.wall-sim-results {
  display: flex;
  gap: var(--space-4);
  align-items: stretch;
  flex-wrap: wrap;
}
.wall-summary {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 96px;
  padding: var(--space-3);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.wall-summary-num { font-size: 38px; font-weight: 700; color: var(--fg-strong); line-height: 1; font-variant-numeric: tabular-nums; }
.wall-summary-unit { font-size: 13px; color: var(--muted); margin-top: 2px; }
.wall-summary-cap { font-size: 10px; color: var(--muted-dim); text-align: center; margin-top: 6px; text-transform: uppercase; letter-spacing: 0.04em; }
.wall-plot { flex: 1 1 360px; min-width: 300px; }
.wall-plot-svg { width: 100%; height: auto; display: block; }

/* SVG plot primitives (shape-coded, not colour-only — deuteranopia) */
.wp-grid { stroke: var(--border); stroke-width: 1; }
.wp-ytick { fill: var(--muted); font-size: 9px; text-anchor: end; }
.wp-xtick { fill: var(--muted); font-size: 9px; text-anchor: middle; }
.wp-axis-title { fill: var(--muted); font-size: 10px; text-anchor: middle; }
.wp-computed { fill: none; stroke: var(--accent); stroke-width: 2; }
.wp-measured { fill: none; stroke: var(--data-cyan); stroke-width: 1.5; stroke-dasharray: 5 3; }
.wp-measured-pt { fill: var(--data-cyan); }
.wp-dip-leader { stroke: var(--data-amber); stroke-width: 1; stroke-dasharray: 2 2; }
.wp-dip-label { fill: var(--data-amber); font-size: 9px; }
.wp-legend { fill: var(--muted); font-size: 10px; }

/* --- Per-band table --- */
.wall-table-wrap { margin-top: var(--space-4); overflow-x: auto; }
.wall-tl-table { width: 100%; border-collapse: collapse; font-size: 12px; font-variant-numeric: tabular-nums; }
.wall-tl-table th, .wall-tl-table td { padding: 4px 8px; text-align: right; border-bottom: 1px solid var(--border); }
.wall-tl-table thead th { color: var(--muted); font-weight: 600; }
.wall-tl-table tbody th { text-align: left; color: var(--fg); font-weight: 600; }
.wall-tl-table td { color: var(--fg); }
.wall-tl-delta td, .wall-tl-delta th { color: var(--muted); }
.wall-tl-note td { text-align: left; color: var(--muted-dim); font-style: italic; border-bottom: none; }
.wall-method-hint { font-size: 11.5px; color: var(--muted-dim); margin: var(--space-3) 0 0; }
.wall-method-hint strong { color: var(--muted); }

/* --- Mode segmented control --- */
.wall-mode-seg {
  display: inline-flex;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-3);
}
.wall-seg-btn {
  background: var(--bg);
  color: var(--muted);
  border: none;
  padding: 6px 14px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.wall-seg-btn + .wall-seg-btn { border-left: 1px solid var(--border-strong); }
.wall-seg-btn.is-active { background: var(--accent-dim); color: var(--fg-strong); }
.wall-seg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* --- Over-wall diffraction: status line + cross-section --- */
.wall-diff-status {
  margin-top: var(--space-3);
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
}
.wall-status-shadow { color: var(--data-amber); font-weight: 600; }
.wall-status-lit { color: var(--data-green); font-weight: 600; }

.xs-ground { stroke: var(--border-strong); stroke-width: 1.5; }
.xs-wall { fill: var(--border-strong); stroke: var(--muted); stroke-width: 1; }
.xs-sight { stroke: var(--muted-dim); stroke-width: 1; stroke-dasharray: 3 3; }
.xs-path { fill: none; stroke-width: 2; }
.xs-path-shadow { stroke: var(--data-amber); }
.xs-path-lit { stroke: var(--data-green); }
.xs-path-ground { fill: none; stroke: var(--data-amber); stroke-width: 1.5; stroke-dasharray: 6 3; opacity: 0.55; }
.xs-src { fill: var(--accent); }
.xs-src-image { fill: none; stroke: var(--accent); stroke-width: 1.5; }
.xs-image-leader { stroke: var(--muted-dim); stroke-width: 1; stroke-dasharray: 1 2; opacity: 0.4; }
.xs-recv { fill: var(--data-cyan); }
.xs-label { fill: var(--muted); font-size: 9px; }
.wall-tl-airabs td, .wall-tl-airabs th { color: var(--muted); }
.wall-tl-net td, .wall-tl-net th { font-weight: 600; color: var(--fg-strong); }
.wall-ground-row { display: flex; align-items: center; gap: var(--space-3); margin: var(--space-2) 0 var(--space-3); }
.wall-ground-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }

/* --- Step 8a (Maya 2026-05-23): formula slider rail (Construction + Stud system groups) --- */
.wall-ctrl-fluid { width: 100%; }
.wall-param-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-3) 0;
  padding: var(--space-2) var(--space-3);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.wall-param-group-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.wall-param-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2) var(--space-4);
}
.wall-mode-seg-tight .wall-seg-btn { padding: 4px 8px; font-size: 11px; }
.wall-catalogue-note {
  margin: var(--space-3) 0;
  padding: var(--space-2) var(--space-3);
  background: var(--bg);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 12px;
  font-style: italic;
}

/* --- Step 8b (Maya 2026-05-23): Rw chip + ISO 717-1 contour overlay --- */
.wall-rw-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
}
.wall-rw-chip-label {
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.wall-rw-chip-value {
  font-size: 26px;
  font-weight: 700;
  color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}
.wall-rw-chip-paren {
  font-size: 14px;
  color: var(--muted);
  font-weight: 500;
}
.wall-rw-chip-cap {
  font-size: 10px;
  color: var(--muted-dim);
  margin-top: 4px;
  text-align: center;
  letter-spacing: 0.02em;
}
/* Phase 6 Step 4 — field-derated DnT,w line per Hopkins 2007 Table 6.3 */
.wall-rw-chip-field {
  font-size: 11px;
  color: var(--muted);
  margin-top: 6px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.wall-rw-chip-field-cite {
  font-size: 9px;
  color: var(--muted-dim);
  font-weight: 400;
}
.wall-rw-chip-unavailable .wall-rw-chip-value {
  font-size: 14px;
  color: var(--muted-dim);
  font-weight: 500;
}

/* Reference contour + hatching on the TL plot */
.wp-contour {
  fill: none;
  stroke: var(--muted);
  stroke-width: 1;
  stroke-dasharray: 2 3;
  opacity: 0.65;
}
.wp-hatch-stroke {
  stroke: var(--muted);
  stroke-width: 1;
  opacity: 0.4;
}
.wp-sum-unfav {
  fill: var(--muted);
  font-size: 9px;
  font-variant-numeric: tabular-nums;
}
/* (removed: .wp-legend-bg — legend moved to a horizontal strip below the
   plot in v=626; opaque background was hiding more curves than it helped.) */

/* --- Step 8c (Maya 2026-05-23): Standards & method two-section stack --- */
.wall-method-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.wall-method-section + .wall-method-section {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.wall-method-section-label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted-dim);
}
.wall-rating-unavailable {
  font-size: 12.5px;
  color: var(--muted);
  font-style: italic;
  line-height: 1.5;
  padding: var(--space-3);
  background: var(--bg);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

/* --- Phase 6 Step 3 (Dr. Chen 2026-05-23): perimeter-leak disclosure table --- */
.wall-method-section-leaks { /* same family as other method sections */ }
.wall-leak-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  margin: var(--space-2) 0;
}
.wall-leak-table th, .wall-leak-table td {
  padding: 4px 8px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}
.wall-leak-table thead th { color: var(--muted); font-weight: 600; }
.wall-leak-table th:nth-child(2),
.wall-leak-table th:nth-child(3),
.wall-leak-table th:nth-child(4),
.wall-leak-table td:nth-child(2),
.wall-leak-table td:nth-child(3),
.wall-leak-table td:nth-child(4) { text-align: right; }
.wall-leak-table td strong { color: var(--fg-strong); }
.wall-leak-table tbody tr:last-child td { border-bottom: none; }

/* --- Phase 6 Step 5 (Dr. Chen 2026-05-23): flanking-transmission disclosure table --- */
.wall-method-section-flanking { /* same family as other method sections */ }
.wall-flanking-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  margin: var(--space-2) 0;
}
.wall-flanking-table th, .wall-flanking-table td {
  padding: 4px 8px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}
.wall-flanking-table thead th { color: var(--muted); font-weight: 600; }
.wall-flanking-table th:nth-child(2),
.wall-flanking-table th:nth-child(3),
.wall-flanking-table th:nth-child(4),
.wall-flanking-table td:nth-child(2),
.wall-flanking-table td:nth-child(3),
.wall-flanking-table td:nth-child(4) { text-align: right; }
.wall-flanking-table td strong { color: var(--fg-strong); }
.wall-flanking-table tbody tr:last-child td { border-bottom: none; }

/* --- Step 8d (Maya 2026-05-23): deferred-rows caption beneath material dropdown --- */
.wall-deferred-caption {
  font-size: 11px;
  color: var(--muted-dim);
  font-style: italic;
  margin: calc(-1 * var(--space-2)) 0 var(--space-3);
  line-height: 1.4;
}

/* --- Step 9 (Maya 2026-05-23): composite-wall inset gesture --- */
.wall-inset-container { margin: var(--space-2) 0 var(--space-3); }
.wall-inset-add-btn {
  /* v=625 — user-reported invisibility on first viewing. Boosted from
     muted-dashed to accent-bordered with a tinted background; size up to
     match the segment buttons' visual weight. The button is the entry
     point to the composite mode; a real engineer needs to see it. */
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  color: var(--fg-strong);
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: background 120ms ease, color 120ms ease, transform 80ms ease;
}
.wall-inset-add-btn:hover { background: var(--accent); color: var(--bg); }
.wall-inset-add-btn:active { transform: translateY(1px); }
.wall-inset-add-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.wall-inset-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--data-cyan);
  border-radius: var(--radius);
}
.wall-inset-row .wall-ctrl { flex: 1 1 200px; }
.wall-inset-pct-pair {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: var(--space-2);
}
.wall-inset-pct-input {
  width: 48px;
  padding: 2px 6px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--fg);
  text-align: right;
}
.wall-inset-pct-unit { font-size: 12px; color: var(--muted); }
.wall-inset-remove {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--muted);
  width: 26px;
  height: 26px;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.wall-inset-remove:hover { color: var(--fg-strong); border-color: var(--muted); }
.wall-inset-fraction-summary {
  font-size: 11px;
  color: var(--muted-dim);
  margin: var(--space-2) 0 0;
  font-variant-numeric: tabular-nums;
}

/* Plot — primary + inset overlay curves under the composite. Maya §5:
   dashed muted at 40% opacity, distinct dash patterns so colour-blind
   readers can distinguish. */
.wp-primary-overlay {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.25;
  stroke-dasharray: 4 3;
  opacity: 0.45;
}
.wp-inset-overlay {
  fill: none;
  stroke: var(--data-cyan);
  stroke-width: 1.25;
  stroke-dasharray: 2 2;
  opacity: 0.55;
}

/* Composite table — primary + inset rows muted, composite row accented. */
.wall-tl-overlay td, .wall-tl-overlay th { color: var(--muted); font-weight: 500; }

/* COMPOSITE BREAKDOWN — third section in right rail */
.wall-method-section-breakdown { /* same family as other method sections */ }
.wall-breakdown-bar {
  display: flex;
  width: 100%;
  height: 28px;
  border-radius: var(--radius);
  overflow: hidden;
  margin: var(--space-2) 0;
  font-size: 11px;
  font-weight: 600;
  color: var(--fg-strong);
}
.wall-breakdown-bar-primary {
  background: var(--accent-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wall-breakdown-bar-inset {
  background: rgba(64, 196, 255, 0.28);    /* matches data-cyan tint */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--fg-strong);
}

/* --- Standards & method --- */
.wall-eq {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  font-family: 'JetBrains Mono', monospace;
}
.wall-eq-line { font-size: 14px; color: var(--fg-strong); }
.wall-eq-sub { font-size: 11px; color: var(--muted); margin: 4px 0; }
.wall-eq-result { font-size: 13px; color: var(--fg); margin-top: 4px; }
.wall-eq-result strong { color: var(--accent); }
.wall-method-plain { font-size: 12.5px; color: var(--muted); line-height: 1.55; margin: 0 0 var(--space-3); }
.wall-method-plain em { color: var(--fg); font-style: italic; }
.wall-cites { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.wall-cites li { font-size: 11.5px; color: var(--muted); line-height: 1.5; }
.wall-cites strong { color: var(--fg); }
.wall-cite-sep { border-top: 1px solid var(--border); padding-top: var(--space-2); }
.wall-est-chip {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  color: var(--data-amber);
  background: rgba(255, 180, 84, 0.12);
  border: 1px solid rgba(255, 180, 84, 0.4);
  border-radius: var(--radius);
  padding: 1px 5px;
  margin-right: 4px;
}
.wall-asm-chip {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  color: var(--data-violet, #9a7cff);
  background: rgba(154, 124, 255, 0.12);
  border: 1px solid rgba(154, 124, 255, 0.4);
  border-radius: var(--radius);
  padding: 1px 5px;
  margin-right: 4px;
}

/* ===== FurnitureLAB — Lab #6 (Phase 0, 2026-05-26) ===========================
   Two-pane layout: catalogue grid (left, fluid), citation-first detail pane
   (right, fixed 420 px). Isometric-ink glyphs in card thumbs; same ink language
   carries to the print plan SVG so Sam's cross-surface parity is one source
   of glyph truth. Maya signed off the visual brief; this is the v0 implementation. */

#furniturelab-root {
  height: 100vh;
  background: var(--bg);
  color: var(--fg);
  overflow: hidden;
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: 1fr 420px;
  grid-template-areas:
    "head head"
    "grid detail";
  gap: 0;
  padding-top: 56px; /* clear the app header */
  box-sizing: border-box;
}
.fl-header {
  grid-area: head;
  padding: 18px 28px 14px 28px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-panel);
}
.fl-title {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: 0.01em;
  margin: 0 0 4px 0;
  color: var(--fg-strong);
}
.fl-beta {
  display: inline-block;
  vertical-align: middle;
  font-size: 10px;
  font-weight: 700;
  color: var(--data-amber);
  background: rgba(255, 180, 84, 0.12);
  border: 1px solid rgba(255, 180, 84, 0.40);
  border-radius: var(--radius);
  padding: 1px 5px;
  margin-left: 8px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.fl-tagline {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
  max-width: 70ch;
  line-height: 1.45;
}

/* Catalogue grid — auto-fill columns at 240 px min, gap 16 px, padded
   from the page edges so cards don't bleed into the chrome rules. */
.fl-grid {
  grid-area: grid;
  overflow-y: auto;
  padding: 20px 24px 24px 28px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
  align-content: start;
}
.fl-grid-loading, .fl-grid-empty, .fl-grid-error {
  font-size: 13px;
  color: var(--muted);
  padding: 24px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  grid-column: 1 / -1;
}
.fl-grid-error { color: var(--data-red); border-color: rgba(255, 107, 107, 0.4); }

/* Card — paper background, ink-on-paper feel echoes the print-report
   palette intentionally so report screenshots and the catalogue feel
   like they belong to the same document family. */
.fl-card {
  background: #FAFAF7;
  color: #1A1A1A;
  border: 1px solid #D8D4CC;
  border-radius: var(--radius-lg);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 120ms ease-out, box-shadow 120ms ease-out, transform 120ms ease-out;
  display: flex;
  flex-direction: column;
}
.fl-card:hover {
  border-color: #1A1A1A;
  background: #F5F1E8;
}
.fl-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.fl-card.selected {
  border-color: #9A3F2A;
  box-shadow: 0 0 0 2px rgba(154, 63, 42, 0.18);
}
.fl-card-art {
  background: #F2EDE3;
  padding: 8px 12px 0 12px;
  display: flex;
  justify-content: center;
}
.fl-card-art svg {
  width: 100%;
  height: 140px;
  display: block;
}
.fl-card-body {
  padding: 10px 14px 12px 14px;
  display: grid;
  gap: 6px;
}
.fl-card-name {
  font-size: 13px;
  font-weight: 600;
  margin: 0;
  color: #1A1A1A;
  line-height: 1.25;
}
.fl-card-meta {
  font-size: 10.5px;
  color: #555;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
}
.fl-card-cat {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
}
.fl-card-spark {
  width: 70px;
  height: 18px;
}
.fl-card-rel {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 6px;
  border-radius: var(--radius);
  display: inline-block;
  width: max-content;
}
.fl-rel-measured  { color: #1B6E37; background: rgba(27, 110, 55, 0.10); border: 1px solid rgba(27, 110, 55, 0.30); }
.fl-rel-derived   { color: #8A6A1F; background: rgba(138, 106, 31, 0.10); border: 1px solid rgba(138, 106, 31, 0.30); }
.fl-rel-estimated { color: #7A2018; background: rgba(122, 32, 24, 0.10); border: 1px solid rgba(122, 32, 24, 0.30); }

/* Detail pane — same dark slate as the rest of the chrome (the card is the
   only paper surface, intentional). */
.fl-detail {
  grid-area: detail;
  background: var(--bg-panel);
  border-left: 1px solid var(--border);
  overflow-y: auto;
  padding: 20px 22px 24px 22px;
}
.fl-detail-empty {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
  padding: 24px 12px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}
.fl-detail-head {
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fl-detail-name {
  font-size: 15px;
  margin: 0;
  color: var(--fg-strong);
  line-height: 1.3;
}
.fl-detail-rel {
  width: max-content;
}
.fl-detail-section {
  margin-bottom: 16px;
}
.fl-detail-section h3 {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin: 0 0 8px 0;
  font-weight: 600;
}
.fl-detail-A {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.fl-detail-A th {
  text-align: left;
  font-weight: 500;
  color: var(--muted);
  padding: 3px 8px 3px 0;
  border-bottom: 1px solid var(--border);
  width: 36%;
}
.fl-detail-A td {
  text-align: right;
  padding: 3px 0;
  border-bottom: 1px solid var(--border);
  color: var(--fg);
}
.fl-detail-note {
  font-size: 11px;
  color: var(--muted);
  margin: 8px 0 0 0;
  line-height: 1.5;
}
.fl-detail-spec {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 4px 12px;
  font-size: 12px;
  margin: 0;
}
.fl-detail-spec dt {
  color: var(--muted);
  font-weight: 500;
}
.fl-detail-spec dd {
  color: var(--fg);
  margin: 0;
  font-variant-numeric: tabular-nums;
}
.fl-citation {
  border-top: 1px solid var(--border);
  padding-top: 12px;
}
.fl-cite-source {
  font-size: 12px;
  color: var(--fg);
  margin: 0 0 4px 0;
}
.fl-cite-source a {
  color: var(--accent);
  text-decoration: none;
}
.fl-cite-source a:hover { text-decoration: underline; }
.fl-cite-ref, .fl-cite-method, .fl-cite-notes {
  font-size: 11.5px;
  color: var(--muted);
  margin: 0 0 4px 0;
  line-height: 1.5;
}
.fl-detail-foot {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.fl-place-btn {
  width: 100%;
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #FAFAF7;
  background: #9A3F2A;
  border: 1px solid #7A2F1F;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 100ms ease-out;
}
.fl-place-btn:hover { background: #B14A33; }
.fl-place-btn:active { background: #842F1F; }

/* Armed-placement affordance — when state.furnitureArmed is non-null,
   the 2D viewport changes cursor + shows a hint at the top. */
.r2d-armed { cursor: crosshair !important; }
.r2d-armed-hint {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(154, 63, 42, 0.92);
  color: #FAFAF7;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: var(--radius);
  pointer-events: none;
  z-index: 5;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  letter-spacing: 0.02em;
}

/* Placed furniture in the 2D viewport — footprint outline + glyph,
   selected state mirrors the treatment-panel pattern. */
.r2d-furniture {
  cursor: pointer;
}
.r2d-furniture .r2d-furniture-footprint {
  fill: rgba(154, 63, 42, 0.18);
  stroke: rgba(154, 63, 42, 0.85);
  stroke-width: 1.2;
}
.r2d-furniture.selected .r2d-furniture-footprint {
  stroke: #00d4ff;
  stroke-width: 2;
  fill: rgba(0, 212, 255, 0.18);
}
.r2d-furniture-label {
  fill: #cfd6df;
  font-size: 10px;
  pointer-events: none;
}
.r2d-furniture.broken .r2d-furniture-label { fill: #ff9090; }

/* Building-structure footprints in the 2D plan. Cool slate fill so they read
   as architecture (vs furniture's warm clay). Walls/partitions use a denser
   fill than pillars/platforms; selection switches to the shared cyan ring. */
.r2d-structure { cursor: pointer; }
.r2d-structure .r2d-structure-footprint {
  fill: rgba(120, 134, 156, 0.30);
  stroke: rgba(150, 165, 188, 0.9);
  stroke-width: 1.2;
}
.r2d-structure-half_wall .r2d-structure-footprint,
.r2d-structure-partition .r2d-structure-footprint,
.r2d-structure-beam .r2d-structure-footprint {
  fill: rgba(120, 134, 156, 0.45);
}
.r2d-structure-beam .r2d-structure-footprint { stroke-dasharray: 4,3; } /* overhead */
.r2d-structure.selected .r2d-structure-footprint {
  stroke: #00d4ff;
  stroke-width: 2;
  fill: rgba(0, 212, 255, 0.22);
}
.r2d-structure-label {
  fill: #cfd6df;
  font-size: 10px;
  pointer-events: none;
}

/* Solid face-edit handles (v=844) — click an edge in plan to add a staircase/
   slope to that face. Mirrors the room vertex-handle vocabulary: faint-cyan
   dashes = pickable, cyan-solid + glow = selected, amber = built. Colour AND
   the glyph shape (tick ladder vs chevron) carry state, so it survives mono /
   deuteranopia. */
.r2d-face .r2d-face-hit { stroke: transparent; stroke-width: 14; fill: none; pointer-events: stroke; cursor: pointer; }
.r2d-face-line { stroke: #74d0ff; stroke-width: 1.5; stroke-dasharray: 5 4; opacity: 0.6; fill: none; pointer-events: none; }
.r2d-face:hover .r2d-face-line { opacity: 1; stroke-width: 2.5; stroke-dasharray: none; }
.r2d-face-line.built { stroke: #ffd24a; stroke-width: 2; stroke-dasharray: none; opacity: 0.95; }
.r2d-face-line.selected { stroke: #00d4ff; stroke-width: 3; stroke-dasharray: none; opacity: 1; filter: drop-shadow(0 0 6px rgba(0, 212, 255, 0.7)); }
.r2d-face-glyph { fill: #ffd24a; stroke: none; pointer-events: none; }
.r2d-face-tick { stroke: #ffd24a; stroke-width: 1.2; opacity: 0.9; pointer-events: none; }
.r2d-face-chevron { stroke: #ffd24a; stroke-width: 1.4; fill: none; pointer-events: none; }
.r2d-face-label { fill: #ffd24a; font-size: 9px; font-weight: 600; pointer-events: none; paint-order: stroke; stroke: rgba(14, 17, 22, 0.85); stroke-width: 2.4px; }

/* Draggable corner handles on the selected solid (resize/reshape its footprint).
   Cyan to match the "selected/editable" vocabulary; fat invisible hit-target. */
.r2d-solid-vertex { cursor: grab; }
.r2d-solid-vertex.dragging { cursor: grabbing; }
.r2d-solid-vertex .r2d-solid-vertex-hit { fill: transparent; pointer-events: all; }
.r2d-solid-vertex .r2d-solid-vertex-dot { fill: #00d4ff; stroke: #fff; stroke-width: 2; }
.r2d-solid-vertex:hover .r2d-solid-vertex-dot { fill: #6ce4ff; }

/* Face build-up panel section (solid editor). */
.ps-face-section { margin-top: .5rem; border-top: 1px solid rgba(150, 165, 188, 0.18); padding-top: .45rem; }
.ps-face-eyebrow { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: #9fb0c4; margin-bottom: .35rem; }
.ps-face-picker { flex-wrap: wrap; }
.ps-face-pick {
  min-width: 26px; padding: 2px 6px; font-size: 12px; line-height: 1.4;
  border: 1px solid rgba(150, 165, 188, 0.35); border-radius: 4px;
  background: transparent; color: #cdd6e2; cursor: pointer;
}
.ps-face-pick.built { border-color: #ffd24a; color: #ffd24a; }
.ps-face-pick.on { border-color: var(--accent); color: var(--accent); background: rgba(74, 163, 255, 0.12); }
.ps-face-pick.on.built { border-color: #00d4ff; color: #00d4ff; }
.ps-face-head { display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #cdd6e2; margin: .4rem 0 .25rem; }
.ps-face-clear {
  border: none; background: transparent; color: #9fb0c4; cursor: pointer;
  font-size: 15px; line-height: 1; padding: 0 4px;
}
.ps-face-clear:hover { color: #ff8a8a; }

/* Toilet architectural plan symbol (v=774) — dividers + part-open door leaf +
   quarter-circle swing arc + WC pan/cistern. role → stroke weight. */
.r2d-toilet-prim { fill: none; pointer-events: none; }
.r2d-toilet-heavy { fill: rgba(120, 134, 156, 0.18); stroke: rgba(168, 182, 205, 0.95); stroke-width: 1.8; }
.r2d-toilet-medium { stroke: rgba(186, 198, 218, 0.95); stroke-width: 1.3; }
.r2d-toilet-swing { stroke: rgba(150, 165, 188, 0.7); stroke-width: 0.8; stroke-dasharray: 3,2.5; }
.r2d-toilet-fixture { stroke: rgba(150, 165, 188, 0.85); stroke-width: 0.9; }
.r2d-structure.selected .r2d-toilet-heavy { stroke: #00d4ff; stroke-width: 2.2; fill: rgba(0, 212, 255, 0.16); }
.r2d-structure.selected .r2d-toilet-medium { stroke: #4fe1ff; }

/* Confidence-overlay legend chip inside the 2D viewport. Text styles
   here only — geometry is laid out in SVG coords by renderFurnitureConfidenceLegend. */
.r2d-furn-confidence-legend .r2d-furn-leg-title {
  fill: #cfd6df;
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.r2d-furn-confidence-legend .r2d-furn-leg-label {
  fill: #e4e6ea;
  font-size: 9.5px;
  font-weight: 500;
}

/* Sidebar confidence-overlay toggle row */
.pf-toggle-row {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "checkbox label"
    "checkbox hint";
  column-gap: 8px;
  row-gap: 1px;
  align-items: center;
  padding: 8px 10px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  user-select: none;
  transition: border-color 100ms, background 100ms;
}
.pf-toggle-row:hover { border-color: var(--border-strong); }
.pf-toggle-row.on {
  border-color: var(--accent);
  background: rgba(74, 163, 255, 0.06);
}
.pf-toggle-row input[type="checkbox"] {
  grid-area: checkbox;
  margin: 0;
  width: 14px;
  height: 14px;
  accent-color: var(--accent);
  cursor: pointer;
}
.pf-toggle-label {
  grid-area: label;
  font-size: 12px;
  font-weight: 600;
  color: var(--fg);
}
.pf-toggle-hint {
  grid-area: hint;
  font-size: 10.5px;
  color: var(--muted);
  line-height: 1.3;
}

/* ===== panel-furniture (RoomLAB sidebar) ===== */
#panel-furniture h2 {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fl-rail-beta {
  font-size: 9px;
  font-weight: 700;
  color: var(--data-amber);
  background: rgba(255, 180, 84, 0.12);
  border: 1px solid rgba(255, 180, 84, 0.40);
  border-radius: var(--radius);
  padding: 1px 5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.pf-body {
  padding: 0 6px;
  display: grid;
  gap: 12px;
}
.pf-intro {
  font-size: 11px;
  color: var(--muted);
  margin: 0 0 4px 0;
  line-height: 1.4;
}
.pf-intro kbd {
  font-family: var(--font-mono, monospace);
  font-size: 10px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 0 4px;
  color: var(--fg);
}
.pf-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.pf-card {
  background: #FAFAF7;
  color: #1A1A1A;
  border: 1px solid #D8D4CC;
  border-radius: var(--radius-lg);
  cursor: pointer;
  display: grid;
  gap: 4px;
  padding: 4px;
  text-align: left;
  transition: border-color 100ms, background 100ms;
}
.pf-card:hover { border-color: #1A1A1A; background: #F5F1E8; }
.pf-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pf-card-art {
  background: #F2EDE3;
  border-radius: var(--radius);
  height: 64px;
  display: flex;
  justify-content: center;
  overflow: hidden;
}
.pf-card-art svg { width: 100%; height: 64px; }
.pf-card-info { padding: 0 2px 2px 2px; }
.pf-card-name {
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.2;
  color: #1A1A1A;
}
.pf-card-A {
  font-size: 9.5px;
  color: #555;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

.pf-placed {
  border-top: 1px solid var(--border);
  padding-top: 10px;
  margin-top: 4px;
}
.pf-placed-head {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  font-weight: 600;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.pf-placed-count {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 6px;
  font-size: 10px;
  color: var(--fg);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
}
.pf-placed-empty {
  font-size: 11px;
  color: var(--muted);
  font-style: italic;
  padding: 6px 0;
  line-height: 1.4;
}
.pf-placed-list { display: grid; gap: 4px; }
.pf-placed-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.pf-row-flip {
  background: none;
  border: none;
  border-left: 1px solid var(--border);
  color: var(--muted);
  padding: 6px 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.03em;
  cursor: pointer;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.pf-row-flip:hover {
  color: var(--accent);
  background: rgba(74, 163, 255, 0.08);
}
.pf-placed-row.selected { border-color: #00d4ff; box-shadow: 0 0 0 1px rgba(0, 212, 255, 0.3); }
.pf-placed-row.broken { border-color: rgba(255, 107, 107, 0.5); }
.pf-row-pick {
  background: none;
  border: none;
  text-align: left;
  padding: 6px 8px;
  color: var(--fg);
  cursor: pointer;
  display: grid;
  grid-template-columns: 28px 1fr;
  align-items: center;
  gap: 8px;
  font-size: 11px;
}
.pf-row-pick:hover { background: var(--bg-panel); }
.pf-row-id {
  font-family: var(--font-mono, monospace);
  color: var(--muted);
  font-size: 10px;
  font-weight: 600;
}
.pf-row-name { color: var(--fg); }
.pf-row-name em { color: var(--data-red); font-style: italic; font-size: 10px; }
.pf-row-remove {
  background: none;
  border: none;
  border-left: 1px solid var(--border);
  color: var(--muted);
  padding: 6px 10px;
  font-size: 14px;
  cursor: pointer;
  line-height: 1;
}
.pf-row-remove:hover { color: var(--data-red); background: rgba(255, 107, 107, 0.10); }

/* ============================================================ */
/* Building structure panel (ps-) — parametric pillars/walls/etc */
/* Shares the furniture-panel visual language (theme tokens).     */
/* ============================================================ */
.ps-body { padding: 0 6px; display: grid; gap: 12px; }
.ps-intro { font-size: 11px; color: var(--muted); margin: 0; line-height: 1.4; }
.ps-note {
  font-size: 10px; color: var(--muted); line-height: 1.4;
  border-top: 1px solid var(--border); padding-top: 8px; margin: 4px 0 0 0;
}
.ps-add-head, .ps-placed-head {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); font-weight: 600; margin-bottom: 6px;
  display: flex; align-items: center; gap: 6px;
}
.ps-chips { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
.ps-chip {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); color: var(--fg); cursor: pointer;
  padding: 7px 4px; font-size: 10px; transition: border-color 100ms, background 100ms;
}
.ps-chip svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.6; }
.ps-chip:hover { border-color: var(--accent); background: var(--bg-panel); }
.ps-chip.armed { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); color: var(--accent); }
.ps-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Draw-solid action — a draw gesture (not a click-to-place chip), so it's a
   full-width button below the chip grid with the dashed "draw" affordance. */
.ps-draw-solid {
  display: block; width: 100%; margin-top: 8px;
  padding: 8px 10px; font: inherit; font-size: 12px; cursor: pointer;
  border: 1px dashed rgba(116, 208, 255, 0.5); border-radius: var(--radius);
  background: rgba(116, 208, 255, 0.08); color: var(--data-cyan, #74d0ff);
  text-align: center;
}
.ps-draw-solid:hover { background: rgba(116, 208, 255, 0.18); border-color: var(--data-cyan, #74d0ff); color: #cfe7ff; }
.ps-draw-solid:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Transient error toast for a rejected solid draw (self-intersecting outline). */
.draw-error-toast { color: #fca5a5; border-color: rgba(229, 68, 68, 0.4); white-space: normal; max-width: 280px; text-align: center; }

.ps-armed {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: rgba(74, 163, 255, 0.10); border: 1px solid var(--accent);
  border-radius: var(--radius); padding: 7px 10px; font-size: 11px; color: var(--fg);
}
.ps-armed-cancel {
  background: none; border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--muted); cursor: pointer; font-size: 10px; padding: 3px 8px;
}
.ps-armed-cancel:hover { color: var(--fg); border-color: var(--accent); }

.ps-placed { border-top: 1px solid var(--border); padding-top: 10px; }
.ps-count {
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: 999px;
  padding: 0 6px; font-size: 10px; color: var(--fg); text-transform: none;
  letter-spacing: 0; font-weight: 600;
}
.ps-empty { font-size: 11px; color: var(--muted); font-style: italic; padding: 6px 0; line-height: 1.4; }
.ps-placed-list { display: grid; gap: 4px; }
.ps-row {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
  display: grid; grid-template-columns: 1fr auto; align-items: stretch;
}
.ps-row.selected { border-color: #00d4ff; box-shadow: 0 0 0 1px rgba(0, 212, 255, 0.3); grid-template-columns: 1fr auto; }
.ps-row-head {
  background: none; border: none; text-align: left; cursor: pointer;
  display: grid; grid-template-columns: 26px 1fr; grid-auto-rows: min-content;
  align-items: center; gap: 2px 8px; padding: 6px 8px; color: var(--fg); font-size: 11px;
}
.ps-row-head:hover { background: var(--bg-panel); }
.ps-row-id { font-family: var(--font-mono, monospace); color: var(--muted); font-size: 10px; font-weight: 600; grid-row: 1 / span 2; }
.ps-row-name { color: var(--fg); font-weight: 600; }
.ps-row-meta { grid-column: 2; color: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.ps-row-remove {
  background: none; border: none; border-left: 1px solid var(--border);
  color: var(--muted); padding: 6px 10px; font-size: 14px; cursor: pointer; line-height: 1;
}
.ps-row-remove:hover { color: var(--data-red); background: rgba(255, 107, 107, 0.10); }

/* Inline editor (expanded card) */
.ps-editor {
  grid-column: 1 / -1; border-top: 1px solid var(--border);
  padding: 8px; display: grid; gap: 7px; background: var(--bg-panel);
}
.ps-field { display: grid; grid-template-columns: 78px 1fr auto; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); }
.ps-field-wide { grid-template-columns: 78px 1fr; }
.ps-field > span:first-child { color: var(--muted); }
.ps-field input, .ps-field select {
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--fg); font-size: 11px; padding: 4px 6px; width: 100%;
  font-variant-numeric: tabular-nums;
}
.ps-field input:focus, .ps-field select:focus { outline: none; border-color: var(--accent); }
.ps-unit { color: var(--muted); font-size: 10px; min-width: 18px; }
.ps-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.ps-row2 .ps-field { grid-template-columns: 58px 1fr auto; }
.ps-check { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--fg); cursor: pointer; }
.ps-seg { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 4px; }
.ps-seg-btn {
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--muted); cursor: pointer; font-size: 10.5px; padding: 4px 2px;
}
.ps-seg-btn.on { border-color: var(--accent); color: var(--accent); background: rgba(74, 163, 255, 0.10); }
.ps-editor-actions { display: flex; justify-content: flex-end; gap: 6px; }
.ps-dup {
  background: none; border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--muted); cursor: pointer; font-size: 10px; padding: 4px 10px;
}
.ps-dup:hover { color: var(--accent); border-color: var(--accent); }

/* ───────────────────────────────────────────────────────────────────────
   Mobile auto-zoom guard (Maya, 2026-06-07 · v=768)

   BUG (Android/tablet Chrome only): focusing any form control whose
   font-size < 16px makes the browser auto-zoom the visual viewport. The
   app shell is position:fixed; height:100vh; overflow:hidden, so the
   auto-zoom scrolls the top header + left rail off-screen and does NOT
   reset on blur — "the upper parts is hidden" persists into draw mode.

   FIX: force every focusable control to ≥16px on coarse-pointer (touch /
   stylus) devices — 16px is the exact threshold below which iOS/Android
   Chrome auto-zoom on focus. We deliberately do NOT add
   maximum-scale=1 / user-scalable=no to the viewport meta (WCAG 1.4.4 —
   low-vision users must keep pinch-zoom).

   Scoping: `pointer: coarse` only. Mouse users (pointer: fine) never
   match this block, so the compact 12px desktop styling above is
   untouched — these are additive selectors at the end of the cascade
   matching a disjoint device class. Guarded by
   tests/mobile-input-zoom.test.mjs.
   ─────────────────────────────────────────────────────────────────── */
@media (pointer: coarse) {
  /* Global safety net — any present-or-future focusable text control
     renders at the no-auto-zoom threshold. 16px is a floor, not a
     restyle; larger inputs keep their size. */
  input,
  textarea,
  select {
    font-size: 16px;
  }

  /* The project / room name modal dialog (was 12px). */
  .rl-modal-input {
    font-size: 16px;
  }

  /* In-draw CAD coord entry (was 0.78rem ≈ 12.5px, width:5ch). At 16px a
     5ch box is too cramped to show "−12.5" comfortably and the panel can
     creep toward the canvas edge on a narrow phone. Widen to fit ~6 mono
     chars, cap the whole panel's width, and pin it inside the viewport so
     it can never overflow the canvas on a small screen. */
  .draw-float-coord-input {
    font-size: 16px;
    width: 7ch;
    padding: 6px 7px;
  }
  .draw-float-coord {
    max-width: calc(100vw - 24px);
    padding: 8px 10px 7px;
    gap: 6px;
  }
  .draw-float-coord-label { font-size: 0.9rem; }
  .draw-float-coord-unit  { font-size: 0.85rem; }
  .draw-float-coord-hint  { font-size: 0.8rem; }

  /* Post-close room-height prompt (was 0.95rem ≈ 15px — just under the
     threshold, so it also triggered the zoom). Bump to 16px and give the
     box a touch more room. */
  .draw-height-prompt-row input {
    font-size: 16px;
    width: 7ch;
    padding: 6px 8px;
  }
  .draw-height-prompt-unit { font-size: 0.92rem; }
  .draw-height-prompt-hint { font-size: 0.8rem; }
}

/* ===========================================================================
   Header user chip + account dropdown (js/ui/account-menu.js)
   Chip inherits the demoted .header-actions button grammar; the menu uses an
   elevated, accent-tinted surface like .project-dd-menu. Replaces the old
   fixed bottom-right #auth-signout button (retired v=815).
   =========================================================================== */
#app-header .user-chip-dd { position: relative; margin-left: 2px; }

#app-header .user-chip {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 7px 3px 4px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--muted);
  transition: background-color 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
}
#app-header .user-chip::-webkit-details-marker { display: none; }
#app-header .user-chip:hover { color: var(--fg-strong); background: rgba(255,255,255,.04); border-color: var(--border); }
#app-header .user-chip-dd[open] > .user-chip { background: rgba(255,255,255,.06); border-color: var(--border-strong); color: var(--fg-strong); }
#app-header .user-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The avatar disc — the ONE intentionally-round element in the chrome. Shared
   look (the inset ring) across the 24px chip and 52px modal sizes. */
.avatar-disc {
  border-radius: 50%;
  overflow: hidden;
  display: grid; place-items: center;
  color: #fff;
  font-family: "Inter Tight", system-ui, sans-serif; font-weight: 600;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  user-select: none;
}
.avatar-disc img { width: 100%; height: 100%; object-fit: cover; display: block; }
.user-chip-avatar { width: 24px; height: 24px; flex: 0 0 24px; font-size: 12px; line-height: 1; }

#app-header .user-chip-name {
  font-size: 0.8rem; font-weight: 500; letter-spacing: 0.01em;
  max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#app-header .user-chip-caret { color: var(--muted-dim); transition: transform 140ms ease-out; }
#app-header .user-chip-dd[open] .user-chip-caret { transform: rotate(180deg); }

#app-header .user-chip-menu {
  position: absolute; top: calc(100% + 6px); right: 0;
  min-width: 200px;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
  padding: 4px; z-index: 200;
}
#app-header .user-chip-menu .user-chip-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: transparent; border: none; border-radius: var(--radius);
  color: var(--fg); padding: 8px 10px;
  font: inherit; font-size: 0.82rem; text-align: left; cursor: pointer;
  transition: background-color 110ms ease-out, color 110ms ease-out;
}
#app-header .user-chip-menu .user-chip-item .uc-ic { width: 16px; height: 16px; flex: 0 0 16px; color: var(--muted); }
#app-header .user-chip-menu .user-chip-item:hover,
#app-header .user-chip-menu .user-chip-item:focus-visible {
  background: rgba(74,163,255,.12); color: var(--fg-strong); outline: none;
}
#app-header .user-chip-menu .user-chip-item:hover .uc-ic,
#app-header .user-chip-menu .user-chip-item:focus-visible .uc-ic { color: var(--accent); }
#app-header .user-chip-divider { height: 1px; background: var(--border); margin: 4px 6px; }

/* Sign out — warm-tinted on hover (session-ending, deliberate) but NOT red;
   red is reserved for the destructive Reset. Sign-out is reversible. */
#app-header .user-chip-menu .user-chip-item-signout:hover,
#app-header .user-chip-menu .user-chip-item-signout:focus-visible {
  background: rgba(255,180,84,.14); color: var(--data-amber);
}
#app-header .user-chip-menu .user-chip-item-signout:hover .uc-ic,
#app-header .user-chip-menu .user-chip-item-signout:focus-visible .uc-ic { color: var(--data-amber); }

/* Below ~720px the header is tight — collapse the chip to avatar-only. */
@media (max-width: 720px) {
  #app-header .user-chip-name,
  #app-header .user-chip-caret { display: none; }
  #app-header .user-chip { padding: 3px; }
}
@media (prefers-reduced-motion: reduce) {
  #app-header .user-chip-caret { transition: none; }
}

/* ===========================================================================
   App modals (Account / About) — shared closeable glass chrome
   (js/ui/modal-shell.js). NOT the Terms modal (welcome-card.js), which is
   intentionally non-closeable.
   =========================================================================== */
.app-modal-scrim {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-4);
  background: rgba(10, 12, 16, 0.62);
  opacity: 0;
  transition: opacity 140ms ease-out;
}
.app-modal-scrim.app-modal-visible { opacity: 1; }
.app-modal-scrim.app-modal-exit { opacity: 0; }

.app-modal-card {
  position: relative;
  width: 100%; max-width: 440px;
  max-height: calc(100vh - 2 * var(--space-4));
  overflow-y: auto;
  background: rgba(20, 24, 32, 0.78);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
  color: var(--fg);
  padding: var(--space-5);
  transform: translateY(6px);
  transition: transform 160ms cubic-bezier(.2,.7,.3,1);
}
.app-modal-scrim.app-modal-visible .app-modal-card { transform: translateY(0); }
@supports not ((backdrop-filter: blur(18px)) or (-webkit-backdrop-filter: blur(18px))) {
  .app-modal-card { background: rgba(20, 24, 32, 0.96); }
}

.app-modal-close {
  position: absolute; top: var(--space-3); right: var(--space-3);
  width: 30px; height: 30px;
  display: grid; place-items: center;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--muted); cursor: pointer;
  transition: background-color 120ms ease-out, color 120ms ease-out, border-color 120ms ease-out;
}
.app-modal-close:hover { color: var(--fg-strong); background: rgba(255,255,255,.05); border-color: var(--border); }
.app-modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

@media (prefers-reduced-motion: reduce) {
  .app-modal-scrim, .app-modal-card { transition: none; }
  .app-modal-card { transform: none; }
}

.app-modal-title {
  font: 600 1rem/1.2 "Inter Tight", sans-serif;
  color: var(--fg-strong); margin: 0 0 var(--space-4); letter-spacing: -.005em;
}

/* --- Account modal ------------------------------------------------------- */
.acct-head { padding-right: 36px; }   /* clear the X button */

/* Segmented tabs — square, instrument-style, inset-active (mirrors .lab-tab.active) */
.seg-tabs {
  display: inline-flex; padding: 2px; gap: 2px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius); margin-bottom: var(--space-4);
}
.seg-tab {
  appearance: none; background: transparent; border: 1px solid transparent;
  border-radius: var(--radius); color: var(--muted);
  font: 600 0.78rem/1 "Inter Tight", sans-serif; letter-spacing: .02em;
  padding: 6px 14px; cursor: pointer;
  transition: background-color 120ms ease-out, color 120ms ease-out, border-color 120ms ease-out;
}
.seg-tab:hover { color: var(--fg); }
.seg-tab[aria-selected="true"] {
  background: rgba(255,255,255,.06); color: var(--fg-strong); border-color: var(--border);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.04), inset 0 -1px 0 rgba(0,0,0,.3);
}
.seg-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.acct-view[hidden] { display: none; }

.acct-identity { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.acct-avatar { width: 52px; height: 52px; flex: 0 0 52px; font-size: 20px; line-height: 1; }
.acct-name  { font-size: 0.95rem; font-weight: 600; color: var(--fg-strong); }

/* Account-level strip — all tiers in one compact row; the current level is
   highlighted (tinted + bordered in its tier hue, bold name). Dots carry the
   tier hue at all times (cheapest → premium: Free grey, Pro blue, Max gold;
   Admin violet = operator) so colour alone signals level. Status display, not a
   selector — no hover/cursor affordance. */
.tier-strip { display: flex; flex-wrap: wrap; gap: 6px; }
.tier-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  font-size: 0.8rem; font-weight: 500; color: var(--muted); letter-spacing: .01em;
}
.tier-pill-dot {
  width: 8px; height: 8px; flex: 0 0 8px; border-radius: 50%;
  background: var(--muted-dim); box-shadow: inset 0 0 0 1px rgba(255,255,255,.10);
}
/* per-tier dot hue (always on, even when not the current level) */
.tier-pill-free  .tier-pill-dot { background: var(--muted); }
.tier-pill-pro   .tier-pill-dot { background: var(--accent); }
.tier-pill-max   .tier-pill-dot { background: var(--data-amber); }
.tier-pill-admin .tier-pill-dot { background: #a78bfa; }
/* current — tinted bg + coloured border + brightened bold name, per tier */
.tier-pill.is-current { color: var(--fg-strong); font-weight: 600; }
.tier-pill-free.is-current  { border-color: rgba(138,146,157,.60); background: rgba(138,146,157,.14); }
.tier-pill-pro.is-current   { border-color: rgba(74,163,255,.60);  background: rgba(74,163,255,.14); }
.tier-pill-max.is-current   { border-color: rgba(255,180,84,.60);  background: rgba(255,180,84,.14); }
.tier-pill-admin.is-current { border-color: rgba(167,139,250,.60); background: rgba(167,139,250,.16); }
.acct-email { font-size: 0.8rem; color: var(--muted); margin-top: 2px;
              font-family: "JetBrains Mono", monospace; word-break: break-all; }

.acct-subhead {
  font: 600 0.72rem/1 "Inter Tight", sans-serif; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: var(--space-4) 0 var(--space-2);
  padding-top: var(--space-3); border-top: 1px solid var(--border);
}
.acct-rows { margin: 0; }
.acct-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4);
  padding: 6px 0; border-bottom: 1px solid var(--border);
}
.acct-row:last-child { border-bottom: none; }
.acct-row dt { color: var(--muted); font-size: 0.8rem; }
.acct-row dd { margin: 0; color: var(--fg); font-size: 0.82rem; text-align: right; }
.acct-rows-mono dd { font-family: "JetBrains Mono", monospace; font-size: 0.78rem;
                     font-variant-numeric: tabular-nums; }
.acct-provider { display: inline-flex; align-items: center; gap: 7px; }
.acct-provider-glyph { width: 15px; height: 15px; flex: 0 0 15px; color: var(--muted); }
.acct-empty { color: var(--muted-dim); font-style: italic; text-align: left !important; }
.acct-note { color: var(--muted-dim); font-size: 0.74rem; line-height: 1.45; margin: var(--space-3) 0 0; }

/* Preferences toggle (switch) */
.pref-row { display: flex; gap: var(--space-3); align-items: flex-start; }
.switch { position: relative; flex: 0 0 auto; display: inline-block; margin-top: 1px; }
.switch-input {
  position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer;
  width: 100%; height: 100%;
}
.switch-track {
  display: block; width: 38px; height: 22px; border-radius: 11px;
  background: var(--border-strong); border: 1px solid var(--border-strong);
  transition: background-color 140ms ease-out, border-color 140ms ease-out;
}
.switch-thumb {
  display: block; width: 16px; height: 16px; border-radius: 50%;
  background: var(--muted); margin: 2px;
  transition: transform 140ms cubic-bezier(.2,.7,.3,1), background-color 140ms ease-out;
}
.switch-input:checked + .switch-track { background: var(--accent-dim); border-color: var(--accent); }
.switch-input:checked + .switch-track .switch-thumb { transform: translateX(16px); background: var(--accent); }
.switch-input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch-input:disabled + .switch-track { opacity: .5; cursor: not-allowed; }

.pref-copy { flex: 1 1 auto; }
.pref-label {
  display: block; color: var(--fg); line-height: 1.5; cursor: pointer;
  font-size: 16px;                 /* ≥16px: prevents iOS auto-zoom on focus */
}
.pref-help { color: var(--muted-dim); font-size: 0.78rem; margin: var(--space-2) 0 0; line-height: 1.45; }
/* Transient (~5s) confirmation under the marketing toggle on opt-out / opt-in. */
.pref-toast {
  margin: var(--space-3) 0 0;
  font-size: 0.78rem; line-height: 1.45; color: var(--fg);
  background: rgba(74,163,255,.08); border: 1px solid rgba(74,163,255,.22);
  border-radius: var(--radius); padding: 8px 11px;
}
.pref-toast[hidden] { display: none; }

/* Account-modal Profile editor — read rows + Edit→Save/Cancel toggle. */
.acct-subhead-row { display: flex; align-items: flex-end; justify-content: space-between; }
.acct-subhead-row .acct-subhead { flex: 1 1 auto; }
.acct-edit-btn {
  flex: 0 0 auto; align-self: center; margin-top: var(--space-3);
  background: transparent; border: 1px solid var(--border);
  color: var(--accent); font-size: 0.78rem; font-weight: 500;
  padding: 3px 12px; border-radius: var(--radius); cursor: pointer;
}
.acct-edit-btn:hover { border-color: var(--accent); background: var(--accent-dim); }
.acct-edit-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.acct-edit-btn[hidden] { display: none; }
.acct-loading { color: var(--muted-dim); font-style: italic; }

.acct-profile-form { margin: var(--space-2) 0 0; }
.acct-edit-label { display: block; margin: 0 0 var(--space-3); }
.acct-edit-label > span { display: block; color: var(--muted); font-size: 0.8rem; margin: 0 0 4px; }
.acct-edit-label input {
  width: 100%; box-sizing: border-box;
  background: var(--bg); border: 1px solid var(--border); color: var(--fg);
  border-radius: var(--radius); padding: 9px 11px;
  font-size: 16px;                 /* ≥16px: no iOS zoom */
  font-family: "Inter Tight", system-ui, sans-serif;
}
.acct-edit-label input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
.acct-edit-label input.is-invalid { border-color: #e0453a; box-shadow: inset 3px 0 0 #e0453a; }
.acct-field-err { display: block; margin-top: 4px; font-size: 0.74rem; color: #ff8585; }
.acct-field-err[hidden] { display: none; }

.acct-edit-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
.acct-btn-ghost, .acct-btn-primary {
  font-size: 0.82rem; font-weight: 500; padding: 7px 16px;
  border-radius: var(--radius); cursor: pointer; border: 1px solid var(--border);
}
.acct-btn-ghost { background: transparent; color: var(--muted); }
.acct-btn-ghost:hover { color: var(--fg); border-color: var(--border-strong); }
.acct-btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.acct-btn-primary:hover { background: var(--accent-hover); }
.acct-btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.acct-btn-ghost:focus-visible, .acct-btn-primary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --- Account modal: danger zone (delete account) ------------------------- */
.acct-danger {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px solid rgba(255,107,107,.28);   /* red-tinted divider, not a box */
}
.acct-subhead-danger { color: var(--data-red); border-top: none; padding-top: 0; }
.acct-danger-note { color: var(--muted); font-size: 0.78rem; line-height: 1.5; margin: 0 0 var(--space-3); }
.acct-danger-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }
/* Calm starter button — outlined red, low-key (escalation tracks commitment). */
.acct-btn-danger-ghost {
  font-size: 0.82rem; font-weight: 500; padding: 7px 16px;
  border-radius: var(--radius); cursor: pointer;
  background: transparent; color: var(--data-red); border: 1px solid rgba(255,107,107,.45);
}
.acct-btn-danger-ghost:hover { border-color: var(--data-red); background: rgba(255,107,107,.10); }
.acct-btn-danger-ghost:focus-visible { outline: 2px solid var(--data-red); outline-offset: 1px; }
/* Expanded confirm block — escalated: stronger red edge + wash */
.acct-danger-confirm {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px solid rgba(255,107,107,.40);
  border-left: 3px solid var(--data-red);
  border-radius: var(--radius);
  background: rgba(255,107,107,.06);
  overflow: hidden;
  animation: acct-danger-expand 140ms ease-out;
}
.acct-danger-confirm[hidden] { display: none; }
@keyframes acct-danger-expand { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: translateY(0); } }
.acct-danger-warn { color: var(--fg); font-size: 0.8rem; line-height: 1.5; margin: 0 0 var(--space-3); }
.acct-reauth { margin: 0 0 var(--space-3); }
.acct-reauth[hidden] { display: none; }
.acct-reauth-head { display: flex; align-items: center; gap: 7px; margin: 0 0 var(--space-2); }
.acct-reauth-lock { width: 14px; height: 14px; flex: 0 0 14px; color: var(--muted); }
.acct-reauth-title { font: 600 0.72rem/1 "Inter Tight", sans-serif; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.acct-reauth-pw[hidden], .acct-reauth-google[hidden] { display: none; }
.acct-reauth-pw { margin-bottom: 0; }
.acct-reauth-help { color: var(--muted); font-size: 0.8rem; margin: 0 0 var(--space-2); }
.acct-danger-error {
  margin: 0 0 var(--space-3); font-size: 0.78rem; line-height: 1.45; color: var(--data-red);
  background: rgba(255,107,107,.08); border: 1px solid rgba(255,107,107,.30);
  border-radius: var(--radius); padding: 8px 11px;
}
.acct-danger-error[hidden] { display: none; }
/* Final destructive action — filled red, dark ink for AA contrast (#1a1d23 on
   #ff6b6b ≈ 7.3:1), unmistakably distinct from the blue .acct-btn-primary. */
.acct-btn-danger {
  font-size: 0.82rem; font-weight: 600; padding: 7px 16px;
  border-radius: var(--radius); cursor: pointer;
  background: var(--data-red); color: #1a1d23; border: 1px solid var(--data-red);
}
.acct-btn-danger:hover { background: #ff8585; border-color: #ff8585; }
.acct-btn-danger:disabled { opacity: .45; cursor: not-allowed; }
.acct-btn-danger:focus-visible { outline: 2px solid var(--data-red); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .acct-danger-confirm { animation: none; } }
@media (prefers-reduced-motion: reduce) { .switch-track, .switch-thumb { transition: none; } }

/* --- About modal --------------------------------------------------------- */
.about-modal .about-hero { display: flex; gap: var(--space-3); align-items: flex-start; padding-right: 36px; }
.about-logo { border-radius: var(--radius); flex: 0 0 56px; }
.about-name { font: 700 1.4rem/1 "Inter Tight", sans-serif; color: var(--fg-strong); margin: 0; letter-spacing: -.01em; }
.about-tagline { color: var(--fg); font-size: 0.84rem; margin: 6px 0 0; line-height: 1.4; }
.about-version {
  color: var(--muted); font-family: "JetBrains Mono", monospace; font-size: 0.72rem;
  margin: 8px 0 0; letter-spacing: .02em;
}
.about-body { color: var(--fg); font-size: 0.86rem; line-height: 1.6; margin: var(--space-4) 0 0; }
.about-subhead {
  font: 600 0.72rem/1 "Inter Tight", sans-serif; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: var(--space-4) 0 var(--space-2);
}
.about-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0; margin: 0; list-style: none; }
.about-chip {
  font-family: "JetBrains Mono", monospace; font-size: 0.7rem; letter-spacing: .01em;
  color: var(--data-cyan);
  background: rgba(92,207,230,.08);
  border: 1px solid rgba(92,207,230,.22);
  border-radius: var(--radius); padding: 3px 8px;
}
.about-disclaimer {
  color: var(--muted); font-size: 0.76rem; line-height: 1.55;
  margin: var(--space-4) 0 0; padding: var(--space-3);
  background: rgba(255,255,255,.02); border-left: 2px solid var(--border-strong);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.about-footer {
  display: flex; align-items: center; gap: 8px;
  margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--border);
  color: var(--muted-dim); font-size: 0.74rem;
}

/* ===========================================================================
   AccountLAB — admin-only account monitor (js/labs/accountlab/main.js)
   =========================================================================== */
#accountlab-root {
  height: 100%; overflow-y: auto; box-sizing: border-box;
  padding: var(--space-5);
  background: var(--bg);
}
.al-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
  margin-bottom: var(--space-4); padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.al-title { font: 700 1.3rem/1.1 "Inter Tight", sans-serif; color: var(--fg-strong); margin: 0; letter-spacing: -.01em; }
.al-tagline { color: var(--muted); font-size: 0.84rem; margin: 6px 0 0; }
.al-head-actions { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.al-stats { display: flex; gap: var(--space-4); }
.al-stat { color: var(--muted); font-size: 0.8rem; }
.al-stat strong { color: var(--fg-strong); font-family: "JetBrains Mono", monospace; font-size: 0.95rem; }
.al-stat-warn strong { color: var(--data-amber); }
.al-refresh {
  background: transparent; border: 1px solid var(--border); color: var(--muted);
  font: inherit; font-size: 0.78rem; padding: 5px 12px; border-radius: var(--radius); cursor: pointer;
  transition: background-color 120ms ease-out, color 120ms ease-out, border-color 120ms ease-out;
}
.al-refresh:hover { color: var(--fg-strong); border-color: var(--border-strong); background: rgba(255,255,255,.04); }

.al-loading, .al-empty { color: var(--muted); font-size: 0.86rem; padding: var(--space-5) 0; }
.al-error {
  color: var(--data-red); font-size: 0.84rem; line-height: 1.5;
  background: rgba(255,107,107,.08); border: 1px solid rgba(255,107,107,.25);
  border-radius: var(--radius); padding: var(--space-3); max-width: 640px;
}

.al-table { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
.al-table thead th {
  text-align: left; color: var(--muted); font-weight: 600; font-size: 0.72rem;
  letter-spacing: .05em; text-transform: uppercase;
  padding: 0 var(--space-3) var(--space-2); border-bottom: 1px solid var(--border);
}
.al-table th.al-num, .al-table td.al-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.al-row { cursor: pointer; }
.al-row > td { padding: 9px var(--space-3); border-bottom: 1px solid var(--border); vertical-align: middle; }
.al-row:hover > td { background: rgba(255,255,255,.03); }
.al-row.is-open > td { background: rgba(74,163,255,.06); }
.al-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.al-acct { display: flex; align-items: center; gap: 10px; }
.al-avatar { width: 28px; height: 28px; flex: 0 0 28px; font-size: 12px; line-height: 1; }
.al-acct-text { display: flex; flex-direction: column; min-width: 0; }
.al-name { color: var(--fg-strong); font-weight: 500; }
.al-email { color: var(--muted); font-family: "JetBrains Mono", monospace; font-size: 0.72rem; }
.al-dash { color: var(--muted-dim); }

.al-pill {
  display: inline-block; font: 600 0.66rem/1 "Inter Tight", sans-serif;
  letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 8px; border-radius: var(--radius); border: 1px solid transparent;
}
.al-tier-free   { color: var(--muted);      background: rgba(138,146,157,.12); border-color: rgba(138,146,157,.30); }
.al-tier-pro    { color: var(--accent);     background: rgba(74,163,255,.12);  border-color: rgba(74,163,255,.35); }
.al-tier-max    { color: var(--data-amber); background: rgba(255,180,84,.12);   border-color: rgba(255,180,84,.38); }
.al-tier-admin  { color: #c4a7ff;           background: rgba(167,139,250,.14);  border-color: rgba(167,139,250,.40); }
.al-status-active    { color: var(--data-green); background: rgba(163,217,119,.12); border-color: rgba(163,217,119,.35); }
.al-status-suspended { color: var(--data-amber); background: rgba(255,180,84,.12);  border-color: rgba(255,180,84,.38); }
.al-status-deleted   { color: var(--muted);      background: rgba(138,146,157,.12); border-color: rgba(138,146,157,.32); }
/* Deleted accounts recede but stay legible (admin still audits them). */
.al-row-deleted > td { opacity: 0.55; }
.al-row-deleted:hover > td { opacity: 0.8; }

.al-detail-row > td { padding: 0 var(--space-3) var(--space-3); border-bottom: 1px solid var(--border); background: rgba(74,163,255,.04); }
.al-detail { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--space-2) var(--space-4); margin: var(--space-3) 0 0; }
.al-detail dt { color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: .04em; }
.al-detail dd { color: var(--fg); font-size: 0.82rem; margin: 2px 0 0; }
.al-detail .al-uid { font-family: "JetBrains Mono", monospace; font-size: 0.72rem; color: var(--muted); word-break: break-all; }
.al-foot-note { color: var(--muted-dim); font-size: 0.74rem; line-height: 1.45; margin: var(--space-4) 0 0; }

