/* DTP validation app - single stylesheet for index.html and event.html */

:root {
  --blue: #1f7fc0;
  --blue-dark: #17669a;
  --blue-light: #e8f2fa;
  --row-selected: #2489cd;

  /* Accent = "which half of the app am I in".  SafetyEvents (the validation side)
     keep the blue; Incidents - the produced, client-facing records - are purple, so
     a glance at the chrome says which collection is being edited.  Every blue in
     this file goes through these four, and `[data-context="incident"]` swaps them.
     Set on <html> by an inline script in each page's head, before first paint. */
  --accent: var(--blue);
  --accent-dark: var(--blue-dark);
  --accent-light: var(--blue-light);
  --accent-row-selected: var(--row-selected);
  --accent-focus: rgba(31, 127, 192, 0.15);

  --grey-bg: #f2f4f7;
  --grey-line: #d7dce3;
  --text: #222a35;
  --muted: #6b7683;
  --orange: #f0932b;
  --red: #c0392b;
  --green: #1e8449;
  --teal: #17a2b8;
}

/* Incident side: same layout, unmistakably not the events side. */
:root[data-context="incident"] {
  --accent: #6f42a1;
  --accent-dark: #55307d;
  --accent-light: #f1ebf9;
  --accent-row-selected: #7e57c2;
  --accent-focus: rgba(111, 66, 161, 0.18);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 13px;
  color: var(--text);
  background: var(--grey-bg);
}

a { color: var(--accent-dark); }

/* ---------------------------------------------------------------- chrome */

.topbar {
  background: var(--accent);
  color: #fff;
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 16px;
  font-size: 15px;
  font-weight: 600;
}
.topbar .sub {
  font-size: 12px;
  font-weight: 400;
  opacity: 0.9;
}
.topbar .spacer { flex: 1; }

.error-banner {
  display: none;
  background: #fdecea;
  border: 1px solid #f5c6cb;
  border-left: 4px solid var(--red);
  color: #7d2820;
  padding: 8px 12px;
  margin: 8px 12px;
  border-radius: 3px;
  white-space: pre-wrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
}
.error-banner.visible { display: block; }
.error-banner .dismiss {
  float: right;
  cursor: pointer;
  font-weight: 700;
  padding: 0 4px;
}

.layout {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px;
}

.sidebar {
  flex: 0 0 260px;
  background: #fff;
  border: 1px solid var(--grey-line);
  border-radius: 4px;
  padding: 12px;
  position: sticky;
  top: 12px;
}
.sidebar h2 {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 0 0 10px;
}
.field { margin-bottom: 12px; }
.field label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 3px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.field.hidden { display: none; }

input[type="text"], input[type="password"], select {
  width: 100%;
  padding: 6px 7px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
  font-size: 13px;
  background: #fff;
  color: var(--text);
}
input[type="text"]:focus, input[type="password"]:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-focus);
}

.hint { font-size: 11px; color: var(--muted); margin-top: 4px; }

.main {
  flex: 1 1 auto;
  min-width: 0;
}

/* ----------------------------------------------------------------- tabs */

.tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 8px;
}
.tab {
  padding: 8px 16px;
  border: 1px solid var(--grey-line);
  border-bottom: none;
  border-radius: 4px 4px 0 0;
  background: #e6eaef;
  cursor: pointer;
  font-weight: 600;
  color: var(--muted);
}
.tab.active {
  background: #fff;
  color: var(--accent-dark);
  border-color: var(--grey-line);
  box-shadow: inset 0 3px 0 var(--accent);
}

.panel {
  background: #fff;
  border: 1px solid var(--grey-line);
  border-radius: 0 4px 4px 4px;
  padding: 10px;
}

.results-line {
  display: flex;
  gap: 32px;
  align-items: center;
  padding: 6px 4px 10px;
  font-size: 13px;
}
.results-line .label { color: var(--muted); }
.results-line .value { font-weight: 700; }
.results-line .spacer { flex: 1; }

