/* modern.css — visual polish layer (loaded last; refines look without
   changing the graph layout or behaviour). */

:root {
  --ink: #1f2933;
  --muted: #647280;
  --accent: #2b6cb0;
  --accent-soft: #ebf3fb;
  --panel: #ffffff;
  --line: #e6e9ee;

  /* ---- 10x polish design tokens (additive; existing vars untouched) ---- */
  --ink-2: #374151;          /* secondary body text */
  --ink-3: #6b7682;          /* tertiary / captions */
  --line-soft: #eef1f5;      /* calmer hairline for inner dividers */
  --highlight: #f5a623;      /* warm secondary accent */
  --accent-ink: #245a94;     /* darker accent for hover/active */

  /* Surfaces & hover wash */
  --surface-1: #fafbfc;
  --surface-hover: #f0f6ff;

  /* Radii — settle the whole UI on an 8px family */
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-pill: 999px;

  /* Softer, layered shadows */
  --sh-1: 0 1px 2px rgba(16, 24, 40, 0.05);
  --sh-2: 0 4px 14px rgba(16, 24, 40, 0.08);
  --sh-3: 0 12px 32px rgba(16, 24, 40, 0.14);

  /* Type scale (Inter) */
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-base: 13px;
  --fs-md: 14px;
  --fs-lg: 16px;
  --fs-xl: 18px;

  /* Vertical rhythm */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'cv11', 'ss01';
  letter-spacing: 0.005em;
}

/* ---- Top navbar ---- */
#navbar-white.navbar-default {
  background: var(--panel);
  border: none;
  box-shadow: 0 1px 3px rgba(16, 24, 40, 0.08);
  transition: background 0.2s ease, box-shadow 0.2s ease;
}
#navbar-white .navbar-brand {
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  font-size: 18px;
}

/* ---- Navbar hidden state (body.nav-hidden) ---- */
body.nav-hidden #navbar-white.navbar-default {
  background: transparent;
  box-shadow: none;
}
body.nav-hidden #navbar-white .navbar-brand { display: none; }

/* The sidebar hamburger must ALWAYS stay visible and usable — float it. */
#menu-toggle {
  position: relative;
  z-index: 1050;
  display: inline-block !important;
  visibility: visible !important;
  opacity: 1 !important;
}

/* Small subtle fixed eye / eye-slash toggle, top-right. */
#navToggle {
  position: fixed;
  top: 10px; right: 12px;
  z-index: 1100;
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  font-size: 14px; line-height: 1;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.65);
  border: 1px solid rgba(203, 213, 224, 0.6);
  border-radius: 7px;
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 0.2s ease, color 0.2s ease, background 0.2s ease;
}
#navToggle:hover { opacity: 1; color: var(--accent); background: rgba(255, 255, 255, 0.92); }
.slidebar-toggle {
  background: transparent;
  border: none;
  color: var(--accent);
  font-size: 18px;
  padding: 14px 16px;
  cursor: pointer;
  transition: color 0.15s ease;
}
.slidebar-toggle:hover { color: var(--ink); }

/* ---- Sidebar panels ---- */
.slidebar-nav { background: #fafbfc; border-right: 1px solid var(--line); }
#communityFiltering label,
#locationFiltering label,
#surpriseFiltering label,
#historyFiltering label {
  font-weight: 600;
  color: var(--ink);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.04em;
}
.borderbottom { border-bottom: 1px solid var(--line) !important; }

/* ---- Buttons ---- */
.btn-info {
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 500;
  border-radius: 6px;
}
.btn-info:hover, .btn-info:focus {
  background: #245a94;
  border-color: #245a94;
}

/* ---- Loading overlay + spinner ---- */
#loadingOverlay {
  display: none;
  position: absolute;
  top: 0; left: 0; right: 0;
  margin-top: 120px;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  z-index: 5;
}
#loadingOverlay .spinner {
  width: 42px; height: 42px;
  margin: 0 auto 14px;
  border: 4px solid var(--accent-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
#loadingOverlay .load-error { color: #c0392b; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- Graph aesthetics ---- */
#graphContainer { margin-top: 64px; transition: margin-top 0.2s ease; }
/* Less top margin when the bar is hidden so content isn't pushed down. */
body.nav-hidden #graphContainer { margin-top: 18px; }
/* Transition ONLY the handful of highlighted bridge edges — never the bulk of
   the ~4600 links, whose opacity is written directly per hover/restyle and must
   snap instantly (see networkLayout.js). A blanket .link transition would
   re-animate every edge on every hover, the exact jank the JS avoids. */
.link.surprise-latest, .link.surprise-past { transition: opacity 0.15s ease; }
/* Crisp white halo so the node labels stay sharply legible over the dense
   edge web. A STRONG, fully-opaque white halo via paint-order:stroke under a
   dark fill — the JS sets each label's font-size / font-weight / fill-opacity
   inline per node (key vs non-key), so that size/weight distinction is
   preserved; here we just guarantee the dark ink + crisp halo base. */
.node text,
.nodeText {
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  fill: #1a1a1a;
}
.node rect.labelBg { stroke: none; }   /* soft translucent label pill (replaces the white outline) */
.node circle { cursor: pointer; }

/* ---- Tooltip ---- */
.tooltip {
  position: absolute;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(16, 24, 40, 0.14);
  padding: 10px 14px;
  font-size: 13px;
  max-width: 280px;
  pointer-events: none;
}
.tooltip a { color: var(--accent); font-weight: 600; }

/* ---- Hover-history entries ---- */
#historyData div {
  font-size: 12px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--line);
  color: var(--muted);
}
#historyData a { color: var(--accent); font-weight: 600; }

/* ---- Surprise Me modal (override surprisePatterns.css positioning) ---- */
.modal { padding-top: 0 !important; }
.modal-content {
  border-radius: 12px;
  overflow: hidden;
  max-width: 520px;
  margin-top: 12vh;
  border: none;
}
.modal-header {
  background: var(--accent) !important;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.modal-footer { background: var(--accent) !important; }
.modal-body { padding: 18px 20px !important; font-size: 15px; line-height: 1.5; }
.modal-body strong { color: var(--accent); }

/* ---- Sidebar filter rows ---- */
.surprise-hint {
  font-size: 11px;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  line-height: 1.45;
  margin: 4px 0 8px;
}
.filter-row {
  display: block;
  margin: 4px 0 !important;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  font-size: 12px;
  color: var(--ink);
  cursor: pointer;
}
.filter-row input { margin-right: 7px; vertical-align: middle; }
.filter-row .cnt { color: var(--muted); font-weight: 400; }

/* ---- Surprise bridges on the graph ---- */
.link.surprise-latest { filter: drop-shadow(0 0 4px rgba(245, 166, 35, 0.9)); }

/* ---- Surprise thread panel (newest card on top) ---- */
#surpriseThread {
  position: fixed;
  right: 18px;
  bottom: 18px;
  width: 340px;
  max-height: 70vh;
  display: none;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(16, 24, 40, 0.18);
  z-index: 1200;
  overflow: hidden;
}
#surpriseThread.open { display: flex; }
.st-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: linear-gradient(135deg, #2b6cb0, #1f4e82);
  color: #fff;
  font-weight: 700;
  font-size: 14px;
}
.st-reset {
  background: rgba(255, 255, 255, 0.18);
  border: none;
  color: #fff;
  border-radius: 6px;
  padding: 3px 10px;
  font-size: 12px;
  cursor: pointer;
}
.st-reset:hover { background: rgba(255, 255, 255, 0.32); }
#surpriseCards { overflow-y: auto; padding: 10px 12px; }

.surprise-card {
  border: 1px solid var(--line);
  border-left: 4px solid #f5a623;
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 10px;
  background: #fffdf8;
  animation: scIn 0.35s ease;
}
@keyframes scIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.sc-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.sc-num { font-weight: 700; color: #b9770f; font-size: 12px; letter-spacing: 0.03em; }
.sc-bw { font-size: 11px; color: var(--muted); background: #f1f4f8; border-radius: 20px; padding: 2px 8px; }
.sc-pair { font-size: 14px; font-weight: 600; margin-bottom: 6px; line-height: 1.35; }
.sc-pair a { color: var(--accent); }
.sc-arrow { color: #f5a623; padding: 0 4px; }
.sc-text { font-size: 12.5px; line-height: 1.5; color: var(--ink); }

@media (max-width: 640px) {
  #surpriseThread { right: 8px; left: 8px; width: auto; bottom: 8px; }
}

/* ====================================================================
   Reach-two-people dialog (#pathDialog) — reuses the .trav-* route map
   styling (renderRoute), so it only needs the shared font + the two
   name inputs styled like #nodeSearch.
   ==================================================================== */
#pathDialog { font-family: 'Inter', system-ui, sans-serif; }

/* "Reach two people" name inputs — full-width, padded, rounded, matching
   #nodeSearch (surprisePatterns.css). */
#pathFrom, #pathTo {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 10px;
  margin: 4px 0;
  font-size: 13px;
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  border: 1px solid #cbd5e0;
  border-radius: 7px;
  outline: none;
}
#pathFrom:focus, #pathTo:focus { border-color: var(--accent); }

/* ====================================================================
   Bring-your-own data — small, subtle file picker label
   ==================================================================== */
.byo {
  display: block;
  margin: 8px 0 4px;
  font-size: 11px;
  color: var(--muted);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.45;
}
.byo input[type="file"] { font-size: 11px; color: var(--muted); margin-top: 3px; }

/* ====================================================================
   Intro overlay — full-screen centered modal scrim + white card
   ==================================================================== */
#introOverlay {
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  z-index: 1300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  box-sizing: border-box;
  /* Deep blue-black glass: the graph reads as a softly glowing constellation behind it. */
  background: rgba(15, 22, 33, 0.62);
  backdrop-filter: blur(6px) saturate(0.9);
  -webkit-backdrop-filter: blur(6px) saturate(0.9);
  animation: introScrim 0.2s ease-out both;
}
@supports not (backdrop-filter: blur(6px)) {
  #introOverlay { background: rgba(15, 22, 33, 0.75); }
}
@keyframes introScrim { from { opacity: 0; } to { opacity: 1; } }
/* State class is .intro-open, NOT .show — Bootstrap 3's global `.show{display:block
   !important}` (CDN) would defeat the flex centering above. Do not reintroduce .show. */
#introOverlay:not(.intro-open) { display: none; }
/* Exit must be ANIMATIONS, not transitions: the entrance animations' fill-mode:both
   pins opacity/transform forever, and animation-origin values beat any transition. */
#introOverlay.intro-closing {
  pointer-events: none;
  animation: introScrimOut 0.24s var(--ease) both;
}
#introOverlay.intro-closing .intro-card {
  animation: introCardOut 0.24s var(--ease) both;
}
@keyframes introScrimOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes introCardOut {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(6px) scale(0.985); }
}
#introOverlay .intro-card {
  background: var(--panel);
  max-width: 600px;
  width: 100%;
  border-radius: 16px;
  padding: 40px 44px 34px;
  box-shadow: 0 24px 70px rgba(16, 24, 40, 0.45);
  box-sizing: border-box;
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  animation: introCard 0.36s var(--ease) 0.04s both;
}
@keyframes introCard {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  #introOverlay, #introOverlay .intro-card,
  #introOverlay.intro-closing, #introOverlay.intro-closing .intro-card { animation: none; }
}
#introOverlay h1, #introOverlay h2, #introOverlay .intro-title {
  margin: 0 0 12px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--ink);
}
#introOverlay p, #introOverlay li {
  font-size: 16px;
  line-height: 1.6;
  color: #374151;
  margin: 0 0 12px;
}
#introOverlay strong { color: var(--accent); }
#introOverlay .intro-btn,
#introOverlay button {
  display: inline-block;
  margin-top: 12px;
  padding: 11px 24px;
  font-size: 15px;
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: #fff;
  background: var(--accent);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s ease;
}
#introOverlay .intro-btn:hover,
#introOverlay button:hover { background: #245a94; }

/* ====================================================================
   Download-report button in the intel panel head — small, subtle
   ==================================================================== */
.intel-report {
  border: 1px solid #cbd5e0;
  background: #fff;
  color: var(--accent);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  padding: 4px 10px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.intel-report:hover { background: #f0f6ff; border-color: var(--accent); }
.intel-report:active { transform: translateY(0.5px); }

/* ====================================================================
   10x VISUAL POLISH LAYER (appended last so it refines, never removes,
   the selectors above). Goal: a calm, product-grade aesthetic on the
   #2b6cb0 / #f5a623 palette — consistent Inter scale, soft shadows,
   8px radii, gentle borders, even vertical rhythm, subtle hovers.
   ==================================================================== */

/* ---- Navbar: quieter chrome, crisper brand ---- */
#navbar-white.navbar-default {
  box-shadow: var(--sh-1);
  border-bottom: 1px solid var(--line-soft);
}
#navbar-white .navbar-brand { letter-spacing: -0.015em; }

/* ---- Sidebar: airier section labels + restful surface ---- */
.slidebar-nav { background: var(--surface-1); }
#communityFiltering label,
#locationFiltering label,
#surpriseFiltering label,
#historyFiltering label {
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  color: var(--ink-3);
  margin-bottom: var(--space-2);
}
/* Even vertical rhythm between sidebar filter groups. */
#communityFiltering,
#locationFiltering,
#surpriseFiltering,
#historyFiltering { padding: var(--space-3) 0; }
.borderbottom { border-bottom: 1px solid var(--line-soft) !important; }

/* ---- Buttons: settled radius + tactile press ---- */
.btn-info {
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  transition: background 0.15s var(--ease), box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
}
.btn-info:hover, .btn-info:focus { box-shadow: var(--sh-2); }
.btn-info:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }

/* ---- Tooltip: softer card ---- */
.tooltip {
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  border-color: var(--line);
  line-height: 1.5;
}

/* ---- Surprise / modal: softer shadow + rounder ---- */
.modal-content { box-shadow: var(--sh-3); border-radius: var(--r-lg); }

/* ---- Surprise thread + cards: consistent radii + soft elevation ---- */
#surpriseThread { border-radius: var(--r-lg); box-shadow: var(--sh-3); }
.surprise-card {
  border-radius: var(--r-md);
  transition: box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
}
.surprise-card:hover { box-shadow: var(--sh-2); }
.sc-bw { border-radius: var(--r-pill); }

/* ---- Shared dialog & card surfaces: align radii / borders ---- */
.bridge-canvas, .trav-canvas, .hv-canvas, .hv-bcanvas { border-radius: var(--r-md); border-color: var(--line); }
.bridge-node circle, .trav-node circle { transition: stroke 0.12s var(--ease); }

/* ====================================================================
   Network Intelligence panel — make it feel premium & readable
   ==================================================================== */
#intelPanel {
  box-shadow: var(--sh-3);
  border-left: 1px solid var(--line-soft);
}
#intelPanel .intel-head {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line-soft);
}
#intelPanel .intel-title {
  font-size: var(--fs-lg);
  letter-spacing: -0.01em;
  margin-right: auto;
}
#intelPanel .intel-close { transition: color 0.12s var(--ease); }
/* Roomier, scroll-friendly body with a gentle fade hint at the top. */
#intelBody { padding: var(--space-2) var(--space-5) calc(var(--space-5) + 8px); }

/* Section rhythm: generous padding, hairline dividers, no border on the
   final section so the panel doesn't end on a stray line. */
.ni-section { padding: var(--space-4) 0; border-bottom: 1px solid var(--line-soft); }
.ni-section:last-child { border-bottom: none; }
.ni-section h3 {
  font-size: var(--fs-md);
  letter-spacing: -0.005em;
  color: #2d3748;
  margin: 0 0 var(--space-3);
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px;
}
.ni-section h3 .ni-hint {
  margin-left: 0;
  font-size: var(--fs-xs);
  color: var(--ink-3);
  font-weight: 500;
}

/* "At a glance" stat tiles — give the headline numbers room to breathe. */
.ni-stats {
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-1);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  margin-bottom: var(--space-3);
}
.ni-stats span { gap: 2px; font-size: var(--fs-xs); color: var(--ink-3); letter-spacing: 0.01em; }
.ni-stats b { font-size: 22px; letter-spacing: -0.02em; }

.ni-summary, .ni-reach { font-size: var(--fs-base); line-height: 1.65; color: var(--ink-2); }

/* People rows — subtle, snappy hover with a calm wash. */
.ni-list { display: flex; flex-direction: column; gap: 2px; }
.ni-person {
  border-radius: var(--r-md);
  transition: background 0.12s var(--ease);
}
.ni-person:hover { background: var(--surface-hover); }
.ni-pname { font-size: var(--fs-base); }
.ni-pwhy { color: var(--ink-3); margin-top: 2px; }

