/* ==========================================================================
   RIVALZ — Layout & sections
   Mobile-first. Breakpoints: 600 (large phone) · 900 (tablet) · 1180 (desktop)
   ========================================================================== */

body {
  /* Clear the fixed tab bar so nothing is ever trapped underneath it. */
  padding-bottom: calc(var(--tabbar-h) + var(--safe-b));
}

/* ---------- Header -------------------------------------------------------- */

/* Transparent at rest so the nav pill floats over the stage; takes on a solid
   background once it detaches from the top.

   NO `backdrop-filter` here, deliberately. On a position:sticky header it makes
   Chromium paint the entire document black whenever the page loads already
   scrolled — e.g. opening /#top3 directly, or reloading after clicking a nav
   anchor. Declaring it up front at blur(0) does not help; the property has to
   be absent. An opaque background gets the same separation for free.
   The fixed .tabbar keeps its glass — only the sticky header triggers this. */
.header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  padding-top: var(--safe-t);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out);
}

.header.is-stuck {
  background: var(--color-bg);
  border-bottom-color: var(--color-border);
}

.header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  height: var(--header-h);
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Felder als dunkles Glas. Seit das Bild bis nach ganz oben durchlaeuft,
   liegen Reiterleiste, Sprachumschalter und Symbolknoepfe direkt auf dem
   Sonnenuntergang. Ohne eigenen Hintergrund waere die Beschriftung dort
   stellenweise unlesbar — mit einem deckenden waeren es wieder die Kaesten,
   die gerade verschwinden sollten. Halbtransparentes Dunkel mit Unschaerfe
   dahinter loest beides: Das Bild bleibt sichtbar, die Schrift steht.

   backdrop-filter kennt nicht jeder Browser. Faellt er weg, bleibt die
   Flaeche einfach etwas weniger ruhig — lesbar ist sie durch die
   Deckkraft trotzdem. */
/* Nur solange die Leiste ueber dem Stadionbild schwebt. Sobald sie sich
   abloest (.is-stuck), traegt sie den Seitenhintergrund — im hellen Thema also
   Weiss, und dunkles Glas darauf sah aus wie vergessene graue Klumpen. Ohne
   diese Regeln fallen die Felder auf ihre eigenen, themenabhaengigen Farben
   aus rivalz-components.css zurueck, und das ist im gescrollten Zustand
   genau richtig. */
.header:not(.is-stuck) .navpill,
.header:not(.is-stuck) .langswitch,
.header:not(.is-stuck) .iconbtn {
  background: rgba(15, 22, 19, 0.55);
  border-color: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  backdrop-filter: blur(12px) saturate(140%);
}

/* Im hellen Thema traegt das Bild hell (Sonnenaufgang statt Sonnenuntergang,
   siehe --hero-bild), also dreht sich auch dieses Glas um. Dunkles Glas auf
   einem Morgenhimmel sind genau die "vergessenen grauen Klumpen", die der
   Kommentar darueber fuer den gescrollten Zustand schon beschreibt — nur
   eben ungescrollt. */
:root[data-theme='light'] .header:not(.is-stuck) .navpill,
:root[data-theme='light'] .header:not(.is-stuck) .langswitch,
:root[data-theme='light'] .header:not(.is-stuck) .iconbtn {
  background: rgba(255, 255, 255, 0.70);
  border-color: rgba(38, 33, 25, 0.12);
}

/* Auf dem Bild ist die Schrift immer hell, unabhaengig vom Thema — dieselbe
   Ueberlegung wie bei .hub.
   ABER nur --color-fg-muted, nicht --color-fg: Der aktive Sprachknopf bringt
   mit --color-surface seine eigene helle Flaeche mit und holt sich die
   Schriftfarbe aus --color-fg. Wird die mit ueberschrieben, steht dort im
   hellen Thema Weiss auf Weiss — "DE" verschwindet. Die wenigen Stellen, die
   --color-fg brauchen und wirklich auf dem Bild liegen, stehen darunter
   einzeln. */
.header:not(.is-stuck) {
  --color-fg-muted: #A7B3AC;
}
.header:not(.is-stuck) .brand__word,
.header:not(.is-stuck) .navpill__link:hover { color: var(--ink-50); }

/* Und dieselbe Umkehr fuer die Schrift darauf. */
:root[data-theme='light'] .header:not(.is-stuck) {
  --color-fg-muted: var(--paper-600);
}
:root[data-theme='light'] .header:not(.is-stuck) .brand__word,
:root[data-theme='light'] .header:not(.is-stuck) .navpill__link:hover { color: var(--paper-950); }

.header .iconbtn { border-style: solid; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
}

/* DAS WAPPEN (04.10.2026, neues Firmenlogo). Die Kachel zeigt das Schild
   mit dem RC-Monogramm, ausgeschnitten aus dem ganzen Wappen
   (public/assets/logo-wappen.jpg) — Band, Schrift und Lorbeer waeren bei
   32 px nur noch Gekruemel. Ein Bild statt einer Glyphe: Das Logo ist
   gezeichnetes Metall auf Stoff, das laesst sich nicht als currentColor-Pfad
   nachbauen. Der Rahmen bleibt, damit die Kachel auf dem dunklen Seitengrund
   als Flaeche lesbar ist. */
.brand__mark {
  display: block;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: 9px;
  overflow: hidden;
  background: #091017;
  box-shadow: inset 0 0 0 1px var(--color-border);
}
.brand__mark img { display: block; width: 100%; height: 100%; object-fit: cover; }

.brand__word {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 800;
  letter-spacing: var(--tracking-tighter);
}

/* Once the giant wordmark is on screen, a second logotype in the header is
   redundant — the mark alone carries it. */
@media (min-width: 900px) {
  .brand--compact .brand__word { display: none; }
}

/* Und auf dem Handy traegt die Marke allein, weil die Zeile sonst nicht
   aufgeht. Gemessen bei 375 px: Wortmarke 108, Bedienfelder 250, Rinne 2x17 —
   zusammen 392 bei 375 verfuegbar. Vor der Glas-Kachel stand das Zahnrad
   genau buendig am Rand, die Zeile war also exakt voll; das vierte Feld
   schneidet es um 14 px ab (unsichtbar, weil body overflow-x: clip traegt —
   es scrollt nicht, es fehlt einfach).

   Von den vier gemessenen Auswegen ist das der einzige, der nichts kostet
   ausser Markentext: Abstaende enger zu ziehen bringt 4 px Rand und bricht
   auf einem 320-px-Geraet trotzdem, und die beiden anderen haetten eine
   Bedienung entfernt. Ohne die Wortmarke stehen wieder exakt die 17 px da,
   die vorher da waren. */
@media (max-width: 560px) {
  .brand__word { display: none; }
}

/* ---------- Nav pill ------------------------------------------------------ */

.navpill {
  display: none;
  gap: var(--space-3xs);
  padding: 5px;
  margin-left: var(--space-2xs);
  border-radius: var(--radius-full);
  background: var(--color-bg-inset);
  border: 1px solid var(--color-border-subtle);
}

.navpill__link {
  position: relative;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-fg-muted);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}

/* Aktiver Reiter in der Markenfarbe statt in Weiss. Vorher hatte er exakt
   dieselben drei Regeln wie .langswitch__btn.is-active — Navigation und
   Spracheinstellung sahen identisch aus, obwohl sie nichts miteinander zu tun
   haben. Die Lime-Toenung ist die im Projekt festgelegte Auszeichnung fuer
   Auswahlzustaende (siehe CLAUDE.md); "background: var(--card)" waere hier
   ein Loch statt einer Hervorhebung. Das weisse Pillen-Muster bleibt dem
   Sprachumschalter, damit sich die beiden unterscheiden. */
.navpill__link.is-active {
  /* --color-primary-soft ist im dunklen Thema genau die in CLAUDE.md
     festgelegte Toenung rgba(0, 229, 122, 0.14), im hellen dagegen lime-50.
     --color-primary-text statt --color-primary: Letzteres ist das helle
     Signalgruen und kaeme im hellen Thema auf 1,8:1 gegen diesen Hintergrund —
     als Schrift unlesbar. Der Text-Token traegt genau dafuer den Hinweis
     "lime as *text* needs 4.5:1" und ist je Thema anders gesetzt. */
  background: var(--color-primary-soft);
  color: var(--color-primary-text);
  box-shadow: none;
}

