2561 lines
69 KiB
TypeScript
2561 lines
69 KiB
TypeScript
import React, {
|
||
useState,
|
||
useEffect,
|
||
useRef,
|
||
useMemo,
|
||
useCallback,
|
||
} from "react";
|
||
import {
|
||
WizardTop,
|
||
WizardBody,
|
||
WizardQ,
|
||
WizardFooter,
|
||
LANE_LABELS,
|
||
ChipGroup,
|
||
PresetGroup,
|
||
Field,
|
||
} from "./onboarding-primitives";
|
||
// Entrepreneur path — 4 steps. Each step is a focused question.
|
||
|
||
const ENTREP_TOTAL = 4;
|
||
const ENTREP_STEP_NAMES = ["Type", "Style", "Idea", "Look"];
|
||
|
||
const IDEA_PROMPTS = [
|
||
"A community for indie game devs to swap playtesters, with weekly demo nights",
|
||
"An AI tool that turns my handwritten recipe notes into a clean cookbook for my family",
|
||
"A waitlist + scheduler for my pottery studio — small classes, six people max",
|
||
"A subscription box service for cold-brew enthusiasts, with monthly tasting cards",
|
||
"A simple tool that turns my Strava data into framed art prints I can sell",
|
||
];
|
||
|
||
export function EntrepIdea({ value, onChange }) {
|
||
const [phIdx, setPhIdx] = React.useState(0);
|
||
const [phChars, setPhChars] = React.useState(0);
|
||
const [deleting, setDeleting] = React.useState(false);
|
||
|
||
React.useEffect(() => {
|
||
if (value.length > 0) return undefined;
|
||
const full = IDEA_PROMPTS[phIdx];
|
||
const speed = deleting ? 18 : 38;
|
||
const t = setTimeout(() => {
|
||
if (!deleting) {
|
||
if (phChars < full.length) setPhChars(phChars + 1);
|
||
else setTimeout(() => setDeleting(true), 1500);
|
||
} else {
|
||
if (phChars > 0) setPhChars(phChars - 1);
|
||
else {
|
||
setDeleting(false);
|
||
setPhIdx((phIdx + 1) % IDEA_PROMPTS.length);
|
||
}
|
||
}
|
||
}, speed);
|
||
return () => clearTimeout(t);
|
||
}, [value, phIdx, phChars, deleting]);
|
||
|
||
return (
|
||
<>
|
||
<WizardQ
|
||
title="What are you building?"
|
||
sub="Don't worry if it's not crisp yet — just dump your thoughts. Talk like you would to a friend."
|
||
/>
|
||
<div style={{ position: "relative" }}>
|
||
<textarea
|
||
className="wiz-input"
|
||
style={{ minHeight: 200, fontSize: 15 }}
|
||
value={value}
|
||
onChange={(e) => onChange(e.target.value)}
|
||
autoFocus
|
||
aria-label="Describe your idea"
|
||
/>
|
||
{value.length === 0 && (
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
top: 12,
|
||
left: 14,
|
||
right: 14,
|
||
pointerEvents: "none",
|
||
color: "var(--fg-faint)",
|
||
font: "14.5px/1.5 var(--font-sans)",
|
||
}}
|
||
>
|
||
{IDEA_PROMPTS[phIdx].slice(0, phChars)}
|
||
<span
|
||
style={{
|
||
display: "inline-block",
|
||
width: 7,
|
||
height: 14,
|
||
verticalAlign: "-2px",
|
||
background: "var(--accent)",
|
||
marginLeft: 1,
|
||
animation: "blink 1s steps(2) infinite",
|
||
boxShadow: "0 0 10px var(--accent-glow)",
|
||
}}
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div
|
||
className="mono"
|
||
style={{
|
||
fontSize: 11,
|
||
color: "var(--fg-faint)",
|
||
letterSpacing: "0.06em",
|
||
marginTop: -16,
|
||
}}
|
||
>
|
||
{value.length} chars · be specific where it matters
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
const ARCHETYPES = [
|
||
{
|
||
id: "saas",
|
||
label: "Web App / SaaS",
|
||
desc: "Dashboards, tools, interactive portals",
|
||
},
|
||
{
|
||
id: "marketplace",
|
||
label: "Marketplace",
|
||
desc: "Directories, bookings, listings",
|
||
},
|
||
{
|
||
id: "marketing",
|
||
label: "Marketing Site",
|
||
desc: "Portfolios, lead capture, landing pages",
|
||
},
|
||
{
|
||
id: "ecommerce",
|
||
label: "Online Store",
|
||
desc: "Carts, checkouts, selling physical/digital goods",
|
||
},
|
||
{
|
||
id: "mobile",
|
||
label: "Mobile App",
|
||
desc: "iOS and Android mobile applications",
|
||
},
|
||
{
|
||
id: "blog",
|
||
label: "Blog / Publication",
|
||
desc: "Newsletters, articles, content hubs",
|
||
},
|
||
{
|
||
id: "not_sure",
|
||
label: "I'm not sure",
|
||
desc: "Let Vibn help you decide based on your description",
|
||
fullWidth: true,
|
||
},
|
||
];
|
||
|
||
function EntrepType({ value, onChange }) {
|
||
return (
|
||
<>
|
||
<WizardQ
|
||
title="What kind of product is it?"
|
||
sub="Helps Vibn set up the right database, integrations, and starting code."
|
||
/>
|
||
<PresetGroup
|
||
options={ARCHETYPES.map((a) => ({
|
||
id: a.id,
|
||
label: a.label,
|
||
desc: a.desc,
|
||
icon: undefined,
|
||
fullWidth: a.fullWidth,
|
||
}))}
|
||
value={value}
|
||
onChange={onChange}
|
||
columns={2}
|
||
/>
|
||
</>
|
||
);
|
||
}
|
||
|
||
function LayoutPreview({ styleId }: { styleId: string }) {
|
||
// Ultra-high-fidelity CSS-rendered miniatures representing your actual page-dashboard.jsx designs!
|
||
const isDark =
|
||
styleId === "sidebar_dark" ||
|
||
styleId === "topbar_dark" ||
|
||
styleId === "rail";
|
||
const accentColor = "var(--accent)";
|
||
|
||
// Theme palettes from page-dashboard.jsx
|
||
const c = isDark
|
||
? {
|
||
bg: "#0e0f12",
|
||
panel: "#16171d",
|
||
border: "rgba(255,255,255,0.06)",
|
||
text: "#f5f4f2",
|
||
subtext: "#a09fa6",
|
||
muted: "#686972",
|
||
accent: "var(--accent)",
|
||
up: "#22c55e",
|
||
down: "#ff4d5e",
|
||
}
|
||
: {
|
||
bg: "#fafaf9",
|
||
panel: "#ffffff",
|
||
border: "#ebebe6",
|
||
text: "#1a1a1a",
|
||
subtext: "#5a5a5e",
|
||
muted: "#a09a90",
|
||
accent: "var(--accent)",
|
||
up: "#2e7d32",
|
||
down: "#d32f2f",
|
||
};
|
||
|
||
const wireLine = (w: string | number, opacity = 0.25) => (
|
||
<div
|
||
style={{
|
||
height: 2.5,
|
||
width: w,
|
||
borderRadius: 1.5,
|
||
background: isDark ? "#ffffff" : "#000000",
|
||
opacity,
|
||
}}
|
||
/>
|
||
);
|
||
|
||
// High-fidelity Avatars matching your screenshots (MR, TR, DP, SK)
|
||
const MiniAvatar = ({
|
||
text: label,
|
||
color,
|
||
}: {
|
||
text: string;
|
||
color: string;
|
||
}) => (
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: "50%",
|
||
background: color,
|
||
color: "#3a2210",
|
||
fontSize: 4.5,
|
||
fontWeight: 700,
|
||
display: "grid",
|
||
placeItems: "center",
|
||
flexShrink: 0,
|
||
}}
|
||
>
|
||
{label}
|
||
</div>
|
||
);
|
||
|
||
// Common high-fidelity dashboard body matching page-dashboard.jsx
|
||
const renderDashboardMockup = () => (
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: "12px 14px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 8,
|
||
background: c.bg,
|
||
color: c.text,
|
||
overflow: "hidden",
|
||
filter: "blur(3.5px)",
|
||
pointerEvents: "none",
|
||
}}
|
||
>
|
||
{/* Header */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "flex-end",
|
||
borderBottom: `1px solid ${c.border}`,
|
||
paddingBottom: 6,
|
||
width: "100%",
|
||
}}
|
||
>
|
||
<div>
|
||
<span
|
||
style={{
|
||
fontSize: 5,
|
||
textTransform: "uppercase",
|
||
color: c.muted,
|
||
letterSpacing: "0.04em",
|
||
display: "block",
|
||
marginBottom: 1,
|
||
fontWeight: 600,
|
||
}}
|
||
>
|
||
Workspace dashboard
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 11.5,
|
||
fontWeight: 700,
|
||
letterSpacing: "-0.01em",
|
||
display: "block",
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
Good afternoon, Mira
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 6,
|
||
color: c.subtext,
|
||
display: "block",
|
||
marginTop: 2,
|
||
}}
|
||
>
|
||
3 deals moved stage today · 12 unread in Inbox · 1 task overdue
|
||
</span>
|
||
</div>
|
||
<div style={{ display: "flex", gap: 4, paddingBottom: 1 }}>
|
||
<div
|
||
style={{
|
||
width: 38,
|
||
height: 11,
|
||
borderRadius: 3,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
paddingLeft: 3,
|
||
}}
|
||
>
|
||
<div
|
||
style={{ height: 2, width: 22, background: c.text, opacity: 0.6 }}
|
||
/>
|
||
</div>
|
||
<div
|
||
style={{
|
||
width: 22,
|
||
height: 11,
|
||
borderRadius: 3,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
width: 28,
|
||
height: 11,
|
||
borderRadius: 3,
|
||
background: isDark ? "#ffffff" : "#1a1a1a",
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* KPI Strip */}
|
||
<div
|
||
style={{
|
||
display: "grid",
|
||
gridTemplateColumns: "repeat(4, 1fr)",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
{[
|
||
{ l: "Revenue · MTD", v: "€286,420", d: "+18.4%", up: true },
|
||
{ l: "Active deals", v: "168", d: "+12", up: true },
|
||
{ l: "Win rate · 30d", v: "34.2%", d: "−1.1%", up: false },
|
||
{ l: "Pipeline ratio", v: "4.8×", d: "healthy", up: true },
|
||
].map((k, i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
padding: "4px 5px",
|
||
borderRadius: 4,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "baseline",
|
||
marginBottom: 2,
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 4, color: c.muted, fontWeight: 500 }}>
|
||
{k.l}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: k.up
|
||
? c.up
|
||
: k.d.startsWith("−") || k.d.startsWith("-")
|
||
? c.down
|
||
: c.up,
|
||
fontWeight: 600,
|
||
}}
|
||
>
|
||
{k.d}
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontSize: 8,
|
||
fontWeight: 700,
|
||
letterSpacing: "-0.01em",
|
||
fontVariantNumeric: "tabular-nums",
|
||
}}
|
||
>
|
||
{k.v}
|
||
</div>
|
||
<svg
|
||
viewBox="0 0 100 20"
|
||
style={{
|
||
width: "100%",
|
||
height: 5,
|
||
display: "block",
|
||
marginTop: 2,
|
||
}}
|
||
preserveAspectRatio="none"
|
||
>
|
||
<path
|
||
d={
|
||
k.up
|
||
? "M0,15 L20,12 L40,14 L60,8 L80,10 L100,2"
|
||
: "M0,2 L20,5 L40,4 L60,12 L80,14 L100,18"
|
||
}
|
||
fill="none"
|
||
stroke={k.up ? c.up : c.down}
|
||
strokeWidth="2"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Main Grid: charts, activity and leaderboards */}
|
||
<div
|
||
style={{
|
||
display: "grid",
|
||
gridTemplateColumns: "1.4fr 1fr",
|
||
gap: 5,
|
||
flex: 1,
|
||
minHeight: 0,
|
||
}}
|
||
>
|
||
{/* Left column: Daily Revenue Chart + Recent Activity */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||
{/* Revenue Daily Chart */}
|
||
<div
|
||
style={{
|
||
padding: 5,
|
||
borderRadius: 5,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 4.5, color: c.muted, fontWeight: 600 }}>
|
||
Revenue, daily
|
||
</span>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 1.5,
|
||
alignItems: "flex-end",
|
||
height: 16,
|
||
}}
|
||
>
|
||
{[10, 16, 22, 18, 25, 8, 6, 14, 20, 26, 22, 28, 12, 10].map(
|
||
(h, idx) => (
|
||
<div
|
||
key={idx}
|
||
style={{
|
||
flex: 1,
|
||
height: `${(h / 30) * 100}%`,
|
||
background:
|
||
idx === 11
|
||
? c.accent
|
||
: isDark
|
||
? "rgba(255,255,255,0.08)"
|
||
: "rgba(0,0,0,0.06)",
|
||
borderRadius: 0.5,
|
||
}}
|
||
/>
|
||
),
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Recent Activity Card */}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: 5,
|
||
borderRadius: 5,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
overflow: "hidden",
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 4.5, color: c.muted, fontWeight: 600 }}>
|
||
Recent activity
|
||
</span>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2.5 }}>
|
||
{[
|
||
{
|
||
av: "MR",
|
||
col: "#d4b8a8",
|
||
name: "Mira Reyes",
|
||
action: "moved Q3 to ",
|
||
highlight: "Negotiation",
|
||
hlCol: c.accent,
|
||
},
|
||
{
|
||
av: "TR",
|
||
col: "#c2d3a8",
|
||
name: "Theo Roux",
|
||
action: "logged call with ",
|
||
highlight: "Sun Kim",
|
||
hlCol: c.text,
|
||
},
|
||
{
|
||
av: "DP",
|
||
col: "#b8d4e8",
|
||
name: "Devi Patel",
|
||
action: "closed ",
|
||
highlight: "Halcyon",
|
||
hlCol: c.text,
|
||
},
|
||
].map((act, i) => (
|
||
<div
|
||
key={i}
|
||
style={{ display: "flex", gap: 4, alignItems: "center" }}
|
||
>
|
||
<MiniAvatar text={act.av} color={act.col} />
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 0.5,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.text,
|
||
fontWeight: 500,
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
{act.name}
|
||
</div>
|
||
<div
|
||
style={{ fontSize: 3, color: c.subtext, lineHeight: 1 }}
|
||
>
|
||
{act.action}
|
||
<span style={{ color: act.hlCol, fontWeight: 600 }}>
|
||
{act.highlight}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Right column: Pipeline Funnel + Team leaderboard */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
||
{/* Pipeline Funnel */}
|
||
<div
|
||
style={{
|
||
padding: 5,
|
||
borderRadius: 5,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 4.5, color: c.muted, fontWeight: 600 }}>
|
||
Pipeline funnel
|
||
</span>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
|
||
{[
|
||
{ s: "New", w: "100%", col: "#5e5cff" },
|
||
{ s: "Qual", w: "80%", col: "#6d5cff" },
|
||
{ s: "Prop", w: "60%", col: "#7c5cff" },
|
||
{ s: "Nego", w: "40%", col: "#8c5cff" },
|
||
{ s: "Won", w: "20%", col: "#22c55e" },
|
||
].map((f, i) => (
|
||
<div
|
||
key={i}
|
||
style={{ display: "flex", alignItems: "center", gap: 4 }}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
width: 12,
|
||
textAlign: "right",
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
{f.s}
|
||
</span>
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
height: 3,
|
||
background: isDark
|
||
? "rgba(255,255,255,0.03)"
|
||
: "rgba(0,0,0,0.03)",
|
||
borderRadius: 1,
|
||
overflow: "hidden",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: f.w,
|
||
height: "100%",
|
||
background: f.col,
|
||
borderRadius: 1,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Team Leaders Card */}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: 5,
|
||
borderRadius: 5,
|
||
background: c.panel,
|
||
border: `1px solid ${c.border}`,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
overflow: "hidden",
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 4.5, color: c.muted, fontWeight: 600 }}>
|
||
Team · this month
|
||
</span>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
justifyContent: "center",
|
||
flex: 1,
|
||
}}
|
||
>
|
||
{[
|
||
{ av: "MR", col: "#d4b8a8", name: "Mira Reyes", val: "€124k" },
|
||
{ av: "DP", col: "#b8d4e8", name: "Devi Patel", val: "€86k" },
|
||
{ av: "TR", col: "#c2d3a8", name: "Theo Roux", val: "€62k" },
|
||
].map((team, i) => (
|
||
<div
|
||
key={i}
|
||
style={{ display: "flex", alignItems: "center", gap: 4 }}
|
||
>
|
||
<MiniAvatar text={team.av} color={team.col} />
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 1,
|
||
flex: 1,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "baseline",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.text,
|
||
fontWeight: 500,
|
||
}}
|
||
>
|
||
{team.name}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
fontWeight: 600,
|
||
}}
|
||
>
|
||
{team.val}
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: 2,
|
||
background: isDark
|
||
? "rgba(255,255,255,0.03)"
|
||
: "rgba(0,0,0,0.03)",
|
||
borderRadius: 1,
|
||
overflow: "hidden",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: i === 0 ? "80%" : i === 1 ? "55%" : "40%",
|
||
height: "100%",
|
||
background: c.accent,
|
||
opacity: 0.8,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
|
||
let content;
|
||
if (styleId === "sidebar_light") {
|
||
// Left-side menu column + main dashboard area — Light/Minimal CRM theme (Screen 3)
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#fcfbfa",
|
||
}}
|
||
>
|
||
{/* Sidebar */}
|
||
<div
|
||
style={{
|
||
width: 52,
|
||
borderRight: "1px solid #eae6de",
|
||
padding: "8px 6px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 6,
|
||
background: "#f5f4ef",
|
||
}}
|
||
>
|
||
{/* Logo / Workspace Selector */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
borderBottom: "1px solid #eae6de",
|
||
paddingBottom: 4,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: 2.5,
|
||
background: accentColor,
|
||
opacity: 0.9,
|
||
}}
|
||
/>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 1 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 4.5,
|
||
fontWeight: 700,
|
||
color: "#1a1a1a",
|
||
letterSpacing: "-0.01em",
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
Lattice Studio
|
||
</span>
|
||
<span
|
||
style={{ fontSize: 3, color: "var(--fg-mute)", lineHeight: 1 }}
|
||
>
|
||
Free · 4 members
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{/* Mini Search box */}
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: 8,
|
||
borderRadius: 2,
|
||
border: "1px solid #eae6de",
|
||
background: "#fff",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
paddingLeft: 3,
|
||
}}
|
||
>
|
||
{wireLine(16, 0.15)}
|
||
</div>
|
||
{/* Menu Sections (VIEWS, TOOLS, ADMIN) */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
marginTop: 2,
|
||
}}
|
||
>
|
||
{/* Standard Links */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
<div style={{ display: "flex", gap: 3, alignItems: "center" }}>
|
||
<div
|
||
style={{
|
||
width: 4,
|
||
height: 4,
|
||
borderRadius: "50%",
|
||
background: accentColor,
|
||
}}
|
||
/>
|
||
{wireLine(12, 0.55)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(14, 0.35)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(16, 0.35)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Views */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
fontWeight: 600,
|
||
letterSpacing: "0.04em",
|
||
textTransform: "uppercase",
|
||
}}
|
||
>
|
||
VIEWS
|
||
</span>
|
||
{["Companies", "People", "Opportunities"].map((v) => (
|
||
<div
|
||
key={v}
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(24, 0.35)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Tools */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
fontWeight: 600,
|
||
letterSpacing: "0.04em",
|
||
textTransform: "uppercase",
|
||
}}
|
||
>
|
||
TOOLS
|
||
</span>
|
||
{["Insights", "Automations", "Docs"].map((v) => (
|
||
<div
|
||
key={v}
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(20, 0.35)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* Main Dashboard (Light) */}
|
||
{renderDashboardMockup()}
|
||
</div>
|
||
);
|
||
} else if (styleId === "sidebar_dark") {
|
||
// Left-side menu column + main dashboard area — Full Dark theme (Screen 3 Dark)
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#0e0f12",
|
||
}}
|
||
>
|
||
{/* Sidebar */}
|
||
<div
|
||
style={{
|
||
width: 52,
|
||
borderRight: "1px solid rgba(255,255,255,0.08)",
|
||
padding: "8px 6px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 6,
|
||
background: "#16171d",
|
||
}}
|
||
>
|
||
{/* Logo / Workspace Selector */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
||
paddingBottom: 4,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: 2.5,
|
||
background: accentColor,
|
||
opacity: 0.9,
|
||
}}
|
||
/>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 1 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 4.5,
|
||
fontWeight: 700,
|
||
color: "#fff",
|
||
letterSpacing: "-0.01em",
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
Lattice Studio
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 3,
|
||
color: "rgba(255,255,255,0.4)",
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
Free · 4 members
|
||
</span>
|
||
</div>
|
||
</div>
|
||
{/* Mini Search box */}
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: 8,
|
||
borderRadius: 2,
|
||
border: "1px solid rgba(255,255,255,0.08)",
|
||
background: "rgba(255,255,255,0.05)",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
paddingLeft: 3,
|
||
}}
|
||
>
|
||
{wireLine(16, 0.15)}
|
||
</div>
|
||
{/* Menu Sections (VIEWS, TOOLS, ADMIN) */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
marginTop: 2,
|
||
}}
|
||
>
|
||
{/* Standard Links */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
<div style={{ display: "flex", gap: 3, alignItems: "center" }}>
|
||
<div
|
||
style={{
|
||
width: 4,
|
||
height: 4,
|
||
borderRadius: "50%",
|
||
background: accentColor,
|
||
}}
|
||
/>
|
||
{wireLine(12, 0.55)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(14, 0.35)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(16, 0.35)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Views */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
fontWeight: 600,
|
||
letterSpacing: "0.04em",
|
||
textTransform: "uppercase",
|
||
}}
|
||
>
|
||
VIEWS
|
||
</span>
|
||
{["Companies", "People", "Opportunities"].map((v) => (
|
||
<div
|
||
key={v}
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(24, 0.35)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Tools */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
fontWeight: 600,
|
||
letterSpacing: "0.04em",
|
||
textTransform: "uppercase",
|
||
}}
|
||
>
|
||
TOOLS
|
||
</span>
|
||
{["Insights", "Automations", "Docs"].map((v) => (
|
||
<div
|
||
key={v}
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(20, 0.35)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* Main Dashboard (Dark) */}
|
||
{renderDashboardMockup()}
|
||
</div>
|
||
);
|
||
} else if (styleId === "topbar_light") {
|
||
// Top Horizontal Dark Header Bar + Light Dashboard (Screen 2)
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#fcfbfa",
|
||
}}
|
||
>
|
||
{/* Top Dark Header */}
|
||
<div
|
||
style={{
|
||
height: 20,
|
||
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
||
padding: "0 10px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
background: "#121110",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
|
||
<div
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: 2,
|
||
background: accentColor,
|
||
opacity: 0.9,
|
||
}}
|
||
/>
|
||
<span
|
||
style={{
|
||
fontSize: 5.5,
|
||
fontWeight: 700,
|
||
color: "#fff",
|
||
fontFamily: "var(--font-sans)",
|
||
letterSpacing: "-0.01em",
|
||
}}
|
||
>
|
||
Lattice
|
||
</span>
|
||
<span style={{ fontSize: 4.5, color: "rgba(255,255,255,0.3)" }}>
|
||
/
|
||
</span>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||
<div
|
||
style={{
|
||
width: 6,
|
||
height: 6,
|
||
borderRadius: "50%",
|
||
background: "#d4b8a8",
|
||
}}
|
||
/>
|
||
<span
|
||
style={{
|
||
fontSize: 4.5,
|
||
color: "rgba(255,255,255,0.6)",
|
||
fontWeight: 500,
|
||
}}
|
||
>
|
||
mira-reyes
|
||
</span>
|
||
</div>
|
||
<span style={{ fontSize: 4.5, color: "rgba(255,255,255,0.3)" }}>
|
||
/
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 4.5,
|
||
color: "rgba(255,255,255,0.8)",
|
||
fontWeight: 500,
|
||
}}
|
||
>
|
||
dashboard
|
||
</span>
|
||
</div>
|
||
{/* Miniature ⌘K Bar */}
|
||
<div
|
||
style={{
|
||
width: 56,
|
||
height: 10,
|
||
borderRadius: 4,
|
||
border: "1px solid rgba(255,255,255,0.15)",
|
||
background: "rgba(255,255,255,0.05)",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
height: 2,
|
||
width: 22,
|
||
background: "#fff",
|
||
opacity: 0.25,
|
||
}}
|
||
/>
|
||
</div>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: "50%",
|
||
background: "rgba(255,255,255,0.2)",
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Page tabs secondary bar */}
|
||
<div
|
||
style={{
|
||
height: 14,
|
||
background: "#1a1918",
|
||
borderBottom: "1px solid rgba(255,255,255,0.04)",
|
||
padding: "0 10px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 10,
|
||
}}
|
||
>
|
||
{["Overview", "Reports", "Goals", "Anomalies", "Custom"].map(
|
||
(tab, idx) => (
|
||
<span
|
||
key={tab}
|
||
style={{
|
||
fontSize: 4.5,
|
||
color: idx === 0 ? "#fff" : "rgba(255,255,255,0.4)",
|
||
fontWeight: idx === 0 ? 600 : 400,
|
||
}}
|
||
>
|
||
{tab}
|
||
</span>
|
||
),
|
||
)}
|
||
</div>
|
||
|
||
{/* Light Dashboard below */}
|
||
{renderDashboardMockup()}
|
||
</div>
|
||
);
|
||
} else if (styleId === "topbar_dark") {
|
||
// Top Horizontal Dark Header Bar + Dark Dashboard (Screen 2 Dark)
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#0e0f12",
|
||
}}
|
||
>
|
||
{/* Top Dark Header */}
|
||
<div
|
||
style={{
|
||
height: 20,
|
||
borderBottom: "1px solid rgba(255,255,255,0.08)",
|
||
padding: "0 10px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
background: "#121110",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", gap: 6, alignItems: "center" }}>
|
||
<div
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: 2,
|
||
background: accentColor,
|
||
opacity: 0.9,
|
||
}}
|
||
/>
|
||
<span
|
||
style={{
|
||
fontSize: 5.5,
|
||
fontWeight: 700,
|
||
color: "#fff",
|
||
fontFamily: "var(--font-sans)",
|
||
letterSpacing: "-0.01em",
|
||
}}
|
||
>
|
||
Lattice
|
||
</span>
|
||
<span style={{ fontSize: 4.5, color: "rgba(255,255,255,0.3)" }}>
|
||
/
|
||
</span>
|
||
<div style={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||
<div
|
||
style={{
|
||
width: 6,
|
||
height: 6,
|
||
borderRadius: "50%",
|
||
background: "#d4b8a8",
|
||
}}
|
||
/>
|
||
<span
|
||
style={{
|
||
fontSize: 4.5,
|
||
color: "rgba(255,255,255,0.6)",
|
||
fontWeight: 500,
|
||
}}
|
||
>
|
||
mira-reyes
|
||
</span>
|
||
</div>
|
||
<span style={{ fontSize: 4.5, color: "rgba(255,255,255,0.3)" }}>
|
||
/
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 4.5,
|
||
color: "rgba(255,255,255,0.8)",
|
||
fontWeight: 500,
|
||
}}
|
||
>
|
||
dashboard
|
||
</span>
|
||
</div>
|
||
{/* Miniature ⌘K Bar */}
|
||
<div
|
||
style={{
|
||
width: 56,
|
||
height: 10,
|
||
borderRadius: 4,
|
||
border: "1px solid rgba(255,255,255,0.15)",
|
||
background: "rgba(255,255,255,0.05)",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
height: 2,
|
||
width: 22,
|
||
background: "#fff",
|
||
opacity: 0.25,
|
||
}}
|
||
/>
|
||
</div>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: "50%",
|
||
background: "rgba(255,255,255,0.2)",
|
||
}}
|
||
/>
|
||
</div>
|
||
|
||
{/* Page tabs secondary bar */}
|
||
<div
|
||
style={{
|
||
height: 14,
|
||
background: "#1a1918",
|
||
borderBottom: "1px solid rgba(255,255,255,0.04)",
|
||
padding: "0 10px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 10,
|
||
}}
|
||
>
|
||
{["Overview", "Reports", "Goals", "Anomalies", "Custom"].map(
|
||
(tab, idx) => (
|
||
<span
|
||
key={tab}
|
||
style={{
|
||
fontSize: 4.5,
|
||
color: idx === 0 ? "#fff" : "rgba(255,255,255,0.4)",
|
||
fontWeight: idx === 0 ? 600 : 400,
|
||
}}
|
||
>
|
||
{tab}
|
||
</span>
|
||
),
|
||
)}
|
||
</div>
|
||
|
||
{/* Dark Dashboard below */}
|
||
{renderDashboardMockup()}
|
||
</div>
|
||
);
|
||
} else if (styleId === "rail") {
|
||
// Icon rail + secondary vertical list panel — Complete Dark-mode Dashboard (Screen 1)
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#0e0f12",
|
||
}}
|
||
>
|
||
{/* Far-left Icon Rail (Black) */}
|
||
<div
|
||
style={{
|
||
width: 18,
|
||
borderRight: "1px solid rgba(255,255,255,0.08)",
|
||
padding: "8px 0",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
gap: 6,
|
||
background: "#121110",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: 3,
|
||
background: accentColor,
|
||
opacity: 0.9,
|
||
}}
|
||
/>
|
||
{[1, 2, 3, 4].map((i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: 2,
|
||
background: "rgba(255,255,255,0.18)",
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
{/* Secondary List Panel (Dark) */}
|
||
<div
|
||
style={{
|
||
width: 52,
|
||
borderRight: "1px solid rgba(255,255,255,0.08)",
|
||
padding: "8px 6px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
background: "#1a1918",
|
||
}}
|
||
>
|
||
{/* Group Header */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "center",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 3.5,
|
||
color: c.muted,
|
||
fontWeight: 600,
|
||
letterSpacing: "0.04em",
|
||
textTransform: "uppercase",
|
||
}}
|
||
>
|
||
MY DASHBOARDS
|
||
</span>
|
||
</div>
|
||
{/* Active lists */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 3.5 }}>
|
||
{[
|
||
{ n: "Workspace overview", s: "default", active: true },
|
||
{ n: "Revenue · weekly", s: "shared by Theo" },
|
||
{ n: "Pipeline health", s: "auto-refresh" },
|
||
{ n: "Team performance", s: "private" },
|
||
].map((dash, idx) => (
|
||
<div
|
||
key={idx}
|
||
style={{ display: "flex", flexDirection: "column", gap: 1 }}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 4,
|
||
color: dash.active ? "#fff" : c.subtext,
|
||
fontWeight: dash.active ? 600 : 400,
|
||
}}
|
||
>
|
||
{dash.n}
|
||
</span>
|
||
<span style={{ fontSize: 3, color: c.muted }}>{dash.s}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
{/* Main Content Area (Dark) */}
|
||
{renderDashboardMockup()}
|
||
</div>
|
||
);
|
||
} else if (styleId === "flux") {
|
||
// Icon rail + secondary vertical list panel — Complete Dark-mode Dashboard (Screen 1)
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#0e0f12",
|
||
}}
|
||
>
|
||
{/* Far-left Icon Rail (Black) */}
|
||
<div
|
||
style={{
|
||
width: 18,
|
||
borderRight: "1px solid rgba(255,255,255,0.08)",
|
||
padding: "8px 0",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
gap: 6,
|
||
background: "#121110",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: 3,
|
||
background: accentColor,
|
||
opacity: 0.9,
|
||
}}
|
||
/>
|
||
{[1, 2, 3, 4].map((i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: 2,
|
||
background: "rgba(255,255,255,0.18)",
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
{/* Secondary List Panel (Dark) */}
|
||
<div
|
||
style={{
|
||
width: 48,
|
||
borderRight: "1px solid rgba(255,255,255,0.08)",
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
background: "#1a1918",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", gap: 4, alignItems: "center" }}>
|
||
{wireLine(28, 0.45)}
|
||
</div>
|
||
<div style={{ height: 1, background: "rgba(255,255,255,0.08)" }} />
|
||
{wireLine(36, 0.35)}
|
||
{wireLine(24, 0.2)}
|
||
{wireLine(32, 0.2)}
|
||
{wireLine(28, 0.2)}
|
||
</div>
|
||
{/* Main Content Area (Dark) */}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: 8,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "center",
|
||
}}
|
||
>
|
||
{wireLine(36, 0.65)}
|
||
<div
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: "50%",
|
||
background: "rgba(255,255,255,0.2)",
|
||
}}
|
||
/>
|
||
</div>
|
||
{/* 4 Mini Cards Grid (Dark) */}
|
||
<div
|
||
style={{
|
||
display: "grid",
|
||
gridTemplateColumns: "repeat(4, 1fr)",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
{[1, 2, 3, 4].map((i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
border: "1px solid rgba(255,255,255,0.06)",
|
||
borderRadius: 4,
|
||
padding: "4px 6px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 2,
|
||
background: "#1a1918",
|
||
}}
|
||
>
|
||
{wireLine("50%", 0.35)}
|
||
<div
|
||
style={{
|
||
height: 2,
|
||
width: "100%",
|
||
background:
|
||
i === 3 ? "oklch(0.65 0.18 25)" : "oklch(0.78 0.16 155)",
|
||
opacity: 0.8,
|
||
}}
|
||
/>
|
||
</div>
|
||
))}
|
||
</div>
|
||
{/* Charts Row (Dark) */}
|
||
<div style={{ display: "flex", gap: 6, flex: 1 }}>
|
||
<div
|
||
style={{
|
||
flex: 1.5,
|
||
border: "1px solid rgba(255,255,255,0.06)",
|
||
borderRadius: 5,
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
background: "#1a1918",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 3,
|
||
alignItems: "flex-end",
|
||
flex: 1,
|
||
paddingTop: 4,
|
||
}}
|
||
>
|
||
{[6, 12, 10, 16, 20, 14, 8].map((h, idx) => (
|
||
<div
|
||
key={idx}
|
||
style={{
|
||
flex: 1,
|
||
height: h,
|
||
background:
|
||
idx === 4
|
||
? "oklch(0.6 0.18 250)"
|
||
: "rgba(255,255,255,0.08)",
|
||
borderRadius: 1,
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
border: "1px solid rgba(255,255,255,0.06)",
|
||
borderRadius: 5,
|
||
background: "#1a1918",
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
{wireLine("70%", 0.45)}
|
||
{wireLine("100%", 0.2)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "flux") {
|
||
// Top horizontal header bar + multi-column page
|
||
content = (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
width: "100%",
|
||
height: "100%",
|
||
}}
|
||
>
|
||
{/* Topbar */}
|
||
<div
|
||
style={{
|
||
height: 20,
|
||
borderBottom: "1px solid var(--hairline)",
|
||
padding: "0 8px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
background: "oklch(0.19 0.009 60 / 0.3)",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", gap: 6 }}>
|
||
<div
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: "50%",
|
||
background: accentColor,
|
||
opacity: 0.7,
|
||
}}
|
||
/>
|
||
{wireLine(12, 0.3)}
|
||
{wireLine(12, 0.3)}
|
||
</div>
|
||
{/* Miniature ⌘K Bar */}
|
||
<div
|
||
style={{
|
||
width: 44,
|
||
height: 10,
|
||
borderRadius: 5,
|
||
border: "1px solid var(--hairline)",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
paddingLeft: 4,
|
||
}}
|
||
>
|
||
{wireLine(8, 0.15)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: "50%",
|
||
background: "rgba(255,255,255,0.15)",
|
||
}}
|
||
/>
|
||
</div>
|
||
{/* Main Grid */}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: 8,
|
||
display: "grid",
|
||
gridTemplateColumns: "repeat(3, 1fr)",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
{[1, 2, 3].map((i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
border: "1px solid var(--hairline)",
|
||
borderRadius: 6,
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
{wireLine("50%", 0.3)}
|
||
{wireLine("100%", 0.15)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "rail") {
|
||
// Icon rail + secondary vertical list panel — Dark CRM/SaaS theme
|
||
content = (
|
||
<div style={{ display: "flex", width: "100%", height: "100%" }}>
|
||
{/* Far-left Icon Rail */}
|
||
<div
|
||
style={{
|
||
width: 18,
|
||
borderRight: "1px solid var(--hairline)",
|
||
padding: "6px 0",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
gap: 6,
|
||
background: "oklch(0.14 0.008 60 / 0.9)",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: 10,
|
||
height: 10,
|
||
borderRadius: 3,
|
||
background: accentColor,
|
||
opacity: 0.8,
|
||
}}
|
||
/>
|
||
{[1, 2, 3].map((i) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: 2,
|
||
background: "rgba(255,255,255,0.12)",
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
{/* Secondary Vertical List Panel */}
|
||
<div
|
||
style={{
|
||
width: 48,
|
||
borderRight: "1px solid var(--hairline)",
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
background: "oklch(0.17 0.008 60 / 0.5)",
|
||
}}
|
||
>
|
||
{wireLine(36, 0.4)}
|
||
<div style={{ height: 1, background: "var(--hairline)" }} />
|
||
{wireLine(28, 0.25)}
|
||
{wireLine(32, 0.15)}
|
||
{wireLine(24, 0.25)}
|
||
{wireLine(28, 0.15)}
|
||
</div>
|
||
{/* Main Content Area */}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: 8,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
{wireLine(36, 0.45)}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
border: "1px solid var(--hairline)",
|
||
borderRadius: 6,
|
||
padding: 8,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
background: "rgba(255,255,255,0.02)",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", gap: 6 }}>
|
||
<div
|
||
style={{
|
||
width: 12,
|
||
height: 12,
|
||
borderRadius: 3,
|
||
background: "rgba(255,255,255,0.08)",
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
paddingTop: 2,
|
||
}}
|
||
>
|
||
{wireLine("40%", 0.3)}
|
||
{wireLine("20%", 0.15)}
|
||
</div>
|
||
</div>
|
||
{wireLine("100%", 0.15)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "flux") {
|
||
// Glass aurora: purple background with a floating card
|
||
content = (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
position: "relative",
|
||
display: "grid",
|
||
placeItems: "center",
|
||
background: "#0c0a1a",
|
||
}}
|
||
>
|
||
{/* Aurora Glow */}
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
width: 80,
|
||
height: 80,
|
||
borderRadius: "50%",
|
||
background:
|
||
"radial-gradient(circle, oklch(0.6 0.18 300) 0%, transparent 70%)",
|
||
filter: "blur(14px)",
|
||
opacity: 0.7,
|
||
top: "10%",
|
||
left: "30%",
|
||
}}
|
||
/>
|
||
{/* Frosted Card */}
|
||
<div
|
||
style={{
|
||
width: "80%",
|
||
height: "70%",
|
||
borderRadius: 8,
|
||
border: "1px solid rgba(255,255,255,0.14)",
|
||
background: "rgba(255,255,255,0.04)",
|
||
backdropFilter: "blur(12px)",
|
||
padding: 8,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
boxShadow: "0 8px 32px rgba(0,0,0,0.3)",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", gap: 4 }}>
|
||
<div
|
||
style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: "50%",
|
||
background: accentColor,
|
||
opacity: 0.8,
|
||
}}
|
||
/>
|
||
{wireLine(18, 0.5)}
|
||
</div>
|
||
{wireLine("80%", 0.3)}
|
||
{wireLine("40%", 0.15)}
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "minimal") {
|
||
// Classic Minimal: parchment background, gridded rule lines
|
||
content = (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#f5f4ef",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
height: 18,
|
||
borderBottom: "1px solid #d4d0c8",
|
||
padding: "0 8px",
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
}}
|
||
>
|
||
{wireLine(24, 0.6)}
|
||
{wireLine(12, 0.4)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
padding: 8,
|
||
display: "grid",
|
||
gridTemplateColumns: "1fr 1fr",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
borderRight: "1px dashed #d4d0c8",
|
||
paddingRight: 4,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
{wireLine("90%", 0.5)}
|
||
{wireLine("50%", 0.25)}
|
||
</div>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
||
{wireLine("100%", 0.5)}
|
||
{wireLine("70%", 0.25)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "bento") {
|
||
// Dark bento grid
|
||
content = (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
padding: 8,
|
||
display: "grid",
|
||
gridTemplateColumns: "1.2fr 1fr",
|
||
gridTemplateRows: "1fr 1fr",
|
||
gap: 5,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
gridRow: "span 2",
|
||
border: "1px solid var(--hairline)",
|
||
borderRadius: 6,
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
{wireLine("50%", 0.4)}
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
background: "rgba(255,255,255,0.03)",
|
||
borderRadius: 4,
|
||
display: "grid",
|
||
placeItems: "center",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: 14,
|
||
height: 14,
|
||
borderRadius: "50%",
|
||
border: "1px solid var(--accent)",
|
||
opacity: 0.7,
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
<div
|
||
style={{
|
||
border: "1px solid var(--hairline)",
|
||
borderRadius: 6,
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
}}
|
||
>
|
||
{wireLine("80%", 0.3)}
|
||
{wireLine("40%", 0.15)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
border: "1px solid var(--hairline)",
|
||
borderRadius: 6,
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 3,
|
||
}}
|
||
>
|
||
{wireLine("100%", 0.3)}
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "swiss") {
|
||
// Editorial swiss: White background, bold geometric layouts
|
||
content = (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#ffffff",
|
||
padding: 10,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "baseline",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: 900,
|
||
color: "#000",
|
||
fontFamily: "sans-serif",
|
||
letterSpacing: "-0.04em",
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
VIBN.
|
||
</span>
|
||
{wireLine(16, 0.6)}
|
||
</div>
|
||
<div style={{ height: 1, background: "#000000", opacity: 0.15 }} />
|
||
<div style={{ display: "flex", gap: 10, flex: 1 }}>
|
||
<div
|
||
style={{
|
||
flex: 1.2,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
height: 8,
|
||
width: "100%",
|
||
background: "#000",
|
||
opacity: 0.85,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
height: 8,
|
||
width: "70%",
|
||
background: "#000",
|
||
opacity: 0.85,
|
||
}}
|
||
/>
|
||
</div>
|
||
<div
|
||
style={{
|
||
flex: 1,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 4,
|
||
paddingTop: 2,
|
||
}}
|
||
>
|
||
{wireLine("100%", 0.4)}
|
||
{wireLine("100%", 0.4)}
|
||
{wireLine("60%", 0.2)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} else if (styleId === "brutalist") {
|
||
// Neo-brutalist yellow card, thick borders, heavy offsets
|
||
content = (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
background: "#f0eee9",
|
||
display: "grid",
|
||
placeItems: "center",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
width: "75%",
|
||
height: "70%",
|
||
background: "#fcf05a",
|
||
border: "2px solid #000",
|
||
borderRadius: 4,
|
||
boxShadow: "3px 3px 0 #000",
|
||
padding: 6,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 5,
|
||
}}
|
||
>
|
||
<div style={{ height: 8, width: "100%", background: "#000" }} />
|
||
<div style={{ display: "flex", gap: 4, alignItems: "center" }}>
|
||
<div
|
||
style={{
|
||
width: 12,
|
||
height: 12,
|
||
borderRadius: "50%",
|
||
background: "#ff5bf5",
|
||
border: "1.5px solid #000",
|
||
}}
|
||
/>
|
||
{wireLine(24, 0.85)}
|
||
</div>
|
||
{wireLine("60%", 0.6)}
|
||
</div>
|
||
</div>
|
||
);
|
||
} else {
|
||
// I'm not sure / Undecided
|
||
content = (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 6,
|
||
background:
|
||
"radial-gradient(circle at center, rgba(255,255,255,0.03) 0%, transparent 60%)",
|
||
}}
|
||
>
|
||
<svg
|
||
width="20"
|
||
height="20"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="var(--accent)"
|
||
strokeWidth="1.8"
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
style={{
|
||
opacity: 0.75,
|
||
filter: "drop-shadow(0 0 8px var(--accent-glow))",
|
||
}}
|
||
>
|
||
<circle cx="12" cy="12" r="10" />
|
||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3M12 17h.01" />
|
||
</svg>
|
||
<span
|
||
style={{
|
||
fontSize: 10,
|
||
color: "var(--fg-faint)",
|
||
textTransform: "uppercase",
|
||
letterSpacing: "0.12em",
|
||
fontFamily: "var(--font-mono)",
|
||
}}
|
||
>
|
||
Automatic
|
||
</span>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
width: "100%",
|
||
height: "100%",
|
||
position: "relative",
|
||
background: isDark ? "oklch(0.155 0.008 60)" : "transparent",
|
||
}}
|
||
>
|
||
{content}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
interface StyleOption {
|
||
id: string;
|
||
label: string;
|
||
desc: string;
|
||
illustration: React.ReactNode;
|
||
fullWidth?: boolean;
|
||
}
|
||
|
||
const SAAS_STYLES: StyleOption[] = [
|
||
{
|
||
id: "sidebar_light",
|
||
label: "Vertical Sidebar — Light",
|
||
desc: "Clean light sidebar + light dashboard layouts.",
|
||
illustration: <LayoutPreview styleId="sidebar_light" />,
|
||
},
|
||
{
|
||
id: "sidebar_dark",
|
||
label: "Vertical Sidebar — Dark",
|
||
desc: "Full dark sidebar + dark dashboard layouts.",
|
||
illustration: <LayoutPreview styleId="sidebar_dark" />,
|
||
},
|
||
{
|
||
id: "topbar_light",
|
||
label: "Top Horizontal — Light",
|
||
desc: "Sleek dark topbar + light dashboard layouts.",
|
||
illustration: <LayoutPreview styleId="topbar_light" />,
|
||
},
|
||
{
|
||
id: "topbar_dark",
|
||
label: "Top Horizontal — Dark",
|
||
desc: "Full dark topbar + dark dashboard layouts.",
|
||
illustration: <LayoutPreview styleId="topbar_dark" />,
|
||
},
|
||
{
|
||
id: "not_sure",
|
||
label: "I'm not sure",
|
||
desc: "Let Vibn help you decide based on your description",
|
||
fullWidth: true,
|
||
illustration: <LayoutPreview styleId="not_sure" />,
|
||
},
|
||
];
|
||
|
||
const MARKETPLACE_STYLES: StyleOption[] = [
|
||
{
|
||
id: "flux",
|
||
label: "Dark Glass / Flux",
|
||
desc: "Modern dark-glass panels with glowing fuchsia aurora backdrops.",
|
||
illustration: <LayoutPreview styleId="flux" />,
|
||
},
|
||
{
|
||
id: "minimal",
|
||
label: "Classic Minimal",
|
||
desc: "Warm parchment neutrals, high-contrast typography and clean grids.",
|
||
illustration: <LayoutPreview styleId="minimal" />,
|
||
},
|
||
];
|
||
|
||
const GENERAL_STYLES: StyleOption[] = [
|
||
{
|
||
id: "bento",
|
||
label: "Dark Bento",
|
||
desc: "Modern dark UI, bento-box card clusters.",
|
||
illustration: <LayoutPreview styleId="bento" />,
|
||
},
|
||
{
|
||
id: "swiss",
|
||
label: "Editorial Swiss",
|
||
desc: "Type-led, gridded, lots of white space — clean and academic.",
|
||
illustration: <LayoutPreview styleId="swiss" />,
|
||
},
|
||
{
|
||
id: "brutalist",
|
||
label: "Neo-Brutalist",
|
||
desc: "Bold offsets, thick hand-drawn borders, highly tactile and organic.",
|
||
illustration: <LayoutPreview styleId="brutalist" />,
|
||
},
|
||
];
|
||
|
||
function EntrepStyle({ productType, value, onChange }) {
|
||
// Dynamically tailor the styles array based on what they picked on Page 2
|
||
const isSaas = productType === "saas";
|
||
const isMarketplace = productType === "marketplace";
|
||
|
||
const styles = isSaas
|
||
? SAAS_STYLES
|
||
: isMarketplace
|
||
? MARKETPLACE_STYLES
|
||
: GENERAL_STYLES;
|
||
|
||
return (
|
||
<>
|
||
<WizardQ
|
||
title="Choose a starting design style"
|
||
sub={
|
||
isSaas
|
||
? "Select the navigation layout that fits your app's density."
|
||
: isMarketplace
|
||
? "Select the design aesthetic for your marketplace templates."
|
||
: "Select the design layout for your custom pages."
|
||
}
|
||
/>
|
||
<PresetGroup
|
||
options={styles.map((s) => ({
|
||
id: s.id,
|
||
label: s.label,
|
||
desc: s.desc,
|
||
icon: undefined,
|
||
illustration: s.illustration,
|
||
fullWidth: s.fullWidth,
|
||
}))}
|
||
value={value}
|
||
onChange={onChange}
|
||
columns={2}
|
||
minimal
|
||
/>
|
||
</>
|
||
);
|
||
}
|
||
|
||
const VIBES = [
|
||
{
|
||
id: "warm",
|
||
name: "Warm coral",
|
||
swatch: "linear-gradient(135deg, #E27855, #B33B2A)",
|
||
desc: "Confident, hand-built, warm.",
|
||
},
|
||
{
|
||
id: "ink",
|
||
name: "Ink & paper",
|
||
swatch: "linear-gradient(135deg, #1d1d1d, #4a4a4a)",
|
||
desc: "Editorial, serif, quiet.",
|
||
},
|
||
{
|
||
id: "sage",
|
||
name: "Sage matte",
|
||
swatch: "linear-gradient(135deg, #7BA890, #3F6B57)",
|
||
desc: "Calm, modern, slightly herbal.",
|
||
},
|
||
{
|
||
id: "neon",
|
||
name: "Neon arcade",
|
||
swatch: "linear-gradient(135deg, #5B6CFF, #FF3DDB)",
|
||
desc: "Loud, fun, late-night.",
|
||
},
|
||
{
|
||
id: "cream",
|
||
name: "Cream linen",
|
||
swatch: "linear-gradient(135deg, #F2E7D5, #C9A977)",
|
||
desc: "Cozy and beige.",
|
||
},
|
||
{
|
||
id: "later",
|
||
name: "Decide later",
|
||
swatch:
|
||
"repeating-linear-gradient(45deg, oklch(0.30 0.010 60), oklch(0.30 0.010 60) 6px, oklch(0.22 0.010 60) 6px, oklch(0.22 0.010 60) 12px)",
|
||
desc: "Vibn picks one that fits.",
|
||
},
|
||
];
|
||
|
||
function EntrepVibe({ value, onChange }) {
|
||
return (
|
||
<>
|
||
<WizardQ
|
||
title="Pick a starting vibe."
|
||
sub="Every color and font is a tweak away once the site is live."
|
||
/>
|
||
<div
|
||
style={{
|
||
display: "grid",
|
||
gridTemplateColumns: "repeat(3, 1fr)",
|
||
gap: 10,
|
||
}}
|
||
>
|
||
{VIBES.map((v) => {
|
||
const active = value === v.id;
|
||
return (
|
||
<button
|
||
key={v.id}
|
||
type="button"
|
||
onClick={() => onChange(v.id)}
|
||
style={{
|
||
padding: "10px 10px 10px",
|
||
borderRadius: 11,
|
||
border: `1px solid ${active ? "var(--accent)" : "var(--hairline)"}`,
|
||
background: active
|
||
? "oklch(0.20 0.04 35 / 0.4)"
|
||
: "oklch(0.18 0.009 60 / 0.6)",
|
||
boxShadow: active
|
||
? "0 0 0 3px oklch(0.74 0.175 35 / 0.1)"
|
||
: "none",
|
||
textAlign: "left",
|
||
color: "var(--fg)",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 8,
|
||
transition: "border-color .15s, background .15s",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
height: 52,
|
||
borderRadius: 7,
|
||
background: v.swatch,
|
||
border: "1px solid oklch(1 0 0 / 0.08)",
|
||
boxShadow: "inset 0 1px 0 oklch(1 0 0 / 0.18)",
|
||
}}
|
||
/>
|
||
<span
|
||
style={{
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
letterSpacing: "-0.005em",
|
||
}}
|
||
>
|
||
{v.name}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 11.5,
|
||
color: "var(--fg-mute)",
|
||
lineHeight: 1.4,
|
||
}}
|
||
>
|
||
{v.desc}
|
||
</span>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</>
|
||
);
|
||
}
|
||
|
||
// ── Path wrapper ───────────────────────────────────────────────────────────
|
||
export function EntrepreneurPath({
|
||
data,
|
||
onUpdate,
|
||
onBack,
|
||
onClose,
|
||
onComplete,
|
||
onJumpToStep,
|
||
step,
|
||
}) {
|
||
const next = () => {
|
||
if (step < ENTREP_TOTAL - 1) onJumpToStep(step + 1);
|
||
else onComplete();
|
||
};
|
||
const back = () => {
|
||
if (step === 0) onBack();
|
||
else onJumpToStep(step - 1);
|
||
};
|
||
|
||
let body,
|
||
canNext,
|
||
onSkip = null;
|
||
if (step === 0) {
|
||
body = (
|
||
<EntrepType
|
||
value={data.productType || ""}
|
||
onChange={(v) => onUpdate({ productType: v })}
|
||
/>
|
||
);
|
||
canNext = !!data.productType;
|
||
} else if (step === 1) {
|
||
body = (
|
||
<EntrepStyle
|
||
productType={data.productType}
|
||
value={data.designStyle || ""}
|
||
onChange={(v) => onUpdate({ designStyle: v })}
|
||
/>
|
||
);
|
||
canNext = !!data.designStyle;
|
||
} else if (step === 2) {
|
||
body = (
|
||
<EntrepIdea
|
||
value={data.idea || ""}
|
||
onChange={(v) => onUpdate({ idea: v })}
|
||
/>
|
||
);
|
||
canNext = (data.idea || "").trim().length >= 8;
|
||
} else {
|
||
body = (
|
||
<EntrepVibe value={data.vibe} onChange={(v) => onUpdate({ vibe: v })} />
|
||
);
|
||
canNext = !!data.vibe;
|
||
onSkip = () => {
|
||
onUpdate({ vibe: "later" });
|
||
next();
|
||
};
|
||
}
|
||
|
||
// 5 total: fork(1) + 4 path steps
|
||
return (
|
||
<>
|
||
<WizardTop
|
||
onBack={back}
|
||
onClose={onClose}
|
||
lane={LANE_LABELS.entrepreneur}
|
||
stepText={ENTREP_STEP_NAMES[step]}
|
||
current={step + 2}
|
||
total={5}
|
||
/>
|
||
<WizardBody
|
||
width={step === 1 ? "xwide" : step === 2 || step === 3 ? "wide" : null}
|
||
>
|
||
{body}
|
||
<WizardFooter
|
||
onNext={next}
|
||
canNext={canNext}
|
||
nextLabel={step === ENTREP_TOTAL - 1 ? "Build →" : "Continue"}
|
||
hint={canNext ? "⌘↵" : null}
|
||
onSkip={onSkip}
|
||
skipLabel={step === 0 ? "I'm not sure" : "Pick for me"}
|
||
/>
|
||
</WizardBody>
|
||
</>
|
||
);
|
||
}
|