import LanguageSwitch from "@/components/LanguageSwitch";
import { Button } from "@/components/ui/button";
import { useAuth } from "@/contexts/AuthContext";
import { useI18n } from "@/i18n/I18nProvider";
import {
ArrowRight,
Boxes,
Brain,
Eye,
Github,
LayoutDashboard,
RefreshCw,
ShieldCheck,
Terminal,
} from "lucide-react";
import { Link } from "react-router-dom";
const REPO_URL = "https://github.com/WebchemistCorp/devist";
export default function Landing() {
const { session } = useAuth();
return (
<div className="min-h-screen bg-background text-foreground">
<Header signedIn={!!session} />
<main>
<Hero signedIn={!!session} />
<About />
<Reso />
<Loops />
<Workflow />
</main>
<Footer />
</div>
);
}
function Header({ signedIn }: { signedIn: boolean }) {
const { t } = useI18n();
return (
<header className="sticky top-0 z-10 border-b bg-background/80 backdrop-blur">
<div className="mx-auto max-w-6xl px-6 h-14 flex items-center justify-between">
<Link to="/" className="flex items-center gap-2 font-semibold">
<span className="text-base">Devist</span>
<span className="text-xs text-muted-foreground hidden sm:inline">
{t("landing.tagline")}
</span>
</Link>
<nav className="flex items-center gap-2">
<LanguageSwitch />
<a
href={REPO_URL}
target="_blank"
rel="noreferrer"
className="text-sm text-muted-foreground hover:text-foreground p-2 rounded-md hover:bg-muted"
title="GitHub"
>
<Github size={16} />
</a>
{signedIn ? (
<Link to="/dashboard">
<Button size="sm">{t("common.openDashboard")}</Button>
</Link>
) : (
<Link to="/login">
<Button size="sm" variant="ghost">
{t("common.signIn")}
</Button>
</Link>
)}
</nav>
</div>
</header>
);
}
function Hero({ signedIn }: { signedIn: boolean }) {
const { t } = useI18n();
return (
<section className="mx-auto max-w-6xl px-6 pt-20 pb-16">
<div className="max-w-3xl">
<h1 className="text-4xl sm:text-5xl font-bold tracking-tight">
{t("landing.tagline")}
</h1>
<p className="mt-5 text-lg text-muted-foreground leading-relaxed">
{t("landing.hero.subhead")}
</p>
<div className="mt-8 flex items-center gap-3 flex-wrap">
<Link to={signedIn ? "/dashboard" : "/login"}>
<Button size="lg" className="gap-2">
{signedIn
? t("common.openDashboard")
: t("landing.hero.getStarted")}
<ArrowRight size={16} />
</Button>
</Link>
<a href={REPO_URL} target="_blank" rel="noreferrer">
<Button size="lg" variant="outline" className="gap-2">
<Github size={16} />
{t("landing.hero.viewGitHub")}
</Button>
</a>
</div>
<pre className="mt-10 rounded-lg border bg-muted/40 p-4 text-sm overflow-x-auto">
<code>
<span className="text-muted-foreground">
{t("landing.hero.codeComment.install")}
</span>
{"\n"}
cargo install devist{"\n\n"}
<span className="text-muted-foreground">
{t("landing.hero.codeComment.scaffold")}
</span>
{"\n"}
devist init my-app --template react-vite{"\n"}
devist start my-app --dev
</code>
</pre>
</div>
</section>
);
}
function About() {
const { t } = useI18n();
const cards = [
{
icon: <Boxes size={20} />,
title: t("landing.about.templates.title"),
body: t("landing.about.templates.body"),
},
{
icon: <Eye size={20} />,
title: t("landing.about.worker.title"),
body: t("landing.about.worker.body"),
},
{
icon: <LayoutDashboard size={20} />,
title: t("landing.about.dashboard.title"),
body: t("landing.about.dashboard.body"),
},
];
return (
<section className="border-t bg-muted/20">
<div className="mx-auto max-w-6xl px-6 py-16">
<div className="max-w-2xl">
<h2 className="text-2xl sm:text-3xl font-bold tracking-tight">
{t("landing.about.title")}
</h2>
<p className="mt-3 text-muted-foreground">
{t("landing.about.subtitle")}
</p>
</div>
<div className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{cards.map((c) => (
<div
key={c.title}
className="rounded-lg border bg-card p-5 space-y-3"
>
<div className="inline-flex items-center justify-center w-9 h-9 rounded-md bg-foreground/5">
{c.icon}
</div>
<h3 className="font-semibold">{c.title}</h3>
<p className="text-sm text-muted-foreground leading-relaxed">
{c.body}
</p>
</div>
))}
</div>
</div>
</section>
);
}
function Reso() {
const { t } = useI18n();
const dimensions = [
{
title: t("landing.reso.dim.scope.title"),
body: t("landing.reso.dim.scope.body"),
},
{
title: t("landing.reso.dim.priority.title"),
body: t("landing.reso.dim.priority.body"),
},
{
title: t("landing.reso.dim.source.title"),
body: t("landing.reso.dim.source.body"),
},
];
return (
<section className="border-t bg-gradient-to-b from-background to-muted/30">
<div className="mx-auto max-w-6xl px-6 py-20">
<div className="max-w-3xl">
<div className="inline-flex items-center gap-2 text-xs font-mono uppercase tracking-widest text-muted-foreground">
<Brain size={14} />
<span>{t("landing.reso.eyebrow")}</span>
</div>
<h2 className="mt-4 text-3xl sm:text-4xl font-bold tracking-tight">
{t("landing.reso.title")}
</h2>
<p className="mt-4 text-lg text-muted-foreground leading-relaxed">
{t("landing.reso.lead")}
</p>
</div>
<div className="mt-12 grid gap-4 sm:grid-cols-3">
{dimensions.map((d) => (
<div
key={d.title}
className="rounded-lg border bg-card p-5 space-y-2"
>
<div className="font-mono text-xs uppercase tracking-widest text-muted-foreground">
{d.title}
</div>
<p className="text-sm leading-relaxed">{d.body}</p>
</div>
))}
</div>
<p className="mt-10 max-w-3xl text-base text-muted-foreground italic leading-relaxed">
{t("landing.reso.tagline")}
</p>
</div>
</section>
);
}
function Loops() {
const { t } = useI18n();
const loops = [
{
name: "advice",
cadence: t("landing.loops.advice.cadence"),
body: t("landing.loops.advice.body"),
},
{
name: "verify",
cadence: t("landing.loops.verify.cadence"),
body: t("landing.loops.verify.body"),
},
{
name: "audit",
cadence: t("landing.loops.audit.cadence"),
body: t("landing.loops.audit.body"),
},
{
name: "consolidate",
cadence: t("landing.loops.consolidate.cadence"),
body: t("landing.loops.consolidate.body"),
},
{
name: "jobs",
cadence: t("landing.loops.jobs.cadence"),
body: t("landing.loops.jobs.body"),
},
{
name: "main",
cadence: t("landing.loops.main.cadence"),
body: t("landing.loops.main.body"),
},
];
const rails = [
t("landing.loops.rails.softDelete"),
t("landing.loops.rails.protected"),
t("landing.loops.rails.budget"),
t("landing.loops.rails.attribution"),
];
return (
<section className="border-t">
<div className="mx-auto max-w-6xl px-6 py-20">
<div className="max-w-3xl">
<div className="inline-flex items-center gap-2 text-xs font-mono uppercase tracking-widest text-muted-foreground">
<RefreshCw size={14} />
<span>{t("landing.loops.eyebrow")}</span>
</div>
<h2 className="mt-4 text-3xl sm:text-4xl font-bold tracking-tight">
{t("landing.loops.title")}
</h2>
<p className="mt-4 text-lg text-muted-foreground leading-relaxed">
{t("landing.loops.lead")}
</p>
</div>
<div className="mt-12 overflow-hidden rounded-lg border bg-card">
<table className="w-full text-sm">
<thead className="bg-muted/50 text-xs font-mono uppercase tracking-widest text-muted-foreground">
<tr>
<th className="text-left px-5 py-3 font-semibold">
{t("landing.loops.col.loop")}
</th>
<th className="text-left px-5 py-3 font-semibold">
{t("landing.loops.col.cadence")}
</th>
<th className="text-left px-5 py-3 font-semibold">
{t("landing.loops.col.purpose")}
</th>
</tr>
</thead>
<tbody className="divide-y">
{loops.map((l) => (
<tr key={l.name} className="hover:bg-muted/30">
<td className="px-5 py-3 font-mono font-semibold text-foreground">
{l.name}
</td>
<td className="px-5 py-3 font-mono text-muted-foreground whitespace-nowrap">
{l.cadence}
</td>
<td className="px-5 py-3 text-muted-foreground leading-relaxed">
{l.body}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="mt-10 rounded-lg border border-foreground/10 bg-muted/30 p-6">
<div className="flex items-center gap-2 mb-4">
<ShieldCheck size={16} className="text-foreground" />
<h3 className="font-semibold">
{t("landing.loops.rails.title")}
</h3>
</div>
<ul className="space-y-2 text-sm text-muted-foreground">
{rails.map((r) => (
<li key={r} className="flex items-start gap-3">
<span className="mt-2 inline-block w-1 h-1 rounded-full bg-foreground/40 shrink-0" />
<span className="leading-relaxed">{r}</span>
</li>
))}
</ul>
</div>
</div>
</section>
);
}
function Workflow() {
const { t } = useI18n();
const steps = [
{
n: "1",
title: t("landing.workflow.install.title"),
cmd: "cargo install devist",
hint: t("landing.workflow.install.hint"),
},
{
n: "2",
title: t("landing.workflow.scaffold.title"),
cmd: "devist init my-app --template react-vite",
hint: t("landing.workflow.scaffold.hint"),
},
{
n: "3",
title: t("landing.workflow.startWorker.title"),
cmd: "devist worker start",
hint: t("landing.workflow.startWorker.hint"),
},
];
return (
<section className="border-t">
<div className="mx-auto max-w-6xl px-6 py-16">
<div className="max-w-2xl">
<h2 className="text-2xl sm:text-3xl font-bold tracking-tight">
{t("landing.workflow.title")}
</h2>
<p className="mt-3 text-muted-foreground">
{t("landing.workflow.subtitle")}
</p>
</div>
<ol className="mt-10 space-y-5">
{steps.map((s) => (
<li
key={s.n}
className="flex gap-5 items-start rounded-lg border bg-card p-5"
>
<div className="shrink-0 inline-flex items-center justify-center w-8 h-8 rounded-full bg-foreground text-background text-sm font-semibold">
{s.n}
</div>
<div className="flex-1 min-w-0 space-y-2">
<h3 className="font-semibold">{s.title}</h3>
<pre className="rounded border bg-muted/40 px-3 py-2 text-sm overflow-x-auto">
<code className="flex items-center gap-2">
<Terminal
size={14}
className="shrink-0 text-muted-foreground"
/>
{s.cmd}
</code>
</pre>
<p className="text-sm text-muted-foreground">{s.hint}</p>
</div>
</li>
))}
</ol>
</div>
</section>
);
}
function Footer() {
const { t } = useI18n();
return (
<footer className="border-t">
<div className="mx-auto max-w-6xl px-6 py-8 flex items-center justify-between text-sm text-muted-foreground">
<span>{t("footer.attribution")}</span>
<div className="flex items-center gap-4">
<a
href={REPO_URL}
target="_blank"
rel="noreferrer"
className="hover:text-foreground"
>
{t("footer.github")}
</a>
<a
href={`${REPO_URL}/blob/main/LICENSE`}
target="_blank"
rel="noreferrer"
className="hover:text-foreground"
>
{t("footer.license")}
</a>
</div>
</div>
</footer>
);
}