/* Im hellen Thema ist --color-primary-soft (#E8FFF3) vom Weiss der
   Sprachpille (#FFFFFF) kaum zu unterscheiden — also genau die Kollision, die
   hier verschwinden soll, nur eine Stufe blasser. Deshalb dort eine Stufe
   kraeftiger und die Schrift eine Stufe dunkler, damit der Kontrast trotzdem
   ueber 4,5:1 bleibt (gemessen 6,3:1).

   Einschraenkung, damit sie niemanden ueberrascht: .header ist oben
   transparent und nimmt erst beim Scrollen (.is-stuck) die Themenfarbe an.
   Diese Werte sind auf den gescrollten Zustand abgestimmt — dort wird die
   Leiste benutzt. Ganz oben liegt die Pille dagegen ueber dem dunklen
   Stadionbild und wirkt im hellen Thema heller als noetig. Lesbar bleibt sie,
   weil sie ihren eigenen Hintergrund mitbringt. */
:root[data-theme="light"] .navpill__link.is-active {
  background: var(--lime-100);
  color: var(--lime-800);
}

@media (hover: hover) and (pointer: fine) {
  .navpill__link:hover { color: var(--color-fg); }
}

/* Die Leiste haelt drei Funktionsgruppen nebeneinander: Navigation links,
   Profil in der Mitte, Bedienelemente rechts. Ohne Trennung lasen sie sich
   als eine einzige volle Reihe. Eine feine Linie und etwas mehr Luft davor
   machen die Gruppen sichtbar, ohne etwas zu entfernen. */
.header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-left: auto;
  padding-left: var(--space-xs);
  border-left: 1px solid var(--color-border-subtle);
}

.header__login,
.header__cta { display: none; }

/* ==========================================================================
   Stage — giant wordmark over backdrop image
   ========================================================================== */

.stage {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  /* Die Haelfte des Blockabstands, wie jeder Abschnitt — siehe den Kommentar
     bei .section. */
  padding-bottom: calc(var(--space-section) / 2);
}

.stage__bg {
  position: absolute;
  /* Nach oben ueber die eigene Kante hinaus, um die Hoehe der Kopfleiste.
     Vorher begann das Bild erst unter der Leiste, und darueber blieb ein
     schwarzer Streifen quer ueber den Bildschirm stehen. z-index: -1 haelt es
     hinter allem, auch hinter der Leiste — die ist oben transparent, also
     scheint das Bild durch. Der Verlauf in .stage__backdrop::after wandert
     mit und haelt die Beschriftung dort lesbar. */
  inset: calc(-1 * (var(--header-h) + var(--safe-t, 0px))) 0 0 0;
  z-index: -1;
  overflow: hidden;
}

/* ---------- Bild hinter dem Schriftzug ------------------------------------
   Eine Datei für alle Bildschirme: assets/hero-bg.jpg (1671px, 460 KB),
   seit dem 17.09.2026 das Abendbild im Blick der Sportarten-Kulissen, vom
   Eigentümer erzeugt. Die helle Fassung macht tools/bauen/hero-sonnenaufgang.py. Fehlt die
   Datei, schlägt nur das url() fehl und der Bereich bleibt leer; kein
   kaputtes Bildsymbol, kein Layoutsprung.

   Der Verlauf davor ist kein Deko-Element: Der Schriftzug ist weiß, und ohne
   Abdunkeln wäre er auf einem hellen Foto unlesbar. Nach unten läuft das Bild
   in den Seitenhintergrund aus, damit die Texte darunter ruhig stehen. */
.stage__backdrop {
  position: absolute;
  inset: 0;
  /* Zwei Fassungen, eine je Thema: Abend und Morgen. Welche gilt, entscheidet
     --hero-bild in rivalz-tokens.css. */
  background-image: var(--hero-bild, url('../assets/hero-bg.jpg'));
  background-size: cover;
  background-position: center 38%;
  background-repeat: no-repeat;
}

/* Abgestufter statt gleichmäßiger Schleier: oben abdunkeln, wo der weiße
   Schriftzug auf dem hellen Himmel liegt — in der Bildmitte fast klar, damit
   das Stadion sichtbar bleibt — unten wieder dunkler, wo Fließtext und
   Schaltflächen stehen, und zuletzt in den Seitenhintergrund auslaufend.

   Die Lesbarkeit des Schriftzugs kommt nicht mehr aus dem Schleier, sondern
   aus seinem eigenen Schatten (siehe .wordmark__word). Das erlaubt ein
   erheblich helleres Bild bei gleichem Kontrast. */
.stage__backdrop::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Lesbarkeit hat Vorrang vor Bildfläche. Das Stadion liest sich in der
     oberen Hälfte rund um den Schriftzug; ab 40 % wird hart abgedunkelt, weil
     darunter durchgehend Text auf den hell erleuchteten Rängen liegt.
     Gemessen statt geschätzt: alle Texte darüber erreichen so über 7:1. */
  background: linear-gradient(to bottom,
    rgba(15, 22, 19, 0.40) 0%,
    rgba(15, 22, 19, 0.12) 18%,
    rgba(15, 22, 19, 0.20) 32%,
    rgba(15, 22, 19, 0.72) 46%,
    rgba(15, 22, 19, 0.88) 62%,
    rgba(15, 22, 19, 0.94) 82%,
    var(--color-bg) 100%);
}

/* Im hellen Modus muss der Schleier aufhellen statt abzudunkeln, sonst kippt
   der Schriftzug (dunkel auf hell) ins Unlesbare. */
:root[data-theme='light'] .stage__backdrop::after {
  background: linear-gradient(to bottom,
    rgba(243, 239, 230, 0.52) 0%,
    rgba(243, 239, 230, 0.22) 24%,
    rgba(243, 239, 230, 0.16) 48%,
    rgba(243, 239, 230, 0.5) 76%,
    rgba(243, 239, 230, 0.88) 92%,
    var(--color-bg) 100%);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .stage__backdrop::after {
    background: linear-gradient(to bottom,
      rgba(243, 239, 230, 0.52) 0%,
      rgba(243, 239, 230, 0.22) 24%,
      rgba(243, 239, 230, 0.16) 48%,
      rgba(243, 239, 230, 0.5) 76%,
      rgba(243, 239, 230, 0.88) 92%,
      var(--color-bg) 100%);
  }
}

/* Mesh, Rasterlinien und Leuchtkugeln sind ersatzlos entfernt: Sie waren der
   Ersatz für ein fehlendes Hintergrundbild. Über dem Stadionfoto legten sie
   nur einen Grünschleier darüber und kosteten zwei dauerhaft animierte,
   56px-weichgezeichnete Compositing-Ebenen. Das Foto trägt den Hero jetzt
   allein. */

/* ---------- Wordmark ------------------------------------------------------
   Full-bleed at the top of the page, over the backdrop image. Only that
   arrangement is borrowed from the reference — colour and surface treatment
   stay on our own palette. */

.stage__panel {
  position: relative;
  padding-inline: var(--gutter);
  padding-top: var(--space-2xs);
}

.wordmark {
  font-size: 0;              /* collapses whitespace between the two spans */
  line-height: 0;
}

/* Sized in vw so the word spans the panel edge to edge at every width. The
   divisor is tuned to RIVALZ's six caps plus the dot — a different brand name
   needs it re-measured, not guessed. */
.wordmark__word {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2.75rem, 21.6vw, 24rem);
  font-weight: 900;
  line-height: 0.78;
  letter-spacing: -0.055em;
  text-align: center;
  color: var(--color-fg);
  user-select: none;
  /* Trägt die Lesbarkeit lokal, statt das ganze Bild abzudunkeln. Zwei
     Schatten: ein enger für die Kante, ein weiter, weicher als Halo. */
  text-shadow:
    0 2px 6px rgba(15, 22, 19, 0.35),
    0 8px 48px rgba(15, 22, 19, 0.55);
}

:root[data-theme='light'] .wordmark__word {
  text-shadow:
    0 2px 6px rgba(243, 239, 230, 0.6),
    0 8px 48px rgba(243, 239, 230, 0.8);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .wordmark__word {
    text-shadow:
      0 2px 6px rgba(243, 239, 230, 0.6),
      0 8px 48px rgba(243, 239, 230, 0.8);
  }
}

/* Archivo's period is a square slab at weight 900, which reads as a glitch
   next to a geometric wordmark. Drawn as a real circle instead, sized in em
   so it tracks the fluid font-size. */
.wordmark__dot {
  display: inline-block;
  width: 0.19em;
  height: 0.19em;
  margin-left: 0.03em;
  border-radius: 50%;
  background: var(--color-primary);
  vertical-align: baseline;
}

/* ---------- Meta + bottom rows -------------------------------------------- */

