/* botAEO app stylesheet
 *
 * Design rules. Everything below follows these; change the rule here before
 * breaking it somewhere else.
 *
 *   Colour   One accent, the brand purple (#9184d9, see static/brand/README.md).
 *            Green / blue / amber / red appear only where they carry meaning:
 *            HTTP status classes and issue severity. No gradients.
 *   Shape    4px for badges and small chips, 6px for controls, 10px for panels
 *            and modals. Full pills are reserved for counts.
 *   Density  13px UI text, 36px table rows (--row-h, mirrored by
 *            TABLE_ROW_HEIGHT in app.js - the virtual scroller depends on it).
 *   Theme    Dark only. Plugin tabs write dark-ground colours inline, so a
 *            light theme needs them migrated to these tokens first.
 *   Layers   --z-sticky < --z-menu < --z-modal < --z-busy < --z-tooltip.
 */

:root {
    --bg: #12141c;
    --surface: #181b25;
    --surface-2: #1f2330;
    --surface-3: #282d3d;
    --border: #2b3040;
    --border-strong: #3a4054;

    --text: #e7e9f1;
    --text-2: #a5acbf;
    --text-3: #7d859b;

    --accent: #9184d9;
    --accent-strong: #6f60c9;   /* button ground: white text is 5.0:1 */
    --accent-press: #6355bd;
    --accent-text: #b3a9ec;
    --accent-soft: rgba(145, 132, 217, 0.14);
    --accent-line: rgba(145, 132, 217, 0.45);

    /* Status colours match the node colours in visualization.js. */
    --ok: #10b981;
    --info: #3b82f6;
    --warn: #f59e0b;
    --err: #ef4444;
    --neutral: #6b7280;
    --high: #fb923c;            /* audit severity between warn and err */

    /* Lighter cuts of the status colours, for text on a dark ground where
       the base colour falls short of 4.5:1. */
    --ok-text: #34d399;
    --info-text: #60a5fa;
    --warn-text: #fbbf24;
    --err-text: #f87171;

    --r-sm: 4px;
    --r: 6px;
    --r-lg: 10px;

    --row-h: 36px;
    --rail-w: 232px;

    --z-sticky: 10;
    --z-menu: 60;
    --z-modal: 1000;
    --z-busy: 2000;
    --z-tooltip: 9999;

    --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --shadow-pop: 0 16px 40px rgba(8, 9, 16, 0.6);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: var(--font);
    background: var(--bg);
    color: var(--text);
    font-size: 13px;
    line-height: 1.5;
    overflow: hidden;
    -webkit-font-smoothing: antialiased;
}

button,
input,
select,
textarea {
    font-family: inherit;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

code {
    font-family: var(--font-mono);
    font-size: 0.92em;
    background: var(--surface-3);
    border-radius: var(--r-sm);
    padding: 1px 5px;
}

/* Visually hidden but still announced by screen readers. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* --- App shell ---------------------------------------------------------- */

.app-shell {
    display: grid;
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    height: 100dvh;
}

.workspace {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}

/* --- Rail --------------------------------------------------------------- */

.rail {
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--surface);
    border-right: 1px solid var(--border);
}

.app-brand {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    height: 52px;
    /* Brand guide: clear space of 1/4 the mark height on all sides. */
    padding: 0 16px;
    text-decoration: none;
    border-bottom: 1px solid var(--border);
}

.app-logo {
    display: block;
    height: 28px;
    width: auto;
}

.app-brand:focus-visible {
    outline-offset: -4px;
    border-radius: var(--r);
}

/* The view list. Scrolls on its own so the brand and the account block stay
   put however many plugins are installed. */
.rail-nav {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 2px 8px 8px;
    /* Scroll shadows: the covers scroll with the content and hide the
       shadows at either end, so a shadow shows only where more list is
       waiting off-screen. */
    background:
        linear-gradient(var(--surface) 40%, transparent) top / 100% 24px no-repeat local,
        linear-gradient(transparent, var(--surface) 60%) bottom / 100% 24px no-repeat local,
        linear-gradient(rgba(8, 9, 16, 0.55), transparent) top / 100% 10px no-repeat scroll,
        linear-gradient(transparent, rgba(8, 9, 16, 0.55)) bottom / 100% 10px no-repeat scroll;
}

.nav-group-label {
    padding: 10px 8px 4px;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-3);
}

.tab-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-shrink: 0;
    width: 100%;
    min-height: 28px;
    padding: 4px 8px;
    background: none;
    border: none;
    border-radius: var(--r);
    color: var(--text-2);
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background 0.12s var(--ease), color 0.12s var(--ease);
}

