/* MetricHarbor operator UI.
 *
 * Plain CSS, no build step and no framework — the same choice the sibling projects made, and
 * it keeps the deploy a file copy. Dense by design: this is a tool for people who want twenty
 * agents on one screen, not six cards with generous padding.
 *
 * Shares its palette and its typefaces with the public page, so signing in is a change of
 * purpose rather than a change of product. It does NOT share a stylesheet with it: the public
 * page is a fixed marketing surface and this is a dense tool, and the two reuse class names
 * like .card and .btn for different things. One palette, two stylesheets.
 */

:root {
    /* Dark only, and no longer following the system preference.
     *
     * This used to offer a light theme and a dark one, on the reasoning that operators work
     * in dark rooms and the OS already knows which they want. The reasoning was sound and the
     * result was still wrong: the public page is dark, so anybody on a light desktop met one
     * product before signing in and a different-looking one after. Coherence across that seam
     * is worth more than matching a preference the rest of the product cannot honour.
     *
     * color-scheme tells the browser to draw ITS widgets dark too — the select arrow, the
     * number spinner, scrollbars, the date picker. */
    color-scheme: dark;

    --bg: #09151e;
    --surface: #10212b;
    /* A step above --surface, for a card that sits on top of a panel rather than on the page.
     * .pin-card asked for this and it was never defined, so pinned targets on the overview
     * had no background at all — var() with no fallback drops the declaration. */
    --panel: #142833;
    --border: #25414d;

    --text: #f3f7f6;
    --muted: #99aeb7;

    /* Mint is both "go" and "healthy" here, as it is on the public page: the primary action
     * and a target that is fine are the same colour because they carry the same news. */
    --accent: #84e8bf;
    /* What is legible ON --accent. White over mint is 1.6:1 and was unreadable. */
    --accent-ink: #09251d;

    --ok: #84e8bf;
    --warn: #f9a86c;
    --crit: #ff817a;

    --radius: 7px;

    --display: 'Space Grotesk', system-ui, -apple-system, "Segoe UI", sans-serif;
    --body: 'DM Sans', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font: 1rem/1.55 var(--body);
}

/* A visible focus ring on everything reachable by keyboard, not just the form controls that
 * had one. Mint on navy is 12.5:1, so it reads as an outline rather than a hint. */
a:focus-visible, button:focus-visible, .btn:focus-visible, summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius);
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

code, .mono, pre { font-family: var(--mono); font-size: .875rem; }

/* ---------------------------------------------------------------- chrome */

.topbar {
    display: flex;
    align-items: center;
    gap: 24px;
    padding: 0 20px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    height: 56px;
}

/* The wordmark, matching the public page: the mark, then Metric in ink and Harbor in mint.
 *
 * The name is one element on purpose. The brand is a flex container, so a bare "Metric"
 * followed by <em>Harbor</em> would be two flex items and the gap meant for the mark would
 * land between the two halves of the word. */
.topbar .brand { display: inline-flex; align-items: center; gap: 9px; }
.brand .mark { width: 26px; height: 26px; color: var(--accent); flex-shrink: 0; }
.brand .name { font: 600 1.125rem var(--display); letter-spacing: -0.045em; white-space: nowrap; }
.brand em { font-style: normal; color: var(--accent); }
.topbar nav { display: flex; gap: 4px; margin-right: auto; }

.topbar nav a {
    padding: 6px 10px;
    border-radius: var(--radius);
    color: var(--muted);
    font-weight: 500;
}

.topbar nav a:hover { background: var(--bg); text-decoration: none; }
.topbar nav a.active { color: var(--text); background: var(--bg); }

.topbar .org,
.topbar .docs { color: var(--muted); font-size: .875rem; }

main { max-width: 1200px; margin: 0 auto; padding: 24px 20px 64px; }

h1 { font-family: var(--display); font-size: 1.5rem; font-weight: 600; margin: 0 0 4px; letter-spacing: -0.03em; }
h2 { font-family: var(--display); font-size: 1.0625rem; font-weight: 600; margin: 28px 0 10px; letter-spacing: -0.02em; }
.subtitle { color: var(--muted); font-size: .9375rem; margin: 0 0 20px; }