.stage__mid,
.stage__bottom {
  position: relative;
  z-index: 1;
  display: grid;
  gap: var(--space-md);
}

/* Da der Schleier jetzt viel leichter ist, sichern die Texte ihren Kontrast
   selbst ab. Nur ein weicher Schatten — kein Kasten, keine Fläche, damit das
   Foto dahinter sichtbar bleibt. */
.stage__mid :is(p, span, li),
.stage__bottom :is(p, span) {
  text-shadow: 0 1px 3px rgba(15, 22, 19, 0.5), 0 2px 16px rgba(15, 22, 19, 0.4);
}

:root[data-theme='light'] .stage__mid :is(p, span, li),
:root[data-theme='light'] .stage__bottom :is(p, span) {
  text-shadow: 0 1px 3px rgba(243, 239, 230, 0.8), 0 2px 16px rgba(243, 239, 230, 0.7);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .stage__mid :is(p, span, li),
  :root:not([data-theme]) .stage__bottom :is(p, span) {
    text-shadow: 0 1px 3px rgba(243, 239, 230, 0.8), 0 2px 16px rgba(243, 239, 230, 0.7);
  }
}

/* Schaltflächen bringen eigene Flächen mit — dort wäre der Schatten Unsinn. */
.stage__bottom .btn span,
.howto span { text-shadow: none; }

/* Die gedämpften Grautöne stammen aus der Zeit, als hier der Seitenhintergrund
   lag. Über einem Foto — besonders über den hell erleuchteten Rängen — fallen
   sie unter jede brauchbare Lesbarkeit. Auf der Bühne daher volle Vordergrund-
   farbe; nur die Ordnungszahlen dürfen zurücktreten. */
.stage__countLabel,
.stage__mid .specs__item,
.stage__bottom .metaitem {
  color: var(--color-fg);
}

.stage__mid .specs__item i,
.stage__bottom .metaitem svg {
  color: var(--color-fg);
  opacity: 0.6;
}

.stage__mid .specs__item {
  border-bottom-color: color-mix(in srgb, var(--color-fg) 22%, transparent);
}

/* Der große Abstand nach oben ist Absicht: Er gibt dem Stadionfoto zwischen
   Schriftzug und Textblock freie Fläche. In vh gemessen, damit der Hero auf
   niedrigen Fenstern nicht auseinanderfällt. */
.stage__mid {
  justify-items: center;
  gap: var(--space-sm);
  margin-top: clamp(3.5rem, 13vh, 7rem);
  margin-bottom: var(--space-md);
}

.stage__users {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  justify-self: start;
}

.stage__count {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 850;
  letter-spacing: var(--tracking-tight);
  line-height: 1;
}

.stage__countLabel {
  font-size: var(--text-sm);
  color: var(--color-fg-muted);
}

.specs {
  display: grid;
  gap: 0.35rem;
  font-size: var(--text-sm);
  justify-self: stretch;
}

.specs__item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-block: 0.2rem;
  color: var(--color-fg-muted);
  border-bottom: 1px solid var(--color-border-subtle);
}
.specs__item i {
  font-style: normal;
  font-variant-numeric: tabular-nums;
  color: var(--color-fg-subtle);
}

.stage__lead {
  margin-top: var(--space-2xs);
  max-width: 40ch;
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
  color: var(--color-fg);
  text-wrap: pretty;
}

.stage__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

/* ---------- "How it works" disc ------------------------------------------- */

.howto {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  width: 132px;
  height: 132px;
  padding: var(--space-2xs);
  border-radius: 50%;
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-size: var(--text-sm);
  font-weight: 700;
  text-align: center;
  line-height: 1.2;
  justify-self: start;
  transition: transform var(--dur-base) var(--ease-spring),
              box-shadow var(--dur-base) var(--ease-out);
}

.howto__icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(15, 22, 19, 0.14);
  font-size: 0.85rem;
}

.howto:active { transform: scale(0.94); }

@media (hover: hover) and (pointer: fine) {
  .howto:hover { transform: scale(1.06); box-shadow: var(--shadow-glow); }
}

/* ---------- Marquee ------------------------------------------------------- */

.marquee {
  overflow: hidden;
  /* ENGER ALS EIN ABSCHNITT, und das ist die eine Ausnahme von der Regel
     "ein Abstand zwischen allen Bloecken".

     Die Regel gilt fuer BLOECKE — Dinge mit Inhalt, die fuer sich stehen.
     Das Band ist keiner: Es ist aria-hidden, rein dekorativ und trennt
     zwei Abschnitte, statt selbst einer zu sein. Mit dem vollen Abstand
     auf beiden Seiten stand es in zwei Handbreit Leere und wirkte wie ein
     vergessener Streifen; das war die Meldung.

     Aussen also ein Viertel statt der Haelfte. Die Nachbarn tragen ihre
     eigene Haelfte bei, deshalb steht der tatsaechliche Abstand darunter
     noch einmal ausdruecklich — sonst waere hier nur die halbe Rechnung. */
  margin-block: calc(var(--space-section) / 4);
  padding-block: var(--space-2xs);
  border-block: 1px solid var(--color-border);
  background: var(--color-bg-subtle);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.marquee__track {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  width: max-content;
  animation: marquee 38s linear infinite;
}

.marquee__track span {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 1rem + 1.6vw, 2.1rem);
  font-weight: 900;
  font-style: italic;
  letter-spacing: var(--tracking-tighter);
  color: transparent;
  -webkit-text-stroke: 1.4px var(--color-border-strong);
}
/* Im hellen Thema ist --color-border-strong ein sehr helles Grau (#C2CDC7) auf
   fast weissem Grund (#F4F7F5) — als 1,4 px dünne Kontur war davon praktisch
   nichts zu sehen, das Band wirkte wie ein leerer Streifen. Eine Stufe dunkler
   bringt es auf dieselbe Präsenz, die es im dunklen Thema hat. Rein
   dekorativ (aria-hidden), deshalb reicht Sichtbarkeit statt Textkontrast. */
:root[data-theme="light"] .marquee__track span {
  -webkit-text-stroke-color: var(--color-fg-subtle);
}
.marquee__track i {
  font-style: normal;
  font-size: 0.7rem;
  color: var(--color-primary-text);
}

/* Und die Nachbarn ziehen mit. Ohne das brachte das Band selbst nur ein
   Viertel ein, waehrend der Abschnitt darueber und darunter je eine volle
   Haelfte beisteuerte — der Abstand waere von 1 auf 0,75 gefallen, also
   kaum sichtbar. So sind es 0,5 auf jeder Seite: halb so viel wie vorher,
   und immer noch mehr als die Luft im Band selbst.

   Der Nachfolger geht mit dem Geschwisterwaehler, der Vorgaenger braucht
   :has(). Fehlt :has(), bleibt es oben beim alten Abstand — schlimmer als
   heute wird es dadurch nie.

   .stage steht ausdruecklich mit in der Liste: Ueber dem Band liegt auf der
   Startseite nicht ein .section, sondern die Buehne. Gemessen war der
   Abstand oben deshalb erst 95 px gegen 42 px unten — die Regel griff auf
   der einen Seite gar nicht. */
.marquee + :is(.section, .stage) { padding-block-start: calc(var(--space-section) / 4); }
:is(.section, .stage):has(+ .marquee) { padding-block-end: calc(var(--space-section) / 4); }

@keyframes marquee { to { transform: translate3d(-50%, 0, 0); } }

@media (hover: hover) {
  .marquee:hover .marquee__track { animation-play-state: paused; }
}

/* ---------- Sections ------------------------------------------------------ */

