/* ============================================================
   SITE-BÜHNE  —  gemeinsames Aussehen für Vorschau und Kundenseite
   ============================================================

   Diese Datei wird sowohl vom Site-Builder als auch von der echten
   Bestellseite geladen. Was hier steht, sieht an beiden Orten gleich aus —
   sonst wäre die Vorschau eine Lüge.

   Zwei Grundgedanken:

   1. ALLES IN PROZENT. Elemente liegen prozentual auf der Bühne, Schriften
      werden in "cqw" gerechnet (Prozent der Bühnenbreite). Ein Entwurf, der
      am 27-Zoll-Monitor stimmt, stimmt damit auch auf dem Handy — ohne dass
      jemand ein zweites Layout pflegen muss.

   2. ANIMATIONEN LAUFEN ERST IM SICHTFELD. Jede Animation steht auf
      "paused", bis das Element die Klasse .is-inview bekommt. Einmal-
      Animationen feuern dadurch genau dann, wenn man sie auch sieht.
   ============================================================ */

.sb-stage {
  position: relative;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  /* KEINE Mindesthöhe mehr an der Bühne (geändert in 0.9.1).
     Sie stand bis 0.9.0 hier, mit der Begründung, das Seitenverhältnis der
     Entwurfsfläche nicht zu verbiegen. Der Preis dafür war, dass BÜHNE und
     FLÄCHE auseinanderlaufen: Zieht man die Höhe klein, bleibt die Bühne bei
     320 px stehen, während die Fläche weiterschrumpft — gemessen 320 gegen
     192 px. Der sichtbare Kasten steht dann still, die Elemente darin
     schrumpfen weiter und lösen sich vom Entwurf.
     Die Mindesthöhe sitzt jetzt an der Fläche (siehe unten). Damit gibt es
     nur noch EINEN Kasten, der die Größe bestimmt. Dass das Verhältnis
     darunter nachgibt, gilt in der Vorschau genauso wie auf der Kundenseite —
     beide laden diese Datei, die Vorschau bleibt also ehrlich. */
  background: #1A1614;
  /* ÜBERSTAND NACH UNTEN JA, ZUR SEITE NEIN  (geschärft in 0.9.1)

     Nach unten muss es überstehen dürfen: Das Aktions-Banner soll auf der
     Kante zur Speisekarte sitzen. Bis 0.9.0 stand hier `overflow: visible`,
     und damit durfte auch seitlich etwas hinausragen.

     Das ist teuer bezahlt: Ein Kreis, den jemand halb über den rechten Rand
     legt - reine Zier - macht die GANZE Seite seitlich scrollbar. Gemessen im
     Demo-Entwurf: 55 px auf einem Laptop, 77 px auf einem breiten Monitor. Auf
     dem Handy wischt man dann versehentlich die Seite zur Seite, statt zu
     scrollen.

     `overflow-x: clip` schneidet waagerecht ab, OHNE senkrecht eine Scrollbox
     daraus zu machen - das ist der Unterschied zu `hidden`, das immer beide
     Achsen anfasst. Im Editor bleibt alles sichtbar: Dort steht die Bühne frei
     in der Arbeitsfläche, und beim Gestalten will man sehen, was übersteht. */
  overflow-x: clip;
  overflow-y: visible;
}
.sb-stage--editor { overflow: visible; }

/* Die ENTWURFSFLÄCHE. Ihr festes Seitenverhältnis ist der Grund, warum die
   Vorschau im Builder maßstäblich dasselbe zeigt wie die echte Seite.
   Sie ist zugleich der Anker für alle cqw-Angaben der Kinder.

   Die Mindesthöhe steht seit 0.9.1 HIER. Sind beide gesetzt, gewinnt
   min-height gegen aspect-ratio: Die Fläche wird höher als das Verhältnis,
   statt daneben eine leere Bühne stehen zu lassen. Genau das ist gewollt —
   die Fläche IST der Kopfbereich, und sie hört auf, wo er aufhört. */
