:root {
  --c-primary: #586D82;
  --c-primary-deep: #46586a;
  --c-text: #22272e;
  --c-text-soft: #5a6472;
  /* Warmer Papier-Grund statt kuehlem Grau. Die Referenzseite der Spec
     (mauritiuschor-trimbach.ch, §4) traegt ihren "spannend, aber serioes"-
     Eindruck aus zwei Teilen: Serife fuer Ueberschriften — und diesem
     Grund. Der erste Teil steht seit F3; der zweite fehlte. --c-bg ist
     der Wert der Referenz; --c-bg-alt ist sein warmer Gegenwert im selben
     Helligkeitsabstand wie vorher (Leuchtdichte-Schritt 0.088 gegen 0.081).
     Kontrast gegen --c-text-soft bleibt ueber 5:1. */
  --c-bg: #faf7f3;
  --c-bg-alt: #f2ece5;
  --c-accent-red: #B2302F;
  --c-accent-green: #2F8151;
  --c-accent-yellow: #F3C432;
  --radius: 14px;
  --shadow: 0 6px 30px rgba(34, 39, 46, 0.08);
  --content-max: 1100px;
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", sans-serif;
  --font-headings: "Iowan Old Style", "Palatino Linotype", Palatino,
    "Book Antiqua", Georgia, "Times New Roman", serif;
  /* ZWEI Schriftrollen, nicht drei (#336, Schritt 7). Schritt 6 hatte eine
     Dickengleiche als dritte Rolle eingefuehrt, ausschliesslich fuer die
     Nachweis-Zeile: "die Serife kuendigt an, die Grotesk spricht, die
     Dickengleiche zeigt die Arbeit". Mit der Abnahme sind vier der acht
     Nachweis-Zeilen gestrichen worden (Nutzerentscheid, siehe die
     B6-Abweichung im Brief). Eine eigene Schrift fuer vier Zeilen ist keine
     Rolle mehr, sondern ein Fremdkoerper — und genau so ist sie in der
     Abnahme auch gelesen worden ("unterschiedliche Fonts?"). Der Nachweis
     bleibt erkennbar: kleiner Grad, farbiger Zustandspunkt, eigene Linie
     darueber. */

  /* Schriftgrade: fuenf Stufen fuer Text, drei fuer Ueberschriften.
     Vorher standen 13 verschiedene Werte in dieser Datei, zehn davon
     zwischen 0.78 und 1.25rem — Unterschiede von 0.3 bis 1px, die niemand
     als Absicht liest, sondern als Unruhe. Genau das war der Abnahme-Befund
     "unterschiedliche Schriftgroessen".

     Nebenbefund aus derselben Messung: .section-lead stand auf 1.1rem und
     .prose auf 1.08rem. Der Vorspann war also 0.3px groesser als der Text,
     den er einleiten soll — als Stufe existierte er nicht. Er nimmt jetzt
     dieselbe Stufe wie der Hero-Vorspann. */
  --fs-1: 0.8rem;
  --fs-2: 0.9rem;
  --fs-3: 1rem;
  --fs-4: 1.1rem;
  /* Die Vorspann-Stufe muss AUF JEDER BREITE ueber --fs-4 liegen, sonst ist
     sie keine Stufe. Die alte Untergrenze war 1.05rem und lag damit auf dem
     Telefon unter dem Fliesstext (1.1rem) — der Vorspann wurde dort kleiner
     als der Text, den er einleitet. */
  --fs-5: clamp(1.15rem, 2.2vw, 1.3rem);
  --fs-h3: 1.2rem;
  --fs-h2: clamp(1.5rem, 3.6vw, 2.1rem);
  --fs-h1: clamp(1.9rem, 5.2vw, 3rem);
  /* Textseiten tragen eine kleinere H1 als die Startseite: dort ist die
     Ueberschrift ein Titel, hier ein Schild. */
  --fs-h1-text: clamp(1.7rem, 4.4vw, 2.4rem);

  /* Breitenskala (#335, F4): vier Stufen statt vier Zufallswerten
     (46/60/62/70ch). Gemessen auf 1440px vor der Aenderung: .hero-lead
     510px, .section-lead 664px, .prose 675px, .textpage .wrap 705px —
     nur der Hero-Vorspann lag unter der Untergrenze (550px, die Haelfte
     von --content-max). Die Variation bleibt gewollt, nur die Untergrenze
     steigt. Nachgemessen bei 1440px nach dem Verbreitern der Hero-Spalte
     (Abnahme #335): .hero-lead 674px, .section-lead 687px, .prose 740px —
     die Stufen liegen jetzt auch in Pixeln in der Reihenfolge der Skala.
     Vorher war der Hero-Vorspann von der schmaleren Spalte abgeschnitten
     und erreichte seine 54ch gar nicht. */
  --measure-1: 54ch;
  --measure-2: 62ch;
  --measure-3: 68ch;
  --measure-4: 75ch;
  /* Abgeleitet: Kartenrand. Kein eigenes Token im Design-Vertrag —
     zieht deshalb mit dem Grund mit, sonst laege eine kuehle Haarlinie
     auf warmem Papier. */
  --c-border: #e7e0d7;

  /* Bewegung: zwei Kurven, zwei Dauern. --ease-standard gilt für reine
     Farbwechsel (dort ist die eingebaute Kurve regelkonform),
     --ease-out für alles, was eintritt oder sich bewegt. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-standard: ease;
  --dur-fast: 150ms;
  /* 250ms, nicht 350: die 300ms-Grenze fuer UI-Bewegung gilt auch hier,
     und der Reveal feuert 11x pro Durchscrollen — bei der Frequenz zieht
     "reduzieren" (review-animations, #335). */
  --dur-reveal: 250ms;

  /* Abstände: acht Stufen. Die Einzelwerte davor standen ohne System
     nebeneinander (0.35 · 0.4 · 0.5 · 0.6 · 0.75 · 0.8 · 0.85 · 0.9 · 1 …). */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.5rem;
  --space-8: 3.5rem;
  --space-band: clamp(3.25rem, 8vw, 5.5rem);
}

/* ── Reset / Basis ──────────────────────────────────────────────── */

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

html {
  /* Anker-Sprünge dürfen nicht unter dem Sticky-Header landen. Der Wert folgt
     der gemessenen Kopfhoehe, nicht einem Gefuehl: 75px ab 780px, 57px
     darunter (kleinere Wortmarke, kleinerer Schriftgrad) — jeweils plus etwa
     15px Luft. Ein fester Wert von 5.5rem liess den Anker unter 780px 31px zu
     tief landen (gemessen #336, Schritt 6, an 320/360/390/430/500/779px). */
  scroll-padding-top: 5.5rem;
}

@media (max-width: 779px) {
  html {
    scroll-padding-top: 4.5rem;
  }
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-3);
  line-height: 1.65;
  letter-spacing: 0;
  color: var(--c-text);
  background: var(--c-bg);
  -webkit-text-size-adjust: 100%;
}

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

h1,
h2,
h3 {
  font-family: var(--font-headings);
  margin: 0 0 var(--space-3);
}

/* apple-design §15: Laufweite wird mit wachsender Groesse enger, Zeilenhoehe
   sinkt mit ihr. Die Richtwerte aus dem Brief (-0.022em usw.) waren an einer
   Grotesk kalibriert; die gewaehlte Serife ("Iowan Old Style" & Co.) traegt
   dichteres Tracking schlechter — am gerenderten Bild (1440/1024/390px)
   nachgeschaerft auf spuerbar mildere Werte, die die geforderte Ordnung
   trotzdem einhalten. */
h1 {
  font-size: var(--fs-h1);
  letter-spacing: -0.012em;
  line-height: 1.1;
}

h2 {
  font-size: var(--fs-h2);
  letter-spacing: -0.006em;
  line-height: 1.18;
}

h3 {
  font-size: var(--fs-h3);
  letter-spacing: -0.002em;
  line-height: 1.32;
}

/* Sektions-Zeichen (#348, Entscheid 01.09.2026): grosse, leise Wasserzeichen
   in den textfreien Zonen — rechts neben der Textspalte, in den Label-Spalten
   mittig zwischen den ersten zwei Zwischentiteln, beim Ausprobieren im freien
   Feld unter dem Knopf. Absolut mit z-index -1: ueber dem Bandgrund
   (Stapel-Kontext kommt vom .wrap), unter jedem Inhalt — Text bleibt scharf.
   Die Groessen sind bewusst verschieden; die Motive sind es auch.
   Die Farbe steht hier und nicht im Markup (currentColor), damit sie eine
   CSS-Entscheidung bleibt. */
.sektion-icon {
  position: absolute;
  z-index: -1;
  margin: 0;
  color: var(--c-primary);
  opacity: 0.08;
}

#grundsaetze .sektion-icon {
  right: 70px;
  top: 50%;
  transform: translateY(-50%);
  width: 210px;
  height: 210px;
}

#nutzen .sektion-icon {
  right: 70px;
  top: 120px;
  width: 180px;
  height: 180px;
}

#preis .sektion-icon {
  right: 90px;
  top: 50%;
  transform: translateY(-50%);
  width: 150px;
  height: 150px;
}

#kontakt .sektion-icon {
  right: 110px;
  top: 50%;
  transform: translateY(-50%);
  width: 130px;
  height: 130px;
}

#funktionsweise .sektion-icon {
  left: 35px;
  top: 205px;
  width: 150px;
  height: 150px;
}

#vertrauen .sektion-icon {
  left: 25px;
  top: 235px;
  width: 170px;
  height: 170px;
}

#demo .sektion-icon {
  left: 150px;
  bottom: 120px;
  width: 160px;
  height: 160px;
}

/* Label-Spalte (#348, Vorschlag B): In den zwei textdichten Kapiteln werden
   die Zwischentitel ab 980px zur schmalen Randspalte — wer scannt, liest nur
   sie; wer liest, hat denselben Text. Nur direkte Kinder des .wrap: die
   Karten-H3s bleiben unberuehrt. */
@media (min-width: 980px) {
  #funktionsweise .wrap,
  #vertrauen .wrap {
    display: grid;
    grid-template-columns: 220px minmax(0, 68ch);
    column-gap: var(--space-7);
    align-items: start;
  }

  #funktionsweise .wrap > h2,
  #vertrauen .wrap > h2 {
    grid-column: 1 / -1;
  }

  #funktionsweise .wrap > h3,
  #vertrauen .wrap > h3 {
    grid-column: 1;
    margin-top: var(--space-2);
    font-family: var(--font-sans);
    font-size: var(--fs-2);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--c-text-soft);
  }

  #funktionsweise .wrap > p,
  #vertrauen .wrap > p,
  #funktionsweise .wrap > .cards,
  #vertrauen .wrap > .cards {
    grid-column: 2;
  }
}

/* Unter 980px gibt es keine leere Spalte: Das Zeichen steht im Textfluss
   (float), etwas gedeckter, weil es kleiner ist. Der Titel darf darum
   herum umbrechen und bekommt mehr Luft zum Text; der Fliesstext raeumt
   das Zeichen weg (clear) statt es zu umfliessen — Entscheid 01.09.2026. */
@media (max-width: 979px) {
  .sektion-icon,
  #funktionsweise .sektion-icon,
  #vertrauen .sektion-icon,
  #demo .sektion-icon,
  #grundsaetze .sektion-icon,
  #nutzen .sektion-icon,
  #preis .sektion-icon,
  #kontakt .sektion-icon {
    position: static;
    float: right;
    transform: none;
    width: 96px;
    height: 96px;
    margin: 0 0 var(--space-3) var(--space-4);
    opacity: 0.15;
  }

  .band h2 {
    margin-bottom: var(--space-4);
  }

  .band h2 ~ p {
    clear: right;
  }
}

