/* Reithalle Hof Buschbur – schlankes, modernes, mobil-first Stylesheet. */
:root {
  --green: #2f6f4e;
  --green-dark: #245740;
  --green-soft: #e7f1ea;
  --bg: #f7f6f2;
  --card: #ffffff;
  --ink: #1f2937;
  --muted: #6b7280;
  --border: #e6e2d8;
  --danger: #b3261e;
  --danger-soft: #fdeceb;
  --warn-soft: #fdf6e3;
  --ok-soft: #e7f1ea;
  --info-soft: #eaf1fb;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(0, 0, 0, .05), 0 6px 20px rgba(0, 0, 0, .04);
  --maxw: 960px;
}

/* WICHTIG – das hidden-Attribut muss immer gewinnen.
   Der Browser bringt dafür nur "[hidden] { display: none }" aus seinem eigenen Stylesheet mit,
   und das verliert gegen JEDE Regel aus dieser Datei, die display setzt: ".abr-zeile { display:
   flex }" oder ".form label { display: grid }" reichen schon. Das Attribut wird dann zwar
   gesetzt, das Element bleibt aber sichtbar – und der Fehler fällt beim Testen leicht durch,
   weil element.hidden im Skript korrekt true meldet.
   Genau das ist in diesem Projekt mehrfach passiert (Burger-Menü, Notiz-Popup, Zeilen der
   Abrechnungsliste, Betragsfelder in den Buchungsformularen) und wurde jedes Mal einzeln
   geflickt. Diese eine Regel erledigt es für alle – auch für die, die noch dazukommen.
   !important ist hier richtig: "hidden" heißt nicht anzeigen, das ist keine Layout-Frage. */
[hidden] { display: none !important; }

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
  line-height: 1.5;
}
a { color: var(--green-dark); }

/* Kopfzeile / Navigation.
   .topbar-wrap statt einer eigenen Klebeposition an .topbar: Im Dev-Betrieb steht der rote
   Umgebungsstreifen (.dev-bar) ÜBER der Kopfzeile, und beide sollen beim Scrollen zusammen oben
   kleben bleiben. Mit zwei einzeln klebenden Geschwistern müsste die Kopfzeile um die exakte
   Streifenhöhe nach unten versetzt werden (ein fest verdrahteter Pixelwert, der bei jeder
   Schrift-/Abstandsänderung des Streifens nachgezogen werden müsste) – als gemeinsamer Klebe-
   Container erledigt der normale Blockfluss das automatisch, ganz ohne Zahlenwert. Auf dem
   Server (kein Streifen) bleibt es bei einem Kind, ändert aber nichts am Verhalten. */
.topbar-wrap { position: sticky; top: 0; z-index: 20; }
.topbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .3rem 1rem; padding: .7rem 1rem;
  background: rgba(255, 255, 255, .92);
  backdrop-filter: saturate(1.4) blur(8px);
  border-bottom: 1px solid var(--border);
}
/* Icons der unteren Leiste liegen als <symbol>-Sprite im Dokument und werden per <use> geholt.
   Das Sprite selbst darf nichts einnehmen – display:none wäre falsch, denn dann fänden die
   <use>-Verweise ihre Vorlage nicht mehr. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Marke + Firmenwechsel als ein Wrapper im Markup, aber bis Desktop-Breite unsichtbar
   (display:contents löst den Wrapper aus dem Layoutbaum): Marke und Umschalter bleiben so eigene
   .topbar-Kinder, genau wie vor dem Wrapper – sonst liefe .firma-wechsels order:10 (eigene Zeile
   unten, siehe @media (max-width: 699px)) ins Leere. Ab 700px wird daraus ein echtes Flex-Element,
   siehe @media (min-width: 700px) weiter unten. */
.topbar-start { display: contents; }