.sb-canvas {
  position: relative;
  flex: 0 0 auto;
  width: min(100%, var(--stage-maxw, 2200px));
  aspect-ratio: var(--stage-ar, 1280 / 780);
  min-height: var(--stage-min, 320px);
  container-type: size;
  container-name: stage;
}

/* -------------------- Ein Element auf der Bühne -------------------- */
.sb-el {
  position: absolute;
  display: flex;
  transform-origin: center center;
  will-change: transform;
}

/* Der Kopfbereich liegt randlos hinter allem und füllt die ganze Bühne. */
.sb-el--header {
  position: absolute;
  inset: 0;
  width: auto; height: auto;
  z-index: 0;
  pointer-events: none;
}
.sb-stage--editor .sb-el--header { pointer-events: auto; }
.sb-el > * { width: 100%; height: 100%; }
.sb-el[data-hidden="1"] { display: none; }

/* ------------------------------------------------------------------
   KLICKS GEHEN DURCH  (Fehler aus 0.8.5, behoben in 0.8.6)

   Elemente dürfen absichtlich über die Unterkante der Bühne hinausragen -
   das Aktions-Banner tut das sogar von Haus aus. Die Bühne liegt dabei ÜBER
   der Info-Leiste, damit so ein Überstand obenauf sitzt und nicht darunter
   verschwindet.

   Genau daraus wurde eine Falle: Ein Element ohne Klick-Aktion war zwar
   unsichtbar (leeres Banner) oder rein schmückend - sein Kasten fing
   trotzdem jeden Klick ab. Wer das Banner knapp unter den Kopfbereich legte
   und gerade keine Aktion laufen hatte, konnte das PLZ-Feld darunter nicht
   mehr anklicken. Mit laufender Aktion rückte alles auseinander, und der
   Fehler war wie weggezaubert - deshalb war er so schwer zu fassen.

   Regel auf der Kundenseite: Nur was eine Klick-Aktion hat, nimmt auch
   Klicks entgegen. Alles andere ist Dekoration und lässt sie durch.
   Im Editor bleibt selbstverständlich alles anfassbar.

   Preis: Text im Kopfbereich lässt sich nicht mehr mit der Maus markieren.
   Für Telefonnummern gibt es die Klick-Aktion "Anrufen" - auf dem Handy
   ohnehin das, was man will.

   Auch die Bühne selbst nimmt keine Klicks mehr entgegen: Die Info-Leiste
   darunter schiebt sich absichtlich ein Stück unter den Kopfbereich
   (schönerer Übergang). Dieser Streifen gehörte damit optisch zur Leiste,
   klicktechnisch aber zur Bühne.
   ------------------------------------------------------------------ */
.sb-stage:not(.sb-stage--editor) { pointer-events: none; }
.sb-stage:not(.sb-stage--editor) .sb-el { pointer-events: none; }
.sb-stage:not(.sb-stage--editor) .sb-el--clickable { pointer-events: auto; }

