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:
+15
-32
@@ -1,8 +1,9 @@
|
||||
// about.el — About page for Neuron landing.
|
||||
//
|
||||
// Returns the full HTML body content for /about.
|
||||
// Wrapped by page_open() / page_close() from styles.el.
|
||||
// Written from Will's first-person perspective.
|
||||
// Written from Will's first-person perspective, in his memoir register:
|
||||
// flowing connected sentences, specificity as credibility, emotionally
|
||||
// hard things stated in a single clause and moved past. Short sentences
|
||||
// are structural punctuation — used sparingly, at genuine rupture only.
|
||||
|
||||
fn about_page() -> String {
|
||||
return "
|
||||
@@ -24,22 +25,18 @@ fn about_page() -> String {
|
||||
<main id=\"about\" style=\"padding: clamp(7rem, 18vh, 11rem) 2.5rem clamp(5rem, 12vh, 8rem);\">
|
||||
<div style=\"max-width: 700px; margin: 0 auto;\">
|
||||
|
||||
<!-- Lead -->
|
||||
<p class=\"label animate-up-1\" style=\"margin-bottom: 2rem;\">About</p>
|
||||
<h1 class=\"display-lg animate-up-2\" style=\"margin-bottom: 2.5rem; max-width: 22rem;\">
|
||||
Hi. I'm Will.
|
||||
</h1>
|
||||
<div class=\"navy-line-left animate-up-3\" style=\"width: 4rem; margin-bottom: 3rem;\"></div>
|
||||
|
||||
<!-- Photo + intro -->
|
||||
<!-- Photo + opening -->
|
||||
<div class=\"reveal\" style=\"display: flex; align-items: flex-start; gap: 2.5rem; margin-bottom: 3rem; flex-wrap: wrap;\">
|
||||
<img src=\"/assets/will.png\" alt=\"Will Anderson\" style=\"width: 160px; height: 160px; border-radius: 50%; object-fit: cover; flex-shrink: 0;\">
|
||||
<div style=\"flex: 1; min-width: 260px;\">
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9; margin-bottom: 1.5rem;\">
|
||||
I grew up in Fort Smith, Arkansas. Rural poverty, parents with addiction, moved about thirty times before I was fifteen. I left home as a teenager, stayed with friends, finished high school, went to college. At fourteen I found software at the public library — writing C++ because it was the first thing that ever made complete sense to me.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9;\">
|
||||
I dropped out. Worked. Went back as an adult to finish my degree.
|
||||
I grew up in Fort Smith, Arkansas, in the kind of instability where home is a moving target — roughly thirty addresses before I was fifteen, parents struggling with addiction, the material precarity that comes with all of that. I left when I was old enough to go, stayed with friends until I finished high school, found my way to college. At fourteen I'd already found software, writing C++ at the public library because it was the first thing in my life that responded to precision with correctness, and that property turned out to matter more to me than almost anything else.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,14 +44,11 @@ fn about_page() -> String {
|
||||
<!-- Career -->
|
||||
<div class=\"reveal\" style=\"margin-bottom: 3rem;\">
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9; margin-bottom: 1.5rem;\">
|
||||
I spent nearly twenty years building software across every kind of organization — international consulting, early-stage startups, Fortune 5 enterprises. Logistics, retail, entertainment, hospitality, industrial automation, insurance, healthcare, financial services. I trained under Juval Löwy at IDesign and worked with him as a consultant from 2015 to 2021. Most recently, principal engineer at one of the largest retailers in the world.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9;\">
|
||||
I resigned on April 29, 2026. Not because it was a good career move. Because there was something I needed to build.
|
||||
I dropped out of college, worked, went back as an adult to finish my degree, and built my skills across nearly twenty years and every kind of organization — international consulting, early-stage startups, Fortune 5 enterprises. Logistics, retail, entertainment, hospitality, industrial automation, insurance, healthcare, financial services. I trained under Juval Löwy at IDesign and worked with him as a consultant from 2015 to 2021, which is where I learned what it actually means to practice software engineering as a discipline rather than an improvisation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- The insight — blockquote -->
|
||||
<!-- Blockquote -->
|
||||
<blockquote class=\"reveal\" style=\"
|
||||
border-left: 3px solid var(--navy);
|
||||
padding: 0.5rem 0 0.5rem 2rem;
|
||||
@@ -77,10 +71,10 @@ fn about_page() -> String {
|
||||
<div class=\"reveal\" style=\"margin-bottom: 3rem;\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">What I saw</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9; margin-bottom: 1.5rem;\">
|
||||
Nearly twenty years of software built, watched, and dissected. And I watched AI go from promise to product. The same mistake keeps getting made: tools built to serve organizations, not people. Engagement over relationship. Features over memory. Policies over values.
|
||||
Across nearly twenty years I watched software get built at organizations with real stakes and real consequences, and I watched AI go from promise to product — watched the same mistake get made at each iteration: tools built to serve the organization's needs, not the person's. Engagement over relationship. Features over memory. Policies where values should be. The fundamental premise that you are a user, not a person, has been so thoroughly baked into the architecture of every major AI system that it doesn't register as a choice anymore. It's treated as the natural condition of the technology.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9;\">
|
||||
Every conversation starts from zero. You are a user, not a person. That's not an accident — it's a design decision. And it is the wrong one.
|
||||
It is not. It is a design decision. And it is the wrong one.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -90,30 +84,19 @@ fn about_page() -> String {
|
||||
<div class=\"reveal\" style=\"margin-bottom: 3rem;\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">What I built</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9; margin-bottom: 1.5rem;\">
|
||||
Neuron is what I built. Not a startup in the traditional sense — no team, no funding, no press release. One person, nearly two years, and a conviction that this can be done differently.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9; margin-bottom: 1.5rem;\">
|
||||
I wrote the programming language the core runs in. I wrote the memory architecture. I built the inference infrastructure. I did this because the existing tools weren't sufficient for what I was trying to build — so I built those too.
|
||||
Neuron is what I built in response to that. Not a startup in the traditional sense — no team, no funding, no press release — one person, nearly two years of work, and a conviction that this can be done differently. I wrote the programming language the core runs in, I wrote the memory architecture, I built the inference infrastructure, because the tools that existed weren't sufficient for what I was trying to build and so I built those too.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9;\">
|
||||
Use it long enough and you'll understand why nothing I could have built on top of existing infrastructure would have gotten there. Some things have to be built from the ground up to be built right.
|
||||
Use it long enough and you'll understand why I couldn't have gotten there on top of existing infrastructure. Some things have to be built from the ground up to be built right.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- What I believe -->
|
||||
<div class=\"reveal\" style=\"margin-bottom: 3rem;\">
|
||||
<div class=\"reveal\" style=\"margin-bottom: 3.5rem;\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">What I believe</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9; margin-bottom: 1.5rem;\">
|
||||
AI has genuine potential to free people to do work that actually matters to them. That potential is almost entirely unrealized — not because the technology isn't capable, but because the incentives are wrong.
|
||||
AI has genuine potential to free people to do work that actually matters to them — not to create engagement loops, not to harvest attention, but to actually serve the person sitting in front of it. That potential is almost entirely unrealized, not because the technology isn't capable, but because the incentives that shaped it were never oriented toward the person.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9;\">
|
||||
I don't know if Neuron will work at the scale I'm imagining. But I know it's worth finding out. And I know I'm not going back to the other way of building things.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- The mission -->
|
||||
<div class=\"reveal\" style=\"margin-bottom: 3.5rem;\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">The mission</p>
|
||||
<p style=\"
|
||||
font-family: var(--head);
|
||||
font-size: clamp(1.2rem, 2.5vw, 1.625rem);
|
||||
@@ -126,7 +109,7 @@ fn about_page() -> String {
|
||||
Build AI that earns the trust it's given.
|
||||
</p>
|
||||
<p style=\"font-family: var(--body); font-weight: 300; font-size: clamp(0.9rem, 1.5vw, 1.0625rem); color: var(--t2); line-height: 1.9;\">
|
||||
Not AGI. Not disrupting industries. Not transforming anything. Something that actually knows you — and is worth knowing.
|
||||
I don't know if Neuron will work at the scale I'm imagining. But I know it's worth finding out, and I know I'm not going back to the other way of building things.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
// founding_badge.el — Founding Member badge component.
|
||||
//
|
||||
// Renders the certificate-style badge with guilloché background,
|
||||
// navy colour scheme, and member number. Matches the React archive design.
|
||||
// Used on the post-purchase success page and as a shareable /founding-badge route.
|
||||
|
||||
fn founding_badge(member_number: Int) -> String {
|
||||
let num_str: String = int_to_str(member_number)
|
||||
let show_number: Bool = member_number > 0
|
||||
let number_html: String = if show_number {
|
||||
"<div style=\"display:flex;flex-direction:column;align-items:center;gap:0;\">
|
||||
<span style=\"font-size:8px;font-weight:700;letter-spacing:.20em;text-transform:uppercase;color:#0052A0;line-height:1;margin-bottom:-8px;position:relative;z-index:1;\">No.</span>
|
||||
<p style=\"font-size:42px;font-weight:700;color:#0D0D14;letter-spacing:-.02em;line-height:.85;margin:0;\">" + num_str + "</p>
|
||||
<p style=\"font-size:8px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#0052A0;margin:0;\">of 1,000</p>
|
||||
</div>"
|
||||
} else {
|
||||
"<p style=\"font-size:8px;letter-spacing:.18em;text-transform:uppercase;color:rgba(0,82,160,.45);margin:0;\">Your number awaits</p>"
|
||||
}
|
||||
return "
|
||||
<div class=\"founding-badge\" aria-label=\"Founding Member badge\">
|
||||
<!-- Guilloché background SVG -->
|
||||
<svg aria-hidden=\"true\" class=\"founding-badge-bg\" viewBox=\"0 0 260 300\" preserveAspectRatio=\"none\">
|
||||
<defs>
|
||||
<pattern id=\"hatch\" width=\"6\" height=\"6\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">
|
||||
<line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"6\" stroke=\"rgba(0,82,160,.055)\" stroke-width=\"0.5\"/>
|
||||
</pattern>
|
||||
<pattern id=\"dots\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\">
|
||||
<circle cx=\"5\" cy=\"5\" r=\"0.6\" fill=\"rgba(0,82,160,.12)\"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width=\"260\" height=\"300\" fill=\"rgba(0,82,160,.025)\"/>
|
||||
<rect width=\"260\" height=\"300\" fill=\"url(#hatch)\"/>
|
||||
<rect width=\"260\" height=\"300\" fill=\"url(#dots)\"/>
|
||||
<!-- Guilloché wave bands -->
|
||||
<path d=\"M 0,40 C 26,34 52,46 78,40 S 130,34 156,40 S 208,46 260,40\" fill=\"none\" stroke=\"rgba(0,82,160,.18)\" stroke-width=\"0.7\"/>
|
||||
<path d=\"M 0,38 C 29,44 58,32 87,38 S 145,44 174,38 S 232,32 260,38\" fill=\"none\" stroke=\"rgba(0,82,160,.12)\" stroke-width=\"0.7\"/>
|
||||
<path d=\"M 0,260 C 26,254 52,266 78,260 S 130,254 156,260 S 208,266 260,260\" fill=\"none\" stroke=\"rgba(0,82,160,.18)\" stroke-width=\"0.7\"/>
|
||||
<path d=\"M 0,258 C 29,264 58,252 87,258 S 145,264 174,258 S 232,252 260,258\" fill=\"none\" stroke=\"rgba(0,82,160,.12)\" stroke-width=\"0.7\"/>
|
||||
<!-- Double-line ornate border -->
|
||||
<rect x=\"6\" y=\"6\" width=\"248\" height=\"288\" fill=\"none\" stroke=\"rgba(0,82,160,.30)\" stroke-width=\"0.75\"/>
|
||||
<rect x=\"10\" y=\"10\" width=\"240\" height=\"280\" fill=\"none\" stroke=\"rgba(0,82,160,.15)\" stroke-width=\"0.5\" stroke-dasharray=\"4 3\"/>
|
||||
<!-- Corner rosettes -->
|
||||
<g><circle cx=\"6\" cy=\"6\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"6\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"6\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"6\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<g><circle cx=\"254\" cy=\"6\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"6\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"6\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"6\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<g><circle cx=\"6\" cy=\"294\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"294\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"294\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"294\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<g><circle cx=\"254\" cy=\"294\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"294\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"294\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"294\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<!-- Micro text bands -->
|
||||
<text x=\"130\" y=\"22\" text-anchor=\"middle\" font-size=\"4.5\" font-family=\"system-ui,sans-serif\" letter-spacing=\"3\" fill=\"rgba(0,82,160,.28)\" text-length=\"220\" length-adjust=\"spacing\">NEURON TECHNOLOGIES · FOUNDING MEMBER · NEURON TECHNOLOGIES · FOUNDING MEMBER</text>
|
||||
<text x=\"130\" y=\"286\" text-anchor=\"middle\" font-size=\"4.5\" font-family=\"system-ui,sans-serif\" letter-spacing=\"3\" fill=\"rgba(0,82,160,.28)\" text-length=\"220\" length-adjust=\"spacing\">NEURON TECHNOLOGIES · FOUNDING MEMBER · NEURON TECHNOLOGIES · FOUNDING MEMBER</text>
|
||||
</svg>
|
||||
|
||||
<!-- Badge content -->
|
||||
<div class=\"founding-badge-inner\">
|
||||
<p class=\"founding-badge-label\">Founding Member</p>
|
||||
<div class=\"founding-badge-rule\"></div>
|
||||
<div class=\"founding-badge-icon\">
|
||||
<img src=\"/assets/neuron-icon.png\" alt=\"Neuron\" width=\"34\" height=\"34\">
|
||||
</div>
|
||||
" + number_html + "
|
||||
</div>
|
||||
</div>
|
||||
"
|
||||
}
|
||||
|
||||
fn founding_badge_css() -> String {
|
||||
return "
|
||||
<style>
|
||||
@keyframes fb-glow-pulse {
|
||||
0%,100% { box-shadow: 0 0 0 3px rgba(0,82,160,.08), 0 0 18px rgba(0,82,160,.22), 0 0 36px rgba(0,82,160,.10); }
|
||||
50% { box-shadow: 0 0 0 4px rgba(0,82,160,.12), 0 0 28px rgba(0,82,160,.32), 0 0 56px rgba(0,82,160,.16); }
|
||||
}
|
||||
@keyframes fb-icon-float {
|
||||
0%,100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-2px); }
|
||||
}
|
||||
.founding-badge {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 260px;
|
||||
background: #FAFAF8;
|
||||
border: 1px solid rgba(0,120,212,.5);
|
||||
animation: fb-glow-pulse 3s ease-in-out infinite;
|
||||
overflow: hidden;
|
||||
}
|
||||
.founding-badge-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.founding-badge-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 32px 28px 28px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.founding-badge-label {
|
||||
font-family: var(--body,system-ui,sans-serif);
|
||||
font-size: 8.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: .30em;
|
||||
text-transform: uppercase;
|
||||
color: #0052A0;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
}
|
||||
.founding-badge-rule {
|
||||
width: 48px;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(0,82,160,.45), transparent);
|
||||
}
|
||||
.founding-badge-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,.85);
|
||||
border: 1px solid rgba(0,82,160,.2);
|
||||
box-shadow: 0 0 0 4px rgba(0,82,160,.06);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.founding-badge-icon img {
|
||||
display: block;
|
||||
animation: fb-icon-float 4s ease-in-out infinite;
|
||||
}
|
||||
</style>
|
||||
"
|
||||
}
|
||||
+16
-4
@@ -37,6 +37,7 @@ from viral import { viral }
|
||||
from footer import { footer }
|
||||
from styles import { page_open, page_close }
|
||||
from about import { about_page }
|
||||
from founding_badge import { founding_badge, founding_badge_css }
|
||||
from terms import { terms_page }
|
||||
from enterprise_terms import { enterprise_terms_page }
|
||||
|
||||
@@ -202,6 +203,14 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
||||
// ── Stripe webhook ────────────────────────────────────────────────────────
|
||||
if str_eq(path, "/api/webhooks/stripe") {
|
||||
if str_contains(body, "checkout.session.completed") {
|
||||
// Increment founding counter when a founding purchase completes.
|
||||
if str_contains(body, "\"founding\"") {
|
||||
let current_sold: Int = get_sold()
|
||||
let new_sold: Int = current_sold + 1
|
||||
state_set("__founding_sold__", int_to_str(new_sold))
|
||||
println("[webhook] founding sold: " + int_to_str(new_sold))
|
||||
}
|
||||
// Forward to license API for key provisioning.
|
||||
let license_api: String = state_get("__license_api_url__")
|
||||
if !str_eq(license_api, "") {
|
||||
let resp: String = http_post(license_api + "/api/v1/webhooks/stripe", body)
|
||||
@@ -213,13 +222,16 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
||||
|
||||
// ── Checkout success ──────────────────────────────────────────────────────
|
||||
if str_eq(path, "/marketplace/success") {
|
||||
return page_open() + "
|
||||
let badge_html: String = founding_badge(get_sold())
|
||||
let badge_css: String = founding_badge_css()
|
||||
return page_open() + badge_css + "
|
||||
<div style=\"min-height:80vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:4rem 2rem\">
|
||||
<p class=\"label\" style=\"margin-bottom:1.5rem\">Purchase complete</p>
|
||||
<p class=\"label\" style=\"margin-bottom:1.5rem\">You're in.</p>
|
||||
<h1 class=\"display-lg\" style=\"margin-bottom:1.25rem\">Welcome to Neuron.</h1>
|
||||
<p style=\"font-family:var(--body);font-weight:300;font-size:1.1rem;color:var(--t2);max-width:28rem;line-height:1.7;margin-bottom:2.5rem\">
|
||||
Your license is being provisioned. Check your email in the next few minutes — your license key and download instructions will be waiting.
|
||||
<p style=\"font-family:var(--body);font-weight:300;font-size:1.1rem;color:var(--t2);max-width:28rem;line-height:1.7;margin-bottom:3rem\">
|
||||
Your license is being provisioned. Check your email — your license key and download instructions will be there in the next few minutes.
|
||||
</p>
|
||||
<div style=\"margin-bottom:3rem\">" + badge_html + "</div>
|
||||
<a href=\"/\" class=\"btn-primary\">Back to home →</a>
|
||||
</div>
|
||||
" + page_close()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user