// Sherpas Labs — Login page (production) // Google OAuth → /auth/login // Partner email+password → /auth/partner/login // Access request → POST /api/access-request const LoginPage = () => { const [requesting, setRequesting] = React.useState(false); const [partnerEmail, setPartnerEmail] = React.useState(""); const [partnerPassword, setPartnerPassword] = React.useState(""); const [partnerLoading, setPartnerLoading] = React.useState(false); const [partnerError, setPartnerError] = React.useState(""); const [changePasswordUser, setChangePasswordUser] = React.useState(null); const params = new URLSearchParams(window.location.search); const error = params.get("error"); const errorMessages = { oauth_denied: "Sign-in was cancelled.", invalid_callback: "Invalid OAuth callback. Please try again.", invalid_state: "Session expired. Please try again.", token_exchange: "Could not complete sign-in. Please try again.", userinfo: "Could not retrieve your profile. Please try again.", pending_approval: "Your request is pending review. We'll be in touch.", access_denied: "Access denied.", session_expired: "Your session expired. Please sign in again.", }; const errorMsg = error ? (errorMessages[error] || "An error occurred. Please try again.") : null; const onGoogle = () => { window.location.href = "/auth/login"; }; const onPartnerSubmit = async (e) => { e.preventDefault(); if (!partnerEmail || !partnerPassword) return; setPartnerLoading(true); setPartnerError(""); try { const res = await fetch("/auth/partner/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: partnerEmail, password: partnerPassword }), credentials: "include", }); const data = await res.json(); if (!res.ok) { setPartnerError(data.message || "Incorrect email or password."); return; } document.cookie = `sherpas_labs_session=${data.session_token}; path=/; SameSite=Lax`; if (data.must_change_password) { setChangePasswordUser({ email: partnerEmail, tempPassword: partnerPassword }); } else { window.location.href = "/index"; } } catch (err) { setPartnerError("Something went wrong. Please try again."); } finally { setPartnerLoading(false); } }; // Responsive: detect mobile const isMobile = window.innerWidth < 768; return (
{/* LEFT — dark manifesto (hidden on mobile) */} {!isMobile && ( )} {/* RIGHT — form */}
{/* Mobile header */} {isMobile && (
Sherpas Labs
)} {!isMobile && (
↗   sherpaswealth.com
)}
Sign in

Open the file room.

Sherpas team uses Google. Design partners sign in with the credentials we issued.

{errorMsg && (
{errorMsg}
)} {/* Google button */}
Sherpas team · @sherpaswealth.com only
{/* Divider */}
or, design partners
{/* Partner form */}
{partnerError && (
{partnerError}
)} {/* Request access */}
Not on the list?
Request access — we read every one.
By signing in you agree to Sherpas Labs access terms.
{requesting && setRequesting(false)} />} {changePasswordUser && { window.location.href = "/index"; }} />}
); }; // --- Google G glyph --- const GoogleGlyph = () => ( React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24" }, React.createElement("path", { fill: "#4285F4", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" }), React.createElement("path", { fill: "#34A853", d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" }), React.createElement("path", { fill: "#FBBC05", d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" }), React.createElement("path", { fill: "#EA4335", d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" }) ) ); // --- Underline field --- const Field = ({ label, type, value, onChange, placeholder, autoComplete }) => { const [focus, setFocus] = React.useState(false); return (
onChange(e.target.value)} placeholder={placeholder} autoComplete={autoComplete} onFocus={() => setFocus(true)} onBlur={() => setFocus(false)} style={{ width: "100%", border: "none", outline: "none", borderBottom: `1px solid ${focus ? "var(--ink)" : "var(--line-strong)"}`, padding: "10px 0", fontFamily: "var(--font-mono)", fontSize: 14, color: "var(--ink)", background: "transparent", transition: "border-color 0.15s", boxSizing: "border-box", }} />
); }; // --- Change password modal --- const ChangePasswordModal = ({ user, onDone }) => { const [newPw, setNewPw] = React.useState(""); const [confirmPw, setConfirmPw] = React.useState(""); const [loading, setLoading] = React.useState(false); const [err, setErr] = React.useState(""); const onSubmit = async (e) => { e.preventDefault(); if (newPw.length < 8) { setErr("Password must be at least 8 characters."); return; } if (newPw !== confirmPw) { setErr("Passwords don't match."); return; } setLoading(true); setErr(""); try { const res = await fetch("/auth/partner/change-password", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: user.email, current_password: user.tempPassword, new_password: newPw }), credentials: "include", }); const data = await res.json(); if (!res.ok) { setErr(data.detail || "Something went wrong."); return; } document.cookie = `sherpas_labs_session=${data.session_token}; path=/; SameSite=Lax`; onDone(); } catch (e) { setErr("Something went wrong. Please try again."); } finally { setLoading(false); } }; return (
● First login

Set your password.

Choose a password you'll use going forward. At least 8 characters.

{err &&
{err}
}
); }; // --- Request access modal --- const RequestAccessModal = ({ onClose }) => { const [sent, setSent] = React.useState(false); const [loading, setLoading] = React.useState(false); const [form, setForm] = React.useState({ name: "", company: "", email: "", role: "", reason: "" }); const [err, setErr] = React.useState(""); const set = (k) => (v) => setForm((f) => ({ ...f, [k]: v })); const onSubmit = async (e) => { e.preventDefault(); if (!form.name || !form.email) { setErr("Name and email are required."); return; } setLoading(true); setErr(""); try { const res = await fetch("/api/access-request", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: form.name, email: form.email, company: form.company, role: form.role, reason: form.reason }), }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || "Request failed"); setSent(true); } catch (e) { setErr(e.message || "Something went wrong."); } finally { setLoading(false); } }; return (
e.stopPropagation()} style={{ background: "var(--bg)", maxWidth: 500, width: "100%", padding: 36, border: "1px solid var(--line-strong)", position: "relative" }}> {!sent ? ( <>
Request access

Tell us who you are.

We read every request carefully, one at a time.

{err &&
{err}
}