TenantAtlas/apps/website/src/components/sections/PageHero.astro
Ahmed Darrazi 478ca5801b
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 54s
chore: commit workspace changes (agent session)
2026-04-22 00:17:33 +02:00

240 lines
13 KiB
Plaintext

---
import Badge from '@/components/primitives/Badge.astro';
import Card from '@/components/primitives/Card.astro';
import Cluster from '@/components/primitives/Cluster.astro';
import Container from '@/components/primitives/Container.astro';
import Headline from '@/components/content/Headline.astro';
import HeroDashboard from '@/components/content/HeroDashboard.astro';
import Lead from '@/components/content/Lead.astro';
import Metric from '@/components/content/Metric.astro';
import PrimaryCTA from '@/components/content/PrimaryCTA.astro';
import SecondaryCTA from '@/components/content/SecondaryCTA.astro';
import type { HeroContent, MetricItem } from '@/types/site';
interface Props {
calloutDescription?: string;
calloutTitle?: string;
hero: HeroContent;
metrics?: MetricItem[];
}
const { calloutDescription, calloutTitle, hero, metrics = [] } = Astro.props;
const isHomepageHero = Astro.url.pathname === '/';
const heroHeadlineSize = isHomepageHero ? 'page' : 'display';
const heroLeadSize = isHomepageHero ? 'body' : 'lead';
const heroPrimaryAnchor = hero.primaryAnchor ?? 'headline';
---
<section
class:list={[
isHomepageHero ? 'hero-gradient pt-2 sm:pt-8 lg:pt-14' : 'pt-8 sm:pt-10 lg:pt-14',
]}
data-hero-root
data-hero-surface={isHomepageHero ? 'homepage' : 'page'}
data-homepage-hero={isHomepageHero ? 'true' : undefined}
data-hero-primary-anchor={isHomepageHero && heroPrimaryAnchor === 'composition' ? 'composition' : undefined}
data-section={isHomepageHero ? 'hero' : undefined}
>
<Container width="wide">
{isHomepageHero ? (
<div class="space-y-6 sm:space-y-8 lg:space-y-10" data-disclosure-layer="1" data-hero-layout>
<div class="grid gap-6 lg:grid-cols-[minmax(0,0.82fr)_minmax(22rem,1.18fr)] lg:items-start lg:gap-10">
<div class="motion-rise flex flex-col gap-5 sm:gap-6 lg:max-w-[40rem]" data-hero-panel="text">
<div class="space-y-5" data-hero-anchor-group>
<div data-hero-text-core>
<div data-hero-eyebrow data-hero-segment="eyebrow">
<Badge>{hero.eyebrow}</Badge>
</div>
<div class="mt-3 space-y-4 sm:mt-5 sm:space-y-5">
<div
data-hero-heading
data-hero-primary-anchor={heroPrimaryAnchor === 'headline' ? 'headline' : undefined}
data-hero-segment="headline"
>
<Headline
as="h1"
size="page"
class="max-w-[13ch] text-balance text-[length:clamp(2.7rem,4.6vw,4.8rem)] leading-[0.94] tracking-[-0.045em]"
>
{hero.titleHtml ? <Fragment set:html={hero.titleHtml} /> : hero.title}
</Headline>
</div>
<div
data-hero-copy-role="supporting"
data-hero-supporting-copy
data-hero-segment="supporting-copy"
>
<Lead class="max-w-[36rem] text-[1.02rem] leading-8 text-[var(--color-copy)] sm:text-[1.08rem]">
{hero.description}
</Lead>
</div>
</div>
</div>
{(hero.primaryCta || hero.secondaryCta) && (
<div data-hero-cta-pair data-hero-segment="cta-pair">
<Cluster data-cta-cluster gap="sm" class="items-center sm:gap-[var(--space-cluster)]">
<PrimaryCTA cta={hero.primaryCta} size="lg" />
{hero.secondaryCta && (
<SecondaryCTA cta={hero.secondaryCta} />
)}
</Cluster>
</div>
)}
</div>
</div>
<div
class="motion-rise lg:pt-1"
style="animation-delay: 120ms;"
data-hero-panel="dashboard"
data-hero-primary-anchor={heroPrimaryAnchor === 'product-visual' ? 'product-visual' : undefined}
data-hero-visual
data-hero-visual-style="governance-surface"
data-hero-segment="product-near-visual"
>
<div class="overflow-hidden rounded-[2rem] border border-[color:var(--color-border)] bg-[linear-gradient(180deg,rgba(255,255,255,0.9),rgba(246,248,252,0.94))] p-3 shadow-[var(--shadow-panel-strong)] sm:p-4">
{hero.visualFocus && (
<div class="mb-3 rounded-[1.45rem] border border-[color:var(--color-border-subtle)] bg-white/88 px-4 py-4 sm:px-5">
<p class="m-0 text-[0.72rem] font-semibold uppercase tracking-[var(--tracking-eyebrow)] text-[var(--color-brand-500)]">
{hero.visualFocus.eyebrow}
</p>
<p class="mt-2 max-w-[42rem] text-sm font-semibold leading-6 text-[var(--color-ink-900)] sm:text-[0.98rem]">
{hero.visualFocus.title}
</p>
<ul class="mt-3 grid gap-2 p-0 sm:grid-cols-3">
{hero.visualFocus.points.map((point) => (
<li class="list-none rounded-[1rem] border border-[color:var(--color-border-subtle)] bg-[var(--surface-muted)] px-3 py-2 text-sm font-medium leading-5 text-[var(--color-ink-800)]">
{point}
</li>
))}
</ul>
</div>
)}
<HeroDashboard />
</div>
</div>
</div>
{hero.trustSubclaims && hero.trustSubclaims.length > 0 && (
<div class="motion-rise space-y-2" data-hero-segment="trust-subclaims" data-hero-trust-signals>
<ul class="flex flex-wrap gap-3 p-0">
{hero.trustSubclaims.map((claim) => (
<li class="list-none rounded-full border border-[color:var(--color-border)] bg-white/80 px-4 py-1.5 text-sm font-medium text-[var(--color-ink-800)]">
{claim}
</li>
))}
</ul>
</div>
)}
</div>
) : (
<!-- Subpage hero: card-based 2-col layout -->
<div
class="grid gap-5 sm:gap-6 lg:grid-cols-[minmax(0,1.08fr)_minmax(20rem,0.92fr)] lg:items-start"
data-disclosure-layer="1"
data-hero-layout
>
<Card class="motion-rise overflow-hidden" data-hero-panel="text">
<div class="space-y-4 sm:space-y-6">
<div data-hero-text-core>
<div data-hero-eyebrow data-hero-segment="eyebrow">
<Badge>{hero.eyebrow}</Badge>
</div>
<div class="mt-3 space-y-4 sm:mt-4 sm:space-y-5">
<div data-hero-heading data-hero-segment="headline">
<Headline
as="h1"
size={heroHeadlineSize}
class="max-w-3xl text-balance"
>
{hero.titleHtml ? <Fragment set:html={hero.titleHtml} /> : hero.title}
</Headline>
</div>
<div data-hero-copy-role="supporting" data-hero-supporting-copy data-hero-segment="supporting-copy">
<Lead class="max-w-2xl" size={heroLeadSize}>
{hero.description}
</Lead>
</div>
</div>
</div>
{(hero.primaryCta || hero.secondaryCta) && (
<div data-hero-cta-pair data-hero-segment="cta-pair">
<Cluster data-cta-cluster gap="sm" class="sm:gap-[var(--space-cluster)]">
<PrimaryCTA cta={hero.primaryCta} />
{hero.secondaryCta && (
<SecondaryCTA cta={hero.secondaryCta} />
)}
</Cluster>
</div>
)}
{hero.highlights && hero.highlights.length > 0 && !hero.trustSubclaims?.length && (
<ul class="grid gap-3 p-0 sm:grid-cols-3">
{hero.highlights.map((highlight) => (
<li class="list-none rounded-[1.1rem] border border-[color:var(--color-line)] bg-white/70 px-4 py-3 text-sm font-medium text-[var(--color-ink-800)]">
{highlight}
</li>
))}
</ul>
)}
</div>
</Card>
<div class="grid gap-4 sm:gap-5" data-hero-panel="supporting">
{hero.productVisual && (
<Card
variant="accent"
class="motion-rise overflow-hidden"
data-hero-segment="product-near-visual"
data-hero-visual
>
<img
src={hero.productVisual.src}
alt={hero.productVisual.alt}
class="max-h-[22rem] w-full rounded-[var(--radius-lg)] object-cover object-top"
loading="eager"
/>
</Card>
)}
{hero.trustSubclaims && hero.trustSubclaims.length > 0 && (
<Card variant="subtle" class="motion-rise" data-hero-segment="trust-subclaims">
<div class="space-y-3" data-hero-trust-signals>
<p class="m-0 text-[0.72rem] font-semibold uppercase tracking-[var(--tracking-eyebrow)] text-[var(--color-copy)]">
Early trust
</p>
<ul class="grid gap-2.5 p-0">
{hero.trustSubclaims.map((claim) => (
<li class="list-none rounded-[1rem] border border-[color:var(--color-line)] bg-white/82 px-4 py-3 text-sm font-medium text-[var(--color-ink-800)]">
{claim}
</li>
))}
</ul>
</div>
</Card>
)}
{!hero.productVisual && (calloutTitle || calloutDescription) && (
<Card variant="accent" class="motion-rise">
<p class="m-0 text-sm font-semibold uppercase tracking-[0.15em] text-[var(--color-brand)]">
Trust-first launch surface
</p>
{calloutTitle && (
<h2 class="mt-4 font-[var(--font-display)] text-3xl font-bold leading-tight text-[var(--color-ink-900)]">
{calloutTitle}
</h2>
)}
{calloutDescription && (
<p class="mt-3 text-base leading-7 text-[var(--color-copy)]">{calloutDescription}</p>
)}
</Card>
)}
{metrics.length > 0 && (
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-1">
{metrics.map((metric) => <Metric item={metric} />)}
</div>
)}
</div>
</div>
)}
</Container>
</section>