/* ============================================================================
   Central Logger — console
   Dark-only. The use scene is a developer triaging a fleet at a terminal,
   usually beside one; a light theme would be the odd surface, not the default.
   ========================================================================= */

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
table { border-collapse: collapse; width: 100%; }
a { color: inherit; }

/* --- Tokens --------------------------------------------------------------- */

:root {
    /* Dark-only, so tell the platform: native select popups, scrollbars,
       date pickers and the search field's clear button all follow this. */
    color-scheme: dark;

    /* Surfaces, coolest to warmest as they rise toward the reader. */
    --bg:            #0b0910;
    --surface:       #14111c;
    --raised:        #1c1826;
    --hover:         #241f30;
    --line:          #2b2539;
    --line-bright:   #3d3550;

    /* Secondary text is tinted from the surface hue, never neutral gray. */
    --ink:           #eae7f2;
    --ink-dim:       #a9a2bd;
    --ink-faint:     #837c99;

    --accent:        #a78bfa;
    --accent-hot:    #8b5cf6;
    --accent-wash:   rgba(167, 139, 250, 0.13);
    --accent-line:   rgba(167, 139, 250, 0.36);

    --sev-debug:     #7c8698;
    --sev-info:      #60a5fa;
    --sev-notice:    #38bdf8;
    --sev-warning:   #fbbf24;
    --sev-error:     #fb7185;
    --sev-critical:  #f43f5e;
    --sev-alert:     #e11d48;
    --sev-emergency: #be123c;
    --ok:            #4ade80;

    --sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
    --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    /* 1.125 steps. Product UI needs many sizes; wide contrast would be noise. */
    --t-xs:   0.6875rem;
    --t-sm:   0.75rem;
    --t-base: 0.8125rem;
    --t-md:   0.875rem;
    --t-lg:   1rem;
    --t-xl:   1.25rem;
    --t-2xl:  1.5rem;

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

    /* Depth carries an offset and a blur; a flat halo is decoration. */
    --lift: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -12px rgba(0, 0, 0, .8);
    --pop:  0 4px 8px rgba(0, 0, 0, .5), 0 24px 48px -16px rgba(0, 0, 0, .9);

    --ease: cubic-bezier(.16, 1, .3, 1);
}

/* --- Base ----------------------------------------------------------------- */

body {
    background: var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    font-size: var(--t-base);
    line-height: 1.5;
    font-feature-settings: "cv05" 1, "ss01" 1;
}

.mono { font-family: var(--mono); font-size: var(--t-sm); }
.dim  { color: var(--ink-dim); }
.faint { color: var(--ink-faint); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.grow { flex: 1; min-width: 0; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

::selection { background: var(--accent-wash); color: #fff; }

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

/* --- Shell ---------------------------------------------------------------- */

.shell {
    position: sticky;
    top: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: 1.5rem;
    height: 48px;
    padding: 0 1.25rem;
    background: rgba(11, 9, 16, .88);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line);
}

.brand {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-weight: 650;
    letter-spacing: -.01em;
    text-decoration: none;
    color: var(--ink);
}
.brand svg { color: var(--accent); }

.tabs { display: flex; align-items: center; gap: .125rem; }

.tab {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .6rem;
    border-radius: var(--r);
    color: var(--ink-dim);
    text-decoration: none;
    font-size: var(--t-base);
    font-weight: 500;
    transition: background 160ms var(--ease), color 160ms var(--ease);
}
.tab:hover { background: var(--raised); color: var(--ink); }
.tab[aria-current="page"] { background: var(--accent-wash); color: var(--accent); }
.tab svg { opacity: .75; }

.count {
    min-width: 1.15rem;
    padding: 0 .3rem;
    border-radius: 999px;
    background: var(--sev-warning);
    color: #221a00;
    font-family: var(--mono);
    font-size: var(--t-xs);
    font-weight: 700;
    text-align: center;
}

.page { padding: 1.75rem 1.5rem 5rem; max-width: 1600px; margin-inline: auto; }
.page--narrow { max-width: 1100px; }

/* --- Headings ------------------------------------------------------------- */

.crumb {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: var(--t-sm);
    color: var(--ink-faint);
    margin-bottom: .35rem;
    text-decoration: none;
}
.crumb:hover { color: var(--ink-dim); }

.title {
    font-size: var(--t-xl);
    font-weight: 650;
    letter-spacing: -.02em;
    line-height: 1.25;
    text-wrap: balance;
}
.subtitle { color: var(--ink-dim); font-size: var(--t-base); margin-top: .25rem; }

/* More space above a section heading than below it. */
.section {
    margin-top: 2.25rem;
    margin-bottom: .75rem;
    display: flex;
    align-items: baseline;
    gap: .625rem;
}
.section h2 { font-size: var(--t-md); font-weight: 600; letter-spacing: -.01em; }
.section p { color: var(--ink-faint); font-size: var(--t-sm); }

.head {
    display: flex;
    align-items: flex-start;
    gap: 1.25rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

/* --- Panels --------------------------------------------------------------- */

.panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    /* clip, not hidden: `hidden` would make this the scroll context for the
       sticky table header inside it, which then pins 48px down from the panel's
       own top and covers the first row. `clip` still rounds the corners. */
    overflow: clip;
}
.panel--pad { padding: 1.25rem 1.5rem; }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: .75rem; }
.stats + .panel { margin-top: .75rem; }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: .875rem 1rem; }
.stat dt { color: var(--ink-faint); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }
.stat dd { font-family: var(--mono); font-size: var(--t-2xl); font-weight: 600; letter-spacing: -.02em; margin-top: .25rem; }
.stat dd.is-quiet { color: var(--ink-faint); }
.stat dd.is-hot { color: var(--sev-error); }
/* The denominator of a ratio is context, not the number itself. */
.stat__of { font-size: var(--t-md); font-weight: 500; color: var(--ink-faint); letter-spacing: 0; }
.stat a { color: var(--ink-dim); font-size: var(--t-sm); text-decoration: none; }
.stat a:hover { color: var(--accent); }

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

.grid { font-size: var(--t-base); }
.grid thead th {
    position: sticky;
    top: 48px;
    z-index: 10;
    background: var(--surface);
    padding: .55rem 1rem;
    text-align: left;
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--ink-faint);
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}
.grid tbody td { padding: .6rem 1rem; border-bottom: 1px solid var(--line); vertical-align: middle; }
.grid tbody tr { transition: background 120ms var(--ease); }
.grid tbody tr:hover { background: var(--hover); }
.grid tbody tr:last-child td { border-bottom: 0; }
.grid a { color: var(--ink); text-decoration: none; font-weight: 500; }
.grid tbody tr:hover a { color: var(--accent); }

.row-sub { color: var(--ink-faint); font-size: var(--t-xs); margin-top: .15rem; display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }

/* --- Severity ------------------------------------------------------------- */

.sev {
    display: inline-flex;
    align-items: center;
    font-family: var(--mono);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: .05em;
    padding: .1rem .375rem;
    border-radius: var(--r-sm);
    white-space: nowrap;
    background: color-mix(in oklab, var(--sev) 16%, transparent);
    color: var(--sev);
    border: 1px solid color-mix(in oklab, var(--sev) 28%, transparent);
}
.sev-debug     { --sev: var(--sev-debug); }
.sev-info      { --sev: var(--sev-info); }
.sev-notice    { --sev: var(--sev-notice); }
.sev-warning   { --sev: var(--sev-warning); }
.sev-error     { --sev: var(--sev-error); }
.sev-critical  { --sev: var(--sev-critical); }
.sev-alert     { --sev: var(--sev-alert); }
.sev-emergency { --sev: var(--sev-emergency); }

.tag {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-family: var(--mono);
    font-size: var(--t-xs);
    padding: .1rem .375rem;
    border-radius: var(--r-sm);
    background: var(--raised);
    color: var(--ink-dim);
    border: 1px solid var(--line);
    white-space: nowrap;
}
.tag--accent { background: var(--accent-wash); color: var(--accent); border-color: var(--accent-line); }
.tag--ok     { background: color-mix(in oklab, var(--ok) 14%, transparent); color: var(--ok); border-color: color-mix(in oklab, var(--ok) 30%, transparent); }
.tag--warn   { background: color-mix(in oklab, var(--sev-warning) 14%, transparent); color: var(--sev-warning); border-color: color-mix(in oklab, var(--sev-warning) 30%, transparent); }
.tag--bad    { background: color-mix(in oklab, var(--sev-error) 14%, transparent); color: var(--sev-error); border-color: color-mix(in oklab, var(--sev-error) 30%, transparent); }

.status {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-family: var(--mono);
    font-size: var(--t-xs);
    color: var(--ink-dim);
}
.status::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 999px;
    background: var(--ok);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--ok) 22%, transparent);
}
.status--stale::before { background: var(--sev-error); box-shadow: 0 0 0 3px color-mix(in oklab, var(--sev-error) 22%, transparent); }
.status--idle::before  { background: var(--ink-faint); box-shadow: none; }

/* --- Controls ------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .45rem .85rem;
    min-height: 2.05rem;
    border-radius: var(--r);
    border: 1px solid var(--line-bright);
    background: var(--raised);
    color: var(--ink);
    font-size: var(--t-base);
    font-weight: 550;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    transition: background 150ms var(--ease), border-color 150ms var(--ease), color 150ms var(--ease);
}
.btn:hover { background: var(--hover); border-color: var(--accent-line); }
.btn:active { transform: translateY(.5px); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; pointer-events: none; }

.btn--primary { background: var(--accent-hot); border-color: var(--accent-hot); color: #fff; }
.btn--primary:hover { background: var(--accent); border-color: var(--accent); }
.btn--ghost { background: transparent; border-color: var(--line); color: var(--ink-dim); }
.btn--ghost:hover { background: var(--raised); border-color: var(--line-bright); color: var(--ink); }
.btn--sm { padding: .3rem .65rem; min-height: 1.75rem; font-size: var(--t-sm); }
.btn--xs { padding: .2rem .5rem; min-height: 1.5rem; font-size: var(--t-xs); }

/* Semantic buttons.
   Edit and Delete were transparent and borderless, which made the two most
   consequential row actions read as plain text — and made deleting look no
   heavier than looking. Each variant now carries its own tint at three
   strengths: a wash for the fill, a stronger edge, and the full colour for the
   label, so it stays legible on the dark ground without shouting. */
.btn--edit   { --tint: var(--sev-info); }
.btn--danger { --tint: var(--sev-error); }
.btn--ok     { --tint: var(--ok); }
.btn--warn   { --tint: var(--sev-warning); }
.btn--run    { --tint: var(--accent); }

.btn--edit, .btn--danger, .btn--ok, .btn--warn, .btn--run {
    background: color-mix(in oklab, var(--tint) 12%, var(--raised));
    border-color: color-mix(in oklab, var(--tint) 32%, transparent);
    color: var(--tint);
}
.btn--edit:hover, .btn--danger:hover, .btn--ok:hover, .btn--warn:hover, .btn--run:hover {
    background: color-mix(in oklab, var(--tint) 22%, var(--raised));
    border-color: var(--tint);
    color: color-mix(in oklab, var(--tint) 70%, white);
}
.btn--edit:focus-visible, .btn--danger:focus-visible, .btn--ok:focus-visible,
.btn--warn:focus-visible, .btn--run:focus-visible {
    outline: 2px solid var(--tint);
    outline-offset: 1px;
}

