/* Halo Apps portal styles — shared stylesheet
 *
 * Goals:
 *  - Match Halo's existing dark-mode palette so injected nodes look native.
 *  - Reuse Halo's own utility classes where possible (`card`, `highlight`,
 *    `text-safe`, `clr-neutral-*`) — see captured DOM in the file-analysis
 *    modal. These styles are *additive* and namespaced to either
 *    `.safeware-ext` (legacy) or `.haloext-ext` (new). Both work — the
 *    loader's `addExtClasses` helper co-emits them so existing operator
 *    CSS overrides keep applying and new overrides can use either prefix.
 *    See docs/DEPRECATION-POLICY.md in glasswall-macro for the rename
 *    schedule.
 *  - Stay framework-free (no React/Vue). One stylesheet, no build step.
 *
 * Design tokens (`--haloext-*`) below mirror `_design-tokens.css`. They live
 * inline here because the deploy ships ONE css file via the
 * portal-extensions-assets ConfigMap; new extensions should reference these
 * tokens (see STYLE-GUIDE.md) rather than inventing palette values.
 *
 * Token-migration status: PROGRESSIVE. Many existing component blocks
 * below still use literal `#hex`, `0.75rem`, `rgba(...)` values. The
 * STYLE-GUIDE §8 boy-scout rule applies: new code uses tokens, touched
 * blocks adopt tokens in the same PR, but don't sweep the whole file
 * in one go — visual-regression risk on ~1400 lines is too high.
 */

:root {
  /* Brand */
  --haloext-color-brand-primary:   #6c5ce7;
  --haloext-color-brand-secondary: #4ec5b6;
  --haloext-color-brand-accent:    #0984e3;

  /* Surfaces */
  --haloext-color-surface-0:       #0a121f;
  --haloext-color-surface-1:       #14192a;
  --haloext-color-surface-2:       #1a2030;
  --haloext-color-surface-3:       rgba(0, 0, 0, 0.55);
  --haloext-color-border:          rgba(255, 255, 255, 0.08);
  --haloext-color-border-strong:   rgba(255, 255, 255, 0.18);

  /* Text */
  --haloext-color-text-primary:    #e7eaf0;
  --haloext-color-text-secondary:  rgba(255, 255, 255, 0.62);
  --haloext-color-text-tertiary:   rgba(255, 255, 255, 0.4);
  --haloext-color-text-inverse:    #0a121f;

  /* States */
  --haloext-color-safe-fg:         #4ade80;
  --haloext-color-safe-bg:         rgba(22, 163, 74, 0.15);
  --haloext-color-safe-border:     rgba(22, 163, 74, 0.40);
  --haloext-color-info-fg:         #60a5fa;
  --haloext-color-info-bg:         rgba(59, 130, 246, 0.15);
  --haloext-color-info-border:     rgba(59, 130, 246, 0.40);
  --haloext-color-warn-fg:         #fbbf24;
  --haloext-color-warn-bg:         rgba(245, 158, 11, 0.15);
  --haloext-color-warn-border:     rgba(245, 158, 11, 0.40);
  --haloext-color-risk-fg:         #f87171;
  --haloext-color-risk-bg:         rgba(239, 68, 68, 0.15);
  --haloext-color-risk-border:     rgba(239, 68, 68, 0.40);

  /* Typography */
  --haloext-font-mono:             "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --haloext-font-size-xs:          0.75rem;
  --haloext-font-size-sm:          0.85rem;
  --haloext-font-size-base:        0.95rem;
  --haloext-font-size-md:          1.05rem;
  --haloext-font-size-lg:          1.2rem;
  --haloext-font-size-xl:          1.5rem;
  --haloext-font-weight-regular:   400;
  --haloext-font-weight-medium:    500;
  --haloext-font-weight-semibold:  600;
  --haloext-font-weight-bold:      700;
  --haloext-line-height-tight:     1.25;
  --haloext-line-height-base:      1.5;
  --haloext-line-height-loose:     1.75;

  /* Spacing (4px scale) */
  --haloext-space-1:               0.25rem;
  --haloext-space-2:               0.5rem;
  --haloext-space-3:               0.75rem;
  --haloext-space-4:               1rem;
  --haloext-space-5:               1.25rem;
  --haloext-space-6:               1.5rem;
  --haloext-space-8:               2rem;

  /* Radii */
  --haloext-radius-sm:             4px;
  --haloext-radius-md:             8px;
  --haloext-radius-lg:             12px;
  --haloext-radius-pill:           999px;

  /* Elevation */
  --haloext-shadow-md:             0 16px 40px rgba(0, 0, 0, 0.45);
  --haloext-shadow-lg:             0 24px 80px rgba(0, 0, 0, 0.55);

  /* Focus ring */
  --haloext-focus-ring:            0 0 0 2px rgba(108, 92, 231, 0.30);

  /* Motion */
  --haloext-transition-fast:       100ms ease-out;
  --haloext-transition-base:       180ms ease-out;
}

.safeware-ext,
.haloext-ext {
  /* Layout: behave like Halo's stacked sections in the right pane. */
  display: block;
  margin-top: 1.5rem;
}

.safeware-ext__header,
.haloext-ext__header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.safeware-ext__header h3,
.haloext-ext__header h3 {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
}

.safeware-ext__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  background: rgba(16, 185, 129, 0.15); /* matches Halo's "safe" green */
  color: #10b981;
  border: 1px solid rgba(16, 185, 129, 0.4);
}

.safeware-ext__badge--info {
  background: rgba(59, 130, 246, 0.15);
  color: #60a5fa;
  border-color: rgba(59, 130, 246, 0.4);
}

.safeware-ext__badge--warn {
  background: rgba(245, 158, 11, 0.15);
  color: #fbbf24;
  border-color: rgba(245, 158, 11, 0.4);
}

.safeware-ext__badge--risk {
  background: rgba(239, 68, 68, 0.15);
  color: #f87171;
  border-color: rgba(239, 68, 68, 0.4);
}

/* Mirror Halo's `.card.highlight` rounded panel. Selectors here are intentionally
 * loose so extensions can also just slap `class="card highlight"` on their own
 * markup and pick up the existing Halo styling for free. */
.safeware-ext__card {
  background: #0f1d2e; /* tuned to sit alongside Halo's dark surfaces */
  border: 1px solid rgba(148, 163, 184, 0.18);
  border-radius: 0.5rem;
  padding: 1rem 1.25rem;
}

.safeware-ext__footer {
  margin-top: 0.5rem;
  font-size: 0.75rem;
  opacity: 0.6;
}

/* ── Macro analysis subsection (01-macro-detection.js) ────────────────
 * Inserted inside Halo's existing "Macros" accordion content. We blend
 * with Halo's typography (no card chrome — we're already inside one). */

.safeware-macro-analysis__hr {
  margin: 1rem 0;
  border: 0;
  border-top: 1px solid rgba(148, 163, 184, 0.18);
}

.safeware-macro-analysis__heading {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem 0;
}

/* Risk-level badge — colours match Halo's high-risk / medium-risk /
 * low-risk gauge tints so the badge feels native to the existing UI. */
.safeware-macro-analysis__risk {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  background: rgba(148, 163, 184, 0.15);
  color: #cbd5e1;
  border: 1px solid rgba(148, 163, 184, 0.4);
}
.safeware-macro-analysis__risk--high {
  background: rgba(239, 68, 68, 0.15);
  color: #f87171;
  border-color: rgba(239, 68, 68, 0.4);
}
.safeware-macro-analysis__risk--medium {
  background: rgba(245, 158, 11, 0.15);
  color: #fbbf24;
  border-color: rgba(245, 158, 11, 0.4);
}
.safeware-macro-analysis__risk--low {
  background: rgba(34, 197, 94, 0.15);
  color: #4ade80;
  border-color: rgba(34, 197, 94, 0.4);
}
.safeware-macro-analysis__risk--none {
  background: rgba(148, 163, 184, 0.15);
  color: #cbd5e1;
  border-color: rgba(148, 163, 184, 0.4);
}

.safeware-macro-analysis__summary {
  margin: 0.5rem 0;
}

.safeware-macro-analysis__h6 {
  margin: 0.75rem 0 0.25rem 0;
}

