migrate stage build to native elc; chat restores from localStorage on return

Build pipeline
- build-stage.sh replaces the old in-Dockerfile bootstrap.py path. Host
  pre-compiles src/*.el into dist/main.c via the canonical native elc at
  foundation/el/dist/platform/elc and applies the stub-decl sed before
  docker buildx runs.
- Dockerfile.stage drops bootstrap.py + python3 from the builder stage
  and just runs cc on the host-supplied dist/main.c.
- Pre-rendered HTML shells under /srv/landing/ are now chowned to the
  landing user so the El page-builder's fs_write at startup can rewrite
  them — without that, post-COPY edits never reach the served HTML and
  the served page stays as the stale build-time fallback.

Chat restore
- session.verified + session.verifiedAt persist through localStorage so
  a return visit within 24h skips the Turnstile gate and lands directly
  in the restored conversation.
- restoreOrGreet() is the single source of truth for what shows up in
  the message pane after the gate clears: replays prior messages with
  skipSave, else drops the canned hello once and remembers it.
- applyVerifiedDom() hides the gate / reveals the chat row, called both
  from the verified-on-load path (DOMContentLoaded if loading, else
  immediate) and from the Turnstile callback.
- neuronDemoReset clears verified + verifiedAt so the gate returns next
  open.

Extracted JS assets (src/assets/js/*.js + manifest.json) and the
extract-js.py helper land here too — they match what the new build-stage
flow produces and removes the inline <script> blobs from the served HTML.
This commit is contained in:
Will Anderson
2026-05-02 11:15:09 -05:00
parent cae5028130
commit 640813e42e
27 changed files with 906 additions and 1461 deletions
+1 -348
View File
@@ -896,354 +896,7 @@ fn account_page(supabase_url: String, supabase_anon_key: String) -> String {
</div>
<script src=\"https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js\"></script>
<script>
(function() {
'use strict';
var SUPABASE_URL = '" + supabase_url + "';
var SUPABASE_ANON_KEY = '" + supabase_anon_key + "';
var sb = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
auth: { flowType: 'implicit' }
});
// ── Helpers ────────────────────────────────────────────────────────────────
function show(id) {
var el = document.getElementById(id);
if (el) el.style.display = '';
}
function hide(id) {
var el = document.getElementById(id);
if (el) el.style.display = 'none';
}
function setText(id, text) {
var el = document.getElementById(id);
if (el) el.textContent = text;
}
function setHtml(id, html) {
var el = document.getElementById(id);
if (el) el.innerHTML = html;
}
// ── Sign-in ────────────────────────────────────────────────────────────────
window.signInWith = async function(provider) {
var btn = document.getElementById('btn-' + provider);
if (btn) { btn.disabled = true; btn.style.opacity = '0.6'; }
try {
var result = await sb.auth.signInWithOAuth({
provider: provider,
options: {
redirectTo: window.location.origin + '/account'
}
});
if (result.error) {
if (btn) { btn.disabled = false; btn.style.opacity = '1'; }
console.error('Sign-in error:', result.error.message);
}
} catch (e) {
if (btn) { btn.disabled = false; btn.style.opacity = '1'; }
console.error('Sign-in failed:', e);
}
};
// ── Email sign-in ──────────────────────────────────────────────────────────
window.signInWithEmail = async function() {
var email = document.getElementById('acct-email-input').value.trim();
var pass = document.getElementById('acct-pass-input').value;
var msg = document.getElementById('acct-email-msg');
var signinBtn = document.getElementById('acct-signin-btn');
if (!sb) { msg.style.display='block'; msg.style.color='#c44'; msg.textContent='Loading... try again in a moment.'; return; }
if (!email || !pass) {
msg.style.display = 'block'; msg.style.color = '#c44';
msg.textContent = 'Please enter your email and password.'; return;
}
if (signinBtn) { signinBtn.disabled = true; signinBtn.textContent = 'Signing in...'; }
// Try sign in first, then sign up if not found
var result = await sb.auth.signInWithPassword({ email: email, password: pass });
if (result.error) {
if (result.error.message && result.error.message.toLowerCase().includes('invalid')) {
// Try sign up
var signupResult = await sb.auth.signUp({
email: email, password: pass,
options: { emailRedirectTo: window.location.origin + '/account' }
});
if (signupResult.error) {
msg.style.display = 'block'; msg.style.color = '#c44';
msg.textContent = signupResult.error.message; return;
}
msg.style.display = 'block'; msg.style.color = 'var(--navy)';
msg.textContent = 'Check your email to confirm your account.'; return;
}
if (signinBtn) { signinBtn.disabled = false; signinBtn.textContent = 'Sign in'; }
msg.style.display = 'block'; msg.style.color = '#c44';
msg.textContent = result.error.message; return;
}
window.location.reload();
};
// ── Sign-out ───────────────────────────────────────────────────────────────
window.signOut = async function() {
var btn = document.getElementById('signout-btn');
var btnTop = document.getElementById('signout-btn-top');
if (btn) { btn.disabled = true; btn.textContent = 'Signing out...'; }
if (btnTop) { btnTop.disabled = true; btnTop.textContent = 'Signing out...'; }
await sb.auth.signOut();
show('signin-section');
hide('dashboard-section');
if (btn) { btn.disabled = false; btn.textContent = 'Sign out'; }
if (btnTop) { btnTop.disabled = false; btnTop.textContent = 'Sign out'; }
};
// ── Render plan card ───────────────────────────────────────────────────────
async function renderPlanCard(row) {
var plan = (row && row.plan) ? row.plan : 'free';
var memberNum = (row && row.member_number) ? row.member_number : null;
var source = (row && row.source) ? row.source : '';
var createdAt = (row && row.created_at) ? row.created_at : null;
// Plan display name
var planNames = { 'founding': 'Founding Member', 'professional': 'Professional', 'free': 'Free' };
var planDisplay = planNames[plan] || 'Free';
// Status
var statusLabel = 'Preorder';
if (plan === 'free') statusLabel = 'Active';
setText('plan-name-el', planDisplay);
// Status badge
var statusHtml = '';
if (plan === 'founding' || plan === 'professional') {
statusHtml += '<span class=\"status-badge-preorder\" style=\"margin-top:.625rem;display:inline-flex\">' +
'<svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/></svg>' +
'Launching within 30 days</span>';
} else {
statusHtml += '<span class=\"plan-status\" style=\"margin-top:.625rem;display:inline-flex\"><span class=\"plan-status-dot\"></span>' + statusLabel + '</span>';
}
setHtml('plan-status-el', statusHtml);
// Billing note (replaces, never appends - this function may run more than once on auth state changes)
var billingNote = '';
if (plan === 'founding') {
billingNote = '<p class=\"plan-billing-note\">Lifetime &middot; Never billed again</p>';
} else if (plan === 'professional') {
billingNote = '<p class=\"plan-billing-note\">Billed monthly &middot; <button class=\"plan-billing-link\" onclick=\"window.location.href=\\\'/contact\\\'\">Cancel</button></p>';
} else {
billingNote = '<p class=\"plan-billing-note\">On the waitlist</p>';
}
setHtml('plan-billing-note-el', billingNote);
// Meta
var meta = '';
if (createdAt) {
var d = new Date(createdAt);
var dateStr = d.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
meta += '<div class=\"plan-meta-item\"><span class=\"plan-meta-label\">Joined</span><span class=\"plan-meta-value\">' + dateStr + '</span></div>';
}
if (memberNum) {
meta += '<div class=\"plan-meta-item\"><span class=\"plan-meta-label\">Member number</span><span class=\"plan-meta-value\">#' + memberNum + ' of 1,000</span></div>';
}
if (meta) {
setHtml('plan-meta-el', meta);
}
// Founding badge - always show for founding members, with member number if assigned
if (plan === 'founding') {
var badgeSection = document.getElementById('badge-section');
var badgeContainer = document.getElementById('badge-html-container');
if (badgeSection) badgeSection.style.display = '';
var badgeN = memberNum || 0;
fetch('/api/founding-badge?n=' + badgeN)
.then(function(r) { return r.text(); })
.then(function(html) {
if (badgeContainer) badgeContainer.innerHTML = html;
})
.catch(function() {});
}
// Show roadmap for founding members
var roadmapSection = document.getElementById('roadmap-section');
if (plan === 'founding' && roadmapSection) roadmapSection.style.display = '';
// Family section
if (plan === 'founding') {
document.getElementById('family-section').style.display = 'block';
var session = await sb.auth.getSession();
var userEmail = session.data.session && session.data.session.user ? session.data.session.user.email : '';
if (userEmail) loadFamilyMembers(userEmail);
}
}
// ── Family plan ────────────────────────────────────────────────────────────
async function loadFamilyMembers(parentEmail) {
var r = await fetch('/api/family/members?parent_email=' + encodeURIComponent(parentEmail));
var members = await r.json();
var list = document.getElementById('family-list');
if (!list) return;
if (!members || !members.length) {
list.innerHTML = '<p style=\"color:var(--t3);font-size:.875rem;margin-bottom:1rem\">No family members yet.</p>';
return;
}
list.innerHTML = members.map(function(m) {
return '<div style=\"display:flex;justify-content:space-between;align-items:center;padding:.75rem 0;border-bottom:1px solid var(--border)\">' +
'<div><p style=\"font-size:.875rem;color:var(--t1)\">' + m.child_email + '</p>' +
'<p style=\"font-size:.75rem;color:var(--t3);text-transform:uppercase;letter-spacing:.06em\">' + m.status + '</p></div>' +
'<button onclick=\"removeFamilyMember(\\\'' + m.child_email + '\\\')\" style=\"background:none;border:none;color:var(--t3);cursor:pointer;font-size:.75rem\">Remove</button>' +
'</div>';
}).join('');
}
window.addFamilyMember = async function() {
var email = document.getElementById('child-email').value.trim();
var year = document.getElementById('child-dob-year').value;
var attest = document.getElementById('family-attest').checked;
var msg = document.getElementById('family-msg');
if (!email || !year || !attest) {
msg.style.display = 'block'; msg.style.color = '#c44';
msg.textContent = 'Please fill in all fields and confirm the attestation.'; return;
}
if (parseInt(year) < 2008) {
msg.style.display = 'block'; msg.style.color = '#c44';
msg.textContent = 'Child must be under 18. Birth year must be 2008 or later.'; return;
}
var session = await sb.auth.getSession();
var parentEmail = session.data.session && session.data.session.user ? session.data.session.user.email : '';
var r = await fetch('/api/family/invite', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({parent_email: parentEmail, child_email: email, child_dob_year: parseInt(year), attested: true})
});
var d = await r.json();
msg.style.display = 'block';
if (d.ok) {
msg.style.color = 'var(--navy)';
msg.textContent = 'Invitation sent to ' + email + '. They will receive an email to set up their account.';
document.getElementById('child-email').value = '';
document.getElementById('child-dob-year').value = '';
document.getElementById('family-attest').checked = false;
loadFamilyMembers(parentEmail);
} else {
msg.style.color = '#c44';
msg.textContent = d.error || 'Something went wrong.';
}
};
window.removeFamilyMember = async function(childEmail) {
var session = await sb.auth.getSession();
var parentEmail = session.data.session && session.data.session.user ? session.data.session.user.email : '';
await fetch('/api/family/remove', {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({parent_email: parentEmail, child_email: childEmail})
});
loadFamilyMembers(parentEmail);
};
// ── Render user info ───────────────────────────────────────────────────────
function renderUserChip(user) {
var email = user.email || '';
var avatarEl = document.getElementById('user-avatar-el');
var emailEl = document.getElementById('user-email-el');
var headerEmailEl = document.getElementById('acct-header-email');
if (emailEl) emailEl.textContent = email;
if (headerEmailEl) headerEmailEl.textContent = email;
var avatarUrl = user.user_metadata && user.user_metadata.avatar_url;
if (avatarEl) {
if (avatarUrl) {
avatarEl.innerHTML = '<img src=\"' + avatarUrl + '\" alt=\"\" referrerpolicy=\"no-referrer\">';
} else {
// Initials fallback
var initial = email ? email.charAt(0).toUpperCase() : '?';
avatarEl.textContent = initial;
}
}
}
// ── Load waitlist data ─────────────────────────────────────────────────────
async function loadWaitlistData(email) {
try {
var sess = await sb.auth.getSession();
var token = sess.data && sess.data.session ? sess.data.session.access_token : '';
if (!token) { showNoPlan(); return; }
var r = await fetch('/api/my-plan', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ access_token: token })
});
var row = await r.json();
if (!row || !row.plan) { showNoPlan(); return; }
renderPlanCard(row);
} catch (e) {
showNoPlan();
}
}
// ── No plan — show pricing ─────────────────────────────────────────────────
function showNoPlan() {
var el = document.getElementById('plan-card');
if (!el) return;
el.innerHTML = '<div class=\"card-label\">Your plan</div>' +
'<p style=\"font-family:var(--body);font-weight:500;font-size:1.125rem;color:var(--t1);margin-bottom:.75rem\">No active plan</p>' +
'<p style=\"font-family:var(--body);font-weight:300;font-size:.9rem;color:var(--t2);line-height:1.7;margin-bottom:1.5rem\">You have an account but no plan selected yet. Pick one below to preorder.</p>' +
'<div style=\"display:flex;gap:1rem;flex-wrap:wrap\">' +
'<a href=\"/checkout?plan=founding\" class=\"btn-primary\" style=\"padding:.75rem 1.5rem\">Founding Member - $199 &#8594;</a>' +
'<a href=\"/checkout?plan=professional\" class=\"btn-ghost\" style=\"padding:.75rem 1.5rem\">Professional - $19/mo</a>' +
'<a href=\"/checkout?plan=free\" class=\"btn-ghost\" style=\"padding:.75rem 1.5rem\">Free tier</a>' +
'</div>';
}
// ── Show dashboard ─────────────────────────────────────────────────────────
function showDashboard(user) {
hide('signin-section');
show('dashboard-section');
renderUserChip(user);
loadWaitlistData(user.email);
}
// ── Init ───────────────────────────────────────────────────────────────────
async function init() {
var result = await sb.auth.getSession();
var session = result.data && result.data.session;
if (session && session.user) {
showDashboard(session.user);
} else {
show('signin-section');
hide('dashboard-section');
}
// Listen for auth changes (e.g. OAuth redirect return)
sb.auth.onAuthStateChange(function(event, session) {
if (session && session.user) {
showDashboard(session.user);
} else {
show('signin-section');
hide('dashboard-section');
}
});
}
init();
})();
</script>
<script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.supabase_url=\"" + supabase_url + "\";window.NEURON_CFG.supabase_anon_key=\"" + supabase_anon_key + "\";</script><script src=\"/assets/js/6dafc1586705.js\" defer></script>
</body>
</html>"
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
function a0_0x1591(){var _0x10c289=['BMf2lwHHBwj1CMDLCG','ywrK','C2v0qxr0CMLIDxrL','CxvLCNLtzwXLy3rVCKfSBa','zMfSC2u','ywrKrxzLBNrmAxn0zw5LCG','mJu4oduXn3rMq0LlsG','B3bLBG','CxvLCNLtzwXLy3rVCG','y2XPy2S','zM9YrwfJAa','yxjPys1LEhbHBMrLza','Dhj1zq','A2v5zg93BG','y2XHC3nmAxn0','Dg9Nz2XL','C3rVCfbYB3bHz2f0Aw9U','mtaXodi1ngX0EfLdDG','odi3nJvNA01dEwy','n05UrhjgAa','ndq2odmXrhrPu1bs','y29UDgfPBNm','lM5HDI1KCM9Wzg93BI1PDgvT','oe9gq1H5vW','ntmXmtCXmfbmCw9Krq','DgfYz2v0','ota5nJiYqujQBwvL','BMf2','mta0reHcyu1g','CMvTB3zL','mtiZnZmXngP2AejQuq','z2v0rwXLBwvUDej5swq','CMvZAxPL','A2v5'];a0_0x1591=function(){return _0x10c289;};return a0_0x1591();}function a0_0x1c65(_0xb184c1,_0x57ab9a){_0xb184c1=_0xb184c1-0x1d0;var _0x159170=a0_0x1591();var _0x1c65a5=_0x159170[_0xb184c1];if(a0_0x1c65['WtKexP']===undefined){var _0xe40532=function(_0x40a838){var _0x2d9cb5='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x170067='',_0xe74606='';for(var _0x4a7359=0x0,_0x30df79,_0x3883ed,_0x6ecaab=0x0;_0x3883ed=_0x40a838['charAt'](_0x6ecaab++);~_0x3883ed&&(_0x30df79=_0x4a7359%0x4?_0x30df79*0x40+_0x3883ed:_0x3883ed,_0x4a7359++%0x4)?_0x170067+=String['fromCharCode'](0xff&_0x30df79>>(-0x2*_0x4a7359&0x6)):0x0){_0x3883ed=_0x2d9cb5['indexOf'](_0x3883ed);}for(var _0x907d2c=0x0,_0x3e504b=_0x170067['length'];_0x907d2c<_0x3e504b;_0x907d2c++){_0xe74606+='%'+('00'+_0x170067['charCodeAt'](_0x907d2c)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0xe74606);};a0_0x1c65['MwOGxL']=_0xe40532,a0_0x1c65['ZDZTWG']={},a0_0x1c65['WtKexP']=!![];}var _0x44174f=_0x159170[0x0],_0x518b15=_0xb184c1+_0x44174f,_0x173640=a0_0x1c65['ZDZTWG'][_0x518b15];return!_0x173640?(_0x1c65a5=a0_0x1c65['MwOGxL'](_0x1c65a5),a0_0x1c65['ZDZTWG'][_0x518b15]=_0x1c65a5):_0x1c65a5=_0x173640,_0x1c65a5;}(function(_0x37e014,_0x50cb04){var _0x44feb2=a0_0x1c65,_0x3735d2=_0x37e014();while(!![]){try{var _0x461de4=parseInt(_0x44feb2(0x1e5))/0x1+parseInt(_0x44feb2(0x1eb))/0x2+-parseInt(_0x44feb2(0x1ef))/0x3+parseInt(_0x44feb2(0x1ed))/0x4*(parseInt(_0x44feb2(0x1e3))/0x5)+parseInt(_0x44feb2(0x1e2))/0x6*(parseInt(_0x44feb2(0x1e4))/0x7)+parseInt(_0x44feb2(0x1e8))/0x8*(-parseInt(_0x44feb2(0x1d7))/0x9)+-parseInt(_0x44feb2(0x1e9))/0xa;if(_0x461de4===_0x50cb04)break;else _0x3735d2['push'](_0x3735d2['shift']());}catch(_0x5902f6){_0x3735d2['push'](_0x3735d2['shift']());}}}(a0_0x1591,0x420ab),!(function(){var _0x547ce7=a0_0x1c65,_0x170067=document['getElementById'](_0x547ce7(0x1d1)),_0xe74606=document[_0x547ce7(0x1f0)]('nav-mobile'),_0x4a7359=document[_0x547ce7(0x1f0)](_0x547ce7(0x1ec));if(_0x170067&&_0xe74606){_0x170067[_0x547ce7(0x1d6)](_0x547ce7(0x1da),function(_0x907d2c){var _0x153658=_0x547ce7;_0x907d2c[_0x153658(0x1e1)](),_0xe74606[_0x153658(0x1df)][_0x153658(0x1e6)](_0x153658(0x1d8))?_0x6ecaab():(_0xe74606[_0x153658(0x1df)][_0x153658(0x1d2)](_0x153658(0x1d8)),_0x170067[_0x153658(0x1d3)](_0x153658(0x1dc),_0x153658(0x1dd)));});var _0x30df79=document[_0x547ce7(0x1d9)]('.nav-dropdown-btn'),_0x3883ed=document[_0x547ce7(0x1d9)]('.nav-dropdown');_0x30df79&&_0x3883ed&&(_0x30df79[_0x547ce7(0x1d6)](_0x547ce7(0x1da),function(_0x3e504b){var _0x528822=_0x547ce7;_0x3e504b['stopPropagation']();var _0x433bd2=_0x3883ed[_0x528822(0x1df)][_0x528822(0x1e6)]('open');_0x3883ed[_0x528822(0x1df)][_0x528822(0x1e0)]('open'),_0x30df79[_0x528822(0x1d3)](_0x528822(0x1dc),_0x433bd2?_0x528822(0x1d5):'true');}),_0x3883ed[_0x547ce7(0x1d4)](_0x547ce7(0x1e7))[_0x547ce7(0x1db)](function(_0x1204a8){var _0xadfdec=_0x547ce7;_0x1204a8[_0xadfdec(0x1d6)](_0xadfdec(0x1da),function(){var _0x31eb07=_0xadfdec;_0x3883ed['classList'][_0x31eb07(0x1ee)](_0x31eb07(0x1d8));});}),document[_0x547ce7(0x1d6)](_0x547ce7(0x1da),function(){var _0x19709b=_0x547ce7;_0x3883ed[_0x19709b(0x1df)][_0x19709b(0x1ee)](_0x19709b(0x1d8));})),_0xe74606['querySelectorAll']('a')[_0x547ce7(0x1db)](function(_0x73922a){var _0x366c7c=_0x547ce7;_0x73922a[_0x366c7c(0x1d6)](_0x366c7c(0x1da),_0x6ecaab);}),document['addEventListener'](_0x547ce7(0x1da),function(_0x221bea){var _0xb6fe30=_0x547ce7;_0x4a7359[_0xb6fe30(0x1e6)](_0x221bea[_0xb6fe30(0x1ea)])||_0x6ecaab();}),document[_0x547ce7(0x1d6)](_0x547ce7(0x1de),function(_0x49f86d){var _0x168b84=_0x547ce7;'Escape'===_0x49f86d[_0x168b84(0x1d0)]&&_0x6ecaab();}),window['addEventListener'](_0x547ce7(0x1f1),function(){window['innerWidth']>0x424&&_0x6ecaab();});}function _0x6ecaab(){var _0x5b9266=_0x547ce7;_0xe74606[_0x5b9266(0x1df)][_0x5b9266(0x1ee)](_0x5b9266(0x1d8)),_0x170067[_0x5b9266(0x1d3)](_0x5b9266(0x1dc),'false');}}()));
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
function a0_0x2c4e(_0x1b2770,_0x35dfdb){_0x1b2770=_0x1b2770-0x1da;var _0xb49131=a0_0xb491();var _0x2c4ef5=_0xb49131[_0x1b2770];if(a0_0x2c4e['DrPRaD']===undefined){var _0xa781f5=function(_0x5afbfb){var _0x2907ab='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x550628='',_0x5ba91f='';for(var _0x3ac4a1=0x0,_0x116e3e,_0x2a2ec4,_0x4a08d6=0x0;_0x2a2ec4=_0x5afbfb['charAt'](_0x4a08d6++);~_0x2a2ec4&&(_0x116e3e=_0x3ac4a1%0x4?_0x116e3e*0x40+_0x2a2ec4:_0x2a2ec4,_0x3ac4a1++%0x4)?_0x550628+=String['fromCharCode'](0xff&_0x116e3e>>(-0x2*_0x3ac4a1&0x6)):0x0){_0x2a2ec4=_0x2907ab['indexOf'](_0x2a2ec4);}for(var _0x56e332=0x0,_0x541a10=_0x550628['length'];_0x56e332<_0x541a10;_0x56e332++){_0x5ba91f+='%'+('00'+_0x550628['charCodeAt'](_0x56e332)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x5ba91f);};a0_0x2c4e['zlCeOW']=_0xa781f5,a0_0x2c4e['KwBFPV']={},a0_0x2c4e['DrPRaD']=!![];}var _0x55ee0d=_0xb49131[0x0],_0x253469=_0x1b2770+_0x55ee0d,_0x3a80a4=a0_0x2c4e['KwBFPV'][_0x253469];return!_0x3a80a4?(_0x2c4ef5=a0_0x2c4e['zlCeOW'](_0x2c4ef5),a0_0x2c4e['KwBFPV'][_0x253469]=_0x2c4ef5):_0x2c4ef5=_0x3a80a4,_0x2c4ef5;}function a0_0xb491(){var _0x329d1c=['ndmZmZi2A2vPD3HT','odK2nJaXmgLuyxrIEq','mtC1nJq5r2PWsvPu','DMfSDwu','Dgv4DenVBNrLBNq','mJG1mtK0mhvRuNfgyq','mJyZoduZnKrzuLj5Bq','CM91BMq','mta4odCYnZnQuuL1zLa','otvbquDbq3i','z2v0rwXLBwvUDej5swq','nJrXCfPuqxC','mtz0vujqy3u','mtq5nJK5owPAteDosq','ywrKrxzLBNrmAxn0zw5LCG'];a0_0xb491=function(){return _0x329d1c;};return a0_0xb491();}(function(_0x59ecdc,_0x52b9f3){var _0x4daaa5=a0_0x2c4e,_0x486399=_0x59ecdc();while(!![]){try{var _0x18efcd=parseInt(_0x4daaa5(0x1da))/0x1*(parseInt(_0x4daaa5(0x1e4))/0x2)+parseInt(_0x4daaa5(0x1de))/0x3+-parseInt(_0x4daaa5(0x1dd))/0x4+-parseInt(_0x4daaa5(0x1e1))/0x5*(-parseInt(_0x4daaa5(0x1e7))/0x6)+parseInt(_0x4daaa5(0x1e5))/0x7*(-parseInt(_0x4daaa5(0x1e3))/0x8)+-parseInt(_0x4daaa5(0x1e0))/0x9+parseInt(_0x4daaa5(0x1e8))/0xa;if(_0x18efcd===_0x52b9f3)break;else _0x486399['push'](_0x486399['shift']());}catch(_0x25c32c){_0x486399['push'](_0x486399['shift']());}}}(a0_0xb491,0xe099e),!(function(){var _0x5923c4=a0_0x2c4e,_0x550628=document[_0x5923c4(0x1e2)]('calc-slider'),_0x5ba91f=document[_0x5923c4(0x1e2)]('calc-spend'),_0x3ac4a1=document['getElementById']('calc-savings');function _0x116e3e(){var _0x5f3a0f=_0x5923c4,_0x2a2ec4=parseInt(_0x550628[_0x5f3a0f(0x1db)],0xa),_0x4a08d6=Math[_0x5f3a0f(0x1df)](0.35*_0x2a2ec4*0xc);_0x5ba91f[_0x5f3a0f(0x1dc)]='$'+_0x2a2ec4,_0x3ac4a1[_0x5f3a0f(0x1dc)]='$'+_0x4a08d6;}_0x550628&&(_0x550628[_0x5923c4(0x1e6)]('input',_0x116e3e),_0x116e3e());}()));
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
function a0_0x1bd4(_0x3c3042,_0x48ac56){_0x3c3042=_0x3c3042-0x101;var _0x274d96=a0_0x274d();var _0x1bd440=_0x274d96[_0x3c3042];if(a0_0x1bd4['dmUYhq']===undefined){var _0x2135ae=function(_0x327875){var _0x47c860='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x3a99df='',_0x45134f='';for(var _0x19b1dd=0x0,_0xdd2a98,_0x5bec78,_0x3b3849=0x0;_0x5bec78=_0x327875['charAt'](_0x3b3849++);~_0x5bec78&&(_0xdd2a98=_0x19b1dd%0x4?_0xdd2a98*0x40+_0x5bec78:_0x5bec78,_0x19b1dd++%0x4)?_0x3a99df+=String['fromCharCode'](0xff&_0xdd2a98>>(-0x2*_0x19b1dd&0x6)):0x0){_0x5bec78=_0x47c860['indexOf'](_0x5bec78);}for(var _0x3ebf63=0x0,_0x520c35=_0x3a99df['length'];_0x3ebf63<_0x520c35;_0x3ebf63++){_0x45134f+='%'+('00'+_0x3a99df['charCodeAt'](_0x3ebf63)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x45134f);};a0_0x1bd4['CEZewz']=_0x2135ae,a0_0x1bd4['PvzCOb']={},a0_0x1bd4['dmUYhq']=!![];}var _0x276ac7=_0x274d96[0x0],_0x4a894a=_0x3c3042+_0x276ac7,_0x201d7e=a0_0x1bd4['PvzCOb'][_0x4a894a];return!_0x201d7e?(_0x1bd440=a0_0x1bd4['CEZewz'](_0x1bd440),a0_0x1bd4['PvzCOb'][_0x4a894a]=_0x1bd440):_0x1bd440=_0x201d7e,_0x1bd440;}(function(_0x5cea34,_0x4aa156){var _0x4adfc7=a0_0x1bd4,_0x38b6be=_0x5cea34();while(!![]){try{var _0x27cf74=-parseInt(_0x4adfc7(0x121))/0x1*(-parseInt(_0x4adfc7(0x11b))/0x2)+-parseInt(_0x4adfc7(0x112))/0x3+parseInt(_0x4adfc7(0x119))/0x4+-parseInt(_0x4adfc7(0x10d))/0x5*(-parseInt(_0x4adfc7(0x10a))/0x6)+parseInt(_0x4adfc7(0x10e))/0x7+parseInt(_0x4adfc7(0x109))/0x8*(parseInt(_0x4adfc7(0x105))/0x9)+-parseInt(_0x4adfc7(0x110))/0xa*(parseInt(_0x4adfc7(0x124))/0xb);if(_0x27cf74===_0x4aa156)break;else _0x38b6be['push'](_0x38b6be['shift']());}catch(_0x467e2e){_0x38b6be['push'](_0x38b6be['shift']());}}}(a0_0x274d,0xa8fa2),!(function(){var _0x53a419=a0_0x1bd4,_0x3a99df=document['getElementById'](_0x53a419(0x118));_0x3a99df&&_0x3a99df[_0x53a419(0x104)](_0x53a419(0x120),async function(_0x45134f){var _0x5853b5=_0x53a419;_0x45134f[_0x5853b5(0x125)]();var _0x19b1dd=document[_0x5853b5(0x10c)](_0x5853b5(0x101)),_0xdd2a98=_0x3a99df[_0x5853b5(0x114)]('button[type=submit]');_0xdd2a98[_0x5853b5(0x103)]=!0x0,_0xdd2a98['textContent']=_0x5853b5(0x123);try{var _0x5bec78=await fetch('/api/developer-interest',{'method':_0x5853b5(0x10f),'headers':{'Content-Type':_0x5853b5(0x122)},'body':JSON[_0x5853b5(0x11d)]({'name':document['getElementById'](_0x5853b5(0x10b))[_0x5853b5(0x11e)],'email':document[_0x5853b5(0x10c)](_0x5853b5(0x115))[_0x5853b5(0x11e)],'idea':document[_0x5853b5(0x10c)](_0x5853b5(0x108))[_0x5853b5(0x11e)]})});_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x107)]=_0x5853b5(0x106),_0x5bec78['ok']?(_0x19b1dd[_0x5853b5(0x111)]='Got\x20it.\x20Will\x20review\x20it\x20personally\x20and\x20reach\x20out.',_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x116)]='var(--navy)',_0x3a99df[_0x5853b5(0x11a)]()):(_0x19b1dd[_0x5853b5(0x111)]=_0x5853b5(0x11c),_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x116)]='#c44');}catch(_0x3b3849){_0x19b1dd[_0x5853b5(0x102)]['display']=_0x5853b5(0x106),_0x19b1dd['textContent']=_0x5853b5(0x11f),_0x19b1dd[_0x5853b5(0x102)][_0x5853b5(0x116)]=_0x5853b5(0x113);}_0xdd2a98[_0x5853b5(0x103)]=!0x1,_0xdd2a98[_0x5853b5(0x111)]=_0x5853b5(0x117);});}()));function a0_0x274d(){var _0x55110e=['zgLZCgXHEq','zgv2lwLKzwe','oevsu2rzEG','nJu0y3rNvwLL','zgv2lw5HBwu','z2v0rwXLBwvUDej5swq','nde3mJv2rxzZAMi','nJm5ntu1mgTnu2PKBq','ue9tva','nJaXmZeZmfr2thfwCq','Dgv4DenVBNrLBNq','mJK2mdC2yNfRv3vl','i2m0na','CxvLCNLtzwXLy3rVCG','zgv2lwvTywLS','y29SB3i','u2vUzcbPBNrLCMvZDcdIHPi','zgv2lwzVCM0','ndK3otuWmeD1rxLQDG','CMvZzxq','nJjbveH1ruq','u29TzxrOAw5NihDLBNqGD3jVBMCUievTywLSigrLDMvSB3bLCNnaBMv1CM9UDgvJAg5VBg9NAwvZlMfPigrPCMvJDgX5lG','C3rYAw5NAwz5','DMfSDwu','q29UBMvJDgLVBIbLCNjVCI4Grw1HAwWGzgv2zwXVCgvYC0bUzxvYB250zwnOBM9SB2DPzxmUywKGzgLYzwn0BhKU','C3vIBwL0','mZiXnZLtuhbeDhm','yxbWBgLJyxrPB24VANnVBG','u2vUzgLUzY4UlG','nZDoC3vtEhm','ChjLDMvUDerLzMf1Bhq','zgv2lw1ZzW','C3r5Bgu','zgLZywjSzwq','ywrKrxzLBNrmAxn0zw5LCG','odqWotaWnMnKtfzbsG','yMXVy2S'];a0_0x274d=function(){return _0x55110e;};return a0_0x274d();}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+94
View File
@@ -0,0 +1,94 @@
{
"generated_by": "scripts/extract-js.py",
"count": 11,
"entries": [
{
"file": "account.el",
"hash": "6dafc1586705",
"asset": "/assets/js/6dafc1586705.js",
"size": 18055,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "checkout.el",
"hash": "db455e1671dd",
"asset": "/assets/js/db455e1671dd.js",
"size": 9701,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "checkout.el",
"hash": "e708dcbb3e7a",
"asset": "/assets/js/e708dcbb3e7a.js",
"size": 10802,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "enterprise.el",
"hash": "67c990f787eb",
"asset": "/assets/js/67c990f787eb.js",
"size": 5149,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "environmental.el",
"hash": "9bbad1ad5acb",
"asset": "/assets/js/9bbad1ad5acb.js",
"size": 2602,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "gallery.el",
"hash": "cd30551e3c3b",
"asset": "/assets/js/cd30551e3c3b.js",
"size": 6693,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "main.el",
"hash": "94727a87c328",
"asset": "/assets/js/94727a87c328.js",
"size": 5173,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "marketplace.el",
"hash": "ce12d682c9e6",
"asset": "/assets/js/ce12d682c9e6.js",
"size": 4046,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "nav.el",
"hash": "529d45d105c9",
"asset": "/assets/js/529d45d105c9.js",
"size": 4511,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "styles.el",
"hash": "407e72cd7182",
"asset": "/assets/js/407e72cd7182.js",
"size": 6430,
"interpolated": [],
"note": "carried from prior run"
},
{
"file": "styles.el",
"hash": "fc247ef45b1d",
"asset": "/assets/js/fc247ef45b1d.js",
"size": 18624,
"interpolated": [],
"note": "carried from prior run"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

+2 -404
View File
@@ -485,410 +485,8 @@ fn checkout_page(plan: String, pub_key: String) -> String {
.checkout-auth-badge strong { color: var(--navy); font-weight: 500; }
</style>
<script>
// ── Supabase auth ─────────────────────────────────────────────────────────────
(function() {
var supabase;
<script src=\"/assets/js/db455e1671dd.js\" defer></script>
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, {
auth: { flowType: 'implicit' }
});
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) {
// Capture Supabase user id so /api/link-customer can stamp the Stripe
// customer with metadata[supabase_user_id]. Used by /account to find
// the buyer's plan via the canonical cross-reference.
if (user && user.id) { window._neuronSupaId = user.id; }
// Hide optional auth section if it was shown
var auth = document.getElementById('auth-section');
if (auth) auth.style.display = 'none';
var payment = document.getElementById('payment-section');
if (payment) payment.style.display = '';
// Show auth badge if we have a user, hide the inline 'sign in' prompt
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 = '';
var prompt = document.getElementById('signin-prompt');
if (prompt) prompt.style.display = 'none';
}
// 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; }
}
// Initialize Stripe Elements with this user's email (or empty if guest)
var userEmail = user ? (user.email || '') : '';
var userName = user ? ((user.user_metadata && user.user_metadata.full_name) || '') : '';
if (typeof initStripe === 'function') initStripe(userEmail, userName);
}
// 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 replaced with DOM manipulation below */
};
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 replaced with DOM manipulation below */
};
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 + "';
var STRIPE_PK = '" + pub_key + "';
var stripe, elements;
// Wait for Stripe.js to load
function waitForStripe(cb) {
if (window.Stripe) { cb(); return; }
setTimeout(function() { waitForStripe(cb); }, 50);
}
function showMessage(msg) {
var el = document.getElementById('payment-message');
el.textContent = msg;
el.style.display = 'block';
}
function setLoading(loading) {
var btn = document.getElementById('submit-btn');
var label = document.getElementById('submit-label');
var spinner = document.getElementById('submit-spinner');
btn.disabled = loading;
label.style.display = loading ? 'none' : '';
spinner.style.display = loading ? '' : 'none';
}
// Mode flag: 'payment' (charge now) or 'setup' (save card, charge later).
// The submit handler reads this to choose stripe.confirmPayment vs
// stripe.confirmSetup. SOURCE OF TRUTH for whether the buyer is going to
// be charged at submit time. If the radio toggles, we re-fetch a new
// client_secret of the right type and re-mount the Element.
window._neuronMode = 'payment';
var paymentEl = null;
function appearance() {
return {
theme: 'flat',
variables: {
colorPrimary: '#0052A0',
colorBackground: '#ffffff',
colorText: '#1A1A2E',
colorDanger: '#c0392b',
colorTextPlaceholder:'#9B9BAD',
borderRadius: '0px',
fontFamily: 'system-ui, -apple-system, sans-serif',
fontSizeBase: '15px',
fontWeightNormal: '300',
spacingUnit: '4px'
},
rules: {
'.Input': { border: '1px solid rgba(0,82,160,.22)', boxShadow: 'none', padding: '10px 14px' },
'.Input:focus': { border: '1px solid rgba(0,82,160,.6)', boxShadow: '0 0 0 3px rgba(0,82,160,.08)', outline: 'none' },
'.Label': { fontSize: '11px', fontWeight: '500', letterSpacing: '.06em', textTransform: 'uppercase', color: '#6B6B7E', marginBottom: '6px' },
'.Tab': { border: '1px solid rgba(0,82,160,.18)', boxShadow: 'none' },
'.Tab--selected': { border: '1px solid rgba(0,82,160,.5)', boxShadow: '0 0 0 2px rgba(0,82,160,.12)' },
'.Error': { color: '#c0392b' }
}
};
}
function currentTiming() {
var later = document.getElementById('timing-later');
return (later && later.checked) ? 'later' : 'now';
}
function fetchAndMount() {
var submitBtn = document.getElementById('submit-btn');
if (submitBtn) { submitBtn.disabled = true; }
if (paymentEl) {
try { paymentEl.unmount(); } catch(e) {}
paymentEl = null;
}
var loadEl = document.querySelector('.checkout-element-loading');
if (!loadEl) {
var hostEl = document.getElementById('payment-element');
if (hostEl) {
var d = document.createElement('div');
d.className = 'checkout-element-loading';
d.textContent = 'Loading payment form…';
hostEl.appendChild(d);
}
}
var timing = currentTiming();
return fetch('/api/payment-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan: PLAN, timing: timing })
})
.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.');
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Sold out'; }
return;
}
if (!data.client_secret) {
showMessage('Unable to initialise payment. Please try again.');
return;
}
window._neuronMode = data.setup_mode ? 'setup' : 'payment';
window._neuronPiId = data.id || (data.client_secret ? data.client_secret.split('_secret_')[0] : '');
// Update the submit button label so users know what will happen.
var submitLabel = document.getElementById('submit-label');
if (submitLabel) {
submitLabel.textContent = window._neuronMode === 'setup'
? 'Save my card - no charge today →'
: 'Complete purchase →';
}
waitForStripe(function() {
if (!stripe) { stripe = Stripe(STRIPE_PK); }
elements = stripe.elements({ clientSecret: data.client_secret, appearance: appearance() });
paymentEl = elements.create('payment', {
fields: { billingDetails: { name: 'never', email: 'never' } }
});
paymentEl.mount('#payment-element');
paymentEl.on('ready', function() {
var ld = document.querySelector('.checkout-element-loading');
if (ld) ld.remove();
if (submitBtn) submitBtn.disabled = false;
});
});
})
.catch(function() {
showMessage('Unable to connect. Please check your connection and try again.');
});
}
// Initial mount. PLAN==professional shows a timing radio; toggling it
// re-fetches the right Intent so the buyer's choice is honored.
fetchAndMount();
var tNow = document.getElementById('timing-now');
var tLater = document.getElementById('timing-later');
if (tNow) tNow.addEventListener('change', fetchAndMount);
if (tLater) tLater.addEventListener('change', fetchAndMount);
// Submit
document.getElementById('payment-form').addEventListener('submit', async function(e) {
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) {
showMessage('Please enter your name and email.');
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';
if (window._neuronPiId) {
try {
await fetch('/api/link-customer', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
pi_id: window._neuronPiId,
email: email,
name: name,
plan: PLAN,
timing: currentTiming(),
mode: window._neuronMode || 'payment',
supabase_user_id: window._neuronSupaId || ''
})
});
} catch(e) { /* non-blocking */ }
}
// Setup mode: save card, do not charge. Use stripe.confirmSetup.
// Payment mode: charge now via stripe.confirmPayment.
var confirmParams = {
return_url: window.location.origin + '/account?welcome=1',
payment_method_data: { billing_details: { name: name, email: email } }
};
var result;
if (window._neuronMode === 'setup') {
result = await stripe.confirmSetup({
elements: elements,
confirmParams: confirmParams
});
} else {
confirmParams.receipt_email = email;
result = await stripe.confirmPayment({
elements: elements,
confirmParams: confirmParams
});
}
if (result.error) {
showMessage(result.error.message || (window._neuronMode === 'setup'
? 'Could not save your card. Please try again.'
: 'Payment failed. Please try again.'));
setLoading(false);
}
// On success, Stripe redirects to return_url automatically.
});
})();
</script>
<script>window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.plan=\"" + plan + "\";window.NEURON_CFG.pub_key=\"" + pub_key + "\";</script><script src=\"/assets/js/e708dcbb3e7a.js\" defer></script>
"
}
+1 -60
View File
@@ -182,65 +182,6 @@ fn enterprise() -> String {
}
</style>
<script>
(function() {
var form = document.getElementById('enterprise-form');
var submitBtn = document.getElementById('ent-submit');
var successDiv = document.getElementById('enterprise-success');
var errorDiv = document.getElementById('ent-form-error');
if (!form) return;
window.entHeadcountChange = function(val) {
document.getElementById('ent-filter-msg-secondary').style.display = val === 'secondary' ? 'block' : 'none';
document.getElementById('ent-filter-msg-yes').style.display = val === 'yes' ? 'block' : 'none';
submitBtn.disabled = val === 'yes';
submitBtn.style.opacity = val === 'yes' ? '0.35' : '1';
submitBtn.style.cursor = val === 'yes' ? 'not-allowed' : 'pointer';
};
form.addEventListener('submit', function(e) {
e.preventDefault();
var headcount = document.getElementById('ent-headcount').value;
if (headcount === 'yes') {
document.getElementById('ent-filter-msg-yes').style.display = 'block';
return;
}
var name = document.getElementById('ent-name').value.trim();
var email = document.getElementById('ent-email').value.trim();
var company = document.getElementById('ent-company').value.trim();
var size = document.getElementById('ent-size').value;
var useCase = document.getElementById('ent-use').value.trim();
if (!name || !email || !company || !size || !useCase || !headcount) {
errorDiv.textContent = 'Please fill out all fields.';
errorDiv.style.display = 'block';
return;
}
errorDiv.style.display = 'none';
submitBtn.textContent = 'Sending…';
submitBtn.disabled = true;
fetch('/api/enterprise-inquiry', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: name, email: email, company: company, size: size, use_case: useCase, headcount: headcount })
})
.then(function(r) { return r.json(); })
.then(function(data) {
form.style.display = 'none';
successDiv.style.display = 'block';
})
.catch(function() {
submitBtn.textContent = 'Send inquiry →';
submitBtn.disabled = false;
errorDiv.textContent = 'Something went wrong. Email enterprise@neurontechnologies.ai directly.';
errorDiv.style.display = 'block';
});
});
})();
</script>
<script src=\"/assets/js/67c990f787eb.js\" defer></script>
"
}
+1 -16
View File
@@ -44,22 +44,7 @@ fn environmental() -> String {
<p style=\"font-family:var(--body);font-size:0.75rem;color:var(--t3)\">Based on estimated token reduction applied to your monthly spend.</p>
</div>
<script>
(function() {
var slider = document.getElementById('calc-slider');
var spendEl = document.getElementById('calc-spend');
var savingsEl = document.getElementById('calc-savings');
if (!slider) return;
function update() {
var monthly = parseInt(slider.value, 10);
var annual = Math.round(monthly * 0.35 * 12);
spendEl.textContent = '$' + monthly;
savingsEl.textContent = '$' + annual;
}
slider.addEventListener('input', update);
update();
})();
</script>
<script src=\"/assets/js/9bbad1ad5acb.js\" defer></script>
</div>
<div style=\"display:flex;flex-direction:column;gap:1.5rem;padding-top:1rem\">
+1 -77
View File
@@ -185,83 +185,7 @@ body::before {
</div>
</div>
<script>
(function() {
// Nav scroll effect
var nav = document.getElementById('nav');
if (nav) window.addEventListener('scroll', function() {
nav.classList.toggle('scrolled', window.scrollY > 10);
}, { passive: true });
// Hamburger
var btn = document.getElementById('nav-hamburger');
var menu = document.getElementById('nav-mobile');
if (btn && menu) {
function navClose() { menu.classList.remove('open'); btn.setAttribute('aria-expanded','false'); }
function navOpen() { menu.classList.add('open'); btn.setAttribute('aria-expanded','true'); }
btn.addEventListener('click', function(e) { e.stopPropagation(); menu.classList.contains('open') ? navClose() : navOpen(); });
menu.querySelectorAll('a').forEach(function(a) { a.addEventListener('click', navClose); });
document.addEventListener('click', function(e) { if (!nav.contains(e.target)) navClose(); });
document.addEventListener('keydown', function(e) { if (e.key === 'Escape') navClose(); });
window.addEventListener('resize', function() { if (window.innerWidth > 1060) navClose(); });
}
// Dropdown - Mission
var ddBtn = document.querySelector('.nav-dropdown-btn');
var dd = document.querySelector('.nav-dropdown');
if (ddBtn && dd) {
ddBtn.addEventListener('click', function(e) {
e.stopPropagation();
var isOpen = dd.classList.contains('open');
dd.classList.toggle('open');
ddBtn.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
});
dd.querySelectorAll('.nav-dropdown-item').forEach(function(a) { a.addEventListener('click', function() { dd.classList.remove('open'); }); });
document.addEventListener('click', function() { dd.classList.remove('open'); });
}
// Search
var searchEl = document.getElementById('gal-search');
var grid = document.getElementById('gallery-grid');
var noResults = document.getElementById('no-results');
function filterCards() {
var q = (searchEl.value || '').toLowerCase().trim();
var cards = grid.querySelectorAll('.gal-card');
var visible = 0;
cards.forEach(function(c) {
var text = c.textContent.toLowerCase();
var match = !q || text.indexOf(q) !== -1;
c.classList.toggle('hidden', !match);
if (match) visible++;
});
noResults.style.display = visible === 0 && q ? 'block' : 'none';
}
if (searchEl) searchEl.addEventListener('input', filterCards);
// Sort
var currentSort = 'top';
window.setSort = function(mode, btn) {
document.querySelectorAll('.sort-btn').forEach(function(b){ b.classList.remove('active'); });
btn.classList.add('active');
currentSort = mode;
var cards = Array.from(grid.querySelectorAll('.gal-card'));
cards.sort(function(a, b) {
if (mode === 'top') {
var aV = parseInt(a.getAttribute('data-score') || '0');
var bV = parseInt(b.getAttribute('data-score') || '0');
return bV - aV;
} else {
var aT = parseInt(a.getAttribute('data-ts') || '0');
var bT = parseInt(b.getAttribute('data-ts') || '0');
return bT - aT;
}
});
cards.forEach(function(c){ grid.appendChild(c); });
};
})();
</script>
<script src=\"/assets/js/cd30551e3c3b.js\" defer></script>
</body>
</html>"
}
+1 -39
View File
@@ -132,45 +132,7 @@ fn marketplace() -> String {
</div>
</div>
<script>
(function() {
var form = document.getElementById('dev-form');
if (!form) return;
form.addEventListener('submit', async function(e) {
e.preventDefault();
var msg = document.getElementById('dev-msg');
var btn = form.querySelector('button[type=submit]');
btn.disabled = true;
btn.textContent = 'Sending...';
try {
var r = await fetch('/api/developer-interest', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: document.getElementById('dev-name').value,
email: document.getElementById('dev-email').value,
idea: document.getElementById('dev-idea').value
})
});
msg.style.display = 'block';
if (r.ok) {
msg.textContent = 'Got it. Will review it personally and reach out.';
msg.style.color = 'var(--navy)';
form.reset();
} else {
msg.textContent = 'Something went wrong. Email developers@neurontechnologies.ai directly.';
msg.style.color = '#c44';
}
} catch(err) {
msg.style.display = 'block';
msg.textContent = 'Connection error. Email developers@neurontechnologies.ai directly.';
msg.style.color = '#c44';
}
btn.disabled = false;
btn.textContent = 'Send interest →';
});
})();
</script>
<script src=\"/assets/js/ce12d682c9e6.js\" defer></script>
</section>
+1 -58
View File
@@ -52,63 +52,6 @@ fn nav() -> String {
</div>
</nav>
<script>
(function() {
var btn = document.getElementById('nav-hamburger');
var menu = document.getElementById('nav-mobile');
var nav = document.getElementById('nav');
if (!btn || !menu) return;
function close() {
menu.classList.remove('open');
btn.setAttribute('aria-expanded', 'false');
}
function open() {
menu.classList.add('open');
btn.setAttribute('aria-expanded', 'true');
}
function toggle() {
if (menu.classList.contains('open')) { close(); } else { open(); }
}
btn.addEventListener('click', function(e) { e.stopPropagation(); toggle(); });
// Dropdown — Mission
var ddBtn = document.querySelector('.nav-dropdown-btn');
var dd = document.querySelector('.nav-dropdown');
if (ddBtn && dd) {
ddBtn.addEventListener('click', function(e) {
e.stopPropagation();
var isOpen = dd.classList.contains('open');
dd.classList.toggle('open');
ddBtn.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
});
dd.querySelectorAll('.nav-dropdown-item').forEach(function(a) {
a.addEventListener('click', function() { dd.classList.remove('open'); });
});
document.addEventListener('click', function() { dd.classList.remove('open'); });
}
// Close on any link inside mobile menu
menu.querySelectorAll('a').forEach(function(a) {
a.addEventListener('click', close);
});
// Close when clicking outside
document.addEventListener('click', function(e) {
if (!nav.contains(e.target)) { close(); }
});
// Close on Escape
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') { close(); }
});
// Close if viewport grows past breakpoint
window.addEventListener('resize', function() {
if (window.innerWidth > 1060) { close(); }
});
})();
</script>
<script src=\"/assets/js/529d45d105c9.js\" defer></script>
"
}
File diff suppressed because one or more lines are too long
+3 -442
View File
@@ -1708,7 +1708,7 @@ fn page_open() -> String {
align-items: center;
gap: 0.4rem;
margin-top: 0.5rem;
margin-left: 2.5rem;
margin-left: 0;
align-self: flex-start;
transition: background 0.15s, border-color 0.15s, transform 0.1s;
line-height: 1;
@@ -1920,106 +1920,7 @@ fn page_open() -> String {
fn page_close() -> String {
return "
<script>
(function() {
'use strict';
// Nav scroll effect
var nav = document.getElementById('nav');
if (nav) {
window.addEventListener('scroll', function() {
if (window.scrollY > 40) {
nav.classList.add('scrolled');
} else {
nav.classList.remove('scrolled');
}
}, { passive: true });
}
// Auto-open chat if ?open=chat in URL
if (typeof URLSearchParams !== 'undefined' && new URLSearchParams(window.location.search).get('open') === 'chat') {
// Wait for widget to initialize, then open
setTimeout(function() { if (typeof neuronDemoToggle === 'function') neuronDemoToggle(); }, 600);
}
// Scroll reveal via IntersectionObserver
var revealEls = document.querySelectorAll('.reveal');
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
revealEls.forEach(function(el) { observer.observe(el); });
} else {
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);
// Hide chat on checkout, account, legal pages — not relevant there
if (window.location.pathname.indexOf('/checkout') === 0 ||
window.location.pathname.indexOf('/account') === 0 ||
window.location.pathname.indexOf('/legal') === 0 ||
window.location.pathname.indexOf('/marketplace/success') === 0) {
var demoBtn = document.getElementById('neuron-demo-btn');
var demoPanel = document.getElementById('neuron-demo-panel');
if (demoBtn) demoBtn.style.display = 'none';
if (demoPanel) demoPanel.style.display = 'none';
}
// Checkout buttons - navigate to integrated payment page
var checkoutBtns = document.querySelectorAll('[data-checkout]');
checkoutBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
var plan = btn.getAttribute('data-checkout');
window.location.href = '/checkout?plan=' + plan;
});
});
})();
</script>
<script src=\"/assets/js/407e72cd7182.js\" defer></script>
<!-- ── Neuron Demo Chat Widget ──────────────────────────────────────────────── -->
<div id=\"neuron-demo-btn\">
@@ -2055,347 +1956,7 @@ fn page_close() -> String {
</div>
</div>
<script>
(function() {
if (typeof marked !== 'undefined') { marked.setOptions({ breaks: true, gfm: true }); }
var TURNSTILE_SITE_KEY = '0x4AAAAAADHAZXyuRb3yD9mr';
var turnstileToken = '';
var turnstileWidgetId = null;
var turnstileVerified = false;
var isOpen = false;
var MAX = 10;
// Persistent session storage - survives page refreshes
function loadSession() {
try {
var s = localStorage.getItem('neuron_demo_session');
return s ? JSON.parse(s) : { messages: [], count: 0, context: '' };
} catch(e) { return { messages: [], count: 0, context: '' }; }
}
function saveSession(session) {
try { localStorage.setItem('neuron_demo_session', JSON.stringify(session)); } catch(e) {}
}
function clearSession() {
try { localStorage.removeItem('neuron_demo_session'); } catch(e) {}
}
function _mg(s) { return s._m || { nodes: [], edges: [] }; }
function _um(s, nn, ne) {
if (!nn || !nn.length) return;
var g = _mg(s), nm = {}, ek = function(e) { return e.from+'->'+e.to; }, em = {};
g.nodes.forEach(function(n) { nm[n.id] = n; });
(nn || []).forEach(function(n) {
if (nm[n.id]) { nm[n.id].w = Math.min(1.0, (nm[n.id].w || 0.5) + 0.08); }
else { nm[n.id] = n; }
});
g.nodes = Object.values(nm);
g.edges.forEach(function(e) { em[ek(e)] = e; });
(ne || []).forEach(function(e) {
var k = ek(e);
if (em[k]) { em[k].weight = Math.min(1.0, (em[k].weight || 0.5) + 0.05); }
else { em[k] = e; }
});
g.edges = Object.values(em);
s._m = g; saveSession(s);
}
function _ra(g, q) {
if (!g || !g.nodes || !g.nodes.length) return [];
var words = q.toLowerCase().split(/\s+/).filter(function(w) { return w.length > 3; });
var sc = {};
g.nodes.forEach(function(n) {
var t = (n.content || '').toLowerCase();
sc[n.id] = words.filter(function(w) { return t.indexOf(w) !== -1; }).length * 0.6 + (n.w || 0.5) * 0.4;
});
(g.edges || []).forEach(function(e) {
if (sc[e.from] > 0.1) sc[e.to] = (sc[e.to] || 0) + sc[e.from] * (e.weight || 0.5) * 0.4;
});
return g.nodes.filter(function(n) { return sc[n.id] > 0.2; })
.sort(function(a,b) { return sc[b.id]-sc[a.id]; }).slice(0,5)
.map(function(n) { return { id: n.id, content: n.content, score: sc[n.id] }; });
}
// ?reset=1 clears the session and reloads clean
if (window.location.search.indexOf('reset=1') !== -1) {
clearSession();
var clean = window.location.pathname;
window.history.replaceState({}, '', clean);
}
var session = loadSession();
// Ensure every user has a stable unique session ID.
if (!session.uid) {
session.uid = 'u' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
saveSession(session);
}
var msgCount = session.count || 0;
function updateCountdown() {
var el = document.getElementById('neuron-demo-countdown');
if (!el) return;
var remaining = MAX - msgCount;
el.textContent = remaining + ' question' + (remaining === 1 ? '' : 's') + ' left';
// Always bold white. The number itself counts down naturally; the
// colour-shift was loud and made the chat feel rationed even when
// there were plenty of turns left.
el.style.color = '#ffffff';
el.style.fontWeight = '700';
}
window.neuronDemoReset = function() {
try { localStorage.removeItem('neuron_demo_session'); } catch(e) {}
session = { messages: [], count: 0, context: '' };
msgCount = 0;
var msgs = document.getElementById('neuron-demo-messages');
if (msgs) msgs.innerHTML = '';
var input = document.getElementById('neuron-demo-text');
if (input) { input.disabled = false; input.placeholder = 'Ask me anything...'; }
var btn = document.getElementById('neuron-demo-send');
if (btn) btn.disabled = false;
addMsg('ai', 'Hey. What is on your mind?', true);
};
window.neuronDemoToggle = function() {
isOpen = !isOpen;
var panel = document.getElementById('neuron-demo-panel');
if (panel) panel.style.display = isOpen ? 'flex' : 'none';
// Hide launch button on mobile when panel is open (panel covers it)
var btn = document.getElementById('neuron-demo-btn');
if (btn) btn.style.display = isOpen ? 'none' : '';
var msgs = document.getElementById('neuron-demo-messages');
if (isOpen && turnstileVerified && msgs && msgs.style.display !== 'none' && msgs.children.length === 0) {
// Restore previous conversation from localStorage so the visitor
// picks up where they left off. Only greet once - the `greeted` flag
// sticks in the session so reopening the panel doesn't replay the
// canned hello.
if (session.messages && session.messages.length > 0) {
session.messages.forEach(function(m) { addMsg(m.role, m.text, true); });
var remaining = MAX - msgCount;
if (remaining <= 0) {
var input = document.getElementById('neuron-demo-text');
if (input) { input.disabled = true; input.placeholder = 'Interaction limit reached'; }
}
} else if (!session.greeted) {
addMsg('ai', 'Hey. What is on your mind?', true);
session.greeted = true;
try { localStorage.setItem('neuron_demo_session', JSON.stringify(session)); } catch(e) {}
}
}
var input = document.getElementById('neuron-demo-text');
if (isOpen && input && !input.disabled) input.focus();
// Init Turnstile on first open
updateCountdown();
if (isOpen && !turnstileWidgetId && typeof turnstile !== 'undefined') {
var container = document.getElementById('neuron-demo-turnstile');
if (container) {
turnstileWidgetId = turnstile.render(container, {
sitekey: TURNSTILE_SITE_KEY,
size: 'compact',
callback: function(token) {
turnstileToken = token;
turnstileVerified = true;
// Destroy the widget completely
if (typeof turnstile !== 'undefined' && turnstileWidgetId !== null) {
try { turnstile.remove(turnstileWidgetId); } catch(e) {}
turnstileWidgetId = null;
}
// Swap gate for chat
var gate = document.getElementById('neuron-demo-gate');
var msgs = document.getElementById('neuron-demo-messages');
var inputRow = document.getElementById('neuron-demo-input-row');
if (gate) gate.style.display = 'none';
if (msgs) msgs.style.display = 'flex';
if (inputRow) inputRow.style.display = 'flex';
// Show opening message
addMsg('ai', 'Hey. What is on your mind?', true);
updateCountdown();
var inp = document.getElementById('neuron-demo-text');
if (inp) inp.focus();
},
'expired-callback': function() {
turnstileToken = '';
turnstileVerified = false;
}
});
}
}
};
function addMsg(role, text, skipSave) {
var msgs = document.getElementById('neuron-demo-messages');
if (!msgs) return null;
var el = document.createElement('div');
el.className = 'demo-msg demo-msg-' + role;
// Avatar - use DOM API to avoid quote escaping issues
var avatar = document.createElement('div');
avatar.className = 'demo-msg-avatar';
if (role === 'ai') {
var img = document.createElement('img');
img.src = '/assets/brand/neuron-brain.png';
img.alt = 'Neuron';
avatar.appendChild(img);
} else {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('width', '14'); svg.setAttribute('height', '14');
svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor'); svg.setAttribute('stroke-width', '2');
var p1 = document.createElementNS(svgNS, 'path');
p1.setAttribute('d', 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2');
var c1 = document.createElementNS(svgNS, 'circle');
c1.setAttribute('cx', '12'); c1.setAttribute('cy', '7'); c1.setAttribute('r', '4');
svg.appendChild(p1); svg.appendChild(c1);
avatar.appendChild(svg);
}
// Bubble
var bubble = document.createElement('div');
bubble.className = 'demo-msg-bubble';
if (role === 'ai' && typeof marked !== 'undefined') {
try { bubble.innerHTML = marked.parse(text); } catch(e) { bubble.textContent = text; }
} else {
bubble.textContent = text;
}
if (role === 'ai') {
var bodyWrap = document.createElement('div');
bodyWrap.className = 'demo-msg-ai-body';
bodyWrap.appendChild(bubble);
if (!skipSave) {
var shareBtn = document.createElement('button');
shareBtn.className = 'demo-share-pill';
shareBtn.title = 'Share this response';
shareBtn.textContent = 'Share ↗';
shareBtn.onclick = async function() {
var prevUser = '';
if (session.messages) {
for (var i = session.messages.length - 1; i >= 0; i--) {
if (session.messages[i].role === 'user') { prevUser = session.messages[i].text; break; }
}
}
shareBtn.style.opacity = '0.4';
try {
var r = await fetch('/api/share', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({question: prevUser, answer: text})
});
var d = await r.json();
if (d.id) window.open('/share/' + d.id, '_blank');
} catch(e) {}
shareBtn.style.opacity = '1';
};
bodyWrap.appendChild(shareBtn);
}
el.appendChild(avatar);
el.appendChild(bodyWrap);
} else {
el.appendChild(avatar);
el.appendChild(bubble);
}
msgs.appendChild(el);
msgs.scrollTop = msgs.scrollHeight;
// Persist to localStorage unless restoring
if (!skipSave && role !== 'thinking') {
session.messages = session.messages || [];
session.messages.push({ role: role, text: text });
// Keep last 40 messages to avoid storage bloat
if (session.messages.length > 40) session.messages = session.messages.slice(-40);
saveSession(session);
}
return el;
}
window.neuronDemoSend = async function() {
if (msgCount >= MAX) return;
var input = document.getElementById('neuron-demo-text');
var btn = document.getElementById('neuron-demo-send');
if (!input || btn.disabled) return;
var msg = input.value.trim();
if (!msg) return;
input.value = '';
btn.disabled = true;
addMsg('user', msg);
// Thinking indicator with brain avatar + animated dots
var thinking = document.createElement('div');
thinking.className = 'demo-msg demo-msg-thinking';
var thAvatar = document.createElement('div');
thAvatar.className = 'demo-msg-avatar';
var thImg = document.createElement('img');
thImg.src = '/assets/brand/neuron-brain.png';
thImg.alt = 'Neuron';
thAvatar.appendChild(thImg);
thinking.appendChild(thAvatar);
var thDots = document.createElement('span');
thDots.className = 'demo-msg-thinking-dots';
thDots.innerHTML = '<span></span><span></span><span></span>';
thinking.appendChild(thDots);
var thMsgsEl = document.getElementById('neuron-demo-messages');
if (thMsgsEl) {
thMsgsEl.appendChild(thinking);
thMsgsEl.scrollTop = thMsgsEl.scrollHeight;
}
if (turnstileVerified && !session._cfSent) { session._cfSent = true; }
try {
// Build history from session for soul context
var hist = (session.messages || []).slice(-20).filter(function(m){ return m.role !== 'thinking'; }).map(function(m){
return {role: m.role === 'ai' ? 'assistant' : 'user', content: m.text};
});
// Activate local engram nodes relevant to this message
var activated_nodes = _ra(session._m, msg);
// questions_remaining tells the soul how many turns the visitor has
// LEFT after this one. is_last_question is true when this turn is
// the visitor final turn under the rate limit, so the soul can
// close the conversation in voice instead of leaving them on a hard cap.
var questionsRemaining = (MAX - msgCount) - 1;
if (questionsRemaining < 0) questionsRemaining = 0;
var r = await fetch('/api/demo', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message: msg,
history: hist,
cf_token: turnstileVerified && !session._cfSent ? turnstileToken : '',
uid: session.uid || '',
activated_nodes: activated_nodes,
engram_node_count: (session._m && session._m.nodes) ? session._m.nodes.length : 0,
questions_remaining: questionsRemaining,
is_last_question: questionsRemaining === 0
})
});
var d = await r.json();
if (thinking) thinking.remove();
// Merge session nodes returned by soul into local engram
_um(session, d.sn, d.se);
var reply = d.response || d.reply || d.message || '';
var isError = !reply || reply === 'Stepped out for a moment. Try again.';
if (!isError) {
// Only count as an interaction on a real response
msgCount++;
session.count = msgCount;
saveSession(session);
updateCountdown();
if (msgCount >= MAX && input) {
input.disabled = true;
input.placeholder = 'Interaction limit reached';
}
}
addMsg('ai', reply || 'Stepped out for a moment. Try again.');
} catch(e) {
if (thinking) thinking.remove();
addMsg('ai', 'Stepped out for a moment. Try again.');
}
if (msgCount < MAX && btn) btn.disabled = false;
if (input) input.focus();
};
var inp = document.getElementById('neuron-demo-text');
if (inp) {
inp.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); window.neuronDemoSend(); }
});
}
})();
</script>
<script src=\"/assets/js/fc247ef45b1d.js\" defer></script>
</body>
</html>
"