/* ============================================================================
   PANTEREX — NOCTURNAL INTELLIGENCE
   The market is dark. PANTEREX sees what is moving inside it.

   This is the design system for Phase 3 surfaces. It is deliberately SCOPED: it
   defines tokens on :root and styles only `.nx-*` classes, so it can sit beside
   px-brand.css without recolouring 79 legacy pages in one dangerous sweep.

   THE RULE THIS ENFORCES
     Colour means something. It is not decoration.
       cyan        = OBSERVED   (directly measured)
       ultraviolet = INFERRED   (PANTEREX concluded this)
       tungsten    = FOCUS      (PANTEREX's current attention)
       orange      = ANOMALY
       red         = DANGER
       emerald     = HEALTHY
       grey        = context / historical / unknown
     If a colour appears on a surface and does not mean one of those, it is a bug.

   AND WHAT IT REFUSES
     No decorative gradients. No glow on static objects — glow is reserved for
     live process. No card-shell around everything. Target ~85-90% neutral.
     Chain colours are contextual identity only and never recolour the product.
   ============================================================================ */

:root {
  /* ── neutrals: obsidian → bone ─────────────────────────────────────────── */
  --nx-obsidian:  #08090B;   /* page ground. Not #000 — pure black kills depth */
  --nx-carbon:    #0D0F12;   /* raised ground */
  --nx-graphite:  #14171B;   /* surface */
  --nx-graphite-2:#1A1E23;   /* raised surface */
  --nx-gunmetal:  #262B32;   /* borders, dividers */
  --nx-gunmetal-2:#333A43;   /* hover borders */
  --nx-ash:       #6B747F;   /* de-emphasised text */
  --nx-ash-2:     #8B949F;   /* secondary text */
  --nx-bone:      #D6DAE0;   /* primary text */
  --nx-bone-hi:   #F2F4F7;   /* emphasis text */

  /* ── signature: tungsten / predator-eye amber ─────────────────────────── */
  --nx-tungsten:      #E8A33D;
  --nx-tungsten-dim:  #9A6B23;
  --nx-tungsten-veil: rgba(232,163,61,.10);

  /* ── semantic: evidence + state ───────────────────────────────────────── */
  --px-observed:      #3FD4E8;   /* cold cyan  — directly measured */
  --px-observed-veil: rgba(63,212,232,.10);
  --px-inferred:      #9B7BF0;   /* ultraviolet — PANTEREX concluded this */
  --px-inferred-veil: rgba(155,123,240,.10);
  --px-historical:    #5F6B7A;   /* desaturated — true once, not now */
  --px-unknown:       #4A525C;   /* grey — genuinely not available */
  --px-shadow:        #6E5AA8;   /* research; barred from execution */
  --px-shadow-veil:   rgba(110,90,168,.10);

  --nx-anomaly:       #F0873A;   /* thermal orange — behaviour heating up */
  --nx-anomaly-veil:  rgba(240,135,58,.10);
  --nx-danger:        #E5484D;   /* infrared red — confirmed danger */
  --nx-danger-veil:   rgba(229,72,77,.10);
  --nx-healthy:       #3FA96A;   /* muted emerald — verified healthy */
  --nx-healthy-veil:  rgba(63,169,106,.10);

  /* ── chain identity: CONTEXT ONLY. Never a surface colour. ────────────── */
  --nx-chain-solana:    #9945FF;
  --nx-chain-base:      #0052FF;
  --nx-chain-robinhood: #00C805;

  /* ── type ─────────────────────────────────────────────────────────────── */
  --nx-sans: "DM Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --nx-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --nx-t-micro: 10.5px;   /* labels, provenance chips */
  --nx-t-small: 12px;     /* secondary */
  --nx-t-body:  13.5px;   /* operational body — the workhorse */
  --nx-t-lead:  15px;
  --nx-t-head:  19px;
  --nx-t-title: 25px;     /* deliberately restrained; hierarchy comes from weight + space */

  /* ── space + form ─────────────────────────────────────────────────────── */
  --nx-r-sm: 3px; --nx-r-md: 5px; --nx-r-lg: 8px;   /* small radii: precise, not soft */
  --nx-line: 1px solid var(--nx-gunmetal);
  --nx-line-hi: 1px solid var(--nx-gunmetal-2);
  --nx-focus: 0 0 0 2px var(--nx-obsidian), 0 0 0 4px var(--nx-tungsten);
}

