/*
 * Orbital Fence — console styling.
 *
 * Everything is sized on the golden ratio, and now on the DS's ladders rather
 * than a parallel set of its own: type climbs by phi^(1/4) and space by
 * phi^(1/2), both anchored where the DS anchors them. Layout follows suit —
 * the log panel is exactly phi times the width of the rail. Nothing in this
 * file is a hand-picked pixel value.
 *
 * Type roles: sans for headings, labels and prose; mono only for data —
 * figures that must align in a column, and identifiers like a designator or a
 * timestamp. That split is what makes it read as a lab notebook over a data
 * table rather than as a terminal emulator.
 *
 * Colour comes from design-system/oscar-ds-tokens.css, loaded before this file.
 * The rule here is that the chrome is entirely neutral: greys for surfaces and
 * ink, --accent for anything interactive, and the three DS semantic accents
 * only where a colour is carrying information. Hue belongs to the scene.
 */

:root {
  /*
   * Type and space come from the DS ladders now. The two systems were already
   * the same lattice: Orbital Fence stepped by sqrt(phi), the DS steps by
   * phi^(1/4), so this console simply uses every second rung of the DS ladder,
   * anchored on the same one. What used to be --f0 IS --fs-micro to three
   * decimal places; the old spacing scale was the same idea with ~4% of
   * accumulated rounding drift, which adopting the tokens removes.
   *
   * Four rungs sit below the DS's published floor. They are exact downward
   * extensions of the same ladder, not invented sizes — the brief says to step
   * the ladder, and this is that case.
   */
  --fs-nano: 0.665rem; /* --fs-micro / phi^(1/4) */
  --fs-pico: 0.59rem; /* --fs-micro / phi^(1/2) */
  --fs-femto: 0.523rem; /* --fs-micro / phi^(3/4) */
  --fs-atto: 0.464rem; /* --fs-micro / phi       */

  /*
   * Likewise below --space-1 (8px). A dense instrument needs two rungs the
   * document-shaped DS never had a use for.
   */
  --space-tight: 6px; /* --space-1 / phi^(1/2) */
  --space-hair: 5px; /* --space-1 / phi       */

  /*
   * The HUD grid is denser than any DS line-height rung: --lh-tight (1.1) is
   * too tight to read a wrapped label and --lh-snug (1.5) wastes rail height.
   * Named for this host so it cannot shadow the DS token.
   */
  --lh-hud: 1.272; /* sqrt(phi) */

  /* Layout: rail : log = 1 : phi */
  --rail-width: 18.5rem;
  --log-width: 29.93rem; /* rail * phi */

  /*
   * Ink, two-tier as the DS requires: labels and meta sit back, readouts and
   * headings carry full contrast. None of it is hued.
   */
  --ink: var(--text-primary);        /* readouts, values, headings */
  --ink-body: var(--text-secondary); /* prose, log body */
  --ink-dim: var(--text-tertiary);   /* keys, micro-labels, captions */

  /*
   * Surfaces. --panel is the DS's deliberately semi-transparent dark surface;
   * the alpha is the point, because the globe is behind it.
   */
  --ground: #0a0a0a; /* matches the renderer clear colour in src/main.js */
  --panel: var(--bg-secondary);
  --edge: var(--border-primary);
  --edge-strong: var(--border-secondary);

  /*
   * Signal colours. These mirror the scene encoding one-for-one so the log
   * legend and the globe cannot disagree. Everything else in the chrome is
   * grey or --accent.
   *
   * --sig-alert covers breakup, conjunction and loss of track alike: it means
   * "something is wrong", and the three are already told apart structurally
   * (left border, strikethrough, and the event word itself) rather than by hue.
   */
  --sig-alert: var(--accent-red);
  --sig-caution: var(--accent-gold); /* synthetic data, warped clock */
  --sig-good: var(--accent-green);   /* live catalogue feed */
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
  background: var(--ground);
  color: var(--ink-body);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: var(--lh-body);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

/*
 * Mono is for data, and only data: figures that have to line up in a column,
 * and identifiers you read character by character rather than as a word. The
 * old stylesheet set mono on <body> and let it cover everything, which is what
 * made the console read as a terminal rather than as an instrument.
 *
 * The previous stack led with IBM Plex Mono and JetBrains Mono, neither of
 * which is bundled and neither of which has an @font-face here — so in
 * practice almost every visitor was already falling through to Menlo or
 * Consolas. --font-mono says that honestly.
 */
.clock,
.stat .v,
.designator,
.lock-grid b,
.detail-grid dd,
.origin-count,
.log-row .c-time,
.log-row .c-id,
.row-data,
.warp select {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/*
 * The whole console is a drag surface — the camera is slewed by dragging
 * anywhere, including across the panels. Text selection fights that: a drag
 * that starts on a label highlights it instead of moving the view, and leaves
 * a blue smear over the readouts. Nothing here is meant to be copied.
 */
* {
  -webkit-user-select: none;
  user-select: none;
}

#scene {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  display: block;
  touch-action: none;
  cursor: grab;
}

#scene.is-dragging {
  cursor: grabbing;
}