.tab-btn:hover {
    background: var(--surface-2);
    color: var(--text);
}

.tab-btn.active {
    background: var(--accent-soft);
    color: var(--accent-text);
}

/* Issue count on the Issues row. --badge is set inline by app.js to the
   worst severity present. */
.nav-badge {
    padding: 0 7px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 18px;
    color: var(--badge, var(--text-2));
    background: color-mix(in srgb, var(--badge, var(--neutral)) 18%, transparent);
}

.rail-foot {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 8px;
    border-top: 1px solid var(--border);
}

.rail-link {
    width: 100%;
    min-height: 28px;
    padding: 4px 8px;
    background: none;
    border: none;
    border-radius: var(--r);
    color: var(--text-2);
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background 0.12s var(--ease), color 0.12s var(--ease);
}

.rail-link:hover {
    background: var(--surface-2);
    color: var(--text);
}

.rail-user {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 7px;
    padding: 8px 8px 2px;
    border-top: 1px solid var(--border);
}

.user-info {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12px;
    color: var(--text-2);
}

.rail-logout {
    flex-shrink: 0;
    padding: 3px 8px;
    background: none;
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text-2);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.12s var(--ease), color 0.12s var(--ease);
}

.rail-logout:hover {
    border-color: var(--text-3);
    color: var(--text);
}

/* Workspace switcher. Hidden by workspaces.js when there is only one
   workspace, so a single-operator install is visually unchanged. */
.workspace-select {
    width: 100%;
    margin-bottom: 7px;
    padding: 6px 8px;
    background: var(--surface-2);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    font-size: 13px;
    cursor: pointer;
}

.workspace-select:hover {
    border-color: var(--accent-line);
}

/* Read-only marker for client logins. Amber rather than red: this is a
   statement of role, not an error. */
.viewer-badge {
    flex-shrink: 0;
    padding: 1px 7px;
    border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
    border-radius: 999px;
    background: color-mix(in srgb, var(--warn) 14%, transparent);
    color: var(--warn-text);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

/* --- Command bar -------------------------------------------------------- */

/* The recent-sites dropdown overhangs into the view below. Without a
   stacking context here, later siblings with opaque backgrounds paint over
   the menu's lower half. */
.url-section {
    position: relative;
    z-index: var(--z-menu);
    flex-shrink: 0;
    padding: 10px 16px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.url-container {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.input-group {
    flex: 1 1 240px;
    min-width: 0;
}

/* The placeholder and the Start button already say what this field is; the
   label stays for screen readers. */
.input-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
}

.url-input-wrap {
    position: relative;
    width: 100%;
}

.url-input {
    width: 100%;
    height: 36px;
    /* Room for the recent-sites toggle so a long URL never runs under it. */
    padding: 0 40px 0 12px;
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text);
    font-size: 14px;
    transition: border-color 0.12s var(--ease), box-shadow 0.12s var(--ease);
}

.url-input:hover {
    border-color: var(--text-3);
}

.url-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.url-input::placeholder {
    color: var(--text-3);
}

.control-buttons {
    display: flex;
    gap: 6px;
}

/* Load / Save / Export sit apart from the crawl controls. A gap rather than
   a rule: a rule is left stranded when this group wraps to its own line. */
.control-buttons-file {
    margin-left: 8px;
}

.progress-container {
    margin-top: 10px;
    display: flex;
    align-items: center;
    gap: 12px;
}

.progress-bar {
    flex: 1;
    height: 3px;
    background: var(--surface-3);
    border-radius: 999px;
    overflow: hidden;
}

.progress-fill {
    height: 100%;
    width: 0%;
    background: var(--accent);
    transition: width 0.3s var(--ease);
}

.progress-text {
    min-width: 140px;
    font-size: 12px;
    color: var(--text-2);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* --- Buttons ------------------------------------------------------------ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 36px;
    padding: 0 12px;
    border: 1px solid transparent;
    border-radius: var(--r);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.12s var(--ease), border-color 0.12s var(--ease),
        color 0.12s var(--ease), transform 0.12s var(--ease);
}

.btn:active:not(:disabled) {
    transform: translateY(1px);
}

.btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.btn-primary {
    background: var(--accent-strong);
    color: #ffffff;
}

.btn-primary:hover:not(:disabled) {
    background: var(--accent-press);
}

.btn-secondary,
.btn-success {
    background: var(--surface-2);
    border-color: var(--border-strong);
    color: var(--text);
}

.btn-secondary:hover:not(:disabled),
.btn-success:hover:not(:disabled) {
    background: var(--surface-3);
    border-color: var(--text-3);
}

.btn-danger {
    background: transparent;
    border-color: color-mix(in srgb, var(--err) 50%, transparent);
    color: var(--err-text);
}

.btn-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--err) 14%, transparent);
}

