/* Docker Manager — the whole visual language lives here.
   /ui renders every component in this file; if it is not on that page, it does
   not exist. Add the component and its example together or neither.

   One committed look: dark, blue primaries, a sober technical surface. There
   is no light theme, so no colour is defined twice. */

/* ========================================================================
   1. Tokens
   ==================================================================== */

:root {
  color-scheme: dark;

  /* Ground up: page, raised panel, sunken well. */
  --bg: #0e1116;
  --bg-soft: #12161d;
  --surface: #181d27;
  --surface-2: #1d232e;
  --sunken: #0d1117;

  /* A card is a flat surface with a light rinsed over the top edge. */
  --card-sheen: linear-gradient(180deg, rgba(255, 255, 255, .045), rgba(255, 255, 255, 0) 62%);

  --line: #313a4b;
  --line-soft: #262e3c;
  --line-strong: #35405230;

  --fg: #eef1f6;
  --fg-dim: #ccd4e0;
  --muted: #a2adbe;
  --faint: #8792a4;

  --primary: #2f6fed;
  --primary-hover: #6098ff;
  --primary-soft: #2f6fed24;
  --primary-line: #2f6fed66;

  --ok: #4ed69a;
  --warn: #f0b249;
  --bad: #ff6b6b;
  --info: #5ec7f5;
  --ok-soft: #3ec98a1f;
  --warn-soft: #e3a53c1f;
  --bad-soft: #ef5a5a1f;
  --info-soft: #4cb8e81f;

  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 14px;

  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px -12px rgba(0, 0, 0, .6);
  --shadow-lg: 0 24px 60px -20px rgba(0, 0, 0, .75);

  --sidebar-w: 240px;
  --topbar-h: 52px;

  --font: "Roboto", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ========================================================================
   2. Base
   ==================================================================== */

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--fg);
  font: 400 14px/1.55 var(--font);
  letter-spacing: .005em;
  -webkit-font-smoothing: antialiased;
}

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

::selection { background: var(--primary-soft); }

:focus-visible {
  outline: 2px solid var(--primary-hover);
  outline-offset: 2px;
  border-radius: 3px;
}

code, kbd, samp, pre { font-family: var(--mono); }

/* Scrollbars, so a dark panel does not sprout a bright rail. */
* { scrollbar-color: #39414f transparent; scrollbar-width: thin; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: #39414f; border-radius: 8px; border: 3px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: #4a5364; background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

/* ========================================================================
   3. The shell — sidebar, topbar, content
   ==================================================================== */

body.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  align-items: start;
}

/* Signed out: one centred column, no chrome. */
body.auth { display: grid; place-items: center; padding: 2rem 1rem; }
body.auth .main { width: 100%; max-width: 25rem; min-height: auto; }
body.auth .content { padding: 0; }

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  gap: .25rem;
  padding: .85rem .7rem 1rem;
  background: var(--bg-soft);
  border-right: 1px solid var(--line-soft);
}

.brand {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .35rem .5rem .9rem;
  color: var(--fg);
  font-weight: 500;
  letter-spacing: .01em;
}
.brand:hover { text-decoration: none; }
.brand-mark {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 7px;
  background: linear-gradient(160deg, var(--primary-hover), var(--primary));
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .02em;
}
.brand small { display: block; color: var(--muted); font-size: 10px; font-weight: 400; letter-spacing: .06em; text-transform: uppercase; }

.side-search { position: relative; margin: 0 .25rem .9rem; }
.side-search input {
  width: 100%;
  padding: .42rem .6rem .42rem 1.9rem;
  font-size: 13px;
  background: var(--sunken);
}
.side-search .icon { position: absolute; left: .55rem; top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }
.side-search kbd { position: absolute; right: .4rem; top: 50%; transform: translateY(-50%); }

kbd {
  padding: .05rem .3rem;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 10px;
  line-height: 1.4;
}

.side-nav { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 1rem; }
.nav-group { display: flex; flex-direction: column; gap: 1px; }
.nav-label {
  margin: 0 0 .3rem .6rem;
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .42rem .6rem;
  border-radius: var(--radius-sm);
  color: var(--fg-dim);
  font-size: 13.5px;
}
.nav-item:hover { background: var(--surface); color: var(--fg); text-decoration: none; }
.nav-item.is-active {
  background: var(--primary-soft);
  color: #dfe9ff;
  box-shadow: inset 2px 0 0 var(--primary-hover);
}
.nav-item .icon { color: var(--muted); flex: none; }
.nav-item.is-active .icon { color: var(--primary-hover); }
.nav-item .count {
  margin-left: auto;
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
}

.side-foot { border-top: 1px solid var(--line-soft); padding-top: .7rem; display: flex; align-items: center; gap: .55rem; }
.avatar {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--fg-dim);
  font-size: 12px;
  font-weight: 500;
}
.side-foot .who { min-width: 0; line-height: 1.25; }
.side-foot .who strong { display: block; font-size: 13px; font-weight: 500; }
.side-foot .who span { color: var(--muted); font-size: 11px; }
.side-foot form { margin-left: auto; }

.main { min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 1rem;
  height: var(--topbar-h);
  padding: 0 1.5rem;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line-soft);
}
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: .5rem; }

