[split-required] [guardrail-change] Enforce 500 LOC budget across all services
Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook) and split all 44 files exceeding 500 LOC into domain-focused modules: - consent-service (Go): models, handlers, services, database splits - backend-core (Python): security_api, rbac_api, pdf_service, auth splits - admin-core (TypeScript): 5 page.tsx + sidebar extractions - pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits - voice-service (Python): enhanced_task_orchestrator split Result: 0 violations, 36 exempted (pipeline, tests, pure-data files). Go build verified clean. No behavior changes — pure structural splits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,12 +5,17 @@ import { Language, PitchFeature, PitchCompetitor } from '@/lib/types'
|
||||
import { t } from '@/lib/i18n'
|
||||
import {
|
||||
ChevronDown, ChevronRight, Globe, Building2, Users, TrendingUp,
|
||||
DollarSign, Cpu, Star, Check, X, Minus, Shield, Tag,
|
||||
DollarSign, Cpu, Star, Check, X, Minus, Shield,
|
||||
} from 'lucide-react'
|
||||
import GradientText from '../ui/GradientText'
|
||||
import FadeInView from '../ui/FadeInView'
|
||||
import GlassCard from '../ui/GlassCard'
|
||||
import BrandName from '../ui/BrandName'
|
||||
import {
|
||||
type FeatureStatus, type ComparisonFeature,
|
||||
EXTENDED_COMPETITORS, ALL_FEATURES, DACH_NOTE, PRICING_COMPARISON, APPSEC_COMPETITORS, APPSEC_FEATURES,
|
||||
} from './CompetitionSlide.data'
|
||||
import { StatusIcon, AiBadge, ratio, CompetitorCard, FeatureTable, AppSecCard, AppSecFeatureTable } from './CompetitionSlide.parts'
|
||||
|
||||
interface CompetitionSlideProps {
|
||||
lang: Language
|
||||
@@ -18,438 +23,8 @@ interface CompetitionSlideProps {
|
||||
competitors: PitchCompetitor[]
|
||||
}
|
||||
|
||||
// ─── 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 Länder',
|
||||
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+ Länder',
|
||||
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+ Länder',
|
||||
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+ Länder',
|
||||
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
|
||||
group?: string
|
||||
}
|
||||
|
||||
const ALL_FEATURES: ComparisonFeature[] = [
|
||||
// ── Code Security & DevSecOps ──
|
||||
{ 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, group: 'code-security' },
|
||||
{ de: 'SAST (Static Application Security Testing)', en: 'SAST (Static Application Security Testing)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'code-security' },
|
||||
{ de: 'DAST (Dynamic Application Security Testing)', en: 'DAST (Dynamic Application Security Testing)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'code-security' },
|
||||
{ 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: true, isUSP: true, group: 'code-security' },
|
||||
{ de: 'Container-Security Scanning (Trivy)', en: 'Container Security Scanning (Trivy)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'code-security' },
|
||||
{ de: 'Secret Detection (Gitleaks)', en: 'Secret Detection (Gitleaks)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'code-security' },
|
||||
{ de: 'LLM-Auto-Fix (automatische Code-Korrekturen)', en: 'LLM Auto-Fix (Automatic Code Corrections)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'code-security' },
|
||||
{ de: 'Firmware & Embedded-Security', en: 'Firmware & Embedded Security', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'code-security' },
|
||||
|
||||
// ── KI & Daten ──
|
||||
{ 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, group: 'ai-data' },
|
||||
{ de: 'RAG mit 25.000+ Sicherheitskontrollen', en: 'RAG with 25,000+ Security Controls', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'ai-data' },
|
||||
{ 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: true, isUSP: true, group: 'ai-data' },
|
||||
{ de: 'KI-gestützte Analyse', en: 'AI-Powered Analysis', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'ai-data' },
|
||||
|
||||
// ── Regulatorische Frameworks ──
|
||||
{ de: 'DSGVO / GDPR', en: 'GDPR', bp: true, vanta: 'partial', drata: 'partial', sprinto: 'partial', proliance: true, dataguard: true, heydata: true, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
{ de: 'AI Act', en: 'AI Act', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'frameworks' },
|
||||
{ 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: true, isUSP: true, group: 'frameworks' },
|
||||
{ de: 'NIS2-Richtlinie', en: 'NIS2 Directive', bp: true, vanta: false, drata: 'partial', sprinto: false, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
{ de: 'SOC 2', en: 'SOC 2', bp: 'partial', vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
{ de: 'ISO 27001', en: 'ISO 27001', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
{ de: 'HIPAA', en: 'HIPAA', bp: false, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: false, heydata: false, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
{ de: 'TISAX', en: 'TISAX', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
{ de: 'HinSchG (Whistleblower)', en: 'HinSchG (Whistleblower)', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: false, heydata: false, isDiff: false, isUSP: false, group: 'frameworks' },
|
||||
|
||||
// ── Compliance-Dokumentation ──
|
||||
{ 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, group: 'documentation' },
|
||||
{ de: 'TOM-Dokumentation', en: 'TOM Documentation', bp: true, vanta: false, drata: false, sprinto: false, proliance: true, dataguard: true, heydata: 'partial', isDiff: false, isUSP: false, group: 'documentation' },
|
||||
{ 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, group: 'documentation' },
|
||||
{ de: 'Löschkonzept / Löschfristen', en: 'Deletion Concept / Retention', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: 'partial', heydata: false, isDiff: false, isUSP: false, group: 'documentation' },
|
||||
{ de: 'Policy-Generator', en: 'Policy Generator', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: true, dataguard: true, heydata: 'partial', isDiff: false, isUSP: false, group: 'documentation' },
|
||||
{ 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, group: 'documentation' },
|
||||
|
||||
// ── Operative Compliance ──
|
||||
{ de: 'Audit-Management', en: 'Audit Management', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'operations' },
|
||||
{ de: 'Risikobewertung', en: 'Risk Assessment', bp: true, vanta: true, drata: true, sprinto: true, proliance: 'partial', dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'operations' },
|
||||
{ de: 'Incident Response', en: 'Incident Response', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false, group: 'operations' },
|
||||
{ de: 'Consent Management', en: 'Consent Management', bp: true, vanta: false, drata: false, sprinto: false, proliance: 'partial', dataguard: false, heydata: 'partial', isDiff: false, isUSP: false, group: 'operations' },
|
||||
{ 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, group: 'operations' },
|
||||
{ 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, group: 'operations' },
|
||||
{ de: 'Schulungs-Management', en: 'Training Management', bp: true, vanta: 'partial', drata: 'partial', sprinto: 'partial', proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false, group: 'operations' },
|
||||
{ de: 'Whistleblower-Portal', en: 'Whistleblower Portal', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'operations' },
|
||||
|
||||
// ── Technische Plattform ──
|
||||
{ de: 'Continuous Monitoring', en: 'Continuous Monitoring', bp: true, vanta: true, drata: true, sprinto: true, proliance: false, dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'platform' },
|
||||
{ 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, group: 'platform' },
|
||||
{ de: 'API / SDK', en: 'API / SDK', bp: true, vanta: true, drata: true, sprinto: 'partial', proliance: false, dataguard: 'partial', heydata: false, isDiff: false, isUSP: false, group: 'platform' },
|
||||
{ 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, group: 'platform' },
|
||||
{ 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, group: 'platform' },
|
||||
{ de: 'Mehrmandantenfähig', en: 'Multi-Tenancy', bp: true, vanta: true, drata: true, sprinto: true, proliance: 'partial', dataguard: true, heydata: false, isDiff: false, isUSP: false, group: 'platform' },
|
||||
{ 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, group: 'platform' },
|
||||
{ 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, group: 'platform' },
|
||||
{ de: 'IPFS/Blockchain (optional)', en: 'IPFS/Blockchain (optional)', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'platform' },
|
||||
|
||||
// ── Branche & Spezial ──
|
||||
{ de: 'Maschinenbau-Branchenfokus', en: 'Manufacturing Industry Focus', bp: true, vanta: false, drata: false, sprinto: false, proliance: false, dataguard: false, heydata: false, isDiff: true, isUSP: true, group: 'industry' },
|
||||
]
|
||||
|
||||
// ─── 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.',
|
||||
}
|
||||
|
||||
// ─── Pricing Comparison Data ──────────────────────────────────────────────────
|
||||
|
||||
interface PricingTier {
|
||||
name: { de: string; en: string }
|
||||
price: string
|
||||
annual: string
|
||||
notes: { de: string; en: string }
|
||||
}
|
||||
|
||||
interface CompetitorPricing {
|
||||
name: string
|
||||
flag: string
|
||||
model: string
|
||||
publicPricing: boolean
|
||||
tiers: PricingTier[]
|
||||
setupFee: string
|
||||
isBP?: boolean
|
||||
}
|
||||
|
||||
const PRICING_COMPARISON: CompetitorPricing[] = [
|
||||
{
|
||||
name: 'ComplAI',
|
||||
flag: '🇩🇪',
|
||||
model: 'Cloud (BSI DE)',
|
||||
publicPricing: true,
|
||||
tiers: [
|
||||
{ name: { de: 'Starter (<10 MA)', en: 'Starter (<10 emp.)' }, price: '€300/mo', annual: '€3.600/yr', notes: { de: '380+ Regularien, modular', en: '380+ regulations, modular' } },
|
||||
{ name: { de: 'Professional (10-250)', en: 'Professional (10-250)' }, price: '€1.250–3.333/mo', annual: '€15.000–40.000/yr', notes: { de: 'Alle Module, Priority Support', en: 'All modules, priority support' } },
|
||||
{ name: { de: 'Enterprise (250+)', en: 'Enterprise (250+)' }, price: 'ab €4.167/mo', annual: 'ab €50.000/yr', notes: { de: 'Dedicated, Custom, SLA', en: 'Dedicated, custom, SLA' } },
|
||||
],
|
||||
setupFee: '€0',
|
||||
isBP: true,
|
||||
},
|
||||
{
|
||||
name: 'Vanta',
|
||||
flag: '🇺🇸',
|
||||
model: 'SaaS',
|
||||
publicPricing: false,
|
||||
tiers: [
|
||||
{ name: { de: 'Startup', en: 'Startup' }, price: '~$500/mo', annual: '~$6K/yr', notes: { de: '1 Framework, <50 MA', en: '1 framework, <50 employees' } },
|
||||
{ name: { de: 'Business', en: 'Business' }, price: '~$2K/mo', annual: '~$25K/yr', notes: { de: 'Multi-Framework, VRM', en: 'Multi-framework, VRM' } },
|
||||
{ name: { de: 'Enterprise', en: 'Enterprise' }, price: '~$5-7K/mo', annual: '~$60-80K/yr', notes: { de: 'Custom, SSO, RBAC', en: 'Custom, SSO, RBAC' } },
|
||||
],
|
||||
setupFee: '~$5-15K',
|
||||
},
|
||||
{
|
||||
name: 'Drata',
|
||||
flag: '🇺🇸',
|
||||
model: 'SaaS',
|
||||
publicPricing: false,
|
||||
tiers: [
|
||||
{ name: { de: 'Foundation', en: 'Foundation' }, price: '~$500/mo', annual: '~$5-8K/yr', notes: { de: '1 Framework, Basis', en: '1 framework, basic' } },
|
||||
{ name: { de: 'Business', en: 'Business' }, price: '~$1.5K/mo', annual: '~$18-20K/yr', notes: { de: 'Multi-Framework, API', en: 'Multi-framework, API' } },
|
||||
{ name: { de: 'Enterprise', en: 'Enterprise' }, price: '~$4-8K/mo', annual: '~$50-100K/yr', notes: { de: 'SafeBase, Custom', en: 'SafeBase, custom' } },
|
||||
],
|
||||
setupFee: '~$5-10K',
|
||||
},
|
||||
{
|
||||
name: 'Sprinto',
|
||||
flag: '🇮🇳',
|
||||
model: 'SaaS',
|
||||
publicPricing: false,
|
||||
tiers: [
|
||||
{ name: { de: 'Growth', en: 'Growth' }, price: '~$350/mo', annual: '~$4K/yr', notes: { de: '1 Framework, KMU', en: '1 framework, SMB' } },
|
||||
{ name: { de: 'Business', en: 'Business' }, price: '~$1K/mo', annual: '~$12K/yr', notes: { de: 'Multi-Framework', en: 'Multi-framework' } },
|
||||
{ name: { de: 'Enterprise', en: 'Enterprise' }, price: '~$2K+/mo', annual: '~$25K+/yr', notes: { de: 'Custom Integrations', en: 'Custom integrations' } },
|
||||
],
|
||||
setupFee: '~$2-5K',
|
||||
},
|
||||
{
|
||||
name: 'Proliance',
|
||||
flag: '🇩🇪',
|
||||
model: 'SaaS',
|
||||
publicPricing: true,
|
||||
tiers: [
|
||||
{ name: { de: 'Basis', en: 'Basic' }, price: '€99/mo', annual: '€1.188/yr', notes: { de: 'DSGVO-Grundlagen', en: 'GDPR basics' } },
|
||||
{ name: { de: 'Professional', en: 'Professional' }, price: '€249/mo', annual: '€2.988/yr', notes: { de: '+ Audit, VVT', en: '+ Audit, records' } },
|
||||
{ name: { de: 'Enterprise', en: 'Enterprise' }, price: '€499/mo', annual: '€5.988/yr', notes: { de: 'Multi-Standort, DSB', en: 'Multi-location, DPO' } },
|
||||
],
|
||||
setupFee: '€0',
|
||||
},
|
||||
{
|
||||
name: 'DataGuard',
|
||||
flag: '🇩🇪',
|
||||
model: 'SaaS + Beratung',
|
||||
publicPricing: false,
|
||||
tiers: [
|
||||
{ name: { de: 'Starter', en: 'Starter' }, price: '~€250/mo', annual: '~€3K/yr', notes: { de: 'Nur Software', en: 'Software only' } },
|
||||
{ name: { de: 'Managed', en: 'Managed' }, price: '~€1K/mo', annual: '~€12K/yr', notes: { de: '+ Ext. DSB', en: '+ Ext. DPO' } },
|
||||
{ name: { de: 'Enterprise', en: 'Enterprise' }, price: '~€2K+/mo', annual: '~€24K+/yr', notes: { de: 'ISO 27001 + TISAX', en: 'ISO 27001 + TISAX' } },
|
||||
],
|
||||
setupFee: '~€2-5K',
|
||||
},
|
||||
{
|
||||
name: 'heyData',
|
||||
flag: '🇩🇪',
|
||||
model: 'SaaS',
|
||||
publicPricing: true,
|
||||
tiers: [
|
||||
{ name: { de: 'Essential', en: 'Essential' }, price: '€83/mo', annual: '€996/yr', notes: { de: '1-19 MA, DSGVO', en: '1-19 empl., GDPR' } },
|
||||
{ name: { de: 'Pro', en: 'Pro' }, price: '€199/mo', annual: '€2.388/yr', notes: { de: '20-99 MA, DSB', en: '20-99 empl., DPO' } },
|
||||
{ name: { de: 'Premium', en: 'Premium' }, price: '€333/mo', annual: '€3.996/yr', notes: { de: '100+ MA, Audit', en: '100+ empl., audit' } },
|
||||
],
|
||||
setupFee: '€0',
|
||||
},
|
||||
]
|
||||
|
||||
// ─── AppSec / Pentesting Competitor Data ─────────────────────────────────────
|
||||
|
||||
interface AppSecCompetitor {
|
||||
name: string
|
||||
flag: string
|
||||
hq: string
|
||||
founded: number
|
||||
employees: number
|
||||
revenue: string
|
||||
revenueNum: number
|
||||
customers: string
|
||||
funding: string
|
||||
pricing: string
|
||||
focus: { de: string; en: string }
|
||||
}
|
||||
|
||||
const APPSEC_COMPETITORS: AppSecCompetitor[] = [
|
||||
{ name: 'Snyk', flag: '🇺🇸', hq: 'Boston', founded: 2015, employees: 1200, revenue: '~$300M ARR', revenueNum: 300_000_000, customers: '3.000+', funding: '$850M (Series G, $7.4B)', pricing: '$25K–100K+/yr', focus: { de: 'SCA + SAST, Developer-First', en: 'SCA + SAST, developer-first' } },
|
||||
{ name: 'Veracode', flag: '🇺🇸', hq: 'Burlington, MA', founded: 2006, employees: 1300, revenue: '~$300M', revenueNum: 300_000_000, customers: '3.500+', funding: 'PE (Thoma Bravo, $2.5B)', pricing: '$50K–500K+/yr', focus: { de: 'SAST + DAST + SCA, Enterprise', en: 'SAST + DAST + SCA, enterprise' } },
|
||||
{ name: 'Checkmarx', flag: '🇮🇱', hq: 'Tel Aviv', founded: 2006, employees: 1000, revenue: '~$250M', revenueNum: 250_000_000, customers: '1.800+', funding: 'PE (Hellman & Friedman)', pricing: '$40K–300K+/yr', focus: { de: 'SAST + DAST + SCA + API', en: 'SAST + DAST + SCA + API' } },
|
||||
{ name: 'SonarSource', flag: '🇨🇭', hq: 'Genf', founded: 2008, employees: 500, revenue: '~$250M', revenueNum: 250_000_000, customers: '400K+ Devs', funding: '$412M (Series D)', pricing: '$15K–150K+/yr', focus: { de: 'Code-Qualitaet + SAST', en: 'Code quality + SAST' } },
|
||||
{ name: 'Semgrep', flag: '🇺🇸', hq: 'San Francisco', founded: 2020, employees: 150, revenue: '~$30M ARR', revenueNum: 30_000_000, customers: '1.500+', funding: '$100M (Series C)', pricing: '$10K–100K+/yr', focus: { de: 'Open-Source SAST, Supply Chain', en: 'Open-source SAST, supply chain' } },
|
||||
{ name: 'Pentera', flag: '🇮🇱', hq: 'Tel Aviv', founded: 2015, employees: 400, revenue: '~$100M', revenueNum: 100_000_000, customers: '900+', funding: '$189M (Series C)', pricing: '$50K–250K+/yr', focus: { de: 'Automatisiertes Pentesting/BAS', en: 'Automated pentesting/BAS' } },
|
||||
{ name: 'Invicti', flag: '🇺🇸', hq: 'Austin, TX', founded: 2018, employees: 500, revenue: '~$100M', revenueNum: 100_000_000, customers: '3.000+', funding: 'PE (Turn/River)', pricing: '$15K–100K+/yr', focus: { de: 'DAST (Acunetix + Netsparker)', en: 'DAST (Acunetix + Netsparker)' } },
|
||||
{ name: 'Intruder', flag: '🇬🇧', hq: 'London', founded: 2015, employees: 100, revenue: '~$10M', revenueNum: 10_000_000, customers: '2.500+', funding: '$15M (Series A)', pricing: '$1.5K–20K+/yr', focus: { de: 'Vulnerability Scanner, SMB', en: 'Vulnerability scanner, SMB' } },
|
||||
]
|
||||
|
||||
interface AppSecFeature {
|
||||
de: string
|
||||
en: string
|
||||
bp: FeatureStatus
|
||||
snyk: FeatureStatus
|
||||
veracode: FeatureStatus
|
||||
checkmarx: FeatureStatus
|
||||
sonar: FeatureStatus
|
||||
semgrep: FeatureStatus
|
||||
pentera: FeatureStatus
|
||||
invicti: FeatureStatus
|
||||
intruder: FeatureStatus
|
||||
isUSP: boolean
|
||||
}
|
||||
|
||||
const APPSEC_FEATURES: AppSecFeature[] = [
|
||||
// Pure AppSec Features only (Compliance USPs removed — belong on Compliance tabs)
|
||||
{ de: 'SAST (Static Analysis)', en: 'SAST (Static Analysis)', bp: true, snyk: true, veracode: true, checkmarx: true, sonar: true, semgrep: true, pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'DAST (Dynamic Analysis)', en: 'DAST (Dynamic Analysis)', bp: true, snyk: false, veracode: true, checkmarx: true, sonar: false, semgrep: false, pentera: true, invicti: true, intruder: true, isUSP: false },
|
||||
{ de: 'SCA (Software Composition)', en: 'SCA (Software Composition)', bp: true, snyk: true, veracode: true, checkmarx: true, sonar: 'partial', semgrep: 'partial', pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'LLM-basierte Auto-Fixes', en: 'LLM-Based Auto-Fixes', bp: true, snyk: 'partial', veracode: 'partial', checkmarx: 'partial', sonar: 'partial', semgrep: false, pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'SBOM-Generierung', en: 'SBOM Generation', bp: true, snyk: true, veracode: 'partial', checkmarx: 'partial', sonar: false, semgrep: false, pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'Container-Security', en: 'Container Security', bp: true, snyk: true, veracode: true, checkmarx: true, sonar: false, semgrep: 'partial', pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'Secret Detection', en: 'Secret Detection', bp: true, snyk: false, veracode: false, checkmarx: false, sonar: 'partial', semgrep: true, pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'IaC Scanning', en: 'IaC Scanning', bp: true, snyk: true, veracode: false, checkmarx: false, sonar: false, semgrep: true, pentera: false, invicti: false, intruder: false, isUSP: false },
|
||||
{ de: 'CI/CD-Integration', en: 'CI/CD Integration', bp: true, snyk: true, veracode: true, checkmarx: true, sonar: true, semgrep: true, pentera: 'partial', invicti: 'partial', intruder: 'partial', isUSP: false },
|
||||
{ de: 'API-Security Testing', en: 'API Security Testing', bp: true, snyk: false, veracode: 'partial', checkmarx: true, sonar: false, semgrep: false, pentera: 'partial', invicti: true, intruder: 'partial', isUSP: false },
|
||||
{ de: 'Automatisiertes Pentesting', en: 'Automated Pentesting', bp: true, snyk: false, veracode: false, checkmarx: false, sonar: false, semgrep: false, pentera: true, invicti: false, intruder: true, isUSP: false },
|
||||
{ de: 'Self-Hosted / On-Premise', en: 'Self-Hosted / On-Premise', bp: true, snyk: false, veracode: false, checkmarx: 'partial', sonar: true, semgrep: 'partial', pentera: 'partial', invicti: 'partial', intruder: false, isUSP: false },
|
||||
]
|
||||
|
||||
// ─── 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({
|
||||
@@ -844,205 +419,3 @@ export default function CompetitionSlide({ lang, features, competitors }: Compet
|
||||
)
|
||||
}
|
||||
|
||||
// ─── 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>
|
||||
)
|
||||
}
|
||||
|
||||
const GROUP_LABELS: Record<string, { de: string; en: string; color: string }> = {
|
||||
'code-security': { de: 'Code Security & DevSecOps', en: 'Code Security & DevSecOps', color: 'text-red-400' },
|
||||
'ai-data': { de: 'KI & Daten', en: 'AI & Data', color: 'text-purple-400' },
|
||||
'frameworks': { de: 'Regulatorische Frameworks', en: 'Regulatory Frameworks', color: 'text-blue-400' },
|
||||
'documentation': { de: 'Compliance-Dokumentation', en: 'Compliance Documentation', color: 'text-emerald-400' },
|
||||
'operations': { de: 'Operative Compliance', en: 'Operative Compliance', color: 'text-amber-400' },
|
||||
'platform': { de: 'Technische Plattform', en: 'Technical Platform', color: 'text-cyan-400' },
|
||||
'industry': { de: 'Branche & Spezial', en: 'Industry & Specialty', color: 'text-orange-400' },
|
||||
}
|
||||
|
||||
function FeatureTable({
|
||||
features,
|
||||
lang,
|
||||
cols,
|
||||
labels,
|
||||
}: {
|
||||
features: ComparisonFeature[]
|
||||
lang: Language
|
||||
cols: readonly string[]
|
||||
labels: string[]
|
||||
highlight?: boolean
|
||||
}) {
|
||||
// Build rows with group headers
|
||||
const rowElements: React.ReactNode[] = []
|
||||
let lastGroup = ''
|
||||
features.forEach((f, i) => {
|
||||
const grp = f.group || ''
|
||||
if (grp && grp !== lastGroup) {
|
||||
const gl = GROUP_LABELS[grp]
|
||||
if (gl) {
|
||||
rowElements.push(
|
||||
<tr key={`grp-${grp}`} className="bg-white/[0.02]">
|
||||
<td colSpan={cols.length + 1} className={`py-1.5 px-2 text-[10px] font-bold uppercase tracking-wider ${gl.color}`}>
|
||||
{lang === 'de' ? gl.de : gl.en}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
lastGroup = grp
|
||||
}
|
||||
rowElements.push(
|
||||
<tr key={i} className={`border-b border-white/5 ${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>
|
||||
)
|
||||
})
|
||||
|
||||
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>{rowElements}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AppSecCard({ competitor: c, lang }: { competitor: AppSecCompetitor; lang: Language }) {
|
||||
return (
|
||||
<div className="bg-white/[0.04] border border-white/5 rounded-xl p-2 text-[11px]">
|
||||
<div className="flex items-center gap-1.5 mb-1">
|
||||
<span className="text-sm">{c.flag}</span>
|
||||
<span className="font-semibold text-white/80 text-xs">{c.name}</span>
|
||||
</div>
|
||||
<div className="text-[10px] text-white/40 mb-1 truncate">{c.hq} · {c.founded}</div>
|
||||
<div className="grid grid-cols-2 gap-x-2 gap-y-0.5 text-white/50">
|
||||
<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 truncate">{c.revenue}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 pt-1 border-t border-white/5 text-[10px]">
|
||||
<div className="text-white/40 truncate">{c.funding}</div>
|
||||
<div className="text-white/50 mt-0.5">{c.pricing}</div>
|
||||
</div>
|
||||
<div className="mt-1 text-[10px] text-white/35 truncate" title={c.focus[lang]}>
|
||||
{c.focus[lang]}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AppSecFeatureTable({ features, lang, highlight }: { features: AppSecFeature[]; lang: Language; highlight?: boolean }) {
|
||||
const cols = ['bp', 'snyk', 'veracode', 'checkmarx', 'sonar', 'semgrep', 'pentera', 'invicti', 'intruder'] as const
|
||||
const labels = ['ComplAI', 'Snyk', 'Veracode', 'Checkmarx', 'Sonar', 'Semgrep', 'Pentera', 'Invicti', 'Intruder']
|
||||
|
||||
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-[160px]">Feature</th>
|
||||
{labels.map((l, idx) => (
|
||||
<th key={l} className={`py-1.5 px-1 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.isUSP ? 'bg-indigo-500/5' : ''}`}>
|
||||
<td className="py-1.5 px-2 flex items-center gap-1.5">
|
||||
{f.isUSP && highlight && <Star className="w-3 h-3 text-yellow-400 shrink-0" />}
|
||||
<span className={f.isUSP && highlight ? '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 text-center">
|
||||
<StatusIcon value={f[col] as FeatureStatus} />
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user