Zum Inhalt
← Zugang & Robustheit

Schalter „Bewegung reduzieren“ mit Pause-Knöpfen

Ein gestalteter Schalter „Bewegung reduzieren“ in der Kopfleiste hält Laufband, Bewegtbild und Karussell sofort an und merkt sich die Wahl; jedes Element hat zusätzlich seinen eigenen Pause-Knopf.

  1. Ein gestalteter Schalter „Bewegung reduzieren“ sitzt fest in der Kopfleiste. Er übernimmt beim ersten Besuch die Systemeinstellung prefers-reduced-motion und merkt sich danach die Wahl der Besucherin per localStorage.
  2. Jede Bewegung, die von selbst startet und länger als fünf Sekunden läuft, hat zusätzlich einen eigenen Pause-Knopf direkt am Element: Laufband, Hintergrund-Loop oder Video, Karussell mit Autoplay.
  3. Alles schaltet sofort und sichtbar: Das Laufband bleibt stehen und zeigt seinen Text vollständig, der Loop friert auf einem ruhigen Bild ein, das Karussell bleibt bedienbar, wechselt aber nicht mehr von selbst.

Die Systemeinstellung kennen viele nicht oder wollen sie nicht für alle Programme ändern. Ein sichtbarer Schalter auf der Seite gibt die Entscheidung dorthin, wo die Bewegung stört. Pause-Knöpfe am Element erfüllen WCAG 2.2.2 auch für Menschen, die nur ein einzelnes Laufband anhalten wollen. Gut gestaltet wirkt das nicht wie ein Pflichtteil, sondern wie Haltung.

Wann einsetzen

Auf jeder Seite mit Laufband, Hintergrundvideo, Loop-Animation oder Karussell mit Autoplay. Besonders bei öffentlichen Häusern, Kultur, Bildung, Verwaltung, Gesundheit und überall, wo das BFSG greifen kann (vom Kunden prüfen lassen).

Wann nicht

Wenn die Seite gar keine selbst startende Bewegung hat: Dann genügt prefers-reduced-motion, ein Schalter ohne Wirkung verwirrt. Nicht als Ersatz für ein ruhiges Grundkonzept: Bewegung, die stört, wird besser gar nicht erst gebaut.

Varianten

  • Globaler Schalter in der Kopfleiste (role="switch") plus Pause-Knöpfe an jedem bewegten Element
  • Nur Pause-Knöpfe am Element, globale Steuerung über prefers-reduced-motion
  • Schalter im Barrierefreiheits-Menü zusammen mit Kontrast und Schriftgröße
  • Karussell nach APG: Start/Stopp-Knopf als erstes Element, Pause bei Hover und Fokus

Umsetzung

Zustand an einer Stelle: <html data-bewegung="ruhig|voll">. Ein Inline-Skript im <head> liest die gespeicherte Wahl (try { localStorage.getItem('…') } catch {}) und fällt sonst auf matchMedia('(prefers-reduced-motion: reduce)') zurück. So läuft nichts erst an und stoppt dann. Schalter: <button role="switch" aria-checked="false">Bewegung reduzieren</button>; ein Klick setzt das Attribut, speichert (ebenfalls in try/catch) und meldet die Wahl über role="status". Jedes bewegte Element hört auf den Zustand und hat zusätzlich einen eigenen Knopf mit wechselndem Namen („Laufband anhalten“ / „Laufband abspielen“). Laufband: Inhalt einmal im DOM, Kopie aria-hidden; angehalten Animation aus und Text umbrechen. Video: video.pause(), Poster bleibt; im Zustand ruhig autoplay gar nicht erst starten. Den Zustand des Video-Knopfs aus video.paused und den Ereignissen play und pause ablesen, nicht aus der CSS-Pseudoklasse :paused: Chrome und Edge kennen sie laut MDN und Web Features Explorer noch nicht (Stand 02.11.2026), auch wenn der web.dev-Digest August 2026 sie als Baseline führt. Wo es sie gibt, darf :paused nur die Optik ergänzen, etwa das Symbol. Canvas oder Lottie: Schleife anhalten und an derselben Stelle fortsetzen. Karussell nach APG: Start/Stopp-Knopf vor den Folien, Timer stoppen, Wechsel ohne Gleiten, aria-live von off auf polite. Auf echten Seiten sollte der Schalter auf jeder Seite an derselben Stelle stehen, damit die Wahl auffindbar bleibt.

Robust umsetzen

Telefon. Schalter bleibt in der Kopfleiste erreichbar, mindestens 44 px hoch. Pause-Knöpfe als Daumenziel am Element, nicht nur beim Hover sichtbar.

Ohne Bewegung. prefers-reduced-motion setzt den Startwert „ruhig“. Der Schalter kann ihn in beide Richtungen überschreiben, die Wahl gilt beim nächsten Besuch wieder.

Barrierefrei. WCAG 2.2.2 Pause, Stop, Hide (A): Pause-Knopf für jede selbst startende Bewegung über 5 s, Fortsetzen an derselben Stelle (Technik G4), Steuerung auf der Seite (G186). WCAG 2.3.3 Animation from Interactions (AAA) wird mit dem globalen Schalter gleich mit bedient. 4.1.2 Name, Role, Value: Schalter als role="switch" mit aria-checked, Pause-Knöpfe mit wechselndem sichtbarem Namen statt nur Symbol. 2.4.7 Focus Visible: alle Knöpfe mit sichtbarem Fokus. 2.5.8 Target Size (AA): Ziele mindestens 24 × 24 px, hier 44 px. 1.4.11 Non-text Contrast: Schalterzustand mit 3:1 und nicht nur über Farbe (Knopf wandert, Text „an/aus“). 4.1.3 Status Messages: Bestätigung per role="status". Karussell nach APG: Start/Stopp vor den Folien, aria-live off beim Laufen, polite im Stand, Pause bei Hover und Fokus.

Code

<!DOCTYPE html>
<!--
  Schalter „Bewegung reduzieren“ mit Pause-Knöpfen
  Fundus · Zugang & Robustheit · https://cecodesign.de/lab/fundus/zugang/bewegung-pausieren-schalter/
  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>Schalter „Bewegung reduzieren“ mit Pause-Knöpfen · Fundus-Demo</title>
