120 lines
4.8 KiB
TypeScript
120 lines
4.8 KiB
TypeScript
|
|
/**
|
||
|
|
* IconicCases — the curated cover-story rail.
|
||
|
|
*
|
||
|
|
* Magazine grid of hand-picked iconic UFO incidents with painterly hero
|
||
|
|
* illustrations. Sits high on the homepage so the casual reader lands on
|
||
|
|
* recognisable history within two seconds: Roswell, Nimitz, Phoenix,
|
||
|
|
* Rendlesham.
|
||
|
|
*/
|
||
|
|
import Link from "next/link";
|
||
|
|
import { ICONIC_CASES, type IconicCase } from "@/lib/iconic-cases";
|
||
|
|
|
||
|
|
const ART_BASE = "/api/static/processing/case-art";
|
||
|
|
|
||
|
|
export function IconicCases({ locale }: { locale: "pt-br" | "en" }) {
|
||
|
|
const cases = ICONIC_CASES;
|
||
|
|
if (cases.length === 0) return null;
|
||
|
|
|
||
|
|
// Split: first 2 go into a wide hero pair, the rest tile below in a 3-up grid.
|
||
|
|
const [first, second, ...rest] = cases;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<section className="mx-auto max-w-7xl px-4 md:px-8 py-12 md:py-16">
|
||
|
|
<div className="flex items-end justify-between mb-6 flex-wrap gap-3">
|
||
|
|
<div>
|
||
|
|
<div className="text-[10px] font-mono uppercase tracking-[0.18em] text-[#5a6678] mb-2">
|
||
|
|
{locale === "en" ? "// The iconic record" : "// Os clássicos da divulgação"}
|
||
|
|
</div>
|
||
|
|
<h2 className="font-display text-2xl md:text-4xl text-[#e7ecf3] tracking-tight">
|
||
|
|
{locale === "en"
|
||
|
|
? "The cases every UFO enthusiast knows"
|
||
|
|
: "Os casos que todo entusiasta UFO conhece"}
|
||
|
|
</h2>
|
||
|
|
</div>
|
||
|
|
<div className="text-[11px] font-mono text-[#5a6678]">
|
||
|
|
{cases.length} {locale === "en" ? "stories" : "histórias"}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Hero pair */}
|
||
|
|
<div className="grid md:grid-cols-2 gap-4 md:gap-5 mb-4 md:mb-5">
|
||
|
|
{[first, second].filter(Boolean).map((c) => (
|
||
|
|
<HeroCard key={c.slug} c={c} locale={locale} />
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Rest grid */}
|
||
|
|
{rest.length > 0 && (
|
||
|
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-5">
|
||
|
|
{rest.map((c) => <CompactCard key={c.slug} c={c} locale={locale} />)}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</section>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
function HeroCard({ c, locale }: { c: IconicCase; locale: "pt-br" | "en" }) {
|
||
|
|
const title = locale === "pt-br" ? c.title_pt_br : c.title_en;
|
||
|
|
const blurb = locale === "pt-br" ? c.blurb_pt_br : c.blurb_en;
|
||
|
|
return (
|
||
|
|
<Link
|
||
|
|
href={c.link}
|
||
|
|
className="group relative block rounded-2xl overflow-hidden border border-[rgba(224,192,128,0.15)] bg-[#0d1220] aspect-[16/11]"
|
||
|
|
>
|
||
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||
|
|
<img
|
||
|
|
src={`${ART_BASE}/${c.image}`}
|
||
|
|
alt={title}
|
||
|
|
loading="lazy"
|
||
|
|
className="absolute inset-0 w-full h-full object-cover group-hover:scale-[1.03] transition-transform duration-700"
|
||
|
|
/>
|
||
|
|
<div className="absolute inset-0 bg-gradient-to-t from-[#0a0e1a] via-[#0a0e1a]/40 to-transparent" />
|
||
|
|
<div className="absolute inset-0 bg-gradient-to-r from-[#0a0e1a]/60 via-transparent to-transparent" />
|
||
|
|
<div className="absolute bottom-0 left-0 right-0 p-5 md:p-7">
|
||
|
|
<div className="text-[10px] font-mono uppercase tracking-[0.18em] text-[#e0c080] mb-2">
|
||
|
|
{c.year} · {c.tags.slice(0, 3).join(" · ")}
|
||
|
|
</div>
|
||
|
|
<h3 className="font-display text-2xl md:text-3xl text-white leading-tight mb-2 group-hover:text-[#e0c080] transition-colors">
|
||
|
|
{title}
|
||
|
|
</h3>
|
||
|
|
<p className="text-[13px] md:text-sm text-[#cbd2dd] leading-relaxed line-clamp-3 max-w-prose">
|
||
|
|
{blurb}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</Link>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
function CompactCard({ c, locale }: { c: IconicCase; locale: "pt-br" | "en" }) {
|
||
|
|
const title = locale === "pt-br" ? c.title_pt_br : c.title_en;
|
||
|
|
const blurb = locale === "pt-br" ? c.blurb_pt_br : c.blurb_en;
|
||
|
|
return (
|
||
|
|
<Link
|
||
|
|
href={c.link}
|
||
|
|
className="group block rounded-xl overflow-hidden border border-[rgba(224,192,128,0.15)] bg-[#0d1220] hover:border-[#e0c080]/50 transition-all"
|
||
|
|
>
|
||
|
|
<div className="relative aspect-[16/10] overflow-hidden">
|
||
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||
|
|
<img
|
||
|
|
src={`${ART_BASE}/${c.image}`}
|
||
|
|
alt={title}
|
||
|
|
loading="lazy"
|
||
|
|
className="absolute inset-0 w-full h-full object-cover group-hover:scale-[1.05] transition-transform duration-500"
|
||
|
|
/>
|
||
|
|
<div className="absolute inset-0 bg-gradient-to-t from-[#0a0e1a]/60 via-transparent to-transparent" />
|
||
|
|
<div className="absolute top-3 left-3 px-2 py-0.5 rounded bg-[#0a0e1a]/80 text-[10px] font-mono text-[#e0c080]">
|
||
|
|
{c.year}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="p-4 md:p-5">
|
||
|
|
<h3 className="font-display text-lg md:text-xl text-[#e7ecf3] leading-snug mb-2 group-hover:text-[#e0c080] transition-colors">
|
||
|
|
{title}
|
||
|
|
</h3>
|
||
|
|
<p className="text-[12px] text-[#9aa6b8] leading-relaxed line-clamp-3">
|
||
|
|
{blurb}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</Link>
|
||
|
|
);
|
||
|
|
}
|