/* ============================================================
   DDL Motion v1.1 — denschDesignLanguage
   ============================================================
   Bewegungs- und Interaktions-Standard der densch-Familie.
   Nachfolger von scroll-fx v1.0 (dessen komplette API — data-scroll,
   data-glow, data-counter, data-ticker, … — läuft hier unverändert
   weiter). Verhalten: ddl-motion.js. Muster: ddl-motion-probe.html.

   SINGLE SOURCE OF TRUTH: Diese Datei liegt GENAU EINMAL auf dem
   Server (/var/www/densch-shared/public/ddl/) und wird auf jeder
   Site unter /densch-shared/ddl/ serviert (Symlink im Web-Root:
   ln -sfn /var/www/densch-shared/public <webroot>/densch-shared).
   KEINE Kopien in Projekte legen — das war das alte scroll-fx-
   Modell und hat gedriftet.

   Einbinden (jede Seite mit Bewegung — noscript-Fallback ist Pflicht,
   sonst bleibt Inhalt ohne JS auf opacity:0):
     <link rel="stylesheet" href="/densch-shared/ddl/ddl-motion.css?v=YYYYMMDD">
     <script src="/densch-shared/ddl/ddl-motion.js?v=YYYYMMDD" defer></script>
     <noscript><style>[data-scroll],[data-scrub-item],[data-glow],
       [data-counter]{opacity:1 !important;transform:none !important}
     </style></noscript>

   DIE 11 MOTION-REGELN
    1  Ein Besitzer pro Element: Scrub ODER Reveal ODER Loop —
       nie gestapelt. Verschachtelung nur Container→Kind mit
       klarer Zuständigkeit (Sektion scrubbt, Kind wird Preset).
    2  Nur transform + opacity werden animiert (blur allein als
       Reveal-Effekt). will-change dort, wo Bewegung bevorsteht.
       AUSNAHME (v1.1): clip-path und Maße an einem EINZELNEN,
       isolierten Element (Verwandlungen, Regel 11) — der Grund
       der Regel, großflächiges Layout-Thrashing, trifft dort
       nicht zu. Nie auf Listen, Rastern oder Sektionen.
    3  Ein Easing, eine Dauer-Skala: var(--ddl-ease) und
       --ddl-dur-press/-ui/-morph/-transit/-reveal. Keine freien
       Easing-/Dauer-Werte im Seiten-CSS. (v1.1: -morph 460ms =
       ein Element wandelt sich in sich selbst, -transit 900ms =
       ein Element verlässt die Bühne — die Lücke zwischen -ui
       und -reveal, in der Verwandlungen leben. In JS über
       DDLMotion.dur.* lesen, nie hart eintippen.)
    4  Feedback auf pointer-down, nie erst auf Release:
       .ddl-press auf jeden Button/CTA/Link mit Klick-Wirkung.
       Hover bewegt nur transform — nie gap, margin, width.
    5  Scroll erzählt, Timer nicht: Sektions-Choreografie wird
       gescrubbt ([data-scrub], umkehrbar & unterbrechbar).
       Einmal-Reveals ([data-scroll]) nur für statische Inhalte;
       Stagger über Progress-Offset oder data-delay ≤ 400 ms —
       keine setTimeout-Ketten im Seiten-JS.
    6  Räumliche Konsistenz: Enter- und Exit-Pfad gehören
       zusammen; die Einflug-Richtung folgt der Layout-Position
       (was links steht, kommt von links — Engine ermittelt das
       automatisch, data-scrub-dir übersteuert). Popover/Menüs
       skalieren vom Auslöser (transform-origin).
    7  Loops sind Ambient-Ausnahme (Equalizer, Status-Pulse):
       [data-loop] setzen — pausiert offscreen, stoppt bei
       Reduced Motion. Nie großflächige Oszillation nahe 0,2 Hz.
    8  Reduced Motion ist Pflicht-Pfad: die Engine registriert
       bei prefers-reduced-motion KEINE Scrub-/Parallax-Handler,
       Reveals werden sofort sichtbar, Loops stehen. Seiten-
       eigene Entrance-Keyframes braucht die Seite selbst im
       @media-Block abzuschalten. prefers-reduced-transparency /
       prefers-contrast regeln .ddl-material automatisch.
    9  Chrome ist Material: Nav/Toolbars = .ddl-material
       (Translucenz + Blur, Tokens unten), Scroll-Edge statt
       harter 1px-Linie ([data-material] toggelt .is-scrolled).
       Nie zwei helle Materialien stapeln.
   10  Echte Gesten = Springs: 1:1-Tracking mit Pointer-Capture
       und Grab-Offset, Velocity-Übergabe beim Loslassen,
       Momentum-Projektion (DDLMotion.project), Rubberband an
       Grenzen (DDLMotion.rubberband). Hauswerte: damping 1.0 /
       response 0.3–0.4 (Standard, kein Overshoot); damping 0.8
       NUR nach echtem Momentum (Flick/Throw). „Zurück oder
       committen" entscheidet das Velocity-VORZEICHEN, nie die
       Position.
   11  Verwandlung statt Beiwerk: Kein neues Element herbeirufen,
       um einen Zustand zu zeigen — das vorhandene verwandeln.
       Der Knopf wird sein eigener Spinner, die Codefelder werden
       der Ladekreis, der Fokusrahmen wandert statt zu blinken.
       Der Nutzer sieht dann, dass GENAU das Ding arbeitet, das
       er angefasst hat; ein Spinner daneben sagt nur, dass
       irgendetwas lädt. Bausteine: .ddlm-* (unten), Verhalten:
       DDLMotion.codeFeld / .pille / .navKapsel / .funke.
       .funke hängt ein umlaufendes Licht an den Rand eines
       BELIEBIGEN Elements (warten / puls) — für Wartezustände,
       die kein Knopf sind (z.B. die Karte, die auf die
       Kartenzahlung wartet). Muster: ddl-motion-probe.html.

   MOCKUPS/ILLUSTRATIONEN (Konvention): CSS-nachgebaute App-
   Screens sind Illustrationen — fixe px-Maße sind dort korrekt,
   aria-hidden="true" + role="img"/aria-label am Wrapper. Echte
   Seiten-Typo dagegen in rem (siehe ddl-typography.css).

   PORTIERUNG EINER SEITE VON scroll-fx AUF DDL MOTION
    1  <link>/<script> von scroll-fx.css/js auf die zentralen
       Pfade oben umstellen — Verhalten bleibt identisch.
    2  Lokale scroll-fx-Kopien löschen (nach Abnahme).
    3  Schrittweise: doppelte Besitzer auflösen (Regel 1),
       eigenes Scroll-JS durch [data-scrub]-Attribute ersetzen,
       .ddl-press ergänzen, Loops mit [data-loop] markieren.
   ============================================================ */

