:root {
  /* Primary white / secondary dark navy — a Procore/Autodesk-style light
     professional chrome instead of a dark "hacker tool" theme. Navy does
     double duty as both structural accent (active tabs, focus rings) and
     the default detection-box color, so the whole app reads as one brand
     rather than chrome + an unrelated accent hue. */
  --bg: #ffffff;
  --bg-panel: #ffffff;
  --bg-panel-2: #f3f5f9;
  --bg-subtle: #eef1f6;
  --border: #dde3ec;
  --border-strong: #0f2547;
  --text: #101d30;
  --text-dim: #5c6b80;
  --accent: #0f2547;
  --accent-hover: #18345c;
  --accent-soft: #e8edf5;
  --success: #16a34a;
  --success-soft: #e6f7ec;
  --danger: #dc2626;
  --danger-soft: #fdecec;
  --warning: #b45309;
  /* Small and mostly-uniform on purpose — flat/square, not the pill/rounded
     treatment of a generic AI-tool template. */
  --radius: 6px;
  --radius-sm: 4px;
  --radius-lg: 8px;
  --shadow-xs: 0 1px 2px rgba(15, 37, 71, 0.06);
  --shadow-sm: 0 1px 3px rgba(15, 37, 71, 0.08);
  --shadow-md: 0 4px 16px rgba(15, 37, 71, 0.10);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  height: 100%;
}

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

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

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

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

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.brand-mark {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  color: var(--accent);
}

.brand-name {
  font: inherit;
  font-size: 1.05rem;
  font-weight: inherit;
  letter-spacing: inherit;
  margin: 0;
  color: var(--accent);
}

.mode-switch {
  display: flex;
  gap: 2px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 3px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* Edge fade as a scroll affordance, but only on the side that actually has
     more to reach: the old rule faded both edges unconditionally, so a bar
     scrolled hard to the left still dimmed its leftmost tab as though
     something were hidden behind it. `.at-start` / `.at-end` are set from
     the scroll handler in main.js. With 9 modes in a 348px-wide bar, over
     half the tabs are off-screen on a phone — this is the only thing telling
     you Measure, Landmarks, Sketch and Drone exist at all. */
  --fade-l: 14px;
  --fade-r: 14px;
  -webkit-mask-image: linear-gradient(to right, transparent, black var(--fade-l), black calc(100% - var(--fade-r)), transparent);
  mask-image: linear-gradient(to right, transparent, black var(--fade-l), black calc(100% - var(--fade-r)), transparent);
}

.mode-switch.at-start { --fade-l: 0px; }
.mode-switch.at-end { --fade-r: 0px; }

.mode-switch::-webkit-scrollbar { display: none; }

.mode-btn {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  padding: 7px 12px;
  border-radius: var(--radius-sm);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s, color 0.12s;
}

.mode-btn:hover { color: var(--text); background: rgba(15, 37, 71, 0.06); }

.mode-btn.active {
  background: var(--accent);
  color: #ffffff;
}

/* Tabs that navigate to another page (Circuits) — look like .mode-btn without main.js binding them. */
.mode-link {
  flex: 0 0 auto; display: flex; align-items: center; gap: 6px; padding: 7px 12px;
  border-radius: var(--radius-sm); font-size: 0.82rem; font-weight: 600; white-space: nowrap;
  color: var(--text-dim); text-decoration: none; transition: background 0.12s, color 0.12s;
}
a.mode-link:hover { color: var(--text); background: rgba(15, 37, 71, 0.06); }
.mode-link.active { background: var(--accent); color: #ffffff; }

/* Icon sprites — sized down from their 24x24 viewBox for compact chrome use.
   currentColor means an icon always matches its button's current text color
   (dim/hover/active states) with no extra rules needed. */
.mode-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: currentColor;
}

.title-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  margin-right: 6px;
  vertical-align: -2px;
  color: var(--accent);
}

.header-status { display: flex; align-items: center; gap: 8px; }

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.75rem;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px 10px;
  white-space: nowrap;
  background: var(--bg-panel-2);
}