.safeware-macro-analysis__findings {
  margin: 0;
  padding-left: 1.25rem;
}
.safeware-macro-analysis__findings li {
  margin: 0.4rem 0;
}

.safeware-macro-analysis__dim {
  opacity: 0.7;
}

.safeware-macro-analysis__mitre {
  margin-left: 0.4rem;
  font-size: 0.78em;
  padding: 0.1rem 0.4rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 0.25rem;
}

/* glasswall-macro 0.8.15+ — ML classifier finding styling.
 *
 * Distinguishes findings emitted by the CodeBERT classifier
 * (ruleId starts with ``GW.MACRO.ML.``) from deterministic rule
 * findings. The badge sits before the title; the probability
 * percentage sits after severity. The list-item gets a subtle
 * left border so the operator can scan ML vs rule hits visually
 * even when the list is long. */
.safeware-macro-analysis__finding-ml {
  border-left: 2px solid rgba(120, 180, 255, 0.45);
  padding-left: 0.5rem;
  margin-left: -0.5rem;
}

.safeware-macro-analysis__ml-badge {
  display: inline-block;
  font-size: 0.7em;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.08rem 0.35rem;
  margin-right: 0.4rem;
  background: linear-gradient(
    180deg,
    rgba(120, 180, 255, 0.18) 0%,
    rgba(120, 180, 255, 0.08) 100%
  );
  color: rgba(180, 215, 255, 0.95);
  border: 1px solid rgba(120, 180, 255, 0.35);
  border-radius: 0.25rem;
  text-transform: uppercase;
  vertical-align: middle;
}

.safeware-macro-analysis__ml-pct {
  margin-left: 0.5rem;
  font-size: 0.82em;
  font-weight: 600;
  padding: 0.08rem 0.4rem;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 0.25rem;
  color: rgba(255, 255, 255, 0.95);
}

.safeware-macro-analysis__evidence {
  font-size: 0.85em;
}

/* 0.6.1+ — "Found in: Module1 ThisDocument" footer on each finding.
 * Shown only when glasswall-macro 0.6.1+ populates ``locations`` on a
 * finding. Sits below the evidence line at lower opacity so the
 * primary signal (title + severity) stays dominant. */
.safeware-macro-analysis__locations {
  margin-top: 0.25rem;
  font-size: 0.78em;
  opacity: 0.7;
}

.safeware-macro-analysis__location {
  font-family: inherit;
  font-size: 0.95em;
  padding: 0 0.25rem;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 0.2rem;
}

.safeware-macro-analysis__macros {
  margin: 0.5rem 0;
  opacity: 0.85;
}

/* 0.9.5+ — "View call graph" link (Halo text-link pattern) +
 * portal-rooted overlay modal hosting the rendered PNG.
 *
 * Replaced the prior in-card button + inline <img> host because:
 *   - the PNG renders ~600px wide; Halo's accordion is narrower,
 *   - the in-card host got wiped by Blazor DOM diff cycles,
 *   - other extensions (02 / 05 / 06) already use the same
 *     backdrop+card modal pattern, so the dismiss UX is consistent.
 *
 * Link styling mirrors `.safeware-encryption-details` (teal underline,
 * brightness:1.15 on hover) so the operator sees one "Halo text link"
 * shape across every extension.
 */
.safeware-macro-analysis__graph {
  margin: var(--haloext-space-3) 0 0;
}

.safeware-macro-analysis__graph-link,
.safeware-macro-analysis__graph-link:visited,
.safeware-macro-analysis__graph-link:hover,
.safeware-macro-analysis__graph-link:focus {
  color: var(--haloext-color-brand-secondary);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  font-size: var(--haloext-font-size-sm);
}
.safeware-macro-analysis__graph-link:hover {
  filter: brightness(1.15);
}
.safeware-macro-analysis__graph-link:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
  border-radius: var(--haloext-radius-sm);
}

/* Modal — matches the §2.6 recipe in STYLE-GUIDE.md (surface-3 backdrop,
 * surface-1 card, border-strong + radius-lg + shadow-lg). z-index sits
 * above Halo's own modals and the password-protected extension. Body
 * stacks a toolbar above a scrollable diagram/source area (was a single
 * centered <img> before #1137 switched the endpoint from PNG to Mermaid
 * text). */
.safeware-macro-graph-modal {
  position: fixed;
  inset: 0;
  z-index: 1000000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.safeware-macro-graph-modal__backdrop {
  position: absolute;
  inset: 0;
  background: var(--haloext-color-surface-3);
  backdrop-filter: blur(2px);
}
.safeware-macro-graph-modal__card {
  position: relative;
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border-strong);
  border-radius: var(--haloext-radius-lg);
  box-shadow: var(--haloext-shadow-lg);
  max-width: min(90vw, 1100px);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.safeware-macro-graph-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--haloext-space-4);
  padding: var(--haloext-space-4) var(--haloext-space-5);
  border-bottom: 1px solid var(--haloext-color-border);
}
.safeware-macro-graph-modal__title {
  margin: 0;
  font-size: var(--haloext-font-size-md);
  font-weight: var(--haloext-font-weight-semibold);
  color: var(--haloext-color-text-primary);
  line-height: var(--haloext-line-height-tight);
}
.safeware-macro-graph-modal__title code {
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-sm);
  color: var(--haloext-color-text-secondary);
  background: transparent;
  word-break: break-all;
}
/* Close affordance is Halo's native `<img class="close pointer">` —
 * the portal already styles `.close.pointer` (size, cursor, hover
 * opacity). We add only a focus ring + a minor flex shrink-guard so
 * the header layout keeps the icon at its native dimensions. */
.safeware-macro-graph-modal__close {
  flex: none;
}
.safeware-macro-graph-modal__close:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
  border-radius: var(--haloext-radius-sm);
}
.safeware-macro-graph-modal__body {
  padding: var(--haloext-space-4) var(--haloext-space-5) var(--haloext-space-5);
  overflow: auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--haloext-space-3);
}
.safeware-macro-graph-modal__hint {
  margin: var(--haloext-space-3) 0;
  font-size: var(--haloext-font-size-sm);
  color: var(--haloext-color-text-secondary);
  text-align: center;
}
.safeware-macro-graph-modal__hint--error {
  color: var(--haloext-color-risk-fg);
}

/* Toolbar (Copy / Download .mmd) — shown above the diagram in BOTH the
 * rendered-SVG and raw-source-fallback states. Buttons carry Halo's own
 * `secondary`/`h-2.5` utility classes for the base look (same pattern the
 * confirm-dialog buttons use, see .haloext-confirm__footer in the haloext-ui block);
 * this block is a minimal fallback in case those Halo styles haven't
 * loaded for the modal. */
.safeware-macro-graph-modal__toolbar {
  display: flex;
  gap: var(--haloext-space-2);
  flex: none;
}
.safeware-macro-graph-modal__toolbar-btn {
  padding: 0.4rem 0.9rem;
  border-radius: var(--haloext-radius-sm);
  cursor: pointer;
  font-size: var(--haloext-font-size-sm);
  font-weight: var(--haloext-font-weight-medium);
}
.safeware-macro-graph-modal__toolbar-btn:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}

/* Diagram container — scrollable with a max-height so a big call graph
 * (many modules/nodes) doesn't overflow the modal card. */
.safeware-macro-graph-modal__diagram {
  overflow: auto;
  max-height: calc(90vh - 10rem);
}
.safeware-macro-graph-modal__diagram svg {
  display: block;
  max-width: 100%;
  height: auto;
}

/* Raw Mermaid source fallback (dagre failed to load, or the text didn't
 * parse/lay out) — monospace, scrollable, and wrapped so long lines don't
 * force horizontal scroll on top of the modal's own vertical scroll. */
.safeware-macro-graph-modal__source {
  margin: 0;
  padding: var(--haloext-space-3);
  background: var(--haloext-color-surface-2);
  border: 1px solid var(--haloext-color-border);
  border-radius: var(--haloext-radius-sm);
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-xs);
  color: var(--haloext-color-text-primary);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: calc(90vh - 10rem);
  overflow: auto;
}

