- gifting tab CTAs now point at our /collections pages (seed defaults + one-off PB migration rewriting shop.mozimo.in cta_urls, idempotent) - Hero/Footer/our-world/journal 'shop' links -> /chocolate; bespoke bulk-ordering -> /gifting#corporate-bulk - still on Shopify (deliberate, until Phase 2): PDP buy button, Header bag+account, footer policies
116 lines
4.0 KiB
TypeScript
116 lines
4.0 KiB
TypeScript
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
import Header from "@/components/Header";
|
|
import Footer from "@/components/Footer";
|
|
import Reveal from "@/components/Reveal";
|
|
import Rise from "@/components/Rise";
|
|
import { journalPosts } from "@/data/catalog";
|
|
|
|
export const Route = createFileRoute("/journal/$slug")({
|
|
component: JournalArticle,
|
|
});
|
|
|
|
function JournalArticle() {
|
|
const { slug } = Route.useParams();
|
|
const post = journalPosts.find((p) => p.slug === slug);
|
|
|
|
if (!post) {
|
|
return (
|
|
<div className="flex min-h-screen flex-col bg-ivory-50">
|
|
<Header />
|
|
<main className="wrap flex flex-1 flex-col items-center justify-center py-40 text-center">
|
|
<p className="eyebrow text-orange-500">404</p>
|
|
<h1 className="display mt-6 text-4xl text-ink-900">
|
|
This page has been{" "}
|
|
<em className="italic text-orange-500">eaten.</em>
|
|
</h1>
|
|
<Link to="/journal" className="btn btn-ghost mt-10">
|
|
Back to the Journal
|
|
</Link>
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-ivory-50">
|
|
<Header />
|
|
<main>
|
|
{/* article hero */}
|
|
<section className="relative overflow-hidden bg-ivory-50">
|
|
<div className="wrap pb-14 pt-40 md:pt-48">
|
|
<Link
|
|
to="/journal"
|
|
className="link-quiet eyebrow text-orange-600 hover:text-orange-500"
|
|
>
|
|
← The Mozimo Journal
|
|
</Link>
|
|
<h1 className="display mt-7 max-w-4xl text-[clamp(2.4rem,5.5vw,4.8rem)] text-ink-900">
|
|
<Rise delay={0.25}>{post.title}</Rise>
|
|
</h1>
|
|
<Reveal delay={0.45}>
|
|
<p className="eyebrow mt-8 text-[0.62rem]! text-ink-500">
|
|
{post.date} · {post.readTime} read
|
|
</p>
|
|
</Reveal>
|
|
</div>
|
|
<Reveal delay={0.3} variant="clip">
|
|
<div className="wrap">
|
|
<img
|
|
src={post.image}
|
|
alt=""
|
|
width={1760}
|
|
height={1000}
|
|
className="aspect-[21/9] w-full rounded-2xl object-cover"
|
|
/>
|
|
</div>
|
|
</Reveal>
|
|
</section>
|
|
|
|
{/* body */}
|
|
<section className="bg-ivory-100 text-ink-900">
|
|
<div className="wrap max-w-3xl py-16 md:py-24">
|
|
{post.body.map((para, i) => (
|
|
<Reveal key={i} delay={Math.min(i * 0.05, 0.3)}>
|
|
<p className="mt-7 text-[1.08rem] leading-[1.85] text-ink-700 first:mt-0 first:text-[1.2rem] first:leading-[1.7] first:text-ink-900">
|
|
{para}
|
|
</p>
|
|
{post.pullQuote && i === 1 && (
|
|
<blockquote className="my-12 text-center">
|
|
<p className="display text-[clamp(1.6rem,3.5vw,2.6rem)] italic text-orange-500">
|
|
“{post.pullQuote}”
|
|
</p>
|
|
<footer className="rule mx-auto mt-8 w-40" />
|
|
</blockquote>
|
|
)}
|
|
</Reveal>
|
|
))}
|
|
|
|
<Reveal delay={0.2}>
|
|
<div className="mt-16 border-t border-ink-900/15 pt-10 text-center">
|
|
<p className="font-script text-3xl text-orange-500">
|
|
taste the story
|
|
</p>
|
|
<div className="mt-6 flex flex-wrap justify-center gap-4">
|
|
<a
|
|
href="/chocolate"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="btn btn-dark"
|
|
>
|
|
Shop the Collection
|
|
</a>
|
|
<Link to="/journal" className="btn btn-ghost">
|
|
More from the Journal
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|