Files
SnapOtter/apps/landing/src/components/Hero.astro
T
SnapOtterandGitHub 00b651c9f8 feat(i18n): 21-language pipeline, landing/docs/API wiring, landing+API translations
Shared Claude Code translation pipeline (scripts/i18n, no API key) plus Astro/VitePress/Scalar i18n wiring. Landing and API reference translated into all 20 languages; docs i18n wiring + English source anchors. The translated docs markdown (apps/docs/<locale>/**, 3,620 files) follows in a companion PR because it exceeds GitHub's per-PR CI file limit.
2026-07-11 13:01:55 +08:00

72 lines
3.0 KiB
Plaintext

---
// biome-ignore-all lint/correctness/noUnusedImports: Astro template consumes component imports.
// biome-ignore-all lint/correctness/noUnusedVariables: Astro template consumes frontmatter values.
import { t } from "@/i18n";
import CategoryCards from "./CategoryCards.astro";
import HeroQuickstart from "./HeroQuickstart.astro";
import HeroSearch from "./HeroSearch.astro";
import TrustSignals from "./TrustSignals.astro";
interface Props {
locale?: string;
}
const { locale = "en" } = Astro.props;
const trustBadges = [
t(locale, "home.hero.badge.selfHosted"),
t(locale, "home.hero.badge.openSource"),
t(locale, "home.hero.badge.airGap"),
t(locale, "home.hero.badge.compliance"),
];
---
<section class="relative overflow-hidden px-6 pt-24 pb-12 md:pt-24 md:pb-16">
<!-- Gradient mesh background (CSS fallback beneath the shadergradient loop) -->
<div class="hero-mesh" aria-hidden="true"></div>
<div class="hero-mesh-extra" aria-hidden="true"></div>
<!-- ShaderGradient warm-wash loop: captured, self-hosted, no framework. -->
<!-- Poster (still) doubles as the reduced-motion and load-failure fallback. -->
<div class="hero-gradient" aria-hidden="true">
<!-- biome-ignore lint/a11y/useMediaCaption: decorative silent background loop with no audio track. -->
<video autoplay muted loop playsinline preload="metadata" tabindex="-1">
<source src="/hero-gradient.webm?v=5" type="video/webm" />
<source src="/hero-gradient.mp4?v=5" type="video/mp4" />
</video>
</div>
<div class="relative mx-auto max-w-4xl text-center">
<!-- Enterprise trust badges -->
<div class="animate-fade-up flex flex-wrap items-center justify-center gap-2">
{trustBadges.map((badge) => (
<span class="inline-flex items-center gap-1.5 rounded-full border border-white/25 bg-black/25 px-3 py-1 text-xs font-medium text-white backdrop-blur-sm">
<svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
{badge}
</span>
))}
</div>
<!-- Headline: names the buying trigger (the network boundary), not "the cloud" -->
<h1 class="animate-fade-up mt-8 font-display text-4xl font-extrabold tracking-tight text-white sm:text-5xl md:text-6xl lg:text-7xl" style="animation-delay: 0.1s; text-shadow: 0 2px 30px rgba(90, 20, 0, 0.28);">
{t(locale, "home.hero.title")}
</h1>
<!-- Subtitle: capability breadth plus the infrastructure surfaces -->
<p class="animate-fade-up mx-auto mt-6 max-w-2xl text-lg text-white/90 md:text-xl" style="animation-delay: 0.15s; text-shadow: 0 1px 16px rgba(90, 20, 0, 0.22);">
{t(locale, "home.hero.subtitle")}
</p>
<!-- Hero tool search -->
<HeroSearch locale={locale} />
<!-- Category cards -->
<CategoryCards locale={locale} />
<!-- Self-host one-command quickstart + live demo -->
<HeroQuickstart locale={locale} />
<!-- Stats strip -->
<TrustSignals locale={locale} />
</div>
</section>