/* ==========================================================================
   RIVALZ — Components
   Every interactive target is ≥44px. Every hover has a matching reverse.
   ========================================================================== */

/* ---------- Icon defaults -------------------------------------------------
   One blanket rule instead of an enumerated selector list: every icon added
   later inherits sane sizing automatically. Symbols that should be filled
   carry fill/stroke presentation attributes in the sprite, which win over
   this inherited value. */

svg use { pointer-events: none; }

svg:not(.sprite) {
  /* base.css makes media block-level; icons live in text flow, so opt back
     out or every inline icon forces a line break. */
  display: inline-block;
  vertical-align: -0.14em;
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.tnum { font-variant-numeric: tabular-nums; }

/* ---------- Buttons ------------------------------------------------------- */

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--color-fg);
  --btn-bd: transparent;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 44px;
  padding: 0.75rem 1.375rem;
  border: 1px solid var(--btn-bd);
  border-radius: var(--radius-full);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 650;
  letter-spacing: -0.008em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition:
    transform var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

.btn svg { font-size: 1.15em; }

.btn--lg { min-height: 52px; padding: 0.9rem 1.75rem; font-size: var(--text-base); }
.btn--sm { min-height: 40px; padding: 0.55rem 1rem;   font-size: var(--text-sm); }

.btn--primary {
  --btn-bg: var(--color-primary);
  --btn-fg: var(--color-on-primary);
  box-shadow: var(--shadow-glow);
}
.btn--outline {
  --btn-fg: var(--color-fg);
  --btn-bd: var(--color-border-strong);
}
.btn--glass {
  --btn-bg: var(--glass-bg);
  --btn-bd: var(--glass-border);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
}
.btn--ghost { --btn-fg: var(--color-fg-muted); }

/* Hover only where a real pointer exists — never gate content behind hover. */
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover { --btn-bg: var(--color-primary-hover); transform: translateY(-2px); }
  .btn--outline:hover { --btn-bd: var(--color-primary); --btn-fg: var(--color-primary-text); }
  .btn--glass:hover   { --btn-bd: var(--color-border-strong); transform: translateY(-2px); }
  .btn--ghost:hover   { --btn-fg: var(--color-fg); }
  .btn:hover svg[data-shift] { transform: translateX(3px); }
}

/* Touch feedback: the press is the only affordance a finger gets. */
.btn:active { transform: translateY(0) scale(0.97); }

.btn[disabled],
.btn[aria-disabled='true'] {
  opacity: 0.5;
  pointer-events: none;
}

/* ---------- Icon button --------------------------------------------------- */

.iconbtn {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  color: var(--color-fg-muted);
  font-size: 1.15rem;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.iconbtn:active { transform: scale(0.92); }

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

.iconbtn--round { border-color: var(--color-border); }
.iconbtn[disabled] { opacity: 0.35; pointer-events: none; }

/* Theme toggle: cross-fade + rotate, one icon per mode. */
.iconbtn__icon {
  grid-area: 1 / 1;
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-slow) var(--ease-spring);
}
.iconbtn__icon--sun  { opacity: 0; transform: rotate(-90deg) scale(0.5); }
.iconbtn__icon--moon { opacity: 1; transform: rotate(0) scale(1); }

:root[data-theme='dark'] .iconbtn__icon--sun  { opacity: 1; transform: rotate(0) scale(1); }
:root[data-theme='dark'] .iconbtn__icon--moon { opacity: 0; transform: rotate(90deg) scale(0.5); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .iconbtn__icon--sun  { opacity: 1; transform: rotate(0) scale(1); }
  :root:not([data-theme]) .iconbtn__icon--moon { opacity: 0; transform: rotate(90deg) scale(0.5); }
}

/* ---------- Badges & eyebrows --------------------------------------------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.3em 0.7em;
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  line-height: 1.4;
}

.badge--live  { background: var(--color-primary-soft); color: var(--color-primary-text); }
/* Zwei Gewichte fuer zwei verschiedene Aussagen.

   "neu jeden Tag" gilt fuer alle sieben Spiele mit Tagesraetsel und bleibt
   deshalb ruhig — ein Umriss, keine Flaeche. "Challenge heute" gilt fuer die
   zwei, die heute wirklich dran sind, und traegt Farbe. Waeren beide gleich
   auffaellig, waere der Unterschied wieder weg, um den es geht. */
.badge--taeglich { background: transparent; color: var(--color-fg-subtle); border: 1px solid var(--color-border); }
.badge--heute    { background: var(--color-accent-soft); color: var(--color-accent-text); }
.badge--soon  { background: var(--color-bg-inset);     color: var(--color-fg-muted); }
.badge--new   { background: var(--color-hot);          color: #fff; }
.badge--gold  { background: var(--color-accent-soft);  color: var(--color-accent-text); }
.badge--ghost {
  background: transparent;
  color: var(--color-fg-subtle);
  border: 1px solid var(--color-border);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-fg-muted);
}
.eyebrow--live { color: var(--color-primary-text); }

.pulse {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
  flex: none;
}
.pulse::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1.5px solid var(--color-primary);
  animation: pulse-ring 2.2s var(--ease-out) infinite;
}

@keyframes pulse-ring {
  0%       { transform: scale(0.6); opacity: 0.9; }
  70%,100% { transform: scale(1.6); opacity: 0; }
}

/* ---------- Generated cover art ------------------------------------------
   Die Kachel ist seit dem 03.09.2026 ein Ausschnitt Stadion bei Flutlicht:
   fast schwarzer Grund, darauf das SPIELBRETT in dünnen Linien und der
   Lichtkegel in der Farbe des Spiels (GAME_SCENES in game-art.js).

   --c1 ist der Akzent des Spiels, --c2 der Nachtgrund. Vorher waren es zwei
   Farbstopps über die Diagonale — der Verlauf, den jedes Werkzeug
   voreingestellt anbietet. Jetzt liegt der Grund flach und das Licht kommt
   als radialer Kegel aus dem SVG, also von dort, wo im Bild der Mast steht.

   `saturate(0.55)` ist ebenfalls weg: Es war da, um die kräftigen
   Familienverläufe zu dämpfen. Auf schwarzem Grund dämpft es nur noch den
   einen Akzent, der die Kachel unterscheidbar macht.

   Alles inline gezeichnet: keine Netzanfragen, kein Nachladeruckeln, scharf
   auf jeder Pixeldichte. */

/* Die Ausblick-Karte am Ende des Katalogs. Sie sieht aus wie eine Kachel,
   ist aber keine: gestrichelter Rand statt durchgezogener, kein Zeiger, kein
   Anheben beim Ueberfahren. Wer sie fuer ein Spiel haelt, hat wieder eine
   Karte vor sich, die nichts tut — und genau davon wollten wir weg. */
/* Das Ausblick-Band ueber die volle Breite unter den Kacheln (17.09.2026,
   vorher eine Karte). Gestrichelt und ohne Flaeche: kein Spiel, nichts zum
   Anklicken — es soll neben den Kacheln leise bleiben. */
.katalog-ausblick {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: var(--space-sm, 12px);
  padding: 12px 16px;
  border: 1px dashed var(--color-border, rgba(127, 127, 127, 0.35));
  border-radius: var(--radius-lg, 16px);
  color: var(--color-fg-muted);
  font-size: 0.9rem; line-height: 1.4;
}
.katalog-ausblick__zeichen { flex: none; display: inline-flex; color: var(--color-primary-text); }
.katalog-ausblick__kranz { width: 32px; height: 32px; display: block; }
.katalog-ausblick__text { flex: 1 1 auto; min-width: 0; }
.katalog-ausblick__text strong { color: var(--color-fg); font-weight: 700; margin-right: 4px; }
.katalog-ausblick__fuss { flex: none; font-size: 0.8rem; opacity: 0.85; white-space: nowrap; }
@media (max-width: 559px) {
  .katalog-ausblick { padding: 10px 12px; font-size: 0.82rem; }
  .katalog-ausblick__fuss { display: none; }
}

.cover {
  position: relative;
  overflow: hidden;
  background: var(--c2, #060907);
  isolation: isolate;
}

/* svg.cover__szene, nicht .cover__szene: die Grundregel oben (svg:not(.sprite))
   setzt width/height auf 1em und einen Standard-Strich. Sie ist spezifischer
   als eine blanke Klasse und würde die Szene sonst auf Icon-Größe schrumpfen.
   fill/stroke hier bewusst auf none — jede Form in der Szene bringt ihre
   eigenen Werte mit, alles ohne Angabe soll unsichtbar bleiben statt einen
   geerbten Strich zu bekommen. */
svg.cover__szene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  fill: none;
  stroke: none;
  stroke-width: 0;
  transition: transform var(--dur-slower) var(--ease-out);
}

