Pitch Deck Complete Overhaul: Definitive 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,550 lines, python-pptx) produces an 11-slide 16:9 PPTX. Two previous prompt-driven sessions have improved it, but persistent visual and content issues remain that make it look unprofessional. This prompt is the single authoritative source for everything that needs to be fixed.

Previous prompts (superseded by this one):

  • solanasis-docs/sales/pitch-deck-rebuild-prompt.md (original rebuild; content spec + deploy steps)
  • solanasis-docs/sales/pitch-deck-styling-overhaul-prompt.md (styling-only; spacing + footer fixes)

This prompt supersedes both. It covers styling AND content through a copywriter’s lens.


Your Mission

  1. Fix all remaining visual/styling issues (hyperlink colors, card heading alignment, spacing)
  2. Review and tighten all text content with a professional copywriter’s eye (this is new; previous prompts said “don’t touch content” but the content now needs a polish pass)
  3. Generate, QA every slide at 300 DPI, 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. 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')
  4. Read each PNG with the Read tool (multimodal) and verify you can see every issue listed below
  5. Create a plan before writing any code

PART A: VISUAL/STYLING FIXES

What you see: On slides 1 and 11, the footer has “solanasis.com | hi@solanasis.com | Boulder, CO” (and on slide 11, also “303-900-8969”). The separator text (”|”, “Boulder, CO”) renders in WHITE as intended. But the hyperlinked text (solanasis.com, hi@solanasis.com, 303-900-8969) renders in LibreOffice’s default blue hyperlink color, which is nearly invisible on the navy/gradient backgrounds.

On slide 11, the CTA card’s “go.solanasis.com/intro” link also renders in blue instead of copper.

Root cause: LibreOffice ignores the font.color.rgb set on hyperlink runs and instead applies its own default hyperlink color from the document’s color scheme. The add_hyperlink_run() helper sets run.font.color.rgb = font_color, but this is overridden at render time.

Fix: Override the hyperlink color at the XML level. After creating the hlinkClick element, you must also set the font color directly on the run’s a:rPr element using a solidFill that LibreOffice will respect. The fix goes in the add_hyperlink_run() helper function:

def add_hyperlink_run(paragraph, text, url, slide, font_name=FONT_BODY,
                      font_size=Pt(14), font_color=COPPER, bold=False,
                      italic=False):
    """Add a styled run with a clickable hyperlink."""
    run = paragraph.add_run()
    run.text = text
    run.font.name = font_name
    run.font.size = font_size
    run.font.color.rgb = font_color
    run.font.bold = bold
    run.font.italic = italic
 
    # Create external hyperlink relationship
    slide_part = slide.part if hasattr(slide, 'part') else slide
    rId = slide_part.relate_to(url, RT_HYPERLINK, is_external=True)
 
    # Add hlinkClick to run properties
    rPr = run._r.find(qn("a:rPr"))
    if rPr is None:
        rPr = etree.Element(qn("a:rPr"))
        run._r.insert(0, rPr)
    hlinkClick = etree.SubElement(rPr, qn("a:hlinkClick"))
    hlinkClick.set(qn("r:id"), rId)
 
    # CRITICAL: Force the font color via solidFill on rPr
    # LibreOffice overrides run.font.color on hyperlinks with theme blue.
    # Setting solidFill directly on rPr forces the correct color.
    for existing_fill in rPr.findall(qn("a:solidFill")):
        rPr.remove(existing_fill)
    solidFill = etree.SubElement(rPr, qn("a:solidFill"))
    srgbClr = etree.SubElement(solidFill, qn("a:srgbClr"))
    srgbClr.set("val", str(font_color))
 
    return run

Verification: After regenerating, the footer text on slides 1 and 11 must be uniformly WHITE (all parts: links, separators, and plain text). The “go.solanasis.com/intro” link on slide 11 must render in COPPER.

Issue 2: Card Heading Vertical Alignment Inconsistent — CRITICAL

What you see: On multi-card slides (2, 4, 5, 7, 8), cards with different-length headings cause body text to start at different Y positions. Example: On slide 2, “Stale WISP” (1 line) has its body text starting higher than “Untested Incident Plan” (wraps to 2 lines). On slide 5, “Family Offices” (1 line) vs “Estate Planning Attorneys” (1 line) vs “RIAs and Financial Advisors” (wraps to 2 lines) all have body text at different heights.

Root cause: The heading and body text share a single text frame. When the heading wraps to more lines, the body text naturally starts lower. MSO_ANCHOR.TOP anchors the entire text frame to the top of the card, but it can’t anchor the body text independently.