/* ----------------------------------------------------------- bulk actions */

/* Only on screen while rows are selected (app.js toggles .hidden), so the write
   actions appear with something to act on and not before.  `.hidden` is generic
   here on purpose - `.field.hidden` above only reaches the sidebar's fields. */
.hidden { display: none !important; }

.bulk-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 8px;
  padding: 8px 10px;
  border: 1px solid var(--accent);
  border-radius: 3px;
  background: var(--accent-light);
  font-size: 12px;
}
.bulk-bar .spacer { flex: 1 1 auto; }
.bulk-count { font-weight: 700; }
.bulk-opt { display: inline-flex; align-items: center; gap: 4px; }
.bulk-status { color: var(--muted); }

/* ---------------------------------------------------------------- table */

/* A header that sorts is a button, so it is reachable by keyboard; it still has to
   look like the header it replaced. */
.sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 700;
  color: inherit;
  cursor: pointer;
  text-align: left;
}
.sort-btn:hover { color: var(--accent); }
.sort-btn.sorted { color: var(--accent); }
.sort-arrow { font-size: 9px; line-height: 1; }

.table-wrap {
  overflow: auto;
  max-height: calc(100vh - 250px);
  border: 1px solid var(--grey-line);
  border-radius: 3px;
}
table.events {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  min-width: 1200px;
  font-size: 12px;
}
table.events thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: #fff;
  border-bottom: 1px solid var(--grey-line);
  border-right: 1px solid var(--grey-line);
  padding: 5px 5px;
  text-align: left;
  font-weight: 700;
  vertical-align: bottom;
  white-space: normal;
}
/* The column headers stay pinned; the filter row scrolls away with the rows it
   filters.  Both used to be sticky (headers at top: 0, filters at top: 46px), which
   held ~40px of controls over the data for the entire scroll - on a 50 row page that
   is a fifth of the table hidden behind inputs nobody is using mid-scroll.  Scrolling
   back up to reach a filter is the cheaper trade: the filters are set once, the rows
   are read continuously. */