/* Kachelbild (Fussball, 17.09.2026): dieselbe Flaeche wie die gezeichnete
   Szene, zugeschnitten statt verzerrt. Es liegt unter dem Verlauf von
   .cover::after, damit Schrift darauf lesbar bleibt wie bisher. */
img.cover__bild {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
  display: block;
  transition: transform var(--dur-slower) var(--ease-out);
}

/* Soft vignette so white text always clears 4.5:1 over the art */
.cover::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--grad-scrim);
  z-index: 1;
}


/* ---------- Game card ----------------------------------------------------- */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}

/* DAS BILD TRAEGT DIE KACHEL (27.09.2026): rund 65 % ihrer Hoehe statt
   weniger als die Haelfte. Moeglich, weil unter dem Titel keine zwei, drei
   Zeilen Beschreibung mehr stehen, sondern drei Schlagwoerter
   (.card__tags). Das Seitenverhaeltnis ist gemessen, nicht geschaetzt — bei
   einer Aenderung am Kachelkoerper neu messen (Bildhoehe / Kachelhoehe). */
.card__cover {
  position: relative;
  aspect-ratio: 3 / 2;
}

/* The cover element carries no intrinsic size — pin it to the ratio box. */
.card__cover > .cover { position: absolute; inset: 0; }


@media (min-width: 560px) {
  }

.card__flags {
  position: absolute;
  z-index: 2;
  top: var(--space-xs);
  left: var(--space-xs);
  right: var(--space-xs);
  display: flex;
  gap: var(--space-3xs);
  flex-wrap: wrap;
}

