/* One vocabulary for the whole console.
   Every screen is built from the components below — card, tile, field, tag, table, list.
   A page that needs a look of its own is a missing component, not a licence for a style
   attribute: there are no inline styles in the page fragments, on purpose.

   The palette is the television app's, raised a step: the console is read on a lit desk
   rather than across a dark room, and at the app's near-black the surfaces and the page
   behind them were the same colour to anything but a calibrated screen — a card had a
   hairline and nothing else saying where it began. Everything is lifted together so the
   relationships hold, and the borders lift with them.

   The accent is still spent only on what is live, selected or primary. Beside it are three
   secondary tones with no verdict attached — info (blue), note (violet), data (teal) —
   which is what lets a page distinguish one kind of thing from another without every
   coloured element reading as a warning. Green means good, amber means look, red means
   wrong; the other three mean nothing at all, which is the point. */

:root {
  color-scheme: dark;
  --bg: #101418;
  --surface: #171d23;
  --surface-lift: #1f262d;
  --surface-hi: #252d35;
  --line: #29323a;
  --line-soft: #202830;
  --text: #eef2f5;
  --muted: #97a2ab;
  --quiet: #717c86;
  --accent: #52b54b;
  --accent-ink: #8fe287;
  --accent-wash: rgba(82, 181, 75, .14);
  --danger: #e5534b;
  --danger-ink: #ff9b94;
  --danger-wash: rgba(229, 83, 75, .14);
  --warn: #e0ad4e;
  --warn-ink: #f2cb78;
  --warn-wash: rgba(239, 196, 107, .14);
  --info: #4f9cd8;
  --info-ink: #93cbef;
  --info-wash: rgba(79, 156, 216, .15);
  --note: #a07ce8;
  --note-ink: #bda1f5;
  --note-wash: rgba(160, 124, 232, .15);
  --data: #3fc2b6;
  --data-ink: #6fdcd0;
  --data-wash: rgba(63, 194, 182, .14);
  --rail: 232px;
  --radius: 12px;
  --radius-sm: 8px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-ink); }

.skip {
  position: absolute; left: -9999px; top: 8px; z-index: 60;
  padding: 8px 14px; border-radius: var(--radius-sm);
  background: var(--surface-lift); color: var(--text); text-decoration: none;
}
.skip:focus { left: 8px; }

/* ---------- rail ---------- */

/* The rail sits a shade under the page rather than level with it. With everything lifted
   together, one border was no longer enough to say where the navigation stopped and the
   screen began. */
.rail {
  position: fixed; inset: 0 auto 0 0; z-index: 20; width: var(--rail);
  display: flex; flex-direction: column;
  padding: 20px 10px 12px;
  background: #0c1013; border-right: 1px solid var(--line);
}
.rail-scroll { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: 12px; }
.rail-brand {
  display: flex; align-items: center; gap: 11px;
  padding: 0 10px 18px; color: var(--text); text-decoration: none;
}
.rail-mark {
  display: grid; place-items: center; width: 30px; height: 30px; flex: 0 0 30px;
  border-radius: 9px; background: var(--accent); color: #06240a;
  font-size: 17px; font-weight: 800; letter-spacing: -.06em;
}
.rail-brand-copy b { display: block; font-size: 15px; line-height: 1.15; }
.rail-brand-copy span {
  display: block; margin-top: 1px; color: var(--quiet);
  font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
}
.rail-group {
  margin: 16px 12px 6px; color: var(--quiet);
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
}
.rail-nav { display: grid; gap: 2px; }
.rail-link {
  position: relative;
  display: flex; align-items: center; gap: 11px; min-height: 36px;
  padding: 0 12px; border-radius: var(--radius-sm);
  color: var(--muted); text-decoration: none; font-size: 13.5px; font-weight: 500;
}
.rail-link svg {
  width: 17px; height: 17px; flex: 0 0 17px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.rail-link:hover { color: var(--text); background: var(--surface); }
.rail-link:hover svg { stroke: var(--accent-ink); }
.rail-link.active { color: var(--text); background: var(--accent-wash); font-weight: 600; }
.rail-link.active svg { stroke: var(--accent); }
/* The page you are on is the one thing in the rail worth marking twice: a wash reads at a
   glance, the bar survives being looked at sideways on a poor monitor. */
.rail-link.active::before {
  content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 3px; height: 18px; border-radius: 0 3px 3px 0; background: var(--accent);
}
.rail-foot {
  display: flex; align-items: center; gap: 9px;
  padding: 12px 12px 0; border-top: 1px solid var(--line);
}
.rail-foot-copy { min-width: 0; }
.rail-foot-copy b { display: block; font-size: 12px; font-weight: 600; }
.rail-foot-copy span { display: block; color: var(--quiet); font-size: 11px; }

/* ---------- page ---------- */

.page {
  width: calc(100% - var(--rail)); margin-left: var(--rail);
  padding: 26px clamp(18px, 2.6vw, 40px) 56px;
  display: grid; gap: 16px; align-content: start;
}
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; padding-bottom: 2px;
}
/* The heading wears the same mark as the rail entry that reached it — the one thing on the
   page confirming which of twelve screens is open, for somebody who arrived by a link
   rather than by the menu. A hidden page (an account, its settings history) has no mark of
   its own and the template leaves it out, which is also what keeps the heading safe for the
   two pages that rewrite it from their own data. */