/* Community rows + swatches */
.ni-comm { padding: var(--space-2) var(--space-1); border-radius: var(--r-md); transition: background 0.12s var(--ease); }
.ni-comm:hover { background: var(--surface-1); }
.ni-csum { font-size: var(--fs-base); line-height: 1.6; color: var(--ink-2); }
.ni-swatch { box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), var(--sh-1); }

.ni-link:hover, .ni-mini:hover { color: var(--accent-ink); text-decoration: underline; }
.ni-empty { font-style: italic; }

/* ====================================================================
   New dashboard sections — fragility / intros — styled to sit naturally
   alongside the existing .ni-person / .ni-list / .ni-section family.
   Defensive: classes only take effect if the JS emits them; harmless
   otherwise, and they never override existing selectors.
   ==================================================================== */

/* A callout sub-card for fragility/risk and warm-intro suggestions. */
.ni-callout {
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: var(--surface-1);
  padding: var(--space-3) var(--space-3);
  margin: var(--space-2) 0;
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-2);
}
/* Fragility leans on the warm accent to read as "watch this". */
.ni-fragile, .ni-callout.ni-warn {
  border-left-color: var(--highlight);
  background: #fffdf8;
}
/* Warm intros lean on the brand blue — opportunity, not risk. */
.ni-intro, .ni-callout.ni-intro { border-left-color: var(--accent); }

.ni-callout .ni-cohead {
  display: block;
  font-size: var(--fs-base);
  font-weight: 700;
  color: #1a202c;
  margin-bottom: 2px;
}
.ni-callout .ni-cowhy { display: block; font-size: var(--fs-sm); color: var(--ink-3); line-height: 1.5; }

/* Small inline metric / tag chips for the new sections. */
.ni-tag, .ni-metric {
  display: inline-block;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-pill);
  padding: 1px 9px;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ni-tag.ni-warn, .ni-metric.ni-warn { color: #b9770f; background: #fdf1dc; }

/* A thin progress/score bar (e.g. fragility score 0–1) reusing the bridge palette. */
.ni-bar {
  position: relative;
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--line-soft);
  overflow: hidden;
  margin: var(--space-2) 0 var(--space-1);
}
.ni-bar > i {
  display: block; height: 100%;
  background: linear-gradient(90deg, #cdd8e3, #13447e);
  border-radius: inherit;
}

/* ====================================================================
   EDGE BETWEENNESS LEGEND (#edgeLegend) — a small, fixed, non-intrusive
   card near the graph (bottom-left), mirroring #nodeMetrics' restraint.
   The gradient bar uses the EXACT edge-betweenness color contract:
   linear-gradient(90deg, #cdd8e3 -> #13447e) so it matches the links.
   ==================================================================== */
#edgeLegend {
  position: fixed;
  left: 14px;
  bottom: 14px;
  z-index: 5;
  width: 180px;
  padding: 9px 11px 10px;
  font-family: 'Inter', system-ui, sans-serif;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(203, 213, 224, 0.5);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0.72;
  transition: opacity 0.3s var(--ease);
  pointer-events: none;
}
#edgeLegend:hover { opacity: 0.97; }

/* "edge betweenness" caption — quiet, uppercase, tracks like #nodeMetrics. */
#edgeLegend .el-label {
  display: block;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #8a929c;
  margin-bottom: 6px;
  text-align: center;
}

/* The gradient bar — SAME endpoints as the link color scale + legend match. */
#edgeLegend .el-bar {
  height: 9px;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, #cdd8e3, #13447e);
  border: 1px solid rgba(19, 68, 126, 0.12);
}

/* "low ............ high" end labels under the bar. */
#edgeLegend .el-ends {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  font-size: 9px;
  letter-spacing: 0.03em;
  color: #9aa3ad;
}
#edgeLegend .el-ends span { line-height: 1.2; }

@media (max-width: 640px) {
  #edgeLegend { width: 150px; left: 8px; bottom: 8px; }
}

/* ====================================================================
   ROLE LEGEND (#roleLegend) — a small, fixed, non-intrusive card that
   keys the Guimera-Amaral role colours (window.ROLE_COLORS) shown by the
   "role" node lens. Mirrors #edgeLegend's restraint exactly: a translucent
   blurred card, quiet uppercase caption, faint until hover, never hit-tests
   the graph. Sits just above #edgeLegend (bottom-left) so the two small
   legends stack without overlapping. Rows are a tiny round swatch + label.
   The swatch colours are set inline per-row (from ROLE_COLORS), so we never
   hardcode the palette here. Defensive / id-anchored — harmless until the
   markup is mounted.
   ==================================================================== */
#roleLegend {
  position: fixed;
  left: 14px;
  bottom: 84px;                 /* clears #edgeLegend, which sits at bottom:14px */
  z-index: 5;
  width: 180px;
  padding: 9px 11px 10px;
  font-family: 'Inter', system-ui, sans-serif;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(203, 213, 224, 0.5);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0.72;
  transition: opacity 0.3s var(--ease);
  pointer-events: none;
}
#roleLegend:hover { opacity: 0.97; }

/* "network role" caption — quiet, uppercase, tracks like #edgeLegend. */
#roleLegend .rl-label {
  display: block;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #8a929c;
  margin-bottom: 6px;
  text-align: center;
}

/* One legend entry: a small round swatch + a quiet role label. */
#roleLegend .rl-row {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 10px;
  line-height: 1.5;
  color: #647280;
}
#roleLegend .rl-row + .rl-row { margin-top: 3px; }
#roleLegend .rl-name { letter-spacing: 0.01em; }
#roleLegend .rl-count {
  margin-left: auto;
  color: #9aa3ad;
  font-variant-numeric: tabular-nums;
}

/* The colour dot — tiny, soft white ring so it reads on any backdrop.
   Background colour is supplied inline per-row from window.ROLE_COLORS. */
.role-swatch {
  flex: 0 0 9px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.9), var(--sh-1);
}

@media (max-width: 640px) {
  #roleLegend { width: 150px; left: 8px; bottom: 78px; }
}

/* ====================================================================
   DASHBOARD ROLE DISTRIBUTION (.ni-role / .ni-role-dot) — small chips
   that show the network's Guimera-Amaral role mix inside the Network
   Intelligence panel, sitting naturally alongside the .ni-* family. Each
   chip is a quiet pill: a tiny coloured dot + the role name + its count.
   The dot colour is set inline per-chip from window.ROLE_COLORS, so the
   palette is never hardcoded here. Defensive — harmless until emitted.
   ==================================================================== */
.ni-roles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) 6px;
  margin: var(--space-1) 0 var(--space-2);
}
.ni-role {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface-1);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-pill);
  padding: 2px 9px 2px 7px;
  line-height: 1.6;
  white-space: nowrap;
}
.ni-role .ni-role-cnt {
  color: var(--ink-3);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
/* The chip's colour dot — colour supplied inline from window.ROLE_COLORS. */
.ni-role-dot {
  flex: 0 0 8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.9), var(--sh-1);
}

/* ====================================================================
   LENS PANEL — final polish pass (modern.css loads last, so this only
   refines the structural styling in surprisePatterns.css; it never
   removes it). Settles the new controls onto the 8px-radius / soft-shadow
   token family and gives the sidebar "Lens" section + its label the same
   rhythm as the other filter groups. Defensive: id/class anchored, so it
   is harmless until the markup is mounted.
   ==================================================================== */

/* The Lens section label matches the other uppercase sidebar group labels. */
#lensPanel label {
  font-weight: 600;
  color: var(--ink-3);
  text-transform: uppercase;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  margin-bottom: var(--space-2);
}
#lensPanel { padding: var(--space-3) 0; }

/* Selects: settle radius + shadow onto the token family (keeps the
   #nodeSearch-matched look from surprisePatterns.css, just on-system). */
#nodeMetricSel,
#edgeModeSel {
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  color: var(--ink);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

/* Backbone readout: align to the muted-caption token + rhythm. */
#backboneReadout {
  color: var(--ink-3);
  font-size: var(--fs-xs);
  margin-top: var(--space-1);
}

/* ---- "What's new — methods" <details> — token-aligned refinements ---- */
.methods-panel {
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
.methods-panel > summary { color: var(--accent); }
.methods-panel > summary:hover { color: var(--accent-ink); }
.methods-panel .methods-list > li { border-bottom-color: var(--line-soft); }
.methods-panel .methods-list > li::before { background: var(--accent); }
.methods-panel .m-cite,
.methods-panel cite { color: var(--ink-3); }

/* ---- Enlarged + more readable Network Intelligence side panel ---- */
#intelPanel { width: 540px; max-width: 96vw; }
#intelPanel .intel-head { padding: 18px 22px; }
#intelPanel .intel-title { font-size: 20px; }
#intelBody { padding: 8px 22px 32px; font-size: 14px; line-height: 1.6; }
#intelBody .ni-section h3 { font-size: 15.5px; }
#intelBody .ni-pname { font-size: 14.5px; }
#intelBody .ni-pwhy  { font-size: 12.8px; line-height: 1.55; }
#intelBody .ni-summary, #intelBody .ni-reach, #intelBody .ni-csum { font-size: 14px; line-height: 1.66; }
#intelBody .ni-stats b { font-size: 23px; }
#intelBody .ni-list { margin: 2px 0; }
#intelBody .ni-person { padding: 9px 12px; }
/* a touch more room for the subtle hover readout too */
#nodeMetrics { min-width: 150px; max-width: 260px; padding: 11px 14px; }

/* ---- Enlarged + readable hover info panel (#nodeMetrics) ---- */
#nodeMetrics {
  width: 330px; max-width: 34vw; max-height: 72vh; padding: 16px 18px;
  border-radius: 12px; background: rgba(255,255,255,0.93);
  border-color: rgba(203,213,224,0.85); box-shadow: 0 10px 30px rgba(16,24,40,0.16);
  /* Sit BELOW the docked right panels (overrides the z:2000 in surprisePatterns)
     so an open #agentPanel / #vitalsPanel / intel / story covers this readout
     instead of it floating over them. It's pointer-events:none, so it never
     blocks the panel anyway. */
  z-index: 1090;
}
/* DEFINITIVE shown state — resolves the duplicate #nodeMetrics.show opacity
   conflict the critique flagged. The old surprisePatterns.css rule used 0.62,
   which made the readout too faint over dense edges AND made readability depend
   on stylesheet load order. This is the single winning rule: near-solid for
   real contrast, marked !important so it wins no matter the load order. */
#nodeMetrics.show {
  opacity: 0.97 !important;
  background: rgba(255, 255, 255, 0.95) !important;
  /* Drop the translucency blur in the shown state so text never sits on a
     see-through, edge-cluttered backdrop. */
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
#nodeMetrics:hover { opacity: 1 !important; }
/* Space-efficient stacking: #nodeMetrics is pointer-events:none (a click-through
   readout), so it can't be hovered to expand — instead, when it's shown the
   secondary insight card YIELDS (fades out) so the two never share the corner.
   See the z-index lowering near #insightPanel so an open docked panel covers
   both cards instead of them floating on top of it. */
#nodeMetrics.show ~ #insightPanel { opacity: 0 !important; pointer-events: none !important; }
#nodeMetrics .nm-name { font-size: 18px; line-height: 1.25; }
#nodeMetrics .nm-sub { font-size: 12.5px; margin-top: 3px; }
#nodeMetrics .nm-rows { margin-top: 12px; }
#nodeMetrics .nm-row { font-size: 13px; line-height: 1.95; }
#nodeMetrics .nm-conns { margin-top: 12px; gap: 6px; }
#nodeMetrics .nm-conns i { font-size: 10.5px; }
#nodeMetrics .nm-conn { font-size: 12px; padding: 3px 9px; }
#nodeMetrics .nm-reason { font-size: 13.5px; line-height: 1.62; margin-top: 11px; }

/* ---- Enriched LinkedIn block (Apify-sourced) inside #nodeMetrics ----
   Additive readout section: a header, status pills, reach line, skill chips,
   and a profile link. Only rendered when a node carries the extra fields, so
   demo nodes are unaffected. Styled to sit cleanly under the existing rows. */
#nodeMetrics .nm-en {
  margin-top: 11px; padding-top: 9px;
  border-top: 1px solid rgba(203, 213, 224, 0.6);
}
#nodeMetrics .nm-en-head {
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
  color: #6b7480; margin-bottom: 7px;
}
#nodeMetrics .nm-en-badges {
  display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 7px;
}
#nodeMetrics .nm-badge {
  display: inline-block; font-size: 9.5px; font-weight: 700;
  letter-spacing: .04em; line-height: 1.5; padding: 2px 8px;
  border-radius: 9px; color: #fff; white-space: nowrap;
}
#nodeMetrics .nm-badge-hire { background: #1a7f37; }   /* green  — hiring */
#nodeMetrics .nm-badge-open { background: #0a66c2; }   /* blue   — open to work */
#nodeMetrics .nm-en-reach {
  display: flex; flex-wrap: wrap; gap: 4px 14px;
  font-size: 11px; color: #7a828d; margin-bottom: 7px;
}
#nodeMetrics .nm-en-reach .nm-en-stat { white-space: nowrap; }
#nodeMetrics .nm-en-reach b {
  font-weight: 700; color: #46505c; font-variant-numeric: tabular-nums;
}
#nodeMetrics .nm-en-skills {
  display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px;
}
#nodeMetrics .nm-chip {
  display: inline-block; font-size: 11px; color: #46505c;
  background: rgba(226, 232, 240, 0.85); border-radius: 9px;
  padding: 2px 9px; line-height: 1.5; white-space: nowrap;
}
#nodeMetrics .nm-en-link {
  display: inline-block; font-size: 11px; font-weight: 600;
  color: #56B4E9; text-decoration: none;
}
#nodeMetrics .nm-en-link:hover { text-decoration: underline; }

/* ====================================================================
   SIDEBAR INFORMATION ARCHITECTURE — group headers + quick-start card.
   Part of the IA pass: the sidebar is organised into labelled groups, each
   introduced by a small uppercase .nav-group header with a top divider, and
   it opens with a soft, readable #quickStart orientation card.
   Defensive / id-class anchored, on-brand tokens — harmless until the markup
   is mounted (loads last, so it refines without removing anything above).
   ==================================================================== */

/* Section/group header: small, uppercase, muted, letter-spaced, with a
   hairline divider above so groups read as distinct bands. The first group
   header drops its top divider/margin so the sidebar doesn't open on a line. */
.nav-group {
  display: block;
  margin: var(--space-4) 0 var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line-soft);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  line-height: 1.3;
}
.nav-group:first-child,
.nav-group.nav-group--first {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* Quick-start orientation card: a soft accent-tinted panel that stays
   comfortably readable (dark body text on a pale blue wash, brand-blue
   accent rail). Calm, not loud. */
#quickStart {
  margin: var(--space-2) 0 var(--space-3);
  padding: var(--space-3) var(--space-3);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-2);
  background: var(--accent-soft);
  border: 1px solid #d7e6f6;
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
}
#quickStart h1,
#quickStart h2,
#quickStart h3,
#quickStart .qs-title {
  margin: 0 0 var(--space-1);
  font-size: var(--fs-base);
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--ink);
}
#quickStart p { margin: 0 0 var(--space-2); }
#quickStart p:last-child { margin-bottom: 0; }
#quickStart ol,
#quickStart ul { margin: var(--space-1) 0 0; padding-left: 18px; }
#quickStart li { margin: 0 0 var(--space-1); line-height: 1.5; }
#quickStart li:last-child { margin-bottom: 0; }
#quickStart strong, #quickStart b { color: var(--accent-ink); font-weight: 700; }
#quickStart a { color: var(--accent); font-weight: 600; text-decoration: none; }
#quickStart a:hover { color: var(--accent-ink); text-decoration: underline; }

/* ====================================================================
   COMMUNITY SUPER-NODE LABELS (#hierViews .hv-clabel / .hv-csize) —
   legibility hardening. The bubbles are filled with the shared community
   palette (window.groupColor) at ~0.85 opacity over a pale canvas, so some
   palette hues (light yellows/greens in a colorblind-safe set) leave white
   text low-contrast. These rules reinforce the existing dark halo so the
   community name + "N people" stay readable on ANY bubble colour. modern.css
   loads last, so this refines (does not replace) the base rules in
   surprisePatterns.css.
   ==================================================================== */