.card__flags .badge {
  background: rgba(15, 22, 19, 0.55);
  color: #fff;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.card__flags .badge--new  { background: var(--color-hot); }
.card__flags .badge--live { background: var(--color-primary); color: var(--color-on-primary); }
/* Auf dem Titelbild liegt ein dunkler, verwaschener Grund unter den Abzeichen
   (.card__flags .badge oben). Der schluckt die Umrissfassung — deshalb hier
   die Randfarbe kraeftiger und der Text hell. */
.card__flags .badge--taeglich { border-color: rgba(255, 255, 255, 0.45); color: #fff; }
.card__flags .badge--heute    { background: #FFC53D; color: #1a1200; }

/* Die Abzeichen liegen UNTER dem gestreckten Link der Kachel (z-index 3), das
   i darueber: Ein Tipp auf "neu jeden Tag" oeffnet weiter das Spiel, ein Tipp
   aufs i die Erklaerung. */
.card__flags { z-index: 4; pointer-events: none; align-items: center; }
/* Nur noch das i, rechts oben — am Telefon und am Laptop (29.09.2026). */
.card__flags { flex-wrap: nowrap; }
.card__flags .badge { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.card__info { flex: none; margin-left: auto; }
/* Ohne laufende Serie bleibt die Fusszeile leer — sie haelt dann keine Hoehe
   mehr fest, das Raster streckt die Kacheln einer Reihe ohnehin gleich. */
.card__foot:has(> .card__stats:empty) { display: none; }
.card__info {
  pointer-events: auto;
  position: relative; z-index: 4;
  display: inline-grid; place-items: center;
  width: 22px; height: 22px; padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: rgba(15, 22, 19, 0.55);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  color: #fff;
  font: italic 700 0.78rem/1 Georgia, 'Times New Roman', serif;
  cursor: pointer;
}
/* Die sichtbare Scheibe bleibt klein, die Trefferflaeche nicht. */
.card__info::before { content: ''; position: absolute; inset: -10px; }
.card__info:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

/* Drei Schlagwoerter statt eines Beschreibungssatzes (27.09.2026). Am Laptop
   als drei kleine Pillen, die bei Platzmangel umbrechen; am Telefon (28.09.)
   in EINER Zeile mit Punkt dazwischen — dort kostete der Umbruch eine Zeile. */
.card__tags { margin: 0; display: flex; flex-wrap: wrap; gap: 4px; }
.card__tags span {
  padding: 2px 7px;
  border-radius: var(--radius-full);
  background: var(--color-bg-inset);
  border: 1px solid var(--color-border);
  color: var(--color-fg-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.35;
  white-space: nowrap;
}
@media (max-width: 560px) {
  .card__tags {
    display: block;
    font-size: var(--text-xs); font-weight: 600; line-height: 1.35;
    color: var(--color-fg-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .card__tags span { padding: 0; border: 0; background: none; font-size: inherit; }
  .card__tags span + span::before { content: "·"; margin: 0 0.4em; opacity: 0.7; }
}

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

.card__title {
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 750;
  letter-spacing: var(--tracking-tight);
  line-height: 1.2;
  /* Platz für zwei Zeilen, auch wenn nur eine gebraucht wird. Der Tagline ist
     bereits begrenzt, aber ein langer Titel wie "Football Connections" brach
     um und zog seine gesamte Rasterzeile mit hoch — das Raster wirkte
     unruhig. Reservierter Platz hält alle Karten gleich hoch, unabhängig
     davon, wie ein künftiger Titel umbricht. */
  min-height: 2.4em;
}

/* Clamped so a long tagline can't make one grid row twice as tall as its
   neighbour — the grid reads as a catalogue, not a list of paragraphs. */
.card__tagline {
  font-size: var(--text-sm);
  color: var(--color-fg-muted);
  line-height: 1.4;
  flex: 1;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

@media (min-width: 560px) {
  .card__body    { padding: var(--space-sm); }
  .card__title   { font-size: var(--text-lg); }
  .card__tagline { -webkit-line-clamp: 3; line-clamp: 3; line-height: 1.45; }
}

.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2xs);
  margin-top: var(--space-2xs);
}

.card__stats {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  /* Die Zeile ist jetzt oft LEER: sie traegt nur noch eine laufende
     Tagesserie, und die haben die wenigsten Kacheln. Ohne feste Hoehe faellt
     sie dann auf 0 zusammen, und Kacheln mit und ohne Serie stuenden
     unterschiedlich hoch nebeneinander — genau das, was der Platzhalter
     "Noch nicht gespielt" frueher verhindert hat. Die Hoehe uebernimmt jetzt
     diese Zeile statt eines Textes, der nichts sagt.
     1.15rem entspricht einer Zeile in --text-xs (12px) bei --leading-tight. */
  min-height: 1.15rem;
  font-size: var(--text-xs);
  /* --color-fg-muted statt --color-fg-subtle: Letzteres kam auf der weissen
     Karte auf 2,9:1 und war damit unter der Lesbarkeitsgrenze. Der gedaempfte
     Ton liegt in beiden Themen ueber 4,5:1 und bleibt trotzdem zurueckhaltend. */
  color: var(--color-fg-muted);
  font-weight: 600;
}

/* Platzhalter für noch nicht gespielte Modi. Die Zeile bleibt besetzt, damit
   alle Karten gleich hoch sind — leer gelassen sahen die ungespielten Kacheln
   abgeschnitten aus. Zurückhaltender als eine echte Statistik, weil sie keine
   ist. */
.card__stats--leer {
  /* Vorher --color-fg-subtle mit zusaetzlich opacity 0.55: zusammen rund
     1,9:1, der Hinweis "Noch nicht gespielt" war praktisch unsichtbar. Er ist
     aber eine Angabe und kein Schmuck. Dieselbe Farbe wie die Zahlen daneben,
     nur leichter gesetzt — das haelt ihn zurueck, ohne ihn zu verstecken. */
  color: var(--color-fg-muted);
  font-weight: 500;
}

/* Two-up phone cards are ~160px wide — the third stat wraps and looks broken. */
.card__stats .is-time { display: none; }
@media (min-width: 560px) {
  .card__stats { gap: 0.75rem; }
  .card__stats .is-time { display: inline-flex; }
}
.card__stats span { display: inline-flex; align-items: center; gap: 0.3em; }
.card__stats svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
/* The star path is filled with currentColor in the sprite, so tint the
   wrapper rather than the svg — a fill rule here would lose to the
   presentation attribute. */
.card__stats .is-star { color: var(--color-accent-text); }

/* Stretched link: whole card is the target, but only one tab stop. */
.card__link::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 3;
  border-radius: inherit;
}
.card__link:focus-visible::after {
  outline: 2px solid var(--color-ring);
  outline-offset: -3px;
}

.card--soon { opacity: 0.82; }

/* HIER STAND EIN SELEKTOR OHNE BLOCK — `.card--soon` allein, ohne { }.

   Entstanden beim Loeschen von `.card--soon .cover { filter: saturate(0.55) }`
   (Commit b27bdc5): Die Deklaration ging, der Selektor blieb stehen.

   Das ist kein Schoenheitsfehler. Ein Selektor ohne Block macht die Regel
   ungueltig, und der Parser liest weiter, bis er den naechsten Block findet —
   er verschluckt also den GANZEN @media-Block darunter. Die Kacheln haben
   sich seither nirgends mehr beim Ueberfahren gehoben, und niemandem ist es
   aufgefallen, weil nichts kaputtgeht, wenn eine Zierde ausfaellt.

   Gefunden beim Nachgehen des Wackelns in Abseits: Die Schwebe-Regel haette
   dort greifen muessen und tat es nicht, obwohl die Medienabfrage passte. */

@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-5px);
    box-shadow: var(--shadow-lg);
    border-color: var(--color-border-strong);
  }
  .card:hover .cover__szene, .card:hover .cover__bild { transform: scale(1.06); }
  .card:hover .card__title  { color: var(--color-primary-text); }
  .card--soon:hover { transform: translateY(-2px); }
}

.card:active { transform: translateY(-1px) scale(0.99); }

/* ---------- Podium card (Netflix-style ranked row) ------------------------ */

.podium {
  position: relative;
  display: flex;
  min-width: 0;
}

/* Grosse Umrissziffer, oben links AUF der Szene.

   Vorher stand sie als eigenes Element links neben der Karte und nahm dort
   Platz im Fluss weg. In der Rasterzelle hiess das: 126 px fuer die Ziffer,
   nur der Rest fuer die Karte — die Kacheln der Rangliste waren damit
   schmaler als die der Empfehlungen daneben, obwohl beide dieselbe Zelle
   fuellen sollen.

   Jetzt liegt sie absolut ueber der Grafik, in derselben Ecke wie die Marke
   im Empfehlungsfall. Beide Zustaende sind dadurch gleich gebaut: volle
   Kachel, Kennzeichnung oben links. Nur der Umriss, keine Fuellung — die
   Szene bleibt dahinter sichtbar. */
.podium__rank {
  position: absolute;
  z-index: 3;
  top: -0.1em;
  left: 0.04em;
  font-family: var(--font-display);
  font-size: clamp(4.5rem, 3rem + 5vw, 7.5rem);
  font-weight: 900;
  font-style: italic;
  line-height: 0.9;
  letter-spacing: -0.06em;
  color: transparent;
  /* NICHT --rank-outline: Der Token ist auf den Seitenhintergrund abgestimmt
     (18–20 % Deckkraft) und war richtig, solange die Ziffer neben der Karte
     stand. Auf dem Farbverlauf der Grafik verschwindet er. Weiss mit halber
     Deckkraft plus ein dunkler Schatten haelt den Umriss ueber hellen wie
     dunklen Stellen der Szene. */
  -webkit-text-stroke: 2.5px rgba(255, 255, 255, 0.55);
  filter: drop-shadow(0 2px 6px rgba(15, 22, 19, 0.55));
  user-select: none;
  pointer-events: none;
  transition: -webkit-text-stroke-color var(--dur-base) var(--ease-out);
}

.podium__card {
  position: relative;
  /* Fuellt die Rasterzelle. Vorher stand hier eine feste Breite — die ist der
     Grund, warum die Reihe frueher rechts auslief. */
  width: 100%;
  min-width: 0;
  flex: 1 1 auto;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--color-border);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}

.podium__cover { aspect-ratio: 4 / 3; }

.podium__body {
  position: absolute;
  z-index: 2;
  inset: auto 0 0 0;
  padding: var(--space-sm);
  color: #fff;
}

.podium__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  line-height: 1.15;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.5);
}