/* ── Call-graph SVG (buildSvg() in 01-macro-detection.js) ─────────────
 * Node-kind fills mirror the ACIE SCREAMING_SNAKE `Kind` tokens Prism
 * emits (see MermaidGraphWriter.cs SafeClass()), lowercased + hyphenated
 * by classForKind(). The plain `__node-rect` rule is the default for any
 * kind without a dedicated variant below — compound selectors have higher
 * specificity, so the variants always win regardless of source order. */
.safeware-macro-graph__subgraph-rect {
  fill: none;
  stroke: var(--haloext-color-border);
  stroke-width: 1;
  stroke-dasharray: 4 3;
}
.safeware-macro-graph__subgraph-label {
  fill: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-xs);
  font-weight: var(--haloext-font-weight-medium);
}
.safeware-macro-graph__edge {
  fill: none;
  stroke: var(--haloext-color-text-tertiary);
  stroke-width: 1.5;
}
.safeware-macro-graph__edge-label {
  fill: var(--haloext-color-text-secondary);
  font-size: 9px;
}
.safeware-macro-graph__arrowhead {
  fill: var(--haloext-color-text-tertiary);
}
.safeware-macro-graph__node-rect {
  fill: var(--haloext-color-surface-2);
  stroke: var(--haloext-color-border-strong);
  stroke-width: 1;
}
.safeware-macro-graph__node-label {
  fill: var(--haloext-color-text-primary);
  font-family: var(--haloext-font-mono);
  font-size: 10px;
  text-anchor: middle;
  dominant-baseline: middle;
}
.safeware-macro-graph__node--ioc .safeware-macro-graph__node-rect {
  fill: var(--haloext-color-risk-bg);
  stroke: var(--haloext-color-risk-fg);
}
.safeware-macro-graph__node--external-destination .safeware-macro-graph__node-rect {
  fill: var(--haloext-color-warn-bg);
  stroke: var(--haloext-color-warn-fg);
}
.safeware-macro-graph__node--api-call .safeware-macro-graph__node-rect {
  fill: var(--haloext-color-info-bg);
  stroke: var(--haloext-color-info-fg);
}
.safeware-macro-graph__node--trigger .safeware-macro-graph__node-rect {
  fill: rgba(108, 92, 231, 0.15);
  stroke: var(--haloext-color-brand-primary);
}
.safeware-macro-graph__node--procedure .safeware-macro-graph__node-rect {
  fill: var(--haloext-color-safe-bg);
  stroke: var(--haloext-color-safe-fg);
}
/* Structural nodes (the document/container scaffolding a macro hangs off).
   Deliberately muted: they carry no risk signal of their own, so they must
   not compete visually with the IOC / external-destination nodes above. */
.safeware-macro-graph__node--file .safeware-macro-graph__node-rect,
.safeware-macro-graph__node--container-part .safeware-macro-graph__node-rect,
.safeware-macro-graph__node--active-object .safeware-macro-graph__node-rect {
  fill: rgba(255, 255, 255, 0.04);
  stroke: var(--haloext-color-border-strong);
}
/* Deobfuscated string — the payload a macro tried to hide. Brand-accent
   outline so the analyst's eye lands on it without implying a verdict. */
.safeware-macro-graph__node--decoded-string .safeware-macro-graph__node-rect {
  fill: rgba(0, 206, 201, 0.12);
  stroke: var(--haloext-color-brand-accent);
}
/* Behaviour primitive — the ACIE classification of what the macro DOES.
   Warn-toned but softer than a confirmed IOC. */
.safeware-macro-graph__node--behavior-primitive .safeware-macro-graph__node-rect {
  fill: var(--haloext-color-warn-bg);
  stroke: var(--haloext-color-warn-border);
}

.safeware-macro-analysis__meta {
  margin: 0.5rem 0 0 0;
  opacity: 0.5;
  font-size: 0.75rem;
}

.safeware-macro-analysis__hint {
  margin: 0.5rem 0;
  opacity: 0.7;
}

.safeware-macro-analysis__status-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 0.65rem;
  padding: 0.55rem 0.75rem;
  border-left: 3px solid var(--haloext-color-warn-fg);
  border-radius: 4px;
  background: var(--haloext-color-warn-bg);
}

.safeware-macro-analysis__status-line strong {
  color: var(--haloext-color-warn-fg);
}

/* === CLEANROOM-CSS-BEGIN === */
/* Halo CleanRoom password-protected extension styles. Merge into
   halo-iac/halo-tf/azure-vm/portal-extensions/extensions.css. */

.safeware-pwd-panel {
    margin-top: 1rem;
}
.safeware-pwd-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.55rem 0.8rem;
    margin-bottom: 0.6rem;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 6px;
    font-size: 0.95rem;
}
.safeware-pwd-submit {
    background: linear-gradient(135deg, #6c5ce7, #0984e3);
    color: white;
    border: none;
    padding: 0.6rem 1.2rem;
    border-radius: 6px;
    font-weight: 600;
    cursor: pointer;
}
.safeware-pwd-submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.safeware-pwd-error {
    margin-top: 0.6rem;
    color: #d63031;
    font-size: 0.85rem;
}

.safeware-pwd-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: rgba(108, 92, 231, 0.12);
    border: 1px solid rgba(108, 92, 231, 0.3);
    border-radius: 999px;
    padding: 0.25rem 0.75rem;
    font-size: 0.85rem;
    color: var(--safeware-text, #1a2030);
    margin: 0.5rem 0;
}

/* Encryption › Settings (05-protection-settings.js) — "could not be
   processed" placeholder-PDF editor. Everything else on that tab uses the
   shared haloext-* layer; these two rules are layout-only and specific to
   this editor, so they stay panel-private: the two-column grid (editor
   left, rendered preview right, single column on narrow viewports) and
   the "PDF page" preview surface below, whose literal white / Helvetica
   11pt mimic the rendered attachment, not the portal theme. */
.safeware-pwd-protected-md__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--haloext-space-4);
    margin-top: var(--haloext-space-2);
}
@media (max-width: 900px) {
    .safeware-pwd-protected-md__grid {
        grid-template-columns: 1fr;
    }
}
/* "Page" feel: white surface, subtle shadow, US-Letter-ish proportions
   capped at the column width. The watcher renders Helvetica on US-Letter
   at attachment time; this preview is approximate (CSS line-wrapping is
   based on the browser font, not Helvetica), so a tooltip below the
   preview tells the operator that. */
.safeware-pwd-protected-md__preview-page {
    background: #ffffff;
    color: #1a2030;
    border: 1px solid var(--haloext-color-border-strong);
    border-radius: var(--haloext-radius-sm);
    padding: var(--haloext-space-5) var(--haloext-space-6);
    min-height: 220px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    font-family: Helvetica, "Helvetica Neue", Arial, sans-serif;
    font-size: 11pt;
    line-height: 1.5;
    overflow-y: auto;
    max-height: 360px;
}
.safeware-pwd-protected-md__preview-page p {
    margin: 0 0 0.7em 0;
}
.safeware-pwd-protected-md__preview-page p:last-child {
    margin-bottom: 0;
}
.safeware-pwd-protected-md__preview-page .safeware-pwd-protected-md__h,
.safeware-pwd-protected-md__preview-page h2 {
    margin: 0 0 0.7em 0;
    font-size: 18pt;
    font-weight: 700;
    line-height: 1.25;
}
.safeware-pwd-protected-md__preview-page strong {
    font-weight: 700;
}
.safeware-pwd-protected-md__preview-page em {
    font-style: italic;
}

/* === CLEANROOM-CSS-END === */

/* ── Powered-by-Safeware badge (09-safeware-branding.js) ──────────────
 * Small fixed chip, bottom-left of the main content web view, on every
 * Safeware-added or Safeware-modified screen (route list lives in the
 * extension). Deliberately quiet: xs type, tertiary text, translucent,
 * pointer-events disabled so it never intercepts clicks. Sits below
 * modal/backdrop layers (extensions use z-index: 1000 for those).
 * `left` is a fallback: the extension overrides it inline to the content
 * column's left edge so the chip never overlaps the left nav (which has
 * Glasswall's own footer branding). */
