Zum Inhalt
← Sektionen & Funktionen

Reparatur-Richtpreise und Reparaturweg

Richtpreise typischer Reparaturen je Modellreihe als echte Tabelle (mobil Kartenliste), Ersatzteile mit Teilenummer, der Satz zur Verlängerung als Platzhalter und ein kurzer Auftrag mit Seriennummer; für Betriebe das Europäische Formular für Reparaturinformationen mit 30 Tagen Bindung und Druckansicht.

Hersteller erfasster Gerätegruppen (Waschmaschine, Geschirrspüler, Kühlgerät, Smartphone, E-Bike-Akku): Pflichtseite mit Richtpreisen je Modellreihe.

Eine Seite „Reparatur“ zeigt typische Reparaturen eines Geräts als ruhige Tabelle je Modellreihe: was, Richtpreis, Dauer, wo (Werkstatt, vor Ort, Einsenden). Darunter steht, wo es Ersatzteile und Werkzeug gibt und dass die Gewährleistung bei Reparatur statt Ersatz um zwölf Monate länger läuft. Ein kurzer Auftrag mit Seriennummer schließt ab. Reparaturbetriebe geben dazu freiwillig das „Europäische Formular für Reparaturinformationen“ aus, 30 Tage bindend.

Reparierbarkeit wird zum Verkaufsargument, wenn sie konkret ist: Preise statt „Service wird bei uns großgeschrieben“. Für Hersteller der betroffenen Produktgruppen ist die Seite seit Juli 2026 Pflicht.

Wann einsetzen

Hersteller (oder Importeure) der von der Richtlinie erfassten Gerätegruppen, etwa Waschmaschinen, Geschirrspüler, Kühlgeräte, Smartphones, E-Bike-Akkus. Freiwillig auch für Manufakturen und Reparaturbetriebe, die mit Reparierbarkeit werben.

Wann nicht

Nicht für Kfz-Werkstätten (Autos stehen nicht auf der Produktliste) und nicht als Pflicht für normale Händler oder Handwerker. Keine erfundenen Richtpreise: ohne echte Zahlen des Herstellers kein Muster.

Varianten

  • hersteller: Richtpreistabelle je Modellreihe mit Tabellenziffern, Ersatzteile und Werkzeug, Satz zur Verlängerung der Gewährleistung als Platzhalter, kurzer Reparaturauftrag mit Seriennummer; mobil eine Kartenliste
  • betrieb: Reparaturangebot als „Europäisches Formular für Reparaturinformationen“, 30 Tage bindend (freiwillig), mit Druck- bzw. PDF-Ansicht über @media print
  • kurz (nicht in der Demo): ein Abschnitt in technische-daten-produktseite statt eigener Seite

Umsetzung

Echte Tabelle je Modellreihe mit caption und th scope: Spalten Reparatur, Richtpreis (Tabellenziffern, „ab“ bzw. Spanne, inkl. MwSt.), Dauer, Ort; Stand-Datum unter der Tabelle. Modellreihen als Reiter oder Auswahl, die nur die Tabelle wechselt. Unter 640 px wird jede Zeile zur Karte (display: grid je tr, Spaltenkopf über data-label), damit nichts waagerecht scrollt. Abschnitt Ersatzteile und Werkzeug mit Bestellweg. Der Satz zur Verlängerung der Gewährleistung um zwölf Monate steht als markierter Platzhalter „vom Kunden prüfen lassen“. Reparaturauftrag als kurzes Formular (kontakt-anfrage-formular) mit Seriennummer, Fehlerbild und Wunschweg; Seriennummer mit Muster und Hilfe, wo sie steht. Betriebs-Variante: das Formular als HTML-Seite mit Angebotsnummer, Ausstellungs- und Gültigkeitsdatum (+30 Tage aus Intl.DateTimeFormat) und einer Druckansicht per @media print und window.print(); als PDF im download-center-dokumente ablegen. Ohne JS bleibt alles statisch lesbar.

Robust umsetzen

Telefon. Tabelle als Liste von Karten je Reparatur, Preis rechts oben. Auftrag einspaltig, Seriennummer mit inputmode und Hilfe zum Typenschild.

Ohne Bewegung. Keine Bewegung, Reiter wechseln ohne Übergang.

Barrierefrei. Echte Tabelle mit th scope; Preise in Text, nicht als Bild. 320 px: Kartenliste statt waagerechtem Scrollen. Kontrast 4,5 : 1. Formularfehler am Feld mit aria-describedby. Druckansicht ohne Navigation, mit allen Angaben als Text.

Code

<!DOCTYPE html>
<!--
  Reparatur-Richtpreise und Reparaturweg
  Fundus · Sektionen & Funktionen · https://cecodesign.de/lab/fundus/sektionen/reparatur-richtpreise/
  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>Reparatur-Richtpreise und Reparaturweg · 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: 'Strichpunkt Sans'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/strichpunkt-sans:vf@latest/latin-full-normal.woff2') format('woff'); font-weight: 400 900; font-stretch: 100% 200%; font-display: swap; }