.page-head h1 {
  margin: 0; font-size: 25px; line-height: 1.2; letter-spacing: -.02em; font-weight: 650;
  display: flex; align-items: center; gap: 12px;
}
.page-mark { width: 34px; height: 34px; border-radius: 10px; }
.page-mark .ico { width: 19px; height: 19px; flex: 0 0 19px; }
.page-head p { margin: 5px 0 0; color: var(--muted); font-size: 14px; max-width: 62ch; }

.crumb {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--muted); text-decoration: none; font-size: 13px;
}
.crumb:hover { color: var(--text); }

/* ---------- icons ---------- */

/* Every icon on the console is one stroked path on a 24×24 grid, drawn in the current
   colour — the rail's marks, a card's heading, a tile, a tag. `Admin.icon` is the only
   place a path is written down, and `data-icon` on any element is how a fragment asks for
   one, so a page never carries SVG markup of its own. */
.ico {
  width: 16px; height: 16px; flex: 0 0 16px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

/* An icon in a tinted plate. The tint is the label: it says which of the console's areas a
   card belongs to before its heading has been read. */
.glyph {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--surface-hi); color: var(--muted);
}
.glyph[data-tone=accent] { background: var(--accent-wash); color: var(--accent-ink); }
.glyph[data-tone=ok] { background: var(--accent-wash); color: var(--accent-ink); }
.glyph[data-tone=bad] { background: var(--danger-wash); color: var(--danger-ink); }
.glyph[data-tone=warn] { background: var(--warn-wash); color: var(--warn-ink); }
.glyph[data-tone=info] { background: var(--info-wash); color: var(--info-ink); }
.glyph[data-tone=note] { background: var(--note-wash); color: var(--note-ink); }
.glyph[data-tone=data] { background: var(--data-wash); color: var(--data-ink); }
.glyph .ico { width: 15px; height: 15px; flex: 0 0 15px; }

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

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px 20px;
  display: grid; gap: 14px; align-content: start; min-width: 0;
}
/* A card whose body runs edge to edge — a list or a table. Everything else in it brings
   the padding back, or the heading sits against the border. */
.card.flush { padding: 0; }
.card.flush > .card-head { padding: 18px 20px 0; }
.card.flush > .card-foot { padding: 14px 20px 18px; }
.card-head { display: grid; gap: 4px; }
.card-head.split {
  grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 12px;
}
.card-title {
  margin: 0; font-size: 15px; font-weight: 650; letter-spacing: -.01em;
  display: flex; align-items: center; gap: 9px; min-width: 0;
}
.card-note { margin: 0; color: var(--muted); font-size: 13px; max-width: 74ch; }
.card-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding-top: 14px; border-top: 1px solid var(--line-soft);
}
.card-sub {
  margin: 0; color: var(--quiet);
  font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}

.grid { display: grid; gap: 16px; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.wide { grid-template-columns: minmax(0, 1.4fr) minmax(280px, 1fr); }

/* ---------- tiles ---------- */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 1px;
  background: var(--line); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.tile { background: var(--surface); padding: 14px 16px; display: grid; gap: 3px; }
.tile b { font-size: 22px; font-weight: 600; letter-spacing: -.02em; line-height: 1.2; }
.tile b.small { font-size: 14px; font-weight: 550; }
.tile span { color: var(--muted); font-size: 12px; }
/* The glyph is the tile's own row, above the number rather than beside it: a strip of
   tiles is read down the numbers, and an icon in that column would push them out of line
   the moment one tile had no icon to show. */
.tile .glyph { margin-bottom: 5px; }
/* Inside a card the surrounding border is already drawn, so the strip loses its own. */
.tiles.plain { border: 0; background: none; gap: 18px; border-radius: 0; overflow: visible; }
.tiles.plain .tile { padding: 0; background: none; }
.tiles.plain .tile b { font-size: 19px; }

/* Label-and-value rows: the quiet way to state settled facts inside a padded card. */
.kv { display: grid; }
.kv-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 9px 0; border-bottom: 1px solid var(--line-soft); font-size: 13.5px;
}
.kv-row:first-child { padding-top: 0; }
.kv-row:last-child { padding-bottom: 0; border-bottom: 0; }
.kv-row > span:first-child { color: var(--muted); }
.kv-row > :last-child { text-align: right; }

/* ---------- controls ---------- */

