Staging a Dynamic Event Section with Email Capture: Zuniga Shoals "Ocean Man" Build

This post documents the process of adding a new event marketing section to the Zuniga Shoals website without touching production, integrating email capture to an existing Google Apps Script endpoint, and identifying a downstream automation issue in the progress-tracking infrastructure.

What Was Done

We deployed a new event section—currently titled "Zuniga Days" (pending rename to "Ocean Man")—to a staging path on the production S3 bucket. The implementation added approximately 212 lines of HTML, CSS, and JavaScript while preserving the production index.html untouched through additive S3 prefixing and a full backup snapshot.

  • Source file: /tmp/zuniga-staging.html (local working copy)
  • Staging deployment target: s3://zunigashoals.com/_staging/index.html
  • Production snapshot: /tmp/zunigashoals.com__index.html__2026-05-26T17-31-10Z.prod-snapshot
  • Live staging URL: https://zunigashoals.com/_staging/index.html

Technical Details: What Was Built

Sticky Ribbon Component

A fixed-position banner at the top of the page introduces the event with the line "Zuniga Days · the last free open anchorage in San Diego." The ribbon uses CSS positioning and z-index layering to remain visible during scroll without obscuring critical navigation. The implementation includes responsive breakpoints to collapse gracefully on mobile viewports.

Event Section Structure

The new section (assigned id="zd" for scoping) sits between the hero and shop sections and contains:

  • Pull quote: "Who's in charge here?" — NO ONE. (establishes the anarchic, permissive brand voice)
  • Three-pillar callout: PERMIT none required, COST free to anchor, RULES see above (legal and operational clarity)
  • Six activity tiles: Each tile uses semantic HTML with flexbox layout for consistent sizing and alignment. Activities include Unsanctioned Dinghy Races, Boat-Diving Contest, Raftup & Rage, Anchor & Anthem, Flag Off Your Stern, and Zuniga Shoals T-Shirt Contest.
  • Value proposition callout: "What you get for free" highlights visible landmarks (Hotel Del, Coronado, Point Loma)
  • Barrier-to-entry removal: "No boat? No problem" section with copy directing visitors to find a dinghy owner
  • Email capture form: Labeled "Tell me the day. I'll be there." with a single email input field

Email Capture Integration

The email form posts to the existing Google Apps Script endpoint already live on the site. The submission payload includes:

{
  "action": "zuniga_days_signup",
  "email": "[user input]",
  "timestamp": "[ISO 8601 timestamp]"
}

This action value allows the existing GAS webhook to route the signup event into the correct spreadsheet or downstream service without requiring a separate deployment. The form includes client-side validation (HTML5 email input type) and prevents duplicate submissions during processing.

CSS Approach

All new styles use a .zd* BEM-style prefix (e.g., .zd-section, .zd-tile, .zd-ribbon) to avoid collisions with existing site CSS. This naming convention makes it trivial to locate, refactor, or remove the entire Zuniga Days section in a future deploy. CSS custom properties (--zd-*) are used for colors, spacing, and typography to maintain design consistency with the broader site palette while allowing quick visual iteration.

Infrastructure: S3 and CloudFront Strategy

S3 Bucket Structure

The Zuniga Shoals site is hosted in s3://zunigashoals.com with a CloudFront distribution (ID not disclosed here for security) fronting all requests. The staging deployment uses an additive path prefix rather than a separate bucket or domain:

  • Production: s3://zunigashoals.com/index.html (served via CloudFront as https://zunigashoals.com/)
  • Staging: s3://zunigashoals.com/_staging/index.html (served as https://zunigashoals.com/_staging/index.html)

Why this approach: Staging on a path prefix within the same bucket avoids Route53 CNAME churn, separate CloudFront distribution costs, and the complexity of managing cross-domain cookie/session state. The underscore prefix signals to search engines and humans that this is a hidden staging environment, not a public page.

No CloudFront Invalidation Required

Because the staging content lives at a unique path that has never been served before, CloudFront has no cached objects to invalidate. The first request to /_staging/index.html will hit S3 directly and cache the response. This also means rolling back is as simple as deleting the staging object—production remains completely untouched.

Production Snapshot Strategy

Before any deployment, we downloaded the current production index.html to a timestamped local file:

aws s3 cp s3://zunigashoals.com/index.html \
  /tmp/zunigashoals.com__index.html__2026-05-26T17-31-10Z.prod-snapshot \
  --no-sign-request

This snapshot serves as an instant rollback point and a baseline for diff comparison if we need to understand what changed between versions.

Key Technical Decisions

Additive Rather Than Mutative

We downloaded production, appended the new section and styles, and deployed to staging—rather than modifying production in place. This approach ensures zero downtime and zero risk of partial deploys affecting live traffic.

Email Capture to Existing GAS Endpoint

Rather than provisioning a new webhook or backend service, the form posts to the existing Google Apps Script already deployed on the site. The action: "zuniga_days_signup" field allows Chloe's team to configure routing rules inside the GAS script without code changes, enabling rapid iteration on downstream handling (spreadsheet, Slack notification, etc.).

CSS Prefixing for Long-Term Maintainability

The .zd-* prefix makes it trivial to search, audit, or eventually remove the entire Zuniga Days section from the codebase. This is especially valuable if the event becomes a permanent fixture (styles merge into the global sheet) or if it's seasonal and needs to be toggled on and off.

Open Questions and Next Steps

  • Event name: "Zuniga Days" is the working title; the preferred branding is "The Zuniga Shoals Ocean Man." Update copy and class names once finalized.
  • Event dates: