/* drewbuerger/homelab
   ------------------------------------------------------------------
   Figtree for the interface, IBM Plex Mono for anything numeric.
   The split is functional, not decorative: capacities, percentages and
   temperatures sit in columns that have to line up, and only a mono face with
   tabular figures does that. Everything a person reads as language is Figtree.

   Surfaces are soft cards on a warm neutral ground rather than hairline boxes
   on a flat one. The array bay keeps its segmented LED strip, because that is
   the one place where a precise readout beats a friendly one.

   Self-hosted fonts: the CSP allows no external hosts, so a font CDN was never
   an option. Latin subset, 84KB for six faces.
   ------------------------------------------------------------------ */

@font-face { font-family: "Figtree"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("fonts/figtree-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("fonts/figtree-latin-500-normal.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("fonts/figtree-latin-600-normal.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 700;
  font-display: swap; src: url("fonts/figtree-latin-700-normal.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2"); }

:root {
  --bg:      #f2f4f7;
  --panel:   #ffffff;
  --raise:   #f8fafc;
  --edge:    #e6e9ef;
  --hair:    #eef1f5;
  --card-edge: transparent;
  --text:    #1a2030;
  --dim:     #667085;
  --faint:   #98a2b3;
  --jade:    #15803d;
  --amber:   #b45309;
  --rust:    #c0392b;
  --accent:  #3b5bd9;
  --shadow:  0 1px 2px rgba(16, 24, 40, .05), 0 4px 14px rgba(16, 24, 40, .06);
  --shadow-hover: 0 2px 4px rgba(16, 24, 40, .06), 0 10px 24px rgba(16, 24, 40, .10);

  --ui:   "Figtree", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --gut: 22px;
  --r:   12px;
  --r-sm: 8px;
}

/* Dark is defined once and applied from two places: the OS preference, guarded
   so an explicit light choice still wins, and an explicit dark choice. Without
   the guard the media query would keep forcing dark on someone who deliberately
   picked light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:      #0f131b;
    --panel:   #171d28;
    --raise:   #1e2532;
    --edge:    #2a3242;
    --hair:    #232b38;
    --card-edge: #262e3c;
    --text:    #e4e9f2;
    --dim:     #8f9bb0;
    --faint:   #5d6980;
    --jade:    #34d399;
    --amber:   #fbbf24;
    --rust:    #f87171;
    --accent:  #7b93ff;
    --shadow:  0 1px 2px rgba(0, 0, 0, .35);
    --shadow-hover: 0 4px 18px rgba(0, 0, 0, .45);
  }
}

:root[data-theme="dark"] {
  --bg:      #0f131b;
  --panel:   #171d28;
  --raise:   #1e2532;
  --edge:    #2a3242;
  --hair:    #232b38;
  --card-edge: #262e3c;
  --text:    #e4e9f2;
  --dim:     #8f9bb0;
  --faint:   #5d6980;
  --jade:    #34d399;
  --amber:   #fbbf24;
  --rust:    #f87171;
  --accent:  #7b93ff;
  --shadow:  0 1px 2px rgba(0, 0, 0, .35);
  --shadow-hover: 0 4px 18px rgba(0, 0, 0, .45);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; }

/* ---------- header ---------- */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 15px var(--gut);
  background: var(--panel);
  border-bottom: 1px solid var(--hair);
  position: sticky;
  top: 0;
  z-index: 5;
}

.mark {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.015em;
  text-decoration: none;
}

.mark-dim { color: var(--faint); font-weight: 500; }

.bar-right { display: flex; align-items: center; gap: 12px; }

.tally {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 4px 9px;
  border-radius: 100px;
  color: var(--dim);
  background: var(--raise);
}

.tally[data-all="yes"] { color: var(--jade); background: color-mix(in srgb, var(--jade) 12%, transparent); }
.tally[data-all="no"]  { color: var(--rust); background: color-mix(in srgb, var(--rust) 12%, transparent); }

.stamp {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.theme {
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--raise);
  border-radius: 100px;
}

.theme-btn {
  font-family: var(--ui);
  font-size: 11px;
  font-weight: 600;
  padding: 4px 11px;
  border: 0;
  border-radius: 100px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease;
}

.theme-btn:hover { color: var(--dim); }
.theme-btn[aria-pressed="true"] { color: var(--text); background: var(--panel); box-shadow: var(--shadow); }
.theme-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ---------- alerts ---------- */

/* Tinted, not white. Sitting on the same surface as the header, the alert read
   as another row of chrome; the whole point is that it is not. */
.alert {
  display: flex;
  gap: 10px;
  align-items: baseline;
  flex-wrap: wrap;
  padding: 12px var(--gut);
  font-size: 13px;
  border-bottom: 1px solid var(--hair);
}

.alert b { font-weight: 700; white-space: nowrap; }

.alert-warn { background: color-mix(in srgb, var(--amber) 9%, var(--panel)); }
.alert-warn b { color: var(--amber); }

.alert-bad { background: color-mix(in srgb, var(--rust) 9%, var(--panel)); }
.alert-bad b { color: var(--rust); }
.alert span { color: var(--dim); }

main { padding: 26px var(--gut) 44px; max-width: 1200px; margin: 0 auto; }

.eyebrow {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--dim);
  margin: 0 0 12px;
}

/* ---------- the bay ---------- */