/* EIN ABSTAND ZWISCHEN ALLEN BLOECKEN, und --space-section IST er.

   Vorher war er das Polster JE BLOCK, und weil zwei Bloecke ohne Rand
   aneinanderstossen, addierte es sich. Gemessen bei 1440 px, Uebergang fuer
   Uebergang:

     Buehne  -> Erklaerkacheln    48 + 0   =  48
     Kacheln -> Laufband          56 + 8   =  64
     Laufband -> Heute             8 + 88  =  96
     Heute   -> Katalog           88 + 88  = 176
     Katalog -> Meistgespielt     88 + 88  = 176
     Meistgespielt -> Schlussband 56 + 88  = 144

   Sechs Uebergaenge, sechs verschiedene Abstaende, und der haeufigste war
   doppelt so gross wie gemeint. Die vier Ausnahmen darunter
   (--top3, --spielinfo, --cta, --stats) waren Korrekturen an genau diesem
   Fehler: Jede zog irgendwo etwas ab, ohne dass daraus eine Regel wurde.

   JEDER BLOCK TRAEGT JETZT DIE HAELFTE, oben wie unten. Zwei Nachbarn ergeben
   damit immer --space-section — 88 px auf dem Desktop, 56 auf dem Handy, weil
   der Wert eine clamp() ist und mitwaechst. Das ersetzt auch den
   Medienabfrage-Sonderfall fuer die Buehne weiter unten: Der gab ihr 48 statt
   32, und genau das macht die clamp() von selbst.

   WAS ES KOSTET: Die Erklaerkacheln haengen nicht mehr enger an der Buehne als
   am Laufband. Das war Absicht (der Kommentar dazu stand hier und ist mit ihm
   gegangen) — gleiche Abstaende ueberall und eine engere Gruppe an einer
   Stelle sind zwei verschiedene Wuensche, und der erste ist der, der gilt.

   Das Laufband bekommt seinen Anteil als RAND, nicht als Polster: Es ist eine
   Flaeche mit Rahmen und Hintergrund, sein padding-block sitzt INNEN um die
   Schrift. Gemessen wird bis zur Kante, also gehoert der Abstand nach aussen.

   .section--stats ist ersatzlos weg — die Klasse stand in keiner HTML-Datei
   und in keinem Renderer. */
.section { padding-block: calc(var(--space-section) / 2); }

/* EIN Sektionspolster je Kante, nicht zwei.

   Jeder Abschnitt polstert oben UND unten mit --space-section. Wo zwei
   aufeinanderfolgen, addiert sich das: Zwischen dem Laufband und "Heute"
   liegen 84 Pixel (das Band polstert nicht), zwischen "Heute" und "Alle
   Spielmodi" 168 — doppelt so viel, obwohl beides dieselbe Art Kante ist.
   Auf der langen Seite sah das aus, als fehlte dort etwas.

   Der Abschnitt, der ANSCHLIESST, laesst sein oberes Polster weg; das des
   Vorgaengers traegt die Kante allein. Dieselbe Bauart wie .section--stats,
   das genau das schon tut.

   NUR AB 900 PIXELN, und das ist die Bedingung, nicht eine Feinheit:
   Darunter blendet die Tab-Leiste die fremden Bereiche aus (BEREICH_AB in
   app.js), und dann steht #catalog als ERSTES unter "Spiele". Ohne oberes
   Polster klebte die Ueberschrift dort an der Kopfleiste. Was hier korrigiert
   wird, gibt es nur, solange alle Abschnitte untereinander stehen. */
@media (min-width: 900px) {
  .section--anschluss { padding-top: 0; }
}

/* Kein Abstand fuer einen Knopf, der nicht da ist.

   .cardgrid__more haelt den Abstand zum Raster darueber — auch dann, wenn
   renderCatalog() den Knopf darin versteckt, weil alle Kacheln schon zu sehen
   sind. Der leere Kasten war dann 0 Pixel hoch und trug trotzdem 18 Pixel
   Rand, also stiller Leerraum unter dem Katalog. Gemessen an der Kante zu
   "Meistgespielt diese Woche": 186 statt 168. */
.cardgrid__more:has(> button[hidden]) { margin-top: 0; }

.sectionhead {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-md);
  margin-bottom: var(--space-sm);
}

.sectionhead__title {
  font-size: var(--text-3xl);
  letter-spacing: var(--tracking-tighter);
}

.sectionhead__sub {
  margin-top: var(--space-3xs);
  color: var(--color-fg-muted);
  font-size: var(--text-sm);
  text-wrap: pretty;
}

/* Am Handy zwei Spalten, die dritte Kachel darunter ueber die volle Breite —
   dieselbe Aufteilung wie im Abschnitt "Heute". */
@media (max-width: 700px) {
  .podiumgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-sm); }
  .podiumgrid .podium:last-child { grid-column: 1 / -1; }
}

/* ---------- Card grid -----------------------------------------------------
   Two-up on phones: the brief is "see everything at a glance", and a single
   full-width column turns 11 modes into a very long scroll. */

.cardgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2xs);
}

@media (min-width: 560px) {
  .cardgrid {
    grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
    gap: var(--space-md);
  }
}

.cardgrid__empty {
  padding: var(--space-2xl) 0;
  text-align: center;
  color: var(--color-fg-muted);
}

/* ---------- Footer -------------------------------------------------------- */

.footer {
  border-top: 1px solid var(--color-border);
  background: var(--color-bg-subtle);
  padding-top: var(--space-2xl);
}

.footer__inner {
  display: grid;
  gap: var(--space-lg);
  padding-bottom: var(--space-lg);
}

.footer__tag {
  margin-top: var(--space-2xs);
  max-width: 34ch;
  color: var(--color-fg-muted);
  font-size: var(--text-sm);
}

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

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

.footer__h {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-fg-subtle);
  margin-bottom: var(--space-3xs);
}

/* Auf Tastfeldern hoeher. Gemessen bei 375 px: 159x33 — elf Pixel unter der
   44-px-Empfehlung, und acht dieser Links stehen dicht uebereinander. Breit
   genug sind sie, es fehlt nur die Hoehe.

   Bewusst hinter (pointer: coarse): Mit der Maus sind 33 px kein Problem, und
   die Fusszeile soll auf dem Rechner nicht auseinanderlaufen. */
@media (pointer: coarse) {
  .footer__col a { min-height: 44px; display: flex; align-items: center; }
}

.footer__col a {
  padding-block: 0.35rem;
  font-size: var(--text-sm);
  color: var(--color-fg-muted);
  transition: color var(--dur-fast) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .footer__col a:hover { color: var(--color-primary-text); }
}

.footer__base {
  padding-block: var(--space-md);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-xs);
  color: var(--color-fg-subtle);
}

/* ==========================================================================
   Scroll reveals — opacity + small translate only (compositor-friendly).
   JS adds .is-in via IntersectionObserver; base.css guarantees the no-JS and
   reduced-motion fallbacks leave everything visible.
   ========================================================================== */

[data-reveal] {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity var(--dur-slower) var(--ease-out),
              transform var(--dur-slower) var(--ease-out);
}

[data-reveal].is-in { opacity: 1; transform: none; }

[data-stagger] > * {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-spring);
  transition-delay: calc(var(--i, 0) * 55ms);
}
[data-stagger].is-in > * { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  [data-stagger] > * { opacity: 1 !important; transform: none !important; }
  .marquee__track, .pulse::after,
}

/* ==========================================================================
   Breakpoints
   ========================================================================== */

/* ---- 600px: large phone / small tablet ---- */
@media (min-width: 600px) {
  .statband { grid-template-columns: repeat(4, 1fr); }
  .steps { grid-template-columns: repeat(3, 1fr); }

  .stage__mid {
    grid-template-columns: 1fr auto;
    align-items: center;
  }
  .specs { min-width: 220px; }
}

/* ---- 900px: web layout proper ---- */
@media (min-width: 900px) {
  body { padding-bottom: 0; }

  .tabbar { display: none; }

  .navpill { display: flex; }
  .header__login,
  .header__cta { display: inline-flex; }

  /* Deliberately *not* min-height:100svh — the hero is a compact band, not a
     full-screen splash, and the catalogue below should already be peeking in
     at the fold. */
  /* Users left, specs right — the backdrop image fills the space between. */
  .stage__mid {
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: var(--space-lg);
    margin-top: clamp(6rem, 20vh, 13rem);
    margin-bottom: var(--space-lg);
  }

  .specs { justify-self: end; min-width: 240px; }

  .stage__bottom {
    grid-template-columns: 1fr auto;
    align-items: end;
  }

  .howto {
    width: 132px;
    height: 132px;
    justify-self: end;
  }

  .stage__lead { font-size: var(--text-lg); max-width: 36ch; }

  .sectionhead { margin-bottom: var(--space-md); }

  .footer__inner { grid-template-columns: 1fr 1.5fr; gap: var(--space-2xl); }
  .footer__cols { grid-template-columns: repeat(3, 1fr); }
}

/* ---- 1180px: desktop ---- */
@media (min-width: 1180px) {
  .cardgrid { gap: var(--space-lg); }
  .rail { padding-inline: max(var(--gutter), calc((100vw - var(--container)) / 2 + var(--gutter))); }

}

/* ---- Landscape phone: the wordmark must not eat the whole screen ---- */
@media (max-height: 560px) and (orientation: landscape) {
  .wordmark__word { font-size: clamp(2.5rem, 12vw, 7rem); }
}

