:root {
  color-scheme: dark;
  /* One background, #121314, for the page, the side columns, the top bar and the panels; the
     lines carry the structure. Only what floats (menus, lists, dialogs' tiles) is one step lighter;
     the menu bar has a surface of its own (--chrome) so it reads apart from everything below it. */
  --bg: #121314;
  --panel: #121314;
  --chrome: #1b1d1f; /* the menu bar: its own surface, lighter than the page */
  --chrome-line: #373a3d;
  --raised: #1b1d1e;
  --line: #2a2c2e;
  --field-line: #3e4143; /* dashed drop areas, hovered cards */
  --hover: #1a1c1d;
  --hover-strong: #25282a;
  /* Textboxes and comboboxes: a fill one step lighter than the page, a thin quiet edge. */
  --input: #191a1b;
  --input-hover: #1e2021;
  --input-line: #333536;
  --input-line-hover: #4d5052;
  --stage: #000000;
  --text: #cccccc;
  --text-strong: #ffffff;
  --muted: #a6a6a6; /* 4.5:1 on --input */
  --flow: #8c8c8c; /* lines between scenario blocks */
  /* Brand: dark orange. */
  --accent: #e86a1a;
  --accent-hover: #f07828;
  /* Fill behind white text: --accent with white is only 3.2:1, this is 4.5:1. */
  --accent-fill: #c15713;
  --accent-fill-hover: #ad4e11;
  --accent-dim: #3b2a1f;
  /* What is chosen (a tab, a filter, a toggle that is on) is never filled like a button:
     a light orange tint, white text and an orange edge. Only .btn.primary is solid orange. */
  --select-bg: rgb(232 106 26 / 13%);
  --select-bg-hover: rgb(232 106 26 / 20%);
  --select-line: rgb(232 106 26 / 55%);
  --select-mark: var(--accent);
  --on-accent: #ffffff;
  --ok: #7dcea0;
  --warn: #e6c15a;
  --bad: #f48771;
  --danger-bg: #5a1d1d;
  --danger-line: #be1100;
  --warn-bg: #352f17;
  --zone-a: #3ddc97;
  --zone-b: #5b8cff;
  --zone-c: #e6c15a;
  --src-camera: #5b8cff; /* Sources: a camera card */
  --src-file: #b98cf5; /* Sources: a file card */
  --radius: 6px;
  --radius-lg: 8px;
  --shadow: 0 8px 24px rgb(0 0 0 / 45%);
  /* One type scale for the whole console (2026-11-04). Every font-size uses one of these:
     xs tags, badges, axes · sm hints, meta, table headers · md controls, rows, buttons ·
     base body, names · lg section and card titles · xl page and dialog titles ·
     num the big figures (gauges, KPIs) · code an ID people read out. */
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-md: 13px;
  --fs-base: 14px;
  --fs-lg: 16px;
  --fs-xl: 20px;
  --fs-num: 22px;
  --fs-code: 26px;
  --font: ui-sans-serif, system-ui, "Segoe UI", "Noto Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Noto Sans Mono", monospace;
}

* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
  font: var(--fs-base)/1.45 var(--font);
  background: var(--bg);
  color: var(--text);
}
button, input, select, textarea { font: inherit; color: inherit; }
/* A flex button keeps its text on the middle line unless it says otherwise. */
button { cursor: pointer; appearance: none; align-items: center; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
[hidden] { display: none !important; }
h1, h2, h3 { font-weight: 600; margin: 0 0 0.75rem; color: var(--text-strong); }
/* Headings without a size of their own sit on the scale, not on the browser's 2em / 1.5em. */
h1 { font-size: var(--fs-xl); }
h2 { font-size: var(--fs-lg); }
h3 { font-size: var(--fs-base); }
.muted { color: var(--muted); }
.err { color: var(--bad); margin: 0; }
.icon { display: block; flex: none; }
.hint { margin: 0; color: var(--muted); font-size: var(--fs-sm); overflow-wrap: anywhere; }

/* ---- Shell ---- */
.shell { display: flex; flex-direction: column; min-height: 100vh; }
/* The menu bar: a lighter surface than the page, a firm lower edge with a shadow, a thin
   orange thread along the top that fades out, the ring-and-dot mark and a two-tone name. */
.top {
  min-height: 54px; display: flex; align-items: center; gap: 20px;
  padding: 6px 16px; border-bottom: 1px solid var(--chrome-line); background: var(--chrome);
  box-shadow: 0 8px 20px rgb(0 0 0 / 35%);
  position: sticky; top: 0; z-index: 10;
}
.top::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; pointer-events: none;
  background: linear-gradient(90deg, var(--accent) 0, rgb(232 106 26 / 0%) 45%);
}
.brand { display: inline-flex; align-items: center; gap: 9px; font-size: var(--fs-lg); font-weight: 700; letter-spacing: 0.02em; color: var(--text-strong); white-space: nowrap; }
.brand b { font-weight: 700; color: var(--accent); }
/* The OpenViewer mark (assets/openviewer_tray_icon.svg): no backdrop, so it sits on the bar. */
.brand-mark { width: 22px; height: 22px; display: block; flex: none; }
.nav { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; flex: 1; min-width: 0; }
.nav button {
  display: inline-flex; align-items: center; gap: 6px; height: 31px;
  padding: 0 8px; border: 0; border-radius: var(--radius); background: transparent;
  color: var(--text); font-size: var(--fs-md); font-weight: 500;
}
.nav button:hover { background: rgb(255 255 255 / 8%); color: var(--text-strong); }
.nav button.active { background: var(--select-bg); color: var(--text-strong); box-shadow: inset 3px 0 0 var(--select-mark); }
.nav button.active .icon { color: var(--accent-hover); }
/* On a wide screen the open page is shown by a tinted pill with an orange icon and an orange
   bar that sits on the bar's lower edge; on a phone the menu list keeps the solid orange. */
@media (min-width: 768px) {
  .nav button { position: relative; padding: 0 10px; }
  .nav button.active { background: var(--select-bg); color: var(--text-strong); box-shadow: none; }
  .nav button.active .icon { color: var(--accent-hover); }
  .nav button.active::after {
    content: ""; position: absolute; left: 6px; right: 6px; bottom: -11px; height: 2px;
    border-radius: 2px 2px 0 0; background: var(--accent);
  }
}
.menu { display: none; justify-content: center; height: 31px; padding: 0 10px; border: 1px solid var(--line); border-radius: var(--radius); background: transparent; }
.tools { position: relative; margin-left: auto; display: flex; align-items: center; gap: 12px; }
/* The folder picker (js/folder-picker.js). */
.vw-dialog.fp-dialog { width: min(640px, calc(100vw - 32px)); }
.fp-bar { display: flex; align-items: center; gap: 8px; }
.fp-bar .fp-path { flex: 1; min-width: 0; font-family: var(--mono); font-size: var(--fs-md); }
.fp-up .icon { transform: rotate(-90deg); }
.fp-quick { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.btn.ghost-text { height: 28px; padding: 0 10px; font-size: var(--fs-sm); color: var(--muted); }
.fp-list { height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; padding: 4px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); }
.fp-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 10px; border: 0; border-radius: var(--radius); background: transparent; color: var(--text); font-size: var(--fs-md); text-align: left; }
.fp-item:hover { background: var(--hover-strong); color: var(--text-strong); }
.fp-item .icon { flex: none; color: var(--muted); }
.fp-empty { margin: auto; color: var(--muted); font-size: var(--fs-md); }
.fp-note { min-height: 18px; margin: 8px 0 0; }
.fp-note.bad { color: var(--bad); }
.fp-note.warn { color: var(--warn); }
/* Engines the node builds before scenarios can change (engines.js). */
.engines-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 8px 16px; font-size: var(--fs-sm);
  background: var(--warn-bg); color: var(--warn); border-bottom: 1px solid var(--warn); }
.engines-bar[hidden] { display: none; }
.engines-bar[data-state="bad"] { color: var(--bad); border-bottom-color: var(--bad); }
.engines-bar strong { color: inherit; }
.engines-bar > span:not(.engines-spin) { flex: 1 1 320px; min-width: 0; }
.engines-bar a, .engines-bar .link-btn { color: inherit; text-decoration: underline; white-space: nowrap; }
.engines-spin { width: 12px; height: 12px; flex: none; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: engines-spin 1s linear infinite; }
@keyframes engines-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .engines-spin { animation: none; } }
/* Settings → Xử lý video và AI → Cách chạy model AI: Tự động or tick the ways the node may use. */
.bk { display: flex; flex-direction: column; gap: 12px; }
.bk-modes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.bk-mode { display: flex; flex-direction: row; align-items: flex-start; gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-lg); color: var(--text); font-size: var(--fs-md); cursor: pointer; }
.bk-mode.on { border-color: var(--accent); background: var(--accent-dim); }
.bk-mode strong, .bk-text strong { display: block; color: var(--text-strong); font-size: var(--fs-md); }
.bk-mode small, .bk-text small { display: block; margin-top: 2px; color: var(--muted); font-size: var(--fs-sm); font-weight: 400; }
.form .bk input { width: auto; flex: none; margin-top: 2px; }
.bk-list { display: flex; flex-direction: column; }
.bk-row { display: flex; flex-direction: row; align-items: center; gap: 12px; padding: 10px 4px; border-top: 1px solid var(--line); color: var(--text); font-size: var(--fs-md); cursor: pointer; }
.bk-row.off { opacity: 0.55; cursor: not-allowed; }
.form .bk-row input { margin-top: 0; }
.bk-text { flex: 1; min-width: 0; }
.bk-state { flex: none; color: var(--muted); border-color: var(--line); }
.sf-field.changed .bk-mode.on { border-color: var(--accent); }
@media (max-width: 560px) { .bk-modes { grid-template-columns: 1fr; } }

/* Settings → Mạng: what the API address is, in words and a table. */
.net-guide { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 760px; }
.net-guide h2 { margin: 0; font-size: var(--fs-lg); }
.net-guide p { margin: 0; }
.net-guide table { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }
.net-guide th { padding: 8px 10px; text-align: left; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; border-bottom: 1px solid var(--line); }
.net-guide td { padding: 9px 10px; vertical-align: top; border-bottom: 1px solid var(--line); }
.net-guide td:first-child { white-space: nowrap; color: var(--text-strong); font-family: var(--mono); }
.net-now { color: var(--muted); }
.net-now strong { color: var(--text-strong); font-family: var(--mono); }

/* The bell: open alerts counted on it, the list under it. */
.bell { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; padding: 0; border: 1px solid transparent; border-radius: var(--radius); background: transparent; color: var(--text); }
.bell:hover, .bell[aria-expanded="true"] { background: rgb(255 255 255 / 8%); color: var(--text-strong); }
.bell.has { color: var(--warn); }
.bell-count { position: absolute; top: 1px; right: 0; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; font-size: var(--fs-xs); font-weight: 700; line-height: 16px; text-align: center; color: #121314; background: var(--warn); }
.bell-count.crit { background: var(--bad); }
.notif {
  position: absolute; top: 46px; right: 0; z-index: 30; width: 380px; max-height: min(70vh, 560px); display: flex; flex-direction: column;
  background: var(--raised); border: 1px solid var(--chrome-line); border-radius: var(--radius-lg); box-shadow: var(--shadow); overflow: hidden;
}
.notif-head, .notif-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; }
.notif-head { border-bottom: 1px solid var(--line); }
.notif-head strong { color: var(--text-strong); font-size: var(--fs-base); }
.notif-head a, .notif-foot a { font-size: var(--fs-sm); }
.notif-foot { border-top: 1px solid var(--line); justify-content: flex-end; }
.notif-body { overflow-y: auto; padding: 6px; }
.notif-body h3 { margin: 8px 8px 4px; color: var(--muted); font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.notif-item { display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 8px; border: 0; border-radius: var(--radius); background: transparent; color: var(--text); text-align: left; }
.notif-item:hover { background: var(--hover-strong); }
.notif-item .icon { flex: none; margin-top: 1px; }
.notif-item.crit .icon { color: var(--bad); }
.notif-item.warn .icon { color: var(--warn); }
.notif-item.ok .icon { color: var(--ok); }
.notif-item.info .icon { color: var(--muted); }
.notif-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.notif-line { font-size: var(--fs-md); overflow-wrap: anywhere; }
.notif-text small { color: var(--muted); font-size: var(--fs-xs); }
.notif-empty { margin: 0; padding: 18px 12px; color: var(--muted); text-align: center; font-size: var(--fs-md); }
.cb.lang { width: auto; }
.lang .cb-btn { height: 30px; font-size: var(--fs-sm); }
.health { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); }
.health::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--warn); flex: none; }
.health.ok::before { background: var(--ok); }
.health.bad::before { background: var(--bad); }
.license-chip { padding: 2px 10px; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: var(--fs-sm); text-decoration: none; white-space: nowrap; }
.license-chip.soon { border-color: var(--warn); color: var(--warn); }
.lic-code { display: flex; gap: 8px; }
.gate-fields input[type=file] { height: auto; padding: 9px 12px; }
.lic-code input { font-family: var(--mono); letter-spacing: 0.04em; }
.lic-code .btn { flex: none; height: 42px; }
.lic-trial { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; margin: 4px 0 0; padding: 14px 0 0; border: 0; border-top: 1px solid var(--line); }
.lic-trial legend { padding: 0 8px 0 0; color: var(--muted); font-size: var(--fs-sm); }
.lic-trial .btn, .lic-trial .lic-trial-state { grid-column: 1 / -1; }
.lic-trial .lic-trial-state { margin: 0; }
.lic-trial .lic-trial-state:empty { display: none; }
@media (max-width: 520px) { .lic-trial { grid-template-columns: 1fr; } }
.avatar {
  width: 32px; height: 32px; padding: 0; border: 1px solid var(--accent); border-radius: 50%;
  background: var(--accent-dim); color: var(--accent); font-weight: 600;
}
.account {
  position: absolute; top: 44px; right: 0; width: 188px; padding: 10px; z-index: 20;
  background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.account strong, .account span { display: block; }
.account strong { font-size: var(--fs-base); color: var(--text-strong); }
.account span { margin-top: 2px; color: var(--muted); font-size: var(--fs-sm); }
.account a {
  display: flex; align-items: center; justify-content: center; margin-top: 10px; height: 32px;
  border: 1px solid var(--line); border-radius: var(--radius); color: var(--text); font-size: var(--fs-md); font-weight: 600;
}
.account a:hover { background: var(--hover-strong); text-decoration: none; }
.main { flex: 1; min-height: 0; padding: 16px 20px 2rem; }
.shell:has(.live-screen) { height: 100vh; }
.main:has(.live-screen) { display: flex; padding: 0; overflow: hidden; }
.live-screen { flex: 1; height: auto; }

/* ---- Page template: every screen is .page > .toolbar + content ---- */
.screen-title {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); border: 0;
}
.page { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.toolbar h1 { margin: 0; font-size: var(--fs-num); }
.toolbar-end { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.list { display: flex; flex-direction: column; gap: 8px; }
.page > .card { max-width: 720px; }
.card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.card-grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.card-grid > .muted { grid-column: 1 / -1; margin: 0; }

/* ---- Chips (filters, tabs) ---- */
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chips button {
  height: 34px; padding: 0 12px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--muted); font-size: var(--fs-md); font-weight: 500;
}
.chips button:hover { background: var(--hover-strong); color: var(--text-strong); }
.chips button.on { background: var(--select-bg); border-color: var(--select-line); color: var(--text-strong); }
.chips button.on:hover { background: var(--select-bg-hover); }

/* ---- Buttons: .btn, .btn.primary, button.danger ---- */
.btn, .actions button {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 34px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: var(--radius); background: transparent; color: var(--text);
  font-size: var(--fs-md); font-weight: 600; white-space: nowrap;
}
.btn:hover, .actions button:hover { background: var(--hover-strong); text-decoration: none; }
.btn.primary { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--on-accent); }
.btn.primary:hover { background: var(--accent-fill-hover); border-color: var(--accent-fill-hover); color: var(--on-accent); }
/* A primary button that destroys (delete dialogs) is red, not orange. */
.btn.primary.danger { background: var(--danger-bg); border-color: var(--danger-line); color: var(--text-strong); }
.btn.primary.danger:hover:not(:disabled) { background: var(--danger-line); border-color: var(--danger-line); }
button.danger, .actions button.danger { background: var(--danger-bg); border-color: var(--danger-line); color: var(--text-strong); }
button.danger:hover, .actions button.danger:hover { background: var(--danger-line); }
.actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
.actions button { height: 28px; padding: 0 10px; font-size: var(--fs-sm); background: var(--bg); }

/* ---- Card and list item ---- */
.card, .panel, .item {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 14px;
}
.card { display: flex; flex-direction: column; gap: 10px; }
.card h2 { margin: 0; font-size: var(--fs-base); }
.item { display: grid; gap: 14px; align-items: center; padding: 10px 12px; }
.item:hover { background: var(--hover); }
.item > * { min-width: 0; }
.item-source { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) 180px auto; }
.item-scenario { display: flex; align-items: stretch; gap: 8px; padding: 0; }
.item-scenario > .actions { align-self: center; flex: none; padding-right: 12px; }
.item-name strong, .sc-row strong { display: block; font-size: var(--fs-base); font-weight: 600; color: var(--text-strong); }
.item-name .kind, .sc-row .kind {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: var(--fs-sm);
}
.item p { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-md); }
.panel { border-radius: var(--radius-lg); padding: 1rem; }
.panel h2 { margin: 0; font-size: var(--fs-base); }

/* ---- Status dot and tag ---- */
.status { display: flex; align-items: center; gap: 8px; font-size: var(--fs-md); }
.status.idle { color: var(--muted); }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); flex: none; }
.idle .dot { background: var(--muted); }
.tag {
  flex: none; height: 22px; padding: 0 8px; border-radius: 99px; border: 1px solid transparent;
  font-size: var(--fs-xs); font-weight: 600; display: inline-flex; align-items: center;
}
.tag.ok { color: var(--ok); border-color: #2a4a3c; }
.banner-warn {
  margin: 0; padding: 10px 14px; border-radius: var(--radius-lg); font-size: var(--fs-md);
  background: var(--warn-bg); border: 1px solid var(--warn); color: var(--warn);
}

/* ---- Sources: groups (areas) that fold, sources as a grid of cards ---- */
.tree { display: flex; flex-direction: column; gap: 8px; }
.tree-head { display: flex; align-items: center; gap: 8px; min-height: 34px; border-radius: var(--radius-lg); }
.tree-head:hover { background: var(--hover); }
.tree-toggle {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 8px;
  border: 0; background: transparent; color: var(--muted); text-align: left; cursor: pointer; border-radius: var(--radius-lg);
}
.tree-toggle:disabled { cursor: default; }
.tree-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tree-chevron { display: inline-flex; transition: transform 0.15s; transform: rotate(90deg); }
.tree.closed .tree-chevron { transform: none; }
.tree-toggle:disabled .tree-chevron { visibility: hidden; }
.tree-toggle strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-md); font-weight: 600; color: var(--text-strong); }
.tree-toggle .kind { flex: none; color: var(--muted); font-size: var(--fs-sm); }
.tree-alarm { flex: none; color: var(--bad); font-size: var(--fs-sm); }
.tree-head .tree-more { margin-left: auto; margin-right: 4px; flex: none; }
.tree-body { padding: 0 0 4px 30px; }
.tree.closed { gap: 0; }
.src-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(216px, 1fr)); gap: 10px; }
/* One source a card: picture · name and kind · state · actions (sources.js). */
.src-row { display: flex; flex-direction: column; align-items: stretch; gap: 6px; padding: 8px; }
.src-thumb {
  width: 100%; aspect-ratio: 16 / 9; display: grid; place-items: center; overflow: hidden;
  border-radius: var(--radius); background: var(--stage); color: var(--muted);
}
.src-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* Camera or file: a badge on the picture, each its own colour. */
.src-thumb { position: relative; }
.src-thumb-icon { display: grid; place-items: center; }
.src-badge {
  position: absolute; top: 6px; left: 6px; display: inline-flex; align-items: center; gap: 4px; height: 20px; padding: 0 7px;
  border-radius: 99px; font-size: var(--fs-xs); font-weight: 600; line-height: 1; color: #fff; background: rgb(0 0 0 / 62%);
  border: 1px solid currentColor;
}
.src-badge span { color: #fff; }
.src-badge.camera { color: var(--src-camera); }
.src-badge.file { color: var(--src-file); }
.src-sub .src-assign { font-size: var(--fs-sm); color: var(--text); text-decoration: underline dotted; text-underline-offset: 3px; }
.src-sub .src-assign:hover { color: var(--accent); }
.src-sub .src-assign.warn { color: var(--warn); }
.src-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.src-main .src-shot { font-size: var(--fs-sm); }
.shot-time { display: grid; grid-template-columns: minmax(0, 1fr) 90px; gap: 10px; align-items: end; }
.src-main strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-base); font-weight: 600; color: var(--text-strong); }
.src-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: var(--fs-sm); }
.src-state { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: var(--fs-sm); white-space: nowrap; }
.src-state > span { overflow: hidden; text-overflow: ellipsis; }
.src-count { color: var(--muted); font-size: var(--fs-sm); }
.src-row .actions { flex-wrap: nowrap; align-items: center; justify-content: flex-start; margin-top: auto; padding-top: 2px; }
.src-row .actions > .more { margin-left: auto; }
.src-row.deleting { opacity: 0.6; pointer-events: none; }
.src-row.deleting .src-state > span { color: var(--warn); }
@media (max-width: 640px) {
  .tree-body { padding-left: 0; }
  .src-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 8px; }
  .src-row .actions { flex-wrap: wrap; }
  .src-fold span { display: none; }
}
.actions button.primary { background: var(--accent-fill); border-color: var(--accent-fill); color: #fff; }
.actions button.primary:hover { background: var(--accent); }

/* "⋯" and the menu it opens (moreMenu in core.js). */
button.more {
  width: 30px; height: 28px; padding: 0; display: inline-grid; place-items: center; font-size: var(--fs-lg); line-height: 1;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); color: var(--text); cursor: pointer;
}
button.more:hover, button.more[aria-expanded="true"] { background: var(--hover-strong); }
.more-menu {
  position: fixed; z-index: 60; min-width: 200px; padding: 4px; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: 0 8px 24px rgb(0 0 0 / 40%);
}
.more-menu button {
  height: 32px; padding: 0 10px; text-align: left; border: 0; border-radius: var(--radius); background: none; color: var(--text); font-size: var(--fs-md); cursor: pointer;
}
.more-menu button:hover, .more-menu button:focus-visible { background: var(--hover-strong); outline: none; }
.more-menu button.danger { color: var(--bad); background: none; }
.more-menu button.danger:hover, .more-menu button.danger:focus-visible { background: var(--danger-bg); color: var(--text-strong); }
.more-menu hr { margin: 4px 0; border: 0; border-top: 1px solid var(--line); }
.confirm-text { margin: 0; line-height: 1.5; }
.confirm-text.warn { color: var(--bad); font-weight: 600; }
.delete-list { margin: 10px 0 14px; padding: 0; list-style: none; display: grid; gap: 6px; }
.delete-list li { display: flex; justify-content: space-between; gap: 12px; padding: 6px 10px; border-radius: var(--radius); background: var(--raised); border: 1px solid var(--line); }
.delete-list li strong { font-variant-numeric: tabular-nums; }

/* ---- Scenarios ---- */
.sc-row {
  flex: 1; min-width: 0; display: grid; grid-template-columns: 220px minmax(0, 1fr) 180px;
  gap: 16px; align-items: center; padding: 12px 14px;
}
/* Scenarios: a grid of cards (scenarios.js card), grouped by where they run in "Tất cả". */
.sc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 10px; }
.sc-item { display: flex; flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; }
.sc-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
.sc-top .src-main { flex: 1 1 auto; }
.sc-top .sc-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-item .mdl-card-facts dd.warn { color: var(--warn); }
.sc-item .mdl-card-facts dd > span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-foot { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 8px; border-top: 1px solid var(--line); }
.sc-foot .sc-where { flex: 1 1 auto; font-size: var(--fs-sm); }
.sc-foot .actions { flex: none; }
.sc-name { padding: 0; border: 0; background: none; font: inherit; font-size: var(--fs-base); font-weight: 600; color: var(--text-strong); cursor: pointer; text-align: left; }
.sc-name:hover { color: var(--accent-hover); text-decoration: underline; }
.sc-where { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: var(--fs-md); color: var(--text); }
.sc-where span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-where.idle { color: var(--muted); }
.sc-where.warn { color: var(--warn); }
.sc-top .actions { flex: none; align-self: flex-start; }
/* A scenario's steps top to bottom on a rail (scenarios.js stepFlow); the alert ends it. */
.sc-flow { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.sc-flow li { position: relative; display: flex; align-items: center; gap: 8px; min-height: 26px; min-width: 0; }
.sc-flow li + li::before { content: ""; position: absolute; left: 10px; top: -3px; height: 6px; width: 1px; background: var(--flow); }
.sc-node {
  flex: none; width: 21px; height: 21px; display: grid; place-items: center; border-radius: 50%;
  border: 1px solid var(--line); background: var(--raised); color: var(--accent);
}
.sc-flow li.end .sc-node { background: var(--accent-dim); border-color: var(--select-line); }
.sc-flow-text { display: flex; align-items: baseline; gap: 6px; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--fs-sm); color: var(--text); }
.sc-flow-text small { color: var(--muted); font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; }
.sc-slots { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; font-size: var(--fs-sm); }
.sc-slots > span:not(.muted) { display: inline-flex; align-items: center; gap: 6px; }
@media (max-width: 640px) { .sc-grid { grid-template-columns: minmax(0, 1fr); } }
.sc-sentence, .sc-slot { margin: 0; font-size: var(--fs-md); }
.sc-sentence { color: var(--muted); line-height: 1.45; }
.sc-slot { display: flex; flex-direction: column; gap: 4px; }
.sc-slot span { display: flex; align-items: center; gap: 8px; }
.sc-mark { width: 10px; height: 10px; border-radius: 50%; flex: none; display: block; }