.btn[data-loading="true"] { color: transparent; position: relative; pointer-events: none; }
.btn[data-loading="true"]::after {
    content: "";
    position: absolute;
    width: 13px; height: 13px;
    border-radius: 999px;
    border: 2px solid rgba(255, 255, 255, .3);
    border-top-color: #fff;
    animation: spin 620ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.field, .select {
    width: 100%;
    padding: .375rem .625rem;
    background: var(--bg);
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    color: var(--ink);
    font-size: var(--t-base);
    transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease);
}
.field::placeholder { color: var(--ink-faint); }
.field:focus, .select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-wash);
}
.field:disabled, .select:disabled { opacity: .5; cursor: not-allowed; }

.select {
    appearance: none;
    padding-right: 1.75rem;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23837c99' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .5rem center;
}

.label { display: block; font-size: var(--t-xs); font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: .3rem; }

.filters {
    display: flex;
    gap: .5rem;
    align-items: flex-end;
    flex-wrap: wrap;
    padding: .75rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
}
.filters > * { min-width: 0; }
.filter-search { flex: 1 1 16rem; }
.filter-pick { flex: 0 1 10rem; }

.search-wrap { position: relative; }
.search-wrap svg { position: absolute; left: .55rem; top: 50%; transform: translateY(-50%); color: var(--ink-faint); pointer-events: none; }
.search-wrap .field { padding-left: 1.9rem; }

/* --- Notices -------------------------------------------------------------- */

.notice {
    display: flex;
    gap: .75rem;
    align-items: flex-start;
    padding: .75rem 1rem;
    border-radius: var(--r-lg);
    background: var(--surface);
    border: 1px solid var(--line);
    font-size: var(--t-base);
}
.notice svg { flex: none; margin-top: .1rem; }
.notice--ok    { border-color: color-mix(in oklab, var(--ok) 32%, transparent); background: color-mix(in oklab, var(--ok) 8%, var(--surface)); }
.notice--ok svg { color: var(--ok); }
.notice--warn  { border-color: color-mix(in oklab, var(--sev-warning) 32%, transparent); background: color-mix(in oklab, var(--sev-warning) 8%, var(--surface)); }
.notice--warn svg { color: var(--sev-warning); }
.notice--bad   { border-color: color-mix(in oklab, var(--sev-error) 34%, transparent); background: color-mix(in oklab, var(--sev-error) 9%, var(--surface)); }
.notice--bad svg { color: var(--sev-error); }
.notice--bad a { color: var(--sev-error); font-weight: 550; }
.notice--info  { border-color: var(--accent-line); background: color-mix(in oklab, var(--accent) 8%, var(--surface)); }
.notice--info svg { color: var(--accent); }

.empty { padding: 3rem 1.5rem; text-align: center; color: var(--ink-dim); }
.empty svg { color: var(--ink-faint); margin: 0 auto .75rem; }
.empty h3 { font-size: var(--t-md); font-weight: 600; color: var(--ink); margin-bottom: .3rem; }
.empty p { font-size: var(--t-base); max-width: 46ch; margin-inline: auto; }
.empty a { color: var(--accent); }

.skeleton {
    background: linear-gradient(90deg, var(--raised) 0%, var(--hover) 40%, var(--raised) 80%);
    background-size: 300% 100%;
    animation: sweep 1.4s ease-in-out infinite;
    border-radius: var(--r-sm);
    height: .75rem;
}
@keyframes sweep { to { background-position: -300% 0; } }

/* --- Code ----------------------------------------------------------------- */

.code {
    font-family: var(--mono);
    font-size: var(--t-sm);
    line-height: 1.6;
    white-space: pre-wrap;
    word-break: break-word;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--r);
    padding: .625rem .75rem;
    overflow: auto;
    max-height: 22rem;
    color: var(--ink-dim);
}
.code--term { color: #cdd6f4; }
.code .cue { color: var(--ink-faint); user-select: none; }

details.more { margin-top: .5rem; }
details.more > summary {
    cursor: pointer;
    font-size: var(--t-xs);
    color: var(--ink-faint);
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .1rem .3rem;
    border-radius: var(--r-sm);
}
details.more > summary::-webkit-details-marker { display: none; }
details.more > summary:hover { color: var(--accent); background: var(--raised); }
details.more > summary svg { transition: transform 160ms var(--ease); }
details.more[open] > summary svg { transform: rotate(90deg); }

/* --- Sparkline --------------------------------------------------------------- */

.spark { display: block; overflow: visible; }
.spark rect { fill: var(--sev-error); }
.spark--quiet rect { fill: var(--ink-faint); }

/* --- Chart ------------------------------------------------------------------ */

.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart .b-info     { fill: var(--sev-info); opacity: .38; }
.chart .b-warning  { fill: var(--sev-warning); opacity: .8; }
.chart .b-error    { fill: var(--sev-error); }
.chart .b-critical { fill: var(--sev-critical); }
.chart .slot { fill: transparent; }
.chart .slot:hover { fill: var(--hover); }
.chart .axis { fill: var(--ink-faint); font-family: var(--mono); font-size: 9px; }
.chart .base { stroke: var(--line); stroke-width: 1; }
/* Hour bars stretch to the panel width; a label drawn inside would stretch with
   them, so the axis is HTML placed at each label's share of the width. */
.chart.chart--bars { height: 126px; }
.chart-axis { position: relative; height: 1rem; margin-top: .4rem; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-faint); }
.chart-axis span { position: absolute; top: 0; white-space: nowrap; }

.legend { display: flex; gap: .9rem; flex-wrap: wrap; font-size: var(--t-xs); color: var(--ink-faint); }
.legend span { display: inline-flex; align-items: center; gap: .3rem; }
.legend i { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

/* --- Sortable headers -------------------------------------------------------- */

.grid thead th a {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}
.grid thead th a:hover { color: var(--accent); }
.grid thead th a[data-active] { color: var(--accent); }
.grid thead th .caret { opacity: .5; font-size: 9px; }
.grid thead th a[data-active] .caret { opacity: 1; }

/* --- Tom Select, wearing the console's clothes ------------------------------- */

.ts-wrapper { min-width: 0; }
.ts-control {
    background: var(--bg) !important;
    border: 1px solid var(--line-bright) !important;
    border-radius: var(--r) !important;
    color: var(--ink) !important;
    padding: .25rem .5rem !important;
    font-size: var(--t-base);
    min-height: 0;
    box-shadow: none !important;
}
.ts-wrapper.focus .ts-control {
    border-color: var(--accent) !important;
    box-shadow: 0 0 0 3px var(--accent-wash) !important;
}
.ts-control input { color: var(--ink) !important; font-size: var(--t-base); }
.ts-control input::placeholder { color: var(--ink-faint); }
.ts-control > .item {
    background: var(--accent-wash) !important;
    color: var(--accent) !important;
    border: 1px solid var(--accent-line) !important;
    border-radius: var(--r-sm) !important;
    padding: 0 .3rem !important;
    font-family: var(--mono);
    font-size: var(--t-xs);
}
.ts-control > .item .remove { border-left-color: var(--accent-line) !important; }
.ts-dropdown {
    background: var(--raised) !important;
    border: 1px solid var(--line-bright) !important;
    border-radius: var(--r) !important;
    color: var(--ink) !important;
    box-shadow: var(--pop) !important;
    font-size: var(--t-base);
    margin-top: 3px !important;
}
.ts-dropdown .option { padding: .3rem .55rem; }
.ts-dropdown .active { background: var(--accent-wash) !important; color: var(--accent) !important; }
.ts-dropdown .no-results { color: var(--ink-faint); padding: .4rem .55rem; }

/* --- Bulk selection -------------------------------------------------------- */

.bulkbar {
    position: sticky;
    bottom: 1rem;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 1rem auto 0;
    padding: .6rem .875rem;
    width: fit-content;
    background: var(--raised);
    border: 1px solid var(--accent-line);
    border-radius: 999px;
    box-shadow: var(--pop);
}
.bulkbar[hidden] { display: none; }
.bulkbar .n { font-family: var(--mono); color: var(--accent); font-weight: 700; }

.pick { accent-color: var(--accent-hot); width: 15px; height: 15px; cursor: pointer; }

.suggestion {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .6rem .875rem;
    border-bottom: 1px solid var(--line);
    font-size: var(--t-base);
}
.suggestion:last-child { border-bottom: 0; }
.suggestion .what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-dim); }

/* --- Source ---------------------------------------------------------------- */

.source {
    font-family: var(--mono);
    font-size: var(--t-sm);
    line-height: 1.65;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--r);
    overflow: auto;
}
.source-line { display: grid; grid-template-columns: 3.75rem minmax(0, 1fr); }
.source-line .n {
    text-align: right;
    padding: 0 .7rem;
    color: var(--ink-faint);
    user-select: none;
    border-right: 1px solid var(--line);
    background: var(--surface);
}
.source-line .c { padding: 0 .85rem; white-space: pre; color: var(--ink-dim); }
.source-line.is-target { background: color-mix(in oklab, var(--sev-error) 11%, transparent); }
.source-line.is-target .n { color: var(--sev-error); font-weight: 700; background: color-mix(in oklab, var(--sev-error) 16%, var(--surface)); }
.source-line.is-target .c { color: var(--ink); }

/* --- Timeline (narrative) ------------------------------------------------- */

.tl { margin-top: .25rem; }
.tl-row { display: grid; grid-template-columns: 6.5rem 1.25rem minmax(0, 1fr); }
.tl-when { text-align: right; padding: .8rem .4rem 0 0; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-faint); white-space: nowrap; }
.tl-rail { position: relative; }
.tl-rail::before { content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; margin-left: -.5px; background: var(--line); }
.tl-row:first-child .tl-rail::before { top: 1rem; }
.tl-row:last-child .tl-rail::before { bottom: calc(100% - 1rem); }
.tl-dot { position: absolute; left: 50%; top: .85rem; width: 7px; height: 7px; margin-left: -3.5px; border-radius: 999px; background: var(--line-bright); outline: 3px solid var(--bg); }

.tl-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
    padding: .5rem .75rem;
    margin: .3rem 0;
    min-width: 0;
}
.tl-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.tl-time { margin-left: auto; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-faint); }
.tl-msg { font-family: var(--mono); font-size: var(--t-sm); margin-top: .3rem; white-space: pre-wrap; word-break: break-word; max-height: 9rem; overflow: auto; color: var(--ink-dim); }

.tl-row.is-anchor .tl-card { border-color: color-mix(in oklab, var(--sev-error) 45%, transparent); background: color-mix(in oklab, var(--sev-error) 7%, var(--surface)); box-shadow: var(--lift); }
.tl-row.is-anchor .tl-dot { background: var(--sev-error); width: 11px; height: 11px; margin-left: -5.5px; top: .75rem; }
.tl-row.is-anchor .tl-when { color: var(--sev-error); font-weight: 600; }
.tl-row.is-anchor .tl-msg { color: var(--ink); }

