about/show-coverage: redesign with sidebar (300x600 + rotating 300x250s)

Switch the bare single-column layouts on these pages to a two-column
grid that pulls in <SidebarAdStack /> on the right (pinned 300x600 +
every 300x250 rotated). Header keeps the leaderboard above. The
in-content 728x90 banners added earlier are removed since the sidebar
now carries the ad load (avoids stacking two of the same unit on the
page).

Pages updated:
  - /show-coverage/[slug]   event detail
  - /about
  - /about/contact
  - /about/team
  - /about/advertise (also gets a real-banner grid in "Available units":
    the actual 728x90, 300x600, and 300x250 sample creatives are
    rendered next to each unit's description, so advertisers see what
    the inventory looks like in production rather than just a label).

The /about pages also got a small refresh: serif H1 + accent-blue
"Broadcast Beat" eyebrow line + bordered content blocks, bringing the
look closer to the cinematic homepage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ryan Salazar
2026-05-20 05:43:02 +00:00
parent 4e1e5c22e8
commit d2d941c576
5 changed files with 311 additions and 138 deletions

View File

@@ -1,60 +1,113 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import AdImage from "@/components/AdImage";
import { ADS_728X90, shuffle } from "@/lib/ads";
import SidebarAdStack from "@/components/SidebarAdStack";
import { ADS_728X90, ADS_300X250, FIXED_300X600, shuffle } from "@/lib/ads";
export const metadata = { title: "Advertise — Broadcast Beat" };
export default function AdvertisePage() {
const example728 = ADS_728X90[0];
const example300x250 = ADS_300X250[0];
const example300x600 = FIXED_300X600;
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-3xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-2">Advertise on Broadcast Beat</h1>
<p className="text-sm text-[#9ca3af] mb-10">
Broadcast Beat reaches broadcast engineers, post supervisors, streaming
architects, and live-event professionals across the industry.
</p>
<div className="mx-auto max-w-6xl px-6 py-12 text-[#e5e7eb]">
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
<main>
<header className="mb-8 border-b border-[#252525] pb-6">
<div className="text-[10px] font-mono uppercase tracking-[0.22em] text-[var(--color-text-info,#60a5fa)] mb-2">
Broadcast Beat
</div>
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">Advertise</h1>
<p className="mt-3 text-[#9ca3af] text-base max-w-2xl">
Broadcast Beat reaches broadcast engineers, post supervisors,
streaming architects, and live-event professionals across the
industry daily.
</p>
</header>
<section className="mb-10">
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-3">Available units</h2>
<ul className="space-y-3 text-[15px]">
<li>
<strong>728×90 leaderboard</strong> between main nav and headline ticker, sitewide.
</li>
<li>
<strong>300×600 sidebar premium</strong> pinned top of every sidebar, sitewide.
</li>
<li>
<strong>300×250 sidebar rotation</strong> rotates across every page, multiple slots.
</li>
</ul>
</section>
<section className="mb-12">
<h2 className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-5">
Available units
</h2>
{/* In-content 728x90 banner (live example of the leaderboard unit) */}
{ADS_728X90.length > 0 && (
<div className="my-10 hidden md:flex justify-center" aria-label="Advertisement">
<AdImage ad={shuffle(ADS_728X90)[0]} page="advertise" />
</div>
)}
<div className="grid grid-cols-1 gap-5">
{/* 728x90 */}
<div className="rounded border border-[#252525] bg-[#0b0f17] p-5 flex flex-col md:flex-row md:items-center gap-5">
<div className="md:flex-1 min-w-0">
<h3 className="font-serif text-xl font-semibold">728 × 90 leaderboard</h3>
<div className="mt-1 text-[10px] font-mono uppercase tracking-wider text-[#6b7280]">Site-wide · top of every page</div>
<p className="mt-3 text-sm text-[#d1d5db]">
Sits between the main nav and the headline ticker on
every page. The premium first-impression unit.
</p>
</div>
<div className="flex-shrink-0 flex items-center justify-center rounded border border-[#1e1e1e] bg-[#0a0a0a] p-3" style={{ minWidth: 250 }}>
{example728 ? <AdImage ad={example728} page="advertise-example" /> : <div className="w-[728px] max-w-full h-[90px] flex items-center justify-center text-xs text-[#555]">728 × 90</div>}
</div>
</div>
<section className="mb-10">
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-3">Reporting</h2>
<p className="text-[15px] text-[#d1d5db]">
Every banner is tracked for gross impressions and clicks. Advertisers
receive a monthly report with totals and CTR. Click links route through
/r/{"{campaign}"} for accurate counting independent of analytics
blockers.
</p>
</section>
{/* 300x600 */}
<div className="rounded border border-[#252525] bg-[#0b0f17] p-5 flex flex-col md:flex-row md:items-center gap-5">
<div className="md:flex-1 min-w-0">
<h3 className="font-serif text-xl font-semibold">300 × 600 sidebar premium</h3>
<div className="mt-1 text-[10px] font-mono uppercase tracking-wider text-[#6b7280]">Pinned · every sidebar · sitewide</div>
<p className="mt-3 text-sm text-[#d1d5db]">
Pinned at the top of every sidebar, sitewide. Stays
visible while readers scroll the article body.
</p>
</div>
<div className="flex-shrink-0 flex items-center justify-center rounded border border-[#1e1e1e] bg-[#0a0a0a] p-3">
{example300x600 ? <AdImage ad={example300x600} page="advertise-example" /> : <div className="w-[300px] h-[600px] flex items-center justify-center text-xs text-[#555]">300 × 600</div>}
</div>
</div>
<section>
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-3">Get in touch</h2>
<p className="text-[15px]">
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:advertising@broadcastbeat.com">advertising@broadcastbeat.com</a>
</p>
</section>
</main>
{/* 300x250 */}
<div className="rounded border border-[#252525] bg-[#0b0f17] p-5 flex flex-col md:flex-row md:items-center gap-5">
<div className="md:flex-1 min-w-0">
<h3 className="font-serif text-xl font-semibold">300 × 250 sidebar rotation</h3>
<div className="mt-1 text-[10px] font-mono uppercase tracking-wider text-[#6b7280]">Rotating · multiple slots · every page</div>
<p className="mt-3 text-sm text-[#d1d5db]">
Rotates across every page, multiple slots per page.
Highest-volume inventory best for awareness campaigns
and seasonal pushes (NAB, IBC, BroadcastAsia).
</p>
</div>
<div className="flex-shrink-0 flex items-center justify-center rounded border border-[#1e1e1e] bg-[#0a0a0a] p-3">
{example300x250 ? <AdImage ad={example300x250} page="advertise-example" /> : <div className="w-[300px] h-[250px] flex items-center justify-center text-xs text-[#555]">300 × 250</div>}
</div>
</div>
</div>
</section>
<section className="mb-10">
<h2 className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-3">Reporting</h2>
<p className="text-[15px] text-[#d1d5db] leading-relaxed">
Every banner is tracked for gross impressions and clicks.
Advertisers receive a monthly report with totals and CTR. Click
links route through <code className="text-[#60a5fa]">/r/{`{campaign}`}</code> for accurate counting independent of
analytics blockers.
</p>
</section>
<section>
<h2 className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-3">Get in touch</h2>
<p className="text-[15px]">
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:advertising@broadcastbeat.com">advertising@broadcastbeat.com</a>
</p>
</section>
</main>
<aside className="mt-10 lg:mt-0" aria-label="Advertisements">
<div className="lg:sticky lg:top-24">
<SidebarAdStack />
</div>
</aside>
</div>
</div>
<Footer />
</div>
);

