diff --git a/pitch-deck/app/api/data/route.ts b/pitch-deck/app/api/data/route.ts index 413a179..58899d9 100644 --- a/pitch-deck/app/api/data/route.ts +++ b/pitch-deck/app/api/data/route.ts @@ -78,6 +78,16 @@ export async function GET() { } } catch (error) { console.error('Database query error:', error) + // Return minimal stub in dev so the pitch renders without a DB connection + if (process.env.NODE_ENV === 'development') { + return NextResponse.json({ + company: { name: 'BreakPilot', tagline: '[dev mode — no DB]' }, + team: [], financials: [], market: [], competitors: [], + features: [], milestones: [], metrics: [], + funding: { instrument: 'Wandeldarlehen', amount: 500000, valuation_cap: 3000000, currency: 'EUR' }, + products: [], + }) + } return NextResponse.json({ error: 'Failed to load pitch data' }, { status: 500 }) } } diff --git a/pitch-deck/app/globals.css b/pitch-deck/app/globals.css index cdeaab4..155b862 100644 --- a/pitch-deck/app/globals.css +++ b/pitch-deck/app/globals.css @@ -1,10 +1,10 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap'); + @tailwind base; @tailwind components; @tailwind utilities; -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap'); - /* === Dark Mode (default) === */ :root { --bg-primary: #0a0a1a; diff --git a/pitch-deck/components/PitchDeck.tsx b/pitch-deck/components/PitchDeck.tsx index bfea8d7..a3b3031 100644 --- a/pitch-deck/components/PitchDeck.tsx +++ b/pitch-deck/components/PitchDeck.tsx @@ -29,7 +29,6 @@ import ProductSlide from './slides/ProductSlide' import HowItWorksSlide from './slides/HowItWorksSlide' import MarketSlide from './slides/MarketSlide' import BusinessModelSlide from './slides/BusinessModelSlide' -import TractionSlide from './slides/TractionSlide' import CompetitionSlide from './slides/CompetitionSlide' import TeamSlide from './slides/TeamSlide' import FinancialsSlide from './slides/FinancialsSlide' @@ -52,6 +51,7 @@ import StrategySlide from './slides/StrategySlide' import FinanzplanSlide from './slides/FinanzplanSlide' import GlossarySlide from './slides/GlossarySlide' import RiskSlide from './slides/RiskSlide' +import MilestonesSlide from './slides/MilestonesSlide' interface PitchDeckProps { lang: Language @@ -181,7 +181,7 @@ export default function PitchDeck({ lang, onToggleLanguage, investor, onLogout, case 'business-model': return case 'traction': - return + return case 'competition': return case 'team': diff --git a/pitch-deck/components/slides/ArchitectureSlide.tsx b/pitch-deck/components/slides/ArchitectureSlide.tsx index bc293e4..ef84b05 100644 --- a/pitch-deck/components/slides/ArchitectureSlide.tsx +++ b/pitch-deck/components/slides/ArchitectureSlide.tsx @@ -1,129 +1,733 @@ 'use client' +import { useState, useEffect, useRef, Fragment } from 'react' +import { motion, AnimatePresence } from 'framer-motion' import { Language } from '@/lib/types' import { t } from '@/lib/i18n' import GradientText from '../ui/GradientText' import FadeInView from '../ui/FadeInView' -import GlassCard from '../ui/GlassCard' -import { Server, Cpu, Shield, Database, Globe, Lock, Layers, Workflow } from 'lucide-react' +import { + Brain, Shield, ScanLine, Zap, Cpu, + Layers, Wrench, X, Users, Lock, + Server, BadgeCheck, +} from 'lucide-react' -interface ArchitectureSlideProps { - lang: Language +interface ArchitectureSlideProps { lang: Language } +type NodeId = 'certifai' | 'complai' | 'scanner' | 'litellm' | 'llm' | 'embeddings' | 'tools' + +interface NodeDef { + id: NodeId + icon: React.ElementType + title: string + subtitle: string + color: string + tech: string[] + services: { name: string; desc: string }[] + primary?: boolean + tier: 'product' | 'proxy' | 'inference' } +function getNodes(de: boolean): NodeDef[] { + return [ + { + id: 'certifai', icon: Brain, + title: 'CERTifAI', + subtitle: de ? 'GenAI Mandantenportal' : 'GenAI Tenant Portal', + color: '#c084fc', tier: 'product', + tech: ['Rust', 'Dioxus', 'MongoDB', 'Keycloak', 'SearXNG', 'LangGraph'], + services: [ + { name: 'LiteLLM Dashboard', desc: de ? 'Modellverwaltung & Kostentracking' : 'Model mgmt & cost tracking' }, + { name: 'LibreChat + SSO', desc: de ? 'Mandanten-Chat mit Keycloak' : 'Tenant chat with Keycloak' }, + { name: 'LangGraph Agents', desc: de ? 'Agent-Orchestrierung' : 'Agent orchestration' }, + { name: 'MCP Hub', desc: de ? 'Tool-Integration für KI-Clients' : 'Tool integration for AI clients' }, + ], + }, + { + id: 'complai', icon: Shield, + title: 'COMPLAI', + subtitle: de ? 'Compliance & Audit' : 'Compliance & Audit', + color: '#818cf8', tier: 'product', + tech: ['Next.js 15', 'FastAPI', 'Go/Gin', 'PostgreSQL', 'Qdrant', 'Valkey'], + services: [ + { name: de ? 'DSGVO / AI Act / NIS2' : 'GDPR / AI Act / NIS2', desc: de ? '70k+ auditierbare Controls' : '70k+ auditable controls' }, + { name: 'RAG Pipeline', desc: de ? '75+ Rechtsquellen, semantische Suche' : '75+ legal sources, semantic search' }, + { name: 'Control Pipeline', desc: de ? 'Gesetzestextanalyse via LLM' : 'Legal text analysis via LLM' }, + { name: 'MCP Client', desc: de ? 'Echtzeit-Findings vom Scanner' : 'Real-time findings from Scanner' }, + ], + }, + { + id: 'scanner', icon: ScanLine, + title: 'Compliance Scanner', + subtitle: de ? 'Code-Sicherheit' : 'Code Security', + color: '#34d399', tier: 'product', + tech: ['Rust', 'Axum', 'MongoDB', 'Semgrep', 'Gitleaks', 'Syft'], + services: [ + { name: 'SAST / SBOM / CVE', desc: de ? 'Vollautomatische Pipeline' : 'Fully automated pipeline' }, + { name: de ? 'KI-Triage' : 'AI Triage', desc: de ? 'LLM filtert False Positives' : 'LLM filters false positives' }, + { name: de ? 'KI-Pentest' : 'AI Pentest', desc: de ? 'Autonome Angriffsketten' : 'Autonomous attack chains' }, + { name: 'MCP Server', desc: de ? 'Live-Findings für COMPLAI' : 'Live findings for COMPLAI' }, + ], + }, + { + id: 'litellm', icon: Zap, + title: 'LiteLLM Proxy', + subtitle: de ? 'KI-Gateway & Guardrails' : 'AI Gateway & Guardrails', + color: '#fbbf24', tier: 'proxy', primary: true, + tech: ['OpenAI-kompatible API', 'Bearer Auth', 'Rate Limiting', 'PII-Filter', 'Spend Tracking'], + services: [ + { name: de ? 'Token-Budget' : 'Token Budget', desc: de ? 'Pro-Mandant Kontingente & Abrechnung' : 'Per-tenant quotas & billing' }, + { name: 'PII Guardrails', desc: de ? 'Datenschutz-Filter für alle Anfragen' : 'Privacy filter on all requests' }, + { name: de ? 'Web-Suche (anonym)' : 'Web Search (anon)', desc: de ? 'SearXNG-Proxy, kein US-Anbieter' : 'SearXNG proxy, no US providers' }, + { name: de ? 'Namespace-Isolierung' : 'Namespace Isolation', desc: de ? 'Mandantentrennung per API-Key' : 'Tenant isolation per API key' }, + { name: de ? 'Failover-Routing' : 'Failover Routing', desc: de ? 'Automatisches Fallback' : 'Automatic fallback between models' }, + ], + }, + { + id: 'llm', icon: Cpu, + title: de ? 'LLM Inferenz' : 'LLM Inference', + subtitle: de ? 'Lokale Sprachmodelle' : 'Local Language Models', + color: '#60a5fa', tier: 'inference', + tech: ['Qwen3-32B', 'Qwen3-Coder-30B', 'DeepSeek-R1-8B', 'Ollama'], + services: [ + { name: de ? 'Vollständig lokal' : 'Fully local', desc: de ? 'Daten verlassen nie den Server' : 'Data never leaves the server' }, + { name: de ? 'Air-Gap fähig' : 'Air-Gap Capable', desc: de ? 'Kein Internet erforderlich' : 'No internet required' }, + { name: de ? 'GPU-optimiert' : 'GPU-optimized', desc: de ? 'Dedizierte Inferenz-Hardware' : 'Dedicated inference hardware' }, + ], + }, + { + id: 'embeddings', icon: Layers, + title: 'Embeddings', + subtitle: de ? 'Semantische Suche' : 'Semantic Search', + color: '#a78bfa', tier: 'inference', + tech: ['bge-m3', 'Qdrant Vector DB', 'Sentence-Transformers'], + services: [ + { name: 'RAG Pipeline', desc: de ? '75+ Rechtsquellen indexiert' : '75+ legal sources indexed' }, + { name: de ? 'Semantische Suche' : 'Semantic Search', desc: de ? 'Multi-linguale Einbettungen' : 'Multi-lingual embeddings' }, + { name: de ? 'Lokal' : 'Fully local', desc: de ? 'Keine externen APIs' : 'No external APIs' }, + ], + }, + { + id: 'tools', icon: Wrench, + title: de ? 'KI-Tools' : 'AI Tools', + subtitle: de ? 'Web-Suche & MCP' : 'Web Search & MCP', + color: '#2dd4bf', tier: 'inference', + tech: ['SearXNG', 'MCP Protocol', 'Semgrep API', 'Gitleaks API'], + services: [ + { name: 'SearXNG', desc: de ? 'Anonymisierte EU-Websuche' : 'Anonymized EU web search' }, + { name: 'MCP Tools', desc: de ? 'Auditdokumente & Code-Findings' : 'Audit docs & code findings' }, + { name: de ? 'Kein US-Anbieter' : 'No US providers', desc: de ? '100% DSGVO-konform' : '100% GDPR-compliant' }, + ], + }, + ] +} + +const LAYERS: { id: string; nodeIds: NodeId[]; tint: string; depth: number }[] = [ + { id: 'product', nodeIds: ['certifai', 'complai', 'scanner'], tint: '#a78bfa', depth: 24 }, + { id: 'proxy', nodeIds: ['litellm'], tint: '#fbbf24', depth: 12 }, + { id: 'inference', nodeIds: ['llm', 'embeddings', 'tools'], tint: '#8b5cf6', depth: 0 }, +] + +const CSS_KF = ` + @keyframes v4FlowDown { from { stroke-dashoffset: 0 } to { stroke-dashoffset: -18px } } + @keyframes v4Pulse { 0%,100% { opacity:1;transform:scale(1) } 50% { opacity:.4;transform:scale(1.4) } } + @keyframes v4Caret { 0%,50% { opacity:1 } 51%,100% { opacity:0 } } + @keyframes v4DotFall { + 0% { transform: translateY(-5px); opacity: 0; } + 12% { opacity: 1; } + 88% { opacity: 1; } + 100% { transform: translateY(38px); opacity: 0; } + } +` + +const MONO: React.CSSProperties = { + fontFamily: '"JetBrains Mono","SF Mono",ui-monospace,monospace', + fontVariantNumeric: 'tabular-nums', +} + +// ── Theme detection ─────────────────────────────────────────────────────────── +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 +} + +// ── Ticker primitives ───────────────────────────────────────────────────────── +function useTicker(fn: () => void, min = 140, max = 360, skipChance = 0.1) { + const ref = useRef(fn) + ref.current = fn + useEffect(() => { + let tid: ReturnType + const loop = () => { + if (Math.random() > skipChance) ref.current() + tid = setTimeout(loop, min + Math.random() * (max - min)) + } + loop() + return () => clearTimeout(tid) + }, [min, max, skipChance]) +} + +function TickerShell({ color, children, isLight }: { color: string; children: React.ReactNode; isLight: boolean }) { + return ( +
{children}
+ ) +} + +function Caret({ color }: { color: string }) { + return ( + + ) +} + +// ── Per-node tickers ────────────────────────────────────────────────────────── +function TickCertifAI({ color, isLight }: { color: string; isLight: boolean }) { + const [n, setN] = useState(8421) + const [hash, setHash] = useState('9f3a…e10b') + const pool = 'abcdef0123456789' + const r = (k: number) => Array.from({ length: k }, () => pool[Math.floor(Math.random() * pool.length)]).join('') + useTicker(() => { setN(v => v + 1); setHash(`${r(4)}…${r(4)}`) }, 1000, 2000, 0.1) + return ( + + + sig + {n.toLocaleString()} + {hash} + + ) +} + +function TickComplAI({ color, isLight }: { color: string; isLight: boolean }) { + const [evals, setEvals] = useState(1284) + const [rate, setRate] = useState(99.2) + useTicker(() => { + setEvals(v => v + 1 + Math.floor(Math.random() * 3)) + setRate(r => Math.max(97, Math.min(99.9, r + (Math.random() - 0.5) * 0.4))) + }, 200, 500, 0.1) + return ( + + + eval + {evals.toLocaleString()} + pass + {rate.toFixed(1)}% + + ) +} + +function TickScanner({ color, isLight }: { color: string; isLight: boolean }) { + const lines = [ + { k: 'PASS', c: '#16a34a', cd: '#4ade80', t: 'CWE-79 xss check' }, + { k: 'WARN', c: '#d97706', cd: '#fbbf24', t: 'drift: model v2.1→2.2' }, + { k: 'PASS', c: '#16a34a', cd: '#4ade80', t: 'bias: demographic parity' }, + { k: 'FAIL', c: '#dc2626', cd: '#f87171', t: 'license: GPL-3 detected' }, + { k: 'PASS', c: '#16a34a', cd: '#4ade80', t: 'prompt-inject: 214 vectors' }, + { k: 'SCAN', c: '#7c3aed', cd: '#a78bfa', t: 'artifact model-card.json' }, + ] + const [i, setI] = useState(0) + useTicker(() => setI(x => (x + 1) % lines.length), 700, 1200, 0.05) + const l = lines[i] + return ( + + {l.k} + {l.t} + + ) +} + +function TickLiteLLM({ color, isLight }: { color: string; isLight: boolean }) { + const [rps, setRps] = useState(428) + const [p50, setP50] = useState(84) + useTicker(() => { + setRps(v => Math.max(200, Math.min(800, v + (Math.random() - 0.5) * 60))) + setP50(v => Math.max(40, Math.min(160, v + (Math.random() - 0.5) * 20))) + }, 250, 500, 0.05) + return ( + + + req/s + {Math.round(rps)} + · + p50 + {Math.round(p50)}ms + + + ) +} + +function TickLLM({ color, isLight }: { color: string; isLight: boolean }) { + const [tokens, setTokens] = useState(14832) + const [stream, setStream] = useState('t_a91f') + const pool = 'abcdef0123456789' + useTicker(() => { + setTokens(v => v + 1 + Math.floor(Math.random() * 5)) + setStream('t_' + Array.from({ length: 4 }, () => pool[Math.floor(Math.random() * pool.length)]).join('')) + }, 120, 340, 0.15) + return ( + + + tok + {tokens.toLocaleString()} + + {stream} + + + ) +} + +function TickEmbeddings({ color, isLight }: { color: string; isLight: boolean }) { + const [vecs, setVecs] = useState(284112) + useTicker(() => setVecs(v => v + 1 + Math.floor(Math.random() * 8)), 180, 420, 0.1) + return ( + + + idx + {vecs.toLocaleString()} + · 1024d + + + ) +} + +function TickTools({ color, isLight }: { color: string; isLight: boolean }) { + const ops = [ + 'search("BSI C5 controls")', 'fetch eur-lex.europa.eu', + 'grep -r "DSGVO"', 'read docs/policy.md', + 'mcp.call(filesystem)', 'search("vLLM 0.6 release")', + ] + const [i, setI] = useState(0) + useTicker(() => setI(x => (x + 1) % ops.length), 900, 1600, 0.05) + return ( + + + call + {ops[i]} + + ) +} + +const NODE_TICKER: Record> = { + certifai: TickCertifAI, + complai: TickComplAI, + scanner: TickScanner, + litellm: TickLiteLLM, + llm: TickLLM, + embeddings: TickEmbeddings, + tools: TickTools, +} + +// ── Animated connector ──────────────────────────────────────────────────────── +function LayerConnector({ tint }: { tint: string }) { + const tracks = [ + { x: '32%', primary: false }, + { x: '50%', primary: true }, + { x: '68%', primary: false }, + ] + return ( +
+ {tracks.map(({ x, primary }, ti) => { + const color = primary ? '#fbbf24' : tint + const dots = primary ? 4 : 3 + const dur = primary ? 1.6 : 2.4 + return ( +
+
+ {Array.from({ length: dots }, (_, j) => ( +
+ ))} +
+ ) + })} +
+ ) +} + +// ── Node card ───────────────────────────────────────────────────────────────── +function NodeCard({ node, selected, onClick, isLight }: { + node: NodeDef; selected: boolean; onClick: () => void; isLight: boolean +}) { + const [hover, setHover] = useState(false) + const active = hover || selected + const c = node.color + const Ticker = NODE_TICKER[node.id] + const Icon = node.icon + + return ( + + ) +} + +// ── 3D slab ─────────────────────────────────────────────────────────────────── +function LayerSlab({ label, sublabel, nodes, tint, depth, selectedId, onSelect, isLight }: { + label: string; sublabel: string; nodes: NodeDef[] + tint: string; depth: number + selectedId: NodeId | null; onSelect: (id: NodeId) => void + isLight: boolean +}) { + const isProxy = nodes.length === 1 && !!nodes[0].primary + return ( +
+
+
+
{label}
+
{sublabel}
+
+
+ {isProxy ? ( +
+ onSelect(nodes[0].id)} isLight={isLight} /> +
+ ) : ( + nodes.map(n => ( + onSelect(n.id)} isLight={isLight} /> + )) + )} +
+
+ ) +} + +// ── Main slide ──────────────────────────────────────────────────────────────── export default function ArchitectureSlide({ lang }: ArchitectureSlideProps) { const i = t(lang) const de = lang === 'de' + const isLight = useIsLight() + const allNodes = getNodes(de) + const nodeMap = Object.fromEntries(allNodes.map(n => [n.id, n])) as Record - const layers = [ - { - icon: Server, - color: 'text-indigo-400', - bg: 'bg-indigo-500/10 border-indigo-500/20', - title: de ? 'Hardware-Schicht' : 'Hardware Layer', - items: [ - { label: 'ComplAI Mini', desc: de ? 'Mac Mini M4 (geplant, optional)' : 'Mac Mini M4 (planned, optional)' }, - { label: 'ComplAI Studio', desc: de ? 'Mac Studio M4 Max (geplant, optional)' : 'Mac Studio M4 Max (planned, optional)' }, - { label: 'ComplAI Cloud', desc: de ? 'BSI-zertifizierte Cloud in Deutschland' : 'BSI-certified cloud in Germany' }, - ], - }, - { - icon: Cpu, - color: 'text-purple-400', - bg: 'bg-purple-500/10 border-purple-500/20', - title: de ? 'KI-Engine' : 'AI Engine', - items: [ - { label: 'Ollama Runtime', desc: de ? 'Lokale LLM-Inferenz, GPU-optimiert' : 'Local LLM inference, GPU-optimized' }, - { label: 'RAG Pipeline', desc: de ? 'Vektorsuche mit Compliance-Wissensbasis' : 'Vector search with compliance knowledge base' }, - { label: 'Agent Framework', desc: de ? 'Autonome Compliance-Agenten (Audit, Monitoring, Reporting)' : 'Autonomous compliance agents (Audit, Monitoring, Reporting)' }, - ], - }, - { - icon: Shield, - color: 'text-emerald-400', - bg: 'bg-emerald-500/10 border-emerald-500/20', - title: de ? 'Compliance-Module' : 'Compliance Modules', - items: [ - { label: 'DSGVO Engine', desc: de ? 'VVT, DSFA, Betroffenenrechte, Löschkonzept' : 'RoPA, DPIA, Data Subject Rights, Deletion Concept' }, - { label: 'AI Act Module', desc: de ? 'Risikoklassifizierung, Konformitätsbewertung, Dokumentation' : 'Risk Classification, Conformity Assessment, Documentation' }, - { label: 'NIS2 Module', desc: de ? 'Cybersecurity-Policies, Incident Response, Meldewege' : 'Cybersecurity Policies, Incident Response, Reporting Chains' }, - ], - }, - { - icon: Layers, - color: 'text-blue-400', - bg: 'bg-blue-500/10 border-blue-500/20', - title: de ? 'Plattform-Services' : 'Platform Services', - items: [ - { label: de ? 'Admin-Dashboard' : 'Admin Dashboard', desc: 'Next.js · ' + (de ? 'Mandantenfähig · Rollenbasiert' : 'Multi-Tenant · Role-Based') }, - { label: 'SDK API', desc: 'Go/Gin · REST · ' + (de ? 'Tenant-isoliert' : 'Tenant-Isolated') }, - { label: 'DevSecOps Suite', desc: 'Semgrep · Trivy · Gitleaks · CycloneDX SBOM' }, - ], - }, - ] + const [activeId, setActiveId] = useState(null) + function toggle(id: NodeId) { setActiveId(prev => prev === id ? null : id) } + const active = activeId ? nodeMap[activeId] : null - const securityFeatures = [ - { icon: Lock, label: de ? 'Zero-Trust Architektur' : 'Zero-Trust Architecture' }, - { icon: Database, label: de ? 'Daten verlassen nie BSI-zertifizierte Server in DE' : 'Data Never Leaves BSI-Certified Servers in DE' }, - { icon: Globe, label: de ? '100% EU-Cloud · Keine US-Anbieter' : '100% EU Cloud · No US Providers' }, - { icon: Workflow, label: de ? 'Air-Gap fähig' : 'Air-Gap Capable' }, - ] + const tenants = de + ? ['Mandant A', 'Mandant B', 'Mandant C', 'Mandant N…'] + : ['Namespace A', 'Namespace B', 'Namespace C', 'Namespace N…'] + + const layerLabels = de + ? ['01 · Anwendung', '02 · Gateway', '03 · Infrastruktur'] + : ['01 · Application', '02 · Gateway', '03 · Infrastructure'] + const layerSublabels = de + ? ['Benutzeroberflächen', 'Routing & Guardrails', 'Compute & Daten'] + : ['User-facing services', 'Routing & guardrails', 'Compute & data'] return ( -
- -

