/* clipmark.de: der Onepager und die Rechtsseite.
 *
 * Getrennt von page.css. Die Kundenseite bekommt ihre Farben und Schriften aus
 * /theme.css und ist pro Kunde anders; diese Seite hat ein festes Aussehen und
 * darf deshalb feste Werte tragen. Zwei Dateien statt einer, weil die
 * Kundenseite nichts von unserer Gestaltung laden soll: sie soll aussehen wie
 * der Kunde, nicht wie wir.
 *
 * Haltung: Schweizer Raster, warmes Papier gegen das kalte Marken-Schiefer,
 * Haarlinien statt Kästen. Keine Rundungen, keine umrandeten Knöpfe: eine
 * Fläche ist entweder gefüllt oder sie ist Text.
 */

/* ----------------------------------------------------------------- Marken */

:root {
  --paper: #fbfaf7;
  --paper-2: #f1eee8;
  --ink: #283a49;          /* die Marke */
  --ink-2: #1d2b37;
  --ink-soft: #5a6c7a;
  --on-dark: #fbfaf7;
  --on-dark-soft: #9aa8b3;
  --rule: rgba(40, 58, 73, 0.16);
  --rule-dark: rgba(251, 250, 247, 0.18);

  /* Breit genug, dass vier Spalten vier Spalten bleiben: Ablauf und Gründe
     haben je vier Einträge, und drei plus einer sieht aus wie ein Nachtrag. */
  --spalte: 1400px;
  --rand: clamp(20px, 5vw, 64px);

  --sans: "Archivo", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;
}

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

/* Konsequent eckig. Eine einzige gerundete Ecke irgendwo im Dokument fiele
   sofort auf, und das passiert erfahrungsgemäß über ein Browser-Standardmaß,
   nicht über eine Regel, die jemand schreibt. */
* { border-radius: 0; }

html {
  /* Hausregel: nie die Bildlaufleiste des Betriebssystems. `scrollbar-color`
     deckt Firefox ab, die ::-webkit-Regeln weiter unten alles andere. */
  scrollbar-color: rgba(40, 58, 73, 0.35) transparent;
  scrollbar-width: thin;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--paper-2); }
::-webkit-scrollbar-thumb { background: rgba(40, 58, 73, 0.3); }
::-webkit-scrollbar-thumb:hover { background: rgba(40, 58, 73, 0.5); }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a { color: inherit; }

/* Sichtbar, ohne dass ein Kasten entsteht. Ein Offset statt eines Rahmens,
   damit der Ring nicht mit den Haarlinien der Seite verwechselt wird. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
.band-dunkel a:focus-visible,
.band-dunkel button:focus-visible { outline-color: var(--on-dark); }

/* ------------------------------------------------------------- Grundraster */

.spalte {
  max-width: var(--spalte);
  margin: 0 auto;
  padding-inline: var(--rand);
}

.band { padding-block: clamp(64px, 9vw, 120px); }
.band-papier-2 { background: var(--paper-2); }
.band-dunkel {
  background: var(--ink);
  color: var(--on-dark);
}
.band-dunkel .lead { color: var(--on-dark-soft); }

/* Die Rubrik. Nummer und Name in einer Zeile, darunter eine Linie über die
   volle Spaltenbreite: das ist der Taktschlag, an dem die Seite hängt. */
.rubrik {
  display: flex;
  align-items: baseline;
  gap: 16px;
  margin: 0 0 clamp(32px, 4vw, 52px);
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
}
.band-dunkel .rubrik { border-bottom-color: var(--rule-dark); }

.rubrik-nr {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}
.band-dunkel .rubrik-nr { color: var(--on-dark-soft); }

.rubrik-titel {
  margin: 0;
  font-size: clamp(15px, 2vw, 17px);
  font-weight: 500;
  letter-spacing: 0.01em;
}

h1, h2, h3 { font-weight: 700; letter-spacing: -0.025em; line-height: 1.08; margin: 0; }