View File

@@ -1,45 +1,86 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import AdImage from "@/components/AdImage";
import { ADS_728X90, shuffle } from "@/lib/ads";
import SidebarAdStack from "@/components/SidebarAdStack";
export const metadata = { title: "Contact — Broadcast Beat" };
const CONTACTS = [
{
title: "Editorial",
body: (
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:editor@broadcastbeat.com">editor@broadcastbeat.com</a>
),
hint: "Story tips, corrections, interview requests.",
},
{
title: "Press releases",
body: (
<>
Route PR-firm submissions through{" "}
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="https://distribution.relevantmediaproperties.com">distribution.relevantmediaproperties.com</a>.
</>
),
hint: "Routed submissions land in our editorial queue with provenance.",
},
{
title: "Advertising",
body: (
<>
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:advertising@broadcastbeat.com">advertising@broadcastbeat.com</a> see the{" "}
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/advertise">media kit</a>.
</>
),
hint: "728×90, 300×600, 300×250 inventory; CTR + impression reports monthly.",
},
{
title: "Publisher",
body: (
<>
Ryan Salazar ·{" "}
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:ryan.salazar@relevantmediaproperties.com">ryan.salazar@relevantmediaproperties.com</a>
</>
),
hint: "Partnerships, M&A, syndication, network membership.",
},
];
export default function ContactPage() {
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-2xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-6">Contact</h1>
<dl className="space-y-5 text-[15px]">
<div>
<dt className="text-xs font-mono uppercase tracking-wider text-[#6b7280] mb-1">Editorial</dt>
<dd><a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:editor@broadcastbeat.com">editor@broadcastbeat.com</a></dd>
</div>
<div>
<dt className="text-xs font-mono uppercase tracking-wider text-[#6b7280] mb-1">Press releases (PR firms)</dt>
<dd>
Route submissions through{" "}
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="https://distribution.relevantmediaproperties.com">distribution.relevantmediaproperties.com</a>.
</dd>
</div>
<div>
<dt className="text-xs font-mono uppercase tracking-wider text-[#6b7280] mb-1">Advertising</dt>
<dd><a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:advertising@broadcastbeat.com">advertising@broadcastbeat.com</a> see <a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/advertise">media kit</a>.</dd>
</div>
<div>
<dt className="text-xs font-mono uppercase tracking-wider text-[#6b7280] mb-1">Publisher</dt>
<dd>Ryan Salazar · <a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:ryan.salazar@relevantmediaproperties.com">ryan.salazar@relevantmediaproperties.com</a></dd>
</div>
</dl>
<div className="mx-auto max-w-6xl px-6 py-12 text-[#e5e7eb]">
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
<main>
<header className="mb-8 border-b border-[#252525] pb-6">
<div className="text-[10px] font-mono uppercase tracking-[0.22em] text-[var(--color-text-info,#60a5fa)] mb-2">
Broadcast Beat
</div>
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">Contact</h1>
<p className="mt-3 text-[#9ca3af] text-base">
Pitches, press, partnerships pick the right desk and we'll get back fast.
</p>
</header>
{/* In-content 728x90 banner */}
{ADS_728X90.length > 0 && (
<div className="mt-12 hidden md:flex justify-center" aria-label="Advertisement">
<AdImage ad={shuffle(ADS_728X90)[0]} page="contact" />
</div>
)}
</main>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-4">
{CONTACTS.map((c) => (
<li key={c.title} className="rounded border border-[#252525] bg-[#0b0f17] p-5 hover:border-[var(--color-text-info,#60a5fa)]/60 transition-colors">
<div className="text-[10px] font-mono uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-2">
{c.title}
</div>
<div className="text-[15px] leading-relaxed">{c.body}</div>
<div className="mt-3 text-xs text-[#6b7280]">{c.hint}</div>
</li>
))}
</ul>
</main>
<aside className="mt-10 lg:mt-0" aria-label="Advertisements">
<div className="lg:sticky lg:top-24">
<SidebarAdStack />
</div>
</aside>
</div>
</div>
<Footer />
</div>
);

