/* KoHub landing page — single stylesheet, no external requests.
   Eigene Identität: 16 px card radii, 8 px control radii, weicher zweilagiger
   Kartenschatten, Marke Petrol #0f6b70, warme helle Flächen. */

/* ==========================================================================
   1. Fonts — self-hosted, subset to latin, weights 400 and 700 only
   ========================================================================== */

@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/fonts/PlusJakartaSans-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("/assets/fonts/PlusJakartaSans-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   2. Tokens — eine Quelle für Farbe, Radius, Schatten und Bewegung
   ========================================================================== */

:root {
  color-scheme: light dark;

  /* Marke — tiefes Petrol. Jeder Wert unten ist gegen die Flächen gerechnet,
     auf denen er tatsächlich vorkommt; die Zahl in Klammern ist der geprüfte
     Kontrast. Wer hier etwas ändert, muss neu rechnen. */
  --blue: #0f6b70;          /* 6.25 auf Weiß, 5.74 auf --page */
  --blue-strong: #0a5257;   /* 8.91 mit weißem Text — der Hover-Zustand */
  --blue-line: #bfdcdd;     /* dekorative Kartenkante, kein Trennstrich */
  /* Marke bei 10 % — als Token, weil rgba() nicht aus --blue ableitbar ist. */
  --blue-wash: rgba(15, 107, 112, 0.1);
  /* Text auf --blue. Eigener Token, damit Dark Mode ihn umdrehen kann. */
  --on-blue: #ffffff;

  /* Akzent — warmes Sand. Nur für Badges und Hervorhebungen, nie für Flächen
     mit Fließtext: darauf schreibt --ink (7.68), kein gedämpfter Ton. */
  --accent: #e8a33d;
  --on-accent: #14211f;

  --ink: #14211f;           /* 15.22 auf --page */
  --body: #3a4b49;          /* 9.20 auf Weiß */
  --muted: #566b69;         /* 5.67 auf Weiß, 5.03 auf --note-bg */
  /* 5.07 auf Weiß — von #66807d angehoben, das nur 4.24 erreichte. */
  --faint: #5c7370;

  --tint: #f0f6f6;
  --tint-soft: #f8f7f4;
  --note-bg: #eaf3f3;
  --rule: #e4e0d8;
  --placeholder: #b45309;

  --surface: #ffffff;
  /* Warmes Off-White statt kaltem Grau: nimmt dem Petrol die Kühle. */
  --page: #f7f5f1;

  /* Header backdrop — translucent so the blur reads, opaque enough to hide
     content scrolling underneath. */
  --header-bg: rgba(255, 255, 255, 0.88);
  --scrim: rgba(20, 33, 31, 0.7);

  --font-sans: "Plus Jakarta Sans", system-ui, -apple-system, sans-serif;

  /* Radien — lg 8, xl 12, 2xl 16, 3xl 22. */
  --radius-control: 8px;
  --radius-chip: 12px;
  --radius-card: 16px;
  --radius-panel: 22px;

  /* Schatten — Farbe folgt --ink, nicht einem neutralen Grau. */
  --shadow-card: 0 1px 2px rgba(20, 33, 31, 0.04), 0 4px 12px rgba(20, 33, 31, 0.05);
  --shadow-lift: 0 2px 4px rgba(20, 33, 31, 0.04), 0 12px 28px rgba(20, 33, 31, 0.08);
  --shadow-dialog: 0 30px 70px -20px rgba(20, 33, 31, 0.45);

  --shell-max: 1180px;
  --shell-gutter: 20px;
  --section-gap: 60px;
  --header-height: 64px;

  /* Motion — eine Sprache für die ganze Seite. Getrennte Dauern statt eines
     einzigen Werts: Zustandswechsel dürfen nicht so lange brauchen wie ein
     Element, das neu in den Blick kommt.
       instant  Farbe, Deckkraft — darf nicht spürbar verzögern
       quick    Hover und Fokus auf Bedienelementen
       settle   Karten, die sich heben; Panels, die aufgehen
       enter    Inhalt, der beim Scrollen erscheint
     Das Ease ist bewusst kein "ease": leicht überschießend am Anfang,
     ruhig am Ende — das lässt Bewegung geführt statt mechanisch wirken. */
  --dur-instant: 120ms;
  --dur-quick: 180ms;
  --dur-settle: 280ms;
  --dur-enter: 480ms;
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.26, 0.64, 1);

  /* Wie weit sich ein Element beim Erscheinen bewegt. Klein halten: es soll
     als Aufblenden lesen, nicht als Rutschen. */
  --enter-shift: 14px;

  /* --- Glas und Licht ------------------------------------------------------
     Glassmorphism funktioniert nur ueber einer Flaeche, die etwas zu zeigen
     hat: die Glaskarten liegen deshalb ausschliesslich auf den Verlaufs- und
     Glow-Baendern (Hero, Metrics, Closing), nie auf --surface. Dort waere die
     Transparenz nicht sichtbar und der Blur nur Rechenaufwand.

     --glass-bg bleibt bewusst hoch deckend (86 %): Fliesstext auf Glas muss
     4.5:1 halten, auch wenn hinter der Karte gerade der helle Teil des
     Verlaufs liegt. Der Effekt kommt aus Kante und Blur, nicht aus Durchsicht. */
  --glass-bg: rgba(255, 255, 255, 0.86);
  --glass-bg-strong: rgba(255, 255, 255, 0.94);
  /* Die Kante traegt Markenfarbe, nicht Weiss. Eine weisse Kante auf einem
     hellen Band ist rechnerisch unsichtbar (1.06 gegen den Grundton) — sie
     waere Code ohne Wirkung. Petrol bei 22 % erreicht 1.39 gegen die
     Glasflaeche und liegt damit genau zwischen --rule (1.32) und --blue-line
     (1.45): die Glaskarte hat dieselbe Kantensichtbarkeit wie jede andere
     Karte der Seite. Die Trennung selbst leistet hier der Schatten, wie bei
     .card — deshalb ist 3:1 an dieser Stelle nicht die Messlatte. */
  --glass-border: rgba(15, 107, 112, 0.22);
  --glass-blur: saturate(1.3) blur(14px);

  /* Der Markenschein. Zwei Staerken: --glow-brand fuer grosse Flaechen
     (hinter dem Telefon), --glow-soft fuer Karten und Baender. */
  --glow-brand: rgba(15, 107, 112, 0.16);
  --glow-soft: rgba(15, 107, 112, 0.08);
  --glow-accent: rgba(232, 163, 61, 0.14);

  /* Verlaufsflaechen. Als Token, weil sie an drei Stellen vorkommen und im
     Dark Mode komplett andere Stopps brauchen. */
  --gradient-brand: linear-gradient(135deg, #0f6b70 0%, #12787d 45%, #188275 100%);
  --gradient-line: linear-gradient(90deg, transparent, var(--blue-line), transparent);

  /* Hebung der Karten beim Ueberfahren. Ein Token, damit Karte, Plan und
     Kennzahl dieselbe Strecke gehen. */
  --lift: 4px;
}

/* Dark mode — Petrol aufgehellt auf #5fbfc4, damit es als Linktext auf der
   dunklen Karte 4.5:1 erreicht. Jeder Token oben wird hier neu gesetzt;
   keiner erbt einen hellen Wert. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --blue: #5fbfc4;          /* 7.98 auf --surface, 7.33 auf --tint */
    --blue-strong: #8fd6d9;   /* 11.38 mit dunklem Text — Hover */
    --blue-line: #2a4a4c;
    --blue-wash: rgba(95, 191, 196, 0.16);
    /* Bleibt dunkel: die Buttonfläche ist in diesem Theme das helle Petrol. */
    --on-blue: #0b1413;

    --accent: #f0b963;        /* 9.69 auf --surface */
    --on-accent: #14211f;     /* 9.33 auf --accent */

    --ink: #edf3f2;
    --body: #bccbc9;
    --muted: #9aafac;
    --faint: #88a09d;

    --tint: #182524;
    --tint-soft: #16221f;
    --note-bg: #16292a;
    /* Auf dunklen Flächen trägt die Kante die Karten-/Hintergrundtrennung,
       die im hellen Theme der Schatten übernimmt — sie muss deshalb die
       3:1-Schwelle für Nicht-Text erreichen, nicht nur wahrnehmbar sein.
       #587674 ist der dunkelste Wert, der das gegen alle vier Flächen
       schafft (3.08 auf --note-bg, die engste Paarung). */
    --rule: #587674;
    --placeholder: #fb923c;

    --surface: #131d1c;
    --page: #0d1615;

    --header-bg: rgba(19, 29, 28, 0.88);
    --scrim: rgba(4, 10, 9, 0.78);

    /* Shadows carry almost no weight on dark surfaces — the border does the
       separating instead, so these stay subtle rather than invisible-black. */
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.32), 0 4px 12px rgba(0, 0, 0, 0.28);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.34), 0 12px 28px rgba(0, 0, 0, 0.42);
    --shadow-dialog: 0 30px 70px -20px rgba(0, 0, 0, 0.75);

    /* Auf Dunkel liegt Glas nicht auf Weiss, sondern auf einem aufgehellten
       Ausschnitt der Flaeche: das Weiss von oben wuerde als milchiger Fleck
       lesen. Die Kante uebernimmt hier die Arbeit des Schattens und muss
       deshalb sichtbar bleiben, nicht nur geahnt. */
    --glass-bg: rgba(25, 38, 37, 0.82);
    --glass-bg-strong: rgba(25, 38, 37, 0.92);
    /* 0.48, nicht weniger: auf Dunkel traegt die Kante die Trennung zwischen
       Karte und Band, sie muss also die 3:1-Schwelle fuer Nicht-Text halten.
       Gerechnet gegen die drei Stellen, an denen Glas auf dem Band liegt
       (ueber dem Markenschein, dem Grundton und dem warmen Schein) ergibt
       0.46 knapp 3.09 — 0.48 haelt den Abstand, ohne als Linie zu lesen. */
    --glass-border: rgba(143, 214, 217, 0.48);

    --glow-brand: rgba(95, 191, 196, 0.16);
    --glow-soft: rgba(95, 191, 196, 0.09);
    --glow-accent: rgba(240, 185, 99, 0.12);

    --gradient-brand: linear-gradient(135deg, #0f4b4f 0%, #12595e 45%, #14655c 100%);
  }
}

:root[data-theme="dark"] {
  --blue: #5fbfc4;          /* 7.98 auf --surface, 7.33 auf --tint */
  --blue-strong: #8fd6d9;   /* 11.38 mit dunklem Text — Hover */
  --blue-line: #2a4a4c;
  --blue-wash: rgba(95, 191, 196, 0.16);
  /* Bleibt dunkel: die Buttonfläche ist in diesem Theme das helle Petrol. */
  --on-blue: #0b1413;

  --accent: #f0b963;        /* 9.69 auf --surface */
  --on-accent: #14211f;     /* 9.33 auf --accent */

  --ink: #edf3f2;
  --body: #bccbc9;
  --muted: #9aafac;
  --faint: #88a09d;

  --tint: #182524;
  --tint-soft: #16221f;
  --note-bg: #16292a;
  /* Auf dunklen Flächen trägt die Kante die Karten-/Hintergrundtrennung,
     die im hellen Theme der Schatten übernimmt — sie muss deshalb die
     3:1-Schwelle für Nicht-Text erreichen, nicht nur wahrnehmbar sein.
     #587674 ist der dunkelste Wert, der das gegen alle vier Flächen
     schafft (3.08 auf --note-bg, die engste Paarung). */
  --rule: #587674;
  --placeholder: #fb923c;

  --surface: #131d1c;
  --page: #0d1615;

  --header-bg: rgba(19, 29, 28, 0.88);
  --scrim: rgba(4, 10, 9, 0.78);

  /* Shadows carry almost no weight on dark surfaces — the border does the
     separating instead, so these stay subtle rather than invisible-black. */
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.32), 0 4px 12px rgba(0, 0, 0, 0.28);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.34), 0 12px 28px rgba(0, 0, 0, 0.42);
  --shadow-dialog: 0 30px 70px -20px rgba(0, 0, 0, 0.75);

  /* Auf Dunkel liegt Glas nicht auf Weiss, sondern auf einem aufgehellten
     Ausschnitt der Flaeche: das Weiss von oben wuerde als milchiger Fleck
     lesen. Die Kante uebernimmt hier die Arbeit des Schattens und muss
     deshalb sichtbar bleiben, nicht nur geahnt. */
  --glass-bg: rgba(25, 38, 37, 0.82);
  --glass-bg-strong: rgba(25, 38, 37, 0.92);
  /* 0.48, nicht weniger: auf Dunkel traegt die Kante die Trennung zwischen
     Karte und Band, sie muss also die 3:1-Schwelle fuer Nicht-Text halten.
     Gerechnet gegen die drei Stellen, an denen Glas auf dem Band liegt
     (ueber dem Markenschein, dem Grundton und dem warmen Schein) ergibt
     0.46 knapp 3.09 — 0.48 haelt den Abstand, ohne als Linie zu lesen. */
  --glass-border: rgba(143, 214, 217, 0.48);

  --glow-brand: rgba(95, 191, 196, 0.16);
  --glow-soft: rgba(95, 191, 196, 0.09);
  --glow-accent: rgba(240, 185, 99, 0.12);

  --gradient-brand: linear-gradient(135deg, #0f4b4f 0%, #12595e 45%, #14655c 100%);
}


@media (min-width: 900px) {
  :root {
    --shell-gutter: 40px;
    /* tuned down with the copy: at 112px the shortened sections left a
       screen-height gap between them */
    --section-gap: 84px;
  }
}

/* ==========================================================================
   3. Reset and base
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchor targets must clear the sticky header. */
  scroll-padding-top: calc(var(--header-height) + 24px);
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.6;
  color: var(--body);
  background: var(--surface);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Card grids and tables must never push the page sideways. */
  overflow-x: hidden;
}

h1,
h2,
h3 {
  margin: 0;
  color: var(--ink);
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: -0.015em;
}

p {
  margin: 0;
}

/* figure und blockquote bringen im Browser einen seitlichen Standardabstand
   mit (40 px links und rechts). Unbemerkt bleibt das nur, solange beide nicht
   im Raster stehen — dort schnuert der Abstand die Zelle ein, ohne dass das
   Raster davon weiss. .hero__figure setzte das bisher einzeln zurueck; der
   Reset gehoert hierher, damit die naechste Figur ihn nicht erneut braucht. */
figure,
blockquote {
  margin: 0;
}

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

img,
svg {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

a {
  color: var(--blue);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  /* Bare URLs in the legal pages have no spaces to break at and would push the
     page sideways on a 320 px screen. */
  overflow-wrap: break-word;
}

a:hover {
  text-decoration: none;
}

/* 2 px at 3:1 against both themes' surfaces — --blue lightens in dark mode, so
   the indicator keeps its contrast without a theme-specific rule. */
:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: 4px;
}

/* On the brand-coloured button the blue ring would sit on blue. */
.button--primary:focus-visible {
  outline-color: var(--ink);
}

/* ==========================================================================
   4. Layout
   ========================================================================== */

.shell {
  width: 100%;
  max-width: var(--shell-max);
  margin: 0 auto;
  padding-inline: var(--shell-gutter);
}

.section {
  padding-block: var(--section-gap);
}

.section--page {
  background: var(--page);
}

/* Adjacent sections on the same surface: the boundary is invisible, so the
   two full gaps around it read as a hole rather than as a rhythm. Halving the
   upper one keeps the sequence breathing without the dead screen. */
.section--page + .section--page {
  padding-top: calc(var(--section-gap) / 2);
}

.section__head {
  max-width: 62ch;
  margin-bottom: 32px;
}

.section__head--center {
  margin-inline: auto;
  text-align: center;
}

.section__title {
  font-size: clamp(1.65rem, 3.2vw, 2.35rem);
}

.section__intro {
  margin-top: 14px;
  color: var(--muted);
  font-size: 1.08rem;
  max-width: 62ch;
}

.section__head--center .section__intro {
  margin-inline: auto;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 20px;
  background: var(--surface);
  border-radius: 0 0 var(--radius-control) 0;
  color: var(--blue);
  font-weight: 700;
}

.skip-link:focus {
  left: 0;
}

/* ==========================================================================
   5. Building blocks
   ========================================================================== */

/* Kicker — letter-spaced label above a heading. Spacing follows the app's
   SectionLabel (0.07em), not the print styleguide's wider 0.18em. */
.kicker {
  display: block;
  margin-bottom: 14px;
  color: var(--blue);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

/* Bar — 96 x 4 px blue rectangle. Exactly once on the page (hero). */
.bar {
  width: 96px;
  height: 4px;
  margin-bottom: 26px;
  background: var(--blue);
  border-radius: 999px;
}

/* Icon chip — mirrors the app's IconChip: brand at 10 % opacity, 12 px radius. */
.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  /* Statt einer Flaeche ein kurzer Verlauf: der Chip bekommt dadurch eine
     Lichtrichtung und liest als Objekt, nicht als Farbfeld. Die Werte bleiben
     im Bereich von --blue-wash, der Kontrast des Icons aendert sich nicht. */
  background:
    linear-gradient(140deg, var(--blue-wash) 0%, rgba(15, 107, 112, 0.04) 100%);
  border: 1px solid transparent;
  border-radius: var(--radius-chip);
  color: var(--blue);
  transition:
    transform var(--dur-settle) var(--ease-spring),
    background-color var(--dur-settle) var(--ease-out),
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chip {
    background:
      linear-gradient(140deg, var(--blue-wash) 0%, rgba(95, 191, 196, 0.05) 100%);
  }
}

:root[data-theme="dark"] .chip {
  background:
    linear-gradient(140deg, var(--blue-wash) 0%, rgba(95, 191, 196, 0.05) 100%);
}


.chip--sm {
  width: 34px;
  height: 34px;
  border-radius: 10px;
}

/* Card — app Card component: 16 px radius, hairline border, soft shadow. */
.card {
  position: relative;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  /* isolation, damit der Verlauf in ::before nicht aus der Karte heraus
     mit dem Hintergrund der Sektion mischt. */
  isolation: isolate;
  transition:
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out),
    transform var(--dur-settle) var(--ease-out);
}

/* Ein Lichtschimmer von oben, der beim Ueberfahren aufgeht. Er liegt HINTER
   dem Inhalt (z-index unter dem Standardfluss) und traegt keinen Text — er
   kann den Kontrast also nicht senken. */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(120% 80% at 50% 0%, var(--glow-soft), transparent 70%);
  opacity: 0;
  transition: opacity var(--dur-settle) var(--ease-out);
}

/* Karten heben sich beim Überfahren an — 4 px statt der frueheren 2 px, jetzt
   von Schimmer und Kante begleitet, sodass die Bewegung als eine Gestaltung
   liest und nicht als Wackeln. Die Rasterkante bleibt bei 4 px ruhig, weil
   der Schatten mitwaechst und die Karte damit gehoben, nicht verschoben,
   erscheint. */
.card:hover {
  transform: translateY(calc(var(--lift) * -1));
  border-color: var(--blue-line);
  box-shadow: var(--shadow-lift);
}

.card:hover::before {
  opacity: 1;
}

/* Das Icon dreht sich einen Hauch mit und bekommt Farbe unter sich — die
   Karte antwortet damit an der Stelle, auf die das Auge zuerst faellt. */
.card:hover .chip {
  transform: translateY(-1px) scale(1.06);
  border-color: var(--blue-line);
  box-shadow: 0 4px 12px var(--glow-soft);
}

.card__title {
  /* tightened with the copy: two lines of text under a 18px gap read as a hole */
  margin-top: 14px;
  font-size: 1.08rem;
  /* Deutsche Komposita sind laenger als die Spalte: "Auftragsverarbeitungs-
     vertrag" braucht als unteilbares Wort 305 px und zwang das 1fr-Raster bei
     640 px auf 299/283 px statt auf zwei gleiche Spalten. Trennen erlaubt es,
     die Spaltenbreite zu unterschreiten. `hyphens: auto` nutzt die deutschen
     Trennregeln (<html lang> ist gesetzt), `overflow-wrap` faengt den Fall
     ab, dass der Browser nicht trennt. Ebenso in .hero h1 und
     .legal__title. */
  overflow-wrap: break-word;
  hyphens: auto;
}

.card__text {
  margin-top: 8px;
  color: var(--muted);
  /* a measure, so a two-line card in a wide column keeps its text as a block
     rather than as one thin ribbon */
  max-width: 46ch;
}

/* Two-line cards in a wide column came out at 188px against 259px elsewhere,
   which read as half-empty next to the section rhythm. A floor evens the
   grids out without padding the copy back up. */
@media (min-width: 900px) {
  .grid--2 .card,
  .grid--3 .card {
    min-height: 210px;
  }
}

.grid {
  display: grid;
  gap: 18px;
}

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

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

  .grid--4 {
    grid-template-columns: repeat(4, 1fr);
  }

  .grid {
    gap: 22px;
  }
}

