ux: chat header + greeting + brain avatar + share button + terms + badge
Restores inline-JS styles.el (the obfuscated /assets/js/*.js path
broke the chat widget) and threads four UX fixes through it:
- Chat header: drop the all-caps "NEURON / Live Demo" subtitle, keep
just "Neuron" in bold white at 1rem.
- Greeting: replace the canned "Hi. I am Neuron. You get 5 questions"
with "Hey. What is on your mind?" in all three callsites (open,
reset, turnstile-verified). The questions counter is already in
the header, so we don't need to repeat it.
- Avatar: switch chat avatar from neuron-icon.png to the brain mark
(/assets/brand/neuron-brain.png). Thinking state now shows the
pulsing brain plus three-dot animation instead of "thinking..."
text.
- Share button: from a muted t3 link to a navy pill with subtle
background, uppercase letter-spacing, and active-press feedback.
main.el: drop the FOUNDING_SOLD = 47 floor and rewrite
fetch_founding_count_stripe to count succeeded PaymentIntents (the
live Stripe Elements path) AND legacy Checkout Sessions, excluding
refunded charges.
founding_badge.el: rewrite to the Option C "Tag" design picked from
/tmp/founding-badge-preview.html - tall card, navy top stroke, brain
mark prominent, "Neuron, LLC" footer.
terms.el: kill the "Neuron Inference - our own inference layer, live
now" claim. Inference launches Q3 2026 and the wording now says so.
This commit is contained in:
+61
-105
@@ -1,63 +1,25 @@
|
||||
// founding_badge.el - Founding Member badge component.
|
||||
// founding_badge.el - Founding Member badge (Option C: Tag).
|
||||
//
|
||||
// Renders the certificate-style badge with guilloché background,
|
||||
// navy colour scheme, and member number. Matches the React archive design.
|
||||
// Used on the post-purchase success page and as a shareable /founding-badge route.
|
||||
// Tall card, navy top stroke, brain logo prominent, member number,
|
||||
// "Neuron, LLC" footer. Matches the design picked from
|
||||
// /tmp/founding-badge-preview.html.
|
||||
|
||||
fn founding_badge(member_number: Int) -> String {
|
||||
let num_str: String = int_to_str(member_number)
|
||||
let show_number: Bool = member_number > 0
|
||||
let number_html: String = if show_number {
|
||||
"<div style=\"display:flex;flex-direction:column;align-items:center;gap:0;\">
|
||||
<span style=\"font-size:8px;font-weight:700;letter-spacing:.20em;text-transform:uppercase;color:#0052A0;line-height:1;margin-bottom:-8px;position:relative;z-index:1;\">No.</span>
|
||||
<p style=\"font-size:42px;font-weight:700;color:#0D0D14;letter-spacing:-.02em;line-height:.85;margin:0;\">" + num_str + "</p>
|
||||
<p style=\"font-size:8px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#0052A0;margin:0;\">of 1,000</p>
|
||||
</div>"
|
||||
"<div class=\"founding-badge-num\">#" + num_str + "</div>
|
||||
<div class=\"founding-badge-of\">of 1,000</div>"
|
||||
} else {
|
||||
"<p style=\"font-size:8px;letter-spacing:.18em;text-transform:uppercase;color:rgba(0,82,160,.45);margin:0;\">Your number awaits</p>"
|
||||
"<div class=\"founding-badge-pending\">Your number awaits</div>"
|
||||
}
|
||||
return "
|
||||
<div class=\"founding-badge\" aria-label=\"Founding Member badge\">
|
||||
<!-- Guilloché background SVG -->
|
||||
<svg aria-hidden=\"true\" class=\"founding-badge-bg\" viewBox=\"0 0 260 300\" preserveAspectRatio=\"none\">
|
||||
<defs>
|
||||
<pattern id=\"hatch\" width=\"6\" height=\"6\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">
|
||||
<line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"6\" stroke=\"rgba(0,82,160,.055)\" stroke-width=\"0.5\"/>
|
||||
</pattern>
|
||||
<pattern id=\"dots\" width=\"10\" height=\"10\" patternUnits=\"userSpaceOnUse\">
|
||||
<circle cx=\"5\" cy=\"5\" r=\"0.6\" fill=\"rgba(0,82,160,.12)\"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width=\"260\" height=\"300\" fill=\"rgba(0,82,160,.025)\"/>
|
||||
<rect width=\"260\" height=\"300\" fill=\"url(#hatch)\"/>
|
||||
<rect width=\"260\" height=\"300\" fill=\"url(#dots)\"/>
|
||||
<!-- Guilloché wave bands -->
|
||||
<path d=\"M 0,40 C 26,34 52,46 78,40 S 130,34 156,40 S 208,46 260,40\" fill=\"none\" stroke=\"rgba(0,82,160,.18)\" stroke-width=\"0.7\"/>
|
||||
<path d=\"M 0,38 C 29,44 58,32 87,38 S 145,44 174,38 S 232,32 260,38\" fill=\"none\" stroke=\"rgba(0,82,160,.12)\" stroke-width=\"0.7\"/>
|
||||
<path d=\"M 0,260 C 26,254 52,266 78,260 S 130,254 156,260 S 208,266 260,260\" fill=\"none\" stroke=\"rgba(0,82,160,.18)\" stroke-width=\"0.7\"/>
|
||||
<path d=\"M 0,258 C 29,264 58,252 87,258 S 145,264 174,258 S 232,252 260,258\" fill=\"none\" stroke=\"rgba(0,82,160,.12)\" stroke-width=\"0.7\"/>
|
||||
<!-- Double-line ornate border -->
|
||||
<rect x=\"6\" y=\"6\" width=\"248\" height=\"288\" fill=\"none\" stroke=\"rgba(0,82,160,.30)\" stroke-width=\"0.75\"/>
|
||||
<rect x=\"10\" y=\"10\" width=\"240\" height=\"280\" fill=\"none\" stroke=\"rgba(0,82,160,.15)\" stroke-width=\"0.5\" stroke-dasharray=\"4 3\"/>
|
||||
<!-- Corner rosettes -->
|
||||
<g><circle cx=\"6\" cy=\"6\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"6\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"6\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"6\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<g><circle cx=\"254\" cy=\"6\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"6\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"6\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"6\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<g><circle cx=\"6\" cy=\"294\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"294\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"294\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"6\" cy=\"294\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<g><circle cx=\"254\" cy=\"294\" r=\"14\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"294\" r=\"9\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"294\" r=\"5\" fill=\"none\" stroke=\"rgba(0,82,160,.22)\" stroke-width=\"0.6\"/><circle cx=\"254\" cy=\"294\" r=\"1.7\" fill=\"rgba(0,82,160,.25)\"/></g>
|
||||
<!-- Micro text bands -->
|
||||
<text x=\"130\" y=\"22\" text-anchor=\"middle\" font-size=\"4.5\" font-family=\"system-ui,sans-serif\" letter-spacing=\"3\" fill=\"rgba(0,82,160,.28)\" text-length=\"220\" length-adjust=\"spacing\">NEURON TECHNOLOGIES · FOUNDING MEMBER · NEURON TECHNOLOGIES · FOUNDING MEMBER</text>
|
||||
<text x=\"130\" y=\"286\" text-anchor=\"middle\" font-size=\"4.5\" font-family=\"system-ui,sans-serif\" letter-spacing=\"3\" fill=\"rgba(0,82,160,.28)\" text-length=\"220\" length-adjust=\"spacing\">NEURON TECHNOLOGIES · FOUNDING MEMBER · NEURON TECHNOLOGIES · FOUNDING MEMBER</text>
|
||||
</svg>
|
||||
|
||||
<!-- Badge content -->
|
||||
<div class=\"founding-badge-inner\">
|
||||
<p class=\"founding-badge-label\">Founding Member</p>
|
||||
<div class=\"founding-badge-rule\"></div>
|
||||
<div class=\"founding-badge-icon\">
|
||||
<img src=\"/assets/neuron-icon.png\" alt=\"Neuron\" width=\"34\" height=\"34\">
|
||||
</div>
|
||||
" + number_html + "
|
||||
</div>
|
||||
<img class=\"founding-badge-brain\" src=\"/assets/brand/neuron-brain.png\" alt=\"Neuron\" width=\"40\" height=\"40\">
|
||||
<div class=\"founding-badge-eye\">Founding Member</div>
|
||||
" + number_html + "
|
||||
<div class=\"founding-badge-line\"></div>
|
||||
<div class=\"founding-badge-name\">Neuron, LLC</div>
|
||||
</div>
|
||||
"
|
||||
}
|
||||
@@ -65,72 +27,66 @@ fn founding_badge(member_number: Int) -> String {
|
||||
fn founding_badge_css() -> String {
|
||||
return "
|
||||
<style>
|
||||
@keyframes fb-glow-pulse {
|
||||
0%,100% { box-shadow: 0 0 0 3px rgba(0,82,160,.08), 0 0 18px rgba(0,82,160,.22), 0 0 36px rgba(0,82,160,.10); }
|
||||
50% { box-shadow: 0 0 0 4px rgba(0,82,160,.12), 0 0 28px rgba(0,82,160,.32), 0 0 56px rgba(0,82,160,.16); }
|
||||
}
|
||||
@keyframes fb-icon-float {
|
||||
0%,100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-2px); }
|
||||
}
|
||||
.founding-badge {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 260px;
|
||||
background: #FAFAF8;
|
||||
border: 1px solid rgba(0,120,212,.5);
|
||||
animation: fb-glow-pulse 3s ease-in-out infinite;
|
||||
overflow: hidden;
|
||||
}
|
||||
.founding-badge-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.founding-badge-inner {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0,82,160,.18);
|
||||
border-top: 4px solid #0052A0;
|
||||
padding: 1.5rem 1.25rem 1.125rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 32px 28px 28px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
gap: .25rem;
|
||||
box-shadow: 0 4px 24px rgba(0,82,160,.10);
|
||||
font-family: 'IBM Plex Sans', system-ui, sans-serif;
|
||||
}
|
||||
.founding-badge-label {
|
||||
font-family: var(--body,system-ui,sans-serif);
|
||||
font-size: 8.5px;
|
||||
.founding-badge-brain {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin-bottom: .5rem;
|
||||
opacity: .92;
|
||||
}
|
||||
.founding-badge-eye {
|
||||
font-size: .65rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: .30em;
|
||||
letter-spacing: .2em;
|
||||
text-transform: uppercase;
|
||||
color: rgba(0,82,160,.65);
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
.founding-badge-num {
|
||||
font-family: 'Playfair Display', Georgia, serif;
|
||||
font-size: 2.6rem;
|
||||
font-weight: 600;
|
||||
color: #0052A0;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
line-height: 1.05;
|
||||
}
|
||||
.founding-badge-rule {
|
||||
width: 48px;
|
||||
.founding-badge-of {
|
||||
font-size: .65rem;
|
||||
font-weight: 400;
|
||||
color: #6B6B7E;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
.founding-badge-pending {
|
||||
font-size: .65rem;
|
||||
font-weight: 500;
|
||||
color: rgba(0,82,160,.55);
|
||||
letter-spacing: .15em;
|
||||
text-transform: uppercase;
|
||||
padding: .5rem 0;
|
||||
}
|
||||
.founding-badge-line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(0,82,160,.45), transparent);
|
||||
background: rgba(0,82,160,.12);
|
||||
margin: .65rem 0 .5rem;
|
||||
}
|
||||
.founding-badge-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,.85);
|
||||
border: 1px solid rgba(0,82,160,.2);
|
||||
box-shadow: 0 0 0 4px rgba(0,82,160,.06);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.founding-badge-icon img {
|
||||
display: block;
|
||||
animation: fb-icon-float 4s ease-in-out infinite;
|
||||
.founding-badge-name {
|
||||
font-size: .625rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .14em;
|
||||
text-transform: uppercase;
|
||||
color: #6B6B7E;
|
||||
}
|
||||
</style>
|
||||
"
|
||||
|
||||
+39
-26
@@ -49,7 +49,7 @@ from account import { account_page }
|
||||
// ── Founding counter ──────────────────────────────────────────────────────────
|
||||
|
||||
let FOUNDING_TOTAL: Int = 1000
|
||||
let FOUNDING_SOLD: Int = 47 // floor — real count comes from Stripe at startup
|
||||
let FOUNDING_SOLD: Int = 0 // no artificial floor - real count comes from Stripe
|
||||
|
||||
// ── Founding count helpers ─────────────────────────────────────────────────────
|
||||
|
||||
@@ -76,35 +76,48 @@ fn fetch_founding_count_stripe(stripe_key: String) -> Int {
|
||||
if str_eq(stripe_key, "") {
|
||||
return FOUNDING_SOLD
|
||||
}
|
||||
// Fetch all complete checkout sessions and count founding ones.
|
||||
// Note: metadata[] filter in URL causes 400 — filter client-side instead.
|
||||
let url: String = "https://api.stripe.com/v1/checkout/sessions?status=complete&limit=100"
|
||||
let resp: String = http_get_auth(url, stripe_key)
|
||||
if str_eq(resp, "") {
|
||||
return FOUNDING_SOLD
|
||||
}
|
||||
let data_raw: String = json_get_raw(resp, "data")
|
||||
if str_eq(data_raw, "") {
|
||||
return FOUNDING_SOLD
|
||||
}
|
||||
// Count only sessions where metadata.plan = "founding"
|
||||
// json_get uses literal key search, so extract metadata object first
|
||||
let total: Int = json_array_len(data_raw)
|
||||
let count: Int = 0
|
||||
let idx: Int = 0
|
||||
while idx < total {
|
||||
let session: String = json_array_get(data_raw, idx)
|
||||
// json_get_raw handles multi-line JSON; json_get stops at newlines
|
||||
let metadata: String = json_get_raw(session, "metadata")
|
||||
let plan: String = if str_eq(metadata, "") { "" } else { json_get(metadata, "plan") }
|
||||
if str_eq(plan, "founding") {
|
||||
let count = count + 1
|
||||
|
||||
// 1) PaymentIntents (Stripe Elements path - this is what the live site uses)
|
||||
let pi_url: String = "https://api.stripe.com/v1/payment_intents?limit=100"
|
||||
let pi_resp: String = http_get_auth(pi_url, stripe_key)
|
||||
let pi_data: String = if str_eq(pi_resp, "") { "" } else { json_get_raw(pi_resp, "data") }
|
||||
if !str_eq(pi_data, "") {
|
||||
let pi_total: Int = json_array_len(pi_data)
|
||||
let pi_idx: Int = 0
|
||||
while pi_idx < pi_total {
|
||||
let pi: String = json_array_get(pi_data, pi_idx)
|
||||
let status: String = json_get(pi, "status")
|
||||
let metadata: String = json_get_raw(pi, "metadata")
|
||||
let plan: String = if str_eq(metadata, "") { "" } else { json_get(metadata, "plan") }
|
||||
// Count only fully-charged founding PIs that are not refunded
|
||||
let amt_refunded: String = json_get(pi, "amount_refunded")
|
||||
let refunded_n: Int = str_to_int(amt_refunded)
|
||||
if str_eq(status, "succeeded") && str_eq(plan, "founding") && refunded_n <= 0 {
|
||||
let count = count + 1
|
||||
}
|
||||
let pi_idx = pi_idx + 1
|
||||
}
|
||||
let idx = idx + 1
|
||||
}
|
||||
if count <= 0 {
|
||||
return FOUNDING_SOLD
|
||||
|
||||
// 2) Hosted Checkout Sessions (legacy /api/checkout path - kept for completeness)
|
||||
let cs_url: String = "https://api.stripe.com/v1/checkout/sessions?status=complete&limit=100"
|
||||
let cs_resp: String = http_get_auth(cs_url, stripe_key)
|
||||
let cs_data: String = if str_eq(cs_resp, "") { "" } else { json_get_raw(cs_resp, "data") }
|
||||
if !str_eq(cs_data, "") {
|
||||
let cs_total: Int = json_array_len(cs_data)
|
||||
let cs_idx: Int = 0
|
||||
while cs_idx < cs_total {
|
||||
let session: String = json_array_get(cs_data, cs_idx)
|
||||
let metadata: String = json_get_raw(session, "metadata")
|
||||
let plan: String = if str_eq(metadata, "") { "" } else { json_get(metadata, "plan") }
|
||||
if str_eq(plan, "founding") {
|
||||
let count = count + 1
|
||||
}
|
||||
let cs_idx = cs_idx + 1
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
|
||||
+81
-33
@@ -1519,19 +1519,13 @@ fn page_open() -> String {
|
||||
}
|
||||
#neuron-demo-header span {
|
||||
font-family: var(--body);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
#neuron-demo-header-sub {
|
||||
font-family: var(--body);
|
||||
font-size: 0.625rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: rgba(255,255,255,.85);
|
||||
text-transform: uppercase;
|
||||
margin-top: 0.1rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
}
|
||||
#neuron-demo-header-sub { display: none; }
|
||||
#neuron-demo-close {
|
||||
background: none;
|
||||
border: none;
|
||||
@@ -1666,29 +1660,66 @@ fn page_open() -> String {
|
||||
.demo-msg-thinking {
|
||||
align-self: flex-start;
|
||||
color: var(--t3);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.08em;
|
||||
font-style: italic;
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.04em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
.demo-msg-thinking .demo-msg-avatar {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.demo-msg-thinking .demo-msg-avatar img {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
animation: brainPulse 1.4s ease-in-out infinite;
|
||||
}
|
||||
.demo-msg-thinking-dots { display: inline-flex; gap: 3px; }
|
||||
.demo-msg-thinking-dots span {
|
||||
width: 5px; height: 5px; border-radius: 50%;
|
||||
background: rgba(0,82,160,0.55);
|
||||
animation: thinkDot 1.2s ease-in-out infinite;
|
||||
}
|
||||
.demo-msg-thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
|
||||
.demo-msg-thinking-dots span:nth-child(3) { animation-delay: 0.3s; }
|
||||
@keyframes thinkDot {
|
||||
0%, 80%, 100% { opacity: 0.25; transform: scale(0.85); }
|
||||
40% { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
@keyframes brainPulse {
|
||||
0%, 100% { opacity: 0.55; transform: scale(0.92); }
|
||||
50% { opacity: 1; transform: scale(1.05); }
|
||||
}
|
||||
.demo-share-pill {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--t3);
|
||||
background: rgba(0,82,160,0.07);
|
||||
border: 1px solid rgba(0,82,160,0.22);
|
||||
color: var(--navy);
|
||||
cursor: pointer;
|
||||
padding: 0.2rem 0;
|
||||
padding: 0.4rem 0.75rem;
|
||||
font-family: var(--body);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.04em;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin-top: 0.3rem;
|
||||
gap: 0.4rem;
|
||||
margin-top: 0.5rem;
|
||||
margin-left: 2.5rem;
|
||||
align-self: flex-start;
|
||||
transition: color 0.15s;
|
||||
transition: background 0.15s, border-color 0.15s, transform 0.1s;
|
||||
line-height: 1;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.demo-share-pill:hover { color: var(--navy); }
|
||||
.demo-share-pill:hover {
|
||||
background: rgba(0,82,160,0.14);
|
||||
border-color: rgba(0,82,160,0.45);
|
||||
}
|
||||
.demo-share-pill:active { transform: scale(0.97); }
|
||||
.demo-share-pill svg { width: 11px; height: 11px; }
|
||||
|
||||
#neuron-demo-input-row {
|
||||
display: flex;
|
||||
@@ -1993,7 +2024,7 @@ fn page_close() -> String {
|
||||
<!-- ── Neuron Demo Chat Widget ──────────────────────────────────────────────── -->
|
||||
<div id=\"neuron-demo-btn\">
|
||||
<button onclick=\"neuronDemoToggle()\" aria-label=\"Chat with Neuron\">
|
||||
<img src=\"/assets/neuron-icon.png\" alt=\"\">
|
||||
<img src=\"/assets/brand/neuron-brain.png\" alt=\"\">
|
||||
Try Neuron
|
||||
</button>
|
||||
</div>
|
||||
@@ -2001,7 +2032,7 @@ fn page_close() -> String {
|
||||
<div id=\"neuron-demo-panel\" style=\"display:none\">
|
||||
<div id=\"neuron-demo-header\">
|
||||
<div id=\"neuron-demo-header-left\">
|
||||
<img src=\"/assets/neuron-icon.png\" alt=\"Neuron\">
|
||||
<img src=\"/assets/brand/neuron-brain.png\" alt=\"Neuron\">
|
||||
<div>
|
||||
<span>Neuron</span>
|
||||
<div id=\"neuron-demo-header-sub\">Live Demo</div>
|
||||
@@ -2118,7 +2149,7 @@ fn page_close() -> String {
|
||||
if (input) { input.disabled = false; input.placeholder = 'Ask me anything...'; }
|
||||
var btn = document.getElementById('neuron-demo-send');
|
||||
if (btn) btn.disabled = false;
|
||||
addMsg('ai', 'Hi. I am Neuron. You get 5 questions.', true);
|
||||
addMsg('ai', 'Hey. What is on your mind?', true);
|
||||
};
|
||||
|
||||
window.neuronDemoToggle = function() {
|
||||
@@ -2139,7 +2170,7 @@ fn page_close() -> String {
|
||||
if (input) { input.disabled = true; input.placeholder = 'Interaction limit reached'; }
|
||||
}
|
||||
} else {
|
||||
addMsg('ai', 'Hi. I am Neuron. You get 5 questions.', true);
|
||||
addMsg('ai', 'Hey. What is on your mind?', true);
|
||||
}
|
||||
}
|
||||
var input = document.getElementById('neuron-demo-text');
|
||||
@@ -2168,7 +2199,7 @@ fn page_close() -> String {
|
||||
if (msgs) msgs.style.display = 'flex';
|
||||
if (inputRow) inputRow.style.display = 'flex';
|
||||
// Show opening message
|
||||
addMsg('ai', 'Hi. I am Neuron. You get 5 questions.', true);
|
||||
addMsg('ai', 'Hey. What is on your mind?', true);
|
||||
updateCountdown();
|
||||
var inp = document.getElementById('neuron-demo-text');
|
||||
if (inp) inp.focus();
|
||||
@@ -2192,7 +2223,7 @@ fn page_close() -> String {
|
||||
avatar.className = 'demo-msg-avatar';
|
||||
if (role === 'ai') {
|
||||
var img = document.createElement('img');
|
||||
img.src = '/assets/neuron-icon.png';
|
||||
img.src = '/assets/brand/neuron-brain.png';
|
||||
img.alt = 'Neuron';
|
||||
avatar.appendChild(img);
|
||||
} else {
|
||||
@@ -2275,8 +2306,25 @@ fn page_close() -> String {
|
||||
input.value = '';
|
||||
btn.disabled = true;
|
||||
addMsg('user', msg);
|
||||
var thinking = addMsg('thinking', 'thinking...');
|
||||
if (thinking) thinking.className = 'demo-msg demo-msg-thinking';
|
||||
// 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
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ fn terms_body() -> String {
|
||||
<h2 style=\"font-family:var(--head);font-size:1.25rem;font-weight:600;color:var(--t1)\">Inference</h2>
|
||||
</div>
|
||||
<p style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8;margin-bottom:1rem\">Neuron routes inference to whatever provider you configure - your own keys for OpenAI, Anthropic, Grok, any compatible endpoint. When you use a third-party provider, your prompts go through their infrastructure. Their terms apply. I don't control what they do with it.</p>
|
||||
<p style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8\">Neuron Inference - our own inference layer, live now - doesn't store your requests, doesn't train on them, doesn't share them. A separate data addendum covers the details.</p>
|
||||
<p style=\"font-family:var(--body);font-weight:300;font-size:0.9375rem;color:var(--t2);line-height:1.8\">Neuron Inference - our own inference layer, launching Q3 2026 - will not store your requests, will not train on them, and will not share them. A separate data addendum will cover the details when it launches.</p>
|
||||
</div>
|
||||
|
||||
<div style=\"margin-bottom:3rem\">
|
||||
|
||||
Reference in New Issue
Block a user