/*
 * ops-observability — "Instrument"
 *
 * Design notes live in docs/design-notes.md. The short version:
 *
 *  - Severity owns warm hues plus green, and nothing else may use them. The interactive accent is
 *    indigo, because it is the one hue left that cannot be mistaken for a status.
 *  - Ground is dark but never #000: every panel edge here is a hairline, and pure black crushes them.
 *  - Monospace is the default voice for machine output, which in this product is almost everything.
 *  - No web fonts, no CDN, no inline styles. Strict CSP, and it has to work on an air-gapped VM.
 */

/* ============================================================ tokens */

:root {
  /* Surfaces. Low chroma, slightly cool, deliberately not black. */
  --ground: #0d0f13;
  --ground-deep: #090a0d;
  --surface: #14171d;
  --surface-raised: #1a1e26;
  --surface-hover: #1f242d;
  --surface-active: #242a34;

  /* Hairlines. Two weights: structural and internal. */
  --line: #262c36;
  --line-soft: #1e232b;
  --line-strong: #333b47;

  /* Ink. */
  --ink: #e8ecf2;
  --ink-dim: #98a2b3;
  --ink-faint: #626c7c;
  --ink-ghost: #414a57;

  /* Severity. Semantic only — see the note above. */
  --sig-ok: #5ccb8b;
  --sig-info: #7fa9de;
  --sig-warn: #e9a93c;
  --sig-error: #ef5f62;
  --sig-fatal: #f87fe0;
  --sig-debug: #7b8494;

  /* Severity washes for row tints and pill backgrounds. */
  --sig-ok-wash: rgba(92, 203, 139, 0.12);
  --sig-info-wash: rgba(127, 169, 222, 0.12);
  --sig-warn-wash: rgba(233, 169, 60, 0.13);
  --sig-error-wash: rgba(239, 95, 98, 0.13);
  --sig-fatal-wash: rgba(248, 127, 224, 0.13);
  --sig-debug-wash: rgba(123, 132, 148, 0.1);

  /* The single interactive accent. */
  --accent: #7c8cff;
  --accent-strong: #93a0ff;
  --accent-dim: #5a68d6;
  --accent-wash: rgba(124, 140, 255, 0.14);
  --accent-line: rgba(124, 140, 255, 0.35);

  /* Type. */
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono", "Cascadia Mono",
    Menlo, Consolas, "Liberation Mono", monospace;

  --text-xs: 11px;
  --text-sm: 12px;
  --text-md: 13px;
  --text-lg: 15px;
  --text-xl: 19px;
  --text-2xl: 26px;
  --text-3xl: 34px;

  /* Geometry. Small radii — this is an instrument, not a card game. */
  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 10px;

  /* Elevation, used only by things that genuinely float. */
  --shadow-drawer: -24px 0 48px rgba(0, 0, 0, 0.5);
  --shadow-float: 0 16px 40px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.4);
  --shadow-pop: 0 8px 24px rgba(0, 0, 0, 0.45);

  /* Shell. */
  --rail-width: 216px;
  --rail-collapsed: 56px;
  --topbar-height: 50px;

  /* Density — reset by the body class below. */
  --row-height: 26px;
  --row-pad-x: 10px;
  --panel-pad: 14px;
  --gap: 12px;

  --transition-fast: 120ms cubic-bezier(0.2, 0.8, 0.2, 1);
  --transition-drawer: 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/*
 * Light theme. A genuine second reading rather than an inversion: surfaces gain a warm paper cast so
 * the hairlines stay visible, and the severity hues darken enough to hold 4.5:1 on a light ground —
 * the dark-mode values would fail contrast here.
 */
:root[data-theme="light"] {
  --ground: #f6f7f9;
  --ground-deep: #eceef2;
  --surface: #ffffff;
  --surface-raised: #ffffff;
  --surface-hover: #f2f4f7;
  --surface-active: #e9ecf1;

  --line: #dde1e8;
  --line-soft: #e9ecf1;
  --line-strong: #c3cad5;

  --ink: #171b22;
  --ink-dim: #59616e;
  --ink-faint: #7d8694;
  --ink-ghost: #a8b0bc;

  --sig-ok: #12864f;
  --sig-info: #2a63a8;
  --sig-warn: #9a6608;
  --sig-error: #c02b2f;
  --sig-fatal: #a4249a;
  --sig-debug: #616a78;

  --sig-ok-wash: rgba(18, 134, 79, 0.1);
  --sig-info-wash: rgba(42, 99, 168, 0.1);
  --sig-warn-wash: rgba(154, 102, 8, 0.12);
  --sig-error-wash: rgba(192, 43, 47, 0.1);
  --sig-fatal-wash: rgba(164, 36, 154, 0.1);
  --sig-debug-wash: rgba(97, 106, 120, 0.08);

  --accent: #4353d8;
  --accent-strong: #2f3fc4;
  --accent-dim: #6a77e6;
  --accent-wash: rgba(67, 83, 216, 0.1);
  --accent-line: rgba(67, 83, 216, 0.35);

  --shadow-drawer: -24px 0 48px rgba(20, 26, 38, 0.16);
  --shadow-float: 0 16px 40px rgba(20, 26, 38, 0.18), 0 2px 8px rgba(20, 26, 38, 0.1);
  --shadow-pop: 0 8px 24px rgba(20, 26, 38, 0.14);
}

body.density-cozy {
  --row-height: 32px;
  --row-pad-x: 12px;
  --panel-pad: 16px;
  --gap: 14px;
}

body.density-spacious {
  --row-height: 38px;
  --row-pad-x: 14px;
  --panel-pad: 20px;
  --gap: 18px;
}

/* ============================================================ base */

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

html,
body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--text-md);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
}

/* Every figure in the product is tabular, so columns do not twitch as values stream in. */
.mono,
code,
pre,
kbd,
.num,
td.num,
th.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "ss01" 1;
}

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

