Files
NeuroSploit/web/public/style.css
T
CyberSecurityUPandClaude Opus 5 c3a6132153 feat(report): findings that answer where / why / how to fix, with a pasteable PoC
A finding is useful only if the reader can find the problem, see why it
matters, fix it, and reproduce it without trusting us. The report answered the
last one badly and the other three not at all: it printed a payload blob and an
evidence blob, and "payload: ' OR 1=1--" tells a developer nothing about WHERE
to look. A PoC script attached as a file is a black box unless you run it.

Findings are now rendered in the order a reader works through them — where the
problem is, what it means, how to fix it, then the proof — in the HTML report,
the Markdown, the Typst/PDF and the web console's finding modal.

The proof is numbered, pasteable steps: baseline request, attack request, how
to read the result, with the real URL and the real payload. They come from the
agent's `repro_steps` when it recorded them, and are derived from the
endpoint/payload/identity pair otherwise, so every finding carries something
runnable. The generated curl redacts Authorization/Cookie/API-key headers — a
report gets shared, and a live session cookie inside one is a new bug. A PoC
script is now offered as an extra artifact that automates the steps, never as
the proof itself.

Technical evidence is the measured difference, not a paraphrase: baseline vs
attack status, size, timing and delta; how many repeats reproduced it; the
controlled marker and whether a browser or a callback observed it; then each
recorded exchange with the headers that decide a class (Location, Set-Cookie,
Access-Control-*, X-Frame-Options, CSP, Retry-After) and a body excerpt.

Finding gains `location` (the parameter/field/flow step, not just the URL) and
`repro_steps`, and the agent contract now asks for them explicitly, along with
impact tied to this app's data and remediation that names the control rather
than saying "sanitise input".

The web console offers the run's PDF when Typst produced one — and only then,
since a dead download button is worse than none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-13 17:48:14 -03:00

742 lines
44 KiB
CSS

