/* ---------------------------------------------------------------------------
   Komponenten der Weboberflaeche. Alle Werte kommen aus tokens.css.
   --------------------------------------------------------------------------- */

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--color-background-primary);
  color: var(--color-text-primary);
  font: var(--typography-body);
}

a { color: var(--color-brand-primary-action); }

:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: 6px;
}

[hidden] { display: none !important; }

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

.skip-link {
  position: absolute;
  left: var(--spacing-lg);
  top: -60px;
  z-index: 30;
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  padding: var(--spacing-sm) var(--spacing-lg);
  font: var(--typography-action);
  text-decoration: none;
  transition: top var(--motion-quick);
}
.skip-link:focus { top: var(--spacing-lg); }

svg.i { width: var(--sizing-icon-compact); height: var(--sizing-icon-compact); flex: none; }
svg.i.lg { width: 21px; height: 21px; }
/* Der goldene Stern -- und derselbe ohne Farbe, fuer die Note, die es noch
   nicht gibt. Sie stehen hier und nicht in `discover.css`: Eine Note steht im
   Entdecken, auf der Veranstaltungsseite, am Veranstalter und im eigenen
   Konto, und ein Stern, dessen Farbe an einem Blatt haengt, ist auf den
   Seiten ohne dieses Blatt schlicht weiss. */
svg.i.gold { color: var(--color-rating-star); width: 14px; height: 14px; }
svg.i.muted { color: var(--color-text-tertiary); width: 14px; height: 14px; }

/* ------------------------------------------------------------------ Kopfzeile */
.appbar {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
  padding: var(--spacing-sm) var(--spacing-xl);
  background: var(--color-surface-primary);
  border-bottom: 1px solid var(--color-border-subtle);
  position: sticky;
  top: 0;
  z-index: 20;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  font: var(--typography-hero-title);
  text-decoration: none;
  color: inherit;
  flex: none;
}
.brand .mark {
  width: 30px; height: 30px;
  border-radius: 10px;
  background: linear-gradient(140deg, var(--color-brand-signature), var(--color-brand-primary-action));
  color: var(--color-on-brand-primary-action);
  display: grid;
  place-items: center;
  font-size: .9rem;
  font-weight: 800;
  box-shadow: var(--elevation-card);
}

.mainnav { display: flex; gap: var(--spacing-xs); margin: 0 auto; }
.mainnav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 7px;
  min-height: var(--layout-touch-target);
  padding: 0 var(--spacing-md);
  border-radius: var(--radius-control);
  text-decoration: none;
  color: var(--color-text-secondary);
  font: var(--typography-action);
  font-weight: 500;
  transition: background var(--motion-quick), color var(--motion-quick);
}
.mainnav a:hover { background: var(--color-surface-secondary); color: var(--color-text-primary); }
.mainnav a[aria-current="page"] { color: var(--color-brand-primary-action); font-weight: 600; }
.mainnav a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: var(--spacing-md); right: var(--spacing-md);
  bottom: -9px;
  height: 2px;
  border-radius: 2px;
  background: var(--color-brand-primary-action);
}
.mainnav a.cta {
  background: var(--color-brand-primary-action);
  color: var(--color-on-brand-primary-action);
  font-weight: 600;
  padding: 0 var(--spacing-lg);
}
.mainnav a.cta:hover {
  background: var(--color-brand-primary-action-pressed);
  color: var(--color-on-brand-primary-action);
}
.mainnav a.cta::after { content: none; }
.mainnav a[aria-disabled="true"] { opacity: .55; cursor: default; }
.mainnav .badge {
  position: absolute;
  top: 4px; right: 2px;
  background: var(--color-brand-primary-action);
  color: var(--color-on-brand-primary-action);
  font-size: .62rem;
  font-weight: 700;
  border-radius: 999px;
  min-width: 17px;
  padding: 1px 5px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  border: 2px solid var(--color-surface-primary);
}

/* `flex: none` hiess: niemals schrumpfen -- und damit schob ein langer
   Kontoname die Leiste ueber den Fensterrand. Sie darf schrumpfen, aber
   nicht wachsen; gekuerzt wird dann der Name, nicht die Zeichen daneben. */
.appbar-tools { display: flex; align-items: center; gap: var(--spacing-sm); flex: 0 1 auto; }
.iconbtn {
  width: var(--layout-touch-target);
  height: var(--layout-touch-target);
  border-radius: 50%;
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-secondary);
  display: grid;
  place-items: center;
  position: relative;
  cursor: pointer;
}
.iconbtn:hover { background: var(--color-surface-secondary); }
.iconbtn .dot {
  position: absolute; top: 10px; right: 11px;
  width: var(--sizing-status-indicator); height: var(--sizing-status-indicator);
  border-radius: 50%;
  background: var(--color-brand-signature);
  border: 2px solid var(--color-surface-primary);
}

.account {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  min-height: var(--layout-touch-target);
  padding: 0 var(--spacing-sm);
  border: 1px solid transparent;
  border-radius: 999px;
  background: transparent;
  font: var(--typography-action);
  color: var(--color-text-primary);
  cursor: pointer;
}
.account:hover { background: var(--color-surface-secondary); }
/* Ein langer Kontoname darf die Leiste nicht aufdruecken.

   Nachgemessen schob „Nachbarschaftshaus Leipzig" die Kopfleiste bei 1026
   Pixeln zwoelf Pixel ueber den Rand -- und damit war jede Seite seitwaerts
   schiebbar, nicht nur die mit langem Inhalt. Flexkinder schrumpfen nicht
   unter ihren Inhalt, solange `min-width: auto` gilt; der Name kuerzt jetzt
   mit Auslassungspunkten. */
.appbar-tools, .account-wrap { min-width: 0; }
.account { min-width: 0; max-width: 100%; }
.account span.name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Das Konto-Menue ist der Ort, an dem man Abmelden sucht. Es lag vorher als
   Knopf am Fuss der rechten Spalte -- gefunden hat ihn dort niemand. */
/* Die Sprachwahl neben der Darstellung: ein Kuerzel am Globus, damit man
   sieht, was gilt, ohne das Menue zu oeffnen. */
.sprachwahl {
  position: relative;
  /* Eine kurze Linie davor: Sie trennt, was man einstellt, von dem, was man
     ist -- ohne einen Strich ueber die ganze Hoehe zu ziehen. */
  padding-left: var(--spacing-md);
  margin-left: 4px;
  border-left: 1px solid var(--color-border-subtle);
}
/* Ein Symbolknopf ist ein Raster mit einer Spalte -- fuer ein Zeichen richtig,
   fuer zwei nebeneinander nicht: Flagge und Kuerzel standen uebereinander.
   Hier ist es eine Zeile: erst die Flagge, dann das Kuerzel. */
.sprachwahl .iconbtn {
  display: flex;
  align-items: center;
  gap: 6px;
  width: auto;
  padding: 0 10px;
  border-radius: var(--radius-control);
  flex: none;
  white-space: nowrap;
}
.sprachwahl .kuerzel {
  font: var(--typography-caption);
  font-weight: 700;
  letter-spacing: .02em;
}
.sprachwahl .menu { width: 220px; }
.sprachwahl .menu button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
}
.sprachwahl .menu button .wort { flex: 1; min-width: 0; }
.sprachwahl .menu button .kuerzel { color: var(--color-text-tertiary); }
/* In der Liste steht die Flagge in fester Breite, damit die Namen daneben eine
   Spalte bilden -- und etwas groesser: Als Textzeichen wirkt sie sonst kleiner
   als der Name. */
.sprachwahl .menu .flagge {
  font-size: 1.15em;
  line-height: 1;
  width: 1.4em;
  text-align: center;
}
/* Am Knopf dagegen nimmt sie die Breite, die sie braucht. Mit fester Breite
   ragte das Flaggenzeichen darueber hinaus und das Kuerzel begann mitten in
   ihr: „🇩🇪" und „DE" lagen uebereinander statt nebeneinander. */
.sprachwahl > .iconbtn > .flagge { font-size: 1.05em; line-height: 1; }
/* Und dieselbe Flagge in der Wahl im Profil. */
.language-choice .filter-option .flagge {
  font-size: 1.15em;
  line-height: 1;
  width: 1.4em;
  text-align: center;
}
.sprachwahl .menu button[aria-current="true"] {
  color: var(--color-brand-primary-action);
  font-weight: 600;
}

.account-wrap { position: relative; }
.menu {
  position: absolute;
  top: calc(100% + var(--spacing-sm));
  right: 0;
  width: 286px;
  max-height: min(78dvh, 640px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-overlay);
  padding: var(--spacing-sm);
  z-index: 40;
}
.menu-head {
  margin: 0 0 var(--spacing-xs);
  padding: var(--spacing-sm) var(--spacing-md);
  font: var(--typography-caption);
  color: var(--color-text-tertiary);
  overflow-wrap: anywhere;
  border-bottom: 1px solid var(--color-border-subtle);
}
.menu-label {
  margin: var(--spacing-md) 0 var(--spacing-xs);
  padding: 0 var(--spacing-md);
  font: var(--typography-caption);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}
.menu a,
.menu button {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  width: 100%;
  min-height: var(--layout-touch-target);
  padding: 0 var(--spacing-md);
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  text-align: left;
  text-decoration: none;
  color: var(--color-text-primary);
  font: var(--typography-action);
  font-weight: 500;
  cursor: pointer;
}
.menu a:hover,
.menu button:hover { background: var(--color-surface-secondary); }
.menu a[aria-current="page"] {
  background: var(--color-selection-background);
  color: var(--color-selection-foreground);
  font-weight: 600;
}
.menu a[aria-disabled="true"] { color: var(--color-disabled-foreground); opacity: .6; cursor: default; }
.menu a[aria-disabled="true"]:hover { background: transparent; }
.menu button.danger { color: var(--color-status-error); }
.menu button.danger:hover { background: var(--color-status-error-surface); }
.menu hr { border: 0; border-top: 1px solid var(--color-border-subtle); margin: var(--spacing-sm) var(--spacing-md); }

.avatar {
  border-radius: 50%;
  background: linear-gradient(150deg, var(--color-brand-signature), var(--color-category-community));
  color: var(--color-on-brand-primary-action);
  display: grid;
  place-items: center;
  font-weight: 700;
  flex: none;
  width: 34px; height: 34px;
  font-size: .78rem;
  letter-spacing: .02em;
}
.avatar.xl { width: 104px; height: 104px; font-size: 2rem; }
/* Wo ein Bild gesetzt ist, deckt es den Verlauf ab -- ohne dass die Kachel
   ihre Form verliert. */
.avatar { background-size: cover; background-position: center; }

/* ---------------------------------------------------------------------- Rahmen
   Eine Spalte. Die Bereichsauswahl liegt im Konto-Menü rechts oben; links
   steht keine zweite Navigation daneben. */
.main { max-width: 1240px; margin: 0 auto; padding: var(--spacing-xl); min-width: 0; }
.cols { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--spacing-xl); align-items: start; }
/* Ohne die Schiene rechts gehoert die Breite dem Inhalt -- sonst bliebe die
   Spalte, in der sie stand, als Luecke stehen. */
.cols.ohne-schiene { grid-template-columns: minmax(0, 1fr); }

/* Eine Wahl auf ihrer Zeile: Knopf, Wort -- und in der Markenfarbe, sobald sie
   gilt. Sie steht hier und nicht in einem Blatt, weil zwei Blaetter sie
   brauchen: das Entdecken und die Seite eines Veranstalters. Dieselbe Frage
   soll ueberall gleich aussehen. */
.filter-wahl {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: 5px 0;
  cursor: pointer;
  color: var(--color-text-secondary);
}
.filter-wahl.an { color: var(--color-brand-primary-action); font-weight: 600; }
.filter-wahl input {
  accent-color: var(--color-brand-primary-action);
  width: 16px;
  height: 16px;
}

/* Platzhalter statt Profilbilder: Wer teilnimmt, hat nicht eingewilligt, im
   Entdecken-Tab Fremden gezeigt zu werden. Der letzte Kreis trägt den Rest. */
.people { display: inline-flex; align-items: center; gap: var(--spacing-sm); }
/* Die Kreise stehen in einer Reihe und mittig zueinander. Ohne feste
   Ausrichtung fiel der "+N"-Kreis um eine halbe Zeile nach unten: Er trägt
   Text, die anderen ein Zeichen -- und der Text brachte eine Grundlinie mit. */
.heads { display: inline-flex; align-items: center; flex: none; }
.head {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  /* Ein Bild, wenn es eines gibt -- sonst ein grauer Grund mit dem Zeichen.
     Grau und nicht in der Markenfarbe: Ein Platzhalter fuer einen Menschen ist
     kein Hinweis der Plattform. Beides sitzt im selben Kreis, damit die Reihe
     nicht springt. */
  background: var(--color-border-subtle) center / cover no-repeat;
  border: 2px solid var(--color-surface-primary);
  color: var(--color-text-tertiary);
  display: grid;
  place-items: center;
  margin-left: -11px;
  flex: none;
  line-height: 1;
  overflow: hidden;
}
.head.photo { background-color: var(--color-surface-secondary); }
.head.photo svg.i { display: none; }
/* Wer keinen Kopf mitbringt, aber einen Namen: die Anfangsbuchstaben. So
   unterscheidet sich eine Person ohne Foto von einem Platzhalter fuer jemanden,
   den man nicht sehen darf. */
.head.letters {
  background: var(--color-selection-background);
  color: var(--color-selection-foreground);
  font: var(--typography-caption);
  font-weight: 700;
  font-size: .72rem;
  letter-spacing: .02em;
}
.head:first-child { margin-left: 0; }
/* Das Zeichen darf den Kreis fuellen -- klein wirkte es wie ein Fleck. */
.head svg.i { width: 17px; height: 17px; color: var(--color-text-secondary); }
/* Nicht ".more": Die Klasse gehoert dem "Weitere laden"-Knopf und brachte
   ihren Abstand nach oben mit -- der Kreis fiel dadurch aus der Reihe. */