a:hover {
  color: var(--accent-strong);
  text-decoration: underline;
  text-underline-offset: 2px;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

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

::selection {
  background: var(--accent-wash);
  color: var(--ink);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Scrollbars sized for dense content; a 15px default bar wastes a column in every panel. */
.scroll {
  overflow: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

.scroll::-webkit-scrollbar {
  width: 9px;
  height: 9px;
}

.scroll::-webkit-scrollbar-track {
  background: transparent;
}

.scroll::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 5px;
  border: 2px solid transparent;
  background-clip: padding-box;
}

.scroll::-webkit-scrollbar-thumb:hover {
  background: var(--ink-ghost);
  background-clip: padding-box;
}

/* ============================================================ shell */

.shell {
  display: grid;
  grid-template-columns: var(--rail-width) 1fr;
  height: 100vh;
  height: 100dvh;
}

body.rail-collapsed .shell {
  grid-template-columns: var(--rail-collapsed) 1fr;
}

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

.rail {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--ground-deep);
  border-right: 1px solid var(--line);
}

.rail-head {
  display: flex;
  align-items: center;
  gap: 9px;
  height: var(--topbar-height);
  padding: 0 12px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.rail-mark {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
}

.rail-mark svg {
  width: 22px;
  height: 22px;
}

.rail-name {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.rail-name b {
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.rail-name span {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

body.rail-collapsed .rail-name,
body.rail-collapsed .nav-label,
body.rail-collapsed .nav-eyebrow,
body.rail-collapsed .rail-foot-text {
  display: none;
}

/*
 * Collapsed to 56px, the ingest-health chip cannot show its label — it rendered as "2 SOUR…" hanging over
 * the rail edge. It becomes a bare status dot, which still answers the only question it needs to at that
 * width: is something wrong? The full text stays available as its tooltip.
 */
body.rail-collapsed [data-role="ingest-health"] {
  width: 12px;
  height: 12px;
  padding: 0;
  overflow: hidden;
  text-indent: -999px;
  border-radius: 50%;
  margin: 0 auto;
}

body.rail-collapsed [data-role="ingest-health"]::before {
  width: 12px;
  height: 12px;
}

body.rail-collapsed .rail-foot-row {
  justify-content: center;
  padding: 0;
}

.nav {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 10px 8px;
  scrollbar-width: thin;
}

.nav-eyebrow {
  padding: 12px 8px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-ghost);
}

.nav-eyebrow:first-child {
  padding-top: 2px;
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 32px;
  padding: 0 10px;
  border-radius: var(--r-md);
  color: var(--ink-dim);
  font-size: var(--text-md);
  font-weight: 500;
  text-decoration: none;
  transition: background var(--transition-fast), color var(--transition-fast);
}

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

.nav-item[aria-current="page"] {
  background: var(--accent-wash);
  color: var(--ink);
}

/* The active marker is a bar in the gutter, matching the row gutter used in every list. */
.nav-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  left: -8px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  border-radius: 0 2px 2px 0;
  background: var(--accent);
}

.nav-item svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.nav-label {
  flex: 1 1 auto;
  white-space: nowrap;
}

.nav-count {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--surface-active);
  color: var(--ink-dim);
}

.nav-count[data-tone="error"] {
  background: var(--sig-error-wash);
  color: var(--sig-error);
}

.nav-count[data-tone="warn"] {
  background: var(--sig-warn-wash);
  color: var(--sig-warn);
}

body.rail-collapsed .nav-count {
  display: none;
}

body.rail-collapsed .nav-item {
  justify-content: center;
  padding: 0;
}

.rail-foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--line);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.rail-foot-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 30px;
  padding: 0 8px;
  border-radius: var(--r-md);
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.rail-foot-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------ topbar */

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

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--topbar-height);
  flex: 0 0 auto;
  padding: 0 14px;
  background: var(--ground-deep);
  border-bottom: 1px solid var(--line);
}

.crumb {
  display: flex;
  align-items: baseline;
  gap: 9px;
  min-width: 0;
}

.crumb h1 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.crumb p {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-spacer {
  flex: 1 1 auto;
}

.topbar-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

/* ------------------------------------------------ the tape (signature) */

/*
 * Sixty one-minute columns of the last hour: stacked response classes, with application errors
 * punching through as full-height ticks. Present on every page, because the question you arrive with
 * is "is right now different from ten minutes ago", and that should be answerable before you pick a
 * page.
 */
.tape {
  position: relative;
  flex: 0 0 auto;
  width: 264px;
  height: 30px;
  padding: 0 9px;
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 10px;
}

.tape-plot {
  position: relative;
  width: 186px;
  height: 22px;
  display: flex;
  align-items: flex-end;
  gap: 1px;
}

.tape-col {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  border-radius: 1px;
  background: var(--line-soft);
  overflow: hidden;
}

.tape-col span {
  display: block;
  width: 100%;
}

.tape-col .c2 {
  background: var(--sig-ok);
  opacity: 0.62;
}

.tape-col .c3 {
  background: var(--sig-info);
  opacity: 0.7;
}

.tape-col .c4 {
  background: var(--sig-warn);
  opacity: 0.85;
}

.tape-col .c5 {
  background: var(--sig-error);
}

/*
 * A cap at the top of the column marks a minute that produced errors.
 *
 * Originally a full-height red bar, on the theory that a single error should be visible in a busy minute.
 * At 60 columns in 186px that backfired badly: on a server with errors in most minutes the tape read as
 * almost solid red — 28% red pixels against 4% green, while 77% of the traffic was actually 2xx. A cap
 * marks the minute without repainting it, and the stacked 5xx segment below still carries the magnitude.
 */
.tape-col.has-error::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  border-radius: 1px;
  background: var(--sig-error);
}

