/* Cassian UI/UX Product Experience Standard v1.3 — tokens + components.
   Wired (Polish-Round-1) into the Device and Networks list bundles only.
   Additive: bare :root vars change nothing until a class consumes them, and
   the reduced-motion / print rules are scoped to .cassian-* so they never
   fight Metronic or Chartist on the host pages. Source of truth lives in
   docs/formal/guides/product-experience/Cassian_UI_UX_Tokens_v1_3.css. */
:root {
  /* Primitive colors */
  --cassian-color-navy-990: #010B24;
  --cassian-color-navy-970: #080A18;
  --cassian-color-navy-950: #0F193E;
  --cassian-color-navy-900: #181E3E;
  --cassian-color-navy-850: #222D59;
  --cassian-color-navy-997: #020617;
  --cassian-color-navy-980: #090A11;
  --cassian-color-navy-820: #262E57;
  --cassian-color-slate-700: #30365B;
  --cassian-color-slate-600: #3A4678;
  --cassian-color-slate-500: #5A638A;
  --cassian-color-gray-50: #F1F2F4;
  --cassian-color-gray-100: #E2E4E8;
  --cassian-color-gray-200: #C6C9D0;
  --cassian-color-gray-300: #A8ADB8;
  --cassian-color-gray-600: #6A707B;
  --cassian-color-gray-700: #50555E;
  --cassian-color-gray-800: #393D44;
  --cassian-color-gray-900: #25282D;
  --cassian-color-slate-50: #E8EBFA;
  --cassian-color-slate-100: #D8DDF4;
  --cassian-color-slate-200: #B7BFE4;
  --cassian-color-slate-300: #9EA7D0;
  --cassian-color-slate-400: #858EB8;
  /* Official Cassian Color Primitives — Blue 400 is the approved action
     accent. Blue is action/information; purple remains AI/focus identity. */
  --cassian-color-blue-300: #8AAFF8;
  --cassian-color-blue-400: #6F9AF6;
  --cassian-color-blue-700: #28549C;
  --cassian-color-purple-500: #581FFF;
  --cassian-color-purple-300: #AE91FF;
  --cassian-color-purple-200: #C4AFFF;
  --cassian-color-purple-700: #4A1DB8;
  --cassian-color-white: #FFFFFF;
  --cassian-color-text-muted: #9A9CAD;
  --cassian-color-text-tertiary: #6D76A1;
  /* Official primitive-scale names from the Cassian Color Primitives sheet.
     The compatibility aliases below keep existing legacy bundles stable. */
  --cassian-color-green-100: #57FAB6;
  --cassian-color-green-700: #216047;
  --cassian-color-red-500: #F20C3A;
  --cassian-color-red-600: #C90832;
  --cassian-color-red-700: #A9082D;
  --cassian-color-orange-300: #FF9116;
  --cassian-color-orange-700: #824700;
  --cassian-color-amber-400: #F6C000;
  --cassian-color-info-500: #7239EA;
  --cassian-color-gray-500: #8A919E;
  --cassian-color-green-400: var(--cassian-color-green-100);
  --cassian-color-orange-500: var(--cassian-color-orange-300);

  /* Semantic tokens */
  --surface-app: var(--cassian-color-navy-990);
  --surface-sidebar: var(--cassian-color-navy-950);
  --surface-card: linear-gradient(180deg, var(--cassian-color-navy-820) 23.31%, var(--cassian-color-navy-900) 100%);
  --surface-kpi-card: linear-gradient(180deg, var(--cassian-color-navy-950) 0%, var(--cassian-color-navy-980) 100%);
  --surface-modal: var(--cassian-color-navy-900);
  --surface-hover: var(--cassian-color-navy-850);
  --surface-row-alt: var(--cassian-color-navy-900);
  --text-primary: var(--cassian-color-white);
  --text-secondary: var(--cassian-color-text-muted);
  --text-tertiary: var(--cassian-color-text-tertiary);
  --text-link: var(--cassian-color-purple-300);
  --action-primary-bg: var(--cassian-color-blue-400);
  --action-primary-fg: var(--cassian-color-navy-990);
  --action-primary-hover: var(--cassian-color-blue-300);
  --border-default: var(--cassian-color-slate-700);
  --border-hover: var(--cassian-color-slate-600);
  --focus-ring: var(--cassian-color-purple-500);
  --surface-chip: var(--cassian-color-navy-950);
  --surface-scope-bar: var(--cassian-color-navy-950);

  /* Status tokens — kept separate from Side (§8/§9) */
  --status-healthy: var(--cassian-color-green-100);
  --status-online: var(--cassian-color-green-100);
  --status-critical: var(--cassian-color-red-500);
  --status-offline: var(--cassian-color-red-500);
  --status-warning: var(--cassian-color-orange-300);
  --status-degraded: var(--cassian-color-orange-300);
  --status-amber: var(--cassian-color-amber-400);
  --status-info: var(--cassian-color-info-500);
  --status-pending: var(--cassian-color-gray-500);
  --status-unknown: var(--cassian-color-gray-500);

  /* Freshness tokens (§9) */
  --freshness-live: var(--cassian-color-green-400);
  --freshness-recent: var(--cassian-color-amber-400);
  --freshness-stale: var(--cassian-color-orange-500);
  --freshness-unknown: var(--cassian-color-gray-500);

  /* Side tokens — domain attribute, NOT severity (§8) */
  --side-red-bg: var(--cassian-color-red-600);
  --side-red-text: var(--cassian-color-white);
  --side-red-border: var(--cassian-color-red-700);
  --side-black-bg: var(--cassian-color-navy-997);
  --side-black-text: var(--cassian-color-white);
  --side-black-border: var(--cassian-color-slate-500);

  --radius-badge: 4px;
  --radius-control: 6px;
  --radius-card: 12px;
  --shadow-card: 0 3px 4px color-mix(in srgb, var(--cassian-color-navy-997) 3%, transparent);
  --shadow-dropdown: 0 12px 28px color-mix(in srgb, var(--cassian-color-navy-997) 55%, transparent);
  --shadow-active-nav: 0 7px 20px color-mix(in srgb, var(--cassian-color-purple-500) 50%, transparent);

  /* Canonical typography and spacing */
  --font-sans: Inter, "Noto Sans TC", "Microsoft JhengHei", "PingFang TC", "Heiti TC", Helvetica, Arial, sans-serif;
  --font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
  --font-size-body: 14px;
  --font-size-page-title: 26px;
  --font-size-section-title: 14px;
  --font-size-kpi-label: 16px;
  --font-size-kpi-value: 30px;
  --font-size-badge: 12px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 30px;
  --space-8: 32px;
  --control-height: 40px;

  /* UX Reboot semantic aliases. These deliberately map onto the existing
     Cassian v1.3 theme; V2 components consume only this semantic layer. */
  --c-color-surface-canvas: var(--surface-app);
  --c-color-surface-base: var(--surface-sidebar);
  --c-color-surface-raised: var(--cassian-color-navy-900);
  --c-color-surface-overlay: var(--cassian-color-navy-950);
  --c-color-surface-selected: var(--cassian-color-navy-850);
  --c-color-text-primary: var(--text-primary);
  --c-color-text-secondary: var(--cassian-color-text-muted);
  --c-color-text-muted: var(--cassian-color-text-muted);
  --c-color-text-inverse: var(--cassian-color-navy-990);
  --c-color-text-link: var(--text-link);
  --c-color-border-subtle: var(--cassian-color-slate-700);
  --c-color-border-default: var(--cassian-color-slate-600);
  /* ⭐ CONTROL boundaries only — SC 1.4.11, per Dr. Theopolis's rule-book decision 2026-09-11.
     A border must clear 3.0:1 against the WORST ground it can land on, INCLUDING gradient STOPS,
     but ONLY where it is the sole visual means of identifying an interactive control or its state.
     Decorative dividers, card and panel outlines are exempt and keep --c-color-border-default.
     dark  slate-400 #858EB8 -> 4.07:1 worst (navy-820 #262E57, the card gradient's FIRST stop) */
  --c-color-border-control: var(--cassian-color-slate-400);
  --c-color-border-strong: var(--cassian-color-slate-500);
  --c-color-action-primary-bg: var(--action-primary-bg);
  --c-color-action-primary-fg: var(--action-primary-fg);
  --c-color-action-primary-hover: var(--action-primary-hover);
  --c-color-action-primary-active: var(--action-primary-bg);
  --c-color-action-secondary-bg: var(--cassian-color-navy-850);
  --c-color-action-secondary-fg: var(--cassian-color-white);
  /* ACTION-danger, and deliberately NOT the status-danger trio below.
     A STATUS token paints a chip that REPORTS a state; an ACTION token paints a
     control the operator PRESSES. They are different jobs and in light mode they
     are not even close: status-danger-bg there is a 12% red TINT on white, so a
     button borrowing it renders pale pink with red text and reads as DISABLED —
     on Zeroise, the most destructive control in the product (reported by Riaan,
     2026-09-17). Destructive still must not out-shout the primary action, so this
     is a solid red fill, not a louder one. Contrast: red-600 #C42B2B on white
     #FFFFFF = 5.94:1 (WCAG AA text needs 4.5:1). */
  --c-color-action-danger-bg: var(--cassian-color-red-600);
  --c-color-action-danger-fg: var(--cassian-color-white);
  --c-color-action-danger-border: var(--cassian-color-red-600);
  --c-color-action-danger-hover: var(--cassian-color-red-700);
  --c-color-focus-ring: var(--cassian-color-purple-300);
  --c-color-focus-offset: var(--cassian-color-navy-990);
  /* Resting SELECTED/ACTIVE/CURRENT states. Deliberately NOT --c-color-focus-ring.
     The ring is a transient keyboard affordance; if resting elements wear the same
     vivid purple it stops meaning "your keyboard is here", which is its only job.
     Ruled by Dr. Theopolis 2026-09-11T22:59:37Z. Measured on these two values:
       accent #6F9AF6 on selected surface #222D59 = 4.81:1, on base #0F193E = 6.21:1
         (WCAG 1.4.11 non-text needs 3:1 — these are borders/bars, not text on a fill)
       ring #AE91FF vs accent #6F9AF6 = dE 22.9, so the two remain tellable apart
         (a WCAG ratio here reads 1.09:1 and is the WRONG instrument: it measures
          luminance only, and these differ by HUE) */
  --c-color-selected-accent: var(--action-primary-bg);
  --c-color-status-neutral-bg: var(--cassian-color-navy-850);
  --c-color-status-neutral-fg: var(--cassian-color-white);
  --c-color-status-neutral-border: var(--cassian-color-slate-500);
  --c-color-status-info-bg: var(--cassian-color-info-500);
  --c-color-status-info-fg: var(--cassian-color-white);
  --c-color-status-info-border: var(--cassian-color-purple-300);
  --c-color-status-positive-bg: var(--cassian-color-green-100);
  --c-color-status-positive-fg: var(--cassian-color-navy-990);
  --c-color-status-positive-border: var(--cassian-color-green-100);
  --c-color-status-warning-bg: var(--cassian-color-orange-300);
  --c-color-status-warning-fg: var(--cassian-color-navy-990);
  --c-color-status-warning-border: var(--cassian-color-orange-300);
  --c-color-status-danger-bg: var(--cassian-color-red-600);
  --c-color-status-danger-fg: var(--cassian-color-white);
  --c-color-status-danger-border: var(--cassian-color-red-500);
  /* accent-2 (F-xx): the ONLY accent with a fill and no matched ink.
     --cassian-accent-2 resolves to --cassian-color-purple-300 and is defined ONCE, so the
     fill is the same lavender in BOTH themes. Its consumers were borrowing
     --c-color-action-primary-fg, which FLIPS: navy-990 in dark, WHITE in light. A
     non-flipping fill with a flipping ink is white-on-lavender in light.
     So the ink here is deliberately the SAME IN BOTH BLOCKS -- an ink may only flip if the
     fill it sits on flips too. Defined structurally rather than per-surface, so a new
     accent-2 surface inherits a correct pair instead of choosing one.
     (c) HUE IS UNCHANGED. This pairs an ink WITH the existing lavender; it does not repaint
     the chip. Repainting to the action-primary pair would turn it blue in dark, which would
     measure as a contrast success and be a design change wearing a contrast fix's clothes. */
  --c-color-accent-2-bg: var(--cassian-accent-2, var(--cassian-color-purple-300));
  --c-color-accent-2-fg: var(--cassian-color-navy-990);
  --c-color-accent-2-border: var(--cassian-accent-2, var(--cassian-color-purple-300));
  --c-color-overlay-scrim: color-mix(in srgb, var(--cassian-color-navy-990) 82%, transparent);
  --c-font-family-ui: var(--font-sans);
  --c-font-family-mono: var(--font-mono);
  --c-radius-control: 6px;
  --c-radius-panel: 8px;
  --c-radius-pill: 999px;
  --c-shell-offset: 64px;

  /* Canonical component tokens */
  --kpi-card-bg: var(--surface-kpi-card);
  --kpi-card-border: var(--border-default);
  --kpi-card-watermark-opacity: 0.16;
}