/* --- Request card --------------------------------------------------------- */

.request { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; font-size: var(--t-lg); }
.request .verb { font-family: var(--mono); font-size: var(--t-base); font-weight: 700; padding: .1rem .4rem; border-radius: var(--r-sm); background: var(--accent-wash); color: var(--accent); }
.request .path { font-family: var(--mono); font-weight: 500; }
.http { font-family: var(--mono); font-size: var(--t-sm); font-weight: 700; padding: .1rem .4rem; border-radius: var(--r-sm); }
.http-5 { background: color-mix(in oklab, var(--sev-error) 16%, transparent); color: var(--sev-error); }
.http-4 { background: color-mix(in oklab, var(--sev-warning) 16%, transparent); color: var(--sev-warning); }
.http-2 { background: color-mix(in oklab, var(--ok) 16%, transparent); color: var(--ok); }

/* --- Live tail ------------------------------------------------------------ */

.stream { max-height: 70vh; overflow: auto; scrollbar-gutter: stable; }
.stream-line { display: grid; grid-template-columns: 6.5rem 5.5rem 11rem minmax(0, 1fr); gap: .625rem; padding: .3rem .875rem; border-bottom: 1px solid color-mix(in oklab, var(--line) 55%, transparent); font-family: var(--mono); font-size: var(--t-sm); align-items: baseline; }
.stream-line:hover { background: var(--hover); }
.stream-line > time { color: var(--ink-faint); }
.stream-line .src { color: var(--ink-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stream-line .msg { color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stream-line a { text-decoration: none; }
a.stream-line { text-decoration: none; color: inherit; }
a.stream-line:hover .msg { color: var(--accent); }
.stream-line:hover .msg { color: var(--ink); }

/* The one authored moment: a row arriving in the live stream announces itself
   and then gets out of the way. */
.stream-line.is-new { animation: arrive 420ms var(--ease) both; }
@keyframes arrive {
    from { opacity: 0; transform: translateY(-4px); background: var(--accent-wash); }
    to   { opacity: 1; transform: none; background: transparent; }
}

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

.pulse { display: inline-flex; align-items: center; gap: .4rem; }
.pulse::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: var(--ok); animation: beat 1.6s ease-in-out infinite; }
@keyframes beat { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.8); } }

/* --- Pagination ----------------------------------------------------------- */

.pager { display: flex; gap: .3rem; align-items: center; justify-content: flex-end; margin-top: 1rem; font-size: var(--t-sm); }
.pager a, .pager span { padding: .25rem .55rem; border-radius: var(--r); color: var(--ink-dim); text-decoration: none; border: 1px solid transparent; }
.pager a:hover { background: var(--raised); color: var(--ink); }
.pager [aria-current] { background: var(--accent-wash); color: var(--accent); border-color: var(--accent-line); }
.pager .disabled { opacity: .35; }

/* --- Auth ----------------------------------------------------------------- */

.gate { min-height: 100dvh; display: grid; place-items: center; padding: 1.5rem; }
.gate-card { width: 100%; max-width: 22rem; }
.gate-brand { display: flex; align-items: center; gap: .5rem; justify-content: center; margin-bottom: 1.5rem; font-weight: 650; }
.gate-brand svg { color: var(--accent); }
.gate .panel { padding: 1.5rem; box-shadow: var(--pop); }
.gate .field { margin-bottom: .875rem; }
.error-text { color: var(--sev-error); font-size: var(--t-sm); margin-top: -.6rem; margin-bottom: .75rem; }

/* Google asks for its mark on a surface that is either white or its own dark
   grey, not tinted with the host's palette — so this one button steps out of
   the token system deliberately. */
