feat(marketing-website): add BreakPilot marketing website with CMP integration
Multi-page marketing website positioned as "Deterministic Regulatory Engineering Platform": - 7 pages: Home, Plattform, CE-Prozess, Product Compliance, Architektur, Team, Preise - Platform Bridge animation (adapted from pitch-deck USP slide) - Cookie-Banner with consent-service integration (breakpilot-marketing site) - DE/EN language toggle + Dark/Light theme - Docker service on port 3014 [guardrail-change] PlatformBridgeSection.tsx added to loc-exceptions (816 LOC, SVG animation) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
'use client'
|
||||
|
||||
import { Check } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
|
||||
const riskColors = {
|
||||
red: { bg: 'bg-red-500/10', border: 'border-red-500/20', text: 'text-red-400', bar: 'bg-red-500' },
|
||||
amber: { bg: 'bg-amber-500/10', border: 'border-amber-500/20', text: 'text-amber-400', bar: 'bg-amber-500' },
|
||||
blue: { bg: 'bg-blue-500/10', border: 'border-blue-500/20', text: 'text-blue-400', bar: 'bg-blue-500' },
|
||||
green: { bg: 'bg-green-500/10', border: 'border-green-500/20', text: 'text-green-400', bar: 'bg-green-500' },
|
||||
}
|
||||
|
||||
export default function AIGovernanceSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="ai-governance" className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.aiGovernance.tag}
|
||||
title={i.aiGovernance.title}
|
||||
titleHighlight={i.aiGovernance.titleHighlight}
|
||||
subtitle={i.aiGovernance.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<FadeInView direction="left">
|
||||
<div className="space-y-3">
|
||||
{i.aiGovernance.riskLevels.map((level, idx) => {
|
||||
const colors = riskColors[level.color]
|
||||
return (
|
||||
<div key={idx} className={`rounded-xl border ${colors.border} ${colors.bg} p-4 flex items-center gap-4`}>
|
||||
<div className={`w-1 h-10 rounded-full ${colors.bar} shrink-0`} />
|
||||
<div>
|
||||
<h4 className={`text-sm font-bold ${colors.text}`}>{level.level}</h4>
|
||||
<p className="text-xs text-white/40">{level.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
<FadeInView direction="right">
|
||||
<div className="rounded-2xl border border-white/[0.06] bg-white/[0.03] p-6 h-full">
|
||||
<h3 className="text-lg font-bold mb-4">Deterministische AI-Act-Compliance</h3>
|
||||
<ul className="space-y-3">
|
||||
{i.aiGovernance.features.map((feature, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/60">
|
||||
<Check className="w-4 h-4 text-accent-signal mt-0.5 shrink-0" />
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
'use client'
|
||||
|
||||
import { Layers, Server, Database } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import TechBadge from '@/components/ui/TechBadge'
|
||||
|
||||
const layerIcons = [Layers, Server, Database]
|
||||
const layerColors = ['border-accent-electric/30', 'border-accent-indigo/30', 'border-accent-purple/30']
|
||||
const layerBg = ['bg-accent-electric/5', 'bg-accent-indigo/5', 'bg-accent-purple/5']
|
||||
|
||||
export default function ArchitectureSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="architecture" className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.architecture.tag}
|
||||
title={i.architecture.title}
|
||||
titleHighlight={i.architecture.titleHighlight}
|
||||
subtitle={i.architecture.subtitle}
|
||||
/>
|
||||
|
||||
<div className="space-y-4 mb-12">
|
||||
{i.architecture.layers.map((layer, idx) => {
|
||||
const Icon = layerIcons[idx]
|
||||
return (
|
||||
<FadeInView key={idx} delay={idx * 0.15}>
|
||||
<div className={`rounded-2xl border ${layerColors[idx]} ${layerBg[idx]} p-6`}>
|
||||
<div className="flex flex-col md:flex-row md:items-center gap-4">
|
||||
<div className="flex items-center gap-3 md:w-64 shrink-0">
|
||||
<Icon className="w-5 h-5 text-white/60" />
|
||||
<h3 className="font-bold text-sm">{layer.name}</h3>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-wrap gap-2">
|
||||
{layer.components.map((comp, ci) => (
|
||||
<span
|
||||
key={ci}
|
||||
className="px-3 py-1.5 rounded-lg text-xs bg-white/[0.06] border border-white/[0.06] text-white/70"
|
||||
>
|
||||
{comp}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<TechBadge className="shrink-0">{layer.tech}</TechBadge>
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FadeInView delay={0.5}>
|
||||
<div className="flex flex-wrap justify-center gap-3">
|
||||
{i.architecture.badges.map((badge, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-white/[0.08] bg-white/[0.03] text-xs text-white/60 font-medium"
|
||||
>
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-accent-signal" />
|
||||
{badge}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { FileText, Cpu, Shield, Search, ClipboardCheck, Download } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import TechBadge from '@/components/ui/TechBadge'
|
||||
import { ANIMATION } from '@/lib/constants'
|
||||
|
||||
const stepIcons = [FileText, Cpu, Shield, Search, ClipboardCheck, Download]
|
||||
|
||||
const flow = {
|
||||
de: {
|
||||
steps: [
|
||||
{
|
||||
num: '01',
|
||||
title: 'Grenzen & Verwendung',
|
||||
description: 'Der Nutzer füllt 14 Textfelder aus — Maschinenbeschreibung, bestimmungsgemäße Verwendung, Energiequellen, Betriebsarten, Personengruppen.',
|
||||
example: '"Kollaborativer 6-Achs-Roboter UR10e mit Kraft-/Momentsensorik, elektrischer Antrieb 48V DC, Ethernet/PROFINET..."',
|
||||
tags: [],
|
||||
},
|
||||
{
|
||||
num: '02',
|
||||
title: 'Automatische Analyse',
|
||||
description: '6 deterministische Schritte in Sekunden: Narrative Parser (200 Keywords) → Komponenten → Pattern Engine (1.058 Patterns) → Gefährdungen → Maßnahmen (225 Bibliothek) → Normen (751 A/B/C).',
|
||||
example: 'Pattern HP059 "Kollisionsgefahr Cobot": RequiredTags [cobot, rotating_joint] — PATTERN FEUERT ✓',
|
||||
tags: ['Deterministisch', 'Kein LLM', 'AND/NOT-Logik'],
|
||||
},
|
||||
{
|
||||
num: '03',
|
||||
title: 'Risikobewertung',
|
||||
description: 'Im Hazard Log erscheinen alle Gefährdungen mit Erstbewertung (S/E/P), RPZ-Berechnung und automatischer SIL/PL-Ableitung nach ISO-Risikograph.',
|
||||
example: 'Cobot-Ergebnis: 12 Gefährdungen, RPZ 4-48, SIL 0-2, PL a-d',
|
||||
tags: ['ISO 12100', 'SIL/PL', 'RPZ'],
|
||||
},
|
||||
{
|
||||
num: '04',
|
||||
title: 'Regulatorische Hinweise',
|
||||
description: 'On-Demand RAG-Suche in 36.708 Chunks: BAuA (TRBS/TRGS/ASR), OSHA Technical Manual, EU-Verordnungen. Keine ISO-Texte — nur gemeinfreie Quellen.',
|
||||
example: 'Gefährdung "Kollision" → TRBS 2111 Kap. 4.3, OSHA 1910.212(a)(1)',
|
||||
tags: ['RAG', 'BAuA', 'OSHA'],
|
||||
},
|
||||
{
|
||||
num: '05',
|
||||
title: 'Verifikation & Nachweise',
|
||||
description: '25 Evidenztypen werden automatisch vorgeschlagen. Der Nutzer erstellt Verifikationspläne und ordnet Prüfberichte zu.',
|
||||
example: 'E01 Hazard Analysis Report, E04 Electrical Safety Test, E14 Software Validation',
|
||||
tags: ['25 Evidenztypen', 'Traceability'],
|
||||
},
|
||||
{
|
||||
num: '06',
|
||||
title: 'CE-Akte generieren',
|
||||
description: 'Strukturiertes Dokument nach MVO 2023/1230 Anhang IV: Beschreibung, Risikobeurteilung, Normen, Maßnahmen, Nachweise, Konformitätserklärung. Export als PDF.',
|
||||
example: 'Vollständige Technische Dokumentation per Klick',
|
||||
tags: ['MVO 2023/1230', 'Anhang IV', 'PDF'],
|
||||
},
|
||||
],
|
||||
},
|
||||
en: {
|
||||
steps: [
|
||||
{
|
||||
num: '01',
|
||||
title: 'Limits & Intended Use',
|
||||
description: 'The user fills in 14 text fields — machine description, intended use, energy sources, operating modes, user groups.',
|
||||
example: '"Collaborative 6-axis robot UR10e with force/torque sensing, electric drive 48V DC, Ethernet/PROFINET..."',
|
||||
tags: [],
|
||||
},
|
||||
{
|
||||
num: '02',
|
||||
title: 'Automatic Analysis',
|
||||
description: '6 deterministic steps in seconds: Narrative Parser (200 keywords) → Components → Pattern Engine (1,058 patterns) → Hazards → Mitigations (225 library) → Norms (751 A/B/C).',
|
||||
example: 'Pattern HP059 "Collision hazard cobot": RequiredTags [cobot, rotating_joint] — PATTERN FIRES ✓',
|
||||
tags: ['Deterministic', 'No LLM', 'AND/NOT logic'],
|
||||
},
|
||||
{
|
||||
num: '03',
|
||||
title: 'Risk Assessment',
|
||||
description: 'The Hazard Log shows all hazards with initial assessment (S/E/P), RPZ calculation and automatic SIL/PL derivation per ISO risk graph.',
|
||||
example: 'Cobot result: 12 hazards, RPZ 4-48, SIL 0-2, PL a-d',
|
||||
tags: ['ISO 12100', 'SIL/PL', 'RPZ'],
|
||||
},
|
||||
{
|
||||
num: '04',
|
||||
title: 'Regulatory Guidance',
|
||||
description: 'On-demand RAG search across 36,708 chunks: BAuA (TRBS/TRGS/ASR), OSHA Technical Manual, EU regulations. No ISO texts — only public domain sources.',
|
||||
example: 'Hazard "Collision" → TRBS 2111 Ch. 4.3, OSHA 1910.212(a)(1)',
|
||||
tags: ['RAG', 'BAuA', 'OSHA'],
|
||||
},
|
||||
{
|
||||
num: '05',
|
||||
title: 'Verification & Evidence',
|
||||
description: '25 evidence types are automatically suggested. Users create verification plans and assign test reports.',
|
||||
example: 'E01 Hazard Analysis Report, E04 Electrical Safety Test, E14 Software Validation',
|
||||
tags: ['25 evidence types', 'Traceability'],
|
||||
},
|
||||
{
|
||||
num: '06',
|
||||
title: 'Generate CE File',
|
||||
description: 'Structured document per MR 2023/1230 Annex IV: Description, risk assessment, norms, mitigations, evidence, declaration of conformity. Export as PDF.',
|
||||
example: 'Complete technical documentation with one click',
|
||||
tags: ['MR 2023/1230', 'Annex IV', 'PDF'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
function StepContent({ step, Icon }: { step: typeof flow.de.steps[0]; Icon: typeof FileText }) {
|
||||
return (
|
||||
<div className="relative pl-16">
|
||||
<div className="absolute left-0 w-12 h-12 rounded-xl bg-accent-electric/10 border border-accent-electric/20 flex items-center justify-center">
|
||||
<Icon className="w-5 h-5 text-accent-electric" />
|
||||
</div>
|
||||
<div className="rounded-2xl border border-white/[0.06] bg-white/[0.03] p-6">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<span className="font-mono text-xs text-accent-electric/60">{step.num}</span>
|
||||
<h3 className="text-lg font-bold">{step.title}</h3>
|
||||
</div>
|
||||
<p className="text-sm text-white/50 mb-3">{step.description}</p>
|
||||
<div className="rounded-lg bg-enterprise-darker border border-white/[0.04] px-4 py-3 mb-3">
|
||||
<p className="font-mono text-xs text-white/40">{step.example}</p>
|
||||
</div>
|
||||
{step.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{step.tags.map(tag => (
|
||||
<TechBadge key={tag}>{tag}</TechBadge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function CEFlowSection() {
|
||||
const { lang } = useApp()
|
||||
const { steps } = flow[lang]
|
||||
|
||||
return (
|
||||
<section className="py-16 sm:py-24">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="relative">
|
||||
{/* Vertical line */}
|
||||
<div className="absolute left-6 top-0 bottom-0 w-px bg-white/[0.06]" />
|
||||
|
||||
<div className="space-y-8">
|
||||
{steps.map((step, idx) => {
|
||||
const Icon = stepIcons[idx]
|
||||
if (idx < 2) {
|
||||
return (
|
||||
<motion.div
|
||||
key={idx}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: ANIMATION.duration, delay: 0.3 + idx * 0.15, ease: ANIMATION.ease }}
|
||||
>
|
||||
<StepContent step={step} Icon={Icon} />
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<FadeInView key={idx} delay={0}>
|
||||
<StepContent step={step} Icon={Icon} />
|
||||
</FadeInView>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
'use client'
|
||||
|
||||
import { Check, AlertTriangle, X } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
|
||||
type ReqStatus = 'done' | 'partial' | 'missing'
|
||||
const statusIcons = { done: Check, partial: AlertTriangle, missing: X }
|
||||
const statusColors = { done: 'text-green-400 bg-green-500/10', partial: 'text-amber-400 bg-amber-500/10', missing: 'text-red-400 bg-red-500/10' }
|
||||
|
||||
const heading = {
|
||||
de: {
|
||||
tag: 'CRA COMPLIANCE',
|
||||
title: 'Was muss ich tun um',
|
||||
titleHighlight: 'CRA-konform zu werden?',
|
||||
subtitle: 'Der Cyber Resilience Act (EU 2024/2847) gilt ab September 2027. BreakPilot zeigt den Status pro Anforderung.',
|
||||
},
|
||||
en: {
|
||||
tag: 'CRA COMPLIANCE',
|
||||
title: 'What do I need to do to become',
|
||||
titleHighlight: 'CRA-compliant?',
|
||||
subtitle: 'The Cyber Resilience Act (EU 2024/2847) applies from September 2027. BreakPilot shows the status per requirement.',
|
||||
},
|
||||
}
|
||||
|
||||
const requirements = {
|
||||
de: [
|
||||
{ req: 'Schwachstellenmanagement einrichten', detail: 'Prozess für Identifikation, Bewertung und Behebung von Schwachstellen', status: 'missing' as ReqStatus },
|
||||
{ req: 'SBOM erstellen und pflegen', detail: 'Software Bill of Materials für jedes Produkt mit digitalen Elementen', status: 'partial' as ReqStatus },
|
||||
{ req: 'Security-Updates ermöglichen (OTA/SOTA)', detail: 'Mechanismus für sichere Software-Updates über die gesamte Lebensdauer', status: 'missing' as ReqStatus },
|
||||
{ req: 'Meldepflichten etablieren (24h/72h)', detail: 'Aktiv ausgenutzte Schwachstellen innerhalb 24h an ENISA melden', status: 'missing' as ReqStatus },
|
||||
{ req: 'Koordinierte Offenlegung (PSIRT)', detail: 'Product Security Incident Response Team und Disclosure Policy', status: 'missing' as ReqStatus },
|
||||
{ req: 'Technische Dokumentation aktualisieren', detail: 'Risikoanalyse, Design-Entscheidungen, Test-Ergebnisse dokumentieren', status: 'partial' as ReqStatus },
|
||||
{ req: 'Secure by Design', detail: 'Standardmäßig sichere Konfiguration, minimale Angriffsfläche', status: 'done' as ReqStatus },
|
||||
{ req: 'Keine bekannten Schwachstellen ausliefern', detail: 'Vor Inverkehrbringen alle bekannten CVEs beheben', status: 'partial' as ReqStatus },
|
||||
],
|
||||
en: [
|
||||
{ req: 'Establish vulnerability management', detail: 'Process for identification, assessment and remediation of vulnerabilities', status: 'missing' as ReqStatus },
|
||||
{ req: 'Create and maintain SBOM', detail: 'Software Bill of Materials for every product with digital elements', status: 'partial' as ReqStatus },
|
||||
{ req: 'Enable security updates (OTA/SOTA)', detail: 'Mechanism for secure software updates throughout the product lifetime', status: 'missing' as ReqStatus },
|
||||
{ req: 'Establish reporting obligations (24h/72h)', detail: 'Report actively exploited vulnerabilities to ENISA within 24h', status: 'missing' as ReqStatus },
|
||||
{ req: 'Coordinated disclosure (PSIRT)', detail: 'Product Security Incident Response Team and disclosure policy', status: 'missing' as ReqStatus },
|
||||
{ req: 'Update technical documentation', detail: 'Document risk analysis, design decisions, test results', status: 'partial' as ReqStatus },
|
||||
{ req: 'Secure by Design', detail: 'Secure configuration by default, minimal attack surface', status: 'done' as ReqStatus },
|
||||
{ req: 'Ship without known vulnerabilities', detail: 'Remediate all known CVEs before placing on market', status: 'partial' as ReqStatus },
|
||||
],
|
||||
}
|
||||
|
||||
export default function CRAFahrplanSection() {
|
||||
const { lang } = useApp()
|
||||
const h = heading[lang]
|
||||
const reqs = requirements[lang]
|
||||
|
||||
return (
|
||||
<section className="py-24 sm:py-32">
|
||||
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading tag={h.tag} title={h.title} titleHighlight={h.titleHighlight} subtitle={h.subtitle} />
|
||||
|
||||
<div className="space-y-3">
|
||||
{reqs.map((item, idx) => {
|
||||
const Icon = statusIcons[item.status]
|
||||
return (
|
||||
<FadeInView key={idx} delay={idx * 0.05}>
|
||||
<div className="rounded-xl border border-white/[0.06] bg-white/[0.03] p-4 flex items-start gap-4">
|
||||
<div className={`w-8 h-8 rounded-lg ${statusColors[item.status]} flex items-center justify-center shrink-0 mt-0.5`}>
|
||||
<Icon className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="text-sm font-bold mb-0.5">{item.req}</h4>
|
||||
<p className="text-xs text-white/40">{item.detail}</p>
|
||||
</div>
|
||||
<span className={`text-xs font-mono shrink-0 ${item.status === 'done' ? 'text-green-400' : item.status === 'partial' ? 'text-amber-400' : 'text-red-400'}`}>
|
||||
{item.status === 'done' ? '✓' : item.status === 'partial' ? '◐' : '✗'}
|
||||
</span>
|
||||
</div>
|
||||
</FadeInView>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
'use client'
|
||||
|
||||
import { Shield, GitBranch, Server, Scan, FileCheck, Layers } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
|
||||
const icons = [Shield, GitBranch, Scan, Server, FileCheck, Layers]
|
||||
|
||||
const content = {
|
||||
de: {
|
||||
tag: '05 / DIFFERENZIERUNG',
|
||||
title: 'Was BreakPilot',
|
||||
titleHighlight: 'einzigartig macht.',
|
||||
subtitle: 'Sechs Alleinstellungsmerkmale, die kein anderer Anbieter in einer Plattform vereint.',
|
||||
items: [
|
||||
{ title: 'Deterministisch, nicht generativ', description: 'Regelbasierte Analyse statt LLM-Interpretation. Jedes Ergebnis ist reproduzierbar und versioniert — unabhängig vom Modell.' },
|
||||
{ title: 'Lückenloser Decision Trail', description: 'Von der Rechtsquelle über die Obligation zum Control bis zur Maßnahme. Jeder Schritt ist auditierbar und dokumentiert.' },
|
||||
{ title: 'Code Security integriert', description: 'SAST, DAST, SBOM und Container Scanning als Teil der Compliance-Plattform — nicht als separates Tool.' },
|
||||
{ title: 'Vollständig on-premise deploybar', description: 'Kein US-Cloud-Anbieter in der gesamten Architektur. Betrieb auf eigener Hardware oder in BSI-zertifizierten Rechenzentren.' },
|
||||
{ title: 'Regulierungsübergreifend', description: 'DSGVO, NIS2, AI Act, Maschinenverordnung, TDDDG, DORA — eine Plattform statt sieben Einzellösungen.' },
|
||||
{ title: '294.000+ atomare Controls', description: 'Abgeleitet aus 380+ Rechtsquellen. Nicht manuell kuratiert, sondern systematisch aus Originaltext extrahiert und verifiziert.' },
|
||||
],
|
||||
},
|
||||
en: {
|
||||
tag: '05 / DIFFERENTIATION',
|
||||
title: 'What makes BreakPilot',
|
||||
titleHighlight: 'unique.',
|
||||
subtitle: 'Six unique selling points that no other provider combines in a single platform.',
|
||||
items: [
|
||||
{ title: 'Deterministic, not generative', description: 'Rule-based analysis instead of LLM interpretation. Every result is reproducible and versioned — independent of the model.' },
|
||||
{ title: 'Seamless decision trail', description: 'From legal source through obligation to control to action. Every step is auditable and documented.' },
|
||||
{ title: 'Code security integrated', description: 'SAST, DAST, SBOM and container scanning as part of the compliance platform — not as a separate tool.' },
|
||||
{ title: 'Fully on-premise deployable', description: 'No US cloud provider in the entire architecture. Operation on own hardware or in BSI-certified data centers.' },
|
||||
{ title: 'Cross-regulatory', description: 'GDPR, NIS2, AI Act, Machinery Regulation, TDDDG, DORA — one platform instead of seven individual solutions.' },
|
||||
{ title: '294,000+ atomic controls', description: 'Derived from 380+ legal sources. Not manually curated, but systematically extracted and verified from original text.' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default function ComparisonSection() {
|
||||
const { lang } = useApp()
|
||||
const c = content[lang]
|
||||
|
||||
return (
|
||||
<section id="comparison" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={c.tag}
|
||||
title={c.title}
|
||||
titleHighlight={c.titleHighlight}
|
||||
subtitle={c.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{c.items.map((item, idx) => {
|
||||
const Icon = icons[idx]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.08}>
|
||||
<div className="w-10 h-10 rounded-xl bg-accent-electric/10 flex items-center justify-center mb-4">
|
||||
<Icon className="w-5 h-5 text-accent-electric" />
|
||||
</div>
|
||||
<h3 className="text-sm font-bold mb-2">{item.title}</h3>
|
||||
<p className="text-xs text-white/40 leading-relaxed">{item.description}</p>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
|
||||
import { X, Check } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import StatusIndicator from '@/components/ui/StatusIndicator'
|
||||
|
||||
export default function ContinuousSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="continuous" className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.continuous.tag}
|
||||
title={i.continuous.title}
|
||||
titleHighlight={i.continuous.titleHighlight}
|
||||
subtitle={i.continuous.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FadeInView direction="left">
|
||||
<div className="rounded-2xl border border-red-500/15 bg-red-500/[0.03] p-6 h-full">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<StatusIndicator label="Offline" status="error" />
|
||||
<h3 className="text-sm font-bold text-red-400">{i.continuous.comparison.annual.title}</h3>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{i.continuous.comparison.annual.points.map((point, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/40">
|
||||
<X className="w-4 h-4 text-red-400/50 mt-0.5 shrink-0" />
|
||||
{point}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
<FadeInView direction="right">
|
||||
<div className="rounded-2xl border border-green-500/15 bg-green-500/[0.03] p-6 h-full">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<StatusIndicator label="Live" status="active" />
|
||||
<h3 className="text-sm font-bold text-green-400">{i.continuous.comparison.continuous.title}</h3>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{i.continuous.comparison.continuous.points.map((point, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/60">
|
||||
<Check className="w-4 h-4 text-green-400 mt-0.5 shrink-0" />
|
||||
{point}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { X } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
|
||||
const data = {
|
||||
de: {
|
||||
before: {
|
||||
title: 'Ausgangslage',
|
||||
items: [
|
||||
'15 Jahre altes Embedded Board',
|
||||
'Kein Secure Element / TPM',
|
||||
'Kein Secure Boot, kein OTA',
|
||||
'Hardcoded Credentials im Firmware',
|
||||
'Alter TCP/IP Stack ohne Patches',
|
||||
'Penetration-Test: 187 Findings',
|
||||
'"Was davon ist wirklich kritisch?"',
|
||||
],
|
||||
},
|
||||
after: {
|
||||
title: 'BreakPilot Delta-Analyse',
|
||||
items: [
|
||||
{ text: '3 Findings blockieren CE/CRA → sofort handeln', type: 'critical' as const },
|
||||
{ text: '12 Findings sind Software-only Fixes', type: 'fixable' as const },
|
||||
{ text: '172 Findings sind kosmetisch oder low-risk', type: 'ok' as const },
|
||||
{ text: 'Hardware-Redesign: wahrscheinlich NICHT nötig', type: 'ok' as const },
|
||||
{ text: 'RED-Re-Zertifizierung: nur bei Funkmodul-Änderung', type: 'fixable' as const },
|
||||
{ text: 'Geschätzter Aufwand: €15k statt €50k', type: 'ok' as const },
|
||||
{ text: 'Jira-Tickets mit Fix-Vorschlägen erstellt', type: 'ok' as const },
|
||||
],
|
||||
},
|
||||
},
|
||||
en: {
|
||||
before: {
|
||||
title: 'Starting Point',
|
||||
items: [
|
||||
'15-year-old embedded board',
|
||||
'No Secure Element / TPM',
|
||||
'No Secure Boot, no OTA',
|
||||
'Hardcoded credentials in firmware',
|
||||
'Legacy TCP/IP stack without patches',
|
||||
'Penetration test: 187 findings',
|
||||
'"Which ones actually matter?"',
|
||||
],
|
||||
},
|
||||
after: {
|
||||
title: 'BreakPilot Delta Analysis',
|
||||
items: [
|
||||
{ text: '3 findings block CE/CRA → act immediately', type: 'critical' as const },
|
||||
{ text: '12 findings are software-only fixes', type: 'fixable' as const },
|
||||
{ text: '172 findings are cosmetic or low-risk', type: 'ok' as const },
|
||||
{ text: 'Hardware redesign: probably NOT necessary', type: 'ok' as const },
|
||||
{ text: 'RED re-certification: only if RF module changes', type: 'fixable' as const },
|
||||
{ text: 'Estimated effort: €15k instead of €50k', type: 'ok' as const },
|
||||
{ text: 'Jira tickets with fix suggestions created', type: 'ok' as const },
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const typeColors = {
|
||||
critical: 'text-red-400',
|
||||
fixable: 'text-amber-400',
|
||||
ok: 'text-green-400',
|
||||
}
|
||||
|
||||
const typeIcons = {
|
||||
critical: '●',
|
||||
fixable: '◐',
|
||||
ok: '●',
|
||||
}
|
||||
|
||||
export default function DeltaImpactSection() {
|
||||
const { lang } = useApp()
|
||||
const d = data[lang]
|
||||
|
||||
return (
|
||||
<section className="py-16 sm:py-24">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 relative">
|
||||
{/* Before */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
|
||||
>
|
||||
<div className="rounded-2xl border border-red-500/15 bg-red-500/[0.03] p-6 h-full">
|
||||
<h3 className="text-sm font-bold text-red-400 mb-5 font-mono uppercase tracking-wider">
|
||||
{d.before.title}
|
||||
</h3>
|
||||
<ul className="space-y-3">
|
||||
{d.before.items.map((item, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/40">
|
||||
<X className="w-4 h-4 text-red-400/50 mt-0.5 shrink-0" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* After */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1, ease: [0.22, 1, 0.36, 1] }}
|
||||
>
|
||||
<div className="rounded-2xl border border-green-500/15 bg-green-500/[0.03] p-6 h-full">
|
||||
<h3 className="text-sm font-bold text-green-400 mb-5 font-mono uppercase tracking-wider">
|
||||
{d.after.title}
|
||||
</h3>
|
||||
<ul className="space-y-3">
|
||||
{d.after.items.map((item, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/60">
|
||||
<span className={`mt-0.5 shrink-0 text-xs ${typeColors[item.type]}`}>
|
||||
{typeIcons[item.type]}
|
||||
</span>
|
||||
{item.text}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
|
||||
import { Shield, FileCheck, ClipboardCheck, Check, X } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
|
||||
const iconMap: Record<string, typeof Shield> = {
|
||||
Shield,
|
||||
FileCheck,
|
||||
ClipboardCheck,
|
||||
}
|
||||
|
||||
export default function DeterministicSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="deterministic" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.deterministic.tag}
|
||||
title={i.deterministic.title}
|
||||
titleHighlight={i.deterministic.titleHighlight}
|
||||
subtitle={i.deterministic.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
|
||||
{i.deterministic.pillars.map((pillar, idx) => {
|
||||
const Icon = iconMap[pillar.icon]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.1}>
|
||||
<div className="w-12 h-12 rounded-xl bg-accent-indigo/10 flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-accent-indigo" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold mb-2">{pillar.title}</h3>
|
||||
<p className="text-sm text-white/50">{pillar.description}</p>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FadeInView>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="rounded-2xl border border-red-500/20 bg-red-500/[0.04] p-6">
|
||||
<h4 className="text-sm font-bold text-red-400 mb-4 font-mono uppercase tracking-wider">
|
||||
{i.deterministic.comparison.llm.title}
|
||||
</h4>
|
||||
<ul className="space-y-3">
|
||||
{i.deterministic.comparison.llm.items.map((item, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/50">
|
||||
<X className="w-4 h-4 text-red-400/60 mt-0.5 shrink-0" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-green-500/20 bg-green-500/[0.04] p-6">
|
||||
<h4 className="text-sm font-bold text-green-400 mb-4 font-mono uppercase tracking-wider">
|
||||
{i.deterministic.comparison.breakpilot.title}
|
||||
</h4>
|
||||
<ul className="space-y-3">
|
||||
{i.deterministic.comparison.breakpilot.items.map((item, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/70">
|
||||
<Check className="w-4 h-4 text-green-400 mt-0.5 shrink-0" />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowRight, ChevronDown } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import GradientText from '@/components/ui/GradientText'
|
||||
import CTAButton from '@/components/ui/CTAButton'
|
||||
import StatusIndicator from '@/components/ui/StatusIndicator'
|
||||
import { ANIMATION } from '@/lib/constants'
|
||||
|
||||
export default function HeroSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="hero" className="relative min-h-screen flex items-center justify-center enterprise-grid overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-enterprise-dark" />
|
||||
|
||||
<div className="relative z-10 max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, ease: ANIMATION.ease }}
|
||||
>
|
||||
<div className="inline-flex items-center gap-3 mb-8 px-4 py-2 rounded-full border border-white/[0.08] bg-white/[0.04]">
|
||||
<StatusIndicator label={i.hero.status} />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.1, ease: ANIMATION.ease }}
|
||||
className="mb-3"
|
||||
>
|
||||
<span className="mono-label tracking-widest">{i.hero.badge}</span>
|
||||
</motion.div>
|
||||
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2, ease: ANIMATION.ease }}
|
||||
className="text-5xl sm:text-6xl lg:text-7xl font-bold mb-6 leading-tight text-shadow-glow"
|
||||
>
|
||||
{i.hero.title}
|
||||
<br />
|
||||
<GradientText>{i.hero.titleHighlight}</GradientText>
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3, ease: ANIMATION.ease }}
|
||||
className="text-lg sm:text-xl text-white/50 max-w-2xl mx-auto mb-10"
|
||||
>
|
||||
{i.hero.subtitle}
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4, ease: ANIMATION.ease }}
|
||||
className="flex flex-col sm:flex-row items-center justify-center gap-4"
|
||||
>
|
||||
<CTAButton href="/plattform">
|
||||
{lang === 'de' ? 'Plattform entdecken' : 'Discover Platform'}
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</CTAButton>
|
||||
<CTAButton variant="ghost" href="/preise">
|
||||
{i.hero.cta}
|
||||
</CTAButton>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 1 }}
|
||||
className="absolute bottom-8 left-1/2 -translate-x-1/2"
|
||||
>
|
||||
<motion.div
|
||||
animate={{ y: [0, 8, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity, ease: 'easeInOut' }}
|
||||
>
|
||||
<ChevronDown className="w-5 h-5 text-white/20" />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client'
|
||||
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
|
||||
const statusColors = {
|
||||
success: 'text-green-400',
|
||||
warning: 'text-amber-400',
|
||||
neutral: 'text-accent-electric',
|
||||
}
|
||||
|
||||
const statusDots = {
|
||||
success: 'bg-green-400',
|
||||
warning: 'bg-amber-400',
|
||||
neutral: 'bg-accent-electric',
|
||||
}
|
||||
|
||||
export default function ImpactSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="impact" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.impact.tag}
|
||||
title={i.impact.title}
|
||||
titleHighlight={i.impact.titleHighlight}
|
||||
subtitle={i.impact.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<FadeInView direction="left">
|
||||
<div className="rounded-2xl bg-enterprise-darker border border-white/[0.06] p-6 font-mono text-sm overflow-hidden">
|
||||
<div className="flex items-center gap-2 mb-4 pb-3 border-b border-white/[0.06]">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500/60" />
|
||||
<div className="w-3 h-3 rounded-full bg-amber-500/60" />
|
||||
<div className="w-3 h-3 rounded-full bg-green-500/60" />
|
||||
<span className="ml-2 text-xs text-white/30">regulatory-impact-analysis</span>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{i.impact.terminalLines.map((line, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`
|
||||
${line.type === 'input' ? 'text-white/70' : ''}
|
||||
${line.type === 'output' ? 'text-white/40' : ''}
|
||||
${line.type === 'signal' ? 'text-green-400' : ''}
|
||||
`}
|
||||
>
|
||||
{line.text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
<FadeInView direction="right">
|
||||
<div className="grid grid-cols-2 gap-4 h-full">
|
||||
{i.impact.outputs.map((output, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="rounded-2xl bg-white/[0.04] border border-white/[0.06] p-5 flex flex-col justify-between"
|
||||
>
|
||||
<p className="text-xs text-white/40 mb-2">{output.label}</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`w-2 h-2 rounded-full ${statusDots[output.status]}`} />
|
||||
<span className={`text-2xl font-bold ${statusColors[output.status]}`}>
|
||||
{output.value}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
'use client'
|
||||
|
||||
import { Check } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
|
||||
const accentColors = ['border-t-accent-electric', 'border-t-accent-indigo', 'border-t-accent-purple']
|
||||
|
||||
export default function LegalSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="legal" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.legal.tag}
|
||||
title={i.legal.title}
|
||||
titleHighlight={i.legal.titleHighlight}
|
||||
subtitle={i.legal.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{i.legal.regulations.map((reg, idx) => (
|
||||
<GlassCard key={idx} delay={idx * 0.1} className={`border-t-2 ${accentColors[idx]}`}>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-2xl font-bold font-mono">{reg.name}</h3>
|
||||
<p className="text-xs text-white/40 mt-1">{reg.fullName}</p>
|
||||
</div>
|
||||
<ul className="space-y-2">
|
||||
{reg.features.map((feature, fi) => (
|
||||
<li key={fi} className="flex items-start gap-2 text-sm text-white/50">
|
||||
<Check className="w-3.5 h-3.5 text-accent-signal mt-0.5 shrink-0" />
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</GlassCard>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,816 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useRef, useMemo } from 'react'
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import GradientText from '@/components/ui/GradientText'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import { useApp } from '@/lib/context'
|
||||
import { X } from 'lucide-react'
|
||||
|
||||
const MONO: React.CSSProperties = {
|
||||
fontFamily: '"JetBrains Mono","SF Mono",ui-monospace,monospace',
|
||||
fontVariantNumeric: 'tabular-nums',
|
||||
}
|
||||
|
||||
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); }
|
||||
}
|
||||
`
|
||||
|
||||
// ── 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
|
||||
}
|
||||
|
||||
// ── Ticker ────────────────────────────────────────────────────────────────────
|
||||
function useTicker(fn: () => void, min = 180, max = 420, skip = 0.1) {
|
||||
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])
|
||||
}
|
||||
|
||||
function TickerShell({ tint, isLight, children }: { tint: string; isLight: boolean; children: React.ReactNode }) {
|
||||
return (
|
||||
<div style={{
|
||||
...MONO, marginTop: 10, padding: '5px 9px',
|
||||
background: isLight ? '#f1f5f9' : 'rgba(0,0,0,.38)',
|
||||
border: `1px solid ${tint}55`,
|
||||
borderRadius: 6, fontSize: 10.5,
|
||||
color: isLight ? '#475569' : 'rgba(236,233,247,.88)',
|
||||
display: 'flex', alignItems: 'center', gap: 7,
|
||||
whiteSpace: 'nowrap', overflow: 'hidden', height: 22,
|
||||
}}>{children}</div>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<TickerShell tint={tint} isLight={isLight}>
|
||||
<span style={{ color: isLight ? '#16a34a' : '#4ade80' }}>●</span>
|
||||
<span style={{ color: tint, opacity: .85 }}>trace</span>
|
||||
<span style={{ color: isLight ? '#1a1a2e' : '#f5f3fc', fontWeight: 600 }}>{n.toLocaleString()}</span>
|
||||
<span style={{ color: isLight ? '#94a3b8' : 'rgba(236,233,247,.45)' }}>evidence-chain</span>
|
||||
</TickerShell>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<TickerShell tint={tint} isLight={isLight}>
|
||||
<span style={{ color: isLight ? '#16a34a' : '#4ade80' }}>●</span>
|
||||
<span style={{ color: tint, opacity: .85 }}>validate</span>
|
||||
<span style={{ color: isLight ? '#1a1a2e' : '#f5f3fc', fontWeight: 600 }}>{v.toLocaleString()}</span>
|
||||
<span style={{ color: isLight ? '#16a34a' : '#4ade80' }}>{rate.toFixed(1)}%</span>
|
||||
</TickerShell>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<TickerShell tint={tint} isLight={isLight}>
|
||||
<span style={{ color: '#fbbf24' }}>✦</span>
|
||||
<span style={{ color: tint, opacity: .85 }}>optimize</span>
|
||||
<span style={{ color: isLight ? '#1a1a2e' : '#f5f3fc', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }}>{ops[i]}</span>
|
||||
</TickerShell>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<TickerShell tint={tint} isLight={isLight}>
|
||||
<span style={{ color: isLight ? '#16a34a' : '#4ade80' }}>●</span>
|
||||
<span style={{ color: tint, opacity: .85 }}>check</span>
|
||||
<span style={{ color: isLight ? '#1a1a2e' : '#f5f3fc', fontWeight: 600 }}>{regs[i]}</span>
|
||||
<span style={{ color: isLight ? '#94a3b8' : 'rgba(236,233,247,.4)' }}>·</span>
|
||||
<span style={{ color: isLight ? '#1a1a2e' : '#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 · Regulatory' : 'Pillar · Regulatory',
|
||||
title: de ? 'DSGVO / NIS2 / AI Act' : 'GDPR / NIS2 / AI Act',
|
||||
body: de
|
||||
? '294.000+ atomare Controls aus 380+ Rechtsquellen. Jede Anforderung deterministisch abgeleitet und auf Artikel, Absatz und Erwägungsgrund rückführbar.'
|
||||
: '294,000+ atomic controls from 380+ legal sources. Every requirement deterministically derived and traceable to article, paragraph and recital.',
|
||||
bullets: de
|
||||
? ['DSGVO, NIS2, AI Act, DORA, TDDDG in einer Plattform', 'Automatische Verarbeitungsverzeichnisse und DSFA', 'Meldepflichten und Fristen automatisch überwacht']
|
||||
: ['GDPR, NIS2, AI Act, DORA, TDDDG in one platform', 'Automated records of processing and DPIA', 'Reporting obligations and deadlines automatically monitored'],
|
||||
stat: { k: de ? 'atomare Controls' : 'atomic controls', v: '294.000+' },
|
||||
},
|
||||
process: {
|
||||
tint: '#c084fc', icon: '⟲',
|
||||
kicker: de ? 'Säule · Regulatory' : 'Pillar · Regulatory',
|
||||
title: de ? 'CE & Maschinenverordnung' : 'CE & Machinery Regulation',
|
||||
body: de
|
||||
? 'Von der Maschinenbeschreibung zur CE-Akte. 1.058 Hazard Patterns, 225 Maßnahmen, 751 Normen — deterministisch zugeordnet, nicht generiert.'
|
||||
: 'From machine description to CE file. 1,058 hazard patterns, 225 mitigations, 751 standards — deterministically mapped, not generated.',
|
||||
bullets: de
|
||||
? ['Risikobeurteilung nach EN ISO 12100 automatisiert', 'SIL/PL-Berechnung aus RPZ-Werten', 'CE-Akte nach MVO 2023/1230 Anhang IV per Klick']
|
||||
: ['Risk assessment per EN ISO 12100 automated', 'SIL/PL calculation from RPZ values', 'CE file per MR 2023/1230 Annex IV with one click'],
|
||||
stat: { k: de ? 'Hazard Patterns' : 'hazard patterns', v: '1.058' },
|
||||
},
|
||||
bidir: {
|
||||
tint: '#fbbf24', icon: '⟨/⟩',
|
||||
kicker: de ? 'Säule · Code Security' : 'Pillar · Code Security',
|
||||
title: 'SAST / DAST / SBOM',
|
||||
body: de
|
||||
? 'Kontinuierliche Code-Analyse für Firmware, Embedded und Backend. Jedes Finding wird automatisch priorisiert: Blocker vs. Major vs. kosmetisch.'
|
||||
: 'Continuous code analysis for firmware, embedded and backend. Every finding is automatically prioritized: blocker vs. major vs. cosmetic.',
|
||||
bullets: de
|
||||
? ['Statische + dynamische Analyse bei jedem Commit', 'SBOM-Generierung (CRA-Pflicht ab 2027)', 'Secret Detection für hardcoded Credentials']
|
||||
: ['Static + dynamic analysis on every commit', 'SBOM generation (CRA mandatory from 2027)', 'Secret detection for hardcoded credentials'],
|
||||
stat: { k: de ? 'Validierungen / Tag' : 'validations / day', v: '~2.400' },
|
||||
},
|
||||
cont: {
|
||||
tint: '#f59e0b', icon: '◎',
|
||||
kicker: de ? 'Säule · Code Security' : 'Pillar · Code Security',
|
||||
title: de ? 'Continuous Pentesting' : 'Continuous Pentesting',
|
||||
body: de
|
||||
? 'Automatisierte Schwachstellensuche statt jährlicher Penetrationstests. Findings werden sofort als Jira-Tickets mit Fix-Vorschlägen erstellt.'
|
||||
: 'Automated vulnerability scanning instead of annual penetration tests. Findings immediately become Jira tickets with fix suggestions.',
|
||||
bullets: de
|
||||
? ['€30.000+ Einsparung vs. externe Pentests', 'Automatische Entscheidung: Hardware-Redesign oder Software-Fix?', 'CVE → Ticket → Fix → Nachweis in einer Pipeline']
|
||||
: ['€30,000+ savings vs. external pentests', 'Automatic decision: hardware redesign or software fix?', 'CVE → ticket → fix → evidence in one pipeline'],
|
||||
stat: { k: de ? 'Ø Kosten-Einsparung' : 'avg cost savings', v: '€30k+ / year' },
|
||||
},
|
||||
trace: {
|
||||
tint: '#a78bfa', icon: '⇄',
|
||||
kicker: de ? 'Integration' : 'Integration',
|
||||
title: de ? 'Jira / Linear Integration' : 'Jira / Linear Integration',
|
||||
body: de
|
||||
? 'Jedes Finding wird automatisch als Ticket im Issue-Tracker erstellt — mit Priorität, Kontext, Fix-Vorschlag und Verknüpfung zur Rechtsquelle.'
|
||||
: 'Every finding is automatically created as a ticket in your issue tracker — with priority, context, fix suggestion and link to legal source.',
|
||||
bullets: de
|
||||
? ['Jira, Linear, GitLab Issues, GitHub Issues', 'Blocker / Major / Minor automatisch klassifiziert', 'Fix-Vorschläge direkt im Ticket']
|
||||
: ['Jira, Linear, GitLab Issues, GitHub Issues', 'Blocker / Major / Minor automatically classified', 'Fix suggestions directly in the ticket'],
|
||||
},
|
||||
engine: {
|
||||
tint: '#c084fc', icon: '◉',
|
||||
kicker: de ? 'Integration' : 'Integration',
|
||||
title: de ? 'End-to-End Traceability' : 'End-to-End Traceability',
|
||||
body: de
|
||||
? 'Lückenloser Decision Trail: Rechtsquelle → Obligation → Control → Maßnahme → Code-Änderung → Nachweis. Ein Klick von der Klausel bis zur Codezeile.'
|
||||
: 'Seamless decision trail: Legal source → Obligation → Control → Action → Code change → Evidence. One click from clause to line of code.',
|
||||
bullets: de
|
||||
? ['Versionierte Evidence-Chain', 'Audit-Log pro Änderung signiert', 'Rechtsquelle bis Codezeile nachvollziehbar']
|
||||
: ['Versioned evidence chain', 'Audit log signed per change', 'Legal source to code line traceable'],
|
||||
},
|
||||
opt: {
|
||||
tint: '#fbbf24', icon: '✦',
|
||||
kicker: de ? 'Integration' : 'Integration',
|
||||
title: de ? 'Delta-Impact-Analyse' : 'Delta Impact Analysis',
|
||||
body: de
|
||||
? 'Neues Funkmodul eingebaut? BreakPilot zeigt sofort: neue Hazards, neue Normen, CRA-Relevanz, RED-Re-Zertifizierung nötig? Hardware-Redesign oder Software-Fix?'
|
||||
: 'New wireless module installed? BreakPilot immediately shows: new hazards, new standards, CRA relevance, RED re-certification needed? Hardware redesign or software fix?',
|
||||
bullets: de
|
||||
? ['Änderungsfolgenanalyse in Echtzeit', 'Hardware vs. Software Entscheidungsunterstützung', 'Spart €50k+ an externer Beratung']
|
||||
: ['Change impact analysis in real-time', 'Hardware vs. software decision support', 'Saves €50k+ in external consulting'],
|
||||
},
|
||||
stack: {
|
||||
tint: '#f59e0b', icon: '◎',
|
||||
kicker: de ? 'Integration' : 'Integration',
|
||||
title: de ? 'Sovereign AI · On-Premise' : 'Sovereign AI · On-Premise',
|
||||
body: de
|
||||
? 'Kein US-Cloud-Anbieter. Self-hosted LLM auf Apple Silicon. BSI-konforme Infrastruktur. Ihre Daten verlassen nie Ihr Netzwerk.'
|
||||
: 'No US cloud provider. Self-hosted LLM on Apple Silicon. BSI-compliant infrastructure. Your data never leaves your network.',
|
||||
bullets: de
|
||||
? ['Kein FISA 702, kein CLOUD Act', 'On-Premise Appliance ab €7.900', 'EU-souveräner Software-Stack']
|
||||
: ['No FISA 702, no CLOUD Act', 'On-premise appliance from €7,900', 'EU-sovereign software stack'],
|
||||
},
|
||||
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 (
|
||||
<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 ? (isLight ? tint : '#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: isLight ? '#1a1a2e' : '#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: isLight
|
||||
? `rgba(71,85,105,${lit ? 1 : .78})`
|
||||
: `rgba(236,233,247,${lit ? .82 : .62})`,
|
||||
transition: 'color .25s',
|
||||
}}>{body}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ── 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 (
|
||||
<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: isLight ? 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: isLight ? '#1a1a2e' : '#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, isLight }: { caption: string; isLight: boolean }) {
|
||||
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: isLight
|
||||
? '0 0 30px rgba(167,139,250,.4), 0 0 60px rgba(167,139,250,.15), inset 0 3px 0 rgba(255,255,255,.5), inset 0 -8px 14px rgba(0,0,0,.2)'
|
||||
: '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: isLight ? 'uspPulseLight 2.6s ease-in-out infinite' : 'uspPulse 2.6s ease-in-out infinite',
|
||||
zIndex: 3,
|
||||
}}>
|
||||
<div style={{ position: 'absolute', inset: -14, borderRadius: '50%', border: `1px dashed ${isLight ? 'rgba(167,139,250,.5)' : 'rgba(216,202,255,.42)'}`, animation: 'uspSpin 14s linear infinite' }} />
|
||||
<div style={{ position: 'absolute', inset: -30, borderRadius: '50%', border: `1px dashed ${isLight ? 'rgba(167,139,250,.3)' : '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: isLight ? 'rgba(109,77,194,.75)' : 'rgba(216,202,255,.75)',
|
||||
textTransform: 'uppercase' as const, fontWeight: 600,
|
||||
}}>{caption}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ── Bridge SVG connectors ─────────────────────────────────────────────────────
|
||||
function BridgeConnectors({ isLight }: { isLight: boolean }) {
|
||||
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={isLight ? '.6' : '.85'} />
|
||||
<stop offset="1" stopColor="#c084fc" stopOpacity={isLight ? '.2' : '.3'} />
|
||||
</linearGradient>
|
||||
<linearGradient id="uspToR" x1="0" x2="1">
|
||||
<stop offset="0" stopColor="#c084fc" stopOpacity={isLight ? '.2' : '.3'} />
|
||||
<stop offset=".7" stopColor="#fbbf24" stopOpacity={isLight ? '.6' : '.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={isLight ? '#eef2ff' : '#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={isLight ? '#eef2ff' : '#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, 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 (
|
||||
<div
|
||||
onClick={onClick}
|
||||
onMouseEnter={() => 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',
|
||||
}}
|
||||
>
|
||||
<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 ? (isLight ? tint : '#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: isLight ? '#1a1a2e' : '#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: isLight
|
||||
? `rgba(71,85,105,${lit ? 1 : .78})`
|
||||
: `rgba(236,233,247,${lit ? .82 : .65})`,
|
||||
transition: 'color .25s',
|
||||
}}>{body}</div>
|
||||
<Ticker tint={tint} isLight={isLight} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ── 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 (
|
||||
<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: isLight ? 'rgba(240,244,255,.72)' : '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: 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',
|
||||
}}
|
||||
>
|
||||
<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: isLight ? item.tint : '#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: isLight ? '#1a1a2e' : '#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: isLight ? '#64748b' : 'rgba(236,233,247,.6)',
|
||||
}}>
|
||||
<X style={{ width: 14, height: 14 }} />
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, lineHeight: 1.6, color: isLight ? '#475569' : '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: isLight ? 'rgba(0,0,0,.04)' : 'rgba(0,0,0,.3)',
|
||||
border: `1px solid ${item.tint}${isLight ? '22' : '33'}`,
|
||||
}}>
|
||||
<span style={{ color: item.tint, fontSize: 12, marginTop: 1 }}>▸</span>
|
||||
<span style={{ fontSize: 12, lineHeight: 1.5, color: isLight ? '#475569' : 'rgba(236,233,247,.78)' }}>{b}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{item.stat && (
|
||||
<div style={{
|
||||
...MONO, padding: '10px 14px', borderRadius: 8,
|
||||
background: isLight ? 'rgba(0,0,0,.04)' : 'rgba(0,0,0,.45)',
|
||||
border: `1px solid ${item.tint}${isLight ? '33' : '55'}`,
|
||||
fontSize: 12, color: isLight ? '#475569' : 'rgba(236,233,247,.9)',
|
||||
display: 'flex', alignItems: 'center', gap: 10,
|
||||
}}>
|
||||
<span style={{ color: isLight ? '#16a34a' : '#4ade80' }}>●</span>
|
||||
<span style={{ color: item.tint }}>{item.stat.k}</span>
|
||||
<span style={{ color: isLight ? '#1a1a2e' : '#f5f3fc', fontWeight: 600 }}>{item.stat.v}</span>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
)
|
||||
}
|
||||
|
||||
// ── 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 (
|
||||
<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 PlatformBridgeSection() {
|
||||
const { lang } = useApp()
|
||||
const de = lang === 'de'
|
||||
const isLight = useIsLight()
|
||||
const details = getDetails(de)
|
||||
const [detail, setDetail] = useState<DetailItem | null>(null)
|
||||
const open = (k: string) => setDetail(details[k])
|
||||
const close = () => setDetail(null)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<style>{CSS_KF}</style>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
|
||||
className="text-center mb-1"
|
||||
>
|
||||
<h2 className="text-5xl md:text-6xl font-bold mb-1">
|
||||
<GradientText>{de ? 'Eine Plattform' : 'One Platform'}</GradientText>
|
||||
</h2>
|
||||
<p className="text-lg text-white/50 max-w-2xl mx-auto">
|
||||
{de ? 'Regulatorik, Code Security und CE — verbunden, nicht isoliert' : 'Regulatory, code security and CE — connected, not isolated'}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.15, ease: [0.22, 1, 0.36, 1] }}
|
||||
>
|
||||
{/* ── MAIN CANVAS ───────────────────────────────────────────────── */}
|
||||
<div style={{
|
||||
position: 'relative', overflow: 'hidden', borderRadius: 16,
|
||||
background: isLight
|
||||
? 'linear-gradient(160deg, #f0f4ff 0%, #eff6ff 50%, #f5f0ff 100%)'
|
||||
: 'radial-gradient(ellipse at 50% 30%, #1a0f34 0%, #0e0720 55%, #050210 100%)',
|
||||
color: isLight ? '#1a1a2e' : '#ece9f7',
|
||||
fontFamily: '"Inter", system-ui, sans-serif',
|
||||
WebkitFontSmoothing: 'antialiased',
|
||||
}}>
|
||||
{/* Ambient glow — dark only, subtle */}
|
||||
{!isLight && (
|
||||
<div style={{
|
||||
position: 'absolute', top: -80, left: '50%', transform: 'translateX(-50%)',
|
||||
width: 600, height: 400, borderRadius: '50%',
|
||||
background: 'radial-gradient(ellipse, rgba(167,139,250,.12), transparent 65%)',
|
||||
filter: 'blur(50px)', pointerEvents: 'none',
|
||||
}} />
|
||||
)}
|
||||
|
||||
{/* Bridge */}
|
||||
<div style={{ position: 'relative', margin: '16px 48px 0', height: 330 }}>
|
||||
<BridgeConnectors isLight={isLight} />
|
||||
<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={de ? 'Regulatorik' : 'Regulatory'} color="#a78bfa" icon="⎈" sub="dsgvo · nis2 · ce · cra" isLight={isLight} />
|
||||
</div>
|
||||
<div style={{ height: 110, display: 'flex', alignItems: 'center' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<PillarRow side="left" tint="#a78bfa" isLight={isLight}
|
||||
title={de ? 'DSGVO / NIS2 / AI Act' : 'GDPR / NIS2 / AI Act'}
|
||||
body={de
|
||||
? '294.000+ atomare Controls aus 380+ Rechtsquellen. Deterministische Ableitung, keine Halluzinationen.'
|
||||
: '294,000+ atomic controls from 380+ legal sources. Deterministic derivation, no hallucinations.'}
|
||||
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" isLight={isLight}
|
||||
title={de ? 'CE & Maschinenverordnung' : 'CE & Machinery Regulation'}
|
||||
body={de
|
||||
? '1.058 Hazard Patterns, 225 Maßnahmen, 751 Normen. CE-Akte nach MVO 2023/1230 per Klick.'
|
||||
: '1,058 hazard patterns, 225 mitigations, 751 standards. CE file per MR 2023/1230 with one click.'}
|
||||
onClick={() => open('process')}
|
||||
active={detail?.title === details.process.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CENTER hub */}
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
|
||||
<div
|
||||
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'} isLight={isLight} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* RIGHT — Code */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', paddingLeft: 20 }}>
|
||||
<div style={{ height: 40, marginBottom: 36 }}>
|
||||
<ColHeader side="right" label="Code Security" color="#fbbf24" icon="⟨/⟩" sub="sast · dast · sbom · pentest" isLight={isLight} />
|
||||
</div>
|
||||
<div style={{ height: 110, display: 'flex', alignItems: 'center' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<PillarRow side="right" tint="#fbbf24" isLight={isLight}
|
||||
title="SAST / DAST / SBOM"
|
||||
body={de
|
||||
? 'Kontinuierliche Code-Analyse für Firmware und Embedded. Jedes Finding automatisch priorisiert: Blocker vs. kosmetisch.'
|
||||
: 'Continuous code analysis for firmware and embedded. Every finding automatically prioritized: blocker vs. cosmetic.'}
|
||||
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" isLight={isLight}
|
||||
title={de ? 'Continuous Pentesting' : 'Continuous Pentesting'}
|
||||
body={de
|
||||
? 'Automatisierte Schwachstellensuche statt jährlicher Pentests. Findings werden sofort zu Jira-Tickets mit Fix-Vorschlägen.'
|
||||
: 'Automated vulnerability scanning instead of annual pentests. Findings become Jira tickets with fix suggestions.'}
|
||||
onClick={() => open('cont')}
|
||||
active={detail?.title === details.cont.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Integrations */}
|
||||
<div style={{ position: 'relative', zIndex: 2, padding: '0 48px 20px' }}>
|
||||
<div style={{
|
||||
...MONO, fontSize: 9.5, letterSpacing: 3.5,
|
||||
color: isLight ? 'rgba(109,77,194,.7)' : '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: isLight ? 'linear-gradient(90deg, transparent, rgba(109,77,194,.4))' : 'linear-gradient(90deg, transparent, rgba(167,139,250,.5))' }} />
|
||||
{de ? 'Integrationen & Infrastruktur' : 'Integrations & Infrastructure'}
|
||||
<span style={{ width: 80, height: 1, background: isLight ? 'linear-gradient(270deg, transparent, rgba(109,77,194,.4))' : 'linear-gradient(270deg, transparent, rgba(167,139,250,.5))' }} />
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10 }}>
|
||||
{([
|
||||
{ tint: '#a78bfa', icon: '⇄', key: 'trace',
|
||||
title: de ? 'Jira / Linear' : 'Jira / Linear',
|
||||
body: de ? 'Findings → Tickets mit Fix-Vorschlägen' : 'Findings → tickets with fix suggestions' },
|
||||
{ tint: '#c084fc', icon: '◉', key: 'engine',
|
||||
title: 'Traceability',
|
||||
body: de ? 'Rechtsquelle → Code → Nachweis' : 'Legal source → code → evidence' },
|
||||
{ tint: '#fbbf24', icon: '✦', key: 'opt',
|
||||
title: de ? 'Delta-Impact' : 'Delta Impact',
|
||||
body: de ? 'Änderungsfolgen sofort sehen' : 'See change impact instantly' },
|
||||
{ tint: '#f59e0b', icon: '◎', key: 'stack',
|
||||
title: 'Sovereign AI',
|
||||
body: de ? 'On-Premise, kein US-Cloud' : 'On-premise, no US cloud' },
|
||||
] as const).map((card) => (
|
||||
<div
|
||||
key={card.key}
|
||||
onClick={() => open(card.key)}
|
||||
style={{
|
||||
padding: '12px 14px', borderRadius: 10, cursor: 'pointer',
|
||||
background: isLight
|
||||
? `linear-gradient(180deg, ${card.tint}10 0%, rgba(248,250,252,.95) 100%)`
|
||||
: `linear-gradient(180deg, ${card.tint}18 0%, rgba(14,8,28,.85) 100%)`,
|
||||
border: `1px solid ${isLight ? 'rgba(0,0,0,.08)' : card.tint + '3a'}`,
|
||||
transition: 'transform .2s, box-shadow .2s',
|
||||
}}
|
||||
onMouseEnter={e => { (e.currentTarget as HTMLDivElement).style.transform = 'translateY(-2px)'; (e.currentTarget as HTMLDivElement).style.boxShadow = `0 8px 24px ${card.tint}33` }}
|
||||
onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.transform = 'translateY(0)'; (e.currentTarget as HTMLDivElement).style.boxShadow = 'none' }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 4 }}>
|
||||
<span style={{
|
||||
width: 20, height: 20, borderRadius: 5,
|
||||
background: `${card.tint}22`, border: `1px solid ${card.tint}66`,
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||
color: card.tint, fontSize: 11,
|
||||
}}>{card.icon}</span>
|
||||
<span style={{ fontSize: 12, fontWeight: 700, color: isLight ? '#1a1a2e' : '#f7f5fc' }}>{card.title}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 11, lineHeight: 1.4, color: isLight ? 'rgba(71,85,105,.8)' : 'rgba(236,233,247,.55)' }}>{card.body}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DetailModal item={detail} onClose={close} isLight={isLight} />
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
'use client'
|
||||
|
||||
import { Check, ArrowRight, Cpu } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import CTAButton from '@/components/ui/CTAButton'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
|
||||
export default function PricingSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="pricing" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.pricing.tag}
|
||||
title={i.pricing.title}
|
||||
titleHighlight={i.pricing.titleHighlight}
|
||||
subtitle={i.pricing.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
{i.pricing.tiers.map((tier, idx) => (
|
||||
<GlassCard
|
||||
key={idx}
|
||||
delay={idx * 0.1}
|
||||
className={`relative ${tier.highlighted ? 'border-accent-electric/30 ring-1 ring-accent-electric/20' : ''}`}
|
||||
>
|
||||
{tier.highlighted && (
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2">
|
||||
<span className="px-3 py-1 rounded-full text-xs font-bold bg-accent-electric text-white">
|
||||
{tier.badge}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!tier.highlighted && (
|
||||
<span className="inline-block mono-label text-xs mb-2">{tier.badge}</span>
|
||||
)}
|
||||
|
||||
<h3 className="text-xl font-bold mt-2 mb-1">{tier.name}</h3>
|
||||
<div className="flex items-baseline gap-1 mb-3">
|
||||
<span className="text-3xl font-bold">EUR {tier.price}</span>
|
||||
<span className="text-white/40 text-sm">{tier.period}</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/40 mb-6">{tier.description}</p>
|
||||
|
||||
<ul className="space-y-3 mb-8">
|
||||
{tier.features.map((feature, fi) => (
|
||||
<li key={fi} className="flex items-start gap-2 text-sm text-white/60">
|
||||
<Check className="w-4 h-4 text-accent-signal mt-0.5 shrink-0" />
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<CTAButton
|
||||
variant={tier.highlighted ? 'primary' : 'ghost'}
|
||||
href="#"
|
||||
className="w-full justify-center"
|
||||
>
|
||||
{i.pricing.cta}
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</CTAButton>
|
||||
</GlassCard>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<FadeInView>
|
||||
<div className="rounded-2xl border border-white/[0.08] bg-white/[0.03] p-6 md:p-8">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center gap-6">
|
||||
<div className="w-14 h-14 rounded-2xl bg-accent-purple/10 flex items-center justify-center shrink-0">
|
||||
<Cpu className="w-7 h-7 text-accent-purple" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-1">
|
||||
<h3 className="text-lg font-bold">{i.pricing.appliance.name}</h3>
|
||||
<span className="mono-label text-xs px-2 py-0.5 rounded bg-white/[0.06]">
|
||||
{i.pricing.appliance.badge}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/40 mb-3">{i.pricing.appliance.description}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{i.pricing.appliance.features.map((f, idx) => (
|
||||
<span key={idx} className="inline-flex items-center gap-1.5 text-xs text-white/50 bg-white/[0.04] px-2.5 py-1 rounded-md">
|
||||
<Check className="w-3 h-3 text-accent-signal" />
|
||||
{f}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right shrink-0">
|
||||
<div className="text-2xl font-bold">EUR {i.pricing.appliance.priceRange}</div>
|
||||
<div className="text-xs text-white/40">{i.pricing.appliance.priceLabel}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
'use client'
|
||||
|
||||
import { Mail, Table, UserSearch, Clock, AlertCircle, Euro, Timer, HelpCircle } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import GradientText from '@/components/ui/GradientText'
|
||||
|
||||
const steps = {
|
||||
de: [
|
||||
{ icon: Mail, label: 'Alles per Email', detail: 'Anforderungen, Rückfragen, Freigaben — verstreut in 200 Threads' },
|
||||
{ icon: Table, label: 'Alles per Excel', detail: 'Risikobeurteilungen, Maßnahmenlisten, Normverweise in 15 Dateien' },
|
||||
{ icon: UserSearch, label: 'Hinterherlaufen', detail: '"Wer war nochmal zuständig für die EMV-Messung?"' },
|
||||
{ icon: Clock, label: 'Wartezeiten & Urlaub', detail: 'CE-Review blockiert weil der Experte 3 Wochen weg ist' },
|
||||
{ icon: AlertCircle, label: 'CE-Berater zu spät', detail: 'Maschine ist fertig, CE-Bewertung fängt erst jetzt an' },
|
||||
{ icon: Euro, label: '€50.000+ pro Jahr', detail: 'Externe Audits, Penetrationstests, CE-Beratung, Normen kaufen' },
|
||||
{ icon: Timer, label: 'Time-to-Market verzögert', detail: 'Wochen bis Monate für eine vollständige CE-Akte' },
|
||||
{ icon: HelpCircle, label: 'Silo-Experten', detail: 'CRA, AI Act, MVO, NIS2 — jeder kennt nur sein Thema' },
|
||||
],
|
||||
en: [
|
||||
{ icon: Mail, label: 'Everything by email', detail: 'Requirements, follow-ups, approvals — scattered across 200 threads' },
|
||||
{ icon: Table, label: 'Everything in Excel', detail: 'Risk assessments, mitigation lists, norm references in 15 files' },
|
||||
{ icon: UserSearch, label: 'Chasing people', detail: '"Who was responsible for the EMC measurement again?"' },
|
||||
{ icon: Clock, label: 'Wait times & vacation', detail: 'CE review blocked because the expert is away for 3 weeks' },
|
||||
{ icon: AlertCircle, label: 'CE consultants too late', detail: 'Machine is finished, CE assessment only starts now' },
|
||||
{ icon: Euro, label: '€50,000+ per year', detail: 'External audits, penetration tests, CE consulting, buying norms' },
|
||||
{ icon: Timer, label: 'Time-to-market delayed', detail: 'Weeks to months for a complete CE file' },
|
||||
{ icon: HelpCircle, label: 'Silo experts', detail: 'CRA, AI Act, MVO, NIS2 — everyone only knows their topic' },
|
||||
],
|
||||
}
|
||||
|
||||
const heading = {
|
||||
de: { tag: 'DAS PROBLEM', title: 'So läuft es', titleHighlight: 'heute.', bridge: 'BreakPilot baut die Brücke zwischen Code, Produkt und Regulation — in Echtzeit, ohne Excel, Email und ohne Hinterherlaufen.' },
|
||||
en: { tag: 'THE PROBLEM', title: "How it works", titleHighlight: 'today.', bridge: 'BreakPilot bridges the gap between code, product and regulation — in real-time, without Excel, email or chasing people.' },
|
||||
}
|
||||
|
||||
export default function ProblemFlowSection() {
|
||||
const { lang } = useApp()
|
||||
const h = heading[lang]
|
||||
const items = steps[lang]
|
||||
|
||||
return (
|
||||
<section className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<FadeInView className="text-center mb-16">
|
||||
<p className="mono-label mb-4">{h.tag}</p>
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-6">
|
||||
{h.title} <GradientText>{h.titleHighlight}</GradientText>
|
||||
</h2>
|
||||
</FadeInView>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-16">
|
||||
{items.map((step, idx) => {
|
||||
const Icon = step.icon
|
||||
return (
|
||||
<FadeInView key={idx} delay={idx * 0.06}>
|
||||
<div className="rounded-xl border border-red-500/10 bg-red-500/[0.03] p-5 h-full">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-red-500/10 flex items-center justify-center">
|
||||
<Icon className="w-4 h-4 text-red-400" />
|
||||
</div>
|
||||
<span className="font-mono text-xs text-red-400/60">{String(idx + 1).padStart(2, '0')}</span>
|
||||
</div>
|
||||
<h3 className="text-sm font-bold mb-1 text-red-300/80">{step.label}</h3>
|
||||
<p className="text-xs text-white/30 leading-relaxed">{step.detail}</p>
|
||||
</div>
|
||||
</FadeInView>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FadeInView>
|
||||
<div className="rounded-2xl border border-accent-electric/20 bg-accent-electric/[0.04] p-8 text-center">
|
||||
<p className="text-lg text-white/70 max-w-3xl mx-auto font-medium leading-relaxed">
|
||||
{h.bridge}
|
||||
</p>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
'use client'
|
||||
|
||||
import { TrendingUp, ShieldAlert, Euro } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import AnimatedCounter from '@/components/ui/AnimatedCounter'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
|
||||
const icons = [TrendingUp, ShieldAlert, Euro]
|
||||
|
||||
export default function ProblemSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="problem" className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.problem.tag}
|
||||
title={i.problem.title}
|
||||
titleHighlight={i.problem.titleHighlight}
|
||||
subtitle={i.problem.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{i.problem.cards.map((card, idx) => {
|
||||
const Icon = icons[idx]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.1}>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-accent-electric/10 flex items-center justify-center">
|
||||
<Icon className="w-5 h-5 text-accent-electric" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-3xl font-bold mb-2">
|
||||
<AnimatedCounter value={card.metric} />
|
||||
</div>
|
||||
<p className="text-sm font-medium text-white/80 mb-2">{card.label}</p>
|
||||
<p className="text-sm text-white/40 mb-3">{card.description}</p>
|
||||
<p className="mono-label text-xs">{card.source}</p>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FadeInView delay={0.4} className="mt-12 text-center">
|
||||
<p className="text-white/40 text-sm max-w-2xl mx-auto border-t border-white/[0.06] pt-8">
|
||||
Die Konsequenz: Unternehmen riskieren Bussgelder, Betriebsstillstand und Wettbewerbsnachteile
|
||||
— oder sie investieren in eine Plattform, die regulatorische Komplexitaet deterministisch beherrscht.
|
||||
</p>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
'use client'
|
||||
|
||||
import { AlertTriangle, ShieldCheck, RefreshCw, Cpu, FileText, Link } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
|
||||
const iconMap: Record<string, typeof AlertTriangle> = { AlertTriangle, ShieldCheck, RefreshCw, Cpu, FileText, Link }
|
||||
|
||||
export default function SafetySection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="safety" className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.safety.tag}
|
||||
title={i.safety.title}
|
||||
titleHighlight={i.safety.titleHighlight}
|
||||
subtitle={i.safety.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{i.safety.features.map((feature, idx) => {
|
||||
const Icon = iconMap[feature.icon]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.08}>
|
||||
<div className="w-10 h-10 rounded-xl bg-amber-500/10 flex items-center justify-center mb-4">
|
||||
<Icon className="w-5 h-5 text-amber-400" />
|
||||
</div>
|
||||
<h3 className="text-sm font-bold mb-2">{feature.title}</h3>
|
||||
<p className="text-xs text-white/40 leading-relaxed">{feature.description}</p>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
'use client'
|
||||
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import TechBadge from '@/components/ui/TechBadge'
|
||||
|
||||
export default function SecuritySection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="security" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.security.tag}
|
||||
title={i.security.title}
|
||||
titleHighlight={i.security.titleHighlight}
|
||||
subtitle={i.security.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<FadeInView direction="left">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{i.security.tools.map((tool, idx) => (
|
||||
<GlassCard key={idx} delay={idx * 0.05} className="p-4">
|
||||
<p className="font-mono text-sm font-bold text-accent-electric mb-1">{tool.name}</p>
|
||||
<p className="text-xs text-white/40">{tool.description}</p>
|
||||
</GlassCard>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
<FadeInView direction="right">
|
||||
<div className="rounded-2xl border border-white/[0.06] bg-white/[0.03] p-6 h-full flex flex-col justify-center">
|
||||
<h3 className="text-lg font-bold mb-2">{i.security.integration.title}</h3>
|
||||
<p className="text-sm text-white/40 mb-6">{i.security.integration.description}</p>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="px-3 py-1.5 rounded-lg bg-red-500/10 border border-red-500/20 text-xs text-red-400 font-mono">
|
||||
CVE-2024-XXXX
|
||||
</span>
|
||||
<ArrowRight className="w-4 h-4 text-white/20" />
|
||||
<span className="px-3 py-1.5 rounded-lg bg-accent-electric/10 border border-accent-electric/20 text-xs text-accent-electric font-mono">
|
||||
JIRA-SEC-142
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mt-3">
|
||||
{i.security.integration.targets.map((target, idx) => (
|
||||
<TechBadge key={idx}>{target}</TechBadge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
'use client'
|
||||
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import TechBadge from '@/components/ui/TechBadge'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
|
||||
const tools = [
|
||||
{ name: 'SAST', de: 'Statische Code-Analyse für Firmware/Embedded', en: 'Static code analysis for firmware/embedded' },
|
||||
{ name: 'DAST', de: 'Dynamische Sicherheitstests gegen laufende Systeme', en: 'Dynamic security testing against running systems' },
|
||||
{ name: 'SBOM', de: 'Software Bill of Materials — CRA-Pflicht ab 2027', en: 'Software Bill of Materials — CRA mandatory from 2027' },
|
||||
{ name: 'Pentesting', de: 'Automatisierte Schwachstellensuche', en: 'Automated vulnerability scanning' },
|
||||
{ name: 'Secret Detection', de: 'Hardcoded Credentials im Code finden', en: 'Find hardcoded credentials in code' },
|
||||
{ name: 'Dependency Audit', de: 'Bekannte CVEs in Abhängigkeiten', en: 'Known CVEs in dependencies' },
|
||||
]
|
||||
|
||||
const heading = {
|
||||
de: {
|
||||
tag: 'SECURITY TOOLCHAIN',
|
||||
title: 'Scan → Priorisierung →',
|
||||
titleHighlight: 'Fix → Nachweis.',
|
||||
subtitle: 'Jedes Finding wird automatisch priorisiert, als Jira-Ticket erstellt und mit Code-Fix-Vorschlag versehen.',
|
||||
pipeline: ['Scan', 'Findings', 'Priorisierung', 'Jira-Ticket', 'Fix', 'Verifikation', 'Evidence'],
|
||||
jiraTitle: 'Automatische Entscheidungsunterstützung',
|
||||
jiraItems: [
|
||||
'Blocker: Finding blockiert CE/CRA-Konformität',
|
||||
'Major: Software-only Fix möglich',
|
||||
'Minor: Kosmetisch, kein Compliance-Impact',
|
||||
'Hardware vs. Software: Brauche ich ein Board-Redesign?',
|
||||
'Code-Fix-Vorschlag direkt im Ticket',
|
||||
],
|
||||
},
|
||||
en: {
|
||||
tag: 'SECURITY TOOLCHAIN',
|
||||
title: 'Scan → Prioritize →',
|
||||
titleHighlight: 'Fix → Evidence.',
|
||||
subtitle: 'Every finding is automatically prioritized, created as a Jira ticket and provided with a code fix suggestion.',
|
||||
pipeline: ['Scan', 'Findings', 'Prioritization', 'Jira Ticket', 'Fix', 'Verification', 'Evidence'],
|
||||
jiraTitle: 'Automatic decision support',
|
||||
jiraItems: [
|
||||
'Blocker: Finding blocks CE/CRA compliance',
|
||||
'Major: Software-only fix possible',
|
||||
'Minor: Cosmetic, no compliance impact',
|
||||
'Hardware vs. Software: Do I need a board redesign?',
|
||||
'Code fix suggestion directly in the ticket',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default function SecurityToolchainSection() {
|
||||
const { lang } = useApp()
|
||||
const h = heading[lang]
|
||||
|
||||
return (
|
||||
<section className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading tag={h.tag} title={h.title} titleHighlight={h.titleHighlight} subtitle={h.subtitle} />
|
||||
|
||||
{/* Pipeline visualization */}
|
||||
<FadeInView className="mb-16">
|
||||
<div className="flex flex-wrap items-center justify-center gap-2">
|
||||
{h.pipeline.map((step, idx) => (
|
||||
<div key={idx} className="flex items-center gap-2">
|
||||
<span className="px-3 py-1.5 rounded-lg bg-accent-electric/10 border border-accent-electric/15 text-xs text-accent-electric font-mono">
|
||||
{step}
|
||||
</span>
|
||||
{idx < h.pipeline.length - 1 && (
|
||||
<ArrowRight className="w-3 h-3 text-white/20" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
{/* Tools grid + Jira integration */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<FadeInView direction="left">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
{tools.map((tool, idx) => (
|
||||
<GlassCard key={idx} delay={idx * 0.05} className="p-4">
|
||||
<p className="font-mono text-sm font-bold text-accent-electric mb-1">{tool.name}</p>
|
||||
<p className="text-xs text-white/40">{lang === 'de' ? tool.de : tool.en}</p>
|
||||
</GlassCard>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
<FadeInView direction="right">
|
||||
<div className="rounded-2xl border border-white/[0.06] bg-white/[0.03] p-6 h-full">
|
||||
<h3 className="text-lg font-bold mb-4">{h.jiraTitle}</h3>
|
||||
<ul className="space-y-3 mb-6">
|
||||
{h.jiraItems.map((item, idx) => (
|
||||
<li key={idx} className="flex items-start gap-3 text-sm text-white/50">
|
||||
<span className={`mt-1 w-2 h-2 rounded-full shrink-0 ${idx === 0 ? 'bg-red-400' : idx === 1 ? 'bg-amber-400' : idx === 2 ? 'bg-green-400' : 'bg-accent-electric'}`} />
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{['Jira', 'Linear', 'GitLab', 'GitHub'].map(t => (
|
||||
<TechBadge key={t}>{t}</TechBadge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
'use client'
|
||||
|
||||
import { Server, Shield, Globe } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import TechBadge from '@/components/ui/TechBadge'
|
||||
|
||||
export default function SovereignSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="sovereign" className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.sovereign.tag}
|
||||
title={i.sovereign.title}
|
||||
titleHighlight={i.sovereign.titleHighlight}
|
||||
subtitle={i.sovereign.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
{i.sovereign.features.map((feature, idx) => {
|
||||
const icons = [Server, Shield, Globe]
|
||||
const Icon = icons[idx]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.1}>
|
||||
<div className="w-10 h-10 rounded-xl bg-accent-purple/10 flex items-center justify-center mb-4">
|
||||
<Icon className="w-5 h-5 text-accent-purple" />
|
||||
</div>
|
||||
<h3 className="text-sm font-bold mb-2">{feature.title}</h3>
|
||||
<p className="text-xs text-white/40">{feature.description}</p>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<FadeInView>
|
||||
<div className="rounded-2xl border border-accent-purple/20 bg-accent-purple/[0.04] p-6 md:p-8">
|
||||
<div className="flex flex-col md:flex-row items-start md:items-center gap-6">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-bold mb-2">{i.sovereign.appliance.title}</h3>
|
||||
<p className="text-sm text-white/40 mb-4">{i.sovereign.appliance.description}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{i.sovereign.appliance.specs.map((spec, idx) => (
|
||||
<TechBadge key={idx}>{spec}</TechBadge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-32 h-32 rounded-2xl bg-white/[0.04] border border-white/[0.06] flex items-center justify-center shrink-0">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-3 bg-white/10 rounded-sm mx-auto mb-2" />
|
||||
<div className="w-12 h-12 bg-white/[0.06] rounded-lg mx-auto border border-white/[0.08]" />
|
||||
<div className="w-4 h-0.5 bg-white/10 rounded-full mx-auto mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
'use client'
|
||||
|
||||
import { Factory, Truck, Cpu, Zap } from 'lucide-react'
|
||||
import { t } from '@/lib/content'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
import TechBadge from '@/components/ui/TechBadge'
|
||||
|
||||
const iconMap: Record<string, typeof Factory> = { Factory, Truck, Cpu, Zap }
|
||||
|
||||
export default function TargetSection() {
|
||||
const { lang } = useApp()
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<section id="targets" className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={i.targets.tag}
|
||||
title={i.targets.title}
|
||||
titleHighlight={i.targets.titleHighlight}
|
||||
subtitle={i.targets.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{i.targets.industries.map((industry, idx) => {
|
||||
const Icon = iconMap[industry.icon]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.1}>
|
||||
<div className="w-10 h-10 rounded-xl bg-accent-electric/10 flex items-center justify-center mb-4">
|
||||
<Icon className="w-5 h-5 text-accent-electric" />
|
||||
</div>
|
||||
<h3 className="text-sm font-bold mb-3">{industry.name}</h3>
|
||||
<div className="flex flex-wrap gap-1.5 mb-4">
|
||||
{industry.regulations.map((reg, ri) => (
|
||||
<TechBadge key={ri}>{reg}</TechBadge>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-accent-signal/80 font-mono">{industry.roi}</p>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { Linkedin, Github, ArrowUpRight } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import SectionHeading from '@/components/ui/SectionHeading'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
|
||||
interface TeamMember {
|
||||
name: string
|
||||
roleDe: string
|
||||
roleEn: string
|
||||
bioDe: string
|
||||
bioEn: string
|
||||
equityPct: number
|
||||
expertise: string[]
|
||||
links: { type: 'linkedin' | 'github'; url: string }[]
|
||||
initials: string
|
||||
gradient: string
|
||||
}
|
||||
|
||||
const team: TeamMember[] = [
|
||||
{
|
||||
name: 'Benjamin Bönisch',
|
||||
roleDe: 'CEO & Co-Founder',
|
||||
roleEn: 'CEO & Co-Founder',
|
||||
bioDe: 'Ehemaliger Lehrer mit Leidenschaft für EdTech und Datenschutz. Hat BreakPilot als DSGVO-konforme Bildungsplattform gegründet und zum Self-Hosted Compliance-Anbieter weiterentwickelt.',
|
||||
bioEn: 'Former teacher with a passion for EdTech and data privacy. Founded BreakPilot as a GDPR-compliant education platform and evolved it into a self-hosted compliance provider.',
|
||||
equityPct: 50,
|
||||
expertise: ['EdTech', 'DSGVO', 'Produktstrategie', 'Go-to-Market'],
|
||||
links: [
|
||||
{ type: 'linkedin', url: 'https://linkedin.com/in/benjamin-boenisch' },
|
||||
],
|
||||
initials: 'BB',
|
||||
gradient: 'from-accent-electric to-accent-indigo',
|
||||
},
|
||||
{
|
||||
name: 'Sharang Parnerkar',
|
||||
roleDe: 'CTO & Co-Founder',
|
||||
roleEn: 'CTO & Co-Founder',
|
||||
bioDe: 'Full-Stack-Ingenieur mit Expertise in KI/ML, Apple Silicon Optimierung und verteilten Systemen. Verantwortlich für die technische Architektur der ComplAI-Plattform.',
|
||||
bioEn: 'Full-stack engineer with expertise in AI/ML, Apple Silicon optimization, and distributed systems. Responsible for the technical architecture of the ComplAI platform.',
|
||||
equityPct: 50,
|
||||
expertise: ['AI/ML', 'Apple Silicon', 'Full-Stack', 'DevOps'],
|
||||
links: [
|
||||
{ type: 'github', url: 'https://github.com/sharangp' },
|
||||
],
|
||||
initials: 'SP',
|
||||
gradient: 'from-accent-indigo to-accent-purple',
|
||||
},
|
||||
]
|
||||
|
||||
const headingContent = {
|
||||
de: {
|
||||
tag: 'TEAM',
|
||||
title: 'Die Menschen hinter',
|
||||
titleHighlight: 'BreakPilot.',
|
||||
subtitle: 'Gründer mit Domain-Expertise in Compliance, KI und Produktentwicklung.',
|
||||
equity: 'Equity',
|
||||
cta: 'Offene Positionen ansehen',
|
||||
hiring: 'Wir stellen ein',
|
||||
hiringText: 'BreakPilot wächst. Wir suchen Ingenieure, die regulatorische Komplexität als technisches Problem lösen wollen.',
|
||||
roles: ['Backend Engineer (Python/Go)', 'Regulatory Analyst', 'DevOps Engineer'],
|
||||
},
|
||||
en: {
|
||||
tag: 'TEAM',
|
||||
title: 'The people behind',
|
||||
titleHighlight: 'BreakPilot.',
|
||||
subtitle: 'Founders with domain expertise in compliance, AI and product development.',
|
||||
equity: 'Equity',
|
||||
cta: 'View open positions',
|
||||
hiring: "We're hiring",
|
||||
hiringText: 'BreakPilot is growing. We are looking for engineers who want to solve regulatory complexity as a technical problem.',
|
||||
roles: ['Backend Engineer (Python/Go)', 'Regulatory Analyst', 'DevOps Engineer'],
|
||||
},
|
||||
}
|
||||
|
||||
const LinkIcons = { linkedin: Linkedin, github: Github }
|
||||
|
||||
export default function TeamSection() {
|
||||
const { lang } = useApp()
|
||||
const h = headingContent[lang]
|
||||
|
||||
return (
|
||||
<section className="py-24 sm:py-32 pt-32">
|
||||
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<SectionHeading
|
||||
tag={h.tag}
|
||||
title={h.title}
|
||||
titleHighlight={h.titleHighlight}
|
||||
subtitle={h.subtitle}
|
||||
/>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-16">
|
||||
{team.map((member, idx) => (
|
||||
<motion.div
|
||||
key={member.name}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: idx * 0.15, duration: 0.5 }}
|
||||
className="bg-white/[0.04] backdrop-blur-xl border border-white/[0.08] rounded-2xl p-6 flex flex-col hover:border-accent-electric/20 transition-colors"
|
||||
>
|
||||
{/* Header: avatar + name + role + equity */}
|
||||
<div className="flex items-center gap-4 mb-5">
|
||||
<div className={`w-16 h-16 rounded-2xl bg-gradient-to-br ${member.gradient} flex items-center justify-center shrink-0 shadow-lg shadow-accent-electric/20`}>
|
||||
<span className="text-xl font-bold text-white">{member.initials}</span>
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2 mb-0.5 flex-wrap">
|
||||
<h3 className="text-xl font-bold text-white truncate">{member.name}</h3>
|
||||
<div className="flex items-center gap-1.5">
|
||||
{member.links.map((link) => {
|
||||
const Icon = LinkIcons[link.type]
|
||||
return (
|
||||
<a
|
||||
key={link.type}
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-white/25 hover:text-accent-electric transition-colors"
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-accent-electric text-sm font-medium">
|
||||
{lang === 'de' ? member.roleDe : member.roleEn}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Equity pill */}
|
||||
<div className="text-right shrink-0">
|
||||
<div className="text-[10px] uppercase tracking-wider text-white/30">{h.equity}</div>
|
||||
<div className="text-base font-bold text-white tabular-nums">{member.equityPct}%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bio */}
|
||||
<p className="text-sm text-white/60 leading-relaxed mb-5 flex-1">
|
||||
{lang === 'de' ? member.bioDe : member.bioEn}
|
||||
</p>
|
||||
|
||||
{/* Expertise tags */}
|
||||
<div className="flex flex-wrap gap-1.5 pt-4 border-t border-white/[0.06]">
|
||||
{member.expertise.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
className="text-xs px-2.5 py-1 rounded-full bg-accent-electric/10 text-accent-electric/80 border border-accent-electric/15"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Hiring CTA */}
|
||||
<FadeInView>
|
||||
<div className="rounded-2xl border border-accent-electric/15 bg-accent-electric/[0.04] p-8 text-center">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-accent-signal/10 border border-accent-signal/20 text-accent-signal text-xs font-medium mb-4">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-accent-signal animate-pulse" />
|
||||
{h.hiring}
|
||||
</div>
|
||||
<p className="text-sm text-white/50 max-w-lg mx-auto mb-6">
|
||||
{h.hiringText}
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-2 mb-6">
|
||||
{h.roles.map((role) => (
|
||||
<span key={role} className="px-3 py-1.5 rounded-lg bg-white/[0.06] border border-white/[0.06] text-xs text-white/60 font-mono">
|
||||
{role}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<a
|
||||
href="mailto:jobs@breakpilot.ai"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-accent-electric text-white text-sm font-medium hover:bg-blue-500 transition-colors glow-blue"
|
||||
>
|
||||
{h.cta}
|
||||
<ArrowUpRight className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
'use client'
|
||||
|
||||
import { Shield, FileCheck, ClipboardCheck } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import AnimatedCounter from '@/components/ui/AnimatedCounter'
|
||||
|
||||
const pillars = {
|
||||
de: [
|
||||
{ icon: Shield, label: 'Deterministisch', detail: 'Regelbasiert, nicht generativ' },
|
||||
{ icon: FileCheck, label: 'Nachvollziehbar', detail: 'Jedes Ergebnis mit Rechtsquelle' },
|
||||
{ icon: ClipboardCheck, label: 'Auditierbar', detail: 'Vollständiger Decision Trail' },
|
||||
],
|
||||
en: [
|
||||
{ icon: Shield, label: 'Deterministic', detail: 'Rule-based, not generative' },
|
||||
{ icon: FileCheck, label: 'Traceable', detail: 'Every result with legal source' },
|
||||
{ icon: ClipboardCheck, label: 'Auditable', detail: 'Complete decision trail' },
|
||||
],
|
||||
}
|
||||
|
||||
const stats = [
|
||||
{ value: '294.000+', labelDe: 'atomare Controls', labelEn: 'atomic controls' },
|
||||
{ value: '380+', labelDe: 'Rechtsquellen', labelEn: 'legal sources' },
|
||||
{ value: '1.058', labelDe: 'Hazard Patterns', labelEn: 'hazard patterns' },
|
||||
{ value: '751', labelDe: 'Normen', labelEn: 'standards' },
|
||||
]
|
||||
|
||||
export default function TrustBar() {
|
||||
const { lang } = useApp()
|
||||
const items = pillars[lang]
|
||||
|
||||
return (
|
||||
<section className="py-24 sm:py-32 section-alt">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Trust pillars */}
|
||||
<FadeInView>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
|
||||
{items.map((pillar, idx) => {
|
||||
const Icon = pillar.icon
|
||||
return (
|
||||
<div key={idx} className="flex items-center gap-4 p-4 rounded-xl border border-white/[0.06] bg-white/[0.03]">
|
||||
<div className="w-10 h-10 rounded-xl bg-accent-indigo/10 flex items-center justify-center shrink-0">
|
||||
<Icon className="w-5 h-5 text-accent-indigo" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-bold">{pillar.label}</h3>
|
||||
<p className="text-xs text-white/40">{pillar.detail}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
{/* Stats */}
|
||||
<FadeInView delay={0.2}>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{stats.map((stat, idx) => (
|
||||
<div key={idx} className="text-center">
|
||||
<div className="text-3xl font-bold mb-1 gradient-text">
|
||||
<AnimatedCounter value={stat.value} />
|
||||
</div>
|
||||
<p className="text-xs text-white/40">{lang === 'de' ? stat.labelDe : stat.labelEn}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { ArrowRight, Scan, FileCheck, Users } from 'lucide-react'
|
||||
import { useApp } from '@/lib/context'
|
||||
import FadeInView from '@/components/ui/FadeInView'
|
||||
import GlassCard from '@/components/ui/GlassCard'
|
||||
|
||||
const icons = [Scan, FileCheck, Users]
|
||||
const gradients = ['from-red-500/10 to-amber-500/10', 'from-accent-electric/10 to-accent-indigo/10', 'from-accent-purple/10 to-accent-electric/10']
|
||||
|
||||
const cases = {
|
||||
de: [
|
||||
{
|
||||
title: '187 Pentest-Findings — was davon ist wirklich kritisch?',
|
||||
description: 'SAST/DAST + Pentesting liefern 187 Findings für ein Embedded Board. BreakPilot priorisiert automatisch: 3 blockieren CE/CRA, 12 sind Software-only Fixes, der Rest ist kosmetisch. Jira-Tickets mit Code-Fix-Vorschlägen werden automatisch erstellt.',
|
||||
highlight: 'Hardware-Redesign für €50k? Wahrscheinlich nicht nötig.',
|
||||
href: '/product-compliance',
|
||||
cta: 'Product Compliance ansehen',
|
||||
},
|
||||
{
|
||||
title: 'CE-Akte in Stunden statt Monaten',
|
||||
description: 'Sondermaschinenbauer beschreibt einen Cobot in 14 Textfeldern. 1.058 Hazard Patterns feuern deterministisch. 12 Gefährdungen, 30 Maßnahmen, 45 Normen — automatisch zugeordnet, nicht generiert.',
|
||||
highlight: 'CE-Akte nach MVO 2023/1230 Anhang IV per Klick.',
|
||||
href: '/plattform',
|
||||
cta: 'Plattform entdecken',
|
||||
},
|
||||
{
|
||||
title: 'Der CE-Experte geht in Rente',
|
||||
description: 'Ein Junior-Konstrukteur nutzt BreakPilot für seine erste Risikobeurteilung. Konsistente Erstbewertung mit Explainability. Der Senior reviewt nur noch, statt alles selbst zu machen.',
|
||||
highlight: 'Expertenwissen operationalisiert, nicht in Köpfen eingesperrt.',
|
||||
href: '/plattform',
|
||||
cta: 'Plattform entdecken',
|
||||
},
|
||||
],
|
||||
en: [
|
||||
{
|
||||
title: '187 pentest findings — which ones actually matter?',
|
||||
description: 'SAST/DAST + pentesting deliver 187 findings for an embedded board. BreakPilot prioritizes automatically: 3 block CE/CRA, 12 are software-only fixes, the rest is cosmetic. Jira tickets with code fix suggestions are created automatically.',
|
||||
highlight: 'Hardware redesign for €50k? Probably not necessary.',
|
||||
href: '/product-compliance',
|
||||
cta: 'View Product Compliance',
|
||||
},
|
||||
{
|
||||
title: 'CE file in hours instead of months',
|
||||
description: 'A special machine builder describes a cobot in 14 text fields. 1,058 hazard patterns fire deterministically. 12 hazards, 30 mitigations, 45 norms — automatically mapped, not generated.',
|
||||
highlight: 'CE file per Machinery Regulation 2023/1230 Annex IV with one click.',
|
||||
href: '/plattform',
|
||||
cta: 'Discover Platform',
|
||||
},
|
||||
{
|
||||
title: 'The CE expert is retiring',
|
||||
description: 'A junior engineer uses BreakPilot for their first risk assessment. Consistent initial assessment with explainability. The senior only reviews instead of doing everything.',
|
||||
highlight: 'Expert knowledge operationalized, not locked in heads.',
|
||||
href: '/plattform',
|
||||
cta: 'Discover Platform',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default function UseCaseCards() {
|
||||
const { lang } = useApp()
|
||||
const items = cases[lang]
|
||||
|
||||
return (
|
||||
<section className="py-24 sm:py-32">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{items.map((item, idx) => {
|
||||
const Icon = icons[idx]
|
||||
return (
|
||||
<GlassCard key={idx} delay={idx * 0.1} hover={false} className="flex flex-col">
|
||||
<div className={`w-12 h-12 rounded-2xl bg-gradient-to-br ${gradients[idx]} flex items-center justify-center mb-5`}>
|
||||
<Icon className="w-6 h-6 text-white/70" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold mb-3 leading-snug">{item.title}</h3>
|
||||
<p className="text-sm text-white/40 mb-4 flex-1">{item.description}</p>
|
||||
<p className="text-sm text-accent-electric font-medium mb-5">{item.highlight}</p>
|
||||
<Link
|
||||
href={item.href}
|
||||
className="inline-flex items-center gap-2 text-sm text-white/50 hover:text-white transition-colors group"
|
||||
>
|
||||
{item.cta}
|
||||
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</GlassCard>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user