591 lines
28 KiB
HTML
591 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="description" content="Moxiegen - Algorithmic enhancement for enterprise LLM and AI models. 10-100x faster inference and training.">
|
||
<title>Moxiegen • Our Algos, Your AI Advantage</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Space+Grotesk:wght@500;600&display=swap');
|
||
|
||
:root {
|
||
--tw-color-primary: #00f0c8;
|
||
}
|
||
|
||
* {
|
||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
|
||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||
transition-duration: 150ms;
|
||
}
|
||
|
||
.hero-bg {
|
||
background: radial-gradient(circle at 30% 20%, rgba(0, 240, 200, 0.15) 0%, transparent 70%),
|
||
radial-gradient(circle at 70% 80%, rgba(0, 240, 200, 0.1) 0%, transparent 70%);
|
||
}
|
||
|
||
/* SCROLL-DRIVEN ANIMATIONS */
|
||
.scroll-animate {
|
||
opacity: 0;
|
||
transform: translateY(60px);
|
||
transition: all 900ms cubic-bezier(0.25, 0.1, 0.25, 1);
|
||
}
|
||
|
||
.scroll-animate.visible {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
/* Staggered delays for benefit cards */
|
||
.scroll-animate.card-1 { transition-delay: 100ms; }
|
||
.scroll-animate.card-2 { transition-delay: 250ms; }
|
||
.scroll-animate.card-3 { transition-delay: 400ms; }
|
||
|
||
/* Navbar scroll effect */
|
||
.nav-scrolled {
|
||
background: rgba(15, 23, 42, 0.95);
|
||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
|
||
}
|
||
|
||
/* BALLOON + M STYLES (hero) */
|
||
.stage {
|
||
position: relative;
|
||
width: 620px; /* widened so the full M is visible */
|
||
height: 680px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
.glowing-m {
|
||
position: absolute;
|
||
z-index: 0;
|
||
filter: drop-shadow(0 0 20px rgba(224, 242, 254, 0.6));
|
||
top: 70px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
}
|
||
#canvas {
|
||
position: absolute;
|
||
z-index: 1;
|
||
background: transparent;
|
||
top: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
}
|
||
</style>
|
||
</head>
|
||
<body class="bg-slate-950 text-white font-sans">
|
||
<!-- NAVBAR -->
|
||
<nav id="navbar" class="border-b border-slate-800 bg-slate-950/80 backdrop-blur-lg sticky top-0 z-50">
|
||
<div class="max-w-screen-2xl mx-auto px-8 py-5 flex items-center justify-between">
|
||
<!-- Logo + Brand -->
|
||
<a href="index.html" class="flex items-center gap-x-3">
|
||
<img src="logo.svg"
|
||
alt="Moxiegen"
|
||
class="h-11 w-auto"
|
||
onerror="this.onerror=null; this.src='logo.png';">
|
||
<span class="font-semibold text-3xl tracking-[-1px] text-white" style="font-family: 'Space Grotesk', sans-serif;">Moxiegen</span>
|
||
</a>
|
||
|
||
<!-- Navigation -->
|
||
<div class="flex items-center gap-x-8 text-sm font-medium">
|
||
<a href="#benefits" class="hover:text-emerald-400">How It Works</a>
|
||
<a href="https://ai.moxiegen.com/" class="hover:text-emerald-400">Moxie Demo</a>
|
||
<a href="#licensing" class="hover:text-emerald-400">Licensing</a>
|
||
|
||
<!-- Auth buttons -->
|
||
<div id="authButtons" class="flex items-center gap-x-4">
|
||
<!-- Login button (shown when not authenticated) -->
|
||
<button id="loginBtn" onclick="login()" class="hidden px-6 py-2.5 bg-white text-slate-950 font-semibold rounded-3xl hover:bg-emerald-400 hover:text-white">
|
||
Login
|
||
</button>
|
||
|
||
<!-- Dashboard button (shown when authenticated) -->
|
||
<a id="dashboardBtn" href="dashboard.html" class="hidden px-6 py-2.5 bg-white text-slate-950 font-semibold rounded-3xl hover:bg-emerald-400 hover:text-white">
|
||
Dashboard
|
||
</a>
|
||
</div>
|
||
|
||
<button onclick="document.getElementById('contact').scrollIntoView({ behavior: 'smooth' })"
|
||
class="px-6 py-2.5 bg-white text-slate-950 font-semibold rounded-3xl hover:bg-emerald-400 hover:text-white">
|
||
Contact Us
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- HERO SECTION -->
|
||
<header class="hero-bg pt-16 pb-20">
|
||
<div class="max-w-screen-2xl mx-auto px-8">
|
||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
|
||
|
||
<!-- Left Text -->
|
||
<div class="lg:col-span-7 scroll-animate" id="hero-text">
|
||
<div class="inline-flex items-center gap-x-2 bg-slate-900 border border-emerald-400/30 text-emerald-400 text-sm font-medium px-5 py-2 rounded-3xl mb-6">
|
||
<div class="w-2 h-2 bg-emerald-400 rounded-full animate-pulse"></div>
|
||
NOW IN ENTERPRISE BETA
|
||
</div>
|
||
|
||
<h1 class="text-7xl lg:text-8xl font-semibold leading-none tracking-[-3px] mb-6" style="font-family: 'Space Grotesk', sans-serif;">
|
||
Our Algos,<br>your AI Advantage.
|
||
</h1>
|
||
|
||
<p class="text-2xl text-slate-300 max-w-2xl mb-8">
|
||
Moxiegen offers algorithmic enhancement of enterprise LLM and AI models,
|
||
increasing efficiency of both inference and training by <span class="text-emerald-400 font-semibold">10-100x</span>.
|
||
</p>
|
||
|
||
<div class="flex items-center gap-x-4">
|
||
<button onclick="document.getElementById('contact').scrollIntoView({ behavior: 'smooth' })"
|
||
class="px-10 py-5 bg-emerald-400 hover:bg-emerald-300 text-slate-950 text-xl font-semibold rounded-3xl flex items-center gap-x-3">
|
||
Get Moxie
|
||
<span class="text-3xl leading-none">→</span>
|
||
</button>
|
||
|
||
<a href="https://ai.moxiegen.com/"
|
||
class="px-8 py-5 border border-white/30 hover:border-white/60 text-xl font-medium rounded-3xl flex items-center">
|
||
Moxie Demo
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right: CUSTOM BALLOON + M (hero) – widened container only -->
|
||
<div class="lg:col-span-5 flex justify-center lg:justify-end scroll-animate" id="hero-logo">
|
||
<div class="stage">
|
||
<!-- THE M – widened SVG so edges are no longer clipped -->
|
||
<svg width="590" height="540" viewBox="0 0 500 500" class="glowing-m">
|
||
<text
|
||
x="50%" y="60%"
|
||
font-family="'Linux Libertine', serif"
|
||
font-size="600" font-weight="400"
|
||
text-anchor="middle" dominant-baseline="middle"
|
||
fill="#020617" stroke="#e0f2fe" stroke-width="3"
|
||
paint-order="stroke">
|
||
M
|
||
</text>
|
||
</svg>
|
||
<!-- THE BALLOON – unchanged -->
|
||
<canvas id="canvas" width="400" height="620"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- MOXY GO SECTION -->
|
||
<section id="moxy" class="py-20 border-b border-slate-800 scroll-animate">
|
||
<div class="max-w-screen-2xl mx-auto px-8">
|
||
<div class="flex flex-col md:flex-row items-center justify-between gap-8">
|
||
<div class="max-w-md">
|
||
<h2 class="text-5xl font-semibold tracking-[-1px] mb-3" style="font-family: 'Space Grotesk', sans-serif;">Moxie Demo.</h2>
|
||
<p class="text-slate-400 text-xl">Real-time performance leaderboard</p>
|
||
</div>
|
||
|
||
<!-- Leaderboard Placeholder -->
|
||
<div class="bg-slate-900 border border-slate-700 rounded-3xl p-8 max-w-2xl w-full md:w-auto scroll-animate">
|
||
<div class="flex items-center justify-between mb-6">
|
||
<div class="text-emerald-400 font-medium flex items-center gap-x-2">
|
||
<span class="text-2xl">🏆</span>
|
||
LIVE LEADERBOARD
|
||
</div>
|
||
<div class="text-xs uppercase tracking-widest bg-slate-800 text-slate-400 px-4 py-1 rounded-3xl">Updated 11 seconds ago</div>
|
||
</div>
|
||
|
||
<div class="text-center py-16 border border-dashed border-slate-600 rounded-3xl text-slate-400">
|
||
<p class="text-2xl mb-2">[Leaderboard Placement]</p>
|
||
<p class="text-sm">Insert your Moxie Go leaderboard table, chart, or screenshot here.<br>Current top models show 87x inference speedup.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- BENEFITS SECTION -->
|
||
<section id="benefits" class="py-20 bg-slate-900">
|
||
<div class="max-w-screen-2xl mx-auto px-8">
|
||
<div class="max-w-2xl mx-auto text-center mb-16 scroll-animate">
|
||
<h2 class="text-6xl font-semibold tracking-[-2px] mb-6 leading-none" style="font-family: 'Space Grotesk', sans-serif;">
|
||
Looks like your AI<br>could use some Moxie.
|
||
</h2>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||
<!-- Benefit 1 -->
|
||
<div class="scroll-animate card-1 bg-slate-950 border border-slate-800 rounded-3xl p-8 hover:border-emerald-400/30 group">
|
||
<div class="h-12 w-12 bg-emerald-400/10 text-emerald-400 rounded-2xl flex items-center justify-center text-3xl mb-6 group-hover:scale-110">🔄</div>
|
||
<h3 class="text-2xl font-semibold mb-3">Simultaneous Training + Inference</h3>
|
||
<p class="text-slate-400">Allows training data center GPU resources to simultaneously perform inference.</p>
|
||
</div>
|
||
|
||
<!-- Benefit 2 -->
|
||
<div class="scroll-animate card-2 bg-slate-950 border border-slate-800 rounded-3xl p-8 hover:border-emerald-400/30 group">
|
||
<div class="h-12 w-12 bg-emerald-400/10 text-emerald-400 rounded-2xl flex items-center justify-center text-3xl mb-6 group-hover:scale-110">🏭</div>
|
||
<h3 class="text-2xl font-semibold mb-3">Massive Offload</h3>
|
||
<p class="text-slate-400">Massively offloads inference data centers, freeing expensive GPU clusters for training workloads.</p>
|
||
</div>
|
||
|
||
<!-- Benefit 3 -->
|
||
<div class="scroll-animate card-3 bg-slate-950 border border-slate-800 rounded-3xl p-8 hover:border-emerald-400/30 group">
|
||
<div class="h-12 w-12 bg-emerald-400/10 text-emerald-400 rounded-2xl flex items-center justify-center text-3xl mb-6 group-hover:scale-110">📱</div>
|
||
<h3 class="text-2xl font-semibold mb-3">Native Consumer Inference</h3>
|
||
<p class="text-slate-400">Enables native LLM inference on consumer devices without any API calls or cloud dependency.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- LICENSING CTA -->
|
||
<section id="licensing" class="py-16 scroll-animate">
|
||
<div class="max-w-screen-2xl mx-auto px-8">
|
||
<div class="bg-gradient-to-r from-slate-900 to-slate-800 border border-emerald-400/20 rounded-3xl p-16 flex flex-col md:flex-row items-center justify-between gap-10">
|
||
<div class="max-w-lg">
|
||
<p class="text-4xl font-semibold leading-tight tracking-[-1px]">
|
||
Licensing now available through customized enterprise contracts.
|
||
</p>
|
||
</div>
|
||
<button onclick="document.getElementById('contact').scrollIntoView({ behavior: 'smooth' })"
|
||
class="flex-shrink-0 px-14 py-6 text-2xl font-semibold bg-white text-slate-950 hover:bg-emerald-400 rounded-3xl flex items-center gap-x-4">
|
||
Request Enterprise License
|
||
<span class="text-4xl">→</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- CONTACT SECTION -->
|
||
<section id="contact" class="py-24 bg-slate-900 scroll-animate">
|
||
<div class="max-w-screen-2xl mx-auto px-8">
|
||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
|
||
<div class="lg:col-span-5">
|
||
<h2 class="text-5xl font-semibold mb-6" style="font-family: 'Space Grotesk', sans-serif;">Ready for 10-100× more Moxie?</h2>
|
||
<p class="text-slate-400 text-2xl">Let's talk about how our algorithms can transform your LLM infrastructure.</p>
|
||
|
||
<div class="mt-12 border-l-4 border-emerald-400 pl-8">
|
||
<p class="font-medium">Email us at:</p>
|
||
<a href="mailto:info@moxiegen.com" class="block text-3xl font-semibold text-emerald-400 hover:text-white">info@moxiegen.com</a>
|
||
</div>
|
||
<div class="mt-12 border-l-4 border-emerald-400 pl-8">
|
||
<p class="font-medium">Call us at:</p>
|
||
<a href="tel:+18552466943" class="block text-3xl font-semibold text-emerald-400 hover:text-white">+1 (855) 246-6943</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="lg:col-span-7 bg-slate-950 rounded-3xl p-10">
|
||
<form id="contact-form" class="space-y-8">
|
||
|
||
<div class="grid grid-cols-2 gap-6">
|
||
<div>
|
||
<label class="block text-sm text-slate-400 mb-2">Your name</label>
|
||
<input type="text" name="name" placeholder="Jane Doe"
|
||
class="w-full bg-slate-900 border border-slate-700 focus:border-emerald-400 rounded-2xl px-6 py-5 outline-none text-lg" required>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm text-slate-400 mb-2">Company</label>
|
||
<input type="text" name="company" placeholder="Acme AI Labs"
|
||
class="w-full bg-slate-900 border border-slate-700 focus:border-emerald-400 rounded-2xl px-6 py-5 outline-none text-lg">
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm text-slate-400 mb-2">Work email</label>
|
||
<input type="email" name="email" placeholder="you@company.com"
|
||
class="w-full bg-slate-900 border border-slate-700 focus:border-emerald-400 rounded-2xl px-6 py-5 outline-none text-lg" required>
|
||
</div>
|
||
|
||
<div>
|
||
<label class="block text-sm text-slate-400 mb-2">Tell us about your use case</label>
|
||
<textarea name="message" rows="4" placeholder="We run 400 H100s and want to run inference + training simultaneously..."
|
||
class="w-full bg-slate-900 border border-slate-700 focus:border-emerald-400 rounded-3xl px-6 py-5 outline-none text-lg resize-none" required></textarea>
|
||
</div>
|
||
|
||
<button type="submit"
|
||
class="w-full py-6 bg-emerald-400 hover:bg-emerald-300 text-slate-950 font-semibold text-2xl rounded-3xl">
|
||
Send message to Moxiegen
|
||
</button>
|
||
</form>
|
||
|
||
<!-- Success message area -->
|
||
<div id="success-message" class="hidden mt-6 text-center py-4 bg-emerald-400/10 border border-emerald-400 rounded-3xl text-emerald-400 font-medium">
|
||
Message sent successfully! We'll get back to you soon.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- FOOTER -->
|
||
<footer class="bg-slate-950 border-t border-slate-800 py-8">
|
||
<div class="max-w-screen-2xl mx-auto px-8 flex flex-col md:flex-row justify-between items-center gap-4 text-slate-400 text-sm">
|
||
<div>© 2026, Moxiegen Business Group</div>
|
||
<div class="flex gap-x-6">
|
||
<a href="#" class="hover:text-white">Privacy</a>
|
||
<a href="#" class="hover:text-white">Terms</a>
|
||
<a href="#" class="hover:text-white">Legal</a>
|
||
</div>
|
||
<div class="text-emerald-400 text-xs font-medium">Built with Moxie</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<script>
|
||
// Tailwind initialization
|
||
function initializeTailwind() {
|
||
return { config(userConfig = {}) { return { content: [], theme: { extend: {} }, plugins: [], ...userConfig }; }, theme: { extend: {} } };
|
||
}
|
||
document.documentElement.setAttribute('data-tailwind-config', JSON.stringify(initializeTailwind()));
|
||
|
||
// SCROLL-DRIVEN ANIMATIONS (Intersection Observer)
|
||
function initScrollAnimations() {
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('visible');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, {
|
||
threshold: 0.15,
|
||
rootMargin: "0px 0px -50px 0px"
|
||
});
|
||
|
||
document.querySelectorAll('.scroll-animate').forEach(el => {
|
||
observer.observe(el);
|
||
});
|
||
|
||
// Navbar scroll effect
|
||
const navbar = document.getElementById('navbar');
|
||
let lastScrollY = window.scrollY;
|
||
|
||
window.addEventListener('scroll', () => {
|
||
if (Math.abs(window.scrollY - lastScrollY) > 5) {
|
||
if (window.scrollY > 100) {
|
||
navbar.classList.add('nav-scrolled');
|
||
} else {
|
||
navbar.classList.remove('nav-scrolled');
|
||
}
|
||
lastScrollY = window.scrollY;
|
||
}
|
||
});
|
||
}
|
||
|
||
// BALLOON + M ANIMATION (hero – still, no bobbing)
|
||
const canvas = document.getElementById('canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
const particles = [];
|
||
const NUM_PARTICLES = 275;
|
||
const MAX_RADIUS = 120;
|
||
const INTAKE_RADIUS = MAX_RADIUS / 8;
|
||
const DOME_HEIGHT = 0.33;
|
||
const BOTTOM_TOTAL_HEIGHT = 1.0 - DOME_HEIGHT;
|
||
const COMPACTED_BOTTOM_HEIGHT = BOTTOM_TOTAL_HEIGHT * 0.6;
|
||
const TOTAL_COMPACTED_HEIGHT = DOME_HEIGHT + COMPACTED_BOTTOM_HEIGHT;
|
||
|
||
const Y_OFFSET = 50 - 100;
|
||
const TOP_SHIFT = 70;
|
||
|
||
function getRadius(h) {
|
||
if (h < DOME_HEIGHT) return MAX_RADIUS * Math.sqrt(Math.max(0, 1 - Math.pow((h - DOME_HEIGHT) / DOME_HEIGHT, 2)));
|
||
if (h >= DOME_HEIGHT && h < TOTAL_COMPACTED_HEIGHT) {
|
||
const t = (h - DOME_HEIGHT) / COMPACTED_BOTTOM_HEIGHT;
|
||
return MAX_RADIUS - (MAX_RADIUS - INTAKE_RADIUS) * Math.pow(t, 1.5);
|
||
}
|
||
return INTAKE_RADIUS;
|
||
}
|
||
|
||
class Particle {
|
||
constructor(isAnchor = false, h = null, theta = null) {
|
||
this.isAnchor = isAnchor;
|
||
this.h = isAnchor ? h : Math.random() * TOTAL_COMPACTED_HEIGHT;
|
||
this.theta = isAnchor ? theta : Math.random() * Math.PI * 2;
|
||
this.speed = isAnchor ? 0 : 0.003 + Math.random() * 0.004;
|
||
}
|
||
}
|
||
|
||
function getPos(h, theta) {
|
||
const r = getRadius(h);
|
||
return { x: 200 + r * Math.cos(theta), y: Y_OFFSET + h * 380, z: Math.sin(theta) };
|
||
}
|
||
|
||
function drawBasket() {
|
||
const baseCenter = { x: 200, y: Y_OFFSET + TOTAL_COMPACTED_HEIGHT * 380 };
|
||
const ropeAttachY = baseCenter.y - 10.8;
|
||
const basketTopY = baseCenter.y + 20;
|
||
const w1 = 36;
|
||
const angleRad = 10 * Math.PI / 180;
|
||
const topXOffset = 22 * Math.cos(angleRad);
|
||
const bottomXOffset = (w1 / 2) - (w1 * 0.05);
|
||
|
||
ctx.shadowColor = '#e0f2fe';
|
||
ctx.shadowBlur = 8;
|
||
ctx.strokeStyle = 'rgba(224, 242, 254, 0.2)';
|
||
ctx.lineWidth = 1.5;
|
||
ctx.beginPath();
|
||
ctx.moveTo(baseCenter.x - topXOffset, ropeAttachY);
|
||
ctx.lineTo(baseCenter.x - bottomXOffset, basketTopY);
|
||
ctx.moveTo(baseCenter.x + topXOffset, ropeAttachY);
|
||
ctx.lineTo(baseCenter.x + bottomXOffset, basketTopY);
|
||
ctx.stroke();
|
||
|
||
ctx.shadowBlur = 10;
|
||
ctx.fillStyle = 'rgba(2, 6, 23, 0.9)';
|
||
ctx.strokeStyle = '#e0f2fe';
|
||
ctx.lineWidth = 1.5;
|
||
const bH = 8.5;
|
||
const widths = [w1, w1 * 0.9, (w1 * 0.9) * 0.9];
|
||
for (let i = 0; i < 3; i++) {
|
||
ctx.beginPath();
|
||
ctx.roundRect(baseCenter.x - widths[i]/2, basketTopY + (i * (bH + 2)), widths[i], bH, 3);
|
||
ctx.fill();
|
||
ctx.stroke();
|
||
}
|
||
ctx.shadowBlur = 0;
|
||
}
|
||
|
||
function initBalloon() {
|
||
particles.length = 0;
|
||
particles.push(new Particle(true, 0, 0));
|
||
for(let i = 0; i < 5; i++) particles.push(new Particle(true, TOTAL_COMPACTED_HEIGHT, (i / 5) * Math.PI * 2));
|
||
for (let i = 0; i < NUM_PARTICLES; i++) particles.push(new Particle());
|
||
animateBalloon();
|
||
}
|
||
|
||
function animateBalloon() {
|
||
ctx.clearRect(0, 0, 400, 620);
|
||
ctx.save();
|
||
ctx.translate(0, TOP_SHIFT);
|
||
|
||
particles.forEach(p => { if(!p.isAnchor) p.theta += p.speed; });
|
||
drawBasket();
|
||
|
||
ctx.lineWidth = 0.4;
|
||
for (let i = 0; i < particles.length; i++) {
|
||
for (let j = i + 1; j < particles.length; j++) {
|
||
const p1 = getPos(particles[i].h, particles[i].theta);
|
||
const p2 = getPos(particles[j].h, particles[j].theta);
|
||
if (Math.hypot(p1.x - p2.x, p1.y - p2.y) < 30) {
|
||
const alpha = 0.05 + (((p1.z + p2.z) / 2) + 1) * 0.05;
|
||
ctx.strokeStyle = `rgba(52, 211, 153, ${alpha})`;
|
||
ctx.beginPath();
|
||
ctx.moveTo(p1.x, p1.y);
|
||
ctx.lineTo(p2.x, p2.y);
|
||
ctx.stroke();
|
||
}
|
||
}
|
||
}
|
||
|
||
particles.forEach(p => {
|
||
const pos = getPos(p.h, p.theta);
|
||
ctx.fillStyle = `rgba(52, 211, 153, ${0.3 + (pos.z + 1) * 0.35})`;
|
||
ctx.beginPath();
|
||
ctx.arc(pos.x, pos.y, 1.2, 0, Math.PI * 2);
|
||
ctx.fill();
|
||
});
|
||
|
||
ctx.restore();
|
||
requestAnimationFrame(animateBalloon);
|
||
}
|
||
|
||
// ============================================
|
||
// AUTHENTICATION
|
||
// ============================================
|
||
async function initAuth() {
|
||
try {
|
||
await moxieAuth.init();
|
||
updateAuthUI();
|
||
} catch (error) {
|
||
console.error('Auth init error:', error);
|
||
document.getElementById('loginBtn').classList.remove('hidden');
|
||
}
|
||
}
|
||
|
||
function updateAuthUI() {
|
||
const loginBtn = document.getElementById('loginBtn');
|
||
const dashboardBtn = document.getElementById('dashboardBtn');
|
||
|
||
if (moxieAuth.isAuthenticated) {
|
||
loginBtn.classList.add('hidden');
|
||
dashboardBtn.classList.remove('hidden');
|
||
} else {
|
||
loginBtn.classList.remove('hidden');
|
||
dashboardBtn.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
async function login() {
|
||
try {
|
||
await moxieAuth.login(window.location.origin + '/dashboard.html');
|
||
} catch (error) {
|
||
console.error('Login error:', error);
|
||
alert('Failed to login. Please try again.');
|
||
}
|
||
}
|
||
|
||
// ============================================
|
||
// FORM SUBMIT HANDLER (Backend API)
|
||
// ============================================
|
||
function initContactForm() {
|
||
const form = document.getElementById('contact-form');
|
||
const successDiv = document.getElementById('success-message');
|
||
|
||
form.addEventListener('submit', async function(e) {
|
||
e.preventDefault();
|
||
|
||
const data = {
|
||
name: form.name.value.trim(),
|
||
email: form.email.value.trim(),
|
||
company: form.company.value.trim() || null,
|
||
message: form.message.value.trim()
|
||
};
|
||
|
||
if (!data.name || !data.email || !data.message) {
|
||
alert('Please fill in all required fields.');
|
||
return;
|
||
}
|
||
|
||
// Disable the submit button while sending
|
||
const btn = form.querySelector('button[type="submit"]');
|
||
const originalText = btn.textContent;
|
||
btn.disabled = true;
|
||
btn.textContent = 'Sending...';
|
||
|
||
try {
|
||
const response = await fetch('/api/contact', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(data)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (response.ok && result.success) {
|
||
successDiv.classList.remove('hidden');
|
||
form.reset();
|
||
|
||
setTimeout(() => {
|
||
successDiv.classList.add('hidden');
|
||
}, 6000);
|
||
} else {
|
||
alert(result.message || 'Something went wrong. Please try again.');
|
||
}
|
||
} catch (error) {
|
||
alert('Network error. Please check your connection and try again.');
|
||
} finally {
|
||
btn.disabled = false;
|
||
btn.textContent = originalText;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Initialize everything when page loads
|
||
window.addEventListener('load', () => {
|
||
initScrollAnimations();
|
||
initBalloon();
|
||
initAuth();
|
||
initContactForm();
|
||
});
|
||
</script>
|
||
|
||
<!-- Auth Scripts -->
|
||
<script src="js/config.js"></script>
|
||
<script src="js/auth.js"></script>
|
||
</body>
|
||
</html> |