Files
vibn-agent-runner/docs_archive/remixed-9edec9e9.tsx

992 lines
64 KiB
TypeScript
Raw Permalink 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.
import { useState, useEffect, useRef } from "react";
const FontLoader = () => (
<style>{`
@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400&family=Outfit:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');
* { margin:0; padding:0; box-sizing:border-box; }
body { background: #f6f4f0; }
@keyframes enter { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }
@keyframes blink { 0%,100%{opacity:.2} 50%{opacity:.8} }
@keyframes breathe { 0%,100%{transform:scale(1)} 50%{transform:scale(1.15)} }
::selection { background: #1a1a1a; color: #fff; }
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #d0ccc4; border-radius: 10px; }
input::placeholder { color: #b5b0a6; }
input:focus, textarea:focus { outline: none; }
button { font-family: 'Outfit', sans-serif; cursor: pointer; }
textarea { font-family: 'Outfit', sans-serif; resize: vertical; }
`}</style>
);
/* ─── DATA ─── */
const projects = [
{ id: 1, name: "Meridian", desc: "Client portal for boutique agencies", status: "building", progress: 68, features: 12, phase: "Frontend Gen", lastActive: "2h ago", color: "#3d5afe", domain: "meridian-app.stackless.dev", repo: "stackless/meridian-build", created: "Jan 12, 2026" },
{ id: 2, name: "Tidepool", desc: "Marine research data platform", status: "prd", progress: 45, features: 7, phase: "Features", lastActive: "20m ago", color: "#00897b", domain: null, repo: null, created: "Feb 3, 2026" },
{ id: 3, name: "Canopy", desc: "Internal team knowledge base", status: "live", progress: 100, features: 18, phase: "Deployed", lastActive: "1d ago", color: "#2e7d32", domain: "canopy.stackless.dev", customDomain: "kb.acmecorp.com", repo: "stackless/canopy-build", created: "Nov 28, 2025" },
{ id: 4, name: "Foxglove", desc: "Prescription mgmt for pharmacies", status: "prd", progress: 20, features: 3, phase: "Discovery", lastActive: "now", color: "#e65100", domain: null, repo: null, created: "Feb 27, 2026" },
];
const activityFeed = [
{ time: "2 min ago", project: "Foxglove", action: "Atlas completed Users & Personas phase", type: "atlas" },
{ time: "18 min ago", project: "Foxglove", action: "You described the core prescription workflow", type: "user" },
{ time: "1h ago", project: "Meridian", action: "Build: Dashboard UI component generated", type: "build" },
{ time: "2h ago", project: "Meridian", action: "Build: Authentication system passed all tests", type: "build" },
{ time: "3h ago", project: "Tidepool", action: "Atlas captured 7 features in MoSCoW framework", type: "atlas" },
{ time: "5h ago", project: "Tidepool", action: "You approved Problem Statement section", type: "user" },
{ time: "8h ago", project: "Meridian", action: "Build: Database schema deployed", type: "build" },
{ time: "1d ago", project: "Canopy", action: "Custom domain kb.acmecorp.com verified and active", type: "deploy" },
{ time: "1d ago", project: "Canopy", action: "v1.2 deployed — added search filters", type: "deploy" },
{ time: "2d ago", project: "Meridian", action: "PRD approved — build pipeline started", type: "deploy" },
{ time: "2d ago", project: "Tidepool", action: "Project created", type: "user" },
{ time: "3d ago", project: "Foxglove", action: "Project created", type: "user" },
];
const chatHistory = [
{ from: "atlas", text: "I see you're building Foxglove — prescription management for small pharmacies. We've locked in the problem statement and identified your primary user.\n\nNow let's map the core workflow. When a pharmacist opens Foxglove first thing in the morning, what do they need to see?" },
{ from: "user", text: "They need to see incoming prescriptions from doctors. The current systems are super clunky. They want to see new scripts, verify them, and mark them as filled." },
{ from: "atlas", text: "Clean workflow. Three stages:\n\n1. Receive — new scripts appear in a queue\n2. Verify — check dosage, interactions, patient history\n3. Fill — mark dispensed, update stock\n\nTwo things I want to nail down: does the pharmacist need to message the prescribing doctor back through Foxglove if there's a dosage flag? And are we building for single-location pharmacies or chains with 23 stores?" },
];
const prdData = [
{ name: "Executive Summary", status: "done", pct: 100 },
{ name: "Problem Statement", status: "done", pct: 100 },
{ name: "Users & Personas", status: "done", pct: 100 },
{ name: "User Flows", status: "active", pct: 60 },
{ name: "Feature Requirements", status: "pending", pct: 20 },
{ name: "Screen Specs", status: "pending", pct: 0 },
{ name: "Business Model", status: "pending", pct: 0 },
{ name: "Non-Functional Reqs", status: "pending", pct: 0 },
{ name: "Risks", status: "pending", pct: 0 },
];
const discoveryPhases = [
{ name: "Big Picture", done: true },
{ name: "Users", done: true },
{ name: "Features", active: true },
{ name: "Business Model" },
{ name: "Screens" },
{ name: "Risks" },
];
/* ─── Micro Components ─── */
const Tag = ({ children, color = "#1a1a1a", bg = "#1a1a1a10" }) => (
<span style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 9px", borderRadius: 4, fontSize: "0.68rem", fontWeight: 600, letterSpacing: "0.02em", color, background: bg, fontFamily: "Outfit" }}>{children}</span>
);
const StatusDot = ({ status }) => {
const c = status === "live" ? "#2e7d32" : status === "building" ? "#3d5afe" : "#d4a04a";
return <span style={{ width: 7, height: 7, borderRadius: "50%", background: c, display: "inline-block", flexShrink: 0, animation: status === "building" ? "breathe 2.5s ease infinite" : "none" }} />;
};
const SectionLabel = ({ children }) => (
<div style={{ fontSize: "0.6rem", fontWeight: 600, color: "#a09a90", letterSpacing: "0.1em", textTransform: "uppercase", marginBottom: 12 }}>{children}</div>
);
const Card = ({ children, style: s = {}, hover = true, ...rest }) => {
const [hovered, setHovered] = useState(false);
return (
<div
onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}
style={{ background: "#fff", border: `1px solid ${hovered && hover ? "#d0ccc4" : "#e8e4dc"}`, borderRadius: 10, boxShadow: hovered && hover ? "0 2px 8px #1a1a1a0a" : "0 1px 2px #1a1a1a05", transition: "all 0.15s", ...s }}
{...rest}
>{children}</div>
);
};
const Btn = ({ children, variant = "primary", style: s = {}, ...rest }) => {
const styles = variant === "primary"
? { background: "#1a1a1a", color: "#fff", border: "1px solid #1a1a1a" }
: variant === "secondary"
? { background: "#fff", color: "#1a1a1a", border: "1px solid #e0dcd4" }
: { background: "transparent", color: "#a09a90", border: "1px solid transparent" };
return (
<button style={{ padding: "8px 16px", borderRadius: 7, fontSize: "0.78rem", fontWeight: 600, transition: "opacity 0.15s", ...styles, ...s }}
onMouseEnter={e => e.currentTarget.style.opacity = "0.8"}
onMouseLeave={e => e.currentTarget.style.opacity = "1"}
{...rest}
>{children}</button>
);
};
const InputField = ({ label, value, onChange, placeholder, type = "text", mono = false }) => (
<div style={{ marginBottom: 16 }}>
<div style={{ fontSize: "0.72rem", fontWeight: 600, color: "#6b6560", marginBottom: 6 }}>{label}</div>
<input type={type} value={value} onChange={onChange} placeholder={placeholder}
style={{ width: "100%", padding: "9px 13px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", fontFamily: mono ? "IBM Plex Mono" : "Outfit", color: "#1a1a1a" }} />
</div>
);
const Toggle = ({ on, onToggle, label }) => (
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0", borderBottom: "1px solid #f0ece4" }}>
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>{label}</span>
<button onClick={onToggle} style={{
width: 38, height: 22, borderRadius: 11, border: "none", padding: 2,
background: on ? "#1a1a1a" : "#ddd8d0", transition: "background 0.2s",
display: "flex", alignItems: "center",
}}>
<div style={{ width: 18, height: 18, borderRadius: "50%", background: "#fff", transition: "transform 0.2s", transform: on ? "translateX(16px)" : "translateX(0)" }} />
</button>
</div>
);
/* ─── SIDEBAR ─── */
const Sidebar = ({ activeProject, setActiveProject, view, setView }) => (
<nav style={{ width: 220, height: "100vh", background: "#fff", borderRight: "1px solid #e8e4dc", display: "flex", flexDirection: "column", fontFamily: "Outfit, sans-serif", flexShrink: 0 }}>
<div style={{ padding: "22px 18px 18px", display: "flex", alignItems: "center", gap: 9 }}>
<div style={{ width: 28, height: 28, borderRadius: 7, background: "#1a1a1a", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", fontSize: "0.82rem", fontWeight: 700, fontFamily: "Newsreader, serif" }}>S</div>
<span style={{ fontSize: "0.95rem", fontWeight: 600, color: "#1a1a1a", letterSpacing: "-0.03em" }}>stackless</span>
</div>
<div style={{ padding: "4px 10px" }}>
{[
{ id: "home", label: "Projects", icon: "⌗" },
{ id: "activity", label: "Activity", icon: "↗" },
{ id: "settings", label: "Settings", icon: "⚙" },
].map(n => (
<button key={n.id}
onClick={() => { setView(n.id); setActiveProject(null); }}
style={{
width: "100%", display: "flex", alignItems: "center", gap: 9,
padding: "8px 10px", borderRadius: 6, border: "none",
background: view === n.id && !activeProject ? "#f6f4f0" : "transparent",
color: view === n.id && !activeProject ? "#1a1a1a" : "#6b6560",
fontSize: "0.82rem", fontWeight: view === n.id && !activeProject ? 600 : 500,
transition: "all 0.12s",
}}
onMouseEnter={e => e.currentTarget.style.background = "#f6f4f0"}
onMouseLeave={e => { if (!(view === n.id && !activeProject)) e.currentTarget.style.background = "transparent"; }}
>
<span style={{ fontSize: "0.8rem", opacity: 0.45, width: 18, textAlign: "center" }}>{n.icon}</span>
{n.label}
</button>
))}
</div>
<div style={{ height: 1, background: "#eae6de", margin: "10px 18px" }} />
<div style={{ padding: "2px 10px", flex: 1, overflow: "auto" }}>
<div style={{ fontSize: "0.6rem", fontWeight: 600, color: "#a09a90", letterSpacing: "0.1em", textTransform: "uppercase", padding: "6px 10px 8px" }}>Projects</div>
{projects.map(p => (
<button key={p.id}
onClick={() => { setActiveProject(p); setView("project"); }}
style={{
width: "100%", display: "flex", alignItems: "center", gap: 9,
padding: "7px 10px", borderRadius: 6, border: "none",
background: activeProject?.id === p.id ? "#f6f4f0" : "transparent",
color: "#1a1a1a", fontSize: "0.82rem", fontWeight: activeProject?.id === p.id ? 600 : 450,
transition: "background 0.12s", textAlign: "left",
}}
onMouseEnter={e => e.currentTarget.style.background = "#f6f4f0"}
onMouseLeave={e => { if (activeProject?.id !== p.id) e.currentTarget.style.background = "transparent"; }}
>
<StatusDot status={p.status} />
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</span>
</button>
))}
</div>
<div style={{ padding: "14px 18px", borderTop: "1px solid #eae6de", display: "flex", alignItems: "center", gap: 9 }}>
<div style={{ width: 28, height: 28, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.72rem", fontWeight: 600, color: "#8a8478" }}>M</div>
<div>
<div style={{ fontSize: "0.78rem", fontWeight: 500, color: "#1a1a1a" }}>Michael</div>
<div style={{ fontSize: "0.62rem", color: "#a09a90" }}>Pro plan</div>
</div>
</div>
</nav>
);
/* ─── ACTIVITY PAGE ─── */
const ActivityPage = ({ setActiveProject, setView }) => {
const [filter, setFilter] = useState("all");
const filtered = filter === "all" ? activityFeed : activityFeed.filter(a => a.type === filter);
const typeIcon = (t) => t === "atlas" ? "A" : t === "build" ? "⚡" : t === "deploy" ? "▲" : "●";
const typeColor = (t) => t === "atlas" ? "#1a1a1a" : t === "build" ? "#3d5afe" : t === "deploy" ? "#2e7d32" : "#8a8478";
return (
<div style={{ padding: "44px 52px", maxWidth: 720, fontFamily: "Outfit, sans-serif", animation: "enter 0.35s ease both" }}>
<h1 style={{ fontFamily: "Newsreader, serif", fontSize: "1.9rem", fontWeight: 400, color: "#1a1a1a", letterSpacing: "-0.03em", marginBottom: 4 }}>Activity</h1>
<p style={{ fontSize: "0.82rem", color: "#a09a90", marginBottom: 28 }}>Everything happening across your projects</p>
{/* Filters */}
<div style={{ display: "flex", gap: 4, marginBottom: 24 }}>
{[
{ id: "all", label: "All" },
{ id: "atlas", label: "Atlas" },
{ id: "build", label: "Builds" },
{ id: "deploy", label: "Deploys" },
{ id: "user", label: "You" },
].map(f => (
<button key={f.id} onClick={() => setFilter(f.id)}
style={{
padding: "6px 14px", borderRadius: 6, border: "none",
background: filter === f.id ? "#1a1a1a" : "#fff",
color: filter === f.id ? "#fff" : "#6b6560",
fontSize: "0.75rem", fontWeight: 600, transition: "all 0.12s",
}}>{f.label}</button>
))}
</div>
{/* Feed */}
<div style={{ position: "relative", paddingLeft: 24 }}>
{/* Timeline line */}
<div style={{ position: "absolute", left: 8, top: 8, bottom: 8, width: 1, background: "#e8e4dc" }} />
{filtered.map((item, i) => (
<div key={i} style={{
display: "flex", gap: 14, marginBottom: 4, padding: "12px 16px",
animation: `enter 0.3s ease ${i * 0.03}s both`,
borderRadius: 8, transition: "background 0.12s", position: "relative",
}}
onMouseEnter={e => e.currentTarget.style.background = "#fff"}
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
>
{/* Dot on timeline */}
<div style={{
position: "absolute", left: -20, top: 18,
width: 9, height: 9, borderRadius: "50%",
background: typeColor(item.type), border: "2px solid #f6f4f0",
}} />
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
<button onClick={() => {
const proj = projects.find(p => p.name === item.project);
if (proj) { setActiveProject(proj); setView("project"); }
}} style={{
background: "none", border: "none", padding: 0,
fontSize: "0.82rem", fontWeight: 600, color: "#1a1a1a",
textDecoration: "none",
}}
onMouseEnter={e => e.currentTarget.style.textDecoration = "underline"}
onMouseLeave={e => e.currentTarget.style.textDecoration = "none"}
>{item.project}</button>
<span style={{ fontSize: "0.68rem", color: "#b5b0a6" }}>·</span>
<span style={{ fontSize: "0.72rem", color: "#b5b0a6" }}>{item.time}</span>
</div>
<div style={{ fontSize: "0.82rem", color: "#6b6560", lineHeight: 1.5 }}>{item.action}</div>
</div>
</div>
))}
</div>
</div>
);
};
/* ─── SETTINGS PAGE ─── */
const SettingsPage = () => {
const [settingsTab, setSettingsTab] = useState("account");
const [emailNotifs, setEmailNotifs] = useState(true);
const [buildNotifs, setBuildNotifs] = useState(true);
const [atlasDigest, setAtlasDigest] = useState(false);
const [darkMode, setDarkMode] = useState(false);
return (
<div style={{ display: "flex", height: "100vh", fontFamily: "Outfit, sans-serif", animation: "enter 0.35s ease both" }}>
{/* Settings nav */}
<div style={{ width: 200, padding: "44px 8px 44px 52px", flexShrink: 0 }}>
<h1 style={{ fontFamily: "Newsreader, serif", fontSize: "1.9rem", fontWeight: 400, color: "#1a1a1a", letterSpacing: "-0.03em", marginBottom: 28 }}>Settings</h1>
{[
{ id: "account", label: "Account" },
{ id: "notifications", label: "Notifications" },
{ id: "billing", label: "Plan & Billing" },
{ id: "team", label: "Team" },
{ id: "domains", label: "Domains" },
{ id: "api", label: "API Keys" },
{ id: "danger", label: "Danger Zone" },
].map(t => (
<button key={t.id} onClick={() => setSettingsTab(t.id)}
style={{
display: "block", width: "100%", textAlign: "left",
padding: "7px 12px", borderRadius: 6, border: "none",
background: settingsTab === t.id ? "#fff" : "transparent",
color: settingsTab === t.id ? "#1a1a1a" : "#8a8478",
fontSize: "0.82rem", fontWeight: settingsTab === t.id ? 600 : 450,
marginBottom: 2, transition: "all 0.12s",
boxShadow: settingsTab === t.id ? "0 1px 3px #1a1a1a08" : "none",
}}>{t.label}</button>
))}
</div>
{/* Settings content */}
<div style={{ flex: 1, padding: "44px 52px", overflow: "auto" }}>
{settingsTab === "account" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Account</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Manage your profile and preferences</p>
<Card style={{ padding: "24px", marginBottom: 20 }} hover={false}>
<div style={{ display: "flex", alignItems: "center", gap: 16, marginBottom: 24 }}>
<div style={{ width: 52, height: 52, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "1.1rem", fontWeight: 600, color: "#8a8478" }}>M</div>
<div>
<div style={{ fontSize: "0.95rem", fontWeight: 600, color: "#1a1a1a" }}>Michael</div>
<div style={{ fontSize: "0.78rem", color: "#a09a90" }}>michael@example.com</div>
</div>
<Btn variant="secondary" style={{ marginLeft: "auto", padding: "6px 14px", fontSize: "0.72rem" }}>Change photo</Btn>
</div>
<InputField label="Full name" value="Michael" onChange={() => {}} />
<InputField label="Email" value="michael@example.com" onChange={() => {}} type="email" />
<InputField label="Company" value="" onChange={() => {}} placeholder="Optional" />
<div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
<Btn variant="primary" style={{ padding: "8px 20px" }}>Save changes</Btn>
</div>
</Card>
<Card style={{ padding: "24px" }} hover={false}>
<h3 style={{ fontSize: "0.88rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Preferences</h3>
<p style={{ fontSize: "0.75rem", color: "#a09a90", marginBottom: 12 }}>Customize your workspace</p>
<Toggle on={darkMode} onToggle={() => setDarkMode(!darkMode)} label="Dark mode" />
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0", borderBottom: "1px solid #f0ece4" }}>
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>Default project view</span>
<select style={{ padding: "4px 10px", borderRadius: 5, border: "1px solid #e0dcd4", fontSize: "0.78rem", fontFamily: "Outfit", color: "#1a1a1a", background: "#faf8f5" }}>
<option>Chat</option>
<option>PRD</option>
<option>Build</option>
</select>
</div>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0" }}>
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>Atlas personality</span>
<select style={{ padding: "4px 10px", borderRadius: 5, border: "1px solid #e0dcd4", fontSize: "0.78rem", fontFamily: "Outfit", color: "#1a1a1a", background: "#faf8f5" }}>
<option>Balanced</option>
<option>Concise</option>
<option>Thorough</option>
</select>
</div>
</Card>
</div>
)}
{settingsTab === "notifications" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Notifications</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Choose what you hear about and when</p>
<Card style={{ padding: "24px" }} hover={false}>
<Toggle on={emailNotifs} onToggle={() => setEmailNotifs(!emailNotifs)} label="Email notifications" />
<Toggle on={buildNotifs} onToggle={() => setBuildNotifs(!buildNotifs)} label="Build completion alerts" />
<Toggle on={atlasDigest} onToggle={() => setAtlasDigest(!atlasDigest)} label="Daily Atlas digest" />
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 0" }}>
<span style={{ fontSize: "0.84rem", color: "#1a1a1a" }}>Notification frequency</span>
<select style={{ padding: "4px 10px", borderRadius: 5, border: "1px solid #e0dcd4", fontSize: "0.78rem", fontFamily: "Outfit", color: "#1a1a1a", background: "#faf8f5" }}>
<option>Real-time</option>
<option>Hourly digest</option>
<option>Daily digest</option>
</select>
</div>
</Card>
</div>
)}
{settingsTab === "billing" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Plan & Billing</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Manage your subscription and usage</p>
<Card style={{ padding: "24px", marginBottom: 16 }} hover={false}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 18 }}>
<div>
<div style={{ fontSize: "0.62rem", fontWeight: 600, color: "#a09a90", letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 4 }}>Current plan</div>
<div style={{ fontFamily: "Newsreader", fontSize: "1.4rem", color: "#1a1a1a" }}>Pro</div>
</div>
<Tag color="#2e7d32" bg="#2e7d3210">Active</Tag>
</div>
<div style={{ display: "flex", gap: 24, paddingTop: 16, borderTop: "1px solid #f0ece4" }}>
{[
{ label: "Projects", value: "10", used: "4" },
{ label: "Builds/mo", value: "20", used: "3" },
{ label: "Deploys", value: "Unlimited", used: "—" },
].map((m, i) => (
<div key={i}>
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 3 }}>{m.label}</div>
<div style={{ fontSize: "0.88rem", color: "#1a1a1a" }}>
<span style={{ fontFamily: "IBM Plex Mono", fontWeight: 500 }}>{m.used}</span>
<span style={{ color: "#b5b0a6" }}> / {m.value}</span>
</div>
</div>
))}
</div>
</Card>
<Card style={{ padding: "20px", display: "flex", alignItems: "center", justifyContent: "space-between" }} hover={false}>
<div>
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#1a1a1a" }}>Payment method</div>
<div style={{ fontSize: "0.78rem", color: "#a09a90", fontFamily: "IBM Plex Mono" }}> 4242 expires 08/27</div>
</div>
<Btn variant="secondary" style={{ padding: "6px 14px", fontSize: "0.72rem" }}>Update</Btn>
</Card>
</div>
)}
{settingsTab === "team" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Team</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Manage collaborators and permissions</p>
<Card style={{ padding: "24px" }} hover={false}>
{[
{ name: "Michael", email: "michael@example.com", role: "Owner" },
{ name: "Craig F.", email: "craig@example.com", role: "Editor" },
].map((m, i) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 0", borderBottom: i === 0 ? "1px solid #f0ece4" : "none" }}>
<div style={{ width: 32, height: 32, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.72rem", fontWeight: 600, color: "#8a8478" }}>{m.name[0]}</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#1a1a1a" }}>{m.name}</div>
<div style={{ fontSize: "0.72rem", color: "#a09a90" }}>{m.email}</div>
</div>
<Tag color="#6b6560" bg="#f0ece4">{m.role}</Tag>
</div>
))}
<div style={{ marginTop: 16 }}>
<Btn variant="secondary" style={{ width: "100%", fontSize: "0.78rem" }}>+ Invite team member</Btn>
</div>
</Card>
</div>
)}
{settingsTab === "domains" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>Domains</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Custom domains for your deployed projects</p>
<Card style={{ padding: "24px" }} hover={false}>
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", fontWeight: 500, color: "#1a1a1a" }}>kb.acmecorp.com</div>
<div style={{ fontSize: "0.72rem", color: "#a09a90" }}> Canopy</div>
</div>
<Tag color="#2e7d32" bg="#2e7d3210">Verified</Tag>
</div>
<div style={{ marginTop: 16 }}>
<Btn variant="secondary" style={{ width: "100%", fontSize: "0.78rem" }}>+ Add custom domain</Btn>
</div>
</Card>
</div>
)}
{settingsTab === "api" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", marginBottom: 4 }}>API Keys</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Access your projects programmatically</p>
<Card style={{ padding: "24px" }} hover={false}>
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.82rem", fontWeight: 500, color: "#1a1a1a" }}>Production key</div>
<div style={{ fontSize: "0.78rem", fontFamily: "IBM Plex Mono", color: "#a09a90" }}>sk_live_3kF9</div>
</div>
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Reveal</Btn>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0" }}>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.82rem", fontWeight: 500, color: "#1a1a1a" }}>Test key</div>
<div style={{ fontSize: "0.78rem", fontFamily: "IBM Plex Mono", color: "#a09a90" }}>sk_test_7mR2</div>
</div>
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Reveal</Btn>
</div>
<div style={{ marginTop: 16 }}>
<Btn variant="secondary" style={{ width: "100%", fontSize: "0.78rem" }}>+ Generate new key</Btn>
</div>
</Card>
</div>
)}
{settingsTab === "danger" && (
<div style={{ maxWidth: 480, animation: "enter 0.25s ease" }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#d32f2f", marginBottom: 4 }}>Danger Zone</h2>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 28 }}>Irreversible actions</p>
<Card style={{ padding: "20px", borderColor: "#f5d5d5" }} hover={false}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div>
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#1a1a1a" }}>Delete account</div>
<div style={{ fontSize: "0.75rem", color: "#a09a90" }}>Permanently remove your account and all projects</div>
</div>
<Btn variant="secondary" style={{ color: "#d32f2f", borderColor: "#f5d5d5", padding: "6px 14px", fontSize: "0.72rem" }}>Delete</Btn>
</div>
</Card>
</div>
)}
</div>
</div>
);
};
/* ─── PROJECT DETAIL ─── */
const ProjectDetail = ({ project }) => {
const [tab, setTab] = useState("chat");
const [msgs, setMsgs] = useState(chatHistory);
const [input, setInput] = useState("");
const [typing, setTyping] = useState(false);
const endRef = useRef(null);
useEffect(() => { endRef.current?.scrollIntoView({ behavior: "smooth" }); }, [msgs, typing]);
const send = () => {
if (!input.trim()) return;
setMsgs(p => [...p, { from: "user", text: input }]);
setInput("");
setTyping(true);
setTimeout(() => {
setTyping(false);
setMsgs(p => [...p, { from: "atlas", text: "Good instinct. For multi-location, the core question is inventory: shared pool vs per-store tracking.\n\nOption A — Shared inventory. Simpler, but Store B can't trust the count if Store A just dispensed.\n\nOption B — Per-location with transfers. Accurate, adds a \"request stock\" workflow.\n\nMost independents start single-location and add multi-store as v2. Want to scope it that way?" }]);
}, 2000);
};
const prdPct = Math.round(prdData.reduce((a, s) => a + s.pct, 0) / prdData.length);
return (
<div style={{ display: "flex", height: "100vh", fontFamily: "Outfit, sans-serif", animation: "enter 0.3s ease" }}>
<div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
{/* Header */}
<div style={{ padding: "18px 32px", borderBottom: "1px solid #e8e4dc", display: "flex", alignItems: "center", justifyContent: "space-between", background: "#fff" }}>
<div style={{ display: "flex", alignItems: "center", gap: 14 }}>
<div style={{ width: 34, height: 34, borderRadius: 9, background: project.color + "12", display: "flex", alignItems: "center", justifyContent: "center" }}>
<span style={{ fontFamily: "Newsreader", fontSize: "1rem", fontWeight: 500, color: project.color }}>{project.name[0]}</span>
</div>
<div>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<h2 style={{ fontSize: "1.05rem", fontWeight: 600, color: "#1a1a1a", letterSpacing: "-0.02em" }}>{project.name}</h2>
<Tag color={project.status === "live" ? "#2e7d32" : project.status === "building" ? "#3d5afe" : "#9a7b3a"}
bg={project.status === "live" ? "#2e7d3210" : project.status === "building" ? "#3d5afe10" : "#d4a04a12"}>
{project.status === "live" ? "Live" : project.status === "building" ? "Building" : "Defining"}
</Tag>
</div>
<p style={{ fontSize: "0.75rem", color: "#a09a90", marginTop: 1 }}>{project.desc}</p>
</div>
</div>
<div style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", fontWeight: 500, color: "#1a1a1a", background: "#f6f4f0", padding: "6px 12px", borderRadius: 6 }}>
{project.progress}%
</div>
</div>
{/* Tabs */}
<div style={{ padding: "0 32px", borderBottom: "1px solid #e8e4dc", display: "flex", gap: 0, background: "#fff" }}>
{[
{ id: "chat", label: "Atlas" },
{ id: "prd", label: "PRD" },
{ id: "build", label: "Build" },
{ id: "deploy", label: "Deploy" },
{ id: "projsettings", label: "Settings" },
].map(t => (
<button key={t.id} onClick={() => setTab(t.id)} style={{
padding: "12px 18px", border: "none", background: "none",
fontSize: "0.8rem", fontWeight: 500, cursor: "pointer",
color: tab === t.id ? "#1a1a1a" : "#a09a90",
borderBottom: tab === t.id ? "2px solid #1a1a1a" : "2px solid transparent",
transition: "all 0.12s", fontFamily: "Outfit",
}}>{t.label}</button>
))}
</div>
<div style={{ flex: 1, overflow: "hidden", display: "flex", background: "#f6f4f0" }}>
{/* CHAT */}
{tab === "chat" && (
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px" }}>
{msgs.map((m, i) => (
<div key={i} style={{ display: "flex", gap: 12, marginBottom: 22, animation: i >= chatHistory.length ? "enter 0.3s ease" : `enter 0.35s ease ${i * 0.08}s both` }}>
<div style={{ width: 28, height: 28, borderRadius: 7, flexShrink: 0, marginTop: 2, background: m.from === "atlas" ? "#1a1a1a" : "#e8e4dc", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.68rem", fontWeight: 700, color: m.from === "atlas" ? "#fff" : "#8a8478", fontFamily: m.from === "atlas" ? "Newsreader" : "Outfit" }}>
{m.from === "atlas" ? "A" : "M"}
</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.68rem", fontWeight: 600, color: "#a09a90", marginBottom: 5, textTransform: "uppercase", letterSpacing: "0.04em" }}>{m.from === "atlas" ? "Atlas" : "You"}</div>
<div style={{ fontSize: "0.88rem", color: "#2a2824", lineHeight: 1.72, whiteSpace: "pre-wrap" }}>
{m.text.split(/(\*\*.*?\*\*)/).map((seg, j) => seg.startsWith("**") && seg.endsWith("**") ? <strong key={j} style={{ fontWeight: 600, color: "#1a1a1a" }}>{seg.slice(2, -2)}</strong> : seg)}
</div>
</div>
</div>
))}
{typing && (
<div style={{ display: "flex", gap: 12, animation: "enter 0.2s ease" }}>
<div style={{ width: 28, height: 28, borderRadius: 7, background: "#1a1a1a", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.68rem", fontWeight: 700, color: "#fff", fontFamily: "Newsreader" }}>A</div>
<div style={{ display: "flex", gap: 5, paddingTop: 10 }}>
{[0,1,2].map(d => <div key={d} style={{ width: 5, height: 5, borderRadius: "50%", background: "#b5b0a6", animation: `blink 1s ease ${d * 0.15}s infinite` }} />)}
</div>
</div>
)}
<div ref={endRef} />
</div>
<div style={{ padding: "14px 32px 22px" }}>
<div style={{ display: "flex", gap: 8, padding: "5px 5px 5px 16px", background: "#fff", border: "1px solid #e0dcd4", borderRadius: 10, alignItems: "center", boxShadow: "0 1px 4px #1a1a1a06" }}>
<input value={input} onChange={e => setInput(e.target.value)} onKeyDown={e => e.key === "Enter" && send()} placeholder="Describe your thinking..."
style={{ flex: 1, border: "none", background: "none", fontSize: "0.86rem", fontFamily: "Outfit", color: "#1a1a1a", padding: "8px 0" }} />
<button onClick={send} style={{ padding: "9px 16px", borderRadius: 7, border: "none", background: input.trim() ? "#1a1a1a" : "#eae6de", color: input.trim() ? "#fff" : "#b5b0a6", fontSize: "0.78rem", fontWeight: 600, transition: "all 0.15s" }}>Send</button>
</div>
</div>
</div>
)}
{/* PRD */}
{tab === "prd" && (
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px", animation: "enter 0.3s ease" }}>
<div style={{ display: "flex", alignItems: "center", gap: 16, padding: "16px 20px", background: "#fff", border: "1px solid #e8e4dc", borderRadius: 10, marginBottom: 20, boxShadow: "0 1px 2px #1a1a1a05" }}>
<div style={{ fontFamily: "IBM Plex Mono", fontSize: "1.4rem", fontWeight: 500, color: "#1a1a1a", minWidth: 48 }}>{prdPct}%</div>
<div style={{ flex: 1 }}>
<div style={{ height: 4, borderRadius: 2, background: "#eae6de" }}>
<div style={{ height: "100%", borderRadius: 2, width: `${prdPct}%`, background: "#1a1a1a", transition: "width 0.6s ease" }} />
</div>
</div>
<span style={{ fontSize: "0.75rem", color: "#a09a90" }}>{prdData.filter(s => s.status === "done").length}/{prdData.length} approved</span>
</div>
{prdData.map((s, i) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "14px 18px", marginBottom: 4, background: "#fff", borderRadius: 8, border: "1px solid #e8e4dc", animation: `enter 0.3s ease ${i * 0.04}s both`, cursor: "pointer", transition: "border-color 0.12s" }}
onMouseEnter={e => e.currentTarget.style.borderColor = "#d0ccc4"}
onMouseLeave={e => e.currentTarget.style.borderColor = "#e8e4dc"}>
<div style={{ width: 24, height: 24, borderRadius: 6, flexShrink: 0, background: s.status === "done" ? "#2e7d3210" : s.status === "active" ? "#d4a04a12" : "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.65rem", fontWeight: 700, color: s.status === "done" ? "#2e7d32" : s.status === "active" ? "#9a7b3a" : "#c5c0b8" }}>
{s.status === "done" ? "✓" : s.status === "active" ? "◐" : "○"}
</div>
<span style={{ flex: 1, fontSize: "0.84rem", color: "#1a1a1a", fontWeight: 450 }}>{s.name}</span>
<div style={{ width: 60, height: 3, borderRadius: 2, background: "#eae6de" }}>
<div style={{ height: "100%", borderRadius: 2, width: `${s.pct}%`, background: s.status === "done" ? "#2e7d32" : s.status === "active" ? "#d4a04a" : "#d0ccc4" }} />
</div>
<span style={{ fontSize: "0.68rem", fontFamily: "IBM Plex Mono", color: s.status === "done" ? "#2e7d32" : "#a09a90", fontWeight: 500, minWidth: 28, textAlign: "right" }}>{s.pct}%</span>
</div>
))}
</div>
)}
{/* BUILD */}
{tab === "build" && (
<div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: 40, animation: "enter 0.3s ease" }}>
{project.status === "prd" ? (
<div style={{ textAlign: "center", maxWidth: 360 }}>
<div style={{ width: 56, height: 56, borderRadius: 14, background: "#fff", border: "1px solid #e8e4dc", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "1.4rem", margin: "0 auto 18px", boxShadow: "0 2px 8px #1a1a1a08" }}>🔒</div>
<h3 style={{ fontFamily: "Newsreader, serif", fontSize: "1.3rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 8 }}>Complete your PRD first</h3>
<p style={{ fontSize: "0.82rem", color: "#a09a90", lineHeight: 1.6, marginBottom: 20 }}>Approve all sections with Atlas, then the builder unlocks automatically.</p>
<div style={{ display: "inline-flex", padding: "8px 16px", borderRadius: 7, background: "#fff", border: "1px solid #e0dcd4", fontSize: "0.78rem", color: "#6b6560", fontWeight: 500 }}>
<span style={{ fontFamily: "IBM Plex Mono", fontWeight: 600, color: "#1a1a1a", marginRight: 6 }}>{prdPct}%</span>complete
</div>
</div>
) : (
<div style={{ width: "100%", maxWidth: 500 }}>
<h3 style={{ fontFamily: "Newsreader", fontSize: "1.2rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 18 }}>Build progress</h3>
{["Auth System", "Database", "Dashboard UI", "Rx Queue", "Inventory", "API"].map((f, i) => {
const pct = Math.max(0, Math.min(100, project.progress + (i * -12)));
return (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", marginBottom: 4, borderRadius: 8, background: "#fff", border: "1px solid #e8e4dc", animation: `enter 0.3s ease ${i * 0.05}s both` }}>
<StatusDot status={pct >= 100 ? "live" : pct > 0 ? "building" : "prd"} />
<span style={{ flex: 1, fontSize: "0.84rem", color: "#1a1a1a" }}>{f}</span>
<div style={{ width: 80, height: 3, borderRadius: 2, background: "#eae6de" }}>
<div style={{ height: "100%", width: `${pct}%`, borderRadius: 2, background: pct >= 100 ? "#2e7d32" : "#3d5afe" }} />
</div>
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.7rem", color: "#a09a90", minWidth: 28, textAlign: "right" }}>{pct}%</span>
</div>
);
})}
</div>
)}
</div>
)}
{/* DEPLOY */}
{tab === "deploy" && (
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px", animation: "enter 0.3s ease" }}>
<div style={{ maxWidth: 560 }}>
<h3 style={{ fontFamily: "Newsreader", fontSize: "1.2rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 4 }}>Deployment</h3>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 24 }}>Links, environments, and hosting for {project.name}</p>
{/* URLs card */}
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
<SectionLabel>Project URLs</SectionLabel>
{project.domain ? (
<>
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
<div style={{ width: 28, height: 28, borderRadius: 6, background: "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.7rem", color: "#8a8478" }}></div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.68rem", color: "#b5b0a6", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>Staging</div>
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#3d5afe", fontWeight: 500 }}>{project.domain}</div>
</div>
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Open </Btn>
</div>
{project.customDomain && (
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0", borderBottom: "1px solid #f0ece4" }}>
<div style={{ width: 28, height: 28, borderRadius: 6, background: "#2e7d3210", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.7rem", color: "#2e7d32" }}></div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.68rem", color: "#b5b0a6", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>Production</div>
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#2e7d32", fontWeight: 500 }}>{project.customDomain}</div>
</div>
<Tag color="#2e7d32" bg="#2e7d3210">SSL Active</Tag>
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>Open </Btn>
</div>
)}
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0" }}>
<div style={{ width: 28, height: 28, borderRadius: 6, background: "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.7rem", color: "#8a8478" }}></div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: "0.68rem", color: "#b5b0a6", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em" }}>Build repo</div>
<div style={{ fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#6b6560", fontWeight: 500 }}>{project.repo}</div>
</div>
<Btn variant="secondary" style={{ padding: "5px 12px", fontSize: "0.7rem" }}>View </Btn>
</div>
</>
) : (
<div style={{ padding: "18px 0", textAlign: "center" }}>
<p style={{ fontSize: "0.82rem", color: "#a09a90", marginBottom: 12 }}>No deployment yet complete your PRD and build to get a live URL.</p>
<div style={{ display: "inline-flex", padding: "6px 14px", borderRadius: 6, background: "#f6f4f0", fontSize: "0.75rem", color: "#8a8478" }}>
<span style={{ fontFamily: "IBM Plex Mono", marginRight: 6 }}>{project.progress}%</span> to deployment
</div>
</div>
)}
</Card>
{/* Custom domain */}
{project.domain && !project.customDomain && (
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
<SectionLabel>Custom Domain</SectionLabel>
<p style={{ fontSize: "0.82rem", color: "#6b6560", lineHeight: 1.6, marginBottom: 14 }}>
Point your own domain to this project. We'll handle SSL certificates automatically.
</p>
<div style={{ display: "flex", gap: 8 }}>
<input placeholder="app.yourdomain.com" style={{ flex: 1, padding: "9px 13px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", fontFamily: "IBM Plex Mono", color: "#1a1a1a" }} />
<Btn variant="primary" style={{ padding: "9px 18px" }}>Connect</Btn>
</div>
</Card>
)}
{/* Environment vars */}
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
<SectionLabel>Environment Variables</SectionLabel>
{project.domain ? (
<>
{[
{ key: "DATABASE_URL", val: "••••••••••••" },
{ key: "API_SECRET", val: "••••••••••••" },
{ key: "SMTP_HOST", val: "mail.stackless.dev" },
].map((env, i) => (
<div key={i} style={{ display: "flex", gap: 10, padding: "8px 0", borderBottom: i < 2 ? "1px solid #f0ece4" : "none", alignItems: "center" }}>
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", fontWeight: 500, color: "#1a1a1a", minWidth: 130 }}>{env.key}</span>
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", color: "#a09a90", flex: 1 }}>{env.val}</span>
<button style={{ background: "none", border: "none", fontSize: "0.7rem", color: "#a09a90", padding: "2px 6px" }}>Edit</button>
</div>
))}
<div style={{ marginTop: 14 }}>
<Btn variant="secondary" style={{ fontSize: "0.75rem", padding: "7px 14px" }}>+ Add variable</Btn>
</div>
</>
) : (
<p style={{ fontSize: "0.82rem", color: "#a09a90", padding: "10px 0" }}>Available after first build completes.</p>
)}
</Card>
{/* Deploy history */}
<Card style={{ padding: "22px" }} hover={false}>
<SectionLabel>Deploy History</SectionLabel>
{project.status === "live" ? (
<>
{[
{ version: "v1.2", time: "1 day ago", status: "live", note: "Added search filters" },
{ version: "v1.1", time: "5 days ago", status: "previous", note: "Bug fix: auth timeout" },
{ version: "v1.0", time: "2 weeks ago", status: "previous", note: "Initial launch" },
].map((d, i) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 0", borderBottom: i < 2 ? "1px solid #f0ece4" : "none" }}>
<span style={{ fontFamily: "IBM Plex Mono", fontSize: "0.78rem", fontWeight: 600, color: "#1a1a1a", minWidth: 36 }}>{d.version}</span>
<span style={{ flex: 1, fontSize: "0.82rem", color: "#6b6560" }}>{d.note}</span>
<span style={{ fontSize: "0.72rem", color: "#b5b0a6" }}>{d.time}</span>
{d.status === "live" && <Tag color="#2e7d32" bg="#2e7d3210">Current</Tag>}
</div>
))}
</>
) : project.domain ? (
<p style={{ fontSize: "0.82rem", color: "#a09a90", padding: "10px 0" }}>First deploy will appear here once the build completes.</p>
) : (
<p style={{ fontSize: "0.82rem", color: "#a09a90", padding: "10px 0" }}>No deploys yet.</p>
)}
</Card>
</div>
</div>
)}
{/* PROJECT SETTINGS */}
{tab === "projsettings" && (
<div style={{ flex: 1, overflow: "auto", padding: "28px 32px", animation: "enter 0.3s ease" }}>
<div style={{ maxWidth: 480 }}>
<h3 style={{ fontFamily: "Newsreader", fontSize: "1.2rem", fontWeight: 400, color: "#1a1a1a", marginBottom: 4 }}>Project Settings</h3>
<p style={{ fontSize: "0.8rem", color: "#a09a90", marginBottom: 24 }}>Configure {project.name}</p>
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
<SectionLabel>General</SectionLabel>
<InputField label="Project name" value={project.name} onChange={() => {}} />
<div style={{ marginBottom: 16 }}>
<div style={{ fontSize: "0.72rem", fontWeight: 600, color: "#6b6560", marginBottom: 6 }}>Description</div>
<textarea value={project.desc} onChange={() => {}} rows={2}
style={{ width: "100%", padding: "9px 13px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", color: "#1a1a1a", fontFamily: "Outfit" }} />
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<Btn variant="primary" style={{ padding: "8px 20px" }}>Save</Btn>
</div>
</Card>
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
<SectionLabel>Collaborators</SectionLabel>
<div style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0" }}>
<div style={{ width: 28, height: 28, borderRadius: "50%", background: "#f0ece4", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.68rem", fontWeight: 600, color: "#8a8478" }}>M</div>
<span style={{ flex: 1, fontSize: "0.82rem", color: "#1a1a1a" }}>Michael</span>
<Tag color="#6b6560" bg="#f0ece4">Owner</Tag>
</div>
<Btn variant="secondary" style={{ width: "100%", marginTop: 12, fontSize: "0.75rem" }}>+ Invite to project</Btn>
</Card>
<Card style={{ padding: "22px", marginBottom: 12 }} hover={false}>
<SectionLabel>Export</SectionLabel>
<p style={{ fontSize: "0.82rem", color: "#6b6560", marginBottom: 14, lineHeight: 1.6 }}>Download your PRD or project data for external use.</p>
<div style={{ display: "flex", gap: 8 }}>
<Btn variant="secondary" style={{ fontSize: "0.75rem" }}>Export PRD as PDF</Btn>
<Btn variant="secondary" style={{ fontSize: "0.75rem" }}>Export as JSON</Btn>
</div>
</Card>
<Card style={{ padding: "20px", borderColor: "#f5d5d5" }} hover={false}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div>
<div style={{ fontSize: "0.84rem", fontWeight: 500, color: "#d32f2f" }}>Delete project</div>
<div style={{ fontSize: "0.75rem", color: "#a09a90" }}>This action cannot be undone</div>
</div>
<Btn variant="secondary" style={{ color: "#d32f2f", borderColor: "#f5d5d5", padding: "6px 14px", fontSize: "0.72rem" }}>Delete</Btn>
</div>
</Card>
</div>
</div>
)}
</div>
</div>
{/* Right panel */}
<div style={{ width: 230, borderLeft: "1px solid #e8e4dc", background: "#fff", padding: "22px 18px", overflow: "auto", flexShrink: 0 }}>
<SectionLabel>Discovery</SectionLabel>
{discoveryPhases.map((ph, i) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 0", borderBottom: i < discoveryPhases.length - 1 ? "1px solid #f0ece4" : "none" }}>
<div style={{ width: 20, height: 20, borderRadius: 5, flexShrink: 0, background: ph.done ? "#2e7d3210" : ph.active ? "#d4a04a12" : "#f6f4f0", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.58rem", fontWeight: 700, color: ph.done ? "#2e7d32" : ph.active ? "#9a7b3a" : "#c5c0b8" }}>
{ph.done ? "✓" : ph.active ? "→" : i + 1}
</div>
<span style={{ fontSize: "0.78rem", fontWeight: ph.active ? 600 : 400, color: ph.done ? "#6b6560" : ph.active ? "#1a1a1a" : "#b5b0a6" }}>{ph.name}</span>
</div>
))}
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
<SectionLabel>Captured</SectionLabel>
{[
{ k: "Users", v: "Pharmacist" },
{ k: "Workflow", v: "Receive → Verify → Fill" },
{ k: "Scope", v: "Single-location MVP" },
{ k: "Open", v: "Multi-location support?" },
].map((item, i) => (
<div key={i} style={{ marginBottom: 14 }}>
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3, fontWeight: 600 }}>{item.k}</div>
<div style={{ fontSize: "0.8rem", color: "#4a4640", lineHeight: 1.45 }}>{item.v}</div>
</div>
))}
<div style={{ height: 1, background: "#f0ece4", margin: "16px 0" }} />
<SectionLabel>Project Info</SectionLabel>
{[
{ k: "Created", v: project.created },
{ k: "Last active", v: project.lastActive },
{ k: "Features", v: `${project.features} defined` },
].map((item, i) => (
<div key={i} style={{ marginBottom: 12 }}>
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3, fontWeight: 600 }}>{item.k}</div>
<div style={{ fontSize: "0.8rem", color: "#4a4640" }}>{item.v}</div>
</div>
))}
</div>
</div>
);
};
/* ─── DASHBOARD ─── */
const Home = ({ setActiveProject, setView }) => {
const [showNew, setShowNew] = useState(false);
return (
<div style={{ padding: "44px 52px", maxWidth: 900, fontFamily: "Outfit, sans-serif", animation: "enter 0.35s ease both" }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 36 }}>
<div>
<h1 style={{ fontFamily: "Newsreader, serif", fontSize: "1.9rem", fontWeight: 400, color: "#1a1a1a", letterSpacing: "-0.03em", lineHeight: 1.15, marginBottom: 4 }}>Projects</h1>
<p style={{ fontSize: "0.82rem", color: "#a09a90" }}>4 total · 2 in definition · 1 building · 1 live</p>
</div>
<Btn variant="primary" onClick={() => setShowNew(!showNew)} style={{ display: "flex", alignItems: "center", gap: 6 }}>
<span style={{ fontSize: "1rem", lineHeight: 1, fontWeight: 300 }}>+</span> New project
</Btn>
</div>
{showNew && (
<Card style={{ padding: "22px 26px", marginBottom: 28, animation: "enter 0.25s ease" }} hover={false}>
<div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 14 }}>
<div style={{ width: 28, height: 28, borderRadius: 7, background: "#1a1a1a", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.72rem", fontWeight: 700, color: "#fff", fontFamily: "Newsreader" }}>A</div>
<div>
<span style={{ fontSize: "0.82rem", fontWeight: 600, color: "#1a1a1a" }}>Atlas</span>
<span style={{ fontSize: "0.72rem", color: "#a09a90", marginLeft: 8 }}>product strategist</span>
</div>
</div>
<p style={{ fontSize: "0.84rem", color: "#6b6560", lineHeight: 1.6, marginBottom: 16 }}>Tell me what you want to build. One sentence is fine — I'll ask the right questions to figure out the rest.</p>
<div style={{ display: "flex", gap: 8 }}>
<input placeholder="e.g. A scheduling tool for independent yoga instructors..." style={{ flex: 1, padding: "10px 14px", borderRadius: 7, border: "1px solid #e0dcd4", background: "#faf8f5", fontSize: "0.84rem", fontFamily: "Outfit", color: "#1a1a1a" }} />
<Btn variant="primary">Start </Btn>
</div>
</Card>
)}
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{projects.map((p, i) => (
<button key={p.id} onClick={() => { setActiveProject(p); setView("project"); }}
style={{
width: "100%", display: "flex", alignItems: "center", padding: "18px 22px", borderRadius: 10,
background: "#fff", border: "1px solid #e8e4dc", cursor: "pointer", fontFamily: "Outfit",
transition: "all 0.15s", textAlign: "left", animation: `enter 0.35s ease ${i * 0.05}s both`,
boxShadow: "0 1px 2px #1a1a1a05",
}}
onMouseEnter={e => { e.currentTarget.style.borderColor = "#d0ccc4"; e.currentTarget.style.boxShadow = "0 2px 8px #1a1a1a0a"; }}
onMouseLeave={e => { e.currentTarget.style.borderColor = "#e8e4dc"; e.currentTarget.style.boxShadow = "0 1px 2px #1a1a1a05"; }}
>
<div style={{ width: 36, height: 36, borderRadius: 9, marginRight: 16, background: p.color + "12", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
<span style={{ fontFamily: "Newsreader, serif", fontSize: "1.05rem", fontWeight: 500, color: p.color }}>{p.name[0]}</span>
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 2 }}>
<span style={{ fontSize: "0.9rem", fontWeight: 600, color: "#1a1a1a" }}>{p.name}</span>
<Tag color={p.status === "live" ? "#2e7d32" : p.status === "building" ? "#3d5afe" : "#9a7b3a"} bg={p.status === "live" ? "#2e7d3210" : p.status === "building" ? "#3d5afe10" : "#d4a04a12"}>
<StatusDot status={p.status} /> {p.status === "live" ? "Live" : p.status === "building" ? "Building" : "Defining"}
</Tag>
</div>
<span style={{ fontSize: "0.78rem", color: "#a09a90" }}>{p.desc}</span>
</div>
<div style={{ display: "flex", gap: 28, alignItems: "center", flexShrink: 0 }}>
<div style={{ textAlign: "right" }}>
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 2 }}>Phase</div>
<div style={{ fontSize: "0.78rem", color: "#6b6560" }}>{p.phase}</div>
</div>
<div style={{ textAlign: "right" }}>
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 2 }}>Features</div>
<div style={{ fontSize: "0.78rem", color: "#6b6560" }}>{p.features}</div>
</div>
<div style={{ textAlign: "right", minWidth: 40 }}>
<div style={{ fontSize: "0.62rem", color: "#b5b0a6", textTransform: "uppercase", letterSpacing: "0.06em", marginBottom: 2 }}>Progress</div>
<div style={{ fontSize: "0.78rem", color: "#1a1a1a", fontWeight: 600, fontFamily: "IBM Plex Mono" }}>{p.progress}%</div>
</div>
<div style={{ width: 60, height: 3, borderRadius: 2, background: "#eae6de", flexShrink: 0 }}>
<div style={{ height: "100%", borderRadius: 2, width: `${p.progress}%`, background: p.status === "live" ? "#2e7d32" : p.status === "building" ? "#3d5afe" : "#d4a04a", transition: "width 0.6s ease" }} />
</div>
</div>
</button>
))}
</div>
</div>
);
};
/* ─── ROOT ─── */
export default function Stackless() {
const [view, setView] = useState("home");
const [activeProject, setActiveProject] = useState(null);
return (
<div style={{ display: "flex", height: "100vh", background: "#f6f4f0", overflow: "hidden" }}>
<FontLoader />
<Sidebar activeProject={activeProject} setActiveProject={setActiveProject} view={view} setView={setView} />
<div style={{ flex: 1, overflow: "auto" }}>
{view === "home" && !activeProject && <Home setActiveProject={setActiveProject} setView={setView} />}
{view === "activity" && <ActivityPage setActiveProject={setActiveProject} setView={setView} />}
{view === "settings" && <SettingsPage />}
{activeProject && <ProjectDetail project={activeProject} />}
</div>
</div>
);
}