/* ============================================================================
   SoundRadar
   A field instrument you can read in daylight. The chrome is chart paper —
   light, high-contrast, printed — and the plots are dark plates inset into it,
   because a spectrogram needs a dark ground to show colour. The two halves are
   joined by one engraved frequency ruler that both plates hang off, which is
   also where markers are planted.

   Ultramarine is the instrument mark: axes, selections, the active state. Red is
   reserved for recording (the Record button, teaching a sound, clips) and
   removal, so it never appears decoratively. Green is the live state: the app
   is listening.
   ========================================================================== */

:root {
  --paper:   #e9ebef;
  --paper-2: #f4f6f8;
  --paper-3: #ffffff;
  --rule:    #ccd2da;
  --rule-2:  #a3adba;
  --ink:     #101820;
  --ink-2:   #4c5867;
  --ink-3:   #7a8794;
  --accent:  #2a3fd4;
  --accent-soft: #e0e4fb;
  --flag:    #cf2b21;
  --live:    #1d8348;
  --plate:   #0c1016;

  --shadow-plate: 0 1px 0 rgba(255,255,255,0.7), 0 2px 10px rgba(16,24,32,0.14);

  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;

  --bar-h: 3rem;
  /* The plates' guaranteed share of the screen. The reports below take what
     is left and scroll inside it, so a long marker list or detection log can
     never squeeze the waterfall away. */
  --plates-min: 55vh;

  /* Plate colours do not follow the paper: the plots stay dark in both themes,
     so a spectrogram always has the dark ground its colour maps need. */
  --plate-grid:  rgba(226, 232, 240, 0.11);
  --plate-grid-2: rgba(226, 232, 240, 0.05);
  --plate-text:  rgba(226, 232, 240, 0.7);
  --plate-trace: #8ea6ff;
  --plate-fill:  rgba(142, 166, 255, 0.2);
  --plate-peak:  rgba(142, 166, 255, 0.45);
  --plate-peak-mark: #ffb45a; /* the loudest peak: light amber */
  --plate-chip:  rgba(12, 16, 22, 0.82);
  --plate-chip-text: #e7ecf2;

  color-scheme: light;
}

/* Low light: the paper goes to slate, the plates stay exactly as they are.
   Same document, different lamp. */
:root[data-theme="dark"] {
  --paper:   #171b21;
  --paper-2: #1e232b;
  --paper-3: #262c35;
  --rule:    #333b45;
  --rule-2:  #4b5563;
  --ink:     #e7ecf2;
  --ink-2:   #a4b0bd;
  --ink-3:   #7b8794;
  --accent:  #7d90ff;
  --accent-soft: #232a4d;
  --flag:    #ff5a4d;
  --live:    #23a05a;
  --shadow-plate: inset 0 0 0 1px #000, 0 2px 10px rgba(0,0,0,0.5);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body { height: 100%; margin: 0; }

body {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.45;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

button, input, select, output, table { font: inherit; color: inherit; }

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

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* The repeated label device: engraved small caps, the way a printed chart
   names its columns. */
.tag {
  display: block;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}

/* ---------- Toolbar ---------- */

.bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
  height: var(--bar-h);
  padding: 0 0.75rem;
  background: var(--paper-2);
  border-bottom: 1px solid var(--rule);
}

/* The whole title is the stamp: name reversed out of ultramarine, the same
   block the instrument prints on its own output. */
.wordmark {
  display: inline-block;
  margin-right: 0.4rem;
  padding: 0.2rem 0.45rem;
  background: var(--accent);
  color: var(--paper-3);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}

.capture {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.1rem;
  padding: 0 0.85rem;
  background: var(--accent);
  color: #fff;
  border: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
}
.capture:hover { filter: brightness(1.08); }
.capture[disabled] { opacity: 0.6; cursor: progress; }
.capture.is-live { background: var(--live); }
.capture.is-paused { background: var(--ink-2); }
.record {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.1rem;
  padding: 0 0.85rem;
  background: var(--flag);
  color: #fff;
  border: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.record:hover { filter: brightness(1.08); }
.record[disabled] { opacity: 0.4; cursor: not-allowed; }
/* White over red: a round dot to start, a square to stop. */
.record-dot {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: #fff;
  flex-shrink: 0;
}
.record.is-recording .record-dot { border-radius: 1px; animation: blink 1.15s ease-in-out infinite; }

.capture-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  flex-shrink: 0;
}
.capture.is-live .capture-dot { background: #fff; animation: blink 1.15s ease-in-out infinite; }
@keyframes blink { 50% { opacity: 0.2; } }

.menus { display: flex; gap: 0.25rem; margin-left: auto; }

.menu { position: relative; }
.menu > summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  height: 2.1rem;
  padding: 0 0.7rem;
  border: 1px solid transparent;
  font-size: 0.8125rem;
  color: var(--ink-2);
  cursor: pointer;
  user-select: none;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary::after {
  content: "";
  width: 0; height: 0;
  border: 3px solid transparent;
  border-top-color: currentColor;
  margin-top: 3px;
}
.menu > summary:hover { color: var(--ink); background: var(--paper); }
.menu[open] > summary {
  background: var(--paper-3);
  border-color: var(--rule);
  border-bottom-color: var(--paper-3);
  color: var(--ink);
}

.menu-panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 1px);
  right: 0;
  width: 18rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 0.8rem;
  background: var(--paper-3);
  border: 1px solid var(--rule);
  box-shadow: 0 8px 24px rgba(16, 24, 32, 0.16);
}
.menu-rule { width: 100%; height: 1px; border: 0; margin: 0.2rem 0; background: var(--rule); }

