Foundation for the adaline-based landing rebuild: - globals.css: warm-cream light theme + Material-3 dark theme mapped onto the existing CSS-var names; CSS-keyframe entrance + IntersectionObserver reveal motion (to replace framer-motion incrementally) - layout.tsx: self-host Instrument Sans/Serif, Newsreader and JetBrains Mono via next/font/google; drop the external Google Fonts <link> - _headers: tighten CSP style/font-src to 'self' now that fonts are local - components/ui: Container, Reveal, Eyebrow, Badge, Button, SectionHeading - lib/landing-content.ts: feature-section copy for the six product screens - public/images: six product screenshots optimized to WebP - add reicon-react/reicon-brands (bundled, CSP-safe SVG icons) Business logic (D1 download counter, platform metadata, theme, SEO) unchanged.
33 lines
964 B
TypeScript
33 lines
964 B
TypeScript
import type { ReactNode } from "react";
|
|
import { Eyebrow } from "./eyebrow";
|
|
|
|
/** Eyebrow + heading + subhead block shared by every section. */
|
|
export function SectionHeading({
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
align = "left",
|
|
className = "",
|
|
}: {
|
|
eyebrow?: string;
|
|
title: ReactNode;
|
|
description?: ReactNode;
|
|
align?: "left" | "center";
|
|
className?: string;
|
|
}) {
|
|
const alignment =
|
|
align === "center" ? "items-center text-center mx-auto" : "items-start text-left";
|
|
|
|
return (
|
|
<div className={`flex max-w-[68ch] flex-col gap-3 md:gap-4 ${alignment} ${className}`}>
|
|
{eyebrow ? <Eyebrow>{eyebrow}</Eyebrow> : null}
|
|
<h2 className="text-balance text-[30px] font-medium leading-[1.12] tracking-[-0.02em] text-[var(--text)] md:text-[34px]">
|
|
{title}
|
|
</h2>
|
|
{description ? (
|
|
<p className="max-w-[62ch] text-pretty text-lg leading-relaxed text-[var(--muted)]">{description}</p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|