1<!DOCTYPE html> 2<html lang="en"> 3<head> 4<meta charset="UTF-8"> 5<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6<title>AETERNO â Beyond Human Sight</title> 7<meta name="description" content="AETERNO Life â sovereign AI health monitoring. Enter the universe of prevention. Educational CDS â not a diagnosis. Zug, Switzerland."> 8<meta name="robots" content="index, follow"> 9<meta name="theme-color" content="#020208"> 10<meta name="mobile-web-app-capable" content="yes"> 11<meta name="apple-mobile-web-app-capable" content="yes"> 12<meta name="apple-mobile-web-app-title" content="AETERNO"> 13<link rel="canonical" href="https://aeternolife.ai/"> 14<link rel="manifest" href="/manifest.webmanifest"> 15<link rel="apple-touch-icon" href="/icon-192.png"> 16<meta property="og:title" content="AETERNO â Beyond Human Sight"> 17<meta property="og:description" content="Sovereign AI health monitoring. Enter the platform."> 18<meta property="og:url" content="https://aeternolife.ai/"> 19<meta property="og:image" content="https://storage.googleapis.com/aeterno-official/og-index.jpg"> 20<meta property="og:type" content="website"> 21<meta name="twitter:card" content="summary_large_image"> 22<meta name="twitter:title" content="AETERNO â Beyond Human Sight"> 23<meta name="twitter:image" content="https://storage.googleapis.com/aeterno-official/og-index.jpg"> 24<link rel="sitemap" type="application/xml" href="https://aeternolife.ai/sitemap.xml"> 25<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;1,300;1,400&family=Montserrat:wght@200;300;400&display=swap" rel="stylesheet"> 26<style> 27*,*::before,*::after{margin:0;padding:0;box-sizing:border-box} 28html,body{width:100%;height:100%;overflow:hidden;
28background:#020208;color:#f0ede6;font-family:'Montserrat',sans-serif;font-weight:300} 29 30.skip-link{ 31 position:absolute;left:12px;top:-44px;z-index:40; 32 background:#c9a84c;color:#04040A;padding:10px 12px;text-decoration:none; 33 font-size:11px;letter-spacing:1px; 34} 35.skip-link:focus{top:12px} 36.skip-btn:focus-visible,.skip-link:focus-visible{outline:2px solid #00d4be;outline-offset:2px} 37 38#space-canvas{position:fixed;inset:0;z-index:0} 39#warp-glow{ 40 position:fixed;inset:0;z-index:1;pointer-events:none; 41 background:radial-gradient(circle at 50% 50%, 42 rgba(255,255,255,0) 0%, 43 rgba(0,212,230,0) 18%, 44 transparent 55%); 45 opacity:0; 46 animation:warp-glow-build 9s ease-in forwards; 47} 48@keyframes warp-glow-build{ 49 0%,25%{opacity:0} 50 55%{opacity:0.35} 51 100%{opacity:0.85} 52} 53 54.nebula{ 55 position:fixed;inset:0;z-index:2;pointer-events:none; 56 background: 57 radial-gradient(ellipse 80% 60% at 20% 80%, rgba(0,212,190,0.07) 0%, transparent 55%), 58 radial-gradient(ellipse 60% 50% at 85% 15%, rgba(167,139,250,0.06) 0%, transparent 50%), 59 radial-gradient(ellipse 50% 40% at 50% 50%, rgba(0,212,230,0.08) 0%, transparent 60%); 60 animation:nebula-warp 9s ease-in forwards; 61} 62@keyframes nebula-warp{ 63 0%{opacity:0.5;transform:scale(1)} 64 50%{opacity:0.75;transform:scale(1.05)} 65 100%{opacity:1;transform:scale(1.35)} 66} 67 68.vignette{ 69 position:fixed;inset:0;z-index:3;pointer-events:none; 70 background:radial-gradient(ellipse at center, transparent 35%, rgba(2,2,8,0.88) 100%); 71 animation:vignette-tunnel 9s ease-in forwards; 72} 73@keyframes vignette-tunnel{ 74 0%{opacity:1} 75 100%{opacity:0.65} 76} 77 78.intro{ 79 position:fixed;inset:0;z-index:10; 80 display:flex;flex-direction:column;align-items:center;justify-content:center; 81 padding:40px 32px;text-align:center; 82 animation:intro-into-eternity 9s ease-in forwards; 83} 84@keyframes intro-into-eternity{ 85 0%,40%{transform:scale(1);filter:blur(0)} 86 75%{transform:scale(1.04);filter:blur(0)} 87 100%{transform:scale(1.14);filter:blur(1px)} 88} 89 90.qe-orbit-1 { animation: qe-spin 12s linear infinite; transform-origin: 50% 50%; } 91.qe-orbit-2 { animation: qe-spin 18s linear infinite reverse; transform-origin: 50% 50%; } 92.qe-orbit-3 { animation: qe-spin 8s linear infinite; transform-origin: 50% 50%; } 93.qe-pulse { animation: qe-glow 3s ease-in-out infinite; } 94.qe-scan { animation: qe-scanline 4s ease-in-out infinite; } 95@keyframes qe-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } 96@keyframes qe-glow { 0%,100% { opacity:0.7; } 50% { opacity:1; } } 97@keyframes qe-scanline { 0%,100% { opacity:0.15; } 50% { opacity:0.5; } } 98 99.eye-wrap{ 100 position:relative;width:120px;height:120px;margin-bottom:48px; 101 animation:eye-appear 1.8s ease both; 102} 103@keyframes eye-appear{ from{opacity:0;transform:scale(0.6)} to{opacity:1;transform:scale(1)} } 104.eye-ring{ 105 position:absolute;border-radius:50%;border:1px solid rgba(201,168,76,0.2); 106 animation:ring-pulse 3s ease-in-out infinite; 107} 108.eye-ring-1{inset:-20px} 109.eye-ring-2{inset:-36px;border-color:rgba(201,168,76,0.12);animation-delay:0.5s} 110.eye-ring-3{inset:-54px;border-color:rgba(201,168,76,0.06);animation-delay:1s} 111@keyframes ring-pulse{ 0%,100%{opacity:0.5;transform:scale(1)} 50%{opacity:1;transform:scale(1.03)} } 112.eye-core{position:absolute;inset:0;display:flex;align-items:center;justify-content:center} 113.eye-core svg{width:96px;height:96px} 114 115.wordmark{ 116 font-family:'Cormorant Garamond',serif; 117 font-size:clamp(28px,5vw,42px); 118 letter-spacing:clamp(12px,3vw,22px); 119 color:#c9a84c; 120 text-shadow:0 0 40px rgba(201,168,76,0.5),0 0 80px rgba(201,168,76,0.2); 121 margin-bottom:8px; 122 animation:wordmark-glow 2s ease 0.4s both; 123} 124@keyframes wordmark-glow{ from{opacity:0;letter-spacing:30px} to{opacity:1} } 125.tagline{ 126 font-size:8px;letter-spacing:5px;text-transform:uppercase; 127 color:rgba(201,168,76,0.45);margin-bottom:56px; 128 animation:fade-up 1.2s ease 0.8s both; 129} 130 131.vision{ 132 max-width:640px;min-height:120px; 133 display:flex;flex-direction:column;gap:16px;align-items:center; 134} 135.vision-line{ 136 font-family:'Cormorant Garamond',serif;
137 font-size:clamp(18px,3.5vw,28px); 138 font-weight:300;line-height:1.5; 139 color:rgba(240,237,230,0); 140 transform:translateY(12px); 141 transition:opacity 1s ease, transform 1s ease, color 1s ease; 142} 143.vision-line.visible{ opacity:1;transform:translateY(0);color:rgba(240,237,230,0.88); } 144.vision-line em{font-style:italic;color:#00d4be} 145.vision-line.gold{color:#c9a84c;opacity:0!important} 146.vision-line.gold.visible{opacity:1!important;font-size:clamp(22px,4vw,34px);letter-spacing:4px} 147 148.countdown-wrap{ 149 position:fixed;bottom:48px;left:50%;transform:translateX(-50%); 150 z-index:20;display:flex;flex-direction:column;align-items:center;gap:12px; 151 animation:fade-up 1s ease 1.2s both; 152} 153@keyframes fade-up{ 154 from{opacity:0;transform:translateX(-50%) translateY(16px)} 155 to{opacity:1;transform:translateX(-50%) translateY(0)} 156} 157.countdown-ring{position:relative;width:48px;height:48px} 158.countdown-ring svg{transform:rotate(-90deg)} 159.countdown-ring circle{fill:none;stroke-width:2} 160.countdown-track{stroke:rgba(201,168,76,0.15)} 161.countdown-progress{ 162 stroke:#c9a84c;stroke-linecap:round; 163 stroke-dasharray:113;stroke-dashoffset:0; 164 animation:countdown 9s linear forwards; 165 filter:drop-shadow(0 0 6px rgba(201,168,76,0.6)); 166} 167@keyframes countdown{ from{stroke-dashoffset:0} to{stroke-dashoffset:113} } 168.countdown-label{ 169 font-size:8px;letter-spacing:4px;text-transform:uppercase; 170 color:rgba(240,237,230,0.35); 171} 172.skip-btn{ 173 position:fixed;top:28px;right:32px;z-index:30; 174 background:transparent;border:1px solid rgba(201,168,76,0.2); 175 color:rgba(201,168,76,0.6);font-family:'Montserrat',sans-serif; 176 font-size:8px;letter-spacing:3px;text-transform:uppercase; 177 padding:10px 18px;cursor:pointer;transition:all 0.3s; 178} 179.skip-btn:hover{border-color:#c9a84c;color:#c9a84c;background:rgba(201,168,76,0.06)} 180 181.audio-hint{ 182 position:fixed;bottom:110px;left:50%;transform:translateX(-50%); 183 font-size:8px;letter-spacing:3px;text-transform:uppercase; 184 color:rgba(240,237,230,0.25);z-index:20; 185 opacity:0;transition:opacity 0.5s;pointer-events:none; 186} 187.audio-hint.show{opacity:1} 188 189.intro.warp-out{ animation:warp-out 0.7s ease-in forwards !important; } 190@keyframes warp-out{ to{opacity:0;transform:scale(2.2);filter:blur(14px)} } 191body.warp-out #space-canvas, 192body.warp-out #warp-glow{ animation:warp-burst 0.7s ease-in forwards; } 193body.warp-out .nebula{ animation:warp-burst 0.7s ease-in forwards; } 194@keyframes warp-burst{ to{transform:scale(2.8);opacity:0} } 195 196@media (prefers-reduced-motion: reduce){ 197 *,*::before,*::after{ 198 animation:none !important; 199 transition:none !important; 200 scroll-behavior:auto !important; 201 } 202} 203</style> 204<link rel="stylesheet" href="aeterno-access.css"> 205</head> 206<body> 207<a class="skip-link" href="/platform.html">Skip intro and open platform</a> 208 209<canvas id="space-canvas" aria-hidden="true"></canvas> 210<div id="warp-glow" aria-hidden="true"></div> 211<div class="nebula" aria-hidden="true"></div> 212<div class="vignette" aria-hidden="true"></div> 213 214<button class="skip-btn" id="skip-btn" type="button">Skip to Platform â</button> 215 216<div class="intro" id="intro"> 217 <div class="eye-wrap" aria-hidden="true"> 218 <div class="eye-ring eye-ring-1"></div> 219 <div class="eye-ring eye-ring-2"></div> 220 <div class="eye-ring eye-ring-3"></div> 221 <div class="eye-core"> 222 <svg viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 223 <circle cx="60" cy="60" r="56" stroke="#C9A84C" stroke-width="0.8" opacity="0.5"/> 224 <circle cx="60" cy="60" r="44" stroke="#C9A84C" stroke-width="0.4" opacity="0.25"/> 225 <g class="qe-orbit-1"><ellipse cx="60" cy="60" rx="48" ry="18" stroke="#C9A84C" stroke-width="0.7" opacity="0.55"/></g> 226 <g class="qe-orbit-2"><ellipse cx="60" cy="60" rx="48" ry="18" stroke="#C9A84C" stroke-width="0.5" opacity="0.35" transform="rotate(60 60 60)"/></g> 227 <g class="qe-orbit-3"><ellipse cx="60" cy="60" rx="48" ry="18" stroke="#C9A84C" stroke-width="0.4" opacity="0.25" transform="rotate(120 60 60)"/></g> 228 <line x1="4" y1="60" x2="116" y2="60" stroke="#C9A84C" stroke-width="0.4" opacity="0.2" class="qe-scan"/>
229 <line x1="60" y1="4" x2="60" y2="116" stroke="#C9A84C" stroke-width="0.4" opacity="0.15"/> 230 <circle cx="60" cy="60" r="10" stroke="#C9A84C" stroke-width="0.6" opacity="0.5"/> 231 <circle cx="60" cy="60" r="4" fill="#C9A84C" class="qe-pulse"/> 232 </svg> 233 </div> 234 </div> 235 236 <div class="wordmark">AETERNO</div> 237 <div class="tagline">Sovereign AI Health · Zug, Switzerland</div> 238 239 <div class="vision" id="vision" aria-live="polite"> 240 <p class="vision-line" data-delay="800">In the silence between heartbeats,<br>the body speaks.</p> 241 <p class="vision-line" data-delay="2200">AETERNO listens â <em>before symptoms arise.</em></p> 242 <p class="vision-line" data-delay="3800">Your health. Your data. Your sovereignty.</p> 243 <p class="vision-line gold" data-delay="5400">Beyond human sight. For life.</p> 244 </div> 245</div> 246 247<div class="countdown-wrap"> 248 <div class="countdown-ring" aria-hidden="true"> 249 <svg width="48" height="48" viewBox="0 0 48 48"> 250 <circle class="countdown-track" cx="24" cy="24" r="18"/> 251 <circle class="countdown-progress" cx="24" cy="24" r="18"/> 252 </svg> 253 </div> 254 <span class="countdown-label">Entering AETERNO</span> 255</div> 256 257<p class="audio-hint" id="audio-hint">Tap anywhere for sound</p> 258
259<script> 260(function () { 261 'use strict'; 262 263 const INTRO_MS = 9000; 264 const INTRO_REDUCED_MS = 5000; 265 const INTRO_DONE_KEY = 'aeterno_intro_v1'; 266 const INTRO_ATTEMPTS_KEY = 'aeterno_intro_attempts'; 267 let redirected = false; 268 const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 269 const params = new URLSearchParams(window.location.search); 270 const forceIntro = params.get('intro') === '1' || params.get('replay') === '1'; 271 272 /** Works on Google static hosting (platform.html) and FastAPI (/platform). */ 273 function getNextUrl() { 274 if (params.get('next') === 'console') return '/console.html'; 275 return '/platform.html?from=intro'; 276 } 277 278 function clearIntroFlags() { 279 try { 280 sessionStorage.removeItem(INTRO_DONE_KEY); 281 sessionStorage.removeItem(INTRO_ATTEMPTS_KEY); 282 } catch (_) { /* ignore */ } 283 } 284 285 if (forceIntro) clearIntroFlags(); 286 287 function showLoopHelp() { 288 document.body.innerHTML = 289 '<div style="min-height:100vh;display:flex;align-items:center;justify-content:center;padding:32px;
289background:#020208;color:#f0ede6;font-family:Montserrat,sans-serif;text-align:center">' + 290 '<div style="max-width:520px">' + 291 '<p style="font-size:11px;letter-spacing:0.2em;text-transform:uppercase;color:#00d4be;margin-bottom:16px">AETERNO</p>' + 292 '<h1 style="font-family:Cormorant Garamond,serif;font-size:28px;color:#c9a84c;font-weight:300;margin-bottom:16px">Opening platformâ¦</h1>' + 293 '<p style="font-size:14px;line-height:1.7;color:rgba(240,237,230,0.75);margin-bottom:24px">' + 294 'If this stays stuck, open the platform directly or clear this siteâs session and retry. ' + 295 'Make sure <code style="color:#c9a84c">platform.html</code> is uploaded to the <strong>aeternolife.ai</strong> bucket root (same place as index.html).</p>' + 296 '<p style="font-size:13px;display:flex;flex-direction:column;gap:12px;align-items:center">' + 297 '<a href="/platform.html?from=intro" style="color:#00d4be">Open /platform.html</a>' + 298 '<a href="/?intro=1" style="color:#c9a84c">Replay space intro</a>' + 299 '<button type="button" id="aeterno-clear-intro" style="background:#c9a84c;color:#020208;border:0;padding:12px 22px;letter-spacing:2px;text-transform:uppercase;font-size:10px;cursor:pointer">Clear & retry intro</button>' + 300 '</p>' + 301 '</div></div>'; 302 var btn = document.getElementById('aeterno-clear-intro'); 303 if (btn) { 304 btn.addEventListener('click', function () { 305 clearIntroFlags(); 306 window.location.replace('/?intro=1'); 307 }); 308 } 309 } 310 311 async function probePlatformThenHelp() { 312 try { 313 const res = await fetch('/platform.html?probe=' + Date.now(), { cache: 'no-store', redirect: 'follow' }); 314 const text = await res.text(); 315 const looksLikePlatform = /id=["']modules["']|id=["']ecosystem["']|hero-h1|Live Health Console/i.test(text); 316 const looksLikeIntro = /aeterno_intro_v1|space-canvas|Skip intro/i.test(text); 317 if (res.ok && looksLikePlatform && !looksLikeIntro) { 318 clearIntroFlags(); 319 window.location.replace(getNextUrl()); 320 return; 321 } 322 } catch (_) { /* fall through */ } 323 showLoopHelp(); 324 } 325 326 (function guardIntroLoop() { 327 // After a completed intro in this tab session, skip straight to platform â 328 // unless user explicitly asked to replay (?intro=1). 329 if (forceIntro) return; 330 const attempts = parseInt(sessionStorage.getItem(INTRO_ATTEMPTS_KEY) || '0', 10) + 1; 331 sessionStorage.setItem(INTRO_ATTEMPTS_KEY, String(attempts)); 332 if (sessionStorage.getItem(INTRO_DONE_KEY) === '1') { 333 if (attempts > 3) { 334 probePlatformThenHelp(); 335 return; 336 } 337 window.location.replace(getNextUrl()); 338 throw new Error('redirecting'); 339 } 340 })(); 341 342 function goToPlatform() { 343 if (redirected) return; 344 redirected = true; 345 sessionStorage.setItem(INTRO_DONE_KEY, '1'); 346 var introEl = document.getElementById('intro'); 347 if (introEl) introEl.classList.add('warp-out'); 348 document.body.classList.add('warp-out'); 349 stopAmbient(); 350 setTimeout(function () { window.location.replace(getNextUrl()); }, 550); 351 } 352 353 var skipBtn = document.getElementById('skip-btn'); 354 if (skipBtn) skipBtn.addEventListener('click', goToPlatform); 355 // Never use ~80ms â "reduce motion" OS setting was skipping the whole universe intro. 356 setTimeout(goToPlatform, reducedMotion ? INTRO_REDUCED_MS : INTRO_MS); 357 358 document.querySelectorAll('.vision-line').forEach((line) => { 359 const delay = parseInt(line.dataset.delay, 10) || 0; 360 setTimeout(() => line.classList.add('visible'), delay); 361 }); 362 363 const canvas = document.getElementById('space-canvas'); 364 const ctx = canvas.getContext('2d'); 365 let particles = [], w, h, cx, cy, maxR, animId; 366 const introStart = performance.now(); 367 const introDuration = reducedMotion ? INTRO_REDUCED_MS : INTRO_MS; 368 369 function warpProgress() { 370 return Math.min(1, (performance.now() - introStart) / introDuration); 371 } 372 373 function resize() { 374 w = canvas.width = window.innerWidth; 375 h = canvas.height = window.innerHeight; 376 cx = w / 2; cy = h / 2; 377 maxR = Math.hypot(cx, cy) * 1.15; 378 const count = Math.floor((w * h) / 900); 379 particles = Array.from({ length: count }, () =>
379 spawnParticle(true)); 380 } 381 382 function spawnParticle(scattered) { 383 const angle = Math.random() * Math.PI * 2; 384 const radius = scattered ? Math.random() * maxR : Math.random() * maxR * 0.08; 385 const depth = Math.random(); 386 return { angle, radius, depth, speed: 0.6 + depth * 1.4, size: 0.4 + depth * 1.8, cyan: Math.random() < 0.55 + depth * 0.25 }; 387 } 388 389 function drawWarp() { 390 const progress = warpProgress(); 391 const warp = 1 + progress * progress * 28; 392 const streak = 4 + progress * 22; 393 ctx.fillStyle = `rgba(2,2,8,${0.22 + progress * 0.35})`; 394 ctx.fillRect(0, 0, w, h); 395 const glowR = maxR * (0.12 + progress * 0.55); 396 const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, glowR); 397 glow.addColorStop(0, `rgba(255,255,255,${0.35 + progress * 0.55})`); 398 glow.addColorStop(0.12, `rgba(120,240,255,${0.25 + progress * 0.45})`); 399 glow.addColorStop(0.35, `rgba(0,180,220,${0.08 + progress * 0.2})`); 400 glow.addColorStop(1, 'rgba(2,2,8,0)'); 401 ctx.fillStyle = glow; 402 ctx.fillRect(0, 0, w, h); 403 particles.forEach((p) => { 404 const spd = p.speed * warp * (0.35 + p.depth * 0.9); 405 const prevR = p.radius; 406 p.radius += spd; 407 if (p.radius > maxR) Object.assign(p, spawnParticle(false)); 408 const x = cx + Math.cos(p.angle) * p.radius; 409 const y = cy + Math.sin(p.angle) * p.radius; 410 const px = cx + Math.cos(p.angle) * Math.max(0, prevR - streak * spd * 0.08); 411 const py = cy + Math.sin(p.angle) * Math.max(0, prevR - streak * spd * 0.08); 412 const alpha = (0.15 + p.depth * 0.75) * (1 - p.radius / maxR * 0.35); 413 if (progress > 0.2 && p.radius > 20) { 414 const grad = ctx.createLinearGradient(px, py, x, y); 415 grad.addColorStop(0, p.cyan ? 'rgba(0,212,230,0)' : 'rgba(255,255,255,0)'); 416 grad.addColorStop(1, p.cyan ? `rgba(80,240,255,${alpha})` : `rgba(240,237,230,${alpha * 0.9})`); 417 ctx.strokeStyle = grad; 418 ctx.lineWidth = p.size * (0.8 + progress * 2.2); 419 ctx.lineCap = 'round'; 420 ctx.beginPath(); 421 ctx.moveTo(px, py); 422 ctx.lineTo(x, y); 423 ctx.stroke(); 424 } else { 425 ctx.fillStyle = p.cyan ? `rgba(0,212,230,${alpha})` : `rgba(240,237,230,${alpha * 0.85})`; 426 ctx.beginPath(); 427 ctx.arc(x, y, p.size * (0.5 + progress), 0, Math.PI * 2); 428 ctx.fill(); 429 } 430 }); 431 animId = requestAnimationFrame(drawWarp); 432 } 433 434 window.addEventListener('resize', resize); 435 resize(); 436 drawWarp(); 437 438 let audioCtx = null; 439 let ambientNodes = []; 440 441 function startAmbient() { 442 if (audioCtx) { 443 if (audioCtx.state === 'suspended') audioCtx.resume(); 444 return; 445 } 446 try { 447 const AC = window.AudioContext || window.webkitAudioContext; 448 if (!AC) return; 449 audioCtx = new AC(); 450 const master = audioCtx.createGain(); 451 master.gain.setValueAtTime(0, audioCtx.currentTime); 452 master.gain.linearRampToValueAtTime(0.22, audioCtx.currentTime + 2); 453 master.connect(audioCtx.destination); 454 [55, 82.5, 110, 165].forEach((freq, i) => { 455 const osc = audioCtx.createOscillator(); 456 const gain = audioCtx.createGain(); 457 const lfo = audioCtx.createOscillator(); 458 const lfoGain = audioCtx.createGain(); 459 osc.type = i === 0 ? 'sine' : 'triangle'; 460 osc.frequency.value = freq; 461 gain.gain.value = 0.08 / (i + 1); 462 lfo.frequency.value = 0.05 + i * 0.02; 463 lfoGain.gain.value = 0.03; 464 lfo.connect(lfoGain); 465 lfoGain.connect(gain.gain); 466 osc.connect(gain); 467 gain.connect(master); 468 osc.start(); 469 lfo.start(); 470 ambientNodes.push(osc, lfo); 471 }); 472 const bufferSize = audioCtx.sampleRate * 2; 473 const noiseBuffer = audioCtx.createBuffer(1, bufferSize, audioCtx.sampleRate); 474 const data = noiseBuffer.getChannelData(0); 475 for (let i = 0; i < bufferSize; i++) data[i] = (Math.random() * 2 - 1) * 0.3; 476 const noise = audioCtx.createBufferSource(); 477 noise.buffer = noiseBuffer; 478 noise.loop = true; 479 const noiseFilter = audioCtx.createBiquadFilter(); 480 noiseFilter.type = 'lowpass'; 481 noiseFilter.frequency.value = 400; 482 const noiseGain = audioCtx.createGain(); 483 noiseGain.gain.value = 0.04; 484 noise.connect(noiseFilter); 485 noiseFilter.connect(noiseGain); 486 noiseGain.connect(master); 487 noise.start(); 488 ambientNodes.push(noise); 489 document.getElementById('audio-hint').classList.remove('show'); 490 } catch (_) {} 491 } 492 493 function stopAmbient() {
494 ambientNodes.forEach((n) => { try { n.stop(); } catch (_) {} }); 495 if (audioCtx) { audioCtx.close(); audioCtx = null; } 496 } 497 498 if (!reducedMotion) startAmbient(); 499 document.addEventListener('pointerdown', startAmbient, { once: true }); 500 setTimeout(() => { 501 if (!audioCtx || audioCtx.state === 'suspended') { 502 document.getElementById('audio-hint').classList.add('show'); 503 } 504 }, 800); 505 506 window.addEventListener('beforeunload', () => { 507 cancelAnimationFrame(animId); 508 stopAmbient(); 509 }); 510})(); 511</script>
vendor: 2 bytes, line 511
511
512<script src="aeterno-access.js"></script>
vendor: 2 bytes, line 512
512
513<script src="aeterno-pwa.js"></script>
vendor: 2 bytes, line 513
513
514<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"cddfe76281444a859168b3c4031e0968","r":1,"spa":2}' crossorigin="anonymous"></script>
514 515</body> 516</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.