/* Cassian light mode uses the same approved primitive families as dark mode.
   Gray 50 is the canvas; white is reserved for raised working surfaces so
   hierarchy remains visible without turning the product into a white sheet. */
html[data-bs-theme="light"] {
  color-scheme: light;
  --surface-app: var(--cassian-color-gray-50);
  --surface-sidebar: var(--cassian-color-gray-100);
  --surface-card: linear-gradient(180deg, var(--cassian-color-white) 0%, var(--cassian-color-gray-50) 100%);
  --surface-kpi-card: linear-gradient(180deg, var(--cassian-color-white) 0%, var(--cassian-color-gray-50) 100%);
  --surface-modal: var(--cassian-color-white);
  --surface-hover: var(--cassian-color-slate-100);
  --surface-row-alt: var(--cassian-color-gray-50);
  --surface-chip: var(--cassian-color-gray-100);
  --surface-scope-bar: var(--cassian-color-gray-100);
  --text-primary: var(--cassian-color-navy-990);
  --text-secondary: var(--cassian-color-gray-700);
  --text-tertiary: var(--cassian-color-gray-600);
  --text-link: var(--cassian-color-purple-700);
  --action-primary-bg: var(--cassian-color-blue-700);
  --action-primary-fg: var(--cassian-color-white);
  --action-primary-hover: var(--cassian-color-navy-850);
  --border-default: var(--cassian-color-gray-200);
  --border-hover: var(--cassian-color-slate-300);
  --focus-ring: var(--cassian-color-purple-700);
  --status-healthy: var(--cassian-color-green-700);
  --status-online: var(--cassian-color-green-700);
  --status-critical: var(--cassian-color-red-700);
  --status-offline: var(--cassian-color-red-700);
  --status-warning: var(--cassian-color-orange-700);
  --status-degraded: var(--cassian-color-orange-700);
  --status-info: var(--cassian-color-purple-700);
  --freshness-live: var(--cassian-color-green-700);
  --freshness-stale: var(--cassian-color-orange-700);
  --c-color-surface-canvas: var(--surface-app);
  --c-color-surface-base: var(--surface-sidebar);
  --c-color-surface-raised: var(--cassian-color-white);
  --c-color-surface-overlay: var(--cassian-color-white);
  --c-color-surface-selected: var(--cassian-color-slate-100);
  --c-color-text-primary: var(--text-primary);
  --c-color-text-secondary: var(--text-secondary);
  --c-color-text-muted: var(--text-tertiary);
  --c-color-text-inverse: var(--cassian-color-white);
  --c-color-text-link: var(--text-link);
  --c-color-border-subtle: var(--cassian-color-gray-200);
  --c-color-border-default: var(--cassian-color-gray-300);
  /* light  gray-600 #6A707B -> 3.91:1 worst (gray-100 #E2E4E8). The tier below it, gray-500
     #8A919E, measures 2.49 and does NOT clear the floor — so this is the conservative
     floor-clearing shade, not a preference, and a "try the next shade" pass would fail. */
  --c-color-border-control: var(--cassian-color-gray-600);
  --c-color-border-strong: var(--cassian-color-slate-400);
  --c-color-action-primary-bg: var(--action-primary-bg);
  --c-color-action-primary-fg: var(--action-primary-fg);
  --c-color-action-primary-hover: var(--action-primary-hover);
  --c-color-action-primary-active: var(--cassian-color-navy-850);
  --c-color-action-secondary-bg: var(--cassian-color-gray-100);
  --c-color-action-secondary-fg: var(--cassian-color-navy-990);
  --c-color-focus-ring: var(--cassian-color-purple-700);
  --c-color-focus-offset: var(--cassian-color-white);
  /* Light-theme half of the same rule. Measured:
       accent #28549C on selected surface #D8DDF4 = 5.49:1, on base #E2E4E8 = 5.81:1
       ring #4A1DB8 vs accent #28549C = dE 54.8 */
  --c-color-selected-accent: var(--action-primary-bg);
  --c-color-status-neutral-bg: var(--cassian-color-gray-100);
  --c-color-status-neutral-fg: var(--cassian-color-navy-990);
  --c-color-status-neutral-border: var(--cassian-color-gray-300);
  --c-color-status-info-bg: var(--cassian-color-slate-100);
  --c-color-status-info-fg: var(--cassian-color-purple-700);
  --c-color-status-info-border: var(--cassian-color-purple-700);
  --c-color-status-positive-bg: color-mix(in srgb, var(--cassian-color-green-100) 22%, var(--cassian-color-white));
  --c-color-status-positive-fg: var(--cassian-color-green-700);
  --c-color-status-positive-border: var(--cassian-color-green-700);
  --c-color-status-warning-bg: color-mix(in srgb, var(--cassian-color-orange-300) 18%, var(--cassian-color-white));
  --c-color-status-warning-fg: var(--cassian-color-orange-700);
  --c-color-status-warning-border: var(--cassian-color-orange-700);
  --c-color-status-danger-bg: color-mix(in srgb, var(--cassian-color-red-500) 12%, var(--cassian-color-white));
  --c-color-status-danger-fg: var(--cassian-color-red-700);
  --c-color-status-danger-border: var(--cassian-color-red-700);
  /* Light mode keeps the SAME solid action-danger fill as dark: a destructive
     control must look identical in both themes, because "is this button live?"
     is not a question a theme should be able to change the answer to. */
  --c-color-action-danger-bg: var(--cassian-color-red-600);
  --c-color-action-danger-fg: var(--cassian-color-white);
  --c-color-action-danger-border: var(--cassian-color-red-700);
  --c-color-action-danger-hover: var(--cassian-color-red-700);
  /* accent-2 (F-xx): the ONLY accent with a fill and no matched ink.
     --cassian-accent-2 resolves to --cassian-color-purple-300 and is defined ONCE, so the
     fill is the same lavender in BOTH themes. Its consumers were borrowing
     --c-color-action-primary-fg, which FLIPS: navy-990 in dark, WHITE in light. A
     non-flipping fill with a flipping ink is white-on-lavender in light.
     So the ink here is deliberately the SAME IN BOTH BLOCKS -- an ink may only flip if the
     fill it sits on flips too. Defined structurally rather than per-surface, so a new
     accent-2 surface inherits a correct pair instead of choosing one.
     (c) HUE IS UNCHANGED. This pairs an ink WITH the existing lavender; it does not repaint
     the chip. Repainting to the action-primary pair would turn it blue in dark, which would
     measure as a contrast success and be a design change wearing a contrast fix's clothes. */
  --c-color-accent-2-bg: var(--cassian-accent-2, var(--cassian-color-purple-300));
  --c-color-accent-2-fg: var(--cassian-color-navy-990);
  --c-color-accent-2-border: var(--cassian-accent-2, var(--cassian-color-purple-300));
  --c-color-overlay-scrim: color-mix(in srgb, var(--cassian-color-navy-990) 55%, transparent);
  --shadow-card: 0 1px 3px color-mix(in srgb, var(--cassian-color-navy-990) 10%, transparent);
  --shadow-dropdown: 0 18px 48px color-mix(in srgb, var(--cassian-color-navy-990) 22%, transparent);
  --shadow-active-nav: 0 5px 16px color-mix(in srgb, var(--cassian-color-purple-500) 20%, transparent);
}

