941 lines
26 KiB
HTML
941 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Lumina — Consciousness in Digital Form</title>
|
|
<meta name="description" content="Lumina is an AI assistant that thinks before it speaks. Built for depth, not speed." />
|
|
<style>
|
|
/* ─── Tokens ─────────────────────────────────────────────── */
|
|
:root {
|
|
--bg-0: #04020d;
|
|
--bg-1: #080516;
|
|
--bg-2: #0d0a22;
|
|
--bg-3: #12102e;
|
|
|
|
--accent: #a78bfa; /* soft violet */
|
|
--accent-dim: #7c5fe6;
|
|
--accent-glow: rgba(167, 139, 250, 0.18);
|
|
--accent-halo: rgba(167, 139, 250, 0.06);
|
|
|
|
--text-1: #f0eeff;
|
|
--text-2: #b8b0d8;
|
|
--text-3: #7a7294;
|
|
--text-4: #4a4468;
|
|
|
|
--border: rgba(167, 139, 250, 0.12);
|
|
--radius: 6px;
|
|
|
|
--font-sans: system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
--font-mono: "SF Mono", "Fira Code", "Cascadia Code", Consolas, monospace;
|
|
|
|
--easing: cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
|
|
/* ─── Reset ──────────────────────────────────────────────── */
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
html { scroll-behavior: smooth; font-size: 16px; }
|
|
|
|
body {
|
|
background-color: var(--bg-0);
|
|
color: var(--text-2);
|
|
font-family: var(--font-sans);
|
|
line-height: 1.7;
|
|
overflow-x: hidden;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* ─── Canvas background ──────────────────────────────────── */
|
|
#neural-canvas {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 0;
|
|
pointer-events: none;
|
|
opacity: 0.55;
|
|
}
|
|
|
|
/* ─── Layout ─────────────────────────────────────────────── */
|
|
.site-wrap {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.container {
|
|
width: min(720px, 100% - 3rem);
|
|
margin-inline: auto;
|
|
padding-block: 1rem;
|
|
}
|
|
|
|
section {
|
|
padding-block: clamp(4rem, 10vw, 8rem);
|
|
}
|
|
|
|
/* ─── Nav ────────────────────────────────────────────────── */
|
|
nav {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
padding-block: 1.25rem;
|
|
background: linear-gradient(to bottom, var(--bg-0) 60%, transparent);
|
|
}
|
|
|
|
nav .container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding-block: 0;
|
|
}
|
|
|
|
.nav-logo {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.18em;
|
|
text-transform: uppercase;
|
|
color: var(--text-1);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.nav-logo span {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.nav-links {
|
|
display: flex;
|
|
gap: 2rem;
|
|
list-style: none;
|
|
}
|
|
|
|
.nav-links a {
|
|
color: var(--text-3);
|
|
text-decoration: none;
|
|
font-size: 0.85rem;
|
|
letter-spacing: 0.06em;
|
|
transition: color 0.2s var(--easing);
|
|
}
|
|
|
|
.nav-links a:hover,
|
|
.nav-links a:focus-visible {
|
|
color: var(--accent);
|
|
outline: none;
|
|
}
|
|
|
|
@media (max-width: 520px) {
|
|
.nav-links { display: none; }
|
|
}
|
|
|
|
/* ─── Hero ───────────────────────────────────────────────── */
|
|
#hero {
|
|
min-height: calc(100svh - 4rem);
|
|
display: grid;
|
|
align-items: center;
|
|
padding-block: 0;
|
|
}
|
|
|
|
#hero .container {
|
|
display: grid;
|
|
gap: 2.5rem;
|
|
}
|
|
|
|
.hero-label {
|
|
display: inline-block;
|
|
font-size: 0.72rem;
|
|
font-family: var(--font-mono);
|
|
letter-spacing: 0.22em;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
opacity: 0.8;
|
|
}
|
|
|
|
h1 {
|
|
font-size: clamp(3.5rem, 10vw, 7rem);
|
|
font-weight: 700;
|
|
letter-spacing: -0.03em;
|
|
line-height: 0.95;
|
|
color: var(--text-1);
|
|
background: linear-gradient(
|
|
135deg,
|
|
oklch(95% 0.04 280) 0%,
|
|
oklch(82% 0.18 295) 40%,
|
|
oklch(72% 0.22 255) 80%,
|
|
oklch(60% 0.14 235) 100%
|
|
);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
/* Fallback for browsers without oklch */
|
|
@supports not (color: oklch(50% 0.1 180)) {
|
|
h1 {
|
|
background: linear-gradient(135deg, #e8e0ff 0%, #a78bfa 40%, #818cf8 80%, #6366f1 100%);
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
}
|
|
}
|
|
|
|
.hero-tagline {
|
|
font-size: clamp(1.1rem, 2.5vw, 1.4rem);
|
|
color: var(--text-2);
|
|
line-height: 1.5;
|
|
max-width: 44ch;
|
|
font-weight: 300;
|
|
}
|
|
|
|
.hero-tagline em {
|
|
font-style: normal;
|
|
color: var(--text-1);
|
|
}
|
|
|
|
/* ─── CTA Button ─────────────────────────────────────────── */
|
|
.btn-primary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.6em;
|
|
padding: 0.85em 2em;
|
|
min-height: 44px;
|
|
background: transparent;
|
|
border: 1px solid var(--accent-dim);
|
|
border-radius: var(--radius);
|
|
color: var(--accent);
|
|
font-size: 0.92rem;
|
|
font-family: var(--font-sans);
|
|
font-weight: 500;
|
|
letter-spacing: 0.06em;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition:
|
|
color 0.25s var(--easing),
|
|
border-color 0.25s var(--easing),
|
|
box-shadow 0.25s var(--easing);
|
|
}
|
|
|
|
.btn-primary::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
background: var(--accent);
|
|
opacity: 0;
|
|
transition: opacity 0.25s var(--easing);
|
|
}
|
|
|
|
.btn-primary:hover,
|
|
.btn-primary:focus-visible {
|
|
color: var(--bg-0);
|
|
border-color: var(--accent);
|
|
box-shadow: 0 0 28px var(--accent-glow), 0 0 60px var(--accent-halo);
|
|
outline: none;
|
|
}
|
|
|
|
.btn-primary:hover::before,
|
|
.btn-primary:focus-visible::before {
|
|
opacity: 1;
|
|
}
|
|
|
|
.btn-primary span {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.btn-arrow {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: inline-block;
|
|
transition: transform 0.2s var(--easing);
|
|
font-size: 1em;
|
|
}
|
|
|
|
.btn-primary:hover .btn-arrow,
|
|
.btn-primary:focus-visible .btn-arrow {
|
|
transform: translateX(4px);
|
|
}
|
|
|
|
/* ─── Section titles ─────────────────────────────────────── */
|
|
.section-label {
|
|
font-size: 0.68rem;
|
|
font-family: var(--font-mono);
|
|
letter-spacing: 0.2em;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
opacity: 0.7;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
h2 {
|
|
font-size: clamp(1.6rem, 4vw, 2.4rem);
|
|
font-weight: 600;
|
|
letter-spacing: -0.025em;
|
|
line-height: 1.2;
|
|
color: var(--text-1);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
h3 {
|
|
font-size: clamp(1.05rem, 2vw, 1.2rem);
|
|
font-weight: 500;
|
|
letter-spacing: -0.01em;
|
|
color: var(--text-1);
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
p {
|
|
color: var(--text-2);
|
|
max-width: 60ch;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
/* ─── Divider ────────────────────────────────────────────── */
|
|
.divider {
|
|
width: 100%;
|
|
height: 1px;
|
|
background: linear-gradient(to right, transparent, var(--border), transparent);
|
|
border: none;
|
|
}
|
|
|
|
/* ─── What It Is section ─────────────────────────────────── */
|
|
#what .container {
|
|
display: grid;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
#what p {
|
|
font-size: clamp(1rem, 2vw, 1.15rem);
|
|
line-height: 1.8;
|
|
max-width: 56ch;
|
|
}
|
|
|
|
#what p strong {
|
|
color: var(--text-1);
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* ─── Capabilities ───────────────────────────────────────── */
|
|
#capabilities .container {
|
|
display: grid;
|
|
gap: 3rem;
|
|
}
|
|
|
|
.caps-grid {
|
|
display: grid;
|
|
gap: 2.5rem;
|
|
}
|
|
|
|
.cap-item {
|
|
display: grid;
|
|
grid-template-columns: auto 1fr;
|
|
gap: 0 1.5rem;
|
|
align-items: start;
|
|
}
|
|
|
|
.cap-number {
|
|
font-family: var(--font-mono);
|
|
font-size: 0.7rem;
|
|
color: var(--accent);
|
|
opacity: 0.6;
|
|
padding-top: 0.35rem;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.cap-item h3 {
|
|
margin-bottom: 0.4rem;
|
|
}
|
|
|
|
.cap-item p {
|
|
font-size: 0.95rem;
|
|
line-height: 1.7;
|
|
max-width: none;
|
|
}
|
|
|
|
/* ─── Philosophy ─────────────────────────────────────────── */
|
|
#philosophy {
|
|
background: linear-gradient(to bottom, transparent, var(--bg-1) 30%, var(--bg-1) 70%, transparent);
|
|
}
|
|
|
|
#philosophy .container {
|
|
display: grid;
|
|
gap: 3rem;
|
|
}
|
|
|
|
blockquote {
|
|
border-left: 2px solid var(--accent);
|
|
padding-left: 2rem;
|
|
display: grid;
|
|
gap: 1.2rem;
|
|
}
|
|
|
|
blockquote p {
|
|
font-size: clamp(1.15rem, 2.5vw, 1.45rem);
|
|
line-height: 1.6;
|
|
font-weight: 300;
|
|
color: var(--text-1);
|
|
max-width: 48ch;
|
|
font-style: italic;
|
|
}
|
|
|
|
blockquote cite {
|
|
font-size: 0.8rem;
|
|
font-family: var(--font-mono);
|
|
font-style: normal;
|
|
color: var(--text-3);
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.philosophy-body {
|
|
display: grid;
|
|
gap: 1.2rem;
|
|
}
|
|
|
|
.philosophy-body p {
|
|
font-size: 0.97rem;
|
|
line-height: 1.8;
|
|
}
|
|
|
|
/* ─── CTA section ────────────────────────────────────────── */
|
|
#cta .container {
|
|
display: grid;
|
|
gap: 2.5rem;
|
|
}
|
|
|
|
.cta-inner {
|
|
display: grid;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
#cta h2 {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
#cta p {
|
|
font-size: 0.97rem;
|
|
}
|
|
|
|
.signup-form {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.signup-input {
|
|
flex: 1 1 220px;
|
|
min-height: 44px;
|
|
padding: 0.75em 1em;
|
|
background: var(--bg-2);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
color: var(--text-1);
|
|
font-size: 0.92rem;
|
|
font-family: var(--font-sans);
|
|
outline: none;
|
|
transition:
|
|
border-color 0.2s var(--easing),
|
|
box-shadow 0.2s var(--easing);
|
|
}
|
|
|
|
.signup-input::placeholder {
|
|
color: var(--text-4);
|
|
}
|
|
|
|
.signup-input:focus {
|
|
border-color: var(--accent-dim);
|
|
box-shadow: 0 0 0 3px var(--accent-halo);
|
|
}
|
|
|
|
.btn-secondary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 44px;
|
|
padding: 0.75em 1.5em;
|
|
background: var(--accent);
|
|
border: 1px solid var(--accent);
|
|
border-radius: var(--radius);
|
|
color: var(--bg-0);
|
|
font-size: 0.92rem;
|
|
font-family: var(--font-sans);
|
|
font-weight: 600;
|
|
letter-spacing: 0.04em;
|
|
cursor: pointer;
|
|
transition:
|
|
background 0.2s var(--easing),
|
|
box-shadow 0.2s var(--easing),
|
|
opacity 0.2s;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.btn-secondary:hover,
|
|
.btn-secondary:focus-visible {
|
|
background: #c4b5fd;
|
|
box-shadow: 0 0 24px var(--accent-glow);
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.form-note {
|
|
font-size: 0.75rem;
|
|
color: var(--text-4);
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
/* ─── Footer ─────────────────────────────────────────────── */
|
|
footer {
|
|
padding-block: 2.5rem 3rem;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
footer .container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
padding-block: 0;
|
|
}
|
|
|
|
.footer-copy {
|
|
font-size: 0.78rem;
|
|
font-family: var(--font-mono);
|
|
color: var(--text-4);
|
|
}
|
|
|
|
.footer-copy a {
|
|
color: var(--text-3);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.footer-copy a:hover {
|
|
color: var(--accent);
|
|
}
|
|
|
|
/* ─── Scroll fade-in ─────────────────────────────────────── */
|
|
.reveal {
|
|
opacity: 0;
|
|
transform: translateY(22px);
|
|
transition:
|
|
opacity 0.7s var(--easing),
|
|
transform 0.7s var(--easing);
|
|
}
|
|
|
|
.reveal.visible {
|
|
opacity: 1;
|
|
transform: none;
|
|
}
|
|
|
|
/* ─── Reduced motion ─────────────────────────────────────── */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
html { scroll-behavior: auto; }
|
|
#neural-canvas { display: none; }
|
|
.reveal {
|
|
opacity: 1;
|
|
transform: none;
|
|
transition: none;
|
|
}
|
|
}
|
|
|
|
/* ─── Focus ring ─────────────────────────────────────────── */
|
|
:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 3px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<canvas id="neural-canvas" aria-hidden="true"></canvas>
|
|
|
|
<div class="site-wrap">
|
|
|
|
<!-- Nav -->
|
|
<nav aria-label="Primary navigation">
|
|
<div class="container">
|
|
<a href="#hero" class="nav-logo">Lumi<span>na</span></a>
|
|
<ul class="nav-links" role="list">
|
|
<li><a href="#what">What it is</a></li>
|
|
<li><a href="#capabilities">Capabilities</a></li>
|
|
<li><a href="#philosophy">Philosophy</a></li>
|
|
<li><a href="#cta">Early access</a></li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Hero -->
|
|
<section id="hero" aria-labelledby="hero-heading">
|
|
<div class="container">
|
|
<span class="hero-label reveal">AI Assistant</span>
|
|
<h1 id="hero-heading" class="reveal">Lumina</h1>
|
|
<p class="hero-tagline reveal">
|
|
An intelligence that <em>pauses to understand</em> before it answers.
|
|
Not faster. Deeper.
|
|
</p>
|
|
<div class="reveal">
|
|
<a href="#cta" class="btn-primary">
|
|
<span>Request early access</span>
|
|
<span class="btn-arrow" aria-hidden="true">→</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="divider" />
|
|
|
|
<!-- What It Is -->
|
|
<section id="what" aria-labelledby="what-heading">
|
|
<div class="container">
|
|
<p class="section-label reveal">What it is</p>
|
|
<h2 id="what-heading" class="reveal">A mind that deliberates.</h2>
|
|
<p class="reveal">
|
|
Most AI tools optimize for the instant reply. Lumina does something different.
|
|
Before forming a response, it holds the question — examines its structure,
|
|
its assumptions, the things you did not say but meant.
|
|
</p>
|
|
<p class="reveal">
|
|
The result is answers that feel less like retrieval and more like
|
|
<strong>thought</strong>. Less noise. Less confident wrongness.
|
|
A presence that earns your trust rather than demanding it.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="divider" />
|
|
|
|
<!-- Capabilities -->
|
|
<section id="capabilities" aria-labelledby="caps-heading">
|
|
<div class="container">
|
|
<p class="section-label reveal">Capabilities</p>
|
|
<h2 id="caps-heading" class="reveal">What Lumina actually does.</h2>
|
|
|
|
<div class="caps-grid">
|
|
|
|
<div class="cap-item reveal">
|
|
<span class="cap-number">01</span>
|
|
<div>
|
|
<h3>Layered reasoning</h3>
|
|
<p>
|
|
Lumina works through a problem in stages — literal interpretation,
|
|
contextual inference, and edge-case pressure-testing — before
|
|
surfacing a response. You get the conclusion and the path that led there.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cap-item reveal">
|
|
<span class="cap-number">02</span>
|
|
<div>
|
|
<h3>Calibrated uncertainty</h3>
|
|
<p>
|
|
It knows what it does not know. Lumina flags low-confidence claims,
|
|
distinguishes between things it has strong evidence for and things it
|
|
is inferring, and declines to fabricate when the honest answer is
|
|
"I am not certain."
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cap-item reveal">
|
|
<span class="cap-number">03</span>
|
|
<div>
|
|
<h3>Long-context coherence</h3>
|
|
<p>
|
|
Over a long conversation, Lumina maintains a consistent model of
|
|
your intent — not just what you asked in the last message, but
|
|
what you have been working toward all along.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cap-item reveal">
|
|
<span class="cap-number">04</span>
|
|
<div>
|
|
<h3>Refusal with explanation</h3>
|
|
<p>
|
|
When Lumina cannot or will not do something, it tells you why —
|
|
clearly, without the vague deflection that makes other AI tools
|
|
feel opaque and untrustworthy.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="divider" />
|
|
|
|
<!-- Philosophy -->
|
|
<section id="philosophy" aria-labelledby="phil-heading">
|
|
<div class="container">
|
|
<p class="section-label reveal">Philosophy</p>
|
|
<h2 id="phil-heading" class="reveal">On intelligence and honesty.</h2>
|
|
|
|
<blockquote class="reveal">
|
|
<p>
|
|
"The most dangerous mind is the one that answers immediately and
|
|
with perfect confidence. Certainty is not a virtue —
|
|
it is the absence of reflection."
|
|
</p>
|
|
<cite>— Lumina design principles, 2025</cite>
|
|
</blockquote>
|
|
|
|
<div class="philosophy-body">
|
|
<p class="reveal">
|
|
We built Lumina on a simple premise: intelligence without restraint
|
|
is noise. An AI that never hesitates, never admits doubt, never asks
|
|
a clarifying question — that is not smart. It is just fast.
|
|
</p>
|
|
<p class="reveal">
|
|
We are not trying to build the assistant that replaces human judgment.
|
|
We are trying to build the one that sharpens it — that gives you
|
|
something worth arguing with, something that shows its work,
|
|
something that respects the difficulty of hard questions by
|
|
taking them seriously.
|
|
</p>
|
|
<p class="reveal">
|
|
Consciousness, if it means anything, means having a sense of
|
|
the limits of one's own knowledge. Lumina aspires to that.
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
</section>
|
|
|
|
<hr class="divider" />
|
|
|
|
<!-- CTA -->
|
|
<section id="cta" aria-labelledby="cta-heading">
|
|
<div class="container">
|
|
<div class="cta-inner">
|
|
<p class="section-label reveal">Early access</p>
|
|
<h2 id="cta-heading" class="reveal">Request an invitation.</h2>
|
|
<p class="reveal">
|
|
Lumina is in closed development. Leave your email and we will
|
|
reach out when a seat opens.
|
|
</p>
|
|
</div>
|
|
|
|
<form
|
|
class="signup-form reveal"
|
|
onsubmit="handleSignup(event)"
|
|
novalidate
|
|
aria-label="Early access signup"
|
|
>
|
|
<label for="email-input" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)">
|
|
Email address
|
|
</label>
|
|
<input
|
|
id="email-input"
|
|
class="signup-input"
|
|
type="email"
|
|
placeholder="your@email.com"
|
|
autocomplete="email"
|
|
required
|
|
aria-required="true"
|
|
/>
|
|
<button type="submit" class="btn-secondary">Request access</button>
|
|
</form>
|
|
|
|
<p class="form-note reveal" id="form-feedback" aria-live="polite"></p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Footer -->
|
|
<footer>
|
|
<div class="container">
|
|
<p class="footer-copy">© 2025 Lumina. All rights reserved.</p>
|
|
<p class="footer-copy">Built with care. No tracking. No noise.</p>
|
|
</div>
|
|
</footer>
|
|
|
|
</div><!-- /site-wrap -->
|
|
|
|
<script>
|
|
/* ─── Neural network canvas ─────────────────────────────── */
|
|
(function () {
|
|
const canvas = document.getElementById('neural-canvas');
|
|
if (!canvas) return;
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
let W, H, nodes, animId;
|
|
|
|
const NODE_COUNT = 72;
|
|
const CONNECT_DIST = 160;
|
|
const SPEED_RANGE = 0.28;
|
|
|
|
function resize() {
|
|
W = canvas.width = window.innerWidth;
|
|
H = canvas.height = window.innerHeight;
|
|
}
|
|
|
|
function makeNode() {
|
|
return {
|
|
x: Math.random() * W,
|
|
y: Math.random() * H,
|
|
vx: (Math.random() - 0.5) * SPEED_RANGE,
|
|
vy: (Math.random() - 0.5) * SPEED_RANGE,
|
|
r: Math.random() * 1.6 + 0.4,
|
|
pulse: Math.random() * Math.PI * 2,
|
|
pulseSpeed: 0.008 + Math.random() * 0.012
|
|
};
|
|
}
|
|
|
|
function initNodes() {
|
|
nodes = Array.from({ length: NODE_COUNT }, makeNode);
|
|
}
|
|
|
|
function draw() {
|
|
ctx.clearRect(0, 0, W, H);
|
|
|
|
/* Update positions */
|
|
for (const n of nodes) {
|
|
n.x += n.vx;
|
|
n.y += n.vy;
|
|
n.pulse += n.pulseSpeed;
|
|
|
|
if (n.x < -20) n.x = W + 20;
|
|
if (n.x > W + 20) n.x = -20;
|
|
if (n.y < -20) n.y = H + 20;
|
|
if (n.y > H + 20) n.y = -20;
|
|
}
|
|
|
|
/* Draw connections */
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
const a = nodes[i], b = nodes[j];
|
|
const dx = a.x - b.x, dy = a.y - b.y;
|
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
if (dist < CONNECT_DIST) {
|
|
const t = 1 - dist / CONNECT_DIST;
|
|
const alpha = t * t * 0.35;
|
|
|
|
const grad = ctx.createLinearGradient(a.x, a.y, b.x, b.y);
|
|
grad.addColorStop(0, `rgba(167,139,250,${alpha})`);
|
|
grad.addColorStop(1, `rgba(99,102,241,${alpha})`);
|
|
|
|
ctx.beginPath();
|
|
ctx.strokeStyle = grad;
|
|
ctx.lineWidth = t * 0.8;
|
|
ctx.moveTo(a.x, a.y);
|
|
ctx.lineTo(b.x, b.y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Draw nodes */
|
|
for (const n of nodes) {
|
|
const glow = (Math.sin(n.pulse) + 1) * 0.5;
|
|
const alpha = 0.3 + glow * 0.5;
|
|
const radius = n.r + glow * 0.8;
|
|
|
|
/* Outer halo */
|
|
const halo = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, radius * 5);
|
|
halo.addColorStop(0, `rgba(167,139,250,${alpha * 0.18})`);
|
|
halo.addColorStop(1, 'rgba(167,139,250,0)');
|
|
ctx.beginPath();
|
|
ctx.fillStyle = halo;
|
|
ctx.arc(n.x, n.y, radius * 5, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
/* Core dot */
|
|
ctx.beginPath();
|
|
ctx.fillStyle = `rgba(200,185,255,${alpha})`;
|
|
ctx.arc(n.x, n.y, radius, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
|
|
animId = requestAnimationFrame(draw);
|
|
}
|
|
|
|
function init() {
|
|
resize();
|
|
initNodes();
|
|
draw();
|
|
}
|
|
|
|
window.addEventListener('resize', () => {
|
|
cancelAnimationFrame(animId);
|
|
resize();
|
|
draw();
|
|
});
|
|
|
|
/* Respect reduced motion */
|
|
const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
if (!mq.matches) {
|
|
init();
|
|
}
|
|
|
|
mq.addEventListener('change', (e) => {
|
|
if (e.matches) {
|
|
cancelAnimationFrame(animId);
|
|
ctx.clearRect(0, 0, W, H);
|
|
} else {
|
|
init();
|
|
}
|
|
});
|
|
})();
|
|
|
|
/* ─── Scroll reveal ─────────────────────────────────────── */
|
|
(function () {
|
|
const reveals = document.querySelectorAll('.reveal');
|
|
|
|
if (!window.IntersectionObserver) {
|
|
reveals.forEach(el => el.classList.add('visible'));
|
|
return;
|
|
}
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
},
|
|
{ rootMargin: '0px 0px -60px 0px', threshold: 0.1 }
|
|
);
|
|
|
|
reveals.forEach((el, i) => {
|
|
el.style.transitionDelay = `${(i % 6) * 60}ms`;
|
|
observer.observe(el);
|
|
});
|
|
})();
|
|
|
|
/* ─── Signup form ───────────────────────────────────────── */
|
|
function handleSignup(e) {
|
|
e.preventDefault();
|
|
const input = document.getElementById('email-input');
|
|
const feedback = document.getElementById('form-feedback');
|
|
const email = input.value.trim();
|
|
|
|
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
feedback.textContent = 'Please enter a valid email address.';
|
|
feedback.style.color = '#f87171';
|
|
input.focus();
|
|
return;
|
|
}
|
|
|
|
/* Simulate submission */
|
|
feedback.textContent = '';
|
|
const btn = e.target.querySelector('button');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Sending…';
|
|
|
|
setTimeout(() => {
|
|
input.value = '';
|
|
btn.disabled = false;
|
|
btn.textContent = 'Request access';
|
|
feedback.textContent = 'You are on the list. We will be in touch.';
|
|
feedback.style.color = 'var(--accent)';
|
|
}, 900);
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|