/* ==========================================================================
   Gatehouse — mission control
   --------------------------------------------------------------------------
   One rule carries the personality: everything that is a *measurement* is set
   in monospace with tabular figures, and everything that is *prose* is set in
   the system sans. Digits in a readout must never shift sideways while a
   counter tweens, and the instrument look falls out of that honestly.

   Colour is semantic, not decorative. There are exactly three verdicts in the
   protocol — allow, would_deny, deny — so there are exactly three signal hues,
   plus one accent (iris) for the machine's own voice: sweep, signals, links.
   ========================================================================== */

:root {
  color-scheme: dark light;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;

  /* surfaces */
  --void:    #05070c;
  --panel:   #0b0f17;
  --panel-2: #0f1521;
  --sunk:    #070a11;
  --rule:    #1a2231;
  --tick:    #2b3a52;

  /* ink */
  --text: #e7edf9;
  --mid:  #a7b4cb;
  --dim:  #76859e;

  /* signal */
  --flow:  #4fe1c2;
  --watch: #f5a524;
  --halt:  #ff4d6d;
  --iris:  #8b7cff;
  --gold:  #ffd166;

  --flow-soft:  rgba(79, 225, 194, .14);
  --watch-soft: rgba(245, 165, 36, .14);
  --halt-soft:  rgba(255, 77, 109, .14);
  --iris-soft:  rgba(139, 124, 255, .14);

  --grid-line: rgba(255, 255, 255, .05);
  --scan:      rgba(255, 255, 255, .028);
  --vignette:  rgba(5, 7, 12, .55);
  --shadow:    0 1px 0 rgba(255, 255, 255, .03) inset;

  --u: 1;          /* global type scale, raised in kiosk */
  --gap: 14px;
  --pad: clamp(14px, 1.4vw, 22px);
}

/* --------------------------------------------------------------- theming ---
   Two ways in, one palette.

   The media query is the default and the no-JS path: the dashboard follows the
   viewer's OS. The [data-theme] attribute is the explicit override, set by the
   toggle and persisted, and it wins because a wall-mounted screen should not
   change appearance because the laptop driving it switched to Light at sunset.

   The media query is scoped to :not([data-theme]) so an explicit choice is
   never overruled by the OS. Both rules carry identical declarations and are
   generated from one source; edit them together.
   ------------------------------------------------------------------------ */

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --void:    #e9edf5;
    --panel:   #ffffff;
    --panel-2: #f4f7fc;
    --sunk:    #f7f9fd;
    --rule:    #dde3ee;
    --tick:    #b9c4d6;

    --text: #0d1320;
    --mid:  #3c4863;
    --dim:  #5d6b85;

    --flow:  #0d9a80;
    --watch: #ad6600;
    --halt:  #d31e45;
    --iris:  #5a49d6;
    --gold:  #9a6b06;

    --flow-soft:  rgba(13, 154, 128, .12);
    --watch-soft: rgba(173, 102, 0, .12);
    --halt-soft:  rgba(211, 30, 69, .12);
    --iris-soft:  rgba(90, 73, 214, .12);

    --grid-line: rgba(13, 19, 32, .07);
    --scan:      rgba(13, 19, 32, .022);
    --vignette:  rgba(233, 237, 245, .5);
    --shadow:    0 1px 2px rgba(16, 24, 40, .04);
  }
}

:root[data-theme="light"] {
    --void:    #e9edf5;
    --panel:   #ffffff;
    --panel-2: #f4f7fc;
    --sunk:    #f7f9fd;
    --rule:    #dde3ee;
    --tick:    #b9c4d6;

    --text: #0d1320;
    --mid:  #3c4863;
    --dim:  #5d6b85;

    --flow:  #0d9a80;
    --watch: #ad6600;
    --halt:  #d31e45;
    --iris:  #5a49d6;
    --gold:  #9a6b06;

    --flow-soft:  rgba(13, 154, 128, .12);
    --watch-soft: rgba(173, 102, 0, .12);
    --halt-soft:  rgba(211, 30, 69, .12);
    --iris-soft:  rgba(90, 73, 214, .12);

    --grid-line: rgba(13, 19, 32, .07);
    --scan:      rgba(13, 19, 32, .022);
    --vignette:  rgba(233, 237, 245, .5);
    --shadow:    0 1px 2px rgba(16, 24, 40, .04);
}

* { box-sizing: border-box; }

/* Several elements below are laid out with display:grid or display:flex, which
   would otherwise beat the hidden attribute. The attribute is how the app
   toggles every empty state, so it has to win. */
[hidden] { display: none !important; }

/* min-height, never height.
 *
 * `height: 100%` on body clamps its box to one viewport while the content
 * overflows it, and a sticky element is confined to its parent's box — so the
 * top bar stuck for exactly one screen and then scrolled away, which reads as
 * sticky being broken rather than as a height bug two hundred lines away.
 * min-height gives the full-viewport background on a short page without
 * capping the box on a long one. */
html { height: 100%; }
body { min-height: 100%; }