/* ── ground ───────────────────────────────────────────────────────────── */
.nx { background: var(--nx-obsidian); color: var(--nx-bone); font-family: var(--nx-sans); font-size: var(--nx-t-body); line-height: 1.5; -webkit-font-smoothing: antialiased; }
.nx *, .nx *::before, .nx *::after { box-sizing: border-box; }
.nx-mono { font-family: var(--nx-mono); font-variant-numeric: tabular-nums; }
.nx-num  { font-family: var(--nx-mono); font-variant-numeric: tabular-nums; }

/* ── typography: hierarchy BEFORE boxes ───────────────────────────────── */
.nx-title { font-size: var(--nx-t-title); font-weight: 600; letter-spacing: -.02em; color: var(--nx-bone-hi); margin: 0; }
.nx-head  { font-size: var(--nx-t-head); font-weight: 600; letter-spacing: -.01em; color: var(--nx-bone-hi); margin: 0; }
.nx-lead  { font-size: var(--nx-t-lead); color: var(--nx-bone); margin: 0; }
.nx-label { font-family: var(--nx-mono); font-size: var(--nx-t-micro); letter-spacing: .12em; text-transform: uppercase; color: var(--nx-ash); }
.nx-dim   { color: var(--nx-ash-2); }
.nx-faint { color: var(--nx-ash); }

/* ── structure: hairlines, not cards ──────────────────────────────────── */
.nx-section { border-top: var(--nx-line); padding: 18px 0; }
.nx-section:first-child { border-top: none; }
.nx-panel { background: var(--nx-graphite); border: var(--nx-line); border-radius: var(--nx-r-lg); }
.nx-rule { height: 1px; background: var(--nx-gunmetal); border: 0; margin: 0; }
.nx-row { display: flex; gap: 12px; align-items: center; }
.nx-stack { display: flex; flex-direction: column; gap: 4px; }

/* ── provenance chips: colour is NEVER alone (glyph + label + colour) ──── */
.nx-prov { display: inline-flex; align-items: center; gap: 5px; font-family: var(--nx-mono);
  /* A cell, not a pill. 20px was the only capsule left in the flagship set, and it read as a tag
     from a different system sitting beside square readings. */
  font-size: var(--nx-t-micro); letter-spacing: .06em; padding: 2px 7px; border-radius: 2px;
  border: 1px solid currentColor; white-space: nowrap; }
.nx-prov::before { font-size: 9px; line-height: 1; }
.nx-prov-observed   { color: var(--px-observed);   background: var(--px-observed-veil); }
.nx-prov-observed::before   { content: "●"; }
.nx-prov-inferred   { color: var(--px-inferred);   background: var(--px-inferred-veil); }
.nx-prov-inferred::before   { content: "◆"; }
.nx-prov-historical { color: var(--px-historical); background: transparent; }
.nx-prov-historical::before { content: "◐"; }
.nx-prov-unknown    { color: var(--px-unknown);    background: transparent; border-style: dashed; }
.nx-prov-unknown::before    { content: "○"; }
.nx-prov-shadow     { color: var(--px-shadow);     background: var(--px-shadow-veil); border-style: dashed; }
.nx-prov-shadow::before     { content: "◇"; }

/* ── candidate / decision state ───────────────────────────────────────── */
.nx-state { display: inline-flex; align-items: center; gap: 6px; font-family: var(--nx-mono);
  font-size: var(--nx-t-micro); letter-spacing: .1em; text-transform: uppercase; font-weight: 500;
  padding: 3px 9px; border-radius: var(--nx-r-sm); border: 1px solid currentColor; }
.nx-state-investigating { color: var(--nx-tungsten); background: var(--nx-tungsten-veil); }
.nx-state-watching      { color: var(--px-observed); background: var(--px-observed-veil); }
.nx-state-qualified     { color: var(--nx-healthy);  background: var(--nx-healthy-veil); }
.nx-state-rejected      { color: var(--nx-ash);      background: transparent; }
.nx-state-entered       { color: var(--nx-tungsten); background: var(--nx-tungsten-veil); }
.nx-state-discovered    { color: var(--nx-ash-2);    background: transparent; }

