Overlapping photo pair with credentials beside a numbered accordion of reasons (from Marbrook)
"use client";
import { ArrowUpRight, Minus, Plus } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import {
Accordion,
AccordionItem,
AccordionPanel,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import type { AboutContent } from "../_types/about";
import type { Surface } from "../_types/common";
import { defaultAbout1Content } from "./data";
import { SectionHeader, surfaceClass, toneFor } from "./section-header";
/** Text tones for each band. On `primary` the copy reads off the inverted
* foreground; everywhere else it uses the regular muted scale. */
const tones = {
dark: {
border: "border-primary-foreground/10",
faint: "text-primary-foreground/35",
icon: "border-primary-foreground/20 text-secondary",
muted: "text-primary-foreground/60",
strong: "text-primary-foreground",
value: "text-secondary",
},
light: {
border: "border-border",
faint: "text-muted-foreground/70",
icon: "border-input text-primary",
muted: "text-muted-foreground",
strong: "text-primary",
value: "text-primary",
},
};
/** An overlapping pair of photos with credentials, beside a numbered
* accordion of reasons and two calls to action. */
export default function About1({
content = defaultAbout1Content,
surface = "primary",
}: {
content?: AboutContent;
surface?: Surface;
}) {
const t = surface === "primary" ? tones.dark : tones.light;
const [main, inset] = content.images;
const [primaryAction, secondaryAction] = content.actions ?? [];
return (
<section className={cn("py-20 sm:py-28", surfaceClass[surface])}>
<div className="mx-auto max-w-7xl px-5 sm:px-8 lg:px-12">
<div className="grid gap-14 lg:grid-cols-12 lg:gap-16">
<div className="lg:col-span-5">
<div className="relative aspect-4/5 overflow-hidden rounded-lg">
<Image
alt={main.alt}
className="object-cover"
fill
sizes="(min-width: 1024px) 40vw, 100vw"
src={main.src}
/>
</div>
{inset && (
<div
className={cn(
"relative -mt-16 ml-auto hidden w-3/5 rounded-lg border-8 sm:block",
surface === "primary"
? "border-primary"
: "border-background",
surface === "muted" && "border-muted",
)}
>
<div className="relative aspect-4/3 overflow-hidden rounded-sm">
<Image
alt={inset.alt}
className="object-cover"
fill
sizes="30vw"
src={inset.src}
/>
</div>
</div>
)}
{content.credentials && content.credentials.length > 0 && (
<div
className={cn(
"mt-8 grid grid-cols-3 gap-6 border-t pt-6 sm:mt-4",
t.border,
)}
>
{content.credentials.map((credential) => (
<div key={credential.label}>
<p className={cn("font-display text-2xl", t.value)}>
{credential.value}
</p>
<p className={cn("mt-1 text-xs leading-snug", t.muted)}>
{credential.label}
</p>
</div>
))}
</div>
)}
</div>
<div className="lg:col-span-7">
<SectionHeader
description={content.description}
eyebrow={content.eyebrow}
title={content.title}
tone={toneFor(surface)}
/>
{content.items && content.items.length > 0 ? (
<Accordion
className={cn("mt-10 border-t", t.border)}
defaultValue={[content.items[0].id]}
>
{content.items.map((item) => (
<AccordionItem
className={cn("border-b last:border-b-0", t.border)}
key={item.id}
value={item.id}
>
<AccordionTrigger
className="items-center gap-6 py-6 text-left [&:not([data-panel-open])_[data-icon=minus]]:hidden [&[data-panel-open]_[data-icon=plus]]:hidden"
icon={
<span
className={cn(
"flex size-8 shrink-0 items-center justify-center rounded-md border",
t.icon,
)}
>
<Plus className="size-4" data-icon="plus" />
<Minus className="size-4" data-icon="minus" />
</span>
}
>
<span className="flex items-baseline gap-5">
<span className={cn("font-mono text-xs", t.faint)}>
{item.id}
</span>
<span className={cn("font-display text-2xl", t.strong)}>
{item.title}
</span>
</span>
</AccordionTrigger>
<AccordionPanel
className={cn(
"max-w-xl pb-7 pl-11 text-sm leading-relaxed",
t.muted,
)}
>
{item.body}
</AccordionPanel>
</AccordionItem>
))}
</Accordion>
) : (
<div className={cn("mt-8 space-y-5 leading-relaxed", t.muted)}>
{content.body?.map((paragraph) => (
<p key={paragraph.slice(0, 40)}>{paragraph}</p>
))}
</div>
)}
{(primaryAction || secondaryAction) && (
<div className="mt-10 flex flex-wrap items-center gap-4">
{primaryAction && (
<Button
className="border-none bg-secondary px-7 text-secondary-foreground shadow-none hover:bg-secondary/90"
render={<Link href={primaryAction.href} />}
size="xl"
>
{primaryAction.label}
</Button>
)}
{secondaryAction && (
<Link
className={cn(
"group inline-flex items-center gap-2 text-sm underline-offset-4 transition-colors hover:underline",
surface === "primary"
? "text-primary-foreground/70 hover:text-secondary"
: "text-primary",
)}
href={secondaryAction.href}
>
{secondaryAction.label}
<ArrowUpRight className="size-4 transition-transform duration-300 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</Link>
)}
</div>
)}
</div>
</div>
</div>
</section>
);
}