TenantAtlas/apps/website/src/components/sections/PageHero.astro
ahmido 40039337d8
Some checks failed
Heavy Governance Lane / heavy-governance (push) Has been skipped
Main Confidence / confidence (push) Failing after 45s
Browser Lane / browser (push) Has been skipped
feat: implement homepage structure spec 216 (#254)
## Summary

Implements Spec 216 for the public website homepage in `apps/website`.

This reworks the homepage into the required narrative flow:
- hero with one dominant CTA, one secondary CTA, product-near visual, and bounded trust subclaims
- outcome framing section
- grouped capability model section
- explicit trust block before the final CTA
- dated progress teaser backed by changelog entries
- final CTA transition to contact

It also adds the full spec-kit artifact set for `specs/216-homepage-structure` and updates the smoke suite to prove section order, CTA hierarchy, onward route reachability, and mobile readability.

## Validation

- `corepack pnpm build:website`
- `cd apps/website && corepack pnpm exec playwright test`

## Notes

- Branch: `216-homepage-structure`
- Commit: `097f8e70`
- Remote branch has been pushed and is ready for review.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #254
2026-04-19 12:56:05 +00:00

107 lines
4.8 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 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;
---
<section class="pt-8 sm:pt-10 lg:pt-14">
<Container width="wide">
<div class="grid gap-6 lg:grid-cols-[1.35fr,0.85fr]" data-disclosure-layer="1">
<Card class="motion-rise overflow-hidden">
<div class="space-y-6">
<Badge>{hero.eyebrow}</Badge>
<div class="space-y-4">
<Headline as="h1" size="display" class="max-w-4xl">
{hero.title}
</Headline>
<Lead class="max-w-3xl" size="lead">
{hero.description}
</Lead>
</div>
{(hero.primaryCta || hero.secondaryCta) && (
<Cluster data-cta-cluster gap="md">
<PrimaryCTA cta={hero.primaryCta} />
{hero.secondaryCta && (
<SecondaryCTA cta={hero.secondaryCta} />
)}
</Cluster>
)}
{hero.trustSubclaims && hero.trustSubclaims.length > 0 && (
<ul class="grid gap-3 p-0 sm:grid-cols-3">
{
hero.trustSubclaims.map((claim) => (
<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)]">
{claim}
</li>
))
}
</ul>
)}
{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-5">
{hero.productVisual && (
<Card variant="accent" class="motion-rise overflow-hidden" data-hero-visual>
<img
src={hero.productVisual.src}
alt={hero.productVisual.alt}
class="w-full rounded-[var(--radius-lg)] object-cover"
loading="eager"
/>
</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 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>