/* Oberfläche des Smart Habitat (#29, #30, #54, #71).
 *
 * Baut ausschliesslich auf den Tokens aus tokens.css auf. Steht hier ein
 * Hex-Wert, ist das ein Fehler.
 *
 * Aufbau der Navigation — eine Matrix, keine Liste mit Filter
 * -----------------------------------------------------------
 * LINKS die Funktions- und Medienachse (Übersicht, Kamera, ThermaGrid,
 * LiquiGrid, SoilGrid, StateGrid, CountGrid, Energie, Aufgaben, Bestand,
 * Geräte, Daten, Einstellungen), OBEN die Umgebungsachse (Alle und die sechs
 * Umgebungen). Die Ansicht ist der Schnittpunkt. Beide Achsen sind immer
 * sichtbar und immer umschaltbar; ein Wechsel auf einer Achse lässt die
 * andere stehen.
 *
 * Mobil zuerst: unter 768 px liegt die Funktionsachse unten, die
 * Umgebungsachse bleibt oben. Beide bleiben erreichbar, ohne ein Menü zu
 * öffnen — auf dem Handy wird kurz nachgesehen, nicht navigiert (#1).
 */

/* ===================================================================== */
/* Grundlagen                                                            */
/* ===================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--size-base);
  line-height: var(--leading-normal);
  color: var(--text);
  background: var(--surface-sunken);
  /* Auf dem Handy soll unter der Funktionsleiste nichts hängen bleiben. */
  overscroll-behavior-y: none;
}

h1, h2, h3, h4 {
  margin: 0;
  line-height: var(--leading-tight);
  font-weight: var(--weight-bold);
}

p {
  margin: 0;
}

ul, ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

button {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--space-2);
  top: -100px;
  z-index: var(--z-toast);
  padding: var(--space-2) var(--space-4);
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: var(--radius-md);
  color: var(--text);
  text-decoration: none;
}

.skip-link:focus {
  top: var(--space-2);
}

.icon {
  width: 1.25em;
  height: 1.25em;
  flex: none;
  display: inline-block;
  vertical-align: -0.22em;
}

/* ===================================================================== */
/* Rahmen: die Matrix                                                    */
/* ===================================================================== */

/* `minmax(0, 1fr)` statt `1fr`: sonst wächst die Spalte auf die
   Mindestbreite ihres Inhalts, und die waagerecht rollbare Funktionsleiste
   (vierzehn Einträge nebeneinander) schiebt die ganze Seite breiter als das
   Handy. Der Körper darf nie waagerecht rollen — rollen dürfen nur die
   Leisten selbst. */
.app {
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "top"
    "main"
    "side";
}

.topbar,
.funcbar,
main {
  min-width: 0;
}

@media (min-width: 768px) {
  .app {
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas:
      "top  top"
      "side main";
  }
}

/* --- Kopf: Marke, Umgebungsachse, Bedienung -------------------------- */

.topbar {
  grid-area: top;
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
}

.topbar-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  min-height: var(--topbar-height);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-bold);
  font-size: var(--size-md);
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}

.brand img {
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
}

.topbar-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* Umgebungsachse — waagerecht, rollbar, immer sichtbar. */
.env-tabs {
  display: flex;
  gap: var(--space-1);
  padding: 0 var(--space-2) var(--space-2);
  overflow-x: auto;
  scrollbar-width: thin;
}

.env-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding: var(--space-2) var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--size-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.env-tab:hover {
  background: var(--surface-2);
  color: var(--text);
}

.env-tab[aria-current="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

.env-tab .env-tab-emoji {
  font-size: 1.05em;
  line-height: 1;
}

/* Der Zustandspunkt am Reiter trägt zusätzlich eine Form über das Symbol —
   Farbe allein sagt hier nichts. */
.env-tab .dot {
  width: 0.85em;
  height: 0.85em;
  color: var(--state-color, var(--text-faint));
}

/* --- Funktionsachse --------------------------------------------------- */

.funcbar {
  grid-area: side;
  background: var(--surface);
  border-top: 1px solid var(--border);
  position: sticky;
  bottom: 0;
  z-index: var(--z-sticky);
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: env(safe-area-inset-bottom);
}

.funcbar ul {
  display: flex;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
}

.func-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 60px;
  min-height: var(--tap-target);
  padding: var(--space-2) var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-muted);
  font-size: var(--size-xs);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.func-item .icon {
  width: 22px;
  height: 22px;
}

.func-item:hover {
  background: var(--surface-2);
  color: var(--text);
}

.func-item[aria-current="page"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: var(--weight-medium);
}

@media (min-width: 768px) {
  .funcbar {
    position: sticky;
    top: 0;
    bottom: auto;
    align-self: start;
    max-height: 100dvh;
    overflow-y: auto;
    overflow-x: hidden;
    border-top: 0;
    border-right: 1px solid var(--border);
  }

  .funcbar ul {
    flex-direction: column;
    padding: var(--space-2);
  }

  .func-item {
    width: 100%;
  }
}

@media (min-width: 1200px) {
  .app {
    grid-template-columns: var(--sidebar-width-wide) minmax(0, 1fr);
  }

  .func-item {
    flex-direction: row;
    justify-content: flex-start;
    gap: var(--space-3);
    font-size: var(--size-sm);
    text-align: left;
  }
}

/* Trennlinie zwischen Medienachse und dem Rest der Funktionen. */
.func-sep {
  border-top: 1px solid var(--border);
  margin: var(--space-1) var(--space-2);
}

/* --- Inhalt ----------------------------------------------------------- */

main {
  grid-area: main;
  min-width: 0;
  padding: var(--space-4) var(--space-3) var(--space-7);
}

@media (min-width: 768px) {
  main {
    padding: var(--space-5) var(--space-5) var(--space-8);
  }
}

.view {
  max-width: var(--content-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.view-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
}

.view-head h2 {
  font-size: var(--size-xl);
}

.view-head .subline {
  color: var(--text-muted);
  font-size: var(--size-sm);
}

/* ===================================================================== */
/* Bausteine                                                             */
/* ===================================================================== */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-sm);
}

.section-title {
  font-size: var(--size-md);
  margin-bottom: var(--space-3);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font-size: var(--size-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.btn:hover {
  background: var(--surface-2);
  border-color: var(--accent);
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}

.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

.btn-icon {
  min-width: var(--tap-target);
  padding: var(--space-2);
}

.btn-quiet {
  border-color: transparent;
  background: transparent;
  color: var(--text-muted);
}

.btn-quiet:hover {
  background: var(--surface-2);
  color: var(--text);
}

/* Filterreihe (Mediengruppe, Zustand, …) — die zweite Achse in der Ansicht. */
.filterbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--size-sm);
  cursor: pointer;
}

.filter-chip[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: var(--weight-medium);
}