/* --- Hervorgehobene Textbloecke --------------------------------------------
   Zwei Formen fuer zwei Aufgaben. Vorher gab es eine (.note) fuer beide, mit
   linker Balkenkante und angeschnittener Rundung — die klassische
   Achtung-Optik. Das war fuer erklaerenden Text falsch (dort wird nichts
   eingeschraenkt) und fiel als einziger Baustein aus dem Kartenmuster des
   Projekts: .card, .mod, .plan, .metric, .quote und .provider sind allseitig
   gerundet und tragen --shadow-card.

   .callout   fasst zusammen, erklaert, ordnet ein. Eine Karte wie die
              anderen, nur getoent statt weiss — sie hebt sich ab, ohne zu
              warnen.
   .hint      grenzt ab, schraenkt ein, weist auf etwas hin. Behaelt die
              abgesetzte Wirkung, traegt sie aber ueber ein Symbol statt ueber
              eine Kante.
   ========================================================================== */

.callout {
  padding: 22px 26px;
  border: 1px solid var(--blue-line);
  border-radius: var(--radius-card);
  background: var(--tint);
  box-shadow: var(--shadow-card);
}

/* Die Ueberschrift der Karte. Gesperrt und in Versalien wie .kicker, weil sie
   dieselbe Aufgabe hat: benennen, was folgt, ohne eine Ebene in der
   Gliederung zu beanspruchen. */
.callout__label {
  display: block;
  margin-bottom: 8px;
  color: var(--blue-strong);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.callout__text {
  margin: 0;
  color: var(--body);
}

/* --- Hinweis ---------------------------------------------------------------
   Fuer Abgrenzungen und rechtliche Vorbehalte. Das Symbol traegt die
   Absetzung: es sagt "hier steht eine Einschraenkung", und zwar an einer
   Stelle, an der man es liest, bevor man den Satz beginnt. */
.hint {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px;
  align-items: start;
  padding: 18px 22px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  background: var(--tint-soft);
}

.hint__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  /* Auf die erste Textzeile ausgerichtet, nicht auf die Oberkante: das Symbol
     soll neben dem Satz stehen, nicht darueber. */
  margin-top: 1px;
  color: var(--blue);
}

.hint__icon svg {
  width: 100%;
  height: 100%;
}

.hint__text {
  margin: 0;
  color: var(--body);
  font-size: 0.94rem;
}

@media (max-width: 599px) {
  .callout {
    padding: 18px 18px;
  }

  .hint {
    padding: 16px 16px;
  }
}

/* Definition rows — label left, value right, hairline below. */
.definitions {
  border-top: 1px solid var(--rule);
}

.definitions__row {
  display: grid;
  gap: 2px 24px;
  padding-block: 15px;
  border-bottom: 1px solid var(--rule);
}

.definitions__label {
  font-weight: 700;
  color: var(--ink);
}

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

@media (min-width: 640px) {
  .definitions__row {
    grid-template-columns: 190px 1fr;
  }
}

/* Check list — clean itemised list with a blue tick. Used for benefits and
   "included in every price". */
/* Feature list inside module blocks. */
.dotted {
  display: grid;
  gap: 10px;
}

.dotted > li {
  position: relative;
  padding-left: 18px;
  color: var(--muted);
}

.dotted > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  background: var(--blue);
  border-radius: 50%;
}

.small {
  font-size: 0.86rem;
  color: var(--muted);
}

/* Fine print under a grid: running text needs a reading measure even when the
   cards above use the full column. Centred so the paragraph does not break out
   to the left under a centred head and grid. */
.small--prose {
  max-width: 72ch;
  margin-inline: auto;
}

/* A single short line of fine print under centred content: the block alone is
   not enough, the line itself has to sit in the middle too. */
.small--center {
  margin-inline: auto;
  text-align: center;
}

/* Legal fine print folded away: complete for whoever looks for it, but not a
   screen of scrolling in the middle of the sales flow. Closed by default and
   readable without JavaScript. */
.legal-note {
  /* full width of the shell, like the grid above it: at 72ch centred the rule
     started and ended inside the cards and read as a stray line */
  border-top: 1px solid var(--rule);
}

.legal-note__summary {
  position: relative;
  /* 44px minimum target, and room for the marker on the right */
  padding: 13px 32px 13px 0;
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}

.legal-note__summary::-webkit-details-marker {
  display: none;
}

/* chevron drawn in CSS, matching the FAQ control */
.legal-note__summary::after {
  content: "";
  position: absolute;
  right: 6px;
  top: 20px;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--blue);
  border-bottom: 2px solid var(--blue);
  transform: rotate(45deg);
  transition: transform var(--dur-settle) var(--ease-spring);
}

.legal-note[open] .legal-note__summary::after {
  transform: rotate(-135deg);
  top: 24px;
}

.legal-note__text {
  padding-bottom: 16px;
  color: var(--muted);
  font-size: 0.86rem;
}

/* Spacing utilities — replace the per-element inline styles so every gap on the
   page comes from the same scale. */
.mt-lg {
  margin-top: 28px;
}

.mt-xl {
  margin-top: 32px;
}

/* In einer Rasterzelle: das Raster setzt die Abstaende, deshalb kein eigener
   nach oben — und gleich hoch wie die Nachbarspalte. */
.callout--flush {
  margin-top: 0;
  height: 100%;
}

/* Placeholder — unfilled information only. Marked by colour AND brackets so the
   meaning survives without colour perception. */
.placeholder {
  color: var(--placeholder);
  font-weight: 700;
}

/* ==========================================================================
   6. Buttons — app Button component: 8 px radius, semibold, opacity hover
   ========================================================================== */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 46px;
  padding: 12px 22px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  font-family: inherit;
  font-size: 0.98rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out),
    box-shadow var(--dur-quick) var(--ease-out),
    transform var(--dur-quick) var(--ease-out);
}

.button--primary {
  position: relative;
  /* Der Hauptknopf traegt den Markenverlauf statt einer Flaeche. Alle drei
     Stopps sind gegen die Schrift geprueft: im Hellmodus haelt der hellste
     Punkt (#188275) mit Weiss 4.68:1 — der frueher gesetzte #1a8a7a kam auf
     4.23 und lag damit unter der Schwelle, obwohl der Kommentar 4.62
     behauptete.

     Die Schriftfarbe steht hier fest auf Weiss, nicht auf --on-blue. Im
     Dunkelmodus wird --on-blue dunkel (#0b1413), weil die Markenfarbe dort
     hell ist (#5fbfc4) — der Verlauf wird aber gleichzeitig ABGEDUNKELT
     (#0f4b4f…). Dunkler Text auf dunklem Verlauf ergab 1.9:1. Mit Weiss sind
     es dort 6.89 bis 9.81. */
  background: var(--gradient-brand);
  color: #ffffff;
  box-shadow: 0 1px 2px rgba(20, 33, 31, 0.06);
  overflow: hidden;
}

/* Ein Lichtstreifen, der beim Ueberfahren einmal ueber den Knopf laeuft.
   Rein dekorativ, unter dem Text (z-index 0 gegen den Inhalt im Fluss) und
   in reduced-motion abgeschaltet. */
.button--primary::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 45%;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(255, 255, 255, 0.28) 50%,
    transparent 100%
  );
  transform: translateX(0);
  opacity: 0;
  transition:
    transform var(--dur-enter) var(--ease-out),
    opacity var(--dur-quick) var(--ease-out);
}

.button--primary:hover::after {
  transform: translateX(360%);
  opacity: 1;
}

/* Im dunklen Theme ist die Knopffarbe das helle Petrol — ein weisser
   Streifen darauf wuerde ausbleichen statt zu glaenzen. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .button--primary::after {
    background: linear-gradient(
      100deg,
      transparent 0%,
      rgba(11, 20, 19, 0.18) 50%,
      transparent 100%
    );
  }
}

:root[data-theme="dark"] .button--primary::after {
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(11, 20, 19, 0.18) 50%,
    transparent 100%
  );
}


/* Der Hauptknopf hebt sich beim Überfahren leicht an und bekommt einen
   farbigen Schein statt einer gesenkten Deckkraft — halbtransparent liest
   sich wie deaktiviert, angehoben wie ansprechbar. */
.button--primary:hover {
  /* Kein Verlauf im Hover: der einfarbige, dunklere Ton ist der klarere
     Zustandswechsel — der Verlauf hat seine Aufgabe im Ruhezustand. */
  background: var(--blue-strong);
  transform: translateY(-1px);
  box-shadow: 0 6px 20px var(--glow-brand), 0 1px 2px rgba(20, 33, 31, 0.08);
}

/* Beim Drücken zurück auf die Grundlinie: die Bewegung bestätigt den Klick. */
.button--primary:active {
  transform: translateY(0);
  transition-duration: var(--dur-instant);
}

.button--secondary {
  background: var(--surface);
  border-color: var(--rule);
  color: var(--ink);
}

.button--secondary:hover {
  background: var(--tint-soft);
  border-color: var(--blue-line);
}

.button--lg {
  min-height: 52px;
  padding: 14px 28px;
  font-size: 1.02rem;
}

/* ==========================================================================
   7. Header
   ========================================================================== */

.header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--header-bg);
  /* Falls back to the solid colour where backdrop-filter is unsupported. */
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--rule);
  transition:
    box-shadow var(--dur-settle) var(--ease-out),
    border-color var(--dur-settle) var(--ease-out);
}

/* Am Seitenanfang sitzt der Header flach auf der Fläche; sobald Inhalt
   darunter wegläuft, bekommt er eine Kante. main.js setzt die Klasse — ohne
   JavaScript bleibt die ruhige Grundform, die für sich stimmig ist. */
.header.is-stuck {
  border-bottom-color: transparent;
  box-shadow: 0 1px 0 var(--rule), 0 6px 20px rgba(20, 33, 31, 0.06);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .header.is-stuck {
    box-shadow: 0 1px 0 var(--rule), 0 6px 24px rgba(0, 0, 0, 0.5);
  }
}

:root[data-theme="dark"] .header.is-stuck {
  box-shadow: 0 1px 0 var(--rule), 0 6px 24px rgba(0, 0, 0, 0.5);
}


.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: var(--header-height);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  font-size: 1.16rem;
  font-weight: 700;
  /* Nur leicht gesperrt: "KoHub" trägt seine Kontur in der Binnenmajuskel,
     weiter Sperrsatz (der alte Wert war 0.14em für Versalien) würde das Wort
     in Einzelbuchstaben zerlegen. */
  letter-spacing: -0.005em;
  text-decoration: none;
}

.wordmark__mark {
  width: 28px;
  height: 28px;
  border-radius: 8px;
}

.header__nav {
  display: flex;
  align-items: center;
  /* Der Grundabstand gilt zwischen den Navigationslinks. Die Spruenge zu den
     Einstellungen und zum Aufruf-Knopf sind groesser und stehen an
     .header__settings — ein einziger Wert fuer alles liess die sieben
     Elemente gleich wichtig aussehen. */
  gap: 28px;
}

/* --- Die Einstellungen als Gruppe ------------------------------------------
   Darstellung und Sprache gehoeren zusammen: beides aendert, WIE die Seite
   erscheint, nicht wohin man geht. 8 px zueinander binden sie zu einer
   Einheit; die groesseren Abstaende nach aussen trennen sie von den Seiten
   davor und von der Handlung danach.

   40 px zur Navigation, 32 px zum Knopf: der linke Sprung ist der groessere,
   weil er die Sorte wechselt (Seiten -> Einstellungen), der rechte nur die
   Wichtigkeit (Einstellung -> Handlung). */
.header__settings {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 12px;
  margin-right: 4px;
}

.header__link {
  color: var(--muted);
  font-size: 0.95rem;
  font-weight: 700;
  text-decoration: none;
  transition: color var(--dur-quick) var(--ease-out);
}

.header__link:hover {
  color: var(--ink);
}

/* --- Hell/Dunkel ------------------------------------------------------------
   Ein Knopf mit zwei Symbolen: sichtbar ist immer das Ziel, nicht der
   Zustand. Im Hellen zeigt er den Mond ("hierhin"), im Dunklen die Sonne.
   Ein Symbol fuer den aktuellen Zustand waere zweideutig — man sieht den
   Zustand schon am Bildschirm.

   Voreinstellung bleibt die Systemvorgabe (prefers-color-scheme). Der Knopf
   setzt data-theme am <html> und widerspricht ihr damit ausdruecklich; main.js
   merkt sich die Wahl. Ohne JavaScript steht der Knopf auf hidden — ein Knopf
   ohne Wirkung ist schlechter als keiner.
   ========================================================================== */

.theme-switch {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Genau so hoch wie der Sprachschalter daneben: 3 px Polsterung, 30 px
     Zeilenhoehe, 2x1 px Kante. Vorher standen hier feste 40 px und der Knopf
     war damit 2 px hoeher als sein Nachbar — bei 8 px Abstand sichtbar.
     Abgeleitet statt gesetzt, damit die beiden zusammenbleiben, wenn sich der
     Sprachschalter aendert. */
  width: 38px;
  height: calc(30px + 6px + 2px);
  flex: 0 0 auto;
  /* 3 px wie die Polsterung des Sprachschalters: dadurch steht das Icon-Feld
     darin genauso hoch wie dessen Pille. */
  padding: 3px;
  border: 1px solid var(--rule);
  /* Dieselbe Rundung wie der Sprachschalter daneben — die beiden stehen als
     Paar in der Kopfzeile. */
  border-radius: var(--radius-control);
  /* Eigene Flaeche statt transparent: --rule ist im Hellmodus eine
     dekorative Kante (1.32 gegen Weiss), dort traegt der Schatten die
     Trennung. Ohne Flaeche stuetzte sich der Knopf allein auf diese Kante und
     war als Knopf kaum erkennbar. Der Sprachschalter loest es genauso. */
  background: var(--surface);
  color: var(--body);
  cursor: pointer;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out),
    color var(--dur-quick) var(--ease-out);
}

/* Das Feld hinter dem Symbol — das Gegenstueck zur getoenten Pille des
   Sprachschalters. Hoehe, Rundung und Toenung sind von
   .lang-switch__option uebernommen, damit die beiden Schalter innen
   dieselbe Form zeigen und zusammenbleiben, wenn sich einer aendert. */
.theme-switch__field {
  display: flex;
  align-items: center;
  justify-content: center;
  /* stretch statt 100 %: in der Schublade ist der Knopf inhaltsbreit
     (width: auto), ein Prozentwert davon ergaebe 0 px. Quer zur Hauptachse
     gestreckt bekommt das Feld in der Kopfzeile trotzdem die volle Breite.

     flex: 0 0 auto ist noetig, nicht kosmetisch: mit `1 1 auto` nahm das Feld
     in der Schublade den freien Platz der Zeile und schrumpfte das SVG darin
     auf 0 px — ein SVG mit viewBox bringt keine Mindestbreite mit. Gemessen
     18 px mit `0 0 auto`, 0 px mit `1 1 auto`. */
  align-self: stretch;
  flex: 0 0 auto;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    color var(--dur-quick) var(--ease-out);
}

/* Die Gestalt des Feldes gilt ueberall dort, wo der Knopf als Kaestchen neben
   dem Sprachschalter steht — in der Kopfzeile wie in der Schublade. Innen
   entspricht er dessen Pille, damit die beiden als Paar gelesen werden. */
.header__settings .theme-switch__field,
.nav-drawer__settings .theme-switch__field {
  /* Hier ist die Breite des Knopfes bestimmt (38 px minus 2x3 px Polsterung),
     ein Prozentwert also tragfaehig — in der Schublade waere er 0. */
  width: 100%;
  min-height: 30px;
  border-radius: calc(var(--radius-control) - 3px);
  background: var(--blue-wash);
  color: var(--blue);
}

.theme-switch svg {
  width: 18px;
  height: 18px;
  /* Ein SVG ohne width-Attribut hat keine Mindestbreite und wird im
     Flex-Layout bis auf 0 zusammengedrueckt. */
  flex: 0 0 auto;
}

/* Beim Ueberfahren reagiert das Feld, nicht die ganze Flaeche — beim
   Sprachschalter schlaegt ebenfalls nur die Pille um. Die Kante des Knopfes
   folgt weiterhin, damit er als Knopf antwortet. */
.theme-switch:hover {
  border-color: var(--blue);
}

.header__settings .theme-switch:hover .theme-switch__field,
.nav-drawer__settings .theme-switch:hover .theme-switch__field {
  background: var(--tint-soft);
  color: var(--ink);
}

.theme-switch:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* Sichtbar ist das Ziel: im Hellen der Mond, im Dunklen die Sonne. Gesteuert
   ueber die Anzeige, nicht ueber die Deckkraft — ein unsichtbares, aber
   vorhandenes Symbol wuerde die Breite des Knopfes mitbestimmen.

   Grundzustand ist hell: Mond sichtbar, Sonne verborgen. */
.theme-switch__sun {
  display: none;
}

.theme-switch__moon {
  display: block;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-switch__sun {
    display: block;
  }

  :root:not([data-theme="light"]) .theme-switch__moon {
    display: none;
  }
}

:root[data-theme="dark"] .theme-switch__sun {
  display: block;
}

:root[data-theme="dark"] .theme-switch__moon {
  display: none;
}

/* Der Knopf ist erst nach dem Einsetzen durch main.js sichtbar. */
.theme-switch[hidden] {
  display: none;
}

/* Der Aufruf-Knopf braucht 158 px, damit "30 Tage testen" in einer Zeile
   bleibt; sonst wird er zweizeilig und die Kopfzeile waechst von 65 auf
   77 px. Gemessen:

     ohne Themenschalter   bricht er unter 820 px  (bestehende Enge)
     mit  Themenschalter   bricht er unter 880 px  (seine 46 px kosten das)

   Unter 880 px weichen deshalb Links und Einstellungen gemeinsam in die
   Schublade; der Knopf behaelt die Zeile fuer sich. Siehe den Block bei
   879 px weiter unten. */
/* --- Die Einstellungen in der Schublade ------------------------------------
   Dieselbe Gruppe wie in der Kopfzeile, nur eine Zeile tiefer: Darstellung und
   Sprache nebeneinander, 8 px zueinander. Als Textzeile stand der
   Themenschalter vorher in der Reihe der Seitenlinks und las sich wie ein
   siebter Menuepunkt — er wechselt aber keine Seite, er wechselt die
   Darstellung. Neben dem Sprachschalter ist das sofort zu sehen.

   Eigene Zeile mit Abstand nach oben, wie ihn der Sprachschalter schon
   hatte; justify-self, weil die Schublade ein Raster mit gestreckten Zellen
   ist und die Gruppe sonst ueber die volle Breite liefe. */
.nav-drawer__settings {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  justify-self: start;
}

/* Die Hoehe der Schublade folgt dem Sprachschalter daneben (40 px Pille plus
   3 px Polsterung und 1 px Kante beiderseits) — dort sind die Zeilen groesser
   als in der Kopfzeile, weil sie mit dem Finger getroffen werden. Breite
   entsprechend mit, damit das Kaestchen quadratisch bleibt. */
.nav-drawer__settings .theme-switch {
  width: calc(40px + 6px + 2px);
  height: calc(40px + 6px + 2px);
}

.nav-drawer__settings .theme-switch__field {
  min-height: 40px;
}

/* Ueber 880 px traegt die Kopfzeile die Einstellungen wieder selbst, und der
   Menue-Knopf ist fort — die Schublade ist dort ohnehin nicht erreichbar.
   Die Regel bleibt trotzdem stehen: sie haelt die zweite Ausfertigung aus
   der Druckausgabe und aus Suchergebnissen im Seitenquelltext heraus. */
@media (min-width: 880px) {
  .nav-drawer__settings {
    display: none;
  }
}

/* --- Language switch -------------------------------------------------------
   Two plain links, not a dropdown: with only two languages a select would add
   a widget where an anchor does the job — and it keeps working without
   JavaScript. The current language is marked with aria-current="true" and
   rendered as static text, so only the other language is clickable. */

.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-control);
  background: var(--surface);
}

.lang-switch__option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  /* Below the 44 px touch minimum on its own, but the pair sits inside a
     44 px-tall header row and the drawer carries full-size rows as well. */
  min-height: 30px;
  padding: 0 8px;
  border-radius: calc(var(--radius-control) - 3px);
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition:
    color var(--dur-quick) var(--ease-out),
    background-color var(--dur-quick) var(--ease-out);
}

a.lang-switch__option:hover {
  color: var(--ink);
  background: var(--tint-soft);
}

.lang-switch__option[aria-current="true"] {
  background: var(--blue-wash);
  color: var(--blue);
}

/* Abstand und Ausrichtung traegt jetzt .nav-drawer__settings — der
   Sprachschalter steht dort nicht mehr allein in seiner Zeile. Die Pillen
   bleiben groesser als in der Kopfzeile: hier wird mit dem Finger getroffen. */
.nav-drawer .lang-switch__option {
  min-height: 40px;
  min-width: 48px;
}

/* --- Mobile navigation -----------------------------------------------------
   Built on <details> so it opens without JavaScript and announces its
   expanded state natively. Shown below 768 px, hidden above. */

.nav-toggle {
  display: none;
}

.nav-toggle > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  /* 44 px square: meets the touch-target minimum on its own. */
  width: 44px;
  height: 44px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  list-style: none;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out);
}

.nav-toggle > summary::-webkit-details-marker {
  display: none;
}

