/* Esctelate admin UI.
   Operations tooling read under pressure at 3am: high contrast, dense tables, no decoration
   that competes with the data. Dark by default because that is when it gets used. */

:root {
    --bg: #14161a;
    --panel: #1c1f26;
    --panel-2: #22262f;
    --line: #2e333d;
    --text: #e6e9ef;
    --muted: #9aa3b2;
    --accent: #5b9dff;
    --ok: #3fb950;
    --warn: #d29922;
    --crit: #f85149;
    --radius: 6px;
    --radius-sm: 4px;

    /* A scale, so spacing is chosen from a set rather than invented per element. Inconsistent
       gaps are most of what makes an interface look unfinished - the eye reads the irregularity
       long before it reads the content. */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 24px;
    --s6: 32px;

    /* Every control is this tall. Mixed heights in one row is the single loudest defect a form
       can have, and it is invisible to whoever wrote it because they added the fields one at a
       time. */
    --control-h: 34px;

    --focus: #5b9dff66;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

code, pre, .ref { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

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

.shell { display: flex; min-height: 100vh; }

/* Sidebar */
.sidebar {
    width: 220px;
    flex: 0 0 220px;
    background: var(--panel);
    border-right: 1px solid var(--line);
    padding: 18px 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.brand { display: flex; gap: 10px; align-items: center; margin-bottom: 18px; padding: 0 6px; }
.brand-mark {
    width: 34px; height: 34px;
    display: grid; place-items: center;
    color: var(--accent);
}
.brand-name { font-weight: 650; }
.brand-sub { font-size: 11px; color: var(--muted); }

.nav-item {
    display: block; padding: 8px 10px; border-radius: var(--radius);
    color: var(--text); font-size: 13px;
}
.nav-item:hover { background: var(--panel-2); text-decoration: none; }
.nav-item.active { background: var(--panel-2); color: var(--accent); font-weight: 600; }
.nav-item-accent { margin-top: 8px; border: 1px dashed var(--line); }

.sidebar-foot { margin-top: auto; padding: 10px 6px 0; border-top: 1px solid var(--line); }
.phase-note { font-size: 11px; color: var(--muted); }

/* Content */
.content { flex: 1; padding: var(--s5) var(--s5) 60px; max-width: 1500px; }

.page-head {
    margin-bottom: var(--s5);
    display: flex; justify-content: space-between; align-items: flex-start;
    gap: 20px; margin-bottom: 18px;
}
.page-head h1 { margin: 0 0 var(--s1); font-size: 22px; font-weight: 650; letter-spacing: -.02em; }
.page-head p { margin: 0; }
.head-actions { display: flex; gap: 10px; align-items: center; }
.head-meta { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }

h2 { font-size: 15px; font-weight: 650; margin: 0 0 var(--s3); letter-spacing: -.01em; }
h3 { font-size: 12px; font-weight: 650; color: var(--muted); margin: 16px 0 6px;
     text-transform: uppercase; letter-spacing: .6px; }

.muted { color: var(--muted); }
.small { font-size: 12px; }
.num { text-align: right; }

/* Cards and layout */
.card {
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius); padding: var(--s4); margin-bottom: var(--s4);
}

/* A card's own head, separated by a rule. Without it a card with a title, a status and a table
   reads as three unrelated things that happen to share a border. */
.card-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--s4); flex-wrap: wrap;
    margin: calc(var(--s4) * -1) calc(var(--s4) * -1) var(--s4);
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--line);
    background: linear-gradient(var(--panel-2), var(--panel));
    border-radius: var(--radius) var(--radius) 0 0;
}
.card-head h2, .card-head h3 { margin: 0; }
.card-head .head-meta { margin-top: 0; align-items: center; }
.card-title-sub { margin: 2px 0 0; font-size: 12px; color: var(--muted); }

.card > .grid:last-child, .card > table:last-child { margin-bottom: 0; }
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 1100px) { .cols { grid-template-columns: 1fr; } }

.empty { padding: 40px; text-align: center; border: 1px dashed var(--line);
         border-radius: var(--radius); }

