/* ============================================================================
   Clearview "Console" — OBS Monitor
   ----------------------------------------------------------------------------
   Requires tokens/tokens.css + css/clearview-refresh.css.
   Namespaced `cvo-`. Shares the app shell with the Media page (`cvm-shell`,
   `cvm-side`, `cvm-head`, `cvm-body` in media.css).

   THE PROBLEM THIS PAGE HAS
   The app connects to 200+ OBS instances. The current UI offers exactly one
   card shape and a column count, so at fleet scale an expanded card carries up
   to 10 stat tiles + 2 VU tracks + a preview + 8 buttons, and 24 machines is
   already several hundred live DOM nodes. There is no way to ask "which
   machines are in trouble" — the five-colour legend explains the dots but
   filters nothing.

   THE RESPONSE
   1. The legend becomes the filter (`.cv-stat` triage strip).
   2. Three densities, not one: Wall (watch), Cards (work), Compact (scan 200).
   3. Alerts are stated in words, not just implied by a colour.
   ============================================================================ */

/* ============================================================ status vocabulary
   Five states, from obsStatusDotColor() at obs-monitor.js:2697. Rec+Stream was
   a hardcoded #ec4899 in five separate places; it becomes a token so the whole
   set can be re-tuned at once and so it re-themes. */
:root {
  --cvo-offline:   var(--cv-neutral);
  --cvo-connected: var(--cv-success);
  --cvo-streaming: var(--cv-warning);
  --cvo-recording: var(--cv-danger);
  --cvo-both:      #EC4899;                 /* rec + stream */
}
[data-theme="light"] { --cvo-both: #BE185D; }   /* 5.9:1 on white */

.cvo-dot { width: 8px; height: 8px; border-radius: var(--cv-radius-full); flex: none; }
.cvo-dot[data-s="offline"]   { background: var(--cvo-offline); }
.cvo-dot[data-s="connected"] { background: var(--cvo-connected); }
.cvo-dot[data-s="streaming"] { background: var(--cvo-streaming); animation: cv-pulse 1.6s var(--cv-ease) infinite; }
.cvo-dot[data-s="recording"] { background: var(--cvo-recording); animation: cv-pulse 1.6s var(--cv-ease) infinite; }
.cvo-dot[data-s="both"]      { background: var(--cvo-both);      animation: cv-pulse 1.6s var(--cv-ease) infinite; }
/* The badge glyph pulses on any live output, so the meaning is "this machine is
   putting something out right now" rather than "this one is red". Connected and
   offline stay still — if everything pulses, nothing does. */
.cvo-prev-badge[data-s="recording"] .cv-i,
.cvo-prev-badge[data-s="streaming"] .cv-i,
.cvo-prev-badge[data-s="both"] .cv-i { animation: cv-pulse 1.6s var(--cv-ease) infinite; }
.cvo-prev-badge[data-s="streaming"] { color: #FFC97A; }
@media (prefers-reduced-motion: reduce) {
  .cvo-dot, .cvo-prev-badge .cv-i { animation: none; }
}

/* Left rail on a card or row — the same verdict at a glance from across a room. */
.cvo-rail-offline   { box-shadow: inset 3px 0 0 var(--cvo-offline); }
.cvo-rail-connected { box-shadow: inset 3px 0 0 var(--cvo-connected); }
.cvo-rail-streaming { box-shadow: inset 3px 0 0 var(--cvo-streaming); }
.cvo-rail-recording { box-shadow: inset 3px 0 0 var(--cvo-recording); }
.cvo-rail-both      { box-shadow: inset 3px 0 0 var(--cvo-both); }

/* Triage tiles inherit .cv-stat; only the accent per state is local. */
.cv-stat[data-s="connected"] .cv-stat-n { color: var(--cvo-connected); }
.cv-stat[data-s="recording"] .cv-stat-n { color: var(--cvo-recording); }
.cv-stat[data-s="streaming"] .cv-stat-n { color: var(--cvo-streaming); }
.cv-stat[data-s="both"]      .cv-stat-n { color: var(--cvo-both); }
.cv-stat[data-s="offline"]   .cv-stat-n { color: var(--cvo-offline); }
.cv-stat[data-s="alert"]     .cv-stat-n,
.cv-stat[data-s="alert"] .cv-stat-l .cv-i { color: var(--cv-warning); }

/* ==================================================================== grid */

.cvo-grid { display: grid; gap: var(--cv-space-2); padding: var(--cv-space-3) 0; }
.cvo-grid[data-cols="auto"] { grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); }
.cvo-grid[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cvo-grid[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cvo-grid[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.cvo-grid[data-cols="6"] { grid-template-columns: repeat(6, minmax(0, 1fr)); }
/* Wall packs tighter — it is for watching, not for reading numbers. */
.cvo-grid[data-view="wall"][data-cols="auto"] { grid-template-columns: repeat(auto-fill, minmax(212px, 1fr)); }
.cvo-grid[data-view="compact"] { display: block; padding-top: var(--cv-space-2); }

.cvo-group-head {
  display: flex; align-items: center; gap: var(--cv-space-2);
  padding: var(--cv-space-3) 0 var(--cv-space-1);
  font-size: var(--cv-text-2xs); font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-disabled);
  grid-column: 1 / -1;
}

/* ================================================================= machine */

.cvo-m {
  position: relative;
  display: flex; flex-direction: column;
  border: 1px solid var(--cv-border);
  border-radius: var(--cv-radius-lg);
  background: var(--cv-bg-surface);
  overflow: hidden;
  transition: border-color var(--cv-dur-fast) var(--cv-ease);
}
.cvo-m:hover { border-color: var(--cv-border-strong); }
.cvo-m[aria-selected="true"] { border-color: var(--cv-accent); background: var(--cv-accent-wash); }
.cvo-m[data-alert="true"] { border-color: var(--cv-warning-edge); }
.cvo-m[data-s="offline"] { opacity: 0.62; }

.cvo-m-head {
  display: flex; align-items: center; gap: var(--cv-space-2);
  min-height: var(--cv-row); padding: 0 var(--cv-space-2);
  border-bottom: 1px solid var(--cv-border-subtle);
}
.cvo-m-name { font-size: var(--cv-text-xs); font-weight: var(--cv-weight-medium); }
.cvo-m-ip { font-family: var(--cv-font-mono); font-size: var(--cv-text-2xs); color: var(--cv-text-muted); }

/* Preview. 16:9 always reserved, so a frame arriving never shifts the grid. */
.cvo-prev {
  position: relative; aspect-ratio: 16 / 9;
  display: grid; place-items: center;
  background: var(--cv-bg-inset);
  color: var(--cv-text-disabled);
  overflow: hidden;
}
.cvo-prev-badge {
  position: absolute; top: var(--cv-space-1); left: var(--cv-space-1);
  display: flex; align-items: center; gap: 4px;
  padding: 1px 5px; border-radius: var(--cv-radius-sm);
  background: rgba(0, 0, 0, 0.66);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: #fff;
}
.cvo-prev-badge[data-s="recording"] { color: #FF6B6B; }
.cvo-prev-badge[data-s="both"] { color: #F9A8D4; }
.cvo-prev-time {
  position: absolute; right: var(--cv-space-1); bottom: var(--cv-space-1);
  padding: 0 4px; border-radius: var(--cv-radius-sm);
  background: rgba(0, 0, 0, 0.72); color: #fff;
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs);
}
.cvo-m-pick { position: absolute; top: var(--cv-space-1); right: var(--cv-space-1); z-index: 1; }

/* Stat strip. Fixed columns rather than the auto-span pairing algorithm the
   current card runs (obs-monitor.js:2907, 105 lines of layout logic inside a
   template literal) — a grid does this in one declaration and stays aligned
   across cards, which is what actually matters when scanning a wall. */
.cvo-stats {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--cv-border-subtle);
  border-top: 1px solid var(--cv-border-subtle);
}
.cvo-st { padding: var(--cv-space-1) var(--cv-space-2); background: var(--cv-bg-surface); min-width: 0; }
.cvo-st-l {
  display: block; font-size: 9px; letter-spacing: var(--cv-tracking-caps);
  text-transform: uppercase; color: var(--cv-text-disabled);
}
.cvo-st-v {
  display: block;
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-xs); color: var(--cv-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cvo-st-v[data-lvl="warn"] { color: var(--cv-warning); }
.cvo-st-v[data-lvl="crit"] { color: var(--cv-danger); }
.cvo-st-v.cvo-st-text { font-family: var(--cv-font-ui); }

/* Alert line. States the reason — the current card conveys it with an icon and
   a colour, which does not tell an operator what to do about it. */
.cvo-alert {
  display: flex; align-items: center; gap: var(--cv-space-1);
  padding: 3px var(--cv-space-2);
  background: var(--cv-warning-wash);
  color: var(--cv-warning);
  font-size: var(--cv-text-2xs);
  border-top: 1px solid var(--cv-warning-edge);
}
.cvo-alert[data-sev="crit"] { background: var(--cv-danger-wash); color: var(--cv-danger); border-top-color: var(--cv-danger-edge); }

/* Profile mismatch: the app knows the expected profile from the Reflex DB room
   link, so show current -> expected rather than a bare warning glyph. */
.cvo-mismatch { display: inline-flex; align-items: center; gap: 4px; font-family: var(--cv-font-mono); }
.cvo-mismatch s { color: var(--cv-text-muted); text-decoration-color: var(--cv-danger); }

/* Primary machine controls. Sized up and spaced out: these are the things an
   operator reaches for under time pressure, sometimes on a tablet at the back
   of a room. 32px visual with a 44px hit area via .cv-hit, and a full 8px
   between neighbours so record and stream cannot be confused by a thumb. */
.cvo-m-acts {
  display: flex; align-items: center; gap: var(--cv-space-2);
  padding: var(--cv-space-2);
  border-top: 1px solid var(--cv-border-subtle);
}
.cvo-act { width: 32px; height: 32px; padding: 0; }
.cvo-act .cv-i { width: 16px; height: 16px; }
/* Record and stream are destructive-ish state changes; tint them so the two
   most consequential buttons are not visually identical to the rest. */
.cvo-act[data-act="record"]:hover { border-color: var(--cvo-recording); color: var(--cvo-recording); }
.cvo-act[data-act="stream"]:hover { border-color: var(--cvo-streaming); color: var(--cvo-streaming); }
.cvo-act[data-on="true"][data-act="record"] { border-color: var(--cvo-recording); color: var(--cvo-recording); background: var(--cv-danger-wash); }
.cvo-act[data-on="true"][data-act="stream"] { border-color: var(--cvo-streaming); color: var(--cvo-streaming); background: var(--cv-warning-wash); }

/* ============================================================== audio strip
   Audio gets its own row rather than a 5px sliver in the action bar. On a
   broadcast wall a silent feed is a fault as serious as a stopped recording,
   and it is invisible unless the level is actually legible — so each channel
   gets a full-width meter, a peak-hold tick, a clip LED and a dBFS number.
   Horizontal rather than the conventional vertical pair because a 272px card
   has width to spend and no height to spare, and because the number needs
   somewhere to sit. */
.cvo-vu-strip {
  display: grid; gap: 1px;
  padding: var(--cv-space-2) var(--cv-space-2) 5px;
  border-top: 1px solid var(--cv-border-subtle);
  background: var(--cv-bg-raised);
}
.cvo-vu-row {
  display: grid; grid-template-columns: 9px 8px minmax(0, 1fr) 40px;
  gap: var(--cv-space-2); align-items: center;
  line-height: 1;
}
.cvo-vu-ch {
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); color: var(--cv-text-muted);
}
.cvo-vu-led { width: 8px; height: 8px; border-radius: 2px; background: var(--cv-bg-active); }
.cvo-vu-led[data-clip="true"] { background: var(--cv-danger); }
.cvo-vu-strip .cv-meter { height: 7px; }
.cvo-vu-peak { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--cv-text); opacity: 0.85; }
.cvo-vu-db {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs); color: var(--cv-text-secondary); text-align: right;
}
.cvo-vu-db[data-lvl="hot"]    { color: var(--cv-warning); }
.cvo-vu-db[data-lvl="clip"]   { color: var(--cv-danger); }
.cvo-vu-db[data-lvl="silent"] { color: var(--cv-danger); }
/* Scale ticks under the pair, so the number has a frame of reference. */
.cvo-vu-scale {
  display: flex; justify-content: space-between;
  margin-top: 2px;
  padding-left: 25px; padding-right: 48px;
  font-family: var(--cv-font-mono); font-size: 8px; color: var(--cv-text-disabled);
}

/* Preview + vertical VU laid out side by side (VU to the RIGHT of the frame). */
.cvo-prev-row { display: flex; align-items: stretch; }
.cvo-prev-row .cvo-prev { flex: 1; min-width: 0; }
.cvo-vu-vert {
  flex: none; width: 40px;
  display: flex; flex-direction: column; gap: 2px;
  padding: 5px 4px;
  background: var(--cv-bg-inset);
  border-left: 1px solid var(--cv-border-subtle);
}
.cvo-vu-vbars { flex: 1; min-height: 40px; display: flex; gap: 4px; justify-content: center; }
.cvo-vu-vtrack { position: relative; width: 9px; background: var(--cv-bg-active); border-radius: 2px; overflow: hidden; }
.cvo-vu-vfill { position: absolute; left: 0; right: 0; bottom: 0; height: 0; background: var(--cv-success); transition: height 90ms linear; }
.cvo-vu-vfill[data-level="hot"]  { background: var(--cv-warning); }
.cvo-vu-vfill[data-level="clip"] { background: var(--cv-danger); }
.cvo-vu-vdb { text-align: center; font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums; font-size: 8px; color: var(--cv-text-secondary); }
@media (prefers-reduced-motion: reduce) { .cvo-vu-vfill { transition: none; } }

/* Record / Stream keep a text label beside the icon (Preview stays icon-only). */
.cvo-act.cvo-act-lbl {
  width: auto; padding: 0 var(--cv-space-2); gap: 5px;
  font-size: var(--cv-text-2xs); font-weight: var(--cv-weight-medium);
}

/* ================================================= inline scene/profile picker
   Scene and profile are the two values an operator changes most, and both
   currently open a modal (showSceneSelector at obs-monitor.js:5171,
   showProfileSelector at :5246). Making the stat itself the control removes a
   dialog from the most frequent interaction on the page. */
.cvo-st-btn {
  display: block; width: 100%;
  padding: var(--cv-space-1) var(--cv-space-2);
  border: none; background: var(--cv-bg-surface);
  font: inherit; text-align: left; cursor: pointer;
  min-width: 0;
  transition: background var(--cv-dur-fast) var(--cv-ease);
}
.cvo-st-btn:hover { background: var(--cv-bg-hover); }
.cvo-st-btn[aria-expanded="true"] { background: var(--cv-accent-wash); }
.cvo-st-btn[disabled] { cursor: not-allowed; }
.cvo-st-btn .cvo-st-l { display: flex; align-items: center; gap: 3px; }
.cvo-st-btn .cvo-st-l .cv-i { width: 9px; height: 9px; opacity: 0.6; }

.cvo-menu { min-width: 190px; max-height: 260px; overflow-y: auto; }
.cvo-menu .cv-menu-item[aria-checked="true"] { color: var(--cv-accent); }
.cvo-menu-head {
  padding: var(--cv-space-1) var(--cv-space-2);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-disabled);
}

