/* ===========================================================================
   Components
   ---------------------------------------------------------------------------
   Structural treatment varies by what the content IS, rather than every block
   being the same rounded card with the same shadow:
     - panel      a bordered region that holds related things
     - instrument a readout; hairline-separated, no border of its own
     - sheet      something that came from an action, so it has depth
     - table      borderless, hairline row rules, no card around it
   ========================================================================= */

/* ---- rail -------------------------------------------------------------- */
.rail {
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--slate);
  border-right: 1px solid var(--hair);
  padding: var(--s4) var(--s3);
  gap: var(--s5);
  min-width: 0;
  overflow: hidden;
}

.brand { display: flex; align-items: center; gap: var(--s3); padding: var(--s1) var(--s2); min-width: 0; }
.brand__mark { flex: 0 0 auto; }
.brand__text { min-width: 0; }
.brand__name {
  font-size: var(--t-headline);
  font-weight: var(--w-semibold);
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.brand__role { font-size: var(--t-cap2); color: var(--dim); white-space: nowrap; }

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav__group + .nav__group { margin-top: var(--s5); }
/* A group heading, in sentence case at a small size. Not an all-caps eyebrow:
   caps labels are decoration pretending to be structure. */
.nav__heading {
  font-size: var(--t-cap2);
  color: var(--dim);
  padding: 0 var(--s2) var(--s2);
  white-space: nowrap;
}

.nav__item {
  position: relative;
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
  min-height: 38px;
  padding: 0 var(--s2);
  border-radius: var(--r-control);
  color: var(--mist);
  font-size: var(--t-data);
  font-weight: var(--w-medium);
  white-space: nowrap;
  transition: color var(--d-micro) var(--e-standard),
              background-color var(--d-micro) var(--e-standard);
}
.nav__item:hover { color: var(--ice); background: var(--shelf); }
.nav__item[aria-current="page"] { color: var(--ice); }

/* The selected marker is one element that MOVES between items rather than
   appearing and disappearing in place, so the eye can follow where it went. */
.nav__marker {
  position: absolute;
  left: 0;
  width: 100%;
  border-radius: var(--r-control);
  background: var(--signal-soft);
  box-shadow: inset 2px 0 0 var(--signal);
  pointer-events: none;
  transition: transform var(--d-spring) var(--e-spring),
              height var(--d-fast) var(--e-standard);
  will-change: transform;
}

.nav__count {
  font-size: var(--t-cap2);
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* Collapsed by choice, or collapsed because the window is narrow — the rail
   is the same width either way, so it has to hide the same things. Keeping
   these rules on the data attribute alone left the labels rendering inside a
   68px column, clipped mid-word. */
.app[data-rail="collapsed"] .brand__text,
.app[data-rail="collapsed"] .nav__label,
.app[data-rail="collapsed"] .nav__heading,
.app[data-rail="collapsed"] .nav__count,
.app[data-rail="collapsed"] .rail__foot-text { display: none; }
.app[data-rail="collapsed"] .nav__item { grid-template-columns: 20px; justify-content: center; }
.app[data-rail="collapsed"] .rail { padding-inline: var(--s3); }

@media (max-width: 900px) {
  .brand__text,
  .nav__label,
  .nav__heading,
  .nav__count,
  .rail__foot-text { display: none; }
  .nav__item { grid-template-columns: 20px; justify-content: center; }
  .rail { padding-inline: var(--s3); }
  /* Collapsing is already forced, so offering the control would be a lie. */
  .rail__foot .nav__item[title="Collapse sidebar"] { display: none; }
}

.rail__foot { display: flex; flex-direction: column; gap: var(--s2); }

/* ---- vitals: the one thing that never stops moving -------------------- */
.vitals {
  display: flex;
  align-items: stretch;
  gap: 0;
  padding: 0 var(--s6) 0 var(--s5);
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  border-bottom: 1px solid var(--hair);
  position: relative;
  z-index: 20;
  /* The readings are fixed-width and must not squash into each other, so on a
     narrow window the STRIP scrolls rather than pushing the whole document
     sideways — which is what it did: 516px of strip inside a 430px viewport
     gave every screen 154px of horizontal page scroll. */
  overflow-x: auto;
  scrollbar-width: none;
}
.vitals::-webkit-scrollbar { display: none; }
.vital { flex: 0 0 auto; }
.vitals__actions { flex: 0 0 auto; }

@media (max-width: 900px) { .vitals { padding-inline: var(--s4); } }
@media (max-width: 720px) {
  /* A keyboard hint is noise on a device with no keyboard. */
  .vitals__hint, .vitals__searchlabel { display: none; }
}

/* Each reading owns a slot with its own label. Not a string of values joined
   by middle dots — that reads as one sentence when it is six measurements. */
.vital {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 var(--s5);
  min-width: 0;
  border-right: 1px solid var(--hair);
}
.vital:first-child { padding-left: 0; }
.vital:last-of-type { border-right: 0; }
.vital__value {
  font-size: var(--t-data);
  font-weight: var(--w-semibold);
  line-height: 1.1;
  letter-spacing: -0.01em;
  display: flex;
  align-items: baseline;
  gap: 3px;
}
.vital__unit { font-size: var(--t-cap2); font-weight: var(--w-regular); color: var(--mist); }
.vital__label { font-size: var(--t-cap2); color: var(--dim); line-height: 1.2; margin-top: 1px; }
.vital--wide { flex: 1 1 auto; }

@media (max-width: 1200px) { .vital[data-optional="1"] { display: none; } }
@media (max-width: 820px) { .vital[data-optional="2"] { display: none; } }

/* A heartbeat, not a decoration: it stops when the pipeline stops. */
.pulse { position: relative; width: 8px; height: 8px; flex: 0 0 auto; }
.pulse__core { position: absolute; inset: 1px; border-radius: 50%; background: var(--live); }
.pulse__ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid var(--live);
  animation: pulse-out 2s var(--e-standard) infinite;
}
.pulse[data-state="stalled"] .pulse__core { background: var(--alarm); }
.pulse[data-state="stalled"] .pulse__ring { border-color: var(--alarm); animation: none; }

/* ---- panel ------------------------------------------------------------- */
.panel {
  background: var(--slate);
  border: 1px solid var(--hair);
  border-radius: var(--r-panel);
  min-width: 0;
}
.panel__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--hair);
}
.panel__title { font-size: var(--t-headline); font-weight: var(--w-semibold); letter-spacing: -0.004em; }
.panel__why { font-size: var(--t-cap); color: var(--dim); }
.panel__body { padding: var(--s5); }
.panel__body--flush { padding: 0; }

/* ---- the hero: live intake -------------------------------------------- */
.intake { position: relative; overflow: hidden; border-radius: var(--r-panel); }
/* The chart runs to the panel edge — radius 0 on the plot itself. A chart
   inset inside a rounded card wastes the width that makes a trend readable. */
.intake__plot { display: block; width: 100%; height: 232px; }
.intake__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s5);
  padding: var(--s5) var(--s5) 0;
  flex-wrap: wrap;
}
.intake__figure { display: flex; align-items: baseline; gap: var(--s2); }
.intake__big {
  font-size: 44px;
  font-weight: var(--w-semibold);
  letter-spacing: -0.03em;
  line-height: 1;
}
.intake__legend { display: flex; gap: var(--s4); flex-wrap: wrap; }
.intake__key { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-cap); color: var(--mist); }
.intake__swatch { width: 10px; height: 3px; border-radius: 2px; flex: 0 0 auto; }

.intake__cursor { pointer-events: none; }
.intake__tip {
  position: absolute;
  pointer-events: none;
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  padding: var(--s2) var(--s3);
  font-size: var(--t-cap);
  box-shadow: var(--lift-2);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--d-micro) var(--e-standard), transform var(--d-micro) var(--e-standard);
}
.intake__tip[data-show="1"] { opacity: 1; transform: translateY(0); }