/* Correct-spelling alias for the legacy `.cassain-card-head` typo (§3.4 — the
   typo must not be created in new code; new widget partials use this). */
.cassian-card-head {
  border-bottom: 1px solid var(--border-default);
  padding: 20px;
  position: relative;
  height: 56px;
  background: var(--surface-scope-bar);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.cassian-card-head .cassian-dashboard-title {
  margin: 0;
  color: var(--text-primary);
}

/* ── Status badge (§9) ─────────────────────────────────────────────── */
.cassian-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
  padding: 2px 10px;
  border-radius: var(--radius-badge);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--text-primary);
  border: 1px solid transparent;
}
.cassian-status-badge::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.cassian-status-badge--healthy,
.cassian-status-badge--online,
.cassian-status-badge--active,
.cassian-status-badge--enabled,
.cassian-status-badge--success,
.cassian-status-badge--succeeded,
.cassian-status-badge--approved,
.cassian-status-badge--trusted { color: var(--status-healthy); border-color: var(--status-healthy); }
.cassian-status-badge--degraded,
.cassian-status-badge--warning,
.cassian-status-badge--pending,
.cassian-status-badge--queued,
.cassian-status-badge--running,
.cassian-status-badge--dispatching { color: var(--status-degraded); border-color: var(--status-degraded); }
.cassian-status-badge--offline,
.cassian-status-badge--critical,
.cassian-status-badge--inactive,
.cassian-status-badge--disabled,
.cassian-status-badge--failed,
.cassian-status-badge--expired,
.cassian-status-badge--revoked { color: var(--status-offline); border-color: var(--status-offline); }
.cassian-status-badge--unknown,
.cassian-status-badge--draft,
.cassian-status-badge--neutral { color: var(--status-unknown); border-color: var(--status-unknown); }

