/* Skyline — Antevolt house system.
   Warm dark ground, ember accent, chamfered plates with traced frames.
   Every colour is a token; decorative colour only ever comes from the
   palette eight. No pure black, no cool grey on the dark side. */

:root {
  /* --- grounds -------------------------------------------------- */
  --bg-primary: #1a1714;      /* page ground everywhere            */
  --bg-secondary: #181512;      /* cards, panels — one step deeper   */
  --bg-dropdown: #16120e;      /* header menus, form listboxes      */
  --border: #2c2620;      /* the standard hairline             */
  --border-light: #241f1a;      /* subtler dividers                  */

  /* --- inks on dark --------------------------------------------- */
  --text-light: #f0e9da;      /* headlines, the brightest bone     */
  --text-primary: #cfc8bc;      /* standard body                     */
  --text-muted: #9b9286;      /* secondary copy                    */
  --text-dim: #8a8175;      /* captions, labels — 4.66:1 floor   */

  /* --- accent: the ember ---------------------------------------- */
  --accent:        #c56e49;
  --accent-dark: #af603e;      /* display-size accent on light      */
  --accent-deep: #8e4829;      /* body-size accent links on light   */

  /* --- the decorative eight ------------------------------------- */
  --pal-slate: #a9b4ae;
  --pal-slate-mid: #97a8a2;
  --pal-slate-lo: #7f918c;
  --pal-steel: #929faa;
  --pal-mint: #9dc9ae;
  --pal-blush: #e5a893;
  --pal-ink: #1b1f1d;
  --pal-ink-soft: #39413d;

  /* --- light reading panels ------------------------------------- */
  --panel-light: #d5dcd9;
  --panel-light-ink: #1b1f1d;
  --panel-light-muted: #4f5e58;
  --panel-light-border: #b1beb9;

  /* --- surface treatments --------------------------------------- */
  --hover-light: rgba(240, 233, 218, .045);   /* bone at 4.5%, never grey */
  --weave: repeating-linear-gradient(135deg,
            rgba(255,255,255,.015) 0 1px, transparent 1px 7px);
  --weave-light: repeating-linear-gradient(135deg,
            rgba(27,31,29,.022) 0 1px, transparent 1px 7px);

  /* --- type ------------------------------------------------------ */
  --font-display: "Uxum Grotesque", "Bricolage Grotesque", "Satoshi", system-ui, sans-serif;
  --font-body: "Satoshi", system-ui, -apple-system, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* --- geometry -------------------------------------------------- */
  --r-sm: 10px;                  /* chips, menu rows, small controls */
  --r-md: 14px;                  /* buttons, cards, menus            */
  --r-lg: 20px;                  /* panels, plates, modals           */
  --r-pill: 999px;
  --radius: var(--r-md);
  /* kept so older rules that still ask for a chamfer resolve to a curve */
  --chamfer-sm: var(--r-md);
  --chamfer-md: var(--r-lg);
  --chamfer-lg: var(--r-lg);
  --ease: cubic-bezier(.32, .72, 0, 1);
  --ease-dur: .34s;

  /* --- legacy aliases: every old rule lands on the new system ---- */
  --navy:      var(--pal-ink);
  --navy-card: var(--bg-secondary);
  --navy-edge: var(--border);
  --gold:      var(--accent);
  --ink:       var(--text-primary);
  --ink-dark:  var(--accent);   /* was "the darkest surface" — now the ember */
  --ink-soft:  var(--text-muted);
  --gray-100:  rgba(240,233,218,.045);
  --gray-150:  #1f1b17;
  --gray-200:  var(--border);
  --gray-300:  var(--border);
  --gray-400:  var(--text-dim);
  --line:      var(--border-light);
  --green:     var(--pal-mint);
  --green-bg: rgba(157,201,174,.12);

  font-family: var(--font-body);
}

/* Severity reads by heat AND luminance on the dark ground: dim/cool for
   quiet, warm/bright for loud, the ember reserved for critical. Mint is
   success (accepted, confirmed), never a severity. */
:root {
  --sev-none: #7f918c;
  --sev-low: #929faa;
  --sev-medium: #a9b4ae;
  --sev-high: #e5a893;
  --sev-critical: #c56e49;
  --ok:   var(--pal-mint);
  --warn: var(--pal-blush);
  --bad:  var(--accent);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
svg { fill: none; stroke: currentColor; }
button { font: inherit; cursor: pointer; background: none; border: none; color: inherit; }

body {
  background: var(--bg-primary);
  color: var(--ink);
  min-height: 100vh;
}

/* ------------------------------------------------ shell + sidebar */
.shell { display: flex; gap: 14px; padding: 14px; width: 100%; margin: 0 auto; }

.sidebar {
  background: var(--bg-secondary);
  border-radius: var(--radius);
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: center;
  min-height: calc(100vh - 28px);
}
.side-btn {
  width: 46px; height: 46px;
  border-radius: 10px;
  display: flex; align-items: center; gap: 0;
  justify-content: flex-start;
  padding: 0 12px;
  color: var(--ink);
  flex: 0 0 auto;
  overflow: hidden;
  white-space: nowrap;
  transition: width var(--ease-dur) var(--ease), gap var(--ease-dur) var(--ease),
              background .15s ease, color .15s ease;
}
.side-btn svg { width: 22px; height: 22px; flex: 0 0 auto; }
.side-btn.side-toggle, .side-btn.active { background: var(--ink-dark); color: var(--text-light); }
.side-btn.active { margin-top: 18px; }
.side-btn.plain:hover { background: var(--gray-150); }
.side-btn.plain:nth-last-child(1) { margin-top: auto; }

/* the label is always in the DOM so its width can animate */
.side-label {
  font-size: 15px; opacity: 0; max-width: 0; overflow: hidden;
  transition: opacity .16s ease, max-width var(--ease-dur) var(--ease);
}

/* expanded rail — the hamburger toggles this */
.shell.rail .sidebar { align-items: stretch; padding: 12px; }
.shell.rail .side-btn { width: 178px; gap: 13px; }
.shell.rail .side-label { opacity: 1; max-width: 130px; }

@media (prefers-reduced-motion: reduce) {
  .side-btn, .side-label, .drawer, .drawer-backdrop, .meter-fill { transition: none !important; }
}

/* ------------------------------------------------ main panel + tabs */
.panel {
  flex: 1;
  background: var(--bg-secondary);
  border-radius: var(--radius);
  padding: 22px 28px 34px;
  min-width: 0;
}
.tabs {
  display: flex;
  background: var(--gray-150);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 26px;
}
.tab {
  flex: 1;
  padding: 13px 10px;
  font-size: 15.5px;
  color: var(--ink-soft);
  border-radius: 8px;
}
.tab.active { background: var(--ink-dark); color: var(--text-light); }
.tab:not(.active):hover { color: var(--ink); }

.placeholder-body { padding: 60px 10px; text-align: center; color: var(--ink-soft); }
.placeholder-body h2 { color: var(--ink); margin-bottom: 10px; font-weight: 500; }

.section-title { font-size: 21px; font-weight: 700; margin-bottom: 18px; }

.census {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px;
  margin-top: 16px;
}
.census-cell {
  background: var(--gray-150);
  border-radius: 4px;
  padding: 12px 14px;
}
.census-cell .k { display: block; color: var(--text-dim); font-size: 12px; margin-bottom: 4px; }
.census-cell .v { font-size: 15px; color: var(--ink); font-weight: 500; }
.kind-sel {
  border: 1px solid var(--gray-300); border-radius: 6px;
  padding: 8px 10px; font: inherit; background: var(--bg-secondary);
}
.rep-lead { color: var(--ink-soft); font-size: 15px; margin: 0 0 16px; }

/* ------------------------------------------------ overview: detail card */
.detail-card { border-radius: 4px; overflow: hidden; }
.detail-head {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--pal-ink); color: var(--text-light);
  padding: 17px 22px;
  font-size: 16px; font-weight: 500;
}
.detail-toggle { color: var(--text-light); display: grid; place-items: center; }
.detail-toggle svg { width: 21px; height: 21px; }
.detail-body { background: var(--gray-150); }
.detail-card.collapsed .detail-body { display: none; }
.detail-row {
  display: flex; align-items: flex-start;
  padding: 17px 22px;
  font-size: 16px;
  border-bottom: 1px solid var(--border);
}
.detail-row:last-child { border-bottom: 0; }
.detail-row .k { width: 46%; color: var(--text-dim); flex: 0 0 auto; }
.detail-row .v { color: var(--ink); word-break: break-word; }

/* ------------------------------------------------ structures register */
.struct-topline {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-bottom: 20px;
}
.struct-topline .search-wrap { margin-bottom: 0; }
.struct-actions { display: flex; gap: 12px; }
.btn.outline {
  border: 1px solid var(--gray-300);
  background: var(--bg-secondary); color: var(--ink);
}
.btn.outline:hover { background: var(--gray-100); }

.reg-table { width: 100%; border-collapse: collapse; }
.reg-table th {
  text-align: center;
  font-size: 15px; font-weight: 500;
  color: var(--ink);
  padding: 16px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.reg-table td {
  padding: 14px 12px;
  border-bottom: 1px solid var(--line);
  font-size: 15px;
  text-align: center;
  vertical-align: middle;
}
.reg-table td.name { text-align: left; padding-left: 22px; }
.reg-table .coord { color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.proc-cell { min-width: 150px; }
.proc-count { font-size: 15px; margin-bottom: 7px; }
.proc-count .done { color: var(--ink); }
.proc-count .sep, .proc-count .all { color: var(--text-dim); }
.proc-bar { height: 7px; border-radius: 4px; background: var(--gray-200); overflow: hidden; }
.proc-bar span { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.note-cell .note-text { color: var(--ink-soft); font-size: 14.5px; }
.note-cell .btn-show {
  background: var(--bg-dropdown); color: var(--text-light);
  border-radius: 5px; padding: 9px 18px; font-size: 14.5px;
  display: inline-flex; align-items: center; gap: 9px;
}
.note-cell .btn-show:hover { background: var(--bg-dropdown); }
.note-cell .btn-show svg { width: 17px; height: 17px; }
.row-actions { display: inline-flex; gap: 8px; }
.btn.tiny {
  border: 1px solid var(--gray-300); background: var(--bg-secondary); color: var(--ink-soft);
  border-radius: 5px; padding: 8px 15px; font-size: 14.5px;
}
.btn.tiny:hover { border-color: var(--ink); color: var(--ink); }
.btn.tiny.danger:hover { border-color: var(--sev-critical); color: var(--sev-critical); }
.btn.tiny svg { width: 16px; height: 16px; }

/* ------------------------------------------------ images layout */
.images-layout { display: flex; gap: 40px; align-items: flex-start; }
.method-col { width: 340px; flex: 0 0 auto; }
.method-head h3 { font-size: 16px; font-weight: 700; }
.method-head p { font-size: 14.5px; margin: 4px 0 16px; }

.method-card {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  border-radius: 8px;
  padding: 18px 20px 22px;
  margin-bottom: 18px;
  color: var(--ink);
}
.method-card:hover { background: var(--gray-150); }
.method-card.active { background: var(--bg-dropdown); border-color: var(--bg-dropdown); color: var(--text-light); }
.method-title {
  display: flex; align-items: center; gap: 12px;
  font-size: 16.5px; font-weight: 700; margin-bottom: 12px;
}
.method-title svg { width: 22px; height: 22px; flex: 0 0 auto; }
.method-desc { font-size: 14.5px; line-height: 1.5; display: block; }

.mode-col { flex: 1; min-width: 0; }
.mode-title { font-size: 26px; font-weight: 700; margin-bottom: 22px; }

/* ------------------------------------------------ browse mode */
.browse-topline { display: flex; align-items: center; gap: 20px; margin-bottom: 16px; min-height: 44px; }
.browse-headline { font-size: 25px; font-weight: 700; line-height: 1.2; }
.browse-topline .btn { margin-left: auto; }
.btn {
  display: inline-flex; align-items: center; gap: 9px;
  border-radius: 6px;
  padding: 10px 16px;
  font-size: 14.5px;
}
.btn svg { width: 18px; height: 18px; }
.btn.dark { background: var(--bg-dropdown); color: var(--text-light); }
.btn.dark:hover { background: var(--bg-dropdown); }
.btn.ghost {
  border: 1px solid var(--gray-300);
  color: var(--gray-400);
  background: var(--bg-secondary);
  padding: 11px 22px;
  font-size: 15px;
}
.btn.ghost.enabled { background: var(--bg-dropdown); color: var(--text-light); border-color: var(--bg-dropdown); cursor: pointer; }
.btn.ghost.enabled:hover { background: var(--bg-dropdown); }
.btn.ghost:disabled { cursor: default; }
.btn.dark:disabled { background: var(--gray-100); color: var(--gray-400); cursor: default; }

.browse-actions { display: flex; gap: 12px; margin-bottom: 6px; position: relative; }
.filter-pop {
  position: absolute;
  top: 46px; left: 0;
  background: var(--bg-secondary);
  border: 1px solid var(--gray-200);
  border-radius: 8px;
  box-shadow: 0 8px 28px rgba(0,0,0,.14);
  min-width: 240px;
  max-height: 320px;
  overflow: auto;
  z-index: 30;
  padding: 6px;
}
.filter-pop button {
  display: block; width: 100%; text-align: left;
  padding: 9px 12px; border-radius: 6px; font-size: 14.5px;
}
.filter-pop button:hover { background: var(--gray-100); }
.filter-pop button.on { background: var(--ink-dark); color: var(--text-light); }

/* "process all" confirmation, anchored above its button */
.confirm-pop {
  position: absolute;
  bottom: calc(100% + 12px); left: 0;
  display: flex; align-items: flex-start; gap: 11px; flex-wrap: wrap;
  background: var(--bg-secondary);
  border-radius: 8px;
  box-shadow: 0 6px 26px rgba(0,0,0,.18);
  padding: 16px 18px;
  width: 520px; max-width: 90vw;
  z-index: 40;
}
.confirm-pop::after {                       /* arrow pointing down at Process All */
  content: ""; position: absolute; top: 100%; left: 46px;
  border: 9px solid transparent; border-top-color: var(--bg-secondary);
  filter: drop-shadow(0 3px 2px rgba(0,0,0,.06));
}
.confirm-icon { width: 20px; height: 20px; flex: 0 0 auto; margin-top: 1px; }
.confirm-pop p { flex: 1 1 300px; font-size: 15px; line-height: 1.45; }
.confirm-actions { display: flex; gap: 10px; margin-left: auto; align-self: flex-end; }
.btn.tiny.solid { background: var(--bg-dropdown); color: var(--text-light); border-color: var(--bg-dropdown); }
.btn.tiny.solid:hover { background: var(--bg-dropdown); color: var(--text-light); }

/* tables */
.img-table, .upload-table { width: 100%; border-collapse: collapse; }
.img-table th, .upload-table th {
  text-align: left;
  font-size: 14.5px;
  font-weight: 700;
  padding: 14px 10px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.img-table td, .upload-table td {
  padding: 10px;
  border-bottom: 1px solid var(--line);
  font-size: 14.5px;
  vertical-align: middle;
}
.img-table .col-check { width: 34px; }
.th-filter { width: 13px; height: 13px; color: var(--gray-400); vertical-align: -1px; }
.img-table img.thumb {
  width: 52px; height: 40px; object-fit: cover; border-radius: 3px; display: block;
  background: var(--gray-150);
}
.img-table input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--ink-dark); }
.img-table tr.sel td { background: rgba(197,110,73,.16); color: var(--text-light); border-bottom-color: var(--border); }
.img-table tr.sel input[type=checkbox] { accent-color: var(--text-light); }
.proc-mark { font-size: 15px; }
.upload-table .num { text-align: right; }
.upload-table td.num { text-align: right; }
.upload-table th { border-bottom: 1px solid var(--gray-300); border-top: 1px solid var(--gray-300); }
.upload-table th:first-child, .upload-table td:first-child { border-right: 1px solid var(--line); width: 90px; }
.status-cell svg { width: 16px; height: 16px; color: var(--ink-soft); }
.remove-x svg { width: 15px; height: 15px; }
.remove-x:hover { color: var(--sev-critical); }

.empty-state { text-align: center; padding: 70px 0; color: var(--gray-400); }
.spinner-dots { display: inline-grid; grid-template-columns: 10px 10px; gap: 5px; margin-bottom: 14px; }
.spinner-dots span { width: 8px; height: 8px; border-radius: 50%; background: var(--gray-300); animation: blink 1.2s infinite; }
.spinner-dots span:nth-child(2) { animation-delay: .3s; }
.spinner-dots span:nth-child(3) { animation-delay: .9s; }
.spinner-dots span:nth-child(4) { animation-delay: .6s; }
@keyframes blink { 0%,100% { opacity: .35; } 50% { opacity: 1; } }

/* pager */
.pager {
  display: flex; justify-content: flex-end; align-items: center;
  gap: 6px; margin-top: 20px; font-size: 14px;
}
.pager button {
  min-width: 30px; height: 30px;
  border-radius: 5px;
  display: grid; place-items: center;
  color: var(--ink-soft);
  padding: 0 6px;
}
.pager button.cur { border: 1px solid var(--ink); color: var(--ink); }
.pager button:hover:not(.cur):not(:disabled) { background: var(--gray-100); }
.pager button:disabled { color: var(--gray-300); cursor: default; }
.pager .per-page {
  border: 1px solid var(--gray-300); border-radius: 5px;
  padding: 5px 8px; color: var(--ink-soft); background: var(--bg-secondary); font-size: 13.5px;
}

/* ------------------------------------------------ structure chooser */
.search-wrap {
  position: relative;
  width: 260px;
  margin-bottom: 10px;
}
.search-wrap svg {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--gray-400);
}
.search-wrap input {
  width: 100%;
  padding: 9px 12px 9px 34px;
  border: 1px solid var(--gray-300);
  border-radius: 6px;
  font: inherit; font-size: 14.5px;
  outline: none;
}
.search-wrap input:focus { border-color: var(--ink-soft); }
.structure-rows { }
.structure-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 15px 6px;
  border-bottom: 1px solid var(--line);
  font-size: 15px;
  color: var(--ink-soft);
}
.structure-row .btn-select {
  background: var(--gray-200);
  color: var(--ink-soft);
  border-radius: 4px;
  padding: 8px 22px;
  font-size: 14.5px;
}
.structure-row .btn-select:hover { background: var(--ink-dark); color: var(--text-light); }

/* ------------------------------------------------ uploading view */
.progress-line { display: flex; align-items: center; gap: 12px; margin: 26px 0 18px; }
.progress-track {
  flex: 1; height: 8px; border-radius: 6px; background: var(--gray-200); overflow: hidden;
}
.progress-fill { height: 100%; width: 0%; background: var(--ink-dark); transition: width .25s; }
.progress-fill.teal { background: var(--pal-mint); }
.progress-pct { font-size: 14px; color: var(--ink-soft); min-width: 36px; text-align: right; }