body {
  margin: 0;
  background: var(--void);
  color: var(--text);
  font-family: var(--sans);
  font-size: calc(14px * var(--u));
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* subtle depth so a wall screen doesn't read as a flat black rectangle */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(120vw 70vh at 50% -20%, rgba(139, 124, 255, .07), transparent 65%),
    radial-gradient(90vw 60vh at 100% 110%, rgba(79, 225, 194, .045), transparent 60%);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  padding: 10px 16px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--iris);
}
.skip:focus { left: 12px; top: 12px; }

/* ---------------------------------------------------------------- chrome -- */

.bar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px clamp(14px, 2vw, 28px);
  background: color-mix(in srgb, var(--void) 88%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--rule);
}

.brand { display: flex; align-items: baseline; gap: 10px; }
.brand .mark { width: 20px; height: 20px; color: var(--iris); align-self: center; }
.wordmark {
  font-family: var(--mono);
  font-size: calc(13px * var(--u));
  font-weight: 600;
  letter-spacing: .28em;
  color: var(--text);
}
.brand .sub {
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--dim);
}

.bar-mid { display: flex; align-items: center; gap: 12px; margin-left: auto; }
.bar-end { display: flex; align-items: center; gap: 10px; }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 10px;
  font-family: var(--mono);
  font-size: calc(10.5px * var(--u));
  letter-spacing: .13em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  border-radius: 2px;
  white-space: nowrap;
}
.badge::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.badge[data-mode="enforce"] { color: var(--flow); background: var(--flow-soft); }
.badge[data-mode="shadow"],
.badge[data-mode="tap"]     { color: var(--watch); background: var(--watch-soft); }

.link-state {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--mono);
  font-size: calc(10.5px * var(--u));
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
}
.link-state .dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--dim);
}
.link-state[data-state="live"] { color: var(--flow); }
.link-state[data-state="live"] .dot {
  background: var(--flow);
  animation: breathe 2.4s ease-in-out infinite;
}
.link-state[data-state="down"] { color: var(--halt); }
.link-state[data-state="down"] .dot { background: var(--halt); }

@keyframes breathe {
  0%, 100% { box-shadow: 0 0 0 0 var(--flow-soft); opacity: 1; }
  50%      { box-shadow: 0 0 0 5px transparent;    opacity: .55; }
}

.switch {
  display: inline-flex;
  border: 1px solid var(--rule);
  border-radius: 2px;
  overflow: hidden;
}
.switch button {
  appearance: none;
  border: 0;
  border-left: 1px solid var(--rule);
  background: transparent;
  color: var(--dim);
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  letter-spacing: .08em;
  padding: 5px 11px;
  cursor: pointer;
}
.switch button:first-child { border-left: 0; }
.switch button:hover { color: var(--text); background: var(--panel-2); }
.switch button[aria-pressed="true"] {
  color: var(--void);
  background: var(--iris);
}

.ghost-btn {
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
  text-decoration: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 5px 11px;
}
.ghost-btn:hover { color: var(--text); border-color: var(--iris); }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--iris);
  outline-offset: 2px;
}

/* warnings — deliberately loud, they are the reason someone walks over */
.warnbar {
  position: relative;
  z-index: 1;
  margin: var(--gap) clamp(14px, 2vw, 28px) 0;
  border: 1px solid var(--watch);
  border-left-width: 3px;
  background: var(--watch-soft);
  padding: 12px 16px;
}
.warnbar h3 {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: calc(10.5px * var(--u));
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--watch);
}
.warnbar ul { margin: 0; padding-left: 18px; }
.warnbar li { margin: 3px 0; color: var(--text); }

/* ------------------------------------------------------------- structure -- */

main {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--gap);
  padding: var(--gap) clamp(14px, 2vw, 28px) 40px;
  max-width: 1960px;
  margin: 0 auto;
}

.row { display: grid; gap: var(--gap); }
/* The radar is a circle, so its card can only be as useful as it is square.
   A 7fr track left a third of the card as empty margin either side of the
   dial; at 5fr the column width becomes the binding constraint instead of the
   viewport height, so the dial fills the card — and the stack beside it stops
   being cramped, which the live feed and the two panels under it needed. */
.row-radar { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
.row-three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.row-split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }

@media (max-width: 1240px) {
  .row-radar, .row-three, .row-split { grid-template-columns: minmax(0, 1fr); }

  /* Once the rows collapse the scope card is the full page width, and a
     square dial cannot fill that without becoming taller than the window.
     So the card hugs the dial instead of the dial rattling around inside the
     card — same rule as above, applied the other way round. */
  .scope {
    width: min(100%, 96vh, 900px);
    margin-inline: auto;
  }
}

.panel {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: var(--pad);
  box-shadow: var(--shadow);
  min-width: 0;
}

/* machined registration marks — the instrument-bezel motif, four corners */
.panel::before {
  content: "";
  position: absolute;
  inset: 5px;
  pointer-events: none;
  background:
    linear-gradient(var(--tick), var(--tick)) 0 0        / 9px 1px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 0 0        / 1px 9px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 100% 0     / 9px 1px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 100% 0     / 1px 9px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 0 100%     / 9px 1px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 0 100%     / 1px 9px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 100% 100%  / 9px 1px no-repeat,
    linear-gradient(var(--tick), var(--tick)) 100% 100%  / 1px 9px no-repeat;
  opacity: .5;
}