/* "+9" passt in einen Kreis, "+11" nicht: Bei 22 Punkt Aussenmass bleiben
   nach dem Rand 18 Punkt Inhalt, und drei Zeichen brauchen knapp 21. Statt die
   Schrift weiter zu verkleinern -- sie steht schon bei .64rem -- darf der
   Kreis in die Breite gehen und wird zur Kapsel. Bis zwei Zeichen bleibt er
   rund, weil "min-width" und "height" dann gleich sind. */
.head.rest {
  background: var(--color-border-subtle);
  color: var(--color-text-secondary);
  font: var(--typography-caption);
  font-weight: 700;
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  width: auto;
  min-width: 32px;
  height: 32px;
  padding: 0 4px;
  border-radius: 999px;
}

/* -------------------------------------------------------------------- Bausteine */
.panel {
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-card);
  overflow: hidden;
}
.panel > h2,
.panel > header {
  font: var(--typography-section-title);
  margin: 0;
  padding: var(--spacing-lg);
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}
.panel > header .hint,
.panel > h2 .hint {
  margin-left: auto;
  font: var(--typography-caption);
  color: var(--color-text-tertiary);
  font-weight: 400;
}

.btn {
  border: 1px solid var(--color-border-subtle);
  background: var(--color-surface-primary);
  color: var(--color-text-primary);
  border-radius: 999px;
  min-height: var(--layout-touch-target);
  padding: 0 var(--spacing-lg);
  font: var(--typography-action);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  cursor: pointer;
  /* Als Verweis gebaut, als Knopf gemeint: Ein `<a class="btn">` erbt sonst
     die Unterstreichung der Verweise und sieht aus wie ein Wort im
     Fliesstext, das versehentlich in einer Pille steht. */
  text-decoration: none;
  transition: background var(--motion-quick), border-color var(--motion-quick);
}
.btn:hover:not([disabled]) { background: var(--color-surface-secondary); }
.btn.primary {
  background: var(--color-brand-primary-action);
  border-color: var(--color-brand-primary-action);
  color: var(--color-on-brand-primary-action);
}
.btn.primary:hover:not([disabled]) {
  background: var(--color-brand-primary-action-pressed);
  border-color: var(--color-brand-primary-action-pressed);
}
.btn.square { border-radius: var(--radius-control); }
.btn.quiet { border-color: transparent; color: var(--color-text-secondary); }
.btn.danger { border-color: var(--color-status-error); color: var(--color-status-error); }
.btn.danger:hover:not([disabled]) { background: var(--color-status-error-surface); }
.btn.block { width: 100%; }
.btn[disabled] {
  background: var(--color-disabled-background);
  border-color: var(--color-disabled-background);
  color: var(--color-disabled-foreground);
  cursor: not-allowed;
}
.btn .spin {
  width: 15px; height: 15px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.notice {
  display: flex;
  gap: var(--spacing-sm);
  align-items: flex-start;
  border-radius: var(--radius-control);
  padding: var(--spacing-md) var(--spacing-lg);
  font: var(--typography-metadata);
  margin-bottom: var(--spacing-lg);
}
.notice svg.i { margin-top: 2px; }

/* ---------------------------------------------------------------- Die Glocke

   Sie haengt an der Leiste und nicht an einer Seite: Eine Meldung kommt nicht
   dann, wenn man auf der richtigen Seite steht. Sie traegt ihre Zahl in der
   Ecke und muss sich dafuer festhalten koennen -- mehr steht nicht mehr an
   ihr, denn das Blatt, das hier aufklappte, ist seit dem 9. September 2026
   eine eigene Seite. */
.glocke { position: relative; text-decoration: none; }
.glocken-zahl {
  position: absolute;
  top: 2px; right: 2px;
  min-width: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--color-brand-primary-action);
  color: var(--color-on-brand-primary-action);
  font: var(--typography-caption);
  line-height: 16px;
  text-align: center;
}
.notice.error { background: var(--color-status-error-surface); color: var(--color-status-error); }
/* Die Warnung faerbt ihr Zeichen und ihre Ueberschrift, nicht jeden Satz
   darin. Durchgaengig eingefaerbter Text liest sich als Alarm; gemeint ist
   „ueberleg dir das". Der Fliesstext bleibt deshalb Text. */
.notice.warning { background: var(--color-status-warning-surface); color: var(--color-text-primary); }
.notice.warning > svg.i,
.notice.warning b { color: var(--color-status-warning); }
.notice.info { background: var(--color-status-info-surface); color: var(--color-status-info); }
.notice.success { background: var(--color-status-success-surface); color: var(--color-status-success); }
.notice code {
  font-family: var(--font-family-numeric);
  font-size: .95em;
  opacity: .85;
  overflow-wrap: anywhere;
}
.notice b { display: block; }

/* ------------------------------------------------------------------- Anmeldung */
.auth-screen {
  min-height: 100dvh;
  display: grid;
  align-content: center;
  justify-items: center;
  padding: var(--spacing-xl);
}
.auth-card {
  width: 100%;
  max-width: 424px;
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-hero);
  box-shadow: var(--elevation-hero);
  padding: var(--spacing-section) var(--spacing-xl);
}
.auth-card .brand { justify-content: center; margin-bottom: var(--spacing-xl); }
.auth-card h1 { font: var(--typography-screen-title); margin: 0 0 var(--spacing-xs); text-align: center; }
.auth-card p.lead {
  margin: 0 0 var(--spacing-xl);
  color: var(--color-text-secondary);
  font: var(--typography-metadata);
  text-align: center;
}

/* Der Trenner zwischen den Wegen: eine Linie mit einem Wort darin. Ohne ihn
   stehen vier Knoepfe untereinander, und der erste sieht aus wie einer von
   vieren statt wie der Hauptweg. */
.auth-trenner {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  margin: var(--spacing-xl) 0 var(--spacing-lg);
  color: var(--color-text-secondary);
  font: var(--typography-metadata);
}
.auth-trenner::before,
.auth-trenner::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--color-border-subtle);
}

.auth-anbieter { display: grid; gap: var(--spacing-sm); }
.auth-anbieter .btn { justify-content: center; gap: var(--spacing-md); }
/* Die Zeichen tragen ihre eigenen Farben; nur Apple folgt der Schriftfarbe,
   damit es im dunklen Modus nicht verschwindet. */
.auth-anbieter svg { width: 18px; height: 18px; flex: none; }

.field { margin-bottom: var(--spacing-lg); }
.field label { display: block; font: var(--typography-action); margin-bottom: 6px; }
.field input {
  width: 100%;
  min-height: var(--layout-touch-target);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-secondary);
  padding: 0 var(--spacing-lg);
  font: var(--typography-body);
  color: var(--color-text-primary);
  transition: border-color var(--motion-quick), background var(--motion-quick);
}
/* Ein mehrzeiliges Feld ist dasselbe Feld, nur hoeher. Es stand bisher ohne
   jede Angabe da -- der Browser gab ihm seine Vorgabebreite von rund zwanzig
   Zeichen, mitten in einem Formular, dessen andere Felder ueber die volle
   Breite gehen. */
.field textarea {
  width: 100%;
  min-height: calc(var(--layout-touch-target) * 2);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-secondary);
  padding: var(--spacing-sm) var(--spacing-lg);
  font: var(--typography-body);
  color: var(--color-text-primary);
  resize: vertical;
  transition: border-color var(--motion-quick), background var(--motion-quick);
}
.field input:hover,
.field textarea:hover { background: var(--color-surface-primary); }
.field .hint { font: var(--typography-caption); color: var(--color-text-tertiary); margin: 5px 0 0; }
/* Der Grund steht in der Meldung ueber dem Formular -- das Feld zeigt nur, wo
   er hingehoert. Den neutralen Hinweistext rot zu faerben behauptete einen
   Feldfehler, den der Server so nicht gemeldet hat. */
.field.invalid input { border-color: var(--color-status-error); background: var(--color-status-error-surface); }

/* ---------------------------------------------------------------- Profilkopf */
.profile-hero {
  position: relative;
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-card);
  padding: var(--spacing-xl);
  display: flex;
  align-items: center;
  gap: var(--spacing-xl);
  flex-wrap: wrap;
  overflow: hidden;
  margin-bottom: var(--spacing-lg);
}
.profile-hero::after {
  content: "";
  position: absolute;
  inset: 0 0 auto auto;
  width: 240px; height: 100%;
  background:
    radial-gradient(120% 140% at 100% 0%, var(--color-selection-background) 0%,
      color-mix(in srgb, var(--color-selection-background) 0%, transparent) 70%);
  pointer-events: none;
}
/* Das Bild gehoert ueber den Farbschleier, nicht darunter. Der Schleier
   (`::after`) ist 240 Punkt breit und haengt rechts -- auf einem breiten
   Bildschirm lag das Bild links davon und blieb sichtbar. Auf dem Telefon ist
   die Karte schmaler als der Schleier: Dort verschwand das Bild darunter, und
   oben in der Kachel klaffte ein leeres Feld. */
.profile-hero .avatar-column { position: relative; z-index: 1; }
.profile-hero .who { min-width: 0; flex: 1; position: relative; z-index: 1; }
.profile-hero h1 {
  font: var(--typography-screen-title);
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  flex-wrap: wrap;
}
.profile-hero h1 svg.i { color: var(--color-brand-primary-action); }
.profile-hero p.sub { margin: 6px 0 0; color: var(--color-text-secondary); font: var(--typography-metadata); }
.profile-hero .actions { position: relative; z-index: 1; }

.state-badges { display: flex; gap: var(--spacing-sm); flex-wrap: wrap; margin-top: var(--spacing-md); }
.tagline {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* „Konto aktiv" ist eine Marke, kein Absatz: Bricht sie um, stehen zwei
     halbe Woerter untereinander in einer Pille. */
  white-space: nowrap;
  font: var(--typography-caption);
  font-weight: 600;
  border-radius: 999px;
  padding: 4px 11px;
  background: var(--color-surface-secondary);
  border: 1px solid var(--color-border-subtle);
  color: var(--color-text-secondary);
}
.tagline.ok { background: var(--color-status-success-surface); border-color: transparent; color: var(--color-status-success); }
.tagline.warn { background: var(--color-status-warning-surface); border-color: transparent; color: var(--color-status-warning); }
.tagline.brand { background: var(--color-selection-background); border-color: transparent; color: var(--color-selection-foreground); }

/* --------------------------------------------------------------------- Kacheln */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
  /* Alle Reihen gleich hoch, nicht nur die Kacheln einer Reihe: Sonst sitzt
     der Strich in der oberen Reihe hoeher als in der unteren, und die Flaeche
     wirkt gestueckelt. */
  grid-auto-rows: 1fr;
  gap: var(--spacing-lg);
}
.tile {
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--elevation-card);
  padding: var(--spacing-lg);
  text-decoration: none;
  color: inherit;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
  --tile-accent: var(--color-brand-primary-action);
  --tile-surface: var(--color-selection-background);
  transition: transform var(--motion-quick), box-shadow var(--motion-quick);
}
.tile:hover { transform: translateY(-2px); box-shadow: var(--elevation-hero); }
.tile.teal { --tile-accent: var(--color-category-sport); --tile-surface: var(--color-status-success-surface); }
/* Die Kachel faerbt ihre Flaeche aus ihrer eigenen Farbe: ein Hauch davon auf
   der Oberflaeche. So dreht sie im Dunkeln mit, statt hell stehen zu bleiben. */
.tile.violet {
  --tile-accent: var(--color-category-education);
  --tile-surface: color-mix(in srgb, var(--color-category-education) 14%, var(--color-surface-primary));
}
.tile.amber { --tile-accent: var(--color-category-leisure); --tile-surface: var(--color-status-warning-surface); }
.tile .ic {
  width: 42px; height: 42px;
  border-radius: 13px;
  background: var(--tile-surface);
  color: var(--tile-accent);
  display: grid;
  place-items: center;
  margin-bottom: var(--spacing-sm);
}
.tile strong { font: var(--typography-hero-title); font-size: 1.02rem; color: var(--tile-accent); }
.tile p { margin: 0; font: var(--typography-metadata); color: var(--color-text-secondary); }
/* Die Marke unter der Kachel sagt, wie viel dahintersteckt: „55
   Veranstaltungen", „2 Mitgliedschaften". Das ist ein Satz und keine Ausgabe --
   deshalb die Schrift der Oberflaeche. Solange hier `managedEventCount = 55`
   stand, war die Schreibmaschinenschrift stimmig; jetzt liesse sie eine Angabe
   fuer Leser wie etwas fuer Entwickler aussehen. */
.tile .cap {
  margin: var(--spacing-md) 0;
  align-self: flex-start;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: .7rem;
  color: var(--tile-accent);
  background: var(--tile-surface);
  border-radius: 8px;
  padding: 3px 9px;
}
/* Die Zeile mit dem Strich sitzt unten, in allen Kacheln auf derselben Hoehe.

   Vorher schloss sie direkt an den Text an: Eine Kachel mit zwei Textzeilen
   und eine mit einer, eine mit Marke und eine ohne -- vier Striche auf vier
   Hoehen, und die Reihe sah aus wie schief gesetzt. Das Raster gibt allen
   Kacheln dieselbe Hoehe; `margin-top: auto` schiebt den Fuss ans untere
   Ende. */
.tile .go {
  margin-top: auto;
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--color-border-subtle);
  font: var(--typography-action);
  color: var(--tile-accent);
  display: flex;
  align-items: center;
  gap: 6px;
}
.tile[aria-disabled="true"] { opacity: .55; }
.tile[aria-disabled="true"]:hover { transform: none; box-shadow: var(--elevation-card); }

/* ------------------------------------------------------------- Rechte Spalte */
.rail { display: flex; flex-direction: column; gap: var(--spacing-lg); position: sticky; top: 77px; }