/* ------------------------------------------------ modal */
.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(40,40,40,.45);
  display: grid; place-items: center;
  z-index: 100;
}
.modal {
  position: relative;
  background: var(--bg-secondary);
  border-radius: 10px;
  width: 480px;
  max-width: calc(100vw - 40px);
  padding: 46px 60px 44px;
  text-align: center;
}
.modal-x { position: absolute; top: 16px; right: 18px; color: var(--ink-soft); }
.modal-x svg { width: 20px; height: 20px; }
.modal-icon { width: 54px; height: 54px; color: var(--ink); margin-bottom: 26px; }
.modal h2 { font-size: 26px; font-weight: 700; line-height: 1.25; margin-bottom: 30px; }
.modal p { font-size: 15.5px; line-height: 1.6; margin-bottom: 34px; color: var(--ink); }
.btn.pill {
  display: block; width: 100%;
  background: var(--bg-dropdown); color: var(--text-light);
  border-radius: 999px;
  padding: 13px 0;
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: .4px;
  margin-bottom: 14px;
}
.btn.pill:hover { background: var(--bg-dropdown); }

/* wide + form modal variants (import structures / add structure) */
.modal.wide {
  width: 1060px;
  padding: 26px 30px 22px;
  text-align: left;
  max-height: calc(100vh - 60px);
  overflow: auto;
}
.modal.form { width: 460px; padding: 24px 28px 26px; text-align: left; }
.modal-bar { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
.modal-bar h2 { font-size: 21px; font-weight: 700; margin: 0; }
.modal-bar-actions { display: flex; align-items: center; gap: 12px; }
.modal-bar .modal-x { position: static; }
.modal-tools { display: flex; gap: 12px; margin: 20px 0 4px; flex-wrap: wrap; }

.geo-bar {
  display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap;
  background: rgba(197,110,73,.10); border: 1px solid rgba(197,110,73,.30);
  border-radius: 8px; padding: 14px 16px; margin-top: 16px;
}
.geo-warn { width: 22px; height: 22px; color: var(--warn); flex: 0 0 auto; align-self: center; }
.geo-bar p { flex: 1 1 320px; font-size: 14px; line-height: 1.5; margin: 0; align-self: center; }
.geo-field { display: flex; flex-direction: column; gap: 5px; font-size: 13px; color: var(--ink-soft); }
.geo-field input, .geo-field select {
  border: 1px solid var(--gray-300); border-radius: 6px;
  padding: 8px 10px; font: inherit; font-size: 14px; background: var(--bg-secondary);
  outline: none; min-width: 175px;
}
.geo-field input:focus, .geo-field select:focus { border-color: var(--ink-soft); }

.import-meta { font-size: 14px; color: var(--ink-soft); margin-top: 16px; }
.import-table { width: 100%; border-collapse: collapse; margin-top: 16px; }
.import-table th {
  text-align: left; font-size: 14.5px; font-weight: 500;
  padding: 15px 18px; background: var(--gray-100);
  border-bottom: 1px solid var(--line);
}
.import-table td {
  padding: 15px 18px; font-size: 14.5px;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
}
.import-table td.mono { font-variant-numeric: tabular-nums; }
.import-table td.raw { color: var(--warn); }
.import-blank { margin-top: 16px; }
.blank-bar { height: 40px; background: var(--gray-100); border-radius: 4px; }
.empty-state .tray { width: 56px; height: 56px; color: var(--gray-300); margin-bottom: 12px; }

/* form fields */
.field { display: block; margin-top: 16px; font-size: 13.5px; color: var(--ink-soft); }
.field span { display: block; margin-bottom: 6px; }
.field input, .field textarea, .field select {
  width: 100%; border: 1px solid var(--gray-300); border-radius: 6px;
  padding: 10px 12px; font: inherit; font-size: 15px; color: var(--ink);
  outline: none; resize: vertical; background: var(--bg-secondary);
}
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--ink-soft); }
.field-row { display: flex; gap: 14px; }
.field-row .field { flex: 1; min-width: 0; }
.field-error { color: var(--sev-critical); font-size: 14px; margin-top: 12px; }
.modal-copy { font-size: 15.5px; line-height: 1.55; margin-top: 16px; color: var(--ink); }
.modal-foot { display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px; }

/* ------------------------------------------------ toast */
.toast {
  position: fixed;
  top: 18px; right: 18px;
  z-index: 200;
  background: var(--green-bg);
  border: 1px solid rgba(157,201,174,.35);
  border-radius: 10px;
  box-shadow: 0 8px 30px rgba(0,0,0,.12);
  display: flex; gap: 13px;
  padding: 18px 20px;
  width: 440px;
  max-width: calc(100vw - 36px);
  align-items: flex-start;
}
.toast-ok { width: 24px; height: 24px; flex: 0 0 auto; }
.toast-title { font-weight: 700; font-size: 15.5px; margin-bottom: 6px; }
.toast-msg { font-size: 14.5px; line-height: 1.5; }
.toast-x { margin-left: auto; color: var(--ink-soft); }
.toast-x svg { width: 16px; height: 16px; }

/* ================================================================= *
 *  Step 04 — Analyze: Inspection tab
 * ================================================================= */


.insp-topline { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.insp-topline .search-wrap { margin-bottom: 0; }
.insp-tools { margin-left: auto; display: flex; align-items: center; gap: 12px; }

.lbl-toggle {
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--ink-dark); color: var(--text-light);
  border-radius: 999px; padding: 7px 8px 7px 16px;
  font-size: 14px;
}
.lbl-toggle i { width: 19px; height: 19px; border-radius: 50%; background: var(--bg-secondary); display: block; }
.lbl-toggle:not(.on) { background: var(--gray-200); color: var(--ink); flex-direction: row-reverse; }
.lbl-toggle:not(.on) { padding: 7px 16px 7px 8px; }
.lbl-toggle:not(.on) i { background: var(--bg-secondary); box-shadow: 0 1px 3px rgba(0,0,0,.3); }

.round-btn {
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; color: var(--ink);
  position: relative; background: var(--bg-secondary);
}
.round-btn svg { width: 21px; height: 21px; }
.round-btn:hover { background: var(--gray-150); }
.round-btn.sm { width: 30px; height: 30px; }
.round-btn.sm svg { width: 17px; height: 17px; }
.round-btn[data-tip]:hover::after {
  content: attr(data-tip);
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  background: var(--ink-dark); color: var(--text-light); font-size: 12.5px;
  padding: 6px 10px; border-radius: 5px; white-space: nowrap; z-index: 60;
}

/* filter sheet */
.filter-sheet {
  display: flex; align-items: flex-end; gap: 26px; flex-wrap: wrap;
  background: var(--gray-100); border: 1px solid var(--gray-200);
  border-radius: 8px; padding: 14px 18px; margin-bottom: 16px;
}
.sheet-group { display: flex; flex-direction: column; gap: 7px; }
.sheet-title { font-size: 12.5px; color: var(--ink-soft); }
.sev-chips { display: flex; gap: 7px; flex-wrap: wrap; }
.sev-chips button {
  border-radius: 999px; padding: 5px 13px; font-size: 13px;
  border: 1px solid var(--gray-300); background: var(--bg-secondary); color: var(--ink-soft);
}
.sev-chips button.on { color: var(--text-light); border-color: transparent; }
.filter-sheet select {
  border: 1px solid var(--gray-300); border-radius: 6px; padding: 7px 10px;
  font: inherit; font-size: 14px; background: var(--bg-secondary); min-width: 190px;
}
.sheet-check { font-size: 14px; display: flex; align-items: center; gap: 8px; color: var(--ink-soft); }

/* image + map panes */
.insp-panes { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.viewer-pane {
  background: var(--gray-150); border-radius: 4px;
  min-height: 400px; display: grid; place-items: center; padding: 24px; overflow: hidden;
}
.viewer-empty { color: var(--ink-soft); font-size: 15px; text-align: center; }
.stage { position: relative; line-height: 0; max-width: 100%; }
.stage img { max-width: 100%; max-height: 352px; display: block; }
.stage-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

.map-pane { position: relative; border-radius: 4px; overflow: hidden; background: var(--pal-ink); min-height: 400px; }
.map-pane canvas { width: 100%; height: 100%; display: block; }
.map-zoom { position: absolute; top: 14px; left: 14px; display: flex; flex-direction: column; }
.map-zoom button {
  width: 32px; height: 32px; background: var(--bg-secondary); color: var(--ink);
  font-size: 19px; line-height: 1; display: grid; place-items: center;
  border: 1px solid var(--gray-300);
}
.map-zoom button:first-child { border-radius: 4px 4px 0 0; border-bottom: 0; }
.map-zoom button:last-child { border-radius: 0 0 4px 4px; }
.map-foot {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: space-between;
  background: rgba(255,255,255,.72); font-size: 12px; color: var(--text-primary); padding: 3px 10px;
}

/* filmstrip */
.strip-bar { display: flex; justify-content: flex-end; margin: 14px 0 8px; }
.filmstrip { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 6px; }
.film-card {
  flex: 0 0 auto; width: 138px;
  border: 2px solid var(--gray-300); border-radius: 6px;
  background: var(--bg-secondary); overflow: hidden; cursor: pointer; position: relative;
}
.filmstrip.big .film-card { width: 210px; }
.film-top { display: flex; align-items: center; gap: 5px; padding: 6px 7px; }
.film-badge {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--gray-200); color: var(--ink);
  display: grid; place-items: center; font-size: 9px; font-weight: 700;
}
.film-badge svg { width: 13px; height: 13px; }
.film-card input[type=checkbox] { margin-left: auto; width: 15px; height: 15px; accent-color: var(--ink-dark); }
.film-thumb { position: relative; line-height: 0; }
.film-thumb img { width: 100%; height: 104px; object-fit: cover; display: block; background: var(--gray-150); }
.filmstrip.big .film-thumb img { height: 150px; }
.film-thumb svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.film-name {
  font-size: 13px; padding: 8px 6px; text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.film-card.on .film-top { background: currentColor; }
.film-card.on .film-badge { background: rgba(255,255,255,.9); }

/* ================================================================= *
 *  annotation workbench
 * ================================================================= */
.workbench {
  position: fixed; inset: 0; z-index: 150;
  background: var(--bg-secondary); display: flex; gap: 0; overflow: auto;
}
.wb-side { width: 340px; flex: 0 0 auto; padding: 30px 34px; }
.wb-side h2 { font-size: 25px; font-weight: 700; margin-bottom: 22px; }
.wb-opt {
  display: flex; align-items: center; gap: 13px;
  width: 100%; padding: 14px 4px; font-size: 16px;
  border-bottom: 1px solid var(--line); color: var(--ink);
}
.wb-opt:hover { color: var(--text-light); background: var(--gray-100); }
.wb-opt svg { width: 19px; height: 19px; }
.wb-label-head {
  display: flex; justify-content: space-between;
  background: var(--gray-150); padding: 13px 14px;
  font-size: 15px; margin-top: 32px;
}
.wb-label-row {
  display: flex; justify-content: space-between;
  padding: 13px 14px; font-size: 16px; color: var(--ink);
}
.wb-label-row .abbr { color: var(--ink-soft); font-size: 14px; }
.wb-label-row .dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; margin-right: 9px; }
.wb-empty-labels { padding: 16px 14px; color: var(--ink-soft); font-size: 14.5px; }

.wb-main { flex: 1; min-width: 0; padding: 28px 34px 28px 0; }
.wb-card { background: var(--gray-100); border-radius: 12px; padding: 20px 24px 26px; }
.wb-toolbar { display: flex; align-items: center; gap: 10px; position: relative; margin-bottom: 18px; }
.wb-tool { box-shadow: 0 1px 4px rgba(0,0,0,.12); }
.wb-tool.on { background: var(--ink-dark); color: var(--text-light); }
.wb-tool:disabled { color: var(--gray-400); cursor: default; background: var(--bg-secondary); }
.zoom-ctl { display: flex; align-items: center; gap: 10px; margin-left: 6px; flex: 1; }
.zoom-ctl button { width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--ink-soft); color: var(--ink-soft); display: grid; place-items: center; font-size: 15px; line-height: 1; }
.zoom-ctl input[type=range] { flex: 1; max-width: 340px; accent-color: var(--ink-dark); }
.wb-close { color: var(--ink); }
.wb-close svg { width: 26px; height: 26px; }

.wb-stage-row { display: flex; align-items: center; gap: 10px; }
.wb-nav { color: var(--ink); flex: 0 0 auto; }
.wb-nav svg { width: 30px; height: 30px; }
.wb-nav:disabled { color: var(--gray-300); cursor: default; }
/* fixed frame, flex + margin: auto centring: the image CONTAIN-fits at 100%
   (no scrollbars); only zooming past 100% overflows into scroll */
.wb-scroll { flex: 1; min-width: 0; overflow: auto; background: var(--bg-secondary); height: 62vh; display: flex; }
.wb-canvas { position: relative; line-height: 0; width: max-content; margin: auto; }
.wb-canvas img { display: block; width: 100%; height: auto; }
.wb-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.wb-canvas.drawing { cursor: crosshair; }

.wb-foot { text-align: center; margin-top: 16px; }
.wb-count { font-size: 14px; color: var(--ink-soft); }
.wb-name { font-size: 20px; font-weight: 700; margin-top: 6px; }
.wb-sub { font-size: 14px; color: var(--ink-soft); margin-top: 3px; }

.ann-field select {
  width: 100%; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 6px; font-size: 14px; background: var(--bg-secondary); color: var(--ink);
}

/* chips are read-only overlays — they must never steal clicks from the
   shape underneath (that made small findings impossible to open/delete) */
.tid-chip { pointer-events: none; }
.tid-chip text { fill: var(--bg-secondary); font-size: 19px; font-weight: 700; font-family: Roboto, Arial, sans-serif; }

/* annotation shapes */
.ann-shape { fill: transparent; stroke-width: 2.5; vector-effect: non-scaling-stroke; }
.ann-shape.pick { cursor: pointer; }
.ann-shape.pick:hover { fill: rgba(255,255,255,.16); }
/* invisible padded click target behind sliver-thin detector boxes */
.ann-hit { fill: transparent; stroke: none; cursor: pointer; }
/* DXF module grid projected into the frame — reference layer, never a target */
.wb-mod { fill: transparent; stroke: rgba(120,200,255,.45); stroke-width: 1.2; vector-effect: non-scaling-stroke; pointer-events: none; }
.wb-mod.aligning { stroke: rgba(90,200,255,.95); stroke-width: 1.6; }
.wb-stage-row { position: relative; }
.wb-alignbar {
  position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px; z-index: 30;
  background: rgba(20,20,20,.88); color: var(--text-primary); border-radius: 9px;
  padding: 8px 14px; font-size: 13px; white-space: nowrap;
}
.wb-alignbar .btn { padding: 5px 10px; font-size: 12.5px; }
.wb-alignbar .btn:not(.dark) { background: var(--border); color: var(--text-primary); border: 1px solid var(--border); }
.wb-align-hint { color: var(--text-dim); font-size: 12px; }
#wb-align-info { font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; color: var(--pal-steel); }
.ann-shape.draft { stroke: var(--pal-blush); stroke-dasharray: none; }
.ann-handle { fill: var(--bg-primary); stroke: var(--text-light); stroke-width: 1.5; }
.ann-handle.first { fill: var(--accent); }
.ann-tag { font-size: 11px; fill: var(--bg-secondary); }
.ann-tag-bg { stroke: none; }

/* severity + label popover */
.ann-pop {
  position: fixed; z-index: 200;
  background: var(--bg-secondary); border-radius: 8px;
  box-shadow: 0 10px 34px rgba(0,0,0,.24);
  width: 300px; padding: 12px 14px 14px;
}
.ann-pop-head { display: flex; justify-content: flex-end; margin-bottom: 10px; }
.ann-src {
  border: 1px solid var(--gray-300); border-radius: 5px;
  padding: 3px 10px; font-size: 12.5px; color: var(--ink-soft);
}
.ann-field { position: relative; margin-bottom: 10px; }
.ann-field input, .ann-sev-btn {
  width: 100%; border: 1px solid var(--gray-300); border-radius: 6px;
  padding: 9px 30px 9px 11px; font: inherit; font-size: 14.5px;
  background: var(--bg-secondary); text-align: left; outline: none; color: var(--ink);
}
.ann-field input:focus { border-color: var(--sev-low); }
.ann-field .caret {
  position: absolute; right: 9px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: var(--gray-400); pointer-events: none;
}
.ann-menu {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--bg-secondary); border-radius: 7px; box-shadow: 0 8px 26px rgba(0,0,0,.2);
  max-height: 230px; overflow: auto; z-index: 10; padding: 5px;
}
/* Opening downward off the bottom of the viewport left the lower
   severities unreachable — there is nothing to scroll once the menu is
   past the edge. Flip it above the field when the room is up there. */
.ann-menu.up { top: auto; bottom: calc(100% + 4px); }
.ann-menu button { display: block; width: 100%; text-align: left; padding: 9px 11px; border-radius: 5px; font-size: 14.5px; }
.ann-menu button:hover { background: var(--gray-100); }
.sev-chip {
  display: inline-block; border-radius: 4px; padding: 3px 14px;
  font-size: 13px; font-weight: 700; color: var(--text-light);
}
.sev-chip.placeholder { background: none; color: var(--gray-400); font-weight: 400; padding-left: 0; }
/* Four controls needed 292px inside a 270px row: "Snap to module" was
   squashed to three lines and the trash icon collapsed to half width. Snap
   is its own action, so it gets its own row and the rest share the next. */
.ann-pop-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 9px; margin-top: 8px;
}
.ann-pop-foot #ann-snap {
  flex: 1 0 100%; margin-left: 0; justify-content: center; white-space: nowrap;
}
.ann-pop-foot .ann-trash { flex: 0 0 34px; }
.ann-pop-foot #ann-cancel { margin-left: auto; }
.ann-pop-foot .btn.pill-sm { flex: 0 0 auto; white-space: nowrap; }
.ann-trash { color: var(--ink); width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; }
.ann-trash:hover { background: var(--gray-100); color: var(--sev-critical); }
.ann-trash svg { width: 18px; height: 18px; }
.btn.pill-sm {
  margin-left: 0; border-radius: 999px; padding: 8px 22px;
  background: var(--ink-dark); color: var(--text-light); font-size: 14px;
}
.ann-pop-foot .btn.pill-sm:nth-of-type(1) { margin-left: auto; }
.btn.pill-sm.ghost-pill { background: var(--bg-secondary); color: var(--ink); border: 1px solid var(--gray-400); }

.sev-filter-pop, .legend-pop {
  position: absolute; top: calc(100% + 8px); right: 0;
  background: var(--bg-secondary); border-radius: 8px; box-shadow: 0 8px 28px rgba(0,0,0,.2);
  padding: 12px; z-index: 50; min-width: 210px;
}
.sev-filter-pop label { display: flex; align-items: center; gap: 12px; padding: 5px 4px; font-size: 14px; }
.sev-filter-pop input { width: 15px; height: 15px; accent-color: var(--ink-dark); }
.sev-filter-pop .sev-chip { flex: 1; text-align: center; padding: 6px 0; font-size: 14px; }
.legend-pop { max-height: 320px; overflow: auto; }
.legend-pop .row { display: flex; justify-content: space-between; gap: 18px; padding: 6px 8px; font-size: 14px; }
.legend-pop .row span:last-child { color: var(--ink-soft); }