.panel-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 14px;
}
.panel-head h2 {
  margin: 0;
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--mid);
}
.head-note {
  margin-left: auto;
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .06em;
  color: var(--dim);
  text-align: right;
}

.eyebrow {
  margin: 0;
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--dim);
}
.eyebrow .per { color: var(--tick); font-weight: 400; }

.empty {
  margin: 0;
  padding: 18px 2px;
  color: var(--dim);
  font-size: calc(12.5px * var(--u));
  max-width: 46ch;
}
.empty-tight { padding: 6px 2px; }

/* ---------------------------------------------------------------- pulse --- */

.pulse-row {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1240px) { .pulse-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .pulse-row { grid-template-columns: minmax(0, 1fr); } }

.pulse {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: var(--pad);
  overflow: hidden;
  box-shadow: var(--shadow);
}
/* status rail: the tile's own colour, carried from its current value */
.pulse::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: var(--rail, var(--tick));
  transition: background .4s ease;
}
.pulse[data-metric="rpm"]    { --rail: var(--iris); }
.pulse[data-metric="people"] { --rail: var(--flow); }
.pulse[data-metric="deny"]   { --rail: var(--tick); }
.pulse[data-metric="p99"]    { --rail: var(--tick); }
.pulse[data-hot="1"]  { --rail: var(--watch); }
.pulse[data-hot="2"]  { --rail: var(--halt); }

.readout {
  margin: 6px 0 0;
  font-family: var(--mono);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  font-size: clamp(2.3rem, calc(3.4vw * var(--u)), 4.2rem);
  line-height: .96;
  letter-spacing: -.035em;
  color: var(--text);
  display: flex;
  align-items: baseline;
  gap: .12em;
}
.readout .suffix {
  font-size: .38em;
  letter-spacing: 0;
  color: var(--dim);
}
.readout-gold { color: var(--gold); }

.num { transition: text-shadow .5s ease; }
.num.tick { text-shadow: 0 0 18px currentColor; }

.pulse-foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
}
.spark { width: 100%; max-width: 190px; height: 26px; display: block; }

.delta {
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
  color: var(--dim);
  white-space: nowrap;
}
.delta[data-dir="up"]   { color: var(--flow); }
.delta[data-dir="down"] { color: var(--halt); }
.delta[data-dir="flat"] { color: var(--dim); }
.delta[data-invert="1"][data-dir="up"]   { color: var(--halt); }
.delta[data-invert="1"][data-dir="down"] { color: var(--flow); }

/* ---------------------------------------------------------------- scope --- */

.scope { display: flex; flex-direction: column; }

/* The dial takes the whole card width and stays square.
   The viewport-height bound is kept only as a ceiling for a very tall window,
   where an unbounded square would push everything else off the screen — but
   it is generous enough that the column, not the window, normally decides. */
.scope-body {
  position: relative;
  width: 100%;
  max-width: min(96vh, 900px);
  aspect-ratio: 1 / 1;
  /* Centred both ways, so any slack left over when the neighbouring stack is
     taller reads as symmetric padding rather than a dial stuck to the top. */
  margin: auto;
  align-self: center;
}
.scope-body canvas { width: 100%; height: 100%; display: block; }

/* contained CRT treatment — the one place the design lets itself be theatrical */
.scope-body::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 50%, transparent 52%, var(--vignette) 100%),
    repeating-linear-gradient(0deg, var(--scan) 0 1px, transparent 1px 3px);
}

.scope-empty {
  position: absolute;
  inset: 0;
  z-index: 1;   /* above the scanline overlay, which would otherwise stripe it */
  display: grid;
  place-content: center;
  text-align: center;
  padding: 0 14%;
  background: color-mix(in srgb, var(--panel) 94%, transparent);
}
.empty-title {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: calc(12px * var(--u));
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--mid);
}
.empty-body { margin: 0; color: var(--dim); font-size: calc(12.5px * var(--u)); }

.scope-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px 22px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
}
.keys {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--mid);
}
.keys-quiet { color: var(--dim); }
.keys li { display: inline-flex; align-items: center; gap: 6px; }
.keys-inline { margin-top: 12px; }
.key {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: currentColor;
}
.key-flow  { background: var(--flow); }
.key-watch { background: var(--watch); }
.key-halt  { background: var(--halt); }

.honest {
  margin: 12px 0 0;
  font-size: calc(11.5px * var(--u));
  line-height: 1.5;
  color: var(--dim);
  border-left: 2px solid var(--rule);
  padding-left: 12px;
  max-width: 62ch;
}

/* ---------------------------------------------------------------- feed ---- */

.feed { display: flex; flex-direction: column; }

/* Deliberately small.
 *
 * At any real traffic level nobody reads this line by line — rows arrive
 * faster than they can be parsed, and the panel's actual job is peripheral:
 * confirming at a glance that traffic is flowing and roughly what colour it
 * is. Sized for that job, it stops competing with the radar and the panels
 * people do read, and the row it drops was never read anyway.
 *
 * Kiosk mode overrides this to something larger, because a wall screen is
 * viewed from across a room and motion is most of the point there. */
