/** * MULTIVERSUM Scroll Story V5 — Welt-Collage / Mindmap-Hover, 6 Kapitel. */ (function () { "use strict"; var WORLD_KEYS = ["multiversum", "nexora", "professional", "freiraum"]; var WORLD_INDEX = { multiversum: 0, nexora: 1, professional: 2, freiraum: 3 }; var WORLD_SHELL_KEY = { multiversum: "general", nexora: "nexora", professional: "vertex", freiraum: "freiraum" }; var BASE = "assets/multiversum-v4/"; var V = "?v=20260629mv-v4live"; var ASSETS = { bg: { multiverse: BASE + "backgrounds/webp/background_deep_space_neutral.webp", nexora: BASE + "backgrounds/webp/background_v4_neutral_blue_plum.webp", professional: BASE + "backgrounds/webp/background_v4_professional_silver.webp", freiraum: BASE + "backgrounds/webp/background_v4_freiraum_plum_magenta.webp", overview: BASE + "backgrounds/webp/background_multiverse_three_worlds.webp", }, orbs: { multiversum: "assets/multiversum-parallax-v4/orbs/Multiversum.png", nexora: "assets/multiversum-parallax-v4/orbs/Nexora.png", professional: "assets/multiversum-parallax-v4/orbs/Professional_new_new.png", freiraum: "assets/multiversum-parallax-v4/orbs/Freiraum.png", }, accents: { nexora: [ BASE + "cards/webp/cards_nexora_floating_data_panels.webp", BASE + "particles/webp/particles_nexora_network.webp", BASE + "trails/webp/trail_nexora_blue_data_flow.webp", ], professional: [ BASE + "cards/webp/cards_professional_precision_panels.webp", BASE + "particles/webp/particles_professional_silver.webp", BASE + "trails/webp/trail_professional_silver_precision.webp", ], freiraum: [ BASE + "cards/webp/cards_freiraum_creative_panels.webp", BASE + "particles/webp/particles_freiraum_magenta.webp", BASE + "trails/webp/trail_freiraum_magenta_orange_flow.webp", ], }, glows: { nexora: BASE + "overlays/webp/overlay_nexora_blue_glow.webp", professional: BASE + "overlays/webp/overlay_professional_silver_glow.webp", freiraum: BASE + "overlays/webp/overlay_freiraum_magenta_orange_glow.webp", merge: BASE + "overlays/webp/overlay_multiverse_connection_glow.webp", }, transitionTrail: BASE + "trails/webp/trail_multiverse_connection.webp", deco: { nexora: { orbit: BASE + "orbits/webp/orbit_nexora_blue_double.webp", orbit2: BASE + "orbits/webp/orbit_nexora_blue_double.webp", particles: BASE + "particles/webp/particles_nexora_network.webp", light: BASE + "overlays/webp/overlay_nexora_blue_glow.webp", line: BASE + "trails/webp/trail_nexora_blue_data_flow.webp", }, professional: { orbit: BASE + "orbits/webp/orbit_professional_silver_double.webp", orbit2: BASE + "orbits/webp/orbit_professional_silver_double.webp", particles: BASE + "particles/webp/particles_professional_silver.webp", light: BASE + "overlays/webp/overlay_professional_silver_glow.webp", line: BASE + "trails/webp/trail_professional_silver_precision.webp", }, freiraum: { orbit: BASE + "orbits/webp/orbit_freiraum_magenta_orange.webp", orbit2: BASE + "orbits/webp/orbit_freiraum_magenta_orange.webp", particles: BASE + "particles/webp/particles_freiraum_magenta.webp", light: BASE + "overlays/webp/overlay_freiraum_magenta_orange_glow.webp", line: BASE + "trails/webp/trail_freiraum_magenta_orange_flow.webp", }, multiverse: { orbit: BASE + "orbits/webp/orbit_nexora_blue_double.webp", orbit2: BASE + "orbits/webp/orbit_freiraum_magenta_orange.webp", particles: BASE + "particles/webp/particles_multiverse_mix.webp", light: BASE + "overlays/webp/overlay_multiverse_connection_glow.webp", line: BASE + "trails/webp/trail_multiverse_connection.webp", }, multiversum: { orbit: BASE + "orbits/webp/orbit_nexora_blue_double.webp", orbit2: BASE + "orbits/webp/orbit_freiraum_magenta_orange.webp", particles: BASE + "particles/webp/particles_multiverse_mix.webp", light: BASE + "overlays/webp/overlay_multiverse_connection_glow.webp", line: BASE + "trails/webp/trail_multiverse_connection.webp", }, }, scrollCue: BASE + "particles/webp/particles_multiverse_mix.webp", }; var PORTFOLIO_CARDS = [ { go: "projects", label: "Projekte", sub: "Arbeit aus allen Welten" }, { go: "leistungen", label: "Leistungen", sub: "Was ich anbiete" }, { go: "about", label: "Über mich", sub: "Persönlichkeit & Kompetenz" }, { go: "contact", label: "Kontakt", sub: "Nächster Schritt" }, { go: "offerte", label: "Offerte", sub: "Leistungen & Preisanfrage" }, ]; var rafId = 0; var frozen = false; var scrollRoot = null; var heroEl = null; var dom = {}; var config = null; var mobileLayout = false; var tabletLayout = false; var reducedMotion = false; var animProgress = 0; var mouseParallax = { x: 0, y: 0 }; var portfolioHoldScrolls = 0; var portfolioHoldUnlocked = false; var PORTFOLIO_HOLD_SCROLLS = 3; var onScrollHandler = null; var bootPaintDone = false; function asset(path) { if (!path || typeof path !== "string") return path; if (path.indexOf("?") !== -1) return path; return path + V; } function lerp(a, b, t) { return a + (b - a) * t; } function clamp(v, min, max) { return Math.min(max, Math.max(min, v)); } function easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; } function easeOutExpo(t) { return t >= 1 ? 1 : 1 - Math.pow(2, -10 * t); } function easeOutQuart(t) { return 1 - Math.pow(1 - t, 4); } function deepLerp(a, b, t) { if (a == null) return b; if (b == null) return a; if (typeof a === "number" && typeof b === "number") return lerp(a, b, t); if (typeof a === "object" && !Array.isArray(a)) { var out = {}; var keys = Object.keys(a); for (var i = 0; i < keys.length; i++) { var k = keys[i]; out[k] = deepLerp(a[k], b[k], t); } return out; } return t < 0.5 ? a : b; } function segmentBlendT(local, holdPortion) { holdPortion = holdPortion !== undefined ? holdPortion : config.segmentHold || 0.72; var trans = (1 - holdPortion) / 2; if (trans <= 0.001) return clamp(local, 0, 1); if (local >= 1 - trans) { return easeInOutCubic((local - (1 - trans)) / trans); } return 0; } function getState(p) { var bounds = config.bounds; var kf = config.keyframes; var hold = config.segmentHold || 0.72; for (var i = 0; i < bounds.length - 1; i++) { if (p <= bounds[i + 1]) { var local = segmentProgress(p, bounds[i], bounds[i + 1]); var t = segmentBlendT(local, hold); var state = deepLerp(kf[i], kf[Math.min(i + 1, kf.length - 1)], t); state.backgrounds = exclusiveBackgroundWeights(state.backgrounds); return state; } } var finalState = kf[kf.length - 1]; finalState.backgrounds = exclusiveBackgroundWeights(finalState.backgrounds); return finalState; } function exclusiveBackgroundWeights(bgs) { var keys = ["multiverse", "nexora", "professional", "freiraum"]; var ranked = keys .map(function (k) { return { k: k, v: (bgs && bgs[k]) || 0 }; }) .sort(function (a, b) { return b.v - a.v; }); var out = { multiverse: 0, nexora: 0, professional: 0, freiraum: 0 }; if (!ranked[0] || ranked[0].v < 0.01) return out; var top = ranked[0]; var second = ranked[1]; if (!second || second.v < 0.04) { out[top.k] = top.v; return out; } var sum = top.v + second.v; if (sum < 0.01) return out; out[top.k] = top.v / sum; out[second.k] = second.v / sum; return out; } function bookendBackgroundForLayout(layoutMode) { if (layoutMode === "intro" || layoutMode === "finale" || layoutMode === "portfolio") { return { multiverse: 1, nexora: 0, professional: 0, freiraum: 0 }; } return null; } function slideKind(index) { var slide = config.slides[index]; return slide ? slide.sceneKind || "intro" : "intro"; } function easeInOutSine(t) { return -(Math.cos(Math.PI * t) - 1) / 2; } function sceneEnvelope(local, slideIndex) { var timing = (config && config.timing) || {}; var enter = timing.enter || 0.12; var exit = timing.exit || 0.1; var enterT = easeInOutSine(clamp(local / enter, 0, 1)); var exitT = local > 1 - exit ? easeInOutSine(clamp((local - (1 - exit)) / exit, 0, 1)) : 0; var hold = enterT * (1 - exitT); if (slideIndex === 0 && local < 0.04) { hold = easeOutQuart(clamp(local / 0.04, 0, 1)); } return hold; } function slideVisibility(p, start, end, fade) { if (p < start || p > end) return { opacity: 0, y: 28, active: false }; var seg = Math.max(end - start, 0.001); var timing = (config && config.timing) || {}; var f = seg * (timing.fade || fade || 0.08); var opacity = 1; var y = 0; if (p < start + f) { var t = easeInOutSine(clamp((p - start) / f, 0, 1)); opacity = t; y = lerp(18, 0, t); } else if (p > end - f) { var t2 = easeInOutSine(clamp((p - (end - f)) / f, 0, 1)); opacity = 1 - t2; y = lerp(0, -18, t2); } return { opacity: opacity, y: y, active: opacity > 0.06 }; } function chapterHash(goChapter, targetHash) { var hashMap = { home: "#home", projects: "#projekte", leistungen: "#leistungen", about: "#ueber-mich", contact: "#kontakt", offerte: "#offerte", }; if (targetHash) { var raw = String(targetHash).trim(); if (!raw) return hashMap[goChapter] || "#home"; return raw.charAt(0) === "#" ? raw : "#" + raw; } return hashMap[goChapter] || "#home"; } function worldHref(world, goChapter, targetHash) { var pages = (window.MVWorldCollage && window.MVWorldCollage.pages) || {}; var page = pages[world] || ""; if (!page) return "#"; var hash = chapterHash(goChapter, targetHash); return hash === "#home" ? page : page + hash; } function requestWorldSwitch(worldKey, targetHash, goChapter) { if (!worldKey) return; var shellKey = WORLD_SHELL_KEY[worldKey]; if (!shellKey) return; var go = goChapter || "home"; var hash = chapterHash(go, targetHash); var href = worldHref(worldKey, go, targetHash); try { if (window.parent && window.parent !== window) { window.parent.postMessage( { type: "alex:switch-world", world: worldKey, targetHash: hash === "#home" ? "" : hash, go: go, href: href, }, "*" ); return; } } catch (e) {} if (href && href !== "#") { window.location.assign(href); return; } var btn = document.querySelector('.mv4-worlds [data-world-key="' + shellKey + '"]'); if (btn) { btn.click(); } else { switchWorldIndex(worldKey); } if (hash !== "#home" || go !== "home") { setTimeout(function () { scrollSectionInFrame(hash, go); }, 480); } } function switchWorldIndex(worldKey) { var idx = WORLD_INDEX[worldKey]; if (idx === undefined) return; if (typeof window.switchToWorldIndex === "function") { window.switchToWorldIndex(idx); return; } try { if (window.parent && window.parent !== window && typeof window.parent.switchToWorldIndex === "function") { window.parent.switchToWorldIndex(idx); } } catch (e2) {} } function scrollSectionInFrame(targetHash, goChapter) { if (window.WeltenSiteIA && typeof window.WeltenSiteIA.scrollToSection === "function") { window.WeltenSiteIA.scrollToSection(targetHash, goChapter); return; } if (goChapter && window.WeltenSiteIA && typeof window.WeltenSiteIA.navigateToChapter === "function") { window.WeltenSiteIA.navigateToChapter(goChapter); } var hash = targetHash || ""; if (!hash) return; setTimeout(function () { var id = hash.replace(/^#/, ""); var el = document.getElementById(id) || document.getElementById("slide-" + id) || document.querySelector('[data-slide="' + id + '"]'); if (el && el.scrollIntoView) { el.scrollIntoView({ behavior: "smooth", block: "start" }); } else if (window.WeltenSiteIA && typeof window.WeltenSiteIA.navigateToChapter === "function") { window.WeltenSiteIA.navigateToChapter(id); } }, 160); } function switchWorld(key, targetHash, goChapter) { requestWorldSwitch(key, targetHash, goChapter); } function isEmbeddedFrame() { try { return !!(window.parent && window.parent !== window); } catch (e) { return false; } } function navigateWorldLink(world, targetHash, goChapter, href) { var go = goChapter || "home"; var hash = chapterHash(go, targetHash); if (isEmbeddedFrame()) { requestWorldSwitch(world, hash, go); return; } var dest = href && href !== "#" ? href : worldHref(world, go, targetHash); if (dest && dest !== "#") { window.location.assign(dest); return; } requestWorldSwitch(world, hash, go); } function bindPressFeedback(root) { root.querySelectorAll(".world-core, .world-card").forEach(function (el) { function clearPressed() { el.classList.remove("is-pressed"); } el.addEventListener("mousedown", function () { el.classList.add("is-pressed"); }); el.addEventListener("mouseup", clearPressed); el.addEventListener("mouseleave", clearPressed); el.addEventListener( "touchstart", function () { el.classList.add("is-pressed"); }, { passive: true } ); el.addEventListener("touchend", clearPressed); el.addEventListener("touchcancel", clearPressed); }); } function bindWorldCoreLinks(root) { root.querySelectorAll("a.world-core[data-world]").forEach(function (core) { core.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); navigateWorldLink(core.getAttribute("data-world"), "", "home", core.getAttribute("href")); }); }); } function bindWorldNavigation(root) { root.addEventListener( "click", function (e) { var core = e.target.closest("a.world-core[data-world]"); if (core) { e.preventDefault(); e.stopPropagation(); navigateWorldLink(core.getAttribute("data-world"), "", "home", core.getAttribute("href")); return; } var card = e.target.closest("a.world-card[data-world]"); if (!card) return; e.preventDefault(); e.stopPropagation(); navigateWorldLink( card.getAttribute("data-world"), card.getAttribute("data-target"), card.getAttribute("data-go"), card.getAttribute("href") ); }, true ); bindPressFeedback(root); bindWorldCoreLinks(root); } function bindGoButtons(root, goChapter) { root.querySelectorAll(".mv-scroll-portfolio [data-go]").forEach(function (btn) { btn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); if (typeof goChapter === "function") goChapter(btn.getAttribute("data-go")); }); }); root.querySelectorAll("[data-world-enter]").forEach(function (btn) { btn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); var world = btn.getAttribute("data-world-enter"); navigateWorldLink(world, "", "home", worldPageHref(world)); }); }); bindWorldNavigation(root); } function segmentProgress(p, start, end) { if (end <= start) return 0; return clamp((p - start) / (end - start), 0, 1); } function isWorldSlide(index) { return slideKind(index) === "world"; } function worldChapterIndex(world) { var map = { multiversum: 0, nexora: 1, professional: 2, freiraum: 3 }; return map[world]; } function collageFocusForScene(sceneIndex) { var kind = slideKind(sceneIndex); if (kind === "world") { var slide = config.slides[sceneIndex]; return slide && slide.worldType ? slide.worldType : "all"; } if (kind === "reveal") return "reveal"; if (kind === "merge") return "all"; return "none"; } function revealedWorldsForScene(sceneIndex) { var slide = config.slides[sceneIndex]; if (!slide) return []; if (slide.revealedWorlds && slide.revealedWorlds.length) return slide.revealedWorlds; if (slide.sceneKind === "world" && slide.worldType) return [slide.worldType]; if (slide.sceneKind === "merge") return WORLD_KEYS; return []; } function slideVisibilityRange(slideIndex) { var slide = config.slides[slideIndex]; var ch = config.chapters || {}; if (!slide) return [0, 0]; if (slide.visibility) return slide.visibility; var kind = slide.sceneKind || "intro"; if (kind === "intro") return [0, (ch.introToMultiversum && ch.introToMultiversum[1]) || (ch.introToNexora && ch.introToNexora[1]) || 0.18]; if (kind === "world" && slide.worldType === "multiversum") { return [ch.multiversumFocus[0], ch.multiversumToNexora[1]]; } if (kind === "world" && slide.worldType === "nexora") { return [ch.nexoraFocus[0], ch.nexoraToProfessional[1]]; } if (kind === "world" && slide.worldType === "professional") { return [ch.professionalFocus[0], ch.professionalToFreiraum[1]]; } if (kind === "world" && slide.worldType === "freiraum") { return [ch.freiraumFocus[0], ch.freiraumFocus[1]]; } if (kind === "merge") return [0, 0]; if (kind === "finale") return ch.finaleCopyOnly || [0.86, 0.91]; return [config.bounds[slideIndex], config.bounds[slideIndex + 1]]; } function chapterRange(name, fallback) { var ch = (config && config.chapters) || {}; return ch[name] || fallback; } function freiraumChapterEnd() { return chapterRange("freiraumFocus", [0.7, 0.8])[1]; } function introChapterEnd() { var ch = (config && config.chapters) || {}; return (ch.introToMultiversum && ch.introToMultiversum[1]) || (ch.intro && ch.intro[1]) || 0.18; } function isPostWorldPhase(phaseP, layoutMode) { if (layoutMode === "finale" || layoutMode === "portfolio") return true; return phaseP >= freiraumChapterEnd(); } function worldsContentVisible(phaseP, layoutMode) { return !isPostWorldPhase(phaseP, layoutMode) && layoutMode === "split-world"; } function layoutModeForProgress(p, activeSlide) { var finCopy = chapterRange("finaleCopyOnly", [0.86, 0.91]); var port = chapterRange("portfolioButtons", [0.93, 0.97]); if (p >= port[0]) return "portfolio"; if (p >= finCopy[0]) return "finale"; if (p >= freiraumChapterEnd()) return "finale"; return layoutModeForScene(activeSlide); } function slideIndexForWorld(worldType) { if (!config || !config.slides) return -1; for (var i = 0; i < config.slides.length; i++) { var slide = config.slides[i]; if (slide && slide.sceneKind === "world" && slide.worldType === worldType) return i; } return -1; } function slideIndexForProgress(p) { if (!config || !config.slides) return 0; var finIdx = -1; for (var i = 0; i < config.slides.length; i++) { if (slideKind(i) === "finale") finIdx = i; var range = slideVisibilityRange(i); if (range[1] <= range[0]) continue; if (p >= range[0] && p < range[1]) return i; } if (p >= freiraumChapterEnd() && finIdx >= 0) return finIdx; return 0; } function boundSegmentForProgress(p) { var bounds = config.bounds || []; if (bounds.length < 2) return 0; for (var s = 0; s < bounds.length - 1; s++) { if (p >= bounds[s] && p < bounds[s + 1]) return s; } return bounds.length - 2; } function worldBuildProgress(p, worldType) { if (!config || !config.chapters) return 0; var key = worldType === "multiversum" ? "multiversumFocus" : worldType === "nexora" ? "nexoraFocus" : worldType === "professional" ? "professionalFocus" : worldType === "freiraum" ? "freiraumFocus" : ""; var range = key ? config.chapters[key] : null; if (!range) return 0; return sceneEnvelope(segmentProgress(p, range[0], range[1]), 0); } function worldScenePhases(local, slideIndex) { var kind = slideKind(slideIndex); if (kind === "world") { var chapter = worldChapterPhases(local); return { orb: chapter.worldOpacity, copy: chapter.textOpacity, cards: chapter.worldIn, hint: 0, trail: 0, env: chapter.worldOpacity, chapter: chapter, }; } var env = sceneEnvelope(local, slideIndex); if (kind === "finale") { var copyOut = local > 0.55 ? easeInOutSine(clamp((local - 0.55) / 0.2, 0, 1)) : 0; return { orb: env, copy: env * (1 - copyOut), cards: env, hint: 0, trail: 0, env: env, }; } return { orb: env, copy: env, cards: env, hint: 0, trail: 0, env: env, }; } function layoutModeForScene(sceneIndex) { var kind = slideKind(sceneIndex); if (kind === "intro") return "intro"; if (kind === "reveal" || kind === "merge") return "overview"; if (kind === "world") return "split-world"; if (kind === "finale") return "finale"; return "intro"; } function worldStageOpacity(activeSlide, p, phases, slideLocal) { if (p >= freiraumChapterEnd()) return 0; var kind = slideKind(activeSlide); if (kind === "intro") return lerp(0.9, 1, easeInOutSine(slideLocal || 0)); if (kind === "finale") return 0; if (kind === "world") return phases.orb || 0; if (kind === "merge") return 0; return 0; } function cardOrbitForZone(kind, world, hasCards, isFocus, isRevealFocus) { if (kind === "world" && isFocus) return "38cqmin"; if (kind === "reveal" && isRevealFocus) return "32cqmin"; if (kind === "reveal" && hasCards) return "28cqmin"; if (kind === "merge" && hasCards) return "36cqmin"; return "28cqmin"; } function topicVisibilityForWorld() { return { opacity: 0, build: 0 }; } function layerShift(p, factor, par) { return -p * factor * 28 * par; } var WORLD_CHAPTER = { worldEnterEnd: 0.2, textStart: 0.22, holdEnd: 0.87, fadeEnd: 1, }; function worldChapterPhases(focusLocal) { var worldIn = easeInOutSine(clamp(focusLocal / WORLD_CHAPTER.worldEnterEnd, 0, 1)); var fadeOut = focusLocal > WORLD_CHAPTER.holdEnd ? easeInOutSine(clamp((focusLocal - WORLD_CHAPTER.holdEnd) / (WORLD_CHAPTER.fadeEnd - WORLD_CHAPTER.holdEnd), 0, 1)) : 0; var worldOpacity = worldIn * (1 - fadeOut); var showText = focusLocal >= WORLD_CHAPTER.textStart && fadeOut < 0.98; var textLocal = 0; if (showText) { textLocal = focusLocal >= WORLD_CHAPTER.holdEnd ? 1 : clamp((focusLocal - WORLD_CHAPTER.textStart) / (WORLD_CHAPTER.holdEnd - WORLD_CHAPTER.textStart), 0, 1); } return { worldIn: worldIn, worldOpacity: worldOpacity, textOpacity: showText ? worldOpacity : 0, textLocal: textLocal, showText: showText, hold: 1 - fadeOut, fadeOut: fadeOut, }; } function worldChapterHold(slideLocal) { return worldChapterPhases(slideLocal).hold; } function applyWorldExplainTexts(copyEl, textLocal, copyOpacity) { if (!copyEl) return; var eyebrowEl = copyEl.querySelector(".mv-scroll-slide__eyebrow"); var leadEl = copyEl.querySelector(".mv-scroll-slide__lead"); var whatEl = copyEl.querySelector(".mv-scroll-slide__what"); var purposeEl = copyEl.querySelector(".mv-scroll-slide__purpose"); var diffEl = copyEl.querySelector(".mv-scroll-slide__diff"); var enterBtn = copyEl.querySelector(".mv-scroll-slide__enter"); var t = textLocal; function step(start, span) { return easeInOutSine(clamp((t - start) / span, 0, 1)) * copyOpacity; } if (eyebrowEl) { var eyebrowOp = step(0, 0.1); eyebrowEl.style.opacity = String(eyebrowOp); } if (leadEl) { leadEl.style.opacity = String(step(0.04, 0.1)); leadEl.style.transform = "translateY(" + lerp(12, 0, step(0.04, 0.1) / Math.max(copyOpacity, 0.001)) + "px)"; } if (whatEl) { whatEl.style.opacity = String(step(0.16, 0.12)); whatEl.style.transform = "translateY(" + lerp(14, 0, step(0.16, 0.12) / Math.max(copyOpacity, 0.001)) + "px)"; } if (purposeEl) { purposeEl.style.opacity = String(step(0.32, 0.12)); purposeEl.style.transform = "translateY(" + lerp(14, 0, step(0.32, 0.12) / Math.max(copyOpacity, 0.001)) + "px)"; } if (diffEl) { diffEl.style.opacity = String(step(0.48, 0.12)); diffEl.style.transform = "translateY(" + lerp(14, 0, step(0.48, 0.12) / Math.max(copyOpacity, 0.001)) + "px)"; } if (enterBtn) { enterBtn.style.opacity = String(step(0.62, 0.12)); } } function resetWorldExplainTexts(copyEl) { if (!copyEl) return; copyEl.querySelectorAll(".mv-scroll-slide__eyebrow, .mv-scroll-slide__lead, .mv-scroll-slide__what, .mv-scroll-slide__purpose, .mv-scroll-slide__diff, .mv-scroll-slide__enter").forEach(function (el) { el.style.opacity = ""; el.style.transform = ""; }); } function topicCardsMarkup(world, items) { var cards = items .map(function (item, i) { return ( '
0' + (i + 1) + "" + '

' + item.title + "

" + item.body + "

" ); }) .join(""); return ( '
' + '
' + cards + "
" ); } function atmosphereMarkup() { return ( '
' + '
' + '' + 'SYS · NEXORA
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' ); } function decoSetMarkup(theme) { var d = ASSETS.deco[theme]; return ( '
' + '' + '' + '' + '' + "
" ); } function worldAccentMarkup() { return ""; } function getCollageCards(world) { var cfg = window.MVWorldCollage; if (!cfg) return []; var all = cfg.dedupeCards ? cfg.dedupeCards(cfg.worldCards[world] || cfg.cards[world] || []) : cfg.worldCards[world] || cfg.cards[world] || []; var limit = cfg.getCardLimit ? cfg.getCardLimit(window.innerWidth) : 8; return all.slice(0, limit); } function worldPageHref(world) { var pages = (window.MVWorldCollage && window.MVWorldCollage.pages) || {}; return pages[world] || "#"; } function cardOrbitAngle(index, total) { return -90 + index * (360 / Math.max(total, 1)); } function worldZoneMarkup(world, label) { var cards = getCollageCards(world); var cardsHtml = cards .map(function (c, i) { var angle = cardOrbitAngle(i, cards.length); return ( '
' +
          c.label +
          '
' + c.label + "
" ); }) .join(""); return ( '
' + '' + '' +
      label +
      '' + '' + label + "" + '
' + cardsHtml + '' + "
" ); } function slidesMarkup() { return config.slides .map(function (s, i) { var copyPos = s.copyPos || ""; var kind = s.sceneKind || "intro"; var isWorld = kind === "world"; var isReveal = kind === "reveal"; var stepClass = "world-step world-step--center"; if (isWorld) { stepClass = s.worldSide === "left" ? "world-step world-step--split" : "world-step world-step--split world-step--visual-right"; } var worldBtn = isWorld && s.worldType ? '' : ""; var copyInner = ""; if (isReveal) { copyInner = ""; } else if (isWorld) { copyInner = '
' + (s.label ? '

' + s.label + "

" : "") + (s.lead ? '

' + s.lead + "

" : "") + (s.body ? '

' + s.body + "

" : "") + (s.purpose ? '

' + s.purpose + "

" : "") + (s.difference ? '

' + s.difference + "

" : "") + worldBtn + "
"; } else { copyInner = '
' + (s.eyebrow ? '

' + s.eyebrow + "

" : "") + (s.title ? "

" + s.title + "

" : "") + (s.body ? '

' + s.body + "

" : "") + "
"; } return ( '
' + '
' + '' + '
' + copyInner + "
" + "
" ); }) .join(""); } function portfolioHref(go) { var hashMap = { projects: "#projekte", leistungen: "#leistungen", about: "#ueber-mich", contact: "#kontakt", offerte: "#offerte", }; var hash = hashMap[go] || "#home"; return hash === "#home" ? "MULTIVERSUM.html" : "MULTIVERSUM.html" + hash; } function portfolioMarkup() { return PORTFOLIO_CARDS.map(function (c) { return ( '' + c.label + '' + c.sub + "" ); }).join(""); } function orbSrc(world) { return asset(ASSETS.orbs[world]); } function heroMarkup() { return ( '
' + '
' + '' + '' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + "
" + '
' + decoSetMarkup("multiverse") + decoSetMarkup("multiversum") + decoSetMarkup("nexora") + decoSetMarkup("professional") + decoSetMarkup("freiraum") + '' + "
" + '
' + '
' + '
' + '
' + '
' + '
' + "
" + '
' + atmosphereMarkup() + '' + '
' + '' + '' + '' + '' + '' + '' + '' + '
' + worldZoneMarkup("multiversum", "MULTIVERSUM") + worldZoneMarkup("nexora", "NEXORA") + worldZoneMarkup("professional", "PROFESSIONAL") + worldZoneMarkup("freiraum", "FREIRAUM") + "
" + '
' + '
' + '
' + slidesMarkup() + "
" + '
' + '
' + portfolioMarkup() + "
" + '' + "
" ); } function cacheDom() { if (!heroEl) return; dom.layers = {}; heroEl.querySelectorAll("[data-parallax-layer]").forEach(function (el) { dom.layers[el.getAttribute("data-parallax-layer")] = el; }); dom.bgs = {}; heroEl.querySelectorAll("[data-bg]").forEach(function (el) { dom.bgs[el.getAttribute("data-bg")] = el; }); dom.stars = heroEl.querySelector('[data-layer="stars"]'); dom.vignette = heroEl.querySelector('[data-layer="vignette"]'); dom.decoSets = {}; heroEl.querySelectorAll("[data-deco-theme]").forEach(function (set) { dom.decoSets[set.getAttribute("data-deco-theme")] = { root: set, orbit: set.querySelector('[data-d="orbit"]'), orbit2: set.querySelector('[data-d="orbit2"]'), light: set.querySelector('[data-d="light"]'), line: set.querySelector('[data-d="line"]'), }; }); dom.particleFields = {}; heroEl.querySelectorAll("[data-particle-field]").forEach(function (el) { dom.particleFields[el.getAttribute("data-particle-field")] = el; }); dom.atmo = {}; heroEl.querySelectorAll("[data-atmo]").forEach(function (el) { dom.atmo[el.getAttribute("data-atmo")] = el; }); dom.topicGrids = {}; heroEl.querySelectorAll("[data-topic-world]").forEach(function (el) { dom.topicGrids[el.getAttribute("data-topic-world")] = el; }); dom.orbs = { multiversum: heroEl.querySelector('[data-orb="multiversum"]'), nexora: heroEl.querySelector('[data-orb="nexora"]'), professional: heroEl.querySelector('[data-orb="professional"]'), freiraum: heroEl.querySelector('[data-orb="freiraum"]'), }; dom.worldZones = dom.orbs; dom.slides = heroEl.querySelectorAll(".mv-scroll-slide"); dom.portfolio = heroEl.querySelector('[data-layer="portfolio"]'); dom.cue = heroEl.querySelector(".mv-scroll-cue"); dom.worldStage = heroEl.querySelector("[data-world-stage]"); dom.worldGlow = heroEl.querySelector("[data-world-glow]"); dom.worldRing = heroEl.querySelector("[data-world-ring]"); dom.radialGlows = {}; heroEl.querySelectorAll("[data-radial-glow]").forEach(function (el) { dom.radialGlows[el.getAttribute("data-radial-glow")] = el; }); dom.overlayGlow = heroEl.querySelector("[data-overlay-glow]"); dom.transitionTrail = heroEl.querySelector("[data-transition-trail]"); dom.worldAccents = {}; heroEl.querySelectorAll("[data-accent-world]").forEach(function (el) { dom.worldAccents[el.getAttribute("data-accent-world")] = el; }); } function getVideoHeroOffset() { if (window.WeltenVideoHero && typeof window.WeltenVideoHero.getOffset === "function") { return window.WeltenVideoHero.getOffset() || 0; } var video = document.getElementById("alWorldVideoHero"); if (!video) return 0; var slideHome = document.getElementById("slide-home"); if (!slideHome || !slideHome.classList.contains("active")) return 0; return video.offsetHeight || 0; } function getProgress() { if (!scrollRoot || !heroEl) return 0; var max = heroEl.offsetHeight - scrollRoot.clientHeight; if (max <= 0) return 0; var offset = getVideoHeroOffset(); var adjusted = Math.max(0, scrollRoot.scrollTop - offset); return clamp(adjusted / max, 0, 1); } function dominantOrbKey(state) { var orbs = state.orbs || {}; var best = ""; var bestOp = 0; WORLD_KEYS.forEach(function (key) { var op = (orbs[key] && orbs[key].opacity) || 0; if (op > bestOp) { bestOp = op; best = key; } }); return bestOp > 0.45 ? best : ""; } function orbFlyCfg(cfg, fly) { if (!cfg) return cfg; var f = easeOutExpo(clamp(fly, 0, 1)); return { x: cfg.x || 0, y: (cfg.y || 0) + lerp(12, 0, f), scale: lerp(0.78, 1, f), opacity: cfg.opacity || 0, blur: lerp(3.2, cfg.blur || 0, f), z: cfg.z || 0, }; } function applyOrb(el, cfg, parallax, entrance, dominant) { if (!el || !cfg) return; var blur = cfg.blur || 0; var ent = entrance !== undefined ? entrance : 1; var scale = cfg.scale * lerp(0.68, 1, easeOutExpo(ent)); var yShift = clamp((cfg.y || 0) - parallax * 6, -5, 12); el.style.transform = "translate3d(calc(-50% + " + cfg.x + "vw), calc(-50% + " + yShift + "vh), " + (cfg.z || 0) + "px) scale(" + scale + ")"; el.style.opacity = String((cfg.opacity || 0) * ent); el.style.filter = blur > 0.1 ? "blur(" + blur + "px)" : ""; el.style.zIndex = String(Math.round(cfg.z || 0)); var shown = (cfg.opacity || 0) * ent > 0.03; el.style.display = shown ? "" : "none"; el.style.visibility = shown ? "visible" : "hidden"; el.style.pointerEvents = shown ? "auto" : "none"; el.classList.toggle("is-dominant", !!dominant && cfg.opacity > 0.42); el.classList.toggle("is-labeled", cfg.opacity > 0.34); } function applyWorldAccents() { return; } function stateActiveWorldMatches(world, activeSlide) { var slide = config.slides[activeSlide]; return !!(slide && slide.sceneKind === "world" && slide.worldType === world); } function visibleWorldsForScene(activeScene) { var kind = slideKind(activeScene); if (kind === "intro") return WORLD_KEYS.slice(); if (kind === "finale") return []; if (kind === "world") { var slide = config.slides[activeScene]; return slide && slide.worldType ? [slide.worldType] : []; } if (kind === "reveal" || kind === "merge") { return WORLD_KEYS; } return []; } function setVisibleWorlds(worlds) { if (!dom.worldZones) return; WORLD_KEYS.forEach(function (world) { var zone = dom.worldZones[world]; if (!zone) return; zone.classList.toggle("is-visible", worlds.indexOf(world) >= 0); }); } function forceHideAllWorldZones() { if (!dom.worldZones) return; WORLD_KEYS.forEach(function (world) { var zone = dom.worldZones[world]; if (!zone) return; zone.style.opacity = "0"; zone.style.visibility = "hidden"; zone.style.pointerEvents = "none"; zone.style.display = "none"; zone.style.transform = ""; zone.style.filter = ""; zone.classList.remove( "is-focus", "is-visible", "is-scene-active", "is-scroll-active", "is-reveal-active", "is-reveal-focus", "is-merge-active", "is-active", "is-hover", "is-expanded", "is-dimmed", "is-background", "is-dominant", "is-labeled" ); var collage = zone.querySelector(".world-collage"); if (collage) { collage.style.opacity = "0"; collage.style.visibility = "hidden"; collage.style.pointerEvents = "none"; collage.style.display = "none"; collage.querySelectorAll(".world-card").forEach(function (cardEl) { cardEl.style.opacity = "0"; cardEl.style.visibility = "hidden"; cardEl.style.display = "none"; }); } var core = zone.querySelector(".world-core"); if (core) { core.style.opacity = "0"; core.style.visibility = "hidden"; core.style.display = "none"; } }); if (dom.worldStage) { dom.worldStage.style.display = "none"; dom.worldStage.style.opacity = "0"; dom.worldStage.style.visibility = "hidden"; dom.worldStage.style.pointerEvents = "none"; } } function resetWorldZoneInlineStyles() { if (!dom.worldZones) return; if (dom.worldStage) { dom.worldStage.style.display = ""; dom.worldStage.style.opacity = ""; dom.worldStage.style.visibility = ""; dom.worldStage.style.pointerEvents = ""; } WORLD_KEYS.forEach(function (world) { var zone = dom.worldZones[world]; if (!zone) return; zone.style.opacity = ""; zone.style.visibility = ""; zone.style.pointerEvents = ""; zone.style.display = ""; zone.style.transform = ""; zone.style.filter = ""; var collage = zone.querySelector(".world-collage"); if (collage) { collage.style.opacity = ""; collage.style.visibility = ""; collage.style.pointerEvents = ""; collage.style.display = ""; collage.querySelectorAll(".world-card").forEach(function (cardEl) { cardEl.style.opacity = ""; cardEl.style.visibility = ""; cardEl.style.display = ""; cardEl.style.pointerEvents = ""; }); } var core = zone.querySelector(".world-core"); if (core) { core.style.opacity = ""; core.style.visibility = ""; core.style.display = ""; core.style.pointerEvents = ""; core.style.zIndex = ""; } }); } function clearOrbPresentationLocks() { if (!dom.orbs) return; WORLD_KEYS.forEach(function (world) { var zone = dom.orbs[world]; if (!zone) return; zone.style.removeProperty("display"); zone.style.removeProperty("visibility"); zone.style.removeProperty("pointer-events"); var core = zone.querySelector(".world-core"); if (core) { core.style.removeProperty("display"); core.style.removeProperty("visibility"); core.style.removeProperty("pointer-events"); } }); } function syncWorldZoneOrbs(state, layoutMode, activeSlide, phases, stageOp, postWorld, worldHold) { if (postWorld) { forceHideAllWorldZones(); return; } if (dom.worldStage) dom.worldStage.style.display = ""; if (layoutMode === "split-world") { var slide = config.slides[activeSlide]; var focusWorld = slide && slide.worldType ? slide.worldType : ""; var hold = worldHold !== undefined ? worldHold : 1; var chapter = phases && phases.chapter; var worldIn = chapter ? chapter.worldIn : easeOutExpo(clamp((phases && phases.orb) || stageOp, 0, 1)); var unitOp = chapter ? chapter.worldOpacity * hold : easeOutExpo(clamp((phases && phases.orb) || stageOp, 0, 1)) * hold; var fly = worldIn; WORLD_KEYS.forEach(function (world) { var zone = dom.orbs[world]; if (!zone) return; var isFocus = focusWorld === world; if (!isFocus || unitOp < 0.03) { zone.style.opacity = "0"; zone.style.visibility = "hidden"; zone.style.pointerEvents = "none"; zone.style.display = "none"; return; } zone.style.display = ""; zone.style.visibility = fly > 0.04 ? "visible" : "hidden"; zone.style.pointerEvents = fly > 0.2 ? "auto" : "none"; var cfg = state.orbs[world] || { x: 0, y: 0, scale: 1, opacity: 1, blur: 0, z: 50 }; var flyCfg = { x: 0, y: lerp(6, 0, fly), scale: lerp(0.88, 1, easeInOutSine(fly)), opacity: unitOp, blur: lerp(1.5, 0, fly), z: cfg.z || 50, }; applyOrb(zone, flyCfg, 0, 1, true); }); return; } var dominant = dominantOrbKey(state); var orbPhase = layoutMode === "overview" ? 0.85 : layoutMode === "intro" ? 1 : 0; var orbEntrance = layoutMode === "intro" ? orbPhase : orbPhase * stageOp; clearOrbPresentationLocks(); function orbWithPhase(cfg) { if (!cfg) return cfg; var next = {}; Object.keys(cfg).forEach(function (k) { next[k] = cfg[k]; }); next.opacity = (cfg.opacity || 0) * orbPhase; return next; } applyOrb(dom.orbs.multiversum, orbFlyCfg(orbWithPhase(state.orbs.multiversum), orbEntrance), 0, orbEntrance, dominant === "multiversum"); applyOrb(dom.orbs.nexora, orbFlyCfg(orbWithPhase(state.orbs.nexora), orbEntrance), 0, orbEntrance, dominant === "nexora"); applyOrb(dom.orbs.professional, orbFlyCfg(orbWithPhase(state.orbs.professional), orbEntrance), 0, orbEntrance, dominant === "professional"); applyOrb(dom.orbs.freiraum, orbFlyCfg(orbWithPhase(state.orbs.freiraum), orbEntrance), 0, orbEntrance, dominant === "freiraum"); } function worldFocusSide(activeScene) { var slide = config.slides[activeScene]; if (!slide || slide.sceneKind !== "world") return ""; return slide.worldSide === "left" ? "left" : slide.worldSide === "right" ? "right" : ""; } function applyWorldZoneStates(activeScene, phases, worldHold) { if (!dom.worldZones) return; var slide = config.slides[activeScene]; var kind = slideKind(activeScene); var revealed = revealedWorldsForScene(activeScene); var highlight = (slide && slide.highlightWorld) || ""; var focusWorld = kind === "world" && slide ? slide.worldType : ""; setVisibleWorlds(visibleWorldsForScene(activeScene)); var focusSide = worldFocusSide(activeScene); var hold = worldHold !== undefined ? worldHold : 1; var chapter = phases && phases.chapter; var env = (phases.env !== undefined ? phases.env : 1) * (kind === "world" ? hold : 1); var cardFlyBase = kind === "world" && chapter ? chapter.worldIn : 1; WORLD_KEYS.forEach(function (world) { var zone = dom.worldZones[world]; if (!zone) return; var hasCards = revealed.indexOf(world) >= 0; var isWorldFocus = focusWorld === world; var isReveal = kind === "reveal"; var isRevealFocus = isReveal && highlight === world; var isMerge = kind === "merge"; var showCollage = hasCards && (isReveal || isWorldFocus || isMerge); var isSoftBg = isReveal && !hasCards; zone.classList.toggle("is-focus", isWorldFocus); zone.classList.toggle("is-reveal-active", isReveal && hasCards); zone.classList.toggle("is-reveal-focus", isRevealFocus); zone.classList.toggle("is-scene-active", showCollage || isWorldFocus || isMerge); zone.classList.toggle("is-scroll-active", isWorldFocus || isRevealFocus); zone.classList.toggle("is-background", isSoftBg); zone.classList.toggle("is-merge-active", isMerge && hasCards); zone.classList.toggle("is-dimmed", isReveal && hasCards && !isRevealFocus); zone.classList.toggle("world-zone--left-focus", isWorldFocus && focusSide === "left"); zone.classList.toggle("world-zone--right-focus", isWorldFocus && focusSide === "right"); var zoneEnv = isWorldFocus ? env : isRevealFocus ? env : hasCards ? Math.max(0.72, env * 0.88) : isSoftBg ? 0.55 : 0; zone.style.setProperty("--scene-env", String(zoneEnv)); var orbit = cardOrbitForZone(kind, world, hasCards, isWorldFocus, isRevealFocus); zone.style.setProperty("--card-orbit", orbit); if (showCollage || isWorldFocus) { var collage = zone.querySelector(".world-collage"); if (collage) { collage.style.setProperty("--card-orbit", orbit); if (isWorldFocus && chapter) { collage.style.opacity = String(chapter.worldOpacity > 0.04 ? 1 : 0); collage.style.transform = "scale(" + lerp(0.96, 1, chapter.worldIn) + ")"; collage.style.visibility = chapter.worldOpacity > 0.04 ? "visible" : "hidden"; } else { var cardEnv = isWorldFocus ? env : isRevealFocus ? env : Math.min(1, env * 0.9 + 0.1); collage.style.opacity = String(cardEnv); collage.style.transform = "scale(" + lerp(0.96, 1, cardEnv) + ")"; collage.style.visibility = cardEnv > 0.04 ? "visible" : "hidden"; } collage.style.pointerEvents = "none"; collage.querySelectorAll(".world-card").forEach(function (cardEl, cardIdx) { var stagger = isWorldFocus && chapter ? cardIdx * 0.04 : cardIdx * 0.08; var cardT = isWorldFocus && chapter ? clamp((cardFlyBase - stagger) / Math.max(1 - stagger, 0.01), 0, 1) : clamp((env - stagger) / (1 - stagger), 0, 1); var cardReveal = easeOutExpo(cardT); var cardOp = isWorldFocus && chapter ? cardReveal * chapter.worldOpacity : cardReveal; cardEl.style.opacity = String(cardOp); cardEl.style.filter = cardReveal < 1 ? "blur(" + lerp(2.5, 0, cardReveal) + "px)" : ""; cardEl.style.pointerEvents = cardT > 0.18 ? "auto" : "none"; }); } var core = zone.querySelector(".world-core"); if (core) { core.style.opacity = String(isWorldFocus ? env : Math.min(1, zoneEnv * 0.95 + 0.05)); core.style.pointerEvents = isWorldFocus && env > 0.18 ? "auto" : "none"; core.style.zIndex = isWorldFocus ? "50" : ""; } } else { zone.style.removeProperty("--scene-env"); var collageOff = zone.querySelector(".world-collage"); if (collageOff) { collageOff.style.opacity = ""; collageOff.style.transform = ""; collageOff.style.visibility = ""; collageOff.style.pointerEvents = ""; collageOff.style.removeProperty("--card-orbit"); collageOff.querySelectorAll(".world-card").forEach(function (cardEl) { cardEl.style.opacity = ""; cardEl.style.filter = ""; cardEl.style.pointerEvents = ""; }); } var coreOff = zone.querySelector(".world-core"); if (coreOff) { coreOff.style.opacity = ""; coreOff.style.pointerEvents = ""; coreOff.style.zIndex = ""; } } if (!isWorldFocus) { zone.classList.remove("is-expanded", "world-zone--left-focus", "world-zone--right-focus"); } }); } function applyCardParallax() { if (mobileLayout || tabletLayout || reducedMotion || !dom.worldZones) return; var par = window.innerWidth <= 1439 ? 0.55 : 1; WORLD_KEYS.forEach(function (world) { var zone = dom.worldZones[world]; if (!zone || (!zone.classList.contains("is-active") && !zone.classList.contains("is-hover"))) return; zone.querySelectorAll(".world-card").forEach(function (card, i) { var depth = 1 + (i % 4) * 0.08; var tx = mouseParallax.x * 14 * depth * par; var ty = mouseParallax.y * 10 * depth * par; card.style.setProperty("--px", tx + "px"); card.style.setProperty("--py", ty + "px"); }); }); } function bindCollageInteractions() { if (!heroEl || !dom.worldZones) return; heroEl.addEventListener("mousemove", function (e) { if (mobileLayout || tabletLayout) return; var rect = heroEl.getBoundingClientRect(); mouseParallax.x = clamp((e.clientX - rect.left) / rect.width - 0.5, -0.5, 0.5) * 2; mouseParallax.y = clamp((e.clientY - rect.top) / rect.height - 0.5, -0.5, 0.5) * 2; applyCardParallax(); }); WORLD_KEYS.forEach(function (world) { var zone = dom.worldZones[world]; if (!zone) return; zone.addEventListener("mouseenter", function () { zone.classList.add("is-hover"); var layout = heroEl ? heroEl.getAttribute("data-layout") : ""; if ( layout === "split-world" || zone.classList.contains("is-scene-active") || zone.classList.contains("is-merge-active") ) { zone.classList.add("is-active"); } }); zone.addEventListener("mouseleave", function () { zone.classList.remove("is-hover", "is-active"); zone.querySelectorAll(".world-card").forEach(function (card) { card.style.removeProperty("--px"); card.style.removeProperty("--py"); }); }); zone.addEventListener("focusin", function () { zone.classList.add("is-active"); }); zone.addEventListener("focusout", function () { zone.classList.remove("is-active"); }); zone.addEventListener("click", function (e) { if (e.target.closest("a.world-card") || e.target.closest(".world-core") || e.target.closest(".world-cards-more")) return; if (!tabletLayout && !mobileLayout) return; if (!zone.classList.contains("is-visible")) return; if (!zone.classList.contains("is-active")) { e.preventDefault(); WORLD_KEYS.forEach(function (w) { var z = dom.worldZones[w]; if (z) z.classList.remove("is-active", "is-expanded"); }); zone.classList.add("is-active", "is-expanded"); } }); }); heroEl.querySelectorAll("[data-world-more]").forEach(function (btn) { btn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); var w = btn.getAttribute("data-world-more"); switchWorld(w); }); }); } function applyDecoImg(el, cfg, weight, build, axis) { if (!el || !cfg) return; var b = build || 0; var scale = cfg.scale * lerp(0.35, 1, easeInOutCubic(b)); var rot = cfg.rotate + axis * 12 * b; var xOff = axis * 8 * (1 - b); el.style.opacity = String(cfg.opacity * weight * lerp(0.2, 1, b)); el.style.transform = "translate3d(calc(-50% + " + xOff + "vw), -50%, 0) scale(" + scale + ") rotate(" + rot + "deg)"; } function applyTopicGrid(el, opacity, build) { if (!el) return; var cards = el.querySelectorAll(".mv-topic-card"); el.style.opacity = String(opacity); el.style.pointerEvents = opacity > 0.35 ? "auto" : "none"; cards.forEach(function (card, i) { var delay = i * 0.09; var t = clamp((build - delay) / (1 - delay), 0, 1); t = easeOutExpo(t); card.style.opacity = String(t * opacity); card.style.transform = "translate3d(0, " + lerp(22, 0, t) + "px, 0) scale(" + lerp(0.94, 1, t) + ")"; }); } function updateFrame() { rafId = 0; if (!heroEl || !config) return; if (!bootPaintDone && heroEl.classList.contains("is-js-ready")) { bootPaintDone = true; } var rawP = getProgress(); if (reducedMotion || mobileLayout) { animProgress = rawP; } else if (rawP <= introChapterEnd()) { animProgress = rawP; } else if (rawP >= freiraumChapterEnd() - 0.05) { animProgress += (rawP - animProgress) * 0.28; if (Math.abs(rawP - animProgress) < 0.00035) animProgress = rawP; } else { var lerpFactor = config.smoothLerp || 0.075; animProgress += (rawP - animProgress) * lerpFactor; if (Math.abs(rawP - animProgress) < 0.00035) animProgress = rawP; } if (rawP !== animProgress) requestTick(); var phaseP = rawP; var p = animProgress; var activeSlideEarly = slideIndexForProgress(phaseP); var layoutModeEarly = layoutModeForProgress(phaseP, activeSlideEarly); var state = getState(p); var bookendBg = bookendBackgroundForLayout(layoutModeEarly); if (bookendBg) state.backgrounds = bookendBg; var px = config.parallax || {}; var par = mobileLayout ? 0.3 : window.innerWidth <= 1024 ? 0.65 : 1; var bounds = config.bounds; var fade = config.fade || 0.055; var mvBuild = worldBuildProgress(p, "multiversum"); var nexBuild = worldBuildProgress(p, "nexora"); var proBuild = worldBuildProgress(p, "professional"); var freBuild = worldBuildProgress(p, "freiraum"); if (dom.layers[1]) dom.layers[1].style.transform = "translate3d(0, " + layerShift(p, px.layer1_bg || 0.08, par) + "vh, 0)"; if (dom.layers[2]) dom.layers[2].style.transform = "translate3d(0, " + layerShift(p, px.layer2_nebula || 0.18, par) + "vh, 0)"; if (dom.layers[3]) dom.layers[3].style.transform = "translate3d(0, " + layerShift(p, px.layer3_particles || 0.28, par) + "vh, 0)"; if (dom.layers[4]) dom.layers[4].style.transform = "translate3d(0, " + layerShift(p, px.layer4_objects || 0.42, par) + "vh, 0)"; if (dom.layers[5]) dom.layers[5].style.transform = "translate3d(0, " + layerShift(p, px.layer5_cards || 0.58, par) + "vh, 0)"; if (dom.layers[6]) dom.layers[6].style.transform = "translate3d(0, " + layerShift(p, px.layer6_copy || 0.72, par) + "vh, 0)"; if (dom.layers[7]) dom.layers[7].style.transform = "translate3d(0, " + layerShift(p, px.layer7_ui || 0.85, par) + "vh, 0)"; ["multiverse", "nexora", "professional", "freiraum"].forEach(function (key) { if (dom.bgs[key]) { var bgOp = (state.backgrounds[key] || 0) * par; dom.bgs[key].style.opacity = String(bgOp); dom.bgs[key].style.visibility = bgOp > 0.015 ? "visible" : "hidden"; var img = dom.bgs[key].querySelector("img"); if (img) { if (bookendBg && key === "multiverse") { img.style.transform = "translate(-50%, -50%) scale(1)"; img.style.width = "100vw"; img.style.height = "100%"; img.style.minHeight = "100%"; img.style.objectFit = "cover"; } else { var zoom = 1 + bgOp * 0.08; img.style.transform = "scale(" + zoom + ")"; } } } }); if (dom.stars) dom.stars.style.opacity = String(state.stars * par); if (dom.vignette) dom.vignette.style.opacity = String(state.vignette * 0.62); var themeWeights = { multiverse: state.backgrounds.multiverse || 0, multiversum: state.backgrounds.multiverse || 0, nexora: state.backgrounds.nexora || 0, professional: state.backgrounds.professional || 0, freiraum: state.backgrounds.freiraum || 0, }; Object.keys(dom.decoSets).forEach(function (theme) { var set = dom.decoSets[theme]; var w = themeWeights[theme] || 0; var d = state.decor; var build = theme === "nexora" ? nexBuild : theme === "professional" ? proBuild : theme === "freiraum" ? freBuild : theme === "multiverse" || theme === "multiversum" ? Math.max(mvBuild, 0.42) : 0.5; var decoOp = w > 0.04 ? Math.min(1, w * 1.2) : 0; set.root.style.opacity = String(decoOp); set.root.style.visibility = decoOp > 0.02 ? "visible" : "hidden"; applyDecoImg(set.orbit, d.orbit, w, build, 0); applyDecoImg(set.orbit2, d.orbit2, w, build, 1); if (set.light) { set.light.style.opacity = String(d.light.opacity * w * lerp(0.3, 1, build)); set.light.style.transform = "translate3d(-50%, calc(-50% + " + lerp(8, 0, build) + "vh), 0) scale(" + lerp(0.8, 1.05, build) + ")"; } if (set.line) { set.line.style.opacity = String(d.line.opacity * w * build); set.line.style.transform = "translate3d(-50%, -50%, 0) scaleX(" + lerp(0.2, 1, build) + ") scaleY(" + lerp(0.6, 1, build) + ")"; } }); Object.keys(dom.particleFields).forEach(function (theme) { var el = dom.particleFields[theme]; var w = theme === "multiversum" ? themeWeights.multiverse || 0 : themeWeights[theme] || 0; var pOp = w > 0.04 ? (state.decor.particles.opacity || 0.2) * w * par : 0; el.style.opacity = String(pOp); el.style.visibility = pOp > 0.02 ? "visible" : "hidden"; }); var activeBound = boundSegmentForProgress(p); var activeSlide = slideIndexForProgress(phaseP); var slideRange = slideVisibilityRange(activeSlide); var slideLocal = segmentProgress(p, slideRange[0], slideRange[1]); var boundLocal = segmentProgress(p, bounds[activeBound], bounds[activeBound + 1]); var phases = worldScenePhases(slideLocal, activeSlide); var layoutMode = layoutModeForProgress(phaseP, activeSlide); var postWorld = isPostWorldPhase(phaseP, layoutMode); if (dom.atmo.nexora) { var atmoScale = postWorld ? 0 : layoutMode === "split-world" ? 0.55 : 1; var nexAtmoOp = themeWeights.nexora > 0.04 ? themeWeights.nexora * nexBuild * atmoScale : 0; dom.atmo.nexora.style.opacity = String(nexAtmoOp); dom.atmo.nexora.style.visibility = nexAtmoOp > 0.02 ? "visible" : "hidden"; dom.atmo.nexora.style.transform = "scale(" + lerp(0.9, 1, nexBuild) + ")"; } if (dom.atmo.professional) { var atmoScalePro = postWorld ? 0 : layoutMode === "split-world" ? 0.5 : 1; var proAtmoOp = themeWeights.professional > 0.04 ? themeWeights.professional * proBuild * atmoScalePro : 0; dom.atmo.professional.style.opacity = String(proAtmoOp); dom.atmo.professional.style.visibility = proAtmoOp > 0.02 ? "visible" : "hidden"; dom.atmo.professional.style.transform = "translateX(" + lerp(-6, 0, proBuild) + "vw)"; } if (dom.atmo.freiraum) { var atmoScaleFre = postWorld ? 0 : layoutMode === "split-world" ? 0.58 : 1; var freAtmoOp = themeWeights.freiraum > 0.04 ? themeWeights.freiraum * freBuild * atmoScaleFre : 0; dom.atmo.freiraum.style.opacity = String(freAtmoOp); dom.atmo.freiraum.style.visibility = freAtmoOp > 0.02 ? "visible" : "hidden"; dom.atmo.freiraum.style.transform = "scale(" + lerp(0.85, 1.08, freBuild) + ") rotate(" + lerp(-4, 3, freBuild) + "deg)"; } if (dom.bgs.overview) { dom.bgs.overview.style.opacity = "0"; } var stageOp = postWorld ? 0 : worldStageOpacity(activeSlide, phaseP, phases, slideLocal); var collageFocus = collageFocusForScene(activeSlide); var revealed = revealedWorldsForScene(activeSlide); heroEl.setAttribute("data-collage-focus", collageFocus); heroEl.setAttribute("data-reveal-step", String(revealed.length)); var wasPostWorld = heroEl.classList.contains("is-post-world"); var prevLayout = heroEl.getAttribute("data-layout") || ""; heroEl.classList.toggle("is-post-world", postWorld); var worldHold = layoutMode === "split-world" && isWorldSlide(activeSlide) ? worldChapterHold(slideLocal) : 1; if (postWorld) { forceHideAllWorldZones(); } else { if ( wasPostWorld || (layoutMode === "intro" && (prevLayout === "split-world" || prevLayout === "finale" || prevLayout === "portfolio")) ) { resetWorldZoneInlineStyles(); } applyWorldZoneStates(activeSlide, phases, worldHold); } if (dom.transitionTrail) { var trailOp = clamp((state.transitionTrail || 0) * par, 0, 1); dom.transitionTrail.style.opacity = String(trailOp); dom.transitionTrail.style.transform = "translate3d(-50%, -50%, 0) scale(" + lerp(0.88, 1.08, trailOp) + ")"; } if (dom.overlayGlow) { var mergeOp = 0; dom.overlayGlow.style.opacity = String(mergeOp); } var dominant = postWorld ? "" : dominantOrbKey(state); syncWorldZoneOrbs(state, layoutMode, activeSlide, phases, stageOp, postWorld, worldHold); applyWorldAccents(); if (dom.worldGlow) { var glowOp = postWorld ? 0 : isWorldSlide(activeSlide) ? phases.orb * 1.05 : layoutMode === "overview" || layoutMode === "intro" ? 0.62 : 0.24; dom.worldGlow.style.opacity = String(glowOp); } if (dom.worldRing) { var ringOp = postWorld ? 0 : isWorldSlide(activeSlide) ? lerp(0.22, 0.78, phases.orb) : layoutMode === "overview" || layoutMode === "intro" ? 0.48 : 0.14; var ringRot = slideKind(activeSlide) === "world" && config.slides[activeSlide].worldType === "professional" ? p * 24 : slideKind(activeSlide) === "world" && config.slides[activeSlide].worldType === "freiraum" ? p * 48 : p * 36; dom.worldRing.style.opacity = String(ringOp); dom.worldRing.style.transform = "translate(-50%, -50%) rotate(" + ringRot + "deg) scale(" + lerp(0.94, 1.02, phases.orb || 0.5) + ")"; } if (dom.radialGlows) { WORLD_KEYS.forEach(function (w) { var el = dom.radialGlows[w]; if (!el) return; var wgt = themeWeights[w] || 0; var ro = postWorld ? 0 : wgt * (isWorldSlide(activeSlide) ? phases.orb * 0.95 : layoutMode === "overview" || layoutMode === "intro" ? 0.5 : 0.15); el.style.opacity = String(clamp(ro, 0, 1)); }); } if (dom.worldStage) { var effectiveStageOp = postWorld ? 0 : stageOp * worldHold; var stageChapter = phases.chapter; var stageScale = layoutMode === "split-world" && stageChapter ? lerp(0.94, 1, stageChapter.worldIn) * (1 - stageChapter.fadeOut * 0.06) : lerp(0.96, 1, phases.orb || 1); dom.worldStage.style.opacity = String(effectiveStageOp); dom.worldStage.style.visibility = effectiveStageOp > 0.03 ? "visible" : "hidden"; dom.worldStage.style.pointerEvents = effectiveStageOp > 0.2 ? "auto" : "none"; dom.worldStage.style.transform = layoutMode === "split-world" ? "translateY(-50%) scale(" + stageScale + ")" : layoutMode === "overview" ? "translate(-50%, -50%) scale(" + lerp(0.94, 1, slideLocal) + ")" : ""; } var topicNex = topicVisibilityForWorld(); var topicPro = topicVisibilityForWorld(); var topicFre = topicVisibilityForWorld(); applyTopicGrid(dom.topicGrids.nexora, topicNex.opacity, topicNex.build); applyTopicGrid(dom.topicGrids.professional, topicPro.opacity, topicPro.build); applyTopicGrid(dom.topicGrids.freiraum, topicFre.opacity, topicFre.build); for (var i = 0; i < dom.slides.length; i++) { var slideKindI = slideKind(i); if (slideKindI === "reveal" || slideKindI === "merge") { dom.slides[i].style.opacity = "0"; dom.slides[i].style.pointerEvents = "none"; dom.slides[i].classList.remove("is-active", "is-world-chapter"); continue; } if (slideKindI === "finale") { continue; } if (postWorld && isWorldSlide(i)) { dom.slides[i].style.opacity = "0"; dom.slides[i].style.pointerEvents = "none"; dom.slides[i].classList.remove("is-active", "is-world-chapter"); continue; } var visRange = slideVisibilityRange(i); var visStart = visRange[0]; var visEnd = visRange[1]; var vis = slideVisibility(p, visStart, visEnd, fade); var slide = dom.slides[i]; var slideLocal = segmentProgress(p, visStart, visEnd); var slidePhases = worldScenePhases(slideLocal, i); var copyEl = slide.querySelector(".world-copy"); var hintEl = slide.querySelector(".world-visual__hint"); var isWorldChapter = isWorldSlide(i); var chapterPh = isWorldChapter ? slidePhases.chapter || worldChapterPhases(slideLocal) : null; var hold = isWorldChapter ? chapterPh.hold : 1; var copyOpacity = isWorldChapter ? vis.opacity * (chapterPh ? chapterPh.textOpacity : slidePhases.env) : vis.opacity; var scale = lerp(0.98, 1, copyOpacity); var worldVisible = chapterPh && chapterPh.worldOpacity > 0.03; if (isWorldChapter) { slide.style.opacity = String(vis.opacity > 0.02 && (worldVisible || copyOpacity > 0.02) ? 1 : 0); slide.style.transform = "translate3d(-50%, -50%, 0)"; slide.style.pointerEvents = vis.active && copyOpacity > 0.2 ? "auto" : "none"; if (copyEl) { var textOp = chapterPh && chapterPh.showText ? vis.opacity * chapterPh.hold : 0; copyEl.style.opacity = String(textOp); copyEl.style.transform = "translate3d(0, " + lerp(14, 0, easeInOutSine(textOp)) + "px, 0)"; if (chapterPh && chapterPh.showText && textOp > 0.01) { applyWorldExplainTexts(copyEl, chapterPh.textLocal, textOp); } else { resetWorldExplainTexts(copyEl); } } } else { if (copyEl) resetWorldExplainTexts(copyEl); slide.style.opacity = String(vis.opacity); slide.style.transform = "translate3d(-50%, calc(-50% + " + vis.y + "px), 0) scale(" + scale + ")"; slide.style.pointerEvents = vis.active && copyOpacity > 0.15 ? "auto" : "none"; if (copyEl) { copyEl.style.opacity = ""; copyEl.style.transform = ""; } } slide.classList.toggle("is-active", vis.active && (isWorldChapter ? copyOpacity > 0.35 || worldVisible : vis.opacity > 0.45)); slide.classList.toggle("is-world-chapter", isWorldChapter && vis.active); if (hintEl) { hintEl.style.opacity = String(isWorldChapter ? slidePhases.hint * vis.opacity : 0); } } if (dom.portfolio) { var finIdx = config.slides.length - 1; var finCopyRange = chapterRange("finaleCopyOnly", [0.86, 0.91]); var frameRange = chapterRange("portfolioFrame", [0.91, 0.93]); var portRange = chapterRange("portfolioButtons", [0.93, 0.97]); var finCopyLocal = segmentProgress(phaseP, finCopyRange[0], finCopyRange[1]); var portLocal = segmentProgress(phaseP, portRange[0], portRange[1]); var copyIn = easeInOutSine(clamp(finCopyLocal / 0.24, 0, 1)); var copyOut = finCopyLocal > 0.62 ? easeOutQuart(clamp((finCopyLocal - 0.62) / 0.38, 0, 1)) : 0; var finCopyOp = phaseP >= finCopyRange[0] && phaseP < finCopyRange[1] ? copyIn * (1 - copyOut) : 0; var finaleDone = phaseP >= finCopyRange[1]; var frameLocal = segmentProgress(phaseP, frameRange[0], frameRange[1]); var skipFrame = !!(config && config.skipPortfolioFrame); var frameOp = skipFrame ? 0 : finaleDone && phaseP >= frameRange[0] && phaseP < portRange[0] ? easeInOutSine(clamp(frameLocal / 0.55, 0, 1)) : 0; var portOp = finaleDone && phaseP >= portRange[0] ? easeInOutSine(clamp((portLocal - 0.18) / 0.42, 0, 1)) : 0; var cardScale = lerp(0.94, 1, portOp); var portY = lerp(18, 0, portOp); dom.portfolio.style.opacity = String(portOp > 0.02 ? portOp : frameOp > 0.02 ? frameOp * 0.4 : 0); dom.portfolio.style.transform = "translate3d(-50%, calc(-50% + " + portY + "px), 0) scale(" + cardScale + ")"; dom.portfolio.style.setProperty("--portfolio-frame-op", String(frameOp)); dom.portfolio.style.visibility = finaleDone && (frameOp > 0.02 || portOp > 0.02) ? "visible" : "hidden"; dom.portfolio.style.display = finaleDone && (frameOp > 0.02 || portOp > 0.02) ? "grid" : "none"; var portfolioInteractive = portOp > 0.55; dom.portfolio.style.pointerEvents = portfolioInteractive ? "auto" : "none"; heroEl.classList.toggle("is-portfolio-frame-visible", frameOp > 0.08); heroEl.classList.toggle("is-portfolio-visible", portOp > 0.12); heroEl.classList.toggle("is-portfolio-interactive", portfolioInteractive); heroEl.classList.toggle("is-portfolio-hold", portfolioInteractive && !portfolioHoldUnlocked); heroEl.classList.toggle("is-finale-copy-visible", finCopyOp > 0.08); var finSlide = dom.slides[finIdx]; if (finSlide) { var finCopy = finSlide.querySelector(".final-copy") || finSlide.querySelector(".scene-copy"); if (finCopy) { finCopy.style.opacity = String(finCopyOp); finCopy.style.transform = "translateY(" + lerp(10, -14, copyOut) + "px)"; } finSlide.style.opacity = String(finCopyOp > 0.02 ? finCopyOp : 0); finSlide.style.pointerEvents = finCopyOp > 0.15 ? "auto" : "none"; finSlide.style.left = "50%"; finSlide.style.top = "50%"; finSlide.style.transform = "translate3d(-50%, -50%, 0)"; } } if (dom.cue) dom.cue.style.opacity = String(p < 0.05 ? 1 : clamp(1 - (p - 0.05) / 0.04, 0, 1)); var releaseRange = chapterRange("releaseToNormalContent", [0.97, 1]); var heroScrollMax = heroMaxScroll(); var videoOffset = getVideoHeroOffset(); var contentReleased = phaseP >= releaseRange[0] || (scrollRoot && heroScrollMax > 0 && scrollRoot.scrollTop >= videoOffset + heroScrollMax - 6); document.body.classList.toggle("is-below-parallax", contentReleased); heroEl.classList.toggle("is-content-released", contentReleased); heroEl.setAttribute("data-active-world", layoutMode === "split-world" && config.slides[activeSlide] && config.slides[activeSlide].worldType ? config.slides[activeSlide].worldType : state.activeWorld || "multiversum"); heroEl.setAttribute("data-scene", String(activeSlide + 1)); heroEl.setAttribute("data-layout", layoutMode); heroEl.setAttribute("data-world-side", worldFocusSide(activeSlide)); heroEl.setAttribute("data-dominant-orb", dominant || ""); heroEl.classList.toggle("is-world-focus", layoutMode === "split-world"); document.body.classList.toggle("is-world-focus", layoutMode === "split-world"); } function requestTick() { if (frozen || rafId) return; rafId = window.requestAnimationFrame(updateFrame); } function freezeHero() { frozen = true; if (rafId) { window.cancelAnimationFrame(rafId); rafId = 0; } if (heroEl) { heroEl.setAttribute("data-active-world", "multiversum"); heroEl.classList.remove("is-world-focus"); } try { document.body.classList.remove("is-world-focus"); } catch (e) {} } function unfreezeHero() { if (!frozen) return; frozen = false; requestTick(); } window.addEventListener("message", function (e) { if (!e.data) return; if (e.data.type === "portfolio-world-pause" || e.data.type === "portfolio-cleanup-transition") freezeHero(); if (e.data.type === "portfolio-world-enter" || e.data.type === "portfolio-world-reveal") unfreezeHero(); }); function heroMaxScroll() { if (!scrollRoot || !heroEl) return 0; return Math.max(heroEl.offsetHeight - scrollRoot.clientHeight, 0); } function isPortfolioHoldActive() { if (!heroEl || !config) return false; var p = getProgress(); var portRange = chapterRange("portfolioButtons", [0.93, 0.97]); if (p < portRange[0]) return false; var portLocal = segmentProgress(p, portRange[0], portRange[1]); return portLocal > 0.42; } function bindPortfolioScrollHold() { if (!scrollRoot || !heroEl) return; function onWheel(e) { if (!isPortfolioHoldActive() || portfolioHoldUnlocked) return; var max = heroMaxScroll(); if (max <= 0) return; var videoOffset = getVideoHeroOffset(); var atHeroEnd = scrollRoot.scrollTop >= videoOffset + max - 8; if (!atHeroEnd) return; if (e.deltaY > 0) { if (portfolioHoldScrolls < PORTFOLIO_HOLD_SCROLLS) { e.preventDefault(); portfolioHoldScrolls += 1; heroEl.setAttribute("data-portfolio-hold", String(portfolioHoldScrolls)); if (portfolioHoldScrolls >= PORTFOLIO_HOLD_SCROLLS) { portfolioHoldUnlocked = true; heroEl.classList.add("is-portfolio-unlocked"); heroEl.removeAttribute("data-portfolio-hold"); } requestTick(); } } else if (e.deltaY < 0 && portfolioHoldScrolls > 0) { portfolioHoldScrolls = Math.max(0, portfolioHoldScrolls - 1); heroEl.setAttribute("data-portfolio-hold", String(portfolioHoldScrolls)); } } var touchStartY = 0; scrollRoot.addEventListener( "wheel", onWheel, { passive: false } ); scrollRoot.addEventListener( "touchstart", function (e) { touchStartY = e.touches && e.touches[0] ? e.touches[0].clientY : 0; }, { passive: true } ); scrollRoot.addEventListener( "touchmove", function (e) { if (!isPortfolioHoldActive() || portfolioHoldUnlocked) return; var max = heroMaxScroll(); var videoOffset = getVideoHeroOffset(); if (max <= 0 || scrollRoot.scrollTop < videoOffset + max - 8) return; var y = e.touches && e.touches[0] ? e.touches[0].clientY : touchStartY; var delta = touchStartY - y; if (delta > 12) { if (portfolioHoldScrolls < PORTFOLIO_HOLD_SCROLLS) { e.preventDefault(); portfolioHoldScrolls += 1; heroEl.setAttribute("data-portfolio-hold", String(portfolioHoldScrolls)); touchStartY = y; if (portfolioHoldScrolls >= PORTFOLIO_HOLD_SCROLLS) { portfolioHoldUnlocked = true; heroEl.classList.add("is-portfolio-unlocked"); heroEl.removeAttribute("data-portfolio-hold"); } requestTick(); } } }, { passive: false } ); } function bindScroll() { if (!scrollRoot) return; onScrollHandler = function () { if (!heroEl) { requestTick(); return; } var max = heroMaxScroll(); var videoOffset = getVideoHeroOffset(); if (scrollRoot.scrollTop < videoOffset + max - 48) { portfolioHoldScrolls = 0; portfolioHoldUnlocked = false; heroEl.classList.remove("is-portfolio-unlocked"); heroEl.removeAttribute("data-portfolio-hold"); } requestTick(); }; scrollRoot.addEventListener("scroll", onScrollHandler, { passive: true }); window.addEventListener("resize", onResize, { passive: true }); requestTick(); } function onResize() { var wasMobile = mobileLayout; mobileLayout = window.matchMedia("(max-width: 767px)").matches; tabletLayout = window.matchMedia("(max-width: 1099px)").matches && !mobileLayout; reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (heroEl) { heroEl.classList.toggle("mv-scroll-story--mobile", mobileLayout); heroEl.classList.toggle("mv-scroll-story--tablet", tabletLayout); heroEl.classList.toggle("mv-scroll-story--reduced", reducedMotion); var vh = config.scrollHeightVh || 820; if (window.innerWidth <= 1024 && config.scrollHeightVhTablet) vh = config.scrollHeightVhTablet; if (mobileLayout) vh = Math.min(vh, 520); heroEl.style.setProperty("--mv-scroll-vh", String(vh)); } if (wasMobile !== mobileLayout && dom.orbs) { WORLD_KEYS.forEach(function (w) { var orb = dom.orbs[w]; if (!orb) return; var img = orb.querySelector(".mv-orb-bubble__standalone"); if (img) img.src = orbSrc(w); }); } requestTick(); } function isDesktopParallaxHero() { try { return window.matchMedia("(min-width: 1024px)").matches; } catch (e) { return window.innerWidth >= 1024; } } function buildParallaxHero(goChapter) { if (window.__mvParallaxBuilding || document.getElementById("mvParallaxHero")) return document.getElementById("mvParallaxHero"); if (document.body.getAttribute("data-world") !== "general") return null; if (!isDesktopParallaxHero()) return null; if (!window.MVSceneConfig || !window.MVSceneConfig.keyframes) return null; window.__mvParallaxBuilding = true; try { config = window.MVSceneConfig; mobileLayout = false; tabletLayout = false; var stage = document.getElementById("dnaStage"); if (!stage) return null; var staleStatic = document.getElementById("mvStaticHero"); if (staleStatic) staleStatic.remove(); scrollRoot = document.getElementById("slide-home"); if (!scrollRoot) return null; if ("scrollRestoration" in window.history) { try { window.history.scrollRestoration = "manual"; } catch (e) {} } scrollRoot.scrollTop = 0; animProgress = 0; var wrap = document.createElement("div"); wrap.innerHTML = heroMarkup(); heroEl = wrap.firstElementChild; heroEl.style.setProperty("--mv-scroll-vh", String(config.scrollHeightVh || 820)); heroEl.style.visibility = "hidden"; heroEl.style.opacity = "0"; heroEl.style.pointerEvents = "none"; stage.parentNode.insertBefore(heroEl, stage); stage.classList.add("mv-dna-hidden"); stage.remove(); cacheDom(); bindGoButtons(heroEl, goChapter); onResize(); bindScroll(); scrollRoot.scrollTop = 0; animProgress = 0; heroEl.setAttribute("data-active-world", "multiversum"); heroEl.setAttribute("data-layout", "intro"); updateFrame(); updateFrame(); heroEl.classList.add("is-js-ready", "is-boot-painted"); heroEl.style.removeProperty("visibility"); heroEl.style.removeProperty("opacity"); heroEl.style.removeProperty("pointer-events"); function signalReady() { if (window.__mvHeroReadySent) return; window.__mvHeroReadySent = true; try { document.body.classList.add("mv-home-ready"); } catch (eReadyClass) {} try { if (window.parent && window.parent !== window) { window.parent.postMessage({ type: "mv-hero-ready" }, "*"); } } catch (eReady) {} try { document.dispatchEvent(new CustomEvent("mv-hero-ready")); } catch (eEv) {} } signalReady(); function finishHeavyBoot() { Object.keys(dom.particleFields || {}).forEach(function (theme) { var src = ASSETS.deco[theme] && ASSETS.deco[theme].particles; if (src && dom.particleFields[theme]) { dom.particleFields[theme].style.backgroundImage = "url(" + asset(src) + ")"; } }); bindCollageInteractions(); bindPortfolioScrollHold(); updateFrame(); if (window.WeltenPreviewI18n && typeof window.WeltenPreviewI18n.applyParallax === "function") { try { var langKey = "mv-preview-lang"; var lang = localStorage.getItem(langKey) || sessionStorage.getItem(langKey) || "de"; window.WeltenPreviewI18n.applyParallax(document, lang); } catch (e) {} } window.__mvParallaxHeroReady = true; bootPaintDone = true; } if (typeof requestIdleCallback === "function") { requestIdleCallback(finishHeavyBoot, { timeout: 1200 }); } else { setTimeout(finishHeavyBoot, 40); } return heroEl; } finally { window.__mvParallaxBuilding = false; } } function destroy() { freezeHero(); if (rafId) window.cancelAnimationFrame(rafId); if (scrollRoot && onScrollHandler) scrollRoot.removeEventListener("scroll", onScrollHandler); window.removeEventListener("resize", onResize); portfolioHoldScrolls = 0; portfolioHoldUnlocked = false; onScrollHandler = null; } window.MVParallaxHero = { build: buildParallaxHero, destroy: destroy, switchWorld: switchWorld, requestWorldSwitch: requestWorldSwitch, applyLang: function (lang) { if (window.WeltenPreviewI18n && typeof window.WeltenPreviewI18n.applyParallax === "function") { window.WeltenPreviewI18n.applyParallax(document, lang || "de"); } }, }; })();