@media (prefers-reduced-motion: reduce) {
  .cursor { animation: none; opacity: 1; }
  .log-row.is-acq, .log-row.is-reacq { animation: none; }
}

/* --- HUD layout ------------------------------------------------------- */

.hud {
  position: fixed;
  inset: 0;
  z-index: 4;
  display: grid;
  grid-template-columns: var(--rail-width) 1fr var(--log-width);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "bar bar bar"
    "rail . log"
    "foot foot foot";
  gap: var(--space-2);
  padding: var(--space-2);
  pointer-events: none;
  font-size: var(--fs-micro);
  line-height: var(--lh-hud);
  letter-spacing: 0.06em;
  /* The boot fade is an entrance, so it takes the DS's entrance curve. */
  transition: opacity var(--dur-reveal) var(--ease-out-expo);
}

.hud > *,
.rail > * {
  pointer-events: auto;
}

/*
 * Rail panels keep their natural height and the rail scrolls instead.
 *
 * Flex items shrink by default, which squeezed the panels shorter than their
 * contents; the plan-view canvas has an intrinsic aspect ratio and so kept its
 * height, spilling out of the telemetry panel and over the one below it.
 */
.rail > .panel {
  flex-shrink: 0;
  overflow: hidden;
}

/*
 * The last-solution panel absorbs whatever height is left over and scrolls its
 * own contents. Without it the rail is taller than the viewport and the panel
 * is cut off by the bottom of the screen, which reads as broken rather than as
 * scrollable — and the last solution is the one panel you want fully visible.
 */
.rail > .panel-detail {
  flex: 1 1 auto;
  min-height: 7rem;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-dim) transparent;
}

.rail {
  grid-area: rail;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-height: 0;
  /* Scroll rather than clip if a short viewport cannot take the whole rail. */
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-dim) transparent;
  pointer-events: none;
}

.hud-bar {
  grid-area: bar;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-tight) var(--space-4);
  border: 1px solid var(--edge);
  background: var(--panel);
  backdrop-filter: blur(2px);
  transition: var(--t-surface), var(--t-color);
}

