feat(pitch-print): TL;DR + Differentiators + KPIs + Tech Stack + P&L promoted
Build pitch-deck / build-push-deploy (push) Successful in 1m43s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 33s
CI / test-python-voice (push) Successful in 31s
CI / test-bqas (push) Successful in 32s

Adds the 5 slides flagged as missing vs Claude Design (30 slides). Standard
PDF now matches Claude's slide count and structure.

New slides (PrintNewSlides.tsx):
- TL;DR / 30 SEKUNDEN — 4 quad cards (Scale / Sovereignty / Bidirectional /
  Speed) with mono kicker, hero stat, body and ticker line. Slot 3, after the
  exec summary.
- Differentiators — 4 under-the-hood cards (Traceability / Engine / Optimizer
  / EU-Trust-Stack) extracted from USP p2. Slot 9, after USP. Each card has
  the lucide icon in a violet/amber tile, full body + bullets, and the mono
  ticker line.
- KPIs (Trajektorie 2026 → 2030) — 8 hero tiles showing year-1 → year-5
  transitions (ARR, customers, ARPU, employees, gross margin, EBIT, net
  income, cash). Derived live from computeAnnualKPIs(fmResults). Slot 23.
- Tech Stack — 8-category grid (Frontend / Backend / Storage / AI-RAG /
  Code-Scanning / Auth / Comms / DevOps), each with lucide icon tile +
  category label + monospaced tech list. Slot 31, after Engineering.

USP p2 redesigned: now hero-sized closing loop only (the 4 cards moved to
Differentiators). Bigger LoopDiagram in a violet-tinted hero panel, 12mm
inner padding, more room for the hub body + bullets.

P&L Detail (PrintFinancialsPage) promoted from financial-only to standard
PDF. Kicker now 21 (was '17b'), subtitle rewritten ('Annualisierte GuV',
no longer 'Investor-only'). Empty-data fallback added so it doesn't crash
if fmResults isn't populated.

Anhang divider moved from PrintAnnexSlides.tsx to PrintNewSlides.tsx (was
pushing PrintAnnexSlides over the 500-LOC cap). Section list inside the
divider updated for the new numbering — now 12 sections from #18 GTM down
to #29 Glossary.