@font-face { font-family: 'Archivo'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/archivo:vf@latest/latin-full-normal.woff2') format('woff2-variations'); font-weight: 100 900; font-stretch: 62% 125%; font-display: swap; }
@font-face { font-family: '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; }

/* Muster ohne Bewegung. UI-Rückmeldungen 150 ms linear (Farbe), Fokus ohne Übergang. */
:root { --t-ui: 150ms; }
* { 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, textarea { 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; }
.platzhalter-text { background: #fff3c4; color: #111; border-radius: 2px; padding: 0 4px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }

/* ═══════════ Welt 1 · Hersteller (industrie-b2b-mittelstand) ═══════════ */
.ind { --grund: #f3f3f0; --text: #111417; --text-2: #4e555b; --flaeche: #e4e5e1; --dunkel: #16202a; --signal: #0e518d; --linie: #cfcfcd; --raster: rgba(17, 20, 23, .13); --fehler: #b3261e;
  background: var(--grund); color: var(--text); font-family: 'Archivo', system-ui, sans-serif; font-size: 1.0625rem; line-height: 1.55; min-height: 100svh; }
.ind :focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
.ind .code { font: 600 .75rem/1.2 'Archivo', sans-serif; font-stretch: 75%; letter-spacing: .08em; text-transform: uppercase; color: var(--text-2); }
.ind-kopf { background: var(--dunkel); color: #fff; }
.ind-kopf .innen { max-width: 1440px; margin: 0 auto; padding: 14px clamp(16px, 3vw, 40px); display: flex; align-items: center; gap: 12px 32px; flex-wrap: wrap; }
.ind-logo { display: grid; gap: 4px; text-decoration: none; margin-right: auto; line-height: 1; }
.ind-logo b { font: 600 1.375rem/1 'Strichpunkt Sans', sans-serif; font-stretch: 140%; letter-spacing: .02em; }
.ind-logo span { font: 600 .6875rem/1 'Archivo', sans-serif; font-stretch: 75%; letter-spacing: .1em; text-transform: uppercase; color: #b8c0c8; }
.ind-kopf nav ul { display: flex; gap: 26px; list-style: none; padding: 0; flex-wrap: wrap; }
.ind-kopf nav a { display: inline-block; padding: 10px 0; text-decoration: none; font-size: .9375rem; border-bottom: 2px solid transparent; }
.ind-kopf nav a[aria-current] { border-color: #fff; }
.ind-kopf nav a:hover { border-color: #8a96a2; }
.ind-kopf :focus-visible { outline-color: #fff; }
.ind-knopf { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 18px; border: 0; background: var(--signal); color: #fff; font-weight: 600; font-size: .9375rem; text-decoration: none; transition: background-color var(--t-ui) linear; }
.ind-knopf:hover { background: #0b4274; }
.ind-knopf svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; }
.ind-main { max-width: 1440px; margin: 0 auto; padding: 0 clamp(16px, 3vw, 40px) clamp(56px, 7vw, 112px); }
/* sichtbares Raster: Zellkanten und Plus-Marker an den Schnittpunkten (blueprint-raster-sichtbar) */
.zelle { position: relative; border-left: 1px solid var(--raster); border-right: 1px solid var(--raster); border-bottom: 1px solid var(--raster); padding: clamp(20px, 3vw, 40px) clamp(16px, 2vw, 24px); }
.zelle::before, .zelle::after { content: '+'; position: absolute; bottom: -.72em; font: 400 14px/1 'Archivo', sans-serif; color: var(--text-2); }
.zelle::before { left: -.33em; }
.zelle::after { right: -.33em; }
.ind-held { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 24px 48px; align-items: end; }
.ind-held h1 { font: 600 clamp(2.25rem, 1.4rem + 3.4vw, 4.25rem)/.98 'Strichpunkt Sans', sans-serif; font-stretch: 116%; letter-spacing: -.03em; margin-top: 12px; text-wrap: balance; }
.ind-held .lead { max-width: 52ch; text-wrap: pretty; }
.kennwerte { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 28px; border-top: 1px solid var(--raster); }
.kennwerte div { padding: 14px 16px 0 0; }
.kennwerte div + div { padding-left: 16px; border-left: 1px solid var(--raster); }
.kennwerte b { display: block; font: 600 1.375rem/1.1 'Archivo', sans-serif; font-stretch: 87%; font-variant-numeric: tabular-nums; }
.kennwerte span { display: block; margin-top: 4px; font-size: .875rem; line-height: 1.4; color: var(--text-2); }
.abschnitt-kopf { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: end; gap: 12px 32px; margin-bottom: 20px; }
.abschnitt-kopf h2 { font: 600 clamp(1.625rem, 1.2rem + 1.6vw, 2.5rem)/1.05 'Strichpunkt Sans', sans-serif; font-stretch: 110%; letter-spacing: -.02em; margin-top: 8px; }
.reiter { display: flex; flex-wrap: wrap; gap: 0; border: 1px solid var(--text); }
.reiter button { min-height: 44px; padding: 6px 18px; border: 0; background: transparent; text-align: left; line-height: 1.2; transition: background-color var(--t-ui) linear, color var(--t-ui) linear; }
.reiter button + button { border-left: 1px solid var(--text); }
.reiter button b { display: block; font-weight: 600; font-size: .9375rem; }
.reiter button span { display: block; font-size: .75rem; font-stretch: 87%; color: var(--text-2); }
.reiter button[aria-selected="true"] { background: var(--text); color: #fff; }
.reiter button[aria-selected="true"] span { color: #c9cfd5; }
.reiter button:hover:not([aria-selected="true"]) { background: var(--flaeche); }
.preise { width: 100%; border-collapse: collapse; font-size: .9375rem; font-stretch: 87%; }
.preise caption { text-align: left; font-weight: 600; font-stretch: 100%; padding-bottom: 12px; font-size: 1rem; }
.preise th, .preise td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--linie); vertical-align: top; }
.preise thead th { background: var(--flaeche); font: 600 .75rem/1.2 'Archivo', sans-serif; font-stretch: 75%; letter-spacing: .08em; text-transform: uppercase; color: var(--text-2); }
.preise td.preis, .preise th.preis { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.preise td.preis { font-weight: 600; font-size: 1.0625rem; }
.preise tbody th { font-weight: 600; }
.preise tbody th small { display: block; font-weight: 400; color: var(--text-2); font-size: .875rem; }
.preise tbody tr { transition: background-color var(--t-ui) linear; }
.preise tbody tr.zeigt { background: #dfe7ef; }
.preise .entfaellt { color: var(--text-2); font-weight: 400; font-size: .9375rem; }
.ort { display: inline-flex; align-items: center; gap: 6px; }
.ort svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; flex: none; }
.unter-tabelle { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px 40px; margin-top: 16px; font-size: .9375rem; color: var(--text-2); }
.verlaengerung { border-left: 3px solid var(--text); padding: 4px 0 4px 14px; color: var(--text); }
.teile { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); border-top: 1px solid var(--raster); border-left: 1px solid var(--raster); }
.teil { display: grid; gap: 6px; align-content: start; padding: 16px; border-right: 1px solid var(--raster); border-bottom: 1px solid var(--raster); }
.teil b { font-weight: 600; }
.teil .nr { font-size: .875rem; font-stretch: 87%; color: var(--text-2); font-variant-numeric: tabular-nums; user-select: all; }
.teil .p { font-weight: 600; font-variant-numeric: tabular-nums; }
.teil p { font-size: .875rem; color: var(--text-2); }
.lieferbar { margin-top: 16px; font-size: .9375rem; }
.lieferbar b { font-variant-numeric: tabular-nums; }
.auftrag { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 24px 48px; align-items: start; }
.formular { display: grid; gap: 18px; }
.feld { display: grid; gap: 6px; }
.feld label, .feld legend { font-weight: 600; font-size: .9375rem; padding: 0; }
.feld .hilfe { font-size: .875rem; color: var(--text-2); }
.feld input, .feld select, .feld textarea { min-height: 48px; padding: 10px 12px; border: 1px solid var(--text); border-radius: 0; background: #fff; font-size: 1rem; }
.feld textarea { min-height: 96px; resize: vertical; }
.feld input:focus-visible, .feld select:focus-visible, .feld textarea:focus-visible { outline-offset: 0; }
.feld[data-fehler] input, .feld[data-fehler] select { border-color: var(--fehler); border-width: 2px; }
.feld .fehler { display: none; color: var(--fehler); font-size: .875rem; font-weight: 600; }
.feld[data-fehler] .fehler { display: block; }
.zwei { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; }
fieldset.feld { border: 0; padding: 0; margin: 0; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chips label { position: relative; display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px; border: 1px solid var(--linie); background: #fff; font-weight: 500; font-size: .9375rem; cursor: pointer; }
.chips input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.chips label:has(input:checked) { border-color: var(--signal); box-shadow: inset 0 0 0 1px var(--signal); background: #e6eef6; }
.chips label:has(input:focus-visible) { outline: 2px solid var(--signal); outline-offset: 2px; }
.richtwert { font-size: .9375rem; padding: 12px 14px; background: var(--flaeche); }
.richtwert b { font-variant-numeric: tabular-nums; }
.typenschild { background: #fff; border: 1px solid var(--raster); padding: 18px; display: grid; gap: 12px; }
.typenschild svg { width: 100%; max-width: 330px; height: auto; justify-self: center; }
.typenschild p { font-size: .9375rem; }
.erfolg { border: 2px solid var(--signal); background: #fff; padding: 20px; display: grid; gap: 10px; }
.erfolg h3 { font: 600 1.375rem/1.15 'Strichpunkt Sans', sans-serif; font-stretch: 110%; }
.erfolg dl { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 16px; font-size: .9375rem; }
.erfolg dt { color: var(--text-2); }
.erfolg dd { font-weight: 600; font-variant-numeric: tabular-nums; }
.ind-zweit { justify-self: start; display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px; border: 1px solid var(--text); background: transparent; font-weight: 600; font-size: .9375rem; }
.ind-recht { margin-top: 24px; font-size: .875rem; color: var(--text-2); max-width: 90ch; }

/* ═══════════ Welt 2 · Betrieb (handwerk-werkstatt-klartext) ═══════════ */
.hw { --grund: #efedea; --zement: #d3cdc2; --holz: #c3b4a5; --salbei: #bdc1a9; --text: #1a1a1a; --orange: #fb6100; --leise: #5f5e5d; --linie: #cdcbc9;
  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 :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, 64px) clamp(16px, 4vw, 48px) clamp(48px, 7vw, 96px); display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: clamp(20px, 3vw, 48px); align-items: start; }
.hw-seite { display: grid; gap: 20px; position: sticky; top: 16px; }
.hw-seite h1 { font: 700 clamp(2rem, 1.4rem + 2.4vw, 3rem)/1.05 'Special Gothic', sans-serif; letter-spacing: -.01em; margin-top: 8px; text-wrap: balance; }
.hw-wahl { border: 0; padding: 0; display: grid; gap: 8px; }
.hw-wahl legend { font-weight: 700; margin-bottom: 8px; padding: 0; }
.hw-wahl label { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 2px 12px; align-items: center; padding: 12px 14px; border-radius: 6px; background: #fff; border: 1px solid var(--zement); cursor: pointer; transition: border-color var(--t-ui) linear; }
.hw-wahl label:hover { border-color: var(--text); }
.hw-wahl input { width: 20px; height: 20px; margin: 0; accent-color: var(--text); grid-row: span 2; }
.hw-wahl b { font-size: 1rem; }
.hw-wahl span { font-size: .875rem; color: var(--leise); }
.hw-wahl label:has(input:checked) { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }
.hw-info { font-size: .9375rem; display: grid; gap: 8px; }
.hw-info li { margin-left: 1.1em; }
/* Das Formular als Blatt */
.blatt-rahmen { display: grid; gap: 14px; }
.blatt-werkzeug { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.hw-zweit { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px; border: 1px solid var(--text); border-radius: 4px; background: #fff; font-weight: 700; font-size: .9375rem; transition: background-color var(--t-ui) linear; }
.hw-zweit:hover, .hw-zweit[aria-pressed="true"] { background: var(--zement); }
.hw-zweit svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.blatt { background: #fff; color: #1a1a1a; border: 1px solid var(--zement); box-shadow: 0 1px 0 var(--zement), 0 18px 40px -28px rgba(0, 0, 0, .35); padding: clamp(18px, 3vw, 40px); display: grid; gap: 18px; font-size: .9375rem; }
.blatt-kopf { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px 24px; align-items: start; padding-bottom: 14px; border-bottom: 2px solid #1a1a1a; }
.blatt-kopf h2 { font: 700 clamp(1.25rem, 1rem + 1vw, 1.625rem)/1.15 'Special Gothic', sans-serif; }
.blatt-kopf p { font-size: .875rem; color: var(--leise); margin-top: 4px; }
.blatt-nr { text-align: right; font-size: .875rem; display: grid; gap: 2px; }
.blatt-nr b { font-variant-numeric: tabular-nums; font-size: 1rem; }
.gueltig { display: flex; flex-wrap: wrap; gap: 6px 20px; align-items: baseline; padding: 10px 14px; background: var(--salbei); border-radius: 4px; }
.gueltig b { font: 700 1.125rem/1.2 'Special Gothic', sans-serif; font-variant-numeric: tabular-nums; }
.blatt section { display: grid; grid-template-columns: 2.25rem minmax(0, 1fr); gap: 4px 12px; padding-top: 12px; border-top: 1px solid var(--linie); }
.blatt section > .nr { font: 700 .875rem/1.5 'Special Gothic', sans-serif; font-variant-numeric: tabular-nums; color: var(--leise); }
.blatt h3 { font-size: .9375rem; font-weight: 700; }
.blatt dl { grid-column: 2; display: grid; grid-template-columns: 10.5rem minmax(0, 1fr); gap: 4px 16px; }
.blatt dt { color: var(--leise); }
.blatt dd { font-weight: 600; }
.blatt dd.betrag { font: 700 1.25rem/1.2 'Special Gothic', sans-serif; font-variant-numeric: tabular-nums; }
.blatt .unterschrift { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; padding-top: 28px; font-size: .8125rem; color: var(--leise); }
.blatt .unterschrift span { border-top: 1px solid #1a1a1a; padding-top: 6px; }
.blatt .fuss { font-size: .8125rem; color: var(--leise); }
.hw-status { font-size: .9375rem; min-height: 1.4em; }
/* Druck bzw. PDF: nur das Blatt, schwarz auf weiß. .druckansicht zeigt dasselbe am Bildschirm. */
@media print {
  @page { size: A4; margin: 16mm 14mm; }
  body { background: #fff !important; }
  .hw-kopf, .hw-seite, .blatt-werkzeug, .hw-status, .hw-recht, .pult, fundus-pult-knopf, .zeiger, [data-welt="hersteller"] { display: none !important; }
  .hw { background: #fff; }
  .hw-main { display: block; padding: 0; max-width: none; }
  .blatt { border: 0; box-shadow: none; padding: 0; font-size: 10.5pt; }
  .gueltig { background: none; border: 1.5pt solid #000; }
  .blatt section { break-inside: avoid; }
}
.druckansicht .hw-kopf, .druckansicht .hw-seite, .druckansicht .hw-recht { display: none; }
.druckansicht .hw-main { grid-template-columns: minmax(0, 1fr); max-width: 860px; }
.druckansicht .blatt { box-shadow: 0 0 0 1px #bbb, 0 30px 60px -30px rgba(0, 0, 0, .4); }
.druckansicht .gueltig { background: none; border: 1.5px solid #1a1a1a; }
.druckansicht { background: #8e8b86; }
.druckansicht .blatt-werkzeug { justify-content: flex-end; }
.druckansicht .blatt-werkzeug .hw-zweit { background: #fff; }
.hw-recht { grid-column: 1 / -1; font-size: .875rem; color: var(--leise); max-width: 90ch; }

/* Gedachter Zeiger */
.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 #111; background: rgba(14, 81, 141, .35); pointer-events: none; opacity: 0; transition: transform 600ms cubic-bezier(0.25, 1, 0.5, 1), opacity 200ms linear; }
.vorfuehrung .zeiger { opacity: 1; }
.zeiger.drueckt { scale: .8; }

/* Pult */
.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: 1000px) {
  .teile { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .auftrag, .ind-held { grid-template-columns: minmax(0, 1fr); }
  .hw-main { grid-template-columns: minmax(0, 1fr); }
  .hw-seite { position: static; }
}
/* Mobil: Tabelle wird Kartenliste (kein waagerechtes Scrollen) */
@media (max-width: 640px) {
  .ind-kopf nav { display: none; }
  .kennwerte { grid-template-columns: minmax(0, 1fr); }
  .kennwerte div + div { padding-left: 0; border-left: 0; border-top: 1px solid var(--raster); }
  .kennwerte div { padding: 12px 0; }
  .preise, .preise tbody, .preise tr, .preise th, .preise td { display: block; }
  .preise caption { display: block; }
  .preise thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .preise tbody { display: grid; gap: 10px; }
  .preise tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; padding: 14px; background: #fff; border: 1px solid var(--raster); }
  .preise tbody th, .preise td { border: 0; padding: 0; }
  .preise tbody th { grid-column: 1; grid-row: 1; }
  .preise td.preis { grid-column: 2; grid-row: 1; text-align: right; }
  .preise td[data-label] { grid-column: 1 / -1; font-size: .875rem; color: var(--text-2); }
  .preise td[data-label]::before { content: attr(data-label) ': '; font-weight: 600; color: var(--text); }
  .preise td.preis::before { content: none; }
  .unter-tabelle { grid-template-columns: minmax(0, 1fr); }
  .teile { grid-template-columns: minmax(0, 1fr); }
  .zwei { grid-template-columns: minmax(0, 1fr); }
  .reiter { width: 100%; }
  .reiter button { flex: 1 1 0; min-width: 0; padding: 6px 10px; }
  .hw-tel .nummer { display: none; }
  .hw-tel { width: 44px; justify-content: center; border: 2px solid currentColor; border-radius: 8px; }
  .blatt-kopf { grid-template-columns: minmax(0, 1fr); }
  .blatt-nr { text-align: left; }
  .blatt dl { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .blatt dd { margin-bottom: 6px; }
  .blatt section { grid-template-columns: minmax(0, 1fr); }
  .blatt dl { grid-column: 1; }
  .pult { bottom: 12px; right: 12px; }
}
@media (max-width: 360px) {
  .reiter button { padding: 6px 6px; }
  .reiter button span { display: none; }
  .blatt .unterschrift { grid-template-columns: minmax(0, 1fr); }
}

/* Vorschau 960 × 720 */
.vorschau body { overflow: hidden; }
.vorschau .pult, .vorschau fundus-pult-knopf, .vorschau .ind-held .lead, .vorschau .kennwerte, .vorschau .ind-recht, .vorschau .hw-recht, .vorschau .hw-info, .vorschau .hw-kopf { display: none !important; }
.vorschau .ind-held h1 { font-size: 2.5rem; }
.vorschau .zelle { padding-top: 22px; padding-bottom: 22px; }
.vorschau .hw-main { padding-top: 24px; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); }

:root.reduziert *, :root.reduziert *::before, :root.reduziert *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; }
:root.reduziert .zeiger { transition: opacity 150ms linear !important; }
@media (hover: none) {
  .reiter button:hover:not([aria-selected="true"]) { background: transparent; }
  .hw-wahl label:hover { border-color: var(--zement); }
  .hw-zweit:hover { background: #fff; }
}
@media (forced-colors: active) {
  button, [role=button], [role=tab], [role=switch], input, select, textarea { border: 1px solid ButtonText !important; }
  .reiter button[aria-selected="true"] { outline: 2px solid Highlight; }
  .chips label:has(input:checked), .hw-wahl label:has(input:checked) { outline: 2px solid Highlight; }
}
</style>
</head>
<body data-v="hersteller">

<!-- ═══ Welt 1 · Steinhage Hausgeräte, Rheine ═══ -->
<div class="welt ind" data-welt="hersteller">
  <header class="ind-kopf">
    <div class="innen">
      <a class="ind-logo" href="#ind" aria-label="Steinhage Hausgeräte, Startseite"><b>STEINHAGE</b><span>Hausgeräte · Rheine</span></a>
      <nav aria-label="Hauptmenü Hersteller"><ul>
        <li><a href="#ind">Waschmaschinen</a></li>
        <li><a href="#ind">Trockner</a></li>
        <li><a href="#ind" aria-current="page">Reparatur</a></li>
        <li><a href="#ind-teile">Ersatzteile</a></li>
        <li><a href="#ind">Kontakt</a></li>
      </ul></nav>
      <a class="ind-knopf" href="#ind-auftrag">Reparatur beauftragen <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></a>
    </div>
  </header>
  <main class="ind-main" id="ind">
    <section class="zelle" aria-labelledby="ind-h1">
      <div class="ind-held">
        <div>
          <p class="code">Service · Reparatur · Waschmaschinen</p>
          <h1 id="ind-h1">Reparieren statt ersetzen. Mit Richtpreis vorab.</h1>
        </div>
        <p class="lead">Was eine typische Reparatur an Ihrer Steinhage kostet, steht hier je Modellreihe. Den verbindlichen Preis nennt Ihnen unser Techniker vor Beginn der Arbeit.</p>
      </div>
      <div class="kennwerte">
        <div><b>3 Werktage</b><span>bis zum Termin vor Ort, Richtwert im Umkreis von 80 km um Rheine</span></div>
        <div><b>inkl. Anfahrt</b><span>Richtpreise mit Ersatzteil, Arbeitszeit, Anfahrt bis 30 km und 19 % MwSt.</span></div>
        <div><b>Teile einzeln</b><span>Ersatzteile und Werkzeug auch für freie Werkstätten und zum Selbermachen</span></div>
      </div>
    </section>

    <section class="zelle" aria-labelledby="ind-h2" id="ind-preise">
      <div class="abschnitt-kopf">
        <div><p class="code">01 · Richtpreise</p><h2 id="ind-h2">Typische Reparaturen je Modellreihe</h2></div>
        <div class="reiter" role="tablist" aria-label="Modellreihe">
          <button type="button" role="tab" id="tab-w600" aria-controls="ind-tabelle" aria-selected="false" tabindex="-1" data-reihe="w600"><b>W 600</b><span>Baujahre 2016 bis 2020</span></button>
          <button type="button" role="tab" id="tab-w700" aria-controls="ind-tabelle" aria-selected="true" tabindex="0" data-reihe="w700"><b>W 700</b><span>Baujahre 2020 bis 2024</span></button>
          <button type="button" role="tab" id="tab-w900" aria-controls="ind-tabelle" aria-selected="false" tabindex="-1" data-reihe="w900"><b>W 900</b><span>seit 2024</span></button>
        </div>
      </div>
      <div role="tabpanel" id="ind-tabelle" aria-labelledby="tab-w700" tabindex="0">
        <table class="preise">
          <caption id="ind-caption">Richtpreise Waschmaschinen Reihe W 700, Stand 01.10.2026</caption>
          <thead><tr><th scope="col">Reparatur</th><th scope="col" class="preis">Richtpreis</th><th scope="col">Dauer</th><th scope="col">Ort</th></tr></thead>
          <tbody id="ind-zeilen"></tbody>
        </table>
      </div>
      <div class="unter-tabelle">
        <p>Richtpreise inkl. Ersatzteil, Arbeitszeit, Anfahrt bis 30 km und 19 % MwSt. Spannen ergeben sich aus den Ausführungen der Reihe. Den verbindlichen Preis nennen wir vor der Reparatur.</p>
        <p class="verlaengerung"><span class="platzhalter-text">Vom Kunden prüfen lassen:</span> Lassen Sie ein Gerät während der Gewährleistung reparieren statt ersetzen, verlängert sich die Gewährleistung um zwölf Monate.</p>
      </div>
    </section>

    <section class="zelle" aria-labelledby="ind-h3" id="ind-teile">
      <div class="abschnitt-kopf"><div><p class="code">02 · Ersatzteile und Werkzeug</p><h2 id="ind-h3">Selbst reparieren oder reparieren lassen</h2></div></div>
      <div class="teile" id="ind-teileliste"></div>
      <p class="lieferbar">Ersatzteile lieferbar bis mindestens: <span id="ind-lieferbar"></span>. Bestellung im <a href="#ind-teile">Teileshop</a> oder telefonisch unter <a href="tel:+495971840220">05971 8402-20</a>.</p>
    </section>

    <section class="zelle" aria-labelledby="ind-h4" id="ind-auftrag">
      <div class="abschnitt-kopf"><div><p class="code">03 · Reparaturauftrag</p><h2 id="ind-h4">Techniker anfragen</h2></div></div>
      <div class="auftrag">
        <form class="formular" id="ind-form" novalidate>
          <div class="zwei">
            <div class="feld"><label for="f-reihe">Modellreihe</label>
              <select id="f-reihe"><option value="w600">W 600 (2016 bis 2020)</option><option value="w700" selected>W 700 (2020 bis 2024)</option><option value="w900">W 900 (seit 2024)</option></select></div>
            <div class="feld" id="feld-sn"><label for="f-sn">Seriennummer</label>
              <input id="f-sn" inputmode="numeric" autocomplete="off" placeholder="z. B. 724 081 139" aria-describedby="f-sn-hilfe f-sn-fehler">
              <p class="hilfe" id="f-sn-hilfe">9 Ziffern, auf dem Typenschild im Türrahmen</p>
              <p class="fehler" id="f-sn-fehler">Bitte die 9 Ziffern vom Typenschild eingeben.</p></div>
          </div>
          <fieldset class="feld" id="feld-fehler" aria-describedby="f-fehler-fehler">
            <legend>Was ist los?</legend>
            <div class="chips" id="f-chips"></div>
            <p class="fehler" id="f-fehler-fehler">Bitte wählen Sie ein Fehlerbild oder „Etwas anderes“.</p>
          </fieldset>
          <p class="richtwert" id="f-richtwert" role="status" hidden></p>
          <div class="feld"><label for="f-text">Beschreibung <span class="hilfe">(freiwillig)</span></label><textarea id="f-text" placeholder="Seit wann, welche Fehlermeldung, Geräusche …"></textarea></div>
          <div class="zwei">
            <div class="feld" id="feld-plz"><label for="f-plz">Postleitzahl des Aufstellorts</label><input id="f-plz" inputmode="numeric" autocomplete="postal-code" maxlength="5" aria-describedby="f-plz-fehler"><p class="fehler" id="f-plz-fehler">Bitte eine fünfstellige Postleitzahl eingeben.</p></div>
            <div class="feld" id="feld-tel"><label for="f-tel">Telefon für die Terminabsprache</label><input id="f-tel" type="tel" autocomplete="tel" aria-describedby="f-tel-fehler"><p class="fehler" id="f-tel-fehler">Bitte eine Telefonnummer angeben.</p></div>
          </div>
          <button class="ind-knopf" type="submit" style="justify-self:start">Reparatur anfragen <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4"/></svg></button>
        </form>
        <aside class="typenschild" aria-labelledby="ts-h" id="ind-seite">
          <p class="code" id="ts-h">Wo steht die Seriennummer?</p>
          <svg viewBox="0 0 330 300" aria-hidden="true"><rect x="40" y="10" width="200" height="280" fill="#e4e5e1" stroke="#111417" stroke-width="2"/><rect x="40" y="10" width="200" height="46" fill="#d5d7d2" stroke="#111417" stroke-width="2"/><rect x="56" y="24" width="60" height="16" fill="#fff" stroke="#111417"/><circle cx="214" cy="33" r="10" fill="#fff" stroke="#111417" stroke-width="2"/><circle cx="140" cy="168" r="78" fill="#fff" stroke="#111417" stroke-width="2"/><circle cx="140" cy="168" r="58" fill="#cfd8e0" stroke="#111417" stroke-width="2"/><rect x="62" y="134" width="22" height="34" fill="#fff" stroke="#0e518d" stroke-width="3"/><path d="M84 151 H244" stroke="#0e518d" stroke-width="2" stroke-dasharray="4 3"/><text x="248" y="146" font-family="Archivo, sans-serif" font-size="12" font-weight="600" fill="#0e518d">Typenschild</text><text x="248" y="162" font-family="Archivo, sans-serif" font-size="12" fill="#111417">im Türrahmen</text></svg>
          <p>Tür öffnen, links im Rahmen sitzt ein weißes Schild. Die Seriennummer steht unter „S-Nr.“, die Modellreihe unter „Typ“.</p>
        </aside>
        <div class="erfolg" id="ind-erfolg" hidden tabindex="-1" role="status">
          <h3>Anfrage eingegangen</h3>
          <dl id="ind-erfolg-daten"></dl>
          <p>Wir rufen Sie innerhalb eines Werktags an und nennen einen Termin. Vor Beginn der Reparatur bekommen Sie den verbindlichen Preis.</p>
          <button class="ind-zweit" type="button" id="ind-neu">Neue Anfrage</button>
        </div>
      </div>
      <p class="ind-recht"><span class="platzhalter-text">Vom Kunden prüfen lassen:</span> Pflichtangaben nach §§ 479a ff. BGB und der Richtlinie (EU) 2024/1799, Produktgruppen, Formulierung der Verlängerung. Hersteller, Modelle und Preise sind für diese Demo erfunden; im Einsatz liefert der Hersteller echte Richtpreise.</p>
    </section>
  </main>
</div>

<!-- ═══ Welt 2 · Gerätewerkstatt Kollwitz, Wittmund ═══ -->
<div class="welt hw" data-welt="betrieb" hidden>
  <header class="hw-kopf">
    <a class="hw-marke" href="#hw" aria-label="Gerätewerkstatt Kollwitz, Startseite"><b>Kollwitz</b><span>Gerätewerkstatt · Wittmund</span></a>
    <a class="hw-tel" href="tel:+494462912340"><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">04462 91 23 40</span><span class="nur-sr">Anrufen: 04462 91 23 40</span></a>
    <a class="hw-signal" href="#hw">Reparatur anfragen</a>
  </header>
  <main class="hw-main" id="hw">
    <aside class="hw-seite">
      <div>
        <p class="label">Kostenvoranschlag</p>
        <h1>Ihr Angebot, 30 Tage fest.</h1>
      </div>
      <ul class="hw-info">
        <li>Wir schreiben unser Angebot auf das Europäische Formular für Reparaturinformationen. Das ist freiwillig und macht Angebote vergleichbar.</li>
        <li>Preis und Bedingungen gelten 30 Tage ab Ausstellung.</li>
        <li>Drucken oder als PDF speichern, dann annehmen per Telefon, E-Mail oder im Laden.</li>
      </ul>
      <fieldset class="hw-wahl">
        <legend>Beispiel ansehen</legend>
        <label><input type="radio" name="geraet" value="wm" checked><b>Waschmaschine</b><span>Laugenpumpe defekt</span></label>
        <label><input type="radio" name="geraet" value="gs"><b>Geschirrspüler</b><span>heizt nicht mehr</span></label>
        <label><input type="radio" name="geraet" value="kt"><b>Kühlschrank</b><span>Türdichtung undicht</span></label>
      </fieldset>
    </aside>
    <div class="blatt-rahmen">
      <div class="blatt-werkzeug">
        <button class="hw-zweit" type="button" id="hw-druckansicht" aria-pressed="false"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3h9l3 3v15H6z M15 3v3h3"/></svg>Druckansicht</button>
        <button class="hw-zweit" type="button" id="hw-drucken"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 9V3h10v6M7 17H4v-7h16v7h-3M7 14h10v7H7z"/></svg>Drucken oder als PDF</button>
        <a class="hw-signal" href="#hw" id="hw-annehmen">Angebot annehmen</a>
      </div>
      <article class="blatt" id="blatt" aria-labelledby="blatt-h"></article>
      <p class="hw-status" role="status" id="hw-status"></p>
    </div>
    <p class="hw-recht"><span class="platzhalter-text">Vom Kunden prüfen lassen:</span> Aufbau und Pflichtinhalte des Formulars nach Richtlinie (EU) 2024/1799 (Anhang I), Bindefrist und Preisangaben. Betrieb, Kundschaft und Preise 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="hersteller" aria-pressed="true">Hersteller</button>
    <button type="button" data-v="betrieb" aria-pressed="false">Betrieb</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 euro = n => new Intl.NumberFormat('de-DE', { maximumFractionDigits: 0 }).format(n) + ' €';
  const spanne = p => p == null ? null : (p[0] === p[1] ? euro(p[0]) : `${euro(p[0])} bis ${euro(p[1])}`);

  /* ═══════════ Hersteller: Richtpreise je Reihe (Beispielwerte der Demo) ═══════════ */
  const ORT = {
    vorort: ['vor Ort', '<path d="M8 1.5c-2.8 0-5 2.1-5 4.8C3 10 8 14.5 8 14.5s5-4.5 5-8.2c0-2.7-2.2-4.8-5-4.8z"/><circle cx="8" cy="6.3" r="1.8"/>'],
    werkstatt: ['Werkstatt Rheine, mit Abholung', '<path d="M1.5 14.5V6L8 2l6.5 4v8.5M5 14.5v-5h6v5"/>'],
  };
  const REPARATUREN = [
    { id: 'pumpe', name: 'Laugenpumpe tauschen', hinweis: 'Gerät pumpt nicht ab', dauer: 'etwa 1 Stunde', ort: 'vorort', p: { w600: [149, 169], w700: [149, 179], w900: [169, 189] } },
    { id: 'manschette', name: 'Türmanschette tauschen', hinweis: 'Wasser läuft vorn aus', dauer: 'etwa 1 Stunde', ort: 'vorort', p: { w600: [129, 149], w700: [139, 159], w900: [149, 169] } },
    { id: 'schloss', name: 'Türverriegelung tauschen', hinweis: 'Tür geht nicht auf oder zu', dauer: 'etwa 45 Minuten', ort: 'vorort', p: { w600: [99, 99], w700: [109, 109], w900: [119, 119] } },
    { id: 'heizung', name: 'Heizstab mit Sensor tauschen', hinweis: 'Wäsche bleibt kalt', dauer: 'etwa 1 Stunde', ort: 'vorort', p: { w600: [159, 179], w700: [169, 189], w900: [179, 199] } },
    { id: 'daempfer', name: 'Stoßdämpfer tauschen (Paar)', hinweis: 'Gerät wandert beim Schleudern', dauer: 'etwa 1 Stunde', ort: 'vorort', p: { w600: [129, 129], w700: [139, 139], w900: [149, 149] } },
    { id: 'kohle', name: 'Kohlebürsten am Motor tauschen', hinweis: 'Trommel dreht nicht', dauer: 'etwa 45 Minuten', ort: 'vorort', p: { w600: [119, 119], w700: [119, 119], w900: null }, entfaellt: 'entfällt: bürstenloser Motor' },
    { id: 'elektronik', name: 'Steuerelektronik tauschen', hinweis: 'Anzeige tot oder Fehlercode E3x', dauer: '1 bis 2 Stunden', ort: 'vorort', p: { w600: [249, 299], w700: [259, 319], w900: [289, 349] } },
    { id: 'lager', name: 'Trommellager und Wellendichtung', hinweis: 'lautes Mahlen beim Schleudern', dauer: '3 Werktage', ort: 'werkstatt', p: { w600: [389, 429], w700: [399, 449], w900: [419, 469] } },
  ];
  const TEILE = {
    w600: [['Türmanschette W 600', '61-114-07', 39.9, 'Werkzeug: Federzange, Schraubendreher'], ['Laugenpumpe W 600', '61-208-03', 54.5, 'Werkzeug: Torx T20'], ['Kohlebürsten-Satz', '61-330-01', 24.9, 'Werkzeug: Torx T25'], ['Stoßdämpfer, Paar', '61-410-02', 46.0, 'Werkzeug: Steckschlüssel 13 mm']],
    w700: [['Türmanschette W 700', '70-114-02', 42.9, 'Werkzeug: Federzange, Schraubendreher'], ['Laugenpumpe W 700', '70-208-05', 56.5, 'Werkzeug: Torx T20'], ['Heizstab mit Sensor', '70-512-01', 61.0, 'Werkzeug: Steckschlüssel 8 mm'], ['Stoßdämpfer, Paar', '70-410-01', 48.0, 'Werkzeug: Steckschlüssel 13 mm']],
    w900: [['Türmanschette W 900', '90-114-01', 46.9, 'Werkzeug: Federzange, Schraubendreher'], ['Laugenpumpe W 900', '90-208-02', 64.0, 'Werkzeug: Torx T20'], ['Heizstab mit Sensor', '90-512-03', 66.0, 'Werkzeug: Steckschlüssel 8 mm'], ['Türverriegelung', '90-260-01', 38.5, 'Werkzeug: Torx T15']],
  };
  const REIHEN = { w600: 'W 600', w700: 'W 700', w900: 'W 900' };
  const LIEFERBAR = 'W 600 bis 2030, W 700 bis 2034, W 900 bis 2038';
  let reihe = 'w700';

  function zeichneTabelle() {
    $('#ind-caption').textContent = `Richtpreise Waschmaschinen Reihe ${REIHEN[reihe]}, Stand 01.10.2026`;
    $('#ind-zeilen').innerHTML = REPARATUREN.map(r => {
      const p = spanne(r.p[reihe]), o = ORT[r.ort];
      return `<tr data-rep="${r.id}"><th scope="row">${r.name}<small>${r.hinweis}</small></th>`
        + (p ? `<td class="preis">${p.includes('bis') ? p : 'ab ' + p}</td>` : `<td class="preis entfaellt">${r.entfaellt}</td>`)
        + `<td data-label="Dauer">${p ? r.dauer : 'nicht nötig'}</td><td data-label="Ort"><span class="ort"><svg viewBox="0 0 16 16" aria-hidden="true">${o[1]}</svg>${p ? o[0] : 'nicht nötig'}</span></td></tr>`;
    }).join('');
    $('#ind-teileliste').innerHTML = TEILE[reihe].map(([n, nr, preis, wz]) => `<div class="teil"><b>${n}</b><span class="nr">Teil-Nr. ${nr}</span><span class="p">${new Intl.NumberFormat('de-DE', { minimumFractionDigits: 2 }).format(preis)} €</span><p>${wz}</p></div>`).join('');
    $('#ind-lieferbar').textContent = LIEFERBAR;
    $$('[role=tab]').forEach(t => { const an = t.dataset.reihe === reihe; t.setAttribute('aria-selected', an); t.tabIndex = an ? 0 : -1; });
    $('#ind-tabelle').setAttribute('aria-labelledby', 'tab-' + reihe);
    zeichneChips();
  }
  function setzeReihe(r, fokus) {
    reihe = r;
    $('#f-reihe').value = r;
    zeichneTabelle();
    if (fokus) $('#tab-' + r).focus();
  }
  $$('[role=tab]').forEach(t => {
    t.addEventListener('click', () => setzeReihe(t.dataset.reihe));
    t.addEventListener('keydown', e => {
      const ids = Object.keys(REIHEN), i = ids.indexOf(reihe);
      const ziel = { ArrowRight: ids[(i + 1) % 3], ArrowLeft: ids[(i + 2) % 3], Home: ids[0], End: ids[2] }[e.key];
      if (ziel) { e.preventDefault(); setzeReihe(ziel, true); }
    });
  });

  /* Auftrag */
  function zeichneChips() {
    const alt = $('input[name=fehlerbild]:checked')?.value;
    $('#f-chips').innerHTML = REPARATUREN.filter(r => r.p[reihe]).map(r => `<label><input type="radio" name="fehlerbild" value="${r.id}"${alt === r.id ? ' checked' : ''}>${r.hinweis}</label>`).join('')
      + `<label><input type="radio" name="fehlerbild" value="anderes"${alt === 'anderes' ? ' checked' : ''}>Etwas anderes</label>`;
    richtwert();
  }
  function richtwert() {
    const v = $('input[name=fehlerbild]:checked')?.value, el = $('#f-richtwert');
    const r = REPARATUREN.find(x => x.id === v);
    $$('#ind-zeilen tr').forEach(tr => tr.classList.toggle('zeigt', tr.dataset.rep === v));
    if (!v) { el.hidden = true; return; }
    el.hidden = false;
    el.innerHTML = r ? `Richtpreis laut Tabelle für ${REIHEN[reihe]}: <b>${spanne(r.p[reihe])}</b> (${r.name}, ${ORT[r.ort][0]})` : 'Wir schauen es uns an. Die Fehlersuche vor Ort kostet pauschal <b>89 €</b> und wird bei Reparatur angerechnet.';
    if (v !== 'anderes') { $('#feld-fehler').removeAttribute('data-fehler'); }
  }
  $('#f-reihe').addEventListener('change', e => setzeReihe(e.target.value));
  $('#f-chips').addEventListener('change', () => { $('#feld-fehler').removeAttribute('data-fehler'); richtwert(); });
  const sn = $('#f-sn');
  const snOk = () => /^\d{9}$/.test(sn.value.replace(/\s/g, ''));
  sn.addEventListener('blur', () => { if (sn.value) markiere('feld-sn', !snOk()); });
  sn.addEventListener('input', () => { const d = sn.value.replace(/\D/g, '').slice(0, 9); sn.value = d.replace(/(\d{3})(?=\d)/g, '$1 '); if (snOk()) markiere('feld-sn', false); });
  function markiere(id, fehler) {
    const f = $('#' + id); if (fehler) f.setAttribute('data-fehler', ''); else f.removeAttribute('data-fehler');
    const i = f.querySelector('input, select'); if (i) i.setAttribute('aria-invalid', fehler);
  }
  $('#ind-form').addEventListener('submit', e => {
    e.preventDefault();
    const fehler = [];
    if (!snOk()) fehler.push('feld-sn');
    if (!$('input[name=fehlerbild]:checked')) fehler.push('feld-fehler');
    if (!/^\d{5}$/.test($('#f-plz').value.trim())) fehler.push('feld-plz');
    if ($('#f-tel').value.replace(/\D/g, '').length < 6) fehler.push('feld-tel');
    ['feld-sn', 'feld-fehler', 'feld-plz', 'feld-tel'].forEach(id => markiere(id, fehler.includes(id)));
    if (fehler.length) { const f = $('#' + fehler[0]); (f.querySelector('input') || f).focus(); return; }
    const v = $('input[name=fehlerbild]:checked').value, r = REPARATUREN.find(x => x.id === v);
    $('#ind-erfolg-daten').innerHTML = [['Anfrage', 'R-2611-0417'], ['Gerät', `Steinhage ${REIHEN[reihe]}, S-Nr. ${sn.value}`], ['Fehlerbild', r ? r.hinweis : 'Etwas anderes'], ['Richtpreis', r ? spanne(r.p[reihe]) : 'Fehlersuche 89 €'], ['Rückruf', 'innerhalb eines Werktags']].map(([t, d]) => `<dt>${t}</dt><dd>${d}</dd>`).join('');
    $('#ind-form').hidden = true; $('#ind-seite').hidden = true; $('#ind-erfolg').hidden = false;
    $('#ind-erfolg').focus({ preventScroll: true });
  });
  function auftragZurueck() {
    $('#ind-form').reset(); $('#ind-form').hidden = false; $('#ind-seite').hidden = false; $('#ind-erfolg').hidden = true;
    ['feld-sn', 'feld-fehler', 'feld-plz', 'feld-tel'].forEach(id => markiere(id, false));
    $('#f-reihe').value = reihe; zeichneChips();
  }
  $('#ind-neu').addEventListener('click', () => { auftragZurueck(); sn.focus(); });

  /* ═══════════ Betrieb: Europäisches Formular für Reparaturinformationen ═══════════ */
  const datum = d => new Intl.DateTimeFormat('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }).format(d);
  const BEISPIELE = {
    wm: { geraet: 'Waschmaschine, Frontlader', marke: 'Steinhage', modell: 'W 700 (Typ W 7.14)', sn: '724 081 139', mangel_kunde: 'Pumpt nicht ab, Wasser bleibt in der Trommel.', mangel: 'Laugenpumpe blockiert, Lagerschaden an der Pumpe.', reparatur: 'Laugenpumpe tauschen, Flusensieb reinigen, Dichtheit prüfen.', preis: 164, preisart: 'Festpreis', dauer: '1 Werktag nach Teileeingang, Teil auf Lager', ersatz: 'nicht nötig', ort: 'Bei Ihnen zu Hause (Aufstellort)', extra: 'Anfahrt im Umkreis von 25 km enthalten' },
    gs: { geraet: 'Geschirrspüler, vollintegriert', marke: 'Kalmar', modell: 'GS 60 i', sn: '19-3305-778', mangel_kunde: 'Geschirr wird nicht warm, Programm läuft sehr lange.', mangel: 'Heizpumpe ohne Funktion, Fehlerspeicher meldet Heizung.', reparatur: 'Heizpumpe tauschen, Fehlerspeicher löschen, Probelauf.', preis: 238, preisart: 'Festpreis', dauer: '3 Werktage, Teil wird bestellt', ersatz: 'nicht verfügbar', ort: 'Bei Ihnen zu Hause (Einbauort)', extra: 'Aus- und Einbau aus der Küchenzeile enthalten' },
    kt: { geraet: 'Kühl-Gefrier-Kombination', marke: 'Nordlicht', modell: 'KG 3410', sn: 'NL 88 1204 5', mangel_kunde: 'Tür schließt nicht dicht, Eis im Gefrierfach.', mangel: 'Türdichtung am Kühlteil eingerissen, Scharnier verzogen.', reparatur: 'Türdichtung tauschen, Scharnier nachstellen.', preis: 129, preisart: 'Höchstpreis (Berechnung nach Aufwand: 68 € je Stunde plus Teil 41 €)', dauer: '1 Werktag', ersatz: 'Kühlbox leihweise, kostenlos', ort: 'Bei Ihnen zu Hause (Aufstellort)', extra: 'Altteil nehmen wir mit' },
  };
  function zeichneBlatt() {
    const b = BEISPIELE[$('input[name=geraet]:checked').value];
    const heute = new Date(), bis = new Date(heute); bis.setDate(bis.getDate() + 30);
    const nr = 'KV-' + heute.getFullYear() + '-' + ({ wm: '0388', gs: '0391', kt: '0394' })[$('input[name=geraet]:checked').value];
    const sek = (n, titel, zeilen) => `<section aria-labelledby="bl-${n}"><span class="nr">${String(n).padStart(2, '0')}</span><h3 id="bl-${n}">${titel}</h3><dl>${zeilen.map(([t, d, k]) => `<dt>${t}</dt><dd${k ? ` class="${k}"` : ''}>${d}</dd>`).join('')}</dl></section>`;
    $('#blatt').innerHTML = `<header class="blatt-kopf"><div><h2 id="blatt-h">Europäisches Formular für Reparaturinformationen</h2><p>Angebot zur Reparatur, freiwillig nach Richtlinie (EU) 2024/1799</p></div>
        <p class="blatt-nr"><span>Angebot</span><b>${nr}</b><span>ausgestellt am ${datum(heute)}</span></p></header>
      <p class="gueltig"><span>Dieses Angebot gilt 30 Tage, bis einschließlich</span><b>${datum(bis)}</b></p>`
      + sek(1, 'Reparaturbetrieb', [['Name', 'Gerätewerkstatt Kollwitz, Inh. Jana Kollwitz'], ['Anschrift', 'Am Markt 7, 26409 Wittmund'], ['Telefon', '04462 91 23 40'], ['E-Mail', 'werkstatt@kollwitz.example']])
      + sek(2, 'Gerät', [['Art', b.geraet], ['Marke und Modell', `${b.marke} ${b.modell}`], ['Seriennummer', b.sn]])
      + sek(3, 'Mangel', [['Beschreibung Kunde', b.mangel_kunde], ['Befund Werkstatt', b.mangel]])
      + sek(4, 'Reparatur', [['Art der Reparatur', b.reparatur], ['Preis inkl. MwSt.', euro(b.preis), 'betrag'], ['Preisart', b.preisart]])
      + sek(5, 'Ablauf', [['Voraussichtliche Dauer', b.dauer], ['Ersatzgerät', b.ersatz], ['Ort der Übergabe', b.ort], ['Weitere Leistungen', b.extra]])
      + `<div class="unterschrift"><span>Ort, Datum, Unterschrift Werkstatt</span><span>Angebot angenommen am, Unterschrift Kunde</span></div>
      <p class="fuss">Wir sind an dieses Angebot bis zum genannten Datum gebunden. <span class="platzhalter-text">Vom Kunden prüfen lassen.</span></p>`;
  }
  $$('input[name=geraet]').forEach(i => i.addEventListener('change', zeichneBlatt));
  const druckKnopf = $('#hw-druckansicht');
  function druckansicht(an) { html.classList.toggle('druckansicht', an); druckKnopf.setAttribute('aria-pressed', an); }
  druckKnopf.addEventListener('click', () => druckansicht(druckKnopf.getAttribute('aria-pressed') !== 'true'));
  $('#hw-drucken').addEventListener('click', () => window.print());
  $('#hw-annehmen').addEventListener('click', e => { e.preventDefault(); $('#hw-status').textContent = 'Danke. Rufen Sie uns an oder antworten Sie auf die E-Mail mit dem Angebot, dann vereinbaren wir den Termin.'; });

  /* ═══════════ Varianten ═══════════ */
  const VARIANTEN = ['hersteller', 'betrieb'];
  const WERTE = {
    hersteller: 'ohne Bewegung · ≤ 640 px Karten',
    betrieb: 'ohne Bewegung · Frist + 30 Tage · A4',
  };
  let V = 'hersteller';
  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 (V !== 'betrieb') druckansicht(false);
    if (url) { const q = new URLSearchParams(location.search); q.set('v', V); history.replaceState(null, '', '?' + q.toString() + location.hash); }
  }
  $$('.pult [data-v]').forEach(b => b.addEventListener('click', () => { pause(); setzeV(b.dataset.v); }));

  /* ═══════════ 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 : 600, () => { zeiger.classList.add('drueckt'); setTimeout(() => zeiger.classList.remove('drueckt'), 160); });
  }
  const sicht = (el, block = 'start') => el && el.scrollIntoView({ block, behavior: 'instant' });
  function tippe(el, text, start, schritt = 70) { el.value = ''; [...text].forEach((c, i) => nach(start + i * schritt, () => { el.value += c; el.dispatchEvent(new Event('input')); })); return start + text.length * schritt; }
  function lauf() {
    timer.forEach(clearTimeout); timer = [];
    if (V === 'hersteller') {
      auftragZurueck(); setzeReihe('w600'); sicht($('#ind-preise'));
      nach(300, () => zeigeAuf($('#tab-w700'), true));
      nach(900, () => setzeReihe('w700'));
      nach(1500, () => zeigeAuf($('#tab-w900'), true));
      nach(2100, () => { setzeReihe('w900'); zeigeAuf($('#ind-zeilen tr[data-rep="kohle"] .preis')); });
      nach(3300, () => { setzeReihe('w700'); zeigeAuf($('#ind-zeilen tr[data-rep="pumpe"] .preis')); });
      nach(4300, () => { sicht($('#ind-auftrag')); zeigeAuf(sn); });
      const t = tippe(sn, '724081139', 4700, 60);
      nach(t + 200, () => { const c = $('input[name=fehlerbild][value="pumpe"]'); zeigeAuf(c.closest('label'), true); });
      nach(t + 800, () => { $('input[name=fehlerbild][value="pumpe"]').checked = true; richtwert(); zeigeAuf($('#f-richtwert')); });
      nach(t + 1800, () => { $('#f-plz').value = '48431'; $('#f-tel').value = '05971 123456'; zeigeAuf($('#ind-form [type=submit]'), true); });
      nach(t + 2400, () => { $('#ind-form').requestSubmit(); sicht($('#ind-auftrag')); zeigeAuf($('#ind-erfolg h3')); });
      nach(t + 4800, lauf);
    } else {
      druckansicht(false); scrollTo(0, 0);
      $('input[name=geraet][value=wm]').checked = true; zeichneBlatt();
      nach(300, () => zeigeAuf($('.gueltig b')));
      nach(1500, () => zeigeAuf($('input[name=geraet][value=gs]').closest('label'), true));
      nach(2100, () => { $('input[name=geraet][value=gs]').checked = true; zeichneBlatt(); const z = $('#blatt dd.betrag'); z.scrollIntoView({ block: 'center', behavior: 'instant' }); zeigeAuf(z); });
      nach(3300, () => zeigeAuf($('input[name=geraet][value=kt]').closest('label'), true));
      nach(3900, () => { $('input[name=geraet][value=kt]').checked = true; zeichneBlatt(); const z = $('#blatt dd.betrag'); z.scrollIntoView({ block: 'center', behavior: 'instant' }); zeigeAuf(z); });
      nach(5100, () => { scrollTo(0, 0); zeigeAuf(druckKnopf, true); });
      nach(5700, () => { druckansicht(true); scrollTo(0, 0); zeigeAuf($('.blatt-kopf h2')); });
      nach(8000, () => druckansicht(false));
      nach(8400, lauf);
    }
  }
  function neustart() {
    timer.forEach(clearTimeout); timer = [];
    laeuft = false; html.classList.remove('vorfuehrung');
    auftragZurueck(); setzeReihe('w700'); druckansicht(false);
    $('input[name=geraet][value=wm]').checked = true; zeichneBlatt(); 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(); }

  zeichneTabelle(); zeichneBlatt();
  setzeV(new URLSearchParams(location.search).get('v'), false);

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