.ticker {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  min-height: 120px;
  max-height: min(30vh, 260px);
  overflow: hidden;
  font-family: var(--mono);
  font-size: calc(10.5px * var(--u));
  -webkit-mask-image: linear-gradient(to bottom, #000 78%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 78%, transparent 100%);
}

.tick-row {
  display: grid;
  grid-template-columns: 3px auto minmax(0, 1fr);
  align-items: baseline;
  gap: 8px;
  padding: 2px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--rule) 55%, transparent);
  white-space: nowrap;
  animation: rowin .32s cubic-bezier(.2, .8, .3, 1) both;
}
@keyframes rowin {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}
.tick-rail { align-self: stretch; background: var(--tick); }
.tick-row[data-v="deny"]       .tick-rail { background: var(--halt); }
.tick-row[data-v="would_deny"] .tick-rail { background: var(--watch); }
.tick-row[data-v="allow"]      .tick-rail { background: var(--flow); }
.tick-row[data-err="1"]        .tick-rail { background: var(--halt); }

.tick-time { color: var(--tick); font-variant-numeric: tabular-nums; }
.tick-body { overflow: hidden; text-overflow: ellipsis; color: var(--mid); }
.tick-tag {
  font-weight: 600;
  letter-spacing: .1em;
  margin-right: 6px;
}
.tick-row[data-v="deny"]       .tick-tag { color: var(--halt); }
.tick-row[data-v="would_deny"] .tick-tag { color: var(--watch); }
.tick-handle { color: var(--text); }
.tick-sep { color: var(--tick); margin: 0 6px; }
.tick-tool { color: var(--iris); }
.tick-fail { color: var(--halt); }

/* --------------------------------------------------------------- charts --- */

.chart-wrap { position: relative; width: 100%; height: 260px; }
.chart-wrap canvas { width: 100%; height: 100%; display: block; }
.chart-square { aspect-ratio: 1 / 1; height: auto; max-width: 380px; margin: 0 auto; }
.chart-wrap .empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  text-align: center;
  max-width: none;
}

/* ------------------------------------------------------------ bars/tools -- */

.bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.bar-row { display: grid; gap: 3px; }
.bar-top {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--mono);
  font-size: calc(11.5px * var(--u));
}
.bar-name {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bar-figs {
  margin-left: auto;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bar-figs .bad { color: var(--halt); }
.bar-track { height: 6px; background: var(--sunk); border-radius: 1px; overflow: hidden; }
.bar-fill {
  display: block;   /* it is an <i>: without this the width below does nothing */
  height: 100%;
  width: 0;
  background: var(--flow);
  transition: width .7s cubic-bezier(.2, .8, .3, 1);
}

/* ---------------------------------------------------------------- cloud --- */

.cloud {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  align-items: baseline;
  gap: 4px 12px;
  line-height: 1.25;

  /* Bounded, because the cloud is the one panel whose height is driven by the
     data rather than by the layout: term count times term size. Unbounded, a
     busy week stretches it to several screens and — since the row is a grid —
     drags the tool leaderboard and rhythm wheel down with it, leaving a page
     of empty space between panels. */
  max-height: 22rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.cloud::-webkit-scrollbar { width: 6px; }
.cloud::-webkit-scrollbar-thumb {
  background: var(--rule);
  border-radius: 3px;
}
.term {
  font-family: var(--mono);
  letter-spacing: -.01em;
  color: var(--mid);
  cursor: default;
}
.term b { font-weight: 600; }

/* -------------------------------------------------------------- models --- */

/* Sits under the live feed in the radar row's right column. The feed is short
   by design now, and this fills the space it used to waste with a panel that
   rewards a long look rather than a glance. */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  min-width: 0;
}

.model-chart {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 16px;
}
@media (max-width: 520px) {
  .model-chart { grid-template-columns: 1fr; justify-items: center; }
}
#models-donut { display: block; width: 148px; max-width: 40vw; }

.model-legend {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.model-item {
  display: grid;
  grid-template-columns: 9px minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: 8px;
  padding: 2px 4px;
  border-radius: 3px;
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  cursor: default;
}
.model-item:hover { background: var(--panel-2); }
.model-swatch {
  width: 9px; height: 9px;
  border-radius: 2px;
  align-self: center;
}
.model-name {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Previous-generation entries are dimmed rather than flagged red. Being on an
   older model is worth noticing, not an error. */
.model-item[data-gen="older"] .model-name { color: var(--mid); }
.model-old {
  margin-left: 6px;
  font-size: calc(8.5px * var(--u));
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--watch);
}
.model-share {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.model-people {
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 6.5em;
}

.models-foot {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--rule);
  font-size: calc(11px * var(--u));
  line-height: 1.5;
  color: var(--watch);
}

/* --------------------------------------------------------- work themes --- */

/* A ranked list rather than a cloud. A cloud encodes one number in font size
   and nothing else, which is all a keyword deserves; a theme carries a name, a
   description, a session count, a headcount and an error share, and the list
   form gives each of those a fixed place the eye can return to. */

.themes {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 24rem;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}
.themes::-webkit-scrollbar { width: 6px; }
.themes::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 3px; }

.theme {
  position: relative;
  padding: 8px 10px 9px 12px;
  border-radius: 6px;
  overflow: hidden;
  transition: background .18s ease;
}
.theme:hover { background: var(--panel-2); }

/* The magnitude bar sits behind the text rather than beside it, so relative
   size reads at a glance without spending a column on it. */
.theme-bar {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 6px;
  background: var(--flow-soft);
  opacity: .55;
  pointer-events: none;
}
.theme[data-tone="halt"]  .theme-bar { background: var(--halt-soft); }
.theme[data-tone="watch"] .theme-bar { background: var(--watch-soft); }
.theme[data-tone="gold"]  .theme-bar { background: var(--iris-soft); }
.theme[data-tone="quiet"] .theme-bar { background: var(--grid-line); }

.theme-head {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.theme-name {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--text);
}
.theme-momentum {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .04em;
  color: var(--dim);
}
.theme-momentum[data-momentum="rising"] { color: var(--flow); }
.theme-momentum[data-momentum="new"]    { color: var(--gold); }
.theme-momentum[data-momentum="fading"] { opacity: .7; }

.theme-desc {
  position: relative;
  margin-top: 2px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--mid);
}
.theme-meta {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .02em;
  color: var(--dim);
}
.theme-count { color: var(--text); }
.theme-cat { text-transform: lowercase; opacity: .8; }
.theme-errors { color: var(--halt); }

.work-foot {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--rule);
  font-size: 11px;
  color: var(--dim);
}