/* --- Stat strip --------------------------------------------------------- */

/* Cells size to their content and share what is left, so all eight sit on
   one row from about 1000px up and wrap cleanly below that. */
.stat-strip {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    overflow: hidden;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.stat-item {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    padding: 7px 14px;
    /* A rule on the left of every cell, pulled 1px out of view for the first
       column of each row, so it holds when the grid wraps. */
    margin-left: -1px;
    border-left: 1px solid var(--border);
}

.stat-label {
    font-size: 11px;
    color: var(--text-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stat-value {
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- URL filter bar ----------------------------------------------------- */

/* Only the URL tables respond to these filters. app.js writes the active
   view to body[data-view]. */
.filter-tree {
    display: none;
    flex-shrink: 0;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 20px;
    padding: 8px 16px;
    border-bottom: 1px solid var(--border);
}

body[data-view="overview"] .filter-tree,
body[data-view="internal"] .filter-tree,
body[data-view="external"] .filter-tree,
body[data-view="status-codes"] .filter-tree {
    display: flex;
}

.filter-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
}

.filter-header {
    margin-right: 4px;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-3);
}

.filter-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 26px;
    padding: 0 8px;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text-2);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    user-select: none;
    transition: background 0.12s var(--ease), border-color 0.12s var(--ease),
        color 0.12s var(--ease);
}

.filter-item:hover {
    background: var(--surface-2);
    color: var(--text);
}

.filter-item.active {
    background: var(--accent-soft);
    border-color: var(--accent-line);
    color: var(--accent-text);
}

.filter-count {
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--text-3);
}

.filter-item.active .filter-count {
    color: inherit;
}

/* --- Views -------------------------------------------------------------- */

.tab-content {
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

.tab-pane {
    display: none;
    height: 100%;
    min-height: 0;
    overflow: hidden;
}

.tab-pane.active {
    display: flex;
    flex-direction: column;
}

/* Plugin panes lay themselves out; give them a scrolling box to do it in. */
.tab-pane.plugin-tab.active {
    display: block;
    overflow: auto;
}

/* Plugins cap their root at calc(100vh - 280px) inline - 280px being the
   height of the old header, URL bar and tab strip. The pane is now the
   scroller and knows its own height, so the cap only wastes space. */
.plugin-tab > [style*="100vh - 280px"] {
    max-height: none !important;
    overflow-y: visible !important;
}

.pane-toolbar {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px 12px;
    padding: 8px 16px;
    border-bottom: 1px solid var(--border);
}

.pane-toolbar-controls {
    display: flex;
    align-items: center;
    gap: 6px;
}

.pane-note {
    flex: 1 1 320px;
    max-width: 80ch;
    font-size: 12px;
    color: var(--text-2);
}

.control {
    height: 30px;
    padding: 0 10px;
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text);
    font-size: 13px;
}

.control:hover {
    border-color: var(--text-3);
}

.control:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.control::placeholder {
    color: var(--text-3);
}

.control-search {
    width: 200px;
}

/* --- Tables ------------------------------------------------------------- */

.table-container {
    flex: 1;
    min-height: 0;
    overflow: auto;
    position: relative;
}

.data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    table-layout: fixed;
}

.data-table th {
    /* !important: column-resize.js sets position: relative inline so it can
       anchor its drag grip. Sticky anchors the grip just as well. */
    position: sticky !important;
    top: 0;
    z-index: var(--z-sticky);
    height: 32px;
    padding: 0 12px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-strong);
    color: var(--text-2);
    font-size: 12px;
    font-weight: 600;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* One line per row, always. The virtual scroller places rows by multiplying
   an index by --row-h, so a wrapped cell would throw every row after it. */
.data-table td {
    height: var(--row-h);
    padding: 0 12px;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.data-table tbody tr:hover td {
    background: var(--surface-2);
}

.data-table a {
    color: var(--accent-text);
    text-decoration: none;
}

.data-table a:hover {
    text-decoration: underline;
}

/* --- DataGrid (data-grid.js) --------------------------------------------
   Column widths live in a <colgroup> the grid writes, and the table's
   min-width is their sum: narrower than the pane it stretches to fill,
   wider and the pane scrolls sideways. */
.grid-toolbar {
    flex-wrap: nowrap;
}

.grid-toolbar .control-search {
    flex: 0 1 260px;
    min-width: 120px;
}

.grid-controls {
    margin-left: auto;
}

.grid-count {
    margin-right: 6px;
    font-size: 12px;
    color: var(--text-2);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.grid-btn {
    cursor: pointer;
    font-weight: 500;
    transition: background 0.12s var(--ease), border-color 0.12s var(--ease);
}

.grid-btn:hover {
    background: var(--surface-2);
}

.grid-btn:active {
    transform: translateY(1px);
}

.grid-menu-wrap {
    position: relative;
}

.grid-menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: var(--z-menu);
    width: 220px;
    max-height: min(420px, 60dvh);
    padding: 4px;
    overflow-y: auto;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
}

.grid-menu[hidden] {
    display: none;
}

.grid-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px;
    border-radius: var(--r);
    color: var(--text);
    cursor: pointer;
}

