SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
3.7 KB · 68 lines tsx
Raw Blame History
1import { ArrowRight } from 'lucide-react';2import Link from 'next/link';3import { LazyGlobe } from '@/components/globe/lazy-globe';4import { Container } from '@/components/ui/section';5import { fmtInt } from '@/lib/format';6import { routes } from '@/lib/site';7import type { HomePayload } from '@/lib/types';8import { SearchButton } from './search-button';910function Count({ label, value, href }: { label: string; value: string; href: string }) {11  return (12    <Link href={href} className="group min-w-0 py-3 pr-4 md:py-0">13      <p className="tnum display text-2xl text-ink md:text-[2rem]">{value}</p>14      <p className="mt-1 text-2xs uppercase tracking-[0.12em] text-ink-3 group-hover:text-ink-2">{label}</p>15    </Link>16  );17}1819/** Hero: statement + live counts (from /stats/home, never hardcoded) + CTAs, with the compact globe island beside/below. */20export function HomeHero({ home }: { home: HomePayload | null }) {21  const s = home?.stats;22  const counts: [string, string, string][] = [23    ['Active satellites', fmtInt(s?.active_satellites), routes.satellites('status=ACTIVE')],24    ['Objects on orbit', fmtInt(s?.objects_on_orbit), routes.satellites('on_orbit=true')],25    ['Launches catalogued', fmtInt(home?.launches.total), routes.launches()],26    ['Active operators', fmtInt(s?.active_operators), routes.operators()],27    ['Countries', fmtInt(s?.active_countries), routes.countries()],28  ];29  return (30    <section className="relative overflow-hidden border-b border-rule">31      <div className="grid-bg pointer-events-none absolute inset-0" aria-hidden />32      <Container wide className="relative">33        <div className="grid items-center gap-4 lg:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)] lg:gap-8">34          <div className="pb-4 pt-10 md:pt-14 lg:py-20">35            <p className="eyebrow flex items-center gap-2">36              <span className="dot pulse text-active" aria-hidden /> Satellite Index · live catalogue37            </p>38            <h1 className="display mt-4 max-w-[14ch] text-[2.5rem] text-ink sm:text-5xl md:text-6xl lg:text-[4.25rem]">39              The world&rsquo;s orbital infrastructure, mapped and indexed.40            </h1>41            <p className="mt-5 max-w-xl text-[15px] leading-relaxed text-ink-2 md:text-lg">42              Every satellite, constellation, operator, launch, debris object and reentry in Earth orbit — one canonical, source-attributed43              database with live SGP4 positions, orbital history and a transparent methodology.44            </p>45            <div className="mt-7 flex flex-wrap items-center gap-3">46              <Link href={routes.explore()} className="inline-flex h-12 items-center justify-center gap-2 rounded-md bg-accent px-5 text-sm font-semibold text-accent-ink hover:brightness-110">47                Explore orbit <ArrowRight className="size-4" aria-hidden />48              </Link>49              <SearchButton />50            </div>51            <dl className="mt-9 grid grid-cols-2 gap-x-4 gap-y-2 border-y border-rule py-2 sm:grid-cols-3 md:mt-12 md:grid-cols-5 md:gap-6 md:border-0 md:py-0">52              {counts.map(([label, value, href]) => (53                <div key={label} className="[&:nth-child(5)]:col-span-2 sm:[&:nth-child(5)]:col-span-1 md:border-l md:border-rule md:pl-4 md:first:border-0 md:first:pl-0">54                  <Count label={label} value={value} href={href} />55                </div>56              ))}57            </dl>58            {!home && <p className="mt-2 text-xs text-warn">Live statistics unavailable — the catalogue API did not respond.</p>}59          </div>60          <div className="relative -mx-4 h-[46vh] min-h-[300px] md:mx-0 md:h-[520px] lg:h-[680px]">61            <LazyGlobe variant="hero" />62          </div>63        </div>64      </Container>65    </section>66  );67}68