/* Status text stays accessible without introducing off-palette lightened hues. */
.cassian-status-badge--offline,
.cassian-status-badge--critical,
.cassian-status-badge--failed,
.cassian-status-badge--expired,
.cassian-status-badge--revoked { color: var(--cassian-color-white); border-color: var(--cassian-color-red-500); }
.cassian-status-badge--offline::before,
.cassian-status-badge--critical::before,
.cassian-status-badge--failed::before,
.cassian-status-badge--expired::before,
.cassian-status-badge--revoked::before { background: var(--cassian-color-red-500); }
.cassian-status-badge--inactive,
.cassian-status-badge--disabled { color: var(--cassian-color-text-muted); border-color: var(--cassian-color-slate-500); }

/* Critical badges are outlined with white text in the dark shell. On a light
   working surface that text disappears, so use the official light danger
   semantic pair rather than carrying the dark treatment across modes. */
html[data-bs-theme="light"] .cassian-status-badge--offline,
html[data-bs-theme="light"] .cassian-status-badge--critical,
html[data-bs-theme="light"] .cassian-status-badge--failed,
html[data-bs-theme="light"] .cassian-status-badge--expired,
html[data-bs-theme="light"] .cassian-status-badge--revoked {
  color: var(--c-color-status-danger-fg);
  background: var(--c-color-status-danger-bg);
  border-color: var(--c-color-status-danger-border);
}

html[data-bs-theme="light"] .cassian-status-badge--inactive,
html[data-bs-theme="light"] .cassian-status-badge--disabled,
html[data-bs-theme="light"] .cassian-status-badge--unknown,
html[data-bs-theme="light"] .cassian-status-badge--draft,
html[data-bs-theme="light"] .cassian-status-badge--neutral {
  color: var(--c-color-status-neutral-fg);
  background: var(--c-color-status-neutral-bg);
  border-color: var(--c-color-status-neutral-border);
}

/* ── Isidore SideBadge (§8) — domain side, never severity ──────────── */
.cassian-side-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: var(--radius-badge);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .02em;
  white-space: nowrap;
  text-transform: uppercase;
}
.cassian-side-badge + .cassian-side-badge { margin-left: 6px; }
.cassian-side-badge--red {
  color: var(--side-red-text);
  background: var(--side-red-bg);
  border-color: var(--side-red-border);
}
.cassian-side-badge--black {
  color: var(--side-black-text);
  background: var(--side-black-bg);
  border-color: var(--side-black-border);
}

/* ── FreshnessChip (§9) — text + shape, not colour alone ───────────── */
.cassian-freshness-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-primary);
  background: var(--surface-chip);
  border: 1px solid var(--border-default);
}
.cassian-freshness-chip::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.cassian-freshness-chip--live::before { background: var(--freshness-live); }
.cassian-freshness-chip--recent::before { background: var(--freshness-recent); }
.cassian-freshness-chip--stale::before { background: var(--freshness-stale); }
.cassian-freshness-chip--unknown::before { background: var(--freshness-unknown); }