/* ==========================================================================
   Bühne: Blöcke auf dem Foto verankern

   Vorher standen Profilleiste, Spielerzahl, Merkmalliste und Textblock ohne
   Fläche direkt auf dem Bild und wirkten dadurch verstreut. Jetzt tragen sie
   eine zurückhaltende Glasfläche: genug Halt, um als bewusst gesetzte
   Bedienelemente zu lesen, aber durchscheinend genug, dass das Stadion
   sichtbar bleibt.
   ========================================================================== */

.stage__left {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  align-items: flex-start;
}






.stage__pitch { max-width: 46ch; }


@media (max-width: 899px) {
  .stage__left { width: 100%; }
  .stage__users { width: 100%; }
}

/* Die Grundregel der Bühne zentriert alle Rasterkinder (sinnvoll auf dem
   Handy). Auf breiten Schirmen sollen die Blöcke aber an derselben Kante
   stehen wie der Textblock darunter — sonst wirkt die Spalte verrutscht. */




/* ==========================================================================
   Spielansicht: Vollbild mit schmaler Leiste

   Im Spiel verschwindet die Hülle. Die Kopfzeile mit Nav-Pille, das Laufband,
   die Fußzeile und die Tab-Leiste lenken vom Spiel ab und kosten auf dem
   Handy die halbe Bildschirmhöhe.
   ========================================================================== */

body.im-spiel .header,
body.im-spiel .marquee,
body.im-spiel .footer,
body.im-spiel .tabbar { display: none; }

body.im-spiel { padding-bottom: 0; }

.spielleiste {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  height: var(--header-h);
  padding-inline: var(--gutter);
  padding-top: var(--safe-t);
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
}

.spielleiste__zurueck {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  min-height: 44px;              /* Fingergröße */
  padding-inline: 0;
  border: 0;
  background: none;
  color: var(--color-fg-muted);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 650;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.spielleiste__zurueck svg { width: 18px; height: 18px; }
.spielleiste__zurueck:active { transform: scale(0.96); }

@media (hover: hover) and (pointer: fine) {
  .spielleiste__zurueck:hover { color: var(--color-fg); }
}

.spielleiste__name {
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  margin-inline: auto;
  /* Der Zurück-Knopf steht links; damit der Name wirklich mittig sitzt,
     bekommt die rechte Seite denselben Platz. */
  padding-right: 5.5rem;
}

@media (max-width: 520px) {
  .spielleiste__name { padding-right: 0; margin-inline: 0 auto; }
}

/* ==========================================================================
   Bühne ohne Flächen: Halt kommt aus dem Textschatten

   Die Kästen sind wieder weg — das Foto soll ungestört zu sehen sein. Damit
   die Texte auf dem hellen Himmel trotzdem lesbar bleiben, tragen sie einen
   weichen dunklen Schimmer direkt am Buchstaben statt einer Fläche darunter.
   ========================================================================== */

.stage__left {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  align-items: flex-start;
}

.stage__mid :is(p, span, li),
.stage__bottom :is(p, span) {
  text-shadow: 0 1px 3px rgba(15, 22, 19, 0.75), 0 2px 18px rgba(15, 22, 19, 0.6);
}

/* Schaltflächen haben eigene Flächen — dort wäre der Schatten Unsinn. */
.stage__bottom .btn span,
.howto span { text-shadow: none; }

.stage__countLabel,
.stage__bottom .metaitem { color: var(--color-fg); }

.stage__pitch { max-width: 46ch; }

@media (min-width: 900px) {
  /* Ohne die Merkmalliste rechts trägt nur noch die linke Spalte Inhalt. */
  .stage__mid { grid-template-columns: 1fr; justify-items: stretch; }
  .stage__left { justify-self: start; }
}

/* ==========================================================================
   Schriftzug etwas tiefer

   Bewusst nur ein kleiner Versatz: Der Himmel darüber soll sichtbar bleiben.
   ========================================================================== */

.stage__panel { padding-top: var(--space-lg); }

@media (min-width: 900px) {
  .stage__panel { padding-top: clamp(2.5rem, 5vh, 4.5rem); }
}

/* ==========================================================================
   Profil in der Kopfzeile

   Kompakt: Level-Kreis, schmaler XP-Balken, Trophäen. Name und XP-Text sind
   ausgeblendet — in der Leistenhöhe wäre dafür kein Platz, ohne dass es
   gedrängt wirkt. Umbenennen läuft weiter über einen Klick auf den Avatar.
   ========================================================================== */

/* Hier standen die Regeln der Profil-Kopfleiste: Avatar mit Fortschrittsring,
   Level-Abzeichen, "LEVEL 4" daneben, ab 1080 px eingeblendet.

   Alles raus. Die Leiste stand allein in der Mitte des Kopfes, und die
   Profilkachel im Hub sagt dasselbe — nur groesser, mit Streak und Rang
   dazu. Zum Profil fuehren jetzt diese Kachel und am Handy die Tab-Leiste.

   Die Grundregeln fuer .avatar, .level-badge, .xp-bar-track und
   .trophy-count stehen weiter in styles.css: Die Profilseite baut ihre
   eigene Fassung, aber der Rest der App greift sie nicht mehr an. */

/* ==========================================================================
   Hub — der erste Bildschirm
   --------------------------
   Vier Kacheln über dem Stadionbild. Der Seitenhintergrund zeigt den oberen
   Bildausschnitt (Himmel), die Tages-Kachel den unteren (Stadion). Dadurch
   ergänzen sich beide Ausschnitte, statt dasselbe Motiv zweimal zu zeigen.
   ========================================================================== */

.hub {
  position: relative;
  z-index: 1;
  padding-top: var(--space-md);
  padding-bottom: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);

  /* Die Bühne liegt in BEIDEN Themen auf dem dunklen Stadionfoto, und die
     Kacheln darauf sind dunkles Glas. Die Schrift muss deshalb hell bleiben,
     auch wenn die Seite drumherum hell ist.

     Umgesetzt als lokale Neubelegung der Text-Token statt als Farbangabe je
     Regel: Alles hier drin greift ohnehin auf --color-fg und
     --color-fg-muted zu, und so bleibt es bei einer Stelle statt einem
     Dutzend. Im hellen Modus stand sonst "Level 1" in Fast-Schwarz auf einer
     fast schwarzen Kachel. */
  --color-fg: var(--ink-50);
  --color-fg-muted: #A7B3AC;
  --color-fg-subtle: #8A968F;

  /* Trennlinien und Umrisse liegen hier auf einem FOTO, nicht auf einer
     Seitenflaeche — --color-border waere dafuer die falsche Farbe. Zwei
     eigene Token, aus demselben Grund wie die drei Zeilen darueber: eine
     Stelle statt einem halben Dutzend Farbangaben. */
  --hub-linie:  rgba(255, 255, 255, 0.14);
  --hub-umriss: rgba(255, 255, 255, 0.45);

  /* Das Glas selbst, aus demselben Grund wie die zwei Zeilen darueber: es hat
     inzwischen ZWEI Traeger — die Kacheln im Raster und die beiden
     Erklaerkacheln darunter (siehe .hub .spielinfo-kachel in styles.css). Als
     abgeschriebener Wert lief es sofort auseinander: die Kopie kannte den
     hellen Modus nicht, und dort stand dann Fast-Schwarz auf fast schwarzem
     Glas. */
  --hub-glas:          rgba(10, 12, 19, 0.74);
  --hub-glas-schatten: none;
}

.hub__top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-xs); }

.hub__greet {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--color-fg);
}

/* Das Markenzeichen bleibt, der Schriftzug geht. Wer die Seite täglich
   öffnet, muss nicht dreimal lesen, wo er gelandet ist. */
.hub__mark {
  width: 30px; height: 30px; flex: none;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  display: grid; place-items: center;
}
.hub__mark::after {
  content: '';
  width: 13px; height: 13px;
  background: var(--color-on-primary);
  clip-path: polygon(48% 0, 100% 0, 60% 42%, 100% 42%, 25% 100%, 42% 55%, 5% 55%);
}

/* Raster UND Erklaerkacheln. Der Abstand steht hier und nicht an den Kacheln:
   .hub setzt zwischen seinen Kindern --space-md (Begruessung gegen Block), und
   zwischen Raster und Kacheln soll derselbe Abstand stehen wie zwischen den
   Kacheln im Raster. Zwei verschiedene Abstaende, also zwei Ebenen. */
.hub__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.hub__grid {
  display: grid;
  gap: var(--space-xs);
  grid-template-columns: 1.5fr 1fr 1fr;
  grid-template-rows: 1.2fr 1fr;
  min-height: min(58vh, 460px);
}

