diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 994df06..27745e6 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,6 @@
import React, { Suspense } from 'react';
import type { Metadata, Viewport } from 'next';
-import { Lora, Inter } from 'next/font/google';
+import { Lora, Inter, Space_Grotesk } from 'next/font/google';
import '../styles/tailwind.css';
// Self-hosted at build time by next/font — no runtime CDN fetch, no FOIT,
@@ -20,6 +20,15 @@ const inter = Inter({
variable: '--font-sans',
display: 'swap',
});
+
+// Brand display face for the AnimatedLogo wordmark + future display
+// typography. Self-hosted by next/font, same pipeline as Lora/Inter.
+const spaceGrotesk = Space_Grotesk({
+ subsets: ['latin'],
+ weight: ['500', '600', '700'],
+ variable: '--font-display',
+ display: 'swap',
+});
// SystemStatusBar removed — the "articles indexed / events tracked / [v2.0.0]"
// strip was eating vertical space without serving readers.
import AskBBAI from '@/components/AskBBAI';
@@ -100,7 +109,7 @@ export default function RootLayout({
}: Readonly<{children: React.ReactNode;}>) {
return (
-
+
{/* hreflang entries removed pending real i18n routes (Phase D). */}
diff --git a/src/app/preview/animated-logo/page.tsx b/src/app/preview/animated-logo/page.tsx
new file mode 100644
index 0000000..661f485
--- /dev/null
+++ b/src/app/preview/animated-logo/page.tsx
@@ -0,0 +1,61 @@
+import AnimatedLogo from '@/components/AnimatedLogo';
+
+export const dynamic = 'force-static';
+
+export const metadata = {
+ title: 'AnimatedLogo preview — AV Beat',
+ robots: { index: false, follow: false },
+};
+
+/**
+ * Standalone preview for /preview/animated-logo. Renders the new
+ * AnimatedLogo component in both light and dark variants at several
+ * sizes so you can eyeball the animation before any wiring decisions.
+ */
+export default function AnimatedLogoPreviewPage() {
+ return (
+
+
AnimatedLogo preview
+
+ Standalone — not wired into Header or Footer. Reload the page to replay the mount animation.
+
+
+
+
+
+ onLight · default sizes
+
+
+
+
+
+
+ onDark · default sizes
+
+
+
+
+
+
+ no tagline · favicon-style
+
+
+
+
+
+ );
+}
diff --git a/src/components/AnimatedLogo.tsx b/src/components/AnimatedLogo.tsx
new file mode 100644
index 0000000..8166604
--- /dev/null
+++ b/src/components/AnimatedLogo.tsx
@@ -0,0 +1,277 @@
+'use client';
+
+import React, { useId } from 'react';
+
+/**
+ * AnimatedLogo
+ * ============
+ * Standalone preview component — NOT yet wired into Header or Footer.
+ * The existing AvBeatLogo (static A+V monogram) keeps the production
+ * surfaces. This component is the experimental brand mark.
+ *
+ * • Single-A monogram inside a rounded-square tile with the brand
+ * gradient (#2563EB → #1E3A8A).
+ * • Wordmark in Space Grotesk 700, loaded via next/font/google as
+ * --font-display in app/layout.tsx.
+ * • Tagline in Inter (var(--font-sans)) with an accent-rule (a short
+ * blue dash) preceding the text.
+ *
+ * Animations (pure CSS, no library):
+ * • Stroke-draw on mount ~1.1s on the A path
+ * • Cyan signal pulse ~3.2s loop traversing the A
+ * • Subtle sheen sweep ~4.4s loop diagonal across the tile
+ * • Wordmark + tagline fade in ~0.9s, delayed until stroke finishes
+ *
+ * All animations halt under `prefers-reduced-motion: reduce`.
+ *
+ * Props
+ * size — emblem edge length in px (default 64). Wordmark scales.
+ * variant — 'onLight' (default) | 'onDark' — chooses text colors.
+ * showTagline (default true) — toggle the "Inside the Future of Pro
+ * AV" line.
+ *
+ * Accessibility: role="img" with aria-label="AV Beat" on the wrapper.
+ */
+export type AnimatedLogoVariant = 'onLight' | 'onDark';
+
+interface Props {
+ size?: number;
+ variant?: AnimatedLogoVariant;
+ showTagline?: boolean;
+ className?: string;
+}
+
+export default function AnimatedLogo({
+ size = 64,
+ variant = 'onLight',
+ showTagline = true,
+ className = '',
+}: Props) {
+ // useId for stable, SSR-safe unique IDs (the page can render two of
+ // these and the gradients won't collide).
+ const uid = useId().replace(/[:]/g, '');
+ const tileGradId = `al-tile-${uid}`;
+ const sheenGradId = `al-sheen-${uid}`;
+ const wordmarkColor = variant === 'onDark' ? '#FFFFFF' : '#0F172A';
+ const taglineColor = variant === 'onDark' ? '#CBD5E1' : '#475569';
+ const accentColor = '#1D4ED8';
+
+ return (
+
+ {/* ── EMBLEM ───────────────────────────────────────────────── */}
+
+
+ {/* ── WORDMARK + TAGLINE ───────────────────────────────────── */}
+
+
+ AV BEAT
+
+ {showTagline && (
+
+
+ Inside the Future of Pro AV
+
+ )}
+
+
+ {/*
+ Component-scoped CSS. Keeping it inline (style jsx-free) so the
+ component is portable to any Next.js page without globals leak.
+ prefers-reduced-motion blanket-disables every keyframe.
+ */}
+
+
+ );
+}