mirror of
https://github.com/logos-co/logos-web.git
synced 2026-08-30 21:51:09 +00:00
155 lines
5.1 KiB
TypeScript
155 lines
5.1 KiB
TypeScript
import Image from 'next/image'
|
|
|
|
import type { HeroSection } from '@repo/content/schemas'
|
|
|
|
import { IconMask } from '@/components/icons/icon-mask'
|
|
import ContentWidth from '@/components/layout/content-width'
|
|
import { Reveal } from '@/components/motion/reveal'
|
|
import { Button } from '@/components/ui'
|
|
import { resolveBasecampInstallCtaLinkProps } from '@/lib/basecamp-release-links'
|
|
|
|
type Props = {
|
|
data: HeroSection
|
|
}
|
|
|
|
function DownloadIcon() {
|
|
return <IconMask src="/icons/download.svg" className="size-[15px]" />
|
|
}
|
|
|
|
function getButtonIcon(iconOverride?: string) {
|
|
if (iconOverride === 'download') {
|
|
return <DownloadIcon />
|
|
}
|
|
if (iconOverride === 'none') {
|
|
return false
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
function StatusCard({
|
|
status,
|
|
compact = false,
|
|
}: {
|
|
status: NonNullable<HeroSection['status']>
|
|
compact?: boolean
|
|
}) {
|
|
return (
|
|
<div
|
|
className={
|
|
compact
|
|
? 'relative w-[393px] rounded-xl'
|
|
: 'relative w-full rounded-xl py-3'
|
|
}
|
|
>
|
|
<div className="flex flex-col items-start gap-2">
|
|
<span className="text-eyebrow font-semibold leading-[1.35] rounded bg-[#ffd328] px-1 py-0.5 text-brand-dark-green uppercase">
|
|
{status.label}
|
|
</span>
|
|
<p
|
|
className={`text-mono-s text-brand-dark-green ${
|
|
compact ? 'w-[345px]' : 'w-full max-w-[680px]'
|
|
}`}
|
|
>
|
|
{status.body}
|
|
{status.secondaryCta ? (
|
|
<>
|
|
{' '}
|
|
<Button
|
|
href={status.secondaryCta.href}
|
|
variant="link"
|
|
icon={false}
|
|
className="inline-flex cursor-pointer align-baseline"
|
|
>
|
|
{status.secondaryCta.label}
|
|
</Button>
|
|
</>
|
|
) : null}
|
|
</p>
|
|
{status.cta ? (
|
|
<Button
|
|
{...resolveBasecampInstallCtaLinkProps(status.cta)}
|
|
variant={status.cta.variant ?? 'secondary'}
|
|
icon={getButtonIcon(status.cta.iconOverride)}
|
|
className="cursor-pointer"
|
|
>
|
|
{status.cta.label}
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function TechOverviewHero({ data }: Props) {
|
|
return (
|
|
<section className="relative mb-13 bg-brand-off-white px-3 pt-10 pb-13 md:mb-25 md:pt-8 md:pb-20">
|
|
<ContentWidth className="relative flex flex-col gap-10 md:gap-[70px]">
|
|
{/* Status card — desktop, anchored top-left over the spine */}
|
|
<div className="absolute top-0 left-0 hidden h-[99px] w-[393px] md:block">
|
|
{data.status ? <StatusCard status={data.status} compact /> : null}
|
|
</div>
|
|
|
|
{/* Header image — mobile, anchored top-left over the spine */}
|
|
<div className="absolute top-2.5 left-0 h-[75px] w-[107px] overflow-hidden md:hidden">
|
|
<div className="absolute top-[-29px] left-[-48px] h-[208px] w-[166px]">
|
|
<Image
|
|
src={
|
|
data.background?.src ??
|
|
'/images/technology-stack/header-top.jpg'
|
|
}
|
|
alt={data.background?.alt ?? ''}
|
|
fill
|
|
sizes="166px"
|
|
className="object-cover"
|
|
/>
|
|
<div className="absolute inset-0 bg-black/30" />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Eyebrow — right column */}
|
|
<p className="text-mono-s ml-[calc(50%+6px)] w-[calc(50%-18px)] max-w-[178px] whitespace-pre-line text-brand-dark-green md:max-w-[226px] xl:w-[226px] xl:max-w-none">
|
|
{data.eyebrow}
|
|
</p>
|
|
|
|
{/* Headline */}
|
|
<Reveal className="w-full md:w-[464px] md:self-center xl:ml-[464px] xl:self-start">
|
|
<h1 className="text-h2 text-center whitespace-pre-line text-brand-dark-green">
|
|
{data.headline}
|
|
</h1>
|
|
</Reveal>
|
|
|
|
{/* Body + CTAs — right column */}
|
|
<div className="ml-[calc(50%+6px)] flex w-[calc(50%-18px)] max-w-[178px] flex-col items-start gap-6 md:max-w-[226px] xl:w-[226px] xl:max-w-none">
|
|
<p className="text-mono-s text-brand-dark-green">{data.body}</p>
|
|
{data.ctas && data.ctas.length > 0 ? (
|
|
<div className="flex flex-col items-start gap-2.5 xl:flex-row">
|
|
{data.ctas.map((cta) => (
|
|
<Button
|
|
key={cta.label}
|
|
{...resolveBasecampInstallCtaLinkProps(cta)}
|
|
variant={cta.variant ?? 'secondary'}
|
|
icon={getButtonIcon(cta.iconOverride)}
|
|
className="cursor-pointer"
|
|
>
|
|
{cta.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
) : null}
|
|
<div className="mt-4 hidden h-px w-[calc(100vw-24px)] -translate-x-[calc(50vw-6px)] bg-brand-dark-green/10 lg:block xl:hidden" />
|
|
</div>
|
|
|
|
{/* Divider + status — mobile only */}
|
|
<div className="md:hidden">
|
|
<div className="h-px w-full bg-brand-dark-green/10" />
|
|
{data.status ? (
|
|
<div className="mt-6">
|
|
<StatusCard status={data.status} />
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</ContentWidth>
|
|
</section>
|
|
)
|
|
}
|