"use client";
import { useMemo } from "react";
import { ArrowRight } from "lucide-react";
import styles from "./story-landing.module.css";
import { Link } from "@/i18n/routing";
import { useExampleAction } from "@/components/examples/use-example-action";
import { ShellSection, useShell } from "@reopt-ai/opt-shell";
import { deriveHeroGradient } from "@reopt-ai/opt-palette";
import {
Badge,
Button,
Card,
CardContent,
FaqAccordion,
} from "@reopt-ai/opt-ui";
import {
storyProductCaseStudies,
storyProductCustomerStories,
storyProductFaqItems,
storyProductFeatures,
storyProductNewsItems,
storyProductStats,
} from "./fixtures";
const FEATURE_ACCENTS = [
"bg-indigo-500",
"bg-emerald-500",
"bg-sky-500",
"bg-amber-500",
"bg-violet-500",
"bg-rose-500",
] as const;
const TRUST_NAMES = [
"F사 (핀테크)",
"S사 (SaaS)",
"C사 (커머스)",
"D사 (헬스케어)",
"E사 (에듀테크)",
"G사 (게임)",
"H사 (물류)",
"I사 (미디어)",
] as const;
/* ── Syntax-highlighted code block for hero ── */
const Kw = ({ children }: { children: React.ReactNode }) => (
<span className="text-purple-400">{children}</span>
);
const Fn = ({ children }: { children: React.ReactNode }) => (
<span className="text-cyan-300">{children}</span>
);
const Str = ({ children }: { children: React.ReactNode }) => (
<span className="text-emerald-400">{children}</span>
);
const Cm = ({ children }: { children: React.ReactNode }) => (
<span className="text-slate-400">{children}</span>
);
function HeroCodeBlock() {
return (
<div className="rounded-2xl border border-white/15 bg-slate-950/70 shadow-2xl backdrop-blur-sm">
<div className="flex items-center gap-2 border-b border-white/10 px-5 py-3">
<div className="h-3 w-3 rounded-full bg-red-400/60" />
<div className="h-3 w-3 rounded-full bg-yellow-400/60" />
<div className="h-3 w-3 rounded-full bg-green-400/60" />
<span className="ml-2 text-xs text-white/55">dashboard.tsx</span>
</div>
<pre className="overflow-hidden px-5 py-4 font-mono text-[12.5px] leading-[1.75] text-slate-300">
<code>
<Kw>import</Kw>
{" { "}
<Fn>ShellPage</Fn>
{", "}
<Fn>ShellSection</Fn>
{" }"}
{"\n "}
<Kw>from</Kw> <Str>"@reopt-ai/opt-shell"</Str>
{"\n"}
<Kw>import</Kw>
{" { "}
<Fn>DashboardGrid</Fn>
{", "}
<Fn>Card</Fn>
{" }"}
{"\n "}
<Kw>from</Kw> <Str>"@reopt-ai/opt-ui"</Str>
{"\n\n"}
<Kw>export function</Kw> <Fn>Dashboard</Fn>
{"() {"}
{"\n "}
<Kw>return</Kw> ({"\n "}
{"<"}
<Fn>ShellPage</Fn> recipe=<Str>"dashboard"</Str>
{">"}
{"\n "}
{"<"}
<Fn>ShellSection</Fn> title=<Str>"Overview"</Str>
{">"}
{"\n "}
{"<"}
<Fn>DashboardGrid</Fn>
{"\n "}
{"stats={"}
<Cm>metrics</Cm>
{"} columns={"}
<span className="text-amber-300">4</span>
{"}"}
{"\n "}
{"/>"}
{"\n "}
{"</"}
<Fn>ShellSection</Fn>
{">"}
{"\n "}
{"</"}
<Fn>ShellPage</Fn>
{">"}
{"\n )"}
{"\n}"}
</code>
</pre>
</div>
);
}
export function StoryLandingHome() {
const { policy } = useShell();
const { notReal } = useExampleAction();
const isCompact = policy.density === "compact";
const featured = storyProductCaseStudies[0];
const heroColors = useMemo(() => {
const theme = policy.theme;
if (theme.mode === "generated" && theme.generatedLightTokens) {
const accent = theme.generatedLightTokens["--opt-accent"] ?? "#4f46e5";
return deriveHeroGradient(accent);
}
return {
base: "linear-gradient(135deg, #0f172a 0%, #1e1b4b 50%, #0f172a 100%)",
glow: "radial-gradient(ellipse at top right, rgba(99,102,241,0.25), transparent 60%), radial-gradient(ellipse at bottom left, rgba(14,165,233,0.15), transparent 50%)",
};
}, [policy.theme]);
/* density-aware spacing tokens */
const heroPy = isCompact ? "py-12 lg:py-16" : "py-14 lg:py-20";
const sectionPy = isCompact ? "py-10 lg:py-14" : "py-16 lg:py-20";
return (
<div className={styles.landing}>
{/* ── Hero ── */}
<section className="relative overflow-hidden">
<div
className="absolute inset-0"
style={{ background: heroColors.base }}
/>
<div
className="absolute inset-0"
style={{ background: heroColors.glow }}
/>
<div
className={`relative mx-auto max-w-7xl px-6 md:px-8 lg:grid lg:grid-cols-[1fr_420px] lg:items-center lg:gap-14 ${heroPy}`}
>
<div>
<h1
className={`leading-[1.25] font-semibold tracking-[-0.045em] text-balance text-white ${isCompact ? "text-3xl sm:text-4xl" : "text-4xl sm:text-5xl lg:text-[3.5rem] lg:leading-[1.15]"}`}
>
제품 경험을 설계하고,
<br />더 빠르게 출시하세요
</h1>
<p
className={`mt-5 max-w-xl leading-[1.8] text-pretty text-indigo-100/75 ${isCompact ? "text-base" : "text-lg"}`}
>
reopt는 디자인 시스템부터 데이터 그리드, 에디터까지 — 제품
팀이 필요한 모든 UI 인프라를 하나의 플랫폼에서 제공합니다.
</p>
<div className={styles.heroActions}>
<button
type="button"
onClick={() => notReal("무료로 시작하기")}
className="inline-flex min-h-12 items-center justify-center rounded-xl bg-white px-6 py-3 text-sm font-semibold text-slate-900 shadow-sm transition-colors hover:bg-slate-100 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-transparent focus-visible:outline-none"
>
무료로 시작하기
</button>
<Link
href="/examples/story-landing/proof"
className="inline-flex min-h-12 items-center justify-center gap-2 rounded-xl border border-white/25 px-6 py-3 text-sm font-semibold text-white transition-colors hover:bg-white/10 focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:outline-none"
>
고객 사례 보기 <ArrowRight size={16} aria-hidden />
</Link>
</div>
</div>
{/* Code illustration */}
<div className="hidden min-w-0 lg:block">
<HeroCodeBlock />
</div>
</div>
</section>
{/* ── Stats belt ── */}
<div className={styles.statsBelt}>
<dl className={styles.stats}>
{storyProductStats.map((stat) => (
<div key={stat.id}>
<dt>{stat.title}</dt>
<dd>
<strong>{stat.value}</strong>
<span>{stat.change}</span>
</dd>
</div>
))}
</dl>
</div>
{/* ── Trust band ── */}
<div className="border-b border-[var(--opt-border)]">
<div className="mx-auto flex max-w-7xl flex-col items-center gap-4 px-6 py-6 md:flex-row md:gap-8 md:px-8">
<span className="shrink-0 text-sm font-medium text-[var(--opt-text-tertiary)]">
2,400+ 팀이 신뢰합니다
</span>
<div className="flex flex-wrap items-center justify-center gap-x-6 gap-y-2 md:justify-start">
{TRUST_NAMES.map((name) => (
<span
key={name}
className="text-sm font-semibold text-[var(--opt-text-tertiary)] transition-colors hover:text-[var(--opt-text-secondary)]"
>
{name}
</span>
))}
</div>
</div>
</div>
{/* ── News ── */}
<div className={`mx-auto max-w-7xl px-6 md:px-8 ${sectionPy}`}>
<ShellSection
className={styles.section}
title="새로운 소식"
description="reopt 플랫폼의 최신 업데이트와 고객 성공 사례를 확인하세요."
>
<div className="grid gap-6 md:grid-cols-3">
{storyProductNewsItems.map((item, i) => (
<Card
padding="none"
shadow={false}
key={item.id}
aria-label={`${item.title} 자세히 보기`}
onClick={() => notReal(item.title)}
className="group flex cursor-pointer flex-col overflow-hidden border-[var(--opt-border)] transition-shadow hover:shadow-lg focus-visible:ring-2 focus-visible:ring-[var(--opt-accent)] focus-visible:outline-none"
>
<div
className={`relative flex h-44 items-end overflow-hidden ${item.gradient}`}
>
<span
aria-hidden="true"
className="pointer-events-none absolute -top-6 -right-2 text-[7rem] leading-none font-black text-black/5 dark:text-white/10"
>
{String(i + 1).padStart(2, "0")}
</span>
<div className="absolute top-4 left-4">
<Badge variant="default">{item.category}</Badge>
</div>
</div>
<CardContent className="flex flex-1 flex-col p-6">
<h3 className="text-lg leading-snug font-semibold text-[var(--opt-text)]">
{item.title}
</h3>
<p className="mt-2 line-clamp-2 text-sm leading-6 text-[var(--opt-text-secondary)]">
{item.description}
</p>
<div className="mt-auto inline-flex items-center gap-1 pt-5 text-sm font-medium text-[var(--opt-accent)] transition-transform group-hover:translate-x-0.5">
자세히 보기
<span aria-hidden="true">→</span>
</div>
</CardContent>
</Card>
))}
</div>
</ShellSection>
</div>
{/* ── Features ── */}
<div
id="features"
className="scroll-mt-20 border-y border-[var(--opt-border)] bg-[var(--opt-bg-subtle)]"
>
<div className={`mx-auto max-w-7xl px-6 md:px-8 ${sectionPy}`}>
<ShellSection
className={styles.section}
title={
<>
반복된 개선으로 탄생한
<br />
Brand-first AI Platform
</>
}
description="제품 팀이 반복적으로 만드는 UI 패턴을 검증된 컴포넌트와 프레임워크로 해결합니다."
>
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
{storyProductFeatures.map((feature, i) => (
<Card
padding="none"
shadow={false}
key={feature.id}
className="border-[var(--opt-border)] bg-[var(--opt-bg)] transition-shadow hover:shadow-md"
>
<CardContent className={isCompact ? "p-4" : "p-6"}>
<div
className={`mb-3 h-1 w-10 rounded-full ${FEATURE_ACCENTS[i % FEATURE_ACCENTS.length]}`}
/>
<h3 className="text-lg font-semibold tracking-tight text-[var(--opt-text)]">
{feature.title}
</h3>
<p className="mt-2 text-sm leading-6 text-[var(--opt-text-secondary)]">
{feature.description}
</p>
</CardContent>
</Card>
))}
</div>
</ShellSection>
</div>
</div>
{/* ── Customer stories ── */}
<div className={`mx-auto max-w-7xl px-6 md:px-8 ${sectionPy}`}>
<ShellSection
className={styles.section}
title="팀의 규모와 관계없이, 더 빠르게 출시하세요"
description="reopt를 도입한 팀들의 실제 성과를 확인하세요."
action={
<Link
href="/examples/story-landing/proof"
className={styles.textLink}
>
더 많은 사례 보기 <ArrowRight size={16} aria-hidden />
</Link>
}
>
<div className="flex flex-col gap-6">
{/* Featured story — full-width card */}
<div className="overflow-hidden rounded-2xl border border-[var(--opt-border)] bg-[linear-gradient(135deg,rgba(238,242,255,0.6),rgba(224,231,255,0.4))] dark:bg-[linear-gradient(135deg,rgba(30,27,75,0.5),rgba(49,46,129,0.3))]">
<div className="grid lg:grid-cols-[1fr_320px]">
<div className={isCompact ? "p-6" : "p-8"}>
<Badge variant="default">{featured.industry}</Badge>
<h3 className="mt-4 text-xl font-semibold text-[var(--opt-text)] sm:text-2xl">
{featured.company}, reopt 도입으로 개발 시간{" "}
{featured.results[0].metric} 단축
</h3>
<p className="mt-3 max-w-2xl text-sm leading-7 text-[var(--opt-text-secondary)]">
“{featured.quote}”
</p>
<div className="mt-6 flex flex-wrap gap-8">
{featured.results.map((r) => (
<div key={r.label}>
<div className="text-2xl font-bold text-[var(--opt-accent)]">
{r.metric}
</div>
<div className="text-xs text-[var(--opt-text-secondary)]">
{r.label}
</div>
</div>
))}
</div>
<Link
href="/examples/story-landing/proof"
className="mt-6 inline-flex items-center gap-1 text-sm font-medium text-[var(--opt-accent)]"
>
자세히 보기 →
</Link>
</div>
<div className={styles.featuredResult}>
<span className={styles.resultLabel}>
더 적은 반복, 더 빠른 개발
</span>
<strong>{featured.results[0].metric}</strong>
<span>개발 시간 단축</span>
<div className={styles.comparison} aria-hidden="true">
<div>
<span>도입 전</span>
<i />
</div>
<div>
<span>도입 후</span>
<i />
</div>
</div>
</div>
</div>
</div>
{/* Smaller story cards */}
<div className="grid gap-6 lg:grid-cols-3">
{storyProductCustomerStories.map((story) => (
<Card
padding="none"
shadow={false}
key={story.id}
className="border-[var(--opt-border)] bg-[var(--opt-bg)]"
>
<CardContent className="flex flex-col gap-4 p-6">
<Badge variant="default" className="self-start">
{story.industry}
</Badge>
<div>
<div className="text-3xl font-bold text-[var(--opt-accent)]">
{story.metric}
</div>
<div className="text-sm font-medium text-[var(--opt-text-secondary)]">
{story.metricLabel}
</div>
</div>
<p className="text-sm leading-6 text-[var(--opt-text-secondary)]">
“{story.quote}”
</p>
<div className="text-sm font-semibold text-[var(--opt-text)]">
— {story.company}
</div>
</CardContent>
</Card>
))}
</div>
</div>
</ShellSection>
</div>
{/* ── CTA ── */}
<div className="border-t border-[var(--opt-border)] bg-[var(--opt-bg-subtle)]">
<div
className={`mx-auto max-w-7xl px-6 text-center md:px-8 ${sectionPy}`}
>
<h2 className="text-3xl font-semibold tracking-tight text-[var(--opt-text)] sm:text-4xl">
지금 바로 시작하세요
</h2>
<p className="mt-4 text-base text-[var(--opt-text-secondary)]">
무료로 시작하고, 팀 규모에 맞춰 확장하세요.
</p>
<div className="mt-7 flex flex-wrap justify-center gap-3">
<Button size="lg" onClick={() => notReal("무료로 시작하기")}>
무료로 시작하기
</Button>
<Link href="/docs" className={styles.secondaryLink}>
문서 살펴보기 <ArrowRight size={16} aria-hidden />
</Link>
</div>
</div>
</div>
{/* ── FAQ ── */}
<div className={`mx-auto max-w-7xl px-6 md:px-8 ${sectionPy}`}>
<ShellSection
className={styles.section}
title="자주 묻는 질문"
description="reopt 플랫폼에 대해 가장 많이 궁금해하시는 내용입니다."
>
<div className={styles.faq}>
<FaqAccordion items={[...storyProductFaqItems]} />
</div>
</ShellSection>
</div>
</div>
);
}