.hv-clabel {
  fill: #ffffff;
  font-weight: 700;
  paint-order: stroke;
  /* Stronger, darker, fully-opaque halo for guaranteed contrast on light hues. */
  stroke: rgba(17, 26, 39, 0.92);
  stroke-width: 3.5px;
  stroke-linejoin: round;
  stroke-linecap: round;
  /* Subtle drop shadow adds separation where the halo alone isn't enough. */
  text-shadow: 0 1px 2px rgba(15, 23, 42, 0.55);
}
.hv-csize {
  fill: #ffffff;
  opacity: 1;
  paint-order: stroke;
  stroke: rgba(17, 26, 39, 0.88);
  stroke-width: 2.5px;
  stroke-linejoin: round;
  stroke-linecap: round;
  text-shadow: 0 1px 2px rgba(15, 23, 42, 0.5);
}

/* ====================================================================
   ASK YOUR NETWORK — GraphRAG query-focused QA panel (#askPanel).
   A sidebar panel: full-width rounded #askInput (matching #nodeSearch),
   a primary #askBtn (matching .btn-info), a readable #askAnswer answer
   card (accent left rail, comfy line-height, scrolls when long), cited-
   person chips (.ask-chip — matching the .nm-conn pill, but clickable),
   a small/muted #askKey <details> for the optional API key, and a quiet
   .ask-loading spinner state. On-brand (#2b6cb0 / Inter); token-aligned.
   modern.css loads last, so this refines without removing anything above.
   Defensive / id-class anchored — harmless until the markup is mounted.
   ==================================================================== */

/* Panel wrapper — sits in the sidebar like the other filter groups, with
   the same comfortable vertical rhythm. */
#askPanel {
  padding: var(--space-3) 0;
  font-family: 'Inter', system-ui, sans-serif;
}
#askPanel label {
  font-weight: 600;
  color: var(--ink-3);
  text-transform: uppercase;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  margin-bottom: var(--space-2);
}

/* The question input — full-width, padded, rounded; mirrors #nodeSearch
   exactly (incl. the on-brand focus ring from the polish pass). */
#askInput {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 10px;
  font-size: var(--fs-base);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  border: 1px solid #cbd5e0;
  border-radius: 7px;
  outline: none;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
#askInput::placeholder { color: var(--ink-3); }
#askInput:hover { border-color: #b6c2d2; }
#askInput:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.14);
}

/* The Ask button — a primary action, matching .btn-info's brand fill,
   radius, soft shadow and tactile press. */
#askBtn {
  display: inline-block;
  margin-top: var(--space-2);
  padding: 8px 16px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
}
#askBtn:hover, #askBtn:focus {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  box-shadow: var(--sh-2);
}
#askBtn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }
#askBtn:disabled,
#askBtn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* The answer card — readable body text, generous line-height, a subtle
   accent left border, and a max-height so long answers scroll rather than
   push the sidebar. Empty by default (hidden until it has content). */
#askAnswer {
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-3);
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--ink-2);
  background: var(--surface-1);
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  max-height: 320px;
  overflow-y: auto;
  overflow-wrap: break-word;
  word-break: break-word;
}
#askAnswer:empty { display: none; }
#askAnswer p { margin: 0 0 var(--space-2); }
#askAnswer p:last-child { margin-bottom: 0; }
#askAnswer strong, #askAnswer b { color: var(--accent-ink); font-weight: 700; }
#askAnswer a { color: var(--accent); font-weight: 600; text-decoration: none; }
#askAnswer a:hover { color: var(--accent-ink); text-decoration: underline; }

/* Cited-person chips — match the .nm-conn pill, but clickable (they focus
   the cited node), so they get a cursor + a gentle on-brand hover. */
#askAnswer .ask-chip,
.ask-chip {
  display: inline-block;
  font-size: 9.5px;
  color: #5a6675;
  background: rgba(226, 232, 240, 0.7);
  border-radius: 9px;
  padding: 1px 7px;
  line-height: 1.5;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
#askAnswer .ask-chip:hover,
.ask-chip:hover {
  background: var(--accent-soft);
  color: var(--accent-ink);
}
/* Optional small grouping of cited chips beneath the answer text. */
#askAnswer .ask-cites,
.ask-cites {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
  margin-top: var(--space-2);
}

/* The optional API-key control — small, muted, tucked into a <details>. */
#askKey,
.ask-key {
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
  color: var(--ink-3);
}
#askKey summary,
.ask-key summary {
  cursor: pointer;
  list-style: none;
  color: var(--ink-3);
  font-size: var(--fs-xs);
  outline: none;
  transition: color 0.12s var(--ease);
}
#askKey summary:hover,
.ask-key summary:hover { color: var(--accent); }
#askKey summary::-webkit-details-marker,
.ask-key summary::-webkit-details-marker { display: none; }
/* If #askKey is itself the text/password input (per the contract it may be
   an input), style it small + muted like the rest of the control. */
input#askKey {
  width: 100%;
  box-sizing: border-box;
  margin-top: var(--space-1);
  padding: 5px 8px;
  font-size: var(--fs-xs);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink-2);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  outline: none;
  transition: border-color 0.12s var(--ease);
}
input#askKey:focus { border-color: var(--accent); }
/* Any key input nested inside a #askKey/.ask-key <details>. */
#askKey input,
.ask-key input {
  width: 100%;
  box-sizing: border-box;
  margin-top: var(--space-1);
  padding: 5px 8px;
  font-size: var(--fs-xs);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink-2);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  outline: none;
  transition: border-color 0.12s var(--ease);
}
#askKey input:focus,
.ask-key input:focus { border-color: var(--accent); }

/* Loading / thinking state — a small inline spinner the JS can toggle
   (e.g. add .ask-loading to #askAnswer or #askBtn while awaiting). */
.ask-loading {
  position: relative;
  color: var(--ink-3);
  font-style: italic;
}
.ask-loading::before {
  content: "";
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-right: 8px;
  vertical-align: -2px;
  border: 2px solid var(--accent-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
/* When the button itself shows the loading state, keep the label readable. */
#askBtn.ask-loading { color: #fff; opacity: 0.85; cursor: progress; }
#askBtn.ask-loading::before {
  border-color: rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
}

/* ---- Ask: highlight-explanation paragraph + model download progress ---- */
#askAnswer .ask-explain {
  font-size: 12.5px; color: #46566a; line-height: 1.55; margin: 8px 0 4px;
  padding: 8px 11px; background: rgba(43,108,176,0.07);
  border-left: 3px solid #2b6cb0; border-radius: 0 6px 6px 0;
}
.ask-progress { height: 6px; background: #e6e9ee; border-radius: 3px; overflow: hidden; margin-top: 7px; }
.ask-progress span { display: block; height: 100%; background: #2b6cb0; transition: width .2s ease; }

/* ---- Transparent on-graph community labels (topic + commonality) ---- */
.comm-overlay-label {
  font-family: 'Inter', system-ui, sans-serif; font-weight: 700; font-size: 19px;
  fill: #1f2933; fill-opacity: 0.34;
  paint-order: stroke; stroke: #ffffff; stroke-width: 4px; stroke-opacity: 0.6; stroke-linejoin: round;
}
.comm-overlay-sub {
  font-family: 'Inter', system-ui, sans-serif; font-weight: 600; font-size: 11.5px;
  fill: #46566a; fill-opacity: 0.55;
  paint-order: stroke; stroke: #ffffff; stroke-width: 3px; stroke-opacity: 0.6; stroke-linejoin: round;
}

/* ====================================================================
   ON-GRAPH COMMUNITY LABEL LEGIBILITY — refinement pass. The labels sit
   over a dense edge field; the JS now drops a translucent rounded "pill"
   rect behind each label group for separation. These rules (a) bump the
   text a touch and strengthen contrast so the topic + commonality read on
   ANY backdrop, and (b) style that background pill if/when the JS adds it.
   modern.css loads last, so this refines the base rules just above without
   removing them. Defensive: the .comm-overlay-bg rule is harmless if no
   pill is emitted.
   ==================================================================== */
.comm-overlay-label {
  font-size: 20px;
  font-weight: 700;
  fill: #000000;          /* black for maximum readability */
  fill-opacity: 1;
  stroke: #ffffff;
  stroke-width: 5px;
  stroke-opacity: 0.95;
  paint-order: stroke;
  stroke-linejoin: round;
}
.comm-overlay-sub {
  font-size: 12px;
  fill: #1a1a1a;          /* near-black */
  fill-opacity: 1;
  stroke: #ffffff;
  stroke-width: 3.5px;
  stroke-opacity: 0.95;
  paint-order: stroke;
  stroke-linejoin: round;
}
/* The translucent rounded pill the JS draws behind each label group. */
.comm-overlay-bg {
  fill: rgba(255, 255, 255, 0.62);
  stroke: rgba(203, 213, 224, 0.5);
  stroke-width: 1px;
  pointer-events: none;
}

/* ====================================================================
   NETWORK CONTEXT CARD (#networkContext) — the prominent "what is this
   network" summary at the top of the sidebar's EXPLORE group. It is the
   first thing a reader sees, so it gets a clear stat row, a readable
   summary, and room for the #storyBtn beneath. Rendered by
   window.renderNetworkContext() as .nc-card > (.nc-stats / .nc-row /
   .nc-summary). On-brand (#2b6cb0 / #f5a623, Inter); token-aligned. Lives
   inside #networkContextCard alongside #storyBtn. Defensive / id-class
   anchored — harmless until the markup is mounted.
   ==================================================================== */
#networkContextCard {
  margin: var(--space-2) 0 var(--space-1);
  font-family: 'Inter', system-ui, sans-serif;
}

/* The card surface — a calm accent-tinted panel that reads as the headline
   orientation block (matching #quickStart's restful wash, a touch bolder). */
#networkContext .nc-card {
  padding: var(--space-3) var(--space-3);
  background: linear-gradient(180deg, #ffffff, var(--surface-1));
  border: 1px solid var(--line-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  color: var(--ink-2);
}

/* Headline stat row — the network's numbers, given room to breathe. */
#networkContext .nc-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--line-soft);
}
#networkContext .nc-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 54px;
}
#networkContext .nc-num {
  font-size: 19px;
  font-weight: 700;
  line-height: 1.05;
  color: var(--accent);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
#networkContext .nc-lbl {
  font-size: var(--fs-xs);
  color: var(--ink-3);
  letter-spacing: 0.01em;
  line-height: 1.3;
}

/* "Top industries / Top orgs" rows — quiet supporting detail. */
#networkContext .nc-row {
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-2);
  margin: 2px 0;
}
#networkContext .nc-key {
  font-weight: 700;
  color: var(--ink);
}

/* The natural-language summary — the readable through-line of the card. */
#networkContext .nc-summary {
  margin-top: var(--space-2);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-2);
}

/* "Tell me the story" button — a clear primary action under the card. */
#networkContextCard .center { margin-top: var(--space-2); text-align: center; }
#storyBtn.btn {
  display: inline-block;
  width: 100%;
  padding: 8px 14px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
}
#storyBtn.btn:hover, #storyBtn.btn:focus {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  box-shadow: var(--sh-2);
  color: #fff;
}
#storyBtn.btn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }

/* ====================================================================
   SUGGESTED QUESTIONS (#suggestedQs) — small pill chips that wrap, sitting
   above the Ask box so the reader always has grounded prompts to click.
   They reuse the .ask-chip / .nm-conn pill look, but as clickable starters:
   pointer cursor + a gentle on-brand hover. Styled both for a dedicated
   .sq-chip class and for bare <button>/<a> children, so they look right
   however window.suggestedQuestions() is wired into the DOM. Defensive /
   id-class anchored — harmless until populated.
   ==================================================================== */
#suggestedQs {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: var(--space-1) 0 var(--space-2);
}
#suggestedQs:empty { display: none; }
#suggestedQs .sq-chip,
#suggestedQs button,
#suggestedQs a {
  display: inline-block;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: #5a6675;
  background: rgba(226, 232, 240, 0.7);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  padding: 3px 10px;
  line-height: 1.5;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease),
              border-color 0.12s var(--ease);
}
#suggestedQs .sq-chip:hover,
#suggestedQs button:hover,
#suggestedQs a:hover {
  background: var(--accent-soft);
  border-color: rgba(43, 108, 176, 0.25);
  color: var(--accent-ink);
}

/* ====================================================================
   NETWORK STORY PANEL (#storyPanel) — a docked side panel (mirrors
   #intelPanel) that narrates the network as an ordered set of beats. The
   networkStory module renders a header (.ns-head with .ns-headtitle /
   .ns-sub / .ns-controls + .ns-btn buttons) and a scrollable ordered list
   (.ns-beats > .ns-beat, each a .ns-step number + .ns-body of .ns-title /
   .ns-finding / .ns-how·.ns-howlabel). The currently-playing beat gets
   .ns-active — given a clear, distinct "active beat" state. On-brand
   (#2b6cb0 / #f5a623, Inter); token-aligned. modern.css loads last, so this
   styles the module's emitted classes without removing anything above.
   Defensive / id-class anchored — harmless until the story is rendered.
   ==================================================================== */
#storyPanel {
  position: fixed;
  top: 0; right: 0;
  z-index: 1100;
  width: 420px; max-width: 94vw;
  height: 100vh;
  background: #ffffff;
  box-shadow: -6px 0 28px rgba(0, 0, 0, 0.18);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  transform: translateX(105%);
  transition: transform 0.28s var(--ease);
}
/* The module shows the panel by adding .open (mirrors #intelPanel). If it
   instead toggles inline display, this stays harmless. */
#storyPanel.open { transform: translateX(0); }

/* Header — sticky title + intro + the play/step/reset controls. */
#storyPanel .ns-head {
  flex: 0 0 auto;
  padding: var(--space-4) var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--line-soft);
  background: linear-gradient(180deg, #fbfcfe, #f4f6f9);
}
#storyPanel .ns-headtitle {
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}
#storyPanel .ns-sub {
  margin-top: var(--space-1);
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-3);
}
#storyPanel .ns-sub b { color: var(--accent-ink); font-weight: 700; }

/* Player controls — a primary Play + two quieter secondary actions. */
#storyPanel .ns-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: var(--space-3);
}
#storyPanel .ns-btn {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--fs-sm);
  font-weight: 600;
  padding: 6px 13px;
  border-radius: var(--r-sm);
  border: 1px solid #cbd5e0;
  background: #fff;
  color: var(--accent);
  cursor: pointer;
  transition: background 0.12s var(--ease), border-color 0.12s var(--ease),
              box-shadow 0.12s var(--ease), transform 0.08s var(--ease);
}
#storyPanel .ns-btn:hover { background: var(--surface-hover); border-color: #b6c2d2; }
#storyPanel .ns-btn:active { transform: translateY(0.5px); }
/* Play is the primary action — brand fill. */
#storyPanel .ns-btn.ns-play {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: var(--sh-1);
}
#storyPanel .ns-btn.ns-play:hover {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  box-shadow: var(--sh-2);
}

/* The scrollable list of beats. */
#storyPanel .ns-beats {
  list-style: none;
  margin: 0;
  padding: var(--space-2) var(--space-4) var(--space-5);
  overflow-y: auto;
  flex: 1 1 auto;
}

/* One beat — a numbered step + its body. Calm at rest; clearly highlighted
   when active (the beat currently playing on the graph). */
#storyPanel .ns-beat {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3);
  margin: var(--space-2) 0;
  border: 1px solid var(--line-soft);
  border-left: 3px solid transparent;
  border-radius: var(--r-md);
  background: var(--surface-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease);
}
#storyPanel .ns-beat:hover { background: var(--surface-hover); border-left-color: #cdd8e3; }

/* ACTIVE BEAT — distinct, unmistakable: warm accent rail, soft elevation,
   a pale wash, and a brand-blue step badge. */
#storyPanel .ns-beat.ns-active {
  background: #fffdf8;
  border-color: #f3dcb0;
  border-left-color: var(--highlight);
  box-shadow: var(--sh-2);
}

/* The step number badge. */
#storyPanel .ns-step {
  flex: 0 0 26px;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: #cdd8e3;
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: 700;
  text-align: center;
  line-height: 26px;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--sh-1);
  transition: background 0.15s var(--ease);
}
#storyPanel .ns-beat:hover .ns-step { background: var(--accent); }
#storyPanel .ns-beat.ns-active .ns-step { background: var(--highlight); }

