/* ── Palette: light ──────────────────────────────────────────────────
   Theme: an instrument panel. Graphite chrome, a cool grey canvas, white panels
   for data, one blue signal for "you can act here" (links, primary buttons, the
   current nav item), and colour otherwise reserved for status — on this panel,
   colour is information, not mood.

   Status colours come in four parts each: -bg / -ink for badges and banners
   (text pairs, 4.5:1), -rule for their border, -edge for solid marks (heartbeat
   cells, card edges; non-text, 3:1 against the panel).

   Every foreground/background pair actually used together is checked against
   WCAG AA in test/assets/color_contrast_test.rb. Run it before changing a value.
   This file has exactly three :root blocks — light palette, dark palette, then
   theme-independent tokens — and no brace may appear inside their comments (the
   tests read the blocks with a simple pattern). */
:root {
  --canvas: #edf0f3;
  --panel: #ffffff;
  --panel-sunken: #f4f6f8;
  --panel-hover: #f7f9fb;
  --ink: #14181d;
  --ink-muted: #58616c;
  --rule: #d5dbe1;
  --rule-strong: #b3bcc6;

  --signal: #2e5bda;
  --signal-fill: #2e5bda;
  --signal-fill-hover: #2349b8;
  --signal-ink: #ffffff;
  --signal-tint: #e8eefc;
  --signal-tint-ink: #1d3fa8;
  --focus: #2e5bda;

  /* The sidebar and the narrow-screen top bar: graphite in both themes. */
  --side-bg: #14181d;
  --side-ink: #c9d1da;
  --side-muted: #8a95a3;
  --side-hover: #1f252c;
  --side-current-bg: #2a313a;
  --side-current-ink: #ffffff;
  --side-accent: #6f93ff;
  --side-rule: #262c34;

  /* The logo: a blue tile, a white plate and a gold cord. The tile has to hold
     against both the graphite sidebar and the light canvas (non-text, 3:1). */
  --mark-tile: #2e5bda;
  --mark-on: #ffffff;
  --mark-gold: #ffc53d;

  /* Machine output (action runs, config errors, hook scripts): dark in both themes. */
  --output-bg: #0e1116;
  --output-ink: #e6eaef;
  --output-muted: #9aa4af;
  --output-rule: #262c34;

  /* Log viewer: level colours, keyword highlight and per-host tags, all on the
     output background. Host tags only tell hosts apart, so they avoid the
     status hues' meanings (no plain green/red). */
  --log-error: #ff9b94;
  --log-warn: #f2c66d;
  --log-mark-bg: #f2c66d;
  --log-mark-ink: #14181d;
  --log-host-0: #7cc4ff;
  --log-host-1: #c3a6ff;
  --log-host-2: #6ee7d2;
  --log-host-3: #ffb47a;
  --log-host-4: #f7a1c4;
  --log-host-5: #d2e58a;

  --field-bg: #ffffff;
  --field-ink: #14181d;
  --field-rule: #aab4bf;

  --btn-bg: #ffffff;
  --btn-ink: #14181d;
  --btn-rule: #c3cbd4;
  --btn-hover: #f2f4f7;

  --danger-ink: #b42318;
  --danger-rule: #e5a29b;
  --danger-tint: #fdecea;

  --ok-bg: #e2f3e9;
  --ok-ink: #155c36;
  --ok-rule: #a8d8bb;
  --ok-edge: #2c9a5a;

  --drift-bg: #fdf0da;
  --drift-ink: #8a4a00;
  --drift-rule: #efc27a;
  --drift-edge: #b26e0a;

  --unhealthy-bg: #fce9e9;
  --unhealthy-ink: #a3191f;
  --unhealthy-rule: #f0b3b3;
  --unhealthy-edge: #d23a3a;

  --unreachable-bg: #e3e7ec;
  --unreachable-ink: #3d4652;
  --unreachable-rule: #c0c8d1;
  --unreachable-edge: #5e6876;

  --unknown-bg: #eef1f4;
  --unknown-ink: #58616c;
  --unknown-rule: #d5dbe1;
  --unknown-edge: #aab3bd;

  /* A heartbeat cell with no round recorded yet. */
  --hb-empty: #e1e6eb;

  /* Metrics chart lines, on the panel (non-text, 3:1). They only tell series apart, so
     they keep clear of the status hues: no plain green, amber or red. */
  --chart-1: #2b63d9;
  --chart-2: #8b4fd6;
  --chart-3: #0d8580;
  --chart-4: #c03a7d;
  --chart-5: #6f7a00;
  --chart-6: #9c5a2e;

  /* Elevation is by role, not decoration: panels and cards sit flat on the canvas;
     only things that float above the page (menus, dialogs, the drawer) cast a shadow. */
  --ring: 0 0 0 3px rgba(46, 91, 218, 0.3);
  --shadow-pop: 0 10px 28px rgba(20, 24, 29, 0.16), 0 1px 3px rgba(20, 24, 29, 0.08);
  --shadow-lift: 0 2px 6px rgba(20, 24, 29, 0.08);
  --scrim: rgba(10, 12, 15, 0.5);
}

/* ── Palette: dark ───────────────────────────────────────────────────
   Not an inversion of the light one: surfaces step up in brightness instead of
   relying on shadows (which barely show on dark), and status colours are
   re-tuned so their -ink stays readable on their own dark -bg. */
@media (prefers-color-scheme: dark) {
  :root {
    --canvas: #0f1216;
    --panel: #171b21;
    --panel-sunken: #1c2128;
    --panel-hover: #1e232a;
    --ink: #e6eaef;
    --ink-muted: #9aa4af;
    --rule: #2a3038;
    --rule-strong: #3e4651;

    --signal: #86a8ff;
    --signal-fill: #3b66dd;
    --signal-fill-hover: #2f58c7;
    --signal-ink: #ffffff;
    --signal-tint: #17233f;
    --signal-tint-ink: #a9c1ff;
    --focus: #86a8ff;

    --side-bg: #0b0d10;
    --side-ink: #c9d1da;
    --side-muted: #7d8894;
    --side-hover: #161a20;
    --side-current-bg: #1f252d;
    --side-current-ink: #ffffff;
    --side-accent: #86a8ff;
    --side-rule: #1c2127;

    --mark-tile: #3b66dd;
    --mark-on: #ffffff;
    --mark-gold: #ffd166;

    --output-bg: #0a0c0f;
    --output-ink: #e6eaef;
    --output-muted: #9aa4af;
    --output-rule: #262c34;

    /* Log viewer: level colours, keyword highlight and per-host tags, all on the
       output background. Host tags only tell hosts apart, so they avoid the
       status hues' meanings (no plain green/red). */
    --log-error: #ff9b94;
    --log-warn: #f2c66d;
    --log-mark-bg: #f2c66d;
    --log-mark-ink: #14181d;
    --log-host-0: #7cc4ff;
    --log-host-1: #c3a6ff;
    --log-host-2: #6ee7d2;
    --log-host-3: #ffb47a;
    --log-host-4: #f7a1c4;
    --log-host-5: #d2e58a;

    --field-bg: #12161b;
    --field-ink: #e6eaef;
    --field-rule: #4a535e;

    --btn-bg: #1c2128;
    --btn-ink: #e6eaef;
    --btn-rule: #3a424c;
    --btn-hover: #232932;

    --danger-ink: #f49b95;
    --danger-rule: #6e2e2a;
    --danger-tint: #3a1a1c;

    --ok-bg: #13281d;
    --ok-ink: #7fd3a2;
    --ok-rule: #24503a;
    --ok-edge: #3fb872;

    --drift-bg: #33260f;
    --drift-ink: #f2bd6b;
    --drift-rule: #5c4519;
    --drift-edge: #e0a23a;

    --unhealthy-bg: #3a1a1c;
    --unhealthy-ink: #f6a6a6;
    --unhealthy-rule: #6a2c2f;
    --unhealthy-edge: #ef5b5b;

    --unreachable-bg: #262c34;
    --unreachable-ink: #bcc4ce;
    --unreachable-rule: #3a424c;
    --unreachable-edge: #8c97a5;

    --unknown-bg: #1e232a;
    --unknown-ink: #9aa4af;
    --unknown-rule: #2a3038;
    --unknown-edge: #4a525c;

    --hb-empty: #262c34;

    /* Metrics chart lines, on the panel (non-text, 3:1). They only tell series apart, so
       they keep clear of the status hues: no plain green, amber or red. */
    --chart-1: #7aa2ff;
    --chart-2: #c29bff;
    --chart-3: #45c7bd;
    --chart-4: #f283b8;
    --chart-5: #c6cf5a;
    --chart-6: #e0a07a;

    --ring: 0 0 0 3px rgba(134, 168, 255, 0.35);
    --shadow-pop: 0 10px 28px rgba(0, 0, 0, 0.55), 0 1px 3px rgba(0, 0, 0, 0.4);
    --shadow-lift: 0 2px 6px rgba(0, 0, 0, 0.4);
    --scrim: rgba(0, 0, 0, 0.6);
  }
}