/* ---- pipeline: stages are a real sequence, so they get connectors ----- */
.flow { display: flex; flex-direction: column; }
.stage {
  position: relative;
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) 0;
}
.stage__glyph {
  position: relative;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--shelf);
  border: 1px solid var(--hair-strong);
  color: var(--mist);
  z-index: 1;
}
/* The connector is the information: it shows work flowing from one stage to
   the next, and its label is the rate. */
.stage:not(:last-child) .stage__glyph::after {
  content: "";
  position: absolute;
  top: 100%; left: 50%;
  width: 1px; height: calc(var(--s3) * 2 + 20px);
  background: linear-gradient(var(--hair-strong), var(--hair));
  transform: translateX(-50%);
}
.stage__name { font-size: var(--t-data); color: var(--ice); font-weight: var(--w-medium); }
.stage__rate { font-size: var(--t-cap2); color: var(--dim); }
.stage__value { font-size: var(--t-data); font-weight: var(--w-semibold); text-align: right; }
.stage[data-attention="1"] .stage__glyph { border-color: var(--ember); color: var(--ember); background: var(--ember-soft); }
.stage[data-attention="1"] .stage__value { color: var(--ember); }

/* ---- triage: "what needs you" ----------------------------------------- */
.triage { display: flex; flex-direction: column; }
.triage__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s5);
  border-bottom: 1px solid var(--hair);
  text-align: left;
  width: 100%;
  transition: background-color var(--d-micro) var(--e-standard);
}
.triage__item:last-child { border-bottom: 0; }
.triage__item:hover { background: var(--shelf); }
.triage__count {
  font-size: var(--t-t3);
  font-weight: var(--w-semibold);
  letter-spacing: -0.01em;
  min-width: 3ch;
  text-align: right;
}
.triage__what { font-size: var(--t-data); }
.triage__why { font-size: var(--t-cap); color: var(--dim); margin-top: 1px; }
.triage__item[data-tone="ember"] .triage__count { color: var(--ember); }
.triage__item[data-tone="alarm"] .triage__count { color: var(--alarm); }
.triage__item[data-tone="signal"] .triage__count { color: var(--signal); }
.triage__go { color: var(--dim); transition: color var(--d-micro) var(--e-standard), transform var(--d-fast) var(--e-snappy); }
.triage__item:hover .triage__go { color: var(--ice); transform: translateX(2px); }

/* ---- controls ---------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 34px;
  min-width: 34px;
  padding: 0 var(--s3);
  border-radius: var(--r-control);
  border: 1px solid var(--hair-strong);
  background: var(--shelf);
  color: var(--ice);
  font-size: var(--t-foot);
  font-weight: var(--w-medium);
  white-space: nowrap;
  position: relative;
  transition: background-color var(--d-micro) var(--e-standard),
              border-color var(--d-micro) var(--e-standard),
              transform var(--d-micro) var(--e-snappy),
              opacity var(--d-micro) var(--e-standard);
}
.btn:hover { border-color: var(--dim); background: var(--hair); }
/* Press is a scale, not a colour change: it reads as the surface giving way
   under the pointer, which is what a press is. */
.btn:active { transform: scale(0.97); }
.btn[disabled] { opacity: 0.45; pointer-events: none; }

.btn--primary { background: var(--signal); border-color: var(--signal); color: #04131c; font-weight: var(--w-semibold); }
.btn--primary:hover { background: color-mix(in oklab, var(--signal) 88%, white); border-color: var(--signal); }
:root[data-theme="light"] .btn--primary { color: #ffffff; }
.btn--quiet { background: transparent; border-color: transparent; color: var(--mist); }
.btn--quiet:hover { background: var(--shelf); color: var(--ice); border-color: transparent; }
.btn--danger { color: var(--alarm); border-color: var(--alarm-soft); background: var(--alarm-soft); }
.btn--danger:hover { border-color: var(--alarm); background: var(--alarm-soft); }
.btn--block { width: 100%; min-height: 40px; }

.field { position: relative; display: flex; align-items: center; }
.field__icon { position: absolute; left: var(--s3); color: var(--dim); pointer-events: none; display: flex; }
.input {
  width: 100%;
  min-height: 34px;
  padding: 0 var(--s3);
  background: var(--shelf);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  color: var(--ice);
  font-size: var(--t-foot);
  transition: border-color var(--d-micro) var(--e-standard),
              background-color var(--d-micro) var(--e-standard),
              box-shadow var(--d-micro) var(--e-standard);
}
.input::placeholder { color: var(--dim); }
.input:hover { border-color: var(--dim); }
.input:focus { outline: none; border-color: var(--signal); box-shadow: 0 0 0 3px var(--signal-soft); }
.field--icon .input { padding-left: calc(var(--s3) * 2 + 14px); }
.input--lg { min-height: 44px; font-size: var(--t-body); padding-inline: var(--s4); }
.field--icon:has(.input--lg) .input { padding-left: calc(var(--s4) * 2 + 14px); }
.field--icon:has(.input--lg) .field__icon { left: var(--s4); }

.select { appearance: none; padding-right: calc(var(--s3) * 2 + 10px); cursor: pointer; }
.field__caret { position: absolute; right: var(--s3); color: var(--dim); pointer-events: none; display: flex; }

/* Segmented control, as on Apple platforms: the thumb slides. */
.segment {
  display: inline-flex;
  position: relative;
  padding: 2px;
  background: var(--shelf);
  border: 1px solid var(--hair);
  border-radius: var(--r-control);
}
.segment__thumb {
  position: absolute;
  top: 2px; bottom: 2px;
  border-radius: 6px;
  background: var(--slate);
  box-shadow: var(--lift-1);
  transition: transform var(--d-spring) var(--e-spring), width var(--d-fast) var(--e-standard);
  will-change: transform;
}
.segment__opt {
  position: relative;
  z-index: 1;
  min-height: 28px;
  padding: 0 var(--s3);
  font-size: var(--t-cap);
  font-weight: var(--w-medium);
  color: var(--mist);
  border-radius: 6px;
  transition: color var(--d-fast) var(--e-standard);
}
.segment__opt[aria-selected="true"] { color: var(--ice); }

/* ---- pills: status and findings --------------------------------------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  height: 20px;
  padding: 0 var(--s2);
  border-radius: var(--r-chip);
  font-size: var(--t-cap2);
  font-weight: var(--w-medium);
  white-space: nowrap;
  background: var(--shelf);
  color: var(--mist);
  border: 1px solid transparent;
}
.pill--live { background: var(--live-soft); color: var(--live); }
.pill--signal { background: var(--signal-soft); color: var(--signal); }
.pill--ember { background: var(--ember-soft); color: var(--ember); }
.pill--alarm { background: var(--alarm-soft); color: var(--alarm); }
.pill__dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }

/* ---- tables: no card, hairline rules, data right-aligned -------------- */
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--t-data); }
.table th {
  position: sticky;
  top: 0;
  z-index: 2;
  text-align: left;
  font-size: var(--t-cap);
  font-weight: var(--w-medium);
  color: var(--dim);
  padding: var(--s3) var(--s4);
  background: var(--slate);
  border-bottom: 1px solid var(--hair);
  white-space: nowrap;
}
.table th[data-sortable] { cursor: pointer; user-select: none; }
.table th[data-sortable]:hover { color: var(--ice); }
.table td {
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--hair);
  vertical-align: middle;
}
.table tbody tr { transition: background-color var(--d-micro) var(--e-standard); }
.table tbody tr:hover { background: var(--shelf); }
.table tbody tr[data-clickable] { cursor: pointer; }
.table .right { text-align: right; }