.tape-col.is-now {
  box-shadow: inset 0 0 0 1px var(--accent-line);
}

.tape-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.tape-meta b {
  font-size: var(--text-sm);
  font-weight: 600;
}

.tape-meta span {
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-ghost);
}

/* ============================================================ controls */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-raised);
  color: var(--ink-dim);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition-fast), color var(--transition-fast),
    border-color var(--transition-fast);
}

.btn:hover:not(:disabled) {
  background: var(--surface-hover);
  border-color: var(--line-strong);
  color: var(--ink);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn svg {
  width: 14px;
  height: 14px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #0b0d14;
  font-weight: 600;
}

.btn-primary:hover:not(:disabled) {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: #0b0d14;
}

.btn-icon {
  width: 30px;
  min-height: 30px;
  padding: 0;
}

.btn-ghost {
  background: transparent;
  border-color: transparent;
}

.btn-ghost:hover:not(:disabled) {
  background: var(--surface-hover);
  border-color: var(--line);
}

.btn[aria-pressed="true"] {
  background: var(--accent-wash);
  border-color: var(--accent-line);
  color: var(--ink);
}

.btn-sm {
  min-height: 24px;
  padding: 0 8px;
  font-size: var(--text-xs);
}

/* Touch targets: pointer-coarse devices get the 44px minimum without inflating the desktop layout. */
@media (pointer: coarse) {
  .btn,
  .btn-icon,
  .nav-item {
    min-height: 44px;
  }

  .btn-icon {
    width: 44px;
  }
}

kbd {
  display: inline-block;
  min-width: 17px;
  padding: 1px 4px;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: var(--r-sm);
  background: var(--surface-raised);
  color: var(--ink-faint);
  font-size: 10px;
  text-align: center;
  line-height: 1.4;
}

.input,
select.input {
  min-height: 30px;
  padding: 0 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--text-sm);
}

.input::placeholder {
  color: var(--ink-ghost);
}

.input:focus {
  outline: none;
  border-color: var(--accent-line);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

select.input {
  cursor: pointer;
  appearance: none;
  padding-right: 26px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-faint) 50%),
    linear-gradient(135deg, var(--ink-faint) 50%, transparent 50%);
  background-position: calc(100% - 14px) center, calc(100% - 9px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* ------------------------------------------------ query field */

.query {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
}

.query svg {
  position: absolute;
  left: 9px;
  width: 14px;
  height: 14px;
  stroke: var(--ink-ghost);
  fill: none;
  stroke-width: 1.7;
  pointer-events: none;
}

.query input {
  width: 100%;
  min-height: 30px;
  padding: 0 68px 0 29px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.query input:focus {
  outline: none;
  border-color: var(--accent-line);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

.query-hint {
  position: absolute;
  right: 8px;
  display: flex;
  gap: 3px;
  pointer-events: none;
}

/* ============================================================ pills and tags */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 19px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--sig-debug-wash);
  color: var(--ink-dim);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.pill::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}

.pill.no-dot::before {
  display: none;
}

.pill[data-tone="ok"] { background: var(--sig-ok-wash); color: var(--sig-ok); }
.pill[data-tone="info"] { background: var(--sig-info-wash); color: var(--sig-info); }
.pill[data-tone="warn"] { background: var(--sig-warn-wash); color: var(--sig-warn); }
.pill[data-tone="error"] { background: var(--sig-error-wash); color: var(--sig-error); }
.pill[data-tone="fatal"] { background: var(--sig-fatal-wash); color: var(--sig-fatal); }
.pill[data-tone="accent"] { background: var(--accent-wash); color: var(--accent); }

/* The live indicator pulses only while actually streaming, so it means something. */
.pill.is-streaming::before {
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.78); }
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 21px;
  padding: 0 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-raised);
  color: var(--ink-dim);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.tag-btn {
  cursor: pointer;
}

.tag-btn:hover {
  border-color: var(--accent-line);
  color: var(--ink);
}

.tag-close {
  display: grid;
  place-items: center;
  width: 13px;
  height: 13px;
  margin-right: -3px;
  border: 0;
  border-radius: 3px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}

.tag-close:hover {
  background: var(--surface-active);
  color: var(--ink);
}

/* Status codes read as numbers first, colour second. */
.status {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.status[data-class="2"] { color: var(--sig-ok); }
.status[data-class="3"] { color: var(--sig-info); }
.status[data-class="4"] { color: var(--sig-warn); }
.status[data-class="5"] { color: var(--sig-error); }
.status[data-class="0"],
.status[data-class="1"] { color: var(--ink-faint); }

.method {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--ink-dim);
}

/* ============================================================ layout primitives */

.content {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--gap);
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

.content::-webkit-scrollbar { width: 10px; }
.content::-webkit-scrollbar-track { background: transparent; }
.content::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 5px;
  border: 3px solid transparent;
  background-clip: padding-box;
}

/* Full-height pages (Live) manage their own scrolling. */
.content.fixed {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}

.grid {
  display: grid;
  gap: var(--gap);
}

.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-wide-narrow { grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr); }
.grid-narrow-wide { grid-template-columns: minmax(0, 1fr) minmax(0, 1.9fr); }

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  min-width: 0;
}

.row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.row-wrap {
  flex-wrap: wrap;
}

.push {
  margin-left: auto;
}

/* ------------------------------------------------ panel */

.panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

/*
 * Wraps rather than overflowing. A panel header carries a title, a subtitle, a legend and often a control;
 * on a narrow viewport that cannot fit on one line, and without wrapping the control — the range selector,
 * for instance — is pushed off the edge of the panel and becomes unreachable.
 */
.panel-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 10px;
  min-height: 38px;
  padding: 8px var(--panel-pad);
  border-bottom: 1px solid var(--line-soft);
  flex: 0 0 auto;
}

/* The spacer only does its job while everything is on one line. */
@media (max-width: 760px) {
  .panel-head .push {
    display: none;
  }

  .panel-head .legend {
    order: 10;
    width: 100%;
  }
}

.panel-head h2,
.panel-head h3 {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: -0.005em;
  white-space: nowrap;
}

.panel-head .sub {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  white-space: nowrap;
}

.panel-body {
  padding: var(--panel-pad);
  min-width: 0;
}

.panel-body.flush {
  padding: 0;
}

.panel-body.tight {
  padding: 8px var(--panel-pad);
}

.panel-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 34px;
  padding: 0 var(--panel-pad);
  border-top: 1px solid var(--line-soft);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  flex: 0 0 auto;
}

.eyebrow {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-ghost);
}

/* ============================================================ KPI cards */

.kpis {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
}

.kpi {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 11px 13px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  min-width: 0;
}

.kpi-label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-ghost);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.kpi-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xl);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.kpi-value small {
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--ink-faint);
  margin-left: 2px;
}

.kpi-note {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.kpi[data-tone="ok"] .kpi-value { color: var(--sig-ok); }
.kpi[data-tone="info"] .kpi-value { color: var(--sig-info); }
.kpi[data-tone="warn"] .kpi-value { color: var(--sig-warn); }
.kpi[data-tone="error"] .kpi-value { color: var(--sig-error); }

.kpi-spark {
  height: 20px;
  margin-top: 2px;
}

/* ============================================================ tables */

.table-wrap {
  overflow-x: auto;
  scrollbar-width: thin;
}

table.data {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--text-sm);
}

table.data th {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 0 var(--row-pad-x);
  height: 30px;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}

table.data th.sortable {
  cursor: pointer;
  user-select: none;
}

table.data th.sortable:hover {
  color: var(--ink);
}

table.data th[aria-sort] {
  color: var(--ink);
}

table.data th .sort-mark {
  display: inline-block;
  width: 8px;
  margin-left: 3px;
  color: var(--accent);
}

table.data td {
  padding: 0 var(--row-pad-x);
  height: var(--row-height);
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink-dim);
  white-space: nowrap;
}

/*
 * Only columns explicitly marked wide are allowed to truncate.
 *
 * The usual trick for ellipsis in a table is `max-width: 0` on every cell, but that makes the browser
 * shrink whichever columns it likes when space is tight — and a figure clipped to "14.…" or "394…" is worse
 * than useless on an operations dashboard. Non-wide cells size to their content and the wrapper scrolls
 * instead.
 */
table.data td.wide {
  max-width: 420px;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.data td.num,
table.data th.num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

table.data tbody tr {
  transition: background var(--transition-fast);
}

table.data tbody tr:hover {
  background: var(--surface-hover);
}

table.data tbody tr.clickable {
  cursor: pointer;
}

table.data tbody tr.is-selected {
  background: var(--accent-wash);
}

table.data td .primary {
  color: var(--ink);
}

/* A hairline bar in the first cell, echoing the log gutter. */
table.data td.gutter {
  width: 3px;
  min-width: 3px;
  max-width: 3px;
  padding: 0;
  border-bottom-color: transparent;
}

table.data td.gutter i {
  display: block;
  width: 3px;
  height: var(--row-height);
  background: var(--sig-debug);
}

td.gutter i[data-level="INFO"] { background: var(--sig-info); }
td.gutter i[data-level="WARN"] { background: var(--sig-warn); }
td.gutter i[data-level="ERROR"] { background: var(--sig-error); }
td.gutter i[data-level="FATAL"] { background: var(--sig-fatal); }
td.gutter i[data-level="DEBUG"],
td.gutter i[data-level="TRACE"],
td.gutter i[data-level="UNKNOWN"] { background: var(--sig-debug); }

/* Inline proportion bar, used for share-of-traffic columns. */
.bar {
  position: relative;
  height: 5px;
  min-width: 42px;
  border-radius: 3px;
  background: var(--line-soft);
  overflow: hidden;
}

.bar i {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--accent-dim);
  transition: width var(--transition-fast);
}

.bar i[data-tone="ok"] { background: var(--sig-ok); }
.bar i[data-tone="warn"] { background: var(--sig-warn); }
.bar i[data-tone="error"] { background: var(--sig-error); }

/* Stacked status-class bar for a table row. */
.stackbar {
  display: flex;
  height: 5px;
  min-width: 54px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--line-soft);
}

.stackbar span { display: block; height: 100%; }
.stackbar .c2 { background: var(--sig-ok); }
.stackbar .c3 { background: var(--sig-info); }
.stackbar .c4 { background: var(--sig-warn); }
.stackbar .c5 { background: var(--sig-error); }

/* ============================================================ log list */

/*
 * Virtualised: only the rows in view exist in the DOM. A live tail can produce thousands of rows a
 * minute, and appending them all is what turns a log viewer into a frozen tab.
 */
.logs {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  contain: strict;
}

.logs::-webkit-scrollbar { width: 10px; }
.logs::-webkit-scrollbar-track { background: transparent; }
.logs::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 5px;
  border: 3px solid transparent;
  background-clip: padding-box;
}

.logs-sizer {
  position: relative;
  width: 100%;
}