Fix approach: Two separate text frames per card. Instead of one text frame with heading + body paragraphs, create:

  1. A heading text frame with a fixed height (sized to fit the tallest heading on that slide, e.g., Inches(0.55) for 2-line headings at 14pt)
  2. A body text frame starting at a fixed Y offset from the top of the card (e.g., card_top + CARD_INTERNAL_PAD_TOP + heading_height)

This ensures body text always starts at the same Y position regardless of heading length.

Implementation for add_card() helper:

def add_card(slide, left, top, width, height, title, body,
             border_color=COPPER, fill_color=WHITE,
             title_color=NAVY, body_color=CHARCOAL,
             title_size=Pt(14), body_size=Pt(12),
             border_width=Pt(1),
             heading_zone_height=Inches(0.55)):
    """Add a card with title and body in separate text frames for alignment."""
    # Card background shape
    card = add_rounded_rect(slide, left, top, width, height,
                            fill_color=fill_color, border_color=border_color,
                            border_width=border_width)
 
    # Heading text frame (fixed height zone)
    heading_tf = add_textbox(
        slide, left + CARD_INTERNAL_PAD_LR, top + CARD_INTERNAL_PAD_TOP,
        width - 2 * CARD_INTERNAL_PAD_LR, heading_zone_height,
        title, font_name=FONT_BODY, font_size=title_size,
        font_color=title_color, bold=True)
    heading_tf._txBody.bodyPr.set("anchor", "t")
 
    # Body text frame (starts at fixed offset below heading zone)
    body_top = top + CARD_INTERNAL_PAD_TOP + heading_zone_height + CARD_HEADING_SPACE_AFTER_INCHES
    body_height = height - CARD_INTERNAL_PAD_TOP - heading_zone_height - CARD_HEADING_SPACE_AFTER_INCHES - CARD_INTERNAL_PAD_BOT
    body_tf = add_textbox(
        slide, left + CARD_INTERNAL_PAD_LR, body_top,
        width - 2 * CARD_INTERNAL_PAD_LR, body_height,
        body, font_name=FONT_BODY, font_size=body_size,
        font_color=body_color)
    body_tf._txBody.bodyPr.set("anchor", "t")
 
    # Apply line spacing
    for p in heading_tf.paragraphs:
        p.line_spacing = TITLE_LINE_SPACING
    for p in body_tf.paragraphs:
        p.line_spacing = CARD_BODY_LINE_SPACING
 
    set_shrink_autofit(heading_tf)
    set_shrink_autofit(body_tf)
 
    return card

Add a new constant:

CARD_HEADING_SPACE_AFTER_INCHES = Inches(0.08)  # Gap between heading zone and body zone

For slides that build cards manually (slides 3, 5, 7, 8): Apply the same two-text-frame pattern. On each multi-card slide, calculate heading_zone_height based on the tallest heading on that slide. All cards on the same slide must use the same heading_zone_height.

Determining heading_zone_height per slide:

  • Slide 2 (4 cards): Tallest heading is “Untested Incident Plan” (~2 lines at 14pt in 2.35” usable width) → Inches(0.55)
  • Slide 4 (3 columns): Tallest heading is “Your Compliance Consultant” (~2 lines at 16pt in 3.1” usable width) → Inches(0.65)
  • Slide 5 (3 cards): Tallest heading is “RIAs and Financial Advisors” (~2 lines at 16pt in 3.2” usable width) → Inches(0.65)
  • Slide 7 (2 cards): Both headings are 1 line → Inches(0.4)
  • Slide 8 (3 cards): All headings are 1 line at 15pt → Inches(0.4)
  • Slide 9 (6 cards): Tallest heading is “Disaster Recovery Verification” or “Responsible AI Implementation” (~2 lines at 14pt in 3.2” usable width) → Inches(0.55)

Issue 3: Line Spacing Constants Already Added — VERIFY

The previous session added spacing constants and replaced all 1.8 line spacing values. Verify these are still in place and haven’t regressed:

ElementLine Spacing
Slide titles (Playfair 36pt)1.15
Card headings (14-16pt bold)1.15
Card body text (11-13pt)1.5
Bullet lists1.4
Footer/footnote text1.2
Italic subtitles1.4
CTA card text1.5

Grep for 1.8 in the script. If any remain, fix them.

Issue 4: Card Margins — VERIFY

The previous session standardized card margins to constants. Verify these are consistent:

  • CARD_INTERNAL_PAD_TOP = Inches(0.15)
  • CARD_INTERNAL_PAD_LR = Inches(0.2)
  • CARD_INTERNAL_PAD_BOT = Inches(0.12)

