Zum Inhalt
← Sektionen & Funktionen

Danke-Seite: was jetzt passiert

Nach dem Absenden: Satz mit dem, was angekommen ist, Vorgangsnummer als Schlüsselanhänger, Zeitleiste mit Fristen in Werktagen, Kalender-Datei, Zusammenfassung mit „Ändern“ und Rückfrage-Kontakt, ohne Tracking-Zwang.

Werkstatt, Praxis, Salon, Beratung: Termin steht, Kalender-Datei, was mitbringen, umbuchen.

Die Seite nach dem Absenden eines Formulars. Oben steht in einem Satz, was angekommen ist, mit Vorgangsnummer. Darunter folgt eine Zeitleiste: was als Nächstes passiert, wer es tut und bis wann. Eine Zusammenfassung zeigt die gesendeten Angaben, damit Besucher Fehler sofort sehen und korrigieren können. Bei einem festen Termin gibt es eine Kalender-Datei (.ics) zum Speichern. Ein Rückfrage-Kontakt mit Name und Telefon steht direkt daneben. Die Seite funktioniert ohne Tracking-Zwang: kein Consent-Banner als Hürde, Messung höchstens nach Einwilligung.

Nach dem Klick auf „Senden“ ist die Unsicherheit am größten: Ist es angekommen, wann meldet sich jemand, muss ich noch etwas tun? Design-Systeme der öffentlichen Hand verlangen deshalb auf der Bestätigungsseite genau diese Teile: Vorgangsnummer, was als Nächstes passiert und wann, Kontakt und eine Möglichkeit, die Bestätigung zu speichern. Eine generische „Vielen Dank für Ihre Nachricht“-Seite beantwortet keine dieser Fragen und erzeugt Rückrufe.

Wann einsetzen

Nach jedem Formular einer Kundenseite: Anfrage, Terminbuchung, Rückrufwunsch, Anmeldung, Bewerbung, Newsletter mit Double-Opt-In. Als eigene Adresse (z. B. /anfrage/danke), damit Besucher sie als Beleg speichern können und die Website den Abschluss zählen kann, wenn eingewilligt wurde.

Wann nicht

Nicht als Toast oder kleine Meldung im Formular, wenn danach noch etwas zu tun ist. Keine Werbung, keine Gutscheine oder Newsletter-Abfragen vor den nächsten Schritten. Keine Zeitzusagen, die der Betrieb nicht einhält („Antwort in 2 Stunden“). Keine Konfetti-Effekte bei ernsten Anliegen (Praxis, Pflege, Kanzlei, Bestattung). Persönliche Daten nicht in die URL schreiben.

Varianten

  • Fester Termin: Datum, Uhrzeit, Ort, Kalender-Datei, Vorbereitung und Umbuchen
  • Offene Anfrage: Antwort bis zu einem konkreten Werktag, Ablauf bis zum Angebot
  • E-Mail bestätigen (Double-Opt-In): Hinweis auf das Postfach, erneut senden mit Wartezeit, Adresse korrigieren
  • Direkt aufgerufen ohne Absenden: neutraler Zustand mit Weg zum Formular statt leerer Zusammenfassung

Umsetzung

Eigene Seite mit status 200 und noindex. Die Formulardaten kommen serverseitig aus der Sitzung bzw. über eine kurzlebige Vorgangs-ID, nie als Klartext in der URL. Aufbau: h1 sagt, was angekommen ist („Ihr Termin steht“, „Anfrage ist da“). Darunter die Vorgangsnummer mit Kopier-Knopf und der Hinweis auf die Bestätigungs-E-Mail. Die nächsten Schritte als geordnete Liste (ol) mit time-Elementen; der aktuelle Schritt trägt aria-current=step. Fristen aus Werktagen gerechnet (Wochenende und Feiertage überspringen), Ausgabe mit Intl.DateTimeFormat de-DE. Zusammenfassung als dl mit „Ändern“-Link je Gruppe zurück ins Formular. Kalender-Datei: VCALENDAR mit VEVENT, DTSTART/DTEND mit TZID=Europe/Berlin, UID aus der Vorgangsnummer, LOCATION, DESCRIPTION, Zeilenenden CRLF, Text escapen; als Blob mit type text/calendar und download-Name. Drucken bzw. als PDF speichern über window.print mit eigenem Druck-CSS. Fokus beim Laden auf die h1 (tabindex=-1), Zustandswechsel in aria-live=polite. Wird die Seite ohne Vorgang aufgerufen, zeigt sie einen neutralen Zustand mit Weg zum Formular. Messung nur nach Einwilligung, ohne Pixel im Kopf.

Robust umsetzen

Telefon. Eine Spalte: Bestätigung, nächste Schritte, Kalender-Knopf in voller Breite, Zusammenfassung, Kontakt. Telefon als tel:-Link mit großem Ziel. Die Zeitleiste steht senkrecht.

Ohne Bewegung. Zeitleiste steht sofort, Zustände wechseln ohne Einblenden.

Barrierefrei. Fokus nach dem Laden auf die h1, damit Screenreader die Bestätigung zuerst hören. Schritte als ol mit aria-current=step, Daten als time mit datetime. Kopier- und Sende-Rückmeldungen in aria-live=polite. Farbe nie allein als Status: jeder Schritt hat ein Wort (Erledigt, Als Nächstes, Später). Kalender-Knopf sagt, was er lädt („Termin als Kalender-Datei (.ics)“).

Code

<!DOCTYPE html>
<!--
  Danke-Seite: was jetzt passiert
  Fundus · Sektionen & Funktionen · https://cecodesign.de/lab/fundus/sektionen/danke-seite-naechste-schritte/
  Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de" data-v="termin">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
<title>Danke-Seite: was jetzt passiert · 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: 'Mona Sans'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/mona-sans:vf@latest/latin-wdth-normal.woff2') format('woff2-variations'); font-weight: 200 900; font-stretch: 75% 125%; 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; }

/* Rezept kfz-werkstatt-autohaus: Betongrau, Asphalt, Verkehrsgelb nur für die Handlung */
:root {
  --grund: #f2f1ed; --flaeche: #e7e5df; --karte: #fbfaf8; --text: #121314; --leise: #595e63;
  --linie: rgba(89, 94, 99, .25); --linie-stark: #232629;
  --asphalt: #232629; --asphalt-2: #2d3135; --asphalt-text: #f2f1ed; --asphalt-leise: #b9bec4; --asphalt-linie: rgba(242, 241, 237, .16);
  --gelb: #f5b700; --gelb-tief: #c99600; --gruen: #1f7a3e; --gruen-hell: #7fd39b; --rot: #b3261e;
  --auf: cubic-bezier(0.16, 1, 0.3, 1); --zu: cubic-bezier(0.7, 0, 0.84, 0);
  --hover: 120ms; --oeffnen: 250ms; --schliessen: 170ms; --linie-dauer: 600ms; --versatz: 90ms;
  --r-klein: 4px; --r-gross: 8px;
  --h: 'Mona Sans', 'Arial Narrow', sans-serif; --t: 'Google Sans Flex', system-ui, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--grund); color: var(--text); font-family: var(--t); -webkit-font-smoothing: antialiased; }