.logs-viewport {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.log-row {
  display: flex;
  align-items: flex-start;
  gap: 0;
  min-height: var(--row-height);
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: calc(var(--row-height) - 2px);
}

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

.log-row.is-selected {
  background: var(--accent-wash);
}

/* The gutter: severity bar, time, source. Identical in every list in the product. */
.log-bar {
  flex: 0 0 3px;
  align-self: stretch;
  background: var(--sig-debug);
}

.log-row[data-level="INFO"] .log-bar { background: var(--sig-info); }
.log-row[data-level="WARN"] .log-bar { background: var(--sig-warn); }
.log-row[data-level="ERROR"] .log-bar { background: var(--sig-error); }
.log-row[data-level="FATAL"] .log-bar { background: var(--sig-fatal); }

.log-row[data-level="ERROR"],
.log-row[data-level="FATAL"] {
  background: var(--sig-error-wash);
}

.log-row[data-level="ERROR"]:hover,
.log-row[data-level="FATAL"]:hover {
  background: color-mix(in srgb, var(--sig-error-wash) 70%, var(--surface-hover));
}

.log-time {
  flex: 0 0 auto;
  padding: 0 9px 0 8px;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.log-src {
  flex: 0 0 auto;
  width: 13px;
  text-align: center;
  color: var(--ink-ghost);
  font-size: 10px;
  font-weight: 700;
}

.log-level {
  flex: 0 0 46px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--sig-debug);
}

.log-row[data-level="INFO"] .log-level { color: var(--sig-info); }
.log-row[data-level="WARN"] .log-level { color: var(--sig-warn); }
.log-row[data-level="ERROR"] .log-level { color: var(--sig-error); }
.log-row[data-level="FATAL"] .log-level { color: var(--sig-fatal); }

.log-who {
  flex: 0 0 auto;
  max-width: 168px;
  padding-right: 10px;
  color: var(--accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.log-msg {
  flex: 1 1 auto;
  min-width: 0;
  padding-right: 10px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.log-row.expanded .log-msg {
  white-space: pre-wrap;
  overflow: visible;
  word-break: break-word;
}

.log-row.expanded {
  align-items: flex-start;
  line-height: 1.5;
  padding: 4px 0;
}

.log-aside {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 10px;
  color: var(--ink-faint);
  font-size: var(--text-xs);
}

.log-trace-flag {
  color: var(--sig-error);
  font-size: 10px;
  font-weight: 700;
}

/* New rows announce themselves once, briefly, then stop. */
.log-row.is-new .log-bar {
  animation: bar-flash 900ms ease-out;
}

@keyframes bar-flash {
  0% { box-shadow: 0 0 0 0 currentColor; filter: brightness(1.9); }
  100% { box-shadow: none; filter: brightness(1); }
}

.log-stack {
  margin: 0;
  padding: 8px 12px 10px 62px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--ground-deep);
  color: var(--ink-dim);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.6;
  white-space: pre;
  overflow-x: auto;
}

/* Frames in the monitored application's own packages are the ones you would actually open. */
.log-stack b {
  color: var(--ink);
  font-weight: 600;
}

/* ------------------------------------------------ live bar */

.livebar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  flex-wrap: wrap;
}

.resume {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 28px;
  padding: 0 6px 0 11px;
  border: 1px solid var(--accent-line);
  border-radius: 999px;
  background: var(--accent-wash);
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.resume:hover {
  background: color-mix(in srgb, var(--accent-wash) 60%, var(--surface-hover));
}

.resume b {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.resume span {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--accent);
  color: #0b0d14;
  font-size: var(--text-xs);
  font-weight: 600;
}

/*
 * The resume affordance floats over the log stream rather than sitting above it.
 *
 * Inserting it into the flow shortened the list container, which re-windowed the virtualised rows and moved
 * the very content the operator had paused in order to read. Overlaying it costs nothing and keeps the
 * paused view perfectly still, which is the entire point of pausing.
 */
.resume-slot {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  pointer-events: none;
}

.resume-slot .resume {
  pointer-events: auto;
  box-shadow: var(--shadow-pop);
  background: var(--surface-raised);
}

/* Anchor for the overlay above. */
.logs-frame {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* Focus mode is always visibly on, and always one click from off. */
.focusbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 11px;
  border: 1px solid var(--accent-line);
  border-left-width: 3px;
  border-radius: var(--r-md);
  background: var(--accent-wash);
  font-size: var(--text-sm);
  flex: 0 0 auto;
}

.focusbar .eyebrow {
  color: var(--accent);
}

.focusbar code {
  color: var(--ink);
  font-size: var(--text-sm);
}

/* ============================================================ charts */

.chart {
  position: relative;
  width: 100%;
}

.chart svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.chart .axis-line {
  stroke: var(--line-soft);
  stroke-width: 1;
  shape-rendering: crispEdges;
}

.chart .axis-text {
  fill: var(--ink-ghost);
  font-family: var(--font-mono);
  font-size: 9px;
  font-variant-numeric: tabular-nums;
}

.chart .grid-line {
  stroke: var(--line-soft);
  stroke-width: 1;
  stroke-dasharray: 2 4;
  shape-rendering: crispEdges;
}

.chart .series-line {
  fill: none;
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.chart .series-area {
  stroke: none;
  opacity: 0.16;
}

.chart .crosshair {
  stroke: var(--ink-ghost);
  stroke-width: 1;
  stroke-dasharray: 2 3;
  shape-rendering: crispEdges;
}

.chart .hover-dot {
  stroke: var(--ground);
  stroke-width: 1.5;
}

.chart-tip {
  position: absolute;
  z-index: 5;
  min-width: 128px;
  padding: 7px 9px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface-raised);
  box-shadow: var(--shadow-pop);
  font-size: var(--text-xs);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--transition-fast);
}

.chart-tip.visible {
  opacity: 1;
}

.chart-tip .tip-time {
  color: var(--ink-faint);
  font-family: var(--font-mono);
  margin-bottom: 4px;
}

.chart-tip dl {
  display: grid;
  grid-template-columns: auto auto;
  gap: 1px 10px;
  margin: 0;
}

.chart-tip dt {
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--ink-dim);
}

.chart-tip dt i {
  width: 6px;
  height: 6px;
  border-radius: 2px;
}

.chart-tip dd {
  margin: 0;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.legend {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: var(--text-xs);
  color: var(--ink-dim);
}

.legend span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.legend i {
  width: 8px;
  height: 8px;
  border-radius: 2px;
}

/* Sparkline in a table cell or KPI card. */
.spark {
  display: block;
  width: 100%;
  height: 100%;
}

.spark path {
  fill: none;
  stroke-width: 1.4;
  stroke-linejoin: round;
}

.spark rect {
  shape-rendering: crispEdges;
}

/* ============================================================ utilities */

/*
 * A small set of named utilities, existing for one specific reason: the Content-Security-Policy is
 * `style-src 'self'` with no `unsafe-inline`, so a `style="…"` attribute in a template is blocked by the
 * browser. Rather than weaken the policy — which would also re-open inline styling as an injection vector
 * for log content — the handful of one-off geometry values templates need live here as classes.
 *
 * Computed geometry (bar widths, chart heights) is applied from JavaScript through the CSSOM, which CSP
 * does not restrict. See Ops.applyStyle.
 */

.mt-gap { margin-top: var(--gap); }
.mb-gap { margin-bottom: var(--gap); }
.mb-md { margin-bottom: 16px; }
.mt-0 { margin-top: 0; }

/* Search fields sit beside other controls and should not stretch across the row. */
.query-md { max-width: 420px; }

/* A panel inside a grid row needs this to shrink rather than force the row taller. */
.min-0 { min-height: 0; }

.chart-lg { height: 164px; }
.chart-md { height: 132px; }
.chart-sm { height: 44px; }
.spark-cell { height: 18px; min-width: 70px; }

/* A pill used purely as a text label, with no status meaning. */
.pill-quiet {
  background: transparent;
  color: var(--ink-faint);
}

.pill-ghost {
  background: transparent;
  color: var(--ink-ghost);
}

/* Icons written directly into a template rather than built by the icon() helper. */
.icon-inline {
  flex: 0 0 auto;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-inline-top {
  margin-top: 2px;
}

/* Keeps a wrapping form from becoming a layout box of its own. */
.form-contents {
  display: contents;
}

.select-sm {
  min-height: 26px;
}

.select-mini {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 26px;
  font-size: 11px;
}

/* Legend and swatch colours, keyed by tone so no colour is written inline. */
.legend i[data-tone="ok"] { background: var(--sig-ok); }
.legend i[data-tone="info"] { background: var(--sig-info); }
.legend i[data-tone="warn"] { background: var(--sig-warn); }
.legend i[data-tone="error"] { background: var(--sig-error); }
.legend i[data-tone="fatal"] { background: var(--sig-fatal); }
.legend i[data-tone="accent"] { background: var(--accent); }

/* Loading placeholders sized to roughly the content they stand in for, so nothing jumps on arrival. */
.kpi-value.skeleton {
  height: 28px;
  width: 64%;
}

.skeleton-w-80 { width: 80%; }
.skeleton-w-70 { width: 70%; }
.skeleton-w-60 { width: 60%; }

/* ============================================================ ranked list */

/*
 * Label, proportion bar, value. Used for response mix, top routes, agents and retention. Horizontal
 * because the labels are paths and user-agent strings — on a vertical bar chart those have to be rotated
 * or truncated away, and the label is the part you came for.
 */
.ranked {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.ranked-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 96px 74px;
  align-items: center;
  gap: 10px;
  min-height: 22px;
  padding: 2px 0;
  color: var(--ink-dim);
  font-size: var(--text-sm);
  text-decoration: none;
  border-radius: var(--r-sm);
}

a.ranked-row:hover {
  background: var(--surface-hover);
  color: var(--ink);
  text-decoration: none;
}

.ranked-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

a.ranked-row .ranked-label {
  color: var(--accent);
}

a.ranked-row:hover .ranked-label {
  color: var(--accent-strong);
}

.ranked-value {
  text-align: right;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 620px) {
  .ranked-row {
    grid-template-columns: minmax(0, 1fr) 56px;
  }
  .ranked-row .bar {
    display: none;
  }
}

/* ============================================================ issue and trace rows */

/*
 * One shared row for issues, requests and anything else that is "a thing with a title, a trend and a
 * count". Carries the same left severity bar as a log row, which is what ties the pages together.
 */
.issue-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px 8px 0;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  background: transparent;
  color: var(--ink-dim);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition-fast);
}

.issue-row:hover {
  background: var(--surface-hover);
  text-decoration: none;
}

.issue-row.is-selected {
  background: var(--accent-wash);
}

.issue-bar {
  flex: 0 0 3px;
  align-self: stretch;
  min-height: 34px;
  background: var(--sig-debug);
}

.issue-row[data-level="INFO"] .issue-bar { background: var(--sig-info); }
.issue-row[data-level="WARN"] .issue-bar { background: var(--sig-warn); }
.issue-row[data-level="ERROR"] .issue-bar { background: var(--sig-error); }
.issue-row[data-level="FATAL"] .issue-bar { background: var(--sig-fatal); }

.issue-main {
  flex: 1 1 auto;
  min-width: 0;
  padding-left: 9px;
}

.issue-title {
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.issue-sub {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 1px;
  color: var(--ink-faint);
  font-size: var(--text-xs);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.issue-route {
  padding: 0 5px;
  border-radius: 3px;
  background: var(--surface-active);
  color: var(--ink-dim);
}

.issue-meta {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  min-width: 62px;
}

.issue-meta b {
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.issue-meta span {
  color: var(--ink-ghost);
  font-size: 10px;
  white-space: nowrap;
}

/* ============================================================ source strip */

.source-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
}

.source-chip {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 12px var(--panel-pad);
  border-right: 1px solid var(--line-soft);
  color: var(--ink-dim);
  text-decoration: none;
  transition: background var(--transition-fast);
}

.source-chip:last-child {
  border-right: 0;
}

.source-chip:hover {
  background: var(--surface-hover);
  text-decoration: none;
}

.source-chip-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.source-chip-head b {
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 600;
}

.source-chip code {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.source-chip-meta {
  display: flex;
  gap: 12px;
  font-size: var(--text-xs);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.source-chip-detail {
  font-size: var(--text-xs);
  color: var(--sig-error);
}

.source-chip[data-tone="error"] {
  background: var(--sig-error-wash);
}

.source-chip[data-tone="warn"] {
  background: var(--sig-warn-wash);
}

/* ============================================================ traffic findings */

.finding {
  display: flex;
  align-items: flex-start;
  gap: 0;
  border-bottom: 1px solid var(--line-soft);
}

.finding:last-child {
  border-bottom: 0;
}

.finding-bar {
  flex: 0 0 3px;
  align-self: stretch;
  background: var(--sig-info);
}

.finding[data-severity="warn"] .finding-bar {
  background: var(--sig-warn);
}

.finding-main {
  flex: 1 1 auto;
  min-width: 0;
  padding: 11px 14px;
}

.finding-head {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
}

.finding-head b {
  color: var(--ink);
  font-size: var(--text-md);
  font-weight: 600;
}

.finding-head code {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--accent);
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--accent-wash);
}

.finding-detail {
  margin: 4px 0 0;
  max-width: 82ch;
  color: var(--ink-dim);
  font-size: var(--text-sm);
  line-height: 1.55;
}

.finding-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 11px 14px 11px 0;
}

@media (max-width: 760px) {
  .finding {
    flex-wrap: wrap;
  }
  .finding-actions {
    padding: 0 14px 12px 17px;
  }
}

/* ============================================================ states */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  padding: 40px 24px;
  text-align: center;
  color: var(--ink-faint);
}

.empty svg {
  width: 26px;
  height: 26px;
  stroke: var(--ink-ghost);
  fill: none;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.empty b {
  color: var(--ink-dim);
  font-size: var(--text-md);
  font-weight: 600;
}

.empty p {
  margin: 0;
  max-width: 46ch;
  font-size: var(--text-sm);
  line-height: 1.55;
}

.empty code {
  color: var(--ink-dim);
  font-size: var(--text-xs);
  padding: 1px 4px;
  border-radius: 3px;
  background: var(--surface-raised);
}

.skeleton {
  border-radius: var(--r-sm);
  background: linear-gradient(
    90deg,
    var(--surface-raised) 0%,
    var(--surface-hover) 40%,
    var(--surface-raised) 80%
  );
  background-size: 300% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
}

@keyframes shimmer {
  0% { background-position: 120% 0; }
  100% { background-position: -60% 0; }
}

.skeleton-row {
  height: var(--row-height);
  margin-bottom: 1px;
}

.skeleton-text {
  height: 9px;
  margin: 5px 0;
}

.notice {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: var(--text-sm);
  color: var(--ink-dim);
}

.notice b {
  color: var(--ink);
}

.notice[data-tone="warn"] { border-left-color: var(--sig-warn); background: var(--sig-warn-wash); }
.notice[data-tone="error"] { border-left-color: var(--sig-error); background: var(--sig-error-wash); }
.notice[data-tone="info"] { border-left-color: var(--sig-info); background: var(--sig-info-wash); }
.notice[data-tone="ok"] { border-left-color: var(--sig-ok); background: var(--sig-ok-wash); }

/* ============================================================ drawer */

.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 60;
  width: min(720px, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--line-strong);
  box-shadow: var(--shadow-drawer);
  transform: translateX(100%);
  transition: transform var(--transition-drawer);
}

.drawer.open {
  transform: translateX(0);
}

.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: rgba(4, 5, 8, 0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-drawer);
}