/* Banners */
.banner {
    background: var(--panel-2); border: 1px solid var(--line); border-left: 3px solid var(--accent);
    border-radius: var(--radius); padding: 10px 14px; margin-bottom: 16px; font-size: 13px;
}
.banner-warn { border-left-color: var(--warn); }
.banner-error { border-left-color: var(--crit); }

/* Tables */
.grid { width: 100%; border-collapse: collapse; font-size: 13px; }
.grid th {
    text-align: left; font-weight: 600; font-size: 11px; letter-spacing: .5px;
    text-transform: uppercase; color: var(--muted);
    padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); white-space: nowrap;
}
.grid td {
    padding: 10px var(--s3); border-bottom: 1px solid var(--line); vertical-align: middle;
}
.grid tbody tr:last-child td { border-bottom: none; }
.grid tbody tr:hover { background: var(--panel-2); }

/* Rows in a table that holds editable controls need to breathe, or a select sits flush against
   the row divider and the whole table looks cramped. */
.grid td:has(input), .grid td:has(select) { padding: 6px var(--s2); }
.clickable { cursor: pointer; }

.ref { font-size: 12px; color: var(--accent); }
.rule { font-size: 11px; color: var(--muted); margin-top: 3px; }
.body {
    margin: 6px 0 0; padding: 8px; background: var(--bg); border: 1px solid var(--line);
    border-radius: var(--radius); font-size: 11px; white-space: pre-wrap; color: var(--muted);
}

