21a7c07547
Callout above the provider list recommends o4-mini/o3, Claude Sonnet 4, Gemini 2.5 Pro, or Grok-3 for best performance, notes that model choice happens in the app, and points to Neuron Inference launching Q3 2026.
995 lines
42 KiB
EmacsLisp
995 lines
42 KiB
EmacsLisp
// components/account.el - Account dashboard page.
|
|
// Client-rendered: El serves the HTML shell, JS handles auth + data.
|
|
|
|
from founding_badge import { founding_badge, founding_badge_css }
|
|
|
|
extern fn el_html_doc(lang: String, head: String, body: String) -> String
|
|
extern fn el_meta_charset(charset: String) -> String
|
|
extern fn el_meta(attrs: String) -> String
|
|
extern fn el_title(text: String) -> String
|
|
extern fn el_link_stylesheet(href: String) -> String
|
|
extern fn el_script_src(src: String, defer_load: Bool) -> String
|
|
extern fn el_script_inline(code: String) -> String
|
|
extern fn el_nav(attrs: String, children: String) -> String
|
|
extern fn el_div(attrs: String, children: String) -> String
|
|
extern fn el_a(href: String, attrs: String, children: String) -> String
|
|
extern fn el_img(attrs: String) -> String
|
|
extern fn el_p(attrs: String, children: String) -> String
|
|
extern fn el_h1(attrs: String, text: String) -> String
|
|
extern fn el_button(attrs: String, label: String) -> String
|
|
extern fn el_span(attrs: String, children: String) -> String
|
|
extern fn el_input(type_attr: String, attrs: String) -> String
|
|
extern fn el_label(for_id: String, attrs: String, children: String) -> String
|
|
extern fn el_ul(attrs: String, children: String) -> String
|
|
extern fn el_li(attrs: String, children: String) -> String
|
|
|
|
fn account_css() -> String {
|
|
let css: String = "*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
:root {
|
|
--bg: #FAFAF8;
|
|
--bg2: #F0F0EC;
|
|
--card: #FFFFFF;
|
|
--navy: #0052A0;
|
|
--navy-d: rgba(0,82,160,.06);
|
|
--navy-m: rgba(0,82,160,.12);
|
|
--navy-b: rgba(0,82,160,.20);
|
|
--navy-65: rgba(0,82,160,.65);
|
|
--navy-85: rgba(0,82,160,.85);
|
|
--t1: #0D0D14;
|
|
--t2: #3A3A4A;
|
|
--t3: #6B6B7E;
|
|
--t4: rgba(13,13,20,.35);
|
|
--border: rgba(0,0,0,.07);
|
|
--border2: rgba(0,0,0,.13);
|
|
--head: 'Playfair Display', Georgia, serif;
|
|
--body: 'IBM Plex Sans', system-ui, sans-serif;
|
|
}
|
|
html { scroll-behavior: smooth; }
|
|
html, body {
|
|
background: var(--bg);
|
|
color: var(--t1);
|
|
font-family: var(--body);
|
|
font-weight: 400;
|
|
-webkit-font-smoothing: antialiased;
|
|
min-height: 100vh;
|
|
}
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
background-image:
|
|
linear-gradient(rgba(0,0,0,.022) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(0,0,0,.022) 1px, transparent 1px);
|
|
background-size: 48px 48px;
|
|
}
|
|
#nav {
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0;
|
|
z-index: 100;
|
|
background: rgba(250,250,248,.95);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.nav-inner {
|
|
max-width: 1280px;
|
|
margin: 0 auto;
|
|
padding: 0 2rem;
|
|
height: 4rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
.nav-logo img { height: 2rem; width: auto; display: block; }
|
|
.nav-back {
|
|
font-family: var(--body);
|
|
font-size: 0.675rem;
|
|
font-weight: 400;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: var(--t3);
|
|
text-decoration: none;
|
|
transition: color 200ms;
|
|
}
|
|
.nav-back:hover { color: var(--t1); }
|
|
.page-shell {
|
|
position: relative;
|
|
z-index: 1;
|
|
min-height: 100vh;
|
|
padding: 6rem 2rem 4rem;
|
|
}
|
|
.page-inner {
|
|
max-width: 680px;
|
|
margin: 0 auto;
|
|
}
|
|
.page-heading { margin-bottom: 2.5rem; }
|
|
.page-eyebrow {
|
|
font-family: var(--body);
|
|
font-size: 0.7rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.22em;
|
|
text-transform: uppercase;
|
|
color: var(--navy-65);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
.page-title {
|
|
font-family: 'Playfair Display', Georgia, serif;
|
|
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
|
font-weight: 600;
|
|
line-height: 1.1;
|
|
letter-spacing: -0.02em;
|
|
color: var(--t1);
|
|
}
|
|
.account-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.25rem;
|
|
}
|
|
.card-dark {
|
|
background: var(--card);
|
|
border: 1px solid rgba(0,0,0,.07);
|
|
box-shadow: 0 2px 12px rgba(13,13,20,.04);
|
|
padding: 2rem;
|
|
}
|
|
.card-label {
|
|
font-family: var(--body);
|
|
font-size: 0.65rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.22em;
|
|
text-transform: uppercase;
|
|
color: var(--navy-65);
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
.plan-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 1.5rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
.plan-name {
|
|
font-family: 'Playfair Display', Georgia, serif;
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
color: var(--t1);
|
|
margin-bottom: 0.375rem;
|
|
}
|
|
.plan-status {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.4rem;
|
|
font-size: 0.65rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: var(--navy);
|
|
background: rgba(0,82,160,.07);
|
|
border: 1px solid rgba(0,82,160,.18);
|
|
padding: 0.3rem 0.75rem;
|
|
}
|
|
.plan-status-dot {
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background: var(--navy);
|
|
flex-shrink: 0;
|
|
}
|
|
.plan-meta {
|
|
margin-top: 1.25rem;
|
|
display: flex;
|
|
gap: 2rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
.plan-meta-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
}
|
|
.plan-meta-label {
|
|
font-size: 0.6rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.18em;
|
|
text-transform: uppercase;
|
|
color: var(--t3);
|
|
}
|
|
.plan-meta-value {
|
|
font-size: 0.875rem;
|
|
font-weight: 400;
|
|
color: var(--t1);
|
|
}
|
|
.timeline {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0;
|
|
}
|
|
.timeline-item {
|
|
display: flex;
|
|
gap: 1.25rem;
|
|
padding-bottom: 1.5rem;
|
|
position: relative;
|
|
}
|
|
.timeline-item:last-child { padding-bottom: 0; }
|
|
.timeline-left {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
width: 1.5rem;
|
|
}
|
|
.timeline-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
border: 2px solid var(--navy);
|
|
background: var(--bg);
|
|
flex-shrink: 0;
|
|
margin-top: 3px;
|
|
}
|
|
.timeline-dot.filled { background: var(--navy); }
|
|
.timeline-line {
|
|
flex: 1;
|
|
width: 1px;
|
|
background: rgba(0,82,160,.15);
|
|
margin-top: 4px;
|
|
}
|
|
.timeline-content {
|
|
flex: 1;
|
|
padding-bottom: 0.25rem;
|
|
}
|
|
.timeline-date {
|
|
font-size: 0.65rem;
|
|
font-weight: 600;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: var(--navy-65);
|
|
margin-bottom: 0.375rem;
|
|
}
|
|
.timeline-text {
|
|
font-family: var(--body);
|
|
font-weight: 400;
|
|
font-size: 0.9rem;
|
|
color: var(--t1);
|
|
line-height: 1.6;
|
|
}
|
|
.timeline-sub {
|
|
font-family: var(--body);
|
|
font-weight: 300;
|
|
font-size: 0.8125rem;
|
|
color: var(--t2);
|
|
line-height: 1.6;
|
|
margin-top: 0.25rem;
|
|
}
|
|
.devices-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.device-icon {
|
|
width: 2.5rem;
|
|
height: 2.5rem;
|
|
border: 1px solid rgba(0,82,160,.18);
|
|
background: rgba(0,82,160,.04);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
.devices-count {
|
|
font-family: 'Playfair Display', Georgia, serif;
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
color: var(--t1);
|
|
}
|
|
.devices-sub { font-size: 0.8125rem; color: var(--t2); }
|
|
.devices-note {
|
|
font-family: var(--body);
|
|
font-weight: 300;
|
|
font-size: 0.8125rem;
|
|
color: var(--t3);
|
|
line-height: 1.65;
|
|
border-top: 1px solid var(--border);
|
|
padding-top: 1rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
.badge-wrap {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 2rem;
|
|
}
|
|
.badge-caption {
|
|
margin-top: 1.25rem;
|
|
font-size: 0.75rem;
|
|
font-weight: 400;
|
|
letter-spacing: 0.04em;
|
|
color: var(--t3);
|
|
text-align: center;
|
|
}
|
|
.signin-wrap {
|
|
min-height: 60vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
padding: 3rem 1rem;
|
|
}
|
|
.signin-icon {
|
|
width: 4rem;
|
|
height: 4rem;
|
|
border: 1px solid rgba(0,82,160,.20);
|
|
background: rgba(0,82,160,.04);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto 1.5rem;
|
|
}
|
|
.signin-title {
|
|
font-family: 'Playfair Display', Georgia, serif;
|
|
font-size: clamp(1.5rem, 3vw, 2rem);
|
|
font-weight: 600;
|
|
color: var(--t1);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
.signin-sub {
|
|
font-family: var(--body);
|
|
font-weight: 300;
|
|
font-size: 0.9375rem;
|
|
color: var(--t2);
|
|
max-width: 24rem;
|
|
margin: 0 auto 2rem;
|
|
line-height: 1.65;
|
|
}
|
|
.signin-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.75rem;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
font-family: var(--body);
|
|
font-size: 0.8125rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.04em;
|
|
color: var(--t1);
|
|
background: var(--card);
|
|
border: 1px solid var(--border2);
|
|
padding: 0.875rem 1.5rem;
|
|
cursor: pointer;
|
|
transition: border-color 200ms, background 200ms, box-shadow 200ms;
|
|
text-decoration: none;
|
|
}
|
|
.signin-btn:hover {
|
|
border-color: rgba(0,82,160,.35);
|
|
background: rgba(0,82,160,.03);
|
|
box-shadow: 0 2px 8px rgba(0,82,160,.08);
|
|
}
|
|
.btn-primary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
font-family: var(--body);
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
text-decoration: none;
|
|
color: #fff;
|
|
background: var(--navy);
|
|
padding: 1rem 2rem;
|
|
border: none;
|
|
cursor: pointer;
|
|
transition: background 300ms, box-shadow 300ms;
|
|
box-shadow: 0 2px 16px rgba(0,82,160,.20);
|
|
}
|
|
.btn-primary:hover { background: #0078D4; }
|
|
.btn-ghost {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
font-family: var(--body);
|
|
font-size: 0.75rem;
|
|
font-weight: 400;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
text-decoration: none;
|
|
color: var(--t2);
|
|
background: transparent;
|
|
padding: 0.875rem 1.75rem;
|
|
border: 1px solid var(--border2);
|
|
cursor: pointer;
|
|
transition: border-color 300ms, color 300ms;
|
|
}
|
|
.btn-ghost:hover { border-color: rgba(0,82,160,.35); color: var(--t1); }
|
|
.action-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
.action-row-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
.user-chip { display: flex; align-items: center; gap: 0.625rem; }
|
|
.user-avatar {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
border-radius: 50%;
|
|
background: rgba(0,82,160,.12);
|
|
border: 1px solid rgba(0,82,160,.20);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
color: var(--navy);
|
|
overflow: hidden;
|
|
flex-shrink: 0;
|
|
}
|
|
.user-avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
.user-email { font-size: 0.8125rem; color: var(--t2); }
|
|
.skeleton {
|
|
background: linear-gradient(90deg, rgba(0,0,0,.05) 25%, rgba(0,0,0,.08) 50%, rgba(0,0,0,.05) 75%);
|
|
background-size: 200% 100%;
|
|
animation: skeleton-shimmer 1.4s infinite;
|
|
border-radius: 3px;
|
|
}
|
|
@keyframes skeleton-shimmer {
|
|
0% { background-position: 200% 0; }
|
|
100% { background-position: -200% 0; }
|
|
}
|
|
.navy-line {
|
|
height: 1px;
|
|
background: linear-gradient(90deg, rgba(0,82,160,.30) 0%, rgba(0,82,160,.08) 100%);
|
|
}
|
|
.acct-section-header { margin-bottom: 1.25rem; }
|
|
.acct-input {
|
|
display: block;
|
|
width: 100%;
|
|
font-family: var(--body);
|
|
font-size: 0.875rem;
|
|
font-weight: 400;
|
|
color: var(--t1);
|
|
background: var(--bg);
|
|
border: 1px solid var(--border2);
|
|
padding: 0.75rem 1rem;
|
|
margin-bottom: 0.625rem;
|
|
outline: none;
|
|
transition: border-color 200ms;
|
|
}
|
|
.acct-input:focus { border-color: rgba(0,82,160,.45); }
|
|
.acct-input::placeholder { color: var(--t4); }
|
|
@media (max-width: 600px) {
|
|
.plan-row { flex-direction: column; }
|
|
.card-dark { padding: 1.5rem; }
|
|
.page-shell { padding: 5.5rem 1.25rem 3rem; }
|
|
}
|
|
::-webkit-scrollbar { width: 4px; }
|
|
::-webkit-scrollbar-track { background: transparent; }
|
|
::-webkit-scrollbar-thumb { background: var(--navy); border-radius: 2px; }
|
|
::-webkit-scrollbar-thumb:hover { background: #0078D4; }
|
|
.acct-email { font-family: var(--body); font-size: .875rem; color: var(--t2); font-weight: 300; }
|
|
.acct-header-row { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 2rem; }
|
|
.status-badge-preorder { display: inline-flex; align-items: center; gap: .375rem; font-size: .65rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: #92600A; background: rgba(146,96,10,.08); border: 1px solid rgba(146,96,10,.2); padding: .3rem .75rem; }
|
|
.download-btn-disabled { display: inline-flex; align-items: center; gap: .75rem; font-family: var(--body); font-size: .75rem; font-weight: 500; letter-spacing: .15em; text-transform: uppercase; color: var(--t3); background: var(--bg2); padding: 1rem 2rem; border: 1px solid var(--border2); cursor: not-allowed; }
|
|
.plan-billing-note { font-family: var(--body); font-size: .8125rem; font-weight: 300; color: var(--t2); margin-top: 1rem; line-height: 1.6; }
|
|
.plan-billing-link { color: var(--navy-65); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; background: none; border: none; font-family: var(--body); font-size: .8125rem; font-weight: 300; padding: 0; }
|
|
.plan-billing-link:hover { color: var(--navy); }
|
|
.download-status { font-size: .65rem; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--t3); margin-bottom: .75rem; }
|
|
.download-title { font-family: 'Playfair Display', Georgia, serif; font-size: 1.25rem; font-weight: 600; color: var(--t1); margin-bottom: .5rem; }
|
|
.download-body { font-family: var(--body); font-weight: 300; font-size: .875rem; color: var(--t2); line-height: 1.7; margin-bottom: 1.5rem; }
|
|
.roadmap-list { display: flex; flex-direction: column; gap: 0; }
|
|
.roadmap-phase { padding: 1.25rem 0; border-bottom: 1px solid var(--border); }
|
|
.roadmap-phase-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: .875rem; }
|
|
.roadmap-phase-label { font-family: var(--body); font-size: .7rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: var(--navy); }
|
|
.roadmap-phase-date { font-family: var(--body); font-size: .7rem; font-weight: 400; color: var(--t3); letter-spacing: .06em; }
|
|
.roadmap-items { list-style: none; display: flex; flex-direction: column; gap: .5rem; }
|
|
.roadmap-items li { font-family: var(--body); font-size: .875rem; font-weight: 300; color: var(--t2); line-height: 1.6; padding-left: 1rem; position: relative; }
|
|
.roadmap-items li::before { content: \"-\"; position: absolute; left: 0; color: var(--navy-65); }
|
|
.signout-section { padding-top: 1rem; display: flex; justify-content: flex-end; }
|
|
.api-key-list { display: flex; flex-direction: column; gap: 1.5rem; }
|
|
.api-key-entry { border-bottom: 1px solid var(--border); padding-bottom: 1.25rem; }
|
|
.api-key-entry:last-child { border-bottom: none; padding-bottom: 0; }
|
|
.api-key-header { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: .625rem; }
|
|
.api-key-name { font-size: .875rem; font-weight: 500; color: var(--t1); }
|
|
.api-key-masked { font-size: .75rem; font-family: monospace; color: var(--t3); }
|
|
.api-key-row { display: flex; align-items: center; gap: .75rem; }
|
|
.api-key-actions { display: flex; gap: .5rem; flex-shrink: 0; }
|
|
.api-key-help { margin-top: .625rem; }
|
|
.api-key-help summary { font-size: .75rem; font-weight: 500; letter-spacing: .03em; color: var(--navy-65); cursor: pointer; list-style: none; padding: .25rem 0; user-select: none; }
|
|
.api-key-help summary::-webkit-details-marker { display: none; }
|
|
.api-key-help summary::before { content: \"\\25B8 \"; font-size: .6rem; }
|
|
.api-key-help[open] summary::before { content: \"\\25BE \"; }
|
|
.api-key-help-body { padding: .75rem 0 .125rem; }
|
|
.api-key-help-body ol { padding-left: 1.25rem; display: flex; flex-direction: column; gap: .375rem; }
|
|
.api-key-help-body li { font-size: .8125rem; font-weight: 300; color: var(--t2); line-height: 1.55; }
|
|
.api-key-help-body a { color: var(--navy-65); text-decoration: underline; text-underline-offset: 2px; }
|
|
.api-key-help-body a:hover { color: var(--navy); }
|
|
.api-key-note { font-size: .75rem; font-weight: 300; color: var(--t3); margin-top: .625rem; line-height: 1.55; padding: .5rem .75rem; background: var(--bg2); border-left: 2px solid var(--navy-b); }
|
|
.api-key-note a { color: var(--navy-65); text-decoration: underline; text-underline-offset: 2px; }
|
|
.api-key-model-note { font-size: .8125rem; font-weight: 300; color: var(--t2); line-height: 1.65; padding: .75rem 1rem; background: var(--navy-d); border-left: 2px solid var(--navy); margin-bottom: 1.5rem; }
|
|
.api-key-model-note strong { font-weight: 600; color: var(--t1); }"
|
|
"<style>" + css + "</style>"
|
|
}
|
|
|
|
fn account_nav() -> String {
|
|
let logo_img: String = el_img("src=\"/assets/brand/neuron-wordmark-on-light.png\" srcset=\"/assets/brand/neuron-wordmark-on-light@2x.png 2x\" alt=\"Neuron\" height=\"28\"")
|
|
el_nav(
|
|
"id=\"nav\"",
|
|
el_div(
|
|
"class=\"nav-inner\"",
|
|
el_a("/", "class=\"nav-logo\" aria-label=\"Neuron home\"", logo_img) +
|
|
el_a("/", "class=\"nav-back\"", "← Home")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_signin_svg_user() -> String {
|
|
"<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"rgba(0,82,160,.70)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">" +
|
|
"<path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/>" +
|
|
"<circle cx=\"12\" cy=\"7\" r=\"4\"/>" +
|
|
"</svg>"
|
|
}
|
|
|
|
fn account_signin_svg_google() -> String {
|
|
"<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>"
|
|
}
|
|
|
|
fn account_signin_svg_github() -> String {
|
|
"<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">" +
|
|
"<path d=\"M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z\"/>" +
|
|
"</svg>"
|
|
}
|
|
|
|
fn account_signin_svg_download() -> String {
|
|
"<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">" +
|
|
"<path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>" +
|
|
"<polyline points=\"7 10 12 15 17 10\"/>" +
|
|
"<line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/>" +
|
|
"</svg>"
|
|
}
|
|
|
|
fn account_signin_svg_device() -> String {
|
|
"<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"rgba(0,82,160,.70)\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">" +
|
|
"<rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\"/>" +
|
|
"<line x1=\"8\" y1=\"21\" x2=\"16\" y2=\"21\"/>" +
|
|
"<line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"21\"/>" +
|
|
"</svg>"
|
|
}
|
|
|
|
fn account_signin_section() -> String {
|
|
let or_divider: String = el_div(
|
|
"style=\"display:flex;align-items:center;gap:1rem;margin:.125rem 0\"",
|
|
el_div("style=\"flex:1;height:1px;background:var(--border2)\"", "") +
|
|
el_span("style=\"font-family:var(--body);font-size:.75rem;color:var(--t3);letter-spacing:.06em\"", "or") +
|
|
el_div("style=\"flex:1;height:1px;background:var(--border2)\"", "")
|
|
)
|
|
let email_input: String = "<input type=\"email\" id=\"acct-email-input\" placeholder=\"Email address\" autocomplete=\"email\" style=\"font-family:var(--body);font-size:.875rem;font-weight:300;color:var(--t1);background:#fff;border:1px solid var(--border2);padding:.875rem 1rem;outline:none;transition:border-color .2s;width:100%;box-sizing:border-box\">"
|
|
let email_btn: String = "<button type=\"button\" id=\"acct-magic-btn\" onclick=\"sendMagicLink()\" style=\"font-family:var(--body);font-size:.75rem;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:#fff;background:var(--navy);border:none;padding:.875rem 1rem;cursor:pointer;transition:background .2s;width:100%;box-sizing:border-box\">Continue with email</button>"
|
|
let email_msg: String = el_p("id=\"acct-email-msg\" style=\"display:none;font-size:.8rem;text-align:center;margin-top:.25rem\"", "")
|
|
let options_wrap: String = el_div(
|
|
"style=\"width:100%;max-width:20rem;margin:0 auto;display:flex;flex-direction:column;gap:.75rem\"",
|
|
el_button("type=\"button\" class=\"signin-btn\" id=\"btn-google\" onclick=\"signInWith('google')\"", account_signin_svg_google() + " Continue with Google") +
|
|
el_button("type=\"button\" class=\"signin-btn\" id=\"btn-github\" onclick=\"signInWith('github')\"", account_signin_svg_github() + " Continue with GitHub") +
|
|
or_divider +
|
|
email_input +
|
|
email_btn +
|
|
email_msg
|
|
)
|
|
el_div(
|
|
"id=\"signin-section\"",
|
|
el_div(
|
|
"class=\"signin-wrap\"",
|
|
el_div("class=\"signin-icon\"", account_signin_svg_user()) +
|
|
el_h1("class=\"signin-title\"", "Sign in to your account") +
|
|
el_p("class=\"signin-sub\"", "Enter your email to receive a sign-in link, or continue with a social account.") +
|
|
options_wrap
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_plan_card() -> String {
|
|
el_div(
|
|
"class=\"card-dark\" id=\"plan-card\"",
|
|
el_p("class=\"card-label\"", "Your Plan") +
|
|
el_div(
|
|
"class=\"plan-row\"",
|
|
el_div(
|
|
"",
|
|
el_p("class=\"plan-name\" id=\"plan-name-el\"", "Loading...") +
|
|
el_div("id=\"plan-status-el\"", "") +
|
|
el_div("id=\"plan-billing-note-el\"", "")
|
|
)
|
|
) +
|
|
el_div("class=\"plan-meta\" id=\"plan-meta-el\"", "")
|
|
)
|
|
}
|
|
|
|
fn account_roadmap_phase1() -> String {
|
|
el_div(
|
|
"class=\"roadmap-phase\"",
|
|
el_div(
|
|
"class=\"roadmap-phase-header\"",
|
|
el_span("class=\"roadmap-phase-label\"", "Now shipping") +
|
|
el_span("class=\"roadmap-phase-date\"", "Q3 2026")
|
|
) +
|
|
el_ul(
|
|
"class=\"roadmap-items\"",
|
|
el_li("", "macOS and Windows clients") +
|
|
el_li("", "Persistent memory — local, encrypted") +
|
|
el_li("", "Bring your own API keys (OpenAI, Anthropic, Grok)") +
|
|
el_li("", "Neuron Inference — priced below the major APIs") +
|
|
el_li("", "Gmail, Slack, Google Calendar connectors") +
|
|
el_li("", "VS Code extension") +
|
|
el_li("", "Founding member badge in-app")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_roadmap_phase2() -> String {
|
|
el_div(
|
|
"class=\"roadmap-phase\"",
|
|
el_div(
|
|
"class=\"roadmap-phase-header\"",
|
|
el_span("class=\"roadmap-phase-label\"", "Following launch") +
|
|
el_span("class=\"roadmap-phase-date\"", "Q4 2026")
|
|
) +
|
|
el_ul(
|
|
"class=\"roadmap-items\"",
|
|
el_li("", "Process and knowledge packets") +
|
|
el_li("", "Local inference via Ollama") +
|
|
el_li("", "Additional connectors — GitHub, Notion, Linear, more") +
|
|
el_li("", "Mobile companion app") +
|
|
el_li("", "Family accounts")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_roadmap_phase3() -> String {
|
|
el_div(
|
|
"class=\"roadmap-phase\" style=\"border-bottom:none\"",
|
|
el_div(
|
|
"class=\"roadmap-phase-header\"",
|
|
el_span("class=\"roadmap-phase-label\"", "2027") +
|
|
el_span("class=\"roadmap-phase-date\"", "H1 2027")
|
|
) +
|
|
el_ul(
|
|
"class=\"roadmap-items\"",
|
|
el_li("", "Imprints — starting with C-suite (CEO, CTO, CFO, CMO, COO)") +
|
|
el_li("", "Enterprise accounts") +
|
|
el_li("", "Plugin marketplace (open to developers)") +
|
|
el_li("", "API access for power users")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_roadmap_section() -> String {
|
|
el_div(
|
|
"id=\"roadmap-section\" style=\"display:none\"",
|
|
el_div(
|
|
"class=\"card-dark\"",
|
|
el_p("class=\"card-label\"", "Product Roadmap") +
|
|
el_p("style=\"font-family:var(--body);font-weight:300;font-size:.875rem;color:var(--t2);line-height:1.7;margin-bottom:1.5rem\"",
|
|
"As a Founding Member, you have visibility into what we're building. This is the real roadmap — not marketing."
|
|
) +
|
|
el_div("class=\"roadmap-list\"", account_roadmap_phase1() + account_roadmap_phase2() + account_roadmap_phase3()) +
|
|
el_p("style=\"font-family:var(--body);font-weight:300;font-size:.75rem;color:var(--t3);margin-top:1.5rem;line-height:1.7\"",
|
|
"This roadmap is directional, not a commitment. Founding Members shape what gets built and when through their feedback and votes. Your input has real weight."
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_family_section() -> String {
|
|
let child_attest: String = "<label style=\"display:flex;gap:.5rem;align-items:flex-start;cursor:pointer;margin:.75rem 0\">" +
|
|
"<input type=\"checkbox\" id=\"family-attest\" style=\"margin-top:.2rem;accent-color:var(--navy)\">" +
|
|
"<span style=\"font-size:.8rem;color:var(--t2)\">I confirm this person is a minor under my care and is using this account under my supervision.</span>" +
|
|
"</label>"
|
|
el_div(
|
|
"id=\"family-section\" style=\"display:none\"",
|
|
el_div(
|
|
"class=\"card-dark\"",
|
|
el_div(
|
|
"class=\"acct-section-header\"",
|
|
el_p("class=\"card-label\"", "Family members") +
|
|
el_p("style=\"font-size:.8125rem;font-weight:300;color:var(--t2);line-height:1.65\"", "Add up to 5 children to your Founding Member plan. $10/month per child.")
|
|
) +
|
|
el_div("id=\"family-list\"", "") +
|
|
el_div(
|
|
"id=\"add-child-form\"",
|
|
el_input("email", "id=\"child-email\" placeholder=\"Child's email\" class=\"acct-input\"") +
|
|
el_input("number", "id=\"child-dob-year\" placeholder=\"Birth year (e.g. 2012)\" min=\"1990\" max=\"2007\" class=\"acct-input\"") +
|
|
child_attest +
|
|
el_button("class=\"btn-primary\" onclick=\"addFamilyMember()\" style=\"padding:.75rem 1.5rem\"", "Add family member") +
|
|
el_p("id=\"family-msg\" style=\"display:none;font-size:.8rem;margin-top:.5rem\"", "")
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_badge_section() -> String {
|
|
el_div(
|
|
"id=\"badge-section\" style=\"display:none\"",
|
|
el_div(
|
|
"class=\"card-dark\" style=\"padding:2.5rem 2rem;text-align:center;border-top:3px solid var(--navy);background:linear-gradient(135deg,rgba(0,82,160,.03) 0%,rgba(0,82,160,.06) 100%)\"",
|
|
el_p("class=\"card-label\" style=\"margin-bottom:1.5rem;letter-spacing:.25em\"", "Your Founding Member Badge") +
|
|
el_div("id=\"badge-html-container\" style=\"display:flex;justify-content:center;margin-bottom:1.5rem\"", "") +
|
|
el_p("style=\"font-family:var(--body);font-weight:300;font-size:.8125rem;color:var(--t3);line-height:1.7;max-width:24rem;margin:0 auto\"",
|
|
"This badge will appear in the Neuron app at launch. Your number is permanent."
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_timeline_item1() -> String {
|
|
el_div(
|
|
"class=\"timeline-item\"",
|
|
el_div(
|
|
"class=\"timeline-left\"",
|
|
el_div("class=\"timeline-dot filled\"", "") +
|
|
el_div("class=\"timeline-line\"", "")
|
|
) +
|
|
el_div(
|
|
"class=\"timeline-content\"",
|
|
el_p("class=\"timeline-date\"", "Now") +
|
|
el_p("class=\"timeline-text\"", "Your preorder is confirmed.") +
|
|
el_p("class=\"timeline-sub\"", "A confirmation email has been sent to your address. Thank you for being here early.")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_timeline_item2() -> String {
|
|
el_div(
|
|
"class=\"timeline-item\"",
|
|
el_div(
|
|
"class=\"timeline-left\"",
|
|
el_div("class=\"timeline-dot filled\" style=\"background:rgba(0,82,160,.4);border-color:rgba(0,82,160,.4)\"", "") +
|
|
el_div("class=\"timeline-line\"", "")
|
|
) +
|
|
el_div(
|
|
"class=\"timeline-content\"",
|
|
el_p("class=\"timeline-date\" style=\"color:var(--navy);font-weight:600\"", "Within 30 days") +
|
|
el_p("class=\"timeline-text\"", "Neuron for Mac & Windows ships.") +
|
|
el_p("class=\"timeline-sub\"", "You'll receive an email with your download link and license key. macOS and Windows simultaneously.")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_timeline_item3() -> String {
|
|
el_div(
|
|
"class=\"timeline-item\"",
|
|
el_div(
|
|
"class=\"timeline-left\"",
|
|
el_div("class=\"timeline-dot\"", "")
|
|
) +
|
|
el_div(
|
|
"class=\"timeline-content\"",
|
|
el_p("class=\"timeline-date\"", "Q3 2026") +
|
|
el_p("class=\"timeline-text\"", "Neuron Inference launches.") +
|
|
el_p("class=\"timeline-sub\"", "Our own inference layer, priced below the major APIs. No setup required - it activates automatically for your plan.")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_timeline_card() -> String {
|
|
el_div(
|
|
"class=\"card-dark\" id=\"timeline-card\"",
|
|
el_p("class=\"card-label\"", "What happens next") +
|
|
el_div(
|
|
"class=\"timeline\"",
|
|
account_timeline_item1() +
|
|
account_timeline_item2() +
|
|
account_timeline_item3()
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_download_card() -> String {
|
|
el_div(
|
|
"class=\"card-dark\" style=\"border-top:3px solid var(--navy)\"",
|
|
el_p("class=\"card-label\"", "Download") +
|
|
el_p("class=\"download-status\" style=\"color:var(--navy)\"", "Shipping within 30 days") +
|
|
el_p("class=\"download-title\"", "Neuron for Mac & Windows") +
|
|
el_p("class=\"download-body\"", "macOS and Windows simultaneously. You'll receive a download link and license key by email the moment it ships. Nothing to do right now.") +
|
|
el_button("type=\"button\" class=\"download-btn-disabled\" disabled aria-disabled=\"true\"",
|
|
account_signin_svg_download() + " Download arriving soon"
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_devices_card() -> String {
|
|
el_div(
|
|
"class=\"card-dark\"",
|
|
el_p("class=\"card-label\"", "Devices") +
|
|
el_div(
|
|
"class=\"devices-row\"",
|
|
el_div("class=\"device-icon\"", account_signin_svg_device()) +
|
|
el_div(
|
|
"",
|
|
el_p("class=\"devices-count\"", "2 devices included with your plan") +
|
|
el_p("class=\"devices-sub\"", "Currently: Setup at launch")
|
|
)
|
|
) +
|
|
el_p("class=\"devices-note\"", "Device activation happens when you install. Your license key will be in your launch email.")
|
|
)
|
|
}
|
|
|
|
fn api_key_provider_row(provider_id: String, provider_name: String, placeholder: String, instructions: String) -> String {
|
|
el_div(
|
|
"class=\"api-key-entry\"",
|
|
el_div(
|
|
"class=\"api-key-header\"",
|
|
el_span("class=\"api-key-name\"", provider_name) +
|
|
el_span("class=\"api-key-masked\" id=\"apikey-masked-" + provider_id + "\"", "Not configured")
|
|
) +
|
|
el_div(
|
|
"class=\"api-key-row\"",
|
|
"<input type=\"password\" id=\"apikey-input-" + provider_id + "\" class=\"acct-input\" placeholder=\"" + placeholder + "\" autocomplete=\"off\" style=\"margin-bottom:0;flex:1\">" +
|
|
el_div(
|
|
"class=\"api-key-actions\"",
|
|
el_button("type=\"button\" class=\"btn-primary\" style=\"padding:.5rem 1rem;font-size:.75rem\" onclick=\"saveApiKey('" + provider_id + "')\"", "Save") +
|
|
el_button("type=\"button\" class=\"btn-ghost\" style=\"padding:.5rem 1rem;font-size:.75rem;display:none\" id=\"apikey-del-" + provider_id + "\" onclick=\"deleteApiKey('" + provider_id + "')\"", "Remove")
|
|
)
|
|
) +
|
|
instructions
|
|
)
|
|
}
|
|
|
|
fn account_api_keys_section() -> String {
|
|
let openai_help: String =
|
|
"<details class=\"api-key-help\">" +
|
|
"<summary>How to get an OpenAI key</summary>" +
|
|
"<div class=\"api-key-help-body\"><ol>" +
|
|
"<li>Go to <a href=\"https://platform.openai.com/api-keys\" target=\"_blank\" rel=\"noopener\">platform.openai.com/api-keys ↗</a></li>" +
|
|
"<li>Click <strong>Create new secret key</strong></li>" +
|
|
"<li>Give it a name (e.g. “Neuron”), then click <strong>Create secret key</strong></li>" +
|
|
"<li>Copy the key immediately — it is only shown once</li>" +
|
|
"</ol>" +
|
|
"<p class=\"api-key-note\">You need billing set up before making API calls. Add a payment method at <a href=\"https://platform.openai.com/settings/organization/billing\" target=\"_blank\" rel=\"noopener\">platform.openai.com ↗</a></p>" +
|
|
"</div></details>"
|
|
|
|
let anthropic_help: String =
|
|
"<details class=\"api-key-help\">" +
|
|
"<summary>How to get an Anthropic key</summary>" +
|
|
"<div class=\"api-key-help-body\"><ol>" +
|
|
"<li>Go to <a href=\"https://console.anthropic.com/settings/keys\" target=\"_blank\" rel=\"noopener\">console.anthropic.com/settings/keys ↗</a></li>" +
|
|
"<li>Click <strong>Create Key</strong></li>" +
|
|
"<li>Give it a name (e.g. “Neuron”), then click <strong>Create Key</strong></li>" +
|
|
"<li>Copy the key immediately — it is only shown once</li>" +
|
|
"</ol>" +
|
|
"<p class=\"api-key-note\">You need to add credits before making API calls. Go to <a href=\"https://console.anthropic.com/settings/plans\" target=\"_blank\" rel=\"noopener\">console.anthropic.com → Plans & Billing ↗</a> to add credits.</p>" +
|
|
"</div></details>"
|
|
|
|
let gemini_help: String =
|
|
"<details class=\"api-key-help\">" +
|
|
"<summary>How to get a Gemini key</summary>" +
|
|
"<div class=\"api-key-help-body\"><ol>" +
|
|
"<li>Go to <a href=\"https://aistudio.google.com/apikey\" target=\"_blank\" rel=\"noopener\">aistudio.google.com/apikey ↗</a></li>" +
|
|
"<li>Sign in with your Google account if prompted</li>" +
|
|
"<li>Click <strong>Create API key</strong></li>" +
|
|
"<li>Select an existing Google Cloud project or create a new one</li>" +
|
|
"<li>Copy the key</li>" +
|
|
"</ol>" +
|
|
"<p class=\"api-key-note\">The free tier (Gemini 1.5 Flash) has generous rate limits. Paid usage is billed through your Google Cloud account.</p>" +
|
|
"</div></details>"
|
|
|
|
let grok_help: String =
|
|
"<details class=\"api-key-help\">" +
|
|
"<summary>How to get a Grok key</summary>" +
|
|
"<div class=\"api-key-help-body\"><ol>" +
|
|
"<li>Go to <a href=\"https://console.x.ai/\" target=\"_blank\" rel=\"noopener\">console.x.ai ↗</a></li>" +
|
|
"<li>Sign in with your X (Twitter) account</li>" +
|
|
"<li>In the left sidebar, click <strong>API Keys</strong></li>" +
|
|
"<li>Click <strong>Create API Key</strong>, give it a name</li>" +
|
|
"<li>Copy the key immediately — it is only shown once</li>" +
|
|
"</ol>" +
|
|
"<p class=\"api-key-note\">xAI offers free monthly credits to new accounts. Usage beyond the free tier is billed per token.</p>" +
|
|
"</div></details>"
|
|
|
|
let providers: String = el_div(
|
|
"class=\"api-key-list\"",
|
|
api_key_provider_row("openai", "OpenAI", "sk-...", openai_help) +
|
|
api_key_provider_row("anthropic", "Anthropic", "sk-ant-...", anthropic_help) +
|
|
api_key_provider_row("gemini", "Gemini", "AIza...", gemini_help) +
|
|
api_key_provider_row("grok", "Grok", "xai-...", grok_help)
|
|
)
|
|
el_div(
|
|
"id=\"api-keys-section\" style=\"display:none\"",
|
|
el_div(
|
|
"class=\"card-dark\"",
|
|
el_div(
|
|
"class=\"acct-section-header\"",
|
|
el_p("class=\"card-label\"", "API Keys") +
|
|
el_p("style=\"font-size:.8125rem;font-weight:300;color:var(--t2);line-height:1.65\"",
|
|
"Add your own AI provider keys. Neuron uses them directly — your keys, your models, your data."
|
|
)
|
|
) +
|
|
el_div(
|
|
"class=\"api-key-model-note\"",
|
|
"<strong>For best performance, use a reasoning model.</strong> o4-mini or o3 (OpenAI) · Claude Sonnet 4 (Anthropic) · Gemini 2.5 Pro (Google) · Grok-3 (xAI). You choose the model in the app — any model works, reasoning models are where Neuron shines. <a href=\"#\" style=\"color:var(--navy-65);text-decoration:underline;text-underline-offset:2px\">Neuron Inference</a> — our own model layer, priced below the major APIs — launches Q3 2026 and becomes the default."
|
|
) +
|
|
providers +
|
|
el_p("id=\"api-keys-msg\" style=\"display:none;font-size:.8rem;margin-top:.75rem\"", "")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_dashboard_section() -> String {
|
|
let header_row: String = el_div(
|
|
"class=\"acct-header-row\"",
|
|
el_div(
|
|
"",
|
|
el_p("class=\"page-eyebrow\" style=\"margin-bottom:.375rem\"", "Account") +
|
|
el_h1("class=\"page-title\" style=\"margin-bottom:.5rem\"", "Your Account") +
|
|
el_p("class=\"acct-email\" id=\"acct-header-email\"", "")
|
|
) +
|
|
el_div(
|
|
"style=\"padding-top:.25rem\"",
|
|
el_button("type=\"button\" class=\"btn-ghost\" id=\"signout-btn-top\" onclick=\"signOut()\"", "Sign out")
|
|
)
|
|
)
|
|
let signout_row: String = el_div(
|
|
"class=\"signout-section\"",
|
|
el_button("type=\"button\" class=\"btn-ghost\" id=\"signout-btn\" onclick=\"signOut()\"", "Sign out")
|
|
)
|
|
el_div(
|
|
"id=\"dashboard-section\" style=\"display:none\"",
|
|
header_row +
|
|
el_div(
|
|
"class=\"account-section\"",
|
|
account_plan_card() +
|
|
account_api_keys_section() +
|
|
account_roadmap_section() +
|
|
account_family_section() +
|
|
account_badge_section() +
|
|
account_timeline_card() +
|
|
account_download_card() +
|
|
account_devices_card() +
|
|
signout_row
|
|
)
|
|
)
|
|
}
|
|
|
|
fn account_page(supabase_url: String, supabase_anon_key: String) -> String {
|
|
let head: String =
|
|
el_meta_charset("UTF-8") +
|
|
el_meta("name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"") +
|
|
el_title("My Account - Neuron") +
|
|
el_meta("name=\"description\" content=\"Manage your Neuron account, view your plan, and access your founding member details.\"") +
|
|
"<link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/assets/favicon-16.png\">" +
|
|
"<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/assets/favicon-32.png\">" +
|
|
"<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">" +
|
|
"<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>" +
|
|
el_link_stylesheet("https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;1,400;1,500&family=IBM+Plex+Sans:wght@300;400;500;600&display=swap") +
|
|
account_css()
|
|
|
|
let body: String =
|
|
account_nav() +
|
|
el_div(
|
|
"class=\"page-shell\"",
|
|
el_div(
|
|
"class=\"page-inner\"",
|
|
account_signin_section() +
|
|
account_dashboard_section()
|
|
)
|
|
) +
|
|
el_script_src("https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js", false) +
|
|
el_script_inline("window.NEURON_CFG=window.NEURON_CFG||{};window.NEURON_CFG.supabase_url=\"" + supabase_url + "\";window.NEURON_CFG.supabase_anon_key=\"" + supabase_anon_key + "\";") +
|
|
el_script_src("/js/account-auth.js", true) +
|
|
el_script_src("/js/account-dashboard.js", true)
|
|
|
|
el_html_doc("lang=\"en\"", head, body)
|
|
}
|