/* Single dark theme on purpose: this is a lit panel on a wall in a room, not a
   document that should follow anyone's OS preference. Every colour is explicit
   so the page holds up regardless of what is behind it. */

:root {
  --s0: #0b0f16;
  --s1: #10151d;
  --s2: #161d28;
  --hair: rgba(232, 237, 244, 0.08);
  --i1: #e8edf4;
  --i2: #8b96a8;
  --i3: #5a6474;

  /* Status colours are reserved: never reused as a series colour. */
  --good: #0ca30c;
  --good-text: #37c74f;
  --warn: #fab219;
  --warn-text: #ffce63;
  --crit: #d03b3b;
  --crit-text: #ff8a8a;

  /* Source identity, validated for colour-vision deficiency on --s1. */
  --sowitec: #3987e5;
  --autarq: #d95926;
  --beszel: #9085e9;
  --kuma: #c98500;
  --aethereon: #d55181;

  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--s0);
  color: var(--i1);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow: hidden; /* a wall never scrolls; the handheld layout re-enables it */
}

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

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.05em;
  color: var(--i2);
  text-transform: uppercase;
  white-space: nowrap;
}
.chip i { width: 11px; height: 11px; border-radius: 3px; flex: none; }

/* ------------------------------------------------------------------ shell */

#login {
  display: none;
  place-items: center;
  height: 100%;
}
body[data-state="auth"] #login { display: grid; }
body[data-state="auth"] #board { display: none; }

#login form {
  background: var(--s1);
  border: 1px solid var(--hair);
  border-radius: 12px;
  padding: 32px 36px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 320px;
}
#login h1 { margin: 0; font-size: 20px; font-weight: 700; }
#login p { margin: 0; color: var(--i2); font-size: 14px; }
#login input {
  background: var(--s0);
  border: 1px solid var(--hair);
  border-radius: 8px;
  color: var(--i1);
  padding: 11px 13px;
  font-size: 15px;
  font-family: var(--sans);
}
#login button {
  background: var(--sowitec);
  border: 0;
  border-radius: 8px;
  color: #fff;
  padding: 11px 13px;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
#login .err { color: var(--crit-text); font-size: 14px; min-height: 1.2em; }

:focus-visible { outline: 2px solid var(--sowitec); outline-offset: 2px; }

#board { display: flex; flex-direction: column; height: 100%; }

/* ------------------------------------------------------------------ brand */

.brand {
  flex: none;
  display: flex;
  align-items: center;
  gap: 16px;
  height: 54px;
  padding: 0 20px;
  border-bottom: 1px solid var(--hair);
  background: var(--s0);
}
/* The lockup is a wide wordmark (~3.8:1); height-constraining it keeps the bar
   slim, since vertical space on a 1080p wall is the scarce resource. */
.brand img { height: 26px; width: auto; display: block; }
.brand .rule { width: 1px; height: 22px; background: var(--hair); }
.brand .name {
  font-family: var(--mono);
  font-size: 15px;
  letter-spacing: 0.16em;
  color: var(--i2);
  text-transform: lowercase;
}
.brand .clock { margin-left: auto; font-family: var(--mono); font-size: 19px; color: var(--i1); }
.brand .clock .date { color: var(--i3); margin-right: 14px; font-size: 15px; }

/* --------------------------------------------------------------- KPI band */

.kband {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  border-bottom: 1px solid var(--hair);
  background: var(--s1);
  flex: none;
}
.k {
  padding: 14px 20px;
  border-right: 1px solid var(--hair);
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}
.k:last-child { border-right: 0; }
.k b { font-family: var(--mono); font-size: clamp(20px, 1.8vw, 32px); font-weight: 700; line-height: 1.1; }
.k b.warnv { color: var(--warn-text); }
.k b.critv { color: var(--crit-text); }
.k b.okv { color: var(--good-text); }
.k span {
  font-size: clamp(10px, 0.7vw, 12px);
  color: var(--i3);
  margin-top: 3px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

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

.grid {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr minmax(420px, 32%);
  gap: 14px;
  padding: 12px 18px;
  min-height: 0;
}
.col { display: flex; flex-direction: column; gap: 11px; min-height: 0; min-width: 0; }
.col-right { overflow: hidden; }

/* A wall has a fixed height, so every section stretches to use it rather than
   leaving a pool of dead space at the bottom. Taller cards mean taller
   sparklines, which is the one thing that genuinely benefits from the room. */
.col-left > div { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.col-right > .tile { flex: 1; }
.col-right > #aethereon { flex: 1.2; }
.col-right > #heatmap { flex: 1.35; }
.col-right > #dns { flex: 0.72; }
#dns.critical {
  border-color: rgba(208, 59, 59, 0.55);
  background: linear-gradient(180deg, rgba(208, 59, 59, 0.12), rgba(208, 59, 59, 0) 70%), var(--s1);
}
#dns .dnsfoot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-top: 7px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--i3);
}
#dns .dnsfoot .now { font-size: 20px; font-weight: 700; color: var(--i1); }
#dns .dnsfoot .now.critv { color: var(--crit-text); }
#dns svg.spark { width: 100%; flex: 1; min-height: 24px; margin-top: 8px; }

