```html

Fixing Cross-Site Analytics Pollution and Restoring the SailJada Booking Modal

This post documents a production incident and its resolution across four distinct web properties managed under the JADA Sailing umbrella. The issue involved two separate but related problems: Google Analytics tag contamination across different business entities, and a broken booking widget on the SailJada crew page that had regressed into a non-functional state.

The Problem

Google Search Console revealed that quickdumpnow.com was being accessed via the jadasailing@gmail.com account, indicating that QDN's analytics and search console ownership was tied to the JADA business account rather than its own administrative contact. Additionally, several QDN subpages (/book, /track, /service-areas/carlsbad/) were completely missing their GA tag (G-539T97NM1Z), while the sailjada.com crew booking page had degraded into a non-functional state with improperly rendered modal windows.

The crew page should have displayed a professional date-picker modal matching the sophisticated booking interface on the queenofsandiego.com homepage, but instead was showing broken styling and non-functional buttons.

Technical Analysis and Root Cause

I performed a comprehensive audit across all four properties:

  • sailjada.com: GA tag G-N6HKL4KLKT (JADA property) — correct
  • queenofsandiego.com: GA tag G-N6HKL4KLKT (JADA property) — correct (same business)
  • quickdumpnow.com: GA tag G-539T97NM1Z (QDN property) — correct on homepage, but missing on critical subpages
  • dangerouscentaur.com: GA tag G-GGP987FJSW — correct
  • 86from.com: GA tag G-YJWFKWVWNQ — correct
  • burialsatseasandiego.com: UA-175171552-1 (legacy UA format) — outdated

The analytics tags themselves were not cross-polluted; the real issue was ownership and access control. QDN's Search Console property was accessible only via the JADA business account, creating a security and administrative boundary violation.

For the SailJada crew page, the investigation revealed that a shared modal widget (jada-modal.js) was being injected across 17 subpages via template generation. The modal definition had multiple issues:

  • CSS brace syntax errors in f-strings during template rendering
  • Missing global function definitions (Stripe initialization, event handlers)
  • Broken QR code relative paths
  • Inconsistent CTA button selectors across different page templates

Infrastructure Changes

File Structure and Deployment Pipeline

The source files involved in this fix:

  • /Users/cb/Documents/repos/tools/lint_format_template.py — Template linting and validation
  • /Users/cb/Documents/repos/tools/deploy_qos.sh — Deployment orchestration with lint guards
  • /Users/cb/Documents/repos/sailjada/crew/index.html — Main crew page
  • 16 additional SailJada subpages under /Users/cb/Documents/repos/sailjada/*/index.html

S3 buckets involved:

  • Production: sailjada.com S3 bucket
  • Staging: staging.sailjada.com bucket for pre-validation
  • Progress tracking: DynamoDB table with kanban card state

CloudFront distributions:

  • sailjada.com production CF distribution (invalidation required for immediate cache purge)
  • staging.sailjada.com CF distribution (separate staging CF for safe testing)

Resolution: Modal Widget Extraction and Rebuild

The booking modal was originally embedded inline in the queenofsandiego.com homepage. To make it reusable across all SailJada subpages, I extracted the modal into a separate shared asset:

// Extracted shared asset: jada-modal.js
// Includes:
// - Modal HTML structure with date picker
// - CSS styling (with corrected brace syntax)
// - Event handlers wrapped in IIFE to avoid global scope pollution
// - Stripe initialization
// - GA event tracking
// - QR code image reference corrections

The extraction required:

  1. Identifying all dependencies (Stripe.js, Google Analytics script tags)
  2. Mapping CTA button selectors across all 17 pages (some used btn-reserve, others used cta-button)
  3. Correcting the CSS brace bug that occurred during f-string template rendering
  4. Fixing relative paths for QR code images (Zelle payment QR was stored in S3, reference needed to be absolute)
  5. Rebuilding the JavaScript with proper IIFE wrapper to prevent global leakage

Deployment Strategy: Staging-First Validation

To prevent another production incident, the deployment followed a strict staged approach:

  • Stage 1: Deploy to staging bucket (staging.sailjada.com)
  • Stage 2: Invalidate specific CloudFront paths (not wildcards, which are expensive and slow)
  • Stage 3: Smoke test through staging CF distribution URL
  • Stage 4: Verify all 17 subpages render correctly with working modal
  • Stage 5: Snapshot production state before promotion
  • Stage 6: Promote verified files to production bucket
  • Stage 7: Invalidate exact production CF paths
  • Stage 8: Final smoke test on production URL

Lint Guard Integration

The deploy_qos.sh script was modified to include a mandatory lint check:

#!/bin/bash
# Lint all templates before deployment
python3 /Users/cb/Documents/repos/tools/lint_format_template.py \
  --source /Users/cb/Documents/repos/sailjada \
  --output /tmp/qos_build

if [ $? -ne 0 ]; then
  echo "Lint check failed. Refusing deployment."
  exit 4
fi

# Proceed with S3 sync and CF invalidation...

This ensures no malformed templates can reach staging or production.

Cross-Contamination Scanning

To verify no other pages were affected, I ran a comprehensive Python scan across all subpages:

python3 << 'EOF'
import os
import re

properties = {
    'sailjada': 'G-N6HKL4KLKT',
    'queenofsandiego': '