/* Bildunterschrift des Telefons mittig unters Geraet (Entscheid 01.09.2026) */
#demo figcaption {
  text-align: center;
}

p {
  margin: 0 0 var(--space-4);
}

p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--c-primary-deep);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

a:hover {
  color: var(--c-text);
}

/* Fokus immer sichtbar — Messlatte wie beim Widget. */
:focus-visible {
  outline: 3px solid var(--c-primary);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ── Skip-Link ──────────────────────────────────────────────────── */

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -100px;
  z-index: 100;
  padding: var(--space-3) var(--space-4);
  background: var(--c-bg);
  color: var(--c-primary-deep);
  font-weight: 600;
  border-radius: 0 0 var(--radius) var(--radius);
  box-shadow: var(--shadow);
}

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

/* ── Logo ───────────────────────────────────────────────────────
   Eigene, hintergrundfreie Wortmarke. Der frühere CSS-Ausschnitt aus
   der quadratischen Datei ist mit #335 entfallen. */

.logo {
  display: inline-block;
  flex: none;
}

.logo img {
  display: block;
  width: 145px;
  height: auto;
}

/* ── Kopfbereich ────────────────────────────────────────────────── */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* Muss dem Grund folgen: die Kopfleiste liegt sticky ueber dem Inhalt,
     ein weisser Schleier auf warmem Papier faellt beim Scrollen auf. */
  background: rgba(250, 247, 243, 0.94);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--c-border);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-2);
}

.brand {
  display: flex;
  align-items: center;
  text-decoration: none;
}

.site-nav ul {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav a {
  color: var(--c-text);
  text-decoration: none;
  font-weight: 500;
  padding-block: var(--space-2);
  /* Die Unterkante ist IMMER da, nur meist durchsichtig: Bekaeme sie der
     markierte Eintrag erst im Moment der Markierung, spraenge die ganze
     Leiste beim Scrollen um zwei Pixel. */
  border-bottom: 2px solid transparent;
  /* Nur Farben gehen ueber, keine Geometrie. */
  transition:
    color var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard);
}

/* Die Kapitelmarkierung (#336, Schritt 7, zurueck auf Nutzerentscheid).
   Sie traegt ihre Aussage NICHT allein ueber die Farbe: aria-current sagt sie
   auch Screenreadern an, und die Unterkante ist eine Form, kein Farbton. */
.site-nav a[aria-current] {
  color: var(--c-primary-deep);
  border-bottom-color: var(--c-primary);
}

/* Nur mit echtem Zeiger: Auf Touch bleibt :hover nach dem Antippen kleben.
   Der urspruengliche Anlass war schaerfer — der Hover sah aus wie die
   Kapitelmarkierung, und nach dem Antippen standen zwei markierte Kapitel
   im Kopf (Schluss-Review #335, I1). Die Markierung ist mit #336,
   Schritt 5 entfallen; das Gate bleibt, weil ein klebender Hover auch
   ohne sie falsch aussieht. Der Uebergang auf .site-nav a ist davon
   unberuehrt. */
@media (hover: hover) and (pointer: fine) {
  .site-nav a:hover {
    color: var(--c-primary-deep);
  }
}

/* Die Sprungmarken-Leiste steht auf JEDER Breite (Sitemap-Entscheid zu
   #336, Abschnitt 5). Vorher war sie unter 780px ausgeblendet und ein
   Kapitelname im Kopf trat an ihre Stelle; mit sieben Marken fuer neun
   Sektionen ist die Leiste selbst die Ortsangabe.

   Kein Klappmenue: Ein JS-Burger widerspraeche der Zusage "Inhalt ohne
   JavaScript vollstaendig lesbar" und der CSP ohne unsafe-inline.

   WAS UNTER 780PX PASSIERT — in Schritt 7 umentschieden. Schritt 6 legte die
   Eintraege dort in einen waagrecht scrollbaren Streifen: eine Zeile hoch,
   alle sechs per Wisch erreichbar, mit Verlaufs-Deckeln als Hinweis und
   scroll-snap gegen das Stehenbleiben mitten im Wort. Der Umbruch auf mehrere
   Zeilen war mit "kostet 15 bis 18 Prozent der Telefonhoehe" verworfen worden.

   DER STREIFEN HAT DIE SEITE FESTGEHALTEN. Gemessen am 23.08.2026 ueber eine
   CDP-Sitzung mit echten Rad-Ereignissen, Zeiger ueber der Kopfleiste,
   Fensterbreite 600px:

     Wisch                        Seite scrollte
     senkrecht (deltaX 0)         1000px   (eine Raste ging verloren)
     schraeg   (deltaX 40)           0px   <- die Seite stand still

   Ein Wisch auf dem Trackpad ist so gut wie nie genau senkrecht. Sobald eine
   waagrechte Komponente dabei ist, reisst der scrollbare Streifen unter dem
   Zeiger die ganze Geste an sich, rastet ein und gibt nichts an die Seite
   weiter — "die Navigation folgt dem Scrollen nicht und springt hin und her"
   (Abnahme Schritt 7). Das ist die Achsen-Verriegelung des Browsers und laesst
   sich per CSS nicht abschalten: `scroll-snap-type: none` und
   `overscroll-behavior-y: auto` aendern beide nichts (beides gemessen).

   Der Umbruch nimmt dem Kopf den Scrollbereich und damit die Geste, die er
   stehlen koennte. Und er kostet gemessen weniger als angenommen:

     Breite   Kopf mit Streifen   Kopf mit Umbruch   Zeilen
       320          50 px              104 px          3
       390          57 px              104 px          3
       500          57 px               74 px          2
       779          57 px               57 px          1

   Bezahlt wird mit 17px ab 430px und 47px darunter, nicht mit den 129 bis
   153px, die im Schritt-6-Kommentar standen. Dafuer scrollt die Seite an jeder
   Stelle, an der der Zeiger stehen kann. */
