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
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:
@@ -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') ||
|
||||
|
||||
Reference in New Issue
Block a user