/* ===========================================================================
   VScan · reachability viewer
   Aesthetic: engineering schematic / blueprint. The repo is drawn as a wiring
   diagram of code paths; one hot signal marks live reachability only.
   =========================================================================== */

:root {
  /* Blueprint ground */
  --ground:      #0B2239;  /* deep architectural blue */
  --ground-sink: #081A2C;  /* recessed wells (graph stage) */
  --panel:       #0E2A45;  /* raised rail / cards */
  --panel-edge:  rgba(120, 178, 224, 0.22);
  --hair:        rgba(120, 178, 224, 0.14); /* hairline rules */
  --grid:        rgba(120, 178, 224, 0.09); /* faint cyan grid lines */

  /* Ink */
  --ink:      #CFE4F2;  /* pale cyan-white */
  --ink-dim:  rgba(207, 228, 242, 0.62);
  --ink-mut:  rgba(207, 228, 242, 0.38);

  /* Structure accents (cyan family, quiet) */
  --dep:   #6FB8E0;  /* dependency fill */
  --fn:    #9FC6E0;  /* function outline */
  --muted: #4E7493;  /* module / file / doc */

  /* The one hot signal — reachability + public entry points only */
  --signal:      #F2B134;  /* amber/gold */
  --signal-soft: rgba(242, 177, 52, 0.16);
  --signal-glow: rgba(242, 177, 52, 0.55);
  --signal-ink:  #F6D28A;  /* lighter amber for readable badge text */

  /* Severity accent — vulnerability only. A warm red-orange, held apart from
     the amber reachability signal so the two read as distinct facts: amber =
     "reached", red = "vulnerable", and a node wearing both is the top problem. */
  --alert:      #F2654E;  /* warning red-orange (KEV / critical) */
  --alert-soft: rgba(242, 101, 78, 0.16);
  --alert-glow: rgba(242, 101, 78, 0.5);
  --alert-ink:  #F79885;  /* lighter tint for text on the dark ground */

  /* Triage "likely safe" — a cool green, distinct from amber and red. */
  --safe:      #4FB286;  /* exploitability triage: likely unaffected */
  --safe-ink:  #7FD3AC;

  --radius: 4px;
  --rail-w: 20rem;
  --detail-w: 21rem;

  --f-display: "Space Grotesk", system-ui, sans-serif;
  --f-body: "Inter", system-ui, sans-serif;
  --f-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", monospace;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--f-body);
  color: var(--ink);
  background: var(--ground);
  /* Blueprint grid wash behind the whole page */
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 28px 28px, 28px 28px;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--signal);
  color: var(--ground);
  padding: 0.5rem 0.9rem;
  font: 600 0.82rem/1 var(--f-mono);
  z-index: 50;
}
.skip-link:focus { left: 0.75rem; top: 0.75rem; }

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------------------------------------------------------------- title block */
/* One slim bar: the mark on the left, the scan selector on the right. The stat
   tiles and the instructional paragraph are gone — the funnel strip below is the
   headline value. */
.titleblock {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap; /* the Meta mark made the bar wider than ~900px viewports */
  gap: 0.5rem 1.5rem;
  padding: 0.6rem 1.4rem;
  border-bottom: 1px solid var(--panel-edge);
  background: linear-gradient(180deg, rgba(14,42,69,0.92), rgba(11,34,57,0.92));
  backdrop-filter: blur(2px);
}

.titleblock__id {
  display: flex;
  align-items: baseline;
  gap: 0.65rem;
}
.wordmark {
  font: 700 1.3rem/1 var(--f-display);
  letter-spacing: -0.02em;
  color: var(--ink);
}
.wordmark::before {
  /* schematic tick: the signal lives in the mark, quietly */
  content: "";
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 0.5rem;
  background: var(--signal);
  box-shadow: 0 0 8px var(--signal-glow);
  vertical-align: 0.1em;
}
.eyebrow {
  font: 500 0.6rem/1 var(--f-mono);
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--signal);
}

.titleblock__load { display: flex; align-items: center; gap: 0.9rem; }

/* Segmented control: the three ways in. A schematic selector switch — one
   throw lit amber, the rest quiet. */
.viewswitch {
  display: inline-flex;
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--ground-sink);
}
.seg {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 0.05rem;
  cursor: pointer;
  padding: 0.42rem 0.8rem;
  border: 0;
  border-left: 1px solid var(--hair);
  background: transparent;
  color: var(--ink-dim);
  font: 500 0.78rem/1.05 var(--f-mono);
  transition: color 0.15s, background 0.15s;
}
.seg:first-child { border-left: 0; }
.seg:hover:not([disabled]) { color: var(--ink); background: var(--signal-soft); }
.seg[aria-pressed="true"] { color: var(--ground); background: var(--signal); }
.seg[aria-pressed="true"] .seg__sub { color: rgba(11,34,57,0.7); }
.seg[disabled] { opacity: 0.4; cursor: not-allowed; }
.seg__sub {
  font: 500 0.56rem/1 var(--f-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mut);
}
.seg--file { position: relative; overflow: hidden; }
.seg--file input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.seg--file:focus-within { outline: 2px solid var(--signal); outline-offset: -2px; }
.scanname {
  order: -1; /* a quiet descriptive label, to the left of the selector */
  font: 400 0.68rem/1.2 var(--f-mono);
  color: var(--ink-mut);
  text-align: right;
  max-width: 30ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- impact funnel */
/* The headline strip under the header, shown only in bundle mode: five stages
   narrowing left -> right, from every dependency down to the proven-exploitable
   few. Neutral chips for the broad early stages; amber for "confirmed", a red
   glow for "exploitable" — so the eye lands on the narrow, dangerous end. */
.funnel {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  padding: 0.7rem 1.4rem;
  border-bottom: 1px solid var(--panel-edge);
  background: linear-gradient(180deg, rgba(14,42,69,0.55), rgba(11,34,57,0.55));
}
.funnel[hidden] { display: none; }
.funnel__stage {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.15rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  background: var(--ground-sink);
}
/* Narrowing ramp: each stage a little slimmer than the last. */
.funnel__stage--i0 { flex-grow: 1.9; }
.funnel__stage--i1 { flex-grow: 1.5; }
.funnel__stage--i2 { flex-grow: 1.2; }
.funnel__stage--i3 { flex-grow: 0.95; }
.funnel__stage--i4 { flex-grow: 0.78; }
.funnel__num {
  font: 500 1.7rem/1 var(--f-display);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.funnel__label {
  font: 500 0.6rem/1.25 var(--f-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.funnel__sub {
  font: 400 0.56rem/1.2 var(--f-mono);
  color: var(--ink-mut);
  max-width: 18ch;
}
.funnel__arrow {
  flex: none;
  align-self: center;
  font: 400 1rem/1 var(--f-mono);
  color: var(--ink-mut);
}
/* Tone: the two confirmed/exploitable stages carry the risk signal. */
.funnel__stage--confirmed { border-color: var(--hair); }
.funnel__stage--confirmed .funnel__num { color: var(--signal-ink); }
.funnel__stage--exploit { border-color: var(--hair); }
.funnel__stage--exploit .funnel__num { color: var(--alert-ink); }
/* A zero count still renders — neutral, never a false alarm (redash: 0 exploitable). */
.funnel__stage.is-zero .funnel__num { color: var(--ink-mut); }
.funnel__stage--confirmed.is-zero { border-color: var(--hair); }
.funnel__stage--exploit.is-zero {
  border-color: var(--hair);
  background: var(--ground-sink);
  box-shadow: none;
}

/* ---------------------------------------------------------------- layout */
.layout {
  flex: 1;
  display: grid;
  /* The detail column is zero-width until a node is selected, so the graph fills
     the space; it opens to --detail-w when a datasheet is shown. */
  grid-template-columns: var(--rail-w) minmax(0, 1fr) 0;
  min-height: 0;
  transition: grid-template-columns 0.18s ease;
}
.layout.has-detail {
  grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--detail-w);
}

.rail {
  border-right: 1px solid var(--panel-edge);
  background: var(--panel);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.panel { padding: 0.85rem 1.05rem; border-bottom: 1px solid var(--hair); }
.panel--filters { margin-top: auto; }
/* Compact, uppercase section headers — one quiet label style shared by the rail
   and the datasheet, so the whole UI reads as a set of small, scannable cards. */
.panel__h {
  margin: 0 0 0.1rem;
  font: 600 0.66rem/1.2 var(--f-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.panel__note, .panel__empty {
  margin: 0.2rem 0 0.7rem;
  font-size: 0.74rem;
  line-height: 1.4;
  color: var(--ink-mut);
}
.panel__empty { margin-bottom: 0; }

/* ---------------------------------------------------------------- entry points */
.eplist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.ep {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  padding: 0.55rem 0.65rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--muted);
  border-radius: var(--radius);
  background: var(--ground-sink);
  color: var(--ink);
  cursor: pointer;
  font-family: var(--f-mono);
  transition: border-color 0.15s, background 0.15s, transform 0.08s;
}
.ep:hover { border-color: var(--panel-edge); }
.ep[aria-pressed="true"] {
  border-color: var(--signal);
  border-left-color: var(--signal);
  background: var(--signal-soft);
}
.ep__method {
  font: 600 0.64rem/1 var(--f-mono);
  letter-spacing: 0.06em;
  padding: 0.25rem 0.4rem;
  border-radius: 2px;
  color: var(--ground);
  background: var(--dep);
}
.ep[aria-pressed="true"] .ep__method { background: var(--signal); }
.ep__path { font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------- reachable panel */
.reach-head {
  font-size: 0.78rem;
  color: var(--ink-dim);
  margin: 0 0 0.75rem;
  line-height: 1.45;
}
.reach-head b { color: var(--signal); font-weight: 600; }
.deplist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }
.dep {
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--signal);
  border-radius: var(--radius);
  background: var(--ground-sink);
}
.dep__name { font: 600 0.82rem/1.2 var(--f-mono); color: var(--ink); word-break: break-all; }
.dep__meta { margin-top: 0.2rem; font: 400 0.68rem/1.2 var(--f-mono); color: var(--ink-dim); display: flex; gap: 0.5rem; flex-wrap: wrap; }
.dep__eco {
  padding: 0.1rem 0.35rem;
  border: 1px solid var(--hair);
  border-radius: 2px;
  color: var(--ink-mut);
}
.dep__ver { color: var(--signal); }
.reach-none { font-size: 0.78rem; color: var(--ink-dim); line-height: 1.45; margin: 0; }

/* ---------------------------------------------------------------- vulnerability findings */
.vuln-head {
  margin: 0 0 0.75rem;
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--ink-dim);
}
.vuln-head b { color: var(--alert-ink); font-weight: 600; }
.vuln-head .is-signal { color: var(--signal); }

.vulnlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }

/* A finding card. The left rule encodes the verdict: a muted hairline for a
   non-reached finding, the amber signal for a reachable one. */
.vuln {
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--muted);
  border-radius: var(--radius);
  background: var(--ground-sink);
}
.vuln--reachable {
  border-left-color: var(--signal);
  background: linear-gradient(90deg, var(--alert-soft), var(--ground-sink) 72%);
}
/* A reachable finding is actionable: clicking it lights its path. */
.vuln--reachable { cursor: pointer; }
.vuln--reachable:hover { border-color: var(--alert); }
.vuln--reachable:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

.vuln__top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.vuln__name { font: 600 0.82rem/1.25 var(--f-mono); color: var(--ink); word-break: break-all; }
.vuln__ver { color: var(--signal); font-weight: 500; }
.vuln__cve { margin-top: 0.25rem; font: 500 0.7rem/1.3 var(--f-mono); color: var(--alert-ink); word-break: break-all; }

.vuln__tags { margin-top: 0.3rem; display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.tag {
  font: 500 0.6rem/1 var(--f-mono);
  letter-spacing: 0.04em;
  padding: 0.18rem 0;
  border-radius: 2px;
  border: none;
  color: var(--ink-dim);
  text-transform: uppercase;
}
.tag--sev { color: var(--alert-ink); }
.tag--sev-critical { color: var(--alert-ink); font-weight: 700; }
.tag--kev { color: var(--alert-ink); font-weight: 700; }
.tag--reach { color: var(--signal-ink); }

/* Advisory exploitability-triage badges — a verdict distinct from the severity
   and reachability tags: "exploitable" (the code calls the vulnerable surface),
   "likely safe" (it demonstrably does not), or "unsure". */
.tri {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.05em;
  padding: 0.18rem 0;
  border-radius: 2px;
  border: none;
  color: var(--ink-dim);
  text-transform: uppercase;
  white-space: nowrap;
}
.tri--affected { color: var(--alert-ink); font-weight: 700; }
.tri--safe { color: var(--safe-ink); }
.tri--uncertain { color: var(--ink-mut); }

/* Per-CVE triage rationale under a finding row in the left rail. */
.cve__triage {
  margin-top: 0.3rem;
  font: 400 0.7rem/1.45 var(--f-body);
  color: var(--ink-dim);
  border-left: 2px solid var(--panel-edge);
  padding-left: 0.5rem;
}
.cve__triage-h {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mut);
  margin-right: 0.3rem;
}

/* Exploitability section in the dependency detail panel. */
.tri-lead { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin-bottom: 0.35rem; }
.tri-conf {
  font: 500 0.62rem/1 var(--f-mono);
  letter-spacing: 0.04em;
  color: var(--ink-mut);
  text-transform: uppercase;
}
.tri-detail { margin-top: 0.4rem; }

/* Advisory dynamic-usage badges (dependency detail panel): a quiet, cyan-family
   family distinct from the red/amber of severity/triage — this tier describes
   usage, it is not a severity signal. */
.use {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.05em;
  padding: 0.18rem 0;
  border-radius: 2px;
  border: none;
  color: var(--ink-dim);
  text-transform: uppercase;
  white-space: nowrap;
}
.use--infra      { color: var(--dep); }
.use--dynamic    { color: var(--signal-ink); }
.use--dev        { color: var(--ink-mut); }
.use--transitive { color: var(--fn); }
.use--none       { color: var(--ink-mut); }
.use--uncertain  { color: var(--ink-mut); }

/* Symbol-level reachability badges — DETERMINISTIC (not advisory). "confirmed"
   means a resolved call reaches the vulnerable function (amber signal); a
   package-only reach is dim; no path is the green all-clear. Parallels .use/.tri. */
.sym {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.05em;
  padding: 0.18rem 0;
  border-radius: 2px;
  border: none;
  color: var(--ink-dim);
  text-transform: uppercase;
  white-space: nowrap;
}
.sym--confirmed { color: var(--signal-ink); }
.sym--package   { color: var(--ink-mut); }
.sym--none      { color: var(--safe-ink); }

/* Taint badges — DETERMINISTIC. "tainted" is the strongest signal on the page:
   attacker input provably reaches the call, so it wears the filled alert glow
   (parallels .tri--affected). "not_proven" is a quiet, dim note. */
.taint {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.05em;
  padding: 0.18rem 0;
  border-radius: 2px;
  border: none;
  color: var(--ink-dim);
  text-transform: uppercase;
  white-space: nowrap;
}
.taint--tainted { color: var(--alert-ink); font-weight: 700; }
.taint--none { color: var(--ink-mut); }

/* Deterministic escalation ladder (dependency datasheet): reachable -> vulnerable
   function reached -> exploitable. Each rung is a status dot + the stage name +
   a detail badge; a reached rung lights in its tone, the rest stay dim. */
.escal { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.escal__step {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.35rem 0 0.35rem 0.1rem;
}
/* A connecting spine between the dots, so the three rungs read as one ladder. */
.escal__step::before {
  content: "";
  position: absolute;
  left: 4px; top: 1.1rem; bottom: -0.35rem;
  width: 1px;
  background: var(--hair);
}
.escal__step:last-child::before { display: none; }
.escal__dot {
  flex: none;
  width: 9px; height: 9px;
  margin-top: 0.3rem;
  border-radius: 50%;
  border: 1.5px solid var(--muted);
  background: var(--ground-sink);
  z-index: 1;
}
.escal__text { display: flex; flex-direction: column; gap: 0.25rem; }
.escal__label {
  font: 500 0.78rem/1.2 var(--f-mono);
  color: var(--ink-mut);
}
.escal__detail { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.escal__none { font: 500 0.66rem/1 var(--f-mono); color: var(--ink-mut); text-transform: uppercase; letter-spacing: 0.05em; }
/* A proven rung lights up, strongest at the dangerous end. */
.escal__step.is-on .escal__label { color: var(--ink); }
.escal__step--reach .escal__dot { border-color: var(--signal); background: var(--signal); box-shadow: 0 0 6px var(--signal-glow); }
.escal__step--sym .escal__dot   { border-color: var(--signal); background: var(--signal); box-shadow: 0 0 7px var(--signal-glow); }
.escal__step--taint .escal__dot { border-color: var(--alert); background: var(--alert); box-shadow: 0 0 9px var(--alert-glow); }
.escal__step--taint.is-on .escal__label { color: var(--alert-ink); }

/* The taint evidence trail: attacker source -> node path -> vulnerable sink. */
.taint-ev {
  margin-top: 0.1rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--alert);
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--alert-soft), var(--ground-sink) 72%);
}
.taint-ev__h {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--alert-ink);
  margin-bottom: 0.35rem;
}
.taint-ev__flow { font: 400 0.68rem/1.5 var(--f-mono); color: var(--ink-dim); word-break: break-word; }
.taint-ev__src  { color: var(--alert-ink); font-weight: 600; }
.taint-ev__sink { color: var(--alert-ink); font-weight: 600; }
.taint-ev__arrow { color: var(--ink-mut); }
.taint-ev .path-steps { color: var(--ink-mut); }
.taint-ev .dim-note { margin-top: 0.3rem; }