.status-pill.ready { color: var(--success); border-color: var(--success-soft); background: var(--success-soft); }
.status-pill.loading { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); }
.model-status { font-family: inherit; cursor: default; }
.model-status[aria-busy="true"] { cursor: progress; }
.model-status.error { color: #982b2b; border-color: #edcaca; background: #fff2f2; cursor: pointer; }
.model-status.error:focus-visible { outline: 2px solid #982b2b; outline-offset: 2px; }

.weather-pill {
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}

.weather-pill:hover { border-color: var(--accent); background: var(--accent-soft); }
.weather-pill.loaded { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); }

/* ---------- Layout ---------- */

.layout {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 16px;
  padding: 16px;
  min-height: 0;
  background: var(--bg-subtle);
}

.stage {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.stage-frame {
  position: relative;
  flex: 1 1 auto;
  /* The video/photo stage stays dark regardless of the app's light chrome —
     footage needs a neutral black surround the same way a photo viewer or
     video editor in a light-themed pro tool still letterboxes on black. */
  background: #05070a;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  min-height: 420px;
  box-shadow: var(--shadow-xs);
  background-image:
    linear-gradient(rgba(120, 155, 200, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120, 155, 200, 0.05) 1px, transparent 1px);
  background-size: 24px 24px;
}

.scan-sweep { display:none; position:absolute; z-index:5; top:0; left:0; right:0; height:2px; pointer-events:none; opacity:0; background:linear-gradient(90deg, transparent 0%, rgba(56,189,248,.18) 12%, #67e8f9 50%, rgba(56,189,248,.18) 88%, transparent 100%); box-shadow:0 0 12px rgba(34,211,238,.62); }
.stage-frame.is-scanning .scan-sweep { display:block; animation:scan-sweep 1.35s ease-in-out infinite; }
@keyframes scan-sweep { 0% { transform:translateY(0); opacity:0; } 10% { opacity:.72; } 88% { opacity:.72; } 100% { transform:translateY(var(--scan-distance, 100vh)); opacity:0; } }
.scan-status { position:absolute; z-index:7; top:12px; right:12px; display:flex; align-items:center; gap:8px; padding:7px 10px; border:1px solid rgba(203,213,225,.22); border-radius:5px; background:rgba(8,18,32,.88); color:#f2f6fb; font-size:10px; line-height:1; font-weight:650; letter-spacing:.035em; text-transform:uppercase; box-shadow:0 4px 14px rgba(0,0,0,.2); animation:scan-status-in .16s ease-out both; pointer-events:none; }
.scan-status[hidden] { display:none; }
.scan-status-dot { width:6px; height:6px; border-radius:50%; background:#60a5fa; box-shadow:0 0 0 0 rgba(96,165,250,.48); animation:scan-dot 1.1s ease-out infinite; }
@keyframes scan-status-in { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
@keyframes scan-dot { 65% { box-shadow:0 0 0 5px rgba(103,232,249,0); } 100% { box-shadow:0 0 0 0 rgba(103,232,249,0); } }

#video, #overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

#video { background: #000; }
#overlay { pointer-events: none; }

/* Persistent mode context, anchored to the stage itself rather than only the
   sidebar — the same "always know what you're looking at" pattern a
   Procore/Autodesk canvas keeps visible regardless of scroll position or,
   on mobile, whether the settings sheet is even open. */
/* Bottom-left, not top-left. Detection labels are drawn above their boxes
   and clamp to the top edge when a box runs off frame, so the top-left
   corner is exactly where overlay labels pile up — the badge was landing on
   top of "Building 83%" on a normal skyline shot. Nothing is ever drawn
   below the bottom-most box, so this corner stays clear. */
.stage-caption {
  position: absolute;
  bottom: 10px;
  left: 10px;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 9px;
  border: 1px solid rgba(203, 213, 225, 0.2);
  background: rgba(8, 18, 32, 0.84);
  color: #ffffff;
  border-radius: 5px;
  font-size: 0.68rem;
  font-weight: 650;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  box-shadow: 0 3px 12px rgba(0,0,0,.16);
  pointer-events: none;
}

.stage-caption .mode-icon { color: #ffffff; }

/* With no source loaded there's nothing on the stage to caption, and the
   badge landed directly on top of the empty state's heading on a narrow
   screen. The active mode tab already says which mode you're in, so the
   badge has no job here until there's a frame under it. */
.stage-frame:not(.has-source) .stage-caption { display: none; }

/* Centered when it fits, scrollable when it doesn't. The old fixed
   `justify-content: center` silently pushed content past the frame's clipped
   edges on a short stage — on a 390x844 phone the "Upload Photo/Video"
   button landed 45px below the frame's bottom and was simply unreachable,
   which is a dead end on the device this app is most used from. `margin:
   auto` on the inner block centers exactly like the old rule while still
   letting overflow scroll into reach, so this holds at any viewport rather
   than at the sizes that happened to be checked. */
.empty-state {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  text-align: center;
  padding: 40px;
  overflow-y: auto;
  overscroll-behavior: contain;
  color: #e8edf3;
}

.empty-state-inner {
  margin: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
}

/* Line-art mark rather than the 🏙️ emoji this used to be: emoji render at
   the mercy of the platform's own font (a flat blue-grey block on this
   machine at 2.4rem, out of key with every other icon in the app), and the
   header/panel icons are already an SVG sprite set — this now matches them. */
.empty-icon {
  color: #6d86a6;
  display: flex;
}

.empty-hint {
  font-size: 0.78rem;
  color: #6d7c90;
  margin: 2px 0 0;
}

.empty-state h2 {
  font-size: 1.3rem;
  font-weight: 700;
  margin: 0;
  max-width: 26ch;
}

.empty-state p {
  color: #98a5b8;
  max-width: 42ch;
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
}

.empty-actions {
  display: flex;
  gap: 10px;
  margin-top: 8px;
  flex-wrap: wrap;
  justify-content: center;
}

.stage-frame.drag-active {
  outline: 2px dashed var(--accent);
  outline-offset: -2px;
}

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

.loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: rgba(5, 7, 10, 0.91);
  font-size: 0.9rem;
  color: #b6c0cf;
  letter-spacing: .025em;
  backdrop-filter: blur(2px);
}

.spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid #232b36;
  border-top-color: var(--accent-hover);
  animation: spin 0.8s linear infinite;
}

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

.stage-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
}

.detection-count {
  margin-left: auto;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: .015em;
  color: var(--text-dim);
}
.detection-count.readout-change { animation:readout-change .24s ease-out; }
@keyframes readout-change { from { opacity:.45; transform:translateY(2px); } to { opacity:1; transform:translateY(0); } }

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

.btn {
  border: 1px solid var(--border);
  background: var(--bg-panel);
  color: var(--text);
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s, box-shadow 0.12s;
}

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

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #ffffff;
}

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

.btn-secondary {
  background: var(--bg-panel-2);
}

.btn-secondary:hover { background: var(--accent-soft); }

.btn-ghost {
  background: transparent;
  border-color: var(--border);
  padding: 7px 14px;
}

/* Buttons and mode tabs had no focus style at all — the browser default is
   suppressed by the custom backgrounds, so keyboard and switch-control users
   had no way to tell what was focused anywhere in the app's primary
   controls. `:focus-visible` rather than `:focus` so a mouse click doesn't
   leave a ring behind. Offset outward so it reads clearly against both the
   filled primary button and the tinted tab bar. */
.btn:focus-visible,
.mode-btn:focus-visible,
.file-btn:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The active tab is already navy-filled, so a navy ring on it would be
   invisible — it gets a light ring against its own dark background instead. */
.mode-btn.active:focus-visible,
.btn-primary:focus-visible {
  outline-color: #ffffff;
  box-shadow: 0 0 0 4px rgba(15, 37, 71, 0.45);
}

.file-btn { display: inline-flex; align-items: center; }
.btn-full { width: 100%; }

/* ---------- Sidebar ---------- */

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
}

.panel {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  transition: box-shadow 0.15s, border-color 0.15s;
}

.panel:hover { box-shadow: var(--shadow-xs); }

.panel-title {
  display: flex;
  align-items: center;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--accent);
  margin: 0 0 8px;
  font-weight: 700;
}

/* ---------- Stat tiles (Job Site / BIM) ---------- */
/* A live KPI row — the same "number, then its label, in a small card" pattern
   a PowerBI/Procore dashboard leads with, instead of burying these same
   figures only in canvas-drawn overlay text or a plain sentence. */

.stat-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  margin: 0 0 14px;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 6px;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-align: center;
}

.stat-value {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--accent);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.stat-label {
  font-size: 0.62rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1.3;
}

/* A tile left alone on the last row of the 3-up grid spans it instead of
   sitting in a third of the width with two empty cells beside it. That's
   BIM's "Completion stage", the one tile here whose value is a phrase rather
   than a number — "Envelope in progress" was wrapping onto three cramped
   lines in a narrow cell. Full width also lets it lay out as a row (label
   left, value right), which is the right shape for a text value: the other
   tiles are scanned as a row of numbers, this one is read as a sentence.
   Written as "last child that starts a row" so it applies wherever that
   shape occurs rather than being pinned to the BIM panel specifically. */
.stat-tiles > .stat-tile:last-child:nth-child(3n + 1) {
  grid-column: 1 / -1;
  flex-direction: row-reverse;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  text-align: left;
  padding: 9px 11px;
}

.stat-tiles > .stat-tile:last-child:nth-child(3n + 1) .stat-value {
  font-size: 0.9rem;
  text-align: right;
}

.stat-tiles > .stat-tile:last-child:nth-child(3n + 1) .stat-label {
  flex: 0 0 auto;
}

/* Four figures of the same kind (Drone): a 2x2 block, not three plus a
   stretched fourth — the span-the-row rule above is for a lone text value. */
.stat-tiles.quad { grid-template-columns: repeat(2, 1fr); }
.stat-tiles.quad > .stat-tile:last-child:nth-child(3n + 1) {
  grid-column: auto;
  flex-direction: column;
  text-align: center;
  gap: 2px;
  padding: 8px 6px;
}
.stat-tiles.quad > .stat-tile:last-child:nth-child(3n + 1) .stat-value { font-size: 1.05rem; text-align: center; }

/* Drone land-cover breakdown: one stacked bar (the whole frame) + legend. */
.land-cover { margin: 0 0 14px; }
.land-cover-bar {
  display: flex;
  height: 12px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
}
.land-cover-bar > span { flex-basis: 0; min-width: 2px; }
.land-cover-bar > span + span { box-shadow: -1px 0 0 var(--bg-panel); }
.land-cover-legend {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: grid;
  gap: 3px;
  font-size: 0.74rem;
}
.land-cover-legend li { display: flex; align-items: center; gap: 7px; min-width: 0; }
.land-cover-legend i { flex: 0 0 10px; height: 10px; border-radius: 2px; }
.land-cover-legend span { flex: 1 1 auto; color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.land-cover-legend b { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }

.drone-scale { display: grid; gap: 8px; margin: 0 0 12px; border: 0; padding: 0; min-width: 0; }
.drone-scale legend { font-size: 0.82rem; font-weight: 600; color: var(--text); padding: 0 0 6px; }
.drone-scale label { display: grid; gap: 4px; font-size: 0.78rem; color: var(--text-dim); }
.drone-scale .panel-hint { margin: 0; }

/* ---------- Toggle groups ---------- */
/* A tinted card around a set of related checkboxes — the same "grouped
   settings" convention Procore/Autodesk panels use so a cluster of options
   reads as one decision ("which element types") rather than a flat list of
   unrelated checkboxes. */

.toggle-group {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}

.panel-hint {
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-dim);
  margin: 0 0 12px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.82rem;
  margin-bottom: 10px;
}

.field:last-child { margin-bottom: 0; }

.field.checkbox {
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

.field input[type="range"] { accent-color: var(--accent); }
.field input[type="color"] {
  width: 100%;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: none;
  padding: 2px;
}

.class-filter {
  border: none;
  padding: 0;
  margin: 0 0 10px;
}

.class-filter legend {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  font-size: 0.82rem;
  padding: 0 0 6px;
  color: var(--text);
  font-weight: 600;
}

.filter-actions { display:inline-flex; gap:4px; margin-left:auto; }
.filter-actions button { min-height:28px; padding:3px 8px; border:1px solid var(--border); border-radius:4px; background:var(--bg-panel); color:var(--text-dim); font-family:inherit; font-size:11px; font-weight:600; line-height:1; cursor:pointer; }
.filter-actions button:hover { color:var(--accent); border-color:var(--accent); background:var(--accent-soft); }
.filter-actions button:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

.class-filter label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.82rem;
  padding: 4px 0;
  color: var(--text);
}

.about-panel { color: var(--text-dim); }

/* Disclosure wrapper applied by collapseLongHints() in main.js. Styled as a
   quiet inline affordance, not a button — it's supporting detail you can open,
   and it shouldn't compete with the panel's actual controls for attention. */
.hint-details {
  margin: 6px 0 0;
}

.hint-details > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-dim);
  padding: 5px 0;
  user-select: none;
}

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