.bay { margin-top: 4px; }

.bay-rows {
  border-radius: var(--r);
  background: var(--panel);
  border: 1px solid var(--card-edge);
  box-shadow: var(--shadow);
  overflow: hidden;
}

/* Name, capped LED strip, then the readout. The strip is held to 300px: across
   a 1200px row, 20 segments stop reading as drive indicators and start reading
   as a barcode. */
.row {
  display: grid;
  grid-template-columns: 92px minmax(0, 300px) 1fr 84px 62px 56px 78px;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--hair);
}

.row:last-child { border-bottom: 0; }

.row-label {
  color: var(--faint);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 9px 16px 7px;
  background: var(--raise);
  border-bottom: 1px solid var(--hair);
}

.segs { display: flex; gap: 2px; height: 16px; }

.segs i {
  flex: 1;
  background: var(--edge);
  border-radius: 2px;
  transform: scaleY(0.4);
  transition: transform 260ms ease, background 260ms ease;
  transition-delay: calc(var(--i) * 12ms);
}

.segs i.on { background: var(--jade); transform: scaleY(1); }
.segs.warm i.on { background: var(--amber); }
.segs.hot  i.on { background: var(--rust); }

.name { font-weight: 600; font-size: 14px; }

.num {
  font-family: var(--mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--dim);
}

.num-strong { color: var(--text); font-weight: 600; }

.state {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: right;
}

/* nowrap is a guard, not the layout. The breakpoint below is sized so this
   never truncates in practice; the ellipsis exists so a longer string, a
   Post-Read pass with a two-digit day count, degrades to one clipped line
   instead of silently doubling every bay row's height. */
.detail {
  color: var(--dim);
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- services ---------- */

.group { margin-bottom: 28px; }

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
}

.tile {
  display: flex;
  align-items: center;
  gap: 13px;
  text-decoration: none;
  padding: 14px 16px;
  border-radius: var(--r);
  background: var(--panel);
  border: 1px solid var(--card-edge);
  box-shadow: var(--shadow);
  transition: box-shadow 160ms ease, transform 160ms ease;
}

.tile:hover, .tile:focus-visible {
  box-shadow: var(--shadow-hover);
  transform: translateY(-2px);
}

.tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* 32px so Radarr and Sonarr are told apart without looking at the label. */
.logo {
  width: 32px;
  height: 32px;
  flex: none;
  object-fit: contain;
  border-radius: var(--r-sm);
}

.mono-badge {
  width: 32px;
  height: 32px;
  flex: none;
  display: grid;
  place-items: center;
  font-size: 14px;
  font-weight: 700;
  color: var(--faint);
  background: var(--raise);
  border-radius: var(--r-sm);
}

.tile-body { min-width: 0; flex: 1; }

.tile-top { display: flex; align-items: center; gap: 8px; }

.tile-name {
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.006em;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tile-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 1px;
  font-size: 13px;
  color: var(--dim);
}

.meta-left { display: flex; align-items: center; gap: 6px; min-width: 0; }
.meta-left > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.lan {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.07em;
  color: var(--faint);
  background: var(--raise);
  border-radius: 4px;
  padding: 1px 5px;
  flex: none;
}

/* Only ever rendered for a service that is not answering, so it is allowed to
   be loud. Healthy tiles carry no text here at all. */
.tile-alarm {
  font-size: 12px;
  font-weight: 600;
  color: var(--rust);
  flex: none;
  white-space: nowrap;
}

.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); flex: none; }
.dot[data-state="up"]   { background: var(--jade); }
.dot[data-state="down"] { background: var(--rust); animation: pulse 1.8s ease-in-out infinite; }

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

footer {
  padding: 18px var(--gut) 34px;
  max-width: 1200px;
  margin: 0 auto;
  color: var(--faint);
  font-size: 13px;
  border-top: 1px solid var(--hair);
}

.ns { padding: var(--gut); line-height: 1.9; }

/* ---------- load sequence ---------- */

[data-state="loading"] main { opacity: 0; }
main { opacity: 1; transition: opacity 220ms ease; }

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

/* 1000px, not 620px: the bay's fixed columns total 372px plus the strip's 300px
   and six 14px gaps, so the detail column gets nothing until the viewport
   clears roughly 976px. Below that, "Pre-Read 84% · 3d 22h left" compressed
   into a four-line stack and tripled every bay row's height. Stack instead. */
@media (max-width: 1000px) {
  .row { grid-template-columns: 1fr 62px 54px; row-gap: 8px; padding: 13px 14px; }
  .name { grid-column: 1; }
  .segs { grid-column: 1 / -1; order: 4; }
  .detail { grid-column: 1 / -1; order: 5; }
  .state { display: none; }
}

@media (max-width: 620px) {
  :root { --gut: 14px; }
  .tiles { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .tile { padding: 12px 13px; gap: 11px; align-items: flex-start; }
  .logo, .mono-badge { width: 28px; height: 28px; }
  /* Wrap rather than clip. Two columns at 390px leaves ~94px for the name, and
     "Home Assistant" does not fit; a second line costs less than hiding half a
     service's name behind an ellipsis. */
  .tile-name { font-size: 14px; white-space: normal; overflow: visible; line-height: 1.25; }
  .tile-top { align-items: flex-start; }
  .dot { margin-top: 5px; }
  .bar { padding: 12px var(--gut); }
}