.grid-menu-item:hover {
    background: var(--surface-3);
}

.grid-menu-item input {
    accent-color: var(--accent);
}

.grid-menu-reset {
    width: 100%;
    margin-top: 4px;
    padding: 7px 8px;
    background: none;
    border: none;
    border-top: 1px solid var(--border);
    color: var(--text-2);
    font-size: 12px;
    text-align: left;
    cursor: pointer;
}

.grid-menu-reset:hover {
    color: var(--text);
}

/* The toolbar's menu must clear the sticky table header below it. */
.pane-toolbar {
    position: relative;
    z-index: calc(var(--z-sticky) + 2);
}

/* With two grids stacked (Links), the open menu also has to clear the
   toolbar of the grid beneath it. */
.pane-toolbar:has(.grid-menu:not([hidden])) {
    z-index: calc(var(--z-sticky) + 3);
}

.grid-table th {
    padding: 0;
}

.grid-head {
    display: flex;
    align-items: center;
    gap: 4px;
    width: 100%;
    height: 100%;
    padding: 0 12px;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: grab;
}

button.grid-head {
    cursor: pointer;
}

button.grid-head:hover {
    color: var(--text);
}

.grid-head:focus-visible {
    outline-offset: -2px;
}

th[aria-sort] .grid-head {
    color: var(--accent-text);
}

th[aria-sort="ascending"] .grid-head::after { content: '\2191'; }
th[aria-sort="descending"] .grid-head::after { content: '\2193'; }

.grid-num {
    text-align: right !important;
}

.grid-num .grid-head {
    justify-content: flex-end;
}

.grid-grip {
    position: absolute;
    top: 0;
    right: -3px;
    z-index: 2;
    width: 7px;
    height: 100%;
    cursor: col-resize;
}

.grid-grip::after {
    content: '';
    position: absolute;
    top: 8px;
    bottom: 8px;
    left: 3px;
    width: 1px;
    background: var(--border-strong);
}

.grid-grip:hover::after,
.grid-resizing .grid-grip::after {
    top: 0;
    bottom: 0;
    background: var(--accent);
}

.grid-resizing {
    cursor: col-resize;
    user-select: none;
}

/* Drop target while a column is being dragged to a new position. */
.grid-drop {
    box-shadow: inset 2px 0 0 var(--accent);
}

/* Pinned first column: stays put while the rest scrolls sideways. */
.grid-table th.grid-pin,
.grid-table td.grid-pin {
    position: sticky !important;
    left: 0;
    border-right: 1px solid var(--border-strong);
}

.grid-table td.grid-pin {
    z-index: 1;
    background: var(--bg);
}

.grid-table th.grid-pin {
    z-index: calc(var(--z-sticky) + 1);
}

.grid-spacer td {
    height: 0;
    padding: 0;
    border: none;
    pointer-events: none;
}

.grid-table tbody tr.grid-spacer:hover td {
    background: none;
}

/* Sits under the header, in the pane rather than the scrolled table, so it
   stays centred however wide the columns are. */
.grid-empty {
    position: sticky;
    left: 0;
    width: 100%;
    max-width: 100%;
    padding: 56px 24px;
    color: var(--text-2);
    text-align: center;
}

.grid-empty[hidden] {
    display: none;
}

#statusCodesTable {
    max-width: 720px;
}

/* The image report is not virtualised, so its rows may run to two lines. */
#imagesTable {
    min-width: 900px;
}

#imagesTable td {
    padding-top: 6px;
    padding-bottom: 6px;
    white-space: normal;
}

.details-btn {
    height: 24px;
    padding: 0 9px;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm);
    color: var(--accent-text);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.12s var(--ease), border-color 0.12s var(--ease);
}

.details-btn:hover {
    background: var(--accent-soft);
    border-color: var(--accent-line);
}

.status-badge,
.severity-pill {
    display: inline-block;
    padding: 0 6px;
    border-radius: var(--r-sm);
    font-size: 11px;
    font-weight: 600;
    line-height: 18px;
    color: var(--tone);
    background: color-mix(in srgb, var(--tone) 16%, transparent);
    --tone: var(--text-2);
}