.btn--google {
    width: 100%;
    background: #fff;
    border-color: #fff;
    color: #1f1f1f;
    font-weight: 550;
    min-height: 2.6rem;
    gap: .6rem;
}
.btn--google:hover { background: #f1f1f1; border-color: #f1f1f1; color: #1f1f1f; }

.gate-fallback { margin-top: 1.25rem; }
.gate-fallback > summary {
    font-size: var(--t-xs);
    color: var(--ink-faint);
    cursor: pointer;
    text-align: center;
    list-style: none;
}
.gate-fallback > summary::-webkit-details-marker { display: none; }
.gate-fallback > summary:hover { color: var(--ink-dim); }
.gate-fallback[open] > summary { margin-bottom: .875rem; }

/* --- System health --------------------------------------------------------- */

.pulse { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; font-size: var(--t-base); }
.pulse .faint { font-size: var(--t-sm); }

.status--bad {
    border-color: color-mix(in oklab, var(--sev-error) 45%, transparent);
    background: color-mix(in oklab, var(--sev-error) 14%, transparent);
    color: var(--sev-error);
}

/* A log tail is the one place a horizontal scrollbar is right: wrapping a stack
   trace makes it unreadable, and these lines are meant to be scanned. */
.code--scroll { max-height: 22rem; overflow: auto; white-space: pre; line-height: 1.55; }

/* --- Server detail: split, gauges, side rails ------------------------------

   Eight and four. The wide column is the machine over time; the narrow one is
   what depends on it. Below 1100px they stack, because a 4fr column of alert
   rows at tablet width is narrower than the text inside it. ----------------- */

.split { display: grid; grid-template-columns: minmax(0, 8fr) minmax(0, 4fr); gap: 1.5rem; align-items: start; }
.split__main { display: grid; gap: 0; min-width: 0; }
.split__side { display: grid; gap: 1.5rem; min-width: 0; position: sticky; top: 4rem; }

/* The main column's first child is a panel, not a section heading, so it needs
   the top margin a .section would have brought with it. */
.split__main > .section:first-child { margin-top: 0; }

.rangebar { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .25rem; }

/* A section heading can carry one link to the fuller view of itself. */
.section__more { margin-left: auto; font-size: var(--t-xs); color: var(--ink-dim); }
.section__more:hover { color: var(--accent); }

/* ── Gauges ────────────────────────────────────────────────────────────────
   A dial rather than a bar, because an arc has two ends: "full" is drawn, not
   assumed. The reading sits in the gap at the bottom of the sweep. */

.gauges {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    gap: .75rem;
    margin-bottom: 1.5rem;
}

.gauge {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    padding: .9rem .75rem .8rem;
    position: relative;
    text-align: center;
    min-width: 0;
}

.gauge__dial { width: 100%; max-width: 7.5rem; margin: 0 auto; display: block; }
.gauge__track { stroke: var(--bg); stroke-width: 9; }
.gauge__fill { stroke: var(--ok); stroke-width: 9; transition: stroke-dasharray .45s var(--ease); }
.gauge__peak { fill: var(--ink-faint); }

.gauge--warn .gauge__fill { stroke: var(--sev-warning); }
.gauge--bad .gauge__fill { stroke: var(--sev-error); }
.gauge--warn .gauge__peak, .gauge--bad .gauge__peak { fill: var(--ink-dim); }

/* Centred inside the dial's own box rather than after it, so the number sits in
   the arc's opening instead of below the card. */
.gauge__read {
    position: absolute;
    left: 0;
    right: 0;
    top: 47%;
    transform: translateY(-50%);
    pointer-events: none;
}
.gauge__value {
    font-family: var(--mono);
    font-size: var(--t-lg);
    font-weight: 600;
    letter-spacing: -.02em;
    line-height: 1.1;
}
.gauge__ceiling { font-size: var(--t-xs); color: var(--ink-faint); margin-top: .1rem; }

.gauge__foot {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    margin-top: -.9rem;
}
.gauge__label { font-size: var(--t-sm); color: var(--ink-dim); font-weight: 550; }
.gauge__peaktext { font-size: var(--t-xs); color: var(--ink-faint); font-family: var(--mono); }

/* ── Side rails ─────────────────────────────────────────────────────────────
   Rows here are whole links, so the target is the row and not a word inside it.
   Every one goes somewhere that answers the next question. */

.side__title {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    font-size: var(--t-sm);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: .6rem;
}
.side__more { font-size: var(--t-xs); text-transform: none; letter-spacing: 0; color: var(--ink-dim); font-weight: 500; }
.side__more:hover { color: var(--accent); }

.side__row {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .65rem .875rem;
    border-bottom: 1px solid var(--line);
    text-decoration: none;
    color: inherit;
    transition: background 130ms var(--ease);
}
.side__row:last-child { border-bottom: 0; }
.side__row:hover { background: var(--raised); }
.side__row .grow { min-width: 0; }
.side__name {
    display: block;
    font-size: var(--t-sm);
    font-weight: 550;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.side__row .row-sub { font-size: var(--t-xs); }

.empty--tight { padding: 1.1rem .875rem; }
.empty--tight h3 { margin-bottom: .2rem; }
.empty--tight p { margin-bottom: .6rem; }

/* Key/value, not a listing: the label column is fixed so the values line up. */
.grid--kv td { padding-top: .4rem; padding-bottom: .4rem; font-size: var(--t-sm); }
.grid--kv td:first-child { width: 8.5rem; }

@media (max-width: 1100px) {
    .split { grid-template-columns: minmax(0, 1fr); }
    .split__side { position: static; }
}

/* --- Select2 --------------------------------------------------------------
   Its stylesheet is light and ships selectors like
   `.select2-container--default .select2-selection--single`, so everything here
   matches that shape or beats it. The goal is a widget indistinguishable from
   the plain `.field` next to it — the library supplies the behaviour, not the
   look. --------------------------------------------------------------------- */

.select2-container { width: 100% !important; }

.select2-container--default .select2-selection--single,
.select2-container--default .select2-selection--multiple {
    background: var(--raised);
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    min-height: 2.3rem;
    padding: 0;
    outline: none;
}
.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--focus .select2-selection--multiple,
.select2-container--default.select2-container--open .select2-selection--single,
.select2-container--default.select2-container--open .select2-selection--multiple {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-wash);
}

.select2-container--default .select2-selection--single .select2-selection__rendered {
    color: var(--ink);
    line-height: 2.3rem;
    padding-left: .6rem;
    padding-right: 1.9rem;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder { color: var(--ink-faint); }
.select2-container--default .select2-selection--single .select2-selection__arrow { height: 2.3rem; right: .35rem; }
.select2-container--default .select2-selection--single .select2-selection__arrow b {
    border-color: var(--ink-faint) transparent transparent;
}
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
    border-color: transparent transparent var(--ink-faint);
}

/* Multiple: the chosen values are pills, so they need to read as data rather
   than as buttons. */
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
    padding: .2rem .3rem;
    display: flex;
    flex-wrap: wrap;
    gap: .25rem;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
    background: var(--accent-wash);
    border: 1px solid var(--accent-line);
    border-radius: var(--r-sm, 4px);
    color: var(--ink);
    font-size: var(--t-xs);
    margin: 0;
    /* Room on the right for the remove button, which Select2 pins to the
       left edge on top of the text unless told otherwise. */
    position: relative;
    padding: .1rem 1.45rem .1rem .5rem;
    display: inline-flex;
    align-items: center;
    max-width: 100%;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__display {
    padding: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
    position: absolute;
    left: auto;
    right: .2rem;
    top: 50%;
    transform: translateY(-50%);
    border: 0;
    background: none;
    color: var(--ink-dim);
    padding: 0 .2rem;
    font-size: var(--t-sm);
    line-height: 1;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
    background: none;
    color: var(--sev-error);
}
.select2-container--default .select2-search--inline .select2-search__field {
    color: var(--ink);
    margin: 0;
    padding: .15rem .2rem;
    min-height: 1.6rem;
}
.select2-container--default .select2-selection--multiple .select2-selection__placeholder { color: var(--ink-faint); }

/* The dropdown paints over the page, so it carries the raised surface. */
.select2-dropdown {
    background: var(--surface);
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    box-shadow: var(--pop);
    color: var(--ink);
    z-index: 60;
}
.select2-container--default .select2-search--dropdown { padding: .4rem; }
.select2-container--default .select2-search--dropdown .select2-search__field {
    background: var(--bg);
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    color: var(--ink);
    padding: .3rem .5rem;
    outline: none;
}
.select2-container--default .select2-search--dropdown .select2-search__field:focus { border-color: var(--accent); }

.select2-container--default .select2-results__option {
    padding: .4rem .6rem;
    font-size: var(--t-sm);
    color: var(--ink-dim);
}
.select2-container--default .select2-results__option--selected { background: var(--raised); color: var(--ink); }
.select2-container--default .select2-results__option--highlighted[aria-selected],
.select2-container--default .select2-results__option--highlighted {
    background: var(--accent);
    color: #fff;
}
.select2-container--default .select2-results__message { color: var(--ink-faint); }
.select2-results__options { max-height: 17rem; }

/* Sheets are native dialogs in the top layer, so their dropdown is parented to
   the dialog — where the library's absolute positioning needs the dialog to be
   the containing block. */
dialog { position: fixed; }
dialog .select2-container { z-index: 60; }

/* --- Responsive ----------------------------------------------------------- */

@media (max-width: 860px) {
    .shell { gap: .5rem; padding: 0 .625rem; }
    .page { padding: 1rem .75rem 3rem; }

    /* The brand mark alone. Its wordmark is the first thing to go: the tabs are
       what the thumb is reaching for. */
    .brand span, .brand { font-size: 0; gap: 0; }
    .brand svg { font-size: 1rem; }

    .tabs { gap: 0; flex: 1; justify-content: space-between; }
    .tab { padding: .5rem .55rem; }
    .tab span { display: none; }
    .tab svg { width: 17px; height: 17px; opacity: 1; }

    .status { white-space: nowrap; font-size: 0; }
    .status::before { margin-right: 0; }

    /* Tables stack. Right-aligning a lone number in a stacked block reads as a
       stray value, so alignment collapses with the layout. */
    /* Block flow, not a table box. A single long unbreakable path in one cell
       otherwise widens the whole table past the viewport and the panel clips it. */
    .grid, .grid tbody { display: block; }
    .grid thead { display: none; }
    .grid tbody tr { display: block; border-bottom: 1px solid var(--line); padding: .6rem 0; }
    .grid tbody tr:last-child { border-bottom: 0; }
    .grid tbody td { display: block; border-bottom: 0; padding: .1rem .875rem; overflow-wrap: anywhere; }

    /* Stacked cells lose their column header, so the ones that are bare numbers
       carry their own label. */
    .grid tbody td[data-label]::before {
        content: attr(data-label) " ";
        color: var(--ink-faint);
        font-size: var(--t-xs);
        text-transform: uppercase;
        letter-spacing: .06em;
    }
    .grid tbody td.right { text-align: left; }
    .grid tbody td:empty { display: none; }

    .tl-row { grid-template-columns: 4.25rem 1rem minmax(0, 1fr); }
    .tl-when { font-size: 10px; }

    /* Time and level lead; the message gets the full width rather than the
       5rem gutter it would otherwise inherit from column one. */
    .stream-line { grid-template-columns: auto auto 1fr; row-gap: .2rem; column-gap: .5rem; padding: .5rem .875rem; }
    .stream-line .src { grid-column: 3; justify-self: end; }
    .stream-line .msg { grid-column: 1 / -1; white-space: normal; }

    .filters { gap: .625rem; }
    .filter-search, .filter-pick { flex: 1 1 100%; }

    .head { gap: .75rem; }
    .stats { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 460px) {
    .stats { grid-template-columns: 1fr 1fr; }
    .stat dd { font-size: var(--t-xl); }
}

/* ── Monitoring ─────────────────────────────────────────────────────────────
   A meter is a number first and a bar second: the bar gives you the shape at a
   glance, the number is what you quote to someone. */

.meter { display: flex; flex-direction: column; gap: .35rem; min-width: 0; }
.meter__head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.meter__label { font-size: var(--t-sm); color: var(--ink-dim); }
.meter__value { font-size: var(--t-sm); font-weight: 600; }
.meter__track { height: 4px; border-radius: 2px; background: var(--bg); overflow: hidden; }
/* Scaled rather than resized: animating width makes the browser lay the row out
   again on every frame, and there are up to five of these per table row. */
.meter__fill { height: 100%; width: 100%; border-radius: 2px; transform-origin: left center; transition: transform .3s ease; }

.chart { display: block; width: 100%; height: 64px; color: var(--accent); }
.chart--cpu { color: #60a5fa; }
.chart--ram { color: #a78bfa; }
.chart--disk { color: #fbbf24; }

/* Three metrics side by side on a desktop, stacked on a phone. Every column is
   allowed to shrink, or a long hostname forces the row wider than the panel. */
.vitals { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 1rem 1.5rem; min-width: 0; }
.vitals > * { min-width: 0; }

.chart-stack { display: grid; gap: 1.25rem; }
.chart-stack section { min-width: 0; }
.chart-stack h3 { font-size: var(--t-sm); font-weight: 600; color: var(--ink-dim); margin: 0 0 .5rem; display: flex; justify-content: space-between; }

.dot { width: .5rem; height: .5rem; border-radius: 50%; display: inline-block; flex: none; }
.dot--ok { background: #34d399; }
.dot--bad { background: var(--sev-error); }
.dot--idle { background: var(--ink-faint); }

/* ── Dialogs ────────────────────────────────────────────────────────────────
   Native <dialog>, so focus trapping, Escape and the backdrop come from the
   platform instead of from three hundred lines of script. */

.sheet {
    border: 1px solid var(--line-bright);
    border-radius: var(--r-lg);
    background: var(--surface);
    color: var(--ink);
    padding: 0;
    width: min(46rem, calc(100vw - 2rem));
    max-height: calc(100vh - 4rem);
    box-shadow: 0 24px 64px -12px rgba(0, 0, 0, .7);
    overflow: hidden;
}
.sheet::backdrop { background: rgba(4, 3, 8, .72); backdrop-filter: blur(3px); }
/* A modal dialog centres itself via auto margins; without them it pins to the
   top-left corner of the viewport. */
.sheet[open] { display: flex; flex-direction: column; margin: auto; }

.sheet__head {
    display: flex; align-items: center; gap: .75rem;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--line);
    background: var(--bg);
}
.sheet__title { font-size: var(--t-md); font-weight: 600; margin: 0; }
.sheet__body { overflow-y: auto; padding: 0; flex: 1; min-height: 0; }
.sheet__foot {
    display: flex; align-items: center; gap: .5rem;
    padding: .85rem 1.25rem;
    border-top: 1px solid var(--line);
    background: var(--bg);
}

/* ── Accordion ──────────────────────────────────────────────────────────────
   One numbered step per section, with the chosen value echoed in the header so
   a collapsed form still tells you what it is set to. */

.steps { display: block; }
.step { border-bottom: 1px solid var(--line); }
.step:last-child { border-bottom: 0; }

.step__toggle {
    display: flex; align-items: center; gap: .6rem; width: 100%;
    padding: .85rem 1.25rem;
    background: none; border: 0; color: var(--ink);
    font: inherit; font-weight: 600; text-align: left; cursor: pointer;
    transition: background .12s var(--ease);
}
.step__toggle:hover { background: var(--hover); }
.step__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.step[data-open] > .step__toggle { background: var(--accent-wash); }
.step__n {
    flex: none; width: 1.35rem; height: 1.35rem; border-radius: 50%;
    display: grid; place-items: center;
    background: var(--line-bright); color: var(--ink-dim);
    font-family: var(--mono); font-size: var(--t-xs); font-weight: 700;
}
.step[data-open] > .step__toggle .step__n { background: var(--accent); color: #160f2b; }
.step__echo { margin-left: auto; font-weight: 400; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-faint); }
.step__body { padding: .35rem 1.25rem 1.1rem; }
.step:not([data-open]) > .step__body { display: none; }

/* ── Forms ──────────────────────────────────────────────────────────────── */

.f { display: grid; grid-template-columns: 11rem 1fr; gap: .55rem 1rem; align-items: baseline; }
.f > label { font-size: var(--t-sm); color: var(--ink-dim); text-align: right; padding-top: .4rem; }
.f__row { display: contents; }
.f__row[hidden] { display: none; }
.f__hint { grid-column: 2; font-size: var(--t-xs); color: var(--ink-faint); margin: -.2rem 0 .35rem; }
.f__wide { grid-column: 1 / -1; }

@media (max-width: 40rem) {
    .f { grid-template-columns: 1fr; }
    .f > label { text-align: left; padding-top: .5rem; }
    .f__hint { grid-column: 1; }
}

.field--sm { padding: .35rem .5rem; font-size: var(--t-sm); }
.field.is-bad, .select.is-bad { border-color: var(--sev-error); }

.f__error {
    grid-column: 1 / -1;
    display: flex; gap: .5rem; align-items: flex-start;
    padding: .6rem .75rem; margin-bottom: .5rem;
    border: 1px solid var(--sev-error); border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--sev-error) 12%, transparent);
    font-size: var(--t-sm); color: var(--ink);
}
.f__error[hidden] { display: none; }
.f__errorhint { font-size: var(--t-xs); color: var(--ink-dim); margin-top: .3rem; line-height: 1.5; }

/* ── Inline help ────────────────────────────────────────────────────────────
   Collapsed by default: it is the answer to a question most people will not
   have, and open by default it would push the form it explains off the screen. */
.help {
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--bg);
    margin-top: .5rem;
}
.help > summary {
    cursor: pointer;
    padding: .5rem .75rem;
    font-size: var(--t-sm);
    color: var(--ink-dim);
    list-style: none;
    display: flex;
    align-items: center;
    gap: .4rem;
}
.help > summary::-webkit-details-marker { display: none; }
.help > summary::before {
    content: "";
    width: 0; height: 0;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    border-left: 5px solid currentColor;
    opacity: .5;
    transition: transform 140ms var(--ease);
}
.help[open] > summary::before { transform: rotate(90deg); }
.help > summary:hover { color: var(--ink); }
.help[open] > summary { border-bottom: 1px solid var(--line); color: var(--ink); }

.help__body { padding: .75rem; display: grid; gap: .5rem; font-size: var(--t-sm); }
.help__body h4 {
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-dim);
    text-transform: none;
    letter-spacing: 0;
    margin-top: .35rem;
}
.help__body h4 .mono { color: var(--sev-error); }
.help__body p { line-height: 1.55; font-size: var(--t-xs); }
.help__body .code { font-size: var(--t-xs); }

/* A row of small buttons in a table cell, wrapping on narrow screens rather
   than forcing the table wider than the panel. */
.rowacts { display: flex; flex-wrap: wrap; gap: .3rem; justify-content: flex-end; }

.chart--net-in  { color: #34d399; }
.chart--net-out { color: #22d3ee; }
.chart--conn    { color: #f472b6; }
.chart--lat     { color: #94a3b8; }
.chart--other   { color: var(--accent); }

/* ── DataTables ─────────────────────────────────────────────────────────────
   The stock stylesheet is built for a white page, so nearly all of it is
   overridden here. Only the chrome is restyled — the table itself keeps the
   .grid rules, so a sorted table looks exactly like an unsorted one. */

.dt-container { --dt-row-selected: var(--accent); }

.dt-container .dt-layout-row {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    padding: .7rem 1rem;
}
.dt-container .dt-layout-row:first-child { border-bottom: 1px solid var(--line); }
.dt-container .dt-layout-row:last-child { border-top: 1px solid var(--line); }
.dt-container .dt-layout-cell { display: flex; align-items: center; gap: .5rem; }
.dt-container .dt-layout-cell.dt-layout-full { flex: 1; }
.dt-container .dt-layout-cell:last-child:not(:only-child) { margin-left: auto; }

.dt-search, .dt-length { display: flex; align-items: center; gap: .45rem; }
.dt-search label, .dt-length label { font-size: var(--t-xs); color: var(--ink-faint); }

/* DataTables ships its own light styling for these two controls at
   `div.dt-container div.dt-length select` — three elements and two classes,
   which outranks anything shorter no matter the load order. Match its shape
   and add the control's own class so ours wins outright. The search box is
   `type="text"`, not `type="search"`, so selecting on the type missed it. */
div.dt-container div.dt-length select.dt-input,
div.dt-container div.dt-search input.dt-input {
    background: var(--bg);
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    color: var(--ink);
    padding: .3rem .55rem;
    font: inherit;
    font-size: var(--t-sm);
    margin: 0;
}
div.dt-container div.dt-search input.dt-input { min-width: 14rem; }
div.dt-container div.dt-length select.dt-input:focus,
div.dt-container div.dt-search input.dt-input:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: var(--accent);
}
div.dt-container div.dt-search input.dt-input::placeholder { color: var(--ink-faint); }

.dt-info { font-size: var(--t-xs); color: var(--ink-faint); }

.dt-paging { display: flex; gap: .25rem; flex-wrap: wrap; }
.dt-paging .dt-paging-button {
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--r);
    color: var(--ink-dim);
    padding: .25rem .55rem;
    min-width: 2rem;
    font: inherit;
    font-size: var(--t-sm);
    cursor: pointer;
    transition: background 120ms var(--ease), color 120ms var(--ease);
}
.dt-paging .dt-paging-button:hover:not(.disabled) { background: var(--hover); color: var(--ink); }
.dt-paging .dt-paging-button.current {
    background: var(--accent-wash);
    border-color: var(--accent-line);
    color: var(--accent);
    font-weight: 600;
}
.dt-paging .dt-paging-button.disabled { opacity: .35; cursor: default; }

/* Sort affordance. Drawn with borders rather than a font glyph so it shares the
   stroke weight of the rest of the interface and cannot be resized by whatever
   font happens to have the arrow character.

   DataTables' own rules carry an extra class — `th.dt-orderable-asc span.dt-…`
   — so anything shorter loses to them no matter the load order. Every selector
   below matches that shape and adds `.grid`, which only our tables have. */

table.dataTable thead th.dt-orderable-asc,
table.dataTable thead th.dt-orderable-desc { cursor: pointer; user-select: none; }
table.dataTable thead th.dt-orderable-asc:hover,
table.dataTable thead th.dt-orderable-desc:hover { color: var(--accent); }

/* Pinned to the right edge of the cell by default, which on a wide column
   leaves the arrows stranded halfway across the header, nowhere near the word
   they belong to. Inline, immediately after the label, instead. */
table.dataTable.grid thead > tr > th.dt-orderable-asc span.dt-column-order,
table.dataTable.grid thead > tr > th.dt-orderable-desc span.dt-column-order,
table.dataTable.grid thead > tr > td.dt-orderable-asc span.dt-column-order,
table.dataTable.grid thead > tr > td.dt-orderable-desc span.dt-column-order {
    position: relative;
    display: inline-block;
    vertical-align: middle;
    inset: auto;
    width: 7px;
    height: 9px;
    margin-left: .4rem;
}

/* No padding reserved on the right any more: the indicator is in the flow. */
table.dataTable.grid thead > tr > th.dt-orderable-asc,
table.dataTable.grid thead > tr > th.dt-orderable-desc { padding-right: 1rem; }
table.dataTable.grid thead > tr > th.dt-orderable-asc.right,
table.dataTable.grid thead > tr > th.dt-orderable-desc.right { padding-right: 1rem; }

table.dataTable.grid thead > tr > th.dt-orderable-asc span.dt-column-order::before,
table.dataTable.grid thead > tr > th.dt-orderable-desc span.dt-column-order::before,
table.dataTable.grid thead > tr > th.dt-orderable-asc span.dt-column-order::after,
table.dataTable.grid thead > tr > th.dt-orderable-desc span.dt-column-order::after,
table.dataTable.grid thead > tr > td.dt-orderable-asc span.dt-column-order::before,
table.dataTable.grid thead > tr > td.dt-orderable-desc span.dt-column-order::before,
table.dataTable.grid thead > tr > td.dt-orderable-asc span.dt-column-order::after,
table.dataTable.grid thead > tr > td.dt-orderable-desc span.dt-column-order::after {
    content: "";
    position: absolute;
    left: 0;
    right: auto;
    display: block;
    width: 0;
    height: 0;
    border-left: 3.5px solid transparent;
    border-right: 3.5px solid transparent;
    opacity: .3;
    transition: opacity 130ms var(--ease);
}

/* The pair meets at the middle with a hairline between them, so at this size it
   still reads as two arrows rather than one smudge. */
table.dataTable.grid thead > tr > th.dt-orderable-asc span.dt-column-order::before,
table.dataTable.grid thead > tr > th.dt-orderable-desc span.dt-column-order::before,
table.dataTable.grid thead > tr > td.dt-orderable-asc span.dt-column-order::before,
table.dataTable.grid thead > tr > td.dt-orderable-desc span.dt-column-order::before {
    bottom: 50%;
    top: auto;
    margin-bottom: 1px;
    border-bottom: 3.5px solid currentColor;
    border-top: 0;
}
table.dataTable.grid thead > tr > th.dt-orderable-asc span.dt-column-order::after,
table.dataTable.grid thead > tr > th.dt-orderable-desc span.dt-column-order::after,
table.dataTable.grid thead > tr > td.dt-orderable-asc span.dt-column-order::after,
table.dataTable.grid thead > tr > td.dt-orderable-desc span.dt-column-order::after {
    top: 50%;
    bottom: auto;
    margin-top: 1px;
    border-top: 3.5px solid currentColor;
    border-bottom: 0;
}

/* Both dim until the column is doing something, then only the live direction
   lights up — an indicator where both halves look equal says nothing. */
table.dataTable.grid thead > tr > th.dt-ordering-asc span.dt-column-order::before,
table.dataTable.grid thead > tr > th.dt-ordering-desc span.dt-column-order::after { opacity: 1; }
table.dataTable.grid thead > tr > th.dt-ordering-asc span.dt-column-order::after,
table.dataTable.grid thead > tr > th.dt-ordering-desc span.dt-column-order::before { opacity: .18; }

table.dataTable.grid thead > tr > th.dt-orderable-asc:hover span.dt-column-order::before,
table.dataTable.grid thead > tr > th.dt-orderable-desc:hover span.dt-column-order::after { opacity: .6; }

table.dataTable thead th.dt-ordering-asc,
table.dataTable thead th.dt-ordering-desc { color: var(--accent); }

/* A search that matches nothing should say so in the table's own voice. */
table.dataTable td.dt-empty { padding: 2.5rem 1rem; text-align: center; color: var(--ink-faint); }

.dt-container .dt-paging-button:focus-visible,
.dt-container input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── DataTables corrections ─────────────────────────────────────────────────
   The sticky header is pinned 48px down to clear the app bar, which works when
   the table is the panel's only child. DataTables inserts control rows above
   it, so the header ends up floating over the first row instead. Paging means
   these tables barely scroll anyway, so they give up stickiness. */
table.dataTable thead th { position: static; top: auto; }

/* A column that cannot be sorted should not draw a sort control. */
table.dataTable thead th:not(.dt-orderable-asc):not(.dt-orderable-desc) .dt-column-order { display: none; }

/* DataTables wraps the table in its own element; the panel's rounded corners
   should still win. */
.panel > .dt-container { border-radius: inherit; overflow: hidden; }

/* ── Navigation ─────────────────────────────────────────────────────────────
   Ten sections plus the brand and the agent counter is more than fits at
   1280px. The brand stays whole, and the tabs scroll rather than wrapping the
   bar onto a second line. */
.brand { white-space: nowrap; flex: none; }
.shell .tabs {
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
    flex-wrap: nowrap;
}
.shell .tabs::-webkit-scrollbar { display: none; }
.shell .tab { flex: none; }

@media (max-width: 1100px) {
    .shell .tab span { display: none; }
    .shell .tab[aria-current] span { display: inline; }
}

/* ── Password reveal ────────────────────────────────────────────────────────
   The toggle sits inside the field's box rather than beside it, so a form of
   mixed field types keeps one alignment. */
.reveal { position: relative; display: block; }
.reveal > input { width: 100%; padding-right: 2.1rem; }
.reveal__btn {
    position: absolute;
    right: .3rem;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    padding: 0;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--ink-faint);
    cursor: pointer;
    transition: color 120ms var(--ease), background 120ms var(--ease);
}
.reveal__btn:hover { color: var(--ink); background: var(--hover); }
.reveal__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── Chart hover ────────────────────────────────────────────────────────────
   The chart itself is static SVG; this is the readout layer. Positioned
   relative so the crosshair, the dot and the tooltip all share one coordinate
   space. */
.chartwrap { position: relative; }
.chartwrap:hover { cursor: crosshair; }

.chart__axis {
    position: relative;
    height: 1rem;
    margin-top: .3rem;
    font-family: var(--mono);
    font-size: 10px;
    color: var(--ink-faint);
}
.chart__axis span {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    white-space: nowrap;
}
/* The first and last labels would otherwise hang off the panel. */
.chart__axis span:first-child { transform: none; }
.chart__axis span:last-child { transform: translateX(-100%); }

.chart__tip {
    position: absolute;
    bottom: calc(100% - .4rem);
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .35rem .55rem;
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    background: var(--raised);
    box-shadow: var(--lift);
    pointer-events: none;
    white-space: nowrap;
    z-index: 5;
}
.chart__tip[hidden] { display: none; }
.chart__tip-value { font-family: var(--mono); font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.chart__tip-time { font-size: 10px; color: var(--ink-faint); }

/* A tag that is also a link — used where a row points at the machine behind it. */
a.tag { text-decoration: none; transition: border-color 120ms var(--ease), color 120ms var(--ease); }
a.tag:hover { border-color: var(--accent-line); color: var(--accent); }

/* --- Rendered markdown: the AI analysis ------------------------------------ */

.prose { font-size: var(--t-base); line-height: 1.55; }
.prose h2, .prose h3 { font-size: var(--t-sm); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-dim); margin: 1.25rem 0 .4rem; }
.prose h2:first-child, .prose h3:first-child { margin-top: .5rem; }
.prose p, .prose ul, .prose ol { margin: 0 0 .6rem; }
.prose ul, .prose ol { padding-left: 1.25rem; }
.prose code { font-family: var(--mono); font-size: var(--t-sm); background: var(--bg); padding: .05rem .3rem; border-radius: var(--r-sm); }
.prose pre { font-family: var(--mono); font-size: var(--t-sm); background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-sm); padding: .6rem .75rem; overflow: auto; }
.prose pre code { background: none; padding: 0; }

/* --- Schedule grid ---------------------------------------------------------
   One row per backup, the day across. The hour lines are a gradient, so the
   markup carries only the bars; each bar's colour is its box's. */
.schedule tbody td { padding: .45rem 1rem; }
.schedule .when { display: inline-flex; gap: .3rem; align-items: center; flex-wrap: wrap; }
.timeline {
    position: relative;
    height: 1.4rem;
    border-left: 1px solid var(--line);
    background: repeating-linear-gradient(90deg, var(--line) 0 1px, transparent 1px calc(100% / 24));
}
.timeline--axis { height: 1rem; border-left: 0; background: none; }
.timeline--axis span {
    position: absolute; top: 0; transform: translateX(-50%);
    font-size: var(--t-xs); color: var(--ink-faint); font-variant-numeric: tabular-nums;
}
.timeline--axis span:first-child { transform: none; }
.bar { position: absolute; top: .2rem; bottom: .2rem; min-width: 3px; border-radius: 2px; background: var(--fill); opacity: .85; }
.bar--external { background: repeating-linear-gradient(135deg, var(--fill) 0 3px, transparent 3px 6px); border: 1px solid var(--fill); opacity: .7; }
.bar--untimed { background: transparent; border: 1px dashed var(--fill); }
.bar--overlap { outline: 2px solid var(--sev-error); outline-offset: 1px; }

/* --- The map: a sky of floating server cards ------------------------------ */

.space { position: relative; overflow: hidden; min-height: calc(100vh - 3.5rem); background: radial-gradient(1200px 700px at 50% -10%, #17122a 0%, var(--bg) 60%); }
.space__top { position: relative; z-index: 20; display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; padding: 1rem 1.5rem; background: linear-gradient(180deg, rgba(11,9,16,.92), rgba(11,9,16,.55) 75%, transparent); }
.space__top #map-search { flex: 1; min-width: 12rem; max-width: 24rem; border-radius: 999px; }
.space__stats { display: flex; gap: .4rem; margin-left: auto; flex-wrap: wrap; }
.space__stats span { background: rgba(28,24,38,.8); border: 1px solid var(--line); border-radius: 999px; padding: .2rem .7rem; font-size: var(--t-xs); color: var(--ink-dim); white-space: nowrap; backdrop-filter: blur(6px); }
.space__stats b { color: var(--ink); }
.space__stats .is-warn b { color: var(--sev-warning); }
.space__stats .is-bad b { color: var(--sev-error); }

.space__stars, .space__nebula, .space__links { position: absolute; inset: 0; pointer-events: none; }
.space__links { width: 100%; height: 100%; }
.space__links line { stroke: var(--accent-line); stroke-width: 1.2; stroke-dasharray: 3 5; }
.star { position: absolute; border-radius: 50%; background: #d9d2f5; animation: twinkle var(--td, 4s) ease-in-out var(--tdel, 0s) infinite alternate; }
@keyframes twinkle { from { opacity: .12; } to { opacity: .85; } }
.space__nebula { background:
    radial-gradient(600px 380px at 22% 30%, rgba(167,139,250,.10), transparent 65%),
    radial-gradient(520px 340px at 84% 18%, rgba(56,189,248,.08), transparent 65%),
    radial-gradient(560px 380px at 78% 82%, rgba(74,222,128,.07), transparent 65%),
    radial-gradient(400px 300px at 45% 70%, rgba(251,113,133,.06), transparent 65%); }

.const-label { position: absolute; transform: translate(-50%, -50%); color: var(--accent); font-size: var(--t-sm); font-weight: 650; letter-spacing: .14em; text-transform: uppercase; text-shadow: 0 0 16px var(--accent-wash); white-space: nowrap; pointer-events: none; opacity: .9; }
.const-label::before { content: '✦ '; }

.node { position: absolute; transform: translate(-50%, -50%); z-index: 2; cursor: pointer; border: 0; background: none; padding: 0; color: var(--ink); font: inherit; text-align: left; animation: drift var(--dur, 9s) ease-in-out var(--del, 0s) infinite alternate; transition: opacity .25s, filter .25s; }
@keyframes drift { from { margin-left: calc(var(--dx) * -1); margin-top: calc(var(--dy) * -1); } to { margin-left: var(--dx); margin-top: var(--dy); } }
.node:hover { z-index: 6; }
.node.dim { opacity: .15; filter: grayscale(.8); }
.vcard { display: block; width: 236px; background: linear-gradient(180deg, rgba(36,31,48,.94), rgba(20,17,28,.94)); border: 1px solid color-mix(in oklab, var(--acc) 40%, var(--line)); border-top: 3px solid var(--acc); border-radius: var(--r-lg); padding: .6rem .75rem .55rem; box-shadow: 0 0 20px color-mix(in oklab, var(--acc) 22%, transparent), 0 14px 30px rgba(0,0,0,.5); backdrop-filter: blur(3px); }
.node:hover .vcard, .node.hit .vcard { box-shadow: 0 0 34px color-mix(in oklab, var(--acc) 55%, transparent), 0 14px 30px rgba(0,0,0,.55); }
.node:focus-visible .vcard { outline: 2px solid var(--accent); outline-offset: 2px; }
.vhead { display: flex; align-items: center; gap: .4rem; margin-bottom: .4rem; flex-wrap: wrap; }
.vdot { width: .5rem; height: .5rem; border-radius: 50%; background: var(--acc); box-shadow: 0 0 8px var(--acc); flex: none; }
.vname { font-family: var(--mono); font-size: var(--t-sm); font-weight: 700; color: var(--acc); white-space: nowrap; }
.vaddr { margin-left: auto; font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-faint); }
.vbadge { background: color-mix(in oklab, var(--sev-error) 18%, transparent); color: var(--sev-error); border: 1px solid color-mix(in oklab, var(--sev-error) 45%, transparent); border-radius: 999px; padding: 0 .45rem; font-size: var(--t-xs); font-weight: 650; white-space: nowrap; }
.vspecs { display: flex; flex-wrap: wrap; gap: .25rem; margin-bottom: .45rem; }
.vchip { background: rgba(255,255,255,.05); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 0 .4rem; font-size: var(--t-xs); color: var(--ink-dim); white-space: nowrap; font-family: var(--mono); }
.vchip small { color: var(--ink-faint); font-size: .92em; font-family: var(--sans); }
.vchip--warn { color: var(--sev-warning); border-color: color-mix(in oklab, var(--sev-warning) 40%, var(--line)); }
.vchip--bad { color: var(--sev-error); border-color: color-mix(in oklab, var(--sev-error) 40%, var(--line)); }
.vfoot { display: flex; align-items: center; gap: .4rem; }
.vcnt { font-size: var(--t-xs); color: var(--ink); }
.vwarn { color: var(--sev-warning); }
.vmore { margin-left: auto; font-size: var(--t-xs); color: var(--ink-faint); }
.node:hover .vmore { color: var(--acc); }

.space-overlay { position: fixed; inset: 0; z-index: 40; background: rgba(6,4,10,.7); backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; padding: 1.25rem; }
.space-panel { position: relative; width: min(560px, 100%); max-height: 86vh; overflow-y: auto; background: linear-gradient(180deg, #221c33, var(--surface)); border: 1px solid color-mix(in oklab, var(--acc, var(--accent)) 45%, transparent); border-top: 3px solid var(--acc, var(--accent)); border-radius: var(--r-lg); padding: 1.4rem 1.5rem; box-shadow: 0 0 60px color-mix(in oklab, var(--acc, var(--accent)) 25%, transparent), 0 24px 60px rgba(0,0,0,.6); animation: pop .18s ease-out; }
@keyframes pop { from { transform: scale(.94); opacity: 0; } }
.space-panel__close { position: absolute; top: .6rem; right: .8rem; background: none; border: 0; color: var(--ink-faint); font-size: 1.1rem; cursor: pointer; padding: .25rem .5rem; }
.space-panel__close:hover { color: var(--ink); }
.space-panel__group { font-size: var(--t-xs); color: var(--accent); letter-spacing: .1em; text-transform: uppercase; margin-bottom: .4rem; }
.space-panel header { display: flex; align-items: center; gap: .6rem; margin-bottom: .4rem; padding-right: 1.6rem; }
.space-panel h3 { margin: 0; font-family: var(--mono); font-size: var(--t-lg); color: var(--acc, var(--accent)); }
.space-panel h4 { margin: 1rem 0 .3rem; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-dim); }
.space-panel__meta { display: flex; flex-wrap: wrap; gap: .3rem .9rem; font-size: var(--t-sm); color: var(--ink-dim); }
.space-panel__line { font-size: var(--t-sm); color: var(--ink-dim); }
.space-panel__sites { list-style: none; margin: 0; padding: 0 0 0 .6rem; }
.space-panel__sites li { position: relative; padding: .2rem 0 .2rem 1.2rem; font-family: var(--mono); font-size: var(--t-sm); display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.space-panel__sites li::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; border-left: 2px solid var(--line); }
.space-panel__sites li:last-child::before { bottom: calc(100% - .9rem); }
.space-panel__sites li::after { content: ''; position: absolute; left: 0; top: .85rem; width: .8rem; border-top: 2px solid var(--line); }
.space-panel__sites em { color: var(--ink-faint); font-style: normal; font-size: var(--t-xs); font-family: var(--sans); }
.space-panel__foot { margin-top: 1rem; }

@media (max-width: 600px) { .space__stats { display: none; } }
.space-overlay[hidden] { display: none; }

/* --- Incidents: what is open right now ------------------------------------- */

.incidents { display: grid; grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)); gap: .75rem; margin-bottom: .5rem; }
.incident { background: color-mix(in oklab, var(--sev-error) 7%, var(--surface)); border: 1px solid color-mix(in oklab, var(--sev-error) 35%, var(--line)); border-top: 3px solid var(--sev-error); border-radius: var(--r-lg); padding: .85rem 1rem; }
.incident__head { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.incident__dot { width: .5rem; height: .5rem; border-radius: 50%; background: var(--sev-error); box-shadow: 0 0 8px var(--sev-error); animation: beat 1.6s ease-in-out infinite; flex: none; }
.incident__name { font-weight: 600; color: var(--ink); text-decoration: none; }
a.incident__name:hover { color: var(--sev-error); }
.incident__what { margin: .35rem 0 .6rem; color: var(--ink-dim); font-size: var(--t-sm); }
.incident__facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .6rem; margin: 0; }
.incident__facts dt { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); }
.incident__facts dd { margin: .1rem 0 0; font-size: var(--t-base); color: var(--ink); overflow-wrap: anywhere; }
.incident__facts .faint { font-size: var(--t-xs); }

/* --- Row menu: the actions a row is not opened for ------------------------- */
/* A native <details>, so it opens without script; the script only closes it
   when the pointer leaves for elsewhere. Sits over the page, so it carries
   the raised surface like every other dropdown. */
.menu { position: relative; }
.menu > summary { list-style: none; }
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary svg { transition: transform 160ms var(--ease); }
.menu[open] > summary svg { transform: rotate(180deg); }
.menu__list {
    position: absolute;
    right: 0;
    top: calc(100% + .25rem);
    z-index: 30;
    min-width: 11rem;
    padding: .3rem;
    background: var(--raised);
    border: 1px solid var(--line-bright);
    border-radius: var(--r);
    box-shadow: 0 10px 30px rgb(0 0 0 / .35);
    display: flex;
    flex-direction: column;
}
.menu__list form { display: contents; }
.menu__item {
    display: block;
    width: 100%;
    padding: .4rem .65rem;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--ink);
    font: inherit;
    font-size: var(--t-sm);
    text-align: left;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
}
.menu__item:hover { background: var(--hover); color: var(--ink); }
.menu__item--danger { color: var(--sev-error); }
.menu__item--danger:hover { background: color-mix(in oklab, var(--sev-error) 14%, var(--raised)); }

