/*
    Pillarbox admin UI — Nocturne.

    Ported from the Nocturne design system and the "Pillarbox v3 Nocturne" screen design
    (claude.ai/design project 47b7c4f2). Two rules from that system are load-bearing here and
    are the ones to check a change against:

      1. NOTHING IS HARD-CODED. Every colour, size, radius and shadow comes from a token below.
         The whole look retunes from the :root block and nowhere else.

      2. THE ACCENT IS A LINE AND A GLOW, NEVER A FLOOD. The primary button is an accent
         OUTLINE on transparent, not a filled block. Large saturated fills are the one thing
         this system does not do.

    WHAT CHANGED FROM THE PILLAR-BOX-RED BUILD, AND WHY IT MATTERS

    The old sheet carried a rule: --brand was pillar box red, --danger was a *different* red, and
    a status was never allowed to reach for the brand colour — otherwise every screen looks like
    it is on fire next to the logo.

    Nocturne is a mono scheme: one accent, no second hue. So that rule inverts rather than
    survives. Here the accent IS the alarm colour, and the brand mark is deliberately quiet — a
    thin outlined glyph in the same accent, carrying no weight of its own. The separation the old
    rule protected is now carried by WEIGHT rather than by hue:

        filled accent   = something is wrong and someone should act   (.badge-critical)
        accent outline  = worth knowing, not yet actionable           (.badge-warning)
        filled neutral  = measured and fine                           (.badge-healthy)
        hairline only   = not measured — absence of data, not health  (.badge-neutral)

    That last distinction is the one to protect. "We did not look" must never render like "we
    looked and it was fine"; DisplayTests pins the mapping, and the four treatments below are
    what makes it visible.
*/

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Tokens — from Nocturne's styles.css. Retune the look here.
   ══════════════════════════════════════════════════════════════════════════════════════ */

:root {
    --color-bg: #161826;
    --color-surface: #232532;
    --color-text: #e9e9ed;
    --color-accent: #e4483a;
    --color-divider: color-mix(in srgb, #e9e9ed 16%, transparent);

    /*
        Tonal ramps, generated in OKLCH on one shared lightness scale — the same step of any
        role carries the same visual weight. On this dark ground: 700–900 for tinted fills and
        subtle borders, 500 as the base, 100–300 for text sitting on those tints.
    */
    --color-neutral-100: #f3f5fe;
    --color-neutral-200: #e4e7f5;
    --color-neutral-300: #cfd3e5;
    --color-neutral-400: #b2b6ca;
    --color-neutral-500: #9397ab;
    --color-neutral-600: #75798c;
    --color-neutral-700: #595d6c;
    --color-neutral-800: #3f424d;
    --color-neutral-900: #292b31;

    --color-accent-100: #fdf4f2;
    --color-accent-200: #fbe2de;
    --color-accent-300: #f6c4bd;
    --color-accent-400: #ef9c91;
    --color-accent-500: #e4685a;
    --color-accent-600: #c9473a;
    --color-accent-700: #9e372d;
    --color-accent-800: #702820;
    --color-accent-900: #451a15;

    /*
        Fonts. Loaded from Google in index.html, but every stack falls through to a system face,
        so a blocked CDN costs the typeface and nothing else — no layout shift into a serif.
    */
    --font-heading: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-heading-weight: 500;
    --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;

    /* Density 0.70x — this system is compact on purpose. */
    --space-1: 2.8px;
    --space-2: 5.6px;
    --space-3: 8.4px;
    --space-4: 11.2px;
    --space-6: 16.8px;
    --space-8: 22.4px;

    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 14px;

    /* On a dark ground, elevation is a hairline edge plus ambient darkness — never a heavy drop. */
    --shadow-sm: 0 0 0 1px #3f424d;
    --shadow-md: 0 0 0 1px #595d6c, 0 6px 18px rgba(0, 0, 0, 0.55);
    --shadow-lg: 0 0 0 1px #9397ab, 0 16px 40px rgba(0, 0, 0, 0.65);

    --nav-width: 226px;
    --topbar-height: 68px;

    /*
        Aliases kept so older markup and scoped stylesheets keep resolving. New rules should use
        the tokens above directly; these exist to stop a stray var() from painting nothing.
    */
    --radius: var(--radius-md);
    --font: var(--font-body);
    --ink: var(--color-text);
    --ink-muted: var(--color-neutral-400);
    --ink-faint: var(--color-neutral-500);
    --line: var(--color-divider);
    --line-strong: var(--color-neutral-700);
    --surface: var(--color-surface);
    --surface-sunken: var(--color-bg);
    --surface-raised: var(--color-surface);
    --brand: var(--color-accent);
    --danger: var(--color-accent-300);
}

*,
*::before,
*::after { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.55;
    color: var(--color-text);
    background: var(--color-bg);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    line-height: 1.12;
    letter-spacing: -0.015em;
    margin: 0 0 var(--space-2);
}

/* Hierarchy here is size and space. Nothing goes past the 500 weight. */
h1 { font-size: 21px; }
h2 { font-size: 17px; }
h3 { font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-neutral-500); }

