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:

  1. Read this prompt completely (you’re reading it now)
  2. Read the current script (solanasis-scripts/generate-pitch-deck.py) end to end
  3. Read the previous rebuild prompt (solanasis-docs/sales/pitch-deck-rebuild-prompt.md) for content spec and deploy steps
  4. 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')
  5. Read each PNG with the Read tool (multimodal) and verify you can see every issue listed below
  6. 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/closing

B. 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:

ElementLine SpacingWhy
Slide titles (Playfair 36pt)1.15Tight; large text needs less spacing
Card headings (14-16pt bold)1.15Tight; headings are short
Card body text (11-13pt)1.5Comfortable reading density
Bullet lists1.4Slightly tighter than body
Footer/footnote text1.2Compact
Italic subtitles1.4Slightly open
CTA card text1.5Readable

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 \n manual 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 \n creates 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 \n from 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.

ElementMinimum SizeCurrent Issues
Slide titlesPt(34-38)OK
SubtitlesPt(14-16)OK
Card headingsPt(14)Slide 9 uses Pt(13); increase to Pt(14)
Card body textPt(11)OK
Bullet itemsPt(12)OK
FootnotesPt(11)OK
Footer/contactPt(12)Currently Pt(11); increase
Badge/pill textPt(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:

  1. Increase card_height from Inches(2.0) to Inches(2.2)
  2. Increase title_size from Pt(13) to Pt(14)
  3. Increase body_size from Pt(11) to Pt(12)
  4. 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.
  5. Reduce line_spacing in 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:

LocationWidthAlignment
Cover slide (below logo)Inches(4)Left (at MARGIN_LEFT)
Before closing linesInches(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

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 footer

Fix (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 footer

Issue 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:

  1. Remove ALL \n from 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"
  2. Ensure every card text frame has tf.margin_top = CARD_INTERNAL_PAD_TOP (use the constant, not varying values)
  3. 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_BOT

Issue 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 default line_spacing from 1.8 to 1.5, add char_width_factor param
  • add_textbox(): no changes needed (line_spacing not set here)
  • add_paragraph(): change default line_spacing to 1.5, change default space_after to Pt(6)
  • add_card(): change title p.line_spacing to 1.15, body p.line_spacing to 1.5, use padding constants, change space_after to CARD_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.8 to appropriate values (1.15 heading, 1.5 body, 1.4 bullets); standardize card margins
  • Slide 4: Remove ALL \n from column data strings; change all p.line_spacing = 1.8 to 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-deck works

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 \n in 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

FileRole
solanasis-scripts/generate-pitch-deck.pySOLE file to modify
solanasis-docs/sales/pitch-deck-styling-overhaul-prompt.mdThis prompt (styling spec)
solanasis-docs/sales/pitch-deck-rebuild-prompt.mdPrevious prompt (content spec, deploy steps)
solanasis-site/public/downloads/solanasis-pitch-deck.pdfDeploy target
solanasis-site/public/images/logo/solanasis-logo-horizontal-dark.pngLogo asset
solanasis-site/public/images/team/dmitri-sunshine.jpgHeadshot asset
solanasis-site/public/images/team/patrick-mcheyser.jpgHeadshot asset

WHAT WENT WRONG IN PREVIOUS SESSIONS (Lessons)

  1. 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.

  2. \n hardcoded 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 \n in data that will be centered; let text wrap naturally.

  3. 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.

  4. 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.

  5. 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.

  6. 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 main

Verify: go.solanasis.com/pitch-deck resolves correctly.