:root {
  --bg: #f4f5f7;
  --panel: #ffffff;
  --ink: #1c2024;
  --muted: #6b7280;
  --line: #e3e5e9;
  --accent: #2f6f4e;
  --accent-ink: #ffffff;
  --danger: #a4302a;
  --ok: #2e9e5b;
  --warn: #b7791f;
  --radius: 10px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #16181c;
    --panel: #1e2126;
    --ink: #e8eaed;
    --muted: #9aa1ac;
    --line: #2c3037;
    --accent: #4f9e75;
    --accent-ink: #0e1114;
    --danger: #e0736c;
    --ok: #55c98a;
    --warn: #d9a441;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

body.centered {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 1rem;
}

/* --- Kopfleiste --- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 0 1.5rem;
  height: 56px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  font-weight: 650;
  letter-spacing: -0.01em;
  text-decoration: none;
  color: var(--ink);
}
.brand span { color: var(--accent); }

.topbar nav { display: flex; gap: 1.25rem; margin-right: auto; }
.topbar nav a {
  text-decoration: none;
  color: var(--muted);
  padding: 0.35rem 0;
  border-bottom: 2px solid transparent;
}
.topbar nav a:hover { color: var(--ink); }
.topbar nav a.on { color: var(--ink); border-bottom-color: var(--accent); }

main {
  /* Breit genug für 168 Stundenbalken. Formulare und Fließtext
     behalten ihre Breite über die Raster darunter - was von Breite
     profitiert, sind Tabellen und Diagramme. */
  max-width: 1400px;
  margin: 0 auto;
  padding: 1.75rem 1.5rem 4rem;
}

.pagehead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.pagehead h1 { margin: 0; }
.actions { display: flex; gap: 0.5rem; align-items: center; }

h1 { font-size: 1.5rem; font-weight: 650; letter-spacing: -0.015em; }
h2 { font-size: 1rem; font-weight: 650; margin: 0 0 0.75rem; }

/* --- Karten --- */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.25rem;
}
.card.danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--line)); }

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}
.cards .card { margin: 0; }

.stat .label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin-bottom: 0.4rem;
}
.stat .value { font-size: 1.2rem; font-weight: 600; }

.split {
  display: grid;
  grid-template-columns: minmax(320px, 380px) 1fr;
  gap: 1.25rem;
  align-items: start;
}
@media (max-width: 800px) { .split { grid-template-columns: 1fr; } }
.stack > .card:last-child { margin-bottom: 1.25rem; }

/* --- Formulare --- */

label {
  display: block;
  margin-bottom: 1rem;
  font-weight: 550;
  font-size: 0.9rem;
}
label small {
  display: block;
  font-weight: 400;
  color: var(--muted);
  margin-top: 0.3rem;
  font-size: 0.82rem;
}
label.check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 400;
}

input[type=text], input[type=password], input[type=number], select, textarea {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}
label.check input { margin: 0; width: auto; }

textarea {
  font-family: var(--mono);
  font-size: 0.88rem;
  resize: vertical;
  min-height: 3.5rem;
}
select { cursor: pointer; }
input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

/* Passwortfeld mit Augenschaltfläche.
   Die Schaltfläche sitzt im Feld; der rechte Innenabstand hält den Text
   von ihr weg, sonst läuft ein langes Passwort darunter. */
/* Der Augenknopf gibt es an zwei Orten: im Eingabefeld (.pw) und
   freistehend neben einem Schlüssel. Gemeinsam ist das Umschalten
   zwischen den beiden Symbolen, verschieden nur die Platzierung. */
