/* Shared chrome only: design tokens, the top bar, the source picker and the view tabs.
   Each view's own rules live beside its module in views/*.css, and self-contained panels in
   components/*.css. index.html links them in cascade order — this file first. */

/* Dark by default (a monitor reads well dark); light via prefers-color-scheme. */
:root {
  --bg: #0d1117;
  --surface: #161b22;
  --surface-2: #1c222b;
  --border: #2a313c;
  --text: #e6edf3;
  --muted: #8b949e;
  --accent: #4a9eff;
  --track: #30363d;
  --warning: #d29922;
  --critical: #f85149;
  /* The minimum/maximum-reading markers on the charts. The max is orange to match the hot end of the
     value fade; since the line's highest end is now also orange, both markers lean on their
     surface-coloured halo and larger radius (set in the chart components) to stay legible where they
     sit on a same-hued stretch of line. */
  --min: #2dd4bf;
  --max: #fb923c;
  /* The line fade. Coloured by value — cyan at the lowest reading shown, blue through the middle, amber
     at the highest — with the warning and critical zones overriding to orange and red where a sensor's
     limits are reached. See util/thresholds.js. */
  --fade-low: #22d3ee;
  --fade-mid: #3b82f6;
  --fade-high: #f59e0b;
  --fade-warn: #f97316;
  --fade-crit: #f85149;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4);
  --radius: 0.7rem;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Everything below is sized in rem against this, so the whole interface scales with the display
     rather than staying at a size chosen for a 1080p screen. On a 4K panel at 100% browser zoom a
     fixed 14px base is physically tiny — this is what the user would otherwise fix by zooming to
     150%, done once here instead.

     13px at phone widths, 14px at 1280, 16px at 1920, 18px at 2560, 22px at 3840. */
  font-size: clamp(13px, 0.3125vw + 10px, 22px);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f6f8fa;
    --surface: #ffffff;
    --surface-2: #f6f8fa;
    --border: #d0d7de;
    --text: #1f2328;
    --muted: #656d76;
    --accent: #0969da;
    --track: #eaeef2;
    --warning: #9a6700;
    --critical: #cf222e;
    --min: #0d9488;
    --max: #ea580c;
    --fade-low: #0891b2;
    --fade-mid: #2563eb;
    --fade-high: #d97706;
    --fade-warn: #ea580c;
    --fade-crit: #cf222e;
    --shadow: 0 1px 2px rgba(31, 35, 40, .08);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--sans);
  background: var(--bg);
  color: var(--text);
  font-size: 1.4rem;
  line-height: 1.4;
}

/* ---- top bar ---- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.14rem;
  padding: 0.86rem 1.43rem;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}

.brand { display: flex; align-items: center; gap: 0.71rem; font-size: 1.07rem; letter-spacing: .2px; }
.brand strong { font-weight: 650; }
.brand .dot {
  width: 0.64rem; height: 0.64rem; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0.21rem color-mix(in srgb, var(--accent) 22%, transparent);
}

.controls { display: flex; gap: 0.57rem; }
.controls input {
  width: min(42vw, 24rem);
  padding: 0.5rem 0.71rem;
  font: 0.93rem/1.2 var(--mono);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.57rem;
}
.controls input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.controls button {
  padding: 0.5rem 1rem;
  font: 600 0.93rem var(--sans);
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: 0.57rem;
  cursor: pointer;
}
.controls button:hover { filter: brightness(1.08); }

/* ---- source buttons ---- */
.sources { display: flex; gap: 0.43rem; }
.controls .src {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.86rem;
  font: 600 0.93rem var(--sans);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.57rem;
  cursor: pointer;
}
/* Hidden when the shown machine is not this one — inline-flex would otherwise override [hidden]. */
.controls .src[hidden] { display: none; }
.controls .src:hover { filter: brightness(1.12); }
.controls .src.active {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}

/* Secondary buttons that are not data sources — deliberately a different class, so the source
   picker's selector cannot pick them up. */
.controls .ghost, .rails-actions .ghost {
  display: inline-flex;
  align-items: center;
  gap: 0.36rem;
  padding: 0.5rem 0.86rem;
  font: 500 0.93rem var(--sans);
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.57rem;
  cursor: pointer;
}
.controls .ghost:hover, .rails-actions .ghost:hover { color: var(--text); }
.controls .ghost.active {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}