/* Columns worth losing before the table starts scrolling sideways. A phone
   shows what the file IS and what was found in it; the type pill and the
   timestamp are the first things a narrow screen can do without. */
@media (max-width: 860px) {
  .table [data-opt="1"] { display: none; }
}
@media (max-width: 680px) {
  .table [data-opt="2"] { display: none; }
}
.table .nowrap { white-space: nowrap; }
.table tbody tr:last-child td { border-bottom: 0; }

/* A row's severity is a dot AND a weight change, never colour alone. */
.sev { display: inline-flex; align-items: center; gap: var(--s2); }
.sev__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--hair-strong); flex: 0 0 auto; }
.sev[data-tone="ember"] .sev__dot { background: var(--ember); box-shadow: 0 0 0 3px var(--ember-soft); }
.sev[data-tone="alarm"] .sev__dot { background: var(--alarm); box-shadow: 0 0 0 3px var(--alarm-soft); }
.sev[data-tone="live"] .sev__dot { background: var(--live); box-shadow: 0 0 0 3px var(--live-soft); }
.sev[data-tone="ember"] .sev__text,
.sev[data-tone="alarm"] .sev__text { font-weight: var(--w-semibold); }

/* ---- instruments: a readout grid, hairlines not cards ----------------- */
.instruments {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
}
.instrument {
  padding: var(--s4) var(--s5);
  border-right: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  min-width: 0;
}
.instrument__value { font-size: var(--t-t2); font-weight: var(--w-semibold); letter-spacing: -0.014em; line-height: 1.1; }
.instrument__label { font-size: var(--t-cap); color: var(--dim); margin-top: var(--s1); }
.instrument__delta { font-size: var(--t-cap2); margin-top: var(--s1); display: flex; align-items: center; gap: 3px; }
.instrument__delta[data-dir="up"] { color: var(--live); }
.instrument__delta[data-dir="down"] { color: var(--alarm); }
.instrument__delta[data-dir="flat"] { color: var(--dim); }

/* ---- sheet / modal ----------------------------------------------------- */
.scrim {
  position: fixed; inset: 0; z-index: 100;
  background: var(--scrim);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  display: grid; place-items: center;
  padding: var(--s5);
}
.sheet {
  width: min(560px, 100%);
  max-height: min(80vh, 720px);
  overflow: auto;
  background: var(--slate);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-sheet);
  box-shadow: var(--lift-3);
}
.sheet__head { padding: var(--s5) var(--s5) var(--s3); }
.sheet__body { padding: 0 var(--s5) var(--s5); }
.sheet__foot {
  display: flex; justify-content: flex-end; gap: var(--s2);
  padding: var(--s4) var(--s5);
  border-top: 1px solid var(--hair);
  position: sticky; bottom: 0;
  background: var(--slate);
}

/* ---- empty states are an invitation to act, not a shrug --------------- */
.empty { display: grid; place-items: center; text-align: center; padding: var(--s8) var(--s5); gap: var(--s3); }
/* An empty state that is the WHOLE screen deserves the room. One inside a
   secondary section does not — a 300px box to say "no file" shouts louder
   than the thing it is reporting on. */
.empty--compact { padding: var(--s5) var(--s4); gap: var(--s2); }
.empty--compact .empty__what { font-size: var(--t-foot); }
.empty--compact .empty__why { font-size: var(--t-cap); }
.empty__art { color: var(--hair-strong); }
.empty__what { font-size: var(--t-headline); font-weight: var(--w-semibold); }
.empty__why { font-size: var(--t-foot); color: var(--mist); max-width: 46ch; }

/* ---- skeletons -------------------------------------------------------
   A whole-page skeleton, shown the instant a nav item is clicked so the
   click has an answer. Before this the router awaited the view's data and
   left the PREVIOUS screen on display, which read as a dead click — and
   since the rail highlighted the new tab immediately, it read as the wrong
   page rather than a slow one. People clicked again.

   The delay is the whole trick. A screen that answers in 60ms must not
   flash a skeleton on its way in; one that takes two seconds has to say so
   at once. Invisible for 90ms, fully there by 230ms: under the first
   threshold nothing is seen, past it the page is already shaped. */
.page-skeleton {
  animation: skel-in 140ms var(--e-standard) 90ms both;
}

@keyframes skel-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ---- skeletons: only for waits over ~300ms --------------------------- */
.skeleton {
  background: linear-gradient(90deg, var(--shelf) 25%, var(--hair) 50%, var(--shelf) 75%);
  background-size: 200% 100%;
  border-radius: var(--r-control);
  animation: shimmer 1.4s linear infinite;
}

/* ---- findings: grouped evidence from a file -------------------------- */
.finds { display: grid; gap: var(--s2); }
.find {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s3);
  align-items: center;
  padding: var(--s3);
  background: var(--shelf);
  border: 1px solid var(--hair);
  border-radius: var(--r-control);
}
.find__val { font-family: var(--f-mono); font-size: var(--t-foot); overflow-wrap: anywhere; }
.find__meta { font-size: var(--t-cap2); color: var(--dim); }

.kv { display: grid; grid-template-columns: minmax(96px, auto) minmax(0, 1fr); gap: var(--s2) var(--s4); font-size: var(--t-foot); }
.kv__k { color: var(--dim); }
.kv__v { overflow-wrap: anywhere; }

/* ---- toast ------------------------------------------------------------- */
.toasts {
  position: fixed;
  bottom: var(--s5); right: var(--s5);
  z-index: 200;
  display: flex; flex-direction: column; gap: var(--s2);
  pointer-events: none;
}
.toast {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  box-shadow: var(--lift-2);
  font-size: var(--t-foot);
  pointer-events: auto;
}
.toast[data-tone="ember"] { border-color: var(--ember); }
.toast[data-tone="alarm"] { border-color: var(--alarm); }

/* ---- grids used by views ---------------------------------------------- */
.grid-now {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 312px;
  gap: var(--s5);
  align-items: start;
}
@media (max-width: 1100px) { .grid-now { grid-template-columns: minmax(0, 1fr); } }

.grid-detail {
  display: grid;
  grid-template-columns: 380px minmax(0, 1fr);
  gap: var(--s5);
  align-items: start;
}
@media (max-width: 1000px) { .grid-detail { grid-template-columns: minmax(0, 1fr); } }

.pager { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); padding: var(--s3) var(--s5); border-top: 1px solid var(--hair); }
.pager__text { font-size: var(--t-cap); color: var(--dim); }

/* A glyph reused in the opposite direction. Cheaper than a second path, and
   it stays in sync if the chevron is ever redrawn. */
.rot180 { transform: rotate(180deg); }
.rot90 { transform: rotate(90deg); }
.rot270 { transform: rotate(270deg); }

/* ===========================================================================
   Touch
   ---------------------------------------------------------------------------
   Apple's 44pt floor is about fingers. Applied unconditionally it makes a
   pointer-driven console feel inflated, so it is applied where it means
   something: on a coarse pointer every control grows to meet it.
   ========================================================================= */
@media (pointer: coarse) {
  .btn,
  .input,
  .nav__item,
  .segment__opt { min-height: var(--hit); }
  .segment__opt { padding-inline: var(--s4); }
  .triage__item { padding-block: var(--s4); }
  .table td { padding-block: var(--s4); }
}

/* ===========================================================================
   Monitoring
   ========================================================================= */

/* ---- meter: a capacity bar that answers WHEN, not just how much ---------
   A percentage is a static fact. The question an administrator actually has
   is "how long have I got", so the headline is the runway and the bar carries
   a ghost segment showing where the fill lands in 90 days. */