/* ── Tokens ───────────────────────────────────────────────── */
:root {
  --ddl-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ddl-dur-press: 100ms;
  --ddl-dur-ui: 300ms;
  --ddl-dur-morph: 460ms;      /* v1.1 — Element wandelt sich in sich selbst */
  --ddl-dur-transit: 900ms;    /* v1.1 — Element verlässt die Bühne          */
  --ddl-dur-reveal: 1s;
  /* Material (Light-Default; dunkle Seiten überschreiben) */
  --ddl-material-bg: rgba(255, 255, 255, 0.72);
  --ddl-material-solid: #ffffff;
  --ddl-material-edge: rgba(0, 0, 0, 0.18);
}

/* ════════════════════════════════════════════════════════════
   REVEALS (scroll-fx v1 API — unverändert)
   [data-scroll] + Varianten, .in-view via IntersectionObserver
   ════════════════════════════════════════════════════════════ */
[data-scroll] {
  opacity: 0;
  will-change: transform, opacity;
  transition:
    opacity var(--ddl-dur-reveal) var(--ddl-ease),
    transform var(--ddl-dur-reveal) var(--ddl-ease),
    filter var(--ddl-dur-reveal) var(--ddl-ease);
}

[data-scroll],
[data-scroll="fade-up"] { transform: translateY(50px); }
[data-scroll].in-view,
[data-scroll="fade-up"].in-view { opacity: 1; transform: translateY(0); }

[data-scroll="fade-down"] { transform: translateY(-50px); }
[data-scroll="fade-down"].in-view { opacity: 1; transform: translateY(0); }

[data-scroll="fade-left"] { transform: translateX(60px); }
[data-scroll="fade-left"].in-view { opacity: 1; transform: translateX(0); }

[data-scroll="fade-right"] { transform: translateX(-60px); }
[data-scroll="fade-right"].in-view { opacity: 1; transform: translateX(0); }

