Zum Inhalt
← Glas & Material

Neo-Skeuomorphismus / taktile UI (Material mit Lichtkante)

Kaufbox einer Hautpflege-Manufaktur, in der jedes Bedienelement wie ein Objekt wirkt: Lichtkante oben, Haarlinie, gestaffelte Schatten, beim Drücken sinkt es ein.

Flächen, Kacheln und Knöpfe wirken wie physische Objekte. Eine Lichtquelle von oben erzeugt eine feine helle Innenkante (inset 0 1px, Weiß mit 5–30 % Deckkraft). Dazu kommen ein Haarlinien-Ring (inset 0 0 0 1px oder .5px) und mehrere weiche Außenschatten mit gestaffelten Radien. Die Fläche trägt einen leichten Verlauf von hell nach dunkel oder einen radialen Glanz statt einer flachen Farbe. Beim Drücken verschwindet die Lichtkante, der Schatten wird kürzer und das Element sinkt 1–2 px ein. Anders als der Neumorphismus von 2020 (Ton in Ton, zu wenig Kontrast) arbeitet die heutige Form mit klaren Konturen, dunklen Gründen oder Glas und bleibt lesbar. Treiber sind Apples Liquid Glass (WWDC 2025) und App-Icon-artige Kacheln auf Produktseiten.

Nach zehn Jahren Flat Design signalisieren Tiefe und Lichtkanten wieder Anfassbarkeit und Wert. Was klickbar ist, erkennt man ohne Erklärung. Die feine Lichtkante kostet kaum etwas, wirkt aber wie sorgfältig gerendertes Material.

Wann einsetzen

Produkt- und App-Seiten: Icons, Feature-Kacheln, Keycaps, Fensterrahmen für Screenshots. Preiskarten, primäre Knöpfe, Kaufboxen und dunkle Themes. Templates, die ohne Fotos hochwertig wirken sollen.

Wann nicht

Lange Formulare, Tabellen und Einstellungen: dort flach bleiben. Helle Ton-in-Ton-Gestaltung, bei der Zustände nur über Schatten erkennbar wären. Marken mit bewusst flacher, grafischer Sprache.

Varianten

  • App-Icon-Kacheln mit Lichtkante und Doppelring (Raycast)
  • Fensterrahmen für Produkt-Screenshots: Lichtkante oben, Haarlinie, zwei tiefe Außenschatten (Sunsama)
  • Glas-Navigation mit innerer Lichtkante und Weißring (mymind, siehe frosted-glass-erweitert)
  • Keycaps und Tastenkürzel als gedrückte Tasten mit dunklerer Unterkante
  • Claymorphism: aufgeblasene, weiche 3D-Icons (eher Illustration als CSS)
  • Klassischer Neumorphismus (heller und dunkler Zwillingsschatten): nur noch mit deutlicher Kontur vertretbar

Umsetzung

Schatten als Token-Satz definieren, nicht pro Komponente: --edge-light: inset 0 1px rgb(255 255 255 / .1); --edge-ring: inset 0 0 0 1px rgb(255 255 255 / .06); --lift-1: 0 1px 4px rgb(0 0 0 / .4); --lift-2: 0 0 20px rgb(0 0 0 / .33). Kachel: background: linear-gradient(180deg, hsl(0 0% 100% / .06), transparent) über der Grundfarbe; box-shadow: var(--lift-1), var(--lift-2), var(--edge-light), var(--edge-ring). Gedrückt (:active und eine Klasse per pointerdown): Lichtkante auf Deckkraft 0, Außenschatten auf 0 1px 2px, translate: 0 1px, 80–120 ms ease-out; zurück in 150–200 ms. Gedrückte und normale Schattenliste mit gleicher Anzahl und Reihenfolge anlegen, sonst springt der Übergang. Helles Thema: Lichtkante kräftiger (.5–.9), Außenschatten weicher mit negativem Spread (0 40px 80px -20px). Die Lichtrichtung bleibt auf der ganzen Seite gleich, auch in Bildern.

Robust umsetzen

Telefon. Unverändert nutzbar. Große Blur-Radien bei vielen Elementen in Scroll-Listen sparsam einsetzen (Repaint). Den Druckzustand über pointerdown setzen, da es keinen Hover gibt und :active auf Mobilgeräten unzuverlässig greift.

Ohne Bewegung. Kaum Bewegung. Das Einsinken darf bleiben, Übergänge auf 0–50 ms kürzen.

Barrierefrei. Zustände nie nur über Schatten unterscheiden: gewählte Optionen zusätzlich über Textfarbe, Gewicht oder Markierung zeigen. Bedienelemente brauchen 3:1 Kontrast zum Grund (WCAG 1.4.11), Text 4.5:1. Fokusring zusätzlich zum Schatten. Echte Rollen setzen (aria-pressed, role="switch", role="radiogroup"). Bei Glas prefers-reduced-transparency beachten und eine opake Fläche als Fallback anbieten.

Code

<!DOCTYPE html>
<!--
  Neo-Skeuomorphismus / taktile UI (Material mit Lichtkante)
  Fundus · Glas & Material · https://cecodesign.de/lab/fundus/glas-material/neo-skeuomorph-taktile-ui/
  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>Neo-Skeuomorphismus / taktile UI · 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: 'Zalando Sans'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/zalando-sans:vf@latest/latin-wdth-normal.woff2') format('woff2-variations'); font-weight: 200 900; font-stretch: 75% 125%; font-display: swap; }
@font-face { font-family: 'Fragment Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/fragment-mono@latest/latin-400-normal.woff2') format('woff2'); font-weight: 400; font-display: swap; }

/* Welt: Rezept „mode-beauty-kollektion“. VERRA, Hautpflege-Manufaktur aus Regensburg.
   Material-Token je Variante (Messwerte aus meta.json):
   Porzellan = Sunsama-Rahmen hell, Onyx = Raycast-Kachel dunkel, Glas = mymind-Glasplatte. */