:root:has(:focus-visible) { scroll-behavior: auto; }
body { font-size: 17px; line-height: 1.55; min-height: 100svh; }
a { color: inherit; text-underline-offset: 3px; text-decoration-thickness: 1px; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
:focus-visible { outline: 2px solid var(--text); outline-offset: 3px; }
.buehne :focus-visible, .fuss :focus-visible { outline-color: var(--gelb); }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.vh:focus { position: fixed; left: 12px; top: 12px; width: auto; height: auto; clip-path: none; white-space: normal; z-index: 100; padding: 10px 14px; background: #fff; color: #111; outline: 3px solid #111; }
.ziffern { font-variant-numeric: tabular-nums lining-nums; }
.label { font: 600 13px/1.2 var(--t); letter-spacing: .06em; text-transform: uppercase; color: var(--leise); }
h1, h2, h3 { font-family: var(--h); font-stretch: 80%; letter-spacing: -.02em; line-height: .98; text-wrap: balance; }
p { text-wrap: pretty; }
.huelle { max-width: 1200px; margin: 0 auto; padding: 0 clamp(18px, 3.4vw, 44px); }

/* Kopf */
.kopf { background: var(--asphalt); color: var(--asphalt-text); border-bottom: 1px solid var(--asphalt-linie); }
.kopf .huelle { display: flex; align-items: center; gap: 24px; min-height: 64px; }
.marke { display: flex; align-items: center; gap: 11px; text-decoration: none; margin-right: auto; }
.marke svg { width: 30px; height: 30px; flex: none; }
.marke b { font: 800 19px/1 var(--h); font-stretch: 78%; letter-spacing: -.01em; text-transform: uppercase; display: block; }
.marke small { display: block; font-size: 12.5px; color: var(--asphalt-leise); margin-top: 3px; }
.kopf nav ul { display: flex; gap: 22px; list-style: none; }
.kopf nav a { text-decoration: none; font-size: 15px; color: var(--asphalt-leise); padding: 6px 0; border-bottom: 2px solid transparent; transition: color var(--hover) var(--auf), border-color var(--hover) var(--auf); }
.kopf nav a:hover { color: var(--asphalt-text); border-color: var(--asphalt-text); }
.offen { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--asphalt-leise); white-space: nowrap; }
.offen i { width: 8px; height: 8px; border-radius: 50%; background: var(--gruen-hell); flex: none; }
.offen.zu i { background: #e6a19c; }
.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(--asphalt-linie); border-radius: var(--r-klein); white-space: nowrap; transition: background-color var(--hover) var(--auf), border-color var(--hover) var(--auf); }
.tel:hover { background: var(--asphalt-2); border-color: var(--asphalt-leise); }
.tel svg { width: 16px; height: 16px; }

/* Schrittspur: Formular abgeschlossen */
.spur { background: var(--asphalt); color: var(--asphalt-leise); }
.spur ol { display: flex; gap: 6px; list-style: none; padding: 14px 0 0; font-size: 13.5px; flex-wrap: wrap; }
.spur li { display: flex; align-items: center; gap: 7px; padding: 5px 10px 5px 7px; border-radius: var(--r-klein); background: var(--asphalt-2); }
.spur li svg { width: 14px; height: 14px; color: var(--gruen-hell); }
.spur li[aria-current] { background: var(--gelb); color: var(--text); font-weight: 700; }
.spur li[aria-current] svg { color: var(--text); }

/* Bühne */
.buehne { background: var(--asphalt); color: var(--asphalt-text); padding: clamp(26px, 4vw, 52px) 0 clamp(34px, 5vw, 64px); overflow: hidden; }
.buehne-raster { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 64px); align-items: start; }
.buehne .label { color: var(--asphalt-leise); display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.buehne .label .chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px; border-radius: var(--r-klein); background: rgba(127, 211, 155, .14); color: var(--gruen-hell); letter-spacing: .04em; }
.buehne .label .chip.warten { background: rgba(245, 183, 0, .14); color: var(--gelb); }
.buehne .label .chip svg { width: 13px; height: 13px; }
h1 { font-size: clamp(2.6rem, 1.5rem + 4.2vw, 4.6rem); font-weight: 800; margin-top: 14px; }
h1:focus, h1:focus-visible { outline: none; } /* Überschrift bekommt den Fokus nur zum Vorlesen, sie ist kein Bedienelement */
.lead { font-size: clamp(18px, 1rem + .4vw, 21px); line-height: 1.5; margin-top: 18px; max-width: 34em; color: var(--asphalt-text); }
.lead strong { font-weight: 700; }
.lead .leise { color: var(--asphalt-leise); }
.aktionen { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; align-items: center; }

