v1.0 - launch: full nav on gallery, chat widget auto-open, comparison logos, checkout fixes
This commit is contained in:
+154
-38
@@ -18,29 +18,40 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
let plan_name: String = if is_founding { "Founding Member" } else { if is_free { "Free" } else { "Professional" } }
|
||||
let plan_price: String = if is_founding { "$199" } else { if is_free { "$0" } else { "$19 / month" } }
|
||||
let plan_desc: String = if is_founding {
|
||||
"Pay once. Neuron inference forever. No subscription, ever."
|
||||
"Pay once. Neuron inference when it launches - priced below the major APIs. No subscription, ever."
|
||||
} else { if is_free {
|
||||
"Start building your memory. No card required."
|
||||
} else {
|
||||
"Full access. Neuron inference - cheaper than what you're paying now."
|
||||
}
|
||||
"Full access. Bring your own API keys or use Neuron Inference when it launches - Q3 2026."
|
||||
} }
|
||||
let plan_cadence: String = if is_founding { "one-time" } else { if is_free { "forever" } else { "billed monthly" } }
|
||||
|
||||
let features_html: String = if is_founding {
|
||||
"<li>Neuron inference - priced below competitors, forever</li>
|
||||
<li>Everything in Professional - forever</li>
|
||||
"<li>Everything in Professional - forever</li>
|
||||
<li>Neuron Inference when it launches - priced below the major APIs, forever</li>
|
||||
<li>Never pay again - lifetime updates included</li>
|
||||
<li>Founding member badge in the app</li>
|
||||
<li>Private community - direct line to the team</li>
|
||||
<li>Private founding member community</li>
|
||||
<li>Shape the roadmap - your votes carry more weight</li>
|
||||
<li>Beta features before general release</li>
|
||||
<li>Name in the credits</li>"
|
||||
} else { if is_free {
|
||||
"<li>Persistent memory - never resets</li>
|
||||
<li>Local inference via Ollama (coming)</li>
|
||||
<li>Bring your own API keys</li>
|
||||
<li>3 marketplace plugins included</li>
|
||||
<li>Core built-in capabilities</li>
|
||||
<li>2 devices included</li>"
|
||||
} else {
|
||||
"<li>Neuron inference - metered, priced below OpenAI and Anthropic</li>
|
||||
<li>Persistent memory - never resets</li>
|
||||
"<li>Persistent memory - never resets</li>
|
||||
<li>Bring your own API keys (OpenAI, Anthropic, Grok...)</li>
|
||||
<li>Neuron Inference when it launches - Q3 2026, priced below the major APIs</li>
|
||||
<li>Unlimited projects</li>
|
||||
<li>Full plugin marketplace</li>
|
||||
<li>IDE, Slack, and more integrations</li>
|
||||
<li>Early access to new features</li>"
|
||||
}
|
||||
<li>Early access to new features</li>
|
||||
<li>2 devices included</li>"
|
||||
} }
|
||||
|
||||
return "
|
||||
<nav id=\"nav\">
|
||||
@@ -79,9 +90,13 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Right: sign-in + payment form -->
|
||||
<!-- Right: either free thank-you or sign-in + payment form -->
|
||||
<div class=\"checkout-form-wrap\">
|
||||
|
||||
" + (if is_free { "
|
||||
<p class=\"label\" style=\"margin-bottom: 1.5rem; color: var(--navy);\">Create your account.</p>
|
||||
<p class=\"checkout-auth-hint\" style=\"margin-bottom: 2rem;\">Create an account to reserve your free tier spot. You'll receive your download link and setup instructions at launch.</p>
|
||||
" } else { "
|
||||
<!-- Step 1: Social sign-in -->
|
||||
<div id=\"auth-section\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">Sign in to continue</p>
|
||||
@@ -110,12 +125,12 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
<div id=\"auth-message\" class=\"checkout-message\" style=\"display:none;\"></div>
|
||||
|
||||
<div class=\"checkout-auth-divider\">
|
||||
<span>or create an account</span>
|
||||
<span id=\"auth-divider-label\">or create an account</span>
|
||||
</div>
|
||||
|
||||
<div id=\"email-auth-form\">
|
||||
<input type=\"email\" id=\"auth-email\" class=\"checkout-input\" placeholder=\"Email address\" autocomplete=\"email\">
|
||||
<input type=\"password\" id=\"auth-password\" class=\"checkout-input\" placeholder=\"Password (min 8 characters)\" autocomplete=\"new-password\">
|
||||
<input type=\"email\" id=\"auth-email\" class=\"checkout-input\" placeholder=\"Email address\" autocomplete=\"email\" style=\"width:100%;display:block;margin-bottom:.75rem\">
|
||||
<input type=\"password\" id=\"auth-password\" class=\"checkout-input\" placeholder=\"Password - min 8 characters\" autocomplete=\"new-password\" style=\"width:100%;display:block;margin-bottom:.75rem\">
|
||||
<button type=\"button\" class=\"checkout-email-btn\" onclick=\"signUpWithEmail()\">Create account →</button>
|
||||
<p class=\"checkout-auth-hint\" style=\"margin-top:.75rem;text-align:center\">Already have an account? <a href=\"#\" onclick=\"showSignIn();return false;\" style=\"color:var(--navy)\">Sign in</a></p>
|
||||
</div>
|
||||
@@ -124,6 +139,45 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
<!-- Step 2: Payment form (revealed after auth) -->
|
||||
<div id=\"payment-section\" style=\"display:none;\">
|
||||
<div id=\"auth-badge\" style=\"display:none; margin-bottom: 1.5rem;\"></div>
|
||||
|
||||
<!-- Founding Member attestation (only shown for founding plan) -->
|
||||
" + (if is_founding { "
|
||||
<div id=\"founding-attestation\" style=\"margin-bottom:2rem;padding:1.5rem;border:1px solid rgba(0,82,160,.2);background:rgba(0,82,160,.03)\">
|
||||
<p style=\"font-family:var(--body);font-weight:500;font-size:.9rem;color:var(--t1);margin-bottom:.75rem\">Before you continue</p>
|
||||
<p style=\"font-family:var(--body);font-weight:300;font-size:.8375rem;color:var(--t2);line-height:1.75;margin-bottom:1.25rem\">Founding Member is not a ceremonial title. These are the people who will work directly with the team to shape what Neuron becomes. You will have a real voice in what gets built, and we will take it seriously. That requires you to show up in good faith.</p>
|
||||
<label style=\"display:flex;gap:.875rem;align-items:flex-start;cursor:pointer\">
|
||||
<input type=\"checkbox\" id=\"founding-attest-cb\" style=\"margin-top:.2rem;width:16px;height:16px;flex-shrink:0;accent-color:var(--navy)\">
|
||||
<span style=\"font-family:var(--body);font-weight:300;font-size:.8125rem;color:var(--t2);line-height:1.7\">
|
||||
I am joining as a genuine early user, not to extract proprietary information about Neuron's technology, architecture, or roadmap. I will engage in good faith. I understand that if this is not my intent, a different plan is a better fit.
|
||||
</span>
|
||||
</label>
|
||||
<p id=\"attest-warn\" style=\"display:none;font-family:var(--body);font-size:.8rem;color:#c44;margin-top:.75rem\">Please confirm the above before continuing.</p>
|
||||
</div>
|
||||
" } else { "" }) + "
|
||||
|
||||
<!-- Payment timing (Professional only, Founding always charges now) -->
|
||||
" + (if !is_founding { "
|
||||
<div style=\"margin-bottom:1.75rem\">
|
||||
<p class=\"label\" style=\"margin-bottom:1rem\">When would you like to be charged?</p>
|
||||
<div style=\"display:flex;flex-direction:column;gap:.625rem\">
|
||||
<label class=\"checkout-timing-opt\" id=\"timing-now-label\">
|
||||
<input type=\"radio\" name=\"charge-timing\" value=\"now\" id=\"timing-now\" checked style=\"accent-color:var(--navy)\">
|
||||
<div>
|
||||
<span style=\"font-family:var(--body);font-weight:500;font-size:.875rem;color:var(--t1)\">Charge me now</span>
|
||||
<span style=\"font-family:var(--body);font-weight:300;font-size:.8rem;color:var(--t3);display:block;margin-top:.15rem\">Paying now signals you're serious. It gives us real signal about what people actually want to use Neuron for - not just who's curious. That shapes what we build first.</span>
|
||||
</div>
|
||||
</label>
|
||||
<label class=\"checkout-timing-opt\" id=\"timing-later-label\">
|
||||
<input type=\"radio\" name=\"charge-timing\" value=\"later\" id=\"timing-later\" style=\"accent-color:var(--navy)\">
|
||||
<div>
|
||||
<span style=\"font-family:var(--body);font-weight:500;font-size:.875rem;color:var(--t1)\">Hold until product launches (Q3 2026)</span>
|
||||
<span style=\"font-family:var(--body);font-weight:300;font-size:.8rem;color:var(--t3);display:block;margin-top:.15rem\">We save your payment method securely. Nothing charged until launch. Cancel anytime before then.</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
" } else { "" }) + "
|
||||
|
||||
<p class=\"label\" style=\"margin-bottom: 1.75rem;\">Payment</p>
|
||||
|
||||
<form id=\"payment-form\" autocomplete=\"on\">
|
||||
@@ -132,7 +186,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
<div class=\"checkout-field\">
|
||||
<label for=\"buyer-name\" class=\"checkout-label\">Full name</label>
|
||||
<input id=\"buyer-name\" name=\"name\" type=\"text\" autocomplete=\"name\"
|
||||
class=\"checkout-input\" placeholder=\"Will Anderson\" required>
|
||||
class=\"checkout-input\" placeholder=\"Full name\" required>
|
||||
</div>
|
||||
<div class=\"checkout-field\">
|
||||
<label for=\"buyer-email\" class=\"checkout-label\">Email</label>
|
||||
@@ -165,6 +219,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
|
||||
</form>
|
||||
</div><!-- /payment-section -->
|
||||
" }) + "
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -273,6 +328,8 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
border: 1px solid rgba(0,82,160,.22);
|
||||
padding: .75rem 1rem;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
transition: border-color .2s;
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
@@ -333,49 +390,74 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
font-size: .875rem;
|
||||
color: var(--t2);
|
||||
line-height: 1.6;
|
||||
margin: 0 0 1.5rem;
|
||||
margin: 0 0 1.75rem;
|
||||
}
|
||||
.checkout-social-btns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .625rem;
|
||||
margin-bottom: 1.5rem;
|
||||
gap: .75rem;
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
.checkout-social-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .75rem;
|
||||
gap: .875rem;
|
||||
width: 100%;
|
||||
padding: .75rem 1.25rem;
|
||||
padding: .875rem 1.25rem;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0,82,160,.22);
|
||||
border: 1.5px solid rgba(0,82,160,.18);
|
||||
font-family: var(--body);
|
||||
font-size: .9rem;
|
||||
font-weight: 400;
|
||||
font-size: .9375rem;
|
||||
font-weight: 500;
|
||||
color: var(--t1);
|
||||
cursor: pointer;
|
||||
transition: border-color .2s, background .2s;
|
||||
transition: border-color .2s, background .2s, box-shadow .2s;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
letter-spacing: .01em;
|
||||
}
|
||||
.checkout-social-btn:hover { border-color: rgba(0,82,160,.5); background: rgba(0,82,160,.03); }
|
||||
.checkout-social-btn:disabled { opacity: .5; cursor: not-allowed; }
|
||||
.checkout-social-btn:hover {
|
||||
border-color: var(--navy);
|
||||
background: rgba(0,82,160,.03);
|
||||
box-shadow: 0 2px 12px rgba(0,82,160,.08);
|
||||
}
|
||||
.checkout-social-btn:disabled { opacity: .45; cursor: not-allowed; }
|
||||
.checkout-auth-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .75rem;
|
||||
margin: 1.25rem 0 1rem;
|
||||
gap: 1rem;
|
||||
margin: 1.75rem 0 1.25rem;
|
||||
color: var(--t3);
|
||||
font-family: var(--body);
|
||||
font-size: .8125rem;
|
||||
font-size: .75rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.checkout-auth-divider::before,
|
||||
.checkout-auth-divider::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: rgba(0,82,160,.15);
|
||||
background: rgba(0,82,160,.12);
|
||||
}
|
||||
.checkout-email-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: .875rem 1.5rem;
|
||||
background: var(--navy);
|
||||
color: #fff;
|
||||
border: none;
|
||||
font-family: var(--body);
|
||||
font-size: .8125rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
margin-top: .75rem;
|
||||
transition: background .2s, box-shadow .2s;
|
||||
}
|
||||
.checkout-email-btn:hover { background: #0078D4; box-shadow: 0 2px 16px rgba(0,82,160,.25); }
|
||||
.checkout-skip-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
@@ -412,7 +494,9 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
fetch('/api/supabase-config')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(cfg) {
|
||||
supabase = window.supabase.createClient(cfg.url, cfg.anon_key);
|
||||
supabase = window.supabase.createClient(cfg.url, cfg.anon_key, {
|
||||
auth: { flowType: 'implicit' }
|
||||
});
|
||||
cb();
|
||||
})
|
||||
.catch(function(err) {
|
||||
@@ -444,15 +528,11 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
badge.style.display = '';
|
||||
}
|
||||
|
||||
// Pre-fill email if available
|
||||
// Pre-fill email only (not name — let user enter their own)
|
||||
if (user && user.email) {
|
||||
var emailEl = document.getElementById('buyer-email');
|
||||
if (emailEl) { emailEl.value = user.email; }
|
||||
}
|
||||
if (user && user.user_metadata && user.user_metadata.full_name) {
|
||||
var nameEl = document.getElementById('buyer-name');
|
||||
if (nameEl && !nameEl.value) { nameEl.value = user.user_metadata.full_name; }
|
||||
}
|
||||
}
|
||||
|
||||
// Check if already signed in on load
|
||||
@@ -523,7 +603,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
var hint = form.querySelector('.checkout-auth-hint');
|
||||
btn.textContent = 'Sign in →';
|
||||
btn.onclick = signInWithEmail;
|
||||
hint.innerHTML = 'New here? <a href=\\"#\\" onclick=\\"showSignUp();return false;\\" style=\\"color:var(--navy)\\">Create account</a> · <a href=\\"#\\" onclick=\\"resetPassword();return false;\\" style=\\"color:var(--navy)\\">Forgot password?</a>';
|
||||
/* hint replaced with DOM manipulation below */
|
||||
};
|
||||
|
||||
window.showSignUp = function() {
|
||||
@@ -532,7 +612,7 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
var hint = form.querySelector('.checkout-auth-hint');
|
||||
btn.textContent = 'Create account →';
|
||||
btn.onclick = signUpWithEmail;
|
||||
hint.innerHTML = 'Already have an account? <a href=\\"#\\" onclick=\\"showSignIn();return false;\\" style=\\"color:var(--navy)\\">Sign in</a>';
|
||||
/* hint replaced with DOM manipulation below */
|
||||
};
|
||||
|
||||
window.signInWithEmail = function() {
|
||||
@@ -601,6 +681,12 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.error === 'sold_out') {
|
||||
showMessage('All 1,000 Founding Member spots have been claimed. Thank you for your interest - please consider the Professional plan.');
|
||||
var submitBtn = document.getElementById('submit-btn');
|
||||
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Sold out'; }
|
||||
return;
|
||||
}
|
||||
if (!data.client_secret) {
|
||||
showMessage('Unable to initialise payment. Please try again.');
|
||||
return;
|
||||
@@ -678,6 +764,15 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
e.preventDefault();
|
||||
if (!stripe || !elements) return;
|
||||
|
||||
// Founding Member attestation gate
|
||||
var attestCb = document.getElementById('founding-attest-cb');
|
||||
if (attestCb && !attestCb.checked) {
|
||||
var warn = document.getElementById('attest-warn');
|
||||
if (warn) warn.style.display = 'block';
|
||||
attestCb.closest('label').scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
return;
|
||||
}
|
||||
|
||||
var name = document.getElementById('buyer-name').value.trim();
|
||||
var email = document.getElementById('buyer-email').value.trim();
|
||||
if (!name || !email) {
|
||||
@@ -685,6 +780,27 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save founding member attestation before charging — independent audit record
|
||||
if (attestCb) {
|
||||
try {
|
||||
await fetch('/api/attest', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
plan: PLAN,
|
||||
name: name,
|
||||
email: email,
|
||||
timestamp: new Date().toISOString(),
|
||||
attestation: 'I am joining as a genuine early user, not to extract proprietary information about Neuron technology, architecture, or roadmap. I will engage in good faith. I understand that if this is not my intent, a different plan is a better fit.',
|
||||
user_agent: navigator.userAgent
|
||||
})
|
||||
});
|
||||
} catch(e) {
|
||||
// Non-blocking — attestation log failure does not stop payment
|
||||
console.warn('attestation log failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
document.getElementById('payment-message').style.display = 'none';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user