.safeware-powered-badge {
  position: fixed;
  left: var(--haloext-space-3);
  bottom: var(--haloext-space-3);
  z-index: 900;
  display: inline-flex;
  align-items: center;
  gap: var(--haloext-space-2);
  padding: var(--haloext-space-1) var(--haloext-space-2);
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border);
  border-radius: var(--haloext-radius-sm);
  font-size: var(--haloext-font-size-xs);
  line-height: var(--haloext-line-height-tight);
  color: var(--haloext-color-text-tertiary);
  opacity: 0.75;
  pointer-events: none;
}
.safeware-powered-badge img {
  display: block;
  height: 13px;
  width: auto;
  opacity: 0.7;
}

/* ── Apps shell (00-apps-shell.js) — sidebar group, page frame, badge ──────
 * Halo's own nav / tab / button classes carry the look (the group is a
 * clone of the Reporting group, the page reuses .protection-tabs and
 * .button-group). These rules only cover what Halo has no class for: an
 * <a> used as a nav row or as a strip tab (reset link styling), the 16 px
 * manifest icons, and the manifest-driven
 * "Powered by" chip — same geometry as .safeware-powered-badge above,
 * which 09 keeps for the non-Apps surfaces it still badges. The shared
 * haloext-ui layer (cards, fields, toast, confirm, …) is a separate block
 * later in this file; the shell's toolbar / body / overview rules live in
 * that block's tail — no selector is declared twice (its contract test
 * pins the split). Tokens only (STYLE-GUIDE §8, spec §12); the
 * px/z-index/opacity literals below each mirror an existing rule (named). */
.safeware-apps-nav__item,
.safeware-apps-nav__list {
  color: inherit;
  text-decoration: none;
}
/* The shell's nodes sit inside Halo's own layout — the nav column's gap and
   the page column's padding space them — so the `.safeware-ext` marker's
   stacked-section top margin (for panels appended under vendor content) must
   not apply to them: it pushed the group 24 px below Protection settings and
   the page title 24 px below Halo's (verified on halo-demo 2.21.0-201948). */
.safeware-apps-nav,
.safeware-apps-page {
  margin-top: 0;
}
/* Header icon, drawn exactly as Halo draws its nav glyphs (verified on
   halo-demo 2.21.0-201948): the 40 × 40 <i> is a replaced element —
   `i.nav-bar-reporting-inactive { content: var(--image-nav-bar-…) }` — whose
   svg paints the glyph inside the 11–29 area, white at opacity 0.4 when
   inactive; the active file adds Halo's teal gradient disc (#5CE2C7 →
   #43C2DA, r 20) under a #071D37 glyph. The two images below are the
   shell's 2 × 2 outline grid on that scheme; the <i> fills the w-2.5 /
   h-2.5 box like the vendor's. Literal colours: Halo's svg files, not
   theme tokens — they do not exist as custom properties. */
.safeware-apps-nav__icon {
  display: block;
  width: 100%;
  height: 100%;
  content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40' fill='none'%3E%3Cg opacity='0.4' stroke='white' stroke-width='1.6'%3E%3Crect x='11.75' y='11.75' width='6.8' height='6.8' rx='1.4'/%3E%3Crect x='21.45' y='11.75' width='6.8' height='6.8' rx='1.4'/%3E%3Crect x='11.75' y='21.45' width='6.8' height='6.8' rx='1.4'/%3E%3Crect x='21.45' y='21.45' width='6.8' height='6.8' rx='1.4'/%3E%3C/g%3E%3C/svg%3E");
}
.safeware-apps-nav__icon--active {
  content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40' fill='none'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='1.81818' y1='22.2727' x2='38.1818' y2='22.2727' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%235CE2C7'/%3E%3Cstop offset='1' stop-color='%2343C2DA'/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx='20' cy='20' r='20' fill='url(%23g)'/%3E%3Cg stroke='%23071D37' stroke-width='1.6'%3E%3Crect x='11.75' y='11.75' width='6.8' height='6.8' rx='1.4'/%3E%3Crect x='21.45' y='11.75' width='6.8' height='6.8' rx='1.4'/%3E%3Crect x='11.75' y='21.45' width='6.8' height='6.8' rx='1.4'/%3E%3Crect x='21.45' y='21.45' width='6.8' height='6.8' rx='1.4'/%3E%3C/g%3E%3C/svg%3E");
}
.safeware-apps-nav__app-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--haloext-color-text-secondary);
}
.safeware-apps-nav__app-icon svg {
  display: block;
  width: var(--haloext-space-4); /* 1rem = the 16 px icon box of spec §6 */
  height: var(--haloext-space-4);
}
.safeware-apps-nav__item .sub-item.nav-highlight-secondary,
.safeware-apps-nav__list .sub-item.nav-highlight-secondary {
  color: var(--haloext-color-text-primary);
}
/* Strip tabs: Halo's `.protection-tabs > *` / `> .selected` rules are
   element-agnostic (verified on halo-demo — padding, min-width 200px, colour,
   `--clr-title`, text-decoration) and style the shell's <a role=tab> exactly
   like the vendor <div role=tab>; the shell adds only the focus ring. An
   earlier `color: inherit` here overrode the selected tab's dark text. */
.safeware-apps-page .protection-tabs a[role="tab"]:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}
.safeware-apps-body__empty {
  margin: 0;
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-sm);
}
.safeware-apps-badge {
  position: fixed;
  left: var(--haloext-space-3);
  bottom: var(--haloext-space-3);
  z-index: 900;
  display: inline-flex;
  align-items: center;
  gap: var(--haloext-space-2);
  padding: var(--haloext-space-1) var(--haloext-space-2);
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border); /* mirrors .safeware-powered-badge verbatim */
  border-radius: var(--haloext-radius-sm);
  font-size: var(--haloext-font-size-xs);
  line-height: var(--haloext-line-height-tight);
  color: var(--haloext-color-text-tertiary);
  opacity: 0.75;
  pointer-events: none;
}
.safeware-apps-badge img {
  display: block;
  height: 13px; /* mirrors .safeware-powered-badge img verbatim */
  width: auto;
  opacity: 0.7;
}
/* ── end Apps shell block — the toolbar / body / overview rules are in the haloext-ui block below ── */

/* ────────────────────────────────────────────────────────────────────
 * 12-large-archive-report.js — Large archive › Report evidence lines.
 * Everything else on the tab is the shared haloext-* layer (STYLE-GUIDE
 * §2). These three stay panel-private because they encode a product
 * decision (plan RR-A §10, MODULE-RESULT-MAPPING.md §5): module verdicts
 * are EVIDENCE for the row's single outcome and must never read as a
 * second verdict — muted monospace block lines, deliberately not
 * pill-shaped — and a VOIDED row strikes its path and its evidence
 * through. Tokens only.
 * ──────────────────────────────────────────────────────────────────── */