Exception: CTA card on slide 11 can use Inches(0.25) top and Inches(0.3) sides.

Issue 5: Services Grid (Slide 9) — VERIFY

Previous session increased card_height to 2.2”, title to Pt(14), body to Pt(12), and reformatted body text as line-separated items. Verify these are in place and rendering correctly.

Issue 6: estimate_text_height() — VERIFY

Previous session added char_width_factor parameter and changed default line_spacing to 1.5. Verify in place.

Issue 7: Accent Rule Consistency

Accent rules should be standardized:

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: Pt(3) height, COPPER color. Verify and fix any inconsistencies.


PART B: CONTENT REVIEW (COPYWRITER LENS)

This is new. Previous prompts said “don’t touch content.” Now we need to review every word with a professional copywriter’s eye. The deck must read like it was written for a managing partner at a $500M RIA, not like a tech startup pitch.

Content Principle: Every word earns its place

  • No filler, no jargon-for-jargon’s-sake
  • Short, punchy sentences
  • Specific over vague (“14 findings mapped to SEC examination priorities” > “comprehensive analysis”)
  • Confident but not arrogant; authoritative but not salesy

Issue B1: “Fractional CSRO” (Slide 8, Path 3) — NEEDS REWORK

Current text:

Fractional CSRO
Monthly cadence, month-to-month. Ongoing cybersecurity and resilience
leadership without a full-time hire.

(Chief Security and Resilience Officer)

Problems:

  1. “CSRO” is not a recognized title. The audience (RIA managing partners, estate planning attorneys) won’t know what it means. It sounds made-up.
  2. The parenthetical definition at the end looks like an afterthought.
  3. “Fractional” is increasingly common in business but the audience may still need context.

Recommended fix — Option A (recommended):

Ongoing Security Leadership
Monthly cadence, month-to-month. Dedicated cybersecurity and resilience
oversight without a full-time hire.

This removes the jargon entirely. “Ongoing Security Leadership” is immediately clear to any executive.

Option B (if you want to keep “fractional” positioning):

Fractional Security Leadership
Monthly cadence, month-to-month. Ongoing cybersecurity and resilience
oversight for firms that need the function, not the headcount.

Option C (keep CISO which is recognized):

Fractional CISO
Monthly cadence, month-to-month. Ongoing cybersecurity leadership
without a full-time hire.

CISO is widely recognized in financial services. CSRO is not.

Decision: ASK the user which option they prefer before making any change. Present all three options. Do not pick one unilaterally. If the user doesn’t respond, leave the current text unchanged and flag it as a TODO.

Issue B2: Review All Slide Text for Professional Polish

IMPORTANT: Do NOT change any text content without presenting the change to the user and getting explicit approval. Content changes are suggestions, not mandates. Present them as a numbered list of proposed changes after the styling fixes are done, and wait for approval before implementing.

Go through every slide and flag anything that reads as:

  • Too casual for a wealth management audience
  • Redundant or filler
  • Unclear to a non-technical executive
  • Missing a period or having inconsistent punctuation

Known items to check:

  • Slide 1 title: “Solanasis: Cybersecurity and Operational Resilience for Wealth Management” — is the colon + full phrase too long for a cover slide? Consider whether a shorter, punchier title works better. But DO NOT change without user confirmation.
  • Slide 4: “Your IT Provider / MSP” — does the audience know what MSP means? If this goes to attorneys, probably not. Consider “Your IT Provider” alone.
  • Slide 8 footnote: “See next slide for our full capabilities beyond the assessment.” — this is meta-commentary about the deck, not about the firm. Consider removing or rewording.
  • Slide 10 bio text: Verify the bios read as third-person executive summaries, not resume bullet points.

Issue B3: Voice Compliance (Non-Negotiable)

These rules are absolute:

  • Zero em dashes (—) in any slide content
  • Zero banned words: genuinely, seamless, frictionless, SMBs, audit
  • Uses “examination” not “audit” everywhere
  • Signature terms present: “quiet drift”, “false comfort” (slide 2)
  • 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 (slide 4)

PART C: SLIDE-BY-SLIDE SPEC

Slide 1: Cover (Navy gradient)

  • Logo top-left, copper accent rule below
  • Title (36pt Playfair, white, bold)
  • Body (16pt, parchment)
  • Founder quote (15pt Libre Baskerville italic, copper hover, line_spacing 1.4)
  • Audience line (14pt, parchment)
  • Key line (18pt, copper, bold) — “Examination-ready in 10 business days.”
  • Footer: ALL text WHITE at Pt(12), including hyperlinks (fix Issue 1)