button {
  background: var(--surface-lift); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 8px 13px; font: inherit; font-size: 13.5px; font-weight: 550; cursor: pointer;
}
button:hover:not(:disabled) { border-color: #3a444d; background: var(--surface-hi); }
button.primary { background: var(--accent); border-color: var(--accent); color: #06240a; font-weight: 650; }
button.primary:hover:not(:disabled) { background: #5cc554; border-color: #5cc554; }
button.danger { background: var(--danger-wash); border-color: rgba(229, 83, 75, .35); color: var(--danger-ink); }
button.danger:hover:not(:disabled) { background: rgba(229, 83, 75, .2); }
button.quiet { background: none; border-color: transparent; color: var(--muted); padding: 6px 8px; }
button.quiet:hover:not(:disabled) { background: var(--surface-lift); color: var(--text); }
button.small { padding: 5px 10px; font-size: 12.5px; }
button:disabled { opacity: .4; cursor: not-allowed; }

input[type=text], input[type=password], input[type=number], input[type=search],
input[type=datetime-local], select, textarea {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 8px 11px; font: inherit; font-size: 13.5px; min-width: 0;
}
textarea {
  min-height: 62px; resize: vertical;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace;
}
input::placeholder, textarea::placeholder { color: var(--quiet); }
select { padding-right: 8px; }

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

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row.tight { gap: 7px; }
.row.end { justify-content: flex-end; }
/* A row mixing labelled fields with bare buttons: the controls line up, not their tops. */
.row.bottom { align-items: flex-end; }

.field { display: grid; gap: 6px; min-width: 0; }
.field > span { font-size: 13px; font-weight: 550; }
.field > em { font-style: normal; color: var(--muted); font-size: 12px; }
.field input, .field select, .field textarea { width: 100%; }
.field.grow { flex: 1 1 260px; }
.field.narrow { max-width: 200px; flex: 0 0 auto; }

.fields { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.fields.stack { grid-template-columns: 1fr; }

.check { display: flex; gap: 9px; align-items: flex-start; font-size: 13.5px; cursor: pointer; }
.check input { margin: 3px 0 0; accent-color: var(--accent); flex: 0 0 auto; }
.check em { display: block; font-style: normal; color: var(--muted); font-size: 12px; }
.checks { display: grid; gap: 8px; }
.checks.columns { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

/* A theme, shown as the colours it actually is.
   This is the one component whose colours are data rather than vocabulary, so the three
   custom properties below are set from a style attribute on the element — the fragment
   still declares no *look* of its own, only which palette this row is. Anything else about
   how a swatch is drawn belongs here. */
.swatch {
  flex: 0 0 auto; width: 46px; height: 30px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); overflow: hidden; display: flex; align-items: flex-end;
  background: var(--swatch-surface, var(--surface));
}
.swatch i {
  display: block; width: 100%; height: 9px;
  background: var(--swatch-accent, var(--accent));
  border-top: 1px solid var(--swatch-hairline, var(--line));
}

.hint { color: var(--muted); font-size: 12.5px; margin: 0; }

/* ---------- tags, notices ---------- */

.tag {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; letter-spacing: .01em;
  background: var(--surface-lift); color: var(--muted);
}
.tag[data-tone=ok] { background: var(--accent-wash); color: var(--accent-ink); }
.tag[data-tone=bad] { background: var(--danger-wash); color: var(--danger-ink); }
.tag[data-tone=warn] { background: var(--warn-wash); color: var(--warn-ink); }
.tag[data-tone=info] { background: var(--info-wash); color: var(--info-ink); }
.tag[data-tone=note] { background: var(--note-wash); color: var(--note-ink); }
.tag[data-tone=data] { background: var(--data-wash); color: var(--data-ink); }
.tag[data-tone=idle] { background: var(--surface-lift); color: var(--quiet); }
/* A verdict tag carries a dot in its own colour. Tone alone is a poor signal on a bad
   monitor and no signal at all to somebody who cannot separate the green from the amber;
   the dot is the shape that says these three are the same kind of statement. */
.tag[data-tone=ok]::before, .tag[data-tone=bad]::before,
.tag[data-tone=warn]::before, .tag[data-tone=idle]::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: 0 0 6px;
}
.tag[data-tone=idle]::before { opacity: .55; }
.tag .ico { width: 13px; height: 13px; flex: 0 0 13px; }

.dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; background: var(--quiet); }
.dot[data-tone=ok] { background: var(--accent); }
.dot[data-tone=bad] { background: var(--danger); }
.dot[data-tone=warn] { background: var(--warn-ink); }
.dot[data-tone=info] { background: var(--info); }

/* A notice is bordered on its leading edge in its own tone rather than all the way round:
   the strip is what the eye finds down a page of cards, and it survives being one of
   several stacked. */
.notice {
  padding: 11px 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); border-left: 3px solid var(--quiet);
  background: var(--surface-lift);
  color: var(--muted); font-size: 13px;
}
.notice[data-tone=info] { border-left-color: var(--info); background: var(--info-wash); color: var(--info-ink); }
.notice[data-tone=warn] { border-left-color: var(--warn); background: var(--warn-wash); color: var(--warn-ink); }
.notice[data-tone=ok] { border-left-color: var(--accent); background: var(--accent-wash); color: var(--accent-ink); }
.notice[role=alert] {
  border-color: rgba(229, 83, 75, .35); border-left-color: var(--danger);
  background: var(--danger-wash); color: var(--danger-ink);
}

.empty { color: var(--quiet); font-size: 13px; margin: 0; }
/* A flush card has no padding of its own, so its empty state has to bring some. */
.card.flush > .empty, .card.flush .list > .empty { padding: 18px 20px; }

.score {
  font: 650 19px/1 ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--accent-ink);
}

code, .code {
  font: 12px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--muted);
}

/* ---------- table ---------- */

.table-wrap { overflow-x: auto; margin: 0 -20px -18px; padding: 0 20px 18px; }
.card.flush .table-wrap { margin: 0; padding: 0; }
table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--line-soft); white-space: nowrap; }
thead th {
  color: var(--quiet); font-size: 11px; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase;
  background: var(--surface-lift); border-bottom-color: var(--line);
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-lift); }
/* The row under the pointer is marked at its leading edge as well as tinted — a long table
   read across a wide screen loses a tint of this weight by the time the eye reaches the
   far column. */
tbody tr:hover td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.muted { color: var(--muted); white-space: normal; }

/* ---------- list ---------- */

