:root {
  --void: #0A0D10;
  --panel: #12161A;
  --panel-2: #171C21;
  --line: #1D2A30;
  --text: #EAF6F6;
  --muted: #4C6B72;
  --cyan: #13A8C4;
  --cyan-bright: #7FE7FF;
  --mint: #3ED9A6;
  --coral: #FF6B57;
  --font-body: -apple-system, "Inter", "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", "JetBrains Mono", ui-monospace, monospace;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
/* Theme-colored scrollbar (default browser scrollbar otherwise stays
   light/system-colored regardless of this app's own dark palette) —
   applies everywhere, including modal-box/module-grid's own inner
   overflow-y:auto scroll areas (e.g. the preset editor modal). */
* { scrollbar-color: var(--line) var(--void); scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--void); }
::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; border: 2px solid var(--void); }
::-webkit-scrollbar-thumb:hover { background: var(--cyan); }
#bg-canvas { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: -1; pointer-events: none; }
body {
  margin: 0;
  background: var(--void);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.5;
}

#app { display: flex; flex-direction: column; min-height: 100vh; }

.topbar {
  display: flex; align-items: center; gap: 28px;
  padding: 14px 24px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, #0D1114, #0A0D10);
}
/* Brand mark and the session/sign-in controls are the topbar's fixed
   anchors — flex-shrink:0 keeps them at natural size so .main-nav (the
   one topbar child with genuinely variable width — nine-plus links) is
   the only thing that gives way on a narrow screen. Without this the row
   had no single element it was "allowed" to shrink, so flexbox squeezed
   everything roughly proportionally instead — the deformed look this
   fixes. */