.site-header .site-nav {
  min-width: 0;
}

.site-header .site-nav ul {
  flex-wrap: wrap;
  row-gap: var(--space-1);
}

/* flex: none ist nicht optional. Ohne sie schrumpfen die Eintraege
   (flex-shrink ist standardmaessig 1), und wenn sie unter ihre Textbreite
   fallen, bricht der Text INNERHALB des Eintrags um. Gemessen bei 390px vor
   dem Fix: die Leiste war 70px statt 40px hoch, weil "So funktioniert's"
   zweizeilig stand. */
.site-header .site-nav li {
  flex: none;
}

.site-header .site-nav a {
  white-space: nowrap;
}

@media (max-width: 779px) {
  /* Wortmarke und Sprungmarken stehen untereinander, nicht nebeneinander.
     Gemessen bei 390px: Die Wortmarke braucht 100px, die sechs Eintraege
     mindestens 280px — zusammen mehr als die Zeile hat. Nebeneinander legten
     sich die umgebrochenen Zeilen ueber die Wortmarke. */
  .site-header .wrap {
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
  }

  .site-header .site-nav {
    width: 100%;
  }

  .site-nav ul {
    justify-content: flex-start;
  }

  .site-header .logo img {
    width: 100px;
  }

  /* Enger und kleiner: Jede eingesparte Zeile ist Kopfhoehe, und die klebt
     unter 780px dauerhaft am oberen Rand. Gemessen bei 390px: mit
     gap var(--space-4) und 0.92rem brauchten die sechs Eintraege drei Zeilen,
     mit diesen Werten zwei. */
  .site-nav ul {
    column-gap: var(--space-3);
  }

  .site-nav a {
    font-size: var(--fs-2);
    padding-block: var(--space-1);
  }
}

@media (max-width: 359px) {
  .site-header .wrap {
    gap: var(--space-2);
  }

  .site-header .logo img {
    width: 84px;
  }
}

/* ── Bänder / Sektionen ─────────────────────────────────────────── */

.wrap {
  position: relative;
  z-index: 0;
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}

/* Die Seitenraender geben unter 360px mit: 24px links und rechts sind auf
   einem 320px-Schirm fast ein Sechstel der Breite. Der Wert steht auf
   .wrap und nicht nur auf dem Kopf — sonst stuende die Wortmarke 16px vom
   Rand und die Ueberschrift darunter 24px, und die Kante der Seite laege
   zweimal woanders. Die Regel MUSS hinter der Basisregel darueber stehen:
   gleiche Spezifitaet, die spaeter notierte gewinnt (in einem frueheren
   Anlauf stand sie oben im Kopf-Block und blieb wirkungslos). */
@media (max-width: 359px) {
  .wrap {
    padding-inline: var(--space-4);
  }
}

.band {
  background: var(--c-bg);
  padding-block: var(--space-band);
}

.band-alt {
  background: var(--c-bg-alt);
}

.section-lead {
  max-width: var(--measure-2);
  font-size: var(--fs-5);
  color: var(--c-text-soft);
  margin-bottom: var(--space-6);
}

/* ── Wellen-Trenner (Logo-Welle als Sektionsübergang) ───────────── */

.wave {
  display: block;
  width: 100%;
  height: clamp(28px, 4.5vw, 52px);
  /* Haarlinie zwischen Welle und Folgeband vermeiden. */
  margin-bottom: -1px;
}

/* Klasse = Zielfarbe des Übergangs; der Hintergrund ist die Herkunftsfarbe. */
.wave-to-bg {
  background: var(--c-bg-alt);
}

.wave-to-alt {
  background: var(--c-bg);
}

/* Rose statt Welle an den drei Kapitelgrenzen. Der Trenner wird damit
   ein zweites, stilles Ortssignal — es stimmt mit der Kopfmarkierung
   (F1) ueberein. Bewusst ohne Bewegung.

   Fix-Runde 1 (Review): eine reine Rose auf flachem Rechteck erzeugte
   eine harte Farbkante, wo die Welle einen weichen Bogen hatte. Die
   Rose sitzt jetzt auf genau derselben Kurve wie .wave (gleicher Pfad,
   gleiche Hoehe, kein Padding) — sie liest sich als "die Welle mit
   einer Rose", nicht als Ersatz der weichen Kante. */
.rose {
  position: relative;
  display: block;
  width: 100%;
  height: clamp(28px, 4.5vw, 52px);
  /* Haarlinie zwischen Kurve und Folgeband vermeiden, wie bei .wave. */
  margin-bottom: -1px;
}

.rose .curve {
  display: block;
  width: 100%;
  height: 100%;
}

.rose-to-bg {
  background: var(--c-bg-alt);
}

.rose-to-alt {
  background: var(--c-bg);
}

/* ── Buttons ────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3) var(--space-5);
  border-radius: 999px;
  font-size: var(--fs-3);
  font-weight: 600;
  text-decoration: none;
  border: 2px solid transparent;
  transition:
    background-color var(--dur-fast) var(--ease-standard),
    color var(--dur-fast) var(--ease-standard),
    transform var(--dur-fast) var(--ease-out);
}

.btn:active {
  transform: scale(0.97);
}

.btn-primary {
  background: var(--c-primary);
  color: #ffffff;
}

.btn-secondary {
  background: transparent;
  color: var(--c-primary-deep);
  border-color: var(--c-primary);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-5);
}

/* ── Hero ───────────────────────────────────────────────────────── */