<script>document.documentElement.classList.add('js');if(matchMedia('(prefers-reduced-motion: reduce)').matches)document.documentElement.classList.add('reduziert')</script>
<script>/* Startzustand vor dem ersten Bild: gespeicherte Wahl > Systemeinstellung. Speicher kann gesperrt sein (Privatmodus, Richtlinie). */
(function () {
  var h = document.documentElement, wahl = null;
  try { wahl = localStorage.getItem('stiftsspeicher.bewegung'); } catch (e) { wahl = null; }
  if (wahl !== 'ruhig' && wahl !== 'voll') wahl = null;
  if (/[?&]reduziert\b/.test(location.search)) wahl = 'ruhig';   /* Fundus-Detailseite: reduzierte Ansicht erzwingen */
  h.setAttribute('data-bewegung', wahl || (h.classList.contains('reduziert') ? 'ruhig' : 'voll'));
  h.setAttribute('data-quelle', wahl ? 'gespeichert' : 'system');
})();</script>
<style>
@font-face { font-family: 'Schibsted Grotesk'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/schibsted-grotesk:vf@latest/latin-wght-normal.woff2') format('woff2-variations'); font-weight: 400 900; 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: 'IBM Plex Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/ibm-plex-mono@latest/latin-400-normal.woff2') format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url('https://cdn.jsdelivr.net/fontsource/fonts/ibm-plex-mono@latest/latin-500-normal.woff2') format('woff2'); font-weight: 500; font-display: swap; }

:root {
  /* Rezept kultur-programm-haltung: Schwarz auf warmem Off-White, Spartenfarben als System (alle AA auf Off-White und mit weißer Schrift) */
  --tinte: #0b0b0b; --grund: #efede9; --papier: #e4e1da; --leise: #5c5a55; --linie: #0b0b0b; --hauch: rgba(11, 11, 11, .14);
  --musik: #b23a14; --vermittlung: #3d5f9a; --kunst: #2a7243; --film: #b0186b;
  --kurve: cubic-bezier(0.215, 0.61, 0.355, 1);   /* easeOutCubic, kurz und ohne Überschwingen */
  --ui: 200ms; --wechsel: 350ms;
  --mono: 'IBM Plex Mono', ui-monospace, monospace;
  --kopf: 'Schibsted Grotesk', system-ui, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--grund); color: var(--tinte); font: 400 1.125rem/1.6 'Atkinson Hyperlegible Next', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
body { min-height: 100svh; display: flex; flex-direction: column; overflow-x: hidden; }
a { color: inherit; }
button { font: inherit; color: inherit; }
.vh { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.mono { font: 500 .8125rem/1.3 var(--mono); letter-spacing: .04em; font-variant-numeric: tabular-nums; }
.label { font: 500 .75rem/1.2 var(--mono); letter-spacing: .14em; text-transform: uppercase; }

/* Fokus: immer sichtbar, 2 px, Versatz 3 px; auf Schwarz in Off-White */
:focus-visible { outline: 2px solid var(--tinte); outline-offset: 3px; }
.dunkel :focus-visible { outline-color: var(--grund); }

/* Kopfleiste: klare Unterkante wie im Rezept */
.kopf { position: sticky; top: 0; z-index: 20; background: var(--grund); border-bottom: 1px solid var(--linie); }
.kopf-innen { max-width: 1240px; margin: 0 auto; padding: 0 clamp(16px, 3vw, 32px); min-height: 68px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px clamp(14px, 2.4vw, 32px); }
.marke { display: flex; align-items: baseline; gap: 10px; text-decoration: none; flex: none; }
.marke b { font: 850 1.375rem/1 var(--kopf); letter-spacing: -.03em; }
.marke span { color: var(--leise); }
.nav { display: flex; flex-wrap: wrap; gap: 0 22px; list-style: none; font-size: 1rem; }
.nav a { text-decoration: none; padding: 6px 0; border-bottom: 1px solid transparent; transition: border-color var(--ui) var(--kurve); }
.nav a:hover { border-color: currentColor; }
.nav a[aria-current] { border-color: currentColor; }
.kopf-rechts { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.ticket { font: 700 1rem/1 var(--kopf); text-decoration: none; background: var(--tinte); color: var(--grund); padding: 0 18px; min-height: 44px; display: inline-flex; align-items: center; transition: background-color var(--ui) var(--kurve); }
.ticket:hover { background: #2b2a28; }

/* Der Schalter */
.schalter { display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 12px 0 10px; border: 1px solid var(--linie); background: transparent; cursor: pointer; text-align: left; transition: background-color var(--ui) var(--kurve); }
.schalter:hover { background: var(--papier); }
.schalter .bahn { position: relative; width: 40px; height: 22px; border: 1.5px solid var(--tinte); border-radius: 11px; flex: none; background: var(--grund); transition: background-color var(--ui) var(--kurve); }
.schalter .knauf { position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius: 50%; background: var(--tinte); transition: translate var(--ui) var(--kurve), background-color var(--ui) var(--kurve); }
.schalter[aria-checked="true"] .bahn { background: var(--tinte); }
.schalter[aria-checked="true"] .knauf { translate: 18px 0; background: var(--grund); }
.schalter .text { display: grid; line-height: 1.1; }
.schalter .text b { font: 700 .9375rem/1.15 var(--kopf); }
.schalter .zustand { color: var(--leise); font-size: .6875rem; }
.schalter.gedrueckt { background: var(--papier); }

/* Laufband */
.band { position: relative; background: var(--tinte); color: var(--grund); display: flex; align-items: stretch; border-bottom: 1px solid var(--linie); }
.band-fenster { flex: 1; overflow: hidden; min-width: 0; display: flex; align-items: center; }
.band-spur { display: flex; flex: none; white-space: nowrap; animation: laufen var(--band-dauer, 60s) linear infinite; }
.band-spur ul { display: flex; list-style: none; flex: none; }
.band-spur li { padding: 11px 0; display: flex; align-items: center; }
.band-spur li::after { content: ''; width: 6px; height: 6px; background: currentColor; margin: 0 22px; rotate: 45deg; flex: none; }
.band-spur b { font-weight: 500; color: #fff; margin-right: .6em; }
@keyframes laufen { to { translate: calc(-1 * var(--band-breite, 50%)) 0; } }
.band[data-laeuft="false"] .band-fenster { overflow: visible; }
.band[data-laeuft="false"] .band-spur { animation: none; white-space: normal; flex: 1; padding: 4px clamp(16px, 3vw, 32px); }
.band[data-laeuft="false"] .band-spur { min-width: 0; }
.band[data-laeuft="false"] .band-spur ul { flex: 1 1 auto; min-width: 0; flex-wrap: wrap; column-gap: 0; }
.band[data-laeuft="false"] .band-spur li { padding: 7px 0; }
.band[data-laeuft="false"] .band-spur ul[aria-hidden] { display: none; }
.band[data-laeuft="false"] .band-spur li:last-child::after { display: none; }

/* Pause-Knöpfe: gleiche Form überall, Name wechselt mit */
.pause { display: inline-flex; align-items: center; gap: 9px; min-height: 44px; padding: 0 14px; border: 1px solid currentColor; background: transparent; cursor: pointer; flex: none; font: 500 .75rem/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; transition: background-color var(--ui) var(--kurve), color var(--ui) var(--kurve); }
.pause svg { width: 12px; height: 12px; flex: none; }
.pause .zeichen-spiel { display: none; }
.pause[data-zustand="steht"] .zeichen-pause { display: none; }
.pause[data-zustand="steht"] .zeichen-spiel { display: block; }
.band .pause { border-width: 0 0 0 1px; border-color: rgba(239, 237, 233, .35); color: var(--grund); }
.band .pause:hover { background: #262523; }
.pause.auf-papier { background: var(--tinte); color: var(--grund); border-color: var(--tinte); }
.pause.auf-papier:hover { background: #2b2a28; }
.pause.rahmen:hover { background: var(--papier); }
.pause.gedrueckt, .schalter.gedrueckt { box-shadow: inset 0 0 0 2px currentColor; }

/* Hauptteil */
main { flex: 1; width: 100%; max-width: 1240px; margin: 0 auto; padding: clamp(24px, 3.6vw, 48px) clamp(16px, 3vw, 32px) 32px; display: grid; gap: clamp(28px, 4vw, 52px); }
.held { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr); gap: clamp(20px, 3.4vw, 48px); align-items: stretch; }
.held-text { display: flex; flex-direction: column; gap: 18px; padding-top: 4px; }
.held-text .label { display: flex; gap: 10px; align-items: center; }
.sparte { display: inline-block; padding: 4px 7px 3px; color: #fff; font: 500 .6875rem/1.2 var(--mono); letter-spacing: .12em; text-transform: uppercase; }
.sparte.kunst { background: var(--kunst); } .sparte.musik { background: var(--musik); } .sparte.vermittlung { background: var(--vermittlung); } .sparte.film { background: var(--film); }
h1 { font: 900 clamp(2.75rem, 6.4vw, 6.25rem)/.94 var(--kopf); letter-spacing: -.035em; text-wrap: balance; }
h1 span { display: block; font-style: italic; font-weight: 800; }
.held-text p.lead { max-width: 40ch; color: #262523; text-wrap: pretty; }
.held-text .daten { display: flex; flex-wrap: wrap; gap: 6px 22px; color: var(--leise); border-top: 1px solid var(--linie); padding-top: 12px; margin-top: auto; }
.held-text .daten b { color: var(--tinte); font-weight: 500; }

/* Loop-Bühne */
.loop { position: relative; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.loop-flaeche { position: relative; aspect-ratio: 13 / 9; min-height: 0; background: var(--papier); border: 1px solid var(--linie); overflow: hidden; }
.loop canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.loop .pause { position: absolute; right: 12px; bottom: 12px; }
.loop .stand { position: absolute; left: 12px; top: 12px; background: var(--grund); padding: 6px 9px 5px; border: 1px solid var(--linie); color: var(--tinte); opacity: 0; transition: opacity var(--ui) var(--kurve); }
.loop[data-laeuft="false"] .stand { opacity: 1; }
.loop figcaption { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 14px; color: var(--leise); }

/* Karussell */
.reihe { display: grid; gap: 16px; }
.reihe-kopf { display: flex; align-items: end; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding-bottom: 12px; }
.reihe-kopf h2 { font: 800 clamp(1.375rem, 2.2vw, 2rem)/1.05 var(--kopf); letter-spacing: -.02em; }
.reihe-kopf h2 small { display: block; font: 500 .75rem/1.4 var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--leise); margin-bottom: 6px; }
.steuer { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.pfeil { width: 44px; height: 44px; border: 1px solid var(--linie); background: transparent; cursor: pointer; display: grid; place-items: center; transition: background-color var(--ui) var(--kurve); }
.pfeil:hover { background: var(--papier); }
.pfeil svg { width: 16px; height: 16px; }
.zaehler { min-width: 9ch; text-align: right; color: var(--leise); }
.takt { height: 3px; border-top: 1px solid var(--linie); position: relative; overflow: hidden; margin-top: -16px; }
.takt i { position: absolute; inset: 0; background: var(--tinte); transform-origin: left; scale: 0 1; }
.takt.laeuft i { animation: takt var(--takt, 6000ms) linear forwards; }
.reihe[data-laeuft="false"] .takt i { animation: none; scale: 0 1; }
@keyframes takt { to { scale: 1 1; } }
.fenster { overflow: hidden; margin: -4px; padding: 4px; }
.spur { display: flex; gap: 16px; list-style: none; transition: translate var(--wechsel) var(--kurve); }
.karte { flex: 0 0 calc((100% - 32px) / 3); display: grid; grid-template-rows: auto 1fr auto; border: 1px solid var(--linie); background: var(--grund); min-height: 212px; }
.karte-kopf { display: flex; justify-content: space-between; align-items: start; gap: 10px; padding: 14px 16px 0; }
.karte-datum { display: grid; }
.karte-datum b { font: 850 2.625rem/.9 var(--kopf); letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.karte-datum span { color: var(--leise); margin-top: 6px; }
.karte-text { padding: 14px 16px 12px; display: grid; gap: 6px; align-content: start; }
.karte-text h3 { font: 700 1.1875rem/1.2 var(--kopf); letter-spacing: -.01em; text-wrap: balance; }
.karte-text p { font-size: 1rem; line-height: 1.45; color: #2b2a28; }
.karte-fuss { display: flex; justify-content: space-between; align-items: center; gap: 10px; border-top: 1px solid var(--hauch); padding: 0 6px 0 16px; min-height: 48px; color: var(--leise); }
.karte-fuss a { font: 700 .9375rem/1 var(--kopf); color: var(--tinte); text-decoration: none; padding: 10px; display: inline-flex; gap: 6px; align-items: center; }
.karte-fuss a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Statusmeldung */
.meldung { position: fixed; left: 50%; bottom: 20px; z-index: 30; translate: -50% 12px; max-width: min(92vw, 520px); display: flex; gap: 12px; align-items: center; background: var(--tinte); color: var(--grund); padding: 12px 16px; font-size: 1rem; line-height: 1.35; opacity: 0; pointer-events: none; transition: opacity var(--ui) var(--kurve), translate var(--ui) var(--kurve); }
.meldung.zeigen { opacity: 1; translate: -50% 0; }
.meldung::before { content: ''; width: 8px; height: 8px; background: var(--grund); rotate: 45deg; flex: none; }

.fuss { border-top: 1px solid var(--linie); }
.fuss-innen { max-width: 1240px; margin: 0 auto; padding: 14px clamp(16px, 3vw, 32px) 18px; display: flex; flex-wrap: wrap; gap: 6px 22px; color: var(--leise); align-items: center; }
.fuss-innen b { color: var(--tinte); font-weight: 500; }
.fuss-innen button { background: none; border: 0; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; color: var(--tinte); min-height: 32px; font: inherit; }

/* Gedachter Zeiger der Vorführung */
.zeiger { position: fixed; left: 0; top: 0; z-index: 40; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; border: 2px solid var(--tinte); background: rgba(239, 237, 233, .55); pointer-events: none; opacity: 0; transition: translate 700ms var(--kurve), opacity var(--ui) var(--kurve), scale 150ms var(--kurve); }
.zeiger.an { opacity: 1; }
.zeiger.klick { scale: .7; }

/* Ruhiger Zustand: alles steht, nichts gleitet (der Schalter selbst bleibt sichtbar umgelegt) */
:root[data-bewegung="ruhig"] .spur { transition: none; }
:root[data-bewegung="ruhig"] .takt i { visibility: hidden; }
:root[data-bewegung="ruhig"] .meldung { translate: -50% 0; }
/* Systemeinstellung: auch die kleinen UI-Übergänge entfallen */
:root.reduziert * { transition-duration: .01ms !important; }
:root.reduziert .zeiger { transition: opacity 150ms linear !important; }

@media (hover: none) {
  .schalter:hover, .pfeil:hover, .pause.rahmen:hover { background: transparent; }
  .ticket:hover, .pause.auf-papier:hover { background: var(--tinte); }
  .band .pause:hover { background: transparent; }
}
@media (max-width: 1020px) {
  .nav { display: none; }
}
@media (max-width: 760px) {
  .held { grid-template-columns: 1fr; }
  .held-text .daten { margin-top: 4px; }
  .karte { flex-basis: calc((100% - 16px) / 2); }
}
@media (max-width: 560px) {
  .kopf-innen { flex-wrap: wrap; gap: 10px 12px; padding-block: 10px; }
  .marke span { display: none; }
  .kopf-rechts { margin-left: auto; }
  .schalter .zustand { display: none; }
  .ticket { padding: 0 14px; }
  .band .pause .pause-wort { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .band .pause { padding: 0 14px; }
  .karte { flex-basis: 86%; }
  .reihe-kopf { align-items: start; }
  .steuer { width: 100%; }
  .zaehler { margin-left: auto; }
  .loop figcaption { flex-direction: column; gap: 2px; }
}
@media (max-width: 380px) {
  .schalter .text b { font-size: .875rem; }
  .steuer { flex-wrap: wrap; }
  .zaehler { min-width: 0; }
}

/* Vorschau 960 × 720 */
.vorschau body { overflow: hidden; }
.vorschau main { padding-top: 22px; gap: 22px; }
.vorschau .fuss, .vorschau .held-text p.lead { display: none; }
.vorschau h1 { font-size: 3.7rem; }
.vorschau .loop-flaeche { aspect-ratio: 16 / 9.4; }
.vorschau .karte { min-height: 0; }
.vorschau .karte-text p { display: none; }

/* Windows-Kontrastmodus: auch randlose Pause-Knöpfe behalten eine Kante */
@media (forced-colors: active) {
  .band .pause, .fuss-innen button { border: 1px solid ButtonText; }
}
</style>
</head>
<body>
<header class="kopf">
  <div class="kopf-innen">
    <a class="marke" href="#start"><b>Stiftsspeicher</b><span class="label">Quedlinburg</span></a>
    <nav aria-label="Hauptnavigation">
      <ul class="nav">
        <li><a href="#start" aria-current="page">Ausstellungen</a></li>
        <li><a href="#programm">Programm</a></li>
        <li><a href="#besuch">Besuch</a></li>
        <li><a href="#barrierefreiheit">Barrierefreiheit</a></li>
      </ul>
    </nav>
    <div class="kopf-rechts">
      <button class="schalter" type="button" role="switch" aria-checked="false" aria-describedby="schalter-hinweis" data-schalter>
        <span class="bahn" aria-hidden="true"><span class="knauf"></span></span>
        <span class="text"><b>Bewegung reduzieren</b><span class="zustand label" aria-hidden="true" data-zustand>Aus</span></span>
      </button>
      <span class="vh" id="schalter-hinweis">Hält Laufband, Bewegtbild und Karussell an. Wir merken uns Ihre Wahl auf diesem Gerät.</span>
      <a class="ticket" href="#tickets">Tickets</a>
    </div>
  </div>
</header>

<section class="band dunkel" aria-label="Heute im Haus" data-band data-laeuft="true">
  <div class="band-fenster">
    <div class="band-spur mono" data-spur>
      <ul>
        <li><b>Heute</b>geöffnet 10 bis 18 Uhr</li>
        <li><b>Do</b>Abendöffnung bis 21 Uhr</li>
        <li><b>14:00</b>Führung „Papier und Presse“, Treffpunkt Kasse</li>
        <li><b>Eintritt</b>frei bis 18 Jahre</li>
        <li><b>Sa 11:00</b>Offene Druckwerkstatt, ohne Anmeldung</li>
        <li><b>Aufzug</b>alle Ebenen stufenlos</li>
      </ul>
    </div>
  </div>
  <button class="pause" type="button" data-pause="band" data-zustand="laeuft">
    <svg class="zeichen-pause" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 1h3v10H2zM7 1h3v10H7z" fill="currentColor"/></svg>
    <svg class="zeichen-spiel" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 1l9 5-9 5z" fill="currentColor"/></svg>
    <span data-name><span class="vh">Laufband </span><span class="pause-wort">anhalten</span></span>
  </button>
</section>

<main id="start">
  <section class="held" aria-labelledby="titel">
    <div class="held-text">
      <p class="label"><span class="sparte kunst">Kunst</span><span>Ausstellung · bis 21.02.2027</span></p>
      <h1 id="titel">Druck und <span>Gegendruck</span></h1>
      <p class="lead">Holzschnitt, Radierung und Risografie aus dem Harz. Zweihundert Blätter, sechs Werkstätten, eine Presse zum Mitmachen im Erdgeschoss.</p>
      <p class="daten mono"><span><b>Di bis So</b> 10–18 Uhr</span><span><b>Eintritt</b> 8 €, ermäßigt 5 €</span><span><b>Ort</b> Speicherboden, 2. OG</span></p>
    </div>
    <figure class="loop" data-loop data-laeuft="true">
      <div class="loop-flaeche">
        <canvas data-leinwand role="img" aria-label="Eine Druckwalze rollt grüne Farbe über eine Holzplatte mit Maserung."></canvas>
        <span class="stand label" aria-hidden="true">Standbild</span>
        <button class="pause auf-papier" type="button" data-pause="loop" data-zustand="laeuft">
          <svg class="zeichen-pause" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 1h3v10H2zM7 1h3v10H7z" fill="currentColor"/></svg>
          <svg class="zeichen-spiel" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 1l9 5-9 5z" fill="currentColor"/></svg>
          <span data-name>Bewegtbild anhalten</span>
        </button>
      </div>
      <figcaption class="mono"><span>Druckwalze über Birnbaumholz, Werkstatt Gernrode</span><span>Schleife 7 s</span></figcaption>
    </figure>
  </section>

  <section class="reihe" id="programm" aria-roledescription="Karussell" aria-labelledby="reihe-titel" data-reihe data-laeuft="true">
    <div class="reihe-kopf">
      <h2 id="reihe-titel"><small>Programm</small>Diese Woche im Haus</h2>
      <div class="steuer">
        <button class="pause rahmen" type="button" data-pause="reihe" data-zustand="laeuft">
          <svg class="zeichen-pause" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 1h3v10H2zM7 1h3v10H7z" fill="currentColor"/></svg>
          <svg class="zeichen-spiel" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 1l9 5-9 5z" fill="currentColor"/></svg>
          <span data-name>Wechsel anhalten</span>
        </button>
        <button class="pfeil" type="button" data-zurueck aria-label="Vorherige Veranstaltung" aria-controls="spur"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M10 2 4 8l6 6" fill="none" stroke="currentColor" stroke-width="1.6"/></svg></button>
        <button class="pfeil" type="button" data-weiter aria-label="Nächste Veranstaltung" aria-controls="spur"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="m6 2 6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.6"/></svg></button>
        <span class="zaehler mono" data-zaehler aria-hidden="true">1–3 / 6</span>
      </div>
    </div>
    <div class="takt" aria-hidden="true" data-takt><i></i></div>
    <div class="fenster">
      <ul class="spur" id="spur" aria-live="off" data-spur-reihe>
        <li class="karte" role="group" aria-roledescription="Folie" aria-label="1 von 6">
          <div class="karte-kopf"><p class="karte-datum"><b>03</b><span class="mono">Di · Nov · 18:00</span></p><span class="sparte kunst">Kunst</span></div>
          <div class="karte-text"><h3>Wie kommt das Bild aufs Papier?</h3><p>Abendvortrag mit Proben aus dem Depot, 45 Minuten.</p></div>
          <p class="karte-fuss mono"><span>Saal im Speicher</span><a href="#tickets" aria-label="5 €, Tickets für den Vortrag am 3. November">5 € <span aria-hidden="true">→</span></a></p>
        </li>
        <li class="karte" role="group" aria-roledescription="Folie" aria-label="2 von 6">
          <div class="karte-kopf"><p class="karte-datum"><b>04</b><span class="mono">Mi · Nov · 10:30</span></p><span class="sparte vermittlung">Vermittlung</span></div>
          <div class="karte-text"><h3>Stempeln mit Kork und Kartoffel</h3><p>Werkstatt für Kinder ab 5 Jahren mit Begleitung.</p></div>
          <p class="karte-fuss mono"><span>Druckwerkstatt, EG</span><a href="#tickets" aria-label="frei, Anmeldung zur Kinderwerkstatt am 4. November">frei <span aria-hidden="true">→</span></a></p>
        </li>
        <li class="karte" role="group" aria-roledescription="Folie" aria-label="3 von 6">
          <div class="karte-kopf"><p class="karte-datum"><b>05</b><span class="mono">Do · Nov · 19:30</span></p><span class="sparte musik">Musik</span></div>
          <div class="karte-text"><h3>Setzkasten. Klavier und Tiegelpresse</h3><p>Ein Konzert, bei dem die Presse den Takt schlägt.</p></div>
          <p class="karte-fuss mono"><span>Speicherboden, 2. OG</span><a href="#tickets" aria-label="14 €, Tickets für das Konzert am 5. November">14 € <span aria-hidden="true">→</span></a></p>
        </li>
        <li class="karte" role="group" aria-roledescription="Folie" aria-label="4 von 6">
          <div class="karte-kopf"><p class="karte-datum"><b>06</b><span class="mono">Fr · Nov · 20:00</span></p><span class="sparte film">Film</span></div>
          <div class="karte-text"><h3>Kino im Speicher: Kurzfilme aus dem Harz</h3><p>Sechs Filme, 70 Minuten, mit Untertiteln.</p></div>
          <p class="karte-fuss mono"><span>Saal im Speicher</span><a href="#tickets" aria-label="7 €, Tickets für das Kino am 6. November">7 € <span aria-hidden="true">→</span></a></p>
        </li>
        <li class="karte" role="group" aria-roledescription="Folie" aria-label="5 von 6">
          <div class="karte-kopf"><p class="karte-datum"><b>07</b><span class="mono">Sa · Nov · 11:00</span></p><span class="sparte kunst">Kunst</span></div>
          <div class="karte-text"><h3>Offene Druckwerkstatt: Linolschnitt</h3><p>Ohne Anmeldung. Material ist da, Schürzen auch.</p></div>
          <p class="karte-fuss mono"><span>Druckwerkstatt, EG</span><a href="#tickets" aria-label="3 €, zur offenen Werkstatt am 7. November">3 € <span aria-hidden="true">→</span></a></p>
        </li>
        <li class="karte" role="group" aria-roledescription="Folie" aria-label="6 von 6">
          <div class="karte-kopf"><p class="karte-datum"><b>08</b><span class="mono">So · Nov · 14:00</span></p><span class="sparte vermittlung">Vermittlung</span></div>
          <div class="karte-text"><h3>Führung in Leichter Sprache</h3><p>Langsam, mit Pausen und Sitzhockern für alle.</p></div>
          <p class="karte-fuss mono"><span>Treffpunkt Kasse</span><a href="#tickets" aria-label="frei, Anmeldung zur Führung am 8. November">frei <span aria-hidden="true">→</span></a></p>
        </li>
      </ul>
    </div>
  </section>
</main>

<footer class="fuss" id="barrierefreiheit">
  <p class="fuss-innen mono"><span><b>Gespeichert</b> <span data-gespeichert>nichts</span></span><span><b>Start</b> <span data-start>Systemeinstellung</span></span><span>Kurve <b>cubic-bezier(0.215, 0.61, 0.355, 1)</b></span><span>Schalter <b>200 ms</b> · Folie <b>350 ms</b> · Takt <b>6 s</b></span><button type="button" data-vergessen>Wahl vergessen</button></p>
</footer>

<p class="meldung" role="status" data-meldung></p>
<span class="zeiger" aria-hidden="true" data-zeiger></span>

<script>
(() => {
  const html = document.documentElement;
  const KEY = 'stiftsspeicher.bewegung';
  const $ = s => document.querySelector(s);
  const reduziert = () => html.classList.contains('reduziert');
  const schalter = $('[data-schalter]'), zustandText = $('[data-zustand]');
  const meldung = $('[data-meldung]');
  const band = $('[data-band]'), spur = $('[data-spur]');
  const loop = $('[data-loop]'), leinwand = $('[data-leinwand]');
  const reihe = $('[data-reihe]'), spurReihe = $('[data-spur-reihe]'), takt = $('[data-takt]');
  const karten = [...spurReihe.children], zaehler = $('[data-zaehler]');
  const knoepfe = { band: $('[data-pause="band"]'), loop: $('[data-pause="loop"]'), reihe: $('[data-pause="reihe"]') };
  const NAMEN = {
    band: ['<span class="vh">Laufband </span><span class="pause-wort">anhalten</span>', '<span class="vh">Laufband </span><span class="pause-wort">abspielen</span>'],
    loop: ['Bewegtbild anhalten', 'Bewegtbild abspielen'],
    reihe: ['Wechsel anhalten', 'Wechsel starten']
  };
  let vorfuehrung = false;

  /* ---------- Speicher: darf fehlen oder gesperrt sein ---------- */
  function lesen() { try { return localStorage.getItem(KEY); } catch (e) { return null; } }
  function speichern(w) { if (vorfuehrung) return; try { localStorage.setItem(KEY, w); } catch (e) { /* ohne Speicher gilt die Wahl nur für diese Seite */ } anzeigeSpeicher(); }
  function vergessen() { try { localStorage.removeItem(KEY); } catch (e) {} anzeigeSpeicher(); }
  function anzeigeSpeicher() {
    const w = lesen();
    $('[data-gespeichert]').textContent = w ? `${KEY} = ${w}` : 'nichts';
    $('[data-start]').textContent = html.dataset.quelle === 'gespeichert' ? 'gespeicherte Wahl' : (reduziert() ? 'System: reduzieren' : 'System: keine Vorgabe');
  }

  /* ---------- Einzelne Bewegungen ---------- */
  const laeuft = { band: true, loop: true, reihe: true };
  function setzeElement(name, an) {
    laeuft[name] = an;
    const k = knoepfe[name];
    k.dataset.zustand = an ? 'laeuft' : 'steht';
    k.querySelector('[data-name]').innerHTML = NAMEN[name][an ? 0 : 1];
    ({ band, loop, reihe })[name].dataset.laeuft = String(an);
    if (name === 'band') bandMessen();
    if (name === 'loop') an ? loopStart() : loopStopp();
    if (name === 'reihe') { spurReihe.setAttribute('aria-live', an ? 'off' : 'polite'); an ? taktStart() : taktStopp(); }
  }
  Object.entries(knoepfe).forEach(([name, k]) => k.addEventListener('click', () => {
    const an = !laeuft[name];
    if (name === 'reihe') ausdruecklich = an;   // APG: wer selbst startet, wird von Hover und Fokus nicht mehr gebremst
    setzeElement(name, an);
  }));

  /* ---------- Globaler Schalter ---------- */
  function setzeGlobal(ruhig, { melden = false, merken = false } = {}) {
    html.dataset.bewegung = ruhig ? 'ruhig' : 'voll';
    schalter.setAttribute('aria-checked', String(ruhig));
    zustandText.textContent = ruhig ? 'An' : 'Aus';
    ['band', 'loop', 'reihe'].forEach(n => setzeElement(n, !ruhig));
    ausdruecklich = false;
    if (merken) { speichern(html.dataset.bewegung); html.dataset.quelle = 'gespeichert'; }
    if (melden) melde(ruhig ? 'Bewegung reduziert. Laufband, Bewegtbild und Wechsel stehen. Wir merken uns das auf diesem Gerät.' : 'Bewegung wieder an. Jedes Element hat weiter seinen eigenen Pause-Knopf.');
    anzeigeSpeicher();
  }
  schalter.addEventListener('click', () => setzeGlobal(schalter.getAttribute('aria-checked') !== 'true', { melden: true, merken: true }));
  $('[data-vergessen]').addEventListener('click', () => {
    vergessen(); html.dataset.quelle = 'system';
    setzeGlobal(reduziert(), { melden: false });
    melde('Gespeicherte Wahl gelöscht. Es gilt wieder die Einstellung Ihres Systems.');
  });
  let meldeUhr;
  function melde(text) {
    clearTimeout(meldeUhr);
    meldung.textContent = text;
    meldung.classList.add('zeigen');
    meldeUhr = setTimeout(() => meldung.classList.remove('zeigen'), 4000);
  }

  /* ---------- Laufband: Tempo 40 px/s, Kopie nur fürs Auge ---------- */
  const original = spur.querySelector('ul');
  const kopie = original.cloneNode(true);
  kopie.setAttribute('aria-hidden', 'true');
  spur.appendChild(kopie);
  function bandMessen() {
    const b = original.getBoundingClientRect().width;
    spur.style.setProperty('--band-breite', b + 'px');
    spur.style.setProperty('--band-dauer', Math.round(b / 40) + 's');
  }
  addEventListener('resize', bandMessen);
  document.fonts && document.fonts.ready.then(bandMessen);

  /* ---------- Bewegtbild: Druckwalze auf Canvas (steht für Video oder Lottie) ---------- */
  const ctx = leinwand.getContext('2d');
  let W = 0, H = 0, dpr = 1, holz = null, farbe = null, rAF = 0, zeit = 4.3, letzte = 0;
  const ZYKLUS = 7;
  function rauschen(x, y) { const s = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453; return s - Math.floor(s); }
  function maserung(c, stift) {
    const g = c.getContext('2d');
    g.clearRect(0, 0, c.width, c.height);
    g.lineWidth = 1.25 * dpr; g.strokeStyle = stift; g.lineCap = 'round';
    const n = 44, ast = [{ x: W * .64, y: H * .42, r: H * .2 }, { x: W * .22, y: H * .78, r: H * .1 }];
    for (let i = -6; i < n + 6; i++) {
      g.beginPath();
      for (let x = -10; x <= W + 10; x += 6) {
        let y = (i / n) * H + Math.sin(x * .006 + i * .45) * 7 + Math.sin(x * .019 + i) * 1.6;
        ast.forEach(a => { const dx = x - a.x, dy = y - a.y, d = Math.hypot(dx, dy); const k = Math.exp(-(d * d) / (2 * a.r * a.r)); y += (dy >= 0 ? 1 : -1) * k * a.r * .55; });
        x === -10 ? g.moveTo(x * dpr, y * dpr) : g.lineTo(x * dpr, y * dpr);
      }
      g.stroke();
    }
    ast.forEach(a => { for (let r = 3; r < a.r * .5; r += 4) { g.beginPath(); g.ellipse(a.x * dpr, a.y * dpr, r * 1.5 * dpr, r * .8 * dpr, 0, 0, Math.PI * 2); g.stroke(); } });
  }
  function aufbauen() {
    const r = leinwand.getBoundingClientRect();
    if (!r.width) return;
    dpr = Math.min(2, devicePixelRatio || 1); W = r.width; H = r.height;
    leinwand.width = Math.round(W * dpr); leinwand.height = Math.round(H * dpr);
    holz = document.createElement('canvas'); holz.width = leinwand.width; holz.height = leinwand.height;
    farbe = document.createElement('canvas'); farbe.width = leinwand.width; farbe.height = leinwand.height;
    maserung(holz, 'rgba(11,11,11,.78)');
    maserung(farbe, '#2a7243');
    const g = farbe.getContext('2d');
    g.globalCompositeOperation = 'destination-over';
    g.fillStyle = 'rgba(42,114,67,.16)'; g.fillRect(0, 0, farbe.width, farbe.height);
    g.globalCompositeOperation = 'source-over';
    for (let i = 0; i < 1400; i++) { const x = rauschen(i, 1) * farbe.width, y = rauschen(i, 2) * farbe.height; g.clearRect(x, y, 1.6 * dpr, 1.6 * dpr); }
    zeichnen();
  }
  const sanft = t => 1 - Math.pow(1 - t, 3);
  function zeichnen() {
    if (!holz) return;
    const t = (zeit % ZYKLUS) / ZYKLUS;                  // 0..1
    const fahrt = Math.min(1, t / .62);                   // Walze fährt 62 % der Schleife
    const rx = -60 + sanft(fahrt) * (W + 120);
    const deck = t < .86 ? 1 : 1 - (t - .86) / .14;      // Blatt wird abgezogen
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.fillStyle = '#e4e1da'; ctx.fillRect(0, 0, leinwand.width, leinwand.height);
    ctx.globalAlpha = .55; ctx.drawImage(holz, 0, 0); ctx.globalAlpha = 1;
    ctx.save();
    ctx.beginPath(); ctx.rect(0, 0, Math.max(0, rx) * dpr, leinwand.height); ctx.clip();
    ctx.globalAlpha = deck; ctx.drawImage(farbe, 0, 0); ctx.globalAlpha = 1;
    ctx.restore();
    if (t < .7) {                                          // Walze
      const x = rx * dpr, w = 30 * dpr;
      const grad = ctx.createLinearGradient(x - w / 2, 0, x + w / 2, 0);
      grad.addColorStop(0, '#0b0b0b'); grad.addColorStop(.35, '#3a3936'); grad.addColorStop(.55, '#6b6862'); grad.addColorStop(1, '#0b0b0b');
      ctx.fillStyle = 'rgba(11,11,11,.16)'; ctx.fillRect(x + w / 2, 0, 10 * dpr, leinwand.height);
      ctx.fillStyle = grad; ctx.fillRect(x - w / 2, -2, w, leinwand.height + 4);
      ctx.fillStyle = '#2a7243'; ctx.fillRect(x - w / 2 - 3 * dpr, 0, 3 * dpr, leinwand.height);
    }
  }
  function bild(jetzt) {
    if (letzte) zeit += Math.min(.05, (jetzt - letzte) / 1000);
    letzte = jetzt; zeichnen();
    rAF = requestAnimationFrame(bild);
  }
  function loopStart() { if (rAF) return; letzte = 0; rAF = requestAnimationFrame(bild); }
  function loopStopp() { cancelAnimationFrame(rAF); rAF = 0; zeichnen(); }   // friert an derselben Stelle ein (G4)
  new ResizeObserver(aufbauen).observe(leinwand);

  /* ---------- Karussell nach APG ---------- */
  let index = 0, taktUhr = 0, ausdruecklich = false, gebremst = { hover: false, fokus: false };
  const sichtbar = () => innerWidth <= 560 ? 1 : innerWidth <= 760 ? 2 : 3;
  let TAKT = 6000;
  function zeige(i) {
    const max = karten.length - sichtbar();
    index = (i + max + 1) % (max + 1);
    const schritt = karten[0].getBoundingClientRect().width + 16;
    spurReihe.style.translate = `${-index * schritt}px 0`;
    const n = sichtbar();
    zaehler.textContent = n === 1 ? `${index + 1} / ${karten.length}` : `${index + 1}–${index + n} / ${karten.length}`;
    karten.forEach((k, j) => { const drin = j >= index && j < index + n; k.inert = !drin; k.setAttribute('aria-hidden', String(!drin)); });
  }
  function taktStart() {
    taktStopp();
    if (!laeuft.reihe || (!ausdruecklich && (gebremst.hover || gebremst.fokus))) return;
    takt.style.setProperty('--takt', TAKT + 'ms');
    takt.classList.remove('laeuft'); void takt.offsetWidth; takt.classList.add('laeuft');
    taktUhr = setTimeout(() => { zeige(index + 1); taktStart(); }, TAKT);
  }
  function taktStopp() { clearTimeout(taktUhr); takt.classList.remove('laeuft'); }
  $('[data-weiter]').addEventListener('click', () => { zeige(index + 1); if (laeuft.reihe) taktStart(); });
  $('[data-zurueck]').addEventListener('click', () => { zeige(index - 1); if (laeuft.reihe) taktStart(); });
  reihe.addEventListener('mouseenter', () => { gebremst.hover = true; if (!ausdruecklich) taktStopp(); });
  reihe.addEventListener('mouseleave', () => { gebremst.hover = false; if (laeuft.reihe) taktStart(); });
  reihe.addEventListener('focusin', () => { gebremst.fokus = true; if (!ausdruecklich) taktStopp(); });
  reihe.addEventListener('focusout', e => { if (!reihe.contains(e.relatedTarget)) { gebremst.fokus = false; if (laeuft.reihe) taktStart(); } });
  addEventListener('resize', () => zeige(index));

  /* Fundus-Rahmen setzt .reduziert evtl. erst nach diesem Skript */
  new MutationObserver(() => { if (reduziert() && html.dataset.quelle !== 'gespeichert' && html.dataset.bewegung !== 'ruhig') setzeGlobal(true); anzeigeSpeicher(); })
    .observe(html, { attributes: true, attributeFilter: ['class'] });

  /* ---------- Vorführung: gedachter Zeiger legt den Schalter um ---------- */
  const zeiger = $('[data-zeiger]');
  let uhren = [], laeuftVor = false;
  const nach = (ms, f) => uhren.push(setTimeout(f, ms));
  function zeigeAuf(el) { const r = el.getBoundingClientRect(); zeiger.style.translate = `${r.left + r.width * .5}px ${r.top + r.height * .55}px`; }
  function tippe(el, t) {
    nach(t, () => { zeiger.classList.add('an'); zeigeAuf(el); });
    nach(t + 800, () => { zeiger.classList.add('klick'); el.classList.add('gedrueckt'); });
    nach(t + 950, () => { zeiger.classList.remove('klick'); el.classList.remove('gedrueckt'); el.click(); });
  }
  function runde() {
    setzeGlobal(reduziert());
    TAKT = 2400; zeige(0); if (laeuft.reihe) taktStart();
    zeiger.style.translate = `${innerWidth * .55}px ${innerHeight * .9}px`;
    if (reduziert()) {
      // Systemeinstellung „reduzieren“: nichts anwerfen, nur Bedienung zeigen (Wechsel ohne Gleiten)
      tippe($('[data-weiter]'), 800); tippe($('[data-weiter]'), 2600);
      nach(4200, () => melde('Ihr System wünscht weniger Bewegung. Laufband, Bewegtbild und Wechsel stehen.'));
      nach(8000, runde);
      return;
    }
    tippe(schalter, 3000);                      // alles steht, sofort
    tippe(knoepfe.loop, 7000);                  // nur das Bewegtbild wieder an
    tippe(schalter, 9800);                      // erst „an“ (ruhig), dann wieder „aus“
    tippe(schalter, 11400);
    nach(13200, () => zeiger.classList.remove('an'));
    nach(14000, runde);
  }
  function vorfuehren() { neustart(); vorfuehrung = true; laeuftVor = true; html.classList.add('vorfuehrung'); runde(); }
  function pause() { uhren.forEach(clearTimeout); uhren = []; laeuftVor = false; taktStopp(); }
  function weiter() { if (!laeuftVor && vorfuehrung) { laeuftVor = true; runde(); } }
  function neustart() {
    pause(); vorfuehrung = false; html.classList.remove('vorfuehrung');
    zeiger.classList.remove('an'); meldung.classList.remove('zeigen');
    TAKT = 6000;
    const w = /[?&]reduziert\b/.test(location.search) ? 'ruhig' : lesen();
    setzeGlobal(w ? w === 'ruhig' : reduziert());
    zeige(0); if (laeuft.reihe) taktStart();
  }

  // Start
  bandMessen(); zeige(0);
  setzeGlobal(html.dataset.bewegung === 'ruhig');
  aufbauen();

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