.meter + .meter { margin-top: var(--s4); }
.meter__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s2);
}
.meter__name { font-size: var(--t-data); font-weight: var(--w-medium); }
.meter__detail { font-size: var(--t-cap2); color: var(--dim); margin-top: 1px; }
.meter__amount { font-size: var(--t-foot); color: var(--mist); white-space: nowrap; font-variant-numeric: tabular-nums; }

.meter__track {
  position: relative;
  height: 10px;
  border-radius: var(--r-chip);
  background: var(--shelf);
  overflow: hidden;
}
.meter__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--r-chip);
  background: var(--signal);
  width: 0;
  transition: width var(--d-slow) var(--e-standard);
}
/* The projection sits beyond the fill, separated by a 2px surface gap so the
   two read as distinct quantities rather than one blended bar. */
.meter__ghost {
  position: absolute;
  top: 0; bottom: 0;
  border-radius: var(--r-chip);
  background: repeating-linear-gradient(
    135deg,
    color-mix(in oklab, var(--signal) 40%, transparent) 0 3px,
    transparent 3px 6px);
  border-left: 2px solid var(--slate);
  width: 0;
  transition: width var(--d-slow) var(--e-standard) 120ms, left var(--d-slow) var(--e-standard);
}
.meter[data-tone="ember"] .meter__fill { background: var(--ember); }
.meter[data-tone="ember"] .meter__ghost { background-image: repeating-linear-gradient(135deg, color-mix(in oklab, var(--ember) 40%, transparent) 0 3px, transparent 3px 6px); }
.meter[data-tone="alarm"] .meter__fill { background: var(--alarm); }
.meter[data-tone="alarm"] .meter__ghost { background-image: repeating-linear-gradient(135deg, color-mix(in oklab, var(--alarm) 40%, transparent) 0 3px, transparent 3px 6px); }

.meter__foot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3); margin-top: var(--s2);
  font-size: var(--t-cap2);
}
.meter__runway { color: var(--mist); }
.meter[data-tone="ember"] .meter__runway { color: var(--ember); font-weight: var(--w-medium); }
.meter[data-tone="alarm"] .meter__runway { color: var(--alarm); font-weight: var(--w-semibold); }
.meter__rate { color: var(--dim); font-variant-numeric: tabular-nums; }

/* ---- paired bars: two measures per row, directly labelled --------------
   Not a stacked bar and not a pie: the whole point is the DIVERGENCE between
   the two measures for the same category, and two bars on a shared scale is
   the only form that shows it without arithmetic. */
.pair { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: var(--s3); align-items: center; }
.pair + .pair { margin-top: var(--s3); }
.pair__label { font-size: var(--t-foot); }
.pair__note { font-size: var(--t-cap2); color: var(--dim); margin-top: 1px; }
.pair__bars { display: grid; gap: 3px; min-width: 0; }
.pair__bar { position: relative; height: 9px; background: var(--shelf); border-radius: 0 var(--r-control) var(--r-control) 0; }
.pair__val {
  position: absolute; inset: 0 auto 0 0;
  /* Rounded only at the data end, anchored flat to the baseline. */
  border-radius: 0 var(--r-control) var(--r-control) 0;
  width: 0;
  transition: width var(--d-slow) var(--e-standard);
}
.pair__val[data-series="a"] { background: var(--chart-a); }
.pair__val[data-series="b"] { background: var(--chart-b); }
.pair__tick { font-size: var(--t-cap2); color: var(--mist); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pair__row { display: grid; grid-template-columns: minmax(0, 1fr) 42px; gap: var(--s2); align-items: center; }

.legend { display: flex; gap: var(--s4); flex-wrap: wrap; }
.legend__key { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-cap); color: var(--mist); }
.legend__swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }

/* ---- ordinal stack: one hue in steps, 2px surface gaps ----------------- */
.stack-bar { display: flex; height: 14px; border-radius: var(--r-chip); overflow: hidden; background: var(--shelf); gap: 2px; }
.stack-bar__seg { min-width: 2px; transition: width var(--d-slow) var(--e-standard); }
.stack-bar__seg:first-child { border-radius: var(--r-chip) 0 0 var(--r-chip); }
.stack-bar__seg:last-child { border-radius: 0 var(--r-chip) var(--r-chip) 0; }
.stack-key { display: grid; grid-template-columns: 10px minmax(0,1fr) auto; gap: var(--s3); align-items: baseline; }
.stack-key + .stack-key { margin-top: var(--s3); }
.stack-key__swatch { width: 10px; height: 10px; border-radius: 3px; margin-top: 3px; }
.stack-key__label { font-size: var(--t-foot); }
.stack-key__why { font-size: var(--t-cap2); color: var(--dim); margin-top: 1px; }
.stack-key__n { font-size: var(--t-foot); font-weight: var(--w-medium); font-variant-numeric: tabular-nums; }

/* ---- yield table: files, bytes and what they produced ------------------ */
.yield { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--t-data); }
.yield th {
  text-align: right; font-size: var(--t-cap); font-weight: var(--w-medium);
  color: var(--dim); padding: 0 var(--s3) var(--s2); white-space: nowrap;
}
.yield th:first-child, .yield td:first-child { text-align: left; }
.yield td { padding: var(--s2) var(--s3); text-align: right; border-top: 1px solid var(--hair); font-variant-numeric: tabular-nums; }
.yield tfoot td { border-top: 1px solid var(--hair-strong); color: var(--mist); padding-top: var(--s3); }
.yield__bar { position: relative; height: 7px; background: var(--shelf); border-radius: 0 4px 4px 0; min-width: 60px; }
.yield__bar > span { position: absolute; inset: 0 auto 0 0; border-radius: 0 4px 4px 0; background: var(--chart-b); width: 0; transition: width var(--d-slow) var(--e-standard); }

/* ---- sparkline ---------------------------------------------------------- */
.spark { display: block; width: 100%; height: 26px; }

/* ---- fleet rows --------------------------------------------------------- */
.host {
  display: grid;
  grid-template-columns: 150px 96px minmax(0, 1fr) auto;
  gap: var(--s4);
  align-items: center;
  padding: var(--s3) var(--s5);
  border-bottom: 1px solid var(--hair);
}
.host:last-child { border-bottom: 0; }
.host__name { font-size: var(--t-data); font-weight: var(--w-medium); }
.host__role { font-size: var(--t-cap2); color: var(--dim); margin-top: 1px; }
.host__load { font-size: var(--t-t3); font-weight: var(--w-semibold); line-height: 1; letter-spacing: -0.01em; }
.host__loadlabel { font-size: var(--t-cap2); color: var(--dim); }
.host__note { font-size: var(--t-cap2); color: var(--dim); }
@media (max-width: 1000px) {
  .host { grid-template-columns: minmax(0, 1fr) auto; }
  .host__spark, .host__note { display: none; }
}

/* ---- backlog chart ------------------------------------------------------ */
.backlog__plot { display: block; width: 100%; height: 180px; }
.backlog__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s3); }
.backlog__verdict { font-size: var(--t-t3); font-weight: var(--w-semibold); letter-spacing: -0.01em; }
.backlog__verdict[data-dir="draining"] { color: var(--live); }
/* Ember, not alarm: a queue gaining depth wants attention, it is not an
   outage — and it has to match the projection line drawn in the chart. */
.backlog__verdict[data-dir="growing"] { color: var(--ember); }
.backlog__verdict[data-dir="holding"] { color: var(--ice); }

/* ---- rank list (targeted services) -------------------------------------- */
.rank { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: var(--s3); align-items: center; padding: var(--s2) 0; }
.rank + .rank { border-top: 1px solid var(--hair); }
.rank__bar { position: relative; height: 6px; background: var(--shelf); border-radius: 0 3px 3px 0; margin-top: var(--s1); }
.rank__bar > span { position: absolute; inset: 0 auto 0 0; background: var(--chart-a); border-radius: 0 3px 3px 0; width: 0; transition: width var(--d-slow) var(--e-standard); }
.rank__name { font-size: var(--t-foot); font-family: var(--f-mono); font-size: var(--t-cap); }
.rank__n { font-size: var(--t-cap); color: var(--mist); font-variant-numeric: tabular-nums; }