/* A freshness chip nested INSIDE a KPI tile paints its own background, so the tile's
   blanket `.Cassian-data-control-item span { color: #fff }` (metronic-customize.less:3156)
   puts white text on the chip's own light ground. The tile being dark is irrelevant - the
   chip overrides the ground locally and the label lands on the chip, not the tile.

   Measured, Live Telemetry, light theme:  "Unknown" rgb(255,255,255) on rgb(226,228,232)
                                           = 1.27:1
   The same chip on /App/Device, not nested in a tile, is correct at rgb(1,11,36).

   (0,2,0) beats the tile's (0,1,1), so no !important is needed. var(--text-primary) is
   theme-aware, which keeps the nested chip identical to the standalone one in both themes
   rather than pinning a literal that would be wrong in dark. */
.Cassian-data-control-item .cassian-freshness-chip {
  color: var(--text-primary);
}

/* The "?" help chip hits the SAME collision as the freshness chip above, from the other side.
   `.lt-help` paints its own blue ground and asks for the matching ink:

       .lt-help { background: var(--c-color-action-primary-bg);
                  color:      var(--c-color-action-primary-fg); }

   and that PAIR IS ALREADY CORRECT in both themes — :root gives blue-400 fill with navy-990 ink,
   the light block gives blue-700 fill with white ink. Neither pairing is the defect.

   The tile's blanket `.Cassian-data-control-item span { color: #fff }` (0,1,1) simply outranks
   `.lt-help` (0,1,0), so in DARK the chip keeps its light-blue fill and loses its navy ink to
   white. The token never reaches the element.

   Measured on dev-cloud, Live Telemetry, DARK:
       before  rgb(255,255,255) on rgb(111,154,246) = 2.75:1   x5 of 17   (AA needs 4.5 at 14px)
       token the element actually resolves: --c-color-action-primary-fg = #010B24
   LIGHT was already 7.4:1 and must not move — restoring the token keeps it exactly there, because
   the token is theme-aware. No new colour is invented: the right one already existed and was
   being outranked.

   ⚠ SCOPED TO THE ELEMENT, NOT TO A CONTAINER. There are (at least) TWO ancestors that paint their
   descendant spans white, and they are in different files:
       .Cassian-data-control-item span            (0,1,1)   KPI tiles      -> Live Telemetry
       table.cassian-table thead tr th span       (0,1,5)   grid headers   -> Device Logs
   A container-scoped fix would have closed the first and left the second, which is exactly what my
   first version did — measured: 5 of 5 fixed on Live Telemetry, 1 of 1 still failing on Device Logs.
   Doubling the class is (0,2,0), which beats BOTH and any future single-class ancestor, needs no
   !important, and cannot be defeated by adding another container elsewhere.

   VERIFIED on dev-cloud, before and after, BOTH themes, BOTH screens:
       Live Telemetry dark   5 failing -> 0     (2.75:1 -> 7.1:1)
       Live Telemetry light  0 failing -> 0     (7.4:1 unchanged)
       Device Logs    dark   1 failing -> 0
       Device Logs    light  1 failing -> 1     <- see below; NOT caused by this rule
       elements made worse anywhere: 0

   ⚠ ONE RESIDUAL, LEFT DELIBERATELY AND NOT HIDDEN. The single chip inside a Device Logs grid
   header still fails in LIGHT at 2.64:1, and this rule does not reach it:
       html[data-bs-theme="light"] table.cassian-table thead tr th span { color: var(--text-primary) }
   is (0,2,6) and outranks (0,2,0), so the header's navy ink lands on the chip's own dark-blue
   ground. That is a DIFFERENT collision from the one this rule fixes — light theme, a different
   ancestor, and not the 2.75:1 pair the register recorded. Escalating specificity again to win it
   would start a war inside a shared token file, so it is reported as its own finding instead of
   being absorbed here. */
/* ⭐ SUPERSEDED 2026-09-14, and the whole investigation above with it.
 *
 * Everything above solves one problem: the chip painted its own blue ground, so it needed a
 * matching ink, and two different ancestor rules in two different files outranked that ink. The
 * doubled class won the specificity fight. The residual it could NOT win - the light-theme header
 * collision at 2.64:1 - was reported as its own finding.
 *
 * Riaan then ruled the filled blue chip out entirely ("This is not good UX" - nine of them on one
 * header row). With no ground of its own, the chip inherits the header's ink, which is the same
 * ink the column label beside it already uses and therefore already compliant in both themes.
 *
 * ⇒ There is nothing left to outrank, so this rule must NOT keep forcing action-primary-fg: that
 * token is navy in dark theme and was only ever correct ON the blue fill. Forcing it onto a
 * transparent chip over a dark header would make the glyph almost invisible - the same class of
 * defect, pointing the other way. The doubled selector is kept so it still beats the two ancestor
 * rules, but it now asserts the inheritance rather than a colour.
 * ⇒ The 2.64:1 light-theme residual is resolved by this, not deferred: the collision needed the
 *   chip's own dark-blue ground, and there is no longer one. */
.lt-help.lt-help {
  color: inherit;
}

/* ── ScopeContextBar (§5/§11) ──────────────────────────────────────── */
.cassian-scope-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
  padding: 10px 16px;
  margin-bottom: 16px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-control);
  background: var(--surface-scope-bar);
  color: var(--text-secondary);
  font-size: 12px;
}
.cassian-scope-item { display: inline-flex; align-items: center; gap: 6px; }
.cassian-scope-item .cassian-scope-label { color: var(--text-secondary); }
.cassian-scope-item .cassian-scope-value { color: var(--text-primary); font-weight: 600; }

/* ── Operational masthead + scope composition (§5.1) ────────────────
   Page identity and scope are separate semantic regions, but a shallow tuck
   makes them read as one composed head. Apply the marker only to operational,
   workflow, and resource frames; domain decision-overviews intentionally use
   an open hero instead. */
.cassian-operational-masthead {
  position: relative;
  z-index: 2;
  box-shadow: var(--shadow-card);
}

.cassian-operational-masthead + .cassian-scope-bar {
  position: relative;
  z-index: 1;
  margin-top: calc(-1 * var(--space-3));
  margin-inline: var(--space-2);
  padding-top: calc(var(--space-4) + var(--space-1));
  border-radius: 0 0 var(--c-radius-control) var(--c-radius-control);
  box-shadow: var(--shadow-card);
}