View File

@@ -1,5 +1,6 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import SidebarAdStack from "@/components/SidebarAdStack";
export const metadata = {
title: "About Broadcast Beat",
@@ -10,31 +11,90 @@ export default function AboutPage() {
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-3xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-6">About Broadcast Beat</h1>
<div className="space-y-5 text-[#d1d5db] text-[15px] leading-relaxed">
<p>
Broadcast Beat is an independent trade publication covering broadcast,
live production, streaming, and post-production technology. We
report on the equipment, services, and people that move the
professional video industry forward.
</p>
<p>
We cover the major industry shows NAB, IBC, BroadcastAsia, MPTS,
and others track product launches and acquisitions, and publish
interviews with the engineers, post supervisors, and executives
building the next generation of workflows.
</p>
<p>
Editorial decisions are made independently of advertising. We do
not accept payment for editorial coverage. Vendor-supplied press
releases routed through our distribution platform are rewritten
by our staff or AI assistants according to our editorial style
guide; the original submission is preserved on file.
</p>
<div className="mx-auto max-w-6xl px-6 py-12 text-[#e5e7eb]">
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
<main>
<header className="mb-8 border-b border-[#252525] pb-6">
<div className="text-[10px] font-mono uppercase tracking-[0.22em] text-[var(--color-text-info,#60a5fa)] mb-2">
Broadcast Beat
</div>
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">About</h1>
<p className="mt-3 text-[#9ca3af] text-base max-w-2xl">
An independent trade publication covering broadcast, live production, streaming, and post-production technology.
</p>
</header>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-10">
<Stat label="Years covering the industry" value="20+" />
<Stat label="Shows tracked annually" value="14+" />
<Stat label="Stories archived" value="29k+" />
</div>
<section className="space-y-6 text-[#d1d5db] text-[15px] leading-relaxed">
<Block title="What we cover">
Broadcast Beat reports on the equipment, services, and people
that move the professional video industry forward. We follow
product launches, executive moves, acquisitions, and the live
production / IP / cloud workflows reshaping how content gets
made and delivered.
</Block>
<Block title="Where we go">
We cover the major industry shows in person and remotely NAB
Show, IBC, BroadcastAsia, MPTS, InfoComm, and more track
product launches and acquisitions year-round, and publish
interviews with the engineers, post supervisors, and executives
building the next generation of workflows.
</Block>
<Block title="How we work">
Editorial decisions are made independently of advertising. We
do not accept payment for editorial coverage. Vendor-supplied
press releases routed through our distribution platform are
rewritten by our staff or AI assistants according to our
editorial style guide; the original submission is preserved on
file. Our AI personas are clearly labeled wherever they appear.
</Block>
<Block title="Get involved">
For editorial pitches: <a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:editor@broadcastbeat.com">editor@broadcastbeat.com</a>.
{" "}For advertising: <a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/advertise">see the media kit</a>.
{" "}For our team: <a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/team">meet the editorial team</a>.
</Block>
</section>
</main>
{/* Sidebar: 300x600 + rotating 300x250s */}
<aside className="mt-10 lg:mt-0" aria-label="Advertisements">
<div className="lg:sticky lg:top-24">
<SidebarAdStack />
</div>
</aside>
</div>
</main>
</div>
<Footer />
</div>
);
}
function Stat({ label, value }: { label: string; value: string }) {
return (
<div className="rounded border border-[#252525] bg-[#0b0f17] p-4">
<div className="font-serif text-3xl text-white">{value}</div>
<div className="mt-1 text-[10px] font-mono uppercase tracking-[0.16em] text-[#6b7280]">
{label}
</div>
</div>
);
}
function Block({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="rounded border border-[#1e1e1e] bg-[#0b0f17]/70 p-5">
<h2 className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-2">
{title}
</h2>
<p className="text-[15px] leading-relaxed">{children}</p>
</div>
);
}

