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>
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>
Investors arriving at slide 16 (Customer Savings) currently jump straight
into annex-strategy without any chapter break — they don't know the main
pitch has ended and the appendix has started.
Adds PrintAnnexDividerPage that sits between customer-savings and
annex-strategy. Layout:
Part II · Anhang BreakPilot · ComplAI
─────────────────────────────────────────────────────────
16 · Kapitelwechsel
Anhang. (giant violet-dotted title, 74pt)
────────
Detail & Belege. (15pt lead)
Auf den folgenden Seiten
17 GTM Strategie 20 Reg. Details 23 KI-Pipeline
18 Finanzplan 21 Architektur 24 Risiken
19 Treibervariablen 22 Engineering 25 Glossar
─────────────────────────────────────────────────────────
BREAKPILOT · COMPLAI WANDELDARLEHEN 16 / 30
Uses .print-page-bg so the violet-tinted dotted background reads as the
same chapter as the rest of the deck. Footer matches the standard Page
primitive.
BASE_PAGES bumped 29 → 30. Bilingual (DE/EN).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The pricing slide previously rendered as a 4-column DataTable buried below
unit economics — the 3 tiers were hard to find. Rebuilt as the Claude Design
PREISE pattern: three prominent product cards side by side.
Each card:
- Mono tier label kicker (STARTER / PROFESSIONAL / ENTERPRISE) at top
- Target audience line ("<25 Mitarbeiter · Basis-Module" etc.)
- Hero price (€3.600 / €18.000 / ab €50.000) + /Jahr unit
- 4–5 feature checkmarks (green ✓)
- Tinted background per tier: violet-50 for Starter, white-gradient for
featured Professional, amber-50 for Enterprise
Professional card carries:
- 2px violet border (vs 1px on others)
- Drop shadow
- "BELIEBT" / "POPULAR" pill badge floating above its top edge in violet
Below the 3 cards, a compact 2-col footer:
- left: 4 Unit Economics tiles (~70% gross margin, ~3.5× LTV/CAC, etc.)
- right: emerald net-effect callout (+€30k per SME / yr)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Infrastructure layer was being cut off (only the chip showed; the 3 inference
service cards never rendered). Root cause: each tier was double-wrapped — an
outer tinted layer card AND inner bordered FlowNode cards — which inflated
the total height past A4 landscape.
Replaces inner FlowNode (border + padding + footer rule) with a new flat
ServiceNode used only inside the tinted layer wrappers:
- no own border / no own padding
- title 11pt → 10pt, kicker 7pt → 6pt
- caps inner items to 4 max
- mono tech footer in 6pt with hairline separator
Also tightened the connectors between tiers: was a 12mm row of three VArrow
SVGs each with its own padding, now a 5mm row of three compact down-arrow
SVGs. Saves ~14mm of vertical space.
Layer chip sizing reduced (7.5pt → 7pt, padding 1.5mm → 1mm) so each chip
takes less of its layer card.
Result: all three layers fit on one A4 landscape page with the LLM
Inference / Embeddings / AI Tools cards visible.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Five fixes per user review:
1. USP p1 overflow (stats were getting clipped). Tightened card spacing:
- icon tile 12mm → 9mm, moved inline next to title
- mono kicker for "SÄULE · COMPLIANCE" tags
- reduced paddings, title 13pt → 12pt, body 8.5pt → 8pt
- violet replaces indigo (already by alias, but explicit here)
2. USP p2 closing loop: was a plain tinted callout, now a 2-col hero panel
- left: violet circle around ∞, mono "DIE SCHLEIFE · ALWAYS IN SYNC",
bold headline (14pt), body
- right: white card containing the LoopDiagram with violet outline
- gradient violet→white→violet background for the panel
3. How It Works: replaced the floating-arrow StepStrip with a real
horizontal-rail timeline:
- Violet gradient connector line behind 4 numbered circles
- Each circle is a 14mm violet disc with the step number
- Title + body below each circle
Replaced the Time-to-Value callout with a dotted-rail timeline:
- 5 day markers (Tag 0/3/7/14/30) as violet pill chips on a dashed rail
- Stop label below each
- Mono header reads "Time-to-Value · Median 14 Tage · Worst Case 28 Tage"
4. Assumptions slide:
- "Skalare Annahmen" → "Treibervariablen des Finanzplans" (plain language)
- subtitle rewritten to explain the three-scenario sensitivity setup
instead of referencing internal fp_assumptions tables
- each category now a violet-bordered card with mono kicker + variable
count, italic instead of bare table
- sensitivity callout expanded with concrete runway impact numbers
5. Architecture diagram: layer chips per Claude Design pattern.
- Each tier wrapped in a tinted rounded card (violet for product +
inference, amber for gateway)
- "01 · APPLICATION LAYER" mono pill with italic sub-label
("User-facing services") next to it
- Gateway layer carries the LiteLLM Proxy title inline with subtitle
- Connector arrows kept between layers
Also fixes "Kleinstunternehmen" → "Kleinunternehmen" typo in solution
pillar 03 and the product pricing-logic callout.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adapts the visual language from the Claude Design reference (light theme) while
preserving our left-rule Page header and split-block cover.
Color palette: indigo (#4f46e5) → violet (#7c3aed) as primary accent across all
slides. COLORS.indigo* aliases kept so the existing 9 slide files inherit the
new palette without edits. New explicit COLORS.violet50..900 names available
for future code.
Body text shifted from pure slate to deep purple-tinted (#1a0f34) per Claude
tokens.fg.
Typography:
- Body / headings: Inter (was Plus Jakarta Sans)
- Mono utility: JetBrains Mono — applied to kicker tags, page numbers, footer,
the "At a glance" stat block on the cover, and the cover key-term labels
- Mono class .print-mono added to print.css
Background:
- New .print-page-bg utility paints a violet-tinted radial gradient
(white → #f5efff → #ebdfff) with a subtle 24px dotted grid SVG overlay
- Applied to every Page and the cover's right pane
Page chrome:
- Kicker label switched to JetBrains Mono with wider letter-spacing (0.18em)
- Right-of-kicker rule fades violet→transparent (was flat slate)
- New 2px violet gradient bar (700→400→700) below the title/subtitle —
the Claude Design "purple bar" accent, scaled down for print
- Footer restyled: mono caps "BREAKPILOT · COMPLAI" left, version (violet) middle,
page number right
Cover:
- Left block now a violet vertical gradient (was flat indigo)
- All small labels ("Investor Brief", "Auf einen Blick", "Confidential",
"Key Terms", and the term labels) restyled to JetBrains Mono with wider tracking
- Right pane carries the violet-tinted dotted bg, matching the rest of the deck
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per user review of the rendered PDF.
Problem: empty bottom-third on each card → added a bottom stat block per
column showing 3 pulled-out data points (e.g. "64% · 70% · 83%") with red
hero numerals. Description text trimmed since the stats now carry the punch.
Solution: pillar bodies were short, leaving large gaps between description
and the green stat at the bottom. Added 5 detail bullets per pillar (specific
tools, frameworks, behaviours) in the previously empty middle. Stat at the
bottom now reads as a real KPI tile, not a floating value.
Strategy: phase KPI was a tiny corner tag. Promoted it to a bottom
"Outcome" block with side-by-side 14pt numerals matching the phase tone
(2 Kunden / ARR €40k etc.). The bullets get more breathing room above.
The Ask reconciliation (was showing nonsense €4M pre / €5M post / 20%
investor share for a €200k Wandeldarlehen): detect convertible/SAFE/
Wandeldarlehen and swap the tiles to Funding / Discount / Maturity /
INVEST-grant. Equity rounds compute Pre/Post from amount × 20% assumed
investor share. Same conditional applied to the cover key-terms grid.
Pricing label "Was der Kunde zahlt vs. spart (KMU 50 MA, Jahr 1)" was
wrapping "1)" onto its own line — switched to a slash-separated form
("Kunde zahlt vs. spart · KMU 50 MA · Jahr 1") that fits on one line.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three critical fixes after reviewing the rendered PDF:
Cover (was: indigo block collapsed to top, white content stacked below):
- The .print-page class in print.css forces flex-direction: column !important,
which broke the horizontal split. Wrap the cover content in a single grid
container — the column-flex parent then has only one child so direction is
irrelevant. Indigo block now runs full-height on the left.
- Title reduced 88pt -> 60pt so "BreakPilot ComplAI." fits without wrapping.
- Funding amount formatter now handles sub-€1M cases (€200k vs €0.2M).
Finanzplan (was: "nicht verfügbar" on both pages 20-21):
- page.tsx was querying the legacy pitch_fm_results table which isn't populated
by the current pipeline. The interactive deck reads from fp_* tables.
- Wire in lib/finanzplan/adapter.ts (finanzplanToFMResults) which bridges the
live fp_* tables to FMResult[] — same source the interactive deck uses.
- Fall back to live default fp_scenario if the version snapshot's fm_scenarios
is empty.
- adapter.ts: populate total_customers + new_customers from fp_kunden_summary
(was hardcoded 0).
The Ask:
- target_date was rendering as raw ISO timestamp "2026-08-01T00:00:00.000Z";
now formatted as "Aug 2026" (locale-aware).
- Hero funding amount uses same sub-€1M formatter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two bug fixes plus the requested visual rework — the deck now looks like a pitch deck, not a research paper.
Bugs:
- BASE_PAGES corrected from 28 to 29; disclaimer no longer shows "29/28"
- fmResults + fmAssumptions now load for the standard PDF, not only when financial=true; Finanzplan annex + KPI dashboard now render
Visual rework (per user: "graphic elements, not just text"):
- Cover: split layout — indigo block left (tagline + hero stats + version meta), white block right with oversized title and key terms
- Modules: 12 lucide icons in indigo-50 tiles (ScanLine, ShieldCheck, FileText, ClipboardCheck, Users, UserCheck, AlertTriangle, Brain, Target, GraduationCap, TrendingUp, MessageSquare)
- USP cards: icon-led card heads with FileSearch/ArrowLeftRight/Repeat/Layers/etc.; LoopDiagram SVG on the closing "Compliance ↔ Code" hub
- How It Works: StepStrip primitive with visible right-arrows between steps
- Market: nested-rectangle MarketFunnel (TAM > SAM > SOM) replaces three stacked boxes
- Customer Savings: 4 hero KPIs + ComparisonBars (today vs. with BP) per cost item
- The Ask: DonutChart for use-of-funds
- Cap Table: DonutChart for equity distribution
- Finanzplan p2: 2×2 chart grid — Revenue (bars), EBIT (bars, tone by sign), Cash balance (line+area), Headcount (bars)
- Architecture: ArchitectureDiagram primitive (3 tiers, vertical arrows between tiers)
- AI Pipeline: PipelineFlow primitive (4 stages, horizontal arrows)
- Team: founder photos (32×32mm) added; falls back to initials if photo_url missing
New primitives:
- PrintCharts.tsx — BarChart, LineChart, ComparisonBars, DonutChart, ProgressBar, MarketFunnel
- PrintDiagrams.tsx — FlowNode, VArrow, HArrow, StepStrip, ArchitectureDiagram, LoopDiagram, PipelineFlow
All files under 500 LOC cap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Throws away the screen-deck-derived print system. Builds a new institutional-research aesthetic:
- 12-col grid on A4 landscape, hairline rules, no colored bars, no icons
- 3-color discipline: indigo (structural), emerald (positive), red (problem)
- Plus Jakarta Sans 800 for hero numerals + titles; tabular numerals everywhere
- 1-to-1 content parity with the interactive deck: full USP (8 cards), full competition matrix (45 features, 12 AppSec features, 8+6 competitor profiles), Finanzplan P&L grid + KPI dashboard, full glossary
- 2-page slides where content demands (Exec Summary, USP, Competition, Finanzplan)
- 28 base pages; +1 for Financial detail; +1 for Cap Table (suppressed on Wandeldarlehen)
Files:
- New: PrintIntroSlides, PrintProductSlides, PrintMarketSlides, PrintCompetitionSlides
- Rewritten: PrintLayout (new primitives Page/KpiRow/TwoCol/ThreeCol/DataTable/MatrixGlyph/Callout), PrintAnnexSlides, PrintFinancialSlides, PrintDeck
- Removed: PrintCoreSlides.tsx, PrintExtraSlides.tsx (obsolete)
- print.css now sets Plus Jakarta Sans as the print font family
- All files under 500 LOC cap
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Clean-Room derivation of 195 controls from BSI QUAIDAL (10 criteria + 15
building blocks + 30 measures + 140 metrics) for EU AI Act Art. 10
training-data quality compliance.
- ingest_bsi_quaidal.py parses YAML frontmatter into a structural index
(no protected prose stored on disk).
- derive_quaidal_mcs.py rewrites each entry via local LLM (qwen3.5:35b-a3b)
with a hard 4-gram plagiarism gate < 20%; achieved mean overlap 0.5%.
- Migration 011 adds compliance.derived_controls table with full source
provenance (framework, section, url, commit SHA, license note).
- apply_quaidal_to_db.py UPSERTs YAML into DB.
- Source repo (legal-sources/bsi-quaidal/) gitignored.
Same pattern as IACE module DIN-reference handling: name the norm and
section, never quote.
Backed by BSI license clarification 2026-05: § 5 UrhG anwendbar,
share:true im Frontmatter; Clean-Room derivation is the safe path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Marketing-Website
- Neue SavingsSection auf Homepage: "Compliance entdeckt sechsstellige
Einsparungen". Pitch-Position der Cookie-Audit-Cost-Optimization-Story
fuer DAX-Konzern-Sales (BMW-Case-Style: 90 Vendors -> 25 nach
Konsolidierung, EUR 500k-3M / Jahr).
- /savings-scan: Kostenloser 5-Min-Saving-Scan-Form (URL + E-Mail).
Form-Submit ist Placeholder, soll an Compliance-Backend gehaengt werden.
- /savings-methodik: 4-Stufen-Erklaerung der Cookie-Tier-Inferenz +
ehrliche Caveats (Listpreise != Vertragspreise, Media-Spend nicht
enthalten) + Datenquellen.
- Content-de + Content-en in content.ts beide um savings-Block ergaenzt
und Section-Numerierung angepasst (03=Savings, 04=Deterministic).
- LOC-Split: savings-Inhalte (DE+EN, ~100 LOC) in content.savings.ts
ausgelagert damit content.ts unter 500-LOC-Hard-Cap bleibt.
Control-Pipeline
- LESSONS-LEARNED-mc-check-types.md fuer die parallele CRA-MC-Generation.
Erklaert die TEXT/PROCESS/REVIEW-Klassifikation die im Compliance-Repo
retrofitted wurde. Verhindert dass CRA-MCs denselben Defekt bekommen.
Mapping-Heuristik fuer verification_method -> check_type, plus
Backfill-Workflow fuer ~62 ambiguous Eintraege.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
globals.css sets html,body { height:100%; overflow:hidden; background:#0a0a1a }
with no media query. In print mode this clips all slides to one viewport
height (explaining the 2-page limit) and renders a black background.
Override with height:auto, overflow:visible, background:white in @media print.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Firefox doesn't honor height on flex containers in print mode — the
container collapses to content height, causing all slides to fit on 2
pages. Moved the authoritative height to the display:block wrapper
(.print-page-break) and changed .print-page to height:100% so it
fills its reliably-sized block parent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
page: slide-page on each block wrapper forces Firefox to allocate a new
physical page per slide — the spec-correct approach. break-before: page
is belt-and-suspenders. Switched from break-after to break-before via
adjacent sibling selector to avoid a blank trailing page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-moz-print-color-adjust: exact ensures Firefox doesn't strip background
colors from headers, badges, and accent elements when printing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chrome's print engine silently ignores break-after/page-break-after on
flex containers. Wrapping each .print-page (flex) in a plain block
.print-page-break element gives Chrome a reliable page break anchor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ConsentBanner: save consent_id to localStorage after successful POST
- Footer: DELETE /api/consent/{id} on consent re-open (Art. 17 DSGVO)
- New proxy route: DELETE /api/consent/[id] → backend withdrawal endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Inline <style> tags in React body are unreliable for @media print in
Chrome. Move all print CSS to app/pitch-print/print.css imported via
a layout.tsx — Next.js injects this as a proper <link> in <head>,
which is guaranteed to be applied before print rendering.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ScriptManager: two blocking mechanisms — injection of CONSENT_SCRIPTS
after consent + activation of type="text/plain" data-consent scripts.
Standard CMP blocking pattern ready for third-party analytics/marketing.
DSE: add Interessenabwaegung (balancing test) for Art. 6(1)(f) DSGVO
processing: Hosting and Server-Logfiles sections now document why
legitimate interest outweighs data subject rights.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Session ID via sessionStorage UUID
- Self-host Google Fonts (Inter, Plus Jakarta Sans, JetBrains Mono) — eliminates
third-party transfer to Google, no more DSGVO violation
- ScriptManager component: consent-change listener for future analytics/marketing scripts
- GeoIP via browser timezone (Intl.DateTimeFormat) + IP injection in proxy
- Vendor-level consent UI: loads vendor config from backend, shows per-vendor
toggles under each category, sends vendor_consents dict
- DSE updated: Google Fonts section now says "lokal gehostet"
- Config proxy route: GET /api/consent/config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Switch from inline pageBreakAfter to CSS class `.print-page` with
explicit `page-break-after: always !important` so Chrome print
preview creates a new page per slide (was collapsing to 2 pages)
- Remove margin/box-shadow in @media print so A4 boundaries align
- Content areas now use flex:1 so cards/pillars stretch to fill the
full page height (no more blank void below content)
- Remove conditional rendering on data-dependent slides — always
render all 9 core pages
- Larger font sizes throughout (11px body, 13px card titles)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds /pitch-print/[versionId] — a server-rendered, print-CSS-optimized
page that generates investor-ready PDFs via the browser's native print
dialog (Save as PDF). Two variants per version:
- Standard PDF (9 pages): Cover, Problem, Solution, Products, Market,
Team, Milestones, The Ask
- Financial PDF (+4 pages): adds Financials P&L table (aggregated from
pitch_fm_results), Assumptions, Cap Table, Legal Disclaimer
White background with indigo accents, A4 landscape via @page CSS, all
color-rendered in print via print-color-adjust: exact. Auto-triggers
window.print() 900ms after load. Admin toolbar visible on screen only.
Export buttons added to /pitch-admin/versions/[id] detail page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ConsentBanner detects loaded scripts (analytics/marketing) and cookies
after consent, sends them to the CMP backend for transparency tracking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Send consent_method, page_url, referrer, device_type, browser, os,
screen_resolution and consent_scope with each consent record for
vendor-agnostic compliance tracking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 20 manually verified golden controls with expected MC topics
- Structural quality tests: min 10K MCs, max 300/MC, no orphans
- Doc-check controls tests: 8 doc types covered, no empty questions
- Quality thresholds: 90% accuracy, enforced by regression tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OSHA 29 CFR 1910 Subpart O (1910.211-1910.219) — complete machine
guarding requirements. US federal law, public domain.
International norms mapping table: China GB/T, Korea KS, India BIS
equivalents to ISO/EN standards. Unfortunately all countries protect
ISO copyright even for identical national adoptions (IDT).
Only OSHA provides truly free machinery safety content.
EU Excel harmonised standards list included for reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email security gateways follow GET redirects automatically and were
consuming the token before the investor clicked through. The verify page
now shows an 'Access Pitch Deck' button; the token is only consumed on
explicit click, which scanners cannot trigger.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Behind Orca's reverse proxy, request.url resolves to http://127.0.0.1:3000
which causes redirects to go to the internal address instead of the public
domain. Use PITCH_BASE_URL (already set in service.toml) as the base.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>