/* The one menu that names its own state: which session you are working in has
   to be readable without opening anything. */
.menu-badge {
  min-width: 0;
  max-width: 8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--ink);
}
.menu[open] > summary .menu-badge { color: var(--accent); }

.icon-btn {
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid var(--rule);
  font-size: 0.9rem;
  color: var(--ink-2);
  cursor: pointer;
}
.icon-btn:hover { background: var(--paper-3); color: var(--ink); }

/* The input level sits in the spectrum plate's upper right, so it is read
   against the plate's dark ground and takes the plate's colours. */
.plate-hud {
  position: absolute;
  top: 0.35rem;
  right: 0.5rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  pointer-events: none;
}
.plate-hud .plate-tag { position: static; }
.vu-label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--plate-text);
  white-space: nowrap;
}
.vu {
  position: relative;
  width: 6rem;
  height: 0.4rem;
  background: rgba(226, 232, 240, 0.16);
  overflow: hidden;
}
.vu-fill {
  height: 100%;
  width: 0;
  background: var(--plate-trace);
  transition: width 60ms linear;
}
.vu-peak {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  left: 0;
  background: var(--plate-chip-text);
  transition: left 90ms linear;
}
.vu.clip .vu-fill { background: var(--flag); }

/* ---------- Plates and ruler ---------- */

.field-view {
  position: relative;
  flex: 1 1 auto;
  min-height: var(--plates-min);
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0.6rem 0.75rem 0.75rem;
}

.plate {
  position: relative;
  min-height: 0;
  background: var(--plate);
  box-shadow: var(--shadow-plate);
  touch-action: none;
  cursor: crosshair;
  overflow: hidden;
}
#spectrumPlot { flex: 1 1 24%; }
#waterfallPlot { flex: 1 1 76%; }

.field-view.no-waterfall #waterfallPlot { display: none; }
.field-view.no-waterfall #spectrumPlot { flex: 1 1 100%; }

.plate canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.overlay { pointer-events: none; }

.plate-tag {
  position: absolute;
  top: 0.35rem;
  right: 0.5rem;
  z-index: 2;
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.34);
  pointer-events: none;
}

/* The ruler: a machined strip of paper between the two plates. Ticks are
   engraved into it and markers are planted on it like pins. */
.ruler {
  position: relative;
  flex: 0 0 3.1rem;
  background: var(--paper-2);
  border-top: 1px solid var(--rule-2);
  border-bottom: 1px solid var(--rule-2);
  touch-action: none;
  cursor: ew-resize;
}
.ruler canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.ruler.dragging { cursor: grabbing; }

/* Zoom/pan pad: a small instrument plate that floats over the lower-right of
   the plots. Square keys, sized for a thumb, coloured for the dark ground it
   sits on rather than for the paper. */
.pad {
  position: absolute;
  z-index: 10;
  right: 1.15rem;
  bottom: 1.2rem;
  display: flex;
  align-items: stretch;
  gap: 1px;
  padding: 2px;
  background: rgba(12, 16, 22, 0.74);
  border: 1px solid rgba(226, 232, 240, 0.16);
}