.lead {
  max-width: 56ch;
  font-size: clamp(17px, 1.6vw, 19px);
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* Ein Satz zwischen Rubrik und Raster. Ohne ihn beginnt ein Abschnitt sofort
   mit Kacheln, und die Seite liest sich wie eine Sammlung von Bruchstücken
   statt wie ein Text. */
.rubrik + .lead { margin: -8px 0 clamp(34px, 4.5vw, 54px); }

/* --------------------------------------------------------------- Kopfzeile */

.kopf {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.kopf-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 68px;
}
.kopf .logo-svg { width: 132px; height: auto; color: var(--ink); }

.kopf-nav {
  display: flex;
  align-items: center;
  gap: clamp(18px, 2.5vw, 34px);
  font-size: 15px;
}
.kopf-nav a { text-decoration: none; color: var(--ink-soft); }
.kopf-nav a:hover { color: var(--ink); }

/* Knopf und Menükasten gibt es nur auf dem Telefon, siehe unten. Auf breiten
   Bildschirmen ist der Knopf weg, und der Kasten um Navigation und Sprachwahl
   löst sich auf: seine Kinder liegen direkt in der Flex-Zeile, und die
   Kopfzeile sieht aus wie ohne ihn. Logo links, Navigation in der Mitte,
   Sprachwahl rechts. */
.kopf-burger { display: none; }
.kopf-menue { display: contents; }

/* ------------------------------------------------------------ Sprachwahl
   Klein und mit Kürzel. Zwei Zeichen statt „Deutsch / English": der Umschalter
   steht neben vier Navigationspunkten und darf sie nicht überstimmen.

   Kein eigener Kasten und kein Rahmen. Als Pille neben dem gefüllten Knopf
   stünden zwei Bedienelemente nebeneinander, die gleich aussehen und
   Verschiedenes tun; dasselbe Missverständnis wie beim Formatumschalter des
   callshop, wo aus zwei Pillen nebeneinander ein durchgehender Balken wurde. */

.sprachwahl {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
}

.sprachwahl-aktiv { color: var(--ink); }
.sprachwahl-trenner { color: var(--rule); }
.sprachwahl-link { color: var(--ink-soft); text-decoration: none; }
.sprachwahl-link:hover { color: var(--ink); }

/* Der Fuß steht auf dunklem Grund und braucht die anderen Tokens. */
.fuss .sprachwahl-aktiv { color: var(--on-dark); }
.fuss .sprachwahl-trenner { color: var(--rule-dark); }
.fuss .sprachwahl-link { color: var(--on-dark-soft); }
.fuss .sprachwahl-link:hover { color: var(--on-dark); }

@media (max-width: 720px) {
  /* Auf dem Telefon steht der Umschalter im Menü und im Fuß. Er bleibt in
     beiden: er ist der einzige Weg zur anderen Fassung, wenn das Band schon
     weggeklickt wurde. */
  .sprachwahl { font-size: 11px; }
}

/* ------------------------------------------------------ Menü auf dem Telefon
   Alles hinter einem Knopf: die fünf Punkte, „Jetzt erstellen" und die
   Sprachwahl. Der Kasten hängt unter der Kopfzeile, so breit wie das Fenster,
   auf Papier und mit einer Haarlinie darunter. Kein Vollbild und keine
   Verdunklung: es sind sieben Zeilen, kein zweiter Bildschirm.

   `hat-menue` setzt erst das Skript. Ohne Skript gibt es keinen Knopf, und
   der Kasten bleibt sichtbar und stapelt sich in der Kopfzeile: schlechter
   anzusehen, aber jeder Punkt erreichbar. */
@keyframes menue-auf {
  from { opacity: 0; transform: translateY(-6px); }
}

@media (max-width: 720px) {
  .kopf:not(.hat-menue) .kopf-inner { flex-wrap: wrap; height: auto; padding-block: 14px; }
  .kopf:not(.hat-menue) .kopf-nav { flex-wrap: wrap; }

  /* 44 Pixel Zielfläche um ein 22 Pixel breites Zeichen. Der negative Rand
     stellt das Zeichen an die Kante, an der auf breiten Bildschirmen die
     Sprachwahl endet, nicht den unsichtbaren Kasten darum. */
  .kopf.hat-menue .kopf-burger {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    margin-inline-end: -11px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--ink);
    cursor: pointer;
  }
  .kopf-burger-strich {
    display: block;
    width: 22px;
    height: 2px;
    background: currentColor;
    transition: transform 160ms ease, opacity 160ms ease;
  }
  /* Offen wird aus drei Strichen ein Kreuz: die äußeren kippen auf die Mitte,
     der mittlere verschwindet. */
  .menue-offen .kopf-burger-strich:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .menue-offen .kopf-burger-strich:nth-child(2) { opacity: 0; }
  .menue-offen .kopf-burger-strich:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  /* Die Kopfzeile ist `sticky` und damit der Bezug für den Kasten: er hängt
     unter ihr, über dem Inhalt, und wandert mit ihr mit. */
  .kopf.hat-menue .kopf-menue {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    max-height: calc(100dvh - 68px);
    overflow-y: auto;
    padding: 4px var(--rand) 24px;
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 24px 32px -24px rgba(29, 43, 55, 0.35);
  }
  .kopf.menue-offen .kopf-menue {
    display: flex;
    animation: menue-auf 160ms ease-out;
  }

  .kopf.hat-menue .kopf-nav {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    font-size: 17px;
  }
  .kopf.hat-menue .kopf-nav a {
    padding: 15px 0;
    border-top: 1px solid var(--rule);
    color: var(--ink);
  }
  .kopf.hat-menue .kopf-nav a:first-child { border-top: 0; }
  /* „Jetzt erstellen" zuerst und als Fläche, so wie er im Fließtext aussieht.
     Im Markup steht er zuletzt, damit die Reihenfolge auf breiten
     Bildschirmen bleibt: Punkte links, Knopf rechts.

     **Mit `a` im Selektor und ausdrücklicher Farbe.** Die Regel für die
     Menüpunkte darüber ist `.kopf.hat-menue .kopf-nav a` und damit spezifischer
     als jede Fassung ohne Elementnamen: der Knopf bekäme Tinte auf Tinte und
     wäre ein dunkler Kasten ohne sichtbare Schrift. Genau dieselbe Falle
     beschreibt der Kommentar an `.kopf-nav .knopf-voll` weiter oben, und sie
     ist hier ein zweites Mal zugeschnappt. */
  .kopf.hat-menue .kopf-nav a.knopf-voll {
    order: -1;
    display: flex;
    margin: 12px 0 8px;
    padding: 14px 20px;
    font-size: 16px;
    border-top: 0;
    color: var(--paper);
  }
  /* Im Menü ist Platz, also bekommen die beiden Kürzel eine Zielfläche, die
     ein Daumen trifft: 15 Pixel Polsterung um eine 14 Pixel hohe Zeile sind
     44 Pixel. In der Kopfzeile daneben ginge das nicht, dort sind 68 Pixel
     Gesamthöhe. */
  .kopf.hat-menue .kopf-menue .sprachwahl {
    border-top: 1px solid var(--rule);
    font-size: 14px;
  }
  .kopf.hat-menue .kopf-menue .sprachwahl-aktiv,
  .kopf.hat-menue .kopf-menue .sprachwahl-link {
    padding: 15px 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kopf-menue { animation: none; }
  .kopf-burger-strich { transition: none; }
}

/* --------------------------------------------------------------- Sprachband
   Schmal, oben, und erst durch das Skript sichtbar. Kein Überlagern des
   Inhalts: ein Band, das etwas verdeckt, wird weggeklickt, bevor es gelesen
   wird. */

.sprachband {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px var(--rand);
  background: var(--paper-2);
  border-block-end: 1px solid var(--rule);
  font-size: 14px;
}

.sprachband[hidden] { display: none; }

.sprachband-text { margin: 0; color: var(--ink-soft); }
.sprachband-verweis { color: var(--ink); }

.sprachband-zu {
  margin-inline-start: auto;
  padding: 4px 8px;
  border: 0;
  background: none;
  color: var(--ink-soft);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

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

/* ------------------------------------------------------------------- Knöpfe
   Gefüllt oder Text, nichts dazwischen. Ein umrandeter Knopf sähe hier aus wie
   ein Eingabefeld, das nicht funktioniert. */

.knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 24px;
  font: inherit;
  font-size: 16px;
  font-weight: 500;
  text-decoration: none;
  border: 0;
  cursor: pointer;
  transition: background-color 140ms ease, color 140ms ease;
}
.knopf-voll { background: var(--ink); color: var(--paper); }
.knopf-voll:hover { background: var(--ink-2); }

/* Flacher als ein Knopf im Fließtext: 68 Pixel Kopfhöhe vertragen keinen in
   voller Größe. Die Farbe muss hier ausdrücklich stehen, sonst gewinnt
   `.kopf-nav a` nach Spezifität und die Schrift wird grau statt papierfarben. */
.kopf-nav .knopf-voll {
  padding: 9px 20px;
  font-size: 15px;
  color: var(--paper);
}
.kopf-nav .knopf-voll:hover { color: #fff; }
/* Auf dunklem Grund kehrt sich der Knopf um. Ohne das stünde Tinte auf Tinte,
   und der einzige Handlungsaufruf des Heros wäre unsichtbar. */
.band-dunkel .knopf-voll,
.hero .knopf-voll { background: var(--paper); color: var(--ink); }
.band-dunkel .knopf-voll:hover,
.hero .knopf-voll:hover { background: #fff; }

/* Der zweite Weg ist ein Text mit kräftiger Unterlänge, kein zweiter Kasten.
   Zwei Kästen nebeneinander lassen die Hierarchie verschwimmen. */
.knopf-text {
  padding-inline: 0;
  background: none;
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
  text-decoration-color: var(--rule);
}
.knopf-text:hover { text-decoration-color: currentColor; }

/* --------------------------------------------------------------------- Hero */

.hero {
  background: var(--ink);
  color: var(--on-dark);
  padding-block: clamp(72px, 11vw, 148px);
  /* Ein sehr flacher Verlauf nach unten rechts: genug, dass die Fläche nicht
     tot wirkt, zu wenig, um als Effekt aufzufallen. */
  background-image:
    radial-gradient(120% 90% at 88% 8%, rgba(255, 255, 255, 0.07), transparent 60%),
    linear-gradient(180deg, transparent 60%, rgba(0, 0, 0, 0.18));
}

/* Text links, Zeichnung rechts, Merkmale darunter über die volle Breite. Erst
   ab 1000 px: darunter steht die Zeichnung unter dem Text, wo sie die
   Überschrift nicht zusammenquetscht. */
.hero-raster {
  display: grid;
  gap: clamp(36px, 5vw, 72px);
  align-items: center;
}
@media (min-width: 1000px) {
  .hero-raster { grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); }
}

.augenbraue {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--on-dark-soft);
  margin: 0 0 clamp(28px, 4vw, 44px);
}

/* Zwei Zeilen: oben wechselt der Anlass, unten steht, was mit ihm passiert.
   Die wechselnden Wörter liegen übereinander und sind deshalb absolut gesetzt;
   die Zeile behält ihre Höhe über `height`, sonst klappte die Überschrift beim
   Laden zusammen. */
.stapel {
  /* 6vw und nicht 7,2: bei rund 1100 Pixeln ist die Textspalte 486 Pixel
     breit, und „ins Bild setzen" braucht bei 79 Pixeln Schriftgröße etwa 500.
     Die Zeile brach dort um, und aus zwei Zeilen wurden drei. Die volle Größe
     wird jetzt erst ab etwa 1370 Pixeln erreicht; dazwischen ist die
     Überschrift kleiner, dafür bleibt sie zweizeilig. */
  font-size: clamp(34px, 6vw, 82px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  margin: 0;
}
.stapel > span { display: block; font-weight: 400; }
.stapel .aktiv { color: var(--on-dark); }
.stapel .aktiv b { font-weight: 700; }

.wechsel {
  position: relative;
  height: 1.06em;
}
.wechsel span {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  /* Kein reines Weiß: das wechselnde Wort ist der Nebensatz, „teilen" die
     Aussage. 5,5:1 gegen den Grund, also weit über der Schwelle. */
  color: rgba(251, 250, 247, 0.62);
  opacity: 0;
}
/* Ohne Bewegung bleibt ein Wort stehen, und zwar das letzte: die Überschrift
   soll auch dann einen Sinn ergeben. Zugleich der Rückfall für Browser, die
   die Animation nicht ausführen. */
.wechsel span:last-child { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .wechsel span {
    opacity: 0;
    /* `backwards` ist hier nicht Kosmetik. Ohne Füllung gilt während der
       Verzögerung der Grundstil, und der lautet für das letzte Wort
       `opacity: 1`: `.wechsel span:last-child` schlägt `.wechsel span` nach
       Spezifität. „Kongresse" stünde die ersten sechzehn Sekunden dauerhaft
       unter allen anderen Wörtern, danach wäre alles sauber. Genau deshalb
       fällt es beim Prüfen durch, wenn man nicht sofort nach dem Laden misst.

       Nur `backwards`, nicht zusätzlich die Spezifität geradeziehen: läuft die
       Animation gar nicht, greift auch die Füllung nicht und das letzte Wort
       bleibt stehen. Eine Regel, die das Wort hart auf 0 setzt, hinterließe in
       dem Fall eine leere Überschrift. */
    animation: wortwechsel 22.4s linear infinite backwards;
  }
  .wechsel span:nth-child(1) { animation-delay: 0s; }
  .wechsel span:nth-child(2) { animation-delay: 3.2s; }
  .wechsel span:nth-child(3) { animation-delay: 6.4s; }
  .wechsel span:nth-child(4) { animation-delay: 9.6s; }
  .wechsel span:nth-child(5) { animation-delay: 12.8s; }
  .wechsel span:nth-child(6) { animation-delay: 16s; }
  .wechsel span:nth-child(7) { animation-delay: 19.2s; }
}

/* Sieben Wörter zu 3,2 Sekunden, macht 22,4 Sekunden je Runde. Davon eine
   halbe zum Auf- und eine halbe zum Abblenden. Die Prozentwerte hängen an der
   Wortzahl: ein achtes Wort verlangt 25,6s und wieder neue Marken, sonst
   überlappen zwei Wörter. Der winzige Versatz nach oben gibt dem Wechsel eine
   Richtung. */
@keyframes wortwechsel {
  0% { opacity: 0; transform: translateY(0.16em); }
  2.2% { opacity: 1; transform: translateY(0); }
  12.1% { opacity: 1; transform: translateY(0); }
  14.3%, 100% { opacity: 0; transform: translateY(-0.1em); }
}

.hero .lead {
  margin: clamp(24px, 3vw, 34px) 0 0;
  color: var(--on-dark-soft);
  max-width: 46ch;
}

.hero-aktionen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(16px, 3vw, 32px);
  margin-top: clamp(32px, 4vw, 46px);
}

/* --------------------------------------------------- Die Explosionszeichnung
   Der Ruhezustand zeigt das fertige Profilbild, weil Foto und Overlay
   deckungsgleich liegen; beim Überfahren fahren die drei Ebenen in die Tiefe
   auseinander und zeigen, woraus es besteht.

   Der Zustand steht in vier Variablen auf der Bühne, und nur die werden
   umgeschaltet. Ohne das stünde derselbe Transform-Block viermal da: für
   Hover, für den Auftakt, für Geräte ohne Hover und für reduzierte Bewegung.
   Sie sind über `@property` typisiert, sonst springt der Wechsel, statt zu
   laufen: eine nicht registrierte Variable kann der Browser nicht
   interpolieren. */

.aufbau {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin-inline: auto;
  aspect-ratio: 1;
  perspective: 1600px;

  /* Die Tiefe wächst mit dem Fenster. Fest gesetzt platzte die Zeichnung auf
     dem Telefon aus ihrer Bühne: dort steht sie dauerhaft offen, und die
     vorderste Ebene wird durch die Perspektive um ein Viertel größer. */
  --auf-1: clamp(60px, 10.5vw, 150px);
  --auf-2: clamp(120px, 21vw, 300px);

  --kipp-x: 0deg;
  --kipp-y: 0deg;
  --tiefe-1: 0px;
  --tiefe-2: 0px;

  /* Dauer, Kurve und Verzögerung des Auftakts an einer Stelle, damit die drei
     Ebenen nicht auseinanderlaufen können. */
  --auftakt: 3000ms cubic-bezier(0.5, 0, 0.25, 1) 500ms;
}

/* Bewegt wird `transform`, nicht die Variablen darin.
 *
 * Zuerst standen die Übergänge auf den Custom Properties selbst. Das sieht
 * aufgeräumter aus, läuft aber ausnahmslos im Hauptthread: der Browser muss je
 * Frame die Variablen neu einsetzen, drei Transforms neu bauen und die Szene
 * neu zeichnen. Dazu kommt `vector-effect: non-scaling-stroke` weiter unten,
 * das bei jeder Größenänderung alle Striche neu rastert. Der Rahmen fiel dabei
 * nicht auf, weil er seine Größe kaum ändert; Foto und Overlay wachsen durch
 * die Perspektive um ein Viertel und ruckelten sichtbar.
 *
 * Über `transform` kann der Browser die Bewegung auslagern. Die Variablen
 * bleiben, aber sie schalten nur noch den Zielwert um. */
.aufbau-tiefe {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--kipp-x)) rotateY(var(--kipp-y));
  transition: transform 760ms cubic-bezier(0.16, 1, 0.3, 1);
}

