feat(pitch-deck): rewrite CompetitionSlide with 6 detailed competitor profiles
- Add Vanta, Drata, Sprinto (international) alongside Proliance, DataGuard, heyData (DACH) - Each card: HQ city/country, offices, employees, revenue, customers + countries, funding, investors, AI badge - Two tabs: Overview & Comparison / Feature Matrix (Detail) - 44-feature comparison table with collapsible sections: Top 5 Unterschiede, Alle Features, USP - Efficiency ratios table (revenue/employee, customers/employee) - DACH landscape note (Secjur, Usercentrics, Caralegal, 2B Advice, OneTrust) - Research-backed data: Vanta $220M/$4.15B, Drata $100M/$2B, Sprinto $38M, DataGuard €52M, heyData €15M - Dynamic feature/USP counts in subtitle - Bilingual (de/en) with i18n subtitle update Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Language, PitchFeature, PitchCompetitor } from '@/lib/types'
|
||||
import { t } from '@/lib/i18n'
|
||||
import { ShieldCheck, Code2, ScanLine, FileSearch, Package, Bug } from 'lucide-react'
|
||||
import {
|
||||
ChevronDown, ChevronRight, Globe, Building2, Users, TrendingUp,
|
||||
DollarSign, Cpu, Star, Check, X, Minus,
|
||||
} from 'lucide-react'
|
||||
import GradientText from '../ui/GradientText'
|
||||
import FadeInView from '../ui/FadeInView'
|
||||
import FeatureMatrix from '../ui/FeatureMatrix'
|
||||
import GlassCard from '../ui/GlassCard'
|
||||
import BrandName from '../ui/BrandName'
|
||||
|
||||
@@ -15,93 +18,604 @@ interface CompetitionSlideProps {
|
||||
competitors: PitchCompetitor[]
|
||||
}
|
||||
|
||||
const securityFeatures = {
|
||||
de: [
|
||||
{ icon: ShieldCheck, title: 'Self-Hosted + PII-Redaction', desc: 'Einziger Anbieter mit On-Premise-Deployment. LLM Gateway maskiert personenbezogene Daten vor KI-Verarbeitung' },
|
||||
{ icon: ScanLine, title: 'DevSecOps Security Suite', desc: 'Semgrep, Gitleaks, Trivy, Grype — 6 integrierte Tools scannen Code, Container und Dependencies kontinuierlich' },
|
||||
{ icon: Bug, title: 'SBOM + CI/CD Evidence', desc: 'CycloneDX/SPDX SBOM-Generator + automatische Compliance-Nachweise direkt aus der Build-Pipeline' },
|
||||
{ icon: Package, title: 'Multi-Framework SDK', desc: 'Consent-Banner und Compliance-Widgets fuer React, Vue, Angular, iOS, Android, Flutter' },
|
||||
{ icon: FileSearch, title: 'IPFS Dokumenten-Archivierung', desc: 'Dezentrale, manipulationssichere Archivierung mit kryptographischem Nachweis der Unveraendertheit' },
|
||||
{ icon: Code2, title: 'KI-Compliance-Advisor (RAG)', desc: '2.274 indexierte Rechtstexte, KI-gestuetzte Dokumentenerstellung, Scope Engine L1-L4' },
|
||||
],
|
||||
en: [
|
||||
{ icon: ShieldCheck, title: 'Self-Hosted + PII Redaction', desc: 'Only provider with on-premise deployment. LLM Gateway masks personal data before AI processing' },
|
||||
{ icon: ScanLine, title: 'DevSecOps Security Suite', desc: 'Semgrep, Gitleaks, Trivy, Grype — 6 integrated tools continuously scan code, containers and dependencies' },
|
||||
{ icon: Bug, title: 'SBOM + CI/CD Evidence', desc: 'CycloneDX/SPDX SBOM generator + automatic compliance evidence directly from the build pipeline' },
|
||||
{ icon: Package, title: 'Multi-Framework SDK', desc: 'Consent banners and compliance widgets for React, Vue, Angular, iOS, Android, Flutter' },
|
||||
{ icon: FileSearch, title: 'IPFS Document Archiving', desc: 'Decentralized, tamper-proof archiving with cryptographic proof of integrity' },
|
||||
{ icon: Code2, title: 'AI Compliance Advisor (RAG)', desc: '2,274 indexed legal texts, AI-powered document generation, Scope Engine L1-L4' },
|
||||
],
|
||||
// ─── Extended Competitor Data ──────────────────────────────────────────────────
|
||||
|
||||
interface ExtendedCompetitor {
|
||||
name: string
|
||||
flag: string
|
||||
hq: string
|
||||
hqCountry: string
|
||||
offices: string[]
|
||||
founded: number
|
||||
employees: number
|
||||
revenue: string
|
||||
revenueNum: number
|
||||
customers: number
|
||||
customerCountries: string
|
||||
fundingTotal: string
|
||||
fundingRound: string
|
||||
investors: string[]
|
||||
aiUsage: 'full' | 'partial' | 'none'
|
||||
aiDetail: { de: string; en: string }
|
||||
market: { de: string; en: string }
|
||||
pricing: string
|
||||
isInternational: boolean
|
||||
}
|
||||
|
||||
const EXTENDED_COMPETITORS: ExtendedCompetitor[] = [
|
||||
{
|
||||
name: 'Vanta',
|
||||
flag: '🇺🇸',
|
||||
hq: 'San Francisco, CA',
|
||||
hqCountry: 'USA',
|
||||
offices: ['New York', 'Dublin', 'London', 'Sydney'],
|
||||
founded: 2018,
|
||||
employees: 1695,
|
||||
revenue: '$220M ARR',
|
||||
revenueNum: 220_000_000,
|
||||
customers: 12000,
|
||||
customerCountries: '58 Laender',
|
||||
fundingTotal: '$504M',
|
||||
fundingRound: 'Series D ($150M, $4.15B val.)',
|
||||
investors: ['Sequoia Capital', 'Wellington Mgmt', 'Craft Ventures', 'CrowdStrike', 'Goldman Sachs', 'Y Combinator'],
|
||||
aiUsage: 'full',
|
||||
aiDetail: { de: 'Vanta AI Agent: Agentic Compliance, Policy-Gen, VRM-Agent, ISO 42001', en: 'Vanta AI Agent: Agentic compliance, policy gen, VRM agent, ISO 42001' },
|
||||
market: { de: 'Global — SOC 2, ISO 27001, HIPAA, PCI DSS', en: 'Global — SOC 2, ISO 27001, HIPAA, PCI DSS' },
|
||||
pricing: '$10K–80K/yr',
|
||||
isInternational: true,
|
||||
},
|
||||
{
|
||||
name: 'Drata',
|
||||
flag: '🇺🇸',
|
||||
hq: 'San Diego, CA',
|
||||
hqCountry: 'USA',
|
||||
offices: ['San Diego'],
|
||||
founded: 2020,
|
||||
employees: 732,
|
||||
revenue: '$100M ARR',
|
||||
revenueNum: 100_000_000,
|
||||
customers: 8000,
|
||||
customerCountries: '80+ Laender',
|
||||
fundingTotal: '$328M',
|
||||
fundingRound: 'Series C ($200M, $2B val.)',
|
||||
investors: ['ICONIQ Growth', 'GGV Capital', 'Salesforce Ventures', 'SentinelOne'],
|
||||
aiUsage: 'full',
|
||||
aiDetail: { de: 'AI Agent: VRM, Doc-Review, Risiko-Scoring, SafeBase AIQA', en: 'AI Agent: VRM, doc review, risk scoring, SafeBase AIQA' },
|
||||
market: { de: 'Global — SOC 2, ISO, HIPAA, GDPR (oberfl.)', en: 'Global — SOC 2, ISO, HIPAA, GDPR (shallow)' },
|
||||
pricing: '$10K–100K/yr',
|
||||
isInternational: true,
|
||||
},
|
||||
{
|
||||
name: 'Sprinto',
|
||||
flag: '🇮🇳',
|
||||
hq: 'Bangalore',
|
||||
hqCountry: 'Indien',
|
||||
offices: ['Bangalore'],
|
||||
founded: 2020,
|
||||
employees: 316,
|
||||
revenue: '$38M ARR',
|
||||
revenueNum: 38_000_000,
|
||||
customers: 3000,
|
||||
customerCountries: '75+ Laender',
|
||||
fundingTotal: '$32M',
|
||||
fundingRound: 'Series B ($20M, 2024)',
|
||||
investors: ['Accel', 'Elevation Capital', 'Blume Ventures'],
|
||||
aiUsage: 'full',
|
||||
aiDetail: { de: 'Autonomous Compliance Engine, No-Code AI Agent Builder', en: 'Autonomous compliance engine, no-code AI agent builder' },
|
||||
market: { de: 'Global SMBs — SOC 2, ISO, GDPR', en: 'Global SMBs — SOC 2, ISO, GDPR' },
|
||||
pricing: '$6K–25K/yr',
|
||||
isInternational: true,
|
||||
},
|
||||
{
|
||||
name: 'Proliance',
|
||||
flag: '🇩🇪',
|
||||
hq: 'Muenchen',
|
||||
hqCountry: 'Deutschland',
|
||||
offices: ['Muenchen'],
|
||||
founded: 2017,
|
||||
employees: 65,
|
||||
revenue: '~€3.9M',
|
||||
revenueNum: 3_900_000,
|
||||
customers: 2000,
|
||||
customerCountries: 'DACH',
|
||||
fundingTotal: 'Pre-Seed',
|
||||
fundingRound: 'Pre-Seed (Possible Ventures)',
|
||||
investors: ['Possible Ventures'],
|
||||
aiUsage: 'none',
|
||||
aiDetail: { de: 'Basis-Risikoerkennung, keine LLM/Agenten', en: 'Basic risk detection, no LLM/agents' },
|
||||
market: { de: 'DACH — DSGVO, ePrivacy, KMUs', en: 'DACH — GDPR, ePrivacy, SMBs' },
|
||||
pricing: '€1.5K–5.7K/yr',
|
||||
isInternational: false,
|
||||
},
|
||||
{
|
||||
name: 'DataGuard',
|
||||
flag: '🇩🇪',
|
||||
hq: 'Muenchen',
|
||||
hqCountry: 'Deutschland',
|
||||
offices: ['Muenchen', 'Berlin', 'London', 'Wien', 'Stockholm'],
|
||||
founded: 2017,
|
||||
employees: 250,
|
||||
revenue: '~€52M',
|
||||
revenueNum: 52_000_000,
|
||||
customers: 4000,
|
||||
customerCountries: '50+ Laender',
|
||||
fundingTotal: '€80M',
|
||||
fundingRound: 'Series B (€61M, €341M val.)',
|
||||
investors: ['Morgan Stanley Expansion', 'One Peak Partners'],
|
||||
aiUsage: 'partial',
|
||||
aiDetail: { de: 'Marketing: 40% weniger Aufwand, keine Agenten/LLM', en: 'Marketing: 40% effort reduction, no agents/LLM' },
|
||||
market: { de: 'DACH + UK — GDPR, ISO 27001, TISAX', en: 'DACH + UK — GDPR, ISO 27001, TISAX' },
|
||||
pricing: '€6K–24K+/yr',
|
||||
isInternational: false,
|
||||
},
|
||||
{
|
||||
name: 'heyData',
|
||||
flag: '🇩🇪',
|
||||
hq: 'Berlin',
|
||||
hqCountry: 'Deutschland',
|
||||
offices: ['Berlin'],
|
||||
founded: 2020,
|
||||
employees: 58,
|
||||
revenue: '~€15M',
|
||||
revenueNum: 15_000_000,
|
||||
customers: 2000,
|
||||
customerCountries: 'EU',
|
||||
fundingTotal: '€18.3M',
|
||||
fundingRound: 'Series A ($16.5M, Jan 2026)',
|
||||
investors: ['Riverside Acceleration Capital'],
|
||||
aiUsage: 'partial',
|
||||
aiDetail: { de: 'KI-Marketing, keine sichtbaren Agenten', en: 'AI marketing, no visible agents' },
|
||||
market: { de: 'DACH + EU — DSGVO, Kleinunternehmen', en: 'DACH + EU — GDPR, small businesses' },
|
||||
pricing: '€1K–3.8K/yr',
|
||||
isInternational: false,
|
||||
},
|
||||
]
|
||||
|
||||
// ─── Feature Comparison Data ───────────────────────────────────────────────────
|
||||
|
||||
type FeatureStatus = true | false | 'partial'
|
||||
|
||||
interface ComparisonFeature {
|
||||
de: string
|
||||
en: string
|
||||
bp: FeatureStatus
|
||||
vanta: FeatureStatus
|
||||
drata: FeatureStatus
|
||||
sprinto: FeatureStatus
|
||||
proliance: FeatureStatus
|
||||
dataguard: FeatureStatus
|
||||
heydata: FeatureStatus
|
||||
isDiff: boolean
|
||||
isUSP: boolean
|
||||
}
|
||||
|
||||
const ALL_FEATURES: ComparisonFeature[] = [
|
||||
// Top 5 Differentiators (isDiff=true) — no other vendor has ANY of these
|
||||
{ de: 'Self-Hosted / On-Premise', en: 'Self-Hosted / On-Premise', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true },
|
||||
{ de: 'Code-Security & DevSecOps (6 Tools)', en: 'Code Security & DevSecOps (6 Tools)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true },
|
||||
{ de: '57 SDK-Compliance-Module', en: '57 SDK Compliance Modules', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true },
|
||||
{ de: 'Hardware-Moat (Mac Mini/Studio)', en: 'Hardware Moat (Mac Mini/Studio)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true },
|
||||
{ de: 'PII-Redaction LLM Gateway', en: 'PII Redaction LLM Gateway', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true },
|
||||
// More USPs
|
||||
{ de: 'IPFS Dokumenten-Archivierung', en: 'IPFS Document Archiving', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: true },
|
||||
{ de: 'SBOM-Generator (CycloneDX/SPDX)', en: 'SBOM Generator (CycloneDX/SPDX)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: true },
|
||||
{ de: 'Multi-Framework Consent SDK', en: 'Multi-Framework Consent SDK', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: true },
|
||||
{ de: 'RAG mit 2.274 Rechtstexten', en: 'RAG with 2,274 Legal Texts', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: true },
|
||||
// Compliance Features (shared)
|
||||
{ de: 'DSGVO / GDPR', en: 'GDPR', bp: true, vanta: 'partial', drata: 'partial', sprinto: 'partial', proliance: true, dataguard: true, heydata: true, isDiff: false, isUSP: false },
|
||||
{ de: 'AI Act', en: 'AI Act', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Cyber Resilience Act (CRA)', en: 'Cyber Resilience Act (CRA)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'NIS2-Richtlinie', en: 'NIS2 Directive', bp: true, vanta: false, drata: 'partial', sprinto: false, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'SOC 2', en: 'SOC 2', bp: 'partial', vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'ISO 27001', en: 'ISO 27001', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'HIPAA', en: 'HIPAA', bp: false, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'TISAX', en: 'TISAX', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'HinSchG (Whistleblower)', en: 'HinSchG (Whistleblower)', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
// Functional Features
|
||||
{ de: 'VVT (Art. 30 DSGVO)', en: 'Records of Processing (Art. 30)', bp: true, vanta: false, drata: false, sprinto: false, proliance: true, dataguard: true, heydata: true, isDiff: false, isUSP: false },
|
||||
{ de: 'TOM-Dokumentation', en: 'TOM Documentation', bp: true, vanta: false, drata: false, sprinto: false, proliance: true, dataguard: true, heydata: 'partial', isDiff: false, isUSP: false },
|
||||
{ de: 'DSFA (Art. 35 DSGVO)', en: 'DPIA (Art. 35 GDPR)', bp: true, vanta: false, drata: false, sprinto: false, proliance: true, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Loeschkonzept / Loeschfristen', en: 'Deletion Concept / Retention', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Auftragsverarbeiter-Mgmt', en: 'Vendor/Processor Management', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: true, dataguard: true, heydata: 'partial', isDiff: false, isUSP: false },
|
||||
{ de: 'Consent Management', en: 'Consent Management', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: false, heydata: 'partial', isDiff: false, isUSP: false },
|
||||
{ de: 'Betroffenenrechte (DSR)', en: 'Data Subject Requests', bp: true, vanta: false, drata: false, sprinto: false, proliance: true, dataguard: true, heydata: 'partial', isDiff: false, isUSP: false },
|
||||
{ de: 'Risikobewertung', en: 'Risk Assessment', bp: true, vanta: true, drata: true, sprinto: true, proliance: 'partial', dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Audit-Management', en: 'Audit Management', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Schulungs-Management', en: 'Training Management', bp: true, vanta: 'partial', drata: 'partial', sprinto: 'partial', proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Policy-Generator', en: 'Policy Generator', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: true, dataguard: true, heydata: 'partial', isDiff: false, isUSP: false },
|
||||
{ de: 'Incident Response', en: 'Incident Response', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
// Technical Features
|
||||
{ de: 'KI-gestuetzte Analyse', en: 'AI-Powered Analysis', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Automatische Evidence-Sammlung', en: 'Automatic Evidence Collection', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Continuous Monitoring', en: 'Continuous Monitoring', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Integrations (Slack, Jira, etc.)', en: 'Integrations (Slack, Jira, etc.)', bp: 'partial', vanta: true, drata: true, sprinto: true, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'API / SDK', en: 'API / SDK', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Datensouveraenitaet (EU)', en: 'Data Sovereignty (EU)', bp: true, vanta: false, drata: false, sprinto: false, proliance: true, dataguard: true, heydata: true, isDiff: false, isUSP: false },
|
||||
{ de: 'Mehrmandantenfaehig', en: 'Multi-Tenancy', bp: true, vanta: true, drata: true, sprinto: true, proliance: 'partial', dataguard: true, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Data Mapping / Datenfluss', en: 'Data Mapping / Data Flow', bp: true, vanta: 'partial', drata: 'partial', sprinto: false, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Cookie-Banner Generator', en: 'Cookie Banner Generator', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: false, heydata: 'partial', isDiff: false, isUSP: false },
|
||||
{ de: 'Dokument-Generator (61 Vorlagen)', en: 'Document Generator (61 Templates)', bp: true, vanta: 'partial', drata: 'partial', sprinto: false, proliance: 'partial', dataguard: 'partial', heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Whistleblower-Portal', en: 'Whistleblower Portal', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Maschinenbau-Branchenfokus', en: 'Manufacturing Industry Focus', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Firmware & Embedded-Security', en: 'Firmware & Embedded Security', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
{ de: 'Autonomer KI-Support-Agent', en: 'Autonomous AI Support Agent', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false },
|
||||
]
|
||||
|
||||
// ─── DACH Landscape Note ───────────────────────────────────────────────────────
|
||||
|
||||
const DACH_NOTE = {
|
||||
de: 'Weitere DACH-Anbieter: Secjur (Hamburg, KI-Compliance, ~€5.5M Seed), Usercentrics (nur CMP, $117M Rev), Caralegal (Privacy/Risk, M&A 2025), 2B Advice (Legacy, 20+ J.), OneTrust (US-Enterprise, $500M+ ARR). Keiner kombiniert DSGVO + Code-Security + Self-Hosted KI.',
|
||||
en: 'Other DACH players: Secjur (Hamburg, AI compliance, ~€5.5M seed), Usercentrics (CMP only, $117M rev), Caralegal (privacy/risk, M&A 2025), 2B Advice (legacy, 20+ yrs), OneTrust (US enterprise, $500M+ ARR). None combines GDPR + code security + self-hosted AI.',
|
||||
}
|
||||
|
||||
// ─── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
function StatusIcon({ value }: { value: FeatureStatus }) {
|
||||
if (value === true) return <Check className="w-3.5 h-3.5 text-green-400 mx-auto" />
|
||||
if (value === 'partial') return <Minus className="w-3.5 h-3.5 text-yellow-400 mx-auto" />
|
||||
return <X className="w-3.5 h-3.5 text-white/15 mx-auto" />
|
||||
}
|
||||
|
||||
function AiBadge({ level, lang }: { level: 'full' | 'partial' | 'none'; lang: Language }) {
|
||||
const colors = { full: 'bg-green-500/15 text-green-400', partial: 'bg-yellow-500/15 text-yellow-400', none: 'bg-white/5 text-white/30' }
|
||||
const labels = { full: { de: 'KI', en: 'AI' }, partial: { de: 'Basis', en: 'Basic' }, none: { de: 'Keine', en: 'None' } }
|
||||
return (
|
||||
<span className={`text-[10px] px-1.5 py-0.5 rounded-full font-medium ${colors[level]}`}>
|
||||
<Cpu className="w-2.5 h-2.5 inline mr-0.5 -mt-px" />
|
||||
{labels[level][lang]}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function ratio(a: number, b: number): string {
|
||||
if (b === 0) return '—'
|
||||
const r = a / b
|
||||
if (r >= 1_000_000) return `${(r / 1_000_000).toFixed(1)}M`
|
||||
if (r >= 1_000) return `${(r / 1_000).toFixed(0)}k`
|
||||
return r.toFixed(0)
|
||||
}
|
||||
|
||||
// ─── Section Accordion ─────────────────────────────────────────────────────────
|
||||
|
||||
function SectionHeader({
|
||||
label,
|
||||
count,
|
||||
open,
|
||||
onToggle,
|
||||
accent,
|
||||
}: {
|
||||
label: string
|
||||
count: number
|
||||
open: boolean
|
||||
onToggle: () => void
|
||||
accent?: string
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
onClick={onToggle}
|
||||
className="w-full flex items-center gap-2 py-2 px-3 bg-white/[0.03] hover:bg-white/[0.06] border border-white/5 rounded-lg transition-colors text-left"
|
||||
>
|
||||
{open ? <ChevronDown className="w-4 h-4 text-white/40 shrink-0" /> : <ChevronRight className="w-4 h-4 text-white/40 shrink-0" />}
|
||||
<span className={`text-sm font-semibold ${accent || 'text-white/80'}`}>{label}</span>
|
||||
<span className="text-xs text-white/30 ml-1">({count})</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
// ─── Component ─────────────────────────────────────────────────────────────────
|
||||
|
||||
type ViewTab = 'overview' | 'features'
|
||||
|
||||
export default function CompetitionSlide({ lang, features, competitors }: CompetitionSlideProps) {
|
||||
const i = t(lang)
|
||||
const coreFeatures = features.filter(f => f.category !== 'security')
|
||||
const secFeats = securityFeatures[lang]
|
||||
const [activeTab, setActiveTab] = useState<ViewTab>('overview')
|
||||
const [openSections, setOpenSections] = useState<Set<string>>(new Set(['top5']))
|
||||
|
||||
const toggleSection = (key: string) => {
|
||||
setOpenSections(prev => {
|
||||
const next = new Set(prev)
|
||||
if (next.has(key)) next.delete(key)
|
||||
else next.add(key)
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
const top5 = ALL_FEATURES.filter(f => f.isDiff)
|
||||
const usps = ALL_FEATURES.filter(f => f.isUSP)
|
||||
const allFeatures = ALL_FEATURES
|
||||
|
||||
const competitorCols = ['bp', 'vanta', 'drata', 'sprinto', 'proliance', 'dataguard', 'heydata'] as const
|
||||
const competitorLabels = ['ComplAI', 'Vanta', 'Drata', 'Sprinto', 'Proliance', 'DataGuard', 'heyData']
|
||||
|
||||
const featureCount = ALL_FEATURES.length
|
||||
const uspCount = usps.length
|
||||
const subtitle = lang === 'de'
|
||||
? `${featureCount} Features, ${uspCount} USPs — kein Anbieter kombiniert DSGVO + Code-Security + Self-Hosted KI`
|
||||
: `${featureCount} features, ${uspCount} USPs — no provider combines GDPR + code security + self-hosted AI`
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FadeInView className="text-center mb-8">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-3">
|
||||
<div className="max-h-[calc(100vh-120px)] overflow-y-auto pr-1 pb-4 scrollbar-thin">
|
||||
{/* Header */}
|
||||
<FadeInView className="text-center mb-4">
|
||||
<h2 className="text-3xl md:text-4xl font-bold mb-2">
|
||||
<GradientText>{i.competition.title}</GradientText>
|
||||
</h2>
|
||||
<p className="text-lg text-white/50 max-w-2xl mx-auto">{i.competition.subtitle}</p>
|
||||
<p className="text-sm text-white/50 max-w-3xl mx-auto">{subtitle}</p>
|
||||
</FadeInView>
|
||||
|
||||
{/* Feature Matrix (Core Compliance) */}
|
||||
<FadeInView delay={0.3}>
|
||||
<GlassCard className="mb-6 p-4 overflow-x-auto" hover={false}>
|
||||
<FeatureMatrix features={coreFeatures} lang={lang} />
|
||||
</GlassCard>
|
||||
</FadeInView>
|
||||
|
||||
{/* Security & Developer Features — nur bei ComplAI */}
|
||||
<FadeInView delay={0.5}>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-sm font-semibold text-indigo-400 mb-3 flex items-center gap-2">
|
||||
<ShieldCheck className="w-4 h-4" />
|
||||
{lang === 'de' ? <>Integrierte Security & Developer Tools — nur bei <BrandName /></> : <>Integrated Security & Developer Tools — <BrandName /> only</>}
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
||||
{secFeats.map((feat, idx) => {
|
||||
const Icon = feat.icon
|
||||
return (
|
||||
<FadeInView key={idx} delay={0.6 + idx * 0.08}>
|
||||
<div className="bg-indigo-500/5 border border-indigo-500/10 rounded-xl p-3">
|
||||
<div className="flex items-center gap-2 mb-1.5">
|
||||
<Icon className="w-4 h-4 text-indigo-400 shrink-0" />
|
||||
<span className="text-xs font-semibold text-white">{feat.title}</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-white/40 leading-relaxed">{feat.desc}</p>
|
||||
</div>
|
||||
</FadeInView>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
|
||||
{/* Competitor Summary */}
|
||||
<div className="grid md:grid-cols-3 gap-4">
|
||||
{competitors.map((c, idx) => (
|
||||
<FadeInView key={c.id} delay={0.9 + idx * 0.1}>
|
||||
<div className="bg-white/[0.04] border border-white/5 rounded-xl p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="font-medium text-white/70">{c.name}</h4>
|
||||
<span className="text-xs text-white/30">{c.customers_count.toLocaleString()} {lang === 'de' ? 'Kunden' : 'customers'}</span>
|
||||
</div>
|
||||
<p className="text-xs text-white/40 mb-2">{c.pricing_range}</p>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{(c.weaknesses || []).slice(0, 2).map((w, widx) => (
|
||||
<span key={widx} className="text-xs px-2 py-0.5 rounded-full bg-red-500/10 text-red-400">
|
||||
{w}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</FadeInView>
|
||||
{/* Tab Bar */}
|
||||
<FadeInView delay={0.15} className="flex justify-center gap-2 mb-4">
|
||||
{([
|
||||
{ key: 'overview' as ViewTab, de: 'Ueberblick & Vergleich', en: 'Overview & Comparison' },
|
||||
{ key: 'features' as ViewTab, de: 'Feature-Matrix (Detail)', en: 'Feature Matrix (Detail)' },
|
||||
]).map(tab => (
|
||||
<button
|
||||
key={tab.key}
|
||||
onClick={() => setActiveTab(tab.key)}
|
||||
className={`px-4 py-1.5 rounded-full text-xs font-medium transition-all ${
|
||||
activeTab === tab.key
|
||||
? 'bg-indigo-500/20 text-indigo-300 border border-indigo-500/30'
|
||||
: 'bg-white/[0.04] text-white/40 border border-white/5 hover:bg-white/[0.08]'
|
||||
}`}
|
||||
>
|
||||
{lang === 'de' ? tab.de : tab.en}
|
||||
</button>
|
||||
))}
|
||||
</FadeInView>
|
||||
|
||||
{/* ─── Tab: Overview ─── */}
|
||||
{activeTab === 'overview' && (
|
||||
<FadeInView delay={0.2}>
|
||||
{/* Competitor Profiles */}
|
||||
<div className="mb-4">
|
||||
{/* International */}
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Globe className="w-3.5 h-3.5 text-blue-400" />
|
||||
<span className="text-xs font-semibold text-blue-400">{lang === 'de' ? 'International' : 'International'}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2 mb-3">
|
||||
{EXTENDED_COMPETITORS.filter(c => c.isInternational).map(c => (
|
||||
<CompetitorCard key={c.name} competitor={c} lang={lang} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* DACH */}
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Building2 className="w-3.5 h-3.5 text-emerald-400" />
|
||||
<span className="text-xs font-semibold text-emerald-400">DACH</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2 mb-3">
|
||||
{EXTENDED_COMPETITORS.filter(c => !c.isInternational).map(c => (
|
||||
<CompetitorCard key={c.name} competitor={c} lang={lang} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Efficiency Ratios */}
|
||||
<GlassCard className="!p-3 mb-4" hover={false}>
|
||||
<h4 className="text-xs font-semibold text-white/60 mb-2 flex items-center gap-1.5">
|
||||
<TrendingUp className="w-3.5 h-3.5" />
|
||||
{lang === 'de' ? 'Effizienz-Kennzahlen' : 'Efficiency Ratios'}
|
||||
</h4>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-[11px]">
|
||||
<thead>
|
||||
<tr className="border-b border-white/10">
|
||||
<th className="text-left py-1.5 px-2 text-white/40 font-medium">{lang === 'de' ? 'Kennzahl' : 'Metric'}</th>
|
||||
{EXTENDED_COMPETITORS.map(c => (
|
||||
<th key={c.name} className="py-1.5 px-2 text-white/50 font-medium text-center">{c.flag} {c.name}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="border-b border-white/5">
|
||||
<td className="py-1.5 px-2 text-white/50">{lang === 'de' ? 'Umsatz / Mitarbeiter' : 'Revenue / Employee'}</td>
|
||||
{EXTENDED_COMPETITORS.map(c => (
|
||||
<td key={c.name} className="py-1.5 px-2 text-center text-white/70">
|
||||
${ratio(c.revenueNum, c.employees)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
<tr className="border-b border-white/5">
|
||||
<td className="py-1.5 px-2 text-white/50">{lang === 'de' ? 'Kunden / Mitarbeiter' : 'Customers / Employee'}</td>
|
||||
{EXTENDED_COMPETITORS.map(c => (
|
||||
<td key={c.name} className="py-1.5 px-2 text-center text-white/70">
|
||||
{(c.customers / c.employees).toFixed(0)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-1.5 px-2 text-white/50">{lang === 'de' ? 'Mitarbeiter' : 'Employees'}</td>
|
||||
{EXTENDED_COMPETITORS.map(c => (
|
||||
<td key={c.name} className="py-1.5 px-2 text-center text-white/70">
|
||||
{c.employees.toLocaleString()}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
{/* DACH Landscape Note */}
|
||||
<div className="text-[11px] text-white/30 text-center italic">
|
||||
{DACH_NOTE[lang]}
|
||||
</div>
|
||||
</FadeInView>
|
||||
)}
|
||||
|
||||
{/* ─── Tab: Feature Matrix ─── */}
|
||||
{activeTab === 'features' && (
|
||||
<FadeInView delay={0.2}>
|
||||
<div className="space-y-2">
|
||||
{/* Top 5 Differences */}
|
||||
<div>
|
||||
<SectionHeader
|
||||
label={lang === 'de' ? 'Top 5 Unterschiede' : 'Top 5 Differences'}
|
||||
count={top5.length}
|
||||
open={openSections.has('top5')}
|
||||
onToggle={() => toggleSection('top5')}
|
||||
accent="text-yellow-400"
|
||||
/>
|
||||
{openSections.has('top5') && (
|
||||
<FeatureTable features={top5} lang={lang} cols={competitorCols} labels={competitorLabels} highlight />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* All Features */}
|
||||
<div>
|
||||
<SectionHeader
|
||||
label={lang === 'de' ? 'Alle Features' : 'All Features'}
|
||||
count={allFeatures.length}
|
||||
open={openSections.has('all')}
|
||||
onToggle={() => toggleSection('all')}
|
||||
/>
|
||||
{openSections.has('all') && (
|
||||
<FeatureTable features={allFeatures} lang={lang} cols={competitorCols} labels={competitorLabels} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* USPs */}
|
||||
<div>
|
||||
<SectionHeader
|
||||
label={lang === 'de' ? 'USP — nur ComplAI' : 'USP — ComplAI only'}
|
||||
count={usps.length}
|
||||
open={openSections.has('usp')}
|
||||
onToggle={() => toggleSection('usp')}
|
||||
accent="text-indigo-400"
|
||||
/>
|
||||
{openSections.has('usp') && (
|
||||
<FeatureTable features={usps} lang={lang} cols={competitorCols} labels={competitorLabels} highlight />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Score Summary */}
|
||||
<div className="mt-4 flex items-center justify-center gap-6">
|
||||
{[
|
||||
{ name: 'ComplAI', score: ALL_FEATURES.filter(f => f.bp === true).length, color: 'text-indigo-400' },
|
||||
{ name: 'Vanta', score: ALL_FEATURES.filter(f => f.vanta === true).length, color: 'text-white/50' },
|
||||
{ name: 'Drata', score: ALL_FEATURES.filter(f => f.drata === true).length, color: 'text-white/50' },
|
||||
{ name: 'Sprinto', score: ALL_FEATURES.filter(f => f.sprinto === true).length, color: 'text-white/50' },
|
||||
{ name: 'Proliance', score: ALL_FEATURES.filter(f => f.proliance === true).length, color: 'text-white/50' },
|
||||
{ name: 'DataGuard', score: ALL_FEATURES.filter(f => f.dataguard === true).length, color: 'text-white/50' },
|
||||
{ name: 'heyData', score: ALL_FEATURES.filter(f => f.heydata === true).length, color: 'text-white/50' },
|
||||
].map(item => (
|
||||
<div key={item.name} className="text-center">
|
||||
<div className={`text-lg font-bold ${item.color}`}>{item.score}/{ALL_FEATURES.length}</div>
|
||||
<div className="text-[10px] text-white/40">{item.name}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FadeInView>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ─── Sub-Components ────────────────────────────────────────────────────────────
|
||||
|
||||
function CompetitorCard({ competitor: c, lang }: { competitor: ExtendedCompetitor; lang: Language }) {
|
||||
return (
|
||||
<div className="bg-white/[0.04] border border-white/5 rounded-xl p-2.5 text-[11px]">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-sm">{c.flag}</span>
|
||||
<span className="font-semibold text-white/80 text-xs">{c.name}</span>
|
||||
</div>
|
||||
<AiBadge level={c.aiUsage} lang={lang} />
|
||||
</div>
|
||||
{/* HQ + Offices */}
|
||||
<div className="text-[10px] text-white/40 mb-1.5 truncate" title={`HQ: ${c.hq}, ${c.hqCountry}` + (c.offices.length > 1 ? ` | Offices: ${c.offices.join(', ')}` : '')}>
|
||||
<span className="text-white/55">{c.hq}, {c.hqCountry}</span>
|
||||
{c.offices.length > 1 && (
|
||||
<span className="ml-1">+ {c.offices.join(', ')}</span>
|
||||
)}
|
||||
</div>
|
||||
{/* KPIs */}
|
||||
<div className="grid grid-cols-2 gap-x-3 gap-y-0.5 text-white/50">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-white/30">{lang === 'de' ? 'Gr.' : 'Est.'}</span>
|
||||
<span className="text-white/70">{c.founded}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Users className="w-2.5 h-2.5 text-white/30" />
|
||||
<span className="text-white/70">{c.employees.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<DollarSign className="w-2.5 h-2.5 text-white/30" />
|
||||
<span className="text-white/70">{c.revenue}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Globe className="w-2.5 h-2.5 text-white/30" />
|
||||
<span className="text-white/70">{c.customers.toLocaleString()} {lang === 'de' ? 'Kd.' : 'cust.'} ({c.customerCountries})</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Funding + Investors */}
|
||||
<div className="mt-1.5 pt-1.5 border-t border-white/5">
|
||||
<div className="text-white/40">
|
||||
<span className="text-white/60 font-medium">{c.fundingTotal}</span>
|
||||
<span className="ml-1 text-[10px]">{c.fundingRound}</span>
|
||||
</div>
|
||||
{c.investors.length > 0 && (
|
||||
<div className="text-[10px] text-white/30 mt-0.5 truncate" title={c.investors.join(', ')}>
|
||||
{c.investors.slice(0, 3).join(', ')}{c.investors.length > 3 ? ' +' + (c.investors.length - 3) : ''}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Market */}
|
||||
<div className="mt-1 text-[10px] text-white/35 truncate" title={c.market[lang]}>
|
||||
{c.market[lang]}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function FeatureTable({
|
||||
features,
|
||||
lang,
|
||||
cols,
|
||||
labels,
|
||||
highlight,
|
||||
}: {
|
||||
features: ComparisonFeature[]
|
||||
lang: Language
|
||||
cols: readonly string[]
|
||||
labels: string[]
|
||||
highlight?: boolean
|
||||
}) {
|
||||
return (
|
||||
<div className="overflow-x-auto mt-1 mb-1">
|
||||
<table className="w-full text-[11px]">
|
||||
<thead>
|
||||
<tr className="border-b border-white/10">
|
||||
<th className="text-left py-1.5 px-2 text-white/40 font-medium min-w-[180px]">Feature</th>
|
||||
{labels.map((l, idx) => (
|
||||
<th key={l} className={`py-1.5 px-1.5 font-medium text-center whitespace-nowrap ${idx === 0 ? 'text-indigo-400' : 'text-white/50'}`}>
|
||||
{idx === 0 ? <BrandName className="text-[11px]" /> : l}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{features.map((f, i) => (
|
||||
<tr key={i} className={`border-b border-white/5 ${highlight && f.isDiff ? 'bg-indigo-500/5' : ''}`}>
|
||||
<td className="py-1.5 px-2 flex items-center gap-1.5">
|
||||
{f.isDiff && <Star className="w-3 h-3 text-yellow-400 shrink-0" />}
|
||||
<span className={f.isDiff ? 'text-white font-medium' : 'text-white/60'}>
|
||||
{lang === 'de' ? f.de : f.en}
|
||||
</span>
|
||||
</td>
|
||||
{cols.map(col => (
|
||||
<td key={col} className="py-1.5 px-1.5 text-center">
|
||||
<StatusIcon value={f[col as keyof ComparisonFeature] as FeatureStatus} />
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ const translations = {
|
||||
},
|
||||
competition: {
|
||||
title: 'Wettbewerb',
|
||||
subtitle: '44 Features vs. ~15-25 bei Wettbewerbern — 9 einzigartige USPs',
|
||||
subtitle: '44 Features, 9 USPs — kein Anbieter kombiniert DSGVO + Code-Security + Self-Hosted KI',
|
||||
feature: 'Feature',
|
||||
selfHosted: 'Self-Hosted',
|
||||
integratedAI: 'Integrierte KI',
|
||||
@@ -357,7 +357,7 @@ const translations = {
|
||||
},
|
||||
competition: {
|
||||
title: 'Competition',
|
||||
subtitle: '44 features vs. ~15-25 competitors — 9 unique USPs',
|
||||
subtitle: '44 features, 9 USPs — no provider combines GDPR + code security + self-hosted AI',
|
||||
feature: 'Feature',
|
||||
selfHosted: 'Self-Hosted',
|
||||
integratedAI: 'Integrated AI',
|
||||
|
||||
Reference in New Issue
Block a user