.safeware-la-report-evidence-line {
    display: block;
    font-family: var(--haloext-font-mono);
    font-size: var(--haloext-font-size-xs);
    color: var(--haloext-color-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.safeware-la-report-evidence-line--voided,
.haloext-table tr[data-voided] .haloext-table__primary {
    text-decoration: line-through;
    opacity: 0.5;
}
.safeware-la-report-evidence-unavailable {
    font-size: var(--haloext-font-size-xs);
    font-style: italic;
    color: var(--haloext-color-text-tertiary);
}

/* ────────────────────────────────────────────────────────────────────
 * 13-large-archive-files-overlay.js — RR-A row badge on the native
 * Processed files table. Small trailing badge in the filename cell:
 * the entry's ONE terminal outcome (+ evidence count in the tooltip).
 * Same tone mapping as the 12 panel, including the weaker hollow
 * amber for scanned-only and struck grey for voided.
 * ──────────────────────────────────────────────────────────────────── */
.safeware-la-overlay-badge {
    display: inline-block;
    margin-left: 0.5rem;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 500;
    white-space: nowrap;
    vertical-align: middle;
    background: rgba(255, 255, 255, 0.07);
    color: rgba(255, 255, 255, 0.6);
}
.safeware-la-overlay-badge--cleaned { color: #4ade80; background: rgba(22, 163, 74, 0.14); }
.safeware-la-overlay-badge--passedthroughverified { color: #4ec5b6; background: rgba(78, 197, 182, 0.14); }
.safeware-la-overlay-badge--passedbyoverride { color: #c084fc; background: rgba(168, 85, 247, 0.14); }
.safeware-la-overlay-badge--blocked { color: #e57373; background: rgba(229, 115, 115, 0.16); }
.safeware-la-overlay-badge--passedavscanonly {
    color: #fbbf24;
    background: rgba(251, 191, 36, 0.06);
    border: 1px dashed rgba(251, 191, 36, 0.55);
}
.safeware-la-overlay-badge--voided {
    color: rgba(255, 255, 255, 0.45);
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.2);
    text-decoration: line-through;
}
.safeware-la-overlay-badge--unknown {
    color: rgba(255, 255, 255, 0.55);
    background: rgba(255, 255, 255, 0.07);
}

/* ── 16-halo-clients.js — white-label preview (layout rules) ──────────────
 * Everything else on Halo clients › Clients is the shared haloext-* layer
 * (STYLE-GUIDE §2). These four stay panel-private because the preview mocks
 * the CLIENT's attract screen — a white ground (literal on purpose: it is
 * the kiosk's background, not the portal theme) with the uploaded logo
 * centred — and the colour row pairs the native colour swatch with its
 * hex field. Tokens elsewhere. */
.safeware-halo-clients__colour-row input[type="color"] {
    width: 2.2rem; /* swatch size, not spacing */
    height: 2rem;
    padding: 0;
    border: 1px solid var(--haloext-color-border-strong);
    border-radius: var(--haloext-radius-sm);
    background: transparent;
    cursor: pointer;
}
.safeware-halo-clients__preview {
    border: 1px solid var(--haloext-color-border-strong);
    border-radius: var(--haloext-radius-md);
    min-height: 5.5rem; /* preview size, not spacing */
    display: flex;
    align-items: center;
    justify-content: center;
    background: #ffffff; /* the client's attract-screen ground, not the portal theme */
}
.safeware-halo-clients__preview img {
    max-height: 4rem; /* logo size, not spacing */
    max-width: 80%;
}
.safeware-halo-clients__preview-note {
    color: rgba(0, 0, 0, 0.45); /* on the white preview ground */
    font-size: var(--haloext-font-size-xs);
}

/* ═══ haloext-ui — shared component layer (spec §9) ═══ */
/* One vocabulary for every app and tab. Rule: Halo's class first (primary /
 * secondary / h-2.5 buttons, protection-tabs, rz-* grid classes, card,
 * search-wrapper); a haloext-* class only where Halo has none. Tokens only —
 * no literal colours, and no literal lengths in padding / margin / gap /
 * inset declarations (test/haloext-ui.test.js enforces both). Where a rule
 * needs a component *size* (a toggle track, a dialog width, an icon box) the
 * value is a size, not spacing, and says so in a comment. Consumed by
 * 00-apps-shell.js (ext.ui, overview) and by every migrated panel; the
 * per-panel *-toast / *-confirm / __card / __fieldset families are deleted
 * as each panel moves onto the shell. The tail of this block ("Apps shell
 * surfaces") styles the shell's toolbar, body and overview list; the sidebar
 * group, page frame and Powered-by chip are styled by the "Apps shell
 * (00-apps-shell.js)" block earlier in this file. `!important` is permitted
 * only inside `.haloext-table--fixed` (it overrides Radzen's compiled/inline
 * grid styles) and on `.haloext-link`'s colour (Halo's anchor rule) —
 * nowhere else. */

/* ── Card ─────────────────────────────────────────────────────────────── */
.haloext-card {
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border);
  border-radius: var(--haloext-radius-md);
  padding: var(--haloext-space-5) var(--haloext-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-5);
  color: var(--haloext-color-text-primary);
}
.haloext-card--nested {
  background: var(--haloext-color-surface-2);
  padding: var(--haloext-space-4) var(--haloext-space-5);
  gap: var(--haloext-space-3);
}
.haloext-card__title {
  margin: 0;
  font-size: var(--haloext-font-size-md);
  font-weight: var(--haloext-font-weight-semibold);
  line-height: var(--haloext-line-height-tight);
  color: var(--haloext-color-text-primary);
}
.haloext-card__intro {
  margin: 0;
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-base);
  line-height: var(--haloext-line-height-base);
}

/* ── Fieldset ─────────────────────────────────────────────────────────── */
.haloext-fieldset {
  border: 1px solid var(--haloext-color-border);
  border-radius: var(--haloext-radius-md);
  padding: var(--haloext-space-4) var(--haloext-space-5);
  margin: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-3);
  background: var(--haloext-color-surface-2);
}
.haloext-fieldset__legend {
  padding: 0 var(--haloext-space-2);
  font-weight: var(--haloext-font-weight-semibold);
  font-size: var(--haloext-font-size-base);
  color: var(--haloext-color-text-primary);
}

/* ── Field row: label / control (+ unit) / help ──────────────────────── */
.haloext-field {
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-2);
  padding: var(--haloext-space-1) 0;
}
.haloext-field__label {
  font-size: var(--haloext-font-size-base);
  font-weight: var(--haloext-font-weight-medium);
  color: var(--haloext-color-text-primary);
}
.haloext-field__control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--haloext-space-2);
}
.haloext-field__unit {
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-sm);
}
.haloext-field__help {
  font-size: var(--haloext-font-size-sm);
  color: var(--haloext-color-text-secondary);
  line-height: var(--haloext-line-height-base);
}
.haloext-field__help code,
.haloext-toggle__help code,
.haloext-card__intro code {
  background: var(--haloext-color-surface-2);
  border-radius: var(--haloext-radius-sm);
  padding: 0 var(--haloext-space-2);
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-sm);
}

/* ── Inputs ───────────────────────────────────────────────────────────── */
.haloext-input,
.haloext-select,
.haloext-textarea {
  box-sizing: border-box;
  padding: var(--haloext-space-2) var(--haloext-space-3);
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border-strong);
  border-radius: var(--haloext-radius-sm);
  color: var(--haloext-color-text-primary);
  font-size: var(--haloext-font-size-base);
  line-height: var(--haloext-line-height-base);
  font-family: inherit;
  max-width: 100%;
  transition: border-color var(--haloext-transition-fast), box-shadow var(--haloext-transition-fast);
}
.haloext-input[type="number"] {
  width: 9rem; /* size, not spacing */
}
.haloext-input[type="text"],
.haloext-input[type="search"],
.haloext-input[type="email"],
.haloext-input[type="url"] {
  width: 18rem; /* size, not spacing */
}
.haloext-input--wide,
.haloext-textarea {
  width: 100%;
}
.haloext-textarea {
  min-height: 6rem; /* size, not spacing */
  resize: vertical;
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-sm);
}
.haloext-input:focus,
.haloext-select:focus,
.haloext-textarea:focus {
  outline: none;
  border-color: var(--haloext-color-brand-primary);
  box-shadow: var(--haloext-focus-ring);
}
.haloext-input:disabled,
.haloext-select:disabled,
.haloext-textarea:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.haloext-input[aria-invalid="true"],
.haloext-select[aria-invalid="true"],
.haloext-textarea[aria-invalid="true"] {
  border-color: var(--haloext-color-risk-border);
}
.haloext-textarea__counter {
  font-size: var(--haloext-font-size-xs);
  color: var(--haloext-color-text-tertiary);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ── Toggle (checkbox switch) ─────────────────────────────────────────── */
.haloext-toggle {
  position: relative;
  display: inline-grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--haloext-space-3);
  row-gap: var(--haloext-space-1);
  align-items: center;
  cursor: pointer;
  padding: var(--haloext-space-1) 0;
}
.haloext-toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
}
.haloext-toggle__track {
  position: relative;
  grid-row: 1 / span 2;
  display: inline-block;
  width: 2.25rem; /* track size, not spacing */
  height: 1.25rem;
  border-radius: var(--haloext-radius-pill);
  background: var(--haloext-color-border-strong);
  transition: background-color var(--haloext-transition-base);
}
.haloext-toggle__track::after {
  content: "";
  position: absolute;
  top: calc(var(--haloext-space-1) / 2);
  left: calc(var(--haloext-space-1) / 2);
  width: 1rem; /* knob size, not spacing */
  height: 1rem;
  border-radius: var(--haloext-radius-pill);
  background: var(--haloext-color-text-primary);
  transition: transform var(--haloext-transition-base);
}
.haloext-toggle input:checked + .haloext-toggle__track {
  background: var(--haloext-color-brand-primary);
}
.haloext-toggle input:checked + .haloext-toggle__track::after {
  transform: translateX(1rem);
}
.haloext-toggle input:focus-visible + .haloext-toggle__track {
  box-shadow: var(--haloext-focus-ring);
}
.haloext-toggle input:disabled + .haloext-toggle__track {
  opacity: 0.45;
  cursor: not-allowed;
}
.haloext-toggle__label {
  font-size: var(--haloext-font-size-base);
  font-weight: var(--haloext-font-weight-medium);
  color: var(--haloext-color-text-primary);
}
.haloext-toggle__label:empty {
  display: none;
}
.haloext-toggle__help {
  grid-column: 2;
  font-size: var(--haloext-font-size-sm);
  color: var(--haloext-color-text-secondary);
  line-height: var(--haloext-line-height-base);
}