.drawer-scrim.open {
  opacity: 1;
  pointer-events: auto;
}

.drawer-head {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--topbar-height);
  padding: 0 14px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.drawer-head h2 {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.drawer-tabs {
  display: flex;
  gap: 2px;
  padding: 0 14px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.drawer-tab {
  position: relative;
  height: 32px;
  padding: 0 10px;
  border: 0;
  background: transparent;
  color: var(--ink-faint);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.drawer-tab:hover {
  color: var(--ink);
}

.drawer-tab[aria-selected="true"] {
  color: var(--ink);
}

.drawer-tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: -1px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--accent);
}

.drawer-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 14px;
  scrollbar-width: thin;
}

.drawer-foot {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  padding: 10px 14px;
  border-top: 1px solid var(--line);
  flex: 0 0 auto;
}

/* Definition grid used throughout the drawer. */
.fields {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  gap: 0;
  margin: 0;
  font-size: var(--text-sm);
}

.fields dt {
  padding: 5px 10px 5px 0;
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
}

.fields dd {
  margin: 0;
  padding: 5px 0;
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  min-width: 0;
  overflow-wrap: anywhere;
}

.fields dd.plain {
  font-family: var(--font-ui);
}

pre.block {
  margin: 0;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ground-deep);
  color: var(--ink-dim);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.62;
  white-space: pre;
  overflow-x: auto;
  scrollbar-width: thin;
}

