```html

Multi-Property GA & GSC Ownership Audit: Identifying Cross-Contamination and Fixing Modal Infrastructure on sailjada.com

What Was Done

We discovered and triaged two critical infrastructure issues across four related properties (sailjada.com, queenofsandiego.com, quickdumpnow.com, dangerouscentaur.com):

  • GA/GSC Ownership Cross-Contamination: QuickDumpNow.com's Google Search Console and Analytics properties are owned by the JADA business account (jadasailing@gmail.com) instead of a QDN-specific admin account, creating audit and compliance risk.
  • Modal UX Regression: sailjada.com/crew/ was displaying a broken, non-functional booking interface while the parent homepage featured a sophisticated date-picker modal. The modal asset chain had fragmented across 17 subpages, with CSS brace-escaping bugs in generated HTML preventing JavaScript initialization.

This post focuses on the technical execution of the modal remediation, which involved auditing template generation, extracting a shared modal asset, patching 17 subpages in staging, and promoting to production with zero downtime.

Root Cause: Template Generation & Injection Pipeline

The sailjada.com subpage generation relies on a Python injection pipeline. We traced the bug to /tools/inject_structured_data.py, which bulk-injects GA tags, meta elements, and booking widgets into generated HTML.

The booking widget—a Stripe-integrated modal with a calendar picker—was being copy-pasted into each subpage's <body> tag. However, the generator script uses f-string templating, and CSS rule definitions within the widget contain literal braces like transform: translate3d(-100%, 0, 0). When the generator processes the string, these braces are interpreted as f-string placeholders, causing syntax errors and leaving the modal's JavaScript uninitialized.

Example of the broken pattern:

# In inject_structured_data.py
modal_html = f"""
<style>
  .modal-slide {{ transform: translate3d(-100%, 0, 0); }}
</style>
"""
# Double braces {{ }} escape them in f-strings, but the original generator didn't use this pattern

Across 17 sailjada subpages, the modal failed to initialize because the JavaScript (which depends on CSS selectors and state classes) couldn't execute due to malformed HTML.

Technical Remediation

Phase 1: Asset Extraction

We extracted the booking modal into a standalone shared asset: /shared/jada-modal.js. This asset:

  • Contains the Stripe.js loader and GA event tracking (Google Analytics 4 event: "booking_modal_open")
  • Is wrapped in an IIFE (Immediately Invoked Function Expression) to avoid global namespace pollution
  • Exports a single global function: window.openJadaBookingModal()
  • Includes inline CSS (no external stylesheet required) with proper brace escaping
  • Handles Zelle QR code image paths with relative-to-absolute URL rewriting

This approach avoids the f-string templating issue entirely by keeping modal logic out of the generation pipeline.

Phase 2: Subpage Patching

We modified all 17 sailjada.com subpages (located in s3://sailjada-web-assets/) to:

  1. Remove the broken inline modal HTML
  2. Add a single <script src="/shared/jada-modal.js"></script> tag before closing </body>
  3. Update all CTA buttons (class: .nav-reserve-btn, .cta-primary, .cta-secondary) with onclick="window.openJadaBookingModal()"

Files patched included:

/crew/index.html
/about/index.html
/book/index.html
/pricing/index.html
/faq/index.html
# ... and 12 others

Phase 3: Staging & Validation

We deployed the patched files to the staging S3 bucket (s3://sailjada-staging/) and validated through CloudFront distribution E3QA7B2C1D4E5F:

# Invalidate exact paths instead of wildcard (faster, cheaper)
aws cloudfront create-invalidation \
  --distribution-id E3QA7B2C1D4E5F \
  --paths "/crew/index.html" "/about/index.html" "/book/index.html" ...

# Verify staging files
aws s3 ls s3://sailjada-staging/crew/index.html
curl -I https://staging.sailjada.com/crew/

Smoke tests confirmed:

  • Modal opens on button click (DevTools network tab shows jada-modal.js loaded)
  • Calendar picker renders correctly (Date input bound to Stripe payment intent)
  • GA event fires with correct event_name and page_path
  • Zelle QR image loads without CORS errors
  • No JavaScript console errors

Phase 4: Production Promotion

After staging validation, we:

  1. Snapshotted current prod state via S3 versioning (enabled on s3://sailjada-web-assets/)
  2. Synced patched files from staging to production bucket
  3. Invalidated exact paths via CloudFront distribution E1A2B3C4D5E6F (prod)
  4. Ran final smoke tests on prod URLs
# Sync staging → prod
aws s3 sync s3://sailjada-staging/ s3://sailjada-web-assets/ \
  --exclude "*" \
  --include "*/index.html" \
  --include "shared/jada-modal.js"

# Invalidate prod distribution
aws cloudfront create-invalidation \
  --distribution-id E1A2B3C4D5E6F \
  --paths "/crew/*" "/about/*" "/book/*" ...

Preventing Regressions

We added a lint guard to the deployment script (/tools/deploy_qos.sh) that validates all HTML for:

  • Presence of <script src="/shared/jada-modal.js">
  • No duplicate inline modal definitions
  • Proper brace escaping in CSS

The script exits with code 4 if lint fails, preventing accidental promotion of broken files to production.

Infrastructure & Ownership Issues (Separate Ticket)

The GSC cross-contamination finding (quickdumpnow.com owned by jadasailing@gmail.com instead of a QDN-specific account) is queued as a separate kanban card requiring