/* =============================================================================
   Self Supply · Price Sensor — design system
   "Petrol & Amber": teal-petrol primary, amber accent. Light + dark via
   CSS custom properties. Self-contained, no external dependencies.
   ========================================================================== */

:root {
    /* brand */
    --petrol: #0d9488;
    --petrol-600: #0f766e;
    --petrol-700: #115e59;
    --amber: #f59e0b;
    --rose: #e11d48;
    --emerald: #059669;

    /* light theme surfaces + text */
    --bg: #f6f7f5;
    --bg-tint: #eef1ee;
    --surface: #ffffff;
    --surface-2: #f9fafb;
    --border: #e2e5e2;
    --border-strong: #cbd0cb;
    --text: #1a201e;
    --text-muted: #647067;
    --text-faint: #94a09a;

    --primary: var(--petrol);
    --primary-hover: var(--petrol-600);
    --on-primary: #ffffff;

    --shadow-sm: 0 1px 2px rgba(16, 24, 20, .06), 0 1px 3px rgba(16, 24, 20, .08);
    --shadow-md: 0 4px 12px rgba(16, 24, 20, .08), 0 2px 4px rgba(16, 24, 20, .06);
    --shadow-lg: 0 12px 32px rgba(16, 24, 20, .14);

    --radius: 12px;
    --radius-sm: 8px;
    --radius-lg: 18px;

    --ring: 0 0 0 3px rgba(13, 148, 136, .28);

    /* Content width. The stations table carries one column per VID plus four station columns and
       three action columns, so a narrow reading measure only turns the page into a ribbon with the
       table scrolling sideways inside it. Wide enough for the whole grid on a normal laptop; the
       min() keeps the side gutters on anything narrower. */
    --content-max: 1760px;

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --mono: "SFMono-Regular", ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
}

/* dark theme: system default, overridable by [data-theme] toggle */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #0b1120;
        --bg-tint: #0f172a;
        --surface: #1e293b;
        --surface-2: #223049;
        --border: #334155;
        --border-strong: #475569;
        --text: #e8edf4;
        --text-muted: #9aa8bd;
        --text-faint: #64748b;
        --primary: #2dd4bf;
        --primary-hover: #5eead4;
        --on-primary: #06251f;
        --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
        --shadow-md: 0 4px 12px rgba(0, 0, 0, .45);
        --shadow-lg: 0 14px 36px rgba(0, 0, 0, .55);
        --ring: 0 0 0 3px rgba(45, 212, 191, .35);
    }
}

:root[data-theme="dark"] {
    --bg: #0b1120;
    --bg-tint: #0f172a;
    --surface: #1e293b;
    --surface-2: #223049;
    --border: #334155;
    --border-strong: #475569;
    --text: #e8edf4;
    --text-muted: #9aa8bd;
    --text-faint: #64748b;
    --primary: #2dd4bf;
    --primary-hover: #5eead4;
    --on-primary: #06251f;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-md: 0 4px 12px rgba(0, 0, 0, .45);
    --shadow-lg: 0 14px 36px rgba(0, 0, 0, .55);
    --ring: 0 0 0 3px rgba(45, 212, 191, .35);
}

/* ----- reset ----- */
*, *::before, *::after { box-sizing: border-box; }
/* The user-agent stylesheet's [hidden] { display: none } only wins on specificity when nothing
   else sets `display` on the same element. Every author-origin `display` declaration here
   (.alert's display: flex is the case that bit us) has equal specificity and later source order,
   so it silently defeats `hidden` and the element stays on screen. Restated with !important so
   `hidden` is unconditionally authoritative regardless of what else targets the element. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }
body {
    margin: 0;
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.55;
    color: var(--text);
    background: var(--bg);
    background-image:
        radial-gradient(1200px 600px at 100% -10%, rgba(13, 148, 136, .06), transparent 60%),
        radial-gradient(900px 500px at -10% 0%, rgba(245, 158, 11, .05), transparent 55%);
    background-attachment: fixed;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}
h1, h2, h3 { line-height: 1.2; margin: 0 0 .35em; letter-spacing: -.01em; }
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ----- app shell ----- */
.app-bar {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 24px;
    background: color-mix(in srgb, var(--surface) 86%, transparent);
    backdrop-filter: saturate(160%) blur(10px);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
}
.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 700;
    font-size: 1.02rem;
    letter-spacing: -.02em;
    color: var(--text);
}
.brand .logo {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    border-radius: 10px;
    background: linear-gradient(135deg, var(--petrol), var(--petrol-700));
    color: #fff;
    font-size: 1.1rem;
    box-shadow: var(--shadow-sm);
}
.brand .sep { color: var(--text-faint); font-weight: 400; }
.app-bar nav { display: flex; gap: 4px; margin-left: 8px; }
.app-bar nav a {
    padding: 7px 12px;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-weight: 600;
    font-size: .92rem;
}
.app-bar nav a:hover { background: var(--bg-tint); color: var(--text); text-decoration: none; }
.app-bar nav a.active { background: color-mix(in srgb, var(--primary) 14%, transparent); color: var(--primary); }
.app-bar .spacer { flex: 1; }
.app-bar .tools { display: flex; align-items: center; gap: 8px; }