p { margin: 0 0 var(--space-3); }

a {
    color: var(--color-accent-300);
    text-decoration: none;
    border-bottom: 1px solid color-mix(in srgb, var(--color-accent) 40%, transparent);
}

a:hover { color: var(--color-accent-200); border-bottom-color: var(--color-accent); }

code, .mono { font-family: var(--font-mono); font-size: 0.92em; }

/* Interactive states are themed, never browser defaults. */
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/*
    FocusOnNavigate moves focus to the page heading after every route change so a screen reader
    announces the new screen. That is worth keeping — but the browser then paints a focus ring
    around the <h1>, and a permanent box around the title on every screen reads as a control
    somebody has selected. The ring goes; the focus, and the announcement, stay.
*/
[tabindex="-1"]:focus-visible { outline: none; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Shell
   ══════════════════════════════════════════════════════════════════════════════════════ */

/*
    A grid, not a flex row, and the page itself does not scroll: the sidebar and the main column
    each own their overflow. That is what lets the topbar stick without the nav sliding away
    with it.
*/
.shell {
    display: grid;
    grid-template-columns: var(--nav-width) minmax(0, 1fr);
    height: 100vh;
    overflow: hidden;
}

.sidebar {
    border-right: 1px solid var(--color-divider);
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: linear-gradient(180deg, color-mix(in srgb, var(--color-surface) 55%, transparent), transparent 40%);
}

.brand {
    height: var(--topbar-height);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 var(--space-8);
    flex: none;
}

/*
    The mark: an accent hairline square holding the letter slot, with the glow the accent is
    allowed to carry. Small and quiet on purpose — in a mono scheme the brand must not compete
    with the alarm state, because they are the same hue.
*/
.brand-mark {
    width: 22px;
    height: 22px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-accent);
    display: grid;
    place-items: center;
    flex: none;
    box-shadow: 0 0 12px color-mix(in srgb, var(--color-accent) 35%, transparent);
}

.brand-mark span {
    width: 9px;
    height: 2px;
    background: var(--color-accent);
}

.brand-word {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 16px;
    letter-spacing: -0.01em;
}

.nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-4);
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.nav a {
    display: flex;
    align-items: center;
    gap: 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-size: 13.5px;
    color: var(--color-neutral-400);
    text-decoration: none;
    padding: 9px 12px;
}

/* The active mark is a short accent bar. Short accent marks stay solid — only free rules fade. */
.nav a .nav-bar {
    width: 2px;
    height: 14px;
    flex: none;
    border-radius: 2px;
    background: transparent;
}

.nav a .nav-label { flex: 1 1 auto; min-width: 0; text-align: left; }

.nav a .nav-count {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--color-neutral-600);
}

.nav a:hover { background: color-mix(in srgb, var(--color-text) 6%, transparent); color: var(--color-text); }

.nav a.active {
    border-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
    background: var(--color-accent-900);
    font-weight: var(--font-heading-weight);
    color: var(--color-text);
}

.nav a.active .nav-bar { background: var(--color-accent); }

/* The failure count is the one number in the nav allowed to draw the eye. */
.nav a .nav-count-alert { color: var(--color-accent-300); }

.nav-footer {
    padding: var(--space-6) var(--space-8) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: none;
    border-top: 1px solid var(--color-divider);
}

.nav-env {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-neutral-400);

    /*
        min-width:0 is the load-bearing one. A flex item defaults to min-width:auto, which refuses
        to shrink below its content — so a long hostname pushed the sidebar wider than its track
        and drew over the menu instead of being clipped by it. Every other rule here is inert
        without it.
    */
    min-width: 0;
}

.nav-env-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-env::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--color-accent);
    flex: none;
    box-shadow: 0 0 8px var(--color-accent);
}

