{"id":1017,"date":"2025-05-29T18:11:08","date_gmt":"2025-05-29T16:11:08","guid":{"rendered":"https:\/\/universum.church\/?page_id=1017"},"modified":"2025-07-02T13:33:50","modified_gmt":"2025-07-02T11:33:50","slug":"elementor-1017","status":"publish","type":"page","link":"https:\/\/universum.church\/index.php\/elementor-1017\/","title":{"rendered":"Elementor #1017"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1017\" class=\"elementor elementor-1017\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0716a29 e-flex e-con-boxed e-con e-parent\" data-id=\"0716a29\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-bca0433 elementor-widget elementor-widget-html\" data-id=\"bca0433\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div id=\"universum-container\" style=\"position: relative; width: 100%; height: 100vh; overflow: hidden;\">\n  \n  <style>\n    #universum-container { \n      margin: 0; \n      padding: 0;\n      overflow: hidden; \n      font-family: \"Winky Rough\", sans-serif;\n      font-optical-sizing: auto;\n      font-weight: 300;\n      font-style: normal;\n      touch-action: none;\n      -webkit-user-select: none;\n      user-select: none;\n      background: linear-gradient(135deg, #000418 0%, #001122 50%, #000C18 100%);\n      position: relative;\n      width: 100vw;\n      height: 100vh;\n      box-sizing: border-box;\n    }\n    \n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    \n    html, body {\n      width: 100vw;\n      height: 100vh;\n      margin: 0;\n      padding: 0;\n      overflow: hidden;\n    }\n    \n    #universum-container::before {\n      content: '';\n      position: absolute;\n      top: 0;\n      left: 0;\n      right: 0;\n      bottom: 0;\n      width: 100vw;\n      height: 100vh;\n      background: \n        radial-gradient(circle at 20% 20%, rgba(255, 153, 51, 0.1) 0%, transparent 50%),\n        radial-gradient(circle at 80% 80%, rgba(0, 255, 255, 0.05) 0%, transparent 50%),\n        radial-gradient(circle at 50% 50%, rgba(233, 184, 7, 0.03) 0%, transparent 70%);\n      pointer-events: none;\n      z-index: 1;\n    }\n    #universum-container canvas { \n   display: block;\n  width: 100vw !important;\n  height: 100vh !important;\n  position: absolute !important;\n  top: 0 !important;\n  left: 0 !important;\n    }\n    .loading { \n      position: fixed; \n      top: 50%; \n      left: 50%; \n      transform: translate(-50%, -50%);\n      background: linear-gradient(135deg, rgba(0, 0, 0, 0.9) 0%, rgba(233, 184, 7, 0.1) 100%);\n      color: white;\n      padding: 30px 40px;\n      border-radius: 15px;\n      z-index: 1000;\n      backdrop-filter: blur(10px);\n      border: 1px solid rgba(233, 184, 7, 0.3);\n      box-shadow: \n        0 0 30px rgba(233, 184, 7, 0.2),\n        inset 0 0 20px rgba(233, 184, 7, 0.05);\n      font-size: 16px;\n      text-align: center;\n      text-shadow: 0 0 10px rgba(233, 184, 7, 0.5);\n      animation: loadingPulse 2s ease-in-out infinite;\n    }\n    .e-con > .e-con-inner{\n        max-width: 100vw !important;\n        padding-block-start: 0px !important;\n        \n    }\n    .e-con{\n        padding-left: 0px !important;\n        padding-right:0px !important;\n    }\n    @keyframes loadingPulse {\n      0%, 100% { \n        box-shadow: 0 0 30px rgba(233, 184, 7, 0.2), inset 0 0 20px rgba(233, 184, 7, 0.05);\n        transform: translate(-50%, -50%) scale(1);\n      }\n      50% { \n        box-shadow: 0 0 50px rgba(233, 184, 7, 0.4), inset 0 0 30px rgba(233, 184, 7, 0.1);\n        transform: translate(-50%, -50%) scale(1.02);\n      }\n    }\n    .instructions {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 100vw;\n      height: 100vh;\n      display: flex;\n      flex-direction: column;\n      justify-content: center;\n      align-items: center;\n      background: \n        linear-gradient(135deg, rgba(0, 4, 24, 0.95) 0%, rgba(0, 17, 34, 0.95) 100%),\n        radial-gradient(circle at 50% 50%, rgba(233, 184, 7, 0.1) 0%, transparent 70%);\n      color: white;\n      text-align: center;\n      line-height: 1.6;\n      cursor: pointer;\n      z-index: 10;\n      padding: 20px;\n      box-sizing: border-box;\n      backdrop-filter: blur(5px);\n      transition: all 0.3s ease;\n    }\n    \n    \n    .instructions:hover {\n      background: \n        linear-gradient(135deg, rgba(0, 4, 24, 0.98) 0%, rgba(0, 17, 34, 0.98) 100%),\n        radial-gradient(circle at 50% 50%, rgba(233, 184, 7, 0.15) 0%, transparent 70%);\n    }\n    .instructions h1 {\n      font-size: 28px;\n      margin-bottom: 25px;\n      background: linear-gradient(45deg, #e9b807, #ffffff, #00ffff);\n      background-size: 200% 200%;\n      -webkit-background-clip: text;\n      -webkit-text-fill-color: transparent;\n      background-clip: text;\n      animation: gradientShift 3s ease-in-out infinite;\n      text-shadow: 0 0 20px rgba(233, 184, 7, 0.3);\n      filter: drop-shadow(0 0 10px rgba(233, 184, 7, 0.2));\n    }\n    \n    @keyframes gradientShift {\n      0%, 100% { background-position: 0% 50%; }\n      50% { background-position: 100% 50%; }\n    }\n    \n    .instructions p {\n      font-size: 16px;\n      max-width: 80%;\n      margin-bottom: 12px;\n      text-shadow: 0 0 10px rgba(0, 0, 0, 0.8);\n      transition: all 0.3s ease;\n    }\n    \n    .instructions p:hover {\n      text-shadow: 0 0 15px rgba(233, 184, 7, 0.4);\n      transform: scale(1.02);\n    }\n    .instructions strong {\n      color: #00ffff;\n      text-shadow: 0 0 15px rgba(0, 255, 255, 0.5);\n      font-weight: 400;\n    }\n    \n    @media (max-width: 768px) {\n      .instructions h1 {\n        font-size: 20px;\n      }\n      .instructions p {\n        font-size: 14px;\n        max-width: 90%;\n      }\n    }\n    \n    .cursor {\n      position: fixed;\n      top: 50%;\n      left: 50%;\n      transform: translate(-50%, -50%);\n      width: 20px;\n      height: 20px;\n      border-radius: 50%;\n      border: 2px solid white;\n      pointer-events: none;\n      z-index: 100;\n      opacity: 0.9;\n      transition: all 0.15s ease;\n      box-shadow: \n        0 0 20px rgba(255, 255, 255, 0.3),\n        inset 0 0 10px rgba(255, 255, 255, 0.1);\n      animation: cursorPulse 2s ease-in-out infinite;\n    }\n    \n    @keyframes cursorPulse {\n      0%, 100% { \n        box-shadow: 0 0 20px rgba(255, 255, 255, 0.3), inset 0 0 10px rgba(255, 255, 255, 0.1);\n      }\n      50% { \n        box-shadow: 0 0 30px rgba(255, 255, 255, 0.5), inset 0 0 15px rgba(255, 255, 255, 0.2);\n      }\n    }\n    \n    .cursor::after {\n      content: '';\n      position: absolute;\n      top: 50%;\n      left: 50%;\n      transform: translate(-50%, -50%);\n      width: 4px;\n      height: 4px;\n      background: white;\n      border-radius: 50%;\n      box-shadow: 0 0 10px rgba(255, 255, 255, 0.8);\n    }\n    .cursor.active {\n      background-color: rgba(255, 255, 255, 0.3);\n      transform: translate(-50%, -50%) scale(0.8);\n      box-shadow: \n        0 0 40px rgba(255, 255, 255, 0.6),\n        inset 0 0 20px rgba(255, 255, 255, 0.3);\n    }\n    \n    @media (hover: none) and (pointer: coarse) {\n      .cursor {\n        display: none !important;\n      }\n    }\n    \n    .mobile-controls {\n      position: fixed;\n      bottom: 25px;\n      left: 50%;\n      transform: translateX(-50%);\n      display: none;\n      z-index: 100;\n      background: linear-gradient(135deg, rgba(0, 0, 0, 0.8) 0%, rgba(233, 184, 7, 0.1) 100%);\n      padding: 12px 20px;\n      border-radius: 15px;\n      color: white;\n      font-size: 12px;\n      text-align: center;\n      backdrop-filter: blur(10px);\n      border: 1px solid rgba(233, 184, 7, 0.3);\n      box-shadow: 0 0 20px rgba(233, 184, 7, 0.2);\n      text-shadow: 0 0 5px rgba(233, 184, 7, 0.3);\n    }\n    \n    @media (hover: none) and (pointer: coarse) {\n      .mobile-controls {\n        display: block;\n      }\n    }\n\n    @import url('https:\/\/fonts.googleapis.com\/css2?family=Winky+Rough:wght@300&display=swap');\n  <\/style>\n\n  <div id=\"loading\" class=\"loading\">Loading Three.js...<\/div>\n  <div id=\"instructions\" class=\"instructions\">\n    <h1>Universum Interactive Explorer<\/h1>\n    <div class=\"desktop-instructions\">\n      <p><strong>Click<\/strong> to activate the experience<\/p>\n      <p><strong>Click on the floor<\/strong> to teleport to that location<\/p>\n      <p>Press <strong>ESC<\/strong> to exit controls<\/p>\n    <\/div>\n    <div class=\"mobile-instructions\">\n      <p><strong>Tap<\/strong> to start exploring<\/p>\n      <p><strong>Drag<\/strong> to look around<\/p>\n      <p><strong>Pinch<\/strong> to zoom in\/out<\/p>\n      <p><strong>Tap the floor<\/strong> to move there<\/p>\n    <\/div>\n    <p>Listen for the audio near the candle light!<\/p>\n    <p>Click\/Tap anywhere to begin<\/p>\n  <\/div>\n  <div id=\"cursor\" class=\"cursor\"><\/div>\n  <div class=\"mobile-controls\">\n    Drag to look \u2022 Pinch to zoom \u2022 Tap floor to move\n  <\/div>\n\n  <!-- Three.js Core -->\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\"><\/script>\n  \n  <!-- Load Three.js addons with all required dependencies -->\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/controls\/PointerLockControls.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/controls\/OrbitControls.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/loaders\/GLTFLoader.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/loaders\/DRACOLoader.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/loaders\/RGBELoader.js\"><\/script>\n  \n  <!-- Post-processing dependencies in correct order -->\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/shaders\/CopyShader.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/shaders\/LuminosityHighPassShader.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/postprocessing\/Pass.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/postprocessing\/EffectComposer.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/postprocessing\/RenderPass.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/postprocessing\/ShaderPass.js\"><\/script>\n  <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.128.0\/examples\/js\/postprocessing\/UnrealBloomPass.js\"><\/script>\n\n  <script>\n    \/\/ Detect if user is on mobile device\n    function isMobileDevice() {\n      return \/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini\/i.test(navigator.userAgent) ||\n             (window.matchMedia && window.matchMedia('(hover: none) and (pointer: coarse)').matches);\n    }\n\n    \/\/ Update instructions based on device\n    function updateInstructions() {\n      const desktopInstructions = document.querySelector('.desktop-instructions');\n      const mobileInstructions = document.querySelector('.mobile-instructions');\n      \n      if (isMobileDevice()) {\n        if (desktopInstructions) desktopInstructions.style.display = 'none';\n        if (mobileInstructions) mobileInstructions.style.display = 'block';\n      } else {\n        if (desktopInstructions) desktopInstructions.style.display = 'block';\n        if (mobileInstructions) mobileInstructions.style.display = 'none';\n      }\n    }\n\n    \/\/ Wait for all scripts to load\n    function waitForThreeJS() {\n      return new Promise((resolve) => {\n        const checkThreeJS = () => {\n          if (typeof THREE !== 'undefined' && \n              typeof THREE.GLTFLoader !== 'undefined' &&\n              typeof THREE.EffectComposer !== 'undefined' &&\n              typeof THREE.UnrealBloomPass !== 'undefined' &&\n              typeof THREE.LuminosityHighPassShader !== 'undefined' &&\n              typeof THREE.CopyShader !== 'undefined') {\n            resolve();\n          } else {\n            setTimeout(checkThreeJS, 100);\n          }\n        };\n        checkThreeJS();\n      });\n    }\n\n    \/\/ Initialize when everything is ready\n    waitForThreeJS().then(() => {\n      console.log('All Three.js components loaded successfully');\n      updateInstructions();\n      initializeUniverse();\n    });\n\n    function initializeUniverse() {\n      \/\/ Check if Three.js is loaded\n      if (typeof THREE === 'undefined') {\n        document.getElementById('loading').textContent = 'Error: Three.js failed to load!';\n        return;\n      }\n      \n      const isMobile = isMobileDevice();\n      console.log('Is mobile device:', isMobile);\n      \n      document.getElementById('loading').textContent = 'Loading model...';\n      \n      \/\/ ========================================\n      \/\/ FILE PLACEHOLDERS - REPLACE WITH YOUR ACTUAL FILE URLS\n      \/\/ ========================================\n      const FILE_URLS = {\n        glbModel: 'https:\/\/universum.church\/wp-content\/uploads\/2025\/05\/universum.glb',\n        hdrEnvironment: 'https:\/\/universum.church\/wp-content\/uploads\/2025\/05\/ui.hdr',\n        backgroundVideo: 'https:\/\/universum.church\/wp-content\/uploads\/2025\/05\/uu.mp4',\n        candleAudio: 'https:\/\/universum.church\/wp-content\/uploads\/2025\/05\/mom.mp3',\n        screenAudio: 'https:\/\/universum.church\/wp-content\/uploads\/2025\/05\/mom.mp3'\n      };\n      \/\/ ========================================\n\n      \/\/ Shader for dots, universum objects, and swan (pulsating white glow effect)\n      const pulsatingWhiteShader = {\n        vertexShader: `\n          varying vec2 vUv;\n          varying vec3 vPosition;\n          varying vec3 vNormal;\n          \n          void main() {\n            vUv = uv;\n            vPosition = position;\n            vNormal = normalize(normalMatrix * normal);\n            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n          }\n        `,\n        fragmentShader: `\n          uniform float time;\n          uniform vec3 color;\n          uniform float glowIntensity;\n          uniform float opacity;\n          \n          varying vec2 vUv;\n          varying vec3 vPosition;\n          varying vec3 vNormal;\n          \n          float breathingPulse(float t, float speed, float intensity) {\n            return 0.5 + intensity * (\n              0.4 * sin(t * speed) + \n              0.3 * sin(t * speed * 1.3 + 0.5) +\n              0.2 * sin(t * speed * 2.1 + 1.3) +\n              0.1 * sin(t * speed * 0.7 + 2.1)\n            );\n          }\n          \n          float sparkle(vec3 pos, float t) {\n            float noise = fract(sin(dot(vec3(pos.x * 50.0, pos.y * 30.0, pos.z * 40.0), vec3(12.9898, 78.233, 45.543))) * 43758.5453);\n            float sparkle = pow(noise, 20.0) * sin(t * noise * 10.0) * 0.5 + 0.5;\n            return sparkle;\n          }\n          \n          void main() {\n            float pulse = breathingPulse(time, 1.5, 0.8);\n            float rim = 1.0 - max(0.0, dot(vNormal, vec3(0.0, 0.0, 1.0)));\n            rim = pow(rim, 2.0) * glowIntensity;\n            float sparkleEffect = sparkle(vPosition, time) * rim * 0.7;\n            \n            vec3 finalColor = color * (0.3 + 0.7 * pulse);\n            finalColor += color * rim * pulse * 1.2;\n            finalColor += color * sparkleEffect;\n            finalColor += vec3(0.1, 0.2, 0.4) * rim * pulse * 0.3;\n            finalColor = min(finalColor, 2.0);\n            \n            float alpha = opacity * (0.2 + 0.8 * pulse) + rim * pulse * 0.4;\n            \n            gl_FragColor = vec4(finalColor, alpha);\n          }\n        `\n      };\n\n      \/\/ Shader for constellation objects (transparent with occasional glow)\n      const constellationVertexShader = `\n        varying vec2 vUv;\n        varying vec3 vNormal;\n        \n        void main() {\n          vUv = uv;\n          vNormal = normalize(normalMatrix * normal);\n          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n        }\n      `;\n\n      const constellationFragmentShader = `\n        uniform float time;\n        uniform vec3 color;\n        \n        varying vec2 vUv;\n        varying vec3 vNormal;\n        \n        void main() {\n          float alpha = 1.0;\n          float glowPeriod = mod(time, 10.0);\n          if (glowPeriod > 9.0 || glowPeriod < 0.5) {\n            float intensity = glowPeriod > 3.0 ? (10.0 - glowPeriod) * 2.0 : glowPeriod * 2.0;\n            vec3 glowColor = color + vec3(0.3, 0.3, 0.5) * intensity;\n            gl_FragColor = vec4(glowColor, alpha + 0.3 * intensity);\n          } else {\n            gl_FragColor = vec4(color, alpha);\n          }\n        }\n      `;\n\n      \/\/ Crown leaves glow shader\n      const leavesGlowVertexShader = `\n        varying vec2 vUv;\n        varying vec3 vPosition;\n        varying vec3 vNormal;\n        \n        void main() {\n          vUv = uv;\n          vPosition = position;\n          vNormal = normalize(normalMatrix * normal);\n          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n        }\n      `;\n\n      const leavesGlowFragmentShader = `\n        uniform float time;\n        uniform vec3 color;\n        uniform float glowIntensity;\n        \n        varying vec2 vUv;\n        varying vec3 vPosition;\n        varying vec3 vNormal;\n        \n        void main() {\n          float pulse = 0.7 + 0.3 * sin(time * 0.8);\n          float rim = 1.0 - max(0.0, dot(vNormal, vec3(0.0, 0.0, 1.0)));\n          rim = pow(rim, 2.0) * glowIntensity;\n          \n          vec3 finalColor = color * (0.6 + 0.4 * pulse);\n          finalColor += color * rim * pulse;\n          \n          gl_FragColor = vec4(finalColor, 0.7);\n        }\n      `;\n\n      \/\/ Scene setup\n      const scene = new THREE.Scene();\n      \n      \/\/ Load HDR environment map with error handling\n      if (THREE.RGBELoader) {\n        const hdrLoader = new THREE.RGBELoader();\n        hdrLoader.load(FILE_URLS.hdrEnvironment, \n          function(texture) {\n            texture.mapping = THREE.EquirectangularReflectionMapping;\n            scene.environment = texture;\n            scene.background = null;\n            \n            scene.traverse(function(child) {\n              if (child.isMesh && child.material && child.material.envMap === null) {\n                child.material.envMap = texture;\n                child.material.needsUpdate = true;\n              }\n            });\n            \n            console.log('HDR environment map loaded successfully');\n          },\n          function(progress) {\n            console.log('HDR loading progress:', progress);\n          },\n          function(error) {\n            console.warn('Could not load HDR environment map:', error);\n            console.log('Continuing without HDR environment...');\n          }\n        );\n      }\n      \n      \/\/ Camera setup with provided parameters\n      const camera = new THREE.PerspectiveCamera(\n        40.761,\n        window.innerWidth \/ window.innerHeight,\n        0.1,\n        10000\n      );\n      \n      \/\/ Set exact position and rotation\n      camera.position.set(-15.357, 6.072, -383.999);\n      camera.rotation.set(3.101, -0.033, -3.137);\n\n      \/\/ Create video element with error handling\n      const video = document.createElement('video');\n      video.src = FILE_URLS.backgroundVideo;\n      video.crossOrigin = 'anonymous';\n      video.loop = true;\n      video.muted = true;\n      video.playsInline = true;\n      video.setAttribute('playsinline', '');\n      \n      video.addEventListener('error', function(e) {\n        console.warn('Video failed to load:', e);\n        console.log('Continuing without video texture...');\n      });\n      \n      video.addEventListener('loadeddata', function() {\n        console.log('Video loaded successfully');\n      });\n      \n      \/\/ Handle video playback on mobile\n      const playVideo = () => {\n        video.play().catch(function(error) {\n          console.warn('Video autoplay failed:', error);\n        });\n      };\n      playVideo();\n\n      const videoTexture = new THREE.VideoTexture(video);\n      videoTexture.minFilter = THREE.LinearFilter;\n      videoTexture.magFilter = THREE.LinearFilter;\n      videoTexture.format = THREE.RGBAFormat;\n      videoTexture.wrapS = THREE.RepeatWrapping;\n      videoTexture.flipY = -1;\n      \n      \/\/ Get the container for proper sizing\n      const container = document.getElementById('universum-container');\n      \n      \/\/ Renderer setup\n      const renderer = new THREE.WebGLRenderer({ antialias: true });\n      renderer.setSize(window.innerWidth, window.innerHeight);\n      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n      renderer.outputEncoding = THREE.sRGBEncoding;\n      renderer.toneMapping = THREE.ACESFilmicToneMapping;\n      renderer.setClearColor(0x000C18);\n      renderer.toneMappingExposure = 0.5;\n      renderer.domElement.style.width = '100vw';\n      renderer.domElement.style.height = '100vh';\n      container.appendChild(renderer.domElement);\n      \n      \/\/ Enable shadows\n      renderer.shadowMap.enabled = true;\n      renderer.shadowMap.type = THREE.PCFSoftShadowMap;\n\n      \/\/ Setup post-processing with BloomPass\n      let composer;\n      if (THREE.EffectComposer && THREE.RenderPass && THREE.UnrealBloomPass && THREE.LuminosityHighPassShader) {\n        try {\n          const renderScene = new THREE.RenderPass(scene, camera);\n          \n          const bloomPass = new THREE.UnrealBloomPass(\n            new THREE.Vector2(window.innerWidth, window.innerHeight),\n            0.42,\n            0.27,\n            0.352\n          );\n\n          composer = new THREE.EffectComposer(renderer);\n          composer.addPass(renderScene);\n          composer.addPass(bloomPass);\n          \n          console.log('Post-processing composer initialized successfully with bloom');\n        } catch (error) {\n          console.warn('Error initializing post-processing composer:', error);\n          console.log('Falling back to regular rendering');\n          composer = null;\n        }\n      } else {\n        console.warn('Post-processing dependencies not available, using regular rendering');\n      }\n      \n      \/\/ Warmer ambient lighting\n      const ambientLight = new THREE.AmbientLight(0xffbb88, 0.16);\n      scene.add(ambientLight);\n      \n      \/\/ Add requested pulsating candle light with warm color\n      const candleLight = new THREE.PointLight(0xff9933, 700, 1340);\n      candleLight.position.set(-14.274, -54.154, -54.228);\n      candleLight.castShadow = false;\n      scene.add(candleLight);\n      \n      \/\/ Add second pulsating light 3 units higher\n      const upperLight = new THREE.PointLight(0xff9933, 700, 1340);\n      upperLight.position.set(-14.274, -51.154, -54.228);\n      upperLight.castShadow = false;\n      scene.add(upperLight);\n      \n      \/\/ Add fire\/flame effect with custom shader for more realism\n      const fireVertexShader = `\n        varying vec2 vUv;\n        void main() {\n          vUv = uv;\n          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n        }\n      `;\n      \n      const fireFragmentShader = `\n        uniform float time;\n        varying vec2 vUv;\n        \n        float rand(vec2 n) { \n          return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);\n        }\n        \n        float noise(vec2 p) {\n          vec2 ip = floor(p);\n          vec2 u = fract(p);\n          u = u * u * (3.0 - 2.0 * u);\n          \n          float res = mix(\n            mix(rand(ip), rand(ip + vec2(1.0, 0.0)), u.x),\n            mix(rand(ip + vec2(0.0, 1.0)), rand(ip + vec2(1.0, 1.0)), u.x), u.y);\n          return res * res;\n        }\n        \n        void main() {\n          vec2 uv = vUv;\n          float y = uv.y;\n          float noise1 = noise(vec2(uv.x * 5.0, uv.y * 10.0 - time * 0.5));\n          float noise2 = noise(vec2(uv.x * 7.0 + 0.3, uv.y * 20.0 - time * 0.3));\n          float fireNoise = noise1 * 0.7 + noise2 * 0.3;\n          float fireShape = (1.0 - y) * fireNoise;\n          fireShape = pow(fireShape, 0.8);\n          \n          vec3 fireColor = mix(\n            vec3(1.0, 0.3, 0.0),\n            vec3(1.0, 0.8, 0.0),\n            (1.0 - y) * fireShape\n          );\n          \n          fireColor = pow(fireColor, vec3(1.5));\n          float alpha = fireShape * 1.2;\n          alpha = clamp(alpha, 0.0, 1.0);\n          \n          gl_FragColor = vec4(fireColor, alpha);\n        }\n      `;\n      \n      \/\/ Create a plane for the fire shader\n      const fireGeometry = new THREE.PlaneGeometry(1.0, 3.0);\n      const fireMaterial = new THREE.ShaderMaterial({\n        uniforms: {\n          time: { value: 0 }\n        },\n        vertexShader: fireVertexShader,\n        fragmentShader: fireFragmentShader,\n        transparent: true,\n        blending: THREE.AdditiveBlending,\n        depthWrite: false,\n        side: THREE.DoubleSide\n      });\n      \n      \/\/ Create multiple fire planes in different orientations for 3D effect\n      const fireGroup = new THREE.Group();\n      \n      for (let i = 0; i < 6; i++) {\n        const angle = i * Math.PI \/ 3;\n        const firePlane = new THREE.Mesh(fireGeometry, fireMaterial);\n        firePlane.position.set(\n          candleLight.position.x,\n          candleLight.position.y + 1.0,\n          candleLight.position.z\n        );\n        firePlane.rotation.y = angle;\n        fireGroup.add(firePlane);\n      }\n      \n      scene.add(fireGroup);\n      \n      \/\/ Setup audio listener and positional audio\n      const listener = new THREE.AudioListener();\n      camera.add(listener);\n      \n      \/\/ Create positional audio source\n      const sound = new THREE.PositionalAudio(listener);\n      const audioLoader = new THREE.AudioLoader();\n      \n      \/\/ Set up audio at candle position\n      const audioPosition = new THREE.Vector3(-5.496, -36.818, 2.325);\n      \n      \/\/ Audio setup with error handling\n      function setupAudio() {\n        audioLoader.load(FILE_URLS.candleAudio, \n          function(buffer) {\n            sound.setBuffer(buffer);\n            sound.setRefDistance(10);\n            sound.setLoop(true);\n            sound.setVolume(0.05);\n            \n            \/\/ On mobile, we need user interaction to play audio\n            const playAudio = () => {\n              if (sound.context.state === 'suspended') {\n                sound.context.resume();\n              }\n              sound.play();\n              container.removeEventListener('touchstart', playAudio);\n              container.removeEventListener('click', playAudio);\n            };\n            \n            if (isMobile) {\n              container.addEventListener('touchstart', playAudio, { once: true });\n            } else {\n              playAudio();\n            }\n            \n            const audioObject = new THREE.Object3D();\n            audioObject.position.copy(audioPosition);\n            audioObject.add(sound);\n            scene.add(audioObject);\n            \n            console.log('Candle audio loaded at position', audioPosition);\n          }, \n          function(xhr) {\n            console.log('Candle audio loading: ' + (xhr.loaded \/ xhr.total * 100) + '%');\n          },\n          function(error) {\n            console.warn('Error loading candle audio:', error);\n            console.log('Continuing without candle audio...');\n          }\n        );\n      }\n      \n      \/\/ Controls setup - different for mobile and desktop\n      let controls;\n      const instructions = document.getElementById('instructions');\n      const cursor = document.getElementById('cursor');\n      \n      if (!isMobile && THREE.PointerLockControls) {\n        \/\/ Desktop: Use PointerLock controls\n        controls = new THREE.PointerLockControls(camera, container);\n        \n        instructions.addEventListener('click', function () {\n          controls.lock();\n        });\n        \n        controls.addEventListener('lock', function () {\n          instructions.style.display = 'none';\n          cursor.style.display = 'block';\n          \/\/ Start video playback on interaction\n          playVideo();\n        });\n        \n        controls.addEventListener('unlock', function () {\n          instructions.style.display = 'flex';\n          cursor.style.display = 'none';\n        });\n      } else if (THREE.OrbitControls) {\n        \/\/ Mobile: Use OrbitControls with touch support\n        controls = new THREE.OrbitControls(camera, renderer.domElement);\n        controls.enableDamping = true;\n        controls.dampingFactor = 0.05;\n        controls.rotateSpeed = 0.5;\n        controls.enablePan = false;\n        controls.minDistance = 5;\n        controls.maxDistance = 500;\n        controls.minPolarAngle = Math.PI * 0.4;\n        controls.maxPolarAngle = Math.PI * 0.85;\n        \n        \/\/ Set the initial target to look at the center of the scene\n        controls.target.set(0, 0, 0);\n        controls.update();\n        \n        instructions.addEventListener('click', function () {\n          instructions.style.display = 'none';\n          playVideo();\n        });\n        \n        instructions.addEventListener('touchstart', function () {\n          instructions.style.display = 'none';\n          playVideo();\n        });\n      }\n      \n      \/\/ ESC key handler to unlock controls (desktop only)\n      if (!isMobile) {\n        document.addEventListener('keydown', function(event) {\n          if (event.code === 'Escape' && controls && controls.isLocked) {\n            controls.unlock();\n          }\n        });\n      }\n      \n      \/\/ Target position for smooth camera movement\n      const cameraTarget = new THREE.Vector3();\n      let isMovingToTarget = false;\n      let movementProgress = 0;\n      const movementDuration = 1.5;\n      \n      \/\/ Floor object reference\n      let floorObject = null;\n      \n      \/\/ Objects to apply shaders to\n      const dotsObjects = [];\n      const constellationObjects = [];\n      let universumObject = null;\n      let swanObject = null;\n      let crownLeavesObject = null;\n      \n      \/\/ Array for Kaars hovering objects\n      const hoveringObjects = [];\n      \n      \/\/ Shader uniforms that will be updated in the render loop\n      const shaderUniforms = {\n        time: { value: 0 },\n        color: { value: new THREE.Color(0xffffff) },\n        glowIntensity: { value: 15.0 },\n        opacity: { value: 1.0 }\n      };\n      \n      const universumUniforms = {\n        time: { value: 0 },\n        color: { value: new THREE.Color(0xffffff) },\n        glowIntensity: { value: 20.0 },\n        opacity: { value: 1.0 }\n      };\n      \n      const swanUniforms = {\n        time: { value: 0 },\n        color: { value: new THREE.Color(0xffffff) },\n        glowIntensity: { value: 4.0 },\n        opacity: { value: 0.3}\n      };\n      \n      const constellationUniforms = {\n        time: { value: 0 },\n        color: { value: new THREE.Color(0xFFFFFF) }\n      };\n      \n      const leavesUniforms = {\n        time: { value: 0 },\n        color: { value: new THREE.Color(0xffffff) },\n        glowIntensity: { value: 2.5 }\n      };\n      \n      \/\/ Raycaster for floor detection\n      const raycaster = new THREE.Raycaster();\n      const mouse = new THREE.Vector2();\n      \n      \/\/ Click indicator ring geometry\n      const ringGeometry = new THREE.RingGeometry(0.5, 0.8, 32);\n      const ringMaterial = new THREE.MeshBasicMaterial({ \n        color: 0x00ffff, \n        side: THREE.DoubleSide,\n        transparent: true,\n        opacity: 0.8\n      });\n      const clickRing = new THREE.Mesh(ringGeometry, ringMaterial);\n      clickRing.rotation.x = -Math.PI \/ 2;\n      clickRing.visible = false;\n      scene.add(clickRing);\n      \n      \/\/ Mouse\/Touch event handlers\n      if (!isMobile) {\n        container.addEventListener('mousedown', function() {\n          if (controls && controls.isLocked) {\n            cursor.classList.add('active');\n          }\n        });\n        \n        container.addEventListener('mouseup', function() {\n          cursor.classList.remove('active');\n        });\n        \n        container.addEventListener('click', onClick, false);\n      } else {\n        \/\/ Mobile touch events\n        let touchStart = null;\n        \n        container.addEventListener('touchstart', function(event) {\n          if (event.touches.length === 1) {\n            touchStart = {\n              x: event.touches[0].clientX,\n              y: event.touches[0].clientY,\n              time: Date.now()\n            };\n          }\n        }, { passive: true });\n        \n        container.addEventListener('touchend', function(event) {\n          if (touchStart && event.changedTouches.length === 1) {\n            const touchEnd = {\n              x: event.changedTouches[0].clientX,\n              y: event.changedTouches[0].clientY,\n              time: Date.now()\n            };\n            \n            \/\/ Check if it's a tap (not a drag)\n            const distance = Math.sqrt(\n              Math.pow(touchEnd.x - touchStart.x, 2) + \n              Math.pow(touchEnd.y - touchStart.y, 2)\n            );\n            const duration = touchEnd.time - touchStart.time;\n            \n            if (distance < 10 && duration < 300) {\n              \/\/ It's a tap!\n              const rect = container.getBoundingClientRect();\n              mouse.x = ((touchEnd.x - rect.left) \/ rect.width) * 2 - 1;\n              mouse.y = -((touchEnd.y - rect.top) \/ rect.height) * 2 + 1;\n              onTap();\n            }\n            \n            touchStart = null;\n          }\n        }, { passive: true });\n      }\n      \n      function onClick(event) {\n        if (controls && !controls.isLocked) return;\n        \n        raycaster.setFromCamera(new THREE.Vector2(0, 0), camera);\n        checkFloorIntersection();\n      }\n      \n      function onTap() {\n        raycaster.setFromCamera(mouse, camera);\n        checkFloorIntersection();\n      }\n      \n      function checkFloorIntersection() {\n        if (floorObject) {\n          console.log(\"Testing intersection with floor:\", floorObject.name);\n          const intersects = raycaster.intersectObject(floorObject, false);\n          \n          if (intersects.length > 0) {\n            handleFloorClick(intersects[0].point);\n          } else {\n            \/\/ Try to intersect with all objects in the scene (fallback)\n            const allIntersects = raycaster.intersectObjects(scene.children, true);\n            \n            const floorIntersects = allIntersects.filter(hit => {\n              if (hit.point.y > camera.position.y) return false;\n              if (!hit.face) return false;\n              if (hit.face.normal.y <= 0.7) return false;\n              return true;\n            });\n            \n            if (floorIntersects.length > 0) {\n              console.log(\"Found floor-like surface\");\n              handleFloorClick(floorIntersects[0].point);\n            }\n          }\n        }\n      }\n      \n      function handleFloorClick(hitPoint) {\n        clickRing.position.copy(hitPoint);\n        clickRing.position.y += 0.01;\n        clickRing.visible = true;\n        clickRing.scale.set(0.1, 0.1, 0.1);\n        \n        gsapRingAnimation(clickRing);\n        \n        \/\/ Keep the camera Y position fixed\n        const fixedCameraHeight = 6.072;\n        cameraTarget.set(hitPoint.x, fixedCameraHeight, hitPoint.z);\n        isMovingToTarget = true;\n        movementProgress = 0;\n        \n        \/\/ For mobile with OrbitControls, update the target but maintain camera height\n        if (isMobile && controls && controls.target) {\n          controls.target.set(hitPoint.x, 0, hitPoint.z);\n        }\n      }\n      \n      \/\/ Animation function for click ring\n      function gsapRingAnimation(ring) {\n        setTimeout(() => {\n          ring.scale.set(1.5, 1.5, 1.5);\n          ring.material.opacity = 0;\n          \n          setTimeout(() => {\n            ring.visible = false;\n            ring.scale.set(0.1, 0.1, 0.1);\n            ring.material.opacity = 0.8;\n          }, 1000);\n        }, 0);\n      }\n      \n      \/\/ Hovering animation for Kaars objects\n      function animateHoveringObjects(time) {\n        const amplitude = 0.05;\n        const frequency = 0.5;\n        \n        hoveringObjects.forEach((obj, index) => {\n          const phase = index * 0.2;\n          const yOffset = amplitude * Math.sin(time * frequency + phase);\n          \n          obj.position.y = obj.userData.originalY + yOffset;\n          obj.rotation.y = obj.userData.originalRotY + 0.01 * Math.sin(time * frequency * 0.3 + phase);\n        });\n      }\n      \n      \/\/ Easing function for smooth camera movement\n      function easeInOutCubic(t) {\n        return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) \/ 2;\n      }\n      \n      \/\/ Initialize loaders\n      let dracoLoader, gltfLoader;\n      \n      if (THREE.DRACOLoader) {\n        dracoLoader = new THREE.DRACOLoader();\n        dracoLoader.setDecoderPath('https:\/\/www.gstatic.com\/draco\/versioned\/decoders\/1.5.5\/');\n      }\n      \n      if (THREE.GLTFLoader) {\n        gltfLoader = new THREE.GLTFLoader();\n        if (dracoLoader) {\n          gltfLoader.setDRACOLoader(dracoLoader);\n        }\n      } else {\n        console.error('GLTFLoader not available');\n        document.getElementById('loading').textContent = 'Error: GLTFLoader not available!';\n        return;\n      }\n      \n      \/\/ Use universum.glb as the model\n      const modelUrl = FILE_URLS.glbModel;\n      \n      \/\/ Load the 3D model\n      let model;\n      gltfLoader.load(\n        modelUrl,\n        function (gltf) {\n          model = gltf.scene;\n          \n          \/\/ Apply materials and collect special objects\n          model.traverse(function (child) {\n            if (child.name === 'screen') {\n              child.receiveShadow = false;\n              child.material = new THREE.MeshBasicMaterial({\n                map: videoTexture,\n                side: THREE.DoubleSide,\n                color: new THREE.Color(8.5, 8.5, 8.5)\n              });\n              child.material.envMap = null;\n\n              \/\/ Add positional audio with error handling\n              const screenAudio = new THREE.PositionalAudio(listener);\n              audioLoader.load(FILE_URLS.screenAudio, \n                function(buffer) {\n                  screenAudio.setBuffer(buffer);\n                  screenAudio.setRefDistance(.5);\n                  screenAudio.setLoop(true);\n                  screenAudio.setVolume(10.0);\n                  \n                  \/\/ On mobile, handle audio context\n                  const playScreenAudio = () => {\n                    if (screenAudio.context.state === 'suspended') {\n                      screenAudio.context.resume();\n                    }\n                    screenAudio.play();\n                    container.removeEventListener('touchstart', playScreenAudio);\n                    container.removeEventListener('click', playScreenAudio);\n                  };\n                  \n                  if (isMobile) {\n                    container.addEventListener('touchstart', playScreenAudio, { once: true });\n                  } else {\n                    playScreenAudio();\n                  }\n                  \n                  console.log('Screen audio loaded successfully');\n                },\n                function(xhr) {\n                  console.log('Screen audio loading: ' + (xhr.loaded \/ xhr.total * 100) + '%');\n                },\n                function(error) {\n                  console.warn('Error loading screen audio:', error);\n                  console.log('Continuing without screen audio...');\n                }\n              );\n              child.add(screenAudio);\n            }\n            \n            if (child.isMesh) {\n              child.userData.originalY = child.position.y;\n              child.userData.originalRotY = child.rotation.y;\n              \n              child.castShadow = true;\n              child.receiveShadow = true;\n              \n              if (child.name === 'floor') {\n                floorObject = child;\n                console.log('Found floor object:', floorObject);\n              } \n              else if (child.name.includes('dot') || child.name === 'dots') {\n                dotsObjects.push(child);\n                const customMaterial = new THREE.ShaderMaterial({\n                  uniforms: {\n                    time: shaderUniforms.time,\n                    color: shaderUniforms.color,\n                    glowIntensity: shaderUniforms.glowIntensity,\n                    opacity: shaderUniforms.opacity\n                  },\n                  vertexShader: pulsatingWhiteShader.vertexShader,\n                  fragmentShader: pulsatingWhiteShader.fragmentShader,\n                  transparent: true,\n                  side: THREE.DoubleSide\n                });\n                child.material = customMaterial;\n              }\n              else if (child.name.includes('constellation')) {\n                constellationObjects.push(child);\n                const customMaterial = new THREE.ShaderMaterial({\n                  uniforms: {\n                    time: constellationUniforms.time,\n                    color: constellationUniforms.color\n                  },\n                  vertexShader: constellationVertexShader,\n                  fragmentShader: constellationFragmentShader,\n                  transparent: true,\n                  side: THREE.DoubleSide\n                });\n                child.material = customMaterial;\n              }\n              else if (child.name.includes('Universum')) {\n                universumObject = child;\n                const customMaterial = new THREE.ShaderMaterial({\n                  uniforms: {\n                    time: universumUniforms.time,\n                    color: universumUniforms.color,\n                    glowIntensity: universumUniforms.glowIntensity,\n                    opacity: universumUniforms.opacity\n                  },\n                  vertexShader: pulsatingWhiteShader.vertexShader,\n                  fragmentShader: pulsatingWhiteShader.fragmentShader,\n                  transparent: true,\n                  side: THREE.DoubleSide\n                });\n                child.material = customMaterial;\n              }\n              else if (child.name.includes('Swan')) {\n                console.log('Found swan object:', child.name);\n                swanObject = child;\n                const customMaterial = new THREE.ShaderMaterial({\n                  uniforms: {\n                    time: swanUniforms.time,\n                    color: swanUniforms.color,\n                    glowIntensity: swanUniforms.glowIntensity,\n                    opacity: swanUniforms.opacity\n                  },\n                  vertexShader: pulsatingWhiteShader.vertexShader,\n                  fragmentShader: pulsatingWhiteShader.fragmentShader,\n                  transparent: true,\n                  side: THREE.DoubleSide\n                });\n                child.material = customMaterial;\n              }\n              else if (child.name.includes('crown_merged_normal_leaves_0')) {\n                console.log('Found crown leaves:', child.name);\n                crownLeavesObject = child;\n                const customMaterial = new THREE.ShaderMaterial({\n                  uniforms: {\n                    time: leavesUniforms.time,\n                    color: leavesUniforms.color,\n                    glowIntensity: leavesUniforms.glowIntensity\n                  },\n                  vertexShader: leavesGlowVertexShader,\n                  fragmentShader: leavesGlowFragmentShader,\n                  transparent: true,\n                  side: THREE.DoubleSide\n                });\n                child.material = customMaterial;\n              }\n              else if (child.name.match(\/Kaars\\.001_2\/i)) {\n                console.log('Found hovering object:', child.name);\n                hoveringObjects.push(child);\n              }\n              else {\n                if (child.material) {\n                  if (scene.environment) {\n                    child.material.envMap = scene.environment;\n                    child.material.envMapIntensity = 1.5;\n                    child.material.needsUpdate = true;\n                  }\n                  if (child.material.map) {\n                    child.material.map.anisotropy = renderer.capabilities.getMaxAnisotropy();\n                  }\n                }\n              }\n            }\n          });\n          \n          \/\/ If floor not found by name, try to find by position\/size\n          if (!floorObject) {\n            console.log('Floor object not found by name, trying to detect by properties...');\n            \n            model.traverse(function (child) {\n              if (child.isMesh) {\n                if (child.name.toLowerCase().includes('plane') || \n                    child.name.toLowerCase().includes('ground') || \n                    child.name.toLowerCase().includes('floor')) {\n                  floorObject = child;\n                  console.log('Found floor-like object by name:', child.name);\n                }\n              }\n            });\n            \n            if (!floorObject) {\n              model.traverse(function (child) {\n                if (child.isMesh) {\n                  const box = new THREE.Box3().setFromObject(child);\n                  const size = box.getSize(new THREE.Vector3());\n                  \n                  if (size.x > 5 && size.z > 5 && size.y < 1) {\n                    floorObject = child;\n                    console.log('Detected possible floor object by size:', child.name, size);\n                  }\n                }\n              });\n            }\n            \n            if (!floorObject) {\n              console.log('Creating temporary floor plane');\n              const tempFloor = new THREE.Mesh(\n                new THREE.PlaneGeometry(100, 100),\n                new THREE.MeshStandardMaterial({\n                  color: 0x333366,\n                  roughness: 0.5,\n                  metalness: 0.3,\n                  transparent: false,\n                  side: THREE.DoubleSide\n                })\n              );\n              tempFloor.rotation.x = -Math.PI \/ 2;\n              tempFloor.position.y = 0;\n              tempFloor.name = 'tempFloor';\n              scene.add(tempFloor);\n              floorObject = tempFloor;\n            }\n          }\n          \n          hoveringObjects.forEach(obj => {\n            obj.userData.originalY = obj.position.y;\n            obj.userData.originalRotY = obj.rotation.y;\n          });\n          \n          scene.add(model);\n          document.getElementById('loading').style.display = 'none';\n          \n          setupAudio();\n          \n          if (scene.environment) {\n            model.traverse(function(child) {\n              if (child.isMesh && child.material) {\n                if (!(child.material instanceof THREE.ShaderMaterial)) {\n                  child.material.envMap = scene.environment;\n                  child.material.envMapIntensity = 1.5;\n                  child.material.needsUpdate = true;\n                }\n              }\n            });\n          }\n        },\n        function (xhr) {\n          const loadingText = document.getElementById('loading');\n          if (xhr.total > 0) {\n            loadingText.textContent = `Loading model: ${Math.floor(xhr.loaded \/ xhr.total * 100)}%`;\n          } else {\n            loadingText.textContent = `Loading model...`;\n          }\n        },\n        function (error) {\n          console.error('Error loading model:', error);\n          document.getElementById('loading').textContent = 'Error loading model!';\n        }\n      );\n\n      \/\/ Create actual stars\n      const createActualStars = () => {\n        const starCount = isMobile ? 10000 : 20000;\n        const starGeometry = new THREE.BufferGeometry();\n        const starPositions = new Float32Array(starCount * 3);\n        const starSizes = new Float32Array(starCount);\n        const starColors = new Float32Array(starCount * 3);\n        \n        for (let i = 0; i < starCount; i++) {\n          const theta = Math.random() * Math.PI * 2;\n          const phi = Math.random() * Math.PI;\n          const radius = 800 + Math.random() * 200;\n          \n          starPositions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);\n          starPositions[i * 3 + 1] = radius * Math.cos(phi);\n          starPositions[i * 3 + 2] = radius * Math.sin(phi) * Math.sin(theta);\n          \n          starSizes[i] = Math.random() * 22.0 + 120.5;\n          \n          const colorType = Math.random();\n          if (colorType < 0.26) {\n            starColors[i * 3] = 1.0;\n            starColors[i * 3 + 1] = 1.0;\n            starColors[i * 3 + 2] = 1.0;\n          } else if (colorType < 0.48) {\n            starColors[i * 3] = 0.8;\n            starColors[i * 3 + 1] = 0.9;\n            starColors[i * 3 + 2] = 1.0;\n          } else {  \n            starColors[i * 3] = 1.0;\n            starColors[i * 3 + 1] = 0.95;\n            starColors[i * 3 + 2] = 0.8;\n          }\n        }\n        \n        starGeometry.setAttribute('position', new THREE.BufferAttribute(starPositions, 3));\n        starGeometry.setAttribute('size', new THREE.BufferAttribute(starSizes, 1));\n        starGeometry.setAttribute('color', new THREE.BufferAttribute(starColors, 3));\n        \n        const starMaterial = new THREE.ShaderMaterial({\n          uniforms: {\n            time: { value: 0 },\n            pixelRatio: { value: window.devicePixelRatio }\n          },\n          vertexShader: `\n            uniform float time;\n            uniform float pixelRatio;\n            attribute float size;\n            attribute vec3 color;\n            varying vec3 vColor;\n            varying float vTwinkle;\n            \n            void main() {\n              vColor = color;\n              vTwinkle = 0.6 + 0.4 * sin(time + position.x * 0.2 + position.y * 0.1 + position.z * 0.15);\n              vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\n              gl_PointSize = size * vTwinkle * pixelRatio * (40.0 \/ -mvPosition.z);\n              gl_Position = projectionMatrix * mvPosition;\n            }\n          `,\n          fragmentShader: `\n            varying vec3 vColor;\n            varying float vTwinkle;\n            \n            void main() {\n              vec2 center = gl_PointCoord - 0.5;\n              float dist = length(center);\n              float brightness = 1.0 - smoothstep(0.1, 0.5, dist);\n              \n              float rays = 0.0;\n              if (dist > 0.2) {\n                rays = 0.05 * (\n                  pow(abs(sin(atan(center.y, center.x) * 4.0)), 10.0) +\n                  pow(abs(sin(atan(center.y, center.x) * 5.0)), 12.0)\n                );\n              }\n              \n              brightness = brightness + rays * vTwinkle;\n              \n              if (brightness < 0.1) discard;\n              \n              gl_FragColor = vec4(vColor * vTwinkle, brightness * vTwinkle);\n            }\n          `,\n          transparent: true,\n          depthWrite: false,\n          blending: THREE.AdditiveBlending\n        });\n        \n        const stars = new THREE.Points(starGeometry, starMaterial);\n        scene.add(stars);\n        \n        return { stars, material: starMaterial };\n      };\n      \n      const actualStars = createActualStars();\n      \n      \/\/ Resize handler\n      window.addEventListener('resize', function () {\n        const newWidth = window.innerWidth;\n        const newHeight = window.innerHeight;\n        \n        camera.aspect = newWidth \/ newHeight;\n        camera.updateProjectionMatrix();\n        renderer.setSize(newWidth, newHeight);\n        renderer.domElement.style.width = '100vw';\n        renderer.domElement.style.height = '100vh';\n        if (composer) {\n          composer.setSize(newWidth, newHeight);\n        }\n      });\n      \n      \/\/ Animation loop\n      let prevTime = performance.now();\n      \n      function animate() {\n        requestAnimationFrame(animate);\n        \n        const time = performance.now();\n        const delta = (time - prevTime) \/ 1000;\n        \n        actualStars.material.uniforms.time.value += delta; \n        \n        shaderUniforms.time.value += delta;\n        universumUniforms.time.value += delta;\n        constellationUniforms.time.value += delta;\n        swanUniforms.time.value += delta;\n        leavesUniforms.time.value += delta;\n        fireMaterial.uniforms.time.value += delta;\n        \n        const candleFlicker = 0.8 + 0.4 * Math.sin(time * 0.01) + 0.2 * Math.sin(time * 0.027);\n        candleLight.intensity = 2 * candleFlicker;\n        \n        \/\/ Apply same flickering to upper light with slight phase offset\n        const upperFlicker = 0.8 + 0.4 * Math.sin(time * 0.01 + 0.5) + 0.2 * Math.sin(time * 0.027 + 0.3);\n        upperLight.intensity = 2 * upperFlicker;\n        \n        animateHoveringObjects(time * 0.001);\n        \n        \/\/ Move camera to target position with easing\n        if (isMovingToTarget) {\n          movementProgress += delta \/ movementDuration;\n          \n          if (movementProgress >= 1.0) {\n            camera.position.x = cameraTarget.x;\n            camera.position.y = cameraTarget.y;\n            camera.position.z = cameraTarget.z;\n            isMovingToTarget = false;\n            movementProgress = 0;\n          } else {\n            const t = easeInOutCubic(movementProgress);\n            camera.position.x = camera.position.x + (cameraTarget.x - camera.position.x) * t * 0.1;\n            camera.position.y = cameraTarget.y;\n            camera.position.z = camera.position.z + (cameraTarget.z - camera.position.z) * t * 0.1;\n          }\n        }\n        \n        \/\/ Update OrbitControls on mobile\n        if (isMobile && controls && controls.update) {\n          controls.update();\n        }\n        \n        prevTime = time;\n        \n        if (composer) {\n          composer.render();\n        } else {\n          renderer.render(scene, camera);\n        }\n      }\n      \n      animate();\n    } \/\/ End initializeUniverse function\n  <\/script>\n<\/div>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Loading Three.js&#8230; Universum Interactive Explorer Click to activate the experience Click on the floor to teleport to that location Press ESC to exit controls Tap to start exploring Drag to look around Pinch to zoom in\/out Tap the floor to move there Listen for the audio near the candle light! Click\/Tap anywhere to begin Drag [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"saved_in_kubio":false,"footnotes":""},"class_list":["post-1017","page","type-page","status-publish","hentry"],"kubio_ai_page_context":{"short_desc":"","purpose":"general"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Elementor #1017 - Universum Church<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/universum.church\/index.php\/elementor-1017\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Elementor #1017 - Universum Church\" \/>\n<meta property=\"og:description\" content=\"Loading Three.js&#8230; Universum Interactive Explorer Click to activate the experience Click on the floor to teleport to that location Press ESC to exit controls Tap to start exploring Drag to look around Pinch to zoom in\/out Tap the floor to move there Listen for the audio near the candle light! Click\/Tap anywhere to begin Drag [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/universum.church\/index.php\/elementor-1017\/\" \/>\n<meta property=\"og:site_name\" content=\"Universum Church\" \/>\n<meta property=\"article:modified_time\" content=\"2025-07-02T11:33:50+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/universum.church\/index.php\/elementor-1017\/\",\"url\":\"https:\/\/universum.church\/index.php\/elementor-1017\/\",\"name\":\"Elementor #1017 - Universum Church\",\"isPartOf\":{\"@id\":\"https:\/\/universum.church\/#website\"},\"datePublished\":\"2025-05-29T16:11:08+00:00\",\"dateModified\":\"2025-07-02T11:33:50+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/universum.church\/index.php\/elementor-1017\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/universum.church\/index.php\/elementor-1017\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/universum.church\/index.php\/elementor-1017\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Strona g\u0142\u00f3wna\",\"item\":\"https:\/\/universum.church\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Elementor #1017\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/universum.church\/#website\",\"url\":\"https:\/\/universum.church\/\",\"name\":\"Universum Church\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/universum.church\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/universum.church\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/universum.church\/#organization\",\"name\":\"Universum Church\",\"url\":\"https:\/\/universum.church\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/universum.church\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/universum.church\/wp-content\/uploads\/2025\/04\/cropped-3-1-3.jpg\",\"contentUrl\":\"https:\/\/universum.church\/wp-content\/uploads\/2025\/04\/cropped-3-1-3.jpg\",\"width\":512,\"height\":512,\"caption\":\"Universum Church\"},\"image\":{\"@id\":\"https:\/\/universum.church\/#\/schema\/logo\/image\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Elementor #1017 - Universum Church","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/universum.church\/index.php\/elementor-1017\/","og_locale":"en_US","og_type":"article","og_title":"Elementor #1017 - Universum Church","og_description":"Loading Three.js&#8230; Universum Interactive Explorer Click to activate the experience Click on the floor to teleport to that location Press ESC to exit controls Tap to start exploring Drag to look around Pinch to zoom in\/out Tap the floor to move there Listen for the audio near the candle light! Click\/Tap anywhere to begin Drag [&hellip;]","og_url":"https:\/\/universum.church\/index.php\/elementor-1017\/","og_site_name":"Universum Church","article_modified_time":"2025-07-02T11:33:50+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/universum.church\/index.php\/elementor-1017\/","url":"https:\/\/universum.church\/index.php\/elementor-1017\/","name":"Elementor #1017 - Universum Church","isPartOf":{"@id":"https:\/\/universum.church\/#website"},"datePublished":"2025-05-29T16:11:08+00:00","dateModified":"2025-07-02T11:33:50+00:00","breadcrumb":{"@id":"https:\/\/universum.church\/index.php\/elementor-1017\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/universum.church\/index.php\/elementor-1017\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/universum.church\/index.php\/elementor-1017\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Strona g\u0142\u00f3wna","item":"https:\/\/universum.church\/"},{"@type":"ListItem","position":2,"name":"Elementor #1017"}]},{"@type":"WebSite","@id":"https:\/\/universum.church\/#website","url":"https:\/\/universum.church\/","name":"Universum Church","description":"","publisher":{"@id":"https:\/\/universum.church\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/universum.church\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/universum.church\/#organization","name":"Universum Church","url":"https:\/\/universum.church\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/universum.church\/#\/schema\/logo\/image\/","url":"https:\/\/universum.church\/wp-content\/uploads\/2025\/04\/cropped-3-1-3.jpg","contentUrl":"https:\/\/universum.church\/wp-content\/uploads\/2025\/04\/cropped-3-1-3.jpg","width":512,"height":512,"caption":"Universum Church"},"image":{"@id":"https:\/\/universum.church\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/pages\/1017","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/comments?post=1017"}],"version-history":[{"count":130,"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/pages\/1017\/revisions"}],"predecessor-version":[{"id":1182,"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/pages\/1017\/revisions\/1182"}],"wp:attachment":[{"href":"https:\/\/universum.church\/index.php\/wp-json\/wp\/v2\/media?parent=1017"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}