.filter-chip .count {
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

/* --- Zustandsanzeige -------------------------------------------------- */
/* `data-state` setzt die drei Farbtokens; alles andere liest nur
   `--state-color` und Freunde. Zusätzlich trägt jeder Zustand ein eigenes
   Symbol mit eigener Grundform und einen deutschen Text — die Farbe steht
   nie allein (#29). */

[data-state="ok"] {
  --state-color: var(--state-ok);
  --state-bg: var(--state-ok-bg);
  --state-border: var(--state-ok-border);
}

[data-state="warn"] {
  --state-color: var(--state-warn);
  --state-bg: var(--state-warn-bg);
  --state-border: var(--state-warn-border);
}

[data-state="alarm"] {
  --state-color: var(--state-alarm);
  --state-bg: var(--state-alarm-bg);
  --state-border: var(--state-alarm-border);
}

[data-state="stale"] {
  --state-color: var(--state-stale);
  --state-bg: var(--state-stale-bg);
  --state-border: var(--state-stale-border);
}

[data-state="silent"] {
  --state-color: var(--state-silent);
  --state-bg: var(--state-silent-bg);
  --state-border: var(--state-silent-border);
}

[data-state="simulated"] {
  --state-color: var(--state-simulated);
  --state-bg: var(--state-simulated-bg);
  --state-border: var(--state-simulated-border);
}

[data-state="unmonitored"] {
  --state-color: var(--state-unmonitored);
  --state-bg: var(--state-unmonitored-bg);
  --state-border: var(--state-unmonitored-border);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 2px var(--space-2) 2px var(--space-2);
  border: 1px solid var(--state-border, var(--border));
  border-radius: var(--radius-pill);
  background: var(--state-bg, var(--surface-2));
  color: var(--state-color, var(--text));
  font-size: var(--size-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.chip .icon {
  width: 1em;
  height: 1em;
}

/* Simuliert wird schraffiert — nie flächig, nie grün (AGENTS.md Regel 2). */
.chip[data-state="simulated"],
.is-simulated {
  background-image: var(--hatch-simulated);
}

.chip[data-state="unmonitored"],
.is-unmonitored {
  background-image: var(--hatch-unmonitored);
}

/* Stumm sieht anders aus als ruhig (Regel 3): doppelter roter Rand. */
.is-silent {
  border-color: var(--state-silent) !important;
  box-shadow: inset 0 0 0 2px var(--state-silent-bg);
}

/* Der Wert eines stummen Sensors ist durchgestrichen — er steht noch da,
   aber er gilt nicht mehr. */
.value-void {
  text-decoration: line-through;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--state-silent);
}

/* --- Der Satz ganz oben (#35, #1) ------------------------------------- */

.verdict {
  border: 1px solid var(--state-border, var(--border));
  border-left: 6px solid var(--state-color, var(--border-strong));
  border-radius: var(--radius-lg);
  background: var(--state-bg, var(--surface));
  padding: var(--space-5) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  /* Auf dem Handy füllt der Satz den ersten Bildschirm: wer unterwegs kurz
     schaut, ist ohne Scrollen fertig (#35). */
  min-height: min(58dvh, 380px);
  justify-content: center;
}

@media (min-width: 768px) {
  .verdict {
    min-height: 0;
    padding: var(--space-5);
  }
}

.verdict-line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--state-color, var(--text));
}

.verdict-line .icon {
  width: 1em;
  height: 1em;
  flex: none;
}

.verdict-word {
  font-size: var(--size-verdict);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
}

.verdict-reason {
  font-size: var(--size-md);
  color: var(--text);
}

.verdict-detail {
  font-size: var(--size-sm);
  color: var(--text-muted);
}

.verdict-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: var(--size-sm);
  color: var(--text-muted);
}