.nav-note { font-size: 12px; color: var(--color-neutral-500); line-height: 1.5; }

/* Who is signed in. Truncates rather than wrapping — an email address is long and the sidebar
   is 226px, and a wrapped address pushes the note below it out of view. */
.nav-user {
    font-size: 12.5px;
    color: var(--color-neutral-300);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-signout { align-self: flex-start; margin-left: calc(var(--space-1) * -1); }

/* ---- Sign-in screens ----

   Their own shell: no sidebar, no nav. The application chrome links to screens that require a
   signed-in operator, so showing it around a sign-in page offers a menu for an app you are not
   yet inside.
*/

.auth-shell {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: var(--space-8);
    background: var(--color-bg);
}

.auth-notice { max-width: 52ch; text-align: center; }
.auth-notice h1 { margin-bottom: var(--space-4); }

/* The sign-in card. Narrow on purpose — a form with two fields spread across a wide card
   reads as an unfinished layout rather than a deliberate one. */
.auth-card {
    width: min(380px, 100%);
    padding: var(--space-8);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
}

.auth-card h1 { font-size: 25px; margin-bottom: var(--space-3); }

/*
    The main column scrolls; the page inside it does not add its own padding, because the
    topbar has to run edge to edge to stick correctly.
*/
.content {
    min-width: 0;
    overflow-x: hidden;
    overflow-y: auto;
    position: relative;
}

.page-header {
    height: var(--topbar-height);
    border-bottom: 1px solid var(--color-divider);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-8);
    padding: 0 var(--space-8);
    position: sticky;
    top: 0;
    background: var(--color-bg);
    z-index: 6;
}

.page-header h1 { margin: 0; white-space: nowrap; }

.page-header-titles {
    display: flex;
    align-items: baseline;
    gap: var(--space-6);
    min-width: 0;
}