.brand {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

/* The one piece of brand colour on the page. */
.brand .glyph {
  color: var(--accent);
  font-size: var(--fs-body);
}

/* An h1, so the page states its own name to a crawler; sized by the ladder,
   not by the browser's heading default. */
.brand .title {
  margin: 0;
  font-size: var(--fs-h5);
  font-weight: 700;
  letter-spacing: 0.34em;
  color: var(--ink);
}

.brand .sub,
.label-tag {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.18em;
}

.bar-right {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.clock {
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.status {
  padding: var(--space-hair) var(--space-2);
  border: 1px solid var(--edge-strong);
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.2em;
}

.status.is-armed {
  color: var(--ground);
  background: var(--sig-alert);
  border-color: var(--sig-alert);
}

/* --- Motion ------------------------------------------------------------
 *
 * The DS law is that every colour-bearing property transitions, so a theme
 * swap reads as one object turning over rather than N repaints. This console
 * ships dark only, so the surfaces above are future-proofing — but the same
 * tokens do real work here, because the HUD changes state constantly: the
 * status chip arms, the clock goes off real time, a row is cued, provenance
 * resolves. All of those used to snap.
 *
 * Durations are matched to what the change means rather than all being
 * --dur-theme. A pointer hover has to feel immediate (--dur-hover); a state
 * flip the operator should notice takes the slower UI beat (--dur-ui). Only
 * the panel surfaces, which change on nothing but a theme, run the full 0.8s.
 */

.status,
.clock,
.origin,
.src,
.tag,
.log-row .c-id .objname {
  transition:
    color var(--dur-ui) var(--ease-theme),
    background-color var(--dur-ui) var(--ease-theme),
    border-color var(--dur-ui) var(--ease-theme);
}

/* The confidence meter also moves; its width keeps the existing linear ramp. */
.conf-bar span {
  transition:
    width 0.3s linear,
    background-color var(--dur-ui) var(--ease-theme);
}

.log-row,
.release,
.warp select {
  transition:
    background-color var(--dur-hover) var(--ease-standard),
    border-color var(--dur-hover) var(--ease-standard),
    color var(--dur-hover) var(--ease-standard),
    opacity var(--dur-ui) var(--ease-standard);
}

/* --- Panels ----------------------------------------------------------- */

.panel {
  border: 1px solid var(--edge);
  background: var(--panel);
  backdrop-filter: blur(2px);
  padding: var(--space-2) var(--space-2);
  min-height: 0;
  transition: var(--t-surface), var(--t-color);
}

.panel h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-pico);
  font-weight: 400;
  letter-spacing: 0.24em;
  color: var(--ink);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 1px solid var(--edge);
  padding-bottom: var(--space-tight);
}

.hint {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.1em;
}

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

.stat {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 1px solid color-mix(in srgb, var(--edge) 60%, transparent);
  padding-bottom: var(--space-hair);
}

.stat .k {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.16em;
}

.stat .v {
  font-size: var(--fs-body);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.planview {
  display: block;
  /* Square plan position indicator, bounded so the rail never overflows. */
  width: 100%;
  max-width: min(100%, 12vh);
  aspect-ratio: 1 / 1;
  margin: var(--space-2) auto 0;
  border: 1px solid var(--edge);
  background: color-mix(in srgb, var(--ground) 70%, transparent);
}

.panel-detail {
  margin-top: auto;
}

/* Prose, not labels: sentence case and no tracking. */
.lock-empty,
.detail-empty {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  line-height: var(--lh-body);
  letter-spacing: normal;
}

.lock-head,
.detail-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-tight);
}

.designator {
  font-size: var(--fs-h5);
  color: var(--ink);
  letter-spacing: 0.12em;
}

.release {
  font: inherit;
  font-size: var(--fs-pico);
  letter-spacing: 0.16em;
  color: var(--sig-alert);
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--sig-alert) 50%, transparent);
  padding: var(--space-hair) var(--space-tight);
  cursor: pointer;
}

.release:hover {
  background: color-mix(in srgb, var(--sig-alert) 16%, transparent);
}

.lock-sub,
.detail-sub {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.12em;
  margin-bottom: var(--space-tight);
}

.footnote {
  margin: var(--space-2) 0 0;
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.1em;
  line-height: var(--lh-body);
}

.foot-note {
  color: var(--ink-dim);
  opacity: 0.85;
}

.lock-grid {
  display: grid;
  grid-template-columns: auto 1fr auto 1fr;
  gap: var(--space-hair) var(--space-2);
  font-size: var(--fs-pico);
}

.lock-grid span {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.16em;
}

.lock-grid b {
  font-weight: 400;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.conf-bar {
  margin-top: var(--space-tight);
  height: 3px;
  background: color-mix(in srgb, var(--edge) 60%, transparent);
}

.conf-bar span {
  display: block;
  height: 100%;
  background: var(--accent);
}

/*
 * The event chip on the last solution. It carries an .ev-* class, so the event
 * decides its colour and the border follows the text — the same currentColor
 * trick .origin and .src use. It used to hardcode alert red, which meant a
 * routine acquisition still got a red box around it.
 *
 * The phrase inside is sentence case, so it takes no label tracking.
 */
.tag {
  font-size: var(--fs-pico);
  color: var(--ink);
  border: 1px solid currentColor;
  border-radius: var(--radius-sm);
  padding: var(--space-hair) var(--space-tight);
  letter-spacing: normal;
}

.detail-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-hair) var(--space-4);
  margin: 0;
}