@media (min-width: 900px) {
  .hero .wrap > div {
    /* .hero .wrap hat nur dieses eine Kind — keine Bildspalte, kein
       zweispaltiges Raster. Ohne Begrenzung liefe die h1 (bis 3rem) ueber
       die vollen 1100px des Wraps; max-width haelt Ueberschrift und
       Schaltflaechen auf lesbarer Zeilenlaenge. Der Vorspann begrenzt sich
       ohnehin selbst ueber --measure-1 und wird von diesem Wert nicht
       beruehrt — die Begrenzung hier wirkt praktisch nur auf die h1.
       70 statt vormals rund 58 Prozent: die 58 stammten aus einem
       verworfenen zweispaltigen Raster und liessen rechts eine Flaeche
       stehen, die groesser war als der Text daneben (Abnahme #335).
       Rechts bleibt weiterhin Weissraum, aber als Rand, nicht als Loch.
       Die genaue Pixelbreite steht bewusst nicht hier: zwei falsche Zahlen
       in Folge an dieser Stelle zeigen, dass eine hier notierte Zahl nicht
       mitgepflegt wird. Wer sie braucht, misst sie: (Innenbreite von .wrap)
       minus (Breite von .hero .wrap > div), per getBoundingClientRect(). */
    max-width: 70%;
  }
}

.hero-logo {
  margin-bottom: var(--space-5);
}

.hero-logo img {
  width: 230px;
}

.hero-lead {
  font-size: var(--fs-5);
  color: var(--c-text-soft);
  max-width: var(--measure-1);
}

/* ── Karten-Raster ──────────────────────────────────────────────── */

/* align-items: start ist nicht kosmetisch (#336, Schritt 7 — Nachbesserung).
   Ein Raster zieht seine Zellen standardmaessig auf die Hoehe der hoechsten;
   die kurzen Karten stehen dann unten leer. Der Befund, mit dem #336 eroeffnet
   wurde, nennt genau das: "werden vom Raster auf gleiche Hoehe gezwungen, was
   ein Drittel Kartenhoehe leer stehen laesst". Mit den zwei gestrichenen
   Nachweis-Zeilen wurde der Unterschied groesser statt kleiner — die
   WhatsApp-Karte traegt ihren Nachweis weiter, die zwei anderen nicht mehr.
   Am ausgelieferten Stand gemessen: 288px Kartenhoehe, davon 96 bis 130px
   leer. Karten enden jetzt dort, wo ihr Text endet. */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-5);
  align-items: start;
}

.card {
  position: relative;
  padding: var(--space-5);
  background: var(--c-bg);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  transition: transform var(--dur-fast) var(--ease-out);
}

/* Der Schatten wandert auf ein Pseudo-Element, dessen opacity übergeht:
   box-shadow ist Paint-gebunden, opacity läuft auf dem Compositor.
   pointer-events: none ist nicht optional — die Karten enthalten Links,
   ein Pseudo-Element über der ganzen Karte fängt sonst jeden Klick ab. */
.card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 10px 34px rgba(34, 39, 46, 0.12);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
  pointer-events: none;
}

/* Bewegung nur dort, wo es einen echten Zeiger gibt. Auf Touch bleibt
   :hover nach dem Tippen kleben — die Karte bliebe dauerhaft angehoben. */
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover {
    background: var(--c-primary-deep);
    color: #ffffff;
    transform: translateY(-1px);
  }

  .btn-secondary:hover {
    background: rgba(88, 109, 130, 0.1);
    color: var(--c-primary-deep);
    transform: translateY(-1px);
  }

  .card:hover {
    transform: translateY(-2px);
  }

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

/* Die drei Wappenrosen-Farben rotierten hier frueher mechanisch ueber die
   Kartenoberkanten (nth-child(3n+1/2/3)). B8 nennt das beim Namen: "sehen aus
   wie Bedeutung, ohne welche zu tragen. Was gleich aussieht, muss gleich
   wichtig sein." Entfernt — nicht ersatzlos: dieselben drei Farben tragen
   jetzt in .nachweis den Wahrheitszustand einer Aussage und bedeuten damit
   etwas. Die Karten sind untereinander gleichwertig und sehen jetzt auch so
   aus. */

.card h3 {
  margin-bottom: var(--space-2);
}

.card p {
  font-size: var(--fs-3);
}

/* ── Fliesstext ─────────────────────────────────────────────────── */

.prose {
  max-width: var(--measure-3);
  font-size: var(--fs-4);
}

/* ── Fusszeile ──────────────────────────────────────────────────── */

.site-footer {
  background: var(--c-text);
  color: #e8ecf1;
  padding-block: var(--space-7);
  font-size: var(--fs-2);
}

.site-footer p {
  margin: 0 0 var(--space-2);
}

.site-footer a {
  color: #ffffff;
}

.site-footer a:hover {
  color: #ffffff;
}

/* Auf dunklem Grund trägt --c-primary zu wenig Kontrast für den Fokusring. */
.site-footer :focus-visible {
  outline-color: #ffffff;
}

/* ── Textseiten (Impressum, Datenschutz) ────────────────────────── */

/* Fluid wie zuvor, aber aus der Skala: die Obergrenze IST --space-8
   (3.5rem) — genau der Wert, fuer den die Zuordnungstabelle der Spec das
   Token vorsah. Er stand hier als Literal, weil der Abstands-Waechter in
   clamp() wegsah; damit war --space-8 ein totes Token (Schluss-Review
   #335, I3). Die Untergrenze steigt dabei von 2.25rem auf --space-7
   (2.5rem, +4px) — die naechste Stufe der Skala nach unten. */
.textpage {
  padding-block: clamp(var(--space-7), 6vw, var(--space-8));
}

.textpage .wrap {
  max-width: var(--measure-4);
}

.textpage h1 {
  font-size: var(--fs-h1-text);
  margin-bottom: var(--space-5);
}

.textpage h2 {
  font-size: var(--fs-h3);
  margin-top: var(--space-6);
}

.textpage ul {
  padding-left: var(--space-5);
}

