Add marketplace section, OAuth checkout, social icons, 35% efficiency claim, preorder CTAs, enterprise Q1 2027, founding member limits, paragraph spacing in demo chat, no em dashes
This commit is contained in:
+289
-5
@@ -13,15 +13,16 @@
|
||||
|
||||
fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
let is_founding: Bool = str_eq(plan, "founding")
|
||||
let is_free: Bool = str_eq(plan, "free")
|
||||
|
||||
let plan_name: String = if is_founding { "Founding Member" } else { "Professional" }
|
||||
let plan_price: String = if is_founding { "$199" } else { "$19 / month" }
|
||||
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."
|
||||
} else {
|
||||
"Full access. Neuron inference - cheaper than what you're paying now."
|
||||
}
|
||||
let plan_cadence: String = if is_founding { "one-time" } else { "billed monthly" }
|
||||
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>
|
||||
@@ -78,9 +79,52 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Right: payment form -->
|
||||
<!-- Right: sign-in + payment form -->
|
||||
<div class=\"checkout-form-wrap\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.75rem;\">Payment</p>
|
||||
|
||||
<!-- Step 1: Social sign-in -->
|
||||
<div id=\"auth-section\">
|
||||
<p class=\"label\" style=\"margin-bottom: 1.25rem;\">Sign in to continue</p>
|
||||
<p class=\"checkout-auth-hint\">Create your account or sign in. Your purchase will be linked to this account.</p>
|
||||
|
||||
<div class=\"checkout-social-btns\">
|
||||
<button type=\"button\" class=\"checkout-social-btn\" id=\"btn-google\" onclick=\"signInWith('google')\">
|
||||
<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" aria-hidden=\"true\">
|
||||
<path d=\"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615z\" fill=\"#4285F4\"/>
|
||||
<path d=\"M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 0 0 9 18z\" fill=\"#34A853\"/>
|
||||
<path d=\"M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z\" fill=\"#FBBC05\"/>
|
||||
<path d=\"M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 6.29C4.672 4.163 6.656 3.58 9 3.58z\" fill=\"#EA4335\"/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
|
||||
<button type=\"button\" class=\"checkout-social-btn\" id=\"btn-github\" onclick=\"signInWith('github')\">
|
||||
<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">
|
||||
<path d=\"M12 0C5.37 0 0 5.37 0 12c0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61-.546-1.385-1.335-1.755-1.335-1.755-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.605-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 21.795 24 17.298 24 12c0-6.63-5.37-12-12-12z\"/>
|
||||
</svg>
|
||||
Continue with GitHub
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div id=\"auth-message\" class=\"checkout-message\" style=\"display:none;\"></div>
|
||||
|
||||
<div class=\"checkout-auth-divider\">
|
||||
<span>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\">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- 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>
|
||||
<p class=\"label\" style=\"margin-bottom: 1.75rem;\">Payment</p>
|
||||
|
||||
<form id=\"payment-form\" autocomplete=\"on\">
|
||||
|
||||
@@ -120,11 +164,14 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
</p>
|
||||
|
||||
</form>
|
||||
</div><!-- /payment-section -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Supabase JS -->
|
||||
<script src=\"https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.js\"></script>
|
||||
<!-- Stripe.js -->
|
||||
<script src=\"https://js.stripe.com/v3/\" async></script>
|
||||
|
||||
@@ -280,8 +327,245 @@ fn checkout_page(plan: String, pub_key: String) -> String {
|
||||
gap: .4rem;
|
||||
margin: 0;
|
||||
}
|
||||
.checkout-auth-hint {
|
||||
font-family: var(--body);
|
||||
font-weight: 300;
|
||||
font-size: .875rem;
|
||||
color: var(--t2);
|
||||
line-height: 1.6;
|
||||
margin: 0 0 1.5rem;
|
||||
}
|
||||
.checkout-social-btns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .625rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.checkout-social-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .75rem;
|
||||
width: 100%;
|
||||
padding: .75rem 1.25rem;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0,82,160,.22);
|
||||
font-family: var(--body);
|
||||
font-size: .9rem;
|
||||
font-weight: 400;
|
||||
color: var(--t1);
|
||||
cursor: pointer;
|
||||
transition: border-color .2s, background .2s;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
}
|
||||
.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-auth-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .75rem;
|
||||
margin: 1.25rem 0 1rem;
|
||||
color: var(--t3);
|
||||
font-family: var(--body);
|
||||
font-size: .8125rem;
|
||||
}
|
||||
.checkout-auth-divider::before,
|
||||
.checkout-auth-divider::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: rgba(0,82,160,.15);
|
||||
}
|
||||
.checkout-skip-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-family: var(--body);
|
||||
font-size: .8125rem;
|
||||
color: var(--t3);
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
padding: 0;
|
||||
}
|
||||
.checkout-skip-btn:hover { color: var(--navy); }
|
||||
.checkout-auth-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
padding: .6rem .875rem;
|
||||
background: rgba(0,82,160,.05);
|
||||
border: 1px solid rgba(0,82,160,.18);
|
||||
font-family: var(--body);
|
||||
font-size: .8125rem;
|
||||
color: var(--t2);
|
||||
}
|
||||
.checkout-auth-badge strong { color: var(--navy); font-weight: 500; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// ── Supabase auth ─────────────────────────────────────────────────────────────
|
||||
(function() {
|
||||
var supabase;
|
||||
|
||||
function initSupabase(cb) {
|
||||
if (supabase) { cb(); return; }
|
||||
fetch('/api/supabase-config')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(cfg) {
|
||||
supabase = window.supabase.createClient(cfg.url, cfg.anon_key);
|
||||
cb();
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Supabase init failed', err);
|
||||
});
|
||||
}
|
||||
|
||||
function showAuthMessage(msg, isError) {
|
||||
var el = document.getElementById('auth-message');
|
||||
el.textContent = msg;
|
||||
el.style.display = 'block';
|
||||
el.style.color = isError ? '#c0392b' : '#2ecc71';
|
||||
}
|
||||
|
||||
function revealPaymentForm(user) {
|
||||
document.getElementById('auth-section').style.display = 'none';
|
||||
document.getElementById('payment-section').style.display = '';
|
||||
|
||||
// Show auth badge if we have a user
|
||||
if (user) {
|
||||
var badge = document.getElementById('auth-badge');
|
||||
var name = user.user_metadata && user.user_metadata.full_name
|
||||
? user.user_metadata.full_name
|
||||
: user.email || '';
|
||||
badge.innerHTML = '<div class=\"checkout-auth-badge\">'
|
||||
+ '<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M20 6L9 17l-5-5\" stroke=\"#0052A0\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'
|
||||
+ 'Signed in as <strong>' + name + '</strong>'
|
||||
+ '</div>';
|
||||
badge.style.display = '';
|
||||
}
|
||||
|
||||
// Pre-fill email if available
|
||||
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
|
||||
function checkExistingSession() {
|
||||
initSupabase(function() {
|
||||
supabase.auth.getUser().then(function(res) {
|
||||
if (res.data && res.data.user) {
|
||||
revealPaymentForm(res.data.user);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle OAuth redirect callback
|
||||
function handleAuthRedirect() {
|
||||
initSupabase(function() {
|
||||
supabase.auth.onAuthStateChange(function(event, session) {
|
||||
if ((event === 'SIGNED_IN' || event === 'INITIAL_SESSION') && session && session.user) {
|
||||
revealPaymentForm(session.user);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Social sign-in
|
||||
window.signInWith = function(provider) {
|
||||
var btns = document.querySelectorAll('.checkout-social-btn');
|
||||
btns.forEach(function(b) { b.disabled = true; });
|
||||
|
||||
initSupabase(function() {
|
||||
supabase.auth.signInWithOAuth({
|
||||
provider: provider,
|
||||
options: {
|
||||
redirectTo: window.location.href
|
||||
}
|
||||
}).then(function(result) {
|
||||
if (result.error) {
|
||||
showAuthMessage(result.error.message || 'Sign-in failed. Please try again.', true);
|
||||
btns.forEach(function(b) { b.disabled = false; });
|
||||
}
|
||||
// On success, browser redirects to OAuth provider — no further action needed here.
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Email signup
|
||||
window.signUpWithEmail = function() {
|
||||
var email = document.getElementById('auth-email').value.trim();
|
||||
var password = document.getElementById('auth-password').value;
|
||||
if (!email || !password) { showAuthMessage('Please enter your email and a password.', true); return; }
|
||||
if (password.length < 8) { showAuthMessage('Password must be at least 8 characters.', true); return; }
|
||||
initSupabase(function() {
|
||||
supabase.auth.signUp({ email: email, password: password }).then(function(result) {
|
||||
if (result.error) { showAuthMessage(result.error.message, true); return; }
|
||||
if (result.data && result.data.session) {
|
||||
revealPaymentForm(result.data.session.user);
|
||||
} else {
|
||||
showAuthMessage('Check your email to confirm your account, then come back to complete your purchase.', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Email sign-in (existing account)
|
||||
window.showSignIn = function() {
|
||||
var form = document.getElementById('email-auth-form');
|
||||
var btn = form.querySelector('.checkout-email-btn');
|
||||
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>';
|
||||
};
|
||||
|
||||
window.showSignUp = function() {
|
||||
var form = document.getElementById('email-auth-form');
|
||||
var btn = form.querySelector('.checkout-email-btn');
|
||||
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>';
|
||||
};
|
||||
|
||||
window.signInWithEmail = function() {
|
||||
var email = document.getElementById('auth-email').value.trim();
|
||||
var password = document.getElementById('auth-password').value;
|
||||
if (!email || !password) { showAuthMessage('Please enter your email and password.', true); return; }
|
||||
initSupabase(function() {
|
||||
supabase.auth.signInWithPassword({ email: email, password: password }).then(function(result) {
|
||||
if (result.error) { showAuthMessage(result.error.message, true); return; }
|
||||
revealPaymentForm(result.data.session.user);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
window.resetPassword = function() {
|
||||
var email = document.getElementById('auth-email').value.trim();
|
||||
if (!email) { showAuthMessage('Enter your email address above first.', true); return; }
|
||||
initSupabase(function() {
|
||||
supabase.auth.resetPasswordForEmail(email, {
|
||||
redirectTo: window.location.origin + '/checkout?plan=' + (new URLSearchParams(window.location.search).get('plan') || 'professional')
|
||||
}).then(function(result) {
|
||||
if (result.error) { showAuthMessage(result.error.message, true); }
|
||||
else { showAuthMessage('Password reset email sent. Check your inbox.', false); }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Init
|
||||
handleAuthRedirect();
|
||||
checkExistingSession();
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var PLAN = '" + plan + "';
|
||||
|
||||
Reference in New Issue
Block a user