Slide 2: The Problem (Parchment)

  • Title + subtitle
  • 4 gap cards (copper border, white fill) — fix heading alignment (Issue 2)
  • Closing with “quiet drift” + “false comfort” highlighted
  • Accent rule before closing

Slide 3: The Urgency (Parchment)

  • Title (may wrap to 2 lines)
  • Two-column regulatory cards (deep navy border)
  • June 3, 2026 copper pill badge
  • Bullets at 1.4 line spacing
  • Closing italic line centered
  • Fix heading alignment between left and right cards (Issue 2)

Slide 4: Who We Are (Parchment)

  • Title: “We Coordinate With Everyone. We Replace No One.”
  • Subtitle about three-party positioning
  • 3 columns: outer (warm stone), center (navy, copper border, Pt(2))
  • No \n in data strings (already fixed, verify)
  • Centered text in columns is OK here (short content)
  • Fix heading alignment (Issue 2)
  • Closing line centered

Slide 5: Who We Serve (Parchment)

  • Title: “Built for Wealth Management”
  • 3 tall cards with badges
  • Fix heading alignment (Issue 2) — “RIAs and Financial Advisors” wraps while others don’t
  • Badge items at 1.4 line spacing

Slide 6: The Assessment (Parchment)

  • Title + italic subtitle
  • Regulatory badge bar (copper pills)
  • Two text columns: “What We Do” (checkmarks) + “What You Get” (bold labels)
  • Closing: accent rule + italic line
  • add_paragraph() calls should use updated defaults (1.5 line spacing, Pt(6) space_after)

Slide 7: Composite Findings (Parchment)

  • Title + italic disclaimer
  • Before/After cards — fix heading alignment (Issue 2)
  • Before: warm stone, slate blue accents
  • After: white, copper accents
  • Closing: accent rule + one-liner

Slide 8: Next Steps (Parchment)

  • Title + italic subtitle
  • 3 path cards with numbered copper circles
  • Content review: “Fractional CSRO” (Issue B1) — get user confirmation on replacement
  • Fix heading alignment (Issue 2)
  • Footnote at bottom

Slide 9: Services Grid (Parchment)

  • Title + italic subtitle
  • 3x2 grid: card_height 2.2”, title Pt(14), body Pt(12), line-separated items
  • Fix heading alignment (Issue 2) — some headings wrap
  • Closing: accent rule + italic line

Slide 10: Team (Parchment)

  • Title + italic subtitle
  • 2 team cards with circular headshots + bios
  • Verify bios read as professional executive summaries

Slide 11: CTA (Navy)

  • Title (38pt, parchment) + copper accent rule
  • Left: “What Makes Us Different” + 3 differentiator items
  • Right: CTA card (deep navy, copper border) with booking info + features + URL
  • Footer: ALL text WHITE at Pt(12), including hyperlinks (fix Issue 1)
  • CTA link “go.solanasis.com/intro” must render in COPPER, not blue (fix Issue 1)
  • Logo bottom-left

PART D: CONSTANTS AND HELPERS

Spacing Constants (already in script, verify)

TITLE_MARGIN_TOP = Inches(0.6)
TITLE_TO_SUBTITLE = Inches(0.15)
SUBTITLE_TO_CONTENT = Inches(0.25)
CARD_INTERNAL_PAD_TOP = Inches(0.15)
CARD_INTERNAL_PAD_LR = Inches(0.2)
CARD_INTERNAL_PAD_BOT = Inches(0.12)
CARD_HEADING_SPACE_AFTER = Pt(8)
CARD_HEADING_SPACE_AFTER_INCHES = Inches(0.08)  # NEW — for two-text-frame cards
CARD_BODY_LINE_SPACING = 1.5
TITLE_LINE_SPACING = 1.15
BODY_LINE_SPACING = 1.5
ACCENT_RULE_TO_TEXT = Inches(0.15)
CONTENT_TO_FOOTER = Inches(0.3)

Brand Constants (already in script, do NOT change)

Navy:        #020532    Copper:      #C47A3D    Parchment:   #FEF9F1
Deep Navy:   #091652    Copper Hover:#D4945E    Warm Stone:  #F0EBE4
Charcoal:    #111827    White:       #FFFFFF    Slate Blue:  #374273

Fonts: Playfair Display (headings), Montserrat (body), Libre Baskerville (accent)
Slide: 13.333 x 7.5 inches (16:9)
Margins: 0.8" left/right, 0.6" top

PART E: WHAT NOT TO CHANGE

  • 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
  • Copper pills/badges: Keep design and content
  • Circular headshots: Keep the ellipse geometry (it works)