.status-2xx { --tone: var(--ok); }
.status-3xx { --tone: var(--info); }
.status-4xx { --tone: var(--warn); }
.status-5xx { --tone: var(--err); }

.severity-error { --tone: var(--err); }
.severity-warning { --tone: var(--warn); }
.severity-info { --tone: var(--info); }

/* Issue severity filters */
.severity-filters {
    justify-content: flex-start;
    gap: 4px;
}

.severity-filters .filter-item[data-filter="error"].active {
    background: color-mix(in srgb, var(--err) 14%, transparent);
    border-color: color-mix(in srgb, var(--err) 50%, transparent);
    color: var(--err-text);
}

.severity-filters .filter-item[data-filter="warning"].active {
    background: color-mix(in srgb, var(--warn) 14%, transparent);
    border-color: color-mix(in srgb, var(--warn) 50%, transparent);
    color: var(--warn-text);
}

.severity-filters .filter-item[data-filter="info"].active {
    background: color-mix(in srgb, var(--info) 14%, transparent);
    border-color: color-mix(in srgb, var(--info) 50%, transparent);
    color: var(--info-text);
}

/* Summary strip (image report) */
.summary-strip {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 24px;
    padding: 12px 16px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.summary-empty {
    color: var(--text-2);
}

/* --- Status bar --------------------------------------------------------- */

.status-bar {
    flex-shrink: 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 28px;
    padding: 0 16px;
    background: var(--surface);
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-2);
    font-variant-numeric: tabular-nums;
}

/* --- Scrollbars --------------------------------------------------------- */

* {
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border: 2px solid transparent;
    border-radius: 999px;
    background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
    background-color: var(--text-3);
}

::-webkit-scrollbar-corner {
    background: transparent;
}

/* --- Busy overlay -------------------------------------------------------
   Loading a saved crawl takes a minute of server work with nothing on screen
   to show for it, and the page looked frozen - so it got rage-clicked. This
   covers the UI, swallows those clicks and says what is happening. */
.busy-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-busy);
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(8, 9, 16, 0.72);
    cursor: progress;
}

.busy-panel {
    max-width: 440px;
    padding: 28px 40px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
    text-align: center;
}

.busy-spinner {
    width: 32px;
    height: 32px;
    margin: 0 auto 16px;
    border: 3px solid var(--accent-soft);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: busy-spin 0.9s linear infinite;
}

@keyframes busy-spin {
    to { transform: rotate(360deg); }
}

.busy-title {
    margin-bottom: 6px;
    color: var(--text);
    font-size: 15px;
    font-weight: 600;
}

.busy-detail {
    color: var(--text-2);
    font-size: 13px;
}

/* --- Modals ------------------------------------------------------------- */

.modal,
.details-modal-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(8, 9, 16, 0.72);
}

.modal-content,
.details-modal {
    display: flex;
    flex-direction: column;
    width: 92%;
    max-width: 880px;
    max-height: 90dvh;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
}

.modal-header,
.details-header {
    flex-shrink: 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 20px;
    border-bottom: 1px solid var(--border);
}

.modal-header h2,
.details-header h3 {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
}

.modal-close,
.close-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--r);
    color: var(--text-2);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s var(--ease), color 0.12s var(--ease);
}

.modal-close:hover,
.close-btn:hover {
    background: var(--surface-3);
    color: var(--text);
}

.modal-body {
    flex: 1;
    min-height: 0;
    padding: 0;
    overflow-y: auto;
}

.modal-footer {
    flex-shrink: 0;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 20px;
    border-top: 1px solid var(--border);
}

/* Crawl history modal */
#dashboardContent {
    padding: 0 !important;
}

.modal-message {
    padding: 40px 20px;
    color: var(--text-2);
    text-align: center;
}

.modal-message-error {
    color: var(--err-text);
}

.history-table th:nth-child(1) { width: 190px; }
.history-table th:nth-child(3) { width: 90px; }
.history-table th:nth-child(4) { width: 110px; }
.history-table th:nth-child(5) { width: 230px; }

.history-table td {
    height: 44px;
}

.actions {
    white-space: nowrap;
}

.actions .btn + .btn {
    margin-left: 4px;
}

.btn-small {
    height: 28px;
    padding: 0 10px;
    font-size: 12px;
}

/* Crawl states */
.status-completed { --tone: var(--ok); }
.status-running { --tone: var(--info); }
.status-paused { --tone: var(--warn); }
.status-failed { --tone: var(--err); }