/* ── Theme-independent tokens ────────────────────────────────────────
   Typography, shape, spacing. CJK typefaces are listed explicitly: without them,
   machines lacking PingFang fall back to a face that does not match the Latin
   one, and line height and weight go off. Monospace is for machine data only
   (versions, hosts, fingerprints, output) — never for decoration. */
:root {
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
             "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
               "Liberation Mono", monospace;

  /* Type scale: five steps, adjacent ratios at least 1.2 for the voice steps
     (24 / 20 / 16); 14 and 12 are told apart by context, not size. Plus one
     display step used only for the big numbers in the status summary strip. */
  --text-display: 1.75rem;  /* 28px status-strip counts */
  --text-2xl:  1.5rem;      /* 24px page title, one per page */
  --text-xl:   1.25rem;     /* 20px dialog titles */
  --text-base: 1rem;        /* 16px section titles, app names */
  --text-sm:   0.875rem;    /* 14px body, tables, buttons */
  --text-xs:   0.75rem;     /* 12px table headers, badges, captions */

  /* Radius varies by role, so not every block reads as the same card:
     controls and panels are near-square (instruments), status badges are small
     tags rather than pills, only floating layers (dialogs, menus) get rounder. */
  --radius-cell:    0.0625rem;  /* heartbeat cells */
  --radius-tag:     0.25rem;    /* status badges, counters */
  --radius-control: 0.375rem;   /* buttons, fields */
  --radius-panel:   0.375rem;   /* panels, app cards, the summary strip */
  --radius-float:   0.625rem;   /* dialogs, menus, the sign-in card */
  --radius-focus:   0.1875rem;  /* focus outline on bare elements */
  --radius-dot:     999px;      /* sentinel, not a size: a fully round end */

  --hairline: 1px;   /* every border: a divider, not content, so it does not scale */
  --edge: 3px;       /* status edge on app cards, current-tab and current-nav markers */
  --focus-width: 2px;

  /* Spacing, named by relationship:
     label-to-control < between groups < between sections. */
  --space-xxs: 0.125rem;
  --space-xs:  0.25rem;
  --space-sm:  0.5rem;
  --space-md:  0.75rem;
  --space-lg:  1rem;
  --space-xl:  1.5rem;
  --space-xxl: 2rem;

  --sidebar-width: 14.5rem;
  --page-max: 96rem;
  --form-max: 48rem;

  /* Two control heights, switched as a whole with their font size and padding. */
  --control-md: 2.25rem;  /* 36px forms, dialogs, page-level actions */
  --control-sm: 1.75rem;  /* 28px in-row actions, toolbars */

  --icon-md: 1rem;
  --icon-sm: 0.875rem;

  --heartbeat-height: 1.25rem;
  --heartbeat-gap: 0.125rem;
}

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

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

/* The UA's display: none for [hidden] loses to any author display rule (e.g. the
   inline-flex on buttons below), so el.hidden = true would silently fail. */
[hidden] {
  display: none !important;
}

:focus-visible {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-width);
  border-radius: var(--radius-focus);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
    animation: none !important;
  }
}

h1 {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: 650;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

h2 {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1.4;
}

h3 {
  margin: var(--space-lg) 0 var(--space-sm);
  font-size: var(--text-sm);
  font-weight: 600;
}

p {
  max-width: 46rem;
}

a {
  color: var(--signal);
  font-weight: 500;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

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

/* Inline code scales with its parent: monospace runs visually larger than the UI face. */
code {
  font-size: 0.875em;
}

pre {
  overflow-x: auto;
}

pre code {
  font-size: var(--text-xs);
  line-height: 1.6;
}

.icon {
  display: inline-block;
  flex: none;
  vertical-align: -0.125em;
}

.icon-md {
  width: var(--icon-md);
  height: var(--icon-md);
}

.icon-sm {
  width: var(--icon-sm);
  height: var(--icon-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── The mark ──────────────────────────────────────────────────────── */
/* A graphic, not text: block-level so it centres in a flex row instead of
   sitting on the glyph baseline. */
.mark {
  display: block;
  flex: none;
}

/* While collecting: the knot slides along the cord, like the instrument in use.
   The tile is the reference and stays still. */
.mark--reading .mark-knot {
  animation: mark-reading 1.6s ease-in-out infinite;
  transform-origin: 12px 16.6px;
}

@keyframes mark-reading {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3.4px); }
}

/* Turbo's page-transition bar, in the signal colour. */
.turbo-progress-bar {
  height: var(--edge);
  background: var(--signal-fill);
}

/* ── Shell: sidebar + content ──────────────────────────────────────── */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
}

.topbar {
  display: none;
}

.sidebar {
  position: sticky;
  top: 0;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow-y: auto;
  padding: var(--space-lg) var(--space-md);
  color: var(--side-ink);
  background: var(--side-bg);
}

.sidebar-brand,
.topbar-brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--side-current-ink);
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.sidebar-brand {
  padding: var(--space-xs) var(--space-sm) var(--space-xl);
}

.sidebar-brand:hover,
.topbar-brand:hover {
  text-decoration: none;
}

.side-nav {
  flex: 1;
}

.side-nav ul {
  display: flex;
  flex-direction: column;
  gap: var(--space-xxs);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Sentence case and muted: a group label, not a shouting eyebrow. */
.side-nav-label {
  margin: var(--space-xl) 0 var(--space-xs);
  padding: 0 var(--space-sm);
  color: var(--side-muted);
  font-size: var(--text-xs);
  font-weight: 600;
}

.side-nav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  height: var(--control-md);
  padding: 0 var(--space-sm);
  border-radius: var(--radius-control);
  color: var(--side-ink);
  font-weight: 500;
}

.side-nav a:hover {
  text-decoration: none;
  background: var(--side-hover);
  color: var(--side-current-ink);
}

/* The current section: brighter plate plus a signal bar on the left edge, so it
   does not depend on the plate's brightness alone. */
.side-nav a.is-current {
  background: var(--side-current-bg);
  color: var(--side-current-ink);
}

.side-nav a.is-current::before {
  content: "";
  position: absolute;
  left: calc(var(--space-md) * -1);
  top: var(--space-sm);
  bottom: var(--space-sm);
  width: var(--edge);
  background: var(--side-accent);
}

.side-nav-text {
  flex: 1;
}

/* A count next to a section (e.g. alerts firing). */
.side-nav-badge {
  min-width: 1.25rem;
  padding: 0 var(--space-xs);
  border-radius: var(--radius-tag);
  background: var(--unhealthy-edge);
  color: var(--signal-ink);
  font-size: var(--text-xs);
  font-weight: 700;
  text-align: center;
}

.sidebar :focus-visible {
  outline-color: var(--side-accent);
}

.sidebar-user {
  margin-top: var(--space-xl);
  padding: var(--space-md) var(--space-sm) 0;
  border-top: var(--hairline) solid var(--side-rule);
}

/* The account menu: one trigger row (avatar, name, role) that opens a popover upward, holding the
   email, the language choice and sign out. Same graphite palette as the sidebar it grows from. */
.account-menu {
  position: relative;
}

.account-trigger {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  color: var(--side-ink);
  cursor: pointer;
  list-style: none;
}