/* The beat body. */
#storyPanel .ns-body { flex: 1 1 auto; min-width: 0; }
#storyPanel .ns-title {
  font-size: var(--fs-md);
  font-weight: 700;
  color: #1a202c;
  line-height: 1.35;
  letter-spacing: -0.005em;
}
#storyPanel .ns-finding {
  margin-top: var(--space-1);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-2);
}
#storyPanel .ns-finding b { color: var(--accent-ink); font-weight: 700; }
/* "how:" — the quiet, secondary method line. */
#storyPanel .ns-how {
  margin-top: var(--space-2);
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-3);
}
#storyPanel .ns-howlabel {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: var(--fs-xs);
  color: #9aa3ad;
  margin-right: 4px;
}

/* Empty state. */
#storyPanel .ns-empty {
  padding: var(--space-5);
  font-size: var(--fs-base);
  font-style: italic;
  color: var(--ink-3);
}

@media (max-width: 640px) {
  #storyPanel { width: 100vw; max-width: 100vw; }
}

/* ====================================================================
   SIDE INSIGHT CARD (#insightPanel) — owned by main.js (window.Insight =
   { show(html), clear() }). EVERY highlight/focus/path on the graph writes
   a generated one-paragraph insight here (networkLayout's highlightNodes /
   focusNode / showPath all call window.Insight.show(...)), so this card is
   meant to be READ — not the tiny, faint hover readout. It is a docked,
   fixed card (~300px) bottom-right, above #nodeMetrics, with a soft shadow,
   a brand-blue accent left rail, and comfortably large body text. It is
   hidden until main.js adds the .show class (insight present), then fades
   in. On-brand (#2b6cb0 / #f5a623, Inter); token-aligned. modern.css loads
   last, so this refines without removing anything above. Defensive /
   id-anchored — harmless until main.js mounts the element.
   ==================================================================== */
#insightPanel {
  position: fixed;
  right: 18px;
  bottom: 92px;
  /* BELOW the docked right panels (#agentPanel z1120, #vitalsPanel z1100, intel/
     story ~1100-1300) so when any of them is open it cleanly COVERS this card
     instead of the card floating on top of the panel. Still well above the graph
     + legends. When the connection card (#nodeMetrics) is open, that card's
     sibling rule fades this one out so the bottom-right is never double-stacked. */
  z-index: 1090;
  width: 300px;
  max-width: calc(100vw - 36px);
  max-height: 60vh;
  overflow-y: auto;
  box-sizing: border-box;
  padding: var(--space-4) var(--space-4) var(--space-4) calc(var(--space-4) + 2px);
  font-family: 'Inter', system-ui, sans-serif;
  /* Larger, readable body text — this is a card to read, not a faint hint. */
  font-size: 14.5px;
  line-height: 1.62;
  color: var(--ink-2);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-left: 4px solid var(--accent);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
  box-shadow: var(--sh-3);
  /* Hidden until main.js adds .show (an insight is present); fade + slide in. */
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity 0.28s var(--ease), transform 0.28s var(--ease);
}
#insightPanel.show {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
/* Empty insight collapses (so a cleared panel never leaves a stray card). */
#insightPanel:empty { display: none; }

/* ====================================================================
   HOVER POPUPS YIELD TO OPEN PANELS — when any docked right panel is open
   (agent / story / intel / vitals / surprise), fully HIDE the hover tooltip,
   the connection-data readout, and the insight card so nothing floats over the
   panel and the workspace stays uncluttered. The z-index lowering on each of
   those elements is the fallback; this removes them outright where :has is
   supported, plus an explicit sibling fallback for the agent panel. */
body:has(#agentPanel.open, #storyPanel.open, #intelPanel.open, #vitalsPanel.open, #surprisePanel.open, #missionPanel.open, #whatifPanel.wi-open, #debatePanel.open, #roundtablePanel.open, #rsiPanel.open, #rsiInsightPanel.open, #rsiStratPanel.open, #rsiInflPanel.open, #rsiLabPanel.open, #hermesPanel.open, #richClubPanel.open, #personaPanel.open, #mapMindPanel.open, #gtPanel.open, #ripplePanel.open) :is(.tooltip, #nodeMetrics, #insightPanel, #liHelpBtn) {
  display: none !important;
}
#agentPanel.open ~ .tooltip,
#agentPanel.open ~ #nodeMetrics,
#agentPanel.open ~ #insightPanel { display: none !important; }

/* ====================================================================
   GRAPH AESTHETICS — depth + label legibility (additive; doesn't touch the
   D3 inline node/edge styling, so layout + lenses are untouched).
   1) A soft radial wash behind the canvas gives the network depth instead of a
      flat white field. 2) A white halo behind node labels (paint-order:stroke,
      which D3 never sets inline) makes every name readable over dense edges. */
#graphContainer {
  background: radial-gradient(ellipse 82% 72% at 52% 40%, #ffffff 0%, #f6f8fb 56%, #edf1f7 100%);
  position: relative;   /* anchor for the on-graph glyph legend overlay */
}
#graphContainer svg .node text { stroke: none; }   /* legibility now via the soft label pill */

/* ---- On-graph glyph legend overlay ---- */
#glyphLegendOverlay {
  position: absolute; left: 14px; bottom: 14px; z-index: 6;
  background: rgba(255,255,255,0.94); border: 1px solid #e3e8f0; border-radius: 10px;
  padding: 8px 11px 9px; max-width: 250px; box-shadow: 0 6px 18px rgba(20,30,60,0.10);
  font-family: 'Inter', sans-serif; color: #46566a;
}
#glyphLegendOverlay .glo-head { display: flex; align-items: center; gap: 8px; }
#glyphLegendOverlay .glo-title { font-weight: 700; font-size: 11.5px; color: #2b3a4d; flex: 1; }
#glyphLegendOverlay .glo-toggle { border: 0; background: none; cursor: pointer; font-size: 16px; line-height: 1; color: #94a3b8; padding: 0 2px; }
#glyphLegendOverlay.collapsed .glo-body { display: none; }
#glyphLegendOverlay .gl-grp { margin: 5px 0 0; }
#glyphLegendOverlay .gl-cap { font-size: 10px; color: #76849a; margin-bottom: 1px; }
#glyphLegendOverlay .gl-row { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; }
#glyphLegendOverlay .gl-roles, #glyphLegendOverlay .gl-tex { gap: 9px; }
#glyphLegendOverlay .gl-item { display: flex; align-items: center; gap: 3px; font-size: 10.5px; }
/* Crisp the community centroid labels too (they sit over the busiest regions). */
#graphContainer svg .comm-overlay-label,
#graphContainer svg .comm-overlay-sub {
  paint-order: stroke;
  stroke: #ffffff;
  stroke-width: 3px;
  stroke-linejoin: round;
}

/* ====================================================================
   AGENT COMMAND CENTER — every agent entry point (Run / Autopilot / Council /
   Surprise / Mission / What-If / Growth / Blind-spots / Debate / Roundtable)
   lands in #copilotPanel .center. Lay them out as one tidy 2-column toolkit
   (primary "Run" spans the top) so it reads as a command center, not a pile of
   buttons. Pure layout — no structure/JS change, so nothing can break. */
#copilotPanel .center {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-top: 9px;
}
#copilotPanel .center .btn {
  width: 100%;
  margin: 0;
  font-size: 11.5px;
  padding: 6px 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}
#copilotPanel .center #agentRunBtn { grid-column: 1 / -1; font-size: 13px; padding: 8px; }
#copilotPanel #agentChips { margin-top: 9px; }

/* The one-paragraph insight body — readable, with quiet inline emphasis and
   on-brand links matching the rest of the UI. */
#insightPanel p {
  margin: 0 0 var(--space-2);
  font-size: 14.5px;
  line-height: 1.62;
  color: var(--ink-2);
}
#insightPanel p:last-child { margin-bottom: 0; }
#insightPanel strong,
#insightPanel b { color: var(--accent-ink); font-weight: 700; }
#insightPanel em,
#insightPanel i { color: var(--ink-2); font-style: italic; }
#insightPanel a { color: var(--accent); font-weight: 600; text-decoration: none; }
#insightPanel a:hover { color: var(--accent-ink); text-decoration: underline; }
/* An optional small uppercase eyebrow / heading, if main.js emits one. */
#insightPanel h1,
#insightPanel h2,
#insightPanel h3,
#insightPanel .insight-title {
  margin: 0 0 var(--space-1);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
}

@media (max-width: 640px) {
  #insightPanel { right: 8px; left: 8px; width: auto; max-width: none; bottom: 84px; }
}

/* ====================================================================
   COMMUNITY-OUTLINES TOGGLE ROW (#hullToggle) — the checkbox in #lensPanel
   that fires LIGraph.toggleHulls(this.checked). Its <label for="hullToggle">
   wraps the checkbox + the "Community outlines" text, sitting right after the
   #backboneReadout caption. It is an inline control row (NOT one of the
   uppercase lens section labels), so it gets readable sentence-case body text
   that matches the lens controls (#nodeMetricSel / #edgeModeSel), with the
   checkbox spaced from its label and a gentle hover. On-brand; token-aligned.
   ==================================================================== */
#lensPanel label[for="hullToggle"] {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: var(--space-3) 0 0;
  /* Override the uppercase #lensPanel label treatment — this is a readable
     control row, like the other lens controls, not a section header. */
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--ink-2);
  cursor: pointer;
  transition: color 0.12s var(--ease);
}
#lensPanel label[for="hullToggle"]:hover { color: var(--accent-ink); }
#hullToggle {
  flex: 0 0 auto;
  margin: 0;
  vertical-align: middle;
  cursor: pointer;
  /* Tint the native control to the brand accent where supported. */
  accent-color: var(--accent);
}
#hullToggle:focus-visible {
  outline: 2px solid rgba(43, 108, 176, 0.45);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ====================================================================
   SAVE-STORY BUTTON (#saveStoryBtn) — the .btn.btn-default.btn-sm input in
   #networkContextCard .center that fires NetworkStory.save(). It sits beside
   the primary #storyBtn ("Tell me the story"), so it reads as the SECONDARY
   action: a quiet white/outline button matching the existing secondary
   buttons (.ego-focus / .hv-back / #storyPanel .ns-btn) rather than the brand
   fill. Token-aligned (#2b6cb0 / Inter), tactile press, full-width to stack
   neatly under the primary. modern.css loads last, so this refines the base
   .btn / .btn-default chrome without removing anything above.
   ==================================================================== */
#saveStoryBtn.btn {
  display: inline-block;
  width: 100%;
  margin-top: var(--space-2);
  padding: 8px 14px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--accent);
  background: #fff;
  border: 1px solid #cbd5e0;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), color 0.12s var(--ease),
              transform 0.08s var(--ease);
}
#saveStoryBtn.btn:hover,
#saveStoryBtn.btn:focus {
  background: var(--surface-hover);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--sh-2);
}
#saveStoryBtn.btn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }

/* ---- Organization view (clickable org name -> #orgDialog) ---- */
.org-row .org-open { cursor: pointer; color: #2b6cb0; border-bottom: 1px dotted #9bbbe0; }
.org-row .org-open:hover { background: #eaf2fb; }
#orgDialog .org-meta { margin-bottom: 6px; }
#orgDialog .org-title { display: block; font-size: 18px; font-weight: 700; color: #1f2933; }
#orgDialog .org-sub { display: block; font-size: 12.5px; color: #6b7682; margin-top: 2px; }
#orgDialog .org-canvas { width: 100%; border: 1px solid #e6e9ee; border-radius: 8px;
  background: radial-gradient(circle at 50% 40%, #fbfcfe 0%, #f1f4f8 100%); }
#orgDialog svg.bridge-svg { height: 440px; }

/* ====================================================================
   NETWORK VITALS PANEL (#vitalsPanel) — a docked side panel (mirrors
   #intelPanel / #storyPanel) that the NetworkVitals module fills with the
   network's headline vital signs. The CONTRACT classes the module emits:
     .vital-row   — one stat: a label on the left + a value on the right
     .vital-bar   — a thin track behind a stat (the gauge background)
     .vital-fill  — the accent fill inside .vital-bar (width set inline %)
     .vital-read  — a muted one-line reading / interpretation of the stat
   The panel itself is the docked shell (sticky header + scrollable body),
   styled to match #intelPanel / #storyPanel exactly: same width, slide-in,
   shadow, gradient header and hairline dividers. #vitalsBtn is the sidebar
   trigger (class="btn btn-default btn-sm" -> NetworkVitals.open()), styled
   to match the existing secondary buttons (#saveStoryBtn / .ns-btn). On-brand
   (#2b6cb0 / #f5a623, Inter); token-aligned. modern.css loads last, so this
   styles the module's emitted markup without removing anything above.
   Defensive / id-class anchored — harmless until the panel is mounted.
   ==================================================================== */

/* The docked panel shell — mirrors #intelPanel / #storyPanel. */
#vitalsPanel {
  position: fixed;
  top: 0; right: 0;
  z-index: 1100;
  width: 420px; max-width: 94vw;
  height: 100vh;
  background: var(--panel);
  box-shadow: -6px 0 28px rgba(0, 0, 0, 0.18);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  transform: translateX(105%);
  transition: transform 0.28s var(--ease);
}
/* The module shows the panel by adding .open (mirrors #intelPanel /
   #storyPanel). If it instead toggles inline display, this stays harmless. */
#vitalsPanel.open { transform: translateX(0); }

/* Header — sticky title + close, matching #intelPanel's .intel-head /
   #storyPanel's .ns-head (gradient band, hairline divider). Supports either
   the .intel-head/.intel-title/.intel-close vocabulary or a .vit-* one. */
#vitalsPanel .vit-head,
#vitalsPanel .intel-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line-soft);
  background: linear-gradient(180deg, #fbfcfe, #f4f6f9);
}
#vitalsPanel .vit-title,
#vitalsPanel .intel-title {
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-right: auto;
}
/* Optional small intro line beneath the title. */
#vitalsPanel .vit-sub {
  flex: 1 1 100%;
  margin-top: var(--space-1);
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-3);
}
#vitalsPanel .vit-sub b { color: var(--accent-ink); font-weight: 700; }
#vitalsPanel .vit-close,
#vitalsPanel .intel-close {
  flex: 0 0 auto;
  border: none;
  background: none;
  font-size: 24px;
  line-height: 1;
  color: var(--ink-3);
  cursor: pointer;
  transition: color 0.12s var(--ease);
}
#vitalsPanel .vit-close:hover,
#vitalsPanel .intel-close:hover { color: var(--ink); }

/* Scrollable body — mirrors #intelBody's rhythm. */
#vitalsPanel .vit-body,
#vitalsPanel .intel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-2) var(--space-5) calc(var(--space-5) + 8px);
  font-size: var(--fs-md);
  line-height: 1.6;
}

/* Optional grouping section inside the body (a labelled band of vitals),
   matching the .ni-section rhythm. */
#vitalsPanel .vit-section {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line-soft);
}
#vitalsPanel .vit-section:last-child { border-bottom: none; }
#vitalsPanel .vit-section > h3,
#vitalsPanel .vit-sectiontitle {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 var(--space-3);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: -0.005em;
  color: #2d3748;
}
#vitalsPanel .vit-section > h3 .vit-hint,
#vitalsPanel .vit-sectiontitle .vit-hint {
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--ink-3);
}

/* ---- CONTRACT: .vital-row — one vital sign (label + value) ---- */
.vital-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) 0 var(--space-1);
}
/* The descriptive label on the left. */
.vital-row .vital-label,
.vital-row > .vital-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink-2);
  letter-spacing: -0.005em;
}
/* The headline value on the right — tabular so figures line up. */
.vital-row .vital-value,
.vital-row > .vital-val {
  flex: 0 0 auto;
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- CONTRACT: .vital-bar — the gauge track ---- */
.vital-bar {
  position: relative;
  height: 7px;
  margin: var(--space-1) 0 var(--space-2);
  border-radius: var(--r-pill);
  background: var(--line-soft);
  overflow: hidden;
}

/* ---- CONTRACT: .vital-fill — accent fill (width set inline as %) ---- */
.vital-fill {
  display: block;
  height: 100%;
  width: 0;                         /* JS sets the real width inline as % */
  max-width: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #4f8fc7, var(--accent));
  transition: width 0.4s var(--ease);
}
/* A "watch this" variant if the module flags a vital as low/at-risk. */
.vital-fill.vital-warn {
  background: linear-gradient(90deg, #f3c272, var(--highlight));
}
.vital-row .vital-value.vital-warn,
.vital-value.vital-warn { color: #b9770f; }

/* ---- CONTRACT: .vital-read — muted one-line reading ---- */
.vital-read {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-3);
}
.vital-read b,
.vital-read strong { color: var(--ink-2); font-weight: 700; }
/* The last reading in a section shouldn't leave a trailing gap. */
.vit-section .vital-read:last-child { margin-bottom: 0; }

/* Empty state, matching .ni-empty. */
#vitalsPanel .vit-empty {
  padding: var(--space-5);
  font-size: var(--fs-base);
  font-style: italic;
  color: var(--ink-3);
}

@media (max-width: 640px) {
  #vitalsPanel { width: 100vw; max-width: 100vw; }
}

/* ---- #vitalsBtn — sidebar trigger, matches the secondary buttons
   (#saveStoryBtn / #storyPanel .ns-btn): quiet white/outline, full-width to
   stack under the primary story button, tactile press. ---- */
#vitalsBtn.btn {
  display: inline-block;
  width: 100%;
  margin-top: var(--space-2);
  padding: 8px 14px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--accent);
  background: #fff;
  border: 1px solid #cbd5e0;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), color 0.12s var(--ease),
              transform 0.08s var(--ease);
}
#vitalsBtn.btn:hover,
#vitalsBtn.btn:focus {
  background: var(--surface-hover);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--sh-2);
}
#vitalsBtn.btn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }

/* ---- Organization picker (OrgView.openOverview) ---- */
.org-pick-head { font-size: 13px; color: #000; margin: 2px 0 10px; }
.org-pick-list { display: flex; flex-direction: column; gap: 6px; max-height: 60vh; overflow-y: auto; }
.org-pick { display: flex; align-items: center; gap: 8px; padding: 9px 12px; border: 1px solid #e6e9ee;
  border-radius: 8px; cursor: pointer; background: #fff; transition: background .12s, border-color .12s; }
.org-pick:hover { background: #eaf2fb; border-color: #2b6cb0; }
.org-pick .fa-building { color: #2b6cb0; }
.org-pick .op-name { font-weight: 700; color: #000; flex: 0 0 auto; }
.org-pick .op-cnt { color: #000; font-size: 12px; }
.org-pick .op-dom { margin-left: auto; color: #46566a; font-size: 12px; font-style: italic; }
.org-back { display: inline-block; margin-bottom: 8px; font-size: 12.5px; color: #2b6cb0; text-decoration: none; }
.org-back:hover { text-decoration: underline; }
#orgDialog .org-title, #orgDialog .org-sub { color: #000; }

/* ====================================================================
   NETWORK AGENT LAYER (#agentPanel) — the ReAct agent's docked side panel,
   its streamed reasoning trace, the multi-specialist "council" grid, and the
   sidebar controls (#agentInput / #agentRunBtn / #councilBtn / #agentChips).
   window.NetworkAgent = { ask, strategy, council, _tools } renders a streamed
   trace into #agentPanel and acts on the graph live. The panel is a docked
   right-side slide-in shell that MIRRORS #intelPanel / #storyPanel / #vitalsPanel
   EXACTLY: same width, slide-in transform, shadow, gradient header band and
   hairline dividers, shown via an .open class. The trace is a vertical stream
   of .agent-step cards, each holding a muted-italic .agent-thought (tagged
   "Thought"), a monospace-ish accent .agent-action ("Action: tool(args)"),
   an .agent-observation result line, and — at the end — a prominent, readable
   .agent-answer. Newly-streamed steps fade/slide in (.agent-step--active /
   the entry animation) so the trace feels alive. The council renders a
   .council-grid of four .council-card specialist cards (title, findings list,
   an "evidence" link) plus a .council-synth synthesis paragraph. On-brand
   (#2b6cb0 accent / #f5a623 gold, Inter); token-aligned. modern.css loads
   last, so this styles the module's emitted markup WITHOUT removing anything
   above. Defensive / id-class anchored — harmless until the markup is mounted.
   ==================================================================== */

/* ---- The docked panel shell — mirrors #intelPanel / #storyPanel / #vitalsPanel. */
#agentPanel {
  position: fixed;
  top: 0; right: 0;
  z-index: 1100;
  width: 460px; max-width: 94vw;
  height: 100vh;
  background: var(--panel);
  box-shadow: -6px 0 28px rgba(0, 0, 0, 0.18);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  transform: translateX(105%);
  transition: transform 0.28s var(--ease);
}
/* The module shows the panel by adding .open (mirrors the other docked panels).
   If it instead toggles inline display, this stays harmless. */
#agentPanel.open { transform: translateX(0); }

/* Header — sticky title + intro + close, matching #intelPanel's .intel-head /
   #storyPanel's .ns-head (gradient band, hairline divider). Supports either an
   .agent-head/.agent-title/.agent-close vocabulary or the shared .intel-* one. */
#agentPanel .agent-head,
#agentPanel .intel-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line-soft);
  background: linear-gradient(180deg, #fbfcfe, #f4f6f9);
}
#agentPanel .agent-title,
#agentPanel .intel-title {
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-right: auto;
}
/* Optional small intro line beneath the title (e.g. the active goal). */
#agentPanel .agent-sub {
  flex: 1 1 100%;
  margin-top: var(--space-1);
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-3);
}
#agentPanel .agent-sub b { color: var(--accent-ink); font-weight: 700; }
#agentPanel .agent-close,
#agentPanel .intel-close {
  flex: 0 0 auto;
  border: none;
  background: none;
  font-size: 24px;
  line-height: 1;
  color: var(--ink-3);
  cursor: pointer;
  transition: color 0.12s var(--ease);
}
#agentPanel .agent-close:hover,
#agentPanel .intel-close:hover { color: var(--ink); }

/* Scrollable body — mirrors #intelBody / .vit-body rhythm. The streamed trace
   and the council grid both live here. */
#agentPanel .agent-body,
#agentPanel .intel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-3) var(--space-5) calc(var(--space-5) + 8px);
  font-size: var(--fs-md);
  line-height: 1.6;
}

/* Empty / thinking states, matching .ni-empty / .vit-empty. */
#agentPanel .agent-empty {
  padding: var(--space-5);
  font-size: var(--fs-base);
  font-style: italic;
  color: var(--ink-3);
}

/* ====================================================================
   ReAct TRACE — a vertical stream of step cards. Each .agent-step is a calm
   card holding a Thought / Action / Observation; the final answer is the
   prominent .agent-answer. New steps fade + slide in so the stream feels live.
   ==================================================================== */

/* One reasoning step — a calm card with an accent left rail. */
.agent-step {
  position: relative;
  padding: var(--space-3) var(--space-3) var(--space-3) calc(var(--space-3) + 2px);
  margin: var(--space-2) 0;
  border: 1px solid var(--line-soft);
  border-left: 3px solid #cdd8e3;
  border-radius: var(--r-md);
  background: var(--surface-1);
  transition: box-shadow 0.15s var(--ease), border-color 0.15s var(--ease);
}
.agent-step:hover { box-shadow: var(--sh-1); border-left-color: var(--accent); }

/* The streamed-in entry animation — every freshly-appended step plays it once,
   so the trace visibly "thinks". The module can also add .agent-step--active /
   .agent-step.active to the latest step for a stronger, lingering accent. */
@keyframes agentStepIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.agent-step { animation: agentStepIn 0.32s var(--ease) both; }
.agent-step--active,
.agent-step.active {
  border-left-color: var(--accent);
  box-shadow: var(--sh-2);
  background: #fff;
}

/* "Thought" — muted, italic reasoning, prefixed with a small uppercase tag. */
.agent-thought {
  font-size: var(--fs-base);
  font-style: italic;
  line-height: 1.6;
  color: var(--ink-3);
}
.agent-thought::before {
  content: "Thought";
  display: inline-block;
  margin-right: 7px;
  font-style: normal;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--fs-xs);
  color: #9aa3ad;
  vertical-align: 1px;
}

/* "Action: tool(args)" — monospace-ish, brand accent, reads as a tool call.
   The module may emit the literal "Action:" text, or rely on this ::before. */
.agent-action {
  margin-top: var(--space-2);
  font-family: 'SFMono-Regular', 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--accent-ink);
  background: var(--accent-soft);
  border: 1px solid rgba(43, 108, 176, 0.18);
  border-radius: var(--r-sm);
  padding: 6px 9px;
  overflow-wrap: break-word;
  word-break: break-word;
}
.agent-action::before {
  content: "Action ";
  font-family: 'Inter', system-ui, sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: var(--fs-xs);
  color: var(--accent);
  margin-right: 6px;
}
/* If the module already writes its own "Action:" label, suppress ours. */
.agent-action.has-label::before { content: none; }
.agent-action b,
.agent-action .agent-tool { color: var(--accent); font-weight: 700; }
.agent-action .agent-args { color: var(--ink-2); }

/* "Observation" — the result line returned by the tool. Quiet body text with a
   small uppercase tag so the reader tracks the loop's third beat. */
.agent-observation {
  margin-top: var(--space-2);
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-2);
}
.agent-observation::before {
  content: "Observation";
  display: inline-block;
  margin-right: 7px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--fs-xs);
  color: #9aa3ad;
  vertical-align: 1px;
}
.agent-observation.has-label::before { content: none; }
.agent-observation b,
.agent-observation strong { color: var(--accent-ink); font-weight: 700; }
/* Clickable cited people inside an observation reuse the .ask-chip pill. */
.agent-observation .ask-chip,
.agent-step .ask-chip { margin: 1px 2px; vertical-align: 1px; }

/* ---- THE FINAL ANSWER — the synthesized conclusion: prominent + readable.
   A bolder card with the brand accent rail, larger body text, clear emphasis.
   This is the thing the reader is meant to walk away with. ---- */
.agent-answer {
  margin: var(--space-4) 0 var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-4) calc(var(--space-4) + 2px);
  font-size: 15px;
  line-height: 1.66;
  color: var(--ink);
  background: linear-gradient(180deg, #ffffff, var(--surface-1));
  border: 1px solid var(--line-soft);
  border-left: 4px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  box-shadow: var(--sh-1);
  overflow-wrap: break-word;
  word-break: break-word;
  animation: agentStepIn 0.34s var(--ease) both;
}
/* A small uppercase eyebrow above the answer, if the module emits one. */
.agent-answer .agent-answer-label,
.agent-answer > h3:first-child {
  display: block;
  margin: 0 0 var(--space-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}
.agent-answer p { margin: 0 0 var(--space-2); }
.agent-answer p:last-child { margin-bottom: 0; }
.agent-answer strong, .agent-answer b { color: var(--accent-ink); font-weight: 700; }
.agent-answer em, .agent-answer i { font-style: italic; color: var(--ink-2); }
.agent-answer a { color: var(--accent); font-weight: 600; text-decoration: none; }
.agent-answer a:hover { color: var(--accent-ink); text-decoration: underline; }
.agent-answer ul, .agent-answer ol { margin: var(--space-1) 0 var(--space-2); padding-left: 18px; }
.agent-answer li { margin: 0 0 var(--space-1); line-height: 1.55; }

/* ====================================================================
   COUNCIL — four specialist cards in a responsive grid + a synthesis para.
   Rendered by NetworkAgent.council() inside #agentPanel's body.
   ==================================================================== */
.council-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin: var(--space-2) 0 var(--space-3);
}
/* On a narrow panel, stack the cards. */
@media (max-width: 520px) {
  .council-grid { grid-template-columns: 1fr; }
}

/* One specialist card — title, findings list, an "evidence" link. */
.council-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--space-3);
  border: 1px solid var(--line-soft);
  border-top: 3px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--surface-1);
  box-shadow: var(--sh-1);
  transition: box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
  animation: agentStepIn 0.32s var(--ease) both;
}
.council-card:hover { box-shadow: var(--sh-2); }
/* Each of the four specialists gets a distinct accent top-rail so the grid
   reads as four distinct viewpoints (cycled; defensive if fewer/more cards). */
.council-card:nth-child(4n+1) { border-top-color: var(--accent); }
.council-card:nth-child(4n+2) { border-top-color: var(--highlight); }
.council-card:nth-child(4n+3) { border-top-color: #38a169; }
.council-card:nth-child(4n+4) { border-top-color: #805ad5; }

.council-card .council-title,
.council-card > h3,
.council-card > h4 {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: -0.005em;
  color: #1a202c;
  line-height: 1.3;
}
/* Optional small role/eyebrow under the title. */
.council-card .council-role {
  margin: -2px 0 var(--space-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-3);
}

/* The findings list — quiet, scannable bullets. */
.council-card .council-findings,
.council-card ul {
  list-style: none;
  margin: 0 0 var(--space-2);
  padding: 0;
  flex: 1 1 auto;
}
.council-card .council-findings li,
.council-card ul li {
  position: relative;
  padding-left: 14px;
  margin: 0 0 var(--space-1);
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--ink-2);
}
.council-card .council-findings li::before,
.council-card ul li::before {
  content: "";
  position: absolute;
  left: 2px; top: 8px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
.council-card li b, .council-card li strong { color: var(--accent-ink); font-weight: 700; }

/* The "evidence" link — a quiet, on-brand footer action that focuses/reveals
   the supporting nodes on the graph. */
.council-card .council-evidence,
.council-card a.council-evidence {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: auto;
  align-self: flex-start;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.12s var(--ease);
}
.council-card .council-evidence:hover { color: var(--accent-ink); text-decoration: underline; }
.council-card .council-evidence::before {
  content: "\f0ea"; /* fa-share-square — matches the FontAwesome already in use */
  font-family: 'FontAwesome', 'Font Awesome 5 Free';
  font-weight: 900;
  font-size: 11px;
  color: var(--accent);
}

/* The synthesis paragraph — the council's combined verdict, styled like the
   prominent final answer so it reads as the conclusion of the four cards. */
.council-synth {
  margin: var(--space-3) 0 var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-4) calc(var(--space-4) + 2px);
  font-size: 15px;
  line-height: 1.66;
  color: var(--ink);
  background: linear-gradient(180deg, #fffdf8, #fff8ec);
  border: 1px solid #f3dcb0;
  border-left: 4px solid var(--highlight);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  box-shadow: var(--sh-1);
  animation: agentStepIn 0.34s var(--ease) both;
}
.council-synth .council-synth-label,
.council-synth > h3:first-child {
  display: block;
  margin: 0 0 var(--space-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #b9770f;
}
.council-synth p { margin: 0 0 var(--space-2); }
.council-synth p:last-child { margin-bottom: 0; }
.council-synth strong, .council-synth b { color: var(--accent-ink); font-weight: 700; }
.council-synth a { color: var(--accent); font-weight: 600; text-decoration: none; }
.council-synth a:hover { color: var(--accent-ink); text-decoration: underline; }

/* ====================================================================
   SIDEBAR AGENT CONTROLS — #agentInput (full-width field), #agentRunBtn
   (primary, matches .btn-info / #askBtn), #councilBtn (secondary outline,
   matches #vitalsBtn / #saveStoryBtn), and #agentChips .agent-chip (clickable
   pills that wrap, matching the .sq-chip / #suggestedQs starters). On-brand;
   token-aligned. Lives in the sidebar like the other control groups.
   ==================================================================== */

/* Optional wrapper section — same comfortable rhythm as #askPanel / #lensPanel. */
#agentControls,
#agentPanelControls {
  padding: var(--space-3) 0;
  font-family: 'Inter', system-ui, sans-serif;
}
#agentControls label,
#agentPanelControls label {
  font-weight: 600;
  color: var(--ink-3);
  text-transform: uppercase;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  margin-bottom: var(--space-2);
}

/* The goal input — full-width, padded, rounded; mirrors #askInput / #nodeSearch
   (incl. the on-brand focus ring). */
#agentInput {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 10px;
  font-size: var(--fs-base);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  border: 1px solid #cbd5e0;
  border-radius: 7px;
  outline: none;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
#agentInput::placeholder { color: var(--ink-3); }
#agentInput:hover { border-color: #b6c2d2; }
#agentInput:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.14);
}

/* #agentRunBtn — the primary action (run the agent). Matches .btn-info /
   #askBtn: brand fill, settled radius, soft shadow, tactile press. */
#agentRunBtn {
  display: inline-block;
  margin-top: var(--space-2);
  padding: 8px 16px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
}
#agentRunBtn:hover, #agentRunBtn:focus {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  box-shadow: var(--sh-2);
}
#agentRunBtn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }
#agentRunBtn:disabled,
#agentRunBtn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* #councilBtn — the SECONDARY action (convene the specialist council). Matches
   #vitalsBtn / #saveStoryBtn: quiet white/outline, sits beside #agentRunBtn. */