.hub__tile {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-xs);
  padding: var(--space-sm);
  border-radius: var(--radius-lg);
  border: 1px solid var(--hub-linie);
  /* Durchscheinend, damit das Bild dahinter trägt. Undurchsichtige Kacheln
     ließen den Hintergrund tot wirken. Der Wert steht bei .hub, weil die
     Erklaerkacheln denselben brauchen. */
  background: var(--hub-glas);
  box-shadow: var(--hub-glas-schatten);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  color: var(--color-fg);
  text-decoration: none;
  transition: transform var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}

.hub__tile--heute {
  grid-column: 1;
  grid-row: 1 / span 2;
  border-color: rgba(var(--marke-rgb), 0.42);
  background:
    linear-gradient(to bottom, rgba(4, 12, 8, 0.30) 0%, rgba(4, 10, 7, 0.62) 46%,
                    rgba(3, 8, 6, 0.86) 74%, rgba(3, 7, 5, 0.94) 100%),
    var(--hero-bild, url('../assets/hero-bg.jpg'));
  /* Herangezoomt auf die Schüssel. Bei "cover" zeigte die Kachel oben
     denselben Himmel wie der Seitenhintergrund, dadurch verschwamm ihre
     Oberkante mit dem Grund und sie wirkte nicht mehr wie ein eigenes
     Element. Der Verlauf davor bleibt bei "cover", damit die Abdunklung
     unabhängig vom Zoom über die ganze Kachel läuft. */
  background-size: cover, 185% auto;
  background-position: center, center 78%;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.hub__tile--profil { grid-column: 2 / span 2; grid-row: 1; justify-content: center; gap: var(--space-2xs); }

/* Erster Besuch: nimmt den Platz aller drei Zustandskacheln ein. Sie zeigen
   ohne gespielte Runde nur Nullen und sind dann per [hidden] weg — siehe
   renderHubStart() in app.js. */
.hub__tile--start {
  grid-column: 2 / span 2; grid-row: 1 / span 2;
  justify-content: center; gap: var(--space-sm);
}
.hub__tile[hidden] { display: none; }
.hub__startBody { display: flex; flex-direction: column; gap: var(--space-2xs); }
.hub__startTitle {
  font-family: var(--font-display);
  font-size: var(--text-2xl); font-weight: 800; color: var(--color-fg);
  letter-spacing: var(--tracking-tight); line-height: var(--leading-tight);
}
.hub__startText {
  font-size: 0.95rem; line-height: var(--leading-snug);
  color: var(--color-fg-muted); max-width: 46ch;
}
.hub__startActions { display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; }
.hub__startLink {
  font-size: 0.88rem; font-weight: 700; color: var(--color-fg-muted);
  text-decoration: underline; text-underline-offset: 3px;
}
/* 94x22 gemessen — das kleinste Ziel der ganzen Startseite, und es steht
   direkt neben dem grossen Startknopf. Wer daneben tippt, loest den anderen
   aus. Die Unterstreichung bleibt an der Schrift, nur die Trefferflaeche
   waechst. */
@media (pointer: coarse) {
  .hub__startLink { min-height: 44px; display: inline-flex; align-items: center; }
}
@media (hover: hover) and (pointer: fine) {
  .hub__startLink:hover { color: var(--color-fg); }
}

.hub__head { display: flex; flex-direction: column; align-items: flex-start; gap: 0.55rem; }
.hub__head--row { flex-direction: row; align-items: center; justify-content: space-between; min-height: 22px; }

.hub__label {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-fg-muted);
}

.hub__title { font-size: var(--text-lg); font-weight: 700; letter-spacing: -0.02em; }
.hub__sub   { font-size: var(--text-sm); color: var(--color-fg-muted); }
.hub__foot  { display: flex; flex-direction: column; gap: 0.4rem; }

/* Die größte Ziffer der Seite. */
.hub__clock {
  font-size: clamp(2.2rem, 5.5vw, 3.6rem);
  font-weight: 700;
  letter-spacing: -0.055em;
  line-height: 0.95;
  color: var(--color-fg);
}

.hub__dots { display: flex; align-items: center; gap: 0.4rem; }
.hub__dots i {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--color-primary);
}
.hub__dots i[data-offen] { background: transparent; border: 1.5px solid var(--hub-umriss); }
.hub__dots span { margin-left: 0.25rem; font-size: var(--text-sm); color: var(--color-fg-muted); }

.hub__profilRow { display: flex; align-items: flex-end; gap: var(--space-sm); }
.hub__profilMain { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.hub__big { font-size: var(--text-2xl); font-weight: 700; letter-spacing: -0.04em; line-height: 1; }
/* ---- Level-Fortschritt als Schuss in sechs Phasen ------------------------
   Steht anstelle des XP-Balkens. Ein 4 px hoher Streifen sagte dasselbe wie
   die Zahl darüber und war der einzige Ort im Hub ohne eigene Form; die
   Figurenreihe erzählt stattdessen eine Bewegung von links nach rechts.

   Erreichte Phasen stehen in Lime als volle Silhouette, kommende als
   gedämpfter Umriss — die Farbe kommt über currentColor aus diesen Regeln,
   die Zeichnung selbst kennt keine Farbe. */
/* Feste Höhe statt freier Skalierung: Die Zeichnung ist fast quadratisch, bei
   voller Kachelbreite wurde jede Figur damit 80 px hoch und die Reihe zum
   größten Element der Startseite. Sie ist eine Fortschrittsanzeige — sie
   gehört in die Größenordnung des Balkens, den sie ersetzt, nur eben lesbar. */
.lvlrun {
  display: flex; align-items: flex-end; justify-content: flex-start;
  gap: 2px; margin-top: 0.4rem; width: 100%; height: 46px;
}
/* Alle sechs gleich: dieselbe Farbe, dieselbe Deckkraft. Vorher markierte
   die Reihe den Level-Fortschritt (erreicht gefuellt, kommend als Umriss) —
   nebeneinander sah sie damit aus, als waere sie halb geladen. Die genaue
   Zahl steht als "91 / 150 XP bis Level 5" direkt darueber. */
.lvlrun__f {
  flex: 1 1 0; min-width: 0; height: 100%;
  color: var(--color-primary-text);
}
/* Die Zeichnung passt sich in die kleinere der beiden Kanten ein (Standard
   von preserveAspectRatio). Dadurch stehen alle sechs auf derselben
   Grundlinie, egal wie breit die Kachel gerade ist. */
.lvlrun__f svg { display: block; width: 100%; height: 100%; }

/* Am Handy sitzt die Kachel über die volle Breite, dort ist Platz. In der
   schmalen Dreierspalte des Hubs wird jede Figur sonst zu klein zum Erkennen
   — deshalb ist die Reihe an die Breite der Kachel gekoppelt und nicht an
   eine feste Figurengröße. */
@media (max-width: 560px) { .lvlrun { gap: 1px; } }

/* Hier stand ein Umbruch für die Handyansicht, und er existierte NUR für die
     Serie: Bei 375 px blieben der Figurenreihe neben Avatar und Serie 119 px,
     also 19 px je Figur — Striche. Der Umbruch schob die Serie in eine eigene
     Zeile und brachte die Reihe auf 35 px.

     Am 13.09.2026 ist die Serie ganz aus der Profilkachel verschwunden: Sie
     stand dreimal auf derselben Seite — hier, groß im Abschnitt „Heute" mit
     Wochenstreifen, und im Profil. Damit ist der Platz ohnehin da und der
     Umbruch gegenstandslos; er würde nur noch eine Zeile umbrechen, die in
     eine passt. */

.hub__line {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-xs);
  margin-top: 0.2rem; padding-top: 0.6rem;
  border-top: 1px solid var(--hub-linie);
  font-size: var(--text-sm); color: var(--color-fg-muted);
}
.hub__line b { color: var(--color-fg); font-weight: 700; }