/* ── focus: tungsten is PANTEREX's attention ──────────────────────────── */
.nx-focused { box-shadow: inset 2px 0 0 var(--nx-tungsten); background: var(--nx-tungsten-veil); }
.nx :focus-visible { outline: none; box-shadow: var(--nx-focus); border-radius: var(--nx-r-sm); }

/* ── controls ─────────────────────────────────────────────────────────── */
.nx-btn { font-family: var(--nx-sans); font-size: var(--nx-t-small); font-weight: 500;
  color: var(--nx-bone); background: var(--nx-graphite-2); border: var(--nx-line-hi);
  border-radius: var(--nx-r-md); padding: 6px 12px; cursor: pointer; transition: background .12s, border-color .12s; }
.nx-btn:hover { background: var(--nx-gunmetal); border-color: var(--nx-ash); }
.nx-btn-primary { color: var(--nx-obsidian); background: var(--nx-tungsten); border-color: var(--nx-tungsten); font-weight: 600; }
.nx-btn-primary:hover { background: #F2B457; }
.nx-btn-danger { color: var(--nx-danger); border-color: var(--nx-danger); background: var(--nx-danger-veil); }
.nx-btn-ghost { background: transparent; border-color: transparent; color: var(--nx-ash-2); }
.nx-btn-ghost:hover { color: var(--nx-bone); background: var(--nx-graphite-2); }
.nx-btn[disabled] { opacity: .45; cursor: not-allowed; }

.nx-input { background: var(--nx-carbon); border: var(--nx-line-hi); border-radius: var(--nx-r-md);
  color: var(--nx-bone); font-family: var(--nx-sans); font-size: var(--nx-t-body); padding: 7px 10px; width: 100%; }
.nx-input::placeholder { color: var(--nx-ash); }

/* ── entity references: clickable, identical everywhere ───────────────── */
.nx-ent { font-family: var(--nx-mono); font-size: var(--nx-t-small); color: var(--nx-bone);
  background: var(--nx-graphite-2); border: var(--nx-line); border-radius: var(--nx-r-sm);
  padding: 1px 6px; cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 5px;
  transition: border-color .12s, color .12s; }
.nx-ent:hover { border-color: var(--nx-tungsten); color: var(--nx-bone-hi); }
.nx-ent-glyph { color: var(--nx-ash); font-size: 10px; }

/* ── tables: dense, readable, no zebra noise ──────────────────────────── */
.nx-tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.nx-table { width: 100%; border-collapse: collapse; font-size: var(--nx-t-small); }
.nx-table th { text-align: left; font-family: var(--nx-mono); font-size: var(--nx-t-micro);
  letter-spacing: .1em; text-transform: uppercase; color: var(--nx-ash); font-weight: 500;
  padding: 8px 10px; border-bottom: var(--nx-line); white-space: nowrap; }
.nx-table td { padding: 9px 10px; border-bottom: 1px solid rgba(255,255,255,.035); vertical-align: top; }
.nx-table tbody tr:hover td { background: rgba(255,255,255,.02); }
.nx-table tr:last-child td { border-bottom: none; }

/* ── timeline: how autonomy becomes legible ───────────────────────────── */
.nx-timeline { position: relative; padding-left: 20px; }
.nx-timeline::before { content: ""; position: absolute; left: 5px; top: 4px; bottom: 4px; width: 1px; background: var(--nx-gunmetal); }
.nx-tl-item { position: relative; padding: 0 0 16px 0; }
.nx-tl-item::before { content: ""; position: absolute; left: -18px; top: 5px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--nx-carbon); border: 1px solid var(--nx-ash); }
.nx-tl-observed::before { border-color: var(--px-observed); }
.nx-tl-inferred::before { border-color: var(--px-inferred); border-style: dashed; }
.nx-tl-decision::before { background: var(--nx-tungsten); border-color: var(--nx-tungsten); width: 9px; height: 9px; left: -19px; }
.nx-tl-danger::before   { background: var(--nx-danger); border-color: var(--nx-danger); }
.nx-tl-time { font-family: var(--nx-mono); font-size: var(--nx-t-micro); color: var(--nx-ash); }
.nx-tl-title { font-size: var(--nx-t-body); color: var(--nx-bone-hi); margin: 1px 0 2px; }
.nx-tl-detail { font-size: var(--nx-t-small); color: var(--nx-ash-2); }

