feat(pitch-deck): redesign USP slide with symmetric bridge composition
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m10s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 32s
CI / test-python-voice (push) Successful in 31s
CI / test-bqas (push) Successful in 30s
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m10s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 32s
CI / test-python-voice (push) Successful in 31s
CI / test-bqas (push) Successful in 30s
Port Claude Design's USP v2 exactly: Compliance ↔ Code bridge with a central glowing orb, two pillar rows per column, animated SVG flow connectors, and four Under-the-Hood feature cards with live tickers. Detail modal (Framer Motion AnimatePresence) slides in on click for all 9 interactive elements. Star field background. All text is our actual content (RFQ Verification, Process Compliance, Bidirectional Sync, Continuous, End-to-End Traceability, Compliance Optimizer, EU Trust Stack, killer quote). Full DE/EN i18n. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,222 +1,789 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import { useState, useEffect, useRef, useMemo } from 'react'
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
import { Language } from '@/lib/types'
|
import { Language } from '@/lib/types'
|
||||||
import GradientText from '../ui/GradientText'
|
import GradientText from '../ui/GradientText'
|
||||||
import FadeInView from '../ui/FadeInView'
|
import FadeInView from '../ui/FadeInView'
|
||||||
import GlassCard from '../ui/GlassCard'
|
import { X } from 'lucide-react'
|
||||||
import {
|
|
||||||
FileCheck,
|
|
||||||
Code,
|
|
||||||
Zap,
|
|
||||||
Shield,
|
|
||||||
GitPullRequest,
|
|
||||||
ArrowLeftRight,
|
|
||||||
} from 'lucide-react'
|
|
||||||
|
|
||||||
interface USPSlideProps {
|
interface USPSlideProps { lang: Language }
|
||||||
lang: Language
|
|
||||||
|
const MONO: React.CSSProperties = {
|
||||||
|
fontFamily: '"JetBrains Mono","SF Mono",ui-monospace,monospace',
|
||||||
|
fontVariantNumeric: 'tabular-nums',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function USPSlide({ lang }: USPSlideProps) {
|
const CSS_KF = `
|
||||||
const de = lang === 'de'
|
@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 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
|
// ── Ticker ────────────────────────────────────────────────────────────────────
|
||||||
? 'Die erste Plattform, die Compliance-Dokumente und tatsächliche Code-Umsetzung verbindet'
|
function useTicker(fn: () => void, min = 180, max = 420, skip = 0.1) {
|
||||||
: 'The first platform that connects compliance documents with actual code implementation'
|
const ref = useRef(fn)
|
||||||
|
ref.current = fn
|
||||||
|
useEffect(() => {
|
||||||
|
let t: ReturnType<typeof setTimeout>
|
||||||
|
const loop = () => {
|
||||||
|
if (Math.random() > skip) ref.current()
|
||||||
|
t = setTimeout(loop, min + Math.random() * (max - min))
|
||||||
|
}
|
||||||
|
loop()
|
||||||
|
return () => clearTimeout(t)
|
||||||
|
}, [min, max, skip])
|
||||||
|
}
|
||||||
|
|
||||||
const complianceItems = de
|
function TickerShell({ tint, children }: { tint: string; children: React.ReactNode }) {
|
||||||
? ['DSGVO-Dokumente', 'Audit-Management', 'RFQ-Anforderungen', 'CE-Bewertungen']
|
return (
|
||||||
: ['GDPR documents', 'Audit management', 'RFQ requirements', 'CE assessments']
|
<div style={{
|
||||||
|
...MONO, marginTop: 10, padding: '5px 9px',
|
||||||
|
background: 'rgba(0,0,0,.38)', border: `1px solid ${tint}55`,
|
||||||
|
borderRadius: 6, fontSize: 10.5, color: 'rgba(236,233,247,.88)',
|
||||||
|
display: 'flex', alignItems: 'center', gap: 7,
|
||||||
|
whiteSpace: 'nowrap', overflow: 'hidden', height: 22,
|
||||||
|
}}>{children}</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const codeItems = de
|
function TickTrace({ tint }: { tint: string }) {
|
||||||
? ['SAST / DAST / SBOM', 'Pentesting', 'Issue-Tracker', 'Auto-Fixes']
|
const [n, setN] = useState(12748)
|
||||||
: ['SAST / DAST / SBOM', 'Pentesting', 'Issue tracker', 'Auto-fixes']
|
useTicker(() => setN(v => v + 1 + Math.floor(Math.random() * 3)), 250, 500)
|
||||||
|
return (
|
||||||
|
<TickerShell tint={tint}>
|
||||||
|
<span style={{ color: '#4ade80' }}>●</span>
|
||||||
|
<span style={{ color: tint, opacity: .85 }}>trace</span>
|
||||||
|
<span style={{ color: '#f5f3fc', fontWeight: 600 }}>{n.toLocaleString()}</span>
|
||||||
|
<span style={{ color: 'rgba(236,233,247,.45)' }}>evidence-chain</span>
|
||||||
|
</TickerShell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const capabilities = [
|
function TickEngine({ tint }: { tint: string }) {
|
||||||
{
|
const [v, setV] = useState(428)
|
||||||
icon: GitPullRequest,
|
const [rate, setRate] = useState(99.4)
|
||||||
color: 'text-indigo-400',
|
useTicker(() => {
|
||||||
label: de ? 'RFQ-Prüfung' : 'RFQ Verification',
|
setV(x => x + 1 + Math.floor(Math.random() * 4))
|
||||||
desc: de
|
setRate(r => Math.max(97, Math.min(99.9, r + (Math.random() - 0.5) * 0.3)))
|
||||||
? '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.'
|
}, 220, 420)
|
||||||
|
return (
|
||||||
|
<TickerShell tint={tint}>
|
||||||
|
<span style={{ color: '#4ade80' }}>●</span>
|
||||||
|
<span style={{ color: tint, opacity: .85 }}>validate</span>
|
||||||
|
<span style={{ color: '#f5f3fc', fontWeight: 600 }}>{v.toLocaleString()}</span>
|
||||||
|
<span style={{ color: '#4ade80' }}>{rate.toFixed(1)}%</span>
|
||||||
|
</TickerShell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TickOptimizer({ tint }: { tint: string }) {
|
||||||
|
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 (
|
||||||
|
<TickerShell tint={tint}>
|
||||||
|
<span style={{ color: '#fbbf24' }}>✦</span>
|
||||||
|
<span style={{ color: tint, opacity: .85 }}>optimize</span>
|
||||||
|
<span style={{ color: '#f5f3fc', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }}>{ops[i]}</span>
|
||||||
|
</TickerShell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TickStack({ tint }: { tint: string }) {
|
||||||
|
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 (
|
||||||
|
<TickerShell tint={tint}>
|
||||||
|
<span style={{ color: '#4ade80' }}>●</span>
|
||||||
|
<span style={{ color: tint, opacity: .85 }}>check</span>
|
||||||
|
<span style={{ color: '#f5f3fc', fontWeight: 600 }}>{regs[i]}</span>
|
||||||
|
<span style={{ color: 'rgba(236,233,247,.4)' }}>·</span>
|
||||||
|
<span style={{ color: '#f5f3fc' }}>{c.toLocaleString()}</span>
|
||||||
|
</TickerShell>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 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<string, DetailItem> {
|
||||||
|
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.',
|
: '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)' },
|
||||||
},
|
},
|
||||||
{
|
process: {
|
||||||
icon: ArrowLeftRight,
|
tint: '#c084fc', icon: '⟲',
|
||||||
color: 'text-purple-400',
|
kicker: de ? 'Säule · Compliance' : 'Pillar · Compliance',
|
||||||
label: de ? 'Bidirektional' : 'Bidirectional',
|
title: de ? 'Prozess-Compliance' : 'Process Compliance',
|
||||||
desc: de
|
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.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: Zap,
|
|
||||||
color: 'text-amber-400',
|
|
||||||
label: de ? 'Prozess-Compliance' : 'Process Compliance',
|
|
||||||
desc: 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.'
|
? '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.',
|
: '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%' },
|
||||||
},
|
},
|
||||||
{
|
bidir: {
|
||||||
icon: Shield,
|
tint: '#fbbf24', icon: '⟷',
|
||||||
color: 'text-emerald-400',
|
kicker: de ? 'Säule · Code' : 'Pillar · Code',
|
||||||
label: de ? 'Kontinuierlich' : 'Continuous',
|
title: de ? 'Bidirektional' : 'Bidirectional Sync',
|
||||||
desc: de
|
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.'
|
? '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.',
|
: '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 }: {
|
||||||
|
side: 'left' | 'right'
|
||||||
|
title: string; body: string; tint: string
|
||||||
|
onClick: () => void; active: boolean
|
||||||
|
}) {
|
||||||
|
const [hover, setHover] = useState(false)
|
||||||
|
const lit = hover || active
|
||||||
|
const isLeft = side === 'left'
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={onClick}
|
||||||
|
onMouseEnter={() => 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)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{
|
||||||
|
flex: '0 0 30px', width: 30, height: 30, borderRadius: 9,
|
||||||
|
background: lit ? `${tint}3a` : `${tint}22`,
|
||||||
|
border: `1px solid ${lit ? tint : tint + '66'}`,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
color: lit ? '#fff' : tint, fontSize: 13, fontWeight: 700, marginTop: 2,
|
||||||
|
boxShadow: lit ? `0 0 14px ${tint}88, inset 0 1px 0 ${tint}80` : `inset 0 1px 0 ${tint}50`,
|
||||||
|
transition: 'all .25s',
|
||||||
|
}}>◆</div>
|
||||||
|
<div style={{ flex: 1, minWidth: 0 }}>
|
||||||
|
<div style={{
|
||||||
|
fontSize: 13, fontWeight: 700, color: '#f7f5fc', letterSpacing: -0.15, marginBottom: 3,
|
||||||
|
display: 'flex', alignItems: 'center', gap: 6,
|
||||||
|
justifyContent: isLeft ? 'flex-end' : 'flex-start',
|
||||||
|
}}>
|
||||||
|
<span>{title}</span>
|
||||||
|
<span style={{
|
||||||
|
fontSize: 10, color: tint, opacity: lit ? 1 : 0,
|
||||||
|
transform: `translateX(${lit ? 0 : (isLeft ? 4 : -4)}px)`,
|
||||||
|
transition: 'all .25s',
|
||||||
|
}}>{isLeft ? '‹' : '›'}</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 11, lineHeight: 1.55, color: `rgba(236,233,247,${lit ? .82 : .62})`, transition: 'color .25s' }}>{body}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Column header ─────────────────────────────────────────────────────────────
|
||||||
|
function ColHeader({ side, label, color, icon, sub }: {
|
||||||
|
side: 'left' | 'right'; label: string; color: string; icon: string; sub: string
|
||||||
|
}) {
|
||||||
|
const isLeft = side === 'left'
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex', alignItems: 'center', gap: 10,
|
||||||
|
flexDirection: isLeft ? 'row-reverse' : 'row',
|
||||||
|
paddingBottom: 10, borderBottom: `1px solid ${color}35`,
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
width: 34, height: 34, borderRadius: 9,
|
||||||
|
background: `linear-gradient(135deg, ${color}55, ${color}20)`,
|
||||||
|
border: `1px solid ${color}88`,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
color: '#fff', fontSize: 15, fontWeight: 700,
|
||||||
|
boxShadow: `0 0 18px ${color}55, inset 0 1px 0 ${color}aa`,
|
||||||
|
}}>{icon}</div>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontSize: 18, fontWeight: 700, color: '#f7f5fc', letterSpacing: -0.3, lineHeight: 1 }}>{label}</div>
|
||||||
|
<div style={{ ...MONO, fontSize: 9.5, letterSpacing: 2, color, opacity: .75, marginTop: 3, textTransform: 'uppercase' as const }}>{sub}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Central hub ───────────────────────────────────────────────────────────────
|
||||||
|
function CentralHub({ caption }: { caption: string }) {
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'relative', width: 260, height: 320, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
<div style={{
|
||||||
|
position: 'relative', width: 120, height: 120, borderRadius: '50%',
|
||||||
|
background: 'radial-gradient(circle at 32% 28%, #f0e9ff 0%, #c4aaff 26%, #7b5cd6 58%, #2a1560 100%)',
|
||||||
|
border: '1.5px solid rgba(216,202,255,.7)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
boxShadow: '0 0 50px rgba(167,139,250,.65), 0 0 100px rgba(167,139,250,.25), inset 0 3px 0 rgba(255,255,255,.35), inset 0 -8px 14px rgba(0,0,0,.35)',
|
||||||
|
animation: 'uspPulse 2.6s ease-in-out infinite', zIndex: 3,
|
||||||
|
}}>
|
||||||
|
<div style={{ position: 'absolute', inset: -14, borderRadius: '50%', border: '1px dashed rgba(216,202,255,.42)', animation: 'uspSpin 14s linear infinite' }} />
|
||||||
|
<div style={{ position: 'absolute', inset: -30, borderRadius: '50%', border: '1px dashed rgba(216,202,255,.2)', animation: 'uspSpin 22s linear infinite reverse' }} />
|
||||||
|
<svg width="54" height="26" viewBox="0 0 54 26" fill="none" stroke="#fff" strokeWidth="2.8" strokeLinecap="round" strokeLinejoin="round"
|
||||||
|
style={{ filter: 'drop-shadow(0 1px 3px rgba(0,0,0,.5))' }}>
|
||||||
|
<path d="M 10 13 C 10 5, 22 5, 27 13 C 32 21, 44 21, 44 13 C 44 5, 32 5, 27 13 C 22 21, 10 21, 10 13 Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', left: 0, right: 0, bottom: 24, textAlign: 'center',
|
||||||
|
...MONO, fontSize: 9.5, letterSpacing: 2.5, color: 'rgba(216,202,255,.75)',
|
||||||
|
textTransform: 'uppercase' as const, fontWeight: 600,
|
||||||
|
}}>{caption}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bridge SVG connectors ─────────────────────────────────────────────────────
|
||||||
|
function BridgeConnectors() {
|
||||||
|
const rfpY = 130
|
||||||
|
const sub2Y = 250
|
||||||
|
const hubCx = 500
|
||||||
|
const hubR = 72
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 1000 400" preserveAspectRatio="none"
|
||||||
|
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', pointerEvents: 'none', zIndex: 1 }}>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="uspFromL" x1="0" x2="1">
|
||||||
|
<stop offset="0" stopColor="#a78bfa" stopOpacity="0" />
|
||||||
|
<stop offset=".3" stopColor="#a78bfa" stopOpacity=".85" />
|
||||||
|
<stop offset="1" stopColor="#c084fc" stopOpacity=".3" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="uspToR" x1="0" x2="1">
|
||||||
|
<stop offset="0" stopColor="#c084fc" stopOpacity=".3" />
|
||||||
|
<stop offset=".7" stopColor="#fbbf24" stopOpacity=".85" />
|
||||||
|
<stop offset="1" stopColor="#fbbf24" stopOpacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<line x1="40" y1={rfpY} x2={hubCx - hubR} y2={rfpY}
|
||||||
|
stroke="url(#uspFromL)" strokeWidth="2" strokeDasharray="4 5"
|
||||||
|
style={{ animation: 'uspFlowR 1.6s linear infinite' }} />
|
||||||
|
<line x1={hubCx + hubR} y1={rfpY} x2="960" y2={rfpY}
|
||||||
|
stroke="url(#uspToR)" strokeWidth="2" strokeDasharray="4 5"
|
||||||
|
style={{ animation: 'uspFlowR 1.6s linear infinite' }} />
|
||||||
|
<line x1="40" y1={sub2Y} x2={hubCx - hubR} y2={sub2Y}
|
||||||
|
stroke="url(#uspFromL)" strokeWidth="2" strokeDasharray="4 5"
|
||||||
|
style={{ animation: 'uspFlowR 1.6s linear infinite' }} />
|
||||||
|
<line x1={hubCx + hubR} y1={sub2Y} x2="960" y2={sub2Y}
|
||||||
|
stroke="url(#uspToR)" strokeWidth="2" strokeDasharray="4 5"
|
||||||
|
style={{ animation: 'uspFlowR 1.6s linear infinite' }} />
|
||||||
|
|
||||||
|
{([rfpY, sub2Y] as number[]).map(y => (
|
||||||
|
<g key={y}>
|
||||||
|
<circle cx={hubCx - hubR} cy={y} r="4" fill="#1a0f34" stroke="#a78bfa" strokeWidth="1.2" />
|
||||||
|
<circle cx={hubCx - hubR} cy={y} r="1.5" fill="#a78bfa" />
|
||||||
|
<circle cx={hubCx + hubR} cy={y} r="4" fill="#1a0f34" stroke="#fbbf24" strokeWidth="1.2" />
|
||||||
|
<circle cx={hubCx + hubR} cy={y} r="1.5" fill="#fbbf24" />
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<circle r="3" fill="#c4aaff" style={{ filter: 'drop-shadow(0 0 6px #a78bfa)' }}>
|
||||||
|
<animate attributeName="cx" from="40" to="960" dur="3.5s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="cy" values={`${rfpY};${rfpY}`} dur="3.5s" repeatCount="indefinite" />
|
||||||
|
</circle>
|
||||||
|
<circle r="3" fill="#fde68a" style={{ filter: 'drop-shadow(0 0 6px #fbbf24)' }}>
|
||||||
|
<animate attributeName="cx" from="960" to="40" dur="3.5s" repeatCount="indefinite" />
|
||||||
|
<animate attributeName="cy" values={`${sub2Y};${sub2Y}`} dur="3.5s" repeatCount="indefinite" />
|
||||||
|
</circle>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Under-the-hood feature card ───────────────────────────────────────────────
|
||||||
|
function FeatureCard({ icon, title, body, tint, Ticker, onClick, active }: {
|
||||||
|
icon: string; title: string; body: string; tint: string
|
||||||
|
Ticker: React.ComponentType<{ tint: string }>
|
||||||
|
onClick: () => void; active: boolean
|
||||||
|
}) {
|
||||||
|
const [hover, setHover] = useState(false)
|
||||||
|
const lit = hover || active
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={onClick}
|
||||||
|
onMouseEnter={() => setHover(true)}
|
||||||
|
onMouseLeave={() => setHover(false)}
|
||||||
|
style={{
|
||||||
|
position: 'relative', padding: '13px 15px',
|
||||||
|
background: `linear-gradient(180deg, ${tint}${lit ? '2a' : '1a'} 0%, ${tint}07 55%, rgba(14,8,28,.85) 100%)`,
|
||||||
|
border: `1px solid ${lit ? tint : tint + '4a'}`,
|
||||||
|
borderRadius: 12,
|
||||||
|
boxShadow: lit
|
||||||
|
? `0 18px 40px ${tint}33, 0 0 0 1px ${tint}66, inset 0 1px 0 ${tint}60`
|
||||||
|
: `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',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
|
||||||
|
<span style={{
|
||||||
|
width: 22, height: 22, borderRadius: 6,
|
||||||
|
background: lit ? `${tint}44` : `${tint}22`,
|
||||||
|
border: `1px solid ${lit ? tint : tint + '66'}`,
|
||||||
|
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
color: lit ? '#fff' : tint, fontSize: 12,
|
||||||
|
boxShadow: lit ? `0 0 12px ${tint}88` : 'none',
|
||||||
|
transition: 'all .25s',
|
||||||
|
}}>{icon}</span>
|
||||||
|
<span style={{ fontSize: 12.5, fontWeight: 700, color: '#f7f5fc', letterSpacing: -0.15, flex: 1 }}>{title}</span>
|
||||||
|
<span style={{ fontSize: 10, color: tint, opacity: lit ? 1 : 0.5, transform: `translateX(${lit ? 0 : -3}px)`, transition: 'all .25s' }}>↗</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 11, lineHeight: 1.45, color: `rgba(236,233,247,${lit ? .82 : .65})`, transition: 'color .25s' }}>{body}</div>
|
||||||
|
<Ticker tint={tint} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Detail modal ──────────────────────────────────────────────────────────────
|
||||||
|
function DetailModal({ item, onClose }: { item: DetailItem | null; onClose: () => void }) {
|
||||||
|
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 (
|
||||||
|
<AnimatePresence>
|
||||||
|
{item && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
onClick={onClose}
|
||||||
|
style={{
|
||||||
|
position: 'absolute', inset: 0, zIndex: 50,
|
||||||
|
background: 'rgba(5,2,16,.72)', backdropFilter: 'blur(6px)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.94 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.94 }}
|
||||||
|
transition={{ duration: 0.22 }}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
style={{
|
||||||
|
width: 560, maxWidth: '88%',
|
||||||
|
background: `linear-gradient(180deg, ${item.tint}18 0%, rgba(20,10,40,.96) 50%, rgba(14,8,28,.98) 100%)`,
|
||||||
|
border: `1px solid ${item.tint}66`,
|
||||||
|
borderRadius: 16,
|
||||||
|
boxShadow: `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: '#ece9f7',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
|
||||||
|
<div style={{
|
||||||
|
width: 38, height: 38, borderRadius: 10,
|
||||||
|
background: `linear-gradient(135deg, ${item.tint}66, ${item.tint}22)`,
|
||||||
|
border: `1px solid ${item.tint}`,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
color: '#fff', fontSize: 16, fontWeight: 700,
|
||||||
|
boxShadow: `0 0 18px ${item.tint}66`,
|
||||||
|
}}>{item.icon}</div>
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div style={{ ...MONO, fontSize: 9.5, letterSpacing: 2.5, color: item.tint, textTransform: 'uppercase' as const, fontWeight: 600, marginBottom: 2 }}>
|
||||||
|
{item.kicker}
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 19, fontWeight: 700, color: '#f7f5fc', letterSpacing: -0.3 }}>{item.title}</div>
|
||||||
|
</div>
|
||||||
|
<button onClick={onClose} style={{
|
||||||
|
background: 'transparent', border: `1px solid ${item.tint}55`,
|
||||||
|
borderRadius: 8, cursor: 'pointer', width: 30, height: 30,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
color: 'rgba(236,233,247,.6)',
|
||||||
|
}}>
|
||||||
|
<X style={{ width: 14, height: 14 }} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 13, lineHeight: 1.6, color: 'rgba(236,233,247,.82)', marginBottom: 16 }}>
|
||||||
|
{item.body}
|
||||||
|
</div>
|
||||||
|
{item.bullets && (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 14 }}>
|
||||||
|
{item.bullets.map((b, i) => (
|
||||||
|
<div key={i} style={{
|
||||||
|
display: 'flex', alignItems: 'flex-start', gap: 10,
|
||||||
|
padding: '8px 12px', borderRadius: 8,
|
||||||
|
background: 'rgba(0,0,0,.3)', border: `1px solid ${item.tint}33`,
|
||||||
|
}}>
|
||||||
|
<span style={{ color: item.tint, fontSize: 12, marginTop: 1 }}>▸</span>
|
||||||
|
<span style={{ fontSize: 12, lineHeight: 1.5, color: 'rgba(236,233,247,.78)' }}>{b}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.stat && (
|
||||||
|
<div style={{
|
||||||
|
...MONO, padding: '10px 14px', borderRadius: 8,
|
||||||
|
background: 'rgba(0,0,0,.45)', border: `1px solid ${item.tint}55`,
|
||||||
|
fontSize: 12, color: 'rgba(236,233,247,.9)',
|
||||||
|
display: 'flex', alignItems: 'center', gap: 10,
|
||||||
|
}}>
|
||||||
|
<span style={{ color: '#4ade80' }}>●</span>
|
||||||
|
<span style={{ color: item.tint }}>{item.stat.k}</span>
|
||||||
|
<span style={{ color: '#f5f3fc', fontWeight: 600 }}>{item.stat.v}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Star field ────────────────────────────────────────────────────────────────
|
||||||
|
function StarField() {
|
||||||
|
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 }))
|
||||||
|
}, [])
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
|
||||||
|
{stars.map((st, i) => (
|
||||||
|
<div key={i} style={{
|
||||||
|
position: 'absolute', left: `${st.x}%`, top: `${st.y}%`,
|
||||||
|
width: st.size, height: st.size, borderRadius: '50%',
|
||||||
|
background: '#fff', opacity: st.op,
|
||||||
|
boxShadow: `0 0 ${st.size * 3}px rgba(180,160,255,.7)`,
|
||||||
|
}} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Main slide ────────────────────────────────────────────────────────────────
|
||||||
|
export default function USPSlide({ lang }: USPSlideProps) {
|
||||||
|
const de = lang === 'de'
|
||||||
|
const details = getDetails(de)
|
||||||
|
const [detail, setDetail] = useState<DetailItem | null>(null)
|
||||||
|
const open = (k: string) => setDetail(details[k])
|
||||||
|
const close = () => setDetail(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FadeInView className="text-center mb-8">
|
<style>{CSS_KF}</style>
|
||||||
<h2 className="text-4xl md:text-5xl font-bold mb-3">
|
|
||||||
|
<FadeInView className="text-center mb-4">
|
||||||
|
<h2 className="text-4xl md:text-5xl font-bold mb-2">
|
||||||
<GradientText>USP</GradientText>
|
<GradientText>USP</GradientText>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg text-white/50 max-w-3xl mx-auto">{subtitle}</p>
|
|
||||||
</FadeInView>
|
</FadeInView>
|
||||||
|
|
||||||
<FadeInView delay={0.2}>
|
<FadeInView delay={0.1}>
|
||||||
<div className="relative max-w-6xl mx-auto" style={{ height: '580px' }}>
|
{/* ── MAIN CANVAS ───────────────────────────────────────────────── */}
|
||||||
|
<div style={{
|
||||||
|
position: 'relative', overflow: 'hidden', borderRadius: 16,
|
||||||
|
background: 'radial-gradient(ellipse at 50% 30%, #1a0f34 0%, #0e0720 55%, #050210 100%)',
|
||||||
|
color: '#ece9f7', fontFamily: '"Inter", system-ui, sans-serif',
|
||||||
|
WebkitFontSmoothing: 'antialiased',
|
||||||
|
}}>
|
||||||
|
{/* Ambient glow */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: -120, left: '50%', transform: 'translateX(-50%)',
|
||||||
|
width: 800, height: 500, borderRadius: '50%',
|
||||||
|
background: 'radial-gradient(ellipse, rgba(167,139,250,.2), transparent 65%)',
|
||||||
|
filter: 'blur(50px)', pointerEvents: 'none',
|
||||||
|
}} />
|
||||||
|
<StarField />
|
||||||
|
|
||||||
{/* CENTER: Large circle */}
|
{/* Interaction hint */}
|
||||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" style={{ width: '440px', height: '440px' }}>
|
<div style={{
|
||||||
<div className="absolute inset-0 rounded-full border-2 border-dashed border-indigo-500/20 animate-[spin_20s_linear_infinite]" />
|
position: 'absolute', top: 20, right: 28, zIndex: 5,
|
||||||
<div className="absolute inset-4 rounded-full border border-white/[0.06] bg-white/[0.015]" />
|
...MONO, fontSize: 9.5, letterSpacing: 2, color: 'rgba(167,139,250,.55)',
|
||||||
|
textTransform: 'uppercase', fontWeight: 600,
|
||||||
|
display: 'flex', alignItems: 'center', gap: 6,
|
||||||
|
}}>
|
||||||
|
<span style={{ display: 'inline-block', width: 6, height: 6, borderRadius: '50%', background: '#4ade80', boxShadow: '0 0 8px #4ade80' }} />
|
||||||
|
{de ? 'Element anklicken' : 'Click any element'}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center z-10">
|
{/* Heading */}
|
||||||
<div className="w-20 h-20 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shadow-xl shadow-indigo-500/30">
|
<div style={{ position: 'relative', padding: '30px 48px 0', textAlign: 'center', zIndex: 2 }}>
|
||||||
<span className="text-3xl font-black text-white">∞</span>
|
<div style={{ ...MONO, fontSize: 10, letterSpacing: 4, color: 'rgba(167,139,250,.7)', textTransform: 'uppercase', fontWeight: 600, marginBottom: 6 }}>
|
||||||
|
{de ? 'Alleinstellungsmerkmal' : 'Unique Selling Proposition'}
|
||||||
|
</div>
|
||||||
|
<h1 style={{
|
||||||
|
fontSize: 34, fontWeight: 700, letterSpacing: -0.8, margin: 0, lineHeight: 1.2,
|
||||||
|
color: '#f7f5fc', animation: 'uspHeading 4s ease-in-out infinite',
|
||||||
|
}}>
|
||||||
|
{de ? 'Die erste Plattform, die ' : 'The first platform bridging '}
|
||||||
|
<span style={{ background: 'linear-gradient(90deg, #c4aaff, #a78bfa)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
|
{de ? 'Compliance' : 'compliance'}
|
||||||
|
</span>
|
||||||
|
{' ↔ '}
|
||||||
|
<span style={{ background: 'linear-gradient(90deg, #fde68a, #fbbf24)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }}>
|
||||||
|
{de ? 'Code' : 'code'}
|
||||||
|
</span>
|
||||||
|
{de ? ' verbindet' : ''}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bridge */}
|
||||||
|
<div style={{ position: 'relative', margin: '14px 48px 0', height: 340 }}>
|
||||||
|
<BridgeConnectors />
|
||||||
|
<div style={{
|
||||||
|
position: 'relative', zIndex: 2,
|
||||||
|
display: 'grid', gridTemplateColumns: '1fr 260px 1fr', gap: 0,
|
||||||
|
alignItems: 'start', height: '100%',
|
||||||
|
}}>
|
||||||
|
{/* LEFT — Compliance */}
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', paddingRight: 20 }}>
|
||||||
|
<div style={{ height: 40, marginBottom: 36 }}>
|
||||||
|
<ColHeader side="left" label="Compliance" color="#a78bfa" icon="⎈" sub="policy · audit · proof" />
|
||||||
|
</div>
|
||||||
|
<div style={{ height: 110, display: 'flex', alignItems: 'center' }}>
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
<PillarRow side="left" tint="#a78bfa"
|
||||||
|
title={de ? 'RFQ-Prüfung' : 'RFQ Verification'}
|
||||||
|
body={de
|
||||||
|
? 'Kunden-Anforderungsdokumente automatisch gegen aktuellen Code validiert. Abweichungen erkannt, Änderungen vorgeschlagen.'
|
||||||
|
: 'Customer requirements auto-validated against current code. Gaps surfaced with implementation proposals.'}
|
||||||
|
onClick={() => open('rfq')}
|
||||||
|
active={detail?.title === details.rfq.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ height: 110, display: 'flex', alignItems: 'center' }}>
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
<PillarRow side="left" tint="#c084fc"
|
||||||
|
title={de ? 'Prozess-Compliance' : 'Process Compliance'}
|
||||||
|
body={de
|
||||||
|
? 'Vom Audit-Finding bis zur Code-Änderung vollständig automatisiert. Rollen, Fristen, Eskalation — End-to-End verwaltet.'
|
||||||
|
: 'From audit finding to code change, fully automated. Roles, deadlines, escalation — managed end-to-end.'}
|
||||||
|
onClick={() => open('process')}
|
||||||
|
active={detail?.title === details.process.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute left-12 top-1/2 -translate-y-1/2 w-[120px] z-10">
|
{/* CENTER hub */}
|
||||||
<div className="flex items-center gap-1.5 mb-2">
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
|
||||||
<FileCheck className="w-4 h-4 text-indigo-400" />
|
<div
|
||||||
<span className="text-sm font-bold text-indigo-400">Compliance</span>
|
onClick={() => 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)' }}
|
||||||
|
>
|
||||||
|
<CentralHub caption={de ? 'Immer in Sync' : 'Always in sync'} />
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-1.5">
|
|
||||||
{complianceItems.map((item, idx) => (
|
|
||||||
<li key={idx} className="flex items-center gap-1.5 text-sm text-white/50">
|
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-indigo-400 shrink-0" />
|
|
||||||
{item}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute right-7 top-1/2 -translate-y-1/2 w-[120px] z-10">
|
{/* RIGHT — Code */}
|
||||||
<div className="flex items-center gap-1.5 mb-2">
|
<div style={{ display: 'flex', flexDirection: 'column', paddingLeft: 20 }}>
|
||||||
<Code className="w-4 h-4 text-purple-400" />
|
<div style={{ height: 40, marginBottom: 36 }}>
|
||||||
<span className="text-sm font-bold text-purple-400">Code</span>
|
<ColHeader side="right" label="Code" color="#fbbf24" icon="⟨/⟩" sub="sast · dast · sbom" />
|
||||||
|
</div>
|
||||||
|
<div style={{ height: 110, display: 'flex', alignItems: 'center' }}>
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
<PillarRow side="right" tint="#fbbf24"
|
||||||
|
title={de ? 'Bidirektional' : 'Bidirectional Sync'}
|
||||||
|
body={de
|
||||||
|
? 'Compliance-Änderungen fliessen in Code; Code-Änderungen aktualisieren Docs. Beide Seiten immer synchron — kein Informationsverlust.'
|
||||||
|
: 'Compliance edits flow into code; code changes update docs. Both sides always in sync — zero drift.'}
|
||||||
|
onClick={() => open('bidir')}
|
||||||
|
active={detail?.title === details.bidir.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ height: 110, display: 'flex', alignItems: 'center' }}>
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
<PillarRow side="right" tint="#f59e0b"
|
||||||
|
title={de ? 'Kontinuierlich' : 'Continuous, Not Yearly'}
|
||||||
|
body={de
|
||||||
|
? 'Klassische Audits einmal jährlich — wir prüfen bei jedem Commit. Findings werden sofort zu Tickets mit konkreten Fixes.'
|
||||||
|
: 'Classic audits run once a year. We run on every commit — findings become tickets with fixes ready.'}
|
||||||
|
onClick={() => open('cont')}
|
||||||
|
active={detail?.title === details.cont.title}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-1.5">
|
|
||||||
{codeItems.map((item, idx) => (
|
|
||||||
<li key={idx} className="flex items-center gap-1.5 text-sm text-white/50">
|
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-purple-400 shrink-0" />
|
|
||||||
{item}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute top-[12%] left-[12%] text-indigo-400/60 text-lg z-20">◀</div>
|
{/* Under the Hood */}
|
||||||
<div className="absolute top-[12%] right-[12%] text-purple-400/60 text-lg z-20">▶</div>
|
<div style={{ position: 'relative', zIndex: 2, padding: '0 48px 16px' }}>
|
||||||
<div className="absolute bottom-[12%] left-[12%] text-amber-400/60 text-lg z-20">◀</div>
|
<div style={{
|
||||||
<div className="absolute bottom-[12%] right-[12%] text-emerald-400/60 text-lg z-20">▶</div>
|
...MONO, fontSize: 9.5, letterSpacing: 3.5, color: 'rgba(167,139,250,.7)',
|
||||||
|
textTransform: 'uppercase', fontWeight: 600, textAlign: 'center', marginBottom: 12,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 14,
|
||||||
|
}}>
|
||||||
|
<span style={{ width: 80, height: 1, background: 'linear-gradient(90deg, transparent, rgba(167,139,250,.5))' }} />
|
||||||
|
{de ? 'Unter der Haube' : 'Under the Hood'}
|
||||||
|
<span style={{ width: 80, height: 1, background: 'linear-gradient(270deg, transparent, rgba(167,139,250,.5))' }} />
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
|
||||||
|
<FeatureCard tint="#a78bfa" icon="⇄"
|
||||||
|
title={de ? 'End-to-End Rückverfolgbarkeit' : 'End-to-End Traceability'}
|
||||||
|
body={de
|
||||||
|
? 'Regulatorische Anforderungen deterministisch mit Code und System verknüpft — inklusive revisionssicherem Evidence-Layer.'
|
||||||
|
: 'Regulatory requirements deterministically linked to system state and code — including audit-proof evidence layer.'}
|
||||||
|
onClick={() => open('trace')}
|
||||||
|
active={detail?.title === details.trace.title}
|
||||||
|
Ticker={TickTrace}
|
||||||
|
/>
|
||||||
|
<FeatureCard tint="#c084fc" icon="◉"
|
||||||
|
title={de ? 'Continuous Compliance Engine' : 'Continuous Compliance Engine'}
|
||||||
|
body={de
|
||||||
|
? 'Automatische Audit-Validierung bei jeder Änderung — Code, Infrastruktur, Prozesse.'
|
||||||
|
: 'Automatic audit validation on every change — code, infrastructure, processes.'}
|
||||||
|
onClick={() => open('engine')}
|
||||||
|
active={detail?.title === details.engine.title}
|
||||||
|
Ticker={TickEngine}
|
||||||
|
/>
|
||||||
|
<FeatureCard tint="#fbbf24" icon="✦"
|
||||||
|
title={de ? 'Compliance Optimizer' : 'Compliance Optimizer'}
|
||||||
|
body={de
|
||||||
|
? 'Maximale Compliance pro €. Findet den Sweet Spot zwischen Geschwindigkeit und Risiko.'
|
||||||
|
: 'Maximally-compliant per €. Finds the sweet spot between speed & risk.'}
|
||||||
|
onClick={() => open('opt')}
|
||||||
|
active={detail?.title === details.opt.title}
|
||||||
|
Ticker={TickOptimizer}
|
||||||
|
/>
|
||||||
|
<FeatureCard tint="#f59e0b" icon="◎"
|
||||||
|
title={de ? 'EU-Trust & Governance' : 'EU Trust & Governance'}
|
||||||
|
body={de
|
||||||
|
? 'DSGVO, NIS-2, DORA, EU AI Act — eine Plattform, EU-souverän.'
|
||||||
|
: 'DSGVO, NIS-2, DORA, EU AI Act — one platform, EU-sovereign.'}
|
||||||
|
onClick={() => open('stack')}
|
||||||
|
active={detail?.title === details.stack.title}
|
||||||
|
Ticker={TickStack}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 4 CORNER CARDS */}
|
{/* Killer quote */}
|
||||||
{capabilities.map((cap, idx) => {
|
<div style={{
|
||||||
const Icon = cap.icon
|
position: 'relative', zIndex: 2, margin: '0 48px 24px',
|
||||||
const posClass = idx === 0 ? 'top-0 left-0'
|
padding: '11px 20px', borderRadius: 10,
|
||||||
: idx === 1 ? 'top-0 right-0'
|
background: 'linear-gradient(90deg, rgba(251,191,36,.08), rgba(167,139,250,.1), rgba(139,92,246,.08))',
|
||||||
: idx === 2 ? 'bottom-0 left-0'
|
border: '1px solid rgba(167,139,250,.22)',
|
||||||
: 'bottom-0 right-0'
|
textAlign: 'center', fontStyle: 'italic',
|
||||||
return (
|
fontSize: 12.5, color: 'rgba(236,233,247,.82)',
|
||||||
<div key={idx} className={`absolute ${posClass} w-[290px]`}>
|
}}>
|
||||||
<GlassCard hover={false} className="p-4" delay={0}>
|
<span style={{ color: '#fbbf24', marginRight: 8, fontSize: 16, verticalAlign: -2 }}>"</span>
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Icon className={`w-5 h-5 ${cap.color}`} />
|
|
||||||
<h3 className={`text-base font-bold ${cap.color}`}>{cap.label}</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-white/50 leading-relaxed">{cap.desc}</p>
|
|
||||||
</GlassCard>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
{/* SVG connection lines */}
|
|
||||||
<svg className="absolute inset-0 w-full h-full pointer-events-none z-0" preserveAspectRatio="none" viewBox="0 0 100 100">
|
|
||||||
<line x1="30" y1="25" x2="22" y2="15" stroke="rgba(99,102,241,0.15)" strokeWidth="0.3" strokeDasharray="1 1" />
|
|
||||||
<line x1="70" y1="25" x2="78" y2="15" stroke="rgba(168,85,247,0.15)" strokeWidth="0.3" strokeDasharray="1 1" />
|
|
||||||
<line x1="30" y1="75" x2="22" y2="85" stroke="rgba(245,158,11,0.15)" strokeWidth="0.3" strokeDasharray="1 1" />
|
|
||||||
<line x1="70" y1="75" x2="78" y2="85" stroke="rgba(16,185,129,0.15)" strokeWidth="0.3" strokeDasharray="1 1" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</FadeInView>
|
|
||||||
|
|
||||||
{/* MOAT — 3 Sätze */}
|
|
||||||
<FadeInView delay={0.4} className="mt-6 max-w-6xl mx-auto">
|
|
||||||
<h3 className="text-xs font-bold text-white/40 uppercase tracking-wider mb-3 text-center">
|
|
||||||
{de ? 'Unser MOAT' : 'Our MOAT'}
|
|
||||||
</h3>
|
|
||||||
<div className="grid md:grid-cols-2 gap-3">
|
|
||||||
<GlassCard hover={false} className="p-4 border-t-2 border-t-indigo-500">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<GitPullRequest className="w-4 h-4 text-indigo-400" />
|
|
||||||
<h4 className="text-xs font-bold text-indigo-400">End-to-End Traceability</h4>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-white/50 leading-relaxed">
|
|
||||||
{de
|
{de
|
||||||
? 'Regulatorische Anforderungen (Gesetz → Obligation → Control) deterministisch mit realem Systemzustand und Code verknüpft — inklusive revisionssicherem Evidence-Layer.'
|
? 'Jeder kann sagen, was verboten ist. Kaum jemand kann sagen, wie weit du maximal gehen kannst. Das ist unser Produkt.'
|
||||||
: 'Regulatory requirements (law → obligation → control) deterministically linked to real system state and code — including audit-proof evidence layer.'}
|
: 'Anyone can say what\'s forbidden. Almost no one can tell you how far you can actually go. That\'s our product.'}
|
||||||
</p>
|
<span style={{ color: '#fbbf24', marginLeft: 8, fontSize: 16, verticalAlign: -2 }}>"</span>
|
||||||
</GlassCard>
|
|
||||||
<GlassCard hover={false} className="p-4 border-t-2 border-t-purple-500">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Zap className="w-4 h-4 text-purple-400" />
|
|
||||||
<h4 className="text-xs font-bold text-purple-400">Continuous Compliance Engine</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-white/50 leading-relaxed">
|
|
||||||
{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.'}
|
|
||||||
</p>
|
|
||||||
</GlassCard>
|
|
||||||
<GlassCard hover={false} className="p-4 border-t-2 border-t-amber-500">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<ArrowLeftRight className="w-4 h-4 text-amber-400" />
|
|
||||||
<h4 className="text-xs font-bold text-amber-400">Compliance Optimizer</h4>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-white/50 leading-relaxed">
|
|
||||||
{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.'}
|
|
||||||
</p>
|
|
||||||
</GlassCard>
|
|
||||||
<GlassCard hover={false} className="p-4 border-t-2 border-t-emerald-500">
|
|
||||||
<div className="flex items-center gap-2 mb-2">
|
|
||||||
<Shield className="w-4 h-4 text-emerald-400" />
|
|
||||||
<h4 className="text-xs font-bold text-emerald-400">EU-Trust & Governance Stack</h4>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-white/50 leading-relaxed">
|
|
||||||
{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.'}
|
|
||||||
</p>
|
|
||||||
</GlassCard>
|
|
||||||
</div>
|
|
||||||
</FadeInView>
|
|
||||||
|
|
||||||
{/* Killer Quote */}
|
<DetailModal item={detail} onClose={close} />
|
||||||
<FadeInView delay={0.5} className="mt-4 max-w-6xl mx-auto">
|
|
||||||
<div className="bg-gradient-to-r from-amber-500/10 to-indigo-500/10 border border-amber-500/20 rounded-xl px-5 py-3 text-center">
|
|
||||||
<p className="text-sm text-white/70 italic">
|
|
||||||
{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."'}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</FadeInView>
|
</FadeInView>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user