.verdict-facts b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* --- Kacheln je Umgebung (#35) ---------------------------------------- */

.tiles {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1100px) {
  .tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-top: 4px solid var(--state-color, var(--border-strong));
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  text-align: left;
  cursor: pointer;
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.tile:hover {
  box-shadow: var(--shadow-md);
}

.tile-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}

.tile-title {
  font-size: var(--size-md);
  font-weight: var(--weight-bold);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.tile-head .chip {
  margin-left: auto;
}

.tile-reason {
  font-size: var(--size-sm);
  color: var(--text-muted);
}

/* Überwachungsgrad: wie viele Sensoren wirklich gemessen werden. Der Balken
   trägt zusätzlich die Zahl — ein Balken allein ist keine Angabe. */
.meter {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.meter-track {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  background-image: var(--hatch-simulated);
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  background: var(--state-color, var(--accent));
  border-radius: var(--radius-pill);
}

.leads {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.lead {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  min-width: 0;
}

.lead-name {
  font-size: var(--size-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
  min-width: 0;
}

.lead-value {
  font-size: var(--size-md);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.lead-unit {
  font-size: var(--size-xs);
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

.tile-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  font-size: var(--size-xs);
  color: var(--text-muted);
  margin-top: auto;
}

/* --- Sparkline (schlichtes Inline-SVG; Diagramme kommen mit #37) ------- */

.sparkline {
  width: 72px;
  height: 24px;
  flex: none;
  color: var(--state-color, var(--text-muted));
}

.sparkline-empty {
  font-size: var(--size-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

/* --- Sensorkarten (#54, #71) ------------------------------------------ */

.sensor-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}

@media (min-width: 560px) {
  .sensor-grid {
    grid-template-columns: repeat(auto-fill, minmax(255px, 1fr));
  }
}

.sensor-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-left: 4px solid var(--state-color, var(--border-strong));
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.sensor-card .sensor-name {
  font-size: var(--size-sm);
  font-weight: var(--weight-medium);
  overflow-wrap: anywhere;
}

.sensor-card .sensor-key {
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  color: var(--text-faint);
  overflow-wrap: anywhere;
}

.sensor-value-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.sensor-value {
  font-size: var(--size-xl);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}

.sensor-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.sensor-reason {
  font-size: var(--size-xs);
  color: var(--text-muted);
}

/* Herkunft (#1: jeder Wert sagt, woher er kommt) */
.origin {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 1px var(--space-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--size-xs);
  color: var(--text-muted);
  white-space: nowrap;
}

.origin[data-origin="simulated"] {
  border-style: solid;
  border-color: var(--state-simulated-border);
  color: var(--state-simulated);
  background-image: var(--hatch-simulated);
}

.origin[data-origin="device"] {
  border-style: solid;
}

/* --- Listen und Tabellen ---------------------------------------------- */

.rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.row-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-left: 4px solid var(--state-color, var(--border-strong));
  border-radius: var(--radius-md);
  background: var(--surface);
}

.row-main {
  flex: 1 1 260px;
  min-width: 0;
}

.row-title {
  font-weight: var(--weight-medium);
  overflow-wrap: anywhere;
}

.row-sub {
  font-size: var(--size-xs);
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--size-sm);
}

table.data th,
table.data td {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

table.data th {
  background: var(--surface-2);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  position: sticky;
  top: 0;
}

table.data tbody tr:last-child td {
  border-bottom: 0;
}

table.data td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* --- Lade-, Fehler- und Leerzustände (#30) ---------------------------- */

.skeleton {
  border-radius: var(--radius-md);
  background: linear-gradient(
    90deg,
    var(--surface-2) 25%,
    var(--surface-3) 37%,
    var(--surface-2) 63%
  );
  background-size: 400% 100%;
  animation: shimmer 1.4s ease infinite;
}

@keyframes shimmer {
  0% { background-position: 100% 0; }
  100% { background-position: 0 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton {
    animation: none;
    background: var(--surface-2);
  }
}

.notice {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.notice[data-state] {
  border-color: var(--state-border);
  background: var(--state-bg);
}

.notice-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-bold);
  color: var(--state-color, var(--text));
}

.notice-body {
  font-size: var(--size-sm);
  color: var(--text);
  overflow-wrap: anywhere;
}

.notice-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* --- Fusszeile: das Alter der Anzeige (#5) ---------------------------- */

.freshness {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  font-size: var(--size-xs);
  color: var(--text-muted);
  background: var(--surface-2);
}

.freshness time {
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* --- Verbindungszustand (#30) ----------------------------------------- */

.conn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--state-border, var(--border));
  border-radius: var(--radius-pill);
  background: var(--state-bg, var(--surface-2));
  color: var(--state-color, var(--text-muted));
  font-size: var(--size-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.conn-label {
  display: none;
}

@media (min-width: 900px) {
  .conn-label {
    display: inline;
  }
}

/* Der Streifen, wenn die Verbindung wirklich weg ist. Er verdrängt Inhalt,
   statt daneben zu stehen: alte Zahlen ohne Warnung sind genau der Fehler
   vom Januar. */
.offline-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--state-alarm-bg);
  border-bottom: 2px solid var(--state-alarm);
  color: var(--state-alarm);
  font-size: var(--size-sm);
  font-weight: var(--weight-medium);
}

.offline-banner .btn {
  margin-left: auto;
}

/* ===================================================================== */
/* Befehlspalette (#54)                                                  */
/* ===================================================================== */

/* `display: flex` schlägt die Vorgabe von [hidden] — das muss ausdrücklich
   zurückgenommen werden, sonst steht die Palette dauerhaft offen. */
.palette-backdrop[hidden] {
  display: none;
}

.palette-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-palette);
  background: rgba(8, 12, 16, 0.5);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-4);
}

.palette {
  width: min(680px, 100%);
  margin-top: max(var(--space-4), 6vh);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  max-height: min(70dvh, 620px);
}

.palette-input-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
}

.palette-input {
  flex: 1;
  min-height: var(--tap-target);
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--size-md);
  outline: none;
}

.palette-list {
  overflow-y: auto;
  padding: var(--space-2);
}

.palette-group {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

.palette-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--tap-target);
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  text-align: left;
  cursor: pointer;
}

.palette-item[aria-selected="true"] {
  background: var(--accent-soft);
  color: var(--accent);
}

.palette-item .pi-main {
  flex: 1;
  min-width: 0;
}

.palette-item .pi-title {
  font-size: var(--size-sm);
  font-weight: var(--weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.palette-item .pi-sub {
  font-size: var(--size-xs);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.palette-foot {
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--border);
  font-size: var(--size-xs);
  color: var(--text-faint);
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
}

kbd {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 1px 5px;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

/* Der Aufruf der Palette im Kopf. Auf dem Handy nur das Symbol. */
.palette-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  min-width: var(--tap-target);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: var(--size-sm);
  cursor: pointer;
}

.palette-trigger:hover {
  border-color: var(--accent);
  color: var(--text);
}

.palette-trigger .trigger-text,
.palette-trigger kbd {
  display: none;
}

@media (min-width: 900px) {
  .palette-trigger .trigger-text,
  .palette-trigger kbd {
    display: inline;
  }
}

/* ===================================================================== */
/* Kleinkram                                                             */
/* ===================================================================== */

.muted {
  color: var(--text-muted);
}

.mono {
  font-family: var(--font-mono);
  font-size: 0.95em;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.inline-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.kv {
  display: grid;
  grid-template-columns: minmax(120px, auto) 1fr;
  gap: var(--space-2) var(--space-4);
  font-size: var(--size-sm);
}

.kv dt {
  color: var(--text-muted);
}

.kv dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* ===================================================================== */
/* Diagramme, Zeitleisten, Handeingabe, Detailschublade (#36, #37, #54)   */
/* ===================================================================== */

/* --- Sparkline mit Schwellwertband (#36) ------------------------------ */
/* Die Bänder liegen hinter der Kurve und sind bewusst blass: sie geben der
   Zahl einen Massstab, sie sollen sie nicht überschreien. Der **gute**
   Bereich bekommt keine Fläche — ein grüner Grund hinter einer erzeugten
   Kurve läse sich als „alles gut" (AGENTS.md Regel 2). */

.sensor-series {
  min-height: 38px;
  display: flex;
  align-items: center;
}

.sparkline-band {
  width: 100%;
  height: 38px;
  flex: 1 1 auto;
  overflow: visible;
}

.sparkline-band .band-warn {
  fill: var(--state-warn);
  opacity: 0.13;
}

.sparkline-band .band-alarm {
  fill: var(--state-alarm);
  opacity: 0.15;
}

.sparkline-band .linie {
  fill: none;
  stroke-width: 1.6px;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sparkline-band .linie-gemessen {
  stroke: var(--state-color, var(--accent));
}

/* Erzeugt ist gestrichelt und blasser — an der Kurve selbst zu sehen, nicht
   nur an einer Beschriftung daneben (#16). */
.sparkline-band .linie-simuliert {
  stroke: var(--state-simulated);
  stroke-dasharray: 3 3;
  opacity: 0.85;
}

.sparkline-band .punkt {
  fill: var(--state-color, var(--accent));
  stroke: none;
}

/* --- Zeitleiste für Zustandsgrössen (#36) ----------------------------- */
/* „Man will sehen, wie lange die Klappe offen war." Eine Kurve zwischen 0
   und 1 beantwortet das nicht — hier ist jeder Abschnitt so breit wie seine
   Dauer, und darunter steht die Summe in Klartext. */

.timeline-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
}

.timeline {
  position: relative;
  width: 100%;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  overflow: hidden;
}

.timeline-part {
  position: absolute;
  top: 0;
  display: block;
}

.timeline-part[data-part="an"] {
  background: var(--state-ok);
}

.timeline-part[data-part="aus"] {
  background: var(--surface-3);
}

/* Ein verdichteter Abschnitt, der teils an und teils aus war. Er wird nicht
   auf „an" oder „aus" gerundet: gestreift heisst „dazwischen, und der Anteil
   steht im Hinweistext". */
.timeline-part[data-part="teils"] {
  background: repeating-linear-gradient(
    90deg,
    var(--state-ok) 0 3px,
    var(--surface-3) 3px 6px
  );
}

/* Nichts gemeldet ist keine Lücke im Bild, sondern eine Aussage im Bild
   (AGENTS.md Regel 3). */
.timeline-part[data-part="luecke"] {
  background: var(--state-silent-bg);
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 4px,
    color-mix(in srgb, var(--state-silent) 45%, transparent) 4px 8px
  );
  box-shadow: inset 0 0 0 1px var(--state-silent-border);
}

.timeline-part.is-simulated {
  background-image: var(--hatch-simulated);
}

.timeline-axis,
.timeline-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.timeline-axis {
  justify-content: space-between;
}

.timeline-summary b {
  font-variant-numeric: tabular-nums;
}

.timeline-gap-note {
  color: var(--state-silent);
  font-weight: var(--weight-medium);
}

.timeline-empty {
  padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--state-silent-border);
  border-radius: var(--radius-sm);
  font-size: var(--size-xs);
  color: var(--state-silent);
}

/* --- Das grosse Diagramm (#37) ---------------------------------------- */

.chart-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.chart-canvas {
  width: 100%;
  min-height: 180px;
}

.chart-note {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.chart-thresholds,
.chart-hint {
  font-size: var(--size-xs);
  color: var(--text-muted);
  margin: 0;
}

.chart-hint {
  padding-left: var(--space-3);
  border-left: 2px solid var(--border-strong);
}

/* Das Hinweisfenster von ECharts wird als Knoten eingesetzt, nicht als
   HTML-Zeichenkette — Sensornamen kommen aus der Datenbank. */
.chart-tip {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: var(--size-xs);
}

.chart-tip-time {
  font-weight: var(--weight-medium);
  color: var(--text);
}

.chart-tip-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.chart-tip-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}

.chart-tip-name {
  color: var(--text-muted);
}

.chart-tip-value {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-medium);
}

.chart-tip-empty {
  color: var(--text-muted);
}

/* --- Handeingabe (#33, Abschnitt 5) ----------------------------------- */
/* Unter zehn Sekunden vom Handy aus, sonst wird sie nicht benutzt. Deshalb
   ein Feld, ein Knopf, beide gross genug für einen Daumen. */

.manual-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px dashed var(--border);
  /* Über der Klickfläche der Karte: wer ins Feld tippt, soll nicht die
     Schublade öffnen. */
  position: relative;
  z-index: 1;
}

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

.manual-input {
  flex: 1 1 5rem;
  min-width: 0;
  min-height: var(--tap-target);
  padding: 0 var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-size: var(--size-md);
  font-family: var(--font-sans);
  font-variant-numeric: tabular-nums;
}

.manual-input:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.manual-unit {
  font-size: var(--size-sm);
  color: var(--text-muted);
  white-space: nowrap;
}

.manual-submit {
  min-height: var(--tap-target);
  flex: none;
}

.manual-feedback {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.manual-feedback[data-state] {
  color: var(--state-color, var(--text-muted));
}

.manual-feedback .icon {
  width: 1em;
  height: 1em;
}

.manual-form.is-small .manual-input {
  font-size: var(--size-base);
}

/* --- Sensorkarte: die ganze Fläche führt in die Schublade -------------- */

.sensor-card-rich {
  position: relative;
}

.sensor-card-rich:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
}

.sensor-link {
  color: inherit;
  text-decoration: none;
}

.sensor-link:hover {
  text-decoration: underline;
}

/* Ein Verweis, dessen Fläche die ganze Karte ist — genau einer in der
   Tastaturreihenfolge, und trotzdem überall anklickbar. */
.sensor-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-md);
}