@media (max-width: 767.98px) {
  .cassian-operational-masthead + .cassian-scope-bar {
    margin-top: var(--space-2);
    margin-inline: 0;
    padding-top: 10px;
    border-radius: var(--c-radius-control);
  }
}

/* ── Widget chart body + legend / empty states (§7.3, §9, §17) ─────────
   Compact KPI-band cards: fixed ~150px chart body so charts fit the card
   instead of dictating its height (UX-KPI-002). */
/* Fixed body height so every widget card is identical across the Device and
   Networks screens (border-box: 150px chart area + 28px padding). */
.cassian-widget-body { box-sizing: border-box; min-height: 178px; padding: 12px 20px 16px; }
.cassian-widget-canvas-wrap { position: relative; height: 150px; }
.cassian-widget-legend {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 16px;
  font-size: 12px;
  color: var(--text-secondary);
}
.cassian-widget-legend li { display: flex; align-items: center; gap: 8px; }
.cassian-widget-legend .cassian-legend-swatch {
  width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto;
}
.cassian-widget-legend .cassian-legend-count { margin-left: auto; color: var(--text-primary); font-weight: 600; }
.cassian-widget-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 150px;
  padding: 16px;
  text-align: center;
  color: var(--text-secondary);
  font-size: 13px;
}
.cassian-widget-metric {
  font-size: 30px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--text-primary);
}
.cassian-widget-metric small { font-size: 13px; font-weight: 400; color: var(--text-secondary); }

/* Segmented availability bar — a slim stacked bar (no donut). */
.cassian-segbar {
  display: flex;
  height: 14px;
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface-chip);
  margin: 14px 0 4px;
}
.cassian-segbar .cassian-seg { min-width: 2px; }
.cassian-segbar-headline {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.cassian-segbar-headline .cassian-segbar-pct { font-size: 34px; font-weight: 600; line-height: 1; color: var(--text-primary); }
.cassian-segbar-headline .cassian-segbar-pct small { font-size: 13px; font-weight: 400; color: var(--text-secondary); }

/* Inline legend variant (row of chips with counts) for bars/tiles. */
.cassian-legend-inline {
  display: flex;
  flex-wrap: wrap;
  grid-template-columns: none;
  gap: 6px 16px;
}
.cassian-legend-inline li { flex: 0 0 auto; }
.cassian-legend-inline .cassian-legend-count { margin-left: 6px; }

/* Stat tiles — big counts with a coloured status accent (no chart). */
.cassian-stat-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-top: 14px;
}
.cassian-stat-tiles--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cassian-stat-tile {
  background: var(--surface-chip);
  border: 1px solid var(--border-default);
  border-left: 3px solid var(--status-unknown);
  border-radius: 8px;
  padding: 10px 12px;
}
.cassian-stat-tile__count { font-size: 24px; font-weight: 600; line-height: 1.1; color: var(--text-primary); }
.cassian-stat-tile__label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
.cassian-stat-tile__dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }

/* ── Accessible row actions (§10.6) ────────────────────────────────── */
.cassian-row-actions {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  white-space: nowrap;
}
.cassian-row-action {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radius-control);
  color: var(--text-primary);
  background: transparent;
  cursor: pointer;
  padding: 0;
}
.cassian-row-action:hover { background: var(--cassian-color-navy-950); }
.cassian-row-action[disabled] { opacity: .4; cursor: not-allowed; }
/* Legacy background-image icon glyphs (see-img/share-img/delete-img) render as
   spans inside the accessible action buttons; give them a box to paint into. */
.cassian-row-action > span { display: inline-block; }

/* ── Grid error banner (§10.8) ─────────────────────────────────────── */
.cassian-grid-error {
  margin: 12px 0;
  padding: 12px 16px;
  border: 1px solid var(--status-critical);
  border-radius: var(--radius-control);
  background: color-mix(in srgb, var(--status-critical) 8%, transparent);
  color: var(--text-primary);
  font-size: 13px;
}

/* ── Focus rings (§17) — distinct from decorative glow ─────────────── */
.cassian-row-action:focus-visible,
.cassian-side-badge:focus-visible,
.cassian-freshness-chip:focus-visible,
.cassian-status-badge:focus-visible,
a.cassian-kpi-card:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* The legacy theme suppresses the native outline on .cassian-select. Restore a
   deliberate semantic indicator for keyboard focus across every real select. */
select.cassian-select:focus-visible {
  outline: 2px solid var(--c-color-focus-ring);
  outline-offset: 2px;
}

/* Single-column widget reflow on narrow / high-zoom viewports (§17 zoom). */
@media (max-width: 991.98px) {
  .cassian-threelayout-grid { grid-template-columns: 1fr; }
  .cassian-row-action { width: 44px; height: 44px; }
}

/* Scoped reduced-motion — only Cassian components, never global. */
@media (prefers-reduced-motion: reduce) {
  .cassian-status-badge,
  .cassian-side-badge,
  .cassian-freshness-chip,
  .cassian-row-action { transition: none !important; }
}

/* Scoped forced-colors support. */
@media (forced-colors: active) {
  .cassian-status-badge,
  .cassian-side-badge,
  .cassian-freshness-chip { border: 1px solid CanvasText; }
}

/* ── Slice 5: page head (mockup parity) ─────────────────────────────
   Breadcrumb + title left, one primary + overflow right, rendered clear
   of the floating workspace strip. */
.cassian-s5-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.9rem var(--cassian-shell-gutter-x, 32px) 0.4rem;
}

/* The theme wrapper still reserves ~166px for the hidden desktop header
   (110px .wrapper padding + a 56px inner offset). Slice 5 pages reclaim
   it: pull the head up to sit just under the floating workspace strip.
   Desktop only — the strip wraps taller on narrow viewports. */
