'use client' import { useState, useEffect, useRef, useMemo, useCallback, Fragment } from 'react' import { Language } from '@/lib/types' import GradientText from '../ui/GradientText' import FadeInView from '../ui/FadeInView' interface MilestonesSlideProps { lang: Language } const MONO: React.CSSProperties = { fontFamily: '"JetBrains Mono","SF Mono",ui-monospace,monospace', fontVariantNumeric: 'tabular-nums', } const CSS_KF = ` @keyframes msFlow { 0%{stroke-dashoffset:0} 100%{stroke-dashoffset:-18} } @keyframes msFadeIn { from{opacity:0} to{opacity:1} } @keyframes msScaleIn { from{opacity:0;transform:scale(.94)} to{opacity:1;transform:scale(1)} } @keyframes msHeadingDark { 0%,100%{text-shadow:0 0 22px rgba(167,139,250,.3)} 50% {text-shadow:0 0 40px rgba(167,139,250,.6)} } @keyframes msHeadingLight { 0%,100%{text-shadow:0 0 22px rgba(124,58,237,.15)} 50% {text-shadow:0 0 36px rgba(124,58,237,.30)} } @keyframes msPulse { 0%,100%{r:9;opacity:.4} 50% {r:14;opacity:.05} } ` // ── Light mode hook ─────────────────────────────────────────────────────────── function useIsLight() { const [isLight, setIsLight] = useState(false) useEffect(() => { const check = () => setIsLight(document.documentElement.classList.contains('theme-light')) check() const obs = new MutationObserver(check) obs.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }) return () => obs.disconnect() }, []) return isLight } // ── Themes ──────────────────────────────────────────────────────────────────── const THEMES = { dark: { key: 'dark' as const, bg: 'radial-gradient(ellipse at 50% 25%, #1a0f34 0%, #0e0720 55%, #050210 100%)', ambient: 'radial-gradient(ellipse, rgba(167,139,250,.18), transparent 65%)', stars: true, fg: '#f7f5fc', fgSoft: 'rgba(236,233,247,.82)', fgMid: 'rgba(236,233,247,.72)', fgMuted: 'rgba(236,233,247,.62)', fgFaint: 'rgba(236,233,247,.55)', fgGhost: 'rgba(236,233,247,.45)', fgWhisper: 'rgba(236,233,247,.4)', accent: '#a78bfa', accent80: 'rgba(167,139,250,.8)', accent70: 'rgba(167,139,250,.7)', accent50: 'rgba(167,139,250,.5)', accent40: 'rgba(167,139,250,.4)', accent20: 'rgba(167,139,250,.2)', headingGrad: 'linear-gradient(90deg, #e9e2ff, #a78bfa 50%, #e9e2ff)', headingAnim: 'msHeadingDark 4s ease-in-out infinite', heuteText: '#e4d4ff', heutePillBg: 'rgba(14,8,28,.95)', heuteCore: '#f0e9ff', done: '#4ade80', doneBright: '#86efac', doneDeep: '#166534', doneSolid: '#22c55e', cardBase: 'rgba(14,8,28,', cardBaseA: '.9', cardBaseAH: '.95', cardTintTop: '18', cardTintTopH: '2e', cardTintMid: '08', cardTintMidH: '14', cardShadowSoft: '0 10px 24px rgba(0,0,0,.45)', cardShadowLift: (t: string) => `0 20px 44px ${t}33, 0 0 0 1px ${t}66, inset 0 1px 0 ${t}66`, statTintTop: '18', statTintTopH: '2a', statTintMid: '06', statShadowSoft: '0 10px 24px rgba(0,0,0,.45)', statShadowLift: (t: string) => `0 18px 40px ${t}33, 0 0 0 1px ${t}55, inset 0 1px 0 ${t}55`, modalScrim: 'rgba(5,2,16,.75)', modalBgMid: 'rgba(20,10,40,.97)', modalBgLow: 'rgba(14,8,28,.98)', modalShadow: (t: string) => `0 30px 80px rgba(0,0,0,.65), 0 0 60px ${t}33, inset 0 1px 0 ${t}55`, bulletBg: 'rgba(0,0,0,.3)', progressTrackBg: 'rgba(255,255,255,.08)', progressTrackBorder: 'rgba(167,139,250,.2)', dotTodoDeep: '#1a0f34', dotLitHi: 'rgba(255,255,255,.5)', dotSoftHi: 'rgba(255,255,255,.3)', sparkOp: 0.45, }, light: { key: 'light' as const, bg: 'radial-gradient(ellipse at 50% 12%, #ffffff 0%, #f5efff 55%, #ebdfff 100%)', ambient: 'radial-gradient(ellipse, rgba(124,58,237,.14), transparent 65%)', stars: false, fg: '#1a0f34', fgSoft: 'rgba(26,15,52,.85)', fgMid: 'rgba(26,15,52,.72)', fgMuted: 'rgba(26,15,52,.62)', fgFaint: 'rgba(26,15,52,.50)', fgGhost: 'rgba(26,15,52,.40)', fgWhisper: 'rgba(26,15,52,.32)', accent: '#7c3aed', accent80: 'rgba(124,58,237,.8)', accent70: 'rgba(124,58,237,.75)', accent50: 'rgba(124,58,237,.55)', accent40: 'rgba(124,58,237,.4)', accent20: 'rgba(124,58,237,.18)', headingGrad: 'linear-gradient(90deg, #3b0e7a, #7c3aed 50%, #3b0e7a)', headingAnim: 'msHeadingLight 4s ease-in-out infinite', heuteText: '#4c1d95', heutePillBg: 'rgba(255,255,255,.98)', heuteCore: '#7c3aed', done: '#16a34a', doneBright: '#4ade80', doneDeep: '#14532d', doneSolid: '#22c55e', cardBase: 'rgba(255,255,255,', cardBaseA: '.92', cardBaseAH: '.98', cardTintTop: '22', cardTintTopH: '3a', cardTintMid: '10', cardTintMidH: '1c', cardShadowSoft: '0 10px 24px rgba(59,26,122,.10), 0 2px 6px rgba(59,26,122,.06)', cardShadowLift: (t: string) => `0 20px 44px ${t}38, 0 0 0 1px ${t}77, inset 0 1px 0 rgba(255,255,255,.9)`, statTintTop: '1e', statTintTopH: '34', statTintMid: '08', statShadowSoft: '0 10px 24px rgba(59,26,122,.10), 0 2px 6px rgba(59,26,122,.06)', statShadowLift: (t: string) => `0 18px 40px ${t}38, 0 0 0 1px ${t}77, inset 0 1px 0 rgba(255,255,255,.9)`, modalScrim: 'rgba(40,20,80,.28)', modalBgMid: 'rgba(255,255,255,.98)', modalBgLow: 'rgba(250,247,255,.98)', modalShadow: (t: string) => `0 30px 80px rgba(59,26,122,.25), 0 0 60px ${t}33, inset 0 1px 0 rgba(255,255,255,.9)`, bulletBg: 'rgba(124,58,237,.06)', progressTrackBg: 'rgba(124,58,237,.12)', progressTrackBorder: 'rgba(124,58,237,.25)', dotTodoDeep: '#faf5ff', dotLitHi: 'rgba(255,255,255,.85)', dotSoftHi: 'rgba(255,255,255,.55)', sparkOp: 0.55, }, } type Theme = typeof THEMES.dark // ── Data ────────────────────────────────────────────────────────────────────── const TODAY_POSITION = 0.56 interface Milestone { id: string when: string tick: string title: { de: string; en: string } short: { de: string; en: string } body: { de: string; en: string } bullets: { de: string[]; en: string[] } tint: string done: boolean next?: boolean } const MILESTONES: Milestone[] = [ { id: 'ihk', when: 'Okt. 2025', tick: '10 · 25', title: { de: 'Gründerzuschuss & IHK', en: 'Founder Grant & IHK' }, short: { de: 'Abstimmung mit Agentur für Arbeit und IHK Konstanz.', en: 'Coordination with Employment Agency and IHK Konstanz.' }, body: { de: 'Seit Oktober 2025 Gründerzuschussantrag in Abstimmung mit der Agentur für Arbeit und der IHK Konstanz. Grundlage für die Unternehmensgründung.', en: 'Since October 2025, founder grant application in coordination with the Employment Agency and IHK Konstanz. Foundation for company formation.', }, bullets: { de: ['Gründerzuschuss beantragt', 'Beratung IHK Konstanz', 'Businessplan finalisiert'], en: ['Founder grant applied', 'IHK Konstanz advisory', 'Business plan finalized'], }, tint: '#a78bfa', done: true, }, { id: 'brand', when: '11. Nov. 2025', tick: '11 · 25', title: { de: 'Markenanmeldung & Domains', en: 'Trademark Filing & Domains' }, short: { de: 'DPMA-Anmeldung BreakPilot + Domain-Portfolio.', en: 'DPMA filing BreakPilot + domain portfolio.' }, body: { de: 'Markenanmeldung BreakPilot beim DPMA am 11.11.2025. Domain-Kauf breakpilot.com, .de, .ai und brakepilot.com, .de, .ai am 21.11.2025.', en: 'BreakPilot trademark filed with DPMA on 11.11.2025. Domain purchase breakpilot.com, .de, .ai and brakepilot.com, .de, .ai on 21.11.2025.', }, bullets: { de: ['DPMA-Markenanmeldung 11.11.2025', 'Domains .com .de .ai gesichert', 'Typo-Domains (.brakepilot) gesichert'], en: ['DPMA trademark filed 11.11.2025', 'Domains .com .de .ai secured', 'Typo domains (.brakepilot) secured'], }, tint: '#a78bfa', done: true, }, { id: 'dev', when: 'Jan. 2026', tick: '01 · 26', title: { de: 'Plattform-Entwicklung gestartet', en: 'Platform Development Started' }, short: { de: '500.000+ Lines of Code, vollständige Architektur.', en: '500,000+ lines of code, full architecture.' }, body: { de: 'Start der Plattform-Entwicklung mit 500.000+ Lines of Code. Vollständige Microservice-Architektur mit Go, Python und TypeScript.', en: 'Platform development started with 500,000+ lines of code. Full microservice architecture with Go, Python and TypeScript.', }, bullets: { de: ['500K+ Lines of Code', 'Go + Python + TypeScript', 'Vollständige Architektur'], en: ['500K+ lines of code', 'Go + Python + TypeScript', 'Full architecture'], }, tint: '#c084fc', done: true, }, { id: 'dpma', when: '27. Mär. 2026', tick: '03 · 26', title: { de: 'Markeneintragung DPMA', en: 'DPMA Trademark Registration' }, short: { de: 'BreakPilot offiziell eingetragen.', en: 'BreakPilot officially registered.' }, body: { de: 'Markeneintragung BreakPilot beim Deutschen Patent- und Markenamt (DPMA) am 27.03.2026.', en: 'BreakPilot trademark registration at the German Patent and Trademark Office (DPMA) on 27.03.2026.', }, bullets: { de: ['DPMA-Eintragung 27.03.2026', 'Markenschutz Deutschland'], en: ['DPMA registration 27.03.2026', 'Trademark protection Germany'], }, tint: '#c084fc', done: true, }, { id: 'rag', when: 'Apr. 2026', tick: '04 · 26', title: { de: 'RAG mit 375+ Dokumenten', en: 'RAG with 375+ Documents' }, short: { de: 'EU + DACH Regularien & Normen indexiert.', en: 'EU + DACH regulations & standards indexed.' }, body: { de: '375+ Gesetze, Verordnungen, Normen und Urteile in die RAG-Pipeline ingestiert. 25.000+ Prüfaspekte generiert.', en: '375+ laws, regulations, standards and rulings ingested into the RAG pipeline. 25,000+ audit controls generated.', }, bullets: { de: ['375+ Dokumente im RAG', '25.000+ Prüfaspekte', 'EU + DACH Abdeckung'], en: ['375+ documents in RAG', '25,000+ audit controls', 'EU + DACH coverage'], }, tint: '#c084fc', done: true, }, { id: 'euipo', when: '1. Mai 2026', tick: '05 · 26', title: { de: 'Markenanmeldung EUIPO', en: 'EUIPO Trademark Filing' }, short: { de: 'EU-weiter Markenschutz beantragt.', en: 'EU-wide trademark protection filed.' }, body: { de: 'Markenanmeldung BreakPilot beim EUIPO (Amt der Europäischen Union für geistiges Eigentum) am 01.05.2026 für EU-weiten Markenschutz.', en: 'BreakPilot trademark filing with EUIPO (European Union Intellectual Property Office) on 01.05.2026 for EU-wide trademark protection.', }, bullets: { de: ['EUIPO-Anmeldung 01.05.2026', 'EU-weiter Markenschutz'], en: ['EUIPO filing 01.05.2026', 'EU-wide trademark protection'], }, tint: '#fbbf24', done: false, next: true, }, { id: 'gmbh', when: 'Aug. 2026', tick: '08 · 26', title: { de: 'GmbH-Gründung', en: 'GmbH Incorporation' }, short: { de: 'Breakpilot COMPLAI GmbH gegründet.', en: 'Breakpilot COMPLAI GmbH incorporated.' }, body: { de: 'Gründung der Breakpilot COMPLAI GmbH im August 2026. Notartermin, Handelsregistereintrag, operative Aufnahme.', en: 'Incorporation of Breakpilot COMPLAI GmbH in August 2026. Notary appointment, commercial register entry, start of operations.', }, bullets: { de: ['GmbH-Gründung August 2026', 'Handelsregistereintrag', 'Operativer Start'], en: ['GmbH incorporation August 2026', 'Commercial register entry', 'Start of operations'], }, tint: '#fbbf24', done: false, }, { id: 'customers', when: 'Aug. 2026', tick: '08 · 26', title: { de: '2 zahlende Kunden', en: '2 Paying Customers' }, short: { de: 'Erste Umsätze ab Gründung.', en: 'First revenue from incorporation.' }, body: { de: 'Zwei zahlende Kunden ab August 2026 — Validierung des Produkts im Maschinenbau-Umfeld mit echten Compliance-Anforderungen.', en: 'Two paying customers from August 2026 — product validation in manufacturing with real compliance requirements.', }, bullets: { de: ['2 zahlende Kunden', 'Maschinenbau-Validierung', 'Erste Umsätze'], en: ['2 paying customers', 'Manufacturing validation', 'First revenue'], }, tint: '#fbbf24', done: false, }, { id: 'beta', when: 'Q3 2026', tick: 'Q3 · 26', title: { de: 'Öffentliches Beta', en: 'Public Beta' }, short: { de: 'Beta-Launch mit ersten zahlenden Kunden.', en: 'Beta launch with first paying customers.' }, body: { de: 'Öffentliches Beta-Release der Plattform. Erste zahlende Kunden aus dem Pilotprogramm gehen live.', en: 'Public beta release of the platform. First paying customers from the pilot program go live.', }, bullets: { de: ['Public Beta verfügbar', 'Onboarding-Prozess live', 'Feedback-Loop etabliert'], en: ['Public beta available', 'Onboarding process live', 'Feedback loop established'], }, tint: '#f59e0b', done: false, }, ] interface StatItem { k: { de: string; en: string }; v: string; tint: string } const STATS: StatItem[] = [ { k: { de: 'Gesetze & Dokumente im RAG', en: 'Laws & Docs in RAG' }, v: '385', tint: '#a78bfa' }, { k: { de: 'Atomare Controls', en: 'Atomic Controls' }, v: '25.000+', tint: '#c084fc' }, { k: { de: 'Compliance-Module', en: 'Compliance Modules' }, v: '12', tint: '#fbbf24' }, { k: { de: 'Pilotkunden', en: 'Pilot Customers' }, v: '2', tint: '#f59e0b' }, { k: { de: 'Lines of Code', en: 'Lines of Code' }, v: '500.000+', tint: '#8b5cf6' }, ] // ── Star Field ──────────────────────────────────────────────────────────────── function StarField() { const stars = useMemo(() => { let s = 77 const r = () => { s = (s * 9301 + 49297) % 233280; return s / 233280 } return Array.from({ length: 95 }, () => ({ x: r() * 100, y: r() * 100, size: r() * 1.4 + 0.3, op: r() * 0.5 + 0.15 })) }, []) return (
{stars.map((st, i) => (
))}
) } function SoftGrid({ t }: { t: Theme }) { return (
) } // ── Timeline ────────────────────────────────────────────────────────────────── interface MilestoneWithPos extends Milestone { x: number; row: 'top' | 'bottom' } function Timeline({ onSelect, selectedId, t, de }: { onSelect: (m: Milestone) => void selectedId: string | null t: Theme de: boolean }) { const trackW = 1160 const innerPad = 120 const usableW = trackW - innerPad * 2 const positions = MILESTONES.map((_, i) => innerPad + (usableW * i) / (MILESTONES.length - 1)) const todayX = innerPad + usableW * TODAY_POSITION const layout: MilestoneWithPos[] = MILESTONES.map((m, i) => ({ ...m, x: positions[i], row: i % 2 === 0 ? 'top' : 'bottom', })) const railColor = t.key === 'dark' ? '#a78bfa' : '#7c3aed' return (
{/* rail background */} {/* past progress */} {/* future dashed */} {/* connector stubs */} {layout.map((m) => ( ))} {/* HEUTE marker — circles only; pill is HTML below */} {/* HEUTE pill — HTML so it sits above milestone cards */}
HEUTE
{layout.map((m) => ( onSelect(m)} active={selectedId === m.id} /> ))}
) } function MilestoneNode({ m, onClick, active, t, de }: { m: MilestoneWithPos; onClick: () => void; active: boolean; t: Theme; de: boolean }) { const [hover, setHover] = useState(false) const lit = hover || active const isTop = m.row === 'top' const cardY = isTop ? 4 : 200 const nodeColor = m.done ? t.done : m.tint const bgTopA = lit ? m.tint + t.cardTintTopH : m.tint + t.cardTintTop const bgMidA = lit ? m.tint + t.cardTintMidH : m.tint + t.cardTintMid const cardBg = `linear-gradient(180deg, ${bgTopA} 0%, ${bgMidA} 55%, ${t.cardBase}${lit ? t.cardBaseAH : t.cardBaseA})` const badge = m.done ? (de ? 'erledigt' : 'done') : (m.next ? (de ? 'als nächstes' : 'next') : (de ? 'geplant' : 'plan')) return ( <> {/* dot */}
setHover(true)} onMouseLeave={() => setHover(false)} style={{ position: 'absolute', left: m.x - 14, top: 180 - 14, width: 28, height: 28, borderRadius: '50%', background: m.done ? `radial-gradient(circle at 35% 30%, ${t.doneBright}, ${t.doneSolid} 60%, ${t.doneDeep})` : `radial-gradient(circle at 35% 30%, ${m.tint}dd, ${m.tint}66 60%, ${t.dotTodoDeep})`, border: `2px solid ${lit ? '#fff' : nodeColor}`, boxShadow: lit ? `0 0 22px ${nodeColor}, 0 0 44px ${nodeColor}66, inset 0 1px 0 ${t.dotLitHi}` : `0 0 10px ${nodeColor}88, inset 0 1px 0 ${t.dotSoftHi}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 11, fontWeight: 700, cursor: 'pointer', zIndex: 5, transition: 'all .25s', transform: lit ? 'scale(1.15)' : 'scale(1)', }}> {m.done ? '✓' : (m.next ? '◉' : '○')}
{/* card */}
setHover(true)} onMouseLeave={() => setHover(false)} style={{ position: 'absolute', left: m.x - 112, top: cardY, width: 224, height: 150, padding: '12px 14px', borderRadius: 12, background: cardBg, border: `1px solid ${lit ? m.tint : m.tint + '55'}`, boxShadow: lit ? t.cardShadowLift(m.tint) : t.cardShadowSoft, cursor: 'pointer', zIndex: 4, transition: 'all .25s', transform: lit ? `translateY(${isTop ? -2 : 2}px)` : 'translateY(0)', display: 'flex', flexDirection: 'column', gap: 6, backdropFilter: t.key === 'light' ? 'blur(6px)' : 'none', }}>
{m.tick} {badge}
{de ? m.title.de : m.title.en}
{de ? m.short.de : m.short.en}
{m.when} {de ? 'Details →' : 'Details →'}
) } // ── Stat Card ───────────────────────────────────────────────────────────────── function StatCard({ item, t, de }: { item: StatItem; t: Theme; de: boolean }) { const [hover, setHover] = useState(false) const bgTop = hover ? item.tint + t.statTintTopH : item.tint + t.statTintTop const bgMid = item.tint + t.statTintMid return (
setHover(true)} onMouseLeave={() => setHover(false)} style={{ position: 'relative', padding: '14px 18px', borderRadius: 12, background: `linear-gradient(180deg, ${bgTop} 0%, ${bgMid} 60%, ${t.cardBase}${t.cardBaseA})`, border: `1px solid ${hover ? item.tint : item.tint + '55'}`, boxShadow: hover ? t.statShadowLift(item.tint) : t.statShadowSoft, transform: hover ? 'translateY(-3px)' : 'translateY(0)', transition: 'all .25s', overflow: 'hidden', backdropFilter: t.key === 'light' ? 'blur(6px)' : 'none', }}>
{de ? item.k.de : item.k.en}
{item.v}
) } // ── Detail modal ────────────────────────────────────────────────────────────── function DetailModal({ item, onClose, t, de }: { item: Milestone | null; onClose: () => void; t: Theme; de: boolean }) { useEffect(() => { if (!item) return const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose() } window.addEventListener('keydown', onKey) return () => window.removeEventListener('keydown', onKey) }, [item, onClose]) if (!item) return null const tint = item.tint const badge = item.done ? (de ? 'ABGESCHLOSSEN' : 'COMPLETED') : (item.next ? (de ? 'ALS NÄCHSTES' : 'NEXT UP') : (de ? 'GEPLANT' : 'PLANNED')) const badgeColor = item.done ? t.done : tint return (
e.stopPropagation()} style={{ width: 580, maxWidth: '88%', background: `linear-gradient(180deg, ${tint}22 0%, ${t.modalBgMid} 50%, ${t.modalBgLow} 100%)`, border: `1px solid ${tint}77`, borderRadius: 16, boxShadow: t.modalShadow(tint), padding: '24px 28px', color: t.fg, animation: 'msScaleIn .22s ease-out', }}>
{item.done ? '✓' : (item.next ? '◉' : '○')}
{badge} {item.when}
{de ? item.title.de : item.title.en}
{de ? item.body.de : item.body.en}
{(de ? item.bullets.de : item.bullets.en).map((b, i) => (
{item.done ? '✓' : '▸'} {b}
))}
) } // ── Inner slide (fixed 1280×680) ────────────────────────────────────────────── function MilestonesInner({ t, de, sel, setSel }: { t: Theme; de: boolean sel: Milestone | null setSel: (m: Milestone | null) => void }) { const doneCnt = useMemo(() => MILESTONES.filter(m => m.done).length, []) const total = MILESTONES.length return (
{/* Ambient glow */}
{t.stars ? : } {/* Progress indicator */}
{de ? 'Fortschritt' : 'Progress'}
{doneCnt} / {total}
{/* Tip */}
{de ? 'Tipp:' : 'Tip:'} {de ? 'Klick auf einen Meilenstein' : 'Click any milestone'}
{/* Timeline */}
{/* Stats */}
{STATS.map(s => )}
{/* Footer */}
{de ? 'Stand heute · live-Metriken aus der Plattform' : 'As of today · live metrics from the platform'}
setSel(null)} t={t} de={de} />
) } // ── Main slide ──────────────────────────────────────────────────────────────── const INNER_W = 1280 const INNER_H = 600 export default function MilestonesSlide({ lang }: MilestonesSlideProps) { const de = lang === 'de' const isLight = useIsLight() const t = isLight ? THEMES.light : THEMES.dark const [sel, setSel] = useState(null) const [scale, setScale] = useState(1) const containerRef = useRef(null) const calcScale = useCallback(() => { if (containerRef.current) { const w = containerRef.current.offsetWidth setScale(Math.min(w / INNER_W, 1)) } }, []) useEffect(() => { calcScale() const obs = new ResizeObserver(calcScale) if (containerRef.current) obs.observe(containerRef.current) return () => obs.disconnect() }, [calcScale]) return (

{de ? 'Meilensteine' : 'Milestones'}

{de ? 'Von der Idee zur GmbH — was wir bereits erreicht haben' : 'From idea to GmbH — what we have already achieved'}

) }