.env-badge {
    padding: 3px 10px;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    background: color-mix(in srgb, var(--amber) 18%, transparent);
    color: var(--amber);
    border: 1px solid color-mix(in srgb, var(--amber) 40%, transparent);
}

.theme-toggle {
    display: grid; place-items: center;
    width: 38px; height: 38px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text);
    cursor: pointer;
    font-size: 1.05rem;
    transition: background .15s, transform .15s;
}
.theme-toggle:hover { background: var(--bg-tint); transform: translateY(-1px); }
.theme-toggle .icon-dark { display: none; }
.theme-toggle .icon-light { display: inline; }
:root[data-theme="dark"] .theme-toggle .icon-dark,
:root:not([data-theme="light"]) .theme-toggle .icon-dark { display: none; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .theme-toggle .icon-light { display: none; }
    :root:not([data-theme="light"]) .theme-toggle .icon-dark { display: inline; }
}
:root[data-theme="dark"] .theme-toggle .icon-light { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-dark { display: inline; }
:root[data-theme="light"] .theme-toggle .icon-light { display: inline; }
:root[data-theme="light"] .theme-toggle .icon-dark { display: none; }

.container { max-width: var(--content-max); margin: 0 auto; padding: 28px 32px 64px; }

.page-head { margin-bottom: 28px; }
.page-head h1 { font-size: 1.7rem; }
.page-head .subtitle { color: var(--text-muted); margin: 0; }
.count-pill {
    display: inline-block;
    margin-left: 10px;
    padding: 2px 11px;
    border-radius: 999px;
    font-size: .8rem;
    font-weight: 700;
    vertical-align: middle;
    background: color-mix(in srgb, var(--primary) 14%, transparent);
    color: var(--primary);
}

.app-footer {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 18px 32px 40px;
    color: var(--text-faint);
    font-size: .82rem;
    display: flex; gap: 10px; align-items: center;
}

/* ----- card ----- */
.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
/* Bottom margin, not the old `.card + .card { margin-top }`: the block after the last card is the
   stations panel, whose first row is the edit/search toolbar. A sibling-only rule left that toolbar
   sitting flush against the "Verify all" card, and the filter inputs read as part of it. */
.card { margin-bottom: 28px; }
.card + .card { margin-top: 28px; }
.card-head { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
.card-head h2 { font-size: 1.08rem; margin: 0; }
.card-head .hint { color: var(--text-muted); font-size: .86rem; font-weight: 400; }
.card-body { padding: 20px; }
/* The one-line count a card leads with ("create 2 · skip 2 · reject 1"). Used by the import wizard,
   where it is the answer to the question the screen exists to ask, and it read as ordinary body text
   indistinguishable from the hints under it. */
.summary { font-size: 1rem; font-weight: 600; margin: 0 0 12px; }

/* ----- buttons ----- */
.btn {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 9px 16px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-family: inherit;
    font-size: .92rem;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s, box-shadow .15s, transform .1s, border-color .15s;
    white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn-primary { background: var(--primary); color: var(--on-primary); box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-lg { padding: 12px 22px; font-size: 1rem; }
.btn-ghost { background: transparent; color: var(--text-muted); border-color: var(--border); }
.btn-ghost:hover { background: var(--bg-tint); color: var(--text); }
.btn-danger { background: transparent; color: var(--rose); border-color: color-mix(in srgb, var(--rose) 40%, transparent); }
.btn-danger:hover { background: color-mix(in srgb, var(--rose) 12%, transparent); }
.btn-sm { padding: 6px 12px; font-size: .84rem; }

/* A disabled button rendered exactly like an enabled one until this rule existed: the markup carried
   `disabled` (the import button spends most of its life that way), the browser refused the click, and
   nothing on screen said why. Written against both :disabled and [disabled] so it also covers an
   <a> or <span> that carries the attribute without being a form control. */
.btn:disabled,
.btn[disabled] {
    cursor: not-allowed;
    opacity: .5;
    box-shadow: none;
}
/* Each variant that defines a :hover has to say so again here, more specifically: a plain
   `.btn:disabled:hover` loses to `.btn-primary:hover` on specificity and the button still lights up
   under the cursor. */
.btn-primary:disabled:hover, .btn-primary[disabled]:hover { background: var(--primary); }
.btn-ghost:disabled:hover, .btn-ghost[disabled]:hover { background: transparent; color: var(--text-muted); }
.btn-danger:disabled:hover, .btn-danger[disabled]:hover { background: transparent; }
.btn:disabled:active,
.btn[disabled]:active { transform: none; }

/* ----- alerts / toast ----- */
.alert {
    padding: 12px 16px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    display: flex; gap: 10px; align-items: flex-start;
    font-size: .92rem;
}
.alert code { background: var(--bg-tint); padding: 2px 6px; border-radius: 5px; font-family: var(--mono); font-size: .85em; }
.alert-success { background: color-mix(in srgb, var(--emerald) 12%, var(--surface)); border-color: color-mix(in srgb, var(--emerald) 40%, transparent); color: color-mix(in srgb, var(--emerald) 75%, var(--text)); }
.alert-error   { background: color-mix(in srgb, var(--rose) 12%, var(--surface)); border-color: color-mix(in srgb, var(--rose) 40%, transparent); color: color-mix(in srgb, var(--rose) 78%, var(--text)); }
.alert-info    { background: color-mix(in srgb, var(--primary) 10%, var(--surface)); border-color: color-mix(in srgb, var(--primary) 38%, transparent); }
.alert .alert-icon { font-size: 1.1rem; line-height: 1.3; }

.toast {
    position: fixed; top: 24px; left: 50%; transform: translateX(-50%); z-index: 100;
    max-width: min(720px, 92vw);
    padding: 18px 22px;
    border-radius: var(--radius);
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    display: flex; gap: 14px; align-items: flex-start;
    font-size: 1.02rem;
    animation: toast-in .3s ease;
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, -12px); } to { opacity: 1; transform: translate(-50%, 0); } }
.toast .toast-dot { width: 11px; height: 11px; margin-top: 6px; border-radius: 50%; background: var(--emerald); flex: none; }
.toast.toast-error { border-color: color-mix(in srgb, var(--rose) 45%, transparent); }
.toast.toast-error .toast-dot { background: var(--rose); }
.toast .toast-body { flex: 1; line-height: 1.4; }
.toast .toast-close {
    background: none; border: none; color: var(--text); cursor: pointer;
    font-size: 1.2rem; line-height: 1; padding: 0 2px; opacity: .6;
}
.toast .toast-close:hover { opacity: 1; }
.toast.toast-hide { animation: toast-out .25s ease forwards; }
@keyframes toast-out { from { opacity: 1; transform: translate(-50%, 0); } to { opacity: 0; transform: translate(-50%, -12px); } }

.validation-summary-errors { color: var(--rose); }
.validation-summary-errors ul { margin: 0; padding-left: 18px; }
.field-validation-error { color: var(--rose); font-size: .8rem; display: block; margin-top: 3px; }

/* ----- forms ----- */
.input, input[type="text"], input[type="password"], input[type="number"] {
    width: 100%;
    padding: 8px 11px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-family: inherit;
    font-size: .92rem;
    transition: border-color .15s, box-shadow .15s;
}
.input:focus, input[type="text"]:focus, input[type="password"]:focus, input[type="number"]:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--ring);
}
.field { margin-bottom: 14px; }
.field label { display: block; font-weight: 600; font-size: .85rem; margin-bottom: 5px; color: var(--text-muted); }