/* The one-line statement of what the screen is for. Truncates rather than wrapping the bar taller. */
.page-note {
    font-size: 12.5px;
    color: var(--color-neutral-500);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.page-body {
    padding: var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
}

/* A body whose own sections carry the padding — the filter bars that span the full width. */
.page-body-flush { padding: 0; display: block; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Cards, grids, stats
   ══════════════════════════════════════════════════════════════════════════════════════ */

.card {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-6);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    min-width: 0;
}

.card + .card { margin-top: 0; }

.card h2 { margin: 0; font-size: 17px; }

.grid { display: grid; gap: var(--space-6); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

.stat { display: flex; flex-direction: column; gap: 0; }

.stat .label {
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-neutral-500);
}

.stat .value {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 32px;
    line-height: 1;
    letter-spacing: -0.03em;
    margin-top: var(--space-4);
    font-variant-numeric: tabular-nums;
}

.stat .hint {
    font-size: 12.5px;
    color: var(--color-neutral-500);
    margin-top: var(--space-3);
    line-height: 1.45;
}

/*
    A metric that is the reason someone is looking at this screen. The accent runs down the
    inside edge and recolours the number — an alarm carried as a line, per the system.
*/
.stat-alarm { box-shadow: inset 2px 0 0 var(--color-accent); }
.stat-alarm .value { color: var(--color-accent-300); }

.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Badges

   Four weights, one hue — see the note at the top of this file for why the difference between
   them is weight and not colour, and which distinction is the load-bearing one.
   ══════════════════════════════════════════════════════════════════════════════════════ */

.badge {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    letter-spacing: 0.02em;
    padding: 3px 10px;
    border: 1px solid transparent;
    border-radius: calc(var(--radius-md) * 0.75);
    white-space: nowrap;
}

/* Filled accent — act on this. */
.badge-critical { background: var(--color-accent-800); color: var(--color-accent-100); }

/* Accent outline — know about it. */
.badge-warning { border-color: var(--color-accent); color: var(--color-accent-300); }

/* Filled neutral — measured, and fine. */
.badge-healthy { background: var(--color-neutral-800); color: var(--color-neutral-100); }

/* Tinted — informational, no judgement attached. */
.badge-info { background: var(--color-accent-900); color: var(--color-accent-300); }

/*
    Hairline only, and the quietest thing on the screen. This is what "we did not look" renders
    as; it must never be mistaken for the filled neutral above, which means "we looked".
*/
.badge-neutral { border-color: var(--color-neutral-700); color: var(--color-neutral-400); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Banners
   ══════════════════════════════════════════════════════════════════════════════════════ */

.banner {
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    padding: var(--space-6) var(--space-8);
    font-size: 13px;
    line-height: 1.55;
    text-wrap: pretty;
}

.banner strong { display: block; font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 15px; }

.banner-critical {
    border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
    background: linear-gradient(90deg, var(--color-accent-900), color-mix(in srgb, var(--color-surface) 70%, transparent));
    box-shadow: inset 3px 0 0 var(--color-accent);
    color: var(--color-text);
}

.banner-warning {
    border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
    color: var(--color-text);
}

.banner-info {
    border-color: var(--color-divider);
    background: color-mix(in srgb, var(--color-surface) 70%, transparent);
    color: var(--color-neutral-300);
}

/* The dashboard's incident strip: the banner plus the action that clears it. */
.banner-row {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    flex-wrap: wrap;
}

.banner-row > :first-child { flex: 1 1 300px; min-width: 0; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Tables

   Row rules are painted as row-level background strips, not cell borders, so the fade-to-
   transparent at each end spans the whole row rather than restarting inside every cell.
   ══════════════════════════════════════════════════════════════════════════════════════ */

.table-wrap { overflow-x: auto; }

table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}

th {
    text-align: left;
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: color-mix(in srgb, var(--color-text) 60%, transparent);
    padding: var(--space-3) var(--space-2);
    border-bottom: 1px solid transparent;
    white-space: nowrap;
}

td {
    padding: var(--space-3) var(--space-2);
    border-bottom: 1px solid transparent;
    vertical-align: top;
}

thead tr {
    background: linear-gradient(to right,
        transparent, var(--color-divider) 48px,
        var(--color-divider) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px;
}

tbody tr {
    background: linear-gradient(to right,
        transparent, color-mix(in srgb, var(--color-text) 8%, transparent) 48px,
        color-mix(in srgb, var(--color-text) 8%, transparent) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px;
}

tbody tr.clickable { cursor: pointer; }

/* The hover tint layers OVER the rule, which keeps painting underneath it. */
tbody tr.clickable:hover {
    background:
        linear-gradient(color-mix(in srgb, var(--color-text) 4%, transparent),
                        color-mix(in srgb, var(--color-text) 4%, transparent)) no-repeat 0 0 / 100% 100%,
        linear-gradient(to right,
            transparent, color-mix(in srgb, var(--color-text) 8%, transparent) 48px,
            color-mix(in srgb, var(--color-text) 8%, transparent) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px;
}

.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Forms and buttons
   ══════════════════════════════════════════════════════════════════════════════════════ */

label {
    display: block;
    font-size: 12px;
    margin-bottom: 5px;
    color: color-mix(in srgb, var(--color-text) 70%, transparent);
}

input, select, textarea {
    width: 100%;
    min-height: 36px;
    padding: 6px 10px;
    font: inherit;
    font-size: 14px;
    color: var(--color-text);
    caret-color: var(--color-accent);
    background: var(--color-surface);
    border: 1px solid var(--color-divider);
    border-radius: var(--radius-md);
}

input:hover, select:hover, textarea:hover {
    border-color: color-mix(in srgb, var(--color-text) 45%, transparent);
}

input:focus-visible, select:focus-visible, textarea:focus-visible {
    border-color: var(--color-accent);
    outline-offset: 0;
}

textarea { min-height: 90px; resize: vertical; line-height: 1.55; }

/*
    A native select paints its own chevron in the OS colour, which on a dark ground reads as a
    light-mode artefact stuck in the corner of the control. Replaced with an accent one drawn
    inline, so it takes the theme.
*/
select {
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    padding-right: 32px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%239184d9' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5 12 6.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 11px center;
    background-size: 13px;
    text-overflow: ellipsis;
}

/* The dropdown list itself is OS-painted; these keep it from opening white. */
select option { background: var(--color-surface); color: var(--color-text); }

/*
    A native checkbox paints an opaque white box on every platform, and accent-color only
    recolours the CHECKED state — so on this ground an unchecked box is the brightest thing on
    the screen and reads as a lit control rather than an empty one. Drawn from tokens instead.
*/
input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    min-height: 0;
    padding: 0;
    flex: none;
    display: inline-grid;
    place-content: center;
    border: 1.5px solid var(--color-divider);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    cursor: pointer;
    vertical-align: -3px;
}

input[type="checkbox"]:hover { border-color: var(--color-accent); }

input[type="checkbox"]:checked { background: var(--color-accent); border-color: var(--color-accent); }

input[type="checkbox"]::before {
    content: "";
    width: 10px;
    height: 10px;
    transform: scale(0);
    background: var(--color-bg);
    clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 16%, 84% 0, 39% 68%);
}

input[type="checkbox"]:checked::before { transform: scale(1); }

.filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
    align-items: flex-end;
}

.filters > div { min-width: 150px; }

/* The full-width filter bar the search screens open with, ruled off from the results. */
.filter-bar {
    padding: var(--space-8);
    border-bottom: 1px solid var(--color-divider);
    display: flex;
    align-items: flex-end;
    gap: var(--space-6);
    flex-wrap: wrap;
}

.result-bar {
    padding: var(--space-4) var(--space-8);
    border-bottom: 1px solid var(--color-divider);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    flex-wrap: wrap;
    font-size: 13px;
    color: var(--color-neutral-500);
}

button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 14px;
    line-height: 1.2;
    color: var(--color-text);
    background: transparent;
    border: 1px solid var(--color-divider);
    padding: var(--space-2) calc(var(--space-3) * 1.2);
    border-radius: var(--radius-md);
    cursor: pointer;
    white-space: nowrap;
}

button:hover:not(:disabled) { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
button:active:not(:disabled) { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
button:disabled { opacity: 0.45; cursor: not-allowed; }

/* The primary action is an accent OUTLINE. Never a fill — that is the system's whole posture. */
button.primary { color: var(--color-accent); border-color: var(--color-accent); background: transparent; }
button.primary:hover:not(:disabled) { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
button.primary:active:not(:disabled) { background: color-mix(in srgb, var(--color-accent) 22%, transparent); }

/*
    Destructive. The only control carrying a tint rather than an outline — with one hue to work
    with, the way "this cannot be undone" separates from "this is the main action" is weight.
*/
button.danger {
    color: var(--color-accent-200);
    border-color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 18%, transparent);
}

button.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

button.link {
    border-color: transparent;
    background: none;
    color: var(--color-accent);
    padding-inline: var(--space-1);
    font-weight: var(--font-heading-weight);
    text-decoration: none;
}

button.link:hover:not(:disabled) { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }

button.icon { width: 36px; height: 36px; padding: 0; flex: none; }

/* The segmented control — the dashboard's time window. */
.seg {
    display: inline-flex;
    overflow: hidden;
    border: 1px solid var(--color-divider);
    border-radius: var(--radius-md);
}

.seg button {
    border: 0;
    border-radius: 0;
    padding: 7px 12px;
    font-size: 13px;
    background: transparent;
    color: var(--color-text);
}

.seg button + button { border-left: 1px solid var(--color-divider); }
.seg button:hover:not(.is-active) { background: color-mix(in srgb, var(--color-text) 7%, transparent); }

.seg button.is-active {
    color: var(--color-accent);
    box-shadow: inset 0 0 0 1px var(--color-accent);
    background: transparent;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Drawer
   ══════════════════════════════════════════════════════════════════════════════════════ */

.drawer-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    display: flex;
    justify-content: flex-end;
    z-index: 20;
    animation: pb-fadein 120ms ease-out;
}

.drawer {
    position: relative;
    background: var(--color-bg);
    width: min(620px, 92vw);
    height: 100%;
    overflow-y: auto;
    border-left: 1px solid var(--color-neutral-700);
    box-shadow: var(--shadow-lg);
    animation: pb-slidein 160ms ease-out;
    padding: 0;
}

/* Sticky, so the subject and the close button stay reachable however long the trail runs. */
.drawer-header {
    padding: var(--space-6) var(--space-8);
    border-bottom: 1px solid var(--color-divider);
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-6);
    position: sticky;
    top: 0;
    background: var(--color-bg);
    z-index: 2;
    margin: 0;
}

.drawer-header h2 { font-size: 18px; letter-spacing: -0.01em; text-wrap: pretty; }

.drawer-section {
    padding: var(--space-6) var(--space-8);
    border-bottom: 1px solid var(--color-divider);
}

.drawer-section:last-child { border-bottom: 0; }

/* Sticky at the bottom for the same reason the header is sticky at the top. */
.drawer-actions {
    position: sticky;
    bottom: 0;
    background: var(--color-bg);
    border-top: 1px solid var(--color-divider);
    padding: var(--space-6) var(--space-8);
    display: flex;
    gap: var(--space-3);
    flex-wrap: wrap;
    align-items: center;
}

@keyframes pb-slidein {
    from { transform: translateX(24px); opacity: 0; }
    to { transform: none; opacity: 1; }
}

@keyframes pb-fadein {
    from { opacity: 0; }
    to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .drawer, .drawer-backdrop { animation: none; }
}

/*
    The preview iframe. Sandboxed with NO allow-scripts (spec §10) — the API already returns
    server-sanitised HTML, and this is the second defence, not the only one.

    Kept on a light ground deliberately: it renders a real customer email, and painting it dark
    would misrepresent what the recipient actually saw.
*/
.preview-frame {
    width: 100%;
    height: 420px;
    border: 1px solid var(--color-neutral-700);
    border-radius: var(--radius-md);
    background: #fff;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Timeline
   ══════════════════════════════════════════════════════════════════════════════════════ */

.timeline { list-style: none; padding: 0; margin: 0; }

.timeline li {
    padding: var(--space-4) 0 var(--space-4) var(--space-6);
    border-left: 2px solid var(--color-neutral-800);
    position: relative;
}

.timeline li::before {
    content: "";
    position: absolute;
    left: -5px;
    top: 15px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-neutral-700);
}

.timeline li.bad::before {
    background: var(--color-accent);
    box-shadow: 0 0 8px color-mix(in srgb, var(--color-accent) 60%, transparent);
}

.timeline li.good::before { background: var(--color-neutral-400); }

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Misc
   ══════════════════════════════════════════════════════════════════════════════════════ */

.muted { color: var(--color-neutral-400); }
.faint { color: var(--color-neutral-500); }
.small { font-size: 12.5px; }
.stack { display: flex; flex-direction: column; gap: var(--space-3); }
.row { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
.spread { display: flex; justify-content: space-between; gap: var(--space-4); align-items: baseline; }
.empty { padding: 56px 0; text-align: center; color: var(--color-neutral-500); font-size: 14px; }
.prose { max-width: 80ch; line-height: 1.55; text-wrap: pretty; }

/* A DNS record, an API key, a diagnostic code — anything meant to be copied verbatim. */
.record {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    background: var(--color-neutral-900);
    border: 1px solid var(--color-neutral-800);
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--color-neutral-300);
    overflow-x: auto;
}

.record .value { white-space: nowrap; }

/* A Copy button sitting inside a record drops to the record's size rather than towering over it. */
.record button { font-size: 12px; padding: 2px var(--space-2); flex: none; }

/* The small uppercase label that names a block without being a heading. */
.kicker {
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-neutral-500);
}

/* A number that has crossed a threshold, inside otherwise neutral running text. */
.alarm-text { color: var(--color-accent-300); }

/* ---- Volume chart ----

   Columns are a single auto-flow grid so they stay equal-width at any series length, and the
   axis labels are a second grid with the same track definition rather than being nested inside
   each bar — a label longer than its column would otherwise widen that column alone and put
   every bar out of step with the one above it.
*/

/*
    Grows to whatever the card has left. The card sits in a two-up grid beside Service health,
    so its height is decided by that neighbour — a fixed 164px chart leaves a slab of dead space
    underneath whenever the health list is the taller of the two. Bars are sized as percentages
    of the peak so they rescale with it.
*/
.chart {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: var(--space-4);
    align-items: end;
    flex: 1 1 auto;
    min-height: 164px;
    margin-top: var(--space-8);
    border-bottom: 1px solid var(--color-divider);
}

.chart-col {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 2px;
    height: 100%;
    min-width: 0;
}

.chart-total {
    font-size: 11px;
    color: var(--color-neutral-600);
    margin-bottom: var(--space-2);
    text-align: center;
    overflow: hidden;
    white-space: nowrap;
}

.chart-sent { background: var(--color-neutral-700); border-radius: 0 0 var(--radius-sm) var(--radius-sm); }

/* The accent cap, with the glow it is allowed to carry — this is the bit worth noticing. */
.chart-bounced {
    background: var(--color-accent);
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    box-shadow: 0 0 10px color-mix(in srgb, var(--color-accent) 55%, transparent);
}

.chart-labels {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: var(--space-4);
    margin-top: var(--space-3);
    font-size: 11.5px;
    color: var(--color-neutral-600);
}

.chart-labels div { overflow: hidden; white-space: nowrap; }

.chart-key { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.chart-key-sent { background: var(--color-neutral-600); }
.chart-key-bounced { background: var(--color-accent); }

/* ---- Service health list ---- */

.health-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4) 0;
    border-top: 1px solid var(--color-divider);
}