.brand, .session-box, .guest-signin { flex-shrink: 0; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand-mark-full { height: 64px; width: auto; display: block; }
.brand-mark-icon { height: 64px; width: 64px; display: block; border-radius: 6px; }
.brand-wordmark {
  font-family: var(--font-mono); font-size: 17px; font-weight: 600; letter-spacing: 2px;
  color: var(--text);
}
.brand-wordmark b { color: var(--coral); font-weight: 600; letter-spacing: 3px; }

/* min-width:0 overrides the flex default of min-width:auto, which pins a
   flex item to at least its content's natural (unwrapped) width — with
   that default in place the browser had no way to shrink .main-nav at
   all as the topbar narrowed, so instead every link's padding/font
   visually compressed trying to fit, and at the extreme, wrapped into a
   second squashed row. Explicitly allowing this item to shrink below its
   content width, combined with overflow-x below, turns that into a
   plain horizontal scroll instead — every link keeps its normal size,
   the ones that don't fit are just off to the right. */
/* Wraps <nav> only so navScrollDot below has something to sit on top of
   at the nav's own trailing edge — position:relative here, not on
   .main-nav itself, since .main-nav is the scrolling element and an
   absolutely-positioned child of a scrolling container would scroll
   away with it instead of staying pinned as a fixed "there's more" hint. */
.nav-scroll-wrap {
  position: relative; display: flex; align-items: center; min-width: 0; margin-left: 12px;
  /* Reserves the same strip a horizontal scrollbar would have occupied
     along the bottom edge, sized for the 18px dot below to sit in
     without overlapping the link row above it — padding grows the box,
     the matching negative margin cancels that growth back out so
     .topbar's own height/vertical centering is unaffected. */
  padding-bottom: 20px; margin-bottom: -20px;
}
.main-nav {
  display: flex; gap: 4px; align-items: center;
  min-width: 0; overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap;
  -webkit-overflow-scrolling: touch;
  /* The scrollbar itself is replaced by navScrollDot (a themed "more to
     scroll" indicator, app.js#updateNavScrollHint) rather than shown —
     dragging a scrollbar thumb this narrow is fiddly anyway; a normal
     trackpad/touch horizontal swipe still scrolls it fine without one. */
  scrollbar-width: none;
}
.main-nav::-webkit-scrollbar { display: none; }
/* Travels along the bottom of the wrap — the same strip the horizontal
   scrollbar it replaces used to occupy — as an actual thumb: its `left`
   (px, set by app.js#updateNavScrollHint on every scroll/resize) tracks
   .main-nav's scroll position 1:1, so it reads as "where am I" the way a
   real scrollbar thumb does, not a static "there's more" decoration.
   Stays visible for the whole scroll range (only hidden when there's no
   overflow at all) for the same reason — a scrollbar thumb doesn't
   vanish once you reach the end, it just sits at the end. Itself
   clickable/draggable — dragging scrolls .main-nav proportionally, a
   plain click nudges it — so it doubles as a mouse-friendly scroll
   control, not just an indicator. A real <button> (index.html), not the
   inert <span> this started as, so it's focusable/keyboard-usable like
   the rest of the topbar's controls. */
.nav-scroll-dot {
  position: absolute; left: 0; bottom: 1px;
  width: 18px; height: 18px; border-radius: 50%; background: var(--mint);
  border: none; padding: 0; box-shadow: 0 0 10px var(--mint);
  cursor: pointer; animation: dot-pulse 2s ease-in-out infinite;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.nav-scroll-dot:hover, .nav-scroll-dot:focus-visible {
  transform: scale(1.15);
  box-shadow: 0 0 14px var(--mint);
  animation-play-state: paused;
}
.nav-scroll-dot:active { transform: scale(0.95); }
.nav-scroll-dot.hidden { display: none; }
@media (prefers-reduced-motion: reduce) {
  .nav-scroll-dot { animation: none; }
}
.main-nav a {
  position: relative; color: var(--muted); text-decoration: none; font-size: 15.5px; font-weight: 600;
  padding: 8px 14px; border-radius: 20px; letter-spacing: 0.3px;
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
  flex-shrink: 0; /* keep every link at its natural size — the row scrolls instead of squeezing them */
}
.main-nav a::after {
  content: ''; position: absolute; left: 14px; right: 14px; bottom: 2px; height: 2px;
  background: var(--mint); border-radius: 2px; transform: scaleX(0); transform-origin: left;
  transition: transform 0.2s ease;
}
.main-nav a:hover { color: var(--mint); background: none; transform: none; }
.main-nav a:hover::after { transform: scaleX(1); }
.main-nav a.active {
  color: #04181C; background: var(--mint); font-weight: 700;
  box-shadow: 0 0 0 1px rgba(62, 217, 166, 0.45), 0 5px 18px -5px rgba(62, 217, 166, 0.65);
}
.main-nav a.active::after { display: none; }
.main-nav a[data-nav="admin"] {
  font-family: var(--font-mono); font-weight: 600; font-size: 14px; letter-spacing: 0.3px;
  color: #04181C; background: var(--mint); border: 1px solid var(--mint); border-radius: 6px;
  padding: 9px 18px; margin-left: 4px;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.main-nav a[data-nav="admin"]::after { display: none; }
.main-nav a[data-nav="admin"]:hover { background: var(--cyan-bright); border-color: var(--cyan-bright); color: #04181C; transform: translateY(-1px); }
.main-nav a[data-nav="admin"].active {
  color: #04181C; background: var(--cyan-bright); border-color: var(--cyan-bright);
  box-shadow: 0 0 0 1px rgba(127, 231, 255, 0.45), 0 5px 18px -5px rgba(127, 231, 255, 0.65);
}
.hidden { display: none !important; }

.session-box { margin-left: auto; display: flex; align-items: center; gap: 10px; font-size: 15px; color: var(--muted); }
.session-pill {
  display: flex; align-items: center; gap: 8px;
  background: var(--panel-2); border: 1px solid rgba(62, 217, 166, 0.5); border-radius: 20px;
  padding: 5px 14px 5px 6px; text-decoration: none; color: var(--mint); font-size: 14.5px;
  transition: background 0.15s ease, border-color 0.15s ease; cursor: pointer; font-family: var(--font-body);
}
.session-pill:hover { background: rgba(62, 217, 166, 0.1); border-color: var(--mint); }
.session-avatar {
  width: 24px; height: 24px; border-radius: 50%; background: var(--mint); color: #04181C;
  display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; font-family: var(--font-mono);
}
.session-box .icon-btn {
  width: auto; height: auto; border-radius: 8px; padding: 7px 11px;
  background: rgba(255, 107, 87, 0.1); border: 1px solid rgba(255, 107, 87, 0.5); color: var(--coral); cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 1rem; line-height: 1;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.session-box .icon-btn:hover { background: rgba(255, 107, 87, 0.2); border-color: var(--coral); }

/* ---- Notification bell (frontend/js/app.js#loadNotifications) — platform
   announcements a super admin posts (Admin Panel > Platform >
   Announcements), read by every signed-in user regardless of tenant. ---- */
.notif-bell-wrap { position: relative; }
.notif-bell {
  width: auto; height: auto; border-radius: 8px; padding: 7px 11px;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 1rem; line-height: 1; position: relative;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.notif-bell:hover { border-color: var(--cyan-bright); color: var(--cyan-bright); }
.notif-badge {
  position: absolute; top: -5px; right: -5px; min-width: 16px; height: 16px; padding: 0 3px;
  border-radius: 999px; background: var(--coral); color: #fff; font-family: var(--font-mono);
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
}
.notif-badge.hidden { display: none; }
.notif-panel {
  position: absolute; top: calc(100% + 8px); right: 0; width: 360px; max-height: 420px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 28px rgba(0,0,0,0.45); z-index: 30;
}
.notif-panel.hidden { display: none; }
.notif-panel-head { padding: 12px 14px; border-bottom: 1px solid var(--line); font-weight: 700; font-size: 14px; }
.notif-item { padding: 12px 14px; border-bottom: 1px solid var(--line); }
.notif-item:last-child { border-bottom: none; }
.notif-item-title { font-weight: 600; font-size: 14.5px; margin-bottom: 3px; }
.notif-item-body { font-size: 13.5px; color: var(--muted); white-space: pre-wrap; line-height: 1.5; }
.notif-item-meta { font-size: 12px; color: var(--muted); margin-top: 6px; }

main#view { flex: 1; padding: 32px 24px; max-width: 1400px; width: 100%; margin: 0 auto; }

.footer {
  padding: 18px 24px; margin-top: auto;
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, #0A0D10, #0D1114);
  display: flex; align-items: center; justify-content: space-between;
  font-size: 14px; color: var(--muted);
}
.footer-brand { display: flex; align-items: center; gap: 8px; }
.footer-copyright { color: var(--muted); }
.footer-mark { width: 16px; height: 16px; opacity: 0.6; }
.footer a {
  color: var(--cyan); text-decoration: none; display: inline-block;
  transition: color 0.15s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.footer a:hover { color: var(--cyan-bright); transform: translateX(4px); }

/* ---- Auth ---- */
.guest-signin.hidden { display: none !important; }
.guest-signin {
  margin-left: auto; display: inline-flex; align-items: center; gap: 8px;
  background: var(--cyan); color: #04181C; border: none; font-weight: 700;
  padding: 9px 20px; border-radius: 8px; font-size: 15px; text-decoration: none;
  transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
}
.guest-signin:hover { background: var(--cyan-bright); box-shadow: 0 4px 16px rgba(127, 231, 255, 0.25); transform: translateY(-1px); }

/* ---- Homepage ---- */
.home-page { overflow: hidden; } /* clips the sonar poster/mock cards' perspective-tilt overhang at narrow widths */

.home-hero {
  position: relative; max-width: 900px; margin: 64px auto 0; text-align: center; padding: 0 24px;
}

/* Radar mark above the badge (app.js#renderHeroRadarMark) — a rotating
   sweep (SMIL <animateTransform>) that lights up a blip as it passes,
   fades it out, and repeats. drop-shadow (not filter:blur on the element
   itself) so the glow doesn't blur the crisp ring/crosshair lines under it. */
.hero-radar-wrap { display: flex; justify-content: center; margin: 56px 0 28px; }
.hero-radar-svg { width: 200px; height: 200px; filter: drop-shadow(0 0 26px rgba(19,168,196,0.35)); }
@media (max-width: 600px) {
  .hero-radar-svg { width: 145px; height: 145px; }
}

.home-badge {
  display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-mono);
  font-size: 14px; letter-spacing: 1.5px; color: var(--mint); background: rgba(62, 217, 166, 0.08);
  border: 1px solid rgba(62, 217, 166, 0.35); border-radius: 999px; padding: 7px 16px 7px 11px; margin-bottom: 26px;
}
.home-badge .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 8px var(--mint); animation: dot-pulse 2s ease-in-out infinite; }
@keyframes dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.home-kicker { font-family: var(--font-mono); font-size: 14px; letter-spacing: 2px; color: var(--mint); margin-bottom: 18px; }
.home-hero h1 { font-size: 58px; line-height: 1.14; margin: 0 0 22px; letter-spacing: -1px; font-weight: 700; }
.home-hero h1 .accent { color: var(--cyan-bright); }
.home-hero h1 .accent-coral { color: var(--coral); }
.home-sub { color: var(--muted); font-size: 19.5px; line-height: 1.65; max-width: 680px; margin: 0 auto 36px; }
.home-actions { display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.home-cta { font-size: 18.5px; padding: 14px 32px; }
.home-cta-secondary {
  font: inherit; font-size: 17px; padding: 13px 26px; margin-top: 0; background: transparent; cursor: pointer;
  border: 1px solid var(--line); color: var(--text); text-decoration: none; border-radius: 8px;
  display: inline-flex; align-items: center; gap: 8px; transition: border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
.home-cta-secondary:hover { border-color: var(--cyan-bright); color: var(--cyan-bright); transform: translateY(-1px); }
/* Idle attention-pulse (same ring-pulse idea as .btn.btn-cta, scoped here
   instead since this is an outline button, not a filled one) plus the
   arrow icon nudging right on a loop — paused on hover/focus so it settles
   the moment someone's actually looking at it. */
.home-cta-secondary { animation: home-cta-pulse 2.4s ease-in-out infinite; }
.home-cta-secondary:hover, .home-cta-secondary:focus-visible { animation-play-state: paused; }
@keyframes home-cta-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(127,231,255,0.35); }
  50% { box-shadow: 0 0 0 8px rgba(127,231,255,0); }
}
.home-cta-secondary svg { animation: home-cta-arrow-nudge 1.6s ease-in-out infinite; }
.home-cta-secondary:hover svg, .home-cta-secondary:focus-visible svg { animation-play-state: paused; }
@keyframes home-cta-arrow-nudge {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .home-cta-secondary, .home-cta-secondary svg { animation: none; }
}
.home-microcopy { font-size: 14.5px; color: var(--muted); font-family: var(--font-mono); }

/* Live threat-map link out to Reef Sentinel — styled distinctly from the
   .home-cta-secondary "See how it works" button (coral/alert accent, a
   pulsing "live" dot) so it reads as "something happening right now
   elsewhere" rather than another in-product action. */
.home-threat-lead { font-size: 15.5px; color: var(--cyan-bright); margin: 22px 0 0; }
.live-attack-btn {
  display: inline-flex; align-items: center; gap: 10px; margin-top: 12px;
  padding: 12px 22px; border-radius: 999px; border: 1px solid rgba(255,107,87,0.4);
  background: rgba(255,107,87,0.08); color: var(--text); text-decoration: none;
  font-family: var(--font-mono); font-size: 14px; font-weight: 700; letter-spacing: 0.5px;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.1s ease;
  animation: live-attack-pulse 2.4s ease-in-out infinite;
}
.live-attack-btn:hover, .live-attack-btn:focus-visible {
  border-color: var(--coral); background: rgba(255,107,87,0.16); transform: translateY(-1px);
  animation-play-state: paused;
}
.live-attack-btn svg { color: var(--coral); flex-shrink: 0; }
.live-dot {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
  background: var(--coral); box-shadow: 0 0 8px var(--coral);
  animation: dot-pulse 1.4s ease-in-out infinite;
}
@keyframes live-attack-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,107,87,0.4); }
  50% { box-shadow: 0 0 0 10px rgba(255,107,87,0); }
}
@media (prefers-reduced-motion: reduce) {
  .live-attack-btn, .live-dot { animation: none; }
}

/* ---- Hero animated pulse diagram ---- */
/* Sits above the hero terminal below (both shown together, not one
   replacing the other) — a glowing dot travels each connector (SMIL
   <animateMotion> in app.js#renderHeroPulseScene, since the path shape is
   generated per segment — easier to keep the motion path and the visible
   line in sync from one source than to fight a CSS offset-path with the
   same coordinates by hand) while every node pings outward on a stagger.
   The SVG itself is inert (no perspective/rotate) — the animation IS the
   depth cue here, not a tilt. */
/* min-width + overflow-x:auto rather than letting the SVG scale down
   without a floor — SVG text sizes in the viewBox's own user units, so
   shrinking the rendered width past a point makes the node labels
   illegible; scrolling horizontally on narrow screens keeps them at a
   fixed, readable size instead. */
.hero-pulse-wrap { max-width: 1120px; margin: 16px auto 0; padding: 0 24px 8px; overflow-x: auto; }
.hero-pulse-svg { display: block; width: 100%; min-width: 640px; height: auto; overflow: visible; }
.hero-pulse-label {
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 2px; fill: var(--muted);
}
.hero-pulse-ring {
  transform-box: fill-box; transform-origin: center;
  animation: hero-pulse-ping 2.4s cubic-bezier(0,0,0.2,1) infinite;
}
@keyframes hero-pulse-ping {
  0% { transform: scale(0.7); opacity: 0.7; }
  75%, 100% { transform: scale(2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-pulse-ring { animation: none; opacity: 0; }
}

/* Hover/focus on a stage node: brighter glow in the node's own color, a
   quicker ping, and a bolder label — a little reward for exploring the
   RECON → REPORT chain node by node instead of just watching it play. */
.hero-pulse-node { cursor: pointer; }
.hero-pulse-node:hover, .hero-pulse-node:focus-visible {
  filter: drop-shadow(0 0 10px var(--c));
}
.hero-pulse-node .hero-pulse-ring, .hero-pulse-node .hero-pulse-ring-static {
  transition: stroke-width 0.2s ease;
}
.hero-pulse-node:hover .hero-pulse-ring, .hero-pulse-node:focus-visible .hero-pulse-ring {
  stroke-width: 3; animation-duration: 0.9s;
}
.hero-pulse-node:hover .hero-pulse-ring-static, .hero-pulse-node:focus-visible .hero-pulse-ring-static {
  stroke-width: 3;
}
.hero-pulse-node:hover .hero-pulse-label, .hero-pulse-node:focus-visible .hero-pulse-label {
  fill: var(--text); font-weight: 700;
}
.hero-pulse-node:focus-visible { outline: none; }
@media (max-width: 900px) {
  .hero-pulse-wrap { margin-top: 12px; }
}


/* ---- Shared section heading pattern ---- */
.home-section { max-width: 1280px; margin: 0 auto; padding: 100px 24px 0; }
.home-section.tight { padding-top: 70px; }
.section-head { text-align: center; max-width: 680px; margin: 0 auto 48px; }
.section-kicker { font-family: var(--font-mono); font-size: 13.5px; letter-spacing: 2.5px; color: var(--cyan-bright); margin-bottom: 12px; }
/* Named .home-section-title (not the generic .section-title used all over
   the authenticated app's own collapsible <summary> headers) — that class
   is defined again, later in this file, at a completely different size
   for a completely different purpose; reusing it here silently lost the
   cascade to that later rule. */
.home-section-title { font-size: 36px; margin: 0 0 14px; letter-spacing: -0.5px; }
.section-sub { color: var(--muted); font-size: 17.5px; line-height: 1.6; margin: 0; }

/* ---- How Recon works: flow steps ---- */
.flow-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; counter-reset: flow; margin-top: 32px; }
.flow-step {
  position: relative; background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 24px 20px; margin: 0 8px; transition: border-color 0.15s ease, transform 0.15s ease;
}
.flow-step:hover { border-color: var(--cyan); transform: translateY(-3px); }
.flow-step-num {
  font-family: var(--font-mono); font-size: 13px; font-weight: 700; color: #04181C;
  width: 26px; height: 26px; border-radius: 50%; background: var(--mint);
  display: flex; align-items: center; justify-content: center; margin-bottom: 16px;
}
.flow-step h3 { font-size: 17.5px; margin: 0 0 8px; }
.flow-step p { font-size: 15.5px; color: var(--muted); line-height: 1.55; margin: 0; }
.flow-step-fact {
  margin-top: 12px; display: inline-block; font-family: var(--font-mono); font-size: 11.5px;
  color: var(--cyan-bright); background: rgba(127,231,255,0.08); border: 1px solid rgba(127,231,255,0.25);
  border-radius: 6px; padding: 3px 8px;
}
.flow-connector { position: absolute; top: 37px; right: -20px; width: 24px; height: 2px; z-index: 1; }
.flow-connector svg { display: block; overflow: visible; }

/* Staggered reveal the first time this row scrolls into view
   (app.js#initFlowGridReveal) — .flow-grid-armed hides the cards only
   once JS has actually committed to animating them in, so a browser
   without IntersectionObserver (or a JS error) leaves them visible
   rather than stuck invisible. Reuses .sonar-signal-in's own keyframe
   rather than a fourth entrance animation on this page; the connector
   arrow is a child of its step, so it fades in together with it for
   free, no separate delay to track. */
.flow-grid-armed .flow-step { opacity: 0; }
.flow-grid-in-view .flow-step {
  animation: sonar-signal-in 0.55s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.flow-grid-in-view .flow-step:nth-child(1) { animation-delay: 0s; }
.flow-grid-in-view .flow-step:nth-child(2) { animation-delay: 0.45s; }
.flow-grid-in-view .flow-step:nth-child(3) { animation-delay: 0.9s; }
.flow-grid-in-view .flow-step:nth-child(4) { animation-delay: 1.35s; }

@media (max-width: 900px) {
  .flow-grid { grid-template-columns: 1fr 1fr; gap: 16px 0; }
  .flow-step { margin: 0; }
  .flow-connector { display: none; }
}
@media (max-width: 560px) {
  .flow-grid { grid-template-columns: 1fr; }
}

/* ---- Sonar section ---- */
.sonar-section {
  display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center;
}
.sonar-kicker-row { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.sonar-kicker-row .home-badge { margin: 0; }
.sonar-copy h2 { font-size: 34px; margin: 0 0 8px; letter-spacing: -0.5px; }
.sonar-copy .sonar-full-name { font-family: var(--font-mono); font-size: 14px; color: var(--muted); letter-spacing: 0.5px; margin: 0 0 18px; }
.sonar-quote {
  margin: 22px 0 0; padding: 18px 20px; border-left: 3px solid var(--coral);
  background: rgba(255,107,87,0.05); border-radius: 0 10px 10px 0;
  font-size: 17px; line-height: 1.65; color: var(--text); font-style: italic;
}
.sonar-mini-list { list-style: none; margin: 22px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.sonar-mini-list li { display: flex; align-items: flex-start; gap: 10px; font-size: 16px; color: var(--muted); }
.sonar-mini-list li svg { flex-shrink: 0; margin-top: 3px; color: var(--mint); }
.sonar-inapp-note {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 22px; padding: 9px 14px;
  background: rgba(127,231,255,0.06); border: 1px solid rgba(127,231,255,0.25); border-radius: 999px;
  font-size: 13.5px; color: var(--muted);
}
.sonar-inapp-note svg { color: var(--cyan-bright); flex-shrink: 0; }
.sonar-inapp-note b { color: var(--text); }

.sonar-mock-verdict {
  margin-top: 14px; padding: 12px 14px; border-radius: 10px; background: rgba(62,217,166,0.07);
  border: 1px solid rgba(62,217,166,0.3); font-size: 14.5px; color: var(--mint); display: flex; gap: 8px; align-items: flex-start;
}
.sonar-mock-verdict svg { flex-shrink: 0; margin-top: 2px; }

@media (max-width: 900px) {
  .sonar-section { grid-template-columns: 1fr; gap: 36px; }
}

/* The real SONAR mark (assets/logo/sonar-backronym-logo.svg) — a
   portrait-format poster that already carries the icon, wordmark, and the
   full S-O-N-A-R backronym breakdown, so it does the "what does SONAR
   stand for" job on its own; .sonar-copy next to it sticks to the "why it
   matters" pitch instead of repeating the breakdown in prose. */
.sonar-poster-wrap { display: flex; justify-content: center; }
.sonar-poster {
  width: 100%; max-width: 380px; height: auto; border-radius: 16px;
  border: 1px solid var(--line); box-shadow: 0 40px 80px -30px rgba(0,0,0,0.65);
  transform: perspective(1200px) rotateY(6deg) rotateX(2deg);
  transition: transform 0.3s ease;
}
.sonar-poster-wrap:hover .sonar-poster { transform: perspective(1200px) rotateY(2deg) rotateX(1deg); }

.sonar-signal-in, .sonar-mock-verdict.sonar-signal-in {
  animation: sonar-signal-in 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes sonar-signal-in {
  from { opacity: 0; transform: translateX(-10px); }
  to { opacity: 1; transform: none; }
}
/* Three-dot "still working" indicator after the synthesizing line —
   same idea as a chat app's typing indicator, staggered per dot. */
.sonar-typing-dots { display: inline-flex; gap: 3px; margin-left: 6px; vertical-align: middle; }
.sonar-typing-dots i {
  width: 3px; height: 3px; border-radius: 50%; background: var(--cyan-bright);
  display: inline-block; animation: sonar-typing 1.2s ease-in-out infinite;
}
.sonar-typing-dots i:nth-child(2) { animation-delay: 0.2s; }
.sonar-typing-dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes sonar-typing { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .sonar-signal-in, .sonar-mock-verdict.sonar-signal-in { animation: none; opacity: 1; transform: none; }
  .sonar-typing-dots i { animation: none; opacity: 0.6; }
}

@media (max-width: 900px) {
  .sonar-poster { max-width: 260px; transform: none; }
  .sonar-poster-wrap:hover .sonar-poster { transform: none; }
}

/* ---- Capabilities grid ---- */
/* "What is Sentinel Recon" plain-language explainer — 3 cards reusing
   .home-feature-card's exact look (icon/heading/body) so it doesn't need
   its own item styling, just its own column count. */
.what-is-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
  margin-top: 44px;
}
@media (max-width: 860px) { .what-is-grid { grid-template-columns: 1fr; } }

.home-features {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
  margin: 0; padding: 0;
}
/* Every card here now opens a demo (see app.js#HOME_FEATURES), so every
   card carries the "Watch it build" CTA — flex column + margin-top:auto
   on the CTA (same trick .home-features-highlight already uses below)
   keeps it flush with the card's bottom edge regardless of how many
   lines the body text above it wraps to. */
.home-features .home-feature-card { display: flex; flex-direction: column; }
.home-features .home-feature-demo-cta { margin-top: auto; padding-top: 14px; }
.home-feature-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 22px;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
.home-feature-card:hover { border-color: var(--cyan); transform: translateY(-2px); }
.home-feature-icon {
  width: 40px; height: 40px; border-radius: 10px; margin-bottom: 14px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(19,168,196,0.08); border: 1px solid rgba(19,168,196,0.25); color: var(--cyan);
}
.home-feature-card h3 { font-size: 17.5px; margin: 0 0 8px; }
.home-feature-card p { font-size: 15.5px; color: var(--muted); line-height: 1.5; margin: 0; }

/* The 8-card "built for real engagements" grid (app.js#HOME_FEATURES):
   its per-card glyph badge, centered above the heading rather than
   left-aligned like the default .home-feature-icon, and a step bigger
   (52px badge / 24px glyph vs. the usual 40px / 20px) to read clearly at
   that centered size. */
.home-feature-icon-wrap { display: flex; justify-content: center; margin-bottom: 14px; }
.home-feature-icon-md { width: 52px; height: 52px; border-radius: 12px; }

/* "Genuine offensive tooling..." card in .what-is-grid: same glyph-in-a-
   square every other card uses, just beside its heading instead of
   stacked above it. */
.home-feature-heading { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.home-feature-heading h3 { margin: 0; }
.home-feature-heading .home-feature-icon { margin-bottom: 0; flex-shrink: 0; }

/* ---- Highlight row (app.js#renderHome's #highlights, the 4-card lineup
   above the fold) — bigger, centered icon so it reads as a small mark per
   card rather than the compact left-aligned glyph the 10-card grid below
   uses. */
.home-features-highlight .home-feature-card { text-align: center; display: flex; flex-direction: column; }
.home-feature-icon-lg {
  width: 72px; height: 72px; border-radius: 16px; margin: 0 auto 16px;
}
.home-features-highlight .home-feature-demo-cta { margin-top: auto; padding-top: 14px; align-self: center; }

/* Tiles wired to a demo modal (HOME_FEATURES' `demo` key, see
   app.js#initHomeDemoTiles) get a visible affordance — an accent border
   and a "Watch it build" pill — so it's obvious before clicking that this
   one card behaves differently from the other twelve. */
.home-feature-card-demo { cursor: pointer; border-color: rgba(127,231,255,0.3); }
.home-feature-card-demo:hover { border-color: var(--cyan-bright); box-shadow: 0 8px 24px -12px rgba(127,231,255,0.35); }
.home-feature-card-demo:focus-visible { outline: 2px solid var(--cyan-bright); outline-offset: 2px; }
.home-feature-demo-cta {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 14px;
  font-family: var(--font-mono); font-size: 12.5px; font-weight: 700; letter-spacing: 0.5px; color: var(--cyan-bright);
}

@media (max-width: 860px) {
  .home-features { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .home-features { grid-template-columns: 1fr; }
}

/* ---- Not-yet-shipped tile, inline in the capabilities grid ---- */
/* Same .home-feature-card shell as everything around it (so it doesn't
   look bolted on), but a dashed border plus a muted "COMING SOON" badge —
   the same signal the old standalone roadmap strip gave — keeps it from
   reading as an already-shipped capability. */
.home-feature-card-soon { border-style: dashed; }
.home-feature-soon-badge {
  display: inline-flex; align-items: center; gap: 5px; margin-bottom: 12px;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; letter-spacing: 1.2px;
  color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px;
}

/* ---- "Reef is offensive" statement band ---- */
/* A rounded card rather than an edge-to-edge divider strip, with one
   corner (top-right) called out — a soft glow plus a viewfinder-style
   bracket, the same "target acquired" visual language as the radar mark's
   crosshairs up in the hero. */
.offensive-band {
  position: relative; margin: 110px 24px 0; padding: 70px 24px;
  background: linear-gradient(180deg, rgba(19,168,196,0.05), rgba(255,107,87,0.04));
  border: 1px solid var(--line); border-radius: 28px;
  text-align: center; overflow: hidden;
}
.offensive-band::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle at 100% 0%, rgba(255,107,87,0.18), transparent 55%);
}
.offensive-band::after {
  content: ''; position: absolute; top: 20px; right: 20px; width: 38px; height: 38px;
  border-top: 2px solid var(--coral); border-right: 2px solid var(--coral);
  border-radius: 0 14px 0 0; opacity: 0.85; pointer-events: none;
}
.offensive-band h2 {
  position: relative; font-size: 46px; margin: 0 0 14px; letter-spacing: -1px;
  font-family: var(--font-mono); text-transform: uppercase;
}
.offensive-band h2 span { color: var(--coral); }
.offensive-band p { position: relative; max-width: 700px; margin: 0 auto; color: var(--muted); font-size: 17.5px; line-height: 1.6; }
@media (max-width: 600px) {
  .offensive-band { margin: 90px 16px 0; border-radius: 20px; }
  .offensive-band h2 { font-size: 30px; }
  .offensive-band::after { width: 26px; height: 26px; top: 14px; right: 14px; }
}

/* ---- Final CTA ---- */
.home-final-cta { text-align: center; padding: 90px 24px 110px; max-width: 720px; margin: 0 auto; }
.home-final-cta h2 { font-size: 34px; margin: 0 0 14px; letter-spacing: -0.5px; }
.home-final-cta p { color: var(--muted); font-size: 17.5px; margin: 0 0 8px; }

@media (max-width: 860px) {
  .home-hero h1 { font-size: 38px; }
  .home-section-title { font-size: 28px; }
}

.auth-card {
  max-width: 380px; margin: 60px auto; background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; padding: 28px;
  animation: auth-card-in 0.4s ease-out;
}
@keyframes auth-card-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
.auth-card h1 { font-size: 20px; margin: 0 0 4px; }
.auth-card p.sub { color: var(--muted); font-size: 15px; margin: 0 0 20px; }
.auth-card label.bold-label { font-weight: 700; color: var(--text); }
.auth-card input:not([type="checkbox"]):not([type="radio"]) {
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.auth-card input:not([type="checkbox"]):not([type="radio"]):focus {
  box-shadow: 0 0 0 3px rgba(19, 168, 196, 0.25);
}
.auth-back-link {
  display: block; text-align: center; margin-top: 18px; color: var(--muted);
  font-size: 14.5px; text-decoration: none; transition: color 0.15s ease;
}
.auth-back-link:hover { color: var(--cyan-bright); }

label { display: block; font-size: 14px; color: var(--muted); margin: 14px 0 6px; letter-spacing: 0.3px; }
input:not([type="checkbox"]):not([type="radio"]), select, textarea {
  width: 100%; background: var(--void); border: 1px solid var(--line); color: var(--text);
  padding: 10px 12px; border-radius: 8px; font-size: 16px; font-family: var(--font-body);
}
input[type="checkbox"], input[type="radio"] { width: 16px; height: 16px; flex-shrink: 0; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--cyan); }

/* New Engagement wizard's name + type fields side by side (frontend/js/
   app.js#renderNewEngagement) — plain stacked labels/inputs each still
   get the generic width:100% rule above, but now that's 100% of their own
   column instead of the whole card, so neither one reads as an oversized
   box next to just a few words. Wraps to stacked on narrow screens since
   a 2-up row of full-size fields wouldn't fit a phone width. */
.form-row-2 { display: flex; gap: 20px; flex-wrap: wrap; }
.form-row-2 > div { flex: 1 1 240px; }

/* New Engagement wizard's Device Type search field (frontend/js/app.js#
   loadPresetRows) — a custom dropdown instead of a native <datalist>,
   whose popup size/padding browsers don't let CSS control, which read as
   "too small" with 16 options in it. */
.preset-search-dropdown {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  max-height: 320px; overflow-y: auto; box-shadow: 0 12px 28px rgba(0,0,0,0.45);
}
.preset-search-option { padding: 12px 16px; font-size: 15.5px; cursor: pointer; }
.preset-search-option .m-name { font-weight: 600; }
.preset-search-option .m-desc { font-size: 13px; color: var(--muted); margin-top: 2px; }
.preset-search-option:hover, .preset-search-option.active { background: var(--panel-2); }
.preset-search-option:hover .m-name, .preset-search-option.active .m-name { color: var(--cyan-bright); }
.preset-search-empty { padding: 12px 16px; font-size: 14px; color: var(--muted); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--cyan); color: #04181C; border: none; font-weight: 600;
  padding: 10px 18px; border-radius: 8px; cursor: pointer; font-size: 16px; margin-top: 18px;
  transition: background 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;
  box-shadow: 0 0 0 rgba(19, 168, 196, 0);
  text-decoration: none; /* .btn is also used on <a> (e.g. the New Engagement link) — no default underline */
}
.btn:hover { background: var(--cyan-bright); box-shadow: 0 4px 16px rgba(127, 231, 255, 0.25); transform: translateY(-1px); }
/* Primary call-to-action variant — a gentle attention-pulse, normal-weight
   text. Scoped to .btn-cta (currently just the New Engagement link) rather
   than the base .btn, so every other button in the app is unaffected. */
.btn.btn-cta { font-weight: 700; animation: btn-cta-pulse 2.2s ease-in-out infinite; }
.btn.btn-cta:hover { animation-play-state: paused; }
/* Opt-out for the Engagements list page's New Engagement button — same
   .btn-cta emphasis (bold text, primary color), just without the pulse.
   Scoped to this one modifier rather than touching .btn-cta itself so
   the homepage's Request a Demo/Pricing CTAs (styles.css's other
   .btn-cta callers) keep their attention-pulse as-is. */
.btn.btn-cta.no-pulse { animation: none; }
@keyframes btn-cta-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(127, 231, 255, 0.45); }
  50% { box-shadow: 0 0 0 9px rgba(127, 231, 255, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .btn.btn-cta { animation: none; }
  .tag.status-running { animation: none; }
  .progress-bar.is-running .progress-bar-fill::after { animation: none; }
}
.btn:active { transform: translateY(0); }
.btn.secondary { background: transparent; border: 1px solid var(--line); color: var(--text); box-shadow: none; }
.btn.secondary:hover { background: var(--panel-2); border-color: var(--cyan); box-shadow: none; transform: none; }
.btn.danger { background: var(--coral); color: #240703; }
.btn.danger:hover { background: #ff8672; box-shadow: 0 4px 16px rgba(255, 107, 87, 0.3); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

/* The OS-drawn "Browse"/"Choose File" button inside a file input is the
   one native form control CSS can still reach into (::file-selector-button
   is the standard hook; ::-webkit-file-upload-button is the same thing for
   older WebKit/Blink that predate it) — styled to match .btn exactly so it
   doesn't look like a bare unstyled system control next to the rest of the
   themed UI. The input's own box (border/background/padding) is set
   inline per use rather than here, same as every other text input in the
   app. */
input[type="file"].file-input-themed::file-selector-button,
input[type="file"].file-input-themed::-webkit-file-upload-button {
  background: var(--cyan); color: #04181C; border: none; font-weight: 600;
  padding: 8px 14px; border-radius: 6px; cursor: pointer; font-size: 14.5px;
  font-family: inherit; margin-right: 12px;
  transition: background 0.15s ease;
}
input[type="file"].file-input-themed::file-selector-button:hover,
input[type="file"].file-input-themed::-webkit-file-upload-button:hover {
  background: var(--cyan-bright);
}

.error-text { color: var(--coral); font-size: 15px; margin-top: 10px; }

.format-toggle { display: inline-flex; background: var(--void); border: 1px solid var(--line); border-radius: 8px; padding: 2px; margin-top: 18px; margin-right: 10px; }
.format-opt { background: none; border: none; color: var(--muted); padding: 7px 14px; border-radius: 6px; font-size: 15px; cursor: pointer; font-family: inherit; transition: background 0.15s ease, color 0.15s ease; }
.format-opt.active { background: var(--cyan); color: #04181C; font-weight: 600; }
.format-opt:not(.active):hover { color: var(--text); }
.format-opt:disabled { opacity: 0.4; cursor: not-allowed; }
.format-opt:disabled:hover { color: var(--muted); }

/* ---- Cards / lists ---- */
.page-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 14px; margin-bottom: 20px; }
/* A page-head's action buttons (Provision Agent / Resolution / Generate
   report / Abort / Delete, etc.) as one wrapping group with a real gap
   instead of each .btn's own margin-top and incidental inline whitespace
   — the previous approach left an odd button (e.g. Delete) stranded
   alone on its own line, mis-aligned under whatever it happened to wrap
   below. Nested inside .page-head so it still wraps as its own unit at
   narrow widths rather than interleaving with the title panel/format
   toggle. */
.page-head-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.page-head-actions .btn { margin-top: 0; }
.page-head-actions-divider { width: 1px; align-self: stretch; min-height: 24px; background: var(--line); }
.page-head h1 { font-size: 22px; margin: 0; }

.card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 18px 20px; margin-bottom: 14px;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
a.card:hover { border-color: var(--cyan); transform: translateY(-1px); }
.card-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.tag {
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.5px;
  padding: 3px 8px; border-radius: 999px; border: 1px solid var(--line); color: var(--muted);
}
/* .tag is also used on real <button> elements (Launch/Retest/History) —
   without this, the browser's native button chrome (a light/white default
   background) shows through instead of the transparent look every
   span-based .tag gets for free, since nothing here sets background on
   its own. Harmless on the coral Launch button (dark-on-white still
   reads), but --cyan-bright text on that native white background was
   nearly invisible for Retest.
   :not(.severity-badge) excludes the CVE badge (.tag.cve-badge.severity-
   badge.severity-X): button.tag's specificity (element+class) otherwise
   beats .severity-X's (class-only) regardless of source order, which was
   silently wiping out that badge's own background/color — confirmed live,
   not a hypothetical. */
button.tag:not(.severity-badge) { background: none; appearance: none; font: inherit; }
/* Animated "Thinking…"/"Drafting…" ellipsis — cycles . / .. / ... while an
   AI call is pending, instead of a static string. */
@keyframes ellipsis-cycle {
  0% { content: ''; }
  25% { content: '.'; }
  50% { content: '..'; }
  75%, 100% { content: '...'; }
}
.ellipsis-anim::after {
  content: '';
  display: inline-block;
  width: 1.4em;
  text-align: left;
  animation: ellipsis-cycle 1.1s steps(1) infinite;
}
/* AI assistant answer text: the model sometimes replies with long unbroken
   strings (paths, URLs) or fenced code blocks (commands, raw tool output)
   that would otherwise blow out the surrounding card's width. Wrapping
   prose breaks anywhere it must, and code gets its own bounded box that
   scrolls horizontally instead of stretching the page. */
.assistant-answer { overflow-wrap: anywhere; }
.assistant-answer pre {
  background: var(--void);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 8px 0;
  max-width: 100%;
  overflow-x: auto;
}
.assistant-answer code {
  font-family: var(--font-mono);
  font-size: 14px;
}
.assistant-answer pre code {
  white-space: pre;
}
.tag.status-draft { color: var(--muted); border-color: var(--line); }
.tag.status-authorized { color: var(--cyan-bright); border-color: var(--cyan); }
.tag.status-running { background: var(--cyan); border-color: var(--cyan); color: #04181c; font-weight: 700; animation: status-running-pulse 1.8s ease-in-out infinite; }
@keyframes status-running-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(19, 168, 196, 0.55); }
  50% { box-shadow: 0 0 0 7px rgba(19, 168, 196, 0); }
}
.tag.status-paused { color: #ffd35a; border-color: #ffd35a; }
.tag.status-completed { background: var(--mint); border-color: var(--mint); color: #04241a; font-weight: 700; }
.tag.status-aborted, .tag.status-blocked_ack, .tag.status-blocked_scope { background: var(--coral); border-color: var(--coral); color: #2b0805; font-weight: 700; }

/* Engagement-detail header badges (In-Scope IP/Hosts, Selected Modules):
   ID selectors here so these override .btn:hover's default solid
   cyan-bright fill with a lighter tint of their own blue instead — see
   renderEngagementDetail in js/app.js. */
#inScopeTargetsBtn:hover, #selectedModulesBtn:hover { background: rgba(19, 168, 196, 0.22); transform: translateY(-1px); }

.empty-state { color: var(--muted); text-align: center; padding: 60px 20px; border: 1px dashed var(--line); border-radius: 12px; }

/* ---- New engagement wizard ---- */
.scope-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.scope-row input { flex: 1; }
/* The CIDR/domain field held no cap of its own — on the wizard's
   full-width card that let it balloon out while its sibling .scope-label
   sat pinned to an inline 180px, an imbalanced pairing for two fields
   that each hold a single short token. flex-basis 0 (not auto) so the
   two split the row's available width evenly regardless of each one's
   content/placeholder length, rather than a fixed cap that leaves empty
   space on a wide card. */
.scope-row .scope-cidr, .scope-row .scope-label { flex: 1 1 0; }
.scope-row button { background: none; border: none; color: var(--coral); cursor: pointer; font-size: 20px; }

.module-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; margin-top: 8px; }
.module-check {
  display: flex; align-items: flex-start; gap: 10px; background: var(--void);
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; cursor: pointer;
}
.module-check.disruptive { border-color: rgba(255,107,87,0.4); }
.module-check input { width: auto; margin-top: 3px; }
.module-check .m-name { font-size: 15px; font-weight: 600; }
.module-check .m-meta { font-size: 13px; color: var(--muted); margin-top: 2px; }

.module-category-row { padding: 14px 16px; border-width: 1px; transition: border-color 0.15s ease; }
.module-category-row:hover { border-color: var(--cyan); }
/* Routed Egress category row (frontend/js/app.js#updateRoutedEgressCategoryState)
   — locked out while the "Enable routed egress" checkbox above is off,
   since none of its modules can actually route through a fresh circuit
   until that's on. */
.module-category-row.category-disabled { opacity: 0.5; cursor: not-allowed; }
.module-category-row.category-disabled:hover { border-color: var(--line); }
.module-category-row .m-name { font-size: 15px; font-weight: 600; }
/* Selection state — same palette the rest of the app already uses for
   success/warning/danger (mint/high-orange/coral), not a new color. */
.module-category-row.sel-all { border-color: var(--mint); }
.module-category-row.sel-partial { border-color: #ff9a5a; }
.module-category-row.sel-none { border-color: var(--coral); }

.risk-toggle { display: flex; gap: 10px; margin-top: 8px; }
.risk-option {
  flex: 1; border: 1px solid var(--line); border-radius: 10px; padding: 14px; cursor: pointer;
}
.risk-option.selected { border-color: var(--cyan); background: rgba(19,168,196,0.08); }
.risk-option.aggressive.selected { border-color: var(--coral); background: rgba(255,107,87,0.08); }
.risk-option h3 { margin: 0 0 4px; font-size: 16px; }
.risk-option p { margin: 0; font-size: 14px; color: var(--muted); }

.ack-box { background: var(--void); border: 1px solid var(--line); border-radius: 10px; padding: 16px; margin-top: 16px; }
.ack-item { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 10px; font-size: 15px; }
.ack-item input { width: auto; margin-top: 3px; }

/* ---- Event feed ---- */
.event-feed { font-family: var(--font-mono); font-size: 14.5px; background: var(--void); border: 1px solid var(--line); border-radius: 10px; padding: 14px; max-height: 460px; overflow-y: auto; }
.event-line { padding: 5px 0; border-bottom: 1px solid #12181c; display: flex; gap: 10px; }
.event-line .ts { color: var(--muted); flex-shrink: 0; }
.event-line.level-finding { color: var(--coral); }
.event-line.level-blocked, .event-line.level-error { color: #ff9a8a; }
.event-line.level-info { color: var(--text); }

.severity-badge {
  display: inline-block; padding: 4px 11px; border-radius: 12px;
  font-size: 13px; font-weight: 700; letter-spacing: 0.5px; font-family: var(--font-mono);
}
.severity-critical { background: var(--coral); color: #2b0805; }
.severity-high { background: #ff9a5a; color: #2b1503; }
.severity-medium { background: #ffd35a; color: #2b2103; }
.severity-low { background: var(--mint); color: #04241a; }
.severity-info { background: var(--line); color: var(--muted); }

/* ---- Stat tiles (engagement summary row) ----
   Neutral panel background + a colored top border + colored value text as
   the accent, rather than a full solid-color fill per tile — several
   adjacent severity hues as large fill blocks read poorly for color-vision
   deficiency; keeping the fill neutral and pairing every tile with its own
   plain-text label (never color alone) is the mitigation the accent color
   still needs even reused at this size. Colors reuse the existing
   severity-badge palette above rather than introducing a second one. */
.stat-tile-row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 22px; }
.stat-tile {
  flex: 1; min-width: 108px; background: var(--panel); border: 1px solid var(--line);
  border-top: 3px solid var(--accent, var(--line)); border-radius: 10px; padding: 14px 16px;
}
/* Severity/total tiles double as a filter control for the findings table
   below — clickable ones get a hover lift (same affordance as .card) and
   the currently-selected one gets a visible ring, since color-alone would
   fail the same CVD check as everything else in this row. */
.stat-tile-clickable { cursor: pointer; transition: border-color 0.15s ease, transform 0.1s ease; }
.stat-tile-clickable:hover { border-color: var(--accent, var(--cyan)); transform: translateY(-1px); }
.stat-tile-active { box-shadow: 0 0 0 2px var(--accent, var(--cyan)) inset; }

/* Compact version of the same summary for the engagements list card —
   full stat tiles would be too heavy repeated per row; same labels/colors,
   inline instead of boxed. */
.mini-stat-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); font-size: 14px; }
.mini-stat { color: var(--muted); white-space: nowrap; }
.mini-stat b { color: var(--text); font-variant-numeric: proportional-nums; }
.stat-tile .stat-value {
  font-size: 28px; font-weight: 700; line-height: 1.1; color: var(--accent, var(--text));
  font-variant-numeric: proportional-nums;
  overflow-wrap: anywhere; /* safety net — a word-value tile (risk rating) wraps instead of overflowing the box */
}
/* Word values (e.g. risk rating's "CRITICAL") need a visibly smaller size
   than a 1-3 digit count at the same weight, or long ones overflow the
   tile — numeric tiles don't get this class. */
.stat-tile .stat-value.stat-value-text { font-size: 20px; }
.stat-tile .stat-label {
  font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; margin-top: 6px;
}

.node-status-badge {
  display: inline-block; padding: 2px 9px; border-radius: 10px;
  font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.3px;
}
.node-status-online { background: var(--mint); color: #04241a; }
.node-status-busy { background: var(--cyan); color: #04181c; }
.node-status-offline { background: var(--line); color: var(--muted); }

.admin-sidebar { width: 190px; flex-shrink: 0; }
.admin-side-group-label {
  font-size: 13px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--cyan-bright); font-weight: 700;
  margin: 20px 0 8px; padding-left: 12px; padding-top: 16px; border-top: 1px solid var(--line);
}
.admin-side-group-label:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.admin-side-link {
  display: block; width: 100%; text-align: left;
  background: none; border: none; border-radius: 8px;
  padding: 9px 12px; margin-bottom: 2px; cursor: pointer;
  font-size: 15.5px; font-family: inherit; color: var(--muted);
  transition: background 0.15s ease, color 0.15s ease;
}
.admin-side-link:hover { background: var(--panel-2); color: var(--text); }
.admin-side-link.active { background: var(--mint); color: #04181C; font-weight: 600; }
@media (max-width: 720px) {
  .admin-sidebar { display: flex; flex-wrap: wrap; width: 100%; gap: 4px; }
  .admin-side-group-label { display: none; }
}

.section-title { font-size: 15px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin: 26px 0 10px; }
/* Collapsible section headers (<summary class="section-title">) are meant
   to be clicked to toggle, not dragged/double-clicked to select — without
   this, a slightly-off click reads as a text selection and highlights the
   label in the browser's default selection color instead of just
   expanding the section. */
summary.section-title { user-select: none; }

/* ---- Modal (replaces native prompt()/confirm() for destructive actions —
   those can be silenced via a browser setting, so anything that must not
   be skippable needs to render as real page content instead) ---- */
.modal-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(5, 7, 8, 0.72);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  animation: modalOverlayFade 0.18s ease-out;
}
.modal-box {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 24px;
  width: 100%;
  max-width: 420px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  /* --modal-dx/dy let a caller "grow" the box out of a specific element
     (see app.js#showModal's originEl param) instead of the generic pop
     from center — they default to 0px so every other showModal() call
     keeps that plain centered grow. */
  animation: modalGrow 0.32s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes modalOverlayFade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes modalGrow {
  from {
    opacity: 0;
    transform: translate(var(--modal-dx, 0px), var(--modal-dy, 0px)) scale(0.1);
  }
  to {
    opacity: 1;
    transform: translate(0, 0) scale(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  .modal-overlay, .modal-box { animation: none; }
}
.modal-title { font-size: 18px; font-weight: 600; margin-bottom: 12px; color: var(--text); }
.modal-body { font-size: 15.5px; color: var(--muted); line-height: 1.5; }
.modal-body p { margin: 0 0 10px; }
.modal-body p:last-child { margin-bottom: 0; }
.modal-input {
  width: 100%; margin-top: 14px; padding: 10px 12px;
  background: var(--void); border: 1px solid var(--line); border-radius: 8px;
  color: var(--text); font-size: 15.5px; font-family: var(--font-mono);
}
.modal-input:focus { outline: none; border-color: var(--cyan); }
.modal-error {
  margin-top: 12px; padding: 10px 12px; border-radius: 8px;
  background: rgba(255, 107, 87, 0.1); border: 1px solid var(--coral);
  color: var(--coral); font-size: 14.5px;
}
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

/* ---- Capability demo modal (app.js#openHomeDemoModal) ---- */
/* Own box size rather than showModal()'s 420px/720px options — this needs
   room for a 970-unit-wide SVG graph plus its one-line intro sentence
   (see .home-demo-intro-nowrap below), and no confirm/cancel footer at all
   (it's a viewer, not a confirmation), so it's a bespoke overlay/box
   rather than a reuse of the confirm-dialog-shaped showModal(). */
.home-demo-box { position: relative; max-width: 1200px; width: 100%; max-height: 88vh; overflow-y: auto; }
/* Opt-in for a demo whose content doesn't need the full 970-unit-wide SVG
   allowance above (app.js#HOME_DEMOS `narrow` flag) — the chat/timeline
   demos (Ask Reef AI, Retest & Resolution, VPN/Proxy Rotation, all capped
   at .demo-chat/.demo-timeline's own 640px max-width) and Stealth Mode
   (viewBox 620 wide). Only Attack Path actually needs the wide box, for
   its 970-unit graph. Without this, these demos get stretched across
   ~972px of content width they were never drawn for, reading as mostly
   empty space around a narrow column of content. .home-demo-intro's own
   640px cap already governs the intro's wrap point either way, so
   narrowing the box doesn't change how that text wraps. */
.home-demo-box-narrow { max-width: 700px; }
.home-demo-close {
  position: absolute; top: 16px; right: 16px; width: 32px; height: 32px; border-radius: 8px;
  background: var(--void); border: 1px solid var(--line); color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: color 0.15s ease, border-color 0.15s ease;
}
.home-demo-close:hover { color: var(--coral); border-color: var(--coral); }
.home-demo-intro { color: var(--muted); font-size: 15px; line-height: 1.55; max-width: 640px; margin-bottom: 18px; }
/* Opt-in for a demo whose intro reads better as one line than wrapped —
   currently just Attack Path's (app.js#HOME_DEMOS `nowrap` flag), measured
   to fit inside .home-demo-box's 1200px cap (1152px content area after
   padding) with margin across common UI sans-serif fonts. Falls back to
   normal wrapping the moment the box can't actually be at that full
   width, rather than a fixed mobile-only breakpoint, since a narrower box
   would otherwise force this long a sentence off the edge. */
.home-demo-intro-nowrap { white-space: nowrap; max-width: none; }
@media (max-width: 1240px) {
  /* 1200px box cap + .modal-overlay's 20px side padding on each edge —
     below this viewport width the box can't actually reach 1200px. */
  .home-demo-intro-nowrap { white-space: normal; }
}
.atkpath-legend { display: flex; flex-wrap: wrap; gap: 18px; margin-bottom: 18px; }
.atkpath-legend span { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--muted); font-family: var(--font-mono); }
.atkpath-legend .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.atkpath-wrap { overflow-x: auto; padding-bottom: 4px; }
.atkpath-svg { display: block; width: 100%; min-width: 760px; height: auto; overflow: visible; }
.atkpath-label { font-family: var(--font-mono); font-size: 12px; font-weight: 700; letter-spacing: 0.5px; }
.atkpath-sublabel { font-family: var(--font-mono); font-size: 11px; fill: var(--muted); text-anchor: middle; }
.atkpath-node { transform-box: fill-box; transform-origin: center; animation: atkpath-node-in 0.4s ease-out both; }
@keyframes atkpath-node-in { from { opacity: 0; transform: scale(0.4); } to { opacity: 1; transform: scale(1); } }
.atkpath-edge { animation: atkpath-edge-in 0.5s ease-out forwards; }
@keyframes atkpath-edge-in { to { stroke-dashoffset: 0; } }
.atkpath-caption {
  opacity: 0; animation: atkpath-caption-in 0.6s ease-out forwards;
}
.atkpath-caption text { font-family: var(--font-mono); font-size: 11px; font-style: italic; fill: var(--muted); }
@keyframes atkpath-caption-in { to { opacity: 1; } }
/* Network Topology Mapper demo's edge reveal — a plain fade rather than
   .atkpath-edge's stroke-draw, since a real topology edge is two <line>s
   (an invisible wide hit-box plus the visible thin one — js/
   networkTopologyGraph.js#renderTopologyEdge), not the single <path>
   .atkpath-edge's stroke-dasharray/dashoffset animation is built for. */
.ntg-demo-edge-in { opacity: 0; animation: atkpath-caption-in 0.5s ease-out forwards; }
@media (prefers-reduced-motion: reduce) {
  .atkpath-node, .atkpath-edge, .atkpath-caption, .ntg-demo-edge-in { animation: none; opacity: 1; }
}
@media (max-width: 700px) {
  .home-demo-box { padding: 20px; }
  .home-demo-intro { padding-right: 30px; }
}

/* ---- Chat + timeline demo variants (app.js#renderAskReefAiDemo,
   #renderRetestResolutionDemo, #renderVpnRotationDemo) — reuse
   .sonar-signal-in for the staggered reveal instead of a second entrance
   animation. ---- */
.demo-chat { display: flex; flex-direction: column; gap: 14px; max-width: 640px; }
.demo-chat-msg {
  padding: 14px 16px; border-radius: 12px; border: 1px solid var(--line);
}
.demo-chat-msg b { display: flex; align-items: center; font-size: 13px; font-family: var(--font-mono); font-weight: 700; letter-spacing: 0.5px; color: var(--muted); margin-bottom: 6px; }
.demo-chat-msg p { margin: 0; font-size: 15px; line-height: 1.55; color: var(--text); }
.demo-chat-msg code { font-family: var(--font-mono); font-size: 13.5px; background: rgba(255,255,255,0.06); padding: 1px 5px; border-radius: 4px; }
.demo-chat-user { background: var(--void); align-self: flex-end; }
.demo-chat-ai { background: rgba(127,231,255,0.06); border-color: rgba(127,231,255,0.25); }

.demo-timeline { display: flex; flex-direction: column; gap: 12px; max-width: 640px; }
.demo-timeline-step {
  display: flex; align-items: baseline; gap: 14px; padding: 12px 16px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--void);
}
.demo-timeline-step .tag { flex-shrink: 0; font-family: var(--font-mono); font-size: 11.5px; }
.demo-timeline-step p { margin: 0; font-size: 14.5px; line-height: 1.5; color: var(--muted); }
@media (max-width: 560px) {
  .demo-timeline-step { flex-direction: column; align-items: flex-start; gap: 6px; }
}

/* ---- "See how it works" scenario modal (app.js#openHowItWorksModal) ----
   A big, deliberately roomier variant of .home-demo-box: a vertical
   timeline with a glowing progress line that fills as each stage reveals,
   telling one complete engagement top to bottom rather than one isolated
   capability. Stage visuals reuse .sonar-signal-in / .demo-timeline-step /
   .sonar-mock-verdict / .tag from elsewhere on this page; only the track,
   node, chip, terminal, and chain pieces below are new. */
.scenario-box { max-width: 1040px; width: 100%; }
.scenario { position: relative; padding-left: 54px; margin-top: 22px; }
.scenario-line {
  position: absolute; left: 21px; top: 4px; bottom: 4px; width: 2px;
  background: var(--line); border-radius: 2px;
}
.scenario-line-fill {
  position: absolute; left: 0; top: 0; width: 100%; height: 0;
  background: linear-gradient(var(--cyan-bright), var(--mint));
  box-shadow: 0 0 12px rgba(127,231,255,0.55);
  animation: scenario-line-fill linear forwards;
}
@keyframes scenario-line-fill { to { height: 100%; } }
.scenario-stage { position: relative; margin-bottom: 36px; }
.scenario-stage:last-child { margin-bottom: 0; }
.scenario-node {
  position: absolute; left: -54px; top: 0; width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; z-index: 2;
  background: var(--panel); border: 2px solid var(--c, var(--cyan)); color: var(--c, var(--cyan));
  font-family: var(--font-mono); font-weight: 700; font-size: 14px;
  box-shadow: 0 0 0 4px var(--void);
}
.scenario-kicker {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-family: var(--font-mono); font-size: 11.5px; font-weight: 700; letter-spacing: 1.5px;
  color: var(--c, var(--cyan)); margin-bottom: 6px;
}
.scenario-badge {
  font-size: 10px; font-weight: 700; letter-spacing: 1px; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px;
}
.scenario-body h3 { font-size: 19px; margin: 0 0 8px; }
.scenario-body p { font-size: 15px; color: var(--muted); line-height: 1.55; margin: 0 0 14px; max-width: 560px; }
.scenario-body code { font-family: var(--font-mono); font-size: 13.5px; background: rgba(255,255,255,0.06); padding: 1px 5px; border-radius: 4px; }

.scenario-checklist { display: flex; flex-direction: column; gap: 8px; font-size: 14.5px; color: var(--text); }
.scenario-terminal {
  font-family: var(--font-mono); font-size: 14px; color: var(--mint);
  background: var(--void); border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px;
  display: inline-block;
}
.scenario-terminal-prompt { color: var(--cyan-bright); margin-right: 8px; }
.scenario-terminal-cursor { display: inline-block; width: 7px; height: 15px; margin-left: 4px; background: var(--mint); vertical-align: -2px; animation: scenario-cursor-blink 1s step-end infinite; }
@keyframes scenario-cursor-blink { 50% { opacity: 0; } }
.scenario-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.scenario-chip {
  font-family: var(--font-mono); font-size: 13px; color: var(--cyan-bright);
  border: 1px solid rgba(127,231,255,0.35); background: rgba(127,231,255,0.06);
  border-radius: 999px; padding: 5px 12px;
}
.scenario-chain { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 13.5px; }
.scenario-chain svg { color: var(--muted); flex-shrink: 0; }
.scenario-chain-fact { color: var(--cyan-bright); }
.scenario-chain-module { color: var(--mint); }
.scenario-chain-dead .scenario-chain-fact { color: var(--muted); }
.scenario-chain-dead .scenario-chain-module { color: var(--muted); font-style: italic; }

.scenario-footer { display: flex; justify-content: center; margin-top: 30px; }

@media (prefers-reduced-motion: reduce) {
  .scenario-terminal-cursor { animation: none; }
}
@media (max-width: 700px) {
  .scenario { padding-left: 42px; }
  .scenario-node { left: -42px; width: 32px; height: 32px; font-size: 12.5px; }
  .scenario-line { left: 15px; }
}

/* ---- Stealth Mode demo (app.js#renderStealthModeDemo) ---- */
.stealth-chart-svg { display: block; width: 100%; min-width: 560px; height: auto; overflow: visible; }
.stealth-lane-label { font-family: var(--font-mono); font-size: 12px; font-weight: 700; letter-spacing: 1px; }
.stealth-threshold-label { font-family: var(--font-mono); font-size: 10px; fill: var(--muted); }
.stealth-bar {
  transform-box: fill-box; transform-origin: center bottom;
  animation: stealth-bar-in 0.35s ease-out both;
}
@keyframes stealth-bar-in { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) {
  .stealth-bar { animation: none; }
}
.stealth-outcome .tag { font-size: 14px; padding: 5px 12px; }
.stealth-outcome p { font-size: 16px; }

/* ---- Remaining HOME_FEATURES demo visuals (app.js#renderRuleChainingDemo,
   #renderWhiteLabelDemo, #renderScheduledEngagementsDemo) — the other four
   new demos (ATT&CK/Compliance, Ad-Hoc Toolbox, Tenant Isolation,
   Cloud+On-Prem) reuse .demo-timeline/.scenario-terminal/.scenario-chips
   as-is, so only these three needed anything new. ---- */
.chain-demo { display: flex; flex-direction: column; gap: 14px; max-width: 640px; }
.chain-demo .demo-timeline-step { margin-top: 4px; }

.brand-demo { display: flex; align-items: center; gap: 16px; max-width: 640px; flex-wrap: wrap; }
.brand-mock { flex: 1 1 180px; border: 1px solid var(--line); border-radius: 12px; background: var(--void); padding: 16px; }
.brand-mock-badge { font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: 1px; color: var(--muted); margin-bottom: 10px; }
.brand-mock-logo {
  width: 36px; height: 36px; border-radius: 8px; margin-bottom: 12px;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-weight: 700; font-size: 13px;
  background: rgba(19,168,196,0.1); border: 1px solid rgba(19,168,196,0.3); color: var(--cyan);
}
.brand-mock-title { font-size: 15px; font-weight: 600; color: var(--text); margin-bottom: 14px; }
.brand-mock-bar { height: 6px; border-radius: 3px; }
.brand-mock-arrow { color: var(--muted); flex-shrink: 0; }

.cadence-row { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.cadence-day {
  width: 42px; height: 42px; border-radius: 10px; border: 1px solid var(--line); background: var(--void);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 11px; font-weight: 700; color: var(--muted);
}
.cadence-day.active { color: #04181c; border-color: var(--cyan-bright); background: var(--cyan-bright); }
.cadence-day.cadence-pulse { animation: cadence-pulse-in 0.45s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
@keyframes cadence-pulse-in { from { transform: scale(0.5); opacity: 0; box-shadow: 0 0 0 8px rgba(127,231,255,0.3); } to { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 rgba(127,231,255,0); } }
@media (prefers-reduced-motion: reduce) {
  .cadence-day.cadence-pulse { animation: none; }
}

/* ---- Pricing modal (app.js#openPricingModal) ---- */
.pricing-box { position: relative; max-width: 980px; width: 100%; }
.pricing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 8px; }
.pricing-card {
  position: relative; background: var(--void); border: 1px solid var(--line); border-radius: 12px;
  padding: 22px; display: flex; flex-direction: column;
}
.pricing-card-highlight { border-color: var(--cyan-bright); box-shadow: 0 0 0 1px rgba(127,231,255,0.25); }
.pricing-tag {
  position: absolute; top: -11px; left: 22px; font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 1px; color: #04181C; background: var(--cyan-bright); border-radius: 999px; padding: 3px 10px;
}
.pricing-card h3 { font-size: 19px; margin: 6px 0 6px; }
.pricing-blurb { font-size: 13.5px; color: var(--muted); line-height: 1.5; margin: 0 0 16px; min-height: 40px; }
.pricing-features { list-style: none; margin: 0 0 20px; padding: 0; flex: 1; display: flex; flex-direction: column; gap: 10px; }
.pricing-features li { display: flex; align-items: flex-start; gap: 8px; font-size: 13.5px; color: var(--text); line-height: 1.4; }
.pricing-features li svg { flex-shrink: 0; margin-top: 2px; color: var(--mint); }
.pricing-cta { width: 100%; margin-top: 0; text-align: center; justify-content: center; }
@media (max-width: 800px) {
  .pricing-grid { grid-template-columns: 1fr; }
  .pricing-blurb { min-height: 0; }
}

/* ---- Engagement status/progress ---- */
.progress-bar {
  width: 100%; height: 6px; border-radius: 3px;
  background: var(--void); border: 1px solid var(--line);
  overflow: hidden; margin: 14px 0 22px;
}
.progress-bar-fill {
  height: 100%; background: linear-gradient(90deg, var(--cyan), var(--mint));
  transition: width 0.4s ease;
  position: relative; overflow: hidden;
}
/* While the engagement is actively running, a light band sweeps across the
   filled portion — same "still processing" cue as the status-running pulse,
   applied to the bar so it reads even when jobs are queued and percent
   hasn't visibly moved yet. */
.progress-bar.is-running .progress-bar-fill::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  animation: progress-bar-shimmer 1.6s ease-in-out infinite;
}
@keyframes progress-bar-shimmer {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

/* ---- Ask Reef AI ---- */
.aai-layout { display: grid; grid-template-columns: 380px 1fr; gap: 20px; align-items: start; }
@media (max-width: 860px) {
  .aai-layout { grid-template-columns: 1fr; }
}
.aai-thread { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; margin: 10px 0; padding-right: 4px; }
.aai-turn { display: flex; flex-direction: column; max-width: 88%; }
.aai-turn.user { align-self: flex-end; align-items: flex-end; }
.aai-turn.assistant { align-self: flex-start; align-items: flex-start; }
.aai-turn.system { align-self: center; max-width: 100%; }
.aai-meta { font-family: var(--font-mono); font-size: 11px; color: var(--muted); margin-bottom: 4px; display: flex; gap: 8px; align-items: center; }
.aai-meta .who { color: var(--cyan-bright); font-weight: 600; }
.aai-turn.user .aai-meta .who { color: var(--mint); }
.aai-bubble { padding: 10px 13px; border-radius: 10px; font-size: 14.5px; line-height: 1.5; border: 1px solid var(--line); background: var(--panel-2); }
.aai-turn.user .aai-bubble { background: rgba(62, 217, 166, 0.08); border-color: rgba(62, 217, 166, 0.35); }
