- Main nav background → brand-blue #1D4ED8 (sampled from logo emblem). - Nav link text → white with darker-blue hover bg so contrast holds. - Article card title fixed from #e0e0e0 to navy #0F172A (was unreadable on the new white surface). - Industry News card date row removed. - /news Topics filter chip row removed. - /news article hover from stark #111 to light-blue #EFF6FF tint matching the brand. - Tagline 'News & Intelligence for Pro AV, Live Production & Display Tech' renamed to 'Pro AV, Display and Live Production Tech' across Header, RSS feed, newsletter welcome template, root layout title, page title, OG/Twitter tags. - Homepage page.tsx now has `export const dynamic = 'force-dynamic'` + `revalidate = 0` so the hero (FeaturedBentoFromDb) actually re-renders against the live supabase row (previously the page was being statically prerendered which masked DB-driven hero swaps).
189 lines
7.0 KiB
TypeScript
189 lines
7.0 KiB
TypeScript
import React, { Suspense } from "react";
|
|
import type { Metadata } from "next";
|
|
import Header from "@/components/Header";
|
|
import Footer from "@/components/Footer";
|
|
import FeaturedBentoFromDb from '@/components/FeaturedBentoFromDb';
|
|
import FeaturedCarouselServer from '@/components/FeaturedCarouselServer';
|
|
import FeaturedBento from "./components/FeaturedBento";
|
|
import SpotlightCarousel from "./components/SpotlightCarousel";
|
|
import ArticleFeed from "./components/ArticleFeed";
|
|
import ScrollRevealSection from "@/components/ScrollRevealSection";
|
|
import NewsletterSignup from "./components/NewsletterSignup";
|
|
import SponsorLogoStrip from "@/components/SponsorLogoStrip";
|
|
import CompanyMentionsHover from "@/components/CompanyMentionsHover";
|
|
|
|
// force-dynamic so the homepage re-renders on every request — the hero
|
|
// component (FeaturedBentoFromDb) reads from supabase live and breaks
|
|
// when the parent page is statically prerendered.
|
|
export const dynamic = 'force-dynamic';
|
|
export const revalidate = 0;
|
|
|
|
export const metadata: Metadata = {
|
|
title: 'AV Beat — Pro AV, Display and Live Production Tech',
|
|
description: 'The digital platform for pro AV, live production, and display tech professionals. Breaking news, deep-dive features, and industry spotlights from InfoComm to ISE and beyond.',
|
|
alternates: {
|
|
canonical: '/',
|
|
},
|
|
openGraph: {
|
|
title: 'AV Beat — Pro AV, Live Production & Display Tech',
|
|
description: 'Breaking news and insights for pro AV, live production, and display tech professionals.',
|
|
url: '/',
|
|
type: 'website',
|
|
images: [
|
|
{
|
|
url: '/assets/images/og-image.png',
|
|
width: 1200,
|
|
height: 630,
|
|
alt: 'AV Beat — Pro AV, Live Production & Display Tech News',
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: 'summary_large_image',
|
|
title: 'AV Beat — Pro AV, Live Production & Display Tech',
|
|
description: 'Breaking news and insights for pro AV, live production, and display tech professionals.',
|
|
images: ['/assets/images/og-image.png'],
|
|
},
|
|
};
|
|
|
|
/* ── Skeleton placeholders ─────────────────────────────── */
|
|
function LeaderboardSkeleton() {
|
|
return (
|
|
<div className="max-w-container mx-auto px-4 py-3">
|
|
<div className="skeleton h-[90px] w-full rounded-sm" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function BentoSkeleton() {
|
|
return (
|
|
<section className="max-w-container mx-auto px-4 py-4 md:py-6">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="skeleton h-4 w-20 rounded-sm" />
|
|
<div className="flex-1 h-px bg-[#DCE6F2]" />
|
|
</div>
|
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-3 md:gap-4">
|
|
<div className="lg:col-span-7 skeleton rounded-sm h-[260px] sm:h-[320px] md:h-[380px]" />
|
|
<div className="lg:col-span-5 grid grid-cols-2 gap-3 md:gap-4">
|
|
{[...Array(4)]?.map((_, i) => (
|
|
<div key={i} className="skeleton-card">
|
|
<div className="skeleton h-[110px] sm:h-[130px]" />
|
|
<div className="p-2 space-y-1.5">
|
|
<div className="skeleton h-3 w-16 rounded-sm" />
|
|
<div className="skeleton h-4 w-full rounded-sm" />
|
|
<div className="skeleton h-4 w-3/4 rounded-sm" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function CarouselSkeleton() {
|
|
return (
|
|
<div className="py-6 px-4">
|
|
<div className="max-w-container mx-auto">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="skeleton h-4 w-24 rounded-sm" />
|
|
<div className="flex-1 h-px bg-[#DCE6F2]" />
|
|
</div>
|
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
{[...Array(4)]?.map((_, i) => (
|
|
<div key={i} className="skeleton-card">
|
|
<div className="skeleton h-[140px]" />
|
|
<div className="p-3 space-y-2">
|
|
<div className="skeleton h-3 w-16 rounded-sm" />
|
|
<div className="skeleton h-4 w-full rounded-sm" />
|
|
<div className="skeleton h-4 w-2/3 rounded-sm" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function FeedSkeleton() {
|
|
return (
|
|
<section className="max-w-container mx-auto px-4 py-4 md:py-6">
|
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 lg:gap-8">
|
|
<div className="lg:col-span-8 space-y-1">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<div className="skeleton h-4 w-20 rounded-sm" />
|
|
<div className="flex-1 h-px bg-[#DCE6F2]" />
|
|
</div>
|
|
{[...Array(5)]?.map((_, i) => (
|
|
<div key={i} className="flex gap-4 py-4 border-b border-[#222]">
|
|
<div className="skeleton flex-shrink-0 w-[140px] h-[95px] rounded-sm" />
|
|
<div className="flex-1 space-y-2">
|
|
<div className="skeleton h-3 w-24 rounded-sm" />
|
|
<div className="skeleton h-5 w-full rounded-sm" />
|
|
<div className="skeleton h-5 w-4/5 rounded-sm" />
|
|
<div className="skeleton h-3 w-32 rounded-sm" />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="lg:col-span-4 space-y-4">
|
|
<div className="skeleton h-[250px] rounded-sm" />
|
|
<div className="skeleton h-[200px] rounded-sm" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export default function HomePage() {
|
|
return (
|
|
<div className="min-h-screen bg-background">
|
|
{/* Sticky top bar + nav */}
|
|
<Header />
|
|
|
|
{/* H1 Hero Heading */}
|
|
<div className="sr-only">
|
|
<h1>AV Beat — Pro AV, Live Production & Display Tech News</h1>
|
|
</div>
|
|
|
|
{/* Featured: cinematic hero + 4-up rail below (FeaturedBentoFromDb) */}
|
|
<ScrollRevealSection id="featured" className="section-enter section-enter-3 scroll-mt-32">
|
|
<Suspense fallback={<BentoSkeleton />}>
|
|
<FeaturedBentoFromDb />
|
|
</Suspense>
|
|
</ScrollRevealSection>
|
|
|
|
{/* Spotlight carousel — temporarily hidden (old stories)
|
|
<ScrollRevealSection className="section-enter section-enter-4" delay={1}>
|
|
<Suspense fallback={<CarouselSkeleton />}>
|
|
<SpotlightCarousel />
|
|
</Suspense>
|
|
</ScrollRevealSection>
|
|
*/}
|
|
|
|
{/* Article feed + sidebar */}
|
|
<ScrollRevealSection id="the-latest" className="section-enter section-enter-5 scroll-mt-32" delay={2}>
|
|
<Suspense fallback={<FeedSkeleton />}>
|
|
<ArticleFeed />
|
|
</Suspense>
|
|
</ScrollRevealSection>
|
|
|
|
{/* Newsletter signup */}
|
|
<ScrollRevealSection className="section-enter section-enter-5">
|
|
<NewsletterSignup />
|
|
</ScrollRevealSection>
|
|
|
|
{/* Content Partners logo strip — current banner advertisers */}
|
|
<ScrollRevealSection className="section-enter section-enter-5">
|
|
<Suspense fallback={null}>
|
|
<SponsorLogoStrip />
|
|
</Suspense>
|
|
</ScrollRevealSection>
|
|
|
|
{/* Footer */}
|
|
<Footer />
|
|
<CompanyMentionsHover />
|
|
</div>
|
|
);
|
|
} |