.detail-grid div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 1px solid color-mix(in srgb, var(--edge) 60%, transparent);
}

.detail-grid dt {
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.18em;
}

.detail-grid dd {
  margin: 0;
  font-size: var(--fs-pico);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.detail-grid em {
  font-style: normal;
  color: var(--ink-dim);
  font-size: var(--fs-atto);
}

/* --- Fence log --------------------------------------------------------- */

.panel-log {
  grid-area: log;
  display: flex;
  flex-direction: column;
  max-height: 100%;
}

.log-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-tight) var(--space-4);
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.12em;
  padding-bottom: var(--space-tight);
  border-bottom: 1px solid var(--edge);
}

.log-legend b {
  font-weight: 400;
  color: var(--ink);
}

.log {
  overflow-y: auto;
  /*
   * Never sideways. The TEARR line is nowrap by design, and an unusually long
   * range-rate would otherwise put a horizontal scrollbar under the entire log
   * rather than affecting only the row that overran.
   */
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-dim) transparent;
  min-height: 0;
}

.log-row {
  padding: var(--space-tight) var(--space-hair);
  border-bottom: 1px solid color-mix(in srgb, var(--edge) 45%, transparent);
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}

.log-row:not([data-track-id]) {
  cursor: default;
}

.log-row:hover,
.log-row:focus-visible {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  outline: none;
}

.row-head {
  display: grid;
  grid-template-columns: 6.18rem 1fr auto;
  gap: var(--space-tight);
  align-items: baseline;
  font-size: var(--fs-micro);
  white-space: nowrap;
}

/*
 * Indented to sit under the event text, so the timestamp column stays a clean
 * left edge. The group gap is one rung down from the DS default: the line has
 * five label/value pairs plus a 6.18rem indent inside a fixed-width panel, and
 * at --space-2 it overran by a few pixels.
 */
.row-data {
  display: flex;
  gap: var(--space-1);
  margin-top: var(--space-hair);
  padding-left: 6.18rem;
  font-size: var(--fs-pico);
  color: var(--ink-body);
  white-space: nowrap;
}

.row-data b {
  font-weight: 400;
  color: var(--ink-dim);
  letter-spacing: 0.12em;
  margin-right: var(--space-hair);
}

/* A sentence about the row, not a label on it. */
.row-data .wide {
  color: var(--ink-dim);
  letter-spacing: normal;
  font-family: var(--font-sans);
}

.log-row .c-time { color: var(--ink-dim); }

.log-row .c-id {
  color: var(--ink);
  letter-spacing: 0.08em;
  text-align: right;
}

/* Sentence-case copy, so it takes no label tracking. */
.log-row .c-event { letter-spacing: normal; }

.log-row i {
  font-style: normal;
  font-size: var(--fs-atto);
  margin-left: var(--space-hair);
  color: var(--ink-dim);
}

.log-row .type {
  border: 1px solid var(--edge);
  padding: 0 var(--space-hair);
}

.log-row .passes { color: var(--ink); }

/*
 * Event-type colouring. Acquisition and update are ordinary traffic and stay
 * neutral; only the events worth looking at take a hue.
 */
.ev-acq { color: var(--ink); }
.ev-upd { color: var(--ink-dim); }
/* Acquiring a historical fragment is routine — most of the catalogue is one. */
.ev-frag { color: var(--ink); }
/* A cloud coming apart in session is not. */
.ev-breakup { color: var(--sig-alert); }
.ev-conj { color: var(--sig-alert); }
.ev-cued { color: var(--accent); }
.ev-lost { color: var(--sig-alert); }

.log-row.is-breakup,
.log-row.is-conjunction {
  background: color-mix(in srgb, var(--sig-alert) 8%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--sig-alert) 50%, transparent);
}