/* Evidence list under the usage section: file:line + a trimmed snippet. */
.evlist { list-style: none; margin: 0.4rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.45rem; }
.ev__loc { font: 500 0.68rem/1.3 var(--f-mono); color: var(--ink-dim); word-break: break-all; }

.vuln__path {
  margin-top: 0.35rem;
  font: 400 0.66rem/1.4 var(--f-mono);
  color: var(--ink-dim);
}
.vuln__path b { color: var(--signal); font-weight: 600; }
.vuln__path .path-steps { color: var(--ink-mut); word-break: break-all; }
.vuln__verdict { margin-top: 0.35rem; font: 400 0.66rem/1.4 var(--f-mono); color: var(--ink-mut); }

.vuln-note { font-size: 0.78rem; color: var(--ink-dim); line-height: 1.45; margin: 0; }
.vuln-note--warn { color: var(--alert-ink); }

/* ---------------------------------------------------------------- filters */
.filters { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  background: var(--ground-sink);
  color: var(--ink-dim);
  cursor: pointer;
  font: 500 0.72rem/1 var(--f-mono);
  letter-spacing: 0.02em;
  transition: color 0.15s, border-color 0.15s, opacity 0.15s;
}
.chip[aria-pressed="true"] { color: var(--ink); border-color: var(--panel-edge); }
.chip[aria-pressed="false"] { opacity: 0.5; }
.chip i { width: 9px; height: 9px; border-radius: 50%; }