.account-trigger::-webkit-details-marker {
  display: none;
}

.account-trigger:hover,
.account-menu[open] .account-trigger {
  background: var(--side-hover);
}

.account-avatar {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-dot);
  color: var(--side-bg);
  background: var(--side-accent);
  font-size: var(--text-xs);
  font-weight: 700;
}

.account-text {
  display: grid;
  flex: 1;
  min-width: 0;
  line-height: 1.25;
}

.account-name {
  overflow: hidden;
  color: var(--side-current-ink);
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-role {
  color: var(--side-muted);
  font-size: var(--text-xs);
}

.account-chevron {
  flex: none;
  color: var(--side-muted);
  transition: transform 120ms ease;
}

.account-menu[open] .account-chevron {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .account-chevron {
    transition: none;
  }
}

.account-popover {
  position: absolute;
  right: 0;
  bottom: calc(100% + var(--space-xs));
  left: 0;
  z-index: 20;
  padding: var(--space-xs);
  border: var(--hairline) solid var(--side-rule);
  border-radius: var(--radius-float);
  background: var(--side-hover);
  box-shadow: var(--shadow-pop);
}

.account-email {
  margin: 0;
  padding: var(--space-sm) var(--space-sm) var(--space-xs);
  overflow: hidden;
  color: var(--side-muted);
  font-size: var(--text-xs);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-section {
  padding: var(--space-xs) 0;
  border-top: var(--hairline) solid var(--side-rule);
}

.account-label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  padding: var(--space-xs) var(--space-sm);
  color: var(--side-muted);
  font-size: var(--text-xs);
}

.account-item-form {
  margin: 0;
}

.account-item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-sm);
  width: 100%;
  height: var(--control-sm);
  padding: 0 var(--space-sm);
  border: 0;
  border-radius: var(--radius-control);
  color: var(--side-ink);
  background: transparent;
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}

.account-item:hover {
  color: var(--side-current-ink);
  background: var(--side-current-bg);
}

.account-item[aria-pressed="true"] {
  color: var(--side-current-ink);
  font-weight: 600;
}

.account-check {
  margin-left: auto;
  color: var(--side-accent);
}

.drawer-scrim {
  display: none;
}

/* ── Page skeleton ─────────────────────────────────────────────────── */
.page {
  width: 100%;
  max-width: var(--page-max);
  min-width: 0;
  padding: var(--space-xl) var(--space-xxl) 4rem;
}

.breadcrumb {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-sm);
}

.breadcrumb a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--ink-muted);
}

.breadcrumb a:hover {
  color: var(--signal);
  text-decoration: none;
}

/* Title on the left, primary actions on the right, an optional full-width row
   below (the heartbeat on the app page). A rule closes the head off from content. */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md) var(--space-xl);
  padding-bottom: var(--space-lg);
  margin-bottom: var(--space-xl);
  border-bottom: var(--hairline) solid var(--rule);
}

.page-title {
  min-width: 0;
}

/* The app's verdict sits on the title line: name and status read as one unit. */
.page-head h1:has(.page-status) {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
}

.page-status {
  display: inline-flex;
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: normal;
}

.page-sub {
  margin: var(--space-xs) 0 0;
  color: var(--ink-muted);
}

.head-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-sm);
  margin-left: auto;
}

.head-actions form {
  margin: 0;
}

/* Key facts under a title: label above value, in a row. */
.head-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-xxl);
  margin: var(--space-md) 0 0;
}

.head-facts dt {
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

.head-facts dd {
  margin: 0;
  font-weight: 500;
}

/* Always on its own row: a max-width on the item itself would clamp its flex
   basis and let it slip up beside the title, so the cap goes on the strip inside. */
.head-pulse {
  flex: 0 0 100%;
}

.head-pulse .heartbeat {
  max-width: 40rem;
}

.section {
  margin-bottom: var(--space-xl);
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-sm);
}

.section-head h2 {
  margin: 0;
}

.section-head form {
  margin: 0;
}

/* Two sections side by side on wide screens (the app page's basics and roles). */
.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: 0 var(--space-xl);
}

/* ── Panels ────────────────────────────────────────────────────────── */
.panel {
  padding: var(--space-lg) var(--space-xl);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-panel);
}

.panel > :first-child,
.panel > turbo-frame > :first-child {
  margin-top: 0;
}

.panel > :last-child,
.panel > turbo-frame > :last-child {
  margin-bottom: 0;
}

/* A panel that is mostly a table: the table runs edge to edge, its own cells
   carry the inset. Clipped so the header fill follows the corners. */
.panel-flush {
  padding: 0;
  overflow-x: auto;
}

.panel-flush > p,
.panel-flush .data-age {
  margin: 0;
  padding: var(--space-md) var(--space-xl);
}

/* Secondary material (deactivated apps): no fill, the canvas shows through. */
.panel-quiet {
  background: transparent;
}

.hint,
.warning,
.empty-hint {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.hint {
  margin: var(--space-xs) 0 0;
}

.hint-consequence {
  font-size: var(--text-xs);
  margin-top: var(--space-xs);
}

.empty-hint {
  margin: 0;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-xl) 0 var(--space-lg);
  text-align: center;
}

.empty-state .empty-icon {
  width: 2rem;
  height: 2rem;
  color: var(--ink-muted);
  opacity: 0.5;
}

/* An absent value ("not configured", "none yet"): muted, so it reads as absence. */
.placeholder,
.muted {
  color: var(--ink-muted);
}

/* Absence is words, not machine data, even inside a monospace cell. */
.placeholder {
  font-family: var(--font-ui);
}

/* A qualifier after a value ("· stale", "· 2 versions side by side"), always in the UI face. */
.suffix {
  font-family: var(--font-ui);
}

.suffix::before {
  content: "·";
  margin-inline-end: var(--space-xs);
  color: var(--ink-muted);
  font-weight: 400;
}

dl {
  margin: 0;
}

/* Label/value pairs in a panel: labels in one column, values aligned in the next. */
.facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-sm) var(--space-xl);
}

.facts dt {
  color: var(--ink-muted);
}

.facts dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* ── Buttons ───────────────────────────────────────────────────────── */
input[type="submit"],
button,
a.btn,
a.btn-primary,
a.btn-quiet,
a.btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  height: var(--control-md);
  padding: 0 var(--space-lg);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--btn-ink);
  background: var(--btn-bg);
  border: var(--hairline) solid var(--btn-rule);
  border-radius: var(--radius-control);
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

input[type="submit"]:hover,
button:hover,
a.btn:hover,
a.btn-quiet:hover,
a.btn-danger:hover {
  text-decoration: none;
  background: var(--btn-hover);
  border-color: var(--rule-strong);
}

input[type="submit"]:focus-visible,
button:focus-visible,
a.btn:focus-visible,
a.btn-primary:focus-visible,
a.btn-quiet:focus-visible,
a.btn-danger:focus-visible {
  outline: var(--focus-width) solid transparent;
  outline-offset: var(--focus-width);
  box-shadow: var(--ring);
}

input[type="submit"].btn-primary,
button.btn-primary,
a.btn-primary {
  color: var(--signal-ink);
  background: var(--signal-fill);
  border-color: var(--signal-fill);
}

input[type="submit"].btn-primary:hover,
button.btn-primary:hover,
a.btn-primary:hover {
  text-decoration: none;
  background: var(--signal-fill-hover);
  border-color: var(--signal-fill-hover);
}

/* Red text, no fill: destructive actions are marked, not shouted. */
input[type="submit"].btn-danger,
button.btn-danger,
a.btn-danger {
  color: var(--danger-ink);
  border-color: var(--danger-rule);
}

input[type="submit"].btn-danger:hover,
button.btn-danger:hover,
a.btn-danger:hover {
  background: var(--danger-tint);
  border-color: var(--danger-ink);
}

/* Compact: in-row actions, toolbars, secondary page actions. */
input[type="submit"].btn-quiet,
button.btn-quiet,
a.btn-quiet {
  height: var(--control-sm);
  padding: 0 var(--space-sm);
  color: var(--ink-muted);
  background: transparent;
  border-color: var(--rule);
  font-size: var(--text-sm);
}