/* Settings: section list down the left, for the same reason as the rail -
   eight sections and counting did not fit across the top. */
#settingsModal .modal-body {
    display: grid;
    grid-template-columns: 188px minmax(0, 1fr);
    align-items: start;
}

.settings-tabs {
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 12px 8px;
}

.settings-tab-btn {
    padding: 6px 10px;
    background: none;
    border: none;
    border-radius: var(--r);
    color: var(--text-2);
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.12s var(--ease), color 0.12s var(--ease);
}

.settings-tab-btn:hover {
    background: var(--surface-2);
    color: var(--text);
}

.settings-tab-btn.active {
    background: var(--accent-soft);
    color: var(--accent-text);
}

.settings-tab-content {
    display: none;
    grid-column: 2;
    min-width: 0;
    padding: 20px 24px 24px;
    border-left: 1px solid var(--border);
    min-height: 100%;
}

.settings-tab-content.active {
    display: block;
}

.settings-tab-content h3 {
    margin: 0 0 16px 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
}

.setting-group {
    margin-bottom: 18px;
}

.setting-group label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
}

.setting-group input[type="text"],
.setting-group input[type="password"],
.setting-group input[type="number"],
.setting-group select,
.setting-group textarea {
    width: 100%;
    padding: 8px 10px;
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text);
    font-size: 13px;
    transition: border-color 0.12s var(--ease), box-shadow 0.12s var(--ease);
}

.setting-group input[type="text"]:hover,
.setting-group input[type="password"]:hover,
.setting-group input[type="number"]:hover,
.setting-group select:hover,
.setting-group textarea:hover {
    border-color: var(--text-3);
}

.setting-group input[type="text"]:focus,
.setting-group input[type="password"]:focus,
.setting-group input[type="number"]:focus,
.setting-group select:focus,
.setting-group textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.setting-group ::placeholder {
    color: var(--text-3);
}

.setting-group textarea {
    resize: vertical;
    min-height: 80px;
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.6;
}

.setting-help {
    display: block;
    margin-top: 5px;
    max-width: 72ch;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-2);
}

.setting-help a {
    color: var(--accent-text) !important;
}

.checkbox-label {
    display: flex !important;
    align-items: center;
    gap: 8px;
    margin-bottom: 0 !important;
    cursor: pointer;
}

.checkbox-label input[type="checkbox"],
.setting-group input[type="checkbox"] {
    width: auto !important;
    margin: 0 6px 0 0 !important;
    accent-color: var(--accent);
}

.checkbox-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 8px 16px;
    margin-top: 8px;
}

.setting-group select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23a5acbf' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
    background-position: right 8px center;
    background-repeat: no-repeat;
    background-size: 16px;
    padding-right: 32px;
    cursor: pointer;
}

.callout {
    padding: 12px 14px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-left: 2px solid var(--accent);
    border-radius: var(--r);
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-2);
}

.callout-warn {
    border-left-color: var(--warn);
}

.callout strong {
    display: block;
    margin-bottom: 4px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.callout ul {
    padding-left: 16px;
}

.code-block {
    display: block;
    margin-top: 6px;
    padding: 10px 12px;
    background: var(--bg);
    white-space: pre;
    overflow-x: auto;
    color: var(--text);
}

/* --- Empty states ------------------------------------------------------- */

.empty-state {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 280px;
    padding: 24px;
    text-align: center;
}

.empty-state-content {
    max-width: 340px;
}

.empty-state svg {
    width: 40px;
    height: 40px;
    margin-bottom: 12px;
    color: var(--text-3);
    stroke-width: 1.5;
}

.empty-state h3 {
    margin: 0 0 6px 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
}

.empty-state p {
    margin: 0;
    color: var(--text-2);
}

/* --- URL details modal -------------------------------------------------- */

.details-content {
    padding: 20px;
    overflow-y: auto;
}

.details-url {
    margin-bottom: 16px;
    padding: 10px 12px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--accent-text);
    font-family: var(--font-mono);
    font-size: 12px;
    word-break: break-all;
}

.details-sections {
    display: grid;
    gap: 12px;
}

.details-section {
    padding: 14px 16px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
}

.details-section h4 {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.details-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 4px 16px;
}

.details-grid > div {
    padding: 3px 0;
    color: var(--text);
}

.details-grid strong {
    color: var(--text-2);
    font-weight: 500;
}

.details-subsection {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
}

.details-subsection h5 {
    margin: 0 0 6px 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
}

.json-preview {
    max-height: 200px;
    padding: 10px 12px;
    overflow-y: auto;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 12px;
    white-space: pre-wrap;
    word-break: break-word;
}