PART F: GENERATION AND QA PROCESS

Generate

cd solanasis-scripts && python generate-pitch-deck.py
"C:/Program Files/LibreOffice/program/soffice.exe" --headless --convert-to pdf ../Solanasis_Pitch_Deck.pptx --outdir ..

Render to PNG (300 DPI)

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')

Screenshots go to _screenshots/{YYYY-MM-DD}/ per CLAUDE.md rules.

Visual QA Checklist (EVERY slide)

Text Visibility:

  • All text is fully visible (no clipping, overflow, or truncation)
  • No text overlaps any other text
  • Hyperlink text is the correct color (not LibreOffice default blue)
  • Footer/contact text is clearly legible on dark backgrounds
  • No text smaller than 11pt in rendered output

Card Heading Alignment:

  • 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 (this is THE key check)
  • No text jammed against card borders (minimum 0.15” padding visible)

Line Spacing:

  • Title text lines tightly spaced (no “floating” feel)
  • Card body text readable but compact (not 1.8-level loose)
  • Bullet lists slightly tighter than body paragraphs

Cards:

  • All cards on same slide are same height
  • All cards have same border width
  • Card text anchored to top
  • Subtle corner radius

Overall:

  • Looks like it came from a professional services firm, not a startup
  • Every slide matches the quality level you’d expect from a Kroll or Agio pitch deck

QA the Content

  • Zero em dashes
  • Zero banned words
  • “examination” not “audit”
  • Signature terms present
  • “Fractional CSRO” replaced (per user decision)
  • All text is grammatically correct
  • Punctuation consistent (periods at end of card body text, etc.)

PART G: DEPLOY

# Copy PDF to site
cp ../Solanasis_Pitch_Deck.pdf solanasis-site/public/downloads/solanasis-pitch-deck.pdf
 
# Commit script (solanasis-scripts repo)
cd solanasis-scripts && git add generate-pitch-deck.py
git commit -m "Overhaul pitch deck: fix hyperlink colors, card alignment, content polish"
 
# Commit PDF (solanasis-site repo)
cd ../solanasis-site && git add public/downloads/solanasis-pitch-deck.pdf
git commit -m "Update pitch deck PDF with visual and content overhaul"
 
# Push both
cd ../solanasis-scripts && git push origin main
cd ../solanasis-site && git push origin main

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


PART H: WHAT WENT WRONG IN PREVIOUS SESSIONS

  1. Hyperlink color override was never tested in the rendered PDF. The code set run.font.color.rgb = WHITE on hyperlink runs, but LibreOffice overrides hyperlink colors with its own theme blue. The fix was in the code but invisible in the output because nobody verified the hyperlinked text specifically in the rendered PDF. Lesson: After changing hyperlink colors, zoom into the rendered PDF and verify the link text color pixel-by-pixel against the non-link text next to it.

  2. Card heading alignment was “fixed” by setting MSO_ANCHOR.TOP but the real problem is structural. When heading and body share a text frame, body text position depends on heading length. The only reliable fix is separate text frames for heading and body within each card, with body starting at a fixed Y offset. Lesson: MSO_ANCHOR.TOP anchors the frame, not individual paragraphs within it.

  3. Visual QA at thumbnail resolution. Looking at a slide thumbnail doesn’t catch that “solanasis.com” is blue while ”| Boulder, CO” is white right next to it. Lesson: Render at 300 DPI and zoom into every region of every slide. Compare adjacent text elements for color consistency.

  4. Content was treated as sacred. “Don’t touch the content” was appropriate early on, but after the layout is stable, the content needs a copywriter’s pass. “Fractional CSRO” is jargon that the target audience won’t recognize. Lesson: Every element of a pitch deck should be reviewed holistically before final deploy.

  5. “Line spacing 1.8 everywhere” was applied globally and never questioned for 2+ sessions. It took a dedicated styling prompt to identify that 1.8x is document-grade spacing, not presentation-grade. Lesson: Question defaults early.

  6. set_shrink_autofit() as a crutch. It only works in PowerPoint, not LibreOffice (which generates the PDF). Don’t rely on it. Size everything correctly. Keep it as a PowerPoint-only safety net.


CRITICAL FILE LIST

FileRole
solanasis-scripts/generate-pitch-deck.pySOLE file to modify
solanasis-docs/sales/pitch-deck-complete-overhaul-prompt.mdThis prompt (authoritative)
solanasis-docs/sales/pitch-deck-rebuild-prompt.mdPrevious prompt (content reference only)
solanasis-docs/sales/pitch-deck-styling-overhaul-prompt.mdPrevious prompt (styling reference only)
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