/* ------------------------------------------------ slide-out drawers */
/* visibility rather than [hidden] so open/close can actually transition —
   display:none would skip the animation in both directions */
.drawer-backdrop {
  position: fixed; inset: 0; z-index: 180; background: rgba(24,24,24,.34);
  opacity: 0; visibility: hidden;
  transition: opacity .24s ease, visibility 0s linear var(--ease-dur);
}
.drawer-backdrop.in {
  opacity: 1; visibility: visible;
  transition: opacity .24s ease, visibility 0s linear 0s;
}
.drawer {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(1040px, 78vw); background: var(--bg-secondary);
  display: flex; flex-direction: column;
  box-shadow: -10px 0 40px rgba(0,0,0,.18);
  transform: translateX(100%);
  transition: transform var(--ease-dur) var(--ease);
}
.drawer-backdrop.in .drawer { transform: none; }
.drawer-head {
  display: flex; align-items: center; gap: 16px;
  padding: 24px 32px 18px; border-bottom: 1px solid var(--line);
}
.drawer-head h2 { font-size: 19px; font-weight: 700; letter-spacing: -.2px; }
.drawer-x { color: var(--ink-soft); display: grid; place-items: center; width: 30px; height: 30px; border-radius: 7px; }
.drawer-x:hover { background: var(--gray-100); color: var(--ink); }
.drawer-x svg { width: 19px; height: 19px; }
.drawer-check { margin-left: auto; display: flex; align-items: center; gap: 9px; font-size: 14px; color: var(--ink-soft); }
.drawer.narrow { width: min(440px, 92vw); }
.drawer-body { flex: 1; overflow: auto; padding: 24px 32px 30px; }

/* ---- side-panel content (Team / Settings / Help) ---- */
.panel-section {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--gray-400); font-weight: 600;
  margin: 30px 0 12px;
}
.panel-section:first-child { margin-top: 0; }

.panel-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 13px 0; border-bottom: 1px solid var(--line); font-size: 14.5px;
}
.panel-row:last-child { border-bottom: 0; }
.panel-row .v { color: var(--ink); font-variant-numeric: tabular-nums; }
.panel-row .v.muted { color: var(--gray-400); }

/* progress meters */
.meter { margin-bottom: 4px; }
.meter-top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 7px; }
.meter-top .k { font-size: 14.5px; }
.meter-top .v { font-size: 14.5px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.meter-track { height: 6px; border-radius: 4px; background: var(--gray-200); overflow: hidden; }
.meter-fill { height: 100%; border-radius: 4px; background: var(--ink-dark);
  width: 0; transition: width .5s var(--ease); }

/* pipeline list */
.panel-steps { counter-reset: s; }
.panel-steps li {
  list-style: none; display: flex; align-items: center; gap: 14px;
  padding: 11px 0; font-size: 14.5px; border-bottom: 1px solid var(--line);
}
.panel-steps li:last-child { border-bottom: 0; }
.panel-steps li::before {
  counter-increment: s; content: counter(s, decimal-leading-zero);
  color: var(--gray-400); font-size: 12.5px;
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}
.step-tag { margin-left: auto; font-size: 11.5px; letter-spacing: .04em;
  border-radius: 999px; padding: 3px 10px; }
.step-tag.live { background: var(--green-bg); color: var(--ok); }
.step-tag.next { background: var(--gray-150); color: var(--gray-400); }

.panel-kbd {
  display: inline-block; min-width: 24px; text-align: center;
  background: var(--gray-100); border: 1px solid var(--gray-200);
  border-radius: 5px; padding: 2px 7px; font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink);
}
.panel-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 26px; }
.drawer-body .field { margin-top: 0; margin-bottom: 16px; }
.drawer-body .search-wrap { width: 100%; max-width: 560px; margin-bottom: 22px; }

.labels-table { width: 100%; border-collapse: collapse; }
.labels-table th {
  font-size: 15px; font-weight: 500; color: var(--ink);
  padding: 16px 12px; background: var(--gray-100);
  border-bottom: 1px solid var(--line); text-align: center;
}
.labels-table th:first-child, .labels-table td:first-child { text-align: left; padding-left: 22px; }
.labels-table th:not(:last-child) { border-right: 1px solid var(--gray-200); }
.labels-table td {
  padding: 13px 12px; font-size: 15px; text-align: center;
  border-bottom: 1px solid var(--line); vertical-align: middle;
}
.labels-table tr:hover td { background: var(--bg-secondary); }
.labels-table .sev-cell { position: relative; }
.labels-table .sev-chip { min-width: 104px; cursor: pointer; padding: 6px 16px; }
.sev-pick {
  position: absolute; top: calc(100% - 4px); left: 50%; transform: translateX(-50%);
  background: var(--bg-secondary); border-radius: 8px; box-shadow: 0 8px 26px rgba(0,0,0,.22);
  padding: 6px; z-index: 30; width: 150px;
}
.sev-pick button { display: block; width: 100%; padding: 3px; }
.sev-pick .sev-chip { display: block; width: 100%; }

/* ================================================================= *
 *  Step 06 — Report
 * ================================================================= */

.download-bar {
  display: block; width: 100%;
  border: 1px solid var(--gray-300); border-radius: 6px;
  padding: 15px 0; font-size: 16px; color: var(--ink-soft);
  background: var(--bg-secondary); text-align: center;
}
.download-bar:hover { background: var(--ink-dark); border-color: var(--ink-dark); color: var(--text-light); }

.modal.report {
  width: 690px; padding: 26px 32px 26px; text-align: left;
  max-height: calc(100vh - 48px); overflow: auto;
}
.rep-field { margin-top: 20px; }
.rep-label { display: block; font-size: 15.5px; margin-bottom: 9px; }
.rep-label.req::before { content: "* "; color: var(--sev-high); }
.rep-hint { font-size: 13.5px; color: var(--gray-400); margin-top: 7px; }
.rep-error { font-size: 13.5px; color: var(--sev-critical); margin-top: 7px; }

/* borrowed-look select */
.sel { position: relative; }
.sel-btn {
  width: 100%; border: 1px solid var(--gray-300); border-radius: 6px;
  padding: 11px 34px 11px 13px; font: inherit; font-size: 15px;
  background: var(--bg-secondary); text-align: left; color: var(--ink);
}
.sel-btn .ph { color: var(--gray-400); }
.sel.open .sel-btn { border-color: var(--ink-soft); }
.sel .caret {
  position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; color: var(--gray-400); pointer-events: none;
}
.sel-menu {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--bg-secondary); border-radius: 7px; box-shadow: 0 8px 28px rgba(0,0,0,.2);
  padding: 5px; z-index: 40;
}
.sel-menu.tall { max-height: 260px; overflow: auto; }
.sel-menu button, .sel-menu label {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; padding: 10px 12px;
  border-radius: 5px; font-size: 15px; cursor: pointer;
}
.sel-menu button:hover, .sel-menu label:hover { background: var(--gray-100); }
.sel-menu input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--ink-dark); }
.menu-search {
  width: 100%; border: 1px solid var(--gray-300); border-radius: 5px;
  padding: 8px 10px; font: inherit; font-size: 14px; margin-bottom: 5px; outline: none;
}

/* date range, two natives inside one reference-style box */
.date-range {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--gray-300); border-radius: 6px; padding: 4px 12px;
  background: var(--bg-secondary);
}
.date-range:focus-within { border-color: var(--ink-soft); }
.date-range input {
  flex: 1; min-width: 0; border: 0; outline: none;
  font: inherit; font-size: 15px; padding: 7px 0; color: var(--ink); background: none;
}
.date-range input:invalid, .date-range input:not(:valid) { color: var(--gray-400); }
.date-range .arrow { color: var(--gray-400); }
.date-range .cal { width: 18px; height: 18px; color: var(--gray-400); flex: 0 0 auto; }

/* severity pills */
.sev-pills { display: flex; gap: 12px; flex-wrap: wrap; }
.sev-pills button {
  border: 1px solid currentColor; border-radius: 6px;
  padding: 10px 26px; font-size: 15px; background: var(--bg-secondary);
}
.sev-pills button.on { background: currentColor; }
.sev-pills button.on span { color: var(--text-light); }

/* toggle switch */
.switch {
  width: 56px; height: 30px; border-radius: 999px; padding: 3px;
  background: var(--ink-dark); display: flex; align-items: center;
}
.switch i { width: 24px; height: 24px; border-radius: 50%; background: var(--bg-secondary); transition: margin .16s; }
.switch.on i { margin-left: 26px; }
.switch:not(.on) { background: var(--gray-300); }

/* spinner for in-flight side-panel actions */
.wb-opt .spin {
  width: 15px; height: 15px; flex: 0 0 auto;
  border: 2px solid var(--gray-300); border-top-color: var(--ink);
  border-radius: 50%; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* wide tables scroll inside their own box instead of stretching the page */
.tablewrap { width: 100%; overflow-x: auto; }
.tablewrap > table { min-width: 640px; }
.tablewrap > .import-table, .tablewrap > .labels-table { min-width: 520px; }

/* ================================================================= *
 *  responsive
 * ================================================================= */

@media (max-width: 1250px) {
  .insp-panes { grid-template-columns: 1fr; }
  .wb-side { width: 270px; padding: 24px; }
  .drawer { width: min(1040px, 88vw); }
}

@media (max-width: 1100px) {
  .images-layout { flex-direction: column; }
  .method-col { width: 100%; }
  .shell { padding: 10px; gap: 10px; }
  .panel { padding: 18px 18px 26px; }
}

@media (max-width: 900px) {
  /* sidebar becomes a top bar so it stops eating horizontal room */
  .shell { flex-direction: column; }
  .sidebar {
    flex-direction: row; min-height: 0; width: 100%;
    padding: 8px 10px; gap: 8px; overflow-x: auto;
  }
  .side-btn.active { margin-top: 0; }
  .side-btn.plain:nth-last-child(1) { margin-top: 0; margin-left: auto; }
  .shell.rail .sidebar { align-items: center; }
  .shell.rail .side-btn { width: auto; }

  .tabs { overflow-x: auto; }
  .tab { flex: 0 0 auto; min-width: 118px; }

  .insp-topline { flex-wrap: wrap; }
  .insp-tools { margin-left: 0; width: 100%; justify-content: flex-end; }
  .struct-topline { flex-wrap: wrap; }
  .struct-topline .search-wrap { width: 100%; }
  .struct-actions { width: 100%; }
  .struct-actions .btn { flex: 1; justify-content: center; }

  /* workbench stacks: options above the viewer */
  .workbench { flex-direction: column; }
  .wb-side { width: 100%; padding: 20px 20px 6px; }
  .wb-main { padding: 8px 20px 22px; }
  .wb-scroll { height: 52vh; }
  /* pin close to the card corner so the tools can wrap freely under it */
  .wb-card { position: relative; padding-top: 52px; }
  .wb-close { position: absolute; top: 14px; right: 18px; }
  .wb-toolbar { flex-wrap: wrap; }
  #btn-sev-filter { order: 9; }
  .zoom-ctl { order: 10; width: 100%; flex-basis: 100%; margin-left: 0; }
  .zoom-ctl input[type=range] { max-width: none; }
  .sev-filter-pop { right: auto; left: 0; }

  .drawer { width: 100%; }
  .drawer-head { flex-wrap: wrap; }
  .drawer-check { margin-left: 0; }
}

@media (max-width: 640px) {
  .shell { padding: 8px; }
  .panel { padding: 14px 12px 22px; border-radius: 8px; }
  .modal { padding: 30px 22px 26px; }
  .modal.wide, .modal.report, .modal.form { padding: 20px 16px 20px; }
  .modal-bar { flex-wrap: wrap; gap: 10px; }
  .modal-tools { gap: 8px; }
  .modal-tools .btn { flex: 1 1 auto; justify-content: center; }
  .browse-topline { flex-wrap: wrap; }
  .browse-headline { font-size: 20px; }
  .browse-actions { flex-wrap: wrap; }
  .confirm-pop { width: min(440px, calc(100vw - 28px)); }
  .date-range { flex-wrap: wrap; }
  .date-range input { flex: 1 1 40%; }
  .sev-pills button { padding: 9px 16px; flex: 1 1 auto; }
  .modal-foot { flex-wrap: wrap; }
  .modal-foot .btn { flex: 1; justify-content: center; }
  .detail-row { flex-direction: column; gap: 4px; }
  .detail-row .k { width: auto; }
  .wb-side h2, .section-title { font-size: 19px; }
  .ann-pop { width: min(300px, calc(100vw - 24px)); }
}

/* ---------------------------------------------------- run progress modal */
/* .modal centres its text for the short confirm dialogs; this one is a
   left-reading list, so it opts out rather than fighting the inherit. */
.modal.run { width: min(560px, calc(100vw - 32px)); text-align: left; }

.run-head { display: flex; align-items: baseline; gap: 14px; padding: 18px 20px 10px; }
.run-pct {
  font-size: 34px; font-weight: 500; color: var(--text-light);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.run-meta { display: flex; flex-direction: column; gap: 2px; }
.run-meta #run-sub { font-size: 14px; color: var(--ink-soft); }
.run-eta { font-size: 13px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.run-eta.soft { color: var(--gray-400); font-style: italic; }

.modal.run .progress-track.big { height: 6px; border-radius: 0; margin: 0 20px; flex: none; }

.run-stages { list-style: none; padding: 16px 20px 4px; display: grid; gap: 2px; }
.run-stage {
  display: grid; grid-template-columns: 26px 1fr; gap: 10px;
  padding: 9px 0; align-items: start;
  transition: opacity var(--ease-dur) var(--ease);
}
.run-stage.pending { opacity: .45; }

.st-icon {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  border: 1px solid var(--gray-300); color: var(--ink-soft); background: var(--bg-secondary);
  transition: background var(--ease-dur) var(--ease), color var(--ease-dur) var(--ease),
              border-color var(--ease-dur) var(--ease);
}
.st-icon svg { width: 13px; height: 13px; }
.st-icon.done { background: var(--ink-dark); border-color: var(--ink-dark); color: var(--text-light); }
.st-icon.running { border-color: var(--pal-mint); color: var(--pal-mint); }
.st-icon.error { background: var(--sev-high); border-color: var(--sev-high); color: var(--text-light); }
.st-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--gray-400); }
.st-spin {
  width: 11px; height: 11px; border-radius: 50%;
  border: 2px solid currentColor; border-right-color: transparent;
  animation: st-turn .7s linear infinite;
}
@keyframes st-turn { to { transform: rotate(360deg); } }

.st-body { min-width: 0; }
.st-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.st-name { font-size: 14.5px; color: var(--ink); }
.run-stage.done .st-name, .run-stage.running .st-name { color: var(--text-light); }
.st-count { font-size: 12.5px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.st-msg {
  font-size: 12.5px; color: var(--ink-soft); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.run-stage.skipped .st-msg, .run-stage.error .st-msg { color: var(--warn); }
.st-track {
  height: 2px; border-radius: 2px; background: var(--gray-200);
  margin-top: 7px; overflow: hidden;
}
.run-stage.pending .st-track { opacity: 0; }
.st-fill {
  display: block; height: 100%; width: 0;
  background: var(--pal-mint); transition: width .3s var(--ease);
}
.run-stage.done .st-fill { background: var(--ink-dark); }
.run-stage.skipped .st-fill { background: var(--gray-300); }
.run-stage.error .st-fill { background: var(--sev-high); }

.run-error {
  margin: 4px 20px 0; padding: 10px 12px; border-radius: 8px;
  background: rgba(229,168,147,.12); color: var(--sev-critical); font-size: 13px; word-break: break-word;
}

/* ---- run history ---- */
.run-badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 12.5px; border: 1px solid var(--gray-300); color: var(--ink-soft);
}
.run-badge.done { background: var(--green-bg); border-color: rgba(157,201,174,.35); color: var(--ok); }
.run-badge.error { background: rgba(229,168,147,.12); border-color: rgba(229,168,147,.35); color: var(--sev-critical); }
.run-badge.running { background: rgba(157,201,174,.10); border-color: rgba(157,201,174,.30); color: var(--pal-mint); }
.run-why { font-size: 12px; color: var(--ink-soft); margin-top: 3px; }

@media (prefers-reduced-motion: reduce) {
  .st-spin { animation-duration: 2.4s; }
}

/* the inline bar reopens the run modal, so it reads as a control */
.progress-line { cursor: pointer; }
.progress-line:hover .progress-fill { filter: brightness(1.12); }

/* processed count reads as a sentence, not a score line — "0/826" was being
   read as an error rather than as work not yet done */
.proc-count .sep { margin: 0 4px; }
.proc-word { color: var(--text-dim); margin-left: 5px; font-size: 13.5px; }
.proc-bar.idle { opacity: .45; }
.btn.dark.danger { background: var(--sev-critical); }
.btn.dark.danger:hover { background: var(--accent-deep); }

/* ------------------------------------------------ Audit tab */
.audit-empty {
  padding: 70px 40px; text-align: center; color: var(--ink-soft);
  max-width: 640px; margin: 0 auto; line-height: 1.6; font-size: 15px;
}
.audit-wrap { display: flex; gap: 18px; align-items: stretch; }
.audit-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.audit-toolbar {
  display: flex; align-items: center; gap: 12px; margin-bottom: 12px;
}
.audit-title { font-weight: 700; font-size: 16px; margin-right: auto; }
.audit-stage {
  position: relative; background: var(--bg-primary); border-radius: 10px;
  overflow: hidden; aspect-ratio: 5 / 4; max-height: 66vh;
}
#audit-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Verdict controls sit in a rail BESIDE the playback, never over it. As a
   bottom bar they covered defects on the frame's bottom edge — exactly the
   ones you have to look at before ruling. The rail holds its width whether
   or not a verdict is pending, so the canvas never resizes mid-playback. */
.audit-stagewrap { display: flex; gap: 12px; align-items: stretch; }
.audit-stage { flex: 1 1 auto; min-width: 0; }
.audit-verdictrail {
  flex: 0 0 208px; width: 208px;
  display: flex; flex-direction: column; justify-content: center;
  padding: 18px 15px; background: var(--bg-secondary);
  border: 1px solid var(--border); border-radius: var(--r-lg);
}
.avr-idle { display: flex; flex-direction: column; gap: 9px; }
.avr-k {
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-soft);
}
.avr-t { font-size: 13px; line-height: 1.55; color: var(--ink-soft); }
.avr-h { font-size: 12px; line-height: 1.7; color: var(--ink-soft); opacity: .8; }
.avr-h b {
  font: 500 11.5px 'Geist Mono', ui-monospace, Menlo, monospace;
  color: var(--text-light); background: var(--bg-dropdown);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px;
}
.audit-verdictbar {
  display: flex; flex-direction: column; align-items: stretch; gap: 9px;
  font-size: 13.5px;
}
.av-info { font-size: 13px; line-height: 1.55; color: var(--ink-soft); }
.audit-verdictbar select {
  width: 100%; background: var(--bg-dropdown); color: var(--text-light);
  border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px;
  font: inherit; font-size: 13px;
}
.btn.tiny { padding: 7px 14px; font-size: 13.5px; border-radius: 5px; background: var(--gray-200); }
.audit-verdictbar .btn.tiny {
  width: 100%; justify-content: center; padding: 9px 12px;
  border-radius: 9px; border-color: transparent;
  background: var(--border); color: var(--text-light);
  box-shadow: inset 0 0 0 1.5px var(--ember-edge);
}
.audit-verdictbar .btn.tiny.av-accept {
  background: var(--ok); color: var(--pal-ink); font-weight: 600;
}
.audit-verdictbar .btn.tiny.av-reject {
  background: var(--sev-high); color: var(--pal-ink); font-weight: 600;
}
.audit-verdictbar .btn.tiny:hover { filter: brightness(1.1); color: var(--text-light); }
.audit-verdictbar .btn.tiny.av-accept:hover,
.audit-verdictbar .btn.tiny.av-reject:hover { color: var(--pal-ink); }

/* narrow windows: the rail would starve the frame, so it goes back under it
   — full width, still outside the image, so nothing is ever covered */
@media (max-width: 1180px) {
  .audit-stagewrap { flex-direction: column; }
  .audit-verdictrail { flex: 0 0 auto; width: auto; }
  .audit-verdictbar { flex-direction: row; align-items: center; gap: 10px; }
  .av-info { margin-right: auto; }
  .audit-verdictbar select { width: auto; }
  .audit-verdictbar .btn.tiny { width: auto; }
  .avr-idle { flex-direction: row; align-items: center; gap: 14px; }
  .avr-t { margin-right: auto; }
}
.audit-transport {
  display: flex; align-items: center; gap: 10px; margin-top: 12px;
}
#audit-scrub { flex: 1; accent-color: var(--ink-dark); }
.audit-frameno { font-size: 13px; color: var(--ink-soft); white-space: nowrap; }
#audit-speed {
  border: 1px solid var(--gray-300); border-radius: 5px; padding: 6px 8px;
  font: inherit; font-size: 13.5px; background: var(--bg-secondary);
}
.audit-side {
  width: 320px; flex: 0 0 auto; border: 1px solid var(--gray-200);
  border-radius: 10px; display: flex; flex-direction: column;
  max-height: 78vh;
}
.audit-side-head {
  display: flex; justify-content: space-between; align-items: center;
  gap: 8px; flex-wrap: wrap;
  padding: 12px 14px; border-bottom: 1px solid var(--line);
  font-weight: 700; font-size: 14px;
}
.audit-dup-count { color: var(--sev-high); font-size: 13px; }
.audit-tracks { overflow-y: auto; }
.audit-track {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-bottom: 1px solid var(--line);
  cursor: pointer; font-size: 13.5px;
}
.audit-track:hover { background: var(--gray-100); }
.audit-track.cur { background: rgba(197,110,73,.10); }
.audit-track.reject { opacity: .55; }
.audit-track.deleted { opacity: .35; text-decoration: line-through; }
.at-chip {
  color: var(--text-light); font-weight: 700; border-radius: 4px;
  padding: 3px 7px; font-size: 12px; flex: 0 0 auto;
}
.at-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.at-cls { font-weight: 600; }
.at-sub { color: var(--ink-soft); font-size: 12.5px; }
.at-dup { color: var(--sev-high); font-weight: 600; }
.at-state { font-size: 12px; color: var(--ink-soft); text-transform: capitalize; }
.at-del { color: var(--gray-400); }
.at-del:hover { color: var(--sev-critical); }
.at-del svg { width: 13px; height: 13px; }
.at-merge { color: var(--gray-400); }
.at-merge:hover, .at-merge.on { color: var(--pal-steel); }
.at-merge svg { width: 15px; height: 15px; }
.at-merged { color: var(--pal-steel); font-weight: 600; }
.audit-track.merge-src { background: rgba(197,110,73,.12); box-shadow: inset 3px 0 0 var(--pal-steel); }
.audit-modseg {
  border-top: 1px solid var(--line); padding: 12px 14px;
  display: flex; flex-direction: column; gap: 7px; flex: 0 0 auto;
}
.audit-modseg .btn { justify-content: center; }
.audit-modseg .btn:disabled { opacity: .45; cursor: not-allowed; }
.modseg-note { font-size: 12.5px; color: var(--ink-soft); min-height: 15px; }
.modseg-note.ok { color: var(--ok); font-weight: 600; }
.modseg-note.stale { color: var(--warn); font-weight: 600; }
.modseg-note.bad { color: var(--sev-high); font-weight: 600; }
.modseg-note.run { color: var(--pal-steel); font-weight: 600; }
@media (max-width: 1000px) {
  .audit-wrap { flex-direction: column; }
  .audit-side { width: 100%; max-height: 40vh; }
}