/* --- Links view --------------------------------------------------------- */

.links-tab-scroll-container {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* Two tables share the pane height equally and scroll independently. */
.links-sections {
    flex: 1;
    min-height: 0;
    display: grid;
    /* minmax(0, ...) on both axes: a grid track otherwise grows to fit the
       table's min-width and pushes the toolbar off the side of the pane. */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
}

/* No overflow: hidden here - the table scrolls inside .table-container, and
   clipping the section would cut off the grid's Columns menu. */
.links-section {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.links-section + .links-section {
    border-top: 1px solid var(--border-strong);
}

.links-section-title {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

/* --- PageSpeed ---------------------------------------------------------- */

.pagespeed-container {
    height: 100%;
    padding: 20px;
    overflow-y: auto;
}

.pagespeed-header {
    margin-bottom: 16px;
}

.pagespeed-header h3 {
    margin: 0 0 4px 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
}

.pagespeed-description {
    margin: 0;
    color: var(--text-2);
}

.pagespeed-placeholder {
    padding: 48px 20px;
    text-align: center;
    color: var(--text-2);
}

.pagespeed-placeholder p {
    margin: 4px 0;
}

.pagespeed-results {
    display: grid;
    gap: 12px;
}

.pagespeed-page-card {
    padding: 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
}

.pagespeed-page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
}

.pagespeed-page-url {
    margin: 0;
    color: var(--accent-text);
    font-size: 13px;
    font-weight: 500;
    word-break: break-all;
}

.pagespeed-analysis-date {
    color: var(--text-3);
    font-size: 12px;
    white-space: nowrap;
}

.pagespeed-results-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.pagespeed-device-result {
    padding: 14px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
}

.pagespeed-device-result h5 {
    margin: 0 0 10px 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
}

.pagespeed-score {
    margin-bottom: 12px;
    padding: 10px;
    border: 1px solid color-mix(in srgb, var(--tone) 35%, transparent);
    border-radius: var(--r);
    background: color-mix(in srgb, var(--tone) 14%, transparent);
    color: var(--tone);
    font-size: 28px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: center;
    --tone: var(--text-2);
}

.score-good { --tone: var(--ok); }
.score-needs-improvement { --tone: var(--warn); }
.score-poor { --tone: var(--err); }
.score-unknown { --tone: var(--text-2); }

.pagespeed-metrics {
    display: grid;
    gap: 1px;
}

.metric {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 2px;
    border-bottom: 1px solid var(--border);
}

.metric:last-child {
    border-bottom: none;
}

.metric-label {
    color: var(--text-2);
}

.metric-value {
    color: var(--text);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.pagespeed-error {
    padding: 10px 12px;
    border: 1px solid color-mix(in srgb, var(--err) 35%, transparent);
    border-radius: var(--r);
    background: color-mix(in srgb, var(--err) 12%, transparent);
    color: var(--err-text);
    text-align: center;
}

/* --- Visualization ------------------------------------------------------ */

.visualization-container {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}

.visualization-header {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 8px 12px;
    padding: 8px 16px;
    border-bottom: 1px solid var(--border);
}

.visualization-header h3 {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.visualization-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.visualization-controls .btn {
    height: 30px;
    padding: 0 10px;
}

.visualization-controls select {
    height: 30px;
    padding: 0 8px;
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text);
    font-size: 13px;
    cursor: pointer;
}

.visualization-controls select:hover {
    border-color: var(--text-3);
}

.visualization-controls select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.graph-container {
    flex: 1;
    min-height: 0;
    position: relative;
    overflow: hidden;
    background: var(--bg);
}

.graph-placeholder {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
}

.graph-placeholder svg {
    width: 40px;
    height: 40px;
    margin-bottom: 12px;
    color: var(--text-3);
    stroke-width: 1.5;
}

.graph-placeholder h3 {
    margin-bottom: 6px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text);
}

.graph-placeholder p {
    color: var(--text-2);
}

.visualization-legend {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px 20px;
    padding: 8px 16px;
    border-top: 1px solid var(--border);
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-2);
}

.legend-color {
    width: 10px;
    height: 10px;
    border-radius: 50%;
}

.legend-2xx { background: var(--ok); }
.legend-3xx { background: var(--info); }
.legend-4xx { background: var(--warn); }
.legend-5xx { background: var(--err); }
.legend-other { background: var(--neutral); }

.cy-tooltip {
    position: absolute;
    z-index: var(--z-tooltip);
    max-width: 400px;
    padding: 10px 12px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    box-shadow: var(--shadow-pop);
    color: var(--text);
    font-size: 12px;
    pointer-events: none;
}

.cy-tooltip .tooltip-url {
    margin-bottom: 6px;
    color: var(--accent-text);
    font-weight: 600;
    word-break: break-all;
}

.cy-tooltip .tooltip-info {
    display: flex;
    flex-direction: column;
    gap: 3px;
    color: var(--text-2);
}

.cy-tooltip .tooltip-status {
    display: inline-block;
    margin-top: 4px;
    padding: 0 6px;
    border-radius: var(--r-sm);
    font-size: 11px;
    font-weight: 600;
    line-height: 18px;
}

.cy-tooltip .status-2xx { background: color-mix(in srgb, var(--ok) 16%, transparent); color: var(--ok); }
.cy-tooltip .status-3xx { background: color-mix(in srgb, var(--info) 16%, transparent); color: var(--info); }
.cy-tooltip .status-4xx { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.cy-tooltip .status-5xx { background: color-mix(in srgb, var(--err) 16%, transparent); color: var(--err); }

/* Cytoscape node interaction states */
.highlighted {
    opacity: 1 !important;
}

.dimmed {
    opacity: 0.2 !important;
}

/* --- Recently crawled picker -------------------------------------------- */

.recent-toggle {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: var(--r-sm);
    color: var(--text-2);
    cursor: pointer;
    transition: background 0.12s var(--ease), color 0.12s var(--ease);
}

.recent-toggle:hover:not(:disabled) {
    background: var(--surface-3);
    color: var(--text);
}

.recent-toggle:focus-visible {
    outline-offset: 0;
}

.recent-toggle svg {
    transition: transform 0.15s var(--ease);
}

.recent-toggle[aria-expanded="true"] svg {
    transform: rotate(180deg);
}

.recent-toggle:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.recent-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: var(--z-menu);
    max-height: 320px;
    padding: 4px;
    overflow-y: auto;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-pop);
}