/* ---------------------------------------------------------------- stage */
.stage {
  position: relative;
  background: var(--ground-sink);
  overflow: hidden;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* The canvas sits below an optional focused-view banner; absolute overlays
   (graph, grid, legend, hint, tooltip) anchor to this box, not the whole stage. */
.stage__canvas {
  position: relative;
  flex: 1;
  min-height: 0;
}

/* Focused-view notice: shown only when a large repo is reduced to its
   security-relevant subgraph. A hairline strip, not a modal — it states the
   curation plainly and gets out of the way. */
.stage__banner {
  display: flex;
  align-items: baseline;
  gap: 0.6rem 0.9rem;
  flex-wrap: wrap;
  padding: 0.6rem 1.1rem;
  border-bottom: 1px solid var(--panel-edge);
  background: linear-gradient(90deg, var(--alert-soft), rgba(14,42,69,0.6) 60%);
  font: 400 0.74rem/1.4 var(--f-mono);
  color: var(--ink-dim);
}
.stage__banner[hidden] { display: none; }
.stage__banner .banner__lead {
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0.01em;
}
.stage__banner .banner__lead::before {
  content: "";
  display: inline-block;
  width: 7px; height: 7px;
  margin-right: 0.5rem;
  background: var(--alert);
  box-shadow: 0 0 8px var(--alert-glow);
  vertical-align: 0.08em;
}
.stage__banner b { color: var(--signal); font-weight: 600; }
.stage__banner .banner__full { color: var(--ink-mut); }
.stage__grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px),
    radial-gradient(ellipse at 50% 42%, rgba(111,184,224,0.07), transparent 62%);
  background-size: 28px 28px, 28px 28px, 100% 100%;
}
#graph { position: absolute; inset: 0; }

/* A quiet left-to-right flow caption in the top-left of the canvas, naming the
   three layers so the diagram reads as a pipeline without a heavy legend box. */
.stage__flow {
  position: absolute;
  left: 1rem; top: 0.85rem;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font: 500 0.64rem/1 var(--f-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mut);
  pointer-events: none;
}
.stage__flow span { color: var(--signal); opacity: 0.8; }

/* Legend: a slim footer strip under the canvas — off the graph, always legible,
   never overlapping. Wraps to a second row only on a narrow viewport. */
.legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1.1rem;
  padding: 0.5rem 1.1rem;
  border-top: 1px solid var(--hair);
  background: rgba(8, 26, 44, 0.6);
}
.legend__item { display: flex; align-items: center; gap: 0.45rem; font: 400 0.66rem/1 var(--f-mono); color: var(--ink-dim); white-space: nowrap; }
.dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.dot--ep  { background: transparent; border: 2px solid var(--signal); }
.dot--dep { background: var(--dep); }
.dot--fn  { background: transparent; border: 1.5px solid var(--fn); }
.dot--mut { background: var(--muted); }
.dot--sig { background: var(--signal); box-shadow: 0 0 7px var(--signal-glow); }
.dot--vuln { background: var(--dep); border: 2px solid var(--alert); }
.dot--unsure { background: repeating-linear-gradient(135deg, var(--alert) 0 1.5px, var(--dep) 1.5px 3px); border: 2px solid var(--alert); }
.dot--tainted { background: var(--alert); border: 2px solid var(--alert); }

/* Vulnerability legend rows appear only once an audit bundle is loaded. */
.dot--trans { background: transparent; border: 1.5px solid var(--dep); }
.legend__item--vuln { display: none; }
body.has-vulns .legend__item--vuln { display: flex; }
/* The transitive-dep legend note only applies when the scan has transitive deps. */
.legend__item--transitive { display: none; }
body.has-transitive .legend__item--transitive { display: flex; }

/* ---------------------------------------------------------------- tooltip */
.tooltip {
  position: absolute;
  z-index: 20;
  max-width: 24rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  background: rgba(8, 26, 44, 0.95);
  font: 400 0.72rem/1.35 var(--f-mono);
  color: var(--ink);
  pointer-events: none;
  transform: translate(-50%, -115%);
}
.tooltip b { color: var(--signal); font-weight: 600; }
.tooltip .t-vuln { color: var(--alert-ink); font-size: 0.66rem; }
.tooltip .t-id { color: var(--ink-mut); font-size: 0.66rem; word-break: break-all; }

/* ---------------------------------------------------------------- zoom controls */
/* A compact schematic selector in the corner. The wheel does the real zooming;
   these are discrete nudges and a one-tap "fit everything". */
.zoomctl {
  position: absolute;
  right: 1rem; bottom: 1rem;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  overflow: hidden;
  background: rgba(8, 26, 44, 0.82);
  backdrop-filter: blur(3px);
}
.zoomctl__btn {
  width: 2rem; height: 2rem;
  display: grid; place-items: center;
  border: 0;
  border-top: 1px solid var(--hair);
  background: transparent;
  color: var(--ink-dim);
  font: 500 1rem/1 var(--f-mono);
  cursor: pointer;
  transition: color 0.15s, background 0.15s;
}
.zoomctl__btn:first-child { border-top: 0; }
.zoomctl__btn:hover { color: var(--ink); background: var(--signal-soft); }
.zoomctl__btn:active { background: rgba(242,177,52,0.28); }
.zoomctl__btn--fit { font-size: 0.86rem; color: var(--ink-mut); }

/* ---------------------------------------------------------------- detail panel */
/* The spec sheet. A title-block callout for whichever node is selected: the
   engineering-drawing metaphor extended to a single part's datasheet. */
/* The datasheet drawer. Collapsed to zero and hidden until a node is selected,
   then it slides in from the right. */
.detail {
  border-left: 1px solid var(--panel-edge);
  background: var(--panel);
  overflow-x: hidden;
  overflow-y: auto;
  min-width: 0;
  min-height: 0;
  transform: translateX(14px);
  opacity: 0;
  transition: transform 0.18s ease, opacity 0.16s ease;
}
.layout.has-detail .detail { transform: none; opacity: 1; }
.detail__inner { display: flex; flex-direction: column; min-height: 100%; }

/* Empty state — an invitation, in the interface's voice. */
.detail__empty {
  margin: auto;
  padding: 2rem 1.4rem;
  text-align: center;
  color: var(--ink-dim);
}
.detail__empty .d-mark {
  display: block;
  width: 26px; height: 26px;
  margin: 0 auto 0.85rem;
  border: 1.5px solid var(--panel-edge);
  border-radius: 5px;
  position: relative;
}
.detail__empty .d-mark::after {
  content: "";
  position: absolute; inset: 7px;
  border: 1.5px solid var(--dep);
  border-radius: 2px;
}
.detail__empty h2 { margin: 0 0 0.4rem; font: 600 0.95rem/1.2 var(--f-display); color: var(--ink); }
.detail__empty p { margin: 0; font-size: 0.8rem; line-height: 1.5; }

/* Head: a kind-coded accent bar, the kind eyebrow, the node's name, close. */
.detail__head {
  position: sticky; top: 0; z-index: 2;
  padding: 1rem 1.2rem 0.85rem;
  border-bottom: 1px solid var(--hair);
  border-left: 3px solid var(--kind, var(--muted));
  background: linear-gradient(180deg, rgba(14,42,69,0.97), rgba(14,42,69,0.9));
  backdrop-filter: blur(3px);
}
.detail__kind {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font: 600 0.6rem/1 var(--f-mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--kind, var(--ink-mut));
}
.detail__kind::before {
  content: ""; width: 7px; height: 7px; border-radius: 2px;
  background: var(--kind, var(--muted));
}
.detail__title {
  margin: 0.5rem 0 0;
  font: 600 1.02rem/1.25 var(--f-mono);
  color: var(--ink);
  word-break: break-word;
  padding-right: 1.8rem;
}
.detail__close {
  position: absolute; top: 0.75rem; right: 0.8rem;
  width: 1.7rem; height: 1.7rem;
  display: grid; place-items: center;
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  background: var(--ground-sink);
  color: var(--ink-dim);
  font: 400 1rem/1 var(--f-mono);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.detail__close:hover { color: var(--ink); border-color: var(--panel-edge); }

.detail__body { padding: 0.9rem 1.1rem 1.3rem; display: flex; flex-direction: column; gap: 0.9rem; }

/* A labelled section inside the datasheet. */
.dsec { display: flex; flex-direction: column; gap: 0.45rem; }
.dsec__h {
  margin: 0;
  font: 600 0.62rem/1 var(--f-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mut);
}
.dsec__h .dsec__count { color: var(--ink-mut); font-weight: 500; }
/* A reused .use badge sitting in a section header (e.g. "Package  transitive"). */
.dsec__h .use { margin-left: 0.5rem; vertical-align: middle; }

/* Key/value rows (signature, source, ecosystem …). */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.8rem; align-items: baseline; }
.kv dt { font: 500 0.66rem/1.3 var(--f-mono); letter-spacing: 0.04em; color: var(--ink-mut); text-transform: uppercase; }
.kv dd { margin: 0; font: 400 0.76rem/1.4 var(--f-mono); color: var(--ink); word-break: break-word; }
.kv dd.is-signal { color: var(--signal); }

.mono { font: 400 0.76rem/1.5 var(--f-mono); color: var(--ink); word-break: break-word; }
.prose { font: 400 0.82rem/1.55 var(--f-body); color: var(--ink-dim); margin: 0; }
.prose--quote {
  border-left: 2px solid var(--hair);
  padding-left: 0.7rem;
  color: var(--ink);
}
.dim-note { font: 400 0.76rem/1.5 var(--f-body); color: var(--ink-mut); margin: 0; }

/* A code block for a signature. */
.codeblock {
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--fn);
  border-radius: var(--radius);
  background: var(--ground-sink);
  font: 500 0.76rem/1.5 var(--f-mono);
  color: var(--ink);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Badges — exposure / authentication / provenance. Risk tone when the value
   widens the attack surface (internet-facing, no auth); quiet otherwise. */
.badges { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.badge {
  display: inline-flex; align-items: baseline; gap: 0.4rem;
  padding: 0.32rem 0.5rem;
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  background: var(--ground-sink);
  font: 500 0.72rem/1 var(--f-mono);
  color: var(--ink);
}
.badge__k { font-size: 0.56rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mut); }
.badge--risk { color: var(--alert-ink); }
.badge--risk .badge__k { color: var(--alert-ink); }
.badge--ok { border-color: rgba(120,178,224,0.4); }


/* Context items grouped by category. */
.ctxgroup { display: flex; flex-direction: column; gap: 0.4rem; }
.ctxgroup__h {
  font: 600 0.58rem/1 var(--f-mono);
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--dep);
}
.ctxlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.ctxitem {
  padding: 0.45rem 0.55rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--dep);
  border-radius: var(--radius);
  background: var(--ground-sink);
}
.ctxitem__detail { font: 500 0.76rem/1.3 var(--f-mono); color: var(--ink); }
.ctxitem__access { margin-top: 0.15rem; font: 400 0.72rem/1.4 var(--f-body); color: var(--ink-dim); }