+

+ + + +

{de ? 'Anhang' : 'Appendix'}

-

+

{i.annex.architecture.title}

-

{i.annex.architecture.subtitle}

+

+ {de ? 'Klicke auf eine Station für Details' : 'Click any node to explore'} +

- {/* Architecture Layers */} -
- {layers.map((layer, idx) => { - const Icon = layer.icon - return ( - -
-
- -

{layer.title}

-
-
- {layer.items.map((item, iidx) => ( -
-
-
- {item.label} - {item.desc} -
-
- ))} -
-
- - ) - })} -
+ +
+ + + {de ? 'Kundenmandanten' : 'Customer Namespaces'} + + {tenants.map(tn => ( + + {tn} + + ))} +
- {/* Security Bar */} - - -
- {securityFeatures.map((feat, idx) => { - const Icon = feat.icon + {/* ── Main canvas ── */} +
+ + {/* Ambient glows */} + {!isLight && ( + <> +
+
+ + )} + + {/* Slabs + connectors */} +
+ {LAYERS.map((layer, li) => { + const nodes = layer.nodeIds.map(id => nodeMap[id]) return ( -
- - {feat.label} -
+ + + {li < LAYERS.length - 1 && } + ) })}
- + + {/* Footer badges */} +
+ {([ + { Icon: Lock, label: de ? 'Kein US-Anbieter · 100% DSGVO' : 'No US providers · 100% GDPR' }, + { Icon: Server, label: de ? 'BSI-zertifiziertes Rechenzentrum' : 'BSI-certified data center' }, + { Icon: BadgeCheck, label: de ? 'EU-souveräne Inferenz' : 'EU-sovereign inference' }, + ] as { Icon: React.ElementType; label: string }[]).map(({ Icon, label }) => ( +
+ + {label} +
+ ))} +
+ + {/* Detail panel */} + + {active && ( + +
+
+
+
+ +
+
+
+ + {active.title} + + + {active.tier === 'product' ? (de ? 'Anwendung' : 'Application') : + active.tier === 'proxy' ? 'Gateway' : + (de ? 'Inferenz' : 'Inference')} + +
+
+ {active.subtitle} +
+
+
+ +
+
+
+
+ {de ? 'Stack' : 'Tech Stack'} +
+
+ {active.tech.map(tk => ( + {tk} + ))} +
+
+
+
+ {de ? 'Funktionen' : 'Capabilities'} +
+
+ {active.services.map(s => ( +
+
+ {s.name} + {s.desc} +
+ ))} +
+
+
+
+ + )} + +
) diff --git a/pitch-deck/components/slides/MilestonesSlide.tsx b/pitch-deck/components/slides/MilestonesSlide.tsx new file mode 100644 index 0000000..9dcf748 --- /dev/null +++ b/pitch-deck/components/slides/MilestonesSlide.tsx @@ -0,0 +1,796 @@ +'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: 'start', + when: 'Mär. 2025', tick: '03 · 25', + title: { de: 'Idee & Team-Start', en: 'Idea & Team Start' }, + short: { de: 'Gründerteam formiert sich, erste Konzeption.', en: 'Founding team forms, first product concept.' }, + body: { + de: 'Zwei Gründer, ein klares Problem: Compliance-Doks und Code leben in getrennten Welten. Start der Konzeption für eine Plattform, die beide Welten verbindet.', + en: 'Two founders, one clear problem: compliance docs and code live in separate worlds. Started designing a platform that bridges both.', + }, + bullets: { + de: ['Team von 2 Gründern', 'Markt-Research DACH + EU', 'Erste Architektur-Skizze'], + en: ['Team of 2 founders', 'Market research DACH + EU', 'First architecture sketch'], + }, + tint: '#a78bfa', done: true, + }, + { + id: 'ihk', + when: 'Okt. 2025', tick: '10 · 25', + title: { de: 'IHK & Agentur für Arbeit', en: 'IHK & Employment Agency' }, + short: { de: 'Gründerzuschuss beantragt & gesichert.', en: 'Founder grant applied for & secured.' }, + body: { + de: 'Information und Austausch mit Agentur für Arbeit und IHK Konstanz für den Gründerzuschuss — seit Oktober 2025 in Bearbeitung und Aufbau.', + en: 'Collaboration with Employment Agency and IHK Konstanz for the founder grant — in processing since October 2025.', + }, + bullets: { + de: ['Gründerzuschuss genehmigt', 'Mentorship-Programm IHK', 'Erste öffentliche Sichtbarkeit'], + en: ['Founder grant approved', 'IHK mentorship program', 'First public visibility'], + }, + tint: '#a78bfa', done: true, + }, + { + id: 'proto', + when: 'Dez. 2025', tick: '12 · 25', + title: { de: 'Prototyp Compliance SDK', en: 'Compliance SDK Prototype' }, + short: { de: 'Compliance SDK & Security Cloud laufen.', en: 'Compliance SDK & Security Cloud running.' }, + body: { + de: 'Entwicklung eines funktionsfähigen Prototypen der Compliance SDK und der Security-Cloud-Lösung — erste End-to-End-Demo läuft seit Dezember 2025.', + en: 'Built a working prototype of the Compliance SDK and Security Cloud solution — first end-to-end demo running since December 2025.', + }, + bullets: { + de: ['SDK: policy → code mapping', 'Security Cloud MVP', 'Interne Demo-Audits erfolgreich'], + en: ['SDK: policy → code mapping', 'Security Cloud MVP', 'Internal demo audits successful'], + }, + tint: '#c084fc', done: true, + }, + { + id: 'pilot', + when: 'Dez. 2025', tick: '12 · 25', + title: { de: '2 Pilotkunden im Gespräch', en: '2 Pilot Customers in Talks' }, + short: { de: 'Schwarzwald + Mobilitäts-Sektor.', en: 'Black Forest + Mobility Sector.' }, + body: { + de: 'Kommunikation seit Dezember 2025 mit Kunden aus dem Schwarzwald (CE-Software-Risikobeurteilung) und einem globalen Maschinen- und Anlagenbauer aus dem Mobilitätssektor (KI-Roadmap).', + en: 'Since December 2025 in talks with a Black Forest CE-software customer (risk assessment) and a global mobility-sector machine builder (AI roadmap).', + }, + bullets: { + de: ['CE-Software-Risikobeurteilung', 'KI-Roadmap für Mobilitätssektor', 'LOIs in Vorbereitung'], + en: ['CE software risk assessment', 'AI roadmap for mobility sector', 'LOIs in preparation'], + }, + tint: '#c084fc', done: true, + }, + { + id: 'reg', + when: '27. Mär. 2026', tick: '03 · 26', + title: { de: 'Eintragung GmbH', en: 'GmbH Registration' }, + short: { de: 'Offizielle Gründung im Handelsregister.', en: 'Official incorporation in commercial register.' }, + body: { + de: 'Notartermin und Eintragung ins Handelsregister am 27.03.2026. Ab diesem Datum voll operative GmbH mit klaren Governance-Strukturen.', + en: 'Notary appointment and commercial register entry on 27.03.2026. Fully operative GmbH with clear governance structures from this date.', + }, + bullets: { + de: ['Gesellschaftsvertrag unterzeichnet', 'HRB-Eintrag Konstanz', 'Erste Rechnung ausgestellt'], + en: ['Articles of association signed', 'HRB entry Constance', 'First invoice issued'], + }, + tint: '#fbbf24', done: false, next: true, + }, + { + id: 'seed', + when: 'Q2 2026', tick: 'Q2 · 26', + title: { de: 'Seed-Runde', en: 'Seed Round' }, + short: { de: '1,5 Mio € für 18 Monate Runway.', en: '€1.5M for 18 months runway.' }, + body: { + de: 'Pre-Seed / Seed-Runde zur Finanzierung des ersten Kundensegments, Ausbau des Teams und Zertifizierung (ISO 27001, BSI C5).', + en: 'Pre-Seed / Seed round to fund first customer segment, team growth and certification (ISO 27001, BSI C5).', + }, + bullets: { + de: ['Ziel: 1,5 Mio € Seed', 'Ausbau auf 8 FTE', 'Zertifizierungs-Track startet'], + en: ['Target: €1.5M seed', 'Scale to 8 FTE', 'Certification track starts'], + }, + 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. Integration in Gitlab + GitHub Cloud.', + en: 'Public beta release of the platform. First paying customers from the pilot program go live. GitLab + GitHub Cloud integration.', + }, + bullets: { + de: ['3–5 zahlende Pilot-Kunden', 'Public Beta verfügbar', 'Git-Integration live'], + en: ['3–5 paying pilot customers', 'Public beta available', 'Git integration live'], + }, + tint: '#f59e0b', done: false, + }, + { + id: 'v1', + when: 'Q4 2026', tick: 'Q4 · 26', + title: { de: 'EU Trust Stack v1.0', en: 'EU Trust Stack v1.0' }, + short: { de: 'DSGVO · NIS-2 · DORA · EU AI Act.', en: 'GDPR · NIS-2 · DORA · EU AI Act.' }, + body: { + de: 'Alle vier zentralen EU-Frameworks voll abgedeckt. EU-souveränes Hosting, vollständige Audit-Trail-Unterstützung, Zertifizierung ISO 27001 abgeschlossen.', + en: 'All four central EU frameworks fully covered. EU-sovereign hosting, complete audit trail support, ISO 27001 certification completed.', + }, + bullets: { + de: ['4 EU-Frameworks live', 'EU-souveränes Hosting', 'ISO 27001 zertifiziert'], + en: ['4 EU frameworks live', 'EU-sovereign hosting', 'ISO 27001 certified'], + }, + 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'} +

+
+ + +
+
+ +
+
+
+
+ ) +} diff --git a/pitch-deck/components/slides/USPSlide.tsx b/pitch-deck/components/slides/USPSlide.tsx index 7c0904b..9395992 100644 --- a/pitch-deck/components/slides/USPSlide.tsx +++ b/pitch-deck/components/slides/USPSlide.tsx @@ -1,222 +1,830 @@ 'use client' +import { useState, useEffect, useRef, useMemo } from 'react' +import { motion, AnimatePresence } from 'framer-motion' import { Language } from '@/lib/types' import GradientText from '../ui/GradientText' import FadeInView from '../ui/FadeInView' -import GlassCard from '../ui/GlassCard' -import { - FileCheck, - Code, - Zap, - Shield, - GitPullRequest, - ArrowLeftRight, -} from 'lucide-react' +import { X } from 'lucide-react' -interface USPSlideProps { - lang: Language +interface USPSlideProps { lang: Language } + +const MONO: React.CSSProperties = { + fontFamily: '"JetBrains Mono","SF Mono",ui-monospace,monospace', + fontVariantNumeric: 'tabular-nums', } -export default function USPSlide({ lang }: USPSlideProps) { - const de = lang === 'de' +const CSS_KF = ` + @keyframes uspFlowR { 0%{stroke-dashoffset:0} 100%{stroke-dashoffset:-14px} } + @keyframes uspSpin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} } + @keyframes uspPulse { + 0%,100% { box-shadow: 0 0 38px rgba(167,139,250,.55), 0 0 80px rgba(167,139,250,.2), inset 0 3px 0 rgba(255,255,255,.35), inset 0 -6px 12px rgba(0,0,0,.35); } + 50% { box-shadow: 0 0 58px rgba(167,139,250,.85), 0 0 110px rgba(167,139,250,.35), inset 0 3px 0 rgba(255,255,255,.4), inset 0 -6px 12px rgba(0,0,0,.35); } + } + @keyframes uspPulseLight { + 0%,100% { box-shadow: 0 0 28px rgba(167,139,250,.4), 0 0 56px rgba(167,139,250,.15), inset 0 3px 0 rgba(255,255,255,.5), inset 0 -6px 12px rgba(0,0,0,.2); } + 50% { box-shadow: 0 0 44px rgba(167,139,250,.65), 0 0 80px rgba(167,139,250,.25), inset 0 3px 0 rgba(255,255,255,.55), inset 0 -6px 12px rgba(0,0,0,.2); } + } + @keyframes uspHeading { + 0%,100% { text-shadow: 0 0 22px rgba(167,139,250,.3); } + 50% { text-shadow: 0 0 36px rgba(167,139,250,.55); } + } +` - const subtitle = de - ? 'Die erste Plattform, die Compliance-Dokumente und tatsächliche Code-Umsetzung verbindet' - : 'The first platform that connects compliance documents with actual code implementation' +// ── 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 +} - const complianceItems = de - ? ['DSGVO-Dokumente', 'Audit-Management', 'RFQ-Anforderungen', 'CE-Bewertungen'] - : ['GDPR documents', 'Audit management', 'RFQ requirements', 'CE assessments'] +// ── Ticker ──────────────────────────────────────────────────────────────────── +function useTicker(fn: () => void, min = 180, max = 420, skip = 0.1) { + const ref = useRef(fn) + ref.current = fn + useEffect(() => { + let t: ReturnType + const loop = () => { + if (Math.random() > skip) ref.current() + t = setTimeout(loop, min + Math.random() * (max - min)) + } + loop() + return () => clearTimeout(t) + }, [min, max, skip]) +} - const codeItems = de - ? ['SAST / DAST / SBOM', 'Pentesting', 'Issue-Tracker', 'Auto-Fixes'] - : ['SAST / DAST / SBOM', 'Pentesting', 'Issue tracker', 'Auto-fixes'] +function TickerShell({ tint, isLight, children }: { tint: string; isLight: boolean; children: React.ReactNode }) { + return ( +
{children}
+ ) +} - const capabilities = [ - { - icon: GitPullRequest, - color: 'text-indigo-400', - label: de ? 'RFQ-Prüfung' : 'RFQ Verification', - desc: de - ? 'Kunden-Anforderungsdokumente werden automatisiert gegen die aktuelle Source-Code-Umsetzung geprüft. Abweichungen werden erkannt, Änderungen vorgeschlagen und auf Wunsch direkt im Code umgesetzt — ohne manuelles Nacharbeiten.' +function TickTrace({ tint, isLight }: { tint: string; isLight: boolean }) { + const [n, setN] = useState(12748) + useTicker(() => setN(v => v + 1 + Math.floor(Math.random() * 3)), 250, 500) + return ( + + + trace + {n.toLocaleString()} + evidence-chain + + ) +} + +function TickEngine({ tint, isLight }: { tint: string; isLight: boolean }) { + const [v, setV] = useState(428) + const [rate, setRate] = useState(99.4) + useTicker(() => { + setV(x => x + 1 + Math.floor(Math.random() * 4)) + setRate(r => Math.max(97, Math.min(99.9, r + (Math.random() - 0.5) * 0.3))) + }, 220, 420) + return ( + + + validate + {v.toLocaleString()} + {rate.toFixed(1)}% + + ) +} + +function TickOptimizer({ tint, isLight }: { tint: string; isLight: boolean }) { + const ops = ['ROI: 2.418 € / dev', 'gap → policy §4.2', 'dedup 128 tickets', 'sweet-spot: 22 KLOC', 'tradeoff: speed↔risk'] + const [i, setI] = useState(0) + useTicker(() => setI(x => (x + 1) % ops.length), 900, 1600, 0.05) + return ( + + + optimize + {ops[i]} + + ) +} + +function TickStack({ tint, isLight }: { tint: string; isLight: boolean }) { + const regs = ['DSGVO', 'NIS-2', 'DORA', 'EU AI Act', 'ISO 27001', 'BSI C5'] + const [i, setI] = useState(0) + const [c, setC] = useState(1208) + useTicker(() => { setI(x => (x + 1) % regs.length); setC(v => v + Math.floor(Math.random() * 3)) }, 800, 1400, 0.05) + return ( + + + check + {regs[i]} + · + {c.toLocaleString()} + + ) +} + +// ── Data ────────────────────────────────────────────────────────────────────── +interface DetailItem { + tint: string + icon: string + kicker: string + title: string + body: string + bullets?: string[] + stat?: { k: string; v: string } +} + +function getDetails(de: boolean): Record { + return { + rfq: { + tint: '#a78bfa', icon: '⇄', + kicker: de ? 'Säule · Compliance' : 'Pillar · Compliance', + title: de ? 'RFQ-Prüfung' : 'RFQ Verification', + body: de + ? 'Kunden-Anforderungsdokumente werden automatisch gegen den aktuellen Source-Code geprüft. Abweichungen werden erkannt, Änderungen vorgeschlagen und auf Wunsch direkt im Code umgesetzt — ohne manuelles Nacharbeiten.' : 'Customer requirement documents are automatically verified against current source code. Deviations are detected, changes proposed and implemented directly in code on request — no manual rework needed.', + bullets: de + ? ['Klauseln automatisch gegen SBOM, SAST-Findings und Policy-Docs abgeglichen', 'Lücken mit konkreten Implementierungsvorschlägen markiert', 'RFQ-Antworten in Stunden statt Wochen'] + : ['Auto-match clauses against SBOM, SAST findings and policy docs', 'Flag gaps with concrete implementation proposals', 'Win-ready RFQ replies in hours, not weeks'], + stat: { k: de ? 'Ø Antwortzeit' : 'avg response time', v: de ? '4,2h (war 12 Tage)' : '4.2h (was 12 days)' }, }, - { - icon: ArrowLeftRight, - color: 'text-purple-400', - label: de ? 'Bidirektional' : 'Bidirectional', - desc: de - ? 'Compliance-Anforderungen fliessen direkt in den Code. Umgekehrt aktualisieren Code-Änderungen automatisch die Compliance-Dokumentation. Beide Seiten sind immer synchron — kein Informationsverlust zwischen Audit und Entwicklung.' - : 'Compliance requirements flow directly into code. Conversely, code changes automatically update compliance documentation. Both sides always stay in sync — no information loss between audit and development.', - }, - { - icon: Zap, - color: 'text-amber-400', - label: de ? 'Prozess-Compliance' : 'Process Compliance', - desc: de + process: { + tint: '#c084fc', icon: '⟲', + kicker: de ? 'Säule · Compliance' : 'Pillar · Compliance', + title: de ? 'Prozess-Compliance' : 'Process Compliance', + body: de ? 'Vom Audit-Finding über das Ticket bis zur Code-Änderung läuft der gesamte Prozess automatisiert durch. Rollen, Fristen und Eskalation werden End-to-End verwaltet. Nachweise werden automatisch generiert und archiviert.' : 'From audit finding to ticket to code change, the entire process runs automatically. Roles, deadlines and escalation are managed end-to-end. Evidence is automatically generated and archived.', + bullets: de + ? ['Finding → Ticket → PR → Nachweis in einem Thread', 'SLA-Tracking pro Control mit Auto-Eskalation', 'Unveränderliches Audit-Log, pro Änderung signiert'] + : ['Finding → ticket → PR → evidence in one thread', 'SLA tracking per control with auto-escalation', 'Immutable audit log signed per change'], + stat: { k: de ? 'automatisierte Prozessschritte' : 'process steps automated', v: '87%' }, }, - { - icon: Shield, - color: 'text-emerald-400', - label: de ? 'Kontinuierlich' : 'Continuous', - desc: de + bidir: { + tint: '#fbbf24', icon: '⟷', + kicker: de ? 'Säule · Code' : 'Pillar · Code', + title: de ? 'Bidirektional' : 'Bidirectional Sync', + body: de + ? 'Compliance-Anforderungen fliessen direkt in den Code. Umgekehrt aktualisieren Code-Änderungen automatisch die Compliance-Dokumentation. Beide Seiten sind immer synchron — kein Informationsverlust zwischen Audit und Entwicklung.' + : 'Compliance requirements flow directly into code. Conversely, code changes automatically update compliance documentation. Both sides always stay in sync — no information loss between audit and development.', + bullets: de + ? ['Policy ↔ Code-Mapping via semantischem Diff', 'Git-nativ: jede Änderung als PR', 'Zero Drift zwischen Audit-Artefakten und Realität'] + : ['Policy ↔ code mapping via semantic diff', 'Git-native: every change shipped as a PR', 'Zero drift between audit artefacts and reality'], + stat: { k: de ? 'Drift-Vorfälle' : 'drift incidents', v: de ? '0 seit März 2024' : '0 since Mar-2024' }, + }, + cont: { + tint: '#f59e0b', icon: '◎', + kicker: de ? 'Säule · Code' : 'Pillar · Code', + title: de ? 'Kontinuierlich' : 'Continuous, Not Yearly', + body: de ? 'Klassische Compliance prüft einmal im Jahr und hofft auf das Beste. Unsere Plattform prüft bei jeder Code-Änderung. Findings werden sofort zu Tickets mit konkreten Implementierungsvorschlägen im Issue-Tracker der Wahl.' : 'Traditional compliance checks once a year and hopes for the best. Our platform checks on every code change. Findings immediately become tickets with concrete implementation proposals in the issue tracker of choice.', + bullets: de + ? ['CI-integrierte Validierung bei jedem Push', 'Fix-Vorschläge generiert, nicht nur gemeldet', 'Compliance-Frische: Minuten statt Monate'] + : ['CI-integrated validation on each push', 'Fix suggestions generated, not just reported', 'Compliance freshness: minutes, not months'], + stat: { k: de ? 'Validierungen / Tag' : 'validations / day', v: '~2.400 / repo' }, }, - ] + trace: { + tint: '#a78bfa', icon: '⇄', + kicker: de ? 'Under the Hood' : 'Under the Hood', + title: de ? 'End-to-End Rückverfolgbarkeit' : 'End-to-End Traceability', + body: de + ? 'Regulatorische Anforderungen (Gesetz → Obligation → Control) deterministisch mit realem Systemzustand und Code verknüpft — inklusive revisionssicherem Evidence-Layer.' + : 'Regulatory requirements (law → obligation → control) deterministically linked to real system state and code — including audit-proof evidence layer.', + bullets: de + ? ['Versionierter Evidence-Chain, unveränderlich gespeichert', 'Ein Klick von Klausel bis Codezeile', 'Signierte Attestierungen pro Build'] + : ['Versioned evidence chain stored immutably', 'One-click drill from clause to line of code', 'Signed attestations per build'], + }, + engine: { + tint: '#c084fc', icon: '◉', + kicker: de ? 'Under the Hood' : 'Under the Hood', + title: de ? 'Continuous Compliance Engine' : 'Continuous Compliance Engine', + body: de + ? 'Statt punktueller Audits: Validierung bei jeder Änderung (Code, Infrastruktur, Prozesse) mit auditierbaren Nachweisen in Echtzeit.' + : 'Instead of point-in-time audits: validation on every change (code, infrastructure, processes) with auditable evidence in real time.', + bullets: de + ? ['Rule-Packs pro Framework (NIS-2, DORA, …)', 'Verarbeitet Code, IaC und Prozess-Events', 'Findings automatisch ans richtige Team geroutet'] + : ['Rule packs per framework (NIS-2, DORA, …)', 'Handles code, infra-as-code, and process events', 'Findings routed to the right team automatically'], + }, + opt: { + tint: '#fbbf24', icon: '✦', + kicker: de ? 'Under the Hood' : 'Under the Hood', + title: de ? 'Compliance Optimizer' : 'Compliance Optimizer', + body: de + ? 'Nicht nur „erlaubt/verboten", sondern die maximal zulässige Ausgestaltung jedes KI-Use-Cases. Deterministische Constraint-Optimierung zeigt den Sweet Spot zwischen Regulierung und Innovation — ersetzt 20–200k EUR Anwaltskosten.' + : 'Not just "allowed/forbidden" but the maximum permissible configuration of every AI use case. Deterministic constraint optimization shows the sweet spot between regulation and innovation — replaces EUR 20–200k in legal fees.', + bullets: de + ? ['ROI-Ranking jedes offenen Findings', 'Abwägung zwischen Liefergeschwindigkeit und Restrisiko', 'Low-Hanging-Wins zuerst'] + : ['ROI-ranks every open finding', 'Balances speed of delivery with residual risk', 'Highlights low-hanging wins first'], + }, + stack: { + tint: '#f59e0b', icon: '◎', + kicker: de ? 'Under the Hood' : 'Under the Hood', + title: de ? 'EU-Trust & Governance Stack' : 'EU Trust & Governance Stack', + body: de + ? 'Souveräne, DSGVO-/AI-Act-konforme Architektur (EU-Hosting, Isolation, Betriebsrat-Fähigkeit) — Marktzugang, den US-Lösungen strukturell nicht erreichen.' + : 'Sovereign, GDPR/AI Act compliant architecture (EU hosting, isolation, works council capability) — market access that US solutions structurally cannot achieve.', + bullets: de + ? ['DSGVO · NIS-2 · DORA · EU AI Act · ISO 27001 · BSI C5', 'EU-souveränes Hosting und Key-Management', 'Eine Plattform, ein Audit, eine Rechnung'] + : ['DSGVO · NIS-2 · DORA · EU AI Act · ISO 27001 · BSI C5', 'EU-sovereign hosting and key-management', 'One platform, one audit, one bill'], + }, + hub: { + tint: '#a78bfa', icon: '∞', + kicker: de ? 'Die Schleife' : 'The Loop', + title: de ? 'Compliance ↔ Code · Immer in Sync' : 'Compliance ↔ Code · Always in sync', + body: de + ? 'Die Plattform ist eine einzige geschlossene Schleife. Jede Policy-Änderung fliesst in den Code; jede Code-Änderung fliesst in die Policy zurück.' + : 'The platform is a single closed loop. Every policy change ripples into code; every code change ripples back into policy. That\'s the USP in one diagram.', + bullets: de + ? ['Single Source of Truth, zwei Oberflächen', 'Echtzeit-Sync, kein Batch-Abgleich', 'Auditoren, Entwickler und Sales fragen denselben Graphen ab'] + : ['Single source of truth, two surfaces', 'Real-time sync, not batch reconciliation', 'Auditors, engineers and sales all query the same graph'], + }, + } +} + +// ── Pillar row ──────────────────────────────────────────────────────────────── +function PillarRow({ side, title, body, tint, onClick, active, isLight }: { + side: 'left' | 'right' + title: string; body: string; tint: string + onClick: () => void; active: boolean; isLight: boolean +}) { + const [hover, setHover] = useState(false) + const lit = hover || active + const isLeft = side === 'left' + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + style={{ + display: 'flex', alignItems: 'flex-start', gap: 12, + flexDirection: isLeft ? 'row-reverse' : 'row', + textAlign: isLeft ? 'right' : 'left', + padding: '10px 14px', borderRadius: 10, cursor: 'pointer', + transition: 'transform .25s, background .25s, box-shadow .25s', + background: lit + ? `linear-gradient(${isLeft ? '270deg' : '90deg'}, ${tint}24 0%, ${tint}0a 70%, transparent 100%)` + : 'transparent', + boxShadow: lit + ? `0 10px 30px ${tint}26, inset 0 0 0 1px ${tint}44` + : 'inset 0 0 0 1px transparent', + transform: lit ? (isLeft ? 'translateX(-3px)' : 'translateX(3px)') : 'translateX(0)', + }} + > +
+
+
+ {title} + {isLeft ? '‹' : '›'} +
+
{body}
+
+
+ ) +} + +// ── Column header ───────────────────────────────────────────────────────────── +function ColHeader({ side, label, color, icon, sub, isLight }: { + side: 'left' | 'right'; label: string; color: string; icon: string; sub: string; isLight: boolean +}) { + const isLeft = side === 'left' + return ( +
+
{icon}
+
+
{label}
+
{sub}
+
+
+ ) +} + +// ── Central hub ─────────────────────────────────────────────────────────────── +function CentralHub({ caption, isLight }: { caption: string; isLight: boolean }) { + return ( +
+
+
+
+ + + +
+
{caption}
+
+ ) +} + +// ── Bridge SVG connectors ───────────────────────────────────────────────────── +function BridgeConnectors({ isLight }: { isLight: boolean }) { + const rfpY = 130 + const sub2Y = 250 + const hubCx = 500 + const hubR = 72 + return ( + + + + + + + + + + + + + + + + + + + + {([rfpY, sub2Y] as number[]).map(y => ( + + + + + + + ))} + + + + + + + + + + + ) +} + +// ── Under-the-hood feature card ─────────────────────────────────────────────── +function FeatureCard({ icon, title, body, tint, Ticker, onClick, active, isLight }: { + icon: string; title: string; body: string; tint: string + Ticker: React.ComponentType<{ tint: string; isLight: boolean }> + onClick: () => void; active: boolean; isLight: boolean +}) { + const [hover, setHover] = useState(false) + const lit = hover || active + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + style={{ + position: 'relative', padding: '13px 15px', + background: isLight + ? lit + ? `linear-gradient(180deg, ${tint}18 0%, ${tint}08 55%, rgba(248,250,252,.95) 100%)` + : 'linear-gradient(180deg, #ffffff, #f8fafc)' + : `linear-gradient(180deg, ${tint}${lit ? '2a' : '1a'} 0%, ${tint}07 55%, rgba(14,8,28,.85) 100%)`, + border: `1px solid ${lit ? tint : isLight ? 'rgba(0,0,0,.1)' : tint + '4a'}`, + borderRadius: 12, + boxShadow: lit + ? `0 18px 40px ${tint}33, 0 0 0 1px ${tint}66, inset 0 1px 0 ${tint}60` + : isLight + ? '0 2px 8px rgba(0,0,0,.08), inset 0 1px 0 rgba(255,255,255,.8)' + : `0 10px 24px rgba(0,0,0,.4), inset 0 1px 0 ${tint}35`, + minWidth: 0, cursor: 'pointer', + transform: lit ? 'translateY(-3px)' : 'translateY(0)', + transition: 'transform .25s, box-shadow .25s, background .25s, border-color .25s', + }} + > +
+ {icon} + {title} + +
+
{body}
+ +
+ ) +} + +// ── Detail modal ────────────────────────────────────────────────────────────── +function DetailModal({ item, onClose, isLight }: { item: DetailItem | null; onClose: () => void; isLight: 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]) + + return ( + + {item && ( + + e.stopPropagation()} + style={{ + width: 560, maxWidth: '88%', + background: isLight + ? `linear-gradient(180deg, ${item.tint}10 0%, rgba(255,255,255,.98) 50%, rgba(248,250,252,.99) 100%)` + : `linear-gradient(180deg, ${item.tint}18 0%, rgba(20,10,40,.96) 50%, rgba(14,8,28,.98) 100%)`, + border: `1px solid ${item.tint}${isLight ? '44' : '66'}`, + borderRadius: 16, + boxShadow: isLight + ? `0 20px 60px rgba(0,0,0,.12), 0 0 40px ${item.tint}18, inset 0 1px 0 rgba(255,255,255,.9)` + : `0 30px 80px rgba(0,0,0,.6), 0 0 60px ${item.tint}33, inset 0 1px 0 ${item.tint}55`, + padding: '22px 26px', + color: isLight ? '#1a1a2e' : '#ece9f7', + }} + > +
+
{item.icon}
+
+
+ {item.kicker} +
+
{item.title}
+
+ +
+
+ {item.body} +
+ {item.bullets && ( +
+ {item.bullets.map((b, i) => ( +
+ + {b} +
+ ))} +
+ )} + {item.stat && ( +
+ + {item.stat.k} + {item.stat.v} +
+ )} +
+
+ )} +
+ ) +} + +// ── Star field ──────────────────────────────────────────────────────────────── +function StarField({ isLight }: { isLight: boolean }) { + const stars = useMemo(() => { + let s = 41 + const r = () => { s = (s * 9301 + 49297) % 233280; return s / 233280 } + return Array.from({ length: 90 }, () => ({ x: r() * 100, y: r() * 100, size: r() * 1.4 + 0.3, op: r() * 0.5 + 0.15 })) + }, []) + if (isLight) return null + return ( +
+ {stars.map((st, i) => ( +
+ ))} +
+ ) +} + +// ── Main slide ──────────────────────────────────────────────────────────────── +export default function USPSlide({ lang }: USPSlideProps) { + const de = lang === 'de' + const isLight = useIsLight() + const details = getDetails(de) + const [detail, setDetail] = useState(null) + const open = (k: string) => setDetail(details[k]) + const close = () => setDetail(null) return (
- -