/* ============================================================== wall density
   Preview-first: the frame is the content and everything else is an overlay.
   For a room where the operator is watching rather than adjusting. */
.cvo-grid[data-view="wall"] .cvo-m-head,
.cvo-grid[data-view="wall"] .cvo-stats,
.cvo-grid[data-view="wall"] .cvo-m-acts { display: none; }
/* The uptime badge and the wall label both sit bottom-right of the preview, so
   in Wall they overlap and the scene name renders on top of the clock. Name and
   scene are the two things worth reading on a watch wall; uptime is available in
   Cards and Compact. */
.cvo-grid[data-view="wall"] .cvo-prev-time { display: none; }
.cvo-grid[data-view="wall"] .cvo-prev { border-radius: var(--cv-radius-lg); }
.cvo-wall-label {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: none; align-items: center; gap: var(--cv-space-1);
  padding: var(--cv-space-1) var(--cv-space-2);
  background: linear-gradient(to top, rgba(0,0,0,0.82), transparent);
  color: #fff; font-size: var(--cv-text-2xs);
}
.cvo-grid[data-view="wall"] .cvo-wall-label { display: flex; }

/* ============================================================ compact density
   One row per machine. This is what makes 200 machines viable: every number is
   in a fixed column, so the eye scans a column instead of hunting across cards.
   Sortable, because at fleet scale "worst CPU first" is the actual question. */