/* ---------------------------------------------------------------- cards */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
}

.card .label { color: var(--muted); font: 500 .8125rem var(--mono); text-transform: uppercase; letter-spacing: 0.04em; }
.card .value { font-family: var(--display); font-size: 1.75rem; font-weight: 600; line-height: 1.2; margin-top: 4px; letter-spacing: -0.04em; }
.card.alarm { border-color: var(--crit); }
.card.alarm .value { color: var(--crit); }

.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}

.panel > header {
    padding: 10px 16px;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: .9375rem;
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ---------------------------------------------------------------- tables */

table { width: 100%; border-collapse: collapse; }

th {
    text-align: left;
    padding: 8px 16px;
    font-size: .8125rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    white-space: nowrap;
}

td { padding: 10px 16px; border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--bg); }

.empty { padding: 28px 16px; color: var(--muted); text-align: center; font-size: .9375rem; }

/* ---------------------------------------------------------------- status */

.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
.dot.ok { background: var(--ok); }
.dot.warn { background: var(--warn); }
.dot.crit { background: var(--crit); }
.dot.idle { background: var(--muted); }

.tag {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: .8125rem;
    font-weight: 500;
    border: 1px solid var(--border);
    color: var(--muted);
    white-space: nowrap;
}

.tag.ok { color: var(--ok); border-color: var(--ok); }
.tag.warn { color: var(--warn); border-color: var(--warn); }
.tag.crit { color: var(--crit); border-color: var(--crit); }

.muted { color: var(--muted); }
.nowrap { white-space: nowrap; }
.right { text-align: right; }

/* ---------------------------------------------------------------- forms */

label { display: block; font-size: .875rem; font-weight: 500; margin-bottom: 4px; }
.field { margin-bottom: 14px; }
.field .hint { color: var(--muted); font-size: .8125rem; margin-top: 4px; }

/* Styled by EXCLUSION, not by listing the types.
 *
 * This used to enumerate text, email, password and number. The schema macro picks the input
 * type from the field's declared type, so a config field of type url produced <input type=url>
 * — unmatched, and therefore a white box on a dark page. A textarea was never matched at all.
 *
 * An allow-list in the stylesheet against a list generated in a template drifts apart every
 * time a field type is added, and the symptom is cosmetic enough to ship. Defaulting to styled
 * and opting out the few controls that genuinely look different cannot drift. */
input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=submit]):not([type=button]):not([type=file]),
textarea,
select {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    font: inherit;
}

textarea { min-height: 84px; resize: vertical; }

input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

.checkbox { display: flex; align-items: flex-start; gap: 8px; }
.checkbox input { margin-top: 3px; }
.checkbox label { margin: 0; font-weight: 400; }

button, .btn {
    display: inline-block;
    padding: 8px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-weight: 500;
    cursor: pointer;
}

button:hover, .btn:hover { border-color: var(--muted); text-decoration: none; }
button.primary, .btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
button.primary:hover, .btn.primary:hover { background: #a3f2d0; border-color: #a3f2d0; }
button.danger { color: var(--crit); border-color: var(--crit); }
button.small { padding: 4px 10px; font-size: .875rem; }

.actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar input[type=text] { width: 240px; }
.toolbar select { width: auto; }

/* ---------------------------------------------------------------- messages */

.flash { padding: 10px 14px; border-radius: var(--radius); margin-bottom: 14px; border: 1px solid; font-size: .9375rem; }
.flash.success { color: var(--ok); border-color: var(--ok); }
.flash.error { color: var(--crit); border-color: var(--crit); }

.notice {
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 20px;
}

.notice.alarm { border-left-color: var(--crit); }

/* The block itself stays normally selectable. It used to be user-select:all, which turns a
 * single click into a select-everything and makes dragging out a portion impossible — the
 * copy button is the better answer to the same problem. */
pre.copy {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
    overflow-x: auto;
    margin: 0;
    user-select: text;
}

.copy-wrap { position: relative; margin: 10px 0 0; }

.copy-button {
    position: absolute;
    top: 6px;
    right: 6px;
    padding: 3px 10px;
    font-size: .8125rem;
    line-height: 1.6;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--muted);
    cursor: pointer;
    font-family: var(--mono);
    /* Visible always rather than on hover: a control you have to discover by waving the
     * mouse at it is one most people never find, and this one matters on exactly the pages
     * where somebody is copying a value they will never see again. */
    opacity: 0.85;
}

