Pitch Deck Styling Overhaul: Complete Restart Prompt
Context
Solanasis is a cybersecurity verification firm targeting wealth management (RIAs, estate planning attorneys, family offices). This pitch deck is a client-facing sales asset handed to managing partners and compliance officers at firms managing generational wealth. It must look like it came from a firm that belongs in the same conversation as Kroll, Visory, and Agio.
The generator script (solanasis-scripts/generate-pitch-deck.py, 1,535 lines, python-pptx) produces an 11-slide 16:9 PPTX. The text content is correct and must NOT be rewritten. The visual output has persistent styling failures that have survived multiple revision attempts. This prompt documents every issue and provides a binding styling spec.
Previous rebuild prompt: solanasis-docs/sales/pitch-deck-rebuild-prompt.md (still useful for content reference and deploy steps, but THIS prompt supersedes it for styling rules)
Your Mission
Fix the visual styling of generate-pitch-deck.py so every slide follows a consistent, professional styling spec. Do NOT rewrite text content. Focus entirely on layout, spacing, alignment, margins, font sizes, contrast, and visual consistency.
Then: generate, convert to PDF, render every slide to PNG at 300 DPI, visually verify every single element at zoom, deploy, and push.
Step 1: Read Everything FIRST
Before writing any code:
- Read this prompt completely (you’re reading it now)
- Read the current script (
solanasis-scripts/generate-pitch-deck.py) end to end - Read the previous rebuild prompt (
solanasis-docs/sales/pitch-deck-rebuild-prompt.md) for content spec and deploy steps - Generate the current output and render to PNGs:
cd solanasis-scripts && python generate-pitch-deck.py "C:/Program Files/LibreOffice/program/soffice.exe" --headless --convert-to pdf ../Solanasis_Pitch_Deck.pptx --outdir ..import fitz doc = fitz.open('../Solanasis_Pitch_Deck.pdf') for i, page in enumerate(doc): pix = page.get_pixmap(dpi=300) pix.save(f'../../_screenshots/2026-03-20/pitch-deck-slide-{i+1:02d}.png') - Read each PNG with the Read tool (multimodal) and verify you can see every issue listed below
- Create a plan before writing any code
BINDING STYLING SPEC
These are the rules. Every slide must follow them. When in doubt, refer back to this section.
A. Spacing Constants (define at top of script)
# Spacing constants (consistent across ALL slides)
TITLE_MARGIN_TOP = Inches(0.6) # Top of slide to top of title
TITLE_TO_SUBTITLE = Inches(0.15) # Title bottom to subtitle top
SUBTITLE_TO_CONTENT = Inches(0.25) # Subtitle bottom to first content element
CARD_INTERNAL_PAD_TOP = Inches(0.15) # Inside card: top edge to first text
CARD_INTERNAL_PAD_LR = Inches(0.2) # Inside card: left and right padding
CARD_INTERNAL_PAD_BOT = Inches(0.12) # Inside card: bottom edge to last text
CARD_HEADING_SPACE_AFTER = Pt(8) # Inside card: heading to body gap
CARD_BODY_LINE_SPACING = 1.5 # Inside card: body text line spacing
TITLE_LINE_SPACING = 1.15 # Slide titles line spacing
BODY_LINE_SPACING = 1.5 # General body text line spacing
ACCENT_RULE_TO_TEXT = Inches(0.15) # Accent rule to text below it
CONTENT_TO_FOOTER = Inches(0.3) # Last content to footer/closingB. Line Spacing Rules
The 1.8 line spacing currently used everywhere is too generous. It makes text look disjointed, pushes content down, and wastes vertical space. Fix:
| Element | Line Spacing | Why |
|---|---|---|
| Slide titles (Playfair 36pt) | 1.15 | Tight; large text needs less spacing |
| Card headings (14-16pt bold) | 1.15 | Tight; headings are short |
| Card body text (11-13pt) | 1.5 | Comfortable reading density |
| Bullet lists | 1.4 | Slightly tighter than body |
| Footer/footnote text | 1.2 | Compact |
| Italic subtitles | 1.4 | Slightly open |
| CTA card text | 1.5 | Readable |
The add_paragraph() helper currently hardcodes p.line_spacing = 1.8. Change this to accept a parameter with a sensible default of 1.5.
The add_card() helper currently hardcodes p.line_spacing = 1.8 on both title and body. Change title to 1.15, body to 1.5.
C. Card Heading Alignment Rules
THE #1 VISUAL PROBLEM: Card headings are not vertically consistent across cards on the same slide. When one card has a 1-line heading and another has a 2-line heading, the body text starts at different Y positions, making the slide look sloppy.
Fix approach: anchor ALL card text to the TOP and accept whitespace at the bottom. Do NOT try to vertically center. Text should always start at CARD_INTERNAL_PAD_TOP from the top edge of the card. The MSO_ANCHOR.TOP anchor must be set on every card’s text frame body properties.
For cards where headings are different lengths (e.g., “Solanasis” vs “Your Compliance Consultant”):
- Do NOT use
\nmanual line breaks in heading text to force wrapping. Let it wrap naturally. - On slide 4 specifically: change “Your Compliance\nConsultant” to “Your Compliance Consultant” (one line, let it wrap), and change “Your IT Provider\n/ MSP” to “Your IT Provider / MSP” (one line, let it wrap). The
\ncreates ugly indented line breaks in centered text. - Allocate enough card height that the tallest heading + longest body text fits with padding.
D. Centered Text Rules
Centered text in cards is only appropriate when ALL cards on the slide use very short, similar-length content. Currently slide 4 uses centered text for items like “Regulatory strategy,\nexam prep, Form ADV” which renders as 3 short centered lines with ragged edges. This looks amateur.
Rules:
- Slide 4 (three-column positioning): Keep centered alignment, but remove all
\nfrom data strings. Let the text wrap naturally. This creates a cleaner paragraph flow. - Slide 8 (three paths): Centered is OK here; content is short and similar across cards.
- All other cards: LEFT alignment. Do not center paragraph text in cards unless all cards on the slide have similar, short content.
E. Footer/Contact Contrast Rules
Footer text is STILL INVISIBLE on both dark slides. This has been a persistent issue across multiple revisions.
Slide 1 (Cover):
- Background is a gradient: NAVY (#020532) → DEEP_NAVY (#091652) → COPPER (#C47A3D)
- At
y = Inches(6.5), the gradient is transitioning toward copper - PARCHMENT (#FEF9F1) on copper background = low contrast
- Fix: Use WHITE (#FFFFFF) for the footer text, and increase font size to Pt(12). White on any part of the navy-to-copper gradient has sufficient contrast.
Slide 11 (CTA):
- Background is solid NAVY (#020532)
- COPPER_HOVER (#D4945E) should theoretically have 5.5:1 contrast on navy, but in the rendered PDF it appears barely visible
- Fix: Use PARCHMENT (#FEF9F1) for the footer text (high contrast on navy), increase to Pt(12), and make the contact info links COPPER (#C47A3D) instead of COPPER_HOVER for stronger visibility. Actually, simplest fix: make ALL footer text WHITE. White on navy = maximum contrast.
Both slides: The footer is also too small at Pt(11). Increase to Pt(12) minimum. A 13.333” wide slide displayed on a screen/projector needs legible footer text.
F. Font Size Minimums
No text on any slide should be smaller than Pt(11). This is a presentation, not a document.
| Element | Minimum Size | Current Issues |
|---|---|---|
| Slide titles | Pt(34-38) | OK |
| Subtitles | Pt(14-16) | OK |
| Card headings | Pt(14) | Slide 9 uses Pt(13); increase to Pt(14) |
| Card body text | Pt(11) | OK |
| Bullet items | Pt(12) | OK |
| Footnotes | Pt(11) | OK |
| Footer/contact | Pt(12) | Currently Pt(11); increase |
| Badge/pill text | Pt(9-10) | OK (badges are meant to be compact) |
G. Services Grid (Slide 9) Specific Fixes
The 3x2 grid is too cramped and text is tiny. Fixes:
- Increase
card_heightfromInches(2.0)toInches(2.2) - Increase
title_sizefromPt(13)toPt(14) - Increase
body_sizefromPt(11)toPt(12) - The body text is run-on sentences (“Gap analysis mapped to your regulatory requirements. Real disaster recovery restore test. Prioritized risk register with evidence.”). Change to use line breaks between items or use bullet points. Format as a short list, not a paragraph.
- Reduce
line_spacingin these cards to 1.4 to fit the slightly larger text
H. estimate_text_height() Accuracy
The current formula uses font_size_pt * 0.55 as the character width estimate. This is approximately correct for Montserrat but too narrow for Playfair Display. For Playfair Display titles at 36pt, use 0.52 (it’s a wider display font). The function should accept an optional char_width_factor parameter:
def estimate_text_height(text, font_size_pt, box_width_inches,
line_spacing=1.5, char_width_factor=0.55):I. Accent Rule Consistency
Accent rules (copper horizontal lines) vary in width across slides: Inches(3), Inches(4), sometimes centered, sometimes left-aligned. Standardize:
| Location | Width | Alignment |
|---|---|---|
| Cover slide (below logo) | Inches(4) | Left (at MARGIN_LEFT) |
| Before closing lines | Inches(3) | Left (at MARGIN_LEFT) |
| CTA slide (below title) | Inches(3) | Left (at MARGIN_LEFT) |
All accent rules should be Pt(3) height, COPPER color.
J. set_shrink_autofit() Usage
LibreOffice ignores shrink-to-fit. It only works in PowerPoint. Currently it’s applied to nearly everything as a safety net. This is fine to keep as a fallback, but do NOT rely on it. Every text element must be sized to fit without shrinking.
After making changes, verify in the rendered PDF (via LibreOffice) that no text is clipped. If text is clipped in the PDF, the box is too small; fix the box, don’t add more shrink-to-fit calls.
K. Vertical Spacing Pattern (Cursor-Based Layout)
Every slide builder should follow this pattern:
cursor_y = TITLE_MARGIN_TOP
# Title
title_h = max(estimate_text_height(title_text, 36, 11, 1.15, 0.52), 0.65)
add_textbox(slide, MARGIN_LEFT, cursor_y, Inches(11), Inches(title_h), ...)
cursor_y += Inches(title_h) + TITLE_TO_SUBTITLE
# Subtitle (if any)
sub_h = max(estimate_text_height(sub_text, 14, 11), 0.35)
add_textbox(slide, MARGIN_LEFT, cursor_y, Inches(11), Inches(sub_h), ...)
cursor_y += Inches(sub_h) + SUBTITLE_TO_CONTENT
# Content...Never use hardcoded Y-positions for elements that depend on content above them. The only exception is footer elements that are pinned to the bottom of the slide (e.g., Inches(6.8) for footers on a 7.5” slide).
ISSUE-BY-ISSUE FIX LIST
Issue 1: Footer text invisible (Slides 1 & 11) — CRITICAL
What you see: The bottom of slides 1 and 11 has contact info (solanasis.com, hi@solanasis.com, phone, Boulder CO) that is nearly impossible to read.
Root cause:
- Slide 1: PARCHMENT text on copper portion of gradient = insufficient contrast
- Slide 11: COPPER_HOVER text at Pt(11) on navy = technically visible but practically too faint/small
Fix (Slide 1, lines ~486-496):
# Change font_color from PARCHMENT to WHITE, font_size from Pt(11) to Pt(12)
# Apply to the add_textbox call AND all add_hyperlink_run/add_run calls in the footerFix (Slide 11, lines ~1476-1488):
# Change font_color from COPPER_HOVER to WHITE, font_size from Pt(11) to Pt(12)
# Apply to the add_textbox call AND all add_hyperlink_run/add_run calls in the footerIssue 2: Card heading vertical alignment inconsistent (Slides 2, 4, 5) — CRITICAL
What you see: On slide 4, “Solanasis” (1 line) starts at a different visual position than “Your Compliance Consultant” (wraps to 2 lines). On slide 5, “Family Offices” (1 line) starts much lower than “Estate Planning Attorneys”. Body text under headings starts at different Y positions.
Root cause: Cards use MSO_ANCHOR.TOP but the heading text itself is different lengths, so the body text flows to different positions. There are also hardcoded \n line breaks creating inconsistent wrapping.
Fix:
- Remove ALL
\nfrom card data strings (especially slide 4 column definitions, lines ~726-748):"Your Compliance\nConsultant"→"Your Compliance Consultant""Regulatory strategy,\nexam prep, Form ADV"→"Regulatory strategy, exam prep, Form ADV""Your IT Provider\n/ MSP"→"Your IT Provider / MSP""Cybersecurity verification,\ndisaster recovery testing,\nvendor risk assessment"→"Cybersecurity verification, disaster recovery testing, vendor risk assessment""Daily operations,\nhelp desk, infrastructure"→"Daily operations, help desk, infrastructure"
- Ensure every card text frame has
tf.margin_top = CARD_INTERNAL_PAD_TOP(use the constant, not varying values) - Verify that the
add_card()helper sets the anchor to TOP via the bodyPr XML
Issue 3: Slash in “Your IT Provider / MSP” causes ugly line break (Slide 4) — HIGH
What you see: ”/ MSP” appears on its own line, indented, in the centered text. The slash makes it look like a code comment, not a business term.
Root cause: The \n before ”/ MSP” in the data string forces a line break. With centered alignment, the ”/ MSP” fragment centers on its own line.
Fix: Remove the \n (covered in Issue 2). The text “Your IT Provider / MSP” will wrap naturally at word boundaries if needed, or fit on one line at Pt(16) in a 3.5” wide box (which it should — 25 chars at ~0.55 width factor = ~5.5” needed in a 3.1” usable width after padding… it will wrap, but naturally, not at the slash). Consider shortening to “Your IT Provider” if it wraps poorly, since the full “MSP” is clear from context.
Issue 4: Line spacing too generous everywhere — HIGH
What you see: Text feels “floaty” and disconnected. Headings and body text have too much vertical gap between lines, pushing content off the bottom of cards and slides.
Root cause: line_spacing = 1.8 is hardcoded in add_paragraph(), add_card(), and most inline paragraph builders. 1.8 is suitable for a document, not a presentation card.
Fix: Apply the line spacing values from Section B of the styling spec. Every p.line_spacing = 1.8 must be reviewed and changed to the appropriate value (1.15 for headings, 1.5 for body, 1.4 for bullets).
Issue 5: Services grid text too small and dense (Slide 9) — MEDIUM
What you see: The 6 service cards have tiny text (13pt headings, 11pt body) that’s hard to read. Body text is run-on sentences that blur together.
Fix: See Section G of the styling spec. Increase font sizes, increase card height, and format body text as short lines (use \n between items, or use bullet unicode chars) instead of run-on sentences.
Reformat the service body text:
# Instead of:
"Gap analysis mapped to your regulatory requirements. Real disaster recovery restore test. Prioritized risk register with evidence."
# Use:
"Gap analysis mapped to regulatory requirements\nReal disaster recovery restore test\nPrioritized risk register with evidence"Issue 6: Content density imbalance in matching cards (Slides 3, 5) — MEDIUM
What you see: On slide 5, the “Estate Planning Attorneys” card has a long paragraph + 3 badges while “Family Offices” has a short paragraph and no badges. The whitespace imbalance is jarring.
Fix: This is a content issue that’s hard to fix without changing text. Accept it and ensure:
- All cards are equal height (sized to the densest card)
- All cards anchor text to the TOP
- All cards have consistent internal padding
- The visual consistency of heading position + padding makes the density difference less noticeable
Issue 7: Inconsistent card margins across slides — MEDIUM
What you see: Different slides use different card internal padding values.
Current values (inconsistent):
add_card()helper:margin_left=0.15,margin_top=0.12- Slide 3 manual cards:
margin_left=0.2,margin_top=0.12 - Slide 4 columns:
margin_left=0.2,margin_top=0.2 - Slide 5 manual cards:
margin_left=0.2,margin_top=0.12 - Slide 11 CTA card:
margin_left=0.3,margin_top=0.25
Fix: Standardize to CARD_INTERNAL_PAD_LR = Inches(0.2) and CARD_INTERNAL_PAD_TOP = Inches(0.15) everywhere, except the CTA card on slide 11 which can keep Inches(0.25) top and Inches(0.3) sides (larger card deserves more padding).
Update the add_card() helper to use the constants:
tf.margin_left = CARD_INTERNAL_PAD_LR
tf.margin_right = CARD_INTERNAL_PAD_LR
tf.margin_top = CARD_INTERNAL_PAD_TOP
tf.margin_bottom = CARD_INTERNAL_PAD_BOTIssue 8: add_paragraph() default space_after too large — LOW
Currently space_after=Pt(10) is the default in add_paragraph(). Combined with line_spacing=1.8, this creates excessive vertical gaps. Change default to Pt(6) and reduce line_spacing to 1.5.
CODE CHANGE CHECKLIST
Work through these in order:
Phase 1: Define Constants (top of file, after brand constants)
- Add spacing constants from Section A
- Keep existing brand constants unchanged
Phase 2: Fix Helpers
-
estimate_text_height(): change defaultline_spacingfrom 1.8 to 1.5, addchar_width_factorparam -
add_textbox(): no changes needed (line_spacing not set here) -
add_paragraph(): change defaultline_spacingto 1.5, change defaultspace_afterto Pt(6) -
add_card(): change titlep.line_spacingto 1.15, bodyp.line_spacingto 1.5, use padding constants, changespace_aftertoCARD_HEADING_SPACE_AFTER
Phase 3: Fix Each Slide Builder
- Slide 1: Footer color → WHITE, footer font size → Pt(12)
- Slide 2: Verify card heading alignment with add_card helper changes; line_spacing 1.5
- Slide 3: Change all
p.line_spacing = 1.8to appropriate values (1.15 heading, 1.5 body, 1.4 bullets); standardize card margins - Slide 4: Remove ALL
\nfrom column data strings; change allp.line_spacing = 1.8to 1.15/1.5; standardize margins to CARD_INTERNAL_PAD constants - Slide 5: Standardize margins; change line_spacing values
- Slide 6: Change line_spacing values
- Slide 7: Change line_spacing values; standardize margins
- Slide 8: Change line_spacing values; standardize margins
- Slide 9: Increase card_height to 2.2, title_size to Pt(14), body_size to Pt(12); reformat body text as line-separated items; change line_spacing to 1.4
- Slide 10: Change line_spacing values
- Slide 11: Footer color → WHITE, footer font size → Pt(12); change line_spacing values
Phase 4: Generate and Verify
- Run
python generate-pitch-deck.py - Convert to PDF via LibreOffice headless
- Render all 11 pages to PNG at 300 DPI
- Screenshots go to
_screenshots/{YYYY-MM-DD}/(per CLAUDE.md rules) - Read each PNG with multimodal Read tool
- For each slide, verify the checklist in the Visual QA section below
Phase 5: Fix Issues Found in QA
- Fix any issues found
- Re-generate, re-render, re-verify the affected slides
- Repeat until all 11 slides pass
Phase 6: Deploy
-
cp ../Solanasis_Pitch_Deck.pdf solanasis-site/public/downloads/solanasis-pitch-deck.pdf - Commit script changes in solanasis-scripts repo
- Commit PDF in solanasis-site repo
- Push both repos
- Verify
go.solanasis.com/pitch-deckworks
VISUAL QA CHECKLIST (Per Slide)
For EVERY slide, verify ALL of:
Text Visibility
- All text is fully visible (no clipping, no overflow, no truncation)
- No text overlaps any other text
- Footer/contact text is clearly legible on dark backgrounds
- No text is smaller than 11pt in the rendered output
Alignment & Spacing
- Card headings on the same slide start at the same visual Y-position
- Card body text on the same slide starts at a consistent Y-position relative to the heading
- No text is jammed against card borders (minimum 0.15” padding visible)
- Consistent gaps between title → subtitle → content → closing
- No bizarre line breaks caused by
\nin data or by slashes/special characters
Line Spacing
- Title text lines are tightly spaced (no “floating” feel)
- Card body text is readable but compact (not 1.8-level loose)
- Bullet lists have slightly tighter spacing than body paragraphs
Cards
- All cards on the same slide are the same height
- All cards on the same slide have the same border width
- Card text is anchored to the top (not middle-centered causing different start positions)
- Card corners are subtle (not overly rounded)
Overall
- The slide looks like it came from a professional services firm, not a startup
- Does this slide “match” the quality level of slides 6 and 7 (which are the best currently)?
WHAT NOT TO CHANGE
- Text content: Do not rewrite any slide text, titles, descriptions, bullet items, or closing lines
- Color palette: Keep all brand colors exactly as defined
- Font families: Keep Playfair Display, Montserrat, Libre Baskerville
- Slide count: Keep 11 slides
- Slide order: Keep current order
- Image assets: Do not modify logos or headshots
- Hyperlinks: Keep all clickable links as-is
- Copper pills/badges: Keep design and content as-is
- Circular headshots: Keep the ellipse geometry (it’s working)
CRITICAL FILE LIST
| File | Role |
|---|---|
solanasis-scripts/generate-pitch-deck.py | SOLE file to modify |
solanasis-docs/sales/pitch-deck-styling-overhaul-prompt.md | This prompt (styling spec) |
solanasis-docs/sales/pitch-deck-rebuild-prompt.md | Previous prompt (content spec, deploy steps) |
solanasis-site/public/downloads/solanasis-pitch-deck.pdf | Deploy target |
solanasis-site/public/images/logo/solanasis-logo-horizontal-dark.png | Logo asset |
solanasis-site/public/images/team/dmitri-sunshine.jpg | Headshot asset |
solanasis-site/public/images/team/patrick-mcheyser.jpg | Headshot asset |
WHAT WENT WRONG IN PREVIOUS SESSIONS (Lessons)
-
Line spacing 1.8 applied globally was never questioned. 1.8x line spacing is comfortable for a blog post but far too loose for a pitch deck. It pushes content off the edges of cards, creates disconnected-looking headings, and wastes vertical space. Every line spacing value must be intentional and element-appropriate.
-
\nhardcoded in data strings created ugly centered-text line breaks. “Your IT Provider\n/ MSP” renders as two centered lines where ”/ MSP” sits alone, indented. Similarly “Regulatory strategy,\nexam prep, Form ADV” creates 3 short centered fragments that look ragged. Never use\nin data that will be centered; let text wrap naturally. -
Footer contrast was “fixed” but not actually verified in the rendered output. The code was changed from SLATE_BLUE to PARCHMENT (slide 1) and COPPER_HOVER (slide 11), but nobody checked whether these colors are actually visible in the rendered PDF at real scale. The gradient on slide 1 shifts the background color at the footer position. Always verify contrast in the rendered PDF, not in the code.
-
Card heading alignment was never evaluated holistically. Each card was styled in isolation. Nobody compared “does the heading in card 1 start at the same Y position as the heading in card 3 on this slide?” Always verify cross-card alignment on every multi-card slide.
-
Visual QA was done at thumbnail resolution. Looking at a slide thumbnail doesn’t catch issues like slightly-too-small footer text or 2px alignment differences. Render at 300 DPI and zoom into every region of every slide.
-
set_shrink_autofit()was used as a crutch. Text that doesn’t fit was “fixed” by adding shrink-to-fit, which only works in PowerPoint, not LibreOffice (which generates the PDF). Size boxes correctly; use shrink-to-fit only as a PowerPoint-specific safety net.
Voice Compliance Checklist (verify AFTER all styling is done)
- Zero em dashes (—) in any slide content
- Zero banned words: genuinely, seamless, frictionless, SMBs, audit
- Uses “examination” not “audit”
- Signature terms present: “quiet drift”, “false comfort”
- Date: June 3, 2026 (Regulation S-P)
- URL: go.solanasis.com/intro
- Three-party model: compliance consultant / Solanasis / IT provider
- “We Coordinate With Everyone. We Replace No One.” exact phrasing
Deploy Steps (same as previous prompt)
# Copy PDF to site
cp ../Solanasis_Pitch_Deck.pdf solanasis-site/public/downloads/solanasis-pitch-deck.pdf
# Build and test
cd solanasis-site && npx astro build
npx playwright test
# Commit script (solanasis-scripts repo)
cd ../solanasis-scripts && git add generate-pitch-deck.py
git commit -m "Fix pitch deck styling: line spacing, heading alignment, footer contrast"
# Commit PDF (solanasis-site repo)
cd ../solanasis-site && git add public/downloads/solanasis-pitch-deck.pdf
git commit -m "Update pitch deck PDF with styling fixes"
# Push both
cd ../solanasis-scripts && git push origin main
cd ../solanasis-site && git push origin mainVerify: go.solanasis.com/pitch-deck resolves correctly.