.hub__families { display: flex; gap: 4px; }
.hub__families i { width: 18px; height: 4px; border-radius: 99px; }
.hub__families i:nth-child(1) { background: var(--color-primary); }
.hub__families i:nth-child(2) { background: #A78BFA; }
.hub__families i:nth-child(3) { background: #FBBF24; }

.hub__trophy { width: 18px; height: 18px; color: var(--color-accent-text); }

@media (hover: hover) and (pointer: fine) {
  .hub__tile:hover { transform: translateY(-3px); border-color: rgba(255, 255, 255, 0.3); }
  /* Weiß auf hellem Glas zeigt nichts an. */
  :root[data-theme='light'] .hub__tile:hover { border-color: rgba(38, 33, 25, 0.28); }
  .hub__tile--heute:hover { border-color: var(--color-primary-text); }
}

/* Handy ist das eigentliche Ziel, nicht der Nebenschauplatz.
   Zwei Spalten statt einer: Tages-Challenge und Profil laufen über die volle
   Breite, "Alle Spiele" und "Liga" teilen sich eine Zeile. Gestapelt passten
   nur drei der vier Kacheln auf den ersten Bildschirm — gemessen 806 px Hub
   gegen 748 px nutzbare Höhe über der Tableiste. So bleibt alles sichtbar,
   ohne dass man scrollen muss. */
@media (max-width: 860px) {
  .hub { gap: var(--space-sm); padding-top: var(--space-sm); }
  .hub__grid {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: none;
    min-height: 0;
  }
  .hub__tile { padding: var(--space-xs); border-radius: var(--radius-md); }
  .hub__tile--heute { grid-column: 1 / -1; grid-row: auto; min-height: 214px; }
  .hub__tile--profil { grid-column: 1 / -1; grid-row: auto; }
  .hub__tile--start { grid-column: 1 / -1; grid-row: auto; }
  .hub__big { font-size: var(--text-xl); }
  .hub__title { font-size: var(--text-base); }
}

/* Erst bei wirklich schmalen Geräten auf eine Spalte. Die Grenze lag zuerst
   bei 380 px — das trifft ausgerechnet 375 px, die häufigste Handybreite
   (iPhone SE, 12/13 mini), und hätte dort die zwei kleinen Kacheln unnötig
   untereinander gesetzt. */
@media (max-width: 330px) {
  .hub__grid { grid-template-columns: 1fr; }
}


/* Im Hub zeigt der Seitenhintergrund bewusst nur den Himmel: Das Stadion
   steckt in der Tages-Kachel. Beide Ausschnitte nebeneinander wirkten wie
   dasselbe Bild zweimal. */
.stage:has(.hub) .stage__backdrop { background-position: center 12%; }

.stage:has(.hub) .stage__backdrop::after {
  background: linear-gradient(to bottom,
    rgba(15, 22, 19, 0.52) 0%,
    rgba(15, 22, 19, 0.42) 22%,
    rgba(15, 22, 19, 0.70) 48%,
    rgba(15, 22, 19, 0.90) 72%,
    var(--color-bg) 100%);
}

/* ---------- Die Bühne im hellen Thema ------------------------------------

   DIESE FASSUNG FEHLTE, und sie ist die Ursache der harten Kante unter der
   Bühne. Die allgemeine Helligkeitsfassung weiter oben
   (:root[data-theme='light'] .stage__backdrop::after) gilt für die Bühne
   ohne Hub. Sobald ein Hub darauf liegt — also auf der Startseite, dem
   ersten, was jemand sieht — gewinnt die Regel darüber, und die kennt nur
   Schwarz. Ergebnis: ein fast schwarzer Verlauf, der unten unvermittelt in
   eine fast weiße Seite stößt. Genau an dieser Kante flimmert das Weiß.

   Der Schleier hellt jetzt auf statt abzudunkeln und läuft wie im dunklen
   Thema in den Seitenhintergrund aus. Er darf dabei viel schwächer sein als
   sein dunkles Gegenstück (0,30 statt 0,52 oben): Das Bild dahinter ist im
   hellen Thema der Sonnenaufgang und trägt bereits hell. */
:root[data-theme='light'] .stage:has(.hub) .stage__backdrop::after {
  /* SCHWACH, und die obere Stufe ist gemessen, nicht gewaehlt.

     Der erste Anlauf verschleierte mit 0,34/0,20/0,40 — und machte damit
     genau das kaputt, wofuer das zweite Bild ueberhaupt existiert: Ein
     Morgenhimmel, dem man die Farbe wegnimmt, ist Nebel. Die Kacheln tragen
     hier ihren eigenen weissen Grund, das Bild darf also stehen.

     Was den Schleier oben trotzdem braucht, ist die Begruessung: Sie steht
     als einziger Text DIREKT auf dem Bild, bei etwa 15 % der Feldhoehe. Am
     dunkelsten Punkt des Himmels (51/102/158) ergibt das mit 0,22 Schleier
     einen Kontrast von 4,83 — mit 0,14 nur noch 4,15 und mit 0,06 nur 3,56.
     Deshalb haelt der Verlauf 0,22 bis 24 % durch und laesst erst darunter
     los. Nachgerechnet aus dem echten Bildpunkt, nicht aus dem Gefuehl. */
  background: linear-gradient(to bottom,
    rgba(243, 239, 230, 0.26) 0%,
    rgba(243, 239, 230, 0.22) 24%,
    rgba(243, 239, 230, 0.10) 46%,
    rgba(243, 239, 230, 0.55) 72%,
    var(--color-bg) 90%);
}

/* Die Umkehr der Textfarben im Hub (siehe .hub) gilt nur, solange das Bild
   dunkel trägt. Im hellen Thema tut es das nicht mehr — dort stand sonst
   fast weiße Schrift auf einem hellen Morgenhimmel. */
:root[data-theme='light'] .hub {
  --color-fg: var(--paper-950);
  --color-fg-muted: var(--paper-600);
  --color-fg-subtle: var(--paper-500);
  --hub-linie:  rgba(38, 33, 25, 0.16);
  --hub-umriss: rgba(38, 33, 25, 0.38);

  /* Dichter als im Dunkeln (0,88 gegen 0,74) und mit Schatten: Auf einem
     hellen Morgenhimmel ist eine zu durchscheinende weisse Kachel kein
     Element mehr, sondern ein Nebelfleck. */
  --hub-glas:          rgba(255, 255, 255, 0.88);
  --hub-glas-schatten: var(--shadow-md);
}

/* Die Kacheln sind im Hellen helles Glas statt dunkles — vorher waren sie das
   einzige Fast-Schwarz auf einer weißen Seite: vier dunkle Blöcke, die wie
   Löcher wirkten, obwohl ringsum alles hell ist. Der Wert steht jetzt als
   --hub-glas im Block darueber, damit die Erklaerkacheln ihn mitbenutzen. */

/* Die Tages-Kachel behält ihr Foto — es ist der Blick ins Stadion und der
   Grund, warum die Kachel anders aussieht als die drei daneben. Nur der
   Schleier davor dreht sich um, damit die Uhrzeit darauf dunkel lesbar
   bleibt. Unten dichter als oben: dort steht der Text. */
:root[data-theme='light'] .hub__tile--heute {
  border-color: rgba(0, 176, 94, 0.45);
  background:
    linear-gradient(to bottom, rgba(255, 255, 255, 0.34) 0%, rgba(255, 255, 255, 0.20) 34%,
                    rgba(248, 245, 238, 0.74) 68%, rgba(243, 239, 230, 0.92) 100%),
    var(--hero-bild, url('../assets/hero-bg.jpg'));
  /* MUSS hier stehen: `background` ist eine Kurzschreibweise und setzt Größe
     und Position mit zurück. Ohne diese zwei Zeilen zeigte die Kachel im
     hellen Thema die linke obere Ecke des Bildes in Originalgröße — also
     Wolken statt Stadion. */
  background-size: cover, 185% auto;
  background-position: center, center 78%;
}

/* Am Handy ist dieselbe Kachel 2,4-mal flacher, und der ganze Text steht in
   der oberen Haelfte statt verteilt. Ein Verlauf, der auf dem Schreibtisch
   oben duenn sein darf — dort steht nichts —, laesst hier den Titel direkt
   auf dem hellen Himmel liegen. Deshalb dichter und gleichmaessiger:
   Lesbarkeit vor Bildflaeche, dieselbe Abwaegung, die die dunkle Fassung mit
   0,30 oben schon trifft.

   STEHT ABSICHTLICH HIER UNTEN und nicht oben im Handy-Block: Beide Regeln
   haben dieselbe Spezifitaet, also entscheidet die Reihenfolge im Blatt.
   Weiter oben hatte diese hier keine Wirkung — nachgemessen, nicht vermutet. */
@media (max-width: 860px) {
  :root[data-theme='light'] .hub__tile--heute {
    background:
      linear-gradient(to bottom, rgba(255, 255, 255, 0.66) 0%, rgba(255, 255, 255, 0.56) 44%,
                      rgba(243, 239, 230, 0.82) 78%, rgba(243, 239, 230, 0.94) 100%),
      var(--hero-bild, url('../assets/hero-bg.jpg'));
    background-size: cover, 185% auto;
    background-position: center, center 78%;
  }
}

/* ==========================================================================
   SPORTAUSWAHL (16.09.2026) — die erste Seite, siehe public/sportwahl.js

   Sichtbar ueber html.sportwahl-an, nicht ueber .view.active: Die Klasse setzt
   der Kopfskript in index.html VOR dem ersten Malen. Die Id-Selektoren
   schlagen .view.active (0,2,0), deshalb braucht es kein !important.

   Tab-Leiste und Navigationspille gehen mit: Start, Heute und Spiele darin
   sind Bereiche der Fussball-Startseite, auf der Auswahl zeigten sie auf
   etwas, das nicht da ist — und "Spiele" stand dort sogar als aktiv.
   ========================================================================== */
#sport-view { display: none; }
html.sportwahl-an #sport-view { display: block; }
html.sportwahl-an #catalog-view,
html.sportwahl-an #game-view,
html.sportwahl-an .tabbar,
html.sportwahl-an .navpill { display: none; }
html.sportwahl-an body { padding-bottom: 0; }

.sportwahl { padding-top: var(--space-lg); }
.sportwahl__kopf { margin-bottom: var(--space-lg); }

.sportwahl__raster {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--space-sm);
}

/* Fussball steht doppelt breit: der einzige Sport mit echten Daten, Liga
   und Tages-Challenge. Die anderen laufen auf Demo-Daten. */
.sportwahl__raster > li:has(> a.sportkachel.is-bereit) { grid-column: span 2; }

.sportkachel {
  position: relative;
  height: 100%;
  min-height: 168px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-md);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-fg);
  text-decoration: none;
  transition: transform var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}