/* ── Filters bar (search / date range / selects) ─────────────────────── */
.haloext-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--haloext-space-3);
}
.haloext-filters__group {
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-1);
}
.haloext-filters__label {
  font-size: var(--haloext-font-size-xs);
  color: var(--haloext-color-text-secondary);
  text-transform: uppercase;
}

/* ── Table wrapper over Halo's Radzen grid classes ───────────────────── */
.haloext-table {
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-3);
}
.haloext-table .rz-grid-table {
  width: 100%;
}
.haloext-table .rz-data-row > td {
  vertical-align: middle;
}
/* Opt-in for wide status tables: the grid, not the page, owns overflow. */
.haloext-table--scroll,
.haloext-table--scroll > .rz-data-grid {
  min-width: 0;
  max-width: 100%;
}
.haloext-table--scroll .rz-data-grid-data {
  overflow-x: auto;
  max-width: 100%;
}
.haloext-table--scroll th,
.haloext-table--scroll td {
  white-space: nowrap;
}
.haloext-table--scroll .rz-data-grid-data:focus-visible,
.haloext-copy-button:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}
.haloext-code-block {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  min-width: 0;
}
.haloext-table__loading,
.haloext-table__empty,
.haloext-table__error {
  padding: var(--haloext-space-4);
  text-align: center;
  color: var(--haloext-color-text-tertiary);
  font-style: italic;
}
.haloext-table__error {
  color: var(--haloext-color-risk-fg);
  font-style: normal;
}
.haloext-row-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--haloext-space-2);
  white-space: nowrap;
}
.haloext-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--haloext-space-3);
  font-size: var(--haloext-font-size-sm);
  color: var(--haloext-color-text-secondary);
}
.haloext-pager__buttons {
  display: inline-flex;
  gap: var(--haloext-space-2);
}

/* ── Inline alert ─────────────────────────────────────────────────────── */
.haloext-alert {
  border: 1px solid var(--haloext-color-info-border);
  background: var(--haloext-color-info-bg);
  color: var(--haloext-color-info-fg);
  border-radius: var(--haloext-radius-md);
  padding: var(--haloext-space-3) var(--haloext-space-4);
  font-size: var(--haloext-font-size-sm);
  line-height: var(--haloext-line-height-base);
}
.haloext-alert--info {
  border-color: var(--haloext-color-info-border);
  background: var(--haloext-color-info-bg);
  color: var(--haloext-color-info-fg);
}
.haloext-alert--safe {
  border-color: var(--haloext-color-safe-border);
  background: var(--haloext-color-safe-bg);
  color: var(--haloext-color-safe-fg);
}
.haloext-alert--warn {
  border-color: var(--haloext-color-warn-border);
  background: var(--haloext-color-warn-bg);
  color: var(--haloext-color-warn-fg);
}
.haloext-alert--risk {
  border-color: var(--haloext-color-risk-border);
  background: var(--haloext-color-risk-bg);
  color: var(--haloext-color-risk-fg);
}

/* ── Status badge (pill) ──────────────────────────────────────────────── */
.haloext-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--haloext-space-1);
  padding: var(--haloext-space-1) var(--haloext-space-2);
  border-radius: var(--haloext-radius-pill);
  border: 1px solid var(--haloext-color-border-strong);
  background: var(--haloext-color-surface-2);
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-xs);
  font-weight: var(--haloext-font-weight-medium);
  line-height: var(--haloext-line-height-tight);
  white-space: nowrap;
}
.haloext-badge--neutral {
  border-color: var(--haloext-color-border-strong);
  background: var(--haloext-color-surface-2);
  color: var(--haloext-color-text-secondary);
}
.haloext-badge--safe {
  border-color: var(--haloext-color-safe-border);
  background: var(--haloext-color-safe-bg);
  color: var(--haloext-color-safe-fg);
}
.haloext-badge--info {
  border-color: var(--haloext-color-info-border);
  background: var(--haloext-color-info-bg);
  color: var(--haloext-color-info-fg);
}
.haloext-badge--warn {
  border-color: var(--haloext-color-warn-border);
  background: var(--haloext-color-warn-bg);
  color: var(--haloext-color-warn-fg);
}
.haloext-badge--risk {
  border-color: var(--haloext-color-risk-border);
  background: var(--haloext-color-risk-bg);
  color: var(--haloext-color-risk-fg);
}

/* ── Destructive button (on top of Halo's primary / secondary) ───────── */
.haloext-btn--danger {
  background: var(--haloext-color-risk-fg);
  border-color: var(--haloext-color-risk-fg);
  color: var(--haloext-color-text-inverse);
}
.haloext-btn--danger:hover:not(:disabled) {
  opacity: 0.9;
}
.haloext-btn--danger:focus-visible {
  box-shadow: var(--haloext-focus-ring);
}

/* ── Toast (ext.ui.toast) — top-left, dark surface, green check ──────── */
.haloext-toast {
  position: fixed;
  top: var(--haloext-space-4);
  left: var(--haloext-space-4);
  z-index: 999999;
  display: flex;
  align-items: flex-start;
  gap: var(--haloext-space-3);
  min-width: 20rem; /* toast size, not spacing */
  max-width: min(28rem, calc(100vw - 2 * var(--haloext-space-4)));
  padding: var(--haloext-space-4) var(--haloext-space-5) var(--haloext-space-4) var(--haloext-space-4);
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border-strong);
  border-radius: var(--haloext-radius-md);
  color: var(--haloext-color-text-primary);
  box-shadow: var(--haloext-shadow-md);
  font-size: var(--haloext-font-size-sm);
  animation: haloext-toast-in var(--haloext-transition-base);
}
@keyframes haloext-toast-in {
  from { opacity: 0; transform: translateY(calc(-1 * var(--haloext-space-2))); }
  to   { opacity: 1; transform: translateY(0); }
}
.haloext-toast__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem; /* icon box size, not spacing */
  height: 1.75rem;
  flex-shrink: 0;
  border-radius: var(--haloext-radius-pill);
  background: var(--haloext-color-safe-bg);
  color: var(--haloext-color-safe-fg);
}
.haloext-toast__content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-1);
}
.haloext-toast__title {
  font-weight: var(--haloext-font-weight-semibold);
  font-size: var(--haloext-font-size-base);
  line-height: var(--haloext-line-height-tight);
}
.haloext-toast__body {
  color: var(--haloext-color-text-secondary);
  line-height: var(--haloext-line-height-base);
}
.haloext-toast__close {
  background: transparent;
  border: 0;
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-lg); /* the × glyph */
  line-height: 1;
  cursor: pointer;
  padding: 0 var(--haloext-space-1);
  flex-shrink: 0;
}
.haloext-toast__close:hover {
  color: var(--haloext-color-text-primary);
}
.haloext-toast__close:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}

/* ── Confirm dialog (ext.ui.confirm) — Halo's restore-settings modal shape:
 *    amber triangle, bold title, × top-right, divider, body, right-aligned
 *    Cancel + primary action. ───────────────────────────────────────────── */