/* Own triangle, so it can rotate on open and sit at the size/color the rest
   of the sidebar uses rather than the browser's default marker. */
.hint-details > summary::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 4px solid currentColor;
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transition: transform 0.15s ease;
}

.hint-details[open] > summary::before { transform: rotate(90deg) translateX(-1px); }
.hint-details > summary:hover { color: var(--accent); }

.hint-details > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.hint-details .panel-hint.small {
  margin-top: 0;
  padding-left: 9px;
  border-left: 2px solid var(--border);
}

.panel-hint.small {
  font-size: 0.72rem;
  margin-top: -4px;
}

.landmark-permissions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.landmark-permissions .btn {
  flex: 1 1 auto;
  padding: 8px 10px;
  font-size: 0.78rem;
}

.status-text {
  font-size: 0.76rem;
  color: var(--text-dim);
  margin: 0 0 4px;
}

.status-text.active { color: var(--success); }

.landmark-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 260px;
  overflow-y: auto;
}

.landmark-list li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 7px 10px;
  background: var(--bg-panel-2);
}

.landmark-list .lm-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zone-remove-btn {
  flex: 0 0 auto;
  padding: 3px 9px;
  font-size: 0.72rem;
}

.lm-height {
  color: var(--text-dim);
  font-weight: 400;
  font-size: 0.75rem;
}

.landmark-list .lm-meta {
  color: var(--text-dim);
  white-space: nowrap;
  flex: 0 0 auto;
}
.safety-entry-copy { display:flex; min-width:0; flex-direction:column; gap:3px; }
.safety-entry-detail { color:var(--text-dim); font-size:11px; line-height:1.4; overflow-wrap:anywhere; }

.landmark-list li.empty {
  color: var(--text-dim);
  justify-content: center;
  font-style: italic;
}

#occupancySparkline {
  width: 100%;
  height: 40px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-panel-2);
  display: block;
}

#overlay.canvas-pointer-active {
  pointer-events: auto;
  cursor: crosshair;
}

.legend-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 6px;
  flex: 0 0 auto;
}

.reference-input-row[hidden] { display: none; }

.reference-input-row {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}

.reference-input-row input {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 7px 8px;
  font-size: 0.82rem;
}

.reference-input-row select {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.82rem;
  padding: 7px 4px;
}

#tradeCategorySelect, #referenceUnitSelect {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 8px;
  font-size: 0.85rem;
}

.text-input-flex {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 8px 10px;
  font-size: 0.82rem;
}

#tradeNoteInput {
  background: var(--bg-panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 8px 10px;
  font-size: 0.82rem;
  width: 100%;
}

input[type="text"]:focus, input[type="number"]:focus, select:focus, .text-input-flex:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ---------- Toggles (checkboxes) ---------- */
/* Flat, square, native <input type=checkbox> restyled via appearance:none —
   no rounded pill/circle anywhere, a filled navy square with a white
   checkmark when checked. Applies globally so every existing checkbox
   (class filters, safety flags, display panel, etc.) picks this up without
   touching markup. */

input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  border: 1.5px solid var(--border-strong);
  border-radius: 3px;
  background: var(--bg-panel);
  cursor: pointer;
  position: relative;
  margin: 0;
  transition: background-color 0.12s, border-color 0.12s;
}

input[type="checkbox"]:hover { border-color: var(--accent); }

input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}

