Pricing
Start free, unlock advanced intelligence
Free
Get started with essential features at no cost
$0 / month
10 free monthly credits
Unlock all core platform features
Build elegant Web and Mobile experiences
Instant access to the most advanced models
One-click LLM integration
Standard
Perfect for first-time builders
Annual
$20 / month
Everything in Free, plus:
Build web & mobile apps
Private project hosting
100 credits per month
Purchase extra credits as needed
GitHub integration
Fork tasks
Pro
Built for serious creators and brands
Annual
$200 / month
Everything in Standard, plus:
1M context window
Ultra thinking
System Prompt Edit
Create custom AI agents
High-performance computing
750 Monthly Credits
Priority customer support
Enterprise
For large organizations with custom needs
Custom
Everything in Business, plus:
User-Level Credit Limits
Audit Logs
Self Hosted Database Support
Priority SLA and Support
User Groups
Deploy apps in your own cloud (VPC setup)
Credit Usage Reports and Analytics Dashboard
Business
For Businesses with custom needs
Custom
Everything in Pro, plus:
Role-Based Access Control (RBAC)
Single sign-on (SSO)
Shared Team Workspaces
Real-time Co-Editing & Coworking
Start Building
on Emergent today
on Emergent today
Try Emergent
`;
const blackBtn = "padding:11px 28px;background:#000;color:#fff;border:none;border-radius:999px;font-size:14px;font-weight:500;cursor:pointer;font-family:inherit;";
function openSuccessModal(email) {
const o = overlay();
const c = card();
c.innerHTML = `${checkIcon}\n
Check your email
\nWe sent a sign-in link to ${email}. Click the link to continue.
\n `; c.querySelector("button").addEventListener("click", () => o.remove()); o.addEventListener("click", e => { if (e.target === o) o.remove(); }); o.appendChild(c); document.body.appendChild(o); } function openResetModal() { const o = overlay(); const c = card(); const renderForm = () => { c.innerHTML = `\nReset your password
\nEnter your email and we'll send you a link to set a new password.
\n \n \n \n `; const input = c.querySelector("input"); const err = c.querySelector(".reset-err"); const go = c.querySelector(".reset-go"); c.querySelector(".reset-cancel").addEventListener("click", () => o.remove()); const submit = async () => { const email = input.value.trim(); if (!email) { err.style.display = "block"; err.textContent = "Please enter your email"; return; } err.style.display = "none"; go.disabled = true; go.style.opacity = "0.6"; go.textContent = ""; const sp = document.createElement("div"); sp.className = "emergent-spinner"; go.appendChild(sp); track(AUTH_EVENTS.PASSWORD_RESET_REQUESTED); try { const sb = await getSupabase(); const captchaToken = await getCaptchaToken(); const {error: error} = await sb.auth.resetPasswordForEmail(email, { redirectTo: buildCallbackUrl(), ...captchaToken ? { captchaToken: captchaToken } : {} }); if (error) { track(AUTH_EVENTS.PASSWORD_RESET_FAILED, { error: error.message }); err.style.display = "block"; err.textContent = error.message; go.disabled = false; go.style.opacity = "1"; sp.remove(); go.textContent = "Send reset link"; return; } renderSent(email); } catch (e2) { track(AUTH_EVENTS.PASSWORD_RESET_FAILED, { error: e2?.message || String(e2) }); err.style.display = "block"; err.textContent = e2?.message || "Failed to send reset link"; go.disabled = false; go.style.opacity = "1"; sp.remove(); go.textContent = "Send reset link"; } }; go.addEventListener("click", submit); input.addEventListener("keydown", e => { if (e.key === "Enter") { e.preventDefault(); submit(); } }); }; const renderSent = email => { c.innerHTML = `${checkIcon}\nCheck your email
\nWe sent a password reset link to ${email}. Click the link to set a new password.
\n `; c.querySelector("button").addEventListener("click", () => o.remove()); }; renderForm(); o.addEventListener("click", e => { if (e.target === o) o.remove(); }); o.appendChild(c); document.body.appendChild(o); } const OTP_LEN = 6; const buildOtpInputs = wrapper => { if (!wrapper) return []; let existing = [ ...wrapper.querySelectorAll("input") ]; if (existing.length >= OTP_LEN) return existing.slice(0, OTP_LEN); wrapper.innerHTML = ""; if (getComputedStyle(wrapper).display.indexOf("flex") === -1) { wrapper.style.display = "flex"; wrapper.style.gap = "10px"; wrapper.style.justifyContent = "center"; } const made = []; for (let n = 0; n < OTP_LEN; n++) { const el = document.createElement("input"); el.className = "hero_auth_otp-input"; el.type = "text"; el.inputMode = "numeric"; el.maxLength = 1; el.autocomplete = n === 0 ? "one-time-code" : "off"; el.setAttribute("aria-label", `Digit ${n + 1} of ${OTP_LEN}`); el.style.cssText = "flex:1 1 0;min-width:0;max-width:56px;height:56px;text-align:center;font-size:22px;font-weight:600;color:#111;background:#fff;border:1px solid #d4d4d8;border-radius:14px;outline:none;font-family:inherit;box-sizing:border-box;transition:border-color .15s;"; wrapper.appendChild(el); made.push(el); } return made; }; const spread = (inputs, startIdx, digits) => { let i = startIdx; for (const ch of digits) { if (i >= inputs.length) break; inputs[i].value = ch; i++; } inputs[Math.min(i, inputs.length - 1)].focus(); }; const isFilled = inputs => inputs.every(i => /\d/.test(i.value)); const wireOtpInputs = (inputs, onComplete) => { inputs.forEach((input, idx) => { if (input._otpWired) return; input._otpWired = true; input.addEventListener("focus", () => { input.style.borderColor = "#111"; input.select(); }); input.addEventListener("blur", () => { input.style.borderColor = "#d4d4d8"; }); input.addEventListener("input", () => { const digits = input.value.replace(/\D/g, ""); if (!digits) { input.value = ""; return; } if (digits.length > 1) spread(inputs, idx, digits); else { input.value = digits; if (idx < inputs.length - 1) inputs[idx + 1].focus(); } if (isFilled(inputs)) onComplete(); }); input.addEventListener("keydown", e => { if (e.key === "Backspace" && !input.value && idx > 0) { e.preventDefault(); inputs[idx - 1].value = ""; inputs[idx - 1].focus(); } else if (e.key === "ArrowLeft" && idx > 0) { e.preventDefault(); inputs[idx - 1].focus(); } else if (e.key === "ArrowRight" && idx < inputs.length - 1) { e.preventDefault(); inputs[idx + 1].focus(); } }); input.addEventListener("paste", e => { e.preventDefault(); const text = ((e.clipboardData || window.clipboardData).getData("text") || "").replace(/\D/g, ""); if (text) { spread(inputs, idx, text); if (isFilled(inputs)) onComplete(); } }); }); }; const findByText = (root, needle) => [ ...root.querySelectorAll("a,button") ].find(el => el.textContent.trim().toLowerCase().includes(needle)) || null; const reveal = el => { el.style.removeProperty("display"); if (getComputedStyle(el).display === "none") el.style.display = "block"; }; function showOtpState(form, phone) { const block = form.closest(".w-form") || form.parentElement; const done = block && block.querySelector(".hero_auth_form-success, .w-form-done"); if (!done) { console.error("[phone:otp] no .hero_auth_form-success success state found"); return; } form.style.display = "none"; reveal(done); const phoneEl = done.querySelector(".hero_auth_phone-confirm div") || done.querySelector(".hero_auth_phone-confirm"); if (phoneEl) phoneEl.textContent = phone; const wrapper = done.querySelector(".hero_auth_otp-wrapper"); const inputs = buildOtpInputs(wrapper); inputs.forEach(i => { i.value = ""; i.disabled = false; }); let status = done.querySelector(".emergent-otp-status"); if (!status) { status = document.createElement("div"); status.className = "emergent-otp-status"; status.style.cssText = "font-size:13px;margin-top:14px;text-align:center;min-height:18px;display:none;"; (wrapper || done).insertAdjacentElement("afterend", status); } const setStatus = (msg, kind) => { status.style.display = msg ? "block" : "none"; status.style.color = kind === "ok" ? "#16a34a" : kind === "muted" ? "#8a8a90" : "#dc2626"; status.textContent = msg || ""; }; const clearInputs = () => { inputs.forEach(i => i.value = ""); inputs[0] && inputs[0].focus(); }; let verifying = false; const verify = async () => { const token = inputs.map(i => i.value).join("").replace(/\D/g, ""); if (token.length < OTP_LEN || verifying) return; verifying = true; inputs.forEach(i => i.disabled = true); setStatus("Verifying…", "muted"); track(AUTH_EVENTS.PHONE_OTP_VERIFY_ATTEMPT); const fail = (msg, err) => { track(AUTH_EVENTS.PHONE_OTP_VERIFY_FAILED, { error: err || msg }); setStatus(msg, "err"); verifying = false; inputs.forEach(i => i.disabled = false); clearInputs(); }; try { const sb = await getSupabase(); const {data: data, error: error} = await sb.auth.verifyOtp({ phone: phone, token: token, type: "sms" }); if (error) return fail(error.message, error.message); if (!data?.session?.access_token) return fail("Verified, but no session returned", "no_session_returned"); setStatus("Verified! Redirecting…", "ok"); redirectWithSession(data.session, "signup"); } catch (e2) { fail(e2?.message || "Verification failed", e2?.message || String(e2)); } }; wireOtpInputs(inputs, verify); const edit = done.querySelector('[data-auth="phone-edit"], .hero_auth-link'); if (edit && !edit._otpBound) { edit._otpBound = true; edit.addEventListener("click", e => { e.preventDefault(); e.stopPropagation(); done.style.display = "none"; form.style.display = ""; setStatus(""); }); } const resend = done.querySelector('[data-auth="phone-resend"]') || findByText(done, "resend"); if (resend && !resend._otpBound) { resend._otpBound = true; resend.addEventListener("click", async e => { e.preventDefault(); e.stopPropagation(); setStatus("Sending a new code…", "muted"); track(AUTH_EVENTS.PHONE_OTP_REQUESTED); try { const sb = await getSupabase(); const captchaToken = await getCaptchaToken(); const {error: error} = await sb.auth.signInWithOtp({ phone: phone, options: { channel: "sms", ...captchaToken ? { captchaToken: captchaToken } : {} } }); if (error) { track(AUTH_EVENTS.PHONE_OTP_REQUEST_FAILED, { error: error.message }); return setStatus(error.message, "err"); } setStatus("New code sent.", "ok"); clearInputs(); } catch (e2) { track(AUTH_EVENTS.PHONE_OTP_REQUEST_FAILED, { error: e2?.message || String(e2) }); setStatus(e2?.message || "Couldn't resend", "err"); } }); } setStatus(""); inputs[0] && inputs[0].focus(); } let _emailOtp = null; const teardownEmailOtp = (form, container) => { (_emailOtp?.timers || []).forEach(clearInterval); _emailOtp = null; if (container) container.style.display = "none"; document.querySelectorAll(".emergent-email-otp-status-injected").forEach(s => s.remove()); if (form) form.style.display = ""; }; function showEmailOtpState(form, btn, start, creds) { const step = document.querySelector('[data-auth="email-otp"]'); if (!step) { console.error('[email-otp] no [data-auth="email-otp"] container found — falling back to password login'); setLoading(btn, true); return legacyEmailSignin(form, btn, creds.email, creds.password); } teardownEmailOtp(form, step); _emailOtp = { ticket: start.ticket, creds: creds, expiresAt: Date.now() + (start.expires_in_seconds || 600) * 1e3, timers: [] }; form.style.display = "none"; reveal(step); const maskedEl = step.querySelector('[data-auth="email-otp-masked"]'); if (maskedEl) maskedEl.textContent = start.email_masked || creds.email; const boxes = step.querySelector('[data-auth="email-otp-boxes"]'); const editLink = step.querySelector('[data-auth="email-otp-edit"]') || findByText(step, "edit"); const resend = step.querySelector('[data-auth="email-otp-resend"]') || findByText(step, "resend"); let status = step.querySelector('[data-auth="email-otp-status"]'); if (!status) { status = document.createElement("div"); status.className = "emergent-email-otp-status-injected"; status.style.cssText = "font-size:13px;margin-top:14px;text-align:center;min-height:18px;display:none;"; (boxes || step).insertAdjacentElement("afterend", status); } const setStatus = (msg, kind) => { status.style.display = msg ? "block" : "none"; status.style.color = kind === "ok" ? "#16a34a" : kind === "muted" ? "#8a8a90" : "#dc2626"; status.textContent = msg || ""; }; const inputs = buildOtpInputs(boxes); const clearInputs = () => { inputs.forEach(i => i.value = ""); inputs[0] && inputs[0].focus(); }; const goBack = msgForForm => { teardownEmailOtp(form, step); if (msgForForm) showError(btn, msgForForm); }; if (editLink && !editLink._emailOtpBound) { editLink._emailOtpBound = true; editLink.addEventListener("click", e => { e.preventDefault(); e.stopPropagation(); goBack(); }); } let cooldown = Math.max(0, start.resend_available_in_seconds || 60); if (resend) { if (resend._resendLabel == null) resend._resendLabel = getLabel(resend) || OTP_COPY.resend; const renderResend = () => { if (cooldown > 0) { resend.style.opacity = ".5"; resend.style.pointerEvents = "none"; setLabel(resend, OTP_COPY.resendIn(cooldown)); cooldown--; } else { resend.style.opacity = ""; resend.style.pointerEvents = ""; setLabel(resend, resend._resendLabel); } }; renderResend(); _emailOtp.timers.push(setInterval(renderResend, 1e3)); } let verifying = false; const verify = async () => { const code = inputs.map(i => i.value).join("").replace(/\D/g, ""); if (code.length < OTP_LEN || verifying || !_emailOtp) return; if (Date.now() > _emailOtp.expiresAt) return goBack(OTP_COPY.ticketExpired); verifying = true; inputs.forEach(i => i.disabled = true); setStatus(OTP_COPY.verifying, "muted"); track(AUTH_EVENTS.EMAIL_OTP_VERIFY_ATTEMPT); const fail = (msg, err, wipe = true) => { track(AUTH_EVENTS.EMAIL_OTP_VERIFY_FAILED, { error: err || msg }); setStatus(msg, "err"); verifying = false; inputs.forEach(i => i.disabled = false); if (wipe) clearInputs(); }; try { const {ok: ok, status: st, json: json} = await otpPost(OTP_COMPLETE_URL, { code: code, ticket: _emailOtp.ticket }); if (ok) { const session = otpSession(json); if (session) { setStatus("", "ok"); return redirectWithSession(session, "signin"); } return fail(OTP_COPY.generic, "complete_ok_no_session"); } const raw = (otpErrMsg(json) || "").toLowerCase(); if (st === 410 || raw.includes("expired") || raw.includes("ticket")) { track(AUTH_EVENTS.EMAIL_OTP_VERIFY_FAILED, { error: "ticket_expired" }); return goBack(OTP_COPY.ticketExpired); } if (st === 423 || raw.includes("locked") || raw.includes("too many")) { track(AUTH_EVENTS.EMAIL_OTP_VERIFY_FAILED, { error: "locked" }); return goBack(OTP_COPY.locked); } const attempts = json?.attempts_left ?? json?.attempts_remaining; fail(attempts != null ? OTP_COPY.invalidWithAttempts(attempts) : otpErrMsg(json) || OTP_COPY.invalidCode, "invalid_code"); } catch (e2) { console.error("[signin/otp-complete]", e2); fail(OTP_COPY.generic, e2?.message || String(e2), false); } }; wireOtpInputs(inputs, verify); if (resend && !resend._emailOtpBound) { resend._emailOtpBound = true; resend.addEventListener("click", async e => { e.preventDefault(); e.stopPropagation(); if (cooldown > 0 || !_emailOtp) return; setStatus("Sending a new code…", "muted"); track(AUTH_EVENTS.EMAIL_OTP_REQUESTED, { resend: true }); try { const {ok: ok, status: st, json: json} = await otpPost(OTP_START_URL, _emailOtp.creds); if (ok && json?.ticket) { _emailOtp.ticket = json.ticket; _emailOtp.expiresAt = Date.now() + (json.expires_in_seconds || 600) * 1e3; cooldown = Math.max(0, json.resend_available_in_seconds || 60); setStatus(OTP_COPY.codeSent, "ok"); clearInputs(); } else if (st === 429) { setStatus(OTP_COPY.cooldown, "err"); } else { track(AUTH_EVENTS.EMAIL_OTP_REQUEST_FAILED, { error: otpErrMsg(json) || st }); setStatus(OTP_COPY.sendFailed, "err"); } } catch (e2) { track(AUTH_EVENTS.EMAIL_OTP_REQUEST_FAILED, { error: e2?.message || String(e2) }); setStatus(OTP_COPY.sendFailed, "err"); } }); } inputs[0] && inputs[0].focus(); } document.querySelectorAll('[data-auth="forgot"]').forEach(el => bindClick(el, () => openResetModal())); window.addEventListener("pageshow", () => { document.querySelectorAll(".emergent-spinner").forEach(sp => { const b = sp.parentElement; sp.remove(); if (b) b.style.opacity = "1"; }); }); const SB_KEY = "sb-" + new URL(SUPABASE_URL).hostname.split(".")[0] + "-auth-token"; const hasStoredSession = () => { try { const raw = localStorage.getItem(SB_KEY); if (!raw) return false; const p = JSON.parse(raw); return !!(p?.refresh_token || p?.access_token); } catch { return false; } }; const tryExistingSession = async () => { if (!hasStoredSession()) return false; try { const sb = await getSupabase(); const { data } = await sb.auth.getSession(); if (data?.session?.access_token) { redirectWithSession(data.session, "signin"); return true; } } catch (e) { console.error("[session-check]", e); } return false; }; document.querySelectorAll('[dom-item="modal-button"]').forEach(el => { el.addEventListener("click", async e => { if (el.__sessionChecked) return; e.preventDefault(); e.stopPropagation(); setLoading(el, true); const went = await tryExistingSession(); setLoading(el, false); if (went) return; el.__sessionChecked = 1; el.click(); }, true); }); })()
Build Full-Stack
Web & mobile apps in minutes
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Enter the 6-digit code sent to