/* NeuroSploit v4.0.0 — web console.
Visual direction: dense security-operations console (not a marketing SaaS
page). Borders over shadows, typography over color, two radii, 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-sm: 6px;
--radius-md: 10px;
--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);
/* 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;
}
: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: 13px; line-height: 1.5; -webkit-font-smoothing: antialiased;
}
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; color: inherit; font-size: 13px; }
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: 13px; 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: 12px; 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: 13px;
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: 12.5px; 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: 11px; 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: 11px; line-height: 1; transition: transform .15s; }
.sb-group.collapsed .caret { transform: rotate(-90deg); }
.sb-group.collapsed .sb-items { display: none; }
.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: 12.5px; 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: 11px; 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: 11px; color: var(--text-faint); display: flex; align-items: center; gap: var(--sp-2); }
.sb-step::before { content: "○"; font-size: 9px; 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: 11px; 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: 12.5px; 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: 12px; }
.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: 12px;
}
.sb-folder-head:hover { background: var(--surface-3); }
.sb-folder-head .caret { font-size: 11px; line-height: 1; color: var(--text-faint); transition: transform .15s; }
.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: 11px; 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: 12px; 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: 12.5px; }
.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: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 600; }
.stat-v { font-size: 30px; font-weight: 650; line-height: 1.15; margin-top: 2px; font-variant-numeric: tabular-nums; }
.stat-unit { font-size: 14px; color: var(--text-faint); font-weight: 500; }
.stat-sub { font-size: 11.5px; 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: 12.5px; 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: 10.5px; 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: 11px; color: var(--text-faint); }
.dash-table td.mono { font-family: var(--mono); font-size: 11.5px; }
.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: 12px; }
.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: 3px; overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: 3px; 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: 11.5px; 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: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }
.fair-point .v { font-size: 18px; font-weight: 600; font-variant-numeric: tabular-nums; }
.fair-likely .v { font-size: 30px; color: var(--accent); }
.fair-note { font-size: 11.5px; 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: 12px; }
.contrib-row:last-child { border-bottom: none; }
.contrib-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contrib-v { font-family: var(--mono); font-size: 12px; }
.fair-params { display: flex; flex-direction: column; gap: var(--sp-2); }
.fair-param { display: flex; align-items: center; gap: var(--sp-2); font-size: 12px; }
.fair-param > span:first-child { width: 92px; flex: none; text-transform: capitalize; }
.fair-param input { flex: 1; font-family: var(--mono); font-size: 12px; }
/* ============================================================ 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: 15px; font-weight: 600; }
.topbar-sub { font-size: 11px; 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: 12.5px; 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: 12px; }
.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); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.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: 12.5px; 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: 11px; 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: #fff; }
.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: 10.5px; 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: 11px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--text-faint); }
.field-help { font-size: 11.5px; color: var(--text-faint); }
.field-error { font-size: 11.5px; 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: 13px; font-weight: 600; }
.section-desc { font-size: 12px; 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: 12.5px; }
.mode-tile .d { font-size: 11px; 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: 12.5px; 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: 12.5px; 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: 14px; 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: 11.5px; 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: 12.5px; flex: 1; }
.cat-head .cat-count { font-size: 11px; color: var(--text-faint); font-family: var(--mono); }
.cat-head .caret { font-size: 12px; line-height: 1; color: var(--text-faint); transition: transform .15s; }
.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: 12.5px; }
.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: 10.5px; 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: 999px; transition: background .15s; }
.switch .thumb { position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: var(--surface); transition: transform .15s; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
.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: 12px; 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: 11.5px; 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: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); }
.review-item .v { font-size: 13px; margin-top: 3px; font-weight: 500; }
.review-item .v.mono { font-family: var(--mono); font-size: 12px; }
/* ============================================================ 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: 15px; 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: 12px; color: var(--text-dim); font-family: var(--mono); }
#liveTargetSub:empty, #detailTargetSub:empty { display: none; }
.run-facts { flex-wrap: wrap; font-size: 11px; 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: 999px; 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: 11.5px; 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: 12px; 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; }
/* Generative Attack Path Chaining */
.attackpath-empty { font-size: 12.5px; color: var(--text-faint); padding: var(--sp-5); text-align: center; border: 1px dashed var(--border-strong); border-radius: var(--radius-sm); }
.ap-toolbar { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-2); }
.ap-stats { font-size: 12px; color: var(--text-dim); }
.ap-stats b { color: var(--text); font-family: var(--mono); }
.ap-inferred-note { color: var(--text-faint); }
.ap-check { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-dim); }
.ap-sev { width: auto; padding: 5px 8px; font-size: 12px; }
.ap-zoom { display: flex; gap: 4px; }
.ap-zoom .btn { min-width: 32px; justify-content: center; }
.ap-provenance { font-size: 11.5px; color: var(--text-faint); margin-bottom: var(--sp-2); }
/* The canvas is a fixed viewport that the graph pans inside — letting the box
grow to the graph's height (1187px on a 27-finding run) meant scrolling the
page blind, with no way to see the shape of the path. */
.ap-canvas-wrap { position: relative; height: min(60dvh, 560px); min-height: 240px; border-radius: var(--radius-md); overflow: hidden; background: var(--surface-2); border: 1px solid var(--border); cursor: grab; touch-action: none; }
.ap-canvas-wrap.dragging { cursor: grabbing; }
.ap-canvas { display: block; width: 100%; height: 100%; }
.ap-canvas text { font-family: var(--sans); }
.ap-hint { position: absolute; right: 8px; bottom: 6px; font-size: 10.5px; color: var(--text-faint); pointer-events: none; }
.ap-col-line { stroke: var(--border); stroke-width: 1; }
.ap-col-name { font-size: 10.5px; fill: var(--text-faint); text-transform: uppercase; letter-spacing: .06em; font-weight: 600; }
.ap-col-count { font-size: 10.5px; fill: var(--text-faint); font-family: var(--mono); }
.ap-node { fill: var(--surface); stroke-width: 1.6; }
.ap-root .ap-node { stroke: var(--accent); }
.ap-icon { font-size: 13px; }
.ap-title { font-size: 11.5px; fill: var(--text); font-weight: 600; }
.ap-meta, .ap-conf { font-size: 10px; fill: var(--text-faint); font-family: var(--mono); }
.ap-node-g { cursor: pointer; }
.ap-node-g:hover .ap-node, .ap-node-g:focus-visible .ap-node { filter: brightness(1.04); stroke-width: 2.4; }
.ap-node-g:focus { outline: none; }
.ap-edge { stroke: var(--border-strong); stroke-width: 1.6; fill: none; }
.ap-edge marker, #apArrow path { fill: var(--border-strong); }
/* An inferred edge is a hypothesis about the path, not evidence of it. */
.ap-edge.inferred { stroke-dasharray: 5 4; opacity: .55; }
.ap-edge.root-edge { opacity: .35; }
.has-focus .ap-node-g:not(.focus) { opacity: .22; }
.has-focus .ap-edge:not(.focus) { opacity: .1; }
.ap-edge.focus { stroke: var(--accent); stroke-width: 2.2; opacity: 1; }
.ap-legend { display: flex; gap: var(--sp-4); flex-wrap: wrap; align-items: center; margin-top: var(--sp-2); font-size: 11px; color: var(--text-faint); }
.ap-key { display: flex; align-items: center; gap: 5px; }
.ap-key i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
/* 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: 10.5px; 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: 12.5px; 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: 10.5px; 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: 12px; 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: 11.5px; 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: 12px; 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: 11.5px; 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: 12.5px; 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: 11.5px; 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: 12.5px; }
.sev { font-size: 10px; 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: 11px; 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: 12.5px; outline: none; color: var(--text); }
.log-echo { color: var(--accent); }
/* ============================================================ Modal (Auth & Keys) */
.modal-overlay { position: fixed; inset: 0; background: rgba(10,9,8,.45); display: flex; align-items: center; justify-content: center; z-index: 60; }
.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: 14px; 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: 12px; 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; }
.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: 12.5px; font-weight: 500; }
.provider-row .p-kind { flex: none; width: 74px; font-size: 10px; text-transform: uppercase; color: var(--text-faint); font-family: var(--mono); }
.provider-row input { flex: 1; font-family: var(--mono); font-size: 12px; }
.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: rgba(255,255,255,.03); border-bottom: 1px solid rgba(255,255,255,.07);
font-size: 11.5px; color: #a5a099;
}
.term-head .icon-btn { color: #a5a099; }
.term-head .icon-btn:hover { background: rgba(255,255,255,.08); border-color: transparent; color: #fff; }
.term-title { font-weight: 600; color: #ded9d2; }
.term-target {
width: auto; max-width: 260px; background: rgba(255,255,255,.05); color: #ded9d2;
border: 1px solid rgba(255,255,255,.12); border-radius: var(--radius-sm); padding: 3px 6px; font-size: 11.5px;
}
.term-status { font-family: var(--mono); font-size: 11px; color: #8b8781; }
.term-btn {
background: transparent; border: 1px solid rgba(255,255,255,.12); color: #a5a099;
border-radius: var(--radius-sm); padding: 3px 8px; font-size: 11px;
}
.term-btn:hover { background: rgba(255,255,255,.08); color: #fff; }
.term-dot { width: 7px; height: 7px; border-radius: 50%; background: #6b6760; 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: 11.5px; 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: 10.5px; color: #77736d; display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.term-hints .k { font-family: var(--mono); color: #b3aea6; }
/* ============================================================ Toasts */
.toasts { position: fixed; right: var(--sp-5); bottom: var(--sp-5); z-index: 80; 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: 12.5px;
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: 54; background: rgba(10, 9, 8, .45);
}
.sidebar-scrim[hidden] { display: none !important; }
.sidebar-toggle {
display: none; position: fixed; left: 10px; top: 10px; z-index: 60;
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: 55;
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: 24px; }
.stat-v { font-size: 26px; }
}
/* 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); }
.ap-canvas-wrap { height: 58dvh; min-height: 160px; }
.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: 16px; }
.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; }
}