Home / Ops / The Locked Demo

Affirmology Demo Launch Plan v2

Updated May 21, 2026 · Affirmology_DemoLaunchPlan_v2.md

Summary. Goal: Stand up affirmology.ai/demo so an investor or partner can enter their birth data, watch their report appear, listen to their personalized audio, and get the same package emailed to them. All driven by the existing Python agent in affirmology-agent/.

Affirmology Demo Launch Plan v2

Goal: Stand up affirmology.ai/demo so an investor or partner can enter their birth data, watch their report appear, listen to their personalized audio, and get the same package emailed to them. All driven by the existing Python agent in affirmology-agent/.

Scope (locked): Investor / partner demo, not public launch. No payments, no accounts, no abuse protection beyond a basic rate cap. Polish over throughput. Vibe: simple, mysterious, "you are looking at something sacred."


Locked Decisions From v1 Review

  1. Visual identity matches the deck. Deep emerald background, warm gold accents, cream text, faint cosmic dust. Not the cream-dominant brief letter aesthetic.
  2. Voice: ElevenLabs Charlotte (meditation), already in .env.
  3. Music bed: Heavenly Circuit.mp3 for v1, every demo. Variations come later.
  4. Audio gating on results page: soft gate. Play button is visible. Report is rendered but faded out and softly blurred until play is pressed; it fades in as soon as the audio starts. No hard lock, no waiting screen. The audio is the centerpiece, the report is the receipts.
  5. Email sender: hello@affirmology.ai, sent from GHL once the domain is verified.
  6. Submission cap: 5 submissions per email per 24 hours. Useful slack for testing.
  7. Results page footer: one quiet link, "Download the report". Nothing else. No sales page, no nurture CTA.

The Visual Language (locked)

Pulled from the deck cover, page 2, page 3, page 4.

--bg-deep      #0a2014    /* the deep emerald, near-black with green undertone */
--bg-card      #112e26    /* one shade lifted, for cards and form rows         */
--bg-quote     #163a30    /* a touch lighter still, for callouts               */
--gold         #c89b3c    /* primary gold for buttons and ornaments            */
--gold-soft    #d9b15a    /* hover state and small flourishes                  */
--gold-glow    rgba(200, 155, 60, 0.25)  /* button shadows, focus rings        */
--cream        #faf6ee    /* primary text on dark bg                           */
--cream-muted  #cdbfa3    /* secondary text, labels, captions                  */
--line         #1f4536    /* dividers, form borders                            */

Typefaces. - Cormorant Garamond (italic and semibold) for the headlines, the pull quotes, and the user's name on the results page. Google Fonts, free, weights 400, 500, 600, 700, plus italic 400 and 500. - Inter (300, 400, 500) for everything you read in paragraphs, labels, buttons. - JetBrains Mono (400) at 11px for the eyebrow tags ("✦ THE INTAKE ✦", "✦ YOUR CHART ✦"). Tracking 0.4em uppercase. Sparingly.

Ornament. The gold four-pointed star ✦ used as a section divider and a list bullet. The deck uses constellation dust in the background. We'll use a faint SVG of scattered gold points fixed behind the content at maybe 8% opacity. Sparingly, never noisy.

Tone of voice. Half-whispered. "Your chart. Your healing. Your voice." style. Never marketing speak. The intake page has at most one sentence of explanation under the title. The results page just says "Sol, your soul's song" above the player. The report speaks for itself.


The Stack (Locked Today)

  1. Cloudflare Pages hosts the intake form and the results page. Domain is already at Cloudflare. Free tier covers everything we need.
  2. Railway hosts the existing Python agent as a FastAPI service behind a public URL. ~$5 to $20 per month for demo volume.
  3. Cloudflare R2 stores audio (MP3) and report files (PDF and markdown). Signed URLs, no egress fees.
  4. Go High Level owns the contact record, the domain verification, and the email send. We will verify affirmology.ai as a sending domain in GHL using DNS records added to Cloudflare. No mail server of our own.
  5. Cloudflare Email Routing (free) catches inbound mail to hello@affirmology.ai and forwards it to your real inbox.

The Flow, End to End

  1. User lands on affirmology.ai/demo. Static page on Cloudflare Pages. Deep emerald background. Cormorant title "Your chart. Your healing. Your voice." in cream and gold.
  2. User fills first name (required), middle name (optional), last name (required), birth date, birth time (with an "I don't know" toggle), time zone (auto-detected, editable), birth location (city autocomplete), email. Submits.
  3. The page POSTs to the Railway agent's /api/runs. Agent returns { run_id, status: "queued" } immediately and starts work in the background. The page navigates to affirmology.ai/demo/r/[run_id].
  4. The agent runs the existing four-engine pipeline (astrology, Human Design, Gene Keys, numerology), verifies, then calls ElevenLabs (Charlotte voice), then mixes the voice with Heavenly Circuit.mp3 using FFmpeg, then uploads audio plus report to R2.
  5. The results page polls /api/runs/[id] every 3 seconds. Progress states display in turn: "Calculating your chart" → "Hearing your themes" → "Composing your voice" → "Mixing your soul's song" → "Ready". A breathing gold star animates while waiting.
  6. On delivered, the results page shows: the user's first name in italic Cormorant at top, the play button below that, the report rendered as styled HTML below the player (faded and softly blurred until play is pressed). A single quiet link sits at the very bottom: "Download the report (PDF)".
  7. In parallel, the agent POSTs to a GHL inbound webhook with the user's contact data and the signed URLs. GHL creates or updates the contact, stores the URLs as custom fields, and fires the "Demo Delivered" email from hello@affirmology.ai.