/* ----- table ----- */
.table-wrap {
    overflow: auto;
    max-height: 74vh;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}
table.grid { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .92rem; }
table.grid thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--surface-2);
    text-align: left;
    font-size: .74rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 11px 14px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
table.grid tbody td { padding: 8px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.grid tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--bg-tint) 45%, transparent); }
table.grid tbody tr:hover td { background: color-mix(in srgb, var(--primary) 7%, transparent); }
table.grid tbody tr:last-child td { border-bottom: none; }
table.grid td input { min-width: 90px; }
.row-actions { display: flex; gap: 6px; white-space: nowrap; }

/* new-station row: visually distinct, tinted + dashed top */
tr.add-row td { background: color-mix(in srgb, var(--amber) 8%, var(--surface)) !important; border-top: 2px dashed color-mix(in srgb, var(--amber) 45%, transparent); }
tr.add-row td input::placeholder { color: var(--text-faint); }

.empty-state { padding: 40px; text-align: center; color: var(--text-muted); }

/* ----- auth (login) ----- */
.auth-wrap {
    min-height: 100vh;
    display: grid; place-items: center;
    padding: 24px;
    background:
        radial-gradient(1000px 600px at 20% 10%, rgba(13, 148, 136, .16), transparent 55%),
        radial-gradient(900px 600px at 90% 90%, rgba(245, 158, 11, .12), transparent 55%),
        var(--bg);
}
.auth-card {
    width: 100%; max-width: 400px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 34px 30px;
}
.auth-card .brand { justify-content: center; margin-bottom: 6px; }
.auth-card .brand .logo { width: 42px; height: 42px; font-size: 1.35rem; }
.auth-title { text-align: center; font-size: 1.4rem; margin: 10px 0 2px; }
.auth-sub { text-align: center; color: var(--text-muted); margin: 0 0 24px; font-size: .9rem; }
.auth-card .btn { width: 100%; justify-content: center; margin-top: 6px; }

