Preisseite: Endpreise, Rechenweg, Vergleich
Preisseite ohne Kartentrio: Tarif-Tabelle mit Monatspreis, Einmalkosten, Gesamtpreis und Monatsdurchschnitt, Laufzeit-Umschalter, Rechenweg Zeile für Zeile, ehrlicher „ab“-Preis mit Fußnote, Vergleich, Grundpreis an der Theke und Rechtshinweis.
Verträge mit Laufzeit und Einmalkosten: Fitness, Sportverein mit Aufnahmegebühr, Musikschule, IT-Wartungsvertrag für Verbraucher.
Eine ganze Preisseite ohne SaaS-Kartentrio. Oben ein Satz, der sagt, was die Preise enthalten: Endpreise inklusive Mehrwertsteuer, einmalige Gebühren schon eingerechnet. Dann die Tarife als Tabelle mit Monatspreis, Einmalkosten, Gesamtpreis über die Laufzeit und Kündigung, umschaltbar nach Laufzeit. Darunter ein Rechenweg, der die Summe für den gewählten Tarif Zeile für Zeile zeigt, ein Leistungsvergleich, der auf dem Telefon als Liste lesbar bleibt, Ermäßigungen mit Nachweis, ein Grundpreis dort, wo Ware nach Menge verkauft wird, Fragen zu Pause, Verlängerung und Kündigung und eine Handlung. Ein „ab“-Preis verweist immer auf den Tarif, für den er gilt.
Preisseiten verlieren Vertrauen im Kleingedruckten: eine Aktivierungsgebühr hier, eine Servicepauschale dort. Wer den Gesamtpreis und den Rechenweg offen zeigt, wirkt seriös und beantwortet die Frage, die sonst am Telefon kommt. Eine Tabelle mit Zeilen statt drei bunter Karten lässt vergleichen, ohne zu drängen. Und sie bildet ab, was die Preisangabenverordnung verlangt.
Wann einsetzen
Fitnessstudio, Kurs- und Coaching-Anbieter, Fahrschule, Friseur, Gebäudedienste, Umzug, IT-Dienstleister und Freizeitanlagen, also überall, wo Verbraucher feste Preise vergleichen. Variante Mitgliedschaft für Verträge mit Laufzeit, Variante Kurskarte für Einzeltermine und Mehrfachkarten.
Wann nicht
Nicht für Angebote nach Aufmaß ohne festen Preis (dafür reparatur-richtpreise oder kostenrechner-konfigurator). Nicht für reine B2B-Angebote, bei denen Nettopreise üblich sind; dann klar „zzgl. MwSt.“ und nur für Unternehmen. Keine Streichpreise und keine Countdown-Rabatte; Preisermäßigungen brauchen den niedrigsten Preis der letzten 30 Tage (aktion-mit-preisangabe, vom Kunden prüfen lassen).
Varianten
- Mitgliedschaft: Tarif-Tabelle mit Laufzeit-Umschalter, Gesamtpreis und Monatsdurchschnitt über die Mindestlaufzeit, Rechenweg je Tarif
- Kurskarte: Einzeltermin, 5er- und 10er-Karte mit Preis je Termin und Gültigkeit, Rechner „Wie oft kommst du?“ mit ehrlicher Empfehlung
Umsetzung
Tarife als ein Datensatz (Monatspreis, Einmalgebühren mit Fälligkeit, Mindestlaufzeit, Kündigungsfrist, Leistungen). Daraus rechnet die Seite Gesamtpreis über die Mindestlaufzeit und Monatsdurchschnitt, Intl.NumberFormat('de-DE', {style: 'currency', currency: 'EUR'}). Tarif-Tabelle als echte <table> mit <caption>, <th scope>; unter 720 px wird jede Zeile per CSS zu einem Block mit Beschriftungen aus data-label. Laufzeit-Umschalter als Radiogruppe (fieldset, legend), der Wechsel tauscht Zahlen ohne Übergang. Rechenweg als <ol> mit <output>: „12 × 34,00 € + 29,00 € Startpaket = 437,00 €“. Leistungsvergleich als Tabelle mit erster Spalte sticky; auf dem Telefon als Liste je Leistung mit drei beschrifteten Zellen, Haken als Text („enthalten“, „–“ mit vh-Text „nicht enthalten“). Jeder „ab“-Preis bekommt eine Fußnote mit Tarif und Laufzeit. Grundpreis je Liter bzw. Kilogramm neben dem Preis, nicht dahinter versteckt. Kündigung führt zum Kündigungsknopf (widerruf-und-kuendigung-schaltflaeche). Rechtliche Angaben (Preisangaben, Verlängerung, Kündigung) als Platzhalter des Betriebs, vom Kunden prüfen lassen.
Robust umsetzen
Telefon. Laufzeit-Umschalter oben über die ganze Breite. Jeder Tarif als Block mit Monatspreis groß, darunter Einmalkosten, Gesamtpreis und Kündigung als Liste. Leistungsvergleich als Liste je Leistung mit drei kurzen, beschrifteten Zellen. Feste Leiste unten mit „Probetraining“.
Ohne Bewegung. Es gibt nur harte Schnitte; die gestufte Markierung erscheint sofort im Endzustand.
Barrierefrei. Tabellen mit caption und th scope, auf dem Telefon bleiben die Beschriftungen als Text sichtbar. Umschalter als Radiogruppe mit legend. Rechenweg als Text, Ergebnis in aria-live="polite". Haken nie nur als Symbol. Fußnoten als Links mit Rücksprung. Limette nur als Fläche mit dunkler Schrift.
Code
<!DOCTYPE html>
<!--
Preisseite: Endpreise, Rechenweg, Vergleich
Fundus · Sektionen & Funktionen · https://cecodesign.de/lab/fundus/sektionen/preisseite-geruest/
Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de" data-v="mitgliedschaft">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Preisseite: Endpreise, Rechenweg, Vergleich · 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: 'Anybody'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/anybody:vf@latest/latin-wdth-normal.woff2') format('woff'); font-weight: 100 900; font-stretch: 50% 150%; font-display: swap; }
@font-face { font-family: 'Funnel Sans'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/funnel-sans:vf@latest/latin-wght-normal.woff2') format('woff'); font-weight: 300 800; font-display: swap; }
@font-face { font-family: 'Azeret Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/azeret-mono:vf@latest/latin-wght-normal.woff2') format('woff'); font-weight: 100 900; font-display: swap; }
/* Token aus fitness-studio-rhythmus (token-brutal-hart: Takt statt Kurve) */
:root {
--kreide: #f2f1ec; --halle: #121412; --limette: #ccff33; --flaeche: #e2e1da; --meta: #5e615c; --rot: #b3261e; --weiss: #fbfaf7;
--h: 'Anybody', 'Arial Narrow', sans-serif; --t: 'Funnel Sans', system-ui, sans-serif; --m: 'Azeret Mono', ui-monospace, monospace;
--hover: 70ms; --umschalt: 150ms; --stufen: 180ms;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--kreide); color: var(--halle); font-family: var(--t); -webkit-font-smoothing: antialiased; }
body { font-size: 1.0625rem; line-height: 1.55; min-height: 100svh; }
a { color: inherit; text-underline-offset: .2em; }
button, input { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; }
:focus-visible { outline: 3px solid var(--halle); outline-offset: 3px; box-shadow: 0 0 0 6px var(--limette); }
.vh { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.huelle { max-width: 1440px; margin: 0 auto; padding: 0 clamp(12px, 3vw, 32px); }
.m { font-family: var(--m); font-size: .8125rem; letter-spacing: .08em; text-transform: uppercase; font-variant-numeric: tabular-nums; }
.z { font-family: var(--m); font-variant-numeric: tabular-nums; letter-spacing: 0; text-transform: none; }
h1, h2, h3 { font-family: var(--h); text-transform: uppercase; text-wrap: balance; }
p { text-wrap: pretty; hyphens: auto; }
.taste { display: inline-flex; align-items: center; gap: 10px; background: var(--limette); color: var(--halle); border: 2px solid var(--halle); font: 700 1rem/1 var(--t); padding: 13px 18px; text-decoration: none; box-shadow: 4px 4px 0 var(--halle); transition: transform var(--hover) steps(1, end), box-shadow var(--hover) steps(1, end); }
.taste:hover { transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--halle); }
.taste:active { transform: translate(4px, 4px); box-shadow: 0 0 0 var(--halle); }
/* Kopf */
.kopf { background: var(--halle); color: var(--kreide); }
.kopf .huelle { display: flex; align-items: center; gap: 28px; min-height: 64px; }
.marke { font: 800 1.75rem/1 var(--h); font-stretch: 62%; text-transform: uppercase; text-decoration: none; margin-right: auto; letter-spacing: .01em; }
.marke span { font: 500 .75rem/1 var(--m); font-stretch: 100%; letter-spacing: .08em; color: #b9bcb5; margin-left: 10px; }
.nav ul { display: flex; gap: 22px; list-style: none; font-weight: 600; font-size: .9375rem; }
.nav a { text-decoration: none; padding: 4px 0; border-bottom: 2px solid transparent; }
.nav a:hover { border-color: var(--kreide); }
.nav a[aria-current] { border-color: var(--limette); }
.kopf .taste { box-shadow: none; border-color: var(--limette); padding: 10px 14px; font-size: .9375rem; }
.kopf :focus-visible { outline-color: var(--limette); box-shadow: none; }
/* Einstieg */
.einstieg { padding: clamp(28px, 4vw, 56px) 0 clamp(24px, 3vw, 40px); display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 24px 48px; align-items: end; border-bottom: 2px solid var(--halle); }
.einstieg h1 { font-weight: 800; font-stretch: 58%; font-size: clamp(3.25rem, 1.2rem + 8vw, 9rem); line-height: .86; letter-spacing: -.01em; margin-top: 14px; }
.einstieg h1 em { font-style: normal; font-stretch: 100%; display: block; }
.lead { font-size: clamp(1.0625rem, 1rem + .3vw, 1.25rem); max-width: 40ch; }
.versprechen { list-style: none; display: grid; gap: 0; margin-top: 18px; border-top: 1px solid var(--halle); }
.versprechen li { padding: 9px 0; border-bottom: 1px solid var(--halle); display: flex; gap: 12px; font-weight: 600; }
.versprechen li::before { content: '■'; color: var(--halle); font-size: .7em; padding-top: .35em; }
.ab { margin-top: 18px; display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: baseline; }
.ab b { font: 800 clamp(1.75rem, 1.3rem + 1.5vw, 2.5rem)/1 var(--h); font-stretch: 75%; }
.ab a { font-size: .875rem; color: var(--meta); }
/* Laufzeit */
.laufzeit { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; padding: 20px 0; border: 0; }
.laufzeit legend { float: left; margin-right: 6px; }
.segment { display: inline-grid; grid-auto-flow: column; border: 2px solid var(--halle); background: var(--weiss); }
.segment label { position: relative; cursor: pointer; }
.segment input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.segment span { display: block; padding: 11px 18px; font-weight: 700; transition: background-color var(--umschalt) steps(1, end), color var(--umschalt) steps(1, end); }
.segment label + label span { border-left: 2px solid var(--halle); }
.segment input:checked + span { background: var(--halle); color: var(--limette); }
.segment input:focus-visible + span { outline: 3px solid var(--halle); outline-offset: 3px; box-shadow: 0 0 0 6px var(--limette); }
.segment label:hover input:not(:checked) + span { background: var(--flaeche); }
.laufzeit .hinweis { font-size: .9375rem; color: var(--meta); }
/* Tarif-Tabelle */
.tarife { width: 100%; border-collapse: collapse; background: var(--weiss); border: 2px solid var(--halle); }
.tarife caption { text-align: left; padding: 0 0 10px; }
.tarife th, .tarife td { text-align: left; vertical-align: top; padding: 16px 14px; border-bottom: 1px solid var(--flaeche); }
.tarife thead th { background: var(--halle); color: var(--kreide); font: 500 .75rem/1.3 var(--m); letter-spacing: .08em; text-transform: uppercase; padding: 10px 14px; border: 0; }
.tarife tbody tr { position: relative; cursor: pointer; }
.tarife tbody tr:hover { background: #f6f5f0; }
.tarife tbody th { position: relative; width: 22%; }
.tarife tbody th b { display: block; font: 800 1.625rem/1 var(--h); font-stretch: 70%; text-transform: uppercase; }
.tarife tbody th small { display: block; font-weight: 400; font-size: .875rem; color: var(--meta); margin-top: 6px; }
.tarife tbody th::before { content: ''; position: absolute; left: -2px; top: -1px; bottom: -1px; width: 8px; background: var(--limette); border-right: 2px solid var(--halle); transform-origin: 50% 0; transform: scaleY(0); }
.tarife tbody tr[aria-selected="true"] th::before { transform: scaleY(1); transition: transform var(--stufen) steps(3, jump-none); }
.tarife tbody tr[aria-selected="true"] { background: #f7ffe0; }
.tarife .preis { font: 800 2rem/1 var(--h); font-stretch: 75%; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tarife .zahl { font-family: var(--m); font-size: .9375rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tarife td small { display: block; font-size: .8125rem; color: var(--meta); margin-top: 4px; font-family: var(--t); white-space: normal; }
.tarife td.gesamt .zahl { font-weight: 700; }
.wahl { font-weight: 700; font-size: .875rem; text-decoration: underline; text-underline-offset: .2em; white-space: nowrap; min-height: 36px; padding: 8px 10px; margin: -8px -10px; }
.wahl:hover { background: var(--limette); }
tr[aria-selected="true"] .wahl { text-decoration: none; background: var(--halle); color: var(--limette); }
/* Rechenweg */
.zwei { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(16px, 2vw, 32px); margin-top: clamp(28px, 4vw, 48px); }
.block { border: 2px solid var(--halle); background: var(--weiss); padding: clamp(16px, 2vw, 24px); }
.block h2 { font-weight: 800; font-stretch: 70%; font-size: clamp(1.75rem, 1.3rem + 1.4vw, 2.5rem); line-height: .95; }
.block > p.m { color: var(--meta); margin-bottom: 8px; }
.rechnung { list-style: none; margin-top: 16px; font-family: var(--m); font-variant-numeric: tabular-nums; font-size: .9375rem; }
.rechnung li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding: 10px 0; border-bottom: 1px dashed var(--meta); }
.rechnung li.summe { border-bottom: 0; border-top: 2px solid var(--halle); font-weight: 800; font-size: 1.0625rem; }
.rechnung li.schnitt { background: var(--limette); border: 2px solid var(--halle); padding: 10px 12px; margin-top: 8px; font-weight: 800; }
.danach { margin-top: 12px; font-size: .9375rem; color: var(--meta); }
.klein { font-size: .8125rem; color: var(--meta); }
/* Vergleich */
.vergleich { margin-top: clamp(32px, 4vw, 56px); }
.vergleich h2, .theke h2, .fragen h2 { font-weight: 800; font-stretch: 70%; font-size: clamp(2rem, 1.4rem + 2vw, 3.25rem); line-height: .95; margin-bottom: 14px; }
.vt { width: 100%; border-collapse: collapse; background: var(--weiss); border: 2px solid var(--halle); }
.vt th, .vt td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--flaeche); vertical-align: top; }
.vt thead th { font: 800 1.25rem/1 var(--h); font-stretch: 70%; text-transform: uppercase; border-bottom: 2px solid var(--halle); }
.vt tbody th { font-weight: 600; width: 28%; }
.vt td.ja::before { content: '■ '; }
.vt td.nein { color: var(--meta); }
/* Theke, Fragen, Recht */
.theke { margin-top: clamp(32px, 4vw, 56px); display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(16px, 2vw, 32px); align-items: start; }
.waren { list-style: none; border-top: 2px solid var(--halle); }
.waren li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 16px; padding: 12px 0; border-bottom: 1px solid var(--halle); }
.waren b { font-weight: 700; }
.waren .z { text-align: right; font-weight: 700; }
.waren .gp { grid-column: 1 / -1; font-family: var(--m); font-size: .8125rem; color: var(--meta); }
.fragen { margin-top: clamp(32px, 4vw, 56px); }
.fragen details { border-top: 2px solid var(--halle); }
.fragen details:last-of-type { border-bottom: 2px solid var(--halle); }
.fragen summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; gap: 16px; padding: 16px 0; font-weight: 700; font-size: 1.125rem; }
.fragen summary::-webkit-details-marker { display: none; }
.fragen summary::after { content: '+'; font: 800 1.5rem/1 var(--h); }
.fragen details[open] summary::after { content: '−'; }
.fragen summary:hover { background: var(--flaeche); }
.fragen details p { padding: 0 0 16px; max-width: 64ch; }
.recht { margin-top: clamp(32px, 4vw, 56px); border: 2px dashed var(--halle); padding: 18px 20px; display: grid; gap: 6px; font-size: .9375rem; }
.recht b { font-family: var(--m); font-size: .8125rem; letter-spacing: .08em; text-transform: uppercase; }
.recht ul { padding-left: 1.1em; }
.handlung { margin-top: clamp(40px, 5vw, 72px); background: var(--halle); color: var(--kreide); padding: clamp(28px, 4vw, 56px) 0; }
.handlung .huelle { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 20px; }
.handlung h2 { font-weight: 800; font-stretch: 58%; font-size: clamp(2.25rem, 1.4rem + 3vw, 4.5rem); line-height: .9; }
.handlung p { color: #c9ccc5; margin-top: 8px; }
.handlung .taste { box-shadow: 4px 4px 0 var(--limette); border-color: var(--limette); }
.handlung :focus-visible { outline-color: var(--limette); box-shadow: none; }
.fuss { background: var(--halle); color: #c9ccc5; border-top: 1px solid #343834; padding: 24px 0 64px; font-size: .875rem; }
.fuss .huelle { display: flex; flex-wrap: wrap; gap: 8px 24px; justify-content: space-between; }
.fuss a { text-decoration: none; }
.fuss a:hover { text-decoration: underline; }
.fuss :focus-visible { outline-color: var(--limette); box-shadow: none; }
/* Kurskarte */
.karten-teil { display: none; }
:root[data-v="kurskarte"] .karten-teil { display: block; }
:root[data-v="kurskarte"] .mitglied-teil { display: none; }
.rechner { display: grid; gap: 14px; margin-top: 14px; }
.rechner label { font-weight: 700; display: flex; justify-content: space-between; gap: 12px; }
.rechner input[type=range] { width: 100%; accent-color: var(--halle); height: 32px; }
.skala { display: flex; justify-content: space-between; font-family: var(--m); font-size: .75rem; color: var(--meta); }
.optionen { list-style: none; border-top: 2px solid var(--halle); }
.optionen li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 14px; padding: 12px 10px; border-bottom: 1px solid var(--flaeche); }
.optionen li b { font-weight: 700; }
.optionen li small { grid-column: 1 / -1; color: var(--meta); font-size: .8125rem; }
.optionen li .z { font-weight: 700; text-align: right; }
.optionen li[data-beste] { background: var(--limette); border: 2px solid var(--halle); }
.optionen li[data-beste] small { color: var(--halle); }
/* Telefonleiste, Pult */
.leiste { display: none; }
.pult { position: fixed; right: 14px; bottom: 14px; z-index: 40; width: 240px; background: var(--weiss); border: 2px solid var(--halle); padding: 12px; display: grid; gap: 8px; font-size: 13px; }
.pult .pm { font: 500 10.5px/1.3 'Azeret Mono', monospace; letter-spacing: .14em; text-transform: uppercase; color: var(--meta); }
.pult .reihe { display: grid; grid-template-columns: 1fr 1fr; border: 2px solid var(--halle); }
.pult .reihe button { padding: 7px 4px; font-weight: 700; }
.pult .reihe button + button { border-left: 2px solid var(--halle); }
.pult .reihe button[aria-pressed="true"] { background: var(--halle); color: var(--limette); }
.pult .werte { font: 400 10.5px/1.5 'Azeret Mono', monospace; color: var(--meta); }
.zeiger { position: fixed; left: 0; top: 0; width: 22px; height: 22px; z-index: 60; pointer-events: none; opacity: 0; }
.vorfuehrung .zeiger { opacity: 1; }
.zeiger.drueckt { scale: .86; transform-origin: 3px 2px; }
.vorschau body { overflow: hidden; }
.vorschau .pult, .vorschau .leiste { display: none !important; }
@media (max-width: 1000px) { .nav { display: none; } .einstieg, .zwei, .theke { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 720px) {
.tarife, .tarife tbody, .tarife tr, .tarife td, .tarife tbody th { display: block; width: auto; }
.tarife { border: 0; background: none; }
.tarife thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.tarife tbody tr { background: var(--weiss); border: 2px solid var(--halle); margin-bottom: 12px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.tarife tbody th { grid-column: 1 / -1; border-bottom: 2px solid var(--halle); padding: 14px 14px 12px 22px; }
.tarife td { padding: 10px 14px; border-bottom: 1px solid var(--flaeche); }
.tarife td::before { content: attr(data-label); display: block; font: 500 .6875rem/1.3 var(--m); letter-spacing: .08em; text-transform: uppercase; color: var(--meta); margin-bottom: 4px; }
.tarife td.leistung, .tarife td.aktion { grid-column: 1 / -1; }
.tarife td.aktion::before { display: none; }
.tarife td.aktion { border-bottom: 0; }
.vt thead { display: none; }
.vt, .vt tbody, .vt tr, .vt th, .vt td { display: block; width: auto; }
.vt tr { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border-bottom: 2px solid var(--halle); }
.vt tbody th { grid-column: 1 / -1; padding: 12px 12px 4px; border: 0; }
.vt td { padding: 4px 12px 12px; border: 0; font-size: .9375rem; }
.vt td::before { content: attr(data-label); display: block; font: 500 .6875rem/1.3 var(--m); letter-spacing: .08em; text-transform: uppercase; color: var(--meta); }
.vt td.ja::before { content: attr(data-label); }
.leiste { display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); background: var(--halle); color: var(--kreide); }
.leiste p { font-size: .8125rem; line-height: 1.3; }
.leiste p b { display: block; font: 800 1.25rem/1 var(--h); font-stretch: 75%; }
.leiste .taste { box-shadow: none; border-color: var(--limette); padding: 12px 14px; }
body { padding-bottom: 72px; }
.pult { bottom: 82px; }
}
@media (max-width: 360px) { .segment span { padding: 10px 12px; } .marke span { display: none; } }
@media (hover: none) { .taste:hover { transform: none; box-shadow: 4px 4px 0 var(--halle); } .tarife tbody tr:hover { background: var(--weiss); } }
:root.reduziert * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
@media (forced-colors: active) {
button, [role=button], [role=tab], [role=switch], input, select, textarea { border: 1px solid ButtonText !important; }
.tarife tbody tr[aria-selected="true"] th::before { background: Highlight; }
.segment input:checked + span { outline: 3px solid Highlight; }
}
</style>
</head>
<body>
<header class="kopf">
<div class="huelle">
<a class="marke" href="#" aria-label="Kreidehalle Menden, Startseite">Kreidehalle<span>Menden</span></a>
<nav class="nav" aria-label="Hauptmenü"><ul><li><a href="#">Kursplan</a></li><li><a href="#" aria-current="page">Preise</a></li><li><a href="#">Studio</a></li><li><a href="#">Kontakt</a></li></ul></nav>
<a class="taste" href="#probe">Probetraining</a>
</div>
</header>
<main id="inhalt">
<div class="huelle">
<section class="einstieg" aria-labelledby="h1">
<div>
<p class="m">Preise · Stand <time datetime="2026-11-01">01.11.2026</time></p>
<h1 id="h1" data-h1>Was es kostet.<em>Alles drin.</em></h1>
</div>
<div>
<p class="lead" data-lead>Alle Preise sind Endpreise inklusive 19 % MwSt. Was einmalig anfällt, steht in derselben Zeile und ist im Gesamtpreis schon eingerechnet.</p>
<ul class="versprechen" data-versprechen>
<li>Startpaket steht in der Tabelle, nicht im Kleingedruckten</li>
<li>Kündigen online mit einem Knopf</li>
<li>Pause bis 3 Monate bei Krankheit oder Elternzeit</li>
</ul>
<p class="ab" data-ab></p>
</div>
</section>
<!-- Mitgliedschaft -->
<div class="mitglied-teil">
<fieldset class="laufzeit">
<legend class="m">Laufzeit</legend>
<span class="segment">
<label><input type="radio" name="laufzeit" value="flex" data-laufzeit><span>Monatlich kündbar</span></label>
<label><input type="radio" name="laufzeit" value="jahr" data-laufzeit checked><span>12 Monate</span></label>
</span>
<span class="hinweis" data-laufzeit-hinweis role="status"></span>
</fieldset>
<table class="tarife" data-tarife>
<caption class="m">Mitgliedschaften · Endpreise in Euro inkl. 19 % MwSt.</caption>
<thead><tr><th scope="col">Tarif</th><th scope="col">pro Monat</th><th scope="col">einmalig</th><th scope="col">Gesamt Mindestlaufzeit</th><th scope="col">im Schnitt pro Monat</th><th scope="col"><span class="vh">Rechnung</span></th></tr></thead>
<tbody></tbody>
</table>
<div class="zwei">
<section class="block" aria-labelledby="h-rechnung">
<p class="m">Rechenweg</p>
<h2 id="h-rechnung" data-rechnung-titel>So rechnen wir</h2>
<ol class="rechnung" data-rechnung aria-live="polite"></ol>
<p class="danach" data-danach></p>
</section>
<section class="block" aria-labelledby="h-rabatt">
<p class="m">Ermäßigung</p>
<h2 id="h-rabatt">20 % weniger mit Nachweis</h2>
<p style="margin-top:12px">Für Schülerinnen und Schüler, Azubis, Studierende und Freiwilligendienste. Den Nachweis zeigst du einmal im Jahr am Tresen.</p>
<p class="klein" style="margin-top:10px">Die Ermäßigung gilt auf den Monatspreis, nicht auf das Startpaket. Beispiel Voll, 12 Monate: 31,20 € statt 39,00 € im Monat.</p>
</section>
</div>
<section class="vergleich" aria-labelledby="h-vergleich">
<h2 id="h-vergleich">Was drin ist</h2>
<table class="vt">
<caption class="vh">Leistungen je Tarif</caption>
<thead><tr><th scope="col">Leistung</th><th scope="col">Tag</th><th scope="col">Voll</th><th scope="col">Voll + Sauna</th></tr></thead>
<tbody>
<tr><th scope="row">Trainingszeiten</th><td data-label="Tag">Mo–Fr 6–16 Uhr</td><td data-label="Voll">täglich 6–23 Uhr</td><td data-label="Voll + Sauna">täglich 6–23 Uhr</td></tr>
<tr><th scope="row">Gerätefläche und Freihantel</th><td class="ja" data-label="Tag">enthalten</td><td class="ja" data-label="Voll">enthalten</td><td class="ja" data-label="Voll + Sauna">enthalten</td></tr>
<tr><th scope="row">Kurse laut Kursplan</th><td class="nein" data-label="Tag">–<span class="vh"> nicht enthalten</span></td><td class="ja" data-label="Voll">alle</td><td class="ja" data-label="Voll + Sauna">alle</td></tr>
<tr><th scope="row">Sauna</th><td class="nein" data-label="Tag">–<span class="vh"> nicht enthalten</span></td><td class="nein" data-label="Voll">–<span class="vh"> nicht enthalten</span></td><td class="ja" data-label="Voll + Sauna">enthalten</td></tr>
<tr><th scope="row">Trainingsplan</th><td data-label="Tag">beim Start</td><td data-label="Voll">alle 8 Wochen</td><td data-label="Voll + Sauna">alle 8 Wochen</td></tr>
<tr><th scope="row">Gast mitbringen</th><td class="nein" data-label="Tag">–<span class="vh"> nicht enthalten</span></td><td data-label="Voll">1 × im Monat</td><td data-label="Voll + Sauna">1 × im Monat</td></tr>
</tbody>
</table>
</section>
</div>
<!-- Kurskarte -->
<div class="karten-teil">
<table class="tarife" data-karten style="margin-top:20px">
<caption class="m">Kurse ohne Vertrag · Endpreise in Euro inkl. 19 % MwSt.</caption>
<thead><tr><th scope="col">Karte</th><th scope="col">Preis</th><th scope="col">je Termin</th><th scope="col">gültig</th><th scope="col">Ersparnis</th><th scope="col"><span class="vh">Auswahl</span></th></tr></thead>
<tbody></tbody>
</table>
<div class="zwei">
<section class="block" aria-labelledby="h-rechner">
<p class="m">Rechner</p>
<h2 id="h-rechner">Wie oft kommst du?</h2>
<div class="rechner">
<label for="oft">Kurse pro Woche <output class="z" data-oft-text for="oft">1</output></label>
<input type="range" id="oft" min="1" max="4" step="1" value="1" data-oft aria-describedby="oft-hilfe">
<div class="skala" aria-hidden="true"><span>1</span><span>2</span><span>3</span><span>4</span></div>
<p class="klein" id="oft-hilfe">Gerechnet mit 4,3 Wochen im Monat. Karten, die vor dem Aufbrauchen ablaufen würden, zählen nicht.</p>
</div>
</section>
<section class="block" aria-labelledby="h-optionen">
<p class="m">Ergebnis</p>
<h2 id="h-optionen" data-optionen-titel>Günstigster Weg</h2>
<ol class="optionen" data-optionen aria-live="polite" style="margin-top:14px"></ol>
</section>
</div>
</div>
<section class="theke" aria-labelledby="h-theke">
<div>
<h2 id="h-theke">An der Theke</h2>
<p>Ware, die nach Menge verkauft wird, steht mit Grundpreis je Liter oder Kilogramm direkt neben dem Preis.</p>
</div>
<ul class="waren">
<li><b>Mineralwasser, still · 0,5 l</b><span class="z">1,50 €</span><span class="gp">Grundpreis 3,00 € / l</span></li>
<li><b>Isotonisches Getränk · 0,75 l</b><span class="z">2,40 €</span><span class="gp">Grundpreis 3,20 € / l</span></li>
<li><b>Nussriegel · 45 g</b><span class="z">2,20 €</span><span class="gp">Grundpreis 48,89 € / kg</span></li>
<li><b>Handtuch leihen · Stück</b><span class="z">1,00 €</span><span class="gp">Stückpreis, kein Grundpreis nötig</span></li>
</ul>
</section>
<section class="fragen" aria-labelledby="h-fragen">
<h2 id="h-fragen">Fragen zu Geld und Vertrag</h2>
<details><summary>Was passiert nach den 12 Monaten?</summary><p>Der Vertrag läuft danach auf unbestimmte Zeit weiter und ist dann monatlich kündbar, zum selben Monatspreis. (Platzhalter des Studios, vom Kunden prüfen lassen.)</p></details>
<details><summary>Gibt es Kosten, die hier nicht stehen?</summary><p>Nein. Das Startpaket ist die einzige Einmalzahlung und steht in der Tabelle. Keine Servicepauschale, keine Kartengebühr. Getränke an der Theke zahlst du nur, wenn du sie kaufst.</p></details>
<details><summary>Kann ich pausieren?</summary><p>Ja, bei Krankheit, Schwangerschaft oder Elternzeit bis zu 3 Monate mit Nachweis. Die Laufzeit verschiebt sich um die Pause.</p></details>
<details><summary>Wie kündige ich?</summary><p>Über den Knopf „Verträge hier kündigen“ unten auf jeder Seite, per E-Mail oder am Tresen. Du bekommst sofort eine Bestätigung.</p></details>
</section>
<aside class="recht" aria-label="Hinweis zu Preisangaben">
<b>Für den Betrieb · vom Kunden prüfen lassen</b>
<ul>
<li>Gesamtpreis inklusive Umsatzsteuer und aller festen Einmalgebühren. „Ab“-Preise mit Fußnote, für welchen Tarif und welche Laufzeit sie gelten.</li>
<li>Grundpreis je 1 l oder 1 kg nur bei Ware nach Menge, direkt am Preis, nicht hinter einem Link.</li>
<li>Verlängerung, Kündigungsfrist und Pause sind Platzhalter. Keine Rechtsberatung.</li>
</ul>
</aside>
</div>
<section class="handlung" id="probe" aria-labelledby="h-probe">
<div class="huelle">
<div><h2 id="h-probe">Erst testen.<br>Dann entscheiden.</h2><p>Probetraining 0 €, 60 Minuten mit Einweisung. Bring Wasser und Hallenschuhe mit.</p></div>
<a class="taste" href="#">Probetraining buchen <span aria-hidden="true">→</span></a>
</div>
</section>
</main>
<footer class="fuss"><div class="huelle"><span>Kreidehalle · Lindenstraße 21 · 58706 Menden (Demo, erfunden)</span><span><a href="#">Impressum</a> · <a href="#">Datenschutz</a> · <a href="#">Verträge hier kündigen</a></span></div></footer>
<div class="leiste" data-leiste><p data-leiste-text></p><a class="taste" href="#probe">Probetraining</a></div>
<div class="pult" role="group" aria-label="Steuerung der Demo">
<span class="pm">Variante</span>
<div class="reihe"><button type="button" data-v="mitgliedschaft" aria-pressed="true">Mitgliedschaft</button><button type="button" data-v="kurskarte" aria-pressed="false">Kurskarte</button></div>
<p class="werte" data-werte></p>
</div>
<svg class="zeiger" viewBox="0 0 22 22" aria-hidden="true" data-zeiger><path d="M3 2 L3 18 L7.5 14 L10.5 20.5 L13.2 19.3 L10.3 13 L16.5 13 Z" fill="#121412" stroke="#fff" stroke-width="1.4" stroke-linejoin="round"/></svg>
<script>
(() => {
const html = document.documentElement;
const reduziert = () => html.classList.contains('reduziert');
const $ = (s, r = document) => r.querySelector(s), $$ = (s, r = document) => [...r.querySelectorAll(s)];
const eur = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' });
// Ein Datensatz, aus dem Tabelle, Rechenweg, „ab“-Preis und Leiste entstehen
const START = { name: 'Startpaket', preis: 29, info: 'Einweisung, Trainingsplan, Chip' };
const TARIFE = [
{ id: 'tag', name: 'Tag', info: 'Mo–Fr bis 16 Uhr, Geräte', flex: 34, jahr: 27 },
{ id: 'voll', name: 'Voll', info: 'immer, Geräte und alle Kurse', flex: 49, jahr: 39 },
{ id: 'sauna', name: 'Voll + Sauna', info: 'wie Voll, dazu Sauna', flex: 59, jahr: 47 }
];
const KARTEN = [
{ id: 'einzel', name: 'Einzeltermin', info: 'ein Kurs nach Wahl', preis: 16, termine: 1, monate: 1 },
{ id: 'fuenf', name: '5er-Karte', info: 'fünf Kurse nach Wahl', preis: 70, termine: 5, monate: 4 },
{ id: 'zehn', name: '10er-Karte', info: 'zehn Kurse nach Wahl', preis: 120, termine: 10, monate: 6 }
];
const WERTE = {
mitgliedschaft: 'Zahlen steps(1, end) 0 ms · Hover 70 ms · Umschalter 150 ms · Markierung steps(3, jump-none) 180 ms · Tabelle wird unter 720 px zu Blöcken',
kurskarte: 'wie Mitgliedschaft · Rechner 1–4 Kurse je Woche × 4,3 · Karten mit Ablauf vor Verbrauch zählen nicht · Vergleich mit Voll, 12 Monate'
};
let v = 'mitgliedschaft', lauf = 'jahr', gewaehlt = 'voll';
const monate = () => lauf === 'jahr' ? 12 : 1;
const gesamt = t => t[lauf] * monate() + START.preis;
const schnitt = t => gesamt(t) / monate();
const jahrSchnitt = t => (t.jahr * 12 + START.preis) / 12;
function tabelle() {
$('[data-tarife] tbody').innerHTML = TARIFE.map(t => `<tr data-tarif="${t.id}" aria-selected="${t.id === gewaehlt}">
<th scope="row"><b>${t.name}</b><small>${t.info}</small></th>
<td data-label="pro Monat"><span class="preis">${eur.format(t[lauf])}</span></td>
<td data-label="einmalig"><span class="zahl">${eur.format(START.preis)}</span><small>${START.name}</small></td>
<td class="gesamt" data-label="Gesamt Mindestlaufzeit"><span class="zahl">${eur.format(gesamt(t))}</span><small>${lauf === 'jahr' ? 'in 12 Monaten' : 'im ersten Monat'}</small></td>
<td data-label="im Schnitt pro Monat"><span class="zahl">${lauf === 'jahr' ? eur.format(schnitt(t)) : '–'}</span><small>${lauf === 'jahr' ? 'alles eingerechnet' : 'danach ' + eur.format(t.flex)}</small></td>
<td class="aktion"><button type="button" class="wahl" data-wahl="${t.id}" aria-pressed="${t.id === gewaehlt}">Rechnung zeigen</button></td></tr>`).join('');
rechnung();
$('[data-laufzeit-hinweis]').textContent = lauf === 'jahr' ? 'Danach monatlich kündbar.' : 'Jederzeit zum Monatsende kündbar.';
const tag = TARIFE[0];
$('[data-ab]').innerHTML = `<b>ab ${eur.format(jahrSchnitt(tag))}</b><span>im Monat, alles eingerechnet<a href="#fn-ab" id="fn-ab-ref" aria-describedby="fn-ab">¹</a></span><span class="klein" id="fn-ab" style="flex-basis:100%">¹ Tarif Tag, 12 Monate: 12 × ${eur.format(tag.jahr)} + ${eur.format(START.preis)} Startpaket = ${eur.format(tag.jahr * 12 + START.preis)}, geteilt durch 12.</span>`;
const t = TARIFE.find(x => x.id === gewaehlt);
$('[data-leiste-text]').innerHTML = `<b>${t.name}: ${eur.format(t[lauf])}</b>${lauf === 'jahr' ? 'im Schnitt ' + eur.format(schnitt(t)) + ' mit Startpaket' : 'monatlich kündbar'}`;
}
function rechnung() {
const t = TARIFE.find(x => x.id === gewaehlt);
$('[data-rechnung-titel]').textContent = `So rechnen wir: ${t.name}`;
const z = [];
if (lauf === 'jahr') {
z.push(`<li><span>12 Monate × ${eur.format(t.jahr)}</span><span>${eur.format(t.jahr * 12)}</span></li>`);
z.push(`<li><span>+ ${START.name}, einmalig</span><span>${eur.format(START.preis)}</span></li>`);
z.push(`<li class="summe"><span>= Gesamt in 12 Monaten</span><span>${eur.format(gesamt(t))}</span></li>`);
z.push(`<li class="schnitt"><span>÷ 12 = im Schnitt pro Monat</span><span>${eur.format(schnitt(t))}</span></li>`);
$('[data-danach]').textContent = `Ab Monat 13: ${eur.format(t.jahr)} im Monat, monatlich kündbar (Platzhalter, vom Kunden prüfen lassen).`;
} else {
z.push(`<li><span>1. Monat</span><span>${eur.format(t.flex)}</span></li>`);
z.push(`<li><span>+ ${START.name}, einmalig</span><span>${eur.format(START.preis)}</span></li>`);
z.push(`<li class="summe"><span>= erster Monat</span><span>${eur.format(t.flex + START.preis)}</span></li>`);
z.push(`<li class="schnitt"><span>jeder weitere Monat</span><span>${eur.format(t.flex)}</span></li>`);
$('[data-danach]').textContent = `Über 12 Monate wären das ${eur.format(t.flex * 12 + START.preis)}, also ${eur.format((t.flex - t.jahr) * 12)} mehr als mit 12 Monaten Laufzeit.`;
}
$('[data-rechnung]').innerHTML = z.join('');
}
function karten() {
const ref = KARTEN[0].preis;
$('[data-karten] tbody').innerHTML = KARTEN.map(k => `<tr data-karte="${k.id}" aria-selected="false">
<th scope="row"><b>${k.name}</b><small>${k.info}</small></th>
<td data-label="Preis"><span class="preis">${eur.format(k.preis)}</span></td>
<td data-label="je Termin"><span class="zahl">${eur.format(k.preis / k.termine)}</span></td>
<td data-label="gültig"><span class="zahl">${k.termine === 1 ? 'am Kurstag' : k.monate + ' Monate'}</span></td>
<td data-label="Ersparnis"><span class="zahl">${k.termine > 1 ? eur.format(ref * k.termine - k.preis) : '–'}</span><small>${k.termine > 1 ? 'gegenüber Einzelterminen' : ''}</small></td>
<td class="aktion"></td></tr>`).join('');
rechner();
}
function rechner() {
const n = +$('[data-oft]').value, proMonat = n * 4.3;
$('[data-oft-text]').textContent = n;
const opt = KARTEN.map(k => {
const brauchtMonate = k.termine / proMonat; // so lange reicht die Karte
const passt = brauchtMonate <= k.monate || k.termine === 1;
return { name: k.name, kosten: proMonat * k.preis / k.termine, passt, info: k.termine === 1 ? `${proMonat.toLocaleString('de-DE', { maximumFractionDigits: 1 })} Termine × ${eur.format(k.preis)}` : (passt ? `reicht ${brauchtMonate.toLocaleString('de-DE', { maximumFractionDigits: 1 })} Monate, gültig ${k.monate} Monate` : `läuft ab, bevor sie aufgebraucht ist`) };
}).filter(o => o.passt);
const voll = TARIFE[1];
opt.push({ name: 'Mitgliedschaft Voll, 12 Monate', kosten: jahrSchnitt(voll), passt: true, info: `alle Kurse und Geräte, im Schnitt mit Startpaket` });
opt.sort((a, b) => a.kosten - b.kosten);
$('[data-optionen]').innerHTML = opt.map((o, i) => `<li ${i === 0 ? 'data-beste' : ''}><b>${o.name}${i === 0 ? ' · günstigste' : ''}</b><span class="z">${eur.format(o.kosten)} / Monat</span><small>${o.info}</small></li>`).join('');
$('[data-optionen-titel]').textContent = `Günstigster Weg bei ${n} × pro Woche`;
$('[data-leiste-text]').innerHTML = `<b>${opt[0].name.replace(', 12 Monate', '')}</b>${eur.format(opt[0].kosten)} im Monat bei ${n} × pro Woche`;
}
// Ereignisse
$$('[data-laufzeit]').forEach(r => r.addEventListener('change', () => { lauf = r.value; tabelle(); }));
$('[data-tarife]').addEventListener('click', e => { const tr = e.target.closest('[data-tarif]'); if (!tr) return; gewaehlt = tr.dataset.tarif; tabelle(); const b = $(`[data-wahl="${gewaehlt}"]`); if (e.target.closest('[data-wahl]') && b) b.focus(); });
$('[data-oft]').addEventListener('input', rechner);
addEventListener('resize', () => { v === 'mitgliedschaft' ? tabelle() : karten(); });
function variante(neu, url = true) {
v = WERTE[neu] ? neu : 'mitgliedschaft'; html.dataset.v = v;
$$('.pult [data-v]').forEach(b => b.setAttribute('aria-pressed', b.dataset.v === v));
$('[data-werte]').textContent = WERTE[v];
if (v === 'kurskarte') {
$('[data-h1]').innerHTML = 'Kurse ohne Vertrag.<em>Ehrlich gerechnet.</em>';
$('[data-lead]').textContent = 'Komm, wann du willst: Einzeltermin, 5er- oder 10er-Karte. Alle Preise sind Endpreise inklusive 19 % MwSt., ohne Startpaket.';
$('[data-versprechen]').innerHTML = '<li>Karten gelten für alle Kurse im Kursplan</li><li>Der Rechner sagt auch, wann sich ein Vertrag lohnt</li><li>Keine Karte verfällt ohne Erinnerung per E-Mail</li>';
$('[data-ab]').innerHTML = `<b>ab ${eur.format(12)}</b><span>je Kurs mit der 10er-Karte (120,00 €, 6 Monate gültig)</span>`;
karten();
} else {
$('[data-h1]').innerHTML = 'Was es kostet.<em>Alles drin.</em>';
$('[data-lead]').textContent = 'Alle Preise sind Endpreise inklusive 19 % MwSt. Was einmalig anfällt, steht in derselben Zeile und ist im Gesamtpreis schon eingerechnet.';
$('[data-versprechen]').innerHTML = '<li>Startpaket steht in der Tabelle, nicht im Kleingedruckten</li><li>Kündigen online mit einem Knopf</li><li>Pause bis 3 Monate bei Krankheit oder Elternzeit</li>';
tabelle();
}
if (url) { const q = new URLSearchParams(location.search); q.set('v', v); history.replaceState(null, '', location.pathname + '?' + q.toString()); }
}
$$('.pult [data-v]').forEach(b => b.addEventListener('click', () => { pause(); variante(b.dataset.v); }));
// Vorführung: harte Schnitte, Zeiger klickt Laufzeit und Tarife
const zeiger = $('[data-zeiger]');
let laeuft = false, timer = [];
const warte = ms => new Promise(r => timer.push(setTimeout(r, ms)));
async function gehe(el, klick = true) {
if (!el || !laeuft) return;
const r = el.getBoundingClientRect();
zeiger.style.transform = `translate(${r.left + Math.min(r.width / 2, 80)}px, ${r.top + r.height / 2}px)`;
await warte(reduziert() ? 120 : 520);
if (klick && laeuft) { zeiger.classList.add('drueckt'); await warte(100); zeiger.classList.remove('drueckt'); el.click(); }
}
async function runde() {
if (!laeuft) return;
const ziel = v === 'mitgliedschaft' ? $('.laufzeit') : $('[data-karten]');
scrollTo(0, ziel.getBoundingClientRect().top + scrollY - (innerWidth < 721 ? 10 : 90));
zeiger.style.transform = `translate(${innerWidth * .6}px, ${innerHeight * .6}px)`;
await warte(700);
if (v === 'mitgliedschaft') {
for (const [l, t] of [['flex', 'voll'], ['flex', 'sauna'], ['jahr', 'sauna'], ['jahr', 'tag'], ['jahr', 'voll']]) {
if (!laeuft) return;
const r = $(`[data-laufzeit][value="${l}"]`);
if (!r.checked) { await gehe(r.nextElementSibling); }
await warte(500);
await gehe($(`[data-wahl="${t}"]`)); await warte(1300);
}
} else {
const s = $('[data-oft]');
scrollTo(0, s.getBoundingClientRect().top + scrollY - 200);
for (const n of [1, 2, 3, 4, 2]) { if (!laeuft) return; await gehe(s, false); s.value = n; s.dispatchEvent(new Event('input')); await warte(1500); }
}
if (laeuft) timer.push(setTimeout(runde, 400));
}
function neustart() { pause(); lauf = 'jahr'; gewaehlt = 'voll'; $('[data-laufzeit][value="jahr"]').checked = true; $('[data-oft]').value = 1; variante(v, false); scrollTo(0, 0); }
function vorfuehren() { pause(); html.classList.add('vorfuehrung'); laeuft = true; runde(); }
function pause() { laeuft = false; timer.forEach(clearTimeout); timer = []; html.classList.remove('vorfuehrung'); }
function weiter() { if (!laeuft) vorfuehren(); }
variante(new URLSearchParams(location.search).get('v') || 'mitgliedschaft', false);
})();
</script>
</body>
</html>