input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 1px;
  width: 4px;
  height: 8px;
  border: solid #ffffff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .layout {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }
  .stage { flex: 0 0 auto; }
  .stage-frame { flex: 0 0 auto; }
  .sidebar { flex: 0 0 auto; overflow: visible; }
  .app-header {
    flex-wrap: wrap;
  }
  .stage-frame {
    min-height: 280px;
  }
  /* iOS Safari auto-zooms the page on focusing any input under 16px — these
     are the ones a user is likely to tap on a phone (text/number fields,
     dropdowns), so they get bumped regardless of their desktop size. */
  input[type="text"],
  input[type="number"],
  select,
  .text-input-flex {
    font-size: 16px;
  }
  .btn,
  .mode-btn {
    min-height: 40px;
  }
  .empty-actions .btn,
  .btn-full {
    min-height: 44px;
  }
  /* The detection-count text (BIM/Electrical stats can run long) was liable
     to get squeezed against the toolbar buttons on a narrow screen — letting
     it wrap to its own full-width row keeps both legible. */
  .stage-toolbar {
    flex-wrap: wrap;
  }
  .detection-count {
    margin-left: 0;
    flex: 1 1 100%;
    text-align: right;
  }
  /* Native range/checkbox controls have a small hit area by default — this
     grows it without changing how big they look, easier to hit precisely
     with a finger instead of a mouse pointer. */
  input[type="range"] {
    height: 28px;
  }
  input[type="checkbox"] {
    width: 20px;
    height: 20px;
  }
  input[type="checkbox"]:checked::after {
    left: 6px;
    top: 2px;
    width: 5px;
    height: 10px;
  }

  /* The checkbox graphic grew to 20px above, but the thing a finger actually
     aims at is the whole label row, and that was still only 20px tall — well
     under the ~44px every touch guideline asks for, and this app is operated
     one-handed, outdoors, often in work gloves. Padding the row (rather than
     inflating the box) keeps the visual density while making the real hit
     area a comfortable target. */
  .field.checkbox,
  .class-filter label,
  .hint-details > summary {
    min-height: 44px;
    padding: 4px 0;
    align-items: center;
  }

  /* The stage is the whole point of the app on a phone, and 280px of a
     844px screen left it smaller than the settings text below it. Sized
     against the viewport so it scales with the device instead of being a
     fixed number tuned for one handset. */
  .stage-frame {
    min-height: min(56vh, 460px);
  }

  /* Three stacked header rows (brand / tabs / status) ate 168px before the
     stage even started. The brand and the status pills have room to share a
     row at any phone width, which gives that space back to the stage. */
  .app-header {
    gap: 8px;
    padding: 10px 14px;
  }
  .brand { flex: 1 1 auto; }
  .header-status { order: 2; }
  .mode-switch { order: 3; flex: 1 1 100%; }

  .empty-state {
    padding: 20px 16px;
  }
  .empty-state h2 {
    font-size: 1.15rem;
  }
  /* Both actions sit on one row rather than stacking — "Upload Photo/Video"
     is the primary path for anyone reviewing footage shot earlier, and
     burying it below the fold is what made it unreachable before. */
  .empty-actions {
    width: 100%;
    flex-wrap: nowrap;
  }
  .empty-actions .btn {
    flex: 1 1 0;
    padding: 9px 8px;
    justify-content: center;
    text-align: center;
  }
}


/* ---------- Reduced motion ---------- */
/* The spinner, the tab-bar's smooth scroll, and every hover/open transition
   are decoration; a user who has asked their OS for less motion should not
   have to sit through them. The spinner keeps a slow rotation rather than
   freezing outright — a completely static "loading" indicator reads as a
   hung app, which is worse than no motion at all. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .spinner {
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
}

/* ============================================================
   Landing page
   ============================================================
   The app's front door. It shares the app's tokens (navy accent, flat
   6px radii, Inter) so the marketing surface and the tool read as one
   product rather than a template bolted onto a tool. Nothing here is
   loaded or measured by the app itself — when the shell is revealed the
   whole landing element is simply removed from the flow. */

.landing {
  --lp-max: 1120px;
  background: var(--bg);
  color: var(--text);
  overflow-x: hidden;
}

.landing[hidden] { display: none; }

/* The app shell fills the viewport via flex on <body>; the landing is a
   normal scrolling document, so <body> has to stop stretching it. */
body:has(.landing:not([hidden])) { display: block; height: auto; }

.app-shell {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.app-shell[hidden] { display: none; }

/* ---------- Landing nav ---------- */

.lp-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 20px;
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: 14px 24px;
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: saturate(1.6) blur(10px);
}

.lp-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-right: auto;
  text-decoration: none;
  color: var(--accent);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.lp-brand .brand-name { font-size: 1.05rem; }

.lp-nav-links { display: flex; gap: 22px; }

.lp-nav-links a {
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.88rem;
  font-weight: 600;
}

.lp-nav-links a:hover { color: var(--accent); }

.lp-nav-links a:focus-visible,
.lp-brand:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* ---------- Hero ---------- */

.lp-hero {
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: 56px 24px 0;
  text-align: center;
}

.lp-eyebrow {
  margin: 0 0 18px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* clamp() rather than breakpoints: the headline is the one element that
   has to hold its line-shape from a 390px phone to a wide desktop. */
.lp-h1 {
  margin: 0 auto 20px;
  max-width: 18ch;
  font-size: clamp(2.1rem, 5.4vw, 3.7rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-weight: 800;
  color: var(--accent);
}

.lp-lede {
  margin: 0 auto;
  max-width: 62ch;
  font-size: clamp(1rem, 1.5vw, 1.15rem);
  line-height: 1.6;
  color: var(--text-dim);
}

.lp-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  margin: 28px 0 12px;
}

.btn-lg {
  padding: 13px 26px;
  font-size: 0.95rem;
  font-weight: 700;
  text-decoration: none;
  min-height: 48px;
  align-items: center;
}

.lp-cta-note {
  margin: 0;
  font-size: 0.82rem;
  color: var(--text-dim);
}

/* ---------- Product shot ---------- */

.lp-shot {
  margin: 44px 0 0;
}

.lp-shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  background: var(--bg-subtle);
}

.lp-shot figcaption {
  margin-top: 10px;
  font-size: 0.78rem;
  color: var(--text-dim);
  text-align: center;
}

.lp-shot-inset { margin: 0; }
.lp-shot-inset figcaption { text-align: left; }

/* ---------- Facts strip ---------- */
/* Four checkable statements about the product, in place of the customer
   logos a page like this usually carries — there are no customers to
   cite, and inventing them is the one thing that would make the page
   less trustworthy rather than more. */

.lp-facts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  max-width: var(--lp-max);
  margin: 64px auto 0;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.lp-facts > div {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 22px 18px;
  background: var(--bg-panel);
  text-align: center;
}

.lp-facts strong {
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--accent);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.lp-facts span {
  font-size: 0.78rem;
  color: var(--text-dim);
  line-height: 1.4;
}

/* ---------- Sections ---------- */

.lp-section {
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: 84px 24px 0;
}

.lp-h2 {
  margin: 0 0 12px;
  font-size: clamp(1.5rem, 3vw, 2.2rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 800;
  color: var(--accent);
}

.lp-h3 {
  margin: 0 0 10px;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--accent);
}