/* ---- Search ---- */
.vw-search {
  height: 37px; width: 240px; max-width: 100%; display: flex; align-items: center; gap: 8px; padding: 0 10px;
  border: 1px solid var(--input-line); border-radius: var(--radius); background: var(--input); color: var(--muted);
}
.vw-search input { min-height: 0; flex: 1; min-width: 0; width: auto; height: 21px; margin: 0; padding: 0; border: 0; background: transparent; }
.vw-search input:focus { outline: none; }
.vw-search:focus-within { border-color: var(--accent); outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- Grids layout page ---- */
.toolbar > p { margin: 0; }
/* ---- Grid dialog (js/grid-editor.js) ---- */
.gd-top { display: grid; grid-template-columns: 1fr 80px 80px; gap: 10px; }
.gd-board {
  display: grid; gap: 2px; min-height: 160px; padding: 2px; background: var(--stage);
  border: 1px solid var(--line); border-radius: var(--radius-lg); touch-action: none; user-select: none;
}
.gd-region {
  display: grid; place-items: center; min-width: 0; padding: 4px; overflow: hidden; text-align: center;
  background: var(--panel); border: 1px solid var(--line); color: var(--muted); font-size: var(--fs-sm); cursor: crosshair;
}
.gd-region.has { color: var(--text); background: var(--raised); }
.gd-region.on { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.gd-drag { background: var(--accent-dim); border: 1px dashed var(--accent); pointer-events: none; }
.gd-cell { display: flex; gap: 10px; }
.viewer-grid-bar .cb { flex: none; width: 150px; }
.viewer-grid-bar .cb-btn { height: 29px; font-size: var(--fs-sm); }
.viewer-grid-bar .cb.on .cb-btn { background: var(--select-bg); border-color: var(--select-line); color: var(--text-strong); }
.viewer-grid-bar .cb.on .cb-btn:hover { background: var(--select-bg-hover); }
.viewer-grid-bar button.danger { background: var(--danger-bg); border-color: var(--danger-line); color: var(--text-strong); }
.viewer-grid-bar button:disabled { opacity: 0.4; }
.viewer-grid-bar { flex-wrap: wrap; overflow: visible; }
.viewer-sizes { align-items: center; }
.viewer-sizes-icon { display: inline-flex; padding: 0 6px 0 4px; color: var(--muted); }
.viewer-bar-end { display: flex; gap: 6px; margin-left: auto; }
.viewer-bar-end button { display: inline-flex; align-items: center; gap: 6px; }

/* ---- Report charts ---- */
.rpt-plot { display: grid; grid-template-columns: 2.5rem 1fr; gap: 0.4rem; min-height: 220px; }
.rpt-cols {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(28px, 1fr);
  gap: 0.35rem; align-items: end; height: 200px; border-bottom: 1px solid var(--line);
}
.rpt-stack { display: flex; flex-direction: column-reverse; width: 100%; height: 100%; }
.rpt-col { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; height: 100%; justify-content: flex-end; }
.rpt-col span { color: var(--muted); font-size: var(--fs-xs); }
.hbar { display: grid; grid-template-columns: 8rem 1fr 2.5rem; gap: 0.5rem; align-items: center; margin: 0.15rem 0; }
.hbar > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar i { display: block; height: 8px; border-radius: 99px; background: var(--line); }
.hbar i b { display: block; height: 100%; border-radius: 99px; background: var(--zone-c); }
.legend { display: flex; gap: 0.8rem; flex-wrap: wrap; color: var(--muted); font-size: var(--fs-sm); }
.legend i { display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 2px; margin-right: 0.3rem; vertical-align: -1px; }
.rpt-range { display: flex; align-items: center; gap: 6px; }
.rpt-range[hidden] { display: none; }
.rpt-note { margin: -4px 0 0; font-size: var(--fs-sm); }
.page[aria-busy="true"] { opacity: 0.55; transition: opacity 0.15s; }
.rpt-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.rpt-kpi { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rpt-kpi > span:first-child { font-size: var(--fs-sm); }
.rpt-kpi strong { font-size: var(--fs-num); color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rpt-kpi > span:last-child { font-size: var(--fs-sm); min-height: 1.2em; }
.rpt-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); gap: 12px; align-items: start; }
.rpt-section { margin: 4px 0 0; font-size: var(--fs-base); }
.rpt-grid h3 { margin: 0; font-size: var(--fs-md); }
/* One card per camera: its scenario, headline figures, chart by class, lists and counters. */
.rpt-cams { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 520px), 1fr)); gap: 12px; align-items: start; }
.rpt-cam { min-width: 0; }
.rpt-cam-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.rpt-cam-head > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rpt-cam-head h3 { margin: 0; font-size: var(--fs-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rpt-cam-head .muted { font-size: var(--fs-sm); }
.rpt-cam-head .btn { flex: none; }
.rpt-cam-kpis { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.rpt-cam-kpis .rpt-kpi { padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius); }
.rpt-cam-kpis .rpt-kpi strong { font-size: var(--fs-lg); }
.rpt-cam .rpt-plot { min-height: 150px; }
.rpt-cam-lists { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
.rpt-cam-lists > .panel { padding: 0; border: 0; background: none; box-shadow: none; }
.rpt-cam-lists > .panel h2 { margin: 0 0 6px; font-size: var(--fs-sm); color: var(--muted); font-weight: 600; }
.rpt-cam-lists > .panel.stack { padding: 10px; border: 1px solid var(--line); border-radius: var(--radius); }
.rpt-warn { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px; border-radius: var(--radius);
  background: var(--warn-bg); color: var(--text); border: 1px solid var(--warn); font-size: var(--fs-sm); }
button.rpt-col, button.hbar { padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
button.hbar { width: 100%; border-radius: 4px; }
button.hbar:hover, button.rpt-col:hover { background: var(--hover); }
button.rpt-col:hover .rpt-stack { filter: brightness(1.15); }
@media (max-width: 767px) {
  .rpt-cols.dense { grid-auto-columns: minmax(0, 1fr); gap: 2px; }
  .rpt-cols.dense .rpt-col span:not(.tick) { visibility: hidden; }
  .rpt-cols.dense .rpt-col span { white-space: nowrap; }
}

/* ---- Events ---- */
.event-detail { display: flex; flex-wrap: wrap; gap: 8px; }
.event-detail img, .event-detail video { max-width: 100%; max-height: 60vh; border-radius: var(--radius); }
.event-detail video { width: 100%; background: var(--stage); }
.event-actions { justify-content: flex-start; margin-top: 8px; }
/* ---- Event dialog: the picture on the left, who/what and tracing on the right ---- */
.ev { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 16px; align-items: start; }
.ev-media { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ev-media-tabs { align-self: flex-start; }
.ev-media-body { position: relative; }
.ev-to-clip { position: absolute; left: 8px; bottom: 8px; display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px;
  border: 1px solid rgb(255 255 255 / 24%); border-radius: 99px; background: rgb(0 0 0 / 62%); color: #fff; font-size: var(--fs-sm); font-weight: 500; cursor: pointer; backdrop-filter: blur(4px); }
.ev-to-clip:hover, .ev-to-clip:focus-visible { background: var(--accent); border-color: var(--accent); outline: none; }
.ev-clip { display: flex; flex-direction: column; gap: 8px; }
.ev-clip-stage { position: relative; border-radius: var(--radius); overflow: hidden; background: var(--stage); }
.ev-clip-stage video { display: block; width: 100%; max-height: 58vh; object-fit: contain; background: #000; }
.ev-clip:fullscreen .ev-clip-stage video { max-height: calc(100vh - 110px); }
.ev-clip:fullscreen { justify-content: center; padding: 16px; background: #000; }
.ev-clip-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; margin: 0; color: #fff; font-size: var(--fs-sm); background: rgb(0 0 0 / 50%); }
.ev-clip-note.bad { color: var(--danger, #e5484d); }
.ev-clip-bar { display: flex; align-items: center; gap: 8px; }
.ev-clip-btn { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); color: var(--text); cursor: pointer; }
.ev-clip-btn:hover { background: var(--hover-strong); color: var(--text-strong); }
.ev-clip-time { flex: none; min-width: 38px; color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; text-align: center; }
.ev-clip-track { position: relative; flex: 1; min-width: 80px; height: 26px; cursor: pointer; touch-action: none; }
.ev-clip-track::before { content: ""; position: absolute; left: 0; right: 0; top: 11px; height: 4px; border-radius: 99px; background: var(--line); }
.ev-clip-track:focus-visible { outline: 2px solid var(--select-line); outline-offset: 2px; border-radius: 4px; }
.ev-clip-span { position: absolute; top: 9px; height: 8px; border-radius: 99px; background: var(--accent); opacity: 0.55; }
.ev-clip-mark { position: absolute; top: 5px; width: 10px; height: 10px; margin-left: -5px; transform: translateY(3px) rotate(45deg); background: var(--accent); border: 2px solid var(--panel, #191a1b); box-sizing: border-box; }
.ev-clip-head { position: absolute; top: 4px; width: 4px; height: 18px; margin-left: -2px; border-radius: 2px; background: var(--text-strong); box-shadow: 0 0 0 2px rgb(0 0 0 / 45%); pointer-events: none; }
.ev-clip-legend { display: flex; flex-wrap: wrap; gap: 6px; }
.ev-clip-moment { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; border: 1px solid var(--line); border-radius: 99px; background: var(--bg); color: var(--muted); font-size: var(--fs-xs); cursor: pointer; }
.ev-clip-moment:hover { border-color: var(--select-line); color: var(--text-strong); }
.ev-clip-moment strong { color: var(--text-strong); font-variant-numeric: tabular-nums; }
.ev-clip-moment i { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.ev-clip-moment.appear i, .ev-clip-moment.leave i { border-radius: 2px; opacity: 0.55; }
.ev-clip-moment.event i { border-radius: 1px; transform: rotate(45deg); }
.ev-media-body > img, .ev-media-body > video { display: block; width: 100%; max-height: 64vh; object-fit: contain; border-radius: var(--radius); background: var(--stage); }
.ev-scene { position: relative; border-radius: var(--radius); overflow: hidden; background: var(--stage); }
.ev-scene canvas { display: block; width: 100%; height: auto; max-height: 64vh; object-fit: contain; touch-action: none; }
.ev-scene.zoomed canvas { cursor: grab; }
.ev-scene.dragging canvas { cursor: grabbing; }
.ev-scene-tools { position: absolute; right: 8px; bottom: 8px; display: flex; gap: 2px; padding: 2px; border: 1px solid rgb(255 255 255 / 20%);
  border-radius: var(--radius); background: rgb(0 0 0 / 60%); backdrop-filter: blur(4px); }
.ev-scene-tools button { min-width: 28px; height: 26px; padding: 0 8px; border: 0; border-radius: 4px; background: transparent; color: #fff; font-size: var(--fs-sm); }
.ev-scene-tools button:hover:not(:disabled) { background: rgb(255 255 255 / 16%); }
.ev-scene-tools button:disabled { opacity: 0.4; }
.ev-side { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.ev-id { display: flex; flex-direction: column; gap: 8px; padding: 12px; border: 1px solid var(--select-line); border-radius: var(--radius); background: var(--select-bg); }
.ev-id-title { color: var(--muted); font-size: var(--fs-sm); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.ev-id-pic { width: 100%; max-height: 200px; object-fit: contain; border-radius: 4px; background: var(--stage); }
.ev-id-value { color: var(--text-strong); font-size: 26px; font-weight: 700; line-height: 1.15; overflow-wrap: anywhere; }
/* A plate reads like one: dark letters on a white plate. */
.ev-id.vehicle .ev-id-value { align-self: flex-start; padding: 4px 12px; border: 2px solid #111; border-radius: 6px; background: #f4f4f2; color: #111;
  font-family: var(--mono); letter-spacing: 0.06em; }
.ev-id-empty { color: var(--muted); font-size: var(--fs-md); font-style: italic; }
.ev-id-score { color: var(--muted); font-size: var(--fs-sm); }
.ev-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 6px 12px; margin: 0; font-size: var(--fs-md); }
.ev-facts dt { color: var(--muted); }
.ev-facts dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
.ev-attr { display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; background: none; color: var(--text); font: inherit; cursor: pointer; text-align: left; }
.ev-attr:hover { color: var(--accent); text-decoration: underline; }
.ev-swatch { display: inline-block; flex: none; width: 12px; height: 12px; border-radius: 50%; box-shadow: 0 0 0 1px var(--line); vertical-align: -1px; margin-right: 6px; }
.ev-attr .ev-swatch { margin-right: 0; }
.ev-trace-pick { display: flex; flex-direction: column; gap: 8px; }
.ev-trace-buttons { display: flex; flex-wrap: wrap; gap: 6px; }
.ev-trace-buttons .btn.on { background: var(--select-bg); border-color: var(--select-line); color: var(--text-strong); }
.ev-side-actions { display: flex; flex-wrap: wrap; gap: 6px; padding-top: 12px; border-top: 1px solid var(--line); }
.ev-side-actions .more { margin-left: auto; }
.ev-trace { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.ev-trace h3 { margin: 0 0 8px; font-size: var(--fs-md); color: var(--text-strong); }
.ev-trace .vw-events-grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
@media (max-width: 760px) { .ev { grid-template-columns: 1fr; } }

/* ---- System / settings ---- */
.remote-ids { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); gap: 8px 32px; }
.remote-ids span { display: block; font-size: var(--fs-sm); }
.remote-ids strong { display: block; font-size: var(--fs-code); font-weight: 600; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; color: var(--text-strong); }
.remote-card .actions { justify-content: flex-start; }
.gauge > span { color: var(--muted); font-size: var(--fs-sm); }
.gauge strong { display: block; margin-top: 6px; font-size: var(--fs-num); font-weight: 600; color: var(--text-strong); }
.table-card { padding: 0; overflow: auto; gap: 0; }
.table-card table { width: 100%; border-collapse: collapse; }
.table-card th, .table-card td { padding: 10px 14px; text-align: left; font-size: var(--fs-md); }
.table-card th { color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
.table-card tr + tr td { border-top: 1px solid var(--line); }

/* ---- Forms: .form, .form-grid, .form-actions, .check ---- */
.form { display: grid; grid-template-columns: minmax(0, 560px); gap: 12px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.form-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
.form-actions .push-left { margin-right: auto; }
label { display: flex; flex-direction: column; gap: 0.25rem; color: var(--muted); font-size: var(--fs-sm); }
.check, .dlg-check { flex-direction: row; align-items: center; justify-content: flex-start; gap: 8px; color: var(--text); font-size: var(--fs-md); }
.form .check input, .dlg-body .check input, .dlg-body .dlg-check input { width: auto; flex: none; }
.dlg-body input, .dlg-body select, .dlg-body textarea,
.form input, .form select, .form textarea { width: 100%; }
.cb { position: relative; display: block; }
.cb-btn { width: 100%; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 400; }
.cb-native { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; pointer-events: none; }
a.btn { width: fit-content; }
input, select, textarea, .cb-btn {
  color: var(--text); background: var(--input); border: 1px solid var(--input-line); border-radius: var(--radius); padding: 0.4rem 0.6rem;
}
input[type=checkbox], input[type=radio] { accent-color: var(--accent); }
textarea { min-height: 120px; resize: vertical; font-family: var(--mono); }
/* The ring is drawn inside the field, over its border, so a scrolling dialog or panel cannot cut its sides. */
input:focus-visible, textarea:focus-visible, select:focus-visible, .cb-btn:focus-visible, .cb-btn[aria-expanded="true"] { outline: 2px solid var(--accent); outline-offset: -2px; border-color: var(--accent); }
select:hover, .cb-btn:hover, input:hover, textarea:hover { border-color: var(--input-line-hover); background-color: var(--input-hover); }
input:disabled, select:disabled, textarea:disabled, .cb-btn:disabled { opacity: 0.5; cursor: not-allowed; background-color: var(--bg); }
select, .cb-btn {
  appearance: none; height: 34px; padding-right: 2rem; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%239d9d9d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.55rem center; background-size: 16px;
}
/* Open list of a combobox (js/combobox.js): same surfaces as menus and dialogs. */
.cb-list {
  position: fixed; z-index: 60; margin: 0; padding: 4px; list-style: none; overflow: auto;
  background: var(--raised); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.cb-item { padding: 6px 10px; border-radius: var(--radius); color: var(--text); font-size: var(--fs-md); cursor: pointer; white-space: nowrap; }
.cb-item.hot { background: var(--hover-strong); color: var(--text-strong); }
.cb-item.on { background: var(--accent-dim); color: var(--text-strong); }
.cb-item.on { background: var(--select-bg); }
.cb-item.on.hot { background: var(--select-bg-hover); }
.cb-item.off { opacity: 0.5; cursor: not-allowed; }
input:not([type=checkbox]):not([type=radio]) { min-height: 34px; }

.live-screen { display: flex; height: 100%; min-height: 0; background: var(--bg); }
.live-cams {
  width: 280px; flex: none; display: flex; flex-direction: column; gap: 8px;
  padding: 13px; background: var(--panel); border-right: 1px solid var(--line); overflow: auto;
}
.live-cams .vw-search { width: auto; }
.vw-heading { margin: 6px 0 0; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
.vw-cam {
  display: flex; align-items: center; gap: 8px; padding: 8px; width: 100%;
  border: 0; border-left: 3px solid transparent; border-radius: 8px;
  background: var(--bg); color: var(--text); text-align: left;
}
.vw-cam:hover { background: var(--hover); }
.vw-cam.on { border-left-color: var(--accent); background: var(--accent-dim); }
.vw-cam.file { cursor: pointer; }
.vw-cam strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-md); }
.vw-cam span { display: block; color: var(--muted); font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vw-mosaic { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; padding: 12px; }
.vw-stage { position: relative; flex: 1; min-height: 0; background: #000; border-radius: 4px; overflow: hidden; }
.vw-placeholder {
  position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); font-size: var(--fs-md); padding: 16px; text-align: center;
}
.stage { position: absolute; inset: 0; background: #000; }
.stage video, .stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.stage video { object-fit: contain; }
.stage canvas { pointer-events: none; }

.shell:has(.viewer-screen) { height: 100vh; }
.main:has(.viewer-screen) { display: flex; padding: 0; overflow: hidden; }
.viewer-screen {
  flex: 1; min-width: 0; min-height: 0; display: grid;
  grid-template-columns: 280px minmax(360px, 1fr); background: var(--bg);
}
/* Filters belong to the event list: the column only opens in Sự kiện. */
.viewer-screen.events-mode { grid-template-columns: 280px minmax(360px, 1fr) 264px; }
.viewer-screen:not(.events-mode) .viewer-filters { display: none; }
.viewer-sources, .viewer-filters {
  min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 10px;
  padding: 13px; background: var(--panel); overflow: auto;
}
.viewer-sources { border-right: 1px solid var(--line); overflow: hidden; gap: 8px; padding: 12px 8px 8px; }
.viewer-sources > .ws-pick, .viewer-sources > .viewer-source-title, .viewer-source-sum, .viewer-sources > .vw-search { margin-inline: 4px; }
.viewer-source-title { margin-block: 0; font-size: var(--fs-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viewer-sources > .ws-pick > span { display: none; }
.viewer-sources > .ws-pick { color: var(--text-strong); }
.viewer-sources > .ws-pick > :last-child { flex: 1; min-width: 0; width: 100%; max-width: none; font-weight: 600; font-size: var(--fs-base); }
.viewer-source-sum { margin-block: -2px 2px; color: var(--muted); font-size: var(--fs-xs); }
.viewer-sources > .vw-search { flex: none; width: auto; }
.viewer-sources > .vw-search input { height: 32px; font-size: var(--fs-md); }
.viewer-filters { border-left: 1px solid var(--line); }
.viewer-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.viewer-panel-head h2 { margin: 0; font-size: var(--fs-base); }
.viewer-panel-head > span { color: var(--muted); font-size: var(--fs-xs); white-space: nowrap; }
.viewer-grid-bar { display: flex; align-items: center; gap: 6px; min-width: 0; overflow-x: auto; }
.viewer-grid-bar > span { flex: none; margin-right: 3px; color: var(--muted); font-size: var(--fs-sm); }
.viewer-grid-bar button {
  flex: none; height: 29px; padding: 0 10px; border: 1px solid var(--line); border-radius: 6px;
  background: var(--panel); color: var(--muted); font-size: var(--fs-sm); font-weight: 500;
}
.viewer-grid-bar button:hover { background: var(--hover); color: var(--text); }
.viewer-grid-bar button.on { background: var(--select-bg); border-color: var(--select-line); color: var(--text-strong); }
.viewer-source-list { flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; min-height: 0; overflow: hidden auto; scrollbar-gutter: auto; }
.viewer-source-list .vw-heading { margin-top: 8px; }
.viewer-source-list .vw-cam {
  flex: none; min-height: 48px; gap: 10px; padding: 5px 8px 5px 5px; border-radius: var(--radius); background: transparent;
}
/* Hover and chosen like the other screens' side lists (after the transparent above, which hid them). */
.viewer-source-list .vw-cam { transition: background 0.1s; }
.viewer-source-list .vw-cam:hover { background: var(--hover-strong); }
/* Chosen: only the orange bar on the left, no tinted background. */
.viewer-source-list .vw-cam.on { background: transparent; border-left-color: var(--select-mark); }
.viewer-source-list .vw-cam.on:hover { background: var(--hover-strong); }
.viewer-source-list .viewer-source-thumb {
  width: 64px; height: 36px; flex: none; display: grid; place-items: center; border-radius: 4px;
  background: var(--raised); color: var(--muted);
}
.viewer-source-list .viewer-source-thumb.file { background: var(--raised); color: var(--accent); }
.viewer-source-list .viewer-source-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 2px; }
.viewer-source-list .viewer-source-meta strong { color: var(--text-strong); font-size: var(--fs-md); font-weight: 500; }
.viewer-source-list .viewer-source-line { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.viewer-source-line .vw-state { flex: 0 1 auto; min-width: 0; }
.viewer-source-line .vw-state > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.viewer-source-line .viewer-source-stat { flex: none; }
.viewer-source-line .viewer-source-stat:empty { display: none; }
.viewer-source-list .viewer-source-meta span { color: var(--muted); font-size: var(--fs-xs); }
.viewer-center { min-width: 0; min-height: 0; display: flex; flex-direction: column; padding: 12px; gap: 10px; }
.viewer-center-head { min-height: 42px; display: flex; flex-direction: column; justify-content: center; }
.viewer-center-head strong { font-size: var(--fs-lg); }
.viewer-center-head span { font-size: var(--fs-sm); }
.viewer-center-body { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; }
.viewer-stage { position: relative; display: flex; min-width: 0; min-height: 0; align-self: center; justify-self: center; }
.viewer-stage > .vw-stage { flex: 1; }
/* A tall, narrow column: the picture goes under the title, not halfway down. */
@media (orientation: portrait) { .viewer-stage { align-self: start; } }
/* Drop target over the mosaic (grids you can edit). Cells show while a camera is dragged or the grid is empty. */
.vw-board { position: absolute; z-index: 3; display: grid; gap: 0; user-select: none; }
/* Cells apart: each cell draws a 2px edge of the page colour all round, so two cells
   meet at a 4px dark gap that shows on bright and dark pictures alike (a light hairline
   was lost in the video). One camera on the whole picture has none. */
.vw-board .vw-cell { --gap: var(--bg); box-shadow: inset 0 0 0 2px var(--gap); transition: background 0.1s; }
.vw-board .vw-cell.single { box-shadow: none; }
.vw-board .vw-cell:hover, .vw-board .vw-cell.edge-t:hover, .vw-board .vw-cell.edge-l:hover { box-shadow: inset 0 0 0 2px var(--gap), inset 0 0 0 3px var(--accent); }
.vw-board.empty .vw-cell, .vw-board.empty .vw-cell.edge-t, .vw-board.empty .vw-cell.edge-l,
.viewer-screen.dragging .vw-board .vw-cell, .viewer-screen.dragging .vw-board .vw-cell.edge-t, .viewer-screen.dragging .vw-board .vw-cell.edge-l {
  box-shadow: inset 0 0 0 1px var(--muted); background: rgb(0 0 0 / 35%);
}
.vw-board .vw-cell.hot, .vw-board .vw-cell.edge-t.hot, .vw-board .vw-cell.edge-l.hot { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-dim); }
.vw-board-hint {
  display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); margin: 0; padding: 6px 12px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); pointer-events: none;
}
.viewer-screen.dragging .vw-board-hint { display: none; }
.vw-cam[draggable=true] { cursor: grab; }
.viewer-source-list .vw-cam:active { cursor: grabbing; }
.viewer-empty { padding: 8px 4px; font-size: var(--fs-sm); }
/* Filters: folding sections, each with its count of filters on. */
.viewer-filters { padding: 0; gap: 0; }
.viewer-filters > .viewer-panel-head { position: sticky; top: 0; z-index: 1; padding: 12px 13px 10px; background: var(--panel); border-bottom: 1px solid var(--line); }
.viewer-filters > .viewer-panel-head h2 { margin-right: auto; }
.vf-active { color: var(--muted); font-size: var(--fs-xs); }
.vf-clear { border: 0; background: none; padding: 0; color: var(--accent); font-size: var(--fs-sm); cursor: pointer; }
.vf-clear:hover { text-decoration: underline; }
.viewer-filter-fields { display: flex; flex-direction: column; }
.viewer-filter-fields label { display: flex; flex-direction: column; gap: 5px; color: var(--muted); font-size: var(--fs-sm); }
.viewer-filter-fields input, .viewer-filter-fields select { width: 100%; font-size: var(--fs-sm); }
.vf-section { border-bottom: 1px solid var(--line); }
.vf-section > summary {
  display: flex; align-items: center; gap: 6px; padding: 10px 13px; cursor: pointer; list-style: none;
  color: var(--text-strong); font-size: var(--fs-sm); font-weight: 600; user-select: none;
}
.vf-section > summary::-webkit-details-marker { display: none; }
.vf-section > summary::after {
  content: ""; margin-left: auto; width: 6px; height: 6px; border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: rotate(-45deg); transition: transform 0.15s;
}
.vf-section[open] > summary::after { transform: rotate(45deg); }
.vf-section > summary:hover { background: var(--hover); }
.vf-badge { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 99px; background: var(--select-bg); box-shadow: inset 0 0 0 1px var(--select-line); color: var(--text-strong); font-size: var(--fs-xs); line-height: 18px; text-align: center; }
.vf-body { display: flex; flex-direction: column; gap: 10px; padding: 0 13px 12px; }
.vf-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.vf-chip {
  display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 8px; max-width: 100%;
  border: 1px solid var(--line); border-radius: 99px; background: var(--panel); color: var(--text); font-size: var(--fs-sm); cursor: pointer;
}
.vf-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vf-chip small { color: var(--muted); font-size: var(--fs-xs); }
.vf-chip:hover { background: var(--hover-strong); color: var(--text-strong); }
.vf-chip.on { background: var(--select-bg); border-color: var(--select-line); color: var(--text-strong); }
.vf-chip.on:hover { background: var(--select-bg-hover); }
.vf-chip.none { opacity: 0.55; }
.vf-more { border: 0; background: none; padding: 0 4px; color: var(--accent); font-size: var(--fs-sm); cursor: pointer; }
.vf-swatches { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px 4px; }
.vf-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 2px 0; min-width: 0;
  border: 0; background: none; color: var(--muted); font-size: var(--fs-xs); cursor: pointer;
}
.vf-swatch i { width: 24px; height: 24px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgb(255 255 255 / 18%), 0 0 0 1px var(--line); }
.vf-swatch span { max-width: 100%; text-align: center; line-height: 1.2; overflow-wrap: anywhere; }
.vf-swatch:hover { color: var(--text-strong); }
.vf-swatch.on { color: var(--text-strong); }
.vf-swatch.on i { box-shadow: inset 0 0 0 1px rgb(255 255 255 / 18%), 0 0 0 2px var(--panel), 0 0 0 4px var(--accent); }
.vf-swatch.none i { opacity: 0.35; }
.vf-check { flex-direction: row !important; align-items: center; gap: 6px !important; margin-top: -4px; font-size: var(--fs-xs) !important; }
.vf-check input { width: auto !important; margin: 0; }
.vf-search-wrap { display: flex; flex-direction: column; gap: 3px; }
.vf-search { position: relative; display: flex; }
.vf-search input { padding-right: 30px; }
.vf-search-go {
  position: absolute; right: 3px; top: 50%; transform: translateY(-50%); display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border: 0; border-radius: var(--radius); background: transparent; color: var(--muted); cursor: pointer;
}
.vf-search-go:hover { background: var(--hover-strong); color: var(--text-strong); }
.vf-typed-hint { color: var(--warn); }
/* Date picker (js/datepicker.js): the console's own, not the browser's. */
.dp-trigger {
  display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 10px; min-width: 0;
  border: 1px solid var(--input-line); border-radius: var(--radius); background: var(--input);
  color: var(--text-strong); font: inherit; font-size: var(--fs-sm); text-align: left; cursor: pointer;
}
.side-tabs .dp-trigger, .rpt-side .dp-trigger {
  height: 32px; padding: 0 10px; border: 1px solid var(--input-line); border-radius: var(--radius); background: var(--input); color: var(--text-strong);
}
.dp-trigger svg { flex: none; color: var(--muted); }
.dp-trigger span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dp-trigger.empty span { color: var(--muted); }
.dp-trigger:hover { border-color: var(--input-line-hover); }
.dp-trigger[aria-expanded="true"] { border-color: var(--accent); }
.dp-pop {
  position: fixed; z-index: 1000; width: 264px; padding: 10px; display: flex; flex-direction: column; gap: 8px;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); color: var(--text);
  box-shadow: 0 12px 32px rgb(0 0 0 / 45%); font-size: var(--fs-sm);
}
.dp-head { display: flex; align-items: center; justify-content: space-between; }
.dp-head strong { color: var(--text-strong); font-size: var(--fs-md); text-transform: capitalize; }
.dp-nav { width: 28px; height: 28px; border: 0; border-radius: var(--radius); background: none; color: var(--text); font-size: 18px; cursor: pointer; }
.dp-nav:hover { background: var(--hover-strong); }
.dp-week, .dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.dp-week span { text-align: center; color: var(--muted); font-size: var(--fs-xs); padding: 2px 0; }
.dp-day {
  height: 30px; border: 1px solid transparent; border-radius: var(--radius); background: none; color: var(--text);
  font: inherit; font-variant-numeric: tabular-nums; cursor: pointer;
}
.dp-day:hover { background: var(--hover-strong); color: var(--text-strong); }
.dp-day.out { color: var(--muted); opacity: 0.55; }
.dp-day.today { border-color: var(--line); color: var(--text-strong); }
.dp-day.range { background: var(--select-bg); border-radius: 0; }
.dp-day.end { border-color: var(--select-line); }
.dp-day.on { background: var(--select-bg-hover); border-color: var(--accent); color: var(--text-strong); font-weight: 600; }
.dp-time { display: flex; align-items: center; gap: 6px; padding-top: 6px; border-top: 1px solid var(--line); }
.dp-time span { margin-right: auto; color: var(--muted); }
.dp-time input { width: 40px; height: 28px; text-align: center; font-variant-numeric: tabular-nums; }
.dp-foot { display: flex; align-items: center; gap: 12px; padding-top: 6px; border-top: 1px solid var(--line); }
.dp-link { border: 0; background: none; padding: 0; color: var(--accent); font: inherit; cursor: pointer; }
.dp-link:hover { text-decoration: underline; }
.dp-done { margin-left: auto; height: 28px; }
.vf-hint { margin: 0; color: var(--muted); font-size: var(--fs-xs); }
.vf-picture { display: inline-flex; align-items: center; justify-content: center; gap: 6px; margin: 12px 13px; }

.vw-modal {
  position: fixed; inset: 51px 0 0; z-index: 30; display: flex; align-items: center; justify-content: center;
  background: rgb(0 0 0 / 55%);
}
.vw-dialog {
  width: min(520px, calc(100vw - 32px)); display: flex; flex-direction: column; max-height: calc(100vh - 96px);
  overflow: hidden; padding: 16px 16px 0; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px rgb(0 0 0 / 45%);
}
.vw-dialog.wide { width: min(720px, calc(100vw - 32px)); }
.vw-dialog.wide.event-dialog { width: min(1120px, calc(100vw - 32px)); }
/* Search by picture: the picture and its steps on the left, what was found on the right. */
.vw-dialog.wide.picture-dialog { width: min(900px, calc(100vw - 32px)); }
.ps { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 20px; min-height: 360px; }
.ps-left { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.ps-right { display: flex; flex-direction: column; gap: 10px; min-width: 0; max-height: 70vh; overflow: auto; }
.ps-title { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.ps-found { display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-sm); }
.ps-card {
  display: grid; grid-template-columns: 64px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 8px;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--bg);
}
.ps-card.on { border-color: var(--select-line); background: var(--select-bg); }
.ps-card.none { opacity: 0.7; }
.ps-card-hint { color: var(--accent); font-size: var(--fs-xs); text-decoration: none; }
.ps-card-hint:hover { text-decoration: underline; }
.ps-card img, .ps-card-pic { width: 64px; height: 48px; object-fit: contain; border-radius: var(--radius); background: #000; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.ps-card-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ps-card-text strong { color: var(--text-strong); font-size: var(--fs-base); overflow-wrap: anywhere; }
.ps-drop {
  position: relative; display: flex; align-items: center; justify-content: center; min-height: 220px; padding: 10px;
  border: 1.5px dashed var(--line-strong, var(--line)); border-radius: var(--radius-lg); background: var(--bg); cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.ps-drop:hover, .ps-drop:focus-visible { border-color: var(--select-line); outline: none; }
.ps-drop.over { border-color: var(--accent); background: var(--select-bg); }
.ps-drop.has-picture { border-style: solid; min-height: 0; padding: 6px; }
.ps-empty { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; font-size: var(--fs-sm); }
.ps-empty strong { color: var(--text-strong); font-size: var(--fs-base); }
.ps-empty u { color: var(--accent); text-decoration: none; }
.ps-drop-icon { color: var(--muted); }
.ps-formats { font-size: var(--fs-xs); }
.ps-picture { position: relative; max-width: 100%; }
.ps-picture img { display: block; max-width: 100%; max-height: 340px; border-radius: var(--radius); }
.ps-boxes { position: absolute; inset: 0; pointer-events: none; }
.ps-box { position: absolute; border: 2px solid var(--accent); border-radius: 3px; }
.ps-box::after {
  content: attr(data-label); position: absolute; left: -2px; bottom: 100%; padding: 1px 5px; border-radius: 3px 3px 0 0;
  background: var(--accent); color: #fff; font-size: var(--fs-xs); white-space: nowrap;
}
.ps-box.face { border-color: var(--zone-b); }
.ps-box.face::after { background: var(--zone-b); }
.ps-box.plate { border-color: var(--zone-c); }
.ps-box.plate::after { background: var(--zone-c); }
.ps-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-sm); }
.ps-step { display: grid; grid-template-columns: 18px auto minmax(0, 1fr); align-items: center; gap: 8px; color: var(--text); }
.ps-step i { display: inline-flex; justify-content: center; color: var(--muted); font-style: normal; }
.ps-step i .vw-spinner { width: 13px; height: 13px; border-width: 2px; }
.ps-step.done i { color: var(--ok); }
.ps-step.fail i { color: var(--bad); }
.ps-step.skip { color: var(--muted); }
.ps-step small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; }
.ps-wait { margin: auto; max-width: 360px; text-align: center; }
.ps-tabs small { color: var(--muted); }
.viewer-events-processing { display: flex; flex-direction: column; gap: 8px; margin: 0 0 10px; }
.viewer-events-run { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border: 1px solid var(--select-line); border-radius: var(--radius); background: var(--select-bg); }
.viewer-events-run-text { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; font-size: var(--fs-sm); }
.viewer-events-run-text strong { color: var(--text-strong); font-variant-numeric: tabular-nums; }
.viewer-events-run-text .muted { font-variant-numeric: tabular-nums; }
.viewer-events-run .vw-progress { height: 6px; }
.viewer-events-context {
  display: flex; align-items: center; gap: 10px; padding: 6px 8px; border: 1px solid var(--select-line); border-radius: var(--radius-lg);
  background: var(--select-bg); color: var(--text-strong); font-size: var(--fs-md);
}
.viewer-events-context img { width: 40px; height: 40px; object-fit: cover; border-radius: var(--radius); flex: none; }
.viewer-events-context span { flex: 1; min-width: 0; }
.viewer-events-context .btn { flex: none; }
.vw-event-badge.likeness { background: var(--accent); color: #fff; }
.vw-event-nopic { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; min-height: 60px; color: var(--muted); font-size: var(--fs-xs); background: var(--bg); }
@media (max-width: 760px) {
  .ps { grid-template-columns: 1fr; min-height: 0; }
  .ps-right { max-height: none; }
}
.vw-dialog header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.vw-dialog h2 { margin: 0; font-size: var(--fs-lg); }
.dlg-body { display: flex; flex-direction: column; gap: 10px; flex: 1; min-height: 0; overflow: auto; padding-bottom: 12px; }
/* A form taller than the window scrolls; its buttons keep their height. */
.dlg-body > .btn { flex: none; }
.vw-dialog .err { padding-bottom: 8px; }
.vw-dialog footer {
  display: flex; justify-content: flex-end; gap: 8px; flex: none; margin: 0 -16px; padding: 12px 16px;
  border-top: 1px solid var(--line); background: var(--panel);
}
.vw-dialog footer .btn { height: 34px; }
.vw-dialog footer .cancel { background: transparent; }
.vw-dialog .close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--radius); background: transparent;
  color: var(--muted);
}
.vw-dialog .close:hover { background: var(--hover-strong); color: var(--text-strong); }
.dlg-body .vw-heading { margin: 12px 0 2px; }

/* ---- Choice cards (dialog pickers) ---- */
.choice-list { display: flex; flex-direction: column; gap: 8px; }
.choice-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.choice-list button, .choice-grid button {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-height: 56px; height: auto;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--bg);
  color: var(--text); text-align: left; font-weight: 600;
}
.choice-list button { min-height: 72px; padding: 12px 14px; }
.choice-list button:hover, .choice-grid button:hover { background: var(--hover); }
.choice-list button:disabled { opacity: 0.5; cursor: not-allowed; }
.choice-list button:disabled:hover { background: var(--bg); }
.tpl-soon { margin-left: 6px; color: var(--muted); font-size: var(--fs-sm); font-weight: 400; }
.choice-grid button.on { border-color: var(--accent); background: var(--accent-dim); }
.choice-list strong { font-size: var(--fs-base); }
.choice-grid span, .choice-text > span { color: var(--muted); font-size: var(--fs-sm); font-weight: 400; }
.choice-list button:has(.choice-icon) { flex-direction: row; align-items: center; gap: 14px; }
.choice-icon {
  flex: none; width: 44px; height: 44px; display: grid; place-items: center;
  border-radius: var(--radius-lg); background: var(--accent-dim); color: var(--accent);
}
.choice-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.form-grid.three { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr); }
@media (max-width: 480px) { .form-grid, .form-grid.three { grid-template-columns: 1fr; } }

@media (max-width: 1100px) {
  .card-grid, .card-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-layout { grid-template-columns: minmax(180px, 240px) minmax(260px, 1fr); }
  .grid-live-panel { grid-column: 1 / -1; }
  .health { display: none; }
  .notif { position: fixed; top: 58px; left: 8px; right: 8px; width: auto; }
  .nav button { padding: 0 6px; }
}
@media (max-width: 960px) and (min-width: 768px) {
  .viewer-screen { grid-template-columns: 210px minmax(280px, 1fr); }
  .viewer-screen.events-mode { grid-template-columns: 210px minmax(280px, 1fr) 205px; }
  .viewer-sources { padding: 10px; }
  .viewer-center { padding: 10px; }
  .viewer-center-body { gap: 8px; }
}
@media (max-width: 720px) {
  .item-source, .sc-row { grid-template-columns: 1fr; }
  .actions { justify-content: flex-start; }
}
@media (max-width: 767px) {
  .menu { display: inline-flex; }
  .nav {
    display: none; position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; align-items: stretch;
    background: var(--chrome); border-bottom: 1px solid var(--chrome-line); padding: 0.4rem; box-shadow: 0 8px 20px rgb(0 0 0 / 35%);
  }
  .nav.open { display: flex; }
  .health { display: none; }
  .live-screen { flex-direction: column; }
  .live-cams { width: auto; max-height: 220px; border-right: 0; border-bottom: 1px solid var(--line); }
  .viewer-screen { display: flex; flex-direction: column; overflow: auto; }
  .viewer-sources { max-height: 380px; border-right: 0; border-bottom: 1px solid var(--line); }
  .viewer-center { flex: none; order: 1; }
  .viewer-screen.events-mode .viewer-center { min-height: 360px; }
  .viewer-sources { order: 2; flex: none; }
  .viewer-filters { order: 3; flex: none; }
  .viewer-center-body { display: flex; flex-direction: column; }
  .viewer-stage { flex: none; }
  .viewer-filters { border-left: 0; border-top: 1px solid var(--line); }
  .vf-swatches { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); }
  .grid-layout { grid-template-columns: 1fr; }
  .card-grid, .card-grid.cols-4 { grid-template-columns: 1fr; }
}

/* ---- Viewer: sources column, filters, event mode ---- */
.viewer-source-pager, .viewer-events-pager { display: flex; align-items: center; justify-content: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); }
.viewer-source-pager button, .viewer-events-pager button {
  height: 26px; min-width: 30px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); color: var(--text);
}
.viewer-source-pager button:hover:not(:disabled), .viewer-events-pager button:hover:not(:disabled) { background: var(--hover-strong); }
.viewer-source-pager button:disabled, .viewer-events-pager button:disabled { opacity: 0.4; cursor: default; }
.viewer-source-thumb { overflow: hidden; }
.viewer-source-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.viewer-source-list .viewer-source-meta span.viewer-source-stat { color: var(--muted); font-size: var(--fs-xs); }
.viewer-source-stat.hot { color: var(--accent); }
.viewer-source-stat.bad { color: var(--bad); }
.viewer-source-stat.warn { color: var(--warn); }
.src-sub.warn { color: var(--warn); }
.vw-progress { display: block; height: 3px; border-radius: 99px; background: var(--line); overflow: hidden; }
.vw-progress b { display: block; height: 100%; background: var(--accent); transition: width 0.4s; }
.vw-progress.busy b { animation: vw-busy 1.2s linear infinite; background: linear-gradient(90deg, transparent, var(--accent), transparent); }
@keyframes vw-busy { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.viewer-mode { display: flex; gap: 2px; padding: 2px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
.viewer-mode button { height: 24px; padding: 0 12px; border: 0; border-radius: 4px; background: transparent; color: var(--muted); font-size: var(--fs-sm); font-weight: 600; }
.viewer-mode button.on { background: var(--select-bg); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--select-line); }
.viewer-filter-group { margin: 0; padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 10px; border: 1px solid var(--line); border-radius: var(--radius-lg); min-width: 0; }
.viewer-filter-group legend { padding: 0 4px; color: var(--muted); font-size: var(--fs-xs); }
.viewer-filter-group:disabled { opacity: 0.45; }
.viewer-custom-range { display: grid; gap: 6px; }
.viewer-range-end { display: grid !important; grid-template-columns: 34px minmax(0, 1fr); align-items: center; gap: 6px !important; }
.viewer-events { display: none; flex-direction: column; gap: 10px; min-width: 0; min-height: 0; }
.viewer-events-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; color: var(--muted); font-size: var(--fs-sm); }
.vw-events-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; align-content: start; overflow: auto; min-height: 0; }
.vw-events-grid > .viewer-empty { grid-column: 1 / -1; }
/* Viewer events: justified rows; viewer-events.js sets each card's width and height. */
.vw-events-rows { display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start; overflow-y: auto; overflow-x: hidden; min-height: 0; }
.vw-events-rows > .vw-empty, .vw-events-rows > .viewer-empty { flex: 1 1 100%; }
.vw-events-rows .vw-event { position: relative; flex: none; display: block; width: 213px; height: 160px; padding: 0; overflow: hidden; }
.vw-events-rows .vw-event-pic { position: absolute; inset: 0; aspect-ratio: auto; border-radius: 0; }
.vw-events-rows .vw-event-badge { top: 4px; bottom: auto; }
.vw-events-rows .vw-event-meta { position: absolute; left: 0; right: 0; bottom: 0; padding: 18px 8px 6px; background: linear-gradient(transparent, rgb(0 0 0 / 78%)); color: #fff; text-align: left; pointer-events: none; }
.vw-events-rows .vw-event-meta strong { color: #fff; }
.vw-events-rows .vw-event-meta .muted { color: rgb(255 255 255 / 80%); }
.viewer-events-page { min-width: 96px; text-align: center; color: var(--text); font-variant-numeric: tabular-nums; }
.vw-event {
  display: flex; flex-direction: column; gap: 6px; padding: 6px; text-align: left; color: var(--text);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.vw-event:hover { border-color: var(--accent); }
.vw-event-pic { position: relative; display: grid; place-items: center; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--radius); background: var(--stage); color: var(--muted); }
.vw-event-pic img, .vw-event-pic canvas { width: 100%; height: 100%; object-fit: cover; }
.vw-event-badge { position: absolute; left: 4px; bottom: 4px; padding: 1px 6px; border-radius: 4px; background: rgb(0 0 0 / 70%); color: var(--text-strong); font-size: var(--fs-xs); display: inline-flex; align-items: center; gap: 4px; }
.vw-event-meta { display: flex; flex-direction: column; min-width: 0; }
.vw-event-meta strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-md); color: var(--text-strong); }
.vw-event-meta .muted { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-xs); }
.viewer-screen.events-mode .viewer-center-head, .viewer-screen.events-mode .viewer-stage { display: none; }
.viewer-screen.events-mode .viewer-center-body { display: flex; flex-direction: column; }
.viewer-screen.events-mode .viewer-events { display: flex; flex: 1; min-height: 0; }

/* ---- Viewer: timeline, new-event bar, related events ---- */
.viewer-events-new {
  height: 30px; border: 1px solid var(--accent); border-radius: var(--radius); background: var(--accent-dim); color: var(--text-strong); font-weight: 600;
}
.viewer-events-new:hover { background: var(--accent-fill); }
.vw-timeline { display: flex; flex-direction: column; gap: 2px; padding: 6px 8px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); }
.vw-timeline-bars { display: flex; align-items: flex-end; gap: 1px; height: 46px; }
.vw-timeline-bar { flex: 1; min-width: 2px; height: 100%; display: flex; align-items: flex-end; padding: 0; border: 0; background: transparent; }
.vw-timeline-bar i { display: block; width: 100%; background: var(--muted); opacity: 0.55; border-radius: 1px 1px 0 0; }
.vw-timeline-bar:hover i { background: var(--accent); opacity: 1; }
.vw-timeline-bar.on i { background: var(--accent); opacity: 1; }
.vw-timeline-axis { display: flex; justify-content: space-between; color: var(--muted); font-size: var(--fs-xs); }
.vw-cam.fresh .viewer-source-stat { animation: vw-fresh 1.6s ease-out; }
@keyframes vw-fresh { from { background: var(--accent-dim); } to { background: transparent; } }
.event-related h3 { margin: 8px 0 6px; font-size: var(--fs-md); color: var(--muted); }
.event-related .vw-events-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }

/* ---- Viewer: what a camera cell shows and offers ---- */
.vw-cell { position: relative; container-type: size; }
.vw-cell.single:hover { box-shadow: none; }
.vw-cell-ui { position: absolute; inset: 0; display: flex; align-items: flex-start; justify-content: space-between; gap: 6px; padding: 4px; pointer-events: none; }
.vw-cell-info {
  display: flex; align-items: center; gap: 6px; min-width: 0; max-width: calc(100% - 4px); padding: 2px 7px;
  border-radius: 4px; background: rgb(0 0 0 / 55%); color: #fff; font-size: var(--fs-xs); line-height: 1.5;
}
.vw-cell-info strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.vw-cell-info .src { flex: none; color: rgb(255 255 255 / 65%); }
.vw-cell-info .dot { width: 6px; height: 6px; }
.vw-cell-tools { display: flex; gap: 4px; flex: none; opacity: 0; transition: opacity 0.12s; pointer-events: auto; }
.vw-cell:hover .vw-cell-tools, .vw-cell:focus-within .vw-cell-tools, .vw-cell.recording .vw-cell-tools { opacity: 1; }
.vw-cell-tools button {
  display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 4px;
  background: rgb(0 0 0 / 65%); color: #fff; cursor: pointer;
}
.vw-cell-tools button:hover:not(:disabled) { background: var(--accent-fill); }
.vw-cell-tools button:disabled { opacity: 0.45; cursor: not-allowed; }
.vw-cell.recording [data-tool="rec"] { background: #c42b1c; }
.vw-cell-rec {
  position: absolute; left: 4px; bottom: 4px; padding: 1px 7px; border-radius: 4px; background: rgb(0 0 0 / 65%);
  color: #ff6b5e; font-size: var(--fs-xs); font-variant-numeric: tabular-nums;
}
@container (max-width: 190px) { .vw-cell-info .src { display: none; } }
@container (max-width: 110px) { .vw-cell-info { display: none; } .vw-cell-tools { margin-left: auto; } .vw-cell-tools button { width: 22px; height: 22px; } }
.vw-toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 80; max-width: min(90vw, 520px);
  padding: 8px 14px; border-radius: var(--radius-lg); background: var(--raised); border: 1px solid var(--line);
  color: var(--text-strong); box-shadow: var(--shadow); font-size: var(--fs-md);
}
.vw-toast.bad { border-color: var(--danger-line); }

/* ---- Viewer: camera state ---- */
.vw-state, .vw-cam .vw-state { display: flex; align-items: center; gap: 6px; }
/* Room for the dot's glow: the row does not clip, its words still end in … */
.vw-cam .vw-state { overflow: visible; padding-left: 3px; }
/* A live source glows, one that is connecting blinks. */
.vw-state.ok .dot, .vw-cell-info .dot.ok { box-shadow: 0 0 0 3px rgb(125 206 160 / 18%); }
.vw-state.warn .dot, .vw-cell-info .dot.warn { animation: vw-blink 1.2s ease-in-out infinite; }
.vw-state.bad .dot { box-shadow: 0 0 0 3px rgb(244 135 113 / 16%); }
@keyframes vw-blink { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .vw-state.warn .dot, .vw-cell-info .dot.warn { animation: none; } }
.dot.ok { background: var(--ok); }
.dot.warn { background: var(--warn); }
.dot.bad { background: var(--bad); }
.dot.idle { background: var(--muted); }
.vw-state.ok .dot { background: var(--ok); }
.vw-state.warn .dot { background: var(--warn); }
.vw-state.bad .dot { background: var(--bad); }
.vw-state.idle .dot { background: var(--muted); }
.viewer-source-list .viewer-source-meta .vw-state { font-size: var(--fs-xs); }

/* ---- Viewer: Nguồn tree ---- */
.vw-tree-head {
  flex: none; display: flex; align-items: center; gap: 4px; width: 100%; min-height: 26px; padding: 2px 8px 2px 2px;
  border: 0; border-radius: var(--radius); background: transparent; color: var(--muted); text-align: left; cursor: pointer;
}
.vw-tree-head:hover { background: var(--hover-strong); color: var(--text-strong); }
.vw-tree-head .icon:first-child { flex: none; color: var(--muted); transition: transform 0.15s; }
.vw-tree-head[aria-expanded="true"] .icon:first-child { transform: rotate(90deg); }
.vw-tree-head.depth-0 { min-height: 30px; font-size: var(--fs-md); font-weight: 600; color: var(--text-strong); }
.vw-tree-head.depth-0:not(:first-child) { margin-top: 10px; }
.vw-tree-head.depth-1 { padding-left: 10px; font-size: var(--fs-sm); font-weight: 500; }
.vw-tree-head.depth-2 { padding-left: 22px; min-height: 26px; font-size: var(--fs-xs); font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.vw-tree-head.depth-1.top { padding-left: 2px; }
.vw-tree-head.top:not(:first-child) { margin-top: 6px; }
.vw-tree-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vw-tree-count { flex: none; color: var(--muted); font-size: var(--fs-xs); font-weight: 400; font-variant-numeric: tabular-nums; }
/* Sources sit under their branch; the heads carry the structure, not lines or boxes. */
.viewer-source-list .vw-cam.vw-tree-leaf { width: 100%; }
@media (prefers-reduced-motion: reduce) { .vw-tree-head .icon:first-child { transition: none; } }

/* ---- Viewer: PTZ panel over the stage ---- */
.vw-ptz {
  position: absolute; right: 10px; bottom: 10px; z-index: 5; width: 256px; display: flex; flex-direction: column; gap: 8px;
  padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: rgb(18 19 20 / 92%);
  box-shadow: var(--shadow); color: var(--text); font-size: var(--fs-sm); backdrop-filter: blur(4px);
}
.vw-ptz header { display: flex; align-items: center; gap: 6px; min-width: 0; }
.vw-ptz header .icon { flex: none; color: var(--accent); }
.vw-ptz-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.vw-ptz .tag { flex: none; }
.vw-ptz-close { display: grid; place-items: center; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 4px; background: transparent; color: var(--muted); }
.vw-ptz-close:hover { background: var(--hover-strong); color: var(--text-strong); }
.vw-ptz-body { display: flex; gap: 10px; }
.vw-ptz-pad { display: grid; grid-template-columns: repeat(3, 34px); grid-template-rows: repeat(3, 34px); gap: 3px; }
.vw-ptz button { touch-action: none; user-select: none; }
.vw-ptz-pad button, .vw-ptz-pair button, .vw-ptz-presets button {
  display: grid; place-items: center; padding: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--raised); color: var(--text); font-size: var(--fs-lg); line-height: 1;
}
.vw-ptz-pad button[data-dir="home"] { border-radius: 50%; color: var(--accent); }
.vw-ptz-pad button:hover, .vw-ptz-pair button:hover, .vw-ptz-presets button:hover { background: var(--hover-strong); }
.vw-ptz-pad button.on, .vw-ptz-pair button.on { background: var(--select-bg); border-color: var(--select-line); color: var(--text-strong); }
.vw-ptz-side { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.vw-ptz-pair { display: grid; grid-template-columns: 1fr 28px 28px; align-items: center; gap: 3px; }
.vw-ptz-pair span { color: var(--muted); }
.vw-ptz-pair button { height: 28px; }
.vw-ptz-speed { display: grid; grid-template-columns: 1fr auto; gap: 2px 6px; color: var(--muted); }
.vw-ptz-speed input { grid-column: 1 / -1; grid-row: 2; min-height: 0; height: 16px; padding: 0; border: 0; background: transparent; accent-color: var(--accent); }
.vw-ptz-speed output { color: var(--text); }
.vw-ptz-presets { display: grid; grid-template-columns: repeat(6, 1fr); gap: 3px; }
.vw-ptz-presets button { height: 26px; font-size: var(--fs-sm); }
.vw-ptz-presets button.set { border-color: var(--accent); color: var(--text-strong); }
.vw-ptz [hidden] { display: none; }
.vw-ptz button:disabled { opacity: 0.4; cursor: not-allowed; }
.vw-ptz-status { margin: 0; min-height: 15px; color: var(--muted); font-size: var(--fs-xs); }
.vw-ptz-status.bad { color: var(--warn); }
@media (max-width: 767px) { .vw-ptz { right: 6px; bottom: 6px; transform: scale(0.9); transform-origin: bottom right; } }

/* ---- Viewer: cell without a picture ---- */
.vw-cell-empty, .vw-cell-state {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  padding: 8px; text-align: center; pointer-events: none;
}
.vw-cell-empty { color: rgb(255 255 255 / 38%); font-size: var(--fs-sm); }
.vw-cell-empty strong { font-weight: 600; }
.vw-cell-empty span { font-size: var(--fs-xs); }
.vw-board.empty .vw-cell-empty, .viewer-screen.dragging .vw-cell-empty { color: rgb(255 255 255 / 60%); }
/* Over the last frame the camera sent (or black): dim it and say why. */
.vw-cell-state { background: rgb(0 0 0 / 62%); color: #fff; font-size: var(--fs-sm); }
.vw-cell-state strong { font-size: var(--fs-md); font-weight: 700; }
.vw-cell-state span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: rgb(255 255 255 / 72%); font-size: var(--fs-xs); }
.vw-cell-state[data-tone="bad"] .icon { color: var(--bad); }
.vw-cell-state[data-tone="warn"] .icon { color: var(--warn); animation: vw-blink 1.2s ease-in-out infinite; }
.vw-cell-state[data-tone="idle"] .icon { color: var(--muted); }
.vw-board .vw-cell.down { --edge: rgb(244 135 113 / 45%); }
@media (prefers-reduced-motion: reduce) { .vw-cell-state[data-tone="warn"] .icon { animation: none; } }
@container (max-height: 70px) { .vw-cell-empty span, .vw-cell-state span, .vw-cell-empty .icon, .vw-cell-state .icon { display: none; } }
@container (max-width: 110px) { .vw-cell-empty strong { font-size: 10px; } .vw-cell-state strong { font-size: 11px; } .vw-cell-state span, .vw-cell-empty span { display: none; } }

/* ---- Waiting: page loading, live connecting, first frame ---- */
@keyframes vw-spin { to { transform: rotate(360deg); } }
.vw-spinner, .vw-placeholder.busy::before {
  content: ""; width: 26px; height: 26px; border: 2px solid rgb(255 255 255 / 14%); border-top-color: var(--accent);
  border-radius: 50%; animation: vw-spin 0.8s linear infinite;
}
/* Waiting covers the stage (cell lines and labels too) with a dim, blurred veil. */
.vw-placeholder.busy {
  z-index: 4; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  background: rgb(12 12 12 / 62%); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: #fff; font-size: var(--fs-md); font-weight: 500; text-shadow: 0 1px 2px rgb(0 0 0 / 60%); pointer-events: none;
}
.vw-placeholder.busy::before { border-color: rgb(255 255 255 / 22%); border-top-color: var(--accent); }
.vw-loading { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-height: 240px; color: var(--muted); font-size: var(--fs-md); }
@media (prefers-reduced-motion: reduce) { .vw-spinner, .vw-placeholder.busy::before { animation-duration: 2.4s; } }

/* ---- Viewer: moving between layouts (zoom on a cell, back to the grid, another grid) ---- */
.viewer-stage .vw-board, .viewer-stage .stage canvas { transition: opacity 0.12s; }
.viewer-stage.moving .vw-board, .viewer-stage.moving .stage canvas { opacity: 0; }
.vw-move { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
/* The stage's own drawing while the layout changes (over the video, under nothing else). */
.stage canvas.vw-move-canvas { opacity: 0; }
.viewer-stage.moving .stage canvas.vw-move-canvas.on { opacity: 1; transition: none; }
/* The spinner only when the new picture is slow to come. */
.vw-move-wait { position: absolute; inset: 0; display: grid; place-items: center; opacity: 0; animation: vw-show 0.2s 0.3s forwards; }
.vw-move-wait .vw-spinner { filter: drop-shadow(0 0 6px rgb(0 0 0 / 70%)); }
@keyframes vw-show { to { opacity: 1; } }


/* ---- Viewer: digital zoom on one camera ---- */
.viewer-stage.dz-ready .vw-board { cursor: crosshair; touch-action: none; }
.viewer-stage.dz-ready.dz-on .vw-board { cursor: grab; }
.viewer-stage.dz-ready.dz-on.dz-dragging .vw-board { cursor: grabbing; }
.vw-zoom-rect { position: absolute; z-index: 4; border: 1.5px dashed var(--accent); background: rgb(232 106 26 / 12%); pointer-events: none; }
.vw-zoom-bar {
  position: absolute; left: 10px; bottom: 10px; z-index: 5; display: flex; align-items: center; gap: 8px; padding: 4px 4px 4px 8px;
  border-radius: var(--radius); background: rgb(0 0 0 / 65%); color: #fff; font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
}
.vw-zoom-bar .icon { color: var(--accent); }
.vw-zoom-bar button { height: 24px; padding: 0 8px; border: 1px solid rgb(255 255 255 / 25%); border-radius: 4px; background: transparent; color: #fff; font-size: var(--fs-sm); }
.vw-zoom-bar button:hover { background: rgb(255 255 255 / 12%); }
.vw-zoom-rect[hidden], .vw-zoom-bar[hidden] { display: none; }

/* ---- Recordings dialog ---- */
.rec-filters { display: flex; flex-wrap: wrap; gap: 8px 12px; margin-bottom: 12px; }
.rec-filters label { display: flex; flex-direction: column; gap: 4px; min-width: 160px; flex: 1; color: var(--muted); font-size: var(--fs-sm); }
.rec-filters label[hidden] { display: none; }
.rec-player video { display: block; width: 100%; max-height: 46vh; border-radius: var(--radius); background: #000; }
.rec-player { margin-bottom: 12px; }
/* Playing: the player takes the dialog, a bar on top leads back to the list. */
.vw-dialog.rec-playing .rec-filters, .vw-dialog.rec-playing .rec-list, .vw-dialog.rec-playing .rec-pager { display: none; }
.vw-dialog.rec-playing .rec-player { margin-bottom: 0; }
.vw-dialog.rec-playing .rec-player video { max-height: 64vh; }
.rec-player-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; min-width: 0; }
.rec-player-bar .rec-name { flex: 1; }
.rec-player-bar .btn { height: 30px; padding: 0 10px; flex: none; }
.rec-back .icon { transform: rotate(180deg); }
.rec-list { display: flex; flex-direction: column; gap: 8px; max-height: 46vh; overflow: hidden auto; }
.rec-row { display: grid; grid-template-columns: 128px minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--bg); }
.rec-row.live { border-color: var(--danger-line); }
.rec-thumb { position: relative; display: grid; place-items: center; width: 128px; aspect-ratio: 16 / 9; padding: 0; overflow: hidden; border: 0; border-radius: var(--radius); background: #000; color: var(--muted); cursor: pointer; }
.rec-thumb:disabled { cursor: default; opacity: 1; }
.rec-thumb img, .rec-thumb video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.rec-thumb.none img { display: none; }
.rec-duration { position: absolute; right: 4px; bottom: 4px; z-index: 1; padding: 0 5px; border-radius: 3px; background: rgb(0 0 0 / 70%); color: #fff; font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.rec-row.live .rec-thumb::after { content: "● REC"; position: absolute; left: 4px; top: 4px; padding: 0 5px; border-radius: 3px; background: #c42b1c; color: #fff; font-size: var(--fs-xs); font-weight: 700; }
.rec-text { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.rec-name { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.rec-name strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rec-name span { color: var(--muted); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; white-space: nowrap; }
.rec-facts { margin: 0; display: flex; flex-wrap: wrap; gap: 4px 12px; color: var(--muted); font-size: var(--fs-sm); }
.rec-row.live .rec-facts span:first-child { color: var(--bad); font-weight: 600; }
.rec-row .actions { display: flex; gap: 6px; }
.rec-row .actions .btn { height: 30px; padding: 0 10px; }
.rec-pager { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 10px; font-size: var(--fs-sm); }
.rec-pager .muted { margin-right: auto; }
.rec-pager button { height: 28px; min-width: 32px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); color: var(--text); }
.rec-pager button:disabled { opacity: 0.4; cursor: default; }
@media (max-width: 640px) {
  .rec-row { grid-template-columns: 96px minmax(0, 1fr); }
  .rec-thumb { width: 96px; }
  .rec-row .actions { grid-column: 1 / -1; }
}
a.btn { color: var(--text); }
/* On the picture: a dark base under the tint, so it stays readable over any video. */
.vw-cell-tools button.on { background: linear-gradient(var(--select-bg-hover), var(--select-bg-hover)), rgb(0 0 0 / 65%); box-shadow: inset 0 0 0 1px var(--select-line); color: var(--text-strong); }

/* ---- Empty and failed states: what happened and what to do next ---- */
.vw-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; padding: 16px; border: 1px dashed var(--line); border-radius: var(--radius-lg); color: var(--muted); }
.vw-empty p { margin: 0; max-width: 60ch; line-height: 1.5; }
.vw-empty .actions { display: flex; flex-wrap: wrap; gap: 8px; }
.vw-empty.bad { border-color: var(--danger-line); }
.vw-empty.bad p { color: var(--text); }
.viewer-source-list .vw-empty { padding: 12px; }
/* In a grid of cards the message takes the whole row. */
.vw-empty { grid-column: 1 / -1; }
.viewer-source-list .vw-empty .btn { height: 30px; padding: 0 10px; }
main > .vw-empty { margin: 24px; }
/* The Viewer's live session while another page is open (parkLive). */
#live-shelf { position: fixed; left: -10px; top: 0; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }

/* ---- Zone and line editor (shape-editor.js) ---- */
.vw-dialog.wide.shapes { width: min(1040px, calc(100vw - 32px)); }
.shape-editor { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 12px; align-items: start; }
.se-stage {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden; border-radius: var(--radius);
  background: var(--stage); border: 1px solid var(--line); touch-action: none; user-select: none;
}
.se-stage:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.se-stage img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: fill; pointer-events: none; }
.se-empty { position: absolute; inset: 0; display: grid; place-items: center; padding: 16px; color: var(--muted); font-size: var(--fs-md); text-align: center; }
.se-svg { position: absolute; inset: 0; width: 100%; height: 100%; cursor: crosshair; }
.se-shape { color: var(--accent); }
.se-shape .se-fill { fill: currentColor; fill-opacity: 0.12; stroke: currentColor; stroke-width: 1.5; stroke-dasharray: 5 4; }
.se-shape.active .se-fill { fill-opacity: 0.22; stroke-width: 2.5; stroke-dasharray: none; }
.se-shape .se-fill.movable { cursor: move; }
.se-shape .se-fill.pickable { cursor: pointer; }
.se-shape .se-fill.se-roi { fill-opacity: 0.05; stroke-dasharray: 10 5; }
.se-shape.active .se-fill.se-roi { fill-opacity: 0.08; stroke-dasharray: 10 5; }
.se-shape.sibling .se-fill { fill-opacity: 0.18; }
.se-hit { stroke: transparent; stroke-width: 14; cursor: pointer; }
.se-edge { fill: none; stroke: currentColor; stroke-width: 2.5; }
.se-edge.preview { stroke-dasharray: 6 4; }
.se-shape:not(.active) .se-edge { stroke-width: 1.5; stroke-dasharray: 5 4; }
.se-arrow { stroke: currentColor; stroke-width: 2; }
.se-arrow-head { fill: currentColor; }
.se-label, .se-arrow-text {
  fill: #fff; font-size: var(--fs-sm); font-weight: 600; paint-order: stroke; stroke: rgb(0 0 0 / 75%); stroke-width: 3px; pointer-events: none;
}
.se-handle { fill: #fff; stroke: currentColor; stroke-width: 2; cursor: grab; }
.se-handle.picked { fill: currentColor; stroke: #fff; }
.se-handle.first { r: 8; }
.se-mid { fill: currentColor; fill-opacity: 0.75; stroke: #fff; stroke-width: 1; cursor: copy; }
@media (pointer: coarse) { .se-handle { r: 11; } .se-mid { r: 8; } }
.se-side { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.se-title { font-size: var(--fs-md); color: var(--text-strong); }
.se-slots { display: flex; flex-direction: column; gap: 4px; }
.se-slot {
  display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; align-items: center; gap: 8px; height: 34px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); color: var(--text); text-align: left; cursor: pointer;
}
.se-slot.on { border-color: var(--accent); background: var(--accent-dim); }
.se-slot i { width: 10px; height: 10px; border-radius: 3px; }
.se-slot span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-md); }
.se-slot em { font-style: normal; color: var(--muted); font-size: var(--fs-sm); }
.se-hint { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }
.se-hint.warn { color: var(--warn); }
.se-tools { display: flex; flex-wrap: wrap; gap: 6px; }
.se-tools .btn { height: 30px; padding: 0 10px; font-size: var(--fs-sm); }
.se-seek, .dlg-body .se-seek { display: flex; flex-direction: row; align-items: center; gap: 10px; font-size: var(--fs-sm); color: var(--muted); }
.job-drop { flex: none; flex-direction: row; min-height: 150px; box-sizing: border-box; }
.job-drop.has-picture { min-height: 66px; padding: 8px; justify-content: flex-start; }
.job-file { display: flex; align-items: center; gap: 12px; width: 100%; min-width: 0; }
.job-file-pic { flex: none; width: 72px; height: 48px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius); background: #000; color: var(--muted); overflow: hidden; }
.job-file-pic img { width: 100%; height: 100%; object-fit: contain; }
.job-file-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; font-size: var(--fs-sm); }
.job-file-text strong { color: var(--text-strong); font-size: var(--fs-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.job-file u { color: var(--accent); text-decoration: none; font-size: var(--fs-sm); }
.job-upload { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); }
.job-upload-line { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 12px; font-size: var(--fs-sm); }
.job-upload-line strong { color: var(--text-strong); font-variant-numeric: tabular-nums; }
.job-upload-line .muted { font-variant-numeric: tabular-nums; }
.job-upload-bar { height: 6px; }
.job-upload.failed strong { color: var(--danger, #e5484d); }
.job-upload.failed .job-upload-bar b { background: var(--danger, #e5484d); }
.se-strip { display: flex; gap: 6px; overflow-x: auto; padding: 2px; }
.se-strip-item { flex: none; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 3px; width: 92px; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius); color: var(--muted); font-size: var(--fs-xs); cursor: pointer; }
.se-strip-item img { width: 100%; border-radius: 3px; display: block; }
.se-strip-item.on { border-color: var(--select-line); background: var(--select-bg); color: var(--text-strong); }
.se-seek input { flex: 1; min-height: 0; padding: 0; accent-color: var(--accent); }
@media (max-width: 720px) { .shape-editor { grid-template-columns: 1fr; } }

/* ---- Models (models.js): icon · name and kind · classes · scenarios · ⋯ ---- */
/* Models: a grid of cards (models.js modelRow), grouped by type in "Tất cả". */
.mdl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 10px; }
.mdl-row { display: flex; flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; }
.mdl-card-top { display: flex; align-items: center; gap: 10px; min-width: 0; }
.mdl-card-top .src-main { flex: 1 1 auto; }
.mdl-card-top .actions { flex: none; align-self: flex-start; }
.mdl-card-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; gap: 6px 12px; margin: 0; font-size: var(--fs-sm); }
.mdl-card-facts dt { color: var(--muted); }
.mdl-card-facts dd { margin: 0; min-width: 0; color: var(--text); font-variant-numeric: tabular-nums; }
.mdl-card-facts .mdl-built, .mdl-card-facts .mdl-progress { margin-top: 0; }
.mdl-card-facts .mdl-bar { flex-basis: 48px; }
.mdl-failed { color: var(--bad); font-size: var(--fs-sm); }
.mdl-built .tag:not(.ok) { border-color: var(--line); color: var(--muted); }
.mdl-row .mdl-usage { margin-top: auto; padding-top: 8px; border-top: 1px solid var(--line); font-size: var(--fs-sm); }
.mdl-icon { flex: none; width: 40px; height: 40px; display: grid; place-items: center; border-radius: var(--radius-lg); background: var(--accent-dim); color: var(--accent); }
.mdl-export > summary { cursor: pointer; font-size: var(--fs-md); font-weight: 600; color: var(--text-strong); padding: 4px 0; }
.mdl-export[open] { display: flex; flex-direction: column; gap: 8px; }
.mdl-built { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.mdl-built .link-btn { text-align: left; }
.link-btn { padding: 0; border: 0; background: none; color: var(--accent); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.link-btn:hover { text-decoration: underline; }
.mdl-top { margin-bottom: 4px; }
.mdl-search { flex: 1 1 100%; max-width: none; width: 100%; }
.mdl-side-actions { display: flex; flex-direction: column; gap: 6px; }
.mdl-side-actions .btn { width: 100%; justify-content: center; }
.mdl-add-top { display: none; }
/* Sources: the search shares its row with "Thu gọn tất cả". */
.mdl-top .src-fold { flex: none; }
.mdl-top:has(.src-fold:not([hidden])) .mdl-search { flex: 1 1 0; min-width: 0; }
/* From and to one under the other: a date input needs the whole column. */
.rpt-side .rpt-range { display: flex; flex-direction: column; align-items: stretch; gap: 4px; }
.rpt-side .rpt-range[hidden] { display: none; }
.rpt-side .rpt-range > span { display: none; }
.rpt-side .rpt-range input { width: 100%; min-width: 0; }
.rpt-side .rpt-tools { margin-top: 6px; }
.rpt-side .rpt-tools .btn { justify-content: flex-start; }
@media (max-width: 720px) {
  /* A phone: the side is a column again (filters, spans as a row, tools), not one scrolling row. */
  .side-tabs.rpt-side { flex-direction: column; flex-wrap: nowrap; overflow: visible; gap: 10px; }
  .side-tabs.rpt-side .mdl-side-actions { flex-direction: column; }
  .side-tabs.rpt-side .rpt-filters .src-ws { display: flex; }
  .side-tabs.rpt-side .side-section { overflow-x: auto; max-width: 100%; }
  .side-tabs.rpt-side .rpt-range { flex-direction: row; }
  .side-tabs.rpt-side .rpt-tools { flex-direction: row; }
  .side-tabs.rpt-side .rpt-tools .btn { flex: 1 1 0; justify-content: center; }
}
label.ws-pick.src-ws { flex-direction: column; align-items: stretch; gap: 4px; margin-top: 8px; white-space: normal; }
label.ws-pick.src-ws .cb { width: 100%; }
label.ws-pick.src-ws-top { display: none; }
@media (max-width: 720px) { .mdl-side-actions .src-ws { display: none; } label.ws-pick.src-ws-top { display: flex; flex: 1 1 100%; } }
/* The first action of a line (Xem, Sự kiện): outlined and a touch brighter, never solid orange. */
.actions button.row-main { color: var(--text-strong); border-color: var(--input-line-hover); }
.actions button.row-main:hover { border-color: var(--accent); color: var(--text-strong); }
@media (max-width: 720px) {
  .mdl-search { flex: 1 1 0; min-width: 0; }
  .mdl-add-top { display: inline-flex; flex: none; }
  .mdl-side-actions .btn.primary { display: none; }
  .mdl-side-actions { flex-direction: row; flex: none; }
  .mdl-side-actions .btn { width: auto; }
}
.mdl-panel { gap: 10px; }
.mdl-head h2 { margin: 0; font-size: var(--fs-lg); color: var(--text-strong); }
.mdl-head p { margin: 4px 0 0; font-size: var(--fs-md); }
.side-items button .side-count { margin-left: auto; color: inherit; opacity: .7; font-size: var(--fs-sm); }
/* The panel is narrower than the page: name and classes on one row, where it is used under them. */
.mdl-group { display: flex; align-items: center; gap: 8px; margin: 14px 0 2px; font-size: var(--fs-base); font-weight: 600; color: var(--text-strong); }
.mdl-group:first-child { margin-top: 0; }
.mdl-group .icon { color: var(--accent); }
.mdl-group small { color: var(--muted); font-weight: 500; }
.mdl-classes { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; overflow: hidden; max-height: 48px; }
.mdl-classes .tag { color: var(--text); border-color: var(--line); font-weight: 500; }
.mdl-classes .muted { font-size: var(--fs-sm); }
.mdl-more { color: var(--muted); font-size: var(--fs-xs); }
.mdl-usage { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: var(--fs-md); }
.mdl-usage span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mdl-usage.warn { color: var(--warn); }
.mdl-digests { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-sm); }
.mdl-digests code { word-break: break-all; color: var(--muted); }
.mdl-engines { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; font-size: var(--fs-md); }
.mdl-engines li { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.mdl-engines li > span { min-width: 0; overflow-wrap: anywhere; }
.mdl-calib { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; }
.mdl-calib p { margin: 0; font-size: var(--fs-md); }
.mdl-calib-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.mdl-export { display: flex; flex-direction: column; gap: 8px; }
.mdl-export h3, .vw-dialog h3 { margin: 4px 0 0; font-size: var(--fs-md); }
.mdl-engine-actions { display: flex; gap: 8px; flex-shrink: 0; }
/* A build running on a model's line. */
.mdl-progress { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: var(--fs-sm); color: var(--muted); min-width: 0; }
.mdl-progress > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mdl-progress strong { color: var(--text); font-variant-numeric: tabular-nums; }
.mdl-bar { position: relative; flex: 0 0 96px; height: 4px; border-radius: 2px; background: var(--line); overflow: hidden; }
.mdl-bar i { position: absolute; inset: 0 auto 0 0; border-radius: 2px; background: var(--accent); transition: width .6s ease; }
.mdl-bar.busy i { animation: mdl-slide 1.4s ease-in-out infinite; }
@keyframes mdl-slide { from { left: -30%; } to { left: 100%; } }
@media (prefers-reduced-motion: reduce) { .mdl-bar i { transition: none; } .mdl-bar.busy i { animation: none; left: 35%; } }
.mdl-digests a { margin: 0 6px; }
.mdl-digests .link { margin-right: 6px; padding: 0; border: 0; background: none; font: inherit; color: var(--danger-line); cursor: pointer; }
.actions button:disabled { opacity: 0.45; cursor: not-allowed; }

/* Add model: steps, file drop, what the file and the machine are (models.js openModel). */
.mdl-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 0 0 4px; padding: 0; list-style: none; }
.mdl-steps li { display: flex; flex-direction: column; gap: 6px; color: var(--muted); font-size: var(--fs-sm); }
.mdl-steps .bar { height: 3px; border-radius: 99px; background: var(--line); }
.mdl-steps li.done .bar { background: var(--ok); }
.mdl-steps li.run { color: var(--text-strong); }
.mdl-steps li.run .bar { background: var(--accent); }
.mdl-steps b { font-weight: 600; }
.mdl-drop, .dlg-body .mdl-drop {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 96px; padding: 16px;
  border: 1px dashed var(--field-line); border-radius: var(--radius-lg); background: var(--bg); text-align: center; cursor: pointer;
}
.mdl-drop:hover, .mdl-drop:focus-within { border-color: var(--accent); }
.mdl-drop input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.mdl-drop .action { color: var(--text-strong); font-weight: 600; font-size: var(--fs-base); }
.mdl-drop .kinds { color: var(--muted); font-size: var(--fs-sm); }
.mdl-report { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.mdl-report p { margin: 0; }
.mdl-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 16px; margin: 0; font-size: var(--fs-md); }
.mdl-facts dt { color: var(--muted); }
.mdl-facts dd { margin: 0; overflow-wrap: anywhere; color: var(--text); }

/* ---- Scenario editor: blocks on a canvas, settings in a side panel ---- */
.vw-dialog.wf-dialog { width: min(1180px, calc(100vw - 32px)); height: calc(100vh - 96px); }
.wf-dialog .dlg-body { overflow: hidden; padding-bottom: 0; }
.wf-top { display: grid; grid-template-columns: minmax(0, 420px) minmax(0, 1fr); gap: 12px; align-items: end; }
.wf-top .hint { margin: 0 0 8px; }
.wf { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 12px; flex: 1; min-height: 0; }
.wf-canvas {
  overflow: auto; min-height: 0; padding: 20px 16px 28px; border: 1px solid var(--line); border-radius: var(--radius-lg);
  background-color: var(--bg); background-image: radial-gradient(var(--line) 1px, transparent 1px); background-size: 18px 18px;
}
.wf-flow { position: relative; display: flex; flex-direction: column; align-items: center; min-width: max-content; margin: 0 auto; }
.wf-flow > :not(.wf-lines) { position: relative; z-index: 1; }
/* Every line between blocks, drawn under them once they are laid out. */
.wf-lines { position: absolute; inset: 0 auto auto 0; overflow: visible; pointer-events: none; z-index: 0; }
.wf-lines path { fill: none; stroke: var(--flow); stroke-width: 2; stroke-dasharray: 6 4; }
.wf-lines marker path { fill: var(--flow); stroke: none; stroke-dasharray: none; }
.wf-inputs { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; border: 0; min-width: 0; }
.wf-inputs legend { padding: 0; margin-bottom: 4px; }
.wf-tree { display: flex; flex-direction: column; align-items: center; }
.wf-branches { display: flex; gap: 28px; align-items: flex-start; justify-content: center; }
.wf-branches > .wf-tree { padding-top: 30px; }
.wf-link { position: relative; display: flex; justify-content: center; width: 2px; height: 34px; }
.wf-plus {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: grid; place-items: center;
  width: 24px; height: 24px; padding: 0; border: 1.5px solid var(--flow); border-radius: 50%; background: var(--panel); color: var(--muted); cursor: pointer;
}
.wf-plus:hover, .wf-plus.active { border-color: var(--accent); color: var(--accent); }
.wf-card, .wf-pick, .wf-io-head {
  display: flex; align-items: center; gap: 12px; text-align: left; font: inherit; color: var(--text); cursor: pointer;
}
.wf-card {
  width: 260px; min-height: 64px; padding: 10px 12px; background: var(--panel); border: 1px solid var(--line);
  border-left: 4px solid var(--zone-b); border-radius: var(--radius-lg); box-shadow: 0 2px 6px rgb(0 0 0 / 30%);
}
.wf-card.wf-rule, .wf-pick.wf-rule { border-left-color: var(--zone-a); }
.wf-card.wf-other { border-left-color: var(--muted); }
.wf-card:hover, .wf-io:hover { border-color: var(--field-line); }
.wf-card.selected, .wf-io.selected { outline: 2px solid var(--accent); outline-offset: 1px; }
.wf-ico { display: grid; place-items: center; flex: none; width: 36px; height: 36px; border-radius: var(--radius); background: var(--raised); color: var(--zone-b); }
.wf-rule .wf-ico { color: var(--zone-a); }
.wf-io .wf-ico, .wf-panel-head .wf-ico { color: var(--muted); }
.wf-meta { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.wf-meta strong { color: var(--text-strong); font-size: var(--fs-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wf-cat { color: var(--muted); font-size: var(--fs-sm); }
.wf-meta em { color: var(--muted); font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wf-meta em:empty { display: none; }
.wf-meta em.wf-missing { color: var(--bad); font-style: normal; }
.wf-card.wf-unused { border-style: dashed; border-left-style: solid; border-left-color: var(--bad); }
.wf-none.wf-warn, .hint.wf-warn { color: var(--bad); }
.wf-io {
  display: flex; flex-direction: column; gap: 10px; width: 280px; padding: 12px; text-align: left; font: inherit; color: var(--text);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: 0 2px 6px rgb(0 0 0 / 30%); cursor: pointer;
}
.wf-out { cursor: default; }
.wf-io-head { padding: 0; background: none; border: 0; }
.wf-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.wf-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--raised); color: var(--text); font: inherit; font-size: var(--fs-sm);
}
.wf-alert { display: flex; align-items: center; gap: 6px; }
.wf-chip-link { flex: 1; min-width: 0; cursor: pointer; color: var(--zone-b); }
.wf-chip-link span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wf-alert.selected .wf-chip-link { border-color: var(--accent); }
.wf-x { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 24px; height: 24px; padding: 0; border: 0; border-radius: var(--radius); background: none; color: var(--muted); cursor: pointer; font-size: var(--fs-lg); }
.wf-x:hover { color: var(--bad); background: var(--hover); }
.wf-add-out { width: 100%; }
.wf-end { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 260px; }
.wf-end .wf-chip-link { flex: none; max-width: 100%; }
.wf-end .wf-chip-link.selected { border-color: var(--accent); }
.wf-none { color: var(--muted); font-size: var(--fs-sm); text-align: center; }
.wf-panel { display: flex; flex-direction: column; gap: 10px; min-height: 0; overflow: auto; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); }
.wf-panel-head { display: flex; align-items: center; gap: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
.wf-panel-head .wf-meta { flex: 1; }
.wf-panel .hint { margin: 0; }
.wf-fields { display: flex; flex-direction: column; gap: 10px; }
.wf-group { margin: 6px 0 0; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
.wf-pick { width: 100%; padding: 10px; background: var(--raised); border: 1px solid var(--line); border-left: 4px solid var(--zone-b); border-radius: var(--radius-lg); }
.wf-pick:hover { border-color: var(--accent); }
.wf-pick .wf-meta strong { white-space: normal; }
.wf-pick .wf-meta em { color: var(--muted); font-size: var(--fs-sm); }
.wf-variant { color: var(--accent); font-size: var(--fs-sm); font-weight: 600; }
.wf-classes { display: flex; flex-direction: column; gap: 6px; }
.wf-classes-bar { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 6px; }
.wf-classes-bar .btn { height: 32px; padding: 0 8px; font-size: var(--fs-sm); }
.wf-classes-bar.short { grid-template-columns: auto auto; justify-content: start; }
.wf-classes-list {
  display: flex; flex-wrap: wrap; gap: 6px; max-height: 168px; overflow: auto; padding: 6px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg);
}
.wf-class {
  display: inline-flex; flex-direction: row; align-items: center; gap: 6px; padding: 3px 9px 3px 6px; border: 1px solid var(--line);
  border-radius: 999px; background: var(--raised); color: var(--muted); font-size: var(--fs-sm); cursor: pointer; user-select: none;
}
.wf-class input { width: 14px; height: 14px; margin: 0; accent-color: var(--accent); }
.wf-class.on { border-color: var(--accent); background: var(--accent-dim); color: var(--text-strong); }
.wf-classes-list > .hint { margin: 2px 4px; }
.wf-classes-count { margin: 0; }
.wf-advanced { border-top: 1px solid var(--line); padding-top: 8px; }
.wf-advanced summary { cursor: pointer; color: var(--muted); font-size: var(--fs-sm); }
.wf-advanced[open] { display: flex; flex-direction: column; gap: 10px; }
@media (max-width: 860px) {
  .vw-dialog.wf-dialog { height: auto; }
  .wf-dialog .dlg-body { overflow: auto; }
  .wf { grid-template-columns: 1fr; }
  .wf-canvas { max-height: 60vh; }
  .wf-top { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) { .wf-card, .wf-plus { transition: none; } }

/* ---- Vector search results: most similar / journey ---- */
.vs-hits { display: flex; flex-direction: column; gap: 8px; }
.vs-hits-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.vs-min { display: flex; flex-direction: row; align-items: center; gap: 8px; font-size: var(--fs-md); }
.vs-journey { display: flex; flex-direction: column; gap: 10px; margin: 0; padding-left: 20px; }
.vs-journey li { display: grid; grid-template-columns: minmax(0, 1fr) 150px; gap: 10px; align-items: center; }
.vs-stay { display: flex; flex-direction: column; gap: 2px; }
@media (max-width: 480px) { .vs-journey li { grid-template-columns: 1fr; } }

/* ---- Faces (faces.js) ---- */
.face-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; }
.face-grid > .empty, .face-grid > .muted { grid-column: 1 / -1; }
.face-card { display: flex; flex-direction: column; align-items: stretch; gap: 8px; padding: 10px; }
.face-pic { aspect-ratio: 1; border-radius: var(--radius); background: var(--raised); overflow: hidden; }
.face-pic img { width: 100%; height: 100%; object-fit: cover; display: block; }
button.face-pic { position: relative; width: 100%; padding: 0; border: 0; cursor: zoom-in; }
.face-count {
  position: absolute; right: 6px; bottom: 6px; padding: 2px 8px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.6); color: #fff; font-size: var(--fs-sm); font-weight: 600;
}
.face-actions { display: flex; gap: 6px; }
.face-models { font-size: var(--fs-sm); color: var(--muted); overflow-wrap: anywhere; }
.face-models > span { color: var(--text); }
.face-models.bad { color: var(--bad); }
.face-actions .btn { flex: 1; padding: 0 8px; }
.face-meta .face-black { color: var(--bad); }
.face-meta { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.face-meta strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-strong); }
.face-meta select.face-black { border-color: var(--bad); }

/* ---- Định danh pager (lists.js), under both tabs ---- */
.list-pager { display: flex; align-items: center; justify-content: flex-end; gap: 8px; font-size: var(--fs-sm); }
.list-pager .muted { margin-right: auto; }
.list-pager button { height: 28px; min-width: 32px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); color: var(--text); }
.list-pager button:disabled { opacity: 0.4; cursor: default; }

/* ---- Plates (plates.js) ---- */
.plate-row { display: grid; grid-template-columns: minmax(110px, auto) minmax(0, 1fr) auto auto; align-items: center; gap: 10px; }
.plate-text { font-family: var(--mono); letter-spacing: 0.04em; color: var(--text-strong); }
.plate-row select.face-black { border-color: var(--bad); }

/* Định danh › Khuôn mặt: a person a card, the face beside the facts (faces.js card). */
.face-grid:has(.face-person) { grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); }
.face-person { display: grid; grid-template-columns: 76px minmax(0, 1fr); grid-template-areas: "pic meta" "acts acts"; gap: 10px 12px; align-items: start; }
.face-person .face-pic { grid-area: pic; width: 76px; }
.face-person .face-meta { grid-area: meta; gap: 4px; }
.face-person .face-actions { grid-area: acts; align-items: center; }
.face-person .face-actions .btn { flex: 1 1 auto; height: 30px; }
.face-list { align-self: flex-start; display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 8px; border-radius: 99px; border: 1px solid var(--line); font-size: var(--fs-xs); font-weight: 600; color: var(--muted); }
.face-list::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.face-list.face-white { color: var(--ok); border-color: rgb(125 206 160 / 40%); }
.face-list.face-black { color: var(--bad); border-color: rgb(244 135 113 / 45%); }
.face-list.face-mixed { color: var(--warn); border-color: rgb(230 193 90 / 40%); }
.face-enrolled { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; font-size: var(--fs-sm); color: var(--muted); }
.face-thumbs { display: inline-flex; flex: none; }
.face-thumbs img { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; border: 1px solid var(--panel); background: var(--raised); }
.face-thumbs img + img { margin-left: -6px; }
.face-warn { font-size: var(--fs-sm); color: var(--bad); }
/* Sửa người: faces with × to drop one (↺ keeps it again). */
.face-edit-head { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; font-size: var(--fs-md); }
.face-edit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; }
.face-edit-tile { position: relative; aspect-ratio: 1; border-radius: var(--radius); overflow: hidden; background: var(--raised); border: 1px solid var(--line); }
.face-edit-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.face-edit-tile.out img { opacity: 0.25; filter: grayscale(1); }
.face-edit-tile.out { border-style: dashed; border-color: var(--bad); }
.face-edit-drop {
  position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; display: grid; place-items: center; padding: 0;
  border: 0; border-radius: 50%; background: rgb(0 0 0 / 65%); color: #fff; font-size: 14px; line-height: 1; cursor: pointer;
}
.face-edit-drop:hover { background: var(--danger-line); }
/* Face pictures: a drop area, then the pictures picked as tiles (faces.js faceDrop). */
.face-drop { display: flex; flex-direction: column; gap: 8px; }
.face-drop-label { font-size: var(--fs-md); color: var(--text); margin-bottom: -2px; }
.face-drop-area { flex-direction: column; min-height: 116px; gap: 4px; text-align: center; font-size: var(--fs-sm); }
.face-drop-area strong { color: var(--text-strong); font-size: var(--fs-base); }
.face-drop-area u { color: var(--accent); text-decoration: none; }
.face-drop-area.off { opacity: 0.5; pointer-events: none; }
.face-drop-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 8px; }
.face-edit-tile.new { border-color: var(--select-line); }
@media (max-width: 560px) { .plate-row { grid-template-columns: 1fr auto; } .plate-row .muted { grid-column: 1 / -1; order: 3; } }

/* ---- System: charts, bars, log ---- */
.chart { gap: 6px; }
.chart-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.chart-head strong { font-size: var(--fs-md); color: var(--text-strong); font-weight: 600; }
.chart-legend { display: flex; gap: 10px; font-size: var(--fs-sm); }
.chart-svg { width: 100%; height: 80px; display: block; background: var(--hover); border-radius: 6px; }
.chart-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.chart-plot { position: relative; }
.chart-top { position: absolute; top: 2px; left: 6px; font-size: var(--fs-xs); color: var(--muted); pointer-events: none; }
.chart-axis { display: flex; justify-content: space-between; font-size: var(--fs-xs); color: var(--muted); }
.stack-bar { display: flex; height: 14px; border-radius: 7px; overflow: hidden; background: var(--hover); }
.stack-bar > i { display: block; height: 100%; }
.swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; }
.page > .card.log-groups { max-width: none; width: 100%; padding: 4px 0; gap: 0; }
.log-group + .log-group { border-top: 1px solid var(--line); }
.log-group-head {
  display: grid; grid-template-columns: 10px minmax(0, 1fr) 110px 56px 150px; gap: 10px; align-items: center; width: 100%;
  padding: 8px 14px; border: 0; background: transparent; color: var(--text); text-align: left; font-size: var(--fs-md);
}
.log-group-head:hover { background: var(--hover); }
.log-group-head .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
.log-group.warning .dot, .log-group.warn .dot { background: var(--warn); }
.log-group.error .dot, .log-group.critical .dot { background: var(--bad); }
.log-group-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log-group-head[aria-expanded="true"] .log-group-text { white-space: normal; overflow-wrap: anywhere; }
.log-group-who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-sm); }
.log-group-count { color: var(--text-strong); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.log-group-time { font-size: var(--fs-sm); text-align: right; white-space: nowrap; }
.log-empty { padding: 8px 14px; margin: 0; }
.log-lines { font-family: var(--mono); font-size: var(--fs-sm); padding: 4px 0 8px 34px; background: var(--bg); }
.log-line { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: 8px; padding: 2px 14px; overflow-wrap: anywhere; }
@media (max-width: 720px) {
  .log-group-head { grid-template-columns: 10px minmax(0, 1fr) auto; }
  .log-group-who, .log-group-time { display: none; }
  .log-lines { padding-left: 0; }
  .log-line { grid-template-columns: minmax(0, 1fr); }
}
.log-line .muted { color: var(--muted); }
.toolbar-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.alert-row { display: flex; gap: 10px; align-items: center; justify-content: space-between; }
.alert-row.crit { border-color: var(--danger-line); background: var(--danger-bg); color: var(--bad); }
@media (max-width: 720px) { .log-line { grid-template-columns: 1fr; } }
.section-title { margin: 8px 0 0; font-size: var(--fs-lg); font-weight: 600; color: var(--text-strong); }
.log-line > :first-child { white-space: nowrap; }

/* ---- System: same frame as Settings, but its cards keep their boxes and the full width ---- */
.settings-panel.sys-panel .card:not(.ws-card) { max-width: none; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); }
.settings-panel.sys-panel .card.table-card:not(.ws-card) { padding: 0; }
.settings-panel.sys-panel { gap: 12px; }
.gauge.warn strong { color: var(--warn); }
.facts { display: grid; grid-template-columns: minmax(160px, max-content) minmax(0, 1fr); gap: 10px 28px; margin: 0; }
.facts dt { color: var(--muted); font-size: var(--fs-md); }
.facts dd { margin: 0; font-size: var(--fs-md); color: var(--text-strong); overflow-wrap: anywhere; }
@media (max-width: 560px) { .facts { grid-template-columns: 1fr; gap: 2px; } .facts dd { margin-bottom: 8px; } }
.kinds-table { width: 100%; border-collapse: collapse; }
.kinds-table th, .kinds-table td { padding: 8px 0; text-align: left; font-size: var(--fs-md); }
.kinds-table th { color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
.kinds-table tr + tr td { border-top: 1px solid var(--line); }
.src-row.row-flash { animation: sf-flash 1.8s ease-out; }
.cam-cell { display: flex; flex-direction: column; gap: 2px; }
.cam-cell small { font-size: var(--fs-xs); }
.cam-link { color: var(--text-strong); text-decoration: none; }
.cam-link:hover { color: var(--accent); text-decoration: underline; }
.gauge-link { margin-top: 6px; font-size: var(--fs-sm); color: var(--accent); text-decoration: none; }
.gauge-link:hover { text-decoration: underline; }
/* Phone: the numbers stay two by two, so the overview fits without scrolling far. */
@media (max-width: 560px) {
  .settings-panel.sys-panel .card-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .settings-panel.sys-panel .gauge { padding: 10px 12px; }
  .settings-panel.sys-panel .gauge strong { font-size: var(--fs-xl); margin-top: 4px; }
}
.side-badge { margin-left: auto; min-width: 18px; padding: 0 5px; border-radius: 9px; background: var(--warn); color: var(--bg); font-size: var(--fs-xs); line-height: 18px; text-align: center; }

/* ---- Settings: side tabs ---- */
.settings-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 0; align-items: stretch; }
/* Row one: the search over the full width. Row two: the tab column and the settings. */
.settings-top { position: relative; }
.settings-top .icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.settings-search { width: 100%; height: 40px; padding-left: 36px; font-size: var(--fs-base); }
.side-tabs { display: flex; flex-direction: column; gap: 4px; position: sticky; top: 8px; align-self: start; min-width: 0; padding-right: 12px; }
.side-hit { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; height: auto !important; min-height: 40px; padding: 6px 12px !important; }
.side-hit span { color: var(--text); }
.side-hit small { color: var(--muted); font-size: var(--fs-xs); }
.side-empty { padding: 4px 12px; }
.sf-field.sf-flash { animation: sf-flash 1.8s ease-out; border-radius: var(--radius); }
@keyframes sf-flash { 0%, 40% { box-shadow: 0 0 0 6px var(--accent-dim); background: var(--accent-dim); } 100% { box-shadow: 0 0 0 6px transparent; background: transparent; } }
@media (prefers-reduced-motion: reduce) { .sf-field.sf-flash { animation: none; outline: 2px solid var(--accent); outline-offset: 4px; } }
/* (Not the workspace box a column may hold: it keeps the look of every select.) */
.side-tabs button:not(.cb-btn):not(.btn):not(.dp-trigger) {
  text-align: left; height: 34px; padding: 0 12px; border-radius: var(--radius-lg); border: 1px solid transparent;
  background: transparent; color: var(--muted); font: inherit; font-size: var(--fs-md); cursor: pointer;
}
.side-tabs button:not(.cb-btn):not(.btn):not(.dp-trigger):hover { background: var(--hover-strong); color: var(--text-strong); }
.side-tabs button.on { background: var(--select-bg); color: var(--text-strong); box-shadow: inset 3px 0 0 var(--select-mark); }
.side-tabs button.on:hover { background: var(--select-bg-hover); }
.side-tabs button.on .icon { color: var(--accent-hover); }
/* One line between the two columns; the settings sit on the page itself, no boxes. */
.settings-panel { min-width: 0; display: flex; flex-direction: column; gap: 32px; padding-left: 32px; border-left: 1px solid var(--line); }
.settings-panel .card:not(.ws-card) { padding: 0; border: 0; border-radius: 0; background: none; }
/* One width for every settings card; the form fills it and the buttons sit at its edge. */
.settings-panel .card:not(.ws-card) { width: 100%; max-width: 760px; }
.settings-panel .form { grid-template-columns: minmax(0, 1fr); gap: 14px; }
.settings-panel .card > p.muted, .sf-intro { margin: 0; color: var(--muted); font-size: var(--fs-md); }
.sf-head { display: flex; align-items: baseline; gap: 10px; min-height: 22px; }
.settings-panel .card:not(.ws-card) > .sf-head h2 { font-size: var(--fs-lg); }
.sf-status { font-size: var(--fs-sm); color: var(--muted); }
.sf-status.ok { color: var(--ok); }
.sf-status.bad { color: var(--warn); }
.sf-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* Settings → Notifications: channels (notify-channels.js). */
.notify-target { font-family: var(--mono); font-size: var(--fs-sm); color: var(--muted); overflow-wrap: anywhere; }
.notify-hint { font-size: var(--fs-sm); margin: 0; }
.notify-key { font-family: var(--mono); width: 100%; }
.notify-row-actions { display: flex; gap: 6px; align-items: center; justify-content: flex-end; }
.notify-kind { border: 0; padding: 0; margin: 0; display: grid; gap: 10px; min-width: 0; }
.notify-kind[hidden] { display: none; }
.notify-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.notify-filter { border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px; display: grid; gap: 10px; }
.notify-filter > summary { cursor: pointer; font-weight: 600; }
.notify-field { display: grid; gap: 6px; }
.notify-tree { max-height: 220px; overflow: auto; border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; }
.notify-tree-area { margin-left: 18px; }
.notify-tree-cams { margin-left: 18px; display: flex; flex-wrap: wrap; gap: 0 14px; }
.notify-checks { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.notify-schedule { display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: end; }
.notify-pace { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 600px) { .notify-schedule, .notify-pace { grid-template-columns: 1fr; } }
.sf-field { position: relative; }
/* The tab's save bar: last in the column, at the foot of the window while there is something to save. */
.settings-bar { order: 99; width: 100%; max-width: 760px; position: sticky; bottom: 0; z-index: 5; display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; margin: 0; padding: 12px 0; border-top: 1px solid var(--line); background: var(--bg); }
.settings-bar-text { margin-right: auto; color: var(--text-strong); font-size: var(--fs-md); }
.settings-bar.idle .settings-bar-text { color: var(--muted); }
.settings-bar.bad .settings-bar-text { color: var(--warn); }
.sf-bar { display: flex; justify-content: flex-end; gap: 8px; }
.sf-default { position: absolute; top: -2px; right: 0; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; border: 0; border-radius: var(--radius); background: none; color: var(--muted); }
.sf-field > .sf-label .sf-caption, .sf-field > .check { padding-right: 26px; }
.sf-default:hover { color: var(--text-strong); background: var(--hover-strong); }
.side-tabs button.unsaved::after { content: ""; display: inline-block; width: 7px; height: 7px; margin-left: 8px; border-radius: 50%; background: var(--warn); vertical-align: middle; }
.sf-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.sf-label { gap: 4px; }
.sf-caption { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.sf-restart { height: 18px; padding: 0 6px; font-size: var(--fs-xs); color: var(--warn); border-color: var(--warn-bg); background: var(--warn-bg); }
.sf-field .check .sf-restart { margin-left: 2px; }
.sf-field.changed input:not([type="checkbox"]), .sf-field.changed select, .sf-field.changed textarea { border-color: var(--accent); }
.sf-field.changed > .check { color: var(--text-strong); }
.sf-field.changed > .check input { outline: 2px solid var(--accent); outline-offset: 1px; }
.sf-field.off { opacity: 0.5; }
.sf-field > .err { font-size: var(--fs-sm); }
.sf-field.bad input, .sf-field.bad select, .sf-field.bad textarea { border-color: var(--bad); }
.settings-panel .form-grid { align-items: start; column-gap: 14px; row-gap: 14px; }
details.card > summary { cursor: pointer; font-weight: 600; color: var(--text-strong); }
details.card[open] > summary { margin-bottom: 12px; }
/* Each group: a heading with its icon, then its tabs, each with its icon; no frame, no lines. */
.side-tabs:not(.searching) { gap: 18px; }
.side-section { display: flex; flex-direction: column; gap: 4px; }
.side-group { display: flex; align-items: center; gap: 8px; padding: 2px 6px 4px; color: var(--text-strong); font-size: var(--fs-md); font-weight: 600; }
.side-group .icon { color: var(--accent); }
.side-items { display: flex; flex-direction: column; gap: 2px; }
.side-items button { display: flex; align-items: center; gap: 10px; height: 34px; }
.side-items button .icon { flex: none; }
.sf-inline { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; }
.sf-inline > label { flex: 1 1 240px; min-width: 0; }
@media (max-width: 720px) {
  .settings-layout { grid-template-columns: 1fr; }
  .settings-panel { padding-left: 0; border-left: 0; gap: 28px; }
  .side-tabs { padding-right: 0; margin-bottom: 18px; align-self: stretch; }
  /* One row that scrolls sideways, so the form starts near the top. */
  .side-tabs { position: static; flex-direction: row; flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; scrollbar-width: thin; }
  .side-tabs button { flex: none; }
  .side-tabs button.on { box-shadow: inset 0 -2px 0 var(--select-mark); }
  /* The groups stay apart on the one row: a line between them, no headings. */
  .side-tabs:not(.searching) { gap: 0; padding: 4px 0; }
  .side-section, .side-items { flex-direction: row; flex: none; gap: 4px; margin: 0; padding: 0; border: 0; background: none; }
  .side-section + .side-section { margin-left: 14px; }
  .side-tabs.searching { flex-direction: column; overflow: visible; }
  /* On the one row a group is marked by its icon only. */
  .side-group { flex: none; padding: 0 2px 0 4px; }
  .side-group span { display: none; }
}

/* Workspaces: the picker at the top, headings on Nguồn, Settings → Workspace. */
label.ws-pick { display: flex; flex-direction: row; align-items: center; gap: 8px; margin: 0; color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; }
label.ws-pick select { width: auto; min-width: 0; max-width: 240px; margin: 0; color: var(--text); font-size: var(--fs-md); }
/* The box itself is a control (md), not the 12 px of its "Workspace" caption. */
label.ws-pick .cb-btn { color: var(--text); font-size: var(--fs-md); }
.viewer-sources > .ws-pick .cb-btn { font-size: var(--fs-base); } /* the Viewer column's title: as before */
.page > .ws-pick { align-self: flex-start; }
.ws-head { display: flex; align-items: baseline; gap: 8px; margin: 14px 0 2px; font-size: var(--fs-lg); color: var(--text-strong); }
.ws-head:first-child { margin-top: 0; }
.ws-head .kind { color: var(--muted); font-size: var(--fs-sm); font-weight: 400; }
/* ---- Settings → Workspace: a list on the left, the one picked on the right ---- */
.ws-tab { display: flex; flex-direction: column; gap: 14px; max-width: 960px; }
.ws-flow { display: flex; flex-wrap: wrap; gap: 10px; }
.ws-flow-step { flex: 1 1 220px; display: flex; gap: 10px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-lg); }
.ws-flow-step .icon { margin-top: 2px; color: var(--accent); }
.ws-flow-step strong { display: block; color: var(--text-strong); font-size: var(--fs-md); }
.ws-flow-step span { color: var(--muted); font-size: var(--fs-sm); }
.ws-layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 28px; align-items: start; margin-top: 6px; }
.ws-list { display: flex; flex-direction: column; gap: 4px; }
.ws-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; text-align: left;
  border: 1px solid transparent; border-radius: var(--radius-lg); background: transparent; color: var(--text);
}
.ws-item .icon { flex: none; color: var(--muted); }
.ws-item:hover { background: var(--hover); }
.ws-item.on { background: var(--accent-dim); border-color: rgb(232 106 26 / 35%); }
.ws-item.on .icon { color: var(--accent-hover); }
.ws-item-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ws-item-text strong { color: var(--text-strong); font-size: var(--fs-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-item-text small { color: var(--muted); font-size: var(--fs-sm); }
.ws-add-btn {
  display: flex; align-items: center; gap: 8px; width: 100%; margin-top: 6px; padding: 10px 12px;
  border: 1px dashed var(--field-line); border-radius: var(--radius-lg); background: transparent; color: var(--muted); font-size: var(--fs-md);
}
.ws-add-btn:hover { color: var(--text-strong); border-color: var(--accent); }
.ws-detail { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.ws-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.ws-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ws-title h2 { margin: 0; font-size: var(--fs-xl); overflow: hidden; text-overflow: ellipsis; }
.ws-industry { gap: 5px; height: 24px; padding: 0 10px; color: var(--text); border-color: var(--line); }
.ws-actions { display: flex; gap: 8px; }
.ws-actions .btn, .ws-areas-head .btn { gap: 6px; }
.ws-why { margin: -6px 0 0; }
.ws-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.ws-stat { display: flex; flex-direction: column; gap: 2px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-lg); }
.ws-stat strong { font-size: var(--fs-num); line-height: 1.1; color: var(--text-strong); font-variant-numeric: tabular-nums; }
.ws-stat span { color: var(--muted); font-size: var(--fs-sm); }
.ws-areas-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 4px; }
.ws-areas-head h3 { margin: 0; font-size: var(--fs-lg); }
.ws-areas-table { width: 100%; border-collapse: collapse; }
.ws-areas-table th { padding: 8px 10px; text-align: left; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; border-bottom: 1px solid var(--line); }
.ws-areas-table td { padding: 10px; font-size: var(--fs-md); border-bottom: 1px solid var(--line); }
.ws-areas-table .num { text-align: right; width: 90px; font-variant-numeric: tabular-nums; }
.ws-area-name { display: flex; align-items: center; gap: 8px; color: var(--text-strong); font-weight: 600; }
.ws-area-name .icon { color: var(--muted); flex: none; }
.ws-row-actions { width: 84px; white-space: nowrap; text-align: right; }
.btn.ghost { width: 30px; height: 30px; padding: 0; border-color: transparent; color: var(--muted); }
.btn.ghost:hover { color: var(--text-strong); }
.btn.ghost.danger-text:hover:not(:disabled) { color: var(--bad); background: var(--hover); }
@media (max-width: 720px) {
  .ws-layout { grid-template-columns: 1fr; gap: 18px; }
  .ws-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* People and the audit log (users.js). */
.account-user { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.account-user .btn { justify-content: flex-start; }
.users-tab { display: flex; flex-direction: column; gap: 12px; }
.member-list { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.member-row { display: flex; flex-direction: column; gap: 4px; width: 100%; padding: 8px; border: 1px solid var(--line); border-radius: var(--radius-lg); }
.member-group { margin: 4px 0 0; font-size: var(--fs-sm); }
.member-place { font-weight: 600; }
.member-roles { display: flex; flex-wrap: wrap; gap: 4px 14px; padding-left: 24px; }
.member-roles[hidden] { display: none; }
.member-help { margin: 0; }
fieldset.members { border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 8px 10px; margin: 0; }
fieldset.members legend { padding: 0 4px; color: var(--muted); font-size: var(--fs-sm); }
.audit-ok { color: var(--ok); }
.audit-denied { color: var(--warn); }
.audit-error { color: var(--bad); }
.audit-detail { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 0 0 10px; }
.audit-detail dt { color: var(--muted); }
.audit-detail dd { margin: 0; overflow-wrap: anywhere; }
.audit-diff { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.audit-diff h3, .vw-dialog h3 { margin: 0 0 4px; font-size: var(--fs-sm); color: var(--muted); }
.audit-diff pre, .vw-dialog pre { margin: 0; padding: 8px; max-height: 280px; overflow: auto; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 720px) { .audit-diff { grid-template-columns: 1fr; } }
/* Add scenario: templates two a row, a heading per industry across the row. */
.tpl-list { max-height: 64vh; overflow: auto; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; align-items: stretch; }
.tpl-list > button[data-tpl="blank"] { grid-column: 1 / -1; min-height: 60px; }
.tpl-list .choice-text > span { line-height: 1.4; }
.tpl-group { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; margin: 10px 0 0; font-size: var(--fs-sm); color: var(--text-strong); font-weight: 600; }
.tpl-group .icon { color: var(--accent); }
.tpl-group small { color: var(--muted); font-weight: 400; }
@media (max-width: 640px) { .tpl-list { grid-template-columns: minmax(0, 1fr); } }

/* ---- The page before the console: login and first owner (gate.js) ---- */
.gate {
  position: fixed; inset: 0; z-index: 60; display: grid; grid-template-columns: minmax(0, 1fr) minmax(380px, 460px);
  background: var(--panel); overflow: auto;
}
.gate-stage {
  position: relative; overflow: hidden; min-height: 100%;
  background: radial-gradient(ellipse 70% 42% at 50% 31%, #1d2a2e, transparent), #0c0d0f;
}
/* The picture fades out under the words, so they stay readable over any part of it. */
.gate-stage::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to bottom, rgb(12 13 15 / 75%), transparent 22%, transparent 42%, rgb(12 13 15 / 88%) 62%, rgb(12 13 15 / 97%) 80%);
}
.gate-scene { position: absolute; inset: 0; width: 100%; height: 100%; }
.gate-floor line { stroke: #2a2e33; stroke-width: 1; }
.gate-zone { fill: rgb(61 220 151 / 8%); stroke: var(--zone-a); stroke-width: 1.5; stroke-dasharray: 8 6; }
.gate-zone-name { fill: var(--zone-a); font-size: 15px; paint-order: stroke; stroke: #0c0d0f; stroke-width: 3px; }
.gate-body { fill: #3a4047; }
.gate-glass { fill: #15181b; }
.gate-box { fill: rgb(255 255 255 / 3%); stroke: var(--zone-b); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.gate-label { fill: var(--text-strong); font-size: 14px; font-family: var(--mono); paint-order: stroke; stroke: #0c0d0f; stroke-width: 3px; }
.gate-car { animation: gate-car 11s linear infinite; }
.gate-person { animation: gate-person 16s ease-in-out infinite; }
.gate-person .gate-box { animation: gate-alert 16s step-end infinite; }
.gate-person .gate-out { animation: gate-out 16s step-end infinite; }
.gate-person .gate-in { fill: var(--accent); animation: gate-in 16s step-end infinite; }
.gate-scan { fill: url(#gate-scan); animation: gate-scan 7s linear infinite; }
@keyframes gate-car {
  0% { transform: translate(405px, 275px) scale(0.16); opacity: 0; }
  8% { opacity: 1; }
  88% { opacity: 1; }
  100% { transform: translate(-130px, 610px) scale(1.7); opacity: 0; }
}
@keyframes gate-person {
  0%, 100% { transform: translate(350px, 500px) scale(0.95); }
  50% { transform: translate(760px, 520px) scale(1.05); }
}
/* The person is inside the zone from about 21% to 79% of the walk there and back. */
@keyframes gate-alert { 0% { stroke: var(--zone-b); } 21% { stroke: var(--accent); } 79% { stroke: var(--zone-b); } }
@keyframes gate-out { 0% { opacity: 1; } 21% { opacity: 0; } 79% { opacity: 1; } }
@keyframes gate-in { 0% { opacity: 0; } 21% { opacity: 1; } 79% { opacity: 0; } }
@keyframes gate-scan { from { transform: translateY(-120px); } to { transform: translateY(800px); } }
@media (prefers-reduced-motion: reduce) {
  .gate-car, .gate-person, .gate-person .gate-box, .gate-person .gate-out, .gate-person .gate-in, .gate-scan { animation: none; }
  .gate-car { transform: translate(250px, 480px) scale(1); }
  .gate-person { transform: translate(680px, 520px); }
  .gate-person .gate-box { stroke: var(--accent); }
  .gate-person .gate-out { opacity: 0; }
  .gate-scan { display: none; }
}
.gate-brand { position: absolute; z-index: 1; top: 28px; left: 32px; display: flex; align-items: center; gap: 10px; color: var(--text-strong); font-size: var(--fs-lg); font-weight: 600; }
.gate-brand.small { position: static; visibility: hidden; }
.gate-brand b { font-weight: 700; color: var(--accent); }
.gate-mark { width: 28px; height: 28px; display: block; flex: none; }
.gate-brand.small .gate-mark { width: 22px; height: 22px; }
.gate-pitch { position: absolute; z-index: 1; left: 32px; right: 32px; bottom: 36px; max-width: 560px; }
.gate-pitch h1 { margin: 0 0 8px; font-size: clamp(26px, 3vw, 38px); line-height: 1.15; letter-spacing: -0.01em; text-wrap: balance; }
.gate-pitch { max-width: 680px; }
.gate-pitch > p { margin: 0; color: var(--text); font-size: var(--fs-lg); }
.gate-points { list-style: none; margin: 20px 0 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 24px; }
.gate-points li { display: flex; gap: 10px; align-items: flex-start; }
.gate-points .icon { flex: none; margin-top: 1px; color: var(--accent); }
.gate-points strong { display: block; color: var(--text-strong); font-size: var(--fs-base); }
.gate-points span { display: block; color: var(--muted); font-size: var(--fs-md); line-height: 1.4; }
/* Short windows: the four parts would cover the picture. */
@media (max-height: 640px) { .gate-points { display: none; } }
.gate-side { display: flex; flex-direction: column; min-height: 100vh; padding: 24px 40px; border-left: 1px solid var(--line); }
.gate-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gate-form { margin: auto 0; padding: 24px 0; display: flex; flex-direction: column; gap: 14px; }
.gate-form h2 { margin: 0; font-size: 24px; } /* the login title: the only heading above --fs-xl */
.gate-lead { margin: -6px 0 6px; color: var(--muted); }
.gate-fields { display: flex; flex-direction: column; gap: 14px; }
.gate-fields label { display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-md); color: var(--text); }
.gate-fields input {
  width: 100%; height: 42px; padding: 0 12px; background: var(--input); border: 1px solid var(--input-line); border-radius: var(--radius);
}
.gate-fields input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.gate-pass { position: relative; display: block; }
.gate-pass input { padding-right: 44px; }
.gate-eye {
  position: absolute; top: 3px; right: 3px; width: 36px; height: 36px; display: grid; place-items: center;
  background: none; border: 0; border-radius: var(--radius); color: var(--muted);
}
.gate-eye:hover { background: var(--hover-strong); color: var(--text-strong); }
.gate-eye[aria-pressed=true] { color: var(--accent); }
.gate-caps { margin: -4px 0 0; color: var(--warn); font-size: var(--fs-sm); }
.gate-form .err:empty { display: none; }
.gate-submit { position: relative; height: 44px; justify-content: center; font-weight: 600; margin-top: 4px; }
.gate-submit.busy .gate-submit-text { visibility: hidden; }
.gate-submit.busy::after {
  content: ""; position: absolute; top: calc(50% - 9px); left: calc(50% - 9px); width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid rgb(255 255 255 / 35%); border-top-color: var(--on-accent); animation: gate-spin 0.8s linear infinite;
}
@keyframes gate-spin { to { transform: rotate(360deg); } }
.gate-health { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--muted); font-size: var(--fs-sm); }
.gate-health > span:first-child { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
.gate-health[data-state=ok] > span:first-child { background: var(--ok); }
.gate-health[data-state=bad] > span:first-child { background: var(--bad); }
.gate-health[data-state=bad] { color: var(--bad); }
@media (max-width: 860px) {
  .gate { display: block; }
  .gate-stage { display: none; }
  .gate-brand.small { visibility: visible; }
  .gate-side { padding: 16px; border-left: 0; }
  .gate-form { width: min(420px, 100%); margin: auto; }
}
.vw-event-nopic { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); }

.plate-rules-note { margin: 0 0 12px; padding: 8px 12px; border-left: 3px solid var(--accent, currentColor); }
.plate-rules-try { margin-bottom: 8px; }
.plate-rules-try p { min-height: 22px; margin: 6px 0 0; }
.plate-rules-formats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 16px; margin-bottom: 8px; }
.plate-rules-format { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr) 24px; gap: 6px; align-items: center; }
.plate-rules-format input { min-width: 0; width: 100%; box-sizing: border-box; }
.plate-rules-format .icon-remove { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: 0; background: none; color: var(--muted); cursor: pointer; font-size: var(--fs-lg); }
.plate-rules-format .icon-remove:hover { color: var(--text-strong); }
.plate-rules-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 16px; margin-bottom: 8px; }
.vw-dialog .dlg-body label.plate-rules-check { display: flex; flex-direction: row; justify-content: flex-start; align-items: center; gap: 8px; margin-bottom: 8px; text-align: left; }
.vw-dialog .dlg-body .plate-rules-check input { width: auto; flex: none; margin: 0; }
.vw-dialog .dlg-body .plate-rules-add { align-self: flex-start; width: auto; }
@media (max-width: 640px) {
  .plate-rules-formats, .plate-rules-fields { grid-template-columns: minmax(0, 1fr); }
}

/* Máy từ xa (#/remote) */
.remote-stage { background: #000; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.remote-stage video { display: block; width: 100%; max-height: 70vh; background: #000; }
.remote-stage video:focus { outline: none; }
#remote-clip { flex: 1; min-width: 160px; width: auto; }

/* Máy từ xa: màn chính */
.rd-lobby { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }
.rd-column { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.rd-column:empty { display: none; }
.rd-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.rd-card-head h2 { margin: 0; flex: 1; }
.rd-config { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.rd-config .card { padding: 0; border: 0; background: none; }
.rd-credential { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 0 12px; padding: 10px 0; border-top: 1px solid var(--line); }
.rd-credential > .muted { grid-column: 1 / -1; font-size: var(--fs-sm); }
.rd-credential strong { font-size: var(--fs-code); font-weight: 600; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; color: var(--text-strong); overflow-wrap: anywhere; }
.rd-credential-actions { display: flex; gap: 4px; }
.rd-icon-btn { display: inline-flex; justify-content: center; width: 32px; height: 32px; padding: 0; border: 1px solid var(--line); border-radius: var(--radius); background: transparent; color: var(--text); }
.rd-icon-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.rd-viewers { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.rd-viewers h3 { margin: 0 0 6px; font-size: var(--fs-md); }
.rd-row { display: flex; align-items: center; gap: 12px; padding: 6px 0; min-width: 0; }
.rd-row + .rd-row { border-top: 1px solid var(--line); }
.rd-row-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.rd-link { padding: 0; border: 0; background: none; color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }
.rd-link:hover { text-decoration: underline; }
@media (max-width: 900px) { .rd-lobby { grid-template-columns: minmax(0, 1fr); } }

/* Máy từ xa: trong phiên */
.rd-session { position: fixed; inset: 0; z-index: 50; background: #000; overflow: hidden; }
.rd-session video { display: block; width: 100%; height: 100%; object-fit: contain; background: #000; cursor: default; }
.rd-session video:focus { outline: none; }
.rd-toolbar {
  position: absolute; top: 8px; left: 0; right: 0; z-index: 2; display: flex; align-items: center; gap: 10px;
  width: fit-content; max-width: calc(100vw - 16px); margin-inline: auto; padding: 4px 8px; border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: rgb(18 19 20 / 92%); color: var(--text); transition: transform 0.2s ease;
}
.rd-toolbar.away { transform: translateY(calc(-100% - 12px)); }
.rd-host { padding: 0 4px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--text-strong); white-space: nowrap; }
.rd-tools { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.rd-group { position: relative; display: flex; gap: 2px; padding-right: 6px; margin-right: 2px; border-right: 1px solid var(--line); }
.rd-group > * { position: relative; }
.rd-tool { display: inline-flex; justify-content: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--radius); background: transparent; color: var(--text); }
.rd-tool:hover { background: var(--line); }
.rd-tool.on { background: var(--select-bg-hover); color: var(--text-strong); box-shadow: inset 0 0 0 1px var(--select-line); }
.rd-tool.danger { color: var(--bad); }
.rd-tool.danger:hover { background: var(--bad); color: #fff; }
.rd-rec { color: var(--bad); font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rd-handle { position: absolute; top: 0; left: 50%; z-index: 1; width: 64px; height: 8px; padding: 0; transform: translateX(-50%); border: 0; border-radius: 0 0 6px 6px; background: rgb(255 255 255 / 35%); }
.rd-menu { position: absolute; top: calc(100% + 8px); left: 0; z-index: 3; display: flex; flex-direction: column; min-width: 200px; padding: 4px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); }
.rd-menu-item { padding: 6px 10px; border: 0; border-radius: var(--radius); background: transparent; color: var(--text); text-align: left; white-space: nowrap; }
.rd-menu-item:hover { background: var(--line); }
.rd-menu-item.on { color: var(--accent); font-weight: 600; }
.rd-stats { position: absolute; top: 60px; right: 12px; z-index: 2; display: grid; grid-template-columns: auto auto; gap: 3px 16px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: rgb(18 19 20 / 92%); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.rd-stats h3 { grid-column: 1 / -1; margin: 0 0 4px; font-size: var(--fs-md); color: var(--accent); }
.rd-stats strong { font-weight: 600; text-align: right; color: var(--text-strong); }
.rd-transfers { position: absolute; right: 12px; bottom: 12px; z-index: 2; display: flex; flex-direction: column; gap: 6px; width: min(340px, calc(100vw - 24px)); }
.rd-transfers:empty { display: none; }
.rd-transfer { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px 6px; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--radius); background: rgb(18 19 20 / 92%); font-size: var(--fs-sm); }
.rd-transfer-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-transfer progress { grid-column: 1 / -1; width: 100%; height: 6px; accent-color: var(--accent); }
.rd-notice { position: absolute; left: 50%; bottom: 16px; z-index: 2; max-width: min(90vw, 560px); margin: 0; padding: 6px 12px; transform: translateX(-50%); border-radius: var(--radius); background: rgb(18 19 20 / 92%); color: var(--text-strong); font-size: var(--fs-md); }
.rd-clip-box { width: 100%; }
/* A dialog opened during a session sits over the picture. */
.rd-in-session .vw-modal { inset: 0; z-index: 70; }
.rd-in-session { overflow: hidden; }
/* The tab already names what is below it. */
.rd-config .card > .sf-head > h2 { display: none; }
.rd-config .sf-head { min-height: 0; }

/* Floor plans of a workspace (Settings → Workspace) and the plan editor (js/maps.js). */
.ws-maps { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.ws-map-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.ws-map-card { display: flex; flex-direction: column; gap: 8px; padding: 10px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg); }
.ws-map-pic { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; aspect-ratio: 16 / 10; padding: 0; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius); background: var(--stage); color: var(--muted); cursor: pointer; }
.ws-map-pic img { width: 100%; height: 100%; object-fit: contain; }
.ws-map-words { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ws-map-words strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-strong); }
.ws-map-actions { display: flex; gap: 6px; align-items: center; }
.ws-map-actions .btn:first-child { margin-right: auto; gap: 6px; }

.vw-dialog.wide.plan-dialog { width: min(1280px, calc(100vw - 32px)); }
.pe { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 16px; min-height: 0; }
.pe-side { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.pe-side .hint { margin: 0; }
.pe-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; max-height: 36vh; overflow: auto; }
.pe-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border: 1px solid transparent; border-radius: var(--radius); background: none; color: var(--src-camera); text-align: left; cursor: pointer; }
.pe-item.file { color: var(--src-file); }
.pe-item:hover { background: var(--select-bg); }
.pe-item.on { background: var(--select-bg); border-color: var(--select-line); }
.pe-item-words { display: flex; flex-direction: column; min-width: 0; }
.pe-item-words strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-strong); font-weight: 500; }
.pe-item-words small { font-size: var(--fs-sm); }
.pe-item-words small.ok { color: var(--ok); }
.pe-pick { display: flex; flex-direction: column; gap: 8px; padding-top: 10px; border-top: 1px solid var(--line); }
.pe-pick .hint, .pe-pick p { margin: 0; }
.pe-pick .btn { align-self: flex-start; white-space: nowrap; }
.pe-fov { display: grid; grid-template-columns: auto minmax(0, 1fr) 44px; gap: 8px; align-items: center; }
.pe-plan { display: flex; justify-content: center; align-items: flex-start; min-width: 0; }
.pe-stage { position: relative; background: var(--stage); border-radius: var(--radius); touch-action: none; user-select: none; overflow: hidden; }
.pe-stage img, .pe-stage svg, .pe-pins { position: absolute; inset: 0; width: 100%; height: 100%; }
.pe-stage img { object-fit: fill; pointer-events: none; }
.pe-stage svg { pointer-events: none; overflow: visible; }
.pe-cone { fill: rgb(91 140 255 / 18%); stroke: rgb(91 140 255 / 70%); stroke-width: 1.5; }
.pe-cone.file { fill: rgb(185 140 245 / 18%); stroke: rgb(185 140 245 / 70%); }
.pe-cone.on { fill: rgb(232 106 26 / 22%); stroke: var(--accent); }
.pe-pin { position: absolute; transform: translate(-50%, -50%); display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 2px solid var(--bg); border-radius: 50%; background: var(--src-camera); color: #0b0c0d; cursor: grab; }
.pe-pin.file { background: var(--src-file); }
.pe-pin.on { background: var(--accent); box-shadow: 0 0 0 3px rgb(232 106 26 / 35%); z-index: 2; }
.pe-pin:focus-visible { outline: 2px solid var(--text-strong); outline-offset: 2px; }
.pe-pin-label { position: absolute; top: calc(100% + 2px); left: 50%; transform: translateX(-50%); padding: 1px 5px; border-radius: 4px; background: rgb(0 0 0 / 70%); color: #fff; font-size: var(--fs-sm); white-space: nowrap; pointer-events: none; }
.pe-handle { position: absolute; transform: translate(-50%, -50%); width: 16px; height: 16px; border: 2px solid #fff; border-radius: 50%; background: var(--accent); cursor: grab; z-index: 3; }
.pe-handle.unset { background: transparent; border-style: dashed; }
@media (max-width: 760px) {
  .pe { grid-template-columns: 1fr; }
  .pe-list { max-height: 24vh; }
}

/* Event dialog with a trace (js/events.js renderHits): the picture leaves room for the journey. */
.vw-dialog header .ev-expand { margin-left: auto; }
.vw-dialog header .ev-expand + .close { margin-left: 0; }
.vw-dialog.wide.event-dialog.expanded { width: calc(100vw - 32px); max-height: calc(100vh - 32px); }
.event-dialog.tracing .ev-media-body > img, .event-dialog.tracing .ev-media-body > video, .event-dialog.tracing .ev-scene canvas { max-height: 40vh; }
.event-dialog.expanded.tracing .ev-media-body > img, .event-dialog.expanded.tracing .ev-media-body > video, .event-dialog.expanded.tracing .ev-scene canvas { max-height: 48vh; }
.event-dialog.tracing .ev-scene canvas { object-fit: contain; }
/* With a trace on wide screens the journey goes under the picture, beside the facts. */
@media (min-width: 900px) {
  .event-dialog.tracing .dlg-body { display: grid; grid-template-columns: minmax(0, 1fr) 280px; grid-template-rows: auto 1fr; column-gap: 16px; row-gap: 12px; align-items: start; }
  .event-dialog.tracing .ev { display: contents; }
  .event-dialog.tracing .ev-media { grid-column: 1; grid-row: 1; }
  .event-dialog.tracing .ev-side { grid-column: 2; grid-row: 1 / span 2; }
  .event-dialog.tracing .ev-trace { grid-column: 1; grid-row: 2; margin-top: 0; min-width: 0; }
}
.vs-min { white-space: nowrap; }
.vs-step { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; }
.vs-step-at { font-size: var(--fs-sm); min-width: 48px; text-align: center; }
.vw-event.current .vw-event-badge { background: var(--accent); color: #fff; }

/* Danh sách: the stays as cards down a time line. */
.vs-tline { list-style: none; margin: 0; padding: 0 0 0 64px; position: relative; display: flex; flex-direction: column; gap: 0; }
.vs-tline::before { content: ""; position: absolute; left: 71px; top: 8px; bottom: 8px; width: 2px; background: var(--line); }
.vs-tline-item { position: relative; display: flex; align-items: center; gap: 0; padding-left: 28px; }
.vs-tline-item::before { content: ""; position: absolute; left: 2px; top: 50%; width: 12px; height: 12px; margin-top: -6px; border-radius: 50%; background: var(--panel); border: 2px solid var(--src-camera); }
.vs-tline-item.current::before { background: var(--accent); border-color: var(--accent); }
.vs-tline-time { position: absolute; left: -64px; top: 50%; transform: translateY(-50%); width: 58px; text-align: right; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; color: var(--muted); }
.vs-tline-time small { display: block; font-size: var(--fs-xs); }
.vs-tline-item.current .vs-tline-time { color: var(--text-strong); font-weight: 600; }
.vs-tline-card { display: flex; align-items: center; gap: 12px; width: 100%; max-width: 560px; padding: 6px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); color: var(--text); text-align: left; cursor: pointer; }
.vs-tline-card:hover { border-color: var(--select-line); background: var(--select-bg); }
.vs-tline-item.current .vs-tline-card { background: var(--select-bg); border-color: var(--select-line); }
.vs-tline-pic { flex: none; display: grid; place-items: center; width: 120px; aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--radius); background: var(--stage); }
.vs-tline-pic img { width: 100%; height: 100%; object-fit: cover; }
.vs-tline-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.vs-tline-meta strong { color: var(--text-strong); font-size: var(--fs-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vs-tline-meta .muted { font-size: var(--fs-sm); }
.vs-tline-gap { position: relative; padding: 6px 0 6px 28px; font-size: var(--fs-xs); color: var(--muted); }
.vs-tline-gap::before { content: "↓"; margin-right: 6px; }
.vs-tline-gap.wrong { color: var(--bad); }
@media (max-width: 480px) { .vs-tline-pic { width: 88px; } }

.vs-tl { display: flex; flex-direction: column; gap: 8px; --tl-axis: 22px; --tl-lane: 30px; }
.vs-tl-tools { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: var(--fs-sm); }
.vs-tl-zoom { display: inline-flex; gap: 2px; }
.vs-tl-grid { display: grid; grid-template-columns: minmax(80px, 150px) minmax(0, 1fr); border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg); }
.vs-tl-names { display: flex; flex-direction: column; padding-top: var(--tl-axis); border-right: 1px solid var(--line); min-width: 0; }
.vs-tl-name { height: var(--tl-lane); line-height: var(--tl-lane); padding: 0 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-sm); color: var(--text); }
.vs-tl-scroll { overflow-x: auto; overflow-y: hidden; min-width: 0; }
.vs-tl-track { position: relative; min-width: 100%; height: calc(var(--tl-axis) + var(--lanes) * var(--tl-lane) + 4px);
  background: repeating-linear-gradient(to bottom, transparent 0 calc(var(--tl-lane) - 1px), rgb(255 255 255 / 4%) calc(var(--tl-lane) - 1px) var(--tl-lane)) 0 var(--tl-axis) / 100% calc(100% - var(--tl-axis)) no-repeat; }
.vs-tl-axis { position: absolute; inset: 0; pointer-events: none; }
.vs-tl-tick { position: absolute; top: 0; bottom: 0; padding: 3px 0 0 4px; border-left: 1px solid var(--line); font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.vs-tl-stay { position: absolute; top: calc(var(--tl-axis) + var(--lane) * var(--tl-lane) + 7px); height: 16px; min-width: 10px; margin-left: -5px; padding: 0 5px; box-sizing: content-box;
  border: 1px solid rgb(91 140 255 / 70%); border-radius: 8px; background: rgb(91 140 255 / 30%); cursor: pointer; }
.vs-tl-stay:hover { background: rgb(91 140 255 / 45%); }
.vs-tl-stay.origin { border-style: dashed; }
.vs-tl-stay.on { background: rgb(91 140 255 / 60%); border-color: var(--text-strong); }
.vs-tl-stay.current { border-color: var(--accent); box-shadow: 0 0 0 2px rgb(232 106 26 / 35%); }
.vs-tl-stay i { position: absolute; top: 50%; width: 6px; height: 6px; margin: -3px 0 0 2px; border-radius: 50%; background: #fff; }
.vs-tl-stay i.pass { background: transparent; border: 1px solid #fff; }
.vs-tl-leg { position: absolute; top: calc(var(--tl-axis) + var(--top) * var(--tl-lane) + 15px); height: calc((var(--rows) - 1) * var(--tl-lane)); --leg: var(--muted); }
.vs-tl-leg.clash { --leg: var(--bad); }
/* From the end of one stay to the start of the next: a diagonal between their lanes. */
.vs-tl-leg.down { background: linear-gradient(to top right, transparent calc(50% - 1px), var(--leg) 50%, transparent calc(50% + 1px)); }
.vs-tl-leg.up { background: linear-gradient(to bottom right, transparent calc(50% - 1px), var(--leg) 50%, transparent calc(50% + 1px)); }
.vs-tl-leg.same { height: 0; border-top: 1px dashed var(--leg); }
.vs-tl-gap { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 0 4px; border-radius: 4px; background: var(--panel); font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.vs-tl-leg.clash .vs-tl-gap { color: var(--bad); }
.vs-tl-detail { display: flex; flex-direction: column; gap: 6px; }

/* A journey on a floor plan (renderHits, view "plan"). */
.vs-plan { display: flex; flex-direction: column; gap: 8px; }
.vs-plan-stage { position: relative; align-self: center; background: var(--stage); border-radius: var(--radius); overflow: hidden; user-select: none; }
.vs-plan-stage img, .vs-plan-stage svg, .vs-plan-marks { position: absolute; inset: 0; width: 100%; height: 100%; }
.vs-plan-stage img { object-fit: fill; pointer-events: none; }
.vs-plan-stage svg { pointer-events: none; }
.vs-plan-cone { fill: rgb(91 140 255 / 14%); stroke: none; }
/* Journey legs: dark navy on a white casing (orange and pink are road colours on map tiles),
   red dashed when the leg is too fast or overlaps. */
.vs-plan-leg-casing { fill: none; stroke: #fff; stroke-width: 10; stroke-opacity: 0.95; stroke-linejoin: round; stroke-linecap: round; }
.vs-plan-leg { stroke: #0b1f4d; stroke-width: 5; }
.vs-plan-leg.wrong { stroke: #dc2626; stroke-dasharray: 10 7; }
.vs-plan-arrow { fill: #0b1f4d; stroke: #fff; stroke-width: 1.5; paint-order: stroke; vector-effect: non-scaling-stroke; }
.vs-plan-arrow.wrong { fill: #dc2626; }
.vs-plan-gap { position: absolute; transform: translate(-50%, -50%); padding: 2px 7px; border: 2px solid #fff; border-radius: 6px; background: #0b1f4d; color: #fff;
  font-size: var(--fs-sm); font-weight: 700; white-space: nowrap; box-shadow: 0 2px 6px rgb(0 0 0 / 45%); }
.vs-plan-gap.wrong { background: #dc2626; color: #fff; }
.vs-plan-other { position: absolute; width: 8px; height: 8px; transform: translate(-50%, -50%); border-radius: 50%; background: rgb(120 120 120 / 80%); border: 1px solid #fff; }
.vs-plan-stop { position: absolute; transform: translate(-50%, -50%); display: grid; place-items: center; min-width: 24px; height: 24px; padding: 0 6px; border: 2px solid #fff; border-radius: 12px; background: var(--src-camera); color: #0b0c0d; cursor: pointer; z-index: 1; }
.vs-plan-stop b { font-size: var(--fs-sm); line-height: 1; }
.vs-plan-stop.on { background: var(--text-strong); }
.vs-plan-stop.current { border-color: var(--accent); box-shadow: 0 0 0 3px rgb(232 106 26 / 40%); }
.vs-plan-name { position: absolute; top: calc(100% + 2px); left: 50%; transform: translateX(-50%); padding: 1px 5px; border-radius: 4px; background: rgb(0 0 0 / 70%); color: #fff; font-size: var(--fs-xs); white-space: nowrap; pointer-events: none; }
.vs-plan-runner { position: absolute; width: 14px; height: 14px; transform: translate(-50%, -50%); border-radius: 50%; background: var(--accent); border: 2px solid #fff; box-shadow: 0 0 0 4px rgb(232 106 26 / 35%); z-index: 2; pointer-events: none; }
.vs-plan-player { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px; align-items: center; font-size: var(--fs-sm); }
.vs-plan-elsewhere { margin: 0; font-size: var(--fs-sm); }

/* World maps (js/geomap.js, Leaflet): the editor and the journey. */
.ws-map-adds { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.ws-map-pic.world { background: linear-gradient(135deg, #1b2a33, #13202a); color: var(--src-camera); }
.ws-map-pic.world small { color: var(--muted); font-size: var(--fs-xs); }
.pe-world { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.pe-geo { width: 100%; height: 62vh; border-radius: var(--radius); background: var(--stage); }
.pe-geo-bar { display: flex; justify-content: flex-end; }
.vs-geo-stage { width: 100%; height: 46vh; border-radius: var(--radius); background: var(--stage); }
.pe-geo .leaflet-container, .vs-geo-stage.leaflet-container { font: inherit; }
.leaflet-container .geo-pin-wrap, .leaflet-container .geo-badge { background: none; border: 0; }
/* On light map tiles: strong fills, a white edge and a dark ring so pins and handles stand out. */
.geo-pin { position: relative; display: grid; place-items: center; width: 30px; height: 30px; box-sizing: border-box; border: 2px solid #fff; border-radius: 50%;
  background: #1f5bff; color: #fff; cursor: grab; box-shadow: 0 0 0 2px rgb(0 0 0 / 65%), 0 3px 8px rgb(0 0 0 / 45%); }
.geo-pin svg { stroke-width: 2.25; }
.geo-pin.file { background: #8a3ffc; }
.geo-pin.on { background: var(--accent); box-shadow: 0 0 0 2px rgb(0 0 0 / 70%), 0 0 0 6px rgb(232 106 26 / 45%), 0 3px 8px rgb(0 0 0 / 45%); }
.geo-pin .pe-pin-label { top: calc(100% + 4px); background: rgb(0 0 0 / 85%); font-weight: 600; }
.pe-handle.geo { position: static; display: block; transform: none; box-sizing: border-box; width: 22px; height: 22px; border: 4px solid var(--accent); background: #fff;
  box-shadow: 0 0 0 2px rgb(0 0 0 / 70%), 0 3px 8px rgb(0 0 0 / 45%); }
.pe-handle.geo.unset { border-style: solid; background: rgb(255 255 255 / 85%); border-color: #1f5bff; }
.pe-handle.geo:hover { transform: scale(1.12); }
.leaflet-container path.geo-guide { stroke: #111; stroke-width: 2; stroke-dasharray: 4 4; fill: none; }
.leaflet-container path.geo-cone { fill: rgb(31 91 255 / 28%); stroke: #1f5bff; stroke-width: 2; stroke-opacity: 1; fill-opacity: 1; }
.leaflet-container path.geo-cone.file { fill: rgb(138 63 252 / 28%); stroke: #8a3ffc; }
.leaflet-container path.geo-cone.on { fill: rgb(232 106 26 / 32%); stroke: #c2410c; stroke-width: 2.5; }
.leaflet-container path.geo-leg-casing { stroke: #fff; stroke-width: 11; stroke-opacity: 0.95; fill: none; stroke-linejoin: round; stroke-linecap: round; }
.leaflet-container path.geo-leg { stroke: #0b1f4d; stroke-width: 6; stroke-opacity: 1; fill: none; stroke-linejoin: round; stroke-linecap: round; }
.leaflet-container path.geo-leg.wrong { stroke: #dc2626; stroke-dasharray: 12 9; }
.leaflet-container .geo-arrow { background: none; border: 0; }
.geo-arrow i { display: grid; place-items: center; width: 22px; height: 22px; box-sizing: border-box; border: 2px solid #fff; border-radius: 50%; background: #0b1f4d; color: #fff;
  box-shadow: 0 1px 4px rgb(0 0 0 / 50%); }
.geo-arrow.wrong i { background: #dc2626; }
.geo-arrow svg { stroke-width: 3.5; }
.leaflet-container .geo-gap-icon { background: none; border: 0; }
.geo-gap-icon .vs-plan-gap { position: absolute; left: 0; top: 0; }
.geo-badge.geo-stop { display: grid; place-items: center; box-sizing: border-box; border: 2px solid #fff; border-radius: 12px; background: #1f5bff; color: #fff; cursor: pointer;
  box-shadow: 0 0 0 2px rgb(0 0 0 / 65%), 0 3px 8px rgb(0 0 0 / 45%); }
.geo-badge.geo-stop .vs-plan-name { background: rgb(0 0 0 / 85%); font-weight: 600; }
.geo-badge.geo-stop b { font-size: var(--fs-sm); line-height: 1; white-space: nowrap; }
.geo-badge.geo-stop .vs-plan-name { top: calc(100% + 2px); }
.geo-badge.geo-stop.on { background: #fff; color: #111; }
.geo-badge.geo-stop.current { border-color: var(--accent); box-shadow: 0 0 0 3px rgb(232 106 26 / 40%); }
.geo-badge.geo-runner { box-sizing: border-box; border-radius: 50%; background: var(--accent); border: 2px solid #fff; box-shadow: 0 0 0 4px rgb(232 106 26 / 35%); }

/* Drawn ways between cameras (js/paths.js): in the editor and on journeys. */
.pe-mode { margin-bottom: 2px; }
.pe-paths { display: flex; flex-direction: column; gap: 8px; }
.pe-paths .hint, .pe-paths p { margin: 0; }
.pe-paths > .btn { align-self: flex-start; }
.pe-path-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; max-height: 40vh; overflow: auto; }
.pe-path-list li { display: flex; align-items: center; gap: 4px; border: 1px solid transparent; border-radius: var(--radius); }
.pe-path-list li.on { background: var(--select-bg); border-color: var(--select-line); }
.pe-path-item { display: flex; flex-direction: column; align-items: flex-start; flex: 1; min-width: 0; padding: 6px 8px; border: 0; background: none; color: var(--text); text-align: left; cursor: pointer; }
.pe-path-item strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-strong); font-weight: 500; }
.pe-path-item small { font-size: var(--fs-sm); }
.plan-dialog.drawing-paths .pe-stage, .plan-dialog.drawing-paths .pe-geo { cursor: crosshair; }
.pe-stage polyline.pe-path { fill: none; stroke: #0f9d58; stroke-width: 4; stroke-linejoin: round; stroke-linecap: round; }
.pe-stage polyline.pe-path.on { stroke: var(--accent); }
.pe-stage polyline.pe-path.drawing { stroke: var(--accent); stroke-dasharray: 6 5; }
.pe-path-dot { position: absolute; width: 10px; height: 10px; transform: translate(-50%, -50%); border-radius: 50%; background: #fff; border: 2px solid var(--accent); pointer-events: none; }
.leaflet-container path.geo-path { stroke: #0f9d58; stroke-width: 5; stroke-opacity: 0.9; fill: none; stroke-linejoin: round; stroke-linecap: round; }
.leaflet-container path.geo-path.on { stroke: var(--accent); }
.leaflet-container path.geo-path.drawing { stroke: var(--accent); stroke-dasharray: 8 6; }
.leaflet-container path.geo-path-dot { fill: #fff; fill-opacity: 1; stroke: var(--accent); stroke-width: 2; }
.vs-plan-stage polyline.vs-plan-leg { fill: none; stroke-linejoin: round; stroke-linecap: round; }

/* Kịch bản → Khối luật: a block, what is known of it, and its button on the right. */
.block-row { display: flex; align-items: center; gap: 12px; padding: 6px 0; }
.block-row > .grow { flex: 1; min-width: 0; }

/* Login security (A1): the QR code to scan, the recovery codes, the two parts of the dialog. */
.totp-qr { display: flex; justify-content: center; padding: 8px 0; }
.totp-qr svg { border-radius: var(--radius-lg); }
.vw-dialog pre.recovery-codes { columns: 2; font-size: var(--fs-md, 14px); line-height: 1.8; text-align: center; }
.security-part + .security-part { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line); }
.security-part h3 { margin: 0 0 6px; }

/* Remote access (A2a): the pairing code, big enough to read off the screen. */
.vw-dialog .pair-code { font: 600 2rem/1.2 var(--mono, ui-monospace, monospace); letter-spacing: 0.15em; text-align: center; margin: 12px 0; }
.remote-owner { margin: 12px 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.remote-owner p { margin: 0; }

/* Remote access (A2d): remote.html on app.aiopenviewer.com and the console opened from it. */
.remote-page { min-height: 100vh; background: var(--bg); }
.remote-top { display: flex; align-items: center; gap: 12px; padding: 12px 20px; border-bottom: 1px solid var(--line); }
.remote-top .lang { margin-left: auto; }
.remote-who { display: flex; align-items: center; gap: 10px; }
.remote-main { max-width: 760px; margin: 32px auto; padding: 0 16px; display: grid; gap: 16px; }
.remote-card h2 { margin-top: 0; }
.remote-form { display: grid; gap: 12px; max-width: 420px; }
.remote-form .btn.link { background: none; border: 0; padding: 0; color: var(--accent, inherit); text-align: left; cursor: pointer; }
.remote-nodes { display: grid; gap: 10px; margin: 12px 0; }
.remote-node { border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 12px 14px; display: grid; gap: 4px; }
.remote-node-head { display: flex; align-items: center; gap: 8px; }
.remote-node-head .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted, #888); }
.remote-node-head .dot.on { background: #3fb950; }
.remote-node-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; align-items: center; }
.remote-node-actions .err { margin: 0; }
.remote-note { padding: 10px 14px; border: 1px solid var(--line); border-radius: var(--radius-lg); }
.remote-boot { position: fixed; inset: 0; z-index: 1000; display: grid; place-content: center; gap: 12px; text-align: center; background: var(--bg); }
.remote-boot p { max-width: 520px; margin: 0 auto; }
.remote-boot .btn { justify-self: center; }
.account-remote { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