.stats { list-style: none; margin: 0; padding: 0 var(--spacing-lg) var(--spacing-lg); }
.stats li {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  padding: var(--spacing-sm) 0;
  font: var(--typography-metadata);
}
.stats li .ic { color: var(--color-text-tertiary); display: grid; place-items: center; }
.stats li .label { flex: 1; color: var(--color-text-primary); }
.stats li .value { font: var(--typography-card-title); font-variant-numeric: tabular-nums; }
.stats li .value.none { color: var(--color-text-tertiary); font-weight: 400; }
.stats li .src { font: var(--typography-caption); color: var(--color-text-tertiary); font-family: var(--font-family-numeric); }

.quick { list-style: none; margin: 0; padding: 0 var(--spacing-sm) var(--spacing-sm); }
.quick a {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  min-height: var(--layout-touch-target);
  padding: 0 var(--spacing-md);
  border-radius: var(--radius-control);
  text-decoration: none;
  color: var(--color-text-primary);
  font: var(--typography-action);
  font-weight: 500;
}
.quick a:hover { background: var(--color-surface-secondary); }
.quick a .go { margin-left: auto; color: var(--color-text-tertiary); }
.quick a[aria-disabled="true"] { opacity: .5; }
/* Ein Menuepunkt allein in seinem Kasten: Der Kasten ist dann die Zeile, und
   die Zeile braucht ringsum denselben Abstand -- oben wie unten. Die Liste
   mehrerer Punkte laesst oben Platz fuer die Ueberschrift darueber; hier gibt
   es keine, und die Luecke sah aus wie ein vergessener Titel. */
.quick.einzeln { padding: var(--spacing-sm); }

.rows { list-style: none; margin: 0; padding: 0; }
.rows li {
  border-top: 1px solid var(--color-border-subtle);
  padding: var(--spacing-md) var(--spacing-lg);
  display: flex;
  gap: var(--spacing-md);
  align-items: center;
}
.rows li .grow { flex: 1; min-width: 0; }
.rows li strong { font: var(--typography-card-title); display: block; }
.rows li span.meta { font: var(--typography-caption); color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.rows li.empty { color: var(--color-text-tertiary); font: var(--typography-metadata); justify-content: center; }

/* Die zwei Wege hinaus: eine eigene Zeile unter dem Kopf und vor der Liste.

   Unter der Liste standen sie hinter allen Zeilen -- bei fuenfzig Geraeten
   blaettert man an fuenfzig Anmeldungen vorbei, um das zu tun, weswegen man
   hergekommen ist. Im Kopf selbst draengten sie sich neben Titel und Zahl.
   Also darunter, hinter derselben Linie, die auch die Zeilen trennt: eine
   Handlungsleiste, wie sie jede Liste haben darf.

   Kleiner als ein Knopf im Formular, weil sie zur Liste gehoeren und nicht zur
   Handlung eines Feldes -- dasselbe Mass wie „Beenden" in der Zeile darunter.
   Rechts, wo auch dort die Knoepfe stehen. */
.sitzungswerkzeuge {
  display: flex;
  justify-content: flex-end;
  gap: var(--spacing-sm);
  flex-wrap: wrap;
  padding: var(--spacing-md) var(--spacing-lg);
  border-top: 1px solid var(--color-border-subtle);
}
.sitzungswerkzeuge .btn {
  min-height: 36px;
  padding: 0 var(--spacing-md);
  font: var(--typography-metadata);
  font-weight: 600;
}
@media (max-width: 640px) {
  /* Schmal traegt jeder Knopf seine Zeile: Nebeneinander bleiben von
     „Alle bis auf die aktive Sitzung beenden" zwei Woerter je Zeile uebrig. */
  .sitzungswerkzeuge { justify-content: stretch; }
  .sitzungswerkzeuge .btn { flex: 1 0 100%; }
}

/* --------------------------------------------------------------- Netzwerkliste */
.net { list-style: none; margin: 0; padding: 0; font-family: var(--font-family-numeric); max-height: 320px; overflow-y: auto; }
.net li {
  border-top: 1px solid var(--color-border-subtle);
  padding: var(--spacing-sm) var(--spacing-lg);
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--spacing-sm);
  align-items: baseline;
  font-size: .74rem;
}
.net .method { font-weight: 700; color: var(--color-text-secondary); }
.net .path { overflow-wrap: anywhere; color: var(--color-text-primary); }
.net .status { font-variant-numeric: tabular-nums; font-weight: 700; }
.net .ok { color: var(--color-status-success); }
.net .warn { color: var(--color-status-warning); }
.net .bad { color: var(--color-status-error); }
.net .aside { grid-column: 2 / 3; color: var(--color-text-tertiary); font-size: .7rem; }
/* Ueber einer Sekunde: kein Fehler, aber etwas, das jemand angesehen haben
   sollte. Dieselbe Grenze zieht der Server im Protokoll (slow=true). */
.net .aside.langsam { color: var(--color-status-warning); font-weight: 700; }
/* Die Uhrzeit steht unter dem Status, klein und an derselben Kante. */
.net .at {
  grid-column: 3;
  justify-self: end;
  color: var(--color-text-tertiary);
  font-size: .62rem;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ Rechte
   Dieselbe Sprache wie die Netzwerkliste darunter -- schmale Ziffernschrift,
   Zeilen durch Haarlinien getrennt. Nur die erste Spalte ist neu: Sie traegt
   das Ja oder Nein, und weil sie feste Breite hat, stehen alle Namen an einer
   Kante und die Verweigerungen fallen beim Ueberfliegen auf. */
.rechte { list-style: none; margin: 0; padding: 0; font-family: var(--font-family-numeric); max-height: 420px; overflow-y: auto; }
.rechte li {
  border-top: 1px solid var(--color-border-subtle);
  padding: var(--spacing-xs) var(--spacing-lg);
  display: grid;
  grid-template-columns: 1.1rem auto 1fr;
  gap: var(--spacing-sm);
  align-items: baseline;
  font-size: .74rem;
}
.rechte .marke { font-weight: 700; text-align: center; }
.rechte .marke.ja { color: var(--color-status-success); }
.rechte .marke.nein { color: var(--color-status-error); }
.rechte .name { color: var(--color-text-primary); overflow-wrap: anywhere; }
.rechte .wert {
  justify-self: end;
  color: var(--color-text-secondary);
  font-size: .7rem;
  overflow-wrap: anywhere;
}
/* Die Kennung der Richtlinie ist die Spur in den Quelltext des Servers und
   steht deshalb dabei -- aber klein, unter dem Namen, damit sie die Zeile
   nicht fuehrt. */
.rechte .richtlinie {
  grid-column: 2 / 4;
  color: var(--color-text-tertiary);
  font-size: .62rem;
}
/* Der Satz zum Verwaltungszugang: Er relativiert alles darueber und muss
   deshalb auffallen, ohne wie ein Fehler auszusehen. */
.rechte li.hinweis {
  display: block;
  color: var(--color-status-warning);
  font-size: .68rem;
  line-height: 1.35;
}
/* Abschnittskoepfe: „Konto", „Diese Veranstaltung". */
.rechte li.titel {
  display: block;
  padding-top: var(--spacing-sm);
  color: var(--color-text-tertiary);
  font: var(--typography-metadata);
  text-transform: uppercase;
  letter-spacing: .06em;
}

.foot-note {
  margin-top: var(--spacing-section);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--color-border-subtle);
  font: var(--typography-caption);
  color: var(--color-text-tertiary);
  max-width: 82ch;
}

.skeleton {
  background: linear-gradient(90deg, var(--color-surface-secondary) 25%,
    color-mix(in srgb, var(--color-text-tertiary) 12%, var(--color-surface-secondary)) 50%,
    var(--color-surface-secondary) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: 8px;
  color: transparent;
}
@keyframes shimmer { to { background-position: -200% 0; } }

@media (max-width: 1180px) {
  .cols { grid-template-columns: minmax(0, 1fr); }
  .rail { position: static; }
}
@media (max-width: 940px) {
  .mainnav a span.lbl { display: none; }
  .main { padding: var(--spacing-lg); }
}
/* Auf schmalem Schirm darf die Kopfzeile nicht umbrechen und nichts
   abschneiden: Die Bereichsreihe gibt nach und scrollt, Konto und Anmeldung
   behalten ihren Platz. */
@media (max-width: 780px) {
  .appbar { flex-wrap: nowrap; gap: var(--spacing-sm); padding: var(--spacing-sm) var(--spacing-md); }
  .mainnav { margin: 0; min-width: 0; overflow-x: auto; scrollbar-width: none; }
  .mainnav::-webkit-scrollbar { display: none; }
  .mainnav a { padding: 0 var(--spacing-sm); }
  .appbar-tools { flex: none; margin-left: auto; }
  .brand { flex: none; }
}
@media (max-width: 620px) {
  .appbar { padding: var(--spacing-sm) var(--spacing-md); gap: var(--spacing-sm); }
  .brand span.wordmark { display: none; }
  .account span.name { display: none; }

  /* Der Profilkopf war eine Zeile: Bild, Name, Knopf. Auf dem Telefon blieb
     fuer den Namen eine Spalte von drei Woertern -- „Nachbarschaftshaus
     Weinheim" stand ueber zwei Zeilen, die Marken darunter jede in einer
     eigenen, und „Neu laden" sass daneben in der Luft. Untereinander ist es
     dieselbe Ordnung, nur in der Richtung, die auf ein Telefon passt. */
  .profile-hero {
    flex-direction: column;
    align-items: stretch;
    padding: var(--spacing-lg);
    gap: var(--spacing-md);
  }
  .profile-hero .avatar-column { align-self: flex-start; }
  .profile-hero .who { flex: none; width: 100%; }
  .profile-hero h1 { font: var(--typography-section-title); }
  .profile-hero .actions { width: 100%; }
  .profile-hero .actions .btn { width: 100%; justify-content: center; }
  .avatar.xl { width: 64px; height: 64px; font-size: 1.3rem; }
}

/* --------------------------------------------------------------- Sprachwahl
 *
 * Eine Liste, kein Formularfeld: Drei Sprachen plus „die des Browsers" stehen
 * untereinander, jede Zeile anklickbar. Ein Auswahlfeld verstecken die Wahl
 * hinter einem Klick und zeigt nie, was es sonst noch gäbe.
 *
 * Die Klasse .filter-option gibt es schon -- aber in discover.css, und die
 * lädt diese Seite nicht. Wer sie hier verwendet, bekommt unformatierte
 * Knöpfe nebeneinander; genau das war der Fall.
 */
.language-choice { display: flex; flex-direction: column; }
.language-choice label {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  padding: var(--spacing-md) var(--spacing-lg);
  border-top: 1px solid var(--color-border-subtle);
  cursor: pointer;
  font: var(--typography-body);
}
.language-choice label:first-child { border-top: none; }
.language-choice label:hover { background: var(--color-surface-secondary); }
.language-choice input {
  accent-color: var(--color-brand-primary-action);
  width: 18px; height: 18px; flex: none;
}
.language-choice .label { flex: 1; min-width: 0; }
/* Das Sprachetikett steht rechts und leise: Es beantwortet die Frage „welches
   de?" für den, der sie stellt, und stört den Rest nicht. */
.language-choice .n {
  color: var(--color-text-tertiary);
  font: var(--typography-metadata);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------- Interessen
 *
 * Je oberste Kategorie eine Gruppe, aufklappbar, darin die Themen als
 * Kacheln. Wie in der App -- wer beides benutzt, soll nicht zweimal lernen,
 * wo er hinfassen muss.
 */
.interest-group { border-top: 1px solid var(--color-border-subtle); }
.interest-head {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  width: 100%;
  padding: var(--spacing-md) var(--spacing-lg);
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font: var(--typography-body);
  color: inherit;
}
.interest-head:hover { background: var(--color-surface-secondary); }
.interest-head .name { flex: 1; min-width: 0; font-weight: 600; }
/* Wie viele gewaehlt sind, steht in der Kopfzeile: Sonst muesste man jede
   Gruppe aufklappen, um zu sehen, ob etwas drinsteht. */
.interest-head .n { color: var(--color-text-tertiary); font: var(--typography-metadata); }
.interest-head svg.i { flex: none; color: var(--color-text-tertiary); }

/* Kacheln statt Pillen: Symbol über dem Wort, vier je Reihe. Genau wie in der
   App -- ein Bild erkennt man schneller als eine Zeile Text, und bei siebzig
   Themen zählt das. */
.interest-topics {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: var(--spacing-sm);
  padding: 0 var(--spacing-lg) var(--spacing-md);
}
.interest-topic {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: var(--spacing-md) 6px;
  border-radius: var(--radius-control);
  border: 1px solid transparent;
  background: var(--color-surface-secondary);
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
  cursor: pointer;
  text-align: center;
}
.interest-topic .wort { overflow-wrap: anywhere; line-height: 1.25; }
.interest-topic:hover { border-color: var(--color-brand-primary-action); }
.interest-topic.on {
  background: var(--color-selection-background);
  border-color: var(--color-brand-primary-action);
  color: var(--color-selection-foreground);
  font-weight: 600;
}

/* Das Symbol trägt die Farbe seiner Oberkategorie -- dieselben Farben wie die
   Karten im Entdecken, damit ein Thema und die Veranstaltungen dazu sichtbar
   zusammengehören. */
.interest-topic .mark,
.interest-head .mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--color-surface-primary);
  color: var(--color-text-secondary);
  flex: none;
}
.interest-head .mark { width: 30px; height: 30px; }
.interest-topic .mark svg.i,
.interest-head .mark svg.i { width: 18px; height: 18px; }