.sportkachel.is-bereit { border-color: var(--color-primary-text); }

@media (hover: hover) and (pointer: fine) {
  a.sportkachel:hover { transform: translateY(-3px); }
}
a.sportkachel:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 3px; }

.sportkachel__emoji { font-size: 2.6rem; line-height: 1; }
.sportkachel__text { display: flex; flex-direction: column; gap: var(--space-3xs); }
.sportkachel__name {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
}
.sportkachel__zeile { color: var(--color-fg-muted); font-size: var(--text-sm); }

.sportkachel__los {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: var(--color-primary-text);
  font-weight: 600;
  font-size: var(--text-sm);
}
.sportkachel__los svg { width: 1em; height: 1em; stroke: currentColor; fill: none; stroke-width: 2; }

/* Im Aufbau: klickbar, aber leiser. Das Emoji bleibt farbig — die Kachel
   fuehrt irgendwohin, und ein entsaettigtes Bild sagte "geht nicht". */

/* AM TELEFON EINE STAFFEL (27.09.2026, Ansage des Eigentuemers): alle
   Sportarten untereinander und ohne Scrollen auf einem Blick. Dieselbe
   Kachel, nur liegend und kleiner — Emoji links, Name und Zeile in der
   Mitte, der Pfeil rechts. Neun Zeilen zu je ~58 px passen unter Kopfleiste
   und Ueberschrift in 812 px; das Rasterformular mit zwei Spalten brauchte
   1399 px. Fussball verliert dafuer seine doppelte Breite — in einer Spalte
   gibt es keine; den Rand in Markenfarbe behaelt er. */
@media (max-width: 560px) {
  #sportwahl.section { padding-block: var(--space-xs) var(--space-sm); }
  .sportwahl { padding-top: 0; }
  .sportwahl__kopf { margin-bottom: var(--space-xs); }
  .sportwahl__kopf .sectionhead__title { font-size: 1.35rem; }
  .sportwahl__kopf .sectionhead__sub { font-size: var(--text-xs); margin-top: 2px; }
  .sportwahl__raster { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .sportwahl__raster > li:has(> a.sportkachel.is-bereit) { grid-column: auto; }
  .sportkachel {
    min-height: 0;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-sm);
    padding: 9px 14px;
    border-radius: var(--radius-md);
  }
  .sportkachel__emoji { font-size: 1.55rem; width: 1.8rem; text-align: center; flex: none; }
  .sportkachel__text { flex: 1 1 auto; min-width: 0; gap: 0; }
  .sportkachel__name { font-size: var(--text-base); line-height: 1.2; }
  .sportkachel__zeile { font-size: var(--text-xs); line-height: 1.3;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Das Wort "Oeffnen" traegt in der Zeile nichts mehr, die ganze Kachel ist
     der Link — der Pfeil allein sagt es. */
  .sportkachel__los { flex: none; font-size: 0; }
  .sportkachel__los svg { width: 18px; height: 18px; }
}

/* Der Chip neben dem Gruss im Hub: zurueck zur Auswahl. Er liegt auf dem
   Stadionfoto, also dieselbe Glasbehandlung wie die Hub-Kacheln. */
.sportchip {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  min-height: 36px;
  padding: 0.3em 0.85em 0.3em 0.6em;
  border-radius: var(--radius-full);
  border: 1px solid var(--hub-linie);
  background: var(--hub-glas);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  color: var(--color-fg);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
}
.sportchip__emoji { font-size: 1.1em; line-height: 1; }
.sportchip__wechsel { color: var(--color-fg-muted); font-weight: 500; }
.sportchip:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

/* ==========================================================================
   STARTSEITE EINER SPORTART IM AUFBAU (16.09.2026)

   Dieselbe Startseite wie beim Fussball; was dort nur Fussball-Inhalt haben
   kann, tritt hier zurueck. Ueber data-sport auf <html>, gesetzt in app.js.
   Die Tages-Challenge, "Heute" und das Podest regelt app.js selbst (mit
   Aufbau-Zustand statt Fussball-Daten) — hier steht nur, was reine Huelle ist:

   Die Filterleiste steht seit dem 16.09.2026 ueberall: Jede Sportart hat eine
   Tages-Challenge, "Heute" und "Taeglich" zaehlen richtig.
   Das Schlussband ("Bereit fuer den Anpfiff?") ist seit dem 16.09.2026 ganz weg.

   DIE ERKLAERKACHELN STANDEN HIER BIS ZUM 27.09.2026 AUCH AUF DER LISTE —
   "html:not([data-sport='fussball']) #spielinfo-grid { display: none; }".
   Der Grund war real: "Alle Spiele erklaert" listete unveraendert GAMES bzw.
   sichtbareSpiele(), also alle zehn Fussball-Spiele, auch unter Basketball.
   Die Kachel ist jetzt sportbezogen (spielinfoWahlOeffnen() in spielinfo.js
   liest ueber katalogSpiele() nur die Spiele der offenen Sportart), und der
   Karteninhalt selbst weicht ueber `basis` auf die geteilte Fussball-Mechanik
   aus (spielinfoInhalt() ebenda) — die Regel steht dann einmal, nicht siebenmal
   fuer jede Sportvariante. Die Sperre hier ist damit nicht mehr noetig und war
   die falsche Antwort auf das richtige Problem: sie versteckte den Baustein,
   statt ihn sportgerecht zu machen. */

.today__stats--sport { cursor: default; }

/* Das Einladungsfeld unter dem Sport-Chip (02.10.2026): rechtsbuendig, klein,
   dieselbe Glasbehandlung wie der Chip, weil es auf demselben Foto liegt. */
.einladung-feld { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; margin-top: 8px; }
.einladung-feld[hidden] { display: none; }
.einladung-feld__karte {
  display: flex; flex-direction: column; gap: 2px; max-width: 260px;
  padding: 8px 10px; border-radius: var(--radius-md, 16px);
  border: 1px solid var(--hub-linie); background: var(--hub-glas);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  color: var(--color-fg);
}
.einladung-feld__kopf { font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-fg-muted); }
.einladung-feld__name { font-weight: 800; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.einladung-feld__von { font-size: 0.75rem; color: var(--color-fg-muted); }
.einladung-feld__knoepfe { display: flex; gap: 6px; margin-top: 6px; }
.einladung-feld__knoepfe button {
  min-height: 32px; padding: 0.3em 0.9em; border-radius: var(--radius-full);
  font: inherit; font-size: 0.8rem; font-weight: 700; cursor: pointer;
}
/* Eigene Knopfstile: button.action/.secondary gelten nur unter #game-view,
   und dieses Feld liegt auf der Startseite (siehe CLAUDE.md, das ist hier
   schon dreimal schiefgegangen). */
.einladung-feld__ja { background: var(--color-primary); color: var(--color-on-primary); border: 1px solid var(--color-primary); }
.einladung-feld__nein { background: transparent; color: var(--color-fg); border: 1px solid var(--hub-linie); }
.einladung-feld__knoepfe button:disabled { opacity: 0.5; cursor: default; }
