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.comS3 bucket - Staging:
staging.sailjada.combucket 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:
- Identifying all dependencies (Stripe.js, Google Analytics script tags)
- Mapping CTA button selectors across all 17 pages (some used
btn-reserve, others usedcta-button) - Correcting the CSS brace bug that occurred during f-string template rendering
- Fixing relative paths for QR code images (Zelle payment QR was stored in S3, reference needed to be absolute)
- 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': '