:root {
  --kurve: cubic-bezier(0, 0, 0.58, 1);       /* ease-out: Drücken 100 ms, Rückkehr 180 ms */
  --bild: cubic-bezier(0.25, 1, 0.5, 1);      /* Rezept: Bild- und Flächenwechsel */
  --druck-ms: 100ms; --zurueck-ms: 180ms; --gleit-ms: 240ms;
  --tief: 1px; --tief-gross: 2px;
}
body[data-variante='porzellan'] {
  --grund: #f3efe9; --text: #121010; --meta: #6b645b; --linie: #d6d0c7;
  --flaeche: linear-gradient(180deg, #ffffff, #f4f0ea);
  --kante: inset 0 1px 0 0 rgba(255, 255, 255, .9);
  --kante-aus: inset 0 1px 0 0 rgba(255, 255, 255, 0);
  --ring: 0 0 0 1px rgba(18, 16, 16, .08);
  --lift: 0 1px 2px rgba(18, 16, 16, .07), 0 8px 18px -8px rgba(18, 16, 16, .24);
  --druck: 0 1px 2px rgba(18, 16, 16, .14), 0 0 0 0 rgba(18, 16, 16, 0);
  --mulde-flaeche: #e9e4dc;
  --mulde: inset 0 1px 2px rgba(18, 16, 16, .10), inset 0 0 0 1px rgba(18, 16, 16, .06);
  /* großer Rahmen: Sunsama .sslab-window.has-frame */
  --rahmen: inset 0 1px 0 0 rgba(255, 255, 255, .5), 0 0 0 1px rgba(0, 0, 0, .04), 0 40px 80px -20px rgba(0, 0, 0, .22), 0 8px 24px -8px rgba(0, 0, 0, .14);
  --buehne: radial-gradient(120% 70% at 50% 0%, #fbf4ea 0%, transparent 62%), linear-gradient(180deg, #efe4d6, #e0cfbb);
  --p-flaeche: linear-gradient(180deg, #2a2423, #121010);
  --p-text: #f6f3ee;
  --p-kante: inset 0 1px 0 0 rgba(255, 255, 255, .22), inset 0 0 0 1px rgba(255, 255, 255, .04);
  --p-kante-aus: inset 0 1px 0 0 rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(255, 255, 255, .04);
  --p-lift: 0 0 0 1px #121010, 0 1px 2px rgba(18, 16, 16, .3), 0 10px 22px -10px rgba(18, 16, 16, .6);
  --p-druck: 0 0 0 1px #121010, 0 1px 1px rgba(18, 16, 16, .3), 0 0 0 0 rgba(18, 16, 16, 0);
  --taste-kante: #cfc8bd;
  --akzent: #a85a1f; --glut: inset 0 0 15px 2px rgba(214, 132, 63, .22), inset 0 .5px 0 rgba(255, 228, 200, .9);
  --an: #3d2a1c;
  --zeiger-f: #121010; --zeiger-r: #f6f3ee;
}
body[data-variante='onyx'] {
  --grund: #141011; --text: #f3efe9; --meta: rgba(243, 239, 233, .62); --linie: rgba(243, 239, 233, .12);
  --flaeche: linear-gradient(180deg, hsl(0 0% 100% / .06), hsl(0 0% 100% / 0)), #1d1819;
  --kante: inset 0 1px 0 0 #ffffff14;
  --kante-aus: inset 0 1px 0 0 #ffffff00;
  --ring: inset 0 0 0 1px #ffffff0d;
  --lift: 0 1px 4px #0006, 0 0 20px #00000054;
  --druck: 0 1px 2px #0006, 0 0 0 #00000000;
  --mulde-flaeche: #0e0b0c;
  --mulde: inset 0 1px 3px rgba(0, 0, 0, .6), inset 0 0 0 1px #ffffff0a;
  /* großer Rahmen: Raycast-Panel */
  --rahmen: inset 0 .5px 0 0 #ffffff4d, 0 0 0 .5px #000c, 0 4px 40px 8px #0006, 0 0 0 0 transparent;
  --buehne: radial-gradient(70% 55% at 50% 0%, rgba(214, 132, 63, .30) 0%, transparent 70%), linear-gradient(180deg, #2a1f1c, #151011);
  --p-flaeche: linear-gradient(180deg, #e3965a, #c8742f);
  --p-text: #1a0f08;
  /* Raycast: farbiges Innenglühen statt weißer Kante */
  --p-kante: inset 0 0 15px 2px #ff9a4940, inset 0 .5px 0 0 #ffdcc0d0;
  --p-kante-aus: inset 0 0 15px 2px #ff9a4920, inset 0 .5px 0 0 #ffdcc000;
  --p-lift: 0 0 0 1px #00000099, 0 1px 4px #0006, 0 0 20px #00000054;
  --p-druck: 0 0 0 1px #00000099, 0 1px 2px #0006, 0 0 0 #00000000;
  --taste-kante: #0a0808;
  --akzent: #e9a46b; --glut: inset 0 0 15px 2px #ff9a4938, inset 0 .5px 0 #ffd2a885;
  --an: #c8742f;
  --zeiger-f: #f3efe9; --zeiger-r: #141011;
}
body[data-variante='glas'] {
  --grund: #e7c9a8; --text: #121010; --meta: #5a4c40; --linie: rgba(18, 16, 16, .14);
  --flaeche: linear-gradient(180deg, rgba(255, 252, 247, .6), rgba(255, 249, 242, .36));
  /* mymind: innere Lichtkante + Weißring, kühler Außenschatten */
  --kante: inset 0 1px 1px rgba(255, 255, 255, .9);
  --kante-aus: inset 0 1px 1px rgba(255, 255, 255, 0);
  --ring: inset 0 0 0 1px rgba(255, 255, 255, .55);
  --lift: 0 8px 30px rgb(116 130 151 / 18%), 0 0 0 0 transparent;
  --druck: 0 1px 3px rgb(116 130 151 / 26%), 0 0 0 0 transparent;
  --mulde-flaeche: rgba(120, 82, 52, .14);
  --mulde: inset 0 1px 2px rgba(80, 50, 30, .16), inset 0 0 0 1px rgba(255, 255, 255, .35);
  --rahmen: inset 0 0 0 1px rgba(255, 255, 255, .55), inset 0 1px 1px rgba(255, 255, 255, .9), 0 8px 30px rgb(116 130 151 / 18%), 0 0 0 0 transparent;
  --buehne: linear-gradient(180deg, rgba(255, 250, 244, .34), rgba(255, 244, 232, .14));
  --p-flaeche: linear-gradient(180deg, rgba(40, 30, 26, .92), rgba(18, 16, 16, .92));
  --p-text: #f6f3ee;
  --p-kante: inset 0 1px 1px rgba(255, 255, 255, .35), inset 0 0 0 1px rgba(255, 255, 255, .10);
  --p-kante-aus: inset 0 1px 1px rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(255, 255, 255, .10);
  --p-lift: 0 0 0 1px rgba(18, 16, 16, .6), 0 1px 2px rgba(18, 16, 16, .25), 0 8px 30px rgb(116 130 151 / 30%);
  --p-druck: 0 0 0 1px rgba(18, 16, 16, .6), 0 1px 2px rgba(18, 16, 16, .25), 0 0 0 0 transparent;
  --taste-kante: rgba(120, 82, 52, .35);
  --akzent: #8a4515; --glut: inset 0 0 15px 2px rgba(214, 132, 63, .28), inset 0 .5px 0 rgba(255, 255, 255, .9);
  --an: #3d2a1c;
  --zeiger-f: #121010; --zeiger-r: #fbf6ee;
}

* { box-sizing: border-box; margin: 0; }
html { background: #f3efe9; font-family: 'Zalando Sans', system-ui, sans-serif; font-stretch: 100%; -webkit-font-smoothing: antialiased; }
body {
  min-height: 100svh; background: var(--grund); color: var(--text);
  display: grid; grid-template-rows: auto 1fr auto; gap: clamp(14px, 2.2vw, 28px);
  padding: clamp(14px, 2.6vw, 36px) clamp(16px, 3.4vw, 48px);
  transition: background-color .6s var(--bild), color .6s var(--bild);
  position: relative; overflow-x: hidden;
}
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
button:focus-visible { outline: 2px solid var(--text); outline-offset: 3px; }
.mono { font: 400 11px/1.35 'Fragment Mono', ui-monospace, monospace; letter-spacing: .06em; text-transform: uppercase; color: var(--meta); }
.ziffern { font-variant-numeric: tabular-nums; }

/* Kampagnenfeld hinter dem Glas */
.feld { position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: 0; transition: opacity 1s var(--bild); overflow: hidden;
  background:
    radial-gradient(34% 40% at 80% 18%, #fbf0e2 0%, rgba(251, 240, 226, 0) 70%),
    radial-gradient(40% 50% at 12% 88%, #c99a80 0%, rgba(201, 154, 128, 0) 70%),
    radial-gradient(50% 60% at 55% 60%, #dfbea2 0%, rgba(223, 190, 162, 0) 72%),
    linear-gradient(165deg, #efe3d5 0%, #dcc3aa 55%, #cfae93 100%); }
.feld i { position: absolute; border-radius: 50%; }
.feld .sonne { width: 34vmax; aspect-ratio: 1; right: -10vmax; top: -14vmax; background: radial-gradient(circle at 40% 40%, #fbe6cc, #e9b98d 60%, rgba(233, 185, 141, 0) 72%); }
.feld .wort { position: absolute; left: 1vw; top: 50%; translate: 0 -50%; font-weight: 300; font-stretch: 125%; font-size: min(26vw, 70vh); line-height: .8; letter-spacing: -.02em; color: #a85a35; opacity: .55; white-space: nowrap; font-style: normal; }
body[data-variante='glas'] .feld { opacity: 1; }
body > :not(.feld):not(.zeiger) { position: relative; z-index: 1; }

/* ---------- Material ---------- */
.taktil {
  background: var(--flaeche);
  box-shadow: var(--lift), var(--kante), var(--ring);
  transition: box-shadow var(--zurueck-ms) var(--kurve), translate var(--zurueck-ms) var(--kurve), background-color .6s var(--bild);
}
.taktil:active, .taktil.gedrueckt {
  box-shadow: var(--druck), var(--kante-aus), var(--ring);
  translate: 0 var(--tief);
  transition-duration: var(--druck-ms);
}
.glas .taktil, body[data-variante='glas'] .taktil, body[data-variante='glas'] .rahmen, body[data-variante='glas'] .kopf-leiste {
  -webkit-backdrop-filter: blur(22px) saturate(180%); backdrop-filter: blur(22px) saturate(180%);
}
.mulde { background: var(--mulde-flaeche); box-shadow: var(--mulde); transition: background-color .6s var(--bild); }

/* ---------- Kopf ---------- */
.kopf { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.kopf-leiste { display: flex; align-items: center; gap: clamp(16px, 3vw, 40px); padding: 8px 8px 8px 20px; border-radius: 999px; }
body[data-variante='glas'] .kopf-leiste { background: var(--flaeche); box-shadow: var(--rahmen); }
.marke { font-weight: 400; font-stretch: 125%; font-size: 17px; letter-spacing: .26em; line-height: 1; }
.marke small { display: block; margin-top: 4px; font: 400 9px/1 'Fragment Mono', monospace; letter-spacing: .14em; color: var(--meta); }
.nav { display: flex; gap: 26px; font-size: 14px; }
.nav a { color: inherit; text-decoration: none; }
.tasche { display: inline-flex; align-items: center; gap: 10px; height: 40px; padding: 0 8px 0 16px; border-radius: 999px; font-size: 14px; }
.tasche b { display: grid; place-items: center; min-width: 24px; height: 24px; border-radius: 999px; font: 400 12px/1 'Fragment Mono', monospace; background: var(--mulde-flaeche); box-shadow: var(--mulde); }
.tasche.neu b { background: var(--an); color: var(--p-text); }
body[data-variante='onyx'] .tasche.neu b { color: #1a0f08; }

/* ---------- Bühne + Kaufbox ---------- */
.shop { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: clamp(20px, 4vw, 64px); align-items: center; max-width: 1240px; width: 100%; margin: 0 auto; }
.rahmen { position: relative; border-radius: 22px; box-shadow: var(--rahmen); background: var(--buehne); overflow: hidden; aspect-ratio: 5 / 5.4; width: min(100%, 600px, calc(66svh * 5 / 5.4)); justify-self: center; transition: background .6s var(--bild); }
.rahmen .mono { position: absolute; z-index: 2; }
.rahmen .ol { left: 20px; top: 18px; } .rahmen .or { right: 20px; top: 18px; text-align: right; } .rahmen .ul { left: 20px; bottom: 18px; } .rahmen .ur { right: 20px; bottom: 18px; }
.licht { position: absolute; inset: 0; background: repeating-linear-gradient(115deg, rgba(255, 255, 255, 0) 0 9%, rgba(255, 255, 255, .16) 9% 15%, rgba(255, 255, 255, 0) 15% 24%); mask: linear-gradient(180deg, #000 0%, transparent 70%); opacity: .9; }
body[data-variante='onyx'] .licht { opacity: .2; }
.sockel { position: absolute; left: 50%; bottom: 12%; width: 54%; height: 15%; translate: -50% 0; }
.sockel::before { content: ''; position: absolute; inset: 22% 0 0; border-radius: 0 0 50% 50% / 0 0 40% 40%; background: linear-gradient(90deg, #cdbba5, #e6d8c6 45%, #c5b19a); box-shadow: 0 30px 40px -18px rgba(60, 35, 15, .45); }
.sockel::after { content: ''; position: absolute; left: 0; right: 0; top: 0; height: 44%; border-radius: 50%; background: radial-gradient(60% 80% at 45% 30%, #fbf3e8, #e3d3bf); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9), inset 0 -2px 4px rgba(120, 90, 60, .25); }
body[data-variante='onyx'] .sockel::before { background: linear-gradient(90deg, #1b1516, #3a302d 45%, #191314); box-shadow: 0 30px 40px -18px rgba(0, 0, 0, .8); }
body[data-variante='onyx'] .sockel::after { background: radial-gradient(60% 80% at 45% 30%, #4a3e39, #261f1e); box-shadow: inset 0 1px 0 #ffffff26, inset 0 -2px 4px rgba(0, 0, 0, .4); }
.flakon { position: absolute; left: 50%; bottom: calc(12% + 15% * .7); height: 66%; aspect-ratio: 160 / 330; translate: -50% 0; transform-origin: 50% 100%; transition: scale 1s var(--bild); }
.flakon svg { display: block; width: 100%; height: 100%; overflow: visible; }

.kauf { display: grid; gap: clamp(14px, 1.8vw, 22px); max-width: 520px; }
.kauf h1 { font-weight: 400; font-stretch: 125%; font-size: clamp(2.1rem, 4.4vw, 4.2rem); line-height: 1.02; letter-spacing: -.01em; }
.formel { color: var(--text); opacity: .86; }
.formel span { white-space: nowrap; }
.text { font-size: 15px; line-height: 1.5; max-width: 42ch; text-wrap: pretty; color: var(--text); opacity: .82; }

/* Wirkstoff-Kacheln: App-Icon-Prinzip, Doppelring + Lichtkante */
.stoffe { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.kachel { position: relative; width: 58px; height: 58px; border-radius: 15px; display: grid; place-content: center; text-align: center; }
.kachel strong { font-weight: 400; font-stretch: 125%; font-size: 20px; line-height: 1; }
.kachel small { display: block; margin-top: 3px; font: 400 9px/1 'Fragment Mono', monospace; letter-spacing: .04em; color: var(--meta); }
.kachel[aria-pressed='true'] { box-shadow: var(--lift), var(--glut), var(--ring); }
.kachel[aria-pressed='true'].gedrueckt, .kachel[aria-pressed='true']:active { box-shadow: var(--druck), var(--glut), var(--ring); }
.kachel[aria-pressed='true'] strong { color: var(--akzent); }
.kachel::after { content: ''; position: absolute; left: 50%; bottom: -9px; width: 14px; height: 2px; border-radius: 2px; translate: -50% 0; background: var(--akzent); opacity: 0; transition: opacity var(--zurueck-ms) var(--kurve); }
.kachel[aria-pressed='true']::after { opacity: 1; }
.stoff-info { flex: 1 1 180px; min-width: 0; font-size: 13.5px; line-height: 1.4; color: var(--text); opacity: .82; padding-left: 6px; }

/* Größe: Mulde mit gleitendem, erhabenem Daumen */
.reihe { display: grid; gap: 8px; }
.reihe-kopf { display: flex; justify-content: space-between; }
.segment { position: relative; display: grid; grid-template-columns: repeat(3, 1fr); padding: 4px; border-radius: 14px; }
.segment .daumen { position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc((100% - 8px) / 3); border-radius: 10px;
  transition: box-shadow var(--zurueck-ms) var(--kurve), translate var(--gleit-ms) var(--kurve), background-color .6s var(--bild); }
.segment .daumen.gedrueckt { translate: var(--x, 0) var(--tief); transition: box-shadow var(--druck-ms) var(--kurve), translate var(--gleit-ms) var(--kurve); }
.segment .daumen { translate: var(--x, 0) 0; }
.segment button { position: relative; z-index: 1; height: 46px; display: grid; place-content: center; gap: 2px; font-size: 15px; line-height: 1; color: var(--meta); transition: color var(--zurueck-ms) var(--kurve); }
.segment button small { font: 400 10px/1 'Fragment Mono', monospace; letter-spacing: .04em; }
.segment button[aria-checked='true'] { color: var(--text); font-weight: 500; }

/* Abo-Schalter */
.abo { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; border-top: 1px solid var(--linie); border-bottom: 1px solid var(--linie); }
.abo p { font-size: 14px; line-height: 1.35; }
.abo p .mono { display: block; margin-top: 2px; }
.schalter { position: relative; flex: none; width: 60px; height: 34px; border-radius: 999px; transition: background-color var(--gleit-ms) var(--kurve); }
.schalter[aria-checked='true'] { background: var(--an); }
.schalter .knopf { position: absolute; top: 3px; left: 3px; width: 28px; height: 28px; border-radius: 999px; translate: 0 0;
  transition: box-shadow var(--zurueck-ms) var(--kurve), translate var(--gleit-ms) var(--kurve), width var(--druck-ms) var(--kurve); }
.schalter[aria-checked='true'] .knopf { translate: 26px 0; }
.schalter.gedrueckt .knopf, .schalter:active .knopf { box-shadow: var(--druck), var(--kante-aus), var(--ring); transition-duration: var(--druck-ms), var(--gleit-ms), var(--druck-ms); }
body[data-variante='porzellan'] .schalter[aria-checked='true'] .knopf, body[data-variante='glas'] .schalter[aria-checked='true'] .knopf { background: linear-gradient(180deg, #fff, #f1ece4); }

/* Menge: Keycaps mit dunklerer Unterkante */
.kaufen { display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: stretch; }
.menge { display: flex; align-items: center; gap: 4px; padding: 5px; border-radius: 14px; }
.menge output { min-width: 30px; text-align: center; font-size: 16px; }
.taste { width: 44px; height: 44px; border-radius: 10px; font-size: 20px; line-height: 1; font-weight: 300;
  background: var(--flaeche);
  box-shadow: 0 2px 0 0 var(--taste-kante), 0 3px 6px -2px rgba(0, 0, 0, .25), var(--kante), var(--ring);
  transition: box-shadow var(--zurueck-ms) var(--kurve), translate var(--zurueck-ms) var(--kurve); }
.taste:active, .taste.gedrueckt { translate: 0 var(--tief-gross); box-shadow: 0 0 0 0 var(--taste-kante), 0 1px 2px -1px rgba(0, 0, 0, .25), var(--kante-aus), var(--ring); transition-duration: var(--druck-ms); }

/* Primärer Knopf */
.primaer { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 56px; padding: 0 22px; border-radius: 14px; font-size: 16px; font-weight: 500;
  background: var(--p-flaeche); color: var(--p-text);
  box-shadow: var(--p-lift), var(--p-kante);
  transition: box-shadow var(--zurueck-ms) var(--kurve), translate var(--zurueck-ms) var(--kurve); }
.primaer:active, .primaer.gedrueckt { translate: 0 var(--tief-gross); box-shadow: var(--p-druck), var(--p-kante-aus); transition-duration: var(--druck-ms); }
.primaer .preis { font: 400 14px/1 'Fragment Mono', monospace; letter-spacing: .02em; opacity: .9; }
.primaer:focus-visible { outline-color: var(--text); }
.fuss { display: flex; flex-wrap: wrap; gap: 6px 18px; }

/* ---------- Steuerung der Demo ---------- */
.steuer { display: flex; align-items: center; justify-content: space-between; gap: 12px 24px; flex-wrap: wrap; max-width: 1240px; width: 100%; margin: 0 auto; }
.modi { display: flex; gap: 4px; }
.modi button { font: inherit; letter-spacing: inherit; text-transform: inherit; color: var(--meta); padding: 6px 10px; border-radius: 999px; border: 1px solid var(--linie); }
.modi button[aria-pressed='true'] { color: var(--grund); background: var(--text); border-color: var(--text); }
body[data-variante='glas'] .modi button[aria-pressed='true'] { color: #f6f3ee; }
.werte { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 3px 14px; max-width: 760px; font-size: 10px; }
.werte b { color: var(--text); font-weight: 400; }

.zeiger { position: fixed; left: 0; top: 0; z-index: 9; width: 20px; height: 20px; pointer-events: none; display: none; transition: scale var(--druck-ms) var(--kurve); }
.zeiger.drueckt { scale: .82; }
.vorfuehrung .zeiger { display: block; }

/* Hover nur mit Maus */
@media (hover: hover) {
  .taktil:hover:not(:active):not(.gedrueckt) { filter: brightness(1.025); }
  .primaer:hover:not(:active) { filter: brightness(1.08); }
}
/* Touch: Druckzustand per pointerdown (Klasse .gedrueckt), größere Zielflächen */
@media (hover: none) {
  .taktil:active:not(.gedrueckt) { translate: 0 var(--tief); }
  .segment button { height: 48px; }
}

/* Glas-Fallback */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body[data-variante='glas'] { --flaeche: #fbf6ef; --buehne: linear-gradient(180deg, #f6ebde, #ead6c2); }
}
@media (prefers-reduced-transparency: reduce) {
  body[data-variante='glas'] { --flaeche: #fbf6ef; --buehne: linear-gradient(180deg, #f6ebde, #ead6c2); }
  body[data-variante='glas'] .taktil, body[data-variante='glas'] .rahmen, body[data-variante='glas'] .kopf-leiste { backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* Reduzierte Bewegung: Einsinken bleibt, Übergänge 0–50 ms */
.reduziert { --druck-ms: 0ms; --zurueck-ms: 50ms; --gleit-ms: 0ms; }
.reduziert body, .reduziert .feld, .reduziert .rahmen, .reduziert .flakon { transition: none; }

/* Layout */
@media (max-width: 1100px) { .nav { display: none; } }
@media (max-width: 760px) {
  body { grid-template-rows: auto auto auto; }
  .shop { grid-template-columns: 1fr; gap: 22px; }
  .rahmen { aspect-ratio: 1 / .92; width: 100%; }
  .kauf { max-width: none; }
  .marke small { display: none; }
  .kopf-leiste { padding: 0; }
  body[data-variante='glas'] .kopf-leiste { padding: 8px 14px; }
  .werte { justify-content: flex-start; }
  .primaer { padding: 0 16px; font-size: 15px; }
  .stoff-info { flex-basis: 100%; padding: 6px 0 0; }
}
/* Vorschau 960 × 720: nur die Bühne */
.vorschau body { overflow: hidden; height: 100svh; min-height: 0; padding: 20px 32px 18px; gap: 16px; }
.vorschau .werte, .vorschau .fuss, .vorschau .text { display: none; }
.vorschau .rahmen { width: min(100%, 440px); }
.vorschau .kauf { gap: 16px; }
</style>
</head>
<body data-variante="porzellan">
<div class="feld" aria-hidden="true"><i class="sonne"></i><em class="wort">Hell</em></div>

<header class="kopf">
  <div class="kopf-leiste">
    <p class="marke">VERRA<small>Hautpflege · Regensburg</small></p>
    <nav class="nav" aria-label="Hauptmenü"><a href="#pflege">Pflege</a><a href="#rituale">Rituale</a><a href="#manufaktur">Manufaktur</a><a href="#nachfuellen">Nachfüllen</a></nav>
  </div>
  <button class="tasche taktil" type="button" data-tasche>Tasche <b class="ziffern" data-anzahl>0</b></button>
</header>

<main class="shop">
  <figure class="rahmen" aria-label="Serum Hell im Braunglas-Flakon auf einem Kalksteinsockel">
    <div class="licht" aria-hidden="true"></div>
    <p class="mono ol">Nr. 03 · Morgenpflege</p>
    <p class="mono or">Braunglas<br>nachfüllbar</p>
    <p class="mono ul">Abgefüllt in Regensburg</p>
    <p class="mono ur ziffern" data-buehne-ml>30 ml</p>
    <div class="sockel" aria-hidden="true"></div>
    <div class="flakon" data-flakon aria-hidden="true">
      <svg viewBox="0 0 160 330">
        <defs>
          <linearGradient id="glas" x1="0" x2="1">
            <stop offset="0" stop-color="#4a2209"/><stop offset=".18" stop-color="#8a4313"/><stop offset=".42" stop-color="#b8611f"/>
            <stop offset=".62" stop-color="#9a4e18"/><stop offset=".88" stop-color="#5a2a0c"/><stop offset="1" stop-color="#3a1806"/>
          </linearGradient>
          <linearGradient id="kappe" x1="0" x2="1">
            <stop offset="0" stop-color="#0d0b0b"/><stop offset=".3" stop-color="#3b3434"/><stop offset=".55" stop-color="#1b1717"/><stop offset="1" stop-color="#070606"/>
          </linearGradient>
          <linearGradient id="ring" x1="0" x2="1">
            <stop offset="0" stop-color="#6b5a45"/><stop offset=".35" stop-color="#e9dcc4"/><stop offset=".6" stop-color="#a8957a"/><stop offset="1" stop-color="#4f4232"/>
          </linearGradient>
          <linearGradient id="etikett" x1="0" x2="1">
            <stop offset="0" stop-color="#d9cfc0"/><stop offset=".3" stop-color="#f7f2ea"/><stop offset=".75" stop-color="#ece4d8"/><stop offset="1" stop-color="#c9bead"/>
          </linearGradient>
        </defs>
        <filter id="weich" x="-50%" y="-200%" width="200%" height="500%"><feGaussianBlur stdDeviation="5"/></filter>
        <!-- Kontaktschatten auf dem Sockel -->
        <ellipse cx="80" cy="324" rx="74" ry="9" fill="#2a1408" fill-opacity=".45" filter="url(#weich)"/>
        <!-- Pipettenball -->
        <path d="M62 8c0-5 8-8 18-8s18 3 18 8v62H62Z" fill="url(#kappe)"/>
        <path d="M68 10c2-3 6-4 8-4" stroke="#ffffff" stroke-opacity=".35" stroke-width="2.5" stroke-linecap="round" fill="none"/>
        <!-- Ring -->
        <rect x="56" y="66" width="48" height="26" rx="3" fill="url(#ring)"/>
        <rect x="56" y="66" width="48" height="1.2" fill="#fff" fill-opacity=".7"/>
        <!-- Flasche -->
        <path d="M64 92h32v10c0 6 4 9 14 13 26 10 38 22 38 44v146c0 14-9 23-23 23H35c-14 0-23-9-23-23V159c0-22 12-34 38-44 10-4 14-7 14-13Z" fill="url(#glas)"/>
        <!-- Pipette im Glas -->
        <rect x="76" y="100" width="8" height="190" rx="4" fill="#f3d2a6" fill-opacity=".18"/>
        <!-- Lichtkante oben (gleiche Lichtrichtung wie die UI) -->
        <path d="M30 128c8-6 20-10 30-12" stroke="#ffd9a8" stroke-opacity=".75" stroke-width="2" stroke-linecap="round" fill="none"/>
        <rect x="24" y="150" width="7" height="150" rx="3.5" fill="#ffe2bd" fill-opacity=".38"/>
        <rect x="130" y="160" width="3" height="130" rx="1.5" fill="#ffe2bd" fill-opacity=".18"/>
        <!-- Etikett -->
        <rect x="12" y="196" width="136" height="92" fill="url(#etikett)"/>
        <text x="80" y="226" text-anchor="middle" font-family="Zalando Sans, sans-serif" font-stretch="125%" font-size="15" letter-spacing="4.5" fill="#1a1414">VERRA</text>
        <line x1="40" y1="236" x2="120" y2="236" stroke="#1a1414" stroke-opacity=".25" stroke-width=".6"/>
        <text x="80" y="254" text-anchor="middle" font-family="Zalando Sans, sans-serif" font-stretch="125%" font-size="11" fill="#1a1414">Serum Hell</text>
        <text x="80" y="270" text-anchor="middle" font-family="Fragment Mono, monospace" font-size="6.4" letter-spacing=".6" fill="#5c534a">VIT. C 12 % · FERULA 0,5 %</text>
        <text x="80" y="281" text-anchor="middle" font-family="Fragment Mono, monospace" font-size="6.4" letter-spacing=".6" fill="#5c534a" data-etikett-ml>30 ML</text>
      </svg>
    </div>
  </figure>

  <section class="kauf" aria-labelledby="produkt">
    <p class="mono">Seren · Morgenritual</p>
    <h1 id="produkt">Serum Hell</h1>
    <p class="mono formel"><span>Vitamin C 12 %</span> + <span>Ferulasäure 0,5 %</span> + <span>Sanddorn 2 %</span></p>
    <p class="text">Für den Morgen, unter dem Sonnenschutz. Zieht in Sekunden ein und lässt die Haut ruhig und klar aussehen. In kleinen Chargen gerührt, Flakon aus Braunglas.</p>

    <div class="stoffe" role="group" aria-label="Wirkstoffe">
      <button class="kachel taktil" type="button" aria-pressed="true" data-stoff="0"><strong>C</strong><small>12 %</small></button>
      <button class="kachel taktil" type="button" aria-pressed="false" data-stoff="1"><strong>Fe</strong><small>0,5 %</small></button>
      <button class="kachel taktil" type="button" aria-pressed="false" data-stoff="2"><strong>Sd</strong><small>2 %</small></button>
      <p class="stoff-info" data-stoff-info aria-live="polite"></p>
    </div>

    <div class="reihe">
      <div class="reihe-kopf"><span class="mono">Größe</span><span class="mono ziffern" data-ml-preis>30 ml · 1,27 € / ml</span></div>
      <div class="segment mulde" role="radiogroup" aria-label="Größe" data-segment>
        <span class="daumen taktil" data-daumen aria-hidden="true"></span>
        <button type="button" role="radio" aria-checked="false" data-ml="15">15 ml <small class="ziffern">26 €</small></button>
        <button type="button" role="radio" aria-checked="true" data-ml="30">30 ml <small class="ziffern">38 €</small></button>
        <button type="button" role="radio" aria-checked="false" data-ml="50">50 ml <small class="ziffern">54 €</small></button>
      </div>
    </div>

    <div class="abo">
      <p id="abo-text">Im Abo alle acht Wochen<span class="mono">−10 % · jederzeit pausieren</span></p>
      <button class="schalter mulde" type="button" role="switch" aria-checked="false" aria-labelledby="abo-text" data-schalter><span class="knopf taktil" aria-hidden="true"></span></button>
    </div>

    <div class="kaufen">
      <div class="menge mulde" role="group" aria-label="Menge">
        <button class="taste" type="button" aria-label="Weniger" data-minus>−</button>
        <output class="ziffern" data-menge aria-live="polite">1</output>
        <button class="taste" type="button" aria-label="Mehr" data-plus>+</button>
      </div>
      <button class="primaer" type="button" data-kaufen><span data-kaufen-text>In die Tasche</span><span class="preis ziffern" data-preis>38,00 €</span></button>
    </div>
    <p class="mono fuss"><span>Versand in 1–2 Werktagen</span><span>Leere Flakons nehmen wir zurück</span></p>
  </section>
</main>

<footer class="steuer mono">
  <div class="modi" role="group" aria-label="Variante wählen" data-modi>
    <button type="button" data-wahl="porzellan" aria-pressed="true">Porzellan</button>
    <button type="button" data-wahl="onyx" aria-pressed="false">Onyx</button>
    <button type="button" data-wahl="glas" aria-pressed="false">Glas</button>
  </div>
  <p class="werte" data-werte aria-live="polite"></p>
</footer>

<svg class="zeiger" data-zeiger viewBox="0 0 20 20" aria-hidden="true"><path d="M3 2 17 10.2l-6.3 1.4-3 5.9Z" fill="var(--zeiger-f)" stroke="var(--zeiger-r)" stroke-width="1.3" stroke-linejoin="round"/></svg>

<script>
(() => {
  const html = document.documentElement, body = document.body;
  const reduziert = () => html.classList.contains('reduziert');
  const $ = s => document.querySelector(s), $$ = s => [...document.querySelectorAll(s)];

  /* ---------- Daten ---------- */
  const GROESSEN = { 15: { preis: 26, skala: .8 }, 30: { preis: 38, skala: .9 }, 50: { preis: 54, skala: 1 } };
  const STOFFE = [
    'Ascorbylglucosid 12\u00a0%: stabile Vitamin-C-Form, gleicht den Teint aus.',
    'Ferulasäure 0,5\u00a0%: hält das Vitamin C länger wirksam.',
    'Sanddornfruchtöl 2\u00a0% aus Niederbayern: pflegt, ohne zu fetten.'
  ];
  /* Werte, wie sie in der Demo stehen (meta.json → messwerte, kurve) */
  const WERTE = {
    porzellan: [['Lichtkante', 'inset 0 1px 0 rgb(255 255 255 / .9)'], ['Rahmen', 'Sunsama: 0 40px 80px -20px / .22 + 0 8px 24px -8px / .14'], ['Ring', '0 0 0 1px / .08']],
    onyx: [['Kachel', 'Raycast: 0 1px 4px #0006, 0 0 20px #00000054'], ['Lichtkante', 'inset 0 1px #ffffff14'], ['Ring', 'inset 0 0 0 1px #ffffff0d']],
    glas: [['Glas', 'blur(22px) saturate(180%)'], ['Kante', 'inset 0 1px 1px / .9 + inset 0 0 0 1px / .55'], ['Schatten', '0 8px 30px rgb(116 130 151 / 18%)']]
  };
  const BEWEGUNG = [['Drücken', '100 ms'], ['Rückkehr', '180 ms'], ['Kurve', 'cubic-bezier(0, 0, .58, 1)'], ['Einsinken', '1 px · Knopf 2 px']];

  const zustand = { ml: 30, stoff: 0, abo: false, menge: 1, tasche: 0 };
  const eur = n => n.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €';

  /* ---------- Darstellung ---------- */
  const segment = $('[data-segment]'), daumen = $('[data-daumen]'), mlKnoepfe = $$('[data-ml]');
  const kacheln = $$('[data-stoff]'), schalter = $('[data-schalter]');
  function zeichnen() {
    const g = GROESSEN[zustand.ml];
    const i = mlKnoepfe.findIndex(b => +b.dataset.ml === zustand.ml);
    mlKnoepfe.forEach((b, j) => b.setAttribute('aria-checked', String(j === i)));
    daumen.style.setProperty('--x', (i * 100) + '%');
    $('[data-flakon]').style.scale = g.skala;
    $('[data-buehne-ml]').textContent = zustand.ml + ' ml';
    $('[data-etikett-ml]').textContent = zustand.ml + ' ML';
    $('[data-ml-preis]').textContent = `${zustand.ml} ml · ${eur(g.preis / zustand.ml).replace(' €', '')} € / ml`;
    kacheln.forEach((k, j) => k.setAttribute('aria-pressed', String(j === zustand.stoff)));
    $('[data-stoff-info]').textContent = STOFFE[zustand.stoff];
    schalter.setAttribute('aria-checked', String(zustand.abo));
    $('[data-menge]').textContent = zustand.menge;
    const preis = g.preis * zustand.menge * (zustand.abo ? .9 : 1);
    $('[data-preis]').textContent = eur(preis);
    $('[data-anzahl]').textContent = zustand.tasche;
  }
  function werteZeigen() {
    const v = body.dataset.variante;
    const bew = reduziert() ? [['Drücken', '0 ms'], ['Rückkehr', '50 ms'], ['Einsinken', '1 px · Knopf 2 px (bleibt)']] : BEWEGUNG;
    $('[data-werte]').innerHTML = [...WERTE[v], ...bew].map(([k, w]) => `<span>${k} <b>${w}</b></span>`).join('');
  }
  function variante(v) {
    body.dataset.variante = v;
    $$('[data-wahl]').forEach(b => b.setAttribute('aria-pressed', String(b.dataset.wahl === v)));
    werteZeigen();
  }

  /* ---------- Aktionen ---------- */
  const aktionen = {
    groesse: ml => { zustand.ml = ml; zeichnen(); },
    stoff: i => { zustand.stoff = i; zeichnen(); },
    abo: () => { zustand.abo = !zustand.abo; zeichnen(); },
    plus: () => { zustand.menge = Math.min(9, zustand.menge + 1); zeichnen(); },
    minus: () => { zustand.menge = Math.max(1, zustand.menge - 1); zeichnen(); },
    kaufen: () => {
      zustand.tasche += zustand.menge; zeichnen();
      const t = $('[data-kaufen-text]'), tasche = $('[data-tasche]');
      t.textContent = 'In der Tasche'; tasche.classList.add('neu');
      clearTimeout(aktionen.uhr);
      aktionen.uhr = setTimeout(() => { t.textContent = 'In die Tasche'; tasche.classList.remove('neu'); }, 1600);
    }
  };
  mlKnoepfe.forEach(b => b.addEventListener('click', () => aktionen.groesse(+b.dataset.ml)));
  kacheln.forEach(k => k.addEventListener('click', () => aktionen.stoff(+k.dataset.stoff)));
  schalter.addEventListener('click', aktionen.abo);
  $('[data-plus]').addEventListener('click', aktionen.plus);
  $('[data-minus]').addEventListener('click', aktionen.minus);
  $('[data-kaufen]').addEventListener('click', aktionen.kaufen);
  $('[data-tasche]').addEventListener('click', e => e.preventDefault());
  $$('[data-wahl]').forEach(b => b.addEventListener('click', () => { pause(); html.classList.remove('vorfuehrung'); variante(b.dataset.wahl); }));
  segment.addEventListener('keydown', e => {
    if (!['ArrowLeft', 'ArrowRight'].includes(e.key)) return;
    const liste = [15, 30, 50], i = liste.indexOf(zustand.ml) + (e.key === 'ArrowRight' ? 1 : -1);
    if (i < 0 || i > 2) return;
    aktionen.groesse(liste[i]); mlKnoepfe[i].focus(); e.preventDefault();
  });

  /* Druckzustand für alle Zeiger (Touch: :active greift auf Mobilgeräten unzuverlässig) */
  function druckZiel(el) { return el.closest('[data-ml]') ? daumen : el.closest('.taktil, .taste, .primaer, .schalter'); }
  document.addEventListener('pointerdown', e => {
    const b = e.target.closest('button'); if (!b) return;
    const z = druckZiel(b); if (!z) return;
    if (b.dataset.ml) { aktionen.groesse(+b.dataset.ml); }
    z.classList.add('gedrueckt');
    const los = () => { z.classList.remove('gedrueckt'); removeEventListener('pointerup', los); removeEventListener('pointercancel', los); };
    addEventListener('pointerup', los); addEventListener('pointercancel', los);
  });

  /* ---------- Vorführung: gedachter Zeiger drückt sich durch die Kaufbox ---------- */
  const zeiger = $('[data-zeiger]');
  const pos = { x: innerWidth * .7, y: innerHeight * .9 };
  let uhren = [], rafId = 0, runde = 0, laeuft = false;
  const VARIANTEN = ['porzellan', 'onyx', 'glas'];
  const warte = ms => new Promise(r => uhren.push(setTimeout(r, ms)));
  function zeigerSetzen() { zeiger.style.transform = `translate(${pos.x - 3}px, ${pos.y - 2}px)`; }
  function fahre(el, ms = 650) {
    const r = el.getBoundingClientRect();
    const zx = r.left + r.width * .5, zy = r.top + r.height * .55;
    const sx = pos.x, sy = pos.y, t0 = performance.now();
    return new Promise(res => {
      const schritt = t => {
        const p = Math.min(1, (t - t0) / ms), e = p < .5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2;
        pos.x = sx + (zx - sx) * e; pos.y = sy + (zy - sy) * e - Math.sin(p * Math.PI) * 24; zeigerSetzen();
        if (p < 1) rafId = requestAnimationFrame(schritt); else res();
      };
      rafId = requestAnimationFrame(schritt);
    });
  }
  async function druecke(el, aktion, halten = 160) {
    const z = druckZiel(el);
    zeiger.classList.add('drueckt'); z.classList.add('gedrueckt');
    if (el.dataset.ml) aktion();
    await warte(halten);
    zeiger.classList.remove('drueckt'); z.classList.remove('gedrueckt');
    if (!el.dataset.ml) aktion();
    await warte(380);
  }
  function grundzustand() { Object.assign(zustand, { ml: 30, stoff: 0, abo: false, menge: 1 }); zeichnen(); }
  async function schleife(id) {
    while (laeuft && id === runde) {
      for (const v of VARIANTEN) {
        if (!laeuft || id !== runde) return;
        variante(v); grundzustand();
        await warte(700);
        const ablauf = [
          [$('[data-ml="50"]'), () => aktionen.groesse(50)],
          [kacheln[2], () => aktionen.stoff(2)],
          [schalter, aktionen.abo],
          [$('[data-plus]'), aktionen.plus],
          [$('[data-kaufen]'), aktionen.kaufen, 260]
        ];
        for (const [el, fn, h] of ablauf) {
          if (!laeuft || id !== runde) return;
          await fahre(el);
          if (!laeuft || id !== runde) return;
          await druecke(el, fn, h);
        }
        await warte(1100);
      }
    }
  }
  function aufraeumen() { uhren.forEach(clearTimeout); uhren = []; cancelAnimationFrame(rafId); $$('.gedrueckt').forEach(e => e.classList.remove('gedrueckt')); zeiger.classList.remove('drueckt'); }
  function vorfuehren() {
    pause(); runde++; laeuft = true;
    html.classList.add('vorfuehrung');
    pos.x = innerWidth * .72; pos.y = innerHeight * .92; zeigerSetzen();
    zustand.tasche = 0; schleife(runde);
  }
  function pause() { laeuft = false; runde++; aufraeumen(); }
  function weiter() { if (html.classList.contains('vorfuehrung')) vorfuehren(); }
  function neustart() { pause(); html.classList.remove('vorfuehrung'); variante('porzellan'); zustand.tasche = 0; grundzustand(); }

  variante('porzellan'); zeichnen();
  addEventListener('load', werteZeigen);   /* ?reduziert setzt das Rahmen-Skript erst danach */

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