/* ------------------------------------------------------------- location */
.loc-topline { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.loc-topline .section-title { margin: 0 auto 0 0; }
.loc-note { font-size: 13px; color: var(--warn); max-width: 420px; }
.loc-empty {
  padding: 60px 20px; text-align: center; color: var(--ink-soft);
  font-size: 15px;
}
.loc-panes { display: flex; gap: 18px; height: calc(100vh - 210px); min-height: 420px; }
.loc-map-pane {
  flex: 1; min-width: 0; position: relative; border-radius: 10px;
  overflow: hidden; background: var(--pal-ink);
}
#loc-map { position: absolute; inset: 0; width: 100%; height: 100%; }
.loc-map-pane .map-zoom { position: absolute; top: 12px; left: 12px; }
/* Marker legend — appears only on a MIXED map, i.e. when at least one defect
   has no module footprint to draw. Overlays satellite imagery, so it keeps the
   house dark ground in both themes, like .loc-strip. */
.loc-map-pane .loc-legend {
  position: absolute; top: 12px; right: 12px; max-width: 268px;
  display: flex; flex-direction: column; gap: 7px;
  padding: 9px 11px; border-radius: 8px; pointer-events: none;
  background: rgba(15,15,15,.78);
  border: 1px solid rgba(240,233,218,.14);
  color: #f0e9da; font-size: 11.5px; line-height: 1.35;
}
.loc-legend span { display: flex; align-items: flex-start; gap: 8px; }
.loc-legend i { flex: 0 0 auto; margin-top: 2px; }
.loc-legend i.lg-mod {
  width: 16px; height: 8px; border-radius: 1px;
  background: rgba(197,110,73,.67); border: 1px solid #c56e49;
}
.loc-legend i.lg-off {
  width: 13px; height: 13px; border-radius: 50%;
  background: rgba(0,0,0,.35); border: 2px dashed #c56e49;
}
.loc-legend i.lg-dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: #c56e49; border: 1px solid rgba(240,233,218,.8);
}
@media (max-width: 1000px) { .loc-map-pane .loc-legend { display: none; } }
.loc-map-pane .map-foot {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: space-between;
  padding: 5px 10px; font-size: 12px;
  background: rgba(255,255,255,.85); color: var(--ink);
}
.loc-side {
  width: 340px; flex: 0 0 auto; display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
}
.loc-side-head {
  padding: 12px 14px; font-weight: 700; font-size: 14px;
  border-bottom: 1px solid var(--line); background: var(--gray-100);
}
.loc-list { overflow-y: auto; }
.loc-row {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px; border-bottom: 1px solid var(--line);
  cursor: pointer; font-size: 13.5px;
}
.loc-row:hover { background: var(--gray-100); }
.loc-row.sel { background: rgba(197,110,73,.10); box-shadow: inset 3px 0 0 var(--pal-steel); }
.loc-row.ok .loc-cls { color: var(--ok); }
.loc-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.loc-cls { font-weight: 600; }
.loc-sub { color: var(--ink-soft); font-size: 12.5px; }
.loc-geo { font-family: ui-monospace, Menlo, monospace; font-size: 12px; color: var(--ink-soft); }
.loc-offplan { color: var(--warn); font-weight: 600; }
.loc-modseg { font-size: 13px; color: var(--ink-soft); max-width: 460px; }
.loc-modseg.ok { color: var(--ok); font-weight: 600; }
.loc-modseg.stale { color: var(--warn); font-weight: 600; }
.loc-match.exact { color: var(--ok); }
.loc-match.conflict { color: var(--warn); }
.loc-match.approx { color: var(--ink-soft); font-weight: 500; }
.loc-strip {
  position: absolute; left: 0; right: 0; bottom: 26px;
  display: flex; gap: 8px; align-items: center; overflow-x: auto;
  padding: 8px 10px; background: rgba(15,15,15,.82);
}
.ls-head { color: var(--text-light); font-size: 12.5px; font-weight: 700; white-space: nowrap; margin-right: 4px; }
.ls-card { position: relative; flex: 0 0 auto; width: 118px; cursor: pointer; }
.ls-card img { width: 100%; height: 86px; object-fit: cover; border-radius: 5px; display: block; }
.ls-card svg { position: absolute; inset: 0 0 16px 0; width: 100%; height: 86px; }
.ls-card svg rect { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.ls-card span { display: block; color: var(--text-muted); font-size: 10.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ls-card:hover img { outline: 2px solid var(--accent); }
@media (max-width: 1000px) {
  .loc-panes { flex-direction: column; height: auto; }
  .loc-map-pane { height: 50vh; }
  .loc-side { width: 100%; max-height: 40vh; }
}

/* ==========================================================================
   THE HOUSE LAYER
   Typography, the chamfer, the traced keyhole frame, the weave, the
   dimension line, the accent dot, film grain. Everything below is the
   system talking; the rules above are plumbing it inherits.
   ========================================================================== */

/* --- faces. Served locally so a demo never waits on a CDN --------------- */
@font-face { font-family: "Satoshi"; src: url("/fonts/satoshi-300.woff2") format("woff2");
             font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("/fonts/satoshi-400.woff2") format("woff2");
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("/fonts/satoshi-500.woff2") format("woff2");
             font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("/fonts/satoshi-700.woff2") format("woff2");
             font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("/fonts/satoshi-900.woff2") format("woff2");
             font-weight: 900; font-style: normal; font-display: swap; }
/* Uxum Grotesque is licensed, not redistributable — it leads the display
   stack so a licensed install is used when present, with a grotesque of the
   same temperament (weight 400, tight tracking) carrying the design when
   it is not. */
@font-face { font-family: "Bricolage Grotesque"; src: url("/fonts/bricolage-400.woff2") format("woff2");
             font-weight: 300 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist Mono"; src: url("/fonts/geistmono-400.woff2") format("woff2");
             font-weight: 400 500; font-style: normal; font-display: swap; }

/* --- ground + body ------------------------------------------------------ */
body {
  background: var(--bg-primary);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: clamp(14.5px, 1.15vw, 19px);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}

/* Film grain: fixed turbulence over the whole viewport, jumping on
   steps(1) at ~26 changes/sec — past film's 24fps, so it reads as grain
   rather than a texture that moves. */
body::after {
  content: "";
  position: fixed; inset: -50%;
  pointer-events: none;
  z-index: 9999;
  opacity: .14;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
  animation: grain .038s steps(1) infinite;
}
@keyframes grain {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-6%, 3%); }
  40%  { transform: translate(4%, -5%); }
  60%  { transform: translate(-3%, -4%); }
  80%  { transform: translate(5%, 4%); }
  100% { transform: translate(-4%, 2%); }
}
@media (prefers-reduced-motion: reduce) { body::after { animation: none; } }

/* --- type ---------------------------------------------------------------
   Display face is ALWAYS weight 400. Mono is labels, never sentences.  */
h1, h2, h3, h4, .section-title, .modal h2, .placeholder-body h2,
.detail-head, .stat-num, .big-num {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -.032em;
  line-height: 1.1;
  color: var(--text-light);
}
.section-title { font-size: clamp(21px, 2.1vw, 30px); line-height: 1.06; }
.modal h2      { font-size: clamp(19px, 1.7vw, 25px); letter-spacing: -.03em; }
.placeholder-body h2 { font-size: clamp(20px, 1.9vw, 27px); }

/* Numbers worth reading are display face, never mono. */
.census-cell .v, .meter-num, .run-count, .kpi-num, .at-chip, .stat-v,
.wb-count, #audit-track-count, #loc-count {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -.02em;
}

/* Mono: kickers, data labels, codes, coordinates. Floor is 10.5px. */
.loc-geo, .audit-frameno, .wb-sub, .census-cell .k, .detail-row .k,
.step-tag, .run-badge, .st-msg, .modseg-note, .ro, #wb-align-info,
.map-foot, .ls-card span, .at-sub, .loc-sub, .film-name, .thumb-name {
  font-family: var(--font-mono);
  font-size: max(10.5px, .74em);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* …but these hold sentences and numbers together — keep the mono, drop
   the shouting */
.st-msg, .modseg-note, .at-sub, .loc-sub, .ls-card span, .film-name,
.thumb-name, .loc-geo, .audit-frameno, #wb-align-info {
  text-transform: none;
  letter-spacing: .04em;
}

/* --- the chamfer + the traced keyhole frame -----------------------------
   A bevelled surface gets the traced frame or no frame, never a plain
   border: the clip cuts the diagonals off unstroked. The frame is an
   evenodd ::before tracing the outline, inner contour inset 1px with its
   chamfer points at chamfer+0.41px — a 45° chamfer inset 1px loses
   2−√2 of its leg. */
/* Only surfaces that need their OWN positioning context get one. The
   popups below are already absolutely positioned by their own rules —
   forcing `relative` here dropped them back into flow, which stretched
   the whole toolbar to the height of the open menu. */
.chamfer, .panel, .sidebar, .modal, .drawer, .census-cell, .detail-card,
.toast, .audit-side, .loc-side, .run-modal, .thumb-card, .film-card,
.audit-modseg .btn {
  position: relative;
}

/* Curved, not cut. Every plate in the app is a soft rectangle with a
   real hairline — with a radius a plain border draws correctly, so the
   traced-outline pseudo the chamfer needed is retired. */
.chamfer, .panel, .sidebar, .modal, .drawer, .census-cell, .detail-card,
.toast, .audit-side, .loc-side, .run-modal, .thumb-card, .film-card,
.sel-menu, .ann-menu, .filter-pop, .sev-filter-pop, .legend-pop,
.ann-pop, .confirm-pop, .audit-modseg .btn, .wb-alignbar {
  clip-path: none !important;
  border: 1px solid var(--frame, var(--border));
  border-radius: var(--r-md);
  overflow: hidden;
}
.chamfer::before, .panel::before, .sidebar::before, .modal::before,
.drawer::before, .census-cell::before, .detail-card::before,
.toast::before, .audit-side::before, .loc-side::before,
.run-modal::before, .thumb-card::before, .film-card::before,
.sel-menu::before, .ann-menu::before, .filter-pop::before,
.sev-filter-pop::before, .legend-pop::before, .ann-pop::before,
.confirm-pop::before, .wb-alignbar::before { display: none; }

.panel, .modal, .run-modal, .drawer, .detail-card { border-radius: var(--r-lg); }
/* the panel holds dropdowns that hang past its edge */
.panel { overflow: visible; }
/* Same reason, and it was the whole "can't reach the severities" bug: the
   frame rule above clips .ann-pop, so its label and severity menus were cut
   off at the popover's own edge — in BOTH directions, with nothing to
   scroll. The fields carry their own radii, so there is nothing to clip. */
.ann-pop { overflow: visible; }

/* --- the house weave: grain, not stripes -------------------------------- */
.panel, .sidebar, .modal, .drawer, .detail-head, .audit-side, .loc-side,
.run-modal, .census-cell, .toast, .ann-pop, .wb-alignbar {
  background-image: var(--weave);
}

/* --- surfaces ----------------------------------------------------------- */
.panel   { background-color: var(--bg-secondary); }
.sidebar { background-color: var(--bg-secondary); }
.sel-menu, .ann-menu, .filter-pop, .sev-filter-pop, .legend-pop,
.confirm-pop, .ann-pop, .modal, .run-modal, .drawer {
  background-color: var(--bg-dropdown);
}

/* --- hover is light, not colour ----------------------------------------- */
.side-btn:hover, .tab:not(.active):hover, .btn:not(.dark):hover,
.wb-opt:hover, .structure-row:hover, .img-table tbody tr:hover,
.audit-track:hover, .loc-row:hover, .thumb-card:hover, .film-card:hover,
.sel-menu li:hover, .ann-menu div:hover, .run-row:hover {
  background-color: var(--hover-light);
  color: var(--text-light);
}
/* cards lift, and the frame turns ember */
.thumb-card, .film-card, .census-cell, .detail-card {
  transition: transform var(--ease-dur) var(--ease);
}
.thumb-card:hover, .film-card:hover, .census-cell:hover {
  transform: translateY(-2px);
  --frame: var(--accent);
}

/* --- the accent dot ----------------------------------------------------
   6–9px filled circle marking active / selected / pointed-at. In the nav
   it arrives by slide + fade. */
.tab { position: relative; }
.tab::after {
  content: "";
  position: absolute; left: 12px; top: 50%;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  opacity: 0;
  transform: translate(-6px, -50%);
  transition: opacity var(--ease-dur) var(--ease),
              transform var(--ease-dur) var(--ease);
}
.tab:hover::after { opacity: .55; transform: translate(0, -50%); }
.tab.active::after { opacity: 1; transform: translate(0, -50%); background: var(--pal-ink); }

/* --- filled surfaces: ember ground, ink type ----------------------------
   The ember is scarce on purpose: ONE filled CTA per view. Everything
   else that used to be "the dark button" is now a quiet plate with the
   standard hairline — same affordance, none of the shouting. */
.tab.active, .side-btn.side-toggle, .side-btn.active,
.filter-pop button.on, .wb-tool.on, .st-icon.done, .lbl-toggle.on i,
.structure-row .btn-select:hover, .download-bar:hover, .at-merge.on,
.btn.dark[data-cta], [data-cta],
#btn-process-all, #btn-to-audit, #btn-modseg, #btn-run-results,
#btn-import-commit, #btn-add-save, #btn-geo-apply, #wb-align-lock,
#report-ok, #btn-loc-csv {
  background-color: var(--accent);
  color: var(--pal-ink);
}
.tab.active svg, .wb-tool.on svg,
#btn-process-all svg, #btn-to-audit svg, #btn-modseg svg,
#btn-loc-csv svg, [data-cta] svg { color: var(--pal-ink); }

/* the quiet plate: what a secondary action looks like here */
.btn.dark {
  background-color: var(--bg-dropdown);
  color: var(--text-primary);
  border: 1px solid var(--border);
}
.btn.dark:not([data-cta]):hover {
  background-color: var(--hover-light);
  border-color: var(--accent);
  color: var(--text-light);
}
.btn.dark svg { color: var(--accent); }

/* The fluid sheen — filled CTA surfaces only. Never ghost buttons or
   small chips, where the blobs read as uneven fill. */
[data-cta], #btn-process-all, #btn-to-audit, #btn-modseg,
#btn-run-results, #btn-import-commit, #btn-add-save, #btn-geo-apply,
#wb-align-lock, #report-ok, #btn-loc-csv {
  position: relative;
  overflow: hidden;
  background-image:
    radial-gradient(60% 120% at 20% 20%, rgba(240,233,218,.22), transparent 60%),
    radial-gradient(50% 100% at 80% 70%, rgba(142,72,41,.35), transparent 65%);
  background-size: 180% 180%, 200% 200%;
  animation: sheen 14s ease-in-out infinite alternate;
}
@keyframes sheen {
  0%   { background-position:   0% 0%,  100% 100%; }
  50%  { background-position:  60% 40%,  30% 60%; }
  100% { background-position: 100% 100%,   0% 0%; }
}
@media (prefers-reduced-motion: reduce) { [data-cta] { animation: none; } }
#btn-process-all:hover, #btn-to-audit:hover, #btn-modseg:hover:not(:disabled),
#btn-loc-csv:hover, [data-cta]:hover { background-color: var(--pal-blush); }
#btn-modseg:disabled { background-color: var(--bg-dropdown); color: var(--text-dim);
  border: 1px solid var(--border); animation: none; background-image: var(--weave); }

/* --- tabs --------------------------------------------------------------- */
.tabs {
  background: transparent;
  gap: 2px;
  border-radius: 0;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0;
}
.tab {
  border-radius: var(--r-md) var(--r-md) 0 0;
  padding: 14px 14px 14px 28px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: clamp(13.5px, 1.02vw, 16px);
  color: var(--text-muted);
  letter-spacing: -.005em;
}

/* --- the dimension line (SectionTag) -----------------------------------
   accent dot → uppercase mono label → hatched band masked at both ends →
   hollow end dot. A run between two anchors, never a stroke into air. */
.section-title {
  display: flex;
  align-items: center;
  gap: 12px;
}
.section-title::before {
  content: "";
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  flex: 0 0 auto;
}
.section-title::after {
  content: "";
  flex: 1 1 auto;
  height: 10px;
  min-width: 56px;
  /* hatched band, then the hollow end dot — the run has two anchors, it
     never trails off into air */
  background:
    radial-gradient(circle at calc(100% - 5px) 50%,
      transparent 0 2.2px, var(--pal-slate-lo) 2.2px 3.6px, transparent 3.7px)
      right center / 100% 100% no-repeat,
    repeating-linear-gradient(115deg,
      rgba(240,233,218,.11) 0 1px, transparent 1px 6px)
      left center / calc(100% - 20px) 100% no-repeat;
  /* fade the band at both ends, then re-open for the dot */
  -webkit-mask-image: linear-gradient(90deg,
      transparent 0, #000 16px, #000 calc(100% - 34px),
      transparent calc(100% - 20px), transparent calc(100% - 13px),
      #000 calc(100% - 12px), #000 100%);
          mask-image: linear-gradient(90deg,
      transparent 0, #000 16px, #000 calc(100% - 34px),
      transparent calc(100% - 20px), transparent calc(100% - 13px),
      #000 calc(100% - 12px), #000 100%);
}

/* --- controls ----------------------------------------------------------- */
.btn, button.btn {
  border-radius: var(--r-md);
  font-family: var(--font-body);
  font-weight: 500;
  letter-spacing: .005em;
  border-color: var(--border);
  color: var(--text-primary);
}
input, select, textarea {
  font-family: var(--font-body);
  background-color: var(--bg-dropdown);
  color: var(--text-primary);
  border-color: var(--border);
  border-radius: var(--r-sm);
}
input::placeholder, textarea::placeholder { color: var(--text-dim); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg-primary); }
::-webkit-scrollbar-thumb { background: var(--border); }
::-webkit-scrollbar-thumb:hover { background: var(--pal-ink-soft); }

/* --- tables ------------------------------------------------------------- */
.img-table th, .import-table th, table th {
  font-family: var(--font-mono);
  font-size: max(10.5px, .72em);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 400;
  border-bottom-color: var(--border);
}
.img-table td, .import-table td, table td { border-bottom-color: var(--border-light); }

/* --- the media surfaces stay honest ------------------------------------
   Thermal frames and the satellite map are DATA. They get the ground and
   the frame, never a tint over the pixels. */
.stage, .map-pane, .wb-canvas, .audit-stage, .loc-map-pane, .film-thumb {
  background-color: var(--pal-ink);
}
.map-foot, .audit-frameno { color: var(--text-dim); }

/* --- severity + status chips ------------------------------------------- */
.at-chip, .sev-chip, .step-tag, .run-badge {
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: max(10.5px, .72em);
  letter-spacing: .12em;
}
.at-chip { color: var(--pal-ink); font-weight: 500; }

/* --- audit + location panes -------------------------------------------- */
.audit-side, .loc-side { background-color: var(--bg-secondary); }
.audit-side-head, .loc-side-head {
  font-family: var(--font-mono);
  font-size: max(10.5px, .74em);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 400;
  border-bottom-color: var(--border);
}
.audit-track.cur { background: rgba(197,110,73,.10); box-shadow: inset 3px 0 0 var(--accent); }
.audit-track.merge-src { background: rgba(197,110,73,.14); box-shadow: inset 3px 0 0 var(--accent); }
.loc-row.sel { background: rgba(197,110,73,.10); box-shadow: inset 3px 0 0 var(--accent); }
.audit-modseg { border-top-color: var(--border); }

/* --- workbench ---------------------------------------------------------- */
.workbench, .wb-main { background-color: var(--bg-primary); }
.wb-card, .wb-side { background-color: var(--bg-secondary); }
.wb-toolbar { border-bottom: 1px solid var(--border); }
.wb-tool { border-radius: var(--r-sm); color: var(--text-muted); }
.wb-mod { stroke: rgba(169,180,174,.5); }
.wb-mod.aligning { stroke: var(--accent); }

/* --- the light reading panel ------------------------------------------- */
.panel-light {
  background-color: var(--panel-light);
  background-image: var(--weave-light);
  color: var(--panel-light-ink);
  --frame: var(--panel-light-border);
  --c: var(--chamfer-lg);
}
.panel-light a { color: var(--accent-deep); }
.panel-light h1, .panel-light h2, .panel-light h3 { color: var(--accent-dark); }
.panel-light .muted { color: var(--panel-light-muted); }

/* --- switches: the state is the knob, not a filled slab ----------------
   A toggle is not a call to action. The pill stays a quiet plate; the
   ember knob (and a bone label) carries "on". */
.lbl-toggle {
  background: var(--bg-dropdown) !important;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: max(10.5px, .72em);
  letter-spacing: .1em;
  text-transform: uppercase;
}
.lbl-toggle.on { color: var(--text-light); border-color: var(--accent); }
.lbl-toggle i { background: var(--pal-ink-soft) !important; box-shadow: none !important; }
.lbl-toggle.on i { background: var(--accent) !important; }

/* --- the report hand-off: one action, so it carries the ember ----------- */
.download-bar {
  background-color: var(--accent);
  color: var(--pal-ink);
  border: 0;
  font-family: var(--font-body);
  font-weight: 500;
  letter-spacing: .01em;
  border-radius: var(--r-md);
  background-image:
    radial-gradient(60% 120% at 20% 20%, rgba(240,233,218,.22), transparent 60%),
    radial-gradient(50% 100% at 80% 70%, rgba(142,72,41,.35), transparent 65%);
  background-size: 180% 180%, 200% 200%;
  animation: sheen 14s ease-in-out infinite alternate;
}
.download-bar:hover { background-color: var(--pal-blush); color: var(--pal-ink); }
.rep-lead { color: var(--text-muted); }

/* --- scrims over media: warm dark, never a white wash ------------------
   Two survivors of the light theme: the map caption bar and the badge on
   a selected filmstrip card were both white translucent, which on the
   dark side reads as a hole punched in the page (and put bone type on
   near-white at 1.7:1). */
.map-foot {
  background: linear-gradient(to top,
              rgba(22,18,14,.92), rgba(22,18,14,.62));
  color: var(--text-primary);
  border-top: 1px solid var(--border-light);
}
.film-badge {
  background: var(--bg-dropdown);
  color: var(--text-primary);
  border: 1px solid var(--border);
}
.film-card.on .film-badge { background: var(--pal-ink); color: var(--text-light); }
.film-card.on .film-top { background: rgba(197,110,73,.22); }
.film-thumb img, .thumb-card img { background: var(--pal-ink); }

/* --- progress is completion, not a call to action -----------------------
   Every structure row carries a bar; painting them all ember drowned the
   one thing on the view that IS an action. Mint reads as "done" and hands
   the ember back to the button. */
.progress-fill, .meter-fill, .proc-bar span, .st-fill,
.run-stage.done .st-fill { background: var(--pal-mint) !important; }
.progress-track, .meter-track, .proc-bar { background: var(--border-light); }

/* the primary action of the Structures view */
#btn-add-structure {
  background-color: var(--accent);
  color: var(--pal-ink);
  border-color: var(--accent);
}
#btn-add-structure svg { color: var(--pal-ink); }
#btn-add-structure:hover { background-color: var(--pal-blush); }

/* ==========================================================================
   AUTO-AUDIT — the on-camera layer
   ========================================================================== */
.demo-hud {
  position: absolute; top: 14px; left: 14px; z-index: 20;
  display: flex; align-items: center; gap: 10px;
  background: rgba(22,18,14,.88);
  background-image: var(--weave);
  border: 1px solid var(--border);
  padding: 8px 14px;
  font-family: var(--font-mono);
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-primary);
  border-radius: var(--r-md);
}
.dh-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  animation: dh-pulse 1.1s ease-in-out infinite;
}
@keyframes dh-pulse { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
.dh-sep { width: 1px; height: 14px; background: var(--border); }
#dh-count { color: var(--text-light); }

.demo-chip {
  position: absolute; top: 14px; right: 14px; z-index: 20;
  width: 218px;
  background: rgba(22,18,14,.92);
  background-image: var(--weave);
  border: 1px solid var(--border);
  padding: 12px 14px 14px;
  border-radius: var(--r-md);
  animation: dc-in .28s var(--ease);
}
@keyframes dc-in { from { opacity: 0; transform: translateY(-6px); } }
.dc-kicker {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .16em; color: var(--text-dim); margin-bottom: 6px;
}
.dc-cls {
  font-family: var(--font-display); font-weight: 400;
  font-size: 21px; letter-spacing: -.02em; color: var(--text-light);
  margin-bottom: 10px;
}
.dc-row {
  display: flex; justify-content: space-between; align-items: baseline;
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .12em; color: var(--text-dim);
  margin-bottom: 5px; text-transform: uppercase;
}
.dc-row b { color: var(--text-primary); font-weight: 500; }
.dc-bar {
  height: 3px; background: var(--border-light);
  margin: 3px 0 8px;
}
.dc-bar i {
  display: block; height: 100%; width: 0;
  background: var(--accent);
  transition: width 1.2s var(--ease);
}
.dc-verdict {
  margin-top: 10px; padding: 7px 0;
  text-align: center;
  font-family: var(--font-mono); font-size: 12px;
  letter-spacing: .22em;
  animation: dc-stamp .22s var(--ease);
}
@keyframes dc-stamp { from { transform: scale(1.25); opacity: 0; } }
.dc-verdict.ok  { background: rgba(157,201,174,.16); color: var(--pal-mint);
                  border: 1px solid rgba(157,201,174,.45); }
.dc-verdict.dim { background: rgba(138,129,117,.14); color: var(--text-muted);
                  border: 1px solid var(--border); }

/* ==========================================================================
   MOTION
   The toggles never actually animated: the OFF state flipped
   flex-direction, so the knob teleported between sides — a property no
   browser can tween. They are rebuilt here as a real switch: a fixed
   track on the right, a knob that TRAVELS, an ember wash that sweeps the
   pill, and a soft trail that lags a beat behind the knob so the motion
   reads as liquid rather than mechanical.
   ========================================================================== */
.lbl-toggle {
  position: relative;
  flex-direction: row !important;          /* kill the row-reverse jump */
  padding: 7px 46px 7px 16px !important;   /* fixed geometry both states */
  overflow: hidden;
  isolation: isolate;
  transition: border-color .42s var(--ease), color .42s var(--ease),
              letter-spacing .42s var(--ease), transform .18s var(--ease);
}
.lbl-toggle > span { position: relative; z-index: 2; }

/* the ember wash: sweeps in from the switch side as it turns on */
.lbl-toggle::before {
  content: "";
  position: absolute; inset: 0;
  z-index: 0;
  background: linear-gradient(270deg,
              rgba(197,110,73,.20), rgba(197,110,73,.05) 55%, transparent);
  opacity: 0;
  transform: translateX(14%);
  transition: opacity .5s var(--ease), transform .62s var(--ease);
}
.lbl-toggle.on::before { opacity: 1; transform: translateX(0); }

/* the track */
.lbl-toggle::after {
  content: "";
  position: absolute; z-index: 1;
  right: 9px; top: 50%;
  width: 30px; height: 16px; margin-top: -8px;
  border-radius: 999px;
  background: var(--border-light);
  box-shadow: inset 0 0 0 1px var(--border);
  transition: background .42s var(--ease), box-shadow .42s var(--ease);
}
.lbl-toggle.on::after {
  background: rgba(197,110,73,.18);
  box-shadow: inset 0 0 0 1px rgba(197,110,73,.55);
}

/* the knob — travels the track on the house curve */
.lbl-toggle i {
  position: absolute; z-index: 2;
  right: 11px; top: 50%;
  width: 12px !important; height: 12px !important;
  margin-top: -6px;
  border-radius: 999px;
  background: var(--pal-ink-soft) !important;
  transform: translateX(-14px);
  transition: transform .46s var(--ease), background .34s var(--ease);
}
.lbl-toggle.on i { transform: translateX(0); background: var(--accent) !important; }

/* the trail: same travel, slower curve — it arrives a beat late, which is
   what makes the knob read as liquid instead of a sliding dot */
.lbl-toggle i::before {
  content: "";
  position: absolute; inset: -4px;
  border-radius: 999px;
  background: radial-gradient(circle, rgba(197,110,73,.5), transparent 68%);
  opacity: 0;
  transform: translateX(9px);
  transition: opacity .5s var(--ease), transform .78s cubic-bezier(.22, 1, .3, 1);
}
.lbl-toggle.on i::before { opacity: 1; transform: translateX(-9px); }

.lbl-toggle:hover { border-color: var(--pal-ink-soft); color: var(--text-primary); }
.lbl-toggle.on:hover { border-color: var(--accent); color: var(--text-light); }
.lbl-toggle.on { letter-spacing: .12em; }
.lbl-toggle:active { transform: scale(.975); }

/* --- everything else that clicks -------------------------------------- */
.btn, .round-btn, .wb-tool, .tab, .side-btn, .at-merge, .at-del,
.download-bar, .film-card, .thumb-card, .census-cell {
  transition: background-color .32s var(--ease), border-color .32s var(--ease),
              color .32s var(--ease), transform .18s var(--ease),
              box-shadow .32s var(--ease);
}
.btn:active, .round-btn:active, .wb-tool:active,
.download-bar:active, .side-btn:active { transform: translateY(1px) scale(.99); }

/* icons lean into the press */
.btn svg, .round-btn svg, .wb-tool svg { transition: transform .28s var(--ease); }
.btn:hover svg, .round-btn:hover svg { transform: scale(1.08); }

/* the ember CTA breathes on hover instead of just swapping colour */
#btn-process-all, #btn-to-audit, #btn-modseg, #btn-loc-csv,
#btn-add-structure, #btn-autoaudit, .download-bar, [data-cta] {
  transition: background-color .34s var(--ease), color .34s var(--ease),
              box-shadow .34s var(--ease), transform .18s var(--ease);
}
#btn-process-all:hover, #btn-to-audit:hover, #btn-loc-csv:hover,
#btn-add-structure:hover, #btn-autoaudit:hover:not(:disabled),
.download-bar:hover, [data-cta]:hover:not(:disabled) {
  box-shadow: 0 0 0 1px rgba(197,110,73,.5), 0 6px 22px -10px rgba(197,110,73,.85);
}

/* tabs: the label drifts a hair as the dot arrives */
.tab { transition: color .3s var(--ease), background-color .3s var(--ease),
                   padding-left .34s var(--ease); }
.tab:hover:not(.active) { padding-left: 31px; }

@media (prefers-reduced-motion: reduce) {
  .lbl-toggle, .lbl-toggle *, .lbl-toggle::before, .lbl-toggle::after,
  .btn, .round-btn, .wb-tool, .tab, .side-btn { transition-duration: .01ms !important; }
}

/* ==========================================================================
   THE REASONING PLATE — the module, lifted out of the frame and resolved
   ========================================================================== */
.audit-stage { position: relative; }
.audit-stage.reasoning #audit-canvas {
  filter: brightness(.42) saturate(.85);
  transition: filter .6s var(--ease);
}
#audit-canvas { transition: filter .6s var(--ease); }

.reveal-plate {
  position: absolute; left: 50%; top: 50%; z-index: 25;
  transform: translate(-50%, -50%);
  transform-origin: center;
  background: var(--pal-ink);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: 0 40px 90px -30px rgba(0,0,0,.85);
  will-change: transform, opacity;
}
#reveal-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* a hairline round the plate — the module is the subject */
.rv-frame {
  position: absolute; inset: 0; pointer-events: none; z-index: 3;
  border: 1px solid rgba(240,233,218,.16);
  border-radius: var(--r-lg);
}

/* one small pill, bottom-left — no banners across the grid */
.rv-pill {
  position: absolute; left: 14px; bottom: 18px; z-index: 4;
  display: inline-flex; align-items: center; gap: 9px;
  height: 26px; padding: 0 12px;
  border-radius: 999px;
  background: rgba(18,15,12,.62);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .06em;
  white-space: nowrap;
}
.rv-id { color: var(--accent); letter-spacing: .12em; }
.rv-caption {
  color: rgba(240,233,218,.8);
  transition: opacity .16s ease;
  background: linear-gradient(90deg, rgba(240,233,218,.5) 40%,
              rgba(240,233,218,.98) 50%, rgba(240,233,218,.5) 60%);
  background-size: 250% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: rv-shimmer 1.9s linear infinite;
}
@keyframes rv-shimmer {
  from { background-position: 105% 0; } to { background-position: -5% 0; }
}
.rv-bar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 4;
  height: 2px; background: rgba(240,233,218,.10);
}
.rv-bar i { display: block; height: 100%; width: 0; background: var(--accent); }
@media (prefers-reduced-motion: reduce) {
  .rv-caption { animation: none; -webkit-text-fill-color: var(--text-light); }
}

/* ==========================================================================
   TAB TRAVEL · THE PICKER · THE STRUCTURE FILTER
   ========================================================================== */

