diff --git a/apps/web/src/components/ui/lazy-component/index.ts b/apps/web/src/components/ui/lazy-component/index.ts
index e57e79d73..80d8cbc1c 100644
--- a/apps/web/src/components/ui/lazy-component/index.ts
+++ b/apps/web/src/components/ui/lazy-component/index.ts
@@ -53,4 +53,5 @@ export {
LazyDistribution,
LazyEducation,
LazySupport,
+ LazyLanding,
} from './lazyExports';
diff --git a/apps/web/src/components/ui/lazy-component/lazyExports.ts b/apps/web/src/components/ui/lazy-component/lazyExports.ts
index a0c3d4399..708aaad25 100644
--- a/apps/web/src/components/ui/lazy-component/lazyExports.ts
+++ b/apps/web/src/components/ui/lazy-component/lazyExports.ts
@@ -351,3 +351,9 @@ export const LazySupport = createLazyComponent(
undefined,
'Support',
);
+// Pre-launch landing page
+export const LazyLanding = createLazyComponent(
+ () => import('@/features/landing/pages/LandingPage'),
+ undefined,
+ 'Landing',
+);
diff --git a/apps/web/src/features/landing/pages/LandingPage.tsx b/apps/web/src/features/landing/pages/LandingPage.tsx
new file mode 100644
index 000000000..60b8acbc9
--- /dev/null
+++ b/apps/web/src/features/landing/pages/LandingPage.tsx
@@ -0,0 +1,648 @@
+import { useState, useRef } from 'react';
+import { Link } from 'react-router-dom';
+import { motion, useInView } from 'framer-motion';
+import {
+ Mic,
+ Shield,
+ Users,
+ ArrowRight,
+ Check,
+ Wrench,
+ Eye,
+ Lock,
+ Music,
+ Globe,
+ Heart,
+} from 'lucide-react';
+
+/* ═══════════════════════════════════════════════════════════════════
+ TALAS LANDING PAGE — Pre-launch
+ Aesthetic: Sumi-e ink wash (墨の濃淡) — scroll unfurling
+ ═══════════════════════════════════════════════════════════════════ */
+
+const inkReveal = {
+ hidden: { opacity: 0, y: 20, filter: 'blur(8px)' },
+ visible: (i: number) => ({
+ opacity: 1,
+ y: 0,
+ filter: 'blur(0px)',
+ transition: { delay: i * 0.12, duration: 0.7, ease: [0.25, 0.1, 0.25, 1] },
+ }),
+};
+
+const brushStroke = {
+ hidden: { scaleX: 0, originX: 0 },
+ visible: {
+ scaleX: 1,
+ transition: { duration: 0.8, ease: [0.33, 1, 0.68, 1] },
+ },
+};
+
+function Section({ children, className = '' }: { children: React.ReactNode; className?: string }) {
+ const ref = useRef(null);
+ const inView = useInView(ref, { once: true, margin: '-60px' });
+ return (
+
+ {children}
+
+ );
+}
+
+export default function LandingPage() {
+ const [email, setEmail] = useState('');
+ const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+ const [errorMsg, setErrorMsg] = useState('');
+
+ const handleSubscribe = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!email || !email.includes('@')) return;
+ setStatus('loading');
+ try {
+ const res = await fetch('/api/v1/newsletter/subscribe', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ email }),
+ });
+ if (!res.ok) {
+ const data = await res.json().catch(() => null);
+ throw new Error(data?.error?.message || 'Subscription failed');
+ }
+ setStatus('success');
+ setEmail('');
+ } catch (err) {
+ setStatus('error');
+ setErrorMsg(err instanceof Error ? err.message : 'An error occurred');
+ setTimeout(() => setStatus('idle'), 4000);
+ }
+ };
+
+ return (
+
+ {/* ═══ ATMOSPHERE — Ink wash background ═══ */}
+
+
+ {/* Ink drip streaks */}
+
+
+
+
+
+ {/* Ghost kanji */}
+
音
+
響
+
創
+
真
+
+
+ {/* ═══ NAVIGATION ═══ */}
+
+
+
+
+ {/* ═══ HERO ═══ */}
+
+
+ {/* Hanko seal */}
+
+
+
+ T
+
+
+
+
+ {/* Title */}
+
+ TALAS
+
+
+
+
+
+ Matériel audio professionnel — ouvert, réparable, transparent.
+
+ Plateforme musicale éthique — sans tracking, sans algorithme.
+
+
+
+ Lancement bientôt — Rejoins les premiers
+
+
+ {/* Email capture — hero */}
+
+ {status === 'success' ? (
+
+
+
+ Inscription confirmée. À bientôt.
+
+
+ ) : (
+ <>
+ setEmail(e.target.value)}
+ placeholder="ton@email.com"
+ required
+ aria-label="Adresse email"
+ className="flex-1 w-full sm:w-auto px-4 py-2.5 bg-[var(--sumi-surface-card)] border border-[var(--sumi-border-default)] rounded-sm text-sm text-[var(--sumi-text-primary)] placeholder:text-[var(--sumi-text-disabled)] focus:outline-none focus:border-[var(--sumi-accent)]/50 focus:shadow-[var(--sumi-shadow-glow)] transition-all"
+ style={{ fontFamily: 'var(--sumi-font-body)' }}
+ />
+
+ {status === 'loading' ? (
+
+ ) : (
+ <>
+ REJOINDRE
+
+ >
+ )}
+
+ >
+ )}
+
+
+ {status === 'error' && (
+ {errorMsg}
+ )}
+
+ {/* Scroll hint */}
+
+
+
+ DÉCOUVRIR
+
+
+
+
+
+ {/* ═══ VALUES — Three pillars ═══ */}
+
+
+
+
+ 三つの柱
+
+
+ Trois engagements
+
+
+
+
+
+ {[
+ {
+ icon: Wrench,
+ kanji: '開',
+ title: 'Hardware Ouvert',
+ desc: 'Schémas publiés sous licence CERN-OHL. Tu peux construire, réparer et améliorer chaque composant. Pas d\'obsolescence programmée.',
+ accent: 'var(--sumi-accent)',
+ },
+ {
+ icon: Shield,
+ kanji: '守',
+ title: 'Plateforme Éthique',
+ desc: 'Zéro tracking comportemental. Zéro algorithme de manipulation. Flux chronologique. Données privées. Code open-source (AGPL-3.0).',
+ accent: 'var(--sumi-sage)',
+ },
+ {
+ icon: Users,
+ kanji: '結',
+ title: 'Communauté Artiste',
+ desc: 'Streaming, marketplace, chat en temps réel, playlists collaboratives. Rémunération transparente. Les artistes contrôlent leur musique.',
+ accent: 'var(--sumi-kin)',
+ },
+ ].map((card, i) => (
+
+ {/* Ghost kanji in card */}
+
+ {card.kanji}
+
+
+
+
+
+
+
+ {card.title}
+
+
+
+ {card.desc}
+
+
+ ))}
+
+
+
+
+ {/* ═══ PRODUCT TEASER — Condenser microphone ═══ */}
+
+
+
+ {/* Microphone illustration — abstract */}
+
+
+ {/* Ink wash backdrop */}
+
+ {/* Microphone silhouette */}
+
+ {/* Gold accent corner */}
+
+
+ {/* Seal */}
+
+ T
+
+
+
+
+ {/* Product details */}
+
+
+ Premier produit
+
+
+
+ Microphone Condensateur
+
+
+
+
+
+ Large diaphragme. Préampli OPA1642. Corps aluminium usiné.
+ Schémas publiés, composants standards, guide de réparation inclus dans la boîte.
+ 30 à 40% moins cher que les concurrents — sans compromis sur la qualité.
+
+
+
+ {[
+ { icon: Eye, text: 'Schémas KiCAD publiés — CERN-OHL-W' },
+ { icon: Wrench, text: 'Réparable — pas de colle, composants standards' },
+ { icon: Globe, text: 'Fabriqué en France — sourcing documenté' },
+ { icon: Lock, text: '120-180 € — transparence totale des coûts' },
+ ].map((item) => (
+
+
+ {item.text}
+
+ ))}
+
+
+
+
+ ÊTRE NOTIFIÉ DU LANCEMENT
+
+
+
+
+
+
+
+
+ {/* ═══ PLATFORM TEASER — Veza ═══ */}
+
+
+ {/* ═══ EMAIL CAPTURE — Bottom CTA ═══ */}
+
+
+
+ 待
+
+
+
+ Rejoins les premiers
+
+
+
+
+
+ Inscris-toi pour être notifié du lancement.
+ Pas de spam — un seul email le jour J.
+
+
+
+ {status === 'success' ? (
+
+
+
+ C'est noté. Merci.
+
+
+ ) : (
+ <>
+ setEmail(e.target.value)}
+ placeholder="ton@email.com"
+ required
+ aria-label="Adresse email pour notification de lancement"
+ className="flex-1 w-full sm:w-auto px-4 py-2.5 bg-[var(--sumi-surface-card)] border border-[var(--sumi-border-default)] rounded-sm text-sm text-[var(--sumi-text-primary)] placeholder:text-[var(--sumi-text-disabled)] focus:outline-none focus:border-[var(--sumi-accent)]/50 focus:shadow-[var(--sumi-shadow-glow)] transition-all"
+ style={{ fontFamily: 'var(--sumi-font-body)' }}
+ />
+
+ {status === 'loading' ? (
+
+ ) : (
+ <>
+ NOTIFIER MOI
+
+ >
+ )}
+
+ >
+ )}
+
+
+ {status === 'error' && (
+
{errorMsg}
+ )}
+
+
+
+ {/* ═══ FOOTER ═══ */}
+
+
+ );
+}
diff --git a/apps/web/src/router/routeConfig.tsx b/apps/web/src/router/routeConfig.tsx
index 2b61f72ec..63b33355f 100644
--- a/apps/web/src/router/routeConfig.tsx
+++ b/apps/web/src/router/routeConfig.tsx
@@ -51,6 +51,7 @@ import {
LazyDistribution,
LazyEducation,
LazySupport,
+ LazyLanding,
} from '@/components/ui/LazyComponent';
import { PublicRoute } from './PublicRoute';
import { ProtectedLayoutRoute } from './ProtectedLayoutRoute';
@@ -87,6 +88,7 @@ export function getPublicRoutes(): RouteEntry[] {
export function getPublicStandaloneRoutes(): RouteEntry[] {
return [
+ { path: '/launch', element: },
{ path: '/design-system', element: },
{ path: '/u/:username', element: },
{