.list { display: grid; }
.list-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 14px; align-items: center;
  padding: 13px 20px; border-bottom: 1px solid var(--line-soft);
}
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: var(--surface-lift); box-shadow: inset 2px 0 0 var(--accent); }
.list-row:focus-within { background: var(--surface-lift); }
a.list-row { color: inherit; text-decoration: none; }
.list-main { display: flex; align-items: center; gap: 12px; min-width: 0; }
.list-main > span { min-width: 0; }
.list-title { font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.list-meta { display: block; color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.list-actions { display: flex; gap: 7px; flex-wrap: wrap; justify-content: flex-end; }

/* The build history under a device row. Held to smaller type than an ordinary chip on
   purpose: a set that has been through six releases must not end up outweighing its own
   name in the row it belongs to. */
.list-versions { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 6px; }
.list-versions .chip { padding: 1px 7px; font-size: 11.5px; }

/* A person is violet, not green. Green is the console's verdict colour and an avatar is no
   verdict — a list of viewers in accent read as a list of things that were working. */
.avatar {
  display: grid; place-items: center; width: 34px; height: 34px; flex: 0 0 34px;
  border-radius: 50%; background: var(--note-wash); color: var(--note-ink);
  font-size: 13px; font-weight: 700;
}
.avatar[data-tone=ok] { background: var(--accent-wash); color: var(--accent-ink); }
.avatar[data-tone=idle] { background: var(--surface-hi); color: var(--quiet); }

.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px; border-radius: 6px;
  background: var(--surface-lift); color: var(--muted); font-size: 12px;
}
.chip[data-tone=accent] { color: var(--accent-ink); background: var(--accent-wash); }
.chip[data-tone=warn] { color: var(--warn-ink); background: var(--warn-wash); }
.chip[data-tone=bad] { color: var(--danger-ink); background: var(--danger-wash); }
.chip[data-tone=info] { color: var(--info-ink); background: var(--info-wash); }
.chip[data-tone=note] { color: var(--note-ink); background: var(--note-wash); }
.chip[data-tone=data] { color: var(--data-ink); background: var(--data-wash); }
.chip .ico { width: 12px; height: 12px; flex: 0 0 12px; }
.chip.mono { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11.5px; }

details summary { color: var(--muted); cursor: pointer; font-size: 13px; }
details[open] summary { margin-bottom: 8px; }

/* ---------- log viewer ---------- */

.log {
  height: min(62vh, 760px); min-height: 340px; overflow: auto;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm);
  font: 12px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace;
}
.log-line {
  display: grid; grid-template-columns: 168px 56px minmax(200px, 260px) minmax(280px, 1fr);
  gap: 10px; padding: 4px 11px; border-bottom: 1px solid var(--line-soft);
}
.log-line:hover { background: var(--surface); }
.log-time, .log-attrs { color: var(--quiet); }
.log-level { font-weight: 700; color: var(--info-ink); }
.log-level.ERROR { color: var(--danger-ink); }
.log-level.WARN { color: var(--warn-ink); }
.log-level.DEBUG { color: var(--quiet); }
/* The line is banded in the level's colour. Scrolling a log is looking for the one line
   that is not INFO, and a coloured word four columns wide is easy to scroll past. */
.log-line.ERROR { background: rgba(229, 83, 75, .07); box-shadow: inset 2px 0 0 var(--danger); }
.log-line.WARN { background: rgba(239, 196, 107, .06); box-shadow: inset 2px 0 0 var(--warn); }
.log-empty { padding: 18px; color: var(--quiet); }

/* ---------- responsive ---------- */

@media (max-width: 1000px) {
  :root { --rail: 62px; }
  .rail { padding: 16px 7px 10px; }
  .rail-brand { justify-content: center; padding: 0 0 14px; }
  .rail-brand-copy, .rail-group, .rail-link span, .rail-foot-copy { display: none; }
  .rail-link { justify-content: center; padding: 0; min-height: 38px; }
  .rail-foot { justify-content: center; padding: 12px 0 0; }
  .grid.wide { grid-template-columns: 1fr; }
  .page { padding: 20px 14px 44px; }
  .table-wrap { margin: 0 -20px -18px; }
}

@media (max-width: 620px) {
  .card { padding: 16px; }
  .list-row { grid-template-columns: 1fr; }
  .list-actions { justify-content: flex-start; }
  .log-line { grid-template-columns: 130px 50px 1fr; }
  .log-attrs { grid-column: 1 / -1; }
}