/* A cited snippet of untrusted doc text — always rendered as text. */
.snip { margin-top: 0.35rem; }
.snip__src {
  display: inline-block; margin-bottom: 0.2rem;
  font: 500 0.6rem/1 var(--f-mono);
  letter-spacing: 0.04em;
  color: var(--ink-mut);
}
.snip__src::before { content: "“ "; }
.snip__text {
  font: 400 0.68rem/1.45 var(--f-mono);
  color: var(--ink-dim);
  background: rgba(8,26,44,0.5);
  border-radius: 2px;
  padding: 0.3rem 0.45rem;
  white-space: pre-wrap;
  word-break: break-word;
}
/* Clamp long snippets/docstrings until expanded. */
.clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
.more {
  margin-top: 0.3rem;
  border: 0; background: transparent;
  padding: 0;
  font: 500 0.66rem/1 var(--f-mono);
  letter-spacing: 0.04em;
  color: var(--signal);
  cursor: pointer;
}
.more:hover { text-decoration: underline; }

/* Doc references. */
.docref { padding-left: 0.1rem; }
.docref__src {
  font: 500 0.64rem/1 var(--f-mono);
  color: var(--signal);
}

/* Trace affordance + CVE list reuse the rail card look. */
.btn-trace {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--signal);
  border-radius: var(--radius);
  background: var(--signal-soft);
  color: var(--signal);
  font: 600 0.72rem/1 var(--f-mono);
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: background 0.15s;
  align-self: flex-start;
}
.btn-trace::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--signal); box-shadow: 0 0 7px var(--signal-glow);
}
.btn-trace:hover { background: rgba(242,177,52,0.26); }
.btn-trace[disabled] { opacity: 0.45; cursor: default; border-color: var(--hair); color: var(--ink-mut); }
.btn-trace[disabled]::before { background: var(--muted); box-shadow: none; }

/* Reachability verdict line. */
.verdict {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font: 500 0.76rem/1.3 var(--f-mono);
}
.verdict::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--muted); }
.verdict--reach { color: var(--signal-ink); }
.verdict--reach::before { background: var(--signal); box-shadow: 0 0 7px var(--signal-glow); }
.verdict--maybe { color: var(--ink-dim); }
.verdict--maybe::before { background: var(--dep); }
/* Confident "unreachable" — the sound, fully-resolved verdict: good news, so green. */
.verdict--safe { color: var(--safe-ink); }
.verdict--safe::before { background: var(--safe); box-shadow: 0 0 6px rgba(79, 178, 134, 0.5); }

/* Mini CVE cards inside the dependency datasheet. */
.cvelist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.cve {
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--alert);
  border-radius: var(--radius);
  background: var(--ground-sink);
}
.cve__id { font: 600 0.74rem/1.3 var(--f-mono); color: var(--alert-ink); word-break: break-all; }
.cve__sum { margin-top: 0.2rem; font: 400 0.72rem/1.4 var(--f-body); color: var(--ink-dim); }
.cve__fix { margin-top: 0.25rem; font: 400 0.68rem/1.3 var(--f-mono); color: var(--ink-mut); }
.cve__fix b { color: var(--dep); font-weight: 600; }

/* ---------------------------------------------------------------- disclosures */
/* One consistent expand/collapse affordance everywhere: a real <details> with a
   rotating chevron, so depth is always exactly one keyboard-reachable click
   away and the default view stays compact. */
summary { cursor: pointer; list-style: none; }
summary::-webkit-details-marker { display: none; }
summary:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; border-radius: 2px; }

/* The chevron: a small caret that points right when closed, down when open. */
.disc__chev {
  flex: none;
  width: 0; height: 0;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  border-left: 5px solid var(--ink-mut);
  transition: transform 0.15s ease;
}
details[open] > summary .disc__chev { transform: rotate(90deg); }

/* Generic disclosure used inside the datasheet ("Details & sources", etc.). */
.disc { border-top: 1px solid var(--hair); padding-top: 0.6rem; }
.disc__sum {
  display: flex; align-items: center; gap: 0.5rem;
  font: 600 0.66rem/1 var(--f-mono);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ink-dim);
}
.disc__sum:hover { color: var(--ink); }
.disc__sum .disc__chev { border-left-color: var(--signal); }
.disc__body { display: flex; flex-direction: column; gap: 0.7rem; margin-top: 0.7rem; }

/* Minimal dependency drawer: Status badges on one line, a condensed attacker-flow
   line, and collapsed per-CVE disclosures (CVE id + severity badge in the row). */
.statusline { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }
.status-flow { margin: 0.5rem 0 0; font: 400 0.74rem/1.5 var(--f-body); color: var(--ink-dim); }
.status-flow code { font: 500 0.68rem/1 var(--f-mono); color: var(--alert-ink); }
.cve-disc { border-top: 1px solid var(--hair); padding-top: 0.55rem; }
.cve-disc .disc__sum { justify-content: flex-start; gap: 0.5rem; text-transform: none; letter-spacing: 0; }
.cve-disc__id {
  flex: 1 1 auto;
  font: 600 0.72rem/1.2 var(--f-mono);
  letter-spacing: 0; text-transform: none;
  color: var(--ink); word-break: break-all;
}
.kv--cve { margin-top: 0.1rem; }