.spark { display: flex; align-items: flex-end; gap: 3px; height: 24px; }

.spark span {
    width: 4px;
    background: var(--color-neutral-700);
    min-height: 1px;
    border-radius: 1px;
}

/* One sending identity inside an account card: domain, its state, and the way in. */
.identity-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-top: 1px solid var(--color-divider);
}

.identity-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/*
    A checkbox and its label on one line. The global `label` rule stacks the label above its
    control, which is right for a text field and wrong for a checkbox — it puts the caption on
    a line of its own and the box underneath it, orphaned.
*/
.inline-check {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: 0;
    min-height: 36px;
    font-size: 13.5px;
    color: var(--color-neutral-400);
    cursor: pointer;
    white-space: nowrap;
}

.inline-check input { width: auto; flex: none; }

.cost-figure {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: 27px;
    letter-spacing: -0.02em;
    margin-top: var(--space-3);
    font-variant-numeric: tabular-nums;
}

/* An identifier quoted inside prose — a config key, a flag, a property name. */
code.accent { color: var(--color-accent-300); }

/* A meter — a daily send cap, a share of a budget. */
.meter {
    height: 6px;
    border-radius: 999px;
    background: var(--color-neutral-900);
    overflow: hidden;
}

.meter span { display: block; height: 100%; background: var(--color-accent); }
.meter-unlimited span { background: var(--color-neutral-700); }