@media (max-width: 640px) {
    .app-bar { padding: 10px 14px; }
    .app-bar .brand .sep, .app-bar .brand .brand-sub { display: none; }
    .container { padding: 20px 14px 48px; }
    /* .table-filters' narrow-viewport override is NOT here: it has to come after the base rule in
       the stations block below, or source order decides it and the media query never applies. */
}

/* ----- stations edit mode ----- */
.stations[data-editing="false"] .cell-edit { display: none; }
.stations[data-editing="false"] .edit-only { display: none; }
.stations[data-editing="true"]  .cell-static { display: none; }
/* The mirror of .edit-only: shown while reading, gone while editing. Its only users are the
   change-history column (header, every row's cell and the add row's placeholder) and the "Edit
   mode" button. History is a read action, so it has no place among the edit controls — and the
   column has to disappear as a whole, header included, or every cell after it shifts one left. */
.stations[data-editing="true"] .view-only { display: none; }
.stations .cell-static { display: inline-block; min-height: 1.2em; }
/* Search-addressed VID cell (ADAC): no URL to edit, so it stays visible in both modes. */
.stations .cell-search-addressed { color: var(--text-muted); font-size: .86rem; font-style: italic; }
/* Ambiguous / not_found: a distinct, non-italic warning colour so it reads at a glance as "needs a
   human", not merely "resolved, quietly". Tooltip carries the "what to do about it" via title=. */
.stations .cell-adac-unresolved { color: var(--amber); font-style: normal; font-weight: 600; cursor: help; }
/* No mapping row at all yet — neither resolved nor a pending failure, just "not started". Dashed
   underline instead of the warning colour: this is not an error state, just an available action. */
.stations .cell-adac-unobserved { color: var(--text-faint); font-style: normal; text-decoration: underline dashed; cursor: help; }
/* Crawl failure, distinct from the amber verification badges above: a red dot never reads as the
   same signal as an amber triangle when both share a cell. */