.podium__meta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.25rem;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82);
}

.podium__meta span { display: inline-flex; align-items: center; gap: 0.35em; }

@media (hover: hover) and (pointer: fine) {
  .podium:hover .podium__card { transform: translateY(-6px) scale(1.015); box-shadow: var(--shadow-xl); }
  .podium:hover .podium__rank { -webkit-text-stroke-color: var(--color-primary-text); }
  .podium:hover .cover__szene { transform: scale(1.05); }
}

/* ---------- Rails (horizontal scroll) -------------------------------------
   Native overflow scrolling — momentum, accessibility and a11y focus order
   all come for free, unlike a JS-driven carousel. */

.rail {
  display: flex;
  gap: var(--space-md);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--gutter);
  padding-inline: var(--gutter);
  padding-block: var(--space-md) var(--space-lg);
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }

/* ---------- Podest: Raster statt Schiene ----------------------------------

   Die drei Kacheln teilen sich die volle Breite des Containers, gleiche
   Aufteilung wie das Raster unter "Heute". Vorher war das eine waagerecht
   scrollende Leiste mit Karten fester Breite (max. 320 px): Bei genau drei
   Eintraegen endete sie nach gut der Haelfte und rechts blieb eine leere
   Flaeche stehen — die Ueberschrift spannte ueber die ganze Breite, der
   Inhalt darunter nicht.

   Am Handy zwei Spalten mit der dritten Kachel darunter, wie im Katalog: Drei
   nebeneinander waeren dort briefmarkengross. */
