Zum Inhalt
← Sektionen & Funktionen

Leistungsseite: Antwort zuerst

Eine Leistungsseite, die mit der Antwort beginnt: Leistung und Ort als H1, darunter in drei Sätzen was, für wen, Preis, Dauer und Gebiet, danach Ablauf, Preise, Beispiel, Fragen, Ansprechperson und eine Handlung.

Handwerksbetriebe mit Festpreis nach Aufmaß: ab-Preis, Einsatztag, Einzugsgebiet in km, Beispiel als Vorher/Nachher mit Ort und Jahr.

Vorlage für die Unterseite einer einzelnen Leistung, etwa „Dachfenster tauschen in Lemgo“ oder „Professionelle Zahnreinigung in Lemgo“. Die H1 nennt Leistung und Ort. Direkt darunter steht die Antwort in zwei bis drei Sätzen: was, für wen, Preisrahmen, Dauer, Einzugsgebiet. Es folgen Ablauf, Preise mit ab-Preis, ein Beispiel (vorher/nachher oder Referenz), Fragen als echte Zwischenüberschriften, eine Ansprechperson mit Foto und genau eine Handlung (Anfrage, Termin oder Anruf). Brotkrumen, das Datum der letzten Prüfung und Verweise auf verwandte Leistungen gehören fest dazu. Das Gerüst ist fest, die Gestaltung kommt aus dem jeweiligen Rezept.

Wer über Google oder eine KI-Suche auf einer Leistungsseite landet, hat eine konkrete Frage. Die Antwort steht in den ersten Zeilen, der Rest belegt sie. Beschreibende Überschriften helfen beim Überfliegen und machen die Seite als Antwortbaustein brauchbar. Ort, Preisrahmen und Prüfdatum schaffen Vertrauen, die eine Handlung am Ende verhindert Entscheidungsstau.

Wann einsetzen

Für jede Leistung, die eine eigene Unterseite verdient: Handwerk, Praxis, Kanzlei, Fahrschule, Immobilien (Verkaufen, Bewerten), Kurse, Friseur, Fitness, Pflege, Kfz. Spätestens ab fünf Leistungen, die auf der Startseite nur als Akkordeon stehen.

Wann nicht

Für Projekte und Referenzen (dafür case-study-geruest). Für Leistungen ohne belastbare Angaben zu Preis oder Dauer nur, wenn statt Zahlen ehrlich gesagt wird, wovon sie abhängen. Nicht als Massenware mit getauschtem Ortsnamen für zwanzig Nachbarorte.

Varianten

  • Handwerk: Leistung mit ab-Preis, Einsatztag und Einzugsgebiet, Beispiel als Vorher/Nachher
  • Praxis: Behandlung in Alltagssprache, Selbstzahler-Preis, Dauer der Sitzung, Kassenfrage als Zwischenüberschrift
  • Kanzlei oder Beratung: Erstgespräch mit festem Preis, Ablauf in Schritten, Fristen als Frage
  • Kurs oder Fahrschule: Kursleistung mit Terminen, Dauer in Stunden, Voraussetzungen als Frage

Umsetzung

Eine Seitenvorlage mit festen Blöcken in fester Reihenfolge: Brotkrumen als <nav aria-label="Brotkrumen"> mit <ol> und aria-current="page", H1 „Leistung in Ort“, Antwort als erster Absatz (2–3 Sätze, Zahlen als Text), daneben eine <dl> mit Preis ab, Dauer, Einzugsgebiet und Zielgruppe. Danach Sprungliste, Ablauf als <ol>, Preise als Tabelle mit ab-Preisen und Stand, Beispiel als <figure> mit Ort und Jahr, Fragen als <h3> mit Antwortabsatz (nicht im Akkordeon versteckt), Ansprechperson mit Foto, Rolle und Durchwahl, eine primäre Handlung, verwandte Leistungen als Linkliste. Prüfdatum als <time datetime> mit Namen der prüfenden Person. Alle Werte stammen aus einem Datensatz je Leistung, aus dem auch JSON-LD (BreadcrumbList, Service mit areaServed und Offer) entsteht, damit Markup und sichtbarer Text nicht auseinanderlaufen. FAQ-Markup bringt seit Mai 2026 keine Rich Results mehr und ist verzichtbar. Bei Praxen kein Vorher/Nachher von Behandlungsergebnissen (Heilmittelwerbung), stattdessen den Ablauf eines Termins zeigen. Preisangaben, Gesundheitsaussagen und Förderhinweise vom Kunden prüfen lassen.

Robust umsetzen

Telefon. Einspaltig in derselben Reihenfolge: Antwort und Faktenliste vor jedem Bild. Eine feste Leiste unten mit „Anrufen“ und der einen Handlung. Preistabelle als Liste, Sprungliste waagerecht umbrechend, kein seitliches Scrollen.

Ohne Bewegung. Keine Bewegung nötig. Markierungen und Wechsel erscheinen ohne Übergang, Sprunglinks springen ohne weiches Scrollen.

Barrierefrei. Eine H1, beschreibende H2 und Fragen als H3 ergeben eine Gliederung, die Screenreader als Überschriftenliste anbieten. Brotkrumen als nav mit aria-current="page". Preise und Dauer als Text, nicht nur als Grafik. Telefon als tel:-Link mit sichtbarer Nummer. Prüfdatum als <time>. Kontrast mindestens 4,5 : 1, Signalfarbe nur mit dunkler Schrift.

Code

<!DOCTYPE html>
<!--
  Leistungsseite: Antwort zuerst
  Fundus · Sektionen & Funktionen · https://cecodesign.de/lab/fundus/sektionen/leistungsseite-antwort-zuerst/
  Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de" data-v="handwerk">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Leistungsseite: Antwort zuerst · 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: 'Special Gothic'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/special-gothic:vf@latest/latin-standard-normal.woff2') format('woff2-variations'); font-weight: 400 700; font-stretch: 75% 125%; font-display: swap; }
@font-face { font-family: 'Atkinson Hyperlegible Next'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/atkinson-hyperlegible-next:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 200 800; font-display: swap; }
@font-face { font-family: 'Hedvig Letters Serif'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/hedvig-letters-serif:vf@latest/latin-opsz-normal.woff2') format('woff2-variations'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Google Sans Flex'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/google-sans-flex:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 100 1000; 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; }

/* Gerüst fest, Token aus dem Rezept. Handwerk: handwerk-werkstatt-klartext */
:root {
  --grund: #efedea; --flaeche: #e4e0d9; --karte: #f7f6f3; --linie: #cfc9be; --linie-stark: #1a1a1a;
  --text: #1a1a1a; --leise: #4f4b45; --meta: #67625a;
  --marke: #1a1a1a; --marke-text: #efedea;
  --signal: #fb6100; --signal-tief: #e25700; --signal-text: #1a1a1a;
  --mark: #fdb98a; --geruest: #a8380a;
  --h-schrift: 'Special Gothic', 'Arial Narrow', sans-serif; --h-gewicht: 700; --h-laufweite: -0.01em; --h-zeile: 1.04; --h-breite: 100%;
  --t-schrift: 'Atkinson Hyperlegible Next', system-ui, sans-serif;
  --label-schrift: 'Special Gothic', sans-serif; --label-breite: 75%; --label-laufweite: .04em; --label-gewicht: 600;
  --rund: 0px; --rund-knopf: 0px;
  --kurve: cubic-bezier(0.4, 0, 0.2, 1); --zu: cubic-bezier(0.4, 0, 1, 1);
  --ui: 150ms; --mark-dauer: 240ms; --wechsel: 280ms;
}
/* Praxis: praxis-gesundheit-ruhig */
:root[data-v="praxis"] {
  --grund: #f5f7f7; --flaeche: #e8ecea; --karte: #ffffff; --linie: #d3d9d7; --linie-stark: #005450;
  --text: #2f3535; --leise: #475353; --meta: #566262;
  --marke: #005450; --marke-text: #ffffff;
  --signal: #005450; --signal-tief: #003f3c; --signal-text: #ffffff;
  --mark: #ffd99a; --geruest: #8a5a00;
  --h-schrift: 'Hedvig Letters Serif', Georgia, serif; --h-gewicht: 400; --h-laufweite: -0.01em; --h-zeile: 1.1;
  --t-schrift: 'Google Sans Flex', system-ui, sans-serif;
  --label-schrift: 'Google Sans Flex', sans-serif; --label-breite: 100%; --label-laufweite: .02em; --label-gewicht: 600;
  --rund: 14px; --rund-knopf: 999px;
  --kurve: cubic-bezier(0.2, 0, 0.38, 0.9); --zu: cubic-bezier(0.2, 0, 1, 0.9);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--grund); color: var(--text); font-family: var(--t-schrift); -webkit-font-smoothing: antialiased; transition: background-color var(--wechsel) var(--kurve), color var(--wechsel) var(--kurve); }
