Files
NeuroSploit/web/public/style.css
T
CyberSecurityUPandClaude Opus 5.5 79685cee49 feat: graph memory in the test loop, wire-bound evidence, authz matrix, report redaction
Harness
- graph_memory: typed attack graph (web + internal network) with Neo4j sync
  over the HTTP API. Graph checkpoints run after recon, validation, each
  chaining round and at the end. They direct agent selection and feed the
  live chains, next moves and dependency todolist into every prompt.
- wire: every reported exchange is checked against recorded flows and
  re-sent through the scope-guarded replay engine. A contradiction means the
  finding cannot stand as confirmed.
- traces: each agent's transcript is shared with the others as a fenced,
  structured digest (endpoints, params, error signatures, versions).
- authz_matrix: role x route authorization matrix validator, with
  identity tiers in creds.
- deliverables: evidence custody, exploit-as-test (honest oracles only),
  detection content and personas, with no credential leakage.
- redact: credentials quoted by agents become $SECRET_n in every
  human-facing report (md/html/json/sarif/typ, findings.md). findings.json
  and the vault keep raw values for re-testing.
- compliance: GDPR replaces LGPD.
- New agents: authorization_matrix, session_lifecycle, api_token_structure,
  exposed_datastore, postgres_superuser_rce, meta/graph_reasoning.

Web console
- New tabs, mounted in both the detail and live views: Attack graph
  (replaces Generative Attack Path Chaining), Chains, Dependencies,
  Traffic, Traces, Deliverables.
- Wire/Status columns, KPIs, CSV export.
- Graph recompute / Neo4j push / Re-test actions, all through the CLI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015v24fuCWxkanS31HDmaDB6
2026-10-11 19:20:29 -03:00

945 lines
62 KiB
CSS