.podiumgrid {
  list-style: none;
  margin: var(--space-md) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: stretch;
}

.railnav { display: none; gap: var(--space-3xs); }

/* ---------- Filters ------------------------------------------------------- */

.filters {
  display: flex;
  gap: var(--space-2xs);
  overflow-x: auto;
  scrollbar-width: none;
  margin-inline: calc(var(--gutter) * -1);
  padding-inline: var(--gutter);
  padding-block: var(--space-3xs) var(--space-md);
  overscroll-behavior-x: contain;
}
.filters::-webkit-scrollbar { display: none; }

/* Der Verlauf sagt, dass rechts noch ein Chip steht. Die Klasse setzt
   filterRandPruefen() in app.js — nur wenn tatsaechlich gescrollt werden kann,
   sonst knabberte er den letzten Chip auch dann an, wenn alles hineinpasst.

   Als Maske und nicht als aufgelegter Verlauf: Die Leiste steht direkt auf dem
   Seitenhintergrund, und eine zweite Stelle, die dessen Farbe kennen muesste,
   waere bei jedem Themenwechsel eine Stelle zu viel. */
.filters--mehr {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 44px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 44px), transparent);
}

.filter {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 40px;
  padding: 0.5rem 1rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-fg-muted);
  font-size: var(--text-sm);
  font-weight: 650;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
/* 40 px, also nur vier zu wenig. Trotzdem mitgenommen: Die Leiste scrollt
   waagerecht, und beim Wischen streift man die Chips ohnehin.
   Hinter der Grundregel, siehe .langswitch__btn oben. */
@media (pointer: coarse) {
  .filter { min-height: 44px; }
}

/* 17 px, nicht 24: Die Symbole sind in einer 24er-Box gezeichnet, im Chip
   stehen sie neben 14-px-Text. In voller Groesse waere das Symbol das
   Auffaelligste an einem Chip, dessen Aussage im Wort steckt. */
.filter svg { width: 17px; height: 17px; flex: none; }

.filter:active { transform: scale(0.95); }

.filter[aria-pressed='true'] {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}

.filter__count {
  /* Kein margin-left mehr: Der Abstand kommt jetzt aus dem gap des Chips,
     sonst stuende die Zahl doppelt so weit weg wie das Symbol. */
  opacity: 0.65;
  font-variant-numeric: tabular-nums;
}

@media (hover: hover) and (pointer: fine) {
  .filter:hover { color: var(--color-fg); border-color: var(--color-border-strong); }
  .filter[aria-pressed='true']:hover { background: var(--color-primary-hover); color: var(--color-on-primary); }
}

/* ---------- Stat band ----------------------------------------------------- */

.statband {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-border);
  border: 1px solid var(--color-border);
}