.brand { display: flex; align-items: center; gap: .5rem; text-decoration: none; color: var(--ink); font-weight: 700; }
.brand-mark {
  width: 22px; height: 22px; flex: none;
  fill: none; stroke: var(--green); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.brand-name { white-space: nowrap; }
.topnav, .account-actions { display: flex; align-items: center; gap: .25rem; }
.topnav a, .topnav .linklike, .account-actions a, .account-actions .linklike {
  padding: .45rem .7rem; border-radius: 8px; text-decoration: none;
  color: var(--ink); font: inherit; font-weight: 500;
}
.topnav a.active, .account-actions a.active { background: var(--green-soft); color: var(--green-dark); }
.topnav a:hover, .topnav .linklike:hover, .account-actions a:hover, .account-actions .linklike:hover { background: #f0efe9; }
/* Umgebungskennzeichnung – erscheint nur lokal (istDev in server.js), auf jeder Seite und in
   jeder Ansicht gleich: ein voller Streifen quer über den Bildschirm, ÜBER der Kopfzeile (bzw.
   auf der Anmeldeseite ganz allein, dort gibt es keine Kopfzeile). Bis 2026-09-10 stattdessen
   zwei rote Chips neben den Menüpunkten bzw. in den Ecken – deren Position hing entweder vom
   Menüinhalt ab oder wirkte an der nackten Fensterkante beliebig. Ein Streifen über der ganzen
   Breite braucht dagegen gar keine Position zu wählen: Er IST die oberste Zeile, in jeder Ansicht,
   ohne Sonderfall. Absichtlich laut (satte Farbe, große Schrift) – eine dezente Markierung
   übersieht man genau in dem Moment, in dem es darauf ankommt: wenn man glaubt, lokal zu sein,
   und in Wirklichkeit die echten Daten des Hofs vor sich hat. Auf dem Server erscheint sie gar
   nicht, dort ist die Abwesenheit das Signal. */
.dev-bar {
  display: flex; align-items: center; justify-content: center;
  padding: .3rem .75rem;
  background: var(--danger); color: #fff;
  font-weight: 800; letter-spacing: .12em; font-size: .8rem; text-align: center;
}

/* Sperrzeiten: "Bearbeiten" und "Löschen" nebeneinander unter der Karte. flex-wrap, damit
   auf schmalen Geräten umgebrochen statt überlaufen wird. */
.sperr-aktionen { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }

.inline-form { display: inline; margin: 0; }
.linklike { border: 0; background: none; cursor: pointer; }

/* Burger-Menü: nur auf dem Handy. Dort bleiben in der Leiste ausschließlich "Hallenplan" und
   "Bestellungen"; alles Weitere klappt rechts auf. Auf dem Desktop (ab 700px, siehe unten)
   ist der Burger aus und die Sekundär-Links stehen wie gewohnt direkt in der Leiste. */
.nav-desktop { display: none; }
.burger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: none;
  border: 1px solid var(--border); border-radius: 10px; background: #fff;
  font-size: 1.3rem; line-height: 1; color: var(--ink); cursor: pointer;
}
.burger:hover { background: #f6f5f0; }
.burger[aria-expanded="true"] { background: var(--green-soft); border-color: var(--green); color: var(--green-dark); }
.burger-panel {
  position: absolute; top: calc(100% + .3rem); right: .7rem; z-index: 30;
  display: grid; gap: .15rem; min-width: 190px; padding: .4rem;
  background: #fff; border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow);
}
.burger-panel[hidden] { display: none; }
.burger-panel a, .burger-panel .linklike {
  display: block; width: 100%; text-align: left; padding: .6rem .7rem; border-radius: 8px;
  text-decoration: none; color: var(--ink); font: inherit; font-weight: 500;
}
.burger-panel a:hover, .burger-panel .linklike:hover { background: #f0efe9; }
.burger-panel a.active { background: var(--green-soft); color: var(--green-dark); }
.burger-panel .inline-form { display: block; }

/* Leiste am unteren Rand (nur Handy, siehe Media-Query weiter unten). Unten statt oben, weil der
   Daumen dort hinreicht – oben am Bildschirmrand ist die unbequemste Stelle. Die Bezeichnungen
   sind dieselben wie in der Kopfzeile des Desktops, damit sich niemand umgewöhnen muss.
   position:fixed ist hier richtig (anders als sonst im Projekt): Die Leiste soll beim Scrollen
   stehen bleiben. Der Ausgleich dafür steckt im padding-bottom von .container. */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: .1rem; padding: .3rem .25rem;
  background: rgba(255, 255, 255, .97);
  backdrop-filter: saturate(1.4) blur(8px);
  border-top: 1px solid var(--border);
  /* iPhone: unterhalb der Leiste liegt die Home-Geste. Ohne diesen Zuschlag läge der letzte
     Millimeter der Tippfläche darunter und wäre nicht treffsicher. */
  padding-bottom: calc(.3rem + env(safe-area-inset-bottom, 0px));
}
.tabbar-item {
  display: grid; justify-items: center; align-content: center; gap: .15rem;
  min-height: 52px; padding: .2rem .1rem; border-radius: 10px;
  text-decoration: none; color: var(--muted); font-weight: 600; font-size: .7rem;
  min-width: 0; /* sonst sprengt eine lange Beschriftung ihre Spalte */
  /* Deckel plus Zentrierung: Bei vier Bereichen bleibt jede Spalte darunter und die Leiste füllt
     die Breite; bei nur einem (Mitarbeiter) zöge sich der Eintrag sonst über den ganzen Bildschirm
     und sähe aus wie ein Knopf, nicht wie Navigation. */
  width: 100%; max-width: 9rem; justify-self: center;
}
.tabbar-icon { display: inline-flex; padding: .1rem .75rem; border-radius: 999px; }
.tabbar-icon svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
/* Der aktive Bereich bekommt Farbe UND eine gefüllte Kapsel – Farbe allein ist auf einem hellen
   Display im Stall schnell übersehen. */
.tabbar-item.active { color: var(--green-dark); }
.tabbar-item.active .tabbar-icon { background: var(--green-soft); }
.tabbar-item:active { background: #f1efe8; }
.tabbar-label { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Layout */
.container { max-width: var(--maxw); margin: 0 auto; padding: 1.25rem 1rem 3rem; }
/* Die Leiste liegt über dem Inhalt (fixed) – ohne diesen Ausgleich verschwände das letzte Element
   der Seite dahinter. Der Geschwister-Selektor greift nur, wenn die Leiste überhaupt da ist:
   Konten ohne zweiten Bereich bekommen keine und brauchen den Platz deshalb nicht. */
.tabbar ~ .container { padding-bottom: calc(5rem + env(safe-area-inset-bottom, 0px)); }
.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 1.5rem; margin-bottom: 1.25rem;
}
.card.narrow { max-width: 460px; margin-left: auto; margin-right: auto; }
.card.center { text-align: center; }
h1 { font-size: 1.5rem; margin: 0 0 .5rem; }
/* Anzahl neben einer Überschrift (z. B. registrierte Nutzer bei "Nutzer & Rollen"). */
.count-badge {
  display: inline-block; vertical-align: .12em; margin-left: .35rem;
  padding: .1rem .55rem; border-radius: 999px;
  background: var(--green-soft); color: var(--green-dark);
  font-size: .95rem; font-weight: 700;
}
.lead { font-size: 1.05rem; color: #374151; }
.muted { color: var(--muted); }
.small { font-size: .9rem; }
.opt { color: var(--muted); font-weight: 400; font-size: .85em; }

/* Eigene Rollen im Profil: Kürzel plus Klartext, damit "BST" o. Ä. verständlich ist. */
.myroles {
  display: grid; gap: .4rem; margin: 0 0 1.25rem;
  padding: .75rem .9rem; background: #f7f6f2; border: 1px solid var(--border); border-radius: 10px;
}
.myroles-label { font-weight: 600; font-size: .95rem; }
.myroles-list { display: flex; flex-wrap: wrap; gap: .4rem 1rem; align-items: center; }
.myroles-name { margin-left: -.2rem; font-size: .92rem; }
/* Zweiter Abschnitt im selben Kasten (Bestellgruppe): trennt sichtbar von den Rollen darüber. */
.myroles-sep { margin-top: .5rem; padding-top: .6rem; border-top: 1px solid var(--border); }

/* Login-Intro (Icon + Titel über dem Anmelden-Block) */
.login-intro { text-align: center; padding: 2rem 1rem 0; }
/* Nur die Breite ist gesetzt, die Höhe folgt dem Seitenverhältnis (437x371). Eine feste Höhe
   wie beim früheren quadratischen Icon ließe oben und unten Leerraum stehen.
   Größer als das alte Icon, weil das Logo jetzt den Schriftzug trägt: "BUSCHBUR" muss lesbar
   sein, nicht nur erkennbar. */
.login-intro-icon { width: 200px; height: auto; margin: 0 auto 1.2rem; display: block; }

/* Hero (Startseite) */
.hero { text-align: center; padding: 2.5rem 1rem; }
.hero h1 { font-size: 2rem; }
.hero .lead { max-width: 40ch; margin: .5rem auto 1.5rem; }
.hero-actions { display: flex; gap: .75rem; justify-content: center; flex-wrap: wrap; }

/* Buttons */
.btn {
  display: inline-block; padding: .6rem 1.1rem; border-radius: 10px;
  font-weight: 600; text-decoration: none; cursor: pointer;
  border: 1px solid transparent; font-size: 1rem;
}
.btn-primary { background: var(--green); color: #fff; }
.btn-primary:hover { background: var(--green-dark); }
.btn-ghost { background: #fff; border-color: var(--border); color: var(--ink); }
.btn-ghost:hover { background: #f6f5f0; }
.btn-danger { background: var(--danger-soft); color: var(--danger); border-color: #f3c9c6; }
.btn-warn { background: var(--warn-soft); color: #92600b; border-color: #ecdca6; }
.btn-warn:hover { background: #f7ecc9; }
.btn-sm { padding: .35rem .7rem; font-size: .9rem; border-radius: 8px; }

/* Formulare */
.form { display: grid; gap: 1rem; margin-top: 1rem; }
/* align-content: start – ohne das verteilen sich Beschriftung, Feld und Hinweis über die ganze
   Zellenhöhe. In einem .grid2 wird die Zeile von der höheren Spalte aufgezogen, und in der
   niedrigeren rutscht das Eingabefeld in die Mitte, weit weg von seiner Beschriftung. */
.form label { display: grid; gap: .35rem; align-content: start; font-weight: 600; font-size: .95rem; }
/* Der Zusatz „(optional)", „(Tage)" gehört NEBEN die Beschriftung, nicht darunter.
   Ohne diese Regel macht die Zeile darüber ihn zu einer eigenen Grid-Zeile (der bekannte
   Fallstrick: display:grid macht jedes Kind zu einer eigenen Zeile). Die Beschriftung wird damit
   zweizeilig, und in einem .grid2 steht das Eingabefeld daneben 25 px tiefer als sein Nachbar –
   nachgemessen auf /admin/rechnungseinstellungen, wo es dreimal auftrat (Zahlungsziel, Telefon,
   BIC).
   Lösung ohne Markup-Änderung an rund 50 Stellen: Für Beschriftungen MIT Zusatz bekommt das
   Raster eine zweite Spalte, der Zusatz wird ausdrücklich in Zeile 1 / Spalte 2 gesetzt, und
   alles andere (Feld, Hinweis, Fehler) spannt weiterhin über beide Spalten. Der Beschriftungstext
   selbst ist ein anonymes Grid-Element und lässt sich nicht ansprechen – er landet von allein in
   Zeile 1 / Spalte 1, weil der Zusatz die Nachbarzelle schon belegt.
   :has() greift nur bei Beschriftungen, die wirklich einen Zusatz tragen; alle anderen bleiben
   einspaltig wie bisher. */
.form label:has(> .opt) { grid-template-columns: max-content 1fr; }
.form label:has(> .opt) > *:not(.opt) { grid-column: 1 / -1; }
.form label > .opt { grid-column: 2; grid-row: 1; justify-self: start; align-self: baseline; }
/* Einzelnes schmales Feld außerhalb eines .grid2: Ein Prozent- oder Betragsfeld über die volle
   Seitenbreite sieht aus, als erwarte es einen langen Text. */
.form label.feld-schmal { max-width: 20rem; }
.form input, .form select, .form textarea {
  font: inherit; padding: .6rem .7rem; border: 1px solid var(--border);
  border-radius: 10px; background: #fff; color: var(--ink); width: 100%;
  /* Mindestens 16px: darunter zoomt Safari auf dem iPhone beim Antippen des Feldes hinein.
     "font: inherit" würde sonst die .95rem der Beschriftung übernehmen. */
  font-size: 1rem;
}
.form input:focus, .form select:focus, .form textarea:focus {
  outline: 2px solid var(--green-soft); border-color: var(--green);
}
/* Datumsfelder sind seit der Umstellung auf freie Eingabe normale Textfelder ([data-datum],
   Punkte setzt app.js). Damit entfällt die frühere Höhenkorrektur für den nativen Datums-Picker:
   sie sind jetzt von sich aus so hoch wie jedes andere Feld. Nur die Breite wird begrenzt –
   ein Datum braucht keine volle Zeilenbreite. */
.form input[data-datum] { max-width: 11rem; }
/* Noch nicht freigeschaltete Felder (z. B. Buchungsformular: erst Tag/Startzeit/Dauer).
   Bewusst nicht nur opacity, sonst wirkt der Rahmen weiter „aktiv“. */
.form input:disabled, .form select:disabled, .form textarea:disabled {
  background: var(--bg); color: var(--muted); border-color: var(--border); cursor: not-allowed;
}
.form label.is-locked { color: var(--muted); }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.err { color: var(--danger); font-weight: 500; font-size: .85rem; }

/* Flash-Nachrichten */
.flash { padding: .75rem 1rem; border-radius: 10px; margin-bottom: 1rem; border: 1px solid var(--border); }
.flash-success { background: var(--ok-soft); border-color: #bcd8c7; }
.flash-error { background: var(--danger-soft); border-color: #f3c9c6; color: var(--danger); }
.flash-info { background: var(--info-soft); border-color: #c7d8f2; }
.flash-warn { background: var(--warn-soft); border-color: #ecdca6; }

/* Statistik-Kacheln (Admin) */
.stat-row { display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0; }
.stat {
  flex: 1 1 140px; display: grid; gap: .2rem; text-decoration: none;
  padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: #fff; color: var(--ink);
}
.stat:hover { background: #f9f8f4; }
.stat-num { font-size: 1.8rem; font-weight: 700; color: var(--green-dark); }
.stat-label { color: var(--muted); font-size: .9rem; }

/* Fußzeile */
.footer {
  max-width: var(--maxw); margin: 0 auto; padding: 1.5rem 1rem 3rem;
  color: var(--muted); font-size: .9rem; display: flex; gap: 1rem; justify-content: space-between;
}

/* Allgemeine Kleinteile */
.card-title { font-size: 1.15rem; margin: 0 0 .75rem; }
.mt { margin-top: 1.5rem; }
.crumb { margin-bottom: .5rem; }
.crumb a { text-decoration: none; color: var(--muted); }
.form-actions { display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }
.dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; flex: none; }

/* Meine Buchungen */
.booking-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.booking-item {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .6rem .75rem; border: 1px solid var(--border); border-radius: 10px; background: #fff;
}
.booking-when { font-weight: 600; }
.booking-act { color: var(--muted); }
.booking-item .inline-form, .booking-item .units { margin-left: auto; }
/* Vergangene Buchungen im Profil: zurückgenommen, damit die kommenden Termine hervorstechen. */
.booking-item.is-past { opacity: .65; }
.booking-item .badge { margin-left: auto; }

/* Tages-Navigation */
/* Grid statt flex: der mittlere Tagesblock hat eine feste Breite, damit die Pfeile bei
   unterschiedlich langen Wochentagsnamen (z. B. "Mo" vs. "Donnerstag") nicht hin- und herspringen. */
.daynav { display: grid; grid-template-columns: auto 200px auto; align-items: center; justify-content: center; gap: .9rem; }
.daynav-center { text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.daynav-date { font-size: 1.25rem; font-weight: 700; }
.daynav-sub { color: var(--muted); font-size: .9rem; }
.daynav-arrow { font-size: 1.5rem; line-height: 1; padding: .35rem 1rem; font-weight: 700; }
.daynav-tools { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin: .9rem 0 .3rem; }

/* Kopfzeile des Plans: Zeitraum-Navigation und Ansichts-Umschalter in einer Zeile.
   Auf schmalen Geräten reicht der Platz für beides nicht – dort bricht der Umschalter
   in eine zweite Zeile um (flex-wrap) statt zu überlaufen. */
.plan-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem 1rem; flex-wrap: wrap; }
.plan-head .plan-switch { margin-bottom: 0; }

/* Umschalter Tag/Woche/Monat */
.plan-switch { display: inline-flex; gap: .2rem; padding: .2rem;
  background: #f0efe9; border-radius: 999px; }
.plan-switch-btn {
  padding: .45rem 1.1rem; border-radius: 999px; text-decoration: none;
  color: var(--ink); font-weight: 600; font-size: .95rem; min-height: 40px;
  display: inline-flex; align-items: center;
}
.plan-switch-btn:hover { background: #e6e4dc; }
.plan-switch-btn.active { background: #fff; color: var(--green-dark); box-shadow: 0 1px 2px rgba(0,0,0,.08); }

/* Wochenübersicht: mobil eine Liste, ab 700px sieben Spalten nebeneinander. */
.weekgrid { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .5rem; }
.weekday { border: 2px solid var(--border); border-radius: 10px; background: #fff; }
.weekday > a {
  display: grid; gap: .2rem; padding: .7rem .8rem; text-decoration: none; color: var(--ink);
}
.weekday-top { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.weekday-name { font-weight: 700; }
.weekday-today {
  font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  background: var(--green); color: #fff; padding: .1rem .45rem; border-radius: 999px;
}
.weekday-status { font-weight: 600; font-size: .92rem; }
.weekday-info { color: var(--muted); font-size: .85rem; }
/* Grund einer Ganztagessperre (siehe blockedFullDayReason). Eigene Farbe statt .weekday-info,
   damit sie sich von der neutralen "X/Y Zeiten frei"-Zeile abhebt, die sie ersetzt. */
.weekday-sperr { color: #9d2f27; font-weight: 600; }
.weekday.is-past { opacity: .55; }
/* Kräftiges Grün, nicht --green-soft: seit "frei" selbst grün hinterlegt ist, wäre der Ring
   sonst auf einem freien Tag unsichtbar. Gleiche Lösung wie bei .monthday.is-today. */
.weekday.is-today { box-shadow: 0 0 0 2px var(--green); }
.weekday:hover { border-color: var(--green); }

/* Gemeinsame Ampel von Hallenplan und Arbeitsplan:
     grün   = alles in Ordnung (Halle frei      / alle Dienste besetzt)
     orange = hinsehen         (teilweise belegt / einzelne Lücken)
     rot    = Problem          (ausgebucht      / niemand eingeteilt)
   Zwei Namensschemata bewusst nebeneinander: dieselbe Farbe bedeutet in den beiden Plänen etwas
   Verschiedenes, und "voll" hieße dort ausgebucht, hier unbesetzt. Die Namen sagen jeweils, was
   gemeint ist – die Farben stehen dafür nur einmal hier. */
.status-frei,      .wp-besetzt   { background: var(--green-soft); border-color: var(--green); }
.status-teilweise, .wp-teilweise { background: #fde6b8; border-color: #e2a63a; }
.status-voll,      .wp-unbesetzt { background: #f7d6d3; border-color: #c9524a; }
.status-teilweise .weekday-status { color: #8a5a06; }
.status-voll .weekday-status { color: #9d2f27; }

.weekday-date { color: var(--muted); font-size: .85rem; font-variant-numeric: tabular-nums; }

/* Monatsübersicht: immer 7 Spalten (Mo–So), auch auf dem Handy – ein Monat ergibt nur als
   Raster Sinn. Die Kacheln sind daher bewusst knapp gehalten. */
.monthgrid { display: grid; grid-template-columns: repeat(7, 1fr); gap: .2rem; margin-top: 1rem; }
.monthhead { text-align: center; font-size: .75rem; font-weight: 700; color: var(--muted); padding: .2rem 0; }
.monthday {
  position: relative; display: flex; align-items: center; justify-content: center;
  min-height: 46px; padding: .2rem; border: 2px solid var(--border); border-radius: 8px;
  text-decoration: none; color: var(--ink); font-variant-numeric: tabular-nums;
}
.monthday:hover { border-color: var(--green); }
.monthday-num { font-weight: 600; font-size: .95rem; }
.monthday-count {
  position: absolute; top: 2px; right: 3px;
  font-size: .7rem; font-weight: 700; line-height: 1;
  color: var(--muted);
}
/* Ganztagessperre in der Monatskachel: Schloss statt Buchungszahl (siehe calendar-month.ejs) –
   der Grund selbst steht im title-Tooltip, für Fließtext ist die Kachel zu klein. */
.monthday-lock { font-size: .8rem; }
.monthday.is-outside { opacity: .35; }
.monthday.is-past { opacity: .55; }
.monthday.is-outside.is-past { opacity: .3; }
.monthday.is-today { box-shadow: 0 0 0 2px var(--green); font-weight: 800; }

.weeklegend { display: flex; flex-wrap: wrap; gap: .4rem 1.1rem; margin-top: 1rem; font-size: .88rem; color: var(--muted); }
.weeklegend-dot {
  display: inline-block; width: 14px; height: 14px; border-radius: 4px;
  border: 2px solid var(--border); vertical-align: -2px; margin-right: .25rem;
}

/* Slot-Liste */
.slotlist { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .4rem; }
.slot {
  display: grid; grid-template-columns: 56px 1fr auto; align-items: center; gap: .75rem;
  padding: .55rem .7rem; border: 1px solid var(--border); border-radius: 10px; background: #fff;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.slot.is-past { opacity: .5; }
.slot.is-exclusive { background: #fbf7f0; }
.slot:hover { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
.slot.is-past:hover { border-color: var(--border); box-shadow: none; }
.slot-time { font-variant-numeric: tabular-nums; font-weight: 700; color: #374151; }
/* Endzeit eines zusammengefassten Zeitraums: unter der Startzeit, damit die schmale
   Zeitspalte (56px, mobil 48px) nicht breiter werden muss. */
.slot-time-bis { display: block; font-weight: 700; font-size: .8rem; color: #374151; }
/* Halbe Stunde, die innerhalb derselben Stunde vom Segment darüber abweicht: eingerückt und
   kleiner, damit die volle Stunde die führende Zeile bleibt. */
.slot-sub {
  margin-left: 1.25rem; background: #fbfaf7; border-style: dashed;
  padding-top: .4rem; padding-bottom: .4rem;
}
.slot-sub .slot-time { font-weight: 500; font-size: .9rem; color: var(--muted); }
.slot-sub .chip { font-size: .8rem; }
/* Buchung, die aus der Zeile darüber weiterläuft: nur als Kontext, nicht als Neuigkeit. */
.chip-cont { background: #fafaf8; border-style: dashed; }
.chip-cont .chip-name { font-weight: 400; color: var(--muted); }
.chip-cont .chip-horse { color: var(--muted); font-weight: 400; }
/* Mehrere Buchungen im selben Zeitfenster stehen untereinander (nicht nebeneinander):
   bei bis zu 4 Reitern gleichzeitig bleibt so auf jeder Breite ablesbar, wer gebucht hat. */
.slot-body { display: flex; flex-direction: column; align-items: flex-start; gap: .3rem; min-height: 1.5rem; }
.slot-empty {
  display: inline-block; background: var(--green-soft); color: var(--green-dark);
  font-size: .8rem; font-weight: 700; padding: .15rem .5rem; border-radius: 999px;
}
/* Vergangener Zeitraum: kein grüner Badge – "vorbei" ist keine buchbare Einladung.
   Größe/Gewicht wie die Endzeit (.slot-time-bis) davor, damit beide als Einheit wirken. */
.slot-empty-past { background: none; color: #374151; font-weight: 700; font-size: .8rem; padding: 0; }
.slot-side { display: flex; align-items: center; gap: .5rem; }

.chip {
  display: inline-flex; align-items: center; gap: .4rem; max-width: 100%;
  padding: .2rem .55rem; border: 1px solid var(--border); border-radius: 999px; background: #fff; font-size: .85rem;
}
.chip-exclusive { background: var(--warn-soft); border-color: #ecdca6; }
/* Pflichtpause im Buchungsformular (Migration 037). Als <fieldset> abgesetzt, weil die drei
   Angaben darin zusammengehören und eine eigene Regel beschreiben – anders als die übrigen
   Felder, die schlicht die Buchung ausfüllen. Warnfarbe, nicht Grün: Es ist eine Auflage, keine
   Wahlmöglichkeit. */
.pause-block {
  margin: .2rem 0 .6rem; padding: .6rem .8rem .7rem;
  border: 1px solid #e6d9b8; border-radius: 10px; background: var(--warn-soft);
}
.pause-block legend { padding: 0 .35rem; font-size: .82rem; font-weight: 700; color: #92600b; }
.pause-block .grid2 { margin-top: .5rem; }
.pause-folge { margin: .5rem 0 0; }
/* Die Ausnahme sitzt am Fuß des Blocks, abgesetzt durch eine Linie: Sie hebt die Regel darüber
   auf und soll nicht wie eine weitere Einstellung derselben wirken. */
.pause-ausnahme { margin: .6rem 0 0; padding-top: .55rem; border-top: 1px solid #e6d9b8; }
/* Pausenfenster in der Aktivitätenpflege: zwei Zeitfelder mit "bis" dazwischen, damit sie als
   EIN Wert lesbar sind statt als zwei unabhängige Angaben. */
.pause-fenster { display: flex; align-items: center; gap: .4rem; }
.pause-fenster input { width: 5.5rem; }
.list-input-zeit { width: 4.2rem; text-align: center; }

/* Rechnungsübersicht (Block "Abrechnungen" und /abrechnung/rechnungen). Eine Zeile je Rechnung,
   als Ganzes anklickbar – deshalb hier der Schatten beim Überfahren, den die nicht klickbaren
   Listen im Projekt bewusst NICHT haben. */
.re-liste { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .4rem; }
.re-zeile {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .55rem .75rem; border: 1px solid var(--border); border-radius: 10px;
  background: #fff;
  transition: border-color .12s ease, box-shadow .12s ease;
}
/* Der Textteil ist der Link auf die Positionen und nimmt die freie Breite ein; der Bezahlt-Knopf
   daneben (nur in der Rechnungsliste) behält seine eigene Trefferfläche. Gleicher Aufbau wie
   .rg-offen – ein Link um einen Knopf herum wäre ungültiges Markup, und wer nur nachsehen will,
   soll nicht versehentlich abhaken. */
.re-info {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  text-decoration: none; color: inherit;
}
/* Der Rahmen gehört zur ganzen Zeile, angesprochen wird er aber vom Link darin – daher :has().
   Der Knopf daneben löst ihn bewusst NICHT aus: Er tut etwas anderes, und ein gemeinsames
   Aufleuchten ließe die Zeile wie eine einzige Fläche wirken. */
.re-zeile:has(.re-info:hover),
.re-zeile:has(.re-info:focus-visible) { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
.re-nummer { font-weight: 700; font-variant-numeric: tabular-nums; }
.re-person { font-weight: 600; }
.re-meta { color: var(--muted); font-size: .85rem; }
/* Der Betrag steht als LETZTES in der Zeile und rückt ans Ende – dadurch stehen die Zahlen
   untereinander bündig, unabhängig davon, wie lang Name und Kennzeichen davor ausfallen. */
.re-betrag { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Der offene Stand über dem Rechnungsblock der Einstiegsseite: die einzige Zahl dort, zu der es
   etwas zu tun gibt, deshalb als Weg in die gefilterte Liste. Warnfarben wie .badge-pending –
   es ist derselbe Zustand ("abgerechnet, aber noch kein Geld") wie an den Zeilen darunter. */
.re-offenstand { margin: .4rem 0 .2rem; }
.re-offenstand a {
  display: inline-block; padding: .35rem .6rem; border-radius: 8px;
  background: var(--warn-soft); color: #92600b; text-decoration: none;
}
.re-offenstand a:hover, .re-offenstand a:focus-visible { text-decoration: underline; }

/* Positionen einer Rechnung (Detailansicht). Grundaufbau wie .abr-zeile, damit eine Position
   überall gleich aussieht; .abr-abgerechnet färbt die offenen gelb. */
.re-pos { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .4rem; }
.re-pos-zeile {
  display: flex; gap: .75rem; align-items: flex-start; justify-content: space-between;
  padding: .7rem .85rem; border: 1px solid var(--border); border-radius: 10px; background: #fff;
}
/* Bezahlte Positionen treten zurück – sie sind erledigt, brauchen aber ihren Platz in der
   Rechnung. Gedämpfter Grund statt Transparenz (die drückte den Textkontrast). */
.re-pos-bezahlt { background: #fafaf8; }
.re-pos-haupt { min-width: 0; display: grid; gap: .15rem; }
.re-pos-kopf { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.re-pos-seite { display: flex; flex-direction: column; align-items: flex-end; gap: .4rem; flex: none; }

/* Aufgaben-Zeile (partials/aufgabenzeile.ejs): ZUSÄTZLICH zu .re-pos-zeile, nicht als Ersatz –
   sonst sähen auch die Rechnungspositionen (abrechnung-rechnung-detail.ejs, dieselbe Basisklasse)
   plötzlich anklickbar aus, obwohl sie es nicht sind. Der Schatten beim Überfahren ist im Projekt
   das Zeichen für "hier kannst du klicken" (siehe die Begründung bei .act-card oben) – das gilt
   hier bewusst nur für Aufgaben.
   Die ganze Zeile führt zur Aufgabe: .aufgabe-zeile-link bekommt ein ::after auf voller Fläche.
   Der "Erledigt"-Knopf bekommt einen eigenen Stapelkontext (position + z-index) und liegt DAMIT
   über der Überlagerung – ohne das würde ein Klick auf den Knopf stattdessen die Aufgabe öffnen,
   weil absolut positionierte Elemente unabhängig von ihrer Reihenfolge im Markup vor normal
   positionierten liegen. */
.aufgabe-zeile { position: relative; transition: border-color .12s ease, box-shadow .12s ease; }
.aufgabe-zeile:hover, .aufgabe-zeile:focus-within {
  border-color: var(--green); box-shadow: 0 0 0 1px var(--green);
}
.aufgabe-zeile-link::after { content: ''; position: absolute; inset: 0; }
.aufgabe-offen-icon { margin-right: .3rem; }
.aufgabe-zeile .re-pos-seite { position: relative; z-index: 1; }

.re-raus { color: var(--danger); }
.re-raus:hover, .re-raus:focus-visible { background: var(--danger-soft); }

/* Abrechnungslauf: "Rechnung erfassen" (abrechnung-rechnung.ejs). */
.rg-kundenliste { display: grid; gap: .4rem; margin-top: .6rem; }
.rg-kunde {
  /* flex-wrap, seit in der Info-Zeile die Bereiche stehen: Ohne sie quetscht der Flex-Container
     den Namen, bis er mitten im Wort umbricht ("Andreas / Matthies"). Mit Umbruch rutscht
     stattdessen die Info unter den Namen – beide bleiben ganz. */
  display: flex; justify-content: space-between; align-items: center; gap: .35rem .75rem;
  flex-wrap: wrap;
  padding: .6rem .8rem; border: 1px solid var(--border); border-radius: 10px;
  background: #fff; text-decoration: none; color: inherit;
  transition: border-color .12s ease, box-shadow .12s ease;
}
/* Gleiche Stärke wie .re-zeile und .rg-offen – die Empfängerkacheln stehen auf derselben Seite
   direkt darüber, zwei verschiedene Rahmenstärken sähen nach zwei verschiedenen Arten von Ziel
   aus. */
.rg-kunde:hover { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
.rg-kunde-name { font-weight: 600; }
/* Die Info-Zeile darf umbrechen, ihre Bestandteile nicht: Seit dort die Bereiche stehen
   ("Fremdreiter 2 · Kurse 1 · Bestellungen 1 · 425,40 €") wird sie bei drei Bereichen so breit,
   dass ein vierstelliger Betrag auf 360 px überliefe. Ein nowrap auf dem Ganzen hätte den
   Überlauf erzwungen, ein Umbruch überall hätte "425,40" von "€" getrennt. */
.rg-kunde-info { color: var(--muted); font-size: .9rem; }
.rg-kunde-info > span { white-space: nowrap; }
.rg-posten { list-style: none; margin: .6rem 0; padding: 0; display: grid; gap: .3rem; }
.rg-post {
  display: flex; align-items: center; gap: .6rem;
  padding: .45rem .6rem; border: 1px solid var(--border); border-radius: 10px; background: #fff;
  cursor: pointer;
}
.rg-post input { flex: none; }
.rg-post-datum { flex: none; color: var(--muted); font-size: .88rem; }
.rg-post-text { flex: 1 1 auto; min-width: 0; }
.rg-post-betrag { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; }
.rg-summe { margin: .4rem 0 .8rem; }
/* Letzter Schritt in der Übersicht: Nummer eintragen, nachdem die Rechnung geschrieben ist.
   Abgesetzt und grün hinterlegt, weil er als einziger Block auf dieser Seite etwas festhält –
   alles darüber ist Anzeige. */
.rg-abschluss {
  margin-top: 1.5rem; padding: .9rem 1rem;
  background: var(--green-soft); border-radius: 12px;
}
.rg-abschluss .card-title { margin-top: 0; }
/* Hier standen bis 2026-09-07 .rg-offene/.rg-offen: die zweite Rechnungsliste unten auf
   "Rechnung erfassen". Sie ist in die eine Rechnungsliste (/abrechnung/rechnungen, .re-zeile)
   aufgegangen; das Muster "Link nimmt die freie Breite, Knopf behält seine eigene Fläche,
   Rahmen über :has()" lebt dort weiter. */

/* Rechnungsnummer an einem Posten der Abrechnungsliste – gleiche Bauart wie .abr-abrdatum. */
.abr-renr { font-size: .82rem; color: var(--muted); margin-top: .15rem; }
.abr-renr span { font-weight: 600; color: var(--ink); }

/* Modul Freigaben: Termin beim Bestätigen anpassen. Auf einem hellen Grund abgesetzt, damit die
   Eingabefelder erkennbar zum Freigeben gehören und nicht wie Angaben zur Buchung wirken. */
.frg-form {
  margin: .6rem 0 .5rem; padding: .6rem .7rem;
  background: var(--green-soft); border-radius: 10px;
}
.frg-zeile { display: flex; flex-wrap: wrap; gap: .5rem; }
.frg-feld { display: grid; gap: .2rem; font-size: .82rem; font-weight: 600; color: var(--muted); }
.frg-feld input, .frg-feld select {
  padding: .4rem .5rem; border: 1px solid var(--border); border-radius: 8px;
  font-size: 1rem; background: #fff;
}
.frg-feld input { width: 8.5rem; }
.frg-kommentar { display: grid; gap: .2rem; margin-top: .5rem; font-size: .82rem; font-weight: 600; color: var(--muted); }
.frg-kommentar input {
  padding: .45rem .6rem; border: 1px solid var(--border); border-radius: 8px;
  font-size: 1rem; background: #fff;
}
.frg-aktionen { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-top: .6rem; }

/* Freigabe-Formular (Verwaltung → Freigaben): Grundfeld und Knopf nebeneinander. */
.reject-form { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; margin: 0; }
.reject-form input { font: inherit; font-size: 1rem; padding: .4rem .6rem; border: 1px solid var(--border); border-radius: 8px; min-width: 14rem; }
.chip-name { font-weight: 600; }
.chip-horse { color: var(--green-dark); font-weight: 500; white-space: nowrap; }
.chip-name-link { text-decoration: none; color: inherit; }
.chip-name-link:hover .chip-name { text-decoration: underline; color: var(--green-dark); }
.chip-act { color: var(--muted); }

.cap-badge { font-size: .8rem; font-weight: 700; padding: .15rem .5rem; border-radius: 999px; }
.cap-ok { background: var(--green-soft); color: var(--green-dark); }
.cap-full { background: var(--danger-soft); color: var(--danger); }

/* Admin */
.admin-menu-group { margin-top: 1.5rem; }
.admin-menu-group:first-of-type { margin-top: 1rem; }
/* Grüner Balken davor statt reinem Fettdruck: auf der Übersicht mit vier Gruppen ging die
   Gliederung sonst im Fließtext unter – Farbe zieht den Blick, bevor man liest. */
.admin-menu-heading {
  display: flex; align-items: center; gap: .5rem;
  font-size: 1.2rem; font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
  color: var(--green-dark); margin: 0 0 .6rem;
}
.admin-menu-heading::before {
  content: ""; width: 4px; height: 1.2rem; border-radius: 999px; background: var(--green); flex: none;
}
.admin-menu { display: flex; gap: .75rem; flex-wrap: wrap; }
/* Die beiden Kategorien der Aktivitätenliste ("Buchung sofort" / "Buchung nach Freigabe").
   Abstand ÜBER der Überschrift deutlich größer als darunter – so gehört sie sichtbar zu dem,
   was ihr folgt, statt zwischen den Gruppen zu schweben. */
.act-gruppe + .act-gruppe { margin-top: 2rem; }
.act-gruppe-titel { margin: 0; font-size: 1.05rem; }
.act-gruppe-hilfe { margin: .15rem 0 0; }
.act-gruppe-leer { margin: .6rem 0 0; }
/* Der Spaltenkopf wiederholt sich je Gruppe. Deshalb rückt er hier enger an seine Liste als
   zuvor, sonst entstünde bei zwei Gruppen doppelt so viel Leerraum wie vorher bei einer. */
.act-gruppe .act-list-head { margin-top: .7rem; }
.act-list { display: grid; gap: .5rem; margin: 1rem 0; }
.act-gruppe .act-list { margin-top: .35rem; margin-bottom: 0; }
.act-card {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .6rem .7rem; border: 1px solid var(--border); border-radius: 10px; background: #fff;
  transition: border-color .12s ease, background-color .12s ease;
}
/* Beim Überfahren nur der Rand – BEWUSST ohne den Schatten, den .slot, .tile und .person-card
   dort zeigen: Diese Zeile ist als Ganzes nicht anklickbar, angefasst werden nur ihre Knöpfe.
   Der Schatten ist im Projekt das Zeichen für "hier kannst du klicken" und würde hier ein
   Versprechen geben, das die Zeile nicht einlöst. Gleiche Zurückhaltung wie bei .abr-karte.
   Der Effekt dient der Zeilenführung: bei fünf gleich gebauten Zeilen sieht man, wo man ist. */
.act-card:hover { border-color: var(--green); background: #fbfdfc; }
/* Die aufgeklappte Bearbeiten-Zeile ist ein Formular, keine Listenzeile: Dort steht man schon
   drin, und ein Hover-Wechsel unter den Eingabefeldern wäre Unruhe ohne Aussage.
   :not(.is-editing), weil sonst genau diese Regel den dauerhaften grünen Bearbeiten-Zustand
   unten wieder zurücksetzt, sobald die Maus über dem Formular steht – beide Regeln haben sonst
   dieselbe Spezifität, und "die Maus steht gerade über den Feldern" ist während des Ausfüllens
   praktisch immer der Fall. */
form.act-card:hover:not(.is-editing) { border-color: var(--border); background: #fff; }
/* Bearbeiten-Zustand der Aktivitäten-Zeile: kräftiger als .act-card:hover oben – die
   Hover-Werte (1px-Randfarbe, #fbfdfc) tragen einen VORÜBERGEHENDEN Effekt, der zusätzlich vom
   Mauszeiger und der Übergangsanimation lebt. Im Ruhezustand, dauerhaft auf der Seite, reichte
   das nicht: zu wenig Kontrast zu den weißen Zeilen daneben. Deshalb hier ein dickerer Rahmen
   UND --green-soft statt #fbfdfc – derselbe Ton, mit dem das Hauptmenü seinen "du bist hier"-
   Zustand markiert (.tabbar-item.active, .topnav a.active), keine neu erfundene Farbe.
   align-items: flex-start statt center (geerbt von .act-card), weil die
   act-feld-gruppe-Blöcke jetzt eigene, unterschiedlich hohe Zeilen bilden – center hätte sie
   mittig statt oben an der Karte ausgerichtet. */
.act-card.is-editing {
  border: 2px solid var(--green); background: var(--green-soft);
  align-items: flex-start;
}
/* Fester Kopf mit dem Namen, getrennt vom Namensfeld darunter: Das Feld ändert sich ja gerade
   womöglich (der Admin tippt vielleicht selbst einen neuen Namen) und taugt deshalb nicht als
   verlässlicher Anker für "welche Aktivität ist das". */
.act-edit-kennzeichen {
  flex-basis: 100%; margin: 0 0 .1rem; font-size: .85rem; color: var(--green-dark);
}
.act-edit-kennzeichen strong { font-size: 1rem; }
/* Feldgruppen (Pflichtpause, Buchungsfrist) brechen auf eine eigene Zeile um statt mit allem
   anderen in einer langen, mehrfach umbrechenden Kette zu stehen – genau das ergab den
   "wild und unruhig"-Eindruck bei mehr als einer Handvoll Feldern in einer Bearbeiten-Zeile.
   Eigener, etwas hellerer Rahmen als die Karte selbst (var(--border) auf weißem Grund statt
   Grün auf Grün) grenzt die Gruppe sichtbar ab, ohne mit dem kräftigen Kartenrahmen zu
   konkurrieren. */
.act-feld-gruppe {
  flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .55rem .65rem; border: 1px solid var(--border); border-radius: 8px; background: #fff;
}
.act-feld-gruppe-titel { flex-basis: 100%; margin: 0; font-size: .82rem; font-weight: 700; color: var(--muted); }
/* Speichern/Abbrechen/Löschen: keine Rahmenbox wie die Feldgruppen darüber – das sind Aktionen,
   keine Eingaben, eine dritte umrahmte Box in Folge wirkte nur wie mehr vom Gleichen. */
.act-feld-gruppe-aktionen { border: none; background: none; padding: .2rem 0 0; }
/* Inaktive Aktivität: gedämpfter Grund statt durchsichtigem Text. opacity .55 legte sich über
   ALLES – auch über die Schrift, die dadurch unter den Textkontrast fiel und mit ihr über den
   Farbpunkt der Aktivität, der gerade der Wiedererkennung dient. Den Zustand sagt jetzt das
   "inaktiv"-Kennzeichen neben dem Namen; die Fläche unterstützt ihn nur noch. */
.act-card.is-inactive { background: #faf9f7; border-color: #e8e5df; }
/* Auch beim Überfahren bleibt eine inaktive Aktivität gedämpft – nur der Rand antwortet. Würde
   sie hier den hellen Grund der aktiven Zeilen annehmen, sähe sie beim Zeigen kurz aus wie
   eingeschaltet. Steht nach der :hover-Regel oben, weil beide dieselbe Spezifität haben. */
.act-card.is-inactive:hover { background: #f6f4f0; border-color: var(--green); }
.act-card.is-inactive .act-name,
.act-card.is-inactive .act-rules,
.act-card.is-inactive .list-view { color: var(--muted); }
.act-card.is-dragging { opacity: .4; }
.act-card.drag-over { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
.drag-handle { cursor: grab; color: var(--muted); font-size: 1.2rem; line-height: 1; padding: 0 .2rem; flex: none; user-select: none; touch-action: none; }
.drag-handle:active { cursor: grabbing; }
.act-edit { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: 0; }
.act-edit input[type="color"] { width: 40px; height: 34px; padding: 0; border: 1px solid var(--border); border-radius: 8px; background: #fff; }
.act-name { min-width: 160px; }
.act-edit input[type="number"] { width: 70px; }
.check { display: inline-flex; align-items: center; gap: .5rem; font-weight: 500; }
/* "eingeloggt bleiben" am Login. Mit .form davor notiert, sonst überstimmt
   `.form label { display: grid }` das inline-flex von .check und stapelt Kästchen und Text.
   min-height für eine fingergerechte Tippfläche (Richtwert 44px). */
/* Jede Auswahlzeile im Formular, nicht nur die mit .remember: Ohne diese Regel greift
   .form label { display: grid } und stellt Kästchen und Beschriftung UNTEREINANDER – bei einem
   Häkchen ist das immer falsch. Vorher trug nur .remember die Ausnahme, was hieß, dass jede neue
   Auswahlzeile den Fehler von vorn machte (zuletzt .pause-ausnahme und die Einstellungen). */
.form label.check { display: inline-flex; min-height: 44px; justify-self: start; }
.check.inline { font-weight: 600; }
.check input[type="checkbox"] { width: 1.4em; height: 1.4em; flex: none; accent-color: var(--green); }
.units { display: inline-flex; align-items: center; gap: .4rem; }
.act-card .inline-form { margin-left: auto; }

/* Buchungsfrist im "Neue Aktivität"-Formular (Migration 041): ein Satz mit zwei Zahlenfeldern
   darin ("Maximal [Tage] Tage oder [Stunden] Stunden vor Wunschtermin buchen"). Mit .form davor
   notiert, sonst überstimmt `.form label { display: grid }` das flex und stapelt jedes Wort in
   eine eigene Zeile (derselbe Grund wie bei .check.remember oben). flex-wrap, weil der Satz auf
   dem Handy nicht in eine Zeile passt – dort brechen Wörter samt Feldern normal um, statt die
   Seite seitlich zu überlaufen. */
.form .frist-zeile { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; font-weight: 400; }
.frist-zeile .list-input-num { flex: 0 0 70px; }

/* Aktivitäten-Liste (nur diese Seite, nicht die act-card-Nutzung bei Produkten/Anlagen):
   CSS-Grid mit fester Spaltenbreite statt Flexbox – bewusst NACH .act-card notiert, damit
   display:grid dessen display:flex überstimmt (gleiche Selektor-Spezifität, Reihenfolge
   entscheidet). Mit flex-grow verteilte sich der Platz je nach Textlänge der jeweiligen Zeile –
   dadurch stimmten Kopf und Werte nie zuverlässig überein. Grid-Spalten sind unabhängig vom
   Inhalt fix, Kopf und Zeilen teilen dasselbe Template. Der Bearbeiten-Formular-Zustand bleibt
   bewusst bei der alten Flex-Zeile (andere Feldanzahl, keine Ausrichtung nötig). */
/* Kopf und Zeilen sind SEPARATE Grid-Container (kein gemeinsames Elternelement) – eine "auto"-
   oder minmax(...,1fr)-Spalte bemisst sich am eigenen Inhalt JEDES Containers für sich. Die
   Kopfzeile hat in der letzten Spalte keinen Inhalt, die Datenzeilen die beiden Knöpfe – bei
   "auto" bekäme "Platzbedarf" dadurch in Kopf und Zeile unterschiedlich viel Platz zugeteilt und
   die Spalten verschieben sich gegeneinander. Deshalb sind ab Spalte 3 alle Breiten fix in Pixeln
   statt inhaltsabhängig (fr/auto/minmax) – garantiert identische Spaltengrenzen in jeder Zeile. */
/* Spaltenmaße: Der Name bleibt bei 200px – er trägt zwar jetzt zusätzlich das
   "inaktiv"-Kennzeichen, kommt damit aber aus (längster Fall "Freies Reiten INAKTIV" ~158px).
   Jeder Pixel mehr fehlt der Regelspalte, und die entscheidet über die Zeilenhöhe: Wird sie zu
   schmal, brechen "Sperrt die ganze Reithalle FREIGABE" und die Kennzeichen um, und die Liste
   bekommt ungleich hohe Zeilen. Die Aktionsspalte hatte zuvor 220px – gemessen brauchen die
   beiden Knöpfe 213px ("Bearbeiten" 95 + "Deaktivieren" 110 + Abstand). Wird sie knapper
   bemessen, läuft der rechtsbündige Knopfblock nach LINKS über und legt sich über die
   Zirkelspalte; genau das passierte, als hier zeitweise ein dritter Knopf stand.
   240px, seit beide Knöpfe auf einheitliche 7rem (112px) gesetzt sind: 2 x 112 + 8px Abstand
   = 232px Bedarf. */
/* Erste Spalte trägt die Sortierbedienung (Griff bzw. Pfeile). Zirkelspalte von 90 auf 75px –
   "1 Zirkel" misst rund 52px, der Rest war Luft, die jetzt die Regelspalte braucht, damit die
   Zeilen nicht wieder umbrechen. */
.act-row {
  display: grid; align-items: center; column-gap: .6rem;
  grid-template-columns: 22px 12px 185px 1fr 75px 240px;
}
/* Griff und Pfeile teilen sich diese Spalte: Am Zeigergerät wird gezogen, auf dem Handy getippt.
   Beides gleichzeitig zu zeigen hieße, in einer ohnehin engen Zeile zwei Bedienungen für
   dieselbe Sache anzubieten. */
.act-sort { display: flex; align-items: center; justify-content: center; }
/* Pfeile: am Desktop verborgen, dort gibt es den Griff. Sie bleiben aber im DOM, damit die
   Umschaltung ohne JavaScript allein über die Bildschirmbreite läuft. */
.act-move { display: none; flex-direction: column; gap: 2px; }
.act-move-btn {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 21px; padding: 0;
  border: 1px solid var(--border); border-radius: 6px; background: #fff;
  color: var(--muted); font-size: .6rem; line-height: 1; cursor: pointer;
}
.act-move-btn:disabled { opacity: .35; cursor: default; }
.act-move-btn:not(:disabled):hover { border-color: var(--green); color: var(--green-dark); }
.act-list-head {
  margin-top: 1rem; padding: 0 .7rem;
  font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
  color: var(--muted);
}
.act-list-head-dot { width: 12px; }
.act-row-actions { display: flex; gap: .5rem; justify-self: end; }
/* Beide Knöpfe einer Zeile exakt gleich groß. Zwei Gründe für die einheitliche Breite: Die
   Beschriftung wechselt je Zustand zwischen "Aktivieren" und "Deaktivieren" – ohne feste Breite
   sprängen die Knöpfe beim Umschalten in der Größe. Und untereinander stünden sonst fünf
   verschieden breite Paare, deren rechte Kanten zwar bündig sind, deren Trennlinie aber zickzack
   verläuft. 7rem trägt die längste Beschriftung ("Deaktivieren").
   inline-flex statt des geerbten inline-block: <a> und <button> haben sonst leicht
   unterschiedliche Höhen, weil der Knopf eigene Browser-Vorgaben mitbringt. */
.act-row-actions .btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 7rem; min-height: 2.25rem;
}
/* Name samt "inaktiv"-Kennzeichen: die Ellipse von .list-view greift weiterhin für lange Namen,
   das Kennzeichen daneben darf aber nicht mitgeschnitten werden. */
.act-name { display: flex; align-items: center; gap: .4rem; }
.act-name .badge { flex: none; }
/* Regeln (Sperrmodus + Kennzeichen). Umbruchfähig – .list-view schnitt "gesondert berechnen"
   mitten im Wort ab. Lieber zwei Zeilen als eine halbe Aussage: Ob eine Aktivität gesondert
   berechnet wird, entscheidet über Geld. */
.act-rules { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .4rem; min-width: 0; }
/* Löschen sitzt im Bearbeiten-Formular, nicht in der Listenzeile. Als Textlink statt als Knopf:
   Neben "Speichern" und "Abbrechen" wäre ein dritter Knopf gleichgewichtig, obwohl er als
   einziger nichts zurückholt. Die Warnung trägt die Bestätigungsseite, die zwingend dazwischen
   liegt. margin-left:auto schiebt ihn ans Zeilenende, weg von "Speichern". */
.act-delete {
  margin-left: auto; padding: .35rem .5rem; border-radius: 8px;
  color: var(--danger); font-size: .85rem; font-weight: 600; text-decoration: none;
}
.act-delete:hover, .act-delete:focus-visible { background: var(--danger-soft); }

/* Kompakte Admin-Listenzeilen (Produkte, Anlagen, …): eine Zeile je Eintrag. "Bearbeiten" lädt
   dieselbe Seite mit ?bearbeiten=<id> neu und ersetzt die Textfelder der betroffenen Zeile direkt
   durch Eingabefelder (kein zusätzliches Formular darunter) – ganz ohne JavaScript, per
   Server-Rendering. Modifier-Klassen (-name/-narrow/-num/-wide) sind seitenübergreifend gleich,
   damit neue Listen (z. B. Anlagen) dasselbe Erscheinungsbild wie Produkte bekommen. */
.list-row { flex-wrap: wrap; font-size: .88rem; }
.list-view { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-view-name { font-weight: 600; flex: 1 1 140px; min-width: 90px; max-width: 240px; }
.list-view-narrow { color: var(--muted); flex: 0 1 70px; min-width: 50px; }
.list-view-num { font-variant-numeric: tabular-nums; font-weight: 600; flex: 0 0 auto; min-width: 60px; }
.list-view-wide { color: var(--muted); flex: 1 1 160px; min-width: 60px; }
.list-view-wide.is-empty { color: #9aa0a6; }
/* "ohne feste Zeit" (Dienstarten) ist länger als eine Uhrzeit à la "06:30–09:00" – die schmale
   Spalte bräuchte sonst eine Ellipse, wo eigentlich nur wenig Text steht. */
.list-view-narrow.is-empty { color: #9aa0a6; flex-basis: 110px; }
.list-input, select.list-input {
  font: inherit; font-size: .88rem; padding: .35rem .5rem; border: 1px solid var(--border);
  border-radius: 8px; background: #fff; color: var(--ink);
}
.list-input-name { flex: 1 1 140px; min-width: 100px; }
.list-input-narrow { flex: 0 1 90px; min-width: 70px; }
.list-input-num { flex: 0 1 90px; min-width: 70px; }
.list-input-wide { flex: 1 1 180px; min-width: 100px; }
.list-input-color { flex: 0 0 42px; width: 42px; height: 32px; padding: 2px; }
.list-check { flex: none; font-size: .88rem; font-weight: 500; }
.list-row .btn-sm { flex: none; }
.list-row .dot { flex: none; }
.list-row .units { flex: none; display: inline-flex; align-items: center; gap: .4rem; }
/* Buchungsfrist-Zeile im kompakten Bearbeiten-Formular (Migration 041): deutlich länger als die
   übrigen .units-Zeilen in dieser Reihe ("Maximal [Tage] Tg. oder [Std.] Std. vor Wunschtermin
   buchen"). inline-flex allein reicht hier NICHT zum Umbrechen – ein inline-level Flex-Container
   bemisst sich an seinem Inhalt, nicht an der verfügbaren Breite, und läuft deshalb über den
   Rand hinaus, egal ob flex-wrap gesetzt ist. flex-basis: 100% zwingt die Zeile auf volle Breite
   der Karte (die IST block-level, siehe .act-card), erst darin wirkt flex-wrap wie gewollt. */
.list-row .units.frist-zeile-kompakt { flex-basis: 100%; flex-wrap: wrap; }

/* Tabellen (falls verwendet) */
.table-wrap { overflow-x: auto; }

/* Historien auf der Nutzer-Detailseite (Buchungen, Bestellungen, Anmeldungen) */
.hist-sub { font-size: .95rem; font-weight: 700; color: var(--muted); margin: .8rem 0 .4rem; }
.hist-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .25rem; }
.hist-row {
  display: flex; align-items: center; gap: .5rem 1rem; flex-wrap: wrap;
  padding: .4rem .6rem; border: 1px solid var(--border); border-radius: 8px; background: #fff;
  font-size: .9rem;
}
.hist-row.is-canceled { opacity: .55; }
.hist-when { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 8.5rem; }
.hist-time { font-variant-numeric: tabular-nums; color: var(--muted); }
.hist-what { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.hist-sum { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Personen (Freigaben / Reiter) */
.people-list { display: grid; gap: .6rem; margin: 1rem 0; }
.person-card {
  padding: .8rem .9rem; border: 2px solid #cec6ac; border-radius: 10px; background: #fff;
  transition: background-color .12s ease, border-color .12s ease, box-shadow .12s ease;
}
/* Hover hebt den kompletten Nutzerblock hervor – bei langen Listen sonst schwer zu erkennen,
   welche Zeilen (Kopf, Kontaktdaten, Rollenformular) noch zum selben Nutzer gehören. */
.person-card:hover { background: #faf9f4; border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
.person-card:hover .role-checks { background: #eef4f0; }
.person-card.is-inactive { opacity: .6; }
.person-card.is-inactive:hover { opacity: .8; }
.person-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-bottom: .3rem; }
.person-meta { display: flex; gap: .5rem 1rem; flex-wrap: wrap; color: #374151; font-size: .9rem; margin-bottom: .5rem; }
.approve-form { display: grid; gap: .5rem; margin: .3rem 0; }
.approve-form input[name="versicherung_note"] { padding: .5rem .6rem; border: 1px solid var(--border); border-radius: 8px; max-width: 360px; }

.badge { font-size: .72rem; font-weight: 700; padding: .12rem .5rem; border-radius: 999px; text-transform: uppercase; letter-spacing: .02em; }
.badge-pending { background: var(--warn-soft); color: #92600b; }
.badge-blocked { background: var(--danger-soft); color: var(--danger); }
/* "inaktiv" ist eine Zustandsangabe, keine Warnung – deshalb neutral grau und nicht rot wie
   .badge-blocked. Eine deaktivierte Aktivität ist kein Problem, sondern eine Entscheidung. */
.badge-inaktiv { background: #eceae5; color: #5f5a51; }
.badge-fremdreiter { background: #eef2ff; color: #3730a3; }
.badge-einsteller { background: var(--green-soft); color: var(--green-dark); }
.badge-role { background: #eef2ff; color: #3730a3; }
/* Bestellung ohne Berechnung (Rolle BSF) – bewusst neutral, nicht wie eine Statusmeldung. */
.badge-free { background: var(--green-soft); color: var(--green-dark); }
/* Barzahler, erst sichtbar sobald geliefert (siehe .order-card.is-cash). Eigener Violett-Ton statt
   des Lila von .badge-group, sonst liest sich "im Namen von" wie "bar bezahlt". */
.badge-cash { background: #ede9fe; color: #5b21b6; }
/* Bestellgruppe: bestellt im Namen einer anderen Stelle. Bewusst NICHT in Großbuchstaben wie die
   Rollenkürzel – hier steht ein Eigenname wie "Olaf Offenstall", der lesbar bleiben muss. */
.badge-group {
  background: #f3e8ff; color: #6b21a8; text-transform: none; letter-spacing: 0;
  font-weight: 600;
}
/* Kostenpflichtige Buchung (Migration 030). Trägt einen Betrag, deshalb wie .badge-group ohne
   Großbuchstaben – "12,50 €" in Versalien läse sich falsch. Bernsteinton statt Grün: Grün steht
   in diesem Projekt bereits für "wird NICHT berechnet" (.badge-free), das wäre die Umkehrung. */
.badge-cost {
  background: #fdf0d5; color: #8a5a00; text-transform: none; letter-spacing: 0;
  font-weight: 700;
}
.group-members { margin: -.4rem 0 .6rem .2rem; }
.group-form { max-width: 420px; }
/* Rolle einer Bestellgruppe (BST/BSF, Migration 025). Anders als .badge-group ein Kürzel, also
   in Großbuchstaben wie die übrigen Rollenkennzeichen. Zwei Farben, weil der Unterschied genau
   der ist, auf den es ankommt: BSF wird nicht berechnet. Grün/Grau statt Ampelfarben – hier ist
   nichts richtig oder falsch, nur verschieden. */
.badge-grouprole { flex: none; }
.badge-grouprole-BST { background: #eef2ff; color: #3730a3; }
.badge-grouprole-BSF { background: var(--green-soft); color: var(--green-dark); }
/* Zusatz in einer Überschrift („Bestellen – im Namen von …"): gehört zur Überschrift, soll sie
   aber nicht dominieren, und darf auf schmalen Displays in die nächste Zeile rutschen. */
.h1-sub { font-size: .8em; font-weight: 600; color: var(--muted); }
.badge-status-bestellt { background: var(--info-soft); color: #2c5aa0; }
.badge-status-in_arbeit { background: var(--warn-soft); color: #92600b; }
.badge-status-ausgeliefert { background: var(--ok-soft); color: var(--green-dark); }
.badge-status-storniert { background: var(--danger-soft); color: var(--danger); }

/* ============================================================
   Bestellmodul
   Eigene Klassen (prod-/day-/ord-), damit das Hallen-Modul unberührt bleibt.
   ============================================================ */

/* --- Produktauswahl im Bestellformular --- */
.prod-list { display: grid; gap: .6rem; margin: .75rem 0 0; }
.prod-card {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .85rem 1rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: #fff; transition: border-color .12s ease, box-shadow .12s ease;
}
/* Sobald eine Menge > 0 gewählt ist, hebt sich die Karte hervor. */
.prod-card.has-qty { border-color: var(--green); box-shadow: 0 0 0 1px var(--green); }
.prod-info { display: grid; gap: .15rem; min-width: 0; }
.prod-name { font-weight: 600; }
.prod-desc { color: var(--muted); font-size: .88rem; }
.prod-price { font-size: .92rem; font-weight: 600; color: #374151; }

/* Mengen-Stepper (− Zahl +). Das Zahlenfeld bleibt erhalten – ohne JS voll bedienbar. */
.stepper { display: flex; align-items: center; gap: .45rem; flex: none; }
.stepper-btn {
  width: 34px; height: 34px; flex: none; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 999px; background: #fff;
  font: inherit; font-size: 1.25rem; line-height: 1; color: var(--ink); cursor: pointer;
}
.stepper-btn:hover { background: var(--green-soft); border-color: var(--green); }
.stepper input[type="number"] {
  width: 3.5rem; padding: .35rem .25rem; text-align: center;
  font: inherit; font-weight: 600; font-variant-numeric: tabular-nums;
  border: 1px solid var(--border); border-radius: 8px; background: #fff; color: var(--ink);
}
/* Native Zahlenfeld-Pfeile ausblenden – gesteuert wird ausschließlich über − und +. */
.stepper input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.stepper input[type="number"]::-webkit-outer-spin-button,
.stepper input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input[type="number"]:focus { outline: 2px solid var(--green-soft); border-color: var(--green); }
/* Feste Breite für die Einheit: sonst verschiebt ein längerer Name ("Umsetzen" vs. "Ballen")
   den ganzen Stepper und die −/+-Knöpfe stehen von Zeile zu Zeile unterschiedlich weit rechts. */
.stepper-unit {
  color: var(--muted); font-size: .85rem;
  flex: 0 0 5.5rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* --- Bestellung ändern: Positionszeilen ---
   Beschriftung und Eingabefeld sollen nebeneinander stehen. Die Regeln sind bewusst mit .form
   davor notiert: sonst greift `.form label { display: grid }` und stapelt "Menge" bzw. die
   Entfernen-Box über ihr Feld. */
.edit-row.is-delivered { background: var(--green-soft); border-color: #8fbfa3; }
.edit-side { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; flex: none; }
.edit-fixed { color: var(--muted); font-size: .9rem; white-space: nowrap; }
.form .edit-field, .form .edit-remove {
  display: inline-flex; align-items: center; gap: .45rem; margin: 0;
  font-size: .9rem; white-space: nowrap;
}
.form .edit-field { font-weight: 600; }
.form .edit-remove { font-weight: 500; color: var(--muted); }
.form .edit-field input[type="number"] {
  width: 4.2rem; text-align: center; font-variant-numeric: tabular-nums;
}
.form .edit-field input[data-datum] { width: auto; }
.form .edit-remove input[type="checkbox"] {
  width: 1.2em; height: 1.2em; flex: none; accent-color: var(--green);
}

/* --- Wunschtermin-Auswahl --- */
.dayopts { display: grid; gap: .4rem; margin-top: .5rem; }
.dayopt {
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  padding: .6rem .85rem; border: 1px solid var(--border); border-radius: 10px;
  background: #fff; cursor: pointer; font-weight: 500;
}
.dayopt:hover { background: #f6f5f0; }
.dayopt input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* Die Markierung hängt an ZWEI Quellen, und das mit Absicht:
   .is-active setzt app.js – nötig für die Wunschtermine, weil dort zusätzlich die Live-Summe und
   der Zuschlag am selben Ereignis hängen.
   :has(input:checked) greift rein über CSS und deckt jede weitere Auswahlgruppe ab, die dieselbe
   Optik nutzt (etwa die Wunsch-Zeitfenster). Ohne diese zweite Regel wäre eine solche Gruppe
   zwar anklickbar, sähe aber dauerhaft unausgewählt aus – der Radiobutton selbst ist ja
   ausgeblendet. */
.dayopt.is-active,
.dayopt:has(input:checked) { background: var(--green); border-color: var(--green); color: #fff; }
.dayopt.is-active:hover,
.dayopt:has(input:checked):hover { background: var(--green-dark); }
.dayopt-extra { font-size: .82rem; font-weight: 700; color: #92600b; white-space: nowrap; }
.dayopt.is-active .dayopt-extra,
.dayopt:has(input:checked) .dayopt-extra { color: rgba(255, 255, 255, .88); }

/* Hinweisbox (z. B. Zuschlagsregeln) */
.notice {
  padding: .75rem .9rem; border-radius: 10px; font-size: .9rem; margin-top: 1rem;
  background: var(--warn-soft); border: 1px solid #ecdca6; color: #7a5109;
}

/* --- Live-Zusammenfassung am Fuß des Bestellformulars --- */
.ordersum {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border);
}
.ordersum-figures { display: grid; gap: .1rem; }
.ordersum-count { color: var(--muted); font-size: .9rem; }
.ordersum-extra { color: #92600b; font-size: .85rem; font-weight: 600; }
.ordersum-total { font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* --- Startseite: Bereiche als Kacheln (views/start.ejs) ---
   Zwei Spalten auf dem Handy, ab 700px so viele wie hineinpassen. Bewusst auto-fit statt einer
   festen Spaltenzahl: Neue Module sollen dazukommen können, ohne dass hier etwas nachgezogen
   werden muss – genau dafür gibt es die Kachelfläche. */
.start-datum { margin: -.2rem 0 1.1rem; }
.tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: .75rem; }
.tile {
  position: relative;
  display: grid; gap: .1rem; align-content: start;
  padding: .9rem .85rem 1rem; min-height: 132px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); text-decoration: none; color: var(--ink);
  /* Als Grid-Item gälte sonst min-width:auto und ein langes Wort ("Einstellungen") könnte die
     Spalte aufblähen – dieselbe Falle wie bei .order-card. */
  min-width: 0;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.tile:hover { border-color: var(--green); box-shadow: 0 1px 2px rgba(0,0,0,.05), 0 8px 24px rgba(0,0,0,.07); }
.tile:focus-visible { outline: 3px solid var(--green-soft); outline-offset: 2px; border-color: var(--green); }
.tile:active { background: #fbfaf7; }
.tile-ico {
  width: 42px; height: 42px; border-radius: 11px; margin-bottom: .55rem;
  background: var(--green-soft); display: grid; place-items: center;
}
.tile-ico svg {
  width: 22px; height: 22px;
  fill: none; stroke: var(--green-dark); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.tile-name { font-weight: 700; font-size: 1rem; letter-spacing: -.01em; }
.tile-sub { color: var(--muted); font-size: .82rem; line-height: 1.35; }
/* Zahl oben rechts: dieselbe Kapselform wie .count-badge in der Verwaltung. Gelb, wenn etwas
   fehlt (unbesetzter Dienst, offene Freigabe), grün, wenn nur etwas anliegt. */
.tile-badge {
  position: absolute; top: .8rem; right: .8rem;
  padding: .1rem .5rem; border-radius: 999px;
  background: var(--green-soft); color: var(--green-dark);
  font-size: .78rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.tile-badge.warn { background: var(--warn-soft); color: #92600b; }

/* Anordnen-Modus (Migration 054): eigener Ziehgriff statt der ganzen Kachel, damit der Rest der
   Fläche beim Sortieren normal scrollbar bleibt – touch-action:none gilt bewusst nur auf dem
   Griff, nicht auf .tile. Links oben, damit er nicht mit .tile-badge (oben rechts) kollidiert. */
/* Firmen-Umschalter in der Kopfzeile (Migration 055). Unauffällig, solange man in der
   Hauptfirma arbeitet – dort ist er nur eine Möglichkeit, keine Information. Sobald eine
   NEBENFIRMA aktiv ist, färbt sich die Zeile: Wer Rechnungen schreibt, muss auf jeder Seite
   ohne Nachdenken sehen, für welche Firma er das gerade tut. */
.firma-wechsel { display: flex; align-items: center; gap: .4rem; margin-left: .25rem; }
.firma-wechsel select {
  font-size: .85rem; padding: .2rem 1.4rem .2rem .45rem; max-width: 13rem;
  border: 1px solid var(--border); border-radius: 8px; background: var(--card); color: var(--ink);
}
/* Firmenfarbe (Migration 057): --firma-akzent steht als Inline-Style am <body>, sobald eine
   Nebenfirma mit eigener Farbe aktiv ist (partials/head.ejs). Der Fallback auf var(--green) hält
   das bisherige Grün für Nebenfirmen OHNE eigene Farbe – nicht-brechende Vorgabe. EINE
   gespeicherte Farbe genügt: color-mix() leitet Hintergrund- und Textvarianten daraus ab, statt
   eine zweite "helle"/"dunkle" Farbe pflegen zu müssen. */
.firma-wechsel.firma-fremd select {
  border-color: var(--firma-akzent, var(--green));
  background: color-mix(in srgb, var(--firma-akzent, var(--green)) 15%, white);
  color: color-mix(in srgb, var(--firma-akzent, var(--green)) 70%, black);
  font-weight: 600;
}
/* Dieselbe Akzentfarbe auf der ganzen Kopf- und (mobilen) Navigationsleiste, nicht nur am
   Umschalter – das war der eigentliche Wunsch: sofort sehen, für welche Firma man arbeitet,
   ohne erst auf den Umschalter zu schauen. */
body.firma-fremd .topbar {
  background: color-mix(in srgb, var(--firma-akzent, var(--green)) 10%, rgba(255, 255, 255, .92));
  border-bottom: 2px solid var(--firma-akzent, var(--green));
}
body.firma-fremd .tabbar {
  background: color-mix(in srgb, var(--firma-akzent, var(--green)) 10%, rgba(255, 255, 255, .97));
  border-top: 2px solid var(--firma-akzent, var(--green));
}
body.firma-fremd .topnav a.active, body.firma-fremd .account-actions a.active {
  background: color-mix(in srgb, var(--firma-akzent, var(--green)) 18%, white);
  color: color-mix(in srgb, var(--firma-akzent, var(--green)) 70%, black);
}
body.firma-fremd .tabbar-item.active { color: color-mix(in srgb, var(--firma-akzent, var(--green)) 70%, black); }
body.firma-fremd .tabbar-item.active .tabbar-icon {
  background: color-mix(in srgb, var(--firma-akzent, var(--green)) 18%, white);
}
/* Auf dem Handy ist in der Kopfzeile kein Platz neben Markenname und Menü – dort steht die
   Auswahl unter der Zeile, über die ganze Breite. */
@media (max-width: 699px) {
  .topbar { flex-wrap: wrap; }
  .firma-wechsel { order: 10; width: 100%; margin: .35rem 0 0; }
  /* 16px und 44px sind hier Pflicht, keine Geschmacksfrage: Unter 16px zoomt iOS beim Antippen
     eines Auswahlfeldes die ganze Seite heran, und darunter liegende Tippflächen trifft man auf
     dem Handy nicht zuverlässig (siehe Arbeitsregeln). */
  .firma-wechsel select { flex: 1; max-width: none; min-height: 44px; font-size: 1rem; }
}
/* Auf dem Desktop steht der Umschalter in DERSELBEN Zeile wie Marke und Menü, auf gleicher Höhe
   mit den Menü-Schaltflächen - nicht mehr in einer eigenen Zeile darunter (das wirkte
   abgehängt). .topbar ist hier eine Flex-Zeile (siehe @media weiter unten); der Umschalter
   steht in der Quellreihenfolge direkt nach der Marke und damit von selbst vor dem Menü. */
@media (min-width: 700px) {
  .firma-wechsel select {
    /* Feste Breite statt automatischer Breite je Auswahl (der Browser bemisst ein <select>
       sonst an der gerade GEWÄHLTEN Option) - sie soll beim Wechseln nicht springen.
       --firma-breite kommt aus der längsten Firmenbezeichnung (partials/head.ejs). */
    width: var(--firma-breite, 16rem);
    max-width: none;
    font-size: .95rem;
    /* Höhe an die Menü-Schaltflächen angeglichen (.topnav a: padding .45rem .7rem). */
    padding: .5rem 1.7rem .5rem .7rem;
  }
}

/* Vorschau des Firmenlogos in den Rechnungseinstellungen. Karierter Grund macht sichtbar, wo
   das Bild durchsichtig ist – ein weißes Logo auf weißem Grund sähe sonst aus wie "kein Logo". */
.firma-logo-vorschau {
  max-width: 160px; max-height: 90px; padding: .4rem; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg);
}

.tiles-toolbar { display: flex; gap: .5rem; flex-wrap: wrap; margin: -.2rem 0 .9rem; }
.tile-handle {
  display: none; position: absolute; top: .5rem; left: .5rem; z-index: 2;
  width: 44px; height: 44px; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--card); border: 1px solid var(--border);
  color: var(--muted); font-size: 1.2rem; line-height: 1; cursor: grab; touch-action: none;
}
.tiles.is-anordnen .tile-handle { display: flex; }
/* Solange sortiert wird, führt ein Klick auf die Kachel nirgendwo hin (siehe start.ejs) –
   der Cursor soll das nicht weiter versprechen. -webkit-user-drag ergänzt das draggable=false
   aus start.ejs für ältere WebKit-Browser (Safari/iOS), user-select verhindert, dass beim
   Ziehen quer über die Kacheln Text markiert wird. */
.tiles.is-anordnen .tile {
  cursor: default; -webkit-user-drag: none; user-select: none; -webkit-user-select: none;
}
.tile.is-dragging { opacity: .55; border-color: var(--green); box-shadow: none; }

/* --- Bestellkarten (Kunde + Verwaltung) ---
   Kräftiger Statusrahmen wie in der Vorgänger-App: die Farbe trägt die Information. */
.order-list { display: grid; gap: .85rem; margin: 1rem 0; }
.order-card {
  border: 3px solid var(--border); border-radius: var(--radius);
  padding: 1rem; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
  /* min-width:0 ist hier Pflicht, nicht Kosmetik: Als Grid-Item gilt sonst min-width:auto, und
     die Karte wächst auf die Breite ihres breitesten unteilbaren Inhalts – auf dem Handy war das
     das Lieferdatum-Feld samt Beschriftung daneben. Die Karte wurde damit breiter als ihr
     Container und schob die ganze Seite seitlich weg. */
  min-width: 0;
}
.order-card.status-in_arbeit { background: #fde6b8; border-color: #e2a63a; }
.order-card.status-ausgeliefert { background: var(--ok-soft); border-color: #8fbfa3; }
.order-card.status-storniert { background: #fdf4f3; border-color: #e0a9a4; }
/* Barzahler, sobald geliefert (Migration 028): ersetzt bewusst das Grün von .status-ausgeliefert,
   nicht nur ein Zusatz-Rahmen – der Zahlungsweg soll auf den ersten Blick auffallen. */
.order-card.status-ausgeliefert.is-cash { background: #ede9fe; border-color: #a78bfa; }
.order-head { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; gap: .5rem; margin-bottom: .5rem; }
.order-head-right { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.order-no { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-weight: 700; }
.order-meta { display: grid; gap: .15rem; color: var(--muted); font-size: .88rem; }

/* Bestellkopf (Verwaltung und Nutzeransicht gleich gestaffelt): Name und Termin sind die
   Arbeitsinformationen und deshalb groß, alles Übrige (Bestelldatum, Nummer, Anleger) ist
   Beleg-Angabe und tritt zurück. */
.order-person strong { font-size: 1.25rem; font-weight: 700; color: var(--ink); }
.order-wish { font-size: 1.1rem; font-weight: 700; color: var(--ink); }
.order-sub { font-size: .85rem; font-weight: 400; color: var(--muted); }
/* Die Bestellnummer erbt sonst font-weight 700 aus .order-no. */
.order-sub.order-no { font-weight: 400; }

/* Positionsliste innerhalb einer Bestellkarte */
.ord-items { list-style: none; margin: .75rem 0 0; padding: .6rem 0 0; display: grid; gap: .12rem; border-top: 1px solid var(--border); }
.ord-item { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .3rem .4rem; border-radius: 8px; font-size: .93rem; transition: background-color .12s ease; }
/* Hover hebt die überfahrene Position hervor – hilft beim Abhaken langer Bestellungen. */
.ord-item:hover { background: #efeee8; }
.ord-item.is-delivered:hover { background: #d9ebe0; }
.ord-item-main { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.ord-item-qty { flex: none; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ord-item.is-delivered { background: var(--green-soft); }
.ord-item.is-delivered, .ord-item.is-delivered .ord-item-qty { color: var(--green-dark); }
.ord-item-note { font-size: .8rem; color: var(--green-dark); padding: 0 .4rem .25rem 1.75rem; }

/* Betragszeilen (Zuschlag / Gesamt) */
.ord-line { display: flex; justify-content: space-between; gap: .75rem; padding: .3rem .4rem; font-size: .93rem; font-variant-numeric: tabular-nums; }
.ord-line-extra { color: #92600b; font-weight: 600; }
.ord-line-total { margin-top: .3rem; padding-top: .5rem; border-top: 1px solid var(--border); font-size: 1.02rem; font-weight: 700; }
/* Kundenkommentar zur Bestellung (o.notes) – z. B. ein Lieferhinweis wie "Linke Raufe bitte um
   17.30 Uhr". Stand vorher als graue Kursivzeile NACH allen Aktionsknöpfen und ging dort
   unter: genau die Information, die vor dem "Als geliefert markieren" gelesen werden muss.
   Gleiche Warnfarbe wie .badge-pending/.btn-warn ("braucht Aufmerksamkeit"), damit sie sich von
   der neutralen internen Notiz darunter abhebt. In orders.ejs/meine-bestellungen.ejs deshalb auch
   vor die Positionsliste gerückt statt dahinter. */
.ord-note {
  display: flex; gap: .5rem; align-items: flex-start;
  font-size: .92rem; font-weight: 600; font-style: normal; color: #7a5109;
  background: var(--warn-soft); border: 1px solid #ecdca6; border-radius: 8px;
  padding: .55rem .7rem; margin: 0 0 .75rem;
}
.ord-note::before { content: "📌"; flex: none; }

/* Barzahlung-Kennzeichen: eigene Zeile über der internen Notiz, speichert sofort beim Umschalten
   (kein "blur" wie bei einem Textfeld). */
.cash-toggle { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .85rem; }

/* Interne Notiz je Bestellung: speichert beim Verlassen des Feldes automatisch. */
.intnote { margin-top: .85rem; padding-top: .6rem; border-top: 1px solid var(--border); display: grid; gap: .3rem; }
.intnote-label {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  color: var(--muted); font-size: .78rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .03em;
}
.intnote-saved { color: var(--green-dark); font-weight: 700; text-transform: none; letter-spacing: 0; font-size: .85rem; }
.intnote-saved.is-error { color: var(--danger); }
.intnote-field {
  font: inherit; font-size: .9rem; width: 100%; padding: .5rem .6rem; resize: vertical;
  border: 1px solid var(--border); border-radius: 8px; background: #fff; color: var(--ink);
}
.intnote-field:focus { outline: 2px solid var(--green-soft); border-color: var(--green); }
.ord-note-danger { color: var(--danger); font-style: italic; margin: .6rem 0 0; font-size: .9rem; }
.ord-actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-top: .85rem; }
/* Das Liefer-Formular trägt kein .form, daher greifen dessen Feldstile hier nicht – ohne diese
   Regel bleibt das Datumsfeld ein unformatiertes Systemfeld (nur ~22px hoch, zu klein zum Tippen). */
.ord-actions input[data-datum] {
  font: inherit; font-size: 1rem; padding: .45rem .55rem; color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px; background: #fff;
}
.ord-actions input[data-datum]:focus { outline: 2px solid var(--green-soft); border-color: var(--green); }

/* Filterleiste als Chips (Status) */
.chip-row { display: flex; gap: .35rem; flex-wrap: wrap; align-items: center; }
.chip-link {
  display: inline-block; padding: .3rem .75rem; border: 1px solid var(--border); border-radius: 999px;
  background: #fff; color: var(--muted); font-size: .88rem; font-weight: 500; text-decoration: none;
}
.chip-link:hover { background: #f6f5f0; }
.chip-link.active { background: var(--green-soft); border-color: #9cc5ae; color: var(--green-dark); font-weight: 700; }
.filter-label { color: var(--muted); font-size: .88rem; font-weight: 600; min-width: 3.5rem; }
.filter-row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin-bottom: .6rem; }

/* Zusammenklappbarer Block in der Verwaltung. Bewusst als <details>: der Browser verwaltet den
   Zustand selbst, es braucht kein JavaScript und die Tastaturbedienung ist eingebaut. */
.admin-collapse { border: 1px solid var(--border); border-radius: 10px; background: #fff; margin-top: 1rem; }
.admin-collapse > summary {
  cursor: pointer; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .7rem .9rem; min-height: 44px; /* Fingergröße auf dem Handy */
  font-weight: 600; border-radius: 10px; list-style: none;
}
.admin-collapse > summary::-webkit-details-marker { display: none; }
.admin-collapse > summary::before { content: '▸'; color: var(--green); font-size: .9rem; }
.admin-collapse[open] > summary::before { content: '▾'; }
.admin-collapse > summary:hover { background: #f7f6f2; }
.admin-collapse[open] > summary { border-bottom: 1px solid var(--border); border-radius: 10px 10px 0 0; }
.admin-collapse-hint { font-weight: 400; font-size: .85rem; color: var(--muted); }
.admin-collapse-body { padding: .8rem .9rem .9rem; }
/* Die Summenzeile ist die letzte Zeile im Block – kein zusätzlicher Abstand nach unten. */
.admin-collapse-body .summary { margin-bottom: 0; }

.booking-item.is-delivered { background: var(--ok-soft); border-color: #bcd8c7; }

.person-actions { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; margin-top: .4rem; }
/* Das Formular selbst erzeugt bewusst KEINEN eigenen Kasten (display: contents): so liegen der
   Häkchen-Kasten und der Speichern-Knopf direkt in .person-actions. Der Kasten belegt eine ganze
   Zeile, dadurch stehen "Rollen speichern" und "Sperren" darunter nebeneinander auf gleicher Höhe.
   Mit dem Formular als eigenem Block wäre "Sperren" immer darunter gelandet, weil die Rollenliste
   die volle Breite braucht. */
.roles-form { display: contents; }
.role-checks {
  flex: 1 0 100%;
  display: flex; gap: .8rem; align-items: center; flex-wrap: wrap;
  padding: .5rem .6rem; background: #f7f6f2; border-radius: 8px;
}
.role-check { font-weight: 600; }
.guardian-meta { margin-top: -.2rem; }
.person-link { text-decoration: none; color: inherit; }
.person-link:hover strong { text-decoration: underline; }

/* Datentabelle (Präsenz) */
.datatable { width: 100%; border-collapse: collapse; font-size: .92rem; }
.datatable th, .datatable td { text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--border); vertical-align: top; }
.datatable th { color: var(--muted); font-size: .8rem; text-transform: uppercase; letter-spacing: .03em; }

/* Sortierbare Tabellenköpfe: der ganze Kopf ist ein Link, die aktive Spalte hervorgehoben. */
.th-sort { padding: 0 !important; }
.th-sort a {
  display: block; padding: .5rem .6rem; text-decoration: none; color: inherit; white-space: nowrap;
}
.th-sort a:hover { background: #f0efe9; color: var(--green-dark); }
.th-sort.is-active a { color: var(--green-dark); }
.th-arrow { font-size: .75em; }
.nowrap { white-space: nowrap; }
/* Nur für Screenreader: Beschriftung vorhanden, aber nicht sichtbar. Die clip-Technik statt
   display:none oder visibility:hidden – nur so bleibt der Text im Accessibility-Tree.
   (Genau daran krankt die .dayopt-Kachelauswahl im Bestellformular, siehe Abschnitt
   Barrierefreiheit in der Projektdokumentation.) */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.datatable tr.row-inactive { opacity: .5; }

/* Nutzer-Suche & Rollen-Filter */
.user-filter { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin: .5rem 0 1rem; }
#userSearch { flex: 1 1 220px; min-width: 180px; font: inherit; padding: .55rem .7rem; border: 1px solid var(--border); border-radius: 10px; background: #fff; }
#userSearch:focus { outline: 2px solid var(--green-soft); border-color: var(--green); }
.role-filters { display: flex; gap: .35rem; flex-wrap: wrap; }
.chip-btn { font: inherit; font-size: .85rem; font-weight: 600; padding: .35rem .7rem; border: 1px solid var(--border); border-radius: 999px; background: #fff; color: var(--ink); cursor: pointer; }
.chip-btn:hover { background: #f6f5f0; }
.chip-btn.active { background: var(--green); border-color: var(--green); color: #fff; }

/* Personen-Live-Suche (z. B. Rundmail: Einzelperson auswählen) */
.user-picker { position: relative; display: grid; gap: .5rem; }
.user-picker input[type="search"] {
  font: inherit; padding: .6rem .7rem; border: 1px solid var(--border); border-radius: 10px; background: #fff; width: 100%;
}
.user-picker input[type="search"]:focus { outline: 2px solid var(--green-soft); border-color: var(--green); }
.user-picker-list {
  position: absolute; z-index: 30; top: 3.1rem; left: 0; right: 0;
  max-height: 260px; overflow-y: auto; background: #fff; border: 1px solid var(--border);
  border-radius: 10px; box-shadow: var(--shadow); padding: .3rem;
}
.user-picker-item {
  display: block; width: 100%; text-align: left; padding: .55rem .6rem; border: 0; background: none;
  border-radius: 8px; cursor: pointer; font: inherit; color: var(--ink);
}
.user-picker-item:hover { background: var(--green-soft); }

/* Desktop: Menü mittig statt rechts, Burger aus, Sekundär-Links direkt in der Leiste.
   Flex statt Grid (bis 2026-09-09 ein 3-Spalten-Grid): Der Firmen-Umschalter steht als weiteres
   Element zwischen Marke und Menü in derselben Zeile - mit Grid müsste jedes Element eine eigene
   Spalte kennen, mit Flex reiht er sich einfach in die Quellreihenfolge ein.
   Zentrierung über GLEICH GROSSE Flanken statt "Menü wächst" (bis 2026-09-10 flex:1 auf .topnav):
   Das zentrierte das Menü nur im Restplatz zwischen Marke/Umschalter und Konto – ohne
   Firmenwechsel (die meisten Nutzer) oder mit kurzem "Abmelden" war der Rest links und rechts
   unterschiedlich breit, das Menü sichtbar nach rechts verschoben. .topbar-start (Marke +
   Umschalter) und .account-actions bekommen jetzt beide flex:1 mit Basis 0 – bei gleichem
   Wachstumsfaktor und gleicher Basis landen beide BOXEN exakt gleich breit, unabhängig vom
   tatsächlichen Inhalt. Das Menü dazwischen behält seine natürliche Breite (flex:0 0 auto) und
   liegt dadurch automatisch in der Mitte der GANZEN Zeile. */
@media (min-width: 700px) {
  .topbar { display: flex; align-items: center; gap: 1rem; }
  .topbar-start { display: flex; align-items: center; gap: 1rem; flex: 1 1 0; }
  .topbar .topnav { flex: 0 0 auto; justify-content: center; }
  .account-actions { flex: 1 1 0; justify-content: flex-end; }
  .nav-desktop { display: inline-flex; align-items: center; gap: .25rem; }
  .burger, .burger-panel { display: none !important; }
  /* Ab hier übernimmt wieder die Zeile oben – unten würde die Leiste nur Platz kosten. */
  .tabbar { display: none; }
  .tabbar ~ .container { padding-bottom: 3rem; }
  /* Kacheln: so viele Spalten wie hineinpassen. Bei 960px Inhaltsbreite ergibt das für die
     heutigen vier Bereiche eine Reihe; kommt ein Modul dazu, bricht es von selbst um. */
  .tiles { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; }
  .tile { min-height: 150px; padding: 1.1rem 1rem 1.2rem; }
  /* Wochenübersicht ab hier als echte Wochenspalten. */
  .weekgrid { grid-template-columns: repeat(7, 1fr); gap: .4rem; }
  .weekday > a { padding: .6rem .5rem; }
  .weekday-status { font-size: .85rem; }
  .weekday-info { font-size: .78rem; }
}

/* Abrechnung */
.filterbar { display: flex; gap: 1rem; align-items: end; flex-wrap: wrap; margin: 1rem 0; }
.filterbar label { display: grid; gap: .3rem; font-weight: 600; font-size: .9rem; }
.filterbar select, .filterbar input { font: inherit; padding: .5rem .6rem; border: 1px solid var(--border); border-radius: 8px; }
/* align-items nötig: ohne es strecken sich alle Knöpfe (Flex-Standard "stretch") auf die Höhe
   des höchsten – sobald ein Beschriftungstext auf schmalen Displays umbricht, werden dadurch
   ALLE Knöpfe unnötig hoch. flex-wrap, damit sie stattdessen sauber umbrechen. */
.filterbar-actions { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.billing-actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1rem; }
.summary { background: var(--green-soft); padding: .6rem .9rem; border-radius: 8px; }

/* Dauerhafter Hinweis im Seiteninhalt – anders als .flash, das eine Rückmeldung auf eine gerade
   ausgeführte Handlung ist und beim nächsten Aufruf verschwindet. Ein Hinweis wie "diese
   Pflichtangaben fehlen noch" gehört dagegen zum Zustand der Seite und steht dort, solange er
   zutrifft. Gleiche Farbsprache wie überall im Projekt: Gelb = etwas fehlt noch, Grün = in
   Ordnung. */
/* Kopfblock einer von der App erzeugten Rechnung (Migration 049): Herkunft, Empfänger, PDF und
   Storno. Abgesetzt hinterlegt, weil er etwas anderes ist als die Positionsliste darunter – er
   beschreibt das DOKUMENT, nicht seinen Inhalt. */
.rg-erzeugt {
  padding: .8rem .9rem; border: 1px solid var(--border); border-radius: 10px;
  background: #faf9f5;
}
.rg-erzeugt-kopf { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.rg-erzeugt .billing-actions { margin-bottom: 0; }

/* Zweite Zeile im Bearbeiten-Formular einer Bestellgruppe (Rechnungsanschrift). Abgesetzt durch
   eine Linie, weil sie einen anderen Zweck hat als Name/Notiz/Rolle darüber: Die betrifft die
   Gruppe im Bestellbetrieb, diese hier nur den Fall, dass eine Rechnung auf sie läuft. */
.gruppe-anschrift { margin-top: .6rem; padding-top: .5rem; border-top: 1px dashed var(--border); }
.feldgruppe-titel {
  display: block; margin-bottom: .35rem;
  font-size: .78rem; font-weight: 600; color: var(--muted);
}

.hinweis {
  margin: .8rem 0 0; padding: .7rem .9rem;
  border: 1px solid var(--border); border-radius: 10px;
  font-size: .9rem; line-height: 1.5;
}
.hinweis.warn { background: var(--warn-soft); border-color: #ecdca6; color: #7a5108; }
.hinweis.gut { background: var(--green-soft); border-color: #bcd8c7; color: var(--green-dark); }

/* Zeichen für Zeichen gleich breit – für Nummern und Kennungen, die man vergleicht oder abtippt
   (Rechnungsnummer, IBAN). Die Abrechnung nutzt dasselbe Muster bereits in eigenen Regeln. */
.mono { font-family: ui-monospace, "IBM Plex Mono", "SFMono-Regular", Menlo, Consolas, monospace; }

/* Modul Abrechnung: Einstiegskarten für die zwei Bereiche. Bewusst NICHT .tile wiederverwendet –
   dort trägt das Icon die Wiedererkennung, hier ist die Summe die Information, und ein zweites
   Icon-Raster direkt hinter der Startseite wirkte wie derselbe Bildschirm noch einmal. */
.abr-einstieg { display: grid; grid-template-columns: repeat(2, 1fr); gap: .75rem; margin-top: 1rem; }
.abr-karte {
  display: grid; gap: .25rem; align-content: start; justify-items: start;
  padding: 1rem; min-width: 0;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); text-decoration: none; color: var(--ink);
  transition: border-color .12s ease, box-shadow .12s ease;
}
.abr-karte:hover { border-color: var(--green); }
.abr-karte:focus-visible { outline: 3px solid var(--green-soft); outline-offset: 2px; border-color: var(--green); }
.abr-karte-titel { font-weight: 700; font-size: 1rem; }
.abr-karte-summe { font-size: 1.35rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--green-dark); }

/* Umschalter Offen/Archiv. Gleiche Bauart wie .plan-switch beim Hallenplan, damit "zwei
   Ansichten desselben Bereichs" in der App überall gleich aussieht. */
.abr-tabs { display: inline-flex; gap: .25rem; background: #efedE6; padding: .25rem; border-radius: 999px; margin-bottom: .75rem; }
.abr-tab {
  padding: .45rem .9rem; border-radius: 999px; text-decoration: none; color: var(--muted);
  font-weight: 600; font-size: .9rem; display: inline-flex; align-items: center; gap: .4rem;
}
.abr-tab:hover { background: #e6e4dc; }
.abr-tab.is-active { background: #fff; color: var(--green-dark); box-shadow: 0 1px 2px rgba(0,0,0,.08); }

.abr-filter { display: flex; flex-wrap: wrap; gap: .6rem 1rem; align-items: flex-end; margin-bottom: .75rem; }
/* Die Suche bekommt den Vorrang beim Platz: Sie wird am häufigsten benutzt, der Zeitraum selten. */
/* .log-suche ist dasselbe Feld im Protokoll (admin/protokoll.ejs). Mitgeführt statt kopiert,
   damit die beiden Suchfelder der App nicht auseinanderlaufen. */
.abr-suche, .log-suche { flex: 1 1 14rem; display: grid; }
.abr-suche input, .log-suche input { width: 100%; padding: .6rem .7rem; border: 1px solid var(--border); border-radius: 10px; font-size: 1rem; }
/* Im Protokoll steht die Suche als eigene Zeile unter der Filterleiste, nicht neben ihr. */
.log-suche { margin: 0 0 .75rem; }
/* Hinweis auf das voreingestellte Zeitfenster. Steht zwischen Filter und Liste, damit klar ist,
   worauf sich die Liste darunter bezieht – und trägt den Weg zum vollen Verlauf gleich mit. */
.log-hinweis { margin: 0 0 .75rem; }
.abr-zeitraum { display: flex; flex-wrap: wrap; gap: .5rem; align-items: flex-end; }
.abr-zeitraum label { display: grid; gap: .2rem; font-size: .82rem; font-weight: 600; color: var(--muted); }
.abr-zeitraum input { width: 8.5rem; padding: .5rem .6rem; border: 1px solid var(--border); border-radius: 10px; font-size: 1rem; }
/* Von und bis als EIN beschriftetes Feld "Zeitraum" statt als zwei nebeneinander: Sie gehören
   zusammen, und in einer Zeile mit bis zu vier weiteren Filtern las sich jedes einzelne wie ein
   eigener. Kein <label>, weil die Gruppe zwei Felder umfasst – die Beschriftung ist ein <span>,
   den die Felder über aria-labelledby als Gruppennamen führen. */
.abr-zeitraum-feld { display: grid; gap: .2rem; }
.abr-feldname { font-size: .82rem; font-weight: 600; color: var(--muted); }
.abr-zeitraum-paar { display: flex; align-items: center; gap: .35rem; }
/* Auf schmalen Displays sind zwei Datumsfelder nebeneinander zu breit – dort untereinander,
   der Gedankenstrich dazwischen entfällt. */
@media (max-width: 480px) {
  .abr-zeitraum-paar { flex-wrap: wrap; }
  .abr-zeitraum-paar > span { display: none; }
  .abr-zeitraum-paar input { flex: 1 1 8.5rem; width: auto; }
}
/* Auswahlfilter (Offenstallgruppe, Nutzer). max-width, weil ein langer Gruppenname die
   Filterzeile sonst über die ganze Breite zieht und Datum und Knopf umbrechen lässt. */
.abr-zeitraum select {
  max-width: 12rem; padding: .5rem .6rem; border: 1px solid var(--border);
  border-radius: 10px; font-size: 1rem; background: #fff;
}

/* Die Postenliste. Bewusst eine Liste aus Karten statt einer Tabelle: Je Posten stehen
   Beschreibung und Notiz in unterschiedlicher Länge, und rechts gehören Betrag und die beiden
   Bedienelemente zusammen – in einer Tabelle mit fester Spaltenbreite wird daraus entweder eine
   Spalte voll Leerraum oder ein seitlich scrollendes Raster. */
.abr-liste { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .5rem; }
.abr-zeile {
  display: flex; gap: .75rem; align-items: flex-start; justify-content: space-between;
  padding: .7rem .85rem; border: 1px solid var(--border); border-radius: 10px; background: #fff;
  transition: opacity .2s ease, transform .2s ease, background-color .2s ease;
}
/* Abgerechnet, aber noch nicht bezahlt: gelb – dieselbe Bedeutung wie im ganzen Projekt
   ("teilweise erledigt", vgl. die Ampel im Hallenplan und .badge-pending). Weiß bleibt der
   noch gar nicht abgerechnete Posten. Bezahltes verschwindet ohnehin ins Archiv. */
.abr-zeile.abr-abgerechnet { background: var(--warn-soft); border-color: #e6d9b8; }
/* Kurzer Übergang beim Abhaken, damit der Posten nicht ohne Erklärung verschwindet. */
.abr-zeile.is-weg { opacity: 0; transform: translateX(1.5rem); }
/* min-width:0 lässt den Textblock schrumpfen – ohne das drückt eine lange Beschreibung die
   Betragsspalte aus der Karte (dieselbe Falle wie bei .order-card und .slot-body). */
.abr-zeile-haupt { min-width: 0; display: grid; gap: .15rem; }
.abr-zeile-kopf { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.abr-datum { font-variant-numeric: tabular-nums; color: var(--muted); font-size: .85rem; }
/* Posten-Art. Ohne Versalien, weil hier Wörter stehen und keine Rollenkürzel wie "ADM" –
   "PAUSCHALE" neben "Buchung" sah aus wie zwei verschiedene Arten von Kennzeichen. */
.badge-typ { background: #eef2ff; color: #3730a3; text-transform: none; letter-spacing: 0; font-weight: 600; }
.abr-person { font-weight: 700; }
/* Name verlinkt aufs Nutzerprofil. Gleiche Bauart wie .chip-name-link im Hallenplan: im Ruhezustand
   nicht als Link gestaltet – in einer Liste aus lauter Namen wären durchgehend unterstrichene
   Zeilen unruhig; erst beim Zeigen wird der Link sichtbar. */
.abr-person-link { text-decoration: none; color: inherit; }
.abr-person-link:hover .abr-person,
.abr-person-link:focus-visible .abr-person { text-decoration: underline; color: var(--green-dark); }
.abr-beschreibung { color: var(--muted); font-size: .88rem; overflow-wrap: anywhere; }
/* Am Desktop steht die Notiz NICHT dauerhaft in der Zeile – sie erscheint beim Zeigen auf das
   Notiz-Icon. Das hält die Liste kurz, gerade wenn viele Posten eine Notiz tragen. Auf dem Handy
   wird dieser Block wieder eingeblendet (Mobil-Block), weil es dort kein Zeigen mit der Maus gibt. */
.abr-notiz { display: none; font-size: .85rem; color: #8a5a00; background: #fdf0d5; padding: .2rem .5rem; border-radius: 8px; justify-self: start; overflow-wrap: anywhere; }
/* "Abgerechnet am" ist – anders als die Notiz – IMMER sichtbar, auch am Desktop: Es ist die
   Voraussetzung für den Bezahlt-Haken daneben (siehe setzeBezahlt in lib/abrechnung.js) und muss
   deshalb auf einen Blick erkennbar sein, nicht erst beim Zeigen mit der Maus. Eigener Blauton
   statt des Amber-Tons der Notiz, damit beide auf einen Blick unterscheidbar bleiben. */
.abr-abrdatum { font-size: .85rem; color: #3730a3; background: #eef2ff; padding: .2rem .5rem; border-radius: 8px; justify-self: start; }
.abr-zeile-seite { flex: none; display: grid; gap: .4rem; justify-items: end; }
.abr-betrag { font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.abr-aktionen { display: flex; align-items: center; gap: .4rem; }
.abr-notiz-knopf {
  position: relative;
  border: 1px solid var(--border); background: #fff; border-radius: 8px; cursor: pointer;
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  font-size: .95rem; line-height: 1;
}
.abr-notiz-knopf:hover { border-color: var(--green); background: var(--green-soft); }
/* Trägt der Posten eine Notiz, sieht man das schon am Icon – sonst müsste man jede Zeile
   einzeln anfahren, um zu erfahren, wo überhaupt etwas vermerkt ist. */
.abr-notiz-knopf.has-notiz { background: #fdf0d5; border-color: #d9b25e; }

/* Die Notiz beim Zeigen mit der Maus, ohne Klick. Eigene Kurzinfo statt des title-Attributs:
   Der Browser blendet title erst nach etwa einer Sekunde ein, kürzt lange Texte und lässt sich
   nicht gestalten. Auch bei Tastaturfokus sichtbar, damit sie nicht nur mit der Maus erreichbar
   ist. Auf dem Handy abgeschaltet – dort gibt es kein Zeigen, und ein Tipp öffnet ohnehin das
   Popup mit dem vollen Text (siehe Mobil-Block). */
.abr-notiz-knopf.has-notiz::after {
  content: attr(data-notiz-text);
  position: absolute; bottom: calc(100% + .4rem); right: 0;
  width: max-content; max-width: 17rem;
  padding: .45rem .6rem; border-radius: 8px;
  background: #1f2937; color: #fff;
  font-size: .82rem; font-weight: 400; line-height: 1.35; text-align: left;
  /* pre-line erhält Absätze aus dem Notizfeld; ohne das stünde alles in einer Zeile. */
  white-space: pre-line;
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
  z-index: 30;
}
.abr-notiz-knopf.has-notiz:hover::after,
.abr-notiz-knopf.has-notiz:focus-visible::after { opacity: 1; }

/* Knopf für "Abgerechnet am": gleiche Bauart wie der Notiz-Knopf, aber OHNE Hover-Kurzinfo –
   das Datum steht ja bereits dauerhaft in der Zeile (.abr-abrdatum), eine zweite Anzeige beim
   Zeigen mit der Maus wäre doppelt. */
.abr-abrdatum-knopf {
  border: 1px solid var(--border); background: #fff; border-radius: 8px; cursor: pointer;
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  font-size: .95rem; line-height: 1;
}
.abr-abrdatum-knopf:hover { border-color: var(--green); background: var(--green-soft); }
.abr-abrdatum-knopf.has-abrdatum { background: #eef2ff; border-color: #c7d2fe; }

.abr-bezahlt { display: inline-flex; align-items: center; gap: .35rem; cursor: pointer; font-size: .85rem; font-weight: 600; white-space: nowrap; }
.abr-bezahlt input { width: 1.15em; height: 1.15em; }
/* Ohne "Abgerechnet am" ist der Haken deaktiviert (siehe setzeBezahlt) – das bleibt optisch
   erkennbar, damit niemand ein gesetztes Häkchen erwartet. Der Zeiger sagt aber NICHT mehr
   "verboten": Ein Klick führt seit 2026-09-07 ins Datums-Popup und hakt danach gleich mit ab
   (public/js/abrechnung.js). Gedämpft, aber ansprechbar – "not-allowed" hielte Leute von dem
   Weg ab, der genau hier weiterführt. */
.abr-bezahlt:has(input:disabled) { cursor: pointer; opacity: .6; }
.abr-bezahlt:has(input:disabled):hover { opacity: 1; text-decoration: underline; }

/* Die Popups "Notiz" und "Abgerechnet am" nutzen die Overlay-Basis der Bild-Lightbox. Dass das
   hidden-Attribut dort gewinnt, erledigt die allgemeine Regel ganz oben in dieser Datei. */
#abrNotizPopup textarea, #abrDatumPopup input {
  width: 100%; font: inherit; font-size: 1rem; padding: .6rem .7rem;
  border: 1px solid var(--border); border-radius: 10px; margin-top: .5rem;
}
/* Der gerade betrachtete Monat in der Jahresübersicht. */
.datatable tr.is-current > td { background: var(--green-soft); }
.bill-group { margin: 1.25rem 0; }
.bill-name { font-size: 1.05rem; margin: 0 0 .5rem; padding-bottom: .3rem; border-bottom: 2px solid var(--border); }
.datatable tfoot td { border-top: 2px solid var(--border); border-bottom: none; }
.right { text-align: right; }
.print-title { display: none; }

/* Honeypot (Spamschutz) – für Menschen unsichtbar */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Rollen-Auswahl bei der Registrierung ("Ich bin …"): jede Option eine eigene Fläche, damit
   die beiden Möglichkeiten klar getrennt sind. Die Regeln stehen bewusst mit .form davor –
   sonst greift `.form label { display: grid }` und stapelt Kästchen, Titel und Beschreibung
   untereinander (und macht die Beschreibung fett mit). */
.rolepick { display: grid; gap: .5rem; }
.form .rolepick-item {
  display: flex; align-items: flex-start; gap: .7rem; margin: 0;
  padding: .7rem .8rem; border: 1px solid var(--border); border-radius: 10px;
  background: #fff; cursor: pointer; font-weight: 400; font-size: 1rem;
}
.form .rolepick-item:hover { border-color: var(--green); background: #f9fbf8; }
/* Angekreuzte Option deutlich hervorheben. */
.form .rolepick-item:has(input:checked) {
  border-color: var(--green); background: var(--green-soft); box-shadow: 0 0 0 1px var(--green);
}
.form .rolepick-item input[type="checkbox"],
.form .rolepick-item input[type="radio"] {
  width: 1.4em; height: 1.4em; flex: none; margin-top: .1rem; accent-color: var(--green);
}
.rolepick-text { display: grid; gap: .2rem; }
/* Uhrzeitfeld in der Wunschzeit-Auswahl: nur so breit wie nötig, sonst zieht es sich als
   Grid-Kind von .rolepick-text über die ganze Zeile. justify-self statt width, damit die
   Mindestbreite des Browsers für type="time" erhalten bleibt. */
.zeit-feld { justify-self: start; margin-top: .35rem; }
.zeit-spanne { color: var(--green-dark); font-size: .88rem; font-weight: 600; }
.zeit-spanne.is-error { color: var(--danger); }
/* Erklärung unter einem Eingabefeld (Ändern-Dialog). In .form ist jedes <label> ein Grid,
   der Hinweis wird dadurch von selbst zur eigenen Zeile unter dem Feld. */
.feld-hinweis { color: var(--muted); font-size: .82rem; font-weight: 400; }
.rolepick-title { font-weight: 600; }
.rolepick-desc { color: var(--muted); font-size: .9rem; font-weight: 400; }
/* Auswahlblock als <fieldset> (Bestellgruppe anlegen): ohne eigene Regeln brächte der Browser
   seinen Standardrahmen mit, der neben den Kästchen darunter fremd wirkt. */
.rolepick-set { border: 0; padding: 0; margin: 0; display: grid; gap: .4rem; }
.rolepick-set legend { padding: 0; font-size: .95rem; font-weight: 600; }

/* Firmenfarbe (Verwaltung → Einstellungen → Allgemeines): sechs vorgeschlagene Farben plus "kein
   Akzent" als Kreise statt eines freien Farbwählers - kuratiert, damit jede Wahl genug Kontrast
   bietet (siehe FARBEN in routes/admin/settings.js). Gleiches Fieldset-Muster wie .rolepick-set. */
.farbpick-set { border: 0; padding: 0; margin: 0 0 1rem; display: grid; gap: .5rem; }
.farbpick-set legend { padding: 0; font-size: .95rem; font-weight: 600; }
.farbpick { display: flex; flex-wrap: wrap; gap: .6rem; }
.farbpick-item {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; cursor: pointer; margin: 0; border-radius: 50%;
}
/* Das native Feld bleibt für Tastatur/Screenreader erhalten, nur unsichtbar - der sichtbare
   Kreis daneben ist rein dekorativ und trägt deshalb keinen eigenen Fokusring. */
.farbpick-item input {
  position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer;
}
.farbpick-swatch {
  display: block; width: 32px; height: 32px; border-radius: 50%;
  background: var(--sw, transparent); box-shadow: 0 0 0 1px var(--border) inset;
}
.farbpick-item:has(input:checked) .farbpick-swatch { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--sw, var(--ink)); }
.farbpick-item:has(input:focus-visible) { outline: 2px solid var(--green); outline-offset: 2px; }
.farbpick-item-none .farbpick-swatch {
  background: repeating-linear-gradient(45deg, #fff, #fff 4px, var(--border) 4px, var(--border) 5px);
}

/* Erziehungsberechtigte(r) */
.guardian { border: 1px solid var(--border); border-radius: 10px; padding: 1rem; display: grid; gap: 1rem; margin: 0; }
.guardian legend { font-weight: 600; padding: 0 .4rem; }

/* Pferde */
.horse-list { display: grid; gap: .8rem; margin: 1rem 0; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.horse-card { display: flex; gap: .8rem; padding: .7rem; border: 1px solid var(--border); border-radius: 10px; background: #fff; }
.horse-photo { flex: none; width: 96px; height: 96px; border-radius: 8px; overflow: hidden; background: #f0efe9; display: flex; align-items: center; justify-content: center; }
.horse-photo img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.horse-nophoto { color: #9aa0a6; font-size: .8rem; text-align: center; }
.horse-info { display: flex; flex-direction: column; gap: .25rem; min-width: 0; }
.photo-form { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; margin: .3rem 0 0; }
.photo-form input[type="file"] { font-size: .8rem; max-width: 100%; }

/* Bild-Lightbox (Pferdefotos u. a.) */
.lightbox-overlay {
  position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
  padding: 2rem; background: rgba(15, 15, 15, .82);
  opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.lightbox-overlay.open { opacity: 1; pointer-events: auto; }
.lightbox-img { max-width: min(92vw, 630px); max-height: 90vh; border-radius: 12px; box-shadow: 0 10px 40px rgba(0, 0, 0, .45); }
.lightbox-close {
  position: absolute; top: 1rem; right: 1.2rem; width: 40px; height: 40px;
  border: 0; border-radius: 999px; background: rgba(255, 255, 255, .92); color: #1f2937;
  font-size: 1.4rem; line-height: 1; cursor: pointer;
}
body.lightbox-lock { overflow: hidden; }

/* Bestellbestätigung (nutzt die Overlay-Basis der Bild-Lightbox) */
.confirm-overlay { padding: 1rem; }
.confirm-box {
  background: #fff; border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 1.5rem; max-width: 440px; width: 100%; max-height: 90vh; overflow-y: auto;
}
.confirm-items { list-style: none; margin: .75rem 0; padding: 0; display: grid; gap: .35rem; }
.confirm-items li { display: flex; justify-content: space-between; gap: .5rem; font-size: .95rem; }
.confirm-total { font-size: 1.1rem; margin-top: .75rem; padding-top: .5rem; border-top: 1px solid var(--border); }

/* Formular mit ungespeicherten Änderungen ([data-dirty-guard] in app.js): der Speichern-Knopf hebt
   sich ab, solange etwas offen ist. Beugt dem Vergessen vor, statt es nur beim Verlassen abzufangen. */
form.is-dirty button[type="submit"] {
  border-color: var(--green); background: var(--green-soft);
  color: var(--green-dark); font-weight: 700;
}
form.is-dirty button[type="submit"]::before { content: "● "; }

/* --- Arbeitsplan ------------------------------------------------------------------------------
   Wochenraster: Zeilen sind Dienstarten, Spalten die Wochentage. Die Tabelle bekommt einen
   eigenen Scroll-Bereich, damit sie auf schmalen Fenstern seitlich rutscht, statt die ganze
   Seite zu verbreitern. */
.wp-scroll { overflow-x: auto; }
.wp-grid { border-collapse: collapse; width: 100%; min-width: 640px; }
.wp-grid th, .wp-grid td { border: 1px solid var(--border); padding: .4rem .5rem; vertical-align: top; }
.wp-grid thead th { background: #f7f6f2; font-size: .9rem; text-align: center; }
.wp-grid thead th a { text-decoration: none; color: inherit; display: block; }
.wp-grid thead th.is-today, .wp-cell.is-today { background: var(--green-soft); }
.wp-wd { display: block; font-weight: 700; }
.wp-dm { display: block; font-weight: 400; color: var(--muted); font-size: .85rem; }
.wp-corner { background: #f7f6f2; width: 10rem; }
.wp-art { text-align: left; font-weight: 600; white-space: nowrap; }
.wp-art-name { display: flex; align-items: center; gap: .35rem; }
/* Unbesetzt ist der Zustand, auf den es ankommt – dezent rot hinterlegt statt nur leer. */
.wp-cell.is-open { background: var(--danger-soft); }
/* Das ".form" davor ist nötig, nicht Zierde: .form label setzt display:grid und stapelte sonst
   Kästchen und Name untereinander (dieselbe Falle wie bei .check). Die Wochenansicht trägt kein
   .form am Formular, dort fiel es deshalb nicht auf. */
.wp-pick,
.form .wp-pick { display: flex; align-items: center; gap: .3rem; font-size: .85rem; font-weight: 400; cursor: pointer; }
.wp-pick input { accent-color: var(--green); width: 1.1em; height: 1.1em; }

/* Tagesansicht: dieselbe Einteilung untereinander – auch die Handy-Variante der Planung. */
.wp-day-card { border: 1px solid var(--border); border-radius: 10px; padding: .75rem .9rem; margin-bottom: .75rem; }
.wp-day-card.is-open { border-color: var(--danger); background: var(--danger-soft); }
.wp-day-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .5rem; }
/* Eine Person je Zeile statt nebeneinander: bei mehreren Mitarbeitern ist untereinander leichter
   zu erfassen, wer eingeteilt ist – und die ganze Zeile wird zur Klickfläche. */
.wp-day-people { display: grid; gap: .3rem; margin-bottom: .6rem; }
.wp-day-detail { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: flex-end; }
.wp-day-detail label { display: grid; gap: .2rem; font-size: .85rem; font-weight: 600; }
.wp-day-detail input { padding: .35rem .5rem; border: 1px solid var(--border); border-radius: 8px; font: inherit; font-size: 1rem; }
.wp-note { flex: 1 1 14rem; }
.wp-note input { width: 100%; }

/* Monatsansicht und Ampel kommen vom Hallenplan (.monthgrid/.monthday sowie die wp- und
   status-Klassen weiter oben) – eigene Farbregeln braucht der Arbeitsplan hier nicht.
   ACHTUNG: In CSS-Kommentaren niemals die Zeichenfolge Stern-Schrägstrich schreiben (etwa in
   einem Platzhalter wie ".wp-<Stern>/"). Sie beendet den Kommentar vorzeitig, der Rest wird als
   fehlerhaftes CSS gelesen und verschluckt stillschweigend die nächste Regel. */

/* Sicherheitsabfrage vor dem Speichern (arbeitsplan-bestaetigen.ejs): Auflistung dessen, was
   gleich passiert. Das Entfernen einer Person ist die Zeile, auf die es ankommt – deshalb rot,
   während eine neue Einteilung grün bleibt. Farbe zusätzlich zum Text, nie statt seiner. */
.wp-diff { margin: 1rem 0; }
.wp-diff-day {
  margin: 1rem 0 .4rem; font-size: .95rem; font-weight: 700;
  padding-bottom: .25rem; border-bottom: 1px solid var(--border);
}
.wp-diff-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .6rem;
  padding: .45rem .6rem; margin-bottom: .3rem;
  border-left: 3px solid var(--border); border-radius: 0 8px 8px 0;
  background: #f7f6f2;
}
.wp-diff-art { font-weight: 600; font-size: .85rem; }
.wp-diff-text { color: var(--ink); }
.wp-diff-entfernt { border-left-color: var(--danger); background: var(--danger-soft); }
.wp-diff-zugewiesen { border-left-color: var(--green); background: var(--green-soft); }
.wp-diff-zeit, .wp-diff-notiz { border-left-color: #92600b; background: var(--warn-soft); }

/* Protokoll (admin/protokoll.ejs). Karten statt Tabelle: sechs Spalten müssten auf dem Handy
   seitlich scrollen – das ist bei den Gebühren- und Statistiklisten ohnehin schon der wunde
   Punkt. Hier steht der Zeitpunkt deshalb über der Änderung statt neben ihr.
   (Entstanden für das Arbeitsplan-Protokoll, das mit Migration 035 im gemeinsamen aufging.) */
.log-list { display: grid; gap: .4rem; margin: 1rem 0; }
.log-row {
  padding: .55rem .7rem; background: #fff;
  border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: 10px;
}
.log-meta { display: flex; flex-wrap: wrap; gap: .3rem .6rem; font-size: .8rem; color: var(--muted); }
.log-actor { font-weight: 600; color: var(--ink); }
.log-body { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .5rem; margin-top: .2rem; }
.log-what { font-weight: 600; }
/* Aufklappbare Einzelheiten (lange Detailtexte, z. B. eine Wochenplanung mit zwanzig
   Änderungen). Die Zusammenfassung bleibt im Fluss der Zeile stehen und sieht aus wie der
   übrige Kleintext – nur der Zeiger verrät, dass mehr dahintersteckt. */
.log-detail { font-size: .8rem; color: var(--muted); }
.log-detail summary { cursor: pointer; list-style: revert; }
.log-detail summary:hover { color: var(--green-dark); }
.log-detail > span { display: block; margin-top: .25rem; }
/* Farbcodierung wie in der Sicherheitsabfrage oben: grün = etwas entstand, rot = etwas fiel weg,
   braun = geändert (tonFuer in lib/protokoll.js). Die früheren Klassen .log-zugewiesen/
   .log-entfernt/.log-zeit/.log-notiz gehörten zum eigenen Arbeitsplan-Protokoll und sind mit
   Migration 035 entfallen. */
.log-neu { border-left-color: var(--green); }
.log-weg { border-left-color: var(--danger); }
.log-aenderung { border-left-color: #92600b; }
/* Das betroffene Objekt führt zum Datensatz. Im Ruhezustand nicht als Link gestaltet – in einer
   Liste aus lauter Verweisen wären durchgehende Unterstreichungen unruhig; gleiche Überlegung
   wie bei .chip-name-link und .abr-person-link. */
.log-link { color: inherit; text-decoration: none; }
.log-link:hover, .log-link:focus-visible { color: var(--green-dark); text-decoration: underline; }

/* Seitenweises Blättern. Die Randknöpfe bleiben am Anfang und am Ende stehen (nur ausgegraut),
   damit die Zeile nicht springt und der Daumen dieselbe Stelle trifft. */
.pager { display: flex; align-items: center; justify-content: space-between; gap: .5rem; flex-wrap: wrap; margin-top: 1rem; }
.pager .is-disabled { opacity: .4; pointer-events: none; }

/* Storno-Knopf an einer Buchung im Hallenplan (nur für die Verwaltung). Als kleiner roter Knopf
   statt als blasses Zeichen – zwischen Name, Pferd und Aktivität ging ein graues ✕ unter.
   Beim Überfahren füllt er sich, damit vor dem Klick klar ist, was getroffen wird. */
.chip-cancel {
  flex: none; margin-left: .35rem;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 .25rem;
  border: 1px solid var(--danger); border-radius: 6px;
  background: var(--danger-soft); color: var(--danger);
  text-decoration: none; font-weight: 800; font-size: .8rem; line-height: 1;
}
.chip-cancel:hover, .chip-cancel:focus-visible { background: var(--danger); color: #fff; }

/* Kostenpflichtige Buchung im Hallenplan (nur für die Verwaltung sichtbar, siehe buildDayModel).
   .chip-kosten ist die Anzeige des Betrags, .chip-kosten-link der Knopf daneben, der auf die
   Seite "Buchung berechnen" führt. Gleiche Maße wie .chip-cancel, damit die beiden Knöpfe eine
   saubere Zeile bilden. Auf dem Handy bekommen beide zusammen eine größere Tippfläche – siehe
   .chip-actions im Mobil-Block: zwei 22px-Ziele direkt nebeneinander wären zu dicht, und eines
   davon storniert. */
.chip-kosten {
  flex: none; margin-left: .35rem; padding: .1rem .4rem; border-radius: 999px;
  background: #fdf0d5; color: #8a5a00; font-weight: 700; font-size: .75rem; white-space: nowrap;
}
.chip-kosten-link {
  flex: none; margin-left: .35rem;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 .25rem;
  border: 1px solid #d9b25e; border-radius: 6px;
  background: #fdf0d5; color: #8a5a00;
  text-decoration: none; font-weight: 800; font-size: .8rem; line-height: 1;
}
.chip-kosten-link:hover, .chip-kosten-link:focus-visible { background: #8a5a00; color: #fff; }

/* Bearbeiten-Knopf an einer Buchung im Hallenplan (nur Verwaltung). Gleiche Maße wie
   .chip-cancel/.chip-kosten-link, Farbe an .badge-status-bestellt angelehnt (blasses Blau statt
   Rot/Gelb, damit die drei Knöpfe auf einen Blick als unterschiedliche Aktionen lesbar bleiben). */
.chip-edit-link {
  flex: none; margin-left: .35rem;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 .25rem;
  border-radius: 999px;
  background: var(--info-soft); color: #2c5aa0;
  text-decoration: none; font-weight: 800; font-size: .8rem; line-height: 1;
}
.chip-edit-link:hover, .chip-edit-link:focus-visible { background: #2c5aa0; color: #fff; }

/* Gesperrter Slot (Sperrzeit) */
.slot.is-blocked { background: #f4f2ef; }
.chip-blocked { background: #efeae2; border-color: #ddd4c6; color: #6b5d45; }

@media print {
  .topbar-wrap, .dev-bar, .topbar, .tabbar, .footer, .no-print, .billing-actions, .filterbar, .crumb { display: none !important; }
  .tabbar ~ .container { padding-bottom: 0; }
  .print-title { display: block; font-weight: 700; font-size: 1.1rem; margin-bottom: .75rem; }
  .card { box-shadow: none; border: none; padding: 0; margin: 0; }
  body { background: #fff; }
  .bill-group { break-inside: avoid; }
}

@media (max-width: 560px) {
  /* Produktkarte stapeln: nebeneinander bliebe für den Namen zu wenig Platz. */
  .prod-card { flex-direction: column; align-items: stretch; gap: .7rem; }
  .prod-card .stepper { justify-content: flex-end; }
  .grid2 { grid-template-columns: 1fr; }
  /* Zwei Abrechnungs-Karten nebeneinander lassen bei 360px weder Titel noch Summe atmen. */
  .abr-einstieg { grid-template-columns: 1fr; }

  /* Postenliste auf dem Handy: Betrag und Bedienelemente rutschen unter den Text, statt ihn auf
     wenige Zeichen zusammenzuquetschen. Die beiden Bedienelemente kommen auf Fingergröße –
     "Bezahlt" verschiebt den Posten sofort ins Archiv, ein Fehlgriff wäre ärgerlich. */
  .abr-zeile { flex-direction: column; align-items: stretch; gap: .5rem; }
  .abr-zeile-seite {
    flex-direction: row; justify-items: stretch; align-items: center;
    justify-content: space-between; display: flex; gap: .5rem;
    border-top: 1px solid var(--border); padding-top: .5rem;
  }
  .abr-betrag { font-size: 1.05rem; }
  .abr-notiz-knopf, .abr-abrdatum-knopf { width: 44px; height: 44px; font-size: 1.1rem; }
  /* Kein Zeigen mit der Maus auf dem Handy: Die Kurzinfo bliebe nach einem Tipp hängen und
     verdeckte die Nachbarzeile. Stattdessen steht die Notiz wieder in der Zeile selbst.
     Das [hidden] der Vorlage muss weiter gewinnen – sonst zeigt eine Zeile ohne Notiz einen
     leeren gelben Streifen. */
  .abr-notiz-knopf.has-notiz::after { content: none; }
  .abr-notiz:not([hidden]) { display: block; }
  .abr-bezahlt { min-height: 44px; padding: 0 .25rem; }
  .abr-bezahlt input { width: 1.4em; height: 1.4em; }
  /* Zwischen Notiz und Bezahlt mehr Luft als die .4rem der Desktop-Regel: Die beiden tun sehr
     Verschiedenes – das eine öffnet ein Feld, das andere schiebt den Posten ins Archiv. */
  .abr-aktionen { gap: .9rem; }
  /* Such- und Zeitraumfelder ebenfalls auf Fingergröße. 16px Schriftgröße allein genügt nicht:
     Sie verhindert nur den iOS-Zoom, macht die Tippfläche aber nicht größer (gemessen 40px
     bzw. 37px). */
  .abr-suche input, .log-suche input { min-height: 44px; }
  /* Modul Freigaben: Auf dem Handy sind Datum, Beginn, Dauer, Kommentar und die beiden Knöpfe
     die eigentliche Arbeit – gemessen lagen sie bei 29–39px. Auf Fingergröße bringen
     (Richtwert 44px); der Freigeben-Knopf war mit 29px der kleinste von allen. */
  .frg-feld input, .frg-feld select, .frg-kommentar input, .reject-form input { min-height: 44px; }
  .frg-aktionen .btn, .reject-form .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Sperrzeiten: "Bearbeiten" und "Löschen" sind btn-sm und blieben sonst bei 35 bzw. 29 px –
     zu klein zum Treffen, und "Löschen" ausgerechnet der kleinste von beiden. */
  .sperr-aktionen .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Rechnung erfassen: Kundenzeilen und Posten-Häkchen sind die Kernbedienung – Fingergröße. */
  .rg-kunde, .rg-post { min-height: 44px; }
  /* Rechnungsübersicht und -positionen: Zeilen und Knöpfe auf Fingergröße. */
  .re-zeile { min-height: 44px; }
  /* Der Bezahlt-Knopf in der Rechnungsliste – dieselbe Hauptaktion wie zuvor im Erfassen-Ablauf, nur an
     ihrem neuen Ort. */
  .re-zeile .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Umschalter Offen/Archiv bzw. Alle/Zahlung offen: lag bei 40px. Er ist der Wechsel zwischen
     den beiden Sichten einer Liste und wird auf dem Handy genauso angetippt wie alles andere. */
  .abr-tab { min-height: 44px; }
  .re-pos-zeile { flex-direction: column; align-items: stretch; }
  .re-pos-seite {
    flex-direction: row; align-items: center; justify-content: space-between;
    border-top: 1px solid var(--border); padding-top: .5rem;
  }
  .re-pos-seite .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Der Sammel-Knopf über der Liste ("Komplett bezahlt" / "Restliche N …") lag bei 39px –
     er ist die Hauptaktion dieser Seite und gehört auf Fingergröße. */
  .re-pos-sammel .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Datum und Dauer nebeneinander, Beginn daneben – bei 360px passen zwei Felder je Zeile. */
  .frg-feld input { width: 100%; }
  .frg-zeile > .frg-feld { flex: 1 1 8rem; }
  /* Protokollfilter: Bereichs-Chips und Datumsfelder auf volle Tippgröße. Bewusst über
     .log-filter eingegrenzt statt an .chip-link/.filterbar allgemein – dieselben Klassen tragen
     die Bestellübersicht, deren Maße hier nicht mitverändert werden sollen. Die Chips sind im
     Protokoll die Hauptbedienung (33px waren zu knapp), die Datumsfelder lagen bei 42px. */
  .log-filter .chip-link { min-height: 44px; display: inline-flex; align-items: center; }
  .log-filter input[data-datum] { min-height: 44px; }
  /* Drucken/CSV liegen dicht nebeneinander und waren mit 35px zu knapp. */
  .billing-actions .btn { min-height: 44px; display: inline-flex; align-items: center; }
  .abr-zeitraum { width: 100%; }
  .abr-zeitraum label { flex: 1 1 8rem; }
  .abr-zeitraum input { width: 100%; min-height: 44px; }
  /* Die beiden Auswahlfilter über die volle Breite: Gruppen- und Nutzernamen sind länger als
     ein Datum, nebeneinander bliebe von beiden nur ein Wortanfang übrig. */
  .abr-zeitraum label:has(select) { flex: 1 1 100%; }
  .abr-zeitraum select { max-width: none; width: 100%; min-height: 44px; }
  .abr-zeitraum .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Der Monatsfilter der Einstiegsseite folgt denselben Maßen – er ist dieselbe Bedienung an
     anderer Stelle, und 37px wären auf dem Handy zu knapp zum Treffen. */
  .abr-monatsfilter select { max-width: none; width: 100%; min-height: 44px; }
  .abr-monatsfilter label { flex: 1 1 100%; }
  .abr-monatsfilter .btn { min-height: 44px; display: inline-flex; align-items: center; }

  .slot { grid-template-columns: 48px 1fr; }
  .slot-side { grid-column: 2; justify-content: flex-end; }
  /* Auf dem Handy nur leicht einrücken – 1,25rem würden vom knappen Platz zu viel wegnehmen. */
  .slot-sub { margin-left: .5rem; }
  /* Pferdename nur im Hallenplan-Raster ausblenden: bei bis zu 4 Buchungen im selben
     Zeitfenster wird die Zeile auf dem Handy sonst unlesbar. Name und Aktivität bleiben.
     "Meine nächsten Buchungen" und das Profil behalten den Pferdenamen – dort steht je
     Zeile nur eine eigene Buchung. */
  .slotlist .chip-horse { display: none; }
  .act-name { min-width: 120px; }
  /* Zeitraum-Navigation und Umschalter passen nebeneinander nicht auf ein Handy-Display;
     gestapelt sehen sie zentriert besser aus als linksbündig. */
  .plan-head { justify-content: center; }
  .daynav { grid-template-columns: auto 150px auto; gap: .5rem; }
  /* Tages-Pfeile: werden am häufigsten getippt, daher auf Fingergröße (Richtwert 44px). */
  .daynav-arrow { font-size: 1.3rem; padding: .55rem .9rem; min-height: 44px; }

  /* Kein Auto-Zoom auf dem iPhone: jedes Eingabefeld mindestens 16px. Die klassenbasierten
     Felder müssen einzeln genannt werden – ein bloßes "input, select, textarea" wäre zu
     unspezifisch und würde von .filterbar/.list-input/.intnote-field überstimmt.
     Die kompaktere Schrift dort bleibt auf dem Desktop erhalten. */
  input, select, textarea,
  .filterbar input, .filterbar select,
  .list-input, select.list-input, .intnote-field { font-size: 16px; }

  /* Mengen-Stepper und Entfernen-Kästchen auf Fingergröße. Die Einheit bekommt dafür etwas
     weniger Platz, sonst passt die Zeile auf sehr schmalen Geräten (320px) nicht mehr.
     Feste Breite bleibt, damit die Stepper über alle Produktzeilen bündig stehen. */
  .stepper-btn { width: 44px; height: 44px; }

  /* Bestellkarte in der Verwaltung (siehe auch min-width:0 an .order-card weiter oben).
     Auf dem Handy standen Beschriftung und Datumsfeld nebeneinander – zusammen breiter als die
     Karte selbst. Beschriftung darum über das Feld, Feld und Hauptknopf über die ganze Zeile;
     "Ändern" und "Stornieren" bleiben nebeneinander, dafür reicht der Platz. */
  .ord-actions .units { flex: 1 1 100%; display: grid; gap: .25rem; }
  .ord-actions input[data-datum] { width: 100%; }
  .ord-actions button[type="submit"] { flex: 1 1 100%; }
  /* Tippflächen auf Fingergröße (Richtwert 44px) – "Stornieren" ist nicht umkehrbar. */
  .ord-actions .btn {
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  }

  /* Status-Kennzeichen über den Namen statt daneben: nebeneinander konkurrieren beide um dieselbe
     Zeile, und das Kennzeichen wurde rechts abgeschnitten. Oben steht es zugleich beim
     Durchblättern sofort im Blick. */
  .order-head { flex-direction: column-reverse; align-items: flex-start; gap: .35rem; }
  .order-meta { min-width: 0; overflow-wrap: anywhere; }

  /* Lange Produktnamen dürfen umbrechen, statt Menge und Preis aus der Karte zu schieben. */
  .ord-item { flex-wrap: wrap; }
  .stepper input[type="number"] { height: 44px; }
  .stepper-unit { flex-basis: 4rem; }
  .form .edit-remove { padding: .5rem 0; }
  .form .edit-remove input[type="checkbox"] { width: 1.5em; height: 1.5em; }

  /* Das feste Spaltenraster der Aktivitäten-Liste (12+200+100+230px) passt nicht auf ein
     Handy-Display. Unter 560px zurück auf Umbruch statt Ausrichtung – eine bündige Kopfzeile
     bringt auf so wenig Platz ohnehin nichts und würde nur zu horizontalem Scrollen zwingen. */
  /* In der Bearbeiten-Zeile einer Aktivität stehen seit Migration 030 ZWEI Häkchen untereinander
     ("Freigabe nötig" und "gesondert berechnen"). Mit je 28px Höhe und 9px Abstand lagen die
     Ziele auf dem Handy zu dicht beieinander – auf Fingergröße bringen (Richtwert 44px). */
  .list-check { min-height: 44px; }
  .act-row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
  .act-list-head { display: none; }
  /* Auf dem Handy die Pfeile statt des Griffs: HTML5-Drag & Drop löst auf Touch-Geräten keine
     Ereignisse aus, der Griff wäre dort ein totes Zeichen. Die Knöpfe stehen untereinander und
     bekommen zusammen die Höhe einer Zeile; einzeln sind sie kleiner als der 44px-Richtwert,
     was hier vertretbar ist: Sie liegen am Zeilenrand weit weg von allem anderen, und ein
     Fehlgriff verschiebt nur eine Zeile, die man mit dem Gegenpfeil zurückholt. */
  .drag-handle { display: none; }
  .act-move { display: flex; }
  .act-move-btn { width: 30px; height: 26px; font-size: .7rem; }
  /* Seit dem dritten Knopf (Löschen, nur bei Aktivitäten ohne Buchungen) passen die Aktionen bei
     360px nicht mehr in eine Zeile – gemessen 364px Gesamtbreite, also seitliches Scrollen für
     die ganze Seite. flex-wrap lässt sie stattdessen umbrechen; margin-left:auto bleibt, damit
     sie bei genug Platz weiterhin rechts stehen. min-width:0 am Container, sonst verhindert die
     Vorgabe min-width:auto den Umbruch und die Zeile wächst wieder über den Rand hinaus. */
  .act-row-actions { justify-self: unset; margin-left: auto; flex-wrap: wrap; min-width: 0; }
  /* Die Knöpfe der Zeile lagen bei 29–35px; bei drei Zielen nebeneinander – eines davon
     löschend – auf Fingergröße bringen (Richtwert 44px). */
  .act-row-actions .btn { min-height: 44px; display: inline-flex; align-items: center; }

  /* Arbeitsplan auf dem Handy: geplant wird über die TAGESANSICHT – ein 7-Spalten-Raster mit
     Häkchen lässt sich mit dem Finger nicht bedienen. Dort deshalb Auswahlzeilen auf Fingergröße
     (Richtwert 44px). Das Wochenraster bleibt schmal und dient unterwegs als Übersicht; es rutscht
     in seinem eigenen Bereich seitlich, ohne die Seite zu verbreitern. */
  /* Untereinander steht schon in der Grundregel – hier nur die Tippflächen vergrößern. */
  .wp-day-people { gap: 0; }
  .wp-day-people .wp-pick { min-height: 44px; font-size: 1rem; gap: .6rem; }
  .wp-day-people .wp-pick input { width: 1.4em; height: 1.4em; }
  .wp-day-detail label { flex: 1 1 8rem; }

  /* Blätterknöpfe im Protokoll auf Fingergröße. .btn-sm ist bewusst nur hier vergrößert und nicht
     global, damit die kompakten Knöpfe der Verwaltungslisten unverändert bleiben. */
  .pager .btn { min-height: 44px; display: inline-flex; align-items: center; }
  /* Reihenfolge auf schmalen Anzeigen: erst die beiden Knöpfe nebeneinander, die Zählung darunter.
     Nebeneinander bliebe für "Seite 1 von 12 · 178 Einträge" sonst zu wenig Platz und die Zeile
     würde ungleichmäßig umbrechen. */
  .pager { justify-content: space-between; }
  .pager .small { order: 3; flex-basis: 100%; text-align: center; }

  /* Knöpfe unter einem Formular auf Fingergröße (Richtwert 44px). Betrifft vor allem die
     Bestätigungsseiten mit unumkehrbaren Aktionen (Buchung stornieren, Bestellung stornieren).
     Nebeneffekt: als <button> und als <a class="btn"> gesetzte Knöpfe waren dort unterschiedlich
     hoch (39 gegen 45px) – die feste Mindesthöhe gleicht das mit an. */
  .form-actions .btn { min-height: 44px; display: inline-flex; align-items: center; }

  /* "Rollen speichern" und "Sperren" stehen jetzt nebeneinander in einer Zeile – auf dem Handy
     auf Fingergröße bringen (Richtwert 44px). Bewusst nur hier und nicht global für .btn-sm,
     damit die kompakten Knöpfe auf anderen Seiten unverändert bleiben. */
  .person-actions .btn { min-height: 44px; }

  /* Rollen- und Sortier-Chips auf "Nutzer & Rollen" ebenfalls auf Fingergröße: Sie liegen dicht
     nebeneinander, und mit 34px trifft man auf dem Handy leicht den Nachbarn. .chip-btn kommt
     ausschließlich auf dieser Seite vor, die Regel wirkt sich also nirgends sonst aus. */
  .chip-btn { min-height: 44px; display: inline-flex; align-items: center; }

  /* Die beiden Aktionen an einer Buchung im Hallenplan (€ berechnen, ✕ stornieren) stehen direkt
     nebeneinander. Bei 22px läge zwischen zwei Zielen weniger als ein Fingerbreit – und das
     rechte storniert. Deshalb auf dem Handy auf Fingergröße.
     KEIN Umbruch in eine eigene Zeile: .chip ist eine Pille (border-radius 999px) ohne
     flex-wrap; ein flex-basis:100% bricht dort nicht um, sondern bläht den Chip auf, bis die
     Tagesansicht seitlich überläuft (bei 360px auf 430px gemessen). Stattdessen behalten die
     Knöpfe ihren Platz (flex: none) und der Aktivitätstext weicht per Ellipse – .chip hat
     max-width:100%, der Chip bleibt damit innerhalb der Spalte. */
  /* Auf dem Handy wird der Chip zur kleinen Karte statt einer Pille: Zwei 44px-Knöpfe passen
     neben Name, Aktivität und Betrag nicht in eine Zeile (gemessen: der Chip hätte 202px
     gebraucht, verfügbar waren 194px, die Knöpfe standen dann außerhalb).
     ENTSCHEIDEND ist flex-wrap – ohne das bricht flex-basis:100% nicht um, sondern verlangt
     nur die volle Breite und reißt die Tagesansicht seitlich auf (bei 360px auf 430px
     gemessen). Die Pillenform (border-radius 999px) passt für zwei Zeilen nicht mehr. */
  .slotlist .chip { flex-wrap: wrap; border-radius: 12px; }
  /* Nur Chips MIT Aktionen auf volle Breite: .slot-body richtet seine Kinder linksbündig aus
     (align-items: flex-start), eine zweizeilige Karte stünde sonst als schmaler hoher Block da
     (gemessen 194x110px bei 360px Platz). Chips ohne Knöpfe – also alles, was Mitglieder ohne
     Verwaltungsrecht sehen – bleiben unverändert schmal. */
  .slotlist .chip:has(.chip-actions) { align-self: stretch; }
  .chip-actions { flex-basis: 100%; display: flex; justify-content: flex-end; gap: .5rem; margin-top: .2rem; }
  .chip-actions .chip-cancel,
  .chip-actions .chip-kosten-link,
  .chip-actions .chip-edit-link { min-width: 44px; height: 44px; margin-left: 0; font-size: 1rem; }
  .slotlist .chip-kosten { flex: none; }
  /* Damit die breiteren Knöpfe die Zeile nicht seitlich aufreißen, muss der Text weichen dürfen.
     Dafür sind ZWEI Stellen nötig, beide wegen min-width:auto – dem Vorgabewert, mit dem ein
     Grid-/Flex-Item nicht unter seine Inhaltsbreite schrumpft (dasselbe Muster hat im
     Bestellmodul die .order-card seitlich überlaufen lassen):
       1. .slot-body sitzt in der 1fr-Spalte von .slot und würde sonst mitwachsen,
       2. Name und Aktivität im Chip selbst, die zusätzlich eine Ellipse bekommen.
     Ohne (1) bleibt (2) wirkungslos: Wenn die Spalte mitwächst, gibt es nie zu wenig Platz. */
  .slot-body { min-width: 0; }
  .slotlist .chip-name,
  .slotlist .chip-act { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* Anmelde-Seite soll auch auf kleinen Handys (360x640) ohne Scrollen auf eine Bildschirmhöhe
     passen. Gekürzt wird ausschließlich der dekorative Kopfbereich und der Leerraum ringsum –
     Eingabefelder (16px) und Tippflächen (44px) bleiben unverändert.
     Die Anmeldeseite wird über :has(.login-intro) erkannt, weil sie als einzige Seite ohne
     Kopfleiste auskommt und dadurch keine eigene Body-Klasse hat. */
  .login-intro { padding-top: .75rem; }
  .login-intro-icon { width: 150px; margin-bottom: .75rem; }
  .container:has(.login-intro) { padding-bottom: 1rem; }
  .container:has(.login-intro) .card { padding: 1.25rem; margin-bottom: 0; }
  .container:has(.login-intro) .card h1 { margin-bottom: .25rem; }
  .container:has(.login-intro) .form { margin-top: .75rem; gap: .85rem; }
  /* Der gewonnene Platz geht zum Teil zurück an den Anmelden-Knopf: 40px lagen unter der
     44px-Vorgabe für Tippflächen. */
  .container:has(.login-intro) .btn-primary { min-height: 44px; }
}

/* Zweite Stufe nur für sehr kurze Displays (z. B. iPhone SE 1. Gen mit 320x568): dort reicht
   selbst der gekürzte Kopfbereich oben nicht. Bewusst an die HÖHE gekoppelt, damit normale
   Handys (640px+) die luftigere Darstellung behalten und das Icon nicht unnötig schrumpft. */
@media (max-width: 560px) and (max-height: 600px) {
  .login-intro { padding-top: .25rem; }
  .login-intro-icon { width: 118px; margin-bottom: .5rem; }
  .container:has(.login-intro) .form { gap: .7rem; }
}

/* Kurspreise: der gemeinsame MwSt-Satz. Schmal gehalten, weil zwei Ziffern hineingehören –
   ein Feld über die volle Breite verspräche eine Eingabe, die es nicht gibt. */
.mwst-feld { max-width: 16rem; }

/* Sperrzeiten des gewählten Tages im Buchungsformular. Kein farbiger Kasten: Das ist eine
   Auskunft über den Tag, keine Fehlermeldung – gewarnt wird erst, wenn die gewählte Zeit
   wirklich hineinfällt (dann steht dort ein .flash-error). */
.sperr-hinweis { margin-top: -.35rem; }

/* Kursschluss auf der Kurspreise-Seite. Wie das MwSt-Feld daneben schmal gehalten – eine
   Uhrzeit braucht keine Feldbreite über die halbe Seite. */
.kursende-feld { max-width: 16rem; }

/* Kartenkopf mit Titel und einer Aktion daneben (z. B. "Nächste Buchungen" + Archiv-Link).
   Der Titel behält seinen eigenen Abstand nach unten nicht – den trägt hier der Kopf. */
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; flex-wrap: wrap; margin-bottom: .75rem; }
.card-head .card-title { margin-bottom: 0; }
/* Wer gebucht hat. Ohne eigene Farbe: Der Name ist die Auskunft, nach der in dieser Liste
   gesucht wird, und darf nicht wie eine Nebenangabe aussehen. */
.booking-who { font-weight: 500; }

/* Uhrzeit-Felder in den Einstellungen: schmal gehalten – eine Uhrzeit braucht keine
   Feldbreite über die halbe Seite. */
.reminder-feld { max-width: 16rem; }

/* Überschrift über einer Auswahlgruppe in Formularen – wie ein Feldname, nur für eine Gruppe
   statt für ein einzelnes Feld. */
.form .feld-titel { margin: 1rem 0 .4rem; font-weight: 600; }

/* Monatsfilter über den Abrechnungs-Kacheln. Bewusst dieselbe Bauart wie die Zeitraumzeile in
   den Bereichslisten (.abr-zeitraum) – es ist derselbe Vorgang an anderer Stelle, und die
   Feldgestaltung von dort bringt nebenbei die Tap-Größe mit, die eine eigene Regel hier erst
   wieder herstellen müsste. */
.abr-monatsfilter { display: flex; flex-wrap: wrap; gap: .5rem; align-items: flex-end; margin: 1rem 0 .2rem; }
.abr-monatsfilter label { display: grid; gap: .2rem; font-size: .82rem; font-weight: 600; color: var(--muted); }
.abr-monatsfilter select {
  max-width: 12rem; padding: .5rem .6rem; border: 1px solid var(--border);
  border-radius: 10px; font-size: 1rem; background: #fff;
}

/* --- Aufgaben: Datei-Anhänge (Migration 052) --------------------------------------------------
   .datei-upload: die beiden Knöpfe + die Vorschauliste im Formular (partials/aufgaben-dateiwahl).
   .anhang-liste: die bereits hochgeladenen Dateien an Aufgabe/Kommentar (partials/aufgaben-anhaenge). */
/* .sr-only allein verliert gegen ".form input" (Spezifität 0,1,1 schlägt 0,1,0) – die beiden
   Datei-Felder hier liegen in einem <form class="form">, das JEDEM input width:100% mitgibt.
   Ohne diese Zeile wären sie zwar unsichtbar (overflow:hidden greift weiterhin), aber ihre Box
   spannte trotzdem die volle Formularbreite auf und erzeugte horizontalen Überlauf – auf dem
   Handy sichtbar an einer Seite, die sich seitlich schieben ließ. Gleiches Fallstrick-Muster wie
   ".form label.check" (siehe README): eine spezifischere Regel statt !important. */
.form input.sr-only { width: 1px; padding: 0; border: 0; }
.datei-upload { margin: .6rem 0; }
.datei-upload-knoepfe { display: flex; gap: .5rem; flex-wrap: wrap; }
/* min-height statt nur padding: Die <label>-Knöpfe stehen für ein <input type="file"> – die
   Tippfläche muss trotzdem die im Projekt übliche Mindestgröße erreichen (siehe CLAUDE.md). */
.datei-upload-knoepfe label { min-height: 44px; display: inline-flex; align-items: center; }
.datei-upload-liste { list-style: none; padding: 0; margin: .5rem 0 0; display: flex; flex-direction: column; gap: .3rem; }
.datei-upload-eintrag {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .4rem .6rem; border: 1px solid var(--border); border-radius: 8px; background: #fff;
}
.datei-upload-name { font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.anhang-liste { list-style: none; padding: 0; margin: .5rem 0; display: flex; flex-wrap: wrap; gap: .6rem; }
.anhang-karte { display: flex; flex-direction: column; align-items: center; gap: .25rem; width: 96px; }
.anhang-karte img {
  width: 96px; height: 96px; object-fit: cover; border-radius: 8px; cursor: zoom-in;
  border: 1px solid var(--border); background: #f0efe9;
}
.anhang-pdf {
  width: 96px; height: 96px; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: .3rem; border: 1px solid var(--border); border-radius: 8px;
  background: #f0efe9; text-decoration: none; padding: .3rem;
}
.anhang-pdf-ico {
  font-size: .7rem; font-weight: 700; letter-spacing: .04em; color: var(--danger);
  border: 1.5px solid var(--danger); border-radius: 4px; padding: .1rem .3rem;
}
.anhang-name { font-size: .72rem; color: var(--ink); text-align: center; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 88px; }
.anhang-groesse { font-size: .7rem; }

/* Die drei Aktions-Reihen einer Aufgabe (Datei anhängen / Erledigt+Bearbeiten / Verschieben+
   löschen) mit einheitlichem, knapperem Abstand statt je einer eigenen .mt-Klasse (1.5rem je
   Block – gestapelt wirkte das für drei Zeilen zur selben Aufgabe unnötig weitläufig). */
/* Fasst Status, Beschreibung und direkte Anhänge der Aufgabe in einem eigenen Kasten zusammen –
   bewusst NICHT dieselbe Tönung wie .kommentar weiter unten (var(--bg), voll gerundet), sondern
   ein grüner Akzentrand auf weißem Grund, wie schon bei .log-row/.wp-diff-row im Projekt. Beide
   Blöcke sollen auf einen Blick als unterschiedliche Dinge erkennbar sein – "das ist die
   Aufgabe" gegen "das ist ein Beitrag dazu" –, nicht nur zwei gleich aussehende Kästen. */
.aufgabe-info {
  margin: .75rem 0 1rem; padding: .85rem 1rem 1rem;
  background: #fff; border: 1px solid var(--border); border-left: 3px solid var(--green);
  border-radius: 0 10px 10px 0;
}
.aufgabe-info > p:first-child { margin-top: 0; }
.aufgabe-info > *:last-child { margin-bottom: 0; }

/* Adresse zum Kopieren (Verwaltung → Einstellungen, "Aufgaben per E-Mail"). Gestrichelter Rand
   statt durchgezogen: signalisiert "zum Anfassen", ohne wie ein Formularfeld auszusehen – hier
   gibt es nichts einzutippen, nur zu übernehmen. Grün bei Hover/Fokus wie überall im Projekt,
   .copied (kurzzeitig nach dem Klick, siehe app.js) bleibt in derselben Farbe stehen. */
.copy-adresse {
  cursor: pointer; border: 1px dashed var(--border); border-radius: 6px;
  padding: .05rem .4rem; background: var(--bg);
}
.copy-adresse:hover, .copy-adresse:focus-visible {
  border-color: var(--green); border-style: solid; color: var(--green-dark);
}
.copy-adresse.copied { border-color: var(--green); border-style: solid; color: var(--green-dark); }

.aufgabe-aktionen { display: flex; flex-direction: column; gap: .6rem; margin: 1rem 0; }
.aufgabe-aktionen > .billing-actions { margin: 0; }
.aufgabe-aktionen > .admin-collapse { margin-top: 0; }

/* Trennt "die Aufgabe" (Titel, Beschreibung, Aktionen) sichtbar von den Kommentaren darunter.
   Ohne die Linie blieb nur der übliche Abstand von .mt (wie bei jeder anderen Zwischenüberschrift
   im Projekt) – auf dieser Seite, mit Aktionen UND einer möglicherweise langen Kommentarliste,
   las sich das als ein durchgehender Fluss statt als zwei erkennbar getrennte Blöcke. */
.aufgabe-kommentare-titel { padding-top: 1.1rem; border-top: 1px solid var(--border); }

/* Kommentare einer Aufgabe: eigene Klassen statt der Rechnungszeilen-Optik (.re-pos-*, siehe
   aufgaben-detail.ejs für die Begründung). Größerer Abstand zwischen den Einträgen als bei den
   dichten Rechnungszeilen – Kommentare sind ein Gespräch im Zeitverlauf, keine Postenliste, und
   sollen sich deutlicher voneinander abheben. */
.kommentar-liste { list-style: none; margin: .9rem 0 0; padding: 0; display: grid; gap: .9rem; }
/* Hintergrund bewusst NICHT weiß (wie die umgebende .card): Ein weißer Kasten auf weißem Grund
   ist nur an einer 1px-Linie erkennbar, die bei --border (sehr helles Beige) kaum auffällt. Der
   leichte Grundton (dieselbe Farbe wie die Seite hinter der Karte) lässt jeden Kommentar als
   eigenen, eingelassenen Block erscheinen statt als bloß umrandeten Text. */
.kommentar {
  padding: .75rem .9rem; border: 1px solid var(--border); border-radius: 10px; background: var(--bg);
}
.kommentar-kopf { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .25rem; }
.kommentar-text { white-space: pre-wrap; color: var(--muted); font-size: .88rem; overflow-wrap: anywhere; }

/* Der "Bearbeiten"-Umschalter je Kommentar: bewusst weiterhin <summary> (funktioniert ohne
   JavaScript), aber inline-flex statt der vollen .admin-collapse-Kastenbreite – dadurch immer
   genau so breit wie sein Inhalt ("Bearbeiten"), nicht so breit wie der jeweils umgebende
   Kommentar. min-height bleibt bei 44px (Tippfläche), nur die Breite schrumpft und bleibt fix. */
.kommentar-bearbeiten { margin-top: .5rem; }
.kommentar-bearbeiten > summary {
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: .3rem;
  min-width: 6.5rem; min-height: 44px; padding: .3rem .6rem;
  font-size: .85rem; font-weight: 600; color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px; background: #fff; list-style: none;
}
.kommentar-bearbeiten > summary::-webkit-details-marker { display: none; }
.kommentar-bearbeiten > summary::before { content: '▸'; color: var(--green); font-size: .8rem; }
.kommentar-bearbeiten[open] > summary::before { content: '▾'; }
.kommentar-bearbeiten > summary:hover { background: #f7f6f2; }
.kommentar-bearbeiten[open] > summary { border-color: var(--green); color: var(--green-dark); }
.kommentar-bearbeiten-body { margin-top: .5rem; }
