Zum Inhalt
← Mikroanimationen

Animiertes Auswahlmenü (anpassbares select)

Natives select mit appearance: base-select. Der Picker fällt 6 px herab und blendet in 300 ms ein, das gewählte Icon zeichnet sich neu.

Das native <select> wird mit appearance: base-select voll gestaltbar. Man gestaltet den Picker (::picker(select)), den Pfeil (::picker-icon) und Optionen mit Icons, Farbflächen oder Preisen. Der Picker blendet beim Öffnen über @starting-style ein, der Pfeil dreht sich, die gewählte Option trägt einen Haken. Stand November 2026 können das Chrome und Edge (ab 135) und Safari (ab 27, September 2026). Firefox zeigt ein normales, voll funktionsfähiges select.

Formularelemente passen zur Markenwelt, ohne eigenes Dropdown-JavaScript und ohne Verlust an Barrierefreiheit. Die kurze Bewegung zeigt, woher die Liste kommt, und bestätigt die Wahl.

Wann einsetzen

Sprachwahl, Filter, Formularauswahl mit Icons oder Farben, Länderauswahl, Leistungsauswahl in Anfrageformularen.

Wann nicht

Bei sehr langen Listen. Dann eine Combobox mit Suche verwenden.

Varianten

  • Picker mit Ein- und Ausblend-Animation
  • Optionen mit Bildern, Icons oder Flaggen
  • Gestaffelte Optionen über sibling-index()
  • Picker als Raster-Layout

Umsetzung

Browserstand (geprüft 02.11.2026, Web Features Explorer, WebKit-Blog zu Safari 27, web.dev September 2026): appearance: base-select mit <selectedcontent>, ::picker-icon und ::checkmark gibt es in Chrome und Edge ab 135 und in Safari ab 27. Firefox hat es nur hinter Flag und zeigt das native select; Baseline ist es deshalb noch nicht. Alles Gestalten gehört in @supports (appearance: base-select), dann bleibt Firefox voll bedienbar. select, ::picker(select) { appearance: base-select; } ::picker(select) { opacity: 0; translate: 0 -6px; transition: opacity .15s, translate .15s, display .15s allow-discrete, overlay .15s allow-discrete; } select:open::picker(select) { opacity: 1; translate: 0; transition-duration: .3s; transition-timing-function: cubic-bezier(.25, 1, .5, 1); } @starting-style { select:open::picker(select) { opacity: 0; translate: 0 -6px; } } select::picker-icon { transition: rotate .3s; } select:open::picker-icon { rotate: 180deg; }. Das gewählte Element zeigt <button><selectedcontent></selectedcontent></button> als erstes Kind des select. Chrome öffnet den Picker standardmäßig dort, wo mehr Platz ist (position-try-order: most-block-size); position-try: normal flip-block öffnet ihn zuerst nach unten. showPicker() braucht eine Nutzergeste, ein Skript kann den Picker also nicht von selbst öffnen.

Robust umsetzen

Telefon. Safari auf iOS ab 27 und Chrome auf Android zeigen den gestalteten Picker; dann Optionen mindestens 48 px hoch und den Picker nicht breiter als das Feld. Firefox auf Android zeigt weiter den nativen Picker.

Ohne Bewegung. Alle Übergänge auf 0 setzen. Der Picker erscheint dann sofort.

Barrierefrei. Native Semantik und Tastatursteuerung bleiben erhalten. Icons und Farbflächen tragen aria-hidden, der Optionstext enthält die ganze Information.

Code

<!DOCTYPE html>
<!--
  Animiertes Auswahlmenü (anpassbares select)
  Fundus · Mikroanimationen · https://cecodesign.de/lab/fundus/mikroanimationen/animiertes-select/
  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>Animiertes Auswahlmenü (anpassbares select) · Fundus-Demo</title>