.sensor-link:focus-visible::after {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.sensor-lastseen {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--state-silent);
  font-weight: var(--weight-medium);
}

.sensor-lastseen .icon {
  width: 1em;
  height: 1em;
}

/* --- Umgebungsansicht (#36) ------------------------------------------- */

.envview-head {
  border-left: 4px solid var(--state-color, var(--border-strong));
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.envview-head-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.envview-head-row .section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

.envview-facts {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}

.envview-facts > div {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.fact-label {
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.fact-value {
  font-size: var(--size-md);
  font-weight: var(--weight-medium);
}

.fact-link {
  color: var(--accent);
  text-decoration: none;
  min-height: var(--tap-target);
  display: flex;
  align-items: center;
}

.fact-link:hover {
  text-decoration: underline;
}

.envview-monitoring {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.envview-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.envview-group-head .section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
}

/* Ein Symbol darf in der Abschnittsüberschrift stehen (Schublade, Gruppen der
   Umgebungsansicht) — dann muss die Zeile es auch ausrichten. */
.section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.section-title .count {
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  color: var(--text-muted);
  font-size: var(--size-xs);
  font-weight: var(--weight-regular);
}

/* Der Abstand zwischen zwei Mediengruppen — sonst laufen 37 Karten zu einer
   Fläche zusammen, in der man wieder sucht statt nachschlägt. */
.envview-group {
  scroll-margin-top: calc(var(--topbar-height) + var(--space-6));
  padding-top: var(--space-2);
}

/* --- Detailschublade (#54) -------------------------------------------- */
/* Sie legt sich über die Ansicht und ersetzt sie nicht: `aria-modal` fehlt
   mit Absicht, und ab 900 px bleibt die Ansicht daneben sichtbar und
   bedienbar. Auf schmalen Schirmen deckt sie alles ab — dort bliebe ohnehin
   nichts übrig, was man noch bedienen könnte. */

.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-drawer) - 1);
  background: rgba(8, 12, 16, 0.45);
}

.drawer-scrim[hidden] {
  display: none;
}