table.events thead tr.filters th {
  position: static;
  z-index: auto;
  background: #fff;
  padding: 3px 4px;
}
table.events thead tr.filters input,
table.events thead tr.filters select {
  width: 100%;
  min-width: 60px;
  padding: 3px 4px;
  font-size: 11px;
}
table.events tbody td {
  border-bottom: 1px solid var(--grey-line);
  border-right: 1px solid var(--grey-line);
  padding: 4px 5px;
  vertical-align: top;
  max-width: 220px;
  overflow-wrap: anywhere;
}
table.events tbody tr:nth-child(even) { background: #fafbfc; }
table.events tbody tr:hover { background: var(--accent-light); }
table.events tbody tr.selected,
table.events tbody tr.selected:hover {
  background: var(--accent-row-selected);
  color: #fff;
}
table.events tbody tr.selected a { color: #fff; }
td.mono, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
td.id-cell { color: var(--accent-dark); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
td.id-cell a.id-link { color: inherit; text-decoration: none; }
td.id-cell a.id-link:hover { text-decoration: underline; }
.empty-row td {
  text-align: center;
  padding: 16px;
  color: var(--muted);
}

/* --------------------------------------------------------------- badges */

.badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 10px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  background: var(--muted);
  white-space: nowrap;
}
.badge-manual { background: var(--orange); }
.badge-m { padding: 1px 6px; margin-top: 3px; }

/* ---------------------------------------------------------- tasks drawer */

/* The one fixed element in the app: a collapsible bar docked to the bottom of
   the viewport tracking snippet production.  z-index 20 clears the sticky table
   headers (z 3); the accent border follows the events/incidents colour swap. */
.tasks-drawer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 20;
  background: #fff;
  border-top: 2px solid var(--accent);
  box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.15);
}
.tasks-drawer .drawer-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 10px;
  font-size: 12px;
}
.tasks-drawer .drawer-toggle {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 2px 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.tasks-drawer .drawer-toggle span { color: var(--muted); }
.tasks-drawer .drawer-body { border-top: 1px solid var(--grey-line); }
.tasks-drawer.collapsed .drawer-body { display: none; }
.tasks-drawer .drawer-status {
  padding: 4px 10px;
  font-size: 11px;
  color: var(--muted);
}
.tasks-drawer .drawer-table-wrap { max-height: 40vh; overflow: auto; }
.tasks-drawer table.events { min-width: 900px; }

/* Keep page content clear of the docked bar: the events table's own viewport
   budget (max-height above) loses the bar's height, and the page bottom gets
   padding so nothing hides behind it. */
body.has-tasks-drawer { padding-bottom: 44px; }
body.has-tasks-drawer .table-wrap { max-height: calc(100vh - 294px); }

.badge-queued { background: var(--muted); }
.badge-running { background: #1565c0; }
.badge-done { background: var(--green); }
.badge-failed { background: var(--red); }
.badge-stalled { background: var(--orange); }

/* ---------------------------------------------------------- speed control */

.speed-control {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 4px;
  font-size: 12px;
}
.speed-control select { padding: 2px 4px; font-size: 12px; }
.badge-default { background: #1565c0; }
.badge-gemini { background: #6f42c1; }
.badge-yes { background: var(--green); }
.badge-no { background: #9aa4ae; }

/* -------------------------------------------------------------- buttons */

button, .btn {
  font-family: inherit;
  font-size: 12px;
  padding: 6px 12px;
  border: none;
  border-radius: 3px;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  font-weight: 600;
}
button:hover, .btn:hover { filter: brightness(1.07); }
button:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }
.btn-grey { background: #6b7683; }
.btn-dark { background: #14563f; }
.btn-teal { background: var(--teal); }
.btn-orange { background: #e2661a; }
.btn-red { background: var(--red); }
.btn-green { background: var(--green); }
.btn-sm { padding: 3px 9px; font-size: 11px; }

/* The Generate tab's Jump button while its window is being cut.  It is disabled
   like every other Jump at that moment, but it must not be dimmed with them: it
   is the one the reviewer pressed and the only thing on screen saying the wait is
   deliberate.  Specificity, not !important, is what beats `button:disabled`. */
button.btn-jumping, button.btn-jumping:disabled {
  background: var(--teal);
  opacity: 1;
  cursor: progress;
}
@media (prefers-reduced-motion: no-preference) {
  button.btn-jumping { animation: jump-pulse 1.1s ease-in-out infinite; }
}
@keyframes jump-pulse {
  50% { filter: brightness(1.35); }
}

.pager {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 10px 4px 2px;
  font-size: 12px;
}
.pager .spacer { flex: 1; }
.pager select { width: auto; }

/* ---------------------------------------------------- event detail page */

.detail-layout {
  display: flex;
  gap: 12px;
  padding: 12px;
  align-items: flex-start;
}
.detail-left { flex: 1 1 50%; min-width: 0; }
.detail-right { flex: 1 1 50%; min-width: 0; position: sticky; top: 12px; }

.card {
  background: #fff;
  border: 1px solid var(--grey-line);
  border-radius: 4px;
  margin-bottom: 12px;
}
.card > h3 {
  margin: 0;
  padding: 8px 12px;
  font-size: 13px;
  border-bottom: 1px solid var(--grey-line);
  background: #f7f9fb;
  border-radius: 4px 4px 0 0;
}
.card > .card-body { padding: 10px 12px; }

.classification-banner {
  background: var(--accent-light);
  border: 1px solid #bcd9ee;
  border-radius: 4px;
  padding: 12px 14px;
  margin-bottom: 12px;
}
.classification-banner .label {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.classification-banner .code {
  font-size: 26px;
  font-weight: 300;
  line-height: 1.2;
}
.classification-banner .desc { color: #33414f; }

.kv-list { list-style: none; margin: 0; padding: 0; }
.kv-list li {
  border-bottom: 1px solid #eef1f4;
  padding: 5px 8px;
  font-size: 12px;
  overflow-wrap: anywhere;
}
.kv-list li:last-child { border-bottom: none; }
.kv-list .k { color: #33414f; }
.kv-list .v { font-weight: 600; }

.two-col { display: flex; gap: 12px; }
.two-col > * { flex: 1 1 0; min-width: 0; }

video#event-video {
  width: 100%;
  max-height: 46vh;
  background: #000;
  display: block;
  border-radius: 4px 4px 0 0;
}
.video-controls {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px;
  flex-wrap: wrap;
}
.video-meta { font-size: 11px; color: var(--muted); }

.form-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.form-row label { font-size: 12px; font-weight: 600; }
.form-row select { width: auto; min-width: 130px; }
.form-note { font-size: 11px; color: var(--muted); }
/* re-validate hint: neutral by default, amber when the run would lose a snippet */
.form-note.warn {
  color: #8a4b12;
  background: #fdf3e6;
  border-left: 3px solid #d98324;
  padding: 6px 8px;
  margin-top: 6px;
}

/* --------------------------------------------- manual classification UI */

.manual-box {
  margin-top: 12px;
  padding: 12px;
  background: #e9ecef;
  border-radius: 4px;
}
.manual-box h4 { margin: 0 0 10px; font-size: 12px; }

.selected-event-display {
  display: none;
  background: #d5f5e0;
  border: 1px solid #86d9a6;
  border-radius: 4px;
  padding: 10px 12px;
  margin-bottom: 10px;
}
.selected-event-display.visible { display: block; }
.selected-event-display .selected-label {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #1e6b41;
}
.selected-event-display .selected-code { font-size: 22px; font-weight: 300; }
.selected-event-display .selected-description { font-size: 12px; color: #245c3c; }

.event-category { margin-bottom: 6px; border-radius: 4px; overflow: hidden; }
.event-category-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 9px 12px;
  color: #fff;
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
  user-select: none;
}
.event-category[data-type="NM"] .event-category-header { background: #c0392b; }
.event-category[data-type="SS"] .event-category-header { background: #e2811d; }
.event-category[data-type="EN"] .event-category-header { background: #17a37b; }
.event-category[data-type="GW"] .event-category-header { background: #23a455; }
.event-category[data-type="DT"] .event-category-header { background: #2b7cb8; }
.event-category[data-type="ND"] .event-category-header { background: #5b6d8a; }
.event-category[data-type="QU"] .event-category-header { background: #7b5ea7; }

.priority-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 10px;
  background: rgba(255, 255, 255, 0.25);
  margin-right: 6px;
}
.priority-badge.priority-1 { background: #8e2b20; }
.priority-badge.priority-2 { background: #b1631a; }
.priority-badge.priority-3 { background: #e2811d; }
.priority-badge.priority-normal { background: #1b7fa8; }
.expand-icon { font-size: 10px; }

.event-category-body { display: none; background: #fff; border: 1px solid var(--grey-line); border-top: none; }
.event-category.expanded .event-category-body { display: block; }
.event-category-description {
  padding: 8px 12px;
  font-size: 11px;
  color: #45505c;
  border-bottom: 1px solid #eef1f4;
}
.event-subtype-list { list-style: none; margin: 0; padding: 0; }
.event-subtype-item {
  padding: 7px 12px;
  border-bottom: 1px solid #eef1f4;
  cursor: pointer;
  display: flex;
  gap: 10px;
  align-items: baseline;
}
.event-subtype-item:hover { background: var(--accent-light); }
.event-subtype-item.selected { background: #d5f5e0; box-shadow: inset 3px 0 0 var(--green); }
.subtype-code { font-weight: 700; min-width: 42px; }
.subtype-description { font-size: 11px; color: #45505c; }
.crossing-type-note {
  padding: 7px 12px;
  font-size: 11px;
  background: #fff8e1;
  color: #6b5300;
}

.status-line { font-size: 12px; margin: 6px 0; }
.status-line.ok { color: var(--green); }
.status-line.err { color: var(--red); }
.status-line.warn { color: #b1631a; }

/* ------------------------------------------------- async pipeline polling */

.poll-row { margin-bottom: 8px; }
.poll-row[hidden] { display: none; }
.spinner {
  width: 12px;
  height: 12px;
  border: 2px solid rgba(31, 127, 192, 0.25);
  border-top-color: var(--accent);
  border-radius: 50%;
  display: inline-block;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }

.manual-actions { margin-top: 12px; margin-bottom: 0; }
.manual-actions button { flex: 1 1 0; }

.readonly-notice[hidden], #validation-card[hidden], #incident-status[hidden] { display: none; }
.readonly-notice > h3 { background: #fff8e1; color: #6b5300; }
.readonly-notice .card-body { color: #6b5300; font-size: 12px; }

/* ------------------------------------------------------ validation sources */

.validation-sources-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.validation-source-card {
  border: 1px solid var(--grey-line);
  border-radius: 4px;
  padding: 8px 10px;
  background: #fff;
  font-size: 11px;
}
.validation-source-card h6 {
  margin: 0 0 6px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.validation-source-card p { margin: 0 0 3px; }
.validation-source-card .source-desc {
  font-style: italic;
  font-size: 10px;
  line-height: 1.3;
  margin-top: 5px;
}
.validation-source-card.default-card { border-color: #90c2e7; background: #f2f8fd; }
.validation-source-card.default-card h6,
.validation-source-card.default-card .source-desc { color: #1565c0; }
.validation-source-card.gemini-card { border-color: #c3aee0; background: #f7f4fc; }
.validation-source-card.gemini-card h6,
.validation-source-card.gemini-card .source-desc { color: #6f42c1; }
.validation-source-card.manual-card { border-color: #f3c48e; background: #fff6ec; }
.validation-source-card.manual-card h6,
.validation-source-card.manual-card .source-desc { color: #e65100; }
.validation-source-card.empty-card {
  background: var(--grey-bg);
  color: var(--muted);
  opacity: 0.7;
}
.validation-source-card.empty-card h6 { color: var(--muted); }

pre.payload {
  background: #1e2530;
  color: #d7e2ee;
  padding: 8px;
  border-radius: 3px;
  font-size: 11px;
  overflow: auto;
  max-height: 180px;
}

/* ----------------------------------------------------------------- login */

.login-wrap {
  display: flex;
  justify-content: center;
  padding: 60px 12px;
}
.login-card {
  width: 100%;
  max-width: 380px;
  background: #fff;
  border: 1px solid var(--grey-line);
  border-radius: 4px;
  padding: 20px;
}
.login-card h2 {
  margin: 0 0 6px;
  font-size: 16px;
}
.login-card .field label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 3px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.login-actions { margin-top: 16px; }
.login-actions button { width: 100%; padding: 8px 12px; }
.login-card .status-line { margin-top: 10px; min-height: 16px; }

/* Signed-in identity in the top bar, on both pages. */
.topbar .who { font-size: 12px; font-weight: 400; opacity: 0.9; }
.topbar button.sign-out { padding: 3px 9px; font-size: 11px; background: rgba(0, 0, 0, 0.22); }

@media (max-width: 1100px) {
  .detail-layout { flex-direction: column; }
  .detail-right { position: static; width: 100%; }
  .layout { flex-direction: column; }
  .sidebar { position: static; width: 100%; flex: 1 1 auto; }
}

/* --------------------------------------------------------- generate tab */

/* Green: this is the tab that *creates* a record rather than judging one, and
   the accent is how the chrome says which half of the app you are in. */
:root[data-context="generate"] {
  --accent: #1e8449;
  --accent-dark: #166135;
  --accent-light: #e7f4ec;
  --accent-row-selected: #27ae60;
  --accent-focus: rgba(30, 132, 73, 0.18);
}

/* Teal: the Peak Videos tab plays finished overlay renders - it neither judges
   nor creates a record, and the accent keeps saying which tab is on screen. */
:root[data-context="peak"] {
  --accent: #117a8b;
  --accent-dark: #0d5d6a;
  --accent-light: #e5f4f7;
  --accent-row-selected: #17a2b8;
  --accent-focus: rgba(23, 162, 184, 0.18);
}

/* Amber: the Timezones tab edits configuration other services read, rather than
   judging or creating a review record - the accent says "this is not the events
   table" before anything is clicked. */
:root[data-context="timezone"] {
  --accent: #b9770e;
  --accent-dark: #8c5a09;
  --accent-light: #fdf3e3;
  --accent-row-selected: #d68910;
  --accent-focus: rgba(214, 137, 16, 0.18);
}

/* The video-edit page: indigo, distinct from every tab accent. */
:root[data-context="video-edit"] {
  --accent: #4f5bd5;
  --accent-dark: #3a44a8;
  --accent-light: #eef0fc;
  --accent-row-selected: #6a75e0;
  --accent-focus: rgba(79, 91, 213, 0.18);
}

/* ------------------------------------------------------ video-edit page */

.ve-page { max-width: 1300px; margin: 16px auto; padding: 0 16px; }
.ve-controls { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 12px; }
.ve-controls .field { min-width: 320px; }
.ve-controls input[type="text"] {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
  font-size: 13px;
}
.ve-status { color: var(--muted); margin: 10px 0; }

.ve-toolbar { display: flex; align-items: center; gap: 14px; margin: 8px 0; flex-wrap: wrap; }
.ve-selected { color: var(--muted); font-size: 12px; }
.ve-legend { display: flex; gap: 14px; font-size: 11px; color: var(--muted); align-items: center; }
.ve-legend .swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 4px;
  vertical-align: -1px;
}
.swatch-period { background: var(--accent-light); border: 1px solid var(--accent); }
.swatch-overlap { background: var(--red); }

.ve-table-wrap {
  overflow: auto;
  max-height: 380px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
}
/* Eight narrow columns - it must not inherit the events table's 1200px floor. */
.ve-table { min-width: 860px; }
/* A capture inside one of the device's processing periods - the footage the
   pipeline actually analyses, so the rows a shift most needs care around. */
.ve-table tr[data-in-period="true"] td { background: var(--accent-light); }
.ve-mismatch { color: var(--orange); cursor: help; }

.ve-staging {
  border: 1px solid var(--grey-line);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  background: #fff;
  padding: 12px 14px;
  margin: 14px 0;
}
.ve-staging h3 { margin: 0 0 4px; font-size: 14px; }
.ve-note { color: var(--muted); font-size: 12px; margin: 4px 0 10px; }
.ve-staging-grid { display: flex; gap: 14px; flex-wrap: wrap; align-items: stretch; }
.ve-fieldset {
  border: 1px solid var(--grey-line);
  border-radius: 3px;
  padding: 8px 10px;
  min-width: 280px;
  flex: 1 1 280px;
}
.ve-fieldset legend { font-size: 12px; font-weight: 600; color: var(--muted); padding: 0 4px; }
.ve-fieldset .hint { margin-top: 6px; }
.ve-shift-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ve-shift-row label { font-size: 12px; color: var(--muted); }
.ve-fieldset input[type="text"] {
  width: 100%;
  padding: 6px 8px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
  font-size: 13px;
}
.ve-stage-device-picked {
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
}

.ve-overlap-count { margin: 12px 0 6px; font-size: 12px; color: var(--green); }
.ve-overlap-count.has-overlaps { color: var(--red); font-weight: 600; }
.ve-timeline { display: block; background: #fff; border: 1px solid var(--grey-line); border-radius: 3px; }
.ve-overlap-panel {
  border: 1px solid var(--red);
  border-radius: 3px;
  background: #fdecea;
  padding: 8px 12px;
  margin-top: 10px;
}
.ve-overlap-panel h4 { margin: 0 0 4px; font-size: 13px; }
.ve-danger-note { color: var(--red); font-size: 12px; margin: 4px 0 8px; }
.ve-overlap-row { padding: 3px 0; font-size: 12px; }
.ve-overlap-row label { font-weight: 600; }
.ve-overlap-meta { color: var(--muted); }

.ve-apply-row { display: flex; align-items: center; gap: 12px; margin-top: 12px; }

/* -------------------------------------------------- video-edit: crop tab */

.ve-danger-banner {
  border: 1px solid var(--red);
  border-left-width: 4px;
  border-radius: 3px;
  background: #fdecea;
  color: var(--red);
  padding: 10px 12px;
  margin: 10px 0;
  font-size: 12px;
}

.crop-layout { display: flex; gap: 16px; align-items: flex-start; }
.crop-stage { flex: 1 1 auto; min-width: 0; max-width: 960px; }
.crop-side { flex: 0 0 280px; }

/* Same absolute-canvas trick as .gen-video-wrap, with two differences: the
   canvas takes the pointer (drawing the rect IS the interaction, so the video
   is driven from its controls only via the strip the canvas leaves free - the
   speed select is moved out of the wrap by crop.js), and there is no
   max-height - a letterboxed video would break the "canvas px * source/client
   scale = source px" arithmetic the crop rect depends on. */
.crop-video-wrap {
  position: relative;
  background: #000;
  line-height: 0;
}
.crop-video-wrap video {
  width: 100%;
  display: block;
}
.crop-video-wrap canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: auto;
  cursor: crosshair;
  touch-action: none;
}

.crop-actions { display: flex; gap: 10px; margin-top: 8px; }
.crop-inputs { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.crop-inputs label { font-size: 12px; color: var(--muted); }
.crop-inputs input[type="number"] {
  width: 64px;
  padding: 4px 6px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
}
.crop-side .hint { margin: 8px 0; }
.crop-side #crop-apply { width: 100%; margin-top: 4px; }
.ve-danger-btn { background: var(--red); }

.crop-progress {
  margin-top: 12px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
  background: #fff;
  padding: 8px 10px;
  font-size: 12px;
}
.crop-progress-state { font-weight: 600; margin-bottom: 6px; }
.crop-progress-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 0;
}
.crop-status-badge { font-weight: 600; color: var(--muted); }
.crop-progress-row[data-status="done"] .crop-status-badge { color: var(--green); }
.crop-progress-row[data-status="failed"] .crop-status-badge { color: var(--red); }
.crop-progress-row[data-status="cropping"] .crop-status-badge,
.crop-progress-row[data-status="downloading"] .crop-status-badge,
.crop-progress-row[data-status="uploading"] .crop-status-badge,
.crop-progress-row[data-status="updating"] .crop-status-badge { color: var(--accent-dark); }

/* The Incidents tab (video-edit page) - everything scoped under its panel. */
#incidents-panel .badge-delivered { background: var(--orange); }
#incidents-panel tr.inc-delivered td { background: #fdf3e7; }
#incidents-panel #inc-crop-video-wrap { margin-top: 10px; max-width: 640px; }
#incidents-panel #inc-crop-video-wrap video { width: 100%; display: block; background: #000; }
#incidents-panel .crop-progress-row[data-status="shifting"] .crop-status-badge { color: var(--accent-dark); }

/* The Timezones panel: the site's own row above the devices that inherit it. */
.tz-body { display: flex; flex-direction: column; gap: 12px; }
.tz-site {
  border: 1px solid var(--grey-line);
  border-left: 3px solid var(--accent);
  background: var(--accent-light);
  border-radius: 4px;
  padding: 10px 12px;
}
.tz-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tz-row select { min-width: 240px; }
.tz-note { color: var(--muted); font-size: 12px; }
#timezone-devices select { min-width: 220px; }

/* The Peak Videos panel: the player above, the list below - the list is short
   (one row per 15 minute render) and the video is the point of the tab. */
.peak-body { display: flex; flex-direction: column; gap: 12px; }
#peak-video { width: 100%; max-width: 960px; background: #000; }
.peak-table-wrap {
  overflow: auto;
  max-height: 320px;
  max-width: 960px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
}
.peak-heading { margin: 4px 0 0; font-size: 14px; color: var(--text); }

.gen-controls {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--grey-line);
}
.gen-controls .field { margin-bottom: 0; }
.gen-controls #gen-video-select { min-width: 340px; }
.gen-controls #gen-window-start { width: 110px; }
.gen-event-pick { display: flex; gap: 6px; align-items: center; }
.gen-event-pick select { min-width: 240px; }
.gen-status { color: var(--muted); font-size: 12px; }

.gen-body {
  display: flex;
  gap: 16px;
  padding: 12px 0;
  align-items: flex-start;
}
.gen-stage { flex: 1 1 auto; min-width: 0; }
.gen-side { flex: 0 0 260px; }

/* The canvas is stretched over the video by absolute positioning rather than
   drawn into it: the video decodes on its own and the overlay never has to
   touch its pixels, so nothing here taints a canvas with cross-origin frames. */
.gen-video-wrap {
  position: relative;
  background: #000;
  line-height: 0;
}
.gen-video-wrap video {
  width: 100%;
  max-height: 62vh;
  display: block;
}
.gen-video-wrap canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Clicks belong to the video's own controls underneath. */
  pointer-events: none;
}

.gen-legend {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 6px 2px;
  font-size: 11px;
  color: var(--muted);
}
.gen-legend .swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 4px;
  vertical-align: -1px;
}
.swatch-ped { background: #f0932b; }
.swatch-veh { background: #17a2b8; }
.swatch-u1 { background: #1e8449; }
.swatch-u2 { background: #c0392b; }

.gen-pick {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 8px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
  margin-bottom: 6px;
  background: #fff;
}
.gen-pick.filled { border-color: var(--accent); background: var(--accent-light); }
.gen-pick-label { font-weight: 600; }
.gen-pick-value { color: var(--muted); text-align: right; }

.gen-side .field { margin-bottom: 10px; }
.gen-side select { width: 100%; }
.gen-side #gen-save { width: 100%; margin-top: 8px; }
.gen-side .status-line { word-break: break-word; }
.gen-side .card { margin-bottom: 12px; }

.gen-table-wrap {
  overflow: auto;
  max-height: 260px;
  border: 1px solid var(--grey-line);
  border-radius: 3px;
}
/* The road-user table has ten narrow columns, not the events table's thirteen
   wide ones, so it must not inherit that table's 1200px floor. */
.gen-table { min-width: 720px; }
.gen-table .gen-pick-cell { white-space: nowrap; }
.gen-table .gen-pick-cell button { margin-right: 4px; }
/* A row the overlay cannot draw. Still listed - the road user was really there -
   but it can never be highlighted, so it must not look selectable. */
.gen-table tr.no-geometry { color: var(--muted); background: #fafbfc; }
/* The table lists the whole video and the player shows 20s of it: the rows that are
   actually on screen are the ones that can be picked right now, so everything else
   steps back rather than disappearing. */
.gen-table tr.out-of-window td { opacity: 0.55; }
.gen-table tr.out-of-window td:last-child { opacity: 1; }
.gen-table tr.picked-1 td { background: var(--accent-light); }
.gen-table tr.picked-2 td { background: #fdecea; }