/* Reachability lamp. Grey until probed, so "unknown" never masquerades as "down". */
.lamp {
  width: 0.57rem; height: 0.57rem;
  border-radius: 50%;
  background: var(--muted);
  flex: none;
}
.src[data-live="yes"] .lamp {
  background: #3fb950;
  box-shadow: 0 0 0 0.21rem color-mix(in srgb, #3fb950 25%, transparent);
}
.src[data-live="no"] .lamp { background: var(--critical); opacity: .55; }
.src.active .lamp { box-shadow: none; }
.src.active[data-live="yes"] .lamp { background: #fff; }

/* The gear reads as the affordance on its own, so on a narrow bar the word can go and the button
   stays a recognisable target rather than wrapping the source picker onto another line.
   Its in-panel variant is styled in components/rails-editor.css. */
.gear { font-size: 1.07em; line-height: 1; }

/* ---- view tabs ---- */
.views { display: flex; gap: 0.29rem; }
.views .view {
  padding: 0.5rem 1rem;
  font: 600 0.93rem var(--sans);
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 0.57rem;
  cursor: pointer;
}
.views .view:hover { color: var(--text); }
.views .view.active {
  color: var(--text);
  background: var(--surface-2);
  border-color: var(--border);
}

[hidden] { display: none !important; }

/* ---- responsive ----
   The top bar is the part that breaks first: a fixed-width machine-id field beside the source
   buttons overflows well before phone width. Below 860px it becomes rows that wrap; below 560px the
   controls go full width so nothing is clipped or unreachable.

   Text size is not adjusted here — the fluid root font-size above already covers it.
   Each view's own reflow lives in its views/*.css. */
@media (max-width: 860px) {
  .topbar {
    flex-wrap: wrap;
    gap: 0.71rem;
    padding: 0.71rem 1rem;
  }
  .controls {
    flex-wrap: wrap;
    flex: 1 1 100%;
    gap: 0.43rem;
  }
  .controls input { flex: 1 1 13rem; width: auto; min-width: 0; }
  .sources { flex: 1 1 auto; }
  .views { order: 3; flex: 1 1 100%; }
}

@media (max-width: 560px) {
  .sources { display: grid; grid-template-columns: 1fr 1fr; gap: 0.43rem; }
  .controls .src { justify-content: center; }
  .controls button, .controls .src { padding: 0.57rem 0.71rem; }
  #railsToggle .label { display: none; }
}

/* ---- the selected machine ----
   Replaces the machineId text field. A machine arrives by URL or from the switcher beside this, so
   this names the current choice — a label that happens to be a button. */
.machine-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.43rem;
  max-width: 16rem;
  padding: 0.5rem 0.86rem;
  font: 600 0.93rem var(--sans);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.57rem;
  cursor: pointer;
}
.machine-chip::before {
  content: "";
  width: 0.5rem; height: 0.5rem; border-radius: 50%;
  background: var(--accent);
  flex: none;
}
.machine-chip:hover { filter: brightness(1.12); }
.machine-chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nothing chosen yet: recessive, and the dot stops claiming a machine is selected. */
.machine-chip.is-empty { color: var(--muted); font-weight: 500; }
.machine-chip.is-empty::before { background: var(--muted); opacity: .5; }

/* The quick machine switcher beside the chip. A native <select> — accessible and keyboard-driven for
   free — dressed to match the source buttons. Hidden until the roster has more than one machine. */
.machine-select {
  max-width: 14rem;
  padding: 0.5rem 0.72rem;
  font: 600 0.9rem var(--sans);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 0.57rem;
  cursor: pointer;
}
.machine-select:hover { filter: brightness(1.12); }
.machine-select:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.machine-select[hidden] { display: none; }

/* Machines is the fleet; the three after it act on whichever machine is selected. The rule says so
   without needing a second tab strip. */
.views-sep {
  width: 1px;
  align-self: stretch;
  margin: 0.29rem 0.36rem;
  background: var(--border);
}
