From daf2d7460a2b27691509fb1668db4c3d6c6deade Mon Sep 17 00:00:00 2001 From: Devin Willis Date: Wed, 26 Aug 2026 15:22:27 -0400 Subject: [PATCH] Lighter email header, open tracking, and full funnel events on the site --- ops/founders-break-reminder.js | 18 +++++++++++-- ops/founders-break-rsvp.js | 19 ++++++++++++-- public/js/main.js | 46 ++++++++++++++++++++++++++++++++-- views/index.ejs | 22 ++++++++-------- 4 files changed, 88 insertions(+), 17 deletions(-) diff --git a/ops/founders-break-reminder.js b/ops/founders-break-reminder.js index 608fb68..0d6c71c 100644 --- a/ops/founders-break-reminder.js +++ b/ops/founders-break-reminder.js @@ -64,7 +64,20 @@ function esc(s) { .replace(/"/g, """).replace(/'/g, "'"); } -var HERO = "https://nyc3.digitaloceanspaces.com/corben-sites/images/c23ea202-5442-41b4-9a1b-1586e81c50eb/e7119448-83ba-4da4-95d8-b64045c21160/email-header-trim.png"; +var HERO = "https://nyc3.digitaloceanspaces.com/corben-sites/images/c23ea202-5442-41b4-9a1b-1586e81c50eb/5b98d309-7283-4202-bf43-263dbcec74c2/email-header-web.png"; +var SITE_ID = "4ae63819-3f63-4fea-b0de-eb1e5befe607"; +var PIXEL = "https://api.corben.world/analytics/open.gif"; + +// 1x1 open pixel. Opens skew high because Apple Mail Privacy Protection and +// Gmail's proxy prefetch images, so read it as a ceiling, not a headcount. +function openPixel(kind, who, when) { + return ''; +} + var UNSUB = "https://api.corben.world/campaigns/unsubscribe?user_id=c23ea202-5442-41b4-9a1b-1586e81c50eb&email="; var SANS = "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif"; @@ -99,7 +112,7 @@ function shell(opts) { 'Unsubscribe from these emails' + ' and we will not email you about the Break again.' + '

' + - ''; + '' + (opts.pixel || '') + ''; } // The date and place, sized like an invitation rather than a spec sheet. @@ -178,6 +191,7 @@ var campaign = await tool("create_campaign", { imageAlt: "A blueprint drawing of a pool table and the break shot.", body: body, unsubscribeFor: '{{email}}', + pixel: openPixel('reminder', '{{email}}', target), }), text_body: "The Break is tomorrow, " + when + ".\n\n" + VENUE + ", " + STREET + ", " + CITY + "\n7:00 to 9:00 PM. Free entry, 18 and over.\n\nBring a valid ID.\n\n" + SITE, diff --git a/ops/founders-break-rsvp.js b/ops/founders-break-rsvp.js index 02e74ae..15e14d5 100644 --- a/ops/founders-break-rsvp.js +++ b/ops/founders-break-rsvp.js @@ -43,7 +43,20 @@ function prettyDate(iso) { }).format(d); } -var HERO = "https://nyc3.digitaloceanspaces.com/corben-sites/images/c23ea202-5442-41b4-9a1b-1586e81c50eb/e7119448-83ba-4da4-95d8-b64045c21160/email-header-trim.png"; +var HERO = "https://nyc3.digitaloceanspaces.com/corben-sites/images/c23ea202-5442-41b4-9a1b-1586e81c50eb/5b98d309-7283-4202-bf43-263dbcec74c2/email-header-web.png"; +var SITE_ID = "4ae63819-3f63-4fea-b0de-eb1e5befe607"; +var PIXEL = "https://api.corben.world/analytics/open.gif"; + +// 1x1 open pixel. Opens skew high because Apple Mail Privacy Protection and +// Gmail's proxy prefetch images, so read it as a ceiling, not a headcount. +function openPixel(kind, who, when) { + return ''; +} + var UNSUB = "https://api.corben.world/campaigns/unsubscribe?user_id=c23ea202-5442-41b4-9a1b-1586e81c50eb&email="; var SANS = "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif"; @@ -78,7 +91,7 @@ function shell(opts) { 'Unsubscribe from these emails' + ' and we will not email you about the Break again.' + '

' + - ''; + '' + (opts.pixel || '') + ''; } // The date and place, sized like an invitation rather than a spec sheet. @@ -225,6 +238,7 @@ if (attending === "no") { imageAlt: "A blueprint drawing of a pool table and the break shot.", body: backBody, unsubscribeFor: encodeURIComponent(email), + pixel: openPixel('confirm-returning', encodeURIComponent(email), eventDate), }), text: "You are confirmed for this week, " + first + ".\n\n" + when + ", 7:00 to 9:00 PM\n" + VENUE + "\n" + STREET + ", " + CITY + "\nFree entry. 18 and over, so bring an ID.\n\n" + SITE, @@ -249,6 +263,7 @@ if (attending === "no") { imageAlt: "A blueprint drawing of a pool table and the break shot.", body: confirmBody, unsubscribeFor: encodeURIComponent(email), + pixel: openPixel('confirm-first', encodeURIComponent(email), eventDate), }), text: "You are on the list, " + first + ". There is nothing else to do before then.\n\n" + when + ", 7:00 to 9:00 PM\n" + VENUE + "\n" + STREET + ", " + CITY + diff --git a/public/js/main.js b/public/js/main.js index 6e9106e..cdae021 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -148,6 +148,44 @@ window.addEventListener("pagehide", function () { clearInterval(timer); }); } + + /* ── Funnel milestones ─────────────────────────────────── + The Corben tracker injected into every site already records pageviews, + clicks on [data-track], form_start, form_submit, scroll depth and + engagement time. These are the steps it cannot see on its own: reaching + the RSVP block, and whether the submission actually succeeded. Same + public /analytics/events endpoint the tracker posts to. */ + + var SITE_ID = (document.currentScript && document.currentScript.dataset.site) || + (document.querySelector('script[data-site]') || {}).dataset; + if (SITE_ID && typeof SITE_ID === "object") SITE_ID = SITE_ID.site; + + function track(type, data) { + if (!SITE_ID) return; + var body = JSON.stringify({ + site_id: SITE_ID, + events: [{ event_type: type, page_path: location.pathname, event_data: data || {}, ts: Date.now() }], + }); + // keepalive so an event fired as the tab closes still lands. + try { + fetch("https://api.corben.world/analytics/events", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: body, keepalive: true, mode: "cors", + }).catch(function () {}); + } catch (e) {} + } + + // Did they ever actually reach the RSVP block? Fires once. + var rsvpSection = document.getElementById("rsvp"); + if (rsvpSection) { + var seen = new IntersectionObserver(function (entries, obs) { + if (!entries[0].isIntersecting) return; + track("rsvp_reached", { source: new URLSearchParams(location.search).get("utm_source") || "direct" }); + obs.disconnect(); + }, { threshold: 0.3 }); + seen.observe(rsvpSection); + } + /* ── RSVP ── */ var form = document.getElementById("rsvp-form"); if (!form) return; @@ -216,9 +254,11 @@ }) .then(function (body) { if (body && body.ok === false) throw new Error(body.error || "rejected"); + track("rsvp_completed", { via: "form" }); showDone(); }) - .catch(function () { + .catch(function (err) { + track("rsvp_failed", { via: "form", reason: String(err && err.message || "unknown").slice(0, 80) }); submit.removeAttribute("aria-busy"); status.textContent = "That did not go through. Try again, or email contact@onetouch.studio and we will add you by hand."; @@ -279,9 +319,11 @@ .then(function (res) { if (!res.ok) throw new Error("HTTP " + res.status); return res.json().catch(function () { return {}; }); }) .then(function (body) { if (body && body.ok === false) throw new Error("rejected"); + track("rsvp_completed", { via: "reminder-email", answer: answer }); showDone(answer); }) - .catch(function () { + .catch(function (err) { + track("rsvp_failed", { via: "reminder-email", answer: answer, reason: String(err && err.message || "unknown").slice(0, 80) }); submit.removeAttribute("aria-busy"); status.textContent = "That link did not go through. Hit RSVP below and you are set."; }); diff --git a/views/index.ejs b/views/index.ejs index 56cfbc3..5de834b 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -158,7 +158,7 @@ Details What to expect The venue - RSVP + RSVP @@ -171,8 +171,8 @@

<%= site.name %>

<%= heroLine %>

- RSVP - What to expect + RSVP + What to expect
@@ -215,13 +215,13 @@ <% details.forEach(function (d) { %>
<%= d.label %>
-
<% if (d.href) { %><%= d.value %><% } else { %><%= d.value %><% } %>
+
<% if (d.href) { %><%= d.value %><% } else { %><%= d.value %><% } %>
<% }) %>

class="is-live"<% } %>><%= isTonight ? "Happening tonight" : `Next: ${nextShort}` %> - Add the whole series to your calendar + Add the whole series to your calendar

@@ -259,10 +259,10 @@

We play at <%= V.name %>.

<%= V.note %>

- <%= V.street %>
<%= V.city %>, <%= V.state %> <%= V.zip %>
+ <%= V.street %>
<%= V.city %>, <%= V.state %> <%= V.zip %>

- @<%= V.instagram %> + @<%= V.instagram %> Pouring since <%= V.established %>

@@ -307,7 +307,7 @@

A few words is plenty. It helps us introduce you to the right people.

- +

One email a week at most. Unsubscribe any time.

@@ -329,7 +329,7 @@ @@ -344,10 +344,10 @@ Hosted by <%= site.host.name %> <%= site.host.email %> - <%= V.name %>, <%= V.street %> + <%= V.name %>, <%= V.street %> - +