Zum Inhalt
← Seitenübergänge

Seitenname als Übergang (Typo-Overlay, kinetische Schrift)

Beim Klick fährt ein Vorhang in Markenfarbe hoch, der Name der Zielseite steigt groß aus der Maske und gibt die neue Seite wieder frei.

Beim Klick auf einen Link deckt ein Vorhang die Seite. Darin erscheint groß der Name der Zielseite: Zeichen für Zeichen aus einer Maske, eingeblendet oder als Wand aus bewegten Zeilen. Der Name hält kurz, während die neue Seite lädt. Dann verlässt er die Bühne, der Vorhang öffnet sich nach oben und die neue Seite ist da.

Navigation bekommt eine Überschrift. Die Ladezeit ist gefüllt statt leer, und die Hausschrift steht für einen Moment allein im Rampenlicht.

Wann einsetzen

Bei Marken mit starker Hausschrift, bei Studios, Event- und Kampagnenseiten und bei Websites mit wenigen, klar benannten Hauptseiten.

Wann nicht

Bei tiefen Informationsarchitekturen mit häufigen Seitenwechseln (zu langsam), bei langen Seitennamen und überall, wo Besucher schnell zum Ziel wollen, etwa in Shops oder Spendenstrecken.

Varianten

  • Maske: Zeichen steigen einzeln aus ihrer Maske und verlassen sie nach oben (Osmo, Page Name Transition mit Wipe)
  • Einblenden: Name blendet ein und rückt 50 px nach oben, dann linear aus (Dennis Snellenberg, gemessen)
  • Kinetisch: Zeilen aus dem Namen fahren gegenläufig ein und beim Öffnen nach vorn (Codrops, kinetische Typo)
  • Typewriter oder Scramble: Name wird getippt oder aus Zufallszeichen aufgelöst

Umsetzung

Den Zielnamen aus einem data-Attribut oder dem Linktext lesen und per SplitText mit mask:'chars' zerlegen. Timeline: Panel rein (yPercent 100 → 0, power3.inOut), Zeichen von yPercent 100 auf 0 (power4.out, stagger 0.03), halten bzw. auf den Fetch warten, Zeichen auf yPercent -100, Panel raus. In einer SPA oder mit Barba.js läuft der Seitentausch unter dem geschlossenen Vorhang. Mit nativen Cross-document View Transitions (@view-transition { navigation: auto; }) kennt die neue Seite den Namen nicht von selbst: Ihn im pageswap-Event in sessionStorage legen und im pagereveal-Event auslesen. Fallstrick: Erst nach document.fonts.ready teilen und messen, sonst springt die Schrift. Die Demo simuliert die Seiten als Zustände in einer Datei.

Robust umsetzen

Telefon. Den Namen per JS oder clamp() auf die Breite setzen. Auf schmalen Bildschirmen ganze Wörter statt Zeichen animieren.

Ohne Bewegung. Den Namen ohne Bewegung kurz einblenden oder den Übergang ganz weglassen.

Barrierefrei. Den Vorhang mit aria-hidden versehen, denn die Zielseite trägt ihren echten Titel selbst. Nach dem Wechsel den Fokus auf die Überschrift der neuen Seite setzen und document.title aktualisieren. So gibt es keine doppelte Ansage.

Code

<!DOCTYPE html>
<!--
  Seitenname als Übergang (Typo-Overlay, kinetische Schrift)
  Fundus · Seitenübergänge · https://cecodesign.de/lab/fundus/seitenuebergaenge/seitenname-typo-uebergang/
  Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Seitenname als Übergang · Fundus-Demo</title>