.eye {
  display: grid;
  place-items: center;
  padding: 0.3rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.eye:hover { color: var(--ink); }
.eye[aria-pressed="true"] { color: var(--accent); }
.eye:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* Sichtbar ist genau ein Symbol - welches, sagt aria-pressed. Beide
   liegen übereinander im selben Rasterfeld, damit das Umschalten die
   Schaltfläche nicht in der Größe springen lässt. */
.eye svg { grid-area: 1 / 1; }
.eye .zu { display: none; }
.eye[aria-pressed="true"] .offen { display: none; }
.eye[aria-pressed="true"] .zu { display: block; }

.pw { position: relative; }
.pw input { padding-right: 2.6rem; }
.pw .eye {
  position: absolute;
  right: 1px;
  top: 0.35rem;
  bottom: 1px;
  width: 2.4rem;
  padding: 0;
  border: none;
  border-radius: 0 6px 6px 0;
}

/* Eine Störung beim Nachladen gehört neben die Schaltfläche, nicht
   in das Feld: im Feld sähe eine Fehlermeldung aus wie ein Wert. */
.secret-error {
  grid-column: 1 / -1;
  color: var(--danger);
}

.inline-form {
  display: flex;
  gap: 1rem;
  align-items: flex-end;
  flex-wrap: wrap;
}
.inline-form label { margin-bottom: 0; }
.inline-form .grow { flex: 1 1 240px; }

button, .button {
  display: inline-block;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-weight: 550;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
button:hover, .button:hover { border-color: var(--muted); }
button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
button.destructive {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
button.small, .button.small { padding: 0.3rem 0.6rem; font-size: 0.85rem; }
button.link {
  border: 0;
  background: none;
  color: var(--muted);
  padding: 0;
  font-weight: 400;
}
button.link:hover { color: var(--ink); text-decoration: underline; }

/* --- Tabellen --- */

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 1.25rem;
}
th, td {
  text-align: left;
  padding: 0.65rem 0.9rem;
  border-bottom: 1px solid var(--line);
}
th {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  font-weight: 600;
}
tbody tr:last-child td { border-bottom: 0; }
tr.disabled td { opacity: 0.55; }
.num { text-align: right; }
td a { color: var(--ink); font-weight: 550; }

dl.kv { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 0.4rem 1rem; }
dl.kv dt { color: var(--muted); font-size: 0.88rem; }
dl.kv dd { margin: 0; }

ul.plain { list-style: none; padding: 0; margin: 0; }
ul.plain li { padding: 0.15rem 0; }

/* --- Bausteine --- */

.dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: 0.45rem;
  vertical-align: baseline;
  background: var(--muted);
}
.dot.on { background: var(--ok); }
.dot.off { background: var(--danger); }
.dot.unknown { background: var(--warn); }

/* Ein aktiver Peer pulsiert. Nur bei Peers, nicht beim Interface-Punkt:
   dass wg0 läuft, ändert sich selten, und ein dauerpulsierendes Feld
   oben auf der Seite wäre reine Unruhe.

   Der Ring ist ein eigenes Element hinter dem Punkt. Den Punkt selbst zu
   skalieren würde die Textzeile daneben verschieben. */
.peerstate .dot.on { position: relative; }
.peerstate .dot.on::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--ok);
  animation: peerpuls 1.8s ease-out infinite;
  pointer-events: none;
}
@keyframes peerpuls {
  0%   { transform: scale(1);   opacity: 0.55; }
  70%  { transform: scale(2.8); opacity: 0; }
  100% { transform: scale(2.8); opacity: 0; }
}
/* Wer Bewegung abbestellt hat, bekommt den Punkt ohne Puls. Die Aussage
   steht ohnehin als Text daneben, es geht also nichts verloren. */
@media (prefers-reduced-motion: reduce) {
  .peerstate .dot.on::after { animation: none; display: none; }
}

/* Bricht das Nachziehen ab, wird der Hinweis darunter blass - damit
   niemand veraltete Zahlen für aktuelle hält. */
.hint.stale { opacity: 0.45; }

.tag {
  display: inline-block;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 0.75rem;
  color: var(--muted);
  font-weight: 500;
}