/* ---- a rank list that picks what the chart beside it shows ---------------
   The list already names every queue and gives its depth, so a row of tabs
   above the chart would be the same eight words twice on one screen. The list
   becomes the control instead: pick on the left, read on the right. */
.rank--pick {
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-radius: var(--r-control);
  color: inherit;
  font: inherit;
  cursor: pointer;
  padding: var(--s2) var(--s3);
  /* The marker sits in the gutter, so selecting a row must not move its text. */
  margin-inline: calc(var(--s3) * -1);
  width: calc(100% + var(--s3) * 2);
  position: relative;
  transition: background var(--d-fast) var(--e-standard);
}
.rank--pick + .rank--pick { border-top: 1px solid var(--hair); }
.rank--pick:hover { background: var(--shelf); }
.rank--pick:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
.rank--pick::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 2px; height: 0;
  background: var(--signal);
  border-radius: 0 2px 2px 0;
  transform: translateY(-50%);
  transition: height var(--d-base) var(--e-spring);
}
.rank--pick[aria-pressed="true"]::before { height: 62%; }
.rank--pick[aria-pressed="true"] { background: var(--shelf); }
.rank--pick[aria-pressed="true"] .rank__label { color: var(--ice); font-weight: var(--w-medium); }
.rank--pick[aria-pressed="true"] .rank__n { color: var(--ice); }
.rank__label { color: var(--mist); transition: color var(--d-fast) var(--e-standard); }

/* A queue row is a label, its shape over three weeks, and its depth.
   It carries a sparkline rather than a bar because these depths run from 318
   to 80 million AND count different things — files, scrapes, tokens — so a
   bar scaled across them encodes nothing except which row is the media queue.
   The shape of each queue against itself is the thing worth comparing. */
.rank--pick { grid-template-columns: minmax(0, 1fr) auto auto; }
.spark { display: block; width: 64px; height: 18px; overflow: visible; }
.rank__trend { display: inline-flex; align-items: center; gap: 3px; }
.rank__trend[data-dir="draining"] { color: var(--live); }
.rank__trend[data-dir="growing"] { color: var(--ember); }
.rank__trend[data-dir="holding"] { color: var(--dim); }

/* ===========================================================================
   Sections
   ---------------------------------------------------------------------------
   The monitoring screen is a DOCUMENT, not a deck of cards.
   Nine bordered, equally-rounded, equally-shadowed boxes down a page is the
   stock SaaS-dashboard kit, and it flattens hierarchy: every box claims the
   same importance, so none of them has any. A section is a rule, a heading
   and its content sitting on the page itself. The bordered panel is kept for
   things that genuinely are discrete objects — a list you act on, a callout
   that interrupts — so when a border does appear it means something.
   ========================================================================= */

.section { padding-top: var(--s6); margin-top: var(--s6); border-top: 1px solid var(--hair); }
.section:first-child { border-top: 0; margin-top: 0; padding-top: 0; }

.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s5);
  margin-bottom: var(--s5);
  flex-wrap: wrap;
}
.section__title { font-size: var(--t-t3); font-weight: var(--w-semibold); letter-spacing: -0.008em; }
.section__why { font-size: var(--t-foot); color: var(--mist); margin-top: var(--s1); max-width: var(--max-measure); }
.section__aside { font-size: var(--t-foot); color: var(--dim); white-space: nowrap; }

/* Two columns that collapse together, with a rule between them rather than a
   gutter of nothing — the rule is what keeps them legible as two readings of
   the same subject. */
.cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s6);
}
.cols > * + * {
  padding-left: var(--s6);
  border-left: 1px solid var(--hair);
  margin-left: calc(var(--s6) * -1 + var(--s6));
}
.cols--wide { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
/* A picker and what it drives: the list is only as wide as its longest label
   plus a bar, and everything else goes to the chart. */
.cols--pick { grid-template-columns: minmax(0, 280px) minmax(0, 1fr); }
/* Columns of different lengths: without this the shorter one's divider
   runs the full height of the taller one. */
.cols--top { align-items: start; }
@media (max-width: 1100px) {
  .cols, .cols--wide, .cols--pick { grid-template-columns: minmax(0, 1fr); gap: var(--s5); }
  .cols > * + * { padding-left: 0; border-left: 0; padding-top: var(--s5); border-top: 1px solid var(--hair); }
}

/* A callout interrupts the reading, so unlike a section it keeps a border. */
.callout {
  padding: var(--s3) var(--s4);
  border-radius: var(--r-control);
  border: 1px solid var(--alarm);
  background: var(--alarm-soft);
}
.callout__what { font-size: var(--t-foot); font-weight: var(--w-semibold); }
.callout__why { font-size: var(--t-cap); color: var(--mist); margin-top: 2px; }

/* ---- a message, and the span a rule matched ---------------------------- */
.msg-text {
  font-size: var(--t-body);
  line-height: 1.6;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
/* The matched span. <mark> carries the meaning for a screen reader; the
   styling only makes it visible. Not a colour alone — it is also boxed, so it
   survives a monochrome screenshot. */
.hit {
  background: var(--ember-soft);
  color: var(--ember);
  border-radius: 3px;
  padding: 1px 3px;
  box-shadow: inset 0 0 0 1px var(--ember);
}
:root[data-theme="light"] .hit { color: var(--ember); }

/* The pattern itself, shown as the machine-generated string it is. */
.pattern {
  font-family: var(--f-mono);
  font-size: var(--t-foot);
  background: var(--shelf);
  border: 1px solid var(--hair);
  border-left: 2px solid var(--signal);
  border-radius: var(--r-control);
  padding: var(--s3);
  margin-top: var(--s2);
  overflow-wrap: anywhere;
}
.matchbox {
  display: inline-block;
  background: var(--ember-soft);
  border: 1px solid var(--ember);
  border-radius: var(--r-control);
  padding: var(--s2) var(--s3);
  font-size: var(--t-foot);
}

/* ---- figures: the headline scale of the corpus -------------------------
   Larger than .instrument because this answers "how big is this system",
   which is the first thing anyone wants and was previously buried at the
   bottom of the page. Each cell carries one derived figure beneath so the
   ratios are read in place rather than worked out. */
.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
}
.figure {
  padding: var(--s4) var(--s5);
  border-right: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  min-width: 0;
}

.figure__value {
  font-size: var(--t-t1);
  font-weight: var(--w-semibold);
  letter-spacing: -0.02em;
  line-height: 1.05;
}
.figure__label { font-size: var(--t-foot); color: var(--ice); margin-top: var(--s2); }
.figure__sub { font-size: var(--t-cap); color: var(--dim); margin-top: 2px; }

/* Figures are readings, not controls. A figure was briefly also a link, with
   the hover lift as its only affordance — invisible beside four identical
   cells that were not links, so the pivot it offered was never found. The way
   into what a rule flagged is .pivot below, which looks like what it is. */

/* ---- rail controls ------------------------------------------------------
   Collapsing the sidebar and signing out are not places you navigate to, so
   they are not listed beside Files and Bots. */
.rail__foot { padding: 0 var(--s1); }
.rail__btn {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-control);
  color: var(--dim);
  transition: color var(--d-micro) var(--e-standard), background-color var(--d-micro) var(--e-standard);
}
.rail__btn:hover { color: var(--ice); background: var(--shelf); }
.app[data-rail="collapsed"] .rail__foot { flex-direction: column; align-items: center; padding-inline: 0; }
@media (pointer: coarse) { .rail__btn { width: var(--hit); height: var(--hit); } }
@media (max-width: 900px) { .rail__foot { flex-direction: column; align-items: center; padding-inline: 0; } }

