Finanzierung mit Pflichtangaben und Warnhinweis
Neben jeder Rate stehen effektiver Jahreszins in gleicher Größe, Sollzins, Nettodarlehen und Laufzeit, darunter als eigene Zeile „Achtung! Kreditaufnahme kostet Geld.“; das repräsentative Beispiel klappt auf, fehlende Pflichtwerte machen den Block zum markierten Platzhalter ohne Rate.
Autohaus und Händler mit Bestand: Teaser kompakt mit Ratenzeile, Fahrzeugseite vollständig neben der Pflichtzeile nach Pkw-EnVKV.
Neben jedem Preis, der eine Rate, eine Null-Prozent-Finanzierung oder „später bezahlen“ nennt, steht ein ruhiger Block. Effektiver Jahreszins, Sollzins, Nettodarlehensbetrag und Laufzeit stehen direkt bei der Rate, der Effektivzins mindestens so groß wie die Rate. Darunter steht als eigene Zeile der Warnhinweis „Achtung! Kreditaufnahme kostet Geld.“ Gesamtbetrag, Barzahlungspreis, Anzahlung und Kreditgeber folgen im Aufklappteil „Finanzierungsbeispiel“. Fehlt ein Pflichtwert, bleibt der Block ein markierter Platzhalter ohne Rate.
Die Rate ist die stärkste Verkaufszahl auf einer Autohaus-, Möbel- oder Heizungsseite. Hier wird sie ehrlich gesetzt: Zahlen in Tabellenziffern, gleich gewichtet, ohne Sternchen-Fußnote. Das wirkt seriöser als die übliche Raten-Pille mit Kleingedrucktem. Ab 20.11.2026 ist es außerdem die Form, die das Recht verlangt.
Wann einsetzen
Überall, wo eine Rate, ein Zins, „0 %“, Leasing, Mietkauf, Ratenzahlung oder Buy now pay later beworben wird: Fahrzeugkarten, Kaufbox, Kostenrechner mit Monatsrate, Kurse mit Ratenzahlung. Auch bei vorgefertigten Bannern von Zahlungsdienstleistern, denn für sie haftet der Händler selbst.
Wann nicht
Nicht, wenn gar nicht mit Finanzierung geworben wird. Kein Rechner, der Raten ohne echte Konditionen des Kreditgebers erfindet. Kein Banner „ab 29 €/Monat“ im Hero ohne die Angaben daneben. Der Warnhinweis darf nicht in den Footer, in eine Fußnote oder hinter einen Link. Generik-Gefahr: grüne Raten-Badges aus dem Shop-Template.
Varianten
- fahrzeugkarte: kompakt im Teaser (Rate und Effektivzins gleich groß, Pflichtwerte in einer Zeile, Warnhinweis), auf der Detailansicht vollständig zusammen mit der Pflichtzeile nach Pkw-EnVKV
- kaufbox: Ratenzeile unter dem Preis, die Pflichtwerte daneben, Aufklappteil „Finanzierungsbeispiel“; wechselt die Ausführung, rechnet die Rate neu
- rechner: Laufzeit-Regler, Monatsrate live aus Betrag und Laufzeit, Effektivzins aus der Konditionstabelle des Kreditgebers und Warnhinweis wandern mit; das repräsentative Beispiel bleibt fest
- ratenzahlung-eigen (nicht in der Demo): Unternehmer stundet selbst, kostenlos, höchstens 50 Tage; dann nur Hinweis auf die Ausnahme, vom Kunden prüfen lassen
Umsetzung
Datenquelle ist ein Objekt finanzierung mit kreditgeber, anschrift, nettodarlehen, sollzins, effektivzins, laufzeit_monate, rate, gesamtbetrag, barzahlungspreis, anzahlung und stand; die Werte liefert der Kreditgeber. Fehlt ein Pflichtwert, rendert der Block nicht als Werbung, sondern als markierter Platzhalter „vom Kunden prüfen lassen“ ohne Rate. Kopfzeile: Rate und effektiver Jahreszins in derselben Schriftgröße, beide mit font-variant-numeric: tabular-nums; Sollzins, Nettodarlehensbetrag und Laufzeit in einer dl direkt darunter. Der Warnhinweis „Achtung! Kreditaufnahme kostet Geld.“ ist ein eigener Absatz mit role=note, Kontrast mindestens 4,5 : 1, nicht kleiner als der Fließtext, nie im Aufklappteil. Das repräsentative Beispiel (Gesamtbetrag, Barzahlungspreis, Anzahlung, Kreditgeber mit Anschrift) steht in details/summary „Finanzierungsbeispiel“, Aufklappen über ::details-content wie akkordeon-hoehe-auto. Rechner: input type=range für die Laufzeit, output für die Rate; der Effektivzins kommt aus der Konditionstabelle des Kreditgebers, nie aus einer Annahme. Rate nach der Annuitätenformel, auf Cent gerundet, letzte Rate als Ausgleich. Einbindung: kostenrechner-konfigurator (Ratenrechner), fahrzeugbestand-feed-teaser (Finanzierungsbeispiel ersetzen), produkt-kaufbox-varianten (Ratenzeile). Ohne JS stehen alle Angaben offen untereinander. Alle Formulierungen vom Kunden prüfen lassen.
Robust umsetzen
Telefon. Rate und Effektivzins nebeneinander, darunter die Pflichtwerte, dann der Warnhinweis, dann der Aufklappteil. Bei 360 px alles untereinander. Im Rechner steht das Ergebnis direkt unter dem Laufzeit-Regler.
Ohne Bewegung. Aufklappen ohne Höhenanimation, Zahlen springen ohne Zählen.
Barrierefrei. Tastatur: summary und Regler erreichbar, Fokus nach fokus-stil-system. Kontrast 4,5 : 1 auch für den Warnhinweis. Keine Bewegung über 5 s. 320 px und 200 % Zoom: dl bricht einspaltig um, Zahlen nie abgeschnitten. Screenreader: Rate und Effektivzins in einem Satz vorlesbar, aria-describedby auf den Warnhinweis.
Code
<!DOCTYPE html>
<!--
Finanzierung mit Pflichtangaben und Warnhinweis
Fundus · Sektionen & Funktionen · https://cecodesign.de/lab/fundus/sektionen/finanzierung-pflichtangaben/
Eigenständige Datei. Bilder liegen hier unter bilder/ – eigene einsetzen.
-->
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Finanzierung mit Pflichtangaben und Warnhinweis · 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: 'Bodoni Moda'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/bodoni-moda:vf@latest/latin-opsz-normal.woff2') format('woff2-variations'); font-weight: 400 900; font-style: normal; font-display: swap; }
@font-face { font-family: 'Instrument Sans'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/instrument-sans:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 400 700; font-display: swap; }
@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: '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; }
/* Eine Kurve für die ganze Demo: token-toggle-asymmetrisch (öffnen expo.out, schließen expo.in, schneller) */
:root {
--auf: cubic-bezier(0.16, 1, 0.3, 1);
--zu: cubic-bezier(0.7, 0, 0.84, 0);
--t-ui: 120ms; --t-auf: 250ms; --t-zu: 170ms;
}
* { box-sizing: border-box; margin: 0; }
html { -webkit-text-size-adjust: 100%; }
body { min-height: 100svh; }
:root:has(:focus-visible) { scroll-behavior: auto; }
button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; }
[hidden] { display: none !important; }
.nur-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.ziffern { font-variant-numeric: tabular-nums; }
sub { font-size: .72em; vertical-align: -.18em; line-height: 0; }
/* ═══════════ Der Baustein: Finanzierungsblock ═══════════
Alle Welten setzen nur diese Variablen. Rate und effektiver Jahreszins teilen sich .fin-zahl (gleiche Größe). */
.fin { --fin-gap: 10px; display: grid; gap: var(--fin-gap); color: var(--fin-text); }
.fin-kopf { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px 12px; }
.fin-titel { font: var(--fin-label-font); letter-spacing: var(--fin-label-lauf, .06em); text-transform: uppercase; color: var(--fin-leise); }
.fin-stand { font-size: .8125rem; color: var(--fin-leise); }
.fin-zahlen { display: grid; grid-template-columns: repeat(2, minmax(0, max-content)); gap: 4px clamp(16px, 3vw, 32px); align-items: end; }
.fin-zahl { display: grid; gap: 4px; min-width: 0; }
.fin-zahl b { font: var(--fin-zahl-font); font-size: var(--fin-zahl-groesse); font-variant-numeric: tabular-nums; letter-spacing: -.01em; line-height: 1; white-space: nowrap; }
.fin-zahl span { font-size: .875rem; line-height: 1.3; color: var(--fin-text); }
.fin-werte { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: .9375rem; line-height: 1.35; padding-top: var(--fin-gap); border-top: 1px solid var(--fin-linie); }
.fin-werte div { display: flex; gap: 6px; min-width: 0; }
.fin-werte dt { color: var(--fin-leise); }
.fin-werte dd { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.fin-voll .fin-werte { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; }
.fin-voll .fin-werte div { flex-direction: column; gap: 2px; padding: 10px 0; border-bottom: 1px solid var(--fin-linie); }
.fin-voll .fin-werte dt { font-size: .8125rem; }
.fin-voll .fin-werte dd { white-space: normal; }
.fin-voll .fin-werte .breit { grid-column: 1 / -1; }
/* Warnhinweis: eigene Zeile, volle Textfarbe, nicht kleiner als Fließtext, nie im Aufklappteil */
.fin-warn { display: flex; align-items: center; gap: 10px; font-size: var(--fin-warn-groesse, 1rem); font-weight: 700; line-height: 1.3; color: var(--fin-text); padding: 8px 0; border-block: 2px solid var(--fin-text); }
.fin-warn i { flex: none; display: grid; place-items: center; width: 1.375em; height: 1.375em; font-style: normal; font-weight: 800; border-radius: var(--fin-radius, 3px); background: var(--fin-text); color: var(--fin-grund); font-size: .875em; }
/* Aufklappteil: ::details-content als Grid 0fr → 1fr (akkordeon-hoehe-auto) */
.fin-bsp summary { list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; cursor: pointer; font-weight: 600; font-size: .9375rem; }
.fin-bsp summary::-webkit-details-marker { display: none; }
.fin-bsp summary svg { flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; transition: transform var(--t-zu) var(--zu); }
.fin-bsp[open] summary svg { transform: rotate(45deg); transition: transform var(--t-auf) var(--auf); }
.fin-bsp summary:hover span { text-decoration: underline; text-underline-offset: 3px; }
.fin-bsp::details-content { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows var(--t-zu) var(--zu), opacity var(--t-zu) var(--zu), content-visibility var(--t-zu) allow-discrete; }
.fin-bsp[open]::details-content { grid-template-rows: 1fr; opacity: 1; transition: grid-template-rows var(--t-auf) var(--auf), opacity var(--t-auf) var(--auf), content-visibility var(--t-auf) allow-discrete; }
.fin-bsp .innen { overflow: hidden; min-height: 0; }
.fin-bsp p { font-size: .9375rem; line-height: 1.5; padding-bottom: 6px; text-wrap: pretty; }
.fin-bsp p + p { color: var(--fin-leise); font-size: .875rem; }
/* Platzhalter: fehlt ein Pflichtwert, wird keine Rate gezeigt */
.fin-platz { display: grid; gap: 8px; padding: 14px 16px; border: 2px dashed var(--fehler-schrift); border-radius: var(--fin-radius, 4px); background: repeating-linear-gradient(135deg, var(--platz-a) 0 10px, var(--platz-b) 10px 20px); color: var(--fin-text); }
.fin-platz .marke { justify-self: start; font: 600 .75rem/1 'Geist Mono', ui-monospace, monospace; letter-spacing: .1em; text-transform: uppercase; background: var(--fehler-schrift); color: #fff; padding: 5px 8px; border-radius: 3px; }
.fin-platz p { font-size: .9375rem; line-height: 1.45; }
.fin-platz ul { font-size: .9375rem; padding-left: 1.1em; line-height: 1.45; }
.fin-platz b { font-weight: 700; }
/* ═══════════ Welt 1 · Fahrzeugkarte (kfz-werkstatt-autohaus) ═══════════ */
.kfz { --grund: #f2f1ed; --karte: #fff; --text: #121314; --leise: #595e63; --linie: rgba(18, 19, 20, .14); --asphalt: #232629; --asphalt-leise: #b9bec4; --gelb: #f5b700;
--fehler-schrift: #b3261e; --platz-a: #fbeceb; --platz-b: #f7e2e0;
--fin-text: var(--text); --fin-leise: var(--leise); --fin-linie: var(--linie); --fin-grund: #fff; --fin-radius: 4px;
--fin-label-font: 600 .8125rem/1.2 'Google Sans Flex', system-ui, sans-serif; --fin-zahl-font: 800 1.75rem/1 'Mona Sans', sans-serif; --fin-zahl-groesse: 1.75rem;
background: var(--grund); color: var(--text); font-family: 'Google Sans Flex', system-ui, sans-serif; font-size: 1.0625rem; line-height: 1.55; min-height: 100svh; }
.kfz .fin-zahl b { font-stretch: 75%; }
.kfz :focus-visible { outline: 3px solid var(--text); outline-offset: 3px; }
.kfz .label { font: 600 .8125rem/1.2 'Google Sans Flex', system-ui, sans-serif; letter-spacing: .06em; text-transform: uppercase; color: var(--leise); }
.kfz-kopf { background: var(--asphalt); color: #fff; }
.kfz-kopf .innen { max-width: 1320px; margin: 0 auto; padding: 12px var(--rand, clamp(16px, 4vw, 48px)); display: flex; align-items: center; gap: 12px 28px; flex-wrap: wrap; }
.kfz-logo { display: grid; gap: 4px; line-height: 1; text-decoration: none; margin-right: auto; }
.kfz-logo b { font: 800 1.5rem/1 'Mona Sans', sans-serif; font-stretch: 75%; letter-spacing: .03em; }
.kfz-logo span { font-size: .8125rem; color: var(--asphalt-leise); }
.kfz-kopf nav ul { display: flex; gap: 22px; list-style: none; padding: 0; }
.kfz-kopf nav a { display: inline-block; padding: 10px 0; text-decoration: none; font-size: .9375rem; font-weight: 500; border-bottom: 2px solid transparent; transition: border-color var(--t-ui) linear; }
.kfz-kopf nav a:hover { border-color: var(--asphalt-leise); }
.kfz-kopf nav a[aria-current] { border-color: #fff; }
.kfz-kopf :focus-visible { outline-color: #fff; }
.kfz-tel { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; font-weight: 700; font-size: 1.125rem; font-variant-numeric: tabular-nums; text-decoration: none; }
.kfz-tel svg, .kfz-knopf svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.kfz-main { max-width: 1320px; margin: 0 auto; padding: clamp(28px, 5vw, 64px) clamp(16px, 4vw, 48px) clamp(40px, 6vw, 80px); }
.kfz-kopfzeile { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 340px); gap: 20px 48px; align-items: end; padding-bottom: 20px; border-bottom: 2px solid var(--asphalt); }
.kfz-kopfzeile h2 { font: 800 clamp(2.25rem, 1.3rem + 3.6vw, 4.25rem)/.98 'Mona Sans', sans-serif; font-stretch: 80%; letter-spacing: -.02em; margin-top: 10px; text-wrap: balance; }
.kfz-kopfzeile .lead { max-width: 58ch; margin-top: 14px; text-wrap: pretty; }
.kfz-bank { border: 2px solid var(--asphalt); border-radius: 8px; background: var(--karte); padding: 12px 16px; display: grid; gap: 2px; font-size: .9375rem; }
.kfz-bank b { font-size: 1rem; }
.kfz-bank small { font-size: .875rem; color: var(--leise); }
.kfz-karten { list-style: none; padding: 0; margin-top: 24px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 1.8vw, 22px); }
.kfz-karten > li { display: grid; min-width: 0; }
.kfz-karte { display: flex; flex-direction: column; background: var(--karte); border: 1px solid var(--linie); border-radius: 10px; overflow: hidden; transition: border-color var(--t-ui) linear; }
.kfz-karte:hover { border-color: var(--asphalt); }
.kfz-karte.zeigt { border-color: var(--asphalt); box-shadow: 0 0 0 1px var(--asphalt); }
.kfz-bild { position: relative; aspect-ratio: 3 / 2; background: #dcdbd6; }
.kfz-bild img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }
.kfz-schild { position: absolute; left: 10px; top: 10px; font-size: .75rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: 5px 9px; border-radius: 5px; background: rgba(255, 255, 255, .94); }
.kfz-koerper { display: flex; flex-direction: column; gap: 14px; padding: 16px 18px 18px; flex: 1; }
.kfz-modell { font: 750 1.375rem/1.1 'Mona Sans', sans-serif; font-stretch: 85%; }
.kfz-linie { display: block; font: 600 .9375rem/1.3 'Google Sans Flex', system-ui, sans-serif; color: var(--leise); margin-top: 2px; letter-spacing: 0; }
.kfz-preis { display: grid; gap: 2px; }
.kfz-preis b { font: 800 2.125rem/1 'Mona Sans', sans-serif; font-stretch: 75%; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.kfz-preis span { font-size: .875rem; color: var(--leise); }
.kfz-fin { background: var(--grund); border-radius: 8px; padding: 12px 14px 4px; }
.kfz-fin .fin-warn { --fin-warn-groesse: 1rem; }
.kfz-fin:has(.fin-platz), .lux-fin:has(.fin-platz) { padding: 0; background: none; border: 0; }
/* Pflichtzeile Pkw-EnVKV wie fahrzeugbestand-feed-teaser: gleiche Größe wie die Eckdaten */
.kfz-pflicht { margin-top: auto; padding-top: 12px; border-top: 2px solid var(--asphalt); font-size: .9375rem; font-weight: 500; line-height: 1.45; }
.kfz-pflicht > span { display: block; }
.kfz-pflicht b { font-weight: 700; font-variant-numeric: tabular-nums; }
.kl { display: inline-grid; place-items: center; min-width: 1.6em; height: 1.45em; padding: 0 .3em; border-radius: 4px; margin-left: 2px; font-weight: 800; background: var(--k); color: var(--kt); }
.kfz-knopf { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 44px; padding: 0 18px; border: 2px solid var(--asphalt); border-radius: 4px; background: transparent; font-weight: 700; font-size: .9375rem; text-decoration: none; transition: background-color var(--t-ui) linear, color var(--t-ui) linear; }
.kfz-knopf:hover, .kfz-knopf.zeigt { background: var(--asphalt); color: #fff; }
.kfz-knopf.voll { background: var(--asphalt); color: #fff; }
.kfz-knopf.voll:hover { background: #000; }
.kfz-knopf.gelb { background: var(--gelb); border-color: var(--asphalt); color: var(--text); }
/* Detailansicht */
.kfz-detail { margin-top: 24px; display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(20px, 3vw, 44px); align-items: start; }
.kfz-detail.kommt { animation: kommt var(--t-auf) var(--auf) both; }
@keyframes kommt { from { opacity: 0; transform: translateY(8px); } }
.kfz-detail .kfz-bild { border-radius: 10px; overflow: hidden; aspect-ratio: 16 / 10; }
.kfz-zurueck { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 2px; border: 0; background: none; font-weight: 600; margin-top: 18px; }
.kfz-zurueck svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.kfz-zurueck:hover span { text-decoration: underline; text-underline-offset: 3px; }
.kfz-eck { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(9rem, 100%), 1fr)); gap: 12px 16px; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--linie); }
.kfz-eck dt { font-size: .75rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--leise); }
.kfz-eck dd { font-size: .9375rem; font-weight: 500; font-variant-numeric: tabular-nums; }
.kfz-detail .kfz-pflicht { margin-top: 18px; }
.kfz-seite { display: grid; gap: 18px; background: var(--karte); border: 1px solid var(--linie); border-radius: 10px; padding: clamp(18px, 2.4vw, 28px); }
.kfz-seite h3 { font: 800 clamp(1.75rem, 1.2rem + 1.6vw, 2.5rem)/1 'Mona Sans', sans-serif; font-stretch: 80%; }
.kfz-seite .fin { --fin-zahl-groesse: 2.25rem; }
.kfz-seite .knoepfe { display: flex; flex-wrap: wrap; gap: 10px; }
.kfz-recht { margin-top: 28px; font-size: .875rem; color: var(--leise); max-width: 90ch; }
.platzhalter-text { background: #fff3c4; color: #121314; border-radius: 3px; padding: 0 4px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
/* ═══════════ Welt 2 · Kaufbox (luxus-manufaktur-stille) ═══════════ */
.lux { --text: #23282f; --grund: #fff; --flaeche: #f4f4f2; --leise: #71757b; --linie: #d9dadb; --messing: #a88a5a;
--fehler-schrift: #b3261e; --platz-a: #fbf3f2; --platz-b: #f6e7e5;
--fin-text: var(--text); --fin-leise: var(--leise); --fin-linie: var(--linie); --fin-grund: #fff; --fin-radius: 0;
--fin-label-font: 500 .75rem/1.2 'Instrument Sans', system-ui, sans-serif; --fin-label-lauf: .08em; --fin-zahl-font: 500 1.5rem/1 'Instrument Sans', sans-serif; --fin-zahl-groesse: 1.5rem; --fin-warn-groesse: .9375rem;
background: var(--grund); color: var(--text); font-family: 'Instrument Sans', system-ui, sans-serif; font-size: .9375rem; line-height: 1.47; min-height: 100svh; }
.lux :focus-visible { outline: 2px solid var(--messing); outline-offset: 3px; }
.lux .label { font: 500 .75rem/1.2 'Instrument Sans', sans-serif; letter-spacing: .08em; text-transform: uppercase; color: var(--leise); }
.lux-kopf { display: flex; align-items: center; justify-content: space-between; gap: 16px 32px; flex-wrap: wrap; padding: 22px clamp(24px, 6vw, 96px); border-bottom: 1px solid var(--linie); }
.lux-marke { display: grid; gap: 4px; text-decoration: none; }
.lux-marke b { font: 400 1.5rem/1 'Bodoni Moda', serif; letter-spacing: .32em; }
.lux-marke span { font-size: .6875rem; letter-spacing: .14em; text-transform: uppercase; color: var(--leise); }
.lux-kopf nav { display: flex; gap: 28px; flex-wrap: wrap; }
.lux-kopf nav a { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; text-decoration: none; padding: 12px 0; background: linear-gradient(var(--messing), var(--messing)) no-repeat left 80% / 0 1px; transition: background-size var(--t-zu) var(--zu); }
.lux-kopf nav a:hover { background-size: 100% 1px; transition: background-size var(--t-auf) var(--auf); }
.lux-main { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: clamp(24px, 5vw, 96px); padding: clamp(24px, 4vw, 64px) clamp(24px, 6vw, 96px) clamp(48px, 8vw, 120px); max-width: 1440px; margin: 0 auto; align-items: start; }
.lux-buehne { position: relative; background: var(--flaeche); aspect-ratio: 5 / 4; display: grid; place-items: center; overflow: hidden; }
.lux-buehne svg { width: 82%; height: auto; }
.lux-buehne figcaption { position: absolute; left: 20px; bottom: 16px; font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--leise); }
.lux-holz { transition: fill 400ms var(--auf); }
.lux-box { display: grid; gap: 22px; position: sticky; top: 24px; }
.lux-box h1 { font: 400 clamp(2.5rem, 5.5vw, 4.25rem)/1.05 'Bodoni Moda', serif; letter-spacing: -.01em; margin-top: 8px; }
.lux-box .text { color: var(--text); max-width: 42ch; }
.lux-gruppe { border: 0; padding: 0; display: grid; gap: 10px; }
.lux-gruppe legend { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--leise); margin-bottom: 10px; padding: 0; }
.lux-gruppe legend b { color: var(--text); font-weight: 500; }
.lux-wahl { display: flex; flex-wrap: wrap; gap: 8px; }
.lux-wahl label { position: relative; display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 16px; border: 1px solid var(--linie); cursor: pointer; transition: border-color var(--t-ui) linear; }
.lux-wahl label:hover { border-color: var(--text); }
.lux-wahl input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.lux-wahl label:has(input:checked) { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }
.lux-wahl label:has(input:focus-visible) { outline: 2px solid var(--messing); outline-offset: 3px; }
.lux-wahl .muster { width: 18px; height: 18px; border-radius: 50%; background: var(--m); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15); }
.lux-preis { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; padding-top: 20px; border-top: 1px solid var(--linie); }
.lux-preis b { font: 400 2.25rem/1 'Bodoni Moda', serif; font-variant-numeric: tabular-nums lining-nums; }
.lux-preis span { font-size: .8125rem; color: var(--leise); }
.lux-fin { padding: 16px 18px 6px; border: 1px solid var(--linie); }
.lux-fin .fin-warn { border-width: 1px; }
.lux-fin .fin-warn i { border-radius: 50%; }
.lux-oder { font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--leise); }
.lux-knopf { display: inline-flex; align-items: center; justify-content: center; gap: 12px; min-height: 52px; padding: 0 28px; border: 0; border-radius: 999px; background: var(--text); color: #fff; font-size: .8125rem; letter-spacing: .08em; text-transform: uppercase; }
.lux-knopf:hover { background: #000; }
.lux-knopf svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.5; }
.lux-neben { display: flex; flex-wrap: wrap; gap: 6px 20px; font-size: .8125rem; color: var(--leise); }
.lux-neben a { color: var(--text); }
.lux-status { font-size: .8125rem; color: var(--text); min-height: 1.2em; }
/* ═══════════ Welt 3 · Rechner (handwerk-werkstatt-klartext) ═══════════ */
.hw { --grund: #efedea; --zement: #d3cdc2; --holz: #c3b4a5; --salbei: #bdc1a9; --text: #1a1a1a; --orange: #fb6100; --leise: #5f5e5d; --linie: #cdcbc9; --tafel: #1d1c1a;
--fehler-schrift: #b3261e; --platz-a: #f6e3e0; --platz-b: #efd7d3;
--fin-text: #f4f1ec; --fin-leise: #c9c4bb; --fin-linie: rgba(244, 241, 236, .2); --fin-grund: #1d1c1a; --fin-radius: 4px;
--fin-label-font: 600 .8125rem/1.2 'Special Gothic', sans-serif; --fin-label-lauf: .04em; --fin-zahl-font: 700 2.75rem/1 'Special Gothic', sans-serif; --fin-zahl-groesse: clamp(2.25rem, 1.6rem + 2vw, 3rem); --fin-warn-groesse: 1.0625rem;
background: var(--grund); color: var(--text); font-family: 'Atkinson Hyperlegible Next', system-ui, sans-serif; font-size: 1.0625rem; line-height: 1.55; min-height: 100svh; }
.hw .fin-zahl b { font-stretch: 85%; }
.hw :focus-visible { outline: 3px solid var(--text); outline-offset: 3px; }
.hw .label { font: 600 .8125rem/1.2 'Special Gothic', sans-serif; font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase; color: var(--leise); }
.hw-kopf { display: flex; align-items: center; gap: 12px 28px; flex-wrap: wrap; padding: 14px clamp(16px, 4vw, 48px); border-bottom: 1px solid var(--zement); }
.hw-marke { display: grid; gap: 2px; margin-right: auto; text-decoration: none; line-height: 1; }
.hw-marke b { font: 700 1.5rem/1 'Special Gothic', sans-serif; letter-spacing: -.01em; }
.hw-marke span { font: 600 .75rem/1.2 'Special Gothic', sans-serif; font-stretch: 75%; letter-spacing: .06em; text-transform: uppercase; color: var(--leise); }
.hw-tel { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; font-weight: 700; font-variant-numeric: tabular-nums; text-decoration: none; }
.hw-tel svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.hw-signal { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 44px; padding: 0 18px; border: 0; border-radius: 4px; background: var(--orange); color: var(--text); font-weight: 700; text-decoration: none; transition: background-color var(--t-ui) linear; }
.hw-signal:hover { background: #ff7a26; }
.hw-main { max-width: 1280px; margin: 0 auto; padding: clamp(28px, 5vw, 72px) clamp(16px, 4vw, 48px) clamp(48px, 7vw, 96px); }
.hw-intro { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 16px 48px; align-items: end; }
.hw-intro h2 { font: 700 clamp(2.25rem, 1.5rem + 3vw, 3.5rem)/1.05 'Special Gothic', sans-serif; letter-spacing: -.01em; margin-top: 10px; text-wrap: balance; }
.hw-intro p.lead { max-width: 52ch; text-wrap: pretty; }
.hw-raster { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 6fr); gap: clamp(16px, 2.4vw, 32px); margin-top: clamp(24px, 4vw, 48px); align-items: start; }
.hw-eingabe { display: grid; gap: 22px; }
.hw-angebot { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 16px; align-items: center; background: var(--zement); border-radius: 8px; padding: 16px; }
.hw-angebot svg { width: 96px; height: 80px; }
.hw-angebot b { display: block; font: 700 1.125rem/1.2 'Special Gothic', sans-serif; }
.hw-angebot span { font-size: .9375rem; }
.hw-feld { display: grid; gap: 10px; padding: 18px 0; border-top: 1px solid var(--zement); }
.hw-feld-kopf { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.hw-feld label, .hw-feld .titel { font-weight: 700; }
.hw-feld output, .hw-wert { font: 700 1.25rem/1 'Special Gothic', sans-serif; font-variant-numeric: tabular-nums; }
.hw-betrag { display: flex; align-items: stretch; gap: 0; }
.hw-betrag button { width: 48px; min-height: 48px; border: 1px solid var(--text); background: #fff; font-size: 1.25rem; font-weight: 700; }
.hw-betrag button:first-child { border-radius: 4px 0 0 4px; }
.hw-betrag button:last-child { border-radius: 0 4px 4px 0; }
.hw-betrag button:hover { background: var(--zement); }
.hw-betrag input { width: 9ch; min-width: 0; text-align: center; border: 1px solid var(--text); border-inline: 0; background: #fff; font: 700 1.125rem/1 'Atkinson Hyperlegible Next', sans-serif; font-variant-numeric: tabular-nums; }
.hw-betrag input:focus-visible { outline-offset: 0; }
.hw-hinweis { font-size: .875rem; color: var(--leise); }
.hw-regler { --p: 50%; width: 100%; height: 32px; margin: 0; background: transparent; appearance: none; -webkit-appearance: none; cursor: pointer; }
.hw-regler::-webkit-slider-runnable-track { height: 8px; border-radius: 4px; background: linear-gradient(to right, var(--text) var(--p), var(--zement) var(--p)); }
.hw-regler::-moz-range-track { height: 8px; border-radius: 4px; background: linear-gradient(to right, var(--text) var(--p), var(--zement) var(--p)); }
.hw-regler::-webkit-slider-thumb { -webkit-appearance: none; width: 28px; height: 28px; margin-top: -10px; border-radius: 50%; background: #fff; border: 3px solid var(--text); }
.hw-regler::-moz-range-thumb { width: 22px; height: 22px; border-radius: 50%; background: #fff; border: 3px solid var(--text); }
.hw-skala { display: flex; justify-content: space-between; font-size: .8125rem; color: var(--leise); font-variant-numeric: tabular-nums; }
.hw-kond { width: 100%; border-collapse: collapse; font-size: .9375rem; }
.hw-kond caption { text-align: left; font-weight: 700; padding-bottom: 8px; }
.hw-kond th, .hw-kond td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--linie); font-variant-numeric: tabular-nums; }
.hw-kond th[scope=col] { font: 600 .75rem/1.2 'Special Gothic', sans-serif; font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase; color: var(--leise); }
.hw-kond td:not(:first-child), .hw-kond th:not(:first-child) { text-align: right; }
.hw-kond tr.aktiv td { background: var(--salbei); font-weight: 700; transition: background-color var(--t-ui) linear; }
.hw-kond tr.aktiv td:first-child { box-shadow: inset 4px 0 0 var(--text); }
.hw-tafel { background: var(--tafel); color: #f4f1ec; border-radius: 8px; padding: clamp(18px, 2.6vw, 30px); display: grid; gap: 18px; position: sticky; top: 16px; }
.hw-tafel :focus-visible { outline-color: #f4f1ec; }
.hw-tafel .label { color: #c9c4bb; }
.hw-tafel .fin-zahl span { color: #f4f1ec; }
.hw-tafel .fin-werte dd { color: #fff; }
.hw-teile { display: grid; gap: 8px; }
.hw-balken { display: flex; height: 14px; border-radius: 3px; overflow: hidden; background: #3a3833; }
.hw-balken span { display: block; height: 100%; transition: flex-grow var(--t-auf) var(--auf); }
.hw-balken .netto { background: var(--salbei); }
.hw-balken .zins { background: var(--orange); }
.hw-legende { display: flex; flex-wrap: wrap; gap: 4px 18px; font-size: .875rem; color: #e6e1d8; font-variant-numeric: tabular-nums; }
.hw-legende i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; background: var(--c); }
.hw-tafel .fin-platz { color: var(--text); }
.hw-tafel .hw-signal { justify-self: start; }
.hw-recht { margin-top: 28px; font-size: .875rem; color: var(--leise); max-width: 90ch; }
/* Gedachter Zeiger der Vorführung */
.zeiger { position: fixed; left: 0; top: 0; z-index: 40; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; border: 2px solid #121314; background: rgba(245, 183, 0, .55); pointer-events: none; opacity: 0; transition: transform 700ms var(--auf), opacity 200ms linear; }
.vorfuehrung .zeiger { opacity: 1; }
.zeiger.drueckt { scale: .8; }
/* Pult der Demo */
.pult { position: fixed; right: 16px; bottom: 16px; z-index: 20; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 8px 10px 8px 14px; max-width: calc(100vw - 32px); background: rgba(18, 19, 20, .94); color: #eef0f2; border-radius: 6px; box-shadow: 0 10px 30px -12px rgba(0, 0, 0, .5); }
.pult, .pult button { font: 500 10.5px/1 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; }
.pult .gruppe { display: flex; flex-wrap: wrap; gap: 2px; }
.pult button { color: rgba(238, 240, 242, .78); background: transparent; border: 0; padding: 8px 9px; border-radius: 3px; transition: background-color var(--t-ui) linear, color var(--t-ui) linear; }
.pult button[aria-pressed="true"] { background: #eef0f2; color: #121314; }
.pult button:focus-visible { outline: 2px solid #eef0f2; outline-offset: 2px; }
.pult .werte { text-transform: none; letter-spacing: .02em; color: rgba(238, 240, 242, .72); max-width: 34ch; line-height: 1.4; }
@media (max-width: 1060px) {
.kfz-karten { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
.kfz-detail { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
.lux-main { grid-template-columns: minmax(0, 1fr); }
.lux-box { position: static; }
.hw-raster, .hw-intro { grid-template-columns: minmax(0, 1fr); }
.hw-tafel { position: static; }
/* schmal: Ergebnis direkt unter dem Regler, Konditionstabelle danach */
.hw-eingabe { display: contents; }
.hw-raster > .hw-tafel { order: 1; }
.hw-kond { order: 2; }
}
@media (max-width: 760px) {
.kfz-kopfzeile { grid-template-columns: minmax(0, 1fr); }
.kfz-kopf nav, .lux-kopf nav { display: none; }
.kfz-karten { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
.kfz-tel .nummer, .hw-tel .nummer { display: none; }
.kfz-tel, .hw-tel { width: 44px; justify-content: center; border: 2px solid currentColor; border-radius: 8px; }
.fin-voll .fin-werte { grid-template-columns: minmax(0, 1fr); }
.pult { bottom: 12px; right: 12px; }
}
@media (max-width: 360px) {
.fin-zahlen { grid-template-columns: minmax(0, 1fr); }
.kfz-koerper { padding: 14px; }
.fin-werte dd { white-space: normal; }
.hw-angebot { grid-template-columns: minmax(0, 1fr); }
}
/* Vorschau 960 × 720: nur die Bühne */
.vorschau body { overflow: hidden; }
.vorschau .pult, .vorschau fundus-pult-knopf, .vorschau .kfz-recht, .vorschau .hw-recht, .vorschau .kfz-kopfzeile .lead, .vorschau .kfz-bank, .vorschau .hw-intro .lead, .vorschau .lux-neben, .vorschau .lux-box .text, .vorschau .hw-kond { display: none !important; }
.vorschau .kfz-kopf, .vorschau .kfz-karte .kfz-pflicht, .vorschau .kfz-karte .kfz-knopf, .vorschau .kfz-eck, .vorschau .kfz-detail .kfz-pflicht { display: none !important; }
.vorschau .kfz-main { padding: 22px 28px 0; }
.vorschau .kfz-kopfzeile { padding-bottom: 14px; }
.vorschau .kfz-kopfzeile h2 { font-size: 2.25rem; margin-top: 6px; }
.vorschau .kfz-karten { margin-top: 16px; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.vorschau .kfz-karte .kfz-bild { aspect-ratio: 5 / 2; }
.vorschau .kfz-karte .kfz-bild img { object-position: 50% 78%; }
.vorschau .kfz-koerper { gap: 10px; padding: 12px 14px; }
.vorschau .kfz-karte .kfz-fin { --fin-zahl-groesse: 1.5rem; padding: 10px 12px 2px; }
.vorschau .kfz-detail { margin-top: 16px; }
.vorschau .kfz-detail .kfz-bild { aspect-ratio: 4 / 3; }
.vorschau .kfz-seite { gap: 12px; padding: 16px 18px; }
.vorschau .kfz-seite .fin { --fin-zahl-groesse: 1.875rem; }
.vorschau .fin-voll .fin-werte div { padding: 6px 0; }
.vorschau .lux-kopf, .vorschau .lux-knopf, .vorschau .lux-status { display: none !important; }
.vorschau .lux-main { padding: 28px 40px 0; gap: 36px; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
.vorschau .lux-buehne { aspect-ratio: 4 / 5; }
.vorschau .lux-box { gap: 14px; top: 0; }
.vorschau .lux-box h1 { font-size: 2.75rem; }
.vorschau .lux-preis { padding-top: 12px; }
.vorschau .hw-main { padding-top: 24px; }
.vorschau .hw-intro h2 { font-size: 2.5rem; }
.vorschau .hw-raster { margin-top: 20px; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
:root.reduziert *, :root.reduziert *::before, :root.reduziert *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
:root.reduziert .fin-bsp::details-content { transition: none !important; }
:root.reduziert .zeiger { transition: opacity 150ms linear !important; }
@media (hover: none) {
.kfz-karte:hover { border-color: var(--linie); }
.kfz-knopf:hover { background: transparent; color: var(--text); }
.kfz-knopf.voll:hover { background: var(--asphalt); color: #fff; }
.kfz-knopf:active { background: var(--asphalt); color: #fff; }
.lux-wahl label:hover { border-color: var(--linie); }
.lux-knopf:active { background: #000; }
.hw-betrag button:active { background: var(--zement); }
}
@media (forced-colors: active) {
button, [role=button], [role=tab], [role=switch], input, select, textarea { border: 1px solid ButtonText !important; }
.kl, .fin-warn i { border: 1px solid CanvasText; }
.fin-platz { border-color: CanvasText; }
.lux-wahl label:has(input:checked) { outline: 2px solid Highlight; }
.hw-kond tr.aktiv td { outline: 2px solid Highlight; }
}
</style>
</head>
<body data-v="fahrzeugkarte">
<!-- ═══ Welt 1 · Autohaus Rehkopf, Wolfenbüttel ═══ -->
<div class="welt kfz" data-welt="fahrzeugkarte">
<header class="kfz-kopf">
<div class="innen">
<a class="kfz-logo" href="#kfz" aria-label="Autohaus Rehkopf, Startseite"><b>REHKOPF</b><span>Autohaus · Werkstatt · Wolfenbüttel</span></a>
<nav aria-label="Hauptmenü Autohaus"><ul>
<li><a href="#kfz">Fahrzeuge</a></li>
<li><a href="#kfz" aria-current="page">Finanzierung</a></li>
<li><a href="#kfz">Werkstatt</a></li>
<li><a href="#kfz">Kontakt</a></li>
</ul></nav>
<a class="kfz-tel" href="tel:+495331904700"><svg viewBox="0 0 24 24" 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="nummer">05331 9047-0</span><span class="nur-sr">Anrufen: 05331 9047-0</span></a>
</div>
</header>
<main class="kfz-main" id="kfz">
<div class="kfz-kopfzeile">
<div>
<p class="label">Finanzierung · Fahrzeugbestand</p>
<h2>Monatlich fahren. Ehrlich gerechnet.</h2>
<p class="lead">Bei jeder Rate stehen Zins, Darlehen und Laufzeit gleich daneben. Das ganze Beispiel klappen Sie mit einem Klick auf, die Fahrzeugseite zeigt alles auf einen Blick.</p>
</div>
<div class="kfz-bank">
<span class="label">Kreditgeber</span>
<b data-bank-name>Leinetal Kreditbank AG</b>
<small data-bank-stand>Konditionen Stand 03.11.2026, Bonität vorausgesetzt</small>
</div>
</div>
<ul class="kfz-karten" id="kfz-karten" aria-label="Fahrzeuge mit Finanzierungsbeispiel"></ul>
<div class="kfz-detail" id="kfz-detail" hidden></div>
<p class="kfz-recht"><span class="platzhalter-text">Vom Kunden prüfen lassen:</span> Pflichtangaben nach § 17 PAngV, Warnhinweis, Pkw-EnVKV und Kreditvermittlung (§ 34c bzw. § 34k GewO). Fahrzeuge, Marken, Bank und Zahlen sind für diese Demo erfunden; die Raten sind nach der Annuitätenformel gerechnet, der Effektivzins ohne Nebenkosten.</p>
</main>
</div>
<!-- ═══ Welt 2 · Ahlgrimm Möbelwerkstätten, Fellbach ═══ -->
<div class="welt lux" data-welt="kaufbox" hidden>
<header class="lux-kopf">
<a class="lux-marke" href="#lux" aria-label="Ahlgrimm Möbelwerkstätten, Startseite"><b>AHLGRIMM</b><span>Möbelwerkstätten · Fellbach</span></a>
<nav aria-label="Hauptmenü Manufaktur"><a href="#lux">Kollektion</a><a href="#lux">Werkstatt</a><a href="#lux">Hölzer</a><a href="#lux">Besuch</a></nav>
</header>
<main class="lux-main" id="lux">
<figure class="lux-buehne">
<svg viewBox="0 0 640 400" role="img" aria-labelledby="lux-bildtext" id="lux-bild"></svg>
<figcaption id="lux-bildtext">Sideboard Lisene, Eiche geölt, 200 cm (Zeichnung)</figcaption>
</figure>
<section class="lux-box" aria-labelledby="lux-name">
<div>
<p class="label">Sideboard · Massivholz</p>
<h1 id="lux-name">Lisene</h1>
</div>
<p class="text">Vier Türen, gegliedert von schmalen Lisenen, auf einem zurückgesetzten Sockel. Gebaut in unserer Werkstatt in Fellbach, aus Holz, das wir selbst einschneiden lassen.</p>
<fieldset class="lux-gruppe">
<legend>Holz: <b data-holz-name>Eiche geölt</b></legend>
<div class="lux-wahl">
<label><input type="radio" name="holz" value="eiche" checked><span class="muster" style="--m:#c79c66" aria-hidden="true"></span>Eiche geölt</label>
<label><input type="radio" name="holz" value="nuss"><span class="muster" style="--m:#6a4630" aria-hidden="true"></span>Nussbaum geölt</label>
</div>
</fieldset>
<fieldset class="lux-gruppe">
<legend>Breite: <b data-breite-name>200 cm</b></legend>
<div class="lux-wahl">
<label><input type="radio" name="breite" value="160">160 cm</label>
<label><input type="radio" name="breite" value="200" checked>200 cm</label>
</div>
</fieldset>
<p class="lux-preis"><b data-lux-preis>4.860 €</b><span>inkl. MwSt., Lieferung und Aufbau</span></p>
<div class="lux-fin" id="lux-fin"></div>
<button class="lux-knopf" type="button" id="lux-kaufen">In den Warenkorb <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
<p class="lux-status" role="status" id="lux-status"></p>
<p class="lux-neben"><span>Anfertigung in 10 bis 12 Wochen</span><a href="#lux">Holzmuster bestellen</a><a href="#lux">Termin in der Werkstatt</a></p>
</section>
</main>
</div>
<!-- ═══ Welt 3 · Tenhagen Heizung und Sanitär, Coesfeld ═══ -->
<div class="welt hw" data-welt="rechner" hidden>
<header class="hw-kopf">
<a class="hw-marke" href="#hw" aria-label="Tenhagen Heizung und Sanitär, Startseite"><b>Tenhagen</b><span>Heizung · Sanitär · Coesfeld</span></a>
<a class="hw-tel" href="tel:+4925419830"><svg viewBox="0 0 24 24" 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="nummer">02541 98 30</span><span class="nur-sr">Anrufen: 02541 98 30</span></a>
<a class="hw-signal" href="#hw-anfrage">Anfragen</a>
</header>
<main class="hw-main" id="hw">
<div class="hw-intro">
<div>
<p class="label">Heizungstausch · Finanzierung</p>
<h2>Die Wärmepumpe jetzt, bezahlt in Raten.</h2>
</div>
<p class="lead">Stellen Sie Betrag und Laufzeit ein. Den Zins legt die Bank je Laufzeit fest, er steht in der Tabelle. Die Rate rechnet sich sofort neu, Zins und Hinweis bleiben daneben.</p>
</div>
<div class="hw-raster">
<form class="hw-eingabe" id="hw-form" aria-label="Finanzierung einstellen">
<div class="hw-angebot">
<svg viewBox="0 0 96 80" aria-hidden="true"><rect x="4" y="8" width="88" height="62" rx="4" fill="#efedea" stroke="#1a1a1a" stroke-width="2"/><circle cx="36" cy="39" r="22" fill="#c3b4a5" stroke="#1a1a1a" stroke-width="2"/><g stroke="#1a1a1a" stroke-width="1.5"><path d="M36 17v44M14 39h44M20.5 23.5l31 31M51.5 23.5l-31 31"/></g><circle cx="36" cy="39" r="5" fill="#1a1a1a"/><path d="M68 20h16M68 28h16M68 36h16M68 44h16M68 52h16" stroke="#1a1a1a" stroke-width="2"/><rect x="12" y="70" width="10" height="6" fill="#1a1a1a"/><rect x="74" y="70" width="10" height="6" fill="#1a1a1a"/></svg>
<div><b>Ihr Angebot: Luft-Wasser-Wärmepumpe 9 kW</b><span>Festpreis mit Demontage, Montage und Inbetriebnahme <strong class="ziffern" style="white-space:nowrap">31.480 €</strong></span></div>
</div>
<div class="hw-feld">
<div class="hw-feld-kopf"><label for="hw-betrag">Finanzierungsbetrag</label><span class="hw-hinweis">5.000 bis 30.000 €, in 500er-Schritten</span></div>
<div class="hw-betrag">
<button type="button" data-schritt="-500" aria-label="500 Euro weniger">−</button>
<input id="hw-betrag" type="text" inputmode="numeric" value="18.000" aria-describedby="hw-betrag-info">
<button type="button" data-schritt="500" aria-label="500 Euro mehr">+</button>
</div>
<p class="hw-hinweis" id="hw-betrag-info">Den Rest zahlen Sie bei Fertigstellung, Fördermittel gehen davon ab.</p>
</div>
<div class="hw-feld">
<div class="hw-feld-kopf"><label for="hw-laufzeit">Laufzeit</label><output for="hw-laufzeit" id="hw-laufzeit-aus">60 Monate</output></div>
<input class="hw-regler" id="hw-laufzeit" type="range" min="12" max="120" step="12" value="60" aria-valuetext="60 Monate">
<div class="hw-skala" aria-hidden="true"><span>12</span><span>36</span><span>60</span><span>84</span><span>120 Monate</span></div>
</div>
<table class="hw-kond" id="hw-kond">
<caption>Konditionen der Bank, Stand 03.11.2026</caption>
<thead><tr><th scope="col">Laufzeit</th><th scope="col">Sollzins gebunden</th><th scope="col">Effektiver Jahreszins</th></tr></thead>
<tbody></tbody>
</table>
</form>
<section class="hw-tafel" aria-labelledby="hw-tafel-titel" id="hw-anfrage">
<h3 class="label" id="hw-tafel-titel">Ihre Finanzierung</h3>
<div id="hw-fin"></div>
<div class="hw-teile" id="hw-teile">
<div class="hw-balken" aria-hidden="true"><span class="netto" id="hw-b-netto" style="flex-grow:18000"></span><span class="zins" id="hw-b-zins" style="flex-grow:2525"></span></div>
<p class="hw-legende"><span><i style="--c:#bdc1a9"></i>Darlehen <b id="hw-l-netto">18.000 €</b></span><span><i style="--c:#fb6100"></i>Zinsen <b id="hw-l-zins">2.524,74 €</b></span></p>
</div>
<a class="hw-signal" href="#hw-anfrage" id="hw-anfragen">Mit dieser Auswahl anfragen</a>
</section>
</div>
<p class="hw-recht"><span class="platzhalter-text">Vom Kunden prüfen lassen:</span> Konditionen, repräsentatives Beispiel und Kreditgeber kommen von der Bank. Ob der Betrieb Kredite vermitteln darf (§ 34c GewO), klärt der Kunde. Firma, Bank und Zahlen sind für diese Demo erfunden.</p>
</main>
</div>
<div class="zeiger" id="zeiger" aria-hidden="true"></div>
<div class="pult">
<span>Variante</span>
<div class="gruppe" role="group" aria-label="Variante der Demo">
<button type="button" data-v="fahrzeugkarte" aria-pressed="true">Fahrzeugkarte</button>
<button type="button" data-v="kaufbox" aria-pressed="false">Kaufbox</button>
<button type="button" data-v="rechner" aria-pressed="false">Rechner</button>
</div>
<span>Daten</span>
<div class="gruppe" role="group" aria-label="Datenlage">
<button type="button" id="p-fehlt" aria-pressed="false">Pflichtwerte fehlen</button>
</div>
<span class="werte" id="werte"></span>
</div>
<script>
(() => {
const html = document.documentElement;
const reduziert = () => html.classList.contains('reduziert');
const $ = (s, r = document) => r.querySelector(s);
const $$ = (s, r = document) => [...r.querySelectorAll(s)];
const zahl = (n, d = 0) => new Intl.NumberFormat('de-DE', { minimumFractionDigits: d, maximumFractionDigits: d }).format(n);
const euro = (n, d) => zahl(n, d ?? (Number.isInteger(n) ? 0 : 2)) + ' €';
const pz = n => zahl(n, 2) + ' %';
/* ── Rechnung: Annuität auf Cent, letzte Rate gleicht aus; effektiver Jahreszins ohne Nebenkosten ── */
function kredit(netto, soll, n) {
const r = soll / 100 / 12;
const R = Math.round((r === 0 ? netto / n : netto * r / (1 - Math.pow(1 + r, -n))) * 100);
let rest = Math.round(netto * 100), zinsen = 0;
for (let i = 1; i < n; i++) { const z = Math.round(rest * r); zinsen += z; rest += z - R; }
const zl = Math.round(rest * r); zinsen += zl;
const letzte = rest + zl;
return { rate: R / 100, letzte: letzte / 100, gesamt: (R * (n - 1) + letzte) / 100, zinsen: zinsen / 100, eff: Math.round((Math.pow(1 + r, 12) - 1) * 10000) / 100 };
}
/* Ein Finanzierungs-Objekt, wie es der Kreditgeber liefert (eff kommt aus seiner Datenquelle) */
const BANK = { kreditgeber: 'Leinetal Kreditbank AG', anschrift: 'Am Leineufer 12, 30159 Hannover', stand: '03.11.2026' };
function finanzierung(bar, anzahlung, soll, n, extra = {}) {
const netto = bar - anzahlung, k = kredit(netto, soll, n);
return { ...BANK, barzahlungspreis: bar, anzahlung, nettodarlehen: netto, sollzins: soll, effektivzins: k.eff, laufzeit_monate: n, rate: k.rate, letzte_rate: k.letzte, gesamtbetrag: k.gesamt, zinsen: k.zinsen, ...extra };
}
const PFLICHT = [['effektivzins', 'Effektiver Jahreszins'], ['sollzins', 'Sollzins'], ['nettodarlehen', 'Nettodarlehensbetrag'], ['laufzeit_monate', 'Laufzeit'], ['kreditgeber', 'Kreditgeber mit Anschrift']];
let FEHLT = false; // Schalter im Pult: so kommt der Datensatz, wenn die Bank noch nicht geliefert hat
const daten = f => FEHLT ? { ...f, effektivzins: null, kreditgeber: null } : f;
/* ── Der Baustein ── */
const plus = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 2v12M2 8h12"/></svg>';
function ratenText(f) {
return f.letzte_rate !== f.rate
? `${f.laufzeit_monate - 1} Monatsraten zu ${euro(f.rate, 2)} und eine letzte Rate zu ${euro(f.letzte_rate, 2)}`
: `${f.laufzeit_monate} Monatsraten zu ${euro(f.rate, 2)}`;
}
function beispielText(f, was) {
return `${was ? was + ', ' : ''}Barzahlungspreis ${euro(f.barzahlungspreis)}, Anzahlung ${euro(f.anzahlung)}, Nettodarlehensbetrag ${euro(f.nettodarlehen)}, Laufzeit ${f.laufzeit_monate} Monate, gebundener Sollzinssatz ${pz(f.sollzins)} p. a., effektiver Jahreszins ${pz(f.effektivzins)}, ${ratenText(f)}, Gesamtbetrag ${euro(f.gesamtbetrag, 2)}. Kreditgeber: ${f.kreditgeber}, ${f.anschrift}.`;
}
/* modus: kompakt (Teaser, Kaufbox) | voll (Detailseite) | rechner */
function block(roh, id, modus = 'kompakt', opt = {}) {
const f = daten(roh);
const fehlt = PFLICHT.filter(([k]) => f[k] == null || f[k] === '').map(([, n]) => n);
if (fehlt.length) {
return `<div class="fin-platz" role="group" aria-label="Finanzierung, Platzhalter">
<span class="marke">Platzhalter · nicht veröffentlichen</span>
<p><b>Vom Kunden prüfen lassen:</b> Für diese Finanzierung fehlen Pflichtangaben. Ohne sie zeigt die Seite keine Rate.</p>
<ul>${fehlt.map(n => `<li>${n} fehlt</li>`).join('')}</ul>
<p>Die Werte liefert der Kreditgeber. Bis dahin steht hier nur der Preis.</p>
</div>`;
}
const satz = `${euro(f.rate, 2)} monatlich, effektiver Jahreszins ${pz(f.effektivzins)}`;
const raten = `<div class="fin-zahlen">
<p class="fin-zahl"><b class="ziffern" data-rate>${euro(f.rate, 2)}</b><span>${opt.rateLabel || 'im Monat'}</span></p>
<p class="fin-zahl"><b class="ziffern" data-eff>${pz(f.effektivzins)}</b><span>effektiver Jahreszins</span></p>
</div>`;
const werte = modus === 'voll'
? [['Sollzins, gebunden', pz(f.sollzins) + ' p. a.'], ['Nettodarlehensbetrag', euro(f.nettodarlehen)], ['Laufzeit', f.laufzeit_monate + ' Monate'], ['Anzahlung', euro(f.anzahlung)],
['Barzahlungspreis', euro(f.barzahlungspreis)], ['Gesamtbetrag', euro(f.gesamtbetrag, 2)], ['Raten', ratenText(f), 'breit'], ['Kreditgeber', `${f.kreditgeber}, ${f.anschrift}`, 'breit']]
: [['Sollzins gebunden', pz(f.sollzins)], ['Nettodarlehen', euro(f.nettodarlehen)], ['Laufzeit', f.laufzeit_monate + ' Monate']];
const dl = `<dl class="fin-werte">${werte.map(([t, d, k]) => `<div${k ? ` class="${k}"` : ''}><dt>${t}</dt><dd>${d}</dd></div>`).join('')}</dl>`;
const warn = `<p class="fin-warn" role="note" id="${id}-warn"><i aria-hidden="true">!</i>Achtung! Kreditaufnahme kostet Geld.</p>`;
const bsp = modus === 'voll' ? '' : `<details class="fin-bsp"${opt.offen ? ' open' : ''}><summary><span>Finanzierungsbeispiel</span>${plus}</summary><div class="innen">
<p>${opt.beispiel || 'Repräsentatives Beispiel: ' + beispielText(f)}</p>
<p>Bonität vorausgesetzt. Kreditvermittlung durch ${opt.vermittler || 'den Händler'}, Konditionen Stand ${f.stand}.</p></div></details>`;
return `<div class="fin${modus === 'voll' ? ' fin-voll' : ''}" role="group" aria-labelledby="${id}-t" aria-describedby="${id}-warn">
<div class="fin-kopf"><p class="fin-titel" id="${id}-t">${opt.titel || 'Finanzierung'}</p>${modus === 'voll' ? `<span class="fin-stand">Repräsentatives Beispiel, Stand ${f.stand}</span>` : ''}</div>
<p class="nur-sr">${satz}.</p>
<div aria-hidden="true">${raten}</div>${modus === 'voll' ? warn + dl : dl + warn}${bsp}
</div>`;
}
/* ═══════════ Welt 1 · Fahrzeugkarte ═══════════ */
const KLASSEN = [['A', 0, '#17703f', '#fff'], ['B', 95, '#367d28', '#fff'], ['C', 115, '#9cbf2b', '#121314'], ['D', 135, '#f0cf1f', '#121314'], ['E', 155, '#f2a12e', '#121314'], ['F', 175, '#e0612a', '#121314'], ['G', Infinity, '#c62a26', '#fff']];
const klasse = g => KLASSEN.find(k => g <= k[1]);
const CO2 = 'CO<sub>2</sub>';
const AUTOS = [
{ nr: '26-0517', marke: 'Velio', modell: 'Arno Kombi', linie: '1.5 Hybrid Komfort', art: 'Neuwagen', ez: 'Neufahrzeug', km: 5, kw: 96, kraftstoff: 'Hybrid (Benzin/Elektro)', getriebe: 'Automatik', preis: 34290, l: 5.2, co2: 118, antrieb: 'v', farbe: '#2c4a6e', form: 'kombi', fin: finanzierung(34290, 6900, 4.49, 48) },
{ nr: '26-0509', marke: 'Velio', modell: 'Mira', linie: '1.0 Turbo Style', art: 'Neuwagen', ez: 'Neufahrzeug', km: 5, kw: 74, kraftstoff: 'Benzin', getriebe: 'Schaltgetriebe', preis: 21450, l: 4.9, co2: 112, antrieb: 'v', farbe: '#9b2c24', form: 'klein', fin: finanzierung(21450, 4300, 4.49, 48) },
{ nr: '26-0488', marke: 'Norra', modell: 'e4', linie: 'Long Range', art: 'Tageszulassung', ez: '10/2026', km: 12, kw: 150, kraftstoff: 'Elektro', getriebe: 'Automatik', preis: 38900, kwh: 16.4, co2: 0, antrieb: 'e', farbe: '#e4e4df', form: 'suv', fin: finanzierung(38900, 7800, 3.99, 60) },
];
const FARBNAME = { '#2c4a6e': 'Dunkelblau', '#9b2c24': 'Rot', '#e4e4df': 'Weiß' };
const nb = t => t.replace(/ (?=(l|kWh|g|km|kW|PS)\b)/g, ' ');
function pflichtzeile(a) {
const k = klasse(a.co2);
const kl = `<b class="kl" style="--k:${k[2]};--kt:${k[3]}">${k[0]}</b>`;
return a.antrieb === 'e'
? nb(`<span>Stromverbrauch kombiniert: <b>${zahl(a.kwh, 1)} kWh/100 km</b></span><span>${CO2}-Emissionen kombiniert: <b>${zahl(a.co2)} g/km</b></span><span>${CO2}-Klasse: ${kl}</span>`)
: nb(`<span>Kraftstoffverbrauch kombiniert: <b>${zahl(a.l, 1)} l/100 km</b></span><span>${CO2}-Emissionen kombiniert: <b>${zahl(a.co2)} g/km</b></span><span>${CO2}-Klasse: ${kl}</span>`);
}
/* Eigene Fahrzeugzeichnung (Seitenansicht, SVG) */
const mix = (hex, ziel, t) => { const a = hex.match(/\w\w/g).map(h => parseInt(h, 16)), b = ziel.match(/\w\w/g).map(h => parseInt(h, 16)); return '#' + a.map((v, i) => Math.round(v + (b[i] - v) * t).toString(16).padStart(2, '0')).join(''); };
const FORMEN = {
klein: { raeder: [182, 418], r: 40, koerper: 'M112,300 L112,240 C112,224 118,213 130,207 L160,158 C166,149 176,145 188,145 L318,145 C334,145 346,151 358,161 L398,199 C440,205 470,213 482,223 C490,231 492,247 490,263 L488,300 Z', glas: 'M170,163 C173,157 180,153 190,153 L312,153 C324,153 334,158 342,165 L374,197 L162,197 Z', saeule: 268, tuer: [206, 340], licht: 'M470,216 L486,222 L487,236 L474,232 Z', heck: 'M113,218 L124,214 L124,236 L113,238 Z' },
kombi: { raeder: [164, 438], r: 41, koerper: 'M82,300 L82,238 C82,222 88,212 100,207 L112,162 C116,152 124,147 136,147 L342,145 C360,145 374,151 388,163 L430,201 C476,207 506,215 516,227 C522,237 522,251 520,267 L518,300 Z', glas: 'M122,162 C124,156 128,154 136,154 L338,153 C352,153 364,159 374,167 L404,199 L118,199 Z', saeule: 272, saeule2: 176, tuer: [196, 352], licht: 'M500,218 L517,226 L518,240 L503,236 Z', heck: 'M83,214 L94,212 L94,240 L83,242 Z' },
suv: { raeder: [172, 432], r: 45, koerper: 'M92,300 L92,226 C92,206 98,194 112,188 L126,140 C130,128 140,122 154,122 L332,120 C352,120 366,128 380,140 L422,184 C472,190 502,200 510,212 C516,222 516,240 514,262 L512,300 Z', glas: 'M138,137 C140,131 146,129 154,129 L328,128 C344,128 356,134 366,144 L394,180 L132,180 Z', saeule: 262, saeule2: 188, tuer: [200, 336], licht: 'M494,203 L511,210 L512,226 L497,222 Z', heck: 'M93,200 L106,196 L106,226 L93,228 Z' },
};
function autoBild(a, id) {
const F = FORMEN[a.form], c = a.farbe, hell = mix(c, 'ffffff', .28), dunkel = mix(c, '000000', .35);
const rad = x => `<circle cx="${x}" cy="300" r="${F.r}" fill="#1b1c1e"/><circle cx="${x}" cy="300" r="${F.r * .62}" fill="url(#f${id})"/>`
+ [0, 72, 144, 216, 288].map(w => `<line x1="${x}" y1="300" x2="${x}" y2="${300 - F.r * .56}" stroke="#7d8389" stroke-width="5" stroke-linecap="round" transform="rotate(${w + (x % 37)} ${x} 300)"/>`).join('')
+ `<circle cx="${x}" cy="300" r="${F.r * .16}" fill="#5d6268"/>`;
const saeulen = [F.saeule, F.saeule2].filter(Boolean).map(x => `<rect x="${x - 6}" y="110" width="12" height="100" fill="${dunkel}" clip-path="url(#g${id})"/>`).join('');
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 400"><defs>`
+ `<linearGradient id="w${id}" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#e9e8e3"/><stop offset="1" stop-color="#d6d5cf"/></linearGradient>`
+ `<linearGradient id="b${id}" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#bebdb7"/><stop offset="1" stop-color="#a9a8a2"/></linearGradient>`
+ `<linearGradient id="l${id}" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="${hell}"/><stop offset=".42" stop-color="${c}"/><stop offset="1" stop-color="${dunkel}"/></linearGradient>`
+ `<linearGradient id="s${id}" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#3a4249"/><stop offset="1" stop-color="#15191c"/></linearGradient>`
+ `<radialGradient id="f${id}"><stop offset="0" stop-color="#e2e5e8"/><stop offset="1" stop-color="#9da3a9"/></radialGradient>`
+ `<clipPath id="g${id}"><path d="${F.glas}"/></clipPath><filter id="u${id}" x="-20%" y="-200%" width="140%" height="500%"><feGaussianBlur stdDeviation="7"/></filter></defs>`
+ `<rect width="600" height="230" fill="url(#w${id})"/><rect y="38" width="600" height="14" fill="#232629" opacity=".9"/>`
+ `<rect y="230" width="600" height="170" fill="url(#b${id})"/><path d="M36,400 L96,230" stroke="#fff" stroke-opacity=".5" stroke-width="5"/><path d="M564,400 L504,230" stroke="#fff" stroke-opacity=".5" stroke-width="5"/>`
+ `<ellipse cx="300" cy="342" rx="230" ry="13" fill="#000" opacity=".34" filter="url(#u${id})"/>`
+ `<g transform="translate(0,40)"><path d="${F.koerper}" fill="url(#l${id})"/><path d="${F.glas}" fill="url(#s${id})"/>${saeulen}`
+ `<path d="${F.glas}" fill="none" stroke="${dunkel}" stroke-width="3"/>`
+ F.tuer.map(x => `<line x1="${x}" y1="203" x2="${x - 4}" y2="282" stroke="${dunkel}" stroke-width="2"/>`).join('')
+ `<path d="M${F.raeder[0] - 60},212 L${F.raeder[1] + 60},210" stroke="#fff" stroke-opacity=".28" stroke-width="2"/>`
+ `<path d="${F.licht}" fill="#f4f1e6" stroke="${dunkel}" stroke-width="1.5"/><path d="${F.heck}" fill="#a3221c"/>${rad(F.raeder[0])}${rad(F.raeder[1])}</g></svg>`;
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
}
const kfzListe = $('#kfz-karten'), kfzDetail = $('#kfz-detail');
let detailNr = null;
function kfzKarten() {
kfzListe.innerHTML = AUTOS.map((a, i) => `<li><article class="kfz-karte" data-nr="${a.nr}" aria-labelledby="kfz-h-${i}">
<div class="kfz-bild"><img src="${autoBild(a, 'k' + i)}" alt="${a.marke} ${a.modell} in ${FARBNAME[a.farbe]}, Seitenansicht (Zeichnung)" width="600" height="400" loading="lazy" decoding="async"><span class="kfz-schild">${a.art}</span></div>
<div class="kfz-koerper">
<h3 id="kfz-h-${i}"><span class="kfz-modell">${a.marke} ${a.modell}</span><span class="kfz-linie">${a.linie}</span></h3>
<p class="kfz-preis"><b>${euro(a.preis)}</b><span>Hauspreis inkl. Überführung und MwSt.</span></p>
<div class="kfz-fin">${block(a.fin, 'kfz-f' + i, 'kompakt', { vermittler: 'Autohaus Rehkopf GmbH', beispiel: 'Repräsentatives Beispiel für dieses Fahrzeug: ' + beispielText(daten(a.fin)) })}</div>
<p class="kfz-pflicht">${pflichtzeile(a)}</p>
<button class="kfz-knopf" type="button" data-oeffne="${a.nr}">Fahrzeug und Finanzierung ansehen <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
</div></article></li>`).join('');
}
function kfzZeige(nr, fokus = true) {
detailNr = nr;
const a = AUTOS.find(x => x.nr === nr);
if (!a) { kfzListe.hidden = false; kfzDetail.hidden = true; kfzKarten(); return; }
kfzListe.hidden = true;
kfzDetail.hidden = false;
kfzDetail.innerHTML = `<div>
<div class="kfz-bild"><img src="${autoBild(a, 'd')}" alt="${a.marke} ${a.modell} in ${FARBNAME[a.farbe]}, Seitenansicht (Zeichnung)" width="600" height="400"><span class="kfz-schild">${a.art}</span></div>
<dl class="kfz-eck">${[['Erstzulassung', a.ez], ['Kilometer', zahl(a.km) + ' km'], ['Leistung', `${a.kw} kW (${Math.round(a.kw * 1.35962)} PS)`], ['Kraftstoff', a.kraftstoff], ['Getriebe', a.getriebe], ['Bestands-Nr.', a.nr]].map(([t, d]) => `<div><dt>${t}</dt><dd>${d}</dd></div>`).join('')}</dl>
<p class="kfz-pflicht">${pflichtzeile(a)}</p>
<button class="kfz-zurueck" type="button" data-zurueck><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19 12H5M11 6l-6 6 6 6"/></svg><span>Zurück zu allen Fahrzeugen</span></button>
</div>
<section class="kfz-seite" aria-labelledby="kfz-d-h">
<div><p class="label">${a.art} · ${a.nr}</p><h3 id="kfz-d-h" tabindex="-1">${a.marke} ${a.modell} <span class="kfz-linie">${a.linie}</span></h3></div>
<p class="kfz-preis"><b>${euro(a.preis)}</b><span>Hauspreis inkl. Überführung und MwSt.</span></p>
${block(a.fin, 'kfz-d', 'voll', { titel: 'Finanzierung für dieses Fahrzeug' })}
<div class="knoepfe"><a class="kfz-knopf voll" href="#kfz">Finanzierung anfragen</a><a class="kfz-knopf" href="#kfz">Probefahrt</a></div>
</section>`;
if (!reduziert()) { kfzDetail.classList.remove('kommt'); void kfzDetail.offsetWidth; kfzDetail.classList.add('kommt'); }
if (fokus) $('#kfz-d-h').focus({ preventScroll: true });
}
function kfzZurueck(fokus = true) {
const war = detailNr;
detailNr = null; kfzDetail.hidden = true; kfzListe.hidden = false;
if (fokus && war) $(`[data-oeffne="${war}"]`)?.focus({ preventScroll: true });
}
document.addEventListener('click', e => {
const o = e.target.closest('[data-oeffne]'); if (o) { kfzZeige(o.dataset.oeffne); return; }
if (e.target.closest('[data-zurueck]')) kfzZurueck();
});
/* ═══════════ Welt 2 · Kaufbox ═══════════ */
const PREISE = { eiche: { 160: 4280, 200: 4860 }, nuss: { 160: 4980, 200: 5640 } };
const HOLZ = { eiche: { name: 'Eiche geölt', c: '#c79c66', d: '#a87d4b', l: '#dcb98a' }, nuss: { name: 'Nussbaum geölt', c: '#6a4630', d: '#4d3121', l: '#8a6146' } };
const luxForm = $('.lux-box');
function luxWahl() { return { holz: $('input[name=holz]:checked').value, breite: +$('input[name=breite]:checked').value }; }
function luxBild(w) {
const H = HOLZ[w.holz], B = w.breite === 200 ? 520 : 420, x0 = (640 - B) / 2, top = 120, hoehe = 170, tueren = 4, tb = (B - 24) / tueren;
let s = `<defs><linearGradient id="lx-schatten" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#000" stop-opacity=".18"/><stop offset="1" stop-color="#000" stop-opacity="0"/></linearGradient>`
+ `<pattern id="lx-maser" width="7" height="170" patternUnits="userSpaceOnUse"><path d="M3 0 C5 40 1 90 4 170" stroke="#000" stroke-opacity=".06" fill="none"/></pattern></defs>`;
s += `<ellipse cx="320" cy="${top + hoehe + 52}" rx="${B / 2 + 30}" ry="10" fill="#000" opacity=".08"/>`;
s += `<rect x="${x0 + 30}" y="${top + hoehe}" width="${B - 60}" height="44" fill="${H.d}" class="lux-holz"/>`;
s += `<rect x="${x0 + 30}" y="${top + hoehe}" width="${B - 60}" height="10" fill="url(#lx-schatten)"/>`;
s += `<rect x="${x0}" y="${top}" width="${B}" height="${hoehe}" fill="${H.c}" class="lux-holz"/><rect x="${x0}" y="${top}" width="${B}" height="${hoehe}" fill="url(#lx-maser)"/>`;
s += `<rect x="${x0 - 6}" y="${top - 12}" width="${B + 12}" height="12" fill="${H.l}" class="lux-holz"/>`;
for (let i = 0; i < tueren; i++) {
const tx = x0 + 12 + i * tb;
s += `<rect x="${tx + 3}" y="${top + 12}" width="${tb - 6}" height="${hoehe - 24}" fill="none" stroke="#000" stroke-opacity=".14"/>`;
for (let j = 1; j < 6; j++) s += `<line x1="${tx + 3 + j * (tb - 6) / 6}" y1="${top + 18}" x2="${tx + 3 + j * (tb - 6) / 6}" y2="${top + hoehe - 18}" stroke="#000" stroke-opacity=".09" stroke-width="2"/>`;
s += `<rect x="${tx + (i % 2 ? 12 : tb - 18)}" y="${top + 74}" width="3" height="22" fill="#23282f" opacity=".55"/>`;
}
s += `<line x1="${x0 + 12}" y1="${top + 6}" x2="${x0 + B - 12}" y2="${top + 6}" stroke="#a88a5a" stroke-width="1"/>`;
return s;
}
function luxRender() {
const w = luxWahl(), preis = PREISE[w.holz][w.breite];
$('#lux-bild').innerHTML = luxBild(w);
$('#lux-bildtext').textContent = `Sideboard Lisene, ${HOLZ[w.holz].name}, ${w.breite} cm (Zeichnung)`;
$('[data-holz-name]').textContent = HOLZ[w.holz].name;
$('[data-breite-name]').textContent = w.breite + ' cm';
$('[data-lux-preis]').textContent = euro(preis);
const offen = $('#lux-fin .fin-bsp')?.open;
$('#lux-fin').innerHTML = block(finanzierung(preis, 0, 0, 24), 'lux-f', 'kompakt', { titel: 'Oder in 24 Monatsraten, 0 % Zinsen', offen, vermittler: 'Ahlgrimm Möbelwerkstätten GmbH' });
}
luxForm.addEventListener('change', luxRender);
$('#lux-kaufen').addEventListener('click', () => { const w = luxWahl(); $('#lux-status').textContent = `Lisene, ${HOLZ[w.holz].name}, ${w.breite} cm liegt im Warenkorb. Ratenzahlung wählen Sie an der Kasse.`; });
/* ═══════════ Welt 3 · Rechner ═══════════ */
/* Konditionstabelle der Bank: der Effektivzins kommt von hier, nie aus einer Annahme */
const KOND = [{ von: 12, bis: 36, soll: 4.79 }, { von: 48, bis: 72, soll: 5.29 }, { von: 84, bis: 120, soll: 5.69 }].map(k => ({ ...k, eff: kredit(10000, k.soll, 12).eff }));
const HW_BSP = finanzierung(15000, 0, 5.69, 84); // repräsentatives Beispiel, fest
const hwBetrag = $('#hw-betrag'), hwLauf = $('#hw-laufzeit');
const MIN = 5000, MAX = 30000;
$('#hw-kond tbody').innerHTML = KOND.map(k => `<tr data-von="${k.von}"><td>${k.von} bis ${k.bis} Monate</td><td>${pz(k.soll)}</td><td>${pz(k.eff)}</td></tr>`).join('');
const leseBetrag = () => { const n = parseInt(hwBetrag.value.replace(/\D/g, ''), 10); return Number.isFinite(n) ? n : 0; };
function hwRender(werteAus = true) {
let betrag = Math.min(MAX, Math.max(MIN, Math.round(leseBetrag() / 500) * 500));
if (werteAus) hwBetrag.value = zahl(betrag);
const n = +hwLauf.value, k = KOND.find(x => n >= x.von && n <= x.bis);
hwLauf.style.setProperty('--p', ((n - 12) / 108 * 100) + '%');
$('#hw-laufzeit-aus').textContent = n + ' Monate';
hwLauf.setAttribute('aria-valuetext', n + ' Monate, Sollzins ' + pz(k.soll));
$$('#hw-kond tr[data-von]').forEach(tr => tr.classList.toggle('aktiv', +tr.dataset.von === k.von));
const f = finanzierung(betrag, 0, k.soll, n);
const offen = $('#hw-fin .fin-bsp')?.open;
$('#hw-fin').innerHTML = block(f, 'hw-f', 'kompakt', { titel: `Ihre Auswahl: ${euro(betrag)} über ${n} Monate`, offen, vermittler: 'Tenhagen Heizung und Sanitär GmbH',
beispiel: 'Repräsentatives Beispiel (gilt für etwa zwei Drittel der Verträge, unabhängig von Ihrer Auswahl): ' + beispielText(daten(HW_BSP)) });
const zeigt = !FEHLT;
$('#hw-teile').hidden = !zeigt;
$('#hw-b-netto').style.flexGrow = betrag; $('#hw-b-zins').style.flexGrow = f.zinsen;
$('#hw-l-netto').textContent = euro(betrag); $('#hw-l-zins').textContent = euro(f.zinsen, 2);
hwAnsage(f);
}
let ansageT;
const ansage = document.createElement('p'); ansage.className = 'nur-sr'; ansage.setAttribute('aria-live', 'polite'); $('.hw-tafel').appendChild(ansage);
function hwAnsage(f) { clearTimeout(ansageT); ansageT = setTimeout(() => { ansage.textContent = FEHLT ? 'Konditionen fehlen, keine Rate.' : `${euro(f.rate, 2)} im Monat, effektiver Jahreszins ${pz(f.effektivzins)}. Achtung! Kreditaufnahme kostet Geld.`; }, 700); }
hwLauf.addEventListener('input', () => hwRender(false));
hwBetrag.addEventListener('change', () => hwRender());
hwBetrag.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); hwRender(); } });
$$('[data-schritt]').forEach(b => b.addEventListener('click', () => { hwBetrag.value = zahl(leseBetrag() + +b.dataset.schritt); hwRender(); }));
$('#hw-form').addEventListener('submit', e => e.preventDefault());
/* ═══════════ Varianten, Pult ═══════════ */
const VARIANTEN = ['fahrzeugkarte', 'kaufbox', 'rechner'];
const WERTE = {
fahrzeugkarte: 'Rate = Effektivzins 1,75 rem · auf 250 / zu 170 ms',
kaufbox: 'Rate = Effektivzins 1,5 rem · auf 300 / zu 200 ms',
rechner: 'Rate = Effektivzins bis 3 rem · auf 280 / zu 180 ms',
};
let V = 'fahrzeugkarte';
function alles() { kfzKarten(); if (detailNr) kfzZeige(detailNr, false); luxRender(); hwRender(); }
function setzeV(v, url = true) {
V = VARIANTEN.includes(v) ? v : VARIANTEN[0];
document.body.dataset.v = V;
$$('[data-welt]').forEach(w => { w.hidden = w.dataset.welt !== V; });
$$('.pult [data-v]').forEach(b => b.setAttribute('aria-pressed', b.dataset.v === V));
$('#werte').textContent = WERTE[V];
if (url) { const q = new URLSearchParams(location.search); q.set('v', V); history.replaceState(null, '', '?' + q.toString() + location.hash); }
}
function setzeFehlt(an) {
FEHLT = an;
$('#p-fehlt').setAttribute('aria-pressed', an);
$('[data-bank-name]').textContent = an ? 'noch nicht hinterlegt' : BANK.kreditgeber;
alles();
}
$$('.pult [data-v]').forEach(b => b.addEventListener('click', () => { pause(); setzeV(b.dataset.v); }));
$('#p-fehlt').addEventListener('click', () => { pause(); setzeFehlt(!FEHLT); });
/* ═══════════ Vorführung ═══════════ */
const zeiger = $('#zeiger');
let timer = [], laeuft = false;
const nach = (ms, fn) => timer.push(setTimeout(fn, ms));
function zeigeAuf(el, druck) {
if (!el) return;
const r = el.getBoundingClientRect();
zeiger.style.transform = `translate(${r.left + Math.min(r.width / 2, 120)}px, ${r.top + r.height / 2}px)`;
if (druck) nach(reduziert() ? 50 : 650, () => { zeiger.classList.add('drueckt'); setTimeout(() => zeiger.classList.remove('drueckt'), 160); });
}
const sicht = el => el && el.scrollIntoView({ block: 'center', behavior: 'instant' });
function oeffne(det, auf) { if (det) det.open = auf; }
function lauf() {
timer.forEach(clearTimeout); timer = [];
if (FEHLT) setzeFehlt(false);
if (V === 'fahrzeugkarte') {
kfzZurueck(false); kfzKarten(); scrollTo(0, 0);
nach(300, () => zeigeAuf($$('.kfz-karte .fin-bsp summary')[1], true));
nach(1000, () => oeffne($$('.kfz-karte .fin-bsp')[1], true));
nach(1400, () => { const d = $$('.kfz-karte .fin-bsp')[1]; if (d) { d.scrollIntoView({ block: 'nearest', behavior: reduziert() ? 'instant' : 'smooth' }); } });
nach(2800, () => { oeffne($$('.kfz-karte .fin-bsp')[1], false); scrollTo({ top: 0, behavior: reduziert() ? 'instant' : 'smooth' }); });
nach(3100, () => zeigeAuf($$('[data-oeffne]')[1], true));
nach(3800, () => { kfzZeige(AUTOS[1].nr, false); zeigeAuf($('.kfz-seite .fin-warn')); });
nach(6400, () => zeigeAuf($('[data-zurueck]'), true));
nach(7100, () => { kfzZurueck(false); setzeFehlt(true); zeigeAuf($('.kfz-karte .fin-platz')); });
nach(9200, () => setzeFehlt(false));
nach(9800, lauf);
} else if (V === 'kaufbox') {
scrollTo(0, 0);
$('input[name=holz][value=eiche]').checked = true; $('input[name=breite][value="200"]').checked = true; luxRender();
nach(300, () => zeigeAuf($('input[name=holz][value=nuss]').closest('label'), true));
nach(1000, () => { $('input[name=holz][value=nuss]').checked = true; luxRender(); zeigeAuf($('#lux-fin [data-rate]')); });
nach(2200, () => zeigeAuf($('input[name=breite][value="160"]').closest('label'), true));
nach(2900, () => { $('input[name=breite][value="160"]').checked = true; luxRender(); zeigeAuf($('#lux-fin [data-eff]')); });
nach(4000, () => { sicht($('#lux-fin')); zeigeAuf($('#lux-fin summary'), true); });
nach(4700, () => oeffne($('#lux-fin .fin-bsp'), true));
nach(7000, () => oeffne($('#lux-fin .fin-bsp'), false));
nach(7400, () => { setzeFehlt(true); zeigeAuf($('#lux-fin .fin-platz')); });
nach(9400, () => setzeFehlt(false));
nach(10000, lauf);
} else {
scrollTo(0, 0);
hwBetrag.value = '18.000'; hwLauf.value = 24; hwRender();
const stufen = [24, 36, 48, 60, 72, 84, 96, 108, 120];
nach(200, () => zeigeAuf(hwLauf));
stufen.forEach((s, i) => nach(600 + i * 420, () => { hwLauf.value = s; hwRender(false); const r = hwLauf.getBoundingClientRect(); zeiger.style.transform = `translate(${r.left + (s - 12) / 108 * r.width}px, ${r.top + r.height / 2}px)`; }));
nach(4700, () => zeigeAuf($('[data-schritt="500"]'), true));
nach(5300, () => { hwBetrag.value = zahl(leseBetrag() + 2000); hwRender(); zeigeAuf($('#hw-fin [data-rate]')); });
nach(6300, () => { sicht($('#hw-fin summary')); zeigeAuf($('#hw-fin summary'), true); });
nach(7000, () => oeffne($('#hw-fin .fin-bsp'), true));
nach(9000, () => oeffne($('#hw-fin .fin-bsp'), false));
nach(9400, () => { setzeFehlt(true); zeigeAuf($('#hw-fin .fin-platz')); });
nach(11200, () => setzeFehlt(false));
nach(11800, lauf);
}
}
function neustart() {
timer.forEach(clearTimeout); timer = [];
laeuft = false; html.classList.remove('vorfuehrung');
setzeFehlt(false); kfzZurueck(false);
$('input[name=holz][value=eiche]').checked = true; $('input[name=breite][value="200"]').checked = true;
hwBetrag.value = '18.000'; hwLauf.value = 60;
alles(); scrollTo(0, 0);
}
function vorfuehren() { laeuft = true; html.classList.add('vorfuehrung'); lauf(); }
function pause() { timer.forEach(clearTimeout); timer = []; if (laeuft) { laeuft = false; html.classList.remove('vorfuehrung'); } }
function weiter() { vorfuehren(); }
setzeV(new URLSearchParams(location.search).get('v'), false);
alles();
})();
</script>
</body>
</html>