.group-head { display: flex; align-items: baseline; gap: 12px; }
.group-head .ratio { margin-left: auto; font-family: var(--mono); font-size: 13px; color: var(--i3); }
.group-head .ratio b { color: var(--i1); font-weight: 600; }

.cards {
  display: grid;
  /* auto-fill, not a fixed four: the homelab grid holds 27 cards once the Kuma
     devices join it, and forcing four columns pushes it to seven rows that do
     not fit the wall. minmax(0, 1fr) rows plus overflow:hidden guarantee cards
     shrink rather than spill over the section below them. */
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  grid-auto-rows: minmax(84px, 1fr);
  gap: 8px;
  margin-top: 8px;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

/* ------------------------------------------------------------ server card */

.card {
  background: var(--s2);
  border: 1px solid var(--hair);
  border-radius: 9px;
  padding: 9px 11px 8px;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.card.hot { border-color: rgba(250, 178, 25, 0.5); }
.card.critical, .card.down {
  border-color: rgba(208, 59, 59, 0.6);
  background: linear-gradient(180deg, rgba(208, 59, 59, 0.12), rgba(208, 59, 59, 0) 70%), var(--s2);
}
.card .n { display: flex; align-items: baseline; gap: 8px; }
.card .n b {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card .n span { margin-left: auto; font-family: var(--mono); font-size: 12.5px; color: var(--i2); white-space: nowrap; }
.card .n b.fit-sm { font-size: 11.5px; letter-spacing: -0.01em; }
.card .n b.fit-wrap {
  white-space: normal;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  line-height: 1.12;
  font-size: 11px;
  word-break: break-word;
}
.card .n span.hot { color: var(--warn-text); font-weight: 700; }
.card .n span.crit { color: var(--crit-text); font-weight: 700; }
.card svg.spark { display: block; margin-top: 4px; width: 100%; flex: 1; min-height: 16px; }

.m3 { display: grid; grid-template-columns: 30px 1fr 26px; gap: 6px; align-items: center; margin-top: 4px; }
.m3 label { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; color: var(--i3); }
.m3 .val { font-family: var(--mono); font-size: 12px; color: var(--i2); text-align: right; }
.m3 .val.hot { color: var(--warn-text); font-weight: 700; }
.m3 .val.crit { color: var(--crit-text); font-weight: 700; }

/* Kuma devices sit in the same grid as Beszel servers but have no CPU/RAM/disk -
   only reachability. The uptime strip fills the space the meters would occupy,
   so the two card kinds stay the same height without padding one out. */
.card.probe .strip {
  display: flex;
  gap: 2px;
  margin-top: 7px;
  flex: none;
  height: 10px;
}
.card.probe .strip i { flex: 1; border-radius: 2px; background: rgba(12, 163, 12, 0.5); }
.card.probe .strip i.down { background: var(--crit); }
.card.probe .strip i.gap { background: rgba(139, 150, 168, 0.14); }
.card.probe .foot {
  display: flex;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--i3);
  letter-spacing: 0.06em;
  margin-top: 6px;
}

.bar { height: 9px; border-radius: 5px; background: rgba(139, 150, 168, 0.16); overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 5px; background: var(--beszel); }
.bar i.hot { background: var(--warn); }
.bar i.crit { background: var(--crit); }

/* -------------------------------------------------- capacity bars (cloud) */

.split {
  display: grid;
  grid-template-columns: 500px 1fr;
  gap: 12px;
  flex: 1;
  min-height: 0;
  margin-top: 8px;
}
.split .cards { grid-template-columns: repeat(2, 1fr); margin-top: 0; }

.capviz {
  background: var(--s1);
  border: 1px solid var(--hair);
  border-radius: 10px;
  padding: 11px 14px 9px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
}
.capviz .vt {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--i3);
  text-transform: uppercase;
}
.capviz .vt .r { margin-left: auto; letter-spacing: 0.03em; color: var(--i2); }

.caprow { display: flex; align-items: center; gap: 9px; flex: 1; min-height: 0; }
.caprow .ch {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--i3);
  width: 84px;
  text-align: right;
  flex: none;
  overflow: hidden;
  text-overflow: ellipsis;
}
.caprow .track {
  position: relative;
  flex: 1;
  height: 20px;
  background: rgba(139, 150, 168, 0.12);
  border-radius: 5px;
  display: flex;
  gap: 2px;
  padding: 2px;
  align-items: stretch;
}
.caprow .seg {
  background: var(--beszel);
  border-radius: 3px;
  min-width: 2px;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.caprow .seg span {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  color: #0b0f16;
  padding: 0 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.caprow .seg.bad { background: var(--crit); }
/* The tick rides above the track: host RAM in use, from Beszel - the gap
   between it and the violet segments is memory used by non-container processes. */
.caprow .tick {
  position: absolute;
  top: -6px;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 7px solid var(--warn);
  transform: translateX(-50%);
}
.caprow .cv { font-family: var(--mono); font-size: 10.5px; color: var(--i2); width: 108px; flex: none; text-align: right; }
.caprow .cv em { font-style: normal; color: var(--i3); }

/* ------------------------------------------------------------------ tiles */

.tile {
  background: var(--s1);
  border: 1px solid var(--hair);
  border-radius: 10px;
  padding: 13px 15px 11px;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.tile .t { display: flex; align-items: baseline; gap: 10px; }
.tile .t .chip { font-size: 12px; }
.tile .t .v { margin-left: auto; font-family: var(--mono); font-weight: 700; font-size: 19px; }
.tile .t .v.critv { color: var(--crit-text); }
.tile .sub { color: var(--i3); font-size: 12.5px; margin-top: 2px; }
.tile svg.chart { display: block; width: 100%; margin-top: 10px; flex: 1; min-height: 60px; }

.lgd {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 9px;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--i3);
  letter-spacing: 0.05em;
}
.lgd i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.lgd b { color: var(--i1); font-weight: 600; }
.lgd .pending { opacity: 0.55; }

.xlbl { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 11px; color: var(--i3); margin-top: 5px; }

/* --------------------------------------------------------- ninjaone tiles */

.status-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; flex: none; }
.nj {
  background: var(--s1);
  border: 1px solid var(--hair);
  border-radius: 10px;
  padding: 14px 18px 13px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.nj .state { font-family: var(--sans); font-size: clamp(18px, 1.6vw, 28px); font-weight: 800; line-height: 1.1; }
.nj.ok .state { color: var(--good-text); }
.nj.crit .state { color: var(--crit-text); }
.nj.crit {
  border-color: rgba(208, 59, 59, 0.55);
  background: linear-gradient(180deg, rgba(208, 59, 59, 0.12), rgba(208, 59, 59, 0) 70%), var(--s1);
}
.nj .det { font-family: var(--mono); font-size: 13.5px; color: var(--i1); }
.nj .sub { color: var(--i3); font-size: 12.5px; }
.nj.ok .det { color: var(--i2); }

/* heatmap tooltip */
.tip {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 50;
  pointer-events: none;
  background: var(--s2);
  border: 1px solid rgba(232, 237, 244, 0.16);
  border-radius: 8px;
  padding: 8px 11px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.5;
  white-space: nowrap;
}
.tip b { display: block; color: var(--i1); font-weight: 600; }
.tip .v { color: var(--kuma); font-weight: 700; }
.tip .v.down { color: var(--crit-text); }
.tip .w { color: var(--i3); margin-left: 8px; }
.heat i { cursor: crosshair; }
.heat i.pad { cursor: default; }
.nj.pending, .nj.unreachable { border-style: dashed; background: transparent; }
.nj.pending .state { color: var(--i3); font-size: 20px; font-weight: 700; }
.nj.unreachable .state { color: var(--warn-text); font-size: 20px; font-weight: 700; }

/* ---------------------------------------------------------------- heatmap */

.heatwrap { display: flex; flex-direction: column; gap: 3px; margin-top: 8px; flex: 1; min-height: 0; }
.heatrow { display: flex; align-items: stretch; gap: 8px; flex: 1; min-height: 0; }
.heatrow .hl {
  font-family: var(--mono);
  /* 18 rows share ~170px, so each label lives on a ~9px line. 8px type with a
     solid line-height is the largest that stays one-per-row without collisions;
     at the TV's 2x scale it reads as 16px from up close. */
  font-size: 8px;
  line-height: 1;
  color: var(--i3);
  width: 88px;
  flex: none;
  text-align: right;
  align-self: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.heat { display: grid; gap: 3px; flex: 1; min-height: 0; }
.heat i { border-radius: 3px; display: block; min-height: 7px; height: 100%; }

/* ------------------------------------------------------------- aethereon */

.triage { display: flex; height: 24px; border-radius: 6px; overflow: hidden; margin-top: 10px; }
.triage b {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 700;
  color: #0b0f16;
  min-width: 0;
}
.triage .todo { background: var(--crit); color: #fff; }
.triage .prog { background: var(--warn); }
.triage .done { background: #2e7d3a; color: #eafbea; }

/* -------------------------------------------------------------- statusbar */

.statusbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 0 20px;
  height: 40px;
  border-top: 1px solid var(--hair);
  background: var(--s1);
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--i3);
  overflow: hidden;
}
.statusbar .src { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.statusbar .src i { width: 8px; height: 8px; border-radius: 50%; background: var(--good); }
.statusbar .src.stale i { background: var(--warn); }
.statusbar .src.unreachable i { background: var(--crit); }
.statusbar .src.pending i { background: var(--i3); }
.statusbar .clock { margin-left: auto; color: var(--i2); }
.statusbar .disconnected { color: var(--crit-text); font-weight: 700; }

/* ------------------------------------------------------------ responsive */

/* Below the wall width this stops being a fixed-height panel and becomes a
   normal scrolling page. Every "stretch to fill the wall" rule has to be undone
   here, or the sections fight over a viewport that no longer bounds them and
   end up overlapping. */
@media (max-width: 1400px) {
  html, body { overflow: auto; height: auto; }
  #board { height: auto; min-height: 100%; }
  .grid { grid-template-columns: 1fr; padding-bottom: 24px; }
  .cards { grid-template-columns: repeat(3, 1fr); grid-auto-rows: auto; flex: none; }
  .split { grid-template-columns: 1fr; }
  .split .cards { grid-template-columns: repeat(3, 1fr); }
  .caprow { min-height: 26px; }
  .col-left > div, .col-right > .tile, .col-right > #heatmap,
  .col-right > #dns, .col-right > #aethereon { flex: none; }
  .card svg.spark { flex: none; height: 34px; }
  .tile svg.chart { flex: none; height: 120px; }
  .heat { flex: none; grid-auto-rows: auto; }
  .heat i { height: 13px; }
  .statusbar { position: static; }
}

/* Handheld: the same data, stacked, at a size that survives a phone. */
@media (max-width: 700px) {
  .kband { grid-template-columns: repeat(2, 1fr); }
  .k { border-bottom: 1px solid var(--hair); padding: 12px 14px; }
  .k:nth-child(2n) { border-right: 0; }
  .grid { padding: 12px; gap: 18px; }
  .col { gap: 18px; }
  .cards { grid-template-columns: repeat(2, 1fr); gap: 9px; }
  .card { padding: 10px 11px 8px; }
  .card svg.spark { height: 28px; }
  .ninja-row { grid-template-columns: 1fr; }
  .tile svg.chart { height: 100px; }
  .statusbar { height: auto; flex-wrap: wrap; gap: 10px; padding: 12px; }
  .statusbar .clock { margin-left: 0; width: 100%; }
}

/* drag reordering */
body.can-edit .cards[data-group] .card { cursor: grab; }
body.can-edit .cards[data-group] .card:active { cursor: grabbing; }
.card.dragging { opacity: 0.45; outline: 1.5px dashed var(--sowitec); outline-offset: 2px; }

@media (prefers-reduced-motion: no-preference) {
  .card.critical, .nj.crit { animation: pulse 2.4s ease-in-out infinite; }
}
@keyframes pulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.28); }
}