/* ===========================================================================
   Investigation dock
   ---------------------------------------------------------------------------
   Overlays the bottom of the content area, never the rail. It is a panel
   rather than a route because an investigation is comparative: two chats open
   at once, jump to the messages of one, come back and the other is still
   there. Tabs behave like a browser's.
   ========================================================================= */

:root { --dock-bar: 38px; }

.dock {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: grid;
  grid-template-rows: 6px var(--dock-bar) minmax(0, 1fr);
  height: calc(var(--dock-h, 320px) + var(--dock-bar) + 6px);
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  border-top: 1px solid var(--hair-strong);
  box-shadow: var(--lift-3);
  animation: rise var(--d-base) var(--e-standard);
}
.dock[hidden] { display: none; }
.dock[data-collapsed="true"] { height: calc(var(--dock-bar) + 6px); }
.dock[data-collapsed="true"] .dock__body { display: none; }

.dock__grip { cursor: ns-resize; background: transparent; position: relative; }
.dock__grip::after {
  content: "";
  position: absolute;
  left: 50%; top: 2px;
  width: 36px; height: 3px;
  transform: translateX(-50%);
  border-radius: var(--r-chip);
  background: var(--hair-strong);
  transition: background-color var(--d-micro) var(--e-standard);
}
.dock__grip:hover::after, .dock__grip:focus-visible::after { background: var(--signal); }
.dock[data-collapsed="true"] .dock__grip { cursor: default; }

.dock__bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: stretch;
  border-bottom: 1px solid var(--hair);
  min-width: 0;
}
.dock__tabs { display: flex; align-items: stretch; overflow-x: auto; scrollbar-width: none; }
.dock__tabs::-webkit-scrollbar { display: none; }

.dock__tab {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  max-width: 210px;
  border-right: 1px solid var(--hair);
  color: var(--mist);
  transition: background-color var(--d-micro) var(--e-standard), color var(--d-micro) var(--e-standard);
}
.dock__tab:hover { background: var(--shelf); color: var(--ice); }
/* The selected tab is marked on its top edge, the way a browser does it, so
   it reads as the front of a stack rather than just a highlighted button. */
.dock__tab[data-active="true"] {
  background: var(--slate);
  color: var(--ice);
  box-shadow: inset 0 2px 0 var(--signal);
}
.dock__tabname {
  display: flex; align-items: center; gap: var(--s2);
  padding: 0 var(--s2) 0 var(--s3);
  height: 100%;
  font-size: var(--t-cap);
  min-width: 0;
}
.dock__x {
  display: grid; place-items: center;
  width: 20px; height: 20px;
  margin-right: 6px;
  border-radius: 4px;
  color: var(--dim);
  flex: 0 0 auto;
}
.dock__x:hover { background: var(--hair); color: var(--ice); }

.dock__acts { display: flex; align-items: center; gap: 2px; padding: 0 var(--s2); border-left: 1px solid var(--hair); }
.dock__act {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border-radius: var(--r-control);
  color: var(--dim);
  transition: color var(--d-micro) var(--e-standard), background-color var(--d-micro) var(--e-standard),
              transform var(--d-fast) var(--e-standard);
}
.dock__act:hover { color: var(--ice); background: var(--shelf); }

.dock__body { overflow-y: auto; min-height: 0; }
.dock__pane { padding: var(--s5); }
.dock__loading { padding: var(--s5); color: var(--dim); font-size: var(--t-foot); }

.dock__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s5); flex-wrap: wrap; margin-bottom: var(--s4);
}
.dock__sub { font-size: var(--t-cap); color: var(--dim); margin-bottom: var(--s2); }
.dock__link {
  font-size: var(--t-foot);
  color: var(--signal);
  text-align: left;
  min-width: 0;
}
.dock__link:hover { text-decoration: underline; }

.dock__figs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
}
.dock__fig { padding: var(--s3) var(--s4); border-right: 1px solid var(--hair); border-bottom: 1px solid var(--hair); }
.dock__figv { font-size: var(--t-t3); font-weight: var(--w-semibold); letter-spacing: -0.012em; line-height: 1.1; }
.dock__figl { font-size: var(--t-cap2); color: var(--dim); margin-top: 2px; }

@media (max-width: 760px) { .dock__tab { max-width: 150px; } }

/* ---- anchored menu ------------------------------------------------------ */
.menu {
  position: fixed;
  z-index: 300;
  min-width: 184px;
  padding: var(--s1);
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  box-shadow: var(--lift-3);
  animation: menu-in var(--d-fast) var(--e-standard);
  transform-origin: bottom left;
}
.menu[data-closing="1"] { animation: menu-in 120ms var(--e-exit) reverse forwards; }
@keyframes menu-in {
  from { opacity: 0; transform: translateY(4px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
.menu__item {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  min-height: 32px;
  padding: 0 var(--s3);
  border-radius: 6px;
  font-size: var(--t-foot);
  color: var(--ice);
  text-align: left;
  transition: background-color var(--d-micro) var(--e-standard);
}
.menu__item:hover, .menu__item:focus-visible { background: var(--shelf); }
.menu__hint { margin-left: auto; font-size: var(--t-cap2); color: var(--dim); }
.menu__rule { height: 1px; margin: var(--s1) 0; background: var(--hair); }

/* ---- rail header and session row ---------------------------------------- */
.rail__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s1) 0 var(--s2);
}
.rail__toggle {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border-radius: var(--r-control);
  color: var(--dim);
  flex: 0 0 auto;
  transition: color var(--d-micro) var(--e-standard),
              background-color var(--d-micro) var(--e-standard),
              transform var(--d-base) var(--e-spring);
}
.rail__toggle:hover { color: var(--ice); background: var(--shelf); }
.app[data-rail="collapsed"] .rail__toggle { transform: rotate(180deg); }
.app[data-rail="collapsed"] .rail__head { grid-template-columns: 1fr; justify-items: center; gap: var(--s3); }

.session {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  min-height: 42px;
  padding: 0 var(--s2);
  border-radius: var(--r-control);
  color: var(--mist);
  transition: background-color var(--d-micro) var(--e-standard), color var(--d-micro) var(--e-standard);
}
.session:hover { background: var(--shelf); color: var(--ice); }
.session__avatar {
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--signal-soft);
  color: var(--signal);
  flex: 0 0 auto;
}
/* Both lines are spans, so the wrapper has to stack them — inline they ran
   together as "Signed inshared key". */