<script>document.documentElement.classList.add('js');if(matchMedia('(prefers-reduced-motion: reduce)').matches)document.documentElement.classList.add('reduziert')</script>
<style>
@font-face { font-family: 'Special Gothic'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/special-gothic:vf@latest/latin-standard-normal.woff2') format('woff2-variations'); font-weight: 400 700; font-stretch: 75% 125%; font-display: swap; }
@font-face { font-family: 'Atkinson Hyperlegible Next'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/atkinson-hyperlegible-next:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 200 800; font-display: swap; }
@font-face { font-family: '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; }

/* Malerwerkstatt Brenneis, Heidelberg-Handschuhsheim · Rezept „Handwerk: Werkstatt und Klartext“:
   Materialflächen (Zement, Holz, Salbei), dunkle Schrift, ein Signalton nur für Anfrage und Anruf. */
:root {
  --grund: #efedea; --zement: #d3cdc2; --holz: #c3b4a5; --salbei: #bdc1a9; --hell: #faf9f6;
  --text: #1a1a1a; --leise: rgba(26, 26, 26, .64); --linie: rgba(26, 26, 26, .16); --linie-fest: rgba(26, 26, 26, .78);
  --signal: #fb6100;                          /* nur „Angebot anfragen“ und „Anrufen“ */

  /* Bewegung: gemessen bei Stripe (Auswahlmenü), Schließen nach Rezept schneller und beschleunigend */
  --kurve: cubic-bezier(0.25, 1, 0.5, 1);     /* power3.out: Picker und Optionen erscheinen */
  --kurve-zu: cubic-bezier(0.4, 0, 1, 1);     /* Accelerate: Picker schließt */
  --kurve-icon: cubic-bezier(0.65, 0, 0.35, 1); /* Icon zeichnet sich ein */
  --kurve-ui: cubic-bezier(0.4, 0, 0.2, 1);   /* Hover, Farbe, Wand */
  --auf: 300ms; --zu: 150ms; --icon: 1000ms; --ui: 150ms; --wand-dauer: 400ms;
  --weg: 6px;                                 /* Picker fällt 6 px herab */
  --staffel: 0ms;                             /* Abstand je Option (Variante „Gestaffelt“) */

  --wand: #a9ac9a;                            /* gesetzt vom Farbton-Menü */
}
.reduziert { --auf: 0ms; --zu: 0ms; --icon: 0ms; --ui: 0ms; --wand-dauer: 0ms; --staffel: 0ms !important; --weg: 0px; }

* { box-sizing: border-box; margin: 0; }
html { background: var(--grund); color: var(--text); font-family: 'Atkinson Hyperlegible Next', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
body { min-height: 100svh; padding: clamp(16px, 2.6vw, 36px); display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr auto; gap: clamp(20px, 2.6vw, 34px); }
a { color: inherit; }
.mono { font: 500 11px/1.35 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; color: var(--leise); }
.gewerk { font: 500 13px/1.2 'Special Gothic', 'Arial Narrow', sans-serif; font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase; font-variant-numeric: tabular-nums; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.rahmen { max-width: 1200px; width: 100%; margin: 0 auto; }

/* Kopf: Telefon und Anfrage immer sichtbar */
.kopf { display: flex; align-items: center; gap: clamp(16px, 2.4vw, 36px); padding-bottom: 14px; border-bottom: 1px solid var(--linie-fest); }
.marke { display: flex; align-items: center; gap: 11px; text-decoration: none; margin-right: auto; }
.marke svg { width: 30px; height: 30px; flex: none; }
.marke b { display: block; font: 700 22px/1 'Special Gothic', sans-serif; font-stretch: 112%; letter-spacing: .01em; }
.marke small { display: block; margin-top: 3px; font: 500 11.5px/1 'Special Gothic', sans-serif; font-stretch: 75%; letter-spacing: .06em; text-transform: uppercase; color: var(--leise); }
.nav { display: flex; gap: clamp(14px, 2vw, 28px); list-style: none; padding: 0; font-size: 15px; }
.nav a { text-decoration: none; color: var(--leise); transition: color var(--ui) var(--kurve-ui); }
.nav a:hover { color: var(--text); }
.tel { display: flex; align-items: center; gap: 8px; text-decoration: none; font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tel svg { width: 16px; height: 16px; color: var(--signal); flex: none; }
.cta { display: inline-flex; align-items: center; gap: 10px; background: var(--signal); color: var(--text); text-decoration: none; font: 700 15px/1 'Atkinson Hyperlegible Next', sans-serif; padding: 13px 18px; border: 0; border-radius: 3px; cursor: pointer; white-space: nowrap; transition: background-color var(--ui) var(--kurve-ui), scale var(--ui) var(--kurve-ui); }
.cta:hover { background: #ff7a26; }
.cta .knapp { display: none; }
.cta:active { scale: .97; }
.cta:focus-visible { outline: 2px solid var(--text); outline-offset: 3px; }

/* Bühne: Wandansicht links, Anfrage rechts */
.buehne { display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1fr); gap: clamp(28px, 5vw, 84px); align-items: center; }

.wand { position: relative; width: 100%; height: min(70vh, 660px); min-height: 420px; overflow: hidden; border-radius: 2px; background: #f3f1ec; display: grid; grid-template-rows: auto minmax(0, 1fr); }
.wand svg { width: 100%; height: 100%; display: block; min-height: 0; }
.wand .flaeche { fill: var(--wand); transition: fill var(--wand-dauer) var(--kurve-ui); }
.wand .ton { fill: var(--wand); transition: fill var(--wand-dauer) var(--kurve-ui); }
.wand-kopf { grid-row: 1; display: flex; justify-content: space-between; gap: 12px; padding: 14px 16px 8px; color: rgba(26, 26, 26, .7); }
.wand svg { grid-row: 2; }

.anfrage { display: grid; gap: clamp(14px, 1.8vw, 22px); max-width: 580px; }
.anfrage > .gewerk { color: var(--leise); display: flex; gap: 10px; align-items: center; }
.anfrage > .gewerk::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: #5d7a4a; }
h1 { font: 700 clamp(2.25rem, 1.5rem + 3vw, 3.5rem)/1.05 'Special Gothic', sans-serif; font-stretch: 100%; letter-spacing: -.01em; text-wrap: balance; }
.text { font-size: 17px; line-height: 1.55; color: var(--leise); max-width: 46ch; text-wrap: pretty; }

.felder { display: grid; gap: 14px; margin-top: 4px; }
.reihe { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
.feld { position: relative; display: grid; gap: 7px; min-width: 0; }
.feld > label { color: var(--leise); }
.feld input { font: 500 17px/1.2 'Atkinson Hyperlegible Next', sans-serif; color: var(--text); background: var(--hell); border: 1px solid var(--linie-fest); border-radius: 3px; padding: 14px; min-height: 52px; width: 100%; font-variant-numeric: tabular-nums; }
.feld input:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.aktion { display: flex; align-items: center; gap: 12px 20px; flex-wrap: wrap; margin-top: 6px; }
.aktion .cta { padding: 16px 22px; font-size: 16px; }
.aktion .cta svg { width: 16px; height: 16px; }
.aktion small { font-size: 14px; line-height: 1.4; color: var(--leise); }
.aktion small a { font-weight: 600; color: var(--text); text-decoration-color: var(--signal); text-underline-offset: 3px; }
.hinweis { min-height: 1.35em; }

/* ——— Das Auswahlmenü ———
   Ohne Unterstützung bleibt es ein normales select (nur Schrift und Rahmen). */
.feld select { font: 500 17px/1.2 'Atkinson Hyperlegible Next', sans-serif; color: var(--text); background: var(--hell); border: 1px solid var(--linie-fest); border-radius: 3px; padding: 12px; min-height: 52px; width: 100%; }

@supports (appearance: base-select) {
  .feld select, .feld select::picker(select) { appearance: base-select; }

  /* Auslöser */
  .feld select { display: flex; align-items: center; gap: 12px; padding: 8px 14px 8px 8px; cursor: pointer; transition: border-color var(--ui) var(--kurve-ui), background-color var(--ui) var(--kurve-ui), scale var(--ui) var(--kurve-ui); }
  .feld select:hover { background: #fff; }
  .feld select:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
  .feld select:active, .feld select.gedrueckt { scale: .985; }
  .feld select > button { display: flex; flex: 1; min-width: 0; }
  .feld selectedcontent { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }
  .feld selectedcontent :is(.beschr, .haken, .trenner) { display: none; }
  .feld selectedcontent .titel { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity var(--auf) var(--kurve), translate var(--auf) var(--kurve); }
  .feld selectedcontent .preis { color: var(--leise); }
  @starting-style { .feld selectedcontent .titel { opacity: 0; translate: 0 5px; } }
  /* Neues Icon zeichnet sich Strich für Strich ein (Stripe: 1 s, 0.65/0/0.35/1) */
  .feld selectedcontent .ico path { stroke-dasharray: 1; stroke-dashoffset: 0; transition: stroke-dashoffset var(--icon) var(--kurve-icon); }
  @starting-style { .feld selectedcontent .ico path { stroke-dashoffset: 1; } }
  .feld selectedcontent .muster { transition: scale var(--auf) var(--kurve); }
  @starting-style { .feld selectedcontent .muster { scale: .4; } }

  /* Pfeil dreht beim Öffnen */
  .feld select::picker-icon { content: ''; width: 12px; height: 12px; flex: none; background: currentColor; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M1.5 4 6 8.5 10.5 4' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E") center / contain no-repeat; transition: rotate var(--auf) var(--kurve); }
  .feld select:open::picker-icon, .feld select.zeigt::picker-icon { rotate: 180deg; }
  .feld select:open, .feld select.zeigt { background: #fff; border-color: var(--text); }

  /* Der Picker: echtes ::picker(select) und – nur für die Vorführung – ein Doppel mit denselben Werten */
  .feld select::picker(select) {
    position-area: bottom span-right; margin: 6px 0; min-width: anchor-size(width); position-try: normal flip-block;
    background: rgba(250, 249, 246, .985); backdrop-filter: blur(10px); border: 1px solid var(--linie-fest); border-radius: 5px; padding: 5px;
    box-shadow: 0 22px 44px -22px rgba(26, 26, 26, .45), 0 2px 6px rgba(26, 26, 26, .08);
    opacity: 0; translate: 0 calc(var(--weg) * -1);
    transition: opacity var(--zu) var(--kurve-zu), translate var(--zu) var(--kurve-zu), display var(--zu) allow-discrete, overlay var(--zu) allow-discrete;
  }
  .feld select:open::picker(select) { opacity: 1; translate: 0 0; transition-duration: var(--auf); transition-timing-function: var(--kurve); }
  @starting-style { .feld select:open::picker(select) { opacity: 0; translate: 0 calc(var(--weg) * -1); } }

  .doppel {
    position: absolute; left: 0; top: calc(100% + 6px); min-width: 100%; z-index: 20;
    background: rgba(250, 249, 246, .985); backdrop-filter: blur(10px); border: 1px solid var(--linie-fest); border-radius: 5px; padding: 5px;
    box-shadow: 0 22px 44px -22px rgba(26, 26, 26, .45), 0 2px 6px rgba(26, 26, 26, .08);
    opacity: 0; translate: 0 calc(var(--weg) * -1);
    transition: opacity var(--zu) var(--kurve-zu), translate var(--zu) var(--kurve-zu);
  }
  .doppel.offen { opacity: 1; translate: 0 0; transition-duration: var(--auf); transition-timing-function: var(--kurve); }
  @starting-style { .doppel.offen { opacity: 0; translate: 0 calc(var(--weg) * -1); } }

  /* Optionen: blenden mit 300 ms ein, in „Gestaffelt“ je 30 ms später */
  .feld option { --n: calc(sibling-index() - 2); }   /* 1. Kind ist der <button> */
  :is(.feld option, .doppel .opt) {
    display: grid; align-items: center; column-gap: 12px; row-gap: 1px; padding: 9px 10px; border-radius: 3px; cursor: pointer; color: var(--text); white-space: normal;
    font: 500 16px/1.25 'Atkinson Hyperlegible Next', sans-serif;
    transition: opacity var(--auf) var(--kurve), translate var(--auf) var(--kurve), background-color var(--ui) var(--kurve-ui);
    transition-delay: calc(var(--n) * var(--staffel)), calc(var(--n) * var(--staffel)), 0s;
  }
  @starting-style { :is(.feld select:open option, .doppel.offen .opt) { opacity: 0; translate: 0 -4px; } }
  .feld option::checkmark { display: none; }
  :is(.feld option:hover, .feld option:focus-visible, .doppel .opt.ueber) { background: var(--zement); outline: none; }
  :is(.feld option:checked, .doppel .opt.gewaehlt) { background: rgba(26, 26, 26, .055); }
  :is(.feld option:checked:hover, .feld option:checked:focus-visible, .doppel .opt.gewaehlt.ueber) { background: var(--zement); }
  :is(.feld option, .doppel .opt) .haken { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; opacity: 0; }
  :is(.feld option:checked, .doppel .opt.gewaehlt) .haken { opacity: 1; }
  :is(.feld option, .doppel .opt) .trenner { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

  /* Inhalte der Optionen */
  :is(.feld, .doppel) .ico { width: 36px; height: 36px; padding: 7px; flex: none; border-radius: 3px; background: var(--salbei); fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
  :is(.feld, .doppel) .titel { font-weight: 600; }
  :is(.feld, .doppel) .beschr { font-size: 14px; font-weight: 400; color: var(--leise); }
  :is(.feld, .doppel) .preis { font: 500 13px/1.2 'Special Gothic', sans-serif; font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; font-variant-numeric: tabular-nums; }
  :is(.feld, .doppel) .muster { width: 30px; height: 30px; flex: none; border-radius: 50%; background: var(--f); box-shadow: inset 0 0 0 1px rgba(26, 26, 26, .18); }

  :is(#leistung option, [data-fuer="leistung"] .opt) { grid-template-columns: 36px minmax(0, 1fr) auto 16px; grid-template-areas: "ico titel preis haken" "ico beschr preis haken"; min-width: 0; }
  :is(#farbton option, [data-fuer="farbton"] .opt) { grid-template-columns: 30px minmax(0, 1fr) 16px; grid-template-areas: "muster titel haken" "muster beschr haken"; }
  :is(#zeit option, [data-fuer="zeit"] .opt) { grid-template-columns: minmax(0, 1fr) 16px; grid-template-areas: "titel haken"; padding-block: 11px; }
  :is(.feld option, .doppel .opt) > .ico { grid-area: ico; }
  :is(.feld option, .doppel .opt) > .titel { grid-area: titel; }
  :is(.feld option, .doppel .opt) > .beschr { grid-area: beschr; }
  :is(.feld option, .doppel .opt) > .preis { grid-area: preis; text-align: right; }
  :is(.feld option, .doppel .opt) > .muster { grid-area: muster; }
  :is(.feld option, .doppel .opt) > .haken { grid-area: haken; }
  #leistung::picker(select) { width: anchor-size(width); }
  #farbton::picker(select) { width: max(anchor-size(width), 290px); }
  [data-fuer="farbton"].doppel { width: max(100%, 290px); }

  /* Variante „Raster“: Picker als Kachelraster */
  [data-variante="raster"] :is(#leistung, #farbton):open::picker(select),
  [data-variante="raster"] :is([data-fuer="leistung"], [data-fuer="farbton"]).doppel { display: grid; gap: 4px; }
  [data-variante="raster"] #leistung:open::picker(select), [data-variante="raster"] [data-fuer="leistung"].doppel { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  [data-variante="raster"] #farbton:open::picker(select), [data-variante="raster"] [data-fuer="farbton"].doppel { grid-template-columns: repeat(4, minmax(0, 1fr)); width: max(100%, 340px); }
  [data-variante="raster"] #farbton:open::picker(select) { width: max(anchor-size(width), 340px); }
  [data-variante="raster"] :is(#leistung option, [data-fuer="leistung"] .opt) { grid-template-columns: 36px minmax(0, 1fr) 16px; grid-template-areas: "ico . haken" "titel titel titel" "preis preis preis"; row-gap: 6px; padding: 12px; align-items: start; }
  [data-variante="raster"] :is(#leistung option, [data-fuer="leistung"] .opt) > .beschr { display: none; }
  [data-variante="raster"] :is(#leistung option, [data-fuer="leistung"] .opt) > .preis { text-align: left; color: var(--leise); }
  [data-variante="raster"] :is(#farbton option, [data-fuer="farbton"] .opt) { grid-template-columns: minmax(0, 1fr); grid-template-areas: "muster" "titel"; justify-items: center; text-align: center; row-gap: 7px; padding: 10px 6px; font-size: 13px; position: relative; }
  [data-variante="raster"] :is(#farbton option, [data-fuer="farbton"] .opt) > .muster { width: 100%; height: auto; aspect-ratio: 1; border-radius: 3px; }
  [data-variante="raster"] :is(#farbton option, [data-fuer="farbton"] .opt) > .beschr { display: none; }
  [data-variante="raster"] :is(#farbton option, [data-fuer="farbton"] .opt) > .haken { position: absolute; top: 14px; right: 10px; color: #fff; filter: drop-shadow(0 0 2px rgba(0, 0, 0, .5)); }
}

/* Fuß: Umschalter und eingesetzte Werte (Werkzeug der Demo, bewusst in Geist Mono) */
.fuss { display: flex; justify-content: space-between; align-items: center; gap: 12px 28px; flex-wrap: wrap; padding-top: 14px; border-top: 1px solid var(--linie); }
.gruppe { display: flex; align-items: center; gap: 10px; }
.umschalter { display: flex; gap: 2px; padding: 3px; border: 1px solid rgba(26, 26, 26, .22); border-radius: 4px; }
.umschalter button { white-space: nowrap; font: 500 10.5px/1 'Geist Mono', ui-monospace, monospace; letter-spacing: .14em; text-transform: uppercase; color: var(--leise); background: none; border: 0; border-radius: 2px; padding: 8px 12px; cursor: pointer; transition: background-color var(--ui) var(--kurve-ui), color var(--ui) var(--kurve-ui); }
.umschalter button[aria-pressed="true"] { background: var(--text); color: var(--grund); }
.umschalter button:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.werte { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px 16px; max-width: 74ch; }
.werte b { color: var(--text); font-weight: 500; }

/* Vorführung: gezeichneter Zeiger */
.zeiger { position: fixed; left: 0; top: 0; z-index: 30; width: 20px; height: 20px; pointer-events: none; display: none; transition: transform .5s var(--kurve-ui), scale var(--ui) var(--kurve-ui); }
.vorfuehrung .zeiger { display: block; }
.zeiger.klick { scale: .82; }

/* Niedrige Fenster (auch Vorschau 960 × 720): verdichten */
@media (max-height: 820px) and (min-width: 861px) {
  body { gap: 18px; padding-block: 20px; }
  .anfrage { gap: 12px; }
  h1 { font-size: clamp(2rem, 1.2rem + 2.4vw, 2.9rem); }
  .text { font-size: 16px; }
  .wand { height: min(74vh, 600px); }
}
/* Vorschau: nur die Bühne */
.vorschau body { overflow: hidden; height: 100svh; grid-template-rows: 1fr; padding: 28px 34px; }
.vorschau .kopf, .vorschau .fuss, .vorschau .text { display: none; }
.vorschau .buehne { gap: 40px; align-items: start; }
.vorschau .anfrage { padding-top: 36px; }
.vorschau .wand { height: calc(100svh - 56px); }

@media (max-width: 1080px) { .nav { display: none; } }
@media (max-width: 860px) {
  .buehne { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .anfrage { max-width: none; }
  .wand { height: auto; aspect-ratio: 4 / 3.4; min-height: 0; order: 2; }
  .fuss { align-items: flex-start; flex-direction: column; }
  .werte { justify-content: flex-start; }
}
@media (max-width: 560px) {
  .kopf .tel span { display: none; }
  .kopf .tel { width: 44px; height: 44px; justify-content: center; border: 1px solid var(--linie-fest); border-radius: 3px; }
  .kopf .cta { padding: 13px 14px; font-size: 14px; }
  .kopf .cta .voll { display: none; }
  .kopf .cta .knapp { display: inline; }
  .marke small { display: none; }
  .reihe { grid-template-columns: minmax(0, 1fr); }
  .aktion .cta { width: 100%; justify-content: center; }
  .wand { aspect-ratio: 4 / 4.2; }
}
/* 320 px (WCAG 1.4.10) */
@media (max-width: 360px) {
  .kopf { gap: 10px; }
  .gruppe, .wand-kopf { flex-wrap: wrap; }
  .werte span { overflow-wrap: anywhere; }
}
@media (forced-colors: active) {
  button, [role=button], [role=tab], [role=switch] { border: 1px solid ButtonText !important; }
}
/* Schmal: Leistungen dreizeilig, damit der Picker so breit bleibt wie das Feld */
@media (max-width: 560px) {
  @supports (appearance: base-select) {
    :is(#leistung option, [data-fuer="leistung"] .opt) { grid-template-columns: 36px minmax(0, 1fr) 16px; grid-template-areas: "ico titel haken" "ico beschr haken" "ico preis haken"; align-items: start; }
    :is(#leistung option, [data-fuer="leistung"] .opt) > .preis { text-align: left; color: var(--leise); margin-top: 2px; }
    :is(.feld, .doppel) .beschr { font-size: 13.5px; }
    .feld selectedcontent .preis { display: none; }
  }
}
/* Touch: größere Tippflächen im Picker */
@media (hover: none) {
  @supports (appearance: base-select) {
    :is(.feld option, .doppel .opt) { min-height: 48px; }
    .feld select:hover { background: var(--hell); }
  }
}
</style>
</head>
<body>
<header class="kopf rahmen">
  <a class="marke" href="#">
    <svg viewBox="0 0 30 30" aria-hidden="true"><rect x="1" y="1" width="28" height="28" fill="#1a1a1a"/><rect x="1" y="18" width="28" height="11" fill="#bdc1a9"/><path d="M1 18h28" stroke="#efedea" stroke-width="1.2"/><path d="M8 7h8.2a3.4 3.4 0 0 1 0 6.8H8z" fill="none" stroke="#efedea" stroke-width="2"/></svg>
    <span><b>Brenneis</b><small>Malerwerkstatt · Heidelberg</small></span>
  </a>
  <nav aria-label="Hauptnavigation"><ul class="nav">
    <li><a href="#">Leistungen</a></li><li><a href="#">Referenzen</a></li><li><a href="#">Ablauf</a></li><li><a href="#">Farbberatung</a></li>
  </ul></nav>
  <a class="tel" href="tel:+496221583041"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M5.6 1.8 3.4 2.3c-.7.2-1.2.9-1.1 1.6.6 5 4.8 9.2 9.8 9.8.7.1 1.4-.4 1.6-1.1l.5-2.2-3-1.4-1.4 1.6a8.3 8.3 0 0 1-3.8-3.8l1.6-1.4z" fill="currentColor"/></svg><span>06221 58 30 41</span></a>
  <a class="cta" href="#anfrage"><span class="voll">Angebot anfragen</span><span class="knapp">Anfrage</span></a>
</header>

<main class="buehne rahmen">
  <figure class="wand" aria-label="Wandansicht eines Altbauzimmers im gewählten Farbton">
    <svg viewBox="0 36 600 684" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
      <defs>
        <linearGradient id="licht" x1="0" y1="0" x2=".7" y2="1"><stop offset="0" stop-color="#fff" stop-opacity=".34"/><stop offset=".55" stop-color="#fff" stop-opacity=".08"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient>
        <linearGradient id="ecke" x1="0" x2="1"><stop offset=".6" stop-color="#000" stop-opacity="0"/><stop offset="1" stop-color="#000" stop-opacity=".16"/></linearGradient>
        <linearGradient id="kehle" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#000" stop-opacity=".1"/><stop offset="1" stop-color="#000" stop-opacity="0"/></linearGradient>
        <linearGradient id="boden" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#b6a28b"/><stop offset="1" stop-color="#9c876f"/></linearGradient>
        <linearGradient id="glas" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#dfe5e4"/><stop offset="1" stop-color="#b9c3c3"/></linearGradient>
      </defs>
      <!-- Decke und Stuck -->
      <rect y="-200" width="600" height="264" fill="#f3f1ec"/>
      <rect y="62" width="600" height="7" fill="#fbfaf7"/>
      <rect y="69" width="600" height="5" fill="#e4e0d8"/>
      <rect y="74" width="600" height="13" fill="#f7f5f0"/>
      <rect y="87" width="600" height="3" fill="#d9d4ca"/>
      <rect y="90" width="600" height="5" fill="#f4f2ed"/>
      <!-- Wand im gewählten Ton -->
      <rect class="flaeche" y="95" width="600" height="545"/>
      <rect y="95" width="600" height="18" fill="url(#kehle)"/>
      <polygon points="0,95 250,95 470,640 0,640" fill="url(#licht)"/>
      <rect y="95" width="600" height="545" fill="url(#ecke)"/>
      <!-- Kassettentür mit Oberlicht -->
      <rect x="352" y="168" width="196" height="474" fill="#000" opacity=".07" transform="translate(7 4)"/>
      <rect x="352" y="168" width="196" height="474" fill="#f8f6f1"/>
      <rect x="352" y="168" width="196" height="474" fill="none" stroke="#d6d0c4" stroke-width="2"/>
      <rect x="368" y="184" width="164" height="80" fill="url(#glas)" stroke="#d6d0c4" stroke-width="2"/>
      <path d="M450 184v80M368 224h164" stroke="#f8f6f1" stroke-width="5"/>
      <rect x="368" y="276" width="164" height="366" fill="#f4f1eb" stroke="#d6d0c4" stroke-width="1.5"/>
      <g fill="none" stroke-width="1.5">
        <rect x="384" y="292" width="58" height="150" stroke="#d6d0c4"/><rect x="458" y="292" width="58" height="150" stroke="#d6d0c4"/>
        <rect x="384" y="462" width="58" height="160" stroke="#d6d0c4"/><rect x="458" y="462" width="58" height="160" stroke="#d6d0c4"/>
        <rect x="389" y="297" width="48" height="140" stroke="#fff"/><rect x="463" y="297" width="48" height="140" stroke="#fff"/>
        <rect x="389" y="467" width="48" height="150" stroke="#fff"/><rect x="463" y="467" width="48" height="150" stroke="#fff"/>
      </g>
      <path d="M450 276v366" stroke="#d6d0c4" stroke-width="1.5"/>
      <rect x="434" y="446" width="5" height="22" rx="2" fill="#a88a57"/>
      <!-- Farbkarte mit Malerkrepp -->
      <g transform="rotate(-2.2 160 300)">
        <rect x="92" y="226" width="140" height="176" fill="#000" opacity=".08" transform="translate(3 4)"/>
        <rect x="92" y="226" width="140" height="176" fill="#faf9f6"/>
        <rect class="ton" x="102" y="236" width="120" height="104"/>
        <rect x="102" y="236" width="120" height="104" fill="none" stroke="#000" stroke-opacity=".1"/>
        <text x="102" y="364" fill="#1a1a1a" style="font: 700 15px 'Special Gothic', sans-serif; font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase" data-karte-name>Neckarkiesel</text>
        <text x="102" y="384" fill="#1a1a1a" fill-opacity=".6" style="font: 500 11px 'Geist Mono', monospace; letter-spacing: .12em" data-karte-nr>PROBE 02 · MATT</text>
        <rect x="134" y="216" width="56" height="20" fill="#e6dcc0" opacity=".92" transform="rotate(4 162 226)"/>
      </g>
      <!-- Fußleiste und Dielen -->
      <rect y="640" width="600" height="80" fill="url(#boden)"/>
      <path d="M0 662h600M0 690h600M140 640v22M410 640v22M60 662v28M300 662v28M520 662v28M200 690v30M460 690v30" stroke="#7d6a55" stroke-opacity=".35" stroke-width="1.2"/>
      <rect y="606" width="600" height="36" fill="#f8f6f1"/>
      <rect y="606" width="600" height="3" fill="#e2ddd3"/>
      <rect y="642" width="600" height="4" fill="#000" opacity=".12"/>
      <!-- Farbeimer -->
      <g transform="translate(80 584)">
        <ellipse cx="46" cy="86" rx="50" ry="8" fill="#000" opacity=".16"/>
        <path d="M4 16h84l-6 68a6 6 0 0 1-6 4H16a6 6 0 0 1-6-4z" fill="#ecebe6"/>
        <path d="M6.2 40h79.6l-2.7 30H8.9z" class="ton"/>
        <path d="M6.2 40h79.6" stroke="#000" stroke-opacity=".12"/>
        <ellipse cx="46" cy="16" rx="42" ry="7" fill="#d8d6cf"/>
        <path d="M4 16a42 7 0 0 0 84 0" fill="none" stroke="#bdbab2" stroke-width="1.5"/>
        <path d="M2 18c4-34 84-34 88 0" fill="none" stroke="#6d6a63" stroke-width="2"/>
      </g>
    </svg>
    <figcaption class="wand-kopf"><span class="mono">Wandansicht</span><span class="mono">Altbau, Handschuhsheim</span></figcaption>
  </figure>

  <form class="anfrage" id="anfrage" data-anfrage data-variante="einblenden" novalidate>
    <p class="gewerk">Anfrage · Antwort in zwei Werktagen</p>
    <h1>Was dürfen wir für Sie streichen?</h1>
    <p class="text">Drei Angaben genügen. Wir melden uns mit einem Termin zur Besichtigung, im Raum Heidelberg kostenlos.</p>

    <div class="felder">
      <div class="feld">
        <label class="gewerk" for="leistung">Leistung</label>
        <select id="leistung" name="leistung">
          <button type="button"><selectedcontent></selectedcontent></button>
          <option value="innen" selected><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path pathLength="1" d="M4 4h13v5H4z"/><path pathLength="1" d="M17 6.5h2.5v5H11V15"/><path pathLength="1" d="M10 15h2v6h-2z"/></svg><span class="titel">Innenräume streichen</span><span class="trenner">, </span><span class="beschr">Wände und Decken, mit Abdecken</span><span class="trenner">, </span><span class="preis">ab 14 €/m²</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          <option value="fassade"><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path pathLength="1" d="M3 11 12 4l9 7"/><path pathLength="1" d="M5.5 9.5V20h13V9.5"/><path pathLength="1" d="M10 20v-5h4v5"/></svg><span class="titel">Fassade</span><span class="trenner">, </span><span class="beschr">Reinigen, Risse schließen, Silikatfarbe</span><span class="trenner">, </span><span class="preis">ab 38 €/m²</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          <option value="tueren"><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path pathLength="1" d="M6 21V3h12v18"/><path pathLength="1" d="M4 21h16"/><path pathLength="1" d="M8.5 6h7v5h-7z"/><path pathLength="1" d="M8.5 13.5h7v4.5h-7z"/></svg><span class="titel">Türen und Fenster lackieren</span><span class="trenner">, </span><span class="beschr">Altlack schleifen, zweimal lackieren</span><span class="trenner">, </span><span class="preis">ab 180 € je Tür</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          <option value="tapete"><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path pathLength="1" d="M7 4h12v13H7"/><path pathLength="1" d="M7 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12v-3"/><path pathLength="1" d="M10.5 8h5M10.5 11.5h5"/></svg><span class="titel">Tapezieren</span><span class="trenner">, </span><span class="beschr">Vlies, Raufaser, Mustertapete</span><span class="trenner">, </span><span class="preis">ab 16 €/m²</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          <option value="putz"><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path pathLength="1" d="M3 17h18v3H3z"/><path pathLength="1" d="M8 17v-4h8v4"/><path pathLength="1" d="M12 13V8.5"/><path pathLength="1" d="M9.5 8.5h5a1.75 1.75 0 0 0 0-3.5h-5a1.75 1.75 0 0 0 0 3.5z"/></svg><span class="titel">Kalk- und Lehmputz</span><span class="trenner">, </span><span class="beschr">Diffusionsoffen, gut für Altbauten</span><span class="trenner">, </span><span class="preis">ab 45 €/m²</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          <option value="treppenhaus"><svg class="ico" viewBox="0 0 24 24" aria-hidden="true"><path pathLength="1" d="M3 20h4v-4h4v-4h4V8h4V4h2"/><path pathLength="1" d="M3 20h18"/></svg><span class="titel">Treppenhaus</span><span class="trenner">, </span><span class="beschr">Für Hausverwaltungen, auch abends</span><span class="trenner">, </span><span class="preis">nach Aufmaß</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
        </select>
      </div>

      <div class="reihe">
        <div class="feld">
          <label class="gewerk" for="farbton">Farbton der Probefläche</label>
          <select id="farbton" name="farbton">
            <button type="button"><selectedcontent></selectedcontent></button>
            <option value="kalkweiss" data-farbe="#ebe7dc" data-nr="01"><span class="muster" style="--f:#ebe7dc"></span><span class="titel">Kalkweiß</span><span class="trenner">, </span><span class="beschr">warm, für Decken</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="neckarkiesel" data-farbe="#a9ac9a" data-nr="02" selected><span class="muster" style="--f:#a9ac9a"></span><span class="titel">Neckarkiesel</span><span class="trenner">, </span><span class="beschr">Grüngrau, ruhig</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="salbei" data-farbe="#c3c8ae" data-nr="03"><span class="muster" style="--f:#c3c8ae"></span><span class="titel">Salbei</span><span class="trenner">, </span><span class="beschr">hell, für Küchen</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="buntsandstein" data-farbe="#a2604d" data-nr="04"><span class="muster" style="--f:#a2604d"></span><span class="titel">Buntsandstein</span><span class="trenner">, </span><span class="beschr">das Rot der Altstadt</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="ocker" data-farbe="#c9a466" data-nr="05"><span class="muster" style="--f:#c9a466"></span><span class="titel">Ocker</span><span class="trenner">, </span><span class="beschr">warm, für Flure</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="tintenblau" data-farbe="#45566a" data-nr="06"><span class="muster" style="--f:#45566a"></span><span class="titel">Tintenblau</span><span class="trenner">, </span><span class="beschr">tief, für eine Wand</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="lehm" data-farbe="#b89b7f" data-nr="07"><span class="muster" style="--f:#b89b7f"></span><span class="titel">Lehm</span><span class="trenner">, </span><span class="beschr">erdig, zu Kalkputz</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="graphit" data-farbe="#4b4a46" data-nr="08"><span class="muster" style="--f:#4b4a46"></span><span class="titel">Graphit</span><span class="trenner">, </span><span class="beschr">für Sockel und Türen</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          </select>
        </div>
        <div class="feld">
          <label class="gewerk" for="zeit">Wunschzeitraum</label>
          <select id="zeit" name="zeit">
            <button type="button"><selectedcontent></selectedcontent></button>
            <option value="bald"><span class="titel">So bald wie möglich</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="4w"><span class="titel">In den nächsten 4 Wochen</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="3m" selected><span class="titel">In 1 bis 3 Monaten</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
            <option value="beratung"><span class="titel">Erst beraten lassen</span><svg class="haken" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg></option>
          </select>
        </div>
      </div>

      <div class="feld">
        <label class="gewerk" for="plz">Postleitzahl</label>
        <input id="plz" name="plz" inputmode="numeric" autocomplete="postal-code" maxlength="5" value="69121">
      </div>
    </div>

    <div class="aktion">
      <button class="cta" type="submit">Angebot anfragen <svg viewBox="0 0 16 16" aria-hidden="true"><path d="M2 8h11M9 4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.8"/></svg></button>
      <small>oder anrufen: <a href="tel:+496221583041">06221 58 30 41</a><br><span class="hinweis" data-hinweis aria-live="polite">Mo bis Fr, 7 bis 17 Uhr</span></small>
    </div>
  </form>
</main>

<footer class="fuss rahmen">
  <div class="gruppe"><span class="mono">Variante</span>
    <div class="umschalter" role="group" aria-label="Variante" data-varianten>
      <button type="button" data-v="einblenden" aria-pressed="true">Einblenden</button>
      <button type="button" data-v="gestaffelt" aria-pressed="false">Gestaffelt</button>
      <button type="button" data-v="raster" aria-pressed="false">Raster</button>
    </div>
  </div>
  <p class="mono werte" data-werte></p>
</footer>

<svg class="zeiger" data-zeiger viewBox="0 0 18 18" aria-hidden="true"><path d="M2 1.5 15.5 9.2l-6 1.3-2.9 5.6Z" fill="#1a1a1a" stroke="#efedea" stroke-width="1.2" stroke-linejoin="round"/></svg>

<script>
(() => {
  const html = document.documentElement;
  const reduziert = () => html.classList.contains('reduziert');
  const kann = CSS.supports('appearance', 'base-select');
  const form = document.querySelector('[data-anfrage]');
  const leistung = document.getElementById('leistung');
  const farbton = document.getElementById('farbton');
  const zeit = document.getElementById('zeit');
  const werte = document.querySelector('[data-werte]');
  const zeiger = document.querySelector('[data-zeiger]');
  const hinweis = document.querySelector('[data-hinweis]');
  const karteName = document.querySelector('[data-karte-name]');
  const karteNr = document.querySelector('[data-karte-nr]');
  const STAFFEL = { einblenden: 0, gestaffelt: 30, raster: 0 };

  /* Wand folgt dem Farbton */
  function wand() {
    const o = farbton.selectedOptions[0];
    html.style.setProperty('--wand', o.dataset.farbe);
    karteName.textContent = o.querySelector('.titel').textContent;
    karteNr.textContent = 'PROBE ' + o.dataset.nr + ' · MATT';
  }
  farbton.addEventListener('change', wand);

  /* Varianten und Werte */
  function variante(v) {
    form.dataset.variante = v;
    html.style.setProperty('--staffel', STAFFEL[v] + 'ms');
    document.querySelectorAll('[data-v]').forEach(b => b.setAttribute('aria-pressed', b.dataset.v === v));
    zeigeWerte();
  }
  // Browserstand (02.11.2026): base-select in Chrome/Edge ab 135 und Safari ab 27; Firefox zeigt das native select.
  const STAND = '<span>base-select hier <b>' + (kann ? 'ja' : 'nein, natives select') + '</b> · Chrome 135+, Safari 27+</span>';
  function zeigeWerte() {
    const v = form.dataset.variante;
    if (reduziert()) {
      werte.innerHTML = '<span>Reduzierte Bewegung</span><span>Dauer <b>0 ms</b></span><span>Picker erscheint sofort</span>' + STAND;
      return;
    }
    werte.innerHTML =
      '<span>Öffnen <b>300 ms</b> · <b>cubic-bezier(.25, 1, .5, 1)</b> · <b>6 px</b></span>' +
      '<span>Schließen <b>150 ms</b> · <b>cubic-bezier(.4, 0, 1, 1)</b></span>' +
      (v === 'gestaffelt' ? '<span>Stagger <b>30 ms</b> · sibling-index()</span>' : '') +
      (v === 'raster' ? '<span>Picker <b>display: grid</b></span>' : '') +
      '<span>Icon <b>1000 ms</b> · <b>cubic-bezier(.65, 0, .35, 1)</b></span>' + STAND;
  }
  document.querySelectorAll('[data-v]').forEach(b => b.addEventListener('click', () => { stopp(); variante(b.dataset.v); }));

  form.addEventListener('submit', e => {
    e.preventDefault();
    hinweis.textContent = 'Demo: Es wurde nichts gesendet.';
  });

  /* ——— Vorführung ———
     Ein Skript darf den nativen Picker ohne Nutzergeste nicht öffnen (showPicker braucht eine Geste).
     Darum zeigt die Vorführung ein Doppel mit denselben Werten; wer selbst klickt, bedient das echte select. */
  let uhren = [], doppel = null, laeuft = false, runde = 0;
  const nach = (ms, fn) => uhren.push(setTimeout(fn, ms));
  const mitte = el => { const r = el.getBoundingClientRect(); return { x: r.left + r.width * .62, y: r.top + r.height * .55 }; };
  function zeigerZu(el, sofort) {
    const p = mitte(el);
    if (sofort) { zeiger.style.transition = 'none'; zeiger.getBoundingClientRect(); }
    zeiger.style.transform = `translate(${p.x}px, ${p.y}px)`;
    if (sofort) { zeiger.getBoundingClientRect(); zeiger.style.transition = ''; }
  }
  function klick(el) {
    zeiger.classList.add('klick'); el.classList.add('gedrueckt');
    setTimeout(() => { zeiger.classList.remove('klick'); el.classList.remove('gedrueckt'); }, 150);
  }
  function oeffnen(sel) {
    schliessen(true);
    if (!kann) return;
    const d = document.createElement('div');
    d.className = 'doppel offen'; d.dataset.fuer = sel.id; d.setAttribute('aria-hidden', 'true');
    [...sel.options].forEach((o, i) => {
      const e = document.createElement('div');
      e.className = 'opt' + (o.selected ? ' gewaehlt' : '');
      e.style.setProperty('--n', i);
      e.dataset.wert = o.value;
      e.innerHTML = o.innerHTML;
      d.append(e);
    });
    sel.classList.add('zeigt');
    sel.closest('.feld').append(d);
    // wie max-block-size: stretch beim echten Picker: höchstens bis zum unteren Fensterrand
    d.style.maxHeight = Math.max(160, innerHeight - sel.getBoundingClientRect().bottom - 14) + 'px';
    d.style.overflow = 'hidden';
    doppel = d;
  }
  function schliessen(sofort) {
    document.querySelectorAll('select.zeigt').forEach(s => s.classList.remove('zeigt'));
    const d = doppel; doppel = null;
    if (!d) return;
    if (sofort) { d.remove(); return; }
    d.classList.remove('offen');
    setTimeout(() => d.remove(), 200);
  }
  function opt(wert) { return doppel && doppel.querySelector(`[data-wert="${wert}"]`); }
  function ueber(wert) {
    if (!doppel) return;
    doppel.querySelectorAll('.opt').forEach(o => o.classList.toggle('ueber', o.dataset.wert === wert));
    const o = opt(wert); if (o) zeigerZu(o);
  }
  function waehlen(sel, wert) {
    sel.value = wert;
    sel.dispatchEvent(new Event('change', { bubbles: true }));
  }

  const RUNDEN = [
    { v: 'einblenden', l: ['tueren', 'fassade'], f: ['ocker', 'buntsandstein'] },
    { v: 'gestaffelt', l: ['tapete', 'putz'], f: ['salbei', 'tintenblau'] },
    { v: 'raster', l: ['putz', 'tueren'], f: ['lehm', 'graphit'] },
    { v: 'einblenden', l: ['fassade', 'innen'], f: ['tintenblau', 'neckarkiesel'] },
    { v: 'gestaffelt', l: ['putz', 'treppenhaus'], f: ['buntsandstein', 'ocker'] },
    { v: 'raster', l: ['tapete', 'fassade'], f: ['kalkweiss', 'buntsandstein'] }
  ];
  function spielRunde() {
    const r = RUNDEN[runde % RUNDEN.length]; runde++;
    variante(r.v);
    // 1. Leistung wählen
    nach(0, () => zeigerZu(leistung));
    nach(550, () => { klick(leistung); oeffnen(leistung); });
    nach(1050, () => ueber(r.l[0]));
    nach(1650, () => ueber(r.l[1]));
    nach(2900, () => { if (opt(r.l[1])) klick(opt(r.l[1])); waehlen(leistung, r.l[1]); schliessen(); });
    // 2. Farbton wählen, die Wand zieht nach
    nach(3900, () => zeigerZu(farbton));
    nach(4450, () => { klick(farbton); oeffnen(farbton); });
    nach(4950, () => ueber(r.f[0]));
    nach(5550, () => ueber(r.f[1]));
    nach(6500, () => { if (opt(r.f[1])) klick(opt(r.f[1])); waehlen(farbton, r.f[1]); schliessen(); });
    nach(7300, () => zeigerZu(zeit));
    nach(8600, spielRunde);
  }
  function stopp() {
    uhren.forEach(clearTimeout); uhren = [];
    schliessen(true);
    laeuft = false; html.classList.remove('vorfuehrung');
  }

  function neustart() {
    stopp();
    runde = 0;
    leistung.value = 'innen'; farbton.value = 'neckarkiesel'; zeit.value = '3m';
    variante('einblenden'); wand();
    hinweis.textContent = 'Mo bis Fr, 7 bis 17 Uhr';
  }
  function vorfuehren() {
    neustart();
    laeuft = true; html.classList.add('vorfuehrung');
    zeigerZu(leistung.closest('.feld').querySelector('label'), true);
    spielRunde();
  }
  function pause() { uhren.forEach(clearTimeout); uhren = []; }
  function weiter() { if (laeuft) { schliessen(true); spielRunde(); } else vorfuehren(); }

  // Wer selbst eingreift, beendet die Vorführung
  addEventListener('pointerdown', e => { if (e.isTrusted && laeuft) stopp(); }, true);
  addEventListener('keydown', e => { if (e.isTrusted && laeuft) stopp(); }, true);
  new MutationObserver(zeigeWerte).observe(html, { attributes: true, attributeFilter: ['class'] });

  variante('einblenden'); wand();

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