/* The page-level progress bar: two pixels along the top of the topbar. It is
   always in the DOM so nothing shifts when work starts. */
.progress {
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  overflow: hidden;
  opacity: 0;
  transition: opacity .2s ease;
}
.progress.is-active { opacity: 1; }
.progress .bar {
  height: 100%;
  width: 40%;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--primary-hover), transparent);
  animation: slide 1.1s ease-in-out infinite;
}
@keyframes slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
@media (prefers-reduced-motion: reduce) {
  .progress .bar { animation: none; width: 100%; opacity: .5; }
}

.content { padding: 1.4rem 1.5rem 3rem; width: 100%; max-width: 110rem; }
.content.narrow { max-width: 52rem; }

/* ========================================================================
   4. Typography
   ==================================================================== */

h1 { margin: 0 0 .2rem; font-size: 19px; font-weight: 500; letter-spacing: -.01em; }
h2 { margin: 2rem 0 .7rem; font-size: 14px; font-weight: 500; }
h3 { margin: 1.4rem 0 .5rem; font-size: 13px; font-weight: 500; color: var(--fg-dim); }
p { margin: .5rem 0; }

.page-head { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: 1.2rem; }
.page-head .grow { min-width: 0; flex: 1; }
.page-head .actions { margin: 0; }

.lede { color: var(--muted); font-size: 13px; margin: 0; }
.muted { color: var(--muted); font-size: 13px; }
.faint { color: var(--faint); }
.mono { font-family: var(--mono); font-size: 12.5px; }
.eyebrow {
  color: var(--faint);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.num { font-variant-numeric: tabular-nums; }

/* ========================================================================
   5. Breadcrumbs
   ==================================================================== */

.crumbs { display: flex; align-items: center; gap: .4rem; font-size: 13px; min-width: 0; }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--fg); text-decoration: none; }
.crumbs .sep { color: var(--faint); }
.crumbs .here { color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ========================================================================
   6. Buttons
   ==================================================================== */

button, .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  padding: .42rem .75rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--primary);
  color: #fff;
  font: 500 13px/1.4 var(--font);
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
button:hover, .button:hover { background: var(--primary-hover); text-decoration: none; }
button:active, .button:active { transform: translateY(.5px); }