.session__who { min-width: 0; text-align: left; display: grid; }
.session__name {
  font-size: var(--t-foot); font-weight: var(--w-medium); color: var(--ice);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.session__sub {
  font-size: var(--t-cap2); color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app[data-rail="collapsed"] .session { grid-template-columns: 26px; justify-content: center; padding-inline: 0; }
.app[data-rail="collapsed"] .session__who,
.app[data-rail="collapsed"] .session__more { display: none; }
@media (max-width: 900px) {
  .rail__head { grid-template-columns: 1fr; justify-items: center; gap: var(--s3); }
  .session { grid-template-columns: 26px; justify-content: center; padding-inline: 0; }
  .session__who, .session__more { display: none; }
}

/* ---- a quoted reply ----------------------------------------------------- */
.quote {
  display: block;
  margin-top: var(--s3);
  padding: var(--s2) var(--s3);
  border-left: 2px solid var(--hair-strong);
  border-radius: 0 var(--r-control) var(--r-control) 0;
  background: var(--shelf);
  transition: border-color var(--d-micro) var(--e-standard);
}
.quote:hover { border-left-color: var(--signal); }
.quote__label { font-size: var(--t-cap2); color: var(--dim); }
.quote__text { font-size: var(--t-foot); color: var(--mist); margin-top: 1px; }

/* A tinted callout inside a panel. Three states share one box so a locked
   archive, a signature mismatch and a recovered password read as the same
   kind of remark rather than three bespoke blocks. */
.note {
  padding: var(--s3);
  border-radius: var(--r-control);
  border: 1px solid var(--hair);
  background: var(--shelf);
}
.note[data-tone="live"]  { border-color: var(--live);  background: var(--live-soft); }
.note[data-tone="ember"] { border-color: var(--ember); background: var(--ember-soft); }
.note[data-tone="alarm"] { border-color: var(--alarm); background: var(--alarm-soft); }

/* A labelled block inside a panel, where a nested panel would add a second
   border and flatten the hierarchy the outer one establishes. */
.subhead {
  font-size: var(--t-cap);
  letter-spacing: .02em;
  color: var(--dim);
  padding-top: var(--s2);
  border-top: 1px solid var(--hair);
  margin-bottom: var(--s2);
}

/* A matched window inside a search hit. Monospaced because what it quotes is
   machine output as often as it is prose, and clamped to two lines so one
   13KB panel dump cannot push the next result off the screen. */
.snippet {
  font-family: var(--f-mono);
  font-size: var(--t-cap);
  color: var(--mist);
  margin-top: 2px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}

/* ---- the API reference ---------------------------------------------------
   A list of operations that opens in place. Each row is a button because that
   is what it does; the method sits in a fixed gutter so the paths line up and
   can be read down the page. */
.doc-op + .doc-op { border-top: 1px solid var(--hair); }
.doc-op__head {
  display: grid;
  grid-template-columns: 52px minmax(0, 22ch) minmax(0, 1fr) 16px;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: var(--s3) var(--s4);
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--d-fast) var(--e-standard);
}
.doc-op__head:hover { background: var(--shelf); }
.doc-op__head:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
.doc-op__method {
  font-size: var(--t-cap2);
  font-family: var(--f-mono);
  letter-spacing: .04em;
  color: var(--dim);
}
.doc-op__method[data-m="post"] { color: var(--live); }
.doc-op__method[data-m="put"], .doc-op__method[data-m="patch"] { color: var(--ember); }
.doc-op__method[data-m="delete"] { color: var(--alarm); }
.doc-op__path { font-size: var(--t-foot); color: var(--ice); }
.doc-op__go { color: var(--dim); transition: transform var(--d-base) var(--e-spring); }
.doc-op__head[aria-expanded="true"] .doc-op__go { transform: rotate(90deg); }
.doc-op > div[hidden] { display: none; }
.doc-op > div:not([hidden]) { padding: 0 var(--s4) var(--s5) calc(var(--s4) + 52px + var(--s3)); }
@media (max-width: 900px) {
  .doc-op__head { grid-template-columns: 52px minmax(0, 1fr) 16px; }
  .doc-op__why { display: none; }
  .doc-op > div:not([hidden]) { padding-left: var(--s4); }
}

/* ---- pivot bar ----------------------------------------------------------
   The way out of a rule and into what it flagged. This is deliberately loud:
   the previous attempt made the `labelled` figure a link whose only affordance
   was a hover state, which nobody found. A pivot is a destination, so it looks
   like a button and says where it goes and how much is there. */
.pivot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s3);
  margin-top: var(--s4);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--hair);
  border-radius: var(--r-panel, 10px);
  background: var(--shelf);
}
.pivot__lead {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-right: auto;
  min-width: 0;
  font-size: var(--t-foot);
  color: var(--mist);
}
.pivot__go {
  --pivot-ink: var(--signal);
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 34px;
  padding: 0 var(--s3) 0 var(--s4);
  border: 1px solid color-mix(in oklab, var(--pivot-ink) 42%, transparent);
  border-radius: var(--r-control);
  background: color-mix(in oklab, var(--pivot-ink) 11%, transparent);
  color: var(--ice);
  font-size: var(--t-foot);
  font-weight: var(--w-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--d-fast) var(--e-standard),
              border-color var(--d-fast) var(--e-standard),
              transform var(--d-fast) var(--e-snappy);
}
.pivot__go:hover {
  background: color-mix(in oklab, var(--pivot-ink) 20%, transparent);
  border-color: var(--pivot-ink);
  transform: translateY(-1px);
}
.pivot__go:active { transform: translateY(0) scale(0.985); }
.pivot__go:focus-visible { outline: 2px solid var(--pivot-ink); outline-offset: 2px; }
.pivot__go[aria-disabled="true"] {
  --pivot-ink: var(--dim);
  color: var(--dim);
  pointer-events: none;
}
.pivot__go[aria-disabled="true"] .pivot__n { color: var(--dim); }
/* The count leads, but in primary ink, not the accent: --signal on --shelf
   measures 4.11:1 in the light theme, under the 4.5:1 floor for text this
   size. The accent is carried by the border and the arrow, which are shapes
   and answer to 3:1 — and text wearing a text token is the right call anyway. */
.pivot__n { color: var(--ice); font-weight: var(--w-semibold); }
.pivot__what { color: var(--mist); }
.pivot__go:hover .pivot__what { color: var(--ice); }
/* The arrow travels on hover — the one bit of motion, and it points the way. */
.pivot__arrow {
  display: inline-flex;
  color: var(--pivot-ink);
  transition: transform var(--d-fast) var(--e-snappy);
}
.pivot__go:hover .pivot__arrow { transform: translateX(3px); }

/* ---- searchable picker --------------------------------------------------
   A select cannot be searched and the rule set is 111 deep, so the rule filter
   is a filter box over a listbox. Shares the menu's material and timing. */
.picker {
  position: fixed;
  z-index: 300;
  display: flex;
  flex-direction: column;
  max-width: min(420px, calc(100vw - 16px));
  max-height: min(380px, calc(100vh - 32px));
  padding: var(--s1);
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  box-shadow: var(--lift-3);
  animation: picker-in var(--d-fast) var(--e-standard);
}
.picker[data-closing="1"] { animation: picker-in 120ms var(--e-exit) reverse forwards; }
@keyframes picker-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
.picker__head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s2) var(--s1);
  margin-bottom: var(--s1);
  border-bottom: 1px solid var(--hair);
  color: var(--dim);
  flex: none;
}
.picker__search {
  flex: 1;
  min-width: 0;
  min-height: 30px;
  border: none;
  background: none;
  color: var(--ice);
  font-size: var(--t-foot);
  font-family: inherit;
}
.picker__search:focus { outline: none; }
.picker__search::placeholder { color: var(--dim); }
.picker__list { overflow-y: auto; overscroll-behavior: contain; min-height: 0; }
.picker__opt {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  min-height: 32px;
  padding: 0 var(--s2);
  border-radius: 6px;
  font-size: var(--t-foot);
  color: var(--ice);
  text-align: left;
  transition: background-color var(--d-micro) var(--e-standard);
}
.picker__opt[data-active] { background: var(--shelf); }
.picker__opt:hover { background: var(--shelf); }
.picker__opt[data-on="1"] .picker__label { color: var(--signal); }
.picker__opt--clear { color: var(--mist); }
.picker__label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--f-mono);
  font-size: var(--t-cap);
}
.picker__opt--clear .picker__label { font-family: inherit; font-size: var(--t-foot); }
.picker__meta { font-size: var(--t-cap2); color: var(--dim); flex: none; }
.picker__tick { color: var(--signal); flex: none; }
.picker__group {
  padding: var(--s2) var(--s2) 2px;
  font-size: var(--t-cap2);
  color: var(--dim);
  text-transform: none;
}
.picker__none { padding: var(--s3) var(--s2); font-size: var(--t-cap); color: var(--dim); }