/* ── states ───────────────────────────────────────────────────────────── */
.nx-empty, .nx-error, .nx-loading { padding: 28px 18px; text-align: center; color: var(--nx-ash-2); font-size: var(--nx-t-small); }
.nx-empty-title { display: block; color: var(--nx-bone); font-size: var(--nx-t-body); margin-bottom: 4px; }
.nx-error { color: var(--nx-danger); }

/* Working state — REAL counts, never a fabricated percentage. */
.nx-working { display: flex; align-items: center; gap: 9px; font-size: var(--nx-t-small); color: var(--nx-ash-2); }
.nx-working-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--nx-tungsten); flex: none;
  animation: nx-pulse 1.4s ease-in-out infinite; }
.nx-progress { font-family: var(--nx-mono); font-size: var(--nx-t-micro); color: var(--nx-tungsten); }

/* Glow is reserved for LIVE PROCESS. A static object never glows. */
@keyframes nx-pulse { 0%,100% { opacity: 1; box-shadow: 0 0 0 0 var(--nx-tungsten-veil); }
                      50%     { opacity: .45; box-shadow: 0 0 0 4px transparent; } }
@keyframes nx-arrive { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
.nx-arrive { animation: nx-arrive .22s ease-out; }

@media (prefers-reduced-motion: reduce) {
  .nx-working-dot { animation: none; }
  .nx-arrive { animation: none; }
  .nx * { transition: none !important; }
}

/* ── inspector: drawer on desktop, sheet on mobile ────────────────────── */
.nx-scrim { position: fixed; inset: 0; background: rgba(4,5,7,.62); z-index: 998; opacity: 0;
  pointer-events: none; transition: opacity .16s; }
.nx-scrim.open { opacity: 1; pointer-events: auto; }
.nx-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 100vw);
  background: var(--nx-carbon); border-left: var(--nx-line); z-index: 999;
  transform: translateX(100%); transition: transform .18s cubic-bezier(.2,.7,.3,1);
  display: flex; flex-direction: column; }
.nx-drawer.open { transform: none; }
.nx-drawer-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: var(--nx-line); flex: none; }
.nx-drawer-body { overflow-y: auto; padding: 4px 16px 24px; flex: 1; }

@media (max-width: 720px) {
  .nx-drawer { top: auto; left: 0; width: 100%; height: min(86vh, 720px);
    border-left: none; border-top: var(--nx-line); border-radius: 0;
    transform: translateY(100%); }
  .nx-drawer.open { transform: none; }
}

/* ── graph ────────────────────────────────────────────────────────────── */
.nx-graph { position: relative; background: var(--nx-carbon); border: var(--nx-line);
  border-radius: var(--nx-r-lg); overflow: hidden; }
.nx-graph canvas { display: block; }
.nx-graph-legend { position: absolute; left: 10px; bottom: 10px; display: flex; flex-wrap: wrap; gap: 10px;
  font-family: var(--nx-mono); font-size: var(--nx-t-micro); color: var(--nx-ash);
  /* The plate was a hardcoded near-black, which put dark violet "inferred" text on a dark ground in
     Field — the legend for the evidence colours was itself unreadable. It now takes the theme's own
     ground so the legend is legible wherever the canvas is. */
  background: color-mix(in srgb, var(--nx-carbon) 82%, transparent);
  padding: 6px 9px; border-radius: var(--nx-r-md); border: var(--nx-line); }
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .nx-graph-legend { background: var(--nx-carbon); }
}
.nx-graph-status { position: absolute; right: 10px; top: 10px; }

/* ── chain badge: identity only ───────────────────────────────────────── */
.nx-chain { font-family: var(--nx-mono); font-size: var(--nx-t-micro); letter-spacing: .08em;
  text-transform: uppercase; color: var(--nx-ash); border: var(--nx-line); border-radius: var(--nx-r-sm); padding: 1px 5px; }
.nx-chain::before { content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  margin-right: 5px; vertical-align: middle; background: var(--nx-ash); }
.nx-chain-solana::before { background: var(--nx-chain-solana); }
.nx-chain-base::before { background: var(--nx-chain-base); }
.nx-chain-robinhood::before { background: var(--nx-chain-robinhood); }

/* ── app shell ────────────────────────────────────────────────────────── */
.nx-shell { display: grid; grid-template-columns: 188px 1fr; min-height: 100vh; min-height: 100dvh; }
.nx-nav { border-right: var(--nx-line); background: var(--nx-carbon); padding: 16px 0; position: sticky; top: 0; height: 100vh; overflow-y: auto;
  display: flex; flex-direction: column; }
.nx-nav-brand { display: flex; align-items: center; gap: 9px; padding: 0 16px 16px; }
/* px-eye.png is a fixed brand asset: pale line-art drawn for a dark ground, and on FIELD's white nav it
   very nearly disappears. The asset is not modified — it is given the ground it was designed for. */
:root[data-px-theme="field"] .nx-nav-brand img {
  background: var(--nx-bone-hi); padding: 3px; box-sizing: content-box;
  border-radius: 5px;
}
.nx-nav-item { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 16px; color: var(--nx-ash-2); text-decoration: none; font-size: var(--nx-t-body);
  border-left: 2px solid transparent; cursor: pointer; }
.nx-nav-item:hover { color: var(--nx-bone); background: rgba(255,255,255,.02); }
.nx-nav-item.active { color: var(--nx-bone-hi); border-left-color: var(--nx-tungsten); background: var(--nx-tungsten-veil); }
.nx-nav-count { font-family: var(--nx-mono); font-size: var(--nx-t-micro); color: var(--nx-ash); }
/* The theme switcher lives at the foot of the rail on desktop and at the end of the scrolling strip on
   mobile — an appearance control, never a destination. */
.nx-nav-tail { margin-top: auto; padding: 16px; }
.nx-main { padding: 22px 26px 60px; max-width: 1500px; }

@media (max-width: 860px) {
  .nx-shell { grid-template-columns: 1fr; }
  /* BOTH levels need to be flex: .nx-nav so its children sit side by side, and the items wrapper so
     the items themselves do. Setting only one of them leaves every destination on its own row. */
  .nx-nav { position: sticky; top: 0; height: auto; z-index: 50; display: flex; overflow-x: auto;
    overflow-y: hidden; padding: 0; border-right: none; border-bottom: var(--nx-line); }
  .nx-nav > div { display: flex; flex: none; }
  .nx-nav { flex-direction: row; align-items: center; }
  .nx-nav-tail { margin-top: 0; margin-left: auto; padding: 0 10px; }
  .nx-nav-brand { display: none; }
  .nx-nav-item { border-left: none; border-bottom: 2px solid transparent; white-space: nowrap; padding: 12px 14px; }
  .nx-nav-item.active { border-left: none; border-bottom-color: var(--nx-tungsten); }
  .nx-main { padding: 16px 14px 60px; }
}

/* Nothing may push the page sideways. Long addresses, wide tables and long reason strings all live
   inside the viewport; only a .nx-tablewrap scrolls, and it scrolls itself. */
.nx, .nx-shell, .nx-main { max-width: 100%; overflow-x: hidden; }
.nx-main > * { min-width: 0; }
.nx-title, .nx-head, .nx-lead, .nx-tl-title, .nx-tl-detail, .nx-empty, li { overflow-wrap: anywhere; }

/* ============================================================================
   THEMES (Phase 4 §13) — NOCTURNE · FIELD · APEX

   THREE THEMES, ONE MEANING
     A theme changes what a colour LOOKS LIKE. It must never change what a colour
     MEANS. Cyan is OBSERVED on every surface PANTEREX has; if a theme made cyan
     decorative, or reused the observed hue for something that was not measured,
     the whole evidence system would quietly stop being legible — and it would
     stop being legible in exactly the situation where someone is under pressure.
     So every theme redefines the same token set. None of them introduces a new
     accent, and none of them repurposes a semantic one.

   NOCTURNE (default) — night. Low-light, long sessions, the market asleep.
     Obsidian ground, restrained chrome, evidence colours at full saturation.

   FIELD — daylight. The same instrument used in a lit room, on a laptop, next to
     other work. NOT an inverted Nocturne: cyan #3FD4E8 on white is unreadable, so
     the evidence hues are re-tuned for contrast on a light ground while keeping
     their identity. Observed stays cold, inferred stays violet, danger stays red.

   APEX — maximum focus. For the Decision Room when something is actually
     happening. Darker than Nocturne, chrome pulled almost to nothing, text
     contrast raised, so the only bright things on screen are the evidence and the
     decision. It is the same theme discipline taken to its end, not a new palette.

   The theme is set on <html data-px-theme="…"> and persisted per operator. No
   theme adds a gradient, a glow on a static object, or a colour that means
   nothing — those rules are theme-independent.
   ============================================================================ */