.stations .cell-crawl-failing { display: inline-block; width: 12px; color: var(--rose); cursor: help; margin-right: 4px; }
/* The VID answers, our station just is not in the answer — amber and hollow against the crawl
   indicator's filled rose: same shape (this is about collecting prices), different severity. The
   amber ⚠ badges above name a resolution state a human must fix; this names a run of failed
   lookups the resolver will retry by itself. */
.stations .cell-vid-missing { display: inline-block; width: 12px; color: var(--amber); cursor: help; margin-right: 4px; }
/* Edit mode packs ~11 input columns in. They still fill their cell, but they no longer collapse to
   fit the viewport: min-width: 0 squeezed a station name down to "JET K" and a URL to "https://ww",
   which is unreadable in the one mode whose whole job is checking and correcting those values. The
   floor below lets the grid grow past the viewport and scroll horizontally inside .table-wrap
   (which is already overflow: auto) — the table moves, the page does not. */
.stations[data-editing="true"] table.grid td input { min-width: 150px; width: 100%; }
/* The URL cells carry the longest values by far and get a wider floor of their own. Enough to show
   the site plus the start of the path, which is what identifies the station at a glance; the rest
   is read by focusing the field. */
.stations[data-editing="true"] table.grid td.vid-cell input { min-width: 260px; }
/* Edit mode gives every cell input width:100%, which an inline dot cannot share a line box with —
   the indicator would push the input onto a second line in exactly the cells the feature
   highlights. The dot occupies a fixed 12px box plus its 4px margin plus the collapsed inter-element
   space, which is why the reserve below is 22px: reserving that width inside the input instead of
   turning the cell into a flex line, because a flex cell also pulls the ADAC cell's .row-actions up
   beside the badge instead of leaving it below, which is a layout change for every
   failing-and-resolved ADAC row. The reserve exists because the input claims 100% of the cell in
   edit mode. Scoped with :has so only a cell that actually carries an indicator is touched. It sets
   no min-width of its own: the floors above are deliberate now, and re-zeroing one here would
   collapse exactly the cells the indicator marks as needing a look. */
.stations[data-editing="true"] table.grid td:has(> .cell-crawl-failing, > .cell-vid-missing) input { width: calc(100% - 22px); }
/* Change-history panel: a small pill for a mapping's VID scope ("ADAC") and the "verification" tag,
   same shape for both so a row with one or two tags reads as a set, not two different UI ideas. */
.history-tag {
    display: inline-block;
    margin-right: 6px;
    padding: 1px 6px;
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--text-muted);
    font-size: .78rem;
}
/* Own top margin as well as the card's bottom one: this panel also follows the toast/validation
   blocks and the history partial, none of which are cards. */
.stations { margin-top: 28px; }
/* wrap, so the filters drop under the edit toggle instead of overlapping it on a narrow viewport */
.table-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-bottom: 16px; }
.table-actions { display: flex; flex-wrap: wrap; gap: 8px; }
/* Pushed to the toolbar's right edge, which is the table's right edge: .table-wrap is a block
   sibling of this toolbar and table.grid is width: 100% inside it, so the two edges coincide (a
   grid wider than the wrap scrolls *inside* it and does not move that edge). nowrap keeps search
   and brand on one line — they read as one filter control, not two stacked ones. */
.table-filters { display: flex; flex-wrap: nowrap; gap: 10px; margin-left: auto; }
/* Two 190px inputs plus their gap no longer fit on one line at this width, and the nowrap above
   would push the page into horizontal scroll rather than the toolbar into a second row.
   Its position is load-bearing: a media query adds no specificity, so this rule and the base rule
   are both (0,0,1,0) and source order alone decides. Written before .table-filters — up in the
   640px block with the other narrow-viewport overrides — it is simply dead. */
@media (max-width: 640px) {
    .table-filters { flex-wrap: wrap; }
}
.filter-input { width: auto; min-width: 190px; }
.sort-arrow { font-size: .72em; opacity: .5; margin-left: 4px; }
th[data-sort] { cursor: pointer; user-select: none; }
th[data-sort].sort-asc .sort-arrow::after { content: "▲"; opacity: 1; }
th[data-sort].sort-desc .sort-arrow::after { content: "▼"; opacity: 1; }
th[data-sort]:not(.sort-asc):not(.sort-desc) .sort-arrow::after { content: "↕"; }

