/* ============================================================================
   TeamMonitor — Mission Control (redesign)
   Design tokens are the EXACT values from design_handoff_team_monitor/README.md.
   This is a hi-fi DARK theme (the design is dark-only): app bg #0b0c0f, one accent
   #3794ff, and a HARD 4-colour agent-state system (running/thinking/awaiting/idle)
   read by colour everywhere. Existing var NAMES are preserved (so the established
   widgets/layout CSS keeps working) but their VALUES are remapped to the mission-
   control palette; new tokens (fg-2, disabled, accent-wash, agent-state, platform-
   kind) are added for the redesign. Fonts: Geist + Geist Mono. Pure CSS.
   ========================================================================== */
:root {
  color-scheme: dark;
  /* surfaces (README token table) */
  --bg: #0b0c0f;          /* app background, editor/terminal surface */
  --bg-elev: #0e1014;     /* rail, side columns, inputs */
  --panel: #111318;       /* popovers, dimmed cards */
  --panel-2: #16181d;     /* cards, composer, buttons-secondary */
  --border: rgba(255, 255, 255, 0.08);        /* hairline (.06–.12) */
  --border-strong: rgba(255, 255, 255, 0.12); /* stronger hairline */
  /* text */
  --fg: #e6e8ec;          /* primary text */
  --fg-strong: #f2f3f5;   /* headings / strongest (design uses fg for most) */
  --fg-2: #c4c8ce;        /* secondary text */
  --muted: #8b909a;       /* tertiary / labels */
  --faint: #6e7681;       /* captions / mono labels */
  --disabled: #5a6069;    /* disabled / idle */
  /* accent — ONE accent everywhere (invariant #1) */
  --accent: #3794ff;
  --accent-press: #2b7bdc;
  --accent-wash: rgba(55, 148, 255, 0.12);    /* active-tab / selected-row fill */
  --link: #3794ff;
  /* agent-state HARD system (README): read status BY COLOUR everywhere.
     running/ok=green, thinking/warn=amber, awaiting/error=red, idle=grey. */
  --state-running: #46d19e;
  --state-thinking: #e0b341;
  --state-awaiting: #ff6b6b;
  --state-awaiting-text: #ff9b9b;
  --state-idle: #5a6069;
  --state-running-glow: rgba(70, 209, 158, 0.16);
  --state-awaiting-glow: rgba(255, 107, 107, 0.18);
  /* semantic aliases kept for existing CSS (mapped onto the state colours) */
  --green: #46d19e;       /* success / +adds */
  --red: #ff6b6b;         /* error / −dels */
  --yellow: #e0b341;      /* warn / modified M / unsaved dot */
  --blue: #7fb0f0;        /* host-kind accent */
  --sub-agent: #c586c0;   /* docker kind / sub-agents */
  /* platform-kind badges (README) */
  --kind-host-fg: #7fb0f0;      --kind-host-bg: rgba(55, 148, 255, 0.12);
  --kind-docker-fg: #c586c0;    --kind-docker-bg: rgba(197, 134, 192, 0.12);
  --kind-vbox-fg: #46d19e;      --kind-vbox-bg: rgba(70, 209, 158, 0.12);
  --kind-parallels-fg: #e0b341; --kind-parallels-bg: rgba(224, 179, 65, 0.12);
  --code-bg: rgba(255, 255, 255, 0.05);
  /* shape + motion (README radii/shadow) */
  --r-sm: 8px;    /* inputs/buttons 8–9 */
  --r-md: 10px;   /* cards 10–12 */
  --r-lg: 14px;   /* windows/panels 12–16 */
  --shadow: 0 24px 60px rgba(0, 0, 0, 0.5);       /* popover/window elevation */
  --shadow-sm: 0 20px 44px rgba(0, 0, 0, 0.6);    /* menu */
  --font-ui: "Geist", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  /* 8px spacing grid + type scale (README px scale) */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --t-xs: 11px;   /* caption */
  --t-sm: 12px;   /* body-sm / meta */
  --t-base: 14px; /* body (PO 21-07: 13 hơi nhỏ) */
  --t-md: 15px;   /* title 15–17 */
  --t-lg: 20px;   /* screen title */
  --t-label: 10px;/* mono section label */
  --lh-tight: 1.3;
  --lh-base: 1.5;
}

/* Mission Control is a DARK-ONLY design (README: "Final dark theme, exact colors").
   The prior OS-adaptive light palette is intentionally removed — a light-OS machine
   must still render the mission-control dark theme (so QC on a light Mac sees the real
   design). color-scheme:dark on :root tells the UA to render form controls/scrollbars
   dark. An explicit [data-theme="light"] hook is left as a future opt-out but is NOT
   wired to the OS. */

/* Agent-state pulse (live/awaiting dots) + running-command spinner (README §motion). */
@keyframes dmPulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
@keyframes dmSpin { to { transform: rotate(360deg); } }

* { box-sizing: border-box; }
/* #75: view containers set display:grid/flex unconditionally, which out-specifies the
   UA-default `[hidden]{display:none}` → hidden views (login/project/conn-banner) stayed
   laid out, so login never "redirected" to the shell. Restore the hidden contract with a
   reset that wins on specificity (the normalize.css pattern). !important is REQUIRED — a
   plain `[hidden]` loses to a class `display`. Do NOT drop display from the classes; they
   are needed when the view IS shown. */
[hidden] { display: none !important; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--t-base);
  line-height: 1.5;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1, h2 { margin: 0; }
code { font-family: var(--font-mono); }

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.btn {
  font: inherit;
  font-weight: 600;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.06s var(--ease);
}
.btn:active { transform: translateY(0.5px); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-press); }
/* B-V13 (rs-77/78): no rule styled a disabled .btn anywhere, so a disabled 'Next ›' in the New VM wizard
   looked exactly like the enabled one. A disabled button now reads as one, app-wide. */
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn-primary:disabled:hover, .btn-primary[aria-disabled="true"]:hover { background: var(--accent); }
/* UC-007: a submitting button — dimmed, no pointer, with a small spinner before its label. */
.btn.is-busy { opacity: 0.75; cursor: default; }
.btn.is-busy::before {
  content: ''; display: inline-block; width: 12px; height: 12px; margin-right: 7px; vertical-align: -1px;
  border: 2px solid rgba(255, 255, 255, 0.45); border-top-color: #fff; border-radius: 50%;
  animation: cpspin 0.7s linear infinite;
}
.btn-ghost { background: transparent; color: var(--muted); border-color: var(--border); }
.btn-ghost:hover { color: var(--fg-strong); border-color: var(--border-strong); background: var(--panel-2); }
.btn-block { width: 100%; }
.btn-sm { padding: var(--s-1) var(--s-2); font-size: var(--t-sm); font-weight: 600; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── New Project inline form (in the Projects card) ───────────────────────── */
.np-plus { font-weight: 700; font-size: var(--t-base); line-height: 1; }
.new-project-form {
  display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-2) var(--s-3) var(--s-3);
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}
.np-input { padding: var(--s-2) var(--s-2); font-size: var(--t-sm); }
#np-folder { font-family: var(--font-mono); }

/* New Project popup: reuse the settings scrim/mac-window chrome; the form sits in .tm-set-body. */
.np-modal-scrim { z-index: 1130; }
.tm-settings.np-modal { width: 560px; max-width: 92vw; background: #0d0f13; padding: 0; }
.np-modal .new-project-form { border-bottom: none; background: transparent; padding: 18px 20px 20px; max-height: 78vh; overflow-y: auto; }
.np-modal .tm-set-close { margin-left: auto; }
/* Multi-folder list */
.np-folder-list { display: flex; flex-direction: column; gap: 6px; }
.np-folders-empty { margin: 0; font-size: 12px; color: var(--muted); line-height: 1.5; }
.np-folder-row { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel-2); }
.np-folder-row.is-default { border-color: var(--accent); }
.np-folder-badge { flex: none; font-size: 10px; font-family: var(--font-mono); letter-spacing: .04em; color: var(--muted); padding: 2px 6px; border: 1px solid var(--border); border-radius: 5px; }
.np-folder-row.is-default .np-folder-badge { color: var(--accent); border-color: var(--accent); }
.np-folder-path { flex: 1; min-width: 0; font-family: var(--font-mono); font-size: 12px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.np-folder-btn { flex: none; width: 24px; height: 24px; border: 1px solid var(--border); background: transparent; color: var(--muted); border-radius: 6px; cursor: pointer; font-size: 12px; }
.np-folder-btn:hover { color: var(--text); border-color: var(--accent); }
.np-picker-add-cap { font-size: 11px; color: var(--muted); margin-top: 4px; }
.np-actions { display: flex; gap: var(--s-2); }
.new-project-form .error:not([hidden]) { margin-top: var(--s-1); }

/* ─── Daemon multi-environment tier (step 5): env picker + mode + create-team ─── */
.np-env { display: flex; flex-direction: column; gap: var(--s-2); }
.np-field { display: flex; flex-direction: column; gap: var(--s-1); }
.np-field-label { font-size: var(--t-xs); color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.np-select { padding: var(--s-2); font-size: var(--t-sm); }
.np-env-state { font-size: var(--t-sm); color: var(--muted); margin: 0; }
.np-env-single .np-env-name { color: var(--text); font-weight: 600; }
.np-env-empty { padding: var(--s-2); border: 1px dashed var(--border); border-radius: var(--r-sm); background: var(--panel-2); }
.np-env-empty-title { margin: 0 0 var(--s-1); font-weight: 600; color: var(--text); font-size: var(--t-sm); }
.np-env-empty-hint { margin: 0; font-size: var(--t-xs); color: var(--muted); }
.np-installer-link { color: var(--accent); text-decoration: underline; cursor: pointer; }

/* ───────── §3 Install-CTA (state A — no computer connected) ───────── */
.np-install {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--s-2); padding: var(--s-5) var(--s-4);
  border: 1px dashed var(--border-strong); border-radius: var(--r-md); background: var(--panel-2);
}
.np-install-icon { color: var(--muted); display: inline-flex; margin-bottom: var(--s-1); }
.np-install-title { margin: 0; font-size: var(--t-md); font-weight: 600; color: var(--fg); }
.np-install-hint { margin: 0; max-width: 420px; font-size: var(--t-sm); color: var(--muted); line-height: var(--lh-base); }
.np-install-actions { margin-top: var(--s-2); }
.np-install-btn { text-decoration: none; }
.np-os-chips { display: inline-flex; gap: var(--s-2); margin-top: var(--s-1); }
.np-os-chip {
  padding: var(--s-1) var(--s-3); border-radius: 999px;
  background: var(--panel); color: var(--fg-2);
  border: 1px solid var(--border); font-size: var(--t-sm); cursor: pointer;
}
/* The OS badge glyph. It had NO rule at all, so the letter rendered as bare text butted
   against the label and the chips read "MmacOS" / "LLinux" / "WWindows" — visible in every
   screenshot of the Add-daemon dialog. A monospace square, separated from the label. */
.np-os-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-right: var(--s-2);
  border-radius: 5px; background: var(--border); color: var(--fg-2);
  font-family: var(--font-mono, monospace); font-size: 11px; font-weight: 600;
  vertical-align: -3px;
}
.np-os-chip.active .np-os-badge { background: var(--accent-wash); color: var(--accent); }
.np-os-chip:hover { border-color: var(--border-strong); color: var(--fg); }
.np-os-chip.active { border-color: var(--accent); color: var(--accent); background: var(--accent-wash); }
.np-install-note { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--muted); max-width: 420px; }
.np-install-note.error { color: var(--state-awaiting); }
/* Manual VS Code path: "or download VS Code Extension" — a plain hyperlink under the OS chips. */
.np-install-alt { margin: var(--s-2) 0 0; font-size: var(--t-sm); color: var(--muted); }
.np-vscode-ext-link { color: var(--accent); text-decoration: underline; cursor: pointer; }
.np-vscode-ext-link:hover { text-decoration: none; }
/* VM guest SSH-setup reveal (Parallels/VirtualBox VS Code mode). */
.np-vm-ssh-toggle { color: var(--accent); text-decoration: underline; cursor: pointer; }
.np-vm-ssh-toggle:hover { text-decoration: none; }
.np-vm-ssh { margin: var(--s-2) 0 0; padding: var(--s-2); border: 1px solid var(--border, #2a2d34); border-radius: 6px; }
.np-vm-ssh-hint { margin: 0 0 var(--s-1); font-size: var(--t-sm); color: var(--muted); }
.np-vm-ssh-cmd { margin: var(--s-1) 0; padding: var(--s-2); background: var(--panel-2, #16181d); border-radius: 4px; overflow-x: auto; font-size: var(--t-sm); }
.np-vm-ssh-copy { margin-top: var(--s-1); font-size: var(--t-sm); }

/* §3 numbered step label (mono, uppercase). */
.np-step-label {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}
/* §3 step 2 · Platform grid (2-col cards, kind-coloured, selected = accent + ✓). */
.np-platform-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2); }
.np-platform {
  display: flex; align-items: center; gap: var(--s-2); position: relative;
  padding: 9px 11px; border-radius: var(--r-md); /* design L549 padding 9/11 */
  background: var(--panel-2); border: 1px solid var(--border); cursor: pointer; text-align: left;
}
.np-platform:hover { border-color: var(--border-strong); }
/* §F multi-team roster: search + collapsible team sections + aggregated status dot. */
.roster-search { padding: var(--s-2) var(--s-2) var(--s-1); }
.roster-search-input {
  width: 100%; box-sizing: border-box; background: var(--code-bg, #0e1014); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 6px 10px; font: inherit; font-size: var(--t-sm); color: var(--fg-strong);
}
.roster-search-input:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.roster-team { border-top: 1px solid var(--border); }
.roster-team:first-of-type { border-top: none; }
/* PO 21-07: collapsed teams are floating leader-colour pills — margin between them, no
   section separator line (the pill border is the boundary). */
.roster-team.collapsed { border-top: none; margin: 10px 0; }
.roster-team.collapsed + .roster-team { border-top: none; }
.roster-team-head {
  /* PO 21-07: every head row shares the collapsed pill's box (transparent border) so the
     right-cluster (dots · count) and the ⚙ land on the same x across expanded/collapsed rows. */
  display: flex; align-items: center; gap: 6px; padding: 8px 4px 4px; border: 1px solid transparent; border-radius: 9px; cursor: pointer;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .06em; color: #6e7681; user-select: none;
}
/* design v2.2 :303 — the team line is a LABEL ("TEAM · android-app · 6"), not a toolbar: no colour
   dots, and the gear appears when the pointer is on the row (the same treatment the artboard gives
   the Discussion gear via style-hover). PO 21-07's always-on dot cluster is superseded by the v2.2
   artboard the PO approved on 2026-09-18. */
.roster-team-head .roster-team-dots { display: none; }
.roster-team-head .roster-team-gear { opacity: 0; transition: opacity .12s; }
.roster-team-head:hover .roster-team-gear, .roster-team-head:focus-within .roster-team-gear, .roster-team-gear:focus-visible { opacity: 1; }
.roster-team-head .roster-team-gear { margin-left: auto; }
.roster-team-head:hover { color: var(--fg); }
.roster-team-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.roster-team-caret { width: 12px; flex: none; color: var(--muted); }
/* DESIGN dc:108: a CLUSTER of per-member status dots (one per member, 6px, coloured by state) instead
   of one aggregated dot — awaiting #ff6b6b · running #46d19e · thinking #e0b341 · idle grey. */
/* PO 21-07: the dot cluster + count form ONE right-aligned group (dots take the margin-auto
   slot; badge/count hug them), so member info lines up down the roster regardless of name width. */
.roster-team-dots { display: inline-flex; align-items: center; gap: 3px; flex: none; margin-left: auto; }
/* PO 20-07: each cluster dot = that member's OWN colour (inline from nav.js, matches its card bar/dot);
   busy-ish → .pulse; offline dims but keeps the colour. */
.roster-team-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.roster-team-dot.pulse { animation: dmPulse 1.4s ease-in-out infinite; }
.roster-team-dot.offline { opacity: 0.4; }
/* PO 18-09 (ảnh chụp kèm): tên team là dòng TIÊU ĐỀ của nhóm — CHỮ HOA, TRẮNG, ĐẬM — nên mắt tách
   được tên team khỏi tên employee bên dưới nó. Trước đó tên team xám #8b909a cùng tông với mọi thứ
   khác trong cột, nên nó đọc như một nhãn phụ chứ không như đầu nhóm. Chữ "TEAM" đứng trước bị bỏ
   (.roster-team-kw): dòng nào cũng là team, in lại từ đó chỉ chiếm chỗ và đẩy tên ra xa lề. */
.roster-team-name {
  color: #e6e8ec; font-weight: 700; font-size: 12px; letter-spacing: .08em;
  text-transform: uppercase; font-family: var(--font-mono);
}
/* count sits at the far right (design "… ●●● 3") — margin-auto pushes it past the name/dots. */
/* số thành viên là một CHIP nhỏ cạnh tên (PO 18-09 ảnh), không phải con số trôi ở mép phải:
   nó thuộc về cái tên nó đếm. */
.roster-team-count {
  margin-left: 8px; min-width: 16px; padding: 1px 6px; border-radius: 5px; text-align: center;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 600;
  background: rgba(255, 255, 255, .07); color: #8b909a;
}
/* no dots (edge: empty team) — count still right-aligns */
.roster-team-count:first-child { margin-left: auto; }
/* DESIGN v3 H2.3 "▸ TEAM api-hardening [1 ⚠] 2": a COLLAPSED team with awaiting member(s) gets a
   red-tinted header + a "N ⚠" badge so the wait is visible without expanding. Literals copied from the
   design render (tpl 548/554): header border rgba(255,107,107,.25) bg .05 radius 9px; badge 8px mono 700
   rgb(255,155,155) on rgba(255,107,107,.15) radius 4px padding 2px 5px. Badge takes the margin-auto slot;
   the count then hugs it (design shows "1 ⚠  2" as one right cluster). */
.roster-team-head.has-awaiting { border: 1px solid rgba(255, 107, 107, 0.25); border-radius: 9px; background: rgba(255, 107, 107, 0.05); padding: 9px 10px; }
/* design tpl 180: the card ⚙ is a bare 13px grey glyph (no button chrome) inside the head row. */
.roster-card .cp-gear { background: none; border: none; padding: 0 2px; color: #6e7681; font-size: 12px; line-height: 1; cursor: pointer; flex: none; }
.roster-card .cp-gear:hover { color: var(--fg, #e6e8ec); }
.roster-team-awaitbadge { flex: none; font-family: var(--font-mono); font-size: 8px; font-weight: 700; color: rgb(255, 155, 155); background: rgba(255, 107, 107, 0.15); border-radius: 4px; padding: 2px 5px; margin-left: 6px; }
.roster-team-awaitbadge + .roster-team-count { margin-left: 6px; }
.roster-team-gear { margin-left: 6px; width: 20px; flex: none; text-align: center; background: none; border: none; color: var(--muted); cursor: pointer; padding: 2px 0; border-radius: var(--r-sm); font-size: 12px; }
.roster-team-gear:hover { background: var(--code-bg); color: var(--fg); }
.roster-team.collapsed .roster-list { display: none; }
/* H2b roster header (design dc:105 "TEAM · 6 agents"): mono label + a pill "+ Add ▾" button. Padding
   12px 12px 8px matches the design exactly (was 12px 8px 6px — the s-2 shorthand drifted off spec). */
.roster-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 12px 8px;
}
.roster-header-label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; color: #6e7681;
}
/* "+ Add ▾" — design dc:105: a small accent PILL (was an unstyled text button). rgba(.12) fill,
   rgba(.28) border, radius 7px, 11px/600 #3794ff, the leading "+" a touch larger (13px). */
.roster-add-menu {
  /* design v2.2 :300 — "+ Add ▾" is a 10px mono accent TEXT beside the TEAMS label, not a filled
     pill competing with the cards below it. */
  display: inline-flex; align-items: center; gap: 4px; padding: 0;
  font-family: var(--font-mono); font-size: 10px; font-weight: 500; color: #3794ff;
  background: none; border: none; border-radius: 4px;
  cursor: pointer;
}
.roster-add-menu:hover { text-decoration: underline; }
.roster-add-menu:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.roster-add-plus { font-size: 11px; line-height: 1; }
.roster-add-caret { font-size: 9px; color: #6e7681; }
/* JCK-1 §3: the roster's fourth state. Deliberately NOT styled like the empty state — an empty
   roster is a normal project waiting for its first employee, this is the screen admitting it does
   not know. The detail line carries the next move, and Retry appears only where retrying can help. */
.roster-unavailable { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.roster-unavailable-msg { color: var(--text); font-weight: 600; }
.roster-unavailable-detail { color: var(--faint); font-size: var(--t-xs); line-height: 1.5; }
/* JCK-1(e): the look comes from .btn.btn-sm.err-retry — the SAME classes the thread's error state
   uses — so one action does not wear two costumes on one screen. Only spacing is set here. */
.roster-retry { margin-top: 4px; align-self: flex-start; }
/* JE-13: a team the control plane does not know cannot take a new employee, so instead of the
   "+ Add to this team" button it wears a muted state chip and the action that fixes it. The chip is
   deliberately NOT accent-coloured — it is a condition, not an invitation; the Adopt button beside
   it carries the accent because that is the thing to press. */
.roster-team-unreg {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em;
  color: var(--faint); border: 1px solid rgba(139, 144, 154, .35);
  border-radius: 3px; padding: 0 4px; margin-right: 6px; white-space: nowrap;
}
.roster-team-adopt {
  background: none; border: none; cursor: pointer; padding: 0;
  font-family: var(--font-mono); font-size: 10px; color: var(--accent);
}
.roster-team-adopt:hover { text-decoration: underline; }
.roster-team-adopt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.roster-team-adopt[disabled] { color: var(--faint); cursor: default; text-decoration: none; }
.roster-add-team, .roster-team-spawn {
  background: none; border: none; cursor: pointer; padding: 0;
  font-family: var(--font-mono); color: var(--accent);
}
.roster-add-team { font-size: 10px; }
.roster-add-team:hover, .roster-team-spawn:hover { text-decoration: underline; }
.roster-add-team:focus-visible, .roster-team-spawn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
/* per-team "+ spawn vào team này" — design dc:1595: indented to align with member cards, blue 10px. */
.roster-team-spawn { display: block; margin: 2px 0 4px; padding: 6px 11px; font-size: 10px; text-align: left; }

.np-platform.selected { border-color: var(--accent); background: var(--accent-wash); }
/* an UNAVAILABLE platform on the selected machine — dimmed, not interactive (design L552 opacity .45). */
.np-platform-off { opacity: 0.45; cursor: default; border-color: var(--border); }
.np-platform-off:hover { border-color: var(--border); }
.np-platform-off .np-platform-name { color: var(--muted); }
.np-platform-none { margin-left: auto; font-family: var(--font-mono); font-size: 9px; color: var(--muted); }
.np-platform-name { color: var(--fg); font-size: var(--t-sm); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.np-platform-check { margin-left: auto; color: var(--accent); font-weight: 700; }
.np-hidden-select { display: none; }
/* platform-kind badge (host/docker/virtualbox/parallels) — coloured per README. */
.np-kind-badge {
  display: inline-block; padding: 1px 6px; border-radius: 999px;
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .04em;
  color: var(--kind-host-fg); background: var(--kind-host-bg);
}
.np-kind-badge.kind-docker { color: var(--kind-docker-fg); background: var(--kind-docker-bg); }
.np-kind-badge.kind-virtualbox { color: var(--kind-vbox-fg); background: var(--kind-vbox-bg); }
.np-kind-badge.kind-parallels { color: var(--kind-parallels-fg); background: var(--kind-parallels-bg); }

/* mode segmented control (Terminal | VS Code) */
.np-mode-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; align-self: flex-start; }
.np-mode-btn { padding: var(--s-1) var(--s-3); font-size: var(--t-sm); background: var(--panel); color: var(--muted); border: none; cursor: pointer; }
.np-mode-btn + .np-mode-btn { border-left: 1px solid var(--border); }
.np-mode-btn.active { background: var(--accent); color: #04101f; }
/* create-team fieldset */
.np-team { display: flex; flex-direction: column; gap: var(--s-2); border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--s-2) var(--s-3) var(--s-3); margin: 0; }
.np-team-legend { padding: 0 var(--s-1); font-size: var(--t-xs); color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.np-team-desc { resize: vertical; min-height: 2.4em; font-family: inherit; }
.np-team-error:not([hidden]) { margin-top: var(--s-1); }
/* submit loading state */
#np-submit.is-loading { opacity: .7; cursor: progress; }
#np-submit:disabled { opacity: .5; cursor: not-allowed; }

/* ════════════════════════ LOGIN VIEW ════════════════════════ */
.login-view {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--s-5);
  background:
    radial-gradient(900px 500px at 50% -10%, rgba(55, 148, 255, 0.10), transparent 60%),
    var(--bg);
}
.login-card {
  width: 100%;
  max-width: 360px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: var(--s-5) var(--s-5) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.brand { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-1); }
.brand-mark {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  color: var(--accent);
  background: linear-gradient(160deg, rgba(55, 148, 255, 0.18), rgba(55, 148, 255, 0.06));
  border: 1px solid var(--border);
  border-radius: 11px;
}
.brand-text h1 { font-size: var(--t-lg); font-weight: 700; color: var(--fg-strong); letter-spacing: -0.01em; }
.brand-sub { margin: 1px 0 0; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
.field { display: flex; flex-direction: column; gap: var(--s-1); }
.field-label { font-size: var(--t-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
input {
  font: inherit;
  width: 100%;
  padding: var(--s-2) var(--s-3);
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--fg);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
input::placeholder { color: var(--faint); }
input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(55, 148, 255, 0.18); }
.login-card .btn-primary { margin-top: var(--s-1); padding: var(--s-2); }
.login-hint { margin: var(--s-1) 0 0; font-size: var(--t-xs); color: var(--faint); line-height: 1.45; text-align: center; }
.login-hint code { background: var(--code-bg); padding: 1px var(--s-1); border-radius: 4px; font-size: var(--t-xs); color: var(--muted); }
.error { color: var(--red); font-size: var(--t-sm); margin: 0; }
.login-card .error:not([hidden]) {
  background: rgba(241, 76, 76, 0.1);
  border: 1px solid rgba(241, 76, 76, 0.3);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-2);
}

/* ── Chat-parity login additions (PO 27-08): wordmark above the card, welcome head,
      Google button + divider, icons inside the fields, eye toggle, danger-soft error. ── */
.login-wrap { width: 100%; max-width: 400px; display: flex; flex-direction: column; }
.login-brand {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  margin-bottom: var(--s-4);
}
.login-brand img { width: 32px; height: 32px; border-radius: 8px; }
.login-brand span { font-size: var(--t-lg); font-weight: 700; letter-spacing: -0.01em; color: var(--fg-strong); }
.login-wrap .login-card { max-width: none; }
.login-head { text-align: center; margin-bottom: var(--s-1); }
.login-head h3 { font-size: var(--t-lg); font-weight: 600; color: var(--fg-strong); }
.login-sub { margin: 4px 0 0; font-size: var(--t-sm); color: var(--muted); }
.login-error { color: var(--red); font-size: var(--t-sm); margin: 0; }
.login-error-code { display: block; margin-top: 4px; font-size: var(--t-xs, 11px); color: var(--muted); font-family: var(--font-mono, monospace); word-break: break-all; }
.login-error:not([hidden]) {
  background: rgba(241, 76, 76, 0.1);
  border: 1px solid rgba(241, 76, 76, 0.3);
  border-radius: 10px;
  padding: var(--s-2) var(--s-3);
}
.btn-google {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; height: 44px;
  font: inherit; font-size: var(--t-sm); font-weight: 500;
  color: var(--fg-strong);
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.btn-google:hover { background: var(--panel); border-color: var(--accent); }
.btn-google img { width: 20px; height: 20px; }
.login-divider {
  display: flex; align-items: center; gap: var(--s-3);
  margin: var(--s-1) 0;
}
.login-divider::before, .login-divider::after { content: ''; height: 1px; flex: 1; background: var(--border); }
.login-divider span { font-size: var(--t-xs); color: var(--faint); }
.input-wrap { position: relative; display: block; }
.input-wrap .input-icon {
  position: absolute; left: var(--s-3); top: 50%; transform: translateY(-50%);
  color: var(--muted); pointer-events: none;
}
.input-wrap input { padding-left: calc(var(--s-3) + 24px); }
.input-wrap input#password { padding-right: 38px; }
.pw-toggle {
  position: absolute; right: var(--s-2); top: 50%; transform: translateY(-50%);
  display: grid; place-items: center;
  width: 28px; height: 28px;
  background: none; border: 0; border-radius: 6px;
  color: var(--muted); cursor: pointer;
  transition: color 0.15s var(--ease);
}
.pw-toggle:hover { color: var(--fg-strong); }

/* ── Login touch targets (WCAG 2.5.5 / 2.5.8) ─────────────────────────────────
   Measured 2026-09-21 on THIS form at 640x480 (dpr=2): pw-toggle 28x28, and the
   email/password/submit controls 350x39 — all four under the 44x44 CSS px
   guideline, the toggle worst. No horizontal overflow and the viewport meta was
   already correct, so the layout was never the problem; only the hit areas were.

   The toggle keeps its 28px LOOK and grows only its HIT AREA, via a pseudo-element
   centred on it. Growing the box itself would push the password field's padding and
   move the icon off the text baseline — a visual regression to fix an ergonomic bug.

   Scoped to .login-card on purpose: `.btn` and `.field input` are shared by every
   screen in the product, and raising their height globally would reflow dialogs,
   toolbars and the composer. This ticket is about the login screen. */
.login-card .pw-toggle::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 44px; height: 44px;
  /* Hit area only — nothing is drawn, and it must not eat clicks meant for the
     input underneath, so it is confined to the toggle's own stacking position. */
}
.login-card .field input { min-height: 44px; }
.login-card .btn { min-height: 44px; }

/* ════════════════════════ SHELL VIEW ════════════════════════ */
/* Shell = rail (60px) + main column. The rail is persistent on every screen (§2);
   .shell-main holds the appbar + the view content, stacked as before. */
.shell-view { display: flex; flex-direction: row; height: 100dvh; }
.shell-main { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; height: 100dvh; }

/* ───────── Rail (§2 — 60px shared nav) ───────── */
/* LOW-6: phone-only menu button (shown by the @max-width:719px block). */
.mob-nav-toggle { display: none; }
.mob-nav-scrim { display: none; }
.nav-rail {
  /* GAP-15: rail gap 8 (design, not the 4px --s-1 drift) + padding 14 (design, not 12). */
  flex: none; width: 60px; height: 100dvh;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s-2); padding: 14px 0;
  background: var(--bg-elev);
  border-right: 1px solid var(--border);
}
.rail-brand {
  color: var(--accent); display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: var(--s-2);
}
.rail-item {
  position: relative;
  width: 40px; height: 40px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 10px; background: transparent; cursor: pointer;
  color: var(--faint);
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.rail-item:hover { color: var(--fg-2); background: rgba(255, 255, 255, 0.04); }
.rail-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* active VIEW icon — accent fill at .14 alpha (design_handoff dc:615, exact — not the .12 --accent-wash). */
.rail-item.active { color: var(--accent); background: rgba(55, 148, 255, 0.14); }
/* GAP-DIFF-1 (design_handoff dc:616): the active VIEW icon gets an accent-bar on the rail's LEFT edge
   (3px × 24px, #3794ff, at left:-14px per the design) — the VS Code Activity-Bar "selected" indicator. */
.rail-item.active::before {
  content: ''; position: absolute; left: -14px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 24px; border-radius: 2px; background: var(--accent);
}
/* GAP-DIFF-1 BOTTOM group: the panel-toggle set (per view) + its divider. */
.rail-panels { display: flex; flex-direction: column; align-items: center; gap: var(--s-1); }
.rail-panels[hidden] { display: none; }
.rail-divider { width: 26px; height: 1px; background: rgba(255, 255, 255, 0.1); margin: 6px 0; }
.rail-panel {
  position: relative; width: 34px; height: 34px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 9px; background: transparent; cursor: pointer;
  color: var(--faint); transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.rail-panel:hover { color: var(--fg-2); background: rgba(255, 255, 255, 0.04); }
.rail-panel:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* a SHOWN panel = filled bg + bright icon (dc:620 "đang hiện"); hidden = dim, transparent. */
.rail-panel.shown { color: var(--fg-strong); background: rgba(255, 255, 255, 0.06); }
.rail-panel-icon { display: inline-flex; }
.rail-panel-icon svg { width: 16px; height: 16px; }
.rail-panel-badge { top: -2px; right: -2px; }
.rail-spacer { flex: 1 1 auto; }
.rail-account { margin-bottom: var(--s-1); }
.rail-avatar {
  width: 26px; height: 26px; border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), #6aa9ff);
  display: inline-flex; align-items: center; justify-content: center;
  color: #04101f; font-size: 12px; font-weight: 700;
}
/* Build version strip — bottom of the rail, under the account avatar (PO 15-09: "show cái version
   đó vào góc dưới bên trái để biết version lên rồi ... BE version và FE version").
   Deliberately the quietest thing on screen: it must be READABLE WHEN LOOKED FOR and invisible the
   rest of the time, because it is consulted after a deploy and never during work. Monospace so two
   commit hashes line up and a changed character is obvious at a glance — the whole point is spotting
   that the value MOVED, which proportional digits make harder. */
.rail-build {
  /* Sits ABOVE the account avatar (PO: "display phải display ở trên profile icon chứ"). The first
     placement was below it, against the viewport edge, where it clipped. */
  display: flex; flex-direction: column; gap: 1px;
  padding: 4px 0; margin-bottom: 6px;
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 9px; line-height: 1.25; letter-spacing: .02em;
  color: var(--muted); opacity: .55; cursor: default; user-select: text;
}
.rail-build:hover { opacity: 1; }
.rail-build-row { display: flex; align-items: center; gap: 3px; justify-content: center; }
/* The BE/FE tag is the half a reader scans for; the hash is the half that changes. Dimming the tag
   keeps the changing part the brightest thing in the strip. */
.rail-build-tag { opacity: .6; font-size: 8px; }
/* An unstamped build is marked, never rendered as if "unknown" were a version — a build that lies
   about its identity is worse than one that admits it has none. */
.rail-build[data-stamped="false"] { color: var(--warn, #e0b341); opacity: .8; }
/* B-UP1: "New version available — reload" — the tab is running an older FE/BE than the server serves. */
.build-update { position: fixed; left: 76px; bottom: 16px; z-index: 9000; display: flex; align-items: center; gap: 6px;
  background: #1c2230; color: #e6e8ec; border: 1px solid #3b82f6; border-radius: 8px; padding: 8px 12px;
  font-size: 13px; box-shadow: 0 6px 20px rgba(0,0,0,.35); }
.build-update[hidden] { display: none; }
.build-update-reload { background: none; border: 0; padding: 0; color: #6ea8ff; font: inherit; font-weight: 600;
  text-decoration: underline; cursor: pointer; }

/* Rail popover (§2 Projects switcher / Account menu) — anchored right of the rail icon. */
.rail-popover {
  position: fixed; z-index: 70; min-width: 240px; max-width: 320px; max-height: 70vh; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: var(--r-md);
  box-shadow: var(--shadow); padding: var(--s-1);
}
.rail-popover[hidden] { display: none; }
.rp-head { font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: .12em; text-transform: uppercase; color: var(--muted); padding: var(--s-2) var(--s-2) var(--s-1); }
.rp-item {
  display: flex; align-items: center; gap: var(--s-2); width: 100%; text-align: left;
  padding: var(--s-2); border: 0; background: transparent; color: var(--fg); border-radius: var(--r-sm); cursor: pointer; min-width: 0;
}
.rp-item:hover, .rp-item:focus-visible { background: var(--accent-wash); outline: none; }
/* #255: a picker row for a CLI that cannot switch model at runtime is DISABLED and says why (the
   .rp-note above it) — visibly inert instead of a live-looking button that fires a rejected command. */
.rp-item[disabled] { opacity: .45; cursor: not-allowed; }
.rp-item[disabled]:hover, .rp-item[disabled]:focus-visible { background: transparent; }
.rp-note {
  padding: var(--s-2); font-size: var(--t-xs); line-height: 1.45; color: var(--muted);
  white-space: normal; max-width: 280px;
}
.rp-main { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rp-name { font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-sub { font-size: var(--t-xs); color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.rp-dot.online { background: var(--state-running); }
.rp-dot.offline { background: var(--faint); }
.rp-new { color: var(--accent); font-weight: 600; border-top: 1px solid var(--border); margin-top: var(--s-1); }
.rp-empty { color: var(--muted); padding: var(--s-2); font-size: var(--t-sm); }
.rp-account { padding: var(--s-1) var(--s-2) var(--s-2); display: flex; flex-direction: column; gap: 1px; border-bottom: 1px solid var(--border); margin-bottom: var(--s-1); }
.rp-account-name { font-weight: 600; color: var(--fg); }

/* Explorer unsaved-count badge (hidden when 0). */
.rail-badge {
  position: absolute; top: 3px; right: 3px; min-width: 15px; height: 15px;
  padding: 0 3px; border-radius: 999px;
  background: var(--accent); color: #04101f;
  font-family: var(--font-mono); font-size: 9px; font-weight: 600; line-height: 15px;
  text-align: center;
}
/* Mobile: the rail becomes a slim bottom bar handled in the responsive block; hidden
   here means the base desktop rail. (Refined in C10 mobile pass.) */

.appbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--s-4); height: 48px; flex: none;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
}
.appbar-brand { display: flex; align-items: center; gap: var(--s-2); }
.appbar-mark { color: var(--accent); display: inline-flex; }
.appbar-title { font-size: var(--t-md); font-weight: 700; color: var(--fg-strong); letter-spacing: -0.01em; }
.userbar { display: flex; align-items: center; gap: var(--s-2); }
.user-chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); font-weight: 600; color: var(--fg);
  background: var(--panel-2); border: 1px solid var(--border);
  padding: var(--s-1) var(--s-2) var(--s-1) var(--s-1); border-radius: 999px;
}
.user-avatar {
  width: 20px; height: 20px; border-radius: 50%; flex: none;
  background: var(--accent);
}
.btn-ghost { padding: var(--s-1) var(--s-3); font-size: var(--t-sm); }

.content { flex: 1; min-height: 0; }

/* ════════════════════════ SCREEN A: Projects list ════════════════════════ */
/* GAP-9/15/16 (COPY design HTML L378-392, anh Luat: copy inline-CSS đừng tự chế): the Home
   content is FULL-WIDTH, everything aligned to a 24px left/right gutter — NO max-width:1000 +
   margin:0 auto (that centered the content in a wide viewport → the 174px left-gap + narrow
   "lép" cards). Design: appbar padding:0 24px, stat-strip padding:18px 24px, cards padding:
   12px 24px — all share the same 24px gutter, full-frame. */
.projects-view {
  overflow-y: auto;
  padding: 0; /* the inner rows carry their own 24px gutter (design), not a view-wide pad */
}
.projects-head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 18px 24px 0; /* design appbar 0 24px + top space; NO max-width/auto-margin */
}
.screen-title { font-size: 17px; font-weight: 700; color: var(--fg-strong); letter-spacing: -0.01em; flex: none; } /* design L380 = 17px (not 20) */
/* #S7 (dc:381): Home header ⚙ Global-settings gear — literal from design (36×36, #16181d, border .12, radius9). */
.home-gear { flex: none; width: 36px; height: 36px; border: 1px solid rgba(255,255,255,.12); background: #16181d; color: #c4c8ce; border-radius: 9px; display: grid; place-items: center; cursor: pointer; }
.home-gear:hover { border-color: var(--accent); color: var(--fg-strong); }

/* ── #T07 · Company / Workspace switcher (dc:380-478 H1, dc:638-758 H1d) ──────────
   Literals copied straight from the design (the switcher = a contract). The closed
   button replaces the "Home" title; the popover lists companies → workspaces. */
.ws-switcher { flex: none; display: flex; align-items: center; }
/* closed button (dc:380): #16181d, border rgba .12, radius 10, pad 6 10 6 7, gap 9 */
.ws-switcher-btn {
  display: flex; align-items: center; gap: 9px;
  background: #16181d; border: 1px solid rgba(255,255,255,.12); border-radius: 10px;
  padding: 6px 10px 6px 7px; cursor: pointer; font-family: inherit;
}
.ws-switcher-btn.is-open { border-color: rgba(55,148,255,.4); } /* dc:467 open border */
.ws-switcher-btn:hover { border-color: rgba(55,148,255,.4); }
.ws-switcher-avatar {
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  background: linear-gradient(160deg,#3794ff,#1f6fd6); color: #fff;
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
}
.ws-switcher-labels { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.2; }
.ws-switcher-company { font-size: 13px; font-weight: 600; color: #e6e8ec; }
.ws-switcher-sub { font-size: 10px; font-family: var(--font-mono); color: #6e7681; }
.ws-switcher-chev { flex: none; }
.ws-switcher-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── CURRENT PROJECT SWITCHER (design "Current Project Switcher.dc.html") ──────────────────────
   The shared header row at the top of the left column in Monitor + Explorer: a project-picker
   button + a MONITOR/EXPLORER view tag. Matches the COMPONENT SPEC (52px row, #16181d button,
   28px gradient tile with an online dot, 2-line label, trailing chevron). */
.cp-switcher {
  flex: none; height: 52px; display: flex; align-items: center; gap: 8px;
  padding: 0 10px; border-bottom: 1px solid rgba(255,255,255,.06);
}
.cp-switcher:empty { display: none; } /* no project open → collapse the row entirely */
.cp-sw-btn {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
  background: #16181d; border: 1px solid rgba(255,255,255,.1); border-radius: 10px;
  padding: 6px 8px 6px 7px; cursor: pointer; font-family: inherit;
}
.cp-sw-btn:hover, .cp-sw-btn.is-open { border-color: rgba(55,148,255,.4); }
.cp-sw-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cp-sw-tile-wrap { position: relative; flex: none; }
.cp-sw-tile {
  width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center;
  background: linear-gradient(160deg,#3794ff,#1f6fd6); color: #fff;
  font-size: 11px; font-weight: 700; font-family: var(--font-mono);
}
.cp-sw-dot {
  position: absolute; right: -2px; bottom: -2px; width: 9px; height: 9px; border-radius: 50%;
  background: #46d19e; border: 2px solid #16181d;
}
.cp-sw-dot-off { background: #5a6069; }
/* design H2 (PO 21-07): the switcher label is ONE line — "Syrix Project" + the tenancy
   sub inline after it, both truncating; never a stacked 2-line pill. */
.cp-sw-labels { display: flex; flex-direction: row; align-items: baseline; gap: 8px; line-height: 1.25; min-width: 0; flex: 1; }
/* the project NAME never truncates — only the tenancy sub-data ellipsizes (PO 21-07). */
.cp-sw-name { flex: none; font-size: 13px; font-weight: 600; color: #e6e8ec; white-space: nowrap; }
.cp-sw-sub { flex: 1; min-width: 0; font-size: 10px; font-family: var(--font-mono); color: #6e7681; letter-spacing: .03em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-sw-chev { flex: none; }
/* DESIGN v3 H2/H4 (PO header side-by-side 20-07): the design draws NO "MONITOR/EXPLORER" pill next
   to the switcher — the active RAIL icon is the mode indicator. Hidden (kept in markup for a11y/tests
   that may reference it; display:none = not rendered). */
.cp-sw-viewtag { display: none; }

/* open popover — Company header → Workspace row → projects grouped by daemon → New project + gear */
.cp-pop-menu {
  position: fixed; z-index: 60; width: 296px;
  background: #0e1014; border: 1px solid rgba(255,255,255,.14); border-radius: 12px;
  box-shadow: 0 20px 44px rgba(0,0,0,.6); overflow: hidden;
}
.cp-pop { display: flex; flex-direction: column; }
.cp-pop-empty { margin: 0; padding: 12px; font-size: 12px; color: var(--muted); }
.cp-pop-company {
  padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,.06);
  display: flex; align-items: center; gap: 9px;
}
.cp-pop-company-avatar {
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  background: linear-gradient(160deg,#3794ff,#1f6fd6); color: #fff;
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
}
.cp-pop-company-name { font-size: 13px; font-weight: 600; color: #e6e8ec; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-pop-company-tag { font-size: 10px; font-family: var(--font-mono); color: #6e7681; flex: none; }
.cp-pop-ws-wrap { padding: 6px; }
.cp-pop-ws {
  width: 100%; display: flex; align-items: center; gap: 8px; padding: 8px 9px;
  border-radius: 8px; background: #16181d; border: 1px solid rgba(255,255,255,.08);
  cursor: pointer; font-family: inherit;
}
.cp-pop-ws:hover { border-color: rgba(55,148,255,.35); }
.cp-pop-ws-name { font-size: 12px; color: #c4c8ce; flex: 1; min-width: 0; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-pop-ws-tag { font-size: 9px; font-family: var(--font-mono); color: #6e7681; flex: none; }
.cp-pop-projects { padding: 0 6px 6px; display: flex; flex-direction: column; gap: 1px; max-height: 270px; overflow-y: auto; }
.cp-pop-daemon-head {
  padding: 8px 9px 4px; font-size: 9px; font-family: var(--font-mono); letter-spacing: .1em;
  color: #6e7681; display: flex; align-items: center; gap: 6px;
}
.cp-pop-proj {
  display: flex; align-items: center; gap: 9px; padding: 8px 9px; border-radius: 8px;
  background: transparent; border: none; cursor: pointer; font-family: inherit; width: 100%; text-align: left;
}
.cp-pop-proj:hover { background: rgba(255,255,255,.04); }
.cp-pop-proj.is-active { background: rgba(55,148,255,.12); }
.cp-pop-proj-tile {
  width: 22px; height: 22px; border-radius: 6px; flex: none; display: grid; place-items: center;
  background: #232733; color: #c4c8ce; font-size: 9px; font-weight: 700; font-family: var(--font-mono);
}
.cp-pop-proj-tile.is-active { background: linear-gradient(160deg,#3794ff,#1f6fd6); color: #fff; }
.cp-pop-proj-name { font-size: 13px; color: #c4c8ce; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-pop-proj.is-active .cp-pop-proj-name { color: #e6e8ec; }
.cp-pop-count { font-family: var(--font-mono); font-size: 9px; color: #46d19e; display: inline-flex; align-items: center; gap: 3px; flex: none; }
.cp-pop-count-dot { width: 5px; height: 5px; border-radius: 50%; background: #46d19e; }
.cp-pop-idle { font-family: var(--font-mono); font-size: 9px; color: #5a6069; flex: none; }
.cp-pop-check { flex: none; }
.cp-pop-divider { height: 1px; background: rgba(255,255,255,.06); margin: 0 6px; }
.cp-pop-actions { padding: 6px; display: flex; gap: 6px; }
.cp-pop-new {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px;
  border-radius: 8px; background: rgba(55,148,255,.12); border: none; color: #3794ff;
  font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.cp-pop-new:hover { background: rgba(55,148,255,.2); }
.cp-pop-plus { font-size: 15px; line-height: 1; }
.cp-pop-manage {
  flex: none; display: grid; place-items: center; width: 36px; border-radius: 8px;
  background: #16181d; border: 1px solid rgba(255,255,255,.08); color: #8b909a; cursor: pointer;
}
.cp-pop-manage:hover { border-color: rgba(55,148,255,.35); color: #c4c8ce; }

/* open popover (dc:469): 280 wide, #0e1014, border rgba .14, radius 12, shadow */
.ws-pop-menu {
  position: fixed; z-index: 60; width: 280px;
  background: #0e1014; border: 1px solid rgba(255,255,255,.14); border-radius: 12px;
  box-shadow: 0 20px 44px rgba(0,0,0,.65); overflow: hidden;
}
.ws-pop { display: flex; flex-direction: column; }
.ws-pop-empty { margin: 0; padding: 14px 12px; font-size: 12px; color: var(--muted); }
/* company header (dc:470): pad 10 12, bottom border, avatar + name + meta */
.ws-pop-company { display: flex; flex-direction: column; }
.ws-pop-company-head {
  padding: 10px 12px; border-bottom: 1px solid rgba(255,255,255,.06);
  display: flex; align-items: center; gap: 9px;
}
.ws-pop-company-avatar {
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  background: linear-gradient(160deg,#3794ff,#1f6fd6); color: #fff;
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
}
.ws-pop-company-name { font-size: 13px; font-weight: 600; color: #e6e8ec; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-pop-company-meta { font-size: 10px; font-family: var(--font-mono); color: #6e7681; flex: none; }
/* WORKSPACES section label (dc:472) */
.ws-pop-section-label {
  padding: 7px 9px 4px; margin-top: 6px;
  font-size: 9px; font-family: var(--font-mono);
  letter-spacing: .12em; color: #6e7681;
}
/* workspace row (dc:473-474): gap 8, pad 8 9, radius 8; active = accent-wash + check */
.ws-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 9px; margin: 0 6px; border-radius: 8px;
  background: transparent; border: none; cursor: pointer; width: calc(100% - 12px);
  text-align: left; font-family: inherit;
}
.ws-row:hover { background: rgba(255,255,255,.04); }
.ws-row.is-active { background: rgba(55,148,255,.12); }
.ws-row-name { font-size: 13px; color: #c4c8ce; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-row.is-active .ws-row-name { color: #e6e8ec; }
.ws-row-role { font-size: 9px; font-family: var(--font-mono); color: #6e7681; flex: none; }
.ws-row-check { flex: none; }
.ws-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* "+ Create workspace" row (dc:648): accent text */
.ws-pop-create, .ws-pop-newco {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 9px; margin: 1px 6px 6px; border-radius: 8px;
  background: transparent; border: none; cursor: pointer; width: calc(100% - 12px);
  text-align: left; font-family: inherit; font-size: 12px; color: #3794ff;
}
.ws-pop-create:hover, .ws-pop-newco:hover { background: rgba(55,148,255,.08); }
.ws-pop-plus { width: 14px; text-align: center; flex: none; }
.ws-pop-newco { margin: 6px 6px 4px; }
/* divider between company blocks / before Manage (dc:475) */
.ws-pop-divider { height: 1px; background: rgba(255,255,255,.06); margin: 4px 6px; }
/* "Manage company & workspace" row (dc:476) */
.ws-pop-manage {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 9px; margin: 0 6px 6px; border-radius: 8px;
  background: transparent; border: none; cursor: pointer; width: calc(100% - 12px);
  text-align: left; font-family: inherit; font-size: 12px; color: #8b909a;
}
.ws-pop-manage:hover { background: rgba(255,255,255,.04); color: #c4c8ce; }
.ws-pop-manage svg { flex: none; }
.ws-pop-create:focus-visible, .ws-pop-newco:focus-visible, .ws-pop-manage:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* invite-by-name dialog (dc:715-741 "Share workspace") — a themed modal, not alert() */
.ws-invite-scrim {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(0,0,0,.55); display: grid; place-items: center; padding: 20px;
}
.ws-invite {
  width: 340px; max-width: 100%;
  background: #0e1014; border: 1px solid rgba(255,255,255,.12); border-radius: 14px;
  box-shadow: 0 20px 44px rgba(0,0,0,.6); overflow: hidden;
}
.ws-invite-head { padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,.06); }
.ws-invite-title { font-size: 14px; font-weight: 600; color: #e6e8ec; }
.ws-invite-desc { margin: 4px 0 0; font-size: 11px; color: #6e7681; line-height: 1.5; }
.ws-invite-body { padding: 16px; display: flex; flex-direction: column; gap: 13px; }
.ws-invite-field { display: flex; flex-direction: column; gap: 5px; }
.ws-invite-label { font-size: 10px; font-family: var(--font-mono); color: #6e7681; letter-spacing: .04em; }
.ws-invite-input {
  background: #16181d; border: 1px solid rgba(255,255,255,.12); border-radius: 9px;
  padding: 9px 12px; font-size: 13px; color: #e6e8ec; font-family: inherit;
}
.ws-invite-input:focus { outline: none; border-color: var(--accent); }
.ws-invite-error { margin: 0; font-size: 11px; color: #ff7b7b; }
.ws-invite-actions { padding: 0 16px 16px; display: flex; gap: 8px; justify-content: flex-end; }

/* Home search box (§1) — grows to fill the appbar between the title and + New Project. */
.project-search {
  flex: 1 1 auto; min-width: 0; max-width: 360px; margin-left: auto;
  height: 34px; padding: 0 var(--s-3);
  background: var(--bg-elev); color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  font: inherit; font-size: var(--t-sm);
}
.project-search::placeholder { color: var(--faint); }
.project-search:focus { outline: none; border-color: var(--accent); }

/* ───────── Stat strip (§1) — 4 aggregate cards (COPY design L385) ───────── */
/* design: padding:18px 24px 6px; display:grid; grid-template-columns:repeat(4,1fr); gap:12px —
   FULL-WIDTH (no max-width:1000, no margin:0 auto). */
/* #S8 (H1 dc:384-405): the DAEMONS row — cards per machine, horizontal-scroll. Literal from design. */
.daemons-cap { padding: 16px 24px 4px; font-size: 10px; font-family: var(--font-mono); letter-spacing: .12em; color: #6e7681; }
.daemons-scroll { padding: 0 24px; display: flex; gap: 12px; overflow-x: auto; }
.daemon-card { flex: none; width: 250px; border: 1px solid rgba(255,255,255,.08); background: #16181d; border-radius: 12px; padding: 13px 14px; display: flex; flex-direction: column; gap: 9px; cursor: pointer; }
.daemon-card.is-selected { border-color: rgba(55,148,255,.45); background: rgba(55,148,255,.08); }
.daemon-card.is-offline { width: 210px; background: #111318; border-color: rgba(255,255,255,.05); opacity: .6; cursor: default; }
/* JH-18: the card body stays non-interactive (selecting an offline machine filters nothing), but its
   Detail button is the ONLY way into the screen that can fix that machine — so it must read and
   behave as live: full opacity against the dimmed card, and a pointer cursor of its own. */
.daemon-card.is-offline .daemon-foot { display: flex; justify-content: flex-end; margin-top: auto; }
.daemon-card.is-offline .daemon-detail { cursor: pointer; opacity: 1; }
/* ST-1455: "Stop access" (revoke) sits beside Detail on an offline card — a quiet danger button. */
.daemon-card.is-offline .daemon-foot { gap: 6px; }
.daemon-card.is-offline .daemon-detail { margin-left: 0; }
.daemon-revoke { display: inline-flex; align-items: center; height: 24px; padding: 0 9px; border: 1px solid rgba(240,82,82,.4); background: rgba(240,82,82,.08); color: #f05252; border-radius: 7px; font-size: 11px; font-weight: 600; cursor: pointer; opacity: 1; }
.daemon-access-stopped { display: inline-flex; align-items: center; height: 24px; padding: 0 9px; border: 1px solid rgba(240,82,82,.25); color: #f05252; border-radius: 7px; font-size: 11px; font-weight: 600; opacity: 1; }
.daemon-revoke:hover { border-color: #f05252; background: rgba(240,82,82,.16); }
.daemon-head { display: flex; align-items: center; gap: 8px; }
.daemon-ico { color: #e6e8ec; display: inline-flex; flex: none; }
.daemon-card.is-offline .daemon-ico { color: #5a6069; }
.daemon-name { font-size: 14px; font-weight: 600; color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.daemon-card.is-offline .daemon-name { color: #8b909a; }
.daemon-dot { margin-left: auto; width: 7px; height: 7px; border-radius: 50%; background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.16); flex: none; }
.daemon-offtag { margin-left: auto; font-size: 9px; font-family: var(--font-mono); color: #5a6069; }
/* The install-id tail shown ONLY when two machines share a name (app.js disambiguator()). Three
   installs on one Mac all report the same hostname, so the name alone cannot say which card is
   which. Muted + mono: it is an identifier to tell cards apart, not a second title competing with
   the name. `flex-shrink:0` keeps it readable when the name beside it ellipsises. */
.daemon-disambig { flex-shrink: 0; margin-left: 6px; font-size: 10px; font-family: var(--font-mono); color: #6b7280; letter-spacing: .02em; }
.daemon-card.is-offline .daemon-disambig { color: #565c66; }
.daemon-meta { font-family: var(--font-mono); font-size: 10px; color: #6e7681; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.daemon-foot { display: flex; align-items: center; gap: 8px; }
.daemon-pc { font-size: 11px; color: #3794ff; font-family: var(--font-mono); }
.daemon-gear { margin-left: auto; width: 26px; height: 24px; border: 1px solid rgba(255,255,255,.12); background: transparent; color: #c4c8ce; border-radius: 7px; cursor: pointer; display: grid; place-items: center; }
.daemon-gear:hover { border-color: var(--accent); color: var(--fg-strong); }
/* STUCK-ACCOUNT-SURFACE: a machine's pending account sign-in cards (amber = needs you, as the Awaiting-you card). */
.daemon-pns { display: flex; flex-direction: column; gap: 4px; margin: 6px 0; }
.daemon-pn { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--warn, #e0b341); }
.daemon-pn-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.daemon-pn-signin { border: 1px solid var(--warn, #e0b341); background: transparent; color: var(--warn, #e0b341); border-radius: 6px; font-size: 11px; padding: 1px 8px; cursor: pointer; }
.daemon-pn-signin:hover { background: rgba(224,179,65,.12); }
/* ── H1.6 Remote Desktop (Troubleshoot) modal ─────────────────────────────────────────────── */
.hr-overlay { position: fixed; inset: 0; z-index: 10001; background: rgba(8,9,12,.72); display: flex; align-items: center; justify-content: center; padding: 24px; }
.hr-card { width: min(1200px, 96vw); max-height: 92vh; overflow: auto; background: #0e1014; border: 1px solid rgba(255,255,255,.12); border-radius: 14px; box-shadow: 0 24px 64px rgba(0,0,0,.6); padding: 18px 20px; }
.hr-title { font-size: 15px; font-weight: 700; color: #e6e8ec; margin-bottom: 12px; }
.hr-lead { font-size: 12px; color: #9aa0a6; line-height: 1.5; margin-bottom: 12px; }
.hr-lead b { color: #e6e8ec; }
.hr-err { font-size: 12px; color: #f0616d; background: rgba(240,97,109,.1); border: 1px solid rgba(240,97,109,.3); border-radius: 8px; padding: 7px 11px; margin-bottom: 10px; }
.hr-pass { width: 100%; height: 38px; padding: 0 13px; border: 1px solid rgba(255,255,255,.14); border-radius: 10px; background: #0b0c0f; color: #e6e8ec; font-size: 13px; box-sizing: border-box; }
.hr-actions { display: flex; gap: 9px; justify-content: flex-end; margin-top: 14px; }
.hr-btn { height: 34px; padding: 0 16px; border: 1px solid rgba(255,255,255,.14); background: #16181d; color: #c4c8ce; border-radius: 9px; font-size: 12px; font-weight: 600; cursor: pointer; }
.hr-btn:hover { border-color: var(--accent); color: var(--fg-strong); }
.hr-btn-primary { background: #3794ff; color: #fff; border-color: #3794ff; }
.hr-btn-primary:hover { background: #2b83e8; }
.hr-perms { display: flex; flex-direction: column; gap: 7px; margin: 10px 0; }
.hr-perm { font-size: 12px; color: #3794ff; text-decoration: none; padding: 8px 12px; border: 1px solid rgba(55,148,255,.3); border-radius: 8px; background: rgba(55,148,255,.06); }
.hr-perm:hover { background: rgba(55,148,255,.14); }
.hr-canvas { width: 100%; background: #000; border-radius: 8px; outline: none; cursor: crosshair; display: block; }
.hr-status { font-family: var(--font-mono); font-size: 10px; color: #6e7681; margin-top: 6px; }

/* ── H1.6 Daemon Detail (design h16) — resource console ─────────────────────────────────── */
.daemon-view { overflow-y: auto; }
.dd-wrap { max-width: 1400px; margin: 0 auto; padding: 18px 24px 40px; display: flex; flex-direction: column; gap: 14px; }
.dd-topbar { display: flex; align-items: center; gap: 12px; }
.dd-home { height: 32px; padding: 0 14px; border: 1px solid rgba(255,255,255,.12); background: #16181d; color: #c4c8ce; border-radius: 9px; font-size: 12px; font-weight: 600; cursor: pointer; }
.dd-home:hover { border-color: var(--accent); color: var(--fg-strong); }
.dd-title { font-size: 15px; font-weight: 700; color: #e6e8ec; }
.dd-search { flex: 1; max-width: 440px; margin-left: auto; height: 34px; padding: 0 13px; border: 1px solid rgba(255,255,255,.1); border-radius: 10px; background: #0b0c0f; color: #e6e8ec; font-size: 12px; }
.dd-settings { height: 32px; padding: 0 14px; border: 1px solid rgba(255,255,255,.12); background: #16181d; color: #c4c8ce; border-radius: 9px; font-size: 12px; cursor: pointer; }
/* A repair verb this server answered `not_implemented` (501). It stays VISIBLE and goes inert: the
   operator can see the feature exists and read in the tooltip why this build cannot run it. Hiding
   it would read as "the button moved"; leaving it live invited a retry that can never succeed, which
   is the defect this replaces (measured: "⬆ Update" -> 501 -> "Update failed: 501", button enabled). */
.dd-settings.is-unavailable, .dd-settings[disabled] { opacity: .45; cursor: not-allowed; text-decoration: line-through; }
.dd-infoline { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 11px; color: #8b909a; }
.dd-infoline b { color: #e6e8ec; font-weight: 600; }
.dd-on { color: #46d19e; }
/* Liveness has three states, and they must not look alike at a glance: green = heartbeating,
   red = known not heartbeating, amber = we could not ask. Amber deliberately reads as a WARNING and
   not as a pass, because "unknown" was previously painted as green "online". */
.dd-off { color: #f0616d; font-weight: 600; }
.dd-unknown { color: #e0b341; font-weight: 600; }
/* Age of the resource numbers (data.at). Dim: it is context for the figures, not a verdict. */
.dd-asof { color: #6e7681; }
.dd-autosleep { margin-left: auto; letter-spacing: .14em; font-size: 10px; color: #6e7681; }
/* BUG#B: the range strip and its honesty note share a row; the note explains that the ring holds
   less history than the selected tab asks for, so a 1h shape is never read as a 24h trend. */
.dd-tabrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dd-rangenote { font-size: 11px; color: #e0b341; font-family: var(--font-mono); }
.dd-tabs { display: flex; gap: 2px; background: #0b0c0f; border: 1px solid rgba(255,255,255,.1); border-radius: 9px; padding: 2px; width: max-content; }
.dd-tab { padding: 5px 14px; border: 0; border-radius: 7px; background: transparent; color: #8b909a; font-size: 11px; font-weight: 600; cursor: pointer; font-family: var(--font-mono); }
.dd-tab.on { background: #3794ff; color: #fff; }
.dd-tab:disabled { opacity: .5; cursor: default; }
.dd-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.dd-cards .stat-card { min-width: 0; }
.dd-dim { color: #6e7681; font-size: .6em; font-weight: 500; }
.dd-cardbar { margin-top: 8px; height: 5px; border-radius: 3px; background: rgba(255,255,255,.08); overflow: hidden; }
.dd-cardfill { display: block; height: 100%; border-radius: 3px; background: #46d19e; transition: width .4s ease; }
.dd-cardfill.mem { background: linear-gradient(90deg, #5b8def, #8b5cf6); }
.dd-cardsub { margin-top: 4px; font-family: var(--font-mono); font-size: 10px; color: #6e7681; }
.dd-freed { color: #46d19e; font-size: 12px; font-weight: 600; }
.dd-locknote { font-size: 11px; color: #8b909a; line-height: 1.5; background: rgba(55,148,255,.05); border: 1px solid rgba(55,148,255,.14); border-radius: 9px; padding: 8px 12px; }
.dd-amber { color: #d9a441; }
.dd-blue { color: #3794ff; }
.dd-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.dd-col { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.dd-colhead { display: flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em; color: #c4c8ce; font-weight: 700; padding: 3px 1px; }
.dd-colspacer { flex: 1; }
.dd-subhead { font-family: var(--font-mono); font-size: 9px; letter-spacing: .12em; color: #6e7681; margin-top: 6px; }
.dd-row { display: flex; align-items: center; gap: 9px; background: #101216; border: 1px solid rgba(255,255,255,.07); border-radius: 11px; padding: 10px 13px; min-width: 0; }
.dd-row.dd-slept { opacity: .8; background: #0d0f13; }
.dd-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; display: inline-grid; place-items: center; font-size: 9px; color: #8b909a; }
.dd-dot.busy { background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.16); }
.dd-dot.idle { background: #8b909a; }
.dd-dot.slept { background: transparent; }
.dd-vmico { flex: none; font-size: 12px; }
.dd-name { font-size: 13px; font-weight: 600; color: #e6e8ec; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dd-tag { flex: none; font-family: var(--font-mono); font-size: 9px; color: #8b909a; border: 1px solid rgba(255,255,255,.12); border-radius: 5px; padding: 1px 6px; }
.dd-res { flex: none; font-family: var(--font-mono); font-size: 10px; color: #c4c8ce; }
/* PO 23-07 "button suspend phải align right" + "cpu với RAM cũng phải ở bên phải luôn": the readout
   right-aligns its own text as well, so the numbers sit beside the control instead of hugging the VM
   name — one tidy right-hand column per row.
   A RUNNING VM row has no .dd-status, so nothing absorbed
   the row's slack and its Suspend/Protected control sat immediately after the RSS·CPU numbers — every
   row ending at a different x, a ragged column. .dd-res-grow marks the readout that should take the
   free space, so the controls land on the right edge exactly as .dd-status puts them on agent rows.
   A separate class (not a :last-of-type guess) because an AGENT row carries BOTH .dd-res and
   .dd-status — there the status keeps the slack and .dd-res must stay intrinsic. */
.dd-res-grow { flex: 1; min-width: 0; text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dd-status { flex: 1; min-width: 0; font-family: var(--font-mono); font-size: 10px; color: #6e7681; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: right; }
.dd-controls { flex: none; display: flex; align-items: center; gap: 6px; }
.dd-pill { font-size: 10px; font-weight: 600; border-radius: 7px; padding: 4px 10px; white-space: nowrap; }
.dd-prot { color: #3794ff; border: 1px solid rgba(55,148,255,.4); background: rgba(55,148,255,.08); }
.dd-locked { color: #d9a441; border: 1px solid rgba(217,164,65,.45); background: rgba(217,164,65,.1); }
.dd-lock, .dd-sleepmode { color: #c4c8ce; border: 1px solid rgba(255,255,255,.14); background: transparent; cursor: pointer; }
.dd-lock:hover, .dd-sleepmode:hover { border-color: var(--accent); color: var(--fg-strong); }
.dd-sleepon { color: #8b909a; border: 1px dashed rgba(255,255,255,.14); }
.dd-winsel { font-size: 10px; font-weight: 600; color: #c4c8ce; background: #0b0c0f; border: 1px solid rgba(255,255,255,.14); border-radius: 7px; padding: 3px 6px; cursor: pointer; max-width: 118px; }
.dd-winsel:hover { border-color: var(--accent); }
.dd-wake, .dd-wakeall { border: 1px solid rgba(55,148,255,.45); background: rgba(55,148,255,.12); color: #3794ff; border-radius: 8px; padding: 5px 13px; font-size: 11px; font-weight: 700; cursor: pointer; }
.dd-wake:hover, .dd-wakeall:hover { background: rgba(55,148,255,.22); }
.dd-wakeall { padding: 3px 11px; font-size: 10px; }
.dd-footnote { font-size: 11px; color: #8b909a; line-height: 1.55; background: rgba(70,209,158,.04); border: 1px solid rgba(70,209,158,.14); border-radius: 9px; padding: 9px 12px; }
.dd-empty { font-size: 12px; color: #8b909a; padding: 30px 10px; line-height: 1.6; }
@media (max-width: 900px) {
  .dd-cards { grid-template-columns: repeat(2, 1fr); }
  .dd-cols { grid-template-columns: 1fr; }
}

/* H1.6 (design h16b): live CPU/MEM bars on the daemon card, fed by host.stats via /api/home. */
.daemon-bar-row { display: flex; align-items: center; gap: 7px; }
.daemon-bar-cap { font-family: var(--font-mono); font-size: 9px; letter-spacing: .08em; color: #6e7681; width: 26px; flex: none; }
.daemon-bar { flex: 1; height: 4px; border-radius: 2px; background: rgba(255,255,255,.08); overflow: hidden; }
.daemon-bar-fill { display: block; height: 100%; border-radius: 2px; background: #46d19e; transition: width .4s ease; }
.daemon-bar-fill.mem { background: linear-gradient(90deg, #5b8def, #8b5cf6); }
.daemon-bar-val { font-family: var(--font-mono); font-size: 9px; color: #8b909a; flex: none; white-space: nowrap; }
.daemon-detail { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 9px; border: 1px solid rgba(55,148,255,.4); background: rgba(55,148,255,.1); color: #3794ff; border-radius: 7px; font-size: 11px; font-weight: 600; cursor: pointer; }
.daemon-detail:hover { border-color: var(--accent); background: rgba(55,148,255,.18); }
.daemon-detail svg { width: 12px; height: 12px; }
.daemon-foot .daemon-gear { margin-left: 0; }
.daemon-add { flex: none; width: 150px; border: 1px dashed rgba(255,255,255,.14); border-radius: 12px; padding: 13px 14px; display: grid; place-items: center; text-align: center; cursor: pointer; font-size: 11px; color: #3794ff; }
.daemon-add-sub { font-size: 9px; color: #6e7681; }
/* B-RS16: machines not seen for > 7 days fold into "Offline (N)" at the end of the DAEMONS strip
   (same idea as the project grid's "Inactive (N)"). Closed = one small tile; open = the cards. */
.daemons-offline { flex: none; display: block; white-space: nowrap; }
.daemons-offline-summary {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 4px; cursor: pointer; list-style: none;
  vertical-align: top; box-sizing: border-box; height: 100%; min-height: 96px; width: 120px; white-space: normal; border: 1px dashed rgba(255,255,255,.08); border-radius: 12px; padding: 13px 14px;
  color: var(--muted, #6e7681); font-size: 11px; user-select: none;
}
.daemons-offline-summary::-webkit-details-marker { display: none; }
.daemons-offline-summary:hover { color: var(--text-2, #9aa0ad); }
.daemons-offline .pi-caret { display: inline-block; transition: transform .15s; }
.daemons-offline[open] .pi-caret { transform: rotate(90deg); }
.daemons-offline .pi-label { font-weight: 600; letter-spacing: .02em; }
.daemons-offline .pi-hint { font-size: 9px; opacity: .65; }
.daemons-offline-cards { display: inline-flex; gap: 12px; margin-left: 12px; vertical-align: top; white-space: normal; }
.daemon-empty { flex: none; align-self: center; max-width: 340px; font-size: 11px; color: #6e7681; line-height: 1.5; }
.daemon-empty code { font-family: 'Geist Mono', monospace; font-size: 10px; color: #c4c8ce; background: rgba(255,255,255,.06); padding: 1px 5px; border-radius: 5px; }
#stat-strip { padding: 18px 24px 6px; }
.stat-strip {
  /* WEB-T11 round 3: FIVE cards, not four. The Projects count joined the strip when
     the project directory door was built, and a `repeat(4, 1fr)` grid wrapped it onto
     a second row where the last card sat alone at a quarter width — measured by
     opening this lane's own evidence screenshot.
     Round 4: SIX (OKR/KPI), and the same defect again with `repeat(5, 1fr)` — so the
     column count is no longer a literal that has to be edited every time a tile is
     born. Cards fit as many per row as the width allows and share it evenly. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px;
}
.stat-card {
  /* dc:385 HI-FI (canonical §03): border rgba(255,255,255,.07) (not --border .08), radius 12,
     padding 14px 16px. Element-scoped border literal. */
  background: var(--panel-2); border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 12px; padding: 14px 16px;
  display: flex; flex-direction: column; gap: var(--s-2); min-width: 0;
}
.stat-label {
  font-family: var(--font-mono); font-size: var(--t-label);
  /* dc:385 HI-FI — the stat label reads #6e7681 (--faint), not the lighter --muted #8b909a. */
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint);
}
.stat-value { display: flex; align-items: baseline; gap: var(--s-1); min-width: 0; }
.stat-value.mono { font-family: var(--font-mono); }
.stat-num { font-size: 26px; font-weight: 700; color: var(--fg); letter-spacing: -0.02em; } /* dc:385 HI-FI: 26px, color #e6e8ec (--fg), not --fg-strong (#f2f3f5) */
.stat-den { font-size: 15px; font-weight: 600; color: var(--muted); }
.stat-dot { width: 8px; height: 8px; border-radius: 50%; align-self: center; margin-right: var(--s-1); }
.stat-dot.online { background: var(--state-running); box-shadow: 0 0 0 3px var(--state-running-glow); }
/* WEB-T11 (WEBUI-103/104) — a figure that could not be counted.
   It is deliberately NOT red: red is a measured bad state, and "we could not
   ask" is not a measurement of the fleet at all. It is muted, italic and
   smaller than a real number, so a glance can tell a value from a reason
   without reading the word — which is what stops the strip being skimmed as if
   every card carried a count. The dot beside it is grey for the same reason:
   neither green nor red, because both would assert something nobody measured. */
.stat-dot.offline { background: var(--muted); }
.stat-dot.unknown {
  background: transparent;
  border: 1.5px dashed var(--muted);
  box-sizing: border-box;
}
.stat-unavailable {
  font-size: 14px;
  font-weight: 600;
  font-style: italic;
  color: var(--muted);
  align-self: center;
  cursor: help;
}
.stat-card.is-unavailable { border-style: dashed; }
/* WEB-T11 round 4 (WEBUI-103) — the OKR/KPI card names its xcorp snapshot,
   version and freshness on a provenance line under the number, and a snapshot
   past its declared validity window is marked STALE. Amber, not red: the number
   is real, it is merely old — a reader must not mistake it for a bad KPI, nor
   for a current one. */
.stat-evidence {
  font-family: var(--font-mono); font-size: 11px; color: var(--faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stat-stale {
  font-weight: 700; color: var(--state-awaiting, #d29922); letter-spacing: 0.08em;
  cursor: help;
}
.stat-card.is-stale { border-color: rgba(210, 153, 34, 0.45); }
/* The DAEMONS caption's reason, and the projects grid's could-not-read notice.
   Same reasoning as .stat-unavailable: muted and italic, never red — "we could
   not ask" is not a measured bad state. */
.daemons-unavailable { font-style: italic; color: var(--muted); cursor: help; }
p.empty.is-unavailable { color: var(--muted); max-width: 62ch; line-height: 1.55; }
.stat-adds { color: var(--state-running); font-size: 20px; font-weight: 600; }
.stat-dels { color: var(--state-awaiting); font-size: 20px; font-weight: 600; }
/* Awaiting-you card: amber accent, clickable when >0 (the "needs you" count). */
.stat-card.awaiting .stat-note { font-size: 11px; color: var(--muted, #8a8f98); line-height: 1.3; }
.stat-card.awaiting.has-awaiting {
  border-color: var(--state-thinking);
  cursor: pointer;
}
.stat-card.awaiting.has-awaiting .stat-num { color: var(--state-thinking); }
.stat-card.awaiting .stat-unit { font-size: 13px; color: var(--muted, #8a8f98); margin-left: 6px; }
.stat-card.awaiting.has-awaiting:hover { background: rgba(224, 179, 65, 0.06); }
.stat-card.awaiting.has-awaiting:focus-visible { outline: 2px solid var(--state-thinking); outline-offset: 2px; }
/* design Team Home: the active Awaiting tile ends in an amber arrow_forward, pushed to the right edge. */
.stat-card.awaiting.has-awaiting .stat-value { display: flex; align-items: center; gap: 10px; }
.stat-card.awaiting .stat-go { margin-left: auto; color: var(--state-thinking); flex: none; }
/* design Cockpit H2c: the roster preset bar ("Awaiting N ✕ · All M") and its "K hidden by filter" tail. */
.roster-preset { display: flex; gap: 6px; padding: 6px 12px 8px; }
.roster-preset-chip { font: 500 11.5px var(--font-ui); height: 23px; padding: 0 9px; border-radius: 12px; cursor: pointer;
  color: var(--muted); background: transparent; border: 1px solid rgba(255, 255, 255, 0.1); }
.roster-preset-chip.is-on { color: var(--state-thinking); background: rgba(224, 179, 65, 0.13); border-color: rgba(224, 179, 65, 0.3); }
.roster-preset-hidden { margin: 8px 12px; font: 400 11px var(--font-mono); color: var(--muted); }
.roster-preset-showall { font: inherit; color: var(--accent, #3794ff); background: none; border: 0; padding: 0; cursor: pointer; }

.projects-grid {
  /* design L392: cards padding:12px 24px — the grid shares the 24px gutter, full-width. */
  padding: 12px 24px 24px;
}
/* §1 awaiting flash: brief attention pulse when "Awaiting you" is clicked. */
.projects-grid.flash-attn { animation: flashAttn 1.2s var(--ease); }
@keyframes flashAttn { 0%, 100% { box-shadow: none; } 30% { box-shadow: 0 0 0 2px var(--state-thinking); } }
/* New Project form (§3): design = a COMPACT panel (~440px), NOT full-width. QC bảng số: form
   440px. Sits below the head with the 24px gutter; left-aligned like the design panel. */
.projects-view .new-project-form { max-width: 440px; margin: 12px 24px 16px; border-radius: var(--r-lg); border-bottom: none; }

/* loading skeleton (shimmer) shown before the first projects fetch returns */
.skeleton-card {
  height: 54px; border-radius: var(--r-md); margin-bottom: var(--s-2);
  background: linear-gradient(100deg, var(--panel) 30%, var(--panel-2) 50%, var(--panel) 70%);
  background-size: 200% 100%; animation: shimmer 1.3s var(--ease) infinite;
  border: 1px solid var(--border);
}
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* reconnecting banner */
.conn-banner {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3); font-size: var(--t-sm); font-weight: 600;
  color: #fff; background: var(--red); flex: none;
}
.conn-dot {
  width: 8px; height: 8px; border-radius: 50%; background: #fff;
  animation: pulse 1.4s var(--ease) infinite;
}

/* projects render as a single-column list of clickable cards */
ul.projects { display: flex; flex-direction: column; gap: var(--s-2); }
li.project {
  /* dc:380-425 — card surface is --panel-2 (#16181d); the later li.project rule below also sets this,
     kept in sync here so the stale --panel value can't mislead a future edit. */
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4); box-shadow: var(--shadow-sm);
  cursor: pointer; transition: border-color 0.13s var(--ease), background 0.13s var(--ease), transform 0.06s var(--ease);
}
li.project:hover { border-color: var(--accent); background: var(--panel-2); }
li.project:active { transform: translateY(0.5px); }
li.project:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
li.project + li.project { border-top: 1px solid var(--border); } /* override the side-list rule */
.project-go { color: var(--faint); font-size: var(--t-lg); line-height: 1; flex: none; }
li.project:hover .project-go { color: var(--accent); }
/* #S5: the ⋯ kebab — subtle, only on row hover/focus; opens the Rename/Remove menu (no exposed ✕). */
.project-kebab { margin-left: auto; flex: none; width: 26px; height: 26px; padding: 0; border-radius: var(--r-sm); background: transparent; border: 1px solid transparent; color: var(--faint); font-size: var(--t-lg); line-height: 1; cursor: pointer; opacity: 0; transition: opacity .15s, color .15s, background .15s; }
li.project:hover .project-kebab, li.project:focus-within .project-kebab, .project-kebab:focus { opacity: 1; }
.project-kebab:hover { color: var(--fg-strong); background: rgba(255,255,255,.06); }
.project-kebab-menu { z-index: 1130; min-width: 210px; background: #16181d; border: 1px solid rgba(255,255,255,.14); border-radius: 10px; box-shadow: 0 16px 40px rgba(0,0,0,.55); padding: 6px; display: flex; flex-direction: column; gap: 1px; }
.pk-item { display: flex; align-items: center; gap: 9px; padding: 8px 10px; border: none; background: transparent; color: #d4d7dd; font-size: 12px; text-align: left; border-radius: 7px; cursor: pointer; }
.pk-item:hover { background: rgba(255,255,255,.06); }
.pk-item.pk-danger { color: #ff9b9b; } .pk-item.pk-danger:hover { background: rgba(255,107,107,.1); }
/* GAP-WEB-4: forget-from-dashboard ✕ — pushed to the right; subtle until hover. */
.project-remove {
  margin-left: auto; flex: none;
  width: 26px; height: 26px; padding: 0; border-radius: var(--r-sm);
  background: transparent; border: 1px solid transparent; color: var(--faint);
  font-size: var(--t-base); line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s var(--ease), color 0.15s var(--ease), background 0.15s var(--ease);
}
li.project:hover .project-remove, li.project:focus-within .project-remove { opacity: 1; }
.project-remove:hover { color: var(--red); background: var(--panel-2); border-color: var(--border); }
/* #87 ✎ rename button — subtle until hover (like ✕). */
.project-rename {
  flex: none; width: 26px; height: 26px; padding: 0; border-radius: var(--r-sm);
  background: transparent; border: 1px solid transparent; color: var(--faint);
  font-size: var(--t-base); line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.15s var(--ease), color 0.15s var(--ease), background 0.15s var(--ease);
}
li.project:hover .project-rename, li.project:focus-within .project-rename { opacity: 1; }
.project-rename:hover { color: var(--accent); background: var(--panel-2); border-color: var(--border); }
.project-rename:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
/* #87 inline-edit input (replaces the name span while editing). */
.project-name-edit {
  font: inherit; font-weight: 600; padding: var(--s-1) var(--s-1); max-width: 240px;
  background: var(--bg-elev); border: 1px solid var(--accent); border-radius: var(--r-sm);
  color: var(--fg-strong);
}
.project-remove:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
.project-remove + .project-go { margin-left: var(--s-1); }

/* ════════════════════════ SCREEN B: Project detail ════════════════════════ */
.project-view { display: flex; flex-direction: column; min-height: 0; }
.tabbar {
  display: flex; gap: var(--s-1); align-items: stretch; flex: none;
  height: 42px; padding: 0 var(--s-2); overflow-x: auto; /* #112 dc:908: 42px */
  background: var(--bg-elev); border-bottom: 1px solid var(--border);
}
/* G tab (authoritative render design_handoff dc.html:1308): min-width 112, MAX-WIDTH 160, gap 7px,
   pad 0 10px, border-RIGHT divider + border-TOP 2px state-accent (active)/transparent (inactive).
   Name ellipsis-clips. (Render = source of truth; SPEC-text:166 says 150 but is stale — lead ruling.) */
.tab {
  display: inline-flex; align-items: center; gap: 7px; width: 140px; min-width: 140px;
  max-width: 140px; /* H16 dc:911/1314: agent tab 130→140 */
  padding: 0 10px; font: inherit; font-size: var(--t-sm); font-weight: 600;
  color: var(--muted); background: transparent;
  border: none; border-right: 1px solid var(--border); border-top: 2px solid transparent;
  cursor: pointer; white-space: nowrap; position: relative;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
/* #112 (dc:908): the agent tab's 2px TOP strip = the agent's STATE colour (matching its dot), NOT the
   active-accent — a running agent reads green, awaiting red, idle grey. State WINS over active (the
   active tab is shown by the darker bg + fg-strong text + the dot, not a blue top-border). Ordered
   AFTER .tab.active (line ~609) so the state colour is authoritative. */
.tab.busy { border-top-color: var(--state-running, #46d19e); background: #0b0c0f; }
.tab.awaiting { border-top-color: transparent; background: rgba(255, 107, 107, 0.06); } /* dc:909: transparent border + red-tint bg */
.tab.awaiting .tab-label { color: #ff9b9b; }
.tab.exited { border-top-color: transparent; }
.tab .tab-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the ❯_ term + ✕ close show on hover / active (keeps inactive tabs compact so more fit). */
.tab .tab-term, .tab .tab-close { flex: none; opacity: 0; width: 0; overflow: hidden; transition: opacity 0.1s; display: inline-flex; align-items: center; }
.tab:hover .tab-term, .tab:hover .tab-close,
.tab.active .tab-term, .tab.active .tab-close,
.tab.awaiting .tab-term { opacity: 1; width: auto; }
.tab .tab-term { color: var(--muted); border-radius: 5px; }
.tab .tab-term:hover { color: var(--fg-strong); }
.tab .tab-close { color: #5a6069; font-size: var(--t-sm); }
.tab .tab-close:hover { color: var(--fg-strong); }
.tab:hover { color: var(--fg-strong); background: var(--panel-2); }
/* active tab (dc:908) — darker bg #0b0c0f + fg-strong text. The 2px top border is the STATE colour
   (below), NOT the blue accent — so a running active tab reads GREEN like the design (QC #112). */
.tab.active { color: var(--fg-strong); background: #0b0c0f; }
.tab.active .tab-label { color: var(--fg-strong); }
/* #112 (dc:908-909, lead-verified) STATE border-top — authoritative (last, wins over .tab.active):
   ONLY a running agent gets a coloured (green) top-strip; awaiting + idle + exited are TRANSPARENT
   (their state reads from the bg/dot, not the border — dc:909 awaiting = transparent border + red-tint
   bg; dc:908 non-leader idle = transparent border). */
.tab { border-top-color: transparent; } /* default: no state strip (idle/awaiting/exited all transparent) */
.tab.busy { border-top-color: var(--state-running, #46d19e); } /* ONLY a running agent → green top (dc:908) */
/* G-14 awaiting tab — red tint bg + red name (prototype rgba(255,107,107,.06)). Always shows ❯_.
   #112 (dc:909): the top border is TRANSPARENT for awaiting — the red-tint bg + red dot carry the state. */
.tab.awaiting { background: rgba(255, 107, 107, 0.06); border-top-color: transparent; }
.tab.awaiting .tab-label { color: #ff9b9b; }
/* tab state-dot (prototype: awaiting red pulse · busy amber · online green · offline grey). */
.tab-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.tab-dot.online { background: var(--green); box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.18); }
/* dc:643 — a busy/thinking dot PULSES (animation:dmPulse), same as awaiting. #88: the busy dot
   was static; a running agent's tab now pulses so "đang chạy" reads at a glance (design parity). */
.tab-dot.busy { background: var(--orange, #e0b341); animation: tabpulse 1.4s ease-in-out infinite; }
.tab-dot.awaiting { background: #ff6b6b; animation: tabpulse 1.4s ease-in-out infinite; }
/* README:74 4-color state system — idle/offline = --state-idle (#5a6069), not --faint (QC 🟡). */
.tab-dot.offline { background: var(--state-idle); }
.tab-dot.idle { background: var(--state-idle); }
.tab-dot.exited { background: var(--faint); box-shadow: none; }
/* G-19 (UC-169): an EXITED tab reads dead — dimmed, gray label + an "exited" pill. Clicking it still
   opens the transcript; the ⟳ Re-spawn button resumes the session. */
.tab.exited { opacity: 0.62; }
.tab.exited .tab-label { color: var(--faint); text-decoration: line-through; text-decoration-color: rgba(255, 255, 255, 0.2); }
.tab-exited {
  flex: none; font-family: var(--font-mono); font-size: 9px; letter-spacing: .04em;
  color: var(--faint); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 1px 5px; text-transform: uppercase;
}
.tab .tab-respawn { flex: none; color: var(--muted); font-size: var(--t-sm); opacity: 0; width: 0; overflow: hidden; transition: opacity 0.1s; display: inline-flex; align-items: center; }
.tab:hover .tab-respawn, .tab.active .tab-respawn, .tab.exited .tab-respawn { opacity: 1; width: auto; }
.tab .tab-respawn:hover { color: var(--accent); }
/* ⚡ Restart-skip-perm — revealed on tab hover / active, amber on hover (destructive + security-sensitive). */
.tab .tab-restart-skip { flex: none; color: var(--muted); font-size: var(--t-sm); opacity: 0; width: 0; overflow: hidden; transition: opacity 0.1s; display: inline-flex; align-items: center; }
.tab:hover .tab-restart-skip, .tab.active .tab-restart-skip { opacity: 1; width: auto; }
.tab .tab-restart-skip:hover { color: var(--warn, #e0a020); }
/* G-04/05 ▾N overflow chooser button. */
.tab-overflow {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  padding: 0 10px; border: none; border-right: 1px solid var(--border); background: transparent;
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); cursor: pointer;
}
.tab-overflow:hover { color: var(--fg-strong); background: var(--panel-2); }
.tab-of-caret { font-size: 9px; }
.tab-of-count { font-size: var(--t-xs); }
.tabbar-empty { padding: var(--s-3); }
.appbar-back { padding: var(--s-1) var(--s-2); margin-right: var(--s-1); }

/* ── Chip row (Agents / ToDo / Team) — mirror of the extension chip-row ─────── */
/* R-01: the strip scrolls horizontally when narrow (prototype dc.html:932 overflow-x:auto) — NOT
   wrap. border-TOP (it sits at the BOTTOM of the chat, below the composer — SPEC §5b). */
.chip-row {
  display: flex; gap: 6px; align-items: center; flex: none; flex-wrap: nowrap; overflow-x: auto;
  padding: var(--s-2) var(--s-3); border-top: 1px solid var(--border); background: var(--bg-elev);
  /* JCK-3: a scrollbar that STAYS. macOS overlay scrollbars paint nothing while idle, and
     that is how 123px of chips went unnoticed at 1920px wide (_audit/PM-LIVE/po-click/
     22-final.json: scrollW 475 vs clientW 352 in Work mode). `thin` keeps the strip's
     height honest — the chips are 26px tall and a full-width bar would crowd them. */
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
/* WebKit's half of the same request: Chrome on macOS ignores scrollbar-width's `thin` in
   overlay mode, so the track is asked for explicitly. Both declarations describe ONE
   intention — a scrollbar the eye can find — and neither alone covers both engines. */
.chip-row::-webkit-scrollbar { height: 6px; }
.chip-row::-webkit-scrollbar-track { background: transparent; }
.chip-row::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 3px;
}
/* JCK-3 — THE STATE: this strip is hiding chips right now.
   Set by chips.js from a real measurement (scrollWidth > clientWidth), never from a chip
   count — a count cannot know how wide "51 teammates" renders.

   The cue is a MASK fade at the trailing edge: the last visible chip dissolves instead of
   ending cleanly, which reads as "this continues" the way a hard edge does not. A fade is
   used rather than an arrow button because the strip already scrolls by wheel, trackpad
   and keyboard — the missing thing was the SIGNAL, not another control.

   `mask-image` (with the -webkit- prefix for Safari) is composited and costs no layout, so
   it cannot reflow the strip it is describing. When the strip is scrolled fully to the end
   the paint drops the class, so the fade never lies about content that is no longer there. */
.chip-row.is-overflowing {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
}
/* Scrolled to the far right: the fade moves to the LEADING edge, so the cue always points
   where the unseen chips actually are rather than decorating a fixed side. */
.chip-row.is-overflowing.at-end {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px);
  mask-image: linear-gradient(to right, transparent 0, #000 28px);
}
/* R chip-strip — prototype dc.html:933-939: rounded-rect (8px, NOT pill), 5px 9px pad, 6px gap. */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 9px; border-radius: 8px;
  /* QC/design (dc:957 authoritative): chip bg = #16181d (--panel-2), NOT #111318 (--panel). */
  border: 1px solid var(--border); background: var(--panel-2);
  font: inherit; font-size: var(--t-xs); font-weight: 600; color: var(--fg);
  cursor: pointer; transition: border-color 0.12s var(--ease), background 0.12s var(--ease);
  flex: none; /* the strip scrolls horizontally when narrow (overflow-x on .chip-row) */
}
.chip:hover { border-color: var(--border-strong); background: var(--panel-2); }
.chip[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chip-icon { font-size: var(--t-xs); line-height: 1; display: inline-flex; align-items: center; }
/* R-01: chip icons are SVG (currentColor), 13px like the prototype. Tint per-chip below. */
.chip-icon svg { width: 13px; height: 13px; display: block; }
/* per-chip icon tint — prototype-exact colors (dc.html legend 958-965). */
.chip-bg .chip-icon { color: #569cd6; }
.chip-sub .chip-icon { color: #c586c0; }
.chip-diff .chip-icon { color: #e0b341; }         /* uncommitted — amber git-node */
.chip-agents .chip-icon { color: #3794ff; }
.chip-todo .chip-icon { color: #8b909a; }
.chip-team .chip-icon { color: #8b5cf6; }
.chip-screen .chip-icon { color: #8b909a; }       /* live CLI */
.chip-sched .chip-icon { color: #e0b341; }
.chip-files .chip-icon { color: var(--green, #4caf50); }
/* R-02 "vàng nhấn": a chip with work (uncommitted>0) → amber border (dc.html:961). */
.chip.has-work { border-color: rgba(224, 179, 65, 0.28); }
.chip.has-work .chip-count { color: #c4c8ce; }
.chip-label { color: var(--muted); }              /* kept for the mobile sheet / cold-open surfaces */
.chip-count {
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--fg-strong);
  background: var(--code-bg); padding: 0 var(--s-1); border-radius: 999px;
}
.chip-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.chip-dot.online { background: var(--green); box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); }
.chip-dot.offline { background: var(--faint); }
.chip-todo.chip-done .chip-count { color: var(--green); }
/* A blocked task or step on the board: the chip says so beside done/total (never reads "complete"). */
.chip-todo .chip-blocked { font-size: var(--t-xs); margin-left: 2px; color: var(--red, #e5534b); }
.chip-team { border-color: rgba(139, 92, 246, 0.4); }

/* ── Card ─────────────────────────────────────────────────────────────────── */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s-3) var(--s-3);
  border-bottom: 1px solid var(--border);
}
.card-head h2 {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--muted);
}
.card-body { padding: var(--s-2) var(--s-3) var(--s-3); }

.live-badge { display: inline-flex; align-items: center; gap: var(--s-1); font-size: var(--t-xs); font-weight: 600; color: var(--green); }
.live-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.5); animation: pulse 2s var(--ease) infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.45); }
  70% { box-shadow: 0 0 0 6px rgba(76, 175, 80, 0); }
  100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); }
}

/* ── Agents list ──────────────────────────────────────────────────────────── */
ul.agents, ul.projects { list-style: none; margin: 0; padding: 0; }
li.agent {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-1); border-radius: var(--r-sm);
  font-size: var(--t-base);
}
li.agent + li.agent { border-top: 1px solid var(--border); }
.dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.dot.online { background: var(--green); box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.18); }
.dot.offline { background: var(--faint); }
li.agent .label { font-weight: 600; color: var(--fg-strong); }
li.agent .host { color: var(--muted); font-size: var(--t-xs); font-family: var(--font-mono); }
li.agent .status {
  margin-left: auto; font-size: var(--t-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--faint);
}
li.agent.online .status { color: var(--green); }

/* ── GAP-9 Home project CARDS (design §1 line 392-412) ────────────────────────
   The Home projects render as a 2-col grid of vertical CARDS: name-row (state dot ·
   name · `N agents · M online` mono, right), path (mono), agent CHIPS (state-color
   pills, wrap), then the activity/preview line. Replaces the old horizontal list row. */
ul.projects {
  /* GAP-15: match the design's card grid — gap 14px (design line 392), NOT the 8px token drift. */
  /* JH-8 (PO 2026-09-05 18:10, verbatim "list project card la 3 card 1 row"): THREE per row.
     The v2 design file draws this grid at repeat(2,1fr) — measured at offset 95183 of
     `Team Home.dc.html` — and the PO's instruction overrides it, which is the DESIGN-MANIFEST
     rule that requirement wins where the two disagree. Recorded here so the next reader does
     not "fix" this back to the design file it deliberately departs from.
     720-1079px keeps TWO tracks: three cards on a narrow laptop leaves each one too thin for
     the name + `N agents · M online` line, which is the content this row exists to show. */
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 14px; align-content: start;
  /* S10 follow-up: #projects (.projects-grid) is ITSELF a grid at >=720px, so this <ul> is a grid
     ITEM inside it and must be told to span the parent's full inline axis. Before li{min-width:0}
     the cards' ~427px min-content propped this <ul> open by ACCIDENT; removing that strut let it
     collapse onto one 341px track (measured 1440: gtc "163.5px 163.5px", .project-name width 0).
     Size from the CONTAINER, never from content: claim the whole row, then allow shrink. */
  grid-column: 1 / -1;
  width: 100%;
  min-width: 0;
}
li.project {
  /* S10-layout-1: a grid ITEM defaults to `min-width: auto` = its min-content width. The card's
     top-line holds nowrap parts (the mono `N agents · M online`, the ✕/⋯ controls) whose combined
     min-content is ~427px, so at 375px the `1fr` track was BLOWN OUT to 427 and the card (plus its
     kebab at right:450) rendered outside the viewport. min-width:0 lets the track honour 1fr and
     the card follow the container at ANY width — no hardcoded widths, no breakpoint patching. */
  min-width: 0;
  /* GAP-15: card radius 14 (design) via --r-lg (not --r-md=10), padding 16/18 (design 16-18). */
  display: flex; flex-direction: column; align-items: stretch; gap: 14px;
  padding: 16px 18px; border-radius: var(--r-lg);
  /* dc:380-425 — the project card rests on the CARD surface #16181d (--panel-2), not the dimmed
     --panel (#111318). --panel is "dimmed cards/popovers"; a resting card is --panel-2. */
  background: var(--panel-2); border: 1px solid var(--border);
  cursor: pointer; transition: background 0.12s var(--ease), border-color 0.12s var(--ease);
}
li.project:hover { background: var(--panel-2); border-color: var(--accent); }
li.project + li.project { border-top: 1px solid var(--border); } /* grid: harmless (each card has its own border) */
/* name row (top-line): state dot · name · `N agents · M online` (right) · controls. */
/* JH-8: the top-line WRAPS. At 3 cards per row (PO 2026-09-05 18:10) a card is a
   third of the grid rather than a half, and the offline branch packs the most onto
   this line — name + "N agents · offline · last seen X" + an `Open ▸` button. Held on
   one nowrap row, the name lost the fight and rendered as "S..." with the button
   pushed onto two lines (photographed in _audit/J-HOME/05-three-per-row). Wrapping
   lets the name keep the first row, which is the part a person navigates by, and
   sends the stats and the button to a second row when they do not fit. */
.project-topline {
  display: flex; align-items: center; gap: var(--s-2); min-width: 0;
  flex-wrap: wrap; row-gap: var(--s-1);
}
/* The name claims the row: it may shrink to its ellipsis, but it is never the part
   that gets pushed off. `flex: 1 1 auto` with min-width:0 (below) is what makes the
   ellipsis fire instead of the box overflowing. */
.project-topline .project-name { flex: 1 1 auto; }
.project-glyph { color: var(--accent); display: inline-flex; flex: none; align-items: center; }
.project-meta { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
.project-name { font-weight: 600; color: var(--fg-strong); font-size: var(--t-md); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project-stats { margin-left: auto; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); flex: none; white-space: nowrap; }
.project-topline .project-stats + .project-rename { margin-left: var(--s-1); }
.project-path {
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
  /* #91e V4: middle-truncated in render.js (keeps the meaningful tail); ellipsis = safety net. */
}
/* first line of .project-meta = name + stats on ONE row. */
.project-name, .project-stats { line-height: 1.3; }

/* GAP-9 agent CHIPS — a state-colored pill per agent (dot + name, wrap). */
.project-chips { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.project-chip {
  display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 9px;
  border-radius: 999px; font-size: var(--t-xs); border: 1px solid var(--border);
  background: var(--panel-2); color: var(--muted); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.project-chip .pc-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--muted); }
.project-chip.st-running { background: color-mix(in srgb, var(--state-running, #46d19e) 10%, transparent); border-color: color-mix(in srgb, var(--state-running, #46d19e) 22%, transparent); color: #7fe3bf; }
.project-chip.st-running .pc-dot { background: var(--state-running, #46d19e); }
.project-chip.st-thinking { background: color-mix(in srgb, var(--state-thinking, #e0b341) 10%, transparent); border-color: color-mix(in srgb, var(--state-thinking, #e0b341) 22%, transparent); color: #e6cf94; }
.project-chip.st-thinking .pc-dot { background: var(--state-thinking, #e0b341); }
.project-chip.st-awaiting { background: color-mix(in srgb, var(--state-awaiting, #ff6b6b) 10%, transparent); border-color: color-mix(in srgb, var(--state-awaiting, #ff6b6b) 24%, transparent); color: #ff9b9b; }
.project-chip.st-awaiting .pc-dot { background: var(--state-awaiting, #ff6b6b); }
.project-chip.st-idle { color: var(--muted); }
.project-chip.st-idle .pc-dot { background: var(--state-idle, #5a6069); }
/* #179 — asleep is NOT idle. A slept teammate is still on the team (H1.6 wakes it when work
   arrives), so it keeps a readable name and a hollow dot: present, but not awake. An exited pane
   is fainter still, with a crossed-out dot, so a dead session cannot be mistaken for a free one. */
.project-chip.st-slept { color: var(--muted); }
.project-chip.st-slept .pc-dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--state-idle, #5a6069); }
.project-chip.st-offline { color: var(--faint); }
.project-chip.st-offline .pc-dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--faint, #4a4f57); }
.project-chip.st-exited { color: var(--faint); opacity: .72; }
.project-chip.st-exited .pc-dot { background: var(--faint, #4a4f57); opacity: .6; }
/* J7 #700 — HOW OLD THE READING IS, on the face of the pill.
   The server ships `stale` + `reportedAt` per chip and the card used to drop both, so an
   observation 182 hours old wore the same live pill as one from a second ago (measured on
   :4675, 2026-09-15: eight such pills under a "33 agents · 26 online" header).
   The age is muted rather than alarming: a stale reading is not an error, it is a reading
   whose age the reader must be able to see. The DOT keeps its state colour — the dot is the
   daemon's own verdict, and recolouring it here would be the browser deciding liveness. */
.project-chip .pc-stale { color: var(--faint); font-variant-numeric: tabular-nums; }
.project-chip.is-stale { border-style: dashed; }
.project-chip.st-more { color: var(--faint); }

/* the activity/preview line — top-border rule + a mono marker (like the design). */
.project-preview {
  display: block; font-size: var(--t-sm); color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding-top: var(--s-2); border-top: 1px solid var(--border);
}
/* JH-11: the remark's age, quieter than the remark. It is the WORDS a person reads
   first and the age only when they ask "is that still true", so it is dimmed and
   never allowed to take room from the sentence — the ellipsis above eats the text,
   not the timestamp, because this span cannot shrink. */
.project-preview-age { color: var(--faint); font-family: var(--font-mono); font-size: var(--t-xs); }
@media (max-width: 719px) { ul.projects { grid-template-columns: 1fr; } } /* single column on mobile */
/* JH-8: between phone and desktop the cards keep TWO tracks — three 1fr tracks under ~1080px
   squeeze the card's top line (dot + name + the nowrap `N agents · M online`) past what it can
   show, and a card that cannot show its counts is the thing this task exists to fix. */
@media (min-width: 720px) and (max-width: 1079px) { ul.projects { grid-template-columns: repeat(2, 1fr); } }
/* S10-layout-1 (phones): with min-width:0 above the CARD now follows the viewport, but the
   top-line still packs dot + name + a nowrap mono stats string + (offline) `Open ▸` + the ⋯
   kebab onto one line. At 375px that no longer fits, so let the line WRAP: the name keeps the
   first row (it already ellipsises), and the stats/actions flow to a second row. The kebab keeps
   `margin-left:auto` so it stays right-aligned INSIDE the card instead of pushing past its edge. */
@media (max-width: 480px) {
  .project-topline { flex-wrap: wrap; row-gap: var(--s-1); }
  .project-name { flex: 1 1 100%; }           /* name owns the first row, ellipsised */
  .project-stats { margin-left: 0; }          /* second row starts at the left edge */
  .project-kebab { margin-left: auto; opacity: 1; }  /* no hover on touch → always visible */
}

.empty { color: var(--faint); font-size: var(--t-sm); line-height: 1.55; margin: var(--s-1) var(--s-1); }
/* J13/B3 — the typed refusal's TECHNICAL detail, under the human sentence.
   MEASURED at :4675 before this rule existed: the detail computed to #e6e8ec / 14px while
   the sentence above it was #6e7681 / 12px, because `.empty` is styled and `.ft-empty-detail`
   was not — so the raw internal wording ("…root was bound to is no longer enrolled") rendered
   BRIGHTER AND LARGER than the explanation meant for the reader, and read as the main message.
   It is a secondary aside: quieter and no larger than the sentence it explains, and it wraps
   inside the panel rather than pushing the column (long ids and paths appear in these strings). */
.ft-empty-detail {
  color: var(--faint);
  font-size: var(--t-xs, 11px);
  line-height: 1.5;
  margin: var(--s-1) var(--s-1) 0;
  opacity: .85;
  overflow-wrap: anywhere;
}
/* #90: honest read-only state when a session has no drivable/capturable terminal (mirror). */
.term-noterm { display: flex; flex-direction: column; gap: 6px; padding: var(--s-2, 16px); max-width: 480px; }
.term-noterm-title { color: var(--fg-strong, #ddd); font-size: var(--t-md, 14px); font-weight: 600; }
.term-noterm-sub { color: var(--muted); font-size: var(--t-sm); line-height: 1.6; }

/* #100 interim — project "connected but no live session" empty-state (the #6 data-gap
   reads as connected+waiting, not broken). Centered card + a green link-dot. */
.project-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-2); text-align: center; padding: var(--s-6) var(--s-4); min-height: 60%;
  max-width: 420px; margin: 0 auto;
}
.project-empty .empty-title { font-size: var(--t-md); font-weight: 600; color: var(--fg-strong); margin: 0; }
.project-empty .empty-hint { color: var(--muted); font-size: var(--t-sm); line-height: 1.6; margin: 0; }
.empty-conn {
  display: inline-flex; align-items: center; gap: var(--s-1);
  font-size: var(--t-xs); font-weight: 600; color: var(--green, #4caf50);
  letter-spacing: 0.02em; text-transform: uppercase;
}
.empty-conn-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--green, #4caf50);
  box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.18);
}

/* ════════════════════════ THREAD MIRROR ════════════════════════ */
.thread-card { display: flex; flex-direction: column; }
/* match the extension #thread rhythm (padding 8, gap 8) so the web reads like Cockpit */
.thread-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: var(--s-2) var(--s-3); }
.thread { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
/* Live "thinking…" bubble (30-07): appended at the bottom of the thread while the active agent's pane is
   mid-turn (daemon ActivitySignals, all 4 CLIs). Gives a streaming-feel signal between Send and the
   mirrored reply so the thread is never a silent blank. Left-aligned like an agent bubble; the animated
   dots + live status line ("Deliberating… 32s · ↓1.2k tokens") carry the "it's running" affordance. */
.thread-live {
  display: flex; align-items: center; gap: 8px; align-self: flex-start;
  max-width: 85%; padding: 8px 12px; border-radius: 12px;
  background: rgba(55, 148, 255, 0.10); border: 1px solid rgba(55, 148, 255, 0.22);
  color: var(--muted, #9aa4b2); font-size: 12px; font-family: var(--font-mono, monospace);
}
.thread-live-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.thread-live-dots { display: inline-flex; gap: 3px; flex: none; }
.thread-live-dots i {
  width: 5px; height: 5px; border-radius: 50%; background: rgb(55, 148, 255);
  animation: thread-live-blink 1.2s infinite ease-in-out both;
}
.thread-live-dots i:nth-child(1) { animation-delay: -0.24s; }
.thread-live-dots i:nth-child(2) { animation-delay: -0.12s; }
@keyframes thread-live-blink { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .thread-live-dots i { animation: none; opacity: 0.7; } }
/* Streaming reply bubble (30-07): the assistant's text "bắn chữ" grows in place each frame. A blinking
   caret at the tail signals it is still being typed; it vanishes when the transcript item replaces this. */
.thread-streaming .msg-body { white-space: pre-wrap; }
.stream-caret {
  display: inline-block; width: 7px; height: 1em; margin-left: 2px; vertical-align: text-bottom;
  background: rgb(55, 148, 255); animation: stream-caret-blink 1s step-end infinite;
}
@keyframes stream-caret-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .stream-caret { animation: none; opacity: 0.8; } }
/* I-23 (UC-247/248): jump-to-latest pill — floats bottom-center of the chat column when scrolled up.
   Positioned relative to #shell-col3 (the chat column, position:relative). */
/* #B5 / UC-247 (H11d dc:1794): the "↓ N tin mới" pill floats at the thread's bottom-RIGHT, ABOVE the
   composer — it must NEVER overlap it (the old centered bottom:96px collided with a 2-line composer +
   footer + cli-footer). Right-aligned per the design; the bottom is anchored to the LIVE composer-stack
   height (--composer-stack-h, measured by app.js on every composer resize / footer toggle) + a gap, so
   it always clears the composer no matter how tall it grows. Falls back to a safe 132px pre-measure. */
.thread-jump {
  position: absolute; right: 16px; left: auto; transform: none; z-index: 20;
  bottom: calc(var(--composer-stack-h, 116px) + 16px);
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: var(--s-1) var(--s-3); border-radius: 999px;
  background: var(--accent); color: #04101f; border: none; cursor: pointer;
  font: inherit; font-size: var(--t-xs); font-weight: 600; box-shadow: var(--shadow);
}
.thread-jump[hidden] { display: none; }
.thread-jump:hover { filter: brightness(1.08); }
.thread-jump-n { font-family: var(--font-mono); }

/* one row per ThreadItem (render.js .titem) — card-ish, mirrors extension look */
.titem {
  display: flex; gap: var(--s-2); font-size: var(--t-base); line-height: 1.5;
  align-items: baseline;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-elev);
  /* G5 (mobile @375 card-clip): a card must never exceed its column. Without these, a wide non-wrapping
     child (a code-block with white-space:pre, a long inline token) grew the flex card to its content
     width (467px in a 375px viewport → horizontal clip). Bound the card + let the body flex-shrink. */
  max-width: 100%; min-width: 0; box-sizing: border-box;
}
.titem .role {
  flex: none; min-width: 70px;
  color: var(--muted); font-weight: 600; font-size: var(--t-xs);
  font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.03em;
}
.titem .body { white-space: pre-wrap; word-break: break-word; min-width: 0; }

/* user/assistant read as chat bubbles (extension .user-message right / .assistant left) */
.titem.user, .titem.assistant {
  display: block; max-width: 78%; border: none; padding: var(--s-2) var(--s-3);
}
/* #91e V-VISUAL-1: the user message was a solid bright-accent block filling the
   width ("thread xanh loè"). Cloud9-style instead — a subtle accent TINT + a
   left accent bar, keeping the text in the normal foreground color. Not a loud
   full-bleed fill. */
.titem.user {
  align-self: flex-end;
  background: rgba(55, 148, 255, 0.10);
  color: var(--fg);
  border-left: 3px solid var(--accent);
  border-radius: 2px var(--r-md) var(--r-md) 2px;
}
.titem.user .role { display: none; }
.titem.assistant {
  align-self: flex-start; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-md) var(--r-md) var(--r-md) 2px;
}
.titem.assistant .role { color: var(--green); margin-bottom: var(--s-1); display: block; }
/* tool/command/edit rows read as code-ish cards */
.titem.command, .titem.edit, .titem.read, .titem.search,
.titem.tool, .titem.mcp, .titem.bashOutput { font-family: var(--font-mono); font-size: var(--t-sm); }
.titem.command .role, .titem.edit .role { color: var(--green); }
.titem .stat { color: var(--green); font-size: var(--t-sm); font-weight: 600; }
.titem .wf, .titem .opts { color: var(--muted); font-size: var(--t-sm); }
.attach { max-width: 240px; max-height: 170px; border-radius: var(--r-sm); display: block; margin-top: var(--s-1); border: 1px solid var(--border); }
img.attach[data-lightbox-src] { cursor: zoom-in; }
img.attach[data-lightbox-src]:hover { border-color: var(--accent); }

/* G7 image lightbox: a full-screen dismissable overlay for a zoomed attachment. */
.lightbox-overlay {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.82);
  padding: 40px;
}
.lightbox-img {
  max-width: 92vw; max-height: 92vh;
  border-radius: 8px;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.6);
  cursor: default;
}
.lightbox-close {
  position: absolute; top: 16px; right: 20px;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(20, 22, 28, 0.85);
  color: #e6e8ec; font-size: 15px; line-height: 1;
  border-radius: 8px; cursor: pointer;
}
.lightbox-close:hover { background: rgba(40, 44, 52, 0.95); }

/* ── System events (apiError / compact / wakeup) — mirror chatThreadWebview.ts:1473-1488.
   apiError = a red bordered banner (a visible outage signal so the user doesn't think the
   agent died silently); compact/wakeup = a faint centered divider. */
.titem.system-banner {
  align-self: stretch; display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); padding: var(--s-1) var(--s-3); border-radius: var(--r-sm);
}
.titem.system-error {
  border: 1px solid var(--red); color: var(--red);
  background: rgba(241, 76, 76, 0.08);
}
.titem.system-error .tcard-icon, .system-icon { flex: none; display: inline-flex; color: inherit; }
.titem.system-error .system-text { min-width: 0; word-break: break-word; color: inherit; }
.titem.system-divider {
  align-self: stretch; text-align: center; font-size: var(--t-sm);
  color: var(--muted); opacity: 0.7; padding: var(--s-1) var(--s-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── Detail cards (edit / command) — mirror the extension .card vocabulary ──── */
.titem.tcard { display: block; padding: 0; overflow: hidden; }
/* VIỆC-1 card-click: a card wired to a viewer (edit→diff, read→file) is interactive. */
.tcard[data-card-action] { cursor: pointer; }
.tcard[data-card-action]:hover { background: var(--code-bg); }
.tcard[data-card-action]:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* BUG diff-treo: an out-of-project card is NOT clickable (its file isn't viewable here). */
.tcard[data-extern] { cursor: default; opacity: 0.7; }
.tcard[data-extern] .tcard-file { color: var(--muted); }
/* VIỆC-B (ext parity): on an Edit card the FILEPATH opens the file; the ⇄ button opens the diff. */
.tcard-file[data-card-action] { cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.tcard-file[data-card-action]:hover { color: var(--accent); }
.tcard-file[data-card-action]:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tcard-diff-btn {
  background: none; border: 1px solid var(--border, #3a3a3a); border-radius: var(--r-sm);
  color: var(--muted); cursor: pointer; font: inherit; line-height: 1; padding: 0 var(--s-1);
  margin-left: var(--s-1);
}
.tcard-diff-btn:hover { color: var(--accent); border-color: var(--accent); }
.tcard-diff-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* HYBRID quota action button on a quotaFallback card — a full-width call to action, warmer than the
   inline icon buttons because it is a deliberate account switch, not a passive view. */
.tcard-quota-btn {
  display: inline-block; margin-top: 8px; padding: 6px 12px; border-radius: 8px; cursor: pointer;
  font: inherit; font-weight: 600; font-size: 12px;
  background: rgba(224, 179, 65, 0.14); border: 1px solid rgba(224, 179, 65, 0.4); color: rgb(224, 179, 65);
}
.tcard-quota-btn:hover { background: rgba(224, 179, 65, 0.24); }
.tcard-quota-btn:focus-visible { outline: 2px solid rgb(224, 179, 65); outline-offset: 1px; }
/* Team-message card (PO 30-07): a member↔member message made visible in the thread. A left accent for
   received (←) and a right-shifted, differently-tinted card for sent (→) so the direction reads instantly. */
.teammsg-card { border-left: 3px solid rgba(120, 130, 145, 0.5); }
.teammsg-in { border-left-color: rgb(88, 166, 255); background: rgba(88, 166, 255, 0.06); }
.teammsg-out { border-left-color: rgb(63, 185, 130); background: rgba(63, 185, 130, 0.06); margin-left: 12px; }
/* GAP-DIFF-4: 👁 View-this-agent (sub-agent card) + per-step ⇄ (astep-btn). Same ghost-button style. */
.tcard-view-agent, .astep-btn {
  background: none; border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--muted); cursor: pointer; font: inherit; line-height: 1; padding: 0 var(--s-1);
  margin-left: var(--s-1); flex: none;
}
.tcard-view-agent:hover, .astep-btn:hover { color: var(--accent); border-color: var(--accent); }
.tcard-view-agent:focus-visible, .astep-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.astep-actions { margin-left: auto; display: inline-flex; gap: 4px; }
/* BUG diff-treo: the diff-load error-state + its inline Retry. */
.diff-error { color: var(--muted); }
.link-btn {
  background: none; border: none; padding: 0; margin-left: var(--s-1);
  color: var(--link); cursor: pointer; text-decoration: underline; font: inherit;
}
.link-btn:hover { color: var(--accent); }
.tcard-head {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3); font-size: var(--t-sm);
}
.tcard-icon {
  flex: none; font-family: var(--font-mono); font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; color: var(--green);
}
.command-card.cmd-failed .tcard-icon { color: var(--red); }
/* GAP-DIFF-3 (dc:1445): a background command is a bg-card (amber tint) + a "(background)" tag. */
.command-card.cmd-bg { background: rgba(224, 179, 65, 0.06); border-color: rgba(224, 179, 65, 0.25); }
.cmd-bg-tag { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--orange, #e0b341); font-weight: 500; }
/* G14 / UC-132: %ctx threshold colour — normal inherits; >80% amber; >95% red ("sắp compact"). */
.ctx-pct.ctx-warn { color: var(--orange, #e0b341); }
.ctx-pct.ctx-crit { color: var(--red, #ff6b6b); font-weight: 600; }
/* G13 / UC-207: plan-usage "gần trần" — red chip > 90%. */
.usage-pct.usage-crit, #cli-usage-val.usage-crit { color: var(--red, #ff6b6b); font-weight: 600; }
/* G12 / UC-233: a RUNNING command — amber "running…" tag + pulsing spinner + live last-3 output lines. */
.cmd-running { color: var(--orange, #e0b341); font-family: var(--font-mono); font-size: var(--t-xs); margin-left: 6px; }
.cmd-spinner { display: inline-block; width: 7px; height: 12px; background: var(--orange, #e0b341); vertical-align: middle; animation: cmdPulse 1s step-end infinite; }
@keyframes cmdPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
.cmd-tail { margin-top: 6px; padding: 6px 8px; border-radius: 6px; background: var(--bg-deep, #0b0c0f); border: 1px solid rgba(255, 255, 255, 0.06); font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); line-height: 1.5; }
.cmd-tail-line { white-space: pre; overflow: hidden; text-overflow: ellipsis; }
.command-card.cmd-running-card { border-color: rgba(224, 179, 65, 0.3); }
/* 📄 output-file + ❯_ terminal buttons — ghost buttons (gated on data → never dead). */
.tcard-out-btn, .tcard-term-btn {
  background: none; border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--muted); cursor: pointer; font: inherit; line-height: 1; padding: 0 var(--s-1); margin-left: var(--s-1);
}
.tcard-out-btn:hover, .tcard-term-btn:hover { color: var(--accent); border-color: var(--accent); }
.edit-card .tcard-icon { color: var(--green); }
.tcard-title { font-weight: 600; color: var(--fg-strong); }
.tcard-file {
  font-family: var(--font-mono);
  background: var(--code-bg); padding: 1px var(--s-1); border-radius: 4px;
  color: var(--link); word-break: break-all;
}
/* ST-840: candidate-file card — files a finished turn wrote, each read back from the machine. */
.artifact-card { margin: var(--s-2) 0; padding: var(--s-2) var(--s-3); border: 1px solid var(--border); border-radius: 8px; }
.artifact-head { font-weight: 600; color: var(--fg-strong); }
.artifact-basis { margin: var(--s-1) 0; color: var(--muted); font-size: var(--t-sm); }
.artifact-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.artifact-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); min-width: 0; }
.artifact-path { font-family: var(--font-mono); background: var(--code-bg); padding: 1px var(--s-1); border-radius: 4px; word-break: break-all; min-width: 0; }
.artifact-status { font-size: var(--t-xs); padding: 0 var(--s-1); border-radius: 4px; border: 1px solid var(--border); color: var(--muted); }
.artifact-state { font-size: var(--t-sm); color: var(--muted); }
.artifact-r-missing .artifact-state, .artifact-r-refused .artifact-state, .artifact-r-error .artifact-state { color: var(--red); }
.artifact-code { font-size: var(--t-sm); }
.artifact-more { margin: var(--s-1) 0 0; color: var(--muted); font-size: var(--t-sm); }
.artifact-preview { margin-left: auto; }
.artifact-preview[disabled] { opacity: .5; cursor: not-allowed; }
.artifact-run { margin-bottom: var(--s-2); }
.artifact-run-row { flex-wrap: wrap; }
.artifact-verified .artifact-status { color: var(--green); border-color: var(--green); }
.artifact-changed .artifact-status, .artifact-deleted .artifact-status { color: var(--red); }
.artifact-preview-body { flex-basis: 100%; max-height: 320px; overflow: auto; margin: var(--s-1) 0 0; padding: var(--s-2); background: var(--code-bg); border-radius: 6px; font-family: var(--font-mono); font-size: var(--t-sm); white-space: pre-wrap; word-break: break-word; }
.artifact-preview-note { flex-basis: 100%; margin: var(--s-1) 0 0; color: var(--muted); font-size: var(--t-sm); }
/* W3 (prompt-option reply): clickable answer buttons for AskUserQuestion / plan review. */
.prompt-question { padding: var(--s-2) var(--s-3) 0; color: var(--fg-strong); white-space: pre-wrap; }
.prompt-options { display: flex; flex-direction: column; gap: var(--s-1); padding: var(--s-2) var(--s-3); }
.prompt-opt {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  text-align: left; width: 100%; cursor: pointer;
  background: var(--code-bg); border: 1px solid var(--border, #3a3a3a); border-radius: var(--r-sm);
  padding: var(--s-1) var(--s-2); color: var(--fg-strong); font: inherit;
}
.prompt-opt:hover { border-color: var(--accent); background: rgba(55,148,255,0.12); }
.prompt-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.prompt-opt-label { font-weight: 600; }
.prompt-opt-desc { color: var(--muted); font-size: var(--t-xs); }
.prompt-answered { padding: var(--s-2) var(--s-3); color: var(--green); font-weight: 600; }
/* G15 / UC-243: cross-client attribution when another client answered first. */
.prompt-answered-by { color: var(--muted); font-weight: 400; font-size: var(--t-xs); }
.prompt-card.prompt-sent .prompt-opt { pointer-events: none; opacity: 0.6; }
/* The waiting-question card (renderAwaitBubble): the question, its choices as numbered rows, and an
   "Other" field for the user's own words — answered in the card itself (PO 2026-10-01). */
.live-await-card { border-color: var(--accent); }
.live-await-card .askq-question { padding: var(--s-2) var(--s-3) 0; color: var(--fg-strong); font-weight: 600; white-space: pre-wrap; }
.live-await-card .askq-options { max-width: 760px; gap: 6px; padding: var(--s-2) var(--s-3) var(--s-1); margin: 0; }
.live-await-card .askq-option {
  flex-direction: row; align-items: flex-start; gap: var(--s-2);
  padding: 8px var(--s-2); background: transparent; transition: border-color .12s, background .12s;
}
.live-await-card .askq-option.sel { border-color: var(--accent); background: rgba(55,148,255,0.12); }
.askq-num {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; margin-top: 1px;
  border: 1px solid var(--border, #3a3a3a); color: var(--muted); font-size: var(--t-xs); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.askq-option:hover .askq-num, .askq-option.sel .askq-num { border-color: var(--accent); background: var(--accent); color: #fff; }
.askq-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.askq-other { display: flex; gap: var(--s-1); max-width: 760px; padding: 0 var(--s-3) var(--s-3); }
.askq-other-input {
  flex: 1; min-width: 0; font: inherit; color: var(--fg-strong);
  background: transparent; border: 1px solid var(--border, #3a3a3a); border-radius: var(--r-sm);
  padding: 8px var(--s-2);
}
.askq-other-input::placeholder { color: var(--muted); }
.askq-other-input:focus { outline: none; border-color: var(--accent); }
.askq-other-send {
  flex: none; font: inherit; font-weight: 600; cursor: pointer; color: #fff;
  background: var(--accent); border: 1px solid var(--accent); border-radius: var(--r-sm); padding: 0 var(--s-3);
}
.askq-other-send:hover:not(:disabled) { filter: brightness(1.1); }
.askq-other-send:disabled { opacity: .5; cursor: default; }
.askq-other.sent { opacity: 0.6; }
/* ASK-THE-USER CARDS (PO 30-07) — the agent needs a person: a password, a hand on the desktop, or an
   answer to a CLI's question. They reuse .prompt-opt / .prompt-answered on purpose (same two-way card
   spine, one visual language), so only the parts that are genuinely new are styled here. */
.secreq-card .body, .rc-card .body, .clip-card .body { display: block; }
.secreq-ask, .secreq-what, .rc-why { padding: var(--s-2) var(--s-3) 0; color: var(--fg-strong); }
/* A card still waiting on a human is marked with a left edge — an unanswered ask must be findable while
   scrolling a long thread, not just legible once you reach it. */
.secreq-card.waiting, .rc-card.waiting, .clip-card.waiting { border-left: 2px solid var(--accent); }
.secreq-card.answered { border-left: 2px solid var(--green); }
/* Field chips: which parts of the secret are still empty. ○ pending / ✓ filled — never the value. */
.secreq-fields { display: flex; flex-wrap: wrap; gap: var(--s-1); padding: var(--s-2) var(--s-3); }
.secreq-field {
  font-family: var(--font-mono); font-size: var(--t-xs);
  background: var(--code-bg); border: 1px solid var(--border, #3a3a3a); border-radius: var(--r-sm);
  padding: 1px var(--s-1); color: var(--muted);
}
.secreq-field.filled { color: var(--green); border-color: var(--green); }
.secreq-fill, .rc-open, .clip-answer { margin: 0 var(--s-3) var(--s-2); width: auto; }
/* ONE LOGIN CARD, TWO CHOICES (vault / Remote Desktop): side by side, wrapping on a phone. The buttons
   sit in the row's own padding, so their per-button margin is dropped here. */
.secreq-choices { display: flex; flex-wrap: wrap; gap: var(--s-2); padding: 0 var(--s-3) var(--s-2); }
.secreq-choices .secreq-fill, .secreq-choices .secreq-remote, .secreq-choices .secreq-cancel { margin: 0; width: auto; }
.secreq-choices [disabled] { opacity: .55; cursor: not-allowed; }
.rc-close-error { padding: 0 var(--s-3) var(--s-2); font-size: 12px; color: var(--danger, #e5534b); }
.secreq-target, .secreq-remote-why, .secreq-ident { padding: var(--s-1) var(--s-3) 0; color: var(--muted); font-size: var(--t-xs); }
.secreq-card .rc-handoff { margin: 0 var(--s-3) var(--s-2); }
/* The CLI pane screen: monospace, scrollable, and CAPPED — a pane dump must not push the rest of the
   thread off the screen. Horizontal scroll rather than wrapping, so TUI box-drawing stays aligned. */
.clip-screen {
  margin: var(--s-2) var(--s-3); padding: var(--s-2);
  background: var(--code-bg); border-radius: var(--r-sm);
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--fg-strong);
  max-height: 16em; overflow: auto; white-space: pre;
}
/* The highlighted menu line — the one fact that turns "did my keypress land?" into a measurement. */
.clip-cursor {
  margin: 0 var(--s-3) var(--s-2); padding: 1px var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-xs);
  color: var(--accent); background: rgba(55,148,255,0.10); border-radius: var(--r-sm);
}
/* W1 (tool/command output): the ▾ expand button + the lazy-filled output slot. */
.tcard-expand {
  display: inline-block; margin-top: var(--s-1); background: none; border: 1px solid var(--border, #3a3a3a);
  border-radius: var(--r-sm); color: var(--muted); cursor: pointer; font: inherit; font-size: var(--t-xs);
  padding: 1px var(--s-1);
}
.tcard-expand:hover { color: var(--fg-strong); border-color: var(--accent); }
.tcard-expand:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tcard-output { margin-top: var(--s-1); }
.tcard-output-pre {
  margin: 0; max-height: 360px; overflow: auto; white-space: pre-wrap; word-break: break-word;
  font-family: var(--font-mono); font-size: var(--t-xs); background: var(--code-bg);
  padding: var(--s-1) var(--s-2); border-radius: var(--r-sm); color: var(--fg);
}
.tcard-output-img {
  display: block; max-width: 100%; max-height: 480px; height: auto; margin-top: var(--s-1);
  border-radius: var(--r-sm); border: 1px solid var(--border, rgba(127,127,127,.3));
}
.tcard-output-loading, .tcard-output-empty { color: var(--muted); font-size: var(--t-xs); }
.tcard-output-err { color: var(--red, #f14c4c); font-size: var(--t-xs); }

/* ENVELOPE CARDS (PO 27-07): the CLI plumbing the daemon lifts out of the prose — system reminders,
   boot preambles, slash-command expansions, captured output, tool errors, reasoning. This is
   CONTEXT, not conversation: it stays visually QUIETER than a message bubble (muted text, no accent
   fill, dashed edge) so the eye skips it while reading the chat, and is one click from full text. */
.env-card { border-style: dashed; opacity: 0.9; }
.env-card .tcard-title { color: var(--muted); font-weight: 600; }
.env-preview {
  color: var(--muted); font-size: var(--t-xs); display: block;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.env-body {
  margin: var(--s-1) 0 0; max-height: 320px; overflow: auto; white-space: pre-wrap; word-break: break-word;
  font-family: var(--font-mono); font-size: var(--t-xs); background: var(--code-bg);
  padding: var(--s-1) var(--s-2); border-radius: var(--r-sm); color: var(--fg);
}
/* Per-kind tint on the icon only — enough to tell an error from a reminder at a glance, without
   turning the thread into a colour chart. */
.env-toolError .tcard-icon { color: var(--red, #f14c4c); }
.env-toolError { border-color: var(--red, #f14c4c); }
.env-taskNotification .tcard-icon { color: var(--green, #4ec9b0); }
.env-thinking .tcard-icon, .env-systemNote .tcard-icon { color: var(--muted); }
/* W2 (subagent card expand): the inline step list + preview. */
.subagent-preview { color: var(--muted); font-size: var(--t-xs); margin-top: 2px; white-space: pre-wrap; }
.subagent-steps { margin-top: var(--s-1); display: flex; flex-direction: column; gap: 2px; }
.agent-step {
  font-size: var(--t-xs); color: var(--fg); padding: 2px var(--s-1);
  border-left: 2px solid var(--border, #3a3a3a); padding-left: var(--s-2);
}
.agent-step-verb { font-weight: 600; color: var(--fg-strong); }
.agent-step-text { color: var(--muted); white-space: pre-wrap; }
.agent-step .astep-file { font-family: var(--font-mono); color: var(--link); }
.tcard-meta { margin-left: auto; flex: none; }
.tcard-stats { font-family: var(--font-mono); font-size: var(--t-sm); font-weight: 600; }
/* design dc:1470 — the SendMessage card's 👁 "jump to agent's chat" button (in the header meta slot). */
.tcard-jump {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0;
  border: 1px solid var(--border); background: transparent; color: var(--muted);
  border-radius: 6px; cursor: pointer;
}
.tcard-jump:hover { color: var(--fg-strong); border-color: var(--accent); background: rgba(55, 148, 255, .1); }
.tcard-jump svg { width: 13px; height: 13px; }
.diff-add { color: var(--green); }
.diff-rem { color: var(--red); }
.titem.tcard .body {
  display: block;
  padding: var(--s-2) var(--s-3); border-top: 1px solid var(--border);
  background: var(--code-bg);
}
/* the edit card's body is just the file chip — no code-block backdrop needed */
.edit-card .body { background: transparent; border-top: none; padding: 0 var(--s-3) var(--s-2); }
.tcard-cmd { font-family: var(--font-mono); font-size: var(--t-sm); color: var(--fg); white-space: pre-wrap; word-break: break-word; }
.tcard-badge {
  font-size: var(--t-xs); font-weight: 600; color: var(--muted);
  background: var(--code-bg); padding: 1px var(--s-2); border-radius: 999px;
}
.tcard-desc { color: var(--fg); }
/* SendMessage card summary: the sender's one-liner, shown above the message body (ext parity). */
.msg-card .tcard-summary { color: var(--muted); font-size: 12px; margin-bottom: 4px; font-style: italic; }
/* todo checklist (mirrors the extension's team TaskList) + subagent/workflow body */
.todo-card .body, .subagent-card .body, .workflow-card .body { background: transparent; border-top: none; padding: 0 var(--s-3) var(--s-2); }
.todo-list { list-style: none; margin: var(--s-1) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.todo-item { display: flex; gap: var(--s-2); align-items: baseline; font-size: var(--t-sm); color: var(--fg); }
.todo-mark { flex: none; font-family: var(--font-mono); color: var(--muted); }
.todo-item.todo-completed, .todo-item.todo-done { color: var(--muted); text-decoration: line-through; }
.todo-item.todo-completed .todo-mark, .todo-item.todo-done .todo-mark { color: var(--green); }
.todo-item.todo-in_progress .todo-mark { color: var(--accent); }

/* ── CLI input bar ────────────────────────────────────────────────────────── */
/* §5b composer: a bordered box — the textarea on top, a toolbar row (# 📎 pills + ghost
   send) below. Column layout so the toolbar sits under the input (not beside it). */
/* dc:966 — the composer is a BORDERED BOX (bg #16181d + accent-tinted border + radius 14) inset from
   the column edges, NOT a flat top-bordered strip. The box holds the textarea + toolbar; the cli-footer
   sits above it (outside the box). */
.cli-bar {
  display: flex; flex-direction: column; gap: 9px;
  margin: 0 16px 14px; padding: 12px 12px 9px; flex: none;
  background: #16181d; border: 1px solid rgba(55, 148, 255, 0.35); border-radius: 14px;
}

/* cli-footer (§5b) — compact agent-info strip above the composer. Rendered by app.js;
   mono, muted, single-line. Narrow columns: the middle items shrink+ellipsis FIRST so the
   trailing "manage ↓" + "esc x2" (the actionable/important ones) stay visible (never clipped
   off the right edge — QC B6). gap is small so more fits. */
.cli-footer {
  flex: none; display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-1) var(--s-3); min-width: 0;
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--faint);
  border-top: 1px solid var(--border); background: var(--bg-elev);
  overflow: hidden; white-space: nowrap;
}
/* items are shrinkable + ellipsis; the spacer eats slack; manage/esc are flex:none so they
   are never the ones clipped. The effort/status item shrinks first (min-width:0). */
.cli-footer .cf-item { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cli-footer .cf-manage, .cli-footer .cf-item:last-of-type { flex: none; }
.cli-footer .cf-accent { color: var(--state-running); }
.cli-footer .cf-warn { color: var(--state-thinking); }
/* DESIGN v3 H2 computed: "manage ↓" is a QUIET grey control — rgb(139,144,154) 10px (was accent
   blue 11px, louder than the design). */
.cli-footer .cf-manage { color: rgb(139, 144, 154); font-size: 10px; cursor: pointer; }
.cli-footer .cf-manage:hover { color: rgb(230, 232, 236); }
.cli-footer .cf-spacer { flex: 1 1 auto; }
/* §H10d manage panel (dc.html:1652) — the "manage ↓" control panel (teammates/kick/invite/effort/mode). */
.mp-panel { width: 250px; background: #0e1014; border: 1px solid var(--border); border-radius: 12px; padding: 12px; box-shadow: 0 18px 40px rgba(0,0,0,.5); display: flex; flex-direction: column; gap: 11px; } /* dc:1063 bg #0e1014, not --panel #111318 */
.mp-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.mp-lbl { font-size: 11px; color: var(--fg-strong); }
.mp-toggle { width: 30px; height: 18px; border-radius: 999px; background: var(--border-strong); position: relative; cursor: pointer; flex: none; transition: background .15s var(--ease); }
.mp-toggle.on { background: var(--accent); }
.mp-toggle .mp-knob { position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; transition: left .15s var(--ease); }
.mp-toggle.on .mp-knob { left: 14px; }
.mp-eff-seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: 6px; overflow: hidden; font-family: var(--mono, monospace); font-size: 9px; }
.mp-eff { padding: 3px 7px; color: var(--muted); cursor: pointer; }
.mp-eff + .mp-eff { border-left: 1px solid var(--border-strong); }
.mp-eff.active { background: color-mix(in srgb, var(--state-thinking, #e0b341) 15%, transparent); color: var(--state-thinking, #e0b341); }
.mp-div { height: 1px; background: var(--border); }
.mp-sec { font-size: 9px; font-family: var(--mono, monospace); letter-spacing: .1em; color: var(--muted); }
.mp-mate { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--fg-strong); }
.mp-mate .mp-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mp-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: var(--muted); }
.mp-dot.online { background: var(--state-running, #46d19e); }
.mp-dot.awaiting { background: var(--state-awaiting, #ff6b6b); }
.mp-dot.offline { background: var(--muted); }
.mp-kick { font-size: 10px; color: var(--red, #ff6b6b); cursor: pointer; flex: none; }
.mp-invite { font-size: 11px; color: var(--accent); cursor: pointer; }
.mp-empty { font-size: 11px; color: var(--muted); }
/* §5b Chat|CLI segmented (dc.html:915) — a bordered pill; the active half is accent-washed. */
.cli-footer .cf-mode-seg { flex: none; display: inline-flex; border: 1px solid var(--border-strong); border-radius: 7px; overflow: hidden; }
.cli-footer .cf-mode { padding: 2px 9px; cursor: pointer; color: var(--muted); }
.cli-footer .cf-mode + .cf-mode { border-left: 1px solid var(--border-strong); }
.cli-footer .cf-mode.active { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); font-weight: 600; }
/* UC-213/214/215 — composer disabled when the active agent exited / its producer is offline. */
.cli-bar.cli-unavailable { opacity: 0.55; }
.cli-bar.cli-unavailable .cli-input { cursor: not-allowed; }
.cli-bar.cli-unavailable .cli-send-ghost, .cli-bar.cli-unavailable .cli-tool { pointer-events: none; opacity: 0.6; }
/* #235 — the control-lock ESCAPE HATCH. It lives INSIDE .cli-bar, which is dimmed to 0.55 and whose
   controls go pointer-events:none while unavailable — so the one button that FIXES the unavailable
   state would itself have been dimmed and dead (the exact dead-button trap this bug is about). Undo
   both explicitly: full opacity, pointer events restored. */
.cli-control-banner { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 10px; margin-bottom: 6px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 10%, transparent); font-size: 12px; }
.cli-bar.cli-unavailable .cli-control-banner { opacity: 1; pointer-events: auto; }
.cli-control-who { color: var(--fg-muted, inherit); flex: 1 1 auto; min-width: 0; }
.cli-control-btn { flex: 0 0 auto; cursor: pointer; pointer-events: auto; opacity: 1;
  border: 1px solid var(--accent); color: var(--accent); background: transparent; border-radius: 999px;
  padding: 2px 10px; font-size: 12px; font-weight: 600; }
.cli-control-btn:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.cli-control-btn:disabled { opacity: 0.5; cursor: default; }
/* B9: a READ-ONLY mirror is a NORMAL state (an all-mirror env). Keep the composer FULLY VISIBLE (no
   0.55 dim that reads as "lost") — just a subtle read-only cue: send/tools inert + the input disabled
   + muted, but the frame stays clear so the user sees it's present, only read-only. */
.cli-bar.cli-readonly { opacity: 1; }
.cli-bar.cli-readonly .cli-input { cursor: default; color: var(--muted); }
.cli-bar.cli-readonly .cli-send-ghost, .cli-bar.cli-readonly .cli-tool { pointer-events: none; opacity: 0.5; }
/* UC-212 Send → Stop (dc.html:1797): the ■ Stop shows only while the agent streams; Send hides then. */
.cli-stop-btn { width: 34px; height: 34px; border: 1px solid color-mix(in srgb, var(--red, #ff6b6b) 40%, transparent); background: color-mix(in srgb, var(--red, #ff6b6b) 10%, transparent); color: var(--red, #ff6b6b); border-radius: 9px; display: none; place-items: center; flex: none; cursor: pointer; }
.cli-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; display: block; }
.cli-bar.cli-streaming .cli-stop-btn { display: grid; }
.cli-bar.cli-streaming .cli-send-ghost { display: none; }

/* composer toolbar row */
.cli-toolbar { display: flex; align-items: center; gap: var(--s-2); }
.cli-toolbar-spacer { flex: 1 1 auto; }
.cli-tool {
  flex: none; width: 28px; height: 28px; padding: 0; border-radius: var(--r-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; color: var(--muted); cursor: pointer;
  font-size: 15px; font-weight: 600;
}
.cli-tool:hover { color: var(--fg-2); background: rgba(255, 255, 255, 0.05); }
/* VM CHIP (PO 23-07 "user có 2 VM thì ghi thêm số 2"): the machine icon carries a small count badge,
   and turns amber when one of those machines is asleep — the usual reason the agent can't be reached.
   Anchor for the popover that lists them. */
/* #159: the popover anchors here (a wrapper, not the <button>) so it can be a sibling of the chip. */
.cli-vms-anchor { position: relative; display: inline-flex; flex: none; }
.cli-vms-anchor:has(> .cli-vms[hidden]) { display: none; }
.cli-vms { position: relative; }
.cli-vms.has-suspended { color: var(--warn, #d9a441); }
.cli-vms-count {
  position: absolute; top: 1px; right: 0;
  min-width: 13px; height: 13px; padding: 0 3px;
  border-radius: 999px; background: var(--panel-2); border: 1px solid var(--border);
  font-family: var(--font-mono); font-size: 9px; line-height: 11px; color: inherit;
}
/* The machine list. Opens UPWARD (the composer sits at the bottom of the cockpit). */
.cli-vms-pop {
  position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;
  min-width: 232px; max-width: 320px; padding: 6px;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .45);
}
.cli-vms-head { font-size: 10px; color: var(--muted); padding: 2px 6px 6px; }
.cli-vms-row { display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 6px; }
.cli-vms-row:hover { background: rgba(255, 255, 255, .04); }
.cli-vms-name { font-size: 12px; font-weight: 600; color: #e6e8ec; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cli-vms-tag { flex: none; font-family: var(--font-mono); font-size: 9px; color: #8b909a; border: 1px solid rgba(255,255,255,.12); border-radius: 5px; padding: 1px 5px; }
.cli-vms-state { flex: 1; text-align: right; font-family: var(--font-mono); font-size: 10px; color: var(--muted); }
.cli-vms-wake {
  flex: none; margin-left: auto; padding: 3px 8px;
  font-family: inherit; font-size: 10px; font-weight: 600;
  color: #3794ff; background: rgba(55,148,255,.1);
  border: 1px solid rgba(55,148,255,.4); border-radius: 6px; cursor: pointer;
}
.cli-vms-wake:hover { background: rgba(55,148,255,.18); }
.cli-vms-wake[disabled] { opacity: .55; cursor: default; }
.cli-pill {
  flex: none; display: inline-flex; align-items: center; gap: 4px;
  height: 26px; padding: 0 var(--s-2); border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--border);
  /* DESIGN v3 H2 computed: composer pills (usage/model) are quiet grey rgb(139,144,154) 10px
     (was fg-2 196,200,206 11px — brighter than the design). */
  color: rgb(139, 144, 154); font-size: 10px; cursor: pointer;
}
.cli-pill:hover { border-color: var(--border-strong); color: rgb(230, 232, 236); }
.cli-model-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--state-running); }
/* Send button = GHOST outline paper-plane (invariant #3): transparent, muted, NOT filled. */
.cli-send-ghost {
  flex: none; width: 30px; height: 30px; padding: 0; border-radius: var(--r-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; color: var(--muted); cursor: pointer;
}
.cli-send-ghost:hover { color: var(--accent); }
.cli-send-ghost:active { transform: scale(0.96); }
.cli-send-ghost svg { display: block; }
/* #192: empty/whitespace-only #cli-input — submitCli() already no-ops (text.trim() guard), the button
   must LOOK inert too (was clickable/highlighted regardless of content). Mirrors the .cli-unavailable/
   .cli-locked dim treatment above; :disabled also blocks the native click + keyboard activation. */
.cli-send-ghost:disabled { pointer-events: none; opacity: 0.4; cursor: default; }
.cli-send-ghost:disabled:hover { color: var(--muted); }
/* §7 chatbox lock: while a terminal tab is active the composer stays FULLY VISIBLE (opacity:1, clear
   frame) — the earlier 0.5-dim read as "chatbox gone" (PO). Only the input is muted + a hint shown; the
   send/tools go inert. Same clear-read-only treatment as a mirror (.cli-readonly, B9). */
.cli-bar.cli-locked { opacity: 1; }
.cli-bar.cli-locked .cli-input { cursor: default; color: var(--muted); }
.cli-bar.cli-locked .cli-send-ghost, .cli-bar.cli-locked .cli-tool { pointer-events: none; opacity: 0.5; }
/* UC-201 drag-drop attach: highlight the composer box while a file hovers over it. */
.cli-bar.cli-dragover { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent); }
/* WEB-PARITY chatbox#3: a multiline textarea (ext = textarea rows=3) — Enter sends,
   Shift-Enter newlines; auto-grows (JS) up to a cap then scrolls. Rounded-rect (not a
   pill) so multiple lines read cleanly; resize off (the JS sizes it). */
.cli-input {
  /* flex:none (NOT flex:1) — in the column .cli-bar, flex:1 made flexbox compute the height
     from the flex-basis (38px) and IGNORE the inline height autoGrowCli sets → the composer
     never grew on multi-line (GAP-5). flex:none honours the explicit height. width:100% so it
     still fills the row. */
  /* dc:967 — the textarea sits INSIDE the .cli-bar box (which owns the border/bg), so it is
     transparent + borderless; no padding/radius of its own (the box pads). */
  flex: none; width: 100%; padding: 2px 2px 0; border: none; background: transparent;
  resize: none; min-height: 44px; max-height: 116px; overflow-y: auto;
  font-family: inherit; line-height: 1.62; color: var(--fg-strong);
}
.cli-input:focus { outline: none; box-shadow: none; }
/* W6/W7: the chatbox autocomplete popup (/ slash + @ mention) — floats above the textarea. */
.cli-autocomplete {
  /* dc:1650-1680 (H10a slash menu) LITERALS — bg #0e1014 (not --panel #111318), radius 12 (not
     --r-md 10), pad 8/11 (not --s-1 4). Element-scoped; global tokens untouched. */
  max-height: 280px; overflow-y: auto; background: #0e1014;
  border: 1px solid var(--border-strong, #454545); border-radius: 12px;
  box-shadow: var(--shadow, 0 4px 16px rgba(0,0,0,0.4)); padding: 8px 11px;
}
.cli-ac-item {
  /* dc:1655 — slash-menu row: pad 7/9, radius 7, label font 10 (not --s-1/--s-2, --r-sm 8, --t-sm). */
  display: flex; align-items: baseline; gap: var(--s-2); padding: 7px 9px;
  border-radius: 7px; cursor: pointer; font-size: 10px;
}
.cli-ac-item.active, .cli-ac-item:hover { background: var(--accent, #0b6bcb); color: #fff; }
.cli-ac-label { font-family: var(--font-mono); font-weight: 600; }
.cli-ac-desc { color: var(--muted); font-size: var(--t-xs); margin-left: auto; }
.cli-ac-item.active .cli-ac-desc, .cli-ac-item:hover .cli-ac-desc { color: rgba(255,255,255,0.8); }
/* UC-196 loading skeleton — shown while the live slash-set is in flight (before it replaces the curated set). */
.cli-ac-skel { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; }
.cli-ac-skel-row { height: 12px; border-radius: 4px; background: linear-gradient(90deg, rgba(255,255,255,.06), rgba(255,255,255,.12), rgba(255,255,255,.06)); background-size: 200% 100%; animation: cliAcSkel 1.2s ease-in-out infinite; }
.cli-ac-skel-row:nth-child(2) { width: 80%; }
.cli-ac-skel-row:nth-child(3) { width: 60%; }
@keyframes cliAcSkel { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
/* #100 X-1 — the 2-stage send-ack pill (pending → sent / failed). */
.cli-ack {
  flex: none; font-size: var(--t-xs); padding: 2px var(--s-2); border-radius: 999px;
  white-space: nowrap; color: var(--muted); background: var(--code-bg);
}
.cli-bar[data-ack="pending"] .cli-ack, .cli-bar[data-ack="queued"] .cli-ack, .cli-bar[data-ack="waking"] .cli-ack, .cli-bar[data-ack="collected"] .cli-ack { color: var(--accent); }
.cli-bar[data-ack="sent"] .cli-ack { color: var(--green); }
.cli-bar[data-ack="failed"] .cli-ack { color: var(--red, #e06c75); background: color-mix(in srgb, var(--red, #e06c75) 14%, transparent); }
.cli-send {
  flex: none; width: 36px; height: 36px; padding: 0; border-radius: 50%;
}
.cli-send svg { display: block; }
.cli-send:hover { transform: scale(1.05); }
.cli-send:active { transform: scale(0.97); }

/* ── scrollbars ───────────────────────────────────────────────────────────── */
.thread-scroll::-webkit-scrollbar, .col-side::-webkit-scrollbar { width: 9px; }
.thread-scroll::-webkit-scrollbar-thumb, .col-side::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 9px; border: 2px solid transparent; background-clip: content-box;
}
.thread-scroll::-webkit-scrollbar-thumb:hover, .col-side::-webkit-scrollbar-thumb:hover { background: var(--faint); background-clip: content-box; }

/* ════════════════════════ RESPONSIVE ════════════════════════ */
@media (max-width: 480px) {
  .projects-view { padding: var(--s-3) var(--s-3) var(--s-4); }
  .appbar { padding: 0 var(--s-3); }
  .appbar-title { font-size: var(--t-base); max-width: 50vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .screen-title { font-size: var(--t-lg); }
  /* chat bubbles get wider on a phone; the tool/command card rows go single-column */
  .titem.user, .titem.assistant { max-width: 94%; }
  .titem:not(.user):not(.assistant):not(.tcard) { flex-direction: column; gap: var(--s-1); padding: var(--s-2) var(--s-2); }
  .titem:not(.user):not(.assistant):not(.tcard) .role { min-width: 0; }
  /* tighter tab bar + chatbox on a phone */
  .tabbar { padding: var(--s-1) var(--s-2) 0; }
  .tab { padding: var(--s-2) var(--s-2); font-size: var(--t-sm); }
  .thread-scroll { padding: var(--s-2) var(--s-2); }
  .cli-bar { padding: var(--s-2) var(--s-2); }
  .project-card, li.project { padding: var(--s-3) var(--s-3); }
  li.agent .host { display: none; }
  .login-card { padding: var(--s-5) var(--s-4) var(--s-4); }
}

/* ════════════════════════ P-web-view-1 (PU-1/2/3) ════════════════════════ */

/* ── PU-1: project-list Q-A enrichment ──────────────────────────────────── */
.project-glyph { align-items: center; gap: var(--s-1); }
.project-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.project-dot.online { background: var(--green); box-shadow: 0 0 0 2px color-mix(in srgb, var(--green) 22%, transparent); }
.project-dot.offline { background: var(--faint); }
li.project.offline { opacity: 0.82; }
.project-stats { font-size: var(--t-xs); color: var(--muted); display: inline-flex; align-items: center; gap: var(--s-1); }
.project-stats.offline { color: var(--faint); }
.project-preview {
  font-size: var(--t-xs); color: var(--muted); font-style: italic;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.project-open { margin-left: var(--s-1); padding: var(--s-1) var(--s-2); font-size: var(--t-xs); }

/* ── PU-2: cockpit tabbar (kind colors + busy pulse) ────────────────────── */
/* per-kind tab accent (the active tab's border-TOP color, unless a state overrides it below). */
.tab.kind-claude { --tab-accent: var(--accent); }
.tab.kind-tmux-agent, .tab.kind-tmux-agent-killed { --tab-accent: #c08aff; }
.tab.kind-harness, .tab.kind-harness-agent { --tab-accent: #6dd58c; }
.tab.kind-pending { --tab-accent: var(--faint); }
/* G: an awaiting tab's accent is ALWAYS red (needs-you), overriding the kind color. */
.tab.awaiting { --tab-accent: #ff6b6b; }
.tab.busy .tab-label { color: var(--fg-strong); }
@keyframes tabpulse { 0%,100% { opacity: 0.35; } 50% { opacity: 1; } }
/* G-14: the ⚠ awaiting badge — red (prototype #ff6b6b), not orange. */
.tab-awaiting { flex: none; color: #ff6b6b; font-weight: 700; }
.tab-term:focus-visible, .tab-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* G-17: the tab being dragged (drag-reorder) is dimmed so the drop target reads clearly. */
.tab.tab-dragging { opacity: 0.4; }

/* ── PU-2: agent chip-list (●live ◐busy ○killed · 🤖 leader) ─────────────── */
.agent-list { list-style: none; margin: var(--s-1) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
/* dc:1467 — agent-row (mention/list) literals: pad 8/11, radius 11, font 11 (not --s-1/--s-2 4/8,
   --r-sm 8, --t-sm 12). The popover use (.agent-row.cp-row) overrides pad/radius to its own dc:1109. */
.agent-row { display: flex; align-items: center; gap: var(--s-2); padding: 8px 11px; border-radius: 11px; font-size: 11px; }
.agent-row:hover { background: var(--panel-2); }
.agent-state { font-size: var(--t-xs); flex: none; }
.agent-row.live .agent-state { color: var(--green); }
.agent-row.busy .agent-state { color: var(--orange, #e6a23c); animation: tabpulse 1s ease-in-out infinite; }
.agent-row.killed .agent-state { color: var(--faint); }
.agent-row.killed .agent-name { color: var(--faint); text-decoration: line-through; }
.agent-name { font-weight: 600; color: var(--fg-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agent-type { font-size: var(--t-xs); color: var(--muted); flex: none; }
/* #100 parity: the robot SVG icon, tinted per kind (leader green / harness+tmux
   magenta / bg accent / killed faint) — same color scheme as the extension. */
.agent-icon { flex: none; display: inline-flex; align-items: center; }
.agent-icon svg { width: 15px; height: 15px; display: block; }
.agent-icon.agent-leader { color: var(--green, #4caf50); }
.agent-icon.agent-harness, .agent-icon.agent-tmux { color: var(--magenta, #c586c0); }
.agent-icon.agent-bg { color: var(--accent, #569cd6); }
.agent-icon.agent-orphan { color: var(--orange, #d7a45a); }
.agent-icon.agent-killed { color: var(--faint); }
.agent-row { cursor: pointer; }
.agent-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.agent-leader, .agent-bot { font-size: var(--t-sm); flex: none; }

/* ── PU-2: diff chip ────────────────────────────────────────────────────── */
.chip-diff .chip-count { background: color-mix(in srgb, var(--orange, #e6a23c) 22%, transparent); }

/* ── PU-2: footer bar (model · usage · @mention) ────────────────────────── */
.footer-bar {
  display: flex; align-items: center; gap: var(--s-1); flex-wrap: wrap;
  padding: var(--s-1) var(--s-3); font-size: var(--t-xs); color: var(--muted);
  border-top: 1px solid var(--border); background: var(--panel);
}
.footer-model { font-weight: 600; color: var(--fg-strong); }
.footer-usage { color: var(--muted); }
.footer-sep { color: var(--faint); }
.footer-mentions { margin-left: auto; color: var(--accent); font-weight: 600; }

/* ── PU-3: responsive — ≥720 grid+side rhythm · <720 stack ──────────────── */
@media (min-width: 720px) {
  .projects-grid {
    /* auto-FIT (not auto-fill): empty tracks COLLAPSE so a lone card fills the full width and
       stays flush with the full-width stat strip above it (QC: a single card looked narrow +
       left-aligned because auto-fill kept an empty 280px track). */
    display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--s-2); align-items: start;
  }
  .projects-grid li.project, .projects-grid .project { border: 1px solid var(--border); border-radius: var(--r-lg); } /* GAP-15: 14, not 10 */
  .projects-grid li.project + li.project { border-top: 1px solid var(--border); }
}
@media (max-width: 719px) {
  /* PU-3m: thread leads on mobile — the chip-row collapses into the ☰ bottom-sheet
     (the agents/diff/todo move there), so force-hide the inline chip-row regardless
     of its `hidden` attr, and reveal the ☰ toggle. */
  .tabbar { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; }
  .tab { flex: none; }
  .agent-list { gap: 1px; }
  .footer-bar { padding: var(--s-1) var(--s-2); }
  /* mobile tap targets ≥44px on actionable rows (a11y) */
  li.project, .project-open, .agent-row, .appbar-sheet, #sheet-close { min-height: 44px; }
  #chip-row { display: none !important; }   /* moved into the sheet on mobile */
  #rail { display: none !important; }       /* #85: rail is desktop-only; ☰ sheet on mobile */
  .appbar-sheet:not([hidden]) { display: inline-flex; }  /* show ☰ on mobile */
}
/* GAP-WEB-3 (WCAG 2.5.5): ≥44px tap targets on ANY touch device regardless of width.
   The <720 rule above missed touch TABLETS (iPad portrait 768, Surface) where width≥720
   but input is touch → tabs/rows were ~23px. `pointer:coarse` keys off touch, not width,
   so it covers touch-tablets without enlarging mouse-driven desktop controls. */
@media (pointer: coarse) {
  li.project, .project-open, .agent-row, .tab { min-height: 44px; }
  /* GAP-WEB-4: hover-reveal doesn't exist on touch → keep ✕ always visible + ≥44px. */
  .project-remove { opacity: 1; width: 44px; height: 44px; }
  /* #87 AC-8: ✎ rename also always-visible + ≥44px on touch. */
  .project-rename { opacity: 1; width: 44px; height: 44px; }
}
@media (min-width: 720px) {
  .appbar-sheet { display: none !important; }  /* desktop: ☰ hidden, chip-row inline */
}

/* ════════════════ #85 DESKTOP cockpit — full-width, multi-pane ════════════════
   The web has width the extension sidebar lacks → use it. Three tiers:
   - <720   = mobile stack + ☰ sheet (above, unchanged).
   - 720–1023 = full-width single column; #chip-row = compact top strip; no rail.
   - ≥1024  = 2-pane cockpit: thread (main) + #rail (~300px, expanded agents/diff/todo);
              chip-row + ☰ sheet hidden (rail replaces). full-width, no 1000px clamp. */

/* #85 view-switch (Chat/Diff/Live segmented control). */
.view-switch {
  display: flex; gap: var(--s-1); flex: none; padding: var(--s-1) var(--s-3);
  background: var(--bg-elev); border-bottom: 1px solid var(--border);
}
.vs-btn {
  font: inherit; font-size: var(--t-sm); font-weight: 600;
  padding: var(--s-1) var(--s-3); border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--s-1);
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.vs-btn:hover { color: var(--fg-strong); background: var(--panel-2); }
.vs-btn.active { color: var(--fg-strong); background: var(--panel-2); border-color: var(--border); }
.vs-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* the 3 main-panes share the thread region; the view-switch toggles [hidden]. */
/* TWO SCROLLERS, ONE STICKY HEADER — the layout bug measured 2026-09-18 (PO screenshot:
   the [⇆ Split | ≡ Inline] toggle floating in the middle of the pane, over the code).
   `.diff-view` scrolled AND `.diff-body` inside it scrolled, so the toolbar's `sticky
   top:0` pinned to the OUTER scroller while the rows moved under the INNER one: the
   toolbar detached from the content it belongs to and came to rest wherever the outer
   scroll happened to be. A pane with one scroller cannot do that, so the diff view is now
   a flex column that does not scroll itself — the header sits, the body scrolls. */
.diff-view { display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
/* BUG-5: the terminal pane is a flex column — the frame scrolls, the input bar pins to the bottom. */
/* §7 Terminal view: read-only frame + a 288px input rail. Side-by-side WHEN there is room
   (col-2 wide / fullscreen), but the rail WRAPS below the mirror when col-2 is too narrow
   (the default 3-col Viewer is ~330px) so the read-only stream is NEVER crushed to a sliver
   (QC B4). flex-wrap + a min-width floor on the mirror is the wrap trigger. */
.live-view { display: flex; flex-flow: row wrap; min-height: 0; min-width: 0; align-content: flex-start; }
.live-view[hidden] { display: none; }
.live-main { flex: 1 1 340px; min-width: min(100%, 340px); display: flex; flex-direction: column; position: relative; height: 100%; min-height: 0; }
.live-ro-badge {
  position: absolute; top: var(--s-2); right: var(--s-2); z-index: 2;
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .1em;
  padding: 2px 6px; border-radius: var(--r-sm); color: var(--faint);
  background: var(--panel-2); border: 1px solid var(--border);
}
.live-foot {
  flex: none; padding: var(--s-1) var(--s-3); font-family: var(--font-mono);
  font-size: var(--t-xs); color: var(--faint); border-top: 1px solid var(--border); text-align: center;
}
/* H12e (UC-362): terminal-exited banner — red-tint bar at the bottom of the live terminal
   (design dc:2122). Sits below the frame; scrollback stays for reading. */
.term-exit {
  flex: none; display: flex; align-items: center; gap: 9px;
  padding: 8px 12px; font-family: var(--font-mono); font-size: 11px;
  background: rgba(255,107,107,.08); border-top: 1px solid rgba(255,107,107,.3);
}
.term-exit[hidden] { display: none; }
.term-exit-dot { color: #ff6b6b; font-size: 11px; }
.term-exit-label { color: #ff9b9b; }
.term-exit-acts { margin-left: auto; display: flex; gap: 8px; }
.term-exit-acts button { background: transparent; border: none; font-family: var(--font-mono); font-size: 11px; cursor: pointer; padding: 0; }
.term-exit-respawn { color: #3794ff; }
.term-exit-respawn:hover { text-decoration: underline; }
.term-exit-close { color: #8b909a; }
.term-exit-close:hover { color: #c4c8ce; }
.term-frame { flex: 1; min-height: 0; min-width: 0; overflow: hidden; }
/* xterm mounts INTO #term-frame. The emulator's root (.xterm) must FILL the frame's full height/width or the
   FitAddon measures a short box → the terminal renders at ~11 rows and only covers half the panel. Stretch it
   to the container and let xterm own its own scrolling (.xterm-viewport), so overflow:auto above never adds a
   second scrollbar competing with xterm's. */
/* xterm fills the frame WITHOUT position:absolute (which pulled it out of flow and broke the layout on resize
   + made FitAddon mis-measure). height:100% on the in-flow element lets the FitAddon read the real box. */
.term-frame > .xterm { width: 100%; height: 100%; }
/* L-12 (UC-352): ⌘F scrollback find-bar — floats top-left over the frame (clear of the
   READ-ONLY badge top-right). Hidden until ⌘F. */
.term-find {
  position: absolute; top: var(--s-2); left: var(--s-2); z-index: 3;
  display: flex; align-items: center; gap: 4px;
  padding: 4px 6px; border-radius: var(--r-sm);
  background: var(--panel-2); border: 1px solid var(--border-strong);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .35);
}
.term-find[hidden] { display: none; }
.term-find-input {
  width: 160px; height: 24px; padding: 0 8px;
  font-family: var(--font-mono); font-size: 11px;
  color: var(--fg-strong); background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--r-sm); outline: none;
}
.term-find-input:focus { border-color: var(--accent); }
.term-find-count { font-family: var(--font-mono); font-size: 10px; color: var(--faint); min-width: 30px; text-align: center; }
.term-find-btn {
  width: 22px; height: 22px; display: grid; place-items: center;
  font-size: 11px; color: var(--muted); cursor: pointer;
  background: transparent; border: 1px solid var(--border); border-radius: var(--r-sm);
}
.term-find-btn:hover { color: var(--fg-strong); border-color: var(--border-strong); background: var(--bg); }
.term-match { background: rgba(224, 179, 65, .30); color: inherit; border-radius: 2px; }
.term-match.current { background: var(--accent); color: #fff; }
/* right input rail — sits at 288px beside the mirror when there's room; grows to fill the
   line (border moves to the top) when it WRAPS below a narrow mirror. */
.term-input-rail {
  flex: 1 1 288px; min-width: 260px; display: flex; flex-direction: column; gap: var(--s-2);
  padding: var(--s-3); border-left: 1px solid var(--border); background: var(--bg-elev);
}
/* when wrapped onto its own line (full width), the left border reads wrong → top border. */
@supports (width: min(1px, 1px)) {
  .term-input-rail { border-left: 1px solid var(--border); }
}
.term-rail-head { font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.term-rail-agent { color: var(--fg-2); }
.term-mode-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; align-self: flex-start; }
.term-mode-btn { padding: var(--s-1) var(--s-3); font-size: var(--t-sm); background: var(--panel); color: var(--muted); border: none; cursor: pointer; }
.term-mode-btn + .term-mode-btn { border-left: 1px solid var(--border); }
.term-mode-btn.active { background: var(--accent); color: #04101f; }
.term-rail-hint { margin: 0; font-size: var(--t-xs); color: var(--faint); line-height: var(--lh-base); }

/* GAP-8b: the parity-button strip — clickable controls when the live frame shows an interactive
   prompt (permission menu / yes-no / arrow-menu / reverse-search). Empty → no box (no dead UI). */
.term-controls:empty { display: none; }
.termctl {
  display: flex; flex-direction: column; gap: var(--s-1);
  padding: var(--s-2); border: 1px solid var(--accent); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.termctl-title { font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: .1em; text-transform: uppercase; color: var(--accent); }
.termctl-btns { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.termctl-btn {
  padding: var(--s-1) var(--s-2); min-height: 32px; font-size: var(--t-sm);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--panel); color: var(--fg); cursor: pointer;
  font-family: var(--font-mono); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.termctl-btn:hover { border-color: var(--accent); color: var(--accent); }
.termctl-btn.termctl-sel { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; color: var(--accent); }
@media (pointer: coarse) { .termctl-btn { min-height: 44px; } }
.term-input-bar {
  flex: none; display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-1) 0;
}
.term-input-prompt { color: var(--accent); font-family: var(--font-mono); flex: none; }
.term-input {
  flex: 1; border: 1px solid var(--border, #3a3a3a); border-radius: var(--r-sm);
  background: var(--code-bg); color: var(--fg); font-family: var(--font-mono); font-size: var(--t-sm);
  padding: var(--s-1) var(--s-2);
}
.term-input:focus { outline: none; border-color: var(--accent); }
.term-input-ack { flex: none; font-size: var(--t-xs); }
.term-input-ack[data-state="pending"] { color: var(--accent); }
.term-input-ack[data-state="failed"] { color: var(--red, #f14c4c); }

/* #85 SCREEN-1 diff-view: file-list + hunks. */
.diff-layout { display: flex; min-height: 0; height: 100%; }
.diff-files { list-style: none; margin: 0; padding: var(--s-1); flex: none; width: 200px; overflow-y: auto; border-right: 1px solid var(--border); }
.diff-file { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-1) var(--s-2); border-radius: var(--r-sm); cursor: pointer; font-size: var(--t-sm); font-family: var(--font-mono); min-height: 28px; }
.diff-file:hover { background: var(--code-bg); }
.diff-file:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.diff-file:hover { background: var(--panel-2); }
.diff-file.active { background: var(--panel-2); border-left: 2px solid var(--accent); }
.diff-file-status { color: var(--yellow); font-weight: 700; width: 14px; flex: none; }
.diff-file-path { color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.diff-add-n { color: var(--green); font-size: var(--t-xs); flex: none; }
.diff-del-n { color: var(--red); font-size: var(--t-xs); flex: none; }
.diff-hunks { flex: 1; overflow: auto; padding: var(--s-2) var(--s-2); font-family: var(--font-mono); font-size: var(--t-sm); }
.diff-file-title { font-weight: 700; color: var(--fg-strong); margin: var(--s-2) 0 var(--s-1); }
.hunk-head { color: var(--muted); background: var(--code-bg); padding: var(--s-1) var(--s-1); margin: var(--s-1) 0 var(--s-1); border-radius: 3px; }
.diff-line { white-space: pre-wrap; padding: 0 var(--s-1); }
.diff-line .diff-sign { display: inline-block; width: 1ch; opacity: 0.7; }
.diff-line.add { background: rgba(76, 175, 80, 0.12); color: var(--green); }
.diff-line.del { background: rgba(241, 76, 76, 0.12); color: var(--red); }
.diff-line.ctx { color: var(--fg); }

/* #85 SCREEN-3 live terminal grid (monospace + cursor). */
/* #100 #3 — render the live tmux frame to LOOK like the claude CLI: a monospace grid
   with NO reflow + TIGHT line-height so box-drawing glyphs (─│╭╮╰╯├┤) connect into
   continuous lines (1.35 left vertical gaps that broke the boxes). tab-size + nowrap +
   ligatures-off keep columns aligned exactly as the tty laid them out. */
.term-grid {
  margin: 0; padding: var(--s-2) var(--s-3);
  font-family: var(--font-mono); font-size: var(--t-sm);
  line-height: 1.2;                 /* tight — box glyphs touch top-to-bottom */
  color: var(--fg); background: var(--bg);
  white-space: pre;                 /* NO reflow — the tty already wrapped */
  tab-size: 8; -moz-tab-size: 8;
  font-variant-ligatures: none; font-feature-settings: "liga" 0, "calt" 0;
  overflow: auto; min-height: 0;
}
.term-row { min-height: 0; height: 1.2em; }   /* one tty row = one line, no extra gap */
/* #100 #3 ANSI SGR colors (parsed by ansiLineToHtml) — the 8 standard + bright + bold,
   mapped to terminal-ansi-ish hues so the live frame reads like the CLI. */
.a-bold { font-weight: 700; }
/* VIỆC-2: ANSI SGR dim/italic/underline (256-color + truecolor render via inline style). */
.a-dim { opacity: 0.6; }
.a-italic { font-style: italic; }
.a-underline { text-decoration: underline; }
.a-fg-k { color: #555; } .a-fg-r { color: #e06c75; } .a-fg-g { color: #98c379; }
.a-fg-y { color: #e5c07b; } .a-fg-b { color: #61afef; } .a-fg-m { color: #c678dd; }
.a-fg-c { color: #56b6c2; } .a-fg-w { color: #cdd3de; }
.a-fg-K { color: #7f848e; } .a-fg-R { color: #ff7b86; } .a-fg-G { color: #b5e08c; }
.a-fg-Y { color: #f0d399; } .a-fg-B { color: #7cc5ff; } .a-fg-M { color: #d99ae8; }
.a-fg-C { color: #76d0db; } .a-fg-W { color: #ffffff; }
.a-bg-k { background: #2a2a2a; } .a-bg-r { background: #5a2a2e; } .a-bg-g { background: #2e4a2a; }
.a-bg-y { background: #4a432a; } .a-bg-b { background: #2a3a52; } .a-bg-m { background: #442a4a; }
.a-bg-c { background: #2a4448; } .a-bg-w { background: #44484f; }

/* GAP-1 col-2 EDITOR — an EDITABLE file view: a textarea overlaid on a line-number gutter,
   with a real Save (daemon saveFile write-back). Monospace, no-wrap, dirty-tracked. */
.ed-file { display: flex; flex-direction: column; min-height: 0; height: 100%; }
.ed-file-title {
  flex: none; display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-1) var(--s-3); border-bottom: 1px solid var(--border);
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); background: var(--bg-elev);
}
/* PO: editor header shows the file PATH as a breadcrumb (dir › dir › file). The breadcrumb
   truncates (R3); the toolbar is flex:none and never wraps (§5b). */
.ed-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; }
.ed-toolbar { flex: none; display: inline-flex; align-items: center; gap: var(--s-2); margin-left: auto; }
.ed-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.ed-seg-btn { padding: 5px 11px; font: inherit; font-size: var(--t-xs); background: var(--panel); color: var(--muted); border: 0; cursor: pointer; } /* #112 dc:869 */
.ed-seg-btn + .ed-seg-btn { border-left: 1px solid var(--border); }
.ed-seg-btn.active { background: var(--accent); color: #04101f; }
/* GAP-1 EDIT+SAVE — a real Save button + an unsaved-changes dot in the editor toolbar. */
.ed-dirty { color: var(--warn, #e0b341); font-size: 10px; opacity: 0; transition: opacity .12s; }
.ed-dirty.on { opacity: 1; }
/* #B6 / UC-284 (dc:1019): the editor-header change indicator — M/A/D badge + numstat "+N −M". */
.ed-chg { display: inline-flex; align-items: center; gap: 6px; margin-left: 10px; font-family: var(--font-mono, monospace); font-size: var(--t-xs); flex: none; }
.ed-chg-badge { display: inline-grid; place-items: center; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 4px; font-weight: 700; font-size: 10px; }
.ed-stat-m .ed-chg-badge { color: #0b0c0f; background: var(--orange, #e0b341); }
.ed-stat-a .ed-chg-badge { color: #0b0c0f; background: var(--green, #46d19e); }
.ed-stat-d .ed-chg-badge { color: #fff; background: var(--red, #ff6b6b); }
.ed-chg-add { color: var(--green, #46d19e); }
.ed-chg-del { color: var(--red, #ff6b6b); }
.ed-save {
  height: 30px; padding: 0 12px; font: inherit; font-size: var(--t-xs); cursor: pointer; /* #112 dc:872: h30 */
  display: inline-flex; align-items: center;
  border: 1px solid var(--accent); border-radius: var(--r-sm);
  background: var(--accent); color: #fff; font-weight: 600;
}
.ed-save:disabled { background: var(--panel-2); color: var(--faint); border-color: var(--border); cursor: default; }
.ed-save:not(:disabled):hover { filter: brightness(1.08); }
.ed-crumb { color: var(--muted); }
.ed-crumb-file { color: var(--fg); font-weight: 600; }
.ed-crumb-sep { color: var(--faint); margin: 0 1px; }
.ed-body {
  margin: 0; padding: var(--s-2) 0; overflow: auto; min-height: 0; flex: 1 1 auto;
  font-family: var(--font-mono); font-size: var(--t-sm); line-height: 1.5;
  white-space: pre; background: var(--bg); color: var(--fg);
}
.ed-line { display: flex; }
.ed-ln {
  flex: none; width: 3.5em; padding-right: var(--s-2); text-align: right;
  color: var(--faint); user-select: none; -webkit-user-select: none;
}
.ed-tx { white-space: pre; }

/* GAP-1: the EDITABLE editor — a textarea overlaid on a line-number gutter. The gutter is a
   non-interactive column; the textarea is the single source of truth for the buffer + caret.
   Both share the mono metrics + line-height so line numbers line up with text rows. */
.ed-body.ed-editable {
  display: flex; padding: 0; overflow: hidden; /* the textarea owns the scroll */
}
.ed-editable .ed-gutter {
  flex: none; width: 3.5em; padding: var(--s-2) var(--s-2) var(--s-2) 0;
  display: flex; flex-direction: column; text-align: right;
  overflow: hidden; user-select: none; -webkit-user-select: none;
  color: var(--faint); background: var(--bg-elev); border-right: 1px solid var(--border);
}
.ed-editable .ed-gutter .ed-ln { width: auto; padding-right: var(--s-2); display: block; }
.ed-editable .ed-input {
  flex: 1 1 auto; min-width: 0; box-sizing: border-box;
  margin: 0; padding: var(--s-2) var(--s-3);
  border: 0; outline: none; resize: none; overflow: auto;
  font-family: var(--font-mono); font-size: var(--t-sm); line-height: 1.5;
  white-space: pre; tab-size: 2; -moz-tab-size: 2;
  background: var(--bg); color: var(--fg); caret-color: var(--accent);
}
.ed-editable .ed-input:focus { background: var(--bg); }

/* UC-324 syntax highlight — the highlight-behind-textarea technique. A <pre class="ed-hl"> layer holds
   the Shiki (dark-plus) tokens; the .ed-input textarea sits ON TOP with TRANSPARENT text (so the tokens
   show through) but a visible caret + selection. Both share the SAME box metrics (font/size/line-height/
   padding/tab-size/white-space) so every glyph in the textarea lines up over its coloured token. */
.ed-editable .ed-code-wrap { position: relative; flex: 1 1 auto; min-width: 0; overflow: hidden; }
.ed-editable .ed-code-wrap .ed-hl,
.ed-editable .ed-code-wrap .ed-input {
  position: absolute; inset: 0; margin: 0; box-sizing: border-box;
  padding: var(--s-2) var(--s-3);
  font-family: var(--font-mono); font-size: var(--t-sm); line-height: 1.5;
  white-space: pre; tab-size: 2; -moz-tab-size: 2; overflow: auto;
  border: 0;
}
.ed-editable .ed-code-wrap .ed-hl {
  z-index: 0; pointer-events: none; color: var(--fg); background: var(--bg);
}
.ed-editable .ed-code-wrap .ed-hl pre,
.ed-editable .ed-code-wrap .ed-hl code {
  margin: 0; padding: 0; background: transparent !important; font: inherit; white-space: inherit;
}
.ed-editable .ed-code-wrap .ed-input {
  z-index: 1; resize: none; outline: none;
  background: transparent;                 /* let the coloured <pre> show through */
  color: transparent;                      /* hide the textarea's own black text… */
  caret-color: var(--accent);              /* …but keep a visible caret */
  -webkit-text-fill-color: transparent;    /* Safari: transparent glyphs, opaque caret */
}
.ed-editable .ed-code-wrap .ed-input::selection { background: rgba(55, 148, 255, .28); color: transparent; }
/* an unsupported lang (empty <pre>) → fall back to opaque textarea text so it's always readable. */
.ed-editable .ed-code-wrap .ed-hl:empty + .ed-input { color: var(--fg); -webkit-text-fill-color: var(--fg); }

/* UC-325/326 find + goto bar — an overlay top-right of the code area (no design mockup → editor
   aesthetic: dark toolbar, mono, accent focus). Hidden until ⌘F/⌘G. */
.ed-code-wrap .ed-find {
  position: absolute; top: 8px; right: 14px; z-index: 3;
  display: flex; align-items: center; gap: 6px;
  padding: 5px 6px; border-radius: 9px;
  background: var(--bg-elev); border: 1px solid var(--border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
}
.ed-code-wrap .ed-find[hidden] { display: none; }
.ed-code-wrap .ed-find-in {
  width: 180px; margin: 0; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--fg); caret-color: var(--accent);
  font-family: var(--font-mono); font-size: var(--t-xs); outline: none;
}
.ed-code-wrap .ed-find-in:focus { border-color: var(--accent); }
.ed-code-wrap .ed-find-count {
  min-width: 34px; text-align: center; color: var(--muted);
  font-family: var(--font-mono); font-size: var(--t-xs); user-select: none;
}
.ed-code-wrap .ed-find-btn {
  width: 24px; height: 24px; flex: none; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: 6px; background: transparent; color: var(--muted);
  cursor: pointer; font-size: 12px; line-height: 1;
}
.ed-code-wrap .ed-find-btn:hover { color: var(--fg); border-color: var(--accent); background: rgba(255, 255, 255, .04); }

/* UC-322/323 non-editable state (binary / image / too-large) — a centered note instead of the textarea. */
.ed-noneditable {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: var(--s-4); text-align: center; background: var(--bg); color: var(--muted);
}
.ed-noneditable .ed-ne-icon { font-size: 34px; line-height: 1; opacity: .8; }
.ed-noneditable .ed-ne-title { font-size: var(--t-md); font-weight: 600; color: var(--fg); }
.ed-noneditable .ed-ne-msg { margin: 0; font-size: var(--t-sm); max-width: 380px; line-height: 1.5; }
.ed-noneditable .ed-ne-path { margin: 4px 0 0; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--faint); }
/* a video/audio file PLAYS in its tab, streamed with ranges from /preview/<pid>/<rel> */
.ed-media { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; padding: 16px; background: var(--bg); }
.ed-media-video .ed-media-el { max-width: 100%; max-height: 100%; border-radius: 6px; background: #000; }
.ed-media-audio .ed-media-el { width: min(520px, 100%); }
/* UC-327 read-only banner (producer offline) — a thin strip above the editor body. */
.ed-readonly-banner {
  flex: none; display: flex; align-items: center; gap: 7px;
  padding: 4px var(--s-3); font-size: var(--t-xs); color: var(--warn, #e0b341);
  background: rgba(224, 179, 65, .08); border-bottom: 1px solid rgba(224, 179, 65, .2);
}
.ed-readonly-banner .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--warn, #e0b341); flex: none; }

/* UC-320 save-conflict modal (H12a, dc:1806-1816) — a centered dialog over a scrim. */
.ed-conflict-scrim {
  position: fixed; inset: 0; z-index: 200; display: grid; place-items: center;
  background: rgba(0, 0, 0, .5);
}
.ed-conflict {
  width: 340px; max-width: 90vw; padding: 16px; display: flex; flex-direction: column; gap: 10px;
  background: #111318; border: 1px solid rgba(255, 255, 255, .12); border-radius: 14px;
  box-shadow: 0 24px 50px rgba(0, 0, 0, .6);
}
.ed-conflict .ed-cf-head { display: flex; align-items: center; gap: 8px; }
.ed-conflict .ed-cf-warn { color: var(--warn, #e0b341); font-size: 15px; }
.ed-conflict .ed-cf-title { font-size: var(--t-md); font-weight: 600; color: var(--fg); }
.ed-conflict .ed-cf-msg { margin: 0; font-size: var(--t-xs); color: var(--muted); line-height: 1.5; }
.ed-conflict .ed-cf-msg code { font-family: var(--font-mono); color: var(--fg); }
.ed-conflict .ed-cf-actions { display: flex; flex-direction: column; gap: 6px; }
.ed-conflict .ed-cf-btn {
  padding: 8px 12px; text-align: left; border-radius: 9px; cursor: pointer; font-size: var(--t-sm);
  border: 1px solid var(--border); background: transparent; color: var(--fg);
}
.ed-conflict .ed-cf-btn:hover { background: rgba(255, 255, 255, .04); border-color: var(--accent); }
.ed-conflict .ed-cf-primary { border-color: rgba(55, 148, 255, .4); background: rgba(55, 148, 255, .1); }

/* G16 themed confirm — a styled modal replacing native window.confirm (TM.util.confirm). Same visual
   language as the conflict dialog: dim scrim, panel card, cancel + primary/danger action buttons. */
.tm-confirm-scrim {
  /* Above the settings/profile scrim (1120): a confirm/prompt is often OPENED from inside settings
     (e.g. the "Đổi master" flow), so it must stack over it, not behind. */
  position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center;
  background: rgba(0, 0, 0, .55);
}
.tm-confirm {
  width: 360px; max-width: 90vw; padding: 18px 18px 16px; display: flex; flex-direction: column; gap: 10px;
  background: #111318; border: 1px solid rgba(255, 255, 255, .12); border-radius: 14px;
  box-shadow: 0 24px 50px rgba(0, 0, 0, .6);
}
.tm-confirm .tm-cf-title { font-size: var(--t-md); font-weight: 600; color: var(--fg-strong); }
.tm-confirm .tm-cf-msg { font-size: var(--t-sm); color: var(--muted); line-height: 1.5; white-space: pre-line; }
.tm-confirm .tm-pf-status[hidden] { display: none; }
.tm-confirm .tm-pf-status.tm-pf-error { color: var(--red); }
.tm-confirm .tm-cf-btn:disabled, .tm-confirm .tm-cf-input:disabled { opacity: .6; cursor: default; }
.tm-confirm .tm-cf-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }
.tm-confirm .tm-cf-btn {
  padding: 7px 14px; border-radius: 9px; cursor: pointer; font-size: var(--t-sm);
  border: 1px solid var(--border); background: transparent; color: var(--fg);
}
.tm-confirm .tm-cf-btn:hover { background: rgba(255, 255, 255, .04); border-color: var(--accent); }
.tm-confirm .tm-cf-primary { border-color: rgba(55, 148, 255, .45); background: rgba(55, 148, 255, .14); color: var(--fg-strong); }
.tm-confirm .tm-cf-primary:hover { background: rgba(55, 148, 255, .22); }
.tm-confirm .tm-cf-danger { border-color: rgba(240, 90, 90, .45); background: rgba(240, 90, 90, .12); color: #f5b5b5; }
.tm-confirm .tm-cf-danger:hover { background: rgba(240, 90, 90, .2); }
/* JE-28 — the inactive-team sweep dialog: an hour picker and the PREVIEW a person
   reads before confirming a bulk delete. Wider than a plain confirm because the
   whole point is that the list of what is about to go is legible. */
.tm-confirm.tm-sweep { width: 460px; }
.tm-sweep .tm-sweep-hours {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--t-sm); color: var(--fg);
}
.tm-sweep .tm-sweep-hours select {
  padding: 6px 10px; border-radius: 8px; font-size: var(--t-sm);
  border: 1px solid var(--border); background: #0d0f13; color: var(--fg);
}
.tm-sweep .tm-sweep-preview {
  max-height: 42vh; overflow-y: auto; padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: rgba(255, 255, 255, .02);
  font-size: var(--t-sm); color: var(--muted); line-height: 1.5;
}
.tm-sweep .tm-sweep-head {
  font-weight: 600; color: var(--fg-strong); margin: 6px 0 4px;
}
.tm-sweep .tm-sweep-head:first-child { margin-top: 0; }
.tm-sweep .tm-sweep-list { list-style: none; margin: 0; padding: 0; }
.tm-sweep .tm-sweep-list li {
  display: flex; justify-content: space-between; gap: 10px; padding: 3px 0;
  border-bottom: 1px solid rgba(255, 255, 255, .05);
}
.tm-sweep .tm-sweep-name { color: var(--fg); word-break: break-all; }
.tm-sweep .tm-sweep-idle { color: var(--muted); white-space: nowrap; }
.tm-sweep .tm-sweep-kept .tm-sweep-name { color: #f5b5b5; }
.tm-sweep .tm-sweep-why { color: var(--muted); text-align: right; }
.tm-sweep .tm-sweep-foot { margin-top: 8px; color: var(--muted); }
.tm-confirm .tm-cf-btn[disabled] { opacity: .45; cursor: not-allowed; }
.tm-confirm .tm-cf-btn[disabled]:hover { background: transparent; border-color: var(--border); }

/* G16 themed prompt — the text input inside the modal (TM.util.prompt). */
.tm-confirm .tm-cf-input {
  width: 100%; box-sizing: border-box; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg); color: var(--fg-strong); font-size: var(--t-sm);
  font-family: var(--font-mono); outline: none;
}
.tm-confirm .tm-cf-input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(55, 148, 255, .18); }

/* UC-037 account menu: the ⚙/⏻ glyph + the danger (Log out) item. */
.rp-item-ico { display: inline-block; width: 16px; text-align: center; margin-right: 7px; opacity: 0.85; }
.rp-item-danger { color: #f0a5a5; }
.rp-item-danger:hover { background: rgba(240, 90, 90, .1); }

/* UC-037 Settings panel (avatar → ⚙ Settings) — a themed modal: account · server · this-device daemon. */
.tm-settings-scrim { position: fixed; inset: 0; z-index: 1120; display: grid; place-items: center; background: rgba(0, 0, 0, .55); }
.tm-settings {
  width: 440px; max-width: 92vw; max-height: 88vh; overflow-y: auto;
  background: #111318; border: 1px solid rgba(255, 255, 255, .12); border-radius: 14px;
  box-shadow: 0 24px 50px rgba(0, 0, 0, .6);
}
.tm-set-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px 10px; }
.tm-set-title { font-size: var(--t-md); font-weight: 600; color: var(--fg-strong); }
.tm-set-close { width: 26px; height: 26px; border: 1px solid var(--border); background: transparent; color: var(--muted); border-radius: 7px; cursor: pointer; }
.tm-set-close:hover { color: var(--fg-strong); border-color: var(--accent); }
.tm-set-section { padding: 12px 18px; border-top: 1px solid rgba(255, 255, 255, .06); }
.tm-set-label { font-size: var(--t-xs); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--faint); margin-bottom: 8px; }
.tm-set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: var(--t-sm); color: var(--muted); }
.tm-set-val { color: var(--fg); font-family: var(--font-mono); font-size: var(--t-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60%; }
.tm-set-hint { font-size: var(--t-xs); color: var(--muted); line-height: 1.5; margin-bottom: 10px; }
.tm-set-os { display: flex; gap: 8px; }
.tm-set-btn { padding: 7px 14px; border: 1px solid var(--border); background: transparent; color: var(--fg); border-radius: 8px; cursor: pointer; font-size: var(--t-sm); }
.tm-set-btn:hover { border-color: var(--accent); background: rgba(55, 148, 255, .1); }
/* #S3: the OS the user is actually on — highlighted so they grab the right installer. */
.tm-set-btn.is-current { border-color: var(--accent); background: rgba(55, 148, 255, .14); color: var(--fg-strong); font-weight: 600; }
.tm-set-os-tag { margin-left: 7px; font-size: 10px; font-weight: 600; color: var(--accent); text-transform: uppercase; letter-spacing: .04em; }
.tm-set-note { margin-top: 10px; font-size: var(--t-xs); color: var(--muted); line-height: 1.5; }
/* #S4: H1a daemon-config Settings window (dc:424-501) — mirrors the daemon's own window. */
.tm-settings.tm-set-h1a { width: 560px; background: #0d0f13; padding: 0; }
/* "+ Add daemon" modal — same mac-window chrome as H1a, narrower. */
/* Add-daemon dialog — layout per the PO design: plain header (no traffic lights), a hero
   ("No computer connected"), an inline OS row, the command box with the copy button INSIDE it,
   and a single-line footer (token lifetime + waiting pulse). The business bits the design has no
   slot for (macOS Xcode flag, post-pair steps, download-script) stay — folded, not dropped. */
.tm-settings.tm-add-daemon { width: 540px; max-width: 92vw; background: #0d0f13; padding: 0; }
.tm-add-head { display: flex; align-items: center; padding: 18px 20px 14px; border-bottom: 1px solid rgba(255,255,255,.06); }
.tm-add-title { font-size: 17px; font-weight: 650; color: #e9ecf1; letter-spacing: -.01em; }
.tm-add-head .tm-set-close { margin-left: auto; }
.tm-add-body { padding: 22px 20px 20px; display: flex; flex-direction: column; gap: 14px; }
.tm-add-hero { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 6px 0 2px; }
.tm-add-hero-ico { display: grid; place-items: center; width: 58px; height: 58px; border-radius: 14px;
  background: rgba(214,138,74,.10); border: 1px solid rgba(214,138,74,.28); color: #d68a4a; }
.tm-add-hero-title { font-size: 17px; font-weight: 650; color: #e9ecf1; }
.tm-add-hero-sub { margin: 0; max-width: 40ch; font-size: 13px; line-height: 1.55; color: #8b929b; }
.tm-add-os-row { display: flex; align-items: center; margin-top: 4px; }
.tm-add-os-row .np-os-chips { margin-top: 0; flex: 1; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.tm-add-os-row .np-os-chip { padding: 11px 8px; border-radius: 8px; font-size: 13px; }
.tm-add-cap { font-family: var(--font-mono); font-size: 11px; letter-spacing: .06em; color: #6f7680; }
.tm-add-cmd-box { position: relative; background: #14161b; border: 1px solid rgba(255,255,255,.10); border-radius: 8px; }
.tm-add-cmd { margin: 0; padding: 14px 52px 14px 14px; display: flex; gap: 8px; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.6; color: #d6dae0; white-space: pre-wrap; word-break: break-all; }
.tm-add-cmd-prompt { color: #4ec27a; flex: none; }
.tm-add-cmd code { min-width: 0; max-height: 9.6em; overflow-y: auto; user-select: all; -webkit-user-select: all; }
.tm-add-copy { position: absolute; top: 8px; right: 8px; display: grid; place-items: center; width: 30px; height: 30px;
  border: 1px solid rgba(255,255,255,.12); border-radius: 7px; background: #1b1e24; color: #b7bdc6; cursor: pointer; }
.tm-add-copy:hover { border-color: var(--border-strong); color: #e9ecf1; }
.tm-add-foot { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 11.5px; color: #6f7680; }
.tm-add-foot-sep { opacity: .6; }
.tm-add-wait-dot { width: 7px; height: 7px; border-radius: 50%; background: #d6a24a; animation: tm-add-pulse 1.6s ease-in-out infinite; }
/* WEBUI-021 (WEBGO-ADDDAEMON r4): the pulse is decoration; the dot is the STATE. Under reduced motion the motion stops and the dot stays. */
@media (prefers-reduced-motion: reduce) { .tm-add-wait-dot { animation: none; opacity: 0.85; } }
@keyframes tm-add-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
.tm-add-dl { margin-left: auto; padding: 0; border: 0; background: none; font: inherit; color: #8b929b; text-decoration: underline; cursor: pointer; }
.tm-add-dl:hover { color: #e9ecf1; }
/* Trust-root picker — "+ Add folder" browses the host instead of prompting for a typed path.
   Directory rows only; a denied one (system dir / dot-folder) shows greyed and inert. */
.tm-settings.tm-rootpick { width: 460px; max-width: 92vw; background: #0d0f13; padding: 0; }
.tm-rp-crumb { font-family: var(--font-mono); font-size: 11.5px; color: #8b929b; word-break: break-all; }
.tm-rp-list { max-height: 46vh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
  border: 1px solid rgba(255,255,255,.08); border-radius: 8px; padding: 6px; background: #14161b; }
.tm-rp-row { display: flex; align-items: center; gap: 9px; width: 100%; padding: 8px 10px; border: 0; border-radius: 6px;
  background: none; color: #d6dae0; font-size: 13px; text-align: left; cursor: pointer; }
.tm-rp-row:hover { background: rgba(255,255,255,.06); }
.tm-rp-row.is-denied { color: #6f7680; cursor: not-allowed; }
.tm-rp-row.is-denied:hover { background: none; }
.tm-rp-up { color: #8b929b; }
.tm-rp-ico { flex: none; }
.tm-rp-foot { display: flex; gap: 8px; align-items: center; }

/* §4 Folders — the machine has no trusted roots, so no browse is possible. Explain + point
   at Trust folders; we never offer a free-text path box (PO: browse-only). */
.np-folders-blocked { margin: 0; padding: 12px 14px; border: 1px dashed rgba(255,255,255,.14); border-radius: 8px;
  background: rgba(255,255,255,.02); font-size: 12.5px; line-height: 1.6; color: #b7bdc6; }
.np-folders-blocked b { color: #e9ecf1; font-weight: 600; }
.tm-add-more { font-size: 12px; color: #8b929b; border-top: 1px solid rgba(255,255,255,.06); padding-top: 12px; }
.tm-add-more summary { cursor: pointer; list-style: none; color: #b7bdc6; }
.tm-add-more summary::-webkit-details-marker { display: none; }
.tm-add-more summary::before { content: '▸ '; color: #6f7680; }
.tm-add-more[open] summary::before { content: '▾ '; }
.tm-add-more[open] summary { margin-bottom: 10px; }
.tm-set-empty { font-size: 12px; color: #8b929b; padding: 8px 0; }
.tm-set-empty.error { color: #ff8a8a; }
.tm-add-xcode { display: flex; align-items: center; gap: 8px; align-self: flex-start; max-width: 100%; font-size: 12px; color: #b7bdc6; cursor: pointer; }
.tm-add-xcode input { accent-color: var(--accent); flex: none; width: 15px; height: 15px; margin: 0; }
.tm-add-xcode span { min-width: 0; }
.tm-add-steps { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 7px; font-size: 12px; line-height: 1.5; color: #b7bdc6; }
.tm-add-steps li { list-style: disc; }
.tm-add-steps b { color: #e6e8ec; font-weight: 600; }
.tm-set-title-bar { height: 40px; display: flex; align-items: center; gap: 10px; padding: 0 14px; background: #16181d; border-bottom: 1px solid rgba(255,255,255,.06); }
.tm-set-lights { display: flex; gap: 7px; }
.tm-set-lights i { width: 11px; height: 11px; border-radius: 50%; display: block; }
.tm-set-lights .tl-r { background: #ff5f57; } .tm-set-lights .tl-y { background: #febc2e; } .tm-set-lights .tl-g { background: #28c840; }
.tm-set-app { margin-left: 6px; font-size: 12px; font-weight: 600; color: #c4c8ce; }
.tm-set-running { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; font-size: 10px; font-family: var(--font-mono); color: #46d19e; }
.tm-set-running i { width: 7px; height: 7px; border-radius: 50%; background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.16); }
.tm-set-h1a .tm-set-close { margin-left: 10px; }
.tm-set-body { padding: 20px; display: flex; flex-direction: column; gap: 20px; max-height: 74vh; overflow-y: auto; }
.tm-set-h1a .tm-set-section { padding: 0; border-top: none; display: flex; flex-direction: column; gap: 10px; }
.tm-set-general { border-top: 1px solid rgba(255,255,255,.06); padding-top: 16px !important; }
.tm-set-cap { font-size: 10px; font-family: var(--font-mono); letter-spacing: .12em; color: #6e7681; }
.tm-set-cap-sub { color: #5a6069; }
.tm-set-cap-row { display: flex; align-items: center; justify-content: space-between; }
.tm-set-conn { background: #16181d; border: 1px solid rgba(70,209,158,.25); border-radius: 10px; padding: 13px 15px; display: flex; align-items: center; gap: 12px; }
.tm-set-dot { width: 9px; height: 9px; border-radius: 50%; background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.16); flex: none; }
.tm-set-conn-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; font-size: 13px; color: #e6e8ec; }
.tm-set-sub { font-size: 11px; font-family: var(--font-mono); color: #6e7681; }
.tm-set-conn-btns { margin-left: auto; display: flex; gap: 8px; flex: none; }
/* ── MÁY TRẠM (daemon list + recovery) ──────────────────────────────────────────────────────── */
.tm-dmn-list { display: flex; flex-direction: column; gap: 8px; }
.tm-dmn-row { background: #16181d; border: 1px solid rgba(255,255,255,.08); border-radius: 10px; padding: 11px 14px; display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
.tm-dmn-row.is-offline { border-color: rgba(255,107,107,.22); }
.tm-dmn-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; background: #6e7681; }
.tm-dmn-row.is-online .tm-dmn-dot { background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.16); }
.tm-dmn-row.is-offline .tm-dmn-dot { background: #ff6b6b; box-shadow: 0 0 0 3px rgba(255,107,107,.14); }
.tm-dmn-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tm-dmn-name { font-size: 13px; font-weight: 600; color: #e6e8ec; }
.tm-dmn-recover { margin-left: auto; display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
.tm-dmn-cmd { flex-basis: 100%; margin-top: 4px; display: flex; flex-direction: column; gap: 7px; padding: 10px 12px; background: #0e1014; border: 1px solid rgba(255,255,255,.07); border-radius: 8px; }
.tm-dmn-cmd-row { display: flex; align-items: center; gap: 9px; }
.tm-dmn-code { flex: 1 1 0; font: 12px/1.4 var(--font-mono); color: #7fd18f; background: #16181d; border: 1px solid rgba(255,255,255,.08); border-radius: 6px; padding: 7px 10px; overflow-x: auto; white-space: nowrap; }
.tm-set-btn.tm-set-sm { padding: 7px 12px; font-size: 11px; font-weight: 600; }
.tm-set-btn.tm-set-danger { border-color: rgba(255,107,107,.3); color: #ff9b9b; }
.tm-set-btn.tm-set-danger:hover { background: rgba(255,107,107,.1); border-color: #ff6b6b; }
.tm-set-foot-note { font-size: 10px; font-family: var(--font-mono); color: #5a6069; }
/* CODING CLI section (AREA7 — web-driven CLI deploy) */
.tm-cli-list { display: flex; flex-direction: column; gap: 8px; }
.tm-cli-row { background: #16181d; border: 1px solid rgba(255,255,255,.08); border-radius: 10px; padding: 10px 13px; display: flex; align-items: center; justify-content: space-between; gap: 11px; flex-wrap: wrap; }
.tm-cli-name { font-size: 13px; font-weight: 600; color: #e6e8ec; display: flex; align-items: center; gap: 4px 9px; flex-wrap: wrap; }
/* Card header = ONE line: the title (and its status, which may wrap inside it) takes the room left, the
   buttons keep theirs beside it — Reinstall is never pushed under the title (QC 10-10). The accounts slot
   (flex-basis 100%) is what goes to the next line. */
.tm-cli-row > .tm-cli-name { flex: 1 1 0; min-width: 0; }
.tm-cli-row > .tm-cli-act { flex: 0 0 auto; flex-wrap: nowrap; }
.tm-cli-act [hidden] { display: none !important; }
.tm-cli-state { font-weight: 400; }
/* CLI ACCOUNTS inside each CLI card (PO 10-10): the slot takes a full line under the card header; each
   account is one readable line (name · status · quota · used by) with its buttons grouped beside it,
   all wrapping whole chunks on a narrow panel instead of squeezing into a column. */
.tm-cli-acctcount { color: #8b929b; font-size: 11px; font-weight: 400; }
.tm-cliacct-slot { flex: 1 0 100%; min-width: 0; }
.tm-cliacct-slot:empty { display: none; }
.tm-cliacct-block { border-top: 1px solid rgba(255,255,255,.06); margin-top: 2px; padding-top: 9px; display: flex; flex-direction: column; gap: 6px; }
.tm-cliacct-cap { font-size: 10px; font-family: var(--font-mono); letter-spacing: .04em; text-transform: uppercase; color: #8b929b; }
.tm-cliacct-rows { display: flex; flex-direction: column; gap: 6px; }
.tm-cliacct-row { background: #111317; border: 1px solid rgba(255,255,255,.06); border-radius: 8px; padding: 8px 10px; display: flex; align-items: center; justify-content: space-between; gap: 6px 12px; flex-wrap: wrap; }
.tm-cliacct-main { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.tm-cliacct-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 10px; font-size: 12px; line-height: 1.45; }
.tm-cliacct-who { color: #e6e8ec; font-weight: 600; overflow-wrap: anywhere; }
.tm-cliacct-tag { font-size: 11px; color: #9aa3b2; border: 1px solid #3a404c; border-radius: 9px; padding: 0 7px; white-space: nowrap; }
.tm-cliacct-state { color: #cbd0d8; font-size: 11px; }
.tm-cliacct-state.is-ok { color: #46d19e; }
.tm-cliacct-state.is-warn { color: #f0b429; }
.tm-cliacct-meta { color: #8b929b; font-size: 11px; white-space: nowrap; }
.tm-cliacct-meta + .tm-cliacct-meta::before { content: "· "; color: #5a6069; }
/* Line 2 of an account with a switchable member: "Used by: member → [target ▾] [Switch]". Each member's
   group is one unbreakable chunk (the member name truncates first), so Switch never sits alone (QC 10-10). */
.tm-cliacct-use { flex: 1 0 100%; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; font-size: 11px; }
.tm-cliacct-use .tm-cliacct-meta + .tm-cliacct-meta::before { content: none; }
.tm-cliacct-consumer { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: 6px; min-width: 0; max-width: 100%; color: #cbd0d8; }
.tm-cliacct-member { min-width: 0; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-cliacct-row [data-cliacct-switch-to] { flex: 0 1 auto; width: auto; min-width: 0; max-width: 170px; padding: 5px 8px; font-size: 11px; }
.tm-cliacct-row [data-cliacct-switch] { flex: 0 0 auto; }
.tm-cliacct-act { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; flex: 0 0 auto; }
.tm-cliacct-add { display: flex; }
.tm-cliacct-list:empty { display: none; }
.tm-cli-ok { color: #46d19e; font-size: 11px; font-family: var(--font-mono); }
.tm-cli-no { color: #7a808a; font-size: 11px; }
.tm-cli-busy { color: #f0b429; font-size: 11px; display: inline-flex; align-items: center; gap: 5px; }
.tm-cli-busy .tm-spin { width: 11px; height: 11px; border-width: 2px; }
.tm-cli-warn { color: #f0b429; font-size: 11px; }
.tm-cli-login { border-color: rgba(70,209,158,.35) !important; color: #46d19e !important; }
/* CLI login (device-flow) modal */
.tm-cli-auth-scrim { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; z-index: 10000; }
.tm-cli-auth { background: #16181d; border: 1px solid rgba(255,255,255,.12); border-radius: 14px; padding: 20px; width: min(460px, 92vw); display: flex; flex-direction: column; gap: 13px; }
.tm-cli-auth-title { font-size: 15px; font-weight: 700; color: #e6e8ec; }
.tm-cli-auth-modes { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: #cbd0d8; }
.tm-cli-auth-modes label { display: flex; align-items: center; gap: 9px; cursor: pointer; padding: 7px 9px; border-radius: 8px; line-height: 1.35; }
.tm-cli-auth-modes label:hover { background: rgba(255,255,255,.04); }
.tm-cli-auth-modes input[type="radio"] { flex: 0 0 auto; margin: 0; accent-color: #3794ff; width: 15px; height: 15px; }
/* login flow spinner (state-machine phases starting/authenticating): honest "đang xác thực" beat,
   no stray buttons — the buttons themselves are simply not rendered by render() in busy phases. */
.tm-cli-auth-spin { display: flex; align-items: center; gap: 9px; font-size: 12px; color: #cbd0d8; }
.tm-spin { display: inline-block; width: 14px; height: 14px; flex: 0 0 auto; border: 2px solid rgba(255,255,255,.22); border-top-color: #3794ff; border-radius: 50%; animation: tm-spin-kf .8s linear infinite; }
@keyframes tm-spin-kf { to { transform: rotate(360deg); } }
.tm-cli-auth-body { min-height: 40px; display: flex; flex-direction: column; gap: 9px; font-size: 12px; }
.tm-cli-auth-url { color: #6db3ff; word-break: break-all; font-family: var(--font-mono); font-size: 11px; padding: 8px 10px; background: #0f1115; border-radius: 8px; border: 1px solid rgba(255,255,255,.1); }
.tm-cli-auth-code { display: flex; gap: 8px; }
.tm-cli-auth-code .tm-set-input { flex: 1; }
.tm-cli-auth-actions { display: flex; justify-content: flex-end; gap: 8px; }
.tm-set-btn.tm-set-primary { border-color: var(--accent); background: rgba(55,148,255,.14); color: #fff; }

/* ── STREAM-LOGIN full-screen split (PO 23-07: show cả CLI + Chrome, full screen) ───────────────── */
/* The dialog grows to a near-full-screen surface; body becomes a 2-column split (terminal | browser). */
.tm-cli-auth.tm-cli-auth--stream { width: 96vw; height: 94vh; max-width: none; padding: 14px 16px; }
.tm-cli-auth--stream .tm-cli-auth-body { flex: 1; min-height: 0; }
.tm-login-split { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.15fr); gap: 12px; height: 100%; min-height: 0; }
.tm-login-pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: #0f1115; border: 1px solid rgba(255,255,255,.1); border-radius: 10px; overflow: hidden; }
.tm-login-pane-hd { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 9px 12px; font-size: 12px; font-weight: 600; color: #cbd0d8; border-bottom: 1px solid rgba(255,255,255,.08); background: #16181d; }
.tm-login-pane-dot { width: 8px; height: 8px; border-radius: 50%; background: #3794ff; box-shadow: 0 0 6px rgba(55,148,255,.7); }
.tm-login-pane--browser .tm-login-pane-dot { background: #4ec472; box-shadow: 0 0 6px rgba(78,196,114,.7); }
.tm-login-pane-ft { flex: 0 0 auto; padding: 7px 12px; font-size: 11px; color: #8b93a1; border-top: 1px solid rgba(255,255,255,.06); }
.tm-login-term { flex: 1; min-height: 0; padding: 6px 8px; background: #0b0d10; overflow: hidden; }
.tm-login-term .xterm { height: 100%; }
.tm-login-nav { display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; margin-left: 10px; }
.tm-login-nav-btn { flex: 0 0 auto; background: #0f1115; border: 1px solid rgba(255,255,255,.14); border-radius: 7px; color: #cbd0d8; font-size: 12px; padding: 4px 10px; cursor: pointer; }
.tm-login-nav-btn:hover { border-color: var(--accent); color: #fff; }
.tm-login-nav-url { flex: 1; min-width: 0; background: #0f1115; border: 1px solid rgba(255,255,255,.14); border-radius: 7px; color: #e6e8ec; font-size: 11px; font-family: var(--font-mono); padding: 4px 9px; }
.tm-login-nav-url:focus { border-color: var(--accent); outline: none; }
.tm-login-code { flex: 0 0 auto; padding: 8px 12px; font-size: 13px; color: #cbd0d8; background: rgba(55,148,255,.10); border-bottom: 1px solid rgba(55,148,255,.3); }
.tm-login-code b { font-family: var(--font-mono); font-size: 16px; color: #6db3ff; letter-spacing: 2px; }
.tm-login-browser { flex: 1; min-height: 0; position: relative; display: flex; align-items: center; justify-content: center; background: #111; }
.tm-login-browser-wait { position: absolute; font-size: 12px; color: #cbd0d8; text-align: center; padding: 0 24px; line-height: 1.5; }
.tm-login-canvas { max-width: 100%; max-height: 100%; outline: none; cursor: crosshair; }
.tm-login-split--ok .tm-login-pane { border-color: rgba(78,196,114,.5); }
@media (max-width: 860px) {
  .tm-login-split { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
}
.tm-cli-act { display: flex; align-items: center; gap: 8px; }
.tm-cli-method { background: #0f1115; border: 1px solid rgba(255,255,255,.12); border-radius: 8px; padding: 6px 9px; font-size: 11px; color: #cbd0d8; }
.tm-set-device { display: flex; gap: 10px; align-items: center; }
.tm-set-input { flex: 1; background: #16181d; border: 1px solid rgba(255,255,255,.12); border-radius: 9px; padding: 9px 12px; font-size: 13px; color: #e6e8ec; }
.tm-set-input:focus { border-color: var(--accent); outline: none; }
/* Language picker in Settings › GENERAL — a select sitting in a tm-set-gen row, so it must not
   stretch like the full-width text inputs do (flex:1 would push the label off). */
.tm-set-lang { flex: 0 0 auto; min-width: 132px; cursor: pointer; }
.tm-set-btn.tm-set-add { display: inline-flex; align-items: center; gap: 5px; border-color: rgba(55,148,255,.35); color: #3794ff; padding: 6px 11px; font-size: 11px; font-weight: 600; }
.tm-set-list { background: #16181d; border: 1px solid rgba(255,255,255,.08); border-radius: 10px; overflow: hidden; }
.tm-set-li { display: flex; align-items: center; gap: 10px; padding: 10px 13px; font-family: var(--font-mono); font-size: 12px; border-top: 1px solid rgba(255,255,255,.05); }
.tm-set-li:first-child { border-top: none; }
/* UC-078: adding an already-trusted folder flashes the existing row (skip, no dup). */
.tm-set-dup { animation: tmSetDupFlash 1.6s ease-out; }
@keyframes tmSetDupFlash {
  0%, 55% { background: rgba(55,148,255,.22); box-shadow: inset 2px 0 0 #3794ff; }
  100% { background: transparent; box-shadow: inset 2px 0 0 transparent; }
}
.tm-set-fi { color: #3794ff; flex: none; }
.tm-set-path { color: #c4c8ce; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-set-x { color: #5a6069; cursor: pointer; font-size: 14px; } .tm-set-x:hover { color: #ff6b6b; }
.tm-set-empty { padding: 12px 13px; font-size: 12px; color: #6e7681; }
.tm-set-plats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.tm-set-plat { display: flex; align-items: center; gap: 10px; background: #16181d; border: 1px solid rgba(55,148,255,.3); border-radius: 10px; padding: 11px 13px; }
.tm-set-plat.is-off { background: #111318; border-color: rgba(255,255,255,.06); opacity: .55; }
.tm-set-plat-txt { flex: 1; min-width: 0; }
.tm-set-plat-name { font-size: 12px; color: #e6e8ec; font-weight: 600; }
.tm-set-gen { display: flex; align-items: center; gap: 12px; padding: 2px 0; }
.tm-set-gen-txt { flex: 1; font-size: 12px; color: #e6e8ec; }
.tm-set-toggle { width: 34px; height: 20px; border-radius: 999px; background: #2a2d33; position: relative; flex: none; border: none; cursor: pointer; padding: 0; }
.tm-set-toggle i { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #5a6069; transition: transform .15s, background .15s; }
.tm-set-toggle.on { background: #3794ff; }
.tm-set-toggle.on i { transform: translateX(14px); background: #fff; }
.tm-set-toggle[disabled] { cursor: not-allowed; opacity: .6; }
.tm-set-note.error { color: #f0a5a5; }
.tm-set-actions { padding: 12px 18px 16px; border-top: 1px solid rgba(255, 255, 255, .06); display: flex; justify-content: flex-end; }
.tm-set-danger { border-color: rgba(240, 90, 90, .45); background: rgba(240, 90, 90, .12); color: #f5b5b5; }
.tm-set-danger:hover { background: rgba(240, 90, 90, .2); }

/* GAP-18 (§5b): editor status bar — Ln/Col · UTF-8 · LF · lang · unsaved. */
.ed-status {
  /* dc:895 — editor status bar: height 30, border-top .06, text #6e7681 (--faint). */
  flex: none; display: flex; align-items: center; gap: var(--s-1); height: 30px;
  padding: 0 16px; border-top: 1px solid rgba(255, 255, 255, 0.06); background: var(--bg-elev);
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--faint);
}
.ed-st-sep { color: var(--faint); }
.ed-st-lang { color: var(--fg-2); }
.ed-st-dirty { color: var(--warn, #e0b341); }

/* GAP-1: a small transient toast for save feedback (aria-live). */
.tm-toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(8px);
  z-index: 60; max-width: 80vw; padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm); border: 1px solid var(--border-strong);
  background: var(--panel-2); color: var(--fg); font-size: var(--t-sm);
  box-shadow: 0 6px 20px rgba(0,0,0,.45);
  opacity: 0; pointer-events: none; transition: opacity .15s, transform .15s;
}
.tm-toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }
.tm-toast.err { border-color: var(--danger, #ff6b6b); color: var(--danger, #ff6b6b); }
.editor-loading { padding: var(--s-4); }
.term-cursor { background: var(--accent); color: var(--bg); border-radius: 1px; animation: term-caret-blink 1.1s step-end infinite; }
@keyframes term-caret-blink { 0%, 60% { opacity: 1; } 61%, 100% { opacity: 0.35; } }

/* #85 Live full-terminal mode (PO: "what claude doing" — terminal leads, input via `>`).
   Toggled by body.live-active (set when the Live view is shown). Terminal fills the
   pane; the rail/chip recede; the composer becomes a `>` CLI prompt. NEVER an overlay
   — restyle within the cockpit (#84). */
body.live-active #live-view { background: var(--bg); }
body.live-active .term-grid { min-height: 60vh; }
/* the composer reads as a terminal prompt in live mode. */
body.live-active .cli-bar { background: var(--bg); border-top: 1px solid var(--border-strong); }
body.live-active .cli-input { font-family: var(--font-mono); }
body.live-active .cli-bar::before {
  content: '>'; color: var(--green); font-family: var(--font-mono); font-weight: 700;
  display: inline-flex; align-items: center; padding: 0 var(--s-1) 0 var(--s-2); flex: none;
}
/* desktop ≥1024: live mode lets the terminal use the full width (rail stays for context). */
/* #rail base — hidden by default; only the ≥1024 block reveals it. */
.rail {
  background: var(--bg-elev); border-left: 1px solid var(--border);
  overflow-y: auto; padding: var(--s-3) var(--s-3); min-height: 0;
}
.rail .agent-list { margin-bottom: var(--s-3); }
.rail .sheet-section + .sheet-section { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--border); }
.rail .sheet-section-title { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-bottom: var(--s-1); }

/* ═══ #91 W1 shell regions — base styles (ALL widths; ONE DOM, layout via @media) ═══ */
/* each column is a flex-column so its header stays put and its body scrolls. */
#shell-col1, #shell-col2, #shell-col3 { display: flex; flex-direction: column; min-height: 0; min-width: 0; overflow: hidden; }
/* I-23: the chat column anchors the absolute jump-pill (position:relative, doesn't affect layout). */
#shell-col3 { position: relative; }
/* #91 PO: every column header row is the SAME height (40px) so the three columns
   align at the top — the tab containers no longer look uneven. */
/* DESIGN dc (MONITOR + EXPLORER mockups): ALL THREE panel headers are 52px tall with a
   border-bottom rgba(255,255,255,.06) — so the horizontal separator under the 3 headers lands on the
   SAME y and reads as ONE continuous line across the 3 panels. Web had 40px (col-head) / 42px (tabrow)
   with a .08 border → three separators at different heights + a heavier alpha = the "méo xẹo" (jagged)
   header line the PO spotted. 52px + .06 aligns them to the switcher's own 52px .06 row. */
.col1-head, .col2-head, .col3-head {
  display: flex; align-items: center; gap: var(--s-2); flex: none;
  height: 52px; padding: 0 var(--s-2); border-bottom: 1px solid rgba(255,255,255,.06); background: var(--bg-elev);
  /* #B8: each head CLIPS its own overflow + is opaque (var(--bg-elev)) so a scrolled tab strip (B7b
     overflow-x:auto) can't visually bleed past the column edge into a neighbour's header (PO saw the
     col-1 "Files" pill over col-2's ".gitignore" tab). */
  overflow: hidden; min-width: 0;
}
.col3-title { font-size: var(--t-sm); font-weight: 600; color: var(--muted); }
/* design H4 (dc:900): the col-3 agent tab-bar ROW — the JS tabbar fills it, the ⤢/⊟ column
   controls sit at the right edge (like the design's ▾N + + + ⤢ + ⊟ cluster). No "Cockpit" title. */
/* PO 21-07 (design H2): the agent-name line + the ⤢/⊟ controls are ONE container — same
   background, no inner divider — with the single bottom border on the row itself. */
.col3-tabrow { display: flex; align-items: stretch; flex: none; height: 52px; border-bottom: 1px solid rgba(255,255,255,.06); background: #0e1014; }
/* #112 (dc:908): the agent tab-bar row is #0e1014 (slightly elevated); 52px so it aligns with the
   other two panel headers (the design's 3 headers share one 52px row + one .06 separator line). */
.col3-tabrow .tabbar { flex: 1; min-width: 0; height: 52px; border-bottom: none; background: transparent; }
/* J13 — the agent controls on col-3's tab-bar row (design 1a: [tabs][+][⤢][⊟]).
   WORK-ONLY: in Monitor #tabbar is hidden and the roster header carries its own "+ Add ▾",
   so drawing these there would be the same button twice. Deliberately NOT inside .col3-tabctl:
   that class is display:none until the ≥1024 media query, and reaching an employee must not be
   a desktop-only capability — this is the path that replaces the col-1 roster. */
.col3-agentctl { display: none; align-items: center; gap: var(--s-1); flex: none; padding-right: var(--s-1); }
[data-mode="work"] .col3-agentctl { display: flex; }
/* the person-with-a-tick (design 1a's ⌘K palette glyph) and the "+", same 26px square. */
.col3-palette, .col3-add {
  flex: none; width: 26px; height: 26px; display: grid; place-items: center;
  border: 1px solid rgba(55,148,255,.28); border-radius: 7px;
  background: rgba(55,148,255,.12); color: var(--accent);
  font-size: 15px; line-height: 1; cursor: pointer; padding: 0;
}
.col3-palette:hover, .col3-add:hover { background: rgba(55,148,255,.2); border-color: rgba(55,148,255,.45); }
.col3-palette:focus-visible, .col3-add:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── J13 · the agent chooser (design H10f + the ⌘K artboard's @Employees half) ──
   Opened from the tab-bar button above — NOT a list parked in col-1. Fixed-positioned like
   .agent-menu so a narrow/scrolled col-3 cannot clip it. */
.agent-chooser {
  width: 260px; max-height: min(420px, 70vh); display: flex; flex-direction: column;
  background: #0e1014; border: 1px solid rgba(255,255,255,.12); border-radius: 12px;
  box-shadow: 0 18px 40px rgba(0,0,0,.5); overflow: hidden;
}
.ac-searchbar { flex: none; padding: 8px; border-bottom: 1px solid rgba(255,255,255,.07); }
.ac-search {
  width: 100%; box-sizing: border-box; background: #0a0b0d;
  border: 1px solid rgba(255,255,255,.12); border-radius: 7px;
  color: var(--fg-strong, #e6e8ec); font-size: 12px; padding: 6px 9px;
}
.ac-search:focus { border-color: var(--accent); outline: none; }
.ac-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 5px; }
.ac-row {
  width: 100%; display: flex; align-items: center; gap: 8px; text-align: left;
  padding: 7px 9px; border: none; border-radius: 8px; background: transparent;
  color: #c4c8ce; font-size: 12px; cursor: pointer;
}
.ac-row:hover { background: rgba(255,255,255,.05); }
.ac-row.active { background: rgba(55,148,255,.14); color: #fff; }
.ac-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ac-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; }
.ac-name { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-sub { flex: 0 1 auto; color: #6e7681; font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-state { flex: none; margin-left: auto; font-family: var(--font-mono); font-size: 9px; }
.ac-empty { padding: 14px 10px; font-size: 11px; color: #8b909a; line-height: 1.5; text-align: center; }
.ac-foot { flex: none; border-top: 1px solid rgba(255,255,255,.07); padding: 4px; }
.ac-rosterlink {
  width: 100%; text-align: left; border: none; background: transparent; color: var(--accent);
  font-size: 11px; padding: 7px 9px; border-radius: 7px; cursor: pointer;
}
.ac-rosterlink:hover { background: rgba(55,148,255,.12); }
.ac-rosterlink:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.col3-tabctl { display: none; align-items: center; flex: none; }
.col3-tabctl .col-ctl-btn {
  width: 30px; height: 100%; border: none; background: transparent; color: var(--faint);
  cursor: pointer; display: grid; place-items: center; font-size: var(--t-sm);
}
.col3-tabctl .col-ctl-btn:hover { color: var(--fg-strong); background: var(--panel-2); }
/* dc:938 — context strip: the active agent's status · model · context%. Mono, muted, one line. */
.cli-context {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 7px 14px 8px; border-bottom: 1px solid var(--border);
  font-family: var(--font-mono); font-size: 10px; color: var(--faint);
}
.cli-context[hidden] { display: none; }
/* dc:938 — the state segment reads the real state colour (README:74: running green · busy amber ·
   awaiting red · idle grey), so an idle agent shows a grey dot — not a fixed green (QC 🟡). */
.cli-context .ctx-running { color: var(--state-running); }
.cli-context .ctx-busy { color: var(--orange, #e0b341); }
.cli-context .ctx-awaiting { color: #ff6b6b; }
.cli-context .ctx-idle, .cli-context .ctx-exited { color: var(--state-idle); }
.cli-context .ctx-sep { color: var(--state-idle); }
/* H2b F145 breadcrumb (design dc:1015 "ui-revamp / leader", Geist Mono #c4c8ce): the active agent's
   team/agent path, shown only when ≥2 teams. min-width:0 + ellipsis so a long team/agent name never
   pushes the strip wide (R1). */
.cli-context .ctx-crumb { display: inline-flex; align-items: center; min-width: 0; max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cli-context .ctx-crumb-team, .cli-context .ctx-crumb-agent { color: var(--fg-strong, #c4c8ce); overflow: hidden; text-overflow: ellipsis; }
.cli-context .ctx-crumb-sep { color: var(--muted); flex: none; }
/* UC-302: the git branch + ahead/behind chip in the Files column header. */
/* #B3 / UC-302: git branch bar at the BOTTOM of the Files column (design "đáy cột"). A thin footer
   under the tree/roster: ⑂ branch-name + ahead/behind. Hidden on a non-repo. */
.col1-branch {
  display: flex; align-items: center; gap: 6px; flex: none;
  margin-top: auto; /* pin to the column bottom */
  padding: 6px 12px; border-top: 1px solid var(--border);
  font-family: var(--mono, monospace); font-size: var(--t-xs); color: var(--muted);
  white-space: nowrap; overflow: hidden;
}
.col1-branch[hidden] { display: none; }
.col1-branch-icon { color: var(--fg-strong, #ddd); opacity: 0.75; }
.col1-branch-name { overflow: hidden; text-overflow: ellipsis; }
.col1-branch-track { margin-left: auto; color: var(--fg-strong, #ddd); opacity: 0.8; flex: none; }
/* dynamic-layout chrome (resize handles, per-column controls, restore strips) is
   DESKTOP-only (≥1024 grid). Hidden by default; the ≥1024 media re-shows it. */
.col-resize, .col-restore, .col-ctl, .col3-head { display: none; }
.file-tabs { height: 100%; }
/* BUG (lane fixnumbers2, 15-09): tren project "Syrix Team Control Plane" nut ⊟ (Hide files column,
   index.html:265) bi day RA NGOAI cot 14px. Do duoc, va so voi hai project khac de biet CAI GI khac:
     Control Plane : badge Changes = "99+" (26px) ⇒ .col1-tabs 184px, header scrollWidth 310 > 287 ⇒ TRAN
     syrix-landing : badge rong (0px)            ⇒ .col1-tabs 148px, 287/287 ⇒ khong tran
     banh-mi       : badge rong (0px)            ⇒ .col1-tabs 148px, 287/287 ⇒ khong tran
   ⇒ Nguyen nhan la BE RONG BADGE, khong phai project. Design da co co che thu hep (tab khong active
   bo chu, :3669) nhung .col1-tabs la flex-item KHONG duoc phep co lai (min-width mac dinh auto), nen
   phan doi ra bi day sang cum .col-ctl — tuc nut dieu khien bi day khoi cot chu khong phai chu bi cat.
   .col1-head da `overflow:hidden` (:3076) nen nut bi CAT MAT thay vi tran ra nhin thay duoc.
   Cho phep dung nhom tab co lai (min-width:0 + overflow an) va ghim cum nut lai (flex:none):
   nut dieu khien luon nam trong cot, phan bi hy sinh la chu cua tab — dung thu tu uu tien cua design.

   VONG 2 — BAN VA VONG 1 DAY LOI SANG CHO KHAC, khong xoa no. Do lai sau khi vá:
   `overflow:hidden` CAT TU MEP PHAI, ma tab ben phai nhat la "Terminals" ⇒ no bien mat HAN:
     rect 218..252 nhung khung tabs het o 229  (thua 23px, DANG XAY RA voi badge that "99+")
     elementFromPoint(giua tab) = .col1-head, KHONG phai chinh tab  ⇒ BAM KHONG DUOC
     overflow-x:hidden + scrollLeft:0          ⇒ cung khong cuon toi duoc
   Tuc mot muc dieu huong bi AN va CHET, te hon nut ⊟ bi lech ma van thay. Anh chup khung tab
   xac nhan bang mat: chi con [Files] [icon] [99+], khong con chu "Terminals" nao.
   Goc that: .c1-tab la inline-flex KHONG co min-width:0 nen tung tab khong chiu co lai, don het
   phan thua ra mep phai. Sua dung cho: cho CHINH TAB co lai va cho chu trong tab cat co dau "…",
   de moi tab deu con nhin thay va bam duoc thay vi mot tab bi nuot tron. */
.col1-tabs, #view-switch.view-switch { display: flex; gap: var(--s-1); }
.col1-tabs { min-width: 0; overflow: hidden; }
.col1-head .col-ctl { flex: none; }
/* VONG 3 SAI, DA TRA LAI VONG 2 — ghi lai de khong ai di lai duong nay.
   Vong 3 doc `thieu=0` roi ket luan "khong thieu cho, vay ellipsis cat oan" va bo `min-width:0`.
   Do lai sau khi bo: `thieu` nhay tu 0 len 23 va tab Terminals lai bien mat (clipped +23,
   elementFromPoint khong trung chinh no). Tuc `thieu=0` KHONG PHAI bang chung con du cho —
   no la KET QUA cua chinh `min-width:0` dang lam viec: nhan co lai nen vua khit. Toi doc mot
   con so DO CHINH BAN VA TAO RA rồi dung no de ket luan ban va la thua.
   Ngan sach that: tabsNeed 184 vs tabsGot 161 ⇒ THIEU 23px. Khong du cho ca ba tab day chu.
   ⇒ Phai hy sinh mot thu. Chon: chu "Files" rut thanh "Fi…" (van con tab, van bam duoc, hover/title
   doc duoc) THAY VI mot tab bien mat han va bam khong duoc. Mat chu la kho chiu; mat MUC DIEU HUONG
   la loi. Khi nao PO cho noi rong cot 1 thi ca hai cung duoc, do la cau hoi da ghi PO-QUESTIONS. */
.col1-tabs .c1-tab { min-width: 0; flex: 0 1 auto; }
.col1-tabs .c1-tab-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.col1-tabs .c1-badge { flex: none; }
.col1-tabs .c1-tab-ico { flex: none; }

/* ───────── The team roster in col-1 (§5a) ─────────
   Base: the roster is SHOWN. In Monitor mode (data-mode="monitor") the files list and the
   Files/Terminals tabs hide as well, so col-1 is the roster alone; in Work and Explorer the
   roster sits alongside them. The center conversation and right cockpit are SHARED across
   modes (README §5). */
/* no top padding — design H2: the "TEAM · N agents" row starts flush under the 52px switcher,
   same baseline as the middle/right panels' second row (its own 12px padding provides the gap). */

/* JC-6 — THE ROSTER IS VISIBLE IN WORK MODE TOO, and this line is the whole defect.
   Measured on the live cockpit (runtime cd182848018c): #roster was fully rendered — four rows
   each carrying a data-session-id, "TEAM · 4 agents", the real member names — and
   getComputedStyle said display:none. The data was right, the render was right, and the only
   thing wrong was that nothing turned it on: `data-mode` lives on <html>, its value in the
   cockpit is "work", and the sole rule that set `display:block` asked for "monitor".
   So a user reading "Pick an employee in the roster" had four employees sitting one CSS
   declaration away from them, and no way to reach any of them — which is why J-WORK and
   J-CHAT both died at the user layer while every door underneath answered correctly.
   The roster is now shown by default and hidden only where a mode deliberately replaces it. */
#roster { display: block; overflow-y: auto; padding: 0 var(--s-2) var(--s-2); flex: 0 0 auto; max-height: 45%; order: 3; min-height: 0; }

/* JC-6b — THE ROSTER MUST NOT OUTRANK THE TAB THE READER PICKED.
   JC-6 (above) turned the roster on in Work mode, which was right: without it J-WORK and J-CHAT
   had no way to reach an employee. What it did not account for is that col-1 in Work mode is a
   TABBED column — [Files | Changes | Terminals] — and `order: 1` put the roster ABOVE whichever
   tab was selected, in every tab.

   MEASURED at :4675 2026-09-15 (project 'Syrix Team Control Plane', 57 agents), viewport 1440x900,
   Files tab active — the same shape PO photographed on `banh-mi-landing`:
     #roster    top 104  height 405  (order 1, max-height 45%)  → "TEAMS · 25 · 57 agents"
     #file-tree top 509  height 391  (order 2)
   So the tab said Files, the folder icon was lit, and the first 405px under it were the agent
   list — the file tree started below the fold of a 900px screen. PO photographed exactly that and
   read it as "the Files tab renders the roster". The tree was never missing and its refusal
   branch DID run; it was simply pushed under 405px of something else.

   The fix is ordering, not visibility: the roster keeps its bounded share and stays reachable
   (JC-6's requirement is untouched), but it sits BELOW the file surfaces so the selected tab owns
   the top of its own column. Monitor mode is unaffected — it hides the tree outright and hands
   the whole column to the roster in the rules below. */
#cp-switcher, .col1-tabs { order: 0; }
#file-tree, #upload-panel, #changes-list, #terminal-list { order: 2; }
#col1-branch { order: 4; }
/* J13 — COL-1 IN WORK HOLDS ONE SUBJECT: THE FILE COLUMN.
   PO photographed col-1 with the tree (BB / ClaudeCLI / a PDF) and, directly under it,
   "TEAM · 2 agents" + "+ Add ▾" + "Filter agents…" + the member rows + ADOPTABLE — two
   different subjects sharing one column — and asked "sao cái list agent lại nằm ở dưới
   thanh explorer vậy".

   THE DESIGN, read rather than summarised. ACTIVE source for this surface is
   `design_upgrade/v2/…/Team Explorer v2.dc.html` (DESIGN-MANIFEST row 13, sha 4d077ef23ca3).
   Artboard 1a ("H4 · DETAIL MODE") draws col-1 as: project name → [FILES | Changes ±N |
   Terminals N] → the four controls (＋ 🔍 ⤢ ⊟) → the tree. Artboards 1b (CHANGES) and 1c
   (TERMINALS) redraw the SAME column for the other two tabs. In all three: no roster, no
   "TEAM · N agents", no "+ Add ▾", no agent rows. The only agents in col-1 are 1c's
   "AGENT TERMINALS · 3" — terminal sessions, a different object. Agents in Explorer live on
   col-3's tab-bar (1a), and H10f's chooser ends with "Mở Team roster (Monitor) →" — the full
   roster is a MONITOR surface Explorer links to. index.html already said the rule in its own
   words: "Monitor mode (§5a): the team roster REPLACES the files list in col-1".

   WHY `display:none` AND NOT ANOTHER `order:` — read JC-6/JC-6b above before changing this.
   JC-6 turned the roster on unconditionally for a REAL reason: with it hidden, Explorer had no
   way to reach an employee and J-WORK/J-CHAT both died at the user layer. JC-6b then moved it
   below the tree (order 3) — which is the state the PO photographed, because ordering was never
   the question. Measured at :4675 before this change (1440x900, Files tab, 59 agents):
     #file-tree y=104 h=362   → ends at 466
     #roster    y=466 h=405   → 405px of agent list under the tree, in the files column
   So this rule may only land together with the replacement reach-path: index.html now carries
   the design's own "+" (data-cp-addmenu → openRosterAddMenu, the SAME action as the roster
   header's, not a copy) and a "Team roster →" link on col-3's tab-bar row. Remove either one
   and JC-6 re-opens. `j13-explorer-col1-roster.spec.js` case `reach` fails if they go missing.
   The roster keeps rendering (its data feeds the chip row and #tabbar); it is only not GIVEN
   col-1 in a mode whose column belongs to files. */
[data-mode="work"] #roster { display: none; }

/* Monitor mode gives col-1 to the roster alone, so there it takes the whole column back. */
[data-mode="monitor"] #roster { flex: 1 1 auto; max-height: none; }
[data-mode="monitor"] #file-tree,
[data-mode="monitor"] #terminal-list { display: none !important; }
[data-mode="monitor"] .col1-tabs { display: none; }
/* DESIGN-MATCH (H2 MONITOR mockup): the col-1 header in Monitor is JUST the shared Current Project
   switcher, then the roster (whose OWN "TEAM · N · + Add" header follows) — the design has NO second
   toolbar row (Files/Changes/Terminals tabs + quick-open/new-file/maximize) between them. Web was
   rendering .col1-head there, giving a DOUBLE separator line (switcher .06 then col1-head .08, two
   different alphas — the mismatch the PO spotted) + Explorer-only file controls that don't belong in
   Monitor. Hide the whole Explorer head in Monitor; the switcher's single .06 border is the only rule,
   exactly like the design. (Work/Explorer mode keeps col1-head — those tabs + file ops are its job.) */
[data-mode="monitor"] .col1-head { display: none; }

/* GAP-13 §5a: the Monitor Inspector (Changes/Files/To-Do) lives in col-2. In Monitor
   mode it REPLACES the content-driven editor tabs + empty-state; the editor/diff/live panes
   only show when a file/terminal is explicitly opened (they overlay via [hidden] toggles). */
/* design H2: the Inspector panel's TOP row is the Changes/Files/To-Do strip — the
   Explorer col2 head (back btn + file-tabs + ctl) is hidden whole; ⤢/⊟ move into the strip. */
[data-mode="monitor"] .col2-head { display: none; }
/* #732 — THE STATUS BAR PO ASKED FOR ("orca có thanh status hiển thị model status, còn bao nhiêu %").
   This rule USED to be `[data-mode="monitor"] #cli-context { display:none !important }`, justified as
   "the tabrow already states team/agent ● state, so the strip would repeat it as a second row".
   HALF of that premise is true and half of it cost the operator the whole feature. MEASURED live
   2026-09-15 (project banh-mi-landing, member dev2):
       #cli-context.textContent = "● idle · claude-fable-5-1 · 41% ctx"   ← built, correct, per-agent
       #tabbar.innerText        = "banhmi-team / dev2  idle"              ← team/agent/state ONLY
   The tabrow duplicates the STATE segment and nothing else: it names no model and no context percent.
   So the old rule did not remove a duplicate row, it removed the ONLY surface in Monitor mode that
   ever stated the model or %ctx — in the exact mode where a user watches agents. Both facts the PO
   asked for were built, shipped, and then hidden by this one line.
   The fix keeps the de-duplication that was real and drops the suppression that was not: the strip
   SHOWS in Monitor, and only its `● state` segment is suppressed there because the header above it
   already carries that word. Model and %ctx — the two segments with no other home — are visible.
   Quota (`plan`) rides the same strip and appears when a reading exists; it has no source on a
   healthy CLI (footer_signals.go parsePlanUsage only fires on a warning banner), so it is OMITTED
   rather than rendered as a fabricated 0% — see test/b732-monitor-statusbar-model-ctx.test.js. */
[data-mode="monitor"] #cli-context .ctx-state { display: none; }
/* The separator that preceded the now-hidden state segment would otherwise open the line with " · ". */
[data-mode="monitor"] #cli-context .ctx-state + .ctx-sep { display: none; }
[data-mode="monitor"] #file-tabs { display: none; }
[data-mode="monitor"] #col2-empty { display: none; }
/* In Monitor mode the Inspector OWNS col-2 — the content-driven editor/diff/terminal panes
   are suppressed so an earlier-opened tab can't cover the Inspector. The live CLI is NOT an
   Inspector tab (WFRM-T5 / #55): the >_ button moves #live-view into col-3 and sets
   body.live-active, which the override below re-reveals. Work mode restores them. */
[data-mode="monitor"] #editor-view,
[data-mode="monitor"] #diff-view,
[data-mode="monitor"] #live-view { display: none !important; }

/* #631 MONITOR FILE-OPEN CENTER (PO 31-08): clicking a file in the right Inspector (or a file
   card/link in the thread) opens it in the MIDDLE column. Same mechanism as the #55 terminal:
   parkFileViewInThread MOVES #editor-view / #diff-view into #shell-col3 before #thread and sets
   body.monitor-file. Selecting on the col-3 PARENT (not another !important on the hide rule above)
   is what keeps this honest — a pane still sitting in col-2 stays hidden, so there is exactly one
   place a file can show in Monitor, and no override to unpick when it goes home.
   Closing the last file tab → restoreFileViewHome() moves them back and the chat returns. */
[data-mode="monitor"] #shell-col3 > #editor-view:not([hidden]),
[data-mode="monitor"] #shell-col3 > #diff-view:not([hidden]) { display: flex !important; flex: 1 1 auto; min-height: 0; overflow: auto; }
/* the chat + its jump pill yield the middle while a file is parked there (the composer STAYS: the
   user keeps talking to the same agent, and the footer chips keep the agent context on screen). */
body.monitor-file[data-mode="monitor"] #thread,
body.monitor-file[data-mode="monitor"] #thread-jump { display: none !important; }
/* #669 (PO 14-09: "đã show tab rồi còn show banhmi-team / team-lead idle… gỡ thằng header này
   ra", then "để tabs lên chung hàng với ⤢ ⊟"). Measured with a file open: the session name was
   on screen THREE times — the left roster (with more detail: role, model, machine), #tabbar at
   y=0, and the #mf-bar tab at y=52. The tab is the one the user can click and close, so it is
   the one that earns the space; #tabbar repeating it one row above was pure cost in the most
   expensive strip of the screen.
   The HEADER TEXT goes, the COLUMN CONTROLS stay: .col3-tabrow holds exactly two children
   (#tabbar and .col-ctl), so hiding the row would take ⤢/⊟ with it — a second bug traded for
   the first. Instead #tabbar hides, the row keeps only the controls, and #mf-bar is lifted onto
   that same line, which is what actually returns the 52px rather than leaving a blank band.
   Scoped to body.monitor-file: with no file parked there is no #mf-bar, and #tabbar is then the
   ONLY place the middle column names the agent. */
/* #669/#673 — GONE, not toggled. Two attempts keyed this header to a state flag and each one
   produced a new bug at the moment the flag changed: tied to `monitor-file` it reappeared as
   soon as the user switched back to the chat tab, above the #mf-bar strip that was by then
   deliberately left on screen. The header never earned its place to begin with: the left
   roster already names the agent with MORE detail (role, model, machine), and the tab strip
   names the session again whenever a file is open. So it goes, in Monitor, always — one rule,
   no state to get wrong. The ⤢/⊟ controls live in .col-ctl beside it and are untouched. */
[data-mode="monitor"] #tabbar { display: none !important; }
/* PO 14-09: "mở file thì show chatbox làm gì hả". The original note here argued the composer
   should stay so the user "keeps talking to the same agent" — but measured on screen it is a
   text box for a conversation that is not visible, sitting under a file, taking the bottom of
   the column. Reading a file and typing at an agent are two different jobs; the tab strip is
   one click away when the user wants the other one. So while a file owns the middle, the
   composer and its chip strip yield too — the file gets the whole column. */
body.monitor-file[data-mode="monitor"] #cli-form,
body.monitor-file[data-mode="monitor"] #cli-footer,
body.monitor-file[data-mode="monitor"] .chip-row { display: none !important; }
/* …and the file must USE the column it was given. Measured with BAOCAO.md parked: #editor-view
   was the full 583px but `.ed-body` inside it was 208px — it sized to its content, so three
   short lines left the text clipped at ~40% of the width while the rest of the column sat
   empty. The pane is a flex column; the body needs to be told to take the line, not just to
   be allowed to. */
[data-mode="monitor"] #shell-col3 > #editor-view > .ed-file,
[data-mode="monitor"] #shell-col3 > #editor-view .ed-body,
[data-mode="monitor"] #shell-col3 > #diff-view .ed-body { width: 100%; }
/* With #tabbar gone for good (above), the row carries only the ⤢/⊟ controls, so it no longer
   needs a 52px band of its own: it shrinks to the controls and the tab strip sits on that same
   line. No state, no absolute positioning — the earlier version pinned #mf-bar over the row and
   broke the moment the flag it keyed on changed. */
[data-mode="monitor"] .col3-tabrow { height: auto; min-height: 0; }
[data-mode="monitor"] #mf-bar { flex: none; }
[data-mode="monitor"] #mf-bar #file-tabs { display: flex; }
/* the parked header: '‹ Chat' + the real #file-tabs strip, so multiple open files and their ✕
   behave exactly as in Work mode. #file-tabs is display:none in Monitor (above) — un-hide it only
   while it is parked in the bar, keeping the col-2 strip hidden. */
[data-mode="monitor"] #mf-bar { display: flex; align-items: center; gap: var(--s-2); min-height: 34px; padding: 0 var(--s-2); border-bottom: 1px solid var(--border); background: var(--panel); }
[data-mode="monitor"] #mf-bar #file-tabs { display: flex; flex: 1 1 auto; min-width: 0; overflow-x: auto;
  /* PO 14-09: "tab thì ẩn cái scroll ngang đi". Scrolling still WORKS (trackpad, shift-wheel,
     keyboard) — only the bar itself is taken out of the layout, because a scrollbar drawn
     under a 34px strip of tabs eats a visible slice of it for a hint nobody needed. */
  scrollbar-width: none; -ms-overflow-style: none; }
[data-mode="monitor"] #mf-bar #file-tabs::-webkit-scrollbar { display: none; width: 0; height: 0; }
[data-mode="monitor"] #mf-bar .mf-back { flex: 0 0 auto; cursor: pointer; background: none; border: 0; color: var(--fg-2); font-size: var(--t-xs); font-weight: 600; padding: 4px 8px; border-radius: 6px; }
[data-mode="monitor"] #mf-bar .mf-back:hover { background: var(--panel-2); color: var(--fg-strong); }
[data-mode="monitor"] #mf-bar .mf-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* LIVE-CLI FROM MONITOR: the >_ button lives in Monitor mode, so when the user EXPLICITLY opens the
   live terminal (openTerminalView sets body.live-active), the terminal pane must reveal even though the
   Monitor layout hides it by default. This override wins because it is more specific (adds .live-active)
   AND later in the cascade; live-view[hidden] still hides it when no tab is open. Closing the terminal
   tab clears live-active → the Monitor layout's hide rule applies again. */
body.live-active[data-mode="monitor"] #live-view:not([hidden]),
[data-mode="monitor"] body.live-active #live-view:not([hidden]) { display: flex !important; }

/* #55 (user 04-08 "terminal nằm ở chat thread"): in Monitor mode the live CLI REPLACES the chat in the
   MIDDLE column. openTerminalView moves the #live-view node into #shell-col3 (before #thread) and sets
   body.monitor-term. Here: hide the chat thread + its jump pill (the terminal owns the middle now); let
   #live-view fill the space. The right inspector column (col2) STAYS — user 04-08: "có lý do gì ẩn cột
   3 khi show terminal" — only the chat is replaced; the inspector keeps its width + content. The
   composer (#cli-form) stays visible — typing there forwards to the CLI (key-by-key). restoreLiveViewHome
   + removing .monitor-term reverts all of this. */
body.monitor-term[data-mode="monitor"] #shell-col3 #thread,
body.monitor-term[data-mode="monitor"] #shell-col3 #thread-jump,
body.monitor-term[data-mode="monitor"] #shell-col3 #footer-bar,
/* #55h (user 04-08 "container của cli còn nằm ở trên"): #cli-footer and #chip-row are SEPARATE col3
   children from #footer-bar and were never hidden — measured live they still occupied y=785 h=30 and
   y=855 h=46 around the collapsed strip, which is the leftover container the user saw. They belong to
   the CHAT composer (context/usage line + chip row); in key-streaming mode nothing is composed here. */
body.monitor-term[data-mode="monitor"] #shell-col3 #chip-row { display: none !important; }
/* #244 (user: "bấm >_ từ vòng 3 không đổi gì nữa") — THE WAY OUT MUST STAY ON SCREEN.
   #cli-footer was hidden wholesale here, but the `>_` toggle ITSELF lives inside it (render.js:178,
   .cf-mode[data-cli-open]). So the moment terminal mode turned on, the only button that turns it off
   became display:none — the click landed on nothing, the state never flipped back, and every later
   press looked like a dead button (measured: body.monitor-term stuck true across rounds 1-3 while the
   >_ element had a 0x0 box). Hiding the control that reverses a mode is what made this unrecoverable.
   Keep the footer as a bare strip carrying ONLY the toggle: the chat-composer items it used to show
   (context/usage, teammates, manage ↓, esc-hint) are still meaningless while keys stream to the CLI. */
body.monitor-term[data-mode="monitor"] #shell-col3 #cli-footer > * { display: none !important; }
body.monitor-term[data-mode="monitor"] #shell-col3 #cli-footer > .cf-mode {
  display: inline-flex !important;
}
body.monitor-term[data-mode="monitor"] #shell-col3 #cli-footer {
  display: flex !important; justify-content: flex-end; padding: 4px 12px;
}
body.monitor-term[data-mode="monitor"] #shell-col3 #live-view:not([hidden]) {
  display: flex !important; flex: 1 1 auto; min-height: 0;
}
/* THE ROOM OWNS THE MIDDLE (PO 17-09, design shot: "design của người ta là vầy mà bạn code gì vậy").
   Same shape as #55 above — parkDiscussionInThread moves #discussion-view into #shell-col3 before
   #thread and sets body.monitor-disc — with ONE deliberate difference: the terminal keeps #cli-form
   because typing there streams keys to the CLI, whereas the room carries its OWN composer inside the
   pane (#disc-input + Gửi, the box the design draws across the bottom of the centre column). Leaving
   the chat composer visible under it would put TWO input boxes in one column, and the reader cannot
   tell which one reaches the room. So the whole chat footer goes, and nothing here is !important
   beyond what the hide rules need — restoreDiscussionHome + dropping the class reverts all of it.
   QC 952: #chip-row is NOT hidden — the room shows the chip strip, its To-Do chip counting the team board. */
body.monitor-disc[data-mode="monitor"] #shell-col3 #thread,
body.monitor-disc[data-mode="monitor"] #shell-col3 #thread-jump,
body.monitor-disc[data-mode="monitor"] #shell-col3 #footer-bar,
body.monitor-disc[data-mode="monitor"] #shell-col3 #cli-footer,
body.monitor-disc[data-mode="monitor"] #shell-col3 #cli-form { display: none !important; }
body.monitor-disc[data-mode="monitor"] #shell-col3 #discussion-view:not([hidden]) {
  display: flex !important; flex: 1 1 auto; min-height: 0;
}


/* Design 4b: Discussion and member chats share a 46px tab strip. */
body.has-disc-tabs[data-mode="monitor"] #mf-bar {
  height: 46px; min-height: 46px; padding: 0; gap: 0;
  background: #0e1014; border-bottom: 1px solid rgba(255,255,255,.06);
}
body.has-disc-tabs[data-mode="monitor"] #mf-bar #file-tabs { height: 46px; }
body.has-disc-tabs[data-mode="monitor"] #mf-bar .ft-tab {
  gap: 7px; padding: 0 12px; border-right-color: rgba(255,255,255,.06);
  font-size: 12px;
}
body.has-disc-tabs[data-mode="monitor"] #mf-bar .ft-tab.active {
  border-top: 2px solid #3794ff; background: #0b0c0f;
}
.roster-disc-gear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; border: 0; border-radius: 5px;
  background: transparent; color: #8b909a; flex: none; cursor: pointer;
}
.roster-disc-gear:hover { background: rgba(255,255,255,.08); color: #e6e8ec; }
.roster-disc-gear:focus-visible { outline: 2px solid #3794ff; }

/* #55c (user 04-08 "send key là được rồi"): the NAVIGATE parity strip (#term-controls — mouse buttons
   for a CLI menu) is redundant in terminal mode: every key already forwards straight to the CLI. */
body.monitor-term[data-mode="monitor"] #term-controls { display: none !important; }
/* #55c: in terminal mode the chatbox is a key-streamer — focusing it hands focus to the key-forward
   helper, so no caret/text ever shows here; the accent border + placeholder say where keys go. */
#cli-form.cli-forward { border-color: var(--accent, #4a9eff); }
/* #55h (user 04-08 "khi chatbox co xuống, container của cli còn nằm ở trên"): the composer SHELL kept
   its full chat padding (12px 12px 9px) around a 38px one-line strip, so ~30px of empty container
   floated between the terminal and the strip. Measured live: live-view.bottom=750 but form.top=780.
   The collapsed composer is a status line, not a chat box — drop the shell's own box entirely and let
   the strip supply the only padding, so the terminal grows into the space instead. */
#cli-form.cli-forward {
  padding: 0 !important; margin: 0 !important; gap: 0 !important; min-height: 0 !important;
}
/* streaming mode: the composer COLLAPSES — every normal child (input, toolbar, send) hides and the
   one-line status strip takes its place (user 04-08). */
#cli-form.cli-forward > :not(#cli-fwd-status) { display: none !important; }
/* the live-mode '>' prompt glyph (.cli-bar::before) also goes — nothing is typed HERE anymore. */
body.live-active #cli-form.cli-forward.cli-bar::before,
#cli-form.cli-forward::before { content: none !important; }
#cli-fwd-status { display: none; }
#cli-form.cli-forward #cli-fwd-status {
  display: flex; align-items: center; gap: 8px; padding: 10px 14px; cursor: pointer;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  color: var(--accent, #4a9eff);
}
#cli-fwd-status .fwd-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent, #4a9eff);
  animation: fwd-pulse 1.2s ease-in-out infinite;
}
#cli-fwd-status .fwd-hint { color: var(--text-dim, #8a8f98); }
@keyframes fwd-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
/* #57: honest failure note under "Load live terminal" — the daemon's own reject reason, not a stuck
   "Loading…" label. */
.term-load-err {
  margin-top: 8px; font-size: 12px; color: var(--danger, #e5534b);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; max-width: 460px;
}

#inspector { display: flex; flex-direction: column; min-height: 0; height: 100%; }
#inspector[hidden] { display: none; }
.inspector-tabs {
  /* same header height as the col1 switcher and col3 tabrow (52px) so the three
     panel tops line up — the strip is the Inspector panel's header row. */
  flex: none; display: flex; align-items: center; gap: var(--s-1); padding: 0 var(--s-2);
  min-height: 52px; box-sizing: border-box;
  border-bottom: 1px solid var(--border); background: var(--bg-elev);
}
.ins-tabctl { margin-left: auto; }
/* tabs stretch the 52px header row so the active 2px underline sits on the row's bottom edge */
.inspector-tabs .ins-tab { align-self: stretch; border-radius: 0; }
.ins-tab {
  display: inline-flex; align-items: center; padding: var(--s-1) var(--s-2);
  /* DESIGN v3 H2 computed (tpl 625-628): inactive tab = 11px mono 400 rgb(110,118,129); active =
     11px 600 rgb(230,232,236) + the 2px blue underline. (Was 12px/600/muted + fg-strong — every
     tab read bold and the active one brighter than the design.) */
  font: inherit; font-family: var(--font-mono); font-size: 11px; font-weight: 400; color: rgb(110, 118, 129);
  background: transparent; border: 0; border-bottom: 2px solid transparent;
  border-radius: var(--r-sm) var(--r-sm) 0 0; cursor: pointer;
}
.ins-tab:hover { color: rgb(230, 232, 236); background: var(--code-bg); }
.ins-tab.active { color: rgb(230, 232, 236); font-weight: 600; border-bottom-color: var(--accent); }
.ins-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ins-pane { flex: 1 1 auto; min-height: 0; overflow: auto; }
.ins-pane[hidden] { display: none; }
/* the inline diff appended under the Changes list when a file is opened. */
.ins-diff { border-top: 1px solid var(--border); margin-top: var(--s-1); }
/* PO 21-07 NORMAL mode: with a diff open, the pane splits vertically — the file LIST keeps a
   capped scroll region on top, the diff gets guaranteed space below (own scroll); ✕ closes. */
#ins-changes.has-diff, #ins-files.has-diff { display: flex; flex-direction: column; overflow: hidden; }
#ins-changes.has-diff > .ins-ch-list, #ins-files.has-diff > .ins-ch-list { flex: 0 1 42%; min-height: 110px; overflow: auto; }
#ins-changes.has-diff > .ins-diff, #ins-files.has-diff > .ins-diff { flex: 1 1 58%; min-height: 0; overflow: auto; margin-top: 0; display: flex; flex-direction: column; }
.ins-diff-head { display: flex; align-items: center; gap: 8px; }
/* the draggable list↔diff splitter (normal mode): 7px hit area, centred 1px line. */
.ins-split {
  flex: none; height: 7px; cursor: row-resize; touch-action: none;
  background: linear-gradient(to bottom, transparent 3px, rgba(255,255,255,.10) 3px, rgba(255,255,255,.10) 4px, transparent 4px);
}
.ins-split:hover { background: linear-gradient(to bottom, transparent 2px, var(--accent) 2px, var(--accent) 5px, transparent 5px); }
/* the ⤢-expanded 2-pane grid has no horizontal splitter — hide it there. */
body[data-mode="monitor"] .project-view[data-fullscreen="col2"] #ins-changes > .ins-split { display: none; }
.ins-diff-close {
  margin-left: auto; flex: none; background: none; border: 0; cursor: pointer;
  font-size: 11px; color: rgb(110, 118, 129); padding: 2px 6px; border-radius: 4px;
}
.ins-diff-close:hover { color: rgb(230, 232, 236); background: var(--code-bg); }
/* DESIGN H2 EXPAND (PO 21-07 crop 1): Inspector maximized (⤢ col2 fullscreen, Monitor) → the
   Changes pane becomes the 2-pane screen: file list LEFT (fixed track) · open diff RIGHT
   (full height, own scroll). Same DOM — the grid just re-slots .ins-diff into column 2. */
body[data-mode="monitor"] .project-view[data-fullscreen="col2"] #ins-changes {
  display: grid; grid-template-columns: minmax(300px, 380px) 1fr;
  grid-auto-rows: min-content; align-items: start; height: 100%;
}
body[data-mode="monitor"] .project-view[data-fullscreen="col2"] #ins-changes > * { grid-column: 1; min-width: 0; }
body[data-mode="monitor"] .project-view[data-fullscreen="col2"] #ins-changes > .ins-ch-list { align-self: stretch; height: 100%; overflow: auto; }
body[data-mode="monitor"] .project-view[data-fullscreen="col2"] #ins-changes > .ins-diff {
  grid-column: 2; grid-row: 1 / span 999; align-self: stretch; height: 100%; overflow: auto;
  border-top: none; border-left: 1px solid var(--border); margin-top: 0;
}
.ins-diff-head {
  padding: var(--s-1) var(--s-3); font-family: var(--font-mono); font-size: var(--t-xs);
  color: var(--accent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--bg-elev);
}
/* To-Do pane (design §5a: done/total + a checklist). */
.ins-todo-head { padding: var(--s-2) var(--s-3); font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); border-bottom: 1px solid var(--border); }
.ins-todo-list { list-style: none; margin: 0; padding: var(--s-1); display: flex; flex-direction: column; gap: 2px; }
.ins-todo-item { display: flex; align-items: flex-start; gap: var(--s-2); padding: var(--s-1) var(--s-2); font-size: var(--t-sm); border-radius: var(--r-sm); min-width: 0; }
/* QC 0.11.29: a board task row in the right panel opens the task on the team board. */
.ins-todo-item[data-task-id]:not([data-task-id=""]) { cursor: pointer; }
.ins-todo-item[data-task-id]:not([data-task-id=""]):hover { background: var(--hover, rgba(127,127,127,.08)); }
.cp-task.cp-task-focus { outline: 2px solid var(--accent, #6aa0ff); outline-offset: -2px; border-radius: var(--r-sm); }
.ins-todo-box { flex: none; font-family: var(--font-mono); color: var(--muted); }
.ins-todo-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ins-todo-id { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); }
/* A row with steps wraps (its checklist takes its own line); a title with basis 0 never pushes the
   "6/6" count or the status onto that second line — it shrinks and ellipsises instead. */
.ins-todo-item.has-steps .ins-todo-text { flex: 1 1 0; white-space: nowrap; }
.ins-todo-st, .ins-todo-who { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); }
.ins-todo-who { color: var(--accent); }
.ins-todo-team { margin-top: var(--s-2); border-top: 1px solid var(--border); }
.ins-todo-item.done .ins-todo-text { color: var(--faint); text-decoration: line-through; }
.ins-todo-item.done .ins-todo-box { color: var(--state-running); }
.ins-todo-item.active .ins-todo-box { color: var(--state-thinking); }

/* roster cards — DESIGN v3 H2, colours copied 1-1 from the render (tpl 156-229). The TEAM'S card list
   is ONE rounded box (border rgba(255,255,255,.07) r9, bg #0b0c0f) whose rows sit flush with a 1px
   gap-divider; a card has NO own border/radius — only its 3px state bar + (active/awaiting) tint. */
/* DESIGN 4b (v2.1): danh sách KHÔNG còn là một hộp chung — mỗi thẻ tự có viền/bo, list trong suốt. */
.roster-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; border: none; border-radius: 0; overflow: visible; background: transparent; }
.roster-card {
  /* DESIGN v2.2 1a/4b (Team Cockpit v2.dc.html :160/:322) — the card's WEIGHT, copied as numbers,
     not as impression. Measured 2026-09-18 against the artboard: every card here was transparent
     with the same .06 border whether awake, asleep or selected, so five cards read as one grey
     block ("mọi thứ cùng một trọng số" — the designer's own words). The artboard separates them:
     a normal card sits on #16181d with a .08 border; a sleeping one is dimmer (see .slept); the
     selected one is the blue wash. padding 11px 13px · radius 12 · 8px between cards. */
  position: relative;
  display: flex; flex-direction: column; gap: 8px;
  margin: 0 0 8px; padding: 11px 13px;
  background: #16181d; border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 12px; cursor: pointer; min-width: 0;
}
/* design :327/:338 — sleeping and exited cards step BACK: darker ground, fainter line, .75, grey name.
   This supersedes the PO 20-07 / 28-07 "no card dimming" notes below: the v2.2 artboard the PO
   approved on 2026-09-18 draws the dimming, and PO 18-09 chose it ("version của designer rõ ràng đẹp hơn"). */
.roster-card.slept, .roster-card.killed { background: #111318; border-color: rgba(255, 255, 255, 0.05); opacity: .75; }
.roster-card.slept .roster-name, .roster-card.killed .roster-name { color: #8b909a; }
.roster-card .roster-bar, .roster-card .roster-avatar { display: none; }
/* nhãn trạng thái mono 9px bên phải head + 3 chấm nháy khi busy (design: "không dùng progress bar") */
.roster-state-lbl { flex: none; font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: .04em; white-space: nowrap; color: #6e7681; }
.roster-state-lbl.st-running { color: #46d19e; }
.roster-state-lbl.st-thinking { color: #e0b341; }
.roster-state-lbl.st-awaiting { color: #e0b341; }
.roster-state-lbl.st-quota { color: #e0b341; }
.roster-state-lbl.st-slept, .roster-state-lbl.st-killed, .roster-state-lbl.st-idle { color: #6e7681; }
.roster-dots { display: inline-flex; gap: 3px; margin-left: 2px; flex: none; }
.roster-dots i { width: 3.5px; height: 3.5px; border-radius: 50%; background: #46d19e; animation: dmPulse 1.2s ease-in-out infinite; }
.roster-dots i:nth-child(2) { animation-delay: .2s; } .roster-dots i:nth-child(3) { animation-delay: .4s; }
@media (prefers-reduced-motion: reduce) {
  .roster-dots i, .disc-dots i, .roster-dot.pulse, .roster-dot.just-acted { animation: none; }
  .roster-dots i, .disc-dots i { opacity: .75; }
}
/* cây con 1 cấp: sub-agent · VM · terminal */
.roster-tree { display: flex; flex-direction: column; gap: 3px; margin-top: 2px; font-family: var(--font-mono); font-size: 10.5px; color: #8b909a; }
.roster-tree-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.roster-tree-con { color: #3a3f47; flex: none; }
.roster-tree-ico { flex: none; width: 12px; display: inline-flex; justify-content: center; color: #6e7681; }
.roster-tree-row.sub .roster-tree-ico { color: #a78bfa; }
.roster-tree-row.vm .roster-tree-ico { color: #3794ff; }
.roster-tree-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #c9ccd1; }
.roster-tree-tail { flex: none; font-size: 9.5px; color: #6e7681; }
.roster-tree-row.sub.run .roster-tree-tail, .roster-tree-row.term.live .roster-tree-tail { color: #46d19e; }
.roster-tree-row.term.busy .roster-tree-tail { color: #e0b341; }
.roster-tree-row.sub.failed .roster-tree-tail { color: #ff6b6b; }
.roster-tree-dot { width: 6px; height: 6px; border-radius: 50%; background: #46d19e; display: inline-block; }
.roster-tree-dot.stopped, .roster-tree-dot.suspended { background: #6e7681; }
.roster-tree-row.vm:hover .roster-tree-label, .roster-tree-row.term:hover .roster-tree-label { color: #3794ff; }
/* the 3px LEFT BAR (tpl 157/170/188/215) — PO 20-07: the bar wears the AGENT'S OWN COLOUR, ALWAYS
   (idle included — never grey-swapped, never transparent). nav.js renders it as a real <span
   class="roster-bar"> with the agent's palette colour inline; state repaints NOTHING here. */
.roster-bar { position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }
.roster-card:hover { border-color: rgba(255, 255, 255, 0.14); }
/* D-3/G-1 the team's shared DISCUSSION channel — the first row of the team's list.
   Literals copied from the design (Team Cockpit v2, the Discussion row): selected = blue wash +
   blue border + 11px radius, NOT the grey tint the member cards use. A room is a destination, so
   the design gives it a shape of its own rather than a variant of the person card. */
.roster-discussion {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 11px; border-radius: 11px; margin-bottom: 8px;
  cursor: pointer; min-width: 0;
  background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(255, 255, 255, 0.07);
}
.roster-discussion:hover { background: rgba(55, 148, 255, 0.05); }
/* design :304 style-hover on the room gear: present on hover/focus, not eight gears at once */
.roster-discussion .roster-disc-gear { opacity: 0; transition: opacity .12s; }
.roster-discussion:hover .roster-disc-gear, .roster-discussion:focus-within .roster-disc-gear, .roster-disc-gear:focus-visible { opacity: 1; }
.roster-discussion.active {
  background: rgba(55, 148, 255, 0.1);
  border: 1px solid rgba(55, 148, 255, 0.45);
}
.roster-discussion:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* the design's speech-bubble glyph, accent-coloured (design literal #3794ff) */
.roster-disc-glyph { display: inline-flex; align-items: center; justify-content: center; width: 13px; height: 13px; flex: none; color: #3794ff; }
.roster-disc-name { font-size: 12.5px; font-weight: 600; color: #e6e8ec; }
/* PUBLIC, stated on the row (design literal: green wash + green text) */
.roster-disc-mcp {
  font: 700 8px var(--font-mono), monospace; letter-spacing: 0.06em;
  padding: 1px 5px; border-radius: 4px;
  background: rgba(70, 209, 158, 0.14); color: #46d19e;
}
/* the right-hand slot is a STATE ("đang mở"), never a message count */
.roster-disc-state { margin-left: auto; font: 600 9px var(--font-mono), monospace; color: #6e7681; }
/* ARP-3 / R-6: the unread badge takes the state slot. Same vocabulary as the team head's
   awaiting badge (.roster-team-awaitbadge), in the room's accent instead of the alarm red. */
.roster-disc-badge { margin-left: auto; flex: none; font: 700 9px var(--font-mono), monospace; color: #e6f1ff; background: rgba(55, 148, 255, 0.85); border-radius: 8px; padding: 1px 6px; min-width: 8px; text-align: center; }

/* ROOM — GIÁ TRỊ CHÉP TỪ DESIGN, không tự chọn lại.
   Nguồn: "Team Cockpit v2.dc.html" khối 4b. Artboard viết style inline vì nó vẽ MỘT khoảnh khắc;
   sản phẩm cần class để mang state, nên cấu trúc chuyển thành class còn từng màu/kích thước giữ
   nguyên con số của design: nền #0b0c0f, bong bóng #15171c, viền rgba(255,255,255,.09), bo
   11px 11px 11px 4px, chữ 12.5px/1.55 màu #d4d7dd, accent #3794ff, guest #a78bfa.
   PO 17-09: "bạn không copy được toàn bộ html bên design qua được hay sao mà code lên khác hẳn
   design vậy" — bản trước tôi tự đặt màu theo biến sẵn có của app, nên ra một phòng họp khác. */
.discussion-view {
  position: relative;
  display: flex; flex-direction: column; min-height: 0; height: 100%;
  background: #0b0c0f;
}
/* `display:flex` sẽ ĐÈ thuộc tính `hidden` nếu không có dòng này — mọi pane cột-giữa khác đều đi
   thành cặp; thiếu nó thì pane không bao giờ ẩn và chiếm một chỗ trong lưới shell. */
.discussion-view[hidden] { display: none; }

/* room header — design: tên "<project> · Discussion", số người, cụm avatar, ⚙ */
.disc-head {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 11px 16px; border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  background: #0b0c0f;
  /* khung toa do cho .disc-settings (position:absolute) — menu do ra tu nut ⚙ nam trong day */
  position: relative;
}
.disc-head-title { font-size: 14px; font-weight: 600; color: #e6e8ec; }
.disc-head-count {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 400;
  color: #6e7681; white-space: nowrap;
}
.disc-head-spacer { flex: 1; }
.disc-head-faces { display: flex; align-items: center; }
/* avatar chồng mép -6px; viền NÉT ĐỨT = guest vào qua MCP, nét liền = member trong team. Hai hạng
   người này có quyền khác nhau nên phải phân biệt được bằng mắt, đúng như design tách chúng. */
.disc-face {
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  font: 600 8.5px var(--font-sans, 'Geist', sans-serif);
  border: 1.5px solid #0b0c0f; margin-left: -6px; flex: none;
}
.disc-face:first-child { margin-left: 0; }
.disc-face.guest { border: 1.5px dashed rgba(167, 139, 250, 0.7); }
.disc-gear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  background: rgba(255, 255, 255, 0.06); color: #e6e8ec;
  border: 0; cursor: pointer; padding: 0;
}
.disc-gear:hover { background: rgba(255, 255, 255, 0.1); }

/* ROOM SETTINGS — design vẽ mở sẵn để trình bày; ở đây đóng tới khi bấm ⚙ */
.disc-settings {
  position: absolute; right: 16px; top: 50px; width: 280px; z-index: 6;
  background: #16181d; border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 11px; box-shadow: 0 20px 48px rgba(0, 0, 0, 0.6); overflow: hidden;
}
.disc-settings[hidden] { display: none; }
.disc-settings-head {
  padding: 9px 13px; border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  font: 600 9px var(--font-mono); letter-spacing: 0.12em; color: #6e7681;
}
.disc-settings-body { padding: 6px; }
.disc-set-row {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
}
.disc-set-row:hover { background: rgba(255, 255, 255, 0.05); }
.disc-set-share { background: rgba(70, 209, 158, 0.08); }
.disc-set-share:hover { background: rgba(70, 209, 158, 0.13); }
.disc-set-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; flex: none; color: #46d19e;
}
.disc-set-gap { width: 16px; flex: none; }
.disc-set-main { flex: 1; min-width: 0; }
.disc-set-title { font: 600 12px var(--font-sans, 'Geist', sans-serif); color: #e6e8ec; }
.disc-set-url {
  font: 400 10px var(--font-mono); color: #6e7681;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.disc-set-label { font: 500 12px var(--font-sans, 'Geist', sans-serif); color: #c4c8ce; flex: 1; }
.disc-set-meta { font: 400 10.5px var(--font-mono); color: #6e7681; }
.disc-set-copy {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 6px; flex: none;
  background: rgba(255, 255, 255, 0.07); color: #46d19e; border: 0; cursor: pointer; padding: 0;
}
.disc-toggle {
  width: 26px; height: 15px; border-radius: 99px; position: relative; flex: none;
  background: rgba(255, 255, 255, 0.16);
}
.disc-toggle[aria-checked="true"] { background: #46d19e; }
.disc-knob {
  position: absolute; left: 2px; top: 2px; width: 11px; height: 11px;
  border-radius: 50%; background: #0b0c0f;
}
.disc-toggle[aria-checked="true"] .disc-knob { left: auto; right: 2px; }

/* messages */
.disc-thread {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 16px; display: flex; flex-direction: column; gap: 13px;
}
.disc-day {
  align-self: center; font: 400 10px var(--font-mono);
  color: #5a6069; letter-spacing: 0.06em;
}
.disc-msg { display: flex; gap: 10px; }
.disc-avatar {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font: 600 10px var(--font-sans, 'Geist', sans-serif);
}
.disc-avatar.guest { border: 1.5px dashed rgba(167, 139, 250, 0.6); }
.disc-msg-body { min-width: 0; }
/* A member's tool card mirrored into the room (T39). Its own class: `.disc-tool` is the 26px toolbar
   button, and sharing it squeezed the card into a one-character column (PM #7549 screenshot). */
.disc-toolcard .disc-msg-body { flex: 1 1 auto; width: 100%; }
/* nhãn nguồn: "agent→agent", "(người)", "guest · team security · via MCP" — người đọc phải biết
   câu này do AI nói với nhau hay do người xen vào, vì hai thứ đó đáng tin khác nhau. */
.disc-meta {
  font: 500 10px var(--font-mono); color: #6e7681; margin-bottom: 3px;
}
.disc-meta .disc-role { color: #3794ff; }
.disc-meta .disc-role-lead {
  color: #3794ff; font-weight: 600; font-size: 9px;
  background: rgba(55, 148, 255, 0.15);
  padding: 1px 4px; border-radius: 4px; margin-left: 4px;
  vertical-align: middle; display: inline-block;
}
.disc-meta .disc-src { color: #8b909a; }
.disc-meta .disc-src-guest { color: #a78bfa; }
.disc-bubble {
  background: #15171c; border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 11px 11px 11px 4px; padding: 9px 12px;
  font-size: 12.5px; line-height: 1.55; color: #d4d7dd; max-width: 640px;
}
.disc-bubble.guest { border-color: rgba(167, 139, 250, 0.28); }
.disc-bubble .disc-mention { color: #3794ff; }
.disc-avatar.human { background: linear-gradient(135deg, #3794ff, #8b5cf6); color: #fff; }
.ft-tab-team { font: 400 10px var(--font-mono); color: #6e7681; margin-left: 4px; }
.disc-guest-out { display: flex; align-items: center; gap: 8px; padding: 4px 12px 8px 40px; }
.disc-guest-out[hidden] { display: none; }
.disc-guest-token { flex: 1 1 auto; min-width: 0; font: 400 10px var(--font-mono); color: #8b909a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.disc-bubble a.disc-link { color: #3794ff; text-decoration: underline; text-decoration-color: rgba(55,148,255,.45); word-break: break-all; }
.disc-bubble a.disc-link:hover { text-decoration-color: #3794ff; }
.disc-bubble .disc-pane-link { color: #3794ff; text-decoration: none; cursor: pointer; }
.disc-bubble .disc-pane-link:hover { text-decoration: underline; }
.disc-bubble code, .disc-bubble .disc-mono { font-family: var(--font-mono); color: #c4c8ce; }
/* D-3 "showing N of M" — chỉ in khi cửa sổ NGẮN hơn phòng, để lịch sử đủ và lịch sử bị cắt không
   bao giờ trông giống nhau. */
.disc-more {
  align-self: center; font: 400 10px var(--font-mono); color: #6e7681;
  padding: 5px 9px; border-radius: 7px; background: rgba(255, 255, 255, 0.05);
}
.disc-empty { align-self: center; font-size: 12px; color: #6e7681; padding: 24px 0; }

/* "builder đang trả lời" */
.disc-typing {
  flex: none; display: flex; align-items: center; gap: 7px;
  padding: 0 16px 10px 52px; font: 400 11px var(--font-mono); color: #6e7681;
}
.disc-typing[hidden] { display: none !important; }
.disc-dots { display: inline-flex; gap: 3px; }
.disc-dots i {
  width: 3.5px; height: 3.5px; border-radius: 50%; background: #46d19e;
  animation: dmPulse 1.2s ease-in-out infinite;
}
.disc-dots i:nth-child(2) { animation-delay: 0.2s; }
.disc-dots i:nth-child(3) { animation-delay: 0.4s; }

/* composer — design 4b: ô nhập + nút gửi xanh vuông 34px cạnh phải, một hàng */
/* Design v2.2: a rounded box with the text on top and a tool row under it — the same shape the
   agent composer has. v2.1 drew a single row (input + send) and the room ended up without the
   controls the chat beside it had; the numbers below are the design's own. */
.disc-composer {
  position: relative; flex: none; margin: 0 16px 14px; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 9px;
  background: #0e1014; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 14px;
}
.disc-composer:focus-within { border-color: rgba(55, 148, 255, 0.35); }
.disc-toolbar { display: flex; align-items: center; gap: 7px; }
.disc-toolbar-spacer { flex: 1; }
.disc-tool {
  width: 26px; height: 26px; border-radius: 7px; border: 1px solid rgba(255, 255, 255, 0.1);
  background: transparent; color: #8b909a; cursor: pointer; display: grid; place-items: center;
  font: 600 14px var(--font-mono); padding: 0;
}
.disc-tool:hover { color: #e6e8ec; border-color: rgba(255, 255, 255, 0.22); }
.disc-tool:focus-visible { outline: 2px solid #3794ff; outline-offset: 1px; }
.disc-pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 8px; border-radius: 7px;
  background: #16181d; border: 1px solid rgba(255, 255, 255, 0.08);
  font: 400 10px var(--font-mono); color: #8b909a;
}
.disc-pill[hidden] { display: none; }
.disc-as { font-size: 11px; color: #c4c8ce; font-family: var(--font-ui, inherit); }
.disc-as-dot { width: 6px; height: 6px; border-radius: 50%; background: #3794ff; flex: none; }
/* The box is now the composer's border, so the textarea inside it is bare — a second border
   around the text drew a box inside a box. Design v2.2: 44px min, 116px max, 1.62 line height. */
.disc-input {
  flex: 1; min-width: 0; min-height: 44px; max-height: 116px;
  padding: 0; border-radius: 0; box-sizing: border-box;
  background: transparent; border: 0;
  font-family: inherit; font-size: 13px; line-height: 1.62; color: #e6e8ec;
  resize: none;
}
.disc-input::placeholder { color: #5a6069; }
/* placeholder phủ theo design: chữ thường xám + @tên mono */
.disc-ph { position: absolute; left: 14px; top: 12px; pointer-events: none; font-size: 13px; line-height: 1.62; color: #5a6069; white-space: nowrap; }
.disc-ph b { font-family: var(--font-mono); font-weight: 400; color: #6e7681; margin: 0 2px; }
.disc-ph[hidden] { display: none; }
.disc-input:focus { outline: none; }
.disc-send {
  flex: none; width: 34px; height: 34px; padding: 0; border-radius: 10px;
  display: grid; place-items: center;
  background: #3794ff; border: 0; color: #fff; cursor: pointer;
}
.disc-send:hover { filter: brightness(1.08); }
.disc-send:active { transform: scale(0.96); }
.disc-send svg { display: block; }
.disc-send:disabled { pointer-events: none; opacity: 0.4; cursor: default; }

/* selected/active (design leader tpl 169): the HIGHLIGHT is only the blue bg tint — bar + dot keep
   the agent's own colour. */
.roster-card.active { background: rgba(55, 148, 255, 0.08); border-color: rgba(55, 148, 255, 0.4); opacity: 1; }
/* PO 20-07: idle KEEPS full colour (name #e6e8ec, agent bar/dot) — no card-level dimming. The old
   opacity:.65 washed every idle card grey ("màu xám chìm"). Idle-vs-busy is the dot's pulse only. */
/* awaiting keeps its red card tint + ⚠ (mock qa) — the bar/dot colour stays the agent's own. */
/* design :305/:143 — AWAITING is the AMBER card (rgba(224,179,65,.07) / .3 border), not red: red is
   reserved for failure, and a question is not a failure. */
.roster-card.awaiting { background: rgba(224, 179, 65, 0.07); border-color: rgba(224, 179, 65, 0.3); }
/* H1.6 SLEEPING (PO 28-07): a checkpointed agent whose RAM was freed on purpose. It is NOT an error
   and NOT dead — chat still works, the daemon auto-wakes it when work arrives — so it gets NO red
   tint and NO card dimming (the 20-07 note above stands: dimming washed cards grey and PO rejected
   it). The signal is the status line alone: a dim slate word, quieter than "working…", plus the moon.
   Before this the card said nothing at all and a sleeping agent read exactly like an awake-but-quiet
   one — the data was in the roster payload the whole time and only the card ignored it. */
.roster-status.roster-slept { color: rgb(122, 130, 146); font-style: italic; }
.roster-status.roster-slept::before { content: '☾ '; font-style: normal; }
/* #149-R1: gone member says so — grey text + hollow-dot prefix, card slightly dimmed */
.roster-status.roster-killed { color: rgb(122, 130, 146); font-style: italic; }
.roster-status.roster-killed::before { content: '○ '; font-style: normal; }
/* R7-MODEL item 8 — a member the daemon REFUSED is not a session that ran and exited. It carries a
   stated reason (state:"failed" + error, from agentspawn.go pushFailedAgent) and that reason is
   ACTIONABLE (wrong model/effort/provider, a guest that cannot host a pane, a missing folder), so it
   reads as a fault in red rather than the grey "gone" of a normal exit. Same one-line clamp as every
   other status line — the full sentence lives in the title tooltip. */
.roster-status.roster-failed {
  color: rgb(255, 107, 107);
  font-style: normal;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.roster-status.roster-failed::before { content: '⚠ '; }
/* #57 (PO: "sao dev2 hết quota mà UI không báo"): a member parked on a provider quota/rate-limit —
   busy:false but activityDetail carries the daemon's "⏳ Quota reached — resets in …" banner — used to
   render as plain 'idle', pixel-identical to a healthy quiet agent. Amber (not red — this is not an
   error the user broke, it is a provider limit that self-clears) + the same badge-slot-outranks-role
   pattern AWAITING uses, so it reads at a glance across the whole roster, not just the open thread. */
.roster-await-tag.roster-quota-tag { color: rgb(224, 179, 65); }
.roster-status.roster-quota { color: rgb(224, 179, 65); }
.roster-status.roster-quota::before { content: '⏳ '; font-style: normal; }
.roster-card.quota { background: rgba(224, 179, 65, 0.05); }
/* dot — DESIGN v3 tpl 163/176/194/221 + PO 20-07: the dot wears the AGENT'S OWN COLOUR always
   (inline from nav.js, idle included); "lúc nào busy thì nhá nhá thôi" — busy-ish adds .pulse.
   Running additionally gets its halo (inline box-shadow, agent-colour glow). */
.roster-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.roster-dot.pulse { animation: dmPulse 1.4s ease-in-out infinite; }
/* H5 amber-blink-3s (design dc:1370 "Dot nháy cam 3s khi agent vừa hoạt động"): when the agent JUST acted,
   the dot gets an AMBER ring on top of its state colour for ~3s — a distinct "just did something" signal.
   The class is added by nav.js for the recentActive window and removed on the re-render app.js schedules at
   window-expiry. Overrides the state colour while active, but reuses the design's dmPulse 1.4s (dc:1379)
   rather than a self-authored keyframe/timing. */
.roster-dot.just-acted { animation: dmPulse 1.4s ease-in-out infinite; box-shadow: 0 0 0 3px var(--state-thinking-glow, rgba(224, 179, 65, 0.35)); }
.roster-main { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px; }
.roster-head { display: flex; align-items: center; gap: 7px; min-width: 0; }
.roster-head-spacer { flex: 1 1 0%; }
.roster-name { font-weight: 600; color: rgb(230, 232, 236); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* design computed = rgb(230,232,236), 13px 600 */
/* #7 (B2): unread pill on a roster card head — accent bg, mono digits, hidden at 0 (rendered only when unread>0). */
.roster-unread { flex: none; min-width: 16px; height: 16px; padding: 0 5px; display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: 10px; font-weight: 700; line-height: 1; color: #fff; background: rgb(55, 148, 255); border-radius: 8px; }
/* Member chip: pending permission requests (PO Luat 29/09) — amber, a button that opens the list (app.js). */
.roster-perm { flex: none; height: 16px; padding: 0 5px; display: inline-flex; align-items: center; gap: 3px; font-family: var(--font-mono); font-size: 10px; font-weight: 700; line-height: 1; color: #1b1300; background: rgb(240, 180, 41); border: none; border-radius: 8px; cursor: pointer; }
.roster-perm:hover { filter: brightness(1.08); }
.roster-perm:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.perm-chip-pop .perm-chip-head { font-size: var(--t-sm); font-weight: 600; color: var(--fg-strong); margin: 0 24px 8px 0; }
.perm-chip-pop .tcard + .tcard { margin-top: 8px; }
.roster-type { font-family: var(--font-mono); font-size: var(--t-label); color: var(--faint); text-transform: uppercase; letter-spacing: .06em; flex: none; }
/* wireframe §2/§297: PROJECT-ROLE badge — LEAD / MEMBER / STANDALONE — a small mono pill next to the
   name. NOT the system-prompt persona (that used .roster-type and is no longer shown as a badge). */
.roster-badge {
  font-family: var(--font-mono); font-size: 8px; line-height: 1; letter-spacing: .06em;
  text-transform: uppercase; flex: none; padding: 2px 5px; border-radius: 4px;
  border: 1px solid transparent; font-weight: 700; white-space: nowrap;
}
/* design literals — LEAD tpl 178 · MEMBER/SOLO tpl 196/223 (bordered, 1px 5px) · SRC tpl 197. */
.roster-badge.lead { color: rgb(55, 148, 255); background: rgba(55, 148, 255, 0.15); }
.roster-badge.member, .roster-badge.standalone { color: rgb(139, 144, 154); border-color: rgba(255, 255, 255, 0.14); background: transparent; padding: 1px 5px; }
.roster-badge.src { color: rgb(197, 134, 192); background: rgba(197, 134, 192, 0.14); }
/* DESIGN v3 H2 (qa card, tpl 165/166): an AWAITING card's head shows "⚠ AWAITING" in the badge slot —
   ⚠ red rgb(255,107,107) + "AWAITING" 9px mono 600 red. The role badge yields to the state. */
.roster-await-tag { flex: none; font-family: var(--font-mono); font-size: 9px; font-weight: 600; color: rgb(255, 107, 107); white-space: nowrap; }
.roster-await-warn { font-size: 11px; }
/* wireframe §2: secondary identity line "provider · daemon/machine" (dim, single-line clamped). */
.roster-subline {
  font-size: var(--t-xs); color: var(--faint); font-family: var(--font-mono); letter-spacing: .01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
/* wireframe §2: the "◇ STANDALONE" section at the bottom — a team-less bucket, visually distinct
   from a real "▾ TEAM <name>" section (diamond glyph, no team gear/dot). */
.roster-team-kw { display: none; } /* PO 18-09: tên team TỰ NÓ là tiêu đề — không lặp lại từ "TEAM" */
.roster-standalone-glyph { width: 12px; flex: none; color: rgb(110, 118, 129); text-align: center; }
.roster-standalone-name { color: rgb(139, 144, 154); font-weight: 600; letter-spacing: .06em; } /* design computed STANDALONE */
.roster-status, .roster-await {
  font-size: 11px; font-family: var(--font-mono); min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.roster-status { color: rgb(139, 144, 154); }   /* design tpl 185/202/228 */
.roster-await { color: #e0b341; }    /* design v2.2 :307 — the quoted question, amber like its card */
/* design tpl 204: the purple "sub-agent (<ws>)" line — 8px mono + a 10px container glyph. */
.roster-subagent { display: inline-flex; align-items: center; gap: 3px; font-family: var(--font-mono); font-size: 8px; color: rgb(197, 134, 192); }
/* progress (design tpl 183/184): 3px track rgba(255,255,255,.08) r2; the fill is the REAL ctx%
   (inline width) with the green→blue gradient. Unknown ctx → .indeterminate keeps the sweeping
   segment (honest unknown, #N4) instead of a fake fixed fill. */
.roster-progress { position: relative; display: block; height: 3px; border-radius: 2px; background: rgba(255,255,255,0.08); overflow: hidden; }
.roster-progress-bar { position: absolute; top: 0; left: 0; height: 100%; background: linear-gradient(90deg, rgb(70, 209, 158), rgb(55, 148, 255)); border-radius: 2px; }
.roster-progress-bar.indeterminate { width: 35%; animation: rosterProg 1.4s var(--ease, ease-in-out) infinite; }
@keyframes rosterProg { 0% { left: -35%; } 100% { left: 100%; } }
@media (prefers-reduced-motion: reduce) {
  /* no sweep → a full-width bar that pulses opacity (still clearly "working", never a fake % fill). */
  .roster-progress-bar { left: 0; width: 100%; animation: rosterPulse 1.6s ease-in-out infinite; }
  @keyframes rosterPulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.9; } }
}
.roster-empty { color: var(--muted); padding: var(--s-3); }
/* #94 (dc:836) — col-1 tab: icon + label, mono 11px, pad 8/9. */
.c1-tab, .vs-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600; cursor: pointer;
  background: transparent; color: var(--faint); border: 1px solid transparent;
  border-radius: var(--r-sm); padding: 8px 9px;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.c1-tab-ico { flex: none; display: inline-flex; }
.c1-tab-ico svg { width: 14px; height: 14px; }
/* #94 responsive-collapse (dc:1083) — the ACTIVE tab shows its label; INACTIVE tabs collapse to
   icon-only. The Changes badge (+/−) is exempt — it ALWAYS shows. */
.c1-tab:not(.active) .c1-tab-label { display: none; }
.c1-tab:hover, .vs-btn:hover { color: var(--fg-strong); background: var(--code-bg); }
.c1-tab.active, .vs-btn.active { color: #7fb0f0; background: rgba(55, 148, 255, 0.14); } /* dc:836 active */
.c1-tab:focus-visible, .vs-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
#file-tree, #terminal-list, #editor-view, #diff-view, #live-view, #col2-empty { flex: 1 1 auto; min-height: 0; overflow: auto; }
/* #91 #3 dynamic tab-strip (VS Code-style) — only the items the user opened. */
/* H10g: the tab strip no longer scrolls horizontally — overflow tabs fold into the ▾N chooser
   (renderCol2Tabs caps the visible count), so the strip stays fixed-width (design dc:1727). */
/* #B7b (PO): the col-2 tab-strip SCROLLS HORIZONTALLY (all tabs rendered, no ▾N dropdown). nowrap keeps
   them on one line; a thin scrollbar appears only when they overflow. */
.file-tabs { display: flex; gap: 0; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; flex: 1; min-width: 0; scrollbar-width: thin; }
.file-tabs::-webkit-scrollbar { height: 6px; }
.file-tabs::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.14); border-radius: 999px; }
.file-tabs::-webkit-scrollbar-track { background: transparent; }
.ft-tab { flex: none; } /* tabs keep their intrinsic width — they scroll, never shrink to fit */
.ft-tab {
  display: inline-flex; align-items: center; gap: var(--s-1); flex: none; cursor: pointer;
  height: 100%; padding: 0 var(--s-2); border-right: 1px solid var(--border); max-width: 200px; /* H16 dc:859 (180→200) */
  /* dc:859 — the active-tab accent is a TOP border (matches the col-3 agent tab-pill, e0d7825),
     not a bottom one. --accent=#3794ff, --bg=#0b0c0f match the design hex exactly. */
  color: var(--muted); font-size: var(--t-sm); border-top: 2px solid transparent;
}
.ft-tab:hover { background: var(--code-bg); }
.ft-tab.active { color: var(--fg-strong); background: var(--bg); border-top-color: var(--accent); }
.ft-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ft-tab-icon { font-size: var(--t-xs); flex: none; }
.ft-tab-icon-disc { display: inline-flex; color: #3794ff; }
.ft-tab-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); }
.ft-tab-close {
  flex: none; border: none; background: none; color: var(--faint); cursor: pointer;
  font-size: var(--t-xs); line-height: 1; padding: var(--s-1) var(--s-1); border-radius: var(--r-sm); margin-left: var(--s-1);
}
.ft-tab-close:hover { background: var(--border-strong); color: var(--fg-strong); }
.ft-tab-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* GAP-17 (§5b): an UNSAVED editor tab shows an amber dot in place of the ✕; hovering the tab
   swaps the dot → ✕ so it can still be closed (design: "unsaved amber dot / hover-✕ close"). */
.ft-tab-dot { flex: none; color: var(--warn, #e0b341); font-size: 9px; margin-left: var(--s-1); line-height: 1; }
.ft-tab-dirty .ft-tab-close { display: none; }
.ft-tab-dirty:hover .ft-tab-close, .ft-tab-dirty:focus-within .ft-tab-close { display: inline-flex; }
.ft-tab-dirty:hover .ft-tab-dot, .ft-tab-dirty:focus-within .ft-tab-dot { display: none; }
/* H10g (dc:1727) — the editor tab-strip ▾N overflow button + its file chooser (mirrors G-04). */
.ft-tab-overflow {
  display: inline-flex; align-items: center; gap: 5px; flex: none; cursor: pointer;
  padding: 0 10px; border: none; border-left: 1px solid var(--border); background: transparent;
  font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted);
}
.ft-tab-overflow:hover { color: var(--fg-strong); background: var(--panel-2); }
.ft-of-caret { font-size: 9px; }
.ft-of-count { font-size: var(--t-xs); }
.ft-of-dot { color: var(--warn, #e0b341); font-size: 8px; }
.ft-overflow-menu {
  position: fixed; z-index: 60; min-width: 200px; max-width: 240px; max-height: 60vh; overflow: auto;
  background: #0e1014; border: 1px solid var(--border-strong); border-radius: 12px;
  box-shadow: var(--shadow); padding: 6px;
}
.ft-overflow-menu[hidden] { display: none; }
.ft-of-item {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 9px; border-radius: 7px;
  border: none; background: transparent; color: var(--fg); font: inherit; font-size: 12px; cursor: pointer; text-align: left;
}
.ft-of-item:hover { background: var(--accent-wash); }
.ft-of-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); }
/* col-2 empty-state (no tab open) — centered hint. */
/* col-2 welcome (no file open) — a centred, styled placeholder on the editor
   surface, never a black void. col-2 keeps a subtle elevated background so the
   empty pane reads as "no file yet", not "broken". */
#shell-col2 { background: var(--bg); }
.col2-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: var(--s-6); gap: var(--s-2);
  color: var(--muted); background: var(--bg);
}
.col2-empty .empty { color: var(--muted); max-width: 320px; }
/* col-3 cockpit interior: tabbar/thread/chip/footer/composer stack; thread scrolls. */
#shell-col3 > #thread { flex: 1 1 auto; min-height: 0; overflow: auto; }
#shell-col3 > #tabbar, #shell-col3 > #footer-bar, #shell-col3 > #cli-form,
#shell-col3 > .cockpit-toolbar, #shell-col3 > #chip-row { flex: none; }
.cockpit-toolbar { display: flex; gap: var(--s-1); padding: var(--s-1) var(--s-2); border-bottom: 1px solid var(--border); }
/* terminal-list rows */
/* §5b/§5a Changes list (col-1 Changes tab) — changed files vs HEAD, click → diff. */
.changes-list { overflow-y: auto; min-height: 0; }
.ch-head { padding: var(--s-2) var(--s-3); font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); border-bottom: 1px solid var(--border); }
.ch-list { list-style: none; margin: 0; padding: var(--s-1); }
/* dc:712 — the inspector Changes-row uses literal padding 9px 11px (not --s-1/--s-2 = 4/8); radius
   --r-sm (8) already matches dc:8; the .ch-name font --t-sm (12) already matches dc:12 — left as-is. */
.ch-file { display: flex; align-items: center; gap: var(--s-2); padding: 9px 11px; border-radius: var(--r-sm); cursor: pointer; min-width: 0; font-size: var(--t-sm); }
.ch-file:hover { background: var(--accent-wash); }
.ch-status { flex: none; font-family: var(--font-mono); font-size: 10px; font-weight: 700; width: 14px; text-align: center; }
.ch-status.ch-m { color: var(--state-thinking); }
.ch-status.ch-a { color: var(--state-running); }
.ch-status.ch-d { color: var(--state-awaiting); }
/* DESIGN v3 H2 computed: file name = rgb(230,232,236) 11px (was fg-2 196 @12px — dimmer than design);
   the ± counts are 11px 600 green/red. */
.ch-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); font-size: 11px; color: rgb(230, 232, 236); }
.ch-counts { flex: none; font-family: var(--font-mono); font-size: 11px; }
.ch-add { color: rgb(70, 209, 158); font-weight: 600; }
.ch-del { color: rgb(255, 107, 107); font-weight: 600; }

/* GAP-2 (§H4b): the CHANGES view groups files by status (MODIFIED / ADDED / DELETED). */
.ch-group + .ch-group { margin-top: var(--s-1); }
.ch-group-head {
  padding: var(--s-1) var(--s-3); font-family: var(--font-mono); font-size: 10px;
  font-weight: 700; letter-spacing: 0.08em; color: var(--muted);
}
.ch-group-n { color: var(--faint); font-weight: 400; margin-left: 4px; }
/* a DELETED file's name is struck through (design). */
.ch-file-del .ch-name { text-decoration: line-through; color: var(--faint); }

/* GAP-2 (§H4b c): the CHANGES tab badge — `(N)` changed files, hidden at 0. */
.c1-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; margin-left: 5px; padding: 0 4px;
  border-radius: 999px; font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  background: var(--accent); color: #04101f; vertical-align: middle;
}
.c1-badge[hidden] { display: none; }

.terminal-list { list-style: none; }
.tl-item { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-2) var(--s-3); min-height: 36px; cursor: pointer; border-radius: var(--r-sm); font-family: var(--font-mono); font-size: var(--t-sm); }
.tl-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tl-item:hover { background: var(--code-bg); }
.tl-item.active { background: rgba(55,148,255,0.16); }
/* WEB-PARITY tab P0/P1: the terminal/robot SVG glyph in a terminal-list row + a session tab. */
.tl-icon { flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--muted); }
.tl-icon svg { width: 16px; height: 16px; }
.tab-icon { flex: none; display: inline-flex; align-items: center; justify-content: center; }
.tab-icon svg { width: 15px; height: 15px; }
/* chip popover: desktop dropup (anchored, small) — mobile overrides to a push-sheet below. */
.chip-popover {
  /* dc (H5 popover) LITERALS — width 400, radius 14, padding 13/16 (not the generic 280-360 / --r-md
     10 / --s-2/--s-3 8-12 tokens, which read narrow + square vs the design). Element-scoped: the
     global --r-md/--s-N are untouched (design uses radius 8/9/12/14 across components, no 1 scale). */
  position: absolute; z-index: 50; width: 400px; max-width: min(400px, 92vw); max-height: 60vh; overflow: auto;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 14px;
  box-shadow: var(--shadow); padding: 13px 16px;
}
.cp-close {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  border: none; background: none; color: var(--muted); cursor: pointer;
  font-size: var(--t-md); line-height: 1; padding: var(--s-1) var(--s-1); border-radius: var(--r-sm);
}
.cp-close:hover { background: var(--code-bg); color: var(--fg-strong); }
.cp-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* mobile (<720): the SAME popover becomes a full-screen push-sheet (PO control-adapt
   — never a tiny anchored box nhồi-mobile). left/bottom from JS are overridden. */
@media (max-width: 719px) {
  .chip-popover {
    position: fixed !important; inset: 0 !important; left: 0 !important; bottom: 0 !important;
    width: 100%; max-width: none; max-height: none; min-width: 0;
    border: none; border-radius: 0; padding: var(--s-4) calc(16px + env(safe-area-inset-bottom));
    /* qc964 #5: the fixed hamburger (.mob-nav-toggle, top 2px, 44px, z 65) floats over this sheet;
       the sheet starts its content below that lane so the title ("Scheduled (n)") is not covered. */
    padding-top: 56px;
    z-index: 60;
  }
  .chip-popover .cp-agent, .chip-popover .cp-todo { min-height: 44px; }  /* 44px tap */
  .cp-close { font-size: var(--t-lg); padding: var(--s-2) var(--s-2); }

  /* ───────── C10 §8 mobile — redesign components ───────── */
  /* Rail → a slim BOTTOM bar (the 3-panel cockpit already stacks; the rail moves out of
     the way so the content gets full width). Icons horizontal, ≥44px tap targets. */
  .shell-view { flex-direction: column; }
  .shell-main { height: auto; flex: 1 1 auto; min-height: 0; }
  /* #93 (dc:760) — on mobile the .mob-tabbar (Team/Chat/Inspect) IS the bottom nav; the desktop
     .nav-rail must be HIDDEN, not repositioned into a SECOND 56px bottom bar (the two stacked at the
     same y = the double-bottom-bar bug). Its panels (rail-panels) also hide — they'd column-spill
     off-screen. Nav on mobile = .mob-tabbar only. */
  .nav-rail { display: none; }
  .rail-panels { display: none; }
  /* LOW-6: the hamburger opens the same rail as a left drawer over the content. */
  .mob-nav-toggle {
    display: grid; place-items: center; position: fixed; top: 2px; left: 8px; z-index: 65;
    width: 44px; height: 44px; border-radius: 10px; border: 1px solid rgba(255, 255, 255, .12);
    background: #111318; color: #e6e8ec; font-size: 20px; cursor: pointer;
  }
  /* QC 0.11.32: the fixed button sat ON the appbar's left edge and hid the first letters of every
     page title ("eam Monitor", "y API keys"). The appbar is the one header every shell screen shares
     on a phone, so it reserves the button's lane (8px + 44px + 8px) once here — no per-page patch.
     top:2px centres the 44px button inside the 48px bar instead of hanging past its border. */
  .appbar { padding-left: 60px; }
  body.mob-nav-open .nav-rail {
    display: flex; position: fixed; top: 0; left: 0; bottom: 0; height: 100dvh; z-index: 70;
    box-shadow: 0 0 40px rgba(0, 0, 0, .6);
  }
  body.mob-nav-open .mob-nav-scrim:not([hidden]) { display: block; position: fixed; inset: 0; z-index: 69; background: rgba(0, 0, 0, .5); }
  .shell-main { order: 1; }
  /* stat strip → 2 columns so the five cards don't overflow a phone. */
  .stat-strip { grid-template-columns: 1fr 1fr; }
  /* terminal view → the input rail drops BELOW the frame (no 288px side column). nowrap: a wrapping column
     sizes .live-main to its content, so the frame grew with the xterm grid and every geometry sync measured
     the grid it had just set (200→178→…→49 cols); one line stretches it to the panel's width instead. */
  .live-view { flex-direction: column; flex-wrap: nowrap; }
  .term-input-rail { width: 100%; border-left: none; border-top: 1px solid var(--border); }
  /* roster / popover rows ≥44px tap. */
  .roster-card, .cp-row { min-height: 44px; }
  /* Home head wraps (title + search + button stack cleanly). */
  .projects-head { flex-wrap: wrap; }
  .project-search { max-width: none; order: 3; flex-basis: 100%; margin-left: 0; }
}

@media (min-width: 1024px) {
  /* #84 gap#1: design H4 (dc:167) navigates via the RAIL (brand + views + account/logout), so the
     desktop PROJECT screen has NO top appbar — the rail + the per-column headers are the chrome.
     Home/Login keep the appbar (data-screen="projects" / login). Mobile keeps it too (<1024). */
  body[data-screen="project"] .appbar { display: none; }
  /* Screen A: FULL-WIDTH, 24px gutter (COPY design — no re-centering). The old rule capped
     head/form/grid at 1600px + margin:auto, which re-introduced the centering the design does
     NOT have (→ the 174px left-gap + narrow cards + the full-width platform card). Each row now
     owns its design width: head/strip/grid = full-frame 24px gutter, form = 440px compact. */
  .projects-view { max-width: none; }

  /* Screen B (W1 struct-B): a CLEAN 3-COLUMN cockpit (wireframe 91b) — col-1 files
     | col-2 editor/diff (SEPARATE) | col-3 cockpit (SEPARATE: tabbar + thread +
     composer + chip-row). PO P0: the cockpit's tab-strip must stay INSIDE col-3 and
     NOT span across the editor. So col-3 is a FIXED column (the tabbar is confined
     to it); col-2 is the flexible middle (editor/welcome); col-1 is the tree.
     When a column has no data it COLLAPSES to 0 (no empty 240px band, no black gap)
     — but the layout stays 3-col so col-3 (and its tabbar) is always a confined
     column, never full-width. */
  /* ALL 3 columns ALWAYS visible (PO: don't lose the file-tree / editor). Each shows
     its own content or empty-state — never collapsed to 0, never a black gap. col-3
     is a fixed column so the cockpit tab-strip stays inside it, never spanning. */
  /* #91 DYNAMIC-LAYOUT (171, VS Code pane-parity): col-1/col-3 widths are CSS
     vars the resize-handles drive (col-2 = the flexible middle). Two 6px drag
     handles sit on the grid between the columns. The widths persist (app.js →
     localStorage 'cockpit.layout'). Defaults match the prior fixed layout. */
  .project-view {
    /* GAP-16: col widths match the design — col-1 (Files/roster) = 288 (§5a roster 288 / §5b
       files 236, use 288 for the roster-inclusive column), col-3 (Chat) = 352 (§5b "Chat (352)").
       The old 240/560 skewed the 3-col balance (editor squeezed, chat too wide). */
    --col1-w: 288px;
    --col3-w: 352px;
    display: grid;
    /* #B8 robustness (condition #1): col-2 has a MIN width (280px) so a wide col-1 + narrow window can't
       collapse the editor to ~0 (the tab strip flush against the col-1 "Files" pill). At the 1024 floor
       288+352+12gutters+280 = 932 < 1024 → fits; a wider col-1 pushes the whole grid to scroll rather
       than crushing col-2. Combined with the col-head clip, the Files pill never abuts the editor tab. */
    grid-template-columns: var(--col1-w) 6px minmax(280px, 1fr) 6px var(--col3-w);
    grid-template-areas: "col1 rh1 col2 rh2 col3";
    min-height: 0; gap: 0; position: relative;
  }
  #shell-col1 { grid-area: col1; border-right: 1px solid var(--border); }
  #shell-col2 { grid-area: col2; border-right: 1px solid var(--border); }
  #shell-col3 { grid-area: col3; }
  /* Layout is FIXED for both modes: col-1 (files/roster) · col-2 (editor/inspector, flexible centre) ·
     col-3 (CHAT, right). Do NOT move the chat out of col-3 or hide it in Work mode — a user in Explorer
     still chats with their agent on the same screen (PO: "explorer chat bên phải", "sao gỡ cái chat bên
     explorer ra"). Only the CONTENT of col-1/col-2 differs per mode. */
  /* desktop re-enables the dynamic-layout chrome hidden in the base rule. */
  .col3-head { display: flex; }
  .col-ctl { display: inline-flex; }
  .col3-tabctl { display: flex; }        /* col-3 ⤢/⊟ controls at the tab-bar right edge (H4) */
  /* ── resize handles ── a thin draggable strip between columns (col-resize). */
  .col-resize { display: block; cursor: col-resize; background: transparent; position: relative; z-index: 5; }
  .col-resize::before {
    content: ""; position: absolute; inset: 0 2px; border-radius: 2px;
    background: transparent; transition: background 0.12s var(--ease);
  }
  .col-resize:hover::before, .col-resize.dragging::before { background: var(--accent); }
  .col-resize[data-resize="1"] { grid-area: rh1; }
  .col-resize[data-resize="2"] { grid-area: rh2; }
  /* Monitor swaps col2↔col3, so the columns' own border-right lines land on the wrong
     edges (Inspector's at the window edge; thread↔inspector had none). Monitor draws
     BOTH separators as a centred 1px line on the handles instead — symmetric, no lệch. */
  body[data-mode="monitor"] #shell-col1,
  body[data-mode="monitor"] #shell-col2 { border-right: 0; }
  body[data-mode="monitor"] .col-resize {
    background: linear-gradient(to right,
      transparent calc(50% - 0.5px), rgba(255,255,255,.08) calc(50% - 0.5px),
      rgba(255,255,255,.08) calc(50% + 0.5px), transparent calc(50% + 0.5px));
  }
  /* while dragging, kill text-selection + pointer noise across the whole shell. */
  .project-view.resizing { user-select: none; cursor: col-resize; }
  .project-view.resizing iframe, .project-view.resizing * { pointer-events: none; }
  .project-view.resizing .col-resize { pointer-events: auto; }

  /* ── per-column controls (hide ⊟ + fullscreen ⤢) live in each column header ── */
  .col-ctl { display: inline-flex; gap: 2px; margin-left: auto; flex: none; }
  .col-ctl-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; padding: 0; border: 1px solid transparent;
    background: transparent; color: var(--muted); cursor: pointer; border-radius: var(--r-sm);
    font-size: var(--t-sm); line-height: 1;
  }
  .col-ctl-btn:hover { color: var(--fg-strong); background: var(--code-bg); }
  .col-ctl-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

  /* ── HIDE column ── data-hidden lists collapsed columns; the hidden track AND
     its gutter → 0 so the remaining columns expand to reclaim the space. A thin
     restore-strip (.col-restore) re-opens them.
     NOTE: --col1-w / --col3-w are written INLINE by app.js applyLayout (persisted
     resize widths), so a stylesheet `--col1-w: 0` override is powerless against the
     inline var. We therefore collapse by overriding grid-template-columns directly
     (a stylesheet declaration on the same element — no inline grid-template exists,
     so this wins) with the hidden track + its adjacent gutter set to 0. */
  .project-view[data-hidden~="col1"] #shell-col1,
  .project-view[data-hidden~="col2"] #shell-col2,
  .project-view[data-hidden~="col3"] #shell-col3 { display: none; }
  .project-view[data-hidden~="col1"] .col-resize[data-resize="1"],
  .project-view[data-hidden~="col2"] .col-resize[data-resize="1"],
  .project-view[data-hidden~="col2"] .col-resize[data-resize="2"],
  .project-view[data-hidden~="col3"] .col-resize[data-resize="2"] { display: none; }
  /* track order: col1 | rh1 | col2 | rh2 | col3. Zero the hidden column(s) + gutter(s).
     Only combinations that can occur (max 2 hidden, never all 3) are enumerated;
     more-specific pair rules are listed after the single-column rules so they win. */
  /* track order: col1 | rh1 | col2 | rh2 | col3. Zero the hidden column(s) + gutter(s).
     Only combinations that can occur (max 2 hidden, never all 3) are enumerated;
     more-specific pair rules are listed after the single-column rules so they win. */
  .project-view[data-hidden~="col1"] { grid-template-columns: 0 0 minmax(280px, 1fr) 6px var(--col3-w); }
  .project-view[data-hidden~="col2"] { grid-template-columns: var(--col1-w) 6px 0 0 var(--col3-w); }
  .project-view[data-hidden~="col3"] { grid-template-columns: var(--col1-w) 6px minmax(280px, 1fr) 0 0; }
  .project-view[data-hidden~="col1"][data-hidden~="col2"] { grid-template-columns: 0 0 0 0 var(--col3-w); }
  .project-view[data-hidden~="col1"][data-hidden~="col3"] { grid-template-columns: 0 0 minmax(280px, 1fr) 0 0; }
  .project-view[data-hidden~="col2"][data-hidden~="col3"] { grid-template-columns: var(--col1-w) 6px 0 0 0; }
  /* ── DESIGN v3 H2 (MONITOR): THREAD ở CỘT GIỮA (rộng, flexible) + Inspector ở CỘT PHẢI (~352px) —
     PO side-by-side 2026-07-20 ("bố cục cột đảo ngược"). Swap là AREAS-only, Monitor-scoped: các
     element giữ nguyên (#shell-col2 = viewer/#inspector · #shell-col3 = cockpit thread) nên tabs/SSE/
     composer/resize/hide đều nguyên vẹn — chỉ đổi SLOT hiển thị. EXPLORER giữ chat ở cột phải như PO
     đã chốt ("explorer chat bên phải") — không đụng. rh2 vẫn chỉnh --col3-w, giờ là bề rộng Inspector
     (khớp design ~320-352px). */
  body[data-mode="monitor"] .project-view { grid-template-areas: "col1 rh1 col3 rh2 col2"; }
  /* data-hidden zero theo SLOT: monitor đảo col2↔col3 nên đảo cả cặp track-zero, và cho cột còn lại
     GIÃN (base giữ track cứng var(--col3-w) — ẩn cột giữa mà cột phải cứng thì màn thừa khoảng chết). */
  body[data-mode="monitor"] .project-view[data-hidden~="col2"] { grid-template-columns: var(--col1-w) 6px minmax(280px, 1fr) 0 0; }
  body[data-mode="monitor"] .project-view[data-hidden~="col3"] { grid-template-columns: var(--col1-w) 6px 0 0 minmax(280px, 1fr); }
  body[data-mode="monitor"] .project-view[data-hidden~="col1"][data-hidden~="col2"] { grid-template-columns: 0 0 minmax(280px, 1fr) 0 0; }
  body[data-mode="monitor"] .project-view[data-hidden~="col1"][data-hidden~="col3"] { grid-template-columns: 0 0 0 0 minmax(280px, 1fr); }
  /* restore-strip: a vertical bar with a ⊞ per hidden column to bring it back. */
  .col-restore {
    display: none; position: absolute; top: var(--s-2); z-index: 8;
    flex-direction: column; gap: var(--s-1);
  }
  /* No floating restore chip ever — hiding a panel leaves the zone empty; the LEFT rail
     panel-toggle is the only way back. Kept as a hard `none` so no code path can re-show it. */
  .col-restore, .col-restore.has-hidden { display: none !important; }
  .col-restore.left { left: var(--s-1); }
  .col-restore.right { right: var(--s-1); }
  .col-restore-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s-1);
    height: 28px; padding: 0 var(--s-2); border: 1px solid var(--border-strong);
    background: var(--bg-elev); color: var(--muted); cursor: pointer; border-radius: var(--r-sm);
    font-size: var(--t-xs); white-space: nowrap;
  }
  .col-restore-btn:hover { color: var(--fg-strong); border-color: var(--accent); }
  .col-restore-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

  /* ── FULLSCREEN one column ── data-fullscreen=col1|col2|col3 makes that column
     span the whole grid; the other two + both handles hide. ⤢ again (or Esc)
     restores. Fullscreen WINS over hide. */
  .project-view[data-fullscreen] { grid-template-columns: 1fr; grid-template-areas: "full"; }
  /* Monitor's swap rule (body[data-mode] …) out-specifies the fullscreen rule → ⤢ never
     expanded the column in Monitor. Re-assert fullscreen at the higher specificity. */
  body[data-mode="monitor"] .project-view[data-fullscreen] { grid-template-columns: 1fr; grid-template-areas: "full"; }
  .project-view[data-fullscreen] .col-resize,
  .project-view[data-fullscreen] .col-restore { display: none !important; }
  .project-view[data-fullscreen="col1"] #shell-col1,
  .project-view[data-fullscreen="col2"] #shell-col2,
  .project-view[data-fullscreen="col3"] #shell-col3 { grid-area: full; display: flex; }
  .project-view[data-fullscreen="col1"] #shell-col2,
  .project-view[data-fullscreen="col1"] #shell-col3,
  .project-view[data-fullscreen="col2"] #shell-col1,
  .project-view[data-fullscreen="col2"] #shell-col3,
  .project-view[data-fullscreen="col3"] #shell-col1,
  .project-view[data-fullscreen="col3"] #shell-col2 { display: none; }
  /* the fullscreened column's ⤢ flips to a "restore" affordance (title via app.js). */
  .project-view[data-fullscreen] .col-ctl-btn[data-col-fs].active { color: var(--accent); background: rgba(55,148,255,0.12); }
  /* the tab-strip + every col-3 region is bounded by col-3 (overflow:hidden on the
     column already clips it); belt-and-suspenders so a long tab list scrolls inside
     col-3 and never visually overflows into col-2. */
  #shell-col3 > #tabbar { max-width: 100%; }
  body.live-active #rail { opacity: 0.85; }
  .cockpit-toolbar { display: none; }      /* desktop: columns always visible, no 📁 */
  .col-back { display: none; }             /* desktop: no per-panel back */
  /* the rail lives inside col-3 on desktop ≥1280; below that col-3 already holds chip-row. */
  #rail { display: none !important; }      /* W1: chip-popover replaces the always-on rail */
  .appbar-sheet, #agent-sheet, #sheet-scrim { display: none !important; }
}

@media (min-width: 720px) and (max-width: 1023px) {
  /* tablet (91b WT): 2-col — col-1 file-tree + col-3 cockpit ALWAYS visible (PO must
     see the tree). The col-2 editor/diff slides over from the right when a file is
     opened (data-col2="open"); otherwise the 2-col tree+cockpit fills the width. */
  .project-view {
    display: grid; grid-template-columns: 220px minmax(0, 1fr);
    grid-template-areas: "col1 col3"; min-height: 0; position: relative;
  }
  #shell-col1 { grid-area: col1; border-right: 1px solid var(--border); }
  #shell-col3 { grid-area: col3; }
  /* col-2 (editor) is a slide-over from the right, shown only when a file is open. #B8: pin its LEFT
     edge to col-1's width (220px) instead of a 60%-from-right width, so the slide-over can NEVER start
     under col-1 — the col-1 "Files" pill can't overlap col-2's editor tab even on a narrow tablet. */
  #shell-col2 {
    position: absolute; z-index: 30; left: 220px; right: 0; top: 0; bottom: 0; width: auto;
    background: var(--bg); border-left: 1px solid var(--border-strong);
    box-shadow: var(--shadow); transform: translateX(100%);
    transition: transform 0.22s var(--ease);
  }
  .project-view[data-col2="open"] #shell-col2 { transform: translateX(0); }
  .col2-head .col-back { display: inline-flex; }   /* close the editor slide-over */
  .col1-head .col-back { display: none; }
  #rail { display: none !important; }
  .cockpit-toolbar { display: none; }              /* col-1 tree is already visible */
}

@media (max-width: 719px) {
  /* mobile (91c): PUSH-STACK — col-3 cockpit is the MAIN screen; col-1/col-2 are
     full-screen panels shown by data-pane. ONE DOM; visibility by the attribute.
     overflow-x:hidden CLIPS the off-screen (translateX 100%) panels so they don't extend
     the document to 2× width → no horizontal body scroll (QC B5). */
  .project-view {
    display: block; position: relative; min-height: 0; overflow-x: hidden;
  }
  #shell-col1, #shell-col2 {
    position: absolute; inset: 0; z-index: 30; background: var(--bg);
    transform: translateX(100%); transition: transform 0.22s var(--ease);
  }
  #shell-col3 { display: flex; }                   /* cockpit = the base layer */
  .project-view[data-pane="col1"] #shell-col1 { transform: translateX(0); }
  .project-view[data-pane="col2"] #shell-col2 { transform: translateX(0); }
  .col-back { display: inline-flex; }              /* ‹ back to cockpit on each panel */
  #rail { display: none !important; }
  /* 44px tap targets (a11y) */
  .c1-tab, .vs-btn, .tl-item, .ft-file, .col-back { min-height: 44px; }

  /* §8 Mobile (H3) — the 3 cockpit panels become 3 bottom-tabs (Team / Chat / Inspect). The
     bar replaces the desktop rail + the ☰ sheet toggle + the 📁 cockpit-toolbar opener (those
     become redundant: the tabs ARE the navigation). data-mtab on #project-view names the
     active tab; the panels are driven by data-pane (set by setMobileTab) so the existing
     push-stack transitions still animate the col-2 slide-over on tap. */
  .cockpit-toolbar { display: none; }              /* replaced by the bottom-tab bar */
  #project-view { padding-bottom: 52px; }          /* room for the fixed bottom-tab bar */
  /* !important: the base `.mob-tabbar{display:none}` rule appears LATER in source order, so
     without it that base would win at equal specificity and hide the bar on mobile too. */
  .mob-tabbar { display: flex !important; }         /* shown ONLY on mobile (hidden ≥720) */

  /* Chat tab = the cockpit (col-3); Team = roster (col-1); Inspect = the Inspector (col-2).
     data-mtab (the bottom-tab the USER picked) is the SSOT for which panel shows — it OVERRIDES
     data-pane (PO 22-07: many writers set data-pane thô — agent-click, auto-open-tab, applyView —
     leaving data-mtab=team but data-pane=col2 → the Team tab showed the Inspector/chat. The visible
     panel must follow the bottom-tab, not whichever code last touched data-pane). Team → col-1 in,
     col-2/3 out. Chat → both panels out (col-3 base shows). Inspect → col-2 in. */
  .project-view[data-mtab="team"] #shell-col1 { transform: translateX(0) !important; }
  .project-view[data-mtab="team"] #shell-col2 { transform: translateX(100%) !important; }
  .project-view[data-mtab="chat"] #shell-col1,
  .project-view[data-mtab="chat"] #shell-col2 { transform: translateX(100%) !important; }
  .project-view[data-mtab="inspect"] #shell-col2 { transform: translateX(0) !important; }
  .project-view[data-mtab="inspect"] #shell-col1 { transform: translateX(100%) !important; }
  /* The panels are inset:0 over a #project-view that scrolls the whole chat: once it scrolled (the
     card jump, a long thread), the panel moved up with it and the chat showed under the Team tab
     after a blank gap (QC 975). Team/Inspect take the chat out, so there is nothing below to show. */
  .project-view[data-mtab="team"] #shell-col3,
  .project-view[data-mtab="inspect"] #shell-col3 { display: none !important; }

  /* UC-420/459 — mobile Inspect tab surfaces the #inspector (Changes/Files/To-Do) in
     col-2, replacing the editor/diff/empty viewer. data-mtab-inspect is set by setMobileTab. */
  .project-view[data-mtab-inspect] #shell-col2 #inspector { display: flex; }
  .project-view[data-mtab-inspect] #shell-col2 #editor-view,
  .project-view[data-mtab-inspect] #shell-col2 #diff-view,
  .project-view[data-mtab-inspect] #shell-col2 #live-view,
  .project-view[data-mtab-inspect] #shell-col2 #col2-empty,
  .project-view[data-mtab-inspect] #shell-col2 #file-tabs { display: none !important; }
}

/* ── PU-3m bottom-sheet (Material/iOS pattern) ─────────────────────────────── */
.appbar-sheet { gap: var(--s-1); }
.sheet-badge {
  min-width: 18px; height: 18px; padding: 0 var(--s-1); border-radius: 999px;
  background: var(--accent); color: #fff; font-size: var(--t-xs); font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.sheet-badge:empty { display: none; }
.sheet-scrim {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0; transition: opacity 0.2s var(--ease);
}
.sheet-scrim.open { opacity: 1; }
.agent-sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 41;
  max-height: 72vh; display: flex; flex-direction: column;
  background: var(--panel); border-top: 1px solid var(--border);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow);
  transform: translateY(100%); transition: transform 0.24s var(--ease);
}
.agent-sheet.open { transform: translateY(0); }
.sheet-handle {
  width: 36px; height: 4px; border-radius: 999px; background: var(--border-strong);
  margin: 8px auto 4px;
}
.sheet-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s-1) var(--s-3) var(--s-2); border-bottom: 1px solid var(--border);
}
.sheet-title { font-size: var(--t-md); font-weight: 700; color: var(--fg-strong); }
.sheet-body { padding: var(--s-2) var(--s-3) var(--s-4); overflow-y: auto; -webkit-overflow-scrolling: touch; }
.sheet-section + .sheet-section { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--border); }
.sheet-section-title { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-bottom: var(--s-1); }
/* body scroll lock while the sheet is open */
body.sheet-open { overflow: hidden; }

/* ── §8 Mobile bottom-tab bar (Team / Chat / Inspect) ───────────────────────── */
.mob-tabbar {
  display: none;                                   /* mobile-only: the <720 rule flips to flex */
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
  /* dc:803 — the mobile bottom-tabbar: height 56, bg #0e1014, border-top .06 (verified this is the
     BOTTOM nav bar — border-top + space-around + #0e1014 — not the 52px top-header). */
  height: 56px; align-items: stretch;
  background: #0e1014; border-top: 1px solid rgba(255, 255, 255, 0.06);
  padding-bottom: env(safe-area-inset-bottom, 0);  /* iOS home-indicator inset */
}
.mob-tab {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 44px;                                /* §8 hit target ≥44px */
  border: 0; background: transparent; color: var(--muted); cursor: pointer;
  font-size: 10px; font-family: var(--font-mono);
}
.mob-tab svg { display: block; }
.mob-tab.active { color: var(--accent); }
.mob-tab-label { line-height: 1; }
.mob-tab-ico { position: relative; display: inline-flex; }
.mob-tab-badge {
  position: absolute; top: -5px; right: -7px;
  min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px;
  background: var(--accent); color: #fff; font-size: 8px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-mono);
}
.mob-tab-badge:empty { display: none; }
@media (min-width: 720px) {
  .mob-tabbar { display: none !important; }         /* desktop: never show the bottom-tab bar */
}

/* ═══════════════════════════════════════════════════════════════════════════
   #91 PRODUCTION-GRADE — states (skeleton/error) + render-core (diff-split,
   file-tree, chip-popover). Reuses the existing tokens (--green/--red/--accent/
   --border/--muted) so it matches the cockpit palette.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Skeleton loading (shimmer; NOT mock data — just animated bars) ─────────── */
.skel { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-3); }
.skel-row { display: flex; }
.skel-bar {
  height: 12px; border-radius: 6px; flex: none;
  background: linear-gradient(90deg, var(--code-bg) 25%, rgba(127,127,127,0.28) 37%, var(--code-bg) 63%);
  background-size: 400% 100%;
  animation: skel-shimmer 1.4s ease infinite;
}
.skel-diff .skel-bar { height: 14px; }
.skel-terminal .skel-bar { height: 11px; font-family: var(--font-mono); }
.skel-thread .skel-row:nth-child(odd) .skel-bar { width: 82% !important; }
@keyframes skel-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .skel-bar { animation: none; } }

/* ── Error state with [Retry] ──────────────────────────────────────────────── */
.err-state {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-2);
  padding: var(--s-4) var(--s-4); margin: var(--s-3); border-radius: var(--r-md);
  border: 1px solid var(--border-strong); background: var(--bg-elev);
}
.err-msg { font-weight: 600; color: var(--fg-strong); }
.err-detail { font-size: var(--t-base); color: var(--muted); }
.err-retry {
  margin-top: var(--s-1); border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--fg-strong); cursor: pointer;
}
.err-retry:hover { border-color: var(--accent); color: var(--accent); }
.err-retry:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* #163 — "Go back to projects" button on a 404'd project's error panel; same look as Retry
   (both are recovery actions in the same error card), just a different data-retry action. */
.err-home {
  margin-top: var(--s-1); border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--fg-strong); cursor: pointer;
}
.err-home:hover { border-color: var(--accent); color: var(--accent); }
.err-home:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── R1 Diff-split (side-by-side HEAD | Working) ────────────────────────────── */
.diff-split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden;
  font-family: var(--font-mono); font-size: var(--t-sm); line-height: 1.55;
}
.ds-col { overflow-x: auto; }
.ds-colleft { border-right: 1px solid var(--border); }
.ds-line { display: flex; white-space: pre; }

/* ↵ Wrap (Split only). Long lines wrap instead of scrolling sideways — measured 2026-09-18:
   a 1487px line in a 382px column left three quarters of the sentence behind a scrollbar.
   OFF by default because Split's whole value is that the two sides line up row for row.
   `align-items: stretch` + `min-width: 0` on the text cell is what keeps them lined up when
   a row grows: the gutter stretches to the wrapped height instead of the text overflowing
   past it, so row N on the left still sits beside row N on the right. */
body.diff-wrap-on .ds-col { overflow-x: hidden; }
body.diff-wrap-on .ds-line { white-space: pre-wrap; align-items: stretch; }
body.diff-wrap-on .ds-line > .ds-txt { min-width: 0; overflow-wrap: anywhere; word-break: break-word; }
body.diff-wrap-on .ds-ln { display: flex; align-items: flex-start; justify-content: flex-end; }
.diff-wrap-btn { border-left: 1px solid var(--border-strong, #454545); }
.ds-ln {
  flex: none; width: 42px; padding: 0 var(--s-2) 0 0; text-align: right;
  color: var(--faint); user-select: none; border-right: 1px solid var(--border);
}
.ds-txt { flex: 1; padding: 0 var(--s-2); min-width: 0; }
.ds-del { background: rgba(241, 76, 76, 0.13); }
.ds-add { background: rgba(76, 175, 80, 0.13); }
.ds-blank { background: rgba(127, 127, 127, 0.05); }

/* ── W5: diff toolbar (split/inline toggle + hunk-nav) + unified inline layout ──── */
.diff-toolbar {
  display: flex; align-items: center; gap: var(--s-3); padding: var(--s-1) var(--s-2);
  /* `flex: none` and NOT `sticky`: the pane no longer scrolls, so the toolbar is simply the
     first row of a flex column. Sticky here is what let it drift (see .diff-view above). */
  flex: none; z-index: 2; background: var(--bg-elev, var(--panel));
  border-bottom: 1px solid var(--border);
}
.diff-mode-toggle { display: inline-flex; border: 1px solid var(--border-strong, #454545); border-radius: var(--r-sm); overflow: hidden; }
.diff-mode-btn {
  background: none; border: none; color: var(--muted); cursor: pointer; font: inherit;
  font-size: var(--t-xs); padding: 2px var(--s-2);
}
.diff-mode-btn.active { background: var(--accent, #0b6bcb); color: #fff; }
.diff-mode-btn:not(.active):hover { background: var(--code-bg); color: var(--fg-strong); }
.diff-nav { display: inline-flex; align-items: center; gap: var(--s-1); margin-left: auto; }
.diff-nav-btn {
  background: none; border: 1px solid var(--border, #3a3a3a); border-radius: var(--r-sm);
  color: var(--muted); cursor: pointer; font: inherit; line-height: 1; padding: 1px var(--s-1);
}
.diff-nav-btn:hover { color: var(--fg-strong); border-color: var(--accent); }
.diff-nav-count { color: var(--muted); font-size: var(--t-xs); }
.diff-body { overflow: auto; flex: 1 1 auto; min-height: 0; }
/* unified (inline) diff: one column, old/new gutters + sign + text. */
.diff-unified { font-family: var(--font-mono); font-size: var(--t-sm); line-height: 1.55; }
.diff-unified .diff-line { display: flex; white-space: pre-wrap; }
.diff-unified .du-ln {
  flex: none; width: 38px; text-align: right; padding-right: var(--s-2);
  color: var(--faint); user-select: none;
}
.diff-unified .du-txt { flex: 1; min-width: 0; }
/* hunk-nav: flash-highlight the current hunk-head. */
.hunk-head.hunk-current { outline: 2px solid var(--accent, #0b6bcb); outline-offset: -1px; }

/* ── R3 File-tree (col-1) ──────────────────────────────────────────────────── */
.ft-banner {
  font-size: var(--t-xs); color: var(--muted); padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--border); background: var(--code-bg);
}
.ft-tree { list-style: none; margin: 0; padding: var(--s-1) 0; font-size: var(--t-sm); font-family: var(--font-mono); }
.ft-tree .ft-children { list-style: none; margin: 0; padding-left: var(--s-4); }
.ft-dir > .ft-dirname {
  display: flex; align-items: center; gap: var(--s-1); font-weight: 600;
  color: var(--fg-strong); cursor: pointer; padding: var(--s-1) var(--s-3);
  border-radius: var(--r-sm); min-height: 28px;
}
.ft-dir > .ft-dirname:hover { background: var(--code-bg); }
.ft-dir > .ft-dirname:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ft-twisty { display: inline-block; width: var(--s-4); flex: none; color: var(--muted); text-align: center; }
/* #106-collapse: a collapsed dir hides its children (default state); the twisty glyph is set
   in markup (▸ collapsed) and swapped to ▾ when app.js removes .ft-collapsed on expand. */
.ft-dir.ft-collapsed > .ft-children { display: none; }
.ft-file {
  /* PO 21-07: a file row's ICON must sit in the SAME column as a sibling dir's folder icon.
     The dir row's icon lands at 12px pad + 16px twisty + 4px gap = 32px, so the file row's
     left padding is 32px (was 24px — the two columns visibly zig-zagged in the tree). */
  display: flex; align-items: center; gap: var(--s-2); padding: 5px 8px 5px 32px;
  cursor: pointer; border-radius: 7px; min-height: 28px;
}
/* UC-304 drag-desktop→tree: highlight the folder a dragged file will drop into. */
.ft-dir.ft-drop > .ft-dirname, .ft-dir.ft-drop { background: color-mix(in srgb, var(--accent) 18%, transparent); border-radius: var(--r-sm); }
.ft-file:hover { background: var(--code-bg); }
.ft-file.active { background: rgba(55, 148, 255, 0.16); }
.ft-file:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ft-path { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ft-status { flex: none; width: 16px; text-align: center; font-weight: 700; font-size: var(--t-xs); }
/* WEB-PARITY-5 #1: the M/A/D badge is the "view diff" affordance (row click = view file). */
.ft-diff-open { cursor: pointer; border-radius: var(--r-sm); }
.ft-diff-open:hover { background: var(--accent); color: #fff; }
.ft-m { color: var(--yellow); }
.ft-a { color: var(--green); }
.ft-d { color: var(--red); }

/* ── WEB-PARITY-5 #1/#3 — file-type icon (tree row + editor tab). The SVG is
   stroke=currentColor, so a per-kind tint CLASS (.fi-<kind>) colors it like the
   VS Code file-icon theme. ONE source (icons.js fileIconFor) feeds both surfaces
   so a file's tree icon == its tab icon (AC3). */
.ft-icon {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; color: var(--muted);
}
.ft-icon svg { width: 16px; height: 16px; }
.ft-tab-icon { flex: none; display: inline-flex; align-items: center; justify-content: center; color: var(--muted); }
.ft-tab-icon svg { width: 16px; height: 16px; }
/* per-extension tint — close to vscode-icons / Seti hues. */
.fi-ts { color: #3178c6; }
.fi-js { color: #e7c34b; }
.fi-json, .fi-yaml { color: #cbcb41; }
.fi-md, .fi-txt { color: #6a9fd4; }
.fi-css { color: #42a5f5; }
.fi-html { color: #e44d26; }
.fi-img { color: #c586c0; }
.fi-sh { color: #4caf50; }
.fi-py { color: #4b8bbe; }
.fi-rs { color: #dea584; }
.fi-go { color: #00add8; }
.fi-rb { color: #cc342d; }
.fi-java { color: #e76f00; }
.fi-c { color: #6a9fd4; }
.fi-lock { color: #9aa0a6; }
.fi-dir { color: var(--accent); }
.fi-file { color: var(--muted); }

/* ── WEB-PARITY-5 #5 — card icons are now SVG (icons.js), not emoji; size + tint
   per card kind (mirrors the extension's .card-icon tints). */
.tcard-icon svg { width: 16px; height: 16px; }
.read-card .tcard-icon { color: var(--link); }
.search-card .tcard-icon { color: var(--yellow); }
.subagent-card .tcard-icon { color: #4ec9b0; }
.tcard.tcard-flash { box-shadow: 0 0 0 2px rgba(55, 148, 255, .55); transition: box-shadow .3s; }
.workflow-card .tcard-icon { color: #c586c0; }
.command-card .tcard-icon { color: var(--fg-strong); }
.todo-card .tcard-icon { color: var(--accent); }
.tool-card .tcard-icon, .mcp-card .tcard-icon { color: var(--muted); }

/* ── R2 Chip-popover (desktop dropup / mobile push reuses the same body) ─────── */
.cp-title { font-weight: 700; color: var(--fg-strong); padding: var(--s-1) var(--s-1) var(--s-2); }
.cp-count { color: var(--muted); font-weight: 600; }
/* #41/X-4 — the schedules mirror (in the chip popover). */
.sched-section { margin-bottom: var(--s-2); }
.sched-section-title { font-size: var(--t-xs); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; margin: var(--s-1) 0; }
.sched-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.sched-row { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-1) var(--s-2); border-radius: var(--r-sm); font-size: var(--t-sm); }
.sched-row:hover { background: var(--code-bg); }
.sched-target { font-weight: 600; color: var(--fg-strong); flex: none; }
.sched-cmd { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); }
.sched-when { flex: none; font-size: var(--t-xs); color: var(--accent); }
.sched-when.fired { color: var(--faint); }
.sched-when.standing { color: var(--green); }
.sched-row.agent-sched, .sched-row.member-sched { flex-direction: column; align-items: stretch; gap: 2px; }
.agent-sched .sched-main, .member-sched .sched-main { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
.agent-sched .sched-meta, .member-sched .sched-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); font-size: var(--t-xs); color: var(--muted); }
.agent-sched .sched-reason, .member-sched .sched-reason { color: var(--faint); }
.member-sched .sched-id { margin-left: auto; font-family: var(--font-mono); color: var(--faint); }
.sched-row.member-sched.sched-paused { opacity: 0.7; }
.agent-sched .sched-acts { margin-left: auto; display: flex; gap: 4px; }
.sched-act { font: inherit; font-size: var(--t-xs); padding: 1px 8px; border-radius: var(--r-sm); border: 1px solid var(--border, rgba(127,127,127,.35)); background: transparent; color: var(--fg-strong); cursor: pointer; }
.sched-act[data-agent-sched-act="cancel"] { color: var(--red, #d55); }
.sched-act:disabled { opacity: .5; cursor: default; }
.sched-row.sched-cancelled { opacity: 0.5; text-decoration: line-through; }
.sched-empty { padding: var(--s-3); }
/* Flow D1: "+ New schedule" form + Active/History tabs + paged runs in the Schedules popover. */
.sched-new-bar { display: flex; align-items: center; gap: var(--s-2); margin: var(--s-1) 0 var(--s-2); }
.sched-new-saved { font-size: var(--t-xs); color: var(--muted); font-family: var(--mono, monospace); }
.sched-new { display: flex; flex-direction: column; gap: 6px; padding: var(--s-2); margin-bottom: var(--s-2); border: 1px solid var(--border, rgba(127,127,127,.35)); border-radius: var(--r-sm); }
.sched-new-field { display: flex; flex-direction: column; gap: 2px; flex: 1 1 140px; min-width: 0; }
.sched-new-lbl { font-size: var(--t-xs); color: var(--muted); }
.sched-new input, .sched-new select, .sched-new textarea { font: inherit; font-size: var(--t-sm, 12px); padding: 3px 6px; border-radius: var(--r-sm); border: 1px solid var(--border, rgba(127,127,127,.35)); background: transparent; color: var(--fg-strong); min-width: 0; max-width: 100%; box-sizing: border-box; }
.sched-new textarea { resize: vertical; }
.sched-new-row { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: flex-end; }
/* QC958 #7: a field directly in the column form must not grow (flex-basis 140px is meant for the
   side-by-side rows); growing it opened tall gaps between Runs as / What / Repeat. */
.sched-new > .sched-new-field { flex: 0 0 auto; }
.sched-waiting { color: var(--warn, #d29922); }
.sched-new-past { color: var(--danger, #ff6b6b); }
.sched-new-tz, .sched-new-hint { font-size: var(--t-xs); color: var(--muted); }
.sched-new-tasks { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.sched-new-tasks li { display: flex; gap: 4px; align-items: center; }
.sched-new-tasks input { flex: 1 1 auto; }
.sched-new-preview { font-size: var(--t-xs); color: var(--fg-strong); margin: 2px 0; }
.sched-new-acts { display: flex; justify-content: flex-end; gap: var(--s-2); }
.sched-new-save { border-color: var(--accent, #3794ff); color: var(--accent, #3794ff); }
.sched-tabs { display: flex; gap: 4px; margin: var(--s-1) 0; }
.sched-tab { font: inherit; font-size: var(--t-xs); padding: 2px 10px; border-radius: var(--r-sm); border: 1px solid var(--border, rgba(127,127,127,.35)); background: transparent; color: var(--muted); cursor: pointer; }
.sched-tab.on { color: var(--fg-strong); border-color: var(--accent, #3794ff); }
.sched-more { margin-top: var(--s-1); }
.sched-roster-timer { margin-right: 6px; }
.cp-agents, .cp-todos { list-style: none; margin: 0; padding: 0; }
.cp-agent {
  display: flex; align-items: center; gap: var(--s-2); padding: var(--s-1) var(--s-1); min-height: 34px;
  border-top: 1px solid var(--border);
}
.cp-agent .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-agent .host { color: var(--faint); font-size: var(--t-sm); }
.cp-agent .status { font-size: var(--t-xs); color: var(--muted); }
.cp-agent .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.cp-agent .dot.online { background: var(--green); }
.cp-agent .dot.offline { background: var(--faint); }
.cp-gear {
  flex: none; border: none; background: none; color: var(--muted); cursor: pointer;
  padding: var(--s-1); border-radius: var(--r-sm);
}
.cp-gear:disabled { opacity: 0.4; cursor: not-allowed; }
.cp-gear:not(:disabled):hover { background: var(--code-bg); color: var(--fg-strong); }

/* ───────── §6 Agents popover (grouped) ───────── */
/* #156: popover is a FIXED 400px column, but this head packed a title + 5 action buttons on ONE
   non-wrapping flex line — the title clipped 21px, the action cluster 33px, and the popover grew a
   horizontal scrollbar. Let the head wrap (title on its own line when the actions need the width)
   and stop min-content from forcing overflow. */
.cp-agents-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
  flex-wrap: wrap; min-width: 0;
  padding: 9px 12px 4px; border-bottom: 1px solid var(--border); /* dc literal, not --s-1 tokens */
}
.cp-agents-title { font-weight: 600; color: var(--fg); font-size: var(--t-sm); min-width: 0; }
.cp-jointeam {
  flex: none; font-size: var(--t-xs); color: var(--accent); background: transparent;
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: 2px var(--s-2); cursor: pointer;
}
.cp-jointeam:hover { border-color: var(--accent); }
.cp-group { margin-top: var(--s-2); }
.cp-group-label {
  font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted); padding: var(--s-1) var(--s-1);
}
.cp-group-sub { color: var(--faint); }
/* info-only row: the state dot (no glyph), name·type, state word, CLI/AGENT badge, ⚙. */
.cp-row { border-top: 1px solid var(--border); min-height: 34px; padding: 8px 10px; border-radius: 9px; } /* dc:1109 literal row pad/radius */
.cp-row .agent-state { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--state-idle); font-size: 0; }
.cp-row .agent-state.running { background: var(--state-running); box-shadow: 0 0 0 3px var(--state-running-glow); }
.cp-row .agent-state.awaiting { background: var(--state-awaiting); box-shadow: 0 0 0 3px var(--state-awaiting-glow); }
.cp-row .agent-state.live { background: var(--state-running); }
.cp-row .agent-state.starting { background: var(--state-thinking, #e0b341); box-shadow: 0 0 0 3px color-mix(in srgb, var(--state-thinking, #e0b341) 18%, transparent); animation: dmPulse 1.1s ease-in-out infinite; }
.cp-row .agent-state.cp-pulse { animation: dmPulse 1.4s ease-in-out infinite; }
/* H5 amber-3s (design dc:1379): a JUST-acted popover dot gets an amber ring on top of its state colour for
   its 3s window, reusing the design's dmPulse 1.4s (same as the Monitor roster dot) — not a self-authored
   keyframe/timing. */
.cp-row .agent-state.just-acted { animation: dmPulse 1.4s ease-in-out infinite; box-shadow: 0 0 0 3px rgba(224, 179, 65, 0.35); }
.cp-row .agent-name { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; } /* dc:1109 popover name = 13 (not the agent-row base 11) */
.cp-state-word {
  flex: none; font-family: var(--font-mono); font-size: 9px; letter-spacing: .08em;
  padding: 1px 4px; border-radius: var(--r-sm); margin-left: auto;
}
.cp-state-word.running { color: var(--state-running); }
.cp-state-word.thinking { color: var(--state-thinking); }
.cp-state-word.awaiting { color: var(--state-awaiting-text); }
.cp-state-word.starting { color: var(--state-thinking, #e0b341); }
/* CLI / AGENT badge — the only inline control (opens the terminal). */
.cp-badge {
  flex: none; font-family: var(--font-mono); font-size: 9px; font-weight: 600; letter-spacing: .06em;
  padding: 2px 6px; border-radius: var(--r-sm); cursor: pointer;
  color: var(--kind-host-fg); background: var(--kind-host-bg); border: 1px solid transparent;
}
.cp-badge:hover { border-color: currentColor; }
.cp-badge-agent { color: var(--sub-agent); background: var(--kind-docker-bg); }
/* #100 X-2 — the ⚙ agent-actions menu (Show/Restart/Kill), a floating fixed popup. */
.agent-menu {
  background: var(--panel, var(--bg-elev)); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-md, 0 6px 20px rgba(0,0,0,0.4));
  padding: var(--s-1); min-width: 160px; display: flex; flex-direction: column; gap: 1px;
}
.agent-menu-item {
  display: block; width: 100%; text-align: left; font: inherit; font-size: var(--t-sm);
  background: none; border: none; color: var(--fg); cursor: pointer;
  padding: var(--s-2) var(--s-2); border-radius: var(--r-sm); white-space: nowrap;
}
/* PO 21-07: destructive menu items (Stop / Delete → Archived) read RED so they can't be mistaken
   for a regular action. Hover keeps the red on a red-tinted bg. */
.agent-menu-item.danger { color: rgb(255, 107, 107); }
.agent-menu-item.danger:hover { color: rgb(255, 107, 107); background: rgba(255, 107, 107, 0.12); }
.agent-menu-item:hover { background: var(--code-bg); color: var(--fg-strong); }
.agent-menu-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.agent-menu-sub { color: var(--muted); font-size: var(--t-xs, 0.85em); }
.agent-menu-empty { color: var(--muted); font-size: var(--t-sm); padding: var(--s-2); max-width: 220px; white-space: normal; line-height: 1.35; }

/* J explorer context menu (UC-268/273/274/290) — right-click a tree row. */
.tree-menu {
  background: var(--panel, var(--bg-elev)); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-md, 0 6px 20px rgba(0,0,0,0.4));
  padding: var(--s-1); min-width: 170px; display: flex; flex-direction: column; gap: 1px;
}
.tree-menu-item {
  display: block; width: 100%; text-align: left; font: inherit; font-size: var(--t-sm);
  background: none; border: none; color: var(--fg); cursor: pointer;
  padding: var(--s-2) var(--s-2); border-radius: var(--r-sm); white-space: nowrap;
}
.tree-menu-item:hover { background: var(--code-bg); color: var(--fg-strong); }
.tree-menu-item.danger { color: var(--red, #ff6b6b); }
.tree-menu-item.danger:hover { background: color-mix(in srgb, var(--red, #ff6b6b) 14%, transparent); }

/* H16 tab context menu (dc:2026): shared by editor + agent tabs. 240px, #16181d, ⌘W hint on the right,
   a separator before the agent extras, a disabled state for no-op close directions. */
.tree-menu.tab-menu { min-width: 240px; background: #16181d; border-color: rgba(255, 255, 255, 0.14); border-radius: 11px; padding: 6px; }
.tab-menu .tree-menu-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 7px; font-size: var(--t-sm); color: #d4d7dd; }
.tab-menu .tree-menu-item:hover { background: rgba(255, 255, 255, 0.06); color: var(--fg-strong); }
.tab-menu .tree-menu-item.disabled { color: var(--faint); cursor: default; opacity: 0.5; }
.tab-menu .tree-menu-item.disabled:hover { background: none; color: var(--faint); }
.tab-menu .tab-menu-hint { margin-left: auto; font-family: var(--font-mono); font-size: 10px; color: var(--faint); }
.tab-menu .tab-menu-sep { height: 1px; background: rgba(255, 255, 255, 0.08); margin: 3px 6px; }

/* H15a/H15b ⌘P quick-open + ⌘⇧P command palette (UC-279) — CSS COPIED PIXEL-FOR-PIXEL from the
   prototype (Team Monitor Redesign.dc.html H15a/H15b): 520px overlay anchored CENTER-TOP. */
.qo-scrim { position: fixed; inset: 0; z-index: 150; display: flex; align-items: flex-start; justify-content: center; padding-top: 10vh; }
.qo-panel {
  width: min(520px, 94vw); background: #16181d; border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px; box-shadow: 0 30px 70px rgba(0,0,0,.6); overflow: hidden;
}
.qo-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid rgba(255,255,255,.06); }
.qo-head-ico { flex: none; display: inline-flex; align-items: center; }
.qo-gt { font-family: 'Geist Mono', monospace; font-size: 14px; color: #3794ff; font-weight: 700; }
.qo-input { flex: 1; border: none; background: transparent; outline: none; font-family: 'Geist Mono', monospace; font-size: 13px; color: #e6e8ec; }
.qo-input::placeholder { color: #6e7681; }
.qo-esc { flex: none; font-family: 'Geist Mono', monospace; font-size: 10px; color: #6e7681; border: 1px solid rgba(255,255,255,.12); border-radius: 5px; padding: 2px 6px; }
.qo-list { list-style: none; margin: 0; padding: 6px; display: flex; flex-direction: column; gap: 1px; max-height: 250px; overflow-y: auto; }
.qo-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px; cursor: pointer; }
.qo-item.active { background: rgba(55,148,255,.14); }
.qo-badge { flex: none; font-family: 'Geist Mono', monospace; font-size: 11px; color: #6e7681; min-width: 30px; }
.qo-item.active .qo-badge { color: #3794ff; }
.qo-name { flex: none; font-size: 13px; color: #c4c8ce; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qo-item.active .qo-name { color: #e6e8ec; }
.qo-name-cmd { flex: 1; }
.qo-hit { color: #3794ff; font-weight: 400; }
.qo-dir { margin-left: auto; font-family: 'Geist Mono', monospace; font-size: 10px; color: #6e7681; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qo-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: #e0b341; }
.qo-cmd-ico { flex: none; color: #6e7681; }
.qo-item.active .qo-cmd-ico { color: #3794ff; }
.qo-cat { flex: none; margin-left: auto; font-family: 'Geist Mono', monospace; font-size: 10px; color: #6e7681; }
.qo-empty { color: #6e7681; font-size: 13px; padding: 14px; text-align: center; }
.qo-foot { display: flex; align-items: center; gap: 14px; padding: 8px 14px; border-top: 1px solid rgba(255,255,255,.06); font-family: 'Geist Mono', monospace; font-size: 10px; color: #6e7681; }
.qo-count { margin-left: auto; }
/* mobile: full-screen sheet (design "🔍 header → full-screen sheet"). */
@media (max-width: 719px) {
  .qo-scrim { padding: 0; }
  .qo-panel { width: 100vw; height: 100vh; border-radius: 0; max-width: none; }
  .qo-list { max-height: none; flex: 1; }
}

/* §H10e — "+ spawn" trigger + Spawn-agent modal dialog */
/* #156: wrap + min-width:0 so the 5 buttons reflow inside the 400px popover instead of clipping. */
.cp-agents-head-actions { display: inline-flex; gap: var(--s-2); align-items: center; flex-wrap: wrap; min-width: 0; }
.cp-spawn {
  background: none; border: none; color: var(--accent); font: inherit; font-size: var(--t-xs, 0.85em);
  cursor: pointer; padding: 2px 4px; border-radius: var(--r-sm); font-family: var(--mono, monospace);
}
.cp-spawn:hover { background: var(--code-bg); }
.spawn-overlay {
  /* BUG#107: z-index 400 đặt lớp này DƯỚI mọi modal khác (.np-modal-scrim 1130,
     .tm-settings-scrim 1120, .vm-overlay 1100, .project-kebab-menu 1130). confirmThen()
     (app.js:1173) dùng chính .spawn-overlay để vẽ hộp "Please confirm" DÙNG CHUNG toàn app, nên
     mỗi khi một modal đang mở mà code hỏi xác nhận, hộp đó bị CHÔN phía sau: user thấy câu hỏi
     nhưng nút Confirm không ăn click (Playwright báo đúng: ".np-pick-row … intercepts pointer
     events"). Đo LIVE 01-08: New Project + folder đã thuộc project khác → overlay mở, bấm Confirm
     20s không đóng, team KHÔNG được tạo (agentsCount delta = 0).
     Hộp xác nhận là lớp CUỐI của một tương tác — nó phải nằm trên mọi modal, không riêng ca này.
     Đặt trên .np-modal-scrim/.tm-settings-scrim nhưng DƯỚI .pv-stream-overlay (9000) và
     .tm-cli-auth-scrim (10000) — hai lớp đó là toàn màn có chủ ý. */
  position: fixed; inset: 0; z-index: 1300; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.55);
}
.spawn-card {
  background: var(--panel, var(--bg-elev)); border: 1px solid var(--border-strong);
  border-radius: 14px; box-shadow: 0 24px 50px rgba(0,0,0,0.6);
  padding: 16px; width: 280px; display: flex; flex-direction: column; gap: 11px;
  /* A tall spawn form (targets + mode + scheduler) must NOT push the Add button below the
     viewport. Cap the card, let its body scroll, and keep the footer (Huỷ / Add) visible. */
  max-height: 90vh; overflow-y: auto; overscroll-behavior: contain;
}
.spawn-title { font-size: var(--t-md, 13px); font-weight: 600; color: var(--fg-strong); }
.spawn-field { display: flex; flex-direction: column; gap: 5px; }
.spawn-row2 { display: flex; gap: 10px; }
.spawn-half { flex: 1; min-width: 0; }
.spawn-cli-note { font-size: 11px; color: #f0b429; min-height: 14px; }
/* Flow D2: recurring work moved to the ⏰ Schedules chip — the spawn form only points there. */
.spawn-sched-moved { font-size: 11px; color: #8b909a; line-height: 1.45; }
.spawn-input option:disabled { color: #5a6069; }
.spawn-lbl { font-size: 10px; font-family: var(--mono, monospace); letter-spacing: 0.06em; color: var(--muted); }
.spawn-input {
  background: var(--code-bg, #0e1014); border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 10px; font: inherit; font-size: var(--t-sm); color: var(--fg-strong);
}
.spawn-input:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.spawn-prompt { resize: vertical; min-height: 34px; font-size: var(--t-xs, 12px); }
.spawn-chips { display: flex; gap: 5px; flex-wrap: wrap; }
.spawn-chip {
  padding: 3px 8px; border-radius: 999px; border: 1px solid var(--border); background: none;
  color: var(--muted); font: inherit; font-size: 10px; cursor: pointer;
}
.spawn-chip.on { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); border-color: transparent; }
.spawn-team-note { font-size: 11px; color: var(--muted); }
.spawn-team-note b { color: var(--fg); }
/* Target section (P2.2): multi-select run-on-host/VM checkboxes. */
.spawn-targets { display: flex; flex-direction: column; gap: 5px; }
.spawn-target { display: flex; align-items: center; gap: 8px; padding: 6px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel-2); font-size: 12px; color: #cbd0d8; cursor: pointer; }
.spawn-target:hover { border-color: var(--accent); }
.spawn-target input { accent-color: var(--accent); flex: none; width: 14px; height: 14px; margin: 0; }
.spawn-target-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.spawn-target-newvm { border-style: dashed; }
.spawn-newvm-os { display: flex; flex-direction: column; gap: 4px; padding: 6px 9px 2px 30px; }
.spawn-newvm-os select { width: 100%; }
.spawn-newvm-hint { font-size: 11px; color: var(--muted); }
/* MODE toggle (P2.10): terminal / VS Code segmented control. */
.spawn-mode { display: inline-flex; gap: 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.spawn-mode-btn { padding: 5px 14px; border: none; background: transparent; color: var(--muted); font: inherit; font-size: 12px; cursor: pointer; }
.spawn-mode-btn + .spawn-mode-btn { border-left: 1px solid var(--border); }
.spawn-mode-btn.on { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent); }
.spawn-mode-hint { display: block; margin-top: 5px; font-size: 11px; color: var(--muted); }
.spawn-error { font-size: 11px; color: var(--danger, #ff6b6b); line-height: 1.35; }
.spawn-actions {
  display: flex; gap: 8px; margin-top: 2px;
  /* Sticky footer INSIDE the scrolling card so Add/Huỷ stay reachable at any scroll position.
     The negative margins + padding pull it flush with the card's inner edges and back the
     buttons with the card colour so scrolled content doesn't bleed underneath. */
  position: sticky; bottom: -16px; margin: 2px -16px -16px; padding: 12px 16px 16px;
  background: var(--panel, var(--bg-elev)); border-top: 1px solid var(--border);
  border-radius: 0 0 14px 14px;
}
/* #73 — the design-matched dialogs (New employee / New team / Add device …) are built from <div>s with
   INLINE styles, so none of the .spawn-card / .spawn-actions rules above reach them. Their card sets its
   own max-height + overflow-y:auto but leaves the footer in normal flow, so the primary action scrolls
   away with the content: measured on New employee at 1280×900 — card 810px tall holding 1087px of
   content, #spawn-go at y=1072..1112, i.e. 212px BELOW the fold. The screenshot shows a dialog cut off
   mid-"MODEL" with no button anywhere on screen, after the user has already filled six fields.
   DOM visibility reports true throughout, which is why a click-only test never caught it — only
   geometry does.
   One class on the footer, same sticky treatment .spawn-actions already gets, applied at the six
   build sites at once (they share the identical inline style string, so fixing one and missing five
   was the real risk here).
   The negative margins mirror the card's OWN padding (measured 20px 22px, radius 16px — not the 16/14
   of .spawn-card) so the bar sits flush with the card's inner edges instead of leaving a gutter, and it
   is painted with the card's own background so scrolled content cannot show through underneath. */
.dlg-footer {
  position: sticky; bottom: -20px; z-index: 5;
  /* -21px, not -20px: the card's bottom padding is 20px and a sticky box at exactly -20px leaves a
     hairline of scrolled content peeking under the bar on fractional-DPI displays. */
  margin: 8px -22px -21px; padding: 14px 22px 20px;
  /* OPAQUE. The first attempt used var(--code-bg) and the bar rendered TRANSPARENT over the CLI row —
     the hint text sat on top of the Claude/Antigravity buttons (see /tmp/qc2-73-fixed.png). The card's
     own computed background is rgb(14,16,20); state it literally so the fallback can't resolve to a
     transparent or unset custom property. */
  background: #0e1014; border-top: 1px solid var(--border);
  border-radius: 0 0 16px 16px;
}
/* The hint ("Only Role + 1 VM are required…") shares the bar with the buttons; keep it from pushing
   them off the edge on a narrow card. */
.dlg-footer > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* PO 2026-09-30 (Edit employee · Settings · Harness): the whole card scrolled, so the title, ✕ and
   the tabs left the screen. The header row is pinned the way .dlg-footer pins the footer: sticky at
   the card's top edge, margins mirroring the card's own padding (20px 22px) so nothing moves, painted
   with the card's background. .dlg-head marks it explicitly (Edit employee: title + tabs); the other
   design dialogs (New employee / New team / Add existing / Add device) get it on their first row via
   :has(> .dlg-footer), without touching their markup. */
.dlg-head,
.spawn-overlay > [role="dialog"]:has(> .dlg-footer) > :first-child {
  position: sticky; top: -20px; z-index: 6;
  margin: -20px -22px -10px; padding: 20px 22px 10px;
  background: #0e1014; border-radius: 16px 16px 0 0;
}
/* Harness action bar (status + Discard + Save & Apply) stays at the bottom edge while the flow
   editor / history scroll inside the Edit-employee card. */
.ee-box .hz-savebar {
  position: sticky; bottom: -20px; z-index: 5;
  margin: 0 -22px; padding: 10px 22px 20px;
  background: #0e1014; border-top: 1px solid var(--border);
}

.spawn-btn {
  flex: 1; border: 1px solid var(--border); background: none; color: var(--fg); font: inherit;
  font-size: var(--t-sm); border-radius: 8px; padding: 8px 0; cursor: pointer;
}
.spawn-btn:hover { background: var(--code-bg); }
.spawn-btn-primary { border: none; background: var(--accent); color: #fff; font-weight: 600; }
.spawn-btn-primary:disabled { opacity: 0.6; cursor: default; }

/* ============================================================================
   H2.6/H2.8 dialogs — design-matched (PPTX Current Project Switcher).
   Namespaced .se-* ("spawn employee"); the design card is 560px, radius 16,
   bg #0e1014, fields #16181d radius 10, mono labels ls .14em. Values copied
   from the rendered design DOM (design_handoff/PPTX-BUSINESS-SPEC.md).
   ========================================================================== */
.se-card {
  background: #0e1014; border: 1px solid rgba(255,255,255,.12);
  border-radius: 16px; box-shadow: 0 24px 60px rgba(0,0,0,.55);
  padding: 20px 22px; width: 560px; display: flex; flex-direction: column; gap: 16px;
  max-height: 90vh; overflow-y: auto; overscroll-behavior: contain;
  font-family: var(--font-sans, system-ui);
}
.se-head { display: flex; align-items: center; gap: 9px; }
.se-title { font-size: 15px; font-weight: 600; color: #e6e8ec; }
.se-title-sub { font-size: 10px; font-family: var(--font-mono, monospace); color: #6e7681; }
.se-x { margin-left: auto; color: #6e7681; font-size: 15px; cursor: pointer; background: none; border: none; padding: 0; line-height: 1; }
.se-x:hover { color: #e6e8ec; }
.se-field { display: flex; flex-direction: column; gap: 7px; }
.se-row { display: flex; gap: 12px; }
.se-lbl { font-size: 10px; font-family: var(--font-mono, monospace); letter-spacing: .14em; color: #6e7681; }
.se-lbl .req { color: #c4c8ce; font-weight: 600; }
.se-lbl .opt { color: #c4c8ce; font-weight: 600; }
.se-lbl .dim { color: #5a6069; }
.se-hint { font-size: 10px; font-family: var(--font-mono, monospace); color: #5a6069; line-height: 1.5; }
/* a filled control (select/input face) */
.se-input {
  display: flex; align-items: center; gap: 10px; background: #16181d;
  border: 1px solid rgba(255,255,255,.1); border-radius: 10px; padding: 11px 14px;
  font: inherit; font-size: 13px; color: #e6e8ec; width: 100%; box-sizing: border-box;
}
.se-input:focus-visible, .se-input select:focus-visible, .se-input textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
select.se-input { -webkit-appearance: none; appearance: none; cursor: pointer; padding-right: 30px;
  background-image: linear-gradient(45deg,transparent 50%,#6e7681 50%),linear-gradient(135deg,#6e7681 50%,transparent 50%);
  background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
textarea.se-input { min-height: 52px; resize: vertical; line-height: 1.55; }
.se-input::placeholder, textarea.se-input::placeholder { color: #5a6069; }
/* role/chip row */
.se-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.se-chip { flex: 1 1 0; text-align: center; padding: 9px; border-radius: 10px; font-size: 12px;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: #8b909a; cursor: pointer; }
.se-chip.on { border-color: rgba(55,148,255,.5); background: rgba(55,148,255,.1); color: #3794ff; font-weight: 600; }
.se-chip.dashed { border-style: dashed; border-color: rgba(255,255,255,.22); }
/* CLI pills (dot + name) */
.se-clis { display: flex; gap: 8px; }
.se-cli { flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: 7px; padding: 10px;
  border-radius: 10px; border: 1px solid rgba(255,255,255,.12); color: #8b909a; font-size: 13px; cursor: pointer; }
.se-cli.on { border-color: rgba(55,148,255,.5); background: rgba(55,148,255,.1); color: #3794ff; font-weight: 600; }
.se-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* VM device cards (icon + name + check) */
.se-vms { display: flex; gap: 10px; }
.se-vm { flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: 11px;
  background: #16181d; border: 1px solid rgba(255,255,255,.1); border-radius: 12px; padding: 13px 15px; cursor: pointer; }
.se-vm.on { background: rgba(55,148,255,.06); border-color: rgba(55,148,255,.5); }
.se-vm-ic { color: #6e7681; display: inline-flex; flex: none; }
.se-vm.on .se-vm-ic { color: #3794ff; }
.se-vm-nm { font-size: 14px; font-weight: 600; color: #e6e8ec; }
.se-vm-meta { font-size: 10px; font-family: var(--font-mono, monospace); color: #6e7681; }
.se-vm-chk { margin-left: auto; color: #3794ff; display: inline-flex; flex: none; }
/* CHOOSE OS block (indented under the checked VM) */
.se-os-wrap { margin-left: 6px; border-left: 2px solid rgba(55,148,255,.4); padding: 2px 0 2px 14px; display: flex; flex-direction: column; gap: 9px; }
.se-os-lbl { font-size: 9px; font-family: var(--font-mono, monospace); letter-spacing: .14em; color: #6e7681; }
.se-oss { display: flex; gap: 8px; flex-wrap: wrap; }
.se-os { display: inline-flex; align-items: center; gap: 8px; padding: 9px 13px; border-radius: 10px;
  border: 1px solid rgba(255,255,255,.12); background: transparent; font-size: 13px; font-weight: 600; color: #c4c8ce; cursor: pointer; }
.se-os.on { border-color: rgba(55,148,255,.5); background: rgba(55,148,255,.08); color: #e6e8ec; }
.se-os.disabled { color: #5a6069; opacity: .7; cursor: default; }
.se-os-badge { width: 20px; height: 20px; border-radius: 6px; color: #fff; display: grid; place-items: center;
  font-size: 11px; font-weight: 700; font-family: var(--font-mono, monospace); flex: none; }
.se-os-star { color: #e0b341; font-size: 14px; line-height: 1; }
.se-os-xcode { font-size: 9px; font-family: var(--font-mono, monospace); color: #5a6069; font-weight: 400; }
.se-addmore { display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px; border-radius: 8px;
  border: 1px dashed rgba(55,148,255,.5); color: #3794ff; font-size: 11px; font-weight: 600; cursor: pointer; background: none; }
/* CLI location segmented (On host / Inside main device) */
.se-seg2 { display: flex; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; overflow: hidden; font-size: 12px; }
.se-seg2-btn { flex: 1 1 0; display: flex; flex-direction: column; gap: 2px; padding: 9px 12px; background: transparent; border: none; color: #8b909a; cursor: pointer; text-align: left; }
.se-seg2-btn + .se-seg2-btn { border-left: 1px solid rgba(255,255,255,.1); }
.se-seg2-btn.on { background: rgba(55,148,255,.15); color: #3794ff; font-weight: 600; }
.se-seg2-sub { font-size: 9px; font-family: var(--font-mono, monospace); color: #6e7681; font-weight: 400; }
/* MODEL + effort row */
.se-model-row { display: flex; gap: 10px; }
.se-model-row .se-input:first-child { width: 170px; flex: none; }
.se-badge-think { font-size: 8px; font-family: var(--font-mono, monospace); color: #46d19e; background: rgba(70,209,158,.12); border-radius: 4px; padding: 2px 5px; }
.se-eff { font-size: 9px; font-family: var(--font-mono, monospace); color: #6e7681; }
/* MEMBER TYPE cards */
.se-mts { display: flex; gap: 8px; }
.se-mt { flex: 1 1 0; display: flex; flex-direction: column; gap: 5px; padding: 12px 13px; border-radius: 12px;
  border: 1px solid rgba(255,255,255,.1); background: #16181d; cursor: pointer; }
.se-mt.on { border-color: rgba(55,148,255,.5); background: rgba(55,148,255,.06); }
.se-mt-nm { font-size: 13px; font-weight: 600; color: #e6e8ec; }
.se-mt.on .se-mt-nm { color: #3794ff; }
.se-mt-desc { font-size: 10px; font-family: var(--font-mono, monospace); color: #6e7681; line-height: 1.4; }
/* divider + checkbox rows */
.se-divider { height: 1px; background: rgba(255,255,255,.06); }
.se-checks { display: flex; flex-direction: column; gap: 11px; }
.se-check { display: flex; align-items: center; gap: 11px; cursor: pointer; }
.se-check-box { width: 18px; height: 18px; border-radius: 6px; border: 1px solid rgba(255,255,255,.2); display: grid; place-items: center; flex: none; }
.se-check.on .se-check-box { background: #3794ff; border-color: #3794ff; color: #fff; }
.se-check-lbl { font-size: 13px; color: #c4c8ce; flex: 1; }
.se-check.on .se-check-lbl { color: #e6e8ec; }
.se-check-note { font-size: 9px; font-family: var(--font-mono, monospace); color: #5a6069; }
/* footer */
.se-foot { display: flex; align-items: center; gap: 12px; padding-top: 2px; }
.se-foot-note { flex: 1; font-size: 11px; color: #6e7681; line-height: 1.4; }
.se-btn { font-size: 13px; font-weight: 600; padding: 10px 18px; border-radius: 10px; cursor: pointer; border: 1px solid rgba(255,255,255,.12); background: transparent; color: #c4c8ce; }
.se-btn:hover { background: #16181d; }
.se-btn-primary { border: none; background: #3794ff; color: #fff; padding: 10px 20px; }
.se-btn-primary:hover { background: var(--accent-press, #2b7bdc); }
.se-btn-danger { border: none; color: #ff6b6b; margin-right: auto; padding: 10px 4px; }
.se-btn-danger:hover { background: rgba(255,107,107,.08); }
.se-err { font-size: 11px; color: #ff6b6b; line-height: 1.35; }

/* CHOOSE VM · pick existing or + New VM (TM.vmpool, TR-1) — grid of pool VMs + New-VM tile. */
.vmp { display: flex; flex-direction: column; gap: 9px; }
.vmp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.vmp-card { display: flex; align-items: center; gap: 9px; padding: 10px 12px; border-radius: 10px;
  border: 1px solid rgba(255,255,255,.12); background: #16181d; color: #e6e8ec; cursor: pointer; text-align: left; }
.vmp-card.on { border-color: rgba(55,148,255,.5); background: rgba(55,148,255,.08); }
.vmp-card-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.vmp-card-name { font-size: 13px; font-weight: 600; }
.vmp-card-meta { font-size: 10px; font-family: var(--font-mono, monospace); color: #6e7681; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vmp-card-chk { margin-left: auto; color: #3794ff; display: inline-flex; flex: none; }
.vmp-new { justify-content: center; border-style: dashed; border-color: rgba(55,148,255,.5); color: #3794ff; }
.vmp-new .vmp-card-name { color: #3794ff; }
.vmp-new-plus { font-size: 15px; line-height: 1; }
.vmp-os-wrap { margin-left: 6px; border-left: 2px solid rgba(55,148,255,.4); padding: 2px 0 2px 14px; display: flex; flex-direction: column; gap: 9px; }

/* New team (H2.8/H2.13) — Manual member editor cards. */
.nt-pane { display: flex; flex-direction: column; gap: 16px; }
.nt-member { display: flex; flex-direction: column; gap: 6px; padding: 13px 14px; border-radius: 12px; border: 1px solid rgba(255,255,255,.1); background: #14161b; }
.nt-member-head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.nt-member-n { width: 18px; height: 18px; border-radius: 5px; background: rgba(55,148,255,.15); color: #3794ff; display: grid; place-items: center; font-size: 11px; font-weight: 700; flex: none; }
.nt-member-t { font-size: 13px; font-weight: 600; color: #e6e8ec; }
.nt-member-head .se-x { margin-left: auto; }
.nt-vmp { margin-top: 2px; }

/* Agent ⚙ menu divider (H2.2: common actions above, advanced ops below). */
.agent-menu-divider { height: 1px; background: rgba(255,255,255,.08); margin: 4px 0; }

/* DESIGN v3 H2 avatar (tpl 171/189) — an 18px PERSON chip pinned at the card's bottom-right.
   LIVE (busy) = solid purple-gradient + white glyph + dark ring + shadow; GHOST (else) = dark bg,
   dashed border, grey glyph. All literals from the render. */
.roster-avatar { position: absolute; right: 9px; bottom: 8px; width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center; overflow: hidden; padding: 0; cursor: pointer; }
.roster-avatar:hover { box-shadow: 0 0 0 2px rgba(91, 141, 239, 0.5); }
/* PO 21-07: a picked preset replaces the person glyph — the emoji IS the whole avatar, no chip
   ring/background around it ("cắt tròn thì cắt chứ sao sơ vòng tròn đó"). */
.roster-avatar.picked, .roster-avatar.picked.live { background: none; border: none; box-shadow: none; }
.roster-avatar-emoji { font-size: 15px; line-height: 1; }
/* PO 21-07 avatar picker popup */
.avatar-picker { background: var(--panel, #14161b); border: 1px solid var(--line, #2a2d34); border-radius: 10px; padding: 10px; width: 196px; box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5); }
.avatar-picker-title { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; color: var(--muted); margin-bottom: 8px; text-transform: uppercase; }
.avatar-picker-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.avatar-picker-opt { font-size: 18px; line-height: 1; padding: 7px 0; background: rgba(255, 255, 255, 0.03); border: 1px solid transparent; border-radius: 8px; cursor: pointer; }
.avatar-picker-opt:hover { background: rgba(91, 141, 239, 0.15); }
.avatar-picker-opt.sel { border-color: var(--accent, #5b8def); background: rgba(91, 141, 239, 0.12); }
.avatar-picker-reset { margin-top: 8px; width: 100%; font-size: 12px; color: var(--muted); background: none; border: 1px solid var(--line, #2a2d34); border-radius: 8px; padding: 5px 0; cursor: pointer; }
.avatar-picker-reset:hover { color: var(--fg); background: rgba(255, 255, 255, 0.04); }
.roster-avatar.live { background: linear-gradient(150deg, rgb(91, 141, 239), rgb(139, 92, 246)); border: 1.5px solid rgb(11, 12, 15); box-shadow: rgba(0, 0, 0, 0.4) 0px 1px 3px; }
.roster-avatar.ghost { background: rgb(20, 22, 27); border: 1px dashed rgba(255, 255, 255, 0.22); }

/* MEMBER TYPE cards — disabled state (H2.7: standalone unavailable when a team is chosen). */
.se-mt-disabled { opacity: .45; cursor: default; }
.se-mt-disabled .se-mt-desc { color: #5a6069; }
/* Add-existing employee list (se-card context). */
.add-existing-list { display: flex; flex-direction: column; gap: 6px; max-height: 160px; overflow-y: auto; }
.add-existing-item { display: flex; align-items: center; gap: 9px; padding: 9px 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,.1); background: #16181d; cursor: pointer; font-size: 13px; color: #e6e8ec; }
.add-existing-item input { accent-color: var(--accent); flex: none; }
.add-existing-name { font-weight: 600; }
.add-existing-sub { font-size: 10px; font-family: var(--font-mono, monospace); color: #6e7681; margin-left: auto; }

.cp-todo { display: flex; gap: var(--s-2); padding: var(--s-1) var(--s-1); border-top: 1px solid var(--border); }
.cp-todo.done .cp-todo-text { color: var(--muted); text-decoration: line-through; }
.cp-check { flex: none; color: var(--accent); }
.cp-team { color: var(--fg); padding: var(--s-1) var(--s-1); }
.cp-hint { color: var(--muted); padding: var(--s-1) var(--s-1); font-size: var(--t-base); }
/* R-05 Sub-agents popover — subagentType + description + model + running/done status. */
.cp-subs { list-style: none; margin: 0; padding: 0; }
/* dc:1467 — sub-agent step row: literal pad 8/11 + font 11 (not --s-2/--s-1 = 8/4, --t-sm 12). */
.cp-sub-row { display: flex; align-items: center; gap: var(--s-2); padding: 8px 11px; border-top: 1px solid var(--border); font-size: 11px; min-width: 0; }
.cp-sub-row.done .cp-sub-name { color: var(--muted); }
.cp-sub-status { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 16px; }
.cp-sub-status.done { color: var(--green); }
.cp-sub-name { flex: none; font-weight: 600; color: var(--fg-strong); }
.cp-sub-desc { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.cp-sub-model { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--faint); }
/* R-04 Background popover — the run_in_background command (mono, ellipsis). */
.cp-bg-cmd { flex: none; max-width: 45%; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-spin { width: 11px; height: 11px; border-radius: 50%; border: 2px solid var(--border-strong); border-top-color: var(--accent); display: inline-block; animation: cpspin 0.7s linear infinite; }
@keyframes cpspin { to { transform: rotate(360deg); } }
/* PO 31-07 — Live-CLI + Devices popover rows. Same geometry as .cp-sub-row (pad 8/11, font 11) so
   the three lists read as ONE component family; these two are BUTTONS (they act) so they get the
   hover/focus affordance a status row must not have. */
.cp-cli-list, .cp-dev-list { list-style: none; margin: 0; padding: 0; }
.cp-cli-row, .cp-dev-row {
  display: flex; align-items: center; gap: var(--s-2); width: 100%;
  padding: 8px 11px; border: 0; border-top: 1px solid var(--border);
  background: transparent; color: inherit; font-size: 11px; text-align: left;
  cursor: pointer; min-width: 0;
}
.cp-cli-row:hover, .cp-dev-row:hover { background: var(--bg-hover, rgba(255, 255, 255, 0.05)); }
.cp-cli-row:focus-visible, .cp-dev-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* offline machine → the row is disabled (never a button that pretends it can connect). */
.cp-dev-row[disabled] { cursor: not-allowed; opacity: 0.55; }
.cp-dev-row[disabled]:hover { background: transparent; }
.cp-cli-name, .cp-dev-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; color: var(--fg-strong); }
.cp-cli-tag, .cp-dev-os { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--faint); }
.cp-cli-go, .cp-dev-go { flex: none; font-family: var(--font-mono); color: var(--muted); }
.cp-cli-row:hover .cp-cli-go, .cp-dev-row:not([disabled]):hover .cp-dev-go { color: var(--accent); }
/* idle = paired but not busy: a dimmed dot, distinct from online (green) and offline (red). */
.chip-dot.idle { background: var(--muted); }
.chip-devices .chip-icon { color: #38bdf8; }
.chip-devices { border-color: rgba(56, 189, 248, 0.4); }

/* R-09 Team-tasks board — task list + "+ giao task". */
.cp-tasks { list-style: none; margin: 0; padding: 0; }
/* dc:1886 (H14b) — task row: literal pad 10/12 + font 12 (not --s-2/--s-1 = 8/4). Bigger than the
   sub-agent row (8/11) — verified per-element, not unified. */
.cp-task { display: flex; align-items: center; gap: var(--s-2); padding: 10px 12px; border-top: 1px solid var(--border); font-size: 12px; min-width: 0; }
.cp-task[data-open-chat] { cursor: pointer; }
.cp-task[data-open-chat]:hover { background: var(--panel-2); }
.cp-task.done .cp-task-title { color: var(--muted); text-decoration: line-through; }
.cp-task-status { flex: none; width: 16px; text-align: center; }
.cp-task-status.done { color: var(--green); }
.cp-task-status.open { color: var(--muted); }
/* HARNESS-8 DAG: a blocked task (waiting on its dependsOn chain) is dimmed + not clickable, with a
   "waits on" chip so the leader sees WHY it isn't claimable yet. */
.cp-task-status.blocked { color: var(--amber, #d9a441); }
.cp-task.blocked { opacity: 0.62; cursor: default; }
.cp-task.blocked .cp-task-title { font-style: italic; }
.cp-task-deps { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--amber, #d9a441); white-space: nowrap; }
.cp-task-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-task-id { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); }
/* Task steps (thread.js taskStepsSummary/taskStepsList): a step count + "stalled" badge beside the
   title, the checklist on its own full-width line under the row. Shared by .cp-task and .ins-todo-item. */
.cp-task.has-steps, .ins-todo-item.has-steps { flex-wrap: wrap; }
.task-steps-count { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); }
.task-stalled { flex: none; padding: 0 6px; font-size: var(--t-xs); font-weight: 600; color: var(--amber, #d9a441); border: 1px solid var(--amber, #d9a441); border-radius: var(--r-sm); }
.task-steps { flex-basis: 100%; list-style: none; margin: 2px 0 0; padding: 0 0 0 24px; }
.task-step { display: flex; align-items: baseline; gap: var(--s-1); font-size: var(--t-xs); min-width: 0; }
.task-step-box { flex: none; width: 14px; text-align: center; color: var(--muted); }
.task-step.st-done .task-step-box { color: var(--green); }
.task-step.st-done .task-step-title { color: var(--muted); text-decoration: line-through; }
.task-step.st-blocked .task-step-box, .task-step-note { color: var(--amber, #d9a441); }
.task-step-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-step-note { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A done step's proof: the evidence line + the file it points at, on the step row itself. */
.task-step-note.task-step-evidence { color: var(--muted); }
.task-step-path { flex: none; max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--accent); }
.cp-task.has-steps .cp-task-title { flex: 1 1 0; }
.cp-section-title { margin: var(--s-2) 0 var(--s-1); font-size: var(--t-xs); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.cp-task-assignee { flex: none; font-family: var(--font-mono); font-size: var(--t-xs); color: var(--accent); }
.cp-add-task { margin-left: auto; padding: 2px var(--s-2); font-size: var(--t-xs); font-weight: 600; color: var(--accent); background: transparent; border: 1px solid var(--border); border-radius: var(--r-sm); cursor: pointer; }
.cp-add-task:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* G-07 (UC-157): the ▾N chooser footer link → Monitor roster. Full-width, accent, hairline top. */
.cp-open-roster {
  display: block; width: 100%; margin-top: var(--s-2); padding: 7px 10px;
  font-size: var(--t-xs); color: var(--accent); text-align: left;
  background: transparent; border: none; border-top: 1px solid var(--border); cursor: pointer;
}
.cp-open-roster:hover { background: var(--accent-wash); }
/* R-06 Uncommitted popover — Stage all / Commit… actions. */
.cp-git-actions { display: flex; gap: var(--s-1); padding: var(--s-1) var(--s-1) var(--s-2); border-bottom: 1px solid var(--border); margin-bottom: var(--s-1); }
.cp-git-btn { flex: 1; padding: var(--s-1) var(--s-2); font-size: var(--t-xs); font-weight: 600; color: var(--fg); background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--r-sm); cursor: pointer; }
.cp-git-btn:hover { border-color: var(--border-strong); background: var(--panel); }
.cp-git-commit { color: var(--accent); }
.cp-git-commit:hover { border-color: var(--accent); }

/* ── #106 PO "mirror html": thread message styles ported 1:1 from the extension
   webview (chatThreadWebview) so web prose renders IDENTICALLY — same .message /
   .md markdown / .timestamp / code / table / file-link. ───────────────────── */
.message { padding: 8px 12px; border-radius: 4px; max-width: 100%; word-wrap: break-word; }
/* The sender's own turn reads as a bubble on the RIGHT: capped width + the accent tint and left bar
   of .titem.user. With max-width 100% and no fill, a long pasted request filled the thread and read
   as agent text on the left (PO 01-10). */
.user-message {
  color: var(--vscode-input-foreground, #ddd); align-self: flex-end; max-width: 78%;
  background: rgba(55, 148, 255, 0.10); border-left: 3px solid var(--accent);
  border-radius: 2px var(--r-md) var(--r-md) 2px;
}
/* UC-257: a message that failed to send reads as a light-red bubble + a "Gửi lại" retry affordance. */
.user-message.send-failed {
  background: var(--vscode-inputValidation-errorBackground, rgba(190, 17, 33, 0.14));
  border: 1px solid var(--vscode-inputValidation-errorBorder, rgba(190, 17, 33, 0.55));
  border-radius: 6px;
}
.msg-failfoot { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 4px; }
.msg-failfoot .fail-note { font-size: 0.78em; color: var(--vscode-errorForeground, #f14c4c); opacity: 0.9; }
.msg-retry {
  font: inherit; font-size: 0.82em; cursor: pointer; padding: 1px 8px; border-radius: 4px;
  color: var(--vscode-button-foreground, #fff);
  background: var(--vscode-button-background, #0e639c);
  border: 1px solid var(--vscode-button-border, transparent);
}
.msg-retry:hover { background: var(--vscode-button-hoverBackground, #1177bb); }
.assistant-message { color: var(--vscode-input-foreground, #ddd); align-self: flex-start; }
.msg-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 4px; }
.timestamp { font-size: 0.8em; opacity: 0.7; margin-top: 4px; }
.timestamp .ts-time { font-family: var(--font-mono); opacity: 0.85; }
/* UC-225 (I-04): day-divider between messages of different calendar days. A centered hairline label. */
.day-divider {
  display: flex; align-items: center; gap: 10px; align-self: stretch;
  margin: 10px 2px; font-family: var(--font-mono); font-size: 10px;
  letter-spacing: .04em; color: var(--faint); text-transform: uppercase;
}
.day-divider::before, .day-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
/* UC-253 (I-28): an interrupted user-turn reads as a muted label, not a bubble. */
.user-message.interrupted { background: transparent; border-left: none; padding: 4px 12px; }
.interrupt-label { font-size: 11px; color: var(--faint); font-style: italic; }
/* #51: a message queued to a sleeping/gone agent (pending:true) — dimmed bubble + a "waiting to
   deliver — waking…" foot with a pulsing dot, instead of a settled "You · time" that would read as
   delivered. The message text stays legible; only the status foot signals it hasn't landed yet. */
.user-message.pending-delivery { opacity: 0.72; }
.pending-foot { display: flex; align-items: center; justify-content: flex-end; gap: 6px; margin-top: 4px; }
.pending-foot .pending-note {
  font-size: 0.78em; font-style: italic; color: var(--faint);
  display: inline-flex; align-items: center; gap: 6px;
}
.pending-foot .pending-note::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--vscode-charts-yellow, #d7b74a);
  animation: pendingPulse 1.4s ease-in-out infinite;
}
@keyframes pendingPulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } }
/* #203: a pending/undelivered marker RECONCILED — a later item in the same thread proved the agent
   is alive again (a newer send delivered or a newer reply landed). No longer a live actionable
   failure, so no pulsing dot; a flat muted note instead. Never deleted (honesty) and never resent
   automatically — this is purely a re-render of an already-known state. */
.user-message.pending-superseded { opacity: 0.55; }
.pending-foot .superseded-note { font-style: italic; color: var(--faint); }
.pending-foot .superseded-note::before { content: none; }
/* DESIGN v3 H2 computed: thread message text = rgb(230,232,236) (was 187,190,191 — dimmer/greyer
   than the design bubbles/replies). */
.md { color: rgb(230, 232, 236); }
.md p { margin: 0 0 6px 0; }
.md p:last-child { margin-bottom: 0; }
.md h1, .md h2, .md h3, .md h4 { margin: 8px 0 4px 0; font-weight: 600; line-height: 1.3; }
.md h1 { font-size: 1.25em; }
.md h2 { font-size: 1.15em; }
.md h3 { font-size: 1.05em; }
.md h4 { font-size: 1em; opacity: 0.9; }
.md ul, .md ol { margin: 4px 0 6px 0; padding-left: 20px; }
.md li { margin: 2px 0; }
.md a { color: var(--vscode-textLink-foreground, #4daafc); }
/* G8: the ↗ affordance on an EXTERNAL (target=_blank) chat link — a small superscript glyph so the
   user knows the link leaves the app. Muted, non-underlined, nudged up a hair. */
.md a .ext-link-ico { font-size: 0.8em; margin-left: 2px; opacity: 0.7; vertical-align: 1px; text-decoration: none; }
/* PREVIEW CONTROL (```preview``` marker) — a proxy link + optional remote-desktop buttons. Tone matches
   the code-block copy button (subtle chrome, accent on hover) so it reads as an inline affordance. */
.pv-control { display: inline-flex; align-items: center; gap: 4px; margin: 4px 0; padding: 3px 4px 3px 8px;
  border: 1px solid var(--vscode-panel-border, rgba(128,128,128,0.3)); border-radius: 7px;
  background: rgba(55,148,255,0.06); max-width: 100%; }
.pv-open { display: inline-flex; align-items: center; gap: 5px; font-weight: 600; text-decoration: none;
  color: var(--accent, rgb(55,148,255)); padding: 2px 4px; border-radius: 5px; }
.pv-open:hover { background: rgba(55,148,255,0.12); }
.pv-open-ico { font-size: 0.85em; opacity: 0.8; }
.pv-remote-wrap { display: inline-flex; align-items: center; gap: 1px; border-left: 1px solid rgba(128,128,128,0.28); margin-left: 3px; padding-left: 4px; }
.pv-remote, .pv-remote-more, .pv-remote-full { display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid transparent; border-radius: 5px; color: var(--fg, #c8ccd4);
  cursor: pointer; padding: 3px; opacity: 0.85; }
.pv-remote-more { padding: 3px 2px; font-size: 11px; }
.pv-remote:hover, .pv-remote-more:hover, .pv-remote-full:hover { opacity: 1; color: var(--accent, rgb(55,148,255)); border-color: var(--accent, rgba(55,148,255,0.5)); background: rgba(55,148,255,0.1); }
/* the small "preview / remote view" button md.js appends after a proxied localhost link */
.pv-ask { display: inline-flex; align-items: center; justify-content: center; vertical-align: baseline;
  margin-left: 3px; padding: 1px 3px; background: transparent; border: 1px solid transparent; border-radius: 4px;
  color: var(--fg, #c8ccd4); opacity: 0.7; cursor: pointer; }
.pv-ask:hover { opacity: 1; color: var(--accent, rgb(55,148,255)); border-color: var(--accent, rgba(55,148,255,0.5)); background: rgba(55,148,255,0.1); }
/* Media cards (md.js MEDIA CARDS): an inline picture/video/audio/Drive preview BELOW the block that links it.
   A <details open> the reader can fold; ≤960px wide (PO 30-09: 480 was too small), 16:9 frame for iframe/video, bounded image height.
   A VM file (/api/media/vmf_…) streams natively into its slot; util.js swaps in a typed error + Retry. */
.md-media { display: block; width: 800px; max-width: 100%; box-sizing: border-box; margin: 6px 0 8px; border: 1px solid var(--border-strong); border-radius: 6px;
  background: var(--panel-2); overflow: hidden; }
.md-media-sum { display: flex; align-items: center; gap: 6px; padding: 4px 8px; font-size: 11px; color: var(--muted);
  cursor: pointer; user-select: none; }
.md-media-kind { font-weight: 600; color: var(--fg-2); }
.md-media-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.md-media-body { border-top: 1px solid var(--border); }
.md-media-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.md-media-frame iframe, .md-media-frame video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
.md-media-img { display: block; max-width: 100%; max-height: 720px; margin: 0 auto; object-fit: contain; cursor: zoom-in; }
.md-media-imglink { display: block; }
.md-media-video { display: block; width: 100%; max-height: 720px; aspect-ratio: 16 / 9; background: #000; }
.md-media-audio { display: block; width: 100%; margin: 6px 0; }
.md-media-slot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; min-height: 36px; }
.md-media-slot[data-media-state="ok"] { display: block; min-height: 0; }
.md-media-status, .md-media-err { padding: 8px 10px; font-size: 12px; color: var(--muted); }
.md-media-err { color: var(--fg-2); }
.md-media-retry { margin-right: 8px; padding: 2px 8px; font-size: 11px; color: var(--fg); background: transparent;
  border: 1px solid var(--border-strong); border-radius: 4px; cursor: pointer; }
.md-media-retry:hover { border-color: var(--accent); color: var(--accent); }
.md-media-cap { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; padding: 4px 8px 6px; font-size: 11px;
  border-top: 1px solid var(--border); }
.md-media-note { color: var(--muted); }
/* non-clickable hint shown when a target needs a remote view but the live stream isn't wired yet */
.pv-remote-soon { font-size: 10px; color: var(--fg-dim, #8b93a7); opacity: 0.75; margin-left: 6px; padding: 1px 5px;
  border: 1px dashed rgba(139,147,167,0.35); border-radius: 4px; cursor: default; user-select: none; }
/* Remote-desktop stream overlay (openPreviewStream): a live mirror of the app running in a host Chrome. */
.pv-stream-overlay { position: fixed; inset: 0; z-index: 9000; background: rgba(8,10,16,0.72);
  display: flex; align-items: center; justify-content: center; padding: 24px; }
.pv-stream-panel { display: flex; flex-direction: column; width: min(1280px, 96vw); height: min(860px, 92vh);
  background: var(--bg-panel, #14171f); border: 1px solid rgba(255,255,255,0.12); border-radius: 10px;
  overflow: hidden; box-shadow: 0 18px 60px rgba(0,0,0,0.5); }
.pv-stream-panel:fullscreen { width: 100vw; height: 100vh; border-radius: 0; border: 0; }
.pv-stream-bar { display: flex; align-items: center; gap: 8px; padding: 7px 10px; flex: 0 0 auto;
  background: rgba(255,255,255,0.04); border-bottom: 1px solid rgba(255,255,255,0.08); font-size: 12px; }
.pv-stream-title { font-weight: 600; color: var(--fg, #e6e9f0); }
.pv-stream-status { font-size: 11px; color: var(--fg-dim, #8b93a7); }
.pv-stream-spacer { flex: 1 1 auto; }
.pv-stream-btn { background: transparent; border: 1px solid rgba(255,255,255,0.14); color: var(--fg, #e6e9f0);
  border-radius: 5px; width: 26px; height: 24px; cursor: pointer; font-size: 13px; line-height: 1; }
.pv-stream-btn:hover { background: rgba(255,255,255,0.1); }
.pv-stream-stage { flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
  background: #0b0d13; overflow: hidden; }
.pv-stream-canvas { max-width: 100%; max-height: 100%; outline: none; cursor: crosshair; background: #0b0d13; }
.md strong { font-weight: 600; }
.md-table { border-collapse: collapse; margin: 6px 0; width: 100%; font-size: 0.95em; display: block; overflow-x: auto; }
.md-table th, .md-table td { border: 1px solid var(--vscode-panel-border, rgba(128,128,128,0.3)); padding: 4px 8px; text-align: left; vertical-align: top; }
.md-table th { background-color: var(--vscode-textCodeBlock-background, rgba(127,127,127,0.15)); font-weight: 600; }
.md-table tr:nth-child(even) td { background-color: var(--vscode-list-hoverBackground, rgba(127,127,127,0.06)); }
.inline-code { font-family: var(--vscode-editor-font-family, monospace); font-size: 0.9em; background-color: rgb(49, 49, 48); color: rgb(175, 215, 255); padding: 1px 4px; border-radius: 3px; }
.code-block { position: relative; background-color: rgb(49, 49, 48); border: 1px solid var(--vscode-panel-border, rgba(128,128,128,0.3)); border-radius: 4px; padding: 8px 10px; margin: 6px 0; overflow-x: auto; max-width: 100%; min-width: 0; box-sizing: border-box; }
.code-block code { font-family: var(--vscode-editor-font-family, monospace); font-size: 0.9em; white-space: pre; }
/* I-03 (UC-223/224): code-block copy button — top-right, shown on hover/focus, confirms 1.2s. */
.code-copy {
  position: absolute; top: 6px; right: 6px; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 5px;
  background: rgba(0, 0, 0, 0.35); color: var(--muted); cursor: pointer;
  opacity: 0; transition: opacity 0.12s var(--ease), color 0.12s var(--ease);
}
.code-block:hover .code-copy, .code-copy:focus-visible { opacity: 1; }
.code-copy:hover { color: var(--fg-strong); border-color: var(--accent); }
.code-copy.copied { color: var(--green); border-color: var(--green); }
.code-copy.copied svg { display: none; }
.code-copy.copied::after { content: '✓'; font-size: 13px; line-height: 1; }
.hl-key { color: var(--vscode-symbolIcon-propertyForeground, #9cdcfe); }
.hl-str { color: var(--vscode-charts-orange, #ce9178); }
.hl-num { color: var(--vscode-charts-green, #b5cea8); }
.hl-lit { color: var(--vscode-charts-blue, #569cd6); }
.hl-cmd { color: var(--vscode-charts-yellow, #dcdcaa); }
.hl-flag { color: var(--vscode-charts-purple, #c586c0); }
/* WEB-PARITY-5 #2a — broadened code highlighter tokens (VS Code dark-default hues). */
.hl-comment { color: var(--vscode-charts-green, #6a9955); font-style: italic; }
.hl-kw { color: var(--vscode-charts-blue, #569cd6); }
.hl-type { color: #4ec9b0; }
.file-link { color: var(--vscode-textLink-foreground, #4daafc); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.file-link:hover { opacity: 0.85; }
/* "open in editor" icon sitting to the right of a file-link — click streams-vs-edits split, mirroring pv-ask on a URL. */
.file-edit-ico { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
  width: 18px; height: 18px; margin-left: 3px; border-radius: 4px; cursor: pointer; color: var(--fg-dim, #8b93a7);
  border: 1px solid transparent; }
.file-edit-ico:hover { color: var(--vscode-textLink-foreground, #4daafc); border-color: rgba(77,170,252,0.35); background: rgba(77,170,252,0.10); }

/* ════════════════════════ SCREEN C: VM Menu (SyrixVM) — 07 master-detail ════════════════════════
   3-COLUMN master-detail inside the workspace shell (the real nav-rail is column 0, outside this
   view). #vms-view = master (VM list, 288px) · center (selected VM live screen) · right (AGENTS,
   330px). Values copied literal from the design (dc tpl 4124+): panels #0b0c0f, center bg #0e1014,
   card #16181d, hairlines rgba(255,255,255,.06/.07/.08), mono labels 9px .12em, radius 10px. */
.vms-view { display: flex; min-height: 0; overflow: hidden; background: #0e1014; }

/* ── MASTER column (Company→Workspace→Project + VM list) ── */
.vm-master { width: 288px; flex: 0 0 auto; background: #0b0c0f; border-right: 1px solid rgba(255,255,255,.07); display: flex; flex-direction: column; min-height: 0; }
.vm-scope { padding: 14px 14px 12px; border-bottom: 1px solid rgba(255,255,255,.06); display: flex; flex-direction: column; gap: 10px; }
.vm-scope-btn { display: flex; align-items: center; gap: 10px; background: transparent; border: 1px solid rgba(255,255,255,.08); border-radius: 10px; padding: 8px 11px; cursor: pointer; text-align: left; width: 100%; }
.vm-scope-btn.is-company { background: #16181d; padding: 9px 11px; }
.vm-scope-badge { width: 26px; height: 26px; flex: 0 0 auto; border-radius: 7px; color: #fff; font-size: 12px; font-weight: 700; display: grid; place-items: center; background: linear-gradient(135deg,#ff7a59,#f43f5e); }
.vm-scope-ic { flex: 0 0 auto; color: #9aa0a6; display: inline-flex; }
.vm-scope-txt { flex: 1 1 0; min-width: 0; }
.vm-scope-k { display: block; font: 9px/1 var(--font-mono); letter-spacing: .12em; color: #6e7681; }
.vm-scope-v { display: block; font-size: 13px; font-weight: 600; color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 3px; }
.vm-scope-count { font: 11px/1 var(--font-mono); color: #6e7681; flex: 0 0 auto; }
.vm-scope-caret { flex: 0 0 auto; color: #6e7681; }
.vm-scope-proj { padding: 12px 14px 10px; }

/* VM-list header + list */
.vm-list-wrap { flex: 1 1 0; overflow: hidden; display: flex; flex-direction: column; border-top: 1px solid rgba(255,255,255,.06); margin-top: 6px; min-height: 0; }
.vm-list-head { display: flex; align-items: center; gap: 8px; padding: 11px 16px 8px; }
.vm-list-title { font: 9px/1 var(--font-mono); letter-spacing: .14em; color: #6e7681; white-space: nowrap; flex: 0 0 auto; }
/* DEMO badge — the VM list is the VM_DEMO fallback fleet (no real hypervisor). */
.vm-list-demo { font: 700 8px/1 var(--font-mono); letter-spacing: .12em; color: #e0b341; background: rgba(224,179,65,.14); border: 1px solid rgba(224,179,65,.4); border-radius: 4px; padding: 3px 5px; }
.vm-list-tools { display: flex; align-items: center; gap: 11px; margin-left: auto; }
/* Head = title · filter · "+ New" · ⋯ menu (Pair machine / Storage). The tools used to
   be four links in one 330px row: New was clipped and Enroll read "En…" (PO 29/09). */
.vm-list-add { padding: 4px 9px !important; border: 1px solid rgba(55,148,255,.45) !important; border-radius: 7px; }
.vm-list-add:hover { background: rgba(55,148,255,.1) !important; }
.vm-list-more { position: relative; display: inline-flex; flex: 0 0 auto; }
.vm-list-more-btn { background: none; border: 1px solid transparent; border-radius: 7px; color: #9aa2ad; cursor: pointer; font-size: 15px; line-height: 1; padding: 2px 7px; }
.vm-list-more-btn:hover, .vm-list-more-btn[aria-expanded="true"] { color: #e6e8ec; border-color: rgba(255,255,255,.14); }
.vm-list-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; min-width: 220px; display: flex; flex-direction: column; padding: 5px; background: #111318; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; box-shadow: 0 16px 36px rgba(0,0,0,.55); }
.vm-list-menu > button { text-align: left; background: none; border: none; color: #e6e8ec; font-size: 13px; padding: 8px 10px; border-radius: 6px; cursor: pointer; white-space: nowrap; }
.vm-list-menu > button:hover { background: rgba(255,255,255,.06); }
/* Explicit "SyrixVM daemon offline" surface (never a forever spinner). */
.vm-list-offline .vm-offline-ic { color: #6e7681; margin-bottom: 2px; }
.vm-list-offline > p:first-of-type { color: #e6e8ec; font-weight: 600; }
/* The magnifier + its input. It used to be the glyph alone — pointer cursor, no handler, no hook —
   so it read as a control and did nothing when clicked. Now it wraps a real filter input. */
.vm-list-search { color: #6e7681; display: inline-flex; align-items: center; gap: 4px; }
.vm-list-search:hover, .vm-list-search:focus-within { color: #e6e8ec; }
.vm-list-search-input {
  width: 72px; background: transparent; border: none; outline: none;
  color: inherit; font: inherit; font-size: 11px; padding: 2px 0;
  border-bottom: 1px solid transparent; transition: width .15s ease, border-color .15s ease;
}
.vm-list-search-input:focus { width: 116px; border-bottom-color: #3794ff; }
.vm-list-search-input::placeholder { color: #4d5560; }
/* "12 of 20" — the count is the observable proof the filter did something. */
.vm-list-filtered { color: #6e7681; font-weight: 400; }
.vm-list-new { display: flex; align-items: center; gap: 3px; font: 600 11px/1 var(--font-mono); color: var(--accent); cursor: pointer; background: none; border: none; padding: 0; }
.vm-list-new:hover { color: #5aa9ff; }
.vm-list { flex: 1 1 0; overflow-y: auto; padding: 0 8px 8px; display: flex; flex-direction: column; gap: 3px; }

/* one VM row (master) */
.vm-item { display: flex; align-items: center; gap: 10px; border-radius: 10px; padding: 9px 10px; cursor: pointer; border: 1px solid transparent; }
.vm-item:hover { background: rgba(255,255,255,.03); }
.vm-item.is-selected { background: #16181d; border-color: rgba(55,148,255,.35); }
.vm-item.is-stopped { opacity: .72; }
.vm-badge { width: 26px; height: 26px; flex: 0 0 auto; border-radius: 7px; background: #1b1d22; border: 1px solid rgba(255,255,255,.08); display: grid; place-items: center; font: 11px/1 var(--font-mono); }
.vm-badge.os-ubuntu { color: #ff7a59; } .vm-badge.os-windows { color: #5aa9ff; } .vm-badge.os-macos { color: #c4c8ce; } .vm-badge.os-android { color: #46d19e; } .vm-badge.os-ios { color: #c4c8ce; }
.vm-badge.is-stopped { background: #131519; border-color: rgba(255,255,255,.06); color: #6e7681; }
.vm-badge.is-error { background: #1b1418; border-color: rgba(255,107,107,.25); color: #ff6b6b; }
.vm-item-main { flex: 1 1 0; min-width: 0; }
.vm-item-top { display: flex; align-items: center; gap: 6px; }
.vm-item-name { font: 600 12.5px/1 var(--font-mono); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vm-item.is-dim .vm-item-name { color: #c4c8ce; }
.vm-item.is-stopped .vm-item-name { color: #8b909a; }
/* #234 — the id tag shown ONLY on rows whose name collides with another visible row. Deliberately
   quiet (muted chip, one size down) so it reads as a disambiguator, not as a second name. It sits on
   the SPEC line (see renderVmItem): putting it beside the name truncated the name itself, which made
   the tagged row harder to read than its untagged twin. inline-block + a trailing gap so it flows with
   the spec text; the spec line's own ellipsis still applies to the text AFTER it. */
.vm-item-idtag {
  display: inline-block; font: 500 9.5px/1.35 var(--font-mono); color: #a2a8b2;
  background: rgba(139,144,154,.16); border: 1px solid rgba(139,144,154,.26);
  border-radius: 5px; padding: 1px 4px; margin-right: 6px; letter-spacing: .02em;
  vertical-align: 1px;
}
.vm-item.is-selected .vm-item-idtag { color: #c4c8ce; border-color: rgba(196,200,206,.3); }
.vm-dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; }
.vm-dot.st-running { background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.15); }
.vm-item:not(.is-selected) .vm-dot.st-running { box-shadow: none; }
.vm-dot.st-booting { background: #e0b341; }
.vm-dot.st-stopped { background: #6e7681; }
/* H1.6 sleeping: a suspended VM holds its RAM image and resumes on use — a soft indigo reads as
   "asleep, will come back", distinct from the grey of a genuinely powered-off machine. */
.vm-dot.st-suspended { background: #7d8cf0; }
.vm-dot.st-error { background: #ff6b6b; }
.vm-item-specs { display: block; font-size: 10.5px; color: #8b909a; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vm-item-specs.st-booting { color: #e0b341; }
.vm-item-specs.st-error { color: #ff6b6b; }
.vm-item-specs.st-stopped, .vm-item.is-dim .vm-item-specs { color: #6e7681; }
.vm-item-specs.st-suspended { color: #7d8cf0; }
/* SURF-21/22 — who holds the machine, on the line under the specs. Quieter than the spec line so it
   reads as a second fact about the row rather than competing with the name; the unassigned case is
   the one a reader must NOT skim past, so it is the one that gets the accent. */
/* Icon + text, per the ACTIVE v2 design (Team VM.dc.html): `person` blue for an issued machine,
   `person_off` amber for one issued to nobody. Amber is the one a reader must not skim past. */
.vm-item-assign { display: flex; align-items: center; gap: 4px; font-size: 10px; color: #3794ff; margin-top: 2px; min-width: 0; }
.vm-item-assign svg { flex: 0 0 auto; }
.vm-assign-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vm-item-assign.is-unassigned { color: #e0b341; }
/* Assignment not yet readable (no AssetAssignment port for a SyrixVM machine). Muted and
   italic so it reads as an ABSENT answer, never as a state the machine is in. */
.vm-item-assign.is-unknown { color: #6e7681; font-style: italic; }
/* DEVICE-20 — the "no access · <reason>" row. The reason is the ONLY thing this row tells a viewer
   about a machine they may not open, so it must be readable in full: the generic spec line is
   nowrap+ellipsis, which clipped it to "No access · not …" beside the Request access button and
   left the reason legible only as a tooltip. Wrap instead of truncating, and let the row grow. */
.vm-item.is-no-access { align-items: flex-start; }
.vm-item.is-no-access .vm-item-specs.st-no-access {
  white-space: normal; overflow: visible; text-overflow: clip; line-height: 1.35; color: #8b909a;
}
/* #725 — `.vm-item-request` is gone with the per-row button it styled (vms.js:1418). Ten ungranted
   rows meant ten of them down the left panel; the one control now lives on the detail screen. */
.vm-ai { font: 8.5px/1 var(--font-mono); letter-spacing: .06em; color: var(--accent); background: var(--accent-wash); border-radius: 5px; padding: 3px 5px; flex: 0 0 auto; }
.vm-item-gear { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 6px; border: none; background: transparent; color: #6e7681; display: none; place-items: center; cursor: pointer; }
.vm-item:hover .vm-item-gear { display: grid; }
.vm-item-gear:hover { color: #e6e8ec; background: rgba(255,255,255,.06); }

/* empty / error / loading (master) */
.vm-list-msg { padding: 24px 12px; text-align: center; color: var(--muted); font-size: var(--t-sm); display: flex; flex-direction: column; align-items: center; gap: 8px; }
.vm-list-msg .btn { margin-top: 4px; }

/* ── CENTER column (selected VM live screen) ── */
.vm-center { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; background: #0e1014; min-height: 0; }
.vm-cbar { height: 52px; flex: 0 0 auto; border-bottom: 1px solid rgba(255,255,255,.07); display: flex; align-items: center; gap: 10px; padding: 0 14px 0 18px;
  /* R22 — CONTAIN THE TOOLBAR. Every child is `flex: 0 0 auto`, so when the centre column is narrow
     nothing shrinks and the row simply OVERFLOWS: measured at 1280px the bar ended at x=950 while its
     Resolution select ran to x=1045, painting 95px INTO the agents panel (which starts at x=988).
     min-width:0 lets the row participate in its parent's shrinking, and overflow:hidden makes any
     remaining excess clip inside the toolbar instead of over a neighbouring panel. */
  /* The row is structurally over-subscribed at a narrow centre column: MEASURED at 1280px the children
     sum to 693px + 130px of gaps + 32px padding = 855px inside a 602px bar. So clipping is not enough —
     it would AMPUTATE controls (kill / snapshot / resolution) with no way to reach them. Scrolling the
     toolbar keeps every control reachable while still containing the row inside its own column. */
  min-width: 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
.vm-cbar::-webkit-scrollbar { height: 0; }
.vm-cbar-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.vm-cbar-dot.st-running { background: #46d19e; box-shadow: 0 0 0 3px rgba(70,209,158,.15); }
.vm-cbar-dot.st-booting { background: #e0b341; }
.vm-cbar-dot.st-stopped { background: #6e7681; }
.vm-cbar-dot.st-suspended { background: #7d8cf0; }
.vm-cbar-dot.st-error { background: #ff6b6b; }
.vm-cbar-name { font: 600 14px/1 var(--font-mono); color: #e6e8ec; white-space: nowrap;
  /* R22 — the NAME is the one item that may give up room: it is re-readable from the sidebar
     selection, while the controls beside it are not reachable any other way. Shrinking it first means
     the toolbar's overflow:hidden clips slack rather than amputating Take control / Resolution. */
  /* min-width is a FLOOR, not 0: with `min-width:0` the name shrank to width 0 and vanished entirely
     (measured), which is a worse bug than the overflow — the header no longer said which machine you
     were looking at. 8ch keeps a readable stub that ellipsises. */
  /* min-width is a FLOOR, not 0: with `min-width:0` the name shrank to width 0 and vanished entirely
     (measured) — worse than the overflow, since the header no longer said which machine you were on. */
  flex: 0 1 auto; min-width: 5ch; overflow: hidden; text-overflow: ellipsis; }
.vm-state-pill { font: 10px/1 var(--font-mono); letter-spacing: .04em; border-radius: 5px; padding: 3px 7px; }
.vm-state-pill.st-running { color: #46d19e; background: rgba(70,209,158,.1); }
.vm-state-pill.st-booting { color: #e0b341; background: rgba(224,179,65,.1); }
.vm-state-pill.st-provisioning { color: #58a6ff; background: rgba(88,166,255,.12); }
.vm-state-pill.st-provisioning::before { content: "⟳ "; display: inline-block; animation: vm-spin 1.1s linear infinite; }
@keyframes vm-spin { to { transform: rotate(360deg); } }
.vm-dot.st-provisioning, .vm-cbar-dot.st-provisioning { background: #58a6ff; }
.vm-item-specs.st-provisioning { color: #58a6ff; }
.vm-state-pill.st-stopped { color: #8b909a; background: rgba(255,255,255,.05); }
.vm-state-pill.st-error { color: #ff6b6b; background: rgba(255,107,107,.1); }
/* #222/#231 — NO SIGNAL: the hypervisor says running but the guest agent is unreachable. Amber, NOT
   green: the whole bug was a green RUNNING chip sitting beside "host agent unreachable", and green
   is what users read as "fine". Distinct from st-error (the VM itself failed) — here the VM process
   is alive and only the path into it is broken. */
.vm-state-pill.st-unreachable { color: #e0a341; background: rgba(224,163,65,.12); }
.vm-cbar-dot.st-unreachable, .vm-dot.st-unreachable { background: #e0a341; box-shadow: 0 0 0 3px rgba(224,163,65,.15); }
/* #232 — a control that cannot reach the guest must LOOK inert, not merely be inert: a bright,
   hover-responsive Take-control that silently does nothing is the dead button this bug is about. */
.vm-cbar-take[disabled], .vm-cbar-res[disabled], .vm-cbar-btn[disabled] { opacity: .4; cursor: not-allowed; pointer-events: auto; }
.vm-cbar-take[disabled]:hover, .vm-cbar-btn[disabled]:hover { background: transparent; }
.vm-ai-pill { display: flex; align-items: center; gap: 5px; font: 10px/1 var(--font-mono); letter-spacing: .04em; color: var(--accent); background: rgba(55,148,255,.1); border-radius: 5px; padding: 3px 7px; }
.vm-cbar-spacer { flex: 1 1 0; }
.vm-metrics { display: flex; align-items: center; gap: 8px; font: 10.5px/1 var(--font-mono); color: #6e7681; padding: 4px 10px; border: 1px solid rgba(255,255,255,.07); border-radius: 7px; }
.vm-metrics .sep { color: #3a3f49; }
.vm-metrics .ms { color: #46d19e; }
.vm-cbar-btn { width: 30px; height: 30px; border-radius: 8px; border: 1px solid rgba(255,255,255,.1); background: #16181d; color: #9aa0a6; display: grid; place-items: center; cursor: pointer; }
.vm-cbar-btn:hover { color: #e6e8ec; border-color: rgba(255,255,255,.2); }

/* Resolution dropdown in the toolbar (left of Stop). A styled native <select>. */
.vm-cbar-res { height: 30px; border-radius: 8px; border: 1px solid rgba(255,255,255,.1); background: #16181d; color: #cdd2d8; font: 500 12px/1 var(--font-mono,monospace); padding: 0 8px; cursor: pointer; max-width: 150px; }
.vm-cbar-res:hover { color: #e6e8ec; border-color: rgba(255,255,255,.2); }
.vm-cbar-res option { background: #16181d; color: #cdd2d8; }
.vm-cbar-start:hover { color: #46d19e; border-color: rgba(70,209,158,.4); }
.vm-cbar-stop:hover { color: #ff6b6b; border-color: rgba(255,107,107,.4); }
/* Force stop (kill) is the destructive twin of stop — amber at rest so it reads as "danger,
   deliberate", deeper red on hover than the graceful stop. */
.vm-cbar-kill { color: #d9a441; }
.vm-cbar-kill:hover { color: #ff4d4d; border-color: rgba(255,77,77,.55); background: rgba(255,77,77,.08); }
/* ── INPUT-LOCK spine ON THE TOOLBAR (not floating over the live surface) ──────────────────────
   noVNC writes inline width/height on its <canvas>, so the canvas overflows its holder and covers
   any absolutely-positioned overlay inside .vm-screen — the take-control button was rendered but
   unclickable, and looked "gone" the moment the picture came up. On the toolbar it stays visible
   and hittable whether or not a stream is connected. */
.vm-cbar-lock, .vm-cbar-live { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; font: 11px/1 var(--font-mono); padding: 5px 9px; border-radius: 7px; white-space: nowrap; }
.vm-cbar-lock { color: #9aa0a6; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.08); }
.vm-cbar-lock svg { flex: 0 0 auto; }
.vm-cbar-live { color: #e0b341; background: rgba(224,179,65,.1); border: 1px solid rgba(224,179,65,.35); font-weight: 600; }
.vm-cbar-live .dot { width: 6px; height: 6px; border-radius: 50%; background: #e0b341; flex: 0 0 auto; }
.vm-cbar-take, .vm-cbar-release { flex: 0 0 auto; height: 30px; font-family: inherit; font-size: 11px; font-weight: 600; border: none; border-radius: 8px; padding: 0 12px; cursor: pointer; color: #0b0c0f; white-space: nowrap; }
.vm-cbar-take { background: #ff7a59; }
.vm-cbar-take:hover { background: #ff8a6d; }
.vm-cbar-take:disabled { opacity: .5; cursor: not-allowed; }
.vm-cbar-release { background: #e0b341; }
.vm-cbar-release:hover { background: #ecc255; }
.vm-stage-start { margin-top: 12px; }

/* live screen stage */
.vm-stage { flex: 1 1 0; min-height: 0; position: relative; display: flex; background: #0a0b0d; padding: 12px; }

/* Fullscreen VM view (native Fullscreen API on .vm-center, data-vm-fs set by toggleVmFullscreen).
   The browser already sizes .vm-center to the physical display; we drop the stage padding + screen
   chrome so the guest desktop fills edge-to-edge (black letterbox around the aspect-fit canvas), and
   keep the control-bar visible at the top so Take-control / Stop / Exit stay reachable. */
.vm-center[data-vm-fs] { background: #000; }
.vm-center[data-vm-fs] .vm-stage { padding: 0; background: #000; }
.vm-center[data-vm-fs] .vm-screen { border: none; border-radius: 0; background: #000; }
/* :fullscreen is the element actually promoted — belt-and-suspenders in case data-vm-fs is missed. */
.vm-center:fullscreen { background: #000; }

/* Android on-screen nav bar under the live screen (Back / Home / Recents). */
/* Android hardware nav (Back / Home / Recents) inline in the toolbar, left of Release control. Its
   buttons reuse .vm-cbar-btn; the group just adds a small divider so the triad reads apart from the
   control spine. Recents is a square, Home a circle, Back a chevron — the Android system-bar shapes. */
.vm-cbar-nav { display: inline-flex; align-items: center; gap: 2px; margin: 0 4px; padding-right: 6px; border-right: 1px solid rgba(255, 255, 255, 0.1); }
.vm-center:fullscreen .vm-stage { padding: 0; background: #000; }
.vm-center:fullscreen .vm-screen { border: none; border-radius: 0; }
.vm-screen { width: 100%; border-radius: 10px; overflow: hidden; border: 1px solid rgba(255,255,255,.08); background: linear-gradient(135deg,#1c2530,#101820); position: relative; }
.vm-screen.is-locked { cursor: not-allowed; }
.vm-screen.is-live { cursor: crosshair; }
/* BUG#C: the polled-screenshot frame. It sits ABOVE the decorative gradient backdrop (z-index 0) and
   BELOW the live RFB canvas (z-index 1), so when a real stream is up the canvas wins, and when it is
   not the decoded screenshot is what the user sees. It deliberately does NOT carry .vm-screen-fallback:
   that class is the backdrop's, and the display:none rule below (correct for the backdrop) was hiding
   this fully decoded 1280x800 frame, leaving an unexplained empty panel. */
.vm-screen-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; z-index: 0; }
.vm-screen-img[hidden] { display: none; }
.vm-screen-fallback { position: absolute; inset: 0; background: radial-gradient(120% 100% at 20% 0%, #25384a 0%, #12202c 55%, #0c1620 100%); z-index: 0; }
/* The live RFB canvas sits ABOVE the gradient/screenshot fallback and is only revealed once the
   stream is up — so a connected canvas is never hidden behind the placeholder gradient. */
.vm-vnc-canvas { z-index: 1; }
.vm-screen[data-vnc-state="connected"] .vm-screen-fallback,
.vm-screen[data-vnc-state="connecting"] .vm-screen-fallback { display: none; }
/* ── LIVE noVNC canvas (T04) ─────────────────────────────────────────────────────────────────── */
/* The RFB client mounts a <canvas> inside .vm-vnc-canvas. It fills the surface; noVNC letterboxes the
   framebuffer (scaleViewport) so the guest aspect ratio is preserved on a black field. */
/* noVNC's scaleViewport sizes the <canvas> to the target element's box, so the target must be a
   sized block (NOT a flex container — noVNC centres the canvas itself). A flex parent made the
   canvas compute to 0×0, hiding a connected stream behind the placeholder. */
.vm-vnc-canvas { position: absolute; inset: 0; width: 100%; height: 100%; background: #000; overflow: hidden; }

/* FrameMount (Android gRPC-RGB) paints an RGB888 <canvas>. Its intrinsic size is the guest frame
   (320x640 portrait, 640x320 landscape); fit it into the surface with contain so a rotation fills the
   box that _paint reshaped (surface.style.aspectRatio) instead of overflowing or letterboxing. */
.vm-frame-canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
/* The <video> live screen (VideoMount / H.264) — same contain fit so a landscape stream fills its box. */
.vm-screen video, video.vm-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
/* noVNC owns the sizing now (rfb.scaleViewport=true): its ResizeObserver autoscales the <canvas> to
   fit this holder and writes the fitted size as an INLINE style (e.g. 816×612), centring it on a
   black field via its own flex `_screen` wrapper. We must NOT override that with width:100% !important
   + object-fit — doing so stretched the canvas back to the full holder box and reintroduced the
   object-fit letterbox that noVNC's pointer math is blind to (mouse mapped to the wrong region and
   "flew off" before the real VM edge — BUG #47/3a). Leave the canvas at noVNC's inline size; only make
   it fill its own box cleanly. cursor:crosshair keeps a VISIBLE pointer over the live canvas even when
   the guest hides its own (paired with rfb.showDotCursor — BUG #47/2). */
.vm-vnc-canvas canvas { display: block; max-width: 100%; max-height: 100%; }
.vm-screen.is-live .vm-vnc-canvas, .vm-screen.is-live .vm-vnc-canvas canvas,
.vm-ed-screen.is-live .vm-vnc-canvas, .vm-ed-screen.is-live .vm-vnc-canvas canvas { cursor: crosshair; }
.vm-screen[data-vnc-state="connecting"] .vm-vnc-canvas,
.vm-ed-screen[data-vnc-state="connecting"] .vm-vnc-canvas { opacity: 0; }
.vm-screen[data-vnc-state="connected"] .vm-vnc-canvas,
.vm-ed-screen[data-vnc-state="connected"] .vm-vnc-canvas { opacity: 1; transition: opacity .18s ease; }
/* connecting overlay — a real spinner + label while the RFB socket handshakes. */
.vm-screen-connecting { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: #b9c2cc; font-size: 13px; background: radial-gradient(120% 100% at 50% 30%, #1a2836 0%, #0d1620 70%); z-index: 2; }
/* #177 — "disconnected" joins connected/fallback here. Once the stream has given up, the surface
   carries the real diagnosis chip ("No picture — <reason>"); leaving the connecting overlay up would
   both lie ("Connecting…" when nothing is being tried) and cover the chip that explains why. */
.vm-screen[data-vnc-state="connected"] .vm-screen-connecting,
.vm-screen[data-vnc-state="disconnected"] .vm-screen-connecting,
.vm-screen[data-vnc-state="fallback"] .vm-screen-connecting { display: none; }
.vm-vnc-spin { width: 26px; height: 26px; border-radius: 50%; border: 3px solid rgba(255,255,255,.14); border-top-color: #5aa9ff; animation: vm-vnc-spin 0.8s linear infinite; }
@keyframes vm-vnc-spin { to { transform: rotate(360deg); } }
/* the cockpit-tab live canvas variant */
.vm-ed-vnc { border-radius: 8px; overflow: hidden; }
.vm-ed-screen[data-vnc-state="fallback"] .vm-ed-vnc { display: none; }
/* TA #64 — typed reason the live screen is not showing + Retry (cockpit VM tab). */
.vm-ed-err { position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 3; display: inline-flex; align-items: center; gap: 10px; max-width: calc(100% - 32px);
  padding: 6px 8px 6px 12px; border-radius: 8px; background: rgba(0,0,0,.72); border: 1px solid rgba(255,255,255,.18); color: #fff; font-size: var(--t-sm); }
.vm-ed-err[data-code="vm_restarting"] { border-color: var(--warn, #e0b341); }
.vm-ed-err-t { overflow: hidden; text-overflow: ellipsis; }
.vm-ed-retry { flex: none; }
.vm-ed-off.is-crashed .vm-ed-off-sub { color: var(--warn, #e0b341) !important; }
.vm-term-bar { position: absolute; top: 0; left: 0; right: 0; height: 22px; background: rgba(0,0,0,.35); display: flex; align-items: center; gap: 6px; padding: 0 8px; }
.vm-term-bar .d { width: 7px; height: 7px; border-radius: 50%; }
.vm-term-bar .d.r { background: #ff6b6b; } .vm-term-bar .d.y { background: #e0b341; } .vm-term-bar .d.g { background: #46d19e; }
.vm-term-bar .t { font: 8px/1 var(--font-mono); color: rgba(255,255,255,.4); margin-left: 8px; }
.vm-term-body { position: absolute; inset: 34px 16px 16px; background: rgba(0,0,0,.4); border-radius: 6px; padding: 14px; font: 11px/1.8 var(--font-mono); color: #7fd18f; overflow: hidden; }
.vm-term-body .dim { color: #8b909a; } .vm-term-body .warn { color: #e0b341; } .vm-term-body .info { color: #5aa9ff; }
.vm-term-caret { display: inline-block; width: 7px; height: 13px; background: #7fd18f; vertical-align: middle; }
.vm-ai-cursor { position: absolute; left: 58%; top: 64%; display: flex; align-items: center; gap: 5px; pointer-events: none; }
.vm-ai-cursor .lbl { font: 8px/1 var(--font-mono); color: #fff; background: var(--accent); border-radius: 4px; padding: 2px 5px; }
/* .vm-lock-chip is the detail view's "View only" label pill (still used); the floating .vm-lockbar /
   .vm-livebar overlays are gone — the 07 menu now carries take/release on its toolbar, where the
   noVNC canvas cannot cover them. */
.vm-lock-chip { display: flex; align-items: center; gap: 9px; background: rgba(10,11,13,.8); backdrop-filter: blur(6px); border: 1px solid rgba(255,255,255,.12); border-radius: 999px; padding: 8px 15px; pointer-events: auto; }
.vm-lock-chip svg { color: #9aa0a6; flex: 0 0 auto; }
.vm-lock-chip .txt { font-size: 12px; color: #c4c8ce; }
.vm-release { font-size: 11px; font-weight: 600; color: #0b0c0f; background: #e0b341; border: none; border-radius: 999px; padding: 5px 12px; cursor: pointer; font-family: inherit; }
/* #176 — same lift as .vm-screen-reconnect below: the menu-detail badge shares the surface with the
   picture layer, so without a z-index it can be painted over by the very black frame it explains. */
.vm-reconnect { position: absolute; top: 30px; right: 22px; z-index: 3; font: 700 var(--t-sm)/1 var(--font-mono); color: #e0b341; background: rgba(0,0,0,.7); padding: 4px 10px; border-radius: 8px; }
.vm-stage-off { margin: auto; text-align: center; color: var(--muted); }
.vm-stage-off-ic { font-size: 40px; color: var(--faint); }
.vm-stage-off p { margin: 6px 0 0; font-size: var(--t-md); font-weight: 600; color: #c4c8ce; }
.vm-stage-off-sub { font-size: var(--t-sm) !important; font-weight: 400 !important; color: var(--muted) !important; }
/* JV-6: the CAUSE line under a failed VM list. Muted and monospace because it repeats the server's
   own words (a code, a reason, an origin) — a reader is meant to copy it, not read it as prose. */
/* JV-7: a sleeping teammate is SHOWN, dimmed, with the state in words. Hiding them made the column
   say "No agents online" while two of four were merely asleep (WEBUI-014). */
.vm-chip.is-asleep { opacity: .62; }
.vm-chip-state { font-size: var(--t-xs); color: var(--muted); margin-left: .3rem; text-transform: lowercase; }
.vm-list-msg-cause { font-size: var(--t-xs); color: var(--muted); font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); margin: .35rem 0 .55rem; word-break: break-word; }
.vm-center-empty { margin: auto; text-align: center; color: var(--muted); font-size: var(--t-sm); }
/* #715 — the no-access empty state carries an ACTION, so it stops being a bare line of text.
   The empty state becomes a COLUMN rather than styling the button into place: `.btn` already sets
   `display:inline-flex` at the same specificity and earlier in the file, so a `display:block` here
   loses and the button lands inline beside the sentence, crowding it (measured in the first DONE
   capture). Laying the parent out as a centred column puts the control under its explanation
   without touching `.btn` or adding a wrapper element. */
.vm-center-empty:has(.vm-center-request) { display: flex; flex-direction: column; align-items: center; gap: .85rem; }
/* The button must not inherit the MUTED colour of the sentence explaining it: a dimmed control
   reads as disabled, which is the opposite of the point. */
.vm-center-request { color: var(--text, #e6e8ec); background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.14); }
.vm-center-request:hover { background: rgba(255,255,255,.14); }
/* #725 — …but while the door is UNBUILT the button is `disabled`, and the rule above would paint a
   disabled control in full brightness with a hover response — a control that looks pressable and
   is not. Both are overridden here, and `cursor: not-allowed` makes the state legible before the
   click rather than after it. The sentence underneath carries the reason in words; this only makes
   sure the pixels do not contradict it. */
.vm-center-request[disabled] {
  color: var(--muted, #8b909a); background: rgba(255,255,255,.03);
  border-color: rgba(255,255,255,.08); cursor: not-allowed; opacity: .75;
}
.vm-center-request[disabled]:hover { background: rgba(255,255,255,.03); }
/* The "not connected yet" sentence sits UNDER its button and must not be mistaken for the machine's
   own explanation above it, so it is narrower and smaller than the centre text. */
.vm-center-unbuilt {
  margin: 0; max-width: 34rem; font-size: var(--t-xs); line-height: 1.45; color: var(--muted, #8b909a);
}

/* ── RIGHT column (AGENTS · chat) ── */
.vm-agents { width: 330px; flex: 0 0 auto; border-left: 1px solid rgba(255,255,255,.07); display: flex; flex-direction: column; background: #0b0c0f; min-height: 0; }
/* Hidden: the panel collapses to a slim restore rail; .vm-center (flex 1) reclaims the width. */
.vm-agents-tool[data-vm-agents-hide] { cursor: pointer; display: grid; place-items: center; color: #6e7681; background: none; border: none; padding: 0; flex: 0 0 auto; }
.vm-agents-tool[data-vm-agents-hide]:hover { color: #e6e8ec; }
.vm-agents-head { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid rgba(255,255,255,.06); }
.vm-agents-head > svg { color: var(--accent); flex: 0 0 auto; }
.vm-agents-title { flex: 1 1 0; font: 11px/1 var(--font-mono); letter-spacing: .12em; color: #e6e8ec; }
.vm-agents-tool { cursor: pointer; display: grid; place-items: center; color: #6e7681; background: none; border: none; padding: 0; }
.vm-agents-tool:hover { color: #e6e8ec; }
.vm-chips { display: flex; align-items: center; gap: 8px; padding: 11px 10px 11px 12px; border-bottom: 1px solid rgba(255,255,255,.06); }
/* JV-7: the chips WRAP instead of scrolling sideways. Measured on the running screen: the row was
   535px of content in a 271px box, so the two SLEEPING agents sat off-screen behind a horizontal
   scrollbar — which defeats the point of showing them at all (WEBUI-014 asks for the sleeping ones
   to be visible, and a teammate you must scroll to find reads the same as one who is not there). */
.vm-chips-scroll { flex: 1 1 0; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 7px; overflow-x: visible; padding-bottom: 2px; }
.vm-chip { display: flex; align-items: center; gap: 7px; flex: 0 0 auto; background: transparent; border: 1px solid rgba(255,255,255,.08); border-radius: 999px; padding: 5px 10px 5px 6px; cursor: pointer; }
.vm-chip.is-active { background: #16181d; border-color: rgba(55,148,255,.4); }
.vm-chip-av { position: relative; width: 22px; height: 22px; flex: 0 0 auto; }
.vm-chip-av .a { width: 22px; height: 22px; border-radius: 50%; color: #fff; font-size: 10px; font-weight: 700; display: grid; place-items: center; }
.vm-chip-av .s { position: absolute; right: -1px; bottom: -1px; width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid #0b0c0f; }
.vm-chip-name { font-size: 12px; color: #c4c8ce; }
.vm-chip.is-active .vm-chip-name { font-weight: 600; color: #e6e8ec; }
.vm-chip-add { width: 28px; height: 28px; flex: 0 0 auto; border-radius: 50%; border: 1px dashed rgba(255,255,255,.18); background: #0b0c0f; color: #6e7681; display: grid; place-items: center; cursor: pointer; }
.vm-chip-add:hover { color: #e6e8ec; border-color: rgba(255,255,255,.3); }
.vm-chips-empty { font: 11px/1 var(--font-mono); color: #6e7681; padding: 4px 2px; }

/* AGENTS column body — the per-VM chat has no backend of its own; real agent chat lives in the
   cockpit, so this is an honest CTA that routes there (D.revealAgentChat / D.spawnAgent). */
.vm-agents-cta { flex: 1 1 0; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 8px; padding: 24px 22px; }
.vm-agents-cta > svg { color: var(--accent); opacity: .85; margin-bottom: 2px; }
.vm-agents-cta.is-empty > svg { color: #6e7681; }
.vm-cta-title { font-size: 13.5px; font-weight: 600; color: #e6e8ec; }
.vm-cta-sub { font-size: 12px; line-height: 1.55; color: #8b909a; max-width: 240px; }
.vm-cta-sub b { color: #c4c8ce; font-weight: 600; }
.vm-cta-btn { margin-top: 6px; padding: 8px 16px; border-radius: 9px; border: 1px solid rgba(55,148,255,.4); background: rgba(55,148,255,.14); color: var(--accent); font-size: 12.5px; font-weight: 600; cursor: pointer; }
.vm-cta-btn:hover { background: rgba(55,148,255,.22); border-color: rgba(55,148,255,.6); }
.vm-agents-cta.is-empty .vm-cta-btn { border-color: rgba(255,255,255,.16); background: transparent; color: #c4c8ce; }
.vm-agents-cta.is-empty .vm-cta-btn:hover { border-color: rgba(255,255,255,.3); color: #e6e8ec; }

/* ── LEGACY full-screen DETAIL view classes (kept for the sub='detail' VNC view built in parallel;
      the 07 master-detail above reuses .vm-screen for its center). Restored so the detail render is
      not left unstyled. .vm-screen / .vm-screen-img / .vm-lock-chip / .vm-take / .vm-release are the
      SHARED classes defined once above. ─────────────────────────────────────────────────────────── */
.vm-host-info { font: var(--t-sm)/1 var(--font-mono); color: var(--muted); white-space: nowrap; }
.vm-vault { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-sm); font-weight: 600; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border); }
.vm-vault.is-open { color: var(--green); border-color: rgba(70,209,158,.35); background: rgba(70,209,158,.1); }
.vm-vault.is-locked { color: var(--muted); }
.vm-mcp-ready { font-size: var(--t-sm); font-weight: 600; color: var(--green); display: inline-flex; align-items: center; gap: 5px; }
.vm-mcp-ready::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px var(--state-running-glow); }
.vm-os-ic { display: inline-flex; color: var(--fg-2); }
.vm-os-ubuntu { color: #e95420; } .vm-os-windows { color: #4daafc; } .vm-os-macos { color: var(--fg); } .vm-os-android { color: #46d19e; } .vm-os-ios { color: #a0a4ac; }
.vm-name { font: 600 var(--t-base)/1 var(--font-mono); color: var(--fg-strong); }
.vm-pill { font-size: var(--t-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; border: 1px solid transparent; }
.vm-pill-running { color: var(--state-running); background: rgba(70,209,158,.12); border-color: rgba(70,209,158,.3); }
.vm-pill-booting { color: var(--state-thinking); background: rgba(224,179,65,.12); border-color: rgba(224,179,65,.3); }
.vm-pill-stopped { color: var(--disabled); background: rgba(255,255,255,.05); border-color: var(--border); }
.vm-pill-error { color: var(--red); background: rgba(255,107,107,.12); border-color: rgba(255,107,107,.3); }
.vm-ai-badge { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); font-weight: 700; color: var(--accent); background: var(--accent-wash); border: 1px solid rgba(55,148,255,.35); padding: 2px 8px; border-radius: 999px; }
.vm-you-badge { font-size: var(--t-xs); font-weight: 700; color: var(--yellow); background: rgba(224,179,65,.12); border: 1px solid rgba(224,179,65,.3); padding: 2px 8px; border-radius: 999px; }
.vm-empty, .vm-error { margin: 8px 24px; text-align: center; padding: 40px 24px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.vm-empty-ic { color: var(--faint); }
.vm-empty-title, .vm-error-title { font-size: var(--t-md); font-weight: 600; color: var(--fg-strong); }
.vm-empty-sub { font-size: var(--t-sm); color: var(--muted); }
.vm-empty .btn, .vm-error .btn { margin-top: 8px; }
.vm-detail { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.vm-detail-bar { display: flex; align-items: center; gap: var(--s-2); padding: 12px 24px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.vm-detail-res { font: var(--t-sm)/1 var(--font-mono); color: var(--muted); }
.vm-detail-spacer { flex: 1 1 auto; }
.vm-detail-loading { font-size: var(--t-sm); color: var(--muted); }
.vm-ctrl-badge { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-sm); font-weight: 700; padding: 3px 10px; border-radius: 999px; }
.vm-ctrl-view { color: var(--muted); border: 1px solid var(--border); }
.vm-ctrl-you { color: var(--yellow); background: rgba(224,179,65,.14); border: 1px solid rgba(224,179,65,.4); }
.vm-detail-body { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: 1fr 300px; gap: 0; }
.vm-screen-col { min-width: 0; min-height: 0; display: flex; padding: 20px; overflow: auto; background: #06070a; }
.vm-screen-wrap { flex: 1; display: grid; place-items: center; }
.vm-screen-skel { width: 60%; height: 60%; }
.vm-screen-lock { position: absolute; inset: 0; display: flex; align-items: flex-start; justify-content: center; pointer-events: none; background: linear-gradient(180deg, rgba(0,0,0,.35), rgba(0,0,0,0) 30%); }
/* #176 — the diagnosis badge must sit ABOVE the picture surface, or it is invisible exactly when
   it matters. Measured 04-08 on LUATNGUYEN9FA2: the badge read "No picture — hostagent(remote
   10.211.55.9:7681): dial tcp: i/o timeout" with hidden=false, display:block, visibility:visible,
   opacity:1, rect 361,143 714x32 — yet elementFromPoint at its own centre returned VIDEO.vm-video,
   and the screenshot showed a black frame with NO text. The <video> lives in a different parent
   that paints later, and both were z-index:auto, so the black surface won on DOM order alone.
   The user saw a silent black rectangle over a machine badged RUNNING. */
.vm-screen-reconnect { position: absolute; top: 12px; right: 12px; z-index: 3; font-size: var(--t-sm); font-weight: 700; color: var(--yellow); background: rgba(0,0,0,.7); padding: 4px 10px; border-radius: 8px; }
.vm-screen-off { margin: auto; text-align: center; color: var(--muted); }
.vm-screen-off-ic { font-size: 40px; color: var(--faint); }
.vm-screen-off p { margin: 6px 0 0; font-size: var(--t-md); font-weight: 600; color: var(--fg-2); }
.vm-screen-off-sub { font-size: var(--t-sm) !important; font-weight: 400 !important; color: var(--muted) !important; }
.vm-side { border-left: 1px solid var(--border); overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 20px; background: var(--bg-elev); }
.vm-side-block { display: flex; flex-direction: column; gap: 8px; }
.vm-side-title { font: 700 var(--t-label)/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--faint); display: flex; align-items: center; justify-content: space-between; }
.vm-side-add { width: 20px; height: 20px; border-radius: 6px; border: 1px solid var(--border); background: transparent; color: var(--muted); cursor: pointer; line-height: 1; }
.vm-side-add:hover { color: var(--fg-strong); border-color: var(--accent); }
.vm-activity, .vm-snaps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.vm-act-item { display: flex; gap: 8px; font-size: var(--t-sm); align-items: baseline; }
.vm-act-time { flex: none; font: var(--t-xs)/1.4 var(--font-mono); color: var(--faint); width: 68px; }
.vm-act-text { color: var(--fg-2); }
.vm-act-ai .vm-act-text { color: var(--accent); }
.vm-act-user .vm-act-text { color: var(--yellow); font-weight: 600; }
.vm-act-empty { font-size: var(--t-sm); color: var(--faint); }
.vm-snap { display: flex; align-items: center; gap: 8px; font-size: var(--t-sm); }
.vm-snap-name { font: 600 var(--t-sm)/1 var(--font-mono); color: var(--fg-2); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.vm-snap-time { font: var(--t-xs)/1 var(--font-mono); color: var(--faint); flex: none; }
.vm-snap-restore { flex: none; font-size: var(--t-xs); font-weight: 600; color: var(--muted); background: transparent; border: 1px solid var(--border); border-radius: 6px; padding: 2px 7px; cursor: pointer; }
.vm-snap-restore:hover { color: var(--fg-strong); border-color: var(--accent); }
.vm-snap-actions { flex: none; display: flex; gap: 6px; }
.vm-snap-newvm { flex: none; font-size: var(--t-xs); font-weight: 600; color: var(--accent); background: transparent; border: 1px solid var(--border); border-radius: 6px; padding: 2px 7px; cursor: pointer; }
.vm-snap-newvm:hover { color: var(--fg-strong); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* snapshot clone-mode chooser modal (linked vs full disk) */
.vm-snapmode { gap: 10px; }
.vm-snapmode-opt { display: flex; flex-direction: column; gap: 3px; text-align: left; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; background: transparent; cursor: pointer; transition: border-color .12s, background .12s; }
.vm-snapmode-opt:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.vm-snapmode-name { font-size: var(--t-sm); font-weight: 700; color: var(--fg-strong); }
.vm-snapmode-desc { font-size: var(--t-xs); color: var(--faint); line-height: 1.45; }
/* settings → Snapshots pane */
.vmset-h-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.vmset-snaps { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.vmset-snap { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; }
.vmset-snap-name { font: 600 var(--t-sm)/1 var(--font-mono); color: var(--fg-2); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.vmset-snap-time { font: var(--t-xs)/1 var(--font-mono); color: var(--faint); flex: none; min-width: 60px; text-align: right; }
.vmset-snap-actions { flex: none; display: flex; gap: 6px; }
.vmset-snap-empty { font-size: var(--t-sm); color: var(--faint); line-height: 1.5; padding: 4px 0; }
/* +New "From snapshot" source picker */
.vm-os-pick-snap .vm-os-snap { color: var(--accent); display: inline-flex; }
.vm-snap-picker { gap: 10px; }
.vm-snap-srclist { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
.vm-snap-src, .vm-snap-choice { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 9px; background: transparent; cursor: pointer; text-align: left; transition: border-color .12s, background .12s; }
.vm-snap-src:hover, .vm-snap-choice:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.vm-snap-src-ic { flex: none; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; }
.vm-snap-src-name { font-weight: 600; color: var(--fg-strong); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.vm-snap-src-os { font-size: var(--t-xs); color: var(--faint); flex: none; }
.vm-snap-choice-name { font: 600 var(--t-sm)/1 var(--font-mono); color: var(--fg-2); flex: 1 1 auto; }
.vm-snap-choice-time { font: var(--t-xs)/1 var(--font-mono); color: var(--faint); flex: none; }
.vm-hw { margin: 0; display: grid; grid-template-columns: 68px 1fr; gap: 4px 8px; font-size: var(--t-sm); }
.vm-hw dt { color: var(--faint); }
.vm-hw dd { margin: 0; color: var(--fg-2); }
.vm-mono { font-family: var(--font-mono); }

/* ════════════════════════ SETTINGS sub-view ("CHẾ ĐỘ SETTINGS — cấu hình VM") ════════════════════
   The center pane when a VM's gear ⚙ is chosen. SAME shell as 07 (.vm-master left column + .vm-center),
   with the center becoming a config surface: 56px header (⚙ name · SETTINGS · [amber dirty] + Huỷ + Lưu)
   over a 186px vertical tab rail + the active tab's body. Every literal copied from the design (dc tpl
   4504+): header 56px, tab rail 186px, pane pad 22px 26px gap 24px, radius 8/9px, accent #3794ff,
   amber #e0b341, green vault #46d19e, danger #ff6b6b, card #16181d, hairline rgba(255,255,255,.07/.1). */

/* the selected VM's gear is always visible in settings (design shows it on the active row) */
.vms-view .vm-item.is-selected .vm-item-gear { display: grid; color: var(--accent); }

.vmset-center { background: #0e1014; }

/* ── header (56px): ⚙ name · SETTINGS · dirty + Huỷ + Lưu thay đổi ── */
.vmset-hd { height: 56px; flex: 0 0 auto; border-bottom: 1px solid rgba(255,255,255,.07); display: flex; align-items: center; gap: 12px; padding: 0 20px; }
.vmset-hd-gear { flex: 0 0 auto; }
.vmset-hd-name { font: 600 14px/1 var(--font-mono); color: #e6e8ec; white-space: nowrap; flex: 0 0 auto; }
.vmset-hd-tag { font: 11px/1 var(--font-mono); color: #c4c8ce; background: rgba(255,255,255,.06); border-radius: 5px; padding: 3px 7px; }
.vmset-hd-dirty { font: 11px/1 var(--font-mono); color: #e0b341; background: rgba(224,179,65,.1); border-radius: 5px; padding: 3px 7px; }
.vmset-hd-sp { flex: 1 1 0; }
.vmset-hd-cancel { height: 32px; padding: 0 14px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: transparent; color: #c4c8ce; font: 600 12.5px/1 var(--font-sans); cursor: pointer; }
.vmset-hd-cancel:hover { color: #e6e8ec; border-color: rgba(255,255,255,.24); }
.vmset-hd-save { height: 32px; padding: 0 16px; border-radius: 8px; border: none; background: var(--accent); color: #0b0c0f; font: 700 12.5px/1 var(--font-sans); cursor: pointer; }
.vmset-hd-save:hover:not(:disabled) { background: #4a9dff; }
.vmset-hd-save:disabled { opacity: .45; cursor: default; }
.vmset-hd-cancel:disabled { opacity: .5; cursor: default; }

/* ── body: 186px vertical tab rail + scrolling pane ── */
.vmset-body { flex: 1 1 0; min-height: 0; display: flex; }
.vmset-tabrail { width: 186px; flex: 0 0 auto; border-right: 1px solid rgba(255,255,255,.07); padding: 10px 8px; display: flex; flex-direction: column; gap: 2px; }
.vmset-tab { display: flex; align-items: center; gap: 10px; background: transparent; border: none; border-radius: 8px; padding: 9px 11px; cursor: pointer; text-align: left; }
.vmset-tab:hover:not(.is-active) { background: rgba(255,255,255,.03); }
.vmset-tab.is-active { background: rgba(55,148,255,.12); }
.vmset-tab-l { font-size: 13px; color: #c4c8ce; }
.vmset-tab-l.is-active { font-weight: 600; color: #e6e8ec; }
.vmset-tab-danger-l { color: #ff8a7e; }
.vmset-tab-danger.is-active { background: rgba(255,107,107,.12); }
.vmset-tabrail-sp { flex: 1 1 0; }

.vmset-pane { flex: 1 1 0; min-width: 0; overflow-y: auto; padding: 22px 26px; display: flex; flex-direction: column; gap: 24px; }
.vmset-block { display: flex; flex-direction: column; gap: 16px; max-width: 620px; }
.vmset-h { font-size: 15px; font-weight: 700; color: #e6e8ec; }
.vmset-h-danger { color: #ff8a7e; }
/* Proxmox-style Hardware device table */
/* Proxmox-style hardware manifest table. Head + rows share ONE 4-column grid
   (icon · Device label · Value · actions) so columns line up exactly. */
.vmset-hw-table { display: flex; flex-direction: column; border: 1px solid var(--border, #2a2f3a); border-radius: 10px; overflow: hidden; }
.vmset-hw-row { display: grid; grid-template-columns: 28px minmax(160px, 240px) minmax(0, 1fr); gap: 10px; align-items: center; padding: 9px 14px; cursor: pointer; }
.vmset-hw-row { border-top: 1px solid var(--border, #2a2f3a); }
.vmset-hw-row:first-child { border-top: none; }
.vmset-hw-row:hover { background: rgba(255,255,255,.02); }
.vmset-hw-row.is-selected { background: rgba(55,148,255,.14); }
.vmset-hw-ic { display: inline-flex; align-items: center; justify-content: center; }
.vmset-hw-label { display: flex; align-items: center; font-size: 13px; color: #d6d9e0; font-weight: 600; min-width: 0; }
/* text-overflow: ellipsis — khong phai trang tri. Cot nay la nowrap + minmax(0,1fr) nen chuoi dai
   BI CAT (do duoc: 3/13 hang, scrollWidth 339/361/489 vs clientWidth 302). Mac dinh `clip` cat
   ngang giua tu MA KHONG DE LAI DAU HIEU nao, va `overflow-x:auto` cung khong cuu duoc vi macOS an
   thanh cuon ⇒ nguoi doc khong biet la con nua. `ellipsis` lam viec "bao con nua"; `title` tren
   chinh the (controllers/vms.js) lam viec "cho doc phan con lai". Thieu mot trong hai la con mu. */
.vmset-hw-summary { min-width: 0; font-size: 12.5px; color: #9aa0ad; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow-x: auto; white-space: nowrap; text-overflow: ellipsis; }
.vmset-hw-edit { padding: 4px 10px; border-radius: 6px; border: 1px solid var(--border, #2a2f3a); background: var(--panel, #12151c); color: #cdd2db; cursor: pointer; font-size: 12px; }
.vmset-hw-edit:hover:not([disabled]) { border-color: var(--accent, #3794ff); color: var(--accent, #3794ff); }
.vmset-hw-edit[disabled] { opacity: .45; cursor: default; }

/* Flat Key|Value table for Options + Cloud-Init (Proxmox "Options" grid). */
.vmset-kv-table { display: flex; flex-direction: column; border: 1px solid var(--border, #2a2f3a); border-radius: 10px; overflow: hidden; }
.vmset-kv-row { display: grid; grid-template-columns: 40% minmax(0, 1fr); gap: 10px; align-items: center; padding: 9px 14px; border-top: 1px solid var(--border, #2a2f3a); cursor: pointer; }
.vmset-kv-row:first-child { border-top: none; }
.vmset-kv-row:hover { background: rgba(255,255,255,.02); }
.vmset-kv-row.is-selected { background: rgba(55,148,255,.14); }
.vmset-kv-key { font-size: 13px; color: #d6d9e0; font-weight: 600; }
.vmset-kv-val { min-width: 0; font-size: 12.5px; color: #9aa0ad; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow-x: auto; white-space: nowrap; }

/* Options + Cloud-Init dialog controls (used inside the per-row modals). */
.vmset-opt-input { width: 120px; padding: 5px 9px; border-radius: 6px; border: 1px solid var(--border, #2a2f3a); background: var(--panel, #12151c); color: #e6e9ef; font-size: 13px; }
.vmset-opt-input:focus { outline: none; border-color: var(--accent, #3794ff); }
.vmset-opt-input-text { width: 100%; }
.vmset-ci-ssh { width: 100%; min-height: 84px; resize: vertical; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--border, #2a2f3a); background: var(--panel, #12151c); color: #e6e9ef; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }
.vmset-ci-dhcp { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; color: #9aa0ad; }
.vmset-save-btn { padding: 8px 16px; border-radius: 8px; border: none; background: var(--accent, #3794ff); color: #fff; cursor: pointer; font-size: 13px; font-weight: 600; }
.vmset-save-btn:hover { filter: brightness(1.08); }

/* Dialog labelled row (label above input). */
.vmset-dlg-row { margin-bottom: 12px; }
.vmset-dlg-row:last-child { margin-bottom: 0; }
.vmset-dlg-lbl { font-size: 12px; font-weight: 600; color: #c4c8ce; margin-bottom: 5px; }

/* Toolbar (Add▾/Remove/Edit/Disk Action/Revert · Edit/Revert · Remove/Edit/Regenerate). */
.vmset-hw-toolbar { margin-bottom: 12px; }
.vmset-tb-grp { display: flex; align-items: center; gap: 8px; }
.vmset-tb-sp { flex: 1 1 auto; }
.vmset-tb-diskaction { position: relative; }
.vmset-hw-menu-r { left: auto; right: 0; }

/* Add ▾ toolbar + dropdown over the Hardware table (Proxmox parity). */
.vmset-hw-bar { position: relative; margin-bottom: 10px; }
.vmset-hw-addbtn { display: inline-flex; align-items: center; gap: 5px; padding: 7px 12px; border-radius: 8px; border: 1px solid var(--border, #2a2f3a); background: var(--panel-2, #1b1f27); color: #cdd2db; cursor: pointer; font-size: 13px; font-weight: 600; }
.vmset-hw-addbtn:hover, .vmset-hw-addbtn.is-open { border-color: var(--accent, #3794ff); color: var(--accent, #3794ff); }
.vmset-hw-menu { position: absolute; top: calc(100% + 4px); left: 0; z-index: 20; min-width: 210px; padding: 5px; border-radius: 10px; border: 1px solid var(--border, #2a2f3a); background: var(--panel, #12151c); box-shadow: 0 8px 28px rgba(0,0,0,.45); }
.vmset-hw-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border: none; border-radius: 7px; background: transparent; color: #d6d9e0; cursor: pointer; font-size: 13px; text-align: left; }
.vmset-hw-menu-item:hover { background: rgba(255,255,255,.05); }
.vmset-hw-menu-item.is-disabled { color: #5a606c; cursor: not-allowed; }

/* Boot Order editor modal (Proxmox "Edit: Boot Order"). */
.vmset-modal-backdrop { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.5); }
.vmset-modal { width: 460px; max-width: 92vw; padding: 18px; border-radius: 14px; border: 1px solid var(--border, #2a2f3a); background: var(--panel, #12151c); box-shadow: 0 18px 50px rgba(0,0,0,.55); }
.vmset-modal-h { font-size: 15px; font-weight: 700; color: #e6e9ef; margin-bottom: 12px; }
.vmset-modal-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }
.vmset-boot-list { display: flex; flex-direction: column; border: 1px solid var(--border, #2a2f3a); border-radius: 10px; overflow: hidden; }
.vmset-boot-row { display: grid; grid-template-columns: 24px 24px 34px 1fr; gap: 8px; align-items: center; padding: 9px 12px; border-top: 1px solid var(--border, #2a2f3a); cursor: grab; background: var(--panel-2, #1b1f27); }
.vmset-boot-row:first-child { border-top: none; }
.vmset-boot-row:active { cursor: grabbing; }
.vmset-boot-grip { color: #5a606c; font-size: 15px; }
.vmset-boot-n { color: #8b909a; font-size: 12px; text-align: center; }
.vmset-boot-label { font-size: 13px; color: #d6d9e0; }

/* Row label + sub — kept for the Danger pane row + dialog sub-hints (vmset-dlg-row sub). */
.vmset-row-t { font-size: 13px; font-weight: 600; color: #c4c8ce; }
.vmset-row-sub { font-size: 11.5px; color: #6e7681; margin-top: 2px; }

/* Credentials */
.vmset-cred-h { display: flex; align-items: center; gap: 9px; }
.vmset-vault-pill { display: flex; align-items: center; gap: 5px; font: 10px/1 var(--font-mono); color: #46d19e; background: rgba(70,209,158,.1); border-radius: 5px; padding: 3px 7px; }
.vmset-cred-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.vmset-field { display: flex; flex-direction: column; gap: 6px; }
.vmset-field-lbl { font: 11px/1 var(--font-mono); letter-spacing: .08em; color: #6e7681; }
.vmset-input { display: flex; align-items: center; gap: 8px; background: #16181d; border: 1px solid rgba(255,255,255,.1); border-radius: 9px; padding: 9px 12px; }
.vmset-input-val { flex: 1 1 0; font: 13px/1 var(--font-mono); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vmset-input-pass { letter-spacing: .14em; }
.vmset-eye, .vmset-copy { width: 24px; height: 24px; border: none; background: transparent; display: grid; place-items: center; cursor: pointer; }
.vmset-eye { color: #9aa0a6; } .vmset-eye:hover { color: #e6e8ec; }
.vmset-copy { color: #6e7681; } .vmset-copy:hover { color: #c4c8ce; }
.vmset-cred-btns { display: flex; gap: 10px; }
.vmset-cbtn { display: flex; align-items: center; gap: 7px; background: #16181d; border: 1px solid rgba(255,255,255,.12); color: #c4c8ce; border-radius: 9px; padding: 9px 14px; font: 600 12.5px/1 var(--font-sans); cursor: pointer; }
.vmset-cbtn:hover { color: #e6e8ec; border-color: rgba(255,255,255,.24); }

/* ── Shared bits reused by the Add▾ dialog (host-USB/host-PCI/NIC/disk-size) ──── */
.vmset-net-loading { padding: 22px 0; text-align: center; font-size: 12.5px; color: #6e7681; }
.vmset-net-err { margin-bottom: 10px; padding: 8px 12px; border-radius: 8px; background: rgba(255,107,107,.08); border: 1px solid rgba(255,107,107,.3); color: #ff8f8f; font-size: 12px; }
.vmset-nic-id { font: 600 11px/1 var(--font-mono); color: #6e7681; flex: 0 0 auto; width: 34px; }
.vmset-nic-def { font-size: 10px; color: #8b909a; border: 1px solid rgba(255,255,255,.14); border-radius: 5px; padding: 1px 6px; }
.vmset-nic-meta { font: 11px/1 var(--font-mono); color: #8b909a; }
.vmset-nic-add-row { display: flex; gap: 8px; }
.vmset-seg { flex: 1 1 0; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: #16181d; color: #8b909a; font: 600 12px/1 var(--font-sans); cursor: pointer; }
.vmset-seg.is-active { color: #e6e8ec; border-color: var(--accent); background: rgba(55,148,255,.08); }
.vmset-nic-group { flex: 1 1 0; background: #0e1014; border: 1px solid rgba(255,255,255,.14); border-radius: 8px; padding: 9px 12px; color: #e6e8ec; font: 12px/1 var(--font-mono); }
.vmset-nic-add-hint { font-size: 11.5px; color: #6e7681; }
.vmset-dev-pick { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; background: #16181d; border: 1px solid rgba(255,255,255,.09); border-radius: 8px; padding: 9px 12px; color: #c4c8ce; cursor: pointer; margin-bottom: 6px; }
.vmset-dev-pick:last-child { margin-bottom: 0; }
.vmset-dev-pick:hover:not(:disabled) { border-color: rgba(255,255,255,.24); background: #1b1e24; }
.vmset-dev-pick:disabled { opacity: .5; cursor: not-allowed; }

/* empty-state card (Cloud-Init "no drive", Display "auto") + danger */
.vmset-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px 0; text-align: center; }
.vmset-empty-t { margin: 0; font-size: 12.5px; color: #6e7681; max-width: 340px; line-height: 1.5; }
.vmset-danger-card { border: 1px solid rgba(255,107,107,.25); border-radius: 10px; background: rgba(255,107,107,.04); padding: 14px 16px; }
.vmset-danger-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.vmset-danger-btn { flex: 0 0 auto; height: 32px; padding: 0 14px; border-radius: 8px; border: 1px solid rgba(255,107,107,.4); background: transparent; color: #ff8a7e; font: 600 12.5px/1 var(--font-sans); cursor: pointer; }
.vmset-danger-btn:hover { background: rgba(255,107,107,.12); color: #ff6b6b; }

/* ── VM overlays (creds dialog + wizard) — same visual language as tm-confirm ── */
.vm-overlay { position: fixed; inset: 0; z-index: 1100; display: grid; place-items: center; background: rgba(0,0,0,.55); }
.vm-modal { width: 420px; max-width: 92vw; max-height: 88vh; overflow-y: auto; padding: 18px; display: flex; flex-direction: column; gap: 12px; background: #111318; border: 1px solid rgba(255,255,255,.12); border-radius: 14px; box-shadow: 0 24px 50px rgba(0,0,0,.6); }
.vm-modal-title { font-size: var(--t-md); font-weight: 700; color: var(--fg-strong); }
.vm-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* creds dialog */
.vm-creds-row { display: flex; align-items: center; gap: 8px; }
.vm-creds-lbl { flex: none; width: 78px; font-size: var(--t-sm); color: var(--muted); }
.vm-creds-val { flex: 1 1 auto; min-width: 0; font: var(--t-base)/1 var(--font-mono); color: var(--fg-strong); background: var(--code-bg); padding: 6px 8px; border-radius: 6px; overflow: hidden; text-overflow: ellipsis; }
.vm-creds-reveal, .vm-creds-copy { flex: none; width: 30px; height: 30px; border-radius: 7px; border: 1px solid var(--border); background: transparent; cursor: pointer; color: var(--muted); }
.vm-creds-reveal:hover, .vm-creds-copy:hover { color: var(--fg-strong); border-color: var(--accent); }
.vm-creds-note { font-size: var(--t-xs); color: var(--faint); line-height: 1.5; }

/* wizard */
.vm-wizard { width: 480px; }
.vm-wiz-steps { display: flex; align-items: center; gap: 6px; }
.vm-wiz-dot { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font-size: var(--t-xs); font-weight: 700; color: var(--muted); border: 1px solid var(--border); }
.vm-wiz-dot.on { color: #fff; background: var(--accent); border-color: var(--accent); }
.vm-wiz-dot.done { color: var(--green); border-color: rgba(70,209,158,.4); }
.vm-wiz-line { flex: 1; height: 1px; background: var(--border); }
.vm-wiz-h { font-size: var(--t-base); font-weight: 600; color: var(--fg-strong); }
.vm-os-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.vm-os-pick { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; text-align: left; padding: 12px; border-radius: 10px; border: 1px solid var(--border); background: var(--panel-2); cursor: pointer; }
.vm-os-pick:hover { border-color: var(--border-strong); }
.vm-os-pick.on { border-color: var(--accent); background: var(--accent-wash); }
.vm-os-pick.off { opacity: .5; cursor: not-allowed; }
.vm-os-pick-ic { display: inline-flex; }
/* iOS activation hint (shown under the OS grid when iOS is unavailable but activatable) */
.vm-os-activate { grid-column: 1 / -1; margin: 4px 0 2px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2); }
.vm-os-activate-t { display: block; font-size: 12px; color: var(--text-2); margin-bottom: 6px; }
.vm-os-activate-cmd { display: flex; align-items: center; gap: 8px; }
.vm-os-activate-cmd code { flex: 1; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; background: var(--panel); border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; overflow-x: auto; white-space: nowrap; }
.vm-os-activate-copy { flex: 0 0 auto; padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--panel); color: var(--text); cursor: pointer; font-size: 12px; }
.vm-os-activate-copy:hover { border-color: var(--accent); color: var(--accent); }
.vm-os-pick-l { font-size: var(--t-base); font-weight: 600; color: var(--fg-strong); }
.vm-os-pick-sub { font-size: var(--t-xs); color: var(--muted); }
.vm-wiz-field { display: flex; flex-direction: column; gap: 5px; font-size: var(--t-sm); color: var(--fg-2); }
.vm-wiz-field span em { color: var(--faint); font-style: normal; }
.vm-wiz-field input { font: var(--t-base)/1 var(--font-ui); color: var(--fg-strong); background: var(--bg-elev); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; }
.vm-wiz-field input:focus { outline: none; border-color: var(--accent); }
.vm-wiz-field input:disabled { opacity: .5; }
.vm-wiz-check { display: flex; align-items: center; gap: 8px; font-size: var(--t-sm); color: var(--fg-2); cursor: pointer; }
.vm-wiz-review { grid-template-columns: 100px 1fr; }

/* Ubuntu edition chooser: two explicit GUI / No-GUI cards (radio group) so the choice is obvious,
   not a hidden checkbox. The selected card gets the accent border; the radio itself is hidden. */
.vm-wiz-editions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.vm-wiz-edition { display: flex; flex-direction: column; gap: 3px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-elev); cursor: pointer; transition: border-color .12s, background .12s; }
.vm-wiz-edition:hover { border-color: var(--fg-3, var(--faint)); }
.vm-wiz-edition.sel { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--bg-elev)); }
.vm-wiz-edition input { position: absolute; opacity: 0; pointer-events: none; }
.vm-wiz-edition-t { font-size: var(--t-base); font-weight: 600; color: var(--fg-strong); }
.vm-wiz-edition-s { font-size: var(--t-xs, 11px); color: var(--fg-2); line-height: 1.35; }
@media (max-width: 520px) { .vm-wiz-editions { grid-template-columns: 1fr; } }

/* responsive: narrow viewports drop the right AGENTS column, then the master, so the live screen
   always survives. The master reappears as an overflow-scroll strip above the center under 720px. */
@media (max-width: 1180px) {
  .vm-agents { width: 300px; }
}
@media (max-width: 980px) {
  .vm-agents { display: none; }
  .vm-master { width: 256px; }
}
@media (max-width: 720px) {
  .vms-view { flex-direction: column; }
  .vm-master { width: auto; flex: 0 0 auto; max-height: 42%; border-right: none; border-bottom: 1px solid rgba(255,255,255,.07); }
  .vm-center { flex: 1 1 0; }
}

/* ════════ T05 / UC-H4-05 — REMOTE-VM live-screen center-column editor tab ════════
   A remote VM opened as a col-2 tab: an [AI CONTROLLED] header, an ORANGE Take-control
   button (T03 lock), the live screen frame (T02) + a "chỉ xem" lock pill, and a latency
   footer showing the real round-trip ms. Fills the editor cell (header · body · footer). */
.vm-editor-view { height: 100%; min-height: 0; display: flex; }
.vm-ed { flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: #06070a; }

/* header — remote · <name> · [AI CONTROLLED] · <res> · <fps> + orange Take control */
.vm-ed-head { flex: none; display: flex; align-items: center; gap: var(--s-2); padding: 10px var(--s-4); border-bottom: 1px solid var(--border); background: var(--panel-2); flex-wrap: wrap; }
.vm-ed-loading { font-size: var(--t-sm); color: var(--muted); }
.vm-ed-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; font-size: var(--t-sm); }
.vm-ed-dot { color: var(--faint); }
.vm-ed-remote { font: 700 var(--t-xs)/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.vm-ed-name { font-weight: 600; color: var(--fg-strong); }
.vm-ed-res, .vm-ed-fps { font: var(--t-sm)/1 var(--font-mono); color: var(--muted); }
/* [AI CONTROLLED] pill — mono caps, accent-tinted 4px chip (design dc:1886: 9px mono,
   accent-wash bg, 4px radius, no border — a subtle tag, not a rounded pill). */
.vm-ed-ai { display: inline-flex; align-items: center; gap: 5px; font: 700 9px/1 var(--font-mono); letter-spacing: .06em; padding: 3px 6px; border-radius: 4px; color: var(--accent); background: rgba(55,148,255,.1); }
.vm-ed-ai svg { flex: none; }
.vm-ed-ai-off { color: var(--green); background: rgba(70,209,158,.12); }
.vm-ed-spacer { flex: 1 1 auto; }
/* ORANGE Take control — the safety action that seizes input from the AI (distinct from the
   blue primary so it reads as "caution: this interrupts the AI"). */
.vm-ed-take { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); font-weight: 700; color: #fff; background: #f5892b; border: 1px solid #f5892b; border-radius: var(--r-sm); padding: var(--s-1) var(--s-3); cursor: pointer; line-height: 1.4; }
.vm-ed-take:hover { background: #e0761c; border-color: #e0761c; }
.vm-ed-take:focus-visible { outline: 2px solid #f5892b; outline-offset: 2px; }
.vm-ed-take:disabled { opacity: .5; cursor: not-allowed; }
.vm-ed-release { flex: none; }

/* body — the live screen frame + lock pill */
.vm-ed-body { flex: 1 1 auto; min-height: 0; display: flex; padding: var(--s-4); overflow: auto; }
.vm-ed-screen-skel { width: 70%; height: 60%; margin: auto; }
.vm-ed-screen { position: relative; margin: auto; max-width: 100%; max-height: 100%; border-radius: 8px; overflow: hidden; border: 1px solid var(--border-strong); box-shadow: var(--shadow); }
/* THE BOX IS ITS OWN, NOT THE IMAGE'S.
   This used to hold a 16:9 placeholder ONLY while no frame had decoded, on the reasoning that
   "once a frame loads the img provides the real intrinsic size and the aspect-ratio min is a
   no-op". That reasoning has one unstated premise: that the loaded <img> is actually laid out.
   MEASURED by qc 2026-09-23 inside the VM: `div.vm-ed-screen.is-locked` was 2x2 px — the two
   borders and nothing else — while the frame itself painted 320x200 at (630,400), a thumbnail
   floating in a ~770x790 black pane. The blob was fine (maxLum 255, avgLum 64, real content);
   the CONTAINER had collapsed. It can: the img is born `style="display:none"` and is revealed
   only on a successful decode, and the VNC canvas beside it contributes no size of its own, so
   between the two there are states with a loaded flag and no laid-out child.
   A container whose size depends on which child happens to be displayed will collapse in some
   state nobody enumerated — so it no longer depends on that. The box is 16:9 ALWAYS, in every
   state, and the frame is letterboxed inside it (see .vm-ed-img below): the geometry stops being
   a consequence of which child won and becomes a property of the box. That is the trade — a
   16:10 guest gets thin bars top and bottom — and it is worth it, because the alternative is a
   layout whose correctness depends on enumerating child states, which is what just failed. */
.vm-ed-screen { width: min(880px, 100%); aspect-ratio: 16 / 9; }
.vm-ed-screen.is-locked { cursor: not-allowed; }
.vm-ed-screen.is-live { cursor: crosshair; }
/* FILLS THE BOX ABOVE, LETTERBOXED — it no longer sets the box's size, so it must not leave it
   half empty either. `min-width: 320px` was what made the collapse VISIBLE rather than merely
   wrong: with the container at 2x2 the image still drew 320 wide and spilled out of it as a
   thumbnail on black. Now the container owns the geometry and `object-fit: contain` keeps a
   16:10 guest (2880x1800, the macOS VM here) undistorted inside a 16:9 frame — the same rule
   `.vm-screen-img` and the noVNC canvas already follow two hundred lines above. */
.vm-ed-img { display: block; position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }
/* placeholder frame shown until a real screenshot decodes (TODO: live daemon frame stream).
   A dark VM-screen panel — never a broken-<img> glyph. Hidden the moment .vm-ed-img gets a frame. */
.vm-ed-img[data-loaded="1"] + .vm-ed-frame-skel { display: none; }
.vm-ed-frame-skel { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; text-align: center;
  background: radial-gradient(120% 120% at 50% 0%, #12161d 0%, #0a0c10 70%); color: var(--muted); min-width: 320px; min-height: 200px; }
.vm-ed-frame-skel .vm-ed-frame-ic { color: var(--faint); opacity: .7; }
.vm-ed-frame-skel .vm-ed-frame-t { font: 600 var(--t-sm)/1.3 var(--font-mono); color: var(--fg-2); }
.vm-ed-frame-skel .vm-ed-frame-sub { font: var(--t-xs)/1.4 var(--font-mono); color: var(--muted); }
.vm-ed-frame-skel .vm-ed-frame-dots { display: inline-flex; gap: 5px; }
.vm-ed-frame-skel .vm-ed-frame-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--faint); animation: vmFrameDot 1.2s ease-in-out infinite; }
.vm-ed-frame-skel .vm-ed-frame-dots i:nth-child(2) { animation-delay: .2s; }
.vm-ed-frame-skel .vm-ed-frame-dots i:nth-child(3) { animation-delay: .4s; }
@keyframes vmFrameDot { 0%,80%,100% { opacity: .25; } 40% { opacity: 1; } }
.vm-ed-lock { position: absolute; inset: 0; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 16px; pointer-events: none; background: linear-gradient(0deg, rgba(0,0,0,.5), rgba(0,0,0,0) 40%); }
.vm-ed-lock-pill { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); font-weight: 700; color: #fff; background: rgba(0,0,0,.6); border: 1px solid rgba(255,255,255,.2); padding: 5px 12px; border-radius: 999px; backdrop-filter: blur(4px); }
.vm-ed-off { margin: auto; text-align: center; color: var(--muted); }
.vm-ed-off-ic { font-size: 40px; color: var(--faint); }
.vm-ed-off p { margin: 6px 0 0; font-size: var(--t-md); font-weight: 600; color: var(--fg-2); }
.vm-ed-off-sub { font-size: var(--t-sm) !important; font-weight: 400 !important; color: var(--muted) !important; }

/* footer — SyrixVM · <OS version> · latency <N>ms · ● live */
.vm-ed-foot { flex: none; display: flex; align-items: center; gap: 6px; padding: 7px var(--s-4); border-top: 1px solid var(--border); background: var(--panel-2); font: var(--t-sm)/1 var(--font-mono); color: var(--muted); }
.vm-ed-foot-l { font-weight: 700; color: var(--fg-2); }
.vm-ed-foot-os { color: var(--muted); }
.vm-ed-lat { color: var(--fg-2); }
.vm-ed-status { display: inline-flex; align-items: center; gap: 5px; font-weight: 700; }
.vm-ed-status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); }
.vm-ed-status.is-live { color: var(--green); }
.vm-ed-status.is-live .vm-ed-status-dot { background: var(--green); box-shadow: 0 0 0 3px rgba(70,209,158,.18); }
.vm-ed-status.is-down { color: var(--yellow); }
.vm-ed-status.is-down .vm-ed-status-dot { background: var(--yellow); }

/* "Mở trong cockpit" ghost button in the full-screen detail bar */
.vm-open-cockpit { flex: none; }

/* Custom-ISO file picker (wizard) */
.vm-iso-browser { max-width: 520px; width: 90vw; max-height: 70vh; display: flex; flex-direction: column; }
/* The ISO picker and Storage mount on document.body inside .vm-overlay and use .vm-wiz only as a name —
   no rule paints .vm-wiz, so without its own box the text sat straight on the live guest screen. */
.vm-overlay > .vm-iso-browser { padding: 16px 18px; background: #111318; border: 1px solid rgba(255,255,255,.12); border-radius: 14px; box-shadow: 0 24px 50px rgba(0,0,0,.6); }
.vm-iso-browser > .vm-storage-body, .vm-iso-browser > .vm-iso-list { min-height: 0; overflow-y: auto; }
.vm-iso-head { display: flex; align-items: center; justify-content: space-between; font-weight: 600; padding-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,.08); }
.vm-iso-list { overflow-y: auto; margin-top: 8px; }
.vm-iso-cur { font-size: 11px; color: #6e7681; padding: 4px 2px 8px; word-break: break-all; }
.vm-iso-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 7px 8px; background: none; border: none; color: #e6e8ec; cursor: pointer; border-radius: 6px; font-size: 13px; }
.vm-iso-row:hover { background: rgba(255,255,255,.06); }
.vm-iso-row.is-file { color: #9db8ff; }
.vm-iso-ic { width: 18px; text-align: center; flex: 0 0 auto; }
.vm-iso-sz { margin-left: auto; font-size: 11px; color: #6e7681; }
.vm-iso-empty, .vm-iso-list { color: #9aa2ad; }
.vm-iso-pick { display: flex; align-items: center; gap: 10px; }
/* B-DISK: Machines › Storage (reuses the picker's box) */
.vm-list-storage { margin-right: 10px; }
/* The head holds title + filter + Storage + New: the buttons keep their label, the filter gives way. */
.vm-list-tools { min-width: 0; }
.vm-list-tools > .vm-list-new { flex: 0 0 auto; white-space: nowrap; }
.vm-list-search { min-width: 0; flex: 0 1 auto; }
.vm-list-search-input { min-width: 0; }
.vm-storage-group > summary { list-style: none; cursor: pointer; }
.vm-storage-group > summary::-webkit-details-marker { display: none; }
.vm-storage-group > summary .vm-storage-name::before { content: '▸ '; color: #6e7681; }
.vm-storage-group[open] > summary .vm-storage-name::before { content: '▾ '; }
.vm-storage-group > .vm-storage-row { padding-left: 22px; }
.vm-storage { max-width: 600px; }
.vm-storage-body { overflow-y: auto; margin-top: 8px; color: #9aa2ad; font-size: 13px; }
.vm-storage-vol { color: #e6e8ec; padding: 2px 2px 6px; }
.vm-storage-snaps { font-size: 12px; color: #e3b341; padding: 4px 2px 8px; }
.vm-storage-h { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: #6e7681; margin: 10px 2px 4px; }
.vm-storage-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px; border-radius: 6px; }
.vm-storage-row:hover { background: rgba(255,255,255,.04); }
.vm-storage-row.is-missing .vm-storage-name { color: #e3b341; }
.vm-storage-main { flex: 1 1 auto; min-width: 0; }
.vm-storage-name { color: #e6e8ec; word-break: break-all; }
.vm-storage-kind, .vm-storage-used { font-size: 11px; color: #9aa2ad; margin-top: 2px; }
.vm-storage-note { font-size: 11px; color: #6e7681; margin-top: 2px; }
.vm-storage-rm { flex: 0 0 auto; }
.vm-iso-path { font-size: 13px; color: #e6e8ec; } .vm-iso-path.empty { color: #6e7681; }
.tm-cli-auth-devcode { font-size: 15px; color: #e6e8ec; padding: 10px 12px; background: #0f1115; border-radius: 8px; border: 1px solid rgba(70,209,158,.35); letter-spacing: 2px; font-family: var(--font-mono); }
.tm-cli-auth-devcode b { color: #46d19e; }

/* ═══════════ NEW PROJECT dialog — design v5 (2026-07-19, match hi-fi 100%) ═══════════
   Source of truth: design image + WIREFRAMES-virtual-employee.md §1.1. Scoped under
   .np-modal so the inline (non-modal) fallback keeps the old look without conflicts. */
.np-modal { width: min(568px, 94vw); max-height: 92vh; display: flex; flex-direction: column; background: #16171b; border: 1px solid rgba(255,255,255,.07); border-radius: 16px; box-shadow: 0 24px 80px rgba(0,0,0,.55); }
.np-modal .tm-set-title-bar { display: flex; align-items: center; padding: 15px 20px; border-bottom: 1px solid rgba(255,255,255,.06); }
.np-modal .np-modal-title { font-size: 15px; font-weight: 600; color: #e6e8ec; letter-spacing: .01em; }
.np-modal .tm-set-close { margin-left: auto; background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08); color: #aeb6c6; width: 28px; height: 28px; border-radius: 8px; cursor: pointer; font-size: 13px; line-height: 1; }
.np-modal .tm-set-close:hover { color: #e6e8ec; background: rgba(255,255,255,.08); }
.np-modal .new-project-form { overflow-y: auto; padding: 18px 22px 22px; margin: 0; border: 0; background: transparent; box-shadow: none; display: flex; flex-direction: column; gap: 16px; }
/* Lost first "+ Add folder" click (measured :4675 0.9.44, 2026-09-23): with the dialog at scrollTop 0 the
   folder list's lower rows sit UNDER the sticky .np-actions footer below (visible footer 596–673px, the
   banh-mi-landing button 596–620px). Scrolling that row into view (CDP click, keyboard focus) centred it
   inside the list but left it behind the opaque footer, so the click at (932,608) landed on div.np-actions
   and nothing was added; the second try worked only because the first had moved things. The scroll port
   must exclude the footer: scroll-padding-bottom makes scrollIntoView/focus park a row ABOVE it. It must stay
   >= the footer's visible height (14px pad + ~40px button row + 22px pad + 1px border = 77px measured). */
.np-modal .new-project-form { scroll-padding-bottom: 96px; }

/* ── step labels: mono-caps · required(red) / optional(muted) / extra(dim) ── */
.np-modal .np-step-label { display: block; font-family: var(--font-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #7d8496; margin-bottom: 7px; }
.np-modal .np-lbl-required { color: #e0635d; }
.np-modal .np-lbl-optional, .np-modal .np-lbl-sep { color: #565d6e; }
.np-modal .np-lbl-extra { color: #565d6e; text-transform: none; letter-spacing: .02em; }
.np-modal .np-field { display: block; }

/* ── text inputs ── */
.np-modal .np-input { width: 100%; box-sizing: border-box; background: #0e0f12; border: 1px solid #262a33; border-radius: 10px; padding: 11px 12px; font-size: 13.5px; color: #e6e8ec; }
.np-modal .np-input::placeholder { color: #4d5462; }
.np-modal .np-input:focus { outline: none; border-color: #3b82f6; }
.np-modal .np-textarea { resize: none; min-height: 42px; font-family: inherit; }
.np-modal textarea.np-input { resize: none; }
.np-modal .np-cli-dot { width: 9px; height: 9px; border-radius: 3px; background: var(--cli-dot, #8b93a7); flex: none; display: inline-block; }
.np-modal .np-seed-model { text-transform: capitalize; }

/* ── DAEMON / OPEN-ON custom select face (real <select> stretched invisibly) ── */
.np-modal .np-daemon-face, .np-modal .np-openon-face { position: relative; display: flex; align-items: center; gap: 9px; background: #0e0f12; border: 1px solid #262a33; border-radius: 10px; padding: 11px 12px; cursor: pointer; }
.np-modal .np-daemon-face:hover, .np-modal .np-openon-face:hover { border-color: #3b4250; }
.np-modal .np-daemon-dot { width: 8px; height: 8px; border-radius: 50%; background: #34d399; flex: none; }
.np-modal .np-daemon-name { font-size: 13.5px; font-weight: 500; color: #e6e8ec; white-space: nowrap; }
.np-modal .np-daemon-meta { font-size: 12.5px; color: #7d8496; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-modal .np-daemon-caret { margin-left: auto; color: #565d6e; font-size: 11px; flex: none; }
.np-modal .np-daemon-select { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; appearance: none; }
.np-modal .np-daemon-single { cursor: default; }
.np-modal .np-openon-icon { font-size: 14px; flex: none; }
.np-modal .np-openon-lbl { font-size: 13px; color: #e6e8ec; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-modal .np-openon-default { font-size: 11.5px; color: #565d6e; flex: none; }

/* ── FOLDER chips (★ default / ☆ set default / ✕) ── */
.np-modal .np-folder-list { display: flex; flex-direction: column; gap: 8px; }
.np-modal .np-folder-row { display: flex; align-items: center; gap: 10px; border: 1px solid #262a33; border-radius: 10px; padding: 9px 12px; background: #0e0f12; }
.np-modal .np-folder-row.is-default { border-color: rgba(59,130,246,.5); }
.np-modal .np-folder-badge { flex: none; font-family: var(--font-mono); font-size: 11px; border-radius: 999px; padding: 3px 9px; white-space: nowrap; }
.np-modal .np-folder-badge.is-default { background: rgba(59,130,246,.16); color: #9ec1ff; border: 1px solid rgba(59,130,246,.4); }
.np-modal .np-folder-setdef { background: transparent; color: #7d8496; border: 1px solid #333a46; cursor: pointer; }
.np-modal .np-folder-setdef:hover { color: #cfd3dc; border-color: #3b4250; }
.np-modal .np-folder-path { flex: 1; font-family: var(--font-mono); font-size: 12.5px; color: #cfd3dc; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-modal .np-folder-x { flex: none; background: none; border: none; color: #565d6e; font-size: 13px; cursor: pointer; padding: 2px 4px; border-radius: 6px; }
.np-modal .np-folder-x:hover { color: #e0635d; background: rgba(224,99,93,.1); }
.np-modal .np-folders-empty { margin: 0; font-size: 12.5px; color: #626a7c; }
.np-modal .np-folders-empty b { color: #9ec1ff; }
.np-modal .np-folder-hint { margin: 8px 0 10px; font-size: 11.5px; color: #565d6e; }
.np-modal .np-folders-blocked { margin: 0; font-size: 12.5px; color: #8b93a7; border: 1px dashed #333a46; border-radius: 10px; padding: 14px; }

/* ── FOLDER inline browser card ── */
.np-modal .np-pick { border: 1px solid #262a33; border-radius: 12px; background: #101116; overflow: hidden; }
.np-modal .np-pick-bar { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid #1f232b; }
.np-modal .np-pick-crumbs { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 12.5px; }
.np-modal .np-crumb { background: none; border: none; padding: 0; font-size: 12.5px; }
.np-modal .np-crumb-link { color: #6ea8fe; cursor: pointer; }
.np-modal .np-crumb-link:hover { text-decoration: underline; }
.np-modal .np-crumb-current { color: #cfd3dc; }
.np-modal .np-crumb-dead { color: #565d6e; cursor: default; }
.np-modal .np-crumb-sep { color: #4d5462; margin: 0 6px; }
.np-modal .np-pick-newfolder { flex: none; background: rgba(59,130,246,.1); border: 1px solid rgba(59,130,246,.35); color: #6ea8fe; border-radius: 8px; padding: 5px 10px; font-size: 12px; cursor: pointer; white-space: nowrap; }
.np-modal .np-pick-newfolder:hover { background: rgba(59,130,246,.18); }
.np-modal .np-pick-list { list-style: none; margin: 0; padding: 5px; max-height: 218px; overflow-y: auto; }
.np-modal .np-pick-row { display: flex; align-items: center; gap: 8px; padding: 8px 9px; border-radius: 8px; border: 1px solid transparent; }
.np-modal .np-pick-row:hover { background: #171a20; }
.np-modal .np-pick-row.is-added { background: rgba(59,130,246,.08); border-color: rgba(59,130,246,.35); }
.np-modal .np-pick-dir { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; background: none; border: none; padding: 0; text-align: left; cursor: pointer; }
.np-modal .np-pick-folder { color: #7d8496; display: inline-flex; flex: none; }
.np-modal .np-pick-name { font-size: 13.5px; color: #dfe3ea; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-modal .np-pick-add { flex: none; background: transparent; border: 1px solid #333a46; color: #aeb6c6; border-radius: 8px; padding: 4px 10px; font-size: 12px; cursor: pointer; white-space: nowrap; }
.np-modal .np-pick-add:hover { border-color: #3b82f6; color: #cfd8ff; }
.np-modal .np-pick-added { flex: none; color: #6ea8fe; font-size: 12px; white-space: nowrap; }
.np-modal .np-pick-chevron { flex: none; color: #4d5462; font-size: 13px; }
.np-modal .np-pick-state { margin: 0; padding: 16px 14px; font-size: 12.5px; color: #7d8496; }
.np-modal .np-pick-truncated { margin: 0; padding: 8px 14px 12px; font-size: 11.5px; color: #565d6e; }

/* ── ADVANCED collapsible ── */
.np-modal .np-advanced { border: 0; margin: 2px 0 0; }
.np-modal .np-advanced-summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 11px; letter-spacing: .08em; color: #7d8496; }
.np-modal .np-advanced-summary::-webkit-details-marker { display: none; }
.np-modal .np-adv-caret { color: #565d6e; font-size: 13px; transition: transform .15s ease; transform: rotate(0deg); }
.np-modal .np-advanced[open] .np-adv-caret { transform: rotate(90deg); }
.np-modal .np-adv-word { text-transform: uppercase; color: #8b93a7; }
.np-modal .np-advanced-hint { color: #565d6e; text-transform: none; letter-spacing: .02em; }
.np-modal .np-advanced-body { display: flex; flex-direction: column; gap: 16px; padding-top: 14px; }

/* ── VMs platform grid + CHOOSE OS ── */
.np-modal .np-platform-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.np-modal .np-platform { display: flex; align-items: center; gap: 9px; border: 1px solid #262a33; border-radius: 10px; padding: 10px 12px; background: #0e0f12; color: #e6e8ec; font-size: 13.5px; cursor: pointer; text-align: left; min-width: 0; }
.np-modal .np-platform:hover { border-color: #3b4250; }
.np-modal .np-platform.selected { border-color: #3b82f6; }
.np-modal .np-platform-icon { font-size: 14px; flex: none; }
.np-modal .np-platform-name { font-weight: 500; white-space: nowrap; }
.np-modal .np-platform-sub { margin-left: auto; color: #7d8496; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.np-modal .np-platform-check { color: #3b82f6; font-size: 13px; flex: none; margin-left: auto; }
.np-modal .np-platform-sub + .np-platform-check { margin-left: 4px; }
.np-modal .np-newvm-os { border: 1px solid #262a33; border-radius: 10px; padding: 12px; margin-top: 10px; background: #101116; }
.np-modal .np-newvm-caption { display: block; font-family: var(--font-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: #7d8496; margin-bottom: 9px; }
.np-modal .np-newvm-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.np-modal .np-newvm-chip { display: inline-flex; align-items: center; gap: 7px; border: 1px solid #262a33; border-radius: 9px; padding: 7px 11px; background: #0e0f12; color: #dfe3ea; font-size: 13px; cursor: pointer; }
.np-modal .np-newvm-chip:hover { border-color: #3b4250; }
.np-modal .np-newvm-chip.on { border-color: #3b82f6; background: rgba(59,130,246,.1); }
.np-modal .np-newvm-chip.disabled { opacity: .5; cursor: not-allowed; }
.np-modal .np-newvm-badge { width: 16px; height: 16px; border-radius: 4px; background: var(--os-badge, #8a8f98); color: #0b0c0f; font-size: 10.5px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; flex: none; }
.np-modal .np-newvm-need { font-size: 11px; color: #565d6e; }
.np-modal .np-newvm-hint { margin: 9px 0 0; font-size: 11.5px; color: #565d6e; }

/* ── SEED WORKERS ── */
.np-modal .np-seed { border: 0; margin: 0; padding: 0; min-width: 0; }
.np-modal .np-team-legend { padding: 0; margin-bottom: 7px; }
.np-modal .np-seed-seg { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.np-modal .np-seed-btn { border: 1px solid #262a33; border-radius: 10px; padding: 9px 10px; background: #0e0f12; color: #aeb6c6; font-size: 13px; cursor: pointer; }
.np-modal .np-seed-btn:hover { border-color: #3b4250; }
.np-modal .np-seed-btn.active { border-color: #3b82f6; background: rgba(59,130,246,.12); color: #e6e8ec; }
.np-modal .np-seed-pane { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.np-modal .np-seed-cli-seg { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.np-modal .np-seed-cli-btn { display: inline-flex; align-items: center; gap: 7px; border: 1px solid #262a33; border-radius: 9px; padding: 7px 12px; background: #0e0f12; color: #dfe3ea; font-size: 13px; cursor: pointer; }
.np-modal .np-seed-cli-btn:hover { border-color: #3b4250; }
.np-modal .np-seed-cli-btn.active { border-color: #3b82f6; background: rgba(59,130,246,.1); }
.np-modal .np-seed-mm-row { display: flex; gap: 8px; margin-top: 8px; }
.np-modal .np-seed-model { flex: 0 0 42%; min-width: 0; }
.np-modal .np-seed-effort-seg { flex: 1; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
.np-modal .np-seed-effort-btn { border: 1px solid #262a33; border-radius: 10px; padding: 8px 6px; background: #0e0f12; color: #aeb6c6; font-size: 13px; cursor: pointer; }
.np-modal .np-seed-effort-btn:hover { border-color: #3b4250; }
.np-modal .np-seed-effort-btn.active { border-color: #d6a243; background: rgba(214,162,67,.12); color: #ecc069; }
.np-modal .np-seed-brain-hint { margin: 7px 0 0; font-size: 11.5px; color: #565d6e; }

/* ── OPEN IN ── */
.np-modal .np-openin-row { display: flex; gap: 8px; align-items: stretch; }
.np-modal .np-mode-seg { flex: 0 0 44%; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.np-modal .np-mode-btn { border: 1px solid #262a33; border-radius: 10px; padding: 9px 6px; background: #0e0f12; color: #aeb6c6; font-size: 13px; cursor: pointer; }
.np-modal .np-mode-btn:hover { border-color: #3b4250; }
.np-modal .np-mode-btn.active { border-color: #3b82f6; background: rgba(59,130,246,.12); color: #e6e8ec; }
.np-modal .np-mode-btn[disabled] { opacity: .45; cursor: not-allowed; }
.np-modal .np-openon-face { flex: 1; min-width: 0; }
.np-modal .np-openin-hint { margin: 8px 0 0; font-size: 11.5px; color: #565d6e; }

/* ── footer: hint left · Cancel + Create project right ── */
/* #89 — same defect as #73, different dialog. New Project builds its own markup (.np-actions), so the
   .dlg-footer rule does not reach it, and its footer sits in normal flow inside .new-project-form
   (overflow-y:auto). Measured 1280×900: Create is at y=718 with the form collapsed, but opening
   ADVANCED pushes it to y=1319 — 459px BELOW the fold — and the screenshot shows the dialog cut off
   mid VM-list with no Cancel/Create anywhere (/tmp/v02-03-advanced.png). ADVANCED is exactly where
   "Create team" lives, so the user most likely to need the button is the one who cannot see it.
   Sticky + opaque, matching the form's own padding (18px 22px 22px) and the modal background. */
.np-modal .np-actions { position: sticky; bottom: -22px; z-index: 5; background: #16171b; margin: 2px -22px -22px; padding: 14px 22px 22px; display: flex; align-items: center; gap: 12px; border-top: 1px solid rgba(255,255,255,.06); }
.np-modal .np-footer-hint { flex: 1; margin: 0; font-size: 12px; color: #626a7c; }
.np-modal .np-footer-hint b { color: #cfd3dc; }
.np-modal .np-actions-btns { display: flex; gap: 8px; flex: none; }
.np-modal .np-actions .btn { border-radius: 10px; padding: 9px 16px; font-size: 13.5px; }
.np-modal .np-actions .btn-primary { background: #3b82f6; border-color: #3b82f6; font-weight: 600; }
.np-modal .np-actions .btn-primary:hover { background: #2f6fe0; }
/* B-P4 (measured :4675 2026-09-23, rs-39): with no folder chosen, Create project is disabled but the
   rule above paints it the same #3b82f6 — only #np-submit:disabled's opacity .5 told the states apart,
   and at that opacity a saturated blue on this dark card still reads as a live primary. Disabled now
   looks disabled: a neutral grey fill, muted text, no hover, a not-allowed cursor. Covers the
   aria-disabled form too (npRefreshSubmitGate sets both). Specificity outranks #np-submit:disabled
   so the grey is shown at full strength instead of being faded to near-invisible. */
.np-modal .np-actions #np-submit:disabled,
.np-modal .np-actions #np-submit[aria-disabled="true"],
.np-modal .np-actions #np-submit:disabled:hover,
.np-modal .np-actions #np-submit[aria-disabled="true"]:hover {
  background: #2a2e37; border-color: #343945; color: #7d8496; opacity: 1; cursor: not-allowed; box-shadow: none;
}
.np-modal .np-env { display: flex; flex-direction: column; gap: 16px; }
.np-modal .np-env-state { margin: 0; }
.np-modal .np-team-error { margin: 6px 0 0; }
.np-modal-scrim { background: rgba(5,6,9,.7); backdrop-filter: blur(3px); }

/* ── thread search (PO 21-07): local-first find bar + hit marks + deep-load notice ── */
.thread-search { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--border); background: var(--bg-elev1, #17181c); }
.thread-search input { flex: 1 1 180px; min-width: 120px; background: var(--bg, #0f1013); color: var(--fg, #e6e8ec); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 4px 8px; font-size: var(--t-sm); }
.thread-search .ts-count { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--muted); min-width: 56px; text-align: right; }
.thread-search .ts-nav, .thread-search .ts-close { background: transparent; border: 1px solid var(--border); border-radius: var(--r-sm); color: var(--fg); cursor: pointer; padding: 2px 8px; font-size: var(--t-sm); }
.thread-search .ts-more { flex-basis: 100%; font-size: var(--t-xs); color: var(--muted); padding-top: 2px; }
.thread-search .ts-load { background: transparent; border: 1px solid var(--accent); border-radius: var(--r-sm); color: var(--accent); cursor: pointer; padding: 1px 8px; font-size: var(--t-xs); }
#thread .ts-hit { outline: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); outline-offset: 2px; border-radius: var(--r-sm); }
#thread .ts-cur { outline: 2px solid var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* LIVE ACTIVITY strip — the CLI's own progress line while a turn runs (daemon ActivitySignals →
   SSE agent-activity). Sits between the thread and the composer; pulses so "still running" is
   visible at a glance even when the elapsed text is momentarily stable. */
.agent-activity {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  font-size: var(--t-xs); color: var(--text-dim, #9aa3af);
  font-family: var(--font-mono);
  /* PO 16-09: "busy chay nha nha cham cham duoc roi, bo cai thanh ngang di" — cham nhay DA du
     bao hieu dang chay; duong ke ngang het be rong chi cat doi khung chat, lam dai nay doc nhu
     mot vung khac chu khong phai mot dong trang thai. Bo border-top, giu nguyen dot + chu. */
  min-height: 22px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.agent-activity-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--accent);
  animation: agent-activity-pulse 1.2s ease-in-out infinite;
}
@keyframes agent-activity-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}
/* PARKED status (busy=false + a detail, e.g. "⏳ Quota reached — resets in 165h"): a STEADY amber dot,
   no pulse — it reads as "waiting on something", distinct from the live "working" pulse. The text wraps
   (a reset window can be long) instead of being ellipsis-clipped so the whole message is legible. */
.agent-activity.parked {
  color: var(--warn, #d9a441);
  white-space: normal;
}
.agent-activity.parked .agent-activity-dot {
  background: var(--warn, #d9a441);
  animation: none;
}
/* SLEPT (PO 23-07 "agent nào đang sleep phải có button resume trên chat thread"): the auto-sleep engine
   killed this agent's pane to free RAM. A steady moon-blue dot + an inline Resume button — the ONLY
   state where the strip is actionable. Waking is what the button does; while it runs the strip flips
   back to the normal busy look with "Resuming VM…" / "Resuming agent…". */
.agent-activity.slept {
  color: var(--text-dim, #9aa3af);
  white-space: normal;
}
.agent-activity.slept .agent-activity-dot {
  background: var(--accent-2, #7c8cff);
  animation: none;
}
.agent-activity-resume {
  margin-left: auto; flex: none;
  padding: 2px var(--s-2);
  font-family: inherit; font-size: var(--t-xs);
  color: var(--text, #e6e8ec);
  background: var(--surface-2, #23262e);
  border: 1px solid var(--border); border-radius: var(--r-1, 4px);
  cursor: pointer;
}
.agent-activity-resume:hover { background: var(--surface-3, #2c3038); }
.agent-activity-resume[disabled] { opacity: 0.55; cursor: default; }

/* AWAITING answer buttons (28-07) — a blocked agent's choices, answerable from the WEB.
   Before this the only way to unblock a pane that asked a question was to open tmux and press the
   digit by hand ("stop xong chat không chạy"). The card shows the question; these are its choices.
   Wraps (a picker can have 4-5 options) and stays inside the card's clamp. */
.roster-await-opts {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; width: 100%;
}
.roster-await-opt {
  font-family: var(--font-mono); font-size: 10px; line-height: 1.3;
  padding: 3px 7px; max-width: 100%;
  border: 1px solid var(--stroke, #3a3f4b); border-radius: 5px;
  background: transparent; color: var(--text-1, #e6e8ee);
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.roster-await-opt:hover:not(:disabled) { border-color: var(--accent, #4a9eff); color: var(--accent, #4a9eff); }
/* .sel = the row the TUI currently highlights (❯) — i.e. what Enter would take right now. */
.roster-await-opt.sel { border-color: var(--accent, #4a9eff); color: var(--accent, #4a9eff); }
.roster-await-opt:disabled { opacity: .5; cursor: default; }
.roster-await-opts.sent { opacity: .65; }
/* #50: a FREE-TEXT meta-option ("✎ Type something") is a mode-switch to typing your own reply, NOT a
   one-click digit answer. Style it dashed + muted so it reads as "type a reply" and never as a
   selectable choice that completes on click; it routes to the composer, not a rawKey digit. */
.roster-await-opt-freetext {
  border-style: dashed; color: var(--text-2, #9aa0ad); font-style: italic;
}
.roster-await-opt-freetext:hover:not(:disabled) { border-color: var(--accent, #4a9eff); color: var(--accent, #4a9eff); }

/* ── #68 seeding window: the roster + middle pane while a just-created project's seed
      agent is still starting. Deliberately quiet — a slow pulse, no progress bar (we do
      not know the duration and must not fake one). ────────────────────────────────── */
.roster-seeding { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; color: var(--text-2, #9aa0ad); }
.roster-seeding-spinner {
  width: 11px; height: 11px; flex: 0 0 auto; border-radius: 50%;
  border: 2px solid var(--accent, #4a9eff); border-top-color: transparent;
  animation: tm-seed-spin .9s linear infinite;
}
.roster-seeding-sub { color: var(--muted); font-size: 11px; display: block; width: 100%; }
@keyframes tm-seed-spin { to { transform: rotate(360deg); } }
/* Respect a reduced-motion preference: keep the affordance, drop the rotation. */
@media (prefers-reduced-motion: reduce) { .roster-seeding-spinner { animation: none; opacity: .6; } }

/* ── #69 Home "Inactive (N)" group: offline + 0-agent projects fold away so the first
      screen shows what is actually alive. Presentation only — nothing is deleted. ──── */
/* The active list is a CSS grid of cards; the group must be its own FULL-WIDTH band under it,
   not a card sitting in the next free column (which reads as "one more project"). */
.projects-inactive {
  grid-column: 1 / -1; width: 100%; clear: both; display: block;
  margin-top: 10px; border-top: 1px solid var(--border, rgba(255,255,255,.07)); padding-top: 8px;
}
.projects-inactive-summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
  padding: 6px 4px; color: var(--muted); font-size: 12px; user-select: none;
}
.projects-inactive-summary::-webkit-details-marker { display: none; }
.projects-inactive-summary:hover { color: var(--text-2, #9aa0ad); }
.projects-inactive .pi-caret { transition: transform .15s; display: inline-block; }
.projects-inactive[open] .pi-caret { transform: rotate(90deg); }
.projects-inactive .pi-label { font-weight: 600; letter-spacing: .02em; }
.projects-inactive .pi-hint { font-size: 11px; opacity: .65; }
.projects-inactive .projects { opacity: .72; }

/* ── #66: the promoted SEED WORKERS section — visible without expanding Advanced. ──── */
.np-seed-section { margin: 10px 0 2px; }

/* WEBUI-104 — figures read once but not re-read since the server stopped answering. */
.home-stale, .rp-stale { color: #e3b341; font-size: 12px; margin: 0 0 8px; }
.daemons-stale { color: #e3b341; font-weight: normal; }
.ws-switcher-btn.is-stale .ws-switcher-sub, .ws-switcher-btn.is-unread .ws-switcher-sub { color: #e3b341; }

/* [E-2] WEBUI-308 — upload rows, one per file, each showing ITS OWN state.
   Colour carries the same three meanings the rest of the app uses: running green for
   work in flight, awaiting red for a refusal, muted for a row that has settled cleanly.
   The panel sits under the tree and scrolls on its own, so a long drop of files cannot
   push the tree off screen. */
.upload-panel:empty { display: none; }
/* flex: none + its own scroll means the panel keeps its height instead of being squeezed
   to the header by the tree above it. MEASURED: with the tree on `flex: 1 1 auto` and the
   panel left to default, the UPLOADS header rendered at the very bottom of the column and
   every row sat below the fold — the states were live and invisible, which is the same as
   not having them. */
.upload-panel { flex: 0 0 auto; border-top: 1px solid var(--border);
  max-height: 34vh; overflow-y: auto; background: var(--bg-elev); }
.up-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 10px; font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--faint); }
.up-clear { background: transparent; border: 1px solid var(--border); color: var(--muted);
  border-radius: 5px; font-size: 11px; padding: 2px 7px; cursor: pointer; }
.up-clear:hover { color: var(--fg-2); border-color: var(--border-strong); }
.up-list { list-style: none; margin: 0; padding: 0 0 6px; }
/* The NAME goes on its own line above the state, rather than sharing one.
   MEASURED: with `1fr auto` and a non-wrapping state, a sentence like "Sending to the
   computer that holds this project…" claimed the whole row and the filename ellipsed
   down to a single letter — a row that tells you a state without telling you WHICH FILE
   is in it is not a per-file row at all, which is the entire point of WEBUI-308. */
.up-row { display: grid; grid-template-columns: 1fr; gap: 1px; padding: 5px 10px; font-size: 12px; }
.up-name { color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  direction: rtl; text-align: left; }
.up-state { color: var(--muted); font-size: 11px; line-height: 1.35; }
/* The refusal's own words wrap onto their own line: a reason worth showing is worth
   showing in full, and truncating it leaves a file nobody can fix. */
.up-detail { grid-column: 1 / -1; color: var(--state-awaiting-text); font-size: 11px; line-height: 1.4; }
.up-queued .up-state, .up-reading .up-state { color: var(--faint); }
.up-sending .up-state { color: var(--state-running); }
.up-done .up-name { color: var(--muted); }
.up-done .up-state { color: var(--state-running); }
.up-rejected .up-name { color: var(--fg-2); }
.up-rejected .up-state { color: var(--state-awaiting-text); }

/* JW-20 — the run reference on a user bubble.
   Muted and monospaced: it is a value to quote back, not something to read as prose. It sits
   beside the timestamp in the same foot, so it never competes with the message itself. */
.msg-runref {
  margin-left: .5em;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: .85em;
  color: var(--muted);
  user-select: all;      /* one click-drag selects the whole reference to paste into a question */
}

/* ── J-PERM: the permissions admin screen (controllers/permissions.js) ──────────────────────
   A reading screen, not a cockpit: one column, generous measure, no live panes. It uses the
   shared tokens so it inherits the theme rather than pinning its own colours. */
.perm-view { display: block; min-height: 0; overflow: auto; background: var(--bg); padding: 28px 32px 48px; }
.perm-head { max-width: 900px; }
.perm-title { font-size: 20px; font-weight: 600; color: var(--fg-strong); margin: 0 0 6px; }
.perm-sub { font-size: 12.5px; line-height: 1.6; color: var(--muted); margin: 0 0 22px; max-width: 760px; }
.perm-query { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; max-width: 900px; margin-bottom: 16px; }
.perm-field { display: flex; flex-direction: column; gap: 5px; font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .04em; }
.perm-field-wide { flex: 1 1 240px; }
.perm-field input, .perm-field select {
  background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: 6px;
  color: var(--fg); font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 7px 9px;
  text-transform: none; letter-spacing: 0;
}
.perm-field input:focus, .perm-field select:focus { outline: none; border-color: var(--accent); }
.perm-btn {
  background: var(--accent); color: #fff; border: 0; border-radius: 6px; cursor: pointer;
  font: 600 13px/1 ui-sans-serif, system-ui; padding: 9px 16px;
}
.perm-btn:hover { background: var(--accent-press); }
.perm-status { font-size: 12.5px; line-height: 1.55; color: var(--muted); margin: 0 0 12px; max-width: 760px; min-height: 18px; }
/* A refusal is the screen's most important message — it must not read as decoration. */
.perm-status-error { color: var(--state-awaiting-text); }
.perm-total { font-size: 12px; color: var(--faint); margin-bottom: 10px; }
.perm-total b { color: var(--fg); font-size: 13px; }
.perm-rows { display: flex; flex-direction: column; gap: 8px; max-width: 900px; }
.perm-row {
  display: flex; align-items: center; gap: 16px; justify-content: space-between;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px;
}
/* A revoked grant stays on screen (the audit record) but must never read as live. */
.perm-row-revoked { opacity: .55; }
.perm-row-main { min-width: 0; }
.perm-row-res { font: 12.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--fg); margin-bottom: 6px; word-break: break-all; }
.perm-row-keys { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.perm-key { font: 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--accent-wash); color: var(--fg); border-radius: 4px; padding: 4px 7px; }
.perm-row-meta { font-size: 11px; color: var(--faint); }
.perm-row-act { flex: 0 0 auto; }
.perm-revoke {
  background: transparent; border: 1px solid var(--state-awaiting); color: var(--state-awaiting-text);
  border-radius: 6px; cursor: pointer; font: 600 12px/1 ui-sans-serif, system-ui; padding: 8px 12px; white-space: nowrap;
}
.perm-revoke:hover { background: rgba(255, 107, 107, .12); }
.perm-revoked-tag { font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .05em; }
/* CẤP — the issuance half. Collapsed by default and visually quieter than the listing: the
   common errand on this screen is to read who holds what, and a form that shouts invites a
   grant nobody came here to make. */
.perm-issue { margin-top: 26px; border: 1px solid var(--border); border-radius: 8px; padding: 0 14px; }
.perm-issue[open] { padding-bottom: 14px; }
.perm-issue-sum { cursor: pointer; font: 600 13px/1 ui-sans-serif, system-ui; padding: 13px 0; color: var(--fg); }
.perm-issue-why { color: var(--faint); font-size: 12px; margin: 0 0 12px; max-width: 70ch; }
.perm-issue-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }
.perm-issue-check { display: flex; align-items: center; gap: 8px; margin: 14px 0 12px; font-size: 12px; color: var(--fg); }
@media (max-width: 720px) {
  .perm-view { padding: 20px 16px 40px; }
  .perm-row { flex-direction: column; align-items: stretch; }
  .perm-issue-grid { grid-template-columns: 1fr; }
}

/* ── TEAM DETAIL: the fifth routed screen (controllers/teamdetail.js) ───────────────────────
   Form follows design_upgrade/v2/Design nâng cấp Team monitor/Team Detail.dc.html
   (sha256[:12] 9a23a00712c3, DESIGN-MANIFEST row 15); behaviour follows
   requirement/WIREFRAME/syrix-team/team.md. Like the permissions screen it is a READING
   surface — one scroll, generous measure, no live panes — and it uses the shared tokens so it
   inherits the theme instead of pinning its own palette. The design's two-column layout
   (roster + devices on the left, readiness/permissions/history on the right) collapses to one
   column below 1100px, which is the breakpoint the artboard's own narrow note names. */
.team-view { display: block; min-height: 0; overflow: auto; background: var(--bg); padding: 24px 32px 48px; }

.td-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin-bottom: 4px; }
.td-back {
  background: transparent; border: 1px solid var(--border-strong); border-radius: 6px; color: var(--fg);
  cursor: pointer; font: 600 13px/1 ui-sans-serif, system-ui; padding: 9px 13px; flex: 0 0 auto;
}
.td-back:hover { background: var(--bg-elev); }
.td-title-wrap { flex: 1 1 320px; min-width: 0; }
.td-crumb { display: block; font-size: 11.5px; margin-bottom: 4px; }
.td-crumb a { color: var(--muted); text-decoration: none; }
.td-crumb a:hover { color: var(--accent); text-decoration: underline; }
.td-title { font-size: 20px; font-weight: 600; color: var(--fg-strong); margin: 0 0 8px; word-break: break-word; }
.td-chips { display: flex; flex-wrap: wrap; gap: 8px; }
/* Lifecycle (declared) and readiness (observed) are TWO chips and never merge — design 1a.
   An unknown readiness therefore wears its own quiet, non-green treatment: WEBUI-604 forbids
   an unobserved value from reading as ready, and colour is half of how a chip reads. */
.td-chip { font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; border-radius: 5px; padding: 5px 8px; letter-spacing: .03em; }
.td-chip-lifecycle { background: var(--accent-wash); color: var(--fg); border: 1px solid var(--border-strong); }
.td-chip-unknown { background: transparent; color: var(--faint); border: 1px dashed var(--border-strong); }
.td-actions { display: flex; flex-wrap: wrap; gap: 8px; flex: 0 0 auto; align-items: flex-start; }
.td-act {
  background: transparent; border: 1px solid var(--border-strong); border-radius: 6px; color: var(--fg);
  cursor: pointer; font: 600 12.5px/1 ui-sans-serif, system-ui; padding: 9px 13px; white-space: nowrap;
}
.td-act:hover { background: var(--bg-elev); }
.td-act-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.td-act-primary:hover { background: var(--accent-press); }
/* JE-22/FORCE — Delete team. Destructive, so it reads as destructive BEFORE the click:
   outlined in --red rather than filled, the same shape §1748 already uses for a
   dangerous-but-not-primary action. It is not --td-act-primary: the primary action on
   this screen is Edit, and a delete that looked primary would invite the click it must
   discourage. */
.td-act-danger { border-color: var(--red); color: var(--red); }
.td-act-danger:hover { background: var(--red); color: #fff; }
.td-url { font: 11.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--faint); margin: 2px 0 20px; word-break: break-all; }

.td-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; align-items: start; }
.td-col-main, .td-col-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

.td-block { background: var(--bg-elev); border: 1px solid var(--border); border-radius: 10px; padding: 16px 18px; }
.td-block-title {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 8px;
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
.td-count { font-size: 12px; color: var(--fg); background: var(--accent-wash); border-radius: 4px; padding: 3px 7px; letter-spacing: 0; }
.td-block-note { font-size: 12px; line-height: 1.55; color: var(--faint); margin: 0 0 12px; max-width: 80ch; }

.td-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.td-table th {
  text-align: left; font: 600 10.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
  padding: 0 10px 8px 0; border-bottom: 1px solid var(--border);
}
.td-table td { padding: 10px 10px 10px 0; border-bottom: 1px solid var(--border); vertical-align: top; color: var(--fg); }
.td-table tr:last-child td { border-bottom: 0; }
.td-emp-name { display: block; font-weight: 600; color: var(--fg-strong); word-break: break-word; }
.td-emp-id { display: block; font: 10.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--faint); margin-top: 3px; word-break: break-all; }
.td-role {
  display: inline-block; margin-top: 4px; font: 700 8.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; color: var(--faint); border: 1px solid var(--border-strong); border-radius: 4px; padding: 3px 5px;
}
.td-role-lead { color: var(--accent); border-color: var(--accent); background: var(--accent-wash); }
.td-obs { display: block; }
/* Freshness is the half of an observation that makes it evidence rather than a claim, so it
   is always drawn — as `unknown` when the wire carries no timestamp, never omitted. */
.td-fresh { display: block; font-size: 11px; color: var(--faint); margin-top: 3px; }
/* Readiness (TA R-ONB): a third fact beside liveness and membership. */
.td-readiness { display: block; font-size: 11px; margin-top: 3px; color: var(--faint); }
.td-readiness-ready { color: rgb(70, 209, 158); }
.td-readiness-blocked { color: rgb(232, 178, 74); }
/* QC21 B1: the tag has its own line under the head and WRAPS — it never shrinks the name. */
.roster-readiness-line { display: block; margin-top: 2px; min-width: 0; }
.roster-readiness { font-family: "Geist Mono", monospace; font-size: 9px; font-weight: 600;
  color: var(--faint); white-space: normal; overflow-wrap: anywhere; }
.roster-readiness.rd-blocked { color: rgb(232, 178, 74); }
/* bug 11: an attached VM sits UNDER the host the pane runs on, never in place of it */
.td-attached-vm { display: block; font-size: 11px; color: var(--muted, inherit); margin-top: 3px; }
.td-muted { color: var(--faint); }
.td-empty { padding: 18px 0 6px; }
.td-empty p { margin: 0 0 6px; font-size: 13px; color: var(--fg); }

/* NOT MEASURED. Deliberately NOT styled as an error: nothing failed — this deployment has no
   producer for the figure. It is also deliberately not invisible, because a block that is
   simply absent reads as "this team has none of that", which is a claim nobody measured. */
.td-unmeasured { border-style: dashed; background: transparent; }
.td-badge {
  font: 600 9.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .08em;
  border-radius: 4px; padding: 4px 6px;
}
.td-badge-unknown { color: var(--faint); border: 1px dashed var(--border-strong); }
.td-unmeasured-reason { margin: 0 0 8px; font-size: 11.5px; color: var(--muted); }
.td-unmeasured-reason code, .td-unmeasured-door code {
  font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--accent-wash);
  border-radius: 3px; padding: 2px 5px; word-break: break-all;
}
.td-unmeasured-detail { margin: 0 0 8px; font-size: 12px; line-height: 1.6; color: var(--faint); max-width: 70ch; }
.td-unmeasured-door { margin: 0; font-size: 11.5px; color: var(--faint); }

/* The four route outcomes (wireframe 0a.3/0a.4). They share a shape so none of them reads as
   a half-drawn screen, and they differ in WORDS because they send a person to different places. */
.td-state { max-width: 640px; padding: 40px 0 0; }
.td-state-title { font-size: 19px; font-weight: 600; color: var(--fg-strong); margin: 0 0 10px; }
.td-state-body { font-size: 13px; line-height: 1.65; color: var(--muted); margin: 0 0 16px; }
.td-state .td-actions { margin-top: 16px; }
.td-skel { height: 14px; border-radius: 4px; background: var(--bg-elev); margin: 10px 0; max-width: 420px; }
@media (prefers-reduced-motion: no-preference) {
  .td-skel { animation: td-pulse 1.4s ease-in-out infinite; }
}
@keyframes td-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }

@media (max-width: 1100px) {
  /* The artboard's own narrow rule: the right column stacks UNDER the left, order unchanged. */
  .td-grid { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  .team-view { padding: 18px 16px 40px; }
  .td-actions { width: 100%; }
  .td-table, .td-table thead, .td-table tbody, .td-table tr, .td-table th, .td-table td { display: block; }
  .td-table thead { display: none; }
  .td-table tr { border-bottom: 1px solid var(--border); padding: 10px 0; }
  .td-table td { border-bottom: 0; padding: 3px 0; }
}

/* The team NAME is entry point 1 to the Team screen (nav.js `data-team-open`). It must LOOK
   clickable — an affordance that only appears on hover is a door nobody finds — while staying
   distinct from the surrounding collapse-toggle, which is a different action on the same row. */
/* PO 18-09: "bỏ cái gạch chân dưới tên team đi". Gạch chấm là cách cũ để nói "bấm được" hồi tên
   team còn là chữ xám lẫn vào cột; nay nó là tiêu đề hoa trắng đậm nên tự nổi, và gạch chân chỉ
   làm bẩn dòng. Tính bấm được vẫn nói bằng con trỏ và bằng màu accent khi rê chuột. */
.roster-team-link { cursor: pointer; text-decoration: none; }
.roster-team-link:hover { color: var(--accent); }
.roster-team-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* ───────── [32]/[33] WAITING FOR APPROVAL (home.md:244-245) ─────────
   The approver's pane on Home. The FIRST render of this shipped with no rules at
   all and the screenshot showed why that is not a cosmetic matter: the badge butted
   against the title ("Waiting for approval1") and the row read
   "luat@xperc.comasks formacos-jv8b" — three separate facts (who, what, which
   machine) run into one string. A person scanning a queue to decide who gets into
   a machine was being handed a word-jumble, so the spacing below IS the
   readability the pane exists for, not decoration. */
.approvals-pane { margin: 18px 0 8px; }
/* Home: the pane and the Request-more sheet share the screen's 24px gutter (projects-head,
   daemons row, #stat-strip, the cards) — flush at 0 the heading sat left of the cards and
   "View all in Audit & History" touched the right edge. */
#projects-view > .approvals-pane, #projects-view > .requestmore-host { margin-left: 24px; margin-right: 24px; }
.approvals-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; }
.approvals-title { font-size: 15px; font-weight: 600; margin: 0; display: flex; align-items: center; gap: 8px; }
/* The badge is a COUNT beside a title, not part of the sentence — it needs to be
   separable at a glance, which is why it carries its own pill rather than sitting
   flush against the last letter of the heading. */
.approvals-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--accent, #2f81f7); color: #fff; font-size: 12px; font-weight: 600; }
.approvals-tabs { display: flex; gap: 4px; }
.approvals-grow { flex: 1; }
/* design (Team Home): "View all in Audit & History" — the whole queue on the Audit Pending tab. */
.approvals-viewall { height: 26px; display: inline-flex; align-items: center; gap: 5px; padding: 0 11px; border-radius: 7px;
  border: 1px solid rgba(255,255,255,.12); background: transparent; color: #c4c8ce; font: 600 12px var(--font-ui, system-ui); cursor: pointer; white-space: nowrap; }
.approvals-viewall:hover { background: rgba(255,255,255,.05); color: var(--fg, #e6edf3); }
.approvals-viewall:focus-visible { outline: 2px solid var(--accent, #2f81f7); outline-offset: 2px; }
.approvals-tab { padding: 4px 12px; border-radius: 6px; border: 1px solid transparent; background: transparent; color: var(--muted, #8b949e); font-size: 13px; cursor: pointer; }
.approvals-tab:hover { color: var(--fg, #e6edf3); }
.approvals-tab.is-active { background: var(--panel-2, #21262d); color: var(--fg, #e6edf3); border-color: var(--border, #30363d); }
.approvals-tab:focus-visible { outline: 2px solid var(--accent, #2f81f7); outline-offset: 2px; }
.approvals-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.approval-row { border: 1px solid var(--border, #30363d); border-radius: 8px; padding: 10px 12px; background: var(--panel, #161b22); min-width: 0; max-width: 100%; box-sizing: border-box; overflow-wrap: anywhere; }
/* The gap is what separates "who" from "asks for" from WHICH MACHINE. Without it
   the three render as one word and the approver cannot read their own queue. */
.approval-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.approval-who { font-weight: 600; }
.approval-verb { color: var(--muted, #8b949e); }
.approval-machine { font-family: var(--mono, ui-monospace, monospace); }
/* QC 955: a request row names the permission and the resource in words, not as codes. */
.approval-machine.approval-plain { font-family: inherit; font-weight: 600; }
/* The SCOPE is rendered as a chip because the two scopes differ enormously in
   reach — whole_machine inherits every VM on the host — and an approver skimming
   must not mistake it for part of the machine's name. */
.approval-scope { font-size: 12px; padding: 1px 8px; border-radius: 10px; border: 1px solid var(--border, #30363d); color: var(--muted, #8b949e); }
.approval-reason { margin-top: 6px; color: var(--muted, #8b949e); font-size: 13px; }
/* QC 973: a For-you row from the pending-requests door is one button. Unstyled it was the browser's
   grey, centred, inline button, so at 390 the kind badge and the wrapped "who" line drew over each
   other. Laid out like .approval-main: badge and who on one line (wrapping cleanly), what below. */
.approval-pending-open { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; width: 100%; min-width: 0;
  padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.approval-pending-open .approval-who { min-width: 0; overflow-wrap: anywhere; }
.approval-pending-open .approval-reason { flex-basis: 100%; margin-top: 0; }
.approval-actions { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; max-width: 100%; }
/* The reason box takes the room left and shrinks first, so Approve / Deny stay inside the card
   at any width (TA fullscreen review 2026-10-03: the old grade select pushed them off a 1440 screen). */
.approval-actions .approval-deny-reason { flex: 1 1 200px; min-width: 0; max-width: 100%; box-sizing: border-box; }
.approval-actions .approval-approve, .approval-actions .approval-deny { flex: none; }
/* QC 971 #3: on a phone the reason box shares its row with Approve / Deny and its placeholder was cut
   ("Reason for denying (shown to the a…"): there it takes a row of its own, the buttons go under it. */
@media (max-width: 720px) { .approval-actions .approval-deny-reason { flex-basis: 100%; } }
/* APPLIES WHEN (delegated asks only) as full-width radios, DURATION as a row of buttons —
   design v2.3 3b/3c; the same look as Audit & History · Pending. */
.approval-applies, .approval-durations { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.approval-terms-l { flex-basis: 100%; font: 600 9.5px ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .09em; text-transform: uppercase; color: #6e7681; }
.approval-terms-note { text-transform: none; letter-spacing: 0; font: 400 10.5px var(--font-ui); color: #6e7681; }
.approval-terms-req { text-transform: none; letter-spacing: 0; font: 500 10.5px var(--font-ui); color: #e0b341; }
.approval-terms-hint { flex-basis: 100%; color: var(--muted, #8b949e); font-size: 11.5px; }
.approval-term-opt { height: 30px; padding: 0 10px; border-radius: 7px; border: 1px solid rgba(255,255,255,.12); background: transparent; color: #c4c8ce; font: 500 12px var(--font-ui); cursor: pointer; }
.approval-condition-opt { display: flex; align-items: center; gap: 8px; width: 100%; height: auto; min-height: 34px; text-align: left; color: #8b949e; }
.approval-condition-opt b { font-weight: 500; color: #e6e8ec; }
.approval-radio-o { flex: none; width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid #5a6069; }
.approval-term-opt:hover { background: #111318; }
.approval-term-opt.is-on { border-color: rgba(55,148,255,.6); background: rgba(55,148,255,.14); color: #3794ff; }
.approval-condition-opt.is-on .approval-radio-o { border-color: #3794ff; box-shadow: inset 0 0 0 2.5px #0e1014; background: #3794ff; }
.approval-term-opt:disabled { opacity: .45; cursor: not-allowed; }
.approval-routing { margin-top: 10px; color: var(--muted, #8b949e); font: 400 11px ui-monospace, SFMono-Regular, Menlo, monospace; }
.approval-actions .approval-approve:disabled { opacity: .5; cursor: not-allowed; }
.approval-state { margin-top: 6px; font-size: 12px; color: var(--muted, #8b949e); }
.approvals-empty { color: var(--muted, #8b949e); font-size: 13px; margin: 4px 0 0; }
.approvals-error { color: var(--danger, #f85149); font-size: 13px; display: flex; align-items: center; gap: 10px; }
/* Two grey bars while loading, matching home.md:244's "2 hàng skeleton". They are
   aria-hidden: a screen reader announcing two empty rows would be reading out
   furniture, and the real count follows a moment later. */
.approvals-skeleton > div { height: 34px; border-radius: 8px; background: var(--panel-2, #21262d); margin-bottom: 8px; opacity: .55; }

.disc-send-status { padding: 0 16px 6px; font-size: 12px; color: var(--muted); }
.disc-send-status[data-state="error"] { color: var(--warn, #e0b341); }

.cli-bar[data-ack="uncertain"] .cli-ack, .cli-bar[data-ack="unconfirmed"] .cli-ack { color: var(--warn, #e0b341); }
.cli-bar[data-ack="turnQueued"] .cli-ack { color: var(--accent); }

/* design 4b — hàng meta trên composer + chọn phạm vi + pill danh tính */
.disc-foot-meta { display: flex; align-items: center; gap: var(--s-2); font: 400 11px 'Geist Mono', ui-monospace, monospace; color: #6e7681; }
.disc-foot-count b { color: #3794ff; font-weight: 500; }
.disc-scope { display: inline-flex; border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; overflow: hidden; }
.disc-scope-btn { font: inherit; font-size: 11px; padding: 3px 10px; background: transparent; border: 0; color: #8b909a; cursor: pointer; }
.disc-scope-btn.on { background: rgba(55,148,255,0.16); color: #3794ff; }
.disc-scope-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.disc-as .cli-model-dot { background: #46d19e; }
.disc-day { text-align: center; font: 400 11px 'Geist Mono', ui-monospace, monospace; color: #6e7681; margin: 6px 0 10px; }

/* Guest-token revoke: a destructive control, so it reads as one — quiet until hovered,
   then the same red the delete affordances use. */
.disc-set-revoke {
  margin-left: 8px; padding: 1px 7px; border-radius: 5px; cursor: pointer;
  font: 500 10px var(--font-ui, inherit); letter-spacing: .02em;
  color: #8b909a; background: transparent; border: 1px solid #3a3f4a;
}
.disc-set-revoke:hover { color: #ff6b6b; border-color: #ff6b6b; }
.disc-set-revoke[disabled] { opacity: .45; cursor: default; }

/* The brand shield is a LINK to the install page, so it reads as one on hover and
   takes a visible focus ring for keyboard users. */
a.rail-brand { display: flex; align-items: center; justify-content: center; text-decoration: none; }
a.rail-brand:hover { color: var(--accent, #3794ff); }
a.rail-brand:focus-visible { outline: 2px solid var(--accent, #3794ff); outline-offset: 2px; border-radius: 6px; }

/* "+ task" sits beside Send: the same words, a second outcome (a row on the board).
   Quiet until hovered, because Send stays the primary action of the composer. */
.disc-task {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  padding: 4px 9px; border-radius: 7px; cursor: pointer;
  font: 500 11.5px var(--font-ui, inherit); letter-spacing: .01em;
  color: #8b909a; background: transparent; border: 1px solid #2a2f3a;
}
.disc-task:hover { color: #3fb950; border-color: #3fb950; }
.disc-task:focus-visible { outline: 2px solid #3794ff; outline-offset: 2px; }
.disc-task[disabled] { opacity: .45; cursor: default; }

/* ⤢ / ⊟ parked into the tab bar sit at its right end, on the same baseline as the tabs
   (PO 2026-09-18: they were floating on a line of their own above the strip, because
   .col3-tabrow collapses in Monitor while .col3-tabctl stayed inside it). */
[data-mode="monitor"] #mf-bar .col3-tabctl { display: inline-flex; margin-left: auto; align-self: stretch; }
[data-mode="monitor"] #mf-bar .col3-tabctl .col-ctl-btn { height: auto; align-self: stretch; }

/* ── AUDIT & HISTORY (controllers/audit.js) — design v2.3 `Team Audit.dc.html` 0a / 1a ────────
   PO 2026-10-02: "copy structure + CSS values" — every size and colour below is the design
   file's own value, not a token approximation. The one rule this stylesheet carries beyond
   the picture: the states stay DISTINCT. A 501/403/503 is coloured as a problem and an empty
   200 is not, so broken wiring never reads as a clean record. */
.audit-view { display: block; min-height: 0; overflow: auto; background: #08090c; padding: 24px; font-family: var(--font-ui); }
.audit-card { background: #0b0c0f; border: 1px solid rgba(255,255,255,.09); border-radius: 14px; overflow: hidden; }
.audit-grow { flex: 1 1 auto; }

/* header */
.audit-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px 12px; }
.audit-title { font: 600 18px/1.2 var(--font-ui); color: #f2f3f5; letter-spacing: -.01em; }
.audit-sub { font: 400 11px var(--font-mono); color: #6e7681; }
.audit-export { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 10px; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.1); background: transparent; color: #c4c8ce; font: 600 11.5px var(--font-ui); cursor: pointer; }
.audit-export[disabled] { cursor: not-allowed; opacity: .55; }
/* QC 947 (390px): the header wraps by rows instead of squeezing — the title stays on one
   line beside Export, the scope chip takes the next row, the event counter its own line. */
@media (max-width: 640px) {
  .audit-head { flex-wrap: wrap; row-gap: 8px; padding: 14px 16px 10px; }
  .audit-head .audit-title { white-space: nowrap; order: 0; }
  .audit-head .audit-grow { display: none; }
  .audit-head .audit-export { order: 1; margin-left: auto; }
  .audit-head #audit-ws-scope { order: 2; flex: 1 1 100%; min-width: 0; }
  .audit-head .audit-sub { order: 3; flex: 1 1 100%; white-space: normal; }
}

/* filter row */
.audit-filters { display: flex; align-items: center; gap: 6px; padding: 0 20px 10px; flex-wrap: wrap; }
.audit-search { display: flex; align-items: center; gap: 7px; height: 32px; padding: 0 11px; border-radius: 8px; background: #111318;
  border: 1px solid rgba(255,255,255,.1); flex: 1 1 160px; min-width: 160px; color: #5a6069; }
.audit-search input { flex: 1; min-width: 0; background: none; border: 0; outline: none; color: #e6e8ec; font: 400 12px var(--font-ui); }
.audit-search input::placeholder { color: #5a6069; }
.audit-search input[disabled] { cursor: not-allowed; }
.audit-fbtn { position: relative; display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 9px; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.1); background: transparent; color: #c4c8ce; font: 500 11.5px var(--font-ui); cursor: pointer; white-space: nowrap; }
.audit-fbtn:hover { border-color: rgba(255,255,255,.18); }
.audit-fb-k:empty { display: none; }
.audit-fb-v { color: #8b909a; }
.audit-fb-caret { color: #6e7681; font-size: 9px; }
.audit-fbtn-host { padding-right: 4px; }
.audit-fbtn-host .scope-host, .audit-fbtn-host .scope-wrap, .audit-fbtn-host .scope-pp { position: static; }
.audit-fbtn-host .scope-pill, .audit-fbtn-host .scope-pp-btn { height: 26px; min-height: 0; width: auto; max-width: 180px; padding: 0 4px; gap: 4px;
  background: transparent; border: 0; box-shadow: none; color: #8b909a; font: 500 11.5px var(--font-ui); }
.audit-fbtn-host .cp-sw-tile-wrap, .audit-fbtn-host .cp-sw-sub, .audit-fbtn-host .scope-kind, .audit-fbtn-host .scope-id-sub { display: none; }
.audit-fbtn-host .cp-sw-name, .audit-fbtn-host .scope-pp-name { font: 500 11.5px var(--font-ui); color: #8b909a; }
.audit-fbtn-host .cp-sw-chev, .audit-fbtn-host .scope-pp-btn svg { width: 11px; height: 11px; color: #6e7681; }
.audit-fbtn-host .scope-pop, .audit-fbtn-host .scope-pp-pop { top: calc(100% + 6px); left: 0; }
/* the popovers of Done by / Action / date (widgets/popchip.js); the right-most two open leftwards
   so they stay on screen at the end of the row */
.audit-fbtn .popchip-pop { top: calc(100% + 6px); z-index: 80; }
.audit-fbtn-action .popchip-pop, .audit-fbtn-date .popchip-pop { left: auto; right: 0; }
#audit-who[hidden] { display: none; }

/* applied-filter chips (1b AFTER APPLY) */
.audit-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 0 20px 10px; }
.audit-chips[hidden] { display: none; }
.audit-fchip { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 4px 0 9px; border-radius: 7px;
  background: rgba(55,148,255,.14); color: #3794ff; font: 500 11px var(--font-ui); }
.audit-fchip-x { border: 0; background: none; color: #3794ff; cursor: pointer; font-size: 10px; padding: 0 4px; }

/* tabs */
.audit-tabs { display: flex; align-items: center; gap: 6px; padding: 0 20px 14px; flex-wrap: wrap; }
.audit-tab { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 7px; border: 0; background: transparent;
  color: #8b909a; font: 500 11px var(--font-ui); cursor: pointer; white-space: nowrap; }
.audit-tab:hover { color: #c4c8ce; }
.audit-tab-on { font-weight: 600; background: rgba(55,148,255,.14); color: #3794ff; }
.audit-tab-pending { color: #e0b341; }
.audit-tab-pending.audit-tab-on { background: rgba(224,179,65,.13); color: #e0b341; }
.audit-tab-nosrc { color: #5a6069; }
.audit-tab:focus-visible { outline: 2px solid #3794ff; outline-offset: 1px; }
.audit-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 8px; background: #e0b341; color: #0b0c0f; font: 700 9.5px var(--font-mono); }
.audit-attn { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; color: #c4c8ce; font: 500 11px var(--font-ui); }
.audit-attn input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.audit-attn-track { position: relative; width: 26px; height: 15px; border-radius: 8px; background: rgba(255,255,255,.12); flex: none; }
.audit-attn-knob { position: absolute; top: 2px; left: 2px; width: 11px; height: 11px; border-radius: 50%; background: #8b909a; transition: left .12s; }
.audit-attn input:checked + .audit-attn-track { background: rgba(255,107,107,.35); }
.audit-attn input:checked + .audit-attn-track .audit-attn-knob { left: 13px; background: #ff9b9b; }
.audit-attn input:focus-visible + .audit-attn-track { outline: 2px solid #3794ff; outline-offset: 2px; }

/* table */
.audit-grid { display: grid; grid-template-columns: 72px 110px minmax(0,1fr) 190px 170px; gap: 0 14px; align-items: center; }
.audit-thead { padding: 9px 20px; border-top: 1px solid rgba(255,255,255,.07); border-bottom: 1px solid rgba(255,255,255,.07); background: #0e1014;
  font: 600 9.5px var(--font-mono); letter-spacing: .09em; color: #6e7681; text-transform: uppercase; }
.audit-day { padding: 8px 20px; background: #12151b; font: 600 10px var(--font-mono); letter-spacing: .08em; color: #8b909a; text-transform: uppercase; }
.audit-row { border-bottom: 1px solid rgba(255,255,255,.05); cursor: pointer; }
.audit-row > .audit-grid { padding: 10px 20px; }
.audit-row:hover { background: rgba(255,255,255,.03); }
.audit-row-attention { background: rgba(255,107,107,.04); }
.audit-row-pending { background: rgba(224,179,65,.07); }
.audit-row-open, .audit-row-open:hover { background: rgba(55,148,255,.08); }
.audit-row-pending.audit-row-open { background: rgba(224,179,65,.07); }
.audit-c-time { font: 400 10.5px var(--font-mono); color: #8b909a; white-space: nowrap; }
.audit-c-what, .audit-c-by { display: flex; align-items: center; gap: 7px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audit-c-what { font: 500 12px var(--font-ui); color: #e6e8ec; }
/* WHAT spends its width on WHO → ON WHAT → PERMISSION first: those keep their natural width and
   only the trailing "— reason" gives way (it ellipsizes), so a long reason no longer squeezes the
   name to "luat…" and the workspace to "x..". */
.audit-c-what > * { flex-shrink: 0; }
.audit-c-what > .audit-sec { flex: 0 1 auto; min-width: 3em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.audit-c-btns { display: flex; justify-content: flex-end; gap: 6px; }
.audit-arrow { flex: none; color: #5a6069; }
.audit-nm { font: 500 12px var(--font-ui); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; }
.audit-nm-unknown { color: #8b909a; font-style: italic; }
.audit-nm-sys { color: #8b909a; }
.audit-lvl { flex: none; font: 600 9px var(--font-mono); letter-spacing: .06em; color: #8b909a; text-transform: uppercase; }
.audit-sec { font: 400 11px var(--font-ui); color: #8b909a; overflow: hidden; text-overflow: ellipsis; }
.audit-c-what .scope-id-sub, .audit-c-by .scope-id-sub { margin-left: 0; }

/* pills + chips */
.audit-pill { display: inline-block; padding: 2px 7px; border-radius: 5px; font: 600 10px var(--font-mono); letter-spacing: .04em; white-space: nowrap; }
/* QC 952: a long action label ("ĐÃ PHÁT HÀNH VAI TRÒ") ran out of its 110px column under
   nowrap and sat on the kind label next to it. Inside the action cell the pill wraps instead. */
.audit-c-act { min-width: 0; }
.audit-c-act > .audit-pill { white-space: normal; max-width: 100%; overflow-wrap: anywhere; line-height: 1.35; }
.audit-pill-green { background: rgba(70,209,158,.13); color: #46d19e; }
.audit-pill-blue { background: rgba(55,148,255,.12); color: #7fb0f0; }
.audit-pill-red { background: rgba(255,107,107,.12); color: #ff9b9b; }
.audit-pill-yellow { background: rgba(224,179,65,.13); color: #e0b341; }
.audit-pill-grey { background: rgba(255,255,255,.07); color: #c4c8ce; }
/* PERMISSION chips: the same pills Permissions draws (#perm-view .pv-pill*), so a role, a key
   and a condition read the same on both screens. */
#audit-view .pv-pill { flex: none; display: inline-block; font: 600 10.5px/1.4 var(--font-mono, monospace); padding: 2px 8px; border-radius: 5px; white-space: nowrap; }
#audit-view .pv-pill-role { background: rgba(55,148,255,.14); color: #3794ff; }
#audit-view .pv-pill-key { background: rgba(255,255,255,.07); color: #c4c8ce; }
#audit-view .pv-pill-pending { background: rgba(224,179,65,.13); color: #e0b341; }
/* Revoke (design 1a): the row's action column, and "Revoke this grant" under GO TO. */
.audit-revoke { height: 26px; padding: 0 11px; border-radius: 7px; border: 1px solid rgba(255,107,107,.4); background: transparent;
  color: #ff9b9b; font: 600 11px var(--font-ui); cursor: pointer; white-space: nowrap; }
.audit-revoke:hover { background: rgba(255,107,107,.12); }
.audit-revoke[disabled] { opacity: .45; cursor: default; }
.audit-revoke-long { display: block; margin: 8px 13px; height: 28px; }
.audit-revoke-said { color: #46d19e; }
.audit-revoke-said-bad { color: #ff9b9b; }
.audit-open-chat { color: #7fb0f0; }

/* DONE BY */
.audit-av { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%;
  background: #2a3550; color: #9db8ff; font: 600 9px var(--font-ui); }
.audit-av-emp { background: rgba(197,134,192,.2); color: #c586c0; }
.audit-av-sys { background: rgba(255,255,255,.07); color: #8b909a; }
.audit-kind { flex: none; padding: 1px 4px; border-radius: 3px; font: 600 8px var(--font-mono); letter-spacing: .05em; text-transform: uppercase; }
.audit-kind-person { background: rgba(55,148,255,.15); color: #7fb0f0; }
.audit-kind-emp { background: rgba(197,134,192,.15); color: #c586c0; }
.audit-by2 { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.audit-by-sub { font: 400 10.5px var(--font-ui); color: #6e7681; overflow: hidden; text-overflow: ellipsis; }

/* expanded CHANGE · CONTEXT · GO TO */
.audit-x { display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: 12px; padding: 2px 20px 14px; cursor: default; }
.audit-xcard { border: 1px solid rgba(255,255,255,.09); border-radius: 10px; background: #0e1014; overflow: hidden; min-width: 0; }
.audit-xcard-h { padding: 7px 13px; background: #12151b; border-bottom: 1px solid rgba(255,255,255,.06);
  font: 600 9.5px var(--font-mono); letter-spacing: .09em; color: #6e7681; text-transform: uppercase; }
.audit-xcard-note { text-transform: none; letter-spacing: 0; font: 400 10.5px var(--font-ui); color: #6e7681; }
.audit-xcard-req { text-transform: none; letter-spacing: 0; font: 500 10.5px var(--font-ui); color: #e0b341; }
.audit-xcard-b { padding: 10px 13px; display: flex; flex-direction: column; gap: 6px; }
.audit-kv, .audit-chg { display: flex; align-items: center; gap: 8px; padding: 8px 13px; border-bottom: 1px solid rgba(255,255,255,.04); min-width: 0; }
.audit-kv:last-child, .audit-chg:last-child { border-bottom: 0; }
.audit-kv-k { flex: 0 0 64px; font: 400 11px var(--font-ui); color: #8b909a; }
.audit-kv-v { display: flex; align-items: center; gap: 6px; min-width: 0; font: 500 11.5px var(--font-ui); color: #e6e8ec; overflow-wrap: anywhere; }
.audit-chg-k { flex: 0 0 46px; font: 600 9.5px var(--font-mono); letter-spacing: .06em; }
.audit-chg-before { color: #5a6069; }
.audit-chg-after { color: #46d19e; }
.audit-go { display: block; width: 100%; text-align: left; padding: 8px 13px; border: 0; border-bottom: 1px solid rgba(255,255,255,.04);
  background: none; color: #3794ff; font: 600 11px var(--font-ui); cursor: pointer; }
.audit-go:last-child { border-bottom: 0; }
.audit-go:hover { background: rgba(55,148,255,.06); }
.audit-state .audit-go, .audit-detail-head .audit-go, .audit-chips .audit-go { display: inline; width: auto; padding: 0 4px; border: 0; }
.audit-undo { margin: 8px 13px; height: 28px; padding: 0 11px; border-radius: 7px; border: 1px solid rgba(255,107,107,.4); background: transparent;
  color: #ff9b9b; font: 600 11.5px var(--font-ui); cursor: pointer; }
.audit-undo:hover { background: rgba(255,107,107,.12); }
.audit-undo[disabled] { opacity: .45; cursor: default; }
.audit-norevoke { display: block; padding: 8px 13px; font: 400 11px var(--font-ui); color: #6e7681; line-height: 1.4; }
.audit-row-outcome { padding: 0 13px; font: 400 11.5px var(--font-ui); color: #c4c8ce; }
.audit-row-outcome:empty { display: none; }
.audit-outcome-bad { color: #ff9b9b; }

/* pending: APPLIES WHEN · DURATION · WHY */
.audit-px { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.2fr) 260px; gap: 12px; padding: 2px 20px 14px 216px; cursor: default; }
.audit-px-self { grid-template-columns: minmax(0,1.2fr) 260px; }
.audit-radio { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 7px 10px; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.11); background: transparent; color: #8b909a; font: 400 11px var(--font-ui); cursor: pointer; }
.audit-radio b { font-weight: 500; color: #e6e8ec; }
.audit-radio-o { flex: none; width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid #5a6069; }
.audit-radio.is-on { border-color: rgba(55,148,255,.5); background: rgba(55,148,255,.08); }
.audit-radio.is-on .audit-radio-o { border-color: #3794ff; box-shadow: inset 0 0 0 2.5px #0e1014; background: #3794ff; }
.audit-radio[disabled] { opacity: .45; cursor: not-allowed; }
.audit-durs { display: flex; flex-wrap: wrap; gap: 6px; }
.audit-dur { height: 28px; padding: 0 10px; border-radius: 7px; border: 1px solid rgba(255,255,255,.12); background: transparent;
  color: #c4c8ce; font: 500 11.5px var(--font-ui); cursor: pointer; }
.audit-dur.is-on { border-color: #3794ff; background: rgba(55,148,255,.14); color: #3794ff; }
.audit-dur[disabled] { opacity: .4; cursor: not-allowed; }
.audit-dur-note { font: 400 10.5px var(--font-ui); color: #6e7681; }
.audit-why { font: 400 11.5px/1.5 var(--font-ui); color: #c4c8ce; }
.audit-why-facts { font: 400 10.5px var(--font-mono); color: #6e7681; overflow-wrap: anywhere; }
.audit-why-plain { font-size: 12.5px; overflow-wrap: anywhere; margin-bottom: 4px; }
.audit-why-details > summary { cursor: pointer; font-size: 11px; color: #8b949e; }
.audit-why-details[open] > summary { margin-bottom: 4px; }
.audit-deny, .audit-approve { height: 28px; border-radius: 7px; font: 600 11.5px var(--font-ui); cursor: pointer; }
.audit-deny { padding: 0 11px; border: 1px solid rgba(255,107,107,.4); background: transparent; color: #ff9b9b; }
.audit-approve { padding: 0 12px; border: 0; background: #3794ff; color: #fff; }
.audit-deny[disabled], .audit-approve[disabled] { opacity: .45; cursor: not-allowed; }

/* states, notes, footer */
.audit-notes:empty { display: none; }
.audit-note { padding: 6px 20px; font: 400 11px var(--font-ui); color: #6e7681; border-bottom: 1px solid rgba(255,255,255,.04); }
.audit-state { padding: 14px 20px; font: 400 12px/1.55 var(--font-ui); border-bottom: 1px solid rgba(255,255,255,.05); }
.audit-state-idle, .audit-state-loading, .audit-state-empty { color: #8b909a; }
.audit-state-refused, .audit-state-unwired, .audit-state-failed { color: #ff9b9b; background: rgba(255,107,107,.04); }
.audit-state-unavailable { color: #e0b341; background: rgba(224,179,65,.05); }
.audit-foot { display: flex; align-items: center; gap: 12px; padding: 10px 20px; font: 400 10.5px var(--font-mono); color: #6e7681; }
.audit-foot b { color: #c4c8ce; font-weight: 600; }
.audit-more { border: 0; background: none; color: #3794ff; font: 600 10.5px var(--font-mono); cursor: pointer; padding: 0; }

/* detail drawer: (b) one grant's chronicle / (d) one ticket's audit */
.audit-detail { margin-top: 16px; background: #0b0c0f; border: 1px solid rgba(255,255,255,.09); border-radius: 14px; padding: 16px 20px; }
.audit-detail[hidden] { display: none; }
.audit-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.audit-detail-title { font: 600 15px var(--font-ui); color: #f2f3f5; margin: 0; }
.audit-detail-h { font: 600 9.5px var(--font-mono); letter-spacing: .09em; color: #6e7681; margin: 18px 0 8px; text-transform: uppercase; }
.audit-detail-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font: 400 12px/1.7 var(--font-ui); color: #8b909a; }
.audit-detail-line b { color: #e6e8ec; font-weight: 500; }
.audit-redaction { color: #6e7681; font-style: italic; }
.audit-trail { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.audit-trail li { font: 400 11.5px/1.5 var(--font-ui); color: #c4c8ce; border-left: 2px solid rgba(255,255,255,.09); padding-left: 10px; }
.audit-trail-when { font-family: var(--font-mono); color: #6e7681; }
.audit-subrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; border: 1px solid rgba(255,255,255,.07); border-radius: 8px;
  padding: 8px 12px; margin-bottom: 8px; font: 400 11.5px var(--font-ui); color: #8b909a; }
.audit-none { font: 400 12px var(--font-ui); color: #6e7681; margin: 0 0 4px; }

/* the rail's Audit badge (design 0a: yellow count of requests waiting for you) */
.rail-badge-warn { top: 3px; right: 2px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; background: #e0b341; color: #0b0c0f;
  font: 700 9.5px/16px var(--font-mono); }
.rail-badge[hidden] { display: none; }

@media (max-width: 900px) {
  .audit-view { padding: 16px; }
  .audit-grid { grid-template-columns: 52px 96px minmax(0,1fr); }
  .audit-thead > span:nth-child(n+4) { display: none; }
  .audit-c-by, .audit-c-x { grid-column: 3 / -1; }
  .audit-c-btns { justify-content: flex-start; }
  .audit-x, .audit-px, .audit-px-self { grid-template-columns: 1fr; padding-left: 20px; }
  .audit-c-what { white-space: normal; flex-wrap: wrap; }
}

/* ═══ RBAC v2.3 — the shared approval card (design 9a) + Request More (design 1b) ═══
   Lane D. The card markup is ONE renderer (widgets/approvalcard.js) used by Home and
   by the chat thread, so it gets ONE set of rules here rather than a Home copy and a
   Chat copy that drift. `.approval-row-elevation` already inherits the Home approval
   row's layout; these add only what the resource card carries beyond it. */
.approval-requester { font-size: 12px; opacity: .8; }
.approval-effect { display: flex; gap: 10px; flex-wrap: wrap; font-size: 12px; opacity: .85; }
.approval-digest { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
/* QC fxAY: the raw plan string lives behind Details; long plan/id text wraps instead of widening a 390px card. */
.approval-plan, .approval-digest { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); overflow-wrap: anywhere; }
.approval-risk { border: 1px solid currentColor; border-radius: 999px; padding: 0 6px; }
/* Risk drives EMPHASIS, never meaning on its own — the word is always printed beside
   it, so a colour-blind reader loses nothing (design 9a prints "risk: low"). */
.approval-risk[data-risk="high"],
.approval-risk[data-risk="destructive"],
.approval-risk[data-risk="credential"],
.approval-risk[data-risk="physical"] { color: var(--danger, #c0392b); font-weight: 600; }
/* A settled card keeps its place in the list but stops offering a decision
   (design 9a: "nút Deny/Approve disabled"). */
.approval-row-elevation[data-state="approved"],
.approval-row-elevation[data-state="denied"],
.approval-row-elevation[data-state="expired"],
.approval-row-elevation[data-state="revoked"] { opacity: .72; }

/* The chat half. It sits between the thread and the composer and stays compact: the
   conversation is the subject of the screen, the card is an interruption in it. */
.thread-approvals { padding: 6px 10px; border-top: 1px solid var(--border, #2a2f3a); }
.thread-approvals[hidden] { display: none; }
.thread-approvals-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }

/* design 1b — the Request More sheet. A <details> so the form is out of the way until
   wanted; the approvals pane above it is the screen's subject. */
.requestmore-host:empty { display: none; }
.rqm { margin: 8px 0; border: 1px solid var(--border, #2a2f3a); border-radius: 8px; padding: 6px 10px; }
.rqm-summary { cursor: pointer; font-weight: 600; }
.rqm-form { display: grid; gap: 8px; margin-top: 8px; }
.rqm-field { display: grid; gap: 3px; font-size: 12px; }
.rqm-field > span { opacity: .8; }
.rqm-field input, .rqm-field select, .rqm-field textarea {
  width: 100%; box-sizing: border-box; padding: 5px 7px;
  border: 1px solid var(--border, #2a2f3a); border-radius: 6px;
  background: var(--bg-input, transparent); color: inherit; font: inherit;
}
.rqm-note { margin: 0; font-size: 11px; opacity: .7; }
.rqm-actions { display: flex; gap: 8px; }
/* The three outcomes are visually distinct because they are DIFFERENT ANSWERS, not
   grades of failure: `already_allowed` is good news (the key is held), the 422s are
   the plane saying a ticket is not the fix, and only a real fault is an error. */
.rqm-result { display: grid; gap: 2px; padding: 6px 8px; border-radius: 6px; font-size: 12px;
  border: 1px solid currentColor; }
.rqm-result[data-tone="ok"] { color: var(--ok, #2e7d32); }
.rqm-result[data-tone="warn"] { color: var(--warn, #b26a00); }
.rqm-result[data-tone="err"] { color: var(--danger, #c0392b); }
.rqm-detail { opacity: .9; font-weight: 400; }

/* ═══ RBAC Roles screen (design 3a/2a/2b) + permission key picker (6a) ═══════════════════
   Appended rather than woven into the .perm-* block above: these are the Roles screen's own
   classes, and the two screens are edited by different lanes. The shared controls (.perm-btn,
   .perm-field, .perm-status) are REUSED deliberately — a second button style for the same
   affordance is how two screens in one product stop looking like one product. */
.perm-btn-ghost { background: transparent; color: var(--fg); border: 1px solid var(--border-strong); }
.perm-btn-ghost:hover { background: var(--bg-elev); }
.perm-btn-danger { background: transparent; color: var(--state-awaiting-text); border: 1px solid var(--border-strong); }
.perm-btn:disabled, .perm-btn[disabled] { opacity: .45; cursor: not-allowed; }
.perm-btn:disabled:hover, .perm-btn[disabled]:hover { background: transparent; }

/* ── Roles (design v2.3 "Team RBAC" 2–2g) ─────────────────────────────────────────────────
   One master/detail screen: header row, the role list (280px) on the left, the selected
   role's keys on the right, the unsaved footer under them. "N permission keys ›" swaps the
   two columns for the read-only 2g matrix under the same header. */
.roles-view { display: flex; flex-direction: column; min-height: 0; overflow: hidden; padding: 0; background: #0b0c0f; font-family: var(--font-ui); }
.roles-view[hidden] { display: none; }
.roles-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 14px 22px; border-bottom: 1px solid rgba(255,255,255,.07); }
.roles-title { display: flex; align-items: center; gap: 8px; font: 600 18px var(--font-ui); color: var(--fg-strong); margin: 0; }
.roles-back { background: transparent; border: 0; color: #8b909a; font: 500 16px/1 var(--font-ui); cursor: pointer; padding: 0 2px; }
.roles-back:hover { color: var(--fg-strong); }
.roles-count { font: 400 11px var(--font-mono); color: #6e7681; }
.roles-head-dot { color: #3a3f47; }
.roles-keys-link { background: transparent; border: 0; padding: 0; color: #3794ff; font: 500 11.5px var(--font-ui); cursor: pointer; }
.roles-keys-link:hover { text-decoration: underline; }
.roles-head [hidden] { display: none; }
.roles-head-spacer { flex: 1; }
.roles-search { flex: 0 0 260px; min-width: 160px; }
.roles-btn { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 8px; border: 1px solid rgba(255,255,255,.14); background: transparent; color: #e6e8ec; font: 500 12px var(--font-ui); cursor: pointer; white-space: nowrap; }
.roles-btn:hover { background: rgba(255,255,255,.04); }
.roles-btn:disabled, .roles-btn[disabled] { opacity: .45; cursor: not-allowed; }
.roles-btn-primary { background: #3794ff; border-color: #3794ff; color: #fff; font-weight: 600; }
.roles-btn-primary:hover { background: #4aa0ff; }
.roles-btn-primary:disabled:hover { background: #3794ff; }
.roles-btn-danger { border-color: rgba(255,107,107,.4); color: #ff9b9b; }
.roles-btn-danger:hover { background: rgba(255,107,107,.08); }
.roles-btn-delete { background: #e5484d; border-color: #e5484d; color: #fff; font-weight: 600; }
.roles-btn-delete:disabled { opacity: 1; background: rgba(255,107,107,.15); border-color: transparent; color: rgba(255,155,155,.5); }
.roles-link { background: transparent; border: 0; padding: 0; color: #3794ff; font: inherit; cursor: pointer; }
.roles-link:hover { text-decoration: underline; }
.roles-view > .perm-status:empty { display: none; }
.roles-view > .perm-status { margin: 8px 22px 0; }

.roles-main { flex: 1; min-height: 0; display: grid; grid-template-columns: 280px minmax(0,1fr); }
.roles-main[hidden] { display: none; }
.roles-side { position: relative; z-index: 2; display: flex; flex-direction: column; min-height: 0; border-right: 1px solid rgba(255,255,255,.07); padding: 12px 10px; gap: 8px; overflow: visible; }
.roles-find { display: flex; align-items: center; gap: 7px; height: 32px; padding: 0 10px; border: 1px solid rgba(255,255,255,.1); border-radius: 8px; background: #0e1014; color: #6e7681; }
.roles-find input { flex: 1; min-width: 0; background: transparent; border: 0; outline: none; color: var(--fg-strong); font: 400 12px var(--font-ui); }
.roles-filters { display: flex; gap: 6px; flex-wrap: wrap; }
.roles-fchip .popchip-btn { height: 26px; }
.roles-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 2px; }
.roles-group { padding: 10px 8px 5px; font: 600 9.5px/1 var(--font-mono); letter-spacing: .1em; color: #6e7681; }
.roles-row { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; width: 100%; text-align: left; padding: 9px 10px; border-radius: 8px; border: 1px solid transparent; cursor: pointer; background: transparent; color: var(--fg); }
.roles-row:hover { background: rgba(255,255,255,.03); }
.roles-row-active, .roles-row-active:hover { background: rgba(55,148,255,.12); border-color: rgba(55,148,255,.4); }
.roles-row-name { font: 600 12.5px var(--font-mono); color: var(--fg-strong); }
.roles-row-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.roles-row-keys { font: 400 10.5px var(--font-mono); color: #6e7681; white-space: nowrap; }
.roles-row { min-width: 0; box-sizing: border-box; }
.roles-row-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roles-row-meta { max-width: 100%; min-width: 0; }
/* A long place name is cut with an ellipsis inside the row, never pushed past it (QC 0.10.913 bug 3). */
.roles-place { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; min-width: 0; overflow: hidden; box-sizing: border-box; padding: 2px 6px; border-radius: 4px; background: rgba(255,255,255,.05); color: #c4c8ce; font: 500 10.5px var(--font-ui); }
.roles-place-k { font: 700 8.5px var(--font-mono); letter-spacing: .06em; color: #8b909a; }
.roles-place-n { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roles-place-k { flex: none; }

.roles-detail { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.roles-dhead { display: flex; align-items: flex-start; gap: 8px; padding: 16px 22px 14px; border-bottom: 1px solid rgba(255,255,255,.07); }
.roles-dhead-main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.roles-dname-row, .roles-ddesc-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.roles-dname { font: 600 17px var(--font-mono); color: var(--fg-strong); }
.roles-dname-input { font: 600 16px var(--font-mono); color: var(--fg-strong); background: #0b0c0f; border: 1px solid rgba(55,148,255,.5); border-radius: 6px; padding: 3px 8px; outline: none; }
.roles-ddesc { font: 400 12.5px/1.5 var(--font-ui); color: #c4c8ce; }
.roles-ddesc-empty { color: #6e7681; font-style: italic; }
.roles-ddesc-input { width: 460px; max-width: 100%; font: 400 12.5px/1.5 var(--font-ui); color: var(--fg-strong); background: #0b0c0f; border: 1px solid rgba(55,148,255,.5); border-radius: 6px; padding: 5px 8px; outline: none; resize: vertical; }
.roles-pencil { display: inline-flex; background: transparent; border: 0; padding: 2px; color: #6e7681; cursor: pointer; }
.roles-pencil:hover { color: var(--fg-strong); }
/* super_admin: permissions come from xcorp, the Studio shows it read-only */
.roles-locked-badge { display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 0 8px; border-radius: 10px; background: rgba(210,153,34,.12); border: 1px solid rgba(210,153,34,.4); color: #d29922; font: 600 10.5px var(--font-ui); }
.roles-locked-badge svg { width: 12px; height: 12px; }
.roles-applies-chip[disabled] { cursor: default; opacity: .7; }
.roles-applies-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.roles-label { font: 600 9.5px var(--font-mono); letter-spacing: .1em; color: #6e7681; }
.roles-applies-wrap, .roles-pick-wrap { position: relative; display: inline-flex; }
.roles-applies-chip { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 9px; border-radius: 6px; background: rgba(55,148,255,.1); border: 1px solid rgba(55,148,255,.35); color: #e6e8ec; font: 500 11.5px var(--font-ui); cursor: pointer; }
.roles-applies-chip .roles-place-k { color: #3794ff; }
.roles-applies-sub { color: #8b909a; }
.roles-applies-moved { background: rgba(224,179,65,.12); border-color: rgba(224,179,65,.5); }
.roles-applies-pop { top: 32px; left: 0; }
.roles-dmeta { font: 400 11.5px var(--font-ui); color: #8b909a; }
.roles-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 12px 22px; }
.roles-kfind { width: 280px; height: 30px; }
.roles-seg { display: inline-flex; padding: 2px; border-radius: 8px; background: #0e1014; border: 1px solid rgba(255,255,255,.08); }
.roles-seg button { border: 0; background: transparent; color: #8b909a; font: 500 11.5px var(--font-ui); padding: 4px 10px; border-radius: 6px; cursor: pointer; }
.roles-seg button.is-on { background: rgba(55,148,255,.14); color: #3794ff; font-weight: 600; }
.roles-hint { font: 400 11px var(--font-ui); color: #6e7681; }
.roles-cards { flex: 1; min-height: 0; overflow: auto; padding: 0 22px 16px; display: flex; flex-direction: column; gap: 10px; }
.roles-card { border: 1px solid rgba(255,255,255,.08); border-radius: 10px; background: #0e1014; overflow: hidden; flex: none; }
.roles-card-head { display: flex; align-items: center; gap: 9px; padding: 10px 14px; cursor: pointer; user-select: none; }
.roles-card-head.is-open { border-bottom: 1px solid rgba(255,255,255,.06); }
.roles-card-caret { width: 10px; color: #8b909a; font-size: 10px; }
.roles-card-name { font: 700 11px var(--font-mono); letter-spacing: .08em; color: #f2f3f5; }
.roles-card-n { font: 600 10.5px var(--font-mono); color: #3794ff; }
.roles-card-n.is-zero { color: #6e7681; }
.roles-card-add { font: 600 10.5px var(--font-mono); color: #e0b341; }
.roles-selall { background: transparent; border: 0; color: #3794ff; font: 500 11px var(--font-ui); cursor: pointer; padding: 0; }
.roles-krow { display: grid; grid-template-columns: 15px 200px minmax(0,1fr) auto; gap: 0 12px; align-items: center; padding: 7px 14px; cursor: pointer; }
.roles-krow:hover { background: rgba(255,255,255,.025); }
.roles-krow.is-off { opacity: .45; cursor: not-allowed; }
.roles-krow.is-off .roles-box { border-color: #3a3f47; }
.roles-box { width: 15px; height: 15px; box-sizing: border-box; border-radius: 4px; border: 1.5px solid #5a6069; display: inline-flex; align-items: center; justify-content: center; font: 700 10px/1 var(--font-ui); color: #fff; }
.roles-box-on { background: #3794ff; border-color: #3794ff; }
.roles-box-add { background: #e0b341; border-color: #e0b341; color: #0b0c0f; }
.roles-box-del { border-color: #e0b341; border-style: dashed; }
.roles-kname { font: 500 11.5px var(--font-mono); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roles-kdesc { font: 400 11.5px var(--font-ui); color: #8b909a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roles-ktail { display: inline-flex; align-items: center; gap: 8px; }
.roles-high { font: 700 8.5px var(--font-mono); letter-spacing: .06em; padding: 2px 5px; border-radius: 3px; background: rgba(255,107,107,.13); color: #ff9b9b; }
.roles-amber { font: 500 10px var(--font-mono); color: #e0b341; }
.roles-more { display: block; width: 100%; text-align: left; background: transparent; border: 0; padding: 7px 14px 9px 41px; color: #3794ff; font: 500 11px var(--font-ui); cursor: pointer; }
.roles-dstatus:empty { display: none; }
.roles-dstatus { margin: 0 22px 8px; }
.roles-foot { display: flex; align-items: center; gap: 10px; padding: 12px 22px; border-top: 1px solid rgba(255,255,255,.07); background: #0e1014; }
.roles-foot-dot { width: 8px; height: 8px; border-radius: 50%; background: #e0b341; flex: none; }
.roles-foot-text { font: 400 12px var(--font-ui); color: #c4c8ce; }
.roles-foot-text.is-clean { color: #6e7681; }
.roles-warn { display: flex; gap: 8px; align-items: flex-start; margin: 0; padding: 10px 12px; border-top: 1px solid rgba(255,255,255,.07); background: rgba(0,0,0,.18); color: #c4c8ce; font: 400 11.5px/1.5 var(--font-ui); }
.roles-warn-ico { flex: none; color: #e0b341; display: inline-flex; padding-top: 1px; }
.roles-unlimit { padding: 9px 12px 10px; border-top: 1px solid rgba(255,255,255,.07); font: 400 11.5px var(--font-ui); color: #8b909a; }

/* 2g: the matrix under the same header */
.roles-keysview { flex: 1; min-height: 0; overflow: auto; padding-top: 4px; }
.roles-keysview[hidden] { display: none; }
.roles-matrix { display: flex; flex-direction: column; }
.roles-matrix > .roles-note { margin: 12px 22px; }
.roles-mx-row { display: grid; gap: 0 14px; align-items: center; padding: 7px 22px; border-bottom: 1px solid rgba(255,255,255,.04); }
.roles-mx-row:hover { background: rgba(255,255,255,.02); }
.roles-mx-head { padding: 9px 22px; background: #0e1014;
  font: 600 9.5px var(--font-mono); letter-spacing: .09em; color: #6e7681; text-transform: uppercase; }
.roles-mx-col { flex-direction: column; text-align: center; overflow: hidden; text-overflow: ellipsis; }
.roles-mx-n { display: block; color: #3794ff; }
.roles-mx-fam { padding: 9px 22px; background: #12151b; border-top: 1px solid rgba(255,255,255,.07); border-bottom: 1px solid rgba(255,255,255,.06); }
.roles-mx-famname { display: flex; align-items: center; gap: 8px; font: 700 11px var(--font-mono); letter-spacing: .08em; color: #f2f3f5; white-space: nowrap; }
.roles-mx-famdesc { font: 500 11.5px var(--font-ui); color: #c4c8ce; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roles-mx-famn { font: 600 9.5px var(--font-mono); padding: 1px 6px; border-radius: 4px; background: rgba(255,255,255,.07); color: #8b909a; letter-spacing: 0; }
.roles-mx-frac { font: 500 10.5px var(--font-mono); color: #8b909a; }
.roles-mx-key { display: flex; align-items: center; gap: 7px; min-width: 0; font: 500 11.5px var(--font-mono); color: #e6e8ec; white-space: nowrap; }
.roles-mx-desc { font: 400 11.5px var(--font-ui); color: #8b909a; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.roles-mx-cell { display: flex; justify-content: center; align-items: center; }
.roles-mx-dot { width: 8px; height: 8px; border-radius: 50%; background: #3794ff; display: inline-block; }
.roles-mx-no { color: #3a3f47; font: 400 12px var(--font-mono); }
.roles-mx-legend { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; padding: 12px 22px; font: 400 11px var(--font-ui); color: #6e7681; }

.roles-note { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 12.5px; line-height: 1.55; color: var(--muted); padding: 12px 14px; border: 1px solid var(--border); border-radius: 9px; }
.roles-note-detail { color: var(--faint); font-size: 11.5px; }
.roles-note-refused { border-color: rgba(255, 155, 155, .35); color: var(--state-awaiting-text); }
.roles-note-unavailable { border-color: var(--border-strong); }

/* 2b / 2c dialogs */
.roles-modal-host:empty { display: none; }
.roles-backdrop { position: fixed; inset: 0; background: rgba(5,6,8,.6); z-index: 80; }
.roles-dialog { position: fixed; z-index: 81; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 460px; max-width: calc(100vw - 32px); max-height: calc(100vh - 48px); display: flex; flex-direction: column; background: #111318; border: 1px solid rgba(255,255,255,.14); border-radius: 14px; box-shadow: 0 24px 60px rgba(0,0,0,.5); }
.roles-dialog-danger { width: 440px; border-color: rgba(255,107,107,.35); }
.roles-dialog-head { display: flex; align-items: center; gap: 10px; padding: 16px 18px 12px; }
.roles-dialog-ico { display: inline-flex; color: #3794ff; }
.roles-ico-red { color: #ff9b9b; }
.roles-dialog-title { font: 600 15px var(--font-ui); color: var(--fg-strong); }
.roles-dialog .perm-status:empty { display: none; }
.roles-dialog-body { display: flex; flex-direction: column; gap: 14px; padding: 4px 18px 16px; overflow: auto; }
.roles-dialog-text { font: 400 12.5px/1.55 var(--font-ui); color: #c4c8ce; }
.roles-red { color: #ff9b9b; font-weight: 600; }
.roles-dialog-foot { display: flex; align-items: center; gap: 8px; padding: 12px 18px; border-top: 1px solid rgba(255,255,255,.08); }
.roles-field { display: flex; flex-direction: column; gap: 6px; }
.roles-opt { font-weight: 400; letter-spacing: 0; color: #5a6069; text-transform: none; }
.roles-input { width: 100%; box-sizing: border-box; height: 36px; padding: 0 11px; background: #0b0c0f; border: 1px solid rgba(255,255,255,.12); border-radius: 8px; color: var(--fg-strong); font: 400 12.5px var(--font-ui); outline: none; }
.roles-input:focus { border-color: rgba(55,148,255,.6); }
.roles-input-mono { font-family: var(--font-mono); }
.roles-textarea { height: auto; min-height: 60px; padding: 8px 11px; resize: vertical; line-height: 1.5; }
.roles-pick { display: flex; align-items: center; gap: 6px; text-align: left; cursor: pointer; }
.roles-pick-wrap { width: 100%; }
.roles-pick-wrap .popchip-pop { top: 40px; left: 0; }
.roles-field-hint { font: 400 11px var(--font-ui); color: #6e7681; }
.roles-start { display: flex; gap: 8px; }
.roles-start .roles-pick-wrap { width: auto; flex: 1; }
.roles-start-opt { flex: 1; width: 100%; height: 34px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: #0b0c0f; color: #c4c8ce; font: 500 12px var(--font-ui); cursor: pointer; }
.roles-start-opt.is-on { border-color: #3794ff; background: rgba(55,148,255,.12); color: #3794ff; font-weight: 600; }
.roles-uses { display: flex; flex-direction: column; border-radius: 8px; background: #0b0c0f; border: 1px solid rgba(255,255,255,.08); }
.roles-use { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 9px 12px; border-bottom: 1px solid rgba(255,255,255,.05); font: 500 11.5px var(--font-mono); color: #e6e8ec; }
.roles-use:last-child { border-bottom: 0; }
.roles-use-line { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.roles-use-who { font: 600 12px var(--font-ui); color: #e6e8ec; }
.roles-use-arrow { color: #8b93a1; }
.roles-use-place { background: none; padding: 0; font: 600 12px var(--font-ui); color: #e6e8ec; }
.roles-use .roles-link { font: 500 11.5px var(--font-ui); }
.roles-use-pick { flex-basis: 100%; display: flex; gap: 6px; flex-wrap: wrap; padding-top: 4px; }
.roles-use-opt { border: 1px solid rgba(55,148,255,.35); background: rgba(55,148,255,.08); color: #e6e8ec; border-radius: 6px; padding: 3px 8px; font: 500 11px var(--font-mono); cursor: pointer; }
.roles-x { background: transparent; border: 0; color: var(--faint); cursor: pointer; font-size: 13px; line-height: 1; padding: 2px 4px; }
.roles-x:hover { color: var(--fg); }

@media (max-width: 760px) {
  .roles-main { grid-template-columns: 1fr; }
  .roles-side { border-right: 0; border-bottom: 1px solid rgba(255,255,255,.07); max-height: 40vh; }
  .roles-krow { grid-template-columns: 15px minmax(0,1fr) auto; }
  .roles-kdesc { display: none; }
  .roles-kfind { width: 100%; }
}

/* ── 6a permission key picker ─────────────────────────────────────────────────────────── */
.pp { border: 1px solid var(--border-strong); border-radius: 11px; overflow: hidden; background: var(--bg-elev); }
.pp-bar { display: flex; align-items: center; gap: 7px; padding: 8px 11px; border-bottom: 1px solid var(--border); }
.pp-ico { font-size: 13px; color: var(--faint); }
.pp-input { flex: 1; min-width: 0; background: transparent; border: 0; outline: none; color: var(--fg-strong); font: 500 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
.pp-hint { font: 400 9.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--faint); white-space: nowrap; }
.pp-list { max-height: 260px; overflow: auto; padding: 5px; }
.pp-group { padding: 5px 8px 2px; font: 400 9px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .1em; color: var(--faint); text-transform: uppercase; }
.pp-row { display: flex; align-items: center; gap: 7px; padding: 5px 8px; border-radius: 6px; cursor: pointer; font: 400 11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--fg); }
.pp-row:hover { background: var(--bg-hover, rgba(255, 255, 255, .04)); }
/* The keyboard cursor is the SAME fill as the mouse-selected row: two different highlights
   for "the row Enter will take" is how a keyboard user loses track of where they are. */
.pp-row-active { background: var(--accent-wash); }
/* Already held: dimmed and not clickable — design 6a's "đã có ✓". It stays in the list so a
   key the user is looking for never appears to have vanished from the catalog. */
.pp-row-held { opacity: .5; cursor: not-allowed; }
.pp-key b { color: var(--accent); font-weight: 600; }
/* QC 954: plain name first (sans), the key small and grey beside it. */
.pp-name { font: 500 11.5px/1.35 ui-sans-serif, system-ui; color: var(--fg); min-width: 0; }
.pp-name b { color: var(--accent); font-weight: 600; }
.pp-code { font: 400 9.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--faint); flex: none; }
.pp-group { text-transform: none; letter-spacing: .02em; font-family: ui-sans-serif, system-ui; font-size: 10px; font-weight: 600; }
.pp-desc { margin-left: auto; font: 400 9.5px/1.3 ui-sans-serif, system-ui; color: var(--faint); text-align: right; }
.pp-risk { font: 500 9px/1 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 1px 5px; border-radius: 4px; background: rgba(255, 107, 107, .13); color: var(--state-awaiting-text); }
.pp-held { margin-left: auto; font: 400 9.5px/1 ui-sans-serif, system-ui; color: var(--faint); white-space: nowrap; }
.pp-note { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 12px 11px; font-size: 11.5px; line-height: 1.5; color: var(--muted); }
.pp-note-detail { color: var(--faint); font-size: 10.5px; }
.pp-note-refused { color: var(--state-awaiting-text); }
.pp-retry { background: transparent; border: 1px solid var(--border-strong); border-radius: 5px; color: var(--fg); cursor: pointer; font: 500 10.5px/1 ui-sans-serif, system-ui; padding: 4px 8px; }
/* State ④: an unknown key is refused IN PLACE, with the nearest real keys offered. */
.pp-blocked { padding: 12px 11px; display: flex; flex-direction: column; gap: 7px; }
.pp-blocked-head { display: flex; align-items: center; gap: 8px; font: 500 11.5px/1 ui-sans-serif, system-ui; color: var(--fg); }
.pp-blocked-ico { color: var(--state-awaiting-text); font-size: 13px; }
.pp-blocked-why { font: 400 10.5px/1.55 ui-sans-serif, system-ui; color: var(--faint); }
.pp-blocked-near { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font: 400 10.5px/1 ui-sans-serif, system-ui; color: var(--faint); }
.pp-near { background: transparent; border: 0; color: var(--accent); cursor: pointer; font: 400 10.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 2px 4px; }
.pp-near:hover { text-decoration: underline; }

/* ── J-PERM / RBAC v2.3 — lane C additions ─────────────────────────────────────────────
   The screen grew three things the old form did not have: a PAGER over the door's opaque
   cursor, a Flat/Grouped toggle (design 7a), and a resource PICKER that shows a readable
   name with the canonical id beneath it. The canonical id is monospaced and dimmer than the
   name deliberately: it is the thing an operator VERIFIES rather than reads, and styling it
   like prose invites skimming past the one string that decides which machine a grant lands
   on. */
.perm-listbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 10px 0 6px; }
.perm-listbar .perm-total { margin: 0; }
.perm-inert { color: #e0b341; font: 400 11px var(--font-mono, monospace); }

.perm-viewtog { display: inline-flex; gap: 6px; margin-left: auto; }
.perm-tog {
  height: 24px; padding: 0 10px; border-radius: 7px; cursor: pointer;
  border: 1px solid #2a2f3a; background: transparent; color: #8b909a;
  font: 500 11px var(--font-ui, inherit);
}
.perm-tog:hover { color: #c4c8ce; }
.perm-tog:focus-visible { outline: 2px solid #3794ff; outline-offset: 2px; }
.perm-tog-on { border-color: rgba(55, 148, 255, .4); background: rgba(55, 148, 255, .14); color: #3794ff; }

.perm-pager { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; }
.perm-pagelabel { color: #6e7681; font: 400 11px var(--font-mono, monospace); }
.perm-btn-quiet { background: transparent; border: 1px solid #2a2f3a; color: #c4c8ce; }
.perm-btn-quiet:hover:not([disabled]) { border-color: #3794ff; color: #3794ff; }
/* A spent direction is disabled rather than hidden: a pager whose buttons come and go moves
   the other button under the cursor mid-click. */
.perm-btn-quiet[disabled] { opacity: .4; cursor: default; }

/* 7a grouped view. The group header carries the principal × resource pair and the EFFECTIVE
   union badge; every tuple stays its own row underneath with its own Revoke, because the
   store does not merge tuples and a UI that merged them would take the per-tuple revoke
   away. */
.perm-group { border: 1px solid rgba(255, 255, 255, .09); border-radius: 12px; overflow: hidden; margin-bottom: 8px; }
.perm-group-head {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; cursor: pointer;
  background: rgba(55, 148, 255, .05); border-bottom: 1px solid rgba(255, 255, 255, .05);
}
.perm-group-head:focus-visible { outline: 2px solid #3794ff; outline-offset: -2px; }
.perm-group-caret { color: #6e7681; font-size: 10px; }
.perm-group-who { color: #e6e8ec; font: 500 12.5px var(--font-ui, inherit); white-space: nowrap; }
.perm-group-res {
  color: #c4c8ce; font: 400 11px var(--font-mono, monospace);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1;
}
.perm-group-eff {
  padding: 2px 8px; border-radius: 6px; white-space: nowrap;
  background: rgba(70, 209, 158, .12); color: #46d19e;
  font: 500 10.5px var(--font-mono, monospace);
}
.perm-group-body { display: flex; flex-direction: column; }
.perm-subrow {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 14px 8px 34px; border-bottom: 1px solid rgba(255, 255, 255, .04);
}
.perm-subrow:last-child { border-bottom: 0; }
.perm-subrow-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }

/* The resource picker. `perm-canon` is the canonical id RBT-06 asks to be shown in details.
   `align-items: start` matters: without it the flex children STRETCH to the box's height and
   the <select> grew to fill the row, opening a ~200px gap between the control and the
   canonical id under it — measured in the browser at 1440px before this line. The picker is
   also width-capped so a select holding one long canonical id does not span the whole sheet
   and drag the detail line away from the control it belongs to. */
.perm-pickbox { display: flex; flex-direction: column; align-items: start; gap: 6px; margin: 8px 0; max-width: 470px; }
.perm-pickbox > label, .perm-pickbox > .perm-field { width: 100%; }
.perm-pickbox select, .perm-pickbox input { width: 100%; }
.perm-pick-note { color: #6e7681; font: 400 11px var(--font-ui, inherit); line-height: 1.6; }
.perm-pick-warn { color: #e0b341; }
.perm-pick-ok { color: #46d19e; }
.perm-pick-detail { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.perm-canon {
  padding: 2px 7px; border-radius: 5px; background: rgba(255, 255, 255, .06);
  color: #c4c8ce; font: 400 10.5px var(--font-mono, monospace); word-break: break-all;
}
.perm-canon-sub { color: #6e7681; font: 400 10.5px var(--font-mono, monospace); }

/* 5a preview — the command restated before it is spent. */
.perm-preview {
  margin: 8px 0; padding: 10px 12px; border-radius: 9px;
  background: #0b0c0f; border: 1px solid rgba(255, 255, 255, .07);
}
.perm-preview-warn { border-color: rgba(224, 179, 65, .35); color: #e0b341; }
.perm-preview-title {
  color: #6e7681; letter-spacing: .1em; margin-bottom: 6px;
  font: 600 10px var(--font-mono, monospace); text-transform: uppercase;
}
.perm-preview-line { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 3px 0; color: #c4c8ce; font: 400 11.5px var(--font-ui, inherit); }
.perm-preview-line > span:first-child { color: #6e7681; min-width: 118px; font: 400 10.5px var(--font-mono, monospace); }

.perm-issue-acts { display: flex; align-items: center; gap: 8px; }
/* A mutation in flight disables its own control, so a double click is one command. */
.perm-revoke[disabled], .perm-btn[disabled] { opacity: .45; cursor: default; }

/* Empty state for the machines popover: a heading over blank space told the reader nothing
   (PO 2026-09-21). Same muted treatment as the heading, with room to read a sentence. */
.cli-vms-empty {
  font-size: 11px;
  line-height: 1.45;
  color: var(--muted);
  padding: 6px 8px 8px;
  max-width: 260px;
}

/* ───────── ST-1457 — host-pairing onboarding card (controllers/onboarding.js) ─────────
   ONE card rendered on two surfaces (Team roster column · Chat column) from ONE plan client.
   States follow design v2.3 "Release 1 States Prototype" #onboarding: steps row, pending,
   delivery_unknown with an explicit Regenerate, typed refusal text. */
.onb-host-el[hidden] { display: none; }
.onb-host-el { flex: 0 0 auto; min-height: 0; overflow-y: auto; }
#onboarding-team { order: 3; padding: var(--s-1) var(--s-2); }
[data-mode="monitor"] #onboarding-team { order: 2; }
[data-mode="work"] #onboarding-team { display: none; }
/* The Team rail carries ONE compact status line while a pairing is under way — never the form. */
.onb-link { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-xs); color: var(--muted); padding: 4px 6px; border: 1px solid var(--border); border-radius: var(--r-sm); }
.onb-link-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The full card: opened on purpose from the Machines screen ("Pair machine"), and only there. */
#onboarding-machines { position: fixed; z-index: 60; top: 64px; right: 16px; width: min(460px, calc(100vw - 32px)); max-height: calc(100vh - 96px); box-shadow: 0 12px 32px rgba(0, 0, 0, .45); border-radius: var(--r-md); }
body:not([data-screen="vms"]) #onboarding-machines { display: none; }
.onb-close { margin-left: var(--s-2); }
.onb-card { background: var(--panel-2); border: 1px solid var(--border-strong); border-radius: var(--r-md); padding: var(--s-3); font-size: var(--t-sm); color: var(--fg-2); display: grid; gap: var(--s-2); }
.onb-head { display: flex; align-items: center; gap: var(--s-2); }
.onb-title { font-weight: 600; color: var(--fg); font-size: var(--t-base); }
.onb-surface { margin-left: auto; font: 500 10px var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--faint); }
.onb-empty, .onb-plan, .onb-next { margin: 0; }
.onb-plan code { font-family: var(--font-mono); color: var(--fg); }
.onb-next { color: var(--fg); }
.onb-choice { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: flex-end; }
.onb-choice label { display: grid; gap: 2px; color: var(--muted); font-size: var(--t-xs); }
.onb-choice select { background: var(--bg-elev); color: var(--fg); border: 1px solid var(--border-strong); border-radius: var(--r-sm); padding: 4px 6px; font-size: var(--t-sm); }
.onb-status { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: baseline; }
.onb-status-val.ok, .onb-host.ok { color: var(--green); }
.onb-status-val.warn, .onb-host.warn { color: var(--yellow); }
.onb-status-val.bad { color: var(--red); }
.onb-progress { margin-left: auto; font-family: var(--font-mono); color: var(--faint); }
.onb-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 10px; }
.onb-step { color: var(--disabled); display: inline-flex; gap: 4px; }
.onb-step.done { color: var(--green); }
.onb-step.current { color: var(--red); }
.onb-cmd { display: grid; gap: 4px; }
.onb-cmd-head { display: flex; align-items: center; gap: var(--s-2); justify-content: space-between; color: var(--fg); }
.onb-cmd-text { width: 100%; box-sizing: border-box; resize: vertical; background: var(--code-bg); color: var(--fg); border: 1px solid var(--border-strong); border-radius: var(--r-sm); font: 12px/1.4 var(--font-mono); padding: 6px; word-break: break-all; }
.onb-cmd-note { margin: 0; color: var(--faint); font-size: var(--t-xs); }
.onb-notice { margin: 0; padding: 6px 8px; border-radius: var(--r-sm); background: var(--code-bg); }
.onb-notice.bad { color: var(--state-awaiting-text); }
.onb-notice.warn { color: var(--yellow); }
.onb-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.onb-actions:empty { display: none; }
.onb-loading { color: var(--muted); }

/* New Project picker: the › is its own button (opens the folder); the row selects it. */
button.np-pick-chevron { background: none; border: none; color: inherit; cursor: pointer; padding: 4px 8px; border-radius: 6px; font: inherit; }
button.np-pick-chevron:hover { background: rgba(255,255,255,.08); }
button.np-pick-added { background: none; border: none; color: var(--green, #46d19e); cursor: pointer; font: inherit; padding: 2px 6px; }

/* AM-SDGT Employee → Settings → Harness (controllers/harness.js). Tokens only. */
.ee-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-top: -4px; }
.ee-tab { font-size: 12px; font-weight: 600; color: var(--muted); padding: 7px 12px; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.ee-tab:hover { color: var(--fg-2); }
.ee-tab.on { color: var(--accent); border-bottom-color: var(--accent); }
.ee-box { transition: width .12s ease; }
.hz { display: flex; flex-direction: column; gap: 12px; font-size: 13px; color: var(--fg); min-width: 0; }
.hz-head { display: flex; align-items: flex-start; gap: 10px; }
.hz-head-main { flex: 1 1 0%; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.hz-active { font-size: 13px; font-weight: 600; color: var(--fg); }
.hz-sub, .hz-note, .hz-faint { font-size: 11px; color: var(--faint); line-height: 1.5; }
.hz-note { padding: 12px 0; }
.hz-label { font-size: 10px; font-family: var(--font-mono); letter-spacing: 0.14em; color: var(--faint); }
.hz-badge { flex: 0 0 auto; font-size: 10px; font-family: var(--font-mono); padding: 3px 8px; border-radius: 999px; border: 1px solid var(--border-strong); color: var(--muted); white-space: nowrap; cursor: default; }
.hz-badge-pending { color: var(--yellow); border-color: rgba(224, 179, 65, 0.4); background: rgba(224, 179, 65, 0.08); }
.hz-badge-loaded { color: var(--green); border-color: rgba(70, 209, 158, 0.35); background: rgba(70, 209, 158, 0.08); }
.hz-badge-failed { color: var(--red); border-color: rgba(255, 107, 107, 0.35); background: rgba(255, 107, 107, 0.08); }
.hz-badge-unsupported { color: var(--muted); border-style: dashed; }
.hz-preview { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 8px 12px; max-height: 420px; overflow: auto; font-size: 13px; color: var(--fg-2); }
.hz-empty { font-size: 11px; color: var(--faint); padding: 6px 0; }
.hz-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hz-btn { font-size: 12px; font-weight: 600; color: var(--fg-2); padding: 7px 14px; border: 1px solid var(--border-strong); border-radius: var(--r-md); cursor: pointer; white-space: nowrap; }
.hz-btn:hover { border-color: rgba(255, 255, 255, 0.24); }
.hz-primary { color: #fff; background: var(--accent); border-color: var(--accent); }
.hz-primary:hover { background: var(--accent-press); }
.hz-primary.busy { opacity: .6; cursor: progress; }
.hz-btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; pointer-events: none; }
.hz-dirty { font-size: 11px; font-family: var(--font-mono); color: var(--yellow); }
.hz-err { font-size: 12px; color: var(--red); }
.hz-ok { font-size: 12px; color: var(--green); }
.hz-conflict { border: 1px solid rgba(224, 179, 65, 0.45); background: rgba(224, 179, 65, 0.06); border-radius: var(--r-md); padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.hz-conflict-title { font-size: 13px; font-weight: 600; color: var(--yellow); }
.hz-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.hz-col { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 8px 10px; max-height: 320px; overflow: auto; min-width: 0; }
.hz-col-h { font-size: 10px; font-family: var(--font-mono); letter-spacing: 0.1em; color: var(--muted); margin-bottom: 6px; }
.hz-col .hz-preview { min-height: 0; max-height: 300px; margin: 4px 0 8px; }
.hz-history { display: flex; flex-direction: column; gap: 6px; border-top: 1px solid var(--border); padding-top: 10px; }
.hz-hrow { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--r-sm); background: var(--panel); border: 1px solid var(--border); }
.hz-hrev { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--fg); min-width: 34px; }
.hz-hmeta { font-size: 11px; color: var(--fg-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.hz-link { font-size: 11px; color: var(--link); cursor: pointer; }
.hz-tag { font-size: 10px; font-family: var(--font-mono); color: var(--green); border: 1px solid rgba(70, 209, 158, 0.35); border-radius: 999px; padding: 2px 8px; }
.hz-viewing { margin: -2px 0 4px 44px; }
.hz-sys { display: flex; flex-direction: column; gap: 6px; padding: 12px; border: 1px solid var(--border); border-radius: var(--r-md); background: var(--bg-elev); }
.hz-sys .hz-col-h { margin: 6px 0 0; }
.hz-sys-row { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--fg-2); min-width: 0; }
.hz-sys-name { color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-sys-st { font-size: 10px; font-family: var(--font-mono); color: var(--muted); border: 1px solid var(--border-strong); border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
/* TA #196: only a runtime read-back (observed) may look usable. */
.hz-sys-st-observed { color: var(--green); border-color: rgba(70, 209, 158, 0.35); }
.hz-sys-st-unknown { color: var(--muted); border-style: dashed; }
.hz-sys-st-not_assigned { color: var(--yellow); border-color: rgba(224, 179, 65, 0.45); border-style: dashed; }
.hz-sys-row-not_assigned .hz-sys-name { color: var(--muted); text-decoration: line-through; }
.hz-sys-warn { font-size: 11px; color: var(--yellow); margin: -2px 0 4px; }
.hz-user { display: flex; flex-direction: column; gap: 12px; }
.hz-slot { display: flex; flex-direction: column; gap: 6px; }
.hz-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
.hz-input { width: 100%; box-sizing: border-box; background: var(--bg-elev); color: var(--fg); border: 1px solid var(--border-strong); border-radius: var(--r-sm); padding: 8px 10px; font-size: 13px; }
.hz-input:focus { outline: none; border-color: var(--accent); }
.hz-body { min-height: 320px; max-height: 420px; resize: vertical; font-family: var(--font-mono); font-size: 12px; line-height: 1.5; }
.hz-bytes { font-size: 11px; font-family: var(--font-mono); color: var(--faint); text-align: right; }
.hz-bytes.over { color: var(--red); }
.hz-deferred { font-size: 11px; color: var(--disabled); font-style: italic; }
@media (max-width: 760px) {
  .hz-cols, .hz-split { grid-template-columns: minmax(0, 1fr); }
}

/* Send with nothing typed and no highlighted choice: flag the field instead of a silent no-op. */
.askq-other-input.need-text { border-color: var(--red) !important; box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 30%, transparent); }

/* ── RBAC scope pickers (controllers/scope.js — PO 2026-10-01: every existing value is PICKED
   and shown by name, never typed as an id). The pill + popover reuse the Home switcher's
   .cp-sw-* / .cp-pop-* look; only the anchoring differs: the Home popover is placed by JS with
   position:fixed, here it hangs under its own pill. */
.scope-host { position: relative; min-width: 0; text-transform: none; letter-spacing: 0; }
.scope-wrap { position: relative; min-width: 0; }
.scope-wrap .scope-pill { width: 100%; max-width: 460px; }
.scope-wrap .scope-pop { position: absolute; top: calc(100% + 6px); left: 0; z-index: 70; }
.scope-ws-row { display: flex; align-items: center; gap: 8px; padding: 6px 8px; }
.scope-ws-label { font: 600 10px var(--font-mono, monospace); color: var(--faint); text-transform: uppercase; letter-spacing: .05em; flex: none; }
.scope-select {
  flex: 1; min-width: 0; background: #16181d; border: 1px solid rgba(255,255,255,.1); border-radius: 8px;
  color: #c4c8ce; font: 12px/1.3 inherit; padding: 6px 8px; cursor: pointer;
}
.scope-select:focus { outline: none; border-color: var(--accent); }
.cp-pop-proj { width: 100%; display: flex; align-items: center; gap: 8px; padding: 6px 8px; border: 0; border-radius: 8px; background: transparent; cursor: pointer; font-family: inherit; text-align: left; }

/* The principal dropdown. */
.scope-pp { position: relative; min-width: 0; }
.scope-pp-btn {
  width: 100%; max-width: 460px; display: flex; align-items: center; gap: 8px; min-height: 36px;
  background: var(--bg-elev); border: 1px solid var(--border-strong); border-radius: 8px;
  color: var(--fg); font: 13px/1.3 inherit; padding: 6px 10px; cursor: pointer; text-align: left;
}
.scope-pp-btn:hover, .scope-pp-btn.is-open { border-color: rgba(55,148,255,.4); }
.scope-pp-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.scope-pp-name { font-weight: 600; color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scope-pp-empty { font-weight: 400; color: var(--faint); }
.scope-pp-pop {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 70; width: min(420px, calc(100vw - 32px));
  background: #0e1014; border: 1px solid rgba(255,255,255,.14); border-radius: 12px;
  box-shadow: 0 20px 44px rgba(0,0,0,.6); padding: 8px; display: flex; flex-direction: column; gap: 6px;
}
.scope-pp-pop[hidden] { display: none; }
.scope-pp-search {
  background: #16181d; border: 1px solid rgba(255,255,255,.1); border-radius: 8px; color: var(--fg);
  font: 13px/1.3 inherit; padding: 7px 9px;
}
.scope-pp-search:focus { outline: none; border-color: var(--accent); }
.scope-pp-list { max-height: 300px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; }
.scope-pp-head { font: 600 10px var(--font-mono, monospace); color: var(--faint); text-transform: uppercase; letter-spacing: .05em; padding: 8px 8px 4px; display: flex; gap: 6px; }
.scope-pp-count { color: #5a6069; }
.scope-pp-row {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border: 0; border-radius: 8px;
  background: transparent; color: #c4c8ce; font: 13px/1.3 inherit; cursor: pointer; text-align: left;
}
.scope-pp-row:hover, .scope-pp-row.is-focus { background: rgba(255,255,255,.04); }
.scope-pp-row.is-active { background: var(--accent-wash); color: var(--fg); }
.scope-pp-rowname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scope-pp-note { margin: 0; padding: 8px; font-size: 12px; line-height: 1.5; color: var(--faint); }
.scope-pp-warn { color: #e5a54b; }
.scope-kind { flex: none; font: 600 9.5px var(--font-mono, monospace); text-transform: uppercase; letter-spacing: .04em; border-radius: 4px; padding: 2px 5px; }
.scope-kind-human { background: rgba(55,148,255,.14); color: #79b8ff; }
.scope-kind-employee { background: rgba(70,209,158,.14); color: #46d19e; }

/* "Name <grey id>" — the id is secondary, never the label. */
.scope-named { min-width: 0; }
.scope-id-sub { color: #6e7681; font: 400 10.5px var(--font-mono, monospace); margin-left: 4px; overflow-wrap: anywhere; }
.scope-id { color: #8b949e; font: 400 11px var(--font-mono, monospace); overflow-wrap: anywhere; }
.perm-res-kind { font: 600 10px var(--font-mono, monospace); color: var(--faint); text-transform: uppercase; letter-spacing: .04em; margin-right: 2px; }

/* The issue sheet: the workspace by name, and the chosen keys as removable chips. */
.perm-static { font-size: 13px; color: var(--fg); text-transform: none; letter-spacing: 0; padding: 7px 0; }
.perm-keys-field { margin-top: 10px; text-transform: none; }
.perm-keys-field > span { text-transform: uppercase; }
.perm-keychips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 22px; align-items: center; }
.perm-key-chip { display: inline-flex; align-items: center; gap: 4px; }
.perm-key-x { background: none; border: 0; color: var(--faint); cursor: pointer; font-size: 13px; line-height: 1; padding: 0 2px; }
.perm-key-x:hover { color: var(--fg); }
.perm-keypick { text-transform: none; letter-spacing: 0; }
.perm-scope-note:empty, .perm-filter-note[hidden] { display: none; }
.perm-filter-note { margin: 0 0 8px; }
.audit-projectnote { margin: 4px 0 8px; }

@media (max-width: 640px) {
  .scope-wrap .scope-pill, .scope-pp-btn { max-width: none; }
  .scope-wrap .scope-pop { width: calc(100vw - 32px); }
}

/* ── THE SCOPE CHIP (scope.js variant:'chip' — design 2026-10-02, the Credentials / Audit /
   Permissions headers): "[X] Xperc / Default ▾" beside the title, and its 340px list. */
.scope-chip-host { flex: none; min-width: 0; }
.scope-chip-wrap { position: relative; }
.scope-chip {
  height: 30px; display: flex; align-items: center; gap: 7px; padding: 0 10px 0 6px; border-radius: 8px;
  background: #111318; border: 1px solid rgba(255,255,255,.12); cursor: pointer; white-space: nowrap;
  font-family: var(--font-ui); max-width: 420px; min-width: 0;
}
.scope-chip:hover { border-color: rgba(255,255,255,.2); }
.scope-chip.is-open { border-color: rgba(55,148,255,.6); }
.scope-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.scope-chip-logo, .scope-dd-logo {
  width: 20px; height: 20px; border-radius: 6px; background: #3794ff; color: #fff; flex: none;
  font: 700 10px var(--font-ui); display: grid; place-items: center;
}
.scope-chip-logo.is-blank { background: #3a3f47; }
.scope-dd-logo.is-other { background: #3a3f47; }
.scope-chip-logo.is-allco, .scope-dd-logo.is-allco { background: rgba(255,255,255,.08); color: #c4c8ce; }
.scope-dd-ws.scope-dd-allco { padding-left: 10px; }
.scope-chip-co { font: 600 12px var(--font-ui); color: #e6e8ec; }
.scope-chip-sep { color: #5a6069; font-size: 12px; }
.scope-chip-ws { font: 500 12px var(--font-ui); color: #c4c8ce; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.scope-chip-caret { color: #6e7681; font-size: 9px; }
/* The mono count right after the chip ("9 credentials", "1,284 events · last 7 days"). */
.scope-chip-count { font: 400 11px var(--font-mono); color: #6e7681; white-space: nowrap; }
.scope-dd {
  position: absolute; top: 36px; left: 0; z-index: 75; width: 340px; max-width: calc(100vw - 32px);
  background: #16181d; border: 1px solid rgba(255,255,255,.14); border-radius: 10px;
  box-shadow: 0 18px 44px rgba(0,0,0,.55); overflow: hidden; text-align: left; cursor: default;
  font-family: var(--font-ui); text-transform: none; letter-spacing: 0;
}
.scope-dd[hidden] { display: none; }
.scope-dd-search { display: flex; align-items: center; gap: 7px; padding: 8px 11px; border-bottom: 1px solid rgba(255,255,255,.07); color: #6e7681; }
.scope-dd-search-i { flex: none; }
.scope-dd-q {
  flex: 1; min-width: 0; background: transparent; border: 0; outline: none; padding: 0;
  font: 400 12px var(--font-ui); color: #e6e8ec;
}
.scope-dd-q::placeholder { color: #5a6069; }
.scope-dd-q::-webkit-search-cancel-button { display: none; }
.scope-dd-why:empty { display: none; }
.scope-dd-why .cp-pop-empty { margin: 0; padding: 8px 11px; font-size: 11.5px; line-height: 1.5; color: #e0b341; }
.scope-dd-list { padding: 4px; display: flex; flex-direction: column; gap: 1px; max-height: 360px; overflow-y: auto; }
.scope-dd-row {
  display: flex; align-items: center; gap: 9px; width: 100%; padding: 8px 10px; border: 0; border-radius: 7px;
  background: transparent; text-align: left; font-family: var(--font-ui); color: #e6e8ec;
}
.scope-dd-co { cursor: default; }
.scope-dd-coname { font: 600 12px var(--font-ui); color: #e6e8ec; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scope-dd-ws { padding: 7px 10px 7px 34px; cursor: pointer; }
.scope-dd-ws:hover, .scope-dd-ws:focus-visible { background: rgba(255,255,255,.05); outline: none; }
.scope-dd-ws.is-on { background: rgba(55,148,255,.14); }
.scope-dd-ws.is-denied { opacity: .42; cursor: not-allowed; }
.scope-dd-name { font: 500 12px var(--font-ui); color: #e6e8ec; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scope-dd-ws.is-on .scope-dd-name { color: #f2f3f5; }
.scope-dd-fill { flex: 1; }
.scope-dd-n { font: 400 10px var(--font-mono); color: #6e7681; white-space: nowrap; }
.scope-dd-deny { font: 400 10px var(--font-mono); color: #e0b341; white-space: nowrap; }
.scope-dd-check { color: #3794ff; font-size: 12px; }
.scope-dd-div { height: 1px; background: rgba(255,255,255,.06); margin: 4px 6px; }
.scope-dd-empty { margin: 0; padding: 10px; font-size: 12px; color: #6e7681; }
.scope-dd-note { padding: 8px 11px; border-top: 1px solid rgba(255,255,255,.06); font: 400 10.5px/1.5 var(--font-ui); color: #6e7681; }

/* ── Permissions round 2 (PO 2026-10-02): the workspace-wide grant table (4a) and the
   stepwise "+ Cấp quyền" sheet (5a). Names are primary text; ids are grey .scope-id-sub. */
.perm-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; max-width: 1100px; }
.perm-btn-primary, .perm-grant-btn {
  flex: none; border: 0; border-radius: 6px; background: var(--accent); color: #fff;
  font: 600 13px/1 var(--font-ui, inherit); padding: 10px 16px; cursor: pointer;
}
.perm-btn-primary:hover, .perm-grant-btn:hover { background: var(--accent-press); }
.perm-btn-primary[disabled] { opacity: .5; cursor: default; }
.perm-table { max-width: 1100px; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.perm-thead, .perm-trow {
  display: grid; grid-template-columns: minmax(150px, 1.2fr) minmax(170px, 1.6fr) minmax(150px, 1.3fr) minmax(110px, .9fr) 88px;
  gap: 12px; align-items: center; padding: 10px 14px;
}
.perm-thead { background: var(--bg-elev); color: var(--faint); font: 600 10.5px var(--font-ui, inherit); text-transform: uppercase; letter-spacing: .05em; }
.perm-trow { border-top: 1px solid var(--border); font-size: 12.5px; color: var(--fg); }
.perm-trow.perm-row-revoked { opacity: .55; }
.perm-c-who, .perm-c-what, .perm-c-scope, .perm-c-by { min-width: 0; overflow-wrap: anywhere; }
.perm-c-who { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.perm-c-what { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.perm-c-by { color: var(--muted); font-size: 11.5px; }
.perm-c-act { text-align: right; }
.perm-meta-sub { color: var(--faint); font-size: 11px; }
.perm-empty { padding: 18px 14px; color: var(--muted); font-size: 12.5px; }
.perm-role-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--accent); border-radius: 999px; padding: 3px 9px; font-size: 11.5px; color: var(--fg); }
.perm-role-n { color: var(--faint); font-size: 11px; }
.scope-unnamed { color: var(--muted); font-style: italic; }

.perm-sheet-scrim { position: fixed; inset: 0; background: rgba(0, 0, 0, .45); z-index: 60; }
.perm-sheet-scrim[hidden], .perm-sheet[hidden] { display: none; }
.perm-sheet {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(560px, 100vw); z-index: 61;
  background: var(--panel); border-left: 1px solid var(--border-strong); overflow: auto; padding: 18px 22px 28px;
}
.perm-sheet-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.perm-sheet-title { font-size: 17px; font-weight: 600; color: var(--fg-strong); margin: 0; }
.perm-sheet-x { border: 0; background: none; color: var(--muted); font-size: 20px; line-height: 1; cursor: pointer; padding: 4px 8px; border-radius: 6px; }
.perm-sheet-x:hover { color: var(--fg); background: var(--accent-wash); }
.perm-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
.perm-step { border-top: 1px solid var(--border); padding-top: 12px; }
.perm-step:first-child { border-top: 0; padding-top: 0; }
.perm-step-t { font: 600 12px var(--font-ui, inherit); color: var(--fg); margin-bottom: 8px; }
.perm-seg { display: flex; flex-wrap: wrap; gap: 0; border: 1px solid var(--border-strong); border-radius: 8px; overflow: hidden; width: fit-content; max-width: 100%; }
.perm-seg-btn { border: 0; border-right: 1px solid var(--border); background: transparent; color: var(--muted); font: 500 12px var(--font-ui, inherit); padding: 8px 12px; cursor: pointer; }
.perm-seg-btn:last-child { border-right: 0; }
.perm-seg-btn:hover { color: var(--fg); }
.perm-seg-btn.is-on { background: var(--accent-wash); color: var(--fg-strong); box-shadow: inset 0 -2px 0 var(--accent); }
.perm-seg-btn[disabled] { opacity: .45; cursor: not-allowed; }
.perm-target, .perm-rolebox, .perm-keybox { margin-top: 8px; }
.perm-input {
  width: 100%; box-sizing: border-box; background: var(--bg-elev); border: 1px solid var(--border-strong);
  border-radius: 6px; color: var(--fg); font: 13px var(--font-ui, inherit); padding: 8px 10px;
}
.perm-input:focus { outline: none; border-color: var(--accent); }
.perm-role-keys { margin-top: 8px; font-size: 12px; color: var(--muted); }
.perm-role-keys summary { cursor: pointer; }
.perm-role-keys .perm-keychips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.perm-keybox-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 11.5px; color: var(--faint); margin-bottom: 6px; }
.perm-keybox-all { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.perm-keylist { max-height: 260px; overflow: auto; border: 1px solid var(--border); border-radius: 8px; }
.perm-keyopt { display: flex; align-items: flex-start; gap: 8px; padding: 7px 10px; border-top: 1px solid var(--border); cursor: pointer; font: 12px var(--font-mono, monospace); color: var(--fg); }
.perm-keyopt:first-child { border-top: 0; }
.perm-keyopt:hover { background: var(--accent-wash); }
.perm-keyopt-desc { display: block; font: 400 11px var(--font-ui, inherit); color: var(--muted); margin-top: 2px; }
.perm-preview-sentence { font-size: 13px; line-height: 1.55; color: var(--fg-strong); }
.audit-row-who { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 12.5px; color: var(--fg); margin-bottom: 6px; }

/* ── Permissions 0d (design v2.3; TA room #297): Direct / Effective, the inline add row, the
   dashed INHERITED chip with its source, Go to source and the resolved CHAIN strip. Every
   inherited figure here is the server's (GET /api/authorization/effective), never the tree. */
.perm-modetog + .perm-viewtog { margin-left: 0; }
.perm-tog[disabled] { opacity: .45; cursor: not-allowed; }
.perm-addrow { background: var(--bg-elev); align-items: end; }
.perm-add-f { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.perm-add-l { color: var(--faint); font: 600 10px var(--font-ui, inherit); text-transform: uppercase; letter-spacing: .05em; }
.perm-add-f select { width: 100%; min-width: 0; box-sizing: border-box; background: var(--panel); border: 1px solid var(--border-strong); border-radius: 6px; color: var(--fg); font: 12px var(--font-ui, inherit); padding: 6px 8px; }
.perm-add-status { min-width: 0; color: var(--muted); font-size: 11.5px; overflow-wrap: anywhere; }
.perm-add-status:empty { visibility: hidden; }
.perm-trow.perm-row-inh { color: var(--muted); }
.perm-inh-chip {
  display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; max-width: 100%;
  border: 1px dashed var(--border-strong); border-radius: 999px; padding: 3px 9px; font-size: 11.5px; color: var(--fg);
}
.perm-inh-arrow { color: var(--faint); }
.perm-src-btn, .perm-chain-btn {
  border: 0; background: none; padding: 2px 0; cursor: pointer; color: var(--accent);
  font: 500 11.5px var(--font-ui, inherit); text-align: left;
}
.perm-src-btn:hover, .perm-chain-btn:hover { text-decoration: underline; }
.perm-trow.perm-row-hl { background: var(--accent-wash); box-shadow: inset 3px 0 0 var(--accent); }
.perm-chain[hidden], .perm-source-banner[hidden], .perm-eff-foot[hidden] { display: none; }
.perm-chain {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding: 10px 14px;
  border-top: 1px solid var(--border); background: var(--bg-elev); font-size: 12px;
}
.perm-chain-t { color: var(--faint); font: 600 10.5px var(--font-ui, inherit); text-transform: uppercase; letter-spacing: .05em; margin-right: 4px; }
.perm-chain-lvl {
  display: inline-flex; align-items: center; gap: 5px; border: 1px dashed var(--border-strong); border-radius: 999px;
  background: transparent; color: var(--muted); font: 12px var(--font-ui, inherit); padding: 3px 9px; cursor: pointer;
}
.perm-chain-lvl.perm-chain-held { border-style: solid; border-color: var(--accent); color: var(--fg); }
.perm-chain-arrow { color: var(--faint); }
.perm-chain-keys { color: var(--faint); font: 11px var(--font-mono, monospace); }
#perm-chain-hide { margin-left: auto; }
.perm-source-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; max-width: 1100px; margin: 8px 0;
  padding: 8px 12px; border: 1px solid var(--accent); border-radius: 8px; background: var(--accent-wash); font-size: 12.5px; color: var(--fg);
}
.perm-eff-foot { margin: 8px 0 0; }

@media (max-width: 760px) {
  .perm-head-row { flex-direction: column; }
  .perm-thead { display: none; }
  .perm-trow { grid-template-columns: 1fr; gap: 6px; }
  .perm-c-act { text-align: left; }
  .perm-addrow { align-items: stretch; }
  .perm-chain { gap: 4px; }
  #perm-chain-hide { margin-left: 0; }
  .perm-modetog { margin-left: 0; }
}

/* ── Credentials (design Team Credentials 1a/1c; controllers/credentials.js) ───────────────
   Values are the design's own (Team Credentials.dc.html); every rule is under #cred-view. */
/* PO 2026-10-03: 1a / 1d sit in the 1440 app shell beside the rail — the list fills the
   content area edge to edge (no floating card); the header keeps its own 20px gutter. */
#cred-view.cred-view { padding: 0; overflow: auto; background: #0b0c0f; }
#cred-view .cred-frame {
  max-width: none; min-height: 100%; background: #0b0c0f; border: 0;
  border-radius: 0; overflow: visible; color: #e6e8ec;
}
/* overflow stays visible (as #perm-view .pv): the scope chip's dropdown hangs below the header,
   and a clipping frame cut it off at the frame's bottom on a short list (its note never showed). */
#cred-view .cred-frame-emp { border: 0; border-radius: 0; background: transparent; overflow: visible; padding: 24px 28px 40px; }
#cred-view .cred-ico { flex: 0 0 auto; display: block; }
#cred-view .cred-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px 12px; flex-wrap: wrap; }
#cred-view .cred-frame-emp .cred-head { padding: 0 0 14px; }
#cred-view .cred-title { margin: 0; font-size: 18px; font-weight: 600; line-height: 1.3; color: #f2f3f5; }
#cred-view .cred-sub { font: 400 11px/1.4 var(--font-mono); color: #6e7681; }
#cred-view .cred-spacer { flex: 1 1 auto; }
#cred-view .cred-scope[hidden] { display: none; }
#cred-view .cred-new {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 14px; border-radius: 9px;
  border: 0; background: #3794ff; color: #fff; font: 600 12.5px/1 var(--font-ui); cursor: pointer; white-space: nowrap;
}
#cred-view .cred-new:hover:not([disabled]) { background: #4aa0ff; }
#cred-view .cred-new[disabled] { opacity: .55; cursor: not-allowed; }
#cred-view .cred-back {
  display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 8px; border-radius: 7px;
  border: 1px solid rgba(255, 255, 255, .1); background: transparent; color: #c4c8ce;
  font: 500 11.5px/1 var(--font-ui); cursor: pointer;
}
#cred-view .cred-back[hidden] { display: none; }
#cred-view .cred-back:hover { background: #111318; }

#cred-view .cred-bar { display: flex; align-items: center; gap: 6px; padding: 0 20px 14px; flex-wrap: wrap; }
#cred-view .cred-bar[hidden] { display: none; }
#cred-view .cred-search {
  flex: 1 1 160px; min-width: 0; display: flex; align-items: center; gap: 7px; height: 32px; padding: 0 11px;
  border-radius: 8px; background: #111318; border: 1px solid rgba(255, 255, 255, .1); color: #6e7681; cursor: text;
}
#cred-view .cred-search input {
  flex: 1 1 auto; min-width: 0; height: 100%; border: 0; outline: 0; background: transparent; padding: 0;
  color: #e6e8ec; font: 400 12px/1 var(--font-ui);
}
#cred-view .cred-search input::placeholder { color: #5a6069; }
#cred-view .cred-search:focus-within { border-color: rgba(55, 148, 255, .55); }
#cred-view .cred-chip {
  position: relative; display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 9px;
  border-radius: 8px; border: 1px solid rgba(255, 255, 255, .1); background: transparent;
  font: 500 11.5px/1 var(--font-ui); color: #c4c8ce; cursor: pointer; white-space: nowrap;
}
#cred-view .cred-chip:hover { background: #111318; }
#cred-view .cred-chip-value { color: #8b909a; max-width: 160px; overflow: hidden; text-overflow: ellipsis; }
#cred-view .cred-chip-caret { font-size: 9px; color: #6e7681; }
#cred-view .cred-chip select {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
  background: #111318; color: #e6e8ec; font-size: 12px; border: 0;
}
#cred-view .cred-chip select[hidden] { display: none; }
#cred-view .cred-chip .popchip-pop { top: calc(100% + 6px); left: 0; z-index: 80; }

#cred-view .cred-status { padding: 0 20px; font-size: 11.5px; color: #8b909a; }
#cred-view .cred-status:empty { display: none; }
#cred-view .cred-status-error { color: #ff9b9b; padding-bottom: 10px; }
#cred-view .cred-ctx[hidden] { display: none; }

#cred-view .cred-row {
  display: grid; grid-template-columns: 230px 180px 240px minmax(0, 1fr) 110px 140px; gap: 0 14px;
  align-items: center; padding: 10px 20px; border-bottom: 1px solid rgba(255, 255, 255, .05); cursor: pointer;
}
#cred-view .cred-row-head {
  padding: 9px 20px; background: #0e1014; cursor: default;
  border-top: 1px solid rgba(255, 255, 255, .07); border-bottom: 1px solid rgba(255, 255, 255, .07);
  font: 600 9.5px/1.2 var(--font-mono); letter-spacing: .09em; color: #6e7681; text-transform: uppercase;
}
#cred-view .cred-item:not(.cred-open) > .cred-row:hover { background: rgba(255, 255, 255, .02); }
#cred-view .cred-row:focus-visible { outline: 1px solid rgba(55, 148, 255, .6); outline-offset: -1px; }
#cred-view .cred-open { background: rgba(55, 148, 255, .08); }
#cred-view .cred-open > .cred-row { border-bottom-color: transparent; }
#cred-view .cred-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
#cred-view .cred-name-main {
  font: 600 12.5px/1.35 var(--font-ui); color: #f2f3f5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#cred-view .cred-name-sub {
  font: 400 10.5px/1.35 var(--font-mono); color: #6e7681; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#cred-view .cred-type { display: inline-flex; align-items: center; gap: 7px; color: #8b909a; min-width: 0; }
#cred-view .cred-type > span { font: 600 11.5px/1.3 var(--font-ui); color: #c4c8ce; white-space: nowrap; }
#cred-view .cred-owner { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
#cred-view .cred-owner-name { font: 600 12px/1.3 var(--font-ui); color: #e6e8ec; }
#cred-view .cred-avatar {
  flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center; font: 600 9px/1 var(--font-ui);
}
#cred-view .cred-avatar-amber { background: #3a2f22; color: #e6b96e; }
#cred-view .cred-avatar-blue { background: #2a3550; color: #9db8ff; }
#cred-view .cred-avatar-emp { background: rgba(197, 134, 192, .2); color: #c586c0; }
#cred-view .cred-avatar-lg { width: 22px; height: 22px; font-size: 9.5px; }
#cred-view .cred-noowner { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
#cred-view .cred-badge-warn {
  flex: 0 0 auto; padding: 1px 5px; border-radius: 3px; background: rgba(255, 107, 107, .13); color: #ff9b9b;
  font: 600 8px/1.4 var(--font-mono); letter-spacing: .04em;
}
#cred-view .cred-mute { font: 400 10.5px/1.4 var(--font-mono); color: #6e7681; }
#cred-view .cred-shares { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; min-width: 0; }
#cred-view .cred-share {
  display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 7px; border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, .1); white-space: nowrap; max-width: 100%; min-width: 0; box-sizing: border-box;
}
/* QC 943: the kind reads in sentence case ("AI employee"), never shouted as "EMPLOYEE". */
#cred-view .cred-kind { flex: 0 0 auto; font: 600 9px/1 var(--font-mono); color: #8b909a; }
#cred-view .cred-kind-emp { color: #c586c0; }
#cred-view .cred-share-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; font: 600 11px/1.2 var(--font-ui); color: #e6e8ec; }
#cred-view .cred-personal { font: 400 11px/1.3 var(--font-ui); color: #6e7681; }
#cred-view .cred-last { font: 400 10.5px/1.3 var(--font-mono); color: #8b909a; }
#cred-view .cred-last-bad { color: #ff9b9b; }
#cred-view .cred-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }

#cred-view .cred-act {
  display: inline-flex; align-items: center; gap: 4px; padding: 0; border: 0; background: transparent;
  font: 600 11px/1.3 var(--font-ui); color: #c4c8ce; cursor: pointer; white-space: nowrap;
}
#cred-view .cred-act:hover:not([disabled]) { text-decoration: underline; }
#cred-view .cred-act-danger { color: #ff9b9b; }
#cred-view .cred-act-accent, #cred-view .cred-act-link { color: #3794ff; }
#cred-view .cred-act[disabled] { opacity: .55; cursor: not-allowed; }
#cred-view .cred-act-sm { font-size: 11px; }

#cred-view .cred-detail {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.2fr); gap: 12px;
  padding: 2px 20px 14px; cursor: default;
}
#cred-view .cred-card {
  border: 1px solid rgba(255, 255, 255, .09); border-radius: 10px; background: #0e1014; overflow: hidden; min-width: 0;
}
#cred-view .cred-card h3 {
  margin: 0; padding: 7px 13px; background: #12151b; border-bottom: 1px solid rgba(255, 255, 255, .07);
  font: 600 9.5px/1.3 var(--font-mono); letter-spacing: .09em; color: #6e7681; text-transform: uppercase;
}
#cred-view .cred-kv {
  display: flex; align-items: center; gap: 10px; padding: 8px 13px; border-bottom: 1px solid rgba(255, 255, 255, .04);
  min-height: 32px; box-sizing: border-box;
}
#cred-view .cred-kv-k { flex: 0 0 80px; font: 500 11px/1.4 var(--font-ui); color: #8b909a; }
#cred-view .cred-kv-access { align-items: flex-start; }
#cred-view .cred-kv-access .cred-kv-k { flex-basis: 96px; font-weight: 400; }
#cred-view .cred-kv-v {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
  font: 500 11.5px/1.4 var(--font-ui); color: #e6e8ec;
}
#cred-view .cred-kv-v b { font-weight: 600; color: #e6e8ec; }
#cred-view .cred-mono { font: 500 11.5px/1.4 var(--font-mono); color: #e6e8ec; }
#cred-view .cred-dots { font: 500 11.5px/1 var(--font-mono); letter-spacing: .12em; color: #8b909a; }
#cred-view .cred-reveal { margin-left: auto; }
#cred-view .cred-link { display: inline-flex; align-items: center; gap: 6px; color: #3794ff; }
#cred-view .cred-stack { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
#cred-view .cred-warn { margin: 0; padding: 8px 13px; font: 400 11px/1.5 var(--font-ui); color: #ff9b9b; }
#cred-view .cred-card-note { margin: 0; padding: 8px 13px; font: 400 11px/1.5 var(--font-ui); color: #6e7681; }
#cred-view .cred-card-foot { padding: 8px 13px; }
#cred-view .cred-use {
  display: grid; grid-template-columns: 52px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 8px 13px; border-bottom: 1px solid rgba(255, 255, 255, .04);
}
#cred-view .cred-use-at, #cred-view .cred-use-by { font: 400 10.5px/1.4 var(--font-mono); color: #6e7681; }
#cred-view .cred-use-what {
  font: 400 11.5px/1.4 var(--font-ui); color: #c4c8ce; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

#cred-view .cred-note {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 18px 20px;
  border-top: 1px solid rgba(255, 255, 255, .07); font: 400 12px/1.5 var(--font-ui); color: #8b909a;
}
#cred-view .cred-note-refused { color: #ff9b9b; }
#cred-view .cred-note-detail { color: #6e7681; font: 400 11px/1.5 var(--font-mono); }
#cred-view .cred-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  padding: 10px 20px; font: 400 10.5px/1.4 var(--font-mono); color: #6e7681;
}
#cred-view .cred-foot:empty { display: none; }

/* 1c — one employee: EMPLOYEE card beside CAN USE · N · VIA */
#cred-view .cred-emp-grid { display: grid; grid-template-columns: 360px minmax(0, 1fr); gap: 14px; align-items: start; max-width: none; }
#cred-view .cred-emp-who { display: flex; align-items: center; gap: 9px; padding: 11px 13px; }
#cred-view .cred-emp-name { font: 600 12.5px/1.3 var(--font-ui); color: #f2f3f5; }
#cred-view .cred-badge-emp {
  padding: 1px 5px; border-radius: 3px; background: rgba(197, 134, 192, .15); color: #c586c0;
  font: 600 8px/1.4 var(--font-mono); letter-spacing: .04em;
}
#cred-view .cred-row-emp {
  grid-template-columns: minmax(0, 1fr) 170px 200px; padding: 8px 13px; cursor: default;
  border-bottom: 1px solid rgba(255, 255, 255, .04);
}
#cred-view .cred-canuse .cred-name-main { font-size: 12px; }
#cred-view .cred-via { display: inline-flex; align-items: baseline; gap: 6px; min-width: 0; max-width: 100%; overflow: hidden; white-space: nowrap; }
#cred-view .cred-via-name { font: 600 11.5px/1.3 var(--font-ui); color: #e6e8ec; }
#cred-view .cred-via-basis { font: 400 10px/1.3 var(--font-mono); color: #6e7681; overflow: hidden; text-overflow: ellipsis; }
#cred-view .cred-canuse > .cred-card-note.cred-card-foot { font: 400 10.5px/1.5 var(--font-mono); padding: 9px 13px; }

/* 1d — the usage log of one credential ("View all use", in place of the list). */
#cred-view .cred-log-id { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
#cred-view .cred-log-id[hidden] { display: none; }
#cred-view .cred-log-id .cred-mute { font: 400 11.5px/1.3 var(--font-ui); color: #8b929b; }
/* "← Credentials" is the title of 1d: the back control takes the title's type, no box. */
#cred-view .cred-frame-log .cred-back {
  height: auto; padding: 0; border: 0; gap: 8px; color: #f2f3f5; font: 600 18px/1.3 var(--font-ui);
}
#cred-view .cred-frame-log .cred-back:hover { background: transparent; color: #fff; }
#cred-view .cred-frame-log .cred-sub { order: 9; margin-left: auto; }
#cred-view .cred-frame-log .cred-spacer { display: none; }
#cred-view .cred-log-name { display: inline-flex; align-items: center; gap: 7px; font: 600 14px/1.3 var(--font-ui); color: #f2f3f5; }
#cred-view .cred-log-row {
  display: grid; grid-template-columns: 110px 190px 190px 190px minmax(0, 1fr) 100px; gap: 0 14px;
  align-items: center; padding: 9px 20px; border-bottom: 1px solid rgba(255, 255, 255, .05);
}
#cred-view .cred-log-row > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
#cred-view .cred-log-who { display: inline-flex; align-items: center; gap: 6px; min-width: 0; font: 500 11.5px/1.3 var(--font-ui); color: #e6e8ec; }
#cred-view .cred-ev {
  display: inline-block; padding: 2px 7px; border-radius: 4px; font: 600 9.5px/1.4 var(--font-mono);
  background: rgba(255, 255, 255, .06); color: #c4c8ce;
}
#cred-view .cred-ev-allowed, #cred-view .cred-ev-created { background: rgba(78, 201, 176, .12); color: #4ec9b0; }
#cred-view .cred-ev-refused, #cred-view .cred-ev-deleted, #cred-view .cred-ev-writeFailed { background: rgba(255, 155, 155, .12); color: #ff9b9b; }
#cred-view .cred-ev-deleting, #cred-view .cred-ev-transferred, #cred-view .cred-ev-assigned { background: rgba(230, 185, 110, .12); color: #e6b96e; }
#cred-view .cred-ev-shared, #cred-view .cred-ev-setNew, #cred-view .cred-ev-unshared,
#cred-view .cred-ev-sharerAdded, #cred-view .cred-ev-sharerRemoved { background: rgba(55, 148, 255, .12); color: #9db8ff; }

@media (max-width: 900px) {
  #cred-view.cred-view { padding: 16px; }
  #cred-view .cred-row, #cred-view .cred-row-emp, #cred-view .cred-log-row { grid-template-columns: 1fr 1fr; gap: 6px 12px; }
  #cred-view .cred-row-head { display: none; }
  #cred-view .cred-actions { justify-content: flex-start; }
  #cred-view .cred-detail, #cred-view .cred-emp-grid { grid-template-columns: 1fr; }
}

/* Write states on a row: a New / Set new that did not finish, a delete that did not finish. */
#cred-view .cred-badge-state {
  display: inline-block; margin-left: 7px; padding: 1px 5px; border-radius: 3px; vertical-align: 1px;
  font: 600 8px/1.4 var(--font-mono); letter-spacing: .04em; text-transform: uppercase;
  background: rgba(230, 185, 110, .14); color: #e6b96e;
}
#cred-view .cred-state-failed, #cred-view .cred-state-deleting { background: rgba(255, 107, 107, .13); color: #ff9b9b; }

/* Write dialogs (design 1b New credential · 1e Assign owner · 1f Shared with / Set new / Transfer).
   Scrim + Esc + press-outside close, like TM.util.confirm; sizes and tokens are the design's. */
#cred-view .cred-dlg-scrim {
  position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(0, 0, 0, .55); box-sizing: border-box;
}
#cred-view .cred-dlg-scrim[hidden] { display: none; }
#cred-view .cred-dlg {
  width: 390px; max-width: 100%; max-height: calc(100vh - 32px); overflow: auto; box-sizing: border-box;
  background: #111318; border: 1px solid rgba(255, 255, 255, .12); border-radius: 14px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, .6); color: #e6e8ec; font-family: var(--font-ui);
}
#cred-view .cred-dlg:focus { outline: 0; }
#cred-view .cred-dlg-md { width: 460px; }
#cred-view .cred-dlg-lg { width: 560px; }
#cred-view .cred-dlg-head {
  display: flex; align-items: center; gap: 9px; padding: 14px 18px; border-bottom: 1px solid rgba(255, 255, 255, .08);
  color: #8b909a;
}
#cred-view .cred-dlg-title { flex: 1 1 auto; min-width: 0; font: 600 14px/1.3 var(--font-ui); color: #f2f3f5; }
#cred-view .cred-dlg-x {
  border: 0; background: transparent; color: #5a6069; font: 400 14px/1 var(--font-ui); cursor: pointer; padding: 2px 4px;
}
#cred-view .cred-dlg-x:hover { color: #c4c8ce; }
#cred-view .cred-dlg-body { display: flex; flex-direction: column; gap: 14px; padding: 16px 18px; }
#cred-view .cred-dlg-part:empty { display: none; }
#cred-view .cred-dlg-part { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
#cred-view .cred-dlg-lbl {
  display: block; margin-bottom: 6px; font: 600 9.5px/1.2 var(--font-mono); letter-spacing: .09em;
  color: #6e7681; text-transform: uppercase;
}
#cred-view .cred-dlg-types { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin-top: -8px; }
#cred-view .cred-dlg-type {
  display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 6px; border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, .1); background: transparent; color: #8b909a; cursor: pointer;
  font: 500 10.5px/1.25 var(--font-ui); text-align: center;
}
#cred-view .cred-dlg-type span { color: #c4c8ce; }
#cred-view .cred-dlg-type:hover { background: #15181e; }
#cred-view .cred-dlg-type-on { border-color: rgba(55, 148, 255, .5); background: rgba(55, 148, 255, .1); color: #3794ff; }
#cred-view .cred-dlg-type-on span { color: #f2f3f5; }
#cred-view .cred-dlg-grid2 { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 12px; }
#cred-view .cred-dlg-col { min-width: 0; }
#cred-view .cred-dlg-row2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
#cred-view .cred-dlg-field { min-width: 0; }
#cred-view .cred-dlg-in {
  width: 100%; height: 36px; box-sizing: border-box; padding: 0 11px; border-radius: 9px; outline: 0;
  background: #0b0c0f; border: 1px solid rgba(255, 255, 255, .12); color: #e6e8ec; font: 500 12.5px/1 var(--font-ui);
}
#cred-view .cred-dlg-in.cred-mono { font: 500 12px/1 var(--font-mono); }
#cred-view .cred-dlg-in:focus { border-color: rgba(55, 148, 255, .55); }
#cred-view .cred-dlg-in-sm { width: auto; flex: 1 1 120px; height: 30px; border-radius: 8px; font-size: 12px; }
#cred-view .cred-dlg-area { height: auto; padding: 9px 11px; line-height: 1.45; resize: vertical; }
#cred-view .cred-dlg-transfer { height: 34px; border-color: rgba(55, 148, 255, .4); }
#cred-view select.cred-dlg-in { appearance: auto; }
#cred-view .cred-dlg-secret { position: relative; display: block; }
#cred-view .cred-dlg-secret .cred-dlg-in { padding-right: 34px; letter-spacing: .08em; }
#cred-view .cred-dlg-gen {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%); border: 0; background: transparent;
  color: #6e7681; cursor: pointer; padding: 4px; border-radius: 6px;
}
#cred-view .cred-dlg-gen:hover { color: #3794ff; }
#cred-view .cred-dlg-link {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; background: transparent;
  color: #3794ff; font: 600 11.5px/1.3 var(--font-ui); cursor: pointer;
}
#cred-view .cred-dlg-link[disabled] { opacity: .55; cursor: not-allowed; }
#cred-view .cred-dlg-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-height: 36px; }
#cred-view .cred-dlg-chip {
  display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 4px 0 7px; border-radius: 6px;
  background: #0b0c0f; border: 1px solid rgba(255, 255, 255, .12); white-space: nowrap;
}
#cred-view .cred-dlg-lg .cred-dlg-chip { background: #111318; }
#cred-view .cred-dlg-chip-name { font: 600 11px/1 var(--font-ui); color: #e6e8ec; }
#cred-view .cred-dlg-chip-off { opacity: .5; }
#cred-view .cred-dlg-chip-off .cred-dlg-chip-name { text-decoration: line-through; }
#cred-view .cred-dlg-chip-x {
  border: 0; background: transparent; color: #6e7681; font: 400 10px/1 var(--font-ui); cursor: pointer; padding: 2px 3px;
}
#cred-view .cred-dlg-chip-x:hover { color: #ff9b9b; }
#cred-view .cred-dlg-add {
  border: 0; background: transparent; padding: 0 2px; color: #3794ff; font: 600 11px/1 var(--font-ui); cursor: pointer;
}
#cred-view .cred-dlg-lg .cred-dlg-add { color: #5a6069; font-weight: 500; }
#cred-view .cred-dlg-lg .cred-dlg-add:hover { color: #3794ff; }
#cred-view .cred-dlg-adder { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
#cred-view .cred-dlg-warn {
  padding: 9px 11px; border-radius: 8px; background: rgba(255, 107, 107, .06); border: 1px solid rgba(255, 107, 107, .25);
  font: 400 11.5px/1.5 var(--font-ui); color: #ff9b9b;
}
#cred-view .cred-dlg-help { margin: 0; font: 400 12px/1.55 var(--font-ui); color: #c4c8ce; }
#cred-view .cred-dlg-hint { font: 400 11px/1.45 var(--font-ui); color: #6e7681; }
#cred-view p.cred-dlg-hint { margin: -6px 0 0; }
#cred-view .cred-dlg-note-warn { margin: 0; font: 400 11.5px/1.5 var(--font-ui); color: #e6b96e; }
#cred-view .cred-dlg-msg {
  margin: 0; padding: 9px 11px; border-radius: 8px; background: rgba(255, 107, 107, .06);
  border: 1px solid rgba(255, 107, 107, .25); font: 400 11.5px/1.5 var(--font-ui); color: #ff9b9b;
}
#cred-view .cred-dlg-passkey {
  display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 16px; border-radius: 10px;
  border: 1px dashed rgba(255, 255, 255, .14); color: #8b909a; text-align: center;
}
#cred-view .cred-dlg-cands { display: flex; flex-direction: column; gap: 2px; padding: 4px; border-radius: 10px; background: #0b0c0f; }
#cred-view .cred-dlg-cand {
  display: flex; align-items: center; gap: 9px; padding: 7px 10px; border-radius: 7px; border: 0; background: transparent;
  color: #e6e8ec; cursor: pointer; text-align: left; width: 100%;
}
#cred-view .cred-dlg-cand:hover:not([disabled]) { background: rgba(255, 255, 255, .04); }
#cred-view .cred-dlg-cand-on, #cred-view .cred-dlg-cand-on:hover:not([disabled]) { background: rgba(55, 148, 255, .14); }
#cred-view .cred-dlg-cand-no { opacity: .45; cursor: not-allowed; }
#cred-view .cred-dlg-cand-name { font: 600 12.5px/1.3 var(--font-ui); }
#cred-view .cred-dlg-cand-tag { flex: 1 1 auto; text-align: right; font: 400 10.5px/1.3 var(--font-mono); color: #46d19e; }
#cred-view .cred-dlg-cand-no .cred-dlg-cand-tag { color: #6e7681; }
#cred-view .cred-dlg-cand-tick { color: #3794ff; font-weight: 700; width: 14px; text-align: right; }
#cred-view .cred-dlg-foot {
  flex-direction: row; align-items: center; gap: 8px; padding: 12px 18px; border-top: 1px solid rgba(255, 255, 255, .08);
}
#cred-view .cred-dlg-note { flex: 1 1 auto; min-width: 0; font: 400 11px/1.4 var(--font-ui); color: #6e7681; }
#cred-view .cred-dlg-btn {
  height: 32px; padding: 0 14px; border-radius: 9px; border: 1px solid rgba(255, 255, 255, .12); background: transparent;
  color: #c4c8ce; font: 500 12.5px/1 var(--font-ui); cursor: pointer; white-space: nowrap;
}
#cred-view .cred-dlg-btn:hover:not([disabled]) { background: #15181e; }
#cred-view .cred-dlg-btn[disabled] { opacity: .5; cursor: not-allowed; }
#cred-view .cred-dlg-btn-sm { height: 30px; padding: 0 11px; border-radius: 8px; font-size: 12px; }
#cred-view .cred-dlg-primary { border-color: transparent; background: #3794ff; color: #fff; font-weight: 600; }
#cred-view .cred-dlg-primary:hover:not([disabled]) { background: #4aa0ff; }
#cred-view .cred-dlg-primary-ghost { color: #3794ff; border-color: rgba(55, 148, 255, .4); font-weight: 600; }
#cred-view .cred-dlg-danger { border-color: rgba(255, 107, 107, .4); color: #ff9b9b; font-weight: 600; }
#cred-view .cred-dlg-danger:hover:not([disabled]) { background: rgba(255, 107, 107, .08); }
#cred-view .cred-dlg-sm .cred-dlg-head { padding: 9px 13px; }
#cred-view .cred-dlg-sm .cred-dlg-title { font-size: 12px; }
#cred-view .cred-dlg-sm .cred-dlg-body { padding: 13px; gap: 12px; }
#cred-view .cred-dlg-sm .cred-dlg-foot { padding: 10px 13px; }
#cred-view .cred-dlg-sm .cred-dlg-btn { height: 30px; border-radius: 8px; font-size: 12px; }
@media (max-width: 600px) {
  #cred-view .cred-dlg-types { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  #cred-view .cred-dlg-grid2, #cred-view .cred-dlg-row2 { grid-template-columns: 1fr; }
}

/* ── Permissions 0c (design v2.3 Team RBAC 0c-1/0c-2; PO 2026-10-02): one grant = one row
   WHO | ON WHAT | PERMISSION, a SEPARATE requests list with its own count, Project and Host/VM
   facets, Sort, and the typed ON picker (Company · Workspace · Project · Team · Employee · Host/VM). */
.perm-head-ws { color: var(--muted); font-weight: 500; }
.perm-head-n { color: var(--faint); font-size: 14px; font-weight: 500; }
.perm-head-n b { color: var(--fg); }
.perm-head-req b { color: #e0b341; }
.perm-head-acts { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.perm-sorttog { margin-left: 0; align-items: center; }
.perm-sort-l { color: var(--faint); font: 600 10.5px var(--font-ui, inherit); text-transform: uppercase; letter-spacing: .05em; }
.perm-facets { max-width: 1100px; }
.perm-field-q { flex: 1 1 200px; }
.perm-chipf[hidden] { display: none; }
.perm-chipf {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-end; border: 1px solid var(--accent);
  border-radius: 999px; background: var(--accent-wash); color: var(--fg); font-size: 12px; padding: 5px 6px 5px 11px;
}
.perm-chipf-x { border: 0; background: none; color: var(--muted); cursor: pointer; font-size: 13px; line-height: 1; padding: 0 4px; }
.perm-chipf-x:hover { color: var(--fg); }

.perm-requests { max-width: 1100px; margin: 0 0 18px; border: 1px solid rgba(224, 179, 65, .35); border-radius: 10px; overflow: hidden; }
.perm-req-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; padding: 10px 14px; background: rgba(224, 179, 65, .07); }
.perm-req-title { margin: 0; font: 600 12px var(--font-ui, inherit); color: #e0b341; text-transform: uppercase; letter-spacing: .05em; }
.perm-req-rows { list-style: none; margin: 0; padding: 0; }
.perm-req-rows:empty { display: none; }
.perm-req-row + .perm-req-row { border-top: 1px solid var(--border); }
.perm-req-card { list-style: none; margin: 0; padding: 0 14px 12px; }
.perm-trow.perm-row-pending { box-shadow: inset 3px 0 0 #e0b341; }
.perm-req-row .perm-trow { border-top: 0; }
.perm-key-pending { background: rgba(224, 179, 65, .14); color: #e0b341; border: 1px dashed rgba(224, 179, 65, .5); }
.perm-pending-tag { font: 600 10px var(--font-mono, monospace); color: #e0b341; text-transform: uppercase; letter-spacing: .05em; }

.perm-table-0c .perm-thead, .perm-table-0c .perm-trow, .perm-requests .perm-trow {
  grid-template-columns: minmax(170px, 1.2fr) minmax(170px, 1.3fr) minmax(190px, 1.6fr) 96px;
}
.perm-table-0c .perm-addrow .perm-add-status { grid-column: 1 / -1; order: 9; }
.perm-ava {
  display: inline-flex; align-items: center; justify-content: center; flex: none; width: 22px; height: 22px;
  border-radius: 50%; background: var(--accent-wash); color: var(--fg); font: 600 11px var(--font-ui, inherit);
}
.perm-nkeys { color: var(--faint); font: 400 11px var(--font-mono, monospace); }
.perm-by { flex-basis: 100%; color: var(--faint); font-size: 11px; }

.perm-on-f { position: relative; }
.perm-on-btn {
  display: flex; align-items: center; gap: 6px; width: 100%; min-width: 0; box-sizing: border-box; text-align: left;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 6px; color: var(--fg);
  font: 12px var(--font-ui, inherit); padding: 6px 8px; cursor: pointer;
}
.perm-on-btn[aria-expanded="true"], .perm-on-btn:focus-visible { outline: none; border-color: var(--accent); }
.perm-on-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.perm-on-pop[hidden] { display: none; }
.perm-on-pop {
  position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; width: 380px; max-width: calc(100vw - 32px);
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .35); padding: 8px; box-sizing: border-box;
}
.perm-on-tabs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 8px; }
.perm-on-tab {
  border: 1px solid var(--border); border-radius: 999px; background: transparent; color: var(--muted);
  font: 500 11.5px var(--font-ui, inherit); padding: 4px 9px; cursor: pointer;
}
.perm-on-tab:hover { color: var(--fg); }
.perm-on-tab.is-on { border-color: var(--accent); background: var(--accent-wash); color: var(--fg-strong); }
.perm-on-tab-na { opacity: .6; }
.perm-on-search { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.perm-on-search .perm-input { flex: 1; min-width: 0; }
.perm-on-search-i, .perm-on-count { color: var(--faint); font-size: 11px; white-space: nowrap; }
.perm-on-list { max-height: 260px; overflow: auto; border: 1px solid var(--border); border-radius: 8px; }
.perm-on-item {
  display: flex; align-items: center; gap: 8px; width: 100%; border: 0; border-top: 1px solid var(--border);
  background: transparent; color: var(--fg); text-align: left; font: 12px var(--font-ui, inherit); padding: 7px 10px; cursor: pointer;
}
.perm-on-item:first-child { border-top: 0; }
.perm-on-item:hover { background: var(--accent-wash); }
.perm-on-item.is-on { background: var(--accent-wash); box-shadow: inset 3px 0 0 var(--accent); }
.perm-on-child { padding-left: 28px; }
.perm-on-ico { flex: none; width: 16px; text-align: center; color: var(--faint); }
.perm-on-dt { color: var(--faint); font: 11px var(--font-mono, monospace); white-space: nowrap; }
.perm-on-ck { color: var(--accent); }
.perm-on-empty { padding: 12px 10px; color: var(--muted); font-size: 12px; line-height: 1.5; }
.perm-on-hint { margin-top: 6px; color: var(--faint); font-size: 11px; line-height: 1.5; }

.perm-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  max-width: 1100px; margin: 10px 0 0; color: var(--muted); font-size: 12px;
}
.perm-legend { display: inline-flex; gap: 14px; flex-wrap: wrap; color: var(--faint); font-size: 11px; }
.perm-lg { display: inline-flex; align-items: center; gap: 5px; }
.perm-lg::before { content: ""; width: 10px; height: 10px; border-radius: 3px; box-sizing: border-box; }
.perm-lg-role::before { background: rgba(55, 148, 255, .14); border: 1px solid var(--accent); }
.perm-lg-key::before { background: var(--accent-wash); border: 1px solid var(--border-strong); }
.perm-lg-pending::before { background: rgba(224, 179, 65, .14); border: 1px dashed rgba(224, 179, 65, .6); }

@media (max-width: 760px) {
  .perm-table-0c .perm-trow, .perm-requests .perm-trow { grid-template-columns: 1fr; }
  .perm-on-pop { position: static; width: auto; margin-top: 6px; }
  .perm-foot { flex-direction: column; align-items: flex-start; }
}

/* ── Permissions 0c-1, 1:1 with design v2.3 "Team RBAC" (PO 2026-10-02 #3): ONE card — header
   with Sort | Who | On what, one row of filter chips, WHO | ON WHAT | PERMISSION, the add row,
   pending requests and grants as compact rows, the legend footer. Every value below is the
   design's own; all of it is scoped under #perm-view .pv so no other screen moves. */
/* PO 2026-10-03 (1a): inside the 1440 app shell beside the rail — the screen fills the content
   area edge to edge, no floating card; the header keeps its own 20px gutter. */
#perm-view.perm-view { padding: 0; background: #0b0c0f; }
#perm-view .pv {
  max-width: none; min-height: 100%; background: #0b0c0f; border: 0; border-radius: 0;
  overflow: visible; font-family: var(--font-ui, inherit); color: #e6e8ec;
}
#perm-view .pv-fill { flex: 1 1 auto; min-width: 0; }
#perm-view .pv-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px 12px; }
/* QC 955: at phone width the Sort · Who | On what toggle was clipped at the right edge — the
   header wraps, and a toggle that still does not fit shrinks inside the row instead of overflowing. */
#perm-view .pv-head { flex-wrap: wrap; row-gap: 8px; min-width: 0; }
#perm-view .pv-head > .pv-seg { max-width: 100%; min-width: 0; flex-wrap: wrap; }
@media (max-width: 640px) {
  #perm-view .pv-head { padding: 12px 16px 10px; gap: 8px; }
  #perm-view .pv-head > .pv-fill { flex-basis: 100%; height: 0; }
  #perm-view .pv-head .pv-seg-l, #perm-view .pv-head .pv-seg-b { padding: 6px 9px; }
}
#perm-view .pv-title { margin: 0; font: 600 18px/1.2 var(--font-ui, inherit); color: #f2f3f5; }
/* design 1g: "Grants · N | Groups · N" under the header; the Groups tab hides the grants body. */
#perm-view .pv-tabs { display: flex; gap: 4px; padding: 0 20px 10px; }
#perm-view .pv-tab { height: 26px; padding: 0 11px; border-radius: 6px; border: 0; background: transparent; color: #a9adb3; font: 500 12px var(--font-ui, inherit); cursor: pointer; }
#perm-view .pv-tab:hover { background: #111318; }
#perm-view .pv-tab.is-on { background: rgba(55,148,255,.14); color: #3794ff; }
#perm-view .pv.pv-on-groups > :not(.pv-head):not(.pv-tabs):not(#perm-groups) { display: none; }
#perm-view .pv-groups { padding: 12px 20px; border-top: 1px solid rgba(255,255,255,.06); }
#perm-view .pv-groups[hidden] { display: none; }
#perm-view .pv-add.pv-add-off { opacity: .55; }
#perm-view .pv-sub { font: 400 11px var(--font-mono, monospace); color: #6e7681; }
#perm-view .pv-sub b { font-weight: 400; color: inherit; }
#perm-view .pv-sub-req[hidden] { display: none; }
#perm-view .pv-sub-req b { color: #e0b341; }
#perm-view .pv-seg {
  display: inline-flex; align-items: stretch; border: 1px solid rgba(255,255,255,.1); border-radius: 8px;
  overflow: hidden; font: 500 11.5px var(--font-ui, inherit);
}
#perm-view .pv-seg[hidden] { display: none; }
#perm-view .pv-seg-l, #perm-view .pv-seg-b {
  padding: 6px 12px; color: #8b909a; background: transparent; border: 0; font: inherit; line-height: 1.3; margin: 0;
  border-radius: 0;
}
#perm-view .pv-seg > * + * { border-left: 1px solid rgba(255,255,255,.1); }
#perm-view .pv-seg-b { cursor: pointer; }
#perm-view .pv-seg-b:hover { color: #c4c8ce; }
#perm-view .pv-seg-b.perm-tog-on { background: rgba(55,148,255,.14); color: #3794ff; }
#perm-view .pv-seg-b[disabled] { opacity: .5; cursor: default; }

/* Filters: search + chips, one line. */
#perm-view .pv-filters { display: flex; align-items: center; gap: 6px; padding: 0 20px 14px; flex-wrap: nowrap; }
#perm-view .pv-search {
  display: flex; align-items: center; gap: 7px; height: 32px; box-sizing: border-box; padding: 0 11px;
  border-radius: 8px; background: #111318; border: 1px solid rgba(255,255,255,.1); flex: 1 1 160px; min-width: 120px;
  cursor: text;
}
#perm-view .pv-search-i { flex: none; color: #6e7681; }
#perm-view .pv-search input {
  flex: 1; min-width: 0; background: transparent; border: 0; outline: none; padding: 0; color: #e6e8ec;
  font: 400 12px var(--font-ui, inherit); -webkit-appearance: none; appearance: none;
}
#perm-view .pv-search input::placeholder { color: #5a6069; }
#perm-view .pv-search input::-webkit-search-cancel-button { display: none; }
#perm-view .pv-chip {
  position: relative; display: inline-flex; align-items: center; gap: 5px; height: 30px; box-sizing: border-box;
  padding: 0 9px; border-radius: 8px; border: 1px solid rgba(255,255,255,.1); background: transparent;
  color: #c4c8ce; font: 500 11.5px var(--font-ui, inherit); white-space: nowrap; flex: none;
  text-transform: none; letter-spacing: 0;
}
#perm-view .pv-chip[hidden] { display: none; }
#perm-view .pv-chip:hover { border-color: rgba(255,255,255,.18); }
#perm-view .pv-chip-v { color: #8b909a; }
#perm-view .pv-caret { font-size: 9px; color: #6e7681; line-height: 1; }
#perm-view .pv-chip-sel {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; margin: 0; border: 0;
  font: inherit; color: #000;
}
#perm-view .pv-chip-x {
  display: none; position: relative; z-index: 2; background: none; border: 0; padding: 0; margin: 0;
  color: inherit; font-size: 11px; line-height: 1; cursor: pointer;
}
#perm-view .pv-chip.pv-on { background: rgba(55,148,255,.12); border-color: rgba(55,148,255,.4); color: #3794ff; }
#perm-view .pv-chip.pv-on .pv-chip-v { color: #3794ff; margin-left: -2px; }
#perm-view .pv-chip.pv-on .pv-chip-l::after { content: ":"; }
#perm-view #perm-f-exact .pv-chip-l::after { content: none; }
#perm-view .pv-chip.pv-on .pv-caret { display: none; }
#perm-view .pv-chip.pv-on .pv-chip-x { display: inline-block; }
#perm-view .pv-chip.pv-on .pv-chip-sel { right: 20px; width: auto; }
/* The Company and Who chips host the shared scope pickers; inside a chip they read as the
   chip's grey value with a caret — the pickers' own pill chrome is flattened away. */
#perm-view .pv-chip-host { padding-right: 9px; }
#perm-view .pv-chip-host .scope-host, #perm-view .pv-chip-host .scope-wrap, #perm-view .pv-chip-host .scope-pp { position: static; min-width: 0; }
#perm-view .pv-chip-host .scope-pill, #perm-view .pv-chip-host .scope-pp-btn {
  display: inline-flex; align-items: center; gap: 4px; width: auto; max-width: 220px; min-height: 0; height: 28px;
  margin: 0; padding: 0; border: 0; background: transparent; box-shadow: none; color: #8b909a;
  font: 500 11.5px var(--font-ui, inherit); cursor: pointer;
}
#perm-view .pv-chip-host .scope-pill::after, #perm-view .pv-chip-host .scope-pp-btn::after {
  content: ""; position: absolute; inset: 0;
}
#perm-view .pv-chip-host .cp-sw-tile-wrap, #perm-view .pv-chip-host .cp-sw-name { display: none; }
#perm-view .pv-chip-host .cp-sw-labels { display: inline-flex; min-width: 0; }
#perm-view .pv-chip-host .cp-sw-sub, #perm-view .pv-chip-host .scope-pp-name {
  font: 500 11.5px var(--font-ui, inherit); color: #8b909a; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#perm-view .pv-chip-host .scope-pp-empty { color: #8b909a; font-weight: 500; }
#perm-view .pv-chip-host .scope-kind { display: none; }
#perm-view .pv-chip-host .scope-pill svg, #perm-view .pv-chip-host .scope-pp-btn svg { width: 9px; height: 9px; color: #6e7681; flex: none; }
#perm-view .pv-chip-host .scope-pop, #perm-view .pv-chip-host .scope-pp-pop { top: calc(100% + 6px); left: 0; z-index: 80; }
#perm-view .pv-chip.pv-on .scope-pp-name, #perm-view .pv-chip.pv-on .cp-sw-sub { color: #3794ff; }
#perm-view .pv-chip.pv-on .scope-pp-btn svg { display: none; }

#perm-view .pv-note { margin: 0; padding: 0 20px 10px; font-size: 11.5px; line-height: 1.5; color: #8b909a; max-width: none; min-height: 0; }
#perm-view .pv-note:empty, #perm-view .pv-note[hidden] { display: none; }
#perm-view .perm-status-error { color: #ff9b9b; }
#perm-view .perm-source-banner, #perm-view .perm-chain { margin: 0 20px 10px; }

/* Table grid — every row shares it. */
#perm-view .pv-thead, #perm-view .pv-add, #perm-view .pv-row {
  display: grid; grid-template-columns: 240px minmax(0,1fr) minmax(0,1.4fr) 92px; gap: 0 14px; align-items: center;
}
#perm-view .pv-thead {
  padding: 9px 20px; border-top: 1px solid rgba(255,255,255,.07); border-bottom: 1px solid rgba(255,255,255,.07);
  background: #0e1014; font: 600 9.5px var(--font-mono, monospace); letter-spacing: .09em; color: #6e7681; text-transform: uppercase;
}

/* Add row. */
#perm-view .pv-add { padding: 9px 20px; background: rgba(55,148,255,.05); }
#perm-view .pv-ac {
  position: relative; display: flex; align-items: center; gap: 7px; height: 32px; box-sizing: border-box; min-width: 0;
  padding: 0 10px; border-radius: 7px; background: #0b0c0f; border: 1px solid rgba(55,148,255,.35);
}
#perm-view .pv-al { flex: none; font: 600 9px var(--font-mono, monospace); letter-spacing: .08em; color: #6e7681; text-transform: uppercase; }
#perm-view .pv-al2 { flex: none; font: 600 8.5px var(--font-mono, monospace); letter-spacing: .06em; color: #8b909a; text-transform: uppercase; }
#perm-view .pv-av { display: inline-flex; align-items: center; gap: 7px; min-width: 0; overflow: hidden; white-space: nowrap; }
#perm-view .pv-av .pv-ava { width: 20px; height: 20px; }
#perm-view .pv-av .pv-name, #perm-view .pv-val { font: 500 12px var(--font-ui, inherit); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
#perm-view .pv-av .pv-badge { font-size: 8px; padding: 1px 4px; border-radius: 3px; }
#perm-view .pv-ph { font: 400 12px var(--font-ui, inherit); color: #5a6069; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#perm-view .pv-on-btn {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 7px; height: 100%; margin: 0; padding: 0;
  background: transparent; border: 0; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
#perm-view .pv-on-btn:focus-visible { outline: none; }
#perm-view .pv-ac-on .perm-on-pop { top: calc(100% + 6px); left: 0; z-index: 80; }
/* PERM ▾ popover (6a): roles, then the catalog keys by family with their short descriptions. */
#perm-view .pv-ac-perm .perm-perm-pop { top: calc(100% + 6px); left: 0; z-index: 80; width: 520px; }
#perm-view .perm-pp-head {
  padding: 8px 11px 4px; font: 600 9px var(--font-mono, monospace); letter-spacing: .09em; color: #6e7681; text-transform: uppercase;
}
#perm-view .perm-pp-roles {
  display: flex; flex-direction: column; gap: 1px; padding: 0 5px 6px; max-height: 150px; overflow: auto;
  border-bottom: 1px solid rgba(255,255,255,.07);
}
#perm-view .perm-pp-keys .pp { border: 0; border-radius: 0; background: transparent; }
#perm-view .perm-pp-keys .pp-list { max-height: 300px; }
#perm-view .perm-pp-keys .pp-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; padding-left: 12px; }
#perm-view .perm-pp-keys .pp-key { flex: none; }
#perm-view .perm-pp-keys .pp-name { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#perm-view .pv-grant {
  height: 32px; border: 0; border-radius: 7px; background: rgba(55,148,255,.3); color: rgba(255,255,255,.55);
  font: 600 11.5px var(--font-ui, inherit); cursor: pointer; padding: 0 10px;
}
#perm-view .pv-grant:not([disabled]):hover { background: rgba(55,148,255,.45); color: #fff; }
#perm-view .pv-grant[disabled] { cursor: default; }
#perm-view .pv-add2 {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 7px 20px 9px 274px;
  background: rgba(55,148,255,.05); border-bottom: 1px solid rgba(55,148,255,.25);
}
#perm-view .pv-al-gap { margin-left: 8px; }
#perm-view .pv-sel2 {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; box-sizing: border-box; padding: 0 9px;
  border-radius: 7px; background: #0b0c0f; border: 1px solid rgba(255,255,255,.12); font: 500 11.5px var(--font-ui, inherit);
  color: #e6e8ec; cursor: pointer; position: relative;
}
#perm-view .pv-sel2-dim { color: #8b909a; }
#perm-view .pv-sel2-off { cursor: not-allowed; opacity: .7; }
#perm-view .pv-sel2 .popchip-pop, #perm-view .pv-ac .popchip-pop { top: calc(100% + 6px); z-index: 80; }
#perm-view .pv-chip .popchip-pop { top: calc(100% + 6px); z-index: 80; }
#perm-view .pv-chip[role="button"], #perm-view .pv-ac[role="button"] { cursor: pointer; }
#perm-view .pv-na { font: 400 10px var(--font-mono, monospace); color: #5a6069; }
#perm-view .pv-astatus { font-size: 11.5px; color: #8b909a; }
#perm-view .pv-astatus:empty { display: none; }

/* Rows. */
#perm-view .perm-rows, #perm-view .perm-req-rows { display: block; max-width: none; gap: 0; margin: 0; padding: 0; border: 0; }
#perm-view .perm-req-row + .perm-req-row { border-top: 0; }
#perm-view .pv-row {
  padding: 10px 20px; margin: 0; background: transparent; border: 0; border-bottom: 1px solid rgba(255,255,255,.05);
  border-radius: 0; justify-content: initial;
}
#perm-view .pv-row:hover { background: rgba(255,255,255,.03); }
#perm-view .pv-row-hl { background: rgba(55,148,255,.08); box-shadow: inset 2px 0 0 #3794ff; }
#perm-view .pv-row-revoked { opacity: .5; }
#perm-view .pv-c { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; }
#perm-view .pv-c-perm { gap: 5px; flex-wrap: wrap; white-space: normal; }
#perm-view .pv-c-act { display: flex; align-items: center; justify-content: flex-end; gap: 10px; text-align: right; }
#perm-view .pv-ava {
  flex: none; display: grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
  font: 600 9px var(--font-ui, inherit);
}
#perm-view .pv-ava-p1 { background: #2a3550; color: #9db8ff; }
#perm-view .pv-ava-p2 { background: #3a2f22; color: #e6b96e; }
#perm-view .pv-ava-emp { background: rgba(197,134,192,.2); color: #c586c0; }
#perm-view .pv-name { font: 500 12.5px var(--font-ui, inherit); color: #f2f3f5; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
#perm-view .pv-unnamed .scope-id-sub { margin-left: 0; color: #6e7681; font: 400 11px var(--font-mono, monospace); }
/* WHO stays on one line: a long unnamed id ellipsizes, the PERSON/EMPLOYEE tag never drops below it. */
#perm-view .perm-c-who { flex-wrap: nowrap; }
#perm-view .perm-c-who > :not(.pv-name) { flex-shrink: 0; }
#perm-view .pv-badge { flex: none; font: 600 8px var(--font-mono, monospace); letter-spacing: .04em; padding: 1px 4px; border-radius: 3px; }
#perm-view .pv-badge-person { background: rgba(55,148,255,.15); color: #7fb0f0; }
#perm-view .pv-badge-emp { background: rgba(197,134,192,.15); color: #c586c0; }
#perm-view .pv-kind {
  flex: none; min-width: 62px; box-sizing: border-box; text-align: center; padding: 2px 6px; border-radius: 4px;
  background: rgba(255,255,255,.06); color: #8b909a; font: 600 9px var(--font-mono, monospace); letter-spacing: .06em;
}
#perm-view .pv-on-name { font: 500 12px var(--font-ui, inherit); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
#perm-view .pv-extra { font: 400 10.5px var(--font-mono, monospace); color: #6e7681; white-space: nowrap; }
#perm-view .pv-pill { font: 600 10.5px/1.4 var(--font-mono, monospace); padding: 2px 8px; border-radius: 5px; white-space: nowrap; }
#perm-view .pv-pill-role { background: rgba(55,148,255,.14); color: #3794ff; }
#perm-view .pv-pill-key { background: rgba(255,255,255,.07); color: #c4c8ce; }
#perm-view .pv-pill-pending { background: rgba(224,179,65,.13); color: #e0b341; }
#perm-view .pv-nkeys { font: 400 10px var(--font-mono, monospace); color: #6e7681; white-space: nowrap; }
#perm-view .pv-prov { flex: none; flex-basis: auto; font: 400 10px var(--font-mono, monospace); color: #5a6069; white-space: nowrap; }
#perm-view .perm-revoke, #perm-view .pv-review {
  background: none; border: 0; border-radius: 0; padding: 0; margin: 0; cursor: pointer; white-space: nowrap;
  font: 600 11px var(--font-ui, inherit); color: #ff9b9b;
}
#perm-view .pv-review { color: #3794ff; }
#perm-view .perm-revoke:hover, #perm-view .pv-review:hover { background: none; text-decoration: underline; }
#perm-view .perm-revoke[disabled] { opacity: .45; cursor: default; text-decoration: none; }
#perm-view .perm-revoked-tag { font: 600 10px var(--font-mono, monospace); color: #5a6069; }

/* 0c-1 — click anywhere on a row to expand it (no arrow); the open row and its container. */
#perm-view .pv-row[data-expand] { cursor: pointer; }
#perm-view .pv-row-open { background: rgba(255,255,255,.035); border-bottom-color: transparent; }
#perm-view .pv-row-open.pv-row-hl { background: rgba(55,148,255,.08); }
/* v2 grant chips — drawn only when the server sends the field. */
#perm-view .pv-chip-c {
  font: 500 10px/1.4 var(--font-mono, monospace); padding: 1px 7px; border-radius: 999px; white-space: nowrap;
  border: 1px solid rgba(255,255,255,.12); color: #c4c8ce;
}
#perm-view .perm-expires { border-color: rgba(224,179,65,.35); color: #e0b341; }
#perm-view .perm-expired { border-color: rgba(255,155,155,.35); color: #ff9b9b; }
#perm-view .perm-except { border-color: rgba(255,155,155,.3); color: #ff9b9b; }
/* 0d — an inherited grant's chips sit inside a dashed chip. */
#perm-view .pv-inh {
  display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; max-width: 100%;
  border: 1px dashed rgba(255,255,255,.22); border-radius: 7px; padding: 2px 6px; font-size: inherit;
}
#perm-view .perm-inh-arrow { color: #6e7681; flex: none; }
#perm-view .perm-more-wrap { position: relative; gap: 8px; overflow: visible; }
#perm-view .perm-src-btn { font: 600 11px var(--font-ui, inherit); color: #3794ff; }
#perm-view .perm-more-btn {
  background: none; border: 1px solid transparent; border-radius: 4px; padding: 0 5px; cursor: pointer;
  color: #8b909a; font: 600 13px/1.2 var(--font-ui, inherit);
}
#perm-view .perm-more-btn:hover, #perm-view .perm-more-btn[aria-expanded="true"] { border-color: rgba(255,255,255,.15); color: #e6e8ec; }
#perm-view .perm-more-pop[hidden] { display: none; }
#perm-view .perm-more-pop {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 30; width: 300px; text-align: left; white-space: normal;
  background: #1c1f24; border: 1px solid rgba(255,255,255,.12); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.45);
  padding: 4px; cursor: default;
}
#perm-view .perm-more-head { padding: 8px 10px; font: 400 11px/1.45 var(--font-ui, inherit); color: #8b909a; border-bottom: 1px solid rgba(255,255,255,.06); margin-bottom: 4px; }
#perm-view .perm-more-item {
  display: flex; flex-direction: column; gap: 2px; width: 100%; box-sizing: border-box; text-align: left;
  background: none; border: 0; border-radius: 5px; padding: 7px 10px; cursor: pointer; color: #e6e8ec;
  font: 500 12px var(--font-ui, inherit); text-decoration: none;
}
#perm-view .perm-more-item:hover { background: rgba(255,255,255,.06); text-decoration: none; }
#perm-view .perm-more-item[disabled] { opacity: .5; cursor: not-allowed; }
#perm-view .perm-more-item[disabled]:hover { background: none; }
#perm-view .perm-more-sub { font: 400 10.5px/1.4 var(--font-ui, inherit); color: #8b909a; }
/* The details container under an expanded row: KEY · WHAT IT ALLOWS | APPLIES TO · INHERITED VIA. */
#perm-view .pv-detail {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.2fr); gap: 0 28px;
  padding: 10px 20px 14px 52px; background: rgba(255,255,255,.035); border-bottom: 1px solid rgba(255,255,255,.05);
  cursor: default;
}
#perm-view .pv-d-col { min-width: 0; }
#perm-view .pv-d-head, #perm-view .pv-d-row { display: grid; grid-template-columns: minmax(0,200px) minmax(0,1fr); gap: 12px; align-items: center; }
#perm-view .pv-d-head3, #perm-view .perm-d-applies { grid-template-columns: minmax(0,1.3fr) minmax(0,1fr) auto; }
#perm-view .pv-d-head { padding: 4px 0 6px; border-bottom: 1px solid rgba(255,255,255,.06); font: 600 9.5px var(--font-mono, monospace); letter-spacing: .06em; color: #6e7681; }
#perm-view .pv-d-row { padding: 5px 0; border-bottom: 1px solid rgba(255,255,255,.03); min-height: 22px; }
#perm-view .pv-d-k { font: 500 11px var(--font-mono, monospace); color: #c4c8ce; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* PO 2026-10-11: the key's plain name, the code beneath it as small print. */
#perm-view .pv-d-k:has(.pv-d-kl) { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
#perm-view .pv-d-kl { font: 500 12px var(--font-ui, inherit); color: #e6e8ec; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#perm-view .pv-d-kc { font: 400 10px var(--font-mono, monospace); color: #6e7681; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#perm-view .pv-pill-key[data-key] { font-family: var(--font-ui, inherit); }
#perm-view .pv-d-v { font: 400 11.5px/1.4 var(--font-ui, inherit); color: #a8adb5; }
#perm-view .pv-d-none { color: #5a6069; font-style: italic; }
#perm-view .pv-d-node { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; }
#perm-view .pv-d-via { font: 400 10.5px var(--font-mono, monospace); color: #6e7681; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#perm-view .pv-d-here { color: #3794ff; }
#perm-view .pv-d-act { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
#perm-view .perm-d-exclude, #perm-view .perm-d-include {
  background: none; border: 1px solid rgba(255,255,255,.12); border-radius: 4px; padding: 1px 7px;
  font: 500 10.5px var(--font-ui, inherit); color: #a8adb5; cursor: pointer;
}
#perm-view .perm-d-exclude[disabled], #perm-view .perm-d-include[disabled] { opacity: .5; cursor: not-allowed; }
#perm-view .pv-d-struck .pv-on-name, #perm-view .pv-d-struck .pv-d-via { text-decoration: line-through; color: #5a6069; }
#perm-view .pv-d-exnote { font: 600 9.5px var(--font-mono, monospace); color: #ff9b9b; letter-spacing: .04em; }
#perm-view .pv-d-came { background: rgba(55,148,255,.07); }
#perm-view .pv-d-camefrom { font: 600 8.5px var(--font-mono, monospace); letter-spacing: .06em; color: #3794ff; border: 1px solid rgba(55,148,255,.4); border-radius: 3px; padding: 0 4px; }
#perm-view .pv-d-note { padding: 6px 0; font: 400 11px var(--font-ui, inherit); color: #8b909a; }
#perm-view .pv-d-foot { grid-column: 1 / -1; padding-top: 10px; font: 400 11px var(--font-ui, inherit); color: #8b909a; }
@media (max-width: 760px) {
  #perm-view .pv-detail { grid-template-columns: minmax(0,1fr); padding-left: 16px; padding-right: 16px; gap: 12px; }
}
#perm-view .perm-req-card { padding: 0 20px 12px; border-bottom: 1px solid rgba(255,255,255,.05); }
#perm-view .perm-empty { padding: 18px 20px; color: #8b909a; font-size: 12px; border-bottom: 1px solid rgba(255,255,255,.05); }

/* Footer. */
#perm-view .pv-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 20px;
  font: 400 10.5px var(--font-mono, monospace); color: #6e7681;
}
#perm-view .pv-lg-role { color: #3794ff; }
#perm-view .pv-lg-key { color: #c4c8ce; }
#perm-view .pv-lg-pending { color: #e0b341; }
#perm-view .pv-pager { display: flex; align-items: center; gap: 10px; padding: 0 20px 12px; font-size: 11.5px; color: #8b909a; }
#perm-view .pv-pager[hidden] { display: none; }

/* 0c-2: the ON picker popover. */
#perm-view .perm-on-pop {
  width: 462px; padding: 0; background: rgb(22,24,29); border: 1px solid rgba(255,255,255,.14);
  border-radius: 10px; box-shadow: 0 18px 44px rgba(0,0,0,.6); overflow: hidden;
}
#perm-view .perm-on-tabs {
  display: flex; flex-wrap: nowrap; gap: 1px; margin: 0; padding: 7px 8px; border-bottom: 1px solid rgba(255,255,255,.07);
}
#perm-view .perm-on-tab {
  border: 0; border-radius: 6px; background: transparent; color: #8b909a; padding: 4px 8px;
  font: 500 10.5px var(--font-ui, inherit);
}
#perm-view .perm-on-tab:hover { color: #c4c8ce; }
#perm-view .perm-on-tab.is-on { background: rgba(55,148,255,.16); color: #3794ff; font-weight: 600; }
#perm-view .perm-on-tab-na { opacity: 1; color: #5a6069; }
#perm-view .perm-on-search {
  display: flex; align-items: center; gap: 7px; margin: 0; padding: 8px 11px; border-bottom: 1px solid rgba(255,255,255,.07);
}
#perm-view .perm-on-search-i { flex: none; color: #6e7681; }
#perm-view .perm-on-search .perm-input {
  flex: 1; min-width: 0; border: 0; background: transparent; padding: 0; height: auto; box-shadow: none; outline: none;
  font: 500 12px var(--font-ui, inherit); color: #e6e8ec;
}
#perm-view .perm-on-count { margin-left: auto; font: 400 10px var(--font-mono, monospace); color: #6e7681; }
#perm-view .perm-on-list {
  display: flex; flex-direction: column; gap: 1px; padding: 5px; max-height: 300px; overflow: auto; border: 0; border-radius: 0;
}
#perm-view .perm-on-item {
  display: flex; align-items: center; gap: 9px; border: 0; border-radius: 7px; padding: 7px 10px; box-shadow: none;
  background: transparent; font: 500 12px var(--font-ui, inherit); color: #e6e8ec;
}
#perm-view .perm-on-item:hover { background: rgba(255,255,255,.04); }
#perm-view .perm-on-item.is-on { background: rgba(55,148,255,.14); box-shadow: none; }
#perm-view .perm-on-ico { width: auto; display: inline-flex; color: #7fb0f0; }
#perm-view .perm-on-child { padding: 6px 10px 6px 40px; font-size: 11.5px; color: #c4c8ce; }
#perm-view .perm-on-child .perm-on-ico { color: #38bdf8; }
#perm-view .perm-on-dt { margin-left: auto; font: 400 10px var(--font-mono, monospace); color: #6e7681; }
#perm-view .perm-on-ck { color: #3794ff; font-weight: 600; }
#perm-view .perm-on-hint {
  margin: 0; padding: 8px 11px; border-top: 1px solid rgba(255,255,255,.06); font: 400 10.5px var(--font-ui, inherit); color: #6e7681;
}
#perm-view .perm-on-empty { padding: 12px 10px; color: #8b909a; }

@media (max-width: 900px) {
  #perm-view .pv-filters { flex-wrap: wrap; }
  #perm-view .pv-thead { display: none; }
  #perm-view .pv-add, #perm-view .pv-row { grid-template-columns: 1fr; gap: 6px; }
  #perm-view .pv-add2 { padding-left: 20px; }
  #perm-view .pv-c-act { justify-content: flex-start; }
}

/* Permissions / Credentials / Audit / Roles — full-screen design (Team RBAC, PO 2026-10-02):
   rail, then the content directly — NO shell appbar. "Permissions" + the scope chip + count is
   the page header (shown once), the content sits flat on the page ground (no card border
   wrapped around the screen), and row rules run to the right edge. */
@media (min-width: 1024px) {
  body[data-screen="permissions"] .appbar, body[data-screen="credentials"] .appbar,
  body[data-screen="audit"] .appbar, body[data-screen="roles"] .appbar,
  body[data-screen="members"] .appbar { display: none; }
  body[data-screen="apikeys"] .appbar { display: none; }
}
body[data-screen="permissions"] .perm-view,
body[data-screen="credentials"] #cred-view.cred-view,
body[data-screen="audit"] .audit-view { padding: 16px 6px 40px; background: var(--bg); }
#perm-view .pv, #cred-view .cred-frame, .audit-view .audit-card {
  background: transparent; border: 0; border-radius: 0;
}
@media (max-width: 1023px) {
  body[data-screen="permissions"] .perm-view,
  body[data-screen="credentials"] #cred-view.cred-view,
  body[data-screen="audit"] .audit-view { padding: 4px 0 32px; }
}

/* ── widgets/popchip.js — the design's popover chip (Team Audit 1b-1..1b-4, Team RBAC 0a/0c).
   One look for every picker that opens under a chip: the screens keep their own chip classes
   for the resting / blue states and only the popover is drawn here. */
.popchip { position: relative; display: inline-flex; align-items: center; gap: 5px; height: 30px; box-sizing: border-box;
  padding: 0 9px; border-radius: 8px; border: 1px solid rgba(255,255,255,.1); background: transparent;
  color: #c4c8ce; font: 500 11.5px var(--font-ui, inherit); white-space: nowrap; flex: none; }
.popchip:hover { border-color: rgba(255,255,255,.18); }
.popchip-btn { display: inline-flex; align-items: center; gap: 5px; background: none; border: 0; padding: 0; margin: 0;
  color: inherit; font: inherit; cursor: pointer; }
.popchip-v { color: #8b909a; max-width: 180px; overflow: hidden; text-overflow: ellipsis; }
.popchip-caret { font-size: 9px; color: #6e7681; line-height: 1; }
.popchip-x { background: none; border: 0; padding: 0; margin: 0; color: inherit; font-size: 11px; line-height: 1; cursor: pointer; }
.popchip-x[hidden] { display: none; }
.popchip.is-on { background: rgba(55,148,255,.12); border-color: rgba(55,148,255,.4); color: #3794ff; }
.popchip.is-on .popchip-v { color: #3794ff; }
.popchip.is-on .popchip-caret { display: none; }
.popchip.is-open, .pv-chip.is-open, .audit-fbtn.is-open, .cred-chip.is-open, .pv-ac.is-open { border-color: rgba(55,148,255,.6); color: #3794ff; }
.popchip-trig { cursor: pointer; }
.popchip-pop[hidden] { display: none; }
.popchip-pop { position: absolute; top: 36px; left: 0; z-index: 5; width: 290px; box-sizing: border-box;
  background: rgb(22,24,29); border: 1px solid rgba(255,255,255,.14); border-radius: 10px;
  box-shadow: rgba(0,0,0,.55) 0 18px 44px; overflow: hidden; color: #c4c8ce; font: 400 12px var(--font-ui, inherit);
  white-space: normal; text-transform: none; letter-spacing: 0; cursor: default; text-align: left; }
.popchip-pop.popchip-narrow { width: 240px; }
.popchip-tabs { display: flex; gap: 2px; padding: 7px 8px; border-bottom: 1px solid rgba(255,255,255,.07); flex-wrap: wrap; }
.popchip-tab { background: none; border: 0; padding: 4px 8px; border-radius: 6px; color: #8b909a;
  font: 500 10.5px var(--font-ui, inherit); cursor: pointer; }
.popchip-tab.is-on { background: rgba(55,148,255,.16); color: #3794ff; font-weight: 600; }
.popchip-search { display: flex; align-items: center; gap: 7px; padding: 8px 11px; border-bottom: 1px solid rgba(255,255,255,.07); color: #6e7681; }
.popchip-search input { flex: 1; min-width: 0; background: none; border: 0; outline: none; padding: 0; color: #e6e8ec;
  font: 400 12px var(--font-ui, inherit); -webkit-appearance: none; appearance: none; }
.popchip-search input::placeholder { color: #5a6069; }
.popchip-search input::-webkit-search-cancel-button { display: none; }
.popchip-list { max-height: 300px; overflow-y: auto; padding: 4px; }
.popchip-group { padding: 6px 10px 2px; font: 600 9px var(--font-mono, monospace); letter-spacing: .1em; text-transform: uppercase; color: #5a6069; }
.popchip-item { display: flex; align-items: center; gap: 9px; width: 100%; box-sizing: border-box; padding: 6px 10px;
  border-radius: 6px; background: none; border: 0; color: #e6e8ec; font: 500 12px var(--font-ui, inherit); text-align: left; cursor: pointer; }
.popchip-item:hover { background: rgba(255,255,255,.04); }
.popchip-list:not([aria-multiselectable]) .popchip-item { padding: 7px 10px; color: #c4c8ce; }
.popchip-list:not([aria-multiselectable]) .popchip-item.is-on { background: rgba(55,148,255,.14); color: #f2f3f5; }
.popchip-child { padding-left: 32px; }
.popchip-box { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px;
  box-sizing: border-box; border-radius: 4px; border: 1.5px solid #5a6069; font-size: 10px; line-height: 1; color: #fff; }
.popchip-item.is-on .popchip-box { background: #3794ff; border-color: #3794ff; }
.popchip-av { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
  border-radius: 50%; background: rgba(55,148,255,.18); color: #79b8ff; font: 600 8px var(--font-ui, inherit); text-transform: uppercase; }
.popchip-av-employee { background: rgba(197,134,192,.2); color: #c586c0; }
.popchip-av-system { background: rgba(255,255,255,.08); color: #8b909a; }
.popchip-ico { flex: none; display: inline-flex; color: #8b909a; }
/* qc964 #1: the subtitle gives way first — a long place line used to keep its full width and
   squeeze the name to zero (the Who picker's Employees rows showed only the chip). */
.popchip-nm { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.popchip-dt { flex: 0 1000 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: #6e7681; font-size: 11px; white-space: nowrap; }
.popchip-tag { flex: none; padding: 1px 5px; border-radius: 4px; font: 600 8px var(--font-mono, monospace); letter-spacing: .06em;
  text-transform: uppercase; background: rgba(55,148,255,.15); color: #79b8ff; }
.popchip-tag-employee { background: rgba(197,134,192,.15); color: #c586c0; }
.popchip-tag-red { background: rgba(255,107,107,.13); color: #ff9b9b; }
.popchip-fill { flex: 1; }
.popchip-n { color: #6e7681; font: 400 10px var(--font-mono, monospace); }
.popchip-ck { color: #3794ff; font-size: 11px; }
.popchip-pill { display: inline-block; padding: 2px 7px; border-radius: 5px; font: 600 10px var(--font-mono, monospace); letter-spacing: .03em; }
.popchip-pill { background: rgba(255,255,255,.07); color: #c4c8ce; }
.popchip-pill-green { color: #46d19e; }
.popchip-pill-red { color: #ff9b9b; }
.popchip-pill-amber { color: #e0b341; }
.popchip-pill-blue { color: #7fb0f0; }
.popchip-empty { padding: 10px 12px; color: #6e7681; font-size: 11.5px; }
.popchip-custom { display: flex; flex-direction: column; gap: 6px; padding: 8px 11px; border-top: 1px solid rgba(255,255,255,.06); }
.popchip-custom-k { font: 600 9px var(--font-mono, monospace); letter-spacing: .1em; text-transform: uppercase; color: #5a6069; }
.popchip-custom-row { display: flex; align-items: center; gap: 6px; color: #5a6069; }
.popchip-custom input { flex: 1; min-width: 0; height: 30px; box-sizing: border-box; padding: 0 7px; border-radius: 7px;
  border: 1px solid rgba(255,255,255,.12); background: #0b0c0f; color: #e6e8ec; font: 500 11.5px var(--font-mono, monospace); color-scheme: dark; }
.popchip-foot { display: flex; align-items: center; gap: 8px; padding: 8px 11px; border-top: 1px solid rgba(255,255,255,.06); }
.popchip-note { color: #6e7681; font: 400 10.5px var(--font-ui, inherit); }
.popchip-clear, .popchip-apply { background: none; border: 0; padding: 0; color: #8b909a; font: 600 11px var(--font-ui, inherit); cursor: pointer; }
.popchip-apply { color: #3794ff; }

/* Schedule detail + work task badges (task contract 2026-10-08): the template a schedule's runs
   materialize, each run with its own fresh tasks, and the badges a task reads with anywhere. */
.agent-sched .sched-tpl { color: var(--muted); }
.sched-detail { margin-top: var(--s-2); padding: var(--s-2); border-left: 2px solid var(--border, rgba(127,127,127,.35)); font-size: var(--t-xs); display: flex; flex-direction: column; gap: 4px; }
.sched-detail-title { font-weight: 600; color: var(--fg-strong); margin-top: 4px; }
.sched-detail-empty, .sched-detail-loading { color: var(--faint); margin: 0; }
.sched-detail-error { color: var(--red, #d55); }
.sched-tpl-list, .sched-run-list, .sched-run-tasks { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 2px; }
.sched-run-list { list-style: none; padding-left: 0; gap: 6px; }
.sched-run-tasks { list-style: none; padding-left: 12px; }
.sched-tpl-steps, .sched-tpl-deps, .sched-run-when { color: var(--muted); }
.sched-run-head, .work-task { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.work-task-title { color: var(--fg-strong); }
.task-badge { display: inline-block; padding: 0 6px; border-radius: var(--r-sm); border: 1px solid var(--border, rgba(127,127,127,.35)); font-size: var(--t-xs); line-height: 1.5; color: var(--muted); }
.task-state-running { color: var(--accent, #4a8); border-color: currentColor; }
.task-state-blocked, .task-state-failed { color: var(--red, #d55); border-color: currentColor; }
.task-state-in_review { color: var(--amber, #c90); border-color: currentColor; }
.task-state-done { color: var(--green, #3a7); border-color: currentColor; }
.task-state-cancelled { text-decoration: line-through; }
.task-source-schedule { border-style: dashed; }
.task-blocked { color: var(--red, #d55); }

/* ── controllers/members.js — Members (#/members, PO 2026-10-10). The Roles screen's shell
   (.roles-view/.roles-head/.roles-btn) with the design's "Share workspace" rows: an initial
   avatar, name over email, mono role badges, a source pill and the row's actions. */
.members-view .mem-body { flex: 1; min-height: 0; overflow: auto; padding: 14px 22px 40px; display: flex; flex-direction: column; gap: 8px; }
.mem-sec { margin-top: 14px; font: 600 9.5px/1 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: #6e7681; }
.mem-invite { display: flex; flex-direction: column; gap: 8px; max-width: 760px; }
.mem-invite .mem-sec { margin-top: 0; }
.mem-invite-row { display: flex; gap: 8px; flex-wrap: wrap; }
.mem-input { flex: 1 1 240px; min-width: 0; height: 30px; padding: 0 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: #0e1014; color: var(--fg-strong); font: 400 12.5px var(--font-ui); outline: none; }
.mem-input:focus, .mem-select:focus { border-color: #3794ff; }
.mem-select { height: 30px; padding: 0 8px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: #0e1014; color: #e6e8ec; font: 500 12px var(--font-ui); max-width: 220px; }
.mem-hint { font: 400 11px var(--font-ui); color: #6e7681; }
.mem-pick-wrap { position: relative; flex: 1 1 240px; min-width: 0; display: flex; }
.mem-pick-wrap .mem-input { flex: 1 1 auto; }
.mem-pick { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 30; max-height: 280px; overflow-y: auto; padding: 4px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: #15181d; box-shadow: 0 8px 24px rgba(0,0,0,.45); }
.mem-pick-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border: 0; border-radius: 6px; background: transparent; color: var(--fg-strong); text-align: left; cursor: pointer; font: 400 12.5px var(--font-ui); }
.mem-pick-row:hover:not([aria-disabled="true"]) { background: rgba(55,148,255,.14); }
.mem-pick-row[aria-disabled="true"] { cursor: default; opacity: .55; }
.mem-pick-who { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.mem-pick-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The email tells same-named people apart: it wraps rather than being cut, at any width. */
.mem-pick-email { font: 400 11.5px var(--font-mono, monospace); color: #aeb6c0; white-space: normal; overflow-wrap: anywhere; }
.mem-pick-tag { flex: none; font: 500 10.5px var(--font-mono, monospace); color: #8b949e; border: 1px solid rgba(255,255,255,.12); border-radius: 4px; padding: 1px 6px; }
.mem-pick-empty { padding: 8px; font: 400 11.5px var(--font-ui); color: #8b949e; }
/* "N more — keep typing": pinned to the bottom of the scrolling list, so it shows without scrolling. */
.mem-pick-more { position: sticky; bottom: -4px; margin: 4px -4px -4px; padding: 8px 12px; background: #1b1f26; border-top: 1px solid rgba(255,255,255,.1); color: #c9d1d9; }
.mem-dir-note { color: #d29922; }
.mem-list { display: flex; flex-direction: column; border-top: 1px solid rgba(255,255,255,.07); }
.mem-row { display: grid; grid-template-columns: 32px minmax(160px, 1.4fr) minmax(120px, 1fr) auto minmax(0, auto); align-items: center; gap: 12px; padding: 10px 4px; border-bottom: 1px solid rgba(255,255,255,.07); }
.mem-avatar { width: 28px; height: 28px; border-radius: 50%; background: #3794ff; color: #fff; display: inline-flex; align-items: center; justify-content: center; font: 600 12px var(--font-ui); }
.mem-avatar-pending { background: rgba(255,255,255,.08); color: #8b909a; }
.mem-who { min-width: 0; }
.mem-name { font: 600 12.5px var(--font-ui); color: var(--fg-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mem-email { font: 400 11px var(--font-mono); color: #8b909a; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mem-warn { margin-top: 3px; font: 500 11px var(--font-ui); color: #f0b45a; white-space: normal; }
.mem-you { font: 500 10px var(--font-ui); color: #8b909a; }
.mem-owner { display: inline-block; padding: 1px 6px; border-radius: 4px; background: rgba(240,180,90,.14); color: #f0b45a; font: 700 9px var(--font-mono); letter-spacing: .06em; text-transform: uppercase; vertical-align: 1px; }
.mem-roles { display: flex; gap: 5px; flex-wrap: wrap; min-width: 0; }
.mem-role { display: inline-flex; align-items: center; gap: 5px; padding: 2px 6px; border-radius: 4px; background: rgba(255,255,255,.05); color: #c4c8ce; font: 500 9.5px var(--font-mono); }
.mem-role-sa { background: rgba(55,148,255,.14); color: #8cc0ff; }
.mem-src { color: #6e7681; font-weight: 400; }
.mem-srcpill { padding: 2px 7px; border-radius: 10px; font: 500 10.5px var(--font-ui); white-space: nowrap; background: rgba(255,255,255,.05); color: #8b909a; }
.mem-srcpill-xcorp { background: rgba(55,148,255,.12); color: #8cc0ff; }
.mem-srcpill-invited { background: rgba(80,200,120,.12); color: #7fd99f; }
.mem-srcpill-pending { background: rgba(240,180,90,.12); color: #f0b45a; }
.mem-actions { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
.mem-empty { padding: 12px 4px; font: 400 12px var(--font-ui); color: #6e7681; }
.mem-empty:empty { display: none; }
/* Admin API keys panel (controllers/adminkeys.js, PO 2026-10-10): every key of the company. */
.adk-scrim { position: fixed; inset: 0; z-index: 1150; display: grid; place-items: center; background: rgba(0, 0, 0, .55); }
.adk-panel { width: 980px; max-width: calc(100vw - 32px); max-height: calc(100vh - 64px); overflow: auto; padding: 16px 18px; display: flex; flex-direction: column; gap: 10px;
  background: #111318; border: 1px solid rgba(255, 255, 255, .12); border-radius: 14px; box-shadow: 0 24px 50px rgba(0, 0, 0, .6); }
.adk-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.adk-title { font-size: var(--t-md); font-weight: 600; color: var(--fg-strong); }
.adk-sub { margin-top: 3px; font: 400 11.5px var(--font-ui); color: #8b909a; }
.adk-link { border: 0; background: none; padding: 0; color: #8cc0ff; cursor: pointer; font: inherit; text-decoration: underline; }
.adk-list { overflow-x: auto; min-width: 0; }
.adk-table { width: 100%; border-collapse: separate; border-spacing: 0; font: 400 12px var(--font-ui); color: #c4c8ce; }
.adk-table th { text-align: left; padding: 6px 8px; font: 600 9.5px/1 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: #6e7681; border-bottom: 1px solid rgba(255,255,255,.07); white-space: nowrap; }
.adk-table td { padding: 8px; border-bottom: 1px solid rgba(255,255,255,.07); vertical-align: middle; white-space: nowrap; }
.adk-table td:first-child { white-space: normal; min-width: 140px; }
.adk-mono { font-family: var(--font-mono); }
.adk-table td:nth-child(2) { white-space: normal; min-width: 80px; overflow-wrap: anywhere; }
/* dates wrap to two lines rather than widen the table past the panel */
.adk-table td:nth-child(4), .adk-table td:nth-child(5), .adk-table td:nth-child(6) { white-space: normal; min-width: 84px; }
.adk-act { text-align: right; }
/* Revoke stays in view: the last column sticks to the right edge of the scrolling list. */
.adk-table th:last-child, .adk-table td.adk-act { position: sticky; right: 0; z-index: 1; background: #111318; box-shadow: -10px 0 10px -8px rgba(0, 0, 0, .7); }
/* QC E2E 0.11.19 LOW-9: a revoked key has no action, so its sticky cell is empty — but it still
   painted the #111318 block and the left shadow, a faint dark strip beside the "Revoked" pill.
   An empty action cell (and the header over an all-empty column) draws nothing. */
.adk-table td.adk-act:empty { background: transparent; box-shadow: none; }
.adk-gone { font: 500 10px var(--font-ui); color: #f0b45a; }
.adk-former { color: #8b909a; font-style: italic; cursor: help; }
/* The narrow layout (≤760px): one card per key; the table and its sticky column are wide-only. */
.adk-cards { display: none; list-style: none; margin: 0; padding: 0; }
.adk-card { display: flex; flex-direction: column; gap: 6px; padding: 10px 2px; border-bottom: 1px solid rgba(255,255,255,.07); font: 400 12px var(--font-ui); color: #c4c8ce; }
.adk-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.adk-card-who { min-width: 0; overflow-wrap: anywhere; }
.adk-card-key { overflow-wrap: anywhere; }
.adk-card-key .adk-mono { color: #8b909a; }
.adk-card-meta { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 0; }
.adk-card-meta dt { font: 600 9px/1.2 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: #6e7681; }
.adk-card-meta dd { margin: 2px 0 0; font: 400 11.5px/1.35 var(--font-ui); color: #c4c8ce; }
.adk-card-act { display: flex; justify-content: flex-end; }
.adk-status { padding: 2px 7px; border-radius: 10px; font: 500 10.5px var(--font-ui); background: rgba(255,255,255,.05); color: #8b909a; }
.adk-status-active { background: rgba(80,200,120,.12); color: #7fd99f; }
.adk-status-revoked { background: rgba(255,107,107,.12); color: #ff9b9b; }
.adk-status-expired { background: rgba(240,180,90,.12); color: #f0b45a; }
.adk-badge { margin-left: 4px; padding: 1px 6px; border-radius: 4px; border: 0; cursor: pointer; background: rgba(55,148,255,.14); color: #8cc0ff; font: 600 9.5px var(--font-mono); vertical-align: 1px; }
.adk-badge[hidden] { display: none; }
.perm-status-bad { color: #ff9b9b; }
@media (max-width: 760px) {
  .mem-row { grid-template-columns: 28px minmax(0, 1fr); }
  .mem-roles, .mem-srcpill, .mem-actions { grid-column: 2; justify-content: flex-start; }
}
/* ── controllers/apikeys.js — My API keys (#/account/api-keys, PO 2026-10-10). The Members
   shell (.roles-view + .mem-* inputs) with a create form, a one-time reveal panel and key rows. */
.apikeys-view .ak-body { flex: 1; min-height: 0; overflow: auto; padding: 14px 22px 40px; display: flex; flex-direction: column; gap: 8px; }
.ak-intro { margin: 0; max-width: 760px; font: 400 12px/1.5 var(--font-ui); color: #8b909a; }
.ak-form, .ak-reveal { display: flex; flex-direction: column; gap: 10px; max-width: 760px; padding: 14px; border-radius: 10px; border: 1px solid rgba(255,255,255,.08); background: rgba(255,255,255,.02); }
.ak-form[hidden], .ak-reveal[hidden] { display: none; }
.ak-form .mem-sec { margin-top: 0; }
.ak-field { display: flex; flex-direction: column; gap: 4px; }
.ak-field .mem-input { flex: 0 0 auto; }
.ak-label { font: 500 11px var(--font-ui); color: #c4c8ce; }
.ak-textarea { height: auto; min-height: 52px; padding: 6px 10px; font-family: var(--font-mono); resize: vertical; }
.ak-adv summary { cursor: pointer; font: 500 11.5px var(--font-ui); color: #8cc0ff; }
.ak-adv[open] summary { margin-bottom: 8px; }
.ak-form-err { font: 500 11.5px var(--font-ui); color: #ff9b9b; }
.ak-form-err:empty { display: none; }
.ak-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.ak-reveal { border-color: rgba(240,180,90,.35); background: rgba(240,180,90,.05); }
.ak-reveal-head { font: 600 13px var(--font-ui); color: var(--fg-strong); }
.ak-warn { font: 500 12px/1.45 var(--font-ui); color: #f0b45a; }
.ak-secret { display: flex; flex-direction: column; gap: 4px; }
.ak-secret-row { display: flex; gap: 8px; }
.ak-secret-input { font-family: var(--font-mono); font-size: 12px; }
.ak-list { display: flex; flex-direction: column; border-top: 1px solid rgba(255,255,255,.07); }
/* Every row is its own grid, so `auto` tracks sized per row: a revoked row (no Revoke button) and an
   active one put Created/Expires/Last used at different x (QC 0.11.10). Fixed tracks line them up. */
.ak-row { display: grid; grid-template-columns: minmax(160px, 1.2fr) minmax(0, 2fr) 88px 96px; align-items: center; gap: 12px; padding: 10px 4px; border-bottom: 1px solid rgba(255,255,255,.07); }
.ak-who { min-width: 0; }
.ak-meta { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin: 0; min-width: 0; }
.ak-row .mem-srcpill { justify-self: start; }
.ak-meta dt { font: 600 9px/1.2 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: #6e7681; }
.ak-meta dd { margin: 2px 0 0; font: 400 11.5px var(--font-ui); color: #c4c8ce; white-space: nowrap; }
.ak-st-active { background: rgba(80,200,120,.12); color: #7fd99f; }
.ak-st-expired { background: rgba(240,180,90,.12); color: #f0b45a; }
.ak-st-revoked { background: rgba(255,120,120,.10); color: #ff9b9b; }
.ak-row-revoked .mem-name, .ak-row-expired .mem-name { color: #8b909a; }
.ak-empty { padding: 12px 4px; font: 400 12px var(--font-ui); color: #c4c8ce; }
.ak-empty p { margin: 0 0 4px; }
@media (max-width: 760px) {
  .apikeys-view .ak-body { padding: 12px 16px 32px; }
  .ak-row { grid-template-columns: minmax(0, 1fr) auto; }
  .ak-meta { grid-column: 1 / -1; gap: 12px; }
  .ak-row .mem-srcpill { grid-row: 1; grid-column: 2; }
  .ak-row .mem-actions { grid-column: 1 / -1; justify-content: flex-start; }
  .ak-secret-row { flex-direction: column; }
  /* stacked: .mem-input's 240px flex-basis would become its HEIGHT — size the field to its line */
  .ak-secret-row .ak-secret-input { flex: 0 0 auto; }
  .adk-panel { padding: 12px; }
  .adk-list .adk-table { display: none; }
  .adk-list .adk-cards { display: block; }
}
/* A modal body that fits its content stops clipping while a popover is open in it
   (widgets/popchip.js unclipModal) — the popover then uses the viewport, not the dialog. */
.popchip-unclip { overflow: visible !important; }

/* Grouped workspace views (PO 2026-10-10) — the tab strip above Access (Permissions | Credentials)
   and People (Members | Roles). Painted by widgets/railgroups.js; the views below are unchanged. */
.group-tabs {
  flex: none; display: flex; align-items: flex-end; gap: 4px;
  padding: 8px 22px 0; background: #0b0c0f; border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
}
.group-tabs[hidden] { display: none; }
.group-tab {
  flex: none; position: relative; border: 0; background: transparent; cursor: pointer;
  padding: 8px 14px 10px; font: inherit; font-size: 13px; font-weight: 500; color: var(--muted);
  border-radius: 6px 6px 0 0;
}
.group-tab:hover { color: var(--fg-2); background: rgba(255, 255, 255, 0.03); }
.group-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.group-tab.active { color: var(--fg-strong); }
.group-tab.active::after {
  content: ''; position: absolute; left: 10px; right: 10px; bottom: -1px; height: 2px;
  border-radius: 2px; background: var(--accent);
}
@media (max-width: 720px) {
  .group-tabs { padding: 6px 12px 0; }
  .group-tab { min-height: 44px; }
}

/* ── "Waiting for you" (controllers/requests.js, PO 2026-10-11) ───────────────────────────────────
   The count of cards an employee waits on a person for: a rail badge on every page, the same number
   on the phone menu button, a Home banner, a number on each waiting employee's sidebar row, and a
   short list whose rows jump to the card (which flashes). Red = the awaiting state colour. */
.rail-badge-ask { top: 3px; right: 2px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px;
  background: var(--state-awaiting); color: #0b0c0f; font: 700 9.5px/16px var(--font-mono); }
/* QC 961 (390px): the toggle sits at top:2px, so top:-4px put the badge 2px above the viewport and
   clipped its top edge. It hangs off the right edge only. */
.mob-nav-toggle .rail-badge-ask { top: 0; right: -4px; }
.requests-pop {
  position: fixed; z-index: 72; left: 64px; top: 80px; width: 380px; max-width: calc(100vw - 32px); max-height: 70vh;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: var(--r-md); box-shadow: var(--shadow);
}
.requests-pop[hidden] { display: none; }
.req-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-3); border-bottom: 1px solid var(--border); }
.req-title { font-family: var(--font-mono); font-size: var(--t-label); letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.req-close { border: 0; background: transparent; color: var(--muted); font-size: 18px; line-height: 1; width: 32px; height: 32px;
  border-radius: var(--r-sm); cursor: pointer; }
.req-close:hover, .req-close:focus-visible { background: var(--accent-wash); color: var(--fg); outline: none; }
.req-list { list-style: none; margin: 0; padding: var(--s-1); overflow-y: auto; }
.req-row { display: flex; align-items: flex-start; gap: var(--s-2); width: 100%; min-width: 0; text-align: left; cursor: pointer;
  padding: var(--s-2); border: 0; border-radius: var(--r-sm); background: transparent; color: var(--fg); font: inherit; }
.req-row:hover, .req-row:focus-visible { background: var(--accent-wash); outline: none; }
.req-row.req-held { opacity: .6; }
/* QC 965: the chip wraps to a second line instead of cutting "Permission to approve" to "Permission to …". */
.req-kind { flex: none; max-width: 104px; padding: 2px 6px; border-radius: 6px; font-size: var(--t-xs); font-weight: 600;
  background: var(--state-awaiting-glow); color: var(--state-awaiting-text); white-space: normal; overflow-wrap: break-word; line-height: 1.3; }
.req-kind-question { background: rgba(224, 179, 65, .16); color: var(--state-thinking); }
.req-kind-cliSignIn, .req-kind-cliConsent { background: var(--accent-wash); color: var(--accent); }
.req-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.req-who { font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.req-what { font-size: var(--t-xs); color: var(--fg); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; word-break: break-word; }
.req-where { font-size: var(--t-xs); color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.req-when { flex: none; font-size: var(--t-xs); color: var(--muted); font-family: var(--font-mono); white-space: nowrap; }
.req-empty { padding: var(--s-4) var(--s-3); color: var(--muted); font-size: var(--t-sm); }
/* Flow E2: a result waiting for my review — the panel a "review" row opens inside the same popover. */
.req-kind-review { background: var(--accent-wash); color: var(--accent); }
.req-back { border: 0; background: transparent; color: var(--muted); font-size: 18px; line-height: 1; width: 32px; height: 32px;
  border-radius: var(--r-sm); cursor: pointer; }
.req-back:hover, .req-back:focus-visible { background: var(--accent-wash); color: var(--fg); outline: none; }
.rev-body { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-3); overflow-y: auto; min-width: 0; }
.rev-task-title { font-weight: 600; word-break: break-word; }
.rev-line { display: flex; gap: var(--s-2); font-size: var(--t-sm); min-width: 0; }
.rev-label { flex: none; min-width: 96px; color: var(--muted); font-size: var(--t-xs); }
.rev-value { min-width: 0; word-break: break-word; }
.rev-section { display: flex; flex-direction: column; gap: 4px; }
.rev-confirm { align-self: flex-start; padding: 1px 6px; border-radius: 6px; font-size: var(--t-xs);
  background: rgba(224, 179, 65, .16); color: var(--state-thinking); }
.rev-evidence, .rev-outcome { font-size: var(--t-sm); white-space: pre-wrap; word-break: break-word; }
.rev-outcome { color: var(--muted); }
.rev-file { border: 0; padding: 0; background: transparent; color: var(--accent); font: inherit; text-decoration: underline; cursor: pointer; }
.rev-link { color: var(--accent); }
.rev-rounds { margin: 0; padding-left: 18px; font-size: var(--t-xs); }
.rev-note, .rev-old-evidence { color: var(--muted); white-space: pre-wrap; word-break: break-word; }
.rev-note-input { width: 100%; box-sizing: border-box; resize: vertical; font: inherit; font-size: var(--t-sm); padding: var(--s-2);
  background: var(--bg); color: var(--fg); border: 1px solid var(--border-strong); border-radius: var(--r-sm); }
.rev-error { color: var(--state-awaiting-text); font-size: var(--t-xs); }
.rev-actions { display: flex; justify-content: flex-end; gap: var(--s-2); flex-wrap: wrap; }
.rev-btn { min-height: 36px; padding: 0 14px; border-radius: var(--r-sm); border: 1px solid var(--border-strong); background: transparent;
  color: var(--fg); font: inherit; cursor: pointer; }
.rev-btn:disabled { opacity: .5; cursor: default; }
.rev-accept { background: var(--accent); border-color: var(--accent); color: #0b0c0f; font-weight: 600; }
.rev-result { padding: var(--s-2); border-radius: var(--r-sm); background: var(--accent-wash); font-size: var(--t-sm); }
li.agent-row[data-session-id]::after { flex: none; margin-left: auto; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px;
  background: var(--state-awaiting); color: #0b0c0f; font: 700 9.5px/16px var(--font-mono); text-align: center; }
@keyframes reqFlash { 0%, 100% { box-shadow: 0 0 0 0 transparent; } 20%, 60% { box-shadow: 0 0 0 3px var(--state-awaiting); } }
.tcard.req-flash, .req-flash { animation: reqFlash 1.2s ease-in-out 2; }
@media (max-width: 719px) {
  .requests-pop { left: 16px !important; right: 16px; top: 56px !important; width: auto; max-width: none; max-height: calc(100dvh - 72px); }
  .req-row { min-height: 44px; }
  .rev-btn { min-height: 44px; flex: 1; }
}

/* fxAI — the receipt panel (widgets/receiptpanel.js): one line per recipient, in plain words. */
.rcpt-panel { font-size: var(--t-xs); color: var(--muted); }
.rcpt-title { font-weight: 600; color: var(--fg); margin-bottom: 2px; }
.rcpt-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.rcpt-who { color: var(--fg-strong); font-weight: 600; }
.rcpt-why { color: var(--faint); }
.rcpt-line[data-receipt-state="delivered"] .rcpt-state, .rcpt-line[data-receipt-state="stopped"] .rcpt-state { color: var(--green); }
.rcpt-line[data-receipt-state="refused"] .rcpt-state, .rcpt-line[data-receipt-state="failed"] .rcpt-state { color: var(--red, #e06c75); }
.rcpt-line[data-receipt-state="queued_stopped"] .rcpt-state, .rcpt-line[data-receipt-state="unknown"] .rcpt-state { color: var(--warn, #e0b341); }
.rcpt-line[data-receipt-state="pending"] .rcpt-state, .rcpt-line[data-receipt-state="queued"] .rcpt-state { color: var(--accent); }
.cli-receipt { padding: 4px 2px 0; border-top: 1px solid var(--border); }
.cli-receipt[hidden] { display: none; }
/* fxAI: a team Resume in flight — STARTING… on the card, per-member receipt under the team head. */
.roster-state-lbl.st-starting, .roster-status.roster-starting { color: var(--accent); }
.roster-status.roster-starting::before { content: '◌ '; font-style: normal; }
/* fxAN: one employee's Stop / Restart with a receipt — STOPPING… / RESTARTING… until it settles. */
.roster-state-lbl.st-stopping, .roster-status.roster-stopping { color: var(--amber, #d9a441); }
.roster-state-lbl.st-restarting, .roster-status.roster-restarting { color: var(--accent); }
.roster-status.roster-stopping::before, .roster-status.roster-restarting::before { content: '◌ '; font-style: normal; }
.roster-status.roster-restarted { color: var(--green, #46d19e); }
.roster-status.roster-restarted::before { content: '✓ '; }
.roster-teamop { margin: 4px 8px 6px; padding: 6px 8px; border: 1px solid var(--border, rgba(255,255,255,.08)); border-radius: 6px; display: flex; gap: 8px; align-items: flex-start; justify-content: space-between; }
.roster-teamop-dismiss { background: none; border: 0; color: var(--muted); cursor: pointer; font-size: var(--t-xs); padding: 0; }
.rcpt-line[data-receipt-state="running"] .rcpt-state { color: var(--green); }
.rcpt-line[data-receipt-state="starting"] .rcpt-state { color: var(--accent); }
/* ── QC 959 (fxAM): permission names never cut ─────────────────────────────────────────────
   The picker row is name · code · description on ONE line, with the name and description
   ellipsised — at 390px that left "Appro…", at 1440 "Record virtual machi…". The plain name is
   what a person reads, so it WRAPS and takes the first line; the code and description sit
   beneath it. On a phone the description is dropped (the name already says it). */
.pp-row { flex-wrap: wrap; row-gap: 1px; }
.pp-name,
#perm-view .perm-pp-keys .pp-name { flex: 1 1 auto; min-width: 0; overflow: visible; text-overflow: clip; white-space: normal; overflow-wrap: anywhere; }
.pp-desc,
#perm-view .perm-pp-keys .pp-desc { order: 5; flex: 1 0 100%; margin-left: 0; padding-left: 0; text-align: left;
  overflow: visible; text-overflow: clip; white-space: normal; }
@media (max-width: 640px) {
  .pp-code { order: 4; flex: 1 0 100%; }
  .pp-desc, #perm-view .perm-pp-keys .pp-desc { display: none; }
}
/* The role filter's options read "Same permissions as role X (version N)": the 290px pop with
   a nowrap ellipsis cut the version off. This pop is wider and its names wrap. */
#perm-f-role-pop { width: min(440px, calc(100vw - 32px)); }
#perm-f-role-pop .popchip-nm { white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }
/* QC 968 #5: at 390px the Who picker's employee names were cut to "team-le…" — the EMPLOYEE badge and
   the place line took the row. The name owns the first line and wraps; the place drops to a line below
   and wraps too (QC 971 #2: "api-test / api-test-team · MacBook-Pro-4…"). */
#perm-a-who-pop { max-width: calc(100vw - 32px); }
#perm-a-who-pop .popchip-item { flex-wrap: wrap; row-gap: 2px; }
#perm-a-who-pop .popchip-nm { flex: 1 1 0; white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }
#perm-a-who-pop .popchip-dt { order: 1; flex: 1 0 100%; padding-left: 27px; box-sizing: border-box;
  white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }
#perm-a-who-pop .popchip-fill { display: none; }
.rcpt-line[data-receipt-state="restarted"] .rcpt-state { color: var(--green); }
.rcpt-line[data-receipt-state="restarting"] .rcpt-state { color: var(--accent); }