.statband__item {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: var(--space-md) var(--space-sm);
  background: var(--color-surface);
  text-align: center;
}

.statband__num {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 850;
  letter-spacing: var(--tracking-tight);
  line-height: 1;
  background: var(--grad-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.statband__label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-fg-muted);
  text-wrap: balance;
}

/* ---------- Steps --------------------------------------------------------- */

.steps {
  display: grid;
  gap: var(--space-md);
}

.step {
  position: relative;
  padding: var(--space-lg) var(--space-md) var(--space-md);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  overflow: hidden;
}

.step::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--grad-brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-slower) var(--ease-out);
}
.step.is-in::before { transform: scaleX(1); }

.step__num {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 900;
  font-style: italic;
  letter-spacing: -0.05em;
  color: transparent;
  -webkit-text-stroke: 1.5px var(--color-border-strong);
  line-height: 1;
}

.step__title { font-size: var(--text-xl); margin-top: var(--space-2xs); }
.step__text  { color: var(--color-fg-muted); margin-top: var(--space-3xs); font-size: var(--text-sm); }


/* ---------- Tab bar (mobile) ---------------------------------------------- */

.tabbar {
  position: fixed;
  z-index: var(--z-tabbar);
  inset: auto 0 0 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  height: calc(var(--tabbar-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: var(--glass-bg);
  border-top: 1px solid var(--glass-border);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
}

.tabbar__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.01em;
  color: var(--color-fg-subtle);
  transition: color var(--dur-fast) var(--ease-out);
}
.tabbar__item svg { width: 22px; height: 22px; }
.tabbar__item:active { color: var(--color-primary-text); }
.tabbar__item.is-active { color: var(--color-primary-text); }

.tabbar__item.is-active::before {
  content: '';
  position: absolute;
  top: 0;
  width: 26px;
  height: 2.5px;
  border-radius: 0 0 3px 3px;
  background: var(--color-primary);
}
.tabbar__item { position: relative; }

/* ---------- Language switch ----------------------------------------------- */

.langswitch {
  display: flex;
  padding: 3px;
  border-radius: var(--radius-full);
  background: var(--color-bg-inset);
  border: 1px solid var(--color-border-subtle);
}

.langswitch__btn {
  min-width: 40px;
  min-height: 32px;
  padding-inline: 0.6rem;
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: 750;
  letter-spacing: var(--tracking-wide);
  color: var(--color-fg-muted);
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}

/* 40x32 gemessen, und DE und EN liegen unmittelbar nebeneinander — die
   Kombination aus klein und dicht ist die, bei der man sich vertippt.

   MUSS HINTER der Grundregel stehen: Gleiche Spezifitaet, also entscheidet
   die Reihenfolge. Davor gesetzt gewinnt das min-height:32px darueber, und
   die Medienabfrage laeuft wirkungslos mit. Genau so beim ersten Versuch
   passiert — die Messung hat es gezeigt, 40x32 unveraendert. */
@media (pointer: coarse) {
  .langswitch__btn { min-height: 44px; min-width: 44px; }
}
.langswitch__btn.is-active {
  background: var(--color-surface);
  color: var(--color-fg);
  box-shadow: var(--shadow-xs);
}

/* ---------- Meta rows ----------------------------------------------------- */

.metaitem {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font-size: var(--text-sm);
  color: var(--color-fg-muted);
  white-space: nowrap;
}
.metaitem strong { color: var(--color-fg); font-weight: 700; }
.metaitem svg { font-size: 1.05em; }

.avatars { display: inline-flex; }
.avatars i {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--color-bg);
  background: var(--grad-brand);
}
.avatars i + i { margin-left: -8px; }

.avatars--lg i { width: 40px; height: 40px; border-width: 3px; }
.avatars--lg i + i { margin-left: -14px; }
.avatars i:nth-child(2) { background: var(--grad-gold); }
.avatars i:nth-child(3) { background: linear-gradient(120deg, var(--sky-400), var(--lime-500)); }

/* Läuft eine Tagesserie, steht sie in der Fußzeile der Kachel statt der
   Roh-Zahlen. Sie ist der Grund, morgen wiederzukommen — "3 gespielt,
   1 gewonnen" ist keiner. */
.card__serie { font-weight: 700; color: var(--color-primary-text); }