/* Pills */
.pill {
    line-height: 1.6;
    display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px;
    background: var(--panel-2); border: 1px solid var(--line); white-space: nowrap;
}
.pill-yes { color: var(--ok); border-color: #23502e; }
.pill-no { color: var(--muted); }
.pill-drill { color: var(--accent); border-color: #23405e; }

.state-open { color: var(--crit); border-color: #5e2320; }
.state-acknowledged { color: var(--warn); border-color: #5e4a20; }
.state-resolved { color: var(--ok); border-color: #23502e; }
.state-closed { color: var(--muted); }

.sev-critical { color: var(--crit); border-color: #5e2320; }
.sev-major { color: var(--warn); border-color: #5e4a20; }
.sev-minor { color: var(--muted); }
.sev-info { color: var(--muted); }

/* Definition lists */
.facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0; }
.facts dt { color: var(--muted); font-size: 12px; }
.facts dd { margin: 0; }

/* Timeline */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
    padding: 8px 0 8px 12px; border-left: 2px solid var(--line); margin-left: 4px;
}
.tl-kind { font-size: 11px; font-weight: 650; margin-right: 8px; }
.kind-resolve { color: var(--ok); }
.kind-reopen, .kind-escalate, .kind-exhausted { color: var(--warn); }
.kind-ack { color: var(--accent); }
.tl-when, .tl-actor { font-size: 11px; color: var(--muted); margin-right: 8px; }
.tl-detail { font-size: 12px; margin-top: 3px; }

/* Forms */
.field { display: block; margin-bottom: 12px; }
.field > span { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
/* Every text-like control, by type rather than by an allow-list. The previous rule named
   input[type=text] and forgot number, date and the rest, so half the fields on a form rendered as
   raw browser controls next to styled ones - which is exactly the mismatch that reads as
   unfinished. Naming what is excluded keeps a new input type styled by default. */
input:not([type=checkbox]):not([type=radio]):not([type=file]),
select,
textarea {
    height: var(--control-h);
    padding: 0 10px;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font: inherit;
    line-height: calc(var(--control-h) - 2px);
}

/* Selects size to their content. Forcing 100% turned a weekday picker into a full-width bar
   with one short word in it. */
select { width: auto; max-width: 100%; padding-right: 6px; cursor: pointer; }

textarea {
    height: auto;
    padding: var(--s2) 10px;
    line-height: 1.5;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
}

input:focus-visible, select:focus-visible, textarea:focus-visible, .btn:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus);
}

input::placeholder, textarea::placeholder { color: var(--muted); opacity: .65; }

.field { display: block; margin-bottom: var(--s3); }
.field > span { display: block; font-size: 12px; color: var(--muted); margin-bottom: var(--s1); }
.field input, .field select, .field textarea { width: 100%; }

.check {
    display: inline-flex; gap: var(--s2); align-items: center;
    height: var(--control-h); font-size: 13px; color: var(--text);
}
.check input { height: auto; }

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
    height: var(--control-h); padding: 0 14px;
    background: var(--panel-2); color: var(--text);
    border: 1px solid var(--line); border-radius: var(--radius-sm);
    font: inherit; font-weight: 500; cursor: pointer; white-space: nowrap;
    text-decoration: none; transition: background .12s, border-color .12s;
}
.btn:hover { background: var(--line); border-color: #3d4450; text-decoration: none; }
.btn-primary {
    background: var(--accent); color: #0b0d10; font-weight: 600; border-color: var(--accent);
}
.btn-primary:hover { background: #74adff; border-color: #74adff; }
.btn-danger { color: var(--crit); border-color: #5e2320; }
.btn-danger:hover { background: #2a1a1a; border-color: var(--crit); }
.btn:disabled { opacity: .45; cursor: default; }
.btn:disabled:hover { background: var(--panel-2); border-color: var(--line); }

#blazor-error-ui {
    display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
    background: var(--crit); color: #fff; padding: 10px 16px;
}
#blazor-error-ui .dismiss { cursor: pointer; float: right; }

/* Sign-in stands outside the shell. */
.signin-shell { display: grid; place-items: center; min-height: 100vh; padding: 24px; }
.signin { width: 100%; max-width: 340px; }
.signin .brand { justify-content: center; margin-bottom: 20px; }
.signin .card { margin-bottom: 12px; }
.signin .btn { width: 100%; }

.signed-in {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 8px 10px; margin-top: 8px; border-top: 1px solid var(--line);
}
.linkish {
    background: none; border: none; color: var(--accent); cursor: pointer;
    font: inherit; font-size: 12px; padding: 0;
}
.linkish:hover { text-decoration: underline; }

/* Global, not scoped inside .actions. The scoped version meant every action-row outside that one
   page had no layout at all - controls sat as inline elements on the text baseline, which is most
   of what made those forms look thrown together. */
.action-row {
    display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap;
    margin: var(--s3) 0;
}
.action-row:last-child { margin-bottom: 0; }
.reason { flex: 1 1 260px; min-width: 200px; }

/* ---------------------------------------------------------------------------
   Sign in.

   Dedicated styles rather than the sidebar's brand block and the generic card:
   this is the one screen seen by somebody who is not yet oriented - possibly at
   3am, possibly on a phone - so it gets its own spacing and its own hierarchy
   instead of borrowing shapes designed for dense tables.
   --------------------------------------------------------------------------- */

.signin-shell {
    display: grid;
    place-items: center;
    min-height: 100vh;
    padding: 24px;
    /* A faint lift behind the panel so it reads as a surface rather than a form
       floating in a void. */
    background:
        radial-gradient(60rem 40rem at 50% -10%, #1d222b 0%, transparent 70%),
        var(--bg);
}

.login { width: 100%; max-width: 380px; }

.login-brand { text-align: center; margin-bottom: 22px; }

.login-mark {
    display: inline-grid;
    place-items: center;
    width: 46px; height: 46px;
    color: var(--accent);
    margin-bottom: 12px;
}

.login-brand h1 { margin: 0; font-size: 19px; font-weight: 650; letter-spacing: -.2px; }
.login-brand p { margin: 3px 0 0; font-size: 12px; color: var(--muted); }

.login-panel {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 28px;
    box-shadow: 0 12px 32px rgb(0 0 0 / .28);
}

.login-lead { margin: 0 0 16px; font-size: 13px; color: var(--muted); }

.login-field { margin-bottom: 16px; }

.login-field label {
    display: block;
    font-size: 12px;
    font-weight: 500;
    color: var(--muted);
    margin-bottom: 6px;
}

.login-field input {
    width: 100%;
    padding: 10px 12px;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font: inherit;
    /* No transition on the border: a focus ring that fades in is harder to see
       when tabbing quickly, which is exactly when it is needed. */
}

.login-field input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgb(91 157 255 / .18);
}

.login-submit {
    display: block;
    width: 100%;
    margin-top: 4px;
    padding: 11px 16px;
    background: var(--accent);
    color: #0b0d10;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    font: inherit;
    font-weight: 600;
    text-align: center;
    cursor: pointer;
}

.login-submit:hover { filter: brightness(1.06); text-decoration: none; }
.login-submit:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

.login-error {
    background: rgb(248 81 73 / .1);
    border: 1px solid rgb(248 81 73 / .35);
    color: #ffb4ae;
    border-radius: var(--radius);
    padding: 10px 12px;
    margin-bottom: 18px;
    font-size: 13px;
}

.login-note {
    margin: 16px 4px 0;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--muted);
    text-align: center;
}

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

/* Coverage timeline.

   A proportional bar rather than a list, because the failure being shown is an absence and an
   absence is easy to scroll past in a table. Widths track duration, so a two-hour hole in a
   seven-day window reads as a thin bright stripe against a calm one. */
.coverage-bar {
    display: flex;
    width: 100%;
    height: 2rem;
    margin: 0.75rem 0 1.25rem;
    border-radius: 4px;
    overflow: hidden;
    background: var(--surface-2, #1b1f24);
}

.coverage-slice {
    flex-basis: 0;
    min-width: 2px;
}

.slice-covered { background: #2f855a; }
.slice-gap { background: #e53e3e; }
.slice-unreachable { background: #d69e2e; }

/* A gap and an unreachable shift need different fixes - a rota change versus a phone number -
   so they are never rendered as the same colour. */
.row-alert td { background: rgba(229, 62, 62, 0.08); }

@media (prefers-contrast: more) {
    .slice-covered { background: #1b5e20; }
    .slice-gap { background: #b71c1c; }
    .slice-unreachable { background: #8d6e00; }
}

/* Records that still exist but will not be acted on.

   Deliberately dimmed rather than hidden. A deactivated responder and a disabled number are
   still part of the configuration an operator is reasoning about - hiding them turns "why is
   nobody being called?" into a search rather than a glance. */
.card-muted { opacity: 0.62; }

.row-disabled td {
    opacity: 0.55;
    text-decoration-color: currentColor;
}

.row-disabled code { text-decoration: line-through; }

/* Readiness verdict.

   Coloured on the left edge rather than filled: this sits above a table an operator needs to
   read, and a full-bleed red panel makes the detail underneath harder to scan at exactly the
   moment it matters most. */
.card-blocked { border-left: 4px solid #e53e3e; }
.card-degraded { border-left: 4px solid #d69e2e; }
.card-ready { border-left: 4px solid #2f855a; }

/* Labelled inline form fields.

   The previous form was a row of bare inputs whose placeholders vanished the moment they held a
   value, leaving two unexplained number boxes next to a phone number. A label that disappears
   once the field is filled is a label that is absent exactly when somebody is checking their
   work. */
.field-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--s3);
    margin: var(--s4) 0;
    padding: var(--s3);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.field-row label {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
}

/* A checkbox label is a row, not a stack - the label belongs beside the box. */
.field-row label.check { flex-direction: row; align-items: center; }

.field-row label.grow { flex: 1 1 14rem; }

.field-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    opacity: 0.7;
}

.field-actions {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-left: auto;
}

.block { display: block; margin: 0.75rem 0; }

.code-area {
    width: 100%;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.85rem;
}

/* A destructive fallback, folded away.

   Round-robin generation overwrites future coverage. Presenting it with the same weight as the
   import invites somebody to flatten a negotiated year into name order with one click. */
.secondary-action { margin-top: 1rem; }

.secondary-action > summary {
    cursor: pointer;
    font-size: 0.85rem;
    opacity: 0.7;
}

.secondary-action > summary:hover { opacity: 1; }

/* The week currently in progress. On a 54-row rota, "who is on call right now" is the one row
   somebody is usually looking for, and scanning dates to find it is work the page can do. */
.row-now td { background: #1b2733; box-shadow: inset 2px 0 0 var(--accent); }

.row-actions { white-space: nowrap; text-align: right; }
.row-actions .btn { height: 26px; padding: 0 10px; font-size: 12px; }

.times-note { margin: 0 0 var(--s2); font-size: 11px; color: var(--muted); }

/* Navigation groups.

   Fifteen equally weighted links make every page look equally likely to be the one you want,
   which means reading all fifteen every time. The labels are quiet - they are signposts, not
   content, and should be findable without competing with the links under them. */
.nav-group { margin-bottom: var(--s4); }

.nav-group-label {
    display: block;
    padding: 0 10px;
    margin-bottom: var(--s1);
    font-size: 10px;
    font-weight: 650;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
    opacity: .65;
}

/* An inline panel opened from a card's header.

   Set into the card rather than floating in it, so it reads as "this belongs to the thing above"
   rather than as another section that happens to be next. Panels replaced a stack of permanent
   forms at the bottom of every card - each of which was visible whether or not anybody wanted it,
   which is what made the page feel assembled rather than designed. */
.panel {
    margin: 0 0 var(--s4);
    padding: var(--s3);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.panel > .field-row { background: none; border: none; padding: 0; }
.panel > small { display: block; margin-top: var(--s2); }

/* Segmented actions in a card header. */
.btn-group { display: inline-flex; gap: 0; }
.btn-group .btn { border-radius: 0; margin-left: -1px; }
.btn-group .btn:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); margin-left: 0; }
.btn-group .btn:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
.btn-group .btn-active,
.btn-active { background: var(--panel-2); border-color: var(--accent); color: var(--accent); z-index: 1; }

/* A section heading inside a card, separating data from the actions above it. */
.card > h3 {
    margin: var(--s5) 0 var(--s2);
    padding-bottom: var(--s2);
    border-bottom: 1px solid var(--line);
    font-size: 11px;
}
.card > h3:first-of-type { margin-top: var(--s3); }

/* Directory rows: a list of people, with detail opening in place.

   The alternative - a card per person, each holding its own table - spends most of the page on
   repeated column headers. Seven people rendered fifty-six cells of chrome around a handful of
   phone numbers, and answering "who can we actually call" meant reading all of it. */
.stack { display: flex; flex-direction: column; gap: 1px; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); }

.chip {
    display: inline-flex; align-items: center; gap: var(--s1);
    padding: 2px 8px;
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    white-space: nowrap;
}

.chip-off { opacity: .5; text-decoration: line-through; }

.chip-tag {
    font-family: inherit;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
}

/* The open row and its detail read as one block rather than two adjacent rows. */
.row-open > td { background: var(--panel-2); border-bottom: none; }
.row-open > td:first-child { box-shadow: inset 2px 0 0 var(--accent); }

.row-detail > td {
    background: var(--panel-2);
    padding: 0 var(--s4) var(--s4);
    box-shadow: inset 2px 0 0 var(--accent);
}

.row-detail h3 {
    margin: var(--s4) 0 var(--s2);
    padding-bottom: var(--s1);
    border-bottom: 1px solid var(--line);
    font-size: 11px;
}

/* A table inside a row does not need the outer table's weight. */
.grid-inner { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm); }
.grid-inner th { font-size: 10px; }
.grid-inner td, .grid-inner th { padding: 6px var(--s2); }

/* The row that says how a ladder ends. Quieter than the steps, because it is not a step. */
.row-terminal td {
    color: var(--muted);
    font-style: italic;
    background: var(--bg);
}

/* Text actions in a row need separating, or "Edit Remove" reads as one word. */
.row-actions .linkish + .linkish { margin-left: var(--s3); }

/* A wide table scrolls inside its own card rather than pushing the page sideways: the
   corpus grid grows a column every time a parser fills another field. */
.scroll-x { overflow-x: auto; }

/* Consequences under a banner, close enough to read as one thought. */
.tight { margin: 0.35rem 0 0; padding-left: 1.1rem; }
.tight li { margin: 0.15rem 0; }