.schicht { position: absolute; }
.schicht-seite { inset: 0; }
/* Foto und Overlay sind deckungsgleich. Das ist der Kern: flach ergeben sie
   zusammen genau das Bild, das die Karten weiter unten zeigen. */
.schicht-foto,
.schicht-overlay {
  inset: 21%;
  transition: transform 760ms cubic-bezier(0.16, 1, 0.3, 1);
}
.schicht-foto { transform: translateZ(var(--tiefe-1)); }
.schicht-overlay { transform: translateZ(var(--tiefe-2)); }

/* Die Beschriftungen. Flach über der Bühne und nicht darin: in der Bühne
   kippten sie mit und wären schräg gestellte Kleinschrift auf dunklem Grund.

   Jede Anschlusslinie ist anders lang, weil die rechte Kante ihrer Ebene im
   gekippten Zustand woanders liegt. Gemessen an der 400 Pixel breiten Bühne:
   Seite 404, Foto 268, Overlay 212 Pixel von links. Die Linie endet damit
   wirklich auf ihrer Ebene und nicht irgendwo daneben. Ändert sich Kippwinkel
   oder Tiefe, müssen diese drei Werte neu gemessen werden. */
.marken {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  /* Diese Kurve gilt beim Verlassen: kurz und ohne Verzögerung, damit die
     Beschriftung nicht hinter dem Zeiger herläuft. Das Einblenden steht in der
     Hover-Regel und darf sich Zeit lassen. */
  transition: opacity 200ms ease;
}
.marke {
  position: absolute;
  left: calc(100% + 28px);
  width: 136px;
  font-family: var(--mono);
  font-size: 10.5px;
  line-height: 1.55;
  letter-spacing: 0.03em;
  color: var(--on-dark-soft);
}
.marke::before {
  content: "";
  position: absolute;
  right: 100%;
  top: 0.72em;
  width: var(--anschluss);
  height: 1px;
  background: rgba(251, 250, 247, 0.3);
}
.marke b { display: block; font-weight: 500; color: var(--on-dark); }

/* An der 400 Pixel breiten Bühne gemessen: die rechte Kante liegt bei 401, 259
   und 194 Pixeln, die Beschriftung beginnt bei 428. Daraus die drei Längen. */
.marke-seite { top: 4%; --anschluss: 27px; }
.marke-foto { top: 36%; --anschluss: 169px; }
.marke-overlay { top: 70%; --anschluss: 234px; }

/* Erst ab 1280 Pixeln ist die rechte Spalte breit genug für Bühne und
   Beschriftungen nebeneinander: gemessen 517 Pixel gegen 400 plus 182. Darunter
   bleibt die Zeichnung ohne Text, sonst stünde sie in ihrer eigenen
   Beschriftung. */
@media (max-width: 1279px) {
  .marken { display: none; }
}
@media (min-width: 1280px) {
  .aufbau {
    max-width: 400px;
    margin-inline: 0 auto;
  }
}

@media (hover: hover) {
  /* Beim Kommen mit Verzögerung, damit die Ebenen zuerst ihren Platz finden. */
  .aufbau:hover .marken {
    opacity: 1;
    transition: opacity 320ms ease 180ms;
  }
}
@media (hover: none) {
  .marken { opacity: 1; }
}

/* Alle Linien exakt gleich stark, egal wie groß eine Ebene gerendert wird.
   Ohne `non-scaling-stroke` skaliert der Strich mit dem viewBox: die Seite ist
   460 px breit, Foto und Overlay 267, und derselbe Wert käme in zwei
   verschiedenen Stärken heraus. */