/* --- the strip: one ember rule that travels between tabs ---------------- */
.tabs { position: relative; }
.tab-ink {
  position: absolute; left: 0; bottom: -1px;
  height: 2px; width: 0;
  background: var(--accent);
  opacity: 0;
  pointer-events: none;
  transition: transform .46s var(--ease), width .46s var(--ease),
              opacity .3s var(--ease);
}
/* the panel steps back, then the next one arrives — never a hard cut */
.tab-out { animation: tab-out .12s var(--ease) forwards; }
.tab-in  { animation: tab-in .38s var(--ease) both; }
@keyframes tab-out { to { opacity: 0; transform: translateY(-5px); } }
@keyframes tab-in {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .tab-out, .tab-in { animation: none; }
  .tab-ink { transition: none; }
}

/* --- the image-kind picker --------------------------------------------- */
.kind-pick { position: relative; flex: 0 0 auto; }
.kind-trigger {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 9px 12px 9px 13px;
  background: var(--bg-dropdown);
  border: 1px solid var(--border);
  color: var(--text-primary);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase;
  border-radius: var(--r-md);
  transition: border-color .3s var(--ease), color .3s var(--ease),
              background-color .3s var(--ease);
}
.kind-trigger:hover, .kind-trigger.open {
  border-color: var(--accent); color: var(--text-light);
}
.kind-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); flex: 0 0 auto;
}
.kind-chev {
  width: 13px; height: 13px; color: var(--text-dim);
  transition: transform .34s var(--ease);
}
.kind-trigger.open .kind-chev { transform: rotate(180deg); }

.kind-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 40;
  min-width: 100%;
  background: var(--bg-dropdown);
  background-image: var(--weave);
  padding: 5px;
  border-radius: var(--r-md);
  box-shadow: 0 22px 44px -18px rgba(0,0,0,.9);
  animation: menu-in .22s var(--ease);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-6px); } }
.kind-menu button {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; width: 100%;
  padding: 9px 11px;
  color: var(--text-muted);
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .1em; text-transform: uppercase;
  white-space: nowrap;
  transition: background-color .22s var(--ease), color .22s var(--ease);
}
.kind-menu button i {
  font-style: normal; font-size: 10.5px; color: var(--text-dim);
  letter-spacing: .14em;
}
.kind-menu button:hover { background: var(--hover-light); color: var(--text-light); }
.kind-menu button.on { color: var(--accent); }
.kind-menu button.on i { color: var(--accent); }

/* --- the structure filter ---------------------------------------------- */
.filter-pop {
  top: calc(100% + 8px);
  width: 320px; min-width: 0;
  max-height: 380px;
  padding: 0;
  overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--bg-dropdown);
  background-image: var(--weave);
  border-radius: var(--r-md);
  box-shadow: 0 30px 60px -22px rgba(0,0,0,.92);
  animation: menu-in .24s var(--ease);
}
.fp-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 15px 10px;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .15em;
}
.fp-kicker { color: var(--text-dim); }
.fp-count { color: var(--accent); }
.fp-search {
  position: relative;
  margin: 0 12px 8px;
}
.fp-search svg {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 14px; height: 14px; color: var(--text-dim);
}
.fp-search input {
  width: 100%;
  padding: 9px 10px 9px 32px;
  background: rgba(240,233,218,.04);
  border: 1px solid var(--border);
  color: var(--text-light);
  font-family: var(--font-body); font-size: 13.5px;
}
.fp-search input:focus { border-color: var(--accent); outline: none; }
.fp-list { overflow-y: auto; padding: 4px 6px 8px; }
.filter-pop button {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; width: 100%;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  font-family: var(--font-body); font-size: 13.5px;
  color: var(--text-muted);
  text-align: left;
  transition: background-color .2s var(--ease), color .2s var(--ease),
              box-shadow .2s var(--ease);
}
.fp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fp-n {
  flex: 0 0 auto;
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .1em; color: var(--text-dim);
}
.filter-pop button:hover { background: var(--hover-light); color: var(--text-light); }
.filter-pop button.on {
  background: rgba(197,110,73,.12);
  color: var(--text-light);
  box-shadow: inset 2px 0 0 var(--accent);
}
.filter-pop button.on .fp-n { color: var(--accent); }
.fp-empty {
  padding: 22px 12px; text-align: center;
  color: var(--text-dim); font-size: 13px;
}

/* --- the active tab is the RULE, not a slab -----------------------------
   A filled ember tab and a travelling ember underline are two indicators
   competing, and the fill hides the very motion that makes the change
   read as seamless. The tab keeps the dot and bone type; the rule does
   the moving. It also hands the ember back to the CTAs, which is where
   the system wants it. */
.tab.active {
  background-color: transparent !important;
  color: var(--text-light);
}
.tab.active::after { background: var(--accent); }
.tab.active svg { color: var(--accent); }
.tab:hover:not(.active) { background-color: var(--hover-light); }


/* ==========================================================================
   MATRIX DOT LOADER (transitions.dev) + the audit action pair
   ========================================================================== */
:root {
  --matrix-cycle: 1200ms;
  --matrix-base: var(--pal-ink-soft);
  --matrix-active: var(--accent);
  --matrix-ease: ease-in-out;
  --matrix-dot: 2px;
}
.t-matrix {
  display: grid;
  grid-template-columns: repeat(4, var(--matrix-dot));
  grid-auto-rows: var(--matrix-dot);
  gap: var(--matrix-dot);
  flex: 0 0 auto;
}
.t-matrix i {
  display: block;
  border-radius: 1px;
  background: var(--matrix-base);
  animation: t-matrix-pulse var(--matrix-cycle) var(--matrix-ease) infinite;
  animation-delay: calc(var(--d, 0) * 1ms);
}
.t-matrix i.is-gap { visibility: hidden; animation: none; }
.t-matrix i.is-steady { animation: none; }
@keyframes t-matrix-pulse {
  0%, 45%, 100% { background-color: var(--matrix-base); }
  15%           { background-color: var(--matrix-active); }
}
@media (prefers-reduced-motion: reduce) {
  .t-matrix i { animation: none !important; }
}
#rv-matrix { --matrix-dot: 3px; margin-right: 2px; }

/* --- Auto-Audit + Align grid: one matched pair -------------------------
   The link was rendering as underlined body text beside a filled slab —
   two different species sitting together. Same height, same curve, same
   type; the ember says which one starts the work. */
.audit-toolbar .btn, .audit-toolbar a.btn, #btn-autoaudit {
  display: inline-flex; align-items: center; gap: 9px;
  height: 36px; padding: 0 16px;
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: 11px; letter-spacing: .13em; text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
#btn-autoaudit {
  background-color: var(--accent);
  color: var(--pal-ink);
  border: 1px solid var(--accent);
  font-weight: 500;
}
#btn-autoaudit svg { width: 15px; height: 15px; color: var(--pal-ink); }
#btn-autoaudit:hover:not(:disabled) { background-color: var(--pal-blush); }
#btn-autoaudit:disabled { opacity: .5; }
.audit-toolbar a.btn, #btn-audit-draw {
  background: var(--bg-dropdown);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.audit-toolbar a.btn:hover, #btn-audit-draw:hover {
  border-color: var(--accent); color: var(--text-light);
  background: var(--hover-light);
}
#btn-audit-draw svg { width: 15px; height: 15px; }
/* armed: the ember, framed like every other live control on this page */
#btn-audit-draw.on {
  background: var(--accent); color: var(--pal-ink);
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--ember-edge);
}
#btn-audit-draw.on svg { color: var(--pal-ink); }
/* pills read better against pills */
.audit-toolbar .lbl-toggle { height: 36px; }

/* The card clips its own overflow for the rounded corners, and the toolbar
   sits 20px from its top edge — so a tooltip drawn ABOVE a tool button lost
   its upper half and the labels read as missing. Drop them below instead,
   where the stage has room. */
.wb-toolbar .round-btn[data-tip]:hover::after {
  top: calc(100% + 8px); bottom: auto; z-index: 80;
}

/* --- curved media: the frames sit IN the card, not against its edge ---- */
.thumb-card, .film-card, .ls-card, .stage, .film-thumb, .map-pane,
.audit-stage, .loc-map-pane, .wb-card {
  border-radius: var(--r-md);
  overflow: hidden;
}
.film-thumb img, .thumb-card img, .ls-card img, .stage img {
  border-radius: 0;
}
.film-card { padding-bottom: 2px; }
.audit-stage, .wb-card, .loc-map-pane { border-radius: var(--r-lg); }
.round-btn { border-radius: var(--r-pill); }
.sev-chip, .at-chip { border-radius: var(--r-pill); }

/* link-buttons are buttons, not links — anywhere they appear */
a.btn { text-decoration: none; }

/* Scrolling menus keep their scroll: the blanket overflow that rounds a
   card would otherwise clip the list dead. */
.sel-menu.tall, .ann-menu, .sev-filter-pop, .legend-pop, .audit-tracks,
.loc-list, .fp-list, .drawer, .run-stages { overflow: auto; }
.filter-pop { overflow: hidden; }

/* --- the reasoning card IS the loader ----------------------------------
   The plate fills with the dot field while the read runs; when it ends
   the field lifts and the module it was reading lands underneath. */
.rv-field {
  position: absolute; inset: 0; z-index: 2;
  display: grid;
  gap: 7px;
  padding: 7px;
  background: var(--pal-ink);
  transition: opacity .62s var(--ease);
}
.rv-field.gone { opacity: 0; }
.rv-field i {
  display: block;
  border-radius: var(--r-sm);
  background: var(--matrix-base);
  animation: t-matrix-pulse var(--matrix-cycle) var(--matrix-ease) infinite;
  animation-delay: calc(var(--d, 0) * 1ms);
}
.rv-field i.is-gap { visibility: hidden; animation: none; }
.rv-field i.is-steady { animation: none; }
@media (prefers-reduced-motion: reduce) { .rv-field i { animation: none !important; } }
/* The idle lattice has to read on its own — if only the pulsing cells
   are visible the card looks like scattered blocks rather than a field
   at work. Cells sit clearly above a darker ground. */
.rv-field {
  background: #141110;
  --matrix-base: #2a2420;
  --matrix-active: var(--accent);
}
.rv-field i { box-shadow: inset 0 0 0 1px rgba(240,233,218,.03); }

/* ═══════════════════════════════════════════════════════════════════════════
   ANIMATED SIDEBAR — vanilla port of beui.dev/components/motion/animated-sidebar
   ---------------------------------------------------------------------------
   The React original drives everything with Framer springs. There is no spring
   primitive in CSS, so the two springs it actually uses are sampled into
   linear() curves below. Both are overdamped (zeta > 1), which is the point:
   the rail settles against a hard zero-width boundary and must not overshoot,
   pause, then snap back on the final frame.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  /* SIDEBAR_MORPH_TRANSITION — spring(380, 35, 0.75), zeta 1.037 */
  --spring-morph: linear(0.0000, 0.0219, 0.0753, 0.1463, 0.2255, 0.3064, 0.3853, 0.4596, 0.5281, 0.5901, 0.6456, 0.6947, 0.7378, 0.7755, 0.8081, 0.8364, 0.8607, 0.8816, 0.8995, 0.9148, 0.9278, 0.9389, 0.9483, 0.9563, 0.9631, 0.9688, 0.9737, 0.9778, 0.9813, 0.9842, 0.9867, 0.9888, 0.9905, 0.9920, 0.9933, 0.9943, 0.9952, 0.9960, 0.9966, 0.9972, 0.9976, 0.9980, 0.9983, 0.9986, 0.9988, 0.9990, 0.9991, 1.0000);
  --spring-morph-dur: 472ms;

  /* SPRING_LAYOUT — spring(360, 32, 0.6), zeta 1.089. Drives the gliding pill. */
  --spring-layout: linear(0.0000, 0.0265, 0.0890, 0.1694, 0.2561, 0.3424, 0.4242, 0.4994, 0.5671, 0.6273, 0.6802, 0.7263, 0.7662, 0.8006, 0.8302, 0.8555, 0.8772, 0.8957, 0.9114, 0.9248, 0.9362, 0.9459, 0.9541, 0.9611, 0.9670, 0.9720, 0.9763, 0.9799, 0.9830, 0.9856, 0.9878, 0.9896, 0.9912, 0.9926, 0.9937, 0.9947, 0.9955, 0.9962, 0.9968, 0.9972, 0.9977, 0.9980, 0.9983, 0.9986, 0.9988, 0.9990, 0.9991, 1.0000);
  --spring-layout-dur: 483ms;

  /* EASE_OUT from lib/ease.ts — "defaults like ease-out feel weak" */
  --ease-out-strong: cubic-bezier(.16, 1, .3, 1);
}

/* --- the rail itself ----------------------------------------------------- */
.sidebar {
  position: relative;
  isolation: isolate;
  width: 66px;                 /* 10 + 46 + 10, border-box */
  transition: width var(--spring-morph-dur) var(--spring-morph);
  will-change: width;
}
.shell.rail .sidebar { width: 202px; }   /* 12 + 178 + 12 */

.side-btn {
  position: relative;
  z-index: 1;
  transition:
    width      var(--spring-morph-dur) var(--spring-morph),
    gap        var(--spring-morph-dur) var(--spring-morph),
    background .15s ease,
    color      .15s ease;
}

/* --- labels: LABEL_ENTER (200ms, 80ms delay) / LABEL_EXIT (120ms) --------- */
.side-label {
  transform: translateX(-6px);
  filter: blur(3px);
  transition:
    opacity   .12s var(--ease-out-strong),
    filter    .12s var(--ease-out-strong),
    transform .12s var(--ease-out-strong),
    max-width var(--spring-morph-dur) var(--spring-morph);
}
.shell.rail .side-label {
  max-width: 140px;
  transform: translateX(0);
  filter: blur(0);
  transition:
    opacity   .2s var(--ease-out-strong) .08s,
    filter    .2s var(--ease-out-strong) .08s,
    transform .2s var(--ease-out-strong) .08s,
    max-width var(--spring-morph-dur) var(--spring-morph);
}

/* --- SharedLayoutBg: one pill, glides between rows ------------------------
   The original renders a single motion.div with a shared layoutId, so moving
   between items morphs one element rather than cross-fading two. Same idea
   here: one absolutely-positioned node the JS re-boxes on hover.            */
.side-pill {
  position: absolute;
  left: 0; top: 0;
  width: 0; height: 0;
  border-radius: var(--r-sm);
  background: var(--hover-light);
  pointer-events: none;
  opacity: 0;
  z-index: 0;
  transition:
    transform var(--spring-layout-dur) var(--spring-layout),
    width     var(--spring-layout-dur) var(--spring-layout),
    height    var(--spring-layout-dur) var(--spring-layout),
    opacity   .18s var(--ease-out-strong),
    filter    .18s var(--ease-out-strong);
}
.side-pill.on { opacity: 1; filter: blur(0); }
/* mount + unmount both come through blur, never a hard cut */
.side-pill:not(.on) { filter: blur(6px); }
/* first appearance jumps to the hovered row instead of flying in from 0,0 */
.side-pill.no-glide { transition: opacity .18s var(--ease-out-strong), filter .18s var(--ease-out-strong); }

/* the pill replaces the old per-button hover fill, or they stack and muddy */
.side-btn.plain:hover { background: transparent; }

/* --- the rail edge: hover strip that toggles, as in AnimatedSidebarRail --- */
.side-rail {
  position: absolute;
  inset-block: 0;
  right: -7px;
  width: 14px;
  z-index: 2;
  cursor: w-resize;
  background: transparent;
  border: 0;
}
.shell:not(.rail) .side-rail { cursor: e-resize; }
.side-rail::after {
  content: "";
  position: absolute;
  inset-block: 12px;
  left: 50%;
  width: 2px;
  border-radius: 2px;
  background: transparent;
  transition: background .18s var(--ease-out-strong);
}
.side-rail:hover::after { background: var(--accent); }
.side-rail:focus-visible::after { background: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .sidebar, .side-pill, .side-label { transition-duration: .16s !important; }
  .side-pill { filter: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   CYLINDER CAROUSEL — vanilla port of beui.dev/components/motion/cylinder-carousel
   Replaces the paginated filmstrip on Inspection: the whole flight rides one
   convex wall, centre frame biggest and raised, edges shrinking away.
   ═══════════════════════════════════════════════════════════════════════════ */
.cyl-stage {
  /* The wall sorts its cards with z-index 970-1000 to fake depth. Without a
     stacking context of its own those numbers compete at page level, and the
     filmstrip paints straight over the full-screen workbench (z 150) and the
     annotation popover (z 200). Contain them. */
  isolation: isolate;
  position: relative;
  width: 100%;
  margin-top: 6px;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  user-select: none;
  border-radius: var(--r-lg);
}
.cyl-stage.dragging { cursor: grabbing; }

.cyl-ball {
  position: absolute;
  top: 50%; left: 50%;
  width: var(--cyl-size, 200px);
  height: var(--cyl-size, 200px);
  margin-left: calc(var(--cyl-size, 200px) / -2);
  margin-top:  calc(var(--cyl-size, 200px) / -2);
  will-change: transform;
}

/* the card that rides on the wall */
.cyl-card {
  position: relative;
  width: 100%; height: 100%;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--bg-dropdown);
  border: 1.5px solid currentColor;
  display: flex;
  flex-direction: column;
  transition: box-shadow .22s var(--ease-out-strong),
              border-color .22s var(--ease-out-strong);
}
.cyl-ball.centre .cyl-card {
  box-shadow: 0 10px 34px rgba(0, 0, 0, .46);
}

.cyl-thumb { position: relative; flex: 1; min-height: 0; }
.cyl-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.cyl-thumb svg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}

.cyl-name {
  flex: 0 0 auto;
  padding: 5px 8px;
  font-size: 11px;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: var(--bg-secondary);
  color: inherit;
}
.cyl-ball.centre .cyl-name { background: currentColor; color: var(--bg-primary); }

.cyl-top {
  position: absolute;
  top: 6px; left: 6px; right: 6px;
  display: flex; align-items: center; gap: 4px;
  z-index: 2;
}
.cyl-top .film-badge { background: rgba(0, 0, 0, .55); }
.cyl-top input[type="checkbox"] { margin-left: auto; }

/* off-centre frames stay legible but recede */
.cyl-ball:not(.centre) .cyl-card { opacity: .82; }