[data-scroll="scale-up"] { transform: scale(0.88); }
[data-scroll="scale-up"].in-view { opacity: 1; transform: scale(1); }

[data-scroll="blur-in"] {
  transform: translateY(30px) scale(0.97);
  filter: blur(10px);
}
[data-scroll="blur-in"].in-view {
  opacity: 1;
  transform: translateY(0) scale(1);
  filter: blur(0);
}

[data-scroll="zoom-out"] { transform: scale(1.15); }
[data-scroll="zoom-out"].in-view { opacity: 1; transform: scale(1); }

[data-scroll="flip-up"] {
  transform: perspective(800px) rotateX(8deg) translateY(40px);
  transform-origin: bottom center;
}
[data-scroll="flip-up"].in-view {
  opacity: 1;
  transform: perspective(800px) rotateX(0) translateY(0);
}

/* ── JS-getriebene Elemente: nur will-change ──────────────── */
[data-hero-fade] { will-change: transform, opacity; }
[data-parallax]  { will-change: transform; }

/* ════════════════════════════════════════════════════════════
   SCRUB-ENGINE (neu in DDL Motion v1)
   [data-scrub] = Sektion (Progress-Quelle), optional
     data-scrub-dir="left|right" (sonst automatisch aus Layout)
   [data-scrub-item="phone3d|text|rise|slide-x|stack|tile-pop"]
     = choreografiertes Kind; optional data-scrub-offset /
     data-scrub-span / data-scrub-dir am Item.
   Die Engine schreibt transform/opacity inline — Items dürfen
   KEIN [data-scroll] tragen und kein eigenes opacity/transform
   im Seiten-CSS haben (Regel 1).
   ════════════════════════════════════════════════════════════ */
[data-scrub-item] {
  opacity: 0;
  will-change: transform, opacity;
}

/* ════════════════════════════════════════════════════════════
   PRESS-FEEDBACK (Regel 4)
   ════════════════════════════════════════════════════════════ */
.ddl-press {
  transition: transform var(--ddl-dur-press) ease-out;
}
.ddl-press:active {
  transform: scale(0.97);
}

/* ════════════════════════════════════════════════════════════
   MATERIAL / CHROME (Regel 9)
   .ddl-material  = translucente Leiste (Nav, Toolbar)
   [data-material] = Engine toggelt .is-scrolled (Scroll-Edge)
   Dunkle Seiten setzen die Tokens um, z. B.:
     --ddl-material-bg: rgba(13,13,13,0.80);
     --ddl-material-solid: #0D0D0D;
     --ddl-material-edge: rgba(0,0,0,0.45);
   ════════════════════════════════════════════════════════════ */
.ddl-material {
  position: relative;
  background: var(--ddl-material-bg);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
}
/* Scroll-Edge: weicher Verlauf statt 1px-Border, erst wenn
   Inhalt unter dem Material liegt */
.ddl-material::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 12px;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(to bottom, var(--ddl-material-edge), transparent);
  transition: opacity var(--ddl-dur-ui) var(--ddl-ease);
}
.ddl-material.is-scrolled::after { opacity: 1; }

/* ════════════════════════════════════════════════════════════
   LOOPS (Regel 7) — [data-loop] pausiert offscreen
   ════════════════════════════════════════════════════════════ */
.ddl-loop-paused,
.ddl-loop-paused *,
.ddl-loop-paused::before,
.ddl-loop-paused::after {
  animation-play-state: paused !important;
}

/* ════════════════════════════════════════════════════════════
   TEXT GLOW / SHINE (scroll-fx v1 API — unverändert)
   ════════════════════════════════════════════════════════════ */
[data-glow] {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 1.2s var(--ddl-ease),
              transform 1.2s var(--ddl-ease);
}
[data-glow].glow-active {
  opacity: 1;
  transform: translateY(0);
}