.haloext-confirm {
  position: fixed;
  inset: 0;
  z-index: 999998;
  display: flex;
  align-items: center;
  justify-content: center;
}
.haloext-confirm__backdrop {
  position: absolute;
  inset: 0;
  background: var(--haloext-color-surface-3);
}
.haloext-confirm__dialog {
  position: relative;
  width: min(34rem, calc(100vw - 2 * var(--haloext-space-4))); /* dialog size, not spacing */
  background: var(--haloext-color-surface-1);
  border: 1px solid var(--haloext-color-border-strong);
  border-radius: var(--haloext-radius-lg);
  color: var(--haloext-color-text-primary);
  box-shadow: var(--haloext-shadow-lg);
  animation: haloext-confirm-in var(--haloext-transition-base);
}
@keyframes haloext-confirm-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
.haloext-confirm__header {
  display: flex;
  align-items: center;
  gap: var(--haloext-space-3);
  padding: var(--haloext-space-4) var(--haloext-space-4) var(--haloext-space-4) var(--haloext-space-5);
}
.haloext-confirm__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--haloext-color-warn-fg);
  flex-shrink: 0;
}
.haloext-confirm__title {
  flex: 1;
  margin: 0;
  font-size: var(--haloext-font-size-md);
  font-weight: var(--haloext-font-weight-semibold);
  line-height: var(--haloext-line-height-tight);
}
.haloext-confirm__close {
  background: transparent;
  border: 1px solid var(--haloext-color-border-strong);
  border-radius: var(--haloext-radius-sm);
  color: inherit;
  font-size: var(--haloext-font-size-lg); /* the × glyph */
  line-height: 1;
  cursor: pointer;
  padding: var(--haloext-space-1) var(--haloext-space-2);
  opacity: 0.7;
  flex-shrink: 0;
}
.haloext-confirm__close:hover {
  opacity: 1;
}
.haloext-confirm__close:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}
.haloext-confirm__divider {
  height: 1px;
  margin: 0;
  background: var(--haloext-color-border);
}
.haloext-confirm__body {
  padding: var(--haloext-space-5) var(--haloext-space-5) var(--haloext-space-4); /* plain text only (ext.ui.confirm writes it with textContent) */
  line-height: var(--haloext-line-height-base);
  color: var(--haloext-color-text-secondary);
  white-space: pre-line; /* a "\n" in the copy becomes a line break */
}
.haloext-confirm__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--haloext-space-3);
  padding: var(--haloext-space-3) var(--haloext-space-5) var(--haloext-space-5);
}
/* Halo's .primary / .secondary / .h-2.5 style the footer buttons; this is the
 * fallback sizing if those rules are not loaded around the dialog. */
.haloext-confirm__footer button {
  padding: var(--haloext-space-2) var(--haloext-space-4);
  border-radius: var(--haloext-radius-sm);
  cursor: pointer;
  font-weight: var(--haloext-font-weight-medium);
}
.haloext-confirm__footer button:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}
.haloext-confirm__cancel,
.haloext-confirm__confirm {
  min-width: 6rem; /* button size, not spacing */
}

/* ── Report surfaces + dialog content ───────────────────────────────────
 * The vocabulary the report tabs share (Encryption / Large archive /
 * ICAP client / Email relay / Service status / Storage audit / Halo
 * clients) and the three modifiers ext.ui.dialog() adds to the confirm
 * markup. Same rules as the rest of this block: tokens only; a literal
 * value is a component size, never spacing, and says so. */

/* Dialog content (ext.ui.dialog): a caller-built Element instead of text. */
.haloext-confirm__dialog--wide {
  width: min(56rem, calc(100vw - 2 * var(--haloext-space-4))); /* dialog size, not spacing */
}
.haloext-confirm__body--content {
  white-space: normal;
  color: var(--haloext-color-text-primary);
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-3);
  max-height: calc(100vh - 14rem); /* dialog size, not spacing — room for header + footer */
  overflow-y: auto;
}
.haloext-confirm__body--content pre {
  margin: 0;
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-xs);
  line-height: var(--haloext-line-height-base);
  white-space: pre-wrap;
  word-break: break-all;
}
.haloext-confirm__error {
  margin: 0;
  color: var(--haloext-color-risk-fg);
  font-size: var(--haloext-font-size-sm);
}
.haloext-confirm__footer:empty {
  display: none; /* a read-only dialog (no buttons) has no footer band */
}
/* Halo lazy-loads route CSS: the file-analysis accordion (07's
 * buildAccordionGroup) relies on .accordian-group and the .text-* verdict
 * colours that only ship with the Processed files route. A dialog opened
 * from an Apps route (hosted on Protection settings) needs these fallbacks;
 * scoped narrowly, they override Halo's classes inside the dialog —
 * intended, so the dialog is on our tokens either way. */
.haloext-confirm__body--content .accordian-group {
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-2);
}
.haloext-confirm__body--content .text-safe {
  color: var(--haloext-color-safe-fg);
}
.haloext-confirm__body--content .text-danger {
  color: var(--haloext-color-risk-fg);
}
.haloext-confirm__body--content .text-unavailable {
  color: var(--haloext-color-text-secondary);
}
.haloext-confirm__body--content .text-warning {
  color: var(--haloext-color-warn-fg);
}

/* Card head (title + badge), compact card, card grid, key/value list */
.haloext-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--haloext-space-3);
}
.haloext-card--compact {
  padding: var(--haloext-space-4) var(--haloext-space-5);
  gap: var(--haloext-space-3);
}
.haloext-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); /* card min size, not spacing */
  gap: var(--haloext-space-4);
}
.haloext-card-grid--stats {
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); /* tile min size, not spacing */
  gap: var(--haloext-space-3);
}
.haloext-kv {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--haloext-space-2) var(--haloext-space-4);
  margin: 0;
  font-size: var(--haloext-font-size-sm);
}
.haloext-kv dt {
  color: var(--haloext-color-text-secondary);
  font-weight: var(--haloext-font-weight-medium);
}
.haloext-kv dd {
  margin: 0;
  min-width: 0;
  color: var(--haloext-color-text-primary);
  overflow-wrap: anywhere;
}
.haloext-kv code,
.haloext-stat code {
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-xs);
}
.haloext-section-title {
  margin: var(--haloext-space-5) 0 var(--haloext-space-3);
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-sm);
  font-weight: var(--haloext-font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.haloext-section-title:first-child {
  margin-top: 0;
}

/* Stat tile — a headline number with a label */
.haloext-stat {
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-2);
  padding: var(--haloext-space-3) var(--haloext-space-4);
  background: var(--haloext-color-surface-2);
  border: 1px solid var(--haloext-color-border);
  border-radius: var(--haloext-radius-md);
}
.haloext-stat--headline {
  border-color: var(--haloext-color-border-strong);
}
.haloext-stat__label {
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-xs);
  font-weight: var(--haloext-font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.haloext-stat__value {
  color: var(--haloext-color-text-primary);
  font-size: var(--haloext-font-size-xl);
  font-weight: var(--haloext-font-weight-semibold);
  font-variant-numeric: tabular-nums;
  line-height: var(--haloext-line-height-tight);
}

/* Filters: the inline popover a Filter button toggles, and the [hidden] the
 * base class was missing (display:flex out-ranks the UA [hidden] rule). */
.haloext-filters--popover {
  padding: var(--haloext-space-3) var(--haloext-space-4);
  background: var(--haloext-color-surface-2);
  border: 1px solid var(--haloext-color-border);
  border-radius: var(--haloext-radius-md);
}
.haloext-filters[hidden] {
  display: none;
}

/* Dense report grid — Halo's Processed files metrics on a table-layout:fixed
 * Radzen grid: 12 px cells, top-aligned, nowrap + ellipsis (widen the column
 * or scroll the grid), the header on Halo's 48 px centred band, the card
 * clipping horizontal overflow so only .rz-data-grid-data scrolls. */
.haloext-table--fixed .card {
  overflow: hidden !important;
  max-width: 100%;
}
.haloext-table--fixed .rz-data-grid {
  overflow-x: hidden !important;
  max-width: 100%;
  min-width: 0;
}
.haloext-table--fixed .rz-data-grid-data {
  overflow-x: auto !important;
  width: 100% !important;
  max-width: 100% !important;
}
.haloext-table--fixed .rz-grid-table {
  table-layout: fixed !important; /* <col> widths are authoritative */
  width: max-content; /* the pre-JS fallback; a resizable grid sets the px total */
  min-width: 0 !important;
}
.haloext-table--fixed td,
.haloext-table--fixed th {
  vertical-align: top !important;
  font-size: var(--haloext-font-size-xs) !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}
.haloext-table--fixed td {
  padding: var(--haloext-space-3) var(--haloext-space-4) !important;
}
.haloext-table--fixed th {
  padding: 0 var(--haloext-space-4) !important;
  vertical-align: middle !important;
}
.haloext-table--fixed .rz-column-title-content {
  font-size: var(--haloext-font-size-xs) !important;
  font-weight: var(--haloext-font-weight-medium) !important;
  color: var(--haloext-color-text-secondary) !important;
}
.haloext-table--fixed td.haloext-table__loading,
.haloext-table--fixed td.haloext-table__empty,
.haloext-table--fixed td.haloext-table__error {
  padding: var(--haloext-space-8) !important;
  white-space: normal !important;
  text-align: center;
}
.haloext-table--fixed .rz-pager .rz-pager-element {
  cursor: pointer;
}
.haloext-table--fixed .rz-pager .rz-pager-element.rz-state-disabled {
  cursor: not-allowed;
  opacity: 0.45;
}
.haloext-table--fixed .rz-pager .rz-pager-page.rz-state-active {
  background: var(--haloext-color-brand-secondary);
  color: var(--haloext-color-text-inverse);
  border-radius: var(--haloext-radius-sm);
}
/* Column resize grip — mirrors Radzen's .rz-column-resizer. Header cells are
 * already position:sticky (a positioning context), so the grip anchors to
 * the cell's right edge without touching `position`. */
.haloext-col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  width: 6px; /* grip size, not spacing — a hair wider than Halo's 4 px */
  height: 100%;
  cursor: col-resize;
  user-select: none;
  touch-action: none;
  z-index: 2;
}
.haloext-col-resizer::after {
  content: "";
  position: absolute;
  top: var(--haloext-space-2);
  bottom: var(--haloext-space-2);
  right: calc(var(--haloext-space-1) / 2);
  width: 2px; /* guide size, not spacing */
  border-radius: 1px;
  background: transparent;
  transition: background-color var(--haloext-transition-fast);
}
.haloext-col-resizer:hover::after,
.haloext-col-resizer--active::after {
  background: var(--haloext-color-brand-secondary);
}
.haloext-table--resizing {
  cursor: col-resize;
  user-select: none;
}

