// 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); }" "" } 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 { "" + "" + "" + "" } fn account_signin_svg_google() -> String { "" + "" + "" + "" + "" + "" } fn account_signin_svg_github() -> String { "" + "" + "" } fn account_signin_svg_download() -> String { "" + "" + "" + "" + "" } fn account_signin_svg_device() -> String { "" + "" + "" + "" + "" } 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 = "" let email_btn: String = "" 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 = "" 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\"", "" + 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 = "
" + "How to get an OpenAI key" + "
    " + "
  1. Go to platform.openai.com/api-keys ↗
  2. " + "
  3. Click Create new secret key
  4. " + "
  5. Give it a name (e.g. “Neuron”), then click Create secret key
  6. " + "
  7. Copy the key immediately — it is only shown once
  8. " + "
" + "

You need billing set up before making API calls. Add a payment method at platform.openai.com ↗

" + "
" let anthropic_help: String = "
" + "How to get an Anthropic key" + "
    " + "
  1. Go to console.anthropic.com/settings/keys ↗
  2. " + "
  3. Click Create Key
  4. " + "
  5. Give it a name (e.g. “Neuron”), then click Create Key
  6. " + "
  7. Copy the key immediately — it is only shown once
  8. " + "
" + "

You need to add credits before making API calls. Go to console.anthropic.com → Plans & Billing ↗ to add credits.

" + "
" let gemini_help: String = "
" + "How to get a Gemini key" + "
    " + "
  1. Go to aistudio.google.com/apikey ↗
  2. " + "
  3. Sign in with your Google account if prompted
  4. " + "
  5. Click Create API key
  6. " + "
  7. Select an existing Google Cloud project or create a new one
  8. " + "
  9. Copy the key
  10. " + "
" + "

The free tier (Gemini 1.5 Flash) has generous rate limits. Paid usage is billed through your Google Cloud account.

" + "
" let grok_help: String = "
" + "How to get a Grok key" + "
    " + "
  1. Go to console.x.ai ↗
  2. " + "
  3. Sign in with your X (Twitter) account
  4. " + "
  5. In the left sidebar, click API Keys
  6. " + "
  7. Click Create API Key, give it a name
  8. " + "
  9. Copy the key immediately — it is only shown once
  10. " + "
" + "

xAI offers free monthly credits to new accounts. Usage beyond the free tier is billed per token.

" + "
" 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\"", "For best performance, use a reasoning model. 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. Neuron Inference — 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.\"") + "" + "" + "" + "" + 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) }