.nav-toggle > summary:hover {
  background: var(--tint-soft);
  border-color: var(--blue-line);
}

/* Burger turns into a cross when open — drawn in CSS, no second icon. */
.nav-toggle__icon {
  position: relative;
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
  transition: background-color var(--dur-quick) var(--ease-out);
}

.nav-toggle__icon::before,
.nav-toggle__icon::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
  transition: transform var(--dur-settle) var(--ease-spring);
}

.nav-toggle__icon::before {
  transform: translateY(-6px);
}

.nav-toggle__icon::after {
  transform: translateY(6px);
}

.nav-toggle[open] .nav-toggle__icon {
  /* Middle bar disappears; the outer two cross. */
  background: transparent;
}

.nav-toggle[open] .nav-toggle__icon::before {
  transform: rotate(45deg);
}

.nav-toggle[open] .nav-toggle__icon::after {
  transform: rotate(-45deg);
}

/* Anchored to .header (the full-width bar), not to the nav or the inner row —
   otherwise "left:0; right:0" resolves against a narrow flex item and the
   panel spills past the viewport on small phones. */
.nav-drawer {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--header-height);
  display: grid;
  gap: 4px;
  padding: 12px var(--shell-gutter) 18px;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
  box-shadow: var(--shadow-lift);
}

.nav-drawer a {
  display: flex;
  align-items: center;
  /* Full-width rows, comfortably above the 24 px minimum. */
  min-height: 48px;
  padding: 0 12px;
  border-radius: var(--radius-control);
  color: var(--ink);
  font-weight: 700;
  text-decoration: none;
}

.nav-drawer a:hover {
  background: var(--tint);
  color: var(--blue);
}

.nav-drawer a[aria-current="page"] {
  color: var(--blue);
  background: var(--blue-wash);
}

/* Der Umbruch zur Schublade liegt bei 879 px, nicht bei 767 px. Gemessen mit
   sichtbarem Themenschalter in der Kopfzeile:

     900 px   Kopfzeile 65 px   (passt)
     860 px   Kopfzeile 65 px   (passt)
     840 px   Kopfzeile 77 px   — der Aufruf-Knopf bricht zweizeilig
     768 px   Kopfzeile 102 px  — zwei Umbrueche

   Vorher wich der Themenschalter unter 880 px aus der Kopfzeile, der
   Menue-Knopf kam aber erst unter 768 px. Dazwischen war er ueberhaupt nicht
   erreichbar. Ein eigener Zwischenschritt half nicht: auch der Menue-Knopf
   allein sprengt die Zeile, solange die sechs Navigationslinks daneben
   stehen (gemessen 77 px bei 840 px). Also wechselt der ganze Kopf an einer
   Stelle — Links, Einstellungen und Menue-Knopf gemeinsam. */
@media (max-width: 879px) {
  /* Header links move into the drawer; the trial CTA stays visible. */
  .header__link {
    display: none;
  }

  /* Same for the language switch — the drawer carries its own copy. */
  .header__settings > .lang-switch {
    display: none;
  }

  /* Die Gruppe traegt sonst nur noch ihre Abstaende — die wuerden als Luecke
     sichtbar. Deckt zugleich den Themenschalter ab, der dafuer frueher eine
     eigene Regel bei 879 px brauchte. */
  .header__settings {
    display: none;
  }

  .nav-toggle {
    display: block;
  }

  .header__inner {
    gap: 10px;
  }

  /* The <details> must not become the drawer's containing block; the sticky
     .header already is one, and it spans the full viewport width. */
  .nav-toggle {
    position: static;
  }

  /* Wordmark keeps its full width; the nav side gives way instead. */
  .wordmark {
    font-size: 1.06rem;
    flex: 0 0 auto;
  }

  .header__nav {
    flex: 0 1 auto;
    gap: 8px;
  }

  .header .button {
    min-height: 44px;
    padding: 10px 14px;
    font-size: 0.88rem;
    /* Two-line buttons look broken in a 64 px header row. */
    white-space: nowrap;
  }
}

/* The CTA carries two spellings. The short one only appears on the narrowest
   phones; the button's aria-label states the full offer either way, so the
   swap never changes what a screen reader announces. Declared before the
   media query so the query wins. */
.label-short {
  display: none;
}

/* Below ~430 px "30 Tage testen" plus the wordmark and toggle no longer fit
   on one line. */
@media (max-width: 430px) {
  .header .button--primary {
    padding: 10px 13px;
  }

  .header .label-long {
    display: none;
  }

  .header .label-short {
    display: inline;
  }
}

/* At 320 px the letter-spaced wordmark plus the controls exceed the row. */
@media (max-width: 360px) {
  .wordmark {
    font-size: 1rem;
    gap: 8px;
  }

  .wordmark__mark {
    width: 24px;
    height: 24px;
  }

  /* On the legal pages the header carries a "Zur Startseite" button next to
     the toggle, which no longer fits. The wordmark already links home and the
     drawer lists every page, so nothing becomes unreachable. */
  .header .button--secondary {
    display: none;
  }
}

/* ==========================================================================
   8. Hero — light, like the app's own surfaces
   ========================================================================== */

.hero {
  position: relative;
  padding-block: 56px 64px;
  /* Drei Schichten statt eines linearen Verlaufs:
       1. ein Markenschein oben rechts, wo die Vorschau steht — er laesst das
          Telefon aus der Flaeche heraustreten, statt darauf zu liegen
       2. ein warmer Akzentschein unten links als Gegengewicht, damit der
          Verlauf nicht in eine Ecke kippt
       3. der eigentliche Grundverlauf von getoent nach Flaeche
     Radiale Verlaeufe zuerst: in `background` gewinnt die erste Schicht. */
  background:
    radial-gradient(120% 90% at 82% 8%, var(--glow-brand) 0%, transparent 58%),
    radial-gradient(90% 70% at 6% 92%, var(--glow-accent) 0%, transparent 60%),
    linear-gradient(180deg, var(--tint) 0%, var(--surface) 78%);
  overflow: hidden;
}

/* Feines Raster ueber dem Verlauf — 44 px, bei 3 % Deckkraft. Es ist nicht
   als Muster gedacht, sondern gibt der Flaeche Textur, damit der Verlauf
   nicht als Farbfeld liest. Auf ::before, damit der Inhalt darueber bleibt. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, currentColor 1px, transparent 1px),
    linear-gradient(to bottom, currentColor 1px, transparent 1px);
  background-size: 44px 44px;
  color: var(--blue);
  opacity: 0.03;
  /* Nach unten auslaufen, sonst schneidet das Raster an der Sektionskante
     hart ab und wird als Kasten sichtbar. */
  mask-image: linear-gradient(180deg, #000 0%, transparent 72%);
}

.hero__grid {
  position: relative;
  display: grid;
  gap: 44px;
  align-items: center;
}

/* Rasterfelder bekommen von sich aus die Mindestbreite ihres Inhalts. Ohne
   diese Zeile drückt die Dashboard-Vorschau die Spalte auf, und der Hero-Text
   läuft auf schmalen Geräten über den Rand — sichtbar wird das nur als
   Beschnitt, weil body overflow-x:hidden trägt. */
.hero__grid > * {
  min-width: 0;
}

.hero h1 {
  font-size: clamp(2.1rem, 5vw, 3.4rem);
  letter-spacing: -0.025em;
  /* "Kommunikationsplattform" hat 22 Zeichen — auf einem 390-px-Schirm passt
     das selbst beim Minimum der Klammer nicht in eine Zeile. Dort darf es
     brechen, statt aus der Seite zu laufen. */
  overflow-wrap: break-word;
  hyphens: auto;
}

/* Im zweispaltigen Hero teilt sich die Überschrift den Platz mit der
   Vorschau. "Kommunikationsplattform" misst bei 3.4rem rund 650 px und
   passt damit nicht in die ~570 px breite Textspalte — es bräche mitten im
   Wort. Die Obergrenze der Klammer sinkt deshalb hier so weit, dass das
   längste Wort ganz in seine Zeile geht. */
@media (min-width: 900px) {
  .hero h1 {
    font-size: clamp(2.1rem, 3.6vw, 2.75rem);
    overflow-wrap: normal;
    hyphens: manual;
  }
}

/* Below the first breakpoint the headline scales with the viewport instead of
   sitting at the clamp minimum, which keeps the long compound word intact. */
@media (max-width: 480px) {
  .hero h1 {
    font-size: clamp(1.75rem, 8.2vw, 2.1rem);
  }
}

.hero__lead {
  margin-top: 22px;
  max-width: 56ch;
  font-size: 1.12rem;
  color: var(--muted);
}

.hero__lead + .hero__lead {
  margin-top: 14px;
}

.hero .actions {
  margin-top: 34px;
}

/* Narrow phones: keep the pair on one line instead of stacking. The buttons
   give up horizontal padding, not their 52px target height. */
@media (max-width: 479px) {
  .hero .actions {
    flex-wrap: nowrap;
  }

  .hero .actions .button--lg {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: 12px;
    font-size: 0.95rem;
    white-space: nowrap;
  }
}

/* Der Einwand "kostet mich das am Ende doch etwas?" entscheidet ueber den
   Klick. Er gehoert direkt unter den Knopf, nicht in die FAQ. */
.hero__reassure {
  margin-top: 14px;
  font-size: 0.92rem;
  color: var(--faint);
}

.hero__figure {
  position: relative;
  margin: 0;
}

/* Der Schein hinter dem Telefon. Er sitzt auf der Figur, nicht auf der
   Sektion, damit er dem Geraet folgt, wenn das Raster umbricht. */
.hero__figure::before {
  content: "";
  position: absolute;
  /* Bewusst grosszuegig ueber das Geraet hinaus: ein Schein, der an der
     Kante endet, liest als Rahmen statt als Licht. */
  inset: -14% -8%;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(closest-side, var(--glow-brand), transparent 72%);
  filter: blur(18px);
}

.hero__figure > * {
  position: relative;
  z-index: 1;
}


@media (min-width: 900px) {
  .hero {
    padding-block: 84px 104px;
  }

  .hero__grid {
    /* Die Textspalte braucht genug Breite, damit "Kommunikationsplattform"
       ungetrennt in eine Zeile passt — sonst bricht das Wort mitten durch.
       Die Vorschau rechts kommt mit weniger aus als der frühere Screenshot,
       weil sie kein festes Seitenverhältnis mitbringt. */
    grid-template-columns: minmax(0, 1.18fr) minmax(0, 1fr);
    gap: 48px;
  }

  /* Kein Anschnitt mehr: der frühere Screenshot durfte über den Rand laufen,
     weil rechts nur Bildfläche abgeschnitten wurde. Die Vorschau trägt dort
     echte Inhalte (die Neuigkeiten-Spalte) — die dürfen nicht wegfallen. */
  .hero__figure {
    min-width: 0;
  }
}


/* Trust row under the hero. */
/* Die vier Punkte tragen ausgeschriebene Aussagen statt Schlagworte. Als
   freier Umbruch bliebe je nach Breite ein einzelnes Element allein in der
   letzten Zeile stehen; das Raster haelt sie paarweise zusammen. */
.trust {
  display: grid;
  /* auto, not 1fr: the column takes the width of its longest entry so the
     block can be centred. With 1fr it filled the row and the items sat left
     against a wide empty right side. */
  grid-template-columns: auto;
  justify-content: center;
  /* the items keep their own left edge inside the centred block, so the icons
     stay in one line instead of stepping with the text length */
  justify-items: start;
  gap: 14px 32px;
  margin-top: 40px;
  padding-block: 26px 0;
  border-top: 1px solid var(--rule);
}

@media (min-width: 620px) {
  .trust {
    grid-template-columns: auto auto;
  }
}

@media (min-width: 1180px) {
  .trust {
    grid-template-columns: repeat(4, auto);
    justify-content: space-between;
  }
}

.trust__item {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--muted);
  font-size: 0.92rem;
  font-weight: 700;
}

.trust__item svg {
  color: var(--blue);
}


figcaption {
  margin-top: 12px;
  font-size: 0.86rem;
  color: var(--muted);
}

/* ==========================================================================
   10. Module — zwei Ebenen als Raster
   ==========================================================================

   Ersetzt die frueheren abwechselnden Zweispalter mit je einem Telefon. Sechs
   Geraete auf einer Seite zeigten sechsmal dieselbe Huelle und machten die
   Seite lang, ohne sie vollstaendiger zu machen; die App selbst ist jetzt im
   Hero der Startseite bedienbar.

   Ebene 1 (.mod)       die sechs Module des Alltags, mit Funktionsliste und
                        Vorteil — hier wird "reicht das fuer uns?" entschieden
   Ebene 2 (.mod-list)  die sechs uebrigen, ein Satz genuegt
   ========================================================================== */

/* Der Kopf der Seite darf schmaler laufen als die 62ch der Vorgabe: eine
   zentrierte Einleitung ueber einem dreispaltigen Raster liest sich besser,
   wenn sie nicht ueber die ganze Rasterbreite geht. */
.section--modules .section__intro {
  max-width: 58ch;
}

/* Die Modulsektion endet mit dem Panel "Ebenfalls enthalten", das selbst eine
   Flaeche mit Polsterung ist. Der volle --section-gap darunter plus dessen
   Innenabstand ergaeben eine Luecke von rund 145 px vor dem Abschlussband —
   sichtbar als Loch. Der Wert unten laesst das Panel und das Band als
   aufeinanderfolgende Bloecke lesen. */
.section--modules {
  padding-bottom: 56px;
}

@media (min-width: 900px) {
  .section--modules {
    padding-bottom: 72px;
  }
}

.modules {
  /* Zahlenspalte der Karten. Als Variable, weil Kopf und Fussnote der Karte
     an derselben Kante ausgerichtet bleiben muessen. */
  --mod-pad: 26px;
}

.modules__grid {
  display: grid;
  gap: 18px;
  margin-top: 44px;
}

@media (min-width: 620px) {
  .modules__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1000px) {
  .modules__grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 22px;
  }
}


/* --- Modulkarte -------------------------------------------------------------
   Erbt bewusst nicht von .card: die Karte hat einen eigenen Fussteil mit
   abgesetzter Flaeche, dafuer braucht sie die Polsterung in den Kindern statt
   auf sich selbst. */
.mod {
  position: relative;
  /* Grid statt Flex, damit die Karte ihre vier Abschnitte (Kopf, Titel,
     Vorteil, Funktionen) an das Raster der Zeile weitergeben kann — siehe
     subgrid unten. Mit Flex richtet sich nur die Kartenhoehe aus, nicht die
     Lage der Abschnitte darin.

     Die Funktionsliste steht seit der Umstellung auf Nutzen-zuerst an vierter
     Stelle und nimmt den Ueberschuss auf: 1fr gehoert daher auf die letzte
     Zeile, sonst dehnt sich der Vorteil und die Listen stehen ungleich hoch. */
  display: grid;
  grid-template-rows: auto auto auto 1fr;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  isolation: isolate;
  transition:
    transform var(--dur-settle) var(--ease-out),
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out);
}

/* Lichtschimmer von oben beim Ueberfahren — dieselbe Sprache wie .card. */
.mod::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(120% 70% at 50% 0%, var(--glow-soft), transparent 70%);
  opacity: 0;
  transition: opacity var(--dur-settle) var(--ease-out);
}

.mod:hover {
  transform: translateY(calc(var(--lift) * -1));
  border-color: var(--blue-line);
  box-shadow: var(--shadow-lift);
}

.mod:hover::before {
  opacity: 1;
}

/* Die Vorteilsflaechen einer Rasterzeile sollen auf einer Linie beginnen. Ohne
   das startet sie in jeder Karte da, wo deren Funktionsliste endet — bei drei
   gegen vier Zeilen Liste sind das rund 45 px Versatz, und die Reihe liest
   sich als drei einzelne Karten statt als eine Zeile.

   subgrid gibt jeder Karte die Zeilen des Rasters, statt dass jede Karte ihre
   vier Abschnitte selbst bemisst: die Hoehen ergeben sich aus der jeweils
   hoechsten Karte der Reihe.

   Die vier Zeilen muss das Raster explizit fuehren. grid-auto-rows: auto
   verteilte sie gleichmaessig und machte alle vier gleich hoch — deshalb
   grid-template-rows mit 1fr auf der Funktionszeile: nur sie dehnt sich, die
   drei anderen bleiben inhaltshoch.

   Diese Regel MUSS hinter der .mod-Basisregel stehen, sonst ueberschreibt
   deren grid-template-rows das subgrid wieder.

   @supports, weil Safari subgrid erst ab 16 kennt. Ohne Unterstuetzung greift
   die Grundform darueber: Karten gleich hoch, Vorteil je Karte auf eigener
   Hoehe. Ein Schoenheitsfehler, kein Defekt. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 620px) {
    .modules__grid {
      grid-auto-flow: row;
      /* Vier Zeilen je Kartenreihe: Kopf, Ueberschrift, Funktionen (dehnbar),
         Vorteil. auto-flow legt weitere Reihen nach demselben Muster an —
         deshalb hier grid-auto-rows statt einer festen Zahl von Reihen: bei
         zwei Spalten sind es drei Kartenreihen, bei drei Spalten zwei.

         grid-auto-rows wiederholt seine Werteliste zyklisch, die vier Werte
         gelten also fuer jede Kartenreihe erneut. */
      grid-auto-rows: auto auto 1fr auto;
    }

    .mod {
      grid-row: span 4;
      grid-template-rows: subgrid;
    }
  }
}

.mod__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: var(--mod-pad) var(--mod-pad) 0;
}

.mod:hover .mod__chip {
  transform: translateY(-1px) scale(1.06);
  border-color: var(--blue-line);
  box-shadow: 0 4px 12px var(--glow-soft);
}

/* Die Nummer gibt dem Raster eine Leserichtung. Sie ist Zierde, nicht
   Information — deshalb aria-hidden im Markup und hier bewusst schwach:
   staerker gesetzt wuerde sie mit dem Modulnamen um Aufmerksamkeit ringen. */
.mod__num {
  flex: 0 0 auto;
  color: var(--blue);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  opacity: 0.5;
}

/* Traeger fuer Name und Claim — eine Rasterzeile der Karte. */
.mod__heading {
  padding-top: 18px;
}

.mod__title {
  padding-inline: var(--mod-pad);
  font-size: 1.2rem;
}

/* Der Claim steht direkt unter dem Namen und traegt keine eigene Zeile Abstand:
   Name und Claim sind zusammen die Ueberschrift, nur zweistufig gesetzt. */
/* The claim now states the gain, not the category, so it carries more weight
   than the module name above it. */
.mod__claim {
  margin-top: 5px;
  padding-inline: var(--mod-pad);
  color: var(--blue);
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.32;
  letter-spacing: -0.01em;
}

/* Evidence for the claim, not the claim itself — smaller and quieter than the
   benefit above. It takes the card's spare height (1fr in grid-template-rows)
   and sits at the foot, separated by a hairline. */
.mod__features {
  display: grid;
  gap: 8px;
  margin-top: 16px;
  padding: 14px var(--mod-pad) var(--mod-pad);
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: 0.88rem;
  align-content: start;
}

.mod__features > li {
  display: grid;
  grid-template-columns: 15px 1fr;
  gap: 9px;
  align-items: start;
  color: var(--muted);
  font-size: 0.94rem;
  line-height: 1.5;
}

.mod__features svg {
  margin-top: 4px;
  color: var(--blue);
}

/* --- Vorteil ----------------------------------------------------------------
   Abgesetzte Flaeche am Kartenfuss statt eines weiteren Absatzes: der Vorteil
   ist die Antwort auf die Liste darueber, nicht ihre Fortsetzung. */
/* Sits directly under the claim and spells it out. No longer a tinted box at
   the foot of the card: it is the argument now, not an appendix to it, and a
   box below the feature list is exactly what a skimming reader skips. */
.mod__benefit {
  margin-top: 10px;
  padding-inline: var(--mod-pad);
  color: var(--body);
  font-size: 0.95rem;
  line-height: 1.55;
}

/* --- Ebene 2: ebenfalls enthalten -------------------------------------------
   Getrennt durch eine Flaeche, nicht durch einen Strich: die sechs sind eine
   andere Art Information, keine Fortsetzung der Liste. */
.modules__more {
  margin-top: 22px;
  padding: 30px 26px;
  background: var(--tint-soft);
  border: 1px solid var(--rule);
  border-radius: var(--radius-panel);
}

@media (min-width: 768px) {
  .modules__more {
    padding: 34px 32px;
  }
}

.modules__more-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 16px;
  margin-bottom: 22px;
}

.modules__more-head .subhead {
  margin: 0;
}

.mod-list {
  display: grid;
  gap: 12px;
}

@media (min-width: 560px) {
  .mod-list {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .mod-list {
    grid-template-columns: repeat(3, 1fr);
    gap: 14px 22px;
  }
}

.mod-list__item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 15px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-chip);
  transition:
    border-color var(--dur-quick) var(--ease-out),
    box-shadow var(--dur-quick) var(--ease-out);
}

.mod-list__item:hover {
  border-color: var(--blue-line);
  box-shadow: var(--shadow-card);
}