input[type="submit"].btn-quiet:hover,
button.btn-quiet:hover,
a.btn-quiet:hover {
  color: var(--ink);
  background: var(--btn-hover);
}

input[type="submit"].btn-quiet.btn-danger,
button.btn-quiet.btn-danger {
  color: var(--danger-ink);
}

.action-buttons,
.form-actions,
.row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

.action-buttons form,
.row-actions form {
  margin: 0;
}

/* In a table cell the row actions hug the right edge, in one line. flex goes on
   an inner div, not the td: changing a td's display breaks column alignment. */
.row-actions {
  flex-wrap: nowrap;
  justify-content: flex-end;
}

.form-actions {
  margin-top: var(--space-lg);
}

/* ── Forms ─────────────────────────────────────────────────────────── */
form div {
  margin-bottom: var(--space-lg);
}

label {
  display: block;
  margin-bottom: var(--space-xs);
  font-weight: 600;
  font-size: var(--text-sm);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="url"],
input[type="search"],
input[type="number"],
textarea,
select {
  width: 24rem;
  max-width: 100%;
  padding: 0 var(--space-md);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--field-ink);
  background: var(--field-bg);
  border: var(--hairline) solid var(--field-rule);
  border-radius: var(--radius-control);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="url"],
input[type="search"],
input[type="number"],
select {
  height: var(--control-md);
}

/* Small counts (minutes, thresholds) don't need a full-width field. */
input[type="number"] {
  width: 8rem;
}

input::placeholder,
textarea::placeholder {
  color: var(--ink-muted);
}

input:hover:not(:disabled),
textarea:hover:not(:disabled),
select:hover:not(:disabled) {
  border-color: var(--ink-muted);
}

input:focus-visible,
textarea:focus-visible,
select:focus-visible,
select:focus {
  outline: var(--focus-width) solid transparent;
  outline-offset: var(--focus-width);
  border-color: var(--focus);
  box-shadow: var(--ring);
}

input:disabled,
textarea:disabled,
select:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Config and secrets are machine text. */
textarea {
  width: 100%;
  min-height: 8rem;
  padding: var(--space-sm) var(--space-md);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.6;
}

select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: calc(var(--icon-md) + var(--space-md) * 2);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2358616c' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-md) center;
  background-size: var(--icon-md) var(--icon-md);
}

input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--signal-fill);
}

/* Forms stay a readable width even though the page column is wide: the app
   form's own sections, and any section whose panel holds a form. */
form > .section,
form > .form-actions,
.section:has(> .panel > form) {
  max-width: var(--form-max);
}

.field-with-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin: 0;
}

.field-with-icon .icon {
  position: absolute;
  left: var(--space-md);
  color: var(--ink-muted);
  pointer-events: none;
}

.field-with-icon input[type="search"],
.field-with-icon input[type="text"] {
  width: 18rem;
  padding-left: calc(var(--icon-sm) + var(--space-md) * 2);
}

.password-setup {
  margin: 0 0 var(--space-lg);
  padding: var(--space-md) var(--space-lg);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-control);
}

.password-setup legend {
  padding: 0 var(--space-xs);
  font-size: var(--text-sm);
  font-weight: 600;
}

.password-setup > label {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-xs);
  font-weight: 400;
}

.password-setup > label input[type="radio"] {
  width: auto;
  margin: 0;
}

/* Without :has() support both fields stay visible, a safe degradation: the
   submitted password_setup decides behaviour, not what is shown. */
.password-setup-manual {
  margin-top: var(--space-md);
}

@supports selector(:has(*)) {
  .password-setup-manual {
    display: none;
  }

  .password-setup:has(input[value="manual"]:checked) .password-setup-manual {
    display: block;
  }
}

/* ── Toolbars and filters ──────────────────────────────────────────── */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-md);
}

/* The labels are for screen readers only: the placeholder and the "All
   statuses" item already say what each control is. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin: 0;
}

.section > .filter-bar {
  margin-bottom: var(--space-md);
}

.filter-bar div {
  margin: 0;
}

.filter-bar select {
  width: auto;
}

.filter-clear {
  font-size: var(--text-sm);
}

/* Two or three mutually exclusive views (cards / list). */
.segmented {
  display: inline-flex;
  padding: var(--space-xxs);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-control);
}

.segmented-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  height: calc(var(--control-sm) - var(--space-xs));
  padding: 0 var(--space-sm);
  border-radius: var(--radius-tag);
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.segmented-item:hover {
  color: var(--ink);
  text-decoration: none;
}

.segmented-item[aria-current] {
  color: var(--signal-tint-ink);
  background: var(--signal-tint);
  font-weight: 600;
}

.pager {
  margin: 0;
  padding: var(--space-md) var(--space-xl);
  border-top: var(--hairline) solid var(--rule);
  font-size: var(--text-sm);
}

/* ── Tabs ──────────────────────────────────────────────────────────── */
/* Underlined tabs on the canvas. The current tab carries a signal bar and
   aria-current; colour is never its only marker. */
.tabs {
  display: flex;
  gap: var(--space-xl);
  margin: 0 0 var(--space-xl);
  overflow-x: auto;
  border-bottom: var(--hairline) solid var(--rule);
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-sm) 0;
  margin-bottom: calc(var(--hairline) * -1);
  border-bottom: var(--edge) solid transparent;
  color: var(--ink-muted);
  font-weight: 500;
  white-space: nowrap;
}

.tab:hover {
  color: var(--ink);
  text-decoration: none;
  border-bottom-color: var(--rule-strong);
}

.tab[aria-current] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--signal-fill);
}

/* The page head's bottom rule and the tab rule would double up. */
.page-head:has(~ .tabs) {
  border-bottom: none;
  margin-bottom: var(--space-md);
}

/* ── Tables ────────────────────────────────────────────────────────── */
/* Dense and aligned: a sunken header row, hairline row rules, tabular figures.
   Headers are small and muted, not letter-spaced capitals (which do nothing for
   Chinese column names). */
table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

th,
td {
  padding: var(--space-sm) var(--space-md);
  text-align: left;
  vertical-align: top;
  border-bottom: var(--hairline) solid var(--rule);
}

th {
  color: var(--ink-muted);
  background: var(--panel-sunken);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
}

.panel-flush th:first-child,
.panel-flush td:first-child {
  padding-left: var(--space-xl);
}

.panel-flush th:last-child,
.panel-flush td:last-child {
  padding-right: var(--space-xl);
}

/* In a padded panel the table sits on the panel's own inset. */
.panel:not(.panel-flush) th:first-child,
.panel:not(.panel-flush) td:first-child {
  padding-left: 0;
}

.panel:not(.panel-flush) th:last-child,
.panel:not(.panel-flush) td:last-child {
  padding-right: 0;
}

