fix(pitch-print): ComplAI brand, em-dash centering, fund fallback 400k
Build pitch-deck / build-push-deploy (push) Successful in 2m47s
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 43s
CI / test-python-voice (push) Successful in 36s
CI / test-bqas (push) Successful in 35s

Three universal fixes before per-slide redesigns:

1. Brand: new <ComplAI /> JSX component renders the product name correctly —
   'Compl' in inherited text color, 'AI' in violet (#7c3aed), no slashes.
   Replaces the previous 'BreakPilot COMPL/AI/' literal in the Executive
   Summary p1 title. Page primitive's title prop now accepts ReactNode so
   JSX brand wordmarks work anywhere a title would.

2. Em-dash centering: Bullets primitive previously placed each em-dash
   marker via absolute positioning with a hardcoded 'top: 4pt', which drifted
   relative to font-size and looked off-center in the rendered PDF. Now uses
   display:flex on the <li> with a fixed-width column that vertically centers
   the 0.5pt rule on the first line height of the text.

3. Funding fallback: cover + The Ask now default to 400_000 (was 1_000_000)
   when no funding amount is in the data. New base case is a €400k
   Wandeldarlehen, not €1M equity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-05-20 17:58:55 +02:00
parent 297eff949e
commit a6f4ca88a4
3 changed files with 36 additions and 9 deletions
@@ -1,5 +1,5 @@
import { Language, PitchCompany, PitchFunding, PitchMarket } from '@/lib/types'
import { Page, KpiRow, TwoCol, ThreeCol, FourCol, Panel, Bullets, Callout, COLORS, Divider } from './PrintLayout'
import { Page, KpiRow, TwoCol, ThreeCol, FourCol, Panel, Bullets, Callout, COLORS, Divider, ComplAI } from './PrintLayout'
interface SlideBase { lang: Language; pageNum: number; totalPages: number; versionName: string }
@@ -8,7 +8,7 @@ interface SlideBase { lang: Language; pageNum: number; totalPages: number; versi
export function PrintCoverPage({ company, funding, lang, versionName }: { company: PitchCompany; funding: PitchFunding; lang: Language; versionName: string }) {
const de = lang === 'de'
const instrument = funding?.instrument || 'Pre-Seed'
const amount = funding?.amount_eur || 1_000_000
const amount = funding?.amount_eur || 400_000
const tagline = de ? (company?.tagline_de || 'Kontinuierliche Compliance für europäische Unternehmen.') : (company?.tagline_en || 'Continuous compliance for European companies.')
const amountLabel = amount >= 1_000_000
? '€' + (amount / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M'
@@ -128,7 +128,7 @@ export function PrintExecSummaryPage1({ market, lang, pageNum, totalPages, versi
const fmt = (v?: number) => v ? (v >= 1e9 ? `${(v / 1e9).toFixed(1).replace('.', ',')} Mrd.` : `${(v / 1e6).toFixed(0)} Mio.`) : '—'
return (
<Page kicker="01" section={de ? 'EXECUTIVE SUMMARY' : 'EXECUTIVE SUMMARY'} title={de ? 'BreakPilot COMPL/AI/' : 'BreakPilot COMPL/AI/'} subtitle={de ? 'DSGVO-konforme KI-Plattform, kontinuierliches Sicherheitsscanning und intelligente Compliance-Automatisierung. 25.000+ atomare Prüfaspekte, 380+ Regularien, EU-souverän gehostet.' : 'GDPR-compliant AI platform, continuous security scanning and intelligent compliance automation. 25,000+ atomic audit aspects, 380+ regulations, EU-sovereign hosted.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<Page kicker="01" section={de ? 'EXECUTIVE SUMMARY' : 'EXECUTIVE SUMMARY'} title={<>BreakPilot <ComplAI /></>} subtitle={de ? 'DSGVO-konforme KI-Plattform, kontinuierliches Sicherheitsscanning und intelligente Compliance-Automatisierung. 25.000+ atomare Prüfaspekte, 380+ Regularien, EU-souverän gehostet.' : 'GDPR-compliant AI platform, continuous security scanning and intelligent compliance automation. 25,000+ atomic audit aspects, 380+ regulations, EU-sovereign hosted.'} pageNum={pageNum} totalPages={totalPages} versionName={versionName}>
<KpiRow items={[
{ n: '25k+', label: de ? 'Prüfaspekte' : 'Audit aspects', tone: 'accent' },
@@ -58,13 +58,29 @@ export const COLORS = {
const FONT = "'Inter', 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif"
const MONO_FONT = "'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace"
/**
* Brand wordmark: `Compl` rendered in the inherited text color, `AI` in violet.
* No slashes, no separators — matches the BreakPilot/ComplAI brand guideline.
* Use this anywhere the product name appears in body or display text.
*
* Use the optional `prefix` to prepend "BreakPilot " in the same style.
*/
export function ComplAI({ prefix = false }: { prefix?: boolean } = {}) {
return (
<>
{prefix && <>BreakPilot </>}
Compl<span style={{ color: COLORS.violet600 }}>AI</span>
</>
)
}
/* ===== PAGE WRAPPER ===== */
interface PageProps {
kicker: string // "03"
section: string // "DAS PROBLEM"
title: string // "Deutsche Unternehmen wollen KI ..."
subtitle?: string
title: React.ReactNode // string or JSX (e.g. <ComplAI prefix /> usage)
subtitle?: React.ReactNode
pageNum: number
totalPages: number
versionName: string
@@ -236,12 +252,23 @@ export function Bullets({ items, dense, tone = 'neutral' }: BulletsProps) {
: tone === 'negative' ? COLORS.red600
: tone === 'accent' ? COLORS.indigo600
: COLORS.slate500
/**
* Em-dash marker uses display:flex to keep the rule vertically centered on
* the first line of text — previously the `top: 4pt` absolute positioning
* drifted relative to font size (looked off-center on the rendered PDF).
* The marker now sits in a fixed-width column at the line height of the text.
*/
const fontSize = dense ? '8.5pt' : '9pt'
const lineH = 1.5
return (
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
{items.map((item, i) => (
<li key={i} style={{ position: 'relative', paddingLeft: '5mm', marginBottom: dense ? '1.5mm' : '2.5mm', fontSize: dense ? '8.5pt' : '9pt', color: COLORS.slate700, lineHeight: 1.5 }}>
<span style={{ position: 'absolute', left: 0, top: dense ? '4pt' : '4.5pt', width: '3mm', height: '0.5pt', background: dotColor }} />
{item}
<li key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: '3mm', marginBottom: dense ? '1.5mm' : '2.5mm', fontSize, color: COLORS.slate700, lineHeight: lineH }}>
{/* The dash sits on the first line: line-height pad above + 0.5pt rule */}
<span style={{ flexShrink: 0, width: '3mm', display: 'inline-flex', alignItems: 'center', height: `${parseFloat(fontSize) * lineH}pt` }}>
<span style={{ width: '100%', height: '0.5pt', background: dotColor, WebkitPrintColorAdjust: 'exact', printColorAdjust: 'exact' }} />
</span>
<span style={{ flex: 1, minWidth: 0 }}>{item}</span>
</li>
))}
</ul>
@@ -238,7 +238,7 @@ function formatFunding(amount: number): string {
export function PrintTheAskPage({ funding, lang, pageNum, totalPages, versionName }: SlideBase & { funding: PitchFunding }) {
const de = lang === 'de'
const amount = funding?.amount_eur || 1_000_000
const amount = funding?.amount_eur || 400_000
const instrument = funding?.instrument || (de ? 'Wandeldarlehen' : 'Convertible Loan')
const isConvertible = (instrument || '').toLowerCase().includes('wandeldarlehen') ||
(instrument || '').toLowerCase().includes('convertible') ||