.copy-button:hover { opacity: 1; color: var(--text); border-color: var(--muted); }
.copy-button.copied { color: var(--ok); border-color: var(--ok); }

/* Room for the button so it never lands on top of the text. */
.copy-wrap pre.copy { padding-right: 76px; }

/* ---------------------------------------------------------------- auth */

.auth { max-width: 360px; margin: 10vh auto; padding: 0 20px; }
.auth .brand { display: inline-flex; align-items: center; gap: 9px; margin-bottom: 4px; }
.auth .brand .mark { width: 30px; height: 30px; }
.auth .brand .name { font-size: 1.375rem; }
.auth .panel { padding: 22px; }
.auth button { width: 100%; }

.dl { display: grid; grid-template-columns: max-content 1fr; gap: 8px 20px; padding: 16px; font-size: .9375rem; }
.dl dt { color: var(--muted); }
.dl dd { margin: 0; }

@media (max-width: 700px) {
    .topbar { gap: 12px; overflow-x: auto; }
    .topbar .org { display: none; }
    main { padding: 16px; }
    .dl { grid-template-columns: 1fr; gap: 2px 0; }
    .dl dd { margin-bottom: 10px; }
}

/* ---------------------------------------------------------------- charts */

.chart { width: 100%; height: auto; display: block; }
.chart .grid { stroke: var(--border); stroke-width: 1; }
.chart .axis { fill: var(--muted); font-size: 12px; font-family: var(--mono); }
.chart .line { fill: none; stroke-width: 1.6; vector-effect: non-scaling-stroke; }

.swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-right: 6px;
    vertical-align: baseline;
}

/* ---------------------------------------------------------------- dashboard
 *
 * Stat tiles and meters, not dial gauges. A gauge spends a whole tile saying one number at
 * one instant; a meter plus a sparkline uses the same space to say where the number is AND
 * whether it is climbing, which is the question somebody opening a server page actually has.
 *
 * Severity never rides on colour alone anywhere here: every meter prints its percentage, and
 * a non-ok meter names its state in the title attribute.
 */

.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin: 18px 0 4px;
}

.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px 8px;
}

.stat-label {
    color: var(--muted);
    font: 500 .8125rem var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.stat-value {
    font-family: var(--display);
    font-size: 1.75rem;
    font-weight: 600;
    letter-spacing: -0.04em;
    line-height: 1.2;
    margin-top: 2px;
    /* Proportional figures: tabular-nums gives every digit a zero's width, which makes a
     * value like 121 look gappy at display sizes. Tabular belongs in columns, not here. */
}

/* The sparkline is read as a gesture beside the number, not measured — so it is quiet,
 * and the only emphasis is the dot marking where the series ends. */
.spark { width: 100%; height: 28px; display: block; margin-top: 6px; overflow: visible; }
.spark path { fill: none; stroke-width: 1.5; vector-effect: non-scaling-stroke; opacity: 0.75; }
.spark circle { opacity: 0.95; }
.stat .spark { color: var(--accent); }

/* ---------------------------------------------------------------- meters */

.meter-row { padding: 11px 16px; border-bottom: 1px solid var(--border); }
.meter-row:last-child { border-bottom: 0; }

.meter-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 6px;
    font-size: .9375rem;
}

.meter-name { font-family: var(--mono); font-size: .8125rem; }
.meter-value { white-space: nowrap; font-weight: 600; }
.meter-value.warning { color: var(--warn); }
.meter-value.critical { color: var(--crit); }
.meter-detail { font-weight: 400; font-size: .8125rem; margin-left: 6px; }