.log-row.is-cued {
  border-left: 2px solid color-mix(in srgb, var(--accent) 55%, transparent);
}

.log-row.is-selected {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}

.log-row.is-stale {
  opacity: 0.55;
}

.log-row.is-lost {
  opacity: 0.32;
}

.log-row.is-lost .c-id {
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--sig-alert) 60%, transparent);
}

.log-row.is-acq {
  animation: rowIn 0.4s ease-out;
}

.log-row.is-reacq {
  animation: rowPing 0.5s ease-out;
}

@keyframes rowIn {
  from {
    opacity: 0;
    transform: translateX(-8px);
    background: color-mix(in srgb, var(--accent) 30%, transparent);
  }
  to { opacity: 1; transform: none; }
}

@keyframes rowPing {
  from { background: color-mix(in srgb, var(--accent) 26%, transparent); }
  to { background: transparent; }
}

/* --- Footer ----------------------------------------------------------- */

.hud-foot {
  grid-area: foot;
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-tight) var(--space-4);
  border: 1px solid var(--edge);
  background: var(--panel);
  color: var(--ink-dim);
  font-size: var(--fs-pico);
  letter-spacing: 0.16em;
  transition: var(--t-surface), var(--t-color);
}

.hud-foot .pulse {
  margin-left: auto;
  color: var(--ink-body);
}

/* --- Briefing --------------------------------------------------------- */
/*
 * The one piece of plain prose on the page. It reads as another footer hint
 * until you open it, then stands up as a panel above the bar — same surface,
 * same edge, same ladder as every other panel, so it does not arrive as a
 * different piece of software. Collapsed, not hidden: the markup is in the
 * DOM either way, which is what lets a search engine see what this is.
 */

.brief {
  position: relative;
}

.brief > summary {
  cursor: pointer;
  color: var(--accent);
  list-style: none;
  transition: var(--t-color);
}

.brief > summary::-webkit-details-marker {
  display: none;
}

.brief > summary::before {
  content: "▸ ";
}

.brief[open] > summary::before {
  content: "▾ ";
}

.brief > summary:hover,
.brief > summary:focus-visible {
  color: var(--accent-hover);
}

.brief-body {
  position: absolute;
  bottom: calc(100% + var(--space-2));
  left: 0;
  z-index: var(--z-panel);
  width: max(0px, min(30rem, calc(100vw - var(--space-6))));
  max-height: 61.8vh; /* 100 / phi */
  overflow-y: auto;
  padding: var(--space-4);
  border: 1px solid var(--edge-strong);
  background: var(--panel);
  backdrop-filter: blur(6px);
  /* Prose, not readouts: normal tracking and the body leading. */
  color: var(--ink-body);
  font-size: var(--fs-micro);
  letter-spacing: normal;
  line-height: var(--lh-body);
  user-select: text;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-dim) transparent;
}

.brief-body h2 {
  margin: 0 0 var(--space-2);
  color: var(--ink);
  font-size: var(--fs-caption);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
}

.brief-body p {
  margin: 0 0 var(--space-3);
}

.brief-body p:last-child {
  margin-bottom: 0;
}

.brief-body strong {
  color: var(--ink);
  font-weight: 700;
}

.brief-body a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: var(--t-color);
}

.brief-body a:hover {
  color: var(--accent-hover);
}

.cursor {
  display: inline-block;
  width: var(--space-tight);
  height: 0.9em;
  margin-left: var(--space-hair);
  background: currentColor;
  vertical-align: -0.1em;
  animation: blink 1.05s steps(2) infinite;
}

@keyframes blink {
  to { opacity: 0; }
}

/* --- Errors / boot ---------------------------------------------------- */

.fatal {
  position: fixed;
  inset: auto 0 45% 0;
  z-index: 9;
  margin: 0 auto;
  max-width: 38rem;
  padding: var(--space-4);
  text-align: center;
  color: var(--sig-alert);
  border: 1px solid var(--sig-alert);
  background: color-mix(in srgb, var(--ground) 92%, transparent);
  font-size: var(--fs-micro);
  /* A fatal message is a sentence someone has to read under pressure. */
  letter-spacing: normal;
  line-height: var(--lh-body);
}