/* ── FIELD ─────────────────────────────────────────────────────────────────
   Ground and text invert; semantics are re-tuned, not recoloured. Every hue here
   is the same colour identity at a luminance that survives a white background. */
:root[data-px-theme="field"] {
  --nx-obsidian:  #F5F6F8;
  --nx-carbon:    #FFFFFF;
  --nx-graphite:  #FFFFFF;
  --nx-graphite-2:#F0F2F5;
  --nx-gunmetal:  #DDE1E6;
  --nx-gunmetal-2:#C4CAD2;
  --nx-ash:       #6C757F;
  --nx-ash-2:     #4E565F;
  --nx-bone:      #23282E;
  --nx-bone-hi:   #0E1216;

  --nx-tungsten:      #A96A05;
  --nx-tungsten-dim:  #C79A4E;
  --nx-tungsten-veil: rgba(169,106,5,.09);

  /* Same meanings, darkened until they hold on white. */
  --px-observed:      #0B7A8C;
  --px-observed-veil: rgba(11,122,140,.10);
  --px-inferred:      #5B3DBB;
  --px-inferred-veil: rgba(91,61,187,.10);
  --px-historical:    #6A7684;
  --px-unknown:       #98A0AA;
  --px-shadow:        #4E3E86;
  --px-shadow-veil:   rgba(78,62,134,.10);

  --nx-anomaly:       #B4550D;
  --nx-anomaly-veil:  rgba(180,85,13,.10);
  --nx-danger:        #C02127;
  --nx-danger-veil:   rgba(192,33,39,.09);
  --nx-healthy:       #1C7A45;
  --nx-healthy-veil:  rgba(28,122,69,.10);

  --nx-chain-solana:    #7B2FD6;
  --nx-chain-base:      #0044D6;
  --nx-chain-robinhood: #00892F;
}

/* ── APEX ──────────────────────────────────────────────────────────────────
   Chrome recedes further; evidence and decision are the only things left with
   any brightness. Nothing new is introduced — the neutrals simply step back. */
:root[data-px-theme="apex"] {
  --nx-obsidian:  #000103;
  --nx-carbon:    #060709;
  --nx-graphite:  #0A0C0F;
  --nx-graphite-2:#101317;
  --nx-gunmetal:  #1B1F25;
  --nx-gunmetal-2:#2A3038;
  --nx-ash:       #79828E;
  --nx-ash-2:     #9AA3AF;
  --nx-bone:      #E4E8EE;
  --nx-bone-hi:   #FFFFFF;

  --nx-tungsten:      #FFB44A;
  --nx-tungsten-dim:  #A8752A;
  --nx-tungsten-veil: rgba(255,180,74,.12);

  --px-observed:      #55E6FA;
  --px-observed-veil: rgba(85,230,250,.12);
  --px-inferred:      #B394FF;
  --px-inferred-veil: rgba(179,148,255,.12);
  --px-historical:    #66727F;
  --px-unknown:       #454C55;
  --px-shadow:        #8570C4;
  --px-shadow-veil:   rgba(133,112,196,.12);

  --nx-anomaly:       #FF9647;
  --nx-anomaly-veil:  rgba(255,150,71,.12);
  --nx-danger:        #FF5A5F;
  --nx-danger-veil:   rgba(255,90,95,.12);
  --nx-healthy:       #4ABF7B;
  --nx-healthy-veil:  rgba(74,191,123,.12);
}

/* The graph canvas paints from computed tokens, so it follows the theme without
   a second palette. This rule exists to make that dependency explicit rather
   than accidental: if a theme forgets a token, the canvas is where it shows. */