.mark.sport-movement { color: var(--color-category-sport); background: color-mix(in srgb, var(--color-category-sport) 12%, var(--color-surface-primary)); }
.mark.learning-education { color: var(--color-category-education); background: color-mix(in srgb, var(--color-category-education) 12%, var(--color-surface-primary)); }
.mark.culture-creativity { color: var(--color-category-culture); background: color-mix(in srgb, var(--color-category-culture) 12%, var(--color-surface-primary)); }
.mark.community-engagement { color: var(--color-category-community); background: color-mix(in srgb, var(--color-category-community) 12%, var(--color-surface-primary)); }
.mark.family-children { color: var(--color-category-family); background: color-mix(in srgb, var(--color-category-family) 12%, var(--color-surface-primary)); }
.mark.food-enjoyment { color: var(--color-category-food); background: color-mix(in srgb, var(--color-category-food) 12%, var(--color-surface-primary)); }
.mark.health-wellbeing { color: var(--color-category-health); background: color-mix(in srgb, var(--color-category-health) 12%, var(--color-surface-primary)); }
.mark.advice-coaching-career { color: var(--color-category-education); background: color-mix(in srgb, var(--color-category-education) 12%, var(--color-surface-primary)); }
.mark.services-everyday { color: var(--color-category-leisure); background: color-mix(in srgb, var(--color-category-leisure) 12%, var(--color-surface-primary)); }
.mark.hobbies-leisure-experiences { color: var(--color-category-leisure); background: color-mix(in srgb, var(--color-category-leisure) 12%, var(--color-surface-primary)); }
.interest-more {
  display: block;
  width: 100%;
  padding: 0 var(--spacing-lg) var(--spacing-md);
  background: none;
  border: 0;
  cursor: pointer;
  text-align: center;
  font: var(--typography-action);
  color: var(--color-brand-primary-action);
}

/* -------------------------------------------------------------------- Konto
 *
 * Ein Formular, kein Kachelfeld: Was hier steht, schreibt man, statt es
 * auszuwaehlen. Zwei Felder nebeneinander, wo sie zusammengehoeren -- Vorname
 * und Nachname sind ein Name, nicht zwei Angaben.
 */
/* Der eigene Abstand kommt aus dem Raster; der Vorgabeabstand der Felder
   ergaebe sonst doppelte Luecken. */
.account-form { padding: var(--spacing-lg); display: flex; flex-direction: column; gap: var(--spacing-md); }
.account-form .field { margin-bottom: 0; }
.account-form .pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-lg); }
/* Das Auswahlfeld traegt dieselbe Form wie die Textfelder. Ohne das steht
   mitten im Formular ein Kasten im Stil des Betriebssystems. */
/* Bis zum 10. September 2026 stand hier ein nachgebautes `<select>`: ein
   eigener Pfeil als Hintergrundbild und `appearance: none`, damit der Browser
   seinen nicht ohne Abstand an die Rundung malt. Der ganze Aufwand galt einem
   Feld, das sich trotzdem als Liste des Betriebssystems oeffnete. Das
   Auswahlfeld des Hauses macht beides ueberfluessig -- die Regel ist weg. */

/* Das Bild im Profilkopf ist zugleich sein eigener Knopf: Der kleine Griff
   sitzt unten rechts auf der Kachel, so wie man es von Profilen kennt. */
.avatar-column {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
  flex: none;
}
.avatar-column .btn { min-height: 32px; font-size: .82rem; }
.avatar-slot { position: relative; display: inline-block; flex: none; }
.avatar-edit {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid var(--color-surface-primary);
  background: var(--color-brand-primary-action);
  color: var(--color-on-brand-primary-action);
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: var(--elevation-card);
}
.avatar-edit:hover { filter: brightness(1.08); }
.avatar-edit svg.i { width: 16px; height: 16px; }

.account-form textarea {
  width: 100%;
  resize: vertical;
  font: var(--typography-body);
  color: var(--color-text-primary);
  background: var(--color-surface-primary);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  padding: 10px 12px;
}
.account-form textarea:focus-visible {
  outline: 2px solid var(--color-brand-primary-action);
  outline-offset: 1px;
}
/* Ein gesperrtes Feld sieht aus wie ein Feld und laesst sich doch nicht
   anfassen. Es traegt die Angabe sichtbar mit, ohne zum Tippen einzuladen. */
.account-form input:disabled {
  background: var(--color-surface-secondary);
  color: var(--color-text-secondary);
  cursor: not-allowed;
}
.account-form .field.readonly .value {
  margin: 0;
  font: var(--typography-body);
  color: var(--color-text-secondary);
}
.account-actions {
  padding: 0 var(--spacing-lg) var(--spacing-lg);
  display: flex;
  gap: var(--spacing-md);
  align-items: center;
  flex-wrap: wrap;
}
@media (max-width: 620px) {
  .account-form .pair { grid-template-columns: 1fr; }
}

/* -------------------------------------------------------------- Rueckmeldung
 *
 * Unten in der Mitte, ueber allem, und nach zweieinhalb Sekunden wieder weg.
 * Sie nimmt keine Klicks an -- was sie sagt, ist bereits geschehen.
 */
.toast {
  position: fixed;
  left: 50%;
  bottom: var(--spacing-section);
  transform: translate(-50%, 12px);
  z-index: 60;
  padding: 10px var(--spacing-lg);
  border-radius: 999px;
  background: var(--color-text-primary);
  color: var(--color-surface-primary);
  font: var(--typography-action);
  box-shadow: var(--elevation-hero);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-quick), transform var(--motion-quick);
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity var(--motion-quick); transform: translate(-50%, 0); }
}


/* ------------------------------------------------------------ Eine Zeile geht */

/* Wenn etwas aus einer Liste faellt -- ein Herz ausgeschaltet, ein Eintrag
   erledigt --, verschwindet es nicht einfach: Es wird blass, macht sich flach,
   und erst dann ist es weg. Ein Sprung genau an der Stelle, auf die man eben
   noch gezeigt hat, liest sich wie ein Fehler.

   Die Hoehe setzt `verabschieden()` in karte.js; hier steht nur, wie es
   aussieht. Eine Klasse fuer alle Listen -- zwei gleiche Vorgaenge sollen
   nicht verschieden aussehen. */
/* Die beiden `!important` sind hier kein Notnagel, sondern die Aussage: Waehrend
   eine Zeile geht, gilt nichts anderes mehr. Sie sind noetig, weil die Karte in
   einer spaeter geladenen Datei `min-height: 148px` und eine eigene
   `transition` traegt (`.event-card.row`, discover.css) -- gleich schwer, und
   bei Gleichstand gewinnt die spaetere Regel. Ohne sie bliebe die Karte 148
   Pixel hoch stehen und spraenge am Ende weg.

   `min-height` wird abgeschaltet und nicht mitbewegt: Es ist eine Schranke,
   keine Bewegung. Liesse man es mitlaufen, hielte es die Hoehe den ganzen
   Uebergang lang fest. */
.geht {
  overflow: hidden;
  min-height: 0 !important;
  opacity: 0;
  transform: scale(.97);
  pointer-events: none;
  transition:
    height var(--motion-standard),
    margin var(--motion-standard),
    opacity var(--motion-quick),
    transform var(--motion-quick) !important;
}


/* --------------------------------------------------------- Leere Bereiche */

/* Wo nichts steht, steht trotzdem etwas: Zeichen, Satz, Erklaerung -- und wo
   es einen gibt, der Weg weiter. Ein leerer Kasten mit einem Halbsatz darin
   sagt keinem, warum er leer ist.

   Dieselbe Form ueberall: keine Treffer in der eigenen Liste, keine Fragen an
   einer Veranstaltung, kein Entwurf. Drei Stellen mit drei Auftritten waeren
   dreimal dieselbe Ratlosigkeit in anderer Gestalt. */
.leerbild {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-section) var(--spacing-lg);
  text-align: center;
}
.leerbild-zeichen {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--color-surface-secondary);
  color: var(--color-text-tertiary);
}
.leerbild-zeichen svg.i { width: 24px; height: 24px; }
.leerbild > b { font: var(--typography-section-title); color: var(--color-text-primary); }
.leerbild > .hint { margin: 0; max-width: 28rem; }
.leerbild > .btn { margin-top: var(--spacing-sm); }

/* ------------------------------------------------- Feld mit Zeichen davor

   Ein Eingabefeld, dem ein Zeichen vorangeht: die Lupe der Suche, der Kalender
   eines Datums, die Liste einer Sortierung. Der Rahmen gehoert der Huelle, nicht
   dem Feld -- so sind Suchfeld, Datumsfeld und Auswahl gleich hoch und gleich
   gerahmt, statt dass jedes den Auftritt mitbringt, den der Browser ihm gibt. */
.feldmitzeichen {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  min-height: 40px;
  padding: 0 var(--spacing-sm);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-primary);
}
.feldmitzeichen svg.i { width: 16px; height: 16px; flex: none; color: var(--color-text-tertiary); }
.feldmitzeichen:focus-within { border-color: var(--color-focus); }
.feldmitzeichen input {
  flex: 1;
  min-width: 0;
  align-self: stretch;
  border: 0;
  background: transparent;
  font: var(--typography-body);
  color: var(--color-text-primary);
}
.feldmitzeichen input:focus { outline: none; }
/* Den Rand traegt hier der Rahmen aussen -- das Auswahlfeld darin ist nur
   noch Wort und Pfeil. */
.feldmitzeichen .aw { flex: 1; min-width: 0; align-self: stretch; }
.feldmitzeichen .aw-knopf {
  height: 100%;
  border: 0;
  background: transparent;
  padding-left: 0;
}
/* Die Sortierung ist so breit wie ihr laengster Eintrag, nicht so breit wie
   der Platz: Sie soll die Suche nicht verdraengen. */


/* ------------------------------------------------ Hinweis auf KI-Bilder
 *
 * Das offizielle Label kommt als Bild vom Server und liegt ueber dem bereits
 * zugeschnittenen Bild -- nicht darin. Eingebrannt ueberstuende es keinen
 * zweiten Zuschnitt: Wer das Bild spaeter anders schneidet, schnitte den
 * Hinweis mit ab.
 *
 * Unten rechts, weil oben die Schilder mit Tag und Uhrzeit sitzen. Die Groesse
 * waechst mit der Flaeche (`cqw`, sonst ein fester Wert) und hat eine
 * Obergrenze: In einer Listenzeile soll der Hinweis lesbar sein, nicht gross.
 */
.art,
.cr-bild {
  position: relative;
  container-type: inline-size;
}

.ki-hinweis {
  position: absolute;
  /* Links unten, nicht rechts: Rechts steht auf der Karte der Preis, und beide
     am selben Rand hiess, dass das Etikett hinter ihm hervorschaute. Das
     Etikett kennzeichnet das Bild, der Preis die Veranstaltung -- zwei
     Auskuenfte, zwei Ecken. */
  left: 4px;
  bottom: 4px;
  width: clamp(38px, 22cqw, 92px);
  height: auto;
  max-width: 40%;
  pointer-events: none;
  /* Auf hellen Bildern verschwaende der schwarze Balken sonst am Rand. */
  filter: drop-shadow(0 1px 2px rgb(0 0 0 / 35%));
}

/* Solange die passende Groesse noch nicht gesetzt ist (`bilderAnwenden` in
   bildflaechen.js), steht das Etikett ohne `src` da. Unsichtbar und nicht
   `display: none`: Es muss seinen Platz behalten, sonst laesst es sich nicht
   messen -- und ohne Mass bekaeme es nie eine Groesse. */
.ki-hinweis:not([src]) {
  visibility: hidden;
}


/* ============================================================== Die Karte
 *
 * Sie steht an drei Stellen: im Anlegen (mit Stift zum Verschieben), auf der
 * Veranstaltungsseite (nur zum Ansehen) und im Entdecken (viele Nadeln). Die
 * Regeln lagen bis zum 25. August 2026 im Stilblatt des Anlegens -- die
 * anderen beiden Seiten laden es nicht, und ohne `position: absolute` stapelt
 * ein Kachelraster sich zu einer Bildsaeule von tausend Punkten Hoehe.
 */
/* Der Ersatz ohne Anbieter. Er sieht ausdruecklich nicht wie eine Karte aus --
   eine gezeichnete Flaeche mit Punkten waere eine Karte, die luegt. */
.kt-ersatz {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
  padding: var(--spacing-lg);
}
.kt-hinweis { margin: 0; font: var(--typography-body); color: var(--color-text-secondary); }
.kt-punkt {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--spacing-sm) var(--spacing-md);
}
.kt-punkt label { font: var(--typography-metadata); color: var(--color-text-secondary); }
.kt-punkt input {
  min-height: 44px;
  padding: 8px var(--spacing-md);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-primary);
  font: var(--typography-body);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-primary);
}
.kt-notiz { margin: 0; font: var(--typography-metadata); color: var(--color-text-tertiary); }

/* ------------------------------------------------- Die Karte von OpenStreetMap
 *
 * Der Stift sitzt fest in der Mitte, die Kacheln bewegen sich darunter. Mit dem
 * Finger ist das dasselbe wie mit der Maus -- und niemand muss ein 24 Punkt
 * grosses Zeichen treffen.
 */
.kt-karte {
  position: relative;
  height: 300px;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  background: var(--color-surface-secondary);
}
.kt-karte.zieht { cursor: grabbing; }
.kt-ebene { position: absolute; inset: 0; will-change: transform; }
.kt-ebene img {
  position: absolute;
  width: 256px;
  height: 256px;
  user-select: none;
  -webkit-user-drag: none;
}
/* Der Stift: ein Tropfen mit Loch, wie er auf jeder Karte steht -- gebaut aus
   einem gedrehten Quadrat mit drei runden Ecken, damit kein Bild nachgeladen
   werden muss. Die Spitze zeigt genau auf die Mitte. */