PrintDeck.tsx: BASE_PAGES bumped 30 → 35. Render order updated; hasFinancialDetail
flag removed (P&L always rendered); cap-table is the only remaining
financial-only conditional and stays suppressed for Wandeldarlehen.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-05-20 13:11:02 +02:00
parent ad61fd3779
commit b4043b20b2
5 changed files with 401 additions and 177 deletions
@@ -4,107 +4,8 @@ import { ArchitectureDiagram, PipelineFlow } from './PrintDiagrams'
interface SlideBase { lang: Language; pageNum: number; totalPages: number; versionName: string }
/* ===== ANHANG DIVIDER ===== */
/**
* Chapter-break slide that sits between the main pitch and the appendix.
* Bypasses the standard Page chrome to render as a hero divider, but keeps
* a footer row consistent with all other slides.
*/
export function PrintAnnexDividerPage({ lang, pageNum, totalPages, versionName }: SlideBase) {
const de = lang === 'de'
const MONO = "'JetBrains Mono', ui-monospace, monospace"
const sections: [string, string, string][] = de ? [
['17', 'Go-to-Market Strategie', 'Pilot · Skalierung · Expansion'],
['18', 'Finanzplan', 'P&L 20262030 · KPI-Dashboard'],
['19', 'Treibervariablen', 'Annahmen + Sensitivitätsszenarien'],
['20', 'Regulatorische Details', 'DSGVO · AI Act · NIS-2 · CRA · MaschVO'],
['21', 'Systemarchitektur', '3 Tiers · LiteLLM Gateway · lokale Inferenz'],
['22', 'Engineering Deep Dive', '500K+ LoC · 45 Container · 100 % Self-Hosted'],
['23', 'KI-Pipeline', 'RAG · Multi-Agent · Document Intelligence · QA'],
['24', 'Risiken & Mitigation', '10 Risiken in 5 Kategorien'],
['25', 'Glossar', '30 Begriffe · Compliance · Engineering · Recht'],
] : [
['17', 'Go-to-Market Strategy', 'Pilot · Scale · Expansion'],
['18', 'Financial Plan', 'P&L 20262030 · KPI dashboard'],
['19', 'Driver Variables', 'Assumptions + sensitivity scenarios'],
['20', 'Regulatory Details', 'GDPR · AI Act · NIS-2 · CRA · Machinery Reg.'],
['21', 'System Architecture', '3 tiers · LiteLLM gateway · local inference'],
['22', 'Engineering Deep Dive', '500K+ LoC · 45 containers · 100 % self-hosted'],
['23', 'AI Pipeline', 'RAG · multi-agent · document intelligence · QA'],
['24', 'Risks & Mitigation', '10 risks across 5 categories'],
['25', 'Glossary', '30 terms · compliance · engineering · law'],
]
return (
<div className="print-page-break">
<div className="print-page print-page-bg" style={{
width: '297mm',
height: '210mm',
color: COLORS.slate900,
fontFamily: "'Inter', system-ui, sans-serif",
boxSizing: 'border-box',
padding: '14mm 18mm',
margin: '0 auto 24px',
boxShadow: '0 4px 24px rgba(59,26,122,0.10)',
overflow: 'hidden',
WebkitPrintColorAdjust: 'exact',
printColorAdjust: 'exact',
display: 'flex',
flexDirection: 'column',
}}>
{/* Top meta row */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: `1px solid ${COLORS.slate200}`, paddingBottom: '3mm' }}>
<span style={{ fontFamily: MONO, fontSize: '7.5pt', fontWeight: 700, color: COLORS.violet600, textTransform: 'uppercase', letterSpacing: '0.22em' }}>{de ? 'Teil II · Anhang' : 'Part II · Appendix'}</span>
<span style={{ fontFamily: MONO, fontSize: '7pt', color: COLORS.slate500, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 700 }}>BreakPilot &middot; ComplAI</span>
</div>
{/* Hero */}
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', maxWidth: '260mm' }}>
<div style={{ fontFamily: MONO, fontSize: '10pt', fontWeight: 700, color: COLORS.violet600, textTransform: 'uppercase', letterSpacing: '0.3em', marginBottom: '6mm' }}>
{de ? '16 · Kapitelwechsel' : '16 · Chapter break'}
</div>
<h1 style={{ fontSize: '74pt', fontWeight: 800, color: COLORS.slate900, lineHeight: 0.95, letterSpacing: '-0.035em', margin: 0 }}>
{de ? 'Anhang' : 'Appendix'}<span style={{ color: COLORS.violet600 }}>.</span>
</h1>
<div style={{ height: '3px', width: '60mm', background: `linear-gradient(90deg, ${COLORS.violet700} 0%, ${COLORS.violet400} 50%, ${COLORS.violet700} 100%)`, marginTop: '6mm', marginBottom: '8mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }} />
<p style={{ fontSize: '15pt', fontWeight: 500, color: COLORS.slate700, lineHeight: 1.3, margin: 0, letterSpacing: '-0.008em', maxWidth: '230mm' }}>
{de
? 'Detailangaben & Belege. Was wir in der Pitch gesagt haben, mit Quellen, Zahlen und Architektur belegt.'
: 'Detail & evidence. Everything we claimed in the pitch, backed by sources, numbers and architecture.'}
</p>
{/* What's coming */}
<div style={{ marginTop: '12mm' }}>
<div style={{ fontFamily: MONO, fontSize: '8pt', fontWeight: 700, color: COLORS.slate500, textTransform: 'uppercase', letterSpacing: '0.18em', marginBottom: '4mm' }}>
{de ? 'Auf den folgenden Seiten' : 'On the following pages'}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '5mm 8mm' }}>
{sections.map(([n, t, sub]) => (
<div key={n} style={{ display: 'flex', alignItems: 'flex-start', gap: '4mm' }}>
<span style={{ fontFamily: MONO, fontSize: '14pt', fontWeight: 800, color: COLORS.violet600, lineHeight: 1, fontVariantNumeric: 'tabular-nums', letterSpacing: '-0.02em', minWidth: '11mm' }}>{n}</span>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: '10pt', fontWeight: 700, color: COLORS.slate900, lineHeight: 1.2 }}>{t}</div>
<div style={{ fontSize: '7.5pt', color: COLORS.slate600, marginTop: '0.5mm', lineHeight: 1.35 }}>{sub}</div>
</div>
</div>
))}
</div>
</div>
</div>
{/* Footer (matches Page primitive) */}
<div className="print-mono" style={{ fontFamily: MONO, paddingTop: '3mm', borderTop: `1px solid ${COLORS.slate200}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between', fontSize: '7pt', color: COLORS.slate500, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 700 }}>
<span>BreakPilot &middot; ComplAI</span>
<span style={{ color: COLORS.violet600 }}>{versionName}</span>
<span style={{ fontVariantNumeric: 'tabular-nums' }}>{String(pageNum).padStart(2, '0')} / {String(totalPages).padStart(2, '0')}</span>
</div>
</div>
</div>
)
}
/* The Anhang divider lives in PrintNewSlides.tsx so this file stays under
* the 500-LOC cap. */
/* ===== STRATEGY / GO-TO-MARKET ===== */
@@ -18,10 +18,13 @@ import {
PrintCompetitionPage1, PrintCompetitionPage2,
} from './PrintCompetitionSlides'
import {
PrintAnnexDividerPage,
PrintStrategyPage, PrintRegulatoryPage, PrintArchitecturePage,
PrintEngineeringPage, PrintAIPipelinePage, PrintRisksPage, PrintGlossaryPage,
} from './PrintAnnexSlides'
import {
PrintTLDRPage, PrintDifferentiatorsPage, PrintKPIHeroPage,
PrintTechStackPage, PrintAnnexDividerPage,
} from './PrintNewSlides'
import {
PrintFinanzplanPage1, PrintFinanzplanPage2, PrintAssumptionsPage,
PrintFinancialsPage, PrintCapTablePage, PrintDisclaimerPage,
@@ -44,17 +47,21 @@ export default function PrintDeck({ pitchData, versionName, fmResults, fmAssumpt
(pitchData.funding?.instrument || '').toLowerCase().includes('convertible')
const hasCapTable = financial && !isWandeldarlehen
const annualRows = aggregateAnnualRows(fmResults)
const hasFinancialDetail = financial && annualRows.length > 0
const hasFinancialData = annualRows.length > 0
const de = lang === 'de'
// Base standard PDF: 30 physical pages.
// 2 (exec) + 1 (cover) + 1 (problem) + 1 (solution) + 2 (usp) + 1 (regL) +
// 1 (product) + 1 (how) + 1 (market) + 1 (bm) + 1 (milestones) + 2 (competition) +
// 1 (team) + 1 (ask) + 1 (savings) + 1 (anhang-divider) + 1 (strategy) +
// 2 (finanzplan) + 1 (assumptions) + 1 (regulatory) + 1 (architecture) +
// 1 (engineering) + 1 (aipipeline) + 1 (risks) + 1 (glossary) + 1 (disclaimer) = 30
const BASE_PAGES = 30
const totalPages = BASE_PAGES + (hasFinancialDetail ? 1 : 0) + (hasCapTable ? 1 : 0)
// Base standard PDF: 35 physical pages.
// 2 (exec) + 1 (TL;DR) + 1 (cover) + 1 (problem) + 1 (solution) + 2 (usp) +
// 1 (differentiators) + 1 (regL) + 1 (product) + 1 (how) + 1 (market) + 1 (bm) +
// 1 (milestones) + 2 (competition) + 1 (team) + 1 (ask) + 1 (savings) +
// 1 (anhang-divider) + 1 (strategy) + 1 (kpis) + 2 (finanzplan) + 1 (p&l detail) +
// 1 (assumptions) + 1 (regulatory) + 1 (architecture) + 1 (engineering) +
// 1 (tech-stack) + 1 (aipipeline) + 1 (risks) + 1 (glossary) + 1 (disclaimer) = 35
// P&L detail is now in standard PDF (was financial-only); cap-table stays
// financial-only (and is suppressed for Wandeldarlehen).
const BASE_PAGES = 35
const totalPages = BASE_PAGES + (hasCapTable ? 1 : 0)
void hasFinancialData
useEffect(() => {
const t = setTimeout(() => window.print(), 900)
@@ -93,96 +100,109 @@ export default function PrintDeck({ pitchData, versionName, fmResults, fmAssumpt
</div>
</div>
<div className="print-deck-wrapper" style={{ padding: '32px 0', fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif" }}>
{/* SLIDE_ORDER from lib/slide-order.ts, minus 3 interactive-only slides:
intro-presenter, ai-qa, annex-sdk-demo */}
<div className="print-deck-wrapper" style={{ padding: '32px 0', fontFamily: "'Inter', system-ui, sans-serif" }}>
{/* PITCH (slides 0117) */}
{/* 1. executive-summary (2 pages) */}
{/* 0102 executive-summary (2 pages) */}
<PrintExecSummaryPage1 market={pitchData.market || []} {...p()} />
<PrintExecSummaryPage2 {...p()} />
{/* 2. cover */}
{/* 03 TL;DR — 30 Sekunden */}
<PrintTLDRPage {...p()} />
{/* 04 cover */}
{(() => { n += 1; return <PrintCoverPage company={pitchData.company} funding={pitchData.funding} versionName={versionName} lang={lang} /> })()}
{/* 3. problem */}
{/* 05 problem */}
<PrintProblemPage {...p()} />
{/* 4. solution */}
{/* 06 solution */}
<PrintSolutionPage {...p()} />
{/* 5. usp (2 pages) */}
{/* 0708 usp (2 pages) */}
<PrintUSPPage1 {...p()} />
<PrintUSPPage2 {...p()} />
{/* 6. regulatory-landscape */}
{/* 09 differentiators */}
<PrintDifferentiatorsPage {...p()} />
{/* 10 regulatory-landscape */}
<PrintRegulatoryLandscapePage {...p()} />
{/* 7. product / modular-toolkit */}
{/* 11 product / modular-toolkit */}
<PrintProductPage products={pitchData.products || []} {...p()} />
{/* 8. how-it-works */}
{/* 12 how-it-works */}
<PrintHowItWorksPage {...p()} />
{/* 9. market */}
{/* 13 market */}
<PrintMarketPage market={pitchData.market || []} {...p()} />
{/* 10. business-model / pricing */}
{/* 14 business-model / pricing */}
<PrintBusinessModelPage {...p()} />
{/* 11. traction (uses milestones) */}
{/* 15 traction (milestones) */}
<PrintMilestonesPage milestones={pitchData.milestones || []} {...p()} />
{/* 12. competition (2 pages) */}
{/* 1617 competition (2 pages) */}
<PrintCompetitionPage1 {...p()} />
<PrintCompetitionPage2 {...p()} />
{/* 13. team */}
{/* 18 team */}
<PrintTeamPage team={pitchData.team || []} {...p()} />
{/* 14. the-ask */}
{/* 19 the-ask */}
<PrintTheAskPage funding={pitchData.funding} {...p()} />
{/* 15. customer-savings */}
{/* 20 customer-savings */}
<PrintCustomerSavingsPage {...p()} />
{/* 16. ANHANG divider — chapter break before the appendix block */}
{/* 21 ANHANG divider — chapter break before the appendix */}
<PrintAnnexDividerPage {...p()} />
{/* 17. annex-strategy */}
{/* APPENDIX (slides 2235) */}
{/* 22 annex-strategy */}
<PrintStrategyPage {...p()} />
{/* 17. annex-finanzplan (2 pages) */}
{/* 23 KPIs — 2026 → 2030 trajectory */}
<PrintKPIHeroPage fmResults={fmResults} {...p()} />
{/* 2425 annex-finanzplan (2 pages) */}
<PrintFinanzplanPage1 fmResults={fmResults} {...p()} />
<PrintFinanzplanPage2 fmResults={fmResults} {...p()} />
{/* Financial-only: detail P&L */}
{hasFinancialDetail && <PrintFinancialsPage annualRows={annualRows} {...p()} />}
{/* 26 P&L detail (was financial-only; now standard) */}
<PrintFinancialsPage annualRows={annualRows} {...p()} />
{/* 18. annex-assumptions */}
{/* 27 annex-assumptions */}
<PrintAssumptionsPage assumptions={fmAssumptions} {...p()} />
{/* 19. annex-regulatory */}
{/* 28 annex-regulatory */}
<PrintRegulatoryPage {...p()} />
{/* 20. annex-architecture */}
{/* 29 annex-architecture */}
<PrintArchitecturePage {...p()} />
{/* 21. annex-engineering */}
{/* 30 annex-engineering */}
<PrintEngineeringPage {...p()} />
{/* 22. annex-aipipeline */}
{/* 31 tech-stack */}
<PrintTechStackPage {...p()} />
{/* 32 annex-aipipeline */}
<PrintAIPipelinePage {...p()} />
{/* 23. risks */}
{/* 33 risks */}
<PrintRisksPage {...p()} />
{/* 24. annex-glossary */}
{/* 34 annex-glossary */}
<PrintGlossaryPage {...p()} />
{/* Financial-only: cap-table (suppressed for Wandeldarlehen) */}
{hasCapTable && <PrintCapTablePage {...p()} />}
{/* 25. legal-disclaimer */}
{/* 35 legal-disclaimer */}
<PrintDisclaimerPage {...p()} />
</div>
</>
@@ -260,13 +260,20 @@ export function PrintAssumptionsPage({ assumptions, lang, pageNum, totalPages, v
)
}
/* ===== FINANCIAL DETAIL (financial-only extra page) ===== */
/* ===== P&L DETAIL — now in standard PDF ===== */
export function PrintFinancialsPage({ annualRows, lang, pageNum, totalPages, versionName }: SlideBase & { annualRows: AnnualPLRow[] }) {
const de = lang === 'de'
const breakEvenYear = annualRows.find(r => r.ebitda_eur > 0)?.year
if (annualRows.length === 0) {
return (
<Page kicker="17b" section={de ? 'ANHANG · DETAIL P&L' : 'APPENDIX · P&L DETAIL'} title={de ? 'Vollständige P&L mit Quartals-Aggregation.' : 'Full P&L with quarterly aggregation.'} subtitle={de ? 'Nur in der Financial-PDF-Version enthalten. Investor-only.' : 'Included only in Financial PDF version. Investor only.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<Page kicker="21" section={de ? 'ANHANG · P&L DETAIL' : 'APPENDIX · P&L DETAIL'} title={de ? 'P&L Detail nicht verfügbar' : 'P&L detail unavailable'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<p style={{ fontSize: '10pt', color: COLORS.slate600 }}>{de ? 'Keine Finanzdaten vorhanden. Bitte Base-Case-Szenario auswählen.' : 'No financial data available. Please select base-case scenario.'}</p>
</Page>
)
}
return (
<Page kicker="21" section={de ? 'ANHANG · P&L DETAIL' : 'APPENDIX · P&L DETAIL'} title={de ? 'Annualisierte Gewinn- und Verlust-Rechnung.' : 'Annualized profit & loss.'} subtitle={de ? 'Konsolidierte Jahreswerte 20262030 in EUR. Klammern () zeigen Aufwendungen. EBITDA in grün ab Break-Even.' : 'Consolidated annual values 20262030 in EUR. Parentheses () mark costs. EBITDA in green from break-even on.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<DataTable
cols={[
@@ -0,0 +1,302 @@
import { Language, FMResult } from '@/lib/types'
import { Page, COLORS, Bullets } from './PrintLayout'
import {
ScanLine, Shield, Database, Brain, ShieldCheck, Lock, MessageSquare, Wrench,
Layers, Sparkles, TrendingUp, Globe,
type LucideIcon,
} from 'lucide-react'
import { getDetails } from '@/components/slides/USPSlide.data'
import { computeAnnualKPIs } from '@/lib/finanzplan/annual-kpis'
interface SlideBase { lang: Language; pageNum: number; totalPages: number; versionName: string }
const MONO = "'JetBrains Mono', ui-monospace, monospace"
/* ====================================================================== */
/* TL;DR — 02 · 30 Sekunden (4 quad cards) */
/* ====================================================================== */
export function PrintTLDRPage({ lang, pageNum, totalPages, versionName }: SlideBase) {
const de = lang === 'de'
const cards = de ? [
{ kicker: '01 · Scale', title: '25.000+ Controls', body: 'Atomare Prüfaspekte über DSGVO, AI Act, NIS-2, CRA, MaschVO und 380+ weitere Quellen.', ticker: 'idx 26.123 atomic checks', tint: COLORS.violet600 },
{ kicker: '02 · Sovereignty', title: '100 % EU-souverän', body: 'BSI-C5-zertifizierte Cloud in Deutschland und Frankreich. Keine US-Anbieter. Air-gap-fähig.', ticker: 'region BSI C5 · DE/FR', tint: COLORS.violet500 },
{ kicker: '03 · Bidirectional', title: 'Compliance ↔ Code', body: 'Policy-Änderungen fliessen in den Code; Code-Änderungen aktualisieren Policies. Zero Drift.', ticker: 'sync policy.md → controller.ts', tint: COLORS.amber600 },
{ kicker: '04 · Speed', title: '<20 Tage audit-ready', body: 'Vom Vertrag zum auditfähigen Status: typischerweise 1420 Tage. White-Glove-Onboarding.', ticker: 'tag 17 bis audit-ready', tint: COLORS.amber700 },
] : [
{ kicker: '01 · Scale', title: '25,000+ Controls', body: 'Atomic audit aspects across GDPR, AI Act, NIS-2, CRA, Machinery Reg. and 380+ other sources.', ticker: 'idx 26,123 atomic checks', tint: COLORS.violet600 },
{ kicker: '02 · Sovereignty', title: '100 % EU-sovereign', body: 'BSI-C5 certified cloud in Germany and France. No US vendors. Air-gap capable.', ticker: 'region BSI C5 · DE/FR', tint: COLORS.violet500 },
{ kicker: '03 · Bidirectional', title: 'Compliance ↔ Code', body: 'Policy edits flow into code; code changes update policies. Zero drift.', ticker: 'sync policy.md → controller.ts', tint: COLORS.amber600 },
{ kicker: '04 · Speed', title: '<20 days audit-ready', body: 'From contract to audit-ready: typically 1420 days. White-glove onboarding.', ticker: 'day 17 to audit-ready', tint: COLORS.amber700 },
]
return (
<Page kicker="02" section={de ? '30 SEKUNDEN' : '30 SECONDS'} title={de ? 'Was BreakPilot in einem Satz.' : 'BreakPilot in one sentence.'} subtitle={de ? 'Kontinuierliche Compliance & Security für den industriellen Mittelstand — EU-souverän, Bidirektional, in unter 20 Tagen produktiv.' : 'Continuous compliance & security for the industrial mid-market — EU-sovereign, bidirectional, productive in under 20 days.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gridTemplateRows: '1fr 1fr', gap: '5mm', flex: 1, minHeight: 0 }}>
{cards.map((c, i) => (
<div key={i} style={{ border: `1px solid ${c.tint}55`, borderTop: `3px solid ${c.tint}`, borderRadius: '4pt', background: `linear-gradient(135deg, ${c.tint}10 0%, #ffffff 100%)`, padding: '5mm 6mm', display: 'flex', flexDirection: 'column', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<div style={{ fontFamily: MONO, fontSize: '8pt', fontWeight: 700, color: c.tint, textTransform: 'uppercase', letterSpacing: '0.22em', marginBottom: '3mm' }}>{c.kicker}</div>
<div style={{ fontSize: '22pt', fontWeight: 800, color: COLORS.slate900, lineHeight: 1.05, letterSpacing: '-0.025em', marginBottom: '3mm' }}>{c.title}</div>
<div style={{ fontSize: '9pt', color: COLORS.slate700, lineHeight: 1.5, flex: 1 }}>{c.body}</div>
<div style={{ marginTop: '3mm', paddingTop: '2mm', borderTop: `1px solid ${COLORS.slate200}`, display: 'flex', alignItems: 'center', gap: '2mm' }}>
<span style={{ width: '2mm', height: '2mm', borderRadius: '50%', background: COLORS.emerald600, WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }} />
<span style={{ fontFamily: MONO, fontSize: '7pt', color: c.tint, fontWeight: 700 }}>{c.ticker}</span>
</div>
</div>
))}
</div>
</Page>
)
}
/* ====================================================================== */
/* DIFFERENTIATORS — 4 under-the-hood cards (moved out of USP p2) */
/* ====================================================================== */
const DIFF_ICON: Record<string, LucideIcon> = {
trace: Layers,
engine: Sparkles,
opt: TrendingUp,
stack: Globe,
}
export function PrintDifferentiatorsPage({ lang, pageNum, totalPages, versionName }: SlideBase) {
const de = lang === 'de'
const d = getDetails(de)
const cards = ['trace', 'engine', 'opt', 'stack'] as const
const tickers = de
? ['trace 13.605 evidence-chain', 'validate 1.450 · 98,9 %', 'optimize gap → policy §4.2', 'check BSI C5 · 1.382']
: ['trace 13,605 evidence-chain', 'validate 1,450 · 98.9 %', 'optimize gap → policy §4.2', 'check BSI C5 · 1,382']
return (
<Page kicker="06" section={de ? 'DIFFERENTIATORS' : 'DIFFERENTIATORS'} title={de ? 'Vier technische Differentiator.' : 'Four technical differentiators.'} subtitle={de ? 'Was kein anderer Anbieter geschlossen liefert: Traceability, kontinuierliche Engine, Compliance-Optimizer, EU-Trust-Stack.' : 'What no other vendor delivers end-to-end: traceability, continuous engine, compliance optimizer, EU-trust stack.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '4mm', flex: 1, minHeight: 0 }}>
{cards.map((k, i) => {
const p = d[k]
const Icon = DIFF_ICON[k]
const tint = i < 2 ? COLORS.violet600 : COLORS.amber600
const tintDark = i < 2 ? COLORS.violet700 : COLORS.amber700
const tintLight = i < 2 ? COLORS.violet50 : COLORS.amber50
return (
<div key={k} style={{ border: `1px solid ${tint}40`, background: `linear-gradient(135deg, ${tintLight} 0%, #ffffff 100%)`, borderRadius: '4pt', padding: '4mm 5mm', display: 'flex', flexDirection: 'column', overflow: 'hidden', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<div style={{ width: '9mm', height: '9mm', background: '#ffffff', borderRadius: '2pt', border: `1px solid ${tint}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: tint, marginBottom: '3mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
{Icon && <Icon size={16} strokeWidth={1.5} />}
</div>
<div style={{ fontFamily: MONO, fontSize: '7pt', fontWeight: 700, color: tintDark, textTransform: 'uppercase', letterSpacing: '0.16em', marginBottom: '1mm' }}>{String(i + 1).padStart(2, '0')} &middot; {p.kicker.replace(/^Säule[\s·]+|^Under the Hood|^Pillar[\s·]+/i, '').trim() || p.kicker}</div>
<div style={{ fontSize: '12pt', fontWeight: 700, color: COLORS.slate900, lineHeight: 1.15, marginBottom: '2.5mm', letterSpacing: '-0.005em' }}>{p.title}</div>
<div style={{ fontSize: '8pt', color: COLORS.slate700, lineHeight: 1.45, marginBottom: '2.5mm' }}>{p.body}</div>
{p.bullets && <Bullets dense items={p.bullets} />}
<div style={{ marginTop: 'auto', paddingTop: '2mm', borderTop: `1px solid ${COLORS.slate200}`, display: 'flex', alignItems: 'center', gap: '2mm' }}>
<span style={{ width: '2mm', height: '2mm', borderRadius: '50%', background: COLORS.emerald600, WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }} />
<span style={{ fontFamily: MONO, fontSize: '6.5pt', color: tintDark, fontWeight: 700 }}>{tickers[i]}</span>
</div>
</div>
)
})}
</div>
</Page>
)
}
/* ====================================================================== */
/* KPI HERO — 8 trajectory tiles 2026 → 2030 */
/* ====================================================================== */
function fmtEur(n: number): string {
const abs = Math.abs(n)
const sign = n < 0 ? '' : ''
if (abs >= 1e6) return `${sign}${(abs / 1e6).toLocaleString('de-DE', { maximumFractionDigits: 1 })}M`
if (abs >= 1e3) return `${sign}${Math.round(abs / 1e3)}k`
return `${sign}${Math.round(abs)}`
}
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 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 },
] : []
return (
<Page kicker="18" section={de ? 'ANHANG · KENNZAHLEN' : 'APPENDIX · KEY METRICS'} title={de ? 'Trajektorie 2026 → 2030.' : 'Trajectory 2026 → 2030.'} subtitle={de ? `Acht investorrelevante KPIs auf einen Blick. Base-Case-Szenario, abgeleitet aus dem Finanzplan. Break-Even: ${breakEvenYear ?? 'Q3 2029'}.` : `Eight investor-relevant KPIs at a glance. Base-case scenario, derived from the financial plan. Break-even: ${breakEvenYear ?? 'Q3 2029'}.`} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
{tiles.length === 0 ? (
<p style={{ fontSize: '10pt', color: COLORS.slate600 }}>{de ? 'Keine Finanzdaten verfügbar.' : 'No financial data available.'}</p>
) : (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gridTemplateRows: '1fr 1fr', gap: '5mm', flex: 1, minHeight: 0 }}>
{tiles.map((t, i) => (
<div key={i} style={{ border: `1px solid ${COLORS.slate200}`, borderTop: `3px solid ${COLORS.violet600}`, background: '#ffffff', padding: '5mm', display: 'flex', flexDirection: 'column', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<div style={{ fontFamily: MONO, fontSize: '7.5pt', fontWeight: 700, color: COLORS.slate500, textTransform: 'uppercase', letterSpacing: '0.18em', marginBottom: '4mm' }}>{t.label}</div>
<div style={{ display: 'flex', alignItems: 'baseline', gap: '3mm', marginBottom: 'auto' }}>
<span style={{ fontSize: '11pt', fontWeight: 600, color: COLORS.slate400, fontVariantNumeric: 'tabular-nums' }}>{t.start}</span>
<span style={{ fontFamily: MONO, fontSize: '11pt', color: COLORS.slate400, fontWeight: 700 }}></span>
<span style={{ fontSize: '24pt', fontWeight: 800, color: t.endColor, lineHeight: 1, fontVariantNumeric: 'tabular-nums', letterSpacing: '-0.025em' }}>{t.end}</span>
</div>
<div style={{ marginTop: '3mm', paddingTop: '2mm', borderTop: `1px solid ${COLORS.slate100}`, fontFamily: MONO, fontSize: '6.5pt', color: COLORS.slate500, textTransform: 'uppercase', letterSpacing: '0.12em', fontWeight: 700 }}>2026 &middot; 2030</div>
</div>
))}
</div>
)}
</Page>
)
}
/* ====================================================================== */
/* TECH STACK — 8-category grid */
/* ====================================================================== */
export function PrintTechStackPage({ lang, pageNum, totalPages, versionName }: SlideBase) {
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, 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'] },
]
return (
<Page kicker="26" section={de ? 'ANHANG · TECH-STACK' : 'APPENDIX · TECH STACK'} title={de ? '8 Kategorien. Polyglott. 100 % Open Source.' : '8 categories. Polyglot. 100 % open source.'} subtitle={de ? 'Alle Komponenten mit kommerziell nutzbarer Lizenz (MIT, Apache-2.0, BSD, ISC, MPL-2.0, LGPL). Keine GPL/AGPL. Keine US-SaaS-Abhängigkeit.' : 'All components carry a commercially usable license (MIT, Apache-2.0, BSD, ISC, MPL-2.0, LGPL). No GPL/AGPL. No US SaaS dependency.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gridTemplateRows: '1fr 1fr', gap: '4mm', flex: 1, minHeight: 0 }}>
{cats.map((c, i) => {
const Icon = c.icon
return (
<div key={i} style={{ border: `1px solid ${COLORS.slate200}`, borderTop: `2px solid ${COLORS.violet600}`, background: '#ffffff', padding: '4mm 5mm', display: 'flex', flexDirection: 'column', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '3mm', marginBottom: '3mm' }}>
<div style={{ width: '8mm', height: '8mm', background: COLORS.violet50, borderRadius: '2pt', display: 'flex', alignItems: 'center', justifyContent: 'center', color: COLORS.violet600, flexShrink: 0, WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<Icon size={15} strokeWidth={1.5} />
</div>
<div style={{ fontSize: '11pt', fontWeight: 700, color: COLORS.slate900, lineHeight: 1.15 }}>{c.name}</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0' }}>
{c.items.map((it, j) => (
<div key={j} style={{ fontFamily: MONO, fontSize: '8pt', color: COLORS.slate700, padding: '1.2mm 0', borderTop: j > 0 ? `1px solid ${COLORS.slate100}` : 'none', lineHeight: 1.3 }}>{it}</div>
))}
</div>
</div>
)
})}
</div>
</Page>
)
}
/* ====================================================================== */
/* ANHANG DIVIDER (moved from PrintAnnexSlides.tsx) */
/* ====================================================================== */
export function PrintAnnexDividerPage({ lang, pageNum, totalPages, versionName }: SlideBase) {
const de = lang === 'de'
const sections: [string, string, string][] = de ? [
['18', 'Go-to-Market Strategie', 'Pilot · Skalierung · Expansion'],
['19', 'Kennzahlen 2026 → 2030', '8 KPIs Trajektorie · Base-Case'],
['20', 'Finanzplan', 'P&L 20262030 · KPI-Dashboard'],
['21', 'P&L Detail', 'Annualisierte Gewinn-/Verlust-Rechnung'],
['22', 'Treibervariablen', 'Annahmen + Sensitivitätsszenarien'],
['23', 'Regulatorische Details', 'DSGVO · AI Act · NIS-2 · CRA · MaschVO'],
['24', 'Systemarchitektur', '3 Tiers · LiteLLM Gateway · lokale Inferenz'],
['25', 'Engineering Deep Dive', '500K+ LoC · 45 Container · 100 % Self-Hosted'],
['26', 'Tech-Stack', '8 Kategorien · polyglott · Open Source'],
['27', 'KI-Pipeline', 'RAG · Multi-Agent · Document Intelligence · QA'],
['28', 'Risiken & Mitigation', '10 Risiken in 5 Kategorien'],
['29', 'Glossar', '30 Begriffe · Compliance · Engineering · Recht'],
] : [
['18', 'Go-to-Market Strategy', 'Pilot · Scale · Expansion'],
['19', 'KPIs 2026 → 2030', '8 KPI trajectory · base case'],
['20', 'Financial Plan', 'P&L 20262030 · KPI dashboard'],
['21', 'P&L Detail', 'Annualized profit & loss'],
['22', 'Driver Variables', 'Assumptions + sensitivity scenarios'],
['23', 'Regulatory Details', 'GDPR · AI Act · NIS-2 · CRA · Machinery Reg.'],
['24', 'System Architecture', '3 tiers · LiteLLM gateway · local inference'],
['25', 'Engineering Deep Dive', '500K+ LoC · 45 containers · 100 % self-hosted'],
['26', 'Tech Stack', '8 categories · polyglot · open source'],
['27', 'AI Pipeline', 'RAG · multi-agent · document intelligence · QA'],
['28', 'Risks & Mitigation', '10 risks across 5 categories'],
['29', 'Glossary', '30 terms · compliance · engineering · law'],
]
return (
<div className="print-page-break">
<div className="print-page print-page-bg" style={{
width: '297mm', height: '210mm', color: COLORS.slate900,
fontFamily: "'Inter', system-ui, sans-serif", boxSizing: 'border-box',
padding: '14mm 18mm', margin: '0 auto 24px',
boxShadow: '0 4px 24px rgba(59,26,122,0.10)', overflow: 'hidden',
WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact',
display: 'flex', flexDirection: 'column',
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: `1px solid ${COLORS.slate200}`, paddingBottom: '3mm' }}>
<span style={{ fontFamily: MONO, fontSize: '7.5pt', fontWeight: 700, color: COLORS.violet600, textTransform: 'uppercase', letterSpacing: '0.22em' }}>{de ? 'Teil II · Anhang' : 'Part II · Appendix'}</span>
<span style={{ fontFamily: MONO, fontSize: '7pt', color: COLORS.slate500, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 700 }}>BreakPilot &middot; ComplAI</span>
</div>
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', maxWidth: '260mm' }}>
<div style={{ fontFamily: MONO, fontSize: '10pt', fontWeight: 700, color: COLORS.violet600, textTransform: 'uppercase', letterSpacing: '0.3em', marginBottom: '5mm' }}>
{de ? '17 · Kapitelwechsel' : '17 · Chapter break'}
</div>
<h1 style={{ fontSize: '64pt', fontWeight: 800, color: COLORS.slate900, lineHeight: 0.95, letterSpacing: '-0.035em', margin: 0 }}>
{de ? 'Anhang' : 'Appendix'}<span style={{ color: COLORS.violet600 }}>.</span>
</h1>
<div style={{ height: '3px', width: '60mm', background: `linear-gradient(90deg, ${COLORS.violet700} 0%, ${COLORS.violet400} 50%, ${COLORS.violet700} 100%)`, marginTop: '5mm', marginBottom: '6mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }} />
<p style={{ fontSize: '13pt', fontWeight: 500, color: COLORS.slate700, lineHeight: 1.3, margin: 0, letterSpacing: '-0.008em', maxWidth: '230mm' }}>
{de
? 'Detailangaben & Belege. Was wir in der Pitch gesagt haben, mit Quellen, Zahlen und Architektur belegt.'
: 'Detail & evidence. Everything we claimed in the pitch, backed by sources, numbers and architecture.'}
</p>
<div style={{ marginTop: '8mm' }}>
<div style={{ fontFamily: MONO, fontSize: '8pt', fontWeight: 700, color: COLORS.slate500, textTransform: 'uppercase', letterSpacing: '0.18em', marginBottom: '4mm' }}>
{de ? 'Auf den folgenden Seiten' : 'On the following pages'}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '4mm 8mm' }}>
{sections.map(([n, t, sub]) => (
<div key={n} style={{ display: 'flex', alignItems: 'flex-start', gap: '4mm' }}>
<span style={{ fontFamily: MONO, fontSize: '13pt', fontWeight: 800, color: COLORS.violet600, lineHeight: 1, fontVariantNumeric: 'tabular-nums', letterSpacing: '-0.02em', minWidth: '11mm' }}>{n}</span>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: '9.5pt', fontWeight: 700, color: COLORS.slate900, lineHeight: 1.2 }}>{t}</div>
<div style={{ fontSize: '7pt', color: COLORS.slate600, marginTop: '0.5mm', lineHeight: 1.35 }}>{sub}</div>
</div>
</div>
))}
</div>
</div>
</div>
<div className="print-mono" style={{ fontFamily: MONO, paddingTop: '3mm', borderTop: `1px solid ${COLORS.slate200}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between', fontSize: '7pt', color: COLORS.slate500, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 700 }}>
<span>BreakPilot &middot; ComplAI</span>
<span style={{ color: COLORS.violet600 }}>{versionName}</span>
<span style={{ fontVariantNumeric: 'tabular-nums' }}>{String(pageNum).padStart(2, '0')} / {String(totalPages).padStart(2, '0')}</span>
</div>
</div>
</div>
)
}
@@ -61,49 +61,43 @@ export function PrintUSPPage1({ lang, pageNum, totalPages, versionName }: SlideB
)
}
/* ===== USP, PAGE 2 (under the hood + closing loop) ===== */
/* ===== USP, PAGE 2 just the closing loop (was: 4 cards + loop) =====
*
* The 4 under-the-hood cards moved to the dedicated Differentiators slide
* (PrintDifferentiatorsPage). This page is now a hero "Compliance Code
* always in sync" closing card for the USP block.
*/
export function PrintUSPPage2({ lang, pageNum, totalPages, versionName }: SlideBase) {
const de = lang === 'de'
const d = getDetails(de)
const cards = ['trace', 'engine', 'opt', 'stack'] as const
const MONO = "'JetBrains Mono', ui-monospace, monospace"
return (
<Page kicker="05" section={de ? 'USP · 2 / 2' : 'USP · 2 / 2'} title={de ? 'Under the Hood, was die Plattform technisch trägt.' : 'Under the Hood, what the platform is built on.'} subtitle={de ? 'Vier technische Differentiator: Traceability, Continuous Engine, Compliance Optimizer, EU-Trust Stack.' : 'Four technical differentiators: traceability, continuous engine, compliance optimizer, EU trust stack.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<Page kicker="05" section={de ? 'USP · 2 / 2' : 'USP · 2 / 2'} title={de ? 'Compliance ↔ Code, immer in Sync.' : 'Compliance ↔ Code, always in sync.'} subtitle={de ? 'Eine geschlossene Schleife: jede Policy-Änderung fliesst in den Code; jede Code-Änderung in die Policy zurück. Zero Drift, eine Quelle der Wahrheit.' : 'A closed loop: every policy change flows into code; every code change flows back into policy. Zero drift, one source of truth.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '5mm' }}>
{cards.map(k => {
const p = d[k]
const Icon = USP_ICON[k]
return (
<div key={k} style={{ borderLeft: `2px solid ${COLORS.amber600}`, paddingLeft: '5mm', display: 'flex', flexDirection: 'column' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '3mm', marginBottom: '2mm' }}>
{Icon && <Icon size={18} strokeWidth={1.5} color={COLORS.amber700} />}
<span style={{ fontSize: '7.5pt', fontWeight: 700, color: COLORS.amber700, textTransform: 'uppercase', letterSpacing: '0.12em' }}>{p.kicker}</span>
{/* Full-page hero loop diagram */}
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
<div style={{ border: `1px solid ${COLORS.violet300}`, background: `linear-gradient(135deg, ${COLORS.violet50} 0%, #ffffff 50%, ${COLORS.violet50} 100%)`, borderRadius: '6pt', padding: '12mm 14mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '4mm', marginBottom: '6mm' }}>
<div style={{ width: '12mm', height: '12mm', borderRadius: '50%', background: COLORS.violet600, color: '#ffffff', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: `0 0 0 4px ${COLORS.violet50}`, WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<Infinity size={22} strokeWidth={2} />
</div>
<div style={{ fontSize: '12pt', fontWeight: 700, color: COLORS.slate900, lineHeight: 1.2, marginBottom: '2.5mm' }}>{p.title}</div>
<div style={{ fontSize: '8.5pt', color: COLORS.slate700, lineHeight: 1.5, marginBottom: '2.5mm' }}>{p.body}</div>
{p.bullets && <Bullets dense items={p.bullets} />}
</div>
)
})}
<span style={{ fontFamily: MONO, fontSize: '9pt', fontWeight: 700, color: COLORS.violet700, textTransform: 'uppercase', letterSpacing: '0.24em' }}>{de ? 'Die Schleife · Always in Sync' : 'The Loop · Always in Sync'}</span>
</div>
<div style={{ fontSize: '18pt', fontWeight: 800, color: COLORS.slate900, marginBottom: '4mm', lineHeight: 1.15, letterSpacing: '-0.01em', maxWidth: '210mm' }}>{d.hub.title}</div>
<div style={{ fontSize: '10pt', color: COLORS.slate700, lineHeight: 1.55, marginBottom: '6mm', maxWidth: '220mm' }}>{d.hub.body}</div>
{/* Closing loop: violet-tinted hero panel with the diagram on the right */}
<div style={{ marginTop: '6mm', border: `1px solid ${COLORS.violet300}`, background: `linear-gradient(135deg, ${COLORS.violet50} 0%, #ffffff 50%, ${COLORS.violet50} 100%)`, borderRadius: '3pt', padding: '5mm 6mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact', display: 'grid', gridTemplateColumns: '1fr 1.2fr', gap: '6mm', alignItems: 'center' }}>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: '3mm', marginBottom: '3mm' }}>
<div style={{ width: '10mm', height: '10mm', borderRadius: '50%', background: COLORS.violet600, color: '#ffffff', display: 'flex', alignItems: 'center', justifyContent: 'center', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<Infinity size={18} strokeWidth={2} />
</div>
<span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: '7.5pt', fontWeight: 700, color: COLORS.violet700, textTransform: 'uppercase', letterSpacing: '0.2em' }}>{de ? 'Die Schleife · Always in Sync' : 'The Loop · Always in Sync'}</span>
</div>
<div style={{ fontSize: '14pt', fontWeight: 800, color: COLORS.slate900, marginBottom: '2mm', lineHeight: 1.15, letterSpacing: '-0.01em' }}>{d.hub.title}</div>
<div style={{ fontSize: '8.5pt', color: COLORS.slate700, lineHeight: 1.55 }}>{d.hub.body}</div>
</div>
<div style={{ background: '#ffffff', border: `1px solid ${COLORS.violet200}`, borderRadius: '3pt', padding: '3mm 4mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<div style={{ background: '#ffffff', border: `1px solid ${COLORS.violet200}`, borderRadius: '3pt', padding: '5mm 6mm', WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }}>
<LoopDiagram lang={lang} />
</div>
{d.hub.bullets && (
<div style={{ marginTop: '5mm' }}>
<Bullets dense tone="accent" items={d.hub.bullets} />
</div>
)}
</div>
</div>
</Page>
)