/* -------------------------------------------------------------- treemap --- */

.treemap {
  position: relative;
  width: 100%;
  height: 300px;
  background: var(--sunk);
  border-radius: 2px;
  overflow: hidden;
}
.tile {
  position: absolute;
  overflow: hidden;
  padding: 8px 9px;
  border: 1px solid var(--panel);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}
.tile-name {
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--void);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tile-figs {
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--void) 78%, transparent);
  white-space: nowrap;
  overflow: hidden;
}
.tile-sm .tile-figs { display: none; }

/* -------------------------------------------------------------- signals --- */

.strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(260px, 1fr);
  gap: var(--gap);
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: thin;
}
@media (max-width: 780px) {
  .strip { grid-auto-flow: row; grid-auto-columns: auto; }
}

.signal {
  border: 1px solid var(--rule);
  border-left: 2px solid var(--sig, var(--iris));
  border-radius: 2px;
  background: var(--panel-2);
  padding: 12px 14px;
  min-width: 0;
}
.signal[data-kind="burst"]        { --sig: var(--iris); }
.signal[data-kind="regression"]   { --sig: var(--halt); }
.signal[data-kind="rising_topic"] { --sig: var(--flow); }
.signal[data-kind="adoption"]     { --sig: var(--gold); }

.signal-top {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--sig);
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .18em;
  text-transform: uppercase;
}
.signal-top svg { width: 13px; height: 13px; flex: none; }
.signal-top .when { margin-left: auto; color: var(--dim); letter-spacing: .06em; }
.signal-subject {
  margin: 8px 0 2px;
  font-family: var(--mono);
  font-size: calc(14px * var(--u));
  color: var(--text);
  word-break: break-word;
}
.signal-detail { margin: 0; color: var(--dim); font-size: calc(12px * var(--u)); }
.signal-mag {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--sig);
}

/* A triaged signal reads as a sentence, not a measurement. The severity chip
   is the only thing that should compete with the headline for attention, and
   only when it is worth competing: routine sits back, critical does not. */
.signal .sev {
  font-family: var(--mono);
  font-size: calc(9.5px * var(--u));
  letter-spacing: .08em;
  text-transform: uppercase;
  border: 1px solid currentColor;
  border-radius: 2px;
  padding: 0 5px;
  color: var(--dim);
}
.signal[data-severity="critical"] .sev { color: var(--halt); background: var(--halt-soft); }
.signal[data-severity="notable"]  .sev { color: var(--watch); background: var(--watch-soft); }
.signal[data-severity="routine"]  .sev { color: var(--dim); }
.signal[data-severity="critical"] { border-color: var(--halt); }

.signal-action {
  margin: 6px 0 0;
  padding-top: 6px;
  border-top: 1px dashed var(--rule);
  color: var(--mid);
  font-size: calc(12px * var(--u));
  line-height: 1.4;
}
.eyebrow-mini {
  font-family: var(--mono);
  font-size: calc(9.5px * var(--u));
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
  margin-right: 7px;
}

/* Provenance on a suggestion card. */
.gen-tag {
  font-family: var(--mono);
  font-size: calc(9.5px * var(--u));
  letter-spacing: .04em;
  border-radius: 2px;
  padding: 1px 6px;
  border: 1px solid currentColor;
  color: var(--dim);
}
.gen-tag[data-gen="llm"] { color: var(--flow); background: var(--flow-soft); }

/* -------------------------------------------------------------- denials --- */