/* The unfilled track is a lighter step of the same surface rather than a contrasting
 * colour, so the fill's state reads across the whole bar. */
.meter-track {
    height: 7px;
    border-radius: 4px;
    background: var(--bg);
    border: 1px solid var(--border);
    overflow: hidden;
}

.meter-fill {
    height: 100%;
    border-radius: 3px;
    background: var(--accent);
    min-width: 2px;
}

.meter-fill.warning { background: var(--warn); }
.meter-fill.critical { background: var(--crit); }

/* The current value on a firing alert. Coloured to match its severity, and always printed
 * as a number — the colour is the echo, the digits are the message. */
.alert-now-warn { color: var(--warn); }
.alert-now-crit { color: var(--crit); }

/* Pinned targets on the overview.
   One box per target the operator chose, sized so a dozen fit a screen without becoming the
   dense table this replaces. auto-fill with a minimum keeps it readable on a laptop and uses
   a wall display properly, with no breakpoints to maintain. */
.pin-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.pin-card { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; }
/* The left edge carries the state, so a card in trouble is findable before reading a number.
   Never colour alone: the meter's own value is printed and coloured too. */
.pin-card.warning { border-left: 3px solid var(--warn); }
.pin-card.critical { border-left: 3px solid var(--crit); }
.pin-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.pin-head a { font-weight: 600; }
.pin-head .muted { font-size: .8125rem; }
.pin-tiles { display: flex; gap: 16px; margin-bottom: 10px; flex-wrap: wrap; }
.pin-tile .label { font: 500 .75rem var(--mono); text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.pin-tile .value { font-family: var(--display); font-size: 1.3125rem; font-weight: 600; letter-spacing: -0.03em; }
.pin-meter { margin-top: 8px; }
.pin-meter-head { display: flex; justify-content: space-between; font-size: .8125rem; margin-bottom: 3px; }
.pin-meter-head .warning { color: var(--warn); }
.pin-meter-head .critical { color: var(--crit); }

/* The phone app, at the foot of the overview. The QR code sits on its own light tile because
   not every phone camera reads a code drawn light-on-dark. */
.app-card {
    display: flex; gap: 22px; align-items: flex-start;
    margin-top: 32px; padding: 18px 20px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
}
.app-qr { flex-shrink: 0; width: 128px; height: 128px; border-radius: 6px; }
.app-card-body h2:not([class]) { margin-top: 0; }
.app-card-body p:not([class]) { margin: 0 0 10px; }
.app-scan { margin: 0 0 10px; }
.app-note { font-size: .8125rem; margin: 12px 0 0; }
/* On a phone there is nothing to scan the code with: the button is the way in. */
@media (max-width: 700px) {
    .app-qr, .app-scan { display: none; }
}

/* The good empty state. Green carries it, and the words say it too — a colour alone is not a
   message, and this is the one state somebody reads from across a room. */
.empty-ok { color: var(--ok); font-weight: 600; }
/* Not-all-clear: no alerts, but an agent is down, so the quiet is not evidence of health. */
.empty-warn { color: var(--warn); font-weight: 600; }

/* Things reporting as down, on a pinned card. Listed before any meter, because a thing that is
   not there outranks a thing that is merely busy. */
.pin-problems { list-style: none; margin: 0 0 10px; padding: 0; font-size: .875rem; }
.pin-problems li { margin: 3px 0; }

/* A target that has stopped collecting. The readings stay on screen because "it was 9 a
   moment ago" is useful context, but dimmed and preceded by the banner, so no glance mistakes
   them for current. Never dimming alone: the banner says it in words. */
.pin-stale { margin: 0 0 10px; font-size: .875rem; }
.pin-tile.is-stale, .pin-meter.is-stale { opacity: .45; }

/* A matrix of readings — rows are one machine, columns are one metric. The bar lives under
   the number inside its cell, narrow enough that a row still reads as a row. */
.matrix td { vertical-align: top; }
.matrix .cell-bar { height: 3px; margin-top: 4px; min-width: 70px; }
.matrix .warning { color: var(--warn); }
.matrix .critical { color: var(--crit); }