.schicht svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: rgba(251, 250, 247, 0.62);
  stroke-width: 1;
}
.schicht svg * { vector-effect: non-scaling-stroke; }
/* Je Textzeile ein enges Linienpaar. Voll deckend, sonst verschwimmen sie mit
   der Schraffur darunter, die dieselbe Stärke und dieselbe Deckkraft hat. */
.binde-schrift { stroke: var(--on-dark); }

@media (hover: hover) {
  .aufbau:hover {
    --kipp-x: -14deg;
    --kipp-y: -22deg;
    --tiefe-1: var(--auf-1);
    --tiefe-2: var(--auf-2);
  }
}
/* Ohne Hover steht sie dauerhaft offen: dort gibt es nichts aufzuklappen, und
   der Aufbau ist trotzdem zu sehen. Ein Auftakt entfällt deshalb. */
@media (hover: none) {
  .aufbau {
    --kipp-x: -14deg;
    --kipp-y: -22deg;
    --tiefe-1: var(--auf-1);
    --tiefe-2: var(--auf-2);
  }
}

/* Die vier Merkmale aus der Skizze, als Zeile unter dem Hero. Kästchen statt
   Häkchen: ein Häkchen verspricht Erledigung, hier steht eine Eigenschaft. */
.merkmale {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1px;
  /* Das Innenmaß der Liste muss weg: die Hintergrundfarbe malt bis in die
     Polsterung, und der Standardwert des Browsers stünde als heller Block vor
     der ersten Zelle. */
  padding: 0;
  margin: clamp(48px, 7vw, 84px) 0 0;
  background: var(--rule-dark);
  border-block: 1px solid var(--rule-dark);
}
.merkmale li {
  list-style: none;
  background: var(--ink);
  padding: 18px 20px;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--on-dark-soft);
}
/* Alle vier Zellen mit derselben Polsterung. Die erste stand früher auf 0,
   damit ihre Zeile bündig zur Überschrift darüber sitzt; seit die Trennlinien
   die Zellen sichtbar machen, fiel sie damit aus der Reihe. */
.merkmale b { display: block; font-weight: 500; color: var(--on-dark); }

.fussnote {
  margin: clamp(26px, 3vw, 36px) 0 0;
  font-size: 14px;
  color: var(--ink-soft);
}

/* Der Übergang vom Schaufenster zum Wizard: ein ruhiger Aufruf unter den
   Beispielen, Text links, Knopf rechts. Kein Kasten: die Haarlinie darüber
   trennt ihn vom Schaufenster ab. */
.selbst-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px 32px;
  margin-top: clamp(32px, 4vw, 48px);
  padding-top: clamp(24px, 3vw, 36px);
  border-top: 1px solid var(--rule);
}
.selbst-cta h3 { font-size: clamp(19px, 2.2vw, 24px); margin-bottom: 8px; }
.selbst-cta p { margin: 0; color: var(--ink-soft); font-size: 15.5px; max-width: 52ch; }

/* -------------------------------------------------------------------- Ablauf
   Nummerierte Schritte. Die Nummer steht in der Haarlinie über dem Text, nicht
   in einem Kreis daneben: ein Kreis wäre eine Rundung und eine Verzierung. */

/* Vier Schritte, also vier oder zwei Spalten. `auto-fit` ergab bei mittleren
   Breiten drei, und der vierte Schritt stand allein in einer zweiten Zeile: das
   sah nach einem Nachtrag aus und nicht nach dem Ende eines Ablaufs. */
.schritte {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(24px, 4vw, 52px);
  padding: 0;
  margin: 0;
  list-style: none;
  counter-reset: schritt;
}
.schritte li {
  counter-increment: schritt;
  padding-top: 18px;
  border-top: 1px solid var(--rule);
}
.schritte li::before {
  content: counter(schritt, decimal-leading-zero);
  display: block;
  margin-bottom: 14px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}
.schritte h3 { font-size: 20px; margin-bottom: 8px; }
.schritte p { margin: 0; color: var(--ink-soft); font-size: 15.5px; }

/* Die beiden Wege im Fliesstext. Fett allein reicht nicht: in einem Absatz aus
   gedaempfter Schrift liest sich ein fetter Name als Betonung und nicht als
   Verweis. Deshalb Tintenfarbe plus Unterstrich in der Haarlinienfarbe, wie
   beim Textknopf, und erst beim Zeigen wird der Strich kraeftig. */
.schritte p a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 4px;
  text-decoration-color: var(--rule);
}
.schritte p a:hover { text-decoration-color: currentColor; }

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

/* -------------------------------------------------------------------- Gründe */

/* Vier Gründe, also zwei mal zwei. Aus demselben Grund wie beim Ablauf nicht
   `auto-fit`: drei Spalten ließen den vierten Grund als Nachzügler dastehen. */
.gruende {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(26px, 4vw, 56px);
  padding: 0;
  margin: 0;
  list-style: none;
}
@media (min-width: 700px) { .gruende { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1180px) { .gruende { grid-template-columns: repeat(4, 1fr); } }
/* Die Haarlinie sitzt am inneren Kasten und nicht am Listenelement: das
   Zeichen gehoert darueber, und am li waere es zwangslaeufig darunter. */
.gruende li { display: flex; flex-direction: column; }
.grund-text { padding-top: 18px; border-top: 1px solid var(--rule); }
.band-dunkel .grund-text { border-top-color: var(--rule-dark); }

/* Papierton statt reinem Weiss: das Zeichen soll den Abschnitt fuellen und
   nicht mit der Ueberschrift um die Aufmerksamkeit streiten. Die Strichstaerke
   im SVG entspricht den Haarlinien der Seite. */
.grund-zeichen {
  display: block;
  margin-bottom: 16px;
  color: var(--paper-2);
}
.grund-zeichen svg { display: block; }
.gruende h3 { font-size: clamp(19px, 2vw, 22px); margin-bottom: 10px; }
.gruende p { margin: 0; font-size: 15.5px; color: var(--ink-soft); }
.band-dunkel .gruende p { color: var(--on-dark-soft); }

/* -------------------------------------------------------------------- Preise
   Zwei Pakete nebeneinander, getrennt durch eine Haarlinie statt durch zwei
   Kästen. Der Preis ist die größte Type der Karte: darauf wird geschaut. */

.pakete {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1px;
  background: var(--rule);
  border-block: 1px solid var(--rule);
}
.paket {
  background: var(--paper);
  padding: clamp(28px, 3.5vw, 44px) clamp(24px, 3vw, 40px);
  display: flex;
  flex-direction: column;
}
.band-papier-2 .paket { background: var(--paper-2); }

.paket-name {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Eine Farbe in den Preiskarten. Der Messingakzent stand hier am Paketnamen
     und an je fünf Aufzählungsquadraten, also zehnmal auf engem Raum, und der
     Preis darunter musste sich dagegen behaupten. */
  color: var(--ink-soft);
  margin: 0 0 18px;
}
.preis {
  font-size: clamp(38px, 5vw, 54px);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  margin: 0;
  font-variant-numeric: tabular-nums;
}
/* **Der Zusatz steht auf einer eigenen Zeile, nicht hinter dem Betrag.**
   Inline gesetzt floss er einfach weiter und brach mitten hinein: aus
   "einmalig, sechs oder zwölf Monate" wurde "einmalig, je" plus eine zweite
   Zeile "Laufzeit", und bei "ab 1.490 €" rutschte sogar das Eurozeichen von
   seiner Zahl weg. Der Betrag ist die größte Type der Karte, er soll als ein
   Block stehen, und die Erklärung darunter.

   Der Abstand zur Zahl ist damit vertikal. Er bleibt klein, weil beides
   zusammengehört: eine Angabe mit ihrer Einheit, nicht zwei Aussagen. */
.preis small {
  display: block;
  font-size: 0.34em;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.35;
  margin-top: 0.5em;
  color: var(--ink-soft);
  /* Der Betrag darüber ist stark negativ spationiert; ohne diese Zeile erbt
     die Erklärung das und wirkt gedrängt. */
  text-wrap: pretty;
}
/* Nur der beschreibende Absatz. `.paket > p` allein gewinnt nach Spezifität
   gegen `.preis`, `.paket-name` und `.paket-fuss` und setzte den Preis auf
   Fließtextgröße: sieht aus wie ein vergessener Stil, ist ein zu breiter
   Selektor. */
.paket > p:not(.preis):not(.paket-name):not(.paket-fuss) {
  color: var(--ink-soft);
  font-size: 15.5px;
  /* Die Karte nutzt die volle Spalte, der Satz darin nicht: bei 636 Pixeln
     Kartenbreite liefe er sonst über 70 Zeichen, und das liest sich in 15,5
     Pixeln nicht mehr angenehm. */
  max-width: 40ch;
  margin: 14px 0 0;
}

.paket ul {
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
  font-size: 15px;
}
.paket li {
  padding: 10px 0 10px 22px;
  border-top: 1px solid var(--rule);
  position: relative;
}
/* Ein kleines Quadrat statt eines Punktes oder Häkchens: dieselbe Geometrie
   wie der Rest der Seite. */
.paket li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 19px;
  width: 7px;
  height: 7px;
  background: var(--ink);
}
.paket-fuss { margin-top: auto; padding-top: 28px; }