.panel:not(.panel-flush) th {
  background: transparent;
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:hover td {
  background: var(--panel-hover);
}

th.num,
td.num {
  text-align: right;
}

.result-failure {
  color: var(--danger-ink);
}

/* Machine data reads in monospace so versions and addresses line up. */
.host-table td:first-child,
.host-table td:nth-child(3),
.overview-grid td:nth-child(3),
.deploy-history td:first-child {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

/* A host running several containers: its rows sit together, one rule around the group. */
.host-table tr:has(+ .row-same-host) td {
  border-bottom-color: transparent;
}

.host-table tr.row-unreachable td {
  color: var(--unreachable-ink);
}

.host-table tr.row-decommissioned td {
  padding-top: 0;
  color: var(--ink-muted);
}

.host-table small,
.deploy-history small {
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

/* The overview list view: a status edge on the first cell of every row. */
.overview-grid td {
  white-space: nowrap;
}

.overview-grid td:first-child {
  font-weight: 600;
  border-left: var(--edge) solid var(--unknown-edge);
}

.overview-grid tr.status-ok td:first-child          { border-left-color: var(--ok-edge); }
.overview-grid tr.status-drift td:first-child       { border-left-color: var(--drift-edge); }
.overview-grid tr.status-unhealthy td:first-child   { border-left-color: var(--unhealthy-edge); }
.overview-grid tr.status-unreachable td:first-child { border-left-color: var(--unreachable-edge); }

/* ── Status badges ─────────────────────────────────────────────────── */
/* Icon + words + tint: status is never colour alone. */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xxs) var(--space-sm);
  border: var(--hairline) solid transparent;
  border-radius: var(--radius-tag);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

.status-badge.status-ok          { color: var(--ok-ink);          background: var(--ok-bg);          border-color: var(--ok-rule); }
.status-badge.status-drift       { color: var(--drift-ink);       background: var(--drift-bg);       border-color: var(--drift-rule); }
.status-badge.status-unhealthy   { color: var(--unhealthy-ink);   background: var(--unhealthy-bg);   border-color: var(--unhealthy-rule); }
.status-badge.status-unreachable { color: var(--unreachable-ink); background: var(--unreachable-bg); border-color: var(--unreachable-rule); }
.status-badge.status-unknown     { color: var(--unknown-ink);     background: var(--unknown-bg);     border-color: var(--unknown-rule); }

/* A reconciliation alert next to an app name ("report not verified"). */
.badge,
.badge-unverified {
  display: inline-block;
  padding: 0 var(--space-sm);
  border: var(--hairline) solid var(--drift-rule);
  border-radius: var(--radius-tag);
  color: var(--drift-ink);
  background: var(--drift-bg);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.6;
}

td > .badge {
  margin-left: var(--space-xs);
}

/* ── Status summary strip (overview) ───────────────────────────────── */
/* One instrument, several read-outs: a single bar divided by rules rather than
   a row of separate cards. Each read-out is a link to that status filter. */
.status-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  margin-bottom: var(--space-xl);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-panel);
  overflow: hidden;
}

.strip-item {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-xxs);
  padding: var(--space-md) var(--space-lg);
  color: var(--ink);
  border-left: var(--hairline) solid var(--rule);
}

/* Each read-out is divided from the previous one; the first sits on the bar's own edge. */
.strip-item:first-child {
  border-left: none;
}

.strip-item:hover {
  text-decoration: none;
  background: var(--panel-hover);
}

.strip-count {
  font-size: var(--text-display);
  font-weight: 650;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.strip-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--ink-muted);
  font-size: var(--text-xs);
  font-weight: 500;
}

/* A non-zero problem count takes its status ink; zero stays quiet. */
.strip-drift:not(.is-zero) .strip-count       { color: var(--drift-ink); }
.strip-unhealthy:not(.is-zero) .strip-count   { color: var(--unhealthy-ink); }
.strip-unreachable:not(.is-zero) .strip-count { color: var(--unreachable-ink); }
.strip-ok:not(.is-zero) .strip-count          { color: var(--ok-ink); }

.strip-item.is-zero .strip-count {
  color: var(--ink-muted);
  font-weight: 500;
}

/* The active filter: a signal bar under the read-out plus aria-current. */
.strip-item[aria-current]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--edge);
  background: var(--signal-fill);
}

/* ── App cards (overview) ──────────────────────────────────────────── */
.app-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: var(--space-lg);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The status edge along the top is the card's first read; the badge says it in words. */
.app-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-top: var(--edge) solid var(--unknown-edge);
  border-radius: var(--radius-panel);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.app-card:hover {
  box-shadow: var(--shadow-lift);
}

.app-card.status-ok          { border-top-color: var(--ok-edge); }
.app-card.status-drift       { border-top-color: var(--drift-edge); }
.app-card.status-unhealthy   { border-top-color: var(--unhealthy-edge); }
.app-card.status-unreachable { border-top-color: var(--unreachable-edge); }

.app-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
}

.app-card-name {
  margin: 0;
  min-width: 0;
  font-size: var(--text-base);
  overflow-wrap: anywhere;
}

.app-card-name a {
  color: var(--ink);
  font-weight: 650;
}

.app-card-name a:hover {
  color: var(--signal);
}

.app-card-alert,
.app-card-note {
  margin: 0;
}

.app-card-note {
  color: var(--unreachable-ink);
}

.app-card-facts {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-md);
}

.app-card-facts div {
  min-width: 0;
}

.app-card-facts dt {
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

.app-card-facts dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* In the narrow card columns an inline "· 2 versions" suffix broke mid-phrase onto the next line;
   give it a line of its own instead, which also keeps the version itself on one line. */
.app-card-facts .suffix {
  display: block;
  font-size: var(--text-xs);
}

.app-card-facts .suffix::before {
  content: none;
}

.app-card-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: auto;
  padding-top: var(--space-md);
  border-top: var(--hairline) solid var(--rule);
}

/* ── Heartbeat strip ───────────────────────────────────────────────── */
/* The panel's one memorable element: the last 40 collection rounds, oldest on
   the left. Height is a second channel next to colour — a calm OK round is a
   short bar, any problem round stands full height — so a run of trouble reads
   as a spike even without colour vision. Unreachable rounds are hatched, empty
   slots (no round yet) are short and faint. */
.heartbeat-cells {
  display: flex;
  align-items: flex-end;
  gap: var(--heartbeat-gap);
  height: var(--heartbeat-height);
  margin: 0;
  padding: 0;
  list-style: none;
}

.hb-cell {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  border-radius: var(--radius-cell);
}

.hb-ok {
  height: 55%;
  background: var(--ok-edge);
}

.hb-drift {
  background: var(--drift-edge);
}

.hb-unhealthy {
  background: var(--unhealthy-edge);
}

.hb-unreachable {
  background: repeating-linear-gradient(
    135deg,
    var(--unreachable-edge) 0 0.1875rem,
    var(--unknown-edge) 0.1875rem 0.3125rem
  );
}

.hb-empty,
.hb-unknown {
  height: 55%;
  background: var(--hb-empty);
}

.hb-cell[title]:hover {
  outline: var(--hairline) solid var(--ink);
  outline-offset: var(--hairline);
}

.heartbeat-caption {
  margin: var(--space-xs) 0 0;
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

/* ── Banners: flash, callouts, errors ──────────────────────────────── */
.flash,
.callout,
.errors,
.deploy-alerts,
.config-parse-error-banner,
.hook-rejection,
.lock-held,
.lock-unknown {
  max-width: 56rem;
  margin: 0 0 var(--space-lg);
  padding: var(--space-md) var(--space-lg);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-control);
  background: var(--panel);
}

.flash {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
}

.flash .icon {
  margin-top: 0.2em;
}

.flash-notice {
  color: var(--ok-ink);
  background: var(--ok-bg);
  border-color: var(--ok-rule);
}

.flash-alert,
.errors,
.config-parse-error-banner,
.hook-rejection {
  color: var(--unhealthy-ink);
  background: var(--unhealthy-bg);
  border-color: var(--unhealthy-rule);
}

.flash-warning,
.callout-warn,
.deploy-alerts {
  color: var(--drift-ink);
  background: var(--drift-bg);
  border-color: var(--drift-rule);
}

.lock-held,
.lock-unknown {
  color: var(--unreachable-ink);
  background: var(--unreachable-bg);
  border-color: var(--unreachable-rule);
}

.errors {
  padding-left: var(--space-xl);
}

.errors > :first-child,
.config-parse-error-banner > :first-child {
  margin-top: 0;
}

.config-parse-error-banner > :last-child {
  margin-bottom: 0;
}

.deploy-alerts,
.config-parse-error-banner {
  max-width: none;
}

.deploy-alert {
  margin: 0 0 var(--space-xs);
  max-width: none;
}

.deploy-alert:last-child {
  margin-bottom: 0;
}

.config-parse-error-banner pre {
  margin: var(--space-sm) 0 0;
  padding: var(--space-md) var(--space-lg);
  color: var(--output-ink);
  background: var(--output-bg);
  border-radius: var(--radius-control);
}