#councilBtn {
  display: inline-block;
  margin-top: var(--space-2);
  margin-left: 7px;
  padding: 8px 16px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--accent);
  background: #fff;
  border: 1px solid #cbd5e0;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), color 0.12s var(--ease),
              transform 0.08s var(--ease);
}
#councilBtn:hover, #councilBtn:focus {
  background: var(--surface-hover);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--sh-2);
}
#councilBtn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }
#councilBtn:disabled,
#councilBtn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* #agentChips — clickable starter pills that wrap, matching #suggestedQs /
   .sq-chip. Clicking a chip fills #agentInput and/or runs the goal. */
#agentChips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: var(--space-2) 0;
}
#agentChips:empty { display: none; }
#agentChips .agent-chip,
#agentChips button,
#agentChips a {
  display: inline-block;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: #5a6675;
  background: rgba(226, 232, 240, 0.7);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  padding: 3px 10px;
  line-height: 1.5;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease),
              border-color 0.12s var(--ease);
}
#agentChips .agent-chip:hover,
#agentChips button:hover,
#agentChips a:hover {
  background: var(--accent-soft);
  border-color: rgba(43, 108, 176, 0.25);
  color: var(--accent-ink);
}

@media (max-width: 640px) {
  #agentPanel { width: 100vw; max-width: 100vw; }
}

/* ====================================================================
   SEMANTIC PANEL (#semanticPanel) — the in-browser sentence-embedding
   controls in the sidebar. Free, local, no key. It holds a full-width
   meaning-search field (#semanticSearch), a primary Search button (#semBtn),
   a clickable result list (#semanticResults -> .sem-row rows: name + a faint
   similarity score), a layout toggle (#layoutToggle, with a clear "on" state
   when the semantic layout is active — e.g. .is-on), a content-based intros
   button (#introsBtn) whose suggestions reuse the same result-row look, and a
   small model-loading / embedding progress line (#embedStatus) with a thin
   progress bar (.embed-bar / .embed-fill).

   This block reuses the established patterns: #semanticSearch mirrors
   #askInput / #agentInput exactly; #semBtn matches the .btn-info / #askBtn
   primary action; #layoutToggle / #introsBtn match the secondary outline
   buttons (#vitalsBtn / #councilBtn); the result rows reuse the .ni-person /
   .ask-chip vocabulary; and the progress bar mirrors .ask-progress / .ni-bar.
   On-brand (#2b6cb0 accent / #f5a623 gold, Inter); token-aligned. modern.css
   loads last, so this refines without removing anything above. Defensive /
   id-class anchored — harmless until the markup is mounted.
   ==================================================================== */

/* Panel wrapper — same comfortable sidebar rhythm as #askPanel / #lensPanel. */
#semanticPanel {
  padding: var(--space-3) 0;
  font-family: 'Inter', system-ui, sans-serif;
}
#semanticPanel > label {
  font-weight: 600;
  color: var(--ink-3);
  text-transform: uppercase;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  margin-bottom: var(--space-2);
}

/* The meaning-search field — full-width, padded, rounded; mirrors #askInput /
   #agentInput exactly, incl. the on-brand focus ring. */
#semanticSearch {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 10px;
  font-size: var(--fs-base);
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--ink);
  border: 1px solid #cbd5e0;
  border-radius: 7px;
  outline: none;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
#semanticSearch::placeholder { color: var(--ink-3); }
#semanticSearch:hover { border-color: #b6c2d2; }
#semanticSearch:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.14);
}

/* #semBtn — the primary action (run the semantic search). Matches .btn-info /
   #askBtn: brand fill, settled radius, soft shadow, tactile press. The base
   markup is a .btn.btn-info.btn-sm, so this refines that chrome on-system. */
#semBtn,
#semBtn.btn {
  display: inline-block;
  margin-top: var(--space-2);
  padding: 8px 16px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), transform 0.08s var(--ease);
}
#semBtn:hover, #semBtn:focus {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  color: #fff;
  box-shadow: var(--sh-2);
}
#semBtn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }
#semBtn:disabled,
#semBtn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* #layoutToggle / #introsBtn — the SECONDARY actions. They match the existing
   secondary outline buttons (#vitalsBtn / #councilBtn / #saveStoryBtn): quiet
   white/outline chrome, settled radius, soft shadow, tactile press. The base
   markup is .btn.btn-default.btn-sm, so this refines that on-system. They sit
   in a .center row together, so #introsBtn gets a little left gap. */
#layoutToggle,
#layoutToggle.btn,
#introsBtn,
#introsBtn.btn {
  display: inline-block;
  margin-top: var(--space-2);
  padding: 8px 14px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: 'Inter', system-ui, sans-serif;
  color: var(--accent);
  background: #fff;
  border: 1px solid #cbd5e0;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              box-shadow 0.15s var(--ease), color 0.12s var(--ease),
              transform 0.08s var(--ease);
}
#introsBtn,
#introsBtn.btn { margin-left: 7px; }
#layoutToggle:hover, #layoutToggle:focus,
#introsBtn:hover, #introsBtn:focus {
  background: var(--surface-hover);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--sh-2);
}
#layoutToggle:active,
#introsBtn:active { transform: translateY(0.5px); box-shadow: var(--sh-1); }
#layoutToggle:disabled, #layoutToggle[disabled],
#introsBtn:disabled, #introsBtn[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* CLEAR "ON" STATE for #layoutToggle when the semantic layout is active. The
   JS may flag it with .is-on (per the contract), .active, or aria-pressed —
   all three flip it to a filled brand-blue "engaged" toggle so the reader can
   see the semantic layout is live at a glance. */
#layoutToggle.is-on,
#layoutToggle.active,
#layoutToggle[aria-pressed="true"],
#layoutToggle.btn.is-on,
#layoutToggle.btn.active {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: inset 0 1px 2px rgba(16, 24, 40, 0.18), var(--sh-1);
}
#layoutToggle.is-on:hover, #layoutToggle.is-on:focus,
#layoutToggle.active:hover, #layoutToggle.active:focus,
#layoutToggle[aria-pressed="true"]:hover,
#layoutToggle[aria-pressed="true"]:focus {
  color: #fff;
  background: var(--accent-ink);
  border-color: var(--accent-ink);
}

/* ---- #embedStatus — the model-loading / embedding progress line + bar.
   A small muted caption with a thin progress track (.embed-bar) and accent
   fill (.embed-fill, width set inline as %). Mirrors .ask-progress / .ni-bar.
   Collapses when empty so it never leaves a stray gap. ---- */
#embedStatus {
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink-3);
}
#embedStatus:empty { display: none; }
#embedStatus b,
#embedStatus strong { color: var(--ink-2); font-weight: 700; }
/* The thin progress track. */
.embed-bar {
  position: relative;
  height: 6px;
  margin-top: 6px;
  border-radius: var(--r-pill);
  background: var(--line-soft);
  overflow: hidden;
}
/* The accent fill — width set inline by the JS as a %. */
.embed-fill {
  display: block;
  height: 100%;
  width: 0;
  max-width: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #4f8fc7, var(--accent));
  transition: width 0.25s var(--ease);
}
/* When loading is in flight, a subtle "done" glint at completion reuses gold. */
.embed-fill.is-done { background: linear-gradient(90deg, var(--accent), var(--highlight)); }

/* ====================================================================
   RESULT LISTS — #semanticResults (search hits) and the content-based intros
   list both render readable, clickable rows. They reuse the .ni-person /
   .ask-chip vocabulary: a calm row that lights up on hover, a name on the
   left, and a faint similarity score on the right. Styled for a dedicated
   .sem-row class AND for bare children, so they look right however the JS
   wires the rows in. Collapse when empty.
   ==================================================================== */
#semanticResults,
#semanticResults .sem-list,
#introsList,
.sem-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: var(--space-2);
}
#semanticResults:empty { display: none; }

/* One clickable result row — name + faint score. Calm at rest, gentle wash on
   hover (matches .ni-person), pointer cursor since it focuses the node. */
#semanticResults .sem-row,
#introsList .sem-row,
.sem-row,
#semanticResults > a,
#semanticResults > div:not(.sem-list):not([id]) {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 7px 9px;
  border-radius: var(--r-md);
  font-size: var(--fs-base);
  line-height: 1.4;
  color: var(--ink-2);
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s var(--ease);
}
#semanticResults .sem-row:hover,
#introsList .sem-row:hover,
.sem-row:hover,
#semanticResults > a:hover,
#semanticResults > div:not(.sem-list):not([id]):hover {
  background: var(--surface-hover);
}

/* The person's name — the primary, readable label. */
.sem-row .sem-name,
.sem-row > .name,
#semanticResults > a .sem-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.005em;
}
/* An optional quiet secondary line (headline / why) under the name. */
.sem-row .sem-why,
.sem-row .sem-sub {
  display: block;
  width: 100%;
  margin-top: 2px;
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--ink-3);
  white-space: normal;
}

/* The faint similarity score on the right — a small, tabular pill so the
   figures line up and stay legible without competing with the name. */
.sem-row .sem-score,
.sem-row > .score {
  flex: 0 0 auto;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-pill);
  padding: 1px 8px;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---- CONTENT-BASED INTROS — the #introsBtn suggestions reuse the result-row
   look but read as A <-> B pairs across communities. The pair rows render in
   #introsList (or appended into #semanticResults). They get the warm gold rail
   of an "opportunity" (matching .ni-intro / the surprise-card accent). ---- */
#introsList {
  margin-top: var(--space-2);
}
#introsList:empty { display: none; }
.sem-row.sem-intro,
#introsList .sem-row {
  align-items: center;
  border-left: 3px solid var(--highlight);
  background: #fffdf8;
  margin-bottom: 4px;
}
#introsList .sem-row:hover,
.sem-row.sem-intro:hover { background: #fff8ec; }
/* The two names of the intro pair, with a gold connector between them. */
.sem-row .sem-pair {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
  line-height: 1.4;
}
.sem-row .sem-pair a { color: var(--accent); text-decoration: none; }
.sem-row .sem-pair a:hover { color: var(--accent-ink); text-decoration: underline; }
.sem-row .sem-arrow { color: var(--highlight); padding: 0 5px; font-weight: 700; }

/* A small empty / no-results note, matching .ni-empty. */
#semanticResults .sem-empty,
#introsList .sem-empty,
.sem-empty {
  padding: var(--space-2) var(--space-1);
  font-size: var(--fs-sm);
  font-style: italic;
  color: var(--ink-3);
}

/* ---- Human-centered story: prominent, user-paced current insight ---- */
.ns-current {
  margin: 12px 0; padding: 16px 18px; border-radius: 12px;
  background: #ffffff; border: 1px solid #e4e9f0; border-left: 4px solid #2b6cb0;
  box-shadow: 0 2px 14px rgba(20,40,80,0.06);
}
.ns-currtitle { font-size: 17px; font-weight: 700; color: #13447e; margin-bottom: 8px; letter-spacing: -0.01em; }
.ns-currfinding { font-size: 15px; line-height: 1.65; color: #1a202c; }
.ns-currfinding b { color: #13447e; }
.ns-currhow { margin-top: 10px; font-size: 12px; color: #6b7682; }
.ns-currhow .ns-howlabel { text-transform: uppercase; letter-spacing: 0.04em; font-size: 10px; color: #9aa3af; }
.ns-currhint { font-size: 14px; color: #46566a; padding: 6px 0; }
.ns-controls { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; }
.ns-controls2 { margin-top: 2px; gap: 8px; flex-wrap: wrap; }
.ns-progress { flex: 1 1 auto; text-align: center; font-size: 12px; font-weight: 600; color: #5a6675; font-variant-numeric: tabular-nums; }
.ns-btn.ns-primary { background: #2b6cb0; color: #fff; border-color: #2b6cb0; }
.ns-btn.ns-primary:hover { background: #245a96; }
.ns-listwrap { margin-top: 14px; }
.ns-listwrap > summary { cursor: pointer; font-size: 12px; color: #6b7682; padding: 4px 0; }
.ns-listwrap .ns-beats { margin-top: 8px; }

/* ====================================================================
   SEMANTIC LAYOUT — on-graph caption + cluster labels.

   When LIGraph.setLayout('semantic') is active, the same nodes/edges are
   re-placed by a 2-D projection of their text embeddings, so people who READ
   similarly sit together regardless of who they know. Two pieces of on-graph
   chrome explain that re-arrangement to the reader, and BOTH are meaningful
   ONLY in the semantic layout:

     • #semCaption  — a small, fixed, readable caption card near the legends
       (bottom-left, above #roleLegend / #edgeLegend). It is populated only in
       semantic layout (and hidden when empty), so it tells the reader WHY the
       cloud is arranged this way ("grouped by what they do, not who they know"
       + the cluster names from Embeddings.clusterNames).

     • .sem-overlay-label / .sem-labels text — the on-graph labels naming each
       semantic cluster, mirroring the structural layout's .comm-overlay-label
       look (black, white halo) so a cluster name stays legible over the dense
       edge field. They reuse the community-label treatment exactly.

   On-brand (#2b6cb0 / #f5a623, Inter); token-aligned. modern.css loads last,
   so this refines without removing anything above. Defensive / id-class
   anchored — harmless until the semantic layout mounts the markup.
   ==================================================================== */

/* ---- #semCaption — the translucent caption card near the legends ----
   A small, ~260px fixed card sitting bottom-left ABOVE #roleLegend (which is at
   bottom:84px) / #edgeLegend (bottom:14px), so the three stack without
   overlapping. Quiet, blurred, translucent at rest (it explains, it doesn't
   shout), and it lifts to near-solid on hover for a comfortable read. It is
   populated only in the semantic layout, so :empty collapses it entirely — it
   never leaves a stray card in the structural layout. */
#semCaption {
  position: fixed;
  left: 14px;
  bottom: 154px;                /* clears #roleLegend (bottom:84px, ~60px tall) */
  z-index: 5;
  width: 260px;
  max-width: calc(100vw - 28px);
  box-sizing: border-box;
  padding: 11px 13px 12px;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-2);
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid rgba(203, 213, 224, 0.55);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  opacity: 0.92;
  transition: opacity 0.3s var(--ease);
  pointer-events: none;         /* never intercept graph drag/hover */
}
#semCaption:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.95);
  pointer-events: auto;         /* but allow text selection / chip clicks on hover */
}
/* Collapse entirely when not populated (i.e. outside the semantic layout). */
#semCaption:empty { display: none; }

/* A small uppercase eyebrow / heading, if the JS emits one. */
#semCaption h1, #semCaption h2, #semCaption h3,
#semCaption .sem-caption-title {
  display: block;
  margin: 0 0 var(--space-1);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}
#semCaption p { margin: 0 0 var(--space-1); }
#semCaption p:last-child { margin-bottom: 0; }
#semCaption strong, #semCaption b { color: var(--accent-ink); font-weight: 700; }
#semCaption em, #semCaption i { color: var(--ink-2); font-style: italic; }
/* Optional inline cluster-name chips (from Embeddings.clusterNames). */
#semCaption .sem-cap-chip {
  display: inline-block;
  margin: 2px 4px 0 0;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-pill);
  padding: 1px 8px;
  line-height: 1.6;
  white-space: nowrap;
}

@media (max-width: 640px) {
  #semCaption { left: 8px; width: 240px; bottom: 146px; }
}

/* ---- .sem-overlay-label / .sem-labels text — on-graph cluster names ----
   The semantic layout's per-cluster labels reuse the structural community
   label's readable treatment: black text with a strong white halo (paint-order
   stroke) so the name reads over ANY backdrop / the dense edge field. We mirror
   the .comm-overlay-label rule (lines above) so the two layouts look cohesive,
   and we also cover the case where the labels live in a .sem-labels <g> as bare
   <text>. Pointer-events stay off (these are decorative overlays). */
.sem-overlay-label,
.sem-labels text {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 20px;
  font-weight: 700;
  fill: #000000;                /* black for maximum readability */
  fill-opacity: 1;
  stroke: #ffffff;
  stroke-width: 5px;
  stroke-opacity: 0.95;
  paint-order: stroke;
  stroke-linejoin: round;
  pointer-events: none;
}
/* An optional smaller sub-line (e.g. the cluster's commonality), matching the
   structural .comm-overlay-sub treatment. */
.sem-overlay-sub,
.sem-labels text.sem-overlay-sub {
  font-size: 12px;
  font-weight: 600;
  fill: #1a1a1a;                /* near-black */
  fill-opacity: 1;
  stroke: #ffffff;
  stroke-width: 3.5px;
  stroke-opacity: 0.95;
  paint-order: stroke;
  stroke-linejoin: round;
}
/* If the JS draws a translucent pill behind a semantic label group (mirroring
   .comm-overlay-bg), style it the same calm, readable way. */
.sem-overlay-bg {
  fill: rgba(255, 255, 255, 0.62);
  stroke: rgba(203, 213, 224, 0.5);
  stroke-width: 1px;
  pointer-events: none;
}

/* ====================================================================
   PREDICTED-LINK OVERLAY (.predicted-links) — DASHED edges that
   window.LIGraph.showPredictedLinks(pairs) draws between non-connected
   node pairs (from GraphEmbed.linkPredict / predictFor), and that
   clearPredicted() removes. The JS owns the actual geometry AND the line
   appearance (stroke colour + dash pattern are set inline per the
   contract), so CSS here must NOT override stroke/dasharray — it only:
     (a) makes the overlay non-interactive so the dashed lines never steal
         hovers/clicks from the real nodes & links beneath them, and
     (b) lifts the dashes visually so they read as a distinct "suggested,
         not-yet-real" layer over the dense edge field (soft glow + a
         subtle draw-in so new predictions are noticeable but calm).
   modern.css loads last, so this refines without removing anything above.
   Defensive / class-anchored — harmless until the overlay is mounted.
   ==================================================================== */
.predicted-links path {
  /* Never hit-test: the dashed suggestions sit ON TOP of the graph, but all
     hovers/clicks must pass through to the real nodes & edges underneath. */
  pointer-events: none;
  /* Round caps/joins so the inline-set dash pattern reads as clean stitches
     rather than hard rectangles. Does not touch stroke colour or dasharray
     (the JS sets those inline, per the contract). */
  stroke-linecap: round;
  stroke-linejoin: round;
  /* A soft halo lifts the dashes off the dense edge field so a predicted link
     reads clearly as a distinct, "suggested" layer on any backdrop. */
  filter: drop-shadow(0 0 2px rgba(245, 166, 35, 0.55));
  /* Gentle draw-in so freshly shown predictions are noticeable but never loud
     (honoured only by browsers that support animating SVG geometry; harmless
     elsewhere — the line simply appears). */
  animation: predLinkIn 0.45s var(--ease) both;
}
@keyframes predLinkIn { from { opacity: 0; } to { opacity: 1; } }

/* If the JS labels the overlay group itself (g.predicted-links), keep the
   group non-interactive too — belt-and-braces with the per-path rule above. */
.predicted-links { pointer-events: none; }

/* ---- Optional legend hint for the predicted-links layer ----
   A tiny dashed-line key the markup MAY mount (#predLegend) so a reader knows
   the dashed overlay edges are PREDICTED, not real connections. Mirrors the
   restraint of #edgeLegend / #roleLegend: a faint, translucent, blurred card
   that never hit-tests the graph and brightens slightly on hover. The dash
   swatch uses the warm accent so it keys the same "suggested" feel as the
   overlay's glow above. Defensive / id-anchored — harmless until mounted. */
#predLegend {
  position: fixed;
  left: 14px;
  bottom: 154px;                /* stacks above #roleLegend (bottom:84px) */
  z-index: 5;
  width: 180px;
  padding: 9px 11px 10px;
  font-family: 'Inter', system-ui, sans-serif;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(203, 213, 224, 0.5);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0.72;
  transition: opacity 0.3s var(--ease);
  pointer-events: none;
}
#predLegend:hover { opacity: 0.97; }
#predLegend .pl-label {
  display: block;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #8a929c;
  margin-bottom: 6px;
  text-align: center;
}
/* One legend entry: a short dashed swatch + a quiet caption. */
#predLegend .pl-row {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 10px;
  line-height: 1.5;
  color: #647280;
}
/* The dashed-line swatch — a thin warm-accent dashed rule mirroring the
   overlay so the key matches the on-graph edges. */
#predLegend .pl-dash {
  flex: 0 0 26px;
  height: 0;
  border-top: 2px dashed #f5a623;
}

@media (max-width: 640px) {
  #predLegend { width: 150px; left: 8px; bottom: 142px; }
}

/* ====================================================================
   SIDEBAR MODULE CARDS — purely VISUAL modularization of the flat,
   all-visible sidebar. The sidebar stays exactly as-is structurally:
   every panel is rendered, nothing is collapsed, moved, hidden, or wrapped
   in an accordion. This block simply makes each <li.dropdown.borderbottom>
   panel read as a clean, consistent MODULE CARD, gives each panel's title a
   consistent MODULE HEADER, and turns the EXPLORE / ANALYZE / CUSTOMIZE
   group labels into quiet section dividers.

   EVERYTHING is scoped under the sidebar (#slidebar-white .nav.navbar-nav)
   so the graph, dialogs, docked panels and overlays are never touched.
   modern.css loads last, so this refines — never removes — the base
   simple-sidebar.css chrome (the heavy bottom-border list look, the link
   padding/colour) above. On-brand (#2b6cb0 / Inter); token-aligned.
   ==================================================================== */

/* ---- The card surface ----
   Each panel <li.dropdown.borderbottom> becomes a discrete module card:
   a white surface, a hairline border, soft 10px corners, comfortable
   margins and a gentle hover lift. We DROP the old heavy bottom-border /
   float list chrome here (scoped to the sidebar only, so .borderbottom
   elsewhere is unaffected) so the cards read cleanly. */
#slidebar-white .nav.navbar-nav > li.dropdown {
  float: none;
  display: block;
  list-style: none;
  margin: 8px 10px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  overflow: hidden;
  transition: box-shadow 0.18s var(--ease), border-color 0.18s var(--ease),
              transform 0.18s var(--ease);
}
/* Neutralise the inherited heavy hairline so the card border is the only
   chrome (scoped to the sidebar — the global .borderbottom is untouched). */