/* Ab drei Spalten teilen sich die Karten dieselben Zeilen: Name, Preis,
   Beschreibung, Liste, Knopf.

   Vorher hing die Ausrichtung an der Textlaenge. Der Kommentar an der
   Beschreibung sagt das auch offen ("bleiben ungefaehr gleich lang, damit die
   Listen darunter auf aehnlicher Hoehe beginnen"), und genau daran zerbricht
   es beim naechsten Umformulieren. Nachgemessen: von 70 dreispaltigen Breiten
   zwischen 900 und 1600 Pixeln standen 27 schief, um bis zu 74 Pixel.

   Eine Preisliste ist zum Vergleichen da. Wenn die Zeilen nicht auf gleicher
   Hoehe beginnen, vergleicht man Abstaende statt Leistungen.

   `subgrid` statt einer Mindesthoehe am Absatz: eine Zahl dort waere wieder
   an die heutige Copy gebunden und laege beim naechsten Satz daneben. */
@media (min-width: 68rem) {
  .pakete {
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto auto auto 1fr auto;
    /* Die Haarlinien zwischen den Karten entstehen aus dem Spaltenabstand vor
       der Flaeche des Rasters. Einen Zeilenabstand darf es deshalb nicht
       geben: der zoege dieselbe Linie quer durch jede Karte. */
    gap: 0 1px;
  }
  .paket {
    display: grid;
    grid-row: span 5;
    grid-template-rows: subgrid;
  }
  /* In der Flex-Fassung schob `margin-top: auto` den Knopf nach unten. Im
     Raster erledigt das die 1fr-Zeile der Liste, und `auto` wuerde ihn
     stattdessen in seiner Zeile zentrieren. */
  .paket-fuss { margin-top: 0; }
}

/* ------------------------------------------------------------------ Umfang */

/* **Das Paar bleibt ein Kasten.** Vorher stand hier `display: contents`, womit
   Bezeichnung und Beschreibung als **eigene** Rasterzellen lagen. Das ergibt
   genau dann „Titel, darunter Text", wenn das Raster zwei Spalten hat: der
   Quelltextfluss füllt Zeile für Zeile. Bei drei Spalten fließt dasselbe Markup
   zu `dt, dd, dt / dd, dt, dd`, und dann steht „Ausgabeformate" neben dem Text
   der Eingabefelder.

   `auto-fit` mit 260 Pixeln erreicht drei Spalten ab etwa 1100 Pixeln
   Fensterbreite, der Fehler war also auf jedem größeren Bildschirm zu sehen und
   auf einem Laptop nicht. Genau dieselbe Falle ist im Panel schon einmal
   aufgetreten und steht dort seit August in CLAUDE.md; hier war sie geblieben.

   Zwei Spalten statt `auto-fit`: sechs Einträge gehen in zwei oder drei Spalten
   auf, und bei drei bleibt in der letzten Reihe nichts übrig. Die Reihenhöhe
   richtet sich damit nach dem längsten Eintrag der Reihe, was bei Fließtext
   ruhiger aussieht als eine Reihe aus drei verschieden hohen Kästen. */
