// Auth screens — login, signup, forgot, reset, MFA, magic-link, verify
// Sits between marketing and admin: own shell, no marketing nav
const Auth = ({ screen = 'login', setPage, setAuthScreen }) => {
const goto = (s) => { setAuthScreen ? setAuthScreen(s) : null; window.scrollTo({top:0}); };
return (
{/* Left brand panel */}
{/* Right form panel */}
setPage('home')} style={{display:'flex',alignItems:'center',gap:10}}>
{Icon.atom()}
SolidAtoms
setPage('home')} className="mono" style={{fontSize:11,color:'var(--ink-3)',letterSpacing:'0.06em'}}>← BACK TO SITE
{screen === 'login' &&
}
{screen === 'signup' && }
{screen === 'forgot' && }
{screen === 'sent' && }
{screen === 'reset' && }
{screen === 'mfa' && }
{screen === 'verify' && }
{screen === 'magic' && }
);
};
// ─────────────────────────────────────────────────────────────────
// LEFT BRAND PANEL
const AuthBrandPanel = ({ screen }) => {
const copy = {
login: {kicker:'Welcome back', headline:'Pick up where you left off.', sub:'Your dashboards, files, conversations — exactly as you left them.'},
signup: {kicker:'New here?', headline:'A place for the work to live.', sub:'One workspace for the studio, your team, every file, every revision.'},
forgot: {kicker:'Forgot it?', headline:'Happens to all of us.', sub:'Enter your email. We\'ll send a one-time link in under a minute.'},
sent: {kicker:'Sent', headline:'Check your inbox.', sub:'If we recognise that email, a reset link is on its way.'},
reset: {kicker:'Reset password', headline:'A fresh password, one last time.', sub:'Long, weird, and ideally generated by a manager.'},
mfa: {kicker:'One more thing', headline:'Two-factor verification.', sub:'Enter the 6-digit code from your authenticator app.'},
verify: {kicker:'Almost there', headline:'Confirm your email.', sub:'We sent a link to verify the address you signed up with.'},
magic: {kicker:'Magic link sent', headline:'Skip the password.', sub:'Click the link in your inbox. The session opens automatically.'},
};
const c = copy[screen] || copy.login;
return (
{/* Ambient glow */}
{/* Decorative pattern */}
{c.headline.split(' ').slice(0,-1).join(' ')} {c.headline.split(' ').slice(-1)}
{c.sub}
JC
Jane Cooper
CTO · LEDGERLINE
"The shipped product was exactly what we'd discussed in week one — not what scope creep turned it into at week eighteen. That alone made it worth it."
);
};
// ─────────────────────────────────────────────────────────────────
// LOGIN
const AuthLogin = ({ goto, setPage }) => (
Sign in
Don't have an account? goto('signup')} style={{color:'var(--accent)',textDecoration:'underline',textUnderlineOffset:3}}>Create one
{/* SSO row */}
);
// ─────────────────────────────────────────────────────────────────
// SIGNUP
const AuthSignup = ({ goto, setPage }) => {
const [pwd, setPwd] = React.useState('');
return (
Create your account
Already have one? goto('login')} style={{color:'var(--accent)',textDecoration:'underline',textUnderlineOffset:3}}>Sign in
);
};
// ─────────────────────────────────────────────────────────────────
// FORGOT PASSWORD
const AuthForgot = ({ goto }) => (
Forgot password?
Enter your email and we'll send a one-time reset link.
);
// ─────────────────────────────────────────────────────────────────
// SENT (post-forgot)
const AuthSent = ({ goto }) => (
✦
Check your inbox
If jane@ledgerline.com matches an account, a reset link is on its way. The link expires in 15 minutes .
NOT SEEING IT?
· Check your spam folder
· Allow up to 60 seconds
· Triple-check the address
goto('forgot')} className="btn btn-ghost" style={{width:'100%',justifyContent:'center'}}>
Try a different email
goto('login')} className="mono" style={{fontSize:11,color:'var(--ink-3)',letterSpacing:'0.06em'}}>← BACK TO SIGN IN
);
// ─────────────────────────────────────────────────────────────────
// RESET (from email link)
const AuthReset = ({ goto }) => {
const [pwd, setPwd] = React.useState('');
return (
Set a new password
Resetting password for jane@ledgerline.com
);
};
// ─────────────────────────────────────────────────────────────────
// MFA
const AuthMFA = ({ goto, setPage }) => {
const [code, setCode] = React.useState(['','','','','','']);
const refs = React.useRef([]);
const handleChange = (i, v) => {
if (!/^\d?$/.test(v)) return;
const next = [...code]; next[i] = v; setCode(next);
if (v && refs.current[i+1]) refs.current[i+1].focus();
};
return (
Two-factor code
Open your authenticator app and enter the 6-digit code for SolidAtoms .
{code.map((v,i) => (
refs.current[i] = el}
value={v}
maxLength={1}
inputMode="numeric"
onChange={(e)=>handleChange(i, e.target.value)}
style={{
width:54,height:64,textAlign:'center',
fontFamily:'var(--mono)',fontSize:24,fontWeight:500,
background:'var(--bg-3)',border:'1px solid', borderColor: v ? 'var(--accent)' : 'var(--line-2)',
borderRadius:10,color:'var(--ink)',outline:'none',
transition:'border-color .15s ease'
}}
onFocus={(e)=>e.target.style.borderColor='var(--accent)'}
onBlur={(e)=>e.target.style.borderColor= v ? 'var(--accent)' : 'var(--line-2)'}
/>
))}
◔
Code refreshes in 22s
RESEND
setPage('portal')} className="btn btn-primary" style={{width:'100%',justifyContent:'center',marginBottom:14,padding:'12px 16px'}}>
Verify & continue {Icon.arrow()}
USE BACKUP CODE INSTEAD
);
};
// ─────────────────────────────────────────────────────────────────
// VERIFY EMAIL
const AuthVerify = ({ goto }) => (
✉
Verify your email
We sent a verification link to jane@ledgerline.com . Click it to activate your account.
goto('mfa')} className="btn btn-primary" style={{width:'100%',justifyContent:'center',marginBottom:10,padding:'12px 16px'}}>
I've verified my email {Icon.arrow()}
Resend verification email
Wrong address?
goto('signup')} style={{fontSize:12,color:'var(--accent)',textDecoration:'underline',textUnderlineOffset:3}}>Change it
);
// ─────────────────────────────────────────────────────────────────
// MAGIC LINK
const AuthMagicLink = ({ goto }) => (
✦
Magic link sent
Click the link in your inbox. It expires in 10 minutes . You can close this tab — the session will open in a new one.
G
Open Gmail
jane@ledgerline.com
{Icon.arrow()}
goto('login')} className="btn btn-ghost" style={{width:'100%',justifyContent:'center'}}>
Use password instead
);
// ─────────────────────────────────────────────────────────────────
// HELPERS
const AuthField = ({ label, type='text', placeholder, rightAction, value, onChange, autoFocus, compact }) => {
const [v, setV] = React.useState('');
const val = value != null ? value : v;
const set = onChange || setV;
return (
{label}
{rightAction}
set(e.target.value)}
autoFocus={autoFocus}
style={{
width:'100%', padding:'12px 14px', borderRadius:8,
background:'var(--bg-3)', border:'1px solid var(--line-2)',
color:'var(--ink)', fontSize:14, fontFamily: type==='password' ? 'var(--mono)' : 'var(--sans)',
letterSpacing: type==='password' ? '0.08em' : 'normal',
outline:'none', transition:'border-color .15s ease'
}}
onFocus={e=>e.target.style.borderColor='var(--accent)'}
onBlur={e=>e.target.style.borderColor='var(--line-2)'}
/>
);
};
const SSOButton = ({ label, icon }) => (
{e.currentTarget.style.background='var(--bg-3)';e.currentTarget.style.borderColor='var(--ink-3)'}}
onMouseLeave={e=>{e.currentTarget.style.background='var(--bg-2)';e.currentTarget.style.borderColor='var(--line-2)'}}>
{icon}
{label}
);
const Divider = () => (
OR
);
const Checkbox = ({ label }) => {
const [on, setOn] = React.useState(false);
return (
setOn(!on)} style={{
width:16,height:16,borderRadius:4,
border:'1px solid', borderColor: on ? 'var(--accent)' : 'var(--line-2)',
background: on ? 'var(--accent)' : 'transparent',
display:'flex',alignItems:'center',justifyContent:'center',
color:'#0a0a0a',transition:'all .15s ease',flexShrink:0
}}>{on && Icon.check()}
setOn(!on)}>{label}
);
};
const PasswordStrength = ({ value }) => {
const score = Math.min(4, Math.floor((
(value.length >= 12 ? 1 : 0) +
(/[A-Z]/.test(value) ? 1 : 0) +
(/\d/.test(value) ? 1 : 0) +
(/[^a-zA-Z0-9]/.test(value) ? 1 : 0)
)));
const labels = ['','Weak','Okay','Strong','Excellent'];
const colors = ['var(--line)','var(--accent-2)','var(--warn)','#7ee787','var(--accent)'];
return (
{[1,2,3,4].map(i => (
))}
Password strength
{labels[score] || '—'}
);
};
window.Auth = Auth;