.mod-list__name {
  display: block;
  color: var(--ink);
  font-size: 0.96rem;
  font-weight: 700;
}

.mod-list__note {
  display: block;
  color: var(--muted);
  font-size: 0.85rem;
}

/* Trailing link that leads to the matching subpage, or the next step after a
   selling section. */
.section__cta {
  margin-top: 32px;
}

/* For sections whose heading is centred, so the call sits under the middle of
   the text rather than off at the left edge. */
.section__cta--center {
  display: flex;
  justify-content: center;
}

/* ==========================================================================
   11. Preise
   ==========================================================================

   Drei Stufen mit Umschalter fuer die Zahlungsweise, darunter Test und
   Grossverein als eigene Flaechen.

   Gegenueber vorher tragen die Karten selbst einen Knopf. Die Preisseite hatte
   an ihrer Entscheidungsstelle keine Handlungsmoeglichkeit — der einzige CTA
   stand 2500 px weiter unten im Abschlussband.
   ========================================================================== */

/* --- Umschalter Jahr/Monat --------------------------------------------------
   Ohne JavaScript verborgen (siehe .js-Regel): zwei Knoepfe, die nichts tun,
   sind schlechter als kein Umschalter. Der Jahrespreis steht dann, und der
   Monatspreis ist in .plan-extras im Text genannt. */
.plan-toggle {
  display: none;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-bottom: 30px;
}

.js .plan-toggle {
  display: flex;
}

.plan-toggle__option {
  min-height: 40px;
  padding: 9px 20px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--muted);
  font-family: inherit;
  font-size: 0.93rem;
  font-weight: 700;
  cursor: pointer;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out),
    color var(--dur-quick) var(--ease-out);
}

.plan-toggle__option:hover {
  border-color: var(--blue-line);
  color: var(--ink);
}

/* Der gewaehlte Zustand ist gefuellt, nicht nur gefaerbt: bei zwei Knoepfen
   nebeneinander muss auf einen Blick klar sein, welcher gilt. */
.plan-toggle__option[aria-pressed="true"] {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--on-blue);
}

/* Der Hinweis auf die Ersparnis. Er gehoert an den Umschalter, nicht in die
   Karte: dort steht der Prozentwert der jeweiligen Stufe. */
.plan-toggle__hint {
  margin-left: 6px;
  color: var(--blue-strong);
  font-size: 0.88rem;
  font-weight: 700;
}

/* Auf schmalen Geraeten rutscht der Hinweis unter die Knoepfe und braucht die
   ganze Zeile, sonst steht er allein rechts neben einem der beiden. */
@media (max-width: 479px) {
  .plan-toggle__hint {
    flex: 1 0 100%;
    margin-left: 0;
    text-align: center;
  }
}

/* --- Die drei Stufen -------------------------------------------------------- */
.plans {
  display: grid;
  gap: 20px;
  align-items: stretch;
}

@media (min-width: 900px) {
  .plans {
    grid-template-columns: repeat(3, 1fr);
    gap: 24px;
  }
}

.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 28px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-card);
  transition:
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out),
    transform var(--dur-settle) var(--ease-out);
}

.plan:hover {
  transform: translateY(calc(var(--lift) * -1));
  border-color: var(--blue-line);
  box-shadow: var(--shadow-lift);
}

/* Die empfohlene Stufe: Markenkante statt einer zweiten Farbe. Sie behaelt den
   gehobenen Schatten auch im Ruhezustand, damit sie in der Reihe vorne steht. */
.plan--featured {
  border-color: var(--blue);
  box-shadow: var(--shadow-lift);
}

.plan--featured:hover {
  border-color: var(--blue);
}

.plan__badge {
  position: absolute;
  top: -13px;
  left: 28px;
  padding: 5px 12px;
  background: var(--blue);
  border-radius: 999px;
  color: var(--on-blue);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.plan__head {
  display: grid;
  gap: 4px;
}

.plan__name {
  color: var(--ink);
  font-size: 1.1rem;
  font-weight: 700;
}

.plan__members {
  color: var(--muted);
  font-size: 0.92rem;
}

/* --- Der Preis --------------------------------------------------------------
   Beide Zahlungsweisen liegen im Markup; eine traegt [hidden]. Die Flaeche
   dehnt sich (flex: 1), damit der Knopf unten sitzt, auch wenn die Karten
   ungleich viel Text tragen. */
.plan__figure {
  flex: 1 0 auto;
  margin-top: 22px;
  /* Die Plakette der Ersparnis ist 7 px hoeher als die Aufpreiszeile der
     monatlichen Zahlung. Ohne Mindesthoehe springen die Karten beim
     Umschalten, und der Knopf wandert unter dem Zeiger weg. Der Wert ist die
     Hoehe des hoeheren Zustands (Preis + Erklaerung + Plakette). */
  min-height: 132px;
}

.plan__figure[hidden] {
  display: none;
}

.plan__price {
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.plan__amount {
  color: var(--ink);
  font-size: 2.85rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  /* Die drei Betraege stehen in einer Reihe untereinander; Proportionalziffern
     verschieben sie gegeneinander. */
  font-variant-numeric: tabular-nums;
}

.plan__unit {
  color: var(--muted);
  font-size: 0.95rem;
  font-weight: 700;
}

/* Erklaert die grosse Zahl. Ohne diese Zeile ist "15 EUR / Monat" bei
   jaehrlicher Zahlung eine Angabe, die man nachrechnen muss. */
.plan__note {
  margin-top: 8px;
  color: var(--muted);
  font-size: 0.9rem;
}

/* Die Ersparnis der Stufe. Als Plakette, nicht als Textzeile: sie ist ein
   Argument, kein Detail. */
.plan__saving {
  display: inline-flex;
  align-items: center;
  margin-top: 14px;
  padding: 4px 11px;
  background: var(--blue-wash);
  border-radius: 999px;
  color: var(--blue-strong);
  font-size: 0.8rem;
  font-weight: 700;
}

/* Der Aufpreis bei monatlicher Zahlung. Bewusst ohne Plakette und in --muted:
   eine Mehrkostenangabe soll informieren, nicht abschrecken. */
.plan__extra {
  margin-top: 14px;
  color: var(--faint);
  font-size: 0.85rem;
}

.plan__cta {
  margin-top: 24px;
  /* Ueber die ganze Kartenbreite: drei Knoepfe in einer Reihe, alle gleich
     breit — so liest die Reihe als Auswahl und nicht als drei Angebote
     unterschiedlichen Gewichts. */
  width: 100%;
}

/* --- Test und Grossverein ---------------------------------------------------
   Standen vorher als zwei graue Zeilen unter den Karten. "30 Tage kostenlos"
   ist das starkste Argument der Seite und hat jetzt eine eigene Flaeche. */
.plan-extras {
  display: grid;
  gap: 18px;
  margin-top: 26px;
}

@media (min-width: 768px) {
  .plan-extras {
    grid-template-columns: repeat(2, 1fr);
    gap: 22px;
  }
}

.plan-extra {
  display: flex;
  gap: 16px;
  padding: 24px 26px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

/* Der Test traegt die getoente Flaeche und die Markenkante — er ist das
   Angebot, das ohne Entscheidung auskommt. */
.plan-extra--trial {
  background: var(--note-bg);
  border-color: var(--blue-line);
}

.plan-extra__body {
  display: grid;
  gap: 6px;
  /* Rueckt den Text gegen die Oberkante des Icons, statt ihn mittig zu
     setzen: bei zwei Zeilen Text sieht mittig schief aus. */
  align-content: start;
}

.plan-extra__title {
  color: var(--ink);
  font-size: 1.05rem;
}

.plan-extra__text {
  color: var(--muted);
  font-size: 0.94rem;
}

.plan-extra__link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 4px;
  /* 28 px Trefferflaeche — ueber der WCAG-2.2-Schwelle von 24 px. */
  min-height: 28px;
  color: var(--blue);
  font-size: 0.92rem;
  font-weight: 700;
  text-decoration: none;
}

.plan-extra__link:hover {
  text-decoration: underline;
}

/* --- Leistungsumfang in Gruppen ---------------------------------------------
   Ersetzt die frueheren zehn Punkte als zweispaltige Aufzaehlung. Drei Karten
   fuer die drei Fragen, die die Punkte beantworten. */
.incl {
  display: grid;
  gap: 16px;
  /* Kein margin-top: der Abschnittskopf setzt den Abstand. */
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Zwei, drei, dann fuenf Spalten. Zehn Punkte gehen in allen drei Faellen
   ohne Rest auf — eine halb gefuellte letzte Zeile faellt bei gleich hohen
   Karten sofort auf.

   Frueher trug dieses Raster drei Gruppenkarten mit 5, 3 und 2 Punkten. Die
   standen dann 739, 446 und 341 px hoch nebeneinander: 398 px Unterschied,
   bei zwei Spalten sogar 481. Jetzt tragen die Punkte selbst das Raster. */
@media (min-width: 560px) {
  .incl {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 860px) {
  .incl {
    grid-template-columns: repeat(3, 1fr);
    gap: 18px;
  }
}

@media (min-width: 1200px) {
  .incl {
    /* Fuenf Spalten, zwei Zeilen — die zehn Punkte fuellen beide vollstaendig. */
    grid-template-columns: repeat(5, 1fr);
  }
}

/* Gleich hohe Kacheln. Das Raster streckt die Zellen von sich aus (die
   Vorgabe ist `stretch`), die Karte nimmt die Hoehe ueber `height: 100%` an.
   Gemessen entstehen dadurch bis zu 107 px Leerraum in der kuerzesten Karte —
   vertretbar, weil die Unterkanten eine Linie bilden und die Reihe dadurch
   als Reihe liest.

   Bei den frueheren Gruppenkarten waeren es 300 bis 400 px gewesen; dort war
   dasselbe Mittel deshalb falsch. Der Unterschied ist die Streuung: zehn
   aehnlich lange Texte gegen drei Gruppen von 5, 3 und 2 Punkten. */
.incl__item {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  transition:
    box-shadow var(--dur-quick) var(--ease-out),
    transform var(--dur-quick) var(--ease-out);
}

/* Die Karten heben sich beim Ueberfahren wie die uebrigen Karten der Seite —
   siehe .card und .mod. */
.js .incl__item:hover {
  transform: translateY(calc(var(--lift) * -1));
  box-shadow:
    0 1px 2px rgba(20, 33, 31, 0.05),
    0 14px 30px -12px var(--glow-brand);
}

.incl__name {
  margin-bottom: 6px;
  font-size: 0.98rem;
  line-height: 1.35;
}

.incl__text {
  margin: 0;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.5;
}

@media (max-width: 559px) {
  .incl__item {
    padding: 18px;
  }
}

.subhead {
  font-size: 1.1rem;
  margin-bottom: 18px;
}

/* ==========================================================================
   12. People
   ========================================================================== */

.person__photo {
  width: 96px;
  height: 96px;
  margin-bottom: 18px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  object-fit: cover;
}

.person__photo--empty {
  display: grid;
  place-items: center;
  padding: 10px;
  background: var(--tint-soft);
  border-style: dashed;
  border-color: var(--placeholder);
  color: var(--placeholder);
  font-size: 0.68rem;
  font-weight: 700;
  text-align: center;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.person__role {
  margin-top: 2px;
  color: var(--muted);
  font-size: 0.9rem;
}

.contact-card {
  display: grid;
  gap: 14px;
  padding: 26px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
}

.contact-line {
  display: flex;
  align-items: center;
  gap: 14px;
}

.contact-line__label {
  display: block;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.contact-line__value {
  display: block;
  color: var(--ink);
  font-weight: 700;
  overflow-wrap: anywhere;
}

a.contact-line__value {
  color: var(--blue);
}

/* --- Kontaktformular ------------------------------------------------------
   Die Karte folgt .card: gleiche Flaeche, Kante, Radius und derselbe
   Lichtschimmer von oben. Sie hebt sich beim Ueberfahren NICHT an — ein
   Formular, das unter dem Zeiger wegwandert, waehrend man ein Feld anklickt,
   ist ein Fehler, kein Detail. Der Schimmer bleibt, die Bewegung nicht.

   Die Felder folgen den Bedienelementen der App: 8 px Radius, 1 px Kante,
   Fokusring in der Marke. Keine eigene Formularsprache — ein Eingabefeld soll
   aussehen wie ein Knopf aussieht, nur eben zum Schreiben. */

.contact-form-card {
  position: relative;
  /* Volle Shell-Breite wie jede andere Flaeche der Seite. Die Breite wird
     NICHT in laengere Felder gegeben, sondern in mehr Spalten: drei Felder
     je Reihe statt zwei (siehe .contact-form__row). Ein 1100 px langes
     Eingabefeld waere falsch, drei 350 px lange sind richtig. */
  /* 32 px statt 26: die Karte traegt jetzt Panel-Radius wie .plan, und ein
     22-px-Radius braucht mehr Innenabstand, sonst schneidet die Rundung in
     den Text der ersten Zeile. */
  padding: 32px;
  background: var(--surface);
  border: 1px solid var(--rule);
  /* Panel statt Card: die Flaeche ist die groesste Einzelkarte der Seite und
     steht allein in ihrem Abschnitt — dieselbe Rolle, die .plan auf der
     Preisseite hat, also derselbe Radius. */
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-card);
  /* Wie .card: der Verlauf in ::before darf nicht aus der Karte heraus mit
     dem Hintergrund der Sektion mischen. */
  isolation: isolate;
  transition:
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out);
}

@media (max-width: 559px) {
  .contact-form-card {
    padding: 22px;
  }
}

.contact-form-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(120% 80% at 50% 0%, var(--glow-soft), transparent 70%);
  opacity: 0;
  transition: opacity var(--dur-settle) var(--ease-out);
}

/* Der Schimmer geht auch beim Tippen auf, nicht nur beim Ueberfahren: solange
   man im Formular arbeitet, ist es die aktive Karte der Seite. */
.contact-form-card:hover,
.contact-form-card:focus-within {
  border-color: var(--blue-line);
  box-shadow: var(--shadow-lift);
}

.contact-form-card:hover::before,
.contact-form-card:focus-within::before {
  opacity: 1;
}

/* Gesperrt und in Versalien wie .kicker und .callout__label: die Zeile
   benennt, was folgt, ohne eine Ebene in der Gliederung zu beanspruchen —
   die Ueberschrift der Sektion steht schon darueber. */
/* carries the card alone since the intro line went — it repeated the section
   heading right above it */
.contact-form__title {
  display: block;
  color: var(--ink);
  font-size: clamp(1.3rem, 2.2vw, 1.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.25;
}

/* Ein Trennstrich zwischen Einleitung und Feldern. Vorher lag dort nur
   Abstand, und die Karte las als eine lange Liste aus Text und Eingaben;
   der Strich trennt "worum es geht" von "was ihr eintragt". */
.contact-form {
  display: grid;
  /* 18 px zwischen den Reihen: die Felder tragen jetzt 12 px Innenabstand
     oben und unten, ein 16-px-Zeilenabstand liess sie zusammenkleben. */
  gap: 18px;
  margin-top: 24px;
  padding-top: 24px;
  border-top: 1px solid var(--rule);
}

/* one grid for all fields so every column edge lines up from row to row */
.contact-form__row {
  display: grid;
  gap: 18px;
}

/* first and last name side by side; club and email keep the full width they need */
@media (min-width: 620px) {
  .contact-form__row {
    grid-template-columns: repeat(2, 1fr);
  }

  .field--wide {
    grid-column: 1 / -1;
  }
}

.contact-form__consents {
  display: grid;
  gap: 4px;
}

/* the whole row is the hit area, so it stays 44px tall around a small box */
.field--check {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  color: var(--ink);
  font-size: 0.9rem;
}

.field--check label {
  cursor: pointer;
}

.field__check {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--blue);
  cursor: pointer;
}

.field {
  display: grid;
  gap: 6px;
  margin: 0;
}

/* Beschriftung und Zusatz in einer Flexzeile mit Grundlinien-Ausrichtung.
   Vorher war das ein Inline-Fluss: der laengere Zusatz ("nur falls euch ein
   Rueckruf lieber ist") brach dann mitten im Satz um und schob sein Feld
   gegenueber dem Nachbarfeld nach unten — die Beschriftungen einer Reihe
   standen nicht mehr auf einer Linie.

   Jetzt bleibt die Zeile einzeilig: die Zusaetze sind auf zwei Worte gekuerzt
   (siehe CONTACT_FORM in src/data/site.js) und brechen nicht mehr um. */
.field__label {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 700;
}

/* --tint statt --surface: auf der weissen Karte muss die Mulde sichtbar sein,
   in die man schreibt. Ein weisses Feld auf weisser Karte traegt allein die
   Kante, und die ist auf --rule zu leise, um als Eingabe zu lesen. */
.field__input {
  width: 100%;
  /* 12/14 statt 11/13 und eine Mindesthoehe von 44 px: dieselbe Zielgroesse,
     die jeder Knopf der Seite hat (.button min-height 44). Ein Eingabefeld,
     das 2 px flacher ist als der Knopf darunter, liest als andere Bauart —
     und 44 px ist ausserdem die Grenze, unter der ein Ziel auf dem Telefon
     zu klein wird. */
  min-height: 44px;
  padding: 12px 14px;
  background: var(--tint);
  border: 1px solid var(--rule);
  border-radius: var(--radius-control);
  color: var(--ink);
  font-family: inherit;
  font-size: 0.95rem;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    border-color var(--dur-quick) var(--ease-out),
    box-shadow var(--dur-quick) var(--ease-out);
}

.field__input::placeholder {
  color: var(--faint);
}

.field__hint {
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.4;
}

.field__input:hover {
  border-color: var(--blue-line);
}

/* Beim Schreiben wird das Feld weiss: die Mulde wird zur Flaeche, auf der der
   eigene Text steht. Der Ring in der Marke ist derselbe wie an den Knoepfen. */
.field__input:focus-visible {
  outline: none;
  background: var(--surface);
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-wash);
}

/* --- Absenden und was danach kommt ------------------------------------------
   Knopf, Zusage und Kleingedrucktes waren drei gleich laute Zeilen
   untereinander, danach ein L: der Knopf links, die Zusage rechts auf halber
   Hoehe, das Kleingedruckte wieder linksbuendig darunter. Drei Elemente,
   drei Kanten.

   Jetzt zwei Spalten. Links eine Textspalte — Zusage ueber Kleingedrucktem,
   beide an derselben linken Linie wie die Felder darueber. Rechts aussen der
   Knopf, senkrecht mittig zur Textspalte. Der Blick laeuft durch die Zusage
   und endet auf der Handlung.

   Der Trennstrich nach oben bleibt: er trennt "was ihr eintragt" von "was
   dann passiert", genau wie der Strich unter der Einleitung. */
.contact-form__actions {
  display: grid;
  gap: 14px;
  margin-top: 6px;
  padding-top: 22px;
  border-top: 1px solid var(--rule);
}

/* Zusage und Kleingedrucktes als ein Stapel. 6 px zueinander: sie sind eine
   Aussage in zwei Lautstaerken, kein Absatzpaar. */
.contact-form__note {
  display: grid;
  gap: 6px;
}

/* Die zwei Spalten erst ab 900 px, nicht ab 620. Darunter ist die Textspalte
   so schmal, dass die Zusage auf drei Zeilen laeuft — der Knopf daneben
   deckte dann die Datenschutzzeile, weil er beide Textzeilen ueberspannt und
   dabei hoeher ist als die eine, aber flacher als beide. Unter 900 px stapelt
   der Abschluss deshalb: Knopf, Zusage, Kleingedrucktes.

   align-items: center richtet den Knopf an der Mitte der Textspalte aus, nicht
   an ihrer Oberkante — sonst saesse er oben und die Gruppe kippte. */
@media (min-width: 900px) {
  .contact-form__actions {
    grid-template-columns: 1fr auto;
    align-items: center;
    /* 32 px zwischen Text und Knopf, damit der Knopf nicht am Satzende
       klebt; 12 px zwischen den beiden Textzeilen, damit sie als eine
       Spalte lesen und nicht als zwei Absaetze. */
    column-gap: 32px;
    row-gap: 12px;
  }

  /* Zwei Zellen in Zeile 2: links der Textblock, rechts der Knopf. Die
     Zuweisung steht explizit und nicht per order, weil die Quellreihenfolge
     (Knopf zuerst) nicht die Bildreihenfolge ist — order verschiebt nur das
     Bild, der Tastaturfokus liefe weiter der Quelle nach, und ein Knopf, den
     man findet, aber nicht dort sieht, ist ein Fehler. */
  .contact-form__note {
    grid-column: 1;
    grid-row: 2;
  }

  .contact-form__submit {
    grid-column: 2;
    grid-row: 2;
  }
}

/* Schmal nimmt der Knopf die ganze Breite und steht zuoberst: auf dem
   Telefon ist er das Ziel, und daumengerecht heisst hier volle Breite.
   Breit behaelt er seine eigene Groesse. */
.contact-form__submit {
  width: 100%;
}

@media (min-width: 900px) {
  .contact-form__submit {
    width: auto;
    /* Ohne min-width schrumpft der Knopf im englischen Satz ("Request a
       trial") deutlich gegenueber dem deutschen und die rechte Kante der
       Karte bekaeme zwei verschiedene Abstaende je Sprache. */
    min-width: 210px;
  }
}

/* Die Rueckmeldung. Ohne data-state ist sie leer und nimmt keinen Platz —
   sonst klaffte ueber der Zusage eine Luecke, solange nichts passiert.
   Sie spannt ueber beide Spalten und steht damit ueber Text und Knopf: eine
   Fehlermeldung darf nicht in der halben Breite neben dem Knopf stehen. */
.contact-form__feedback {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 700;
  grid-column: 1 / -1;
  grid-row: 1;
}

.contact-form__feedback:not([data-state]) {
  display: none;
}

.contact-form__feedback[data-state="error"] {
  color: var(--placeholder);
}

/* Die Zusage, die die Telefonnummer ersetzt. Sie steht nicht auf --note-bg:
   neben dem gefuellten Knopf waere ein zweiter gefuellter Balken eine
   zweite Handlung, und sie ist keine. Statt der Flaeche traegt sie nur das
   Uhr-Symbol in der Marke — dieselbe Bauart wie die Haken in .included. */
.contact-form__promise {
  display: flex;
  /* Oben ausgerichtet: laeuft die Zusage ueber mehrere Zeilen, saesse ein
     mittig gesetztes Symbol auf Zeile zwei statt neben dem Satzanfang. */
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 700;
  line-height: 1.45;
}

.contact-form__promise svg {
  flex: none;
  /* Auf die Grundlinie der ersten Zeile geschoben: bei flex-start sitzt das
     Symbol sonst an der Oberkante des Textkastens, also sichtbar hoeher als
     der Buchstabe daneben. */
  margin-top: 2px;
  color: var(--blue);
}

.contact-form__privacy {
    grid-column: 1 / -1;
  }
}

/* Schmal nimmt der Knopf die ganze Breite — daumengerecht. Breit behaelt er
   seine eigene Groesse und steht links, wie jeder andere Knopf der Seite. */
.contact-form__submit {
  width: 100%;
}

@media (min-width: 620px) {
  .contact-form__submit {
    width: auto;
    /* Ohne min-width waere der Knopf schmaler als die Zusage daneben und die
       Gruppe kippte nach rechts. 210 px gibt ihm das Gewicht der Handlung. */
    min-width: 210px;
  }
}

/* Die Rueckmeldung. Ohne data-state ist sie leer und nimmt keinen Platz —
   sonst klaffte ueber dem Versprechen eine Luecke, solange nichts passiert.
   Sie spannt ueber beide Spalten: eine Fehlermeldung darf nicht in der
   halben Breite neben dem Knopf stehen. */
.contact-form__feedback {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 700;
  grid-column: 1 / -1;
}

.contact-form__feedback:not([data-state]) {
  display: none;
}

.contact-form__feedback[data-state="error"] {
  color: var(--placeholder);
}

/* Das Versprechen, das die Telefonnummer ersetzt. Es steht nicht mehr auf
   --note-bg: neben dem gefuellten Knopf waere ein zweiter gefuellter Balken
   eine zweite Handlung, und es ist keine. Statt der Flaeche traegt es jetzt
   nur das Uhr-Symbol in der Marke — dieselbe Bauart wie die Haken in
   .included und die Zeilen in .callout. */
.contact-form__promise {
  display: flex;
  /* Oben ausgerichtet, nicht mittig: schmal laeuft die Zusage ueber drei
     Zeilen, und ein mittig gesetztes Symbol saesse dann auf Zeile zwei
     statt neben dem Satzanfang. */
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  color: var(--body);
  font-size: 0.88rem;
  font-weight: 700;
  line-height: 1.45;
}

.contact-form__promise svg {
  flex: none;
  /* Auf die Grundlinie der ersten Zeile geschoben: bei flex-start sitzt das
     Symbol sonst an der Oberkante des Textkastens, also sichtbar hoeher als
     der Buchstabe daneben. */
  margin-top: 2px;
  color: var(--blue);
}



/* Das Kleingedruckte unter der Zusage. Es startet 27 px eingerueckt — die
   Breite des Uhr-Symbols plus dessen Abstand (17 + 10). Damit fluchtet sein
   Satzanfang mit dem Satzanfang der Zusage darueber und nicht mit dem
   Symbol: die linke Spalte hat eine Textkante, keine zwei. */
.contact-form__privacy {
  margin: 0;
  padding-left: 27px;
  color: var(--faint);
  font-size: 0.82rem;
  line-height: 1.5;
  /* Der Satz ist die leiseste Zeile der Karte und darf nicht ueber die ganze
     Breite laufen: sonst liest er wie eine weitere Aussage statt wie
     Kleingedrucktes. */
  max-width: 68ch;
}

/* Die Bestaetigung nach dem Absenden. Sie ersetzt das Formular im Ganzen und
   sitzt dann allein in einer Karte, die auf 32 px Innenabstand und
   Panel-Radius ausgelegt ist. Zentriert und mit Luft darum, damit sie die
   Flaeche traegt statt als kleiner Textblock oben links darin zu sitzen. */
.contact-form__done {
  display: grid;
  gap: 10px;
  padding: 26px 0;
  justify-items: center;
  text-align: center;
}

.contact-form__done:focus-visible {
  outline: none;
}

.contact-form__done-title {
  margin: 0;
  color: var(--ink);
  font-size: 1.1rem;
  font-weight: 700;
}

.contact-form__done-text {
  margin: 0;
  color: var(--body);
  max-width: 52ch;
}

/* ==========================================================================
   13. FAQ
   ========================================================================== */

.faq {
  max-width: 78ch;
  margin-inline: auto;
  display: grid;
  gap: 12px;
}

.faq details {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-chip);
  box-shadow: var(--shadow-card);
}

