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>
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>
- New pitch_short_links table stores 6-char alphanumeric codes mapped to magic link tokens
- GET /p/[code] redirects to /auth/verify?token=... (302, validates expiry)
- All magic link generation points (invite, generate-link, resend) now create a short code
- Emails (invite + resend) use the short URL — less token-like, cleaner for spam filters
- Copy-link UI shows short URL prominently with full URL as fallback
- Migration 008 added to /api/admin/migrate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ROW_LABEL_MAP (DE→EN) covering GuV, Liquidität, Kunden, Betriebliche Aufwendungen rows
- Add FORMULA_TOOLTIPS_EN with English tooltip text for all formula-driven rows
- Add MONTH_LABELS_EN (Mrz→Mar, Mai→May, Okt→Oct)
- LabelWithTooltip now accepts `de` flag, translates display text and tooltip accordingly
- Month column headers switch between DE/EN month abbreviations
- Falls back to original German label for any row not in the map
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/admin/investors/:id now returns preferred_lang
- PATCH /api/admin/investors/:id accepts preferred_lang (de/en), validates value
- Investor detail page: DE/EN toggle in the Pitch Version card, instant save on click
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Version dropdown on the invite form shows all committed versions
- Selected version is assigned to the investor at creation time (no separate step needed)
- API validates version is committed before upserting
- Leaving the dropdown empty keeps any existing assignment (COALESCE behavior)
- version_id included in audit log
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add English email template variants (greeting, message, closing, subject, CTA copy)
- Add `preferred_lang` column to `pitch_investors` — stored per investor, deck opens in that language by default
- Invite form: DE/EN language toggle that switches email defaults and pitch language setting
- Invite form: "Send email" toggle — when off, creates investor + returns magic link without sending email (for cold outreach attachment)
- `app/page.tsx`: initializes pitch language from investor's `preferred_lang` before first render (no flash)
- Migration 007 added to `/api/admin/migrate` route for production rollout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Splits master controls >200 members by re-clustering their object groups
with k=4-20 per group. First round: 38 groups → 325 sub-groups → 253 new MCs.
25 generic MCs remain (monitoring, procedure, etc.) — need regulation-source split.
Session summary: Block F complete, Control Generation (1,599+), Pass 0a/0b,
Production Sync, G-pre1/2/3 Object Clustering + Master Controls + API,
G1-G4 Compliance Execution Layer (Decision Trace, Commit Ledger, Decision Memory,
Pre-Deployment Enforcement).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: deployment_checks (verdict, blocking/warning controls, risk score)
New API:
POST /v1/deployment-checks (SDK asks: "can I deploy?")
GET /v1/deployment-checks/{id} (check result)
POST /v1/deployment-checks/{id}/override (manual override with justification)
GET /v1/deployment-checks/stats (approval/block rate)
Check logic: queries G1 decision_traces + G3 open failures per affected control.
Verdict: approved (0 blocking) or blocked (with fix recommendations).
454 tests pass, 0 regressions.
Block G complete: G1-G4 all implemented.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: decision_events (assessment→decision→fix→verification→failure cycle)
New API:
POST /v1/decision-events (record lifecycle event)
GET /v1/decision-events (list with filters)
GET /v1/decision-events/timeline/{control_id} (full chronological timeline)
GET /v1/decision-events/stats (failure rate, cycle times)
Each event captures input_state, output_state, actor, evidence.
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: compliance_commits (commit hash, affected controls, risk level)
New API:
POST /v1/compliance-commits (SDK registers commit + impact)
GET /v1/compliance-commits (list with filters)
GET /v1/compliance-commits/by-control/{id} (all commits for a control)
GET /v1/compliance-commits/stats (dashboard)
GET /v1/compliance-commits/{id} (detail)
GIN index on affected_control_ids for fast @> containment queries.
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New table: decision_traces (status, reason, evidence, fix plan per control)
New API:
POST/GET/PUT /v1/decision-traces (CRUD for decisions)
GET /v1/decision-traces/stats (compliance dashboard)
GET /v1/controls/{id}/full-trace (Regulation→Obligation→Control→Decision→Evidence)
454 tests pass, 0 regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
G-pre1: 144k objects clustered into 7,466 groups via Mini-Batch K-Means
on bge-m3 embeddings. Two-stage: k=5000 base + sub-cluster groups >50.
G-pre2: 5,114 Master Controls from lifecycle phase chains
(define→implement→test→monitor), linking 172,504 atomic controls.
G-pre3: REST API for Master Controls
GET /v1/master-controls (list, search, filter)
GET /v1/master-controls/stats
GET /v1/master-controls/{mc_id} (detail with phase-controls)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8 tests confirm all REGULATION_LICENSE_MAP, ACTION_TYPES, _NEGATIVE_PATTERNS,
_ACTION_SYNONYMS, and _OBJECT_SYNONYMS entries are correctly migrated to DB.
Dicts kept as fallback for DB-unavailability resilience.
Block F complete: F1-F5 all done.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uses Ollama (qwen3.5:35b-a3b, think:false) to generate additional
German synonyms for action types and object tokens. Results stored
with source='llm' in action_synonyms/object_synonyms tables.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: init scripts ran repeatedly (on container restart) and tried
vault secrets enable / vault auth enable for already-existing paths.
Vault logged ERRORs and burned 40-84% CPU in the loop.
Fix:
- Marker file /vault/data/.init-complete skips re-initialization
- vault secrets list / vault auth list checks before enable calls
- No more "path already in use" errors on subsequent runs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The preview-data API was returning `fm_scenarios` but PitchDeck reads
`data.fp_scenarios`, so fpBaseScenarioId was always null and the
Finanzplan slide fell back to the global default scenario (Base Case 200k)
instead of the version's assigned scenario (e.g. 1 Mio. Euro Base).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Registers /generate/run-pass0a and /generate/pass0a-status/{job_id}
on the core control-pipeline (port 8098). Previously Pass 0a was only
available on the compliance backend which connects to Production DB,
causing a split-brain when controls are generated locally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI Q&A: fetch is_showcase from DB; showcase sessions receive no financial/funding
context and have an explicit LLM guard refusing to discuss investment details.
FAQ context and financial slide IDs stripped from system prompt.
FAB: flex layout so Fullscreen button is always visible regardless of panel height.
Presenter: pass activeSlideOrder to usePresenterMode so buildSlideAudioPlan maps
slideIdx → slideId from the filtered list, not the full SLIDE_ORDER. Progress
calculation also filters to active scripts only.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NavigationFAB and SlideOverview now accept slideNames prop and render only the
active slide list (filtered for showcase mode). Adds AI presenter start button
to the FAB footer so it's accessible even when intro-presenter slide is hidden.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds is_showcase boolean to pitch_investors; when set, filters out financials,
the ask, cap table, assumptions, finanzplan, risks, and intro-presenter slides.
Slide navigation is fully dynamic — progress bar and counts update accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>