+ + + +

USP

-

{subtitle}

- -
+ + {/* ── MAIN CANVAS ───────────────────────────────────────────────── */} +
+ {/* Ambient glow — dark only */} + {!isLight && ( +
+ )} + - {/* CENTER: Large circle */} -
-
-
- -
-
- -
-
- -
-
- - Compliance -
-
    - {complianceItems.map((item, idx) => ( -
  • - - {item} -
  • - ))} -
-
- -
-
- - Code -
-
    - {codeItems.map((item, idx) => ( -
  • - - {item} -
  • - ))} -
-
- -
-
-
-
+ {/* Interaction hint */} +
+ + {de ? 'Element anklicken' : 'Click any element'}
- {/* 4 CORNER CARDS */} - {capabilities.map((cap, idx) => { - const Icon = cap.icon - const posClass = idx === 0 ? 'top-0 left-0' - : idx === 1 ? 'top-0 right-0' - : idx === 2 ? 'bottom-0 left-0' - : 'bottom-0 right-0' - return ( -
- -
- -

{cap.label}

+ {/* Bridge */} +
+ +
+ {/* LEFT — Compliance */} +
+
+ +
+
+
+ open('rfq')} + active={detail?.title === details.rfq.title} + />
-

{cap.desc}

- +
+
+
+ open('process')} + active={detail?.title === details.process.title} + /> +
+
- ) - })} - {/* SVG connection lines */} - - - - - - -
- + {/* CENTER hub */} +
+
open('hub')} + style={{ cursor: 'pointer', transition: 'transform .25s, filter .25s' }} + onMouseEnter={e => { (e.currentTarget as HTMLDivElement).style.transform = 'scale(1.05)'; (e.currentTarget as HTMLDivElement).style.filter = 'brightness(1.15)' }} + onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.transform = 'scale(1)'; (e.currentTarget as HTMLDivElement).style.filter = 'brightness(1)' }} + > + +
+
- {/* MOAT — 3 Sätze */} - -

