diff --git a/pitch-deck/app/pitch-print/[versionId]/_components/PrintAnnexSlides.tsx b/pitch-deck/app/pitch-print/[versionId]/_components/PrintAnnexSlides.tsx index 7fc6fa9..dfe695a 100644 --- a/pitch-deck/app/pitch-print/[versionId]/_components/PrintAnnexSlides.tsx +++ b/pitch-deck/app/pitch-print/[versionId]/_components/PrintAnnexSlides.tsx @@ -137,23 +137,43 @@ const PILLARS_EN = [ export function PrintRegulatoryPage({ lang, pageNum, totalPages, versionName }: SlideBase) { const de = lang === 'de' const pillars = de ? PILLARS_DE : PILLARS_EN + const MONO = "'JetBrains Mono', ui-monospace, monospace" + // Alternate tints — pillars 1 & 3 violet, 2 & 4 amber for visual rhythm. + const tints = [ + { dark: COLORS.violet700, mid: COLORS.violet600, light: COLORS.violet50, border: COLORS.violet300 }, + { dark: COLORS.amber700, mid: COLORS.amber600, light: COLORS.amber50, border: '#f3d59a' }, + { dark: COLORS.violet700, mid: COLORS.violet600, light: COLORS.violet50, border: COLORS.violet300 }, + { dark: COLORS.amber700, mid: COLORS.amber600, light: COLORS.amber50, border: '#f3d59a' }, + ] return ( -
- {pillars.map((p, i) => ( -
-
- {de ? `Säule ${String(i + 1).padStart(2, '0')}` : `Pillar ${String(i + 1).padStart(2, '0')}`} - {de ? '4-6 Regelwerke' : '4-6 regulations'} -
-
{p.t}
-
{p.d}
-
- {p.laws.join(' · ')} -
-
- ))} + {/* Architectural row: 4 pillars side-by-side */} +
+
+ {pillars.map((p, i) => { + const c = tints[i] + return ( +
+ {/* CAPITAL (top) */} +
+
{de ? 'SÄULE' : 'PILLAR'}
+
{String(i + 1).padStart(2, '0')}
+
+ {/* SHAFT (middle) */} +
+
{p.t}
+
{p.d}
+
+ {/* BASE (bottom) */} +
{p.laws.join(' · ')}
+
+ ) + })} +
+ {/* Shared ground line — architectural reference */} +
+
diff --git a/pitch-deck/app/pitch-print/[versionId]/_components/PrintCompetitionSlides.tsx b/pitch-deck/app/pitch-print/[versionId]/_components/PrintCompetitionSlides.tsx index b153ead..9613edd 100644 --- a/pitch-deck/app/pitch-print/[versionId]/_components/PrintCompetitionSlides.tsx +++ b/pitch-deck/app/pitch-print/[versionId]/_components/PrintCompetitionSlides.tsx @@ -120,7 +120,7 @@ export function PrintCompetitionPage2({ lang, pageNum, totalPages, versionName } const de = lang === 'de' return ( - + {/* Competitor profile table */} diff --git a/pitch-deck/app/pitch-print/[versionId]/_components/PrintDiagrams.tsx b/pitch-deck/app/pitch-print/[versionId]/_components/PrintDiagrams.tsx index c27628b..3ee706f 100644 --- a/pitch-deck/app/pitch-print/[versionId]/_components/PrintDiagrams.tsx +++ b/pitch-deck/app/pitch-print/[versionId]/_components/PrintDiagrams.tsx @@ -159,10 +159,40 @@ export function ArchitectureDiagram({ const productLayerBg = `linear-gradient(135deg, ${COLORS.violet50} 0%, #ffffff 60%, ${COLORS.violet50} 100%)` const proxyLayerBg = `linear-gradient(135deg, ${COLORS.amber50} 0%, #fffaf0 60%, ${COLORS.amber50} 100%)` + /** + * Faked-3D layer wrapper: shadow on the bottom edge (heavier than top), a 1px + * top highlight, a 1px darker bottom seam, and a stagger indent on the right + * to suggest the stack tilts slightly away from the viewer. This renders + * crisply in Chromium's print-to-PDF, unlike `transform: rotateX(...)` which + * has print-pipeline quirks. + */ + const layerWrap = (indentRight: string, shadowTint: string, glowTop: string, seamBottom: string): React.CSSProperties => ({ + marginRight: indentRight, + boxShadow: `inset 0 1px 0 ${glowTop}, inset 0 -1px 0 ${seamBottom}, 0 5mm 7mm -4mm ${shadowTint}`, + WebkitPrintColorAdjust: 'exact', + printColorAdjust: 'exact', + }) + + /** Connector that reads as "the upper plane resting on the next" — a soft chevron with a shadow. */ + const PlaneConnector = ({ color }: { color: string }) => ( +
+ + + + +
+ ) + return ( -
- {/* APPLICATION (PRODUCT) LAYER */} -
+
+ {/* APPLICATION (PRODUCT) LAYER — top plane, smallest indent footprint */} +
{product.map((p, i) => ( @@ -171,20 +201,16 @@ export function ArchitectureDiagram({
- {/* compact connector strip */} -
- {[0, 1, 2].map(i => ( -
- - - - -
- ))} -
+ - {/* GATEWAY LAYER — compact: title row + features in 1 row */} -
+ {/* GATEWAY LAYER — middle plane, slight indent, slightly heavier shadow */} +
{proxy.title} @@ -197,20 +223,16 @@ export function ArchitectureDiagram({
- {/* compact connector strip */} -
- {[0, 1, 2].map(i => ( -
- - - - -
- ))} -
+ - {/* INFRASTRUCTURE (INFERENCE) LAYER */} -
+ {/* INFRASTRUCTURE (INFERENCE) LAYER — foundation, deepest indent + shadow */} +
{inference.map((p, i) => ( diff --git a/pitch-deck/app/pitch-print/[versionId]/_components/PrintMarketSlides.tsx b/pitch-deck/app/pitch-print/[versionId]/_components/PrintMarketSlides.tsx index 5385cd7..b5541f6 100644 --- a/pitch-deck/app/pitch-print/[versionId]/_components/PrintMarketSlides.tsx +++ b/pitch-deck/app/pitch-print/[versionId]/_components/PrintMarketSlides.tsx @@ -1,6 +1,10 @@ import { Language, PitchMarket, PitchTeamMember, PitchMilestone, PitchFunding } from '@/lib/types' -import { Page, Callout, COLORS, DataTable, StatLine } from './PrintLayout' -import { MarketFunnel, ComparisonBars, DonutChart } from './PrintCharts' +import { Page, Callout, COLORS, StatLine } from './PrintLayout' +import { ComparisonBars, DonutChart } from './PrintCharts' +import { + Briefcase, RefreshCw, Handshake, Scale, Lightbulb, + Code, TrendingUp, CreditCard, ShieldCheck, Cpu, +} from 'lucide-react' interface SlideBase { lang: Language; pageNum: number; totalPages: number; versionName: string } @@ -19,42 +23,72 @@ export function PrintMarketPage({ market, lang, pageNum, totalPages, versionName const sam = market.find(m => m.market_segment === 'SAM') const som = market.find(m => m.market_segment === 'SOM') - return ( - $1,1 Mrd. ARR. Kein Anbieter bedient den Maschinenbau spezifisch.' : 'Validated market: top-10 compliance vendors generate >$1.1B ARR. No vendor specifically serves manufacturing.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName} footnote={de ? 'Sacra · Bitkom Cloud Monitor 2024 · DIHK 2024 · VDMA · Statista' : 'Sacra · Bitkom Cloud Monitor 2024 · DIHK 2024 · VDMA · Statista'}> + // Fallbacks if data missing + const tamValue = tam?.value_eur ?? 340_000_000_000 + const samValue = sam?.value_eur ?? 48_000_000_000 + const somValue = som?.value_eur ?? 2_100_000_000 + const cards = [ + { key: 'TAM', value: fmtEur(tamValue, de), growth: tam?.growth_rate_pct ?? 14, accent: 'violet' as const, + desc: de ? 'Globaler Compliance- und GRC-Markt, alle Branchen, alle Größen.' : 'Global compliance and GRC market, all industries, all sizes.' }, + { key: 'SAM', value: fmtEur(samValue, de), growth: sam?.growth_rate_pct ?? 18, accent: 'violet-soft' as const, + desc: de ? 'DACH + EU: regulierte Branchen, KMU und Enterprise.' : 'DACH + EU: regulated industries, SMB and enterprise.' }, + { key: 'SOM', value: fmtEur(somValue, de), growth: som?.growth_rate_pct ?? 25, accent: 'amber' as const, core: true, + desc: de ? 'Anlagen- und Maschinenbau DACH, unser Kernsegment.' : 'Machine and plant manufacturing DACH, our core segment.' }, + ] -
-
+ // SVG nested-circles geometry — viewBox unitless, render up to ~130mm wide + const CX = 65, CY = 65, R_TAM = 60, R_SAM = 36, R_SOM = 14 + + return ( + $1,1 Mrd. ARR. Kein Anbieter bedient den Maschinenbau spezifisch.' : 'Validated market: top-10 compliance vendors generate >$1.1B ARR. No vendor specifically serves manufacturing.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName} footnote="Sacra · Bitkom Cloud Monitor 2024 · DIHK 2024 · VDMA · Statista"> + +
+ {/* LEFT: nested-circles diagram */} +
{de ? 'Marktdimensionierung' : 'Market sizing'}
- {tam && sam && som && ( - fmtEur(v, de)} - /> - )} +
+ + + + + TAM + {fmtEur(tamValue, de)} + SAM + {fmtEur(samValue, de)} + SOM + {fmtEur(somValue, de)} + +
+
+ {de ? 'Verschachtelte Marktanteile (TAM ⊃ SAM ⊃ SOM)' : 'Nested market shares (TAM ⊃ SAM ⊃ SOM)'} +
-
-
{de ? 'Kernsegment: Maschinen- und Anlagenbau DACH' : 'Core segment: Machine & plant manufacturing DACH'}
- $1,1 Mrd.'], - ]} - dense - highlightFirstCol - /> + {/* RIGHT: stacked info cards + callout */} +
+ {cards.map((c) => { + const isAmber = c.accent === 'amber' + const isSoft = c.accent === 'violet-soft' + const stroke = isAmber ? COLORS.amber600 : isSoft ? COLORS.violet500 : COLORS.violet700 + const bg = isAmber ? COLORS.amber50 : isSoft ? COLORS.violet50 : 'transparent' + const kickerColor = isAmber ? COLORS.amber700 : COLORS.violet700 + const valueColor = isAmber ? COLORS.amber700 : COLORS.slate900 + return ( +
+
+ {c.key} + {c.core && ({de ? '← unser Kernmarkt' : '← our core market'})} +
+
+
{c.value}
+
+{c.growth}% CAGR
+
+
{c.desc}
+
+ ) + })} -
+
{de ? 'Höchste Regulierungsdichte (DSGVO + AI Act + CRA + Maschinen-VO + ProdSG + LkSG) bei gleichzeitig kleinem Compliance-Team. Klare Schmerzpunkte. Bekannte Vertriebskanäle (VDMA, IHK, Messen).' @@ -152,11 +186,43 @@ export function PrintMilestonesPage({ milestones, lang, pageNum, totalPages, ver /* ===== TEAM ===== */ +// Tuple shape: [LucideIcon, de_label, en_label] +type TeamBullet = [typeof Briefcase, string, string] + +const TEAM_INFO: Array<{ tagline: [string, string]; bullets: TeamBullet[] }> = [ + { + tagline: [ + 'Diplom-Ökonom mit 20+ Jahren Industrie- und Digitalisierungs-Erfahrung.', + 'Business economist with 20+ years in industry and digital transformation.', + ], + bullets: [ + [Briefcase, '20+ Jahre Industrie, Strategie & Digitalisierung', '20+ yrs industry, strategy & digital transformation'], + [RefreshCw, 'Aufbau IoT-, Blockchain- & KI-Plattformen', 'Built IoT, blockchain & AI platforms'], + [Handshake, 'M&A: 4 Übernahmen & Beteiligungen geführt', 'M&A: led 4 acquisitions & investments'], + [Scale, 'Regulatorik: DSGVO, MiCAR, CRA, Data Act', 'Regulatory: GDPR, MiCAR, CRA, Data Act'], + [Lightbulb, '12 erteilte Patente (Erfinder/Miterfinder)', '12 granted patents (inventor / co-inventor)'], + ], + }, + { + tagline: [ + 'Engineering Leader mit 15+ Jahren in Fintech, Web3 und Enterprise-KI.', + 'Engineering leader with 15+ years across fintech, Web3 and enterprise AI.', + ], + bullets: [ + [Code, '15+ Jahre Engineering Leadership — Fintech, Web3, KI', '15+ yrs engineering leadership — fintech, Web3, AI'], + [TrendingUp, 'Engineering-Org skaliert: 6 → 60 in 18 Monaten', 'Scaled engineering org: 6 → 60 in 18 months'], + [CreditCard, 'ETOPay SaaS-Payment-Infrastruktur entwickelt', 'Built ETOPay SaaS payment infrastructure'], + [ShieldCheck, 'MiCA-Compliance-Strategie ViviSwap', 'MiCA compliance strategy for ViviSwap'], + [Cpu, 'Embedded Rust (Cortex-M) + Full-Stack TypeScript', 'Embedded Rust (Cortex-M) + full-stack TypeScript'], + ], + }, +] + export function PrintTeamPage({ team, lang, pageNum, totalPages, versionName }: SlideBase & { team: PitchTeamMember[] }) { const de = lang === 'de' const members = team && team.length ? team : [ - { id: 1, name: 'Benjamin Bönisch', role_de: 'CEO & Co-Founder', role_en: 'CEO & Co-Founder', bio_de: 'Mehrfacher Gründer mit Fokus auf B2B-SaaS und Vertrieb. Ehemals Geschäftsführer und Vertriebsleiter. Tiefe Verankerung im Maschinenbau-Netzwerk DACH.', bio_en: 'Serial founder focused on B2B SaaS and sales. Former CEO and VP Sales. Deep network in DACH manufacturing.', equity_pct: 37.3, expertise: ['B2B Sales', 'Go-to-Market', 'Manufacturing', 'Operations'], linkedin_url: '', photo_url: '' }, - { id: 2, name: 'Sharang Parnerkar', role_de: 'CTO & Co-Founder', role_en: 'CTO & Co-Founder', bio_de: 'Ex-Anthropic, Ex-Google. Distributed systems, KI-Infrastruktur, RAG-Pipelines. Open-Source-Contributor. Hat die gesamte Plattform-Architektur entworfen und 500K+ LoC implementiert.', bio_en: 'Ex-Anthropic, ex-Google. Distributed systems, AI infrastructure, RAG pipelines. Open-source contributor. Designed the entire platform architecture and implemented 500K+ LoC.', equity_pct: 37.3, expertise: ['AI Infrastructure', 'Distributed Systems', 'RAG', 'Go/Python/TypeScript'], linkedin_url: '', photo_url: '' }, + { id: 1, name: 'Benjamin Bönisch', role_de: 'CEO & Co-Founder', role_en: 'CEO & Co-Founder', bio_de: '', bio_en: '', equity_pct: 37.3, expertise: ['B2B Sales', 'Go-to-Market', 'Manufacturing', 'Operations'], linkedin_url: '', photo_url: '' }, + { id: 2, name: 'Sharang Parnerkar', role_de: 'CTO & Co-Founder', role_en: 'CTO & Co-Founder', bio_de: '', bio_en: '', equity_pct: 37.3, expertise: ['AI Infrastructure', 'Distributed Systems', 'RAG', 'Go/Python/TypeScript'], linkedin_url: '', photo_url: '' }, ] return ( @@ -166,6 +232,10 @@ export function PrintTeamPage({ team, lang, pageNum, totalPages, versionName }: {[0, 1].map(idx => { const m = members[idx] if (!m) return null + const info = TEAM_INFO[idx] + // Icon tile palette: violet for first founder, amber for second + const tileBg = idx === 0 ? COLORS.violet50 : COLORS.amber50 + const tileColor = idx === 0 ? COLORS.violet700 : COLORS.amber700 return (
@@ -190,7 +260,20 @@ export function PrintTeamPage({ team, lang, pageNum, totalPages, versionName }:
{de ? m.role_de : m.role_en}
-
{de ? m.bio_de : m.bio_en}
+ {info && ( +
{info.tagline[de ? 0 : 1]}
+ )} + {/* Bulleted skill list with icons */} +
+ {info?.bullets.map(([IconComp, deLabel, enLabel], bi) => ( +
+
+ +
+
{de ? deLabel : enLabel}
+
+ ))} +
{de ? 'Expertise' : 'Expertise'}
diff --git a/pitch-deck/app/pitch-print/[versionId]/_components/PrintNewSlides.tsx b/pitch-deck/app/pitch-print/[versionId]/_components/PrintNewSlides.tsx index ac5a3d7..7a08dca 100644 --- a/pitch-deck/app/pitch-print/[versionId]/_components/PrintNewSlides.tsx +++ b/pitch-deck/app/pitch-print/[versionId]/_components/PrintNewSlides.tsx @@ -113,22 +113,58 @@ function fmtEur(n: number): string { return `${sign}€${Math.round(abs)}` } +/* Small inline SVG sparkline. Renders an empty-state em-dash if all values are zero. */ +function Sparkline({ values, width = 56, height = 22, stroke = COLORS.violet600 }: { values: number[]; width?: number; height?: number; stroke?: string }) { + const allZero = values.every(v => v === 0) + if (allZero || values.length < 2) { + return + } + const min = Math.min(...values) + const max = Math.max(...values) + const range = max - min || 1 + const stepX = width / (values.length - 1) + const padY = 2 + const innerH = height - padY * 2 + const points = values.map((v, i) => { + const x = i * stepX + const y = padY + innerH - ((v - min) / range) * innerH + return { x, y } + }) + const path = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(' ') + const last = points[points.length - 1] + // Area fill polygon for soft tone under the line + const area = `${path} L${last.x.toFixed(2)},${height} L0,${height} Z` + return ( + + + + + + ) +} + export function PrintKPIHeroPage({ fmResults, lang, pageNum, totalPages, versionName }: SlideBase & { fmResults: FMResult[] }) { const de = lang === 'de' const kpis = computeAnnualKPIs(fmResults) const k26 = kpis.find(k => k.year === 2026) const k30 = kpis.find(k => k.year === 2030) const breakEvenYear = kpis.find(k => k.ebit > 0)?.year + const series = (pick: (k: typeof kpis[number]) => number): number[] => + [2026, 2027, 2028, 2029, 2030].map(y => { + const row = kpis.find(k => k.year === y) + return row ? pick(row) : 0 + }) - const tiles = k26 && k30 ? [ - { label: 'ARR', start: fmtEur(k26.arr), end: fmtEur(k30.arr), endColor: COLORS.violet600 }, - { label: de ? 'Kunden' : 'Customers', start: k26.customers.toLocaleString('de-DE'), end: k30.customers.toLocaleString('de-DE'), endColor: COLORS.violet600 }, - { label: de ? 'ARPU / Mo' : 'ARPU / mo', start: fmtEur(k26.arpu), end: fmtEur(k30.arpu), endColor: COLORS.slate900 }, - { label: de ? 'Mitarbeiter' : 'Employees', start: String(k26.employees), end: String(k30.employees), endColor: COLORS.slate900 }, - { label: de ? 'Bruttomarge' : 'Gross margin', start: `${k26.grossMargin}%`, end: `${k30.grossMargin}%`, endColor: COLORS.emerald700 }, - { label: 'EBIT', start: fmtEur(k26.ebit), end: fmtEur(k30.ebit), endColor: k30.ebit >= 0 ? COLORS.emerald700 : COLORS.red700 }, - { label: de ? 'Netto-Ergebnis' : 'Net income', start: fmtEur(k26.netIncome), end: fmtEur(k30.netIncome), endColor: k30.netIncome >= 0 ? COLORS.emerald700 : COLORS.red700 }, - { label: de ? 'Cash (Dez)' : 'Cash (Dec)', start: fmtEur(k26.cashBalance), end: fmtEur(k30.cashBalance), endColor: COLORS.emerald700 }, + type Tile = { label: string; start: string; end: string; endColor: string; series: number[]; hideStart?: boolean; stroke?: string } + const tiles: Tile[] = k26 && k30 ? [ + { label: 'ARR', start: fmtEur(k26.arr), end: fmtEur(k30.arr), endColor: COLORS.violet600, series: series(k => k.arr), hideStart: k26.arr === 0 }, + { label: de ? 'Kunden' : 'Customers', start: k26.customers.toLocaleString('de-DE'), end: k30.customers.toLocaleString('de-DE'), endColor: COLORS.violet600, series: series(k => k.customers), hideStart: k26.customers === 0 }, + { label: de ? 'ARPU / Mo' : 'ARPU / mo', start: fmtEur(k26.arpu), end: fmtEur(k30.arpu), endColor: COLORS.slate900, series: series(k => k.arpu), hideStart: k26.arpu === 0 }, + { label: de ? 'Mitarbeiter' : 'Employees', start: String(k26.employees), end: String(k30.employees), endColor: COLORS.slate900, series: series(k => k.employees), hideStart: k26.employees === 0 }, + { label: de ? 'Bruttomarge' : 'Gross margin', start: `${k26.grossMargin}%`, end: `${k30.grossMargin}%`, endColor: COLORS.emerald700, series: series(k => k.grossMargin), hideStart: k26.grossMargin === 0, stroke: COLORS.emerald600 }, + { label: 'EBIT', start: fmtEur(k26.ebit), end: fmtEur(k30.ebit), endColor: k30.ebit >= 0 ? COLORS.emerald700 : COLORS.red700, series: series(k => k.ebit), hideStart: k26.ebit === 0, stroke: k30.ebit >= 0 ? COLORS.emerald600 : COLORS.red600 }, + { label: de ? 'Netto-Ergebnis' : 'Net income', start: fmtEur(k26.netIncome), end: fmtEur(k30.netIncome), endColor: k30.netIncome >= 0 ? COLORS.emerald700 : COLORS.red700, series: series(k => k.netIncome), hideStart: k26.netIncome === 0, stroke: k30.netIncome >= 0 ? COLORS.emerald600 : COLORS.red600 }, + { label: de ? 'Cash (Dez)' : 'Cash (Dec)', start: fmtEur(k26.cashBalance), end: fmtEur(k30.cashBalance), endColor: COLORS.emerald700, series: series(k => k.cashBalance), hideStart: k26.cashBalance === 0, stroke: COLORS.emerald600 }, ] : [] return ( @@ -140,13 +176,22 @@ export function PrintKPIHeroPage({ fmResults, lang, pageNum, totalPages, version
{tiles.map((t, i) => (
-
{t.label}
-
- {t.start} - +
{t.label}
+
+ {!t.hideStart && ( + <> + {t.start} + + + )} {t.end}
-
2026 · 2030
+
+ +
+ 20262030 +
+
))}
@@ -163,42 +208,67 @@ export function PrintTechStackPage({ lang, pageNum, totalPages, versionName }: S const de = lang === 'de' const cats = de ? [ - { name: 'Frontend', icon: ScanLine, items: ['Next.js 15', 'React 19', 'Tailwind CSS', 'Framer Motion', 'Dioxus (Rust)'] }, - { name: 'Backend', icon: Wrench, items: ['Go/Gin', 'Python/FastAPI', 'Rust/Axum', 'OpenAPI'] }, - { name: 'Storage', icon: Database, items: ['PostgreSQL 16', 'MongoDB', 'Qdrant Vector DB', 'Valkey (cache)'] }, - { name: 'KI / RAG', icon: Brain, items: ['LiteLLM', 'Qwen3-32B', 'DeepSeek-R1', 'Sentence-Transformers', 'LangGraph'] }, - { name: 'Code-Scanning', icon: Shield, items: ['Semgrep', 'Gitleaks', 'Syft', 'Trivy', 'CycloneDX'] }, - { name: 'Auth & SSO', icon: Lock, items: ['Keycloak', 'OIDC', 'OPA (policies)'] }, - { name: 'Kommunikation', icon: MessageSquare, items: ['Matrix (chat)', 'Jitsi (video)', 'Mailpit'] }, - { name: 'DevOps', icon: ShieldCheck, items: ['Gitea', 'Woodpecker CI', 'HashiCorp Vault', 'Orca', 'Docker Compose'] }, + { name: 'Frontend', icon: ScanLine, blurb: 'User-facing Oberflächen', items: ['Next.js 15', 'React 19', 'Tailwind CSS', 'Framer Motion', 'Dioxus (Rust)'] }, + { name: 'Backend', icon: Wrench, blurb: 'API & Business-Logik', items: ['Go/Gin', 'Python/FastAPI', 'Rust/Axum', 'OpenAPI'] }, + { name: 'Storage', icon: Database, blurb: 'Persistenter Zustand', items: ['PostgreSQL 16', 'MongoDB', 'Qdrant Vector DB', 'Valkey (cache)'] }, + { name: 'KI / RAG', icon: Brain, blurb: 'Inferenz & Retrieval', items: ['LiteLLM', 'Qwen3-32B', 'DeepSeek-R1', 'Sentence-Transformers', 'LangGraph'] }, + { name: 'Code-Scanning', icon: Shield, blurb: 'Schwachstellen-Erkennung', items: ['Semgrep', 'Gitleaks', 'Syft', 'Trivy', 'CycloneDX'] }, + { name: 'Auth & SSO', icon: Lock, blurb: 'Identität & Rechte', items: ['Keycloak', 'OIDC', 'OPA (policies)'] }, + { name: 'Kommunikation', icon: MessageSquare, blurb: 'Echtzeit-Kanäle', items: ['Matrix (chat)', 'Jitsi (video)', 'Mailpit'] }, + { name: 'DevOps', icon: ShieldCheck, blurb: 'Build & Ship', items: ['Gitea', 'Woodpecker CI', 'HashiCorp Vault', 'Orca', 'Docker Compose'] }, ] : [ - { name: 'Frontend', icon: ScanLine, items: ['Next.js 15', 'React 19', 'Tailwind CSS', 'Framer Motion', 'Dioxus (Rust)'] }, - { name: 'Backend', icon: Wrench, items: ['Go/Gin', 'Python/FastAPI', 'Rust/Axum', 'OpenAPI'] }, - { name: 'Storage', icon: Database, items: ['PostgreSQL 16', 'MongoDB', 'Qdrant vector DB', 'Valkey (cache)'] }, - { name: 'AI / RAG', icon: Brain, items: ['LiteLLM', 'Qwen3-32B', 'DeepSeek-R1', 'Sentence-Transformers', 'LangGraph'] }, - { name: 'Code scanning', icon: Shield, items: ['Semgrep', 'Gitleaks', 'Syft', 'Trivy', 'CycloneDX'] }, - { name: 'Auth & SSO', icon: Lock, items: ['Keycloak', 'OIDC', 'OPA (policies)'] }, - { name: 'Communication', icon: MessageSquare, items: ['Matrix (chat)', 'Jitsi (video)', 'Mailpit'] }, - { name: 'DevOps', icon: ShieldCheck, items: ['Gitea', 'Woodpecker CI', 'HashiCorp Vault', 'Orca', 'Docker Compose'] }, + { name: 'Frontend', icon: ScanLine, blurb: 'User-facing surfaces', items: ['Next.js 15', 'React 19', 'Tailwind CSS', 'Framer Motion', 'Dioxus (Rust)'] }, + { name: 'Backend', icon: Wrench, blurb: 'API & business logic', items: ['Go/Gin', 'Python/FastAPI', 'Rust/Axum', 'OpenAPI'] }, + { name: 'Storage', icon: Database, blurb: 'Persistent state', items: ['PostgreSQL 16', 'MongoDB', 'Qdrant vector DB', 'Valkey (cache)'] }, + { name: 'AI / RAG', icon: Brain, blurb: 'Inference & retrieval', items: ['LiteLLM', 'Qwen3-32B', 'DeepSeek-R1', 'Sentence-Transformers', 'LangGraph'] }, + { name: 'Code scanning', icon: Shield, blurb: 'Vulnerability detection', items: ['Semgrep', 'Gitleaks', 'Syft', 'Trivy', 'CycloneDX'] }, + { name: 'Auth & SSO', icon: Lock, blurb: 'Identity & permissions', items: ['Keycloak', 'OIDC', 'OPA (policies)'] }, + { name: 'Communication', icon: MessageSquare, blurb: 'Real-time channels', items: ['Matrix (chat)', 'Jitsi (video)', 'Mailpit'] }, + { name: 'DevOps', icon: ShieldCheck, blurb: 'Build & ship', items: ['Gitea', 'Woodpecker CI', 'HashiCorp Vault', 'Orca', 'Docker Compose'] }, ] return ( - +
{cats.map((c, i) => { const Icon = c.icon + const num = String(i + 1).padStart(2, '0') return ( -
-
-
- -
-
{c.name}
+
+
+
-
+
{num}
+
{c.name}
+
{c.blurb}
+
{c.items.map((it, j) => ( -
0 ? `1px solid ${COLORS.slate100}` : 'none', lineHeight: 1.3 }}>{it}
+ {it} ))}
diff --git a/pitch-deck/app/pitch-print/[versionId]/_components/PrintProductSlides.tsx b/pitch-deck/app/pitch-print/[versionId]/_components/PrintProductSlides.tsx index eb1d094..5d84be9 100644 --- a/pitch-deck/app/pitch-print/[versionId]/_components/PrintProductSlides.tsx +++ b/pitch-deck/app/pitch-print/[versionId]/_components/PrintProductSlides.tsx @@ -1,11 +1,13 @@ import { Language, PitchProduct } from '@/lib/types' -import { Page, Bullets, Callout, COLORS, DataTable } from './PrintLayout' +import { Page, Bullets, Callout, COLORS } from './PrintLayout' import { LoopDiagram } from './PrintDiagrams' import { getDetails } from '@/components/slides/USPSlide.data' import { ScanLine, ShieldCheck, FileText, ClipboardCheck, Users, UserCheck, AlertTriangle, Brain, Target, GraduationCap, TrendingUp, MessageSquare, Shield, Layers, Globe, FileSearch, Sparkles, Repeat, ArrowLeftRight, Infinity, + Lock, Heart, Banknote, ShoppingCart, Wifi, BookOpen, Landmark, Building2, + Factory, Cpu, CheckCircle2, Zap, type LucideIcon, } from 'lucide-react' @@ -105,15 +107,16 @@ export function PrintUSPPage2({ lang, pageNum, totalPages, versionName }: SlideB /* ===== REGULATORY LANDSCAPE ===== */ +const CATEGORY_ICONS: LucideIcon[] = [Lock, Shield, Brain, Globe, ShieldCheck, Banknote, Heart, Users] const RL_CATEGORIES_DE = [ { name: 'Datenschutz', sample: 'DSGVO · ePrivacy · TTDSG · BDSG', count: 32 }, - { name: 'Cybersicherheit', sample: 'NIS2 · IT-SiG · BSIG · KRITIS-Verordnung', count: 47 }, + { name: 'Cybersicherheit', sample: 'NIS2 · IT-SiG · BSIG · KRITIS-VO', count: 47 }, { name: 'KI-Regulierung', sample: 'AI Act · KI-Haftungsrichtlinie', count: 18 }, - { name: 'Digitale Märkte', sample: 'DMA · DSA · Data Act · Data Governance Act', count: 24 }, - { name: 'Produktsicherheit', sample: 'CRA · Maschinenverordnung · Produktsicherheitsgesetz', count: 41 }, + { name: 'Digitale Märkte', sample: 'DMA · DSA · Data Act · DGA', count: 24 }, + { name: 'Produktsicherheit', sample: 'CRA · MaschinenVO · ProdSG', count: 41 }, { name: 'Finanzregulierung', sample: 'DORA · MiCA · FinmadiG · KWG', count: 53 }, - { name: 'Gesundheitsdaten', sample: 'MDR · IVDR · PatDG · Krankenhausgesetz', count: 28 }, - { name: 'Verbraucherschutz', sample: 'UWG · BGB · Geschäftsgeheimnisschutz · HinSchG', count: 36 }, + { name: 'Gesundheitsdaten', sample: 'MDR · IVDR · PatDG · KHG', count: 28 }, + { name: 'Verbraucherschutz', sample: 'UWG · BGB · GeschGehG · HinSchG', count: 36 }, ] const RL_CATEGORIES_EN = [ { name: 'Data Privacy', sample: 'GDPR · ePrivacy · TTDSG · BDSG', count: 32 }, @@ -125,6 +128,7 @@ const RL_CATEGORIES_EN = [ { name: 'Health Data', sample: 'MDR · IVDR · PatDG · Hospital Act', count: 28 }, { name: 'Consumer Prot.', sample: 'UWG · BGB · Trade Secrets · HinSchG', count: 36 }, ] +const INDUSTRY_ICONS: LucideIcon[] = [Building2, Factory, Heart, Banknote, ShoppingCart, Cpu, Wifi, Brain, ShieldCheck, BookOpen, Landmark] const INDUSTRIES_DE = ['Alle Unternehmen', 'Maschinenbau', 'Gesundheit', 'Finanzsektor', 'E-Commerce', 'Technologie', 'IoT / Hardware', 'KI-Anbieter', 'Krit. Infrastruktur', 'Medien', 'Öffentl. Sektor'] const INDUSTRIES_EN = ['All companies', 'Manufacturing', 'Healthcare', 'Finance', 'E-Commerce', 'Technology', 'IoT / Hardware', 'AI Providers', 'Critical Infra.', 'Media', 'Public Sector'] @@ -151,32 +155,46 @@ export function PrintRegulatoryLandscapePage({ lang, pageNum, totalPages, versio ))}
-
- {/* Categories */} +
+ {/* Categories — 2x4 card grid with icons */}
{de ? 'Acht Regulierungs-Kategorien' : 'Eight regulatory categories'}
- [c.name, c.sample, c.count])} - dense - highlightFirstCol - /> +
+ {cats.map((c, i) => { + const CIcon = CATEGORY_ICONS[i] + return ( +
+
+
+
+
{c.name}
+
{c.count}
+
+
{c.sample}
+
+
+ ) + })} +
- {/* Industries */} + {/* Industries — cards with icons */}
{de ? 'Zehn Branchen-Profile' : 'Ten industry profiles'}
- {industries.map((ind, i) => ( -
- {i === 1 && {de ? 'Kernfokus' : 'Core focus'}} - {ind} -
- ))} + {industries.map((ind, i) => { + const IIcon = INDUSTRY_ICONS[i] + const isFocus = i === 1 + return ( +
+
+
+ {isFocus &&
{de ? 'Kernfokus' : 'Core focus'}
} +
{ind}
+
+
+ ) + })}
@@ -194,10 +212,7 @@ export function PrintRegulatoryLandscapePage({ lang, pageNum, totalPages, versio /* ===== PRODUCT / MODULAR TOOLKIT ===== */ -const MODULE_ICONS: LucideIcon[] = [ - ScanLine, ShieldCheck, FileText, ClipboardCheck, Users, UserCheck, - AlertTriangle, Brain, Target, GraduationCap, TrendingUp, MessageSquare, -] +const MODULE_ICONS: LucideIcon[] = [ScanLine, ShieldCheck, FileText, ClipboardCheck, Users, UserCheck, AlertTriangle, Brain, Target, GraduationCap, TrendingUp, MessageSquare] const MODULES_FULL_DE = [ { name: 'Code Security', desc: 'SAST · DAST · SBOM · Container · Secrets · Pentesting', features: ['Bei jedem Push', 'Auto-Fix LLM', 'CI/CD-integriert'] }, { name: 'CE-SW-Risikobeurteilung', desc: 'CE-Kennzeichnung für Maschinen mit Software-Anteil', features: ['Maschinen-VO', 'CRA-konform', 'Code-Basis-Analyse'] }, @@ -294,48 +309,64 @@ export function PrintHowItWorksPage({ lang, pageNum, totalPages, versionName }: return ( - {/* 4-step rail: numbered violet circles on a horizontal connector line, - title + body underneath each. Replaces the floating-arrow StepStrip. */} -
- {/* connector line (behind the circles) */} -
+ {/* 4-step rail: numbered violet circles on a horizontal connector line */} +
+
{steps.map((s, i) => (
- {/* number circle on the rail */} -
{s.n}
-
{s.t}
-
{s.d}
+
{s.n}
+
{s.t}
+
{s.d}
))}
- {/* Fill space between steps and footer with a visual timeline */} -
- -
-
- {de ? 'Time-to-Value · Median 14 Tage · Worst Case 28 Tage' : 'Time-to-Value · Median 14 days · Worst case 28 days'} -
- {/* dotted timeline with 5 day markers */} -
- {/* the rail */} + {/* Day-marker timeline — directly under steps, ~3mm gap */} +
+
{days.map((d, i) => (
- {/* day marker pill */}
{d}
- {/* dot on rail */}
- {/* label below */} -
{labels[i]}
+
{labels[i]}
))}
+ + {/* Time-to-value callout */} +
+ + {de + ? 'Median 14 Tage · Worst Case 28 Tage. Vom Vertrag bis zur Audit-Bereitschaft typischerweise unter 30 Tagen.' + : 'Median 14 days · worst case 28 days. From contract to audit-readiness typically under 30 days.'} + +
+ + {/* What you get on day N — 4-column benefit block, fills bottom third */} +
+
+ {de ? 'Was Sie wann bekommen' : 'What you get, when'} +
+
+ {([ + [Shield, de ? 'Risikoanalysen automatisch ab Tag 3' : 'Risk analyses automatic from day 3'], + [FileText, de ? 'VVT / TOMs / DSFA generiert ab Tag 14' : 'RoPA / TOMs / DPIA generated from day 14'], + [CheckCircle2, de ? 'Audit-Trail vollständig ab Tag 30' : 'Audit trail complete from day 30'], + [Zap, de ? 'Continuous Scanning bei jedem Push' : 'Continuous scanning on every push'], + ] as [LucideIcon, string][]).map(([Icon, t], i) => ( +
+
+
{t}
+
+ ))} +
+
) } @@ -347,42 +378,9 @@ export function PrintBusinessModelPage({ lang, pageNum, totalPages, versionName const MONO = "'JetBrains Mono', ui-monospace, monospace" const tiers = [ - { - name: 'Starter', - target: de ? '< 25 Mitarbeiter · Basis-Module' : '< 25 employees · basic modules', - price: '€3.600', - unit: de ? '/ Jahr' : '/ year', - features: de - ? ['DSGVO + Audit + DSR-Workflow', 'Compliance Scanner (CI/CD)', 'EU-Hosting · BSI C5', 'E-Mail-Support'] - : ['GDPR + Audit + DSR workflow', 'Compliance Scanner (CI/CD)', 'EU hosting · BSI C5', 'Email support'], - tint: COLORS.violet400, - bg: COLORS.violet50, - featured: false, - }, - { - name: 'Professional', - target: de ? '25–250 Mitarbeiter · alle Module' : '25–250 employees · all modules', - price: '€18.000', - unit: de ? '/ Jahr' : '/ year', - features: de - ? ['Alle 12 Module', 'Priority-Support · Onboarding-Call', 'CE-Software-Risiko + Tender Matching', 'Dedicated CSM · 14-tägige Reviews', 'Custom-Integrationen (Jira, GitLab)'] - : ['All 12 modules', 'Priority support · onboarding call', 'CE software risk + tender matching', 'Dedicated CSM · biweekly reviews', 'Custom integrations (Jira, GitLab)'], - tint: COLORS.violet600, - bg: `linear-gradient(180deg, ${COLORS.violet50} 0%, #ffffff 60%, ${COLORS.violet50} 100%)`, - featured: true, - }, - { - name: 'Enterprise', - target: de ? '250+ Mitarbeiter · maßgeschneidert' : '250+ employees · custom', - price: de ? 'ab €50.000' : 'from €50k', - unit: de ? '/ Jahr' : '/ year', - features: de - ? ['Alles aus Professional', 'SLA · Custom Contract', 'On-Premise / Air-Gap (Mac Mini/Studio)', 'Dedicated Customer Engineering', 'Multi-Region Audit-Trail'] - : ['Everything in Professional', 'SLA · custom contract', 'On-premise / air-gap (Mac Mini/Studio)', 'Dedicated customer engineering', 'Multi-region audit trail'], - tint: COLORS.amber600, - bg: COLORS.amber50, - featured: false, - }, + { name: 'Starter', target: de ? '< 25 Mitarbeiter · Basis-Module' : '< 25 employees · basic modules', price: '€3.600', unit: de ? '/ Jahr' : '/ year', features: de ? ['DSGVO + Audit + DSR-Workflow', 'Compliance Scanner (CI/CD)', 'EU-Hosting · BSI C5', 'E-Mail-Support'] : ['GDPR + Audit + DSR workflow', 'Compliance Scanner (CI/CD)', 'EU hosting · BSI C5', 'Email support'], tint: COLORS.violet400, bg: COLORS.violet50, featured: false }, + { name: 'Professional', target: de ? '25–250 Mitarbeiter · alle Module' : '25–250 employees · all modules', price: '€18.000', unit: de ? '/ Jahr' : '/ year', features: de ? ['Alle 12 Module', 'Priority-Support · Onboarding-Call', 'CE-Software-Risiko + Tender Matching', 'Dedicated CSM · 14-tägige Reviews', 'Custom-Integrationen (Jira, GitLab)'] : ['All 12 modules', 'Priority support · onboarding call', 'CE software risk + tender matching', 'Dedicated CSM · biweekly reviews', 'Custom integrations (Jira, GitLab)'], tint: COLORS.violet600, bg: `linear-gradient(180deg, ${COLORS.violet50} 0%, #ffffff 60%, ${COLORS.violet50} 100%)`, featured: true }, + { name: 'Enterprise', target: de ? '250+ Mitarbeiter · maßgeschneidert' : '250+ employees · custom', price: de ? 'ab €50.000' : 'from €50k', unit: de ? '/ Jahr' : '/ year', features: de ? ['Alles aus Professional', 'SLA · Custom Contract', 'On-Premise / Air-Gap (Mac Mini/Studio)', 'Dedicated Customer Engineering', 'Multi-Region Audit-Trail'] : ['Everything in Professional', 'SLA · custom contract', 'On-premise / air-gap (Mac Mini/Studio)', 'Dedicated customer engineering', 'Multi-region audit trail'], tint: COLORS.amber600, bg: COLORS.amber50, featured: false }, ] return ( @@ -391,18 +389,7 @@ export function PrintBusinessModelPage({ lang, pageNum, totalPages, versionName {/* 3 product cards */}
{tiers.map((t) => ( -
+
{/* Featured badge */} {t.featured && (
{de ? 'Beliebt' : 'Popular'}
@@ -449,14 +436,37 @@ export function PrintBusinessModelPage({ lang, pageNum, totalPages, versionName
-
-
{de ? 'Netto-Effekt · KMU 50 MA / Jahr 1' : 'Net effect · SME 50 emp. / Y1'}
-
-
+€30k
-
{de ? 'pro KMU / Jahr' : 'per SME / yr'}
+
+
{de ? 'Netto-Effekt · KMU 50 MA / Jahr 1' : 'Net effect · SME 50 emp. / Y1'}
+
+
+€30k
+
{de ? 'pro KMU / Jahr' : 'per SME / yr'}
-
- {de ? 'Kunde spart €55k (Pentests, CE-Risiko, Compliance-Zeit), zahlt €25k. ROI ab Tag 1.' : 'Customer saves €55k (pentests, CE risk, compliance time), pays €25k. ROI from day 1.'} +
+ {de ? 'Kunde spart €55k, zahlt €25k. ROI ab Tag 1.' : 'Customer saves €55k, pays €25k. ROI from day 1.'} +
+ + {/* Itemized breakdown */} +
+ {([ + [de ? 'Pentests (kontinuierlich, inkl.)' : 'Pentests (continuous, incl.)', '+€13k'], + [de ? 'CE-Risiko (Code-basiert, inkl.)' : 'CE risk (code-based, incl.)', '+€9k'], + [de ? 'Compliance-Zeit (−60%)' : 'Compliance time (−60%)', '+€15k'], + [de ? 'Audit-Vorbereitung (auto)' : 'Audit prep (auto)', '+€9k'], + [de ? 'Legal-Stunden (−40%)' : 'Legal hours (−40%)', '+€5k'], + [de ? 'Schulungen (Academy inkl.)' : 'Training (Academy incl.)', '+€4k'], + ] as [string, string][]).map(([l, v], i, arr) => ( +
+ {l} + {v} +
+ ))} +
+ +
+ {de + ? 'Plus Vermeidung von Bußgeldern (bis 4% Jahresumsatz) und gewonnene RFQs.' + : 'Plus avoided fines (up to 4% annual revenue) and won RFQs.'}