```html

Cross-Site GA Contamination & Modal Widget Refactor: Anatomy of a Production Incident

Last week we discovered a critical issue: Google Search Console was reporting that quickdumpnow.com (QDN) was being accessed and tracked via jadasailing@gmail.com—the business account for sailjada.com (JADA). While GA tags themselves weren't technically mixed on homepages, the ownership and access control was broken, and we had secondary GA tag gaps on QDN subpages. This post covers the incident investigation, the root cause analysis, and the architectural refactor we performed to fix it.

The Problem: Ownership vs. Implementation

When we audited GA tags across both properties, the initial findings looked clean at first glance:

  • sailjada.comG-N6HKL4KLKT (JADA GA4 property)
  • queenofsandiego.comG-N6HKL4KLKT (same, shared business account—correct)
  • quickdumpnow.comG-539T97NM1Z (QDN GA4 property, different account)

The homepages themselves had the right tags. But when we scanned subpages—specifically /book, /track, and service area pages like /service-areas/carlsbad/index.html—we found:

  1. QDN subpages missing GA entirely: Critical booking and tracking pages had no GA tag at all
  2. GSC ownership problem: The QDN property in Google Search Console was accessible only via jadasailing@gmail.com, not the QDN business account
  3. Secondary issue discovered during crew page audit: A separate modal widget bug on sailjada.com/crew/ was making the booking flow look unprofessional

Root Cause: Template Generation and Access Control Drift

The GA tag gaps on QDN subpages traced back to how pages were being generated. We found two bulk-injection points:

  • /Users/cb/Documents/repos/tools/ensure_ga_tag.py — responsible for guaranteeing GA tags on production files
  • /Users/cb/Documents/repos/tools/inject_structured_data.py — handling SAILJADA subpage generation specifically

When QDN pages were pushed to S3 (s3://quickdumpnow-prod-web/), they bypassed the ensure_ga_tag pipeline. This happened because the deployment script (/Users/cb/Documents/repos/tools/deploy_qos.sh) didn't enforce tag validation for QDN properties—it was originally written for JADA only.

The GSC ownership issue was organizational: when quickdumpnow.com's domain was initially set up, it was added to the JADA Google Workspace account instead of creating a separate admin account. This meant all Search Console and Analytics permissions flowed through jadasailing@gmail.com, creating a security and audit nightmare.

The Crew Page Modal Widget Bug

During the investigation, we discovered why sailjada.com/crew/ looked broken. The booking modal should appear when users click any CTA button (Reserve, Check Availability, etc.), but the modal wasn't being loaded on subpages—only on the homepage.

The root cause was a shared JavaScript asset problem. The modal widget was defined inline in the JADA homepage HTML, but subpages included a separate `jada-modal.js` that had several issues:

  • Missing global function references for Stripe and GA event tracking
  • Incorrect anchor ID mapping for the modal container
  • Relative path to a QR code image that didn't exist in subpage contexts
  • CSS brace mismatch in f-string template rendering (tracked in the generator)

This is where the f-string bug manifested. In inject_structured_data.py, a line like:

css_block = f"{base_css} .modal {{ display: block; }}"

was being generated without proper brace escaping, resulting in malformed CSS when the template expanded.

Our Fix: Three-Pronged Approach

1. GA Tag Enforcement in Deployment

We modified deploy_qos.sh to call a lint check before any S3 upload:

#!/bin/bash
# deploy_qos.sh (excerpt)

BUCKET="${1}"
SOURCE_DIR="${2}"

# Run GA tag validation
python3 tools/lint_format_template.py --check-ga "${SOURCE_DIR}" || {
  echo "ERROR: GA tag validation failed. Aborting deployment."
  exit 4
}

# Proceed with S3 sync
aws s3 sync "${SOURCE_DIR}" "s3://${BUCKET}/" --delete

The lint script (tools/lint_format_template.py) now enforces that every HTML file has either a GA4 tag matching its domain's property OR a documented exemption. QDN pages must have G-539T97NM1Z, JADA pages must have G-N6HKL4KLKT.

2. Shared Modal Widget Asset Creation

We extracted the modal widget logic from the homepage and created a universal jada-modal.js asset served from CloudFront distribution d2xjf4k5n7p9r2q1.cloudfront.net:

// jada-modal.js (shared asset)
(function() {
  window.openJADABookingModal = function(options) {
    // Initialize Stripe if needed
    if (!window.Stripe) {
      console.error('Stripe.js not loaded');
      return;
    }
    // Show modal, trigger GA event
    gtag('event', 'booking_modal_open', {
      page_location: window.location.href
    });
    // ... modal rendering logic
  };
})();

Every subpage now includes a single script tag pointing to this asset, plus a small inline script that binds all CTA buttons to the modal trigger.

3. QDN Subpage GA Tag Patching

We synced down the five QDN pages that were missing GA tags from S3 (s3://quickdumpnow-prod-web/):

  • /book/index.html
  • /track/index.html
  • /service-areas/carlsbad/index.html
  • /service-areas/oceanside/index.html
  • /service-areas/escondido/index.html

Each received the correct GA4 measurement ID injected into the <head> section, then redeployed to staging for validation before promotion to production.

Infrastructure & Deployment Flow

Our deployment pipeline now looks