From 00f2323c9815fb91f68c5b33446dfa82f5927fd0 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Fri, 1 May 2026 18:13:06 -0500 Subject: [PATCH] v1.0 - launch: full nav on gallery, chat widget auto-open, comparison logos, checkout fixes --- Dockerfile.stage | 83 +++ build-local.sh | 101 +++ runtime/el_runtime.c | 216 +++++-- src/about.el | 2 +- src/account.el | 1273 ++++++++++++++++++++++++++++++++++++++ src/checkout.el | 192 ++++-- src/comparison.el | 54 +- src/efficiency.el | 10 +- src/enterprise.el | 29 +- src/enterprise_terms.el | 22 +- src/environmental.el | 23 +- src/footer.el | 2 +- src/founding_sold.txt | 2 +- src/gallery.el | 267 ++++++++ src/how_it_works.el | 4 +- src/inference.el | 16 +- src/local_first.el | 2 +- src/main.el | 751 +++++++++++++++++++++- src/marketplace.el | 156 ++++- src/mission.el | 18 +- src/nav.el | 37 +- src/pillars.el | 2 +- src/pricing.el | 31 +- src/safety.el | 118 ++-- src/shares/77654328.json | 1 + src/shares/77654378.json | 1 + src/shares/77654502.html | 1 + src/shares/77654516.html | 2 + src/shares/77654579.html | 1 + src/shares/77654947.html | 19 + src/shares/77655050.html | 1 + src/shares/77655150.html | 104 ++++ src/shares/77656956.html | 139 +++++ src/shares/77657016.html | 139 +++++ src/shares/77657891.html | 174 ++++++ src/shares/77662996.html | 171 +++++ src/shares/77663798.html | 168 +++++ src/shares/77675038.html | 168 +++++ src/styles.el | 541 ++++++++++++++-- src/terms.el | 38 +- src/viral.el | 95 +-- 41 files changed, 4740 insertions(+), 434 deletions(-) create mode 100644 Dockerfile.stage create mode 100755 build-local.sh create mode 100644 src/account.el create mode 100644 src/gallery.el create mode 100644 src/shares/77654328.json create mode 100644 src/shares/77654378.json create mode 100644 src/shares/77654502.html create mode 100644 src/shares/77654516.html create mode 100644 src/shares/77654579.html create mode 100644 src/shares/77654947.html create mode 100644 src/shares/77655050.html create mode 100644 src/shares/77655150.html create mode 100644 src/shares/77656956.html create mode 100644 src/shares/77657016.html create mode 100644 src/shares/77657891.html create mode 100644 src/shares/77662996.html create mode 100644 src/shares/77663798.html create mode 100644 src/shares/77675038.html diff --git a/Dockerfile.stage b/Dockerfile.stage new file mode 100644 index 0000000..0340cc7 --- /dev/null +++ b/Dockerfile.stage @@ -0,0 +1,83 @@ +# Dockerfile.stage — Stage build: landing server + soul-demo in one image. +# +# Both processes run in the same container: +# - neuron-web on port 8080 (landing page server) +# - soul-demo on port 7772 (demo chat, localhost only) +# +# Both binaries are compiled from C inside Docker for linux/amd64. +# The engram snapshot is baked in so the soul has memory from first boot. + +# ── Stage 1: compile both binaries ──────────────────────────────────────────── +FROM debian:bookworm-slim AS builder + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + libcurl4-openssl-dev \ + libssl-dev \ + python3 \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +# El runtime (shared by both binaries) +COPY runtime/el_runtime.c runtime/el_runtime.h ./ + +# ── Build neuron-web ────────────────────────────────────────────────────────── +COPY dist/web_stubs.c ./ +COPY dist/bootstrap.py ./ +COPY dist/main-combined.el ./ + +RUN python3 bootstrap.py main-combined.el > main.c && \ + sed -i \ + 's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);\nel_val_t supabase_get(el_val_t project_url, el_val_t service_key, el_val_t table_and_query);|' \ + main.c && \ + cc -O2 -rdynamic \ + -o neuron-web \ + main.c web_stubs.c el_runtime.c \ + -lcurl -lpthread -ldl -lm + +# ── Build soul-demo ─────────────────────────────────────────────────────────── +COPY dist/soul-demo.c ./ +COPY dist/vessel_stubs.c ./ + +RUN cc -O2 -rdynamic \ + -o soul-demo \ + soul-demo.c vessel_stubs.c el_runtime.c \ + -lcurl -lpthread -ldl -lm + +# ── Stage 2: runtime image ──────────────────────────────────────────────────── +FROM debian:bookworm-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libcurl4 \ + libssl3 \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && groupadd -r landing && useradd -r -g landing landing \ + && mkdir -p /srv/landing/assets /srv/landing/shares \ + && mkdir -p /srv/soul/engram-demo \ + && chown -R landing:landing /srv/landing /srv/soul + +COPY --from=builder /build/neuron-web /usr/local/bin/neuron-web +COPY --from=builder /build/soul-demo /usr/local/bin/soul-demo + +# Engram snapshot — baked in so soul has memory from cold start +COPY dist/engram-snapshot.json /srv/soul/engram-demo/snapshot.json + +COPY src/assets /srv/landing/assets + +COPY dist/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +ENV LANDING_ROOT=/srv/landing +ENV PORT=8080 +ENV NEURON_HOME=/srv/soul/engram-demo +ENV NEURON_PORT=7772 + +USER landing +EXPOSE 8080 + +CMD ["/usr/local/bin/entrypoint.sh"] diff --git a/build-local.sh b/build-local.sh new file mode 100755 index 0000000..84eb8ee --- /dev/null +++ b/build-local.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# build-local.sh — Build and run neuron-web + soul-demo for local development. +# +# Mirrors the container's entrypoint.sh: both processes start together, +# soul-demo in background, neuron-web in foreground. Kill neuron-web (Ctrl-C) +# and the soul dies with it. No separate process manager. No port conflicts. +# +# Usage: +# ./build-local.sh — build only +# ./build-local.sh --run — build and start both servers + +set -euo pipefail +cd "$(dirname "$0")" + +NEURON_DIR="$(cd ../../neuron && pwd)" +EL_HOME="${EL_HOME:-../../foundation/el}" +BOOTSTRAP="${EL_HOME}/bootstrap.py" +RUNTIME_SRC="${EL_HOME}/el-compiler/runtime" + +COMPONENTS=(nav hero pillars how_it_works inference efficiency comparison + environmental enterprise mission local_first pricing marketplace viral + footer styles about founding_badge terms enterprise_terms checkout safety + gallery account) + +echo "==> Combining El sources" +{ + for f in "${COMPONENTS[@]}"; do + [ -f "src/${f}.el" ] && grep -hv '^[[:space:]]*from\|^[[:space:]]*import' "src/${f}.el" && echo "" + done + grep -v '^from\|^import' src/main.el +} > dist/main-combined.el +echo " $(wc -l < dist/main-combined.el) lines" + +echo "==> Bootstrap El → C" +python3 "${BOOTSTRAP}" dist/main-combined.el > dist/main.c + +echo "==> Injecting stubs" +sed -i '' 's|#include "el_runtime.h"|#include "el_runtime.h"\nel_val_t http_get_auth(el_val_t url, el_val_t tok);\nel_val_t http_post_auth(el_val_t url, el_val_t tok, el_val_t body);\nel_val_t cwd(void);\nel_val_t color_bold(el_val_t s);\nel_val_t unix_timestamp(void);\nel_val_t gcs_write(el_val_t bucket, el_val_t object_name, el_val_t content);\nel_val_t gcs_read(el_val_t bucket, el_val_t object_name);\nel_val_t supabase_insert(el_val_t project_url, el_val_t service_key, el_val_t table, el_val_t row_json);|' dist/main.c + +echo "==> Compiling neuron-web" +cc -O2 \ + -I"${RUNTIME_SRC}" \ + -o dist/neuron-web \ + dist/main.c \ + dist/web_stubs.c \ + "${RUNTIME_SRC}/el_runtime.c" \ + -lcurl -lpthread +echo " dist/neuron-web built ok" + +if [[ "${1:-}" == "--run" ]]; then + # Load credentials + if [ -f "$HOME/Secrets/credentials/infrastructure.env" ]; then + set -a; source "$HOME/Secrets/credentials/infrastructure.env"; set +a + fi + + # Get Soma key for Neuron inference + SOMA_KEY="${NEURON_LLM_0_KEY:-$(gcloud secrets versions access latest --secret=soma-operator-key --project=neuron-785695 2>/dev/null || echo '')}" + + # Kill any leftover instances from a previous run + pkill -f "dist/neuron-web" 2>/dev/null || true + pkill -f "neuron/dist/soul-demo" 2>/dev/null || true + sleep 0.5 + + echo "==> Starting soul-demo on :7772" + NEURON_LLM_0_URL="https://soma-prod-us-r4tfklscwq-uc.a.run.app/v1/chat/completions" \ + NEURON_LLM_0_KEY="$SOMA_KEY" \ + NEURON_LLM_0_FORMAT="openai" \ + NEURON_LLM_0_MODEL="neuron" \ + NEURON_LLM_1_KEY="${ANTHROPIC_API_KEY:-}" \ + NEURON_LLM_1_FORMAT="anthropic" \ + NEURON_HOME="$HOME/.neuron/engram-demo" \ + NEURON_PORT=7772 \ + "${NEURON_DIR}/dist/soul-demo" & + SOUL_PID=$! + + echo " soul-demo pid=$SOUL_PID — waiting for init..." + sleep 5 + + echo "==> Starting neuron-web on :3001" + # Trap exit to also kill the soul + trap "echo '==> Stopping...'; kill $SOUL_PID 2>/dev/null; exit 0" INT TERM + + NEURON_LLM_0_URL="https://soma-prod-us-r4tfklscwq-uc.a.run.app/v1/chat/completions" \ + NEURON_LLM_0_KEY="$SOMA_KEY" \ + NEURON_LLM_0_FORMAT="openai" \ + NEURON_LLM_0_MODEL="neuron" \ + NEURON_LLM_1_KEY="${ANTHROPIC_API_KEY:-}" \ + NEURON_LLM_1_FORMAT="anthropic" \ + STRIPE_SECRET_KEY="${STRIPE_SECRET_KEY:-}" \ + STRIPE_PUBLISHABLE_KEY="${STRIPE_PUBLISHABLE_KEY:-}" \ + STRIPE_PRICE_FOUNDING="${STRIPE_PRICE_FOUNDING:-}" \ + STRIPE_PRICE_PROFESSIONAL="${STRIPE_PRICE_PROFESSIONAL:-}" \ + STRIPE_WEBHOOK_SECRET="${STRIPE_WEBHOOK_SECRET:-}" \ + SUPABASE_SERVICE_KEY="${SUPABASE_SERVICE_KEY:-}" \ + SUPABASE_ANON_KEY="${SUPABASE_ANON_KEY:-}" \ + PORT=3001 LANDING_ROOT=./src \ + dist/neuron-web + + # neuron-web exited — clean up soul + kill $SOUL_PID 2>/dev/null || true +fi diff --git a/runtime/el_runtime.c b/runtime/el_runtime.c index 74ea701..3f9e040 100644 --- a/runtime/el_runtime.c +++ b/runtime/el_runtime.c @@ -1895,12 +1895,28 @@ el_val_t json_get(el_val_t jsonv, el_val_t keyv) { while (*p == ' ' || *p == '\t' || *p == '\n') p++; if (*p == '"') { p++; - const char* start = p; - while (*p && !(*p == '"' && *(p-1) != '\\')) p++; - size_t len = (size_t)(p - start); - char* out = el_strbuf(len); - memcpy(out, start, len); - out[len] = '\0'; + /* Unescape the JSON string value into a clean buffer. */ + size_t cap = strlen(p) + 1; + char* out = el_strbuf(cap); + char* w = out; + while (*p && *p != '"') { + if (*p == '\\' && *(p+1)) { + p++; + switch (*p) { + case '"': *w++ = '"'; break; + case '\\': *w++ = '\\'; break; + case '/': *w++ = '/'; break; + case 'n': *w++ = '\n'; break; + case 'r': *w++ = '\r'; break; + case 't': *w++ = '\t'; break; + default: *w++ = *p; break; + } + } else { + *w++ = *p; + } + p++; + } + *w = '\0'; return el_wrap_str(out); } const char* start = p; @@ -4534,30 +4550,143 @@ static const char* llm_resolve_model(const char* m) { return m; } -/* Make an Anthropic /v1/messages request with the given JSON body. Returns - * the assistant's first text content as an owned string, or a JSON error - * fragment on transport failure. */ -static el_val_t llm_request(const char* json_body) { - const char* api_key = getenv("ANTHROPIC_API_KEY"); - if (!api_key || !*api_key) { - return http_error_json("ANTHROPIC_API_KEY not set"); +/* + * ── Configurable LLM provider chain ────────────────────────────────────────── + * + * Providers are configured via indexed env vars. The runtime tries each in + * order (0, 1, 2, ...) and returns the first successful non-empty response. + * + * Per provider (N = 0, 1, 2, ...): + * NEURON_LLM_N_URL — endpoint URL (base URL; /v1/chat/completions appended + * if format is "openai" and not already in URL) + * NEURON_LLM_N_KEY — API key + * NEURON_LLM_N_FORMAT — "openai" (default) or "anthropic" + * NEURON_LLM_N_MODEL — model name override (optional) + * + * Example — Neuron inference primary, Anthropic fallback: + * NEURON_LLM_0_URL=https://soma.../v1/chat/completions + * NEURON_LLM_0_KEY=svc-key + * NEURON_LLM_0_FORMAT=openai + * NEURON_LLM_0_MODEL=neuron + * NEURON_LLM_1_URL=https://api.anthropic.com/v1/messages + * NEURON_LLM_1_KEY=sk-ant-... + * NEURON_LLM_1_FORMAT=anthropic + * + * If no NEURON_LLM_0_URL is set, falls back to legacy ANTHROPIC_API_KEY. + */ + +#define LLM_MAX_PROVIDERS 16 + +/* forward declarations */ +static el_val_t llm_extract_text(el_val_t resp_val); +static el_val_t llm_extract_text_openai(el_val_t resp_val); + +static el_val_t llm_extract_text_openai(el_val_t resp_val) { + const char* resp = EL_CSTR(resp_val); + if (!resp || !*resp) return el_wrap_str(el_strdup("")); + if (resp[0] == '{' && strstr(resp, "\"error\"")) return el_wrap_str(el_strdup("")); + const char* choices = json_find_key(resp, "choices"); + if (!choices || *choices != '[') return el_wrap_str(el_strdup("")); + choices++; + while (*choices == ' ' || *choices == '\t') choices++; + if (*choices != '{') return el_wrap_str(el_strdup("")); + const char* end = json_skip_value(choices); + size_t n = (size_t)(end - choices); + char* obj = malloc(n + 1); memcpy(obj, choices, n); obj[n] = '\0'; + const char* msg = json_find_key(obj, "message"); + if (!msg || *msg != '{') { free(obj); return el_wrap_str(el_strdup("")); } + const char* msg_end = json_skip_value(msg); + size_t mn = (size_t)(msg_end - msg); + char* msg_obj = malloc(mn + 1); memcpy(msg_obj, msg, mn); msg_obj[mn] = '\0'; + const char* content = json_find_key(msg_obj, "content"); + el_val_t result = el_wrap_str(el_strdup("")); + if (content && *content == '"') { + JsonParser jp = { .p = content, .end = content + strlen(content), .err = 0 }; + char* text = jp_parse_string_raw(&jp); + if (!jp.err && text) result = el_wrap_str(text); } + free(msg_obj); free(obj); + return result; +} + +/* Send a request to one provider. Returns the raw response string. + * format: 0 = openai, 1 = anthropic */ +static el_val_t llm_provider_request(const char* url, const char* key, + int format, const char* model, + const char* system_str, + const char* user_str) { + char* esc_sys = system_str && *system_str ? json_escape_alloc(system_str) : NULL; + char* esc_user = json_escape_alloc(user_str ? user_str : ""); + JsonBuf b; jb_init(&b); struct curl_slist* h = NULL; h = curl_slist_append(h, "Content-Type: application/json"); - { - size_t n = strlen(api_key) + 16; - char* line = malloc(n); - snprintf(line, n, "x-api-key: %s", api_key); - h = curl_slist_append(h, line); - free(line); + + if (format == 0) { /* OpenAI */ + char full_url[1024]; + if (strstr(url, "/chat/completions") || strstr(url, "/messages")) { + snprintf(full_url, sizeof(full_url), "%s", url); + } else { + snprintf(full_url, sizeof(full_url), "%s/v1/chat/completions", url); + } + { size_t n = strlen(key)+24; char* l=malloc(n); snprintf(l,n,"Authorization: Bearer %s",key); h=curl_slist_append(h,l); free(l); } + jb_putc(&b, '{'); + jb_puts(&b, "\"model\":"); jb_emit_escaped(&b, model ? model : "neuron"); + jb_puts(&b, ",\"max_tokens\":4096,\"messages\":["); + if (esc_sys && *esc_sys) { jb_puts(&b,"{\"role\":\"system\",\"content\":\""); jb_puts(&b,esc_sys); jb_puts(&b,"\"},"); } + jb_puts(&b, "{\"role\":\"user\",\"content\":\""); jb_puts(&b, esc_user); jb_puts(&b, "\"}]}"); + el_val_t resp = http_do("POST", full_url, b.buf, h); + curl_slist_free_all(h); free(b.buf); + if (esc_sys) free(esc_sys); free(esc_user); + return llm_extract_text_openai(resp); + } else { /* Anthropic */ + { size_t n = strlen(key)+16; char* l=malloc(n); snprintf(l,n,"x-api-key: %s",key); h=curl_slist_append(h,l); free(l); } + { size_t n = strlen(LLM_VERSION)+32; char* l=malloc(n); snprintf(l,n,"anthropic-version: %s",LLM_VERSION); h=curl_slist_append(h,l); free(l); } + jb_putc(&b, '{'); + jb_puts(&b, "\"model\":"); jb_emit_escaped(&b, model ? model : LLM_DEFAULT_MODEL); + jb_puts(&b, ",\"max_tokens\":4096"); + if (esc_sys && *esc_sys) { jb_puts(&b,",\"system\":\""); jb_puts(&b,esc_sys); jb_puts(&b,"\""); } + jb_puts(&b, ",\"messages\":[{\"role\":\"user\",\"content\":\""); jb_puts(&b, esc_user); jb_puts(&b, "\"}]}"); + el_val_t resp = http_do("POST", url, b.buf, h); + curl_slist_free_all(h); free(b.buf); + if (esc_sys) free(esc_sys); free(esc_user); + return llm_extract_text(resp); } - { - size_t n = strlen(LLM_VERSION) + 32; - char* line = malloc(n); - snprintf(line, n, "anthropic-version: %s", LLM_VERSION); - h = curl_slist_append(h, line); - free(line); +} + +static el_val_t llm_chain_call(const char* system_str, const char* user_str) { + char url_key[64], key_key[64], fmt_key[64], model_key[64]; + for (int i = 0; i < LLM_MAX_PROVIDERS; i++) { + snprintf(url_key, sizeof(url_key), "NEURON_LLM_%d_URL", i); + snprintf(key_key, sizeof(key_key), "NEURON_LLM_%d_KEY", i); + snprintf(fmt_key, sizeof(fmt_key), "NEURON_LLM_%d_FORMAT", i); + snprintf(model_key, sizeof(model_key), "NEURON_LLM_%d_MODEL", i); + const char* url = getenv(url_key); + const char* key = getenv(key_key); + if (!url || !*url || !key || !*key) break; /* end of chain */ + const char* fmt_s = getenv(fmt_key); + int fmt = (fmt_s && strcmp(fmt_s, "anthropic") == 0) ? 1 : 0; + const char* model = getenv(model_key); + fprintf(stderr, "[llm] trying provider %d (%s)\n", i, url); + el_val_t result = llm_provider_request(url, key, fmt, model, system_str, user_str); + const char* t = EL_CSTR(result); + if (t && *t && t[0] != '{') return result; /* success */ + fprintf(stderr, "[llm] provider %d failed or empty, trying next\n", i); } + /* Legacy fallback: ANTHROPIC_API_KEY */ + const char* api_key = getenv("ANTHROPIC_API_KEY"); + if (!api_key || !*api_key) return http_error_json("no LLM providers configured"); + fprintf(stderr, "[llm] using legacy ANTHROPIC_API_KEY fallback\n"); + return llm_provider_request(LLM_API_URL, api_key, 1, NULL, system_str, user_str); +} + +/* Legacy llm_request — kept for backward compat with agentic loop internals */ +static el_val_t llm_request(const char* json_body) { + const char* api_key = getenv("ANTHROPIC_API_KEY"); + if (!api_key || !*api_key) return http_error_json("ANTHROPIC_API_KEY not set"); + struct curl_slist* h = NULL; + h = curl_slist_append(h, "Content-Type: application/json"); + { size_t n=strlen(api_key)+16; char* l=malloc(n); snprintf(l,n,"x-api-key: %s",api_key); h=curl_slist_append(h,l); free(l); } + { size_t n=strlen(LLM_VERSION)+32; char* l=malloc(n); snprintf(l,n,"anthropic-version: %s",LLM_VERSION); h=curl_slist_append(h,l); free(l); } el_val_t resp = http_do("POST", LLM_API_URL, json_body, h); curl_slist_free_all(h); return resp; @@ -4611,45 +4740,14 @@ static el_val_t llm_extract_text(el_val_t resp_val) { } el_val_t llm_call(el_val_t model, el_val_t prompt) { - const char* m = llm_resolve_model(EL_CSTR(model)); - const char* u = EL_CSTR(prompt); - if (!u) u = ""; - char* esc_user = json_escape_alloc(u); - JsonBuf b; jb_init(&b); - jb_putc(&b, '{'); - jb_puts(&b, "\"model\":"); jb_emit_escaped(&b, m); - jb_puts(&b, ",\"max_tokens\":4096"); - jb_puts(&b, ",\"messages\":[{\"role\":\"user\",\"content\":\""); - jb_puts(&b, esc_user); - jb_puts(&b, "\"}]}"); - free(esc_user); - el_val_t resp = llm_request(b.buf); - free(b.buf); - return llm_extract_text(resp); + const char* u = EL_CSTR(prompt); if (!u) u = ""; + return llm_chain_call(NULL, u); } el_val_t llm_call_system(el_val_t model, el_val_t system_prompt, el_val_t user_prompt) { - const char* m = llm_resolve_model(EL_CSTR(model)); const char* s = EL_CSTR(system_prompt); if (!s) s = ""; const char* u = EL_CSTR(user_prompt); if (!u) u = ""; - char* esc_sys = json_escape_alloc(s); - char* esc_user = json_escape_alloc(u); - JsonBuf b; jb_init(&b); - jb_putc(&b, '{'); - jb_puts(&b, "\"model\":"); jb_emit_escaped(&b, m); - jb_puts(&b, ",\"max_tokens\":4096"); - if (*s) { - jb_puts(&b, ",\"system\":\""); - jb_puts(&b, esc_sys); - jb_puts(&b, "\""); - } - jb_puts(&b, ",\"messages\":[{\"role\":\"user\",\"content\":\""); - jb_puts(&b, esc_user); - jb_puts(&b, "\"}]}"); - free(esc_sys); free(esc_user); - el_val_t resp = llm_request(b.buf); - free(b.buf); - return llm_extract_text(resp); + return llm_chain_call(s, u); } /* ── Tool registry for llm_call_agentic ─────────────────────────────────── */ diff --git a/src/about.el b/src/about.el index 8a5e130..eb470c9 100644 --- a/src/about.el +++ b/src/about.el @@ -141,7 +141,7 @@ fn about_page() -> String {
-

© 2026 Neuron Technologies. All rights reserved.

+

© 2026 Neuron, LLC. All rights reserved.

Your memory. Your AI.

diff --git a/src/account.el b/src/account.el new file mode 100644 index 0000000..b22627a --- /dev/null +++ b/src/account.el @@ -0,0 +1,1273 @@ +// 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 } + +fn account_page(supabase_url: String, supabase_anon_key: String) -> String { + return " + + + + + My Account - Neuron + + + + + + + + + + + + + + +
+
+ + +
+
+
+ + + + +
+

Sign in to view your account

+

Use the same sign-in method you used when you signed up.

+ +
+ + + +
+
+ or +
+
+ + + + +

+ New here? Choose a plan to get started +

+

+
+
+
+ + +
+ + +
+
+

Account

+

Your Account

+

+
+
+ +
+
+ +
+ + +
+

Your Plan

+
+
+

Loading...

+
+
+
+
+
+ + +
+
+

Product Roadmap

+

As a Founding Member, you have visibility into what we're building. This is the real roadmap — not marketing.

+
+ +
+
+ Now shipping + Q3 2026 +
+
    +
  • macOS and Windows clients
  • +
  • Persistent memory — local, encrypted
  • +
  • Bring your own API keys (OpenAI, Anthropic, Grok)
  • +
  • Neuron Inference — priced below the major APIs
  • +
  • Gmail, Slack, Google Calendar connectors
  • +
  • VS Code extension
  • +
  • Founding member badge in-app
  • +
+
+ +
+
+ Following launch + Q4 2026 +
+
    +
  • Process and knowledge packets
  • +
  • Local inference via Ollama
  • +
  • Additional connectors — GitHub, Notion, Linear, more
  • +
  • Mobile companion app
  • +
  • Family accounts
  • +
+
+ +
+
+ 2027 + H1 2027 +
+
    +
  • Imprints — starting with C-suite (CEO, CTO, CFO, CMO, COO)
  • +
  • Enterprise accounts
  • +
  • Plugin marketplace (open to developers)
  • +
  • API access for power users
  • +
+
+
+

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.

+
+
+ + +
+
+
+

Family members

+

Add up to 5 children to your Founding Member plan. $10/month per child.

+
+
+
+ + + + +

+
+
+
+ + +
+
+

Your Founding Member Badge

+
+

+ This badge will appear in the Neuron app at launch. Your number is permanent. +

+
+
+ + +
+

What happens next

+
+
+
+
+
+
+
+

Now

+

Your preorder is confirmed.

+

A confirmation email has been sent to your address. Thank you for being here early.

+
+
+
+
+
+
+
+
+

Within 30 days

+

Neuron for Mac & Windows ships.

+

You'll receive an email with your download link and license key. macOS and Windows simultaneously.

+
+
+
+
+
+
+
+

Q3 2026

+

Neuron Inference launches.

+

Our own inference layer, priced below the major APIs. No setup required - it activates automatically for your plan.

+
+
+
+
+ + +
+

Download

+

Shipping within 30 days

+

Neuron for Mac & Windows

+

macOS and Windows simultaneously. You'll receive a download link and license key by email the moment it ships. Nothing to do right now.

+ +
+ + +
+

Devices

+
+
+ + + + + +
+
+

2 devices included with your plan

+

Currently: Setup at launch

+
+
+

Device activation happens when you install. Your license key will be in your launch email.

+
+ + +
+ +
+ +
+
+ +
+
+ + + + + +" +} diff --git a/src/checkout.el b/src/checkout.el index e150659..9f4a4a8 100644 --- a/src/checkout.el +++ b/src/checkout.el @@ -18,29 +18,40 @@ fn checkout_page(plan: String, pub_key: String) -> String { let plan_name: String = if is_founding { "Founding Member" } else { if is_free { "Free" } else { "Professional" } } let plan_price: String = if is_founding { "$199" } else { if is_free { "$0" } else { "$19 / month" } } let plan_desc: String = if is_founding { - "Pay once. Neuron inference forever. No subscription, ever." + "Pay once. Neuron inference when it launches - priced below the major APIs. No subscription, ever." + } else { if is_free { + "Start building your memory. No card required." } else { - "Full access. Neuron inference - cheaper than what you're paying now." - } + "Full access. Bring your own API keys or use Neuron Inference when it launches - Q3 2026." + } } let plan_cadence: String = if is_founding { "one-time" } else { if is_free { "forever" } else { "billed monthly" } } let features_html: String = if is_founding { - "
  • Neuron inference - priced below competitors, forever
  • -
  • Everything in Professional - forever
  • + "
  • Everything in Professional - forever
  • +
  • Neuron Inference when it launches - priced below the major APIs, forever
  • Never pay again - lifetime updates included
  • Founding member badge in the app
  • -
  • Private community - direct line to the team
  • +
  • Private founding member community
  • Shape the roadmap - your votes carry more weight
  • Beta features before general release
  • Name in the credits
  • " + } else { if is_free { + "
  • Persistent memory - never resets
  • +
  • Local inference via Ollama (coming)
  • +
  • Bring your own API keys
  • +
  • 3 marketplace plugins included
  • +
  • Core built-in capabilities
  • +
  • 2 devices included
  • " } else { - "
  • Neuron inference - metered, priced below OpenAI and Anthropic
  • -
  • Persistent memory - never resets
  • + "
  • Persistent memory - never resets
  • +
  • Bring your own API keys (OpenAI, Anthropic, Grok...)
  • +
  • Neuron Inference when it launches - Q3 2026, priced below the major APIs
  • Unlimited projects
  • Full plugin marketplace
  • IDE, Slack, and more integrations
  • -
  • Early access to new features
  • " - } +
  • Early access to new features
  • +
  • 2 devices included
  • " + } } return "