/* The tab bar scrolls sideways, and a scrolling box clips whatever hangs out
   of it. The Others menu therefore floats in a fixed layer, placed under its
   tab by script when it opens. */
.menu--tab { flex: none; }
.menu--tab > summary { cursor: pointer; }
.menu--tab > .menu__list { position: fixed; right: auto; top: auto; min-width: 9rem; }
.menu--tab .menu__item[aria-current="page"] { color: var(--accent); background: var(--accent-wash); }

/* The day grid is drawn wide and scrolls sideways; the first two columns stay
   put. Cells are border-box so the second pinned column can start exactly
   where the first one's declared width ends. */
.schedule-scroll { overflow-x: auto; }
.schedule--day { --day-width: 2880px; }
.schedule--day th, .schedule--day td { box-sizing: border-box; }
.schedule--day .timeline { min-width: var(--day-width); }
.schedule .is-pinned { position: sticky; z-index: 2; background: var(--surface); }
.schedule thead .is-pinned { z-index: 3; }
.schedule tbody tr:hover .is-pinned { background: var(--hover); }
.schedule .is-pinned--first { left: 0; }
.schedule .is-pinned--second { left: 15rem; box-shadow: 1px 0 0 var(--line); }
.filters { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; margin: -.25rem 0 1rem; font-size: var(--t-sm); color: var(--ink-faint); }
.filters label { display: inline-flex; align-items: center; gap: .35rem; }

