/* boom · running pig — gebündelt + minifiziert */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%}html{color-scheme:dark;background-color:#000;-webkit-text-size-adjust:100%;text-size-adjust:100%}body{overflow:hidden;overscroll-behavior:none;background:#000;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}img,svg{display:block;max-width:100%}@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:0.001ms !important;animation-iteration-count:1 !important;transition-duration:0.001ms !important}}:root{--color-sky-top:#bde3f0;--color-sky-bottom:#eaf6fb;--color-cloud:#ffffff;--color-ground:#d9e8c5;--color-ground-line:#c4d9aa;--pig-size:clamp(240px,40vmin,380px);--ground-height:22vh}@font-face{font-family:"Sweedolicious";src:url("../assets/fonts/sweedolicious/sweedolicious.woff2") format("woff2");font-weight:normal;font-style:normal;font-display:swap}.stage{position:fixed;inset:0;overflow:hidden;width:100%;height:100vh;height:100dvh;background:#000}.sky,.ground,.pig{position:absolute}.sky{inset:0;z-index:1}.ground{left:0;right:0;bottom:0;height:var(--ground-height);z-index:2}.pig{left:50%;bottom:calc(var(--ground-height) - 1vh);transform:translateX(-50%);z-index:3}.sky{background:linear-gradient( to bottom,var(--color-sky-top) 0%,var(--color-sky-bottom) 100% )}.cloud{position:absolute;display:block;left:100vw;width:18vmin;height:7vmin;background:var(--color-cloud);border-radius:50%;filter:blur(0.4px);opacity:0.9;animation:cloud-drift 90s linear infinite}.cloud::before,.cloud::after{content:"";position:absolute;background:inherit;border-radius:50%}.cloud::before{width:60%;height:160%;top:-60%;left:12%}.cloud::after{width:45%;height:130%;top:-40%;right:14%}.cloud--1{top:14%;width:18vmin;height:7vmin;animation-duration:105s;animation-delay:-10s}.cloud--2{top:30%;width:13vmin;height:5vmin;animation-duration:120s;animation-delay:-36s}.cloud--3{top:9%;width:24vmin;height:9vmin;animation-duration:95s;animation-delay:-48s}.cloud--4{top:22%;width:10vmin;height:4vmin;animation-duration:135s;animation-delay:-95s}.cloud--5{top:35%;width:16vmin;height:6vmin;animation-duration:110s;animation-delay:-99s}@keyframes cloud-drift{from{transform:translateX(0)}to{transform:translateX(-130vw)}}.ground{background:linear-gradient( to bottom,var(--color-ground) 0%,var(--color-ground-line) 100% );border-top:2px solid var(--color-ground-line);box-shadow:0 -1px 12px rgba(255,255,255,0.5) inset}.pig{width:var(--pig-size)}.pig::before{content:"";position:absolute;left:50%;bottom:-1%;width:58%;height:5.5%;z-index:-1;border-radius:50%;background:radial-gradient(ellipse at center,rgba(30,26,22,0.60) 0%,rgba(30,26,22,0.42) 42%,rgba(30,26,22,0) 74%);filter:blur(2.5px);transform:translateX(-50%) scaleX(calc(1 + var(--energy,0) * 0.35))}.pig__viewport{width:100%;aspect-ratio:220 / 132;overflow:hidden;transform:scale(var(--pig-fit,1)) scale(calc(1 + var(--beat,0) * 0.05),calc(1 - var(--beat,0) * 0.08));transform-origin:50% 96.97%}.pig__film{height:100%;background-repeat:no-repeat;background-position:left center;background-size:100% 100%;will-change:transform}.pig[data-gait="trot"] .pig__film{background-image:url("../assets/pig/pig-trot.png");width:800%}.pig[data-gait="run"] .pig__film{background-image:url("../assets/pig/pig-run.png");width:800%}.pig[data-gait="sprint"] .pig__film{background-image:url("../assets/pig/pig-sprint.png");width:900%}.pig[data-gait="trot"]{--pig-fit:1.000}.pig[data-gait="run"]{--pig-fit:1.025}.pig[data-gait="sprint"]{--pig-fit:1.130}.audio-toggle{position:fixed;right:calc(max(18px,3vmin) + env(safe-area-inset-right,0px));bottom:calc(max(18px,3vmin) + env(safe-area-inset-bottom,0px));z-index:10;display:flex;align-items:center;gap:9px;padding:0;border:0;background:transparent;cursor:pointer}.audio-toggle__dot{flex:0 0 auto;width:8px;height:8px;border-radius:50%;background:#e2453b;box-shadow:0 0 7px 1px rgba(226,69,59,0.85);transition:background-color 0.2s ease,box-shadow 0.2s ease}.audio-toggle:not(.is-muted) .audio-toggle__dot{background:#2ecc71;box-shadow:0 0 7px 1px rgba(46,204,113,0.9)}.audio-toggle__label{font-family:"Sweedolicious",system-ui,sans-serif;font-size:clamp(15px,3.7vmin,21px);line-height:1;color:#2a2320;text-shadow:0 2px 4px rgba(30,26,22,0.18);transition:transform 0.15s ease}.audio-toggle:hover .audio-toggle__label{transform:translateY(-1px) scale(1.06)}.audio-toggle:active .audio-toggle__label{transform:none}.audio-toggle:focus-visible{outline:2px solid #5a4a42;outline-offset:3px;border-radius:10px}.audio-toggle.is-loading .audio-toggle__dot{animation:audio-pulse 1s ease-in-out infinite}@keyframes audio-pulse{50%{opacity:0.4}}@media (prefers-reduced-motion:reduce){.audio-toggle.is-loading .audio-toggle__dot{animation:none}.audio-toggle__label{transition:none}}.impressum-toggle{position:fixed;left:calc(max(18px,3vmin) + env(safe-area-inset-left,0px));bottom:calc(max(18px,3vmin) + env(safe-area-inset-bottom,0px));z-index:10;display:block;padding:0;border:0;background:transparent;cursor:pointer;transition:opacity 0.3s ease}.impressum-toggle__pig{display:block;width:48px;height:48px;filter:drop-shadow(0 2px 4px rgba(30,26,22,0.25));transition:transform 0.15s ease}.impressum-toggle:hover .impressum-toggle__pig{transform:translateY(-1px) scale(1.06)}.impressum-toggle:active .impressum-toggle__pig{transform:none}.impressum-toggle:focus-visible{outline:2px solid #5a4a42;outline-offset:3px;border-radius:10px}@media (prefers-reduced-motion:reduce){.impressum-toggle__pig{transition:none}}.sky,.ground,.pig,.audio-toggle{transition:filter 0.55s ease}body.impressum-open .sky,body.impressum-open .ground,body.impressum-open .pig,body.impressum-open .audio-toggle{filter:grayscale(1)}body.impressum-open .impressum-toggle{opacity:0;pointer-events:none}@media (prefers-reduced-motion:reduce){.impressum-toggle{transition:none}.sky,.ground,.pig,.audio-toggle{transition:none}}.impressum-panel{position:absolute;inset:0;z-index:60;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:max(12px,env(safe-area-inset-top,0px)) max(16px,env(safe-area-inset-right,0px)) max(12px,env(safe-area-inset-bottom,0px)) max(16px,env(safe-area-inset-left,0px));font-family:"Sweedolicious",system-ui,sans-serif;color:#2a2320;opacity:0;transform:scale(0.12);transform-origin:left bottom;transition:transform 0.55s cubic-bezier(0.22,1,0.36,1),opacity 0.4s ease;pointer-events:none}body.impressum-open .impressum-panel{opacity:1;transform:none;pointer-events:auto;cursor:zoom-out}.impressum-panel__art{position:relative;width:clamp(240px,62vmin,560px);aspect-ratio:1 / 1}.impressum-panel__pig{width:100%;height:100%;display:block;cursor:zoom-out;filter:drop-shadow(0 6px 16px rgba(30,26,22,0.25))}.impressum-panel__curve{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}.impressum-panel__mail,.impressum-panel__tel{text-decoration:none}body.impressum-open .impressum-panel__pig,body.impressum-open .impressum-panel__mail,body.impressum-open .impressum-panel__tel{pointer-events:auto}body.impressum-open .impressum-panel__mail,body.impressum-open .impressum-panel__tel{cursor:pointer}.impressum-panel__mail text,.impressum-panel__tel text{font-family:"Sweedolicious",system-ui,sans-serif;fill:#2a2320;text-anchor:middle;paint-order:stroke;stroke:rgba(255,255,255,0.55);stroke-width:3px}.impressum-panel__mail text{font-size:58px}.impressum-panel__tel text{font-size:52px}.impressum-panel__mail:hover text,.impressum-panel__tel:hover text{fill:#000}.impressum-panel__meta{--u:clamp(0.7rem,3.2vw,1.25rem);position:absolute;left:0;right:0;bottom:calc(max(18px,3vmin) + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;align-items:center;gap:0.04em;line-height:1.02;text-shadow:0 1px 4px rgba(30,26,22,0.18)}.impressum-panel__line{display:block;white-space:nowrap}.impressum-panel__name{font-size:calc(var(--u) * 2.21)}.impressum-panel__city{font-size:calc(var(--u) * 1.34)}.impressum-panel__street{font-size:calc(var(--u) * 1.00)}.impressum-panel__privacy{margin-top:1.1em;font-family:"Sweedolicious",system-ui,sans-serif;font-size:clamp(15px,3.7vmin,21px);line-height:1;text-decoration:none;color:#2a2320;cursor:pointer}body.impressum-open .impressum-panel__privacy{pointer-events:auto}.impressum-panel__privacy:hover{color:#000}.privacy-popup{position:absolute;inset:0;z-index:70;pointer-events:none;visibility:hidden;cursor:zoom-out;transition:visibility 0s linear 0.45s}body.privacy-open .privacy-popup{pointer-events:auto;visibility:visible;transition:visibility 0s}.privacy-popup__box{position:absolute;top:0;left:0;right:0;background:rgba(224,226,229,0.88);border-radius:0 0 16px 16px;padding:calc(max(28px,env(safe-area-inset-top,0px)) + 8px) clamp(22px,6vw,72px) 34px;box-shadow:0 10px 40px rgba(30,26,22,0.25);font-family:"Sweedolicious",system-ui,sans-serif;color:#2a2320;font-size:clamp(17px,2.8vw,22px);line-height:1.45;text-align:left;max-height:100dvh;overflow-y:auto;-webkit-overflow-scrolling:touch;transform:translateY(-100%);transition:transform 0.45s cubic-bezier(0.22,1,0.36,1)}body.privacy-open .privacy-popup__box{transform:translateY(0)}.privacy-popup__box p{margin:0 0 0.7em}.privacy-popup__box p:last-child{margin-bottom:0}@media (max-height:620px){.impressum-panel{justify-content:flex-start}.impressum-panel__art{margin-top:max(10px,env(safe-area-inset-top,0px));width:min(48vh,clamp(190px,62vmin,560px))}.impressum-panel__meta{--u:clamp(0.6rem,2.4vw,1rem)}}@media (orientation:landscape) and (max-height:480px){.impressum-panel__art{display:none}.impressum-panel__meta{--u:clamp(0.55rem,2.6vh,1.05rem);text-shadow:none}.impressum-panel__privacy{margin-top:0.8em}}@media (orientation:landscape) and (max-height:620px){.privacy-popup__box{font-size:clamp(13px,3.4vh,22px);line-height:1.32;padding:calc(max(12px,env(safe-area-inset-top,0px)) + 6px) clamp(18px,6vw,72px) 18px}.privacy-popup__box p{margin:0 0 0.5em}}@media (prefers-reduced-motion:reduce){.impressum-panel{transition:opacity 0.2s ease;transform:none}}
/* ===== Welt: Parallax, Staub, Tempo-Effekte ===== */
:root{--color-hill:#cfe4bd;--color-ground-dark:#bcd4a1;--color-fore:#a9c489;--color-dust:198,183,149}
.fx{position:absolute;width:0;height:0;overflow:hidden;pointer-events:none}
.world{position:absolute;inset:0;transition:filter 0.55s ease}
.sky{z-index:1}.hills{z-index:2}.ground{z-index:3;overflow:hidden}.dust{z-index:4}.pig{z-index:5}.fore{z-index:6}
.hills,.wind,.fore,.dust{pointer-events:none}
.hills__band,.ground__far,.ground__near,.wind__band,.fore__band{position:absolute;left:0;background-repeat:repeat-x;will-change:transform}

/* Ferne Huegelkette: traegste Ebene, sitzt auf dem Horizont */
.hills{position:absolute;left:0;right:0;bottom:var(--ground-height);height:10vh;overflow:hidden}
.hills__band{bottom:0;height:100%;width:calc(100% + var(--tile,560px));background-image:radial-gradient(58% 128% at 16% 100%,var(--color-hill) 0 62%,transparent 63%),radial-gradient(44% 104% at 49% 100%,var(--color-hill) 0 62%,transparent 63%),radial-gradient(66% 148% at 82% 100%,var(--color-hill) 0 62%,transparent 63%);background-size:var(--tile,560px) 100%;background-position:0 100%;opacity:0.5}

/* Windstreifen: erst ab Tempo sichtbar */
.wind{position:absolute;left:0;right:0;top:0;height:46%;overflow:hidden}
.wind__band{top:0;height:100%;width:calc(100% + var(--tile,460px));background-image:linear-gradient(90deg,transparent 0 9%,rgba(255,255,255,0.60) 9% 19%,transparent 19% 100%),linear-gradient(90deg,transparent 0 46%,rgba(255,255,255,0.45) 46% 58%,transparent 58% 100%),linear-gradient(90deg,transparent 0 70%,rgba(255,255,255,0.40) 70% 79%,transparent 79% 100%);background-size:var(--tile,460px) 2px;background-position:0 26%,0 52%,0 74%;filter:blur(0.7px);opacity:clamp(0,calc((var(--speed,0) - 0.42) * 1.15),0.55);transition:opacity 0.4s ease}

/* Bodentextur. Alle Reihen teilen die Kachelbreite der Ebene, damit der
   Umbruch nahtlos bleibt; unterschiedliche Strichmuster verstecken ihn. */
.ground__far{top:0;height:52%;width:calc(100% + var(--tile,300px));background-image:linear-gradient(90deg,transparent 0 7%,var(--color-ground-dark) 7% 11%,transparent 11% 39%,var(--color-ground-dark) 39% 42%,transparent 42% 100%),linear-gradient(90deg,transparent 0 22%,var(--color-ground-dark) 22% 25%,transparent 25% 63%,var(--color-ground-dark) 63% 68%,transparent 68% 100%),linear-gradient(90deg,transparent 0 48%,var(--color-ground-dark) 48% 51%,transparent 51% 86%,var(--color-ground-dark) 86% 89%,transparent 89% 100%);background-size:var(--tile,300px) 2px;background-position:0 22%,0 52%,0 80%;opacity:0.40}
.ground__near{top:38%;height:62%;width:calc(100% + var(--tile,220px));background-image:linear-gradient(90deg,transparent 0 6%,var(--color-ground-dark) 6% 12%,transparent 12% 37%,var(--color-ground-dark) 37% 41%,transparent 41% 100%),linear-gradient(90deg,transparent 0 20%,var(--color-ground-dark) 20% 25%,transparent 25% 61%,var(--color-ground-dark) 61% 67%,transparent 67% 100%),linear-gradient(90deg,transparent 0 44%,var(--color-ground-dark) 44% 48%,transparent 48% 82%,var(--color-ground-dark) 82% 88%,transparent 88% 100%),linear-gradient(90deg,transparent 0 13%,var(--color-ground-dark) 13% 17%,transparent 17% 55%,var(--color-ground-dark) 55% 59%,transparent 59% 100%),linear-gradient(90deg,transparent 0 33%,var(--color-ground-dark) 33% 39%,transparent 39% 73%,var(--color-ground-dark) 73% 76%,transparent 76% 100%);background-size:var(--tile,220px) 3px;background-position:0 10%,0 30%,0 50%,0 70%,0 90%;opacity:0.55}

/* Staub am Huf: Pool aus JS, Physik in app.js */
.dust{position:absolute;inset:0;overflow:hidden}
.dust__p{position:absolute;left:0;top:0;width:24px;height:24px;border-radius:50%;opacity:0;background:radial-gradient(circle at 50% 50%,rgba(var(--color-dust),0.85) 0%,rgba(var(--color-dust),0.40) 46%,rgba(var(--color-dust),0) 70%)}

/* Kameranaher Vordergrund: rauscht am schnellsten vorbei, daher unscharf */
.fore{position:absolute;left:0;right:0;bottom:0;height:13vh;overflow:hidden}
.fore__band{bottom:0;height:100%;width:calc(100% + var(--tile,260px));background-image:radial-gradient(30% 62% at 12% 108%,var(--color-fore) 0 68%,transparent 70%),radial-gradient(22% 48% at 38% 113%,var(--color-fore) 0 68%,transparent 70%),radial-gradient(34% 68% at 66% 106%,var(--color-fore) 0 68%,transparent 70%),radial-gradient(18% 42% at 88% 115%,var(--color-fore) 0 68%,transparent 70%);background-size:var(--tile,260px) 100%;background-position:0 100%;filter:blur(calc(2px + var(--speed,0) * 5px));opacity:calc(0.30 + var(--speed,0) * 0.35)}

/* Schatten atmet mit dem Bodenkontakt: eng und dunkel beim Aufsetzen,
   weit und blass in der Flugphase */
.pig::before{transform:translateX(-50%) scaleX(calc((1 + var(--energy,0) * 0.35) * (1 - var(--contact,0) * 0.12))) scaleY(calc(1 + var(--contact,0) * 0.30));opacity:calc(0.70 + var(--contact,0) * 0.30)}

/* Echte horizontale Bewegungsunschaerfe, kein Weichzeichner */
.pig.is-blurred .pig__viewport{filter:url(#fx-motion)}

body.impressum-open .world{filter:grayscale(1)}

/* ===== Labels ===== */
/* Ein Zustand, eine Regel. Beim Verbinden steht bereits FINDING THE BEAT -
   so bleibt die Schrift stehen und allein der Punkt wechselt Rot -> Gruen,
   ohne Sprung im Layout. */
.audio-toggle__label::after{content:"REST\A THE PIG"}
.audio-toggle.is-muted .audio-toggle__label::after{content:"FEED\A THE PIG"}
.audio-toggle.is-searching .audio-toggle__label::after,
.audio-toggle.is-loading .audio-toggle__label::after{content:"FINDING\A THE BEAT"}
.audio-toggle.is-searching .audio-toggle__dot{animation:audio-pulse 1s ease-in-out infinite}

/* ===== SomaFM-Nennung: erscheint, wenn der Ton laeuft ===== */
.audio-credit{position:fixed;right:calc(max(18px,3vmin) + env(safe-area-inset-right,0px));bottom:calc(max(18px,3vmin) + env(safe-area-inset-bottom,0px) + 26px);z-index:10;font-family:"Sweedolicious",system-ui,sans-serif;font-size:clamp(11px,2.3vmin,14px);line-height:1;color:#2a2320;text-decoration:none;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 0.5s ease}
body.audio-on .audio-credit{opacity:0.6;pointer-events:auto}
body.audio-on .audio-credit:hover{opacity:1}
body.impressum-open .audio-credit{opacity:0;pointer-events:none}
.audio-credit:focus-visible{outline:2px solid #5a4a42;outline-offset:3px;border-radius:8px;opacity:1}

@media (prefers-reduced-motion:reduce){.audio-toggle.is-searching .audio-toggle__dot{animation:none}.dust{display:none}.wind__band{opacity:0}.world,.audio-credit{transition:none}}

/* ===== Nachbesserungen Styling ===== */

/* Huegel: eine einzige weiche Wellenlinie statt einzelner Kuppen.
   Die Kurve endet mit derselben Steigung, mit der sie beginnt, daher
   kachelt sie nahtlos. Farbe steckt im Pfad (--color-hill entspricht ihr). */
.hills__band{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 560 100' preserveAspectRatio='none'%3E%3Cpath d='M0,58 C93,18 187,18 280,58 C373,98 467,98 560,58 L560,100 L0,100 Z' fill='%23cfe4bd'/%3E%3C/svg%3E");opacity:0.45}

/* Vordergrund: im Ruhezustand unsichtbar, taucht erst mit Tempo auf und
   bleibt weich - er soll Geschwindigkeit andeuten, nicht Steine zeigen */
:root{--color-fore:#bcd4a1}
.fore__band{filter:blur(calc(6px + var(--speed,0) * 5px));opacity:calc(var(--speed,0) * 0.42)}

/* Ecke unten rechts: SomaFM / Beat Blender / Label auf einer Fluchtlinie,
   der Statuspunkt haengt links daneben */
.audio-corner{position:fixed;right:calc(max(18px,3vmin) + env(safe-area-inset-right,0px));bottom:calc(max(18px,3vmin) + env(safe-area-inset-bottom,0px));z-index:10;display:flex;flex-direction:column;align-items:flex-start;gap:7px}
.audio-toggle{position:static;right:auto;bottom:auto}
.audio-credit{position:static;right:auto;bottom:auto;display:flex;flex-direction:column;align-items:flex-start;padding-left:17px;line-height:1.1}
.audio-credit__line{display:block}
.audio-toggle__label{white-space:pre;text-align:left;line-height:1.08}

/* Beim Laden nur der pulsende Punkt, keine Schrift */

/* ===== Unschaerfe nur im Hintergrund, Schwein bleibt scharf ===== */
.pig.is-blurred .pig__viewport{filter:none}
.world.is-blurred .ground__far,.world.is-blurred .ground__near{filter:url(#fx-motion)}

/* Der Groessensprung zwischen den Gangarten wird interpoliert statt geschaltet,
   damit der Wechsel trot/run/sprint - besonders beim Ausrollen - nicht kickt.
   Das Beat-Wippen bleibt davon unberuehrt und reagiert weiter sofort. */
@property --pig-fit{syntax:"<number>";inherits:true;initial-value:1}
.pig{transition:filter 0.55s ease,--pig-fit 0.30s cubic-bezier(0.22,1,0.36,1)}
@media (prefers-reduced-motion:reduce){.pig{transition:none}}

/* ===== Haertung ===== */
/* Unschaerfe nur auf der schnellsten Bodenebene. Die ferne Ebene zieht nur
   mit einem Drittel Tempo - scharf ist dort physikalisch richtig und spart
   eine grossflaechige Filterberechnung. */
.world.is-blurred .ground__far{filter:none}
/* Radien haengen an der groben Stufung, nicht an --speed */
.fore__band{filter:blur(calc(6px + var(--blur-q,0) * 5px))}

/* Der Hinweis darf nicht per Tabulator erreichbar sein, solange er unsichtbar
   ist - ein fokussierbares, unsichtbares Ziel ist eine Tastaturfalle */
.audio-credit{visibility:hidden;transition:opacity 0.5s ease,visibility 0s linear 0.5s}
body.audio-on .audio-credit{visibility:visible;transition:opacity 0.5s ease,visibility 0s}

/* Sehr flache Viewports (Telefon quer): die Ecke darf dem Schwein nicht
   ins Bild wachsen */
@media (orientation:landscape) and (max-height:480px){.audio-corner{gap:4px}.audio-credit{font-size:clamp(9px,2.6vh,12px)}.audio-toggle__label{font-size:clamp(13px,3.4vh,17px)}}
@media (prefers-reduced-motion:reduce){.audio-credit{transition:none}}

/* ===== Zwischenschritt ===== */

/* FINDING THE BEAT ist eine Statusmeldung, keine Aufforderung - sie darf
   beim Ueberfahren nicht mitwachsen. Klickbar bleibt sie. */
.audio-toggle.is-loading:hover .audio-toggle__label,
.audio-toggle.is-searching:hover .audio-toggle__label{transform:none}

/* Berge vorerst aus. Diese eine Zeile entfernen holt sie zurueck -
   Markup, Wellenlinie und Parallax-Anbindung bleiben unangetastet. */
.hills{display:none}

/* ===== Wolken: Lufttiefe =====
   Bisher hingen alle fuenf mit derselben Deckkraft (0.9) und derselben
   Silhouette im Himmel - fuenf Kopien desselben Objekts. Ohne die Berge
   traegt der Himmel die Tiefe jetzt allein, also uebernehmen das die Wolken:
   gross und schnell = nah und deckend, klein und langsam = fern und blass.
   Technik unveraendert - weiterhin drei Ellipsen, kein Verlauf. */
.cloud--3{opacity:0.95}
.cloud--1{opacity:0.88}
.cloud--5{opacity:0.80}
.cloud--2{opacity:0.70;filter:blur(0.8px)}
.cloud--4{opacity:0.60;filter:blur(1px)}

/* ... und jede bekommt eine eigene Silhouette, damit sie nicht als
   Wiederholung desselben Umrisses gelesen werden */
.cloud--2::before{width:52%;height:140%;top:-52%;left:20%}
.cloud--2::after{width:38%;height:112%;top:-32%;right:9%}
.cloud--3::before{width:66%;height:172%;top:-68%;left:8%}
.cloud--3::after{width:50%;height:120%;top:-30%;right:18%}
.cloud--4::before{width:58%;height:150%;top:-55%;left:16%}
.cloud--4::after{width:40%;height:118%;top:-32%;right:10%}
.cloud--5::before{width:64%;height:146%;top:-50%;left:9%}
.cloud--5::after{width:48%;height:134%;top:-45%;right:20%}

/* ===== Links im Privacy-Fenster =====
   Das echte Schweinchenrosa (#f8aeb1, aus dem Sprite gemessen) hat auf dem
   Kastenhintergrund nur 1.41:1 - als Text unsichtbar. Daher derselbe Farbton
   (H 352, zwischen laufendem Schwein und Hinterteil), nur so weit abgedunkelt,
   dass er traegt. Schrift bleibt Sweedolicious wie der ganze Kasten. */
:root{--color-pig:#f8aeb1;--color-pig-ink:#c43a4f}
.privacy-popup__box a{font:inherit;color:var(--color-pig-ink);text-decoration:none;transition:color 0.2s ease}
.privacy-popup__box a:hover{color:#a52738}
.privacy-popup__box a:focus-visible{outline:2px solid var(--color-pig-ink);outline-offset:3px;border-radius:4px}
@media (prefers-reduced-motion:reduce){.privacy-popup__box a{transition:none}}

/* Solange eines der Fenster offen ist, verschwindet die GANZE Ecke: Hinweis,
   Punkt und Beschriftung. body.impressum-open liegt bei beiden Fenstern an,
   das Privacy-Fenster oeffnet ja aus dem Impressum heraus - eine Regel genuegt.
   Gleiche Geste wie beim Impressum-Knopf, der ebenfalls ganz verschwindet. */
.audio-toggle__label{transition:transform 0.15s ease,opacity 0.35s ease}
.audio-corner{transition:opacity 0.35s ease}
body.impressum-open .audio-corner{opacity:0;pointer-events:none}
@media (prefers-reduced-motion:reduce){.audio-toggle__label{transition:none}}

/* ===== Privacy-Fenster auf kleinen Geraeten =====
   Kein sichtbarer Scrollbalken - auf sehr alten, schmalen Geraeten laesst
   sich das Scrollen nicht vermeiden, es soll aber nichts vom Design nehmen. */
.privacy-popup__box{scrollbar-width:none;-ms-overflow-style:none}
.privacy-popup__box::-webkit-scrollbar{width:0;height:0;display:none}

/* Auf kurzen Hochformaten (Telefone bis 780 px Hoehe) wird der Satz gestrafft,
   damit der laengere Text dort ueberhaupt nicht erst scrollen muss. Groessere
   Geraete behalten den urspruenglichen, luftigeren Satz. */
@media (orientation:portrait) and (max-height:780px){
.privacy-popup__box{line-height:1.30;padding:calc(max(20px,env(safe-area-inset-top,0px)) + 6px) clamp(18px,5vw,72px) 24px}
.privacy-popup__box p{margin:0 0 0.45em}
.privacy-popup__box p:last-child{margin-bottom:0}
}

/* Zarter Schatten an der Unterkante, solange noch Text folgt. Ersetzt den
   ausgeblendeten Scrollbalken als Hinweis; der aeussere Schlagschatten des
   Kastens bleibt dabei erhalten. */
.privacy-popup__box{transition:transform 0.45s cubic-bezier(0.22,1,0.36,1),box-shadow 0.3s ease}
.privacy-popup__box.has-more{box-shadow:0 10px 40px rgba(30,26,22,0.25),inset 0 -30px 24px -24px rgba(30,26,22,0.28)}
@media (prefers-reduced-motion:reduce){.privacy-popup__box{transition:none}}

/* Sauber scrollbar, wo es noetig ist: Scrollkette zur Seite unterbunden,
   Fokusring nur bei Tastaturbedienung, weiches Ausrollen auf Touch. */
.privacy-popup__box{overscroll-behavior:contain;scroll-behavior:smooth}
.privacy-popup__box:focus{outline:none}
.privacy-popup__box:focus-visible{outline:2px solid #5a4a42;outline-offset:-4px}
@media (prefers-reduced-motion:reduce){.privacy-popup__box{scroll-behavior:auto}}

/* ===== Bodentextur entschaerft =====
   Vorher waren es harte Rechteck-Striche aus linearen Verlaeufen, in Reihen
   gleicher Kachelbreite - scharfkantig, gleichmaessig, also genau der
   Computer-Look. Und sie standen im Ruhezustand mit voller Deckkraft da.

   Jetzt: weiche Radialflecken mit langem Auslauf statt Kanten, unregelmaessig
   ueber die Kachel verstreut statt in Reihen, und die Deckkraft haengt am
   Tempo. Im Trott ist die Textur nur noch ein Hauch, beim Rennen kommt sie
   heraus - dort, wo Bewegungsunschaerfe sie ohnehin verschleift. */
.ground__far{background-image:radial-gradient(15% 26% at 13% 26%,var(--color-ground-dark) 0 14%,transparent 74%),radial-gradient(11% 20% at 41% 64%,var(--color-ground-dark) 0 14%,transparent 74%),radial-gradient(17% 24% at 69% 34%,var(--color-ground-dark) 0 14%,transparent 74%),radial-gradient(10% 18% at 89% 72%,var(--color-ground-dark) 0 14%,transparent 74%);background-size:var(--tile,300px) 100%;background-position:0 0;opacity:calc(0.03 + var(--speed,0) * 0.15)}
.ground__near{background-image:radial-gradient(18% 30% at 11% 22%,var(--color-ground-dark) 0 16%,transparent 72%),radial-gradient(12% 22% at 33% 61%,var(--color-ground-dark) 0 16%,transparent 72%),radial-gradient(20% 27% at 57% 31%,var(--color-ground-dark) 0 16%,transparent 72%),radial-gradient(11% 20% at 75% 74%,var(--color-ground-dark) 0 16%,transparent 72%),radial-gradient(16% 25% at 92% 45%,var(--color-ground-dark) 0 16%,transparent 72%);background-size:var(--tile,220px) 100%;background-position:0 0;opacity:calc(0.05 + var(--speed,0) * 0.28)}

/* Bodentextur aus. Der Staub am Huf und der vorbeirauschende Vordergrund
   tragen den Tempoeindruck allein - die Flecken im Gras waren zu viel.
   Diese eine Zeile entfernen holt sie zurueck; Markup, Verlaeufe und
   Parallax-Anbindung bleiben unangetastet. */
.ground__far,.ground__near{display:none}

/* ===== SomaFM-Hinweis: immer sichtbar, nie klickbar =====
   Er nennt die Quelle, BEVOR gefuettert wird - und er ist bewusst kein Link
   mehr. Auf der Seite soll es genau eine Sache zu klicken geben. Der Dank
   und die Verweise auf SomaFM stehen weiterhin im Privacy-Fenster. */
.audio-credit{opacity:0.45;pointer-events:none;cursor:default;user-select:none;-webkit-user-select:none}
body.audio-on .audio-credit{opacity:0.62;pointer-events:none}
body.impressum-open .audio-credit{opacity:0}
.audio-credit{visibility:visible;transition:opacity 0.5s ease}

/* ===== Variante D: der Hinweis wechselt mit dem Zustand =====
   Vor dem Fuettern steht da, was das Fuettern ausloest. Sobald es laeuft,
   ist die Erklaerung erledigt und es bleibt der reine Dank stehen.

   Beide Fassungen liegen uebereinander, die laengere haelt den Platz. Sonst
   wuerde der Block beim Wechsel schmaler und die linksbuendige Beschriftung
   seitlich wegspringen. So bleibt Breite UND Hoehe ueber alle Zustaende
   konstant - nichts bewegt sich ausser der Schrift selbst. */
.audio-credit{display:block;position:relative}
.audio-credit__idle,.audio-credit__live{display:block;white-space:nowrap}
.audio-credit__idle{transition:opacity 0.4s ease,visibility 0s}
.audio-credit__live{position:absolute;left:0;top:0;opacity:0;visibility:hidden;transition:opacity 0.4s ease,visibility 0s linear 0.4s}
body.audio-on .audio-credit__idle{opacity:0;visibility:hidden;transition:opacity 0.4s ease,visibility 0s linear 0.4s}
body.audio-on .audio-credit__live{opacity:1;visibility:visible;transition:opacity 0.4s ease,visibility 0s}
@media (prefers-reduced-motion:reduce){.audio-credit__idle,.audio-credit__live{transition:none}}

/* Vor dem Fuettern nur noch die eine dezente Zeile. Die Kredite stehen im
   laufenden Betrieb und im Privacy-Fenster - hier reicht der Hinweis.
   Der Kasten haelt weiterhin die Hoehe von zwei Zeilen, damit beim Wechsel
   nichts springt; die eine Zeile sitzt dabei unten, direkt ueber dem Knopf. */
.audio-credit{display:flex;flex-direction:column;justify-content:flex-end;align-items:flex-start;min-height:2.2em}
.audio-credit__idle{position:static}

/* ===== Hinweis im Himmel =====
   Er haengt bewusst NICHT an der Ecke unten rechts - dort waere er das
   breiteste Element und wuerde den Knopf nach links druecken. Unten in der
   Wiese stand er frueher; in eingebetteten Browsern (Instagram & Co.) bleibt
   von der Buehne aber so wenig Hoehe uebrig, dass Fuetterknopf, Schweinehintern
   und Notiz sich dort gegenseitig auf die Fuesse treten. Oben im Himmel
   passiert nie etwas, also steht er jetzt immer dort - mittig, mit dem
   Infozeichen in derselben Zeile. Sobald gefuettert wird, ist er erledigt und
   blendet aus. Ausserhalb von .world, damit ihn das Kameraruckeln nicht
   erfasst. */
.scene-note{position:absolute;left:50%;transform:translateX(-50%);top:calc(max(14px,2.5vh) + env(safe-area-inset-top,0px));z-index:7;width:min(88vw,30em);margin:0;text-align:center;font-family:"Sweedolicious",system-ui,sans-serif;font-size:clamp(10px,2.2vmin,14px);line-height:1.3;color:#2a2320;opacity:0.5;pointer-events:none;user-select:none;-webkit-user-select:none;transition:opacity 0.5s ease}
body.audio-on .scene-note,body.impressum-open .scene-note{opacity:0}
/* Im flachen Querformat bleibt die Position, nur der Satz wird schmaler und
   kleiner gesetzt, damit er dem Schwein nicht in den Himmel waechst. */
@media (orientation:landscape) and (max-height:480px){.scene-note{width:min(80vw,34em);font-size:clamp(10px,2.8vh,12px)}}
@media (prefers-reduced-motion:reduce){.scene-note{transition:none}}

/* Die Ecke zeigt den Dank wieder nur im Betrieb */
.audio-credit{display:block;min-height:0;white-space:nowrap;opacity:0;visibility:hidden;transition:opacity 0.5s ease,visibility 0s linear 0.5s}
body.audio-on .audio-credit{opacity:0.62;visibility:visible;transition:opacity 0.5s ease,visibility 0s}

/* ===== Infozeichen =====
   Der Kreis ist bewusst leicht unrund, Punkt und Stamm sind gezeichnete
   Formen - dieselbe Handschrift wie die Illustrationen, kein Bibliotheks-Icon.
   Die Deckkraft der Zeile kommt jetzt aus der FARBE, nicht aus opacity: sonst
   koennte das Zeichen beim Ueberfahren nicht heller werden als sein Elternteil. */
.scene-note{opacity:1;color:rgba(42,35,32,0.52)}
body.audio-on .scene-note,body.impressum-open .scene-note{opacity:0;visibility:hidden;transition:opacity 0.5s ease,visibility 0s linear 0.5s}
.scene-note{transition:opacity 0.5s ease,visibility 0s}

/* Sichtbar bleiben rund 15 px - anfassbar sind 45. Ein 15-px-Ziel trifft auf
   dem Telefon niemand zuverlaessig; die negativen Aussenabstaende halten die
   Zeile trotzdem auf ihrer Hoehe. */
.scene-info{display:inline-flex;align-items:center;justify-content:center;vertical-align:-0.2em;margin:-15px -15px -15px 0.45em;padding:15px;border:0;background:transparent;color:inherit;cursor:pointer;pointer-events:auto;-webkit-tap-highlight-color:transparent;transition:color 0.2s ease}
.scene-info svg{width:1.1em;height:1.1em;display:block;overflow:visible}
.scene-info__ring{fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round}
.scene-info__dot{fill:currentColor}
.scene-info__stem{fill:none;stroke:currentColor;stroke-width:2.1;stroke-linecap:round}
.scene-info:hover{color:rgba(42,35,32,0.88)}
.scene-info:focus-visible{outline:2px solid #5a4a42;outline-offset:-11px;border-radius:50%}
@media (prefers-reduced-motion:reduce){.scene-info{transition:none}}

/* ===== Das kleine Fenster =====
   Bewusst mittig und kompakt, damit es sich klar vom grossen Privacy-Fenster
   unterscheidet, das von oben hereinfaehrt. */
.info-popup{position:absolute;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:max(16px,env(safe-area-inset-top,0px)) max(16px,env(safe-area-inset-right,0px)) max(16px,env(safe-area-inset-bottom,0px)) max(16px,env(safe-area-inset-left,0px));pointer-events:none;visibility:hidden;cursor:zoom-out;transition:visibility 0s linear 0.35s}
body.info-open .info-popup{pointer-events:auto;visibility:visible;transition:visibility 0s}
.info-popup__box{width:min(88vw,25em);max-height:86dvh;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none;-ms-overflow-style:none;background:rgba(224,226,229,0.94);border-radius:16px;padding:clamp(20px,4vw,28px) clamp(20px,5vw,32px);box-shadow:0 12px 44px rgba(30,26,22,0.28);font-family:"Sweedolicious",system-ui,sans-serif;color:#2a2320;font-size:clamp(14px,2.4vmin,17px);line-height:1.42;text-align:left;opacity:0;transform:scale(0.93) translateY(6px);transition:opacity 0.3s ease,transform 0.35s cubic-bezier(0.22,1,0.36,1)}
.info-popup__box::-webkit-scrollbar{width:0;height:0;display:none}
body.info-open .info-popup__box{opacity:1;transform:none}
.info-popup__box p{margin:0 0 0.5em}
.info-popup__box p:last-child{margin-bottom:0}
@media (prefers-reduced-motion:reduce){.info-popup__box{transition:opacity 0.2s ease;transform:none}}

/* Kasten transparent - der Text steht frei in der Szene, wie beim Impressum.
   Statt Hintergrund traegt ihn ein weicher heller Schein, damit er auch ueber
   dem Schwein und den Wolken lesbar bleibt. */
.info-popup__box{background:transparent;box-shadow:none;text-shadow:0 1px 3px rgba(255,255,255,0.85),0 0 12px rgba(255,255,255,0.65)}

/* Das Zeichen sass 15 px zu weit rechts: der Innenabstand des Tippfelds
   schob es optisch weg. Der negative Aussenabstand hebt ihn jetzt auf, das
   45-px-Ziel bleibt erhalten. */
.scene-info{margin:-15px -15px -15px calc(0.3em - 15px)}

/* ===== Wolken: sichtbar in Bewegung =====
   Gemessen zogen sie mit 3,6-5,1 px/s ueber ein Telefon - langsamer als der
   Sekundenzeiger einer Uhr am Rand. Zusammen mit der Deckkraft, die ich fuer
   die Lufttiefe abgesenkt hatte, lasen sich die hinteren als stehender Dunst.
   Jetzt rund die Haelfte schneller, und die blassesten wieder etwas kraeftiger. */
.cloud--1{animation-duration:70s}
.cloud--2{animation-duration:80s;opacity:0.76}
.cloud--3{animation-duration:64s}
.cloud--4{animation-duration:90s;opacity:0.68}
.cloud--5{animation-duration:74s}

/* ===== Windstreifen: weich statt grob =====
   Vorher harte 2-px-Balken mit scharfen Enden - dasselbe Problem wie bei der
   Bodentextur. Jetzt laufen die Streifen an beiden Enden aus, sind feiner,
   zahlreicher und staerker weichgezeichnet. */
.wind__band{background-image:linear-gradient(90deg,rgba(255,255,255,0) 5%,rgba(255,255,255,0.55) 11%,rgba(255,255,255,0.55) 17%,rgba(255,255,255,0) 24%,transparent 24%),linear-gradient(90deg,rgba(255,255,255,0) 34%,rgba(255,255,255,0.45) 40%,rgba(255,255,255,0.45) 47%,rgba(255,255,255,0) 55%,transparent 55%),linear-gradient(90deg,rgba(255,255,255,0) 62%,rgba(255,255,255,0.40) 67%,rgba(255,255,255,0.40) 72%,rgba(255,255,255,0) 79%,transparent 79%),linear-gradient(90deg,rgba(255,255,255,0) 18%,rgba(255,255,255,0.32) 24%,rgba(255,255,255,0.32) 29%,rgba(255,255,255,0) 36%,transparent 36%),linear-gradient(90deg,rgba(255,255,255,0) 74%,rgba(255,255,255,0.30) 79%,rgba(255,255,255,0.30) 84%,rgba(255,255,255,0) 91%,transparent 91%);background-size:var(--tile,460px) 2px;background-position:0 19%,0 37%,0 55%,0 71%,0 86%;filter:blur(1.6px);opacity:clamp(0,calc((var(--speed,0) - 0.36) * 0.95),0.40)}

/* Der Dank verlinkt wieder auf SomaFM - anfassbar aber ausschliesslich,
   solange die Musik laeuft. Im Ruhezustand ist er unsichtbar und damit auch
   nicht per Tabulator erreichbar; dort bleibt der Fuetterknopf das einzige
   Ziel. Beim Ueberfahren wird er schwarz, wie die Links im Impressum. */
.audio-credit{color:#2a2320;text-decoration:none;transition:opacity 0.5s ease,color 0.2s ease,visibility 0s linear 0.5s}
body.audio-on .audio-credit{pointer-events:auto;cursor:pointer;transition:opacity 0.5s ease,color 0.2s ease,visibility 0s}
body.audio-on .audio-credit:hover{color:#000;opacity:0.9}
.audio-credit:focus-visible{outline:2px solid #5a4a42;outline-offset:3px;border-radius:8px;opacity:0.9}
@media (prefers-reduced-motion:reduce){.audio-credit{transition:none}}

/* Die Welt ragt oben ueber die Buehne hinaus. Das Kamera-Ruckeln schiebt sie
   um bis zu 2,4 px nach unten - ohne diesen Ueberstand entstuende dabei oben
   ein Spalt, durch den der schwarze Buehnenhintergrund blitzt. .stage hat
   overflow:hidden, der Ueberstand ist also nie zu sehen. */
.world{top:-10px}