.recent-menu[hidden] {
    display: none;
}

.recent-item {
    display: flex;
    flex-direction: column;
    gap: 1px;
    width: 100%;
    padding: 7px 10px;
    background: transparent;
    border: none;
    border-radius: var(--r);
    text-align: left;
    cursor: pointer;
}

.recent-item:hover,
.recent-item:focus-visible {
    background: var(--accent-soft);
    outline: none;
}

.recent-url {
    color: var(--text);
    font-size: 13px;
    font-weight: 500;
    word-break: break-all;
}

.recent-meta {
    color: var(--text-2);
    font-size: 12px;
}

.recent-empty {
    padding: 14px 12px;
    color: var(--text-2);
    text-align: center;
}

/* --- Narrow screens -----------------------------------------------------
   Below 900px the rail folds into a bar across the top and the view list
   scrolls sideways. */
@media (max-width: 900px) {
    .app-shell {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr);
    }

    .rail {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        border-right: none;
        border-bottom: 1px solid var(--border);
    }

    .app-brand {
        height: 48px;
        border-bottom: none;
    }

    .rail-foot {
        flex: 1;
        flex-direction: row;
        align-items: center;
        justify-content: flex-end;
        border-top: none;
    }

    .rail-link {
        width: auto;
        white-space: nowrap;
    }

    /* A pinned 340px column would leave almost nothing to scroll. */
    .grid-table th.grid-pin,
    .grid-table td.grid-pin {
        left: auto;
    }

    .grid-toolbar {
        flex-wrap: wrap;
    }

    .workspace-select {
        width: auto;
        max-width: 180px;
        margin-bottom: 0;
    }

    .rail-user {
        margin-top: 0;
        padding: 0 0 0 8px;
        border-top: none;
    }

    .user-info {
        display: none;
    }

    .rail-nav {
        order: 3;
        flex: 0 0 100%;
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        padding: 0 8px 8px;
    }

    .nav-group-label {
        display: none;
    }

    .tab-btn {
        width: auto;
        white-space: nowrap;
    }

    .control-buttons-file {
        margin-left: 0;
    }

    .pagespeed-results-grid {
        grid-template-columns: 1fr;
    }

    #settingsModal .modal-body {
        grid-template-columns: minmax(0, 1fr);
    }

    .settings-tabs {
        position: static;
        flex-direction: row;
        overflow-x: auto;
        border-bottom: 1px solid var(--border);
    }

    .settings-tab-content {
        grid-column: 1;
        border-left: none;
    }

    .modal-content,
    .details-modal {
        width: 96%;
        max-height: 96dvh;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
    }

    .btn:active:not(:disabled) {
        transform: none;
    }

    /* The spinner is the only sign that a long load is alive, so it keeps
       turning, slowly. */
    .busy-spinner {
        animation-duration: 2.4s;
    }
}