/* The schedule grid's first column carries the backup's facts under its
   name; the second pinned column starts where that column's width ends. */
.schedule .is-pinned--second { left: 22rem; }
.facts { display: grid; grid-template-columns: max-content 1fr; gap: .1rem .5rem; margin-top: .35rem; font-size: var(--t-xs); color: var(--ink-faint); }
.facts dt { text-transform: uppercase; letter-spacing: .04em; font-size: .62rem; align-self: baseline; }
.facts dd { margin: 0; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A dropdown has to leave its box. The dialog and the panel clip their own
   overflow to keep their corners rounded, which also cut off a select's option
   list inside a dialog and an open row menu near the bottom of a panel. The
   clipping is lifted only while one is open, so the corners stay rounded the
   rest of the time. Dropdowns in a dialog belong to the dialog on purpose — a
   modal's top layer would paint over one appended to the body. */
.sheet:has(.select2-container--open) { overflow: visible; }
.panel:has(.menu[open]) { overflow: visible; }

/* Inside the sideways-scrolling panel the table header must not pin to the
   top as well: the panel is then its scroll context, and the header pins a
   header's height down from the panel's top and covers the first row. Left
   pinning stays for the two named columns. */
.schedule thead th { top: auto; }

/* Table layout treats a header's declared width as advice once the timeline
   column forces the table wider than the panel; the pinned columns then shrank
   to their content and the second one, pinned at 22rem, landed on the
   timeline. Minimum widths on the cells are honoured regardless. */
.schedule .is-pinned--first { width: 22rem; min-width: 22rem; max-width: 22rem; }
.schedule .is-pinned--second { width: 19rem; min-width: 19rem; max-width: 19rem; }
/* Pinned header cells sit above the axis header, which keeps the base
   header's z-index and otherwise scrolls over them. */
.schedule thead .is-pinned { z-index: 12; }

/* --- Dashboard: verdict, tiles, groups -------------------------------------
   The page opens with one line saying whether anything is wrong, then a tile
   per part of the application. A tile is a link, wears its state as colour,
   and draws the last month under its number so the direction is read with
   the count. Green means green everywhere here: ok, warn, bad, and a neutral
   accent for the counts that are neither good nor bad. */

.verdict {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    padding: .7rem 1rem;
    margin-bottom: 1rem;
    border-radius: var(--r-lg);
    border: 1px solid color-mix(in oklab, var(--tone) 32%, var(--line));
    background: color-mix(in oklab, var(--tone) 7%, var(--surface));
    font-size: var(--t-base);
}
.verdict--ok   { --tone: var(--ok); }
.verdict--warn { --tone: var(--sev-warning); }
.verdict--bad  { --tone: var(--sev-error); }
.verdict strong { color: var(--tone); font-weight: 650; }
.verdict__dot {
    width: .6rem; height: .6rem; border-radius: 50%; flex: none;
    background: var(--tone);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--tone) 22%, transparent);
}
.verdict--bad .verdict__dot { animation: beat 1.6s ease-in-out infinite; }
.verdict__items { display: flex; flex-wrap: wrap; gap: .35rem .5rem; color: var(--ink-dim); }
.verdict__item {
    display: inline-flex; align-items: center;
    padding: .05rem .5rem;
    border-radius: 999px;
    font-size: var(--t-sm);
    font-weight: 550;
    color: var(--tone);
    background: color-mix(in oklab, var(--tone) 14%, transparent);
    border: 1px solid color-mix(in oklab, var(--tone) 30%, transparent);
}
.verdict__item--bad  { --tone: var(--sev-error); }
.verdict__item--warn { --tone: var(--sev-warning); }

.kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .75rem; }
@media (min-width: 1500px) { .kpis { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 460px)  { .kpis { grid-template-columns: minmax(0, 1fr); } }

.kpi {
    --tone: var(--ink-dim);
    display: grid;
    gap: .35rem;
    padding: .9rem 1rem .8rem;
    min-width: 0;
    border-radius: var(--r-lg);
    border: 1px solid color-mix(in oklab, var(--tone) 28%, var(--line));
    background:
        radial-gradient(120% 90% at 100% 0%, color-mix(in oklab, var(--tone) 12%, transparent), transparent 60%),
        var(--surface);
    color: var(--ink);
    text-decoration: none;
    transition: border-color 160ms var(--ease), transform 160ms var(--ease), box-shadow 160ms var(--ease);
}
.kpi:hover { border-color: color-mix(in oklab, var(--tone) 55%, var(--line)); transform: translateY(-1px); box-shadow: var(--lift); }
.kpi--ok   { --tone: var(--ok); }
.kpi--warn { --tone: var(--sev-warning); }
.kpi--bad  { --tone: var(--sev-error); }
.kpi--info { --tone: var(--accent); }

.kpi__top { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.kpi__label { color: var(--ink-faint); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; white-space: nowrap; }
.kpi__pill {
    margin-left: auto;
    padding: .05rem .5rem;
    border-radius: 999px;
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--tone);
    background: color-mix(in oklab, var(--tone) 14%, transparent);
    border: 1px solid color-mix(in oklab, var(--tone) 30%, transparent);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 60%;
}
.kpi__value {
    font-family: var(--mono);
    font-size: 2.25rem;
    font-weight: 600;
    letter-spacing: -.03em;
    line-height: 1;
    color: var(--tone);
    margin-top: .15rem;
}
.kpi--info .kpi__value { color: var(--ink); }
.kpi__of { font-size: var(--t-lg); font-weight: 500; color: var(--ink-faint); letter-spacing: 0; }
.kpi__trend { display: grid; gap: .25rem; margin-top: .25rem; color: var(--tone); }
.kpi--info .kpi__trend { color: var(--accent); }
.kpi__trend .trend { width: 100%; height: 30px; }
.kpi__delta { font-size: var(--t-xs); color: var(--ink-faint); font-family: var(--sans); }
.kpi__delta--bad  { color: var(--sev-error); }
.kpi__delta--good { color: var(--ok); }
.kpi__foot { font-size: var(--t-sm); color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kpi:hover .kpi__foot { color: var(--ink); }

/* Sparkline bars in the colour of whatever they sit in. */
.trend { display: block; overflow: visible; }
.trend__bar { fill: currentColor; opacity: .85; }
.trend__base { stroke: currentColor; stroke-opacity: .25; stroke-width: 1; }

/* A ring of member states. */
.ring { position: relative; width: var(--ring-size, 3.5rem); height: var(--ring-size, 3.5rem); flex: none; }
.ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.ring__track { fill: none; stroke: var(--bg); stroke-width: 5; }
.ring__seg { fill: none; stroke-width: 5; stroke-linecap: butt; }
.ring__seg--ok   { stroke: var(--ok); }
.ring__seg--warn { stroke: var(--sev-warning); }
.ring__seg--bad  { stroke: var(--sev-error); }
.ring__seg--idle { stroke: var(--ink-faint); }
.ring__label {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--mono); font-size: calc(var(--ring-size, 3.5rem) * .24); font-weight: 600; letter-spacing: -.02em;
}

/* A small dial: the server page's gauge at list size. */
.dial { display: grid; justify-items: center; gap: 0; width: 3.4rem; flex: none; }
.dial svg { width: 100%; height: auto; display: block; }
.dial__track { stroke: var(--bg); stroke-width: 11; }
.dial__fill { stroke: var(--ok); stroke-width: 11; }
.dial--warn .dial__fill { stroke: var(--sev-warning); }
.dial--bad .dial__fill { stroke: var(--sev-error); }
.dial__value { fill: var(--ink); font-family: var(--mono); font-size: 25px; font-weight: 600; }
.dial__label { font-size: var(--t-xs); color: var(--ink-faint); margin-top: -.55rem; }

/* A strip of runs or hours. */
.strip { display: inline-flex; gap: 2px; align-items: center; vertical-align: middle; }
.strip__cell { display: block; width: .5rem; height: .9rem; border-radius: 2px; background: var(--ok); }
.strip__cell--warn { background: var(--sev-warning); }
.strip__cell--bad  { background: var(--sev-error); }
.strip__cell--idle { background: var(--ink-faint); }
.strip__cell--none { background: transparent; border: 1px solid var(--line-bright); }
.strip--hours .strip__cell { width: .36rem; height: .8rem; }

/* One segment per member, so the red one is findable without a number. */
.segbar { display: flex; gap: 2px; flex: 1; min-width: 0; height: .45rem; }
.segbar__seg { display: block; flex: 1; border-radius: 2px; background: var(--ok); min-width: 3px; }
.segbar__seg--warn   { background: var(--sev-warning); }
.segbar__seg--bad    { background: var(--sev-error); }
.segbar__seg--silent { background: var(--sev-error); }
.segbar__seg--idle   { background: var(--ink-faint); }

/* Group cards on the dashboard. */
.gcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr)); gap: .75rem; }
.gcard {
    --tone: var(--ok);
    display: flex;
    align-items: center;
    gap: .9rem;
    padding: .8rem .9rem;
    min-width: 0;
    border-radius: var(--r-lg);
    border: 1px solid color-mix(in oklab, var(--tone) 28%, var(--line));
    background: color-mix(in oklab, var(--tone) 5%, var(--surface));
    color: inherit;
    text-decoration: none;
    transition: border-color 160ms var(--ease), transform 160ms var(--ease), box-shadow 160ms var(--ease);
}
.gcard:hover { border-color: color-mix(in oklab, var(--tone) 55%, var(--line)); transform: translateY(-1px); box-shadow: var(--lift); }
.gcard--warn  { --tone: var(--sev-warning); }
.gcard--bad   { --tone: var(--sev-error); }
.gcard--empty { --tone: var(--ink-faint); }
.gcard__body { display: grid; gap: .15rem; flex: 1; min-width: 0; }
.gcard__name { font-weight: 600; font-size: var(--t-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gcard__headline { font-size: var(--t-sm); color: var(--tone); font-weight: 550; }
.gcard__rows { display: grid; gap: .25rem; margin-top: .3rem; }
.gcard__row { display: flex; align-items: center; gap: .5rem; }
.gcard__kind { width: 3.6rem; font-size: var(--t-xs); color: var(--ink-faint); flex: none; }
.gcard__count { font-size: var(--t-xs); color: var(--ink-dim); width: 2.4rem; text-align: right; flex: none; }
.gcard__side { display: grid; gap: .3rem; justify-items: end; flex: none; color: var(--tone); }
.gcard__trend { color: var(--ink-faint); }

/* --- Groups page: one card per client, every member read ------------------ */

.group {
    --tone: var(--ok);
    margin-bottom: 1.25rem;
    border-radius: var(--r-lg);
    border: 1px solid color-mix(in oklab, var(--tone) 26%, var(--line));
    background: var(--surface);
    overflow: clip;
}
.group--warn  { --tone: var(--sev-warning); }
.group--bad   { --tone: var(--sev-error); }
.group--empty { --tone: var(--ink-faint); }
.group__head {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem;
    background: color-mix(in oklab, var(--tone) 6%, var(--surface));
    border-bottom: 1px solid var(--line);
    flex-wrap: wrap;
}
.group__title { display: grid; gap: .1rem; flex: 1; min-width: 12rem; }
.group__title h2 { font-size: var(--t-lg); font-weight: 650; letter-spacing: -.01em; }
.group__verdict { font-size: var(--t-sm); color: var(--tone); font-weight: 600; }
.group__meta { font-size: var(--t-sm); color: var(--ink-faint); display: flex; gap: .6rem; flex-wrap: wrap; }
.group__acts { display: flex; gap: .3rem; }
.group__body { display: grid; grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr)); gap: 0; }
.group__col { min-width: 0; border-right: 1px solid var(--line); }
.group__col:last-child { border-right: 0; }
.group__col h3 {
    display: flex; align-items: baseline; gap: .5rem;
    padding: .55rem 1rem;
    font-size: var(--t-xs); font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
    color: var(--ink-faint);
    border-bottom: 1px solid var(--line);
}
.group__col h3 span { margin-left: auto; font-weight: 500; letter-spacing: 0; text-transform: none; }
.group__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: .35rem .9rem;
    padding: .6rem 1rem;
    border-bottom: 1px solid var(--line);
}
.group__row:last-child { border-bottom: 0; }
.group__who { display: flex; align-items: center; gap: .45rem; min-width: 0; }
.group__who a { color: var(--ink); text-decoration: none; font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.group__who a:hover { color: var(--accent); }
.group__sub { grid-column: 1 / -1; font-size: var(--t-xs); color: var(--ink-faint); display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.group__dials { display: flex; gap: .35rem; }
.group__unwatched { color: var(--sev-warning); font-weight: 600; }
.group__foot {
    display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
    padding: .7rem 1.25rem;
    border-top: 1px solid var(--line);
    font-size: var(--t-sm); color: var(--ink-dim);
}
.group__foot .trend { color: var(--sev-error); }
.group__empty { padding: 1.5rem 1.25rem; color: var(--ink-dim); font-size: var(--t-sm); }
.group__incident {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    padding: .55rem 1.25rem;
    background: color-mix(in oklab, var(--sev-error) 8%, var(--surface));
    border-bottom: 1px solid color-mix(in oklab, var(--sev-error) 30%, var(--line));
    font-size: var(--t-sm);
}
.group__incident a { color: var(--ink); text-decoration: none; font-weight: 550; }

@media (max-width: 900px) {
    .group__col { border-right: 0; border-bottom: 1px solid var(--line); }
}

.dot--warn { background: var(--sev-warning); }

/* --- The map, editable: drop targets, ghosts, inline rename --------------- */
/* A constellation label is now something to drop a card on and to click for a
   rename, so it takes pointer events and shows its edge while a drag is on. */
.const-label {
    pointer-events: auto;
    padding: .3rem .7rem;
    border-radius: 999px;
    border: 1px dashed transparent;
    transition: border-color 150ms var(--ease), background 150ms var(--ease), box-shadow 150ms var(--ease);
    display: flex;
    align-items: center;
    gap: .3rem;
}
.const-label__edit {
    background: none;
    border: 0;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: .95em;
    padding: 0 .1rem;
    opacity: 0;
    transition: opacity 150ms var(--ease), color 150ms var(--ease);
}
.const-label:hover .const-label__edit, .const-label__edit:focus-visible { opacity: 1; }
.const-label__edit:hover { color: var(--ink); }
.const-label.is-editing { text-transform: none; letter-spacing: 0; text-shadow: none; z-index: 8; border-color: var(--accent-line); background: rgba(28,24,38,.96); padding: .4rem .5rem; }
.const-form { display: flex; align-items: center; gap: .3rem; }
.const-form input { width: 12rem; }

.space.is-dragging .const-label { border-color: var(--accent-line); background: rgba(28,24,38,.85); backdrop-filter: blur(4px); }
.space.is-dragging .node:not(.is-lifting):not(.node--ghost):not(.node--empty) { pointer-events: none; }
.is-over { border-color: var(--accent) !important; box-shadow: 0 0 0 4px var(--accent-wash), 0 0 24px var(--accent-line); color: #fff; }

/* Where a server can be dropped in a constellation that has none. */
.node--empty {
    width: 236px;
    height: 84px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed var(--accent-line);
    border-radius: var(--r-lg);
    background: rgba(20,17,28,.6);
    color: var(--ink-faint);
    font-size: var(--t-sm);
    cursor: default;
    animation: none;
}

.node.is-lifting { opacity: .3; animation: none; }
.node--ghost {
    pointer-events: none;
    z-index: 30;
    animation: none;
    transform: translate(-50%, -50%) rotate(-2deg) scale(1.03);
    filter: drop-shadow(0 24px 30px rgba(0,0,0,.65));
}

/* The two places that are not on the sky, shown only while dragging. */
.space__dropbar {
    position: fixed;
    left: 50%;
    bottom: 1.25rem;
    transform: translateX(-50%);
    display: flex;
    gap: .5rem;
    z-index: 35;
    opacity: 0;
    pointer-events: none;
    transition: opacity 150ms var(--ease);
}
.space.is-dragging .space__dropbar { opacity: 1; pointer-events: auto; }
.dropzone {
    padding: .65rem 1.1rem;
    border-radius: 999px;
    border: 1px dashed var(--accent-line);
    background: rgba(28,24,38,.94);
    color: var(--ink-dim);
    font-size: var(--t-sm);
    backdrop-filter: blur(6px);
    box-shadow: var(--pop);
}

.space__toast {
    position: fixed;
    left: 50%;
    bottom: 1.25rem;
    transform: translateX(-50%);
    z-index: 36;
    padding: .55rem 1rem;
    border-radius: 999px;
    border: 1px solid color-mix(in oklab, var(--ok) 40%, var(--line));
    background: rgba(28,24,38,.96);
    color: var(--ink);
    font-size: var(--t-sm);
    box-shadow: var(--pop);
    animation: pop .18s ease-out;
}
.space__toast[data-tone="bad"] { border-color: color-mix(in oklab, var(--sev-error) 50%, var(--line)); color: var(--sev-error); }
.space__toast[hidden] { display: none; }

.space-panel__move { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; margin-top: .3rem; }
.space-panel__move select { min-width: 12rem; flex: 1; }
.space-panel__move input { flex: 1 1 100%; }
.space-panel__move input[hidden] { display: none; }

/* --- The map, compact: smaller cards, labels on a pill at the hub ----------
   Everything a size down, as the sky reads better zoomed out; the label sits
   at the centre of its constellation on a solid pill above the ties, so it
   is always readable and never under a card. */
.vcard { width: 200px; padding: .5rem .6rem .45rem; border-top-width: 2px; }
.vhead { gap: .35rem; margin-bottom: .3rem; }
.vname { font-size: 11px; }
.vaddr { font-size: 10px; }
.vbadge { font-size: 10px; padding: 0 .4rem; }
.vspecs { gap: .2rem; margin-bottom: .35rem; }
.vchip { font-size: 10px; padding: 0 .3rem; }
.vcnt, .vmore { font-size: 10px; }
.node--empty { width: 200px; height: 72px; font-size: var(--t-xs); }

.const-label {
    z-index: 4;
    font-size: var(--t-xs);
    letter-spacing: .12em;
    background: rgba(11, 9, 16, .9);
    border: 1px solid var(--line);
    box-shadow: 0 0 0 5px rgba(11, 9, 16, .55), 0 0 22px var(--accent-wash);
    text-shadow: none;
    opacity: 1;
}
.space__links line { stroke-width: 1; stroke-dasharray: 2 5; }