/* Cell text: the primary line (file name, path, client name), the muted
 * monospace sub-line (id, error code, heartbeat age); mono / muted inline. */
.haloext-table__primary {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: var(--haloext-font-weight-medium);
}
.haloext-table__sub {
  display: block;
  margin-top: var(--haloext-space-1);
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-xs);
  line-height: var(--haloext-line-height-tight);
  color: var(--haloext-color-text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.haloext-table__row--muted > td {
  opacity: 0.55; /* a retired / switched-off row recedes but stays legible */
}
.haloext-mono {
  font-family: var(--haloext-font-mono);
}
.haloext-muted {
  color: var(--haloext-color-text-tertiary);
}
/* Action link inside a cell (View analysis, View details). !important plus
 * :visited / :hover / :focus because Halo's compiled <a> baseline colour
 * wins over a single class. */
.haloext-link,
.haloext-link:visited,
.haloext-link:hover,
.haloext-link:focus {
  color: var(--haloext-color-brand-secondary) !important;
  text-decoration: underline;
  text-underline-offset: 2px; /* size, not spacing */
  cursor: pointer;
}
.haloext-link:hover {
  filter: brightness(1.15);
}
/* Icon-only button (copy to clipboard); --flash is the "copied" tint. */
.haloext-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--haloext-space-1) var(--haloext-space-2);
  background: transparent;
  border: none;
  border-radius: var(--haloext-radius-sm);
  color: var(--haloext-color-text-secondary);
  cursor: pointer;
  vertical-align: middle;
  min-width: 24px; /* target size, not spacing */
  min-height: 24px; /* target size, not spacing */
}
.haloext-icon-btn:hover {
  color: var(--haloext-color-brand-secondary);
  background: var(--haloext-color-surface-2);
}
.haloext-icon-btn i.clipboard {
  display: inline-block;
  width: 14px; /* Halo glyph size, not spacing */
  height: 14px;
}
.haloext-icon-btn--flash,
.haloext-icon-btn--flash:hover {
  color: var(--haloext-color-safe-fg);
}
.haloext-link:focus-visible,
.haloext-icon-btn:focus-visible {
  outline: none;
  box-shadow: var(--haloext-focus-ring);
}
/* Badge modifiers: --hollow = a weaker assurance (dashed, no fill) in the
 * tone's colour; --struck = a voided / retired state. */
.haloext-badge--hollow {
  background: transparent;
  border-style: dashed;
}
.haloext-badge--struck {
  text-decoration: line-through;
  opacity: 0.75;
}

/* ── Apps shell surfaces (00-apps-shell.js) — toolbar, body, overview ──
 * Only what Chunk 2's "Apps shell (00-apps-shell.js)" block (sidebar
 * group, page frame, strip tabs, .safeware-apps-body__empty, Powered-by
 * chip) does not cover. Everything else on these surfaces is Halo's own
 * class (button-group, rz-*) or the haloext-ui rules above. */
.safeware-apps-toolbar {
  flex-shrink: 0;
}
.safeware-apps-body {
  display: flex;
  flex-direction: column;
  gap: var(--haloext-space-5);
  min-width: 0;
}

/* Overview list (/apps, spec §8) */
.safeware-apps-overview__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--haloext-space-3);
  flex-wrap: wrap;
}
.safeware-apps-overview__summary {
  margin: 0;
  color: var(--haloext-color-text-secondary);
  font-size: var(--haloext-font-size-sm);
  font-variant-numeric: tabular-nums;
}
.safeware-apps-overview__refresh {
  flex-shrink: 0;
}
.safeware-apps-overview__row--muted > td {
  opacity: 0.55;
}
.safeware-apps-overview__row--muted > td:last-child {
  opacity: 1; /* the Enabled toggle stays legible so the app can be switched back on */
}
.safeware-apps-overview__app {
  display: flex;
  align-items: center;
  gap: var(--haloext-space-3);
  min-width: 0;
}
.safeware-apps-overview__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem; /* icon box size, not spacing */
  height: 2rem;
  flex-shrink: 0;
  border-radius: var(--haloext-radius-md);
  background: var(--haloext-color-surface-2);
  border: 1px solid var(--haloext-color-border);
  color: var(--haloext-color-text-primary);
}
.safeware-apps-overview__icon svg {
  display: block;
  width: 16px; /* manifest icons are 16 px line icons (spec §4) — size, not spacing */
  height: 16px;
  stroke: currentColor;
  fill: none;
}
.safeware-apps-overview__name {
  font-weight: var(--haloext-font-weight-semibold);
  color: var(--haloext-color-text-primary);
}
.safeware-apps-overview__desc {
  font-size: var(--haloext-font-size-sm);
  color: var(--haloext-color-text-secondary);
}
.safeware-apps-overview__publisher-logo {
  display: block;
  height: 14px; /* wordmark size — mirrors the 13px Powered-by chip image; not spacing */
  width: auto;
  opacity: 0.85;
}
.safeware-apps-overview__version {
  font-family: var(--haloext-font-mono);
  font-size: var(--haloext-font-size-sm);
  white-space: nowrap;
}
.safeware-apps-overview__links {
  white-space: nowrap;
}
.safeware-apps-overview__links a {
  color: var(--haloext-color-brand-accent);
}
.safeware-apps-overview__open {
  color: var(--haloext-color-brand-accent);
  font-weight: var(--haloext-font-weight-medium);
}
/* ═══ end haloext-ui ═══ */