#slidebar-white .nav.navbar-nav > li.dropdown.borderbottom {
  border-bottom: 1px solid var(--line-soft) !important;
}
/* A very subtle lift on hover so the cards feel tactile without shouting. */
#slidebar-white .nav.navbar-nav > li.dropdown:hover {
  box-shadow: var(--sh-2);
  border-color: var(--line);
  transform: translateY(-1px);
}

/* ---- The <a> wrapper: strip all link chrome ----
   Each panel is wrapped in <a href="#"> for legacy markup reasons; it must
   read as a plain block container, not a link. Remove underline/colour/
   background, make it a block, and give the card its consistent inner pad
   (overriding simple-sidebar.css's 12px top/bottom link padding). */
#slidebar-white .nav.navbar-nav > li.dropdown > a,
#slidebar-white .nav.navbar-nav > li.dropdown > a:hover,
#slidebar-white .nav.navbar-nav > li.dropdown > a:focus,
#slidebar-white .nav.navbar-nav > li.dropdown > a:active {
  display: block;
  padding: 12px 14px;
  color: var(--ink);
  background: none !important;
  text-decoration: none !important;
  cursor: default;
  outline: none;
}

/* ---- The MODULE HEADER ----
   The first <label> inside each panel is the panel title. Give every panel a
   consistent, calm header: semibold, slightly tracked, secondary ink, a small
   gap beneath. The existing emoji/icon prefixes are preserved (they live in
   the label text). Scoped to the FIRST label of each panel so per-control
   labels inside (e.g. the Lens "Color nodes by" selects) keep their own look.
   :first-of-type covers the common case; we also target the immediate-child
   label so panels whose first element is the label are always caught. */
#slidebar-white .nav.navbar-nav > li.dropdown > a > div > label:first-of-type,
#slidebar-white .nav.navbar-nav > li.dropdown > a > div > label:first-child {
  display: block;
  margin: 0 0 6px;
  padding: 0;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.35;
  color: var(--ink-2);
  text-transform: none;
}
/* The filter panels (community / location / surprise / history) already get an
   uppercase treatment from the polish pass above; keep them as the consistent
   module header too — re-assert the header rhythm so they sit level with the
   other cards (these selectors are id-anchored, so they refine, not fight). */
#slidebar-white #communityFiltering > label:first-of-type,
#slidebar-white #locationFiltering > label:first-of-type,
#slidebar-white #organizationFiltering > label:first-of-type,
#slidebar-white #surpriseFiltering > label:first-of-type,
#slidebar-white #historyFiltering > label:first-of-type {
  display: block;
  margin: 0 0 6px;
  font-size: 12.5px;
  letter-spacing: 0.02em;
  color: var(--ink-2);
}

/* The two panels whose title is a <summary> (methods, BYO data) get the same
   module-header weight/colour so every card's title reads consistently, while
   keeping their disclosure-triangle affordance. */
#slidebar-white .nav.navbar-nav > li.dropdown > a > details > summary {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-2);
}

/* ---- Airy, consistent inner spacing ----
   Keep all the existing controls (inputs / buttons / chips / checkbox filter
   rows) exactly as styled; just make sure the breathing room inside each card
   is even. The legacy <br> after most titles adds a hard line break; soften it
   to a small, consistent gap so spacing reads from the card, not the markup. */
#slidebar-white .nav.navbar-nav > li.dropdown > a > div > br {
  display: block;
  content: "";
  margin-top: 2px;
}
/* The orientation / helper hint sits just under the header in most cards. */
#slidebar-white .nav.navbar-nav > li.dropdown .surprise-hint {
  margin: 2px 0 9px;
}
/* The #quickStart card carries its own accent-tinted styling (above); inside
   the module card, let that styling show edge-to-edge by removing the inner
   pad's double margin so it reads as one calm intro block. */
#slidebar-white .nav.navbar-nav > li.dropdown > a > #quickStart {
  margin: 0;
}

/* ---- Group / section dividers ----
   The EXPLORE / ANALYZE / CUSTOMIZE labels become quiet section dividers:
   small uppercase, muted, with extra top space and a hairline above so each
   group reads as a distinct band between the cards. (The base .nav-group rule
   above already sets the type; here we tune the rhythm to the card margins and
   align the hairline with the card gutter.) Scoped to the sidebar. */
#slidebar-white .nav.navbar-nav > li.nav-group {
  float: none;
  display: block;
  margin: 18px 10px 4px;
  padding: 14px 4px 0;
  border-top: 1px solid var(--line-soft);
  background: none;
}
#slidebar-white .nav.navbar-nav > li.nav-group > span {
  display: block;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  line-height: 1.3;
  color: var(--ink-3);
}
/* The small group icon (added in the HTML by the other agent) — a quiet,
   slightly-accented glyph that keys each section without competing with it. */
#slidebar-white .nav.navbar-nav > li.nav-group .ng-icon {
  display: inline-block;
  margin-right: 6px;
  font-size: 11px;
  font-weight: 400;
  color: var(--accent);
  opacity: 0.85;
  vertical-align: baseline;
}

/* ---- Sidebar action buttons: full-width module buttons so labels never clip in the ~250px rail ---- */
#slidebar-white .nav.navbar-nav .center .btn,
#slidebar-white .nav.navbar-nav .explore-btns .btn {
  display: block; width: 100%; margin: 6px 0 0; white-space: normal; text-align: center;
}
#slidebar-white .nav.navbar-nav .center .btn:first-child,
#slidebar-white .nav.navbar-nav .explore-btns .btn:first-child { margin-top: 0; }

/* The .center button wrappers sit in a flex-centered card, which shrinks them to
   content — force them full-width so the full-width buttons actually fill the card. */
#slidebar-white .nav.navbar-nav .center { width: 100%; box-sizing: border-box; }

/* Stronger contrast on the reading surfaces (insight + story), and the story's
   narrative lead-in connective. Keep Inter — just make sure nothing reads faint. */
#insightPanel, #insightPanel p { color: var(--ink); }
.ns-lead { font-weight: 600; color: var(--accent); }
.ns-currfinding, #insightPanel p, #agentPanel .na-obs, #agentPanel .na-answer { color: var(--ink); }

/* Make the side insight card unmistakable: a clear accent header + always on top. */
#insightPanel .insight-caption {
  font: 700 11px 'Inter', system-ui, sans-serif; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--accent); margin-bottom: 6px;
}
#insightPanel .insight-caption::before { content: "\1F4A1  "; }
#insightPanel .insight-body { color: var(--ink); font-size: 14px; line-height: 1.6; }

/* ====================================================================
   ORG WEB-RESEARCH BRIEFING (#orgDialog .org-web) — the free, CORS-only
   Wikipedia/Wikidata briefing window.OrgResearch surfaces inside the org
   dialog. It is the CONTRACT slot the dialog mounts after the in-network
   mini-graph, so it reads as a calm, cited "what is this company" card.
   CONTRACT classes (styled here cohesively on the #2b6cb0 / Inter system):
     .org-web          — the section itself; a top divider sets it apart
     .org-web-thumb    — the small rounded logo / thumbnail image
     .org-web-summary  — the readable dark-text encyclopedia extract
     .org-web-facts    — small key/value rows (industry / HQ / founded …)
     .org-web-src      — the quiet "Source: Wikipedia" link
     .org-web-loading  — the shimmer / "Researching…" placeholder state
   modern.css loads last, so this refines the org dialog WITHOUT removing
   anything above. Defensive / id-class anchored — harmless until mounted.
   ==================================================================== */

/* The briefing section — a clean band beneath the mini node-link, set off
   by a hairline top divider so it reads as a distinct, cited block. */
#orgDialog .org-web {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line-soft);
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-2);
}

/* Head row: the small thumbnail sits beside the name + one-line descriptor.
   Defensive — works whether the JS lays the head out with .org-web-head or
   just stacks the children, since the thumb floats/aligns on its own. */
#orgDialog .org-web .org-web-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}
#orgDialog .org-web .org-web-body { flex: 1 1 auto; min-width: 0; }

/* The thumbnail — a small, soft-cornered logo image with a quiet ring so it
   reads cleanly on the dialog's pale radial canvas. */
#orgDialog .org-web-thumb {
  flex: 0 0 auto;
  width: 64px;
  height: 64px;
  border-radius: var(--r-md);
  object-fit: cover;
  background: var(--surface-1);
  border: 1px solid var(--line);
  box-shadow: var(--sh-1);
}

/* Org name + short Wikidata descriptor inside the head (defensive helpers). */
#orgDialog .org-web .org-web-name {
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--ink);
  line-height: 1.3;
}
#orgDialog .org-web .org-web-desc {
  margin-top: 1px;
  font-size: var(--fs-sm);
  color: var(--ink-3);
  text-transform: capitalize;
  line-height: 1.45;
}

/* The encyclopedia extract — the readable through-line of the briefing:
   comfortable dark body text, a touch of top space when it follows the head. */
#orgDialog .org-web-summary {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--ink-2);
}
#orgDialog .org-web-summary strong,
#orgDialog .org-web-summary b { color: var(--ink); font-weight: 700; }

/* Key facts — small key/value rows shown as quiet pill chips that wrap. The
   block tolerates either a <ul>/<li> list or flat .org-web-fact rows. */
#orgDialog .org-web-facts {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) 6px;
}
#orgDialog .org-web-facts li,
#orgDialog .org-web-facts .org-web-fact {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-2);
  background: var(--surface-1);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  padding: 2px 9px;
}
/* The fact label / key reads bolder + darker than its value. */
#orgDialog .org-web-facts li b,
#orgDialog .org-web-facts li strong,
#orgDialog .org-web-facts .org-web-fact .org-web-fact-key {
  font-weight: 700;
  color: var(--ink);
}
#orgDialog .org-web-facts .org-web-fact-val {
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* The provenance line — a quiet, on-brand "Source: Wikipedia" credit. */
#orgDialog .org-web-src {
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
  color: var(--ink-3);
}
#orgDialog .org-web-src a {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
  transition: color 0.12s var(--ease);
}
#orgDialog .org-web-src a:hover {
  color: var(--accent-ink);
  text-decoration: underline;
}

/* The "you know N people here" in-network line, when the briefing weaves it in. */
#orgDialog .org-web .org-web-net {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-2);
}
#orgDialog .org-web .org-web-net b { color: var(--accent-ink); font-weight: 700; }

/* The "no public profile" empty state — quiet, italic, never alarming. */
#orgDialog .org-web .org-web-none {
  font-size: var(--fs-base);
  color: var(--ink-3);
  font-style: italic;
}

/* Loading state — a soft shimmer + muted "Researching…" text while the free
   Wikipedia lookup is in flight. Works as a text line OR as a skeleton bar
   (give the element no text and a height to show the shimmer alone). */
#orgDialog .org-web-loading {
  position: relative;
  color: var(--ink-3);
  font-size: var(--fs-base);
  font-style: italic;
  background-image: linear-gradient(
    100deg,
    rgba(43, 108, 176, 0.00) 30%,
    rgba(43, 108, 176, 0.10) 50%,
    rgba(43, 108, 176, 0.00) 70%
  );
  background-size: 220% 100%;
  background-repeat: no-repeat;
  border-radius: var(--r-sm);
  animation: orgWebShimmer 1.25s var(--ease) infinite;
}
@keyframes orgWebShimmer {
  from { background-position: 180% 0; }
  to   { background-position: -80% 0; }
}

