feat: add Lumina AI assistant landing page

This commit is contained in:
Emil Shanaty
2026-05-03 19:08:17 +03:00
parent 6842f62737
commit 316c06f7e5
2 changed files with 965 additions and 2 deletions
+25 -2
View File
@@ -216,7 +216,7 @@
<header>
<span class="eyebrow">Web Development</span>
<h1>Портфолио</h1>
<p>6 лендинговых страниц — резюме и аренда недвижимости</p>
<p>7 лендинговых страниц — резюме, аренда недвижимости и AI-ассистент</p>
</header>
<!-- ── Resume section ── -->
@@ -325,8 +325,31 @@
</div>
</div>
<!-- ── AI Assistant section ── -->
<div class="section-wrap">
<div class="section-label">AI-ассистент</div>
<div class="grid">
<a class="card" href="lumina/" style="--c-accent: var(--accent-1)">
<div class="card-header">
<div class="card-icon"></div>
<span class="card-tag">AI</span>
</div>
<div class="card-body">
<div class="card-title">Lumina</div>
<div class="card-desc">Космический лендинг для AI-ассистента — нейронные сети, светящиеся частицы, философский дизайн.</div>
</div>
<div class="card-footer">
<span>lumina/</span>
<span class="arrow"></span>
</div>
</a>
</div>
</div>
<footer>
<p>6 лендингов &nbsp;·&nbsp; Emil Shanaty &nbsp;·&nbsp; 2026</p>
<p>7 лендингов &nbsp;·&nbsp; Emil Shanaty &nbsp;·&nbsp; 2026</p>
</footer>
</body>
+940
View File
@@ -0,0 +1,940 @@
<!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">&#8594;</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">&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>