/* ── App page details ──────────────────────────────────────────────── */
.data-age {
  margin: 0 0 var(--space-md);
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* While collecting: the mark and text on one line. */
.data-age--reading {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.deploy-history-hint,
.actions-hint,
.action-unavailable {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.affected-scope {
  color: var(--ink);
}

details.confirm-action {
  margin-top: var(--space-md);
}

details.confirm-action summary {
  width: fit-content;
  color: var(--danger-ink);
  font-weight: 600;
  cursor: pointer;
}

details.confirm-action[open] summary {
  margin-bottom: var(--space-sm);
}

details form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

details form div {
  margin: 0;
}

/* ── Actions tab ── one panel per group (app, rollback, accessories, proxy,
   housekeeping), lock status on top. Disabled controls stay visible: the
   reason is printed next to them, the button itself just greys out. */
.action-status {
  margin-bottom: var(--space-lg);
}

.action-status > p,
.action-status > div {
  margin: 0 0 var(--space-sm);
}

.lock-reading,
.lock-free {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.lock-held p {
  margin: 0 0 var(--space-xs);
}

pre.lock-details {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.action-group {
  margin-bottom: var(--space-lg);
  padding: var(--space-lg) var(--space-xl);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-panel);
}

.action-group-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs) var(--space-lg);
  margin-bottom: var(--space-md);
}

.action-group-head h3,
.action-group-head p {
  margin: 0;
}

.action-group > p {
  margin: 0 0 var(--space-sm);
}

/* A <fieldset disabled> greys out a whole form at once; it carries no frame of its own. */
.action-fieldset {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.action-fieldset > ol {
  flex: 0 0 100%;
}

.restart-form {
  margin: 0;
}

select.select-compact {
  width: auto;
  height: var(--control-md);
}

input[type="submit"]:disabled,
button:disabled {
  color: var(--ink-muted);
  background: var(--btn-bg);
  border-color: var(--rule);
  opacity: 0.6;
  cursor: not-allowed;
}

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

.accessory-table {
  width: 100%;
}

.accessory-table td {
  vertical-align: top;
}

.accessory-table .row-actions {
  justify-content: flex-start;
}

.accessory-table details.confirm-action {
  margin-top: var(--space-xs);
}

.app-card-foot form {
  margin: 0;
}

.rollback-steps {
  margin: 0 0 var(--space-lg);
  padding-left: var(--space-xl);
}

.rollback-steps > li {
  margin-bottom: var(--space-lg);
}

.rollback-steps label {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin: var(--space-xs) 0 0;
  font-family: var(--font-mono);
  font-weight: 400;
}

.rollback-steps label.unavailable {
  color: var(--ink-muted);
}

.rollback-steps label small {
  font-family: var(--font-ui);
}

/* Lists of records (deactivated apps). */
.record-list {
  margin: var(--space-sm) calc(var(--space-xl) * -1) calc(var(--space-lg) * -1);
  padding: 0;
  list-style: none;
}

.record-list li {
  border-top: var(--hairline) solid var(--rule);
}

.record-list a.record-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  padding: var(--space-sm) var(--space-xl);
  color: var(--ink);
  font-weight: 600;
}

.record-list a.record-row:hover {
  text-decoration: none;
  background: var(--panel-hover);
  color: var(--signal);
}

.record-meta {
  color: var(--ink-muted);
  font-weight: 400;
}

/* ── Action run page: status bar + terminal ────────────────────────── */
.terminal {
  margin-bottom: var(--space-md);
  overflow: hidden;
  background: var(--output-bg);
  border: var(--hairline) solid var(--output-rule);
  border-radius: var(--radius-panel);
}

.run-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-lg);
  padding: var(--space-sm) var(--space-lg);
  border-bottom: var(--hairline) solid transparent;
}

.run-state {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
}

.run-duration {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

.run-running     { color: var(--signal-tint-ink); background: var(--signal-tint); }
.run-success     { color: var(--ok-ink);          background: var(--ok-bg); }
.run-failure     { color: var(--unhealthy-ink);   background: var(--unhealthy-bg); }
.run-interrupted { color: var(--drift-ink);       background: var(--drift-bg); }

.spin {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.terminal-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs) var(--space-lg);
  padding: var(--space-sm) var(--space-lg);
  border-bottom: var(--hairline) solid var(--output-rule);
}

.terminal-head h2 {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  color: var(--output-muted);
  font-size: var(--text-xs);
  font-weight: 600;
}

.terminal-command {
  color: var(--output-muted);
  overflow-wrap: anywhere;
}

.action-output {
  min-height: 12rem;
  max-height: 70vh;
  margin: 0;
  padding: var(--space-md) var(--space-lg) var(--space-lg);
  overflow-y: auto;
  color: var(--output-ink);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-all;
}

.terminal-note {
  margin: 0;
}

/* ── Sign-in pages ─────────────────────────────────────────────────── */
/* The only centred layout: nothing to scan here, one thing to do. The card is
   the one place a resting surface is elevated — it floats alone on the canvas. */
.page-auth {
  display: grid;
  min-height: 100vh;
  place-items: start center;
  padding: 12vh var(--space-lg) var(--space-xxl);
  max-width: none;
}

.auth {
  width: min(100%, 25rem);
}

.auth-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  margin: 0 auto var(--space-xl);
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
}

.auth-card {
  padding: var(--space-xxl);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-float);
  box-shadow: var(--shadow-pop);
}

.auth-card h1 {
  margin: 0 0 var(--space-xl);
  font-size: var(--text-xl);
}

.auth-card input[type="text"],
.auth-card input[type="email"],
.auth-card input[type="password"],
.auth-card input[type="submit"] {
  width: 100%;
}

.auth-card input[type="submit"] {
  height: calc(var(--control-md) + var(--space-xs));
  margin-top: var(--space-xs);
  font-size: var(--text-base);
}

.auth-card .flash-alert,
.auth-card .flash-notice {
  margin: 0 0 var(--space-lg);
  padding: var(--space-sm) var(--space-md);
  border: var(--hairline) solid;
  border-radius: var(--radius-control);
}

.auth-aside {
  max-width: none;
  margin-top: var(--space-lg);
  text-align: center;
}

/* ── Custom select (select_controller) ─────────────────────────────── */
/* No margin on the wrapper: inside an align-items: center row a margin would
   push it off the shared midline. */
.sel {
  position: relative;
  display: inline-flex;
  margin: 0;
}

.sel-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  height: var(--control-md);
  min-width: 9rem;
  padding: 0 var(--space-md);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--field-ink);
  background: var(--field-bg);
  border: var(--hairline) solid var(--field-rule);
  border-radius: var(--radius-control);
  cursor: pointer;
  text-align: left;
}

.sel-trigger:hover {
  border-color: var(--ink-muted);
  background: var(--field-bg);
}

.sel-trigger:focus-visible,
.sel.is-open .sel-trigger {
  outline: var(--focus-width) solid transparent;
  outline-offset: var(--focus-width);
  border-color: var(--focus);
  box-shadow: var(--ring);
}

.sel-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sel-chevron {
  width: var(--icon-md);
  height: var(--icon-md);
  opacity: 0.6;
  transition: transform 0.15s ease;
}

.sel.is-open .sel-chevron {
  transform: rotate(180deg);
}

.sel-list {
  position: absolute;
  z-index: 50;
  top: calc(100% + var(--space-xs));
  left: 0;
  min-width: 100%;
  max-height: 18rem;
  overflow-y: auto;
  padding: var(--space-xs);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-pop);
  animation: pop-in 0.12s ease-out;
}

.sel.drops-up .sel-list {
  top: auto;
  bottom: calc(100% + var(--space-xs));
}

@keyframes pop-in {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: scale(1); }
}

.sel-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-tag);
  font-size: var(--text-sm);
  color: var(--field-ink);
  cursor: pointer;
  white-space: nowrap;
}

.sel-item.is-active {
  background: var(--panel-sunken);
}

.sel-check {
  width: var(--icon-sm);
  height: var(--icon-sm);
  opacity: 0;
  color: var(--signal);
}

.sel-item.is-selected .sel-check {
  opacity: 1;
}

/* ── Dialogs ───────────────────────────────────────────────────────── */
/* The confirmation dialog (app/javascript/confirm_dialog.js) and the one-time
   hook token dialog. */
.confirm-dialog,
.token-dialog {
  padding: var(--space-xl);
  color: var(--ink);
  background: var(--panel);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-float);
  box-shadow: var(--shadow-pop);
  animation: pop-in 0.12s ease-out;
}