<script>document.documentElement.classList.add('js');if(matchMedia('(prefers-reduced-motion: reduce)').matches)document.documentElement.classList.add('reduziert')</script>
<style>
@font-face { font-family: 'Big Shoulders'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/big-shoulders:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-display: swap; }
@font-face { font-family: 'Archivo'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/archivo:vf@latest/latin-full-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-stretch: 62% 125%; font-display: swap; }
@font-face { font-family: 'Geist Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/geist-mono:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-display: swap; }

:root {
  /* Uferbande e.V. – ein tiefer Markenton (Havel-Petrol), helle Töne derselben Farbe, warmes Grau.
     Gelb gehört nur dem Spenden-Button. */
  --marke: #073b4c; --marke-2: #0f5568; --marke-3: #3d7f90;
  --hell: #edf5fa; --hell-2: #cfe3ec;
  --grau: #dfdfd7; --tinte: #1f2421; --weiss: #ffffff;
  --signal: #ffca0a; --signal-druck: #f0b900;
  --leise: rgba(31, 36, 33, .66); --linie: rgba(7, 59, 76, .16);
  --display: 'Big Shoulders', 'Arial Narrow', sans-serif;
  --rand: clamp(16px, 3.4vw, 52px);
  --kopf: 68px;
  --dock: 0px;          /* aus der Höhe der Demo-Steuerung gesetzt */
  --io: cubic-bezier(0.76, 0, 0.24, 1);   /* power3.inOut (Quart), Panel */
  --aus: cubic-bezier(0.22, 1, 0.36, 1);  /* power4.out (Quint), Name */
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--weiss); color: var(--tinte); font-family: 'Archivo', system-ui, sans-serif; font-size: 100%; -webkit-font-smoothing: antialiased; }
body { min-height: 100svh; background: var(--seite, var(--weiss)); overflow-x: clip; }
body[data-seite='projekte'], body[data-seite='spenden'] { --seite: var(--hell); }
body[data-seite='mitmachen'] { --seite: var(--grau); }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
svg { display: block; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.mono { font: 500 11px/1.4 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; }
.dach { font-size: 12px; font-weight: 600; font-stretch: 112%; letter-spacing: .12em; text-transform: uppercase; color: var(--marke-2); }
.zahl { font-variant-numeric: tabular-nums lining-nums; white-space: nowrap; }
[tabindex='-1']:focus { outline: none; }
:focus-visible { outline: 2px solid var(--marke); outline-offset: 3px; }

/* ---------- Kopf ---------- */
.kopf { position: sticky; top: 0; z-index: 20; height: var(--kopf); display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 24px; padding: 0 var(--rand); background: var(--seite, var(--weiss)); border-bottom: 1px solid var(--linie); }
.marke { display: inline-flex; align-items: center; gap: 10px; }
.marke svg { width: 34px; height: 34px; flex: none; }
.marke b { font: 800 26px/1 var(--display); letter-spacing: .01em; text-transform: uppercase; color: var(--marke); }
.marke small { display: block; font-size: 11px; font-weight: 600; font-stretch: 110%; letter-spacing: .1em; text-transform: uppercase; color: var(--leise); margin-top: 2px; }
.nav { display: flex; justify-content: center; gap: clamp(16px, 2.6vw, 40px); }
.nav a { position: relative; font-weight: 600; font-size: 15px; padding: 8px 0; color: var(--tinte); transition: color .15s linear; }
.nav a::after { content: ''; position: absolute; left: 0; right: 0; bottom: 2px; height: 2px; background: var(--marke); transform: scaleX(0); transform-origin: left; transition: transform .2s var(--io); }
.nav a:hover { color: var(--marke); }
.nav a:hover::after, .nav a[aria-current='page']::after { transform: scaleX(1); }
.nav a[aria-current='page'] { color: var(--marke); }
.spenden-knopf { display: inline-flex; align-items: center; gap: 8px; height: 44px; padding: 0 22px; border: 0; border-radius: 999px; background: var(--signal); color: var(--tinte); font-weight: 700; cursor: pointer; font-size: 15px; transition: background-color .15s linear; }
.spenden-knopf:hover { background: var(--signal-druck); }
.spenden-knopf svg { width: 16px; height: 16px; }

/* ---------- Seiten ---------- */
.seiten { position: relative; }
.seite { min-height: calc(100svh - var(--kopf)); padding: clamp(28px, 6vh, 72px) var(--rand) calc(var(--dock) + clamp(32px, 6vh, 64px)); }
.seite[hidden] { display: none; }
.titel { font: 800 clamp(2.75rem, 1.6rem + 5vw, 6rem)/.95 var(--display); letter-spacing: .005em; text-transform: uppercase; color: var(--marke); text-wrap: balance; }
.lead { font-size: clamp(1rem, .95rem + .25vw, 1.125rem); line-height: 1.6; max-width: 54ch; color: var(--tinte); text-wrap: pretty; }
.knopf-linie { display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 22px; border: 1.5px solid var(--marke); border-radius: 999px; color: var(--marke); font-weight: 600; transition: background-color .15s linear, color .15s linear; }
.knopf-linie:hover { background: var(--marke); color: var(--weiss); }
.quelle { font-size: 12px; color: var(--leise); }

/* Start */
.start { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: clamp(24px, 4vw, 64px); align-items: stretch; }
.start__text { display: flex; flex-direction: column; gap: clamp(18px, 3vh, 28px); justify-content: center; }
.start__titel { max-width: 9ch; line-height: 1.02; } /* Platz für die Ü-Punkte unter der Zeile darüber */
.start__zahlen { display: grid; grid-template-columns: repeat(3, auto); justify-content: start; gap: 8px clamp(24px, 3.4vw, 52px); padding-top: clamp(12px, 2.4vh, 22px); border-top: 1px solid var(--linie); margin-top: 6px; }
.start__zahlen b { display: block; font: 800 clamp(2rem, 1.3rem + 2.4vw, 3.4rem)/1 var(--display); color: var(--marke); }
.start__zahlen span { font-size: 13px; color: var(--leise); }
.start__zahlen .quelle { grid-column: 1 / -1; }
.bild { position: relative; border-radius: 6px; overflow: hidden; background: var(--hell); min-height: 360px; }
.bild svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.bild__etikett { position: absolute; left: 14px; bottom: 14px; background: var(--weiss); color: var(--tinte); font-size: 12px; font-weight: 600; padding: 7px 11px; border-radius: 4px; }

/* Projekte */
.kopfzeile { display: flex; justify-content: space-between; align-items: end; gap: 16px 32px; flex-wrap: wrap; margin-bottom: clamp(22px, 4vh, 44px); }
.kopfzeile .lead { max-width: 44ch; }
.projekte { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(12px, 1.6vw, 24px); }
.projekt { background: var(--weiss); border-radius: 6px; overflow: hidden; display: flex; flex-direction: column; }
.projekt__band { height: clamp(84px, 13vh, 132px); background: var(--marke); position: relative; }
.projekt__band svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.projekt__band span { position: absolute; left: 16px; top: 12px; font: 800 30px/1 var(--display); color: var(--hell); }
.projekt__inhalt { padding: 18px 18px 20px; display: grid; gap: 10px; }
.projekt h2 { font: 700 clamp(1.4rem, 1.1rem + .9vw, 1.9rem)/1.02 var(--display); text-transform: uppercase; color: var(--marke); }
.projekt p { font-size: 14.5px; line-height: 1.55; }
.balken { height: 6px; background: var(--hell-2); border-radius: 3px; overflow: hidden; }
.balken i { display: block; height: 100%; background: var(--marke); }
.projekt__stand { font-size: 13px; color: var(--leise); display: flex; justify-content: space-between; gap: 8px; }

/* Mitmachen */
.wege { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(10px, 1.4vw, 20px); }
.weg { background: var(--weiss); border-radius: 6px; padding: clamp(16px, 2vw, 24px); display: flex; flex-direction: column; gap: 12px; min-height: 250px; }
.weg:first-child { background: var(--marke); color: var(--hell); }
.weg:first-child h2, .weg:first-child .weg__nr { color: var(--weiss); }
.weg:first-child .knopf-text { color: var(--weiss); }
.weg__nr { font: 800 15px/1 var(--display); color: var(--marke-3); letter-spacing: .04em; }
.weg h2 { font: 700 clamp(1.5rem, 1.2rem + 1vw, 2.1rem)/1 var(--display); text-transform: uppercase; color: var(--marke); }
.weg p { font-size: 14.5px; line-height: 1.55; flex: 1; }
.knopf-text { font-weight: 600; font-size: 14px; color: var(--marke); display: inline-flex; gap: 6px; align-items: center; }
.knopf-text::after { content: '→'; transition: transform .15s linear; }
.knopf-text:hover::after { transform: translateX(3px); }

/* Wirkung */
.wirkung { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(24px, 5vw, 80px); align-items: start; }
.grosszahlen { display: grid; gap: clamp(14px, 2.6vh, 26px); }
.grosszahl { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 18px; padding-bottom: clamp(12px, 2vh, 20px); border-bottom: 1px solid var(--linie); }
.grosszahl b { font: 800 clamp(3rem, 2rem + 4vw, 5.5rem)/.9 var(--display); color: var(--marke); }
.grosszahl span { font-size: 15px; line-height: 1.45; max-width: 26ch; }
.mittel h2 { font: 700 clamp(1.75rem, 1.2rem + 2vw, 3rem)/1 var(--display); text-transform: uppercase; color: var(--marke); margin-bottom: 18px; }
.stapel { display: flex; height: 22px; border-radius: 4px; overflow: hidden; margin-bottom: 18px; }
.stapel i:nth-child(1) { background: var(--marke); } .stapel i:nth-child(2) { background: var(--marke-3); }
.stapel i:nth-child(3) { background: var(--hell-2); } .stapel i:nth-child(4) { background: var(--grau); }
.legende { list-style: none; display: grid; gap: 10px; font-size: 15px; }
.legende li { display: grid; grid-template-columns: 14px 1fr auto; gap: 12px; align-items: center; padding-bottom: 10px; border-bottom: 1px solid var(--linie); }
.legende li::before { content: ''; width: 12px; height: 12px; border-radius: 2px; background: var(--f); }
.legende b { font-weight: 700; }
.kontakt { display: flex; align-items: center; gap: 14px; margin-top: 24px; font-size: 14px; line-height: 1.45; }
.kontakt__kreis { width: 48px; height: 48px; border-radius: 50%; background: var(--hell-2); color: var(--marke); display: grid; place-items: center; font: 800 18px/1 var(--display); flex: none; }

/* Spenden */
.spenden { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: clamp(24px, 5vw, 80px); align-items: start; }
.formular { background: var(--weiss); border-radius: 6px; padding: clamp(18px, 2.4vw, 32px); display: grid; gap: 16px; }
.umschalter { display: grid; grid-template-columns: 1fr 1fr; background: var(--hell); border-radius: 999px; padding: 4px; }
.umschalter button { height: 40px; border: 0; border-radius: 999px; background: none; font-weight: 600; cursor: pointer; transition: background-color .15s linear; }
.umschalter button[aria-pressed='true'] { background: var(--marke); color: var(--weiss); }
.betraege { display: grid; gap: 8px; }
.betrag { display: grid; grid-template-columns: 74px 1fr; align-items: center; gap: 14px; min-height: 56px; padding: 8px 14px; border: 1.5px solid var(--linie); border-radius: 6px; background: none; text-align: left; cursor: pointer; transition: border-color .15s linear; }
.betrag b { font: 800 28px/1 var(--display); color: var(--marke); }
.betrag span { font-size: 14px; line-height: 1.4; }
.betrag[aria-pressed='true'] { border-color: var(--marke); box-shadow: inset 0 0 0 1px var(--marke); }
.formular .spenden-knopf { justify-content: center; height: 52px; font-size: 16px; }
.ziel { margin-top: clamp(18px, 3vh, 30px); display: grid; gap: 8px; max-width: 46ch; }
.ziel strong { font-size: 15px; }
.ziel .balken { height: 8px; background: var(--hell-2); }

/* ---------- Vorhang mit Seitenname ---------- */
.vorhang { position: fixed; inset: 0; z-index: 50; visibility: hidden; background: var(--marke); color: var(--hell); overflow: hidden; pointer-events: none; }
.vorhang__wellen { position: absolute; left: -5%; right: -5%; bottom: 0; width: 110%; height: 46%; }
.vorhang__kopf { position: absolute; top: 22px; left: var(--rand); right: var(--rand); display: flex; justify-content: space-between; font-size: 12px; font-weight: 600; font-stretch: 112%; letter-spacing: .12em; text-transform: uppercase; color: rgba(237, 245, 250, .7); }
.vorhang__mitte { position: absolute; inset: 0; display: grid; place-items: center; align-content: center; gap: 4px; }
.vorhang__name { display: inline-block; white-space: nowrap; font: 800 100px/.86 var(--display); letter-spacing: .005em; text-transform: uppercase; color: var(--weiss); }
.vorhang__unter { margin-top: clamp(6px, 1.4vh, 14px); font-size: clamp(15px, 1.5vw, 20px); font-weight: 500; font-stretch: 105%; letter-spacing: .01em; color: rgba(237, 245, 250, .82); }
.vorhang__reihen { position: absolute; inset: 0; display: none; }
.vorhang__reihen p { position: absolute; left: 50%; white-space: nowrap; font: 800 100px/.86 var(--display); text-transform: uppercase; color: transparent; -webkit-text-stroke: 1.5px rgba(207, 227, 236, .42); }
.vorhang[data-variante='kinetisch'] .vorhang__reihen { display: block; }
/* Masken: Ober- und Unterkante der Versalien nicht abschneiden */
.vorhang__name .zeichen-mask, .vorhang__name .wort-mask { padding-block: .06em; margin-block: -.06em; }

/* ---------- Demo-Steuerung ---------- */
.dock { position: fixed; z-index: 60; left: 50%; bottom: 14px; translate: -50% 0; width: max-content; max-width: calc(100% - 24px); background: rgba(16, 24, 26, .9); color: #e9efee; border-radius: 14px; padding: 9px 12px; display: grid; gap: 7px; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }
.dock__zeile { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.dock .mono { color: rgba(233, 239, 238, .62); }
.modi { display: flex; gap: 4px; }
.modi button { font: 500 11px/1 'Geist Mono', ui-monospace, monospace; letter-spacing: .12em; text-transform: uppercase; color: rgba(233, 239, 238, .7); background: none; border: 1px solid rgba(233, 239, 238, .2); border-radius: 999px; height: 28px; padding: 0 11px; cursor: pointer; transition: color .15s linear, background-color .15s linear, border-color .15s linear; }
.modi button:hover { color: #fff; border-color: rgba(233, 239, 238, .45); }
.modi button[aria-pressed='true'] { background: #e9efee; color: #10181a; border-color: #e9efee; }
.dock__werte { display: flex; flex-wrap: wrap; gap: 2px 14px; max-width: 760px; }
.dock__werte b { color: #fff; font-weight: 500; }

:root.reduziert * { transition-duration: .01ms !important; animation-duration: .01ms !important; }

/* ---------- Kleinere Breiten ---------- */
@media (max-width: 1100px) {
  .wege { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .weg { min-height: 0; }
}
@media (max-width: 900px) {
  .start, .wirkung, .spenden { grid-template-columns: minmax(0, 1fr); }
  .bild { min-height: 300px; }
  .projekte { grid-template-columns: minmax(0, 1fr); }
  .projekt { flex-direction: row; }
  .projekt__band { width: 34%; height: auto; flex: none; }
}
@media (max-width: 640px) {
  :root { --kopf: 108px; }
  .kopf { grid-template-columns: 1fr auto; grid-template-rows: 60px 48px; gap: 0 12px; }
  .nav { grid-column: 1 / -1; grid-row: 2; justify-content: space-between; border-top: 1px solid var(--linie); }
  .nav a { font-size: 14px; }
  .marke b { font-size: 22px; }
  .spenden-knopf { height: 40px; padding: 0 16px; font-size: 14px; }
  .wege { grid-template-columns: minmax(0, 1fr); }
  .start__zahlen { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px 14px; }
  .projekt { flex-direction: column; }
  .projekt__band { width: auto; height: 76px; }
  .dock { padding: 8px 10px; bottom: 10px; width: calc(100% - 20px); }
  .dock__zeile > span.mono { display: none; }
  .modi { width: 100%; }
  .modi button { flex: 1; padding: 0 6px; }
  .dock__werte { font-size: 10px; letter-spacing: .1em; }
}
@media (hover: none) {
  /* Kein Hover: Unterstreichung nur für die aktive Seite, Tippen löst den Übergang aus */
  .nav a:hover::after { transform: scaleX(0); }
  .nav a[aria-current='page']::after { transform: scaleX(1); }
  .nav a, .spenden-knopf { -webkit-tap-highlight-color: transparent; }
}

/* Vorschau in der Galerie: nur die Bühne */
.vorschau body { overflow: hidden; }
.vorschau .dock { display: none; }
</style>
</head>
<body data-seite="start">
<header class="kopf">
  <a class="marke" href="#start" data-ziel="start" aria-label="Uferbande e.V., Startseite">
    <svg viewBox="0 0 34 34" aria-hidden="true"><circle cx="17" cy="17" r="17" fill="#073b4c"/><path d="M6 14.5c2.2-2 4.4-2 6.6 0s4.4 2 6.6 0 4.4-2 6.6 0M6 19.5c2.2-2 4.4-2 6.6 0s4.4 2 6.6 0 4.4-2 6.6 0M9 24.5c1.6-1.4 3.2-1.4 4.8 0s3.2 1.4 4.8 0 3.2-1.4 4.8 0" fill="none" stroke="#edf5fa" stroke-width="1.8" stroke-linecap="round"/><path d="M23.5 13V5.5" stroke="#cfe3ec" stroke-width="1.6" stroke-linecap="round"/><rect x="22.2" y="5.5" width="2.6" height="5" rx="1.3" fill="#cfe3ec"/></svg>
    <span><b>Uferbande</b><small>e.V. · Potsdam</small></span>
  </a>
  <nav class="nav" aria-label="Hauptnavigation">
    <a href="#projekte" data-ziel="projekte">Projekte</a>
    <a href="#mitmachen" data-ziel="mitmachen">Mitmachen</a>
    <a href="#wirkung" data-ziel="wirkung">Wirkung</a>
  </nav>
  <a class="spenden-knopf" href="#spenden" data-ziel="spenden">
    <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 14s-5.5-3.3-5.5-7.4A3 3 0 0 1 8 4.8a3 3 0 0 1 5.5 1.8C13.5 10.7 8 14 8 14Z" fill="currentColor"/></svg>Spenden
  </a>
</header>

<main class="seiten" data-seiten>
  <!-- Seite: Start -->
  <section class="seite" data-seite="start" aria-labelledby="t-start">
    <div class="start">
      <div class="start__text">
        <p class="dach">Gemeinnütziger Verein · seit 2014</p>
        <h1 class="titel start__titel" id="t-start" tabindex="-1">Wir holen die Havel zurück.</h1>
        <p class="lead">Jeden zweiten Samstag räumen Freiwillige die Ufer zwischen Sacrow und Caputh auf. Wir pflanzen Schilf, messen die Wasserqualität und zeigen Schulklassen, was im Fluss lebt.</p>
        <p><a class="knopf-linie" href="#mitmachen" data-ziel="mitmachen">Nächster Einsatz: Sa 8. Nov, 10 Uhr</a></p>
        <div class="start__zahlen">
          <div><b class="zahl">38,4&nbsp;t</b><span>Müll aus dem Uferbereich</span></div>
          <div><b class="zahl">12,6&nbsp;km</b><span>Uferlinie in Pflege</span></div>
          <div><b class="zahl">1.140</b><span>Freiwillige im Jahr 2025</span></div>
          <p class="quelle">Stand: Jahresbericht 2025</p>
        </div>
      </div>
      <figure class="bild" aria-label="Illustration: Schilfgürtel an der Havel mit Uferlinie und Wasser">
        <svg viewBox="0 0 600 720" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
          <defs>
            <linearGradient id="himmel" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#edf5fa"/><stop offset="1" stop-color="#cfe3ec"/></linearGradient>
            <linearGradient id="wasser" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#0f5568"/><stop offset="1" stop-color="#073b4c"/></linearGradient>
          </defs>
          <rect width="600" height="720" fill="url(#himmel)"/>
          <circle cx="430" cy="170" r="64" fill="#ffffff" opacity=".8"/>
          <path d="M0 330 C60 300 90 312 140 296 C190 280 220 300 270 288 C330 272 360 296 420 284 C480 272 530 290 600 276 L600 380 L0 380Z" fill="#3d7f90"/>
          <path d="M0 352 C80 334 130 346 200 336 C270 326 320 344 400 334 C470 326 540 340 600 330 L600 390 L0 390Z" fill="#0f5568"/>
          <rect y="380" width="600" height="340" fill="url(#wasser)"/>
          <g fill="none" stroke="#cfe3ec" stroke-linecap="round" data-hero-wellen>
            <path d="M40 420h90M190 428h140M400 418h110" stroke-width="2" opacity=".55"/>
            <path d="M0 470h70M120 462h200M380 474h160" stroke-width="2" opacity=".4"/>
            <path d="M60 520h150M260 512h90M420 526h150" stroke-width="2.5" opacity=".32"/>
            <path d="M10 584h220M300 576h120M470 590h110" stroke-width="3" opacity=".24"/>
          </g>
          <g data-schilf></g>
        </svg>
        <figcaption class="bild__etikett">Neustädter Havelbucht · Pflanzfläche 2027</figcaption>
      </figure>
    </div>
  </section>

  <!-- Seite: Projekte -->
  <section class="seite" data-seite="projekte" aria-labelledby="t-projekte" hidden>
    <div class="kopfzeile">
      <h1 class="titel" id="t-projekte" tabindex="-1">Projekte</h1>
      <p class="lead">Drei Uferabschnitte, drei Aufgaben. Wir zeigen ehrlich, wie weit wir sind.</p>
    </div>
    <div class="projekte">
      <article class="projekt">
        <div class="projekt__band" data-band="1"><span>01</span></div>
        <div class="projekt__inhalt">
          <p class="dach">Neustädter Havelbucht</p>
          <h2>Schilfgürtel neu pflanzen</h2>
          <p>2.400 Schilfballen in Kokosmatten. Sie bremsen Wellen, halten das Ufer fest und geben dem Teichrohrsänger einen Brutplatz.</p>
          <div class="balken" aria-hidden="true"><i style="width:62%"></i></div>
          <p class="projekt__stand zahl"><span>7.420 € von 12.000 €</span><span>62 %</span></p>
        </div>
      </article>
      <article class="projekt">
        <div class="projekt__band" data-band="2"><span>02</span></div>
        <div class="projekt__inhalt">
          <p class="dach">Tiefer See</p>
          <h2>Messstelle Wasserqualität</h2>
          <p>Einmal im Monat messen wir Sauerstoff, Nitrat und Sichttiefe. Alle Werte stehen offen auf unserer Website.</p>
          <div class="balken" aria-hidden="true"><i style="width:100%"></i></div>
          <p class="projekt__stand zahl"><span>Läuft seit 2019</span><span>81 Messungen</span></p>
        </div>
      </article>
      <article class="projekt">
        <div class="projekt__band" data-band="3"><span>03</span></div>
        <div class="projekt__inhalt">
          <p class="dach">Templiner See, Westufer</p>
          <h2>Uferputz auf 1,8 km</h2>
          <p>Angelschnur, Kronkorken, Glasbruch. Alle zwei Wochen sammeln wir ein, was nach dem Baden liegen bleibt.</p>
          <div class="balken" aria-hidden="true"><i style="width:40%"></i></div>
          <p class="projekt__stand zahl"><span>Nächster Einsatz 8. Nov</span><span>14 Plätze frei</span></p>
        </div>
      </article>
    </div>
  </section>

  <!-- Seite: Mitmachen -->
  <section class="seite" data-seite="mitmachen" aria-labelledby="t-mitmachen" hidden>
    <div class="kopfzeile">
      <h1 class="titel" id="t-mitmachen" tabindex="-1">Mitmachen</h1>
      <p class="lead">Vier Wege, dabei zu sein. Für keinen davon brauchst du Gummistiefel. Für einen schon.</p>
    </div>
    <div class="wege">
      <article class="weg"><span class="weg__nr">01</span><h2>Uferputz am Samstag</h2><p>Alle zwei Wochen von 10 bis 13 Uhr. Handschuhe, Greifzangen und Kaffee bringen wir mit.</p><a class="knopf-text" href="#projekte" data-ziel="projekte">Termine ansehen</a></article>
      <article class="weg"><span class="weg__nr">02</span><h2>Ufer-Patenschaft</h2><p>5 € im Monat für 10 Meter Ufer. Einmal im Jahr bekommst du Post von deinem Abschnitt.</p><a class="knopf-text" href="#spenden" data-ziel="spenden">Pate werden</a></article>
      <article class="weg"><span class="weg__nr">03</span><h2>Schulklassen-Tag</h2><p>Ein Vormittag am Wasser mit Messkoffer und Kescher. Für Klasse 3 bis 10, kostenlos.</p><a class="knopf-text" href="#wirkung" data-ziel="wirkung">Tag anfragen</a></article>
      <article class="weg"><span class="weg__nr">04</span><h2>Eigene Spendenaktion</h2><p>Geburtstag, Firmenlauf, Hochzeit: Sammle für einen Abschnitt. Wir schicken dir das Material.</p><a class="knopf-text" href="#spenden" data-ziel="spenden">Aktion starten</a></article>
    </div>
  </section>

  <!-- Seite: Wirkung -->
  <section class="seite" data-seite="wirkung" aria-labelledby="t-wirkung" hidden>
    <div class="kopfzeile">
      <h1 class="titel" id="t-wirkung" tabindex="-1">Wirkung</h1>
      <p class="lead">Was 2025 am Ufer passiert ist und wohin jeder Euro gegangen ist.</p>
    </div>
    <div class="wirkung">
      <div class="grosszahlen">
        <div class="grosszahl"><b class="zahl">212</b><span>Einsätze seit der Gründung, davon 26 im Jahr 2025</span></div>
        <div class="grosszahl"><b class="zahl">4.180</b><span>Meter Angelschnur aus dem Schilf gezogen</span></div>
        <div class="grosszahl"><b class="zahl">63</b><span>Schulklassen waren mit uns am Wasser</span></div>
        <p class="quelle">Stand: Jahresbericht 2025</p>
      </div>
      <div class="mittel">
        <h2>So haben wir 2025 jeden Euro eingesetzt</h2>
        <div class="stapel" aria-hidden="true"><i style="width:81%"></i><i style="width:9%"></i><i style="width:7%"></i><i style="width:3%"></i></div>
        <ul class="legende zahl">
          <li style="--f:#073b4c">Projektarbeit am Ufer<b>81 %</b></li>
          <li style="--f:#3d7f90">Material und Werkzeug<b>9 %</b></li>
          <li style="--f:#cfe3ec">Verwaltung<b>7 %</b></li>
          <li style="--f:#dfdfd7">Spendenwerbung<b>3 %</b></li>
        </ul>
        <div class="kontakt"><span class="kontakt__kreis" aria-hidden="true">JK</span><p>Fragen zum Geld?<br><strong>Jana Krüger</strong>, Schatzmeisterin</p></div>
      </div>
    </div>
  </section>

  <!-- Seite: Spenden -->
  <section class="seite" data-seite="spenden" aria-labelledby="t-spenden" hidden>
    <div class="spenden">
      <div>
        <h1 class="titel" id="t-spenden" tabindex="-1">Spenden</h1>
        <p class="lead" style="margin-top:18px">Von 100 Euro gehen 81 direkt in die Arbeit am Ufer. Die Zuwendungsbestätigung kommt im Februar per Post.</p>
        <div class="ziel">
          <strong>Schilf für die Neustädter Havelbucht</strong>
          <div class="balken" aria-hidden="true"><i style="width:62%"></i></div>
          <p class="projekt__stand zahl"><span>7.420 € von 12.000 €</span><span>noch 4.580 €</span></p>
        </div>
        <p class="quelle" style="margin-top:28px;max-width:46ch;line-height:1.6">Gemeinnützig nach § 52 AO. Eingetragen im Vereinsregister des Amtsgerichts Potsdam. Satzung und Jahresbericht stehen offen zum Download.</p>
      </div>
      <form class="formular" onsubmit="return false">
        <div class="umschalter" role="group" aria-label="Spendenrhythmus">
          <button type="button" aria-pressed="true">Einmalig</button>
          <button type="button" aria-pressed="false">Monatlich</button>
        </div>
        <div class="betraege" role="group" aria-label="Betrag">
          <button type="button" class="betrag" aria-pressed="false"><b class="zahl">15 €</b><span>3 Schilfballen für die Havelbucht</span></button>
          <button type="button" class="betrag" aria-pressed="true"><b class="zahl">40 €</b><span>Greifzangen für eine ganze Schulklasse</span></button>
          <button type="button" class="betrag" aria-pressed="false"><b class="zahl">90 €</b><span>Ein Nachfüllset für den Messkoffer</span></button>
        </div>
        <button class="spenden-knopf" type="submit">40 € spenden</button>
      </form>
    </div>
  </section>
</main>

<!-- Übergang: Vorhang mit dem Namen der Zielseite. aria-hidden, weil die Zielseite ihren echten Titel selbst trägt. -->
<div class="vorhang" data-vorhang aria-hidden="true">
  <svg class="vorhang__wellen" viewBox="0 0 1200 400" preserveAspectRatio="none" data-v-wellen></svg>
  <div class="vorhang__reihen" data-reihen></div>
  <div class="vorhang__kopf"><span>Uferbande e.V. · Potsdam</span><span class="zahl" data-v-nr>02 / 05</span></div>
  <div class="vorhang__mitte"><p class="vorhang__name" data-v-name>Projekte</p><p class="vorhang__unter" data-v-unter></p></div>
</div>
<p class="sr" aria-live="polite" data-ansage></p>

<aside class="dock" aria-label="Demo-Steuerung">
  <div class="dock__zeile">
    <span class="mono">Übergang</span>
    <div class="modi" role="group" aria-label="Variante des Übergangs">
      <button type="button" data-variante="maske" aria-pressed="true">Maske</button>
      <button type="button" data-variante="gemessen" aria-pressed="false">Gemessen</button>
      <button type="button" data-variante="kinetisch" aria-pressed="false">Kinetisch</button>
    </div>
  </div>
  <p class="mono dock__werte" data-werte aria-live="polite"></p>
</aside>

<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/SplitText.min.js"></script>
<script>
(() => {
  gsap.registerPlugin(SplitText);
  const html = document.documentElement;
  const reduziert = () => html.classList.contains('reduziert');
  const $ = (s, r = document) => r.querySelector(s);
  const $$ = (s, r = document) => [...r.querySelectorAll(s)];

  const SEITEN = {
    start:     { name: 'Uferbande', unter: 'Für saubere Havelufer seit 2014', titel: 'Uferbande e.V. · Potsdam' },
    projekte:  { name: 'Projekte',  unter: 'Drei Uferabschnitte in Arbeit',     titel: 'Projekte · Uferbande e.V.' },
    mitmachen: { name: 'Mitmachen', unter: 'Vier Wege, dabei zu sein',          titel: 'Mitmachen · Uferbande e.V.' },
    wirkung:   { name: 'Wirkung',   unter: 'Zahlen aus dem Jahresbericht 2025', titel: 'Wirkung · Uferbande e.V.' },
    spenden:   { name: 'Spenden',   unter: '81 von 100 Euro gehen direkt ans Ufer', titel: 'Spenden · Uferbande e.V.' },
  };
  const REIHE = Object.keys(SEITEN);

  // Werte aus den Messwerten (Snellenberg, live geprüft): Name power4.out 800 ms, y 0 → -50 px,
  // Ausblenden 600 ms linear, Panel power3.inOut. Maske nach Steckbrief: Zeichen yPercent 100 → 0, Versatz 30 ms.
  const W = {
    panelRein: 0.5, panelRaus: 0.55, panelKurve: 'power3.inOut',
    nameDauer: 0.8, nameKurve: 'power4.out',
    maskeVersatz: 0.03, maskeRausDauer: 0.45, maskeRausVersatz: 0.015, maskeHalten: 0.25,
    gemessenY: -50, gemessenAus: 0.6, gemessenHalten: 0.35,
    kinWeg: 22, kinDauer: 1.0, kinVersatz: 0.04, kinNachVorn: 1.22, kinRaus: 0.6,
    inhaltY: 40,
  };
  const WERTE = {
    maske: `<span>Panel <b>power3.inOut 500 / 550 ms</b></span><span>Zeichen <b>yPercent 100 → 0 · power4.out 800 ms</b></span><span>Versatz <b>30 ms</b></span><span>Halten <b>250 ms</b></span><span>Raus <b>yPercent -100 · power3.in 450 ms</b></span>`,
    gemessen: `<span>Panel <b>power3.inOut 500 / 550 ms</b></span><span>Name <b>opacity 0 → 1 · y 0 → -50 px · power4.out 800 ms</b></span><span>Halten <b>350 ms</b></span><span>Aus <b>600 ms linear</b></span><span>Gesamt <b>1400 ms</b></span>`,
    kinetisch: `<span>Panel <b>power3.inOut 500 / 550 ms</b></span><span>Zeilen <b>x ±22 vw → 0 · power4.out 1000 ms</b></span><span>Name <b>xPercent -11 → 0</b></span><span>Versatz <b>40 ms</b></span><span>Nach vorn <b>scale 1 → 1.22 · power3.in 600 ms</b></span>`,
    reduziert: `<span>Reduzierte Bewegung: <b>Name steht, Vorhang blendet 200 ms ein und aus</b></span>`,
  };

  const vorhang = $('[data-vorhang]'), vName = $('[data-v-name]'), vUnter = $('[data-v-unter]'), vNr = $('[data-v-nr]');
  const reihen = $('[data-reihen]'), wellenSvg = $('[data-v-wellen]'), seitenWrap = $('[data-seiten]');
  const ansage = $('[data-ansage]'), werteEl = $('[data-werte]'), dock = $('.dock');

  let aktuell = 'start', variante = 'maske', tl = null, teilung = null, warteschlange = null;

  // ---------- Bildwelt: Schilf im Hero, Wellen im Vorhang, Bänder der Projektkarten ----------
  function zufall(s) { return () => (s = (s * 16807) % 2147483647) / 2147483647; }
  (function schilf() {
    const g = $('[data-schilf]'), r = zufall(7); let out = '';
    for (let i = 0; i < 34; i++) {
      const x = 10 + i * 17.5 + r() * 12, top = 250 + r() * 170, neig = (r() - .5) * 40;
      const farbe = i % 3 ? '#062c38' : '#0f5568';
      out += `<path d="M${x} 730 Q${x + neig * .3} ${(730 + top) / 2} ${x + neig} ${top}" stroke="${farbe}" stroke-width="${2 + r() * 2.2}" fill="none" stroke-linecap="round"/>`;
      if (r() > .45) out += `<rect x="${x + neig - 4}" y="${top - 6}" width="8" height="${30 + r() * 16}" rx="4" fill="#7a5c3a" transform="rotate(${neig * .6} ${x + neig} ${top})"/>`;
      if (r() > .5) { const bx = x + neig * .55, by = top + 120 + r() * 80; out += `<path d="M${bx} ${by} q${18 + r() * 20} ${-40 - r() * 30} ${36 + r() * 30} ${-70 - r() * 40}" stroke="${farbe}" stroke-width="2" fill="none" stroke-linecap="round"/>`; }
    }
    g.innerHTML = out;
  })();
  (function wellen() {
    let out = '';
    for (let i = 0; i < 9; i++) {
      const y = 40 + i * 40, a = 6 + i * 1.6, l = 220 - i * 8;
      let d = `M0 ${y}`;
      for (let x = 0; x <= 1200; x += 20) d += ` L${x} ${(y + Math.sin((x / l) * Math.PI * 2 + i * .9) * a).toFixed(1)}`;
      out += `<path d="${d}" fill="none" stroke="rgba(207,227,236,${(.08 + i * .022).toFixed(3)})" stroke-width="${1.2 + i * .12}"/>`;
    }
    wellenSvg.innerHTML = out;
  })();
  $$('[data-band]').forEach(b => {
    const n = +b.dataset.band, r = zufall(n * 31);
    let out = '<svg viewBox="0 0 300 120" preserveAspectRatio="none" aria-hidden="true">';
    for (let i = 0; i < 6; i++) {
      const y = 30 + i * 16 + r() * 4; let d = `M0 ${y}`;
      for (let x = 0; x <= 300; x += 10) d += ` L${x} ${(y + Math.sin(x / (34 + n * 8) + i) * (3 + n)).toFixed(1)}`;
      out += `<path d="${d}" fill="none" stroke="rgba(207,227,236,${.18 + i * .05})" stroke-width="1.4"/>`;
    }
    b.insertAdjacentHTML('beforeend', out + '</svg>');
  });

  // ---------- Werkzeuge ----------
  function zeigeWerte() { werteEl.innerHTML = reduziert() ? WERTE.reduziert : WERTE[variante]; }
  function setzeVariante(v) {
    variante = v;
    $$('[data-variante]').forEach(b => b.setAttribute('aria-pressed', String(b.dataset.variante === v)));
    zeigeWerte();
  }
  const rand = () => parseFloat(getComputedStyle(html).getPropertyValue('--rand')) || 24;
  // Name auf Breite setzen: so groß, dass er gut 85 % der Breite füllt, höchstens 42 % der Höhe
  function passeName(el) {
    el.style.fontSize = '100px';
    const w = el.getBoundingClientRect().width || 1;
    const ziel = Math.min(innerWidth - 2 * rand(), 1500) * (innerWidth < 640 ? .94 : .86);
    const fs = Math.max(40, Math.min(100 * ziel / w, innerHeight * .42));
    el.style.fontSize = fs.toFixed(1) + 'px';
    return fs;
  }
  function baueReihen(name, fs) {
    reihen.innerHTML = '';
    const zeilen = [];
    for (let i = -3; i <= 3; i++) {
      if (i === 0) continue;   // Mitte trägt der echte Name
      const p = document.createElement('p');
      p.textContent = Array(5).fill(name).join(' · ');
      p.style.fontSize = fs.toFixed(1) + 'px';
      p.style.top = `calc(50% + ${(i * fs * .9).toFixed(1)}px - ${(fs * .12).toFixed(1)}px)`;
      p.dataset.richtung = i % 2 ? 1 : -1;
      reihen.appendChild(p); zeilen.push(p);
      gsap.set(p, { xPercent: -50, yPercent: -50 });   // zentriert; bewegt wird x in px
    }
    return zeilen;
  }
  function seiteZeigen(ziel, fokus) {
    $$('.seite').forEach(s => { s.hidden = s.dataset.seite !== ziel; });
    document.body.dataset.seite = ziel;
    $$('.nav a').forEach(a => a.dataset.ziel === ziel ? a.setAttribute('aria-current', 'page') : a.removeAttribute('aria-current'));
    document.title = SEITEN[ziel].titel;
    scrollTo(0, 0);
    aktuell = ziel;
    if (fokus) { $(`#t-${ziel}`).focus({ preventScroll: true }); ansage.textContent = 'Seite: ' + SEITEN[ziel].name; }
  }
  function aufraeumen() {
    if (teilung) { teilung.revert(); teilung = null; }
    gsap.set([vorhang, vName, vUnter, wellenSvg, seitenWrap, reihen], { clearProps: 'all' });
    reihen.innerHTML = '';
  }

  // ---------- Der Übergang ----------
  function gehe(ziel, fokus = true, danach = null) {
    if (!SEITEN[ziel]) return;
    if (tl && tl.isActive()) { warteschlange = ziel; return; }
    if (ziel === aktuell) return;
    aufraeumen();
    const s = SEITEN[ziel];
    vName.textContent = s.name;
    vUnter.textContent = s.unter;
    vNr.textContent = `${String(REIHE.indexOf(ziel) + 1).padStart(2, '0')} / ${String(REIHE.length).padStart(2, '0')}`;
    vorhang.dataset.variante = reduziert() ? 'reduziert' : variante;
    const fs = passeName(vName);
    const fertig = () => {
      aufraeumen();
      tl = null;
      if (warteschlange) { const w = warteschlange; warteschlange = null; gehe(w, fokus); }
      if (danach) danach();
    };
    tl = gsap.timeline({ onComplete: fertig });

    if (reduziert()) {
      // Kein Weg, kein Versatz: Vorhang mit stehendem Namen blendet ein und aus
      tl.set(vorhang, { visibility: 'visible', yPercent: 0, opacity: 0 })
        .to(vorhang, { opacity: 1, duration: 0.2, ease: 'none' })
        .add(() => seiteZeigen(ziel, fokus))
        .to(vorhang, { opacity: 0, duration: 0.2, ease: 'none' }, '+=0.3');
      return tl;
    }

    tl.set(vorhang, { visibility: 'visible', yPercent: 100 })
      .to(vorhang, { yPercent: 0, duration: W.panelRein, ease: W.panelKurve }, 0)
      .add(() => seiteZeigen(ziel, fokus), W.panelRein);

    let raus;
    if (variante === 'maske') {
      // Osmo-Prinzip: Zeichen steigen einzeln aus ihrer Maske, kurz halten, nach oben wieder hinaus
      const wort = innerWidth < 640;   // schmal: ganzes Wort statt Zeichen
      teilung = SplitText.create(vName, { type: wort ? 'words' : 'chars', mask: wort ? 'words' : 'chars', charsClass: 'zeichen', wordsClass: 'wort' });
      const teile = wort ? teilung.words : teilung.chars;
      const stand = 0.15 + W.nameDauer * .55 + W.maskeVersatz * (teile.length - 1);
      raus = stand + W.maskeHalten;
      tl.fromTo(teile, { yPercent: 100 }, { yPercent: 0, duration: W.nameDauer, ease: W.nameKurve, stagger: W.maskeVersatz }, 0.15)
        .fromTo(vUnter, { opacity: 0, y: 12 }, { opacity: 1, y: 0, duration: 0.6, ease: W.nameKurve }, 0.35)
        .to(teile, { yPercent: -100, duration: W.maskeRausDauer, ease: 'power3.in', stagger: W.maskeRausVersatz }, raus)
        .to(vUnter, { opacity: 0, duration: 0.3, ease: 'none' }, raus);
      raus += 0.1;
    } else if (variante === 'gemessen') {
      // Snellenberg, gemessen: opacity 0 → 1 und y 0 → -50 px in 800 ms power4.out, dann 600 ms linear aus
      raus = W.panelRein + W.gemessenHalten;
      tl.fromTo([vName, vUnter], { opacity: 0, y: 0 }, { opacity: 1, y: W.gemessenY, duration: W.nameDauer, ease: W.nameKurve, stagger: 0.06 }, 0.1)
        .to([vName, vUnter], { opacity: 0, duration: W.gemessenAus, ease: 'none' }, 0.8);
    } else {
      // Kinetisch: Zeilen aus dem Namen fahren gegenläufig ein und beim Öffnen nach vorn
      const zeilen = baueReihen(s.name, fs);
      const weg = innerWidth * W.kinWeg / 100;
      tl.fromTo(zeilen, { x: i => zeilen[i].dataset.richtung * weg }, { x: 0, duration: W.kinDauer, ease: W.nameKurve, stagger: W.kinVersatz }, 0.05)
        .fromTo(vName, { xPercent: -W.kinWeg / 2, opacity: 0 }, { xPercent: 0, opacity: 1, duration: W.kinDauer, ease: W.nameKurve }, 0.12)
        .fromTo(vUnter, { opacity: 0 }, { opacity: 1, duration: 0.5, ease: 'none' }, 0.4);
      raus = 0.95;
      tl.to([reihen, vName], { scale: W.kinNachVorn, opacity: 0, duration: W.kinRaus, ease: 'power3.in', transformOrigin: '50% 50%' }, raus)
        .to(vUnter, { opacity: 0, duration: 0.3, ease: 'none' }, raus);
      raus += 0.1;
    }
    tl.fromTo(wellenSvg, { xPercent: 0 }, { xPercent: -4, duration: raus + W.panelRaus, ease: 'none' }, 0)
      .to(vorhang, { yPercent: -100, duration: W.panelRaus, ease: W.panelKurve }, raus)
      .fromTo(seitenWrap, { y: W.inhaltY }, { y: 0, duration: W.nameDauer, ease: W.nameKurve }, raus + 0.1);
    return tl;
  }

  // ---------- Bedienung ----------
  document.addEventListener('click', e => {
    const a = e.target.closest('[data-ziel]');
    if (a) { e.preventDefault(); pause(); gehe(a.dataset.ziel); return; }
    const v = e.target.closest('[data-variante]');
    if (v) { pause(); setzeVariante(v.dataset.variante); return; }
    const b = e.target.closest('.betrag, .umschalter button');
    if (b) {
      $$('button', b.parentElement).forEach(x => x.setAttribute('aria-pressed', String(x === b)));
      if (b.classList.contains('betrag')) $('.formular .spenden-knopf').textContent = b.querySelector('b').textContent + ' spenden';
    }
  });
  const dockMessen = () => html.style.setProperty('--dock', Math.ceil(dock.getBoundingClientRect().height + 14) + 'px');
  addEventListener('resize', dockMessen);

  // ---------- Vorführung: durch alle Seiten, Variante wechselt mit jedem Schritt ----------
  let uhr = null, schritt = 0, lauf = 0;
  const VARIANTEN = ['maske', 'gemessen', 'kinetisch'];
  const ZIELE = ['projekte', 'mitmachen', 'wirkung', 'spenden', 'start'];
  function takt(verzug) {
    const meins = lauf;
    uhr = setTimeout(() => {
      if (meins !== lauf) return;
      if (!reduziert()) setzeVariante(VARIANTEN[schritt % 3]);
      let ziel = ZIELE[schritt % ZIELE.length];
      if (ziel === aktuell) ziel = ZIELE[(schritt + 1) % ZIELE.length];
      schritt++;
      const t = gehe(ziel, false, () => { if (meins === lauf) takt(1500); });
      if (!t) takt(1500);
    }, verzug);
  }
  function stoppe() {
    lauf++;
    clearTimeout(uhr); uhr = null;
    if (tl) { tl.progress(1); }
    warteschlange = null;
  }
  function vorfuehren() {
    stoppe();
    gsap.globalTimeline.resume();
    document.fonts.ready.then(() => {
      seiteZeigen('start', false);
      schritt = 0;
      takt(1400);
    });
  }
  function neustart() {
    stoppe();
    gsap.globalTimeline.resume();
    seiteZeigen('start', false);
    setzeVariante(variante);
    uhr = setTimeout(() => { uhr = null; gehe('projekte', false); }, 400);
  }
  function pause() { lauf++; clearTimeout(uhr); uhr = null; }

  seiteZeigen('start', false);
  setzeVariante('maske');
  dockMessen();
  if (document.fonts) document.fonts.ready.then(dockMessen);
  // Detailseite schaltet .reduziert per Klasse um: Werte-Anzeige nachziehen
  new MutationObserver(zeigeWerte).observe(html, { attributes: true, attributeFilter: ['class'] });

})();
</script>
</body>
</html>