/* NeuroSploit v4.2.1 — web console.
Visual direction: dense security-operations console (not a marketing SaaS
page). Borders over shadows, typography over color, a tokenised design system (spacing / type / radius / z-index / motion), one accent.
*/
:root {
/* spacing scale — 4/8/12/16/24/32/48/64 */
--sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
--sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
--radius-xs: 3px;
--radius-sm: 6px;
--radius-md: 10px;
--radius-pill: 999px;
/* type scale — six steps replace the ten ad-hoc sizes the UI grew. */
--fs-2xs: 10.5px; /* micro labels, badges */
--fs-xs: 11.5px; /* sub-text, meta, table cells */
--fs-sm: 12.5px; /* default UI text */
--fs-base: 13px; /* body / emphasis */
--fs-lg: 15px; /* section titles */
--fs-xl: 18px; /* card headings */
--fs-2xl: 26px; /* large stats */
--fs-display: 30px; /* hero numbers */
/* motion */
--t-fast: .12s ease;
--t-base: .18s ease;
/* stacking order — one scale instead of scattered magic numbers. */
--z-sticky: 10;
--z-header: 20;
--z-overlay: 50;
--z-modal: 60;
--z-toast: 80;
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
--mono: "SF Mono", "Cascadia Code", "JetBrains Mono", Consolas, monospace;
/* light (default, explicit) */
--bg: #f5f4f1;
--surface: #ffffff;
--surface-2: #faf9f6;
--surface-3: #efeeea;
--border: #e1dfd8;
--border-strong: #cfccc3;
--text: #17161a;
--text-dim: #6b6862;
--text-faint: #9c988f;
--accent: #b5590a;
--accent-hover: #9a4b07;
--accent-contrast: #fff8f0;
--accent-soft: #fbe9d8;
--focus-ring: #b5590a;
--sev-critical-bg: #fbe0dc; --sev-critical-fg: #8a271a;
--sev-high-bg: #fbe6cf; --sev-high-fg: #8a4a0a;
--sev-medium-bg: #f8edc9; --sev-medium-fg: #715600;
--sev-low-bg: #dcecdd; --sev-low-fg: #235c34;
--sev-info-bg: #e2e6f0; --sev-info-fg: #333e5c;
--shadow-float: 0 12px 32px rgba(20, 16, 8, 0.16), 0 2px 6px rgba(20, 16, 8, 0.08);
--shadow-sm: 0 1px 2px rgba(0, 0, 0, .25);
/* The terminal keeps a dark ground in both themes — it renders the CLI's own
ANSI palette, which is authored for a dark background and washes out on a
light one. Only the chrome around it follows the theme. */
--term-bg: #131215;
--term-fg: #ddd9d2;
/* Terminal chrome stays dark in both themes, so these are fixed (defined
once, no dark override) — the console's own ANSI palette expects it. */
--term-fg-dim: #a5a099;
--term-fg-bright: #ffffff;
--term-title: #ded9d2;
--term-fill: rgba(255, 255, 255, .05);
--term-hover: rgba(255, 255, 255, .08);
--term-line: rgba(255, 255, 255, .12);
--term-dot: #6b6760;
--term-fg-faint: #8b8781;
--overlay-scrim: rgba(10, 9, 8, .45);
}
:root[data-theme="dark"] {
--bg: #121113;
--surface: #191819;
--surface-2: #1f1e20;
--surface-3: #262427;
--border: #2d2b2e;
--border-strong: #3c3a3d;
--text: #ece9e4;
--text-dim: #a19d95;
--text-faint: #6f6b64;
--accent: #e08a3e;
--accent-hover: #ec9c57;
--accent-contrast: #1a1208;
--accent-soft: #3a2a16;
--focus-ring: #e08a3e;
--sev-critical-bg: #3a1c17; --sev-critical-fg: #f0968a;
--sev-high-bg: #3a2914; --sev-high-fg: #eeae6a;
--sev-medium-bg: #362c10; --sev-medium-fg: #e2c568;
--sev-low-bg: #17301f; --sev-low-fg: #78c896;
--sev-info-bg: #232840; --sev-info-fg: #9aa6d8;
--shadow-float: 0 16px 40px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
--term-bg: #0f0e10;
--term-fg: #ddd9d2;
}
* { box-sizing: border-box; }
/* The [hidden] attribute must always win. Any component class that sets its
own `display` (buttons, chips, flex/grid containers, ...) has the SAME
specificity as the browser's built-in `[hidden] { display: none }` rule —
whichever is declared later in the cascade wins, which silently breaks
`el.hidden = true` on anything already styled with `display`. Force it. */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
background: var(--bg); color: var(--text); font-family: var(--sans);
font-size: var(--fs-base); line-height: 1.5; -webkit-font-smoothing: antialiased;
}
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; color: inherit; font-size: var(--fs-base); }
a { color: var(--accent); text-decoration: none; }
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* ============================================================ AppShell */
/* 100vh is wrong on mobile: it measures the viewport WITHOUT the browser's
collapsing address bar, so the bottom of the app sits under it and the
footer/CTA is unreachable until you scroll the chrome away. dvh tracks the
visible box; the vh line stays as the fallback for older engines. */
.app { display: flex; height: 100vh; height: 100dvh; overflow: hidden; }
.sidebar {
width: 248px; flex: none; background: var(--surface); border-right: 1px solid var(--border);
display: flex; flex-direction: column;
}
.sb-top {
display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-4) var(--sp-4) var(--sp-3);
}
.brand { display: flex; align-items: center; gap: var(--sp-2); font-weight: 600; font-size: var(--fs-base); letter-spacing: .01em; flex: 1; }
.brand .mark { width: 22px; height: 22px; border-radius: var(--radius-sm); background: var(--accent); color: var(--accent-contrast); display: flex; align-items: center; justify-content: center; font-size: var(--fs-sm); font-weight: 700; font-family: var(--mono); }
.icon-btn {
background: transparent; border: 1px solid transparent; color: var(--text-dim);
width: 26px; height: 26px; border-radius: var(--radius-sm); font-size: var(--fs-base);
display: flex; align-items: center; justify-content: center;
}
.icon-btn:hover { background: var(--surface-3); border-color: var(--border); color: var(--text); }
.sb-new {
margin: 0 var(--sp-4) var(--sp-4); padding: var(--sp-3) var(--sp-3);
border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
background: var(--surface); color: var(--text); font-size: var(--fs-sm); font-weight: 600; text-align: left;
}
.sb-new:hover { border-color: var(--accent); color: var(--accent); }
.sb-groups { flex: 1; overflow-y: auto; padding: 0 var(--sp-2) var(--sp-4); }
.sb-group-head {
display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-2);
font-size: var(--fs-xs); font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint);
cursor: pointer; user-select: none;
}
.sb-group-head .count { margin-left: auto; font-weight: 400; }
.sb-group-head .caret { font-size: var(--fs-xs); line-height: 1; transition: transform var(--t-base); }
.sb-group.collapsed .caret { transform: rotate(-90deg); }
.sb-group.collapsed .sb-items { display: none; }
.sb-run-row { position: relative; }
.sb-run-row .sb-run { padding-right: 26px; }
.sb-run-del { position: absolute; top: 6px; right: 4px; width: 20px; height: 20px; line-height: 1; padding: 0; border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text-faint); font-size: var(--fs-sm); cursor: pointer; opacity: 0; transition: opacity var(--t-fast), background var(--t-fast), color var(--t-fast); }
.sb-run-row:hover .sb-run-del { opacity: 1; }
.sb-run-del:hover { background: var(--sev-critical-bg); color: var(--sev-critical-fg); }
.sb-run { display: block; width: 100%; text-align: left; background: transparent; border: none; border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-2); color: var(--text); margin-bottom: 1px; }
.sb-run:hover { background: var(--surface-3); }
.sb-run.active { background: var(--accent-soft); }
.sb-run .name { font-size: var(--fs-sm); font-weight: 500; display: flex; align-items: center; gap: 6px; min-width: 0; }
.sb-run .name .label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* Every sidebar line clips inside the rail — a long target URL used to spill
past the sidebar border into the main pane. */
.sb-run .sub { display: block; font-size: var(--fs-xs); color: var(--text-faint); font-family: var(--mono); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-run .sub-facts { display: flex; justify-content: space-between; gap: var(--sp-2); }
.run-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.sev-dot-critical { background: var(--sev-critical-fg); }
.sev-dot-high { background: var(--sev-high-fg); }
.sev-dot-medium { background: var(--sev-medium-fg); }
.sev-dot-low { background: var(--sev-low-fg); }
.sev-dot-info { background: var(--sev-info-fg); }
.sb-steps { padding: var(--sp-1) var(--sp-2) var(--sp-2) var(--sp-5); display: flex; flex-direction: column; gap: 2px; }
.sb-step { font-size: var(--fs-xs); color: var(--text-faint); display: flex; align-items: center; gap: var(--sp-2); }
.sb-step::before { content: "○"; font-size: var(--fs-2xs); width: 10px; }
.sb-step.done { color: var(--text-dim); }
.sb-step.done::before { content: "●"; color: var(--sev-low-fg); }
.sb-step.active { color: var(--accent); font-weight: 600; }
.sb-step.active::before { content: "◐"; color: var(--accent); }
.sb-bottom { border-top: 1px solid var(--border); padding: var(--sp-3) var(--sp-4); display: flex; align-items: center; justify-content: space-between; }
.sb-version { font-size: var(--fs-xs); color: var(--text-faint); font-family: var(--mono); }
.sb-bottom-actions { display: flex; gap: var(--sp-1); }
.sb-link {
margin: 0 var(--sp-4) var(--sp-3); padding: var(--sp-2) var(--sp-3);
border: 1px solid transparent; border-radius: var(--radius-sm);
background: transparent; color: var(--text-dim); font-size: var(--fs-sm); text-align: left;
}
.sb-link:hover { background: var(--surface-3); color: var(--text); }
.sb-search { position: relative; margin: 0 var(--sp-4) var(--sp-3); }
.sb-search input { padding-left: 26px; font-size: var(--fs-sm); }
.sb-search .search-icon { left: 8px; }
/* Runs are grouped into one folder per target: twelve rows of near-identical
URLs was a wall to scroll past, and the host is what an operator scans for. */
.sb-folder { margin-bottom: 2px; }
.sb-folder-head {
display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2);
border-radius: var(--radius-sm); cursor: pointer; user-select: none; font-size: var(--fs-sm);
}
.sb-folder-head:hover { background: var(--surface-3); }
.sb-folder-head .caret { font-size: var(--fs-xs); line-height: 1; color: var(--text-faint); transition: transform var(--t-base); }
.sb-folder-head .fname { flex: 1; min-width: 0; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-folder-head .fmeta { font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-faint); }
.sb-folder.collapsed .caret { transform: rotate(-90deg); }
.sb-folder.collapsed .sb-items { display: none; }
.sb-folder .sb-items { padding-left: var(--sp-3); border-left: 1px solid var(--border); margin-left: 9px; }
.sb-empty { padding: var(--sp-4) var(--sp-2); font-size: var(--fs-sm); color: var(--text-faint); }
/* ============================================================ Dashboard */
.dashboard { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.dashboard[hidden] { display: none; }
.dash-range { width: auto; padding: 6px 8px; font-size: var(--fs-sm); }
.dash-body { flex: 1; overflow-y: auto; padding: var(--sp-5); }
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-4); }
.stat-tile { border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--sp-4); background: var(--surface); }
.stat-k { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 600; }
.stat-v { font-size: var(--fs-display); font-weight: 650; line-height: 1.15; margin-top: 2px; font-variant-numeric: tabular-nums; }
.stat-unit { font-size: var(--fs-lg); color: var(--text-faint); font-weight: 500; }
.stat-sub { font-size: var(--fs-xs); color: var(--text-dim); margin-top: 2px; }
/* The score tile carries a status color, so it also carries a word — the band
label — because color alone is not an encoding. */
.stat-score.sev-critical { border-color: var(--sev-critical-fg); }
.stat-score.sev-critical .stat-v { color: var(--sev-critical-fg); }
.stat-score.sev-high { border-color: var(--sev-high-fg); }
.stat-score.sev-high .stat-v { color: var(--sev-high-fg); }
.stat-score.sev-medium { border-color: var(--sev-medium-fg); }
.stat-score.sev-medium .stat-v { color: var(--sev-medium-fg); }
.stat-score.sev-low { border-color: var(--sev-low-fg); }
.stat-score.sev-low .stat-v { color: var(--sev-low-fg); }
.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--sp-3); }
.dash-card { border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--sp-4); background: var(--surface); }
.dash-card h3 { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); font-weight: 600; }
.dash-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.dash-card-head h3 { margin: 0; }
.dash-wide { grid-column: 1 / -1; }
.dash-sub { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 600; margin: var(--sp-4) 0 var(--sp-2); }
.dash-foot { margin-top: var(--sp-4); font-size: var(--fs-xs); color: var(--text-faint); }
.dash-table td.mono { font-family: var(--mono); font-size: var(--fs-xs); }
.dash-table { min-width: 480px; }
/* Bars: thin marks, value labeled on every row, recessive track. */
.bar-row { display: grid; grid-template-columns: 90px 1fr 42px; align-items: center; gap: var(--sp-3); padding: 3px 0; font-size: var(--fs-sm); }
.bar-label { color: var(--text-dim); text-transform: capitalize; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bar-track { height: 10px; background: var(--surface-3); border-radius: var(--radius-xs); overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: var(--radius-xs); background: var(--text-faint); }
.bar-critical { background: var(--sev-critical-fg); }
.bar-high { background: var(--sev-high-fg); }
.bar-medium { background: var(--sev-medium-fg); }
.bar-low { background: var(--sev-low-fg); }
.bar-info { background: var(--sev-info-fg); }
.bar-neutral { background: var(--accent); }
.bar-value { font-family: var(--mono); font-size: var(--fs-xs); text-align: right; color: var(--text); }
.fair-hero { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-4); background: var(--surface-2); }
.fair-range { display: flex; gap: var(--sp-5); flex-wrap: wrap; align-items: baseline; }
.fair-point { display: flex; flex-direction: column; }
.fair-point .k { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }
.fair-point .v { font-size: var(--fs-xl); font-weight: 600; font-variant-numeric: tabular-nums; }
.fair-likely .v { font-size: var(--fs-display); color: var(--accent); }
.fair-note { font-size: var(--fs-xs); color: var(--text-dim); margin-top: var(--sp-3); }
.contrib-row { display: flex; align-items: center; gap: var(--sp-3); padding: 5px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.contrib-row:last-child { border-bottom: none; }
/* Clickable contributor: a full-width button that reads like a row but opens
the finding. */
button.contrib-row { width: 100%; text-align: left; background: transparent; border-left: none; border-right: none; border-top: none; color: var(--text); cursor: pointer; padding: 5px var(--sp-2); margin: 0 calc(-1 * var(--sp-2)); border-radius: var(--radius-xs); transition: background var(--t-fast); }
button.contrib-row:hover { background: var(--surface-3); }
button.contrib-row:hover .contrib-title { color: var(--accent); }
button.contrib-row .contrib-v { margin-left: auto; }
.contrib-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contrib-v { font-family: var(--mono); font-size: var(--fs-sm); }
.fair-params { display: flex; flex-direction: column; gap: var(--sp-2); }
.fair-param { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); }
.fair-param > span:first-child { width: 92px; flex: none; text-transform: capitalize; }
.fair-param input { flex: 1; font-family: var(--mono); font-size: var(--fs-sm); }
/* ============================================================ Main / Topbar */
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.topbar {
display: flex; align-items: center; gap: var(--sp-4); padding: var(--sp-3) var(--sp-5);
border-bottom: 1px solid var(--border); background: var(--bg); min-height: 56px;
}
.topbar-title { font-size: var(--fs-lg); font-weight: 600; }
.topbar-sub { font-size: var(--fs-xs); color: var(--text-faint); font-family: var(--mono); margin-top: 1px; }
.topbar-spacer { flex: 1; }
.btn {
border-radius: var(--radius-sm); border: 1px solid var(--border-strong); padding: var(--sp-2) var(--sp-4);
font-size: var(--fs-sm); font-weight: 500; background: var(--surface); color: var(--text);
display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap;
}
.btn:hover { border-color: var(--text-dim); }
.btn-sm { padding: 6px var(--sp-3); font-size: var(--fs-sm); }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); font-weight: 600; }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-danger { background: transparent; border-color: var(--sev-critical-fg); color: var(--sev-critical-fg); }
.btn-danger:hover { background: var(--sev-critical-bg); }
/* A paused run is a state to notice, not an error — the resume button wears
the warning hue so the run never looks quietly stalled. */
.btn-warn { background: var(--sev-medium-bg); border-color: var(--sev-medium-fg); color: var(--sev-medium-fg); }
.btn-warn:hover { background: var(--sev-medium-bg); filter: brightness(1.12); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
a.btn:disabled, a.btn[aria-disabled="true"] { pointer-events: none; }
.btn:focus-visible { outline-offset: 2px; }
/* ============================================================ Wizard */
.wizard { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.stepper {
display: flex; align-items: center; padding: 0 var(--sp-5); border-bottom: 1px solid var(--border);
background: var(--surface); overflow-x: auto;
}
.step-tab {
display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-4) var(--sp-4) var(--sp-3);
border-bottom: 2px solid transparent; color: var(--text-faint); font-size: var(--fs-sm); font-weight: 500;
background: transparent; border-top: none; border-left: none; border-right: none; white-space: nowrap;
}
.step-tab .n { font-family: var(--mono); font-size: var(--fs-xs); width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--border-strong); display: flex; align-items: center; justify-content: center; }
.step-tab.done .n { background: var(--sev-low-fg); border-color: var(--sev-low-fg); color: var(--term-fg-bright); }
.step-tab.done .n::before { content: "✓"; }
.step-tab.active { color: var(--text); border-bottom-color: var(--accent); }
.step-tab.active .n { border-color: var(--accent); color: var(--accent); }
.step-tab:disabled { cursor: default; }
.wizard-body { flex: 1; overflow-y: auto; padding: var(--sp-6) var(--sp-5) var(--sp-8); }
.wizard-panel { max-width: 780px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--sp-6); }
.wizard-panel-wide { max-width: none; gap: var(--sp-4); }
.wizard-panel[hidden] { display: none; }
.lead-tip { margin-top: calc(-1 * var(--sp-2)); }
.cat-match { font-size: var(--fs-2xs); color: var(--accent); font-family: var(--mono); }
.footer-actions { display: flex; gap: var(--sp-2); }
.field-group { display: flex; flex-direction: column; gap: var(--sp-2); }
.field-group + .field-group { margin-top: var(--sp-5); }
.field-label { font-size: var(--fs-xs); font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--text-faint); }
.field-help { font-size: var(--fs-xs); color: var(--text-faint); }
.field-error { font-size: var(--fs-xs); color: var(--sev-critical-fg); font-weight: 500; }
.field-error::before { content: "⚠ "; }
.field-group:has(.field-error:not([hidden])) input,
.field-group:has(.field-error:not([hidden])) textarea { border-color: var(--sev-critical-fg); }
.field-row { display: flex; gap: var(--sp-4); flex-wrap: wrap; }
.field-row > * { flex: 1; min-width: 160px; }
.section-title { font-size: var(--fs-base); font-weight: 600; }
.section-desc { font-size: var(--fs-sm); color: var(--text-dim); margin-top: 2px; }
input[type="text"], input[type="number"], input[type="password"], select, textarea {
border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: 8px 10px;
background: var(--surface); color: var(--text); width: 100%;
}
input:focus-visible, select:focus-visible, textarea:focus-visible { border-color: var(--accent); }
textarea { resize: vertical; min-height: 72px; }
.narrow { max-width: 120px; }
.mode-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--sp-3); }
.mode-tile {
border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-3);
background: var(--surface); text-align: left; display: flex; flex-direction: column; gap: 2px;
}
.mode-tile .t { font-weight: 600; font-size: var(--fs-sm); }
.mode-tile .d { font-size: var(--fs-xs); color: var(--text-faint); }
.mode-tile:hover { border-color: var(--text-dim); }
.mode-tile.selected { border-color: var(--accent); background: var(--accent-soft); }
.auth-mode-toggle { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; }
.auth-mode-toggle button { border: none; background: var(--surface); padding: 8px 14px; font-size: var(--fs-sm); color: var(--text-dim); }
.auth-mode-toggle button.selected { background: var(--accent); color: var(--accent-contrast); font-weight: 600; }
.auth-mode-toggle button:disabled { opacity: .45; cursor: not-allowed; }
.check-row { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--text-dim); }
.role-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.role-row { display: flex; gap: var(--sp-2); align-items: center; }
.role-row input { flex: 1; }
.role-row input.role-name { max-width: 140px; flex: none; }
/* leads step reuses category cards */
.lead-toolbar { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.search-wrap { position: relative; flex: 1; min-width: 200px; max-width: 320px; }
.search-icon { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); color: var(--text-faint); font-size: var(--fs-lg); line-height: 1; pointer-events: none; }
.search-wrap input { padding-left: 28px; }
.chips { display: flex; gap: var(--sp-2); }
.chip { border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-dim); border-radius: var(--radius-sm); padding: 6px 10px; font-size: var(--fs-xs); display: flex; gap: 5px; }
.chip span { color: var(--text-faint); font-family: var(--mono); }
.chip-active { border-color: var(--accent); color: var(--accent); }
.chip-active span { color: var(--accent); }
.categories { display: flex; flex-direction: column; gap: var(--sp-2); }
.cat-card { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.cat-head { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-3); cursor: pointer; background: var(--surface); }
.cat-head .cat-name { font-weight: 600; font-size: var(--fs-sm); flex: 1; }
.cat-head .cat-count { font-size: var(--fs-xs); color: var(--text-faint); font-family: var(--mono); }
.cat-head .caret { font-size: var(--fs-sm); line-height: 1; color: var(--text-faint); transition: transform var(--t-base); }
.cat-card.collapsed .caret { transform: rotate(-90deg); }
.cat-card.collapsed .agent-rows { display: none; }
.agent-rows { border-top: 1px solid var(--border); background: var(--surface-2); }
.agent-row { display: flex; align-items: center; gap: var(--sp-3); padding: 7px var(--sp-3); border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.agent-row:last-child { border-bottom: none; }
.agent-row.hidden-by-search { display: none; }
.agent-row .agent-title { flex: 1; }
.agent-row .agent-cwe { font-size: var(--fs-2xs); color: var(--text-faint); font-family: var(--mono); }
.switch { position: relative; width: 30px; height: 17px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch .track { position: absolute; inset: 0; background: var(--border-strong); border-radius: var(--radius-pill); transition: background var(--t-base); }
.switch .thumb { position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: var(--surface); transition: transform var(--t-base); box-shadow: var(--shadow-sm); }
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track + .thumb { transform: translateX(13px); }
/* partial selection (some agents on, not all) — reads as "partial", not "off" */
.switch input:indeterminate + .track { background: var(--border-strong); }
.switch input:indeterminate + .track + .thumb { transform: translateX(7px); background: var(--accent); }
.custom-leads { display: flex; flex-direction: column; gap: var(--sp-2); }
.custom-lead-chip { display: flex; align-items: center; gap: var(--sp-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px var(--sp-3); font-size: var(--fs-sm); background: var(--surface-2); }
.custom-lead-chip .x { margin-left: auto; color: var(--text-faint); }
.custom-lead-chip .x:hover { color: var(--sev-critical-fg); }
.wizard-footer {
display: flex; align-items: center; justify-content: space-between; padding: var(--sp-4) var(--sp-5);
border-top: 1px solid var(--border); background: var(--surface);
}
.summary-line { font-size: var(--fs-xs); color: var(--text-faint); }
.summary-line b { color: var(--text); font-weight: 600; }
.review-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--sp-4); }
.review-item { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-3); }
.review-item .k { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); }
.review-item .v { font-size: var(--fs-base); margin-top: 3px; font-weight: 500; }
.review-item .v.mono { font-family: var(--mono); font-size: var(--fs-sm); }
/* ============================================================ Live run / detail */
.runpage { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.runpage[hidden] { display: none; }
.run-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); padding: var(--sp-5) var(--sp-5) var(--sp-4); border-bottom: 1px solid var(--border); flex-wrap: wrap; }
/* The header is a title plus a row of buttons. Without a flex basis the
buttons win the width fight and the title wraps one character per line —
which is exactly what happened at 390px. */
.run-head-main { flex: 1 1 260px; }
.run-actions { flex: 0 0 auto; flex-wrap: wrap; }
.run-head-main { min-width: 0; }
.run-target { font-size: var(--fs-lg); font-weight: 600; font-family: var(--mono); overflow-wrap: anywhere; }
.run-meta { display: flex; align-items: center; gap: var(--sp-3); margin-top: var(--sp-1); font-size: var(--fs-sm); color: var(--text-dim); font-family: var(--mono); }
#liveTargetSub:empty, #detailTargetSub:empty { display: none; }
.run-facts { flex-wrap: wrap; font-size: var(--fs-xs); color: var(--text-faint); }
.run-facts span:not(:last-child)::after { content: " ·"; }
.phase-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); animation: pulse 1.4s infinite; }
.phase-dot.static { animation: none; }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.3} }
.run-actions { display: flex; gap: var(--sp-2); }
.progress-wrap { display: flex; align-items: center; gap: var(--sp-3); padding: 0 var(--sp-5) var(--sp-4); }
.progress-bar { flex: 1; height: 6px; border-radius: var(--radius-pill); background: var(--surface-3); border: 1px solid var(--border); overflow: hidden; }
.progress-fill { height: 100%; width: 0%; background: var(--accent); transition: width .3s; }
/* agent count unknown yet (still reconning) — slide a segment instead of
sitting at a static, easy-to-miss 0% fill */
.progress-bar.indeterminate .progress-fill { width: 30% !important; animation: progress-indeterminate 1.3s infinite linear; }
@keyframes progress-indeterminate { 0% { margin-left: -30%; } 100% { margin-left: 100%; } }
.progress-label { font-size: var(--fs-xs); color: var(--text-faint); font-family: var(--mono); white-space: nowrap; }
.run-tabs { display: flex; gap: var(--sp-1); padding: 0 var(--sp-5); border-bottom: 1px solid var(--border); }
.run-tab { padding: var(--sp-3) var(--sp-3); font-size: var(--fs-sm); font-weight: 500; color: var(--text-faint); border-bottom: 2px solid transparent; background: none; border-top: none; border-left: none; border-right: none; }
.run-tab.active { color: var(--text); border-bottom-color: var(--accent); }
.run-body { flex: 1; overflow-y: auto; padding: var(--sp-5); }
.run-tab-panel[hidden] { display: none; }
/* findings table */
/* Wide tables scroll inside their own box; without this the whole page
scrolled sideways below ~900px and the last columns were unreachable. */
.table-wrap { overflow-x: auto; }
.findings-toolbar { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.sev-summary { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }
.sev-pill {
border: 1px solid transparent; border-radius: var(--radius-sm); padding: 3px 9px;
font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
}
.sev-pill b { font-family: var(--mono); font-weight: 700; }
.sev-pill.picked { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); min-width: 720px; }
/* Sticky header: a 27-row findings table scrolls past its own column labels,
and severity/confidence are unreadable without them. */
.data-table th { position: sticky; top: 0; z-index: 1; background: var(--bg); text-align: left; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); font-weight: 600; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border-strong); white-space: nowrap; cursor: pointer; user-select: none; }
.data-table th:hover { color: var(--text); }
.data-table th.sorted { color: var(--accent); }
.data-table th.sorted[data-dir="asc"]::after { content: " ↓"; }
.data-table th.sorted[data-dir="desc"]::after { content: " ↑"; }
.data-table td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border); vertical-align: top; }
.data-table tbody tr { cursor: pointer; }
.data-table tbody tr:hover { background: var(--surface-2); }
/* Numbered, pasteable steps — the proof a reader can check without trusting
the report. */
.poc-steps { margin: var(--sp-2) 0 0; padding-left: 22px; display: flex; flex-direction: column; gap: var(--sp-2); }
.poc-steps li { font-size: var(--fs-sm); color: var(--text-dim); }
.poc-steps .step {
margin: 4px 0 0; background: var(--surface-2); border: 1px solid var(--border);
border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-3);
font-family: var(--mono); font-size: var(--fs-xs); color: var(--text);
white-space: pre-wrap; overflow-wrap: anywhere;
}
.poc-file { display: flex; align-items: center; gap: var(--sp-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2); }
.poc-file .fn { font-family: var(--mono); font-size: var(--fs-sm); flex: 1; }
.poc-pre { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--sp-3); font-family: var(--mono); font-size: var(--fs-xs); max-height: 220px; overflow: auto; white-space: pre-wrap; word-break: break-word; margin-top: var(--sp-2); }
/* Evidence prose carries raw cookies, tokens and URLs — single "words" wider
than the modal. Without a break they run under the modal's edge and the
tail of the value is simply unreadable. */
.fm-prose { font-family: var(--sans); font-size: var(--fs-sm); line-height: 1.6; color: var(--text-dim); white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--sp-1) 0; }
.review-item .v { overflow-wrap: anywhere; }
.data-table .col-endpoint { font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-dim); max-width: 260px; overflow: hidden; text-overflow: ellipsis; }
.data-table .col-conf { font-family: var(--mono); text-align: right; }
.empty-state { padding: var(--sp-7) var(--sp-5); text-align: center; color: var(--text-faint); font-size: var(--fs-sm); }
.sev { font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; padding: 3px 8px; border-radius: var(--radius-sm); letter-spacing: .02em; white-space: nowrap; }
.sev-critical { background: var(--sev-critical-bg); color: var(--sev-critical-fg); }
.sev-high { background: var(--sev-high-bg); color: var(--sev-high-fg); }
.sev-medium { background: var(--sev-medium-bg); color: var(--sev-medium-fg); }
.sev-low { background: var(--sev-low-bg); color: var(--sev-low-fg); }
.sev-info { background: var(--sev-info-bg); color: var(--sev-info-fg); }
.log-panel { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); max-height: 100%; overflow-y: auto; padding: var(--sp-3); }
.log-line { font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-dim); padding: 1px 0; white-space: pre-wrap; word-break: break-word; }
.log-tab-panel { display: flex; flex-direction: column; height: 100%; gap: var(--sp-2); }
.log-tab-panel .log-panel { flex: 1; }
.send-prompt-row { display: flex; align-items: center; gap: var(--sp-2); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-3); background: var(--surface); }
.send-prompt-row .repl-prompt { color: var(--accent); font-family: var(--mono); }
#sendPromptInput { flex: 1; border: none; background: transparent; font-family: var(--mono); font-size: var(--fs-sm); outline: none; color: var(--text); }
.log-echo { color: var(--accent); }
/* ============================================================ Modal (Auth & Keys) */
.modal-overlay { position: fixed; inset: 0; background: var(--overlay-scrim); display: flex; align-items: center; justify-content: center; z-index: var(--z-modal); }
.modal-overlay[hidden] { display: none; }
.modal { width: 620px; max-width: calc(100vw - 40px); max-height: calc(100vh - 80px); max-height: calc(100dvh - 80px); background: var(--surface); border-radius: var(--radius-md); box-shadow: var(--shadow-float); display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--border); }
.modal-sm { width: 520px; }
.modal-lg { width: 760px; }
.modal-foot { display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--border); }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border); }
.modal-head .title { font-size: var(--fs-lg); font-weight: 600; }
.modal-tabs { display: flex; gap: var(--sp-1); padding: 0 var(--sp-5); border-bottom: 1px solid var(--border); }
.modal-tab { padding: var(--sp-3) var(--sp-2); font-size: var(--fs-sm); font-weight: 500; color: var(--text-faint); border-bottom: 2px solid transparent; background: none; border-top: none; border-left: none; border-right: none; }
.modal-tab.active { color: var(--text); border-bottom-color: var(--accent); }
.modal-body { padding: var(--sp-5); overflow-y: auto; flex: 1; }
.modal-panel[hidden] { display: none; }
.field-status { font-size: var(--fs-xs); font-family: var(--mono); color: var(--text-faint); overflow-wrap: anywhere; }
.field-status.ok { color: var(--sev-low-fg); }
.field-status.bad { color: var(--sev-critical-fg); }
.provider-row { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) 0; border-bottom: 1px solid var(--border); }
.provider-row:last-child { border-bottom: none; }
.provider-row .p-name { flex: none; width: 150px; font-size: var(--fs-sm); font-weight: 500; }
.provider-row .p-kind { flex: none; width: 74px; font-size: var(--fs-2xs); text-transform: uppercase; color: var(--text-faint); font-family: var(--mono); }
.provider-row input { flex: 1; font-family: var(--mono); font-size: var(--fs-sm); }
.provider-row .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border-strong); flex: none; }
.provider-row .dot.set { background: var(--sev-low-fg); }
/* ============================================================ Terminal dock
Docked, not floating: it is a flex child of .main, so opening it shortens
the view above instead of covering the primary action the way the old
floating drawer covered "Next →" and "Start Exploitation". */
.term-dock {
/* A fixed 320px dock eats 82% of a phone in landscape. Track the viewport
and keep a floor that still shows a usable number of rows. */
position: relative; flex: none; height: clamp(180px, 42dvh, 340px); min-height: 140px;
display: flex; flex-direction: column; overflow: hidden;
background: var(--term-bg); color: var(--term-fg); border-top: 1px solid var(--border-strong);
}
.term-dock[hidden] { display: none; }
.term-dock.expanded { flex: 1; height: auto; }
.term-resize { position: absolute; top: 0; left: 0; right: 0; height: 6px; cursor: ns-resize; z-index: 2; touch-action: none; }
.term-resize:hover { background: var(--accent); opacity: .5; }
body.resizing-ns { user-select: none; cursor: ns-resize; }
.term-head {
display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); flex-wrap: wrap;
background: var(--term-fill); border-bottom: 1px solid var(--term-line);
font-size: var(--fs-xs); color: var(--term-fg-dim);
}
.term-head .icon-btn { color: var(--term-fg-dim); }
.term-head .icon-btn:hover { background: var(--term-hover); border-color: transparent; color: var(--term-fg-bright); }
.term-title { font-weight: 600; color: var(--term-title); }
.term-target {
width: auto; max-width: 260px; background: var(--term-fill); color: var(--term-title);
border: 1px solid var(--term-line); border-radius: var(--radius-sm); padding: 3px 6px; font-size: var(--fs-xs);
}
.term-status { font-family: var(--mono); font-size: var(--fs-xs); color: var(--term-fg-faint); }
.term-btn {
background: transparent; border: 1px solid var(--term-line); color: var(--term-fg-dim);
border-radius: var(--radius-sm); padding: 3px 8px; font-size: var(--fs-xs);
}
.term-btn:hover { background: var(--term-hover); color: var(--term-fg-bright); }
.term-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--term-dot); flex: none; }
.term-dot[data-state="on"] { background: var(--sev-low-fg); }
.term-dot[data-state="pending"] { background: var(--accent); animation: pulse 1.4s infinite; }
.term-dot[data-state="error"] { background: var(--sev-critical-fg); }
.term-alert { padding: var(--sp-2) var(--sp-3); font-size: var(--fs-xs); background: var(--sev-high-bg); color: var(--sev-high-fg); }
.term-alert[data-kind="error"] { background: var(--sev-critical-bg); color: var(--sev-critical-fg); }
.term-host { flex: 1; min-height: 0; padding: var(--sp-2) var(--sp-3) 0; }
.term-host .xterm { height: 100%; }
.term-hints { padding: 4px var(--sp-3) var(--sp-2); font-size: var(--fs-2xs); color: var(--term-fg-faint); display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.term-hints .k { font-family: var(--mono); color: var(--term-fg-dim); }
/* ============================================================ Toasts */
.toasts { position: fixed; right: var(--sp-5); bottom: var(--sp-5); z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--sp-2); max-width: 380px; }
.toast {
border: 1px solid var(--border-strong); border-left-width: 3px; border-radius: var(--radius-sm);
background: var(--surface); color: var(--text); padding: var(--sp-3) var(--sp-4); font-size: var(--fs-sm);
box-shadow: var(--shadow-float); cursor: pointer;
}
.toast-ok { border-left-color: var(--sev-low-fg); }
.toast-warn { border-left-color: var(--sev-high-fg); }
.toast-error { border-left-color: var(--sev-critical-fg); }
.toast-info { border-left-color: var(--accent); }
/* ============================================================ Responsive */
.sidebar-scrim {
display: none; position: fixed; inset: 0; z-index: var(--z-overlay); background: var(--overlay-scrim);
}
.sidebar-scrim[hidden] { display: none !important; }
.sidebar-toggle {
display: none; position: fixed; left: 10px; top: 10px; z-index: var(--z-modal);
width: 32px; height: 32px; border-radius: var(--radius-sm);
border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
}
/* Wide screens: line length, not width, is the constraint — a findings table
stretched across 2560px is harder to read, not easier. */
@media (min-width: 1600px) {
.dash-body > *, .run-body > * { max-width: 1500px; margin-left: auto; margin-right: auto; }
}
@media (max-width: 1200px) {
.sidebar { width: 216px; }
}
/* Tablet portrait and small laptops: the sidebar is the first thing to go,
because the main pane is where the work happens. */
@media (max-width: 900px) {
.sidebar-toggle { display: block; }
.sidebar {
position: fixed; left: -280px; top: 0; bottom: 0; width: min(280px, 82vw); z-index: var(--z-modal);
transition: left .2s ease; box-shadow: var(--shadow-float);
}
.sidebar.open { left: 0; }
/* Tapping the page behind an open drawer must close it — otherwise the only
way out is the same small button that opened it. */
.sidebar-scrim { display: block; }
.topbar, .run-head { padding-left: 56px; }
.field-row { flex-direction: column; }
.review-grid { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
}
@media (max-width: 680px) {
.topbar { flex-wrap: wrap; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4) var(--sp-3) 56px; }
.topbar-sub { display: none; }
.run-head { flex-direction: column; padding: var(--sp-4) var(--sp-4) var(--sp-3) 56px; }
/* In the column layout the title block must size to its content — the
desktop `flex: 1` made it grow and pushed the buttons to the bottom of a
tall empty header. */
.run-head-main { flex: 0 0 auto; width: 100%; }
.run-actions { width: 100%; }
.run-actions .btn { flex: 1 1 auto; justify-content: center; }
.run-facts { display: block; }
.run-facts span { display: inline; }
.wizard-body, .run-body, .dash-body { padding: var(--sp-4) var(--sp-3) var(--sp-6); }
.wizard-panel { gap: var(--sp-4); }
.modal { width: calc(100vw - 16px); max-width: none; max-height: calc(100dvh - 32px); }
.modal-body, .modal-head, .modal-tabs, .modal-foot { padding-left: var(--sp-4); padding-right: var(--sp-4); }
.review-grid, .dash-grid, .stat-row { grid-template-columns: 1fr; }
.data-table { min-width: 560px; }
.lead-toolbar .btn { flex: 1 1 auto; justify-content: center; }
.search-wrap { max-width: none; }
.term-status { display: none; }
.term-target { max-width: 150px; }
.bar-row { grid-template-columns: 76px 1fr 34px; }
}
@media (max-width: 480px) {
.stepper { padding: 0 var(--sp-2); }
.step-tab { padding: var(--sp-3) var(--sp-2) var(--sp-2); }
.wizard-footer { flex-direction: column; align-items: stretch; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); }
.footer-actions .btn { flex: 1 1 auto; justify-content: center; }
.mode-tiles { grid-template-columns: 1fr 1fr; }
.fair-likely .v { font-size: var(--fs-2xl); }
.stat-v { font-size: var(--fs-2xl); }
}
/* Phone in landscape: ~390px of height, most of it browser chrome. Anything
with a fixed vertical share has to shrink or the content disappears. */
@media (max-height: 500px) and (orientation: landscape) {
.topbar, .run-head { padding-top: var(--sp-2); padding-bottom: var(--sp-2); min-height: 0; }
.term-dock { height: clamp(120px, 46dvh, 220px); }
.term-hints { display: none; }
}
/* Touch devices: a 26px button is a miss target, and iOS zooms the whole page
when a focused input is under 16px — which then breaks the layout the user
was typing into. */
@media (pointer: coarse) {
.icon-btn { width: 38px; height: 38px; }
.btn { min-height: 40px; }
.btn-sm { min-height: 34px; }
.sb-run { padding: var(--sp-3) var(--sp-2); }
.run-tab, .step-tab, .modal-tab { min-height: 44px; }
input[type="text"], input[type="number"], input[type="password"], select, textarea { font-size: var(--fs-lg); }
.switch { width: 38px; height: 22px; }
.switch .thumb { width: 18px; height: 18px; }
.switch input:checked + .track + .thumb { transform: translateX(16px); }
.switch input:indeterminate + .track + .thumb { transform: translateX(8px); }
}
/* Respect a reduced-motion preference: the drawer slide and the progress
animation are decoration, not information. */
@media (prefers-reduced-motion: reduce) {
* { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
/* Scoping/Guardrails UI accents */
.pill { display:inline-block; padding:1px 7px; border-radius: var(--radius-pill); background:var(--sev-medium-bg); color:var(--sev-medium-fg); font-weight:600; letter-spacing:.02em; }
.req { color:var(--text-dim); font-weight:400; font-size:.9em; }
/* Interrupted-run resume banner (web persistence). */
.resume-bar { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); background: var(--accent-soft); border-bottom: 1px solid var(--border-strong); font-size: var(--fs-sm); color: var(--text); z-index: var(--z-header); }
.resume-bar .resume-ico { color: var(--accent); font-weight: 700; }
.resume-bar .resume-text { flex: 1; min-width: 0; }
.resume-bar .resume-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.resume-bar .resume-x { background: transparent; border: none; color: var(--text-dim); cursor: pointer; font-size: var(--fs-sm); padding: 2px 6px; border-radius: var(--radius-xs); }
.resume-bar .resume-x:hover { background: var(--surface-3); color: var(--text); }
/* Live "what's being tested" agent panel. */
.agent-panel { margin: 0 var(--sp-5) var(--sp-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); overflow: hidden; }
.agent-panel-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-xs); font-weight: 600; color: var(--text-dim); border-bottom: 1px solid var(--border); background: var(--surface-2); }
.agent-legend { display: flex; align-items: center; gap: var(--sp-2); font-weight: 400; color: var(--text-faint); }
.agent-legend .ap { width: 8px; height: 8px; border-radius: 2px; display: inline-block; margin-right: 3px; vertical-align: middle; }
.ap.running { background: var(--accent); } .ap.done { background: var(--text-faint); } .ap.found { background: var(--sev-high-fg); } .ap.failed { background: var(--sev-critical-fg); }
.agent-grid { display: flex; flex-wrap: wrap; gap: 5px; padding: var(--sp-3); max-height: 180px; overflow-y: auto; }
.agent-chip { display: inline-flex; align-items: center; gap: 4px; font-family: var(--mono); font-size: var(--fs-2xs); padding: 2px 7px; border-radius: var(--radius-pill); border: 1px solid var(--border); background: var(--surface-2); color: var(--text-dim); white-space: nowrap; }
.agent-chip b { background: var(--sev-high-bg); color: var(--sev-high-fg); border-radius: var(--radius-pill); padding: 0 5px; font-size: 9px; }
.agent-chip.ap-running { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.agent-chip.ap-found { border-color: var(--sev-high-fg); color: var(--sev-high-fg); }
.agent-chip.ap-done { opacity: .6; }
.agent-chip.ap-failed { border-color: var(--sev-critical-fg); color: var(--sev-critical-fg); text-decoration: line-through; }
.agent-chip .spin { width: 7px; height: 7px; border: 1.5px solid currentColor; border-right-color: transparent; border-radius: 50%; display: inline-block; animation: aspin .7s linear infinite; }
@keyframes aspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .agent-chip .spin { animation: none; } }
/* ---------------------------------------------------------------------------
Run detail — attack graph, dependencies, traffic, traces, deliverables.
Ideas from autonomous-pentest consoles (KPI cards, typed node graph, traffic
table), built on this console's own tokens so both themes work.
------------------------------------------------------------------------- */
.muted { color: var(--text-faint); font-size: var(--fs-xs); }
.panel-toolbar { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.panel-help { color: var(--text-dim); font-size: var(--fs-sm); margin: 0 0 var(--sp-3); max-width: 900px; }
.chip-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.input-sm { padding: 6px 10px; font-size: var(--fs-sm); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); }
.toggle { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); color: var(--text-dim); }
/* KPI cards above the findings table */
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--sp-3); margin: var(--sp-3) 0; }
.kpi { border: 1px solid var(--border); background: var(--surface); border-radius: var(--radius-md); padding: var(--sp-3) var(--sp-4); }
.kpi-k { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 600; }
.kpi-v { font-size: var(--fs-2xl); font-weight: 700; font-family: var(--mono); margin: 2px 0; }
.kpi-sub { font-size: var(--fs-xs); color: var(--text-dim); }
.kpi-ok .kpi-v { color: var(--sev-low-fg); }
.kpi-bad .kpi-v { color: var(--sev-critical-fg); }
/* status + wire badges in the findings table */
.status-badge, .wire-badge, .chain-status, .gm-st { display: inline-block; font-size: var(--fs-2xs); font-weight: 600; padding: 2px 7px; border-radius: var(--radius-pill); border: 1px solid transparent; white-space: nowrap; }
.st-confirmed { background: var(--sev-low-bg); color: var(--sev-low-fg); }
.st-needs-review, .st-needs_review { background: var(--sev-info-bg); color: var(--sev-info-fg); }
.st-needs_validation { background: var(--sev-medium-bg); color: var(--sev-medium-fg); }
.st-hypothesis, .st-candidate { background: var(--surface-3); color: var(--text-dim); }
.st-rejected { background: var(--sev-critical-bg); color: var(--sev-critical-fg); }
.wire-verified { background: var(--sev-low-bg); color: var(--sev-low-fg); }
.wire-contradicted { background: var(--sev-critical-bg); color: var(--sev-critical-fg); }
.wire-unverified, .wire-pending { background: var(--surface-3); color: var(--text-dim); }
.wire-none { background: transparent; color: var(--text-faint); border-color: var(--border); }
/* chains */
.moves { border: 1px solid var(--border); border-left: 3px solid var(--accent); background: var(--surface); border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-3); }
.moves-h { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--accent); font-weight: 700; margin-bottom: 4px; }
.moves ul { margin: 0; padding-left: 18px; font-size: var(--fs-sm); color: var(--text-dim); }
.chain-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.chain-card { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); }
.chain-card.st-confirmed { border-left: 3px solid var(--sev-low-fg); }
.chain-card.st-needs_validation { border-left: 3px solid var(--sev-medium-fg); }
.chain-card.st-hypothesis { border-left: 3px dashed var(--border-strong); }
.chain-card summary { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); cursor: pointer; flex-wrap: wrap; list-style: none; }
.chain-card summary::-webkit-details-marker { display: none; }
.chain-title { font-weight: 600; font-size: var(--fs-base); flex: 1; min-width: 260px; }
.chain-pot { font-size: var(--fs-xs); color: var(--sev-medium-fg); font-weight: 600; }
.chain-meta { font-size: var(--fs-xs); color: var(--text-faint); font-family: var(--mono); }
.chain-body { padding: 0 var(--sp-4) var(--sp-4); border-top: 1px solid var(--border); }
.chain-impact { margin: var(--sp-3) 0; font-size: var(--fs-sm); }
.chain-steps { margin: 0 0 var(--sp-3); padding-left: 20px; font-family: var(--mono); font-size: var(--fs-xs); }
.chain-steps .step { padding: 3px 0; color: var(--text-dim); }
.chain-steps .step.st-confirmed { color: var(--sev-low-fg); background: none; }
.chain-steps .step.st-needs_validation { color: var(--sev-medium-fg); background: none; }
.chain-steps .step.st-hypothesis { color: var(--text-faint); background: none; }
.chain-block { margin-top: var(--sp-2); }
.chain-block-h { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 700; }
.chain-block ul { margin: 4px 0 0; padding-left: 18px; font-size: var(--fs-sm); }
/* dependency todolist */
.todo-chain { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); margin-bottom: var(--sp-3); }
.todo-chain header { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.todo-title { font-weight: 600; flex: 1; min-width: 240px; }
.todo-progress { width: 110px; height: 6px; background: var(--surface-3); border-radius: var(--radius-pill); overflow: hidden; }
.todo-progress span { display: block; height: 100%; background: var(--sev-low-fg); }
.todo-steps { list-style: none; margin: 0; padding: var(--sp-2) var(--sp-4) var(--sp-3); counter-reset: ts; }
.todo-step { display: flex; gap: var(--sp-3); padding: var(--sp-2) 0; border-bottom: 1px dashed var(--border); align-items: flex-start; }
.todo-step:last-child { border-bottom: none; }
.ts-badge { flex: 0 0 120px; font-size: var(--fs-2xs); font-weight: 700; padding: 3px 8px; border-radius: var(--radius-pill); text-align: center; }
.ts-done .ts-badge { background: var(--sev-low-bg); color: var(--sev-low-fg); }
.ts-ready .ts-badge { background: var(--accent-soft); color: var(--accent); }
.ts-blocked .ts-badge { background: var(--surface-3); color: var(--text-faint); }
.ts-blocked .ts-step { color: var(--text-faint); }
.ts-step { font-family: var(--mono); font-size: var(--fs-xs); }
.ts-action { font-size: var(--fs-sm); color: var(--text-dim); margin-top: 2px; }
/* graph (web + internal network) */
.gm-wrap { position: relative; height: 640px; border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; cursor: grab;
background-color: var(--surface-2); background-image: radial-gradient(var(--border) 1px, transparent 1px); background-size: 18px 18px; }
.gm-wrap.dragging { cursor: grabbing; }
.gm-wrap[hidden] { display: none; }
.gm-stage { position: absolute; left: 0; top: 0; transform-origin: 0 0; }
.gm-svg { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; }
.gm-e { fill: none; stroke-width: 1.4; opacity: .55; }
.gm-e.gm-trav { stroke-width: 2; opacity: .85; }
.gm-e.es-confirmed { stroke: var(--sev-low-fg); }
.gm-e.es-needs_validation { stroke: var(--sev-medium-fg); stroke-dasharray: 6 4; }
.gm-e.es-hypothesis { stroke: var(--text-faint); stroke-dasharray: 2 4; }
.gm-e.es-needs_review { stroke: var(--sev-info-fg); stroke-dasharray: 4 3; }
.gm-stage.focusing .gm-e { opacity: .12; }
.gm-stage.focusing .gm-e.hl { opacity: 1; stroke-width: 2.6; }
.gm-node { position: absolute; background: var(--surface); border: 1px solid var(--border-strong); border-left: 4px solid var(--border-strong); border-radius: var(--radius-sm); padding: 6px 8px; cursor: pointer; overflow: hidden; box-shadow: 0 1px 0 var(--border); }
.gm-node:hover { border-color: var(--text-dim); }
.gm-node.sel { outline: 2px solid var(--focus-ring); }
.gm-node-h { display: flex; align-items: center; gap: 5px; }
.gm-lbl { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .04em; font-weight: 700; color: var(--text-faint); }
.gm-node .sev { font-size: var(--fs-2xs); padding: 0 5px; }
.gm-crit { color: var(--sev-critical-fg); font-size: var(--fs-xs); }
.gm-node-t { font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 3px; }
.gk-asset { border-left-color: var(--sev-info-fg); }
.gk-surface { border-left-color: var(--text-faint); }
.gk-weak { border-left-color: var(--sev-high-fg); }
.gk-step { border-left-color: var(--accent); }
.gk-cred { border-left-color: var(--sev-medium-fg); }
.gk-impact { border-left-color: var(--sev-critical-fg); background: var(--sev-critical-bg); }
.gk-meta { border-left-color: var(--border); opacity: .8; }
.gm-leg { font-size: var(--fs-xs); padding: 3px 8px 3px 6px; border: 1px solid var(--border); border-left-width: 4px; border-radius: var(--radius-xs); background: var(--surface); color: var(--text-dim); }
.gm-leg-sep { width: 1px; height: 18px; background: var(--border); }
.gm-edge-leg { font-size: var(--fs-xs); color: var(--text-dim); display: inline-flex; align-items: center; gap: 6px; }
.gm-edge-leg::before { content: ""; width: 22px; border-top: 2px solid; }
.gm-edge-leg.es-confirmed::before { border-color: var(--sev-low-fg); }
.gm-edge-leg.es-needs_validation::before { border-color: var(--sev-medium-fg); border-top-style: dashed; }
.gm-edge-leg.es-hypothesis::before { border-color: var(--text-faint); border-top-style: dotted; }
.gm-zoom { position: absolute; left: 10px; bottom: 10px; display: flex; flex-direction: column; gap: 4px; }
.gm-detail { margin-top: var(--sp-3); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); padding: var(--sp-3) var(--sp-4); }
.gm-detail header { display: flex; align-items: center; gap: var(--sp-2); justify-content: space-between; margin-bottom: var(--sp-2); }
.kv { border-collapse: collapse; font-size: var(--fs-xs); width: 100%; }
.kv th { text-align: left; color: var(--text-faint); font-weight: 600; padding: 2px var(--sp-3) 2px 0; width: 160px; vertical-align: top; }
.kv td { font-family: var(--mono); padding: 2px 0; word-break: break-all; }
/* traffic */
.method { font-family: var(--mono); font-size: var(--fs-2xs); font-weight: 700; border: 1px solid var(--border-strong); border-radius: var(--radius-xs); padding: 1px 6px; }
.tf { font-family: var(--mono); font-weight: 700; }
.tf-2 { color: var(--sev-low-fg); }
.tf-3 { color: var(--sev-info-fg); }
.tf-4 { color: var(--sev-medium-fg); }
.tf-5 { color: var(--sev-critical-fg); }
.tf-0 { color: var(--text-faint); }
/* traces */
.trace-layout { display: grid; grid-template-columns: 240px 1fr; gap: var(--sp-4); }
.trace-files { list-style: none; margin: 0; padding: 0; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); max-height: 620px; overflow: auto; }
.trace-files li { display: flex; justify-content: space-between; gap: var(--sp-2); padding: 6px var(--sp-3); border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.trace-files a { color: var(--text); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trace-files a:hover { color: var(--accent); }
.trace-hits { display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-sm); }
.trace-hit { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); padding: 6px var(--sp-3); }
.trace-hit a { font-weight: 600; color: var(--text); }
.trace-hit code { display: block; font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-dim); margin-top: 3px; white-space: pre-wrap; word-break: break-all; }
.trace-hit mark { background: var(--accent-soft); color: var(--accent); }
@media (max-width: 760px) { .trace-layout { grid-template-columns: 1fr; } .gm-wrap { height: 460px; } }
/* deliverables */
.deliv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--sp-3); }
.deliv-card { display: flex; flex-direction: column; gap: 4px; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); padding: var(--sp-3) var(--sp-4); text-decoration: none; color: var(--text); }
a.deliv-card:hover { border-color: var(--accent); }
.deliv-card span { font-size: var(--fs-sm); color: var(--text-dim); }
.deliv-card code { font-family: var(--mono); font-size: var(--fs-2xs); color: var(--text-faint); }
.deliv-group ul { margin: 4px 0 0; padding-left: 16px; font-size: var(--fs-xs); max-height: 160px; overflow: auto; }
/* CLI output dialog */
.out-pre { margin: 0; padding: var(--sp-4) var(--sp-5); font-family: var(--mono); font-size: var(--fs-xs); white-space: pre-wrap; word-break: break-word; overflow: auto; max-height: 60vh; background: var(--surface-2); }
.moves li { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin: 2px 0; }
.moves details summary { cursor: pointer; font-size: var(--fs-xs); color: var(--accent); margin-top: 4px; }
.gm-leg.gk-asset { border-left-color: var(--sev-info-fg); }
.gm-leg.gk-surface { border-left-color: var(--text-faint); }
.gm-leg.gk-weak { border-left-color: var(--sev-high-fg); }
.gm-leg.gk-step { border-left-color: var(--accent); }
.gm-leg.gk-cred { border-left-color: var(--sev-medium-fg); }
.gm-leg.gk-impact { border-left-color: var(--sev-critical-fg); background: var(--surface); }
.panel-toolbar .input-sm { width: 200px; flex: 0 0 auto; }
.panel-toolbar select.input-sm { width: 150px; }
.panel-toolbar .search-wrap { flex: 1 1 260px; max-width: 420px; }
.gm-detail header { justify-content: flex-start; }
.gm-detail header [data-close] { margin-left: auto; }