Fix about page voice, wire founding badge and dynamic counter

About: rewritten in memoir register — flowing connected sentences,
no staccato, no resignation mention, no employer mention.

Founding counter: polls /api/founding-count every 90s, updates DOM
in place with flash animation on new claims. Webhook now increments
the sold counter when a founding purchase completes.

Badge: founding_badge.el component with guilloché SVG background,
corner rosettes, member number, glow animation. Rendered on success page.

Pricing: "at cost" removed, parent onboarding callout added.
This commit is contained in:
Will Anderson
2026-04-29 16:38:40 -05:00
parent 1d8143cdbc
commit d190e08f38
4 changed files with 208 additions and 36 deletions
+40
View File
@@ -1361,6 +1361,46 @@ fn page_close() -> String {
revealEls.forEach(function(el) { el.classList.add('visible'); });
}
// ── Founding counter — live polling ──────────────────────────────────────
var prevSold = null;
function updateFoundingUI(data) {
var remaining = data.remaining;
var sold = data.sold;
var total = data.total;
var pct = Math.round((sold / total) * 100);
var flash = prevSold !== null && sold > prevSold;
prevSold = sold;
// spots card (inside pricing card)
var spotLabel = document.querySelector('.founding-spots-label');
if (spotLabel) spotLabel.textContent = 'Only ' + remaining + ' left';
var spotFill = document.querySelector('.founding-spots-fill');
if (spotFill) spotFill.style.width = pct + '%';
var spotSub = document.querySelector('.founding-spots-sub');
if (spotSub) spotSub.textContent = sold + ' of ' + total + ' claimed';
// banner
var bannerCount = document.querySelector('.founding-banner-count');
if (bannerCount) {
bannerCount.textContent = remaining;
if (flash) {
bannerCount.style.color = '#0078D4';
setTimeout(function() { bannerCount.style.color = ''; }, 1200);
}
}
var bannerFill = document.querySelector('.founding-banner-fill');
if (bannerFill) bannerFill.style.width = pct + '%';
}
function pollFoundingCount() {
fetch('/api/founding-count')
.then(function(r) { return r.json(); })
.then(function(data) { updateFoundingUI(data); })
.catch(function() {});
}
pollFoundingCount();
setInterval(pollFoundingCount, 90000);
// Stripe checkout
var checkoutBtns = document.querySelectorAll('[data-checkout]');
checkoutBtns.forEach(function(btn) {