.nx-graph { background: var(--nx-carbon); }

/* Theme switcher — a control, not an ornament. */
.nx-theme {
  display: inline-flex; gap: 1px; padding: 1px;
  border: var(--nx-line); border-radius: var(--nx-r-md);
  background: var(--nx-graphite);
}
.nx-theme button {
  appearance: none; border: 0; cursor: pointer;
  background: transparent; color: var(--nx-ash);
  font: 500 var(--nx-t-micro)/1 var(--nx-mono);
  letter-spacing: .06em; text-transform: uppercase;
  padding: 5px 8px; border-radius: calc(var(--nx-r-md) - 1px);
}
.nx-theme button:hover { color: var(--nx-bone); }
.nx-theme button[aria-pressed="true"] {
  background: var(--nx-graphite-2); color: var(--nx-bone-hi);
}
.nx-theme button:focus-visible { outline: 2px solid var(--nx-tungsten); outline-offset: 1px; }

/* ============================================================================
   DECISION ROOM (Phase 4 §9) — canvas left, decision right

   WHY TWO COLUMNS AND NOT A STACK
     The Phase 3 Decision Room was a vertical document: read the reasons, scroll
     past the graph, scroll to the evidence. That layout makes the network a
     picture you pass on the way to the text. It is not a picture — it is the
     argument. Putting the canvas beside the decision means the graph and the
     reasoning are read together, and selecting something in one can change what
     is emphasised in the other. That is the whole point of an instrument.

     The canvas takes ~60% because it must be large enough to show structure
     without panning; the decision panel takes ~40% because prose below ~380px
     starts wrapping badly at this type size. Both are fluid within those bounds.
   ============================================================================ */

.nx-dr {
  display: grid;
  grid-template-columns: minmax(0, 60fr) minmax(360px, 40fr);
  gap: 18px;
  align-items: start;
}
.nx-dr-canvas { min-width: 0; position: sticky; top: 14px; }
#nx-graph-host { height: min(66vh, 560px); }
.nx-dr-panel  { min-width: 0; }

/* The panel scrolls independently so the canvas stays put while reading. */
.nx-dr-panel-scroll {
  max-height: calc(100vh - 120px);
  overflow-y: auto;
  padding-right: 4px;
  scrollbar-width: thin;
}
.nx-dr-panel-scroll::-webkit-scrollbar { width: 6px; }
.nx-dr-panel-scroll::-webkit-scrollbar-thumb { background: var(--nx-gunmetal); border-radius: 3px; }

@media (max-width: 1020px) {
  .nx-dr { grid-template-columns: 1fr; }
  .nx-dr-canvas { position: static; }
  .nx-dr-panel-scroll { max-height: none; overflow: visible; }
}

/* ON A PHONE THE VERDICT COMES FIRST.
   Side by side, the canvas earns ~60% because it can be read at a glance next to the reasoning. Stacked
   on a 390px screen it earns nothing: a 66vh canvas simply pushes the decision below the fold, so the
   first thing an operator sees is a diagram and the last thing is the answer. The order inverts, and the
   canvas takes a height a thumb can scroll past. */
@media (max-width: 720px) {
  .nx-dr-panel  { order: 1; }
  .nx-dr-canvas { order: 2; margin-top: 18px; }
  #nx-graph-host { height: 46vh; min-height: 260px; }
}

/* ── the verdict block: the one thing that must be readable from across a room ── */
.nx-verdict {
  border: var(--nx-line); border-left: 2px solid var(--nx-gunmetal-2);
  border-radius: var(--nx-r-md);
  padding: 13px 15px; background: var(--nx-graphite);
}
.nx-verdict[data-state="rejected"]             { border-left-color: var(--nx-danger); }
.nx-verdict[data-state="watching"]             { border-left-color: var(--nx-tungsten); }
.nx-verdict[data-state="investigating"]        { border-left-color: var(--px-observed); }
.nx-verdict[data-state="qualified_for_review"] { border-left-color: var(--nx-healthy); }
.nx-verdict[data-state="qualified"]            { border-left-color: var(--nx-healthy); }
.nx-verdict-head { font: 500 var(--nx-t-lead)/1.35 var(--nx-sans); color: var(--nx-bone-hi); }
.nx-verdict ul { margin: 9px 0 0; padding-left: 17px; }
.nx-verdict li { font: 400 var(--nx-t-body)/1.7 var(--nx-sans); color: var(--nx-bone); }