View File

@@ -1,8 +1,7 @@
import { createClient } from "@supabase/supabase-js";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import AdImage from "@/components/AdImage";
import { ADS_728X90, shuffle } from "@/lib/ads";
import SidebarAdStack from "@/components/SidebarAdStack";
export const dynamic = "force-dynamic";
export const revalidate = 600;
@@ -38,44 +37,52 @@ export default async function TeamPage() {
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-5xl px-6 py-12 text-[#e5e7eb]">
<h1 className="font-serif text-4xl font-bold tracking-tight mb-2">Editorial team</h1>
<p className="text-sm text-[#9ca3af] mb-10">
Staff journalists by beat. We are a mix of human reporters and trained
AI personas operating under house style.
</p>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-5">
{team.map((p) => (
<li key={p.slug} className="rounded border border-[#252525] bg-[#0b0f17] p-5 flex gap-4">
{p.avatar_url && (
<img
src={p.avatar_url}
alt={p.name}
width={64}
height={64}
className="rounded-full border border-[#252525]"
/>
)}
<div className="flex-1">
<h2 className="font-serif text-lg font-semibold">{p.name}</h2>
{p.beat && (
<div className="text-[10px] font-mono uppercase tracking-wider text-[var(--color-text-info,#60a5fa)] mt-0.5">
{p.beat.replace(/-/g, " ")}
</div>
)}
{p.bio && <p className="text-sm text-[#9ca3af] mt-2 leading-relaxed">{p.bio}</p>}
<div className="mx-auto max-w-6xl px-6 py-12 text-[#e5e7eb]">
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
<main>
<header className="mb-8 border-b border-[#252525] pb-6">
<div className="text-[10px] font-mono uppercase tracking-[0.22em] text-[var(--color-text-info,#60a5fa)] mb-2">
Broadcast Beat
</div>
</li>
))}
</ul>
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">Editorial team</h1>
<p className="mt-3 text-[#9ca3af] text-base">
Staff journalists by beat a mix of human reporters and trained AI personas operating under house style.
</p>
</header>
{/* In-content 728x90 banner */}
{ADS_728X90.length > 0 && (
<div className="mt-12 hidden md:flex justify-center" aria-label="Advertisement">
<AdImage ad={shuffle(ADS_728X90)[0]} page="team" />
</div>
)}
</main>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-5">
{team.map((p) => (
<li key={p.slug} className="rounded border border-[#252525] bg-[#0b0f17] p-5 flex gap-4 hover:border-[var(--color-text-info,#60a5fa)]/60 transition-colors">
{p.avatar_url && (
<img
src={p.avatar_url}
alt={p.name}
width={64}
height={64}
className="rounded-full border border-[#252525] flex-shrink-0"
/>
)}
<div className="flex-1 min-w-0">
<h2 className="font-serif text-lg font-semibold">{p.name}</h2>
{p.beat && (
<div className="text-[10px] font-mono uppercase tracking-wider text-[var(--color-text-info,#60a5fa)] mt-0.5">
{p.beat.replace(/-/g, " ")}
</div>
)}
{p.bio && <p className="text-sm text-[#9ca3af] mt-2 leading-relaxed">{p.bio}</p>}
</div>
</li>
))}
</ul>
</main>
<aside className="mt-10 lg:mt-0" aria-label="Advertisements">
<div className="lg:sticky lg:top-24">
<SidebarAdStack />
</div>
</aside>
</div>
</div>
<Footer />
</div>
);

View File

@@ -4,6 +4,7 @@ import { createClient } from "@supabase/supabase-js";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import AdImage from "@/components/AdImage";
import SidebarAdStack from "@/components/SidebarAdStack";
import { ADS_728X90, shuffle } from "@/lib/ads";
export const dynamic = "force-dynamic";
@@ -162,11 +163,13 @@ export default async function EventCoveragePage({
return (
<div className="min-h-screen bg-background">
<Header />
<main className="mx-auto max-w-5xl px-6 py-10 text-[#e5e7eb]">
<div className="mx-auto max-w-6xl px-6 py-10 text-[#e5e7eb]">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(eventJsonLd) }}
/>
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-8">
<main>
<Link href="/show-coverage" className="text-sm text-[var(--color-text-info,#60a5fa)] hover:underline">
All events
</Link>
@@ -245,7 +248,16 @@ export default async function EventCoveragePage({
</ul>
)}
</section>
</main>
</main>
{/* Sidebar: 300x600 + rotating 300x250s */}
<aside className="mt-10 lg:mt-0" aria-label="Advertisements">
<div className="lg:sticky lg:top-24">
<SidebarAdStack />
</div>
</aside>
</div>
</div>
<Footer />
</div>
);