.cyl-empty {
  padding: 30px 0;
  text-align: center;
  color: var(--muted, #8a7f72);
  font-size: 13px;
}

/* the roll position readout, in place of the old pager */
.cyl-count {
  display: flex; align-items: center; justify-content: center;
  gap: 10px;
  padding: 8px 0 2px;
  font-size: 12px;
  color: var(--muted, #8a7f72);
  letter-spacing: .04em;
}
.cyl-count b { color: var(--text-light); font-weight: 500; }

@media (prefers-reduced-motion: reduce) {
  .cyl-card { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   BEUI PORTS — number-ticker · chromatic-text-reveal · text-shimmer
                agent-progress · tabs (segment) · select
   ═══════════════════════════════════════════════════════════════════════════ */

/* --chromatic-sweep must be a typed custom property or it cannot tween: an
   unregistered var flips from start to end with no interpolation. */
@property --chromatic-sweep {
  syntax: "<percentage>";
  inherits: false;
  initial-value: -14%;
}

:root {
  /* Tabs indicator — spring(170, 24, 1.2), zeta 0.84, deliberate overshoot */
  --spring-tab: linear(0.0000, 0.0179, 0.0640, 0.1286, 0.2044, 0.2854, 0.3676, 0.4477, 0.5237, 0.5941, 0.6581, 0.7155, 0.7661, 0.8102, 0.8482, 0.8804, 0.9075, 0.9300, 0.9484, 0.9634, 0.9753, 0.9846, 0.9918, 0.9972, 1.0012, 1.0040, 1.0058, 1.0070, 1.0075, 1.0077, 1.0075, 1.0072, 1.0067, 1.0061, 1.0055, 1.0048, 1.0042, 1.0036, 1.0031, 1.0026, 1.0022, 1.0018, 1.0014, 1.0000);
  --spring-tab-dur: 723ms;

  /* Select chevron — duration .4 / bounce .3 */
  --spring-chev: linear(0.0000, 0.0245, 0.0876, 0.1755, 0.2772, 0.3841, 0.4898, 0.5897, 0.6808, 0.7612, 0.8303, 0.8880, 0.9348, 0.9715, 0.9993, 1.0193, 1.0329, 1.0411, 1.0451, 1.0459, 1.0444, 1.0412, 1.0370, 1.0322, 1.0273, 1.0225, 1.0179, 1.0138, 1.0103, 1.0072, 1.0047, 1.0026, 1.0010, 0.9998, 0.9990, 0.9984, 0.9981, 0.9979, 0.9979, 0.9980, 0.9981, 0.9983, 0.9986, 1.0000);
  --spring-chev-dur: 641ms;

  /* Select panel height — duration .42 / bounce .14 */
  --spring-panel: linear(0.0000, 0.0180, 0.0640, 0.1284, 0.2036, 0.2839, 0.3651, 0.4441, 0.5190, 0.5883, 0.6513, 0.7078, 0.7578, 0.8013, 0.8389, 0.8710, 0.8981, 0.9208, 0.9395, 0.9548, 0.9672, 0.9771, 0.9848, 0.9909, 0.9954, 0.9988, 1.0013, 1.0029, 1.0040, 1.0047, 1.0050, 1.0050, 1.0049, 1.0046, 1.0043, 1.0039, 1.0035, 1.0031, 1.0027, 1.0023, 1.0020, 1.0017, 1.0014, 1.0000);
  --spring-panel-dur: 577ms;
}

/* ------------------------------------------------------- number-ticker --- */
.nt { display: inline-flex; align-items: center; font-variant-numeric: tabular-nums; }
.nt-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.nt-glyphs { display: inline-flex; align-items: center; }
.nt-fix { display: inline-block; }
.nt-digit {
  position: relative;
  display: inline-block;
  overflow: hidden;
  height: 1.1em;
  width: 1ch;
}
.nt-col {
  position: absolute;
  inset-inline: 0;
  top: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  will-change: transform, filter;
}
.nt-n { height: 1.1em; display: flex; align-items: center; justify-content: center; line-height: 1; }

/* ----------------------------------------------- chromatic-text-reveal --- */
.ctr { display: inline-flex; align-items: baseline; }
.ctr-prefix { white-space: nowrap; }
.ctr-slot { position: relative; display: inline-grid; }
.ctr-size { visibility: hidden; grid-area: 1 / 1; white-space: nowrap; }
.ctr-word {
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  white-space: nowrap;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  contain: paint;          /* bound the sweep's repaint to this word */
}

/* ---------------------------------------------------------- text-shimmer - */
@keyframes beui-text-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
.tsh {
  background-image: linear-gradient(110deg,
    var(--text-dim) 30%, var(--text-light) 50%, var(--text-dim) 70%);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation-name: beui-text-shimmer;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

/* -------------------------------------------------------- agent-progress - */
@keyframes beui-agp-breathe {
  0%, 100% { opacity: .28; transform: scale(.72); }
  50%      { opacity: 1;   transform: scale(1); }
}
.agp {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  color: var(--text-muted);
}
.agp-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  width: 20px; height: 20px;
  flex: 0 0 auto;
}
.agp-cell {
  border-radius: 1px;
  background: currentColor;
  animation: beui-agp-breathe 1.55s var(--ease-in-out-strong, cubic-bezier(.77, 0, .175, 1)) infinite;
}
.agp-label { font-family: var(--font-body); font-weight: 500; color: var(--text-primary); }
.agp-time { font-variant-numeric: tabular-nums; opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  .tsh { animation: none !important; }
  .agp-cell { animation-duration: 2.4s; }
  .nt-col { transition: none !important; }
}

/* agent-progress sits in the reasoning pill, so it inherits the pill's mono
   scale rather than the component default */
.rv-pill .agp { font-size: 11px; gap: 8px; color: rgba(240, 233, 218, .62); }
.rv-pill .agp-grid { width: 14px; height: 14px; gap: 1.5px; }
.rv-pill .agp-label {
  font-family: var(--font-mono);
  font-weight: 400;
  letter-spacing: .06em;
  color: rgba(240, 233, 218, .88);
}
.rv-pill .agp-time { color: var(--accent); opacity: 1; }
.rv-sep { width: 1px; height: 11px; background: rgba(240, 233, 218, .18); }

/* ------------------------------------------------- beui tabs (segment) ---
   Upstream's `segment` variant: a tray with no gaps and ONE indicator that
   glides between triggers via a shared layoutId. The 2px rule is retired -
   the indicator is now the full trigger box, and the active label flips to
   the ink that reads on it. Spring is 170/24/1.2, which overshoots ~0.8%
   on purpose so it settles with life instead of snapping.                 */
.tabs {
  position: relative;
  padding: 4px;
  border-radius: var(--r-md);
  background: var(--bg-dropdown);
  border: 1px solid var(--border-light);
  gap: 0;
}
.tab {
  position: relative;
  z-index: 1;
  border-radius: calc(var(--r-md) - 4px);
  background: transparent;
  transition: color .18s var(--ease-out-strong);
}
/* the indicator paints the active state now, not the button */
.tab.active { background: transparent; color: var(--bg-primary); }
.tab:not(.active):hover { color: var(--text-light); background: transparent; }

.tab-ink {
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  height: auto;
  width: 0;
  border-radius: calc(var(--r-md) - 4px);
  background: var(--accent);
  opacity: 0;
  pointer-events: none;
  z-index: 0;
  transition:
    transform var(--spring-tab-dur) var(--spring-tab),
    width     var(--spring-tab-dur) var(--spring-tab),
    opacity   .3s var(--ease-out-strong);
}

/* upstream TabsContent: opacity 0 -> 1 with a 4px rise, 0.18s ease-out.
   The brief step-back is kept so the swap is never a hard cut. */
.tab-out { animation: tab-out .1s var(--ease-out-strong) forwards; }
.tab-in  { animation: beui-tab-in .18s var(--ease-out-strong) both; }
@keyframes beui-tab-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .tab-ink { transition: opacity .2s linear; }
  .tab-in, .tab-out { animation: none; }
}

/* ------------------------------------------------------- beui select -----
   The gooey trigger is the whole idea: the edge facing the panel snaps flat
   while the two are attached, then rounds back as they pull apart. Upstream
   keyframes the radius (open 0->0->12 over .6s at times 0/.4/1, close
   12->0->12 over .42s at 0/.5/1); the same keyframes are below. Panel height
   springs with bounce .14, the chevron with bounce .3, and items stagger in
   through blur.                                                            */
@keyframes beui-sel-open {
  0%   { border-bottom-left-radius: 0;             border-bottom-right-radius: 0; }
  40%  { border-bottom-left-radius: 0;             border-bottom-right-radius: 0; }
  100% { border-bottom-left-radius: var(--r-md);   border-bottom-right-radius: var(--r-md); }
}
@keyframes beui-sel-close {
  0%   { border-bottom-left-radius: var(--r-md);   border-bottom-right-radius: var(--r-md); }
  50%  { border-bottom-left-radius: 0;             border-bottom-right-radius: 0; }
  100% { border-bottom-left-radius: var(--r-md);   border-bottom-right-radius: var(--r-md); }
}
.kind-trigger.open  { animation: beui-sel-open  .6s  var(--ease-out-strong) both; }
.kind-trigger.shut  { animation: beui-sel-close .42s var(--ease-out-strong) both; }

.kind-chev {
  transition: transform var(--spring-chev-dur) var(--spring-chev);
}
.kind-trigger.open .kind-chev { transform: rotate(180deg); }

/* the panel unfolds instead of appearing */
.kind-menu {
  display: block;
  overflow: hidden;
  height: 0;
  opacity: 0;
  visibility: hidden;
  animation: none;
  transition:
    height     var(--spring-panel-dur) var(--spring-panel),
    opacity    .16s var(--ease-out-strong) .12s,
    visibility 0s linear var(--spring-panel-dur);
}
.kind-pick.open .kind-menu {
  opacity: 1;
  visibility: visible;
  transition:
    height     var(--spring-panel-dur) var(--spring-panel),
    opacity    .18s var(--ease-out-strong),
    visibility 0s linear 0s;
}

/* staggerChildren .035 after delayChildren .05, each arriving through blur */
.kind-menu button {
  opacity: 0;
  transform: translateY(-6px);
  filter: blur(3px);
  transition:
    opacity   .2s var(--ease-out-strong),
    transform .2s var(--ease-out-strong),
    filter    .2s var(--ease-out-strong),
    background-color .22s var(--ease),
    color .22s var(--ease);
}
.kind-pick.open .kind-menu button {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  transition-delay: calc(.05s + var(--i, 0) * .035s);
}

@media (prefers-reduced-motion: reduce) {
  .kind-trigger.open, .kind-trigger.shut { animation: none; }
  .kind-menu, .kind-menu button { transition-duration: .12s; }
  .kind-menu button { filter: none; transform: none; }
}

/* --- picker sizing: it is one control in a row of buttons and must match ---
   The wrapper stretched to the row height but the trigger inside did not, so
   the pill floated ~7px short of every sibling. */
.kind-pick { align-self: stretch; display: flex; }
.kind-trigger { height: 100%; }
.kind-menu { z-index: 60; }

/* the UA focus ring is a blue lozenge that belongs to no design system */
.btn:focus-visible,
.kind-trigger:focus-visible,
.tab:focus-visible,
.side-btn:focus-visible,
.round-btn:focus-visible,
.lbl-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-md);
}
.tab:focus-visible { border-radius: calc(var(--r-md) - 4px); }

/* --- tab labels must read dead-centre in the segment pill ----------------
   Two things were pulling them off. First, `flex: 1` floors each tab at its
   own min-content width, so "Inspection" claimed more room than "Audit" and
   the tabs were never equal. Second, the accent dot sits absolutely at
   left:12px INSIDE the pill, so the label looked shoved right on short
   labels and crowded on long ones. In the segment design the ember fill is
   already the active marker, so the dot is redundant there - it stays on
   hover, where it marks "pointed at" without competing with a filled pill. */
.tabs .tab {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* the 28px left padding was reserved for the dot, and it was pushing every
     label exactly 14px right of centre. The dot is absolute and the tabs are
     ~179px wide, so it needs no reserved column at all. */
  padding: 14px;
}
/* the accent dot is gone from the tab bar entirely - the segment pill is the
   active marker, and the dot only ever competed with the label */
.tabs .tab::after { content: none; }

/* --- Cancel Process: sits on the inline progress line, which is the only
   handle on a run that has been sent to the background --------------------- */
.progress-line .proc-cancel {
  flex: 0 0 auto;
  margin-left: 4px;
  padding: 7px 13px;
  font-size: 13px;
  gap: 7px;
}
.progress-line .proc-cancel svg { width: 14px; height: 14px; }
.progress-line .proc-cancel:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--text-light);
}
.progress-line .proc-cancel:disabled { opacity: .55; cursor: default; }

/* --- tab hover: no layout on hover, ever --------------------------------
   `.tab:hover:not(.active) { padding-left: 31px }` slid the label sideways on
   every hover. It was a 3px nudge when the dot reserved 28px on the left; once
   the padding was balanced to 14px it became a 17px jump, and padding is not
   in the transition list, so it landed in one frame. Hover is a colour change
   and a soft ground now - nothing that moves a glyph. */
.tabs .tab:hover:not(.active) { padding: 14px; }
.tabs .tab {
  transition:
    color            .2s var(--ease-out-strong),
    background-color .2s var(--ease-out-strong);
}
.tabs .tab:not(.active):hover {
  color: var(--text-light);
  background-color: var(--hover-light);
}
.tabs .tab:not(.active):active { background-color: rgba(240, 233, 218, .07); }

@media (prefers-reduced-motion: reduce) {
  .tabs .tab { transition-duration: .01ms; }
}

/* --- reasoning field: the beui `dither` loader, in ember -----------------
   One shared pulse on every cell; only the delay differs, and the delays come
   from the Bayer matrix. The cells stay ember rather than the component's
   `bg-current` grey - at the bottom of the pulse the ember over the dark
   ground still reads as a warm block, so the lattice never disappears. */
@keyframes beui-dither {
  0%, 100% { opacity: .14; }
  50%      { opacity: 1; }
}
.rv-field[data-variant="dither"] i {
  background: var(--accent);
  border-radius: var(--r-sm);
  box-shadow: none;
  animation: beui-dither var(--dither-speed, 1600ms)
             var(--ease-in-out-strong, cubic-bezier(.77, 0, .175, 1)) infinite both;
}
@media (prefers-reduced-motion: reduce) {
  .rv-field[data-variant="dither"] i { animation: none; opacity: .5; }
}

/* --- the readout can never run past the plate ---------------------------
   Belt and braces on top of the shortened lines: the pill is capped to the
   plate and the verb truncates, so an unusually narrow module (or a longer
   line added later) degrades to an ellipsis instead of spilling off the edge. */
.rv-pill { max-width: calc(100% - 28px); }
.rv-pill .agp { min-width: 0; }
.rv-id, .rv-sep, .rv-pill .agp-grid, .rv-pill .agp-time { flex: 0 0 auto; }
.rv-pill .agp-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* smaller cells want a tighter lattice, or the gaps eat the pattern */
.rv-field[data-variant="dither"] { gap: 4px; padding: 5px; }
.rv-field[data-variant="dither"] i { border-radius: 5px; }

/* --- the four corner cells trace the plate's curve -----------------------
   The plate clips to --r-lg, so a square cell in a corner had its corner
   shaved off by the curve and read as misaligned. Each corner cell now
   carries the plate's radius less the field's own padding on its OUTER
   corner, so the lattice ends flush with the frame instead of being cut. */
.rv-field[data-variant="dither"] { --dither-corner: calc(var(--r-lg) - 5px); }
.rv-field[data-variant="dither"] i.c-tl { border-top-left-radius:     var(--dither-corner); }
.rv-field[data-variant="dither"] i.c-tr { border-top-right-radius:    var(--dither-corner); }
.rv-field[data-variant="dither"] i.c-bl { border-bottom-left-radius:  var(--dither-corner); }
.rv-field[data-variant="dither"] i.c-br { border-bottom-right-radius: var(--dither-corner); }

/* --- no left-edge marker bars -------------------------------------------
   Selected rows used to grow a 2-3px ember stripe on their leading edge.
   The tinted ground already says "this one", and the stripe read as a
   stray highlight hanging off the side of the row. */
.filter-pop button.on,
.audit-track.cur,
.audit-track.merge-src,
.loc-row.sel {
  box-shadow: none;
}

/* --- house select: the same skin on every dropdown -----------------------
   The native <select> stays in the DOM (all the app's change handlers hang
   off it) but is taken out of sight and out of the hit area; the trigger and
   menu below are what the reviewer sees. Motion matches the image picker:
   the panel springs open on a measured height, the chevron flips on a bouncy
   spring, and the rows arrive staggered through blur.                       */
.hsel { position: relative; display: inline-block; vertical-align: middle; }
.hsel.is-block { display: block; width: 100%; }

.hsel-native {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
  clip-path: inset(50%);
}

.hsel-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%;
  padding: 9px 12px;
  background: var(--bg-dropdown);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 14px;
  text-align: left;
  transition: border-color .22s var(--ease-out-strong),
              color .22s var(--ease-out-strong);
}
.hsel-trigger:hover, .hsel.open .hsel-trigger {
  border-color: var(--accent);
  color: var(--text-light);
}
.hsel-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hsel-chev {
  width: 15px; height: 15px; flex: 0 0 auto;
  color: var(--text-dim);
  transition: transform var(--spring-chev-dur) var(--spring-chev);
}
.hsel.open .hsel-chev { transform: rotate(180deg); }

/* gooey: the edge facing the panel flattens while they are attached */
.hsel.open  .hsel-trigger { animation: beui-sel-open  .6s  var(--ease-out-strong) both; }
.hsel.shut  .hsel-trigger { animation: beui-sel-close .42s var(--ease-out-strong) both; }

.hsel-menu {
  position: fixed;              /* JS places it - see the note in beui.js */
  z-index: 400;
  overflow: hidden;
  height: 0;
  opacity: 0;
  visibility: hidden;
  max-height: 320px;
  overflow-y: auto;
  background: var(--bg-dropdown);
  background-image: var(--weave);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 5px;
  box-shadow: 0 22px 44px -18px rgba(0, 0, 0, .9);
  transition:
    height     var(--spring-panel-dur) var(--spring-panel),
    opacity    .16s var(--ease-out-strong) .12s,
    visibility 0s linear var(--spring-panel-dur);
}
.hsel.open .hsel-menu {
  opacity: 1;
  visibility: visible;
  transition:
    height     var(--spring-panel-dur) var(--spring-panel),
    opacity    .18s var(--ease-out-strong),
    visibility 0s linear 0s;
}

.hsel-opt {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  width: 100%;
  padding: 9px 11px;
  border-radius: var(--r-sm);
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 13.5px;
  text-align: left;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-6px);
  filter: blur(3px);
  transition:
    opacity   .2s var(--ease-out-strong),
    transform .2s var(--ease-out-strong),
    filter    .2s var(--ease-out-strong),
    background-color .2s var(--ease),
    color .2s var(--ease);
}
.hsel.open .hsel-opt {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  transition-delay: calc(.05s + var(--i, 0) * .035s);
}
.hsel-opt:hover:not(:disabled) { background: var(--hover-light); color: var(--text-light); }
.hsel-opt:disabled { opacity: .4; cursor: default; }
.hsel-opt.on { color: var(--text-light); background: rgba(197, 110, 73, .12); }

/* the tick is the selected marker - no left-edge stripe */
.hsel-tick { width: 14px; height: 14px; flex: 0 0 auto; opacity: 0; }
.hsel-opt.on .hsel-tick {
  opacity: 1;
  background: var(--accent);
  clip-path: polygon(14% 47%, 5% 57%, 39% 90%, 95% 22%, 85% 14%, 38% 71%);
}