.confirm-dialog {
  max-width: 28rem;
}

.confirm-dialog::backdrop,
.token-dialog::backdrop {
  background: var(--scrim);
}

.confirm-dialog h2 {
  margin: 0 0 var(--space-sm);
}

.confirm-dialog p {
  margin: 0;
  color: var(--ink-muted);
  white-space: pre-line;
}

.confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
  margin-top: var(--space-xl);
}

.token-dialog {
  width: min(48rem, calc(100vw - var(--space-xxl) * 2));
  max-height: calc(100vh - var(--space-xxl) * 2);
  overflow-y: auto;
}

.token-dialog h2 {
  margin: 0 0 var(--space-lg);
  font-size: var(--text-xl);
}

.token-dialog h3 {
  margin: var(--space-lg) 0 var(--space-sm);
}

.token-dialog .hint {
  margin: var(--space-lg) 0 0;
}

/* The one warning that must win attention between two code blocks. */
.token-warning {
  margin: 0;
  padding: var(--space-md) var(--space-lg);
  color: var(--drift-ink);
  background: var(--drift-bg);
  border: var(--hairline) solid var(--drift-rule);
  border-radius: var(--radius-control);
}

.token-scripts pre {
  margin: 0;
  padding: var(--space-md) var(--space-lg);
  color: var(--output-ink);
  background: var(--output-bg);
  border-radius: var(--radius-control);
}

.token-scripts pre code {
  color: inherit;
}

.scroll-locked {
  overflow: hidden;
  scrollbar-gutter: stable;
}

/* ── Narrow screens ────────────────────────────────────────────────── */
/* Below 900px the sidebar leaves the grid. With JS (.has-drawer) it becomes a
   drawer behind the top bar's menu button; without JS it stacks above the
   content as an ordinary block, so navigation is never unreachable. */
@media (max-width: 899.98px) {
  .shell {
    display: block;
  }

  .topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    height: 3.25rem;
    padding: 0 var(--space-lg);
    background: var(--side-bg);
  }

  .menu-button,
  .menu-button:hover {
    color: var(--side-ink);
    background: transparent;
    border-color: var(--side-rule);
  }

  .shell:not(.has-drawer) .menu-button {
    display: none;
  }

  .sidebar {
    position: static;
    height: auto;
  }

  .sidebar-brand {
    display: none;
  }

  .has-drawer .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 40;
    width: min(18rem, 85vw);
    height: 100vh;
    box-shadow: var(--shadow-pop);
    transform: translateX(-105%);
    visibility: hidden;
  }

  /* Animate the slide in only: a transition on the closed state would also play
     on page load, when .has-drawer first moves the sidebar off-canvas. */
  .has-drawer.is-drawer-open .sidebar {
    transform: none;
    visibility: visible;
    transition: transform 0.2s ease;
  }

  .drawer-scrim {
    position: fixed;
    inset: 0;
    z-index: 35;
    display: block;
    background: var(--scrim);
  }

  .page {
    padding: var(--space-lg) var(--space-lg) var(--space-xxl);
  }

  h1 {
    font-size: var(--text-xl);
  }

  .panel {
    padding: var(--space-md) var(--space-lg);
  }

  .panel:not(.panel-flush) {
    overflow-x: auto;
  }

  .panel-flush th:first-child,
  .panel-flush td:first-child,
  .panel-flush > p,
  .panel-flush .data-age {
    padding-left: var(--space-lg);
  }

  .panel-flush th:last-child,
  .panel-flush td:last-child {
    padding-right: var(--space-lg);
  }

  .record-list {
    margin-left: calc(var(--space-lg) * -1);
    margin-right: calc(var(--space-lg) * -1);
  }

  .record-list a.record-row {
    padding-left: var(--space-lg);
    padding-right: var(--space-lg);
  }

  .field-with-icon input[type="search"],
  .field-with-icon input[type="text"] {
    width: 100%;
  }

  .app-card-facts {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  .app-card-facts div:first-child {
    grid-column: 1 / -1;
  }
}

/* ── Deploys (version list, confirm page, global feed) ─────────────── */
.loading-hint {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  color: var(--ink-muted);
}

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--icon-md);
  height: var(--icon-md);
  border-radius: var(--radius-dot);
  background: var(--panel-sunken);
  color: var(--ink-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  object-fit: cover;
  vertical-align: middle;
}

.commit-author {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin-right: var(--space-xs);
}

.deploy-versions .commit-title {
  max-width: 28rem;
  overflow-wrap: anywhere;
}

.deploy-versions tr.is-blocked td {
  color: var(--ink-muted);
}

.row-blocker {
  color: var(--unhealthy-ink);
  font-size: var(--text-sm);
}

a.version-link {
  text-decoration-style: dotted;
}

.deploy-route {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-lg);
  margin-bottom: var(--space-md);
  font-size: var(--text-xl);
}

.deploy-route-label {
  display: block;
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

.deploy-route-arrow {
  color: var(--ink-muted);
}

.deploy-target-commit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

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

.commit-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-lg);
  border-bottom: var(--hairline) solid var(--rule);
}

.commit-list li:last-child {
  border-bottom: 0;
}

.warning-list {
  margin: 0;
  padding-left: var(--space-lg);
}

/* ── Logs (centralized query page, app logs tab) ───────────────────── */
/* Query form: captions above the controls, one wrapping row. */
.log-query {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-md) var(--space-lg);
  margin-bottom: var(--space-md);
}

.log-field {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.log-field > label,
.log-field > legend {
  margin: 0 0 var(--space-xs);
  padding: 0;
  color: var(--ink-muted);
  font-size: var(--text-xs);
  font-weight: 600;
}

.log-field select {
  width: auto;
  min-width: 9rem;
}

.log-field .segmented {
  align-items: center;
  height: var(--control-md);
}

/* The time window is a radio group drawn as a segmented control. */
.segmented label.segmented-item {
  position: relative;
  margin: 0;
  font-weight: 400;
  cursor: pointer;
}

.segmented-item input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.segmented-item:has(input:checked) {
  color: var(--signal-tint-ink);
  background: var(--signal-tint);
  font-weight: 600;
}

.segmented-item:has(input:focus-visible) {
  box-shadow: var(--ring);
}

.log-field-keyword {
  flex: 1 1 26rem;
}

.log-keyword {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-md);
}

.log-keyword .field-with-icon {
  flex: 1 1 14rem;
}

.log-keyword .field-with-icon input[type="search"] {
  width: 100%;
}

label.log-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  color: var(--ink-muted);
  font-weight: 400;
  white-space: nowrap;
}

.log-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-sm);
}

.log-follow-status {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.log-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-left: auto;
}

.log-toggles [aria-pressed="true"] {
  color: var(--signal-tint-ink);
  background: var(--signal-tint);
}

.log-toggles .is-on .icon {
  animation: spin 2s linear infinite;
}

.log-terminal {
  margin-bottom: 0;
}

.log-loading {
  padding: var(--space-lg);
  color: var(--output-muted);
}