/* ====================================================================
   COPILOT DEEP-RESEARCH REPORT (#agentPanel .na-card / .na-synth / .na-list)
   — cohesion pass for the free Wikipedia org-research report the network
   agent streams into #agentPanel. The module injects its own scoped
   #na-styles at runtime (so it works standalone); these rules sit on the
   modern.css token system so the report reads consistently with the rest of
   the app when this stylesheet applies. They REUSE the existing .na-*
   vocabulary (no new selectors removed) and only refine surface / type /
   rhythm. Defensive / id-class anchored — harmless until the report mounts.
   ==================================================================== */

/* Per-org cited card — settle the radius / border / shadow onto the tokens
   and give a gentle tactile hover, mirroring the .surprise-card / .na-card feel. */
#agentPanel .na-card {
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  transition: box-shadow 0.15s var(--ease), border-color 0.15s var(--ease);
}
#agentPanel .na-card:hover { box-shadow: var(--sh-2); border-color: var(--line); }
#agentPanel .na-card h4 {
  letter-spacing: -0.005em;
  color: #13447e;
}

/* The cited facts list inside each org card — comfortable, readable rows. */
#agentPanel .na-card li { color: var(--ink-2); line-height: 1.5; }

/* The "Show on graph / Show evidence" affordance — a quiet on-brand chip
   matching the .ni-tag / .ask-chip family, with a tactile press. */
#agentPanel .na-evi {
  border: 1px solid transparent;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: var(--r-sm);
  font-weight: 600;
  transition: background 0.12s var(--ease), color 0.12s var(--ease),
              border-color 0.12s var(--ease), transform 0.08s var(--ease);
}
#agentPanel .na-evi:hover {
  background: #dbe9f8;
  color: var(--accent-ink);
  border-color: rgba(43, 108, 176, 0.25);
}
#agentPanel .na-evi:active { transform: translateY(0.5px); }

/* The overall synthesis — the report's closing paragraph; lift it onto the
   token line-height + readable ink while keeping its warm gold top rule. */
#agentPanel .na-synth {
  border-top: 2px solid var(--highlight);
  color: var(--ink);
  line-height: 1.65;
}
#agentPanel .na-synth h4 { letter-spacing: -0.005em; }

/* The clickable org / people rows beneath the report — snappy hover wash. */
#agentPanel .na-list li {
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink-2);
  transition: color 0.12s var(--ease);
}
#agentPanel .na-list li:hover { color: var(--accent-ink); }

/* Story panel close button (the panel now slides in via .open on render) */
#storyPanel .ns-head { position: relative; }
#storyPanel .ns-close {
  position: absolute; top: 8px; right: 10px; border: 0; background: none;
  font-size: 22px; line-height: 1; color: #94a3b8; cursor: pointer; padding: 0 4px;
}
#storyPanel .ns-close:hover { color: var(--accent); }

/* ====================================================================
   GRAPH LEGIBILITY PASS (appended last so it reinforces, never removes,
   any selector above). Goal: the main network graph reads as crisp,
   clearly-outlined node dots sitting ABOVE a quiet, THIN edge web, with
   sharply readable labels. The per-edge / per-node STATE styling is owned
   by networkLayout.js, which writes stroke + stroke-width + opacity INLINE
   per element (edgeMode, surprise / path / highlight, broker ring). Those
   inline styles correctly WIN over these rules for any active state — so
   here we only set a thin, quiet BASE so the resting graph is legible:
     • edges default to a thin hairline,
     • node circles get a clean white outline,
     • node labels keep a strong white halo + dark ink (set just above).
   ==================================================================== */

/* ---- Edges: a thin, quiet hairline at rest ----
   The bulk of the ~4600 links should read as a faint web, not a heavy mat.
   This is the BASE width only; baseRestyle() in networkLayout.js writes the
   per-edge stroke-width inline for each edgeMode (community / betweenness /
   organization / uniform) and for the bolder surprise / path / highlight
   states, and those inline widths win where set. There is intentionally no
   heavy global .link width anywhere — this thin default is the floor. */
.link {
  stroke-width: 0.7px;
  stroke-linecap: round;
}

/* ---- Node circles: a crisp white outline so dots separate from the web ----
   A clean ~1.4px white ring lifts every node off the dense edge field and
   makes the dots read as distinct, well-outlined points. The JS sets the
   base stroke inline and applyBrokerRing()/baseRestyle() override the stroke
   + width inline for the betweenness broker ring and the surprise / path /
   highlight states, so those inline values win where set; this is the calm
   resting outline. Nodes paint AFTER links (g.node sits above the links in
   the DOM), so the outlined dots already sit ABOVE the edges. */
.nodeCircle {
  stroke: #ffffff;
  stroke-width: 1.4px;
}

/* ---- On-graph community + semantic overlay labels: confirm legibility ----
   These already render black with a strong white halo (paint-order:stroke)
   in the rules above; this is a final, harmless reinforcement so the topic /
   commonality names stay crisply readable over the edge web on ANY backdrop.
   It does not change colour or weight — just re-asserts the halo + dark ink. */
.comm-overlay-label,
.sem-overlay-label,
.sem-labels text {
  paint-order: stroke;
  stroke-linejoin: round;
}

/* =====================================================================
   Legend master toggle  (#legendToggle)
   A small, faint pill that shows/hides ALL the on-graph legend cards
   (edge betweenness, structural role, community, glyph overlay, prediction).
   Purely additive: it flips `body.legends-off` and never repositions the
   crowded bottom-left legend column. Sits just to the RIGHT of that column
   (which is left:14px, ~180px wide) so it overlaps nothing; when the legends
   are hidden it slides into the now-empty corner.
   ===================================================================== */
#legendToggle {
  position: fixed; left: 200px; bottom: 16px; z-index: 8;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px;
  font: 600 11px/1 'Inter', system-ui, sans-serif; letter-spacing: 0.02em;
  color: #2b4a6f; cursor: pointer;
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(203, 213, 224, 0.8); border-radius: 999px;
  box-shadow: 0 4px 14px rgba(16, 24, 40, 0.10);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0.8; transition: opacity 0.2s var(--ease), left 0.2s var(--ease), color 0.2s var(--ease);
}
#legendToggle:hover { opacity: 1; }
#legendToggle .lt-ic { font-size: 13px; line-height: 1; }
/* Hidden state: dim + dashed so the pill reads as "off", and slide into the corner. */
#legendToggle.is-off { color: #8a94a3; border-style: dashed; }
body.legends-off #legendToggle { left: 14px; }

/* The actual hide — applies to every on-graph legend card at once. */
body.legends-off :is(#edgeLegend, #roleLegend, #communityLegend, #glyphLegendOverlay, #predLegend) {
  display: none !important;
}

/* Small screens: the legend column reflows to left:8px (~150px wide); keep the
   pill clear of it and of the full-width surprise thread that docks at bottom. */
@media (max-width: 720px) {
  #legendToggle { left: 168px; bottom: 10px; }
  body.legends-off #legendToggle { left: 8px; }
}

/* =====================================================================
   DEMO LANDING POLISH (2026-07) — one centered intro moment, an identity
   navbar, and a composed bottom-left rail. Additive layer: every rule
   here either styles new elements or out-specifies (two-id selectors,
   no !important wars) the fixed-position rules it retires. The old
   left:14px fixed legend coordinates stay in the file but are inert for
   elements that now live inside #legendDock / #utilityDock.
   ===================================================================== */

/* ---- Intro card internals ---- */
#introOverlay .intro-kicker {
  font: 700 11px/1 'Inter', system-ui, sans-serif;
  letter-spacing: 0.12em;
  color: var(--accent);
  margin-bottom: 10px;
}
#introOverlay h1 { font-size: 28px; letter-spacing: -0.02em; }
#introOverlay .intro-lede { font-size: 15.5px; color: #374151; margin: 0 0 16px; }
#introOverlay .intro-chips { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 16px; }
#introOverlay .intro-chips span {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 7px 12px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface-1);
  font: 600 13px/1 'Inter', system-ui, sans-serif;
  color: var(--muted);
}
#introOverlay .intro-chips b {
  color: var(--ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-width: 2ch; text-align: right;
}
#introOverlay .intro-rows { margin: 0 0 6px; }
#introOverlay .intro-row { font-size: 14.5px; line-height: 1.55; color: #374151; margin: 0 0 7px; text-wrap: pretty; }
#introOverlay .intro-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
#introOverlay .intro-actions .intro-btn { margin-top: 0; padding: 12px 20px; white-space: nowrap; }
#introOverlay .intro-actions .intro-btn:hover { transform: translateY(-1px); }
#introOverlay .intro-btn-ghost,
#introOverlay .intro-actions .intro-btn-ghost {
  background: var(--surface-1);
  color: var(--accent-ink);
  border: 1px solid var(--line);
}
#introOverlay .intro-btn-ghost:hover,
#introOverlay .intro-actions .intro-btn-ghost:hover { background: var(--surface-hover); }
#introOverlay .intro-tour-link {
  /* Deliberate second row, centered under the buttons — not a right-hugging orphan. */
  width: 100%; text-align: center;
  margin-top: 2px; padding: 6px 2px;
  border: none; background: none; cursor: pointer;
  font: 500 13px/1 'Inter', system-ui, sans-serif;
  color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
}
#introOverlay .intro-tour-link:hover { color: var(--accent); background: none; }

/* ---- Navbar identity row ---- */
#navbar-white .navbar-brand { font-size: 16px; }
.nav-sub {
  /* Share the brand's vertical box (Bootstrap brand = 15px padding / 20px line)
     so title and subtitle sit on one baseline instead of hugging the top edge. */
  display: inline-block;
  padding: 15px 0;
  vertical-align: top;
  font: 500 12.5px/20px 'Inter', system-ui, sans-serif;
  color: var(--muted);
  margin-left: 12px;
  white-space: nowrap;
}
#navChips {
  position: fixed; top: 10px; right: 96px; z-index: 1040;
  display: flex; gap: 8px;
}
.nav-chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 6px 11px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface-1);
  font: 600 12px/1 'Inter', system-ui, sans-serif;
  color: var(--muted); white-space: nowrap;
}
.nav-chip b { color: var(--accent); font-size: 12.5px; font-variant-numeric: tabular-nums; }
/* Full-bleed mode (☰ toggles body.nav-hidden): the whole identity row goes away,
   not just the brand — otherwise the subtitle + chips would float over the graph. */
body.nav-hidden .nav-sub,
body.nav-hidden #navChips { display: none; }
@media (max-width: 1180px) { .nav-sub { display: none; } }
@media (max-width: 860px)  { #navChips { display: none; } }

/* ---- Composed bottom-left rail (docks are siblings of #graphContainer) ---- */
#graphs { position: relative; }
#utilityDock {
  position: absolute; left: 14px; bottom: 14px; z-index: 8;
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  max-width: calc(100% - 28px);
}
#legendDock {
  position: absolute; left: 14px; bottom: 56px; z-index: 7;
  display: flex; flex-direction: column-reverse; gap: 8px; align-items: flex-start;
  /* overflow stays visible so card shadows aren't clipped; scroll only kicks in
     on short viewports where the stack could actually exceed the canvas */
  overflow: visible;
}
@media (max-height: 760px) {
  #legendDock { max-height: calc(100vh - 190px); overflow-y: auto; }
}
#launcherPanels {
  /* z above the passive #surpriseThread (z 1200): a panel the user just opened
     must never be buried under ambient result cards */
  position: absolute; left: 292px; bottom: 56px; z-index: 1210;
  display: flex; flex-direction: column-reverse; gap: 10px; align-items: flex-start;
  max-height: calc(100vh - 190px);
}
@media (max-width: 720px) {
  #launcherPanels { left: 14px; max-width: calc(100vw - 28px); }
  #launcherPanels #scholarLensPanel,
  #launcherPanels #liveScopeToast { max-width: calc(100vw - 42px); }
}

/* Dock members compose in flex — retire their fixed/absolute coordinates. */
#utilityDock #legendToggle { position: static; left: auto; bottom: auto; opacity: 1; }
#legendDock #edgeLegend,
#legendDock #communityLegend,
#legendDock #semCaption,
#legendDock #glyphLegendOverlay {
  position: static !important;
  left: auto !important; bottom: auto !important;
}
/* #roleLegend duplicates #glyphLegendOverlay's content (buildGlyphLegend fills both) —
   retire it rather than show the same legend twice. */
#roleLegend { display: none !important; }
/* NOTE: no blanket `body.legends-off #legendDock{display:none}` here — the legend
   cards are already hidden individually by the legends-off rule above, and hiding
   the whole dock would also kill #semCaption, which must stay visible in semantic
   mode regardless of the legends toggle. */

/* Scholar Lens + Live LinkedIn pills: docked, quiet, matching the ≡ Legend pill.
   Two-id selectors beat their runtime-injected single-id styles without !important. */
#utilityDock #liveScopeBtn,
#utilityDock #scholarLensBtn {
  position: static; left: auto; right: auto; bottom: auto;
  background: rgba(255, 255, 255, 0.92);
  color: #2b4a6f;
  border: 1px solid rgba(203, 213, 224, 0.8);
  border-radius: 999px;
  padding: 6px 11px;
  font: 600 11px/1 'Inter', system-ui, sans-serif;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 14px rgba(16, 24, 40, 0.10);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
#utilityDock #liveScopeBtn:hover,
#utilityDock #scholarLensBtn:hover {
  background: var(--surface-hover);
  color: var(--accent-ink);
  transform: none;
  box-shadow: 0 4px 14px rgba(16, 24, 40, 0.14);
}
#launcherPanels #liveScopeToast,
#launcherPanels #scholarLensPanel {
  position: static !important;
  left: auto !important; bottom: auto !important;
}

/* ---- Declutter: one onboarding surface, tighter sidebar hierarchy ---- */
#slidebar-white li.dropdown:has(#quickStart) { display: none !important; }
.nav-group span { font-size: 10px; letter-spacing: 0.1em; color: var(--ink-3); }
#slidebar-white .nav-group { margin-top: 14px; }

/* ---- Legend cards as one family: opaque, shared chrome, no staircase ---- */
#legendDock > div {
  background: rgba(255, 255, 255, 0.96);
  opacity: 1;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 6px 20px rgba(16, 24, 40, 0.10);
}
#legendDock #semCaption:empty { display: none; }

/* ---- ≡ Legend pill: "off" is its default state — keep it first-class.
   Solid border + full-contrast label; the accent tint marks legends ON. ---- */
#utilityDock #legendToggle.is-off { color: #2b4a6f; border-style: solid; opacity: 1; }
#utilityDock #legendToggle:not(.is-off) {
  color: var(--accent-ink);
  border-color: rgba(43, 108, 176, 0.5);
  background: var(--surface-hover);
}

/* ---- jQuery-UI dialog close button ships as an EMPTY button (no icon span),
   rendering as a bare outlined square. Give every dialog a real ✕. ---- */
.ui-dialog .ui-dialog-titlebar-close {
  display: inline-flex; align-items: center; justify-content: center;
}
.ui-dialog .ui-dialog-titlebar-close::before {
  content: "\2715";
  font: 600 12px/1 'Inter', system-ui, sans-serif;
  color: var(--muted);
}
.ui-dialog .ui-dialog-titlebar-close:hover::before { color: var(--ink); }

/* ---- Autopilot audit: warm orange collides with the graph's community/gold
   encodings and outranks the blue primary — quiet it to a ghost pill; the 🧠
   glyph carries the differentiation. ---- */
#autopilotBtn.btn-warning {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid #cbd5e0;
}
#autopilotBtn.btn-warning:hover, #autopilotBtn.btn-warning:focus {
  background: var(--surface-hover);
  color: var(--accent-ink);
  border-color: var(--accent);
}

/* ---- Fit the canvas to the viewport so the bottom rail never falls below the
   fold (the svg ships an inline height:88vh, hence !important; viewBox +
   preserveAspectRatio make this a pure scale, nothing clips). Header (~70px)
   + 64px container margin + rail clearance = 160px; nav-hidden frees 46px. ---- */
#graphContainer > svg {
  height: calc(100vh - 160px) !important;
  min-height: 420px;
}
body.nav-hidden #graphContainer > svg {
  height: calc(100vh - 114px) !important;
}