body { min-height: 100svh; font-size: 17px; line-height: 1.55; }
a { color: inherit; }
button { font: inherit; color: inherit; }
.ziffern { font-variant-numeric: tabular-nums lining-nums; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.label { font: var(--label-gewicht) 13px/1.2 var(--label-schrift); font-stretch: var(--label-breite); letter-spacing: var(--label-laufweite); text-transform: uppercase; color: var(--meta); }
:root[data-v="praxis"] .label { text-transform: none; font-size: 14px; }
.mono { font: 500 10.5px/1.4 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; }
:focus-visible { outline: 2px solid var(--text); outline-offset: 3px; }
h1, h2, h3 { font-family: var(--h-schrift); font-weight: var(--h-gewicht); letter-spacing: var(--h-laufweite); line-height: var(--h-zeile); text-wrap: balance; }
p { text-wrap: pretty; }
.huelle { max-width: 1180px; margin: 0 auto; padding: 0 clamp(18px, 3.4vw, 40px); }

/* Kopf */
.kopf { border-bottom: 1px solid var(--linie); background: var(--grund); transition: background-color var(--wechsel) var(--kurve); }
.kopf .huelle { display: flex; align-items: center; gap: 22px; min-height: 66px; }
.marke { display: flex; align-items: center; gap: 10px; text-decoration: none; margin-right: auto; }
.marke svg { width: 30px; height: 30px; flex: none; }
.marke b { font: 700 19px/1 var(--h-schrift); letter-spacing: -.01em; }
:root[data-v="handwerk"] .marke b { text-transform: uppercase; }
:root[data-v="praxis"] .marke b { font-weight: 400; font-size: 21px; }
.marke small { display: block; font-size: 12.5px; color: var(--meta); margin-top: 3px; letter-spacing: 0; }
.kopf nav ul { display: flex; gap: 22px; list-style: none; }
.kopf nav a { text-decoration: none; font-size: 15.5px; color: var(--leise); padding: 6px 0; border-bottom: 2px solid transparent; transition: color var(--ui) var(--kurve), border-color var(--ui) var(--kurve); }
.kopf nav a:hover, .kopf nav a[aria-current] { color: var(--text); border-color: var(--text); }
.tel { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; font-size: 15px; text-decoration: none; padding: 9px 14px; border: 1.5px solid var(--linie-stark); border-radius: var(--rund-knopf); white-space: nowrap; transition: background-color var(--ui) var(--kurve), color var(--ui) var(--kurve); }
.tel:hover { background: var(--marke); color: var(--marke-text); border-color: var(--marke); }
.tel svg { width: 16px; height: 16px; flex: none; }

/* Brotkrumen */
.krumen { padding: 18px 0 0; }
.krumen ol { display: flex; flex-wrap: wrap; gap: 4px 0; list-style: none; font-size: 14.5px; color: var(--meta); }
.krumen li + li::before { content: '/'; margin: 0 10px; color: var(--linie-stark); opacity: .5; }
.krumen a { text-decoration: none; border-bottom: 1px solid var(--linie); transition: border-color var(--ui) var(--kurve), color var(--ui) var(--kurve); }
.krumen a:hover { color: var(--text); border-color: var(--text); }
.krumen [aria-current] { color: var(--text); }

/* Einstieg: H1, Antwort, Fakten */
.einstieg { display: grid; grid-template-columns: minmax(0, 1.36fr) minmax(0, .96fr); gap: clamp(22px, 4vw, 64px); padding: clamp(22px, 3vw, 40px) 0 clamp(30px, 4vw, 54px); align-items: start; border-bottom: 1px solid var(--linie); }
.einstieg .label { display: flex; align-items: center; gap: 10px; }
:root[data-v="handwerk"] .einstieg .label::before { content: ''; width: 18px; height: 2px; background: var(--text); }
h1 { font-size: clamp(2.3rem, 1.4rem + 3.2vw, 4rem); margin-top: 12px; }
:root[data-v="praxis"] h1 { font-size: clamp(2.25rem, 1.4rem + 3vw, 3.7rem); }
h1 .ort { color: var(--leise); }
:root[data-v="handwerk"] h1 .ort { font-stretch: 75%; }
:root[data-v="praxis"] h1 .ort { font-style: normal; color: var(--marke); }
.antwort { font-size: clamp(18px, 1rem + .35vw, 21px); line-height: 1.55; max-width: 62ch; margin-top: 20px; }
.teil { background-image: linear-gradient(var(--mark), var(--mark)); background-repeat: no-repeat; background-position: 0 90%; background-size: 0% 46%; transition: background-size var(--mark-dauer) var(--kurve); border-radius: 2px; }
.teil.an { background-size: 100% 46%; }
.reduziert .teil { transition: none; }
.geprueft { display: flex; align-items: flex-start; gap: 10px; margin-top: 20px; font-size: 14.5px; color: var(--meta); }
.geprueft .punkt { flex: none; margin-top: 7px; width: 8px; height: 8px; border-radius: 50%; background: #3f7a3a; box-shadow: 0 0 0 3px rgba(63, 122, 58, .18); }
.geprueft b { color: var(--text); font-weight: 600; }

.fakten { background: var(--karte); border: 1px solid var(--linie); border-radius: var(--rund); padding: clamp(18px, 2vw, 26px); display: grid; gap: 16px; transition: background-color var(--wechsel) var(--kurve), border-color var(--wechsel) var(--kurve); }
:root[data-v="handwerk"] .fakten { border-top: 4px solid var(--text); }
.fakten dl { display: grid; }
.fakten .zeile { display: grid; grid-template-columns: 112px 1fr; gap: 12px; padding: 11px 0 11px 12px; border-top: 1px solid var(--linie); position: relative; transition: background-color var(--mark-dauer) var(--kurve); }
.fakten .zeile:first-child { border-top: 0; }
.fakten .zeile::before { content: ''; position: absolute; left: 0; top: 10px; bottom: 10px; width: 3px; background: var(--mark); transform: scaleY(0); transform-origin: top; transition: transform var(--mark-dauer) var(--kurve); }
.fakten .zeile.an::before { transform: scaleY(1); }
.fakten dt { font: var(--label-gewicht) 13px/1.35 var(--label-schrift); font-stretch: var(--label-breite); letter-spacing: var(--label-laufweite); text-transform: uppercase; color: var(--meta); padding-top: 3px; }
:root[data-v="praxis"] .fakten dt { text-transform: none; font-size: 14px; }
.fakten dd { font-weight: 600; line-height: 1.35; }
.fakten dd small { display: block; font-weight: 400; font-size: 14px; color: var(--meta); margin-top: 2px; }
.fakten dd.gross { font: var(--h-gewicht) 28px/1.05 var(--h-schrift); letter-spacing: -.01em; }
.fakten dd.gross small { font-family: var(--t-schrift); letter-spacing: 0; margin-top: 5px; }

.handlung { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 52px; padding: 0 22px; background: var(--signal); color: var(--signal-text); font-weight: 700; font-size: 16.5px; text-decoration: none; border: 0; border-radius: var(--rund-knopf); cursor: pointer; transition: background-color var(--ui) var(--kurve), transform var(--ui) var(--kurve); }
.handlung:hover { background: var(--signal-tief); }
.handlung:active { transform: translateY(1px); }
.handlung svg { width: 18px; height: 18px; transition: transform var(--ui) var(--kurve); }
.handlung:hover svg { transform: translateX(3px); }
.fakten .handlung { width: 100%; padding: 0 16px; font-size: 16px; }
.fakten .oder { font-size: 14.5px; color: var(--meta); text-align: center; }
.fakten .oder a { color: var(--text); font-weight: 700; text-decoration-thickness: 1px; text-underline-offset: 3px; }

/* Sprungliste */
.sprung { margin-top: clamp(26px, 3.4vw, 44px); padding-top: 16px; border-top: 1px solid var(--linie); display: grid; gap: 8px; }
.sprung ol { display: flex; flex-wrap: wrap; gap: 4px 22px; list-style: none; counter-reset: s; }
.sprung li { counter-increment: s; }
.sprung a { text-decoration: none; font-weight: 600; font-size: 15.5px; display: inline-flex; gap: 8px; align-items: baseline; padding: 4px 0; border-bottom: 1.5px solid transparent; transition: border-color var(--ui) var(--kurve); }
.sprung a::before { content: counter(s, decimal-leading-zero); font: 500 11px/1 'Geist Mono', monospace; color: var(--meta); }
.sprung a:hover { border-color: var(--text); }

/* Blöcke */
.block { display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 2fr); gap: clamp(18px, 4vw, 64px); padding: clamp(34px, 4.6vw, 64px) 0; border-bottom: 1px solid var(--linie); scroll-margin-top: 12px; }
.block > header { position: sticky; top: 20px; align-self: start; }
.block > header h2 { font-size: clamp(1.6rem, 1.15rem + 1.6vw, 2.3rem); }
.block > header p { color: var(--meta); font-size: 15px; margin-top: 10px; max-width: 30ch; }
.block > header .label { margin-bottom: 10px; }

.schritte { list-style: none; display: grid; counter-reset: n; }
.schritte li { counter-increment: n; display: grid; grid-template-columns: 56px 1fr auto; gap: 4px 18px; padding: 16px 0; border-top: 1px solid var(--linie); }
.schritte li:first-child { border-top: 0; padding-top: 0; }
.schritte li::before { content: counter(n); grid-row: span 2; font: var(--h-gewicht) 34px/1 var(--h-schrift); color: var(--meta); }
:root[data-v="praxis"] .schritte li::before { width: 40px; height: 40px; border-radius: 50%; background: var(--flaeche); color: var(--marke); font-size: 20px; display: grid; place-items: center; }
.schritte h3 { font-family: var(--t-schrift); font-weight: 700; font-size: 18px; letter-spacing: 0; line-height: 1.3; }
.schritte p { grid-column: 2 / 4; color: var(--leise); max-width: 60ch; }
.schritte .zeit { font-size: 14px; color: var(--meta); white-space: nowrap; align-self: baseline; }

.preise { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums lining-nums; }
.preise th, .preise td { text-align: left; padding: 14px 0; border-top: 1px solid var(--linie); vertical-align: baseline; }
.preise thead th { border-top: 0; padding-top: 0; font: var(--label-gewicht) 13px/1.2 var(--label-schrift); font-stretch: var(--label-breite); letter-spacing: var(--label-laufweite); text-transform: uppercase; color: var(--meta); }
:root[data-v="praxis"] .preise thead th { text-transform: none; font-size: 14px; }
.preise td:last-child, .preise th:last-child { text-align: right; white-space: nowrap; padding-left: 18px; }
.preise td:last-child { font: var(--h-gewicht) 21px/1.1 var(--h-schrift); }
.preise td small { display: block; color: var(--meta); font-size: 14px; margin-top: 2px; }
.preise tr.zusatz td:first-child { padding-left: 18px; position: relative; }
.preise tr.zusatz td:first-child::before { content: '+'; position: absolute; left: 0; color: var(--meta); font-weight: 700; }
.hinweis { margin-top: 16px; font-size: 14.5px; color: var(--meta); display: grid; gap: 6px; max-width: 70ch; }
.platzhalter { display: inline-block; font: 500 10.5px/1.3 'Geist Mono', monospace; letter-spacing: .1em; text-transform: uppercase; color: var(--geruest); border: 1px dashed currentColor; padding: 3px 7px; border-radius: 4px; width: fit-content; }

.beispiel { display: grid; gap: 16px; }
.paar { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.paar figure { margin: 0; background: var(--flaeche); border-radius: var(--rund); overflow: hidden; }
.paar svg { display: block; width: 100%; height: auto; }
.paar figcaption { display: flex; justify-content: space-between; gap: 10px; padding: 10px 14px 12px; font-size: 14px; color: var(--meta); }
.paar figcaption b { color: var(--text); font: var(--label-gewicht) 13px/1.3 var(--label-schrift); font-stretch: var(--label-breite); letter-spacing: var(--label-laufweite); text-transform: uppercase; }
.steckbrief { display: flex; flex-wrap: wrap; gap: 8px 26px; padding: 14px 0 0; border-top: 1px solid var(--linie); }
.steckbrief div { display: grid; }
.steckbrief dt { font-size: 13.5px; color: var(--meta); }
.steckbrief dd { font: var(--h-gewicht) 22px/1.15 var(--h-schrift); }
.beispiel > p { max-width: 62ch; color: var(--leise); }
.stunde { display: flex; gap: 4px; min-height: 120px; }
.stunde div { position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 8px; padding: 12px; border-radius: 10px; background: var(--flaeche); min-width: 0; }
.stunde div:nth-child(2) { background: #cfe0dc; } .stunde div:nth-child(3) { background: #b7d1cb; } .stunde div:nth-child(4) { background: #9fc2ba; } .stunde div:nth-child(5) { background: #ffe1ae; }
.stunde b { font-weight: 600; font-size: 15px; line-height: 1.25; hyphens: auto; }
.stunde span { font-size: 13.5px; color: var(--text); }
.stunde-achse { display: flex; justify-content: space-between; font-size: 13px; color: var(--meta); margin-top: 6px; }

.fragen { display: grid; grid-template-columns: 1fr 1fr; gap: 26px clamp(20px, 3vw, 40px); }
.fragen section { display: grid; gap: 8px; align-content: start; padding-top: 14px; border-top: 2px solid var(--text); }
:root[data-v="praxis"] .fragen section { border-top: 1px solid var(--linie); }
.fragen h3 { font-size: clamp(1.15rem, 1rem + .5vw, 1.4rem); line-height: 1.2; }
:root[data-v="handwerk"] .fragen h3 { font-family: var(--t-schrift); font-weight: 700; letter-spacing: 0; }
.fragen p { color: var(--leise); }

.person { display: grid; grid-template-columns: minmax(0, 220px) 1fr; gap: clamp(18px, 3vw, 36px); align-items: start; }
.person figure { margin: 0; border-radius: var(--rund); overflow: hidden; background: var(--flaeche); aspect-ratio: 4 / 5; }
.person figure svg { width: 100%; height: 100%; display: block; }
.person h3 { font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2rem); }
.person .rolle { color: var(--meta); margin-top: 4px; }
.person .zitat { margin-top: 14px; max-width: 54ch; font-size: 18px; }
:root[data-v="praxis"] .person .zitat { font-family: var(--h-schrift); font-size: 20px; line-height: 1.4; }
.person .knoepfe { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; margin-top: 22px; }
.person .knoepfe a.direkt { font-weight: 700; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.person .erreichbar { font-size: 14.5px; color: var(--meta); margin-top: 12px; }

.verwandt ul { list-style: none; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.verwandt a { display: grid; gap: 6px; height: 100%; padding: 16px 18px 18px; background: var(--karte); border: 1px solid var(--linie); border-radius: var(--rund); text-decoration: none; transition: border-color var(--ui) var(--kurve), background-color var(--ui) var(--kurve); }
.verwandt a:hover { border-color: var(--text); }
.verwandt b { font-size: 17px; display: flex; justify-content: space-between; gap: 10px; }
.verwandt b::after { content: '→'; transition: transform var(--ui) var(--kurve); }
.verwandt a:hover b::after { transform: translateX(3px); }
.verwandt span { font-size: 14.5px; color: var(--meta); }

.fuss { padding: 26px 0 120px; font-size: 14px; color: var(--meta); }
.fuss .huelle { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 20px; }

/* Gerüst-Ansicht: zeigt die festen Bausteine der Vorlage */
[data-g] { position: relative; }
.g-tag { position: absolute; z-index: 3; top: -11px; left: -6px; display: none; font: 500 10px/1 'Geist Mono', monospace; letter-spacing: .12em; text-transform: uppercase; background: var(--geruest); color: #fff; padding: 5px 7px 4px; border-radius: 3px; white-space: nowrap; pointer-events: none; }
:root[data-geruest] .g-tag { display: block; }
:root[data-geruest] [data-g] { outline: 1.5px dashed var(--geruest); outline-offset: 6px; }
:root[data-geruest] [data-g].block { outline-offset: -1px; }
:root[data-geruest] .block > .g-tag { top: 10px; left: 0; }

/* mobile Leiste */
.leiste { display: none; }

/* Pult */
.pult { position: fixed; right: 14px; bottom: 14px; z-index: 20; display: grid; gap: 9px; padding: 12px; width: 268px; background: rgba(20, 20, 19, .92); color: #f1efea; border-radius: 10px; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: 0 12px 34px rgba(0, 0, 0, .2); }
.pult .mono { color: rgba(241, 239, 234, .72); }
.pult .reihe { display: grid; grid-template-columns: 1fr 1fr; border: 1px solid rgba(241, 239, 234, .24); border-radius: 6px; overflow: hidden; }
.pult button { background: none; border: 0; padding: 8px 10px; font: 500 12.5px/1.2 'Geist Mono', monospace; letter-spacing: .04em; color: rgba(241, 239, 234, .82); cursor: pointer; transition: background-color var(--ui) var(--kurve), color var(--ui) var(--kurve); }
.pult .reihe button + button { border-left: 1px solid rgba(241, 239, 234, .24); }
.pult button[aria-pressed="true"] { background: #f1efea; color: #141413; }
.pult .einzeln { border: 1px solid rgba(241, 239, 234, .24); border-radius: 6px; text-align: left; display: flex; justify-content: space-between; }
.pult .einzeln:hover, .pult .reihe button:hover:not([aria-pressed="true"]) { background: rgba(241, 239, 234, .1); }
.pult :focus-visible { outline: 2px solid #f1efea; outline-offset: 2px; }
.pult .werte { font: 400 10.5px/1.5 'Geist Mono', monospace; color: rgba(241, 239, 234, .72); }

dialog.ld { margin: auto; width: min(720px, calc(100vw - 28px)); max-height: min(80vh, 680px); border: 0; border-radius: 12px; padding: 0; background: #141413; color: #f1efea; box-shadow: 0 30px 80px rgba(0, 0, 0, .35); }
dialog.ld::backdrop { background: rgba(20, 20, 19, .45); }
dialog.ld header { display: flex; justify-content: space-between; align-items: start; gap: 16px; padding: 18px 20px 12px; border-bottom: 1px solid rgba(241, 239, 234, .16); }
dialog.ld h2 { font: 600 17px/1.3 'Atkinson Hyperlegible Next', sans-serif; letter-spacing: 0; }
dialog.ld header p { font-size: 14px; color: rgba(241, 239, 234, .72); margin-top: 4px; }
dialog.ld button { background: none; border: 1px solid rgba(241, 239, 234, .3); color: inherit; border-radius: 6px; padding: 6px 10px; cursor: pointer; font: 500 12px/1 'Geist Mono', monospace; }
dialog.ld pre { margin: 0; padding: 16px 20px 20px; overflow: auto; max-height: calc(min(80vh, 680px) - 90px); font: 400 12.5px/1.55 'Geist Mono', monospace; color: #e7e3da; white-space: pre-wrap; word-break: break-word; }
dialog.ld pre .k { color: #f6a56b; }
dialog.ld:focus-visible { outline: none; }

/* Zeiger der Vorführung */
.zeiger { position: fixed; left: 0; top: 0; z-index: 30; width: 22px; height: 22px; margin: -4px 0 0 -4px; pointer-events: none; opacity: 0; transition: translate 600ms var(--kurve), opacity 200ms var(--kurve); }
.zeiger.an { opacity: 1; }
.reduziert .zeiger { transition: opacity 150ms linear; }

:root.reduziert *, :root.reduziert *::before, :root.reduziert *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
.vorschau body { overflow: hidden; }
.vorschau .pult, .vorschau .leiste { display: none !important; }

@media (max-width: 1000px) { .kopf nav { display: none; } }
@media (max-width: 900px) {
  .einstieg { grid-template-columns: minmax(0, 1fr); }
  .block { grid-template-columns: minmax(0, 1fr); }
  .block > header { position: static; }
  .block > header p { max-width: 60ch; }
}
@media (max-width: 760px) {
  body { font-size: 17px; }
  .kopf .tel span { display: none; }
  .kopf .tel { padding: 9px 11px; }
  .marke small { display: none; }
  .fragen { grid-template-columns: minmax(0, 1fr); }
  .verwandt ul { grid-template-columns: minmax(0, 1fr); }
  .fakten .zeile { grid-template-columns: 92px minmax(0, 1fr); }
  .person { grid-template-columns: 110px 1fr; }
  .person .zitat, .person .knoepfe, .person .erreichbar { grid-column: 1 / -1; }
  .paar { grid-template-columns: 1fr; }
  .schritte li { grid-template-columns: 40px 1fr; }
  .schritte li::before { font-size: 26px; }
  .schritte .zeit { grid-column: 2; }
  .schritte p { grid-column: 2; }
  .preise thead { display: none; }
  .preise tr { display: grid; grid-template-columns: 1fr auto; gap: 4px 14px; padding: 12px 0; border-top: 1px solid var(--linie); }
  .preise td { border: 0; padding: 0; }
  .preise td:last-child { font-size: 19px; }
  .stunde { flex-direction: column; min-height: 0; }
  .stunde div { flex: none !important; flex-direction: row; align-items: baseline; padding: 10px 12px; }
  .leiste { display: grid; grid-template-columns: auto 1fr; gap: 8px; position: fixed; left: 0; right: 0; bottom: 0; z-index: 15; padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); background: var(--grund); border-top: 1px solid var(--linie); }
  .leiste .tel { justify-content: center; min-height: 48px; }
  .leiste .handlung { min-height: 48px; font-size: 15.5px; padding: 0 14px; }
  .pult { bottom: 82px; left: 12px; right: 12px; width: auto; }
}
@media (hover: none) {
  .handlung:hover svg, .verwandt a:hover b::after { transform: none; }
  .verwandt a:active { border-color: var(--text); }
}
@media (forced-colors: active) {
  button, [role=button], [role=tab], .handlung { border: 1px solid ButtonText; }
  .teil.an { background: Mark; color: MarkText; }
  .fakten .zeile.an::before { background: Highlight; }
}
</style>
</head>
<body>
<header class="kopf">
  <div class="huelle">
    <a class="marke" href="#" data-ref="marke"></a>
    <nav aria-label="Hauptmenü"><ul data-ref="menue"></ul></nav>
    <a class="tel" data-ref="kopftel" href="#"></a>
  </div>
</header>

<main id="inhalt">
  <div class="huelle">
    <nav class="krumen" aria-label="Brotkrumen" data-g="1 Brotkrumen"><ol data-ref="krumen"></ol></nav>

    <section class="einstieg" aria-labelledby="h1">
      <div>
        <p class="label" data-ref="gewerk"></p>
        <h1 id="h1" data-g="2 H1: Leistung + Ort" data-ref="h1"></h1>
        <p class="antwort" data-g="3 Antwort in 2–3 Sätzen" data-ref="antwort"></p>
        <p class="geprueft" data-g="4 Datum der letzten Prüfung" data-ref="geprueft"></p>
        <nav class="sprung" aria-labelledby="sprung-titel">
          <span class="label" id="sprung-titel">Auf dieser Seite</span>
          <ol data-ref="sprung"></ol>
        </nav>
      </div>
      <aside class="fakten" aria-label="Auf einen Blick" data-g="5 Fakten + eine Handlung">
        <p class="label">Auf einen Blick</p>
        <dl data-ref="fakten"></dl>
        <a class="handlung" href="#ansprech" data-ref="handlung1"></a>
        <p class="oder" data-ref="oder"></p>
      </aside>
    </section>

    <section class="block" id="ablauf" aria-labelledby="h-ablauf" data-g="6 Ablauf">
      <header><h2 id="h-ablauf" data-ref="h-ablauf"></h2><p data-ref="ablauf-unter"></p></header>
      <ol class="schritte" data-ref="schritte"></ol>
    </section>

    <section class="block" id="preise" aria-labelledby="h-preise" data-g="7 Preise mit ab-Preis">
      <header><h2 id="h-preise" data-ref="h-preise"></h2><p data-ref="preise-unter"></p></header>
      <div>
        <table class="preise">
          <caption class="vh" data-ref="preise-cap"></caption>
          <thead><tr><th scope="col" data-ref="preise-sp1"></th><th scope="col">Preis</th></tr></thead>
          <tbody data-ref="preisliste"></tbody>
        </table>
        <div class="hinweis" data-ref="preise-hinweis"></div>
      </div>
    </section>

    <section class="block" id="beispiel" aria-labelledby="h-beispiel" data-g="8 Beispiel">
      <header><h2 id="h-beispiel" data-ref="h-beispiel"></h2><p data-ref="beispiel-unter"></p></header>
      <div class="beispiel" data-ref="beispiel"></div>
    </section>

    <section class="block" id="fragen" aria-labelledby="h-fragen" data-g="9 Fragen als echte Überschriften">
      <header><h2 id="h-fragen" data-ref="h-fragen"></h2><p data-ref="fragen-unter"></p></header>
      <div class="fragen" data-ref="fragen"></div>
    </section>

    <section class="block" id="ansprech" aria-labelledby="h-ansprech" data-g="10 Ansprechperson + Handlung">
      <header><h2 id="h-ansprech" data-ref="h-ansprech"></h2></header>
      <div class="person">
        <figure data-ref="foto"></figure>
        <div>
          <h3 data-ref="p-name"></h3>
          <p class="rolle" data-ref="p-rolle"></p>
          <p class="zitat" data-ref="p-zitat"></p>
          <div class="knoepfe">
            <a class="handlung" href="#" data-ref="handlung2"></a>
            <a class="direkt" href="#" data-ref="p-tel"></a>
          </div>
          <p class="erreichbar" data-ref="p-zeiten"></p>
        </div>
      </div>
    </section>

    <section class="block verwandt" id="verwandt" aria-labelledby="h-verwandt" data-g="11 Verwandte Leistungen">
      <header><h2 id="h-verwandt" data-ref="h-verwandt"></h2></header>
      <ul data-ref="verwandt"></ul>
    </section>
  </div>
</main>

<footer class="fuss"><div class="huelle" data-ref="fuss"></div></footer>

<div class="leiste" data-ref="leiste"></div>

<div class="pult" aria-label="Steuerung der Demo" role="group">
  <span class="mono">Branche · Token</span>
  <div class="reihe">
    <button type="button" data-v="handwerk" aria-pressed="true">Handwerk</button>
    <button type="button" data-v="praxis" aria-pressed="false">Praxis</button>
  </div>
  <button type="button" class="einzeln" data-geruest-knopf aria-pressed="false"><span>Gerüst zeigen</span><span aria-hidden="true">⌗</span></button>
  <button type="button" class="einzeln" data-ld-knopf><span>JSON-LD zeigen</span><span aria-hidden="true">{ }</span></button>
  <p class="werte" data-ref="werte"></p>
</div>

<dialog class="ld" aria-labelledby="ld-titel">
  <header>
    <div><h2 id="ld-titel">JSON-LD dieser Leistungsseite</h2><p>Entsteht aus demselben Datensatz wie der sichtbare Text. Gehört als &lt;script type="application/ld+json"&gt; in den Kopf.</p></div>
    <button type="button" data-ld-zu>Schließen</button>
  </header>
  <pre data-ref="ld-text"></pre>
</dialog>

<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="#141413" stroke="#fff" stroke-width="1.4" stroke-linejoin="round"/></svg>

<script type="application/ld+json" id="ld"></script>

<script>
(() => {
  const html = document.documentElement;
  const reduziert = () => html.classList.contains('reduziert');
  const $ = (s, r = document) => r.querySelector(s);
  const ref = n => document.querySelector(`[data-ref="${n}"]`);
  const PRUEFDATUM = '2026-11-02', PRUEFDATUM_TEXT = '02.11.2026';
  const eur = n => new Intl.NumberFormat('de-DE', { maximumFractionDigits: 0 }).format(n) + ' €';
  const pfeil = '<svg viewBox="0 0 18 18" aria-hidden="true"><path d="M3 9h11M10 4.5 14.5 9 10 13.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square"/></svg>';
  const hoerer = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M4.2 1.5 6.4 4.4 5 6.1c.8 1.8 2.2 3.2 4 4l1.6-1.4 2.9 2.2-1.1 2.4c-.3.6-.9.9-1.5.8C6.3 13.4 2.6 9.7 1.9 5.1c-.1-.6.2-1.2.8-1.5Z" fill="currentColor"/></svg>';

  // ------------------------------------------------------------------
  // Ein Datensatz je Leistung: daraus entstehen Seite und JSON-LD
  // ------------------------------------------------------------------
  const DATEN = {
    handwerk: {
      betrieb: { name: 'Dachwerk Begatal', zusatz: 'Dachdeckermeister · Lemgo', typ: 'RoofingContractor', ort: 'Lemgo', plz: '32657', strasse: 'Am Lüttfeld 14', tel: '05261 40 77 12' },
      menue: ['Leistungen', 'Referenzen', 'Betrieb', 'Kontakt'],
      krumen: ['Start', 'Leistungen', 'Dachfenster tauschen'],
      gewerk: 'Leistung · Dach',
      leistung: 'Dachfenster tauschen', ort: 'Lemgo',
      antwort: [
        'Wir ', ['was', 'tauschen alte Dachfenster gegen neue, gedämmte Fenster'], ' in gleicher oder größerer Größe, ',
        ['wer', 'für Eigentümer von Ein- und Zweifamilienhäusern'], '. Ein Fenster in gleicher Größe kostet ',
        ['preis', 'ab 1.150 € inklusive Einbau, Anschluss und Entsorgung'], ' und ist ',
        ['dauer', 'an einem Arbeitstag'], ' fertig. Wir arbeiten ',
        ['gebiet', 'in Lemgo und im Umkreis von 25 km'], ', also auch in Detmold, Lage, Bad Salzuflen und im Extertal.'
      ],
      pruefer: 'Jannes Kesting, Dachdeckermeister',
      fakten: [
        ['preis', 'Preis', 'ab 1.150 €', 'je Fenster, gleiche Größe, inkl. MwSt.', true],
        ['dauer', 'Dauer', '1 Arbeitstag', 'für 1 bis 2 Fenster, Dach nie über Nacht offen'],
        ['gebiet', 'Gebiet', '25 km um Lemgo', 'Aufmaß in diesem Umkreis kostenlos'],
        ['wer', 'Für wen', 'Eigentümer, Hausverwaltungen', 'Ein- und Zweifamilienhäuser']
      ],
      handlung: 'Dachfenster-Tausch anfragen',
      oder: 'oder anrufen',
      sprung: ['Ablauf', 'Preise', 'Beispiel', 'Fragen', 'Ansprechpartner'],
      hAblauf: 'So läuft der Tausch ab', ablaufUnter: 'Vom Foto bis zur Abnahme, meist in zwei bis drei Wochen.',
      schritte: [
        ['Anfrage mit Foto', 'Sie schicken ein Foto vom Typenschild oben im Fensterrahmen. Daran erkennen wir Größe, Baujahr und Hersteller.', '5 Minuten'],
        ['Aufmaß vor Ort', 'Wir messen innen und außen und prüfen Eindeckrahmen, Dämmung und Dampfbremse.', 'ca. 30 Minuten'],
        ['Festpreis-Angebot', 'Mit Fenstertyp, Zubehör und einem festen Termin. Kein Nachtrag, wenn das Dach hält, was das Aufmaß zeigt.', 'in 3 Werktagen'],
        ['Einbautag', 'Altes Fenster raus, neues rein, Anschluss an Deckung und Dämmung. Innen kleben wir den Raum unter dem Fenster ab.', '1 Tag'],
        ['Abnahme', 'Wir zeigen Ihnen die Bedienung, nehmen das alte Fenster mit und saugen innen nach.', '20 Minuten']
      ],
      hPreise: 'Was kostet ein neues Dachfenster?', preiseUnter: 'Festpreise nach Aufmaß. Diese ab-Preise gelten für Standardgrößen.',
      sp1: 'Leistung je Fenster',
      preise: [
        ['Tausch in gleicher Größe', 'Schwingfenster Kunststoff, Zweifachverglasung', 1150],
        ['Tausch in gleicher Größe, Holz', 'Dreifachverglasung, besserer Wärmeschutz', 1480],
        ['Größeres Fenster', 'Sparren werden ausgewechselt, Innenfutter neu', 2350],
        ['Außenrollladen mit Solarantrieb', 'zusätzlich, bei Tausch montiert', 690, true],
        ['Gerüst', 'zusätzlich, nur ab 6 m Traufhöhe', 380, true]
      ],
      preiseHinweis: ['Alle Preise inklusive 19 % Mehrwertsteuer, Stand November 2026.', 'Beispielwerte der Demo · Preisangaben vom Kunden prüfen lassen'],
      hBeispiel: 'Beispiel aus der Lemgoer Altstadt', beispielUnter: 'Fachwerkhaus, zwei Fenster von 1987, getauscht im Oktober 2026.',
      hFragen: 'Häufige Fragen zum Dachfenstertausch', fragenUnter: 'Jede Frage ist eine eigene Zwischenüberschrift. So findet man sie beim Überfliegen und in der Suche.',
      fragen: [
        ['Wird es beim Tausch drinnen schmutzig?', 'Etwas. Wir arbeiten von außen und kleben innen ab. Putzreste am Futter saugen wir am Ende ab.'],
        ['Kann ich das Fenster größer machen lassen?', 'Meist um eine Größe. Dafür wird ein Sparren ausgewechselt. Ob das bei Ihrem Dach geht, sehen wir beim Aufmaß.'],
        ['Was passiert, wenn es am Einbautag regnet?', 'Dann verschieben wir. Wir öffnen das Dach nur bei trockenem Wetter, und es bleibt nie über Nacht offen.'],
        ['Gibt es dafür eine Förderung?', 'Bei einer energetischen Sanierung manchmal. Das hängt vom Fenster und vom Programm ab. Wir sagen es Ihnen beim Aufmaß, verbindlich ist der Förderbescheid.']
      ],
      hAnsprech: 'Ihr Ansprechpartner',
      person: { name: 'Jannes Kesting', rolle: 'Dachdeckermeister, seit 2009 im Betrieb', zitat: '„Ich komme selbst zum Aufmaß. Dann wissen Sie vorher, wer bei Ihnen auf dem Dach steht.“', zeiten: 'Telefon Mo bis Fr 7 bis 16 Uhr. Außerhalb rufen wir am nächsten Werktag zurück.', tel: '05261 40 77 12' },
      hVerwandt: 'Verwandte Leistungen',
      verwandt: [['Dachrinne und Fallrohr erneuern', 'Zink oder Kupfer, ab 48 € je Meter'], ['Dach von innen dämmen', 'Zwischensparren, ohne Gerüst'], ['Sturmschaden reparieren', 'Notdienst, Abdeckung am selben Tag']],
      fuss: ['Dachwerk Begatal GmbH · Am Lüttfeld 14 · 32657 Lemgo', 'Mitglied der Handwerkskammer OWL zu Bielefeld · Seite geprüft am 02.11.2026']
    },
    praxis: {
      betrieb: { name: 'Zahnarztpraxis Lindenhaus', zusatz: 'Dr. Thale Haferkamp · Lemgo', typ: 'Dentist', ort: 'Lemgo', plz: '32657', strasse: 'Mittelstraße 61', tel: '05261 18 90 40' },
      menue: ['Behandlungen', 'Praxis', 'Team', 'Kontakt'],
      krumen: ['Start', 'Behandlungen', 'Professionelle Zahnreinigung'],
      gewerk: 'Behandlung · Vorsorge',
      leistung: 'Professionelle Zahnreinigung', ort: 'Lemgo',
      antwort: [
        'Bei der Zahnreinigung ', ['was', 'entfernen wir Beläge und Verfärbungen auch dort, wo die Zahnbürste nicht hinkommt'], ', ',
        ['wer', 'für Erwachsene und Jugendliche ab 12 Jahren'], '. Ein Termin ', ['dauer', 'dauert etwa 60 Minuten'], ' und kostet ',
        ['preis', 'ab 95 €'], ', manche Krankenkassen geben einen Zuschuss. Die Praxis liegt ',
        ['gebiet', 'in der Lemgoer Innenstadt'], ', mit Parkplätzen im Hof und einem Aufzug.'
      ],
      pruefer: 'Dr. Thale Haferkamp, Zahnärztin',
      fakten: [
        ['preis', 'Preis', 'ab 95 €', 'Selbstzahler, je nach Aufwand bis 140 €', true],
        ['dauer', 'Dauer', 'ca. 60 Minuten', 'ein Termin, keine Betäubung nötig'],
        ['gebiet', 'Ort', 'Lemgo, Mittelstraße 61', 'Parkplätze im Hof, Aufzug'],
        ['wer', 'Für wen', 'Erwachsene, Jugendliche ab 12', 'auch mit Implantaten oder Zahnspange']
      ],
      handlung: 'Termin zur Zahnreinigung buchen',
      oder: 'oder anrufen',
      sprung: ['Ablauf', 'Kosten', 'Ein Termin', 'Fragen', 'Ansprechpartnerin'],
      hAblauf: 'So läuft die Zahnreinigung ab', ablaufUnter: 'Fünf Schritte in einer Stunde, in Ruhe erklärt.',
      schritte: [
        ['Befund', 'Wir sehen uns Zähne und Zahnfleisch an und messen, wo es blutet. Das zeigt, wo wir gründlicher reinigen.', '10 Minuten'],
        ['Beläge und Zahnstein entfernen', 'Mit feinen Handinstrumenten und Ultraschall, auch zwischen den Zähnen und am Zahnfleischrand.', '20 Minuten'],
        ['Verfärbungen entfernen', 'Ein feiner Pulverstrahl löst Flecken von Kaffee, Tee oder Rauchen.', '10 Minuten'],
        ['Polieren und Fluorid', 'Glatte Flächen bieten Belägen weniger Halt. Das Fluorid härtet den Zahnschmelz.', '10 Minuten'],
        ['Tipps für zu Hause', 'Wir zeigen Ihnen Bürste und Zahnseide oder Zwischenraumbürsten in der passenden Größe.', '10 Minuten']
      ],
      hPreise: 'Was kostet die Zahnreinigung?', preiseUnter: 'Abrechnung nach der Gebührenordnung für Zahnärzte. Der Betrag hängt von der Zahl der Zähne und dem Aufwand ab.',
      sp1: 'Leistung',
      preise: [
        ['Professionelle Zahnreinigung, Erwachsene', 'ca. 60 Minuten', 95],
        ['Jugendliche von 12 bis 17 Jahren', 'ca. 45 Minuten', 70],
        ['Reinigung von Implantaten', 'zusätzlich, je Implantat', 8, true]
      ],
      preiseHinweis: ['Manche gesetzlichen Kassen geben einen Zuschuss. Die Rechnung bekommen Sie direkt von uns.', 'Beispielwerte der Demo · Preis- und Gesundheitsangaben vom Kunden prüfen lassen'],
      hBeispiel: 'Ein Termin in 60 Minuten', beispielUnter: 'So teilen wir die Stunde auf. Bilder von Behandlungsergebnissen zeigen wir bewusst nicht.',
      hFragen: 'Häufige Fragen zur Zahnreinigung', fragenUnter: 'Jede Frage ist eine eigene Zwischenüberschrift. So findet man sie beim Überfliegen und in der Suche.',
      fragen: [
        ['Zahlt meine Krankenkasse die Zahnreinigung?', 'Gesetzliche Kassen zahlen sie nicht als Regelleistung, viele geben aber einen Zuschuss. Fragen Sie Ihre Kasse nach dem Betrag.'],
        ['Tut die Zahnreinigung weh?', 'Meist nicht. An empfindlichen Stellen kann es ziehen. Sagen Sie es uns, dann machen wir eine Pause oder betäuben die Stelle.'],
        ['Wie oft sollte ich kommen?', 'Für die meisten reicht ein- bis zweimal im Jahr. Wie oft es bei Ihnen sinnvoll ist, besprechen wir nach dem Befund.'],
        ['Darf ich danach gleich essen?', 'Nach dem Fluorid bitte 30 Minuten nichts essen und trinken. Danach ist alles wie immer.']
      ],
      hAnsprech: 'Ihre Ansprechpartnerin',
      person: { name: 'Frauke Tölke', rolle: 'Dentalhygienikerin, macht seit 2014 Prophylaxe', zitat: '„Ich erkläre jeden Schritt, bevor ich ihn mache. Sie können jederzeit die Hand heben.“', zeiten: 'Telefon Mo bis Do 8 bis 18 Uhr, Fr 8 bis 13 Uhr. Online-Termine rund um die Uhr.', tel: '05261 18 90 40' },
      hVerwandt: 'Verwandte Behandlungen',
      verwandt: [['Kontrolluntersuchung', 'zweimal im Jahr, Kassenleistung'], ['Zahnfleischbehandlung', 'bei Parodontitis, in mehreren Sitzungen'], ['Zahnaufhellung', 'nach der Reinigung, Selbstzahler']],
      fuss: ['Zahnarztpraxis Lindenhaus · Mittelstraße 61 · 32657 Lemgo', 'Notdienst außerhalb der Sprechzeiten: 116 117 · Seite geprüft am 02.11.2026']
    }
  };

  // ------------------------------------------------------------------
  // Bilder: eigene SVG-Zeichnungen (im Einsatz echte Fotos)
  // ------------------------------------------------------------------
  function dachSVG(neu) {
    const id = neu ? 'n' : 'a';
    const ziegel = '#55524c', fuge = '#3f3d39', hell = '#6a6660';
    const rahmen = neu ? '#2d3033' : '#7a5236', blech = neu ? '#3a3e42' : '#8d8f8c';
    let moos = '';
    if (!neu) for (let i = 0; i < 26; i++) { const x = (i * 53) % 360, y = 30 + ((i * 37) % 230); moos += `<circle cx="${x}" cy="${y}" r="${2 + (i % 3)}" fill="#7d8a52" opacity=".55"/>`; }
    return `<svg viewBox="0 0 360 260" role="img" aria-label="${neu ? 'Nachher: neues Dachfenster mit dunklem Rahmen und Rollladenkasten' : 'Vorher: altes Holzfenster mit verwittertem Rahmen und beschlagenem Glas'}">
      <defs>
        <pattern id="z-${id}" width="40" height="52" patternUnits="userSpaceOnUse">
          <rect width="40" height="52" fill="${ziegel}"/>
          <path d="M0 25.5h40M0 51.5h40M0.5 0v26M20.5 26v26" stroke="${fuge}" stroke-width="1.5"/>
          <path d="M2 3h16M22 29h16" stroke="${hell}" stroke-width="1" opacity=".6"/>
        </pattern>
        <linearGradient id="g-${id}" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0" stop-color="${neu ? '#c9d7de' : '#a9b0ad'}"/><stop offset="1" stop-color="${neu ? '#7f97a5' : '#8b918d'}"/>
        </linearGradient>
      </defs>
      <rect width="360" height="260" fill="url(#z-${id})"/>
      ${moos}
      <rect x="112" y="${neu ? 36 : 44}" width="136" height="${neu ? 190 : 176}" fill="${blech}"/>
      ${neu ? '<rect x="118" y="40" width="124" height="18" fill="#22252a"/><rect x="124" y="45" width="40" height="8" fill="#3d5a73"/>' : ''}
      <rect x="122" y="${neu ? 62 : 54}" width="116" height="${neu ? 154 : 156}" fill="${rahmen}"/>
      <rect x="132" y="${neu ? 72 : 64}" width="96" height="${neu ? 134 : 136}" fill="url(#g-${id})"/>
      ${neu
        ? '<path d="M140 200 L200 76 L214 76 L154 200 Z" fill="#fff" opacity=".22"/><path d="M168 200 L214 104 L220 104 L174 200 Z" fill="#fff" opacity=".14"/><rect x="132" y="72" width="96" height="6" fill="#1d2023"/>'
        : '<path d="M140 190 q4 -40 2 -80 M160 196 q-3 -30 1 -64 M205 192 q5 -50 0 -96" stroke="#e6ebe8" stroke-width="2" fill="none" opacity=".45"/><rect x="132" y="186" width="96" height="14" fill="#5d6460" opacity=".5"/><path d="M122 120 l6 3 M122 150 l9 2 M236 90 l-7 4 M236 170 l-8 3" stroke="#a88b6b" stroke-width="2"/>'}
    </svg>`;
  }
  function portraitSVG(v) {
    const H = v === 'handwerk';
    const grund = H ? '#c3b4a5' : '#cfe0dc', haut = H ? '#d9a882' : '#e2b493', haar = H ? '#3b2f27' : '#5a3b2a', jacke = H ? '#2f3133' : '#e9f1ef', kragen = H ? '#fb6100' : '#005450';
    return `<svg viewBox="0 0 200 250" role="img" aria-label="Platz für das Foto von ${DATEN[v].person.name} (in der Demo gezeichnet)">
      <rect width="200" height="250" fill="${grund}"/>
      <circle cx="160" cy="44" r="58" fill="#fff" opacity=".18"/>
      <path d="M20 250 C24 196 58 176 100 176 C142 176 176 196 180 250 Z" fill="${jacke}"/>
      <path d="M84 176 L100 206 L116 176 Z" fill="${H ? '#46494c' : '#fff'}"/>
      <path d="M76 180 L100 214 L124 180" fill="none" stroke="${kragen}" stroke-width="${H ? 5 : 4}"/>
      <rect x="88" y="146" width="24" height="36" rx="10" fill="${haut}" />
      <ellipse cx="100" cy="112" rx="38" ry="46" fill="${haut}"/>
      ${H
        ? '<path d="M62 104 C62 70 82 60 102 60 C124 60 140 74 138 104 C130 88 116 82 100 82 C84 82 70 88 62 104 Z" fill="' + haar + '"/><path d="M76 136 C86 150 114 150 124 136 C122 152 112 160 100 160 C88 160 78 152 76 136 Z" fill="' + haar + '" opacity=".85"/>'
        : '<path d="M58 118 C52 74 76 58 102 58 C130 58 150 78 142 120 C140 100 132 86 116 80 C100 92 80 96 64 96 C60 104 58 110 58 118 Z" fill="' + haar + '"/><path d="M140 112 C152 130 150 160 136 176 C140 150 140 132 140 112 Z" fill="' + haar + '"/>'}
      <circle cx="86" cy="114" r="3.2" fill="#2b2420"/><circle cx="114" cy="114" r="3.2" fill="#2b2420"/>
      <path d="M90 134 Q100 141 110 134" fill="none" stroke="#8a4f3a" stroke-width="2.6" stroke-linecap="round"/>
    </svg>`;
  }
  const markeSVG = v => v === 'handwerk'
    ? '<svg viewBox="0 0 30 30" aria-hidden="true"><path d="M2 17 L15 5 L28 17" fill="none" stroke="#1a1a1a" stroke-width="3.4"/><rect x="11" y="15" width="8" height="10" fill="#fb6100"/></svg>'
    : '<svg viewBox="0 0 30 30" aria-hidden="true"><circle cx="15" cy="15" r="14" fill="#005450"/><path d="M15 7 C11 12 9 15 9 18 a6 6 0 0 0 12 0 C21 15 19 12 15 7 Z" fill="#ffc776"/></svg>';

  // ------------------------------------------------------------------
  // Aufbau
  // ------------------------------------------------------------------
  // Zahl und Einheit nie trennen
  const nb = t => String(t).replace(/ (€|Minuten|Min\.|km|Jahren)/g, '\u00a0$1').replace(/\b(ab|bis|ca\.) (\d)/g, '$1\u00a0$2');
  const el = (tag, attrs = {}, kinder = []) => {
    const e = document.createElement(tag);
    for (const [k, w] of Object.entries(attrs)) { if (k === 'text') e.textContent = nb(w); else if (k === 'html') e.innerHTML = w; else e.setAttribute(k, w); }
    [].concat(kinder).forEach(c => c && e.append(typeof c === 'string' ? nb(c) : c));
    return e;
  };
  const telHref = t => 'tel:+49' + t.replace(/\s/g, '').replace(/^0/, '');
  let v = 'handwerk';

  function aufbauen() {
    const D = DATEN[v];
    html.dataset.v = v;
    document.title = `${D.leistung} in ${D.ort} · Fundus-Demo`;
    ref('marke').innerHTML = markeSVG(v);
    ref('marke').append(el('span', {}, [el('b', { text: D.betrieb.name }), el('small', { text: D.betrieb.zusatz })]));
    ref('menue').replaceChildren(...D.menue.map((m, i) => el('li', {}, el('a', Object.assign({ href: '#', text: m }, i === 0 ? { 'aria-current': 'true' } : {})))));
    ref('kopftel').href = telHref(D.betrieb.tel);
    ref('kopftel').innerHTML = hoerer + `<span>${D.betrieb.tel}</span>`;
    ref('kopftel').setAttribute('aria-label', 'Anrufen: ' + D.betrieb.tel);

    ref('krumen').replaceChildren(...D.krumen.map((k, i) => el('li', {}, i < D.krumen.length - 1 ? el('a', { href: '#', text: k }) : el('span', { 'aria-current': 'page', text: k }))));
    ref('gewerk').textContent = D.gewerk;
    ref('h1').replaceChildren(D.leistung + ' ', el('span', { class: 'ort', text: 'in ' + D.ort }));
    ref('antwort').replaceChildren(...D.antwort.map(t => typeof t === 'string' ? t : el('span', { class: 'teil', 'data-teil': t[0], text: t[1] })));
    ref('geprueft').replaceChildren(el('span', { class: 'punkt', 'aria-hidden': 'true' }), el('span', {}, ['Zuletzt geprüft am ', el('time', { datetime: PRUEFDATUM, text: PRUEFDATUM_TEXT }), ' von ', el('b', { text: D.pruefer })]));

    ref('fakten').replaceChildren(...D.fakten.map(f => el('div', { class: 'zeile', 'data-teil': f[0] }, [el('dt', { text: f[1] }), el('dd', f[4] ? { class: 'gross' } : {}, [f[2], el('small', { text: f[3] })])])));
    ref('handlung1').innerHTML = `<span>${D.handlung}</span>` + pfeil;
    ref('oder').replaceChildren(D.oder + ': ', el('a', { href: telHref(D.betrieb.tel), text: D.betrieb.tel }));

    const ziele = ['ablauf', 'preise', 'beispiel', 'fragen', 'ansprech'];
    ref('sprung').replaceChildren(...D.sprung.map((s, i) => el('li', {}, el('a', { href: '#' + ziele[i], text: s }))));

    ref('h-ablauf').textContent = D.hAblauf; ref('ablauf-unter').textContent = D.ablaufUnter;
    ref('schritte').replaceChildren(...D.schritte.map(s => el('li', {}, [el('h3', { text: s[0] }), el('span', { class: 'zeit', text: s[2] }), el('p', { text: s[1] })])));

    ref('h-preise').textContent = D.hPreise; ref('preise-unter').textContent = D.preiseUnter;
    ref('preise-cap').textContent = 'Preise: ' + D.leistung; ref('preise-sp1').textContent = D.sp1;
    ref('preisliste').replaceChildren(...D.preise.map(p => el('tr', p[3] ? { class: 'zusatz' } : {}, [el('td', {}, [p[0], el('small', { text: p[1] })]), el('td', { text: 'ab ' + eur(p[2]) })])));
    ref('preise-hinweis').replaceChildren(el('p', { text: D.preiseHinweis[0] }), el('span', { class: 'platzhalter', text: D.preiseHinweis[1] }));

    ref('h-beispiel').textContent = D.hBeispiel; ref('beispiel-unter').textContent = D.beispielUnter;
    const B = ref('beispiel');
    if (v === 'handwerk') {
      B.innerHTML = `<div class="paar">
          <figure>${dachSVG(false)}<figcaption><b>Vorher</b><span>Holz, Baujahr 1987</span></figcaption></figure>
          <figure>${dachSVG(true)}<figcaption><b>Nachher</b><span>Kunststoff, Rollladen, Oktober 2026</span></figcaption></figure>
        </div>
        <dl class="steckbrief ziffern"><div><dt>Fenster</dt><dd>2</dd></div><div><dt>Einbau</dt><dd>1 Tag</dd></div><div><dt>Preis gesamt</dt><dd>3.020 €</dd></div><div><dt>Ort</dt><dd>Lemgo, Altstadt</dd></div></dl>`;
    } else {
      const teile = D.schritte.map(s => [s[0], parseInt(s[2], 10)]);
      B.replaceChildren(
        el('div', { class: 'stunde', role: 'list', 'aria-label': 'Aufteilung eines Termins von 60 Minuten' }, teile.map(t => { const d = el('div', { role: 'listitem' }, [el('b', { text: t[0] }), el('span', { class: 'ziffern', text: t[1] + ' Min.' })]); d.style.flex = t[1] + ' 1 0'; return d; })),
        el('div', { class: 'stunde-achse ziffern', 'aria-hidden': 'true' }, [el('span', { text: '0 Min.' }), el('span', { text: '30' }), el('span', { text: '60 Min.' })]),
        el('p', { text: 'Wer Angst vor dem Termin hat, sagt es bei der Buchung. Dann planen wir 15 Minuten mehr ein und fangen mit einem Gespräch an.' })
      );
    }

    ref('h-fragen').textContent = D.hFragen; ref('fragen-unter').textContent = D.fragenUnter;
    ref('fragen').replaceChildren(...D.fragen.map((f, i) => el('section', { 'aria-labelledby': 'frage-' + i }, [el('h3', { id: 'frage-' + i, text: f[0] }), el('p', { text: f[1] })])));

    ref('h-ansprech').textContent = D.hAnsprech;
    ref('foto').innerHTML = portraitSVG(v);
    ref('p-name').textContent = D.person.name; ref('p-rolle').textContent = D.person.rolle;
    ref('p-zitat').textContent = D.person.zitat; ref('p-zeiten').textContent = D.person.zeiten;
    ref('handlung2').innerHTML = `<span>${D.handlung}</span>` + pfeil;
    ref('p-tel').href = telHref(D.person.tel); ref('p-tel').textContent = D.person.tel;

    ref('h-verwandt').textContent = D.hVerwandt;
    ref('verwandt').replaceChildren(...D.verwandt.map(w => el('li', {}, el('a', { href: '#' }, [el('b', { text: w[0] }), el('span', { text: w[1] })]))));
    ref('fuss').replaceChildren(...D.fuss.map(f => el('span', { text: f })));

    ref('leiste').replaceChildren(
      el('a', { class: 'tel', href: telHref(D.betrieb.tel), 'aria-label': 'Anrufen: ' + D.betrieb.tel, html: hoerer + '<span>Anrufen</span>' }),
      el('a', { class: 'handlung', href: '#ansprech', html: `<span>${v === 'handwerk' ? 'Tausch anfragen' : 'Termin buchen'}</span>` + pfeil })
    );

    // Gerüst-Etiketten
    document.querySelectorAll('.g-tag').forEach(t => t.remove());
    document.querySelectorAll('[data-g]').forEach(n => n.prepend(el('span', { class: 'g-tag', 'aria-hidden': 'true', text: n.dataset.g })));

    // JSON-LD aus demselben Datensatz
    const ld = jsonLD(D);
    $('#ld').textContent = JSON.stringify(ld);
    ref('ld-text').innerHTML = JSON.stringify(ld, null, 2).replace(/[&<>]/g, z => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[z])).replace(/"(@?[\wäöü]+)":/g, '<span class="k">"$1"</span>:');

    ref('werte').textContent = v === 'handwerk'
      ? 'Kurve cubic-bezier(0.4, 0, 0.2, 1) · UI 150 ms · Markierung 240 ms · Wechsel 280 ms · Special Gothic + Atkinson Next'
      : 'Kurve cubic-bezier(0.2, 0, 0.38, 0.9) · UI 150 ms · Markierung 240 ms · Wechsel 280 ms · Hedvig Letters Serif + Google Sans Flex';
    verknuepfen();
  }

  function jsonLD(D) {
    const basis = 'https://www.beispiel.de/';
    const pfad = ['', v === 'handwerk' ? 'leistungen/' : 'behandlungen/', v === 'handwerk' ? 'leistungen/dachfenster-tauschen/' : 'behandlungen/zahnreinigung/'];
    const min = D.preise[0][2];
    return {
      '@context': 'https://schema.org',
      '@graph': [
        { '@type': 'BreadcrumbList', itemListElement: D.krumen.map((k, i) => ({ '@type': 'ListItem', position: i + 1, name: k, item: basis + pfad[i] })) },
        {
          '@type': 'Service', name: `${D.leistung} in ${D.ort}`, description: D.antwort.map(t => typeof t === 'string' ? t : t[1]).join(''),
          provider: { '@type': D.betrieb.typ, name: D.betrieb.name, telephone: '+49 ' + D.betrieb.tel.replace(/^0/, ''), address: { '@type': 'PostalAddress', streetAddress: D.betrieb.strasse, postalCode: D.betrieb.plz, addressLocality: D.betrieb.ort, addressCountry: 'DE' } },
          areaServed: v === 'handwerk' ? ['Lemgo', 'Detmold', 'Lage', 'Bad Salzuflen', 'Extertal'] : 'Lemgo',
          offers: { '@type': 'Offer', priceCurrency: 'EUR', priceSpecification: { '@type': 'PriceSpecification', minPrice: min, priceCurrency: 'EUR', valueAddedTaxIncluded: true } },
          dateModified: PRUEFDATUM
        }
      ]
    };
  }

  // Fakt und Satzteil zeigen aufeinander
  function markiere(teil, an) {
    document.querySelectorAll(`[data-teil="${teil}"]`).forEach(n => n.classList.toggle('an', an));
  }
  function verknuepfen() {
    ref('fakten').querySelectorAll('.zeile').forEach(z => {
      z.addEventListener('pointerenter', () => { if (!laeuft) markiere(z.dataset.teil, true); });
      z.addEventListener('pointerleave', () => { if (!laeuft) markiere(z.dataset.teil, false); });
    });
    ref('antwort').querySelectorAll('.teil').forEach(s => {
      s.addEventListener('pointerenter', () => { if (!laeuft) markiere(s.dataset.teil, true); });
      s.addEventListener('pointerleave', () => { if (!laeuft) markiere(s.dataset.teil, false); });
    });
  }

  // ------------------------------------------------------------------
  // Pult: Branche (Token), Gerüst, JSON-LD
  // ------------------------------------------------------------------
  const knoepfe = [...document.querySelectorAll('.pult [data-v]')];
  function variante(neu, url = true) {
    v = DATEN[neu] ? neu : 'handwerk';
    knoepfe.forEach(b => b.setAttribute('aria-pressed', String(b.dataset.v === v)));
    aufbauen();
    if (url) { const q = new URLSearchParams(location.search); q.set('v', v); history.replaceState(null, '', location.pathname + '?' + q.toString() + location.hash); }
  }
  knoepfe.forEach(b => b.addEventListener('click', () => { const lief = laeuft; pause(); variante(b.dataset.v); if (lief) vorfuehren(); }));
  const gKnopf = $('[data-geruest-knopf]');
  function geruest(an) { html.toggleAttribute('data-geruest', an); gKnopf.setAttribute('aria-pressed', String(an)); gKnopf.firstElementChild.textContent = an ? 'Gerüst ausblenden' : 'Gerüst zeigen'; }
  gKnopf.addEventListener('click', () => geruest(!html.hasAttribute('data-geruest')));
  const dlg = $('dialog.ld');
  $('[data-ld-knopf]').addEventListener('click', () => dlg.showModal());
  $('[data-ld-zu]').addEventListener('click', () => dlg.close());
  dlg.addEventListener('click', e => { if (e.target === dlg) dlg.close(); });

  // Sprunglinks: weich nur ohne reduzierte Bewegung
  document.addEventListener('click', e => {
    const a = e.target.closest('a[href^="#"]');
    if (!a) return;
    const ziel = a.getAttribute('href').length > 1 && document.getElementById(a.getAttribute('href').slice(1));
    e.preventDefault();
    if (!ziel) return;
    rolle(ziel.getBoundingClientRect().top + scrollY - 8, 700);
    const h = ziel.querySelector('h2'); if (h) { h.tabIndex = -1; h.focus({ preventScroll: true }); }
  });

  // ------------------------------------------------------------------
  // Vorführung: Antwort zerlegt sich in ihre fünf Teile, dann das Gerüst
  // ------------------------------------------------------------------
  const KURVE = t => { // cubic-bezier(0.4, 0, 0.2, 1) angenähert
    const x1 = .4, x2 = .2; let a = t;
    for (let i = 0; i < 6; i++) { const x = 3 * (1 - a) * (1 - a) * a * x1 + 3 * (1 - a) * a * a * x2 + a * a * a - t; const d = 3 * (1 - a) * (1 - a) * x1 + 6 * (1 - a) * a * (x2 - x1) + 3 * a * a * (1 - x2); if (Math.abs(d) < 1e-6) break; a -= x / d; }
    return 3 * (1 - a) * a * a + a * a * a;
  };
  let rollId = 0;
  function rolle(y, ms) {
    const s = document.scrollingElement, von = s.scrollTop, bis = Math.max(0, Math.min(y, s.scrollHeight - innerHeight)), id = ++rollId;
    if (reduziert() || ms <= 0) { s.scrollTop = bis; return; }
    const t0 = performance.now();
    const schritt = t => { if (id !== rollId) return; const p = Math.min(1, (t - t0) / ms); s.scrollTop = von + (bis - von) * KURVE(p); if (p < 1) requestAnimationFrame(schritt); };
    requestAnimationFrame(schritt);
  }
  const zeiger = $('[data-zeiger]');
  let uhr = [], laeuft = false;
  const nach = (ms, f) => uhr.push(setTimeout(f, ms));
  function zeigeAuf(n, dx = .5, dy = .5) { if (!n) return; const r = n.getBoundingClientRect(); zeiger.style.translate = `${r.left + r.width * dx}px ${r.top + r.height * dy}px`; zeiger.classList.add('an'); }
  const zuBlock = id => { const b = document.getElementById(id); return b ? b.getBoundingClientRect().top + scrollY - 10 : 0; };

  function runde() {
    uhr.forEach(clearTimeout); uhr = [];
    geruest(false); document.querySelectorAll('.an').forEach(n => n.classList.remove('an'));
    rolle(0, 0);
    let t = 500;
    ['was', 'wer', 'preis', 'dauer', 'gebiet'].forEach(teil => {
      nach(t, () => { const s = ref('antwort').querySelector(`[data-teil="${teil}"]`); zeigeAuf(s, .55, .7); markiere(teil, true); });
      t += 950;
    });
    nach(t, () => zeigeAuf(ref('handlung1'), .7, .5)); t += 1200;
    nach(t, () => { zeiger.classList.remove('an'); geruest(true); }); t += 2000;
    ['ablauf', 'preise', 'beispiel', 'fragen', 'ansprech'].forEach(id => { nach(t, () => rolle(zuBlock(id), 900)); t += 2100; });
    nach(t, () => rolle(0, 1100)); t += 1600;
    nach(t, runde);
  }
  function vorfuehren() { pause(); html.classList.add('vorfuehrung'); laeuft = true; runde(); }
  function pause() { uhr.forEach(clearTimeout); uhr = []; rollId++; laeuft = false; }
  function weiter() { if (!laeuft && html.classList.contains('vorfuehrung')) { laeuft = true; runde(); } }
  function neustart() { pause(); html.classList.remove('vorfuehrung'); zeiger.classList.remove('an'); geruest(false); document.querySelectorAll('.an').forEach(n => n.classList.remove('an')); rolle(0, 0); }

  variante(new URLSearchParams(location.search).get('v') || 'handwerk', false);

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