.lp-sub {
  margin: 0 0 32px;
  max-width: 60ch;
  color: var(--text-dim);
  line-height: 1.6;
}

.lp-body {
  margin: 0 0 20px;
  max-width: 54ch;
  color: var(--text-dim);
  line-height: 1.65;
}

/* ---------- Modes grid ---------- */

.lp-modes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.lp-modes li {
  padding: 20px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}

.lp-modes li:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-sm);
  transform: translateY(-2px);
}

.lp-modes svg {
  width: 22px;
  height: 22px;
  color: var(--accent);
  margin-bottom: 12px;
}

.lp-modes h4 {
  margin: 0 0 6px;
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--text);
}

.lp-modes p {
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--text-dim);
}

/* ---------- Split section ---------- */

.lp-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: center;
}

.lp-checks {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.lp-checks li {
  position: relative;
  padding-left: 28px;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text-dim);
}

.lp-checks li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 6px;
  width: 6px;
  height: 11px;
  border: solid var(--success);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ---------- Steps ---------- */

.lp-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.lp-steps li {
  padding: 24px 20px;
  background: var(--bg-panel-2);
  border-radius: var(--radius);
}

.lp-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin-bottom: 14px;
  border-radius: 50%;
  background: var(--accent);
  color: #ffffff;
  font-size: 0.85rem;
  font-weight: 700;
}

.lp-steps h4 {
  margin: 0 0 6px;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
}

.lp-steps p {
  margin: 0;
  font-size: 0.87rem;
  line-height: 1.55;
  color: var(--text-dim);
}

/* ---------- Honest-limits block ---------- */
/* Deliberately quiet and late in the page, but present: the app states a
   limitation at every point it makes a claim, and a landing page that
   dropped that voice would be overselling what the tool does. */

.lp-honest {
  max-width: 820px;
}

.lp-honest p {
  margin: 0;
  padding: 20px 22px;
  background: var(--bg-panel-2);
  border-left: 3px solid var(--warning);
  border-radius: var(--radius-sm);
  font-size: 0.87rem;
  line-height: 1.65;
  color: var(--text-dim);
}

/* ---------- Final CTA + footer ---------- */

.lp-final {
  max-width: var(--lp-max);
  margin: 96px auto 0;
  padding: 64px 24px;
  text-align: center;
  background: var(--bg-panel-2);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.lp-final p {
  margin: 0 0 26px;
  color: var(--text-dim);
}

.lp-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 18px;
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: 28px 24px 44px;
  color: var(--text-dim);
  font-size: 0.82rem;
}

.lp-footer p { margin: 0; }

/* ---------- Landing responsive ---------- */

@media (max-width: 900px) {
  .lp-modes { grid-template-columns: repeat(2, 1fr); }
  .lp-split { grid-template-columns: 1fr; gap: 32px; }
  .lp-facts { grid-template-columns: repeat(2, 1fr); }
  .lp-steps { grid-template-columns: 1fr; }
}

@media (max-width: 620px) {
  /* The section anchors are the first thing to go: the sticky bar has to
     keep the brand and the CTA, and those are what the page is for. */
  .lp-nav-links { display: none; }
  .lp-nav { padding: 10px 16px; }
  .lp-hero { padding-top: 36px; }
  .lp-section { padding: 56px 16px 0; }
  .lp-modes { grid-template-columns: 1fr; }
  .lp-cta-row .btn { flex: 1 1 100%; justify-content: center; }
  .lp-final { margin-top: 64px; padding: 48px 16px; }
  .lp-footer { padding: 24px 16px 36px; }
}

/* The app header's brand doubles as the way back to the landing page, so it
   has to be a real button — but it must not look like one sitting in the
   header, so it inherits the brand's own type and colour. */
.brand-home {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 2px 4px;
  margin: -2px -4px;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.brand-home:hover { background: var(--accent-soft); }

.brand-home:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Rotate control: an icon button that carries its current angle as a badge,
   so a rotated source is obvious from the toolbar without reading the frame. */
.rotate-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.rotate-btn.is-active {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}

.rotation-badge {
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--accent);
  color: #ffffff;
}

/* The <video> keeps playing and keeps feeding the detectors; it just stops
   being what you look at once the frame is rotated (the overlay canvas paints
   the rotated frame instead). Opacity rather than display:none so playback
   and layout are untouched. */
.stage-frame.source-rotated #video { opacity: 0; }