.pad-btn {
  width: 1.85rem;
  height: 1.85rem;
  display: grid;
  place-items: center;
  padding: 0;
  background: rgba(226, 232, 240, 0.06);
  border: 0;
  color: var(--plate-text);
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
  touch-action: manipulation;
}
.pad-btn:hover { background: rgba(226, 232, 240, 0.16); color: #fff; }
.pad-btn:active { background: rgba(226, 232, 240, 0.26); }

.pad-sep { width: 1px; margin: 0.3rem 0.15rem; background: rgba(226, 232, 240, 0.18); }

.tooltip {
  position: absolute;
  z-index: 12;
  padding: 0.3rem 0.5rem;
  background: var(--ink);
  color: var(--paper-3);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.35;
  white-space: nowrap;
  pointer-events: none;
  transform: translate(-50%, -118%);
}
.tooltip.hidden { display: none; }
.tooltip b { color: #fff; font-weight: 700; }

.curtain {
  position: absolute;
  inset: 0;
  z-index: 14;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  text-align: center;
}
.curtain.hidden { display: none; }
.curtain-inner { max-width: 28rem; }
.curtain-title {
  margin: 0 0 0.4rem;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.curtain.is-error .curtain-title { color: var(--flag); }
.curtain-body {
  margin: 0 0 1.1rem;
  font-size: 0.9rem;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* ---------- Plates / reports splitter ---------- */

/* Straddles the boundary without taking any room of its own. */
.splitter {
  flex: 0 0 0;
  position: relative;
  z-index: 40;
  cursor: row-resize;
  touch-action: none;
}
.splitter::before { content: ""; position: absolute; left: 0; right: 0; top: -0.55rem; height: 1.1rem; }
.splitter::after {
  content: "";
  position: absolute;
  left: 50%; top: -2px;
  width: 2.75rem; height: 4px;
  transform: translateX(-50%);
  background: var(--rule-2);
  opacity: 0.7;
}
.splitter:hover::after, .splitter:focus-visible::after, .splitter.is-dragging::after {
  background: var(--accent);
  opacity: 1;
}

/* Once sized, the plates keep exactly the height chosen and the reports share
   what is left, uncapped. */
body.is-split .field-view { flex: 0 0 var(--plates-h); min-height: 0; }
body.is-split .report { flex: 1 1 0; }
body.is-split .report.collapsed { flex: 0 0 auto; }
body.is-split .report-body { max-height: none; }
/* With no report on screen there is nothing to give the room to. */
body:not(:has(.report:not([hidden]))) .splitter { display: none; }
body.is-split:not(:has(.report:not([hidden]))) .field-view { flex: 1 1 auto; }

/* ---------- Report table ---------- */

/* Reports shrink before the plates do (down to their header, which keeps
   Hide / Export / Clear in reach) and scroll their rows instead of growing. */
.report {
  flex: 0 1 auto;
  min-height: 2.25rem; /* the header; a flex item's default minimum is all its rows */
  display: flex;
  flex-direction: column;
  background: var(--paper-2);
  border-top: 1px solid var(--rule);
}
.report.collapsed .report-body { display: none; }

.report-head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.75rem;
  border-bottom: 1px solid var(--rule);
}
.report-title {
  margin: 0;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.report-count {
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.3rem;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
}
.report-spacer { flex: 1; }

.report-head { flex-shrink: 0; }
/* The cap sits on the rows, not the report: two full reports then have the
   same natural height and give up equal room when the screen runs short,
   instead of the longer list squeezing the other one down to a row or two. */
.report-body { overflow: auto; flex: 1 1 auto; min-height: 0; max-height: 38vh; }

.report-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
}
.report-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0.3rem 0.6rem;
  background: var(--paper-2);
  border-bottom: 1px solid var(--rule);
  text-align: left;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.report-table td {
  padding: 0.25rem 0.6rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}
.report-table tbody tr:hover { background: var(--paper-3); }
.num-col, .num-cell {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.col-pin { width: 2.6rem; }
.col-meter { width: 8rem; }
.col-trend { width: 6.5rem; }
.col-alarm { width: 7.6rem; white-space: nowrap; }
.col-alarm .icon-cell, .col-alarm .cell-num { vertical-align: middle; }

/* Pin badge: the same number that appears on the ruler, so the table
   row and the mark on the chart are obviously the same thing. */
.pin-badge {
  display: grid;
  place-items: center;
  width: 1.4rem;
  height: 1.4rem;
  background: var(--pin-color, var(--accent));
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
}

.cell-name {
  width: 100%;
  min-width: 5rem;
  padding: 0.2rem 0.3rem;
  background: transparent;
  border: 1px solid transparent;
  font-weight: 600;
}
.cell-name:hover { border-color: var(--rule); }
.cell-name:focus { background: var(--paper-3); border-color: var(--accent); outline: none; }

.cell-num {
  width: 5.2rem;
  padding: 0.2rem 0.3rem;
  background: transparent;
  border: 1px solid transparent;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.cell-num:hover { border-color: var(--rule); }
.cell-num:focus { background: var(--paper-3); border-color: var(--accent); outline: none; }
.cell-num::-webkit-inner-spin-button { opacity: 0.3; }

.row-meter {
  position: relative;
  height: 0.55rem;
  background: var(--rule);
  overflow: hidden;
}
.row-meter-fill {
  height: 100%;
  width: 0;
  background: var(--pin-color, var(--accent));
  transition: width 60ms linear;
}
.row-meter-peak {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  background: var(--ink);
  opacity: 0;
}
.row-trend {
  display: block;
  width: 100%;
  height: 1.6rem;
  background: var(--paper-3);
}
.row-actions { text-align: right; white-space: nowrap; }

/* ---------- Controls ---------- */

.field { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.field-row { flex-direction: row; align-items: center; gap: 0.55rem; }
.field-row .tag { width: 4.6rem; flex-shrink: 0; }

input[type="number"], select {
  flex: 1;
  min-width: 0;
  height: 2rem;
  padding: 0 0.45rem;
  background: var(--paper-3);
  border: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}
input[type="number"] { text-align: right; }
select { cursor: pointer; }
input[type="number"]:focus, select:focus { border-color: var(--accent); outline: none; }

input[type="range"] {
  flex: 1;
  min-width: 0;
  height: 1.5rem;
  accent-color: var(--accent);
  cursor: pointer;
}

.num {
  flex-shrink: 0;
  width: 4.3rem;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

.check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8125rem;
  cursor: pointer;
}
input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
  accent-color: var(--accent);
  cursor: pointer;
}

.btn {
  height: 2rem;
  padding: 0 0.75rem;
  background: var(--paper-3);
  border: 1px solid var(--rule);
  font-size: 0.8125rem;
  cursor: pointer;
}
.btn:hover { border-color: var(--rule-2); background: var(--paper); }
.btn[disabled] { opacity: 0.45; cursor: default; }
.btn[disabled]:hover { border-color: var(--rule); background: var(--paper-3); }
.btn-block { width: 100%; }
.btn-primary {
  height: 2.4rem;
  padding: 0 1.2rem;
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  font-weight: 600;
}
.btn-primary:hover { filter: brightness(1.08); background: var(--accent); }

.seg { display: flex; border: 1px solid var(--rule); }
.seg-btn {
  flex: 1;
  height: 1.9rem;
  background: var(--paper-3);
  border: 0;
  font-size: 0.75rem;
  color: var(--ink-2);
  cursor: pointer;
}
.seg-btn + .seg-btn { border-left: 1px solid var(--rule); }
.seg-btn.on { background: var(--accent); color: #fff; font-weight: 600; }

.chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.chip {
  padding: 0.25rem 0.5rem;
  background: var(--paper-3);
  border: 1px solid var(--rule);
  font-size: 0.6875rem;
  color: var(--ink-2);
  cursor: pointer;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip-danger:hover { border-color: var(--flag); color: var(--flag); }

.link {
  background: none;
  border: 0;
  padding: 0.1rem 0.2rem;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
}
.link:hover { color: var(--accent); }
.link.on { color: var(--flag); }
.link-danger:hover { color: var(--flag); }

.icon-cell {
  width: 1.5rem;
  height: 1.5rem;
  background: transparent;
  border: 0;
  font-size: 0.75rem;
  color: var(--ink-3);
  cursor: pointer;
}
.icon-cell:hover { color: var(--ink); background: var(--rule); }
.icon-cell.on { color: var(--accent); }
.icon-cell.danger:hover { color: var(--flag); }

/* The finder. An armed bell is flagged red because it can make noise; while it
   is actually beeping it inverts, so the row that is sounding is unmistakable
   in a list of ten. */
.alarm-bell.on { color: var(--flag); }
.alarm-bell.is-sounding { background: var(--flag); color: #fff; animation: alarm-pulse 0.5s ease-in-out infinite; }
@keyframes alarm-pulse { 50% { opacity: 0.4; } }
.cell-trigger { width: 4.3rem; }

/* ---------- Detection menu ---------- */

/* The one menu with a list in it: wider, and scrolling inside itself rather
   than running off the bottom of a tablet held in landscape. */
.menu-panel-wide {
  width: 23rem;
  max-width: calc(100vw - 1.5rem);
  max-height: calc(100vh - var(--bar-h) - 1rem);
  overflow-y: auto;
}

.det-list { display: flex; flex-direction: column; }
.det-row {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.4rem 0;
  border-top: 1px solid var(--rule);
}
.det-row:first-child { border-top: 0; }
/* A category heading: its switch (box + name, one hit target), how many of
   its sounds are on, and the ▾ that folds it away. */
.det-group {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
  padding: 0.15rem 0;
  border-bottom: 1px solid var(--rule);
}
.det-group:first-child { margin-top: 0.2rem; }
.det-group .check { flex: 1; min-height: 2rem; }
.det-group .tag { color: var(--ink-2); font-size: 0.625rem; }
.det-all {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.75rem;
  color: var(--ink-2);
  padding: 0.15rem 0 0.35rem;
}
.det-all > span:first-child { white-space: nowrap; min-width: 6.5rem; }
.det-all input[type="range"] { flex: 1; min-width: 0; }
.det-all .num { width: auto; }
.det-all-help { margin: 0 0 0.35rem; }
.help-btn {
  width: 1.1rem; height: 1.1rem; padding: 0; margin-left: -0.25rem;
  border: 1px solid var(--ink-3); border-radius: 50%;
  background: none; color: var(--ink-2);
  font-size: 0.65rem; font-weight: 700; line-height: 1; cursor: help;
}
.det-group-count { font-size: 0.6875rem; color: var(--ink-3); white-space: nowrap; }
.det-group-toggle { width: 2rem; height: 2rem; font-size: 0.8rem; }
/* Folded but sounding: the heading carries the signal its rows cannot. */
.det-group.is-collapsed.is-active .tag,
.det-group.is-collapsed.is-active .det-group-count { color: var(--accent); }
.det-kind {
  margin-left: 0.4rem;
  padding: 0 0.3rem;
  border: 1px solid var(--rule);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.det-row .check { font-weight: 600; min-height: 1.9rem; }
.det-row.is-active .check span { color: var(--det-color, var(--accent)); }
.det-row.is-off .det-tune { display: none; }
/* Meter, threshold slider, its value, bell — only while the detector is on. */
.det-tune {
  display: grid;
  grid-template-columns: 1fr 5.5rem 2.4rem auto;
  align-items: center;
  gap: 0.5rem;
}
.det-tune .num { width: auto; }
.det-swatch {
  width: 0.6rem;
  height: 0.6rem;
  flex-shrink: 0;
  background: var(--det-color, var(--accent));
}

/* Live score with the threshold marked on it: set the trigger by watching
   the bar, the same way a marker's alarm level is set against its meter. */
.det-meter {
  position: relative;
  height: 0.55rem;
  background: var(--rule);
  overflow: hidden;
}
.det-meter-fill {
  height: 100%;
  width: 0;
  background: var(--det-color, var(--accent));
  opacity: 0.55;
  transition: width 80ms linear;
}
.det-row.is-active .det-meter-fill { opacity: 1; }
.det-meter-mark {
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  margin-left: -1px;
  background: var(--ink);
}
.det-tune input[type="range"] { width: 100%; }

/* These set their own display, which would otherwise beat [hidden]. */
.teach-card[hidden], .det-all[hidden] { display: none; }

/* A learned sound's row: its switch, and Edit beside it. */
.det-row-head { display: flex; align-items: center; gap: 0.5rem; }
.det-row-head .check { flex: 1; min-width: 0; }
.det-row-head .link { min-height: 2rem; padding: 0 0.4rem; }

/* The teach card, inside My sounds. Record is the largest thing on it: it is
   what a thumb is looking for while the sound is happening. */
.teach-slot:empty { display: none; }
.teach-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.5rem 0;
  padding: 0.6rem 0.65rem;
  background: var(--paper-2);
  border: 1px solid var(--rule);
}
.teach-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.teach-head .link { min-height: 2rem; }
.teach-name {
  flex: 1;
  min-width: 0;
  height: 2.2rem;
  padding: 0 0.5rem;
  background: var(--paper-3);
  border: 1px solid var(--rule);
  font-weight: 600;
}
.teach-name:focus { border-color: var(--accent); outline: none; }
.teach-rec-row { display: flex; align-items: center; gap: 0.6rem; }
.teach-rec {
  flex-shrink: 0;
  min-width: 7.5rem;
  height: 2.75rem;
  font-weight: 700;
}
.teach-rec.is-recording {
  background: var(--flag);
  border-color: var(--flag);
  color: #fff;
}
.teach-line {
  margin: 0;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.teach-examples { list-style: none; margin: 0; padding: 0; }
.teach-examples li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  min-height: 2rem;
  border-top: 1px solid var(--rule);
  font-size: 0.75rem;
}
.teach-examples li.is-new { color: var(--accent); }
.teach-examples .icon-cell { width: 2rem; height: 2rem; }
.teach-msg { margin: 0; font-size: 0.75rem; font-weight: 600; color: var(--flag); }
.teach-msg:empty { display: none; }
.teach-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.teach-actions .link { min-height: 2rem; }
.teach-actions-right { display: flex; gap: 0.25rem; }
.teach-buttons { display: grid; grid-template-columns: 1fr auto; gap: 0.4rem; }
.teach-import-msg:empty { display: none; }
.teach-import-msg { margin-top: 0.35rem; }
/* Recording carries on with the menu closed; its summary says so. */
.menu.is-recording > summary::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--flag);
  animation: blink 1.15s ease-in-out infinite;
}

.model-card {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.55rem 0.65rem;
  background: var(--paper-2);
  border: 1px solid var(--rule);
}
.model-card-head { display: flex; align-items: baseline; gap: 0.5rem; }
.model-card-name { font-weight: 700; font-size: 0.8125rem; }
.model-card-meta { font-family: var(--font-mono); font-size: 0.6875rem; color: var(--ink-3); }
.model-state { font-size: 0.6875rem; font-weight: 600; color: var(--ink-2); }
.model-state.is-ready { color: var(--accent); }
.model-state.is-error { color: var(--flag); }

/* ---------- Detections report ---------- */

.report[hidden] { display: none; }
.col-when { width: 7.5rem; white-space: nowrap; }
.col-sound { min-width: 8rem; }
.col-conf { width: 8rem; }
.det-sound { display: flex; align-items: center; gap: 0.45rem; font-weight: 600; }
.det-detail { font-weight: 400; color: var(--ink-3); }
.det-clock { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.det-ago { color: var(--ink-3); font-size: 0.6875rem; }
.det-open .det-sound { color: var(--det-color, var(--accent)); }
.det-conf { display: flex; align-items: center; gap: 0.45rem; }
.det-conf .det-meter { flex: 1; }
.det-conf .det-meter-fill { opacity: 1; }
.report-detections .col-actions { width: 5.5rem; }
/* Recording dot: red, the app's colour for "capturing", and only while the
   clip is still being cut. */
.clip-rec {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  margin: 0 0.5rem;
  border-radius: 50%;
  background: var(--flag);
  vertical-align: middle;
  animation: blink 1.15s ease-in-out infinite;
}
.clip-rec[hidden] { display: none; }

.hint, .empty {
  margin: 0;
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--ink-3);
  text-wrap: pretty;
}
.empty { padding: 0.8rem 0.75rem; }
.hint.is-error { color: var(--flag); }

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

@media (max-width: 900px) {
  /* The wordmark, six menus and capture stop fitting one row a little below
     900px, so the menus take a row of their own under the other two. Panels
     then hang from that row's right edge rather than from their own menu:
     anchored to a menu near the left, a right-aligned panel would run off the
     screen. */
  .bar { flex-wrap: wrap; height: auto; padding: 0.4rem 0.75rem; row-gap: 0.3rem; }
  .wordmark { margin-right: auto; }
  .menus { order: 3; width: 100%; margin-left: 0; justify-content: flex-end; position: relative; }
  .menu { position: static; }
  .menu-panel { top: 100%; right: 0; }
  .menu-panel-wide { max-height: calc(100vh - 6.5rem); max-height: calc(100dvh - 6.5rem); }

  .vu { width: 4rem; }
  /* The two widest, most decorative columns go first: the numbers they
     illustrate are still in the row. */
  .report-table .col-meter, .report-table .col-trend { display: none; }
}

@media (max-width: 620px) {
  :root { --plates-min: 50vh; }
  .bar { flex-wrap: wrap; height: auto; padding: 0.5rem 0.6rem; }
  /* Wrapped, the menus drop to their own row so capture keeps its corner. */
  .menus { order: 3; width: 100%; margin-left: 0; overflow-x: auto; justify-content: flex-start; }
  /* That row scrolls, and a scroll container clips everything positioned
     inside it: an absolute panel would be cut down to the row's 2rem and
     every tap on it would land on the plate behind. So on a phone a panel is
     a fixed sheet across the screen instead. `top: auto` keeps it where it
     would have opened — right under its summary — and a long one scrolls. */
  .menu-panel, .menu-panel-wide {
    position: fixed;
    top: auto;
    left: 0.5rem;
    right: 0.5rem;
    width: auto;
    max-width: none;
    max-height: calc(100vh - 7rem);
    max-height: calc(100dvh - 7rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* Five actions do not fit beside the title at phone width; wrap them rather
     than widening the page past the screen. */
  .report-head { flex-wrap: wrap; row-gap: 0.15rem; }
  .report-head .link { white-space: nowrap; }
  .wordmark { margin-right: auto; }
  .field-view { padding: 0.4rem; }
  .pad { right: 0.75rem; bottom: 0.8rem; }
  .report-body { max-height: 42vh; }
  .report-table thead { display: none; }
  .report-table, .report-table tbody, .report-table tr, .report-table td { display: block; width: 100%; }
  .report-table tr {
    display: grid;
    grid-template-columns: 2.6rem 1fr auto;
    align-items: center;
    gap: 0.2rem 0.4rem;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--rule);
  }
  .report-table td { border: 0; padding: 0; }
  /* Bandwidth goes, the alarm stays: hunting a noise by ear with a phone in
     one hand is exactly what the finder is for. */
  .report-table .col-band { display: none; }
  .cell-num { width: 100%; }
  .col-alarm { width: auto; }
  .cell-trigger { width: 4.3rem; }
  /* A detection reads as when, what, how sure, and its buttons; the rest is
     in the CSV. The name is what gives way — never the buttons. */
  .report-detections .report-table tr { grid-template-columns: auto minmax(0, 1fr) auto auto; }
  .report-detections .col-dur, .report-detections .col-hz { display: none; }
  .report-detections .det-conf .det-meter { display: none; }
  .report-detections .det-sound { flex-wrap: wrap; row-gap: 0; }
  .report-detections .col-actions { width: auto; }
  .col-when, .col-conf { width: auto; }
}

/* A short screen — a phone held sideways — has little height to share: the
   plates settle for less so the reports' headers still fit below them. */
@media (max-height: 500px) {
  :root { --plates-min: 40vh; }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:   #171b21;
    --paper-2: #1e232b;
    --paper-3: #262c35;
    --rule:    #333b45;
    --rule-2:  #4b5563;
    --ink:     #e7ecf2;
    --ink-2:   #a4b0bd;
    --ink-3:   #7b8794;
    --accent:  #7d90ff;
    --accent-soft: #232a4d;
    --flag:    #ff5a4d;
  --live:    #23a05a;
    --shadow-plate: inset 0 0 0 1px #000, 0 2px 10px rgba(0,0,0,0.5);
    color-scheme: dark;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .capture.is-live .capture-dot { opacity: 1; }
}

/* The My sounds blurb sits under the Teach/Import buttons, not against them. */
.teach-buttons + .hint, .teach-slot + .hint { padding-top: 0.6rem; }

/* ---------- Recordings menu ---------- */
.rec-list { display: flex; flex-direction: column; gap: 0.35rem; }
.rec-row { display: flex; align-items: center; gap: 0.3rem; }
.rec-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.rec-name { font-weight: 600; font-size: 0.8125rem; }
.rec-meta { font-family: var(--font-mono); font-size: 0.6875rem; color: var(--ink-3); }
.rec-row .btn { min-width: 2rem; padding: 0 0.5rem; }