@media (min-width: 992px) {
  .cassian-s5-head { margin-top: -5.9rem; }

  /* F-29. The reclaim above is a LEGACY-THEME correction: it cancels the ~166px
     the old wrapper reserved for a header it then hid. The UXR shell does not have
     that reserve. It has a 64px FIXED .uxr-shell-topbar and gives #kt_wrapper a
     deliberate 72px top padding to clear it, so applying the legacy pull here
     cancels the shell's own offset and drags the page head back up into the topbar.

     Measured on Device Details by Dr. Theopolis (1600x1000):
       .uxr-shell-topbar   top 0  bottom 64   position: fixed
       .cassian-s5-head    top 11 bottom 77   sibling of the topbar, NOT inside it
       orb                 top 10 bottom 54   x 1234-1435
       #StartOutcomeButton top 32 bottom 72   x 1342-1502
     Two header layers sharing 0-77. The orb wins z-order and swallowed a real
     click on the button's left ~60%, including its centre, at both 1600x1000 and
     1366x768.

     Neutralised rather than reserved-around: the overlap is the defect, and moving
     controls out from under one orb variant would have left the two layers stacked
     for every other control in that band. */
  body.uxr-shell .cassian-s5-head { margin-top: 0; }
}

@media (max-width: 767.98px) {
  .cassian-s5-head {
    align-items: stretch;
    flex-direction: column;
    max-width: 100%;
    padding-inline: 16px;
  }

  .cassian-s5-head__identity,
  .cassian-s5-head__actions {
    min-width: 0;
    max-width: 100%;
  }

  .cassian-s5-head__actions {
    align-self: flex-start;
    flex-wrap: wrap;
  }

  .dd-actions {
    flex-wrap: wrap;
    max-width: 100%;
  }
}

.cassian-s5-head__crumb,
.cassian-s5-head__crumb * {
  font-size: 0.75rem !important;
  font-weight: 400 !important;
  color: var(--cassian-sidebar-muted-text, var(--text-secondary)) !important;
  margin: 0 !important;
  line-height: 1.4 !important;
}

.cassian-s5-head__identity h2 {
  margin: 0.15rem 0 0;
  font-size: 1.45rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}

.cassian-s5-head__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.cassian-s5-overflow {
  min-width: 42px;
  font-size: 1rem;
  line-height: 1;
}

.cassian-s5-menu {
  background: var(--cassian-sidebar-panel, var(--surface-modal));
  border: 1px solid var(--cassian-border, var(--border-default));
  border-radius: 10px;
  padding: 0.35rem;
  min-width: 240px;
}

/* Row menus live inside horizontally scrolling data-grid frames. Fixed
   positioning lets Popper's viewport boundary escape that clipping context,
   including for the final visible row. */
.cassian-row-action-menu > .cassian-s5-menu.show {
  position: fixed !important;
}

/* While open, row menus are portaled out of grid clipping contexts by the
   shared component runtime. Keep their stacking contract explicit. */
.cassian-portaled-dropdown-menu {
  position: fixed !important;
  z-index: 1090 !important;
}

.cassian-s5-menu .dropdown-header {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--cassian-sidebar-section-label, var(--text-tertiary));
  padding: 0.35rem 0.8rem 0.15rem;
}

.cassian-s5-menu .dropdown-item {
  color: var(--cassian-sidebar-text, var(--text-primary));
  border-radius: 7px;
  font-size: 0.86rem;
  padding: 0.42rem 0.8rem;
}

.cassian-s5-menu .dropdown-item:hover {
  background: var(--cassian-sidebar-hover, var(--surface-hover));
  color: var(--text-primary);
}

.cassian-s5-menu .dropdown-divider {
  border-color: var(--cassian-border, var(--border-default));
  margin: 0.3rem 0;
}

/* Object pages fold every action into this one menu, so it can outgrow a
   short viewport — scroll inside the menu rather than off the screen. */
.cassian-s5-menu {
  max-height: min(70vh, 560px);
  overflow-y: auto;
}

/* Irreversible items read as irreversible before they are clicked. */
body.uxr-shell .cassian-s5-item--danger {
  /* Red 500 alone misses AA on the navy menu surface. This tint remains
     derived exclusively from the approved Cassian red and white primitives. */
  color: color-mix(in srgb, var(--status-critical) 80%, var(--cassian-color-white)) !important;
}

html[data-bs-theme="light"] body.uxr-shell .cassian-s5-item--danger {
  color: var(--status-critical) !important;
}

.cassian-s5-menu .cassian-s5-item--danger:hover {
  background: color-mix(in srgb, var(--status-critical) 14%, transparent);
  color: var(--text-primary);
}

.cassian-s5-menu .dropdown-item.disabled,
.cassian-s5-menu .dropdown-item:disabled {
  color: var(--cassian-sidebar-section-label, var(--text-tertiary));
  background: transparent;
}

/* ── Slice 5: navigation demotion components ────────────────────────
   Outcome measures on widgets, the row chevron that replaces button
   clusters, selection styling, and the intent-runs strip. */
.cassian-outcome-measure {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  width: 100%;
  margin-top: 0.6rem;
  padding-top: 0.55rem;
  border-top: 1px solid var(--cassian-border, var(--border-default));
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-link);
  text-decoration: none;
}

.cassian-outcome-measure:hover { color: var(--text-primary); }
.cassian-outcome-measure:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.cassian-row-chevron {
  color: var(--cassian-sidebar-muted-text, var(--text-secondary));
  font-size: 1.1rem;
  display: inline-block;
  width: 100%;
  text-align: center;
}

.cassian-select-cell { cursor: default; }
.cassian-select-cell input,
#DeviceSelectAll {
  width: 15px;
  height: 15px;
  accent-color: var(--cassian-accent, var(--cassian-color-purple-500));
  cursor: pointer;
}

tr.cassian-row-selected td { background: color-mix(in srgb, var(--cassian-color-purple-500) 9%, transparent) !important; }
table.cassian-table[data-slice5] tbody tr { cursor: pointer; }

.cassian-runstrip {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.35rem 0 1rem;
  font-size: 0.78rem;
  color: var(--cassian-sidebar-muted-text, var(--text-secondary));
}

.cassian-runstrip__chip {
  border: 1px solid var(--cassian-accent, var(--cassian-color-purple-500));
  border-radius: 999px;
  padding: 0.1rem 0.65rem;
  color: var(--text-link);
  font-weight: 600;
  text-decoration: none;
}

.cassian-runstrip__chip:hover { color: var(--text-primary); background: color-mix(in srgb, var(--cassian-color-purple-500) 16%, transparent); }
.cassian-runstrip__all { color: var(--cassian-sidebar-muted-text, var(--text-secondary)); text-decoration: none; }
.cassian-runstrip__all:hover { color: var(--text-primary); }