/* ---------------------------------------------------------------- dependency groups (findings) */
.deplist--vuln { gap: 0.4rem; }
.depgroup {
  border: 1px solid var(--hair);
  border-left: 2px solid var(--muted);
  border-radius: var(--radius);
  background: var(--ground-sink);
  overflow: hidden;
}
.depgroup--reachable {
  border-left-color: var(--signal);
  background: linear-gradient(90deg, var(--alert-soft), var(--ground-sink) 72%);
}
.depgroup__sum {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 0.35rem 0.55rem;
  padding: 0.5rem 0.6rem;
}
.depgroup__sum:hover { background: rgba(120, 178, 224, 0.05); }
.depgroup__name {
  grid-column: 2;
  font: 600 0.82rem/1.2 var(--f-mono);
  color: var(--ink);
  word-break: break-all;
}
.depgroup__ver { color: var(--signal); font-weight: 500; }
.depgroup__tags {
  grid-column: 2;
  display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center;
}
.depgroup__count {
  font: 500 0.6rem/1 var(--f-mono);
  letter-spacing: 0.04em;
  padding: 0.22rem 0.4rem;
  border-radius: 2px;
  border: 1px solid var(--hair);
  color: var(--ink-dim);
}
.depgroup__body {
  padding: 0.15rem 0.6rem 0.6rem;
  border-top: 1px solid var(--hair);
}
.depgroup__body .cvelist { margin-top: 0.55rem; }
/* A reachable CVE inside an expanded group wears the amber rule. */
.cve--reachable { border-left-color: var(--signal); }

/* ---------------------------------------------------------------- entry-point groups */
.eplist--grouped { gap: 0.35rem; }
.eplist--inner { margin-top: 0.4rem; padding-left: 0.35rem; gap: 0.35rem; }
.epgroup {
  border: 1px solid var(--hair);
  border-left: 2px solid var(--muted);
  border-radius: var(--radius);
  background: var(--ground-sink);
}
.epgroup__sum {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.6rem;
}
.epgroup__sum:hover { background: rgba(120, 178, 224, 0.05); }
.epgroup__name {
  flex: 1;
  font: 500 0.82rem/1.2 var(--f-mono);
  color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.epgroup__count {
  font: 500 0.64rem/1 var(--f-mono);
  padding: 0.18rem 0.4rem;
  border-radius: 2px;
  border: 1px solid var(--hair);
  color: var(--ink-dim);
}
.epgroup[open] > .epgroup__sum { border-bottom: 1px solid var(--hair); }
.epgroup[open] > .epgroup__sum .epgroup__name { color: var(--signal); }
.epgroup > .eplist--inner { padding: 0.4rem 0.5rem 0.5rem; margin-top: 0; }

/* ---------------------------------------------------------------- assessment summary (primary) */
/* The one block shown by default for an endpoint: the integrated agent summary
   and its sources, clearly marked advisory. Everything else sits in a disclosure. */
.advisory-flag {
  margin-left: 0.5rem;
  font: 600 0.54rem/1 var(--f-mono);
  letter-spacing: 0.1em;
  padding: 0.14rem 0.36rem;
  border: 1px solid var(--signal);
  border-radius: 2px;
  color: var(--signal);
  text-transform: uppercase;
}
.summary-card {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 0.65rem 0.75rem;
  border: 1px dashed var(--signal);
  border-radius: var(--radius);
  background: var(--signal-soft);
}
.summary-lead {
  margin: 0;
  font: 400 0.84rem/1.55 var(--f-body);
  color: var(--ink);
}
.sources-line {
  margin: 0;
  font: 400 0.7rem/1.4 var(--f-mono);
  color: var(--ink-dim);
  word-break: break-word;
}
.sources-line__k {
  margin-right: 0.45rem;
  font-size: 0.56rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-mut);
}
.advisory-foot {
  margin: 0;
  padding-top: 0.1rem;
  font: 400 0.66rem/1.4 var(--f-body);
  color: var(--ink-dim);
}

/* Function source line (qualified name · path:line · method). */
.src-line {
  margin: 0;
  font: 400 0.7rem/1.4 var(--f-mono);
  color: var(--ink-dim);
  word-break: break-word;
}
.src-line__loc { color: var(--ink); }

/* ---------------------------------------------------------------- responsive */
@media (max-width: 860px) {
  .titleblock { flex-wrap: wrap; gap: 0.6rem 1rem; }
  .titleblock__load { flex-wrap: wrap; }
  .viewswitch { flex-wrap: wrap; }

  /* Funnel wraps to two rows rather than squashing; arrows drop out when wrapped. */
  .funnel { flex-wrap: wrap; gap: 0.4rem; padding: 0.6rem 1rem; }
  .funnel__stage { flex: 1 1 8rem; }
  .funnel__arrow { display: none; }
  .funnel__num { font-size: 1.4rem; }

  /* Stack the three panes; the detail drawer becomes a full-width row. */
  .layout, .layout.has-detail { grid-template-columns: 1fr; }
  .rail {
    border-right: none;
    border-bottom: 1px solid var(--panel-edge);
    max-height: none;
    overflow: visible;
  }
  .panel--filters { margin-top: 0; }
  .stage { min-height: 70vh; }
  .detail {
    border-left: none;
    border-top: 1px solid var(--panel-edge);
    overflow: visible;
    transform: none;
    opacity: 1;
  }
  .layout:not(.has-detail) .detail { display: none; }
  .detail__head { position: static; }
}

@media (max-width: 520px) {
  .funnel__stage { flex: 1 1 6rem; }
  .funnel__num { font-size: 1.2rem; }
}

/* ---------------------------------------------------------------- AI attack analysis */
/* Live, model-generated narrative in the dependency drawer. A Meta-blue to violet
   accent marks the AI badge; everything else follows the drawer palette. */