pre.block b {
  color: var(--ink);
  font-weight: 600;
}

pre.block em {
  color: var(--sig-error);
  font-style: normal;
  font-weight: 600;
}

/* ------------------------------------------------ trace timeline */

/*
 * Time runs downward, matching the way a log is read. The rail on the left is the request's span; each
 * entry sits at its own offset, so a gap in the timeline is visible as a gap.
 */
.timeline {
  position: relative;
  padding-left: 92px;
}

.timeline::before {
  content: "";
  position: absolute;
  left: 78px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--line);
}

.tl-entry {
  position: relative;
  padding: 7px 0 8px 14px;
  border-bottom: 1px solid var(--line-soft);
}

.tl-entry:last-child {
  border-bottom: 0;
}

.tl-offset {
  position: absolute;
  left: -92px;
  top: 8px;
  width: 66px;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  color: var(--ink-faint);
}

.tl-dot {
  position: absolute;
  left: -18px;
  top: 12px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--sig-debug);
  box-shadow: 0 0 0 3px var(--surface);
}

.tl-entry[data-level="INFO"] .tl-dot { background: var(--sig-info); }
.tl-entry[data-level="WARN"] .tl-dot { background: var(--sig-warn); }
.tl-entry[data-level="ERROR"] .tl-dot { background: var(--sig-error); }
.tl-entry[data-level="FATAL"] .tl-dot { background: var(--sig-fatal); }