- {de ? 'Unser MOAT' : 'Our MOAT'} -

-
- -
- -

End-to-End Traceability

+ {/* RIGHT — Code */} +
+
+ +
+
+
+ open('bidir')} + active={detail?.title === details.bidir.title} + /> +
+
+
+
+ open('cont')} + active={detail?.title === details.cont.title} + /> +
+
+
-

- {de - ? 'Regulatorische Anforderungen (Gesetz → Obligation → Control) deterministisch mit realem Systemzustand und Code verknüpft — inklusive revisionssicherem Evidence-Layer.' - : 'Regulatory requirements (law → obligation → control) deterministically linked to real system state and code — including audit-proof evidence layer.'} -

-
- -
- -

Continuous Compliance Engine

-
-

- {de - ? 'Statt punktueller Audits: Validierung bei jeder Änderung (Code, Infrastruktur, Prozesse) mit auditierbaren Nachweisen in Echtzeit.' - : 'Instead of point-in-time audits: validation on every change (code, infrastructure, processes) with auditable evidence in real time.'} -

-
- -
- -

Compliance Optimizer

-
-

- {de - ? 'Nicht nur „erlaubt/verboten", sondern die maximal zulässige Ausgestaltung jedes KI-Use-Cases. Deterministische Constraint-Optimierung zeigt den Sweet Spot zwischen Regulierung und Innovation — ersetzt 20-200k EUR Anwaltskosten.' - : 'Not just "allowed/forbidden" but the maximum permissible configuration of every AI use case. Deterministic constraint optimization shows the sweet spot between regulation and innovation — replaces EUR 20-200k in legal fees.'} -

