Commit Graph

22 Commits

Author SHA1 Message Date
will.anderson c72127032e Fix initStripe load order, subscription webhook email extraction, chat textarea UX
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m18s
- checkout.el: swap stripe_el_script before auth_script so initStripe is
  defined when Supabase auth fires onAuthStateChange on page load
- main.el: fix Stripe webhook email extraction for checkout.session.completed
  (subscription) events — customer_details is nested at data.object level,
  not at root; previous code only worked for payment_intent.succeeded
- page_close.c: replace <input type="text"> with <textarea rows="1"> in
  the chat widget input row so long questions are visible as you type
- page_css.c: update #neuron-demo-text CSS for textarea (resize:none,
  overflow:hidden, min/max-height, align-items:flex-end on row)
- chat-widget.el: add auto-resize event listener (grows up to ~4 lines),
  reset height to auto on send
2026-05-12 12:22:59 -05:00
will.anderson 5ce5f4a8be fix: binary asset serving + checkout centering
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m34s
el_runtime: http_response() JSON-encoded the body via jb_emit_escaped(),
which stops at the first null byte. PNG/binary files contain null bytes
at byte 8 (IHDR chunk length), so only 8 bytes were served — browsers
received a corrupt/truncated image and showed broken icons.

Fix: when _tl_fs_read_len > 0 (binary fs_read), copy raw bytes into a
thread-local side-channel (_tl_binary_body/_tl_binary_size) and write
the sentinel "__el_binary__" into the envelope body field. http_send_response()
detects the sentinel and substitutes the real bytes for sending.

checkout.el: .checkout-shell, .checkout-summary, and .checkout-form-wrap
had no CSS, leaving the page left-aligned and single-column. Added grid
layout (2-col desktop, 1-col mobile), max-width centering, and sticky
order summary.
2026-05-11 20:10:19 -05:00
will.anderson c966f2b455 implement free plan age verification via Stripe SetupIntent; personalize soul demo greeting with user name and timezone
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m49s
2026-05-11 02:03:39 -05:00
will.anderson f2ab12e65b feat(native-el-ui): rewrite checkout/main to use el-html vessel, fix page_close linker error
- checkout.el and main.el: replace raw import of el-html vessel with direct
  extern fn declarations; implementations come from dist/elhtml_impl.c (c_source)
- Add src/elhtml.el as reference file (all el-html extern fn declarations; not imported)
- dist/elhtml_impl.c: pre-compiled el-html vessel C (strips int main + sample global)
- dist/page_close.c: pre-compiled page_close implementation; elc OOMs after emitting
  the ~71KB page_open body and silently drops page_close, so supply it as c_source
- manifest.el: add elhtml_impl.c and page_close.c as c_source entries
- .gitignore: un-ignore dist/elhtml_impl.c and dist/page_close.c
2026-05-08 22:14:38 -05:00
will.anderson 96fca7ebf7 fix(checkout): split checkout_page into helpers to avoid single-function OOM in elc --emit-header
Dev — Build & local smoke test / build-smoke (pull_request) Failing after 11m34s
Extract nav and style blocks into checkout_nav_html() and checkout_style_html()
so the compiler processes each template in isolation rather than one 490-line
function with mixed HTML template AST and BinOp string concat.
2026-05-07 16:00:53 -05:00
will.anderson 494f4ef585 Merge stage fixes into dev — HAVE_CURL, free-tier checkout, Stripe dedup, escaped styles 2026-05-07 01:05:36 -05:00
will.anderson 00e62bb010 Fix free tier checkout and Stripe duplicate customers
Free tier:
- checkout-stripe.el bails out immediately for plan=free (no Stripe init)
- checkout-auth.el skips payment section reveal and initStripe for free plan
- checkout-free.el shows #free-success panel after auth (no card ever shown)
- /api/payment-intent returns early for free plan — no Stripe call

Stripe dedup (all paid plans):
- Stripe init now deferred to window.initStripe(email, name), called by
  checkout-auth.el after sign-in — email is known before intent is created
- /api/payment-intent finds-or-creates Stripe Customer by email before
  creating the PaymentIntent/SetupIntent and attaches customer upfront
- Eliminates the window between intent creation and /api/link-customer
  that was producing duplicate guest customers