.ai-sec { --meta: #4C9DFF; --meta-2: #8C7BFF; }
.ai-body { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
.ai-head, .ai-loading { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; margin: 0; }
.ai-badge {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.28rem 0.55rem 0.28rem 0.45rem;
  font: 600 0.6rem/1.2 var(--f-mono);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: #D6E6FF;
  background: linear-gradient(90deg, rgba(76, 157, 255, 0.18), rgba(140, 123, 255, 0.18));
  border: 1px solid rgba(110, 150, 255, 0.45);
  border-radius: 999px;
}
.ai-badge__mark {
  width: 0.62rem; height: 0.62rem; flex: none;
  background: linear-gradient(135deg, var(--meta), var(--meta-2));
  border-radius: 50%;
  box-shadow: 0 0 6px rgba(120, 140, 255, 0.6);
}
.ai-loading__txt { font: 400 0.76rem/1.4 var(--f-body); color: var(--ink-dim); }
.ai-loading .ai-badge__mark { animation: ai-pulse 1.2s ease-in-out infinite; }
@keyframes ai-pulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
.ai-prov { font: 500 0.58rem/1 var(--f-mono); letter-spacing: 0.05em; text-transform: uppercase; }
.ai-prov--live { color: var(--safe-ink); }
.ai-prov--offline { color: var(--ink-mut); }
/* Deterministic VScan verdict, shown above the model narrative so the
   authoritative result always sits beside the prose it may contradict. */
.ai-verdict { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; margin: 0; }
.ai-verdict__k { font: 500 0.58rem/1 var(--f-mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mut); }
.ai-verdict__v {
  padding: 0.28rem 0.55rem;
  font: 700 0.62rem/1.25 var(--f-mono);
  letter-spacing: 0.07em; text-transform: uppercase;
  border: 1px solid var(--vc, var(--hair));
  border-radius: var(--radius);
  color: var(--vc-ink, var(--ink));
  background: var(--vc-soft, transparent);
  overflow-wrap: anywhere;
}
.ai-verdict--exploit { --vc: var(--alert);  --vc-ink: var(--alert-ink);  --vc-soft: var(--alert-soft); }
.ai-verdict--reach   { --vc: var(--signal); --vc-ink: var(--signal-ink); --vc-soft: var(--signal-soft); }
.ai-verdict--safe    { --vc: var(--dep);    --vc-ink: var(--dep);        --vc-soft: rgba(111, 184, 224, 0.16); }
.ai-narr { color: var(--ink); overflow-wrap: anywhere; }
.ai-fix {
  display: flex; flex-direction: column; gap: 0.25rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--meta);
  border-radius: var(--radius);
}
.ai-fix__k { font: 600 0.6rem/1 var(--f-mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--meta); }
.ai-fix__v { overflow-wrap: anywhere; }

/* Rendered Markdown (md.js) inside the AI narrative / fix. Same rules live in
   chat.css under .bubble--md: the two pages' stylesheets are self-contained. */
.md { overflow-wrap: anywhere; min-width: 0; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }
.md-p { margin: 0 0 0.55rem; }
.md strong { color: var(--ink); font-weight: 600; }
.md em { font-style: italic; }
.md-h { margin: 0.7rem 0 0.3rem; font: 600 0.86rem/1.3 var(--f-display); color: var(--ink); letter-spacing: 0.005em; }
.md-h1 { font-size: 0.94rem; }
.md-h3 { font-size: 0.82rem; }
.md-ul, .md-ol { margin: 0 0 0.55rem; padding-left: 1.2rem; }
.md-ul li, .md-ol li { margin: 0.15rem 0; padding-left: 0.1rem; }
.md-ul li::marker { color: #6E96FF; }
.md-ol li::marker { color: #6E96FF; font-family: var(--f-mono); font-size: 0.9em; }
.md-code {
  padding: 0.08em 0.38em;
  font: 500 0.88em/1.4 var(--f-mono);
  color: #D6E6FF;
  background: rgba(76, 157, 255, 0.12);
  border: 1px solid rgba(110, 150, 255, 0.28);
  border-radius: 3px;
  overflow-wrap: anywhere;
}
.md-pre {
  margin: 0.2rem 0 0.65rem; padding: 0.6rem 0.75rem;
  overflow-x: auto; max-width: 100%;
  background: var(--ground-sink, #081A2C);
  border: 1px solid var(--hair);
  border-left: 2px solid #6E96FF;
  border-radius: var(--radius);
}
.md-pre code { font: 400 0.76rem/1.55 var(--f-mono); color: var(--ink); white-space: pre; background: none; border: 0; padding: 0; }
.md-link { color: var(--dep); text-decoration: underline; text-decoration-color: rgba(111, 184, 224, 0.4); text-underline-offset: 2px; overflow-wrap: anywhere; }
.md-link:hover { text-decoration-color: currentColor; }

/* ---------------------------------------------------------------- vs. a conventional scanner */
/* One quiet line under the funnel: the noise-reduction story, in numbers computed
   from the loaded bundle. A hairline meter carries the same % in the Meta accent. */
.noise {
  position: relative;
  margin: 0;
  padding: 0.5rem 1.4rem 0.6rem;
  font: 400 0.78rem/1.45 var(--f-body);
  color: var(--ink-dim);
  border-bottom: 1px solid var(--panel-edge);
  background: rgba(8, 26, 44, 0.45);
}
.noise[hidden] { display: none; }
.noise b { font-weight: 600; color: var(--ink); }
.noise .noise__pct {
  background: linear-gradient(90deg, #4C9DFF, #8C7BFF);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: #8C9DFF;
}
.noise::after {
  content: ""; position: absolute; left: 1.4rem; right: 1.4rem; bottom: 0;
  height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, #4C9DFF 0, #8C7BFF var(--noise-removed, 0%), rgba(120, 178, 224, 0.14) var(--noise-removed, 0%));
  opacity: 0.8;
}

/* ---------------------------------------------------------------- Meta mark (header) */
.meta-mark {
  display: inline-flex; align-items: center; gap: 0.4rem;
  align-self: center;
  padding: 0.22rem 0.55rem 0.22rem 0.45rem;
  font: 600 0.56rem/1.2 var(--f-mono);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: #D6E6FF; white-space: nowrap;
  background: linear-gradient(90deg, rgba(76, 157, 255, 0.16), rgba(140, 123, 255, 0.16));
  border: 1px solid rgba(110, 150, 255, 0.4);
  border-radius: 999px;
}
.meta-mark__dot {
  width: 0.55rem; height: 0.55rem; flex: none; border-radius: 50%;
  background: linear-gradient(135deg, #4C9DFF, #8C7BFF);
  box-shadow: 0 0 6px rgba(120, 140, 255, 0.6);
}
@media (max-width: 860px) {
  .titleblock__id { flex-wrap: wrap; row-gap: 0.4rem; }
  .noise { padding: 0.5rem 1rem 0.6rem; }
  .noise::after { left: 1rem; right: 1rem; }
}
.ai-note { font-style: italic; }
@media (max-width: 520px) {
  .ai-head, .ai-loading { gap: 0.35rem 0.5rem; }
}

/* ---------------------------------------------------------------- motion */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