.faq details[open] {
  border-color: var(--blue-line);
}

.faq summary {
  position: relative;
  padding: 18px 52px 18px 20px;
  color: var(--ink);
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

/* Plus / minus drawn in CSS — no icon needed for this control. */
.faq summary::before,
.faq summary::after {
  content: "";
  position: absolute;
  right: 20px;
  top: 27px;
  background: var(--blue);
  border-radius: 1px;
}

.faq summary::before {
  width: 13px;
  height: 2px;
}

.faq summary::after {
  width: 2px;
  height: 13px;
  right: 25.5px;
  top: 21.5px;
  transition: transform var(--dur-settle) var(--ease-spring);
}

.faq details[open] summary::after {
  transform: scaleY(0);
}

.faq__answer {
  padding: 0 20px 20px;
  color: var(--muted);
  max-width: 68ch;
}

/* ==========================================================================
   14. Closing band and footer
   ========================================================================== */

.closing {
  background: var(--tint);
  text-align: center;
}

.closing__claim {
  max-width: 30ch;
  margin: 0 auto;
  font-size: clamp(1.7rem, 3.6vw, 2.5rem);
}

.closing__lead {
  max-width: 46ch;
  margin: 18px auto 0;
  font-size: 1.06rem;
  color: var(--muted);
}

.closing__brand {
  margin-top: 18px;
  color: var(--blue);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.closing .actions {
  margin-top: 30px;
  justify-content: center;
}

.footer {
  padding-block: 48px 36px;
  background: var(--surface);
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: 0.9rem;
}

.footer__grid {
  display: grid;
  gap: 28px;
}

.footer a {
  color: var(--muted);
  text-decoration: none;
}

.footer a:hover {
  color: var(--blue);
  text-decoration: underline;
}

.footer__wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  font-size: 1.16rem;
  font-weight: 700;
  letter-spacing: -0.005em;
}

.footer__list {
  display: grid;
  gap: 4px;
  margin-top: 12px;
}

/* Footer links measured at 18-23 px tall, below the 24 px WCAG 2.2 target
   minimum. Padding lifts the hit area without changing the visual rhythm. */
.footer__list li {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 4px;
  min-height: 28px;
}

.footer__list a,
.footer__legal a {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding-block: 3px;
}

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 24px;
}

.footer__legal a {
  color: var(--ink);
  font-weight: 700;
}

.footer__baseline {
  margin-top: 32px;
  padding-top: 20px;
  border-top: 1px solid var(--rule);
  color: var(--faint);
  font-size: 0.83rem;
}

@media (min-width: 768px) {
  .footer__grid {
    grid-template-columns: 1fr auto;
    align-items: start;
  }

  .footer__legal {
    justify-content: flex-end;
  }
}

/* ==========================================================================
   15. Rechtliche Unterseiten
   ==========================================================================

   Impressum, Datenschutzerklaerung. Fliesstext, der gelesen werden muss —
   nicht ueberflogen. Die Gestaltung leistet hier zwei Dinge: die Zeile kurz
   halten und die Abschnitte voneinander trennen.

   Die Datenschutzerklaerung bekommt zusaetzlich einen Einstieg (drei
   Kernaussagen plus Abschnittsliste, siehe PrivacyIntro.astro). Sie war
   vorher 3.100 px durchgehender Text ohne Orientierung.
   ========================================================================== */

.legal {
  padding-block: 48px var(--section-gap);
}

/* .prose stand in sechs Seiten im Markup, ohne dass es dazu eine Regel gab.
   Jetzt traegt es, was es verspricht: eine Textspalte in Lesebreite. 72ch ist
   etwas mehr als die 68ch der Absaetze unten, damit Tabellen und Karten die
   Spalte nutzen koennen, ohne dass der Fliesstext mitwaechst. */
.prose {
  max-width: 72ch;
}

.legal__title {
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  /* "Datenschutzerklärung" is 20 characters — long enough to exceed a 320 px
     viewport at the clamp minimum, so allow it to break. */
  overflow-wrap: break-word;
  hyphens: auto;
}

@media (max-width: 400px) {
  .legal__title {
    font-size: clamp(1.5rem, 7.5vw, 1.9rem);
  }
}

/* Der Vorspann steht etwas groesser als der Rest: er sagt, worum es auf der
   Seite geht, und wird als einziger Absatz von allen gelesen. */
.legal__lead {
  margin-top: 16px;
  color: var(--body);
  font-size: 1.08rem;
}

.legal .hint {
  margin-block: 32px;
}

/* Abschnittsueberschriften tragen eine Linie darueber. Bei acht Abschnitten
   Rechtstext ist der Abstand allein zu schwach: man sieht nicht, wo einer
   endet und der naechste beginnt.

   :not() statt eines Overrides mit hoeherer Spezifitaet: die Beschriftung im
   Navigationskasten ist ein h2 (sie benennt die Navigation), aber keine
   Abschnittsueberschrift. Sie hier auszunehmen ist ehrlicher, als sie
   nachtraeglich zurueckzusetzen. */
.legal h2:not(.legal-toc__label) {
  margin-top: 52px;
  padding-top: 26px;
  border-top: 1px solid var(--rule);
  font-size: 1.35rem;
  /* Sprungziele muessen unter dem klebenden Kopf landen. html traegt bereits
     scroll-padding-top; das hier faengt den Fall ab, dass eine Ueberschrift
     als erstes Element einer Flaeche sitzt. */
  scroll-margin-top: calc(var(--header-height) + 28px);
}

/* Die erste Ueberschrift nach dem Inhaltsverzeichnis braucht die Linie nicht —
   der Kasten ist selbst eine Flaeche und trennt schon.

   Der Selektor wiederholt die Klassen der Regel darueber, weil er sie sonst
   nicht schlaegt: .legal h2:not(...) hat zwei Klassen, .legal-toc + h2
   haette nur eine. */
.legal .legal-toc + h2:not(.legal-toc__label) {
  margin-top: 44px;
  padding-top: 0;
  border-top: 0;
}

.legal h3 {
  margin-top: 28px;
  font-size: 1.05rem;
}

/* Fliesstext-Absaetze. Der Selektor ist bewusst ein Kindselektor: er soll nur
   Absaetze treffen, die direkt in der Textspalte liegen, nicht die in
   eingebetteten Bloecken (.definitions, .legal-toc) — die tragen eigene
   Abstaende und eigene Breiten.

   Alle rechtlichen Seiten haben die Struktur main.legal > div.shell.prose > p,
   ein Selektor genuegt also. */
.prose > p {
  margin-top: 14px;
  max-width: 68ch;
}

.legal .dotted,
.legal .definitions {
  margin-top: 18px;
  max-width: 68ch;
}

/* --- Die Anbieterkarte im Impressum ----------------------------------------
   Die Pflichtangaben nach § 5 DDG als ein Block, vor den Rechtstexten.

   Vorher standen sie als zwei Abschnitte mit je einer Definitionsliste
   ("Angaben gemaess § 5 DDG" und "Kontakt") zwischen den Haftungstexten. Die
   Trennung stellte eine Grenze dar, die das Gesetz nicht kennt: § 5 verlangt
   Name, Anschrift und die Angaben zur schnellen elektronischen
   Kontaktaufnahme in einem Zug.

   Eine Karte, weil das Impressum nachgeschlagen und nicht gelesen wird: die
   vier Fragen (wer, wo, wer haftet, wie erreichbar) sollen zusammen und ohne
   Suchen beantwortet sein.
   ========================================================================== */

.provider {
  margin-top: 20px;
  max-width: 68ch;
  padding: 4px 22px 6px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

.provider__grid {
  margin: 0;
}

.provider__row {
  display: grid;
  gap: 2px 24px;
  padding-block: 15px;
  border-bottom: 1px solid var(--rule);
}

/* Die letzte Zeile braucht keine Linie: darunter endet die Karte. */
.provider__row:last-child {
  border-bottom: 0;
}

.provider__label {
  margin: 0;
  color: var(--ink);
  font-weight: 700;
}

.provider__value {
  margin: 0;
  color: var(--body);
}

/* <address> traegt in Browsern kursiv als Vorgabe — hier ist es eine
   Anschrift, keine Betonung. */
.provider__address {
  font-style: normal;
}

.provider__value a {
  color: var(--blue);
  text-decoration-color: var(--blue-line);
  text-underline-offset: 3px;
}

.provider__value a:hover {
  text-decoration-color: currentColor;
}

@media (min-width: 640px) {
  .provider__row {
    /* Dieselben 190 px wie .definitions__row auf den uebrigen Rechtsseiten:
       die Beschriftungsspalte soll ueber alle Seiten auf einer Linie stehen. */
    grid-template-columns: 190px 1fr;
  }
}

@media (max-width: 599px) {
  .provider {
    padding-inline: 16px;
  }
}

/* --- Abschnittsliste --------------------------------------------------------
   Das Inhaltsverzeichnis der Erklaerung, direkt unter der Einleitung.

   Hier standen bis vor kurzem drei Karten mit den Kernaussagen daneben, der
   Kasten nahm nur die rechte Spalte. Die Karten sind entfernt — eine
   Zusammenfassung neben dem verbindlichen Text erzeugt zwei Fassungen
   derselben Aussage, von denen nur eine gilt. Der Kasten steht jetzt allein
   und ueber die Breite der Textspalte.

   Kein position:sticky: der Kasten ist ein Inhaltsverzeichnis am Anfang, wie
   ein gedrucktes Dokument es hat. Ein mitlaufender Kasten braeuchte eine
   zweispaltige Seite, in der die Navigation neben dem GANZEN Text steht —
   ein Umbau der Seitenstruktur, und die naechste Stufe, falls die Erklaerung
   weiter waechst. */
.legal-toc {
  /* Der Abstand nach oben kam vorher vom Wrapper .legal-intro, den es nicht
     mehr gibt. */
  margin-top: 34px;
  padding: 22px 24px;
  background: var(--tint-soft);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  /* Auf der Breite des Fliesstextes (.prose > p traegt 68ch): der Kasten ist
     Teil des Dokuments, nicht ein Band darum. Ohne die Grenze liefe er ueber
     die ganze Shell und waere breiter als jeder Absatz, auf den er zeigt. */
  max-width: 68ch;
}

/* Beschriftung des Kastens — dieselbe Form wie .kicker, weil sie dieselbe
   Aufgabe hat: benennen, was folgt. */
.legal-toc__label {
  margin: 0;
  color: var(--blue);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.legal-toc__list {
  /* Kein margin-bottom: die Liste ist das letzte Element im Kasten, und der
     Innenabstand des Kastens traegt den Abschluss. Vorher stand hier der
     Browser-Standard von 1em, der zusammen mit dem padding eine leere Zeile
     unter "8. Stand" ergab. */
  margin: 12px 0 0;
  padding-left: 0;
  /* Die Nummern kommen aus dem Zaehler unten, nicht vom Listenmarker: so
     stehen sie in Markenfarbe und mit fester Breite. */
  list-style: none;
  counter-reset: toc;
}

.legal-toc__list > li {
  counter-increment: toc;
}

.legal-toc__list > li::before {
  content: counter(toc) ".";
  display: inline-block;
  width: 1.6em;
  color: var(--blue);
  font-size: 0.85rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.legal-toc__list a {
  display: inline-flex;
  align-items: center;
  /* 28 px Trefferflaeche fuer acht dicht stehende Links. */
  min-height: 28px;
  color: var(--body);
  font-size: 0.93rem;
  text-decoration: none;
}

.legal-toc__list a:hover {
  color: var(--blue);
  text-decoration: underline;
}

.backlink {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 48px;
  font-weight: 700;
  text-decoration: none;
}

.backlink:hover {
  text-decoration: underline;
}


/* ==========================================================================
   16b. App-Vorschau — das Dashboard als Handy, in HTML
   ==========================================================================

   Die Vorschau trägt die Vereinsfarbe der Seite. Das ist keine Kosmetik: die
   App übernimmt laut Produktversprechen Logo und Farbe des Vereins, die
   Vorschau zeigt sie also in *einer* möglichen Vereinsfarbe — hier KoHub.

   Eigene --app-*-Tokens statt der Seiten-Tokens, damit die Flächen im Gerät
   unabhängig von der Seite gesetzt werden können: das Telefon zeigt immer
   eine App-Oberfläche, auch wenn die Seite drumherum warm getönt ist.
   ========================================================================== */



/* --- Gehäuse ---------------------------------------------------------------
   Ein Telefon, nicht ein Bild von einem Telefon: Rahmen, Kante und Schatten
   sind CSS. Keine Marke, keine Knöpfe eines bestimmten Herstellers. */

.preview__phone {
  width: 100%;
  max-width: 310px;
  padding: 9px;
  border-radius: 40px;
  background: var(--phone-frame);
  /* Innen die helle Kante, außen der Schlagschatten — zusammen liest sich das
     als Aluminiumrahmen statt als schwarzes Rechteck. */
  box-shadow:
    inset 0 0 0 1px var(--phone-edge),
    0 24px 60px -18px rgba(20, 33, 31, 0.5),
    0 4px 14px rgba(20, 33, 31, 0.16);
}

.preview__screen {
  position: relative;
  border-radius: 32px;
  overflow: hidden;
  background: var(--app-page);
  /* Ein Telefonschirm ist hoch, nicht quadratisch. Feste Proportion, damit
     der Ausschnitt auf jeder Breite gleich wirkt. */
  aspect-ratio: 9 / 18.5;
}

/* Statusleiste: Uhrzeit links, Sensorinsel mittig, Anzeigen rechts. */
.preview__status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 18px 3px;
  color: var(--app-ink);
  font-size: 10px;
  font-weight: 700;
}

/* Die Sensorinsel ist immer schwarz — auch auf dunklem Schirm, sonst
   verschwindet sie und das Gerät verliert sein erkennbares Merkmal. */
.preview__island {
  width: 58px;
  height: 15px;
  border-radius: 999px;
  background: #05100f;
}

.preview__status-right {
  display: flex;
  align-items: center;
  gap: 5px;
}

.preview__signal {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 9px;
}

.preview__signal i {
  display: block;
  width: 2.5px;
  border-radius: 1px;
  background: var(--app-ink);
}

.preview__signal i:nth-child(1) { height: 3px; }
.preview__signal i:nth-child(2) { height: 5px; }
.preview__signal i:nth-child(3) { height: 7px; }
.preview__signal i:nth-child(4) { height: 9px; }

.preview__battery {
  width: 20px;
  height: 10px;
  padding: 1.5px;
  border: 1px solid var(--app-ink);
  border-radius: 3px;
  opacity: 0.9;
}

.preview__battery span {
  display: block;
  width: 72%;
  height: 100%;
  border-radius: 1px;
  background: var(--app-ink);
}

/* Kopfzeile der App im Gerät. */
.preview__appbar {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Rechts mehr Luft, damit der Zähler am Menüknopf nicht an die
     Bildschirmkante stößt. */
  padding: 6px 16px 10px 12px;
}

/* One app bar per club sits in the shell, all but one carrying [hidden].
   display:flex would win against it, so it needs saying explicitly — same as
   .demo__pane and .preview__detailbar. */
.preview__appbar[hidden] {
  display: none;
}

.preview__appbar .preview__club {
  flex: 1;
  font-size: 12px;
}

/* Menüknopf rechts in der Kopfzeile — wie in der App: quadratisch, auf
   gedämpftem Grund, mit Zähler für Ungelesenes. Drei Balken in CSS. */
.preview__menu {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3.5px;
  width: 27px;
  height: 27px;
  flex: 0 0 auto;
  margin-left: auto;
  border-radius: 7px;
  background: var(--app-tint);
}

.preview__menu i {
  display: block;
  width: 13px;
  height: 1.75px;
  border-radius: 1px;
  background: var(--app-muted);
}

.preview__menu::after {
  content: "35";
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 14px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--app-brand);
  color: var(--app-on-brand);
  font-size: 8px;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
  /* Der Ring trennt den Zähler vom Knopf darunter, wie in der App. */
  box-shadow: 0 0 0 1.5px var(--app-page);
}

/* Der scrollbare Bereich zwischen Kopfzeile und Registerleiste. */
.preview__body {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 0 12px 12px;
  overflow: hidden;
}

/* Registerleiste am unteren Rand — fünf Ziele, wie in der App. */
.preview__tabs {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  /* 72 px in der App, auf den Maßstab der Vorschau gebracht. */
  padding: 7px 4px 15px;
  background: var(--app-surface);
  border-top: 1px solid var(--app-rule);
}

.preview__tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  /* Fuenf gleich breite Zellen, die auch kuerzen duerfen — sonst schieben
     lange Beschriftungen wie "Neuigkeiten" die Nachbarn ineinander. */
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--app-muted);
  /* 10.5px in der App bei 390 px Geraetebreite. Die Vorschau ist schmaler,
     deshalb verhaeltnisgleich kleiner — sonst stossen die fuenf
     Beschriftungen aneinander. */
  font-size: 6.4px;
  letter-spacing: -0.01em;
  font-weight: 500;
}