body.is-booting .hud {
  opacity: 0.35;
}

/* --- Narrow viewports -------------------------------------------------- */

@media (max-width: 1180px) {
  .hud {
    grid-template-columns: 1fr;
    grid-template-areas:
      "bar"
      "log"
      "foot";
  }
  .rail {
    display: none;
  }
  .panel-log {
    max-height: 38.2vh; /* 100 / phi^2 */
    align-self: end;
  }
}

/* ---- Catalogue source panel ------------------------------------------- */
/* Provenance is a first-class readout here: the console is showing real
   catalogue objects, and the operator has to be able to tell that at a glance
   rather than take it on trust. */

.panel-source .source-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-tight);
}

.origin {
  font-size: var(--fs-pico);
  letter-spacing: 0.09em;
  padding: 0.1rem 0.36rem;
  border: 1px solid currentColor;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

/*
 * Four mutually exclusive provenance states, so only one hue is ever on
 * screen. Real catalogue data is the unremarkable case and stays neutral;
 * synthetic data is the one an operator must not mistake for real, so it is
 * the one that gets flagged.
 */
.origin.is-live { color: var(--sig-good); }
.origin.is-real { color: var(--ink); }
.origin.is-synth { color: var(--sig-caution); }
.origin.is-pending { color: var(--ink-dim); }

.origin-count {
  font-size: var(--fs-pico);
  color: var(--ink-dim);
  letter-spacing: 0.06em;
}

.source-detail {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  font-size: var(--fs-pico);
  line-height: 1.35;
  color: var(--ink-dim);
}

.source-events {
  list-style: none;
  margin: var(--space-tight) 0 0;
  padding: var(--space-hair) 0 0;
  border-top: 1px solid var(--edge);
  font-size: var(--fs-atto);
  color: var(--ink-dim);
}

.source-events li { display: flex; gap: 0.3rem; flex-wrap: wrap; line-height: 1.35; }
.source-events b { color: var(--sig-alert); font-weight: 500; }
.source-events i { color: var(--ink-dim); font-style: normal; }

.source-site {
  margin-top: var(--space-tight);
  padding-top: var(--space-hair);
  border-top: 1px solid var(--edge);
  font-size: var(--fs-atto);
  color: var(--ink-dim);
  letter-spacing: 0.05em;
}

/* ---- Provenance markers on contacts ------------------------------------ */

.src {
  font-size: var(--fs-atto);
  letter-spacing: 0.08em;
  padding: 0.02rem 0.26rem;
  border: 1px solid currentColor;
  border-radius: var(--radius-sm);
}

.src.is-real { color: var(--ink-dim); }
.src.is-synth { color: var(--sig-caution); }

.detail-real,
.lock-real {
  display: flex;
  align-items: center;
  gap: 0.32rem;
  flex-wrap: wrap;
  margin: var(--space-hair) 0;
  font-size: var(--fs-pico);
  color: var(--ink-dim);
}

.detail-real b,
.lock-real b { color: var(--ink); font-weight: 500; letter-spacing: 0.03em; }
.detail-real i,
.lock-real i { font-style: normal; color: var(--ink-dim); }
.detail-real i.ev { color: var(--sig-alert); }

/* The catalogue name of a real object, shown inline in the log. */
.log-row .c-id .objname {
  display: block;
  font-style: normal;
  font-size: var(--fs-atto);
  letter-spacing: 0.02em;
  color: var(--ink-dim);
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.log-row.is-selected .c-id .objname { color: var(--accent); }

/* ---- Rate control ------------------------------------------------------ */

.warp { display: inline-flex; align-items: center; gap: 0.25rem; }

.warp select {
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--edge-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: var(--fs-pico);
  letter-spacing: 0.06em;
  padding: 0.05rem 0.2rem;
}

/* Warped time is not real time; the clock says so rather than implying now. */
.clock.is-warped { color: var(--sig-caution); }

/* Catching up to real time: the clock is running fast on purpose. */
.status.is-syncing {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
