Files
0pt/components/sections/hero-section.tsx
adminandClaude Sonnet 4.6 df63d3a6dd fix: production audit — sync catalog IDs, wire Stripe, mount Toaster, add 404
- Catalog IDs: synced /api/catalog and /katalog/[id] to consistent English slugs
  (nordic-companies, tech-decisionmakers, vehicles-fleet, financial-series,
  buying-signals, sanctions-pep); View links from catalog page were all broken
- Added 2 missing catalog detail entries: buying-signals, sanctions-pep
- featured-products-section: link cards to /katalog/{id} instead of hardcoded demo URL
- hero-section: 'See a dataset' now links to /katalog/nordic-companies
- layout.tsx: mount <Toaster> (sonner was installed but never mounted)
- bestall/forskott: pay() tries Stripe checkout first, falls back to demo mode
- api/payments/[order_id]/deposit: Stripe Checkout Session creation (501 if unconfigured)
- app/not-found.tsx: custom 404 page matching mono design system
- philosophy-section: fix TS type error on PRINCIPLES array

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 17:38:31 +02:00

126 lines
6.3 KiB
TypeScript

"use client";
import Link from "next/link";
import { useReveal } from "@/hooks/use-reveal";
import { useT } from "@/components/locale-provider";
type Loc = { en: string; sv: string };
type LocOrStr = string | Loc;
const STATS: [LocOrStr, Loc][] = [
["20", { en: "markets", sv: "marknader" }],
[{ en: "Per field", sv: "Per fält" }, { en: "provenance & confidence", sv: "härkomst & konfidens" }],
["50/50", { en: "start & approved", sv: "start & godkänt" }],
["GDPR", { en: "native, with DPA", sv: "native, med DPA" }],
];
// Data-native hero artifact: a miniature company table that "is" the product.
const ROWS: [string, LocOrStr, LocOrStr, 0 | 1 | 2 | 3][] = [
["556016-0680", "Volvo Group", "GLEIF", 3],
["915933149", "Equinor ASA", "Brreg", 3],
["25052943", "Maersk A/S", "CVR", 2],
["0112038-9", "Nokia Oyj", "PRH", 2],
["—", { en: "Email (FI)", sv: "E-post (FI)" }, { en: "license", sv: "licens" }, 0],
];
const PROV = ["var(--prov-0)", "var(--prov-1)", "var(--prov-2)", "var(--prov-3)"];
export function HeroSection() {
const ref = useReveal<HTMLElement>();
const t = useT();
// Resolve a value that may be a literal string (brand/number) or a localized {en,sv} pair.
const tx = (v: LocOrStr) => (typeof v === "string" ? v : t(v));
return (
<section ref={ref} className="grain-overlay relative overflow-hidden border-b border-border bg-background">
{/* graph-paper grid */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-[0.5]"
style={{
backgroundImage:
"linear-gradient(to right, var(--border) 1px, transparent 1px), linear-gradient(to bottom, var(--border) 1px, transparent 1px)",
backgroundSize: "32px 32px",
maskImage: "radial-gradient(120% 90% at 30% 20%, #000 30%, transparent 75%)",
WebkitMaskImage: "radial-gradient(120% 90% at 30% 20%, #000 30%, transparent 75%)",
}}
/>
{/* faint oversized wordmark */}
<div aria-hidden className="pointer-events-none absolute -right-10 top-1/2 -translate-y-1/2 select-none">
<span className="text-[34vw] font-semibold leading-none tracking-tighter text-border/50 md:text-[22vw]">0pt</span>
</div>
{/* single red marker — the only red at rest */}
<span aria-hidden className="animate-float pointer-events-none absolute right-[8%] top-[20%] inline-block h-3 w-3" style={{ backgroundColor: "var(--accent-red)" }} />
<div className="relative mx-auto grid max-w-[80rem] grid-cols-1 gap-16 px-6 py-28 lg:grid-cols-[1.1fr_0.9fr] lg:py-40">
<div className="flex flex-col justify-center">
<p className="reveal delay-1 font-mono text-[11px] uppercase tracking-[0.18em] text-muted-foreground">
{t({ en: "00 — Data services on demand", sv: "00 — Datatjänster på beställning" })}
</p>
<h1 className="reveal delay-2 mt-8 text-[clamp(3rem,8vw,7rem)] font-medium leading-[0.92] tracking-[-0.035em]">
{t({ en: "Order data.", sv: "Beställ data." })}
<br />
{t({ en: "Validate fields.", sv: "Validera fält." })}
</h1>
<p className="reveal delay-3 mt-8 max-w-[32ch] text-xl leading-snug text-muted-foreground">
{t({
en: "Build the job. Pay half. Track production. Approve. The source is always visible.",
sv: "Bygg jobbet. Betala hälften. Följ produktionen. Godkänn. Källan syns — alltid.",
})}
</p>
<div className="reveal delay-4 mt-12 flex flex-wrap gap-3">
<Link
href="/bestall"
className="bg-foreground px-7 py-4 font-mono text-[12px] uppercase tracking-[0.14em] text-background transition-colors hover:bg-[var(--accent-red)]"
>
{t({ en: "Build an order", sv: "Bygg beställning" })}
</Link>
<Link
href="/katalog/nordic-companies"
className="border border-foreground px-7 py-4 font-mono text-[12px] uppercase tracking-[0.14em] transition-colors hover:bg-foreground hover:text-background"
>
{t({ en: "See a dataset", sv: "Se ett dataset" })}
</Link>
</div>
<div className="reveal delay-5 mt-16 grid grid-cols-2 gap-px border border-border bg-border sm:grid-cols-4">
{STATS.map(([n, l], i) => (
<div key={i} className="group bg-background p-4 transition-colors hover:bg-muted/40">
<p className="text-2xl font-semibold tracking-[-0.03em]">{tx(n)}</p>
<p className="mt-2 font-mono text-[10px] uppercase tracking-[0.1em] text-muted-foreground">{t(l)}</p>
</div>
))}
</div>
</div>
{/* the artifact */}
<div className="reveal-fade flex items-center">
<div className="w-full border border-border bg-background">
<div className="flex items-center justify-between border-b border-border px-4 py-2.5">
<span className="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">dim_company</span>
<span className="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">{t({ en: "provenance", sv: "härkomst" })}</span>
</div>
{ROWS.map((r, i) => (
<div
key={i}
className={`reveal delay-${i + 1} group flex items-center justify-between border-b border-border px-4 py-3.5 transition-colors last:border-b-0 hover:bg-muted/40`}
>
<div className="min-w-0">
<p className="truncate text-sm transition-transform duration-200 group-hover:translate-x-0.5">{tx(r[1])}</p>
<p className="font-mono text-[10.5px] text-muted-foreground">{r[0]}</p>
</div>
<span className="ml-3 inline-flex items-center gap-1.5 border border-border px-1.5 py-0.5 font-mono text-[10px] uppercase text-muted-foreground">
<span
className="inline-block h-[7px] w-[7px]"
style={{ backgroundColor: r[3] === 0 ? "transparent" : PROV[r[3]], border: r[3] === 0 ? `1px solid ${PROV[0]}` : "none" }}
/>
{tx(r[2])}
</span>
</div>
))}
</div>
</div>
</div>
</section>
);
}