.kt-stift {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 24px;
  height: 24px;
  margin: -30px 0 0 -12px;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  background: var(--color-brand-primary-action);
  box-shadow: 0 2px 6px rgb(0 0 0 / 35%);
  pointer-events: none;
}
.kt-stift::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 7px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--color-surface-primary);
}
.kt-zoom {
  position: absolute;
  right: var(--spacing-sm);
  top: var(--spacing-sm);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.kt-zoom button {
  width: 34px;
  height: 34px;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-primary);
  font: var(--typography-action);
  color: var(--color-text-primary);
  cursor: pointer;
}
/* Namensnennung ist keine Zierde, sondern die Bedingung, unter der diese
   Kacheln benutzt werden duerfen. Deshalb immer sichtbar, nicht ausblendbar. */
.kt-quelle {
  position: absolute;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: 2px 6px;
  border-radius: var(--radius-control) 0 0 0;
  background: color-mix(in srgb, var(--color-surface-primary) 80%, transparent);
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
}
/* Wenn keine Kachel ankommt: ein Satz statt einer leeren Flaeche. */
.kt-karte.blind { background: var(--color-surface-secondary); }
.kt-blind {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  max-width: 80%;
  padding: 8px var(--spacing-md);
  border-radius: var(--radius-control);
  background: color-mix(in srgb, var(--color-surface-primary) 92%, transparent);
  text-align: center;
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
}
.kt-leer {
  position: absolute;
  left: 50%;
  bottom: var(--spacing-lg);
  transform: translateX(-50%);
  margin: 0;
  padding: 6px var(--spacing-md);
  border-radius: var(--radius-control);
  background: color-mix(in srgb, var(--color-surface-primary) 90%, transparent);
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
  white-space: nowrap;
  pointer-events: none;
}


/* ------------------------------------------------- Die Zeichen der Kartendienste
 *
 * Zwei Stellen zeigen dieselben drei Wege: „Navigieren" auf der
 * Veranstaltungsseite und die Blase an einer Nadel im Entdecken.
 */
.kt-zeichen {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 10px;
  background: var(--color-surface-secondary);
  color: var(--color-text-secondary);
}
.kt-zeichen svg.i { width: 20px; height: 20px; }
/* Drei Wege, drei Farben -- damit man sie am Rand des Blicks auseinanderhaelt.
   Eigene Toene, keine fremden Marken. */