-
- -
- -

EU-Trust & Governance Stack

-
-

- {de - ? 'Souveräne, DSGVO-/AI-Act-konforme Architektur (EU-Hosting, Isolation, Betriebsrat-Fähigkeit) — Marktzugang, den US-Lösungen strukturell nicht erreichen.' - : 'Sovereign, GDPR/AI Act compliant architecture (EU hosting, isolation, works council capability) — market access that US solutions structurally cannot achieve.'} -

-
-
-
+
- {/* Killer Quote */} - -
-

+ {/* Under the Hood */} +

+
+ + {de ? 'Unter der Haube' : 'Under the Hood'} + +
+
+ open('trace')} + active={detail?.title === details.trace.title} + Ticker={TickTrace} + /> + open('engine')} + active={detail?.title === details.engine.title} + Ticker={TickEngine} + /> + open('opt')} + active={detail?.title === details.opt.title} + Ticker={TickOptimizer} + /> + open('stack')} + active={detail?.title === details.stack.title} + Ticker={TickStack} + /> +
+
+ + {/* Killer quote */} +
+ " {de - ? '„Jeder kann sagen, was verboten ist. Kaum jemand kann sagen, wie weit du maximal gehen kannst. Das ist unser Produkt."' - : '"Everyone can say what is forbidden. Almost no one can say how far you can go. That is our product."'} -