.denials { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.denial {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 12px;
  padding: 7px 10px;
  background: var(--panel-2);
  border-left: 2px solid var(--halt);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: calc(11.5px * var(--u));
}
.denial[data-shadowed="1"] { border-left-color: var(--watch); opacity: .9; }
.denial .when { color: var(--dim); font-variant-numeric: tabular-nums; white-space: nowrap; }
.denial .reason {
  color: var(--text);
  font-family: var(--sans);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.denial .meta { color: var(--dim); white-space: nowrap; }
.denial .ref { color: var(--iris); }
.denial .shadow-tag { color: var(--watch); }

/* ---------------------------------------------------------- suggestions --- */

.cards {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}
.card {
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: var(--panel-2);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.card-top {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
}
.layer-tag {
  color: var(--iris);
  border: 1px solid currentColor;
  border-radius: 2px;
  padding: 1px 6px;
}
.impact { margin-left: auto; color: var(--gold); font-variant-numeric: tabular-nums; }
.card h3 {
  margin: 0;
  font-size: calc(15px * var(--u));
  font-weight: 600;
  line-height: 1.3;
  color: var(--text);
}
.card p { margin: 0; color: var(--mid); font-size: calc(12.5px * var(--u)); }

.evidence {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .06em;
  color: var(--dim);
}
.evidence li {
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 2px 7px;
  white-space: nowrap;
}

.snippet { position: relative; }
.snippet pre {
  margin: 0;
  padding: 11px 42px 11px 12px;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: calc(11.5px * var(--u));
  line-height: 1.55;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 160px;
  overflow: auto;
}
.copy-btn {
  position: absolute;
  top: 7px;
  right: 7px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  appearance: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: var(--panel);
  color: var(--dim);
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 3px 7px;
  cursor: pointer;
}
.copy-btn svg { width: 11px; height: 11px; }
.copy-btn:hover { color: var(--text); border-color: var(--iris); }
.copy-btn[data-done="1"] { color: var(--flow); border-color: var(--flow); }

.card-actions { display: flex; gap: 8px; align-items: center; margin-top: auto; }
.act {
  appearance: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: transparent;
  color: var(--mid);
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 6px 13px;
  cursor: pointer;
}
.act-ship:hover    { color: var(--flow); border-color: var(--flow); background: var(--flow-soft); }

.panel-foot {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-size: calc(11.5px * var(--u));
  line-height: 1.55;
  color: var(--dim);
  max-width: 78ch;
}
.act-dismiss:hover { color: var(--halt); border-color: var(--halt); background: var(--halt-soft); }
.act[disabled] { opacity: .5; cursor: default; }
.act-note {
  font-family: var(--mono);
  font-size: calc(10.5px * var(--u));
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
}
.act-note[data-tone="ok"]  { color: var(--flow); }
.act-note[data-tone="bad"] { color: var(--halt); }

.effect {
  border-top: 1px solid var(--rule);
  padding-top: 8px;
  font-size: calc(11.5px * var(--u));
  color: var(--dim);
}
.effect b { color: var(--flow); font-weight: 600; }

/* ------------------------------------------------------------- footer ----- */

.footer-panel { background: var(--panel); }
.footgrid {
  display: grid;
  gap: var(--gap) clamp(20px, 3vw, 48px);
  grid-template-columns: auto minmax(0, 1fr) minmax(200px, auto);
  align-items: start;
}
@media (max-width: 980px) { .footgrid { grid-template-columns: minmax(0, 1fr); } }

.savings .readout { font-size: clamp(2.6rem, calc(3.6vw * var(--u)), 4.4rem); }
.savings-note {
  margin: 4px 0 0;
  font-size: calc(11.5px * var(--u));
  color: var(--dim);
  max-width: 24ch;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 12px 18px;
  margin: 0;
}
.stat { min-width: 0; }
.stat dt {
  font-family: var(--mono);
  font-size: calc(9.5px * var(--u));
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim);
}
.stat dd {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: calc(16px * var(--u));
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
  color: var(--text);
}
.stat dd.warn { color: var(--watch); }
.stat dd.bad  { color: var(--halt); }

.chips { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 3px 9px;
  font-family: var(--mono);
  font-size: calc(10.5px * var(--u));
  color: var(--mid);
}
.chip::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--flow);
}
.chip[data-open="1"] { color: var(--halt); border-color: var(--halt); }
.chip[data-open="1"]::before { background: var(--halt); }
.chip .trips { color: var(--dim); }

/* ---------------------------------------------------------------- kiosk --- */

.kiosk-head {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 16px clamp(18px, 2.4vw, 40px) 4px;
  position: relative;
  z-index: 1;
}
.kiosk-head .wordmark { font-size: calc(15px * var(--u)); }
.kiosk-scene {
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--iris);
}
.kiosk-clock {
  margin-left: auto;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: calc(15px * var(--u));
  letter-spacing: .06em;
  color: var(--mid);
}

body[data-kiosk] { --u: 1.42; }
body[data-kiosk] .bar,
body[data-kiosk] [data-kiosk-hide],
body[data-kiosk] .honest { display: none; }
body[data-kiosk] .kiosk-head { display: flex; }
/* A wall screen is read from across a room, and the motion is most of the
   point there — so kiosk keeps the big feed the desk view no longer needs. */