.preview__tab svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
}

/* Der aktive Reiter trägt Farbe und mehr Gewicht — beides, wie in der App. */
.preview__tab--active {
  color: var(--app-brand);
  font-weight: 700;
}

/* Der Griffbalken unten, wie ihn aktuelle Telefone zeigen. */
.preview__home {
  position: absolute;
  left: 50%;
  bottom: 5px;
  width: 88px;
  height: 4px;
  transform: translateX(-50%);
  border-radius: 999px;
  background: var(--app-ink);
  opacity: 0.28;
}

/* Grundgröße des Inhalts im Gerät. */
.preview__screen {
  font-size: 11px;
  line-height: 1.4;
  color: var(--app-body);
}

.preview__brand {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding: 0 4px;
}

.preview__club {
  color: var(--app-ink);
  font-weight: 700;
  font-size: 11.5px;
  /* Lange Vereinsnamen dürfen die Spalte nicht sprengen. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview__group {
  margin: 10px 0 2px;
  padding: 0 8px;
  color: var(--app-muted);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.preview__navitem {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 7px;
  color: var(--app-body);
  white-space: nowrap;
}

.preview__navitem svg {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
}

.preview__navitem--active {
  background: var(--app-tint);
  color: var(--app-brand);
  font-weight: 700;
}

.preview__badge {
  margin-left: auto;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--app-brand);
  color: var(--app-on-brand);
  font-size: 9px;
  font-weight: 700;
}

/* --- Inhalt --- */

.preview__main {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  min-width: 0;
}

.preview__card {
  padding: 9px 10px;
  background: var(--app-surface);
  border: 1px solid var(--app-rule);
  border-radius: 10px;
}

.preview__hello {
  display: flex;
  align-items: center;
  gap: 10px;
}

.preview__avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  border-radius: 8px;
  background: var(--app-tint);
  color: var(--app-brand);
  font-size: 10px;
  font-weight: 700;
}

.preview__avatar--club {
  background: var(--app-brand);
  color: var(--app-on-brand);
  border-radius: 9px;
}

.preview__greeting {
  display: block;
  color: var(--app-ink);
  font-size: 12.5px;
}

.preview__sub {
  display: block;
  color: var(--app-muted);
  font-size: 10px;
}

.preview__stat {
  padding: 8px 4px;
}

.preview__stats {
  display: grid;
  /* minmax(0, 1fr): sonst hält jede Karte die Breite ihres längsten Wortes
     ("Neuigkeiten") und die Reihe sprengt die Vorschau auf schmalen Geräten. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.preview__stat .preview__sub {
  /* Lieber abschneiden als die Spalte aufdrücken. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview__stat {
  text-align: center;
}

.preview__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-bottom: 5px;
  border-radius: 7px;
  background: var(--app-tint);
  color: var(--app-brand);
}

.preview__chip svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
}

.preview__chip--tone-2 {
  background: var(--app-tone-2-bg);
  color: var(--app-tone-2);
}

.preview__chip--tone-3 {
  background: var(--app-tone-3-bg);
  color: var(--app-tone-3);
}

.preview__statvalue {
  display: block;
  color: var(--app-ink);
  font-size: 17px;
  line-height: 1;
}

.preview__cardhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 9px;
}

.preview__label {
  color: var(--app-ink);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.preview__all {
  color: var(--app-brand);
  font-size: 10px;
  font-weight: 700;
}

.preview__teamrow {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.preview__team {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px 5px 5px;
  border: 1px solid var(--app-rule);
  border-radius: 8px;
  color: var(--app-ink);
  font-weight: 700;
  white-space: nowrap;
}

.preview__teammark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 19px;
  height: 19px;
  border-radius: 5px;
  background: var(--app-tint);
  color: var(--app-brand);
  font-size: 9px;
}

/* Im Telefon steht alles untereinander — eine Spalte, wie in der echten App. */
.preview__columns {
  display: grid;
  grid-template-columns: 1fr;
  gap: 9px;
  min-width: 0;
}


.preview__row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px;
  border: 1px solid var(--app-rule);
  border-radius: 8px;
  min-width: 0;
}

.preview__row + .preview__row {
  margin-top: 7px;
}

.preview__row--news {
  display: block;
}

.preview__date {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border-radius: 7px;
  background: var(--app-tint);
  color: var(--app-brand);
}

.preview__month {
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.preview__day {
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}

.preview__rowtext {
  min-width: 0;
}

.preview__rowtitle {
  display: block;
  color: var(--app-ink);
  font-size: 11px;
  /* Titel dürfen die Karte nicht auseinanderdrücken. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ==========================================================================
   16c. Glas, Kennzahlen und Vorschau-Umschalter
   ==========================================================================

   Alles in diesem Abschnitt gehoert zu Flaechen mit Verlauf: das Kennzahlen-
   band und das Abschlussband. Glas ausserhalb dieser Flaechen waere wirkungs-
   los (siehe Kommentar an --glass-bg).
   ========================================================================== */

/* --- Glaskarte ---------------------------------------------------------------
   Variante von .card, nicht Ersatz: sie erbt Radius, Polsterung und die
   Hebung, tauscht nur Flaeche und Kante. */
.card--glass {
  background: var(--glass-bg);
  border-color: var(--glass-border);
  /* Fallt sauber auf die halbdeckende Flaeche zurueck, wo backdrop-filter
     fehlt — dort bleibt die Karte lesbar, nur ohne Blur. */
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

.card--glass:hover {
  background: var(--glass-bg-strong);
  border-color: var(--blue-line);
}

/* --- Band mit Verlauf --------------------------------------------------------
   Die Traegerflaeche fuer Glaskarten. Zwei Scheine plus Grundton, damit
   die Transparenz der Karten etwas zu zeigen hat. */
.band {
  position: relative;
  background:
    radial-gradient(80% 70% at 12% 0%, var(--glow-brand) 0%, transparent 62%),
    radial-gradient(70% 80% at 92% 100%, var(--glow-accent) 0%, transparent 58%),
    var(--tint);
  overflow: hidden;
}

/* Haarlinie oben und unten, die in der Mitte am hellsten ist: sie trennt das
   Band von den Nachbarsektionen, ohne als durchgezogener Strich zu wirken. */
.band::before,
.band::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--gradient-line);
}

.band::before {
  top: 0;
}

.band::after {
  bottom: 0;
}

/* --- Kennzahlen -------------------------------------------------------------
   Vier Werte in einer Reihe. Die Zahl steht gross und in Markenfarbe, das
   Etikett darunter gedaempft — die Reihe liest sich damit als Kennzahlen,
   nicht als vier kurze Absaetze. */
.metrics {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 18px;
}

@media (min-width: 900px) {
  .metrics {
    grid-template-columns: repeat(4, 1fr);
    gap: 22px;
  }
}

.metric {
  position: relative;
  padding: 24px 22px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-card);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-card);
  text-align: center;
  transition:
    transform var(--dur-settle) var(--ease-out),
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out);
}

.metric:hover {
  transform: translateY(calc(var(--lift) * -1));
  border-color: var(--blue-line);
  box-shadow: var(--shadow-lift);
}

/* Das Symbol sitzt ueber der Zahl und mittig, weil die Kachel mittig setzt.
   Es traegt keine Aussage, die nicht im Etikett steht — es gibt der Kachel
   einen Anker fuers Auge, damit die Reihe nicht als vier Zahlen im Nichts
   liest. */
.metric__icon {
  margin: 0 auto 14px;
}

.metric__value {
  display: block;
  color: var(--blue);
  font-size: clamp(2rem, 4.4vw, 2.7rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.03em;
  /* Die Zahlen sollen in der Reihe untereinander stehen wie in einer Tabelle;
     Proportionalziffern verschieben sie gegeneinander. */
  font-variant-numeric: tabular-nums;
}

.metric__label {
  display: block;
  margin-top: 10px;
  color: var(--ink);
  font-size: 0.95rem;
  font-weight: 700;
}

.metric__note {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-size: 0.85rem;
}

/* --- Aussagen ---------------------------------------------------------------
   Der Rahmen fuer Stimmen aus Vereinen. Bis echte Zitate da sind, tragen die
   Karten Aussagen ueber das Produkt, die belegbar sind — kein erfundenes Lob
   mit erfundenem Namen. Inhalte stehen in src/data/site.js unter PROOF. */
.quotes {
  display: grid;
  gap: 18px;
  margin-top: 22px;
}

@media (min-width: 900px) {
  /* auto-fit rather than a fixed three: the band carries however many
     statements are in the data, and a fixed count leaves a hole when it is
     fewer. The 260px floor stops two cards from stretching absurdly wide. */
  .quotes {
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 22px;
  }
}

.quote {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 26px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-card);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-card);
  transition:
    transform var(--dur-settle) var(--ease-out),
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out);
}

.quote:hover {
  transform: translateY(calc(var(--lift) * -1));
  border-color: var(--blue-line);
  box-shadow: var(--shadow-lift);
}

.quote__text {
  margin: 0;
  color: var(--ink);
  font-size: 1.04rem;
  /* Etwas luftiger als der Fliesstext: eine hervorgehobene Aussage darf
     langsamer gelesen werden. */
  line-height: 1.55;
}

/* Die Fusszeile sitzt unten, auch wenn die Aussagen verschieden lang sind —
   deshalb margin-top:auto in der Spalte. */
.quote__by {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: auto;
  padding-top: 4px;
}

.quote__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  background: var(--blue-wash);
  border-radius: var(--radius-chip);
  color: var(--blue);
}

.quote__mark svg {
  width: 19px;
  height: 19px;
}

.quote__who {
  display: block;
  color: var(--ink);
  font-size: 0.92rem;
  font-weight: 700;
}

.quote__role {
  display: block;
  color: var(--muted);
  font-size: 0.85rem;
}

/* --- Vertrauensleiste unter den Kennzahlen ---------------------------------
   Wo andere Seiten Kundenlogos zeigen, stehen hier die Eigenschaften, die
   wir tatsaechlich belegen koennen. */
/* Same treatment as .trust: as a centred flex row every wrapped line centred
   on its own, so the icons stepped in and out with the length of each label.
   A grid of auto columns centres the block as a whole and keeps the items on
   one left edge inside it. */
.proof-strip {
  display: grid;
  grid-template-columns: auto;
  justify-content: center;
  justify-items: start;
  gap: 12px 28px;
  margin-top: 34px;
  padding-top: 26px;
  border-top: 1px solid var(--rule);
}

@media (min-width: 620px) {
  .proof-strip {
    grid-template-columns: auto auto;
  }
}

@media (min-width: 1080px) {
  .proof-strip {
    grid-template-columns: repeat(4, auto);
  }
}

.proof-strip__item {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 700;
}

.proof-strip__item svg {
  flex: 0 0 auto;
  color: var(--blue);
}

/* --- Die App als bedienbares Telefon ----------------------------------------
   Ein Rahmen, fuenf Screens darin, Registerleiste am unteren Rand.

   Ersetzt den frueheren Umschalter ueber dem Geraet. Der hatte fuenf komplette
   Telefon-Instanzen im Raster liegen; weil .preview__phone seine Breite vom
   Elternteil nahm, waren die Rahmen 242 bis 310 px breit — je nachdem, wie
   viel Platz das Pane bekam. Jetzt gibt es genau ein Geraet.

   Eigene .demo-*-Klassen statt .preview-*: die Huelle traegt hier andere
   Aufgaben (feste Hoehe, scrollender Bereich, klickbare Leiste). Die
   Bausteine INNERHALB des Screens bleiben .preview-* — sie sind unveraendert
   und werden von AppScreen.astro weiterbenutzt.
   ========================================================================== */

.demo {
  /* Die App-Tokens des Geraets. Identisch zu .preview, weil im Rahmen
     dieselben Bausteine stecken — siehe Abschnitt 16b fuer die Begruendung
     jedes Werts. */
  --app-brand: #0f6b70;
  --app-on-brand: #ffffff;
  --app-ink: #14211f;
  --app-body: #3a4b49;
  --app-muted: #566b69;
  --app-surface: #ffffff;
  --app-page: #f4f7f7;
  --app-rule: #e0ddd5;
  --app-tint: #e6f2f2;
  --app-tone-2: #1c5a63;
  --app-tone-2-bg: #e4f0f2;
  --app-tone-3: #2d5f52;
  --app-tone-3-bg: #e6f0ec;

  --phone-frame: #1b2726;
  --phone-edge: #354746;

  /* Senkrechte Achse: der Hinweis steht unter dem Geraet, nicht daneben.
     Waagerecht (der frueheren Fassung) wurde er als zweites Flex-Kind neben
     den Rahmen gesetzt und auf dessen Hoehe gestreckt — gemessen 586 px statt
     22 px. */
  display: flex;
  flex-direction: column;
  align-items: center;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .demo {
    --app-brand: #5fbfc4;
    --app-on-brand: #0b1413;
    --app-ink: #edf3f2;
    --app-body: #bccbc9;
    --app-muted: #9aafac;
    --app-surface: #131d1c;
    --app-page: #0d1615;
    --app-rule: #5c7a7b;
    --app-tint: #1b2e2e;
    --app-tone-2: #7ecfd0;
    --app-tone-2-bg: #192b2d;
    --app-tone-3: #6fbfa4;
    --app-tone-3-bg: #172a26;

    --phone-frame: #0a1211;
    --phone-edge: #2c3d3c;
  }
}

:root[data-theme="dark"] .demo {
  --app-brand: #5fbfc4;
  --app-on-brand: #0b1413;
  --app-ink: #edf3f2;
  --app-body: #bccbc9;
  --app-muted: #9aafac;
  --app-surface: #131d1c;
  --app-page: #0d1615;
  --app-rule: #5c7a7b;
  --app-tint: #1b2e2e;
  --app-tone-2: #7ecfd0;
  --app-tone-2-bg: #192b2d;
  --app-tone-3: #6fbfa4;
  --app-tone-3-bg: #172a26;

  --phone-frame: #0a1211;
  --phone-edge: #2c3d3c;
}


/* --- The call to try the demo ----------------------------------------------
   A graphic does not show that it can be touched, so this says it outright.

   It uses the same surface as .hint — border, --tint-soft, --radius-card — so
   it reads as part of the page rather than as loose text under the device. The
   pointer sits in a .chip, the way every other icon on the page does.

   After the first use (main.js sets .demo--used) it steps down to the quiet
   line rather than disappearing: someone who scrolls away and comes back
   should still find their way back into the demo.

   Without JavaScript the tab bar shows its state but does not switch, so the
   call is gated on .js — otherwise it would invite an action that cannot
   happen. */
.demo__hint {
  display: none;
}

.js .demo__hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  /* the space is reserved for the full two-line state, so stepping down after
     the first use does not shift what sits below the demo */
  min-height: 74px;
  margin: 18px auto 0;
  /* the width of the device, so the call reads as belonging to it */
  width: 272px;
  max-width: 100%;
  padding: 12px 16px;
  /* carries the brand rather than a neutral note surface: this is the one
     thing on the page asking to be touched, so it may not read as a caption */
  border: 1px solid var(--blue);
  border-radius: var(--radius-card);
  background: var(--note-bg);
  box-shadow: var(--shadow-card);
  text-align: left;
  transition:
    padding var(--dur-settle) var(--ease-out),
    border-color var(--dur-settle) var(--ease-out),
    box-shadow var(--dur-settle) var(--ease-out),
    background-color var(--dur-settle) var(--ease-out);
}

/* The pointer in a chip, like every other icon on the page. */
.demo__hint-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border-radius: var(--radius-chip);
  /* filled, not washed: the mark is the loudest part of the call */
  background: var(--blue);
  color: var(--on-blue);
  transition:
    width var(--dur-settle) var(--ease-out),
    height var(--dur-settle) var(--ease-out);
}

.demo__hint-mark svg {
  width: 19px;
  height: 19px;
  transition:
    width var(--dur-settle) var(--ease-out),
    height var(--dur-settle) var(--ease-out);
}

.demo__hint-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

/* The call itself — sized like a card title, not like a headline. */
.demo__hint-call {
  color: var(--blue);
  font-size: 1.04rem;
  font-weight: 700;
  letter-spacing: -0.012em;
  line-height: 1.3;
}

.demo__hint-note {
  /* --muted, not --faint: on --note-bg the fainter tone lands at 4.49 */
  color: var(--muted);
  font-size: 0.83rem;
  font-weight: 500;
}

/* The pointer taps — the same motion the note describes. */
.js .demo:not(.demo--used) .demo__hint-mark svg {
  animation: demo-tap 2.4s var(--ease-out) infinite;
}

@keyframes demo-tap {
  0%, 62%, 100% { transform: none; }
  70% { transform: translateY(3px); }
  78% { transform: none; }
  86% { transform: translateY(3px); }
}

/* .demo--used marks the demo as operated. It only stops the pointer and the
   pulse above — the call keeps its surface and its wording, so someone who
   scrolls back finds the same thing they left. */

/* While the call stands, the mark above the active tab pulses along quietly —
   text and motion then point at the same place. Opacity only, not size: a
   growing mark in a 65 px bar reads as a fault. */
.js .demo:not(.demo--used) .demo__tab--active::before {
  animation: demo-pulse 2.4s var(--ease-out) infinite;
}

@keyframes demo-pulse {
  0%, 55%, 100% { opacity: 1; }
  72% { opacity: 0.35; }
}

/* --- Per-club branding -------------------------------------------------------
   The point of the club switch: the same screens as another club's.

   How the real app does it (kohub_frontend,
   src/shared/hooks/useBrandingApplier.ts): the club's colour is written onto
   --brand/--primary and the text on it is derived by luminance
   (contrastForeground in branding.helper.ts); surfaces keep the theme's own
   values. This reproduces that, with one difference the app does not need: a
   dark theme here lightens the hue, exactly as club 0 already did long before
   the switch existed (#0f6b70 becomes #5fbfc4). Both clubs follow the same
   rule, because one club lightening and the other not would read as a bug.

   A club owns its whole device, not only its accents: surface and page carry a
   trace of the hue, the way club 0's dark surface leans green. Without that the
   tennis club is a green phone with ochre buttons.

   One source per club — --club-N-*, read by the device and by the tile in the
   switch, so the tile always previews the colour the device will take. Each
   value is measured against the surface it lands on; the dark values sit within
   a tenth of club 0's, so no club looks weaker than another. */