+ ? 'Jeder kann sagen, was verboten ist. Kaum jemand kann sagen, wie weit du maximal gehen kannst. Das ist unser Produkt.' + : 'Anyone can say what\'s forbidden. Almost no one can tell you how far you can actually go. That\'s our product.'} + " +
+ +
diff --git a/pitch-deck/lib/i18n.ts b/pitch-deck/lib/i18n.ts index a3e68b3..b6abff2 100644 --- a/pitch-deck/lib/i18n.ts +++ b/pitch-deck/lib/i18n.ts @@ -26,7 +26,7 @@ const translations = { 'Investition & Cap Table', 'Kundenersparnis', 'KI Q&A', - 'Anhang: Architektur', + 'Anhang: Systemarchitektur', 'Anhang: Regulatorik', 'Anhang: Engineering', 'Anhang: KI-Pipeline', @@ -276,8 +276,8 @@ const translations = { subtitle: 'Drei Szenarien für robuste Planung', }, architecture: { - title: 'Technische Architektur', - subtitle: 'Self-Hosted KI-Stack für maximale Datensouveränität', + title: 'Systemarchitektur', + subtitle: 'BreakPilot · CERTifAI · Compliance Scanner — verbunden über LiteLLM', }, gtm: { title: 'Go-to-Market Strategie', @@ -322,7 +322,7 @@ const translations = { 'Investment & Cap Table', 'Customer Savings', 'AI Q&A', - 'Appendix: Architecture', + 'Appendix: System Architecture', 'Appendix: Regulatory', 'Appendix: Engineering', 'Appendix: AI Pipeline', @@ -572,8 +572,8 @@ const translations = { subtitle: 'Three scenarios for robust planning', }, architecture: { - title: 'Technical Architecture', - subtitle: 'Self-hosted AI stack for maximum data sovereignty', + title: 'System Architecture', + subtitle: 'BreakPilot · CERTifAI · Compliance Scanner — connected via LiteLLM', }, gtm: { title: 'Go-to-Market Strategy', diff --git a/pitch-deck/middleware.ts b/pitch-deck/middleware.ts index 5a0f33d..2cfd3ba 100644 --- a/pitch-deck/middleware.ts +++ b/pitch-deck/middleware.ts @@ -34,6 +34,11 @@ export async function middleware(request: NextRequest) { const { pathname } = request.nextUrl const secret = process.env.PITCH_JWT_SECRET + // Skip all auth in local dev when no secret is configured + if (!secret && process.env.NODE_ENV === 'development') { + return NextResponse.next() + } + // Allow public paths if (isPublicPath(pathname)) { return NextResponse.next() diff --git a/pitch-deck/package.json b/pitch-deck/package.json index 2c0a658..e78f486 100644 --- a/pitch-deck/package.json +++ b/pitch-deck/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "scripts": { - "dev": "next dev -p 3012", + "dev": "next dev --turbopack -p 3012", "build": "next build", "start": "next start -p 3012", "admin:create": "tsx scripts/create-admin.ts",