.button.secondary, button.secondary {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--fg-dim);
}
.button.secondary:hover, button.secondary:hover { background: #262d39; color: var(--fg); }

.button.ghost, button.ghost, button.link {
  background: transparent;
  border-color: transparent;
  color: var(--fg-dim);
}
.button.ghost:hover, button.ghost:hover, button.link:hover { background: var(--surface-2); color: var(--fg); }

.button.danger, button.danger { background: var(--bad); color: #1a0c0c; }
.button.danger:hover, button.danger:hover { background: #ff7070; }

.button.quiet-danger, button.quiet-danger {
  background: transparent; border-color: #ef5a5a55; color: var(--bad);
}
.button.quiet-danger:hover, button.quiet-danger:hover { background: var(--bad-soft); }

.button.sm, button.sm { padding: .24rem .5rem; font-size: 12px; }
.button.lg, button.lg { padding: .58rem 1.1rem; font-size: 14px; }
.button.block, button.block { width: 100%; }

button:disabled, .button.is-disabled {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}

button.icon-only { padding: .35rem; width: 28px; height: 28px; }

.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }

/* A button that is waiting on the server. */
button.is-busy { position: relative; color: transparent; pointer-events: none; }
button.is-busy::after {
  content: "";
  position: absolute;
  width: 13px; height: 13px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.spinner {
  display: inline-block;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--primary-hover);
  animation: spin .7s linear infinite;
  vertical-align: -2px;
}

/* ========================================================================
   7. Forms
   ==================================================================== */

label { display: block; color: var(--fg-dim); font-size: 12.5px; font-weight: 500; margin-bottom: .3rem; }

input, select, textarea {
  width: 100%;
  padding: .5rem .65rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #10151d;
  color: var(--fg);
  font: 400 13.5px/1.5 var(--font);
}
input::placeholder, textarea::placeholder { color: #75808f; }
input:hover, select:hover, textarea:hover { border-color: #333d4d; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary-line);
  box-shadow: 0 0 0 3px var(--primary-soft);
}
textarea { font-family: var(--mono); font-size: 12.5px; min-height: 7rem; resize: vertical; }
select {
  appearance: none;
  padding-right: 1.8rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: right 1rem center, right .72rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
input[type="checkbox"], input[type="radio"] { width: auto; accent-color: var(--primary); }

.field { margin-bottom: .9rem; }
.field .hint { margin: .3rem 0 0; color: var(--muted); font-size: 12px; }
.field.is-invalid input, .field.is-invalid select { border-color: var(--bad); }
.field .msg { margin: .3rem 0 0; color: var(--bad); font-size: 11.5px; }

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; }

.check { display: flex; align-items: flex-start; gap: .55rem; margin-bottom: .55rem; }
.check input { margin-top: .18rem; }
.check span { color: var(--fg-dim); font-size: 13px; font-weight: 400; }
.check small { display: block; color: var(--faint); font-size: 11.5px; }

/* A switch, for settings that take effect immediately. */
.switch { display: inline-flex; align-items: center; gap: .55rem; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch .track {
  width: 34px; height: 19px; flex: none;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  transition: background .15s ease, border-color .15s ease;
  position: relative;
}
.switch .track::after {
  content: "";
  position: absolute; top: 2px; left: 2px;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform .15s ease, background .15s ease;
}
.switch input:checked + .track { background: var(--primary); border-color: var(--primary); }
.switch input:checked + .track::after { transform: translateX(15px); background: #fff; }
.switch input:focus-visible + .track { box-shadow: 0 0 0 3px var(--primary-soft); }

/* Legacy shape used by the settings forms. */
.settings { display: flex; flex-direction: column; max-width: 38rem; }
.settings label.check { display: flex; align-items: center; gap: .5rem; font-weight: 400; }
.settings label.check input { width: auto; }
.hint { margin: .3rem 0 .7rem; color: var(--muted); font-size: 12px; }

/* ========================================================================
   8. Cards and panels
   ==================================================================== */

.card {
  position: relative;
  background: var(--card-sheen), var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.1rem 1.2rem;
  box-shadow: var(--shadow);
}
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }

.card-head {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin: -1.1rem -1.2rem 1rem;
  padding: .7rem 1.2rem;
  border-bottom: 1px solid var(--line-soft);
}
.card-head h2, .card-head h3 { margin: 0; font-size: 13px; }
.card-head .grow { flex: 1; min-width: 0; }
.card-foot {
  margin: 1rem -1.2rem -1.1rem;
  padding: .7rem 1.2rem;
  border-top: 1px solid var(--line-soft);
  display: flex;
  align-items: center;
  gap: .5rem;
}

.panel-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.split { display: grid; gap: 1rem; grid-template-columns: 2fr 1fr; align-items: start; }
/* A chart needs the room a fact list does not. */
.split.wide-right { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); }
.split.wide-left { grid-template-columns: minmax(0, 1fr) 15rem; }
@media (max-width: 1100px) {
  .split.wide-right, .split.wide-left { grid-template-columns: minmax(0, 1fr); }
}

/* ========================================================================
   Charts (section 23)
   ==================================================================== */

/* ⚠️ `min-width: 0` on the host: a grid item defaults to `auto`, which is the
   content's minimum — and uPlot sizes itself from the element, so without this
   the chart can only ever grow the column it lives in. */
.chart { min-width: 0; width: 100%; }
.chart.is-loading { opacity: .55; }
.chart-empty { padding: 2.5rem 0; text-align: center; }

/* uPlot ships a light theme; these are the four things it paints itself. */
.chart .u-legend { font-size: 12px; color: var(--muted); }
.chart .u-legend .u-marker { border-radius: 2px; }
.chart .u-legend th { font-weight: 400; }
.chart .u-legend .u-value { color: var(--fg-dim); font-variant-numeric: tabular-nums; }
.chart .u-select { background: var(--primary-soft); }
.chart .u-cursor-x, .chart .u-cursor-y { border-color: var(--line); }

/* A segmented switch: one question, a handful of answers, one of them current.
   Cheaper to read than a select, and it shows the alternatives without a
   click. */
.seg { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--sunken); }
.seg-quiet { margin: 0 0 .8rem; border-color: transparent; background: transparent; padding: 0; }
.seg-btn {
  padding: .25rem .6rem;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  font-weight: 400;
}
.seg-btn:hover { color: var(--fg); background: var(--surface-2); }
.seg-btn.is-active { background: var(--primary-soft); color: #dfe9ff; }
.seg-quiet .seg-btn.is-active { background: var(--surface-2); color: var(--fg); }

.filter-list { display: flex; flex-direction: column; gap: .1rem; max-height: 22rem; overflow-y: auto; }
.filter-list .check { padding: .2rem .1rem; }
.filter-list .check small { display: block; color: var(--faint); font-size: 11px; }
@media (max-width: 62rem) { .split, .field-row { grid-template-columns: 1fr; } }

/* A number worth looking at, with the trend under it. */
.stat { display: flex; flex-direction: column; gap: .15rem; }
.stat .value { font-size: 24px; font-weight: 500; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.stat .label { color: var(--muted); font-size: 12px; }
.stat .delta { font-size: 11.5px; }
.stat .delta.up { color: var(--ok); }
.stat .delta.down { color: var(--bad); }
.stat-chart { margin-top: .5rem; }

/* ========================================================================
   9. Status: pills, dots, tags
   ==================================================================== */

.pill {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .1rem .45rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.6;
  white-space: nowrap;
}
.pill.ok { color: var(--ok); background: var(--ok-soft); }
.pill.bad { color: var(--bad); background: var(--bad-soft); }
.pill.warn { color: var(--warn); background: var(--warn-soft); }
.pill.info { color: var(--info); background: var(--info-soft); }
.pill.off { color: var(--muted); background: #7f8a9c14; }
.pill.solid { border-color: transparent; }

.dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.dot.pulse { box-shadow: 0 0 0 0 currentColor; animation: pulse 1.8s ease-out infinite; }
@keyframes pulse {
  70% { box-shadow: 0 0 0 5px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* Visually hidden, still read aloud: a select in a window bar has no room for
   a visible label but still needs one. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The one place a destructive form lives. Bordered rather than filled: it must
   read as dangerous without becoming the loudest thing on the page. */
.card.danger { border-color: var(--bad-soft, #7f2b2b); }
button.danger {
  background: var(--bad, #b4443c);
  border-color: transparent;
  color: #fff;
}
button.danger:hover { filter: brightness(1.08); }

/* Separates the sections of a long form without pretending to be a card edge. */
.rule {
  border: 0;
  border-top: 1px solid var(--line, #2a3040);
  margin: 1.5rem 0 1rem;
}

.inline-form {
  display: flex;
  gap: var(--gap-sm, .5rem);
  align-items: center;
  flex-wrap: wrap;
}

.tag {
  display: inline-block;
  padding: .05rem .4rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--fg-dim);
  font-family: var(--mono);
  font-size: 11.5px;
}

/* ========================================================================
   10. Tables
   ==================================================================== */

/* ⛔ Scrolls by default, not on request. A table wider than its column widens
   the grid track, the page grows past the viewport and the sidebar scrolls out
   of sight — the symptom reads as "the layout is broken", nowhere near the
   table that caused it. The opt-in version of this rule was the bug (RULE-16). */
.table-wrap {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow-x: auto;
}
/* Sticky headers and a horizontal scroll container are mutually exclusive:
   sticky resolves against the scroller, not the viewport. These tables are
   short, and a page that fits is worth more than a header that follows. */
.table-wrap .grid th { position: static; }
.table-wrap .grid { border: 0; }

.grid { width: 100%; border-collapse: collapse; font-size: 13px; }
.grid th {
  position: sticky;
  top: var(--topbar-h);
  z-index: 1;
  padding: .5rem .75rem;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}
.grid td {
  padding: .55rem .75rem;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
  /* A container name or a domain has no spaces to break at, and one long
     enough would otherwise decide the width of the whole table. */
  overflow-wrap: anywhere;
}
.grid tbody tr:last-child td { border-bottom: 0; }

/* ------------------------------------------------------------------------
   DataTables, re-skinned.

   The vendored stylesheet is a light theme, and it loads first so these win.
   Everything here is a token — a table that searches must still look like the
   rest of the product (RULE-16). `table.grid.dataTable` rather than `.grid`,
   because DataTables adds its class and an element selector beats ours.
   ---------------------------------------------------------------------- */

table.grid.dataTable {
  border-collapse: collapse;
  color: inherit;
}
table.grid.dataTable > thead > tr > th,
table.grid.dataTable > tbody > tr > td {
  border-bottom: 1px solid var(--line-soft);
  box-shadow: none;
}
table.grid.dataTable > thead > tr > th { border-bottom-color: var(--line); }
table.grid.dataTable > tbody > tr { background: transparent; }
table.grid.dataTable > tbody > tr:hover > td { background: #ffffff05; }
table.grid.dataTable > tbody > tr:last-child > td { border-bottom: 0; }
table.grid.dataTable td.dt-empty { padding: 1.6rem; color: var(--muted); text-align: center; }

/* ⛔ DataTables sniffs a column's type and right-aligns the ones it reads as a
   date or a number. Alignment is the template's call — `.right` where it is
   wanted — and a library guessing it put a right-aligned header over a column
   of left-aligned cells. */
table.grid.dataTable th.dt-type-numeric,
table.grid.dataTable th.dt-type-date,
table.grid.dataTable td.dt-type-numeric,
table.grid.dataTable td.dt-type-date { text-align: left; }
table.grid.dataTable th.right,
table.grid.dataTable td.right { text-align: right; }

/* The sort affordance: the header is a button in all but name. */
table.grid.dataTable thead th.dt-orderable-asc,
table.grid.dataTable thead th.dt-orderable-desc { cursor: pointer; }
table.grid.dataTable thead th.dt-orderable-asc:hover,
table.grid.dataTable thead th.dt-orderable-desc:hover { color: var(--fg-dim); }
table.grid.dataTable thead th.dt-ordering-asc,
table.grid.dataTable thead th.dt-ordering-desc { color: var(--fg); }
table.grid.dataTable thead th span.dt-column-order:before,
table.grid.dataTable thead th span.dt-column-order:after { opacity: .35; }
table.grid.dataTable thead th.dt-ordering-asc span.dt-column-order:before,
table.grid.dataTable thead th.dt-ordering-desc span.dt-column-order:after { opacity: 1; }

/* table.js hoists these out of the scrolling wrapper, so they sit directly in
   the page: the search above the table, the paging below it. */
.dt-layout-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: 0 0 .6rem;
}
.dt-layout-row + .table-wrap { margin-top: 0; }
/* What stays inside the frame is only the table's own row, and it fills it. */
.table-wrap .dt-container,
.table-wrap .dt-layout-row,
.table-wrap .dt-layout-cell { width: 100%; margin: 0; }
.table-wrap + .dt-layout-row { margin: .6rem 0 0; }
.dt-layout-cell.dt-end { margin-left: auto; }
.dt-container .dt-layout-row:empty { display: none; }

div.dt-search { position: relative; }
div.dt-search input.dt-input {
  width: 14rem;
  max-width: 100%;
  margin: 0;
  padding: .38rem .6rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
  color: var(--fg);
  font: inherit;
  font-size: 13px;
}
div.dt-search input.dt-input::placeholder { color: var(--faint); }

div.dt-info { color: var(--muted); font-size: 12px; }

div.dt-paging { display: flex; gap: .25rem; }
div.dt-paging button.dt-paging-button {
  min-width: 1.9rem;
  padding: .25rem .5rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--fg-dim);
  font: inherit;
  font-size: 12.5px;
}
div.dt-paging button.dt-paging-button:hover:not(.disabled) {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--fg);
}
div.dt-paging button.dt-paging-button.current {
  background: var(--primary-soft);
  border-color: var(--primary-line);
  color: #dfe9ff;
}
div.dt-paging button.dt-paging-button.disabled { opacity: .35; cursor: default; }
.grid tbody tr:hover td { background: #ffffff05; }
.grid td.right, .grid th.right { text-align: right; }
.grid td.tight { width: 1%; white-space: nowrap; }
.grid .row-actions { display: flex; gap: .3rem; justify-content: flex-end; opacity: .35; transition: opacity .12s ease; }
.grid tr:hover .row-actions { opacity: 1; }
.grid th.sortable { cursor: pointer; }
.grid th.sortable::after { content: " ↕"; color: var(--faint); }
.grid th.sorted-asc::after { content: " ↑"; color: var(--primary-hover); }
.grid th.sorted-desc::after { content: " ↓"; color: var(--primary-hover); }

.facts { display: grid; grid-template-columns: 9.5rem minmax(0, 1fr); gap: .45rem 1rem; margin: .8rem 0; font-size: 13px; }
.facts dt { color: var(--muted); font-size: 12px; }
.facts dd { margin: 0; min-width: 0; }

.pagination { display: flex; align-items: center; gap: .3rem; margin-top: .8rem; }
.pagination .page {
  min-width: 26px;
  padding: .18rem .45rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--fg-dim);
  font-size: 12px;
  text-align: center;
}
.pagination .page:hover { border-color: #3a4557; text-decoration: none; }
.pagination .page.is-current { background: var(--primary-soft); border-color: var(--primary-line); color: #dfe9ff; }
.pagination .spacer { flex: 1; }

/* ========================================================================
   11. Messages
   ==================================================================== */

.notice, .error, .alert {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  margin: 0 0 1rem;
  padding: .6rem .8rem;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 13px;
}
.notice { border-left-color: var(--primary); background: var(--primary-soft); }
.alert.ok { border-left-color: var(--ok); background: var(--ok-soft); }
.alert.warn { border-left-color: var(--warn); background: var(--warn-soft); }
.alert.bad, .error { border-left-color: var(--bad); background: var(--bad-soft); color: var(--fg); }
.alert strong, .error strong, .notice strong { font-weight: 500; }
.alert .grow, .notice .grow { flex: 1; min-width: 0; }

.empty {
  display: grid;
  place-items: center;
  gap: .4rem;
  padding: 2.5rem 1rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  text-align: center;
}
.empty h3 { margin: 0; color: var(--fg-dim); font-size: 13.5px; }
.empty p { margin: 0; font-size: 13px; max-width: 30rem; }

/* ========================================================================
   12. Tabs
   ==================================================================== */

.tabs { display: flex; gap: .15rem; border-bottom: 1px solid var(--line); margin-bottom: 1rem; }
.tab {
  padding: .45rem .75rem;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
}
.tab:hover { color: var(--fg); text-decoration: none; }
.tab.is-active { color: var(--fg); border-bottom-color: var(--primary-hover); }
.tab .count { margin-left: .35rem; color: var(--faint); font-size: 11px; }

/* ========================================================================
   13. Progress, meters, skeletons
   ==================================================================== */

.meter { height: 6px; border-radius: 999px; background: var(--sunken); overflow: hidden; }
.meter > span { display: block; height: 100%; border-radius: 999px; background: var(--primary); }
.meter.ok > span { background: var(--ok); }
.meter.warn > span { background: var(--warn); }
.meter.bad > span { background: var(--bad); }
.meter-row { display: grid; grid-template-columns: 1fr auto; gap: .2rem .8rem; align-items: center; font-size: 12.5px; }
.meter-row .meter { grid-column: 1 / -1; }

.skeleton {
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--surface-2) 25%, #2a3240 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease infinite;
  height: .9rem;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* ========================================================================
   14. Charts — plain SVG, no library
   ==================================================================== */

.chart { display: block; width: 100%; overflow: visible; }
.chart .axis { stroke: var(--line-soft); stroke-width: 1; }
.chart .grid-line { stroke: var(--line-soft); stroke-width: 1; stroke-dasharray: 2 4; }
.chart .line { fill: none; stroke: var(--primary-hover); stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; }
.chart .line.alt { stroke: var(--info); }
.chart .area { fill: url(#areaFill); }
.chart .bar { fill: var(--primary); }
.chart .bar.alt { fill: #2f3846; }
.chart .tick { fill: var(--muted); font-size: 9.5px; font-family: var(--mono); }
.chart .point { fill: var(--primary-hover); }
.chart .donut-track { fill: none; stroke: var(--surface-2); stroke-width: 10; }
.chart .donut-value { fill: none; stroke: var(--primary); stroke-width: 10; stroke-linecap: round; }
.chart .donut-value.ok { stroke: var(--ok); }
.chart .donut-value.warn { stroke: var(--warn); }
.chart-legend { display: flex; flex-wrap: wrap; gap: .25rem 1rem; margin-top: .6rem; font-size: 11.5px; color: var(--muted); }
.chart-legend span { display: inline-flex; align-items: center; gap: .35rem; }
.chart-legend i { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

.sparkline { height: 34px; }

/* ========================================================================
   15. Console and logs
   ==================================================================== */

.window {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--sunken);
  box-shadow: var(--shadow);
}
.window-bar {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem .7rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  color: var(--muted);
}
.window-bar .lights { display: flex; gap: .3rem; }
.window-bar .lights i { width: 9px; height: 9px; border-radius: 50%; background: var(--line); display: block; }
.window-bar .title { font-family: var(--mono); font-size: 11.5px; color: var(--fg-dim); }
.window-bar .grow { flex: 1; }
.window-body { padding: .7rem .8rem; }

.log, .terminal {
  margin: 0;
  padding: .7rem .8rem;
  background: var(--sunken);
  color: #cfd6e2;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow: auto;
  height: min(62vh, 46rem);
}
.log { border: 1px solid var(--line); border-radius: var(--radius); }
.terminal { height: 60vh; min-height: 20rem; border: 1px solid var(--line); border-radius: var(--radius); }
.window .log, .window .terminal { border: 0; border-radius: 0; }

.log .ts { color: var(--faint); }
.log .ok { color: var(--ok); }
.log .warn { color: var(--warn); }
.log .bad { color: var(--bad); }
.log .info { color: var(--info); }
.log .cmd { color: #cbb2ff; }

pre.code {
  margin: 0;
  padding: .7rem .8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--sunken);
  color: #cfd6e2;
  font-size: 12px;
  overflow-x: auto;
}

/* ========================================================================
   16. Timeline — a job, step by step
   ==================================================================== */

.timeline { list-style: none; margin: 0; padding: 0 0 0 1.1rem; border-left: 1px solid var(--line); }
.timeline li { position: relative; padding: 0 0 .9rem .9rem; font-size: 13px; }
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before {
  content: "";
  position: absolute;
  left: -1.42rem;
  top: .35rem;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--line);
}
.timeline li.ok::before { border-color: var(--ok); background: var(--ok); }
.timeline li.bad::before { border-color: var(--bad); background: var(--bad); }
.timeline li.now::before { border-color: var(--primary-hover); background: var(--primary-hover); }
.timeline li.todo { color: var(--faint); }
.timeline .when { color: var(--faint); font-family: var(--mono); font-size: 11px; }

/* ========================================================================
   17. Overlays — dropdown, modal, toast
   ==================================================================== */

.menu {
  min-width: 11rem;
  padding: .3rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}
.menu a, .menu button {
  display: flex;
  width: 100%;
  align-items: center;
  /* ⚠️ Both, explicitly. The base `button` rule centres its content, and a
     menu item that inherits that reads as a heading rather than a choice —
     which is how it looked the first time this was opened in a browser. */
  justify-content: flex-start;
  text-align: left;
  white-space: normal;
  gap: .5rem;
  padding: .35rem .5rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--fg-dim);
  font-size: 13px;
  font-weight: 400;
  text-align: left;
}
.menu a:hover, .menu button:hover { background: var(--surface-2); color: var(--fg); text-decoration: none; }
.menu hr { border: 0; border-top: 1px solid var(--line-soft); margin: .3rem 0; }
/* A menu item that has to be a form (it posts) still has to sit in the column
   like the others. */
.menu form { display: block; width: 100%; }
.menu .danger { color: var(--bad); }
.menu button[disabled] { opacity: .45; cursor: not-allowed; }
/* An item that cannot be used still occupies its own row, with the reason on
   hover — a disabled button receives no pointer events, so the tip goes on a
   wrapper (RULE-16). */
.menu .tip { display: block; width: 100%; }
/* ⚠️ Beside the menu, not above the item. A dropdown is a stack of rows a few
   pixels apart, so a tip above one covers the one before it — which is how it
   looked the first time this was opened in a browser. */
.menu .tip::after {
  top: 50%;
  right: calc(100% + .5rem);
  bottom: auto;
  left: auto;
  translate: 0 -50%;
}

/* ⛔ A row's actions are a dropdown, never a modal. A modal dims the page and
   takes over the keyboard to ask "which of these three", which is the wrong
   weight for the question — and it covers the row the menu is about (RULE-16).
   `popover` rather than an absolutely positioned div: the menu renders in the
   top layer, so `.table-wrap`'s `overflow-x: auto` cannot clip it, and light
   dismiss, Escape and returning focus to the trigger come from the browser.
   ⚠️ The UA stylesheet centres a popover in the viewport with `inset: 0;
   margin: auto`. Both have to go, or `top`/`left` are ignored. */
.menu[popover] {
  position: fixed;
  inset: auto;
  margin: 0;
  padding: .3rem;
  overflow: visible;
  color: inherit;
}
.menu[popover]::backdrop { background: transparent; }

/* A button says what it does; the sentence explaining why belongs on hover.
   Paragraphs beside every button made the actions card read as documentation
   with controls buried in it (RULE-16). */
.tip { position: relative; }
.tip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + .4rem);
  left: 50%;
  translate: -50% 0;
  z-index: 40;
  width: max-content;
  max-width: 17rem;
  padding: .35rem .55rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  box-shadow: var(--shadow-lg);
  color: var(--fg-dim);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  opacity: 0;
  pointer-events: none;
  transition: opacity .1s ease-out .3s;
}
.tip:hover::after, .tip:focus-visible::after { opacity: 1; }

/* ⛔ A closed dialog stays closed, whatever classes it carries.
   The browser hides one with `dialog:not([open]) { display: none }` — but that
   is the UA stylesheet, and ANY author rule setting `display` on the element
   beats it. Giving a dialog `class="sheet actions"` was enough: `.actions` is a
   flex utility, so the dialog rendered inline, permanently open, on the page.
   This restores the intent as an author rule, where a class collision cannot
   reach past it (RULE-16). */
dialog:not([open]) { display: none; }

/* A native <dialog> reuses the .modal box; the browser supplies the backdrop,
   Escape, focus trapping and inertness of the page behind it (RULE-16). */
dialog.sheet {
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  max-width: min(30rem, calc(100vw - 2rem));
  width: 100%;
}
dialog.sheet::backdrop { background: #05070ac2; }
/* A form with a dozen fields needs the room; a confirmation does not. */
dialog.sheet.wide { max-width: min(46rem, calc(100vw - 2rem)); }
/* A row's actions: one trigger, a short list. Narrow, because it holds verbs
   rather than a form (RULE-16). */
/* ⚠️ A tall dialog must scroll inside itself, not push the page: the browser
   centres it and the top would otherwise be unreachable. */
dialog.sheet .modal { max-height: calc(100vh - 4rem); overflow-y: auto; }
dialog.sheet .modal { max-width: none; }

/* ⛔ The title and the buttons stay put while the body scrolls.
   The New stack form is taller than a laptop screen, and with the whole panel
   scrolling as one it opened with no heading in sight and with `Create stack`
   below the fold — a form that looks unsubmittable. Only the dialog's own
   title is pinned: that is `> h3`, and a heading *within* a dialog is an
   `<h4>` for exactly this reason (RULE-16). */
dialog.sheet .modal > h3,
dialog.sheet .modal > .modal-foot {
  position: sticky;
  z-index: 1;
  /* Bleed back over the panel's padding, or the scrolling body shows through
     in the gutters either side of a pinned bar. */
  margin-inline: calc(var(--modal-pad) * -1);
  padding-inline: var(--modal-pad);
  background: var(--surface);
}
dialog.sheet .modal > h3 {
  top: calc(var(--modal-pad) * -1);
  margin: calc(var(--modal-pad) * -1) calc(var(--modal-pad) * -1) .9rem;
  padding-block: var(--modal-pad) .6rem;
  border-bottom: 1px solid var(--line);
}
dialog.sheet .modal > .modal-foot {
  bottom: calc(var(--modal-pad) * -1);
  margin-bottom: calc(var(--modal-pad) * -1);
  padding-block: .8rem var(--modal-pad);
  border-top: 1px solid var(--line);
}
dialog.sheet[open] { animation: sheet-in .12s ease-out; }
@keyframes sheet-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

.modal-backdrop {
  position: relative;
  display: grid;
  place-items: center;
  padding: 2rem 1rem;
  border-radius: var(--radius);
  background: #05070ac2;
}
.modal {
  --modal-pad: 1.2rem;
  width: 100%;
  max-width: 27rem;
  background: var(--card-sheen), var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--modal-pad);
}
.modal h3 { margin: 0 0 .4rem; font-size: 15px; color: var(--fg); }
/* A heading inside a dialog, below its title. Same size — the difference is
   which one is pinned to the top of the panel, not how loud it is. */
.modal h4 { margin: 1.3rem 0 .4rem; font-size: 15px; color: var(--fg); }
.modal h4:first-child { margin-top: 0; }
.modal .modal-foot { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.1rem; }

.toast-stack { display: flex; flex-direction: column; gap: .5rem; }
.toast {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  padding: .6rem .75rem;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
  font-size: 13px;
}
.toast.ok { border-left-color: var(--ok); }
.toast.bad { border-left-color: var(--bad); }
.toast.info { border-left-color: var(--info); }
.toast .grow { flex: 1; min-width: 0; }
.toast small { display: block; color: var(--muted); font-size: 11.5px; }

/* ========================================================================
   18. Odds and ends carried over from the first pass
   ==================================================================== */

.head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: 1rem; }
.actions form { display: flex; align-items: center; gap: .5rem; }
.actions.row { display: flex; }
.links { padding-left: 1.1rem; margin: 1rem 0; }
.links li { margin: .35rem 0; }

.stack { display: flex; flex-direction: column; gap: 1rem; }
.row { display: flex; align-items: center; gap: .5rem; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1; min-width: 0; }
.right { margin-left: auto; }
.nowrap { white-space: nowrap; }

/* ========================================================================
   19. The /ui reference page
   ==================================================================== */

.kit-section { margin-bottom: 2.5rem; scroll-margin-top: calc(var(--topbar-h) + 1rem); }
.kit-section > h2 {
  margin: 0 0 .2rem;
  font-size: 15px;
  font-weight: 500;
}
.kit-note { margin: 0 0 1rem; color: var(--muted); font-size: 13px; max-width: 46rem; }
.kit-demo {
  padding: 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-soft);
}
.kit-demo + .kit-demo { margin-top: .8rem; }
.kit-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.kit-toc { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: 1.8rem; }
.kit-toc a {
  padding: .2rem .55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--fg-dim);
  font-size: 12px;
}
.kit-toc a:hover { border-color: var(--primary-line); color: var(--fg); text-decoration: none; }

.swatches { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); }
.swatch { border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; font-size: 11px; }
.swatch .chip { height: 42px; }
.swatch .meta { padding: .3rem .45rem; background: var(--surface); }
.swatch .meta code { color: var(--muted); font-size: 10.5px; }

.icon { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.icon.sm { width: 13px; height: 13px; }

/* ========================================================================
   20. Signed out — login and step-up
   ==================================================================== */

.auth-card { width: 100%; max-width: 22.5rem; padding: 1.6rem 1.5rem 1.5rem; }
.auth-head { display: flex; align-items: center; gap: .65rem; margin-bottom: .3rem; }
.auth-head .brand-mark { width: 30px; height: 30px; font-size: 12px; }
.auth-head h1 { margin: 0; font-size: 17px; }
.auth-card > .lede { margin: 0 0 1.3rem; }
.auth-card .field:last-of-type { margin-bottom: 1.2rem; }
.auth-foot {
  margin: 1.2rem -1.5rem -1.5rem;
  padding: .7rem 1.5rem;
  border-top: 1px solid var(--line-soft);
  color: var(--faint);
  font-size: 11.5px;
}

/* ========================================================================
   21. Utilities — because the CSP forbids style attributes
   ==================================================================== */

/* ⛔ `style-src 'self'` blocks inline style attributes, not just <style>
   blocks. A template that reaches for style="" gets silently ignored markup:
   an absolutely-positioned element lands back in flow, a meter renders full.
   Everything that would have been inline lives here instead, and a test
   fails the build if a template grows a style attribute again. */

.sprite { position: absolute; width: 0; height: 0; }

.mt-xs { margin-top: .4rem; }
.mt-s { margin-top: .7rem; }
.mt-1 { margin-top: 1rem; }
.mt-2 { margin-top: 1.6rem; }
.mb-0 { margin-bottom: 0; }
.mb-s { margin-bottom: .6rem; }
.mb-1 { margin-bottom: 1rem; }
.my-1 { margin: .9rem 0; }
.center { justify-content: center; }
.of { font-size: 14px; }

/* Meter and skeleton widths in five-per-cent steps. A meter is read at a
   glance and the exact figure is spelled out beside it, so the rounding costs
   nothing and keeps every width out of the markup. */
.w-0 { width: 0; }
.w-5 { width: 5%; }
.w-10 { width: 10%; }
.w-15 { width: 15%; }
.w-20 { width: 20%; }
.w-25 { width: 25%; }
.w-30 { width: 30%; }
.w-35 { width: 35%; }
.w-40 { width: 40%; }
.w-45 { width: 45%; }
.w-50 { width: 50%; }
.w-55 { width: 55%; }
.w-60 { width: 60%; }
.w-65 { width: 65%; }
.w-70 { width: 70%; }
.w-75 { width: 75%; }
.w-80 { width: 80%; }
.w-85 { width: 85%; }
.w-90 { width: 90%; }
.w-95 { width: 95%; }
.w-100 { width: 100%; }

.chip.t-bg { background: var(--bg); }
.chip.t-bg-soft { background: var(--bg-soft); }
.chip.t-surface { background: var(--surface); }
.chip.t-surface-2 { background: var(--surface-2); }
.chip.t-sunken { background: var(--sunken); }
.chip.t-primary { background: var(--primary); }
.chip.t-ok { background: var(--ok); }
.chip.t-warn { background: var(--warn); }
.chip.t-bad { background: var(--bad); }
.chip.t-info { background: var(--info); }

.chart-legend i.t-primary { background: var(--primary-hover); }
.chart-legend i.t-info { background: var(--info); }
.chart-legend i.t-warn { background: var(--warn); }
.chart-legend i.t-muted { background: var(--surface-2); border: 1px solid var(--line); }

.chart.donut { max-width: 11rem; margin: 0 auto; }
.log.h-sm, .terminal.h-sm { height: 12rem; }
.log.h-md, .terminal.h-md { height: 15rem; }
.menu.narrow { max-width: 13rem; }

/* Long machine-written strings — a repository URL, an image reference — must
   wrap rather than push the card open. */
.break { overflow-wrap: anywhere; }
.facts dd { overflow-wrap: anywhere; }
.grid td.mono { overflow-wrap: anywhere; }

.window-bar .tools { display: flex; align-items: center; gap: .3rem; }
.window-bar .tools button { padding: .15rem .45rem; font-size: 11.5px; }