.kt-zeichen.ton-google { background: #E8F0FE; color: #1A56C4; }
.kt-zeichen.ton-apple { background: #ECFAF0; color: #1F6F4A; }
.kt-zeichen.ton-osm { background: var(--color-selection-background); color: var(--color-brand-primary-action); }
/* Der Punkt selbst -- kein Kartendienst, sondern die nackten Koordinaten fuer
   alles, was nicht in der Liste steht: das Navigationsgeraet im Auto, eine
   Fahrzeug-App, ein Mensch am anderen Ende. */
.kt-zeichen.ton-punkt { background: var(--color-surface-secondary); color: var(--color-text-secondary); }
/* In der Blase ist weniger Platz als in einem Menue -- dasselbe Zeichen, kleiner. */
.kt-zeichen.klein { width: 22px; height: 22px; border-radius: 7px; }
.kt-zeichen.klein svg.i { width: 13px; height: 13px; }


/* ============================================================== Der Kalender
 *
 * Ein Monatsraster und darunter der gewaehlte Tag. Die Farben der Punkte
 * kommen aus der Kategorie -- dieselben wie auf den Karten, damit ein Blick
 * genuegt: dieser gruene Punkt ist der Sport am Mittwoch.
 */
/* Der Inhalt steht so weit innen wie die Kopfzeile mit ihrem Zeichen -- sonst
   steht das Raster einen Fingerbreit weiter links als der Titel darueber, und
   die Kachel sieht schief aus. */
.kal .panel-body {
  display: flex; flex-direction: column; gap: var(--spacing-lg);
  padding: 0 var(--spacing-lg) var(--spacing-lg);
}
/* „Als Naechstes" ist eine Ueberschrift wie jede andere, kein Kleingedrucktes
   in der Kachel darunter. Der Abstand daneben sagt, wie lange es noch hin
   ist. */
.kal-abschnitt {
  display: flex; align-items: baseline; gap: var(--spacing-sm);
  margin: 0;
  font: var(--typography-section-title);
}
.kal-abschnitt span {
  font: var(--typography-metadata);
  color: var(--color-brand-primary-action);
}
.kal .hint { margin-left: auto; font: var(--typography-metadata); color: var(--color-text-tertiary); }

/* „Als Naechstes" -- die haeufigste Frage steht oben, nicht im Raster. */
.kal-naechster {
  position: relative;
  display: flex; align-items: center; gap: var(--spacing-lg);
  padding: var(--spacing-md) var(--spacing-lg);
  margin-top: calc(var(--spacing-lg) * -1 + var(--spacing-sm));
  border-radius: var(--radius-card);
  overflow: hidden;
  text-decoration: none;
  color: var(--color-text-primary);
  background: var(--color-surface-secondary);
}
/* Ein schmaler Streifen in der Farbe der Kategorie -- kein Farbteppich: Der
   Text muss lesbar bleiben, und die Kachel ist keine Werbung. */
.kal-naechster::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: linear-gradient(180deg, var(--card-a, #40576B), var(--card-b, #7C93A6));
}
.kal-naechster:hover { background: var(--color-selection-background); }
.kal-naechster-kopf {
  font: var(--typography-metadata);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-text-tertiary);
}
.kal-naechster-bild {
  width: 96px; height: 72px; flex: none;
  border-radius: var(--radius-control);
  background: linear-gradient(150deg, var(--card-a, #40576B), var(--card-b, #7C93A6));
  background-size: cover; background-position: center;
}
.kal-naechster-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.kal-naechster strong {
  font: var(--typography-section-title);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Die Farben der Punkte, erklaert. Klein, unter dem Raster -- wer sie einmal
   gelesen hat, braucht sie nicht mehr. */
.kal-legende {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--spacing-sm) var(--spacing-lg);
  margin: 0;
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
}
.kal-legende span { display: inline-flex; align-items: center; gap: 6px; }
.kal-legende i {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--punkt, #999);
}
.kal-naechster-zeile {
  display: flex; align-items: center; gap: 6px;
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
}
.kal-naechster-zeile svg.i { width: 15px; height: 15px; flex: none; }

.kal-kopf { display: flex; align-items: center; gap: var(--spacing-sm); }
.kal-kopf h3 {
  margin: 0;
  min-width: 11ch;
  text-align: center;
  font: var(--typography-section-title);
}
.kal-blatt, .kal-heute {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 36px;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-primary);
  color: var(--color-text-primary);
  cursor: pointer;
}
.kal-blatt { width: 36px; }
.kal-heute { margin-left: auto; padding: 0 var(--spacing-md); font: var(--typography-action); }
.kal-blatt:hover, .kal-heute:hover { border-color: var(--color-brand-primary-action); }

.kal-wochentage, .kal-gitter {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}
/* Das Wochenende steht in einer ruhigeren Farbe -- man findet es, ohne die
   Zahlen zu lesen. Getoente Flaechen waren dafuer zu laut: Zwei helle Spalten
   sahen aus wie eine Auswahl, und der gewaehlte Tag verlor daneben. */
.kal-wochentage > :nth-child(6), .kal-wochentage > :nth-child(7),
.kal-gitter > :nth-child(7n - 1) .kal-zahl,
.kal-gitter > :nth-child(7n) .kal-zahl { color: var(--color-text-tertiary); }
.kal-gitter > :nth-child(7n - 1).heute .kal-zahl,
.kal-gitter > :nth-child(7n).heute .kal-zahl { color: var(--color-brand-primary-action); }
.kal-wochentage {
  font: var(--typography-metadata);
  color: var(--color-text-tertiary);
  text-align: center;
}
.kal-gitter.laedt { opacity: .55; }

.kal-tag {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-height: 46px;
  padding: 4px 2px 3px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--color-text-primary);
  font: var(--typography-body);
  cursor: pointer;
}
.kal-tag:hover { background: var(--color-surface-secondary); }
.kal-tag.fremd { color: var(--color-text-tertiary); }
/* Heute ist ein Ring, kein Fuellfleck: Der gewaehlte Tag ist die gefuellte
   Flaeche, und beides zugleich waere nicht mehr zu unterscheiden. */
.kal-tag.heute .kal-zahl {
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px var(--color-brand-primary-action);
  color: var(--color-brand-primary-action);
  font-weight: 600;
}
.kal-tag.gewaehlt {
  background: var(--color-selection-background);
  border-color: var(--color-brand-primary-action);
}
.kal-zahl { line-height: 26px; font-variant-numeric: tabular-nums; }
.kal-punkte { display: flex; align-items: center; gap: 3px; min-height: 8px; }
.kal-punkt { width: 7px; height: 7px; border-radius: 50%; background: var(--punkt, #999); }
.kal-mehr { font: var(--typography-metadata); color: var(--color-text-tertiary); }

.kal-fehler {
  display: flex; align-items: center; gap: var(--spacing-md); flex-wrap: wrap;
  padding: var(--spacing-md) var(--spacing-lg);
  border-radius: var(--radius-card);
  background: var(--color-status-warning-surface);
  font: var(--typography-body);
  color: var(--color-status-warning);
}
.kal-fehler span { display: flex; align-items: center; gap: var(--spacing-sm); }
.kal-fehler svg.i { width: 18px; height: 18px; flex: none; }
.kal-fehler .btn { margin-left: auto; }
/* Nach einem Fehler wissen wir ueber den Tag nichts -- also steht dort auch
   nicht, er sei frei. */
.kal-unbekannt { margin: 0; color: var(--color-text-secondary); }

/* Der gewaehlte Tag steht in einer eigenen Flaeche unter dem Raster -- er ist
   die Antwort auf den Klick und nicht eine Fussnote des Monats. */
.kal-tagesteil {
  margin-top: var(--spacing-lg);
  padding: var(--spacing-lg);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: var(--color-surface-primary);
  box-shadow: var(--elevation-card);
}
.kal-tagesteil h3 { margin: 0 0 var(--spacing-md); font: var(--typography-section-title); }
.kal-liste { display: flex; flex-direction: column; }

.kal-zeile {
  display: flex; align-items: center; gap: var(--spacing-md);
  padding: var(--spacing-md) 0;
  border-top: 1px solid var(--color-border-subtle);
  text-decoration: none;
  color: inherit;
}
.kal-zeile:first-child { border-top: 0; }
.kal-zeile:hover strong { color: var(--color-brand-primary-action); }
/* Die Uhrzeit als eigener kleiner Block: Anfang in der Farbe der Rolle, Ende
   darunter in Grau. Zwei nackte Zahlen untereinander lasen sich wie zwei
   Termine. */
.kal-zeit {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  min-width: 62px;
  padding: 6px 8px;
  border-radius: var(--radius-control);
  background: color-mix(in srgb, var(--rolle, #999) 8%, transparent);
  font-variant-numeric: tabular-nums;
}
.kal-zeit b {
  font: var(--typography-body-strong, var(--typography-body));
  color: var(--rolle, var(--color-text-primary));
}
.kal-zeit span { font: var(--typography-metadata); color: var(--color-text-tertiary); }
/* Der Faden zwischen Anfang und Ende -- ein Punkt oben, eine Linie nach
   unten: die Dauer als Bild, nicht als Rechnung. */
.kal-faden {
  position: relative;
  width: 8px; align-self: stretch; flex: none;
  margin: 6px 0;
}
.kal-faden::before {
  content: '';
  position: absolute; left: 1px; top: 2px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--rolle, #999);
}
.kal-faden::after {
  content: '';
  position: absolute; left: 4px; top: 10px; bottom: 0;
  width: 2px; border-radius: 1px;
  background: color-mix(in srgb, var(--rolle, #999) 45%, transparent);
}
/* Das Bild macht die Zeile ansehnlich, klein genug bleibt sie trotzdem: Eine
   Tagesliste ist kein Entdecken-Strom. */
.kal-bild {
  width: 54px; height: 42px; flex: none;
  border-radius: var(--radius-control);
  background: linear-gradient(150deg, var(--card-a, #40576B), var(--card-b, #7C93A6));
  background-size: cover; background-position: center;
}
/* Was vorbei ist, steht blasser da -- es gehoert in den Monat, draengt sich
   aber nicht auf. */
.kal-zeile.vorbei { opacity: .62; }
.kal-inhalt { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.kal-inhalt strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kal-unten {
  display: flex; align-items: center; gap: 5px;
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kal-unten svg.i { width: 14px; height: 14px; flex: none; }
.kal-pfeil { width: 18px; height: 18px; flex: none; color: var(--color-text-tertiary); }

/* „Neuen Termin erstellen" -- gestrichelt, weil dort noch nichts ist. */
.kal-neu {
  display: flex; align-items: center; justify-content: center; gap: var(--spacing-sm);
  margin-top: var(--spacing-md);
  padding: var(--spacing-lg);
  border: 1px dashed var(--color-border-subtle);
  border-radius: var(--radius-card);
  text-decoration: none;
  font: var(--typography-action);
  color: var(--color-text-secondary);
}
.kal-neu:hover {
  border-color: var(--color-brand-primary-action);
  color: var(--color-brand-primary-action);
  background: var(--color-selection-background);
}
.kal-neu svg.i { width: 18px; height: 18px; }

/* Ein freier Tag ist kein Fehler. Er sieht deshalb auch nicht so aus. */
.kal-frei {
  display: flex; flex-direction: column; align-items: center; gap: var(--spacing-sm);
  padding: var(--spacing-section) var(--spacing-lg);
  text-align: center;
}
.kal-frei-zeichen {
  display: grid; place-items: center;
  width: 52px; height: 52px;
  border-radius: 50%;
  background: var(--color-selection-background);
  color: var(--color-brand-primary-action);
}
.kal-frei b { font: var(--typography-section-title); }
.kal-frei p { margin: 0; max-width: 42ch; color: var(--color-text-secondary); }
.kal-frei .btn { margin-top: var(--spacing-sm); }

@media (max-width: 560px) {
  .kal-tag { min-height: 44px; }
  /* Auf dem Telefon hat der Titel Vorrang. Die Marke stand daneben und nahm
     ihm die halbe Zeile -- „Nachbarschaftsfest im Hof" hiess dann
     „Nachbarsch…". Sie rutscht jetzt unter die Zeile, wenn es eng wird. */
  .kal-zeile { gap: var(--spacing-sm); flex-wrap: wrap; }
  .kal-marke { order: 3; margin-left: 0; }
  .kal-inhalt { flex: 1 1 100%; order: 2; }
  .kal-bild { width: 46px; height: 40px; }
  .kal-pfeil { order: 4; margin-left: auto; }
  .kal-naechster { gap: var(--spacing-md); }
  .kal-naechster-bild { width: 72px; height: 60px; }
}


/* ================================================================ Die Marken
 *
 * Was der Server als Marke schickt -- „Vor Ort", „12 Plaetze frei",
 * „Einladung" --, sieht ueberall gleich aus. Die Regeln lagen im Stilblatt des
 * Entdeckens; der Kalender im Profil laedt es nicht, und seine Marken standen
 * dort als nackter Text mitten in der Zeile.
 *
 * Welcher Ton gilt, sagt der Server. Hier steht nur, wie er aussieht: vier
 * Toene fuer vier Aussagen -- reichlich Platz, es wird knapp, es wird dringend,
 * und es ist vorbei.
 */
.pill { font: var(--typography-caption); font-weight: 600; padding: 3px 10px; border-radius: 999px; }
.pill[class*="ton-"] { display: inline-flex; align-items: center; gap: 5px; }
.pill[class*="ton-"] svg.i { width: 13px; height: 13px; }
.pill.ton-positive { background: var(--color-status-success-surface); color: var(--color-status-success); }
.pill.ton-warning { background: var(--color-status-warning-surface); color: var(--color-status-warning); }
.pill.ton-critical { background: var(--color-status-error-surface); color: var(--color-status-error); }
.pill.ton-neutral { background: var(--color-surface-secondary); color: var(--color-text-secondary); }
/* Der Teilnahmestand kennt noch drei eigene Toene: „Du bist dabei",
   „entscheide dich", „vorbei". Sie kommen aus demselben Vertrag und standen
   bisher nirgends als Marke -- im Kalender tun sie es. */
.pill.ton-settled { background: var(--color-status-success-surface); color: var(--color-status-success); }
.pill.ton-attention { background: var(--color-status-warning-surface); color: var(--color-status-warning); }
.pill.ton-ended { background: var(--color-surface-secondary); color: var(--color-text-tertiary); }


/* ========================================================= Hilfe & Diagnose
 *
 * Drei Kacheln untereinander: melden, exportieren, Kennungen. Ruhig gehalten
 * -- wer hier landet, hat schon ein Problem und braucht keine Gestaltung, die
 * auch noch etwas will.
 */
.hilfe-block { display: flex; flex-direction: column; gap: var(--spacing-md);
  padding: 0 var(--spacing-lg) var(--spacing-lg); }
.hilfe-block .btn { align-self: flex-start; }
.hilfe-zeile {
  display: flex; align-items: center; gap: var(--spacing-sm);
  margin: 0;
  font: var(--typography-body);
  color: var(--color-text-secondary);
}
.hilfe-zeile svg.i { width: 16px; height: 16px; flex: none; }
.hilfe-zeile.quiet { color: var(--color-text-tertiary); }

.hilfe-auszuege { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; }
.hilfe-auszug {
  display: flex; align-items: center; gap: var(--spacing-md);
  padding: var(--spacing-md) 0;
  border-top: 1px solid var(--color-border-subtle);
}
.hilfe-auszug:first-child { border-top: 0; }
.hilfe-auszug-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.hilfe-auszug-text span { font: var(--typography-metadata); color: var(--color-text-tertiary); }
.hilfe-auszug .btn { margin-left: auto; }

/* Die Kennung steht in Schreibmaschinenschrift: Sie wird vorgelesen,
   abgetippt und verglichen -- dafuer muss man 1 und l unterscheiden. */
.hilfe-kennung {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: var(--spacing-md);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: var(--color-surface-secondary);
}
.hilfe-kennung-kopf { font: var(--typography-action); }
.hilfe-kennung code {
  font-family: var(--font-family-numeric, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: .86rem;
  word-break: break-all;
  user-select: all;
  color: var(--color-text-primary);
}
.hilfe-kennung .hint { font: var(--typography-metadata); color: var(--color-text-tertiary); }

@media (max-width: 560px) {
  /* Auf dem Telefon steht das Datum ueber der Marke und dem Knopf, statt sich
     mit ihnen um dieselbe Zeile zu streiten -- „25.08.2026," in einer Zeile
     und „19:44" in der naechsten liest niemand gern. */
  .hilfe-auszug { flex-wrap: wrap; }
  .hilfe-auszug-text { flex: 1 1 100%; }
  .hilfe-auszug .btn { margin-left: auto; }
}


/* Titel und Beisatz einer Kachelkopfzeile: untereinander, der Beisatz leiser.
   Nebeneinander ergaben sie einen Satz, der mitten im Titel umbrach --
   „Datenexport Alles, was hier ueber dich" in der ersten Zeile. */
.panel > header .head-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.panel > header .head-text .lead { font: var(--typography-metadata); font-weight: 400; }


/* ============================================================ Tafel fuer Gaeste
 *
 * Eine Tafel, ueberall dieselbe: Zeichen, Ansage, der Weg hinein, der Weg
 * daran vorbei -- und die Zusage darunter, dass die Daten sicher liegen. Eine
 * Sperre, die auf jeder Seite anders aussieht, liest sich wie ein Fehler und
 * nicht wie eine Regel.
 */
.gasttafel {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--spacing-md);
  max-width: 30rem;
  margin: var(--spacing-section) auto;
  padding: var(--spacing-section) var(--spacing-xl);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: var(--color-surface-primary);
  box-shadow: var(--elevation-card);
  text-align: center;
}
/* Person mit Schloss: Das Zeichen sagt in einem Bild, worum es geht -- ein
   Konto, das zu ist. Zwei Zeichen aus dem eigenen Satz, uebereinandergelegt;
   ein drittes Motiv dafuer zu zeichnen waere eins zu viel. */
.gasttafel-zeichen {
  position: relative;
  display: grid; place-items: center;
  width: 84px; height: 84px;
  margin-bottom: var(--spacing-sm);
  border-radius: 50%;
  background: var(--color-selection-background);
  color: var(--color-brand-primary-action);
}
.gasttafel-zeichen > svg.i { width: 38px; height: 38px; }
.gasttafel-schloss {
  position: absolute; right: -2px; bottom: 2px;
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: var(--color-selection-background);
  box-shadow: 0 0 0 4px var(--color-surface-primary);
}
.gasttafel-schloss svg.i { width: 18px; height: 18px; }
.gasttafel h1 { margin: 0; font: var(--typography-screen-title); }
.gasttafel-text { margin: 0; max-width: 32ch; color: var(--color-text-secondary); }
.gasttafel-knopf { min-width: min(18rem, 100%); justify-content: center; }
/* „oder" mit einer Linie links und rechts -- der Strich trennt die beiden
   Wege, ohne dass einer davon wie ein Fehler aussieht. */
.gasttafel-oder {
  display: flex; align-items: center; gap: var(--spacing-md);
  width: min(18rem, 100%);
  margin: 0;
  font: var(--typography-metadata);
  color: var(--color-text-tertiary);
}
.gasttafel-oder::before, .gasttafel-oder::after {
  content: ''; flex: 1; height: 1px;
  background: var(--color-border-subtle);
}
.gasttafel-fuss {
  display: flex; align-items: center; gap: 6px;
  margin: var(--spacing-sm) 0 0;
  font: var(--typography-metadata);
  color: var(--color-text-tertiary);
}
.gasttafel-fuss svg.i { width: 15px; height: 15px; }

/* --------------------------------------------------- Bereich „Veranstalten"

   Die Startseite des Bereichs: die Wege nebeneinander, darunter das, was als
   Naechstes ansteht. Die Kacheln sind gleich gross und tragen ihre Farbe nur
   im Zeichen -- eine Wand aus fuenf farbigen Flaechen liest sich als Reklame,
   nicht als Menue. */
.vk-kopf {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-lg);
  margin-bottom: var(--spacing-xl);
}
.vk-kopf .grow { flex: 1; min-width: 0; }
.vk-kopf h1 { margin: 0 0 var(--spacing-xs); }
.vk-kopf p { margin: 0; }
/* Kacheln, Werkzeugleiste und Liste als Spalte -- mit demselben Abstand, den
   `#main` auf der eigenen Seite zwischen Kopf und Rumpf laesst. */
.vk-inhalt { display: flex; flex-direction: column; gap: var(--spacing-lg); }
.vk-gitter {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--spacing-lg);
}
.vk-kachel {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
  padding: var(--spacing-lg);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: var(--color-surface-primary);
  text-decoration: none;
  color: inherit;
}
.vk-kachel:hover { border-color: var(--color-brand-primary-action); }
.vk-kachel strong { font: var(--typography-card-title); }
.vk-kachel .quiet { font: var(--typography-metadata); color: var(--color-text-secondary); }
.vk-zeichen {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: var(--radius-card);
  margin-bottom: var(--spacing-sm);
  background: var(--color-selection-background);
  color: var(--color-brand-primary-action);
}
/* Der Pfeil sitzt in der Ecke und ruecht beim Zeigen -- dieselbe Geste wie in
   den Zeilen darunter, damit beide als „hier geht es weiter" gelesen werden. */
.vk-pfeil {
  position: absolute;
  top: var(--spacing-lg); right: var(--spacing-lg);
  color: var(--color-text-tertiary);
  transition: transform .15s ease;
}
.vk-kachel:hover .vk-pfeil { transform: translateX(3px); }
/* Je Weg eine eigene Farbe -- nicht als Schmuck, sondern damit die Kacheln
   beim zweiten Besuch am Fleck wiedererkannt werden, bevor man liest. Nur das
   Zeichen traegt sie; die Flaeche bleibt ruhig. */
.vk-kachel.ton-rot .vk-zeichen {
  background: var(--color-status-error-surface); color: var(--color-status-error);
}
.vk-kachel.ton-gelb .vk-zeichen {
  background: var(--color-status-warning-surface); color: var(--color-status-warning);
}
.vk-kachel.ton-gruen .vk-zeichen {
  background: var(--color-status-success-surface); color: var(--color-status-success);
}
.vk-kachel.ton-blau .vk-zeichen {
  background: var(--color-status-info-surface); color: var(--color-status-info);
}
.vk-kachel.ton-violett .vk-zeichen {
  background: var(--color-selection-background); color: var(--color-brand-secondary-action);
}
@media (prefers-reduced-motion: reduce) {
  .vk-pfeil { transition: none; }
}
.vk-naechste header { display: flex; align-items: baseline; }
.vk-naechste header .hint { margin-left: auto; }
/* Dieselbe Karte wie unter „Aktivität" -- sie bringt ihren eigenen Rahmen mit
   und braucht hier nur Luft, keinen zweiten Kasten. */
.vk-karten {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
  padding: var(--spacing-lg);
}

/* Der Fuss einer Katalogzeile: der Schluessel links, die Handlungen rechts.
   Sie stehen nebeneinander und nicht in einem Menue -- „Verweigern" ist der
   einzige Weg, ein Recht loszuwerden, das aus einer Rolle kommt, und wer es
   erst suchen muss, entzieht stattdessen die ganze Rolle. */
.rk-fuss { display: flex; align-items: center; gap: var(--spacing-sm); margin-top: var(--spacing-sm); }
.rk-fuss code { flex: 1; min-width: 0; }
.rk-fuss .btn { min-height: 30px; font-size: .78rem; padding: 0 var(--spacing-md); }

/* -------------------------------------------------------- Rollen & Rechte

   Zwei Spalten: links die Rollen, rechts ihre Rechte. Auf schmalen Flaechen
   untereinander -- eine Liste neben einer Liste ist auf dem Telefon zweimal
   zu wenig Platz. */
.rollen-satz {
  display: grid;
  grid-template-columns: minmax(280px, 380px) 1fr;
  gap: var(--spacing-lg);
  align-items: start;
}
@media (max-width: 900px) {
  .rollen-satz { grid-template-columns: 1fr; }
}
.rollen-hinweis { padding: 0 var(--spacing-lg) var(--spacing-md); margin: 0; }
.rollen-liste { list-style: none; margin: 0; padding: 0; }
.rollen-liste li { border-top: 1px solid var(--color-border-subtle); }
/* Nur die Zeile selbst, nicht jeder Knopf darin: Die Regel erwischte auch den
   Menueknopf, machte ihn zeilenbreit -- und `right: 8px` schob ihn damit an
   den linken Rand statt an den rechten. */
.rollen-liste li > button[data-vorlage] {
  display: flex;
  gap: var(--spacing-md);
  width: 100%;
  padding: var(--spacing-md) var(--spacing-lg);
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.rollen-liste li.an { background: var(--color-selection-background); }
.rollen-liste li.an > button[data-vorlage] { box-shadow: inset 3px 0 0 var(--color-brand-primary-action); }
/* Die Zeile traegt das Menue: Der Knopf sitzt darin, das Blatt darunter. */
.rollen-liste li { position: relative; }
.rollen-liste li > button[data-vorlage] { padding-right: 44px; }
.rl-mehr {
  position: absolute;
  top: var(--spacing-md); right: var(--spacing-sm);
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border: 0; border-radius: 999px;
  background: none;
  color: var(--color-text-tertiary);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.rl-mehr:hover, .rl-mehr[aria-expanded="true"] {
  background: var(--color-selection-background);
  color: var(--color-text-primary);
}
.rl-menue {
  position: absolute;
  top: 44px; right: var(--spacing-sm);
  z-index: 3;
  min-width: 200px;
  display: grid;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: var(--color-surface-primary);
  box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
  overflow: hidden;
}
.rl-menue button {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-md);
  border: 0;
  background: none;
  font: var(--typography-action);
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.rl-menue button:hover { background: var(--color-selection-background); }
.rl-menue button svg { width: 18px; height: 18px; color: var(--color-text-tertiary); }
/* Was nicht rueckgaengig zu machen ist, sieht auch so aus. */
.rl-menue button.gefahr { color: var(--color-status-error); }
.rl-menue button.gefahr svg { color: var(--color-status-error); }
.rl-menue button.gefahr:hover { background: var(--color-status-error-surface); }
.rollen-liste strong { display: block; font: var(--typography-card-title); }
.rollen-liste .quiet { display: block; font: var(--typography-metadata); color: var(--color-text-secondary); }
.rollen-liste .meta { display: block; font: var(--typography-metadata); color: var(--color-text-tertiary); margin-top: 2px; }
.rl-zeichen {
  width: 36px; height: 36px; flex: none;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--color-status-error-surface);
  color: var(--color-status-error);
}
.rollen-fuss {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-md) var(--spacing-lg);
  border-top: 1px solid var(--color-border-subtle);
}
.rollen-fuss.abschluss { position: sticky; bottom: 0; justify-content: flex-end; background: var(--color-surface-primary); }
.rollen-formular { padding: var(--spacing-md) var(--spacing-lg); display: grid; gap: var(--spacing-md); }
.rollen-formular .rollen-fuss { padding: 0; border: 0; }
.rl-alle { margin-left: auto; display: flex; align-items: center; gap: var(--spacing-sm); font: var(--typography-metadata); }
/* Eine Gruppe je Zusammenhang -- „Teilnahme", „Warteliste", „Kommunikation".
   Zweiunddreissig Rechte in einer Reihe sind eine Wand. */
.rl-gruppe { border-top: 1px solid var(--color-border-subtle); }
.rl-gruppe h3 {
  display: flex; align-items: center; gap: var(--spacing-sm);
  margin: 0;
  padding: var(--spacing-md) var(--spacing-lg);
  font: var(--typography-action);
  color: var(--color-text-secondary);
}
.rl-gruppe h3 svg { width: 18px; height: 18px; color: var(--color-brand-primary-action); }
.rl-recht {
  display: flex; align-items: flex-start; gap: var(--spacing-md);
  padding: var(--spacing-sm) var(--spacing-lg) var(--spacing-md);
  cursor: pointer;
}
.rl-recht:hover { background: var(--color-surface-secondary, var(--color-selection-background)); }
.rl-recht strong { display: block; font-weight: 600; }
.rl-recht .quiet { display: block; font: var(--typography-metadata); color: var(--color-text-secondary); }
.rl-recht input { margin-top: 4px; flex: none; }
.rl-recht .btn { flex: none; min-height: 30px; font-size: .78rem; padding: 0 var(--spacing-md); }

/* ------------------------------------------------ Die Rechte einer Rolle

   Je Zusammenhang eine Karte, die sich auf- und zuklappen laesst. Gestrichen
   wird mit einem Zeichen, nicht mit einem Schalter: Ein Schalter behauptet, es
   gaebe hier ein „aus" -- es gibt nur „gehoert dazu" und „steht gleich nicht
   mehr da". */
.rl-gruppen { padding: 0 var(--spacing-lg) var(--spacing-lg); display: grid; gap: var(--spacing-md); }
.rl-karte {
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.rl-kopf {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  width: 100%;
  padding: var(--spacing-md);
  border: 0;
  background: var(--color-surface-secondary, var(--color-selection-background));
  font: var(--typography-action);
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.rl-karte.offen .rl-kopf { border-bottom: 1px solid var(--color-border-subtle); }
/* Name und Zahl bleiben beieinander, der Pfeil geht an die Kante: Er zeigt
   nicht auf den Namen, sondern auf die Karte -- und Pfeile, die in fuenf
   Zeilen an fuenf verschiedenen Stellen stehen, sind keine Spalte, sondern
   Zufall. `.grow` traegt hier kein `flex: 1`; das steht nur in den Listen. */
.rl-kopf .grow { flex: 0 1 auto; min-width: 0; }
.rl-pfeil {
  margin-left: auto;
  color: var(--color-text-tertiary);
  display: grid;
  place-items: center;
}
.rl-gruppenzeichen {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--color-selection-background);
  color: var(--color-brand-primary-action);
}
.rl-gruppenzeichen.ton-participation { background: var(--color-status-error-surface); color: var(--color-status-error); }
.rl-gruppenzeichen.ton-waitlist { background: var(--color-status-info-surface); color: var(--color-status-info); }
.rl-gruppenzeichen.ton-communication { background: var(--color-status-success-surface); color: var(--color-status-success); }
.rl-gruppenzeichen.ton-schedulingPoll { background: var(--color-status-warning-surface); color: var(--color-status-warning); }

/* Ein Raster statt einer Reihe: So stehen die beiden Zeichen in jeder Zeile
   an derselben Stelle, auch wenn der Text darueber einmal eine und einmal drei
   Zeilen braucht. Als Flexzeile rutschten sie mit -- und dann steht das „x“
   der einen Zeile neben dem Text der naechsten. */
.rl-zeile {
  display: grid;
  grid-template-columns: 1fr 34px 34px;
  align-items: center;
  gap: var(--spacing-md);
  padding: var(--spacing-md);
  border-top: 1px solid var(--color-border-subtle);
}
.rl-karte .rl-zeile:first-of-type { border-top: 0; }
.rl-zeile strong { font-weight: 600; }
.rl-zeile .quiet { display: block; font: var(--typography-metadata); color: var(--color-text-secondary); }
.rl-zeile .quiet { display: block; font: var(--typography-metadata); color: var(--color-text-secondary); }
/* Gestrichen heisst durchgestrichen -- und bleibt lesbar, denn bis zum
   Speichern kann man es sich anders ueberlegen. */
.rl-zeile.gestrichen strong,
.rl-zeile.gestrichen .quiet { text-decoration: line-through; color: var(--color-text-tertiary); }
.rl-marke {
  margin-left: var(--spacing-sm);
  padding: 1px var(--spacing-sm);
  border-radius: 999px;
  font: var(--typography-metadata);
  background: var(--color-status-error-surface);
  color: var(--color-status-error);
}
.rl-marke.neu { background: var(--color-status-success-surface); color: var(--color-status-success); }
.rl-marke.wartet { background: var(--color-border-subtle); color: var(--color-text-secondary); }
.rl-knopf {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 0; border-radius: 999px;
  background: none;
  color: var(--color-text-tertiary);
  cursor: pointer;
}
.rl-knopf[data-weg]:hover { background: var(--color-status-error-surface); color: var(--color-status-error); }
.rl-zeile.gestrichen .rl-knopf[data-weg]:hover { background: var(--color-status-success-surface); color: var(--color-status-success); }
.rl-knopf[data-info]:hover,
.rl-knopf[aria-expanded="true"] { background: var(--color-selection-background); color: var(--color-brand-primary-action); }
.rl-knopf svg { width: 18px; height: 18px; }

/* Die Beschreibung liegt unter ihrer Zeile und nicht in einer Sprechblase:
   Sie darf mehrzeilig sein, und ein Fenster, das beim Wegsehen verschwindet,
   liest niemand zu Ende. */
.rl-info {
  padding: 0 var(--spacing-md) var(--spacing-md) var(--spacing-md);
  background: var(--color-surface-secondary, var(--color-selection-background));
  border-top: 1px solid var(--color-border-subtle);
}
.rl-info p { margin: var(--spacing-md) 0; font: var(--typography-metadata); color: var(--color-text-secondary); }
/* Die Zahl im Kopf: wie viele Rechte in diesem Bereich zur Rolle gehoeren.
   Auch die Null steht da -- sie ist die Auskunft, dass hier nichts vergeben
   ist, und nicht das Fehlen einer Auskunft. */
.rl-zahl {
  min-width: 22px;
  padding: 1px var(--spacing-sm);
  border-radius: 999px;
  background: var(--color-border-subtle);
  color: var(--color-text-secondary);
  font: var(--typography-metadata);
  text-align: center;
}

/* Der Weg zum Katalog -- gestrichelt, weil hier nichts steht, sondern etwas
   dazukommt. */
.rl-hinzu {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  width: 100%;
  padding: var(--spacing-md);
  border: 1px dashed var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: none;
  font: var(--typography-action);
  color: var(--color-brand-primary-action);
  cursor: pointer;
}
.rl-hinzu:hover:not(:disabled) { border-color: var(--color-brand-primary-action); }
.rl-hinzu:disabled { opacity: .5; cursor: default; }
/* „Fertig“ schliesst den Katalog und steht deshalb im Kopf, wo sonst die
   Zahl steht -- ein Knopf am Fuss einer langen Liste waere weg, sobald man
   das dritte Recht gesucht hat. */
.panel header .hint {
  margin-left: auto;
  border: 0;
  background: none;
  font: inherit;
  color: var(--color-brand-primary-action);
  cursor: pointer;
}

/* ------------------------------------------- Fenster „Recht hinzufuegen"

   Links die Bereiche, rechts ihre Rechte, darueber eine Suche quer durch
   alle. Als `<dialog>`: Fokusfalle und Escape bringt der Browser mit. */
/* Eine Spalte mit fester Hoehe: Kopf, Suche, Liste, Fuss. Nur die Liste
   waechst und scrollt -- vorher wuchs das ganze Fenster ueber den Bildschirm
   hinaus, und der Fuss lag mitten in den Zeilen. */
.rf {
  display: flex;
  flex-direction: column;
  width: min(680px, 92vw);
  max-height: min(86vh, 720px);
  padding: 0;
  border: 0;
  border-radius: var(--radius-card);
  background: var(--color-surface-primary);
  color: var(--color-text-primary);
  overflow: hidden;
}
/* Ein Blatt, das man groesser ziehen kann.
 *
 * Zwei Blaetter fuehren eine Liste in zwei Spalten -- „Rollen oder Rechte
 * vergeben" und „Recht hinzufuegen". In der Vorgabe (680 Punkte, 86vh) blieben
 * fuer die Rechte ein paar Zeilen uebrig: Wer einen Bereich mit zehn Rechten
 * waehlte, sah drei davon und musste rollen. Also eine Mindestgroesse, unter
 * die es nicht faellt, und ein Griff an der Ecke fuer den, dem auch das nicht
 * reicht.
 *
 * `resize` braucht einen Ueberlauf, und `.rf` hat ihn (`hidden`). Die
 * Obergrenzen bleiben: Ein Blatt, das man aus dem Fenster hinausziehen kann,
 * waere kein Zugewinn.
 */
.rf.gross {
  width: min(820px, 94vw);
  min-width: min(34rem, 94vw);
  height: min(38rem, 90vh);
  min-height: min(28rem, 90vh);
  max-width: 94vw;
  max-height: 90vh;
  resize: both;
}
/* Der Griff sitzt in der Ecke, in der auch der bestaetigende Knopf steht --
   die Fusszeile bekommt rechts Luft, damit beides nebeneinander passt. */
.rf.gross .rf-fuss { padding-right: var(--spacing-xl); }

.rf header, .rf-suche, .rf-fuss { flex: none; }
.rf::backdrop { background: rgb(0 0 0 / 35%); }
.rf header { padding: var(--spacing-lg) var(--spacing-lg) var(--spacing-md); }
.rf header h2 { margin: 0 0 var(--spacing-xs); }
/* Ein Wort statt eines Zeichens: Das „x“ sass neben zwei Zeilen Text und
   damit nirgends richtig -- oben zu hoch, mittig zu tief. Ein Wort steht an
   der Oberkante und sagt ausserdem, was es tut. */
.rf-zu {
  flex: none;
  align-self: flex-start;
  border: 0;
  padding: 0;
  background: none;
  font: var(--typography-action);
  color: var(--color-brand-primary-action);
  cursor: pointer;
}
.rf-zu:hover { text-decoration: underline; }
.rf header p { margin: 0; }
.rf-suche {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  margin: 0 var(--spacing-lg) var(--spacing-md);
  padding: 0 var(--spacing-md);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  color: var(--color-text-tertiary);
}
.rf-suche svg { width: 18px; height: 18px; flex: none; }
.rf-suche input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: var(--spacing-md) 0;
  background: none;
  font: inherit;
  color: inherit;
}
.rf-suche input:focus { outline: none; }
/* Dieselbe Kante wie Titel und Suche, links wie rechts: Die Spalten stiessen
   vorher an den Rand des Fensters, und dann steht der Bereichsname weiter
   links als die Ueberschrift darueber -- zwei Kanten in einem Fenster, und
   keine davon sieht gewollt aus. */
.rf-satz {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: var(--spacing-md);
  margin: 0 var(--spacing-lg);
  padding-top: var(--spacing-md);
  border-top: 1px solid var(--color-border-subtle);
  flex: 1;
  min-height: 0;
}
.rf-bereiche {
  border-right: 1px solid var(--color-border-subtle);
  overflow-y: auto;
  padding: 0 var(--spacing-md) 0 0;
}
.rf-bereiche button {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  width: 100%;
  border-radius: var(--radius-card);
  padding: var(--spacing-sm);
  border: 0;
  background: none;
  font: var(--typography-metadata);
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.rf-bereiche button.an { background: var(--color-selection-background); font-weight: 600; }
.rf-bereiche .rl-gruppenzeichen { width: 26px; height: 26px; }
.rf-bereiche .rl-gruppenzeichen svg { width: 15px; height: 15px; }
.rf-rechte { overflow-y: auto; }
.rf-rechte > .quiet { padding: var(--spacing-lg) 0; margin: 0; }
/* Eine Zeile: Haekchen, Name, Pfeil. Die Beschreibung liegt darunter und
   klappt auf -- als `<details>`, damit kein Neuzeichnen noetig ist und die
   Liste beim Aufklappen nicht an den Anfang springt. Sie darf lang sein: Was
   ein Recht bedeutet, laesst sich selten in einer Zeile sagen. */
.rf-recht {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-md);
  padding: var(--spacing-sm);
  border-radius: var(--radius-card);
}
.rf-recht:hover { background: var(--color-surface-secondary, var(--color-selection-background)); }
/* Haekchen und Wort auf einer Linie.

   Vorher stand hier ein geratener Abstand von neun Punkten -- er passte zu
   einer Schriftgroesse und verrutschte mit jeder anderen. Jetzt ist beides
   ausdruecklich: Die Zeile des Namens ist 34 Punkte hoch, das Haekchen 16 --
   also sitzt es (34 - 16) / 2 = 9 Punkte unter der Oberkante. Dieselbe
   Rechnung, aber mit Zahlen, die im Stilblatt stehen und nicht im Zufall. */
.rf-recht input { flex: none; align-self: flex-start; width: 16px; height: 16px; margin-top: 9px; }
.rf-details summary { min-height: 34px; }
/* In der Uebersicht gibt es nichts auszuwaehlen -- die Zeile beginnt dann
   dort, wo sonst das Haekchen sitzt. */
.rf-recht.ohne-wahl { padding-left: var(--spacing-sm); }
/* Die Uebersicht steht in einer Kachel und braucht deshalb den Abstand des
   Fensters nicht -- aber dieselbe Aufteilung. */
.rf-satz.uebersicht { margin: 0; padding: var(--spacing-lg); border-top: 0; max-height: 420px; }
.rf-satz.uebersicht .rf-bereiche button { font: var(--typography-metadata); }
.rf-satz.uebersicht .rl-zahl { margin-left: auto; }
.rf-bereiche .rl-zahl { flex: none; margin-left: auto; }
/* Eine Null tritt zurueck: In diesem Bereich ist nichts mehr zu holen, und das
   soll man sehen, ohne dass es die Aufmerksamkeit derer bekommt, wo noch etwas
   liegt. */
.rf-bereiche .rl-zahl.leer { background: none; color: var(--color-text-tertiary); }
.rf-details { flex: 1; min-width: 0; }
.rf-details summary {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: 0;
  list-style: none;
  cursor: pointer;
  font-weight: 600;
}
.rf-details summary::-webkit-details-marker { display: none; }
.rf-details summary label,
.rf-details summary > .grow { flex: 1; min-width: 0; cursor: pointer; }
.rf-pfeil { flex: none; color: var(--color-text-tertiary); display: grid; place-items: center; }
.rf-pfeil svg { width: 16px; height: 16px; transition: transform .15s ease; }
.rf-details[open] .rf-pfeil svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .rf-pfeil svg { transition: none; } }
.rf-details p {
  margin: 0 0 var(--spacing-sm);
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
}
.rf-fuss {
  display: flex;
  justify-content: flex-end;
  gap: var(--spacing-sm);
  padding: var(--spacing-lg);
  border-top: 1px solid var(--color-border-subtle);
}
@media (max-width: 600px) {
  .rf-satz { grid-template-columns: 1fr; }
  .rf-bereiche { display: flex; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--color-border-subtle); }
  .rf-bereiche button { width: auto; white-space: nowrap; }
}

/* Das Fenster fuer eine neue Rolle: dasselbe Fenster, nur schmaler und ohne
   Liste. Ein Feld, ein Hinweis, zwei Knoepfe. */
.rf.schmal { width: min(420px, 92vw); max-height: none; }
.rf.schmal form { display: contents; }
.rf-feld { padding: 0 var(--spacing-lg) var(--spacing-lg); }
.rf-feld .feld { display: block; margin-bottom: var(--spacing-sm); }
.rf-feld input {
  width: 100%;
  padding: var(--spacing-md);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  background: var(--color-surface-primary);
  font: inherit;
  color: inherit;
}
.rf-feld input:focus-visible { outline: 2px solid var(--color-brand-primary-action); outline-offset: 1px; }
.pflicht { color: var(--color-status-error); }
/* Der Hinweis nimmt die Frage vorweg, die an dieser Stelle aufkommt: „und die
   Rechte?" -- er steht deshalb im Feld und nicht im Fuss. */
.rf-tipp {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-sm);
  margin: var(--spacing-md) 0 0;
  padding: var(--spacing-md);
  border-radius: var(--radius-card);
  background: var(--color-status-warning-surface);
  color: var(--color-status-warning);
  font: var(--typography-metadata);
}
.rf-tipp svg { width: 18px; height: 18px; flex: none; }

/* ------------------------------------------------------ Zuweisungen

   Eine Tafel: Wer hat an welcher Veranstaltung was, und seit wann. Keine
   Karten -- hier wird verglichen und gesucht, nicht gestoebert, und dafuer
   sind Spalten das richtige Mittel. */
.zw-filter .feldzeile { flex-wrap: wrap; gap: var(--spacing-md); padding: var(--spacing-lg); }
.zw-filter .feld.wachsend { flex: 1; min-width: 220px; }
.unsichtbar {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.zw-tafel { width: 100%; border-collapse: collapse; }
.zw-tafel th, .zw-tafel td {
  padding: var(--spacing-md);
  text-align: left;
  border-top: 1px solid var(--color-border-subtle);
  vertical-align: middle;
}
.zw-tafel th {
  font: var(--typography-metadata);
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.zw-tafel tbody tr:hover { background: var(--color-surface-secondary, var(--color-selection-background)); }
.zw-tafel strong { font-weight: 600; }
.zw-zeit { font-variant-numeric: tabular-nums; color: var(--color-text-secondary); white-space: nowrap; }
.zw-tun { text-align: right; }
.zw-tun .btn { min-height: 30px; font-size: .78rem; padding: 0 var(--spacing-md); }
.zw-leer { color: var(--color-text-tertiary); text-align: center; }
.zw-fuss { padding: var(--spacing-md) var(--spacing-lg); margin: 0; border-top: 1px solid var(--color-border-subtle); }
/* Rolle und Einzelrecht sehen verschieden aus: Das eine ist ein Buendel mit
   Namen, das andere eine einzelne Erlaubnis. */
.zw-rolle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px var(--spacing-sm);
  border-radius: 999px;
  background: var(--color-selection-background);
  color: var(--color-brand-primary-action);
  font: var(--typography-metadata);
  white-space: nowrap;
}
.zw-rolle.einzeln { background: var(--color-border-subtle); color: var(--color-text-secondary); }
.zw-rolle svg { width: 14px; height: 14px; }
@media (max-width: 720px) {
  .zw-tafel thead { display: none; }
  .zw-tafel tr { display: grid; gap: 2px; padding: var(--spacing-sm) 0; }
  .zw-tafel td { border: 0; padding: 2px var(--spacing-lg); }
  .zw-tafel tr + tr { border-top: 1px solid var(--color-border-subtle); }
  .zw-tun { text-align: left; }
}

/* ------------------------------------------------- Rollen: der Leerzustand

   Kein Kasten mit einer grauen Zeile, sondern ein Anfang: Bild, Satz, Knopf.
   Und darunter die Frage, die an dieser Stelle wirklich aufkommt -- „was ist
   das ueberhaupt" -- statt einer Aufforderung ins Leere. */
.rl-leer {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--spacing-xl) var(--spacing-lg);
  gap: var(--spacing-md);
}
.rl-leer h2 { margin: 0; font: var(--typography-card-title); font-size: 1.15rem; }
.rl-leer p { margin: 0; max-width: 42ch; }
.rl-bild {
  width: 96px; height: 96px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--color-status-error-surface);
  color: var(--color-status-error);
  margin-bottom: var(--spacing-sm);
}
.rl-bild svg { width: 44px; height: 44px; }
.rl-leer.klein { padding: var(--spacing-xl) var(--spacing-lg); }
.rl-leer.klein .rl-bild { width: 64px; height: 64px; }
.rl-leer.klein .rl-bild svg { width: 28px; height: 28px; }
.rl-leer.klein h2 { font-size: 1rem; }
/* Der Hinweis steht als eigener Kasten und nicht als dritter Absatz: Er
   beantwortet etwas anderes als der Text darueber -- nicht „was tue ich
   jetzt", sondern „was ist das". */
.rl-was {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-md);
  width: 100%;
  margin-top: var(--spacing-md);
  padding: var(--spacing-md);
  border-radius: var(--radius-card);
  background: var(--color-status-error-surface);
  text-align: left;
}
.rl-was strong { display: block; margin-bottom: 2px; }
.rl-was .quiet { display: block; font: var(--typography-metadata); color: var(--color-text-secondary); }
.rl-was-zeichen {
  width: 34px; height: 34px; flex: none;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--color-surface-primary);
  color: var(--color-status-error);
}
.rl-was-zeichen svg { width: 18px; height: 18px; }


/* Der Anmeldeschluss auf einer Karte -- eine Zeile wie die anderen, aber mit
   ihrem eigenen Ton: Draengt sie, steht sie in der Warnfarbe, sonst ruhig wie
   Ort und Zeit daneben. Eine geschlossene Anmeldung bleibt sichtbar; sie
   erklaert, warum es hier nichts mehr zu tun gibt. */
.event-card .line.frist { color: var(--color-text-secondary); }
.event-card .line.frist time { font-variant-numeric: tabular-nums; }
.event-card .line.frist.draengt { color: var(--color-status-error); font-weight: 600; }
.event-card .line.frist.draengt svg.i { color: var(--color-status-error); }
.event-card .line.frist.zu { color: var(--color-text-tertiary); }

/* Die Seite bleibt unsichtbar, bis die Woerter der gewaehlten Sprache da
   sind -- gesetzt im Kopf jeder Seite, geloescht von texts.js, sobald
   applyMarkupTexts() gelaufen ist. Danach blendet sie ein, statt zu springen. */
body { transition: opacity .15s ease-out; }
html[data-texte="wartet"] body { opacity: 0; }
@media (prefers-reduced-motion: reduce) { body { transition: none; } }

/* Die Abos: das Bild links, wie in jeder Zeile mit einem Gesicht. */
.rows li .abo-kopf { flex: none; display: block; text-decoration: none; }
.rows li .abo-kopf .head { width: 40px; height: 40px; }

/* ------------------------------------------------------- Auswahlfeld im Haus
   Der Ersatz für `<select>`. Der Knopf sieht aus wie ein Eingabefeld -- er
   steht in denselben Formularen daneben und darf nicht wie ein Knopf wirken --
   und das Menü ist dasselbe `.menu`, das die Kopfleiste schon benutzt.

   Warum überhaupt: Ein `<select>` klappt die Liste des Betriebssystems auf.
   Blaue Markierung, eigene Schrift, heller Grund im dunklen Bild, an jedem
   Gerät anders. An dieser Stelle sah man nicht die Anwendung, sondern den
   Rechner, auf dem sie läuft. */
.aw { position: relative; }
.aw-knopf {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  width: 100%;
  min-height: var(--layout-touch-target);
  padding: 0 var(--spacing-md) 0 var(--spacing-lg);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-control);
  background: var(--color-surface-secondary);
  font: var(--typography-body);
  color: var(--color-text-primary);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--motion-quick), background var(--motion-quick);
}
.aw-knopf:hover { background: var(--color-surface-primary); }
/* Ein Fokusrahmen fuer alle: Sechs Seiten hatten ihren eigenen am `<select>`,
   und jede haette ihn jetzt einzeln nachziehen muessen. */
.aw-knopf:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; }
.aw-knopf svg.i { color: var(--color-text-tertiary); flex: none; }
/* Das Wort nimmt den Platz, das Zeichen bleibt rechts stehen. Ohne `min-width`
   schiebt ein langer Eintrag das Zeichen aus dem Feld heraus. */
.aw-wort {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.aw.gesperrt .aw-knopf {
  color: var(--color-disabled-foreground);
  cursor: default;
  opacity: .6;
}
/* Das Menü steht am Bildschirm und nicht im Kasten: `fixed` statt `absolute`.
   Im Dialog des Meldeblatts hing es sonst an dessen Kante -- von zehn Gründen
   waren vier zu sehen, „Gewalt" war mittendrin abgeschnitten. Dasselbe droht
   in jeder Tabelle mit `overflow` und in jedem scrollenden Kasten, und davon
   hat diese Anwendung reichlich.

   Wo es steht, rechnet `auswahl.js` beim Öffnen aus -- nur dort ist bekannt,
   wo der Knopf gerade liegt und ob nach unten noch Platz ist. Breite und
   Stelle kommen darum als Stilangabe am Element an. */
/* So breit wie sein laengster Eintrag, mindestens so breit wie der Knopf.
   Nur die Knopfbreite zu nehmen brach „Zuletzt hinzugefuegt" bei der
   Sortierung in vier Zeilen -- das native Feld hatte seine Liste nach dem
   Inhalt bemessen, und das ist auch richtig so. */
.aw-menue {
  position: fixed;
  top: auto;
  right: auto;
  width: max-content;
  max-width: min(92vw, 26rem);
  max-height: min(52dvh, 420px);
}
.aw-menue [role="option"] { gap: var(--spacing-sm); }
/* Der Haken steht an jedem Punkt und wird nur verborgen: So bleibt die Kante
   der Wörter dieselbe, ob gewählt oder nicht. */
.aw-haken { display: flex; visibility: hidden; flex: none; }
.aw-menue [role="option"][aria-selected="true"] .aw-haken { visibility: visible; }
.aw-menue [role="option"][aria-selected="true"] { font-weight: 600; }
.aw-menue [role="option"][disabled] {
  color: var(--color-disabled-foreground);
  opacity: .6;
  cursor: default;
}
.aw-menue [role="option"][disabled]:hover { background: transparent; }
.aw-punktwort { min-width: 0; overflow-wrap: anywhere; }
