Creating a Dynamic Proposal Template System: Deploying Ash-Scattering Charter Proposals to S3 + CloudFront
What Was Done
We identified and resolved a missing static asset in the Queen of San Diego proposal system: the charter proposal for ash-scattering ceremonies had never been published to production, despite existing as a template. The solution involved:
- Creating the production HTML file from an existing template pattern
- Implementing a two-tier pricing model with decision-fatigue reduction (two options instead of four)
- Including regulatory compliance information (USCG/EPA/MPRSA requirements)
- Deploying to S3 with CloudFront cache invalidation
- Verifying deployment across the CDN edge network
Technical Details: File Structure and Template Pattern
The Queen of San Diego site uses a static-site-generation pattern with a centralized template library. The proposal system lives at:
/Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/
The missing file was created at:
jada-charter-proposal-sue.html
Discovery process revealed the file had never been written to disk despite being referenced in documentation. The template pattern used by other proposals in the same directory follows this structure:
- HTML5 semantic markup with microdata for pricing and schema.org compliance
- Inline CSS for proposal styling (no external stylesheets to reduce HTTP requests)
- Embedded image references using relative paths to
/assets/images/directory - Form submission endpoints wired to
bookings@queenofsandiego.com - Hollywood history context sections (thematic branding)
The file was created with approximately 15 iterations to achieve:
- Correct pricing data (Option A: $750 for 2-hr Captain-only; Option B: $1,575 for 3-hr full crew)
- Legal compliance language for ash scattering under EPA/MPRSA and USCG regulations
- Recommendation bias toward Option A (reduced decision fatigue for the end user)
- Removal of personal identifiers (sister-in-law name and email replaced with general booking address)
Infrastructure: S3 Deployment and CloudFront Distribution
The site uses a multi-stage deployment pipeline:
Local Git repo → S3 bucket → CloudFront distribution → DNS (Route53)
S3 Bucket Configuration:
- Bucket name:
queenofsandiego.com(derived from domain, follows naming convention) - Object path:
proposals/jada-charter-proposal-sue.html - Deployment command uses AWS CLI with environment variables sourced from secrets file:
set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && \
aws s3 cp proposals/jada-charter-proposal-sue.html \
s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html \
--content-type text/html
CloudFront Distribution Setup:
After S3 upload, cache invalidation was required to ensure edge nodes served the new content immediately:
aws cloudfront create-invalidation \
--distribution-id [DISTRIBUTION_ID] \
--paths "/proposals/jada-charter-proposal-sue.html" \
--region us-east-1
The distribution ID is stored in the environment secrets file and resolved at deploy time. Invalidation status was verified using:
aws cloudfront get-invalidation \
--distribution-id [DISTRIBUTION_ID] \
--id [INVALIDATION_ID]
This returns status tracking to confirm the invalidation completed across CloudFront's edge locations before URL went live.
DNS / Route53:
No Route53 changes were required; queenofsandiego.com already resolves to the CloudFront distribution. The proposed URL (https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html) was immediately accessible after invalidation cleared.
Key Architectural Decisions
Why Static HTML Instead of Dynamic Generation?
The proposal system uses pre-generated static HTML for several reasons:
- Performance: CloudFront serves cached HTML with zero server-side rendering latency
- Simplicity: No database queries, no runtime dependencies, reduced attack surface
- Version control: Every proposal version is tracked in Git with full history
- Offline preview: Developers can open files locally to verify before deployment
Pricing Model: Two Options vs. Four
The original card referenced four pricing tiers with truncated values. We consolidated to two because:
- Decision fatigue: Research shows three to four options is the cognitive load threshold; two reduces paralysis
- Clear recommendation: Option A ($750) is marked as recommended, guiding users toward a faster closing path
- Business logic: The two options correspond to actual service tiers (Captain-only vs. full crew), not arbitrary price points
Regulatory Compliance Inline
Ash-scattering charter proposals must include USCG and EPA compliance language. Rather than linking to external documents, we embedded a brief legal notice directly in the HTML. This ensures:
- Users see compliance info without additional clicks
- The proposal is self-contained and can be printed as a single document
- No broken links if external compliance pages move or change
Deployment Verification
Post-deployment validation included:
- S3 object metadata check: Verified file exists and is readable via
aws s3api head-object - HTTP response test:
curlrequest to the live URL confirmed 200 status and correct HTML content - CloudFront cache status: Checked
x-cacheandx-amz-cf-*headers to confirm edge serving
All checks passed; the proposal is live and cached globally.
What's Next
Pending items for the proposal:
- Asset integration: A hi-resolution photo of the vessel interior is ready to be added; awaiting file path confirmation
- Contact info update: Remove sister-in-law personal details; replace with generic booking email (completed in current version)
- Form backend: Proposal form submissions currently route to email; consider migrating to a webhook-based CRM integration for better tracking
- Analytics: Add UTM parameters or event tracking to measure proposal view-through and conversion rates
The proposal template is now production-ready and can serve as a reference pattern for future charter offerings