/* Compact table wrapper — horizontal scroll ONLY when the table genuinely
   can't fit (no border/rounded box, no vertical scroll, so no scrollbars appear
   for the near-zero overflow the bordered .cv-table-wrap used to trigger). */
.cvo-tbl-wrap { overflow-x: auto; overflow-y: visible; }
.cvo-tbl { width: 100%; border-collapse: separate; border-spacing: 0; table-layout: fixed; }
.cvo-tbl th {
  position: sticky; top: 0; z-index: var(--cv-z-sticky);
  height: var(--cv-row); padding: 0 var(--cv-space-2);
  background: var(--cv-bg-raised);
  border-bottom: 1px solid var(--cv-border);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  /* --cv-text-muted on --cv-bg-raised is 4.25:1 — under AA for 9px caps.
     Column headers are content here, not chrome. */
  color: var(--cv-text-secondary); text-align: left; white-space: nowrap;
  cursor: pointer; user-select: none;
}
.cvo-tbl th:hover { color: var(--cv-text); }
.cvo-tbl th[aria-sort] { color: var(--cv-accent); }
.cvo-tbl th .cv-i { opacity: 0.5; }
.cvo-tbl th[aria-sort] .cv-i { opacity: 1; }
.cvo-tbl td {
  height: var(--cv-row-dense); padding: 0 var(--cv-space-2);
  border-bottom: 1px solid var(--cv-border-subtle);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cvo-tbl tbody tr:hover td { background: var(--cv-bg-hover); }
.cvo-tbl tbody tr[aria-selected="true"] td { background: var(--cv-accent-wash); }
.cvo-tbl .cvo-num {
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  text-align: right;
}
.cvo-tbl td.cvo-fit { width: 1%; padding: 0; }

@media (max-width: 900px) {
  .cvo-grid[data-cols] { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
}

/* ================================================= compact: groups + meters */
.cvo-tbl tr.cvo-grp td {
  height: var(--cv-row);
  padding: 0 var(--cv-space-2);
  background: var(--cv-bg-raised);
  border-bottom: 1px solid var(--cv-border);
  border-top: 1px solid var(--cv-border);
  font-size: 9px; font-weight: var(--cv-weight-semibold);
  letter-spacing: var(--cv-tracking-caps); text-transform: uppercase;
  color: var(--cv-text-secondary);
}
.cvo-tbl tr.cvo-grp:first-child td { border-top: none; }
.cvo-tbl tr.cvo-grp td .cv-chip-count { color: var(--cv-text-disabled); }

/* A level bar next to the number. At fleet scale the shape is read before the
   digits — a short bar in a column of long ones is a silent feed. */
.cvo-tbl-vu { display: inline-flex; align-items: center; gap: 5px; justify-content: flex-end; }
.cvo-tbl-vu .cv-meter { width: 32px; height: 5px; flex: none; }

/* ==================================================================== selection
   Two ways in: a group checkbox on every room header, and a Select menu that
   can pick by status. Both operate on WHAT IS CURRENTLY VISIBLE — see selectSet()
   — because the next click after selecting can start or stop recording on the
   whole set, and a selection that reached machines scrolled out of view, or
   filtered away, would be a genuinely dangerous surprise. */
.cvo-group-head { cursor: default; }
.cvo-group-head .cv-check {
  text-transform: none; letter-spacing: 0;
  font-size: var(--cv-text-2xs); color: var(--cv-text-secondary);
  cursor: pointer;
}
.cvo-group-head .cv-check:hover { color: var(--cv-text); }

.cvo-tbl tr.cvo-grp td .cv-check { text-transform: none; letter-spacing: 0; }

/* Composition readout in the bulk bar. Knowing "6 recording, 2 offline" before
   pressing Stop recording is the difference between a bulk action and a bulk
   accident. */
.cvo-sel-mix { display: flex; align-items: center; gap: var(--cv-space-2); flex-wrap: wrap; font-size: var(--cv-text-2xs); }
.cvo-sel-mix span { display: inline-flex; align-items: center; gap: 4px; color: var(--cv-text-muted); }
.cvo-sel-warn {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px var(--cv-space-2); border-radius: var(--cv-radius-sm);
  background: var(--cv-warning-wash); color: var(--cv-warning);
  font-size: var(--cv-text-2xs);
}

.cvo-selmenu { min-width: 260px; }
.cvo-selmenu .cv-menu-item { justify-content: flex-start; }
.cvo-selmenu .cvo-selmenu-n {
  margin-left: auto;
  font-family: var(--cv-font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--cv-text-2xs); color: var(--cv-text-muted);
}
.cvo-selmenu .cv-menu-item[disabled] { opacity: 0.4; cursor: not-allowed; }
.cvo-selmenu-foot {
  padding: var(--cv-space-1) var(--cv-space-2);
  border-top: 1px solid var(--cv-border-subtle);
  font-size: 9px; color: var(--cv-text-disabled);
}

/* ==================================================== compact: in-cell controls
   Scene and profile are switchable from Compact too. At fleet scale this is
   where an operator actually works, so making them read-only here would mean
   dropping to Cards just to change a scene.

   The chevron sits at low opacity rather than appearing only on hover: 26 rows
   x 2 columns of always-bright chevrons is noise, but a hover-only affordance
   is undiscoverable — which is the same failure as the title-attribute pattern
   this design replaced. */
.cvo-tbl .cvo-cell-btn {
  display: inline-flex; align-items: center; gap: 3px;
  max-width: 100%; min-width: 0;
  padding: 1px 4px; margin: 0 -4px;
  border: 1px solid transparent; border-radius: var(--cv-radius-sm);
  background: none; color: inherit; font: inherit;
  cursor: pointer;
  transition: background var(--cv-dur-fast) var(--cv-ease), border-color var(--cv-dur-fast) var(--cv-ease);
}
.cvo-tbl .cvo-cell-btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cvo-tbl .cvo-cell-btn .cv-i { width: 10px; height: 10px; opacity: 0.35; flex: none; }
.cvo-tbl .cvo-cell-btn:hover { background: var(--cv-bg-active); border-color: var(--cv-border-strong); }
.cvo-tbl .cvo-cell-btn:hover .cv-i { opacity: 1; }
.cvo-tbl .cvo-cell-btn[aria-expanded="true"] {
  background: var(--cv-accent-wash); border-color: var(--cv-accent-edge); color: var(--cv-accent);
}
.cvo-tbl .cvo-cell-btn[aria-expanded="true"] .cv-i { opacity: 1; }
.cvo-tbl .cvo-cell-btn[disabled] { cursor: default; }
.cvo-tbl .cvo-cell-btn[disabled] .cv-i { display: none; }

/* The attention flag is a real control, not a decoration: it carries the reason
   on hover and on focus, and clicking it isolates that machine so the bulk bar
   can act on it. */
.cvo-flag {
  display: inline-grid; place-items: center;
  width: 18px; height: 18px; flex: none;
  border: none; border-radius: var(--cv-radius-sm);
  background: none; cursor: pointer;
  color: var(--cv-warning);
}
.cvo-flag[data-sev="crit"] { color: var(--cv-danger); }
.cvo-flag:hover { background: var(--cv-warning-wash); }
.cvo-flag[data-sev="crit"]:hover { background: var(--cv-danger-wash); }