body[data-kiosk] .ticker {
  min-height: 260px;
  max-height: min(60vh, 700px);
  font-size: calc(11.5px * var(--u));
}
body[data-kiosk] .tick-row { padding: 4px 0; }
body[data-kiosk] .scope-body { width: min(100%, 64vh, 900px); }
body[data-kiosk] main { padding-top: 8px; gap: 18px; }

.scene-off { display: none !important; }
.scene-in { animation: scenein .5s ease both; }
@keyframes scenein {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------- reduced motion --- */

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

/* ------------------------------------------------------------ theme toggle -- */

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 30px;
  padding: 0;
  line-height: 0;
}
.icon-btn svg { width: 15px; height: 15px; }

/* Show the icon for the theme the button would switch *to*, which is the
   convention people expect: a moon means "click for dark". */
.ico-dark  { display: none; }
.ico-light { display: block; }
:root[data-theme="light"] .ico-dark  { display: block; }
:root[data-theme="light"] .ico-light { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .ico-dark  { display: block; }
  :root:not([data-theme]) .ico-light { display: none; }
}

/* The canvases repaint from JS on theme change; give everything else a short
   crossfade so the switch reads as deliberate rather than as a glitch. */
@media (prefers-reduced-motion: no-preference) {
  body, .panel, .bar, .pulse {
    transition: background-color .18s ease, border-color .18s ease, color .18s ease;
  }
}

/* ------------------------------------------------ improvements found --- */

/* A counter, not a chart. There are three numbers worth knowing — how many
   findings are waiting, how many were acted on, how many were judged not worth
   acting on — and a bar chart of three values is decoration. The layer rows
   underneath answer the follow-up question, which is whose desk the work
   lands on: CLAUDE.md and claude.ai project instructions are edited by
   different people. */

.improve-counts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  align-items: end;
}
.improve-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
a.improve-stat:hover .improve-n { color: var(--flow); }
.improve-n {
  font-family: var(--mono);
  font-size: calc(26px * var(--u));
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.improve-shipped { color: var(--flow); }
.improve-dim { color: var(--mid); }
.improve-k {
  font-size: calc(9.5px * var(--u));
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

.improve-layers {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
}
.improve-layer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 8px;
  padding: 2px 4px;
  border-radius: 3px;
  font-family: var(--mono);
  font-size: calc(11px * var(--u));
}
.improve-layer:hover { background: var(--panel-2); }
.improve-layer-name {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.improve-layer-n {
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}
.improve-layer-n b { color: var(--text); font-weight: 500; }

.improve-foot {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--rule);
  font-size: calc(11px * var(--u));
  line-height: 1.5;
  color: var(--dim);
}
.improve-foot .win { color: var(--flow); }

/* ----------------------------------------------------------- work views --- */

/* A segmented control in the panel head. Deliberately quiet: it changes how
   the panel is drawn, never what it says, so it should not compete with the
   heading for attention. */
.viewpick {
  display: flex;
  gap: 1px;
  margin-left: auto;
  margin-right: 10px;
  padding: 1px;
  border: 1px solid var(--rule);
  border-radius: 4px;
}
.viewpick button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--dim);
  font: inherit;
  font-size: calc(9.5px * var(--u));
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 3px;
  cursor: pointer;
}
.viewpick button:hover { color: var(--text); }
.viewpick button[aria-pressed="true"] {
  background: var(--panel-2);
  color: var(--text);
}
.viewpick button:focus-visible {
  outline: 2px solid var(--flow);
  outline-offset: 1px;
}

/* The cloud host owns its box: the layout is computed against these exact
   pixels, so it cannot be sized by its contents. */
.cloud-host {
  position: relative;
  width: 100%;
  height: 22rem;
  overflow: hidden;
  /* The sphere needs a stacking context of its own, or its z-index shuffling
     fights with the rest of the page. */
  isolation: isolate;
}

.cloud-word {
  /* inline-block with line-height 1 makes the border box exactly font-size
     tall, and no letter-spacing keeps its width equal to canvas measureText.
     The layout positions words by that box, so both are load-bearing rather
     than cosmetic. */
  display: inline-block;
  font-family: var(--mono);
  letter-spacing: 0;
  line-height: 1;
  color: var(--mid);
  cursor: default;
  opacity: 0;
  transition: opacity .45s ease, color .2s ease;
}
.cloud-word.is-in { opacity: 1; }
/* Only the packed layout fades in as a whole; the sphere sets per-word opacity
   every frame for depth, so a transition on it would smear the rotation. */
.cloud-host .cloud-word[style*="translate3d"] { transition: none; }
.cloud-word:hover { color: var(--text); }

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

/* A stream on a drawn axis: ribbons above the line are the top bodies of work
   (thickness = active sessions), notes above call out arrivals, pins below
   place detector signals at the instant they fired, and the legend carries
   identity for every ribbon too thin to label itself. All horizontal
   positions are fractions of the window, so a resize costs one redraw. */

.tl {
  position: relative;
  padding: 4px 0 2px;
  --tl-notes-h: 26px;
  --tl-stream-h: 108px;
}

