Files
vibn-agent-runner/design-templates/VIBN (2)/auth-style-a.jsx

432 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// auth-screens.jsx — Sign-in / Sign-up / Onboarding for the
// Lattice brand, in three aesthetic directions that match the
// three nav styles from the prior file:
//
// A · Light minimal ← Sidebar / Notion school
// B · Dark split-hero ← Topbar / Vercel school
// C · Glass aurora ← Floating-pill marketing school
//
// Each style ships all three screens. Shared <LatticeMark>,
// <SocialBtn>, <Field> components keep the family resemblance.
// ============================================================
// ── Shared atoms ────────────────────────────────────────────
// Branded "G" / "MS" social logos drawn inline as little glyphs
// so there are no missing-asset placeholders. They're recognizable
// without using actual brand marks.
const GoogleGlyph = () => (
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
<path fill="#4285F4" d="M21.6 12.2c0-.7-.1-1.4-.2-2H12v3.8h5.4c-.2 1.2-.9 2.2-2 2.9v2.4h3.2c1.9-1.7 3-4.3 3-7.1z"/>
<path fill="#34A853" d="M12 22c2.7 0 5-.9 6.6-2.4l-3.2-2.4c-.9.6-2 1-3.4 1-2.6 0-4.8-1.7-5.6-4.1H3.1v2.5C4.8 19.8 8.2 22 12 22z"/>
<path fill="#FBBC05" d="M6.4 14.1c-.2-.6-.3-1.3-.3-2s.1-1.4.3-2V7.6H3.1C2.4 9 2 10.4 2 12s.4 3 1.1 4.4l3.3-2.3z"/>
<path fill="#EA4335" d="M12 6c1.5 0 2.8.5 3.8 1.5l2.9-2.9C16.9 3.1 14.7 2 12 2 8.2 2 4.8 4.2 3.1 7.6l3.3 2.5C7.2 7.7 9.4 6 12 6z"/>
</svg>
);
const MicrosoftGlyph = () => (
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true">
<rect x="1" y="1" width="10" height="10" fill="#F25022"/>
<rect x="13" y="1" width="10" height="10" fill="#7FBA00"/>
<rect x="1" y="13" width="10" height="10" fill="#00A4EF"/>
<rect x="13" y="13" width="10" height="10" fill="#FFB900"/>
</svg>
);
const AppleGlyph = () => (
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M16.4 12.7c0-2.6 2.1-3.8 2.2-3.9-1.2-1.7-3-2-3.6-2-1.5-.2-3 .9-3.8.9-.8 0-2-.9-3.3-.9-1.7 0-3.3 1-4.2 2.6-1.8 3.1-.5 7.7 1.3 10.2.9 1.2 1.9 2.6 3.2 2.5 1.3-.1 1.8-.8 3.4-.8 1.6 0 2 .8 3.4.8 1.4 0 2.3-1.2 3.1-2.5.7-1 1.1-2 1.4-3-2.6-1-3.1-3.7-3.1-3.9zM13.5 5c.7-.9 1.2-2.1 1.1-3.4-1 .1-2.3.7-3 1.6-.7.8-1.3 2-1.1 3.2 1.2.1 2.3-.6 3-1.4z"/>
</svg>
);
const sansAuth = "'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif";
// Tiny stroke icon helper (re-defining locally so this file is standalone)
const Icn = ({ d, size = 16, sw = 1.6 }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none"
stroke="currentColor" strokeWidth={sw}
strokeLinecap="round" strokeLinejoin="round">{d}</svg>
);
const Pa = {
eye: <><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></>,
check: <path d="M5 12l5 5L20 7"/>,
arrow: <path d="M5 12h14M13 5l7 7-7 7"/>,
chevR: <path d="m9 6 6 6-6 6"/>,
chevL: <path d="m15 6-6 6 6 6"/>,
star: <path d="m12 3 2.6 6.2 6.7.5-5.1 4.4 1.6 6.6L12 17.3 6.2 20.7l1.6-6.6L2.7 9.7l6.7-.5z"/>,
spark: <path d="M12 3v4M12 17v4M3 12h4M17 12h4M6 6l3 3M15 15l3 3M6 18l3-3M15 9l3-3"/>,
bolt: <path d="m13 2-9 13h7l-1 7 9-13h-7z"/>,
shield: <path d="M12 2 4 5v7c0 5 3.5 9 8 10 4.5-1 8-5 8-10V5z"/>,
briefcase: <><rect x="3" y="7" width="18" height="13" rx="2"/><path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M3 13h18"/></>,
};
// Brand mark (gradient triangle), shared
const Mark = ({ size = 20, mono }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none">
{mono ? (
<path d="M3 20 L12 4 L21 20 Z" fill="currentColor"/>
) : (
<>
<defs>
<linearGradient id={`mk${size}`} x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stopColor="#6e6cff"/>
<stop offset="100%" stopColor="#b15bff"/>
</linearGradient>
</defs>
<path d="M3 20 L12 4 L21 20 Z" fill={`url(#mk${size})`}/>
</>
)}
</svg>
);
// ============================================================
// STYLE A — LIGHT MINIMAL (Notion / Linear school)
// Centered card on warm neutral, no flourish, lots of air.
// ============================================================
const a = {
bg: "#f5f5f2", surface: "#ffffff",
border: "#e8e8e3", borderStrong: "#d8d8d2",
text: "#111", subtext: "#5a5a5e", muted: "#8a8a90",
accent: "#5e5cff", accentText: "#fff",
};
const fontA = sansAuth;
const AFieldLabel = ({ children, optional }) => (
<div style={{
display: "flex", justifyContent: "space-between", alignItems: "baseline",
fontSize: 12, fontWeight: 500, color: a.text, marginBottom: 6,
}}>
<span>{children}</span>
{optional && <span style={{ color: a.muted, fontWeight: 400 }}>optional</span>}
</div>
);
const AField = ({ label, value, placeholder, hint, type = "text", icon, optional }) => (
<div style={{ marginBottom: 14 }}>
{label && <AFieldLabel optional={optional}>{label}</AFieldLabel>}
<div style={{
display: "flex", alignItems: "center", gap: 8,
padding: "10px 12px", borderRadius: 7,
background: "#fff", border: `1px solid ${a.border}`,
fontSize: 13, color: value ? a.text : a.muted,
boxShadow: "0 1px 0 #00000004",
}}>
{icon && <span style={{ color: a.muted, display: "flex" }}>{icon}</span>}
<span style={{ flex: 1 }}>{value || placeholder}</span>
{type === "password" && <span style={{ color: a.muted, display: "flex" }}>
<Icn d={Pa.eye} size={14} /></span>}
</div>
{hint && <div style={{ fontSize: 11, color: a.muted, marginTop: 5 }}>{hint}</div>}
</div>
);
const ASocial = ({ children, glyph }) => (
<button style={{
flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 8,
padding: "10px 12px", borderRadius: 7, background: "#fff",
border: `1px solid ${a.border}`, color: a.text, fontSize: 13,
fontFamily: fontA, fontWeight: 500, cursor: "pointer",
}}>
{glyph}
<span>{children}</span>
</button>
);
const APrimary = ({ children, full = true }) => (
<button style={{
width: full ? "100%" : "auto",
padding: "11px 18px", borderRadius: 7,
background: "#111", color: "#fff", border: "none",
fontSize: 13, fontWeight: 500, fontFamily: fontA, cursor: "pointer",
display: "flex", alignItems: "center", justifyContent: "center", gap: 6,
}}>{children}</button>
);
const ACardShell = ({ children, foot }) => (
<div style={{
width: "100%", height: "100%", background: a.bg,
color: a.text, fontFamily: fontA,
display: "grid", gridTemplateRows: "auto 1fr auto",
}}>
{/* Top bar: brand on left, support on right */}
<header style={{
display: "flex", justifyContent: "space-between", alignItems: "center",
padding: "20px 28px",
}}>
<div style={{ display: "flex", alignItems: "center", gap: 8, fontWeight: 600, fontSize: 14 }}>
<Mark size={20} />
Lattice
</div>
<div style={{ fontSize: 12, color: a.subtext, display: "flex", gap: 18 }}>
<span>Status</span>
<span>Docs</span>
<span>Sign in </span>
</div>
</header>
{/* Centered card */}
<main style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }}>
<div style={{
width: 420, padding: "32px 36px", borderRadius: 12,
background: a.surface, border: `1px solid ${a.border}`,
boxShadow: "0 1px 2px #0000000a, 0 8px 32px -12px #0000000f",
}}>
{children}
</div>
</main>
{/* Footer band */}
<footer style={{
display: "flex", justifyContent: "space-between", alignItems: "center",
padding: "16px 28px", fontSize: 11, color: a.muted,
}}>
<span>© 2026 Lattice Studio · Made in Copenhagen</span>
<div style={{ display: "flex", gap: 16 }}>
<span>Privacy</span><span>Terms</span><span>Security</span>
</div>
</footer>
</div>
);
const ASignIn = () => (
<ACardShell>
<h1 style={{ fontSize: 22, fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }}>
Welcome back
</h1>
<p style={{ fontSize: 13, color: a.subtext, margin: "6px 0 22px" }}>
Sign in to your Lattice workspace.
</p>
<div style={{ display: "flex", gap: 8, marginBottom: 18 }}>
<ASocial glyph={<GoogleGlyph/>}>Google</ASocial>
<ASocial glyph={<MicrosoftGlyph/>}>Microsoft</ASocial>
<ASocial glyph={<span style={{ color: a.text, display: "flex" }}><AppleGlyph/></span>}>Apple</ASocial>
</div>
<div style={{
display: "flex", alignItems: "center", gap: 10,
fontSize: 11, color: a.muted, margin: "0 0 18px",
}}>
<div style={{ flex: 1, height: 1, background: a.border }}></div>
<span style={{ textTransform: "uppercase", letterSpacing: "0.08em" }}>or with email</span>
<div style={{ flex: 1, height: 1, background: a.border }}></div>
</div>
<AField label="Email" value="mira@lattice.co" />
<div style={{ marginBottom: 14 }}>
<div style={{
display: "flex", justifyContent: "space-between", alignItems: "baseline",
fontSize: 12, fontWeight: 500, color: a.text, marginBottom: 6,
}}>
<span>Password</span>
<span style={{ color: a.accent, cursor: "pointer", fontWeight: 400 }}>Forgot?</span>
</div>
<div style={{
display: "flex", alignItems: "center", gap: 8,
padding: "10px 12px", borderRadius: 7, background: "#fff",
border: `1px solid ${a.border}`, fontSize: 13, color: a.text,
letterSpacing: "0.2em",
}}>
<span style={{ flex: 1 }}></span>
<span style={{ color: a.muted, display: "flex" }}><Icn d={Pa.eye} size={14}/></span>
</div>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 18 }}>
<div style={{
width: 14, height: 14, borderRadius: 3, background: "#111",
display: "flex", alignItems: "center", justifyContent: "center",
color: "#fff",
}}><Icn d={Pa.check} size={10} sw={2.4}/></div>
<span style={{ fontSize: 12, color: a.subtext }}>Keep me signed in for 30 days</span>
</div>
<APrimary>Sign in </APrimary>
<div style={{ fontSize: 12, color: a.subtext, marginTop: 18, textAlign: "center" }}>
New here? <span style={{ color: a.text, fontWeight: 500, cursor: "pointer" }}>
Create an account
</span>
</div>
</ACardShell>
);
const ASignUp = () => (
<ACardShell>
<h1 style={{ fontSize: 22, fontWeight: 600, margin: 0, letterSpacing: "-0.01em" }}>
Create your workspace
</h1>
<p style={{ fontSize: 13, color: a.subtext, margin: "6px 0 22px" }}>
Free for up to 10 people. No card needed.
</p>
<div style={{ display: "flex", gap: 8, marginBottom: 18 }}>
<ASocial glyph={<GoogleGlyph/>}>Continue with Google</ASocial>
<ASocial glyph={<MicrosoftGlyph/>}>Microsoft</ASocial>
</div>
<div style={{
display: "flex", alignItems: "center", gap: 10,
fontSize: 11, color: a.muted, margin: "0 0 18px",
}}>
<div style={{ flex: 1, height: 1, background: a.border }}></div>
<span style={{ textTransform: "uppercase", letterSpacing: "0.08em" }}>or with email</span>
<div style={{ flex: 1, height: 1, background: a.border }}></div>
</div>
<AField label="Full name" placeholder="Mira Reyes" />
<AField label="Work email" value="mira@lattice.co"
hint="We'll send a 6-digit code to confirm." />
<AField label="Password" value="••••••••••" type="password"
hint="At least 10 characters, including a number." />
<div style={{ display: "flex", alignItems: "flex-start", gap: 8, margin: "4px 0 18px" }}>
<div style={{
width: 14, height: 14, borderRadius: 3, marginTop: 2,
background: "#fff", border: `1px solid ${a.borderStrong}`,
}}></div>
<span style={{ fontSize: 12, color: a.subtext, lineHeight: 1.5 }}>
I agree to Lattice's <span style={{ color: a.text, fontWeight: 500 }}>Terms</span> and{" "}
<span style={{ color: a.text, fontWeight: 500 }}>Privacy Policy</span>.
</span>
</div>
<APrimary>Create workspace →</APrimary>
<div style={{ fontSize: 12, color: a.subtext, marginTop: 18, textAlign: "center" }}>
Already have one? <span style={{ color: a.text, fontWeight: 500, cursor: "pointer" }}>
Sign in
</span>
</div>
</ACardShell>
);
const AOnboarding = () => {
const Step = ({ n, label, state }) => (
<div style={{ display: "flex", alignItems: "center", gap: 8, flex: 1, minWidth: 0 }}>
<div style={{
width: 22, height: 22, borderRadius: "50%",
background: state === "done" ? "#22c55e" : state === "active" ? "#111" : "transparent",
color: state === "todo" ? a.muted : "#fff",
border: state === "todo" ? `1px solid ${a.borderStrong}` : "none",
display: "flex", alignItems: "center", justifyContent: "center",
fontSize: 11, fontWeight: 600, flexShrink: 0,
}}>{state === "done" ? <Icn d={Pa.check} size={12} sw={2.4} /> : n}</div>
<div style={{ fontSize: 12, color: state === "todo" ? a.muted : a.text,
whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</div>
</div>
);
const Tile = ({ title, sub, selected, icon }) => (
<div style={{
padding: 14, borderRadius: 8, cursor: "pointer", textAlign: "left",
border: selected ? `1.5px solid ${a.accent}` : `1px solid ${a.border}`,
background: selected ? "#f6f5ff" : "#fff",
boxShadow: selected ? `0 0 0 3px ${a.accent}1a` : "0 1px 0 #00000004",
}}>
<div style={{
width: 28, height: 28, borderRadius: 7, marginBottom: 10,
background: selected ? a.accent : "#f1f0eb",
color: selected ? "#fff" : a.subtext,
display: "flex", alignItems: "center", justifyContent: "center",
}}>{icon}</div>
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 2 }}>{title}</div>
<div style={{ fontSize: 11, color: a.muted, lineHeight: 1.4 }}>{sub}</div>
</div>
);
return (
<div style={{
width: "100%", height: "100%", background: a.bg, color: a.text,
fontFamily: fontA, display: "grid", gridTemplateRows: "auto 1fr auto",
}}>
<header style={{
display: "flex", justifyContent: "space-between", alignItems: "center",
padding: "20px 28px",
}}>
<div style={{ display: "flex", alignItems: "center", gap: 8, fontWeight: 600, fontSize: 14 }}>
<Mark size={20} /> Lattice
</div>
<div style={{ fontSize: 12, color: a.subtext }}>Step 2 of 4 · ⌘. to skip</div>
</header>
<main style={{
padding: "12px 28px 28px",
display: "flex", flexDirection: "column", alignItems: "center",
}}>
<div style={{
width: 640, padding: "30px 36px 36px", borderRadius: 14,
background: a.surface, border: `1px solid ${a.border}`,
boxShadow: "0 1px 2px #0000000a, 0 8px 32px -12px #0000000f",
}}>
{/* Stepper */}
<div style={{ display: "flex", alignItems: "center", gap: 4, marginBottom: 22 }}>
<Step n="1" label="Account" state="done" />
<div style={{ flex: 1, height: 1, background: a.border, margin: "0 6px" }}></div>
<Step n="2" label="Workspace" state="active" />
<div style={{ flex: 1, height: 1, background: a.border, margin: "0 6px" }}></div>
<Step n="3" label="Invite team" state="todo" />
<div style={{ flex: 1, height: 1, background: a.border, margin: "0 6px" }}></div>
<Step n="4" label="Import" state="todo" />
</div>
<h1 style={{ fontSize: 24, fontWeight: 600, margin: 0, letterSpacing: "-0.02em" }}>
Tell us about your work
</h1>
<p style={{ fontSize: 13, color: a.subtext, margin: "6px 0 22px" }}>
We'll tailor your workspace based on this. You can change it later.
</p>
<AField label="Workspace name" value="Lattice Studio"
hint="This is how your team will see it." />
<div style={{ fontSize: 12, fontWeight: 500, margin: "16px 0 8px" }}>
What do you do?
</div>
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10 }}>
<Tile title="Sales & Revenue" sub="Pipeline, contacts, deals" selected icon={<Icn d={Pa.bolt} size={15}/>} />
<Tile title="Operations" sub="Vendors, ops, suppliers" icon={<Icn d={Pa.shield} size={15}/>} />
<Tile title="Product" sub="Customers, feedback, research" icon={<Icn d={Pa.spark} size={15}/>} />
<Tile title="Recruiting" sub="Candidates, pipeline" icon={<Icn d={Pa.briefcase} size={15}/>} />
<Tile title="Just exploring" sub="I'll figure it out" icon={<Icn d={Pa.star} size={15}/>} />
</div>
<div style={{ fontSize: 12, fontWeight: 500, margin: "20px 0 8px" }}>How big is your team?</div>
<div style={{ display: "flex", gap: 6 }}>
{["Just me", "210", "1150", "51200", "200+"].map((s, i) => (
<div key={s} style={{
flex: 1, padding: "9px 8px", textAlign: "center", borderRadius: 7,
fontSize: 12, fontWeight: 500, cursor: "pointer",
border: i === 1 ? `1.5px solid ${a.accent}` : `1px solid ${a.border}`,
background: i === 1 ? "#f6f5ff" : "#fff",
color: i === 1 ? a.accent : a.subtext,
}}>{s}</div>
))}
</div>
<div style={{
display: "flex", justifyContent: "space-between", marginTop: 26, alignItems: "center",
}}>
<button style={{
background: "transparent", border: "none", color: a.subtext,
fontSize: 13, fontFamily: fontA, cursor: "pointer", padding: 0,
}}> Back</button>
<APrimary full={false}>Continue <Icn d={Pa.arrow} size={13}/></APrimary>
</div>
</div>
</main>
<footer style={{
display: "flex", justifyContent: "space-between", alignItems: "center",
padding: "16px 28px", fontSize: 11, color: a.muted,
}}>
<span>Press <code style={{
background: "#fff", padding: "1px 5px", borderRadius: 3,
border: `1px solid ${a.border}`, fontFamily: "monospace",
}}> + Enter</code> to continue</span>
<span>Need help? <span style={{ color: a.text, fontWeight: 500 }}>support@lattice.co</span></span>
</footer>
</div>
);
};
Object.assign(window, { ASignIn, ASignUp, AOnboarding });