.alert {
  padding: 0.75rem 1rem;
  border-radius: var(--radius);
  margin-bottom: 1.25rem;
  border: 1px solid;
}
.alert.error {
  background: color-mix(in srgb, var(--danger) 10%, var(--panel));
  border-color: color-mix(in srgb, var(--danger) 35%, var(--line));
}
.alert.notice {
  background: color-mix(in srgb, var(--warn) 12%, var(--panel));
  border-color: color-mix(in srgb, var(--warn) 35%, var(--line));
}

.hint { color: var(--muted); font-size: 0.88rem; margin: 0 0 0.9rem; }
.hint.warn { color: var(--warn); }
.muted { color: var(--muted); }
.empty { color: var(--muted); padding: 1.5rem 0; }

.mono { font-family: var(--mono); font-size: 0.88em; }
.break { word-break: break-all; }

pre.conf {
  font-family: var(--mono);
  font-size: 0.85rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.9rem 1rem;
  overflow-x: auto;
  margin: 0 0 1rem;
}

.tabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}
.tab {
  padding: 0.4rem 0.75rem;
  text-decoration: none;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: 0.9rem;
  font-weight: 550;
}
.tab:hover { color: var(--ink); }
.tab.on { color: var(--ink); border-bottom-color: var(--accent); }

.notice-inline {
  background: color-mix(in srgb, var(--warn) 12%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--warn) 30%, var(--line));
  border-radius: 7px;
  padding: 0.6rem 0.75rem;
  text-align: left;
}

.qr { text-align: center; }
.qr img {
  width: 320px;
  height: 320px;
  max-width: 100%;
  background: #fff;
  padding: 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
}

/* --- Anmeldung --- */

.login { width: min(360px, 100%); margin: 0; }
.login h1 { margin: 0 0 1.25rem; font-size: 1.35rem; }
.login h1 span { color: var(--accent); }
.login button { width: 100%; }

/* --- Schlüsselanzeige --- */

.keyrow {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 0.4rem 0.6rem;
  align-items: center;
  margin-bottom: 0.5rem;
}
.keyrow .keylabel {
  grid-column: 1 / -1;
  font-size: 0.88rem;
  color: var(--muted);
}
.keyrow code {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 0.4rem 0.55rem;
  font-size: 0.82rem;
  user-select: all;
}

/* --- Verbrauchsdiagramm --------------------------------------------
   Zwei Serien aus den ersten beiden Kategorienplätzen einer geprüften
   Palette. Die Trennung liegt in beiden Modi weit über dem Zielwert für
   Farbfehlsichtigkeit; Identität hängt zusätzlich an Legende und
   Tabellenansicht, nie an der Farbe allein. */

:root {
  --series-rx: #2a78d6;
  --series-tx: #eb6834;
}
@media (prefers-color-scheme: dark) {
  :root {
    --series-rx: #3987e5;
    --series-tx: #d95926;
  }
}

.legend { display: flex; gap: 1.25rem; flex-wrap: wrap; margin-bottom: 0.9rem; font-size: 0.88rem; }
.legend .key { display: flex; align-items: center; gap: 0.4rem; color: var(--ink); }
.legend .swatch { width: 10px; height: 10px; border-radius: 2px; }
.legend .swatch.s1 { background: var(--series-rx); }
.legend .swatch.s2 { background: var(--series-tx); }

.chartwrap { position: relative; margin-bottom: 1rem; }
.chartwrap .ymax {
  position: absolute; top: 0; right: 0;
  font-size: 0.75rem; color: var(--muted);
  background: var(--panel); padding-left: 0.4rem;
}
.chart { display: block; overflow: visible; }
.chart .axis { stroke: var(--line); stroke-width: 1; }
/* 2px Fugen zwischen den gestapelten Flächen: sonst verschmelzen sie. */
.chart .bar-rx { fill: var(--series-rx); stroke: var(--panel); stroke-width: 2; }
.chart .bar-tx { fill: var(--series-tx); stroke: var(--panel); stroke-width: 2; }
.chart .bar-empty { fill: var(--line); }
.chart g:hover .bar-rx, .chart g:hover .bar-tx { opacity: 0.75; }
.chart .tick { fill: var(--muted); font-size: 9px; font-family: inherit; }

.table-view summary { cursor: pointer; color: var(--muted); font-size: 0.88rem; }
.table-view table { margin-top: 0.75rem; }
form.spaced { margin-top: 0.75rem; }

/* --- Benutzerverwaltung --- */

/* Aufgeklappt wird die Zeile hoch. Ohne diese Zeile stünde der
   Benutzername mittig neben einem 600 Pixel hohen Formular. */
.konten td { vertical-align: top; }

.inline-actions { display: inline-block; text-align: left; }
.inline-actions summary { list-style: none; display: inline-block; }
.inline-actions summary::-webkit-details-marker { display: none; }
.card.nested {
  margin-top: 0.6rem;
  padding: 0.9rem;
  min-width: 260px;
  /* Sonst zieht sich das Formular über die ganze Tabellenbreite, und ein
     Passwortfeld ist dann 700 Pixel breit. */
  max-width: 28rem;
  background: var(--bg);
}
.card.nested label { margin-bottom: 0.6rem; }


/* --- Verbrauchsansicht ------------------------------------------- */

/* Der Zeitraum-Umschalter. Links, keine Schaltflächen: die Seite
   funktioniert ohne Skript, und ein Zeitraum ist verlinkbar. */
.periods {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 1rem;
}
.periods a {
  display: grid;
  gap: 0.15rem;
  padding: 0.45rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  text-decoration: none;
  color: var(--muted);
  background: var(--bg);
}
.periods a:hover { border-color: var(--accent); color: var(--ink); }
.periods a.on {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--panel));
  color: var(--ink);
}
.periods .pname { font-size: 0.8rem; }
.periods .pval { font-weight: 600; font-variant-numeric: tabular-nums; }

/* Sprungziel des Zeitraum-Wechsels ohne Skript - nicht bündig an den
   Fensterrand, sonst wirkt die Karte abgeschnitten. */
#verbrauch { scroll-margin-top: 1rem; }
/* Solange die neue Ansicht unterwegs ist. Nur gedämpft, nicht
   ausgeblendet: ein leeres Feld wäre unruhiger als ein blasses. */
#verbrauch.laedt { opacity: 0.6; }

.sparkhead {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 0.5rem;
  font-size: 0.86rem;
  color: var(--muted);
}
.sparkhead b { color: var(--ink); font-variant-numeric: tabular-nums; }

.spark {
  display: block;
  width: 100%;
  height: 46px;
  overflow: visible;
}
/* Die Kurve wird in der Breite gestreckt. Ohne vector-effect würde
   die Linie mitgestreckt und wäre waagerecht dick, senkrecht dünn. */
.spark polyline {
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.spark .spark-rx { stroke: var(--series-rx); }
.spark .spark-tx { stroke: var(--series-tx); }
.spark-empty { margin: 0.2rem 0 0; }

/* Der laufende Abschnitt bekommt einen Rahmen statt einer Farbe - die
   Farben sind für die beiden Richtungen vergeben. */
.chart .now rect { stroke: var(--muted); stroke-width: 0.5; }

td.rate { color: var(--muted); font-variant-numeric: tabular-nums; }


/* --- Zertifikatsübersicht ---------------------------------------- */

.certrow {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.7rem 0.85rem;
  margin-bottom: 0.7rem;
}
.certrow.aktiv { border-color: var(--accent); }
.certrow .certhead {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.4rem;
}
.certrow dl.kv { font-size: 0.88rem; }
.tag.on { border-color: var(--accent); color: var(--accent); }
.tag.warn { border-color: var(--danger); color: var(--danger); }