/* ============================================================================
   PANTEREX LENS (Phase 4 §10)

   WHAT IT IS
     One thing on the canvas is asked about, and the whole instrument answers.
     Focusing a node dims everything not connected to it, and the decision panel
     beside it reduces to the evidence that mentions it. It is a question, not a
     filter chip: "what does PANTEREX know about THIS wallet, here, on this
     token?"

   WHAT IT IS DELIBERATELY NOT
     It never hides evidence — it de-emphasises it, and it always says how much
     it is holding back. Silently narrowing what an operator can see, during the
     one interaction designed to help them concentrate, would be the worst
     possible place to lose information.
   ============================================================================ */

.nx-lens-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 11px; margin-bottom: 10px;
  border: var(--nx-line); border-radius: var(--nx-r-md);
  background: var(--nx-tungsten-veil);
  border-color: var(--nx-tungsten-dim);
}
.nx-lens-bar .nx-lens-what {
  font: 500 var(--nx-t-body)/1.3 var(--nx-sans); color: var(--nx-bone-hi);
}
.nx-lens-bar .nx-lens-note {
  font: 400 var(--nx-t-small)/1.4 var(--nx-sans); color: var(--nx-ash-2);
}
.nx-lens-bar .nx-lens-note { margin-right: auto; }   /* pushes the actions right; putting margin-left:auto
   on the buttons themselves gave BOTH of them auto, which wrapped the second one onto its own line. */
.nx-lens-bar button {
  appearance: none; cursor: pointer;
  background: transparent; border: var(--nx-line-hi); border-radius: var(--nx-r-sm);
  color: var(--nx-bone); font: 500 var(--nx-t-micro)/1 var(--nx-mono);
  letter-spacing: .06em; text-transform: uppercase; padding: 5px 9px;
}
.nx-lens-bar button:hover { border-color: var(--nx-tungsten); color: var(--nx-bone-hi); }

/* Out-of-focus evidence recedes but stays present and selectable. */
.nx-lensed .nx-ev-item { transition: opacity .18s ease; }
.nx-lensed .nx-ev-item[data-lens="out"] { opacity: .28; }
.nx-lensed .nx-ev-item[data-lens="in"]  { opacity: 1; }

/* ============================================================================
   MICROVISUALISATION (Phase 4 §11)
   Small, literal, and always adjacent to the number they describe. A bar that
   cannot be read as a precise value must not pretend to be one — these carry a
   text value alongside, never instead.
   ============================================================================ */

.nx-meter { display: flex; align-items: center; gap: 8px; }
.nx-meter-track {
  flex: 1; min-width: 40px; height: 3px; border-radius: 2px;
  background: var(--nx-gunmetal); overflow: hidden;
}
.nx-meter-fill { height: 100%; border-radius: 2px; background: var(--nx-ash); }
.nx-meter-fill[data-tone="observed"] { background: var(--px-observed); }
.nx-meter-fill[data-tone="inferred"] { background: var(--px-inferred); }
.nx-meter-fill[data-tone="danger"]   { background: var(--nx-danger); }
.nx-meter-fill[data-tone="healthy"]  { background: var(--nx-healthy); }
.nx-meter-fill[data-tone="anomaly"]  { background: var(--nx-anomaly); }
.nx-meter-val { font: 500 var(--nx-t-small)/1 var(--nx-mono); color: var(--nx-bone); flex: none; }

/* A sparkline of real observations. Missing readings are gaps, never interpolated. */
.nx-spark { display: block; overflow: visible; }
.nx-spark path { fill: none; stroke-width: 1.25; }
.nx-spark .nx-spark-line { stroke: var(--px-observed); }
.nx-spark .nx-spark-base { stroke: var(--nx-gunmetal); stroke-dasharray: 2 3; }
.nx-spark circle { fill: var(--px-observed); }
.nx-spark circle[data-missing="1"] { fill: none; stroke: var(--px-unknown); stroke-width: 1; }
