Spaces:
Running
Running
| // Oracle Engine Front-end Logic | |
| // UTILITIES | |
| const $ = (sel, root = document) => root.querySelector(sel); | |
| const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel)); | |
| const clamp = (v, min, max) => Math.max(min, Math.min(max, v)); | |
| const rand = (min, max) => Math.random() * (max - min) + min; | |
| const pick = (arr) => arr[Math.floor(Math.random() * arr.length)]; | |
| // INIT FEATHERS, YEAR, COUNTER | |
| document.addEventListener('DOMContentLoaded', () => { | |
| if (window.feather) feather.replace(); | |
| $('#year').textContent = new Date().getFullYear(); | |
| initCounters(); | |
| initTheme(); | |
| initNav(); | |
| initOrbits(); | |
| initConceptEngine(); | |
| initSignupModal(); | |
| initNewsletter(); | |
| }); | |
| // THEME TOGGLE | |
| function initTheme() { | |
| const btn = $('#themeToggle'); | |
| // Default to dark mode; ensure class exists | |
| document.documentElement.classList.add('dark'); | |
| btn?.addEventListener('click', () => { | |
| const isDark = document.documentElement.classList.toggle('dark'); | |
| btn.innerHTML = isDark ? '<i data-feather="moon" class="w-4 h-4"></i>' : '<i data-feather="sun" class="w-4 h-4"></i>'; | |
| feather.replace(); | |
| }); | |
| } | |
| // MOBILE NAV | |
| function initNav() { | |
| const btn = $('#menuBtn'); | |
| const panel = $('#mobileNav'); | |
| btn?.addEventListener('click', () => { | |
| panel.classList.toggle('hidden'); | |
| }); | |
| // Close on navigation | |
| $$('#mobileNav a').forEach(a => a.addEventListener('click', () => panel.classList.add('hidden'))); | |
| } | |
| // ORBIT CANVAS BACKGROUND | |
| function initOrbits() { | |
| const canvas = $('#orbits'); | |
| const ctx = canvas.getContext('2d'); | |
| let width, height, dpr; | |
| let particles = []; | |
| let mouse = { x: 0, y: 0 }; | |
| function resize() { | |
| dpr = Math.min(2, window.devicePixelRatio || 1); | |
| width = canvas.width = Math.floor(window.innerWidth * dpr); | |
| height = canvas.height = Math.floor(window.innerHeight * dpr); | |
| canvas.style.width = window.innerWidth + 'px'; | |
| canvas.style.height = window.innerHeight + 'px'; | |
| particles = createParticles(Math.floor((width * height) / (18000 * dpr))); | |
| } | |
| function createParticles(count) { | |
| const arr = []; | |
| for (let i = 0; i < count; i++) { | |
| arr.push({ | |
| x: rand(0, width), | |
| y: rand(0, height), | |
| r: rand(0.6, 2.2) * dpr, | |
| dx: rand(-0.15, 0.15) * dpr, | |
| dy: rand(-0.15, 0.15) * dpr, | |
| hue: rand(190, 280), | |
| alpha: rand(0.25, 0.75), | |
| }); | |
| } | |
| return arr; | |
| } | |
| function step() { | |
| ctx.clearRect(0, 0, width, height); | |
| // subtle space glow | |
| const grd = ctx.createRadialGradient(width * 0.5, height * 0.4, 0, width * 0.5, height * 0.4, Math.max(width, height) * 0.8); | |
| grd.addColorStop(0, 'rgba(99,102,241,0.05)'); | |
| grd.addColorStop(1, 'rgba(6,182,212,0.02)'); | |
| ctx.fillStyle = grd; | |
| ctx.fillRect(0, 0, width, height); | |
| // connections | |
| for (let i = 0; i < particles.length; i++) { | |
| const p = particles[i]; | |
| p.x += p.dx; | |
| p.y += p.dy; | |
| if (p.x < 0 || p.x > width) p.dx *= -1; | |
| if (p.y < 0 || p.y > height) p.dy *= -1; | |
| // mouse interaction | |
| const mx = mouse.x * dpr, my = mouse.y * dpr; | |
| const dxm = p.x - mx, dym = p.y - my; | |
| const distm = Math.sqrt(dxm * dxm + dym * dym); | |
| const influence = clamp(140 * dpr - distm, 0, 140 * dpr) / (140 * dpr); | |
| p.x += (dxm / (distm || 1)) * influence * 0.4; | |
| p.y += (dym / (distm || 1)) * influence * 0.4; | |
| // draw particle | |
| ctx.beginPath(); | |
| ctx.fillStyle = `hsla(${p.hue}, 90%, 65%, ${p.alpha})`; | |
| ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // connections | |
| for (let j = i + 1; j < particles.length; j++) { | |
| const q = particles[j]; | |
| const dx = p.x - q.x; | |
| const dy = p.y - q.y; | |
| const dist = Math.sqrt(dx * dx + dy * dy); | |
| if (dist < 120 * dpr) { | |
| ctx.beginPath(); | |
| const a = clamp((120 * dpr - dist) / (120 * dpr), 0, 1) * 0.2; | |
| ctx.strokeStyle = `hsla(${p.hue}, 90%, 65%, ${a})`; | |
| ctx.lineWidth = 0.6 * dpr; | |
| ctx.moveTo(p.x, p.y); | |
| ctx.lineTo(q.x, q.y); | |
| ctx.stroke(); | |
| } | |
| } | |
| } | |
| requestAnimationFrame(step); | |
| } | |
| window.addEventListener('resize', resize); | |
| window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; }); | |
| resize(); | |
| step(); | |
| } | |
| // COUNTER ANIMATION | |
| function initCounters() { | |
| const el = $('#conceptCounter'); | |
| if (!el) return; | |
| const target = 24813; | |
| const duration = 1200; | |
| const start = performance.now(); | |
| function tick(t) { | |
| const p = clamp((t - start) / duration, 0, 1); | |
| const ease = 1 - Math.pow(1 - p, 3); | |
| el.textContent = Math.floor(target * ease).toLocaleString(); | |
| if (p < 1) requestAnimationFrame(tick); | |
| } | |
| requestAnimationFrame(tick); | |
| } | |
| // CONCEPT ENGINE DATA | |
| const CONCEPT_BANK = [ | |
| { | |
| title: 'Bio‑Responsive Hydration Platform', | |
| domains: ['Biotech', 'Wellness', 'Materials'], | |
| summary: 'A vessel and platform that modulates mineralization, pH, and micro‑dosage based on real‑time biomarkers and contextual signals.', | |
| bullets: [ | |
| 'Algae‑infused bio‑reactor keeps water oxygenated', | |
| 'Mineral composite equilibrates pH and electrolytes', | |
| 'DNA‑tagged capsules for personalized supplementation' | |
| ], | |
| timing: '2028–2032', | |
| readiness: 'Tech‑Adjacencies Converging', | |
| viability: 92, | |
| tags: ['Biotech', 'Sustainability', 'AI/ML'] | |
| }, | |
| { | |
| title: 'Neural‑Loom Adaptive Textiles', | |
| domains: ['Textiles', 'Haptics', 'Biometrics'], | |
| summary: 'Fabrics that sense physiology and environment to tune thermal, moisture, and tension—displaying biofeedback via chromic fibers.', | |
| bullets: [ | |
| 'Piezo‑fiber mesh for haptic comfort mapping', | |
| 'Biofeedback dyes shift with stress metrics', | |
| 'On‑thread compute for low‑latency responses' | |
| ], | |
| timing: '2027–2031', | |
| readiness: 'Advanced Prototypes', | |
| viability: 88, | |
| tags: ['Wellness', 'Haptics', 'Materials'] | |
| }, | |
| { | |
| title: 'Carbon‑Sequestering Architecture Blocks', | |
| domains: ['Construction', 'Climate Tech', 'Materials'], | |
| summary: 'Building blocks that sequester CO₂ over their lifecycle via mineral carbonation and self‑healing mycelium matrices.', | |
| bullets: [ | |
| 'Mycelium + mineral composite with self‑heal', | |
| 'On‑site carbon capture during curing', | |
| 'Modular units for rapid retrofitting' | |
| ], | |
| timing: '2029–2034', | |
| readiness: 'Pilots Active', | |
| viability: 85, | |
| tags: ['Climate', 'Construction', 'Circularity'] | |
| }, | |
| { | |
| title: 'Ambient Compute Orchestrator', | |
| domains: ['Edge AI', 'Spatial UX'], | |
| summary: 'An ambient layer that senses room intent, orchestrates devices, and suggests interventions across spaces.', | |
| bullets: [ | |
| 'Edge transformers for multi‑modal context', | |
| 'Acoustic + visual sensing fusion', | |
| 'Privacy‑first on‑device inference' | |
| ], | |
| timing: '2026–2030', | |
| readiness: 'Production Readiness', | |
| viability: 90, | |
| tags: ['AI/ML', 'Edge', 'UX'] | |
| }, | |
| { | |
| title: 'Oceanic Biomaterial Foundry', | |
| domains: ['Blue Tech', 'Biomimicry'], | |
| summary: 'Marine algae and bacteria cultivated to produce high‑performance biopolymers with tunable properties.', | |
| bullets: [ | |
| 'Programmable tensile strength and transparency', | |
| 'Fully biodegradable at end‑of‑life', | |
| 'Offshore cultivation reduces land use' | |
| ], | |
| timing: '2030–2036', | |
| readiness: 'Research Phase', | |
| viability: 78, | |
| tags: ['Biomaterials', 'Sustainability'] | |
| }, | |
| { | |
| title: 'Micro‑Mobility Swarm Interfaces', | |
| domains: ['Urban Systems', 'Mobility'], | |
| summary: 'City‑level orchestration for micro‑vehicles with real‑time lane optimization and safety predictions.', | |
| bullets: [ | |
| 'Dynamic curb allocation', | |
| 'Predictive conflict avoidance', | |
| 'Open API for city and operator apps' | |
| ], | |
| timing: '2027–2032', | |
| readiness: 'Early Pilots', | |
| viability: 83, | |
| tags: ['Mobility', 'Cities', 'AI/ML'] | |
| }, | |
| { | |
| title: 'TerraForm Design', | |
| domains: ['Industrial Design', 'Sustainability', 'Circular Economy'], | |
| summary: 'An industrial design service specializing in circular economy products, focusing on modularity, repairability, and using sustainable/recycled materials.', | |
| bullets: [ | |
| 'Modular product architecture for easy repair and upgrades', | |
| 'Lifecycle Optimization AI analyzes supply chain and end-of-life logistics', | |
| 'Recycled materials sourcing with carbon footprint tracking' | |
| ], | |
| timing: '2025–2029', | |
| readiness: 'Production Readiness', | |
| viability: 94, | |
| tags: ['Sustainability', 'Circular Economy', 'Industrial Design'] | |
| } | |
| ]; | |
| // CONCEPT ENGINE UI | |
| function initConceptEngine() { | |
| const grid = $('#conceptGrid'); | |
| const generateBtn = $('#generateBtn'); | |
| const generateBtnMobile = $('#generateBtnMobile'); | |
| function renderCard(item) { | |
| const el = document.createElement('article'); | |
| el.className = 'rounded-2xl border border-white/10 bg-white/5 p-6 card-hover'; | |
| el.innerHTML = ` | |
| <div class="flex items-center justify-between"> | |
| <div class="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-2 py-1 text-xs text-slate-300"> | |
| <span class="w-2 h-2 rounded-full bg-accent-500 animate-pulse"></span> | |
| ${item.timing} | |
| </div> | |
| <div class="text-xs text-slate-400">${item.readiness}</div> | |
| </div> | |
| <h3 class="mt-4 font-display text-xl">${item.title}</h3> | |
| <p class="mt-2 text-sm text-slate-300">${item.summary}</p> | |
| <div class="mt-3 flex flex-wrap gap-2"> | |
| ${item.tags.map(t => `<span class="rounded-lg bg-white/5 border border-white/10 px-2 py-1 text-[11px] text-slate-300">${t}</span>`).join('')} | |
| </div> | |
| <ul class="mt-4 grid gap-1.5 text-sm text-slate-300"> | |
| ${item.bullets.map(b => `<li class="flex items-start gap-2"><i data-feather="chevron-right" class="w-4 h-4 text-accent-400 mt-0.5"></i><span>${b}</span></li>`).join('')} | |
| </ul> | |
| <div class="mt-5"> | |
| <div class="flex items-center justify-between text-xs text-slate-400"> | |
| <span>Viability Index</span> | |
| <span>${item.viability}%</span> | |
| </div> | |
| <div class="h-1.5 rounded bg-white/10 overflow-hidden mt-1"> | |
| <div class="h-full bg-gradient-to-r from-accent-500 to-concept-cyan" style="width:${item.viability}%"></div> | |
| </div> | |
| </div> | |
| `; | |
| grid.appendChild(el); | |
| feather.replace(); | |
| } | |
| function renderAll() { | |
| grid.innerHTML = ''; | |
| const shuffled = [...CONCEPT_BANK].sort(() => Math.random() - 0.5).slice(0, 6); | |
| shuffled.forEach(renderCard); | |
| animateCountUp(); | |
| } | |
| function generateNew() { | |
| grid.classList.add('opacity-60', 'scale-[0.99]'); | |
| setTimeout(() => { | |
| renderAll(); | |
| grid.classList.remove('opacity-60', 'scale-[0.99]'); | |
| }, 220); | |
| } | |
| generateBtn?.addEventListener('click', generateNew); | |
| generateBtnMobile?.addEventListener('click', generateNew); | |
| renderAll(); | |
| } | |
| function animateCountUp() { | |
| const el = $('#conceptCounter'); | |
| if (!el) return; | |
| const start = parseInt(el.textContent.replace(/,/g, '')) || 0; | |
| const target = start + Math.floor(rand(5, 18)); | |
| const duration = 800; | |
| const t0 = performance.now(); | |
| function tick(t) { | |
| const p = clamp((t - t0) / duration, 0, 1); | |
| const ease = 1 - Math.pow(1 - p, 3); | |
| el.textContent = Math.floor(start + (target - start) * ease).toLocaleString(); | |
| if (p < 1) requestAnimationFrame(tick); | |
| } | |
| requestAnimationFrame(tick); | |
| } | |
| // SIGNUP MODAL | |
| function initSignupModal() { | |
| const modal = $('#signupModal'); | |
| const openBtn = $('#openSignup'); | |
| const closeBtn = $('#closeSignup'); | |
| const form = $('#signupForm'); | |
| const msg = $('#signupMsg'); | |
| const open = () => { modal.classList.remove('hidden'); modal.classList.add('modal-enter'); }; | |
| const close = () => { modal.classList.add('hidden'); modal.classList.remove('modal-enter'); msg.textContent = ''; }; | |
| openBtn?.addEventListener('click', open); | |
| closeBtn?.addEventListener('click', close); | |
| modal?.addEventListener('click', (e) => { if (e.target === modal) close(); }); | |
| form?.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| msg.textContent = 'Creating your workspace...'; | |
| setTimeout(() => { | |
| msg.textContent = 'Success! Check your email to confirm your account.'; | |
| setTimeout(close, 1000); | |
| }, 900); | |
| }); | |
| } | |
| // NEWSLETTER | |
| function initNewsletter() { | |
| const form = $('#newsletter'); | |
| const msg = $('#newsletterMsg'); | |
| form?.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| msg.textContent = 'Subscribed! Welcome to the loop.'; | |
| form.reset(); | |
| }); | |
| } | |
| // ACCESSIBILITY: close modals with Escape | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape') { | |
| $('#mobileNav')?.classList.add('hidden'); | |
| const modal = $('#signupModal'); | |
| if (modal && !modal.classList.contains('hidden')) modal.classList.add('hidden'); | |
| } | |
| }); |