.drawer {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.drawer[hidden] {
  display: none;
}

@media (min-width: 900px) {
  .drawer-scrim {
    display: none;
  }

  .drawer {
    inset: 0 0 0 auto;
    width: min(560px, 48vw);
    border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  }

  /* Aufgezogen: dieselbe Schublade, volle Breite. Kein zweiter Bildschirm,
     keine zweite Adresse — nur `&detail_full=1`. */
  .drawer.is-full {
    inset: 0;
    width: 100%;
    border-radius: 0;
  }
}

.drawer-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

.drawer-head-text {
  min-width: 0;
  flex: 1 1 auto;
}

.drawer-title {
  margin: 0;
  font-size: var(--size-lg);
  line-height: var(--leading-tight);
  overflow-wrap: anywhere;
}

.drawer-title:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.drawer-sub {
  margin: 2px 0 0;
  font-size: var(--size-xs);
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.drawer-tools {
  display: flex;
  gap: var(--space-1);
  flex: none;
}

.drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.drawer-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.drawer-lead {
  padding: var(--space-3);
  border: 1px solid var(--state-border, var(--border));
  border-left: 4px solid var(--state-color, var(--border-strong));
  border-radius: var(--radius-md);
  background: var(--state-bg, var(--surface-2));
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.drawer-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.drawer-link {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-left: 3px solid var(--state-color, var(--border));
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  font-size: var(--size-sm);
}

.drawer-link:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}

.drawer-link .icon {
  flex: none;
  color: var(--state-color, var(--text-muted));
}

.drawer-link-value {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Auf schmalen Schirmen liegt die Funktionsachse unten — die Schublade deckt
   sie ab, deshalb bleibt unten Platz für die Sicherheitszone des Geräts. */
@media (max-width: 899px) {
  .drawer-body {
    padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
  }
}

/* ===================================================================== */
/* Wetter (#32)                                                          */
/* ===================================================================== */

/* Oben rechts im Kopf, durchgängig sichtbar: vier der sechs Umgebungen
   hängen am Aussenwetter, und die Vorhersage ist der Teil, den kein Sensor
   liefern kann.

   Zwei Regeln stecken in diesem Block:
   1. Eine überfällige Vorhersage wird **abgeblendet** (`[data-stale]`), nicht
      kommentarlos weitergezeigt. Kommentarlos weitergezeigte alte Zahlen sind
      der Fehler, an dem sieben Monate lang nichts auffiel.
   2. Ein Hinweis hebt sich ab, bleibt aber `warn` und wird nie `alarm`: es
      ist eine Vorhersage, kein Ereignis (#25). */

.weather {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.weather-now {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-md);
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}

.weather-now:hover {
  background: var(--surface-2);
}

.weather-temp {
  font-weight: var(--weight-medium);
  font-size: var(--size-base);
}

.weather-symbol {
  font-size: 1.3rem;
  line-height: 1;
}

.weather-symbol-sm {
  font-size: 1rem;
}

.weather-symbol-lg {
  font-size: 3rem;
}

/* Kein Wettercode geliefert: ein blasser Punkt, kein geratenes Symbol.
   Ein falsches Symbol sähe aus wie eine Aussage (AGENTS.md Regel 6). */
.weather-symbol.is-unknown {
  color: var(--text-faint);
  font-size: 1rem;
}

/* Die Zahl der Hinweise am Jetzt-Wert — damit sie auch dann sichtbar ist,
   wenn der Streifen darunter zugeklappt ist (Handy). */
.weather-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.3rem;
  border: 1px solid var(--state-border);
  border-radius: var(--radius-pill);
  background: var(--state-bg);
  color: var(--state-color);
  font-size: var(--size-xs);
  font-weight: var(--weight-bold);
}

/* Überfällig: sichtbar abgeblendet statt stillschweigend weitergezeigt. */
.weather[data-stale="true"] .weather-now,
.weather[data-stale="true"] .weather-days,
.weather-hero[data-stale="true"] .weather-hero-main,
.weather-hero[data-stale="true"] .kv {
  opacity: 0.55;
}

.weather-toggle .icon {
  transition: transform var(--transition-fast);
}

.weather[data-open="true"] .weather-toggle .icon {
  transform: rotate(90deg);
}

/* Schmaler Schirm: nur Symbol und Temperatur, der Rest klappt auf. */
.weather-strip {
  display: none;
}

.weather[data-open="true"] .weather-strip {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: var(--z-drawer);
  min-width: 16rem;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}

/* Ab grosser Breite steht die Vorhersage fest unter dem Jetzt-Wert. Darunter
   bleibt sie ein Aufklapper: der Kopf ist sonst hoeher als das, was er
   ueberschreibt. */
@media (min-width: 1200px) {
  .weather {
    flex-direction: column;
    align-items: flex-end;
    gap: 0;
  }

  .weather-toggle {
    display: none;
  }

  /* Hinweise **neben** den Tagen, nicht darueber: nebeneinander kostet eine
     Zeile statt drei, und die Warnung steht damit direkt neben der Zahl, auf
     die sie sich bezieht. */
  .weather-strip,
  .weather[data-open="true"] .weather-strip {
    display: grid;
    grid-template-columns: minmax(0, auto) auto;
    grid-template-areas:
      "adv days"
      "meta meta";
    align-items: center;
    justify-items: end;
    position: static;
    min-width: 0;
    max-width: 32rem;
    padding: 0;
    border: 0;
    background: none;
    box-shadow: none;
    gap: var(--space-1) var(--space-3);
  }

  .weather-strip .weather-advisories {
    grid-area: adv;
    flex-direction: column;
    align-items: flex-end;
    min-width: 0;
    max-width: 100%;
  }

  .weather-strip .weather-days {
    grid-area: days;
  }

  .weather-strip .weather-meta {
    grid-area: meta;
  }

  /* Je Hinweis eine Zeile, und jede bleibt eine Zeile: der ganze Text haengt
     am `title`. Ein Kopf, der bei drei Warnungen auf vier Zeilen waechst,
     schiebt den Inhalt vom Schirm. */
  .weather-strip .weather-advisory {
    flex-wrap: nowrap;
    max-width: 100%;
    overflow: hidden;
  }

  /* Gekuerzt wird die Aufzaehlung der Umgebungen, nie die Warnung selbst:
     „Hitze …" waere eine Warnung, die nicht mehr sagt, wovor sie warnt. */
  .weather-strip .weather-advisory-title {
    flex: none;
    white-space: nowrap;
  }

  .weather-strip .weather-advisory-where {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .weather-strip .weather-meta {
    max-width: 100%;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

.weather-days {
  display: flex;
  gap: var(--space-3);
}

.weather-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 2.75rem;
  font-size: var(--size-xs);
  line-height: 1.25;
}

.weather-day-name {
  color: var(--text-muted);
}

.weather-day-temp b {
  font-weight: var(--weight-medium);
}

.weather-day-min {
  margin-left: 0.25em;
  color: var(--text-faint);
}

.weather-day-rain {
  color: var(--state-stale);
}

/* Hinweise: Farbe, Form und Wort — nie Farbe allein (#29). */
.weather-advisories {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.weather-advisories.is-stacked {
  flex-direction: column;
  gap: var(--space-2);
}

.weather-advisory {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border: 1px solid var(--state-border);
  border-radius: var(--radius-pill);
  background: var(--state-bg);
  color: var(--state-color);
  font-size: var(--size-xs);
}

.weather-advisory .icon {
  width: 0.95em;
  height: 0.95em;
}

.weather-advisory-title {
  font-weight: var(--weight-medium);
}

.weather-advisory-where {
  color: var(--text-muted);
}

.weather-advisories.is-stacked .weather-advisory {
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  font-size: var(--size-sm);
}

.weather-advisory-detail {
  flex-basis: 100%;
  margin: 0;
  color: var(--text);
}

/* Herkunft und Abrufzeitpunkt. Steht an jeder Anzeige, nicht nur in der
   grossen: Open-Meteo verlangt als CC-BY-Quelle die Namensnennung, und
   Regel 1 verlangt sie ohnehin. */
.weather-meta {
  margin: 0;
  font-size: var(--size-xs);
  color: var(--text-faint);
}

.weather-meta[data-state] {
  color: var(--state-color);
}

.weather-credit {
  color: inherit;
}

.weather-empty {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--size-xs);
  color: var(--state-color, var(--text-muted));
}

/* Die grosse Ansicht (#/weather) */
.weather-hero {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--state-border, var(--border));
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.weather-hero-main {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.weather-hero-temp {
  font-size: var(--size-2xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
}

.weather-hero-text {
  color: var(--text-muted);
}

.weather-spark {
  color: var(--accent);
}

.weather-spark .sparkline {
  width: 100%;
  height: 90px;
}


/* ==========================================================================
   Artenkarte (#53) — das Terrarienschild, aber es kennt den Raum
   ==========================================================================
   Aufbau und Gestaltungsentscheidungen folgen dem Entwurf; die Farben,
   Abstände und Radien kommen ausnahmslos aus `tokens.css`. Eine zweite
   Palette neben der bestehenden wäre eine zweite Formsprache — und die
   Zustandsfarben müssen hier dieselben sein wie auf jeder Sensorkarte,
   sonst hiesse Gelb auf der Karte etwas anderes als daneben.
   ========================================================================== */

.card-view {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.card-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.card-tools {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.row-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* --- Die Wendebewegung ------------------------------------------------- */
/* Die Karte wird wirklich gewendet, nicht überblendet: eine Karte, die man
   umdreht, ist die Handlung, die das gedruckte Schild kennt. Wer weniger
   Bewegung will, bekommt sie nicht — dann wechselt die Seite ohne Drehung.
   Die abgewandte Seite steht im Dokument, ist aber `inert` (speciescard.js);
   sonst liefe die Tabulatortaste in eine Seite, die niemand sieht. */
.card-flipbox {
  perspective: 2200px;
}

.card-flipper {
  display: grid;
  transform-style: preserve-3d;
  transition: transform 620ms cubic-bezier(0.4, 0.05, 0.2, 1);
}

.card-flipbox.is-back .card-flipper {
  transform: rotateY(180deg);
}

.card-flipper > * {
  grid-area: 1 / 1;
  backface-visibility: hidden;
}

.card-flipper > .card-back {
  transform: rotateY(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .card-flipper {
    transition: none;
  }
}

/* --- Vorderseite -------------------------------------------------------- */
.card-face {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.3fr);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

@media (max-width: 900px) {
  .card-face {
    grid-template-columns: minmax(0, 1fr);
  }
}

.card-plate {
  position: relative;
  min-height: 240px;
  background: var(--surface-3);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.card-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Zeichnungen statt Foto (#87). `contain` statt `cover`: ein Foto darf man
   beschneiden, eine Zeichnung nicht — bei ihr ist der Bildausschnitt Teil der
   Zeichnung, und ein abgeschnittener Schlangenkopf sieht nach Fehler aus. Der
   Grund bleibt zusammen mit dem Rest sichtbar, weil die Herkunftszeile
   darunter ausdrücklich „Zeichnung" sagt. */
.card-drawing {
  object-fit: contain;
  background: var(--surface-sunken);
}

/* Stilvorlagen (#86) stehen noch eine Stufe weiter weg von einer Aufnahme als
   die Zeichnungen aus #87: sie zeigen die Grossgruppe, nicht die Art. Sie
   bekommen deshalb Rand ringsum und füllen die Fläche nicht aus — schon von
   weitem sieht man ein Zeichen und kein Bild. Gesagt wird es trotzdem in
   Worten darunter; das hier ist der Zusatz, nicht der Ersatz. */
.card-template {
  padding: var(--space-4);
}

/* Fotograf, Lizenz und Verweis stehen an jedem fremden Bild — auch im
   Ausdruck. Bilder ohne freie Lizenz kommen gar nicht erst hierher (#53). */
.card-credit {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--space-5) var(--space-3) var(--space-2);
  font-size: var(--size-xs);
  line-height: 1.4;
  color: #ffffff;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.65));
}

.card-plate-empty {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  color: var(--text-muted);
  font-size: var(--size-sm);
}

.card-plate-empty p {
  margin: 0;
}

.card-text {
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 0;
}

/* --- Namensblock -------------------------------------------------------- */
.card-names {
  border-bottom: 2px solid var(--accent);
  padding-bottom: var(--space-3);
}

.card-de {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-regular);
  font-size: var(--size-2xl);
  line-height: var(--leading-tight);
  color: var(--accent);
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.card-sci {
  margin: var(--space-1) 0 0;
  font-family: var(--font-display);
  font-size: var(--size-md);
  color: var(--text-muted);
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.card-name-row {
  margin-top: var(--space-2);
  display: flex;
  gap: var(--space-2) var(--space-4);
  flex-wrap: wrap;
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.card-name-row b {
  color: var(--text);
  font-weight: var(--weight-medium);
}

/* --- Leere Felder: sichtbar leer, nicht unsichtbar leer ----------------- */
.card-empty {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  color: var(--text-faint);
  font-style: italic;
  font-size: var(--size-sm);
}

.card-empty-mark {
  font-style: normal;
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-1);
  white-space: nowrap;
}

/* --- Herkunftsmarken ---------------------------------------------------- */
.card-origin {
  font-size: var(--size-xs);
}

.card-origin[data-origin="inaturalist"] {
  border-style: solid;
  border-color: var(--accent);
  color: var(--accent);
}

.card-origin[data-origin="measured"] {
  border-style: solid;
}

.card-origin[data-origin="empty"] {
  border-style: dashed;
  color: var(--text-faint);
}

.card-prov {
  display: flex;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  flex-wrap: wrap;
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.card-prov-title {
  font-weight: var(--weight-medium);
  color: var(--text);
}

/* --- Giftigkeit --------------------------------------------------------- */
.card-warn {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  border-left: 4px solid var(--state-color, var(--border-strong));
  background: var(--state-bg, var(--surface-2));
  font-size: var(--size-sm);
}

.card-warn p {
  margin: var(--space-1) 0 0;
}

.card-warn-disclaimer,
.card-warn-src {
  font-size: var(--size-xs);
  color: var(--text-muted);
}

/* --- Haltungswerte gegen den Raum -------------------------------------- */
.card-climate {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.card-climate-head {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.card-climate-head em {
  font-style: normal;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-faint);
}

.card-crow {
  display: grid;
  grid-template-columns:
    minmax(0, 1.1fr) minmax(0, 0.9fr) minmax(0, 1.2fr) minmax(0, 1fr);
  gap: var(--space-1) var(--space-3);
  align-items: baseline;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
}

@media (max-width: 720px) {
  .card-crow {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.card-crow:last-of-type {
  border-bottom: 0;
}

.card-crow-head {
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  background: var(--surface-2);
}

.card-crow-label small {
  display: block;
  color: var(--text-faint);
  font-size: var(--size-xs);
}

.card-crow-soll,
.card-crow-ist {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.card-soll-value,
.card-ist-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.card-ist-value {
  font-size: var(--size-md);
  font-weight: var(--weight-bold);
  color: var(--state-color, var(--text));
}

.card-ist-what,
.card-ist-src {
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.card-ist-src {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
}

/* Kein Messwert ist keine Null. Er steht als das da, was er ist. */
.card-ist-none {
  font-style: italic;
  color: var(--text-faint);
}

.card-crow-delta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--size-xs);
  color: var(--state-color, var(--text-muted));
}

.card-note {
  margin: var(--space-2) var(--space-3) var(--space-2);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

/* --- Steckbrief, Anmerkungen, Pflege, Verbreitung ---------------------- */
.card-lower {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-3);
}

.card-box {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  background: var(--surface-2);
  min-width: 0;
}

.card-box-title {
  margin: 0 0 var(--space-2);
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-faint);
}

.card-box .card-note {
  margin-left: 0;
  margin-right: 0;
}

.card-facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-1) var(--space-3);
  font-size: var(--size-sm);
}

.card-facts dt {
  color: var(--text-faint);
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.card-facts dd {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  overflow-wrap: anywhere;
}

.card-duties {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.card-duty {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--state-color, var(--border-strong));
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  font-size: var(--size-sm);
}

.card-duty:hover {
  background: var(--surface-3);
}

.card-duty-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.card-duty-when {
  font-size: var(--size-xs);
  color: var(--text-muted);
  white-space: nowrap;
}

/* --- Rückseite: der Stammbaum ------------------------------------------ */
.card-back {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.card-back-head {
  border-bottom: 2px solid var(--accent);
  padding-bottom: var(--space-2);
}

.card-back-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-regular);
  font-size: var(--size-xl);
  color: var(--accent);
}

.card-back-sub {
  margin: var(--space-1) 0 0;
  font-size: var(--size-xs);
  color: var(--text-faint);
}

.card-tree {
  list-style: none;
  margin: 0;
  padding: 0;
}

.card-rung {
  display: grid;
  grid-template-columns: 112px 18px minmax(0, 1fr);
  align-items: baseline;
  gap: 0 var(--space-3);
  padding: 3px 0;
  position: relative;
}

.card-rung-rank {
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  text-align: right;
}

.card-rung-spine {
  position: relative;
}

.card-rung-spine::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -8px;
  bottom: -8px;
  width: 1px;
  background: var(--border-strong);
  transform: translateX(-0.5px);
}

.card-rung:first-child .card-rung-spine::before {
  top: 50%;
}

.card-rung:last-child .card-rung-spine::before {
  bottom: 50%;
}

.card-rung-spine i {
  position: absolute;
  left: 50%;
  top: 0.55em;
  transform: translate(-50%, -50%);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--text-faint);
}

.card-rung-name i {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--size-base);
}

.card-rung-common {
  margin-left: var(--space-2);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

/* Wo iNaturalist keinen deutschen Namen führt, steht das da — erfunden wird
   keiner (#53). */
.card-rung-missing {
  color: var(--text-faint);
  font-style: italic;
}

/* Zwischenränge werden abgeblendet: iNaturalist führt sie, das Rückgrat
   tragen sie nicht. Welche Stufe welche ist, sagt das Backend (`major`) —
   hier wird es nur dargestellt. */
.card-rung[data-major="0"] {
  opacity: 0.55;
}

.card-rung[data-major="0"] .card-rung-name i {
  font-size: var(--size-sm);
}

.card-rung[data-self="1"] .card-rung-rank {
  color: var(--accent);
  font-weight: var(--weight-bold);
}

.card-rung[data-self="1"] .card-rung-name i {
  font-weight: var(--weight-bold);
  color: var(--accent);
  font-size: var(--size-md);
}

.card-rung[data-self="1"] .card-rung-spine i {
  width: 12px;
  height: 12px;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

.card-synonym {
  border-left: 3px solid var(--accent);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: var(--size-sm);
}

.card-provlist {
  margin: 0;
  padding-left: var(--space-4);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

/* --- Druck: A5 quer, ohne Bedienelemente (#53) ------------------------- */
/* Zwei Seiten, weil das Schild zwei hat: vorne der Steckbrief, hinten der
   Stammbaum. Die Wendebewegung wird dafür aufgehoben — auf Papier liegen
   beide Seiten nebeneinander, nicht hintereinander. */
/* --- Verbreitung: Fundpunkte, keine Fläche (#53) ----------------------- */
/* Der Umriss ist Hintergrund, die Punkte tragen die Aussage. Deshalb liegt
   die Fläche auf `--surface` und nicht auf einer eigenen Farbe: eine kräftig
   eingefärbte Weltkarte sähe aus wie eine Verbreitungsangabe, und genau das
   ist sie nicht. */
.card-dist {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.card-dist-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.card-dist-state {
  font-size: var(--size-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  border: 1px solid currentColor;
  color: var(--text-muted);
}

.card-dist-state[data-state="ok"] { color: var(--state-ok); }
.card-dist-state[data-state="stale"] { color: var(--state-stale); }
.card-dist-state[data-state="silent"] { color: var(--state-silent); }
.card-dist-state[data-state="unmonitored"] { color: var(--state-unmonitored); }

.card-dist-count {
  margin: 0;
  font-size: var(--size-sm);
  color: var(--text);
}

.card-dist-canvas {
  width: 100%;
  /* 2:1 ist das Seitenverhältnis einer gleichabständigen Weltkarte. Fest
     gesetzt, weil ECharts eine Höhe braucht — ein Diagramm in einem Kasten
     ohne Höhe zeichnet nichts und meldet auch nichts. */
  aspect-ratio: 2 / 1;
  min-height: 120px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.card-dist-empty {
  padding: var(--space-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  text-align: center;
}

.card-dist-failed {
  margin: 0;
  padding: var(--space-2);
  font-size: var(--size-xs);
  color: var(--state-silent);
  border: 1px solid currentColor;
  border-radius: var(--radius-sm);
}

.card-dist-match {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  margin: 0;
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.card-dist-match-label { color: var(--text-faint); }

.card-dist-key { font-variant-numeric: tabular-nums; }

/* Ein unscharfer Treffer ist ein Vorschlag (Regel 5). Er wird deshalb
   sichtbar abgesetzt und nicht bloss anders formuliert — was man nur beim
   genauen Lesen bemerkt, bemerkt beim Ausdrucken niemand. */
.card-dist-fuzzy {
  flex-basis: 100%;
  color: var(--state-warn);
}

.card-dist-licenses {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: 0;
  font-size: var(--size-xs);
  color: var(--text-muted);
}

.card-dist-license {
  padding: 0 var(--space-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.card-dist-src { color: var(--text-faint); }

.card-dist-tip { font-size: var(--size-xs); line-height: 1.4; }
.card-dist-tip-head { font-weight: 600; }
.card-dist-tip-faint { color: var(--text-faint); }

@media print {
  @page {
    size: A5 landscape;
    margin: 8mm;
  }

  body {
    background: #ffffff;
  }

  .skip-link,
  .topbar,
  .funcbar,
  #offline-streifen,
  .drawer,
  .drawer-scrim,
  .palette-backdrop,
  .card-head,
  .card-tools,
  .row-actions {
    display: none !important;
  }

  .app {
    display: block;
  }

  main {
    padding: 0;
  }

  .card-view {
    gap: 0;
  }

  .card-flipbox {
    perspective: none;
  }

  .card-flipper {
    display: block;
    transform: none !important;
    transform-style: flat;
  }

  .card-flipper > * {
    backface-visibility: visible;
    transform: none !important;
  }

  .card-face,
  .card-back {
    box-shadow: none;
    border: 1px solid #999999;
    break-inside: avoid;
  }

  .card-back {
    break-before: page;
    margin-top: 0;
  }

  .card-plate {
    min-height: 0;
  }

  /* Die Verbreitungskarte gehört mit aufs gedruckte Schild — sie ist einer
     der Gründe, warum es #53 gibt. Sie wird deshalb **nicht** ausgeblendet,
     sondern flacher gesetzt, damit sie im A5-Querformat neben den übrigen
     Kästen Platz hat. ECharts zeichnet sie als SVG; ein Canvas käme hier als
     Raster heraus. */
  .card-dist-canvas {
    aspect-ratio: 2 / 1;
    max-height: 46mm;
    border-color: #999999;
    break-inside: avoid;
  }

  .card-dist-failed {
    border-color: #999999;
  }
}

/* ===================================================================== */
/* Startseite: Lageplan mit Zeitmaschine (#76, #61, #42)                 */
/* ===================================================================== */

/* Reines SVG, kein WebGL, kein 3D — es muss auf dem Handy laufen (#42).
   Der Plan liegt in einem viewBox von 1000 × 600 und skaliert mit; die
   Schriftgrössen darin sind deshalb an Zeicheneinheiten gebunden und werden
   auf schmalen Geräten hochgesetzt. Bei 375 px wäre eine Beschriftung von
   16 Einheiten sonst rund sechs Pixel hoch — vorhanden und unlesbar. */

.sitemap-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.sitemap-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

.sitemap-head .section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 0;
}

.sitemap-canvas {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--ground);
  overflow: hidden;
}

svg.sitemap {
  display: block;
  width: 100%;
  height: auto;
}

/* --- Umriss, Gebäude, Wege -------------------------------------------- */

.sitemap .building {
  fill: var(--building);
  stroke: var(--building-line);
  stroke-width: 2;
}

.sitemap .building-label,
.sitemap .sitemap-note {
  fill: var(--text-faint);
  letter-spacing: 0.1em;
}

.sitemap .sitemap-note {
  font-size: 11px;
  letter-spacing: 0;
}

.sitemap .tracks path {
  fill: none;
  stroke: var(--border-strong);
  stroke-width: 2;
  stroke-dasharray: 5 6;
  opacity: 0.55;
}

/* --- Die Zonen --------------------------------------------------------- */

.sitemap .zone {
  cursor: pointer;
}

/* Die Beschriftung gehört zum Ziel. Auf dem Handy ist der Umriss des
   Terrariums keine 20 px breit — der Name daneben ist der Griff, an dem man
   ihn trifft (#1: nachgesehen wird unterwegs). */
.sitemap .zone text {
  pointer-events: auto;
}

.sitemap .zone-plot {
  stroke: var(--state-color, var(--border-strong));
  fill: var(--state-bg, var(--surface-2));
  fill-opacity: 0.85;
  transition: fill var(--transition-base), stroke var(--transition-base);
}

.sitemap .zone.is-hatched .zone-plot {
  fill-opacity: 1;
}

/* Das Grundstueck liegt hinter allem und ist so gross, dass seine Flaeche
   sonst jede Aussage der fuenf Zonen darauf uebertoente. Es traegt seine
   Farbe blasser — die Zahl daneben und der Zustandstext bleiben voll. */
.sitemap .zone.is-backdrop .zone-plot {
  fill-opacity: 0.35;
}

/* Stumm hat eine eigene Farbe und einen eigenen Strich — „ein Gerät, das
   melden sollte, ist tot" sieht anders aus als „der Wert ist zu hoch". */
.sitemap .zone.is-silent .zone-plot {
  stroke: var(--state-silent);
  stroke-dasharray: 9 5;
}

.sitemap .zone:hover .zone-plot,
.sitemap .zone:focus-visible .zone-plot {
  stroke-width: 4;
}

.sitemap .zone:focus-visible {
  outline: none;
}

.sitemap .zone:focus-visible .zone-plot {
  stroke: var(--accent);
  stroke-width: 4.5;
}

.sitemap .zone-leader {
  fill: none;
  stroke: var(--text-faint);
  stroke-width: 1;
  opacity: 0.7;
  pointer-events: none;
}

.sitemap .zone-name {
  font-size: 16px;
  font-weight: var(--weight-medium);
  fill: var(--text);
}

.sitemap .zone-sub,
.sitemap .zone-caveat {
  font-size: 11px;
  fill: var(--text-faint);
}

.sitemap .zone-value {
  font-size: 20px;
  font-variant-numeric: tabular-nums;
  fill: var(--text);
}

.sitemap .zone-state {
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  fill: var(--state-color, var(--text-muted));
}

/* Auf dem Handy: der Plan wird **nicht** kleingerechnet, er wird
   geschoben. Bei 343 nutzbaren Pixeln lägen 1000 Zeicheneinheiten bei
   Faktor 0.34 — eine Beschriftung von 16 Einheiten wäre dann fünf Pixel
   hoch, also vorhanden und unlesbar. Stattdessen behält der Plan eine
   Mindestbreite und scrollt in seinem eigenen Kasten; die Seite selbst
   scrollt weiterhin nur senkrecht. Die Beschriftung wird zusätzlich
   angehoben, damit sie auch bei Faktor 0.66 lesbar bleibt. */
@media (max-width: 640px) {
  .sitemap-canvas {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  svg.sitemap {
    width: 660px;
    max-width: none;
  }

  .sitemap .zone-name { font-size: 22px; }
  .sitemap .zone-value { font-size: 26px; }
  .sitemap .zone-state { font-size: 15px; }
  .sitemap .zone-sub,
  .sitemap .zone-caveat { font-size: 14px; }
  .sitemap .building-label,
  .sitemap .sitemap-note { font-size: 13px; }
}

/* --- Legende ----------------------------------------------------------- */

.sitemap-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: var(--size-sm);
  color: var(--text-muted);
}

.sitemap-legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.sitemap-legend-swatch {
  width: 14px;
  height: 14px;
  flex: none;
  border: 1px solid var(--state-border, var(--border));
  border-radius: var(--radius-sm);
  background: var(--state-bg, var(--surface-2));
}

.sitemap-legend-item.is-hatched .sitemap-legend-swatch {
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 3px,
    var(--state-color) 3px 6px
  );
}

.sitemap-hint {
  font-size: var(--size-sm);
}

/* ===================================================================== */
/* Die Zeitmaschine (#42, #76)                                           */
/* ===================================================================== */

.timemachine {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.tm-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

.tm-clock {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-medium);
  min-width: 14ch;
}

.tm-position {
  font-size: var(--size-sm);
  color: var(--text-muted);
}

/* „Rückblick" wird ausdrücklich benannt. Eine Karte, die die Vergangenheit
   zeigt und dabei aussieht wie das Jetzt, wäre genau die stille
   Falschmeldung, um die es in #3 geht. */
.tm-position[data-back="true"] {
  color: var(--state-stale);
  font-weight: var(--weight-medium);
}

.tm-position[data-back="true"]::before {
  content: "Rückblick — ";
}

.tm-scrub {
  position: relative;
  padding-bottom: var(--space-5);
}

/* Der Regler ist das Bedienelement dieser Seite und wird mit dem Daumen
   getroffen: 44 px hoch, mit einem Griff, der breit genug ist (#29). */
.tm-range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: var(--tap-target);
  margin: 0;
  background: transparent;
  cursor: pointer;
}

.tm-range::-webkit-slider-runnable-track {
  height: 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
}

.tm-range::-moz-range-track {
  height: 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
}

.tm-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 28px;
  margin-top: -11px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--accent);
}

.tm-range::-moz-range-thumb {
  width: 18px;
  height: 28px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--accent);
}

.tm-range:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.tm-marks {
  position: relative;
  height: var(--space-5);
}

.tm-mark {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0;
  border: none;
  background: none;
  color: var(--text-faint);
  font-size: var(--size-xs);
  white-space: nowrap;
  cursor: pointer;
}

.tm-mark-tick {
  width: 2px;
  height: 8px;
  background: currentColor;
}

.tm-mark-label {
  max-width: 12ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tm-mark[data-kind="alarm"] { color: var(--state-alarm); }
.tm-mark[data-kind="warn"] { color: var(--state-warn); }
.tm-mark[data-kind="silent"] { color: var(--state-silent); }
.tm-mark[data-kind="task"] { color: var(--state-ok); }

.tm-mark:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* Auf schmalen Geräten stünden fünf Beschriftungen übereinander. Der Strich
   bleibt, das Wort geht — es steht weiterhin im `title` der Marke. */
@media (max-width: 640px) {
  .tm-mark-label { display: none; }
  .tm-marks { height: var(--space-3); }
  .tm-scrub { padding-bottom: var(--space-3); }
}

.tm-note {
  font-size: var(--size-xs);
}

.tm-note[data-state] {
  color: var(--state-color);
}

/* ===================================================================== */
/* Was es noch nicht gibt (#33/#40, #34/#41)                             */
/* ===================================================================== */

/* Benannte, leere Abschnitte. Kein Balken, keine Beispielzahl: was hier
   stünde, sähe aus wie ein Messwert (AGENTS.md Regel 6). */
.home-pending {
  border-style: dashed;
  background: var(--surface);
}

.home-pending .section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--state-color);
}

.home-pending-what {
  margin-bottom: var(--space-2);
}

/* ===================================================================== */
/* Weniger Bewegung (#42)                                                */
/* ===================================================================== */

/* Wer weniger Bewegung will, bekommt keine: die Zonen wechseln ihre Farbe
   ohne Überblendung. Regler und Abspielen bleiben — sie sind der Inhalt
   dieser Ansicht und werden jedes Mal ausdrücklich ausgelöst, nicht von der
   Seite selbst begonnen. */
@media (prefers-reduced-motion: reduce) {
  .sitemap .zone-plot {
    transition: none;
  }
}