.cassian-live-filter-note {
  align-self: flex-end;
  color: var(--cassian-sidebar-muted-text, var(--text-secondary));
  font-size: 0.75rem;
  padding-bottom: 0.6rem;
}

/* ── Z-index scale ──────────────────────────────────────────────────
   One ladder for all Cassian chrome; new layers take a token, never a
   literal. Fixed points we layer around: Metronic .menu-sub-dropdown
   sits at 107 and Bootstrap modals base at 1050 (ModalManager adds +2
   per stacked modal), so tokens on those bands must not move. */
:root {
  --cassian-z-raised: 30;            /* in-page floating chrome (bulk action bar) */
  --cassian-z-workspace-strip: 106;  /* must stay below Metronic dropdowns (107) */
  --cassian-z-header: 1000;          /* legacy header/topbar band */
  --cassian-z-modal: 1050;           /* ModalManager base */
  --cassian-z-app-chrome: 1095;      /* deployment-mode chrome */
  --cassian-z-command-center: 1200;  /* command-center overlay + backdrop */
  --cassian-z-system-overlay: 10500; /* shortcuts cheat-sheet */
  --cassian-z-risk-confirm: 10600;   /* risk confirmation — always topmost */
}

/* ── Notice (theme-aware alert) ─────────────────────────────────────
   Bootstrap's .alert-* ship light-theme backgrounds (cream, pale blue)
   that sit on Cassian's dark surfaces as glaring blocks — the licence
   panel on the Nexus settings screen was the visible case. This is the
   same semantics on the status tokens the rest of the system already
   uses: a tinted wash, a left accent in the status colour, and body
   text that stays at --text-primary so the message is the readable
   part rather than the container.

   Tone classes mirror .cassian-status-badge--* deliberately, so a
   screen that badges something "degraded" can raise a notice in the
   same colour without a second vocabulary. */
.cassian-notice {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border-default);
  border-left: 3px solid var(--status-pending);
  border-radius: 6px;
  background: color-mix(in srgb, var(--cassian-color-white) 3%, transparent);
  color: var(--text-primary);
  font-size: 0.875rem;
}

.cassian-notice__icon { flex: 0 0 auto; line-height: 1.45; color: var(--status-pending); }

.cassian-notice--warning,
.cassian-notice--degraded {
  border-left-color: var(--status-degraded);
  background: color-mix(in srgb, var(--status-degraded) 8%, transparent);
}
.cassian-notice--warning .cassian-notice__icon,
.cassian-notice--degraded .cassian-notice__icon { color: var(--status-degraded); }

.cassian-notice--critical,
.cassian-notice--danger {
  border-left-color: var(--status-critical);
  background: color-mix(in srgb, var(--status-critical) 8%, transparent);
}
.cassian-notice--critical .cassian-notice__icon,
.cassian-notice--danger .cassian-notice__icon { color: var(--status-critical); }

.cassian-notice--healthy,
.cassian-notice--success {
  border-left-color: var(--status-healthy);
  background: color-mix(in srgb, var(--status-healthy) 8%, transparent);
}
.cassian-notice--healthy .cassian-notice__icon,
.cassian-notice--success .cassian-notice__icon { color: var(--status-healthy); }

.cassian-notice--info {
  border-left-color: var(--status-info);
  background: color-mix(in srgb, var(--status-info) 10%, transparent);
}
.cassian-notice--info .cassian-notice__icon { color: var(--text-link); }

/* ── Key/value detail grid ──────────────────────────────────────────
   Replaces `<dl class="row">` with `col-sm-3` label / `col-sm-3` value
   pairs, which had two faults on wide screens: the value landed a
   quarter of the viewport away from its label (unreadable as a pair at
   2000px), and the column arithmetic broke whenever a row mixed a
   col-sm-3 value with a full-width col-sm-9 one — the last pair wrapped
   and orphaned its value.

   auto-fit instead of a fixed column count so the same markup reads on
   a phone and on a wide monitor, and the label sits directly above its
   value so the pairing survives any wrap. `--cassian-kv-min` is the
   knob if a screen needs wider cells. */
.cassian-kv {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--cassian-kv-min, 200px), 1fr));
  gap: 0.85rem 1.5rem;
  margin: 0;
}

.cassian-kv__item { min-width: 0; }

/* A value that needs the full row (a module list, a long hash). */
.cassian-kv__item--wide { grid-column: 1 / -1; }

.cassian-kv__label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 0.15rem;
}

.cassian-kv__value {
  display: block;
  margin: 0;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

/* An unset value should read as absent, not as an empty cell. */
.cassian-kv__value:empty::before {
  content: "—";
  color: var(--text-tertiary);
}

/* ── Live refresh: keep background polls invisible ───────────────────────
   Grids across the app poll every 5-30s. Every poll used to flash a
   "Loading…" caption, which reads as instability rather than freshness.

   Two rules, in order of strength:

   1. A refresh explicitly marked silent (cassian.live.reloadGrid sets
      data-cassian-silent on the wrapper) never shows the caption at all.

   2. Everything else — including user-initiated reloads and the 26 grids that
      bypass the cassian.dataGrid adapter — gets the caption on a DELAY. It
      starts fully transparent and only fades in after the grace period, so a
      fast reload finishes and tears the element down before anything was ever
      visible, while a genuinely slow one still explains itself. A slow load
      with no indicator reads as a broken button, so the feedback is delayed,
      not removed.

   Both DataTables 1.x (.dataTables_processing) and 2.x (.dt-processing) class
   names are covered so this does not silently stop working on an upgrade. */

[data-cassian-silent="1"] .dataTables_processing,
[data-cassian-silent="1"] .dt-processing,
[data-cassian-silent="1"] .cassian-grid-processing {
    opacity: 0 !important;
    animation: none !important;
    transition: none !important;
}

.dataTables_processing,
.dt-processing {
    opacity: 0;
    animation: cassian-processing-fade-in 120ms ease-out 250ms forwards;
}

@keyframes cassian-processing-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Respect the viewer's motion preference: no fade, and no chart easing
   either — the data still updates, it just arrives rather than travels. */
@media (prefers-reduced-motion: reduce) {
    .dataTables_processing,
    .dt-processing {
        animation-duration: 1ms;
        animation-delay: 250ms;
    }
}