.tl-label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--text-sm);
}

.tl-label b {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--ink);
}

.tl-detail {
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-dim);
  overflow-wrap: anywhere;
}

/* ============================================================ command palette */

.palette-scrim {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: none;
  padding-top: 14vh;
  justify-content: center;
  background: rgba(4, 5, 8, 0.55);
  backdrop-filter: blur(2px);
}

.palette-scrim.open {
  display: flex;
}

.palette {
  width: min(560px, calc(100vw - 32px));
  max-height: 60vh;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface-raised);
  box-shadow: var(--shadow-float);
  overflow: hidden;
  animation: palette-in var(--transition-fast) ease-out;
}

@keyframes palette-in {
  from { transform: scale(0.98) translateY(-4px); opacity: 0; }
  to { transform: scale(1) translateY(0); opacity: 1; }
}

.palette-input {
  flex: 0 0 auto;
  height: 44px;
  padding: 0 14px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font-size: var(--text-lg);
}

.palette-input:focus {
  outline: none;
}

.palette-input::placeholder {
  color: var(--ink-ghost);
}

.palette-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 6px;
  scrollbar-width: thin;
}

.palette-group {
  padding: 8px 8px 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-ghost);
}

.palette-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 34px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-dim);
  font-size: var(--text-md);
  text-align: left;
  cursor: pointer;
}

.palette-item.active {
  background: var(--accent-wash);
  color: var(--ink);
}

.palette-item svg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.palette-item .hint {
  margin-left: auto;
  color: var(--ink-ghost);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.palette-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 12px;
  border-top: 1px solid var(--line);
  color: var(--ink-ghost);
  font-size: var(--text-xs);
}

/* ============================================================ toast */

.toasts {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 90;
  display: flex;
  flex-direction: column-reverse;
  gap: 7px;
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 13px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface-raised);
  box-shadow: var(--shadow-pop);
  color: var(--ink);
  font-size: var(--text-sm);
  animation: toast-in 160ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.toast[data-tone="error"] { border-color: var(--sig-error); color: var(--sig-error); }
.toast[data-tone="ok"] { border-color: var(--sig-ok); }

@keyframes toast-in {
  from { transform: translateY(8px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* ============================================================ login */

body.login-page {
  display: grid;
  place-items: center;
  overflow: auto;
  background:
    radial-gradient(1100px 520px at 50% -10%, rgba(124, 140, 255, 0.07), transparent 65%),
    var(--ground);
}

.login-card {
  width: min(376px, calc(100vw - 32px));
  padding: 26px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-float);
}

.login-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}

.login-brand svg {
  width: 26px;
  height: 26px;
}

.login-brand b {
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.login-card h1 {
  margin: 16px 0 4px;
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: -0.015em;
}

.login-card > p {
  margin: 0 0 20px;
  color: var(--ink-faint);
  font-size: var(--text-sm);
  line-height: 1.55;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 13px;
}

.field label {
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--ink-dim);
}

.field input {
  min-height: 38px;
  padding: 0 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ground-deep);
  color: var(--ink);
  font-size: var(--text-md);
}

.field input:focus {
  outline: none;
  border-color: var(--accent-line);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

.login-card .btn-primary {
  width: 100%;
  min-height: 38px;
  margin-top: 5px;
}

.login-foot {
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  color: var(--ink-ghost);
  font-size: var(--text-xs);
  line-height: 1.6;
}

/* ============================================================ responsive */

/*
 * Desktop is the target — this is a tool used at a keyboard. Narrow layouts stay genuinely usable
 * rather than pretending to be a phone app: the rail collapses to icons, dense grids fold to one
 * column, and tables scroll horizontally instead of crushing their columns to nothing.
 */
@media (max-width: 1180px) {
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-wide-narrow,
  .grid-narrow-wide { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
  .shell { grid-template-columns: var(--rail-collapsed) 1fr; }
  .rail-name,
  .nav-label,
  .nav-eyebrow,
  .nav-count,
  .rail-foot-text { display: none; }
  .nav-item { justify-content: center; padding: 0; }
  .grid-2,
  .grid-3 { grid-template-columns: minmax(0, 1fr); }
  .tape { display: none; }
  .crumb p { display: none; }
}

@media (max-width: 620px) {
  :root { --topbar-height: 46px; }
  .content { padding: 10px; }
  .topbar { padding: 0 10px; gap: 8px; }
  .log-who { display: none; }
  .log-level { flex-basis: 34px; }
  .fields { grid-template-columns: minmax(0, 1fr); }
  .fields dt { border-bottom: 0; padding-bottom: 0; }
  .drawer { width: 100vw; }
  .timeline { padding-left: 62px; }
  .timeline::before { left: 50px; }
  .tl-offset { left: -62px; width: 42px; }
}

/* ============================================================ reduced motion */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .drawer { transition: none; }
}

/* ============================================================ print */

@media print {
  .rail,
  .topbar,
  .drawer,
  .palette-scrim,
  .toasts { display: none !important; }
  body { overflow: visible; }
  .shell { display: block; }
  .content { overflow: visible; padding: 0; }
}