/* ----- monitoring dashboard (SEL-37) ----- */
.monitoring-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.monitoring-updated { color: var(--text-muted); font-size: .86rem; margin-left: auto; }

/* Every pill pairs its dot with the state word, so meaning survives greyscale and colour blindness. */
.status-pill {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 4px 12px; border-radius: 999px;
    border: 1px solid var(--border); background: var(--surface-2);
    font-size: .86rem; font-weight: 600; color: var(--text);
}
.status-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--text-faint); flex: none; }

.status-pill[data-state="ok"] .status-dot,
.status-pill[data-overall-state="ok"] .status-dot { background: var(--emerald); }
.status-pill[data-state="degrading"] .status-dot,
.status-pill[data-overall-state="degrading"] .status-dot { background: var(--amber); }
.status-pill[data-state="outage"] .status-dot,
.status-pill[data-overall-state="outage"] .status-dot { background: var(--rose); }
.status-pill[data-state="unobserved"] .status-dot,
.status-pill[data-overall-state="unknown"] .status-dot { background: var(--text-faint); }

/* Cycle card states (SEL-37): overdue reuses the outage vocabulary — it is the same missed-cycle
   condition the watchdog alerts on. awaiting-first-cycle and scheduler-disabled are grey like
   unobserved: neither one is a fault. */
.status-pill[data-state="overdue"] .status-dot { background: var(--rose); }
.status-pill[data-state="awaiting-first-cycle"] .status-dot,
.status-pill[data-state="scheduler-disabled"] .status-dot { background: var(--text-faint); }
.status-pill[data-state="overdue"] {
    border-color: color-mix(in srgb, var(--rose) 40%, transparent);
    background: color-mix(in srgb, var(--rose) 10%, var(--surface));
}

.status-pill[data-state="outage"],
.status-pill[data-overall-state="outage"] {
    border-color: color-mix(in srgb, var(--rose) 40%, transparent);
    background: color-mix(in srgb, var(--rose) 10%, var(--surface));
}
.status-pill[data-state="degrading"],
.status-pill[data-overall-state="degrading"] {
    border-color: color-mix(in srgb, var(--amber) 45%, transparent);
    background: color-mix(in srgb, var(--amber) 12%, var(--surface));
}

/* Proxy pool card (SEL-10). 'disabled' is grey like 'unobserved' — a deliberate configuration, not
   a fault. 'degraded' and 'pool-unavailable' both fail OPEN (requests still leave over the host IP,
   prices still arrive) and share the amber vocabulary; only 'budget-exhausted' fails CLOSED and
   gets the red 'outage' treatment, matching CrawlHealthService's policy for what may turn the
   overall /api/health pill red.
   'awaiting-first-refresh' is grey too: the pool loads on the first proxied request, so an empty
   pool before then means "nothing has needed a proxy yet", not "the provider is down". */
.status-pill[data-state="disabled"] .status-dot,
.status-pill[data-state="awaiting-first-refresh"] .status-dot { background: var(--text-faint); }
.status-pill[data-state="degraded"] .status-dot,
.status-pill[data-state="pool-unavailable"] .status-dot { background: var(--amber); }
.status-pill[data-state="budget-exhausted"] .status-dot { background: var(--rose); }

.status-pill[data-state="degraded"],
.status-pill[data-state="pool-unavailable"] {
    border-color: color-mix(in srgb, var(--amber) 45%, transparent);
    background: color-mix(in srgb, var(--amber) 12%, var(--surface));
}
.status-pill[data-state="budget-exhausted"] {
    border-color: color-mix(in srgb, var(--rose) 40%, transparent);
    background: color-mix(in srgb, var(--rose) 10%, var(--surface));
}

.monitoring-kalibrate { margin: 0; color: var(--text); }
.monitoring-kalibrate-ts { margin: 6px 0 0; color: var(--text-muted); font-size: .86rem; }
.monitoring-cycle-ts { margin: 8px 0 0; color: var(--text-muted); font-size: .86rem; }
.monitoring-proxy-detail { margin: 6px 0 0; color: var(--text); font-size: .86rem; }
.monitoring-proxy-ts { margin: 6px 0 0; color: var(--text-muted); font-size: .86rem; }