:root {
  --club-0-brand: #0f6b70;
  --club-0-on-brand: #ffffff;
  --club-0-surface: #ffffff;
  --club-0-page: #f4f7f7;
  --club-0-ink: #14211f;
  --club-0-body: #3a4b49;
  --club-0-muted: #566b69;
  --club-0-rule: #e0ddd5;
  --club-0-tint: #e6f2f2;
  --club-0-tone-2: #1c5a63;
  --club-0-tone-2-bg: #e4f0f2;
  --club-0-tone-3: #2d5f52;
  --club-0-tone-3-bg: #e6f0ec;

  /* Tennis — warm ochre. 5.92 on its own surface, white text on it at 5.92. */
  --club-1-brand: #8a5a0f;
  --club-1-on-brand: #ffffff;
  --club-1-surface: #ffffff;
  --club-1-page: #f9f6f1;
  --club-1-ink: #211a11;
  --club-1-body: #4b4034;
  --club-1-muted: #6b5c48;
  --club-1-rule: #e5ddcd;
  --club-1-tint: #f6ecdb;
  --club-1-tone-2: #7a5214;
  --club-1-tone-2-bg: #f5ead9;
  --club-1-tone-3: #5f5a1c;
  --club-1-tone-3-bg: #f1eeda;
  --club-1-frame: #2b2317;
  --club-1-edge: #4a3f2c;
  /* Musikverein — deep bordeaux. 6.83 on its own surface. */
  --club-2-brand: #a32e54;
  --club-2-on-brand: #ffffff;
  --club-2-surface: #ffffff;
  --club-2-page: #faf5f6;
  --club-2-ink: #1f1116;
  --club-2-body: #4a3239;
  --club-2-muted: #6d4d56;
  --club-2-rule: #e8d8dc;
  --club-2-tint: #f9e8ed;
  --club-2-tone-2: #8c3358;
  --club-2-tone-2-bg: #f7e7ec;
  --club-2-tone-3: #8a3a4a;
  --club-2-tone-3-bg: #f7e9eb;
  --club-2-frame: #2b1a20;
  --club-2-edge: #4d3540;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --club-0-brand: #5fbfc4;
    --club-0-on-brand: #0b1413;
    --club-0-surface: #131d1c;
    --club-0-page: #0d1615;
    --club-0-ink: #edf3f2;
    --club-0-body: #bccbc9;
    --club-0-muted: #9aafac;
    --club-0-rule: #5c7a7b;
    --club-0-tint: #1b2e2e;
    --club-0-tone-2: #7ecfd0;
    --club-0-tone-2-bg: #192b2d;
    --club-0-tone-3: #6fbfa4;
    --club-0-tone-3-bg: #172a26;

    --club-1-brand: #e6b163;
    --club-1-on-brand: #1a1206;
    --club-1-surface: #1d1710;
    --club-1-page: #161009;
    --club-1-ink: #f3eee6;
    --club-1-body: #cbc2b3;
    --club-1-muted: #a89b88;
    --club-1-rule: #7a6b52;
    --club-1-tint: #2e2416;
    --club-1-tone-2: #ecc07d;
    --club-1-tone-2-bg: #2c2315;
    --club-1-tone-3: #d6c77e;
    --club-1-tone-3-bg: #292716;
    --club-1-frame: #150f07;
    --club-1-edge: #3d3320;
    --club-2-brand: #eb9ab4;
    --club-2-on-brand: #1f0a12;
    --club-2-surface: #1d1016;
    --club-2-page: #160b10;
    --club-2-ink: #f5eaee;
    --club-2-body: #cdb9c0;
    --club-2-muted: #a98f99;
    --club-2-rule: #7a5561;
    --club-2-tint: #33202a;
    --club-2-tone-2: #f0a8bf;
    --club-2-tone-2-bg: #311e28;
    --club-2-tone-3: #e8a3ac;
    --club-2-tone-3-bg: #2e1d22;
    --club-2-frame: #150a0f;
    --club-2-edge: #3f2b34;
  }
}

:root[data-theme="dark"] {
  --club-0-brand: #5fbfc4;
  --club-0-on-brand: #0b1413;
  --club-0-surface: #131d1c;
  --club-0-page: #0d1615;
  --club-0-ink: #edf3f2;
  --club-0-body: #bccbc9;
  --club-0-muted: #9aafac;
  --club-0-rule: #5c7a7b;
  --club-0-tint: #1b2e2e;
  --club-0-tone-2: #7ecfd0;
  --club-0-tone-2-bg: #192b2d;
  --club-0-tone-3: #6fbfa4;
  --club-0-tone-3-bg: #172a26;

  --club-1-brand: #e6b163;
  --club-1-on-brand: #1a1206;
  --club-1-surface: #1d1710;
  --club-1-page: #161009;
  --club-1-ink: #f3eee6;
  --club-1-body: #cbc2b3;
  --club-1-muted: #a89b88;
  --club-1-rule: #7a6b52;
  --club-1-tint: #2e2416;
  --club-1-tone-2: #ecc07d;
  --club-1-tone-2-bg: #2c2315;
  --club-1-tone-3: #d6c77e;
  --club-1-tone-3-bg: #292716;
  --club-1-frame: #150f07;
  --club-1-edge: #3d3320;
  --club-2-brand: #eb9ab4;
  --club-2-on-brand: #1f0a12;
  --club-2-surface: #1d1016;
  --club-2-page: #160b10;
  --club-2-ink: #f5eaee;
  --club-2-body: #cdb9c0;
  --club-2-muted: #a98f99;
  --club-2-rule: #7a5561;
  --club-2-tint: #33202a;
  --club-2-tone-2: #f0a8bf;
  --club-2-tone-2-bg: #311e28;
  --club-2-tone-3: #e8a3ac;
  --club-2-tone-3-bg: #2e1d22;
  --club-2-frame: #150a0f;
  --club-2-edge: #3f2b34;
}

/* The device takes the chosen club's colours, all of them.

   Repeated per theme layer for specificity, not for theming: the dark rules
   are written as `:root[data-theme="dark"] .demo` (0,2,1), which outranks a
   plain `.demo[data-demo-club="N"]` (0,2,0) and would put the default club's
   colours back on top. The values stay in one place — the --club-N-* above,
   which is what already switches per theme. */
.demo[data-demo-club="1"],
:root[data-theme="dark"] .demo[data-demo-club="1"] {
  --app-brand: var(--club-1-brand);
  --app-on-brand: var(--club-1-on-brand);
  --app-surface: var(--club-1-surface);
  --app-page: var(--club-1-page);
  --app-ink: var(--club-1-ink);
  --app-body: var(--club-1-body);
  --app-muted: var(--club-1-muted);
  --app-rule: var(--club-1-rule);
  --app-tint: var(--club-1-tint);
  --app-tone-2: var(--club-1-tone-2);
  --app-tone-2-bg: var(--club-1-tone-2-bg);
  --app-tone-3: var(--club-1-tone-3);
  --app-tone-3-bg: var(--club-1-tone-3-bg);
  --phone-frame: var(--club-1-frame);
  --phone-edge: var(--club-1-edge);
}

.demo[data-demo-club="2"],
:root[data-theme="dark"] .demo[data-demo-club="2"] {
  --app-brand: var(--club-2-brand);
  --app-on-brand: var(--club-2-on-brand);
  --app-surface: var(--club-2-surface);
  --app-page: var(--club-2-page);
  --app-ink: var(--club-2-ink);
  --app-body: var(--club-2-body);
  --app-muted: var(--club-2-muted);
  --app-rule: var(--club-2-rule);
  --app-tint: var(--club-2-tint);
  --app-tone-2: var(--club-2-tone-2);
  --app-tone-2-bg: var(--club-2-tone-2-bg);
  --app-tone-3: var(--club-2-tone-3);
  --app-tone-3-bg: var(--club-2-tone-3-bg);
  --phone-frame: var(--club-2-frame);
  --phone-edge: var(--club-2-edge);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .demo[data-demo-club="1"] {
    --app-brand: var(--club-1-brand);
    --app-on-brand: var(--club-1-on-brand);
    --app-surface: var(--club-1-surface);
    --app-page: var(--club-1-page);
    --app-ink: var(--club-1-ink);
    --app-body: var(--club-1-body);
    --app-muted: var(--club-1-muted);
    --app-rule: var(--club-1-rule);
    --app-tint: var(--club-1-tint);
    --app-tone-2: var(--club-1-tone-2);
    --app-tone-2-bg: var(--club-1-tone-2-bg);
    --app-tone-3: var(--club-1-tone-3);
    --app-tone-3-bg: var(--club-1-tone-3-bg);
    --phone-frame: var(--club-1-frame);
    --phone-edge: var(--club-1-edge);
  }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .demo[data-demo-club="2"] {
    --app-brand: var(--club-2-brand);
    --app-on-brand: var(--club-2-on-brand);
    --app-surface: var(--club-2-surface);
    --app-page: var(--club-2-page);
    --app-ink: var(--club-2-ink);
    --app-body: var(--club-2-body);
    --app-muted: var(--club-2-muted);
    --app-rule: var(--club-2-rule);
    --app-tint: var(--club-2-tint);
    --app-tone-2: var(--club-2-tone-2);
    --app-tone-2-bg: var(--club-2-tone-2-bg);
    --app-tone-3: var(--club-2-tone-3);
    --app-tone-3-bg: var(--club-2-tone-3-bg);
    --phone-frame: var(--club-2-frame);
    --phone-edge: var(--club-2-edge);
  }
}

/* --- Device and club switch side by side ------------------------------------
   Below the breakpoint the switch moves above the device: a horizontal row of
   clubs reads as a choice just as well, and the device stays the tallest thing
   on the screen either way. */
.demo__stage {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 18px;
}

/* Device and its call in one column, so the call sits under the phone rather
   than under the stage — it points at the tab bar, not at the club list. */
.demo__column {
  display: flex;
  flex-direction: column;
  align-items: center;
}

@media (min-width: 1080px) {
  .demo__stage {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 26px;
  }
}

.demo__clubs {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  width: 100%;
  max-width: 340px;
}

@media (min-width: 1080px) {
  .demo__clubs {
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: flex-start;
    width: 212px;
  }

  .demo__club {
    justify-content: flex-start;
  }
}

/* The heading is shown in both layouts: once the buttons are marks only, the
   line above them is the only thing saying what they switch. */
.demo__clubs-head {
  margin: 0 0 8px;
  text-align: center;
  /* full row of its own above the marks */
  flex: 0 0 100%;
}

@media (min-width: 1080px) {
  .demo__clubs-head {
    margin-bottom: 4px;
    text-align: left;
  }
}

.demo__clubs-title {
  display: block;
  color: var(--ink);
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.3;
}

/* Below the column layout the long sentence would wrap to three lines above a
   row of three marks. The short form says the same thing.

   Both selectors chain the base class: .demo__clubs-note already sets
   display:block, and a single-class rule would not outrank it. */
.demo__clubs-note.demo__clubs-note--long {
  display: none;
}

@media (min-width: 1080px) {
  .demo__clubs-note.demo__clubs-note--long {
    display: block;
  }

  .demo__clubs-note.demo__clubs-note--short {
    display: none;
  }
}

.demo__clubs-note {
  display: block;
  margin-top: 3px;
  color: var(--faint);
  font-size: 0.8rem;
  line-height: 1.45;
}

/* One club. A radio, so it carries its own state — the ring is the state, the
   colour of the mark is which club it is. */
.demo__club {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  /* the 44 px minimum applies to the whole control, not just the mark */
  min-height: 46px;
  min-width: 46px;
  padding: 7px 11px 7px 8px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  background: var(--surface);
  color: var(--body);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--dur-settle) var(--ease-out),
    background-color var(--dur-settle) var(--ease-out),
    transform var(--dur-settle) var(--ease-spring);
}

.demo__club:hover {
  border-color: var(--blue);
}

.demo__club--active {
  border-color: var(--blue);
  background: var(--note-bg);
}

/* The mark carries the club's own colour, so the tile previews the switch
   before it happens. Both values come from the same --club-N-* pair the device
   reads, which is why the tile follows the theme without a rule of its own. */
.demo__club-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: var(--radius-chip);
  background: var(--club-mark-bg);
  color: var(--club-mark-ink);
  font-size: 0.9rem;
  font-weight: 700;
}

.demo__club[data-demo-club-option="0"] {
  --club-mark-bg: var(--club-0-brand);
  --club-mark-ink: var(--club-0-on-brand);
}

.demo__club[data-demo-club-option="1"] {
  --club-mark-bg: var(--club-1-brand);
  --club-mark-ink: var(--club-1-on-brand);
}

.demo__club[data-demo-club-option="2"] {
  --club-mark-bg: var(--club-2-brand);
  --club-mark-ink: var(--club-2-on-brand);
}

/* Marks only until the column layout: the name lives in aria-label, so the
   control keeps its accessible name while the row stays compact. */
.demo__club-text {
  display: none;
}

@media (min-width: 1080px) {
  .demo__club-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
}

.demo__club-name {
  color: var(--ink);
  font-size: 0.87rem;
  font-weight: 600;
  line-height: 1.25;
}

.demo__club-kind {
  color: var(--faint);
  font-size: 0.76rem;
  line-height: 1.3;
}

/* On narrow screens the club name alone carries the row — the kind would wrap
   the buttons onto three lines. */
@media (max-width: 1079px) {
  .demo__club-kind {
    display: none;
  }
}

.demo__phone {
  /* Feste Breite statt width:100%: das Geraet soll ueberall gleich gross sein,
     unabhaengig davon, wie viel Platz die Spalte hergibt. Genau das war der
     Fehler der frueheren Loesung.

     272 statt der frueheren 300 px: bei 300 ragte die Demo unter 780 px
     Fensterhoehe aus dem Bild — auf einem 13"-Laptop (1440x720) fehlten 61 px
     und der Aufruf-Knopf war nicht sichtbar. Ausserdem ueberragte das Geraet
     die Textspalte daneben um 66 px, der Hero endete rechts tiefer als
     links. */
  width: 272px;
  max-width: 100%;
  padding: 9px;
  /* Der Radius folgt der Breite (272/300 = 0.907), sonst liest die Kante auf
     dem kleineren Rahmen dicker. */
  border-radius: 36px;
  background: var(--phone-frame);
  box-shadow:
    inset 0 0 0 1px var(--phone-edge),
    0 24px 60px -18px rgba(20, 33, 31, 0.5),
    0 4px 14px rgba(20, 33, 31, 0.16);
}

/* Auf sehr schmalen Geraeten darf der Rahmen schrumpfen, sonst laeuft er aus
   der Spalte. 300 px plus Polsterung passen erst ab etwa 360 px Viewport. */
/* Unter 320 px Viewport bleibt nach den Raendern der Seite zu wenig fuer
   272 px — dort darf der Rahmen mitgehen. Bei 300 px Breite griff diese Regel
   noch bis 359 px; das ist mit dem kleineren Geraet nicht mehr noetig. */
@media (max-width: 329px) {
  .demo__phone {
    width: 100%;
    padding: 7px;
    border-radius: 31px;
  }
}

.demo__screen {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Feste Hoehe statt aspect-ratio: der Rahmen muss auch dann gleich hoch
     bleiben, wenn ein Screen wenig Inhalt hat. 9:18.5 auf 254 px Innenbreite
     (272 minus 2x9 Polsterung) ergibt 522 px — der Wert ist die Proportion,
     nur ausgerechnet. */
  height: 522px;
  border-radius: 29px;
  overflow: hidden;
  background: var(--app-page);
  font-size: 11px;
  line-height: 1.4;
  color: var(--app-body);
  /* Die Demo ist zum Ansehen und Antippen, nicht zum Markieren. */
  user-select: none;
}

@media (max-width: 329px) {
  .demo__screen {
    height: 486px;
    border-radius: 25px;
  }
}

/* --- Der scrollende Bereich -------------------------------------------------
   Nimmt den Platz zwischen Kopfzeile und Registerleiste ein. Laengere Screens
   scrollen darin, kuerzere lassen unten Luft — wie auf einem echten Handy. */
.demo__view {
  position: relative;
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  /* Der Bildlauf soll im Geraet enden und nicht die Seite mitnehmen. */
  overscroll-behavior: contain;
  /* Traegheit auf Touch-Geraeten, damit sich das Wischen wie in einer App
     anfuehlt. */
  -webkit-overflow-scrolling: touch;
  /* Duenne Leiste, in Markenfarbe — die Vorgabe des Systems ist auf 282 px
     Breite zu breit und faellt als Fremdkoerper auf. */
  scrollbar-width: thin;
  scrollbar-color: var(--app-rule) transparent;
}

.demo__view::-webkit-scrollbar {
  width: 4px;
}

.demo__view::-webkit-scrollbar-thumb {
  background: var(--app-rule);
  border-radius: 999px;
}

.demo__pane[hidden] {
  display: none;
}

/* Der Wechsel blendet auf, ohne den Weg zu gehen: das Geraet steht, nur sein
   Inhalt wechselt. */
.js .demo__pane {
  animation: demo-pane-in var(--dur-settle) var(--ease-out);
}

@keyframes demo-pane-in {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --- Die Registerleiste ----------------------------------------------------
   Echte Knoepfe. Sie sieht aus wie .preview__tabs und verhaelt sich wie eine
   Bedienleiste: Hover, Fokus, gedrueckter Zustand. */
.demo__tabs {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  flex: 0 0 auto;
  /* 72 px in der App, auf den Massstab der Vorschau gebracht. */
  padding: 6px 4px 14px;
  background: var(--app-surface);
  border-top: 1px solid var(--app-rule);
}

.demo__tab {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  /* 44 px Trefferflaeche — die Leiste ist im Geraetemassstab gesetzt, die
     Flaeche zum Antippen muss trotzdem echt sein. */
  min-height: 44px;
  padding: 5px 2px;
  background: transparent;
  border: 0;
  border-radius: 10px;
  color: var(--app-muted);
  font-family: inherit;
  font-size: 8.5px;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition:
    background-color var(--dur-quick) var(--ease-out),
    color var(--dur-quick) var(--ease-out);
}

.demo__tab svg {
  width: 17px;
  height: 17px;
  /* Das Symbol traegt die Farbe des Knopfes, nicht seine eigene. */
  color: inherit;
}

.demo__tab:hover {
  background: var(--app-tint);
  color: var(--app-brand);
}

/* Der Fokusring sitzt innen: aussen wuerde er am Rahmen abgeschnitten. */
.demo__tab:focus-visible {
  outline: 2px solid var(--app-brand);
  outline-offset: -2px;
}

.demo__tab--active {
  color: var(--app-brand);
}

/* Der gewaehlte Zustand traegt zusaetzlich einen Punkt ueber dem Symbol —
   Farbe allein waere fuer Menschen mit Farbsehschwaeche kein Unterschied. */
.demo__tab--active::before {
  content: "";
  position: absolute;
  top: 2px;
  width: 14px;
  height: 2.5px;
  background: var(--app-brand);
  border-radius: 999px;
}

/* --- Die Detailleiste ------------------------------------------------------
   Chat und Teamdetail tragen sie unterhalb der Kopfzeile. Sie steht in der
   Huelle, weil sie ueber dem scrollenden Bereich liegt. */
.demo__screen > .preview__detailbar {
  position: static;
  flex: 0 0 auto;
}

.demo__screen > .preview__detailbar[hidden] {
  display: none;
}

/* Der Body braucht im Geraet keinen eigenen Abstand nach oben mehr: die
   Detailleiste steht jetzt als Geschwister darueber, nicht ueberlappend. */
.demo .preview__body--detail {
  padding-top: 10px;
}

/* --- Knopf zum Anlegen (FAB) ------------------------------------------------
   In der App MOBILE_FAB_CLASS: `fixed bottom-[calc(88px+safe-area)] right-4`
   — er haengt am Ausschnitt, nicht am Inhalt.

   Genau das war der Fehler vorher: der Knopf stand als sticky-Element IM
   scrollenden Bereich und wanderte deshalb mit der Hoehe des Inhalts. In
   Neuigkeiten (zwei hohe Karten) und Terminen (zwei niedrigere) sass er an
   verschiedenen Stellen. Jetzt sitzt er am Rahmen und damit auf jedem Screen
   gleich — er wird nur ein- und ausgeblendet.

   Der Wert: die Registerleiste ist 65 px hoch (gemessen). Der Knopf sitzt
   vollstaendig darueber, mit 8 px Luft zur Leiste — vorher ragte er 11 px in
   sie hinein und lag damit auf den Registern. */
.demo__fab {
  position: absolute;
  right: 12px;
  bottom: 73px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: var(--app-brand);
  color: var(--app-on-brand);
  box-shadow: 0 6px 14px -4px rgba(16, 24, 40, 0.35);
}

.demo__fab svg {
  width: 15px;
  height: 15px;
}

/* Ein <span> hat kein eingebautes display:none, das [hidden] wieder
   herstellen koennte — die Klassenregel setzt flex, also muss das Verstecken
   ausdruecklich dazu. Dieselbe Falle wie frueher bei der Eingabeleiste. */
.demo__fab[hidden] {
  display: none;
}


/* --- Abschlussband ---------------------------------------------------------
   Der letzte Aufruf bekommt eigenes Gewicht: Verlauf, Schein und eine
   Glaskarte, in der Anspruch und Knoepfe stehen. Bisher sah er aus wie jede
   andere Sektion. */
.closing {
  position: relative;
  background:
    radial-gradient(70% 90% at 20% 0%, var(--glow-brand) 0%, transparent 60%),
    radial-gradient(60% 80% at 85% 100%, var(--glow-accent) 0%, transparent 58%),
    var(--tint);
  overflow: hidden;
}

.closing::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--gradient-line);
}

/* Der Anspruch bekommt im Panel mehr Zeilenbreite als die frueheren 30ch:
   auf der freien Flaeche lief der Satz zweizeilig, im 760-px-Panel liesse
   30ch ein einzelnes Wort in der dritten Zeile stehen. 34ch bricht ihn
   wieder in zwei. */
.closing__panel .closing__claim {
  max-width: 34ch;
}

.closing__panel {
  position: relative;
  max-width: 760px;
  margin: 0 auto;
  padding: 46px 32px 44px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-panel);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-lift);
}

@media (min-width: 768px) {
  .closing__panel {
    padding: 56px 48px 52px;
  }
}

/* ==========================================================================
   17. Motion — one subtle fade-in, disabled for reduced motion
   ========================================================================== */

/* Only applied once main.js confirms JS is running, so no-JS keeps content visible. */
.js .reveal {
  opacity: 0;
  transform: translateY(var(--enter-shift));
  transition:
    opacity var(--dur-enter) var(--ease-out),
    transform var(--dur-enter) var(--ease-out);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* --- Gestaffelter Auftritt ---------------------------------------------------
   Kinder eines .reveal-stagger erscheinen kurz nacheinander statt gleichzeitig.
   Das Auge folgt der Reihe, statt einen Block aufblitzen zu sehen.

   Die Verzögerung steckt in einer Custom Property, die main.js je Kind setzt —
   so bleibt die Staffelung an die tatsächliche Anzahl gebunden und braucht
   keine nth-child-Kette im Stylesheet. */
.js .reveal-stagger > * {
  opacity: 0;
  transform: translateY(var(--enter-shift));
  transition:
    opacity var(--dur-enter) var(--ease-out),
    transform var(--dur-enter) var(--ease-out);
  transition-delay: var(--stagger-delay, 0ms);
}

.js .reveal-stagger.is-visible > * {
  opacity: 1;
  transform: none;
}

/* --- Hero-Auftritt -----------------------------------------------------------
   Der Hero wartet nicht auf den Scroll: er ist beim Laden schon im Blick. Die
   Reihenfolge folgt der Leserichtung — Kicker, Überschrift, Text, Buttons,
   dann das Bild und die Vertrauensleiste. */
.js .hero__enter > * {
  opacity: 0;
  transform: translateY(var(--enter-shift));
  animation: hero-enter var(--dur-enter) var(--ease-out) forwards;
  animation-delay: var(--stagger-delay, 0ms);
}

@keyframes hero-enter {
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  /* Alles, was normalerweise eingeblendet wird, steht sofort sichtbar da.
     Ohne diese Regeln bliebe opacity:0 stehen, sobald die globale
     animation-duration-Bremse oben greift — der Inhalt wäre unsichtbar. */
  .js .reveal,
  .js .reveal-stagger > *,
  .js .hero__enter > * {
    opacity: 1;
    transform: none;
    transition: none;
    animation: none;
  }

  /* Signature-Interaktionen bleiben als Farbwechsel erhalten, nur ohne Weg. */
  .js .card:hover,
  .js .plan:hover,
  .js .metric:hover,
  .js .quote:hover,
  .js .mod:hover {
    transform: none;
  }

  /* Das Icon in der Karte bleibt stehen, die Faerbung bleibt. */
  .js .card:hover .chip,
  .js .mod:hover .mod__chip {
    transform: none;
  }

  /* Der Lichtstreifen auf dem Hauptknopf ist reine Bewegung — ohne Weg hat er
     keinen Inhalt und faellt deshalb ganz weg, statt aufzublitzen. */
  .button--primary::after {
    display: none;
  }

  /* Der Screenwechsel darf nicht aufblenden; der neue Screen steht sofort. */
  .js .demo__pane {
    animation: none;
  }
}

/* ==========================================================================
   18. Print
   ========================================================================== */

@media print {
  .header,
  .nav-toggle,
  .closing .actions,
  .hero .actions {
    display: none;
  }

  body {
    color: #000000;
    background: #ffffff;
  }

  /* Verlaeufe und Glas drucken als graue Flaechen. Im Druck traegt die Kante
     die Trennung, wie im dunklen Theme. */
  .band,
  .closing,
  .hero {
    background: #ffffff !important;
  }

  .band::before,
  .band::after,
  .closing::before,
  .hero::before,
  .hero__figure::before,
  .card::before,
  .mod::before {
    display: none;
  }

  /* Die Registerleiste der Demo ist Bedienung, kein Inhalt: gedruckt bleibt
     der Screen stehen, auf dem die Demo geladen wurde. */
  .demo__tabs {
    display: none !important;
  }

  /* Im Druck darf das Geraet nicht auf 580 px beschnitten werden — was nicht
     ins Fenster passte, waere sonst weg. Der Rahmen waechst mit dem Inhalt. */
  .demo__screen {
    height: auto;
  }

  .demo__view {
    overflow: visible;
  }

  .metric,
  .quote,
  .closing__panel,
  .card,
  .plan,
  .mod,
  .mod-list__item,
  .plan-extra,
  .incl__item {
    box-shadow: none;
  }

  /* Die Abschnittsliste bleibt im Druck stehen: ein Inhaltsverzeichnis ist auf
     Papier so nuetzlich wie am Schirm — nur die Sprungmarken sind dort tot,
     die Nummern fuehren aber weiter zum richtigen Abschnitt. */
  .legal-toc {
    margin-top: 20px;
  }

  /* Der Umschalter ist Bedienung, kein Inhalt. Im Druck steht der Jahrespreis
     (das sichtbare Pane) — die monatliche Zahlung nennt der Text unter den
     Karten. */
  .plan-toggle {
    display: none !important;
  }

  /* Das Panel der zweiten Ebene traegt im Druck keine eigene Flaeche: getont
     gedruckt wuerde es als grauer Kasten erscheinen, der die Liste darin
     schlechter lesbar macht. */
  .modules__more {
    background: #ffffff !important;
  }

  /* The mobile drawer is closed markup, not print content. */
  .nav-drawer {
    display: none;
  }
}

/* --- 16c. Weitere Screens der Vorschau -------------------------------------
   Neuigkeiten, Termine, Postfach, Teams und Dokumente. Huelle und Tokens
   teilen sie sich mit dem Dashboard; hier steht nur, was ihren Inhalt
   ausmacht. Groessen aus den echten Komponenten uebernommen und mit
   demselben Faktor (~0,62) verkleinert wie beim Dashboard. */

/* Kennzeichnung: Team-Beitraege tragen die Akzentfarbe, vereinsweite den
   neutralen Grund — genau wie NewsCard und EventCard es unterscheiden. */
.preview__tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  border-radius: 5px;
  font-size: 7.5px;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
}

.preview__tag--team {
  background: var(--app-tone-2-bg);
  color: var(--app-tone-2);
}

.preview__tag--general {
  background: var(--app-tint);
  color: var(--app-muted);
}

/* Teamleiter- und Administrationschats. chatTypeBadgeClass() gibt ihnen in
   der App Amber und Rot; hier tragen sie die vorhandenen Markentoene, damit
   das Geraet nicht zwei Fremdfarben in einer 22 px hohen Zeile fuehrt. Sie
   bleiben unterscheidbar, weil das Abzeichen seinen Namen mitfuehrt. */
.preview__tag--lead {
  background: var(--app-tone-3-bg);
  color: var(--app-tone-3);
}

.preview__tag--admin {
  background: var(--app-tint);
  color: var(--app-brand);
}

/* Offene Rueckmeldung: in der App ein warnender Ton. Im Branding waere Gelb
   ein Fremdkoerper, deshalb der dritte Markenton — die Kachel faellt durch
   Fuellung auf, nicht durch eine fremde Farbe. */
.preview__tag--open {
  background: var(--app-tone-3-bg);
  color: var(--app-tone-3);
}

/* --- Neuigkeiten ---------------------------------------------------------- */

.preview__post--new {
  border-color: var(--app-brand);
}

.preview__posthead {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
  min-width: 0;
}

.preview__avatar--xs {
  width: 15px;
  height: 15px;
  margin-left: auto;
  flex: 0 0 auto;
  font-size: 7px;
  border-radius: 5px;
}

.preview__posttitle {
  display: block;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--app-ink);
}

.preview__posttext {
  margin: 4px 0 0;
  font-size: 8.5px;
  line-height: 1.45;
  color: var(--app-muted);
  /* Wie im Original: zwei Zeilen, dann abgeschnitten. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.preview__postfoot {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 8px;
}

.preview__react {
  padding: 2px 6px;
  border: 1px solid var(--app-rule);
  border-radius: 20px;
  font-size: 8px;
  font-weight: 600;
  color: var(--app-body);
}

.preview__postdate {
  margin-left: auto;
  font-size: 7.5px;
}

/* --- Termine -------------------------------------------------------------- */

.preview__event--pending {
  border-color: var(--app-brand);
}

.preview__eventtop {
  display: flex;
  gap: 8px;
  min-width: 0;
}

.preview__date--lg {
  width: 34px;
  height: 38px;
}

.preview__eventmain {
  min-width: 0;
  flex: 1;
}

.preview__meta {
  display: flex;
  align-items: center;
  gap: 3px;
  margin-top: 3px;
  font-size: 8px;
  color: var(--app-muted);
}

.preview__meta svg {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  color: var(--app-muted);
}

/* Das zweite Icon-Paar bekommt Luft, damit Zeit und Ort nicht verkleben. */
.preview__meta svg + svg,
.preview__meta svg:nth-of-type(2) {
  margin-left: 5px;
}

/* Die drei Antwortknoepfe. In der App volle Breite nebeneinander. */
.preview__respond {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
  margin-top: 9px;
}

.preview__resp {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 5px 2px;
  border: 1px solid var(--app-rule);
  border-radius: 6px;
  font-size: 8px;
  font-weight: 600;
  color: var(--app-muted);
}

.preview__resp svg {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
}

/* Der gewaehlte Knopf: Fuellung und kraeftige Kante. Die App faerbt hier
   gruen, amber und rot (responseConfig in EventCard.tsx) — auf 300 px
   Rahmenbreite waeren das drei Fremdfarben, und Rot gegen Gruen ist fuer
   Menschen mit Rotgruenschwaeche kein Unterschied. Symbol und Beschriftung
   tragen die Aussage; die Fuellung sagt nur, dass geantwortet wurde. */
.preview__resp--yes {
  border-color: var(--app-brand);
  background: var(--app-tint);
  color: var(--app-brand);
}

.preview__resp--maybe {
  border-color: var(--app-tone-3);
  background: var(--app-tone-3-bg);
  color: var(--app-tone-3);
}

/* Die Absage bleibt farblich zurueckhaltend: sie soll erkennbar sein, aber
   nicht als Warnung ins Auge fallen. */
.preview__resp--no {
  border-color: var(--app-muted);
  background: var(--app-page);
  color: var(--app-body);
}

.preview__counts {
  display: block;
  margin-top: 6px;
  font-size: 7.5px;
}

/* --- Postfach und Teams: Listen ------------------------------------------- */

/* Listenkarten tragen ihre Trennlinien innen, deshalb kein eigenes Polster. */
.preview__list {
  padding: 0;
  overflow: hidden;
}

.preview__file {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 8px 9px;
  min-width: 0;
}

.preview__file + .preview__file {
  border-top: 1px solid var(--app-rule);
}


.preview__teammark--lg {
  width: 22px;
  height: 22px;
  font-size: 9px;
  border-radius: 7px;
}







/* --- Teams: Mitgliederraster ---------------------------------------------- */


.preview__members {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  min-width: 0;
}

.preview__member {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px;
  text-align: center;
  min-width: 0;
}

.preview__membername {
  width: 100%;
  font-size: 8.5px;
  font-weight: 700;
  color: var(--app-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- Dokumente ------------------------------------------------------------ */

/* Das Dateikuerzel ersetzt das farbige Formatsymbol der App. Ein Kuerzel
   traegt bei dieser Groesse besser als ein Piktogramm. */
.preview__ext {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 26px;
  flex: 0 0 auto;
  border-radius: 5px;
  background: var(--app-tint);
  color: var(--app-brand);
  font-size: 6.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.preview__ext--doc {
  background: var(--app-tone-2-bg);
  color: var(--app-tone-2);
}

.preview__filemain {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}

.preview__filename {
  font-size: 9.5px;
  font-weight: 700;
  color: var(--app-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.preview__file .preview__sub {
  font-size: 7.5px;
}

.preview__dl {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  border-radius: 6px;
  background: var(--app-tint);
  color: var(--app-brand);
}

.preview__dl svg {
  width: 11px;
  height: 11px;
}

/* --- 16d. Angleichung an die echten Screens --------------------------------
   Die Werte stammen aus NewsCard, EventCard, MessageBubble, ChatHeader,
   TeamDetail und MemberCard, heruntergerechnet auf den Massstab der
   Vorschau (~0,62). */

/* Kennzeichnung "Neu": in der App die gefuellte Markenfarbe. */
.preview__tag--new {
  background: var(--app-brand);
  color: var(--app-on-brand);
}

/* --- Neuigkeiten ---------------------------------------------------------- */

/* "Mehr lesen" steht in der App unter dem gekuerzten Text, in Markenfarbe. */
.preview__more {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-top: 5px;
  color: var(--app-brand);
  font-size: 8px;
  font-weight: 700;
}

.preview__more svg {
  width: 8px;
  height: 8px;
}

/* Die Fusszeile sitzt hinter einer Trennlinie — genau wie im Original. */
.preview__postfoot {
  margin-top: 9px;
  padding-top: 8px;
  border-top: 1px solid var(--app-rule);
}

.preview__react {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border: 1px solid var(--app-rule);
  border-radius: 20px;
  background: var(--app-tint);
  font-size: 8px;
  font-weight: 700;
  color: var(--app-ink);
}

/* Eigene Reaktion: Rahmen und Flaeche in der Markenfarbe. */
.preview__react--mine {
  border-color: var(--app-brand);
  background: var(--app-tint);
  color: var(--app-brand);
}

.preview__reactemoji {
  font-size: 9px;
  line-height: 1;
}

.preview__postmeta {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}

.preview__posttime {
  font-size: 7.5px;
  color: var(--app-muted);
}

.preview__smile {
  width: 11px;
  height: 11px;
  color: var(--app-muted);
}

/* --- Termine -------------------------------------------------------------- */

/* Zeit und Ort stehen nebeneinander mit deutlichem Abstand, nicht verkettet. */
.preview__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 10px;
  margin-top: 4px;
}

.preview__metaitem {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 8px;
  font-weight: 500;
  color: var(--app-muted);
}

.preview__metaitem svg {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  opacity: 0.7;
}

/* Teilnehmerreihe: uebereinandergelegte Kuerzel, dann die Zahlen. */
.preview__participants {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 5px;
  margin-top: 6px;
  min-width: 0;
}

.preview__stack {
  display: flex;
  flex: 0 0 auto;
  /* Der Ring des ersten Kuerzels braucht Platz nach links. */
  padding-left: 2px;
}

.preview__avatar--stack {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  font-size: 6px;
  letter-spacing: 0;
  border-radius: 6px;
  /* Der Ring liegt nach innen (inset), damit er am linken Rand nicht
     abgeschnitten wird — ein aeusserer Schatten braeuchte Platz, den die
     Zeile nicht hat. */
  box-shadow: inset 0 0 0 1.5px var(--app-surface);
}

.preview__avatar--stack + .preview__avatar--stack {
  margin-left: -4px;
}

.preview__partcount {
  font-size: 7.5px;
  color: var(--app-muted);
}

/* Zusagen tragen in der App den bestaetigenden Ton — hier der dritte
   Markenton, damit kein fremdes Gruen ins Branding kommt. */
.preview__partyes {
  color: var(--app-tone-3);
  font-weight: 700;
}

.preview__respond {
  margin-top: 10px;
}

/* --- Postfach: Chatliste ---------------------------------------------------
   Nach ChatList.tsx und ChatListItem.tsx: Suchfeld oben, darunter die Chats
   mit Wappen, Name, Typ-Abzeichen, Zeit, Vorschau der letzten Nachricht und
   dem Zaehler fuer Ungelesenes.

   Ersetzt den frueheren Chatverlauf. Der war ein Screen zu weit: die
   Registerleiste fuehrt in der App auf die Liste, das Gespraech erreicht man
   erst durch Antippen. Damit fielen Blasen, Tagestrenner und Eingabeleiste
   weg — rund 200 Zeilen, die nichts mehr gestylt haetten.
   ========================================================================= */

.preview__chatlist {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Das Suchfeld. In der App ein echtes <input> in einer 72 px hohen Zeile;
   hier eine Attrappe im Massstab der Vorschau. */
.preview__search {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 4px;
  padding: 5px 8px;
  border: 1px solid var(--app-rule);
  border-radius: 7px;
  background: var(--app-surface);
  color: var(--app-muted);
  font-size: 8px;
}

.preview__search svg {
  width: 10px;
  height: 10px;
  flex: 0 0 auto;
}

/* Eine Zeile der Liste. In der App durch eine Trennlinie geteilt, nicht als
   Karte — die Liste soll als ein Block wirken, nicht als Stapel. */
.preview__chat {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  /* `py-3.5` (14 px) im Massstab der Vorschau: der Rahmen ist innen 282 px
     breit gegen 390 px auf einem Geraet, das Verhaeltnis rund 0.72. */
  padding: 10px 2px;
  border-bottom: 1px solid var(--app-rule);
}

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

/* `h-11 w-11` (44 px) im selben Massstab. Das Wappen traegt die Zeile — mit
   22 px wirkte die Liste gedraengt und liess unten 144 px frei. */
.preview__teammark--chat {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  margin-top: 1px;
  border-radius: 9px;
  font-size: 13px;
}

.preview__chatmain {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.preview__chattop {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

/* Der Name darf kuerzen, Abzeichen und Zeit nicht: sie sind kurz und tragen
   den Zusammenhang. */
.preview__chatname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--app-ink);
  font-size: 10.5px;
  font-weight: 700;
}

.preview__chattop .preview__tag {
  flex: 0 0 auto;
  padding: 1px 4px;
  font-size: 6.5px;
}

.preview__chattime {
  flex: 0 0 auto;
  /* Nach rechts geschoben, wie `ml-auto` in ChatListItem. */
  margin-left: auto;
  color: var(--app-muted);
  font-size: 7px;
}

.preview__chatbottom {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

.preview__chatpreview {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--app-muted);
  font-size: 9.5px;
}

/* Ungelesene Chats tragen die Vorschau kraeftiger — der Zaehler allein waere
   zu leicht zu uebersehen. */
.preview__chat--unread .preview__chatpreview {
  color: var(--app-body);
  font-weight: 500;
}

.preview__unread {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 13px;
  height: 13px;
  padding-inline: 3px;
  border-radius: 999px;
  background: var(--app-brand);
  color: var(--app-on-brand);
  font-size: 7px;
  font-weight: 700;
  line-height: 1;
}

/* --- Kopfzeile von Chat und Teamdetail ------------------------------------ */


.preview__detailtext {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.preview__detailname {
  font-size: 10px;
  font-weight: 700;
  color: var(--app-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.preview__detailsub {
  font-size: 7.5px;
  color: var(--app-muted);
}

/* --- Teams: Mitgliederraster ---------------------------------------------- */

/* Auf dem Telefon zeigt die App zwei Spalten, nicht drei. */
.preview__members {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
  min-width: 0;
}

.preview__avatar--md {
  width: 26px;
  height: 26px;
  font-size: 9.5px;
  border-radius: 9px;
}

.preview__member {
  gap: 5px;
  padding: 10px 6px;
}

.preview__membername {
  font-size: 9px;
}

/* --- 16e. Huelle wie im echten AppShell ------------------------------------
   Belege: AppShell.tsx (TopBar immer, Registerleiste im Chat ausgeblendet),
   TopBar.tsx (immer Verein, nie Bereich), ChatHeader.tsx und TeamDetail.tsx
   (eigene 72-px-Leiste innerhalb der Seite), ChatInputBar.tsx (schwebend),
   NewsPage/EventsPage (kein Titel, Filterleiste rechts, FAB). */

/* Detailleiste von Chat und Teamdetail — sitzt UNTER der TopBar, innerhalb
   der Seite. In der App 72 px hoch, hier auf den Massstab gebracht. */
.preview__detailbar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 7px 12px;
  background: var(--app-surface);
  border-bottom: 1px solid var(--app-rule);
}

.preview__backicon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  color: var(--app-muted);
}

.preview__backicon svg {
  width: 13px;
  height: 13px;
}

.preview__detailtext {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.preview__detailname {
  font-size: 10px;
  font-weight: 600;
  color: var(--app-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.preview__detailsub {
  font-size: 7.5px;
  color: var(--app-muted);
}

/* Die Detailseiten fuellen die Flaeche selbst, ohne aeusseres Polster. */
.preview__body--detail {
  padding-top: 8px;
}

/* Unter dem FAB bleibt so viel Platz, dass die letzte Karte nicht unter ihm
   endet. Frueher waren es 42 px — fuer einen Knopf, der im scrollenden
   Bereich sass und mitwanderte. Er haengt jetzt am Rahmen und ist 30 px hoch
   bei 54 px Abstand zum Boden; ueber der Registerleiste beginnt er also erst
   weit unten. 20 px genuegen, damit nichts direkt an ihn stoesst. */
.preview__body--fab {
  padding-bottom: 20px;
}

/* --- Filterleiste der Listenseiten ---------------------------------------- */

/* In der App rechtsbuendig ueber der Liste, ohne Titel daneben. */
.preview__filterrow {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 2px;
}

.preview__segment {
  display: flex;
  gap: 1.5px;
  padding: 1.5px;
  border-radius: 6px;
  background: var(--app-tint);
}

.preview__seg {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 16px;
  border-radius: 5px;
  color: var(--app-muted);
}

.preview__seg svg {
  width: 10px;
  height: 10px;
}

/* Der gewaehlte Eintrag hebt sich durch eine eigene Flaeche ab. */
.preview__seg--active {
  background: var(--app-surface);
  color: var(--app-ink);
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08);
}
