/* Console chrome. Colours, radii, shadows and type here are Semantic UI 2.5's
   own values (button/segment/label/message/input/menu/table variables), so the
   page reads as native Semantic without shipping the framework -- the console
   runs on a robot that is often off the internet, and only the icon font is
   loaded from a CDN (with an SVG fallback). Keep new rules on Semantic values
   rather than inventing gradients or lifts: Semantic surfaces are flat. */
:root {
  color-scheme: light;
  --page-bg: #f7f7f7;
  --surface: #ffffff;
  --border: rgba(34,36,38,.15);
  --text: rgba(0,0,0,.87);
  --text-muted: rgba(0,0,0,.6);
  --text-faint: rgba(0,0,0,.4);
  --primary: #2185d0;
  --primary-hover: #1678c2;
  --primary-active: #1a69a4;
  --primary-tint: #f8ffff;
  --primary-border: #a9d5de;
  --focus-border: #85b7d9;
  --positive: #21ba45;
  --positive-hover: #16ab39;
  --positive-tint: #fcfff5;
  --positive-text: #2c662d;
  --positive-border: #a3c293;
  --negative: #db2828;
  --negative-hover: #d01919;
  --negative-tint: #fff6f6;
  --negative-text: #9f3a38;
  --negative-border: #e0b4b4;
  --warning: #b58105;
  --warning-tint: #fffaf3;
  --warning-text: #573a08;
  --warning-border: #c9ba9b;
  --grey-button: #e0e1e2;
  --grey-button-hover: #cacbcd;
  --grey-button-active: #babbbc;
  --nav-height: 60px;
  --radius: .28571429rem;
  --shadow-sm: 0 1px 2px 0 rgba(34,36,38,.15);
  --shadow-md: 0 2px 4px 0 rgba(34,36,38,.12), 0 2px 10px 0 rgba(34,36,38,.15);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* Semantic's own stack. Lato is used when the device has it and otherwise
   falls back exactly as Semantic itself falls back. */
body { margin: 0; min-height: 100vh; background: var(--page-bg); color: var(--text); font: 14px/1.4285em Lato,-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,Helvetica,sans-serif; padding-top: var(--nav-height); }
h1,h2,h3,p { margin: 0; }
h1,h2,h3 { font-weight: 700; line-height: 1.28571429em; }
button, input, select { font: inherit; color: inherit; }

/* ---------- Navbar ---------- */
/* The top bar keeps its original look, so its tints and shadow are written out
   here rather than taken from the shared tokens above -- those now carry
   Semantic's own (much paler) message colours. */
#navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 50; height: var(--nav-height); background: transparent; border-bottom: 1px solid transparent; transition: background-color .22s ease, border-color .22s ease, box-shadow .22s ease; }
#navbar.scrolled { background: rgba(255,255,255,.92); backdrop-filter: blur(10px); border-bottom-color: var(--border); box-shadow: 0 1px 2px rgba(0,0,0,.05), 0 1px 3px rgba(0,0,0,.04); }
.nav-inner { height: 100%; max-width: 1320px; margin: 0 auto; padding: 0 24px; display: flex; align-items: center; gap: 24px; }
.nav-brand { display: flex; align-items: baseline; gap: 8px; font-size: 17px; font-weight: 700; letter-spacing: -.01em; white-space: nowrap; }
.nav-brand small { font-size: 11px; font-weight: 500; color: var(--text-faint); letter-spacing: .04em; }
.brand-dot { width: 9px; height: 9px; flex: none; border-radius: 50%; background: var(--primary); box-shadow: 0 0 0 3px #eaf4fc; transition: background-color .2s ease, box-shadow .2s ease; }
/* In the simple console the brand dot doubles as the connection indicator --
   its colour is the only cue, so it stays to the small set below rather than
   growing a shade per state. */
.brand-dot.online { background: var(--positive); box-shadow: 0 0 0 3px #e7f8ec; }
.brand-dot.syncing, .brand-dot.protected, .brand-dot.reconnecting { background: var(--warning); box-shadow: 0 0 0 3px #fdf6e3; }
.brand-dot.paused { background: var(--text-faint); box-shadow: 0 0 0 3px rgba(0,0,0,.06); }
.brand-dot.waiting { background: var(--negative); box-shadow: 0 0 0 3px #fdedec; }
/* The logo carries the connection dot as a corner badge, so the brand reads as
   one mark instead of a dot and a picture sitting side by side. */
/* .nav-brand aligns its children on their baselines, which would hang the
   wordmark off the top of the taller logo; the pair is centred instead, and the
   wordmark keeps its own inline baseline for the trailing <small>. */
.brand-mark { position: relative; flex: none; align-self: center; display: inline-flex; }
.brand-text { align-self: center; }
.brand-mark img { display: block; width: 28px; height: 28px; }
.brand-mark .brand-dot { position: absolute; right: 0; bottom: 1px; }
/* Wide bars lay the tabs and the status pills out inline; `display: contents`
   keeps them direct flex children of .nav-inner so the menu wrapper itself has
   no effect until the narrow layout turns it into a dropdown. */
.nav-menu { display: contents; }
.nav-toggle { display: none; }
.nav-tabs { display: flex; gap: 4px; margin-left: 4px; }
.tab { border: 0; background: none; color: var(--text-muted); padding: 8px 16px; border-radius: 999px; cursor: pointer; font-size: 14px; font-weight: 600; transition: background-color .15s, color .15s; }
.tab:hover { background: rgba(0,0,0,.045); color: var(--text); }
.tab.active { color: #fff; background: var(--primary); }
.nav-status { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.battery-pill { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 999px; background: rgba(0,0,0,.045); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 12px; white-space: nowrap; }
.battery-icon { position: relative; display: inline-block; width: 20px; height: 11px; flex: none; border: 1.5px solid var(--text-muted); border-radius: 2px; }
.battery-icon::after { content: ''; position: absolute; top: 2.5px; right: -4px; width: 2.5px; height: 4px; background: var(--text-muted); border-radius: 0 1px 1px 0; }
.battery-fill { display: block; height: 100%; width: 0; background: var(--positive); }
.battery-pill.battery-mid .battery-fill { background: var(--warning); }
.battery-pill.battery-low .battery-fill { background: var(--negative); }
.battery-pill.battery-low b { color: var(--negative); }
.battery-charge-icon { display: none; font-style: normal; font-size: 12px; color: var(--warning); }
.battery-pill.battery-charging .battery-charge-icon { display: inline-block; }
/* The simple console folds the charge glyph into the battery icon itself
   instead of a separate lightning-bolt pill neighbour. */
.battery-bolt { display: none; position: absolute; top: 50%; left: 50%; width: 6px; height: 9px; transform: translate(-50%, -50%); background: #fff; clip-path: polygon(58% 0%, 0% 58%, 38% 58%, 28% 100%, 100% 34%, 52% 34%); filter: drop-shadow(0 0 .5px rgba(0,0,0,.45)); }
.battery-pill.battery-charging .battery-bolt { display: block; animation: battery-bolt-pulse 1.6s ease-in-out infinite; }
@keyframes battery-bolt-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
.robot-mode-pill { display: inline-flex; align-items: center; padding: 5px 12px; border-radius: 999px; font-size: 12px; font-weight: 700; letter-spacing: .02em; }
.robot-mode-pill > span { font-variant-numeric: tabular-nums; }
.robot-mode-pill.mode-idle { color: var(--text-muted); background: rgba(0,0,0,.08); }
.robot-mode-pill.mode-init { color: var(--warning); background: #fdf6e3; }
.robot-mode-pill.mode-running { color: var(--positive); background: #e7f8ec; }
.robot-mode-pill.mode-error { color: var(--negative); background: #fdedec; }
.status-pill { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px; font-size: 12px; font-weight: 700; letter-spacing: .02em; white-space: nowrap; }
/* `flex: none` keeps the state dot a circle: as a flex item it would otherwise
   be squeezed into an ellipse whenever the bar runs out of width. */
.status-pill::before { content: ''; width: 6px; height: 6px; flex: none; border-radius: 50%; background: currentColor; }
.status-pill.online { color: var(--positive); background: #e7f8ec; }
.status-pill.offline { color: var(--negative); background: #fdedec; }
.status-pill.syncing, .status-pill.protected { color: var(--warning); background: #fdf6e3; }
.status-pill.reconnecting { color: var(--warning); background: #fdf6e3; }
.status-pill.paused { color: var(--text-muted); background: rgba(0,0,0,.08); }

/* ---------- Layout ---------- */
main { max-width: 1320px; margin: 0 auto; padding: 20px 24px 40px; }
.panel { display: none; }
.panel.active { display: block; }
/* Semantic segment: flat white, hairline border, one soft shadow, no hover. */
.segment { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm); padding: 1em; margin-bottom: 16px; }
.segment:last-child { margin-bottom: 0; }
.segment-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.segment-header h3 { font-size: 16px; font-weight: 700; }
.segment-actions { display: flex; align-items: center; gap: 8px; min-width: 0; }
.meta-text { color: var(--text-faint); font-size: 12px; font-variant-numeric: tabular-nums; }
.meta-strong { color: var(--text-muted); font-weight: 700; }
.divider { border: 0; border-top: 1px solid rgba(34,36,38,.1); margin: 14px 0; }
/* Semantic "basic" labels: white face, coloured text and a matching ring. */
.status-tag { display: inline-flex; align-items: center; padding: .5833em .833em; border-radius: var(--radius); background: #fff; color: var(--text-muted); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; box-shadow: 0 0 0 1px var(--border) inset; }
.status-tag.warn { color: var(--warning-text); background: var(--warning-tint); box-shadow: 0 0 0 1px var(--warning-border) inset; }
.status-tag.bad { color: var(--negative-text); background: var(--negative-tint); box-shadow: 0 0 0 1px var(--negative-border) inset; }
.tag { display: inline-flex; align-items: center; padding: .5833em .833em; border-radius: var(--radius); font-size: 11px; font-weight: 700; }
.tag-positive { color: var(--positive-text); background: var(--positive-tint); box-shadow: 0 0 0 1px var(--positive-border) inset; }
.tag-warning { color: var(--warning-text); background: var(--warning-tint); box-shadow: 0 0 0 1px var(--warning-border) inset; }
.tag-muted { color: var(--text-muted); background: #e8e8e8; }
/* Semantic negative message. */
.inline-error { position: absolute; left: 10px; right: 10px; bottom: 10px; z-index: 2; padding: .75em 1em; border-radius: var(--radius); background: var(--negative-tint); color: var(--negative-text); box-shadow: 0 0 0 1px var(--negative-border) inset; font-size: 12px; font-weight: 700; }
.inline-error[hidden] { display: none; }

/* ---------- Operate tab ---------- */
.operate-row { display: grid; grid-template-columns: minmax(0,2.1fr) minmax(260px,1fr); gap: 16px; align-items: stretch; margin-bottom: 16px; }
.operate-row-split { grid-template-columns: 1fr 1fr; }
.operate-row > .segment { margin-bottom: 0; }
.video-card { padding: 0; overflow: hidden; display: flex; }
.camera-stage { position: relative; flex: 1; min-height: 420px; display: grid; place-items: center; overflow: hidden; background: #1b1c1d; border-radius: var(--radius); }
.camera-stage video { display: block; width: 100%; height: 100%; max-height: 78vh; object-fit: contain; }
.camera-empty { position: absolute; color: rgba(255,255,255,.55); font-size: 13px; text-align: center; padding: 0 20px; }
.camera-meta { position: absolute; left: 10px; bottom: 10px; padding: 4px 9px; border-radius: var(--radius); background: rgba(0,0,0,.5); color: #fff; font-size: 11px; font-variant-numeric: tabular-nums; }
.fullscreen-btn { position: absolute; right: 10px; top: 10px; z-index: 2; width: 34px; height: 34px; padding: 0; display: grid; place-items: center; border: 0; border-radius: var(--radius); background: rgba(0,0,0,.5); color: #fff; font-size: 16px; line-height: 1; cursor: pointer; transition: background-color .1s ease; }
.fullscreen-btn:hover { background: var(--primary); }
/* CSS-only "fill the viewport" overlay -- deliberately not the real Fullscreen
   API; see the comment in app.js for why. Sizing the video with auto width/
   height plus max-width/max-height (rather than 100%/100% + object-fit) lets
   the browser's own replaced-element algorithm shrink it to fit without ever
   cropping, independent of object-fit quirks across engines. */
.camera-stage.is-fullscreen { position: fixed; inset: 0; z-index: 1000; width: 100vw; height: 100vh; height: 100dvh; display: flex; align-items: center; justify-content: center; border-radius: 0; background: #000; }
.camera-stage.is-fullscreen:fullscreen { width: 100%; height: 100%; }
.camera-stage.is-fullscreen video { width: auto; height: auto; max-width: 100%; max-height: 100%; object-fit: contain; }
.camera-stage.is-fullscreen .fullscreen-btn { top: max(10px, env(safe-area-inset-top)); right: max(10px, env(safe-area-inset-right)); }
body.camera-fullscreen-lock { overflow: hidden; }
.controls-card { display: flex; flex-direction: column; }
.control-block { display: flex; flex-direction: column; gap: 10px; }
.control-block-row { flex-direction: row; align-items: center; justify-content: space-between; }
.control-block-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.control-block label { font-size: 13px; font-weight: 700; color: var(--text); }
.control-block-head b { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--primary); font-weight: 700; }
input[type="range"] { width: 100%; height: 6px; accent-color: var(--primary); cursor: pointer; }
input[type="range"]::-webkit-slider-thumb { cursor: pointer; }
input[type="range"]::-moz-range-thumb { cursor: pointer; }
.input-row { display: flex; gap: 8px; }
.input-row input { flex: 1; min-width: 0; }
/* Semantic input: hairline border, no shadow, blue border on focus. */
input[type="number"], input[type="text"], input[type="password"], select { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); padding: .67857143em 1em; min-height: 36px; transition: border-color .1s ease; }
input[type="number"]:focus, input[type="text"]:focus, input[type="password"]:focus, select:focus { outline: none; border-color: var(--focus-border); box-shadow: none; }
input[type="number"]:disabled, input[type="text"]:disabled, select:disabled, button:disabled { opacity: .45; cursor: not-allowed; }
.field-dirty { border-color: var(--warning-border) !important; background: var(--warning-tint); }
input[type="range"].field-dirty { accent-color: #fbbd08; }
/* Semantic buttons: flat fills, bold labels, no borders and no hover lift.
   .btn is the standard grey button, .btn-primary the blue call to action,
   .btn-negative the destructive red one and .btn-secondary Semantic's "basic"
   button (transparent with an inset ring) for low-emphasis actions. */
.btn { border: 0; border-radius: var(--radius); background: var(--grey-button) none; color: var(--text-muted); padding: .78571429em 1.5em; min-height: 36px; cursor: pointer; font-weight: 700; line-height: 1em; white-space: nowrap; text-shadow: none; box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34,36,38,.15) inset; transition: background-color .1s ease, color .1s ease, box-shadow .1s ease; }
.btn:hover:not(:disabled) { background-color: var(--grey-button-hover); color: rgba(0,0,0,.8); }
.btn:active:not(:disabled) { background-color: var(--grey-button-active); color: rgba(0,0,0,.9); }
.btn:focus-visible { outline: 2px solid var(--focus-border); outline-offset: 2px; }
.btn-primary { background-color: var(--primary); color: #fff; }
.btn-primary:hover:not(:disabled) { background-color: var(--primary-hover); color: #fff; }
.btn-primary:active:not(:disabled) { background-color: var(--primary-active); color: #fff; }
.btn-negative { background-color: var(--negative); color: #fff; }
.btn-negative:hover:not(:disabled) { background-color: var(--negative-hover); color: #fff; }
.btn-negative:active:not(:disabled) { background-color: #b21e1e; color: #fff; }
.btn-positive { background-color: var(--positive); color: #fff; }
.btn-positive:hover:not(:disabled) { background-color: var(--positive-hover); color: #fff; }
.btn-secondary { background: transparent none; color: var(--text-muted); box-shadow: 0 0 0 1px var(--border) inset; }
.btn-secondary:hover:not(:disabled) { background: #fff; color: var(--text); box-shadow: 0 0 0 1px rgba(34,36,38,.35) inset; }
.btn-secondary:active:not(:disabled) { background: #f8f8f8; color: rgba(0,0,0,.9); box-shadow: 0 0 0 1px rgba(0,0,0,.15) inset; }
/* Semantic icon button: square padding instead of the wide label padding. */
.btn-icon { width: 36px; padding: .78571429em 0; text-align: center; }
.stat-row { display: flex; gap: 16px; padding: 10px 0 4px; font-size: 12px; color: var(--text-muted); flex-wrap: wrap; }
.stat-row b { color: var(--text); font-variant-numeric: tabular-nums; margin-left: 5px; font-weight: 700; }
/* ---------- Status Overview Cards ---------- */
.system-overview { margin-bottom: 16px; }
.status-overview-card { background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-sm); }
.status-overview-card .segment-header h3 { color: var(--text); }
.status-actions { display: flex; gap: 8px; align-items: center; }
.status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.status-item { display: flex; align-items: flex-start; gap: 14px; padding: 1em; background: #fff; border-radius: var(--radius); border: 1px solid var(--border); box-shadow: var(--shadow-sm); }
.status-icon { width: 44px; height: 44px; flex-shrink: 0; display: grid; place-items: center; border-radius: var(--radius); background: #f3f4f5; color: var(--text-muted); }
.status-icon-mode.mode-idle { background: #f3f4f5; color: var(--text-muted); }
.status-icon-mode.mode-sleeping { background: #f3f0ff; color: #6435c9; }
.status-icon-mode.mode-init { background: var(--warning-tint); color: var(--warning-text); }
.status-icon-mode.mode-running { background: var(--positive-tint); color: var(--positive-text); }
.status-icon-mode.mode-error { background: var(--negative-tint); color: var(--negative-text); }
.status-icon-health.health-good { background: var(--positive-tint); color: var(--positive-text); }
.status-icon-health.health-warn { background: var(--warning-tint); color: var(--warning-text); }
.status-icon-health.health-bad { background: var(--negative-tint); color: var(--negative-text); }
.status-content { flex: 1; min-width: 0; }
.status-label { font-size: 12px; color: var(--text-faint); font-weight: 700; margin-bottom: 4px; }
.status-value { font-size: 16px; font-weight: 700; line-height: 1.3; color: var(--text); overflow-wrap: break-word; }
.status-detail { font-size: 12px; margin-top: 4px; color: var(--text-muted); }

.robot-state-tags { display: flex; align-items: center; gap: 7px; }
.robot-state-summary { padding-top: 0; padding-bottom: 10px; }
.robot-plugin-grid { display: grid; grid-template-columns: repeat(auto-fit,minmax(240px,1fr)); gap: 8px; }
.robot-plugin { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; gap: 3px 8px; padding: .78571429em 1em; border: 1px solid var(--border); border-radius: var(--radius); background: #fff; }
.robot-plugin.bad { background: var(--negative-tint); border-color: var(--negative-border); color: var(--negative-text); }
.robot-plugin > b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.robot-plugin > small { grid-column: 1 / -1; color: var(--text-faint); font-size: 11px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.metric-value.status-good { color: var(--positive); }
.metric-value.status-warn { color: var(--warning); }
.metric-value.status-bad { color: var(--negative); }

/* Semantic toggle checkbox: flat grey track, blue when on, white handle with
   Semantic's own two-part shadow. */
.switch { position: relative; display: inline-flex; align-items: center; gap: 9px; cursor: pointer; user-select: none; font-weight: 700; white-space: nowrap; }
.switch input { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); }
.switch-track { width: 42px; height: 24px; flex: 0 0 auto; border-radius: 999px; background: rgba(0,0,0,.05); transition: background-color .3s ease; position: relative; }
.switch-track::after { content: ""; display: block; width: 18px; height: 18px; margin: 3px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px 0 rgba(34,36,38,.15), 0 0 0 1px rgba(34,36,38,.15) inset; transition: transform .3s ease; }
.switch:hover .switch-track { background: rgba(0,0,0,.15); }
.switch input:checked + .switch-track { background: var(--primary); }
.switch:hover input:checked + .switch-track { background: var(--primary-hover); }
.switch input:checked + .switch-track::after { transform: translateX(18px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--focus-border); outline-offset: 3px; }
.switch input:disabled + .switch-track { opacity: .45; }
.switch:has(input:disabled) { cursor: not-allowed; color: var(--text-faint); }
.switch-row { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; margin-bottom: 6px; }

.imu-card, .multi-zone-range-card { display: flex; flex-direction: column; position: relative; }
#imu-attitude, #multi-zone-range-canvas { position: relative; flex: 1; min-height: 300px; overflow: hidden; border-radius: var(--radius); background: #1b1c1d; outline: 0; touch-action: none; }
#imu-attitude canvas, #multi-zone-range-canvas canvas { display: block; width: 100%; height: 100%; cursor: grab; }
#imu-attitude canvas:active, #multi-zone-range-canvas canvas:active { cursor: grabbing; }
#imu-attitude:focus-visible, #multi-zone-range-canvas:focus-visible { box-shadow: inset 0 0 0 2px var(--focus-border); }

/* ---------- SLAM ---------- */
.slam-card { display: flex; flex-direction: column; position: relative; }
.slam-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.slam-stats { padding-top: 10px; font-size: 12px; }
.slam-stats b { color: var(--text); font-weight: 700; margin-left: 4px; }
.disclosure { margin: 10px 0 12px; }
.disclosure > summary { display: inline-flex; align-items: center; gap: 5px; width: fit-content; color: var(--text-muted); font-size: 12px; font-weight: 700; cursor: pointer; list-style: none; padding: 4px 8px; border-radius: var(--radius); transition: color .1s ease, background-color .1s ease; }
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before { content: '▸'; font-size: 10px; transition: transform .2s ease; }
.disclosure[open] > summary::before { transform: rotate(90deg); }
.disclosure > summary:hover { color: var(--text); background: rgba(0,0,0,.05); }
.disclosure > .input-row { margin-top: 12px; }
#slam-canvas { position: relative; flex: 1; min-height: 460px; overflow: hidden; border-radius: var(--radius); background: #1b1c1d; touch-action: none; }
#slam-canvas canvas { display: block; width: 100%; height: 100%; cursor: grab; }
#slam-canvas canvas:active { cursor: grabbing; }
#slam-canvas.goal-pick canvas, #slam-canvas.goal-pick canvas:active { cursor: crosshair; }

/* HUD overlays. The layers themselves are click-through so a drag started
   anywhere still orbits the camera; only the controls in them take clicks. */
.hud { position: absolute; z-index: 2; display: flex; align-items: center; gap: 6px; pointer-events: none; font-size: 12px; font-variant-numeric: tabular-nums; color: rgba(255,255,255,.9); }
/* `display` here outranks the browser's own [hidden] rule, so every layer
   that toggles itself off needs saying so explicitly. */
.hud[hidden], .hud-bottom > *[hidden] { display: none; }
.hud button, .hud select { pointer-events: auto; }
.hud-topleft { top: 10px; left: 10px; }
.hud-topleft b { padding: 4px 8px; border-radius: var(--radius); background: rgba(0,0,0,.6); font-weight: 700; }
.hud-topright { top: 10px; right: 10px; }
/* Semantic "inverted basic" buttons: dark face, hairline ring, blue on hover. */
.hud-btn { border: 0; border-radius: var(--radius); background: rgba(0,0,0,.6); color: rgba(255,255,255,.8); box-shadow: 0 0 0 1px rgba(255,255,255,.2) inset; padding: 4px 9px; font-size: 12px; font-weight: 700; cursor: pointer; transition: background-color .1s ease, color .1s ease; }
.hud-btn:hover { background: var(--primary); color: #fff; box-shadow: none; }
.hud-btn.active { background: var(--primary); color: #fff; box-shadow: none; }
.hud-btn-danger { background: var(--negative); color: #fff; box-shadow: none; }
.hud-btn-danger:hover { background: var(--negative-hover); }
.hud-chip { padding: 4px 8px; border-radius: var(--radius); background: rgba(0,0,0,.6); color: rgba(255,255,255,.75); }
.hud-chip.warn { color: #fbbd08; }
.hud-chip.bad { color: #ff695e; }
/* Scan progress reads as a loading bar across the top edge of the view it
   belongs to, rather than as one more row of the page. */
.hud-scan { top: 0; left: 0; right: 0; display: block; height: 22px; }
.hud-scan i { display: block; height: 3px; width: 0; background: var(--primary); transition: width .2s linear; }
.hud-scan span { display: block; padding: 3px 10px; color: rgba(255,255,255,.7); }
/* Hint, message and navigation bar stack bottom-centre: each is transient, so
   they never fight for the same fixed slot. */
.hud-bottom { left: 10px; right: 10px; bottom: 10px; flex-direction: column; align-items: center; gap: 6px; }
.hud-hint, .hud-toast { max-width: 100%; padding: 5px 11px; border-radius: 999px; background: rgba(0,0,0,.75); text-align: center; }
.hud-hint { color: rgba(255,255,255,.6); }
/* The toast has to take the pointer for its tooltip -- that hover is the only
   way to the diagnostics behind the message. */
.hud-toast { color: #fff; overflow-wrap: anywhere; pointer-events: auto; cursor: help; }
.hud-nav { display: flex; align-items: center; gap: 10px; max-width: 100%; padding: 6px 6px 6px 12px; border-radius: 999px; background: rgba(0,0,0,.85); box-shadow: var(--shadow-md); }
.hud-nav b { font-weight: 700; }
.hud-nav b.warn { color: #fbbd08; }
.hud-nav b.bad { color: #ff695e; }
.hud-nav span { color: rgba(255,255,255,.66); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.slam-card .input-row { flex-wrap: wrap; gap: 8px; }
.slam-card .input-row input[type="text"] { flex: 1 1 200px; min-width: 160px; }

/* ---------- Vectors ---------- */
.vector { display: flex; flex-wrap: wrap; gap: 4px 12px; font-variant-numeric: tabular-nums; font-size: 13px; }
.vector span { display: inline-flex; align-items: center; gap: 5px; }
.vector i { width: 6px; height: 6px; border-radius: 50%; }
.vector b { color: var(--text-faint); font-weight: 700; }
.axis-legend { display: flex; align-items: center; gap: 10px; color: var(--text-faint); font-size: 11px; }
.axis-legend span { display: flex; align-items: center; gap: 5px; }
.axis-legend i, .axis-x, .axis-y, .axis-z { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.axis-x { background: #db2828; } .axis-y { background: #21ba45; } .axis-z { background: #2185d0; }

/* ---------- Tables ---------- */
.metrics-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 14px; }
.metric-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.metric-table tr { border-bottom: 1px solid rgba(34,36,38,.1); }
.metric-table tr:last-child { border-bottom: 0; }
.metric-table td { padding: .78571429em .5em; vertical-align: middle; }
.metric-label { color: var(--text-muted); white-space: nowrap; width: 1%; }
.metric-value { font-weight: 700; font-variant-numeric: tabular-nums; width: 100%; }
.metric-unit { color: var(--text-faint); text-align: right; white-space: nowrap; }
.table-scroll { overflow-x: auto; }
/* Semantic table: #F9FAFB header, hairline rows, no zebra. */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th { text-align: left; padding: .92857143em .78571429em; font-size: 13px; font-weight: 700; color: var(--text); background: #f9fafb; border-bottom: 1px solid rgba(34,36,38,.1); }
.data-table td { padding: .78571429em; border-bottom: 1px solid rgba(34,36,38,.1); }
.data-table tbody tr { transition: background-color .1s ease; }
.data-table tbody tr:hover { background: rgba(0,0,0,.05); }
.data-table tr:last-child td { border-bottom: 0; }
.data-table .mono { font-variant-numeric: tabular-nums; font-family: ui-monospace,SFMono-Regular,Consolas,monospace; font-size: 12px; color: var(--text-muted); }

/* ---------- Charts ---------- */
.chart-grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 14px; }
.chart-tile { display: flex; flex-direction: column; gap: 8px; }
.chart-tile-wide { margin-top: 14px; }
.chart-tile-label { font-size: 12px; font-weight: 700; color: var(--text-muted); }
.chart-tile-head { display: flex; align-items: center; justify-content: space-between; }
.chart-tile canvas { width: 100%; height: 140px; display: block; border: 1px solid var(--border); border-radius: var(--radius); background: #fff; }
.chart-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.chart-pair .chart-tile { margin-top: 0; }
.ahrs-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 14px; }

/* ---------- Settings forms ---------- */
.form-grid { display: flex; flex-wrap: wrap; align-items: end; gap: 12px; }
.form-grid label { display: grid; gap: 4px; font-size: 13px; font-weight: 700; color: var(--text); }
.form-grid input { width: 110px; }
.form-grid #camera-sensor-mode { width: 150px; }
.form-grid .checkbox-field { display: flex; flex-direction: row; align-items: center; gap: 6px; min-height: 34px; font-size: 13px; font-weight: 700; color: var(--text); }
.form-grid .checkbox-field input { width: auto; accent-color: var(--primary); }
.form-grid-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.select-field { display: flex; flex-direction: row; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--text); }

/* ---------- Responsive ---------- */
/* Below this width the bar cannot hold brand + tabs + battery without crushing
   them, so the tabs fold behind one toggle that shows the battery itself;
   connection state stays on the brand dot, which never moves. */
@media (max-width: 640px) {
  .nav-inner { gap: 12px; }
  /* Folded down to one control, the toggle shows the battery instead of a
     hamburger glyph -- connection state already lives in the brand dot. */
  .nav-toggle { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-width: 44px; min-height: 34px; margin-left: auto; padding: 5px 11px; border: 1px solid var(--border); border-radius: 999px; background: rgba(255,255,255,.9); cursor: pointer; }
  .nav-toggle .battery-pill.compact { padding: 0; background: none; }
  .nav-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
  .nav-menu { display: none; position: absolute; top: calc(var(--nav-height) - 8px); right: 14px; min-width: 200px; padding: 8px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 8px 28px rgba(0,0,0,.12); }
  #navbar.menu-open .nav-menu { display: block; }
  #navbar.menu-open { background: rgba(255,255,255,.96); backdrop-filter: blur(10px); border-bottom-color: var(--border); box-shadow: 0 1px 2px rgba(0,0,0,.05), 0 1px 3px rgba(0,0,0,.04); }
  .nav-menu .nav-tabs { flex-direction: column; gap: 4px; margin: 0; }
  .nav-menu .tab { padding: 10px 12px; border-radius: 8px; text-align: left; font-size: 14px; }
  /* The toggle already shows the battery; the dropdown is tabs only so the
     reading never appears twice at once. */
  .nav-status { display: none; }
}
@media (max-width: 1000px) {
  .operate-row, .operate-row-split { grid-template-columns: 1fr; }
  .camera-stage { min-height: 320px; }
  .chart-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 700px) {
  .nav-inner { padding: 0 14px; gap: 12px; }
  .nav-brand small { display: none; }
  .tab { padding: 7px 11px; font-size: 13px; }
  main { padding: 16px 14px 32px; }
  .metrics-grid { grid-template-columns: 1fr; }
  .chart-grid, .chart-pair { grid-template-columns: 1fr; }
  .form-grid { flex-direction: column; align-items: stretch; }
  .form-grid input { width: 100%; }
  .form-grid-actions { margin-left: 0; }
  .input-row { flex-direction: column; }
  .segment { padding: 1em; }
  /* The HUD keeps the same anchors on a phone, just tighter, so the map does
     not lose its top corners to two rows of chips. */
  .hud { font-size: 11px; }
  .hud-topleft, .hud-topright { top: 8px; }
  .hud-topleft { left: 8px; }
  .hud-topright { right: 8px; }
  .hud-btn { padding: 3px 7px; font-size: 11px; }
  .hud-bottom { left: 8px; right: 8px; bottom: 8px; }
  .hud-nav { flex-wrap: wrap; justify-content: center; border-radius: var(--radius); }
}