/* ---- a filter trigger that opens a picker ------------------------------- */
.picksel {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 34px;
  max-width: 260px;
  padding: 0 var(--s2) 0 var(--s3);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  background: var(--shelf);
  color: var(--ice);
  font-size: var(--t-foot);
  transition: border-color var(--d-fast) var(--e-standard),
              background-color var(--d-fast) var(--e-standard);
}
.picksel:hover { border-color: var(--dim); }
.picksel[data-set="1"] { border-color: color-mix(in oklab, var(--signal) 48%, transparent); }
.picksel[data-set="1"] .picksel__val { color: var(--signal); font-family: var(--f-mono); font-size: var(--t-cap); }
.picksel__val { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picksel__caret { color: var(--dim); flex: none; }

/* ---- why a rule matched -------------------------------------------------
   One block per matched term: the keyword as the author typed it, where it
   was found and how often, then the message text around each hit with the
   span marked. The excerpt is panel text written by whoever operates the
   malware, so it is set as data — monospace, pre-wrap, never interpreted. */
.why__term {
  padding: var(--s3);
  border: 1px solid var(--hair);
  border-radius: var(--r-control);
  background: var(--shelf);
}
.why__kw {
  font-family: var(--f-mono);
  font-size: var(--t-cap);
  color: var(--ice);
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--bg);
  box-shadow: inset 0 0 0 1px var(--hair-strong);
}
.why__excerpt {
  font-family: var(--f-mono);
  font-size: var(--t-cap);
  line-height: 1.65;
  color: var(--mist);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* A long panel dump would otherwise push the column wide. */
  max-height: 150px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ---- direction split ---------------------------------------------------
   Outbound against inbound on one conversation. Two numbers side by side do
   not convey a 1,312:1 ratio; a bar does, and the keys underneath keep the
   exact counts so nothing is lost to the picture. */
.split { margin-top: var(--s4); }
.split__bar {
  display: flex;
  height: 6px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--shelf);
}
.split__out { background: var(--signal); }
.split__in  { background: var(--ember); flex: 1; }
.split__keys {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s4);
  margin-top: var(--s2);
  font-size: var(--t-cap);
  color: var(--mist);
}
.split__key { display: inline-flex; align-items: center; gap: var(--s2); }
/* Identity is never colour alone: each key names its number in words. */
.split__dot { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.split__dot--out { background: var(--signal); }
.split__dot--in  { background: var(--ember); }

/* .dock__note stood here: a banner that told the reader two records were the
   same thing. The records are one record now, so the sentence had nothing left
   to do — a UI that has to explain its own data model has not finished being
   designed. Nothing uses the class, so nothing styles it. */

/* ---- a conversation, drawn ---------------------------------------------
   Replaces four figures and a flat list of ids. Two encodings carry it: a
   curve for WHEN the traffic happened, and one lane per bot on that same axis
   for WHO carried it. Colour does exactly two jobs — cool for our machines,
   warm for the other party — and never stands for identity, which position
   and the label carry. */
.convo__chart { margin-top: var(--s4); }
.convo__legend {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s2) var(--s4);
  margin-bottom: var(--s2);
  font-size: var(--t-cap);
  color: var(--mist);
}
.convo__key { display: inline-flex; align-items: center; gap: var(--s2); }
.convo__span { margin-left: auto; color: var(--dim); font-size: var(--t-cap2); }
.convo__dot { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.convo__dot--out { background: var(--signal); }
.convo__dot--in  { background: var(--ember); }

.convo__plotwrap { position: relative; }
.convo__plot { display: block; width: 100%; height: 120px; }
.convo__cursor { pointer-events: none; }
.convo__tip {
  position: absolute;
  top: 0;
  pointer-events: none;
  opacity: 0;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--hair-strong);
  border-radius: var(--r-control);
  background: var(--material);
  backdrop-filter: var(--material-blur);
  -webkit-backdrop-filter: var(--material-blur);
  box-shadow: var(--lift-2, var(--lift-3));
  font-size: var(--t-cap);
  white-space: nowrap;
  transition: opacity var(--d-micro) var(--e-standard);
  z-index: 2;
}
.convo__tip[data-on="1"] { opacity: 1; }
.convo__tipm { color: var(--ice); font-size: var(--t-cap2); margin-bottom: 2px; }
.convo__tipr { display: flex; align-items: center; gap: var(--s2); color: var(--mist); }
.convo__axis {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: var(--s1);
  font-size: var(--t-cap2);
  color: var(--dim);
}
.convo__peak { color: var(--mist); }

/* ---- per-bot lanes ------------------------------------------------------ */
.convo__lanes { margin-top: var(--s5); }
.convo__sub {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s2);
  font-size: var(--t-cap);
  color: var(--mist);
}
.lane {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
  padding: 3px 0;
  border-bottom: 1px solid var(--hair);
}
.lane:last-child { border-bottom: none; }
.lane__id {
  font-size: var(--t-cap);
  color: var(--signal);
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color var(--d-micro) var(--e-standard);
}
.lane__id:hover { color: var(--ice); text-decoration: underline; }
.lane__track { position: relative; height: 16px; min-width: 0; }
/* A strip of month cells. 2px of surface between fills keeps adjacent months
   legible as separate months rather than one smear. */
.lane__strip { display: flex; gap: 2px; height: 100%; }
.lane__cell {
  flex: 1;
  min-width: 0;
  border-radius: 2px;
  background: var(--signal);
  transition: opacity var(--d-micro) var(--e-standard);
}
.lane__cell:hover { outline: 1px solid var(--ice); outline-offset: 1px; }
/* Lanes without month detail still say WHEN, as a plain span. */
.lane__bar {
  position: absolute;
  top: 5px;
  height: 6px;
  border-radius: 3px;
  background: color-mix(in oklab, var(--signal) 55%, transparent);
}
.lane__nums {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-size: var(--t-cap2);
  color: var(--mist);
}
/* Volume as a bar behind its own number: 152K against 2 is a 76,000x spread,
   and as plain text both are the same size. */
.lane__vol {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  min-width: 62px;
  padding: 2px var(--s2);
  border-radius: 3px;
  overflow: hidden;
}
.lane__volbar {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  background: color-mix(in oklab, var(--signal) 20%, transparent);
  border-radius: 3px;
}
.lane__vol .num { position: relative; color: var(--ice); }
.lane__in { display: inline-flex; align-items: center; gap: 4px; min-width: 46px; justify-content: flex-end; }
.lane__live, .lane__dead { min-width: 62px; text-align: right; }
.lane__live { display: inline-flex; align-items: center; justify-content: flex-end; gap: 5px; color: var(--live, var(--signal)); }
.lane__pulse {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
  animation: lane-pulse 2.4s var(--e-standard) infinite;
}
@keyframes lane-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.35; transform: scale(0.8); }
}
.lane__dead { color: var(--dim); }

@media (max-width: 720px) {
  .lane { grid-template-columns: 84px minmax(0, 1fr); }
  .lane__nums { grid-column: 1 / -1; justify-content: flex-start; }
}

/* A named division inside one record — the person pane carries several. */
.convo__section { margin-top: var(--s5); }
.convo__section + .convo__section { margin-top: var(--s6); }

/* An operation's example call. Set as code, because it is one: the reader
   copies it. Scrolls on its own rather than widening the page. */
.doc-op__eg {
  margin: 0;
  padding: var(--s2) var(--s3);
  overflow-x: auto;
  font-size: var(--t-foot);
  line-height: 1.55;
  color: var(--ice);
  white-space: pre;
  background: var(--shelf);
  border: 1px solid var(--hair);
  border-radius: var(--r-control);
}