/* Conditional controls must stay hidden when component styles set display. */
[hidden] { display: none !important; }
.workspace-guide { display:flex; align-items:center; justify-content:space-between; gap:18px; padding:5px 2px 8px; }
.workspace-eyebrow { font-size:10px; font-weight:750; letter-spacing:.13em; color:var(--text-dim); }
.workspace-guide h2 { margin:5px 0; font-size:20px; letter-spacing:-.025em; }
.workspace-guide p { margin:0; font-size:12px; color:var(--text-dim); line-height:1.5; }
.mode-picker { display:flex; flex-direction:column; gap:5px; flex-shrink:0; font-size:11px; color:var(--text-dim); }
.mode-picker select { min-height:40px; min-width:130px; padding:8px 12px; border:1px solid var(--border); border-radius:6px; background:white; color:var(--text); }
.workspace-controls { display:flex; align-items:center; gap:10px; min-height:52px; padding:8px 10px; border:1px solid var(--border); border-radius:7px; background:linear-gradient(180deg,#fff,#f9fbfd); box-shadow:var(--shadow-xs); }
.workspace-controls-title { flex:0 0 auto; color:var(--text-dim); font-size:10px; font-weight:750; letter-spacing:.08em; text-transform:uppercase; }
.workspace-chip { display:inline-flex; flex:0 0 auto; align-items:center; gap:6px; min-height:32px; padding:5px 9px; border:1px solid var(--border); border-radius:5px; background:#fff; color:#34445a; font-size:11px; font-weight:600; white-space:nowrap; }
.workspace-toggle { cursor:pointer; user-select:none; transition:border-color .12s, background .12s, color .12s; }
.workspace-toggle:has(input:checked) { border-color:#b7c6d9; background:#eef3f9; color:#142c4c; }
.workspace-chip input[type="checkbox"] { width:14px; height:14px; margin:0; }
.workspace-chip input[type="checkbox"]:checked::after { left:3px; top:0px; width:4px; height:8px; }
.workspace-chip input[type="range"] { width:108px; height:18px; margin:0; accent-color:var(--accent); }
.workspace-range-value { min-width:28px; color:var(--accent); text-align:right; font-variant-numeric:tabular-nums; }
.workspace-select { gap:8px; }
.workspace-select select { min-height:26px; padding:3px 6px; border:1px solid var(--border); border-radius:4px; background:white; color:var(--text); font-size:11px; }
.workspace-toggle-list { display:flex; flex:1 1 auto; align-items:center; gap:6px; min-width:0; overflow-x:auto; scrollbar-width:thin; }
.workspace-global-labels { margin-left:auto; }
.workspace-controls [type="range"]:focus-visible,
.workspace-controls select:focus-visible,
.workspace-controls input[type="checkbox"]:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.video-review-controls { display:flex; align-items:center; gap:8px; min-height:48px; padding:6px 9px; border:1px solid var(--border); border-radius:7px; background:#f8fafc; }
.video-review-controls .btn { min-height:32px; padding:5px 10px; font-size:11px; }
.video-review-controls .video-step { min-width:42px; font-variant-numeric:tabular-nums; }
.video-play-toggle { min-width:60px; }
.video-scrubber-wrap { display:flex; flex:1 1 auto; min-width:60px; align-items:center; }
.video-scrubber-wrap input { width:100%; margin:0; accent-color:var(--accent); }
.video-time-readout { min-width:88px; color:var(--text-dim); text-align:center; font-size:11px; font-variant-numeric:tabular-nums; }
.video-speed-wrap { display:flex; flex:0 0 auto; align-items:center; gap:6px; color:var(--text-dim); font-size:10px; font-weight:600; }
.video-speed-wrap select { min-height:30px; padding:4px 6px; border:1px solid var(--border); border-radius:4px; background:white; color:var(--text); font-size:11px; }
.video-annotation-hint { margin:-5px 2px 0; color:var(--text-dim); font-size:10px; line-height:1.35; }
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
.bim-baseline-tools { display:flex; gap:7px; margin:0 0 4px; }
.bim-baseline-tools .btn { flex:1 1 auto; min-height:34px; padding:6px 9px; font-size:11px; }
#bimBaselineNote { margin:4px 0 12px; padding:8px 10px; border:1px solid var(--border); border-radius:5px; background:#f8fafc; line-height:1.45; }
.stage-frame:not(.has-source) #video { visibility:hidden; }
.stage-frame:not(.has-source) { background-color:#101d30; }
.stage-toolbar { flex-wrap:wrap; }
.source-name { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text-dim); font-size:12px; flex-basis:100%; }
.source-feedback { padding:11px 14px; border:1px solid #b8cce5; border-radius:6px; background:#eff5fc; color:#153b67; font-size:13px; line-height:1.5; overflow-wrap:anywhere; }
.source-feedback[data-tone="error"] { color:#982b2b; background:#fff2f2; border-color:#edcaca; }
.source-feedback[data-tone="quality"] { color:#77540a; background:#fff8df; border-color:#ead99f; }
.safety-quality-notice { margin:10px 0; padding:10px 12px; border:1px solid #cbd9e8; border-left:4px solid #6d86a6; border-radius:5px; background:#f3f7fb; color:#34475f; font-size:12px; line-height:1.5; }
.safety-quality-notice.limited { border-color:#ead99f; border-left-color:#c18a12; background:#fff8df; color:#654908; }
.mode-picker select:focus-visible { outline:2px solid var(--accent); outline-offset:3px; }
@media (max-width:620px) {
  .workspace-guide { align-items:flex-start; flex-direction:column; gap:10px; }
  .workspace-guide h2 { font-size:19px; }
  .mode-picker { width:100%; flex-direction:row; align-items:center; justify-content:space-between; }
  .mode-picker select { min-width:170px; }
  .workspace-controls { align-items:flex-start; flex-wrap:wrap; gap:6px; padding:8px; }
  .workspace-controls-title { align-self:center; margin-right:2px; }
  .workspace-toggle-list { order:5; flex-basis:100%; padding-bottom:2px; }
  .workspace-global-labels { margin-left:auto; }
  .workspace-chip { min-height:36px; }
  .workspace-chip input[type="range"] { width:94px; }
  .video-review-controls { display:grid; grid-template-columns:auto auto minmax(44px,1fr) auto auto; gap:5px; padding:6px; }
  .video-speed-wrap { grid-column:1 / -1; justify-content:flex-end; }
  .video-time-readout { min-width:74px; font-size:10px; }
  .video-review-controls .btn { min-height:40px; }
  .bim-baseline-tools .btn { min-height:42px; }
  .stage-frame:not(.has-source) { min-height:360px; }
  .stage-toolbar .btn { min-height:44px; }
}

.measurement-summary { margin:12px 0 8px; padding:10px 12px; background:var(--accent-soft); border-radius:6px; font-size:12px; line-height:1.5; font-weight:600; color:var(--accent); }
#measurementList { max-height:320px; overflow-y:auto; }

/* ---------- Toggles, v2 ----------
   Toolbar chips: the checkbox inside each chip is visually hidden and the
   chip itself carries the state — filled tint + solid mark when on, plain
   outline + hollow mark when off — so on/off reads at a glance from across
   the toolbar instead of from a 14px tick. The mark takes the layer's own
   overlay colour (--chip-color, copied from the panel's legend swatch), so
   a chip names the boxes it controls on the image. Square corners throughout,
   matching the flat navy theme. */
.workspace-toggle { position: relative; gap: 7px; color: var(--text-dim); font-weight: 600; }
.workspace-toggle > input[type="checkbox"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; margin: 0; cursor: pointer; border: 0; }
.workspace-toggle > input[type="checkbox"]::after { display: none; }
.chip-mark {
  flex: 0 0 auto; width: 10px; height: 10px; border-radius: 2px;
  border: 1.5px solid var(--chip-color, #8a98ab); background: transparent;
  transition: background-color .12s, border-color .12s, box-shadow .12s;
}
.workspace-toggle:hover { border-color: #9fb3cc; color: var(--text); }
.workspace-toggle:has(input:checked) { border-color: #9fb3cc; background: var(--accent-soft); color: var(--accent); }
.workspace-toggle:has(input:checked) .chip-mark { background: var(--chip-color, var(--accent)); border-color: var(--chip-color, var(--accent)); box-shadow: inset 0 0 0 1.5px rgba(255,255,255,.55); }
.workspace-toggle:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.workspace-toggle:active { transform: translateY(1px); }

/* Named groups (Job Site: Detect / Safety flags / Experimental). On a wide
   screen the grouped list takes its own full-width row and wraps, so every
   chip is visible; each group is a labelled cluster. */
.workspace-controls:has(.workspace-toggle-list.grouped) { flex-wrap: wrap; row-gap: 8px; }
.workspace-toggle-list.grouped { flex-basis: 100%; flex-wrap: wrap; overflow: visible; gap: 6px 14px; padding-top: 8px; border-top: 1px solid var(--border); }
.workspace-toggle-group { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.workspace-group-name { flex: 0 0 auto; margin-right: 2px; color: var(--text-dim); font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.workspace-toggle-group + .workspace-toggle-group { padding-left: 14px; border-left: 1px solid var(--border); }

/* A sideways-scrolling row (phones) fades out at whichever edge has more,
   so a chip cut off at the edge reads as "more this way". */
.workspace-toggle-list.fade-right { -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent); }
.workspace-toggle-list.fade-left { -webkit-mask-image: linear-gradient(90deg, transparent, #000 28px); mask-image: linear-gradient(90deg, transparent, #000 28px); }
.workspace-toggle-list.fade-left.fade-right { -webkit-mask-image: linear-gradient(90deg, transparent, #000 28px, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(90deg, transparent, #000 28px, #000 calc(100% - 28px), transparent); }

/* Slider chip: value reads as part of the control, not a stray number. */
.workspace-chip:has(input[type="range"]) { gap: 9px; }
.workspace-range-value { padding: 2px 6px; border-radius: 3px; background: var(--accent-soft); font-size: 11px; font-weight: 700; }

/* Sidebar on/off rows → switches. Label first, the switch in a consistent
   right-hand column, so a long list of layers and safety flags scans as
   "what" then "on/off" instead of a ragged column of ticks. Square-cornered
   track and knob to match the flat theme. Applies to layer/flag rows only;
   checkboxes elsewhere keep the square tick. */
.class-filter label, .field.checkbox { cursor: pointer; }
.class-filter label > input[type="checkbox"],
.field.checkbox > input[type="checkbox"] {
  order: 3; margin-left: auto;
  width: 30px; height: 17px; border: 0; border-radius: 3px;
  background: #c3ccd8; transition: background-color .15s;
}
.class-filter label > input[type="checkbox"]::after,
.field.checkbox > input[type="checkbox"]::after {
  content: ''; position: absolute; left: 2px; top: 2px; width: 13px; height: 13px;
  border: 0; border-radius: 2px; background: #fff; transform: none;
  box-shadow: 0 1px 2px rgba(15, 37, 71, .25); transition: transform .15s;
}
.class-filter label > input[type="checkbox"]:checked,
.field.checkbox > input[type="checkbox"]:checked { background: var(--accent); }
.class-filter label > input[type="checkbox"]:checked::after,
.field.checkbox > input[type="checkbox"]:checked::after { left: 2px; top: 2px; width: 13px; height: 13px; border: 0; transform: translateX(13px); }
.class-filter label > input[type="checkbox"]:hover:not(:checked),
.field.checkbox > input[type="checkbox"]:hover:not(:checked) { background: #aab6c6; }
.class-filter label { gap: 10px; }
.toggle-group > label + label { border-top: 1px solid rgba(221, 227, 236, .8); }
.class-filter label:has(input:not(:checked)) { color: var(--text-dim); }
.field.checkbox > span { flex: 1 1 auto; }
@media (prefers-reduced-motion: reduce) {
  .class-filter label > input[type="checkbox"]::after, .field.checkbox > input[type="checkbox"]::after, .chip-mark { transition: none; }
}

/* Header: a long model status ("Loading object detection model…") pushed
   the mode tabs off the edge ("Dron"). It truncates; the full text is in
   its title. */
.model-status { max-width: 190px; overflow: hidden; text-overflow: ellipsis; display: inline-block; }
.mode-switch { min-width: 0; }

@media (max-width: 860px) {
  .class-filter label > input[type="checkbox"],
  .field.checkbox > input[type="checkbox"] { width: 38px; height: 22px; }
  .class-filter label > input[type="checkbox"]::after,
  .field.checkbox > input[type="checkbox"]::after,
  .class-filter label > input[type="checkbox"]:checked::after,
  .field.checkbox > input[type="checkbox"]:checked::after { width: 18px; height: 18px; }
  .class-filter label > input[type="checkbox"]:checked::after,
  .field.checkbox > input[type="checkbox"]:checked::after { transform: translateX(16px); }
}
@media (max-width: 620px) {
  /* One sideways row on a phone (13 chips would wrap to five rows); groups
     stay inline, separated by their names. */
  .workspace-toggle-list.grouped { flex-wrap: nowrap; overflow-x: auto; padding-top: 6px; }
  .workspace-toggle-group { flex-wrap: nowrap; }
}

/* ---------- Robot bridge panel ---------- */
.robot-advisory { margin: 0 0 12px; padding: 8px 10px; border: 1px solid #ead99f; border-left: 4px solid #c18a12; border-radius: 5px; background: #fff8df; color: #654908; font-size: 0.75rem; line-height: 1.45; }
.robot-fields { display: grid; gap: 8px; margin: 0 0 10px; }
.robot-fields .field { margin: 0; font-size: 0.75rem; color: var(--text-dim); }
.robot-fields input, .robot-fields select { min-height: 32px; padding: 5px 8px; border: 1px solid var(--border); border-radius: 4px; background: var(--bg-panel); color: var(--text); font: inherit; font-size: 0.8rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.robot-fields select { font-family: inherit; }
.robot-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.robot-status { display: flex; align-items: center; gap: 7px; margin: 8px 0 2px; font-size: 0.78rem; font-weight: 600; color: var(--text-dim); overflow-wrap: anywhere; }
.robot-status::before { content: ''; flex: 0 0 8px; height: 8px; border-radius: 2px; background: #aab6c6; }
.robot-status[data-state="connected"] { color: var(--success); }
.robot-status[data-state="connected"]::before { background: var(--success); }
.robot-status[data-state="connecting"]::before { background: var(--accent); }
.robot-status[data-state="retrying"], .robot-status[data-state="error"] { color: #982b2b; }
.robot-status[data-state="retrying"]::before, .robot-status[data-state="error"]::before { background: var(--danger); }
#robotPanel code { font-size: 0.72rem; padding: 0 3px; border-radius: 3px; background: var(--bg-subtle); overflow-wrap: anywhere; }
#rosDownloadBtn { margin-top: 6px; }
@media (max-width: 860px) { .robot-fields input, .robot-fields select { font-size: 16px; min-height: 40px; } }
.robot-fields .field, .robot-row > * { min-width: 0; }
.robot-fields input, .robot-fields select { width: 100%; box-sizing: border-box; }

/* ---------- Progress mode ---------- */
.progress-trades { display: grid; gap: 6px; margin: 4px 0 12px; }
.progress-trade { display: grid; grid-template-columns: minmax(84px, 1fr) 2fr; grid-template-rows: auto auto; column-gap: 8px; align-items: center; font-size: 12px; }
.progress-trade-name { font-weight: 600; color: var(--text); }
.progress-bar { position: relative; height: 8px; background: var(--bg-subtle); border: 1px solid var(--border); border-radius: 2px; overflow: hidden; }
.progress-bar i { position: absolute; inset: 0 auto 0 0; transition: width 0.3s ease; }
.progress-trade-status { grid-column: 2; color: var(--text-dim); font-size: 11px; }
.progress-plan { margin: 0 0 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px 8px; }
.progress-plan summary { cursor: pointer; font-weight: 600; font-size: 12px; }
.progress-plan-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 8px; }
.progress-plan-grid .field { margin-bottom: 6px; }
.progress-plan-grid input, .progress-plan-grid select { width: 100%; min-width: 0; }
.progress-save { display: grid; gap: 4px; margin-bottom: 10px; }
.progress-changes { font-size: 12px; margin-bottom: 6px; }
.progress-changes p { margin: 2px 0; }
.progress-changes-head { color: var(--text-dim); }
.progress-change.advanced { color: var(--success); }
.progress-change.regressed { color: var(--danger); }
.progress-timeline { list-style: none; margin: 0 0 10px; padding: 0; display: grid; gap: 6px; max-height: 260px; overflow-y: auto; }
.progress-timeline li { display: flex; gap: 8px; align-items: center; font-size: 12px; padding: 4px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.progress-timeline img { width: 64px; height: 48px; object-fit: cover; border-radius: 2px; flex: none; }
.progress-actions { display: grid; gap: 6px; }
.progress-confirm { font-size: 12px; margin: 0; }
/* Progress → Project: schedule status, all-areas dashboard, floor plan with pins */
.progress-project { margin: 10px 0 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px 8px; }
.progress-project > summary { cursor: pointer; font-weight: 600; font-size: 12px; }
.progress-project > .field { margin-bottom: 6px; }
.progress-project > .field input { width: 100%; min-width: 0; }
.progress-sched-status { font-size: 12px; margin: 0 0 8px; padding-left: 8px; border-left: 3px solid var(--c, transparent); color: var(--text); }
.progress-sched-status:empty { display: none; }
.progress-dashboard { font-size: 12px; margin-bottom: 8px; }
.progress-kpis { margin: 0 0 6px; color: var(--text-dim); }
.progress-attention { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 4px; }
.progress-attention li { display: grid; grid-template-columns: auto 1fr; column-gap: 6px; align-items: baseline; }
.progress-attention-why { grid-column: 1 / -1; color: var(--text-dim); font-size: 11px; }
.progress-status-tag { font-size: 11px; padding: 0 6px; border: 1px solid var(--c); color: var(--c); border-radius: 9px; white-space: nowrap; }
.progress-area-link { background: none; border: 0; padding: 0; font: inherit; font-weight: 600; color: var(--text); text-align: left; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; min-width: 0; overflow-wrap: anywhere; }
.progress-floors { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.progress-floors th, .progress-floors td { text-align: left; padding: 2px 4px; border-bottom: 1px solid var(--border); }
.progress-floors th { color: var(--text-dim); font-weight: 600; font-size: 11px; }
.progress-floorplan { margin: 6px 0 8px; }
.progress-plan-upload { display: block; width: 100%; text-align: center; margin-bottom: 6px; cursor: pointer; box-sizing: border-box; }
.progress-plan-canvas { display: block; width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: crosshair; margin-top: 4px; }
.progress-plan-canvas[hidden] { display: none; }
/* BIM → Facade progress by floor */
.facade-floors { display: grid; gap: 3px; margin: 2px 0 8px; max-height: 320px; overflow-y: auto; }
.facade-floor { display: grid; grid-template-columns: 58px 1fr auto; gap: 6px; align-items: center; font-size: 11px; }
.facade-floor-name { font-weight: 600; color: var(--text); }
.facade-floor-status { color: var(--text-dim); white-space: nowrap; }
/* Progress → Project: curve, schedule import, project files */
.progress-curve { position: relative; margin: 0 0 8px; color: var(--text); }
.progress-curve:empty { display: none; }
.progress-project-files { display: grid; gap: 6px; margin-top: 8px; }
.progress-file-btn { display: block; text-align: center; cursor: pointer; box-sizing: border-box; }
.schedule-preview { font-size: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px 8px; display: grid; gap: 4px; }
.schedule-preview ul { margin: 0; padding-left: 16px; color: var(--text-dim); max-height: 120px; overflow-y: auto; }
.schedule-preview .schedule-actions { display: flex; gap: 6px; }
.survey-preview { margin: 4px 0 8px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.survey-preview:empty { display: none; }
/* AI assistant */
.ai-key-row { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; margin-bottom: 4px; }
.ai-key-row .field.checkbox { margin: 0; flex: 1 1 auto; }
.ai-conversation { display: grid; gap: 8px; margin: 4px 0 8px; max-height: 420px; overflow-y: auto; }
.ai-conversation:empty { display: none; }
.ai-msg { font-size: 13px; line-height: 1.45; padding: 6px 8px; border-radius: var(--radius-sm); border: 1px solid var(--border); white-space: pre-wrap; overflow-wrap: anywhere; }
.ai-msg.user { background: var(--bg-subtle); }
.ai-msg.error { border-color: var(--danger); color: var(--danger); }
.ai-msg .ai-meta { display: block; margin-top: 4px; font-size: 11px; color: var(--text-dim); white-space: normal; }
.ai-actions { display: flex; gap: 6px; margin-bottom: 6px; }
.ai-actions .btn-primary { flex: 1; }
#aiQuestion { width: 100%; box-sizing: border-box; resize: vertical; font: inherit; }
/* Progress → Video walkthrough */
.walk-seg-suggested { font-size: 11px; color: var(--text-dim); margin-top: -3px; }
.progress-trade-dates { margin: 0 0 8px; }
.progress-trade-dates > summary { cursor: pointer; font-size: 12px; font-weight: 600; }
.progress-trade-date-rows { display: grid; gap: 4px; }
.progress-trade-date-row { display: grid; grid-template-columns: minmax(70px, 1fr) 1fr 1fr; gap: 4px; align-items: center; font-size: 11px; }
.progress-trade-date-row input { min-width: 0; width: 100%; box-sizing: border-box; font-size: 11px; }
.progress-scale-row { font-size: 12px; margin: 0 0 6px; }
.progress-scale-row input { width: 80px; }
.progress-quantities { margin: 0 0 6px; color: var(--text-dim); }
.walk-buttons { display: flex; gap: 6px; align-items: flex-end; }
.walk-buttons .btn { flex: 1; }
.walk-progress { width: 100%; height: 6px; margin: 0 0 6px; }
.walk-progress[hidden] { display: none; }
.walk-segments { list-style: none; margin: 0 0 8px; padding: 0; display: grid; gap: 8px; }
.walk-seg { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px; display: grid; gap: 6px; }
.walk-seg-head { display: flex; gap: 8px; align-items: flex-start; font-size: 12px; }
.walk-seg-head img { width: 96px; height: 54px; object-fit: cover; border-radius: 2px; flex: none; }
.walk-seg-head div { display: grid; gap: 2px; min-width: 0; }
.walk-seg-summary { color: var(--text-dim); font-size: 11px; }
.walk-seg-name { width: 100%; min-width: 0; box-sizing: border-box; }
.walk-seg-actions:empty { display: none; }
.walk-strip { display: flex; gap: 3px; overflow-x: auto; padding-bottom: 2px; }
.walk-strip button { padding: 0; border: 1px solid var(--border); border-radius: 2px; background: none; cursor: pointer; flex: none; line-height: 0; }
.walk-strip button:disabled { cursor: default; outline: 2px solid var(--accent); outline-offset: -2px; }
.walk-strip img { width: 48px; height: 27px; object-fit: cover; }
@media (prefers-reduced-motion: reduce) { .progress-bar i { transition: none; } }