Component 1: Intake Page

Path: affirmology.ai/demo (Cloudflare Pages, index.html at /demo/) Layout: Single centered column, max width 560px, vertically centered on first load, soft scroll thereafter. Star-dust SVG fixed behind everything at 8% opacity. Above the fold: Eyebrow "✦ AFFIRMOLOGY ✦". Title "Your chart. Your healing. Your voice." in Cormorant italic, cream. One subline in Inter at --cream-muted: "Enter the moment you arrived. Receive your soul's song." Form fields: First name. Middle name (optional). Last name. Birth date. Birth time (24h, with "I don't know" toggle that disables the field and surfaces a small note: "Your rising sign and houses will be soft."). Time zone (browser-detected, dropdown). Birth location (text input with a geocoder autocomplete). Email. Submit button: Full-width, gold background, deep emerald text, "Begin" in Inter 500. Hover lifts to --gold-soft. Submit behavior: Fetch POST to /api/runs. On 200, navigate to results page. On error, inline error in cream, no popups.

Component 2: Results Page

Path: affirmology.ai/demo/r/[run_id] Waiting state: Same emerald background. Breathing gold star ✦ centered, 64px. Below it, the current progress phrase in Cormorant italic, fading in and out as the phase changes. Optional: a faint percentage in JetBrains Mono at the very bottom. Delivered state, top to bottom: 1. Eyebrow "✦ YOUR SOUL'S SONG ✦" in gold mono caps. 2. The user's name: "Sol" in Cormorant italic, large. Subtitle: "Recorded for the moment you arrived." in cream-muted. 3. The audio player. Custom-styled HTML5 audio. Gold play/pause button. Cream waveform or thin progress bar. Time codes in JetBrains Mono. 4. A thin gold divider with a centered ✦. 5. The report rendered as styled HTML. Headlines in Cormorant. Body in Inter. Same emerald background. Width matches the audio player. 6. At the bottom, "Download the report (PDF)" as a quiet link in --cream-muted, underlined. The soft gate: Section 5 starts at opacity: 0.25 with filter: blur(6px). A JavaScript listener on the audio's play event transitions it to opacity: 1; filter: blur(0) over 1.5 seconds. Nothing is hidden, just hushed until you press play. Polling: Client polls /api/runs/[id] every 3 seconds while status is in queued, computing_chart, generating_script, recording_audio, mixing. Stops on delivered or failed.

Component 3: Agent API on Railway

Wraps: the existing affirmology-agent Python package. Stack: FastAPI + uvicorn, dockerized. Background work via FastAPI's built-in BackgroundTasks for demo volume (no Redis or Celery needed). Endpoints: - POST /api/runs - accepts the form payload, validates, starts the background task, returns { run_id, status }. Enforces the 5-per-email-per-day cap by checking R2 manifest or a tiny SQLite file. - GET /api/runs/{run_id} - returns current status. On delivered, also returns the rendered report markdown, the signed audio URL, the signed PDF URL, and any verification flags. - POST /api/webhook/test/ghl - fires a test payload to the GHL webhook for setup verification. Background pipeline: compute_chart (existing) → generate_reports (existing) → verify_report (existing) → generate_script (Phase 2 work, partially stubbed today; v1 uses the existing Affirmology_Script_v5.md template filled with the chart values) → synthesize_audio (ElevenLabs Charlotte, new) → mix_audio (FFmpeg, voice on top, Heavenly Circuit.mp3 ducked to -18 dB, new) → render_pdf (PDF skill, new) → upload_to_r2 (new) → notify_ghl (new) → mark delivered. Deploy: Add Dockerfile, railway.toml, FastAPI module under src/affirmology/api.py. Push to a new GitHub repo, connect Railway, railway up. Environment variables: ANTHROPIC_API_KEY, ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID (Charlotte), R2_ACCESS_KEY, R2_SECRET_KEY, R2_BUCKET, R2_ENDPOINT, GHL_WEBHOOK_URL, DEMO_RATE_LIMIT=5.

Component 4: GHL Domain Verification, Webhook, and Email

This is the most manual piece. Two separate setups inside GHL.

4a. Verify affirmology.ai as a sending domain (one-time). 1. In GHL: Settings → Email Services → Dedicated Domain → "Add Domain" → enter affirmology.ai. 2. GHL gives you about 4 DNS records (a TXT for SPF, two CNAMEs for DKIM, one TXT for DMARC). 3. In Cloudflare DNS for affirmology.ai, add each record exactly as shown. Set proxy status to "DNS only" (gray cloud), not "Proxied". 4. Back in GHL, click "Verify". Usually green within 5 minutes, sometimes up to an hour. 5. Add hello@affirmology.ai as a "From" identity inside Settings → My Staff or Settings → Sender Domains.