/* Tiefen-Taste (Rezept: nur die Haupthandlung) */
.taste { --tief: 4px; position: relative; display: inline-flex; align-items: center; gap: 10px; min-height: 52px; padding: 0 20px; border-radius: var(--r-klein); background: var(--gelb); color: var(--text); font-weight: 700; font-size: 16px; text-decoration: none; box-shadow: 0 var(--tief) 0 var(--gelb-tief); transform: translateY(0); transition: transform var(--hover) var(--auf), box-shadow var(--hover) var(--auf); margin-bottom: var(--tief); }
.taste:hover { transform: translateY(-1px); box-shadow: 0 calc(var(--tief) + 1px) 0 var(--gelb-tief); }
.taste:active, .taste.druck { transform: translateY(var(--tief)); box-shadow: 0 0 0 var(--gelb-tief); }
.taste svg { width: 19px; height: 19px; flex: none; }
.zweit { display: inline-flex; align-items: center; gap: 9px; min-height: 48px; padding: 0 16px; border-radius: var(--r-klein); border: 1.5px solid var(--asphalt-linie); color: var(--asphalt-text); font-weight: 600; font-size: 15.5px; text-decoration: none; transition: border-color var(--hover) var(--auf), background-color var(--hover) var(--auf); margin-bottom: 4px; }
.zweit:hover { border-color: var(--asphalt-leise); background: var(--asphalt-2); }
.zweit svg { width: 17px; height: 17px; flex: none; }
.ics { margin-top: 16px; max-width: min(560px, 100%); }
.buehne-raster > * { min-width: 0; }
.ics summary { list-style: none; cursor: pointer; display: inline-flex; gap: 8px; align-items: center; font-size: 14px; color: var(--asphalt-leise); padding: 4px 0; }
.ics summary::-webkit-details-marker { display: none; }
.ics summary::before { content: ''; width: 7px; height: 7px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); transition: transform var(--schliessen) var(--zu); }
.ics[open] summary::before { transform: rotate(45deg); transition: transform var(--oeffnen) var(--auf); }
.ics summary:hover { color: var(--asphalt-text); }
.ics pre { margin-top: 10px; padding: 14px 16px; background: #181a1c; border: 1px solid var(--asphalt-linie); border-radius: var(--r-gross); font: 400 12px/1.6 'Geist Mono', ui-monospace, monospace; color: #d7dade; white-space: pre-wrap; overflow-wrap: anywhere; max-width: 100%; }
.ics[open] pre { animation: auf var(--oeffnen) var(--auf); }
@keyframes auf { from { opacity: 0; transform: translateY(-6px); } }
.ics pre b { color: var(--gelb); font-weight: 500; }

/* Schlüsselanhänger: Vorgangsnummer zum Mitnehmen */
.anhaenger { justify-self: center; position: relative; width: min(300px, 100%); padding-top: 34px; }
.anhaenger .ring { position: absolute; left: 50%; top: 0; width: 62px; height: 62px; margin-left: -31px; border: 5px solid #8d9399; border-radius: 50%; border-bottom-color: #6b7177; }
.tag { position: relative; transform-origin: 50% 8px; background: var(--gelb); color: var(--text); border-radius: 26px 26px var(--r-gross) var(--r-gross); padding: 46px 22px 22px; box-shadow: 0 24px 40px -18px rgba(0, 0, 0, .55); }
.tag::before { content: ''; position: absolute; left: 50%; top: 14px; width: 20px; height: 20px; margin-left: -10px; border-radius: 50%; background: var(--asphalt); box-shadow: inset 0 2px 3px rgba(0,0,0,.5); }
.tag.schwingt { animation: schwingen 1400ms var(--auf) both; }
@keyframes schwingen { 0% { transform: rotate(-9deg); } 35% { transform: rotate(5deg); } 65% { transform: rotate(-2deg); } 100% { transform: rotate(0); } }
.tag .label { color: rgba(18, 19, 20, .78); }
.tag .nummer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; margin-top: 6px; }
.tag .nummer strong { font: 800 clamp(30px, 2.6vw, 36px)/1 var(--h); font-stretch: 75%; letter-spacing: .01em; white-space: nowrap; }
.kopieren { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; padding: 7px 10px; border: 1.5px solid var(--text); border-radius: var(--r-klein); white-space: nowrap; transition: background-color var(--hover) var(--auf), color var(--hover) var(--auf); }
.kopieren:hover, .kopieren.druck { background: var(--text); color: var(--gelb); }
.kopieren svg { width: 14px; height: 14px; }
.tag dl { margin-top: 18px; display: grid; gap: 0; border-top: 2px solid var(--text); }
.tag dl div { display: grid; grid-template-columns: 76px 1fr; gap: 10px; padding: 9px 0; border-bottom: 1px solid rgba(18, 19, 20, .22); font-size: 15px; }
.tag dt { font-weight: 600; font-size: 12.5px; letter-spacing: .05em; text-transform: uppercase; padding-top: 2px; }
.tag dd { font-weight: 600; line-height: 1.3; }
.tag dd small { display: block; font-weight: 400; font-size: 13.5px; }
.tag .perfo { margin-top: 16px; font-size: 12.5px; border-top: 2px dashed rgba(18, 19, 20, .5); padding-top: 10px; }

/* Brief (Double-Opt-In) */
.brief { justify-self: center; width: min(330px, 100%); position: relative; padding-top: 18px; }
.brief .umschlag { position: relative; background: var(--karte); color: var(--text); border-radius: var(--r-gross); padding: 22px 22px 20px; box-shadow: 0 24px 40px -18px rgba(0, 0, 0, .55); }
.brief .umschlag::before { content: ''; position: absolute; inset: 0 0 auto 0; height: 10px; border-radius: var(--r-gross) var(--r-gross) 0 0; background: repeating-linear-gradient(-45deg, var(--rot) 0 10px, var(--karte) 10px 20px, #2b4a7a 20px 30px, var(--karte) 30px 40px); }
.brief .absender { font-size: 13px; color: var(--leise); margin-top: 8px; }
.brief .betreff { font: 800 22px/1.1 var(--h); font-stretch: 80%; margin-top: 6px; }
.brief .knopf-attrappe { display: inline-flex; margin-top: 14px; padding: 10px 14px; background: var(--gelb); border-radius: var(--r-klein); font-weight: 700; font-size: 14.5px; box-shadow: 0 3px 0 var(--gelb-tief); }
.brief .frist { display: flex; justify-content: space-between; gap: 8px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--linie); font-size: 13.5px; color: var(--leise); }
.brief .frist b { color: var(--text); }
.brief.schwingt .umschlag { animation: einwurf 900ms var(--auf) both; }
@keyframes einwurf { from { transform: translateY(-14px) rotate(-3deg); opacity: 0; } }

/* Hauptteil */
.haupt { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: clamp(24px, 4vw, 64px); padding-top: clamp(36px, 5vw, 64px); padding-bottom: clamp(48px, 6vw, 88px); }
.haupt h2 { font-size: clamp(1.75rem, 1.25rem + 2vw, 2.6rem); font-weight: 750; font-stretch: 85%; }
.kopfzeile { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 22px; }
.kopfzeile p { color: var(--leise); font-size: 15px; }

/* Zeitleiste */
.zeitleiste { list-style: none; position: relative; display: grid; }
.zeitleiste::before, .zeitleiste::after { z-index: 0; }
.zeitleiste::before { content: ''; position: absolute; left: 15px; top: 14px; bottom: 26px; width: 2px; background: var(--linie); }
.zeitleiste::after { content: ''; position: absolute; left: 15px; top: 14px; width: 2px; background: var(--text); height: var(--fortschritt, 0px); transform-origin: top; }
.zeitleiste.zeichnet::after { animation: zeichnen var(--linie-dauer) var(--auf) both; }
@keyframes zeichnen { from { transform: scaleY(0); } }
.schritt { position: relative; z-index: 1; display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: 0 18px; padding-bottom: 26px; }
.zeitleiste.zeichnet .schritt { animation: rein 400ms var(--auf) both; animation-delay: calc(var(--i) * var(--versatz)); }
@keyframes rein { from { opacity: 0; transform: translateY(8px); } }
.schritt .punkt { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; background: var(--grund); border: 2px solid var(--linie-stark); position: relative; z-index: 1; font: 800 14px/1 var(--h); }
.schritt .punkt svg { width: 16px; height: 16px; }
.schritt[data-status="erledigt"] .punkt { background: var(--text); color: var(--grund); }
.schritt[data-status="jetzt"] .punkt { background: var(--gelb); border-color: var(--text); box-shadow: 0 0 0 5px rgba(245, 183, 0, .25); }
.schritt[data-status="spaeter"] .punkt { border-color: rgba(89, 94, 99, .55); color: var(--leise); }
.schritt .status { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--leise); }
.schritt[data-status="jetzt"] .status { color: var(--text); }
.schritt[data-status="jetzt"] .status::before { content: ''; width: 18px; height: 2px; background: var(--text); }
.schritt h3 { font-size: clamp(1.25rem, 1.05rem + .7vw, 1.55rem); font-weight: 750; font-stretch: 85%; letter-spacing: -.01em; line-height: 1.1; margin-top: 5px; }
.schritt time, .schritt .wann { display: inline-block; font-weight: 700; }
.schritt .zeile { display: flex; gap: 6px 16px; flex-wrap: wrap; margin-top: 6px; font-size: 15px; color: var(--leise); }
.schritt .zeile b { color: var(--text); font-weight: 700; }
.schritt p { margin-top: 6px; max-width: 58ch; }
.schritt:last-child { padding-bottom: 0; }

/* Seitenleiste */
.seite { display: grid; gap: 18px; align-content: start; }
.karte { background: var(--karte); border: 1px solid var(--linie); border-radius: var(--r-gross); padding: 20px 22px; }
.person { display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: 14px; align-items: center; }
.portraet { width: 64px; height: 64px; border-radius: var(--r-gross); background: repeating-linear-gradient(135deg, var(--flaeche) 0 6px, #dcd9d1 6px 12px); display: grid; place-items: center; font: 800 20px/1 var(--h); font-stretch: 80%; color: var(--leise); position: relative; }
.portraet::after { content: 'Foto'; position: absolute; bottom: 4px; font: 500 9px/1 'Geist Mono', monospace; letter-spacing: .1em; text-transform: uppercase; color: var(--leise); }
.person b { display: block; font-size: 17px; }
.person span { font-size: 14.5px; color: var(--leise); }
.karte h2.klein, .karte h3 { font-size: 1.2rem; font-weight: 750; font-stretch: 85%; letter-spacing: -.01em; margin-bottom: 12px; line-height: 1.15; }
.karte .wege { display: grid; gap: 10px; margin-top: 16px; }
.weg { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border: 1.5px solid var(--linie-stark); border-radius: var(--r-klein); text-decoration: none; font-weight: 700; transition: background-color var(--hover) var(--auf), color var(--hover) var(--auf); }
.weg:hover { background: var(--asphalt); color: var(--asphalt-text); }
.weg svg { width: 18px; height: 18px; flex: none; }
.weg small { display: block; font-weight: 400; font-size: 13.5px; color: var(--leise); }
.weg:hover small { color: var(--asphalt-leise); }
.karte .hinweis { margin-top: 12px; font-size: 14px; color: var(--leise); }
.liste { list-style: none; display: grid; gap: 10px; }
.liste li { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 10px; font-size: 15.5px; line-height: 1.4; }
.liste li::before { content: ''; width: 14px; height: 14px; margin-top: 3px; border: 2px solid var(--text); border-radius: 3px; }
.liste li small { display: block; color: var(--leise); font-size: 14px; }
.liste.pfeile li::before { border: 0; border-radius: 0; width: 14px; height: 2px; margin-top: 11px; background: var(--text); }
.leer-weg .liste { gap: 16px; } .leer-weg .liste li { font-size: 17px; }
.anhaenger.leer .tag { background: transparent; border: 2px dashed var(--asphalt-leise); box-shadow: none; color: var(--asphalt-text); }
.anhaenger.leer .tag::before { background: var(--asphalt-leise); box-shadow: none; }
.anhaenger.leer .tag .label, .anhaenger.leer .tag p { color: var(--asphalt-leise); }

/* Zusammenfassung */
.angaben { grid-column: 1 / -1; border-top: 2px solid var(--text); padding-top: 26px; }
.gruppen { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); gap: 0 32px; }
.gruppe { padding: 16px 0 18px; border-bottom: 1px solid var(--linie); }
.gruppe header { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.gruppe h3 { font-size: 1.1rem; font-weight: 750; font-stretch: 85%; letter-spacing: -.01em; }
.gruppe header a { font-size: 14px; font-weight: 700; }
.gruppe dl { display: grid; gap: 4px; }
.gruppe dl div { display: grid; grid-template-columns: minmax(0, 9em) minmax(0, 1fr); gap: 10px; font-size: 15.5px; }
.gruppe dt { color: var(--leise); }
.gruppe dd { font-weight: 600; overflow-wrap: anywhere; }
.fotos { display: flex; gap: 8px; margin-top: 6px; }
.fotos svg { width: 72px; height: 54px; border-radius: var(--r-klein); }
.fuss-hinweise { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 14px 36px; font-size: 14.5px; color: var(--leise); }
.fuss-hinweise p { display: flex; gap: 10px; align-items: flex-start; max-width: 46ch; }
.fuss-hinweise svg { width: 17px; height: 17px; flex: none; margin-top: 2px; color: var(--text); }

/* Erneut senden (Double-Opt-In) */
.erneut { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 14px; }
.erneut button[disabled] { opacity: .62; cursor: not-allowed; }
.adresse-form { display: none; gap: 8px; margin-top: 12px; flex-wrap: wrap; align-items: end; }
.adresse-form.offen { display: flex; }
.adresse-form label { display: grid; gap: 4px; font-size: 14px; flex: 1 1 220px; }
.adresse-form input { font: inherit; font-size: 16px; padding: 10px 12px; border: 1.5px solid var(--linie-stark); border-radius: var(--r-klein); background: #fff; color: var(--text); min-width: 0; }
.adresse-form .fehler { color: var(--rot); font-size: 14px; flex-basis: 100%; }
.klein-knopf { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px; border: 1.5px solid var(--linie-stark); border-radius: var(--r-klein); font-weight: 700; font-size: 15px; transition: background-color var(--hover) var(--auf), color var(--hover) var(--auf); }
.klein-knopf:hover:not([disabled]) { background: var(--asphalt); color: var(--asphalt-text); }

/* Ohne Vorgang aufgerufen */
.leer-weg { display: none; }
:root[data-leer] .leer-weg { display: grid; }
:root[data-leer] .nur-vorgang { display: none !important; }

/* Fuß */
.fuss { background: var(--asphalt); color: var(--asphalt-leise); font-size: 14.5px; padding: 36px 0 44px; }
.fuss .huelle { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: 20px 36px; }
.fuss b { color: var(--asphalt-text); display: block; margin-bottom: 6px; }
.fuss a { color: var(--asphalt-text); }
.fuss ul { list-style: none; display: grid; gap: 4px; }

/* Rückmeldung */
.meldung { position: fixed; left: 50%; bottom: 22px; transform: translate(-50%, 12px); opacity: 0; background: var(--text); color: var(--grund); padding: 11px 16px; border-radius: var(--r-klein); font-weight: 600; font-size: 15px; pointer-events: none; transition: opacity var(--schliessen) var(--zu), transform var(--schliessen) var(--zu); z-index: 30; max-width: calc(100vw - 32px); }
.meldung.an { opacity: 1; transform: translate(-50%, 0); transition: opacity var(--oeffnen) var(--auf), transform var(--oeffnen) var(--auf); }

/* Leiste unten (Telefon) */
.leiste { display: none; }

/* Pult */
.pult { position: fixed; right: 14px; bottom: 14px; z-index: 40; width: 262px; padding: 12px; display: grid; gap: 9px; background: rgba(18, 19, 20, .94); color: #f2f1ed; border-radius: var(--r-gross); box-shadow: 0 12px 34px rgba(0, 0, 0, .24); }
.pult .mono { font: 500 10.5px/1.4 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; color: rgba(242, 241, 237, .72); }
.pult .reihe { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border: 1px solid rgba(242, 241, 237, .24); border-radius: var(--r-klein); overflow: hidden; }
.pult button { padding: 8px 6px; font: 500 11.5px/1.2 'Geist Mono', monospace; color: rgba(242, 241, 237, .84); transition: background-color var(--hover) var(--auf), color var(--hover) var(--auf); }
.pult .reihe button + button { border-left: 1px solid rgba(242, 241, 237, .24); }
.pult button[aria-pressed="true"] { background: #f2f1ed; color: #121314; }
.pult .einzeln { border: 1px solid rgba(242, 241, 237, .24); border-radius: var(--r-klein); text-align: left; padding: 8px 10px; display: flex; justify-content: space-between; gap: 8px; }
.pult :focus-visible { outline: 2px solid var(--gelb); outline-offset: 2px; }
.pult .werte { font: 400 10.5px/1.5 'Geist Mono', monospace; color: rgba(242, 241, 237, .72); }

@media (max-width: 1040px) { .kopf nav { display: none; } }
@media (max-width: 860px) {
  .buehne-raster, .haupt { grid-template-columns: minmax(0, 1fr); }
  .seite { position: static; }
  .anhaenger { justify-self: start; width: min(290px, 100%); }
  .brief { justify-self: start; }
}
@media (max-width: 640px) {
  .offen { display: none; }
  .tel span { display: none; }
  .spur ol { font-size: 12.5px; }
  .spur li:not([aria-current]) span { display: none; }
  .aktionen { flex-direction: column; align-items: stretch; }
  .taste, .zweit { justify-content: center; }
  .anhaenger { justify-self: center; }
  .gruppe dl div { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .leiste { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; position: fixed; left: 0; right: 0; bottom: 0; z-index: 25; padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); background: var(--asphalt); border-top: 1px solid var(--asphalt-linie); }
  .pult { bottom: calc(82px + env(safe-area-inset-bottom)); } /* über der Leiste, sonst verdeckt „Steuerung +“ den Hauptknopf */
  .leiste a, .leiste button { display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 48px; border-radius: var(--r-klein); font-weight: 700; font-size: 15.5px; text-decoration: none; }
  .leiste a { color: var(--asphalt-text); border: 1.5px solid var(--asphalt-linie); }
  .leiste button { background: var(--gelb); color: var(--text); }
  .leiste svg { width: 18px; height: 18px; }
  .fuss { padding-bottom: 100px; }
  .meldung { bottom: 86px; }
}
@media (hover: none) { .taste:hover { transform: none; box-shadow: 0 var(--tief) 0 var(--gelb-tief); } }

/* Vorschau: nur die Bühne */
.vorschau body { overflow: hidden; }
.vorschau .leiste, .vorschau .meldung { display: none; }

/* Druck: Beleg ohne Navigation */
@media print {
  .kopf nav, .tel, .offen, .spur, .aktionen, .ics, .leiste, .pult, .meldung, .kopieren, .seite .wege, .fuss-hinweise, .gruppe header a { display: none !important; }
  html, .buehne, .kopf, .fuss { background: #fff !important; color: #000 !important; }
  .buehne .label, .lead .leise, .fuss, .kopf .marke small { color: #333 !important; }
  .tag { box-shadow: none; border: 2px solid #000; background: #fff; }
  .buehne-raster, .haupt { grid-template-columns: 1fr 1fr; }
  .zeitleiste .schritt { break-inside: avoid; animation: none !important; }
  a[href^="tel:"]::after { content: ' (' attr(href) ')'; font-weight: 400; }
}

.reduziert *, .reduziert *::before, .reduziert *::after { animation: none !important; transition-duration: .01ms !important; }
@media (forced-colors: active) {
  button, [role=button], [role=tab], [role=switch], input, select, textarea, .taste, .zweit, .weg { border: 1px solid ButtonText !important; }
  .schritt .punkt, .zeitleiste::after { forced-color-adjust: none; }
}
</style>
</head>
<body>
<a class="vh" href="#titel">Zur Bestätigung springen</a>
<header class="kopf">
  <div class="huelle">
    <a class="marke" href="#" aria-label="Werkstatt Ohlhof, Startseite">
      <svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 2.5 27.7 9.25v13.5L16 29.5 4.3 22.75V9.25z" fill="#f5b700"/><circle cx="16" cy="16" r="5.6" fill="#232629"/><path d="M16 7.2 23.6 11.6" stroke="#232629" stroke-width="1.6"/></svg>
      <span><b>Werkstatt Ohlhof</b><small>Kfz-Meisterbetrieb · Northeim</small></span>
    </a>
    <nav aria-label="Hauptmenü"><ul><li><a href="#">Leistungen</a></li><li><a href="#">Festpreise</a></li><li><a href="#">Reifenhotel</a></li><li><a href="#">Kontakt</a></li></ul></nav>
    <span class="offen" id="kopf-status"><i></i><span>Geöffnet bis 18 Uhr</span></span>
    <a class="tel" href="tel:+4955519473010"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2"/></svg><span class="ziffern">05551 94730-10</span></a>
  </div>
</header>

<div class="spur nur-vorgang" id="spur-huelle"><div class="huelle"><ol id="spur" aria-label="Fortschritt"></ol></div></div>

<section class="buehne" aria-labelledby="titel">
  <div class="huelle buehne-raster">
    <div>
      <p class="label" id="kennung"></p>
      <h1 id="titel" tabindex="-1"></h1>
      <p class="lead" id="lead"></p>
      <div class="aktionen" id="aktionen"></div>
      <div id="zusatz"></div>
    </div>
    <div id="figur"></div>
  </div>
</section>

<main class="huelle haupt" id="haupt">
  <section class="nur-vorgang" aria-labelledby="weiter-titel">
    <div class="kopfzeile"><h2 id="weiter-titel">So geht es weiter</h2><p id="weiter-stand"></p></div>
    <ol class="zeitleiste" id="zeitleiste"></ol>
  </section>

  <section class="leer-weg" aria-labelledby="leer-titel" style="gap:14px">
    <h2 id="leer-titel">Was Sie hier tun können</h2>
    <ul class="liste pfeile">
      <li><span><a href="#"><b>Einen Termin buchen</b></a><small>In vier kurzen Schritten, mit Festpreis vorab.</small></span></li>
      <li><span><b>Ihre Bestätigung wiederfinden</b><small>Sie steckt in der E-Mail mit dem Betreff „Ihr Termin bei Werkstatt Ohlhof“, zusammen mit der Vorgangsnummer.</small></span></li>
      <li><span><a href="tel:+4955519473010"><b>Anrufen</b></a><small>Wir finden Ihren Termin auch über Name und Kennzeichen.</small></span></li>
    </ul>
  </section>

  <aside class="seite" aria-label="Rückfragen">
    <div class="karte">
      <h2 class="klein" id="frage-titel">Frage dazu?</h2>
      <div class="person">
        <div class="portraet" aria-hidden="true">SO</div>
        <div><b>Sabine Ohlhof</b><span>Serviceannahme, kennt Ihren Vorgang</span></div>
      </div>
      <div class="wege">
        <a class="weg" href="tel:+4955519473010"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2"/></svg><span><span class="ziffern">05551 94730-10</span><small>Mo bis Fr 7:30 bis 18 Uhr, Sa 8 bis 12 Uhr</small></span></a>
        <a class="weg" href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg><span>Antwort auf die E-Mail<small>landet direkt bei der Annahme</small></span></a>
      </div>
      <p class="hinweis nur-vorgang" id="frage-hinweis"></p>
    </div>
    <div class="karte nur-vorgang" id="mitbringen-karte">
      <h2 class="klein" id="mitbringen-titel"></h2>
      <ul class="liste" id="mitbringen"></ul>
    </div>
  </aside>

  <section class="angaben nur-vorgang" aria-labelledby="angaben-titel">
    <div class="kopfzeile"><h2 id="angaben-titel">Ihre Angaben</h2><p>Etwas falsch? Über „Ändern“ kommen Sie zurück ins Formular, nichts geht verloren.</p></div>
    <div class="gruppen" id="angaben"></div>
  </section>

  <div class="fuss-hinweise">
    <p><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg><span>Ihre Angaben zeigen wir nur in dieser Sitzung. Auf dieser Seite messen wir nichts ohne Ihre Einwilligung.</span></p>
    <p class="nur-vorgang"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M4 5h16v11H8l-4 4z"/></svg><span>Wie war die Buchung? <a href="#">Zwei Fragen, freiwillig</a>.</span></p>
  </div>
</main>

<footer class="fuss">
  <div class="huelle">
    <div><b>Werkstatt Ohlhof</b>Am Rhumewehr 14<br>37154 Northeim</div>
    <div><b>Annahme</b><span class="ziffern">Mo bis Fr 7:30 bis 18 Uhr<br>Sa 8 bis 12 Uhr</span></div>
    <div><b>Kontakt</b><a href="tel:+4955519473010" class="ziffern">05551 94730-10</a><br><a href="#">service@werkstatt-ohlhof.de</a></div>
    <ul><li><a href="#">Impressum</a></li><li><a href="#">Datenschutz</a></li><li><a href="#">Barrierefreiheit</a></li></ul>
  </div>
</footer>

<div class="leiste" id="leiste">
  <a href="tel:+4955519473010"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2"/></svg>Anrufen</a>
  <button type="button" id="leiste-handlung"></button>
</div>

<div class="meldung" id="meldung" role="status" aria-live="polite"></div>

<div class="pult" aria-label="Steuerung der Demo" role="group">
  <span class="mono">Fall</span>
  <div class="reihe" id="pult-v">
    <button type="button" data-v="termin" aria-pressed="true">Termin</button>
    <button type="button" data-v="anfrage" aria-pressed="false">Anfrage</button>
    <button type="button" data-v="bestaetigen" aria-pressed="false">E-Mail</button>
  </div>
  <button type="button" class="einzeln" id="pult-leer" aria-pressed="false"><span>Ohne Vorgang aufgerufen</span><span aria-hidden="true">○</span></button>
  <p class="werte" id="pult-werte"></p>
</div>

<script>
(() => {
  const h = document.documentElement;
  const reduziert = () => h.classList.contains('reduziert');
  const $ = (s, w = document) => w.querySelector(s);
  const TZ = 'Europe/Berlin';

  /* ---------- Datum: Werktage in Niedersachsen ---------- */
  function ostern(j) { const a = j % 19, b = Math.floor(j / 100), c = j % 100, d = Math.floor(b / 4), e = b % 4, f = Math.floor((b + 8) / 25), g = Math.floor((b - f + 1) / 3), hh = (19 * a + b - d - g + 15) % 30, i = Math.floor(c / 4), k = c % 4, l = (32 + 2 * e + 2 * i - hh - k) % 7, m = Math.floor((a + 11 * hh + 22 * l) / 451), mo = Math.floor((hh + l - 7 * m + 114) / 31), t = ((hh + l - 7 * m + 114) % 31) + 1; return new Date(Date.UTC(j, mo - 1, t)); }
  function feiertage(j) {
    const o = ostern(j), plus = n => new Date(o.getTime() + n * 864e5).toISOString().slice(0, 10);
    return new Set([`${j}-01-01`, plus(-2), plus(1), `${j}-05-01`, plus(39), plus(50), `${j}-10-03`, `${j}-10-31`, `${j}-12-25`, `${j}-12-26`]);
  }
  function teile(d) { const p = Object.fromEntries(new Intl.DateTimeFormat('en-CA', { timeZone: TZ, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hourCycle: 'h23', weekday: 'short' }).formatToParts(d).map(x => [x.type, x.value])); return { j: +p.year, m: +p.month, t: +p.day, std: +p.hour, min: +p.minute, wt: p.weekday }; }
  function tagIso(t) { return `${t.j}-${String(t.m).padStart(2, '0')}-${String(t.t).padStart(2, '0')}`; }
  function werktag(iso) { const d = new Date(iso + 'T12:00:00Z'), w = d.getUTCDay(); return w !== 0 && w !== 6 && !feiertage(d.getUTCFullYear()).has(iso); }
  function plusWerktage(iso, n) { let d = new Date(iso + 'T12:00:00Z'); while (n > 0) { d = new Date(d.getTime() + 864e5); if (werktag(d.toISOString().slice(0, 10))) n--; } return d.toISOString().slice(0, 10); }
  // Ortszeit Berlin → Date (UTC)
  function berlin(iso, std, min) { const [j, m, t] = iso.split('-').map(Number); let g = Date.UTC(j, m - 1, t, std, min); const p = teile(new Date(g)); const diff = Date.UTC(p.j, p.m - 1, p.t, p.std, p.min) - g; return new Date(g - diff); }
  const fTag = new Intl.DateTimeFormat('de-DE', { timeZone: TZ, weekday: 'long', day: 'numeric', month: 'long' });
  const fKurz = new Intl.DateTimeFormat('de-DE', { timeZone: TZ, weekday: 'short', day: 'numeric', month: 'short' });
  const fZeit = new Intl.DateTimeFormat('de-DE', { timeZone: TZ, hour: 'numeric', minute: '2-digit' });
  const zeit = d => fZeit.format(d).replace(':00', '') + ' Uhr';
  const kurz = d => fKurz.format(d).replace(/\.(?=,)/, '');
  const iso8601 = d => d.toISOString();

  /* ---------- Vorgang (kommt im Einsatz aus der Sitzung) ---------- */
  const jetzt = new Date();
  const heute = tagIso(teile(jetzt));
  const eingang = new Date(Math.floor(jetzt.getTime() / 60000) * 60000 - 60000);
  const terminTag = plusWerktage(heute, 6);
  const T = {
    abgabe: berlin(terminTag, 7, 30), abgabeEnde: berlin(terminTag, 8, 0), anruf: berlin(terminTag, 14, 0), abholung: berlin(terminTag, 18, 0),
    teile: berlin(plusWerktage(heute, 1), 12, 0),
    sichtung: berlin(plusWerktage(heute, 1), 16, 0), rueckruf: berlin(plusWerktage(heute, 2), 12, 0),
    link: new Date(jetzt.getTime() + 48 * 3600e3)
  };
  const nr = 'OH-' + tagIso(teile(jetzt)).slice(2).replace(/-/g, '').slice(0, 4) + '-0473';
  let mail = 'k.brandes@beispiel.de';
  const maske = m => m.replace(/^(.)[^@]*(@.*)$/, (_, a, b) => a + '•••' + b);

  const ICON = {
    kal: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4M12 13v5M9.5 15.5h5"/></svg>',
    druck: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M7 9V3h10v6M7 17H4v-7h16v7h-3"/><rect x="7" y="14" width="10" height="7"/></svg>',
    pfeil: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>',
    kopie: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h3"/></svg>',
    haken: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg>',
    uhr: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><circle cx="12" cy="12" r="8.5"/><path d="M12 7.5V12l3 2"/></svg>',
    post: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg>'
  };
  const tm = (d, txt) => `<time datetime="${iso8601(d)}">${txt}</time>`;
  const SPUR = ['Leistung', 'Fahrzeug', 'Termin', 'Kontakt'];

  const V = {
    termin: {
      spur: SPUR,
      kennung: () => `<span class="chip">${ICON.haken}Gebucht</span><span>Vorgang ${nr}</span>`,
      titel: 'Ihr Termin steht.',
      lead: () => `<strong>${fTag.format(T.abgabe)}</strong>, Abgabe ${zeit(T.abgabe)} bis ${zeit(T.abgabeEnde)}. <span class="leise">Die Bestätigung ist unterwegs an ${maske(mail)}. Falls sie nicht ankommt, schauen Sie bitte im Spam-Ordner nach.</span>`,
      aktionen: () => `<a class="taste" href="#" id="ics-knopf">${ICON.kal}Termin in den Kalender (.ics)</a><button type="button" class="zweit" data-drucken>${ICON.druck}Beleg drucken</button><a class="zweit" href="#">${ICON.pfeil}Verschieben</a>`,
      ics: true,
      figur: () => `<div class="anhaenger" aria-label="Schlüsselanhänger mit Ihrer Vorgangsnummer" role="group"><span class="ring" aria-hidden="true"></span><div class="tag"><p class="label">Schlüsselanhänger</p><div class="nummer"><strong class="ziffern">${nr}</strong><button type="button" class="kopieren" data-kopieren>${ICON.kopie}<span>Kopieren</span></button></div><dl><div><dt>Abgabe</dt><dd>${kurz(T.abgabe)}<small>${zeit(T.abgabe)} bis ${zeit(T.abgabeEnde)}</small></dd></div><div><dt>Leistung</dt><dd>Inspektion<small>und Räderwechsel</small></dd></div><div><dt>Fahrzeug</dt><dd class="ziffern">NOM-K 417</dd></div></dl><p class="perfo">Diese Nummer nennen, dann geht es schneller.</p></div></div>`,
      weiterStand: () => `Stand ${zeit(jetzt)}`,
      schritte: () => [
        { s: 'erledigt', titel: 'Buchung ist angekommen', zeile: `${tm(eingang, 'heute, ' + zeit(eingang))}<span>automatisch</span>`, text: `Wir haben den Termin für Sie reserviert. Die Bestätigung mit allen Angaben kommt per E-Mail.` },
        { s: 'jetzt', titel: 'Wir prüfen die Teile', zeile: `<span>bis <b>${tm(T.teile, kurz(T.teile) + ', ' + zeit(T.teile))}</b></span><span>Lager, Ohlhof</span>`, text: `Fehlt ein Ersatzteil für Ihr Fahrzeug, rufen wir an. Hören Sie nichts von uns, ist alles da.` },
        { s: 'spaeter', titel: 'Sie bringen das Auto', zeile: `<span><b>${tm(T.abgabe, kurz(T.abgabe))}</b>, ${zeit(T.abgabe)} bis ${zeit(T.abgabeEnde)}</span><span>Annahme</span>`, text: `Früher da? Der Schlüsselkasten am Tor nimmt den Schlüssel ab 6 Uhr an. Den Anhänger mit der Vorgangsnummer finden Sie dort.` },
        { s: 'spaeter', titel: 'Wir rufen vor jeder Mehrarbeit an', zeile: `<span>gegen <b>${zeit(T.anruf)}</b></span><span>Meister Jens Ohlhof</span>`, text: `Findet sich bei der Inspektion mehr, hören Sie Preis und Grund vorher. Ohne Ihr Ja machen wir nichts zusätzlich.` },
        { s: 'spaeter', titel: 'Abholen und bezahlen', zeile: `<span>bis <b>${zeit(T.abholung)}</b></span><span>bar, Karte oder Rechnung</span>`, text: `Wir schicken eine SMS, sobald das Auto fertig ist.` }
      ],
      mitbringenTitel: 'Bitte mitbringen',
      mitbringen: ['Fahrzeugschein<small>Teil I, für die Inspektion nach Herstellervorgabe</small>', 'Serviceheft, falls vorhanden', 'Schlüssel fürs Felgenschloss<small>sonst können wir die Räder nicht wechseln</small>'],
      frageHinweis: () => `Halten Sie die Vorgangsnummer <b class="ziffern">${nr}</b> bereit.`,
      angaben: () => [
        { t: 'Leistung', d: [['Auftrag', 'Inspektion nach Herstellervorgabe'], ['Zusatz', 'Räderwechsel, Reifen aus dem Reifenhotel'], ['Festpreis', '<span class="ziffern">ab 189 €</span> inkl. MwSt. (Beispielwert)']] },
        { t: 'Fahrzeug', d: [['Art', 'Kombi, Benzin, Baujahr 2017'], ['Kennzeichen', '<span class="ziffern">NOM-K 417</span>'], ['Kilometer', '<span class="ziffern">ca. 86.000</span>']] },
        { t: 'Termin', d: [['Abgabe', `${fTag.format(T.abgabe)}, ${zeit(T.abgabe)}`], ['Abholung', `am selben Tag bis ${zeit(T.abholung)}`], ['Ersatzwagen', 'nicht gewünscht']] },
        { t: 'Kontakt', d: [['Name', 'Katrin Brandes'], ['Telefon', '<span class="ziffern">0151 •••• 2280</span>'], ['E-Mail', maske(mail)]] }
      ],
      leiste: 'Kalender'
    },
    anfrage: {
      spur: ['Schaden', 'Fotos', 'Kontakt'],
      kennung: () => `<span class="chip warten">${ICON.uhr}Wird gesichtet</span><span>Vorgang ${nr}</span>`,
      titel: 'Ihre Anfrage ist da.',
      lead: () => `Wir melden uns <strong>bis ${fTag.format(T.rueckruf)}, ${zeit(T.rueckruf)}</strong> mit einem ersten Kostenrahmen. <span class="leise">Eine Kopie Ihrer Anfrage ging an ${maske(mail)}.</span>`,
      aktionen: () => `<a class="taste" href="#">${ICON.pfeil}Fotos nachreichen</a><button type="button" class="zweit" data-drucken>${ICON.druck}Beleg drucken</button>`,
      ics: false,
      figur: () => `<div class="anhaenger" aria-label="Ihre Vorgangsnummer" role="group"><span class="ring" aria-hidden="true"></span><div class="tag"><p class="label">Schadenakte</p><div class="nummer"><strong class="ziffern">${nr}</strong><button type="button" class="kopieren" data-kopieren>${ICON.kopie}<span>Kopieren</span></button></div><dl><div><dt>Schaden</dt><dd>Delle und Kratzer<small>Tür hinten links</small></dd></div><div><dt>Fotos</dt><dd>3 angekommen</dd></div><div><dt>Antwort</dt><dd>bis ${kurz(T.rueckruf)}<small>${zeit(T.rueckruf)}</small></dd></div></dl><p class="perfo">Diese Nummer nennen, dann geht es schneller.</p></div></div>`,
      weiterStand: () => `Stand ${zeit(jetzt)}`,
      schritte: () => [
        { s: 'erledigt', titel: 'Anfrage und Fotos sind da', zeile: `${tm(eingang, 'heute, ' + zeit(eingang))}<span>3 Fotos, 4,2 MB</span>`, text: 'Die Fotos sind scharf genug für eine erste Einschätzung.' },
        { s: 'jetzt', titel: 'Der Meister sieht sich die Fotos an', zeile: `<span>bis <b>${tm(T.sichtung, kurz(T.sichtung) + ', ' + zeit(T.sichtung))}</b></span><span>Jens Ohlhof</span>`, text: 'Er prüft, ob Ausbeulen ohne Lackieren reicht oder ob die Tür lackiert werden muss.' },
        { s: 'spaeter', titel: 'Rückruf mit Kostenrahmen', zeile: `<span>bis <b>${tm(T.rueckruf, kurz(T.rueckruf) + ', ' + zeit(T.rueckruf))}</b></span><span>Telefon, wie gewünscht</span>`, text: 'Sie hören eine Spanne und wovon sie abhängt. Verbindlich wird der Preis erst nach dem Blick aufs Auto.' },
        { s: 'spaeter', titel: 'Besichtigung, 15 Minuten', zeile: '<span>nach Absprache</span><span>ohne Kosten</span>', text: 'Danach bekommen Sie den Kostenvoranschlag schriftlich, auch für die Versicherung.' }
      ],
      mitbringenTitel: 'Das hilft uns noch',
      mitbringen: ['Ein Foto aus etwa zwei Metern Abstand<small>zeigt, wie groß die Fläche ist</small>', 'Schadennummer der Versicherung<small>falls schon vorhanden</small>'],
      frageHinweis: () => `Halten Sie die Vorgangsnummer <b class="ziffern">${nr}</b> bereit.`,
      angaben: () => [
        { t: 'Schaden', d: [['Stelle', 'Tür hinten links'], ['Art', 'Delle mit Kratzer, etwa handgroß'], ['Fotos', '<span class="fotos">' + [0, 1, 2].map(i => `<svg viewBox="0 0 72 54" role="img" aria-label="Foto ${i + 1} von 3"><rect width="72" height="54" fill="${['#8b9198', '#7d838a', '#959ba1'][i]}"/><path d="M0 ${30 + i * 4}C20 ${26 + i * 3} 45 ${36 - i * 2} 72 ${28 + i}" stroke="#c9ced3" stroke-width="2" fill="none"/><ellipse cx="${30 + i * 6}" cy="${33 - i}" rx="${9 + i * 2}" ry="5" fill="#5f656b" opacity=".6"/></svg>`).join('') + '</span>']] },
        { t: 'Fahrzeug', d: [['Art', 'Kombi, Baujahr 2017'], ['Kennzeichen', '<span class="ziffern">NOM-K 417</span>'], ['Versicherung', 'noch nicht gemeldet']] },
        { t: 'Kontakt', d: [['Name', 'Katrin Brandes'], ['Rückruf', 'werktags nach 16 Uhr'], ['E-Mail', maske(mail)]] }
      ],
      leiste: 'Fotos'
    },
    bestaetigen: {
      spur: null,
      kennung: () => `<span class="chip warten">${ICON.post}Noch ein Schritt</span><span>Reifen-Erinnerung</span>`,
      titel: 'Bitte bestätigen Sie Ihre E-Mail.',
      lead: () => `Wir haben einen Link an <strong id="mail-ziel">${maske(mail)}</strong> geschickt. <span class="leise">Erst nach dem Klick darauf erinnern wir Sie an den Reifenwechsel. Der Link gilt 48 Stunden.</span>`,
      aktionen: () => `<button type="button" class="taste" id="erneut-knopf">${ICON.post}<span>Link erneut senden</span></button><button type="button" class="zweit" id="adresse-knopf" aria-expanded="false" aria-controls="adresse-form">${ICON.pfeil}Adresse ändern</button>`,
      zusatz: () => `<form class="adresse-form" id="adresse-form" novalidate><label>Neue E-Mail-Adresse<input type="email" id="adresse-feld" autocomplete="email" value="${mail}"></label><button type="submit" class="zweit">Speichern und senden</button><p class="fehler" id="adresse-fehler" hidden></p></form>`,
      ics: false,
      figur: () => `<div class="brief" aria-hidden="true"><div class="umschlag"><p class="absender">Von: Werkstatt Ohlhof</p><p class="betreff">Bitte bestätigen: Reifen-Erinnerung</p><span class="knopf-attrappe">Ja, erinnern Sie mich</span><p class="frist"><span>Link gültig bis</span><b>${kurz(T.link)}, ${zeit(T.link)}</b></p></div></div>`,
      weiterStand: () => '',
      schritte: () => [
        { s: 'erledigt', titel: 'Anmeldung ist angekommen', zeile: `${tm(eingang, 'heute, ' + zeit(eingang))}`, text: 'Gespeichert ist nur Ihre Adresse und der Wunsch nach Erinnerung, noch nicht aktiv.' },
        { s: 'jetzt', titel: 'Link in der E-Mail anklicken', zeile: `<span>bis <b>${tm(T.link, kurz(T.link) + ', ' + zeit(T.link))}</b></span><span>Betreff „Bitte bestätigen“</span>`, text: 'Keine E-Mail da? Nach wenigen Minuten im Spam-Ordner nachsehen. Ohne Klick löschen wir die Adresse nach 48 Stunden.' },
        { s: 'spaeter', titel: 'Erinnerung zum Saisonwechsel', zeile: `<span><b>${(teile(jetzt).m >= 3 && teile(jetzt).m < 9) ? 'Anfang Oktober' : 'Anfang März'}</b></span><span>eine E-Mail, mehr nicht</span>`, text: 'Mit einem Link zum Termin. Abmelden geht in jeder E-Mail mit einem Klick.' }
      ],
      mitbringenTitel: null,
      frageHinweis: () => 'Bei Fragen zur Erinnerung reicht eine Antwort auf die E-Mail.',
      angaben: () => [
        { t: 'Erinnerung', d: [['Wofür', 'Reifenwechsel Sommer und Winter'], ['Wie oft', 'zweimal im Jahr'], ['Kanal', 'E-Mail']] },
        { t: 'Kontakt', d: [['E-Mail', `<span id="mail-angabe">${maske(mail)}</span>`], ['Fahrzeug', 'freiwillig, nicht angegeben']] }
      ],
      leiste: 'Erneut senden'
    }
  };

  let v = new URLSearchParams(location.search).get('v');
  if (!V[v]) v = 'termin';
  let leer = false, sperre = 0, sperrTimer = null, meldTimer = null;

  function melden(text) {
    const m = $('#meldung'); m.textContent = text; m.classList.add('an');
    clearTimeout(meldTimer); meldTimer = setTimeout(() => m.classList.remove('an'), 2200);
  }

  function icsText() {
    const z = d => d.toISOString().replace(/[-:]/g, '').replace(/\.\d+/, '');
    const esc = s => s.replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\n/g, '\\n');
    return ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Werkstatt Ohlhof//Termin//DE', 'CALSCALE:GREGORIAN', 'METHOD:PUBLISH', 'BEGIN:VEVENT',
      `UID:${nr}@werkstatt-ohlhof.de`, `DTSTAMP:${z(jetzt)}`, `DTSTART:${z(T.abgabe)}`, `DTEND:${z(T.abgabeEnde)}`,
      'SUMMARY:' + esc('Werkstatt Ohlhof: Auto abgeben (Inspektion, Räderwechsel)'),
      'LOCATION:' + esc('Werkstatt Ohlhof, Am Rhumewehr 14, 37154 Northeim'),
      'DESCRIPTION:' + esc(`Vorgang ${nr}\nBitte mitbringen: Fahrzeugschein, Serviceheft, Felgenschloss\nRückfragen: 05551 94730-10`),
      'BEGIN:VALARM', 'ACTION:DISPLAY', 'DESCRIPTION:' + esc('Morgen Auto zur Werkstatt Ohlhof bringen'), 'TRIGGER:-PT14H', 'END:VALARM',
      'END:VEVENT', 'END:VCALENDAR'].join('\r\n') + '\r\n';
  }

  function zeichne(anim) {
    const d = V[v];
    h.dataset.v = v;
    // Schrittspur
    const spur = $('#spur');
    $('#spur-huelle').hidden = !d.spur;
    if (d.spur) spur.innerHTML = d.spur.map(s => `<li>${ICON.haken}<span>${s}</span></li>`).join('') + `<li aria-current="step">${ICON.haken}<span>Abgeschickt</span></li>`;
    if (leer) {
      $('#kennung').innerHTML = '<span>Bestätigung</span>';
      $('#titel').textContent = 'Hier erscheint Ihre Bestätigung.';
      $('#lead').innerHTML = 'Diese Seite zeigt nach dem Absenden, was als Nächstes passiert. Sie haben sie ohne Vorgang geöffnet, etwa über ein Lesezeichen. <span class="leise">Ihre Daten liegen hier nicht offen, sie stehen in Ihrer Bestätigungs-E-Mail.</span>';
      $('#aktionen').innerHTML = `<a class="taste" href="#">${ICON.kal}Termin buchen</a><a class="zweit" href="tel:+4955519473010">Anrufen</a>`;
      $('#zusatz').innerHTML = ''; $('#figur').innerHTML = `<div class="anhaenger leer" aria-hidden="true"><span class="ring"></span><div class="tag"><p class="label">Vorgangsnummer</p><div class="nummer"><strong class="ziffern">OH-····-····</strong></div><p class="perfo" style="border-top-color:currentColor">Steht in Ihrer Bestätigungs-E-Mail.</p></div></div>`;
    } else {
      $('#kennung').innerHTML = d.kennung();
      $('#titel').textContent = d.titel;
      $('#lead').innerHTML = d.lead();
      $('#aktionen').innerHTML = d.aktionen();
      $('#zusatz').innerHTML = (d.ics ? `<details class="ics" id="ics-vorschau"><summary>Was steht in der Kalender-Datei?</summary><pre></pre></details>` : '') + (d.zusatz ? d.zusatz() : '');
      if (d.ics) $('#ics-vorschau pre').innerHTML = icsText().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/^([A-Z-]+)(:|;)/gm, '<b>$1</b>$2');
      $('#figur').innerHTML = d.figur();
    }
    $('#weiter-stand').textContent = d.weiterStand();
    const zl = $('#zeitleiste');
    zl.innerHTML = d.schritte().map((s, i) => `<li class="schritt" data-status="${s.s}" style="--i:${i}"${s.s === 'jetzt' ? ' aria-current="step"' : ''}><span class="punkt" aria-hidden="true">${s.s === 'erledigt' ? ICON.haken : i + 1}</span><div><span class="status">${{ erledigt: 'Erledigt', jetzt: 'Als Nächstes', spaeter: 'Danach' }[s.s]}</span><h3>${s.titel}</h3><div class="zeile">${s.zeile}</div><p>${s.text}</p></div></li>`).join('');
    // Fortschrittslinie bis zum aktuellen Schritt
    requestAnimationFrame(() => {
      const akt = zl.querySelector('[data-status="jetzt"] .punkt');
      if (akt) zl.style.setProperty('--fortschritt', (akt.getBoundingClientRect().top - zl.getBoundingClientRect().top - 14 + 16) + 'px');
    });
    $('#mitbringen-karte').hidden = !d.mitbringenTitel;
    if (d.mitbringenTitel) { $('#mitbringen-titel').textContent = d.mitbringenTitel; $('#mitbringen').innerHTML = d.mitbringen.map(x => `<li><span>${x}</span></li>`).join(''); }
    $('#frage-hinweis').innerHTML = d.frageHinweis();
    $('#angaben').innerHTML = d.angaben().map(g => `<section class="gruppe" aria-label="${g.t}"><header><h3>${g.t}</h3><a href="#" aria-label="${g.t} ändern">Ändern</a></header><dl>${g.d.map(([a, b]) => `<div><dt>${a}</dt><dd>${b}</dd></div>`).join('')}</dl></section>`).join('');
    $('#leiste-handlung').textContent = leer ? 'Termin buchen' : d.leiste;
    $('#pult-werte').textContent = { termin: 'Fester Termin · .ics mit UTC-Zeiten, VALARM −14 h · Fristen in Werktagen (NI)', anfrage: 'Offene Anfrage · Antwort bis +2 Werktage 12 Uhr · keine Kalender-Datei', bestaetigen: 'Double-Opt-In · Link 48 h · erneut senden mit 30 s Sperre' }[v] + ' · Kurve auf (0.16,1,0.3,1) 250 ms, zu (0.7,0,0.84,0) 170 ms';
    // Bewegung: Linie, Schritte, Anhänger
    zl.classList.remove('zeichnet');
    const fig = $('.tag') || $('.brief');
    if (anim && !reduziert()) { void zl.offsetWidth; zl.classList.add('zeichnet'); if (fig) { fig.classList.remove('schwingt'); void fig.offsetWidth; fig.classList.add('schwingt'); } }
    binden();
  }

  function kopieren(btn) {
    const fertig = () => { const s = btn.querySelector('span'); s.textContent = 'Kopiert'; melden(`Vorgangsnummer ${nr} kopiert.`); setTimeout(() => { s.textContent = 'Kopieren'; }, 1800); };
    if (navigator.clipboard && window.isSecureContext) navigator.clipboard.writeText(nr).then(fertig, fertig); else fertig();
  }
  function icsLaden(echt) {
    if (echt) {
      const url = URL.createObjectURL(new Blob([icsText()], { type: 'text/calendar;charset=utf-8' }));
      const a = document.createElement('a'); a.href = url; a.download = `werkstatt-ohlhof-${nr}.ics`; document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    }
    melden('Kalender-Datei erstellt. Öffnen Sie sie, um den Termin zu speichern.');
  }
  function erneutSenden(btn) {
    if (sperre > 0) return;
    melden(`Neuer Link an ${maske(mail)} gesendet.`);
    sperre = 30; btn.disabled = true;
    const s = btn.querySelector('span');
    const tick = () => { if (sperre <= 0) { btn.disabled = false; s.textContent = 'Link erneut senden'; return; } s.textContent = `Erneut senden in ${sperre} s`; sperre--; sperrTimer = setTimeout(tick, 1000); };
    tick();
  }

  function binden() {
    document.querySelectorAll('[data-kopieren]').forEach(b => b.onclick = () => kopieren(b));
    document.querySelectorAll('[data-drucken]').forEach(b => b.onclick = () => window.print());
    const ics = $('#ics-knopf'); if (ics) ics.onclick = e => { e.preventDefault(); icsLaden(true); };
    const er = $('#erneut-knopf');
    if (er) { er.onclick = () => erneutSenden(er); if (sperre > 0) { er.disabled = true; } }
    const ak = $('#adresse-knopf'), af = $('#adresse-form');
    if (ak && af) {
      ak.onclick = () => { const auf = !af.classList.contains('offen'); af.classList.toggle('offen', auf); ak.setAttribute('aria-expanded', auf); if (auf) $('#adresse-feld').focus(); };
      af.onsubmit = e => {
        e.preventDefault();
        const f = $('#adresse-feld'), fe = $('#adresse-fehler'), wert = f.value.trim();
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(wert)) { fe.hidden = false; fe.textContent = 'Bitte eine vollständige E-Mail-Adresse eingeben, zum Beispiel name@beispiel.de.'; f.setAttribute('aria-invalid', 'true'); f.setAttribute('aria-describedby', 'adresse-fehler'); f.focus(); return; }
        fe.hidden = true; f.removeAttribute('aria-invalid'); mail = wert; sperre = 0; clearTimeout(sperrTimer);
        zeichne(false); melden(`Adresse geändert. Neuer Link an ${maske(mail)} gesendet.`); $('#titel').focus({ preventScroll: true });
      };
    }
    $('#leiste-handlung').onclick = () => { const z = $('#ics-knopf') || $('#erneut-knopf') || $('.aktionen .taste'); if (z) z.click(); };
  }

  function setzeV(neu, schreiben) {
    v = neu;
    document.querySelectorAll('#pult-v button').forEach(b => b.setAttribute('aria-pressed', b.dataset.v === v));
    if (schreiben) { const q = new URLSearchParams(location.search); q.set('v', v); history.replaceState(null, '', location.pathname + '?' + q + location.hash); }
    zeichne(true);
  }
  function setzeLeer(an) {
    leer = an; h.toggleAttribute('data-leer', an);
    const b = $('#pult-leer'); b.setAttribute('aria-pressed', an); b.lastElementChild.textContent = an ? '●' : '○';
    zeichne(true);
  }
  document.querySelectorAll('#pult-v button').forEach(b => b.addEventListener('click', () => setzeV(b.dataset.v, true)));
  $('#pult-leer').addEventListener('click', () => setzeLeer(!leer));

  // Kopf-Status aus echten Zeiten (vereinfacht, im Einsatz: oeffnungszeiten-status)
  (function status() {
    const t = teile(new Date()), min = t.std * 60 + t.min, wt = t.wt, tag = werktag(tagIso(t));
    let offen = false, bis = '';
    if (tag && wt !== 'Sat' && min >= 450 && min < 1080) { offen = true; bis = '18 Uhr'; }
    if (wt === 'Sat' && !feiertage(t.j).has(tagIso(t)) && min >= 480 && min < 720) { offen = true; bis = '12 Uhr'; }
    const el = $('#kopf-status'); el.classList.toggle('zu', !offen); el.lastElementChild.textContent = offen ? `Geöffnet bis ${bis}` : 'Gerade geschlossen';
  })();

  if (new URLSearchParams(location.search).has('leer')) { leer = true; h.setAttribute('data-leer', ''); $('#pult-leer').setAttribute('aria-pressed', 'true'); $('#pult-leer').lastElementChild.textContent = '●'; }
  zeichne(true);
  setTimeout(() => { if (!h.classList.contains('vorschau')) $('#titel').focus({ preventScroll: true }); }, 60);

  /* ---------- Vorführung ---------- */
  let uhren = [];
  const spaeter = (f, ms) => uhren.push(setTimeout(f, ms));
  function stopp() { uhren.forEach(clearTimeout); uhren = []; }
  function neustart() { stopp(); sperre = 0; clearTimeout(sperrTimer); const d = $('#ics-vorschau'); if (d) d.open = false; zeichne(true); }
  function druecke(el, dann) { if (!el) return; el.classList.add('druck'); spaeter(() => { el.classList.remove('druck'); if (dann) dann(); }, 220); }
  function vorfuehren() {
    neustart();
    h.classList.add('vorfuehrung');
    spaeter(() => druecke($('[data-kopieren]'), () => { const s = $('[data-kopieren] span'); if (s) { s.textContent = 'Kopiert'; spaeter(() => { s.textContent = 'Kopieren'; }, 1500); } }), 1700);
    if (v === 'termin') {
      spaeter(() => druecke($('#ics-knopf'), () => { icsLaden(false); const d = $('#ics-vorschau'); if (d) d.open = true; }), 3400);
      spaeter(() => { const d = $('#ics-vorschau'); if (d) d.open = false; }, 7600);
    } else if (v === 'bestaetigen') {
      spaeter(() => druecke($('#erneut-knopf'), () => erneutSenden($('#erneut-knopf'))), 3000);
    } else {
      spaeter(() => druecke($('.aktionen .taste')), 3400);
    }
    spaeter(vorfuehren, 9000);
  }
  function pause() { stopp(); clearTimeout(sperrTimer); h.classList.remove('vorfuehrung'); }
  function weiter() { vorfuehren(); }

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