.sb-el--clickable { cursor: pointer; }
.sb-el--clickable:focus-visible { outline: 3px solid #fff; outline-offset: 3px; }

/* -------------------- Text -------------------- */
.sb-text {
  display: flex;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.sb-text__inner { display: block; width: 100%; }
.sb-text[data-gradient="1"] { -webkit-text-fill-color: transparent; }

/* -------------------- Bild --------------------
   Die innere Ebene trägt das Bild. Der Zoom vergrößert sie, der Rahmen
   darüber schneidet zu - wie in einem Fotoprogramm. */
.sb-image { background-color: transparent; position: relative; }
.sb-img { position: absolute; inset: 0; }
.sb-image--empty {
  border: 2px dashed rgba(255,255,255,.45);
  border-radius: 10px;
  background: rgba(255,255,255,.06);
  position: relative;
}
.sb-image--empty::after {
  content: "Bild wählen";
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255,255,255,.7);
  font: 600 clamp(11px, 1.4cqw, 16px)/1 "Inter", system-ui, sans-serif;
  letter-spacing: .04em;
}

/* -------------------- Form / Symbol / Knopf -------------------- */
.sb-shape--line { height: 100% !important; }
.sb-icon { display: flex; align-items: center; justify-content: center; line-height: 1; }
.sb-button {
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 10px 26px -12px rgba(0,0,0,.6);
  white-space: nowrap;
}

/* -------------------- Kopfbereich --------------------
   Hier sitzt das overflow:hidden - und nur hier. Es hält das
   Hintergrundbild in Form, ohne überstehende Elemente abzuschneiden. */
.sb-header { position: relative; overflow: hidden; width: 100%; height: 100%; }
.sb-header__bg,
.sb-header__veil { position: absolute; inset: 0; }
.sb-header__edge {
  position: absolute; left: 0; right: 0; bottom: -1px;
  width: 100%; display: block;
}
.sb-header__edge path { fill: var(--page-bg, #FAF7F2); }

/* -------------------- Aktions-Banner --------------------

   Der Textsatz hing bis 0.9.0 an `cqw` der BÜHNE. Damit blieb die Schrift
   gleich groß, wenn man das Banner flacher zog - und wurde einfach
   abgeschnitten. Zwei Zeilen standen in einem Kasten, in den längst nur noch
   eine passte.

   Seit 0.9.1 ist das Banner sein EIGENER Container, und die Schrift rechnet
   in `cqh` - Prozent der Bannerhöhe. Wird das Banner flacher, schrumpft die
   Schrift mit. Die zusätzliche Deckelung über `cqw` fängt den anderen Fall
   ab: ein sehr hohes, schmales Banner, in dem die Schrift sonst breiter
   würde als der Platz. */
.sb-banner {
  display: flex; gap: .6rem; align-items: stretch;
  overflow: hidden;
  background: linear-gradient(100deg, var(--pb-from), var(--pb-to));
  color: var(--pb-text);
  box-shadow: 0 16px 40px -20px rgba(0,0,0,.7);
  padding: .5rem .8rem;
  container-type: size;
  container-name: banner;
}
/* Ohne laufende Aktion verschwindet die GANZE Ebene, nicht nur ihr Inhalt.
   Vorher blieb ein leerer, unsichtbarer Kasten stehen - siehe die Erklärung
   weiter oben bei "Klicks gehen durch". */
.sb-banner[data-empty="1"] { display: none; }
.sb-el--banner[data-empty="1"] { display: none; }
.sb-stage--editor .sb-el--banner[data-empty="1"] { display: flex; }
.sb-stage--editor .sb-banner[data-empty="1"] {
  display: flex; align-items: center; justify-content: center;
  opacity: .55;
}
.sb-stage--editor .sb-banner[data-empty="1"]::after {
  content: "Aktions-Banner — erscheint, sobald eine Aktion aktiv ist";
  font: 600 clamp(10px, 1.1cqw, 14px)/1.3 "Inter", system-ui, sans-serif;
  text-align: center; padding: 0 .8rem;
}
.sb-banner__card {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; justify-content: center;
  text-align: center; padding: .3rem .5rem;
}
.sb-banner__title {
  font: 800 var(--pb-title, clamp(11px, min(26cqh, 7cqw), 30px))/1.15
        "Bricolage Grotesque", system-ui, sans-serif;
  overflow: hidden; text-overflow: ellipsis;
}
/* Ohne zweite Zeile darf der Titel größer werden - die Höhe gehört ihm allein. */
.sb-banner__card[data-lines="1"] { --pb-title: clamp(12px, min(40cqh, 8cqw), 34px); }
/* Zuschnitt aus (0.8.9): Der Text bricht um, statt mit "..." zu enden.
   Für lange Aktionstexte, die vollständig dastehen sollen. */
.sb-banner--free { overflow: visible; }
.sb-banner--free .sb-banner__title {
  overflow: visible; text-overflow: clip; white-space: normal;
}
.sb-banner__rules {
  display: flex; gap: .4rem; flex-wrap: wrap; justify-content: center;
  margin-top: .15rem;
  font: 500 clamp(8px, min(17cqh, 4.5cqw), 15px)/1.25 "Inter", system-ui, sans-serif;
  opacity: .9;
  /* Passt die zweite Zeile nicht mehr, verschwindet sie - statt den Titel
     mit halben Buchstaben aus dem Banner zu drücken. Der Titel ist die
     Aussage, die Regel die Fußnote. */
  overflow: hidden;
}
.sb-banner--sm { --pb-pad: .3rem; }
.sb-banner--lg .sb-banner__title { --pb-title: clamp(13px, min(32cqh, 8cqw), 36px); }
.sb-banner--lg .sb-banner__card[data-lines="1"] { --pb-title: clamp(14px, min(46cqh, 9cqw), 42px); }
.sb-banner--sm .sb-banner__title { --pb-title: clamp(10px, min(22cqh, 6cqw), 22px); }

/* ============================================================
   ANIMATIONEN

   Aufbau immer gleich:
     • Die Bewegung sitzt auf dem inneren .sb-anim, nicht auf .sb-el.
       So kollidiert sie nicht mit der Drehung des Elements.
     • Alles läuft über CSS-Variablen, die sitecore.js setzt.
     • animation-play-state: paused, bis .is-inview gesetzt wird.
     • --anim-iter entscheidet zwischen Dauerlauf und Einmal.
   ============================================================ */
.sb-el[data-anim]:not([data-anim="none"]) > .sb-anim {
  animation-duration: var(--anim-dur, 1s);
  animation-delay: var(--anim-delay, 0s);
  animation-iteration-count: var(--anim-iter, infinite);
  animation-fill-mode: both;
  animation-play-state: paused;
}

/* ACHTUNG - hier lag der Fehler, der ALLE Animationen stillgelegt hat:
   Diese Regel muss mindestens so spezifisch sein wie die obige. Stand hier
   nur ".sb-el.is-inview > .sb-anim", gewann das "paused" von oben (mehr
   Auswahlteile), und keine einzige Animation ist je losgelaufen - obwohl die
   Klasse .is-inview korrekt gesetzt wurde. Beim Ändern also immer beide
   Regeln im selben Zuschnitt halten. */
.sb-el.is-inview[data-anim]:not([data-anim="none"]) > .sb-anim {
  animation-play-state: running;
}

/* -------------------- Pause zwischen zwei Durchläufen --------------------
   Der Taktgeber in sitecore.js hängt diese Klasse für einen Wimpernschlag an
   und nimmt sie sofort wieder ab. In dem Moment ist keine Animation gesetzt -
   und beim Abnehmen fängt sie sauber von vorn an. Ohne `!important` würden
   die Regeln weiter unten gewinnen, die den Namen je Animationsart setzen. */
.sb-el.sb-anim-off > .sb-anim,
.sb-el.sb-anim-off > .sb-anim::after,
.sb-el.sb-anim-off > .sb-anim .sb-text__inner,
.sb-el.sb-anim-off > .sb-anim > .ic-fx {
  animation-name: none !important;
}

/* Solange eine Einmal-Animation noch nicht dran war, bleibt das Element
   unsichtbar — sonst blitzt es kurz auf und rutscht dann erst herein. */
.sb-el[data-anim="fade"]:not(.is-inview) > .sb-anim,
.sb-el[data-anim="slide"]:not(.is-inview) > .sb-anim,
.sb-el[data-anim="pop"]:not(.is-inview) > .sb-anim,
.sb-el[data-anim="flip"]:not(.is-inview) > .sb-anim { opacity: 0; }

/* -------------------- Einblenden -------------------- */
.sb-el[data-anim="fade"] > .sb-anim {
  animation-name: sb-fade;
  animation-timing-function: cubic-bezier(.22,.61,.36,1);
}
.sb-el[data-anim="fade"][data-anim-dir="up"] > .sb-anim { --fx: 0; --fy: var(--anim-dist, 26px); }
.sb-el[data-anim="fade"][data-anim-dir="down"] > .sb-anim { --fx: 0; --fy: calc(var(--anim-dist, 26px) * -1); }
.sb-el[data-anim="fade"][data-anim-dir="left"] > .sb-anim { --fx: calc(var(--anim-dist, 26px) * -1); --fy: 0; }
.sb-el[data-anim="fade"][data-anim-dir="right"] > .sb-anim { --fx: var(--anim-dist, 26px); --fy: 0; }
.sb-el[data-anim="fade"][data-anim-dir="none"] > .sb-anim { --fx: 0; --fy: 0; }
@keyframes sb-fade {
  from { opacity: 0; transform: translate(var(--fx, 0), var(--fy, 26px)); }
  to   { opacity: 1; transform: translate(0, 0); }
}

/* -------------------- Reinschieben -------------------- */
.sb-el[data-anim="slide"] > .sb-anim {
  animation-name: sb-fade;                     /* gleiche Kurve, größerer Weg */
  animation-timing-function: cubic-bezier(.16,.84,.32,1);
}
.sb-el[data-anim="slide"][data-anim-dir="up"] > .sb-anim { --fx: 0; --fy: var(--anim-dist, 90px); }
.sb-el[data-anim="slide"][data-anim-dir="down"] > .sb-anim { --fx: 0; --fy: calc(var(--anim-dist, 90px) * -1); }
.sb-el[data-anim="slide"][data-anim-dir="left"] > .sb-anim { --fx: calc(var(--anim-dist, 90px) * -1); --fy: 0; }
.sb-el[data-anim="slide"][data-anim-dir="right"] > .sb-anim { --fx: var(--anim-dist, 90px); --fy: 0; }

/* -------------------- Reinpoppen -------------------- */
.sb-el[data-anim="pop"] > .sb-anim {
  animation-name: sb-pop;
  animation-timing-function: cubic-bezier(.34,1.56,.64,1);
}
@keyframes sb-pop {
  0%   { opacity: 0; transform: scale(.72); }
  60%  { opacity: 1; transform: scale(calc(1 + var(--anim-amp, 18%) / 100)); }
  100% { opacity: 1; transform: scale(1); }
}

/* -------------------- 3D-Flip -------------------- */
.sb-el[data-anim="flip"] > .sb-anim {
  animation-name: sb-flip-y;
  animation-timing-function: cubic-bezier(.22,.61,.36,1);
  backface-visibility: hidden;
}
.sb-el[data-anim="flip"][data-anim-axis="x"] > .sb-anim { animation-name: sb-flip-x; }
@keyframes sb-flip-y {
  from { opacity: 0; transform: perspective(900px) rotateY(-90deg); }
  to   { opacity: 1; transform: perspective(900px) rotateY(0); }
}
@keyframes sb-flip-x {
  from { opacity: 0; transform: perspective(900px) rotateX(90deg); }
  to   { opacity: 1; transform: perspective(900px) rotateX(0); }
}

/* -------------------- Schweben -------------------- */
.sb-el[data-anim="float"] > .sb-anim {
  animation-name: sb-float-y;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
.sb-el[data-anim="float"][data-anim-axis="x"] > .sb-anim { animation-name: sb-float-x; }
@keyframes sb-float-y {
  from { transform: translateY(calc(var(--anim-dist, 12px) / -2)); }
  to   { transform: translateY(calc(var(--anim-dist, 12px) / 2)); }
}
@keyframes sb-float-x {
  from { transform: translateX(calc(var(--anim-dist, 12px) / -2)); }
  to   { transform: translateX(calc(var(--anim-dist, 12px) / 2)); }
}

/* -------------------- Pulsieren -------------------- */
.sb-el[data-anim="pulse"] > .sb-anim {
  animation-name: sb-pulse;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
@keyframes sb-pulse {
  from { transform: scale(1); }
  to   { transform: scale(calc(1 + var(--anim-amp, 6%) / 100)); }
}

/* -------------------- Wackeln -------------------- */
.sb-el[data-anim="wobble"] > .sb-anim {
  animation-name: sb-wobble;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
@keyframes sb-wobble {
  from { transform: rotate(calc(var(--anim-angle, 3deg) * -1)); }
  to   { transform: rotate(var(--anim-angle, 3deg)); }
}

/* -------------------- Schimmer (nur Text) -------------------- */
.sb-el[data-anim="shimmer"] > .sb-anim {
  background-image: linear-gradient(
      var(--anim-angle, 100deg),
      var(--shimmer-base, currentColor) 0%,
      var(--shimmer-base, currentColor) calc(50% - var(--anim-width, 35%) / 2),
      var(--anim-color, #fff) 50%,
      var(--shimmer-base, currentColor) calc(50% + var(--anim-width, 35%) / 2),
      var(--shimmer-base, currentColor) 100%);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation-name: sb-shimmer;
  animation-timing-function: linear;
}
@keyframes sb-shimmer {
  from { background-position: 150% 0; }
  to   { background-position: -150% 0; }
}

/* -------------------- Umriss-Glühen -------------------- */
.sb-el[data-anim="glow"] > .sb-anim {
  animation-name: sb-glow;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
@keyframes sb-glow {
  from { filter: drop-shadow(0 0 0 var(--anim-color, #E8A33D)); }
  to   { filter: drop-shadow(0 0 var(--anim-amp, 18px) var(--anim-color, #E8A33D)); }
}

/* -------------------- Schreibmaschine (nur Text) -------------------- */
.sb-el[data-anim="typewriter"] > .sb-anim .sb-text__inner {
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  vertical-align: bottom;
  animation: sb-type var(--anim-dur, 2.4s) steps(40, end) var(--anim-delay, 0s)
             var(--anim-iter, 1) both;
  animation-play-state: paused;
  border-right: .08em solid transparent;
}
.sb-el[data-anim="typewriter"].is-inview > .sb-anim .sb-text__inner { animation-play-state: running; }
.sb-el[data-anim="typewriter"][data-anim-cursor="1"] > .sb-anim .sb-text__inner {
  border-right-color: var(--anim-cursor, #E8A33D);
  animation: sb-type var(--anim-dur, 2.4s) steps(40, end) var(--anim-delay, 0s) var(--anim-iter, 1) both,
             sb-caret .8s step-end infinite;
  animation-play-state: paused;
}
.sb-el[data-anim="typewriter"][data-anim-cursor="1"].is-inview > .sb-anim .sb-text__inner { animation-play-state: running; }
@keyframes sb-type { from { max-width: 0; } to { max-width: 100%; } }
@keyframes sb-caret { 50% { border-right-color: transparent; } }

/* -------------------- Ken Burns (nur Bild) -------------------- */
.sb-el[data-anim="kenburns"] > .sb-anim {
  animation-name: sb-kb-center;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
.sb-el[data-anim="kenburns"][data-anim-dir="up"] > .sb-anim { animation-name: sb-kb-up; }
.sb-el[data-anim="kenburns"][data-anim-dir="down"] > .sb-anim { animation-name: sb-kb-down; }
.sb-el[data-anim="kenburns"][data-anim-dir="left"] > .sb-anim { animation-name: sb-kb-left; }
.sb-el[data-anim="kenburns"][data-anim-dir="right"] > .sb-anim { animation-name: sb-kb-right; }
@keyframes sb-kb-center {
  from { transform: scale(1); } to { transform: scale(var(--anim-zoom, 1.12)); }
}
@keyframes sb-kb-up {
  from { transform: scale(1) translateY(0); }
  to   { transform: scale(var(--anim-zoom, 1.12)) translateY(-3%); }
}
@keyframes sb-kb-down {
  from { transform: scale(1) translateY(0); }
  to   { transform: scale(var(--anim-zoom, 1.12)) translateY(3%); }
}
@keyframes sb-kb-left {
  from { transform: scale(1) translateX(0); }
  to   { transform: scale(var(--anim-zoom, 1.12)) translateX(-3%); }
}
@keyframes sb-kb-right {
  from { transform: scale(1) translateX(0); }
  to   { transform: scale(var(--anim-zoom, 1.12)) translateX(3%); }
}

/* -------------------- Glanzstreifen -------------------- */
.sb-el[data-anim="shine"] > .sb-anim { position: relative; overflow: hidden; }
.sb-el[data-anim="shine"] > .sb-anim::after {
  content: "";
  position: absolute; top: -60%; bottom: -60%;
  left: -40%;
  width: var(--anim-width, 18%);
  background: linear-gradient(90deg, transparent, var(--anim-color, #fff), transparent);
  opacity: .55;
  transform: rotate(var(--anim-angle, 25deg));
  animation: sb-shine var(--anim-dur, 4.5s) ease-in-out var(--anim-delay, 0s)
             var(--anim-iter, infinite) both;
  animation-play-state: paused;
  pointer-events: none;
}
.sb-el[data-anim="shine"].is-inview > .sb-anim::after { animation-play-state: running; }
@keyframes sb-shine {
  0%   { left: -40%; }
  55%  { left: 130%; }
  100% { left: 130%; }
}

/* ============================================================
   RÜCKSICHT AUF "BEWEGUNG REDUZIEREN"

   Wer das im Betriebssystem eingeschaltet hat, hat meist einen guten Grund
   (Migräne, Schwindel). Dauerläufer stehen dann still; Auftritte bleiben
   erhalten, aber sehr kurz — sonst wäre das Element gar nicht sichtbar.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .sb-el[data-anim-loop="1"] > .sb-anim,
  .sb-el[data-anim-loop="1"] > .sb-anim::after {
    animation: none !important;
  }
  .sb-el[data-anim] > .sb-anim { animation-duration: .01s !important; }
  .sb-el:not(.is-inview) > .sb-anim { opacity: 1 !important; }
}

/* ============================================================
   HANDY
   ============================================================ */
@media (max-width: 720px) {
  .sb-el[data-hide-mobile="1"] { display: none; }
}

/* ==================== Mehrere Animationen (ab 0.8.9) ====================
   Zwei Animationen auf demselben Element gehen nicht - beide schreiben
   `transform`, die zweite gewinnt. Jede bekommt deshalb ihre eigene, inein-
   ander geschachtelte Ebene; ihre Transformationen multiplizieren sich, was
   genau dem entspricht, was man erwartet: Der Zoom skaliert, was der Glanz
   darin bewegt.

   Die Zwischenebene traegt bewusst auch `.sb-el` - daran haengen alle
   Animationsregeln oben. Hier bekommt sie nur ihre absolute Lage wieder
   abgenommen. */
.sb-el.sb-animsub {
  position: static;
  inset: auto;
  width: 100%;
  height: 100%;
  display: flex;
}
.sb-el.sb-animsub > .sb-anim { width: 100%; height: 100%; }

/* ==================== Weichgezeichneter Seitenhintergrund (0.8.9) ========
   Der Filter darf nicht auf der Seite selbst liegen - dort wuerde er
   Speisekarte, Warenkorb und Schrift gleich mit weichzeichnen. Das Bild
   bekommt deshalb eine eigene Flaeche HINTER dem Inhalt. */
.page-blurbg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  transform-origin: center center;
  will-change: filter;
}

/* ==================== Weichzeichner-Flaeche (0.8.9) ====================
   Eine Form, die nicht selbst etwas zeigt, sondern alles UNTER sich
   weichzeichnet. Fuer Text ueber unruhigen Fotos. */
.sb-blurbox {
  /* Ohne eigene Ebene greift backdrop-filter in manchen Browsern nicht. */
  isolation: isolate;
  -webkit-backdrop-filter: blur(0px);
}
/* Browser ohne backdrop-filter (aeltere Fassungen): dann wenigstens ein
   dezenter Schleier statt einer unsichtbaren, wirkungslosen Flaeche. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .sb-blurbox { background: rgba(255,255,255,.14) !important; }
}
/* Im Editor sichtbar machen, auch wenn gerade nichts darunter liegt -
   sonst waehlt man eine unsichtbare Flaeche aus und sucht sie. */
.sb-stage--editor .sb-blurbox {
  outline: 1px dashed rgba(255,255,255,.35);
  outline-offset: -1px;
}

/* ==================== KI-Hinweis (ab 1.3.0) ====================
   Das kleine Kreissymbol „KI“/„AI“ auf jedem Bild, das mit KI bearbeitet
   oder erzeugt ist (KI-VO Art. 50 Abs. 4). Hier und nicht in shop.css,
   weil site.css auf der Kundenseite UND in allen Baukästen geladen ist –
   die Vorschau soll das Symbol genauso zeigen wie die Speisekarte.

   Größe und Deckkraft kommen als Variablen aus dem Item- bzw.
   Category-Builder (ki_hinweis). Die doppelte Klasse schlägt
   `.sb-el > * { width: 100% }` weiter oben: Sonst würde das Symbol im
   Kopfbereich die ganze Bühne füllen. */
.ki-mark.ki-mark {
  --ki-inset: max(4px, calc(var(--ki-size, 22px) * .3));
  position: absolute;
  z-index: 60;
  width: var(--ki-size, 22px);
  height: var(--ki-size, 22px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  font: 800 calc(var(--ki-size, 22px) * .4)/1 "Inter", system-ui, sans-serif;
  letter-spacing: .02em;
  text-transform: none;
  color: #fff;
  background: rgba(20, 17, 15, .6);
  border: 1px solid rgba(255, 255, 255, .7);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
  opacity: var(--ki-op, .75);
  /* Die Bühne der Kundenseite lässt Klicks durch (siehe oben) – das Symbol
     muss sie trotzdem annehmen, sonst gäbe es am Handy keine Erklärung. */
  pointer-events: auto;
  cursor: help;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* Ein 22-px-Kreis ist für einen Finger zu klein. Die Trefferfläche wächst
   unsichtbar um 8 px – innerhalb der Kachel, die ohnehin abschneidet. */
.ki-mark.ki-mark::before { content: ""; position: absolute; inset: -8px; border-radius: 50%; }
.ki-mark[hidden] { display: none !important; }
.ki-mark--tl { top: var(--ki-inset); left: var(--ki-inset); }
.ki-mark--tr { top: var(--ki-inset); right: var(--ki-inset); }
.ki-mark--bl { bottom: var(--ki-inset); left: var(--ki-inset); }
.ki-mark--br { bottom: var(--ki-inset); right: var(--ki-inset); }

/* Die Erklärung. Fest am Fenster statt im Bild: Eine Kachel schneidet ab,
   und die Blase muss über den Rand der Kachel hinaus lesbar sein. */
.ki-blase {
  position: fixed;
  z-index: 10050;
  max-width: min(290px, calc(100vw - 20px));
  padding: .55rem .75rem .6rem;
  border-radius: 10px;
  background: #1F1B18;
  color: #F6F1EA;
  font: 500 13px/1.45 "Inter", system-ui, sans-serif;
  text-align: left;
  box-shadow: 0 12px 30px -10px rgba(0, 0, 0, .55);
  pointer-events: none;
}
.ki-blase[hidden] { display: none; }
.ki-blase b {
  display: block;
  margin-bottom: 2px;
  font-weight: 800;
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #E8A33D;
}