2026-05-07 01:00:51 -05:00
Will Anderson 5cb13d67f7 feat: convert web El source to native HTML template syntax
Replace all return "..." HTML string literals with native El templates —
removes all \" escapes, converts + interpolations to {expr}/{raw(expr)},
and replaces conditional string concatenation with {#if}/{#else}/{/if}.
No functional changes; output is identical.
2026-05-05 05:21:19 -05:00
Will Anderson 94f6e749a0 Add El source files for all client-side JS
Recovers original JS from git history and ports it into proper El source
files under src/js/. Each file wraps the original JS in a native_js call
inside a main() function, making it valid El that compiles to a
self-contained IIFE via elc --target=js --bundle.

Files added:
  src/js/account-auth.el       - Supabase OTP magic-link (sendMagicLink)
  src/js/account-dashboard.el  - Account dashboard: session, plan card, family
  src/js/chat-widget.el        - Demo chat widget (neuronDemoToggle/Send/Reset)
  src/js/checkout-auth.el      - Checkout auth: OAuth, email sign-in/up
  src/js/checkout-free.el      - Free plan: auth-badge watch -> payment reveal
  src/js/checkout-stripe.el    - Stripe Payment Element (reads NEURON_CFG)
  src/js/enterprise.el         - Enterprise inquiry form + headcount filter
  src/js/environmental.el      - Efficiency calculator slider
  src/js/gallery.el            - Gallery nav, search/sort, Supabase voting
  src/js/main.el               - Share page voting + copyForPlatform
  src/js/marketplace.el        - Developer interest form
  src/js/nav.el                - Nav hamburger + Mission dropdown
  src/js/styles.el             - Landing: nav scroll, reveal, founding counter
2026-05-04 11:23:21 -05:00
Will Anderson 8704f7cdfc update el runtime to v1.2.0, refactor checkout script to external asset
Deploy marketing to Cloud Run / deploy (push) Has been cancelled
2026-05-03 01:13:25 -05:00
Will Anderson 88ee3d53de fix: free tier shows payment form only after account created (auth-badge reveal)
Deploy marketing to Cloud Run / deploy (push) Has been cancelled
2026-05-02 18:48:38 -05:00
Will Anderson 6702fb7f9a fix: free tier checkout - full Stripe form with SetupIntent (card saved, no charge)
Deploy marketing to Cloud Run / deploy (push) Has been cancelled
2026-05-02 18:38:51 -05:00
Will Anderson d2908099c4 fix: add sign-up form to free tier checkout - was showing text with no form
Deploy marketing to Cloud Run / deploy (push) Has been cancelled
2026-05-02 18:23:28 -05:00
Will Anderson 640813e42e migrate stage build to native elc; chat restores from localStorage on return
Build pipeline
- build-stage.sh replaces the old in-Dockerfile bootstrap.py path. Host
  pre-compiles src/*.el into dist/main.c via the canonical native elc at
  foundation/el/dist/platform/elc and applies the stub-decl sed before
  docker buildx runs.
- Dockerfile.stage drops bootstrap.py + python3 from the builder stage
  and just runs cc on the host-supplied dist/main.c.
- Pre-rendered HTML shells under /srv/landing/ are now chowned to the
  landing user so the El page-builder's fs_write at startup can rewrite
  them — without that, post-COPY edits never reach the served HTML and
  the served page stays as the stale build-time fallback.

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

Extracted JS assets (src/assets/js/*.js + manifest.json) and the
extract-js.py helper land here too — they match what the new build-stage
flow produces and removes the inline <script> blobs from the served HTML.
2026-05-02 11:15:09 -05:00
Will Anderson 1349450b14 checkout: hold-until-launch radio actually works (SetupIntent path)
The Professional plan's "Hold until product launches" radio was wired
into the markup but ignored by both /api/payment-intent and the JS
submit handler. Buyers who picked it would still get charged
immediately because the server always created a PaymentIntent and
the client always called stripe.confirmPayment.

Fix:
  * /api/payment-intent reads body.timing. When plan=professional and
    timing=later, it creates a SetupIntent (usage=off_session) instead
    of a PaymentIntent and returns {setup_mode:true, client_secret:...}.
    Founding stays unconditional (lifetime, charge now).
  * checkout JS now reads the radio (currentTiming()), passes timing
    to the server, and re-fetches a new client_secret on radio change
    so the buyer's choice is honored even if they toggle after first
    mount.
  * window._neuronMode tracks 'payment' vs 'setup'. The submit handler
    branches: stripe.confirmSetup for save-card, stripe.confirmPayment
    for charge-now. The submit button label updates to "Save my card -
    no charge today" when in setup mode so the buyer sees the
    intent before they hit submit.
  * /api/link-customer receives timing + mode so the server can
    differentiate at attach time.

A future webhook on setup_intent.succeeded will create the actual
Subscription with trial_end at launch (Q3 2026 / 2026-09-01) - that
piece is queued via metadata[hold_until]=launch on the SetupIntent.
For now, the saved payment method sits in Stripe untouched.

The point: a buyer who picks "Hold until launch" is NOT charged. The
flow has to be airtight - no surprise charges.
2026-05-02 00:45:44 -05:00
Will Anderson eea9ff8ff4 account: server-side plan lookup via /api/my-plan, scrub internal comments from JS
The /account "Loading..." spinner stayed on forever because the
browser-side waitlist read went through the anon key and didn't reach
the row. Replaced it with a POST /api/my-plan: the server verifies
the user's access_token via Supabase /auth/v1/user, then reads the
waitlist row with the service key. Bypasses RLS without exposing the
service key to the browser.

Stripped implementation comments from the served JS so the browser
doesn't broadcast how internals are shaped.

Build pipeline: declared the supabase_auth_user stub for both
build-local.sh and Dockerfile.stage so the bootstrap-injected forward
declarations match what's actually linked.
2026-05-01 23:46:26 -05:00
Will Anderson d6731f7834 checkout: re-add link-customer + redirect to /account, fix success/account routes
Restores the /api/link-customer endpoint that was lost in the stash. It
runs right before stripe.confirmPayment() and:
  - searches Stripe for an existing Customer by email
  - creates one if missing (URL-encodes + and @ so Gmail aliases work)
  - attaches the Customer to the live PaymentIntent
  - upserts the Supabase waitlist row with stripe_customer_id + plan

Stripe locks customer on a Charge once set, so the webhook handler is
the second-line backstop for any race where confirmPayment beats the
link call.

Also: change return_url from /marketplace/success to /account?welcome=1
so buyers land where they need to be, and switch /marketplace/success
and /account from str_eq to str_starts_with so Stripe-appended query
strings (?payment_intent=...&redirect_status=succeeded) don't 404.
2026-05-01 23:34:52 -05:00
Will Anderson 702888d3aa checkout: drop auth wall so payment form mounts on page load
The auth-first flow blocked Stripe Elements from initialising for any
visitor without an existing Supabase session. Users hit the checkout
page, saw "Sign in to continue", and could not get to a card field at
all. Restored the inline-JS path (HEAD before extraction broke it),
flipped payment-section visible by default, kept the sign-in panel
behind an explicit "Already have an account? Sign in" link.

Build pipeline: added supabase_get stub injection and -lssl/-lcrypto
linker flags (web_stubs.c uses EVP for the AES-256-GCM transport).
Without those the Docker build aborts at link time.
2026-05-01 23:26:12 -05:00
Will Anderson 00f2323c98 v1.0 - launch: full nav on gallery, chat widget auto-open, comparison logos, checkout fixes 2026-05-01 18:13:06 -05:00
Will Anderson ff1f9577db Add marketplace section, OAuth checkout, social icons, 35% efficiency claim, preorder CTAs, enterprise Q1 2027, founding member limits, paragraph spacing in demo chat, no em dashes 2026-05-01 09:20:45 -05:00
Will Anderson 51d2e64c22 Em dash removal, nav/form/terms updates
- Replace all em dashes with hyphens site-wide (22 files)
- Add Safety to nav
- Remove Express Interest button from enterprise header (form is the path now)
- Enterprise form: secondary headcount selection shows honest odds message; yes disables submit button
- Terms §5: add network layer indemnification (patent pending 64/036,821); no proprietary names
- Terms §4: Neuron Inference is live now, not Q3 2026
2026-04-29 17:14:56 -05:00
Will Anderson 393d982d04 Add integrated checkout, child safety section, and content polish
- checkout.el: Custom Stripe Payment Element page at /checkout?plan=...
- safety.el: Hard Bell and emergency routing architecture section
- main.el: Wire checkout + safety, /api/payment-intent, STRIPE_PUBLISHABLE_KEY
- enterprise_terms.el: Add nav bar (was missing, page had no navigation)
- styles.el: Checkout buttons now link to integrated page, not hosted Stripe
- El runtime: Auto-detect HTML responses, serve text/html vs application/json
- footer.el: Wordmark image centered above "Built Different." tagline
- environmental.el: Replace SQLite with custom on-device storage
- enterprise.el: Replace SQLite with custom on-device storage
- pricing.el: Local models degraded performance note near Ollama feature
- about.el: "left home at fifteen", remove programming language mention
2026-04-29 16:59:57 -05:00