.textpage li {
  margin-bottom: var(--space-2);
}

/* Code auf der Bot-Seite (#292): Kennung, Header-Name, robots.txt-Beispiel.
   Ohne eigene Regel fällt der Browser auf 13 px Monospace neben dem
   Fliesstext zurück. Systemschrift, keine Fremdressource (ADR-021).
   overflow-x auf dem Block, nicht auf der Seite: eine lange Zeile scrollt
   im Kasten, nie der Body. */
.textpage code,
.textpage pre {
  /* Kein Handwert fuer font-size (Typografie-Waechter): Inline-Code erbt die
     Stufe des Absatzes, der Block nimmt --fs-3. Die Schriftliste ist bewusst
     keine nackte "monospace" — nur das eine Schluesselwort loest den
     Browser-Sonderfall mit 13 px aus. */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.textpage pre {
  font-size: var(--fs-3);
  margin: var(--space-3) 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--c-bg-alt);
  border-radius: var(--radius);
  overflow-x: auto;
  line-height: 1.6;
}

.address {
  margin-bottom: var(--space-4);
  line-height: 1.7;
}

.meta-line {
  margin-top: var(--space-7);
  font-size: var(--fs-2);
  color: var(--c-text-soft);
}

.back-link {
  display: inline-block;
  margin-top: var(--space-7);
  font-weight: 600;
}

/* ── Scroll-Reveals ─────────────────────────────────────────────── */

/* Verdeckt wird nur, solange ein Skript den Inhalt auch wieder aufdeckt.
   boot.js setzt .reveal-aktiv im <head>, also vor dem ersten Paint (kein
   Aufblitzen), und nimmt die Klasse bei DOMContentLoaded zurueck, wenn
   site.js sich nicht gemeldet hat (kein dauerhaft verdeckter Inhalt).
   Ohne JavaScript, bei fehlendem boot.js und bei fehlendem site.js steht
   die Seite vollstaendig sichtbar da.

   Bewusst NICHT `@media (scripting: enabled)`: das bedeutet "Skripte sind
   erlaubt", nicht "das Skript ist gelaufen" — bei einem 404 auf site.js
   blieben elf von elf Inhaltsbloecken fuer immer unsichtbar
   (Schluss-Review #335, C1). */
html.reveal-aktiv .reveal {
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity var(--dur-reveal) var(--ease-out),
    transform var(--dur-reveal) var(--ease-out);
}

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

/* site.js kehrt bei reduzierter Bewegung früh zurück und setzt nie
   .is-visible — deshalb muss hier die Verdeckung selbst aufgehoben werden. */
@media (prefers-reduced-motion: reduce) {
  /* Traegt .reveal-aktiv mit: die Verdeckung oben ist jetzt an die Klasse
     gebunden und damit spezifischer als ein blosses `.reveal` hier. */
  html.reveal-aktiv .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  /* Bewegung aus, Farbe bleibt. */
  .btn,
  .card {
    transition-property: background-color, color, border-color;
  }

  /* .card::after traegt nur den Schatten-Uebergang (opacity) — hier bleibt
     keine Farbe uebrig, die man behalten koennte. Der Schatten erscheint
     unter reduzierter Bewegung sofort statt eingeblendet. Task 7 hat den
     Uebergang von box-shadow auf .card (dort neutralisiert) auf opacity an
     diesem Pseudo-Element verlagert und ist damit durch eine zweite Tuer
     entkommen (Schluss-Review #335, M1). */
  .card::after {
    transition-property: none;
  }

  .btn:hover,
  .card:hover {
    transform: none;
  }

  /* Bewegung raus, Rueckmeldung bleibt. Ohne die Farbe hier haette ein
     Nutzer mit reduzierter Bewegung GAR keine Druckrueckmeldung: :active
     traegt sonst nur den Transform, und der Hintergrundwechsel haengt am
     :hover — den es auf einem Touchgeraet nicht gibt. Reduzierte Bewegung
     heisst sanfter, nicht nichts (review-animations, #335). */
  .btn:active {
    transform: none;
  }

  .btn-primary:active {
    background-color: var(--c-primary-deep);
  }

  .btn-secondary:active {
    background-color: rgba(88, 109, 130, 0.1);
  }

}

/* ── Nachweis-Zeile — das Erkennungsmerkmal (B6) ─────────────────
   Der Brief verlangt: "Jede tragende Aussage der Seite fuehrt sichtbar ihren
   Nachweis unmittelbar mit — die Messung, die Quelle, das Vertragskapitel,
   oder offen 'nicht gemessen'. Kein Fussnoten-Apparat am Seitenende, sondern
   ein durchgehendes Gestaltungselement."

   Form: eine Zeile in der Dickengleichen, unmittelbar unter der Behauptung,
   eingeleitet von einem Zustandswort. Die Dickengleiche kommt sonst NIRGENDS
   auf der Seite vor — das ist der Punkt: Der Nachweis ist am Schriftbild
   erkennbar, bevor er gelesen ist.

   Drei Zustaende, drei Farben — dieselben drei Farben, die vorher mechanisch
   ueber die Kartenoberkanten rotierten (B8). Sie bedeuten jetzt etwas:

     belegt      gruen  #2F8151  heute nachpruefbar
     vertraglich blau   #586D82  in der Leistungsvereinbarung zugesagt
     offen       gelb   #F3C432  kein Nachweis — Absicht, Vorbereitung, Beispiel

   Die Farbe traegt die Aussage NIE allein: Das Zustandswort steht als Text
   daneben, und der Text bleibt in --c-text-soft. Gelb auf Papier ist als
   Textfarbe unlesbar; als Marke von 8x8px ist es eindeutig. So haengt weder
   Lesbarkeit noch Bedeutung an der Farbwahrnehmung.

   Die Zustandswoerter stammen aus der Spalte "Zustand" der
   Pflichtstueck-Tabelle des Briefs (Abschnitt 7) — die Seite zeigt damit die
   Tabelle, gegen die sie geprueft wird. */