/* Label/value pairs that align on a shared column — account facts, message metadata. */
.facts { margin: 0; display: grid; grid-template-columns: auto minmax(0, 1fr); }
.facts > div { display: contents; }

.facts dt {
    font-size: 12.5px;
    color: var(--color-neutral-500);
    padding: var(--space-3) var(--space-6) var(--space-3) 0;
    border-top: 1px solid var(--color-divider);
    white-space: nowrap;
}

.facts dd {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 12.5px;
    text-align: right;
    padding: var(--space-3) 0;
    border-top: 1px solid var(--color-divider);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Loading

   Every wait in this app is the brand mark posting a letter. The animation lives here rather
   than in a scoped stylesheet because index.html needs it too — the first wait a user sees is
   the WASM runtime downloading, before any component exists to render it.

   The mark takes its colour from currentColor, so it is the accent here and stays correct if
   the accent is ever retuned in the token block above.
   ══════════════════════════════════════════════════════════════════════════════════════ */

.pb-loader {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    padding: var(--space-8);
    color: var(--color-accent);
}

.pb-loader-inline { flex-direction: row; padding: 0; gap: var(--space-3); }

.pb-loader-text { font-size: 13px; color: var(--color-neutral-500); }

.pb-loader-mark { filter: drop-shadow(0 0 14px color-mix(in srgb, var(--color-accent) 40%, transparent)); }

.pb-loader-letter {
    /*
        fill-box so the origin is the letter's own centre, which puts the 14° tilt in the same
        place the static mark has it. Without it the origin is the whole 72×72 viewBox and the
        letter swings in from off-centre.
    */
    transform-box: fill-box;
    transform-origin: center;
    animation: pb-post 1.5s cubic-bezier(0.55, 0.06, 0.68, 0.19) infinite;
}

@keyframes pb-post {
    0% { transform: translateY(-11px) rotate(14deg); opacity: 0; }
    18% { transform: translateY(-7px) rotate(14deg); opacity: 1; }
    58% { transform: translateY(13px) rotate(14deg); opacity: 1; }
    70% { transform: translateY(16px) rotate(14deg); opacity: 0; }
    100% { transform: translateY(16px) rotate(14deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    /*
        No travel at all for anyone who has asked for less motion. The letter sits where the
        static mark puts it and the whole thing breathes instead, which still says "working"
        without anything moving across the screen.
    */
    .pb-loader-letter {
        animation: none;
        transform: rotate(14deg);
    }

    .pb-loader-mark { animation: pb-breathe 2s ease-in-out infinite; }
}

@keyframes pb-breathe {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.45; }
}

.loading { padding: var(--space-8); text-align: center; color: var(--color-neutral-500); }