.log-summary {
  color: var(--output-muted);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.log-output {
  min-height: 12rem;
  max-height: 70vh;
  padding: var(--space-sm) 0;
  overflow: auto;
  color: var(--output-ink);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.6;
}

/* time · host · source · text. The text column takes the rest and wraps. */
.log-row {
  display: grid;
  grid-template-columns: max-content max-content max-content minmax(0, 1fr);
  column-gap: var(--space-md);
  padding: 0 var(--space-lg) 0 calc(var(--space-lg) - var(--edge));
  border-left: var(--edge) solid transparent;
}

.log-row:hover {
  background: var(--output-rule);
}

.log-time {
  color: var(--output-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.log-host {
  white-space: nowrap;
}

.log-host-0 { color: var(--log-host-0); }
.log-host-1 { color: var(--log-host-1); }
.log-host-2 { color: var(--log-host-2); }
.log-host-3 { color: var(--log-host-3); }
.log-host-4 { color: var(--log-host-4); }
.log-host-5 { color: var(--log-host-5); }

.log-source {
  color: var(--output-muted);
  white-space: nowrap;
}

.log-line {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.log-row.lvl-error {
  border-left-color: var(--log-error);
}

.log-row.lvl-error .log-line {
  color: var(--log-error);
}

.log-row.lvl-warn {
  border-left-color: var(--log-warn);
}

.log-row.lvl-warn .log-line {
  color: var(--log-warn);
}

.log-line mark {
  color: var(--log-mark-ink);
  background: var(--log-mark-bg);
  border-radius: var(--radius-cell);
}

/* View toggles (log_viewer_controller). */
.log-viewer.only-problems .log-row:not(.lvl-error):not(.lvl-warn) {
  display: none;
}

.log-viewer.nowrap .log-row {
  grid-template-columns: max-content max-content max-content max-content;
}

.log-viewer.nowrap .log-line {
  white-space: pre;
  overflow-wrap: normal;
}

.log-empty {
  margin: 0;
  padding: var(--space-lg);
  color: var(--output-muted);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
}

/* Hosts that could not be read. Host chips keep the terminal's colours so a
   host looks the same here as in the lines below. */
.log-errors {
  max-width: none;
  padding-left: var(--space-lg);
  list-style: none;
}

.log-errors li + li {
  margin-top: var(--space-xs);
}

.log-errors .log-host {
  margin-right: var(--space-sm);
  padding: 0 var(--space-xs);
  background: var(--output-bg);
  border-radius: var(--radius-tag);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.log-pick h2 {
  margin: 0 0 var(--space-xs);
  font-size: var(--text-base);
}

.log-pick-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin: var(--space-md) 0 0;
  padding: 0;
  list-style: none;
}

@media (max-width: 899.98px) {
  .log-row {
    display: flex;
    flex-wrap: wrap;
    column-gap: var(--space-sm);
  }

  .log-line {
    flex-basis: 100%;
  }
}

/* ── Alerts ────────────────────────────────────────────────────────── */
/* Firing alerts lead the alerts page: one row per alert, the red edge marks it as live. */
.alert-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.alert-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-lg);
  padding: var(--space-md) var(--space-lg);
  background: var(--panel);
  border: var(--hairline) solid var(--unhealthy-rule);
  border-left: var(--edge) solid var(--unhealthy-edge);
  border-radius: var(--radius-control);
}

.alert-item-main {
  min-width: 0;
}

.alert-item-main p {
  margin: 0;
}

.alert-item-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

.alert-item-app {
  font-weight: 650;
}

.alert-item-rule,
.alert-item-meta {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.alert-item-main .alert-item-message {
  margin-top: var(--space-xs);
}

.alert-item-main .alert-item-meta {
  margin-top: var(--space-xxs);
  font-size: var(--text-xs);
}

/* The app page's banner for its own firing alerts, above the tabs. */
.firing-alerts {
  max-width: 56rem;
  margin: 0 0 var(--space-lg);
  padding: var(--space-md) var(--space-lg);
  color: var(--unhealthy-ink);
  background: var(--unhealthy-bg);
  border: var(--hairline) solid var(--unhealthy-rule);
  border-radius: var(--radius-control);
}

.firing-alerts p {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  margin: 0 0 var(--space-xs);
}

.firing-alerts p:last-child {
  margin-bottom: 0;
}

.strip-alerts .strip-count {
  color: var(--unhealthy-ink);
}

/* A checkbox and its words on one line (rule channels, "enabled"). */
label.check-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-weight: 400;
}

.field-label {
  margin: 0 0 var(--space-xs);
  font-weight: 600;
  font-size: var(--text-sm);
}

.rule-fieldset {
  max-width: 40rem;
  margin: 0 0 var(--space-lg);
  padding: var(--space-md) var(--space-lg) 0;
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius-control);
}

.rule-fieldset legend {
  padding: 0 var(--space-xs);
  font-weight: 600;
  font-size: var(--text-sm);
}

@media (max-width: 640px) {
  .alert-item {
    flex-direction: column;
  }
}

/* ── Resource metrics ──────────────────────────────────────────────── */
/* Hand-drawn SVG line charts (MetricsHelper). Lines take their colour from the
   --chart-* tokens through currentColor, so a line and its legend swatch can never
   disagree; past six series the colours repeat dashed. Gridlines are hairline rules,
   axis labels muted: the data is the only thing with colour. */
.metrics-subhead {
  margin: var(--space-xl) 0 var(--space-sm);
  font-size: var(--text-sm);
  font-weight: 600;
}

.metrics .section-head + .metrics-subhead {
  margin-top: 0;
}

.metrics-charts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: var(--space-lg);
  margin-top: var(--space-lg);
}

.metrics-host {
  margin-bottom: var(--space-lg);
}

.metrics-host-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm) var(--space-xl);
}

.metrics-host-head h4 {
  margin: 0;
  font-size: var(--text-sm);
}

.metrics-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-xl);
}

.metrics-facts dt {
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

.metrics-facts dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.metrics-table td {
  white-space: nowrap;
}

.metrics-table small {
  font-size: var(--text-xs);
}

/* Usage past 80% / 90%: the status inks, and always a word next to the number. */
.usage-warn {
  color: var(--drift-ink);
  font-weight: 600;
}

.usage-critical {
  color: var(--unhealthy-ink);
  font-weight: 600;
}

.usage-tag {
  font-size: var(--text-xs);
}

.chart {
  margin: 0;
  min-width: 0;
}

.chart-title {
  margin-bottom: var(--space-xs);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-muted);
}

/* Narrow screens scroll the chart sideways inside its own box rather than shrinking
   its labels to nothing. */
.chart-plot {
  position: relative;
  overflow-x: auto;
}

.chart-svg {
  display: block;
  width: 100%;
  min-width: 22rem;
  height: auto;
  overflow: visible;
}

.chart-grid {
  stroke: var(--rule);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.chart-axis {
  fill: var(--ink-muted);
  font-family: var(--font-ui);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.chart-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

.chart-dot {
  fill: currentColor;
}

.chart-dashed {
  stroke-dasharray: 5 3;
}

/* A reference level (a memory limit, the CPU count): dashed and neutral. */
.chart-ref {
  stroke: var(--ink-muted);
  stroke-width: 1;
  stroke-dasharray: 4 3;
  vector-effect: non-scaling-stroke;
}

.chart-cursor {
  stroke: var(--rule-strong);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.chart-c-0 { color: var(--chart-1); }
.chart-c-1 { color: var(--chart-2); }
.chart-c-2 { color: var(--chart-3); }
.chart-c-3 { color: var(--chart-4); }
.chart-c-4 { color: var(--chart-5); }
.chart-c-5 { color: var(--chart-6); }

.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xxs) var(--space-lg);
  margin: var(--space-xs) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-xs);
}

.chart-legend li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-width: 0;
}

.chart-swatch {
  width: var(--icon-md);
  height: var(--space-xs);
  flex: none;
  overflow: visible;
}

.chart-legend-value {
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.chart-tooltip {
  position: absolute;
  top: 0;
  z-index: 2;
  min-width: 10rem;
  padding: var(--space-xs) var(--space-sm);
  margin: 0 var(--space-sm);
  background: var(--panel);
  border: var(--hairline) solid var(--rule-strong);
  border-radius: var(--radius-control);
  box-shadow: var(--shadow-pop);
  font-size: var(--text-xs);
  pointer-events: none;
}

.chart-tooltip-time {
  color: var(--ink-muted);
  margin-bottom: var(--space-xxs);
}

.chart-tooltip-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
}

/* The series colour as a leading bar; the words stay in ink (the chart tokens are
   only 3:1, not text contrast). */
.chart-tooltip-swatch {
  display: inline-block;
  height: 0.75em;
  margin-inline-end: var(--space-xs);
  border-left: var(--edge) solid currentColor;
}

.chart-tooltip-label,
.chart-tooltip-value {
  white-space: nowrap;
}

.chart-tooltip-value {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* The overview card's metrics line: one sentence and the last hour of CPU. */
.app-card-metrics {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.sparkline {
  width: 7.5rem;
  height: var(--heartbeat-height);
  flex: none;
}