.umfang {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(22px, 3vw, 34px) clamp(28px, 4vw, 60px);
  margin: 0;
  align-content: start;
}
@media (min-width: 760px) { .umfang { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Die Haarlinie sitzt jetzt am Paar und nicht mehr an der Bezeichnung: seit
   links ein Zeichen steht, reichte eine Linie an der Bezeichnung nur noch über
   den Text und hörte vor dem Zeichen auf. */
.umfang > div {
  padding-top: 14px;
  border-top: 1px solid var(--rule);
}

/* Zeichen und Bezeichnung in einer Zeile, der Text darunter hängend eingezogen.
   Kein Raster mit dem Zeichen über beide Zeilen: das verlangte `display:
   contents` an der Bezeichnung, also genau die Konstruktion, die den Abschnitt
   ab drei Spalten zerrissen hat. Ein Einzug ist eine Zahl und kann nicht
   umbrechen. */
.umfang dt {
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 700;
  font-size: 15.5px;
}
.umfang dt svg { flex: none; color: var(--ink-soft); }
/* Der Abstand nach unten kommt jetzt aus dem `gap` des Rasters. Bliebe er hier
   als `margin`, zählte er innerhalb des Kastens und käme zum Zeilenabstand
   hinzu: die Reihen stünden doppelt so weit auseinander wie gedacht. */
.umfang dd {
  margin: 4px 0 0;
  /* Breite des Zeichens plus der Abstand darüber: der Text beginnt damit genau
     unter der Bezeichnung und nicht unter dem Zeichen. */
  padding-left: 40px;
  color: var(--ink-soft);
  font-size: 15px;
}

/* ------------------------------------------------------------------ Fragen
   Aufklappen kann der Browser selbst, details und summary brauchen kein
   Skript, und der Text steht dabei im Dokument statt in einer Zeile
   JavaScript, die ihn nachlädt. Für einen Crawler ist das der Unterschied
   zwischen lesbar und unsichtbar.

   Zwei Spalten auf breiten Schirmen. Bewusst kein Raster aus Paaren wie beim
   Leistungsumfang daneben: eine aufgeklappte Antwort ändert ihre Höhe, und
   ein display von contents risse die Zeilen dann auseinander. */
.fragen {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0 clamp(28px, 4vw, 64px);
  align-content: start;
}
@media (min-width: 820px) { .fragen { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.frage {
  border-top: 1px solid var(--rule);
  padding: 14px 0 16px;
}

/* Der eigene Marker ist ein Kreuz, aus dem ein Minus wird: zwei Striche in
   CSS, keine Datei, keine Schrift. Das Dreieck des Browsers muss dafür weg,
   und zwar auf zwei Wegen: über list-style für die heutigen Browser und über
   den Webkit-Pseudoelement-Marker für ältere Fassungen von Safari. */
.frage summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
  /* **Die Fragen bleiben einzeilig, und deshalb ist die Groesse gerechnet.**
     Der engste Fall ist nicht der Zweispalter, sondern ein 360 Pixel breites
     Telefon: dort bleiben nach Rand, Zeichen und Abstand 290 Pixel fuer den
     Text, und der Wert unten ist die Untergrenze, bei der die laengste Frage
     hineinpasst. Ab 1000 Pixeln steht die Groesse auf 16, dort waechst die
     Spalte schneller als die Schrift.

     Die zweite Haelfte der Zusicherung steht im Katalog: keine Frage darf
     laenger sein als die Spalte traegt. `test/faq.test.js` haelt die Grenze
     fest, sonst bricht die naechste laengere Frage die Regel unbemerkt. */
  font-size: clamp(14px, 1.6vw, 16px);
  line-height: 1.35;
  /* Bleibt als Netz: bricht eine Frage doch einmal um, stehen zwei ausgewogene
     Zeilen da und nicht eine lange plus ein Wort. */
  text-wrap: balance;
}
.frage summary::-webkit-details-marker { display: none; }

/* Das Zeichen haengt an einem eigenen Kasten, damit die Frage darueber
   umbrechen kann, ohne dass es mitwandert. */
.frage summary::after {
  content: "";
  flex: 0 0 14px;
  height: 14px;
  margin-top: 5px;
  background:
    linear-gradient(var(--ink), var(--ink)) center / 14px 1.5px no-repeat,
    linear-gradient(var(--ink), var(--ink)) center / 1.5px 14px no-repeat;
  transition: background-size 160ms ease;
}
/* **Der waagerechte Strich bleibt stehen, der senkrechte schrumpft in die
   Mitte.** Vorher drehte sich der ganze Kasten um 90 Grad, und weil dabei der
   *waagerechte* Strich stehenblieb, stand das Minus im offenen Zustand senkrecht.
   Eine Drehung ist hier ohnehin die falsche Bewegung: das Ziel ist ein Strich,
   der schon da ist, und der darf sich nicht mitdrehen. `background-size` ist
   ein interpolierbarer Wert, die Bewegung bleibt also erhalten. */
.frage[open] summary::after { background-size: 14px 1.5px, 1.5px 0; }
@media (prefers-reduced-motion: reduce) { .frage summary::after { transition: none; } }

.frage summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

.frage p {
  margin: 10px 0 0;
  color: var(--ink-soft);
  font-size: 15.5px;
  max-width: 62ch;
  text-wrap: pretty;
}

/* ------------------------------------------------------------------ Kontakt */

/* Zwei Zeilen, nicht drei. Bei 18ch brach der Satz nach jedem Drittel um,
   während rechts daneben die halbe Spalte leer stand. `balance` verteilt die
   Wörter gleichmäßig, statt die letzte Zeile als Rest übrig zu lassen. */
.kontakt-kopf {
  font-size: clamp(30px, 5vw, 52px);
  /* Als <p> erbt der Satz die 1,6 des Fließtexts. Bei 52 Pixeln sind das 83
     Pixel Zeilenabstand, und die zwei Zeilen fallen auseinander. Derselbe Wert
     wie bei h1, h2, h3. */
  line-height: 1.08;
  letter-spacing: -0.035em;
  max-width: 30ch;
  text-wrap: balance;
  margin: 0 0 20px;
}
.kontakt-wege {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(16px, 3vw, 28px);
  align-items: center;
  margin-top: clamp(30px, 4vw, 42px);
}

/* Die Adresse steht offen im Quelltext, nicht hinter einem Knopf.
   Verschleierung per JavaScript spart etwas Spam und kostet Anfragen: auf einer
   Geschäftsseite ist das der falsche Tausch, und sie wäre zugleich der einzige
   Grund, hier überhaupt ein Skript zu laden. */
.mail-klar {
  font-family: var(--mono);
  font-size: clamp(15px, 2vw, 18px);
  letter-spacing: -0.01em;
  text-underline-offset: 5px;
}

/* --------------------------------------------------------------------- Fuß */

.fuss {
  background: var(--ink-2);
  color: var(--on-dark-soft);
  padding-block: clamp(40px, 5vw, 64px);
  font-size: 14px;
}
.fuss-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px 32px;
}
.fuss .logo-svg { width: 112px; height: auto; color: var(--on-dark); }
.fuss-links { display: flex; flex-wrap: wrap; gap: 24px; }
.fuss a { text-decoration: none; }
.fuss a:hover { color: var(--on-dark); text-decoration: underline; text-underline-offset: 4px; }

/* ------------------------------------------------------------------ Bewegung
   Ein einziger inszenierter Moment beim Laden, danach Ruhe. Die Abschnitte
   blenden beim Scrollen auf, aber nur wo `animation-timeline` da ist: ohne
   @supports stünde in Firefox eine unsichtbare Seite. */

@media (prefers-reduced-motion: no-preference) {
  .auftritt > * {
    animation: hoch 700ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  }
  .auftritt > :nth-child(1) { animation-delay: 60ms; }
  .auftritt > :nth-child(2) { animation-delay: 140ms; }
  .auftritt > :nth-child(3) { animation-delay: 220ms; }
  .auftritt > :nth-child(4) { animation-delay: 300ms; }

  /* Zeichnung und Merkmale stehen außerhalb von `.auftritt` und brauchen
     deshalb ihre eigene Verzögerung. Die Zeichnung blendet nur ein und wird
     nicht verschoben: ein Transform auf dem Vorfahren einer `perspective`
     rechnet die ganze 3D-Szene mit. */
  .hero-figur { animation: einblenden 700ms ease 220ms backwards; }
  .hero .merkmale { animation: hoch 700ms cubic-bezier(0.16, 1, 0.3, 1) 380ms backwards; }

  /* Der Auftakt: die Zeichnung kommt aufgefächert an, hält knapp eine Sekunde
     und legt sich dann zusammen. Wer nichts tut, hat die Erklärung einmal
     gesehen. Bewusst ohne Füllung: mit `forwards` gewänne sie als Animation
     dauerhaft gegen die Transition, und der Hover danach bliebe wirkungslos.
     `backwards` allein zeigt den Anfangswert schon während der Verzögerung. */
  /* Nicht dieselbe Kurve wie beim Hover. `cubic-bezier(0.16, 1, 0.3, 1)` zieht
     den ersten Kontrollpunkt auf y=1 und ist damit fast sofort am Ziel: gut
     für eine Reaktion auf den Zeiger, aber der Auftakt sah dadurch aus, als
     schnappe er zu und driftete danach nur noch. `cubic-bezier(0.5, 0, 0.25, 1)`
     läuft weich an und weich aus. Die Bewegung dauert jetzt 2,1 statt 1,4
     Sekunden, das Halten davor 0,9. */
  /* Drei Animationen statt einer, weil jede auf dem `transform` ihres eigenen
     Elements läuft. Gleiche Dauer, gleiche Verzögerung, gleiche Kurve, also
     laufen sie synchron. */
  @media (hover: hover) {
    .aufbau-tiefe { animation: auftakt-kippen var(--auftakt) backwards; }
    .schicht-foto { animation: auftakt-foto var(--auftakt) backwards; }
    .schicht-overlay { animation: auftakt-overlay var(--auftakt) backwards; }
  }

  /* Der Test fragt **beide** Eigenschaften ab, die hier gebraucht werden.
     `animation-timeline` allein genügt nicht: ein Browser, der den Namen
     kennt, aber `animation-range` nicht auswertet, bekäme sonst eine
     Animation, deren Bereich er ignoriert. Das `backwards` in `both` hält
     dann den ersten Keyframe fest, und der ist `opacity: 0`. */
  @supports (animation-timeline: view()) and (animation-range: entry 5%) {
    .beim-scrollen {
      animation: hoch 1ms linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 22%;
    }

    /* Auf dem Telefon klappt die Zeichnung beim Vorbeiscrollen **auf**.
       Ohne Zeiger gibt es dort nichts zu überfahren, und der Bildlauf ist die
       Geste, die ohnehin stattfindet; sie kostet keinen Sensor, keine
       Erlaubnis und kein Skript.

       **Die Richtung war vorher falsch herum.** Die erste Fassung drehte nur
       zwanzig Grad um den offenen Ruhewinkel, von -32 auf -12 Grad. Das sieht
       aus, als lege sich die Zeichnung beim Scrollen zusammen, also genau
       gegen die Aussage: erst am Ende steht das fertige Bild, davor die
       Einzelteile. Jetzt beginnt sie flach, und die Ebenen fahren auseinander.

       **Der Bereich ist kurz, nicht der ganze Durchlauf.** Mit
       `animation-range: cover` verteilte sich die Bewegung über die gesamte
       Strecke durch das Fenster und war nicht fertig, wenn die Zeichnung oben
       hinausläuft. Bei 375x812 gemessen: die Zeichnung steht beim Laden bei
       22 Prozent des Durchlaufs, ist also noch flach, und bei 60 Prozent ist
       ihre Oberkante gut 120 Pixel unter dem Fensterrand, sie steht dann
       vollständig offen im Bild.

       Nur `hover: none`: auf dem Desktop gehört die Bewegung dem Zeiger, und
       die Anschlusslinien der Beschriftungen sind gegen die dortigen Winkel
       gemessen (27, 169 und 234 Pixel). Ein zweiter Antrieb auf denselben
       Werten ließe sie ins Leere zeigen.

       Bewegt wird `transform` und nicht `--kipp-y`. Eine Variable im Übergang
       läuft ausnahmslos im Hauptthread, und `vector-effect: non-scaling-stroke`
       rastert bei jeder Größenänderung alle Striche neu; das ist weiter oben
       schon einmal teuer geworden.

       Drei Animationen statt einer, weil jede auf dem `transform` ihres
       eigenen Elements läuft. Gleicher Bereich, also laufen sie synchron. */
    @media (hover: none) {
      .aufbau-tiefe,
      .schicht-foto,
      .schicht-overlay {
        animation-duration: 1ms;
        animation-timing-function: linear;
        animation-fill-mode: both;
        animation-timeline: view();
        animation-range: cover 25% cover 60%;
      }
      .aufbau-tiefe { animation-name: aufklappen-kippen; }
      .schicht-foto { animation-name: aufklappen-foto; }
      .schicht-overlay { animation-name: aufklappen-overlay; }
    }
  }
}

@keyframes hoch {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

@keyframes einblenden {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Aus dem fertigen Bild werden die Einzelteile: flach und deckungsgleich am
   Anfang, aufgefächert am Ende. Dieselben Zielwerte wie beim Hover, aber
   ausgeschrieben und nicht als Variable: ein Keyframe, der `var(--kipp-y)`
   läse, bekäme den Wert des Zustands, den er gerade selbst überschreibt. Die
   Tiefen dürfen als Variable stehen, sie werden hier nicht umgeschaltet. */
@keyframes aufklappen-kippen {
  from { transform: rotateX(0deg) rotateY(0deg); }
  to { transform: rotateX(-14deg) rotateY(-22deg); }
}
@keyframes aufklappen-foto {
  from { transform: translateZ(0px); }
  to { transform: translateZ(var(--auf-1)); }
}
@keyframes aufklappen-overlay {
  from { transform: translateZ(0px); }
  to { transform: translateZ(var(--auf-2)); }
}

/* Die ersten 30 Prozent halten den offenen Stand, danach legt sich alles
   zusammen. Die Zielwerte stehen hier ausgeschrieben und nicht als Variable:
   ein Keyframe, der `var(--kipp-x)` läse, bekäme den Wert des Zustands, den er
   gerade selbst überschreibt. */
@keyframes auftakt-kippen {
  0%, 30% { transform: rotateX(-14deg) rotateY(-22deg); }
  100% { transform: rotateX(0deg) rotateY(0deg); }
}
@keyframes auftakt-foto {
  0%, 30% { transform: translateZ(var(--auf-1)); }
  100% { transform: translateZ(0px); }
}
@keyframes auftakt-overlay {
  0%, 30% { transform: translateZ(var(--auf-2)); }
  100% { transform: translateZ(0px); }
}

/* Keine Bewegung, aber auch keine verschlossene Zeichnung: sie steht offen da,
   derselbe Zustand wie ohne Hover. */
@media (prefers-reduced-motion: reduce) {
  .aufbau {
    --kipp-x: -14deg;
    --kipp-y: -22deg;
    --tiefe-1: var(--auf-1);
    --tiefe-2: var(--auf-2);
  }
  .aufbau-tiefe,
  .schicht-foto,
  .schicht-overlay { transition: none; }
  .marken { opacity: 1; transition: none; }
}

/* --------------------------------------------------------------- Rechtsseite
   Eine Textseite, kein Auftritt. Schmale Spalte, ruhige Zwischentitel, und die
   Adresse in einer getönten Fläche statt in einem Kasten mit Rand. */

.recht { max-width: 72ch; padding-block: clamp(40px, 6vw, 72px); }
.recht h1 { font-size: clamp(28px, 4vw, 40px); margin-bottom: 10px; }
.recht .stand {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  margin: 0 0 clamp(32px, 4vw, 48px);
}
.recht .massgeblich {
  background: var(--paper-2);
  padding: 12px 18px;
  /* --ink, wie .anschrift auf demselben Grund: bei 14px ist das die kleinste
     Schrift auf dem hellsten Rand der Seite, und --ink-soft läge mit 4,70:1
     zu nah an der WCAG-AA-Grenze von 4,5:1. */
  color: var(--ink);
  font-size: 14px;
}
.recht h2 {
  font-size: 20px;
  margin: clamp(38px, 5vw, 54px) 0 14px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
}
.recht h3 { font-size: 16px; margin: 26px 0 8px; }
.recht p, .recht li { color: var(--ink-soft); font-size: 15.5px; }
.recht p { margin: 0 0 14px; }
.recht ul { margin: 0 0 16px; padding-left: 20px; }
.recht li { margin-bottom: 8px; }
.recht strong { color: var(--ink); font-weight: 700; }
.recht a { color: var(--ink); text-underline-offset: 3px; }

.anschrift {
  background: var(--paper-2);
  padding: 20px 24px;
  margin-bottom: 18px;
  color: var(--ink);
  font-size: 15.5px;
  line-height: 1.7;
}
.recht code {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--paper-2);
  padding: 2px 5px;
}
.zurueck {
  display: inline-block;
  margin-bottom: 32px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  text-decoration: none;
}
.zurueck:hover { color: var(--ink); }

/* ------------------------------------------------------------ Demo im Rahmen

   Die Demoseite steckt hinter einem `details`, damit ein zweites vollstaendiges
   Werkzeug nicht im Fluss der Seite steht. Kein Skript: `details` und `summary`
   koennen das allein, und der Onepager bleibt damit skriptfrei. */

/* ------------------------------------------------------------- Schaufenster

   Eine Spur zum Wischen statt drei fester Kacheln. Scroll-Snap macht das
   Blaettern nativ: auf einem Telefon fuehlt es sich an wie erwartet, ohne dass
   eine Bibliothek Beruehrungen nachbaut.

   Das Padding links und rechts ist die halbe Spurbreite. Ohne es koennte das
   erste und das letzte Bild nie in der Mitte stehen, weil der Scrollbereich
   dort endet. */
/* Die Spur ist schmaler als ihr Inhalt, sonst gibt es nichts zu blaettern:
   stuenden alle Kacheln gleichzeitig vollstaendig im Bild, haette weder das
   Einrasten noch die Vergroesserung der Mitte einen Anlass. Die Nachbarn sind
   deshalb angeschnitten sichtbar, und das zeigt zugleich, dass es weitergeht. */
.schau {
  /* Wie viele Kacheln gleichzeitig im Fenster stehen. Der Wert steht **hier**
     und nicht im Skript: schau.js liest ihn aus und rechnet Kachelbreite,
     Mitte und Klonzahl daraus. Wer den Haltepunkt verschiebt, verschiebt
     damit beides zugleich, und die Zahl kann nie auseinanderlaufen.

     Der Abstand liegt als eigenes Token daneben, weil er in die Kachel
     hineingehört und nicht als `gap` dazwischen: ein `gap` machte die Kachel
     schmaler als ihren Anteil am Fenster, und der Schlitten verschiebt sich in
     Prozent seiner eigenen Breite. */
  --pro-sicht: 1;
  --kachel-abstand: 0px;
  max-inline-size: 100%;
  margin-inline: auto;
}
@media (min-width: 900px) {
  .schau {
    --pro-sicht: 3;
    --kachel-abstand: clamp(16px, 2vw, 28px);
  }
}

/* Die Pfeile liegen über dem Bild an den Rändern, statt der Kachel Breite
   wegzunehmen. Nur mit Skript erreichbar — ohne Skript wischt man. */
/* Die Pfeile standen bis zum 2026-09-02 absolut über den Kacheln, deshalb war
   dieser Kasten der Bezug. Sie stehen jetzt neben dem Telefon und sind dort
   gewöhnliche Nachbarn in einer Reihe. */
.schau-pfeil {
  flex: none;
  inline-size: 44px;
  block-size: 44px;
  border-radius: 50%;
  border: 1px solid var(--ink-soft);
  background: var(--paper);
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.18);
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.schau-pfeil:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.schau-pfeil:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
/* Auf schmalen Screens sind die Pfeile weg: dort wischt man. */
@media (max-width: 560px) {
  .schau-pfeil { display: none; }
}

/* Ohne Skript: ein gewöhnlicher Scrollbereich, jede Kachel füllt die Breite
   und rastet beim Wischen ein. Mit Skript (`.schau-slider`) wird der Scroll
   abgeschaltet und die Kacheln schieben sich per `transform`.

   Fenster und Spur sind deshalb **zwei** Kästen. Ein `overflow` auf der Spur
   selbst schnitte im Schiebemodus nichts ab: der Klippbereich gehört zur Box
   und wandert mit ihrem `transform` mit, und die Spur liefe samt Fenster aus
   der Spalte heraus. Ohne Skript trägt das Fenster nichts bei, dort scrollt
   die Spur selbst. */
.schau-spur {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.schau-spur::-webkit-scrollbar { display: none; }
/* Die Bilder haben verschiedene Seitenverhältnisse. Nebeneinander werden sie
   deshalb an der Oberkante ausgerichtet statt auf gleiche Höhe gezogen: ein
   erzwungenes Verhältnis schnitte das Hochformat an, und angeschnitten zeigt
   ein Schaufenster nicht mehr, was es zeigen soll. */
.schau-spur { align-items: flex-start; }
.schau-spur > li {
  flex: 0 0 calc(100% / var(--pro-sicht));
  padding-inline: calc(var(--kachel-abstand) / 2);
  scroll-snap-align: start;
}
.schau-spur:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

/* Der Schiebemodus, den schau.js einschaltet. `.zieht` während des Wischens:
   da klebt die Kachel am Finger statt zu animieren.

   `clip` statt `hidden`: `hidden` machte aus dem Fenster einen Scrollcontainer,
   und ein Feld, das per Tabulator in einer verdeckten Kachel Fokus bekommt,
   schöbe ihn seitlich weg. Die Spur stünde danach dauerhaft neben ihrem
   `transform`. */
.schau-slider .schau-fenster {
  overflow: clip;
}
.schau-slider .schau-spur {
  overflow: visible;
  scroll-snap-type: none;
  transition: transform 420ms cubic-bezier(0.22, 0.61, 0.36, 1);
  /* **Ohne diese Zeile gibt es kein Wischen auf dem Telefon.**
     Ohne Skript scrollt die Spur nativ, und die Geste gehört dem Browser. Im
     Schiebemodus ist der Scroll abgeschaltet, die Geste soll uns gehören, und
     dafür muss sie angemeldet sein: sonst deutet der Browser eine waagerechte
     Bewegung weiterhin als seine eigene (auf iOS als „zurück"), bricht den
     Pointer mit `pointercancel` ab, und `pointermove` hört mitten in der Geste
     auf zu feuern.
     `pan-y` und nicht `none`: senkrechtes Scrollen muss dem Browser bleiben,
     sonst klebt die Seite fest, sobald ein Finger auf einer Kachel landet. */
  touch-action: pan-y;
}
.schau-slider .schau-spur.zieht {
  transition: none;
}

/* Stehen drei Kacheln nebeneinander, ist die mittlere die gewählte: sie
   bestimmt, welche Demo im Rahmen darunter läuft. Ohne einen Unterschied wäre
   diese Kopplung unsichtbar, und der Rahmen wechselte scheinbar grundlos.

   Die Nachbarn sind deshalb etwas kleiner, aber voll sichtbar: es sind echte
   Ausgaben aus dem Renderer, und ein abgeblendetes Beispiel ist ein halb
   gezeigtes. Über `scale` und nicht über die Kachelbreite, weil eine
   Transformation den Fluss nicht anfasst: die Kachel behält ihren Drittelanteil
   am Fenster, und die Prozentrechnung des Schlittens bleibt gültig.

   `top` als Ursprung hält die Oberkanten bündig. Die Bilder sind verschieden
   hoch (1:1 neben 4:5), und die Oberkante ist die einzige Linie, die sie
   gemeinsam haben. */
.schau-slider .schau-spur > li {
  transition: scale 260ms ease;
  transform-origin: center top;
}
.schau-slider .schau-spur > li:not(.ist-gewaehlt) {
  scale: 0.9;
}
@media (prefers-reduced-motion: reduce) {
  .schau-slider .schau-spur > li { transition: none; }
}

.beispiel { margin: 0; }
.beispiel img {
  inline-size: 100%;
  block-size: auto;
  display: block;
  border-radius: 2px;
}
/* Punkte darunter. Ankerverweise, damit sie ohne Skript funktionieren und mit
   der Tastatur erreichbar sind. */
.schau-punkte {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: clamp(20px, 3vw, 32px);
}
.schau-punkte a {
  inline-size: 9px;
  block-size: 9px;
  border-radius: 50%;
  border: 1px solid var(--ink-soft);
  /* Der Anfasser ist groesser als der Punkt: neun Pixel trifft niemand mit dem
     Daumen. */
  box-shadow: 0 0 0 10px transparent;
}
.schau-punkte a:hover { background: var(--ink-soft); }
.schau-punkte a[aria-current="true"] { background: var(--ink); border-color: var(--ink); }

/* Die Demo steht über dem Schaufenster und ist immer offen. Der Abstand liegt
   deshalb unten, zwischen ihr und der Auswahl, die sie steuert. */
.demo { margin-bottom: clamp(28px, 3.5vw, 44px); }
.demo-hinweis {
  margin: 0 0 14px;
  text-align: center;
  color: var(--ink-soft);
  font-size: 15px;
}

/* Pfeil, Telefon, Pfeil. Der Abstand wächst mit der Breite, damit die Knöpfe
   auf einem grossen Schirm nicht am Gerät kleben. */
.demo-zeile {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2.5vw, 36px);
}

/* Eine Kachel holt sich mit einem Klick in die Mitte, siehe public/schau.js.
   Die gewählte hat nichts mehr zu holen und behält den normalen Zeiger. */
.schau-slider .schau-spur li { cursor: pointer; }
.schau-slider .schau-spur li.ist-gewaehlt { cursor: default; }

.demo-rahmen {
  margin-top: 20px;
  /* Als Kind der Reihe misst der Rahmen sonst seinen Inhalt und bleibt
     schmaler als die Spalte. Unterhalb von 760 Pixeln soll er sie füllen,
     darüber gilt die feste Gerätebreite weiter unten. */
  flex: 1 1 auto;
  min-inline-size: 0;
  border: 1px solid var(--rule);
  background: var(--paper);
  /* Der Rahmen traegt die Hoehe, nicht der iframe: eine fremde Seite kann ihre
     eigene Hoehe nicht nach aussen melden, ohne dass beide Seiten miteinander
     reden, und genau das soll hier niemand tun muessen. */
  block-size: min(80vh, 900px);
}
.demo-rahmen iframe {
  inline-size: 100%;
  block-size: 100%;
  border: 0;
  display: block;
  transition: opacity 200ms ease;
}
/* Beim Wechsel der Demo wird aus- und wieder eingeblendet, siehe
   public/schau.js. Ohne das zeigt der Rahmen für einen Moment eine halb
   aufgebaute Seite: erst weiss, dann Text ohne Schrift, dann das Bild. */
.demo-rahmen iframe.wechselt { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .demo-rahmen iframe { transition: none; } }

/* Auf schmalen Geraeten haelt das Telefon nur auf: dort **ist** der Bildschirm
   schon ein Telefon, und ein gezeichneter Rahmen darum kostet nur Platz. */
.demo-telefon { display: none; }

/* ------------------------------------------------------------- Im Geraet
   Ab Tablettbreite steckt die Demo in einem gezeichneten Telefon. Die Zahlen
   sind nicht geschaetzt, sondern aus phone.svg gerechnet: die innerste Kante
   liegt dort bei x 74.943 bis 926.185 und y 94.879 bis 1895.79 im viewBox
   1001x2001, nach Abzug der beiden Gruppen-Verschiebungen (-593+8.235, -79).
   Daraus folgen die Prozentwerte unten. Der Eckradius von 99.29 Einheiten ist
   horizontal und vertikal verschieden gross ausgedrueckt, damit er rund bleibt
   und nicht zur Ellipse wird. */
@media (min-width: 760px) {
  .demo-rahmen {
    position: relative;
    flex: none;
    inline-size: min(380px, 100%);
    /* Kein `margin-inline: auto` mehr: in der zentrierten Reihe aus Pfeil,
       Telefon, Pfeil schluckte der automatische Rand den ganzen freien Platz,
       und die Knöpfe standen an den Rändern der Spalte statt neben dem Gerät. */
    aspect-ratio: 1001 / 2001;
    block-size: auto;
    border: 0;
    background: none;
  }

  .demo-telefon {
    display: block;
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    /* Das Geraet ist Zierrat. Klicks gehen durch es hindurch an die Demo, sonst
       liesse sich die Seite im Rahmen nicht bedienen. */
    pointer-events: none;
    z-index: 1;
  }

  .demo-rahmen iframe {
    position: absolute;
    left: 7.487%;
    top: 4.742%;
    inline-size: 85.039%;
    block-size: 90.001%;
    border-radius: 11.664% / 5.514%;
  }
}