[data-theme-dark] [data-glow],
[data-theme-dark][data-glow],
.theme-dark [data-glow],
.theme-dark[data-glow],
body.dark [data-glow] {
  background: linear-gradient(180deg, #3a3a3c 0%, #3a3a3c 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
[data-theme-dark] [data-glow].glow-active,
[data-theme-dark][data-glow].glow-active,
.theme-dark [data-glow].glow-active,
.theme-dark[data-glow].glow-active,
body.dark [data-glow].glow-active {
  background: linear-gradient(180deg, #f5f5f7 30%, #86868b 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: glowShineDark 2.5s ease 0.3s forwards;
}
@keyframes glowShineDark {
  0%   { background-position: -200% center; filter: brightness(1); }
  30%  { filter: brightness(1.4); text-shadow: 0 0 40px rgba(255, 255, 255, 0.15); }
  100% { background-position: 200% center; filter: brightness(1); }
}
[data-glow].glow-active { animation: glowShineLight 2s ease 0.3s forwards; }
@keyframes glowShineLight {
  0%   { filter: brightness(1); }
  35%  { filter: brightness(1.08); }
  100% { filter: brightness(1); }
}

/* ════════════════════════════════════════════════════════════
   COUNTER (scroll-fx v1 API — unverändert)
   ════════════════════════════════════════════════════════════ */
[data-counter] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
[data-counter].counted { opacity: 1; transform: translateY(0); }

/* ════════════════════════════════════════════════════════════
   STICKY PIN SECTIONS (scroll-fx v1 API — unverändert)
   ════════════════════════════════════════════════════════════ */
[data-sticky-section] { position: relative; }
[data-sticky-panel] {
  position: sticky;
  top: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: transform, opacity;
  transition: transform 0.1s linear, opacity 0.1s linear;
}

/* ════════════════════════════════════════════════════════════
   SCROLL PROGRESS BAR (scroll-fx v1 API — unverändert)
   ════════════════════════════════════════════════════════════ */
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  height: 2px;
  background: var(--accent, #2997ff);
  width: calc(var(--scroll-progress, 0) * 100%);
  z-index: 9999;
  transition: none;
}

/* ════════════════════════════════════════════════════════════
   TICKER / LOGO SCROLLER (scroll-fx v1 API — unverändert)
   ════════════════════════════════════════════════════════════ */
[data-ticker] {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  cursor: grab;
  scrollbar-width: none;
  mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%);
}
[data-ticker]::-webkit-scrollbar { display: none; }
[data-ticker]:active { cursor: grabbing; }

/* ════════════════════════════════════════════════════════════
   REDUCED MOTION (Regel 8) — Pflicht-Pfad.
   Die Engine registriert zusätzlich keine Scrub-/Parallax-
   Handler; dieser Block stellt die Sichtbarkeit sicher.
   Press-Feedback bleibt: Reduced Motion heißt weniger
   Bewegung, nicht weniger Feedback.
   ════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  [data-scroll],
  [data-glow],
  [data-counter],
  [data-hero-fade],
  [data-parallax],
  [data-scrub-item] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    animation: none !important;
    transition: none !important;
  }
  [data-loop],
  [data-loop] *,
  [data-loop]::before,
  [data-loop]::after {
    animation: none !important;
  }
  [data-sticky-panel] {
    transform: none !important;
    opacity: 1 !important;
    transition: none !important;
  }
}

/* ── Material-Fallbacks (Regel 8/9) ───────────────────────── */
@media (prefers-reduced-transparency: reduce) {
  .ddl-material {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--ddl-material-solid);
  }
}
@media (prefers-contrast: more) {
  .ddl-material {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: var(--ddl-material-solid);
    border-bottom: 1px solid currentColor;
  }
  .ddl-material::after { display: none; }
}

/* ════════════════════════════════════════════════════════════
   VERWANDLUNGEN (Regel 11) — .ddlm-*                    v1.1
   ════════════════════════════════════════════════════════════
   Bausteine, die einen Zustand zeigen, indem sie das vorhandene
   Element verwandeln, statt ein zweites daneben zu stellen.
   Verhalten: DDLMotion.codeFeld() / .pille() / .navKapsel().
   Muster: ddl-motion-probe.html.

   Konsumiert AUSSCHLIESSLICH die Brücken-Tokens von
   ddl-forms.css (--ddl-accent, --ddl-text, --ddl-border, …) —
   damit laufen sie überall, wo eine Formular-Brücke existiert.
   EINE Ausnahme: --ddl-ok / --ddl-ok-tint gehören noch nicht
   zum 30er-Satz (nur order und q definieren sie). Deshalb hier
   mit Hausfarbe als Fallback; Brücken dürfen überschreiben.
   ════════════════════════════════════════════════════════════ */

/* ── 1 · Einmalcode ───────────────────────────────────────────
   ACHTUNG, das Markup ist Teil des Standards: EIN echtes
   <input> liegt durchsichtig über der ganzen Reihe, die
   Kästchen sind nur gezeichnet. Sechs Einzelfelder liest
   Safari als Kontaktformular und legt das Adressbuch darüber;
   außerdem wirft iOS-Autofill den KOMPLETTEN Code in ein Feld.
   Autofill gehört abbestellt (autocomplete="off", nichts-
   sagender name, data-lpignore/1p-ignore/bwignore).          */
.ddlm-code { position: relative; display: inline-block; }
.ddlm-code__reihe { display: flex; gap: 10px; justify-content: center; }

.ddlm-slot {
  position: relative;
  width: 54px;
  height: 66px;
  flex: none;
  border: 1px solid var(--ddl-border);
  border-radius: var(--ddl-radius-md);
  background: var(--ddl-bg-light);
  display: grid;
  place-items: center;
  transition: border-color var(--ddl-dur-ui) var(--ddl-ease);
}
.ddlm-slot.is-voll { border-color: var(--ddl-accent-tint); }
.ddlm-slot__zif {
  font-family: var(--ddl-font-mono);
  font-size: 1.6rem;
  font-weight: 500;
  line-height: 1;
  color: var(--ddl-text);
}
.ddlm-slot.is-frisch .ddlm-slot__zif { animation: ddlmSetzen var(--ddl-dur-ui) var(--ddl-ease); }
@keyframes ddlmSetzen {
  from { transform: translateY(4px) scale(0.82); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* Das eine echte Feld — unsichtbar, aber voll bedienbar.
   font-size 16px ist Pflicht: darunter zoomt iOS beim Fokus. */
.ddlm-code__feld {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  font-size: 16px;
  text-align: center;
  outline: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ddlm-code__feld::selection { background: transparent; }

/* Regel 6/11: EIN Fokus, der wandert — nicht sechs, die blinken. */
.ddlm-ring {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  border: 2px solid var(--ddl-accent);
  border-radius: var(--ddl-radius-md);
  box-shadow: 0 0 0 4px var(--ddl-accent-tint);
  pointer-events: none;
  opacity: 0;
  transition:
    transform var(--ddl-dur-morph) var(--ddl-ease),
    width var(--ddl-dur-morph) var(--ddl-ease),
    opacity var(--ddl-dur-ui) var(--ddl-ease);
}
.ddlm-ring.is-sichtbar { opacity: 1; }
.ddlm-code.no-ring .ddlm-ring { display: none; }
.ddlm-code.no-ring .ddlm-slot.is-dran {
  border-color: var(--ddl-accent);
  box-shadow: 0 0 0 3px var(--ddl-accent-tint);
}

/* Lichtpunkt: ein Segment auf dunkler Bahn. Langsam = „ich
   warte", einmal zügig herum = „Ziffer sitzt".                */
.ddlm-funke { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
/* Freistehend: das Trägerelement muss positioniert sein. */
.ddlm-hat-funke { position: relative; }
.ddlm-funke rect {
  fill: none;
  stroke: var(--ddl-accent);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 0.16 0.84;
  stroke-dashoffset: 1;
  opacity: 0;
}
/* Zwei Tempi, zwei Aussagen — und beide auch OHNE Codefeld
   nutzbar (.is-warten / .is-puls an einem beliebigen Element,
   Größe stellt DDLMotion.funke): langsam+leise = „ich warte",
   einmal zügig herum = „angekommen". */
.ddlm-slot.is-dran .ddlm-funke rect,
.ddlm-funke.is-warten rect { opacity: 0.5; animation: ddlmRunde 2.4s linear infinite; }
.ddlm-slot.is-dreht .ddlm-funke rect,
.ddlm-funke.is-puls rect {
  opacity: 1;
  stroke-dasharray: 0.22 0.78;
  animation: ddlmRunde var(--ddl-dur-transit) var(--ddl-ease) 1;
}
@keyframes ddlmRunde { to { stroke-dashoffset: 0; } }
.ddlm-code.no-funke .ddlm-funke { display: none; }

/* Orbit-Abschluss: gepunktete Bahn + Nabe. Gepunktet mit
   Absicht — ein durchgezogener Ring liest sich in dieser Größe
   selbst als Spinner und konkurriert mit den Kästchen.        */
.ddlm-bahn {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  overflow: visible;
  transition: opacity var(--ddl-dur-ui) var(--ddl-ease);
}
.ddlm-bahn.is-sichtbar { opacity: 1; }
.ddlm-bahn circle {
  fill: none;
  stroke: var(--ddl-border);
  stroke-width: 1.5;
  stroke-dasharray: 2 8;
  stroke-linecap: round;
}
.ddlm-nabe {
  position: absolute;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: var(--ddl-text-muted);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ddl-dur-ui) var(--ddl-ease);
}
.ddlm-nabe.is-sichtbar { opacity: 0.7; }

/* Farbe ist dem Urteil vorbehalten — vorher bleibt alles neutral. */
.ddlm-slot.is-ok  { border-color: var(--ddl-ok, #16A34A); }
.ddlm-slot.is-ok  .ddlm-slot__zif { color: var(--ddl-ok, #16A34A); }
.ddlm-slot.is-bad { border-color: var(--ddl-err); }
.ddlm-slot.is-bad .ddlm-slot__zif { color: var(--ddl-err); }

@media (max-width: 430px) {
  .ddlm-slot { width: 46px; height: 58px; }
  .ddlm-code__reihe { gap: 7px; }
  .ddlm-slot__zif { font-size: 1.35rem; }
}

/* ── 2 · Pille: der Knopf wird sein eigener Spinner ────────── */
.ddlm-pill { position: relative; width: 100%; max-width: 21rem; height: 3.25rem; }
.ddlm-pill__platte {
  position: absolute;
  inset: 0;
  border-radius: var(--ddl-radius-pill);
  background: var(--ddl-accent);
  clip-path: inset(0 0 0 0 round 999px);
}
.ddlm-pill[data-zustand="ok"]     .ddlm-pill__platte { background: var(--ddl-ok, #16A34A); }
.ddlm-pill[data-zustand="fehler"] .ddlm-pill__platte { background: var(--ddl-err); }
.ddlm-pill__cta {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--ddl-radius-pill);
  font: inherit;
  font-weight: 600;
  color: var(--ddl-text-on-accent);
}
.ddlm-pill__cta:focus-visible { outline: 2px solid var(--ddl-accent); outline-offset: 3px; }
.ddlm-pill__spule {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  opacity: 0;
  pointer-events: none;
}
/* 0,82 einer Umdrehung, nicht 1: ein geschlossener Ring ist
   symmetrisch — man sähe ihn nie drehen.                      */
.ddlm-pill__spule circle {
  fill: none;
  stroke: var(--ddl-text-on-accent);
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-dasharray: 0.82 0.18;
  transform-origin: 50% 50%;
}
.ddlm-pill[data-zustand="sendet"] .ddlm-pill__spule { opacity: 1; }
.ddlm-pill[data-zustand="sendet"] .ddlm-pill__spule circle {
  animation: ddlmSpule var(--ddl-dur-transit) linear infinite;
}
@keyframes ddlmSpule { to { transform: rotate(360deg); } }

/* ── 3 · Navigation: Kapsel gleitet UND dehnt sich ─────────── */
.ddlm-nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 6px;
  border-radius: var(--ddl-radius-pill);
}
.ddlm-nav__kapsel {
  position: absolute;
  top: 6px;
  left: 0;
  height: calc(100% - 12px);
  border-radius: var(--ddl-radius-pill);
  background: var(--ddl-accent-tint);
  border: 1px solid var(--ddl-accent-tint);
  pointer-events: none;
}
.ddlm-nav__punkt {
  position: relative;
  z-index: 1;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  color: var(--ddl-text-muted);
  padding: 0.55rem 1rem;
  border-radius: var(--ddl-radius-pill);
  white-space: nowrap;
  transition: color var(--ddl-dur-ui) var(--ddl-ease);
}
.ddlm-nav__punkt[aria-selected="true"] { color: var(--ddl-accent); }
.ddlm-nav__punkt:focus-visible { outline: 2px solid var(--ddl-accent); outline-offset: 2px; }

/* ── Regel 8: Reduced Motion ist Pflicht-Pfad ─────────────── */
@media (prefers-reduced-motion: reduce) {
  .ddlm-ring { transition: opacity var(--ddl-dur-ui) var(--ddl-ease); }
  .ddlm-funke rect { animation: none !important; }
  .ddlm-slot.is-dran .ddlm-funke rect,
  .ddlm-funke.is-warten rect { opacity: 0.5; stroke-dashoffset: 0.75; }
  .ddlm-slot.is-frisch .ddlm-slot__zif { animation: none; }
  .ddlm-pill[data-zustand="sendet"] .ddlm-pill__spule circle { animation: none; }
}