/* Blocksatz, nicht flex. Als Flex-Zeile stand das Zustandswort links und der
   Beleg daneben — mit haengendem Einzug in Wortbreite. In einer 240px-Karte
   blieben davon rund 150px Textbreite uebrig, also vier bis fuenf Woerter pro
   Zeile. Als Block laeuft der Beleg hinter dem Zustandswort weiter und bricht
   auf die volle Breite um. */
.nachweis {
  display: block;
  max-width: var(--measure-3);
  margin: calc(var(--space-3) * -1) 0 var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px solid var(--c-border);
  font-size: var(--fs-1);
  line-height: 1.55;
  color: var(--c-text-soft);
}

/* Zustandswort: nicht umbrechen, damit Marke und Wort zusammenbleiben, und
   danach ein Geviert Abstand zum Beleg. */
.nachweis > b {
  margin-right: var(--space-2);
  font-weight: 600;
  color: var(--c-text);
  white-space: nowrap;
}

.nachweis > b::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: var(--space-1);
  border-radius: 2px;
  background: var(--zustand, var(--c-text-soft));
}

.nachweis--belegt {
  --zustand: var(--c-accent-green);
}

.nachweis--vertraglich {
  --zustand: var(--c-primary);
}

.nachweis--offen {
  --zustand: var(--c-accent-yellow);
}

/* Innerhalb einer Karte sitzt die Zeile am Fuss und traegt die volle Breite. */
.card .nachweis {
  max-width: none;
  margin: var(--space-4) 0 0;
}

/* ── Produktbilder (B9) ─────────────────────────────────────────
   "Mindestens ein echtes Produktbild. Ein Chat-Produkt, das nur beschrieben
   wird, ist unglaubwuerdig. Wortmarke und Ornament zaehlen nicht."

   Zwei Stueck, und sie sind zugleich die Antwort auf B8: Bis hierher ist die
   Seite durchgehend Text. Ein Telefon und ein Dashboard sind der Unterschied,
   aus dem Betonung entsteht — ohne dass irgendwo eine Farbe lauter werden
   muss. */

/* Text und Produktbild nebeneinander, sobald Platz dafuer da ist. Ohne dieses
   Raster stand das Telefon als 380px-Saeule unter dem Text, mit rund 1000px
   Leere daneben (gemessen bei 1456px) — das las sich wie ein unfertiger
   Abschnitt, nicht wie eine Vorfuehrung. Die Grenze liegt bei 900px: darunter
   waere die Textspalte neben einem 380px-Telefon schmaler als --measure-1 und
   damit schlechter lesbar als untereinander. */
.nebeneinander {
  display: grid;
  gap: var(--space-8);
  align-items: start;
}

@media (min-width: 900px) {
  .nebeneinander {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}

.produkt {
  margin: 0;
  min-width: 0;
}

/* Die Nachweis-Zeile unter einem Produktbild darf die Spaltenbreite NICHT
   bestimmen. Sie traegt sonst ihre --measure-3 (68ch) in die auto-Spalte des
   Rasters, die Spalte wird dadurch breiter als das Bild, und die Textspalte
   daneben faellt unter die Breiten-Untergrenze aus #335 (gemessen: 485px statt
   mindestens 550px). Sie folgt jetzt der Breite des Bildes. */
.produkt figcaption.nachweis {
  max-width: none;
}

.produkt--schmal {
  max-width: 380px;
}

/* Ausserhalb des Rasters (unter 900px) braucht das Bild wieder Luft nach oben. */
@media (max-width: 899px) {
  .produkt {
    margin-top: var(--space-6);
  }
}

.produkt figcaption {
  margin-top: var(--space-4);
}

/* ── Dashboard-Nachbildung ──────────────────────────────────────
   Rahmen wie ein Fenster, damit sofort klar ist: das ist eine Oberflaeche,
   kein Diagramm der Seite. Die Nachbildung selbst liegt als SVG daneben und
   bringt ihre eigenen Farben mit (die des echten Dashboards, nicht die der
   Website) — genau deshalb wirkt sie wie ein Produkt und nicht wie eine
   Illustration. */

/* Volle Spaltenbreite. In einer halben Spalte (rund 570px bei 1440px
   Fenster) stapelt das Dashboard seine Karten untereinander und sieht aus wie
   eine Telefon-App — also wie das Gegenteil dessen, was der Abschnitt zeigen
   soll. */
.produkt--breit {
  margin-top: var(--space-6);
}

.fenster {
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  overflow: hidden;
  background: #ffffff;
  box-shadow: var(--shadow);
}

.fenster-leiste {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  background: var(--c-bg-alt);
  border-bottom: 1px solid var(--c-border);
}

.fenster-leiste span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--c-border);
}

/* Das Fenster zeigt eine eingebettete Seite, kein Bild. Die Hoehe steht
   fest, weil der Inhalt scrollt: Ein Rahmen, der mit seinem Inhalt waechst,
   ist auf einer Seite mit neun Sektionen kein Fenster mehr, sondern ein
   zweites Dokument. 520px zeigen Kopfleiste, Kennzahlen und den Verlauf
   vollstaendig; alles Weitere ist einen Wisch entfernt. */
/* Der Halter traegt die Hoehe, der Schirm den Inhalt.

   WARUM ZWEI ELEMENTE (#336, Schritt 7 — Nachbesserung): Unter 380px
   Rahmenbreite laeuft die Beispielansicht seitlich ueber — ihr Inhalt hat
   eine harte Mindestbreite von 378px (gemessen: 340px Rahmen -> 38px
   Ueberlauf, 360 -> 18, 370 -> 8, ab 380 -> 0). Auf dem Telefon entstand
   damit ein Kasten, der sich nach links und rechts schieben laesst, mitten
   in einer Seite, die nach unten scrollt. site.js gibt dem Schirm deshalb
   dort eine FESTE Breite von 390px und verkleinert ihn per transform auf die
   verfuegbare Breite. `transform` aendert die Layout-Groesse aber nicht — der
   Halter ist das Element, dessen Hoehe sich mitverkleinern laesst, und er
   klammert den Ueberstand ab. */