4b. Inbound webhook + email automation. 1. Workflows → Create Workflow → Trigger: "Inbound Webhook". 2. GHL generates a webhook URL. Copy it into the agent's GHL_WEBHOOK_URL env var. 3. Sample payload (paste this into the GHL test sender so it can map fields): json { "first_name": "Sol", "last_name": "Parker", "email": "sol@example.com", "birth_date": "1990-03-21", "birth_time": "14:32", "birth_location": "Buenos Aires, Argentina", "audio_url": "https://r2.../audio.mp3?signed", "report_url": "https://r2.../report.pdf?signed", "run_id": "run_abc123" } 4. Add action: Create/Update Contact. Map email to email, first_name and last_name accordingly. Map audio_url, report_url, birth_date, birth_time, birth_location, run_id to custom fields (create them if they don't exist). 5. Add action: Send Email. From: hello@affirmology.ai. Subject: "Your Affirmology, ready". Body template (cream + gold, dark emerald): a Cormorant headline, a paragraph in Inter, two gold buttons (Listen and Download), one quiet line of closing copy. I will deliver the HTML email template alongside the code.

4c. Inbound forwarding. 1. In Cloudflare: Email → Email Routing → enable for affirmology.ai. 2. Add a route: hello@affirmology.ai → forwards to jeff@jeffparker.love. 3. Verify your destination address in Cloudflare's confirmation email.

Component 5: Storage on Cloudflare R2

Bucket: affirmology-demo-renders Objects per run: runs/{run_id}/audio.mp3, runs/{run_id}/report.pdf, runs/{run_id}/report.md, runs/{run_id}/chart.json. Access: Signed URLs valid for 30 days, generated server-side in the agent. No public bucket. Setup: Create the bucket in the Cloudflare R2 dashboard. Generate an R2 API token with Object Read & Write scope. Hand me the access key, secret, endpoint, and bucket name.


Build Order

  1. Intake + results HTML, fully mocked. Both pages built against a stub API that returns a canned report and a placeholder audio after 30 fake seconds. Locks the visual direction. About one focused session.
  2. Dockerize and deploy the existing agent to Railway. Just compute_chart and generate_reports exposed through /api/runs. Audio still placeholder. Proves the deploy story. About one focused session.
  3. ElevenLabs Charlotte synthesis + FFmpeg mix with Heavenly Circuit. Replaces the placeholder. About one to two sessions.
  4. R2 upload + signed URLs. About half a session.
  5. GHL webhook integration in the agent. I write the POST and the email template HTML; you set up the workflow in the GHL UI in parallel. About half a session for me, 30 minutes for you in GHL.
  6. Domain wiring. You add the GHL DNS records to Cloudflare. You configure Cloudflare Email Routing. You point Cloudflare Pages at affirmology.ai/demo. About 30 minutes once everything else is green.

Total time to a live demo: 4 to 6 focused sessions.


What You Own vs What I Build

You (manual setup, one-time): - Railway account. Connect to a GitHub repo for the agent. - Cloudflare Pages project. Point at affirmology.ai/demo. - Cloudflare R2 bucket and API token. - ElevenLabs Creator-tier account. Charlotte voice ID is already in .env. - GHL: verify affirmology.ai as a sending domain (Component 4a), build the inbound webhook workflow (Component 4b). - Cloudflare Email Routing: forward hello@affirmology.ai to your real inbox (Component 4c). - Hand me the resulting URLs and tokens so I can fill the agent's .env.

I build: - Intake page HTML, CSS, JS. Self-contained, no framework. Matches the deck. - Results page HTML, CSS, JS. Polling, soft gate, custom audio player, inline report. - FastAPI wrapper around the existing Python agent. - Dockerfile, railway.toml, deploy config. - ElevenLabs synthesis function (Charlotte). - FFmpeg mix function (Heavenly Circuit at -18 dB under voice). - PDF render function (using the PDF skill). - R2 upload helper and signed-URL generator. - GHL webhook POST in the agent. - GHL HTML email template, deck-styled. - A README that ties all the deploy steps together.


What I Will Need From You as Inputs

Before I can finish components 3 to 5, I need: - Railway public URL (after you deploy the first stub). - R2 access key, secret, endpoint, bucket name. - GHL inbound webhook URL. - Confirmation that affirmology.ai is verified as a sending domain in GHL.

I can build components 1 and 2 (the HTML pages) without any of the above. They're mocked against a stub. So the next session can start there.


Next Concrete Step

Build the intake page and the results page in the deck's visual language, mocked against a fake agent. Drop both files in AFFIRMOLOGY/CLAUDE OUTPUTS/Affirmology/ for review. You open them in your browser, submit the form, watch the simulated 30-second render, see the report fade in under the play button when you press play. That is the experience locked.

Greenlight that and I'll build it next.