@media (prefers-reduced-motion: reduce) {
  .hsel-menu, .hsel-opt { transition-duration: .12s; }
  .hsel-opt { filter: none; transform: none; }
  .hsel.open .hsel-trigger, .hsel.shut .hsel-trigger { animation: none; }
}

/* --- the flat edge HOLDS while the panel is attached ---------------------
   The keyframed version flattened the edge, then rounded it back at ~40%
   while the menu was still open, so the two blobs re-separated on screen
   with the panel still hanging off. A transition on `.open` instead: the
   edge facing the panel goes flat when it opens, stays flat for exactly as
   long as the panel is up, and eases back when it closes. */
.kind-trigger.open, .kind-trigger.shut,
.hsel.open .hsel-trigger, .hsel.shut .hsel-trigger { animation: none; }

.kind-trigger, .hsel-trigger {
  transition:
    border-color            .22s var(--ease-out-strong),
    color                   .22s var(--ease-out-strong),
    background-color        .22s var(--ease-out-strong),
    border-top-left-radius     .3s var(--ease-out-strong),
    border-top-right-radius    .3s var(--ease-out-strong),
    border-bottom-left-radius  .3s var(--ease-out-strong),
    border-bottom-right-radius .3s var(--ease-out-strong);
}

/* panel below: the bottom edge is the one facing it */
.kind-pick.open .kind-trigger,
.hsel.open:not(.up) .hsel-trigger {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
/* panel flipped above: flatten the top edge instead */
.hsel.open.up .hsel-trigger {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

@media (prefers-reduced-motion: reduce) {
  .kind-trigger, .hsel-trigger { transition-duration: .01ms; }
}

/* --- same flat-edge rule for the remaining dropdowns ---------------------
   The Reports pickers and the annotation label/severity fields are the other
   trigger-plus-panel pairs in the app. The annotation ones only toggle
   `hidden` on the panel and carry no state class, so `:has()` reads the
   panel's own state instead of asking the JS to announce it. */
.sel-btn,
.ann-field > input,
.ann-field > .ann-sev-btn {
  transition:
    border-color               .22s var(--ease-out-strong),
    background-color           .22s var(--ease-out-strong),
    border-bottom-left-radius  .3s  var(--ease-out-strong),
    border-bottom-right-radius .3s  var(--ease-out-strong);
}

.sel.open .sel-btn,
.ann-field:has(> .ann-menu:not([hidden])) > input,
.ann-field:has(> .ann-menu:not([hidden])) > .ann-sev-btn {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

@media (prefers-reduced-motion: reduce) {
  .sel-btn, .ann-field > input, .ann-field > .ann-sev-btn { transition-duration: .01ms; }
}

/* --- modals arrive, they do not appear -----------------------------------
   These were toggled with [hidden], so they cut in on one frame with a flat
   grey wash behind them. A CSS animation (not a transition) is what works
   here: display goes none -> grid, and only an animation runs across that.
   No JS change needed - the rule keys off the attribute the app already sets.

   The veil is warm-black and blurred rather than grey at 45%: the panel then
   reads as sitting above the app instead of on top of a dimmed screenshot. */
@keyframes serene-veil {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes serene-rise {
  from { opacity: 0; transform: translateY(16px) scale(.982); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-backdrop {
  background: rgba(10, 8, 6, .58);
  backdrop-filter: blur(7px) saturate(.9);
  -webkit-backdrop-filter: blur(7px) saturate(.9);
}
.modal-backdrop:not([hidden]) {
  animation: serene-veil .38s var(--ease-out-strong) both;
}
/* the panel follows the veil by a beat, so the two are not one hard event */
.modal-backdrop:not([hidden]) > * {
  animation: serene-rise .56s var(--ease-out-strong) .07s both;
}

@media (prefers-reduced-motion: reduce) {
  .modal-backdrop:not([hidden]),
  .modal-backdrop:not([hidden]) > * { animation-duration: .12s; animation-delay: 0s; }
  .modal-backdrop:not([hidden]) > * { animation-name: serene-veil; }
}

/* --- calmer tabs ---------------------------------------------------------
   Hover was a 200ms snap and the panel swap was beui's 180ms rise, which
   next to a 723ms indicator glide read as two different tempos in one
   control. Both are slowed and softened so the whole tab bar moves at one
   unhurried pace. The hover plate is inset a hair and shares the pill's
   radius, so it reads as the pill's shadow arriving rather than a grey box. */
.tabs .tab {
  transition:
    color            .34s var(--ease-out-strong),
    background-color .34s var(--ease-out-strong);
}
.tabs .tab:not(.active):hover {
  background-color: var(--hover-light);
  background-clip: padding-box;
}

/* the incoming panel drifts up rather than popping */
.tab-out { animation: tab-out .16s var(--ease-out-strong) forwards; }
.tab-in  { animation: beui-tab-in .42s var(--ease-out-strong) both; }
@keyframes beui-tab-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes tab-out {
  to { opacity: 0; transform: translateY(-4px); }
}

@media (prefers-reduced-motion: reduce) {
  .tabs .tab { transition-duration: .01ms; }
  .tab-in, .tab-out { animation: none; }
}

/* --- the ember chip sits in a dark frame ---------------------------------
   Same read as the reasoning card: there, the dark ground shows through the
   gaps and gives every ember square its own outline. Doing it with an INSET
   shadow rather than a border means zero layout change - no button resizes,
   no toolbar reflows, and it follows each element's own border-radius. */
:root { --ember-edge: #141110; }

.tab-ink,
.side-btn.side-toggle, .side-btn.active,
.wb-tool.on, .at-merge.on,
.filter-pop button.on,
.btn.dark[data-cta], [data-cta],
#btn-process-all, #btn-to-audit, #btn-modseg, #btn-run-results,
#btn-import-commit, #btn-add-save, #btn-geo-apply, #wb-align-lock,
#report-ok, #btn-loc-csv {
  box-shadow: inset 0 0 0 2px var(--ember-edge);
}

/* small ember chips get a proportionally thinner frame */
.st-icon.done, .lbl-toggle.on i {
  box-shadow: inset 0 0 0 1.5px var(--ember-edge);
}

/* focus still wins over the frame */
.btn:focus-visible, .side-btn:focus-visible, .tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* the remaining ember surfaces, found by scanning for the accent fill:
   the report CTA, the track number chips, and the toggle knobs - the knobs
   need !important only because the switch rebuild zeroes their shadow with
   one, and same-importance ties fall back to specificity, which this wins. */
#btn-download-reports { box-shadow: inset 0 0 0 2px var(--ember-edge); }
.at-chip { box-shadow: inset 0 0 0 1.5px var(--ember-edge); }
.lbl-toggle.on i { box-shadow: inset 0 0 0 1.5px var(--ember-edge) !important; }

/* --- nav text must never re-rasterise ------------------------------------
   Measured: nothing in the tab bar moves on hover - padding, font-size,
   weight and letter-spacing are all identical hovered and not. So the wobble
   was never layout, it was the glyphs being REPAINTED.

   Two causes, both fixed here:

   1. Subpixel (LCD) antialiasing computes coverage from the colour BEHIND
      the text. Fading a hover fill in, or gliding the ember pill under a
      label, changes that backdrop every frame, so the glyph edges are
      re-resolved every frame and the word appears to shimmer. Grayscale
      antialiasing does not consult the backdrop, so the glyphs stay
      bit-identical throughout.

   2. Animating `background-color` on the tab itself is a PAINT property, so
      the browser repaints the whole element - text included - on every frame
      of the transition. Moving the fill onto a pseudo-element and animating
      its `opacity` instead is composited on the GPU: the text is rasterised
      once and never touched again. */
.tabs .tab {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  position: relative;
  isolation: isolate;
}
.tabs .tab::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--hover-light);
  opacity: 0;
  pointer-events: none;
  transition: opacity .34s var(--ease-out-strong);
}
.tabs .tab:not(.active):hover::before { opacity: 1; }

/* the pseudo owns the hover fill now - the element itself stays flat, so no
   paint-property transition remains to drag the text through a repaint */
.tabs .tab,
.tabs .tab:not(.active):hover,
.tabs .tab:not(.active):active {
  background-color: transparent;
}
.tabs .tab { transition: color .34s var(--ease-out-strong); }

@media (prefers-reduced-motion: reduce) {
  .tabs .tab::before { transition-duration: .01ms; }
}

/* --- reserve the scrollbar gutter ----------------------------------------
   Measured across a full sweep of the tab bar: every label jumped by up to
   9.29px, and the jump TOGGLED - present on Structures, Audit, Location and
   Reports, absent on Images, Inspection and Overview. That pattern is the
   page scrollbar: tabs whose content is tall enough to scroll take ~10px off
   the layout width, the tray narrows, and seven `flex: 1 1 0` tabs each
   shrink by ~1.4px, so every label slides. Reserving the gutter permanently
   means the width never changes and nothing moves on a tab switch. */
html { scrollbar-gutter: stable; }

/* --- the scrollbar is always there ---------------------------------------
   `scrollbar-gutter: stable` alone stopped the LAYOUT moving - measured, the
   sidebar, panel, tray and tab bodies now sit at identical coordinates on
   every tab. What was left is the bar itself: Structures, Images, Inspection
   and Location are tall enough to scroll, Overview, Audit and Reports are
   not, so a 10px bar materialised and vanished at the right edge as you moved
   between them. `overflow-y: scroll` keeps the track present on every tab, so
   there is nothing left to appear or disappear.

   Styled while we are here: the OS bar was the last un-themed surface in the
   app, and it applies to every scroller - tables, menus, the filmstrip. */
html {
  overflow-y: scroll;
  scrollbar-gutter: stable;
}

* {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover {
  background: #3d362e;
  background-clip: content-box;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE GATE — fluid orb (rare-ui) as the whole ground, one way in
   ═══════════════════════════════════════════════════════════════════════════ */
.gate {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: grid;
  place-items: center;
  background: var(--bg-primary);       /* painted before WebGL is up, and the
                                          fallback if the context never comes */
  overflow: hidden;
}
.gate-fluid {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
/* the fluid is loud on its own - this sits the type back on top of it */
.gate-veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 62% 52% at 50% 46%, rgba(10, 8, 6, .82) 0%,
                                                rgba(10, 8, 6, .55) 42%,
                                                rgba(10, 8, 6, 0) 78%),
    linear-gradient(to bottom, rgba(10, 8, 6, .38), rgba(10, 8, 6, .12) 45%, rgba(10, 8, 6, .55));
  pointer-events: none;
}

.gate-inner {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: 0 24px;
  animation: gate-rise .9s var(--ease-out-strong) both;
}
@keyframes gate-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.gate-kicker {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
}
.gate-title {
  font-family: var(--font-display);
  font-size: clamp(52px, 8.4vw, 116px);
  line-height: .92;
  font-weight: 500;
  letter-spacing: -.02em;
  color: var(--text-light);
  margin-bottom: 18px;
}
.gate-sub {
  font-size: clamp(14px, 1.25vw, 17px);
  color: var(--text-muted);
  margin-bottom: 42px;
}

.gate-btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 15px 30px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--pal-ink);
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 500;
  box-shadow: inset 0 0 0 2px var(--ember-edge),      /* the house frame */
              0 18px 44px -16px rgba(0, 0, 0, .8);
  transition: transform .34s var(--ease-out-strong),
              box-shadow .34s var(--ease-out-strong);
}
.gate-btn svg { width: 18px; height: 18px; transition: transform .34s var(--ease-out-strong); }
.gate-btn:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 0 0 2px var(--ember-edge),
              0 24px 56px -16px rgba(0, 0, 0, .9);
}
.gate-btn:hover svg { transform: translateX(4px); }
.gate-btn:active { transform: translateY(0); }
.gate-btn:focus-visible { outline: 2px solid var(--text-light); outline-offset: 4px; }

/* on the way out the whole gate lifts, so the dashboard is revealed rather
   than cut to - it is already rendered underneath */
.gate.leaving {
  animation: gate-leave .72s var(--ease-out-strong) forwards;
  pointer-events: none;
}
@keyframes gate-leave {
  to { opacity: 0; transform: scale(1.06); filter: blur(8px); }
}
.gate.gone { display: none; }

/* nothing behind the gate should scroll while it is up */
body.gated { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .gate-inner { animation: none; }
  .gate.leaving { animation-duration: .2s; }
  .gate-btn, .gate-btn svg { transition: none; }
}

/* the gate lost its subtext and the button its chevron - close the gap the
   subtext used to hold open, and drop the arrow's spacing and hover slide */
.gate-title { margin-bottom: 40px; }
.gate-sub { display: none; }
.gate-btn { gap: 0; }

/* --- the Comet, from beui.dev/components/motion/loader -------------------
   Six dots pinned round one ring that spins linearly; each is a touch
   smaller and fainter than the one ahead, so the trail reads as a tail
   rather than as six separate dots. Geometry is computed in app.js from
   the component's own formulas, so only the motion lives here. */
.comet { position: relative; display: block; width: 20px; height: 20px; }
.comet-ring {
  position: absolute;
  inset: 0;
  animation: comet-spin 1s linear infinite;
}
.comet-ring i {
  position: absolute;
  top: 50%;
  left: 50%;
  border-radius: 50%;
  background: currentColor;
}
@keyframes comet-spin { to { transform: rotate(360deg); } }

/* the comet IS the marker - a ring inside a ring read as mush */
.st-icon.running {
  border-color: transparent;
  background: transparent;
  width: 22px; height: 22px;
}

/* --- modal footer: one size, and an actual hierarchy ---------------------
   `.btn.ghost` carries 11px/22px at 15px while `.btn.outline` carries
   10px/16px at 14.5px, so the two footer buttons were visibly different
   sizes. Ghost is also coloured as a DISABLED control (--gray-400), which
   is why the affirmative action looked dead. Both fixed, scoped to the
   footer so nothing else that uses those classes moves. */
.modal-foot {
  gap: 10px;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--border-light);
}
.modal-foot .btn {
  padding: 11px 20px;
  font-size: 14px;
  font-weight: 500;
  border-radius: var(--r-pill);
  min-height: 42px;
  border: 1px solid var(--border);
  transition: background-color .22s var(--ease-out-strong),
              border-color .22s var(--ease-out-strong),
              color .22s var(--ease-out-strong);
}
/* the affirmative: quiet plate, bone type */
.modal-foot .btn.ghost {
  background: var(--bg-dropdown);
  color: var(--text-light);
}
.modal-foot .btn.ghost:hover { border-color: var(--accent); }
/* the destructive: recedes until you reach for it */
.modal-foot .btn.outline {
  background: transparent;
  color: var(--text-muted);
}
.modal-foot .btn.outline:hover {
  color: var(--text-light);
  border-color: var(--sev-high, #c0563d);
  background: rgba(192, 86, 61, .1);
}

/* the comet burns terracotta - the dots paint from currentColor, so this is
   set on the comet itself rather than on .st-icon, leaving the stage's own
   mint progress bar alone */
.comet { color: var(--accent); }

/* ==========================================================================
   GEMINI SECOND OPINION (Audit) — advisory, muted; the accent stays scarce
   ========================================================================== */
.at-ai {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .06em;
  color: var(--text-muted); text-transform: uppercase;
  border: 1px solid var(--border); border-radius: 4px; padding: 2px 6px;
  margin-top: 2px; width: max-content; max-width: 100%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: zoom-in;
}
.at-ai:hover { border-color: var(--text-muted); color: var(--text-primary); }
.at-ai.ok     { color: var(--pal-mint); border-color: rgba(157,201,174,.45); }
.at-ai.dim    { color: var(--text-muted); }
.at-ai.unsure { color: var(--sev-high); border-color: rgba(214,160,90,.45); }
.at-ai i { font-style: normal; font-weight: 700; }
.audit-ai-count { color: var(--text-muted); font-size: 12.5px; font-weight: 500; }
.av-ai {
  font-size: 12.5px; line-height: 1.5; color: var(--text-muted);
  border-left: 2px solid var(--border); padding: 2px 0 2px 10px;
}
.av-ai b { font-weight: 600; color: var(--text-primary); }
.av-ai b.ok { color: var(--pal-mint); }
.av-ai b.unsure { color: var(--sev-high); }
.av-ai a { color: var(--text-muted); text-decoration: underline dotted; cursor: pointer; }
.av-ai a:hover { color: var(--text-primary); }
.dc-reason {
  font-size: 11.5px; line-height: 1.45; color: var(--text-muted);
  margin: 2px 0 4px; letter-spacing: 0;
}
#btn-gemini-vet.busy { opacity: .7; cursor: progress; }

/* head-to-head: Gemini (blind) vs the reviewer's frozen verdicts */
.audit-h2h { border-top: 1px solid var(--line); flex: 0 0 auto; }
.h2h-head {
  width: 100%; display: flex; align-items: center; gap: 8px;
  padding: 10px 14px; background: none; border: 0; color: inherit;
  font: inherit; cursor: pointer; text-align: left;
}
.h2h-head:hover { background: var(--gray-100); }
.h2h-k { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .14em;
         text-transform: uppercase; color: var(--text-dim); }
.h2h-sum { flex: 1; font-size: 12.5px; color: var(--text-muted); }
.h2h-sum b { color: var(--text-primary); font-weight: 600; }
.h2h-chev { width: 14px; height: 14px; color: var(--text-dim); transition: transform .2s var(--ease); }
.audit-h2h.open .h2h-chev { transform: rotate(180deg); }
.h2h-body { padding: 0 14px 12px; display: flex; flex-direction: column; gap: 10px; max-height: 34vh; overflow-y: auto; }
.h2h-matrix { display: grid; grid-template-columns: auto 1fr 1fr; gap: 2px 8px;
              font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .06em;
              color: var(--text-dim); text-transform: uppercase; }
.h2h-matrix b { font-family: var(--font-mono); font-size: 13px; color: var(--text-primary); font-weight: 500; letter-spacing: 0; }
.h2h-matrix b.ok { color: var(--pal-mint); }
.h2h-matrix b.warn { color: var(--sev-high); }
.h2h-open { font-size: 12px; color: var(--text-muted); line-height: 1.5; }
.h2h-open b { color: var(--text-primary); font-weight: 600; }
.h2h-row {
  display: flex; align-items: baseline; gap: 8px; padding: 6px 0;
  border-top: 1px solid var(--line); font-size: 12.5px; cursor: pointer;
}
.h2h-row:hover { color: var(--text-primary); }
.h2h-row .at-chip { font-size: 11px; padding: 2px 6px; }
.h2h-row .h2h-vs { flex: 1; min-width: 0; color: var(--text-muted); }
.h2h-row .h2h-vs i { font-style: normal; color: var(--text-primary); }
.h2h-row .h2h-vs small { display: block; color: var(--text-dim); font-size: 11.5px; line-height: 1.4;
                         white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.h2h-row:hover .h2h-vs small { white-space: normal; }
.h2h-empty { font-size: 12px; color: var(--text-dim); }