.fenster-halter {
  overflow: hidden;
  /* Rueckfallhoehe fuer den Fall ohne JavaScript. site.js setzt die exakte
     Hoehe des eingebetteten Dokuments; ohne sie soll wenigstens ungefaehr das
     Richtige dastehen, statt eines Scrollkastens mit angeschnittenem
     Diagramm. Gemessen am ausgelieferten Stand mit sechs Karten (#360, seit
     der Karte „Widget-Nutzung"): 1866px bei 1050px Rahmenbreite, 1945px bei
     900px. tests/test_website_dashboard_beispiel.py prueft, dass dieser Wert
     nicht davonlaeuft. */
  height: 1950px;
}

/* Unter 900px legt die Beispielansicht ihre Karten untereinander und wird
   dadurch deutlich hoeher: gemessen 2952px bei 700px und 3207px bei 340px
   Rahmenbreite (sechs Karten, #360). */
@media (max-width: 899px) {
  .fenster-halter {
    height: 3250px;
  }
}

.fenster-schirm {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: #f8fafc;
}

/* ── WhatsApp-Telefon (B9, zweites Produktbild) ─────────────────
   Rahmen von uns, Inhalt vom Produkt: der Schirm zeigt
   demo.munica.ch/whatsapp.html.

   DER SCHIRM IST 390x844 UND WIRD VERKLEINERT, statt den Rahmen einfach
   schmal zu machen (#336, Schritt 7 — Nachbesserung). Der erste Anlauf gab
   dem Rahmen 250 bis 300px Breite und liess die eingebettete Seite auf DIESE
   Breite umbrechen. Ausgeliefert sah man dann eine Seite, die es auf keinem
   Geraet gibt: Der Begruessungstext brauchte doppelt so viele Zeilen wie das
   Feld hoch war und stand oben UND unten angeschnitten da, der Kopf brach auf
   drei Zeilen um. Das las sich wie ein defektes Produkt.

   390x844 ist die Geometrie eines Telefons; die eingebettete Seite legt sich
   genau so aus, wie sie es auf einem Telefon tut, und `transform: scale()`
   verkleinert das fertige Bild. Der Preis ist Schriftgroesse (rund 11px
   effektiv) — das ist der Preis eines Produktbildes, und daneben steht der
   Knopf in die volle Demo.

   Zwei Eigenschaften der Vorlage tragen den Rahmen weiterhin: ihr eigener
   Telefonrahmen samt Statuszeile erscheint erst ab 768px, ihr Debug-Panel
   erst ab 1200px. 390px bleibt unter beidem.

   `overflow: hidden` ist nicht optional: Der Schirm ist groesser als sein
   Rahmen, und ohne die Klammer stuende er ueber die Kante hinaus. */
.telefon {
  width: 100%;
  max-width: 301px;
  /* 40px Statuszeile + 844px Schirm = 884px Buehne; 884 * 0.72 = 636.5, plus
     2 x 10px Rand = 656.5 -> 657px. Wer an einer dieser Zahlen dreht, muss
     alle drei nachziehen; tests/test_website_telefon.py rechnet sie nach. */
  height: 657px;
  margin-inline: auto;
  overflow: hidden;
  /* Rahmen statt Innenabstand: Der Telefonrand ist ein Rand, kein Abstand aus
     der Skala — und die Abstands-Skala hat keine 10px-Stufe (die Waechter in
     tests/test_website_motion.py bestehen zu Recht darauf). */
  border: 10px solid #111111;
  border-radius: 44px;
  box-shadow: 0 18px 50px rgba(34, 39, 46, 0.22);
  /* Der Grund der eingebetteten Seite. Er traegt den halben Pixel, den die
     Rundung 636.5 -> 637 unten offen laesst, und steht vor dem Laden des
     Schirms da, damit im schwarzen Rahmen keine weisse Flaeche aufblitzt. */
  background: #ece5dd;
}

/* Statuszeile und Schirm werden GEMEINSAM verkleinert, nicht einzeln: So
   gilt ueberall dasselbe 390px-Koordinatensystem, und die Statuszeile ist in
   denselben Massen gebaut, in denen ein Telefon sie zeichnet — statt in
   umgerechneten 0.72-Werten, die niemand mehr nachvollzieht. */
.telefon-buehne {
  width: 390px;
  transform: scale(0.72);
  transform-origin: 0 0;
}

.telefon-statuszeile {
  /* Hoehe statt Innenabstand, aus demselben Grund wie der Rand oben: Das ist
     die Bauhoehe einer Telefon-Statuszeile, keine Rhythmusstufe der Seite. */
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: var(--space-5);
  /* Dasselbe WhatsApp-Gruen wie der Chat-Kopf darunter — die beiden sollen
     als eine Flaeche lesen, so wie auf dem Telefon. */
  background: #075e54;
  color: #ffffff;
  /* --fs-2 (0.9rem = 14.4px) im 390px-System, durch die Verkleinerung
     effektiv rund 10px — die Groesse, in der ein Telefon seine Statuszeile
     setzt. Ein Handwert waere hier die dritte Zahl gewesen, die niemand mehr
     nachrechnet; tests/test_website_typografie.py besteht zu Recht darauf. */
  font-size: var(--fs-2);
  font-variant-numeric: tabular-nums;
}

.telefon-zeichen {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.telefon-schirm {
  display: block;
  width: 390px;
  height: 844px;
  border: 0;
  background: #ece5dd;
}