.tl-lane { position: relative; }
.tl-lane-notes { height: var(--tl-notes-h); }

.tl-streambox {
  position: relative;
  height: var(--tl-stream-h);
}
.tl-streambox canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* The hover readout. One line, following the pointer horizontally, above the
   stream so it never hides what it describes. */
.tl-tip {
  position: absolute;
  top: -22px;
  padding: 2px 8px;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: var(--panel);
  box-shadow: var(--shadow);
  font-family: var(--mono);
  font-size: calc(10px * var(--u));
  color: var(--text);
  white-space: nowrap;
  pointer-events: none;
  z-index: 3;
}

/* ---- the axis ---- */

.tl-axis {
  position: relative;
  height: 22px;
  border-top: 1px solid var(--rule);
}
.tl-axis::after {
  content: "";
  position: absolute;
  right: -1px;
  top: -4px;
  border-left: 6px solid var(--rule);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
}

.tl-tick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  font-family: var(--mono);
  font-size: calc(9px * var(--u));
  letter-spacing: .05em;
  color: var(--dim);
  white-space: nowrap;
}
.tl-tick::before {
  content: "";
  width: 1px;
  height: 5px;
  background: var(--rule);
}
.tl-tick[data-now="1"] { color: var(--flow); }
.tl-tick[data-now="1"]::before { background: var(--flow); height: 8px; }
.tl-tick:last-child { transform: translateX(-100%); align-items: flex-end; }

/* ---- notes above the stream ---- */

.tl-note {
  position: absolute;
  bottom: 2px;
  transform: translateX(-50%);
  display: flex;
  align-items: baseline;
  gap: 4px;
  max-width: 32%;
  font-size: calc(10.5px * var(--u));
  color: var(--text);
  white-space: nowrap;
  cursor: default;
  z-index: 2;
}
.tl-note-mark { font-size: calc(9px * var(--u)); }
.tl-note[data-state="new"]    .tl-note-mark { color: var(--gold); }
.tl-note[data-state="rising"] .tl-note-mark { color: var(--flow); }
.tl-note-name { overflow: hidden; text-overflow: ellipsis; }
.tl-note-verb { color: var(--dim); }
/* The hairline anchors the note to its instant, dropping through the stream
   to the axis — an annotation floats, a callout points. */
.tl-note::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 1px;
  height: calc(var(--tl-stream-h) + 2px);
  background: linear-gradient(color-mix(in srgb, var(--text) 38%, transparent), transparent 85%);
  pointer-events: none;
}

/* ---- signal pins below the axis ---- */

.tl-lane-marks {
  --tl-mark-rows: 1;
  min-height: calc(22px + (var(--tl-mark-rows) - 1) * 16px);
}

.tl-mark {
  position: absolute;
  top: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  cursor: default;
  --stem-x: 50%;
}
.tl-mark[data-row="1"] { padding-top: 16px; }
.tl-mark[data-row="2"] { padding-top: 32px; }
.tl-mark[data-row="3"] { padding-top: 48px; }
.tl-mark::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--stem-x);
  width: 1px;
  background: var(--rule);
}
.tl-mark[data-row="0"]::before { height: 7px; }
.tl-mark[data-row="1"]::before { height: 23px; }
.tl-mark[data-row="2"]::before { height: 39px; }
.tl-mark[data-row="3"]::before { height: 55px; }
.tl-mark-dot {
  position: absolute;
  top: 5px;
  left: var(--stem-x);
  transform: translateX(-50%);
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--watch);
}
.tl-mark[data-row="1"] .tl-mark-dot { top: 21px; }
.tl-mark[data-row="2"] .tl-mark-dot { top: 37px; }
.tl-mark[data-row="3"] .tl-mark-dot { top: 53px; }
.tl-mark[data-sev="critical"] .tl-mark-dot { background: var(--halt); }
.tl-mark[data-sev="routine"]  .tl-mark-dot { background: var(--dim); }
.tl-mark-label {
  padding-top: 10px;
  font-family: var(--mono);
  font-size: calc(9px * var(--u));
  line-height: 1.35;
  color: var(--watch);
  white-space: nowrap;
}
.tl-mark[data-sev="critical"] .tl-mark-label { color: var(--halt); }
.tl-mark[data-sev="routine"]  .tl-mark-label { color: var(--dim); }
.tl-mark-more .tl-mark-label { color: var(--dim); }

/* ---- legend ---- */

.tl-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
}
.tl-key {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: calc(10.5px * var(--u));
  color: var(--mid);
  cursor: default;
}
.tl-key:hover { color: var(--text); }
.tl-key-swatch {
  width: 10px; height: 10px;
  border-radius: 2px;
  flex: none;
}
.tl-key-other .tl-key-swatch {
  background: color-mix(in srgb, var(--rule) 45%, transparent);
  border: 1px solid var(--rule);
}

.timeline-foot {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--rule);
  font-size: calc(11px * var(--u));
  line-height: 1.5;
  color: var(--dim);
}

@media (max-width: 760px) {
  .tl { --tl-notes-h: 0px; --tl-stream-h: 84px; }
  .tl-lane-notes { display: none; }
  .tl-mark-label { display: none; }
}
