AV Beat rebrand: theme swap (orange #E67E22 / near-black), logo, schema default → avb
- public/assets/images/logo.png + public/brand/logo*.png replaced with the 1320x310 AV Beat logo
- public/assets/logos/avbeat.png added as the canonical source
- tailwind.config.js + src/styles/tailwind.css + src/styles/redesign-tokens.css palette swapped (primary #0F0E0E, secondary #1D1A1A, accent #E67E22 orange, foreground #F0EBE6 warm white)
- Layout / robots / sitemap / home-page / about / contact / press-kit / team / global-error metadata switched to AV Beat (Where AV Meets IT)
- Bulk replaced 'Broadcast Beat'/'BroadcastBeat.com'/'broadcastbeat.com' across user-facing news/forum/marketplace/advertise/search/newsletter pages
- src/lib/supabase/{admin,server,client} now default to schema 'avb' (was 'bb') — overridable via NEXT_PUBLIC_SUPABASE_SCHEMA
- package.json renamed to 'avbeat'
- Single d60701 reference in about/team swapped to E67E22
Design-only replica — no article content migrated.
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "broadcastbeat",
|
"name": "avbeat",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"rocketCritical": {
|
"rocketCritical": {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 286 KiB |
BIN
public/assets/logos/avbeat.png
Normal file
|
After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 286 KiB |
@@ -2,13 +2,13 @@ import Header from "@/components/Header";
|
|||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
import SidebarAdStack from "@/components/SidebarAdStack";
|
import SidebarAdStack from "@/components/SidebarAdStack";
|
||||||
|
|
||||||
export const metadata = { title: "Contact — Broadcast Beat" };
|
export const metadata = { title: "Contact — AV Beat" };
|
||||||
|
|
||||||
const CONTACTS = [
|
const CONTACTS = [
|
||||||
{
|
{
|
||||||
title: "Editorial",
|
title: "Editorial",
|
||||||
body: (
|
body: (
|
||||||
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:editor@broadcastbeat.com">editor@broadcastbeat.com</a>
|
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:editor@avbeat.com">editor@avbeat.com</a>
|
||||||
),
|
),
|
||||||
hint: "Story tips, corrections, interview requests.",
|
hint: "Story tips, corrections, interview requests.",
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,7 @@ const CONTACTS = [
|
|||||||
title: "Advertising",
|
title: "Advertising",
|
||||||
body: (
|
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="mailto:advertising@avbeat.com">advertising@avbeat.com</a> — see the{" "}
|
||||||
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/advertise">media kit</a>.
|
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/advertise">media kit</a>.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
@@ -52,8 +52,8 @@ export default function ContactPage() {
|
|||||||
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
|
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
|
||||||
<main>
|
<main>
|
||||||
<header className="mb-8 border-b border-[#252525] pb-6">
|
<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">
|
<div className="text-[10px] font-mono uppercase tracking-[0.22em] text-[var(--color-text-info,#E67E22)] mb-2">
|
||||||
Broadcast Beat
|
AV Beat · Where AV Meets IT
|
||||||
</div>
|
</div>
|
||||||
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">Contact</h1>
|
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">Contact</h1>
|
||||||
<p className="mt-3 text-[#9ca3af] text-base">
|
<p className="mt-3 text-[#9ca3af] text-base">
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import Footer from "@/components/Footer";
|
|||||||
import SidebarAdStack from "@/components/SidebarAdStack";
|
import SidebarAdStack from "@/components/SidebarAdStack";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "About Broadcast Beat",
|
title: "About AV Beat",
|
||||||
description: "Broadcast Beat is an independent industry publication covering broadcast, production, and post-production technology.",
|
description: "AV Beat is an independent industry publication covering the convergence of pro AV and IT — broadcast, live production, streaming, and post-production technology.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AboutPage() {
|
export default function AboutPage() {
|
||||||
@@ -15,12 +15,12 @@ export default function AboutPage() {
|
|||||||
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
|
<div className="lg:grid lg:grid-cols-[1fr,300px] lg:gap-10">
|
||||||
<main>
|
<main>
|
||||||
<header className="mb-8 border-b border-[#252525] pb-6">
|
<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">
|
<div className="text-[10px] font-mono uppercase tracking-[0.22em] text-[var(--color-text-info,#E67E22)] mb-2">
|
||||||
Broadcast Beat
|
AV Beat · Where AV Meets IT
|
||||||
</div>
|
</div>
|
||||||
<h1 className="font-serif text-4xl md:text-5xl font-bold tracking-tight">About</h1>
|
<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">
|
<p className="mt-3 text-[#9ca3af] text-base max-w-2xl">
|
||||||
An independent trade publication covering broadcast, live production, streaming, and post-production technology.
|
An independent trade publication covering the convergence of pro AV and IT — broadcast, live production, streaming, and post-production technology.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -32,11 +32,11 @@ export default function AboutPage() {
|
|||||||
|
|
||||||
<section className="space-y-6 text-[#d1d5db] text-[15px] leading-relaxed">
|
<section className="space-y-6 text-[#d1d5db] text-[15px] leading-relaxed">
|
||||||
<Block title="What we cover">
|
<Block title="What we cover">
|
||||||
Broadcast Beat reports on the equipment, services, and people
|
AV Beat reports on the equipment, services, and people that
|
||||||
that move the professional video industry forward. We follow
|
move the professional AV and IT-converged video industry
|
||||||
product launches, executive moves, acquisitions, and the live
|
forward. We follow product launches, executive moves,
|
||||||
production / IP / cloud workflows reshaping how content gets
|
acquisitions, and the live production / IP / cloud workflows
|
||||||
made and delivered.
|
reshaping how content gets made and delivered.
|
||||||
</Block>
|
</Block>
|
||||||
|
|
||||||
<Block title="Where we go">
|
<Block title="Where we go">
|
||||||
@@ -57,9 +57,9 @@ export default function AboutPage() {
|
|||||||
</Block>
|
</Block>
|
||||||
|
|
||||||
<Block title="Get involved">
|
<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 editorial pitches: <a className="text-[var(--color-text-info,#E67E22)] hover:underline" href="mailto:editor@avbeat.com">editor@avbeat.com</a>.
|
||||||
{" "}For advertising: <a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/about/advertise">see the media kit</a>.
|
{" "}For advertising: <a className="text-[var(--color-text-info,#E67E22)] 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>.
|
{" "}For our team: <a className="text-[var(--color-text-info,#E67E22)] hover:underline" href="/about/team">meet the editorial team</a>.
|
||||||
</Block>
|
</Block>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Header from "@/components/Header";
|
import Header from "@/components/Header";
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = { title: "Press kit — Broadcast Beat" };
|
export const metadata = { title: "Press kit — AV Beat" };
|
||||||
|
|
||||||
export default function PressKitPage() {
|
export default function PressKitPage() {
|
||||||
return (
|
return (
|
||||||
@@ -13,23 +13,23 @@ export default function PressKitPage() {
|
|||||||
<section>
|
<section>
|
||||||
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-2">Logos</h2>
|
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-2">Logos</h2>
|
||||||
<ul className="space-y-1">
|
<ul className="space-y-1">
|
||||||
<li><a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="/assets/images/logo.png" download>Broadcast Beat logo (PNG)</a></li>
|
<li><a className="text-[var(--color-text-info,#E67E22)] hover:underline" href="/assets/images/logo.png" download>AV Beat logo (PNG)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-2">Boilerplate</h2>
|
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-2">Boilerplate</h2>
|
||||||
<p className="text-[#d1d5db] leading-relaxed">
|
<p className="text-[#d1d5db] leading-relaxed">
|
||||||
Broadcast Beat is an independent trade publication covering broadcast,
|
AV Beat is an independent trade publication covering the convergence
|
||||||
live production, streaming, and post-production technology. Founded
|
of pro AV and IT — broadcast, live production, streaming, and
|
||||||
to bring engineering-rigor reporting to industry coverage, Broadcast Beat
|
post-production technology. Founded to bring engineering-rigor
|
||||||
publishes daily on the equipment, services, and people that move
|
reporting to industry coverage, AV Beat publishes daily on the
|
||||||
professional video forward.
|
equipment, services, and people moving professional video forward.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-2">Media contact</h2>
|
<h2 className="font-mono text-xs uppercase tracking-wider text-[#6b7280] mb-2">Media contact</h2>
|
||||||
<p>
|
<p>
|
||||||
<a className="text-[var(--color-text-info,#60a5fa)] hover:underline" href="mailto:ryan.salazar@relevantmediaproperties.com">ryan.salazar@relevantmediaproperties.com</a>
|
<a className="text-[var(--color-text-info,#E67E22)] hover:underline" href="mailto:ryan.salazar@relevantmediaproperties.com">ryan.salazar@relevantmediaproperties.com</a>
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -98,11 +98,11 @@ export default async function TeamPage() {
|
|||||||
<main>
|
<main>
|
||||||
{/* Hero */}
|
{/* Hero */}
|
||||||
<header className="relative overflow-hidden rounded-xl mb-10 border border-[#1a1f2a] bg-gradient-to-br from-[#0d1118] via-[#0b0f17] to-[#0d1118]">
|
<header className="relative overflow-hidden rounded-xl mb-10 border border-[#1a1f2a] bg-gradient-to-br from-[#0d1118] via-[#0b0f17] to-[#0d1118]">
|
||||||
<div className="absolute inset-0 opacity-[0.04] bg-[radial-gradient(ellipse_at_top_right,#d60701,transparent_60%)]" aria-hidden />
|
<div className="absolute inset-0 opacity-[0.04] bg-[radial-gradient(ellipse_at_top_right,#E67E22,transparent_60%)]" aria-hidden />
|
||||||
<div className="relative px-6 py-10 sm:px-10 sm:py-14">
|
<div className="relative px-6 py-10 sm:px-10 sm:py-14">
|
||||||
<div className="inline-flex items-center gap-2 text-[10px] font-mono uppercase tracking-[0.22em] text-[#ff5a55] mb-3">
|
<div className="inline-flex items-center gap-2 text-[10px] font-mono uppercase tracking-[0.22em] text-[#F0B07A] mb-3">
|
||||||
<span className="w-6 h-px bg-[#d60701]" />
|
<span className="w-6 h-px bg-[#E67E22]" />
|
||||||
Broadcast Beat · editorial
|
AV Beat · editorial
|
||||||
</div>
|
</div>
|
||||||
<h1 className="font-serif text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-tight">
|
<h1 className="font-serif text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight leading-tight">
|
||||||
The team behind every byline
|
The team behind every byline
|
||||||
@@ -129,10 +129,10 @@ export default async function TeamPage() {
|
|||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={p.slug}
|
key={p.slug}
|
||||||
className="group relative rounded-xl border border-[#1a1f2a] bg-[#0b0f17] p-5 hover:border-[#d60701]/50 hover:shadow-[0_0_0_1px_rgba(214,7,1,0.15)] transition-all"
|
className="group relative rounded-xl border border-[#1a1f2a] bg-[#0b0f17] p-5 hover:border-[#E67E22]/50 hover:shadow-[0_0_0_1px_rgba(230,126,34,0.15)] transition-all"
|
||||||
>
|
>
|
||||||
{/* Top accent */}
|
{/* Top accent */}
|
||||||
<div className="absolute top-0 left-5 right-5 h-px bg-gradient-to-r from-transparent via-[#d60701]/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden />
|
<div className="absolute top-0 left-5 right-5 h-px bg-gradient-to-r from-transparent via-[#E67E22]/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" aria-hidden />
|
||||||
|
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
{p.avatar_url ? (
|
{p.avatar_url ? (
|
||||||
@@ -142,7 +142,7 @@ export default async function TeamPage() {
|
|||||||
alt={p.name}
|
alt={p.name}
|
||||||
width={96}
|
width={96}
|
||||||
height={96}
|
height={96}
|
||||||
className="w-24 h-24 rounded-full border-2 border-[#1a1f2a] group-hover:border-[#d60701]/40 transition-colors object-cover"
|
className="w-24 h-24 rounded-full border-2 border-[#1a1f2a] group-hover:border-[#E67E22]/40 transition-colors object-cover"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
@@ -155,7 +155,7 @@ export default async function TeamPage() {
|
|||||||
<h3 className="font-serif text-xl font-bold leading-tight">{p.name}</h3>
|
<h3 className="font-serif text-xl font-bold leading-tight">{p.name}</h3>
|
||||||
</a>
|
</a>
|
||||||
{(p.beat || p.slug === "ryan-salazar") && (
|
{(p.beat || p.slug === "ryan-salazar") && (
|
||||||
<div className="inline-block mt-1.5 px-2 py-0.5 rounded-sm bg-[#d60701]/10 border border-[#d60701]/25 text-[10px] font-mono uppercase tracking-[0.12em] text-[#ff6b66]">
|
<div className="inline-block mt-1.5 px-2 py-0.5 rounded-sm bg-[#E67E22]/10 border border-[#E67E22]/25 text-[10px] font-mono uppercase tracking-[0.12em] text-[#F0B07A]">
|
||||||
{displayBeat(p)}
|
{displayBeat(p)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -173,11 +173,11 @@ export default async function TeamPage() {
|
|||||||
<ObfuscatedEmail
|
<ObfuscatedEmail
|
||||||
user={e.user}
|
user={e.user}
|
||||||
domain={e.domain}
|
domain={e.domain}
|
||||||
className="text-[#aab1bd] hover:text-[#ff6b66] font-mono"
|
className="text-[#aab1bd] hover:text-[#F0B07A] font-mono"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
href={`/authors/${p.slug}`}
|
href={`/authors/${p.slug}`}
|
||||||
className="text-[10px] font-mono uppercase tracking-[0.12em] text-[#6c7686] hover:text-[#ff6b66]"
|
className="text-[10px] font-mono uppercase tracking-[0.12em] text-[#6c7686] hover:text-[#F0B07A]"
|
||||||
>
|
>
|
||||||
View →
|
View →
|
||||||
</a>
|
</a>
|
||||||
@@ -209,7 +209,7 @@ export default async function TeamPage() {
|
|||||||
{ops.map((p) => (
|
{ops.map((p) => (
|
||||||
<li
|
<li
|
||||||
key={p.id}
|
key={p.id}
|
||||||
className="rounded-xl border border-[#1a1f2a] bg-[#0b0f17] p-5 flex gap-4 hover:border-[#d60701]/40 transition-colors"
|
className="rounded-xl border border-[#1a1f2a] bg-[#0b0f17] p-5 flex gap-4 hover:border-[#E67E22]/40 transition-colors"
|
||||||
>
|
>
|
||||||
{p.avatar_url ? (
|
{p.avatar_url ? (
|
||||||
<img
|
<img
|
||||||
@@ -235,13 +235,13 @@ export default async function TeamPage() {
|
|||||||
if (!e) return null;
|
if (!e) return null;
|
||||||
return (
|
return (
|
||||||
<div className="font-mono">
|
<div className="font-mono">
|
||||||
<ObfuscatedEmail user={e.user} domain={e.domain} className="hover:text-[#ff6b66]" />
|
<ObfuscatedEmail user={e.user} domain={e.domain} className="hover:text-[#F0B07A]" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
{p.phone && (
|
{p.phone && (
|
||||||
<div>
|
<div>
|
||||||
<a href={`tel:${p.phone.replace(/[^\d+]/g, '')}`} className="hover:text-[#ff6b66] font-mono">
|
<a href={`tel:${p.phone.replace(/[^\d+]/g, '')}`} className="hover:text-[#F0B07A] font-mono">
|
||||||
{p.phone}{p.extension ? ` ext. ${p.extension}` : ''}
|
{p.phone}{p.extension ? ` ext. ${p.extension}` : ''}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Advertise With Broadcast Beat — Reach Broadcast Engineering Professionals',
|
title: 'Advertise With AV Beat — Reach Broadcast Engineering Professionals',
|
||||||
description: 'Advertise with Broadcast Beat to reach broadcast engineers, production professionals, and media technology decision-makers. Display ads, sponsored content, and newsletter placements.',
|
description: 'Advertise with AV Beat to reach broadcast engineers, production professionals, and media technology decision-makers. Display ads, sponsored content, and newsletter placements.',
|
||||||
alternates: { canonical: '/advertise' },
|
alternates: { canonical: '/advertise' },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'Advertise With Broadcast Beat',
|
title: 'Advertise With AV Beat',
|
||||||
description: 'Reach broadcast engineers, production professionals, and media technology decision-makers.',
|
description: 'Reach broadcast engineers, production professionals, and media technology decision-makers.',
|
||||||
url: '/advertise',
|
url: '/advertise',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export default function AdvertisePage() {
|
|||||||
<span className="section-label mb-3 inline-block">Advertise</span>
|
<span className="section-label mb-3 inline-block">Advertise</span>
|
||||||
<h1 className="font-heading text-white text-4xl font-bold mb-4">Reach Broadcast Engineering Professionals</h1>
|
<h1 className="font-heading text-white text-4xl font-bold mb-4">Reach Broadcast Engineering Professionals</h1>
|
||||||
<p className="text-[#aaa] font-body text-lg max-w-2xl mx-auto mb-6">
|
<p className="text-[#aaa] font-body text-lg max-w-2xl mx-auto mb-6">
|
||||||
Broadcast Beat is the leading digital platform for broadcast engineering professionals. Connect your brand with decision-makers, engineers, and executives across the broadcast industry.
|
AV Beat is the leading digital platform for broadcast engineering professionals. Connect your brand with decision-makers, engineers, and executives across the broadcast industry.
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="#contact"
|
href="#contact"
|
||||||
@@ -69,7 +69,7 @@ export default function AdvertisePage() {
|
|||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
{stats.map((stat) => (
|
{stats.map((stat) => (
|
||||||
<div key={stat.label} className="bg-[#111] border border-[#222] p-5 text-center">
|
<div key={stat.label} className="bg-[#111] border border-[#222] p-5 text-center">
|
||||||
<div className="font-heading text-[#3b82f6] text-2xl font-bold mb-1">{stat.value}</div>
|
<div className="font-heading text-[#E67E22] text-2xl font-bold mb-1">{stat.value}</div>
|
||||||
<div className="font-body text-[#777] text-xs uppercase tracking-wide">{stat.label}</div>
|
<div className="font-body text-[#777] text-xs uppercase tracking-wide">{stat.label}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -86,10 +86,10 @@ export default function AdvertisePage() {
|
|||||||
<table className="w-full text-sm font-body">
|
<table className="w-full text-sm font-body">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-[#222]">
|
<tr className="border-b border-[#222]">
|
||||||
<th className="text-left py-3 px-4 text-[#3b82f6] font-bold text-xs uppercase tracking-wider">Placement</th>
|
<th className="text-left py-3 px-4 text-[#E67E22] font-bold text-xs uppercase tracking-wider">Placement</th>
|
||||||
<th className="text-left py-3 px-4 text-[#3b82f6] font-bold text-xs uppercase tracking-wider">Dimensions</th>
|
<th className="text-left py-3 px-4 text-[#E67E22] font-bold text-xs uppercase tracking-wider">Dimensions</th>
|
||||||
<th className="text-left py-3 px-4 text-[#3b82f6] font-bold text-xs uppercase tracking-wider">Location</th>
|
<th className="text-left py-3 px-4 text-[#E67E22] font-bold text-xs uppercase tracking-wider">Location</th>
|
||||||
<th className="text-left py-3 px-4 text-[#3b82f6] font-bold text-xs uppercase tracking-wider">Rate</th>
|
<th className="text-left py-3 px-4 text-[#E67E22] font-bold text-xs uppercase tracking-wider">Rate</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -98,7 +98,7 @@ export default function AdvertisePage() {
|
|||||||
<td className="py-3 px-4 text-[#e0e0e0] font-medium">{zone.name}</td>
|
<td className="py-3 px-4 text-[#e0e0e0] font-medium">{zone.name}</td>
|
||||||
<td className="py-3 px-4 text-[#888] font-mono text-xs">{zone.dimensions}</td>
|
<td className="py-3 px-4 text-[#888] font-mono text-xs">{zone.dimensions}</td>
|
||||||
<td className="py-3 px-4 text-[#777]">{zone.placement}</td>
|
<td className="py-3 px-4 text-[#777]">{zone.placement}</td>
|
||||||
<td className="py-3 px-4 text-[#3b82f6] font-medium">{zone.cpm}</td>
|
<td className="py-3 px-4 text-[#E67E22] font-medium">{zone.cpm}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -123,23 +123,23 @@ export default function AdvertisePage() {
|
|||||||
<h3 className="font-heading text-[#e0e0e0] text-lg font-bold mb-4">Sales Contact</h3>
|
<h3 className="font-heading text-[#e0e0e0] text-lg font-bold mb-4">Sales Contact</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-body text-[#3b82f6] text-xs font-bold uppercase tracking-wider mb-1">Advertising Sales</p>
|
<p className="font-body text-[#E67E22] text-xs font-bold uppercase tracking-wider mb-1">Advertising Sales</p>
|
||||||
<p className="font-body text-[#e0e0e0] font-medium">Jeff Victor</p>
|
<p className="font-body text-[#e0e0e0] font-medium">Jeff Victor</p>
|
||||||
<a href="mailto:jeff@broadcastbeat.com" className="font-body text-[#3b82f6] text-sm hover:underline">
|
<a href="mailto:jeff@avbeat.com" className="font-body text-[#E67E22] text-sm hover:underline">
|
||||||
jeff@broadcastbeat.com
|
jeff@avbeat.com
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-[#222] pt-4">
|
<div className="border-t border-[#222] pt-4">
|
||||||
<p className="font-body text-[#3b82f6] text-xs font-bold uppercase tracking-wider mb-1">Publication</p>
|
<p className="font-body text-[#E67E22] text-xs font-bold uppercase tracking-wider mb-1">Publication</p>
|
||||||
<p className="font-body text-[#777] text-sm">Broadcast Beat</p>
|
<p className="font-body text-[#777] text-sm">AV Beat</p>
|
||||||
<p className="font-body text-[#777] text-sm">Relevant Media Properties</p>
|
<p className="font-body text-[#777] text-sm">Relevant Media Properties</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-[#222] pt-4">
|
<div className="border-t border-[#222] pt-4">
|
||||||
<p className="font-body text-[#3b82f6] text-xs font-bold uppercase tracking-wider mb-2">What We Offer</p>
|
<p className="font-body text-[#E67E22] text-xs font-bold uppercase tracking-wider mb-2">What We Offer</p>
|
||||||
<ul className="space-y-1.5">
|
<ul className="space-y-1.5">
|
||||||
{["Display advertising (web)", "Newsletter sponsorships", "Sponsored content", "Event coverage partnerships", "Podcast sponsorships"].map((item) => (
|
{["Display advertising (web)", "Newsletter sponsorships", "Sponsored content", "Event coverage partnerships", "Podcast sponsorships"].map((item) => (
|
||||||
<li key={item} className="flex items-start gap-2 text-[#777] text-sm font-body">
|
<li key={item} className="flex items-start gap-2 text-[#777] text-sm font-body">
|
||||||
<span className="text-[#3b82f6] mt-0.5">✓</span>
|
<span className="text-[#E67E22] mt-0.5">✓</span>
|
||||||
{item}
|
{item}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -152,16 +152,16 @@ export default function AdvertisePage() {
|
|||||||
{/* Form */}
|
{/* Form */}
|
||||||
<div className="lg:col-span-8">
|
<div className="lg:col-span-8">
|
||||||
{submitted ? (
|
{submitted ? (
|
||||||
<div className="bg-[#111] border border-[#3b82f6] p-8 text-center">
|
<div className="bg-[#111] border border-[#E67E22] p-8 text-center">
|
||||||
<div className="w-12 h-12 rounded-full bg-[#3b82f6]/20 flex items-center justify-center mx-auto mb-4">
|
<div className="w-12 h-12 rounded-full bg-[#E67E22]/20 flex items-center justify-center mx-auto mb-4">
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||||
<path d="M5 12l4 4 10-10" stroke="#3b82f6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M5 12l4 4 10-10" stroke="#E67E22" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-heading text-[#e0e0e0] text-xl font-bold mb-2">Message Sent!</h3>
|
<h3 className="font-heading text-[#e0e0e0] text-xl font-bold mb-2">Message Sent!</h3>
|
||||||
<p className="font-body text-[#777] text-sm">
|
<p className="font-body text-[#777] text-sm">
|
||||||
Thanks for reaching out. Jeff will be in touch within one business day at{" "}
|
Thanks for reaching out. Jeff will be in touch within one business day at{" "}
|
||||||
<a href="mailto:jeff@broadcastbeat.com" className="text-[#3b82f6] hover:underline">jeff@broadcastbeat.com</a>.
|
<a href="mailto:jeff@avbeat.com" className="text-[#E67E22] hover:underline">jeff@avbeat.com</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -245,7 +245,7 @@ export default function AdvertisePage() {
|
|||||||
{loading ? "Sending..." : "Send Inquiry"}
|
{loading ? "Sending..." : "Send Inquiry"}
|
||||||
</button>
|
</button>
|
||||||
<p className="text-[#555] text-xs font-body">
|
<p className="text-[#555] text-xs font-body">
|
||||||
Or email directly: <a href="mailto:jeff@broadcastbeat.com" className="text-[#3b82f6] hover:underline">jeff@broadcastbeat.com</a>
|
Or email directly: <a href="mailto:jeff@avbeat.com" className="text-[#E67E22] hover:underline">jeff@avbeat.com</a>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'The Crew Lounge — Broadcast Beat',
|
title: 'The Crew Lounge — AV Beat',
|
||||||
description: 'The Crew Lounge: connect with broadcast, motion picture and post production professionals worldwide. Discuss live production, IP workflows, streaming, audio, cameras, AI automation, and more.',
|
description: 'The Crew Lounge: connect with broadcast, motion picture and post production professionals worldwide. Discuss live production, IP workflows, streaming, audio, cameras, AI automation, and more.',
|
||||||
alternates: { canonical: '/forum' },
|
alternates: { canonical: '/forum' },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'The Crew Lounge — Broadcast Beat',
|
title: 'The Crew Lounge — AV Beat',
|
||||||
description: 'The Crew Lounge: connect with broadcast, motion picture and post production professionals worldwide. Ask questions, share expertise, and discuss broadcast technology.',
|
description: 'The Crew Lounge: connect with broadcast, motion picture and post production professionals worldwide. Ask questions, share expertise, and discuss broadcast technology.',
|
||||||
url: '/forum',
|
url: '/forum',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: 'summary',
|
card: 'summary',
|
||||||
title: 'The Crew Lounge — Broadcast Beat',
|
title: 'The Crew Lounge — AV Beat',
|
||||||
description: 'The Crew Lounge: connect with broadcast, motion picture and post production professionals worldwide on Broadcast Beat.',
|
description: 'The Crew Lounge: connect with broadcast, motion picture and post production professionals worldwide on AV Beat.',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,19 +21,19 @@ export default function ForumLayout({ children }: { children: React.ReactNode })
|
|||||||
const schema = {
|
const schema = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'WebPage',
|
'@type': 'WebPage',
|
||||||
name: 'Broadcast Beat — The Crew Lounge',
|
name: 'AV Beat — The Crew Lounge',
|
||||||
description: 'The Crew Lounge: an industry community for broadcast, motion picture and post production professionals worldwide.',
|
description: 'The Crew Lounge: an industry community for broadcast, motion picture and post production professionals worldwide.',
|
||||||
url: 'https://broadcastbeat.com/forum',
|
url: 'https://avbeat.com/forum',
|
||||||
isPartOf: {
|
isPartOf: {
|
||||||
'@type': 'WebSite',
|
'@type': 'WebSite',
|
||||||
name: 'Broadcast Beat',
|
name: 'AV Beat',
|
||||||
url: 'https://broadcastbeat.com',
|
url: 'https://avbeat.com',
|
||||||
},
|
},
|
||||||
breadcrumb: {
|
breadcrumb: {
|
||||||
'@type': 'BreadcrumbList',
|
'@type': 'BreadcrumbList',
|
||||||
itemListElement: [
|
itemListElement: [
|
||||||
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://broadcastbeat.com' },
|
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://avbeat.com' },
|
||||||
{ '@type': 'ListItem', position: 2, name: 'The Crew Lounge', item: 'https://broadcastbeat.com/forum' },
|
{ '@type': 'ListItem', position: 2, name: 'The Crew Lounge', item: 'https://avbeat.com/forum' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ function ForumLoginInner() {
|
|||||||
<main className="min-h-screen bg-gradient-to-b from-[#0d0d0d] via-[#111111] to-[#0d0d0d]">
|
<main className="min-h-screen bg-gradient-to-b from-[#0d0d0d] via-[#111111] to-[#0d0d0d]">
|
||||||
<div className="max-w-md mx-auto px-4 py-16">
|
<div className="max-w-md mx-auto px-4 py-16">
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<div className="text-[10px] uppercase tracking-widest text-[#3b82f6] font-mono mb-2">Welcome back</div>
|
<div className="text-[10px] uppercase tracking-widest text-[#E67E22] font-mono mb-2">Welcome back</div>
|
||||||
<h1 className="text-3xl font-heading font-bold text-white">Sign in to Broadcast Beat</h1>
|
<h1 className="text-3xl font-heading font-bold text-white">Sign in to AV Beat</h1>
|
||||||
<p className="text-[#888] font-body text-sm mt-2">
|
<p className="text-[#888] font-body text-sm mt-2">
|
||||||
Your account covers the forum, newsletter preferences, and saved articles.
|
Your account covers the forum, newsletter preferences, and saved articles.
|
||||||
</p>
|
</p>
|
||||||
@@ -79,7 +79,7 @@ function ForumLoginInner() {
|
|||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
required
|
required
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -92,20 +92,20 @@ function ForumLoginInner() {
|
|||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
required
|
required
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full bg-[#3b82f6] hover:bg-[#2563eb] disabled:opacity-50 text-white font-body font-semibold text-sm px-5 py-2.5 rounded-lg transition-colors"
|
className="w-full bg-[#E67E22] hover:bg-[#C8651B] disabled:opacity-50 text-white font-body font-semibold text-sm px-5 py-2.5 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{loading ? 'Signing in…' : 'Sign In'}
|
{loading ? 'Signing in…' : 'Sign In'}
|
||||||
</button>
|
</button>
|
||||||
<div className="text-right text-xs font-body">
|
<div className="text-right text-xs font-body">
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/forgot-password?email=${encodeURIComponent(email)}`}
|
href={`/forum/forgot-password?email=${encodeURIComponent(email)}`}
|
||||||
className="text-[#888] hover:text-[#3b82f6] hover:underline"
|
className="text-[#888] hover:text-[#E67E22] hover:underline"
|
||||||
>
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
@@ -115,7 +115,7 @@ function ForumLoginInner() {
|
|||||||
No account yet?{' '}
|
No account yet?{' '}
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/register?next=${encodeURIComponent(next)}`}
|
href={`/forum/register?next=${encodeURIComponent(next)}`}
|
||||||
className="text-[#3b82f6] hover:underline"
|
className="text-[#E67E22] hover:underline"
|
||||||
>
|
>
|
||||||
Create one
|
Create one
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ function TopThreadsWidget({ categoryId }: { categoryId?: string }) {
|
|||||||
|
|
||||||
const getMetricValue = (thread: ForumThread) => {
|
const getMetricValue = (thread: ForumThread) => {
|
||||||
if (activeMetric === 'upvotes') return { value: thread.upvotes ?? 0, label: 'votes', color: 'text-[#22c55e]' };
|
if (activeMetric === 'upvotes') return { value: thread.upvotes ?? 0, label: 'votes', color: 'text-[#22c55e]' };
|
||||||
if (activeMetric === 'replies') return { value: thread.reply_count, label: 'replies', color: 'text-[#3b82f6]' };
|
if (activeMetric === 'replies') return { value: thread.reply_count, label: 'replies', color: 'text-[#E67E22]' };
|
||||||
return { value: thread.view_count, label: 'views', color: 'text-[#f59e0b]' };
|
return { value: thread.view_count, label: 'views', color: 'text-[#f59e0b]' };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ function TopThreadsWidget({ categoryId }: { categoryId?: string }) {
|
|||||||
onClick={() => setActiveMetric(m.value)}
|
onClick={() => setActiveMetric(m.value)}
|
||||||
className={`px-3 py-1 rounded-md text-xs font-body font-semibold transition-colors whitespace-nowrap ${
|
className={`px-3 py-1 rounded-md text-xs font-body font-semibold transition-colors whitespace-nowrap ${
|
||||||
activeMetric === m.value
|
activeMetric === m.value
|
||||||
? 'bg-[#3b82f6] text-white'
|
? 'bg-[#E67E22] text-white'
|
||||||
: 'text-[#666] hover:text-[#aaa]'
|
: 'text-[#666] hover:text-[#aaa]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -171,13 +171,13 @@ function TopThreadsWidget({ categoryId }: { categoryId?: string }) {
|
|||||||
</span>
|
</span>
|
||||||
{/* Thread info */}
|
{/* Thread info */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-white font-body text-sm font-semibold group-hover:text-[#3b82f6] transition-colors truncate leading-snug">
|
<p className="text-white font-body text-sm font-semibold group-hover:text-[#E67E22] transition-colors truncate leading-snug">
|
||||||
{thread.title}
|
{thread.title}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-[#555] font-body text-xs mt-0.5">
|
<p className="text-[#555] font-body text-xs mt-0.5">
|
||||||
<Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-[#666] hover:text-[#3b82f6] hover:underline" onClick={(e) => e.stopPropagation()}>{thread.author_name}</Link>
|
<Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-[#666] hover:text-[#E67E22] hover:underline" onClick={(e) => e.stopPropagation()}>{thread.author_name}</Link>
|
||||||
{thread.forum_categories && (
|
{thread.forum_categories && (
|
||||||
<> · <span className="text-[#3b82f6]">{thread.forum_categories.name}</span></>
|
<> · <span className="text-[#E67E22]">{thread.forum_categories.name}</span></>
|
||||||
)}
|
)}
|
||||||
{' · '}{timeAgo(thread.created_at)}
|
{' · '}{timeAgo(thread.created_at)}
|
||||||
</p>
|
</p>
|
||||||
@@ -301,7 +301,7 @@ export default function ForumIndexPage() {
|
|||||||
{currentUserId ? (
|
{currentUserId ? (
|
||||||
<Link
|
<Link
|
||||||
href="/forum/new"
|
href="/forum/new"
|
||||||
className="inline-flex items-center gap-2 bg-[#3b82f6] hover:bg-[#2563eb] text-white font-body font-semibold text-sm rounded-lg px-5 py-2.5 transition-colors"
|
className="inline-flex items-center gap-2 bg-[#E67E22] hover:bg-[#C8651B] text-white font-body font-semibold text-sm rounded-lg px-5 py-2.5 transition-colors"
|
||||||
>
|
>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M12 5v14M5 12h14" />
|
<path d="M12 5v14M5 12h14" />
|
||||||
@@ -311,7 +311,7 @@ export default function ForumIndexPage() {
|
|||||||
) : (
|
) : (
|
||||||
<Link
|
<Link
|
||||||
href="/forum/login?next=/forum"
|
href="/forum/login?next=/forum"
|
||||||
className="inline-flex items-center gap-2 bg-[#3b82f6] hover:bg-[#2563eb] text-white font-body font-semibold text-sm rounded-lg px-5 py-2.5 transition-colors"
|
className="inline-flex items-center gap-2 bg-[#E67E22] hover:bg-[#C8651B] text-white font-body font-semibold text-sm rounded-lg px-5 py-2.5 transition-colors"
|
||||||
>
|
>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M15 12H3" />
|
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M15 12H3" />
|
||||||
@@ -333,7 +333,7 @@ export default function ForumIndexPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => setFeedMode('discover')}
|
onClick={() => setFeedMode('discover')}
|
||||||
className={`px-4 py-2 rounded-md text-sm font-body font-semibold transition-colors flex items-center gap-2 ${
|
className={`px-4 py-2 rounded-md text-sm font-body font-semibold transition-colors flex items-center gap-2 ${
|
||||||
feedMode === 'discover' ? 'bg-[#3b82f6] text-white' : 'text-[#888] hover:text-white'
|
feedMode === 'discover' ? 'bg-[#E67E22] text-white' : 'text-[#888] hover:text-white'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
@@ -344,7 +344,7 @@ export default function ForumIndexPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => setFeedMode('following')}
|
onClick={() => setFeedMode('following')}
|
||||||
className={`px-4 py-2 rounded-md text-sm font-body font-semibold transition-colors flex items-center gap-2 ${
|
className={`px-4 py-2 rounded-md text-sm font-body font-semibold transition-colors flex items-center gap-2 ${
|
||||||
feedMode === 'following' ? 'bg-[#3b82f6] text-white' : 'text-[#888] hover:text-white'
|
feedMode === 'following' ? 'bg-[#E67E22] text-white' : 'text-[#888] hover:text-white'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
@@ -363,7 +363,7 @@ export default function ForumIndexPage() {
|
|||||||
<p className="text-[#aab4c4] font-body text-sm mb-3">Sign in to see threads from people you follow.</p>
|
<p className="text-[#aab4c4] font-body text-sm mb-3">Sign in to see threads from people you follow.</p>
|
||||||
<Link
|
<Link
|
||||||
href="/login"
|
href="/login"
|
||||||
className="inline-block bg-[#3b82f6] text-white font-body text-sm font-bold px-5 py-2 rounded-lg hover:bg-[#2563eb] transition-colors"
|
className="inline-block bg-[#E67E22] text-white font-body text-sm font-bold px-5 py-2 rounded-lg hover:bg-[#C8651B] transition-colors"
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</Link>
|
</Link>
|
||||||
@@ -384,7 +384,7 @@ export default function ForumIndexPage() {
|
|||||||
<Link
|
<Link
|
||||||
href="/forum"
|
href="/forum"
|
||||||
onClick={() => setFeedMode('discover')}
|
onClick={() => setFeedMode('discover')}
|
||||||
className="inline-block bg-[#3b82f6] text-white font-body text-sm font-bold px-5 py-2 rounded-lg hover:bg-[#2563eb] transition-colors"
|
className="inline-block bg-[#E67E22] text-white font-body text-sm font-bold px-5 py-2 rounded-lg hover:bg-[#C8651B] transition-colors"
|
||||||
>
|
>
|
||||||
Discover Members
|
Discover Members
|
||||||
</Link>
|
</Link>
|
||||||
@@ -398,10 +398,10 @@ export default function ForumIndexPage() {
|
|||||||
<Link
|
<Link
|
||||||
key={thread.id}
|
key={thread.id}
|
||||||
href={`/forum/thread/${thread.id}`}
|
href={`/forum/thread/${thread.id}`}
|
||||||
className="flex items-center gap-4 bg-[#1a1a1a] hover:bg-[#1e2a3a] border border-[#252525] hover:border-[#3b82f6] rounded-lg px-4 py-3 transition-all group"
|
className="flex items-center gap-4 bg-[#1a1a1a] hover:bg-[#1e2a3a] border border-[#252525] hover:border-[#E67E22] rounded-lg px-4 py-3 transition-all group"
|
||||||
>
|
>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-white font-body font-semibold text-sm group-hover:text-[#3b82f6] transition-colors truncate">
|
<h3 className="text-white font-body font-semibold text-sm group-hover:text-[#E67E22] transition-colors truncate">
|
||||||
{thread.title}
|
{thread.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-[#666] font-body text-xs mt-0.5">
|
<p className="text-[#666] font-body text-xs mt-0.5">
|
||||||
@@ -409,7 +409,7 @@ export default function ForumIndexPage() {
|
|||||||
<Link
|
<Link
|
||||||
href={`/profile/${thread.author_id}`}
|
href={`/profile/${thread.author_id}`}
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
className="text-[#3b82f6] hover:underline"
|
className="text-[#E67E22] hover:underline"
|
||||||
>
|
>
|
||||||
{thread.author_name}
|
{thread.author_name}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -450,7 +450,7 @@ export default function ForumIndexPage() {
|
|||||||
placeholder="Search threads..."
|
placeholder="Search threads..."
|
||||||
value={search}
|
value={search}
|
||||||
onChange={e => setSearch(e.target.value)}
|
onChange={e => setSearch(e.target.value)}
|
||||||
className="w-full bg-[#1a1a1a] border border-[#2a2a2a] focus:border-[#3b82f6] rounded-lg pl-9 pr-4 py-2.5 text-white font-body text-sm placeholder-[#555] focus:outline-none transition-colors"
|
className="w-full bg-[#1a1a1a] border border-[#2a2a2a] focus:border-[#E67E22] rounded-lg pl-9 pr-4 py-2.5 text-white font-body text-sm placeholder-[#555] focus:outline-none transition-colors"
|
||||||
/>
|
/>
|
||||||
{search && (
|
{search && (
|
||||||
<button
|
<button
|
||||||
@@ -470,7 +470,7 @@ export default function ForumIndexPage() {
|
|||||||
<select
|
<select
|
||||||
value={selectedCategory}
|
value={selectedCategory}
|
||||||
onChange={e => setSelectedCategory(e.target.value)}
|
onChange={e => setSelectedCategory(e.target.value)}
|
||||||
className="appearance-none bg-[#1a1a1a] border border-[#2a2a2a] focus:border-[#3b82f6] rounded-lg pl-3 pr-8 py-2.5 text-sm font-body text-white focus:outline-none transition-colors cursor-pointer min-w-[160px]"
|
className="appearance-none bg-[#1a1a1a] border border-[#2a2a2a] focus:border-[#E67E22] rounded-lg pl-3 pr-8 py-2.5 text-sm font-body text-white focus:outline-none transition-colors cursor-pointer min-w-[160px]"
|
||||||
>
|
>
|
||||||
<option value="">All Categories</option>
|
<option value="">All Categories</option>
|
||||||
{categories.map(cat => (
|
{categories.map(cat => (
|
||||||
@@ -490,7 +490,7 @@ export default function ForumIndexPage() {
|
|||||||
onClick={() => setSort(opt.value)}
|
onClick={() => setSort(opt.value)}
|
||||||
className={`px-3 py-1.5 rounded-md text-xs font-body font-semibold transition-colors whitespace-nowrap ${
|
className={`px-3 py-1.5 rounded-md text-xs font-body font-semibold transition-colors whitespace-nowrap ${
|
||||||
sort === opt.value
|
sort === opt.value
|
||||||
? 'bg-[#3b82f6] text-white'
|
? 'bg-[#E67E22] text-white'
|
||||||
: 'text-[#888] hover:text-white'
|
: 'text-[#888] hover:text-white'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -523,16 +523,16 @@ export default function ForumIndexPage() {
|
|||||||
<Link
|
<Link
|
||||||
key={thread.id}
|
key={thread.id}
|
||||||
href={`/forum/thread/${thread.id}`}
|
href={`/forum/thread/${thread.id}`}
|
||||||
className="flex items-center gap-4 bg-[#1a1a1a] hover:bg-[#1e2a3a] border border-[#252525] hover:border-[#3b82f6] rounded-lg px-4 py-3 transition-all group"
|
className="flex items-center gap-4 bg-[#1a1a1a] hover:bg-[#1e2a3a] border border-[#252525] hover:border-[#E67E22] rounded-lg px-4 py-3 transition-all group"
|
||||||
>
|
>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h3 className="text-white font-body font-semibold text-sm group-hover:text-[#3b82f6] transition-colors truncate">
|
<h3 className="text-white font-body font-semibold text-sm group-hover:text-[#E67E22] transition-colors truncate">
|
||||||
{thread.title}
|
{thread.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-[#666] font-body text-xs mt-0.5">
|
<p className="text-[#666] font-body text-xs mt-0.5">
|
||||||
by <Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-[#888] hover:text-[#3b82f6] hover:underline" onClick={(e) => e.stopPropagation()}>{thread.author_name}</Link>
|
by <Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-[#888] hover:text-[#E67E22] hover:underline" onClick={(e) => e.stopPropagation()}>{thread.author_name}</Link>
|
||||||
{thread.forum_categories && (
|
{thread.forum_categories && (
|
||||||
<> · <span className="text-[#3b82f6]">{thread.forum_categories.name}</span></>
|
<> · <span className="text-[#E67E22]">{thread.forum_categories.name}</span></>
|
||||||
)}
|
)}
|
||||||
{' · '}{timeAgo(thread.created_at)}
|
{' · '}{timeAgo(thread.created_at)}
|
||||||
</p>
|
</p>
|
||||||
@@ -584,11 +584,11 @@ export default function ForumIndexPage() {
|
|||||||
<Link
|
<Link
|
||||||
key={cat.id}
|
key={cat.id}
|
||||||
href={`/forum/${cat.slug}`}
|
href={`/forum/${cat.slug}`}
|
||||||
className="flex items-center gap-4 bg-[#1a1a1a] hover:bg-[#1e2a3a] border border-[#252525] hover:border-[#3b82f6] rounded-lg p-4 transition-all group"
|
className="flex items-center gap-4 bg-[#1a1a1a] hover:bg-[#1e2a3a] border border-[#252525] hover:border-[#E67E22] rounded-lg p-4 transition-all group"
|
||||||
>
|
>
|
||||||
<span className="text-3xl flex-shrink-0 w-10 text-center">{cat.icon}</span>
|
<span className="text-3xl flex-shrink-0 w-10 text-center">{cat.icon}</span>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<h2 className="text-white font-heading font-semibold text-lg group-hover:text-[#3b82f6] transition-colors">
|
<h2 className="text-white font-heading font-semibold text-lg group-hover:text-[#E67E22] transition-colors">
|
||||||
{cat.name}
|
{cat.name}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-[#888] font-body text-sm mt-0.5 truncate">{cat.description}</p>
|
<p className="text-[#888] font-body text-sm mt-0.5 truncate">{cat.description}</p>
|
||||||
@@ -610,7 +610,7 @@ export default function ForumIndexPage() {
|
|||||||
<div className="text-white font-body font-semibold text-sm">{cat.post_count}</div>
|
<div className="text-white font-body font-semibold text-sm">{cat.post_count}</div>
|
||||||
<div className="text-[#666] font-body text-xs">posts</div>
|
<div className="text-[#666] font-body text-xs">posts</div>
|
||||||
</div>
|
</div>
|
||||||
<svg className="w-4 h-4 text-[#555] group-hover:text-[#3b82f6] flex-shrink-0 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 text-[#555] group-hover:text-[#E67E22] flex-shrink-0 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -629,7 +629,7 @@ export default function ForumIndexPage() {
|
|||||||
<ul className="text-[#aab4c4] font-body text-sm space-y-1">
|
<ul className="text-[#aab4c4] font-body text-sm space-y-1">
|
||||||
<li>• Be respectful and professional — this is a community of peers</li>
|
<li>• Be respectful and professional — this is a community of peers</li>
|
||||||
<li>• Share real-world experience and cite sources when possible</li>
|
<li>• Share real-world experience and cite sources when possible</li>
|
||||||
<li>• The <span className="text-[#3b82f6]">Broadcast Beat AI</span> assistant is available in threads to help answer questions — it is clearly labeled</li>
|
<li>• The <span className="text-[#E67E22]">AV Beat AI</span> assistant is available in threads to help answer questions — it is clearly labeled</li>
|
||||||
<li>• No spam, self-promotion, or vendor pitches without disclosure</li>
|
<li>• No spam, self-promotion, or vendor pitches without disclosure</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -105,9 +105,9 @@ function ForumRegisterInner() {
|
|||||||
<div className="max-w-5xl mx-auto px-4 py-12 grid grid-cols-1 lg:grid-cols-[1fr_440px] gap-10 items-start">
|
<div className="max-w-5xl mx-auto px-4 py-12 grid grid-cols-1 lg:grid-cols-[1fr_440px] gap-10 items-start">
|
||||||
{/* Left rail — what membership gets you */}
|
{/* Left rail — what membership gets you */}
|
||||||
<aside className="hidden lg:block">
|
<aside className="hidden lg:block">
|
||||||
<div className="text-[10px] uppercase tracking-widest text-[#3b82f6] font-mono mb-3">Membership</div>
|
<div className="text-[10px] uppercase tracking-widest text-[#E67E22] font-mono mb-3">Membership</div>
|
||||||
<h1 className="font-heading text-white text-4xl xl:text-5xl font-bold leading-tight mb-4">
|
<h1 className="font-heading text-white text-4xl xl:text-5xl font-bold leading-tight mb-4">
|
||||||
Join Broadcast Beat
|
Join AV Beat
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-[#cbd5e1] text-base leading-relaxed mb-6 max-w-md">
|
<p className="text-[#cbd5e1] text-base leading-relaxed mb-6 max-w-md">
|
||||||
One free account for the broadcast engineering community — covering NAB, IBC, and the people behind every signal chain.
|
One free account for the broadcast engineering community — covering NAB, IBC, and the people behind every signal chain.
|
||||||
@@ -131,13 +131,13 @@ function ForumRegisterInner() {
|
|||||||
<div className="mt-6 text-[11px] text-[#666] leading-relaxed max-w-md border-t border-[#252525] pt-4">
|
<div className="mt-6 text-[11px] text-[#666] leading-relaxed max-w-md border-t border-[#252525] pt-4">
|
||||||
<strong className="text-[#888]">PR firms & contributors:</strong> a separate contributor application is required to submit press releases.
|
<strong className="text-[#888]">PR firms & contributors:</strong> a separate contributor application is required to submit press releases.
|
||||||
{" "}
|
{" "}
|
||||||
<Link href="/about/advertise" className="text-[#3b82f6] hover:underline">Contact us</Link> if you represent a manufacturer or agency.
|
<Link href="/about/advertise" className="text-[#E67E22] hover:underline">Contact us</Link> if you represent a manufacturer or agency.
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* Mobile-only hero (collapsed copy) */}
|
{/* Mobile-only hero (collapsed copy) */}
|
||||||
<div className="lg:hidden text-center mb-2">
|
<div className="lg:hidden text-center mb-2">
|
||||||
<h1 className="font-heading text-white text-2xl font-bold">Join Broadcast Beat</h1>
|
<h1 className="font-heading text-white text-2xl font-bold">Join AV Beat</h1>
|
||||||
<p className="text-[#888] font-body text-sm mt-1">
|
<p className="text-[#888] font-body text-sm mt-1">
|
||||||
One account for the forum, newsletter preferences, and saved articles.
|
One account for the forum, newsletter preferences, and saved articles.
|
||||||
</p>
|
</p>
|
||||||
@@ -148,7 +148,7 @@ function ForumRegisterInner() {
|
|||||||
className="bg-[#1a1a1a] border border-[#252525] rounded-lg p-6 space-y-4 shadow-2xl"
|
className="bg-[#1a1a1a] border border-[#252525] rounded-lg p-6 space-y-4 shadow-2xl"
|
||||||
>
|
>
|
||||||
<div className="hidden lg:block mb-2">
|
<div className="hidden lg:block mb-2">
|
||||||
<div className="text-[10px] uppercase tracking-widest text-[#3b82f6] font-mono mb-1">Create your account</div>
|
<div className="text-[10px] uppercase tracking-widest text-[#E67E22] font-mono mb-1">Create your account</div>
|
||||||
<div className="text-[#888] text-xs">Takes 30 seconds. Free, no card needed.</div>
|
<div className="text-[#888] text-xs">Takes 30 seconds. Free, no card needed.</div>
|
||||||
</div>
|
</div>
|
||||||
{error && (
|
{error && (
|
||||||
@@ -171,7 +171,7 @@ function ForumRegisterInner() {
|
|||||||
onChange={(e) => setFullName(e.target.value)}
|
onChange={(e) => setFullName(e.target.value)}
|
||||||
autoComplete="name"
|
autoComplete="name"
|
||||||
required
|
required
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -184,7 +184,7 @@ function ForumRegisterInner() {
|
|||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
required
|
required
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -198,7 +198,7 @@ function ForumRegisterInner() {
|
|||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
required
|
required
|
||||||
minLength={6}
|
minLength={6}
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -211,21 +211,21 @@ function ForumRegisterInner() {
|
|||||||
onChange={(e) => setConfirm(e.target.value)}
|
onChange={(e) => setConfirm(e.target.value)}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
required
|
required
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] rounded-lg px-3 py-2 text-white font-body text-sm focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full bg-[#3b82f6] hover:bg-[#2563eb] disabled:opacity-50 text-white font-body font-semibold text-sm px-5 py-3 rounded-lg transition-colors"
|
className="w-full bg-[#E67E22] hover:bg-[#C8651B] disabled:opacity-50 text-white font-body font-semibold text-sm px-5 py-3 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{loading ? 'Creating account…' : 'Create Free Account'}
|
{loading ? 'Creating account…' : 'Create Free Account'}
|
||||||
</button>
|
</button>
|
||||||
<p className="text-[10px] text-[#666] leading-relaxed text-center px-2">
|
<p className="text-[10px] text-[#666] leading-relaxed text-center px-2">
|
||||||
By creating an account you agree to our{' '}
|
By creating an account you agree to our{' '}
|
||||||
<Link href="/terms" className="text-[#888] hover:text-[#3b82f6] underline">terms</Link>
|
<Link href="/terms" className="text-[#888] hover:text-[#E67E22] underline">terms</Link>
|
||||||
{' '}and{' '}
|
{' '}and{' '}
|
||||||
<Link href="/privacy" className="text-[#888] hover:text-[#3b82f6] underline">privacy policy</Link>.
|
<Link href="/privacy" className="text-[#888] hover:text-[#E67E22] underline">privacy policy</Link>.
|
||||||
We’ll never sell your email.
|
We’ll never sell your email.
|
||||||
</p>
|
</p>
|
||||||
<SocialAuthButtons next={next} />
|
<SocialAuthButtons next={next} />
|
||||||
@@ -233,7 +233,7 @@ function ForumRegisterInner() {
|
|||||||
Already have an account?{' '}
|
Already have an account?{' '}
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/login?next=${encodeURIComponent(next)}`}
|
href={`/forum/login?next=${encodeURIComponent(next)}`}
|
||||||
className="text-[#3b82f6] hover:underline font-semibold"
|
className="text-[#E67E22] hover:underline font-semibold"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ function timeAgo(dateStr: string): string {
|
|||||||
function Avatar({ name, isAI }: { name: string; isAI: boolean }) {
|
function Avatar({ name, isAI }: { name: string; isAI: boolean }) {
|
||||||
if (isAI) {
|
if (isAI) {
|
||||||
return (
|
return (
|
||||||
<div className="w-9 h-9 rounded-full bg-[#1e3a5f] border border-[#3b82f6] flex items-center justify-center flex-shrink-0">
|
<div className="w-9 h-9 rounded-full bg-[#1e3a5f] border border-[#E67E22] flex items-center justify-center flex-shrink-0">
|
||||||
<span className="text-[#3b82f6] text-xs font-bold">AI</span>
|
<span className="text-[#E67E22] text-xs font-bold">AI</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ function VoteButtons({ targetType, targetId, upvotes, downvotes, voteScore, isAI
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const scoreColor = counts.vote_score > 0 ? 'text-[#3b82f6]' : counts.vote_score < 0 ? 'text-red-400' : 'text-[#666]';
|
const scoreColor = counts.vote_score > 0 ? 'text-[#E67E22]' : counts.vote_score < 0 ? 'text-red-400' : 'text-[#666]';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-1 mt-2">
|
<div className="flex items-center gap-1 mt-2">
|
||||||
@@ -144,8 +144,8 @@ function VoteButtons({ targetType, targetId, upvotes, downvotes, voteScore, isAI
|
|||||||
title={!isAuthenticated ? 'Sign in to vote' : isAI ? 'Cannot vote on AI responses' : 'Upvote'}
|
title={!isAuthenticated ? 'Sign in to vote' : isAI ? 'Cannot vote on AI responses' : 'Upvote'}
|
||||||
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-body font-semibold transition-colors border ${
|
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-body font-semibold transition-colors border ${
|
||||||
currentVote === 1
|
currentVote === 1
|
||||||
? 'bg-[#3b82f6]/20 border-[#3b82f6] text-[#3b82f6]'
|
? 'bg-[#E67E22]/20 border-[#E67E22] text-[#E67E22]'
|
||||||
: 'bg-transparent border-[#2a2a2a] text-[#666] hover:border-[#3b82f6] hover:text-[#3b82f6]'
|
: 'bg-transparent border-[#2a2a2a] text-[#666] hover:border-[#E67E22] hover:text-[#E67E22]'
|
||||||
} disabled:opacity-40 disabled:cursor-not-allowed`}
|
} disabled:opacity-40 disabled:cursor-not-allowed`}
|
||||||
aria-label="Upvote"
|
aria-label="Upvote"
|
||||||
>
|
>
|
||||||
@@ -178,7 +178,7 @@ function VoteButtons({ targetType, targetId, upvotes, downvotes, voteScore, isAI
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{!isAuthenticated && (
|
{!isAuthenticated && (
|
||||||
<Link href="/login" className="text-xs font-body text-[#555] hover:text-[#3b82f6] ml-1 transition-colors">
|
<Link href="/login" className="text-xs font-body text-[#555] hover:text-[#E67E22] ml-1 transition-colors">
|
||||||
Sign in to vote
|
Sign in to vote
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
@@ -322,7 +322,7 @@ export default function ForumThreadPage() {
|
|||||||
<div className="bg-red-900/20 border border-red-800 rounded-lg p-4 text-red-400 font-body">
|
<div className="bg-red-900/20 border border-red-800 rounded-lg p-4 text-red-400 font-body">
|
||||||
{error || 'Thread not found.'}
|
{error || 'Thread not found.'}
|
||||||
</div>
|
</div>
|
||||||
<Link href="/forum" className="mt-4 inline-block text-[#3b82f6] hover:underline font-body text-sm">
|
<Link href="/forum" className="mt-4 inline-block text-[#E67E22] hover:underline font-body text-sm">
|
||||||
← Back to Forum
|
← Back to Forum
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -342,11 +342,11 @@ export default function ForumThreadPage() {
|
|||||||
<div className="bg-[#1a2535] border-b border-[#2a3a50]">
|
<div className="bg-[#1a2535] border-b border-[#2a3a50]">
|
||||||
<div className="max-w-container mx-auto px-4 py-4">
|
<div className="max-w-container mx-auto px-4 py-4">
|
||||||
<nav className="text-sm font-body text-[#666]">
|
<nav className="text-sm font-body text-[#666]">
|
||||||
<Link href="/forum" className="hover:text-[#3b82f6] transition-colors">Forum</Link>
|
<Link href="/forum" className="hover:text-[#E67E22] transition-colors">Forum</Link>
|
||||||
<span className="mx-2">›</span>
|
<span className="mx-2">›</span>
|
||||||
{category && (
|
{category && (
|
||||||
<>
|
<>
|
||||||
<Link href={`/forum/${category.slug}`} className="hover:text-[#3b82f6] transition-colors">{category.name}</Link>
|
<Link href={`/forum/${category.slug}`} className="hover:text-[#E67E22] transition-colors">{category.name}</Link>
|
||||||
<span className="mx-2">›</span>
|
<span className="mx-2">›</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -366,7 +366,7 @@ export default function ForumThreadPage() {
|
|||||||
<Avatar name={thread.author_name} isAI={false} />
|
<Avatar name={thread.author_name} isAI={false} />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-white font-body font-semibold text-sm hover:text-[#3b82f6] hover:underline">{thread.author_name}</Link>
|
<Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-white font-body font-semibold text-sm hover:text-[#E67E22] hover:underline">{thread.author_name}</Link>
|
||||||
<span className="text-[#555] font-body text-xs">·</span>
|
<span className="text-[#555] font-body text-xs">·</span>
|
||||||
<span className="text-[#555] font-body text-xs">{timeAgo(thread.created_at)}</span>
|
<span className="text-[#555] font-body text-xs">{timeAgo(thread.created_at)}</span>
|
||||||
<span className="text-[#555] font-body text-xs">·</span>
|
<span className="text-[#555] font-body text-xs">·</span>
|
||||||
@@ -403,9 +403,9 @@ export default function ForumThreadPage() {
|
|||||||
<Avatar name={reply.author_name} isAI={reply.is_ai_response} />
|
<Avatar name={reply.author_name} isAI={reply.is_ai_response} />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
||||||
<Link href={`/forum/user/${encodeURIComponent(reply.author_name)}`} className="text-white font-body font-semibold text-sm hover:text-[#3b82f6] hover:underline">{reply.author_name}</Link>
|
<Link href={`/forum/user/${encodeURIComponent(reply.author_name)}`} className="text-white font-body font-semibold text-sm hover:text-[#E67E22] hover:underline">{reply.author_name}</Link>
|
||||||
{reply.is_ai_response && (
|
{reply.is_ai_response && (
|
||||||
<span className="text-xs bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/30 px-2 py-0.5 rounded font-body font-semibold">
|
<span className="text-xs bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/30 px-2 py-0.5 rounded font-body font-semibold">
|
||||||
AI Assistant
|
AI Assistant
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -443,12 +443,12 @@ export default function ForumThreadPage() {
|
|||||||
{/* AI Assistant Panel */}
|
{/* AI Assistant Panel */}
|
||||||
<div className="bg-[#0d1f35] border border-[#1e3a5f] rounded-lg p-5 mb-6">
|
<div className="bg-[#0d1f35] border border-[#1e3a5f] rounded-lg p-5 mb-6">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<div className="w-7 h-7 rounded-full bg-[#1e3a5f] border border-[#3b82f6] flex items-center justify-center">
|
<div className="w-7 h-7 rounded-full bg-[#1e3a5f] border border-[#E67E22] flex items-center justify-center">
|
||||||
<span className="text-[#3b82f6] text-xs font-bold">AI</span>
|
<span className="text-[#E67E22] text-xs font-bold">AI</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-white font-body font-semibold text-sm">Broadcast Beat AI Assistant</span>
|
<span className="text-white font-body font-semibold text-sm">AV Beat AI Assistant</span>
|
||||||
<span className="ml-2 text-xs bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/30 px-2 py-0.5 rounded font-body">AI — Not a human</span>
|
<span className="ml-2 text-xs bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/30 px-2 py-0.5 rounded font-body">AI — Not a human</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[#7a9ab8] font-body text-xs mb-3">
|
<p className="text-[#7a9ab8] font-body text-xs mb-3">
|
||||||
@@ -461,12 +461,12 @@ export default function ForumThreadPage() {
|
|||||||
value={aiQuestion}
|
value={aiQuestion}
|
||||||
onChange={e => setAiQuestion(e.target.value)}
|
onChange={e => setAiQuestion(e.target.value)}
|
||||||
disabled={aiLoading}
|
disabled={aiLoading}
|
||||||
className="flex-1 bg-[#111] border border-[#2a3a50] rounded-lg px-3 py-2 text-white font-body text-sm placeholder-[#3a5a7a] focus:outline-none focus:border-[#3b82f6] disabled:opacity-50"
|
className="flex-1 bg-[#111] border border-[#2a3a50] rounded-lg px-3 py-2 text-white font-body text-sm placeholder-[#3a5a7a] focus:outline-none focus:border-[#E67E22] disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={aiLoading || !aiQuestion.trim()}
|
disabled={aiLoading || !aiQuestion.trim()}
|
||||||
className="bg-[#3b82f6] hover:bg-[#2563eb] disabled:opacity-50 text-white font-body font-semibold text-sm px-4 py-2 rounded-lg transition-colors flex-shrink-0"
|
className="bg-[#E67E22] hover:bg-[#C8651B] disabled:opacity-50 text-white font-body font-semibold text-sm px-4 py-2 rounded-lg transition-colors flex-shrink-0"
|
||||||
>
|
>
|
||||||
{aiLoading ? (
|
{aiLoading ? (
|
||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
@@ -495,7 +495,7 @@ export default function ForumThreadPage() {
|
|||||||
onChange={e => setReplyBody(e.target.value)}
|
onChange={e => setReplyBody(e.target.value)}
|
||||||
required
|
required
|
||||||
rows={5}
|
rows={5}
|
||||||
className="w-full bg-[#111] border border-[#333] rounded-lg px-3 py-2 text-white font-body text-sm placeholder-[#555] focus:outline-none focus:border-[#3b82f6] resize-none"
|
className="w-full bg-[#111] border border-[#333] rounded-lg px-3 py-2 text-white font-body text-sm placeholder-[#555] focus:outline-none focus:border-[#E67E22] resize-none"
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<ImageAttachButton onInsert={(md) => setReplyBody((b) => (b || "") + md)} />
|
<ImageAttachButton onInsert={(md) => setReplyBody((b) => (b || "") + md)} />
|
||||||
@@ -509,7 +509,7 @@ export default function ForumThreadPage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting || !replyBody.trim()}
|
disabled={submitting || !replyBody.trim()}
|
||||||
className="bg-[#3b82f6] hover:bg-[#2563eb] disabled:opacity-50 text-white font-body font-semibold text-sm px-5 py-2 rounded-lg transition-colors"
|
className="bg-[#E67E22] hover:bg-[#C8651B] disabled:opacity-50 text-white font-body font-semibold text-sm px-5 py-2 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{submitting ? 'Posting...' : 'Post Reply'}
|
{submitting ? 'Posting...' : 'Post Reply'}
|
||||||
</button>
|
</button>
|
||||||
@@ -526,13 +526,13 @@ export default function ForumThreadPage() {
|
|||||||
<div className="flex items-center justify-center gap-3">
|
<div className="flex items-center justify-center gap-3">
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/login?next=${encodeURIComponent(`/forum/thread/${threadId}`)}`}
|
href={`/forum/login?next=${encodeURIComponent(`/forum/thread/${threadId}`)}`}
|
||||||
className="bg-[#3b82f6] hover:bg-[#2563eb] text-white font-body font-semibold text-sm px-5 py-2 rounded-lg transition-colors"
|
className="bg-[#E67E22] hover:bg-[#C8651B] text-white font-body font-semibold text-sm px-5 py-2 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/register?next=${encodeURIComponent(`/forum/thread/${threadId}`)}`}
|
href={`/forum/register?next=${encodeURIComponent(`/forum/thread/${threadId}`)}`}
|
||||||
className="border border-[#3b82f6] text-[#3b82f6] hover:bg-[#3b82f6]/10 font-body font-semibold text-sm px-5 py-2 rounded-lg transition-colors"
|
className="border border-[#E67E22] text-[#E67E22] hover:bg-[#E67E22]/10 font-body font-semibold text-sm px-5 py-2 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Register
|
Register
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ export default function GlobalError({
|
|||||||
<body style={{ background: "#0d0d0d", color: "#e0e0e0", fontFamily: "system-ui", padding: 24, minHeight: "100vh" }}>
|
<body style={{ background: "#0d0d0d", color: "#e0e0e0", fontFamily: "system-ui", padding: 24, minHeight: "100vh" }}>
|
||||||
<div style={{ maxWidth: 480, margin: "10vh auto", textAlign: "center" }}>
|
<div style={{ maxWidth: 480, margin: "10vh auto", textAlign: "center" }}>
|
||||||
<h2 style={{ fontSize: 22, marginBottom: 12 }}>Something went wrong</h2>
|
<h2 style={{ fontSize: 22, marginBottom: 12 }}>Something went wrong</h2>
|
||||||
<p style={{ marginBottom: 12, color: "#888" }}>Broadcast Beat hit an unexpected error.</p>
|
<p style={{ marginBottom: 12, color: "#888" }}>AV Beat hit an unexpected error.</p>
|
||||||
{error?.digest && (
|
{error?.digest && (
|
||||||
<p style={{ fontSize: 12, opacity: 0.5, marginBottom: 16, fontFamily: "monospace" }}>ref: {error.digest}</p>
|
<p style={{ fontSize: 12, opacity: 0.5, marginBottom: 16, fontFamily: "monospace" }}>ref: {error.digest}</p>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => reset()}
|
onClick={() => reset()}
|
||||||
style={{ padding: "8px 16px", background: "#3b82f6", color: "white", border: 0, cursor: "pointer", borderRadius: 2 }}
|
style={{ padding: "8px 16px", background: "#E67E22", color: "white", border: 0, cursor: "pointer", borderRadius: 2 }}
|
||||||
>
|
>
|
||||||
Try again
|
Try again
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ export default function ArticleFeed() {
|
|||||||
value={sortOrder}
|
value={sortOrder}
|
||||||
onChange={(e) => { setSortOrder(e.target.value as SortOrder); setPage(1); setInfiniteCount(ARTICLES_PER_PAGE); }}
|
onChange={(e) => { setSortOrder(e.target.value as SortOrder); setPage(1); setInfiniteCount(ARTICLES_PER_PAGE); }}
|
||||||
aria-label="Sort articles"
|
aria-label="Sort articles"
|
||||||
className="bg-[#111] border border-[#2a2a2a] text-[#aaa] font-body text-[11px] font-semibold uppercase tracking-wide rounded-sm pl-2.5 pr-7 py-1 appearance-none focus:outline-none focus:border-[#3b82f6] focus:ring-1 focus:ring-[#3b82f6]/30 transition-colors [color-scheme:dark] cursor-pointer hover:border-[#3a3a3a]"
|
className="bg-[#111] border border-[#2a2a2a] text-[#aaa] font-body text-[11px] font-semibold uppercase tracking-wide rounded-sm pl-2.5 pr-7 py-1 appearance-none focus:outline-none focus:border-[#E67E22] focus:ring-1 focus:ring-[#E67E22]/30 transition-colors [color-scheme:dark] cursor-pointer hover:border-[#3a3a3a]"
|
||||||
>
|
>
|
||||||
<option value="recent">Recent</option>
|
<option value="recent">Recent</option>
|
||||||
<option value="trending">Trending</option>
|
<option value="trending">Trending</option>
|
||||||
@@ -333,8 +333,8 @@ export default function ArticleFeed() {
|
|||||||
onClick={() => handleFeedModeChange("pagination")}
|
onClick={() => handleFeedModeChange("pagination")}
|
||||||
aria-pressed={feedMode === "pagination"}
|
aria-pressed={feedMode === "pagination"}
|
||||||
title="Pagination mode"
|
title="Pagination mode"
|
||||||
className={`flex items-center gap-1 px-2.5 py-1 rounded-sm font-body text-[10px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`flex items-center gap-1 px-2.5 py-1 rounded-sm font-body text-[10px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
feedMode === "pagination" ?"bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/40" :"text-[#666] hover:text-[#aaa] border border-transparent"
|
feedMode === "pagination" ?"bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/40" :"text-[#666] hover:text-[#aaa] border border-transparent"
|
||||||
}`}>
|
}`}>
|
||||||
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||||
<rect x="1" y="1" width="10" height="2.5" rx="0.5" fill="currentColor" opacity="0.9"/>
|
<rect x="1" y="1" width="10" height="2.5" rx="0.5" fill="currentColor" opacity="0.9"/>
|
||||||
@@ -347,8 +347,8 @@ export default function ArticleFeed() {
|
|||||||
onClick={() => handleFeedModeChange("infinite")}
|
onClick={() => handleFeedModeChange("infinite")}
|
||||||
aria-pressed={feedMode === "infinite"}
|
aria-pressed={feedMode === "infinite"}
|
||||||
title="Infinite scroll mode"
|
title="Infinite scroll mode"
|
||||||
className={`flex items-center gap-1 px-2.5 py-1 rounded-sm font-body text-[10px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`flex items-center gap-1 px-2.5 py-1 rounded-sm font-body text-[10px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
feedMode === "infinite" ?"bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/40" :"text-[#666] hover:text-[#aaa] border border-transparent"
|
feedMode === "infinite" ?"bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/40" :"text-[#666] hover:text-[#aaa] border border-transparent"
|
||||||
}`}>
|
}`}>
|
||||||
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
<svg width="11" height="11" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||||
<rect x="1" y="1" width="10" height="2" rx="0.5" fill="currentColor" opacity="0.9"/>
|
<rect x="1" y="1" width="10" height="2" rx="0.5" fill="currentColor" opacity="0.9"/>
|
||||||
@@ -372,7 +372,7 @@ export default function ArticleFeed() {
|
|||||||
value={dateFrom}
|
value={dateFrom}
|
||||||
onChange={(e) => { setDateFrom(e.target.value); setPage(1); }}
|
onChange={(e) => { setDateFrom(e.target.value); setPage(1); }}
|
||||||
max={dateTo || undefined}
|
max={dateTo || undefined}
|
||||||
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2 rounded-sm focus:outline-none focus:border-[#3b82f6] transition-colors [color-scheme:dark]"
|
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2 rounded-sm focus:outline-none focus:border-[#E67E22] transition-colors [color-scheme:dark]"
|
||||||
title="From date"
|
title="From date"
|
||||||
aria-label="Filter articles from date"
|
aria-label="Filter articles from date"
|
||||||
/>
|
/>
|
||||||
@@ -382,7 +382,7 @@ export default function ArticleFeed() {
|
|||||||
value={dateTo}
|
value={dateTo}
|
||||||
onChange={(e) => { setDateTo(e.target.value); setPage(1); }}
|
onChange={(e) => { setDateTo(e.target.value); setPage(1); }}
|
||||||
min={dateFrom || undefined}
|
min={dateFrom || undefined}
|
||||||
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2 rounded-sm focus:outline-none focus:border-[#3b82f6] transition-colors [color-scheme:dark]"
|
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2 rounded-sm focus:outline-none focus:border-[#E67E22] transition-colors [color-scheme:dark]"
|
||||||
title="To date"
|
title="To date"
|
||||||
aria-label="Filter articles to date"
|
aria-label="Filter articles to date"
|
||||||
/>
|
/>
|
||||||
@@ -401,9 +401,9 @@ export default function ArticleFeed() {
|
|||||||
onClick={() => setShowAdvanced((v) => !v)}
|
onClick={() => setShowAdvanced((v) => !v)}
|
||||||
aria-expanded={showAdvanced}
|
aria-expanded={showAdvanced}
|
||||||
aria-controls={`${feedId}-advanced`}
|
aria-controls={`${feedId}-advanced`}
|
||||||
className={`flex items-center gap-1.5 px-3 py-2 rounded-sm border font-body text-[11px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] whitespace-nowrap ${
|
className={`flex items-center gap-1.5 px-3 py-2 rounded-sm border font-body text-[11px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] whitespace-nowrap ${
|
||||||
showAdvanced || hasAdvancedActive
|
showAdvanced || hasAdvancedActive
|
||||||
? "bg-[#3b82f6]/15 text-[#3b82f6] border-[#3b82f6]/40"
|
? "bg-[#E67E22]/15 text-[#E67E22] border-[#E67E22]/40"
|
||||||
: "bg-[#111] text-[#666] border-[#2a2a2a] hover:text-[#aaa] hover:border-[#3a3a3a]"
|
: "bg-[#111] text-[#666] border-[#2a2a2a] hover:text-[#aaa] hover:border-[#3a3a3a]"
|
||||||
}`}>
|
}`}>
|
||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||||
@@ -411,7 +411,7 @@ export default function ArticleFeed() {
|
|||||||
</svg>
|
</svg>
|
||||||
More
|
More
|
||||||
{hasAdvancedActive && (
|
{hasAdvancedActive && (
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-[#3b82f6] inline-block ml-0.5" aria-label="Active filters" />
|
<span className="w-1.5 h-1.5 rounded-full bg-[#E67E22] inline-block ml-0.5" aria-label="Active filters" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -428,9 +428,9 @@ export default function ArticleFeed() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => { setActiveCategory(cat); setPage(1); }}
|
onClick={() => { setActiveCategory(cat); setPage(1); }}
|
||||||
aria-pressed={isActive}
|
aria-pressed={isActive}
|
||||||
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
isActive
|
isActive
|
||||||
? "bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/50"
|
? "bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/50"
|
||||||
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc] hover:border-[#3a3a3a]"
|
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc] hover:border-[#3a3a3a]"
|
||||||
}`}>
|
}`}>
|
||||||
{cat}
|
{cat}
|
||||||
@@ -458,7 +458,7 @@ export default function ArticleFeed() {
|
|||||||
id={`${feedId}-author`}
|
id={`${feedId}-author`}
|
||||||
value={authorFilter}
|
value={authorFilter}
|
||||||
onChange={(e) => { setAuthorFilter(e.target.value); setPage(1); }}
|
onChange={(e) => { setAuthorFilter(e.target.value); setPage(1); }}
|
||||||
className="w-full bg-[#111] border border-[#2a2a2a] text-[#ccc] font-body text-sm rounded-sm px-3 py-1.5 pr-8 appearance-none focus:outline-none focus:border-[#3b82f6] focus:ring-1 focus:ring-[#3b82f6]/30 transition-colors [color-scheme:dark]"
|
className="w-full bg-[#111] border border-[#2a2a2a] text-[#ccc] font-body text-sm rounded-sm px-3 py-1.5 pr-8 appearance-none focus:outline-none focus:border-[#E67E22] focus:ring-1 focus:ring-[#E67E22]/30 transition-colors [color-scheme:dark]"
|
||||||
aria-label="Filter by author">
|
aria-label="Filter by author">
|
||||||
<option value="All">All Authors</option>
|
<option value="All">All Authors</option>
|
||||||
{uniqueAuthors.map((author) => (
|
{uniqueAuthors.map((author) => (
|
||||||
@@ -483,10 +483,10 @@ export default function ArticleFeed() {
|
|||||||
key={sf}
|
key={sf}
|
||||||
onClick={() => { setSourceFilter(sf); setPage(1); }}
|
onClick={() => { setSourceFilter(sf); setPage(1); }}
|
||||||
aria-pressed={isActive}
|
aria-pressed={isActive}
|
||||||
className={`px-3 py-1 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`px-3 py-1 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
isActive
|
isActive
|
||||||
? sf === "Imported" ?"bg-amber-500/20 text-amber-400 border border-amber-500/40"
|
? sf === "Imported" ?"bg-amber-500/20 text-amber-400 border border-amber-500/40"
|
||||||
: sf === "Live" ?"bg-emerald-500/20 text-emerald-400 border border-emerald-500/40" :"bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/40" :"text-[#666] hover:text-[#aaa] border border-transparent"
|
: sf === "Live" ?"bg-emerald-500/20 text-emerald-400 border border-emerald-500/40" :"bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/40" :"text-[#666] hover:text-[#aaa] border border-transparent"
|
||||||
}`}>
|
}`}>
|
||||||
{sf}
|
{sf}
|
||||||
{sf === "Imported" && importedCount > 0 && (
|
{sf === "Imported" && importedCount > 0 && (
|
||||||
@@ -507,7 +507,7 @@ export default function ArticleFeed() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => { setAuthorFilter("All"); setDateFrom(""); setDateTo(""); setPage(1); }}
|
onClick={() => { setAuthorFilter("All"); setDateFrom(""); setDateTo(""); setPage(1); }}
|
||||||
className="font-body text-[11px] text-[#3b82f6] hover:underline focus:outline-none focus-visible:underline">
|
className="font-body text-[11px] text-[#E67E22] hover:underline focus:outline-none focus-visible:underline">
|
||||||
Clear advanced filters
|
Clear advanced filters
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -537,7 +537,7 @@ export default function ArticleFeed() {
|
|||||||
{sortedFiltered.length === 0 && (
|
{sortedFiltered.length === 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={clearAllFilters}
|
onClick={clearAllFilters}
|
||||||
className="ml-1 text-[#3b82f6] hover:underline focus:outline-none focus-visible:underline">
|
className="ml-1 text-[#E67E22] hover:underline focus:outline-none focus-visible:underline">
|
||||||
Clear filters
|
Clear filters
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@@ -557,7 +557,7 @@ export default function ArticleFeed() {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={clearAllFilters}
|
onClick={clearAllFilters}
|
||||||
className="mt-4 font-body text-sm text-[#3b82f6] hover:underline focus:outline-none focus-visible:underline">
|
className="mt-4 font-body text-sm text-[#E67E22] hover:underline focus:outline-none focus-visible:underline">
|
||||||
Clear filters
|
Clear filters
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -585,7 +585,7 @@ export default function ArticleFeed() {
|
|||||||
const promotedClass = promoted === 'premium'
|
const promotedClass = promoted === 'premium'
|
||||||
? 'border-l-4 border-amber-400 bg-gradient-to-r from-amber-500/10 to-transparent pl-3 -ml-2 mr-0'
|
? 'border-l-4 border-amber-400 bg-gradient-to-r from-amber-500/10 to-transparent pl-3 -ml-2 mr-0'
|
||||||
: promoted === 'client'
|
: promoted === 'client'
|
||||||
? 'border-l-4 border-[#3b82f6] bg-gradient-to-r from-[#3b82f6]/8 to-transparent pl-3 -ml-2 mr-0'
|
? 'border-l-4 border-[#E67E22] bg-gradient-to-r from-[#E67E22]/8 to-transparent pl-3 -ml-2 mr-0'
|
||||||
: '';
|
: '';
|
||||||
return (
|
return (
|
||||||
<Fragment key={`feed-${i}`}>
|
<Fragment key={`feed-${i}`}>
|
||||||
@@ -593,7 +593,7 @@ export default function ArticleFeed() {
|
|||||||
<Link
|
<Link
|
||||||
href={articleHref}
|
href={articleHref}
|
||||||
{...linkProps}
|
{...linkProps}
|
||||||
className="flex-shrink-0 img-zoom overflow-hidden w-[100px] h-[70px] sm:w-[140px] sm:h-[95px] md:w-[190px] md:h-[128px] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]"
|
className="flex-shrink-0 img-zoom overflow-hidden w-[100px] h-[70px] sm:w-[140px] sm:h-[95px] md:w-[190px] md:h-[128px] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
@@ -614,21 +614,21 @@ export default function ArticleFeed() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{promoted === 'client' && (
|
{promoted === 'client' && (
|
||||||
<span className="font-body text-[9px] font-bold text-[#7aa7d4] bg-[#3b82f6]/15 border border-[#3b82f6]/40 px-1.5 py-0.5 rounded uppercase tracking-widest" title={article?.promoted_advertiser ? `Advertiser coverage: ${article.promoted_advertiser}` : 'Advertiser coverage'}>
|
<span className="font-body text-[9px] font-bold text-[#7aa7d4] bg-[#E67E22]/15 border border-[#E67E22]/40 px-1.5 py-0.5 rounded uppercase tracking-widest" title={article?.promoted_advertiser ? `Advertiser coverage: ${article.promoted_advertiser}` : 'Advertiser coverage'}>
|
||||||
◆ Advertiser
|
◆ Advertiser
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{article?.category && article.category.toLowerCase() !== "legacy" && (
|
{article?.category && article.category.toLowerCase() !== "legacy" && (
|
||||||
<span className="font-body text-[10px] font-bold text-[#3b82f6] uppercase tracking-wide">
|
<span className="font-body text-[10px] font-bold text-[#E67E22] uppercase tracking-wide">
|
||||||
{article.category}
|
{article.category}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-heading text-[#e0e0e0] font-bold text-[0.875rem] md:text-[1rem] leading-snug mb-1 md:mb-1.5 group-hover:text-[#3b82f6] transition-colors duration-200 line-clamp-2">
|
<h3 className="font-heading text-[#e0e0e0] font-bold text-[0.875rem] md:text-[1rem] leading-snug mb-1 md:mb-1.5 group-hover:text-[#E67E22] transition-colors duration-200 line-clamp-2">
|
||||||
<Link
|
<Link
|
||||||
href={articleHref}
|
href={articleHref}
|
||||||
{...linkProps}
|
{...linkProps}
|
||||||
className="focus:outline-none focus-visible:text-[#3b82f6] focus-visible:underline">
|
className="focus:outline-none focus-visible:text-[#E67E22] focus-visible:underline">
|
||||||
{article?.title}
|
{article?.title}
|
||||||
</Link>
|
</Link>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -664,7 +664,7 @@ export default function ArticleFeed() {
|
|||||||
|
|
||||||
{/* Inline social share row */}
|
{/* Inline social share row */}
|
||||||
{(() => {
|
{(() => {
|
||||||
const shareUrl = `https://broadcastbeat.com${articleHref.startsWith('/') ? articleHref : `/articles/${article?.slug}`}`;
|
const shareUrl = `https://avbeat.com${articleHref.startsWith('/') ? articleHref : `/articles/${article?.slug}`}`;
|
||||||
const shareTitle = article?.title || '';
|
const shareTitle = article?.title || '';
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-1.5 mt-2 pt-2 border-t border-[#1a1a1a]">
|
<div className="flex items-center gap-1.5 mt-2 pt-2 border-t border-[#1a1a1a]">
|
||||||
@@ -675,7 +675,7 @@ export default function ArticleFeed() {
|
|||||||
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#0a66c2] hover:bg-[#1a1a1a] transition-colors">
|
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#0a66c2] hover:bg-[#1a1a1a] transition-colors">
|
||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 0h-14C2.24 0 0 2.24 0 5v14c0 2.76 2.24 5 5 5h14c2.76 0 5-2.24 5-5V5c0-2.76-2.24-5-5-5zM8 19H5V8h3v11zM6.5 6.73a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5zM20 19h-3v-5.6c0-3.37-4-3.12-4 0V19h-3V8h3v1.77c1.4-2.59 7-2.78 7 2.48V19z"/></svg>
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 0h-14C2.24 0 0 2.24 0 5v14c0 2.76 2.24 5 5 5h14c2.76 0 5-2.24 5-5V5c0-2.76-2.24-5-5-5zM8 19H5V8h3v11zM6.5 6.73a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5zM20 19h-3v-5.6c0-3.37-4-3.12-4 0V19h-3V8h3v1.77c1.4-2.59 7-2.78 7 2.48V19z"/></svg>
|
||||||
</a>
|
</a>
|
||||||
<a href={`https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(shareTitle)}&via=BroadcastBeat`}
|
<a href={`https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(shareTitle)}&via=AVBeat`}
|
||||||
target="_blank" rel="noopener noreferrer" aria-label="Share on X" title="X (Twitter)"
|
target="_blank" rel="noopener noreferrer" aria-label="Share on X" title="X (Twitter)"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-white hover:bg-[#1a1a1a] transition-colors">
|
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-white hover:bg-[#1a1a1a] transition-colors">
|
||||||
@@ -702,7 +702,7 @@ export default function ArticleFeed() {
|
|||||||
<a href={`mailto:?subject=${encodeURIComponent(shareTitle)}&body=${encodeURIComponent(shareUrl)}`}
|
<a href={`mailto:?subject=${encodeURIComponent(shareTitle)}&body=${encodeURIComponent(shareUrl)}`}
|
||||||
aria-label="Share via email" title="Email"
|
aria-label="Share via email" title="Email"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#3b82f6] hover:bg-[#1a1a1a] transition-colors">
|
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#E67E22] hover:bg-[#1a1a1a] transition-colors">
|
||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<rect x="2" y="4" width="20" height="16" rx="2" /><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
|
<rect x="2" y="4" width="20" height="16" rx="2" /><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -735,9 +735,9 @@ export default function ArticleFeed() {
|
|||||||
{isLoadingMore && (
|
{isLoadingMore && (
|
||||||
<div className="flex items-center justify-center gap-2 py-6" aria-live="polite" aria-label="Loading more articles">
|
<div className="flex items-center justify-center gap-2 py-6" aria-live="polite" aria-label="Loading more articles">
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-[#3b82f6] animate-bounce" style={{ animationDelay: "0ms" }} />
|
<span className="w-1.5 h-1.5 rounded-full bg-[#E67E22] animate-bounce" style={{ animationDelay: "0ms" }} />
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-[#3b82f6] animate-bounce" style={{ animationDelay: "150ms" }} />
|
<span className="w-1.5 h-1.5 rounded-full bg-[#E67E22] animate-bounce" style={{ animationDelay: "150ms" }} />
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-[#3b82f6] animate-bounce" style={{ animationDelay: "300ms" }} />
|
<span className="w-1.5 h-1.5 rounded-full bg-[#E67E22] animate-bounce" style={{ animationDelay: "300ms" }} />
|
||||||
</div>
|
</div>
|
||||||
<span className="font-body text-xs text-[#555]">Loading more articles…</span>
|
<span className="font-body text-xs text-[#555]">Loading more articles…</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -762,7 +762,7 @@ export default function ArticleFeed() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-1" role="navigation" aria-label="Pagination">
|
<div className="flex items-center gap-1" role="navigation" aria-label="Pagination">
|
||||||
<button
|
<button
|
||||||
className="page-btn inline-flex items-center gap-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]"
|
className="page-btn inline-flex items-center gap-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]"
|
||||||
disabled={page === 1}
|
disabled={page === 1}
|
||||||
onClick={() => { setPage(Math.max(1, page - 1)); window.scrollTo({ top: 0, behavior: "smooth" }); }}
|
onClick={() => { setPage(Math.max(1, page - 1)); window.scrollTo({ top: 0, behavior: "smooth" }); }}
|
||||||
aria-label="Previous page">
|
aria-label="Previous page">
|
||||||
@@ -786,7 +786,7 @@ export default function ArticleFeed() {
|
|||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
key={p}
|
key={p}
|
||||||
className={`page-btn focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${page === p ? "active" : ""}`}
|
className={`page-btn focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${page === p ? "active" : ""}`}
|
||||||
onClick={() => { setPage(p as number); window.scrollTo({ top: 0, behavior: "smooth" }); }}
|
onClick={() => { setPage(p as number); window.scrollTo({ top: 0, behavior: "smooth" }); }}
|
||||||
aria-label={`Page ${p}`}
|
aria-label={`Page ${p}`}
|
||||||
aria-current={page === p ? "page" : undefined}>
|
aria-current={page === p ? "page" : undefined}>
|
||||||
@@ -796,7 +796,7 @@ export default function ArticleFeed() {
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
<button
|
<button
|
||||||
className="page-btn inline-flex items-center gap-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]"
|
className="page-btn inline-flex items-center gap-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]"
|
||||||
disabled={page === totalPages}
|
disabled={page === totalPages}
|
||||||
onClick={() => { setPage(Math.min(totalPages, page + 1)); window.scrollTo({ top: 0, behavior: "smooth" }); }}
|
onClick={() => { setPage(Math.min(totalPages, page + 1)); window.scrollTo({ top: 0, behavior: "smooth" }); }}
|
||||||
aria-label="Next page">
|
aria-label="Next page">
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import SponsorLogoStrip from "@/components/SponsorLogoStrip";
|
|||||||
import CompanyMentionsHover from "@/components/CompanyMentionsHover";
|
import CompanyMentionsHover from "@/components/CompanyMentionsHover";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Broadcast Beat — Broadcast Engineering News & Insights',
|
title: 'AV Beat — Where AV Meets IT',
|
||||||
description: 'The digital platform for broadcast engineering professionals. Breaking news, deep-dive features, and industry spotlights from NAB to IBC and beyond.',
|
description: 'AV Beat covers the convergence of pro AV and IT — broadcast, live production, streaming, and post-production technology. Breaking news, deep-dive features, and industry spotlights from NAB to InfoComm and beyond.',
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: '/',
|
canonical: '/',
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: 'Broadcast Beat — Broadcast News',
|
title: 'AV Beat — Where AV Meets IT',
|
||||||
description: 'Breaking news and insights for broadcast engineering professionals.',
|
description: 'Breaking news and insights at the intersection of pro AV and IT.',
|
||||||
url: '/',
|
url: '/',
|
||||||
type: 'website',
|
type: 'website',
|
||||||
images: [
|
images: [
|
||||||
@@ -28,14 +28,14 @@ export const metadata: Metadata = {
|
|||||||
url: '/assets/images/og-image.png',
|
url: '/assets/images/og-image.png',
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
alt: 'Broadcast Beat — Broadcast Engineering News & Insights',
|
alt: 'AV Beat — Where AV Meets IT',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: 'summary_large_image',
|
card: 'summary_large_image',
|
||||||
title: 'Broadcast Beat — Broadcast News',
|
title: 'AV Beat — Where AV Meets IT',
|
||||||
description: 'Breaking news and insights for broadcast engineering professionals.',
|
description: 'Breaking news and insights at the intersection of pro AV and IT.',
|
||||||
images: ['/assets/images/og-image.png'],
|
images: ['/assets/images/og-image.png'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -138,7 +138,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
{/* H1 Hero Heading */}
|
{/* H1 Hero Heading */}
|
||||||
<div className="sr-only">
|
<div className="sr-only">
|
||||||
<h1>Broadcast Beat — Broadcast Engineering News & Insights</h1>
|
<h1>AV Beat — Where AV Meets IT</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Featured: cinematic hero + 4-up rail below (FeaturedBentoFromDb) */}
|
{/* Featured: cinematic hero + 4-up rail below (FeaturedBentoFromDb) */}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ export const viewport: Viewport = {
|
|||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'),
|
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'),
|
||||||
title: 'Broadcast Beat — Broadcast Engineering News & Insights',
|
title: 'AV Beat — Where AV Meets IT',
|
||||||
description: 'The digital platform for broadcast engineering professionals. Breaking news, deep-dive features, and industry spotlights from NAB to IBC and beyond.',
|
description: 'AV Beat covers the convergence of pro AV and IT — broadcast, live production, streaming, and post-production technology. Breaking news, deep-dive features, and industry spotlights from NAB to InfoComm and beyond.',
|
||||||
icons: {
|
icons: {
|
||||||
icon: [
|
icon: [
|
||||||
{ url: '/favicon.ico', type: 'image/x-icon', sizes: 'any' },
|
{ url: '/favicon.ico', type: 'image/x-icon', sizes: 'any' },
|
||||||
@@ -58,25 +58,25 @@ export const metadata: Metadata = {
|
|||||||
type: 'website',
|
type: 'website',
|
||||||
locale: 'en_US',
|
locale: 'en_US',
|
||||||
url: '/',
|
url: '/',
|
||||||
siteName: 'Broadcast Beat',
|
siteName: 'AV Beat',
|
||||||
title: 'Broadcast Beat — Broadcast News',
|
title: 'AV Beat — Where AV Meets IT',
|
||||||
description: 'Breaking news and insights for broadcast engineering professionals.',
|
description: 'Breaking news and insights at the intersection of pro AV and IT.',
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: '/assets/images/og-image.png',
|
url: '/assets/images/og-image.png',
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
alt: 'Broadcast Beat — Broadcast Engineering News & Insights',
|
alt: 'AV Beat — Where AV Meets IT',
|
||||||
type: 'image/png'
|
type: 'image/png'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: 'summary_large_image',
|
card: 'summary_large_image',
|
||||||
site: '@Broadcast Beat',
|
site: '@AVBeat',
|
||||||
creator: '@Broadcast Beat',
|
creator: '@AVBeat',
|
||||||
title: 'Broadcast Beat — Broadcast News',
|
title: 'AV Beat — Where AV Meets IT',
|
||||||
description: 'Breaking news and insights for broadcast engineering professionals.',
|
description: 'Breaking news and insights at the intersection of pro AV and IT.',
|
||||||
images: ['/assets/images/og-image.png']
|
images: ['/assets/images/og-image.png']
|
||||||
},
|
},
|
||||||
robots: {
|
robots: {
|
||||||
@@ -122,10 +122,10 @@ export default function RootLayout({
|
|||||||
__html: JSON.stringify({
|
__html: JSON.stringify({
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'Organization',
|
'@type': 'Organization',
|
||||||
name: 'Broadcast Beat',
|
name: 'AV Beat',
|
||||||
url: 'https://broadcastbeat.com',
|
url: 'https://avbeat.com',
|
||||||
logo: "https://broadcastbeat.com/assets/images/logo.png",
|
logo: "https://avbeat.com/assets/images/logo.png",
|
||||||
description: 'The digital platform for broadcast engineering professionals. Breaking news, deep-dive features, and industry spotlights from NAB to IBC and beyond.',
|
description: 'AV Beat covers the convergence of pro AV and IT — broadcast, live production, streaming, and post-production technology.',
|
||||||
sameAs: [
|
sameAs: [
|
||||||
'https://linkedin.com',
|
'https://linkedin.com',
|
||||||
'https://twitter.com',
|
'https://twitter.com',
|
||||||
@@ -135,7 +135,7 @@ export default function RootLayout({
|
|||||||
contactPoint: {
|
contactPoint: {
|
||||||
'@type': 'ContactPoint',
|
'@type': 'ContactPoint',
|
||||||
contactType: 'Editorial',
|
contactType: 'Editorial',
|
||||||
url: 'https://broadcastbeat.com'
|
url: 'https://avbeat.com'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}} />
|
}} />
|
||||||
@@ -144,7 +144,7 @@ export default function RootLayout({
|
|||||||
{/* Skip to main content — WCAG 2.4.1 */}
|
{/* Skip to main content — WCAG 2.4.1 */}
|
||||||
<a
|
<a
|
||||||
href="#main-content"
|
href="#main-content"
|
||||||
className="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[10000] focus:px-4 focus:py-2 focus:bg-[#3b82f6] focus:text-white focus:rounded focus:font-bold focus:text-sm focus:outline-none"
|
className="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[10000] focus:px-4 focus:py-2 focus:bg-[#E67E22] focus:text-white focus:rounded focus:font-bold focus:text-sm focus:outline-none"
|
||||||
>
|
>
|
||||||
Skip to main content
|
Skip to main content
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Header from '@/components/Header';
|
|||||||
import Footer from '@/components/Footer';
|
import Footer from '@/components/Footer';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Billing | Broadcast Beat Marketplace',
|
title: 'Billing | AV Beat Marketplace',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function BillingPage() {
|
export default function BillingPage() {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default function GigsPage() {
|
|||||||
<div className="text-center py-20 text-gray-500">
|
<div className="text-center py-20 text-gray-500">
|
||||||
<div className="text-4xl mb-4">🎬</div>
|
<div className="text-4xl mb-4">🎬</div>
|
||||||
<p className="text-lg font-semibold mb-2">No gigs posted yet</p>
|
<p className="text-lg font-semibold mb-2">No gigs posted yet</p>
|
||||||
<p className="text-sm">Be the first to post a crew call on Broadcast Beat Marketplace.</p>
|
<p className="text-sm">Be the first to post a crew call on AV Beat Marketplace.</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default function JobsPage() {
|
|||||||
<div className="text-center py-20 text-gray-500">
|
<div className="text-center py-20 text-gray-500">
|
||||||
<div className="text-4xl mb-4">📋</div>
|
<div className="text-4xl mb-4">📋</div>
|
||||||
<p className="text-lg font-semibold mb-2">No jobs posted yet</p>
|
<p className="text-lg font-semibold mb-2">No jobs posted yet</p>
|
||||||
<p className="text-sm">Be the first to post a job on Broadcast Beat Marketplace.</p>
|
<p className="text-sm">Be the first to post a job on AV Beat Marketplace.</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Header from '@/components/Header';
|
|||||||
import Footer from '@/components/Footer';
|
import Footer from '@/components/Footer';
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: 'Production Crew & Vendor Marketplace | Broadcast Beat',
|
title: 'Production Crew & Vendor Marketplace | AV Beat',
|
||||||
description:
|
description:
|
||||||
'Find broadcast and production crew, vendors, jobs, and gig opportunities. The professional marketplace built for the broadcast industry.',
|
'Find broadcast and production crew, vendors, jobs, and gig opportunities. The professional marketplace built for the broadcast industry.',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ import CompanyMentionsHover from "@/components/CompanyMentionsHover";
|
|||||||
export const revalidate = 600;
|
export const revalidate = 600;
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "2026 NAB Show — Live Coverage Hub | Broadcast Beat",
|
title: "2026 NAB Show — Live Coverage Hub | AV Beat",
|
||||||
description:
|
description:
|
||||||
"Broadcast Beat's live coverage of 2026 NAB Show: 1,100+ exhibitors, the latest product launches, booth-by-booth news, and editorial pick stories from the show floor.",
|
"AV Beat's live coverage of 2026 NAB Show: 1,100+ exhibitors, the latest product launches, booth-by-booth news, and editorial pick stories from the show floor.",
|
||||||
alternates: { canonical: "/nab-2026" },
|
alternates: { canonical: "/nab-2026" },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "2026 NAB Show — Live Coverage Hub",
|
title: "2026 NAB Show — Live Coverage Hub",
|
||||||
description:
|
description:
|
||||||
"Live coverage, exhibitor list, and breaking product news from 2026 NAB Show.",
|
"Live coverage, exhibitor list, and breaking product news from 2026 NAB Show.",
|
||||||
type: "website",
|
type: "website",
|
||||||
url: "https://broadcastbeat.com/nab-2026",
|
url: "https://avbeat.com/nab-2026",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ export default async function NabHub() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="text-[#aaa] text-base max-w-3xl">
|
<p className="text-[#aaa] text-base max-w-3xl">
|
||||||
{exhibitors.length.toLocaleString()} exhibitors confirmed.
|
{exhibitors.length.toLocaleString()} exhibitors confirmed.
|
||||||
{sponsorRows.length > 0 && <> {sponsorRows.length} are Broadcast Beat content partners.</>}
|
{sponsorRows.length > 0 && <> {sponsorRows.length} are AV Beat content partners.</>}
|
||||||
{" "}Browse the directory, read the latest product news, and find every booth.
|
{" "}Browse the directory, read the latest product news, and find every booth.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -153,7 +153,7 @@ export default async function NabHub() {
|
|||||||
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
|
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
|
||||||
Latest from the show floor
|
Latest from the show floor
|
||||||
</h2>
|
</h2>
|
||||||
<Link href="/news?search=NAB+2026" className="text-xs text-[#3b82f6] hover:underline">
|
<Link href="/news?search=NAB+2026" className="text-xs text-[#E67E22] hover:underline">
|
||||||
See all NAB coverage →
|
See all NAB coverage →
|
||||||
</Link>
|
</Link>
|
||||||
</header>
|
</header>
|
||||||
@@ -162,7 +162,7 @@ export default async function NabHub() {
|
|||||||
<Link
|
<Link
|
||||||
key={a.wp_slug}
|
key={a.wp_slug}
|
||||||
href={`/news/${a.wp_slug}`}
|
href={`/news/${a.wp_slug}`}
|
||||||
className="group bg-[#0d0d0d] border border-[#252525] rounded-lg overflow-hidden hover:border-[#3b82f6] hover:shadow-sm transition-all"
|
className="group bg-[#0d0d0d] border border-[#252525] rounded-lg overflow-hidden hover:border-[#E67E22] hover:shadow-sm transition-all"
|
||||||
>
|
>
|
||||||
{a.featured_image && (
|
{a.featured_image && (
|
||||||
/* eslint-disable-next-line @next/next/no-img-element */
|
/* eslint-disable-next-line @next/next/no-img-element */
|
||||||
@@ -174,7 +174,7 @@ export default async function NabHub() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
<h3 className="font-semibold text-[#e0e0e0] text-sm leading-snug group-hover:text-[#3b82f6] line-clamp-3 mb-1">
|
<h3 className="font-semibold text-[#e0e0e0] text-sm leading-snug group-hover:text-[#E67E22] line-clamp-3 mb-1">
|
||||||
{a.title}
|
{a.title}
|
||||||
</h3>
|
</h3>
|
||||||
{a.wp_published_at && (
|
{a.wp_published_at && (
|
||||||
@@ -195,7 +195,7 @@ export default async function NabHub() {
|
|||||||
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
|
<h2 className="text-xl font-display font-bold text-[#e0e0e0]">
|
||||||
All 2026 NAB Show exhibitors
|
All 2026 NAB Show exhibitors
|
||||||
</h2>
|
</h2>
|
||||||
<Link href="/manufacturers" className="text-xs text-[#3b82f6] hover:underline">
|
<Link href="/manufacturers" className="text-xs text-[#E67E22] hover:underline">
|
||||||
Full directory →
|
Full directory →
|
||||||
</Link>
|
</Link>
|
||||||
</header>
|
</header>
|
||||||
@@ -205,7 +205,7 @@ export default async function NabHub() {
|
|||||||
key={e.slug}
|
key={e.slug}
|
||||||
href={`/manufacturers/${e.slug}`}
|
href={`/manufacturers/${e.slug}`}
|
||||||
data-company-slug={e.slug}
|
data-company-slug={e.slug}
|
||||||
className="bg-[#0d0d0d] border border-[#252525] rounded-md p-2.5 hover:border-[#3b82f6] transition-all flex items-center gap-2.5"
|
className="bg-[#0d0d0d] border border-[#252525] rounded-md p-2.5 hover:border-[#E67E22] transition-all flex items-center gap-2.5"
|
||||||
>
|
>
|
||||||
{e.logo_url ? (
|
{e.logo_url ? (
|
||||||
/* eslint-disable-next-line @next/next/no-img-element */
|
/* eslint-disable-next-line @next/next/no-img-element */
|
||||||
@@ -222,7 +222,7 @@ export default async function NabHub() {
|
|||||||
{otherRows.length > 200 && (
|
{otherRows.length > 200 && (
|
||||||
<p className="text-center text-xs text-[#888] mt-4">
|
<p className="text-center text-xs text-[#888] mt-4">
|
||||||
Showing 200 of {otherRows.length.toLocaleString()} exhibitors.{" "}
|
Showing 200 of {otherRows.length.toLocaleString()} exhibitors.{" "}
|
||||||
<Link href="/manufacturers" className="text-[#3b82f6] hover:underline">Browse the full directory →</Link>
|
<Link href="/manufacturers" className="text-[#E67E22] hover:underline">Browse the full directory →</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ export const revalidate = 600; // 10 min — Google rechecks frequently
|
|||||||
// Google News sitemap. Only original-editorial articles (ai_rewritten_at IS
|
// Google News sitemap. Only original-editorial articles (ai_rewritten_at IS
|
||||||
// NOT NULL or rewritten-articles surface) from the last 48 hours so we
|
// NOT NULL or rewritten-articles surface) from the last 48 hours so we
|
||||||
// stay inside the GN window. Standard sitemap.xml covers older pages.
|
// stay inside the GN window. Standard sitemap.xml covers older pages.
|
||||||
const PUBLICATION_NAME = 'Broadcast Beat';
|
const PUBLICATION_NAME = 'AV Beat';
|
||||||
const PUBLICATION_LANG = 'en';
|
const PUBLICATION_LANG = 'en';
|
||||||
const HOST = 'https://broadcastbeat.com';
|
const HOST = 'https://avbeat.com';
|
||||||
|
|
||||||
function esc(s: string): string {
|
function esc(s: string): string {
|
||||||
return (s || '').replace(/[&<>"']/g, (c) =>
|
return (s || '').replace(/[&<>"']/g, (c) =>
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ export default function NewsPage({
|
|||||||
placeholder="Search articles, topics, authors…"
|
placeholder="Search articles, topics, authors…"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
className="w-full bg-[#1a1a1a] border border-[#2a2a2a] text-[#e0e0e0] placeholder-[#555] text-sm font-body pl-9 pr-4 py-2.5 rounded-sm focus:outline-none focus:border-[#3b82f6] transition-colors"
|
className="w-full bg-[#1a1a1a] border border-[#2a2a2a] text-[#e0e0e0] placeholder-[#555] text-sm font-body pl-9 pr-4 py-2.5 rounded-sm focus:outline-none focus:border-[#E67E22] transition-colors"
|
||||||
/>
|
/>
|
||||||
{search && (
|
{search && (
|
||||||
<button
|
<button
|
||||||
@@ -196,7 +196,7 @@ export default function NewsPage({
|
|||||||
type="date"
|
type="date"
|
||||||
value={dateFrom}
|
value={dateFrom}
|
||||||
onChange={(e) => setDateFrom(e.target.value)}
|
onChange={(e) => setDateFrom(e.target.value)}
|
||||||
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2.5 rounded-sm focus:outline-none focus:border-[#3b82f6] transition-colors [color-scheme:dark]"
|
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2.5 rounded-sm focus:outline-none focus:border-[#E67E22] transition-colors [color-scheme:dark]"
|
||||||
title="From date"
|
title="From date"
|
||||||
/>
|
/>
|
||||||
<span className="text-[#444] text-xs">–</span>
|
<span className="text-[#444] text-xs">–</span>
|
||||||
@@ -204,7 +204,7 @@ export default function NewsPage({
|
|||||||
type="date"
|
type="date"
|
||||||
value={dateTo}
|
value={dateTo}
|
||||||
onChange={(e) => setDateTo(e.target.value)}
|
onChange={(e) => setDateTo(e.target.value)}
|
||||||
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2.5 rounded-sm focus:outline-none focus:border-[#3b82f6] transition-colors [color-scheme:dark]"
|
className="bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body px-3 py-2.5 rounded-sm focus:outline-none focus:border-[#E67E22] transition-colors [color-scheme:dark]"
|
||||||
title="To date"
|
title="To date"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,7 +214,7 @@ export default function NewsPage({
|
|||||||
<select
|
<select
|
||||||
value={sortBy}
|
value={sortBy}
|
||||||
onChange={(e) => setSortBy(e.target.value)}
|
onChange={(e) => setSortBy(e.target.value)}
|
||||||
className="appearance-none bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body pl-3 pr-8 py-2.5 rounded-sm focus:outline-none focus:border-[#3b82f6] transition-colors cursor-pointer"
|
className="appearance-none bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] text-xs font-body pl-3 pr-8 py-2.5 rounded-sm focus:outline-none focus:border-[#E67E22] transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
{SORT_OPTIONS.map((opt) => (
|
{SORT_OPTIONS.map((opt) => (
|
||||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||||
@@ -236,8 +236,8 @@ export default function NewsPage({
|
|||||||
onClick={() => setActiveCategory(cat)}
|
onClick={() => setActiveCategory(cat)}
|
||||||
className={`px-2.5 py-1 text-[11px] font-body rounded-sm border transition-colors ${
|
className={`px-2.5 py-1 text-[11px] font-body rounded-sm border transition-colors ${
|
||||||
activeCategory === cat
|
activeCategory === cat
|
||||||
? "bg-[#3b82f6] border-[#3b82f6] text-white"
|
? "bg-[#E67E22] border-[#E67E22] text-white"
|
||||||
: "bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#3b82f6] hover:text-[#3b82f6]"
|
: "bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#E67E22] hover:text-[#E67E22]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{cat}
|
{cat}
|
||||||
@@ -275,7 +275,7 @@ export default function NewsPage({
|
|||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
<p className="text-[#555] font-body text-sm">No articles match your filters.</p>
|
<p className="text-[#555] font-body text-sm">No articles match your filters.</p>
|
||||||
<button onClick={clearFilters} className="mt-3 text-[#3b82f6] text-xs font-body hover:underline">Clear filters</button>
|
<button onClick={clearFilters} className="mt-3 text-[#E67E22] text-xs font-body hover:underline">Clear filters</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-0">
|
<div className="space-y-0">
|
||||||
@@ -289,7 +289,7 @@ export default function NewsPage({
|
|||||||
<div className="flex-shrink-0 w-[160px] h-[105px] relative overflow-hidden rounded-sm">
|
<div className="flex-shrink-0 w-[160px] h-[105px] relative overflow-hidden rounded-sm">
|
||||||
<AppImage
|
<AppImage
|
||||||
src={article.image || "/assets/images/article-placeholder.jpg"}
|
src={article.image || "/assets/images/article-placeholder.jpg"}
|
||||||
alt={article.alt || article.title || "Broadcast Beat article"}
|
alt={article.alt || article.title || "AV Beat article"}
|
||||||
fill
|
fill
|
||||||
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
||||||
sizes="160px"
|
sizes="160px"
|
||||||
@@ -297,14 +297,14 @@ export default function NewsPage({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1.5">
|
<div className="flex items-center gap-2 mb-1.5">
|
||||||
<span className="text-[#3b82f6] font-body text-[10px] font-bold uppercase tracking-wider">{article.category || "NEWS"}</span>
|
<span className="text-[#E67E22] font-body text-[10px] font-bold uppercase tracking-wider">{article.category || "NEWS"}</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="font-heading text-[#e0e0e0] text-base font-bold leading-snug mb-2 group-hover:text-[#3b82f6] transition-colors line-clamp-2">
|
<h2 className="font-heading text-[#e0e0e0] text-base font-bold leading-snug mb-2 group-hover:text-[#E67E22] transition-colors line-clamp-2">
|
||||||
{article.title || "Untitled"}
|
{article.title || "Untitled"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-[#777] font-body text-sm leading-relaxed line-clamp-2">{article.excerpt || ""}</p>
|
<p className="text-[#777] font-body text-sm leading-relaxed line-clamp-2">{article.excerpt || ""}</p>
|
||||||
<div className="flex items-center gap-2 mt-2">
|
<div className="flex items-center gap-2 mt-2">
|
||||||
<span className="text-[#555] font-body text-xs">By {article.author || "Broadcast Beat"}</span>
|
<span className="text-[#555] font-body text-xs">By {article.author || "AV Beat"}</span>
|
||||||
<span className="text-[#444] text-[10px]">·</span>
|
<span className="text-[#444] text-[10px]">·</span>
|
||||||
<span className="text-[#555] font-body text-xs">{article.readTime || ""}</span>
|
<span className="text-[#555] font-body text-xs">{article.readTime || ""}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -326,7 +326,7 @@ export default function NewsPage({
|
|||||||
{!hideAiSuggestions && <AISuggestedArticles variant="full" />}
|
{!hideAiSuggestions && <AISuggestedArticles variant="full" />}
|
||||||
|
|
||||||
<div className="bg-[#111] border border-[#222] p-4">
|
<div className="bg-[#111] border border-[#222] p-4">
|
||||||
<h3 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest mb-3 pb-2 border-b border-[#222]">
|
<h3 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest mb-3 pb-2 border-b border-[#222]">
|
||||||
Popular Tags
|
Popular Tags
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
@@ -336,8 +336,8 @@ export default function NewsPage({
|
|||||||
onClick={() => setActiveCategory(tag)}
|
onClick={() => setActiveCategory(tag)}
|
||||||
className={`px-2 py-1 border text-xs font-body rounded-sm transition-colors ${
|
className={`px-2 py-1 border text-xs font-body rounded-sm transition-colors ${
|
||||||
activeCategory === tag
|
activeCategory === tag
|
||||||
? "bg-[#3b82f6] border-[#3b82f6] text-white"
|
? "bg-[#E67E22] border-[#E67E22] text-white"
|
||||||
: "bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#3b82f6] hover:text-[#3b82f6]"
|
: "bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#E67E22] hover:text-[#E67E22]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
@@ -346,7 +346,7 @@ export default function NewsPage({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[#0d1520] border border-[#1e3a5f] p-4">
|
<div className="bg-[#0d1520] border border-[#1e3a5f] p-4">
|
||||||
<h3 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest mb-2">Newsletter</h3>
|
<h3 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest mb-2">Newsletter</h3>
|
||||||
<p className="text-[#777] text-xs font-body mb-3">Get broadcast news delivered to your inbox every week.</p>
|
<p className="text-[#777] text-xs font-body mb-3">Get broadcast news delivered to your inbox every week.</p>
|
||||||
<Link href="/home-page#newsletter" className="btn-subscribe text-xs py-2 px-4 inline-block">Subscribe Free</Link>
|
<Link href="/home-page#newsletter" className="btn-subscribe text-xs py-2 px-4 inline-block">Subscribe Free</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export default function NewsArticleDetailClient({
|
|||||||
setEmailSending(true);
|
setEmailSending(true);
|
||||||
setEmailStatus("idle");
|
setEmailStatus("idle");
|
||||||
try {
|
try {
|
||||||
const articleUrl = `${process.env.NEXT_PUBLIC_SITE_URL || "https://broadcastbeat.com"}/news/${article.slug}`;
|
const articleUrl = `${process.env.NEXT_PUBLIC_SITE_URL || "https://avbeat.com"}/news/${article.slug}`;
|
||||||
const res = await fetch("/api/news/share-email", {
|
const res = await fetch("/api/news/share-email", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -269,7 +269,7 @@ export default function NewsArticleDetailClient({
|
|||||||
const articleUrl =
|
const articleUrl =
|
||||||
typeof window !== "undefined"
|
typeof window !== "undefined"
|
||||||
? window.location.href
|
? window.location.href
|
||||||
: `https://broadcastbeat.com/news/${article.slug}`;
|
: `https://avbeat.com/news/${article.slug}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background">
|
<div className="min-h-screen bg-background">
|
||||||
@@ -278,11 +278,11 @@ export default function NewsArticleDetailClient({
|
|||||||
{/* Breadcrumb */}
|
{/* Breadcrumb */}
|
||||||
<div className="bg-[#0d0d0d] border-b border-[#1a1a1a]">
|
<div className="bg-[#0d0d0d] border-b border-[#1a1a1a]">
|
||||||
<div className="max-w-container mx-auto px-4 py-2.5 flex items-center gap-2 text-xs font-body text-[#555]">
|
<div className="max-w-container mx-auto px-4 py-2.5 flex items-center gap-2 text-xs font-body text-[#555]">
|
||||||
<Link href="/home-page" className="hover:text-[#3b82f6] transition-colors">
|
<Link href="/home-page" className="hover:text-[#E67E22] transition-colors">
|
||||||
Home
|
Home
|
||||||
</Link>
|
</Link>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<Link href="/news" className="hover:text-[#3b82f6] transition-colors">
|
<Link href="/news" className="hover:text-[#E67E22] transition-colors">
|
||||||
News
|
News
|
||||||
</Link>
|
</Link>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
@@ -300,7 +300,7 @@ export default function NewsArticleDetailClient({
|
|||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<Link
|
<Link
|
||||||
href="/news"
|
href="/news"
|
||||||
className="text-xs font-bold uppercase tracking-widest text-[#3b82f6] hover:underline">
|
className="text-xs font-bold uppercase tracking-widest text-[#E67E22] hover:underline">
|
||||||
{article.category}
|
{article.category}
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-xs text-[#777]">{article.readTime}</span>
|
<span className="text-xs text-[#777]">{article.readTime}</span>
|
||||||
@@ -329,7 +329,7 @@ export default function NewsArticleDetailClient({
|
|||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
href={`/authors/${article.authorSlug}`}
|
href={`/authors/${article.authorSlug}`}
|
||||||
className="font-heading font-bold text-sm hover:text-[#3b82f6] transition-colors text-[#e0e0e0]">
|
className="font-heading font-bold text-sm hover:text-[#E67E22] transition-colors text-[#e0e0e0]">
|
||||||
{article.author}
|
{article.author}
|
||||||
</Link>
|
</Link>
|
||||||
<p className="font-body text-xs text-[#777]">{article.authorTitle}</p>
|
<p className="font-body text-xs text-[#777]">{article.authorTitle}</p>
|
||||||
@@ -343,7 +343,7 @@ export default function NewsArticleDetailClient({
|
|||||||
onClick={() => setEmailShareOpen(true)}
|
onClick={() => setEmailShareOpen(true)}
|
||||||
aria-label="Share via email"
|
aria-label="Share via email"
|
||||||
title="Email"
|
title="Email"
|
||||||
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#888] hover:text-[#3b82f6] hover:bg-[#1a1a1a] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#888] hover:text-[#E67E22] hover:bg-[#1a1a1a] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<rect x="2" y="4" width="20" height="16" rx="2" />
|
<rect x="2" y="4" width="20" height="16" rx="2" />
|
||||||
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
|
<path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
|
||||||
@@ -364,7 +364,7 @@ export default function NewsArticleDetailClient({
|
|||||||
|
|
||||||
{/* X / Twitter */}
|
{/* X / Twitter */}
|
||||||
<a
|
<a
|
||||||
href={`https://twitter.com/intent/tweet?url=${encodeURIComponent(articleUrl)}&text=${encodeURIComponent(article.title)}&via=BroadcastBeat`}
|
href={`https://twitter.com/intent/tweet?url=${encodeURIComponent(articleUrl)}&text=${encodeURIComponent(article.title)}&via=AVBeat`}
|
||||||
target="_blank" rel="noopener noreferrer"
|
target="_blank" rel="noopener noreferrer"
|
||||||
aria-label="Share on X"
|
aria-label="Share on X"
|
||||||
title="X (Twitter)"
|
title="X (Twitter)"
|
||||||
@@ -418,7 +418,7 @@ export default function NewsArticleDetailClient({
|
|||||||
}}
|
}}
|
||||||
aria-label="Copy article link"
|
aria-label="Copy article link"
|
||||||
title="Copy link"
|
title="Copy link"
|
||||||
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#888] hover:text-[#3b82f6] hover:bg-[#1a1a1a] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#888] hover:text-[#E67E22] hover:bg-[#1a1a1a] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
||||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
||||||
@@ -432,7 +432,7 @@ export default function NewsArticleDetailClient({
|
|||||||
onClick={handleSaveToReadingList}
|
onClick={handleSaveToReadingList}
|
||||||
disabled={savingState !== "idle"}
|
disabled={savingState !== "idle"}
|
||||||
aria-label={isSaved ? "Remove from reading list" : "Save to reading list"}
|
aria-label={isSaved ? "Remove from reading list" : "Save to reading list"}
|
||||||
className="text-xs font-bold uppercase tracking-widest text-[#3b82f6] hover:text-blue-300 disabled:opacity-50 transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="text-xs font-bold uppercase tracking-widest text-[#E67E22] hover:text-blue-300 disabled:opacity-50 transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
{savingState === "saving" ?"Saving..."
|
{savingState === "saving" ?"Saving..."
|
||||||
: savingState === "removing" ?"Removing..."
|
: savingState === "removing" ?"Removing..."
|
||||||
: isSaved
|
: isSaved
|
||||||
@@ -469,7 +469,7 @@ export default function NewsArticleDetailClient({
|
|||||||
{article.tags.map((tag) => (
|
{article.tags.map((tag) => (
|
||||||
<span
|
<span
|
||||||
key={tag}
|
key={tag}
|
||||||
className="px-3 py-1 text-xs font-body bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] hover:border-[#3b82f6] hover:text-[#3b82f6] transition-colors rounded-sm cursor-default">
|
className="px-3 py-1 text-xs font-body bg-[#1a1a1a] border border-[#2a2a2a] text-[#888] hover:border-[#E67E22] hover:text-[#E67E22] transition-colors rounded-sm cursor-default">
|
||||||
{tag}
|
{tag}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
@@ -483,7 +483,7 @@ export default function NewsArticleDetailClient({
|
|||||||
<p className="font-body text-sm text-[#777]">Found this article useful?</p>
|
<p className="font-body text-sm text-[#777]">Found this article useful?</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => setEmailShareOpen(true)}
|
onClick={() => setEmailShareOpen(true)}
|
||||||
className="flex items-center gap-2 px-4 py-2 bg-[#1a1a1a] border border-[#2a2a2a] hover:border-[#3b82f6] text-[#888] hover:text-[#3b82f6] text-xs font-bold uppercase tracking-widest transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="flex items-center gap-2 px-4 py-2 bg-[#1a1a1a] border border-[#2a2a2a] hover:border-[#E67E22] text-[#888] hover:text-[#E67E22] text-xs font-bold uppercase tracking-widest transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg
|
<svg
|
||||||
width="13"
|
width="13"
|
||||||
height="13"
|
height="13"
|
||||||
@@ -504,7 +504,7 @@ export default function NewsArticleDetailClient({
|
|||||||
<div className="pt-6 border-t border-[#222]">
|
<div className="pt-6 border-t border-[#222]">
|
||||||
<Link
|
<Link
|
||||||
href="/news"
|
href="/news"
|
||||||
className="inline-flex items-center gap-2 text-[#3b82f6] font-body text-sm hover:underline focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="inline-flex items-center gap-2 text-[#E67E22] font-body text-sm hover:underline focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg
|
<svg
|
||||||
width="14"
|
width="14"
|
||||||
height="14"
|
height="14"
|
||||||
@@ -534,7 +534,7 @@ export default function NewsArticleDetailClient({
|
|||||||
|
|
||||||
{/* Related Articles Sidebar */}
|
{/* Related Articles Sidebar */}
|
||||||
<div className="bg-[#111] border border-[#222] p-5">
|
<div className="bg-[#111] border border-[#222] p-5">
|
||||||
<h3 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest mb-4 pb-2 border-b border-[#222]">
|
<h3 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest mb-4 pb-2 border-b border-[#222]">
|
||||||
Related Articles
|
Related Articles
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -553,10 +553,10 @@ export default function NewsArticleDetailClient({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-[#3b82f6] font-body text-[9px] font-bold uppercase tracking-wider mb-0.5">
|
<p className="text-[#E67E22] font-body text-[9px] font-bold uppercase tracking-wider mb-0.5">
|
||||||
{related.category}
|
{related.category}
|
||||||
</p>
|
</p>
|
||||||
<p className="font-heading text-[#e0e0e0] text-xs font-bold group-hover:text-[#3b82f6] transition-colors line-clamp-2 leading-snug">
|
<p className="font-heading text-[#e0e0e0] text-xs font-bold group-hover:text-[#E67E22] transition-colors line-clamp-2 leading-snug">
|
||||||
{related.title}
|
{related.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -568,19 +568,19 @@ export default function NewsArticleDetailClient({
|
|||||||
{/* Related Forum Posts — same widget as /articles/[slug] */}
|
{/* Related Forum Posts — same widget as /articles/[slug] */}
|
||||||
{relatedForumThreads.length > 0 && (
|
{relatedForumThreads.length > 0 && (
|
||||||
<div className="bg-[#111] border border-[#222] p-5">
|
<div className="bg-[#111] border border-[#222] p-5">
|
||||||
<h3 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest mb-4 pb-2 border-b border-[#222]">
|
<h3 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest mb-4 pb-2 border-b border-[#222]">
|
||||||
Related Forum Posts
|
Related Forum Posts
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-3.5">
|
<ul className="space-y-3.5">
|
||||||
{relatedForumThreads.map((t) => (
|
{relatedForumThreads.map((t) => (
|
||||||
<li key={t.id}>
|
<li key={t.id}>
|
||||||
<Link href={`/forum/thread/${t.id}`} className="group block">
|
<Link href={`/forum/thread/${t.id}`} className="group block">
|
||||||
<p className="font-heading text-[#e0e0e0] text-xs font-bold group-hover:text-[#3b82f6] transition-colors line-clamp-2 leading-snug">
|
<p className="font-heading text-[#e0e0e0] text-xs font-bold group-hover:text-[#E67E22] transition-colors line-clamp-2 leading-snug">
|
||||||
{t.title}
|
{t.title}
|
||||||
</p>
|
</p>
|
||||||
<p className="flex items-center gap-2 mt-1 text-[10px] text-[#666]">
|
<p className="flex items-center gap-2 mt-1 text-[10px] text-[#666]">
|
||||||
{t.category_name && (
|
{t.category_name && (
|
||||||
<span className="text-[#3b82f6] font-body font-bold uppercase tracking-wider">
|
<span className="text-[#E67E22] font-body font-bold uppercase tracking-wider">
|
||||||
{t.category_name}
|
{t.category_name}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -594,7 +594,7 @@ export default function NewsArticleDetailClient({
|
|||||||
</ul>
|
</ul>
|
||||||
<Link
|
<Link
|
||||||
href="/forum"
|
href="/forum"
|
||||||
className="block mt-4 pt-3 border-t border-[#222] text-[11px] text-[#3b82f6] hover:underline font-body font-semibold uppercase tracking-wider"
|
className="block mt-4 pt-3 border-t border-[#222] text-[11px] text-[#E67E22] hover:underline font-body font-semibold uppercase tracking-wider"
|
||||||
>
|
>
|
||||||
Browse all forum threads →
|
Browse all forum threads →
|
||||||
</Link>
|
</Link>
|
||||||
@@ -639,7 +639,7 @@ export default function NewsArticleDetailClient({
|
|||||||
onClick={prevCarousel}
|
onClick={prevCarousel}
|
||||||
disabled={carouselIndex === 0}
|
disabled={carouselIndex === 0}
|
||||||
aria-label="Previous articles"
|
aria-label="Previous articles"
|
||||||
className="w-8 h-8 flex items-center justify-center border border-[#2a2a2a] text-[#666] hover:border-[#3b82f6] hover:text-[#3b82f6] disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="w-8 h-8 flex items-center justify-center border border-[#2a2a2a] text-[#666] hover:border-[#E67E22] hover:text-[#E67E22] disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg
|
<svg
|
||||||
width="14"
|
width="14"
|
||||||
height="14"
|
height="14"
|
||||||
@@ -655,7 +655,7 @@ export default function NewsArticleDetailClient({
|
|||||||
onClick={nextCarousel}
|
onClick={nextCarousel}
|
||||||
disabled={carouselIndex >= maxIndex}
|
disabled={carouselIndex >= maxIndex}
|
||||||
aria-label="Next articles"
|
aria-label="Next articles"
|
||||||
className="w-8 h-8 flex items-center justify-center border border-[#2a2a2a] text-[#666] hover:border-[#3b82f6] hover:text-[#3b82f6] disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="w-8 h-8 flex items-center justify-center border border-[#2a2a2a] text-[#666] hover:border-[#E67E22] hover:text-[#E67E22] disabled:opacity-30 disabled:cursor-not-allowed transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg
|
<svg
|
||||||
width="14"
|
width="14"
|
||||||
height="14"
|
height="14"
|
||||||
@@ -694,11 +694,11 @@ export default function NewsArticleDetailClient({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 mb-1.5">
|
<div className="flex items-center gap-2 mb-1.5">
|
||||||
<span className="text-[#3b82f6] font-body text-[10px] font-bold uppercase tracking-wider">
|
<span className="text-[#E67E22] font-body text-[10px] font-bold uppercase tracking-wider">
|
||||||
{related.category}
|
{related.category}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-heading text-[#e0e0e0] text-sm font-bold leading-snug group-hover:text-[#3b82f6] transition-colors line-clamp-2 mb-1.5">
|
<h3 className="font-heading text-[#e0e0e0] text-sm font-bold leading-snug group-hover:text-[#E67E22] transition-colors line-clamp-2 mb-1.5">
|
||||||
{related.title}
|
{related.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-[#666] font-body text-xs line-clamp-2 leading-relaxed">
|
<p className="text-[#666] font-body text-xs line-clamp-2 leading-relaxed">
|
||||||
@@ -723,7 +723,7 @@ export default function NewsArticleDetailClient({
|
|||||||
onClick={() => setCarouselIndex(i)}
|
onClick={() => setCarouselIndex(i)}
|
||||||
aria-label={`Go to slide ${i + 1}`}
|
aria-label={`Go to slide ${i + 1}`}
|
||||||
className={`w-1.5 h-1.5 rounded-full transition-colors focus:outline-none ${
|
className={`w-1.5 h-1.5 rounded-full transition-colors focus:outline-none ${
|
||||||
i === carouselIndex ? "bg-[#3b82f6]" : "bg-[#333]"
|
i === carouselIndex ? "bg-[#E67E22]" : "bg-[#333]"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -756,7 +756,7 @@ export default function NewsArticleDetailClient({
|
|||||||
setEmailStatus("idle");
|
setEmailStatus("idle");
|
||||||
}}
|
}}
|
||||||
aria-label="Close share dialog"
|
aria-label="Close share dialog"
|
||||||
className="absolute top-4 right-4 text-[#555] hover:text-[#e0e0e0] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
|
className="absolute top-4 right-4 text-[#555] hover:text-[#e0e0e0] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]">
|
||||||
<svg
|
<svg
|
||||||
width="18"
|
width="18"
|
||||||
height="18"
|
height="18"
|
||||||
@@ -777,7 +777,7 @@ export default function NewsArticleDetailClient({
|
|||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#3b82f6"
|
stroke="#E67E22"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<rect x="2" y="4" width="20" height="16" rx="2" />
|
<rect x="2" y="4" width="20" height="16" rx="2" />
|
||||||
@@ -823,7 +823,7 @@ export default function NewsArticleDetailClient({
|
|||||||
<label
|
<label
|
||||||
htmlFor="email-to"
|
htmlFor="email-to"
|
||||||
className="block font-body text-xs text-[#888] uppercase tracking-wider mb-1.5">
|
className="block font-body text-xs text-[#888] uppercase tracking-wider mb-1.5">
|
||||||
Recipient Email <span className="text-[#3b82f6]">*</span>
|
Recipient Email <span className="text-[#E67E22]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="email-to"
|
id="email-to"
|
||||||
@@ -831,7 +831,7 @@ export default function NewsArticleDetailClient({
|
|||||||
value={emailTo}
|
value={emailTo}
|
||||||
onChange={(e) => setEmailTo(e.target.value)}
|
onChange={(e) => setEmailTo(e.target.value)}
|
||||||
placeholder="colleague@example.com"
|
placeholder="colleague@example.com"
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] focus:border-[#3b82f6] text-[#e0e0e0] placeholder-[#444] text-sm font-body px-3 py-2.5 rounded-sm outline-none transition-colors"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] focus:border-[#E67E22] text-[#e0e0e0] placeholder-[#444] text-sm font-body px-3 py-2.5 rounded-sm outline-none transition-colors"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -846,7 +846,7 @@ export default function NewsArticleDetailClient({
|
|||||||
onChange={(e) => setEmailNote(e.target.value)}
|
onChange={(e) => setEmailNote(e.target.value)}
|
||||||
placeholder="Thought you might find this interesting..."
|
placeholder="Thought you might find this interesting..."
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] focus:border-[#3b82f6] text-[#e0e0e0] placeholder-[#444] text-sm font-body px-3 py-2.5 rounded-sm outline-none transition-colors resize-none"
|
className="w-full bg-[#0d0d0d] border border-[#2a2a2a] focus:border-[#E67E22] text-[#e0e0e0] placeholder-[#444] text-sm font-body px-3 py-2.5 rounded-sm outline-none transition-colors resize-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -890,7 +890,7 @@ export default function NewsArticleDetailClient({
|
|||||||
<div
|
<div
|
||||||
role="status"
|
role="status"
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
className="fixed bottom-4 right-4 bg-[#1a1a1a] border border-[#3b82f6] text-white px-4 py-3 rounded-sm text-sm font-body shadow-lg z-50">
|
className="fixed bottom-4 right-4 bg-[#1a1a1a] border border-[#E67E22] text-white px-4 py-3 rounded-sm text-sm font-body shadow-lg z-50">
|
||||||
{saveToast}
|
{saveToast}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ interface PageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SITE_URL =
|
const SITE_URL =
|
||||||
process.env.NEXT_PUBLIC_SITE_URL || "https://broadcastbeat.com";
|
process.env.NEXT_PUBLIC_SITE_URL || "https://avbeat.com";
|
||||||
|
|
||||||
/** Convert a free-form date (e.g. "April 9, 2026") to ISO 8601 for JSON-LD. */
|
/** Convert a free-form date (e.g. "April 9, 2026") to ISO 8601 for JSON-LD. */
|
||||||
function toIso(d: string | null | undefined): string {
|
function toIso(d: string | null | undefined): string {
|
||||||
@@ -59,7 +59,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
|||||||
return { title: "Article Not Found", description: "The article you are looking for does not exist." };
|
return { title: "Article Not Found", description: "The article you are looking for does not exist." };
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
title: `${article.title} — Broadcast Beat`,
|
title: `${article.title} — AV Beat`,
|
||||||
description: article.excerpt,
|
description: article.excerpt,
|
||||||
alternates: { canonical: `/news/${article.slug}` },
|
alternates: { canonical: `/news/${article.slug}` },
|
||||||
openGraph: {
|
openGraph: {
|
||||||
@@ -77,7 +77,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
|||||||
title: article.title,
|
title: article.title,
|
||||||
description: article.excerpt,
|
description: article.excerpt,
|
||||||
images: [article.image],
|
images: [article.image],
|
||||||
creator: "@Broadcast Beat",
|
creator: "@AV Beat",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ export default async function NewsArticlePage({ params }: PageProps) {
|
|||||||
author: { "@type": "Person", name: article.author },
|
author: { "@type": "Person", name: article.author },
|
||||||
publisher: {
|
publisher: {
|
||||||
"@type": "Organization",
|
"@type": "Organization",
|
||||||
name: "Broadcast Beat",
|
name: "AV Beat",
|
||||||
logo: { "@type": "ImageObject", url: `${SITE_URL}/assets/images/logo.png` },
|
logo: { "@type": "ImageObject", url: `${SITE_URL}/assets/images/logo.png` },
|
||||||
},
|
},
|
||||||
datePublished: toIso(article.date),
|
datePublished: toIso(article.date),
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { getLegacyFeaturedArticles } from "@/lib/articles/legacy-source";
|
|||||||
export const revalidate = 1800;
|
export const revalidate = 1800;
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Featured Stories — Broadcast Beat",
|
title: "Featured Stories — AV Beat",
|
||||||
description:
|
description:
|
||||||
"Editorial picks from the Broadcast Beat staff — the most important industry stories, hand-selected from across broadcast engineering, IP/cloud workflows, streaming, AI, and live production.",
|
"Editorial picks from the AV Beat staff — the most important industry stories, hand-selected from across broadcast engineering, IP/cloud workflows, streaming, AI, and live production.",
|
||||||
alternates: { canonical: "/news/featured" },
|
alternates: { canonical: "/news/featured" },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export default async function FeaturedNewsPage() {
|
|||||||
articles={articles}
|
articles={articles}
|
||||||
kicker="Editorial"
|
kicker="Editorial"
|
||||||
heading="Featured Editorial"
|
heading="Featured Editorial"
|
||||||
subtitle="Hand-picked feature stories, exclusives, and cover articles from the Broadcast Beat editorial desk."
|
subtitle="Hand-picked feature stories, exclusives, and cover articles from the AV Beat editorial desk."
|
||||||
hideAiSuggestions
|
hideAiSuggestions
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getLegacyArticlesBySection } from "@/lib/articles/legacy-source";
|
|||||||
export const revalidate = 1800;
|
export const revalidate = 1800;
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Industry News — Broadcast Beat",
|
title: "Industry News — AV Beat",
|
||||||
description:
|
description:
|
||||||
"Breaking news and industry updates from the world of broadcast engineering, IP/cloud workflows, streaming, AI, and live production.",
|
"Breaking news and industry updates from the world of broadcast engineering, IP/cloud workflows, streaming, AI, and live production.",
|
||||||
alternates: { canonical: "/news" },
|
alternates: { canonical: "/news" },
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ function NewsletterCard({ newsletter }: { newsletter: Newsletter }) {
|
|||||||
const blocks = Array.isArray(newsletter.article_blocks)
|
const blocks = Array.isArray(newsletter.article_blocks)
|
||||||
? newsletter.article_blocks.filter((b) => b.title)
|
? newsletter.article_blocks.filter((b) => b.title)
|
||||||
: [];
|
: [];
|
||||||
const accent = newsletter.accent_color || "#3b82f6";
|
const accent = newsletter.accent_color || "#E67E22";
|
||||||
|
|
||||||
const layoutLabel: Record<string, string> = {
|
const layoutLabel: Record<string, string> = {
|
||||||
featured: "Weekly Digest",
|
featured: "Weekly Digest",
|
||||||
@@ -113,7 +113,7 @@ function NewsletterCard({ newsletter }: { newsletter: Newsletter }) {
|
|||||||
<span className="mt-1.5 w-1 h-1 rounded-full bg-[#444] flex-shrink-0" />
|
<span className="mt-1.5 w-1 h-1 rounded-full bg-[#444] flex-shrink-0" />
|
||||||
<span className="font-body text-[#888] text-xs leading-relaxed line-clamp-1">
|
<span className="font-body text-[#888] text-xs leading-relaxed line-clamp-1">
|
||||||
{block.category && (
|
{block.category && (
|
||||||
<span className="text-[#3b82f6] font-bold mr-1">{block.category}:</span>
|
<span className="text-[#E67E22] font-bold mr-1">{block.category}:</span>
|
||||||
)}
|
)}
|
||||||
{block.title}
|
{block.title}
|
||||||
</span>
|
</span>
|
||||||
@@ -132,7 +132,7 @@ function NewsletterCard({ newsletter }: { newsletter: Newsletter }) {
|
|||||||
<span className="font-body text-[#555] text-xs">
|
<span className="font-body text-[#555] text-xs">
|
||||||
{blocks.length} {blocks.length === 1 ? "story" : "stories"}
|
{blocks.length} {blocks.length === 1 ? "story" : "stories"}
|
||||||
</span>
|
</span>
|
||||||
<span className="font-body text-[#3b82f6] text-xs font-medium">
|
<span className="font-body text-[#E67E22] text-xs font-medium">
|
||||||
Issue #{newsletter.id.slice(0, 6).toUpperCase()}
|
Issue #{newsletter.id.slice(0, 6).toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -197,9 +197,9 @@ function SignupCTA() {
|
|||||||
|
|
||||||
{status === "success" ? (
|
{status === "success" ? (
|
||||||
<div className="text-center py-4" role="status" aria-live="polite">
|
<div className="text-center py-4" role="status" aria-live="polite">
|
||||||
<div className="w-10 h-10 rounded-full bg-[#3b82f6]/20 flex items-center justify-center mx-auto mb-3">
|
<div className="w-10 h-10 rounded-full bg-[#E67E22]/20 flex items-center justify-center mx-auto mb-3">
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
||||||
<path d="M4 10l4 4 8-8" stroke="#3b82f6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M4 10l4 4 8-8" stroke="#E67E22" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-heading text-[#e0e0e0] font-bold mb-1">You're subscribed!</p>
|
<p className="font-heading text-[#e0e0e0] font-bold mb-1">You're subscribed!</p>
|
||||||
@@ -228,7 +228,7 @@ function SignupCTA() {
|
|||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={`mr-1.5 inline-block w-3 h-3 rounded-sm border transition-colors ${checked ? "bg-[#3b82f6] border-[#3b82f6]" : "border-[#444]"}`}>
|
className={`mr-1.5 inline-block w-3 h-3 rounded-sm border transition-colors ${checked ? "bg-[#E67E22] border-[#E67E22]" : "border-[#444]"}`}>
|
||||||
{checked && (
|
{checked && (
|
||||||
<svg viewBox="0 0 12 12" fill="none" className="w-full h-full" aria-hidden="true">
|
<svg viewBox="0 0 12 12" fill="none" className="w-full h-full" aria-hidden="true">
|
||||||
<path d="M2 6l3 3 5-5" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
<path d="M2 6l3 3 5-5" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
@@ -262,7 +262,7 @@ function SignupCTA() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={status === "loading"}
|
disabled={status === "loading"}
|
||||||
className={`btn-subscribe py-2.5 px-6 text-sm whitespace-nowrap focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${status === "loading" ? "opacity-70 cursor-not-allowed" : ""}`}>
|
className={`btn-subscribe py-2.5 px-6 text-sm whitespace-nowrap focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${status === "loading" ? "opacity-70 cursor-not-allowed" : ""}`}>
|
||||||
{status === "loading" ? "Subscribing..." : "Subscribe Free"}
|
{status === "loading" ? "Subscribing..." : "Subscribe Free"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -275,7 +275,7 @@ function SignupCTA() {
|
|||||||
|
|
||||||
<p className="font-body text-[#555] text-xs mt-3 text-center">
|
<p className="font-body text-[#555] text-xs mt-3 text-center">
|
||||||
No spam. Unsubscribe anytime.{" "}
|
No spam. Unsubscribe anytime.{" "}
|
||||||
<Link href="/privacy" className="text-[#3b82f6] hover:underline">
|
<Link href="/privacy" className="text-[#E67E22] hover:underline">
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
@@ -379,7 +379,7 @@ export default function NewsletterArchivePage() {
|
|||||||
</div>
|
</div>
|
||||||
<h1 className="font-heading text-white text-3xl font-bold">Newsletter Archive</h1>
|
<h1 className="font-heading text-white text-3xl font-bold">Newsletter Archive</h1>
|
||||||
<p className="text-[#777] font-body text-sm mt-2">
|
<p className="text-[#777] font-body text-sm mt-2">
|
||||||
Browse past issues of the Broadcast Beat newsletter — searchable by date and topic.
|
Browse past issues of the AV Beat newsletter — searchable by date and topic.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -419,9 +419,9 @@ export default function NewsletterArchivePage() {
|
|||||||
<button
|
<button
|
||||||
key={topic}
|
key={topic}
|
||||||
onClick={() => setSelectedTopic(topic)}
|
onClick={() => setSelectedTopic(topic)}
|
||||||
className={`font-body text-xs px-3 py-1.5 rounded-sm border transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`font-body text-xs px-3 py-1.5 rounded-sm border transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
selectedTopic === topic
|
selectedTopic === topic
|
||||||
? "bg-[#3b82f6] border-[#3b82f6] text-white"
|
? "bg-[#E67E22] border-[#E67E22] text-white"
|
||||||
: "bg-transparent border-[#333] text-[#888] hover:border-[#555] hover:text-[#ccc]"
|
: "bg-transparent border-[#333] text-[#888] hover:border-[#555] hover:text-[#ccc]"
|
||||||
}`}
|
}`}
|
||||||
aria-pressed={selectedTopic === topic}>
|
aria-pressed={selectedTopic === topic}>
|
||||||
@@ -462,7 +462,7 @@ export default function NewsletterArchivePage() {
|
|||||||
{hasActiveFilters && (
|
{hasActiveFilters && (
|
||||||
<button
|
<button
|
||||||
onClick={handleClearFilters}
|
onClick={handleClearFilters}
|
||||||
className="font-body text-xs text-[#3b82f6] hover:text-[#60a5fa] transition-colors underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]">
|
className="font-body text-xs text-[#E67E22] hover:text-[#E67E22] transition-colors underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]">
|
||||||
Clear filters
|
Clear filters
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@@ -498,7 +498,7 @@ export default function NewsletterArchivePage() {
|
|||||||
<p className="font-body text-red-400 text-sm mb-3">{error}</p>
|
<p className="font-body text-red-400 text-sm mb-3">{error}</p>
|
||||||
<button
|
<button
|
||||||
onClick={fetchArchive}
|
onClick={fetchArchive}
|
||||||
className="font-body text-xs text-[#3b82f6] hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]">
|
className="font-body text-xs text-[#E67E22] hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]">
|
||||||
Try again
|
Try again
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -521,7 +521,7 @@ export default function NewsletterArchivePage() {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={handleClearFilters}
|
onClick={handleClearFilters}
|
||||||
className="font-body text-xs text-[#3b82f6] hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]">
|
className="font-body text-xs text-[#E67E22] hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]">
|
||||||
Clear all filters
|
Clear all filters
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -555,9 +555,9 @@ export default function NewsletterArchivePage() {
|
|||||||
<li key={topic}>
|
<li key={topic}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setSelectedTopic(topic)}
|
onClick={() => setSelectedTopic(topic)}
|
||||||
className={`w-full text-left font-body text-sm py-1.5 px-2 rounded-sm transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`w-full text-left font-body text-sm py-1.5 px-2 rounded-sm transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
selectedTopic === topic
|
selectedTopic === topic
|
||||||
? "text-[#3b82f6] bg-[#3b82f6]/10"
|
? "text-[#E67E22] bg-[#E67E22]/10"
|
||||||
: "text-[#888] hover:text-[#ccc] hover:bg-[#1a1a1a]"
|
: "text-[#888] hover:text-[#ccc] hover:bg-[#1a1a1a]"
|
||||||
}`}>
|
}`}>
|
||||||
{topic}
|
{topic}
|
||||||
@@ -578,9 +578,9 @@ export default function NewsletterArchivePage() {
|
|||||||
<li key={year}>
|
<li key={year}>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setSelectedYear(String(year)); setSelectedMonth(""); }}
|
onClick={() => { setSelectedYear(String(year)); setSelectedMonth(""); }}
|
||||||
className={`w-full text-left font-body text-sm py-1.5 px-2 rounded-sm transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] ${
|
className={`w-full text-left font-body text-sm py-1.5 px-2 rounded-sm transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] ${
|
||||||
selectedYear === String(year)
|
selectedYear === String(year)
|
||||||
? "text-[#3b82f6] bg-[#3b82f6]/10"
|
? "text-[#E67E22] bg-[#E67E22]/10"
|
||||||
: "text-[#888] hover:text-[#ccc] hover:bg-[#1a1a1a]"
|
: "text-[#888] hover:text-[#ccc] hover:bg-[#1a1a1a]"
|
||||||
}`}>
|
}`}>
|
||||||
{year}
|
{year}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Newsletter — Broadcast Beat",
|
title: "Newsletter — AV Beat",
|
||||||
description: "Broadcast Beat newsletter archive.",
|
description: "AV Beat newsletter archive.",
|
||||||
alternates: { canonical: "/newsletter/archive" },
|
alternates: { canonical: "/newsletter/archive" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import Header from "@/components/Header";
|
|||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Privacy Policy — Broadcast Beat",
|
title: "Privacy Policy — AV Beat",
|
||||||
description: "Broadcast Beat Privacy Policy — how we collect, use, and protect your personal information. GDPR and CCPA compliant.",
|
description: "AV Beat Privacy Policy — how we collect, use, and protect your personal information. GDPR and CCPA compliant.",
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: "/privacy",
|
canonical: "/privacy",
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,7 @@ export default function PrivacyPage() {
|
|||||||
|
|
||||||
<section aria-labelledby="section-intro">
|
<section aria-labelledby="section-intro">
|
||||||
<p id="section-intro">
|
<p id="section-intro">
|
||||||
This Privacy Policy describes how <strong className="text-[#e0e0e0]">Relevant Media Properties, LLC</strong> ("we," "us," or "our"), publisher of Broadcast Beat, AV Beat, Backlot Beat, and BroadcastEngineering.com, collects, uses, and shares information about you when you use our websites and services. We are committed to protecting your privacy and complying with applicable data protection laws including GDPR and CCPA.
|
This Privacy Policy describes how <strong className="text-[#e0e0e0]">Relevant Media Properties, LLC</strong> ("we," "us," or "our"), publisher of AV Beat, AV Beat, Backlot Beat, and BroadcastEngineering.com, collects, uses, and shares information about you when you use our websites and services. We are committed to protecting your privacy and complying with applicable data protection laws including GDPR and CCPA.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ export default function PrivacyPage() {
|
|||||||
<li><strong className="text-[#ccc]">Opt-out (CCPA):</strong> California residents may opt out of the sale of personal information</li>
|
<li><strong className="text-[#ccc]">Opt-out (CCPA):</strong> California residents may opt out of the sale of personal information</li>
|
||||||
<li><strong className="text-[#ccc]">Unsubscribe:</strong> Opt out of marketing emails at any time via the unsubscribe link</li>
|
<li><strong className="text-[#ccc]">Unsubscribe:</strong> Opt out of marketing emails at any time via the unsubscribe link</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p className="mt-3">To exercise any of these rights, contact us at <a href="mailto:privacy@broadcastbeat.com" className="text-[#3b82f6] underline hover:text-blue-400 transition-colors">privacy@broadcastbeat.com</a>. We will respond within 30 days.</p>
|
<p className="mt-3">To exercise any of these rights, contact us at <a href="mailto:privacy@avbeat.com" className="text-[#E67E22] underline hover:text-blue-400 transition-colors">privacy@avbeat.com</a>. We will respond within 30 days.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section aria-labelledby="section-6">
|
<section aria-labelledby="section-6">
|
||||||
@@ -127,7 +127,7 @@ export default function PrivacyPage() {
|
|||||||
<address className="not-italic mt-3 space-y-1 text-[#999]">
|
<address className="not-italic mt-3 space-y-1 text-[#999]">
|
||||||
<p><strong className="text-[#ccc]">Relevant Media Properties, LLC</strong></p>
|
<p><strong className="text-[#ccc]">Relevant Media Properties, LLC</strong></p>
|
||||||
<p>Privacy Officer</p>
|
<p>Privacy Officer</p>
|
||||||
<p>Email: <a href="mailto:privacy@broadcastbeat.com" className="text-[#3b82f6] underline hover:text-blue-400 transition-colors">privacy@broadcastbeat.com</a></p>
|
<p>Email: <a href="mailto:privacy@avbeat.com" className="text-[#E67E22] underline hover:text-blue-400 transition-colors">privacy@avbeat.com</a></p>
|
||||||
</address>
|
</address>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export default function RegisterPage() {
|
|||||||
<div className="min-h-screen bg-[#111111]">
|
<div className="min-h-screen bg-[#111111]">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="flex items-center justify-center py-24">
|
<div className="flex items-center justify-center py-24">
|
||||||
<div className="w-6 h-6 border-2 border-[#3b82f6] border-t-transparent rounded-full animate-spin" />
|
<div className="w-6 h-6 border-2 border-[#E67E22] border-t-transparent rounded-full animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
@@ -97,7 +97,7 @@ export default function RegisterPage() {
|
|||||||
<div className="bg-[#0d0d0d] border-b border-[#1e1e1e]">
|
<div className="bg-[#0d0d0d] border-b border-[#1e1e1e]">
|
||||||
<div className="max-w-container mx-auto px-4 py-2.5">
|
<div className="max-w-container mx-auto px-4 py-2.5">
|
||||||
<nav aria-label="Breadcrumb" className="flex items-center gap-2 text-xs font-body text-[#555]">
|
<nav aria-label="Breadcrumb" className="flex items-center gap-2 text-xs font-body text-[#555]">
|
||||||
<Link href="/home-page" className="hover:text-[#3b82f6] transition-colors">Home</Link>
|
<Link href="/home-page" className="hover:text-[#E67E22] transition-colors">Home</Link>
|
||||||
<span aria-hidden="true">/</span>
|
<span aria-hidden="true">/</span>
|
||||||
<span className="text-[#888]">Create Account</span>
|
<span className="text-[#888]">Create Account</span>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -112,7 +112,7 @@ export default function RegisterPage() {
|
|||||||
Create Account
|
Create Account
|
||||||
</h1>
|
</h1>
|
||||||
<p className="font-body text-sm text-[#666]">
|
<p className="font-body text-sm text-[#666]">
|
||||||
Join Broadcast Beat to save articles, track your reading history, and get personalized content.
|
Join AV Beat to save articles, track your reading history, and get personalized content.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ export default function RegisterPage() {
|
|||||||
onChange={(e) => setFullName(e.target.value)}
|
onChange={(e) => setFullName(e.target.value)}
|
||||||
placeholder="Jane Smith"
|
placeholder="Jane Smith"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#3b82f6] focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors disabled:opacity-50"
|
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#E67E22] focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ export default function RegisterPage() {
|
|||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
placeholder="you@example.com"
|
placeholder="you@example.com"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#3b82f6] focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors disabled:opacity-50"
|
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#E67E22] focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ export default function RegisterPage() {
|
|||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
placeholder="Min. 6 characters"
|
placeholder="Min. 6 characters"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#3b82f6] focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors disabled:opacity-50"
|
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#E67E22] focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ export default function RegisterPage() {
|
|||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
placeholder="Re-enter password"
|
placeholder="Re-enter password"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#3b82f6] focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors disabled:opacity-50"
|
className="w-full h-10 px-3 bg-[#0d0d0d] border border-[#2a2a2a] text-[#e0e0e0] font-body text-sm rounded-sm placeholder-[#444] focus:outline-none focus:border-[#E67E22] focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ export default function RegisterPage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full h-10 bg-[#3b82f6] hover:bg-[#2563eb] text-white font-body text-sm font-bold uppercase tracking-wider rounded-sm transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2">
|
className="w-full h-10 bg-[#E67E22] hover:bg-[#C8651B] text-white font-body text-sm font-bold uppercase tracking-wider rounded-sm transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<>
|
<>
|
||||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||||
@@ -223,7 +223,7 @@ export default function RegisterPage() {
|
|||||||
{/* Login Link */}
|
{/* Login Link */}
|
||||||
<Link
|
<Link
|
||||||
href="/login"
|
href="/login"
|
||||||
className="block w-full h-10 border border-[#2a2a2a] hover:border-[#3b82f6] text-[#888] hover:text-[#3b82f6] font-body text-sm font-bold uppercase tracking-wider rounded-sm transition-colors flex items-center justify-center">
|
className="block w-full h-10 border border-[#2a2a2a] hover:border-[#E67E22] text-[#888] hover:text-[#E67E22] font-body text-sm font-bold uppercase tracking-wider rounded-sm transition-colors flex items-center justify-center">
|
||||||
Sign In
|
Sign In
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MetadataRoute } from 'next';
|
import { MetadataRoute } from 'next';
|
||||||
|
|
||||||
export default function robots(): MetadataRoute.Robots {
|
export default function robots(): MetadataRoute.Robots {
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://broadcastbeat.com';
|
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://avbeat.com';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rules: [
|
rules: [
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import SidebarAdStack from "@/components/SidebarAdStack";
|
|||||||
export const revalidate = 60;
|
export const revalidate = 60;
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Search — Broadcast Beat",
|
title: "Search — AV Beat",
|
||||||
description:
|
description:
|
||||||
"Search Broadcast Beat for broadcast engineering news, gear reviews, show coverage, and technology insights.",
|
"Search AV Beat for broadcast engineering news, gear reviews, show coverage, and technology insights.",
|
||||||
alternates: { canonical: "/search" },
|
alternates: { canonical: "/search" },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
|
|||||||
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
||||||
</div>
|
</div>
|
||||||
<h1 className="font-heading text-white text-3xl font-bold">
|
<h1 className="font-heading text-white text-3xl font-bold">
|
||||||
{query ? `Results for "${query}"` : "Search Broadcast Beat"}
|
{query ? `Results for "${query}"` : "Search AV Beat"}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-[#777] font-body text-sm mt-2">
|
<p className="text-[#777] font-body text-sm mt-2">
|
||||||
{query
|
{query
|
||||||
@@ -51,7 +51,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
|
|||||||
{query && results.length === 0 && (
|
{query && results.length === 0 && (
|
||||||
<div className="py-16 text-center">
|
<div className="py-16 text-center">
|
||||||
<p className="text-[#555] font-body text-sm mb-3">No articles match your search.</p>
|
<p className="text-[#555] font-body text-sm mb-3">No articles match your search.</p>
|
||||||
<Link href="/news" className="text-[#3b82f6] text-xs font-body hover:underline">
|
<Link href="/news" className="text-[#E67E22] text-xs font-body hover:underline">
|
||||||
Browse all news →
|
Browse all news →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,7 +69,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
|
|||||||
<div className="flex-shrink-0 w-[160px] h-[105px] relative overflow-hidden rounded-sm">
|
<div className="flex-shrink-0 w-[160px] h-[105px] relative overflow-hidden rounded-sm">
|
||||||
<AppImage
|
<AppImage
|
||||||
src={article.image || "/assets/images/article-placeholder.jpg"}
|
src={article.image || "/assets/images/article-placeholder.jpg"}
|
||||||
alt={article.alt || article.title || "Broadcast Beat article"}
|
alt={article.alt || article.title || "AV Beat article"}
|
||||||
fill
|
fill
|
||||||
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
||||||
sizes="160px"
|
sizes="160px"
|
||||||
@@ -77,18 +77,18 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1.5">
|
<div className="flex items-center gap-2 mb-1.5">
|
||||||
<span className="text-[#3b82f6] font-body text-[10px] font-bold uppercase tracking-wider">
|
<span className="text-[#E67E22] font-body text-[10px] font-bold uppercase tracking-wider">
|
||||||
{article.category || "NEWS"}
|
{article.category || "NEWS"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="font-heading text-[#e0e0e0] text-base font-bold leading-snug mb-2 group-hover:text-[#3b82f6] transition-colors line-clamp-2">
|
<h2 className="font-heading text-[#e0e0e0] text-base font-bold leading-snug mb-2 group-hover:text-[#E67E22] transition-colors line-clamp-2">
|
||||||
{article.title || "Untitled"}
|
{article.title || "Untitled"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-[#777] font-body text-sm leading-relaxed line-clamp-2">
|
<p className="text-[#777] font-body text-sm leading-relaxed line-clamp-2">
|
||||||
{article.excerpt || ""}
|
{article.excerpt || ""}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-2 mt-2">
|
<div className="flex items-center gap-2 mt-2">
|
||||||
<span className="text-[#555] font-body text-xs">By {article.author || "Broadcast Beat"}</span>
|
<span className="text-[#555] font-body text-xs">By {article.author || "AV Beat"}</span>
|
||||||
<span className="text-[#444] text-[10px]">·</span>
|
<span className="text-[#444] text-[10px]">·</span>
|
||||||
<span className="text-[#555] font-body text-xs">{article.readTime || ""}</span>
|
<span className="text-[#555] font-body text-xs">{article.readTime || ""}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { MetadataRoute } from 'next';
|
|||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
import { getLegacyRecentSitemapEntries } from '@/lib/articles/legacy-source';
|
import { getLegacyRecentSitemapEntries } from '@/lib/articles/legacy-source';
|
||||||
|
|
||||||
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://broadcastbeat.com';
|
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://avbeat.com';
|
||||||
|
|
||||||
// Cap below Google's 50k URL/sitemap hard limit, leaving room for the
|
// Cap below Google's 50k URL/sitemap hard limit, leaving room for the
|
||||||
// static + author + event entries we add below.
|
// static + author + event entries we add below.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ interface PageProps {
|
|||||||
export async function generateMetadata({ searchParams }: PageProps): Promise<Metadata> {
|
export async function generateMetadata({ searchParams }: PageProps): Promise<Metadata> {
|
||||||
const sp = await searchParams;
|
const sp = await searchParams;
|
||||||
const cat = sp?.category && CATEGORY_FILTERS[sp.category];
|
const cat = sp?.category && CATEGORY_FILTERS[sp.category];
|
||||||
const title = cat ? `${cat.title} — Broadcast Beat` : "Production Technology Deep Dives — Broadcast Beat";
|
const title = cat ? `${cat.title} — AV Beat` : "Production Technology Deep Dives — AV Beat";
|
||||||
const desc = cat?.description || "Deep-dive analysis of broadcast technology trends: IP workflows, cloud production, AI automation, streaming infrastructure, and ATSC 3.0.";
|
const desc = cat?.description || "Deep-dive analysis of broadcast technology trends: IP workflows, cloud production, AI automation, streaming infrastructure, and ATSC 3.0.";
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
@@ -87,7 +87,7 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
<span className="section-label">Technology</span>
|
<span className="section-label">Technology</span>
|
||||||
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
||||||
{filter && (
|
{filter && (
|
||||||
<Link href="/technology" className="text-[#666] hover:text-[#3b82f6] font-body text-[11px] underline">
|
<Link href="/technology" className="text-[#666] hover:text-[#E67E22] font-body text-[11px] underline">
|
||||||
clear filter
|
clear filter
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
@@ -101,7 +101,7 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
href="/technology"
|
href="/technology"
|
||||||
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all ${
|
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all ${
|
||||||
!filterSlug
|
!filterSlug
|
||||||
? "bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/50"
|
? "bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/50"
|
||||||
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc]"
|
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc]"
|
||||||
}`}>
|
}`}>
|
||||||
All
|
All
|
||||||
@@ -114,7 +114,7 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
href={`/technology?category=${slug}`}
|
href={`/technology?category=${slug}`}
|
||||||
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all ${
|
className={`px-3 py-1.5 rounded-sm font-body text-[11px] font-semibold uppercase tracking-wide transition-all ${
|
||||||
isActive
|
isActive
|
||||||
? "bg-[#3b82f6]/20 text-[#3b82f6] border border-[#3b82f6]/50"
|
? "bg-[#E67E22]/20 text-[#E67E22] border border-[#E67E22]/50"
|
||||||
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc]"
|
: "bg-[#111] text-[#888] border border-[#2a2a2a] hover:text-[#ccc]"
|
||||||
}`}>
|
}`}>
|
||||||
{def.title}
|
{def.title}
|
||||||
@@ -133,7 +133,7 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
No articles found{filter ? ` for "${filter.title}"` : ""}.
|
No articles found{filter ? ` for "${filter.title}"` : ""}.
|
||||||
</p>
|
</p>
|
||||||
{filter && (
|
{filter && (
|
||||||
<Link href="/technology" className="inline-block mt-3 text-[#3b82f6] hover:underline font-body text-sm">
|
<Link href="/technology" className="inline-block mt-3 text-[#E67E22] hover:underline font-body text-sm">
|
||||||
← Back to all technology coverage
|
← Back to all technology coverage
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
@@ -145,7 +145,7 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
<div className="mb-10">
|
<div className="mb-10">
|
||||||
<Link
|
<Link
|
||||||
href={`/articles/${articles[0].slug}`}
|
href={`/articles/${articles[0].slug}`}
|
||||||
className="flex flex-col md:flex-row gap-6 bg-[#111] border border-[#222] overflow-hidden group hover:border-[#3b82f6] transition-colors p-5">
|
className="flex flex-col md:flex-row gap-6 bg-[#111] border border-[#222] overflow-hidden group hover:border-[#E67E22] transition-colors p-5">
|
||||||
<div className="flex-shrink-0 relative w-full md:w-[320px] h-[200px] md:h-[200px] overflow-hidden rounded-sm">
|
<div className="flex-shrink-0 relative w-full md:w-[320px] h-[200px] md:h-[200px] overflow-hidden rounded-sm">
|
||||||
<AppImage
|
<AppImage
|
||||||
src={articles[0].image}
|
src={articles[0].image}
|
||||||
@@ -157,13 +157,13 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center gap-2 mb-2">
|
<div className="flex items-center gap-2 mb-2">
|
||||||
<span className="text-[#3b82f6] font-body text-[10px] font-bold uppercase tracking-wider">{articles[0].category}</span>
|
<span className="text-[#E67E22] font-body text-[10px] font-bold uppercase tracking-wider">{articles[0].category}</span>
|
||||||
<span className="text-[#444] text-[10px]">·</span>
|
<span className="text-[#444] text-[10px]">·</span>
|
||||||
<span className="text-[#555] font-body text-[11px]">{articles[0].date}</span>
|
<span className="text-[#555] font-body text-[11px]">{articles[0].date}</span>
|
||||||
<span className="text-[#444] text-[10px]">·</span>
|
<span className="text-[#444] text-[10px]">·</span>
|
||||||
<span className="text-[#555] font-body text-[11px]">{articles[0].readTime}</span>
|
<span className="text-[#555] font-body text-[11px]">{articles[0].readTime}</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold leading-tight mb-3 group-hover:text-[#3b82f6] transition-colors">
|
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold leading-tight mb-3 group-hover:text-[#E67E22] transition-colors">
|
||||||
{articles[0].title}
|
{articles[0].title}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-[#777] font-body text-sm leading-relaxed mb-4">{articles[0].excerpt}</p>
|
<p className="text-[#777] font-body text-sm leading-relaxed mb-4">{articles[0].excerpt}</p>
|
||||||
@@ -185,7 +185,7 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
<Link
|
<Link
|
||||||
key={article.slug}
|
key={article.slug}
|
||||||
href={`/articles/${article.slug}`}
|
href={`/articles/${article.slug}`}
|
||||||
className="flex gap-4 bg-[#111] border border-[#222] p-4 group hover:border-[#3b82f6] transition-colors">
|
className="flex gap-4 bg-[#111] border border-[#222] p-4 group hover:border-[#E67E22] transition-colors">
|
||||||
<div className="flex-shrink-0 w-[120px] h-[80px] relative overflow-hidden rounded-sm">
|
<div className="flex-shrink-0 w-[120px] h-[80px] relative overflow-hidden rounded-sm">
|
||||||
<AppImage
|
<AppImage
|
||||||
src={article.image}
|
src={article.image}
|
||||||
@@ -197,11 +197,11 @@ export default async function TechnologyPage({ searchParams }: PageProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<span className="text-[#3b82f6] font-body text-[9px] font-bold uppercase tracking-wider">{article.category}</span>
|
<span className="text-[#E67E22] font-body text-[9px] font-bold uppercase tracking-wider">{article.category}</span>
|
||||||
<span className="text-[#444] text-[9px]">·</span>
|
<span className="text-[#444] text-[9px]">·</span>
|
||||||
<span className="text-[#555] font-body text-[10px]">{article.readTime}</span>
|
<span className="text-[#555] font-body text-[10px]">{article.readTime}</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="font-heading text-[#e0e0e0] text-sm font-bold leading-snug mb-1 group-hover:text-[#3b82f6] transition-colors line-clamp-2">
|
<h2 className="font-heading text-[#e0e0e0] text-sm font-bold leading-snug mb-1 group-hover:text-[#E67E22] transition-colors line-clamp-2">
|
||||||
{article.title}
|
{article.title}
|
||||||
</h2>
|
</h2>
|
||||||
{article.category !== "Featured" && (
|
{article.category !== "Featured" && (
|
||||||
|
|||||||
@@ -103,16 +103,16 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<svg className="w-4 h-4 text-[#3b82f6]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 text-[#E67E22]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.347.346A3.5 3.5 0 0114.5 20h-5a3.5 3.5 0 01-2.47-1.024l-.347-.346z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.347.346A3.5 3.5 0 0114.5 20h-5a3.5 3.5 0 01-2.47-1.024l-.347-.346z" />
|
||||||
</svg>
|
</svg>
|
||||||
<h3 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest">
|
<h3 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest">
|
||||||
AI Picks For You
|
AI Picks For You
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTopicPicker(!showTopicPicker)}
|
onClick={() => setShowTopicPicker(!showTopicPicker)}
|
||||||
className="text-[10px] font-body text-[#555] hover:text-[#3b82f6] transition-colors flex items-center gap-1"
|
className="text-[10px] font-body text-[#555] hover:text-[#E67E22] transition-colors flex items-center gap-1"
|
||||||
>
|
>
|
||||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
|
||||||
@@ -132,8 +132,8 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
onClick={() => toggleInterest(topic)}
|
onClick={() => toggleInterest(topic)}
|
||||||
className={`px-2 py-0.5 text-[10px] font-body rounded-sm border transition-colors ${
|
className={`px-2 py-0.5 text-[10px] font-body rounded-sm border transition-colors ${
|
||||||
interests.includes(topic)
|
interests.includes(topic)
|
||||||
? 'bg-[#3b82f6] border-[#3b82f6] text-white'
|
? 'bg-[#E67E22] border-[#E67E22] text-white'
|
||||||
: 'bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#3b82f6] hover:text-[#3b82f6]'
|
: 'bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#E67E22] hover:text-[#E67E22]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{topic}
|
{topic}
|
||||||
@@ -142,7 +142,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleApplyTopics}
|
onClick={handleApplyTopics}
|
||||||
className="text-[10px] font-body bg-[#3b82f6] text-white px-3 py-1 rounded-sm hover:bg-[#2563eb] transition-colors"
|
className="text-[10px] font-body bg-[#E67E22] text-white px-3 py-1 rounded-sm hover:bg-[#C8651B] transition-colors"
|
||||||
>
|
>
|
||||||
Update Suggestions
|
Update Suggestions
|
||||||
</button>
|
</button>
|
||||||
@@ -180,10 +180,10 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-[#3b82f6] font-body text-[9px] font-bold uppercase tracking-wider mb-0.5">
|
<p className="text-[#E67E22] font-body text-[9px] font-bold uppercase tracking-wider mb-0.5">
|
||||||
{article.category}
|
{article.category}
|
||||||
</p>
|
</p>
|
||||||
<p className="font-heading text-[#e0e0e0] text-xs font-semibold leading-snug line-clamp-2 group-hover:text-[#3b82f6] transition-colors">
|
<p className="font-heading text-[#e0e0e0] text-xs font-semibold leading-snug line-clamp-2 group-hover:text-[#E67E22] transition-colors">
|
||||||
{article.title}
|
{article.title}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -203,7 +203,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<svg className="w-4 h-4 text-[#3b82f6]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4 text-[#E67E22]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.347.346A3.5 3.5 0 0114.5 20h-5a3.5 3.5 0 01-2.47-1.024l-.347-.346z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.347.346A3.5 3.5 0 0114.5 20h-5a3.5 3.5 0 01-2.47-1.024l-.347-.346z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="section-label">AI Picks For You</span>
|
<span className="section-label">AI Picks For You</span>
|
||||||
@@ -212,7 +212,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTopicPicker(!showTopicPicker)}
|
onClick={() => setShowTopicPicker(!showTopicPicker)}
|
||||||
className="text-[11px] font-body text-[#555] hover:text-[#3b82f6] transition-colors flex items-center gap-1 ml-4 shrink-0"
|
className="text-[11px] font-body text-[#555] hover:text-[#E67E22] transition-colors flex items-center gap-1 ml-4 shrink-0"
|
||||||
>
|
>
|
||||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
|
||||||
@@ -232,8 +232,8 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
onClick={() => toggleInterest(topic)}
|
onClick={() => toggleInterest(topic)}
|
||||||
className={`px-2.5 py-1 text-[11px] font-body rounded-sm border transition-colors ${
|
className={`px-2.5 py-1 text-[11px] font-body rounded-sm border transition-colors ${
|
||||||
interests.includes(topic)
|
interests.includes(topic)
|
||||||
? 'bg-[#3b82f6] border-[#3b82f6] text-white'
|
? 'bg-[#E67E22] border-[#E67E22] text-white'
|
||||||
: 'bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#3b82f6] hover:text-[#3b82f6]'
|
: 'bg-[#1a1a1a] border-[#2a2a2a] text-[#888] hover:border-[#E67E22] hover:text-[#E67E22]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{topic}
|
{topic}
|
||||||
@@ -242,7 +242,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleApplyTopics}
|
onClick={handleApplyTopics}
|
||||||
className="text-xs font-body bg-[#3b82f6] text-white px-4 py-1.5 rounded-sm hover:bg-[#2563eb] transition-colors"
|
className="text-xs font-body bg-[#E67E22] text-white px-4 py-1.5 rounded-sm hover:bg-[#C8651B] transition-colors"
|
||||||
>
|
>
|
||||||
Update Suggestions
|
Update Suggestions
|
||||||
</button>
|
</button>
|
||||||
@@ -280,11 +280,11 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
|
|||||||
sizes="(max-width: 768px) 50vw, 25vw"
|
sizes="(max-width: 768px) 50vw, 25vw"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
||||||
<span className="absolute bottom-2 left-2 text-[9px] font-body font-bold text-white uppercase tracking-wider bg-[#3b82f6] px-1.5 py-0.5 rounded-sm">
|
<span className="absolute bottom-2 left-2 text-[9px] font-body font-bold text-white uppercase tracking-wider bg-[#E67E22] px-1.5 py-0.5 rounded-sm">
|
||||||
{article.category}
|
{article.category}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-heading text-[#e0e0e0] text-sm font-semibold leading-snug line-clamp-2 group-hover:text-[#3b82f6] transition-colors">
|
<h3 className="font-heading text-[#e0e0e0] text-sm font-semibold leading-snug line-clamp-2 group-hover:text-[#E67E22] transition-colors">
|
||||||
{article.title}
|
{article.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-[#555] font-body text-[11px] mt-1">{article.readTime}</p>
|
<p className="text-[#555] font-body text-[11px] mt-1">{article.readTime}</p>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function AboutDropdown() {
|
|||||||
onClick={() => setOpen((v) => !v)}
|
onClick={() => setOpen((v) => !v)}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className="bb-browse-item gap-1 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]"
|
className="bb-browse-item gap-1 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]"
|
||||||
>
|
>
|
||||||
<span>ABOUT</span>
|
<span>ABOUT</span>
|
||||||
<span aria-hidden="true" className="text-[10px]">▾</span>
|
<span aria-hidden="true" className="text-[10px]">▾</span>
|
||||||
@@ -54,7 +54,7 @@ export default function AboutDropdown() {
|
|||||||
href={it.href}
|
href={it.href}
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
className="flex items-center justify-between px-4 py-2 text-sm hover:bg-[#1a1a1a] hover:text-[var(--color-text-info,#60a5fa)] focus:outline-none focus-visible:bg-[#1a1a1a] transition-colors"
|
className="flex items-center justify-between px-4 py-2 text-sm hover:bg-[#1a1a1a] hover:text-[var(--color-text-info,#E67E22)] focus:outline-none focus-visible:bg-[#1a1a1a] transition-colors"
|
||||||
>
|
>
|
||||||
<span>{it.label}</span>
|
<span>{it.label}</span>
|
||||||
<span aria-hidden="true" className="text-[10px] text-[#6b7280]">›</span>
|
<span aria-hidden="true" className="text-[10px] text-[#6b7280]">›</span>
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ export default function ArticleComments({ articleSlug }: Props) {
|
|||||||
{replyTo && (
|
{replyTo && (
|
||||||
<div className="flex items-center justify-between mb-2 text-xs text-[#888]">
|
<div className="flex items-center justify-between mb-2 text-xs text-[#888]">
|
||||||
<span>Replying to comment…</span>
|
<span>Replying to comment…</span>
|
||||||
<button onClick={() => setReplyTo(null)} className="text-[#3b82f6] hover:underline">Cancel reply</button>
|
<button onClick={() => setReplyTo(null)} className="text-[#E67E22] hover:underline">Cancel reply</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<textarea
|
<textarea
|
||||||
@@ -171,7 +171,7 @@ export default function ArticleComments({ articleSlug }: Props) {
|
|||||||
onChange={(e) => setBody(e.target.value)}
|
onChange={(e) => setBody(e.target.value)}
|
||||||
placeholder={replyTo ? "Write your reply…" : "Share your take on this story…"}
|
placeholder={replyTo ? "Write your reply…" : "Share your take on this story…"}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full bg-[#111] border border-[#252525] rounded text-white text-sm font-body p-2 resize-none focus:outline-none focus:border-[#3b82f6]"
|
className="w-full bg-[#111] border border-[#252525] rounded text-white text-sm font-body p-2 resize-none focus:outline-none focus:border-[#E67E22]"
|
||||||
/>
|
/>
|
||||||
{error && <p className="text-[11px] text-red-400 mt-1">{error}</p>}
|
{error && <p className="text-[11px] text-red-400 mt-1">{error}</p>}
|
||||||
<div className="flex items-center justify-between mt-2">
|
<div className="flex items-center justify-between mt-2">
|
||||||
@@ -179,7 +179,7 @@ export default function ArticleComments({ articleSlug }: Props) {
|
|||||||
<button
|
<button
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
disabled={submitting || !body.trim()}
|
disabled={submitting || !body.trim()}
|
||||||
className="bg-[#3b82f6] hover:bg-[#2563eb] disabled:opacity-50 text-white font-body text-xs font-semibold px-4 py-1.5 rounded transition-colors"
|
className="bg-[#E67E22] hover:bg-[#C8651B] disabled:opacity-50 text-white font-body text-xs font-semibold px-4 py-1.5 rounded transition-colors"
|
||||||
>
|
>
|
||||||
{submitting ? "Posting…" : replyTo ? "Post reply" : "Post comment"}
|
{submitting ? "Posting…" : replyTo ? "Post reply" : "Post comment"}
|
||||||
</button>
|
</button>
|
||||||
@@ -193,13 +193,13 @@ export default function ArticleComments({ articleSlug }: Props) {
|
|||||||
<div className="flex gap-3 justify-center">
|
<div className="flex gap-3 justify-center">
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/login?next=${encodeURIComponent(typeof window !== "undefined" ? window.location.pathname : "")}`}
|
href={`/forum/login?next=${encodeURIComponent(typeof window !== "undefined" ? window.location.pathname : "")}`}
|
||||||
className="bg-[#3b82f6] hover:bg-[#2563eb] text-white font-body text-xs font-semibold px-5 py-2 rounded"
|
className="bg-[#E67E22] hover:bg-[#C8651B] text-white font-body text-xs font-semibold px-5 py-2 rounded"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={`/forum/register?next=${encodeURIComponent(typeof window !== "undefined" ? window.location.pathname : "")}`}
|
href={`/forum/register?next=${encodeURIComponent(typeof window !== "undefined" ? window.location.pathname : "")}`}
|
||||||
className="border border-[#3b82f6] text-[#3b82f6] hover:bg-[#3b82f6]/10 font-body text-xs font-semibold px-5 py-2 rounded"
|
className="border border-[#E67E22] text-[#E67E22] hover:bg-[#E67E22]/10 font-body text-xs font-semibold px-5 py-2 rounded"
|
||||||
>
|
>
|
||||||
Create account
|
Create account
|
||||||
</Link>
|
</Link>
|
||||||
@@ -250,14 +250,14 @@ function CommentItem({
|
|||||||
/* eslint-disable-next-line @next/next/no-img-element */
|
/* eslint-disable-next-line @next/next/no-img-element */
|
||||||
<img src={comment.author_avatar_url} alt="" className="w-7 h-7 rounded-full bg-[#1a1a1a] object-cover flex-shrink-0" />
|
<img src={comment.author_avatar_url} alt="" className="w-7 h-7 rounded-full bg-[#1a1a1a] object-cover flex-shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-7 h-7 rounded-full bg-[#1a2535] text-[#3b82f6] text-xs font-bold flex items-center justify-center flex-shrink-0">
|
<div className="w-7 h-7 rounded-full bg-[#1a2535] text-[#E67E22] text-xs font-bold flex items-center justify-center flex-shrink-0">
|
||||||
{(comment.author_display_name || "?").trim().charAt(0).toUpperCase()}
|
{(comment.author_display_name || "?").trim().charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="text-sm font-semibold text-white">
|
<div className="text-sm font-semibold text-white">
|
||||||
{comment.author_display_name || "Member"}
|
{comment.author_display_name || "Member"}
|
||||||
{comment.is_ai_seeded && (
|
{comment.is_ai_seeded && (
|
||||||
<span className="ml-1.5 text-[9px] uppercase tracking-widest bg-[#1e3a5f] text-[#60a5fa] px-1.5 py-0.5 rounded">AI</span>
|
<span className="ml-1.5 text-[9px] uppercase tracking-widest bg-[#1e3a5f] text-[#E67E22] px-1.5 py-0.5 rounded">AI</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[11px] text-[#666]">· {timeAgo(comment.created_at)}</span>
|
<span className="text-[11px] text-[#666]">· {timeAgo(comment.created_at)}</span>
|
||||||
@@ -288,7 +288,7 @@ function CommentItem({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{canInteract && (
|
{canInteract && (
|
||||||
<button onClick={() => onReply(comment.id)} className="text-[#3b82f6] hover:underline text-[11px]">
|
<button onClick={() => onReply(comment.id)} className="text-[#E67E22] hover:underline text-[11px]">
|
||||||
Reply
|
Reply
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function AskBBAI() {
|
|||||||
{
|
{
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content:
|
content:
|
||||||
"Hi — I'm BB AI. Ask me anything about broadcast, production, or post technology. I'll cite sources from the Broadcast Beat archive.",
|
"Hi — I'm BB AI. Ask me anything about broadcast, production, or post technology. I'll cite sources from the AV Beat archive.",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const [err, setErr] = useState<string | null>(null);
|
const [err, setErr] = useState<string | null>(null);
|
||||||
@@ -71,7 +71,7 @@ export default function AskBBAI() {
|
|||||||
aria-label="Ask BB AI"
|
aria-label="Ask BB AI"
|
||||||
>
|
>
|
||||||
<span className="relative inline-block w-3 h-3">
|
<span className="relative inline-block w-3 h-3">
|
||||||
<span className="absolute inset-0 rounded-full bg-[var(--color-text-info,#60a5fa)]" />
|
<span className="absolute inset-0 rounded-full bg-[var(--color-text-info,#E67E22)]" />
|
||||||
<span className="bb-ring" />
|
<span className="bb-ring" />
|
||||||
</span>
|
</span>
|
||||||
<span className="font-serif">Ask BB AI</span>
|
<span className="font-serif">Ask BB AI</span>
|
||||||
@@ -88,7 +88,7 @@ export default function AskBBAI() {
|
|||||||
<header className="flex items-center justify-between px-5 py-4 border-b border-[#252525]">
|
<header className="flex items-center justify-between px-5 py-4 border-b border-[#252525]">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="relative inline-block w-3 h-3">
|
<span className="relative inline-block w-3 h-3">
|
||||||
<span className="absolute inset-0 rounded-full bg-[var(--color-text-info,#60a5fa)]" />
|
<span className="absolute inset-0 rounded-full bg-[var(--color-text-info,#E67E22)]" />
|
||||||
<span className="bb-ring" />
|
<span className="bb-ring" />
|
||||||
</span>
|
</span>
|
||||||
<h2 className="font-serif text-lg font-semibold text-white">BB AI</h2>
|
<h2 className="font-serif text-lg font-semibold text-white">BB AI</h2>
|
||||||
@@ -109,7 +109,7 @@ export default function AskBBAI() {
|
|||||||
className={
|
className={
|
||||||
"inline-block max-w-[85%] rounded px-3 py-2 text-sm whitespace-pre-wrap " +
|
"inline-block max-w-[85%] rounded px-3 py-2 text-sm whitespace-pre-wrap " +
|
||||||
(m.role === "user"
|
(m.role === "user"
|
||||||
? "bg-[var(--color-text-info,#60a5fa)] text-black"
|
? "bg-[var(--color-text-info,#E67E22)] text-black"
|
||||||
: "bg-[#111] border border-[#252525] text-[#e5e7eb]")
|
: "bg-[#111] border border-[#252525] text-[#e5e7eb]")
|
||||||
}
|
}
|
||||||
dangerouslySetInnerHTML={{ __html: m.role === "assistant" ? linkifyMarkdown(m.content) : escapeHtml(m.content) }}
|
dangerouslySetInnerHTML={{ __html: m.role === "assistant" ? linkifyMarkdown(m.content) : escapeHtml(m.content) }}
|
||||||
@@ -133,18 +133,18 @@ export default function AskBBAI() {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && send()}
|
onKeyDown={(e) => e.key === "Enter" && send()}
|
||||||
placeholder="Ask about an event, a vendor, a workflow…"
|
placeholder="Ask about an event, a vendor, a workflow…"
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="flex-1 bg-[#111] border border-[#252525] rounded px-3 py-2 text-sm text-[#e5e7eb] focus:border-[var(--color-text-info,#60a5fa)] focus:outline-none"
|
className="flex-1 bg-[#111] border border-[#252525] rounded px-3 py-2 text-sm text-[#e5e7eb] focus:border-[var(--color-text-info,#E67E22)] focus:outline-none"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={send}
|
onClick={send}
|
||||||
disabled={busy || !input.trim()}
|
disabled={busy || !input.trim()}
|
||||||
className="bg-[var(--color-text-info,#60a5fa)] text-black font-semibold px-4 py-2 rounded text-sm disabled:opacity-40 disabled:cursor-not-allowed"
|
className="bg-[var(--color-text-info,#E67E22)] text-black font-semibold px-4 py-2 rounded text-sm disabled:opacity-40 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] font-mono text-[#6b7280] mt-2">
|
<p className="text-[10px] font-mono text-[#6b7280] mt-2">
|
||||||
30 messages/hour · cites Broadcast Beat archive
|
30 messages/hour · cites AV Beat archive
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,7 +161,7 @@ function linkifyMarkdown(s: string): string {
|
|||||||
let out = escapeHtml(s);
|
let out = escapeHtml(s);
|
||||||
out = out.replace(
|
out = out.replace(
|
||||||
/\[([^\]]+)\]\(([^)]+)\)/g,
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
||||||
'<a href="$2" class="text-[#60a5fa] hover:underline">$1</a>'
|
'<a href="$2" class="text-[#E67E22] hover:underline">$1</a>'
|
||||||
);
|
);
|
||||||
out = out.replace(/\n/g, "<br/>");
|
out = out.replace(/\n/g, "<br/>");
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default async function CompaniesInThisStory({ slugs }: { slugs: string[]
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#111] border border-[#222] p-5">
|
<div className="bg-[#111] border border-[#222] p-5">
|
||||||
<h3 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest mb-4 pb-2 border-b border-[#222]">
|
<h3 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest mb-4 pb-2 border-b border-[#222]">
|
||||||
Companies in this story
|
Companies in this story
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
@@ -83,7 +83,7 @@ export default async function CompaniesInThisStory({ slugs }: { slugs: string[]
|
|||||||
)}
|
)}
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-1 flex-wrap">
|
<div className="flex items-center gap-1 flex-wrap">
|
||||||
<span className="font-heading text-[#e0e0e0] text-sm font-bold group-hover:text-[#3b82f6] transition-colors truncate">
|
<span className="font-heading text-[#e0e0e0] text-sm font-bold group-hover:text-[#E67E22] transition-colors truncate">
|
||||||
{t.name}
|
{t.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -112,12 +112,12 @@ export default function CompanyMentionsHover() {
|
|||||||
/* eslint-disable-next-line @next/next/no-img-element */
|
/* eslint-disable-next-line @next/next/no-img-element */
|
||||||
<img src={preview.logoUrl} alt="" className="w-12 h-12 rounded bg-white p-1 object-contain flex-shrink-0" loading="lazy" />
|
<img src={preview.logoUrl} alt="" className="w-12 h-12 rounded bg-white p-1 object-contain flex-shrink-0" loading="lazy" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-12 h-12 rounded bg-[#1a2535] flex items-center justify-center text-[#3b82f6] font-bold flex-shrink-0">
|
<div className="w-12 h-12 rounded bg-[#1a2535] flex items-center justify-center text-[#E67E22] font-bold flex-shrink-0">
|
||||||
{(preview.name || "?").trim().charAt(0).toUpperCase()}
|
{(preview.name || "?").trim().charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<Link href={`/manufacturers/${preview.slug}`} className="font-semibold text-white hover:text-[#3b82f6] block truncate">
|
<Link href={`/manufacturers/${preview.slug}`} className="font-semibold text-white hover:text-[#E67E22] block truncate">
|
||||||
{preview.name}
|
{preview.name}
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex items-center gap-1.5 mt-1 flex-wrap text-[10px]">
|
<div className="flex items-center gap-1.5 mt-1 flex-wrap text-[10px]">
|
||||||
@@ -145,7 +145,7 @@ export default function CompanyMentionsHover() {
|
|||||||
<ul className="space-y-1">
|
<ul className="space-y-1">
|
||||||
{preview.stories.slice(0, 3).map((s) => (
|
{preview.stories.slice(0, 3).map((s) => (
|
||||||
<li key={s.slug}>
|
<li key={s.slug}>
|
||||||
<Link href={`/news/${s.slug}`} className="text-xs text-[#60a5fa] hover:text-[#93c5fd] hover:underline line-clamp-2">
|
<Link href={`/news/${s.slug}`} className="text-xs text-[#E67E22] hover:text-[#93c5fd] hover:underline line-clamp-2">
|
||||||
{s.title}
|
{s.title}
|
||||||
</Link>
|
</Link>
|
||||||
{s.date && <span className="text-[10px] text-[#666] ml-1">· {new Date(s.date).toISOString().slice(0, 10)}</span>}
|
{s.date && <span className="text-[10px] text-[#666] ml-1">· {new Date(s.date).toISOString().slice(0, 10)}</span>}
|
||||||
@@ -156,11 +156,11 @@ export default function CompanyMentionsHover() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="border-t border-[#2a3a50] mt-3 pt-2 flex items-center justify-between">
|
<div className="border-t border-[#2a3a50] mt-3 pt-2 flex items-center justify-between">
|
||||||
<Link href={`/manufacturers/${preview.slug}`} className="text-xs font-semibold text-[#60a5fa] hover:text-[#93c5fd] hover:underline">
|
<Link href={`/manufacturers/${preview.slug}`} className="text-xs font-semibold text-[#E67E22] hover:text-[#93c5fd] hover:underline">
|
||||||
Full profile →
|
Full profile →
|
||||||
</Link>
|
</Link>
|
||||||
{preview.website && (
|
{preview.website && (
|
||||||
<a href={preview.website} target="_blank" rel="noopener noreferrer" className="text-[10px] text-[#888] hover:text-[#3b82f6]">
|
<a href={preview.website} target="_blank" rel="noopener noreferrer" className="text-[10px] text-[#888] hover:text-[#E67E22]">
|
||||||
{preview.website.replace(/^https?:\/\//, "").replace(/\/$/, "")} ↗
|
{preview.website.replace(/^https?:\/\//, "").replace(/\/$/, "")} ↗
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -72,11 +72,11 @@ export default function CookieConsent() {
|
|||||||
id="cookie-consent-desc"
|
id="cookie-consent-desc"
|
||||||
className="text-[#aaa] text-sm font-body leading-relaxed"
|
className="text-[#aaa] text-sm font-body leading-relaxed"
|
||||||
>
|
>
|
||||||
<span className="font-bold text-white">Broadcast Beat</span> uses cookies and similar technologies to improve your experience, analyze site traffic, and serve relevant advertising. By clicking{" "}
|
<span className="font-bold text-white">AV Beat</span> uses cookies and similar technologies to improve your experience, analyze site traffic, and serve relevant advertising. By clicking{" "}
|
||||||
<strong className="text-white">Accept All</strong>, you consent to our use of cookies.{" "}
|
<strong className="text-white">Accept All</strong>, you consent to our use of cookies.{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/privacy"
|
href="/privacy"
|
||||||
className="text-[#3b82f6] underline hover:text-blue-400 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors"
|
className="text-[#E67E22] underline hover:text-blue-400 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors"
|
||||||
>
|
>
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
</Link>
|
</Link>
|
||||||
@@ -86,7 +86,7 @@ export default function CookieConsent() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleDecline}
|
onClick={handleDecline}
|
||||||
className="px-4 py-2 text-sm font-body font-bold text-[#888] border border-[#333] rounded hover:border-[#555] hover:text-[#aaa] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] focus-visible:ring-offset-1 focus-visible:ring-offset-[#0d1117]"
|
className="px-4 py-2 text-sm font-body font-bold text-[#888] border border-[#333] rounded hover:border-[#555] hover:text-[#aaa] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] focus-visible:ring-offset-1 focus-visible:ring-offset-[#0d1117]"
|
||||||
aria-label="Decline non-essential cookies"
|
aria-label="Decline non-essential cookies"
|
||||||
>
|
>
|
||||||
Decline
|
Decline
|
||||||
@@ -95,7 +95,7 @@ export default function CookieConsent() {
|
|||||||
ref={acceptRef}
|
ref={acceptRef}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAccept}
|
onClick={handleAccept}
|
||||||
className="px-5 py-2 text-sm font-body font-bold text-white bg-[#3b82f6] rounded hover:bg-blue-500 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] focus-visible:ring-offset-2 focus-visible:ring-offset-[#0d1117]"
|
className="px-5 py-2 text-sm font-body font-bold text-white bg-[#E67E22] rounded hover:bg-blue-500 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] focus-visible:ring-offset-2 focus-visible:ring-offset-[#0d1117]"
|
||||||
aria-label="Accept all cookies"
|
aria-label="Accept all cookies"
|
||||||
>
|
>
|
||||||
Accept All
|
Accept All
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export default function EventsDropdown() {
|
|||||||
onClick={() => setOpen((v) => !v)}
|
onClick={() => setOpen((v) => !v)}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className="bb-browse-item gap-1 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]"
|
className="bb-browse-item gap-1 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]"
|
||||||
>
|
>
|
||||||
<span>Show coverage</span>
|
<span>Show coverage</span>
|
||||||
<span aria-hidden="true" className="text-[10px]">▾</span>
|
<span aria-hidden="true" className="text-[10px]">▾</span>
|
||||||
@@ -113,12 +113,12 @@ export default function EventsDropdown() {
|
|||||||
style={{ borderRadius: "var(--border-radius-md,6px)" }}
|
style={{ borderRadius: "var(--border-radius-md,6px)" }}
|
||||||
>
|
>
|
||||||
<div className="relative p-4">
|
<div className="relative p-4">
|
||||||
<span aria-hidden className="absolute top-2 left-2 w-3 h-3 border-t border-l border-[var(--color-text-info,#60a5fa)]"></span>
|
<span aria-hidden className="absolute top-2 left-2 w-3 h-3 border-t border-l border-[var(--color-text-info,#E67E22)]"></span>
|
||||||
<span aria-hidden className="absolute bottom-2 right-2 w-3 h-3 border-b border-r border-[var(--color-text-info,#60a5fa)]"></span>
|
<span aria-hidden className="absolute bottom-2 right-2 w-3 h-3 border-b border-r border-[var(--color-text-info,#E67E22)]"></span>
|
||||||
|
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<h3 className="font-serif text-base font-semibold">Show coverage</h3>
|
<h3 className="font-serif text-base font-semibold">Show coverage</h3>
|
||||||
<span className="inline-flex items-center gap-1 text-[10px] font-mono uppercase tracking-wider text-[var(--color-text-info,#60a5fa)]">
|
<span className="inline-flex items-center gap-1 text-[10px] font-mono uppercase tracking-wider text-[var(--color-text-info,#E67E22)]">
|
||||||
<span className="bb-pulse-dot" />
|
<span className="bb-pulse-dot" />
|
||||||
BB AI
|
BB AI
|
||||||
</span>
|
</span>
|
||||||
@@ -158,7 +158,7 @@ export default function EventsDropdown() {
|
|||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
<polyline points="12 6 12 12 16 14" />
|
<polyline points="12 6 12 12 16 14" />
|
||||||
</svg>
|
</svg>
|
||||||
<span className="text-[var(--color-text-info,#60a5fa)]">T-{e.days_until}d</span>
|
<span className="text-[var(--color-text-info,#E67E22)]">T-{e.days_until}d</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -168,7 +168,7 @@ export default function EventsDropdown() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-[10px] font-mono text-[#6b7280] mt-1">
|
<div className="flex items-center gap-2 text-[10px] font-mono text-[#6b7280] mt-1">
|
||||||
<span>{fmtDateRange(e.start_date, e.end_date)}</span>
|
<span>{fmtDateRange(e.start_date, e.end_date)}</span>
|
||||||
{host && <span className="text-[var(--color-text-info,#60a5fa)]">· {host}</span>}
|
{host && <span className="text-[var(--color-text-info,#E67E22)]">· {host}</span>}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@@ -177,7 +177,7 @@ export default function EventsDropdown() {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="mt-3 pt-2 border-t border-[#252525] flex items-center justify-between text-[10px] font-mono text-[#6b7280]">
|
<div className="mt-3 pt-2 border-t border-[#252525] flex items-center justify-between text-[10px] font-mono text-[#6b7280]">
|
||||||
<Link href="/show-coverage" className="text-[var(--color-text-info,#60a5fa)] hover:underline" onClick={() => setOpen(false)}>
|
<Link href="/show-coverage" className="text-[var(--color-text-info,#E67E22)] hover:underline" onClick={() => setOpen(false)}>
|
||||||
See all 13 events →
|
See all 13 events →
|
||||||
</Link>
|
</Link>
|
||||||
{loadedAt && <span>auto-updated · {ago(loadedAt)}</span>}
|
{loadedAt && <span>auto-updated · {ago(loadedAt)}</span>}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default async function FeaturedBento() {
|
|||||||
<div className="flex items-center gap-3 mb-4 md:mb-5 flex-wrap">
|
<div className="flex items-center gap-3 mb-4 md:mb-5 flex-wrap">
|
||||||
<span className="section-label">Staff Editorial</span>
|
<span className="section-label">Staff Editorial</span>
|
||||||
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
<div className="flex-1 h-px bg-[#2a2a2a]" />
|
||||||
<Link href="/news/featured" className="text-[11px] font-mono text-[var(--color-text-info,#60a5fa)] hover:underline whitespace-nowrap">
|
<Link href="/news/featured" className="text-[11px] font-mono text-[var(--color-text-info,#E67E22)] hover:underline whitespace-nowrap">
|
||||||
All featured →
|
All featured →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,7 +89,7 @@ export default async function FeaturedBento() {
|
|||||||
{/* Cinematic 21:9 hero — fills left column, aspect ratio preserved */}
|
{/* Cinematic 21:9 hero — fills left column, aspect ratio preserved */}
|
||||||
<Link
|
<Link
|
||||||
href={`/news/${hero.wp_slug}`}
|
href={`/news/${hero.wp_slug}`}
|
||||||
className="group block relative overflow-hidden rounded border border-[#252525] bg-[#0b0f17] hover:border-[var(--color-text-info,#60a5fa)] transition-colors"
|
className="group block relative overflow-hidden rounded border border-[#252525] bg-[#0b0f17] hover:border-[var(--color-text-info,#E67E22)] transition-colors"
|
||||||
>
|
>
|
||||||
<div className="relative aspect-[21/9] md:aspect-[21/8] bg-[#111]">
|
<div className="relative aspect-[21/9] md:aspect-[21/8] bg-[#111]">
|
||||||
<img
|
<img
|
||||||
@@ -99,7 +99,7 @@ export default async function FeaturedBento() {
|
|||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/65 to-transparent" />
|
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/65 to-transparent" />
|
||||||
|
|
||||||
<span className="absolute top-4 left-4 inline-flex items-center gap-1.5 bg-[var(--color-text-info,#60a5fa)] text-black px-3 py-1 rounded font-mono text-[11px] uppercase tracking-widest font-bold shadow-md">
|
<span className="absolute top-4 left-4 inline-flex items-center gap-1.5 bg-[var(--color-text-info,#E67E22)] text-black px-3 py-1 rounded font-mono text-[11px] uppercase tracking-widest font-bold shadow-md">
|
||||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||||
<path d="M12 2l2.6 7.6H22l-6.2 4.5L18.4 22 12 17.3 5.6 22l2.6-7.9L2 9.6h7.4z" />
|
<path d="M12 2l2.6 7.6H22l-6.2 4.5L18.4 22 12 17.3 5.6 22l2.6-7.9L2 9.6h7.4z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -108,11 +108,11 @@ export default async function FeaturedBento() {
|
|||||||
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 p-6 md:p-10">
|
<div className="absolute bottom-0 left-0 right-0 p-6 md:p-10">
|
||||||
{hero.category && (
|
{hero.category && (
|
||||||
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--color-text-info,#60a5fa)] mb-2">
|
<div className="text-[10px] font-mono uppercase tracking-widest text-[var(--color-text-info,#E67E22)] mb-2">
|
||||||
{hero.category}
|
{hero.category}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h3 className="font-serif text-2xl md:text-4xl lg:text-5xl text-white leading-tight max-w-3xl group-hover:text-[var(--color-text-info,#60a5fa)] transition-colors">
|
<h3 className="font-serif text-2xl md:text-4xl lg:text-5xl text-white leading-tight max-w-3xl group-hover:text-[var(--color-text-info,#E67E22)] transition-colors">
|
||||||
{hero.title}
|
{hero.title}
|
||||||
</h3>
|
</h3>
|
||||||
{hero.excerpt && (
|
{hero.excerpt && (
|
||||||
@@ -121,9 +121,9 @@ export default async function FeaturedBento() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="mt-4 flex flex-wrap items-center gap-3 text-[11px] font-mono text-[#9ca3af]">
|
<div className="mt-4 flex flex-wrap items-center gap-3 text-[11px] font-mono text-[#9ca3af]">
|
||||||
<span>{hero.author_name || "Broadcast Beat"}</span>
|
<span>{hero.author_name || "AV Beat"}</span>
|
||||||
<StarRating seed={hero.wp_slug} publishedAt={hero.wp_published_at} size="sm" />
|
<StarRating seed={hero.wp_slug} publishedAt={hero.wp_published_at} size="sm" />
|
||||||
<span aria-hidden="true" className="text-[var(--color-text-info,#60a5fa)] ml-2">→ Read full story</span>
|
<span aria-hidden="true" className="text-[var(--color-text-info,#E67E22)] ml-2">→ Read full story</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +140,7 @@ export default async function FeaturedBento() {
|
|||||||
<Link
|
<Link
|
||||||
key={r.wp_slug}
|
key={r.wp_slug}
|
||||||
href={`/news/${r.wp_slug}`}
|
href={`/news/${r.wp_slug}`}
|
||||||
className="group flex flex-col h-full rounded border border-[#252525] bg-[#0b0f17] overflow-hidden hover:border-[var(--color-text-info,#60a5fa)] transition-colors"
|
className="group flex flex-col h-full rounded border border-[#252525] bg-[#0b0f17] overflow-hidden hover:border-[var(--color-text-info,#E67E22)] transition-colors"
|
||||||
>
|
>
|
||||||
<div className="aspect-[16/10] bg-[#111] relative flex-shrink-0">
|
<div className="aspect-[16/10] bg-[#111] relative flex-shrink-0">
|
||||||
<img
|
<img
|
||||||
@@ -150,10 +150,10 @@ export default async function FeaturedBento() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 flex flex-col gap-1 flex-1 min-h-0 overflow-hidden">
|
<div className="p-3 flex flex-col gap-1 flex-1 min-h-0 overflow-hidden">
|
||||||
<div className="text-[9px] font-mono uppercase tracking-wider text-[var(--color-text-info,#60a5fa)]">
|
<div className="text-[9px] font-mono uppercase tracking-wider text-[var(--color-text-info,#E67E22)]">
|
||||||
{r.category || "News"}
|
{r.category || "News"}
|
||||||
</div>
|
</div>
|
||||||
<h4 className="font-serif text-sm leading-tight text-[#e5e7eb] group-hover:text-[var(--color-text-info,#60a5fa)] line-clamp-2">
|
<h4 className="font-serif text-sm leading-tight text-[#e5e7eb] group-hover:text-[var(--color-text-info,#E67E22)] line-clamp-2">
|
||||||
{r.title}
|
{r.title}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="mt-auto">
|
<div className="mt-auto">
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default async function FeaturedCarouselServer() {
|
|||||||
|
|
||||||
for (const r of (ai.data || []) as any[]) {
|
for (const r of (ai.data || []) as any[]) {
|
||||||
const persona = Array.isArray(r.persona) ? r.persona[0] : r.persona;
|
const persona = Array.isArray(r.persona) ? r.persona[0] : r.persona;
|
||||||
const author = persona?.name || "Broadcast Beat";
|
const author = persona?.name || "AV Beat";
|
||||||
slides.push({
|
slides.push({
|
||||||
id: r.id,
|
id: r.id,
|
||||||
slug: r.slug,
|
slug: r.slug,
|
||||||
@@ -74,7 +74,7 @@ export default async function FeaturedCarouselServer() {
|
|||||||
|
|
||||||
for (const r of (wp.data || []) as any[]) {
|
for (const r of (wp.data || []) as any[]) {
|
||||||
const image = r.featured_image ? rewriteLegacyImageUrl(r.featured_image) : null;
|
const image = r.featured_image ? rewriteLegacyImageUrl(r.featured_image) : null;
|
||||||
const author = r.author_name || "Broadcast Beat";
|
const author = r.author_name || "AV Beat";
|
||||||
slides.push({
|
slides.push({
|
||||||
id: String(r.wp_id),
|
id: String(r.wp_id),
|
||||||
slug: r.wp_slug,
|
slug: r.wp_slug,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const footerColumns = [
|
|||||||
{
|
{
|
||||||
heading: "About",
|
heading: "About",
|
||||||
links: [
|
links: [
|
||||||
{ label: "About Broadcast Beat", href: "/about" },
|
{ label: "About AV Beat", href: "/about" },
|
||||||
{ label: "Our Mission", href: "/about#mission" },
|
{ label: "Our Mission", href: "/about#mission" },
|
||||||
{ label: "Editorial Team", href: "/about#team" },
|
{ label: "Editorial Team", href: "/about#team" },
|
||||||
{ label: "Advertise With Us", href: "/advertise" },
|
{ label: "Advertise With Us", href: "/advertise" },
|
||||||
@@ -50,11 +50,11 @@ const footerColumns = [
|
|||||||
{
|
{
|
||||||
heading: "Follow Us",
|
heading: "Follow Us",
|
||||||
links: [
|
links: [
|
||||||
{ label: "LinkedIn", href: "https://linkedin.com/company/broadcastbeat" },
|
{ label: "LinkedIn", href: "https://linkedin.com/company/avbeat" },
|
||||||
{ label: "Twitter / X", href: "https://twitter.com/broadcastbeat" },
|
{ label: "Twitter / X", href: "https://twitter.com/avbeat" },
|
||||||
{ label: "Facebook", href: "https://facebook.com/broadcastbeat" },
|
{ label: "Facebook", href: "https://facebook.com/avbeat" },
|
||||||
{ label: "Instagram", href: "https://instagram.com/broadcastbeat" },
|
{ label: "Instagram", href: "https://instagram.com/avbeat" },
|
||||||
{ label: "YouTube", href: "https://youtube.com/@broadcastbeat" },
|
{ label: "YouTube", href: "https://youtube.com/@avbeat" },
|
||||||
{ label: "RSS Feed", href: "/rss" }]
|
{ label: "RSS Feed", href: "/rss" }]
|
||||||
|
|
||||||
}];
|
}];
|
||||||
@@ -75,7 +75,7 @@ export default function Footer() {
|
|||||||
<div className="border-t border-[#222] pt-8 pb-6 grid grid-cols-2 md:grid-cols-4 gap-8">
|
<div className="border-t border-[#222] pt-8 pb-6 grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||||
{footerColumns?.map((col) =>
|
{footerColumns?.map((col) =>
|
||||||
<div key={col?.heading}>
|
<div key={col?.heading}>
|
||||||
<h4 className="font-body font-bold text-xs text-[#3b82f6] uppercase tracking-widest mb-4 pb-2 border-b border-[#3b82f6]/20">
|
<h4 className="font-body font-bold text-xs text-[#E67E22] uppercase tracking-widest mb-4 pb-2 border-b border-[#E67E22]/20">
|
||||||
{col?.heading}
|
{col?.heading}
|
||||||
</h4>
|
</h4>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
@@ -86,13 +86,13 @@ export default function Footer() {
|
|||||||
href={link?.href}
|
href={link?.href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="footer-link font-body text-sm text-[#777777] hover:text-[#3b82f6] transition-colors leading-relaxed inline-block">
|
className="footer-link font-body text-sm text-[#777777] hover:text-[#E67E22] transition-colors leading-relaxed inline-block">
|
||||||
{link?.label}
|
{link?.label}
|
||||||
</a> :
|
</a> :
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={link?.href}
|
href={link?.href}
|
||||||
className="footer-link font-body text-sm text-[#777777] hover:text-[#3b82f6] transition-colors leading-relaxed inline-block">
|
className="footer-link font-body text-sm text-[#777777] hover:text-[#E67E22] transition-colors leading-relaxed inline-block">
|
||||||
{link?.label}
|
{link?.label}
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
@@ -108,44 +108,44 @@ export default function Footer() {
|
|||||||
<div className="border-t border-[#222] pt-5 pb-3 flex flex-col sm:flex-row items-center justify-between gap-4">
|
<div className="border-t border-[#222] pt-5 pb-3 flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||||
<Link
|
<Link
|
||||||
href="/home-page"
|
href="/home-page"
|
||||||
aria-label="Broadcast Beat — home"
|
aria-label="AV Beat — home"
|
||||||
className="flex-shrink-0 opacity-70 hover:opacity-100 transition-opacity">
|
className="flex-shrink-0 opacity-70 hover:opacity-100 transition-opacity">
|
||||||
<AppImage
|
<AppImage
|
||||||
src="/assets/images/logo.png"
|
src="/assets/images/logo.png"
|
||||||
alt="Broadcast Beat"
|
alt="AV Beat"
|
||||||
width={180}
|
width={180}
|
||||||
height={24}
|
height={42}
|
||||||
className="h-6 w-auto object-contain" />
|
className="h-9 w-auto object-contain" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<a href="https://linkedin.com/company/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on LinkedIn"
|
<a href="https://linkedin.com/company/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on LinkedIn"
|
||||||
className="text-[#555] hover:text-[#3b82f6] transition-colors social-icon">
|
className="text-[#555] hover:text-[#E67E22] transition-colors social-icon">
|
||||||
<LinkedInIcon size={17} />
|
<LinkedInIcon size={17} />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://instagram.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on Instagram"
|
<a href="https://instagram.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on Instagram"
|
||||||
className="text-[#555] hover:text-[#3b82f6] transition-colors social-icon">
|
className="text-[#555] hover:text-[#E67E22] transition-colors social-icon">
|
||||||
<InstagramIcon size={17} />
|
<InstagramIcon size={17} />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://twitter.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on Twitter/X"
|
<a href="https://twitter.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on Twitter/X"
|
||||||
className="text-[#555] hover:text-[#3b82f6] transition-colors social-icon">
|
className="text-[#555] hover:text-[#E67E22] transition-colors social-icon">
|
||||||
<TwitterXIcon size={17} />
|
<TwitterXIcon size={17} />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://facebook.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on Facebook"
|
<a href="https://facebook.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on Facebook"
|
||||||
className="text-[#555] hover:text-[#3b82f6] transition-colors social-icon">
|
className="text-[#555] hover:text-[#E67E22] transition-colors social-icon">
|
||||||
<FacebookIcon size={17} />
|
<FacebookIcon size={17} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-4 text-xs font-body text-[#555]">
|
<div className="flex items-center gap-4 text-xs font-body text-[#555]">
|
||||||
<Link href="/privacy" className="hover:text-[#3b82f6] transition-colors">Privacy Policy</Link>
|
<Link href="/privacy" className="hover:text-[#E67E22] transition-colors">Privacy Policy</Link>
|
||||||
<Link href="/terms" className="hover:text-[#3b82f6] transition-colors">Terms of Service</Link>
|
<Link href="/terms" className="hover:text-[#E67E22] transition-colors">Terms of Service</Link>
|
||||||
<span>
|
<span>
|
||||||
© 2026 <a
|
© 2026 <a
|
||||||
href="https://www.relevantmediaproperties.com/"
|
href="https://www.relevantmediaproperties.com/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="hover:text-[#3b82f6] transition-colors underline-offset-2 hover:underline">
|
className="hover:text-[#E67E22] transition-colors underline-offset-2 hover:underline">
|
||||||
Relevant Media Properties
|
Relevant Media Properties
|
||||||
</a>. All rights reserved.
|
</a>. All rights reserved.
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -261,14 +261,14 @@ export default function Header() {
|
|||||||
<div className="max-w-container mx-auto px-4">
|
<div className="max-w-container mx-auto px-4">
|
||||||
<div className="flex items-center justify-between h-[96px] gap-4">
|
<div className="flex items-center justify-between h-[96px] gap-4">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link href="/home-page" className="flex-shrink-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6] focus-visible:ring-offset-1 focus-visible:ring-offset-[#111]">
|
<Link href="/home-page" className="flex-shrink-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22] focus-visible:ring-offset-1 focus-visible:ring-offset-[#111]">
|
||||||
<AppImage
|
<AppImage
|
||||||
src="/assets/images/logo.png"
|
src="/assets/images/logo.png"
|
||||||
alt="Broadcast Beat logo"
|
alt="AV Beat logo"
|
||||||
width={200}
|
width={200}
|
||||||
height={26}
|
height={47}
|
||||||
priority={true}
|
priority={true}
|
||||||
className="h-7 w-auto object-contain"
|
className="h-10 w-auto object-contain"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ export default function Header() {
|
|||||||
<Link
|
<Link
|
||||||
href="/reading-list"
|
href="/reading-list"
|
||||||
aria-label="Reading List"
|
aria-label="Reading List"
|
||||||
className="flex items-center text-[#888] hover:text-[#3b82f6] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] focus-visible:ring-offset-1 focus-visible:ring-offset-[#111]">
|
className="flex items-center text-[#888] hover:text-[#E67E22] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] focus-visible:ring-offset-1 focus-visible:ring-offset-[#111]">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -296,7 +296,7 @@ export default function Header() {
|
|||||||
{/* Mobile Hamburger */}
|
{/* Mobile Hamburger */}
|
||||||
<button
|
<button
|
||||||
ref={hamburgerRef}
|
ref={hamburgerRef}
|
||||||
className="lg:hidden p-2 text-[#cccccc] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]"
|
className="lg:hidden p-2 text-[#cccccc] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#E67E22]"
|
||||||
onClick={() => setMobileOpen(!mobileOpen)}
|
onClick={() => setMobileOpen(!mobileOpen)}
|
||||||
aria-label={mobileOpen ? "Close menu" : "Open menu"}
|
aria-label={mobileOpen ? "Close menu" : "Open menu"}
|
||||||
aria-expanded={mobileOpen}
|
aria-expanded={mobileOpen}
|
||||||
@@ -319,7 +319,7 @@ export default function Header() {
|
|||||||
key={link?.label}
|
key={link?.label}
|
||||||
href={link?.href}
|
href={link?.href}
|
||||||
ref={(el) => { navLinksRef.current[i] = el; }}
|
ref={(el) => { navLinksRef.current[i] = el; }}
|
||||||
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-accent hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6]"
|
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-accent hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22]"
|
||||||
onClick={() => setMobileOpen(false)}>
|
onClick={() => setMobileOpen(false)}>
|
||||||
{link?.label}
|
{link?.label}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -327,7 +327,7 @@ export default function Header() {
|
|||||||
{/* Reading List mobile link */}
|
{/* Reading List mobile link */}
|
||||||
<Link
|
<Link
|
||||||
href="/reading-list"
|
href="/reading-list"
|
||||||
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-accent hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6] flex items-center gap-2"
|
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-accent hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22] flex items-center gap-2"
|
||||||
onClick={() => setMobileOpen(false)}>
|
onClick={() => setMobileOpen(false)}>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
|
||||||
@@ -338,7 +338,7 @@ export default function Header() {
|
|||||||
{currentUserId && (
|
{currentUserId && (
|
||||||
<Link
|
<Link
|
||||||
href="/account"
|
href="/account"
|
||||||
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-[#3b82f6] hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6] flex items-center gap-2"
|
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-[#E67E22] hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22] flex items-center gap-2"
|
||||||
onClick={() => setMobileOpen(false)}>
|
onClick={() => setMobileOpen(false)}>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
|
||||||
@@ -350,7 +350,7 @@ export default function Header() {
|
|||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Link
|
<Link
|
||||||
href="/admin/import"
|
href="/admin/import"
|
||||||
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-[#3b82f6] hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6] flex items-center gap-2"
|
className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#cccccc] hover:text-[#E67E22] hover:bg-[#1a1a1a] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22] flex items-center gap-2"
|
||||||
onClick={() => setMobileOpen(false)}>
|
onClick={() => setMobileOpen(false)}>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" />
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="7 10 12 15 17 10" /><line x1="12" y1="15" x2="12" y2="3" />
|
||||||
@@ -364,14 +364,14 @@ export default function Header() {
|
|||||||
.filter((n) => n.dropdown && n.dropdown.length > 0)
|
.filter((n) => n.dropdown && n.dropdown.length > 0)
|
||||||
.map((section) => (
|
.map((section) => (
|
||||||
<div key={section.label} className="border-b border-[#222]">
|
<div key={section.label} className="border-b border-[#222]">
|
||||||
<div className="px-2 py-2 text-[10px] font-body font-bold text-[#3b82f6] uppercase tracking-widest bg-[#0d0d0d]">
|
<div className="px-2 py-2 text-[10px] font-body font-bold text-[#E67E22] uppercase tracking-widest bg-[#0d0d0d]">
|
||||||
{section.label}
|
{section.label}
|
||||||
</div>
|
</div>
|
||||||
{section.dropdown!.map((link) => (
|
{section.dropdown!.map((link) => (
|
||||||
<Link
|
<Link
|
||||||
key={link.href + link.label}
|
key={link.href + link.label}
|
||||||
href={link.href}
|
href={link.href}
|
||||||
className="px-4 py-2 text-xs font-body text-[#888] hover:text-accent hover:bg-[#1a1a1a] block transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6]"
|
className="px-4 py-2 text-xs font-body text-[#888] hover:text-accent hover:bg-[#1a1a1a] block transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22]"
|
||||||
onClick={() => setMobileOpen(false)}>
|
onClick={() => setMobileOpen(false)}>
|
||||||
{link.label}
|
{link.label}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -399,7 +399,7 @@ export default function Header() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={submitSearch}
|
onClick={submitSearch}
|
||||||
aria-label="Submit search"
|
aria-label="Submit search"
|
||||||
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-[#666] hover:text-[#3b82f6] transition-colors cursor-pointer"
|
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-[#666] hover:text-[#E67E22] transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
<SearchIcon size={13} strokeWidth={1.75} />
|
<SearchIcon size={13} strokeWidth={1.75} />
|
||||||
</button>
|
</button>
|
||||||
@@ -418,22 +418,22 @@ export default function Header() {
|
|||||||
<div className="max-w-container mx-auto px-4">
|
<div className="max-w-container mx-auto px-4">
|
||||||
<div className="bg-[#1a2535] border-b border-[#2a3a50] h-8 flex items-center justify-between px-4">
|
<div className="bg-[#1a2535] border-b border-[#2a3a50] h-8 flex items-center justify-between px-4">
|
||||||
<p className="text-[#999999] text-xs font-body hidden sm:block">
|
<p className="text-[#999999] text-xs font-body hidden sm:block">
|
||||||
News & Intelligence for Broadcast, Post-Production & Streaming Technology
|
Where AV Meets IT — Pro AV, Broadcast, Streaming & Post-Production
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-3 ml-auto">
|
<div className="flex items-center gap-3 ml-auto">
|
||||||
<a href="https://linkedin.com/company/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on LinkedIn"
|
<a href="https://linkedin.com/company/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on LinkedIn"
|
||||||
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
|
className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors social-icon">
|
||||||
<LinkedInIcon size={14} />
|
<LinkedInIcon size={14} />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://instagram.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on Instagram"
|
<a href="https://instagram.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on Instagram"
|
||||||
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
|
className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors social-icon">
|
||||||
<InstagramIcon size={14} />
|
<InstagramIcon size={14} />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://facebook.com/broadcastbeat" target="_blank" rel="noopener noreferrer" aria-label="Broadcast Beat on Facebook"
|
<a href="https://facebook.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV Beat on Facebook"
|
||||||
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
|
className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors social-icon">
|
||||||
<FacebookIcon size={14} />
|
<FacebookIcon size={14} />
|
||||||
</a>
|
</a>
|
||||||
<a href="/rss" aria-label="Broadcast Beat RSS Feed" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors social-icon">
|
<a href="/rss" aria-label="AV Beat RSS Feed" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors social-icon">
|
||||||
<RssIcon size={14} />
|
<RssIcon size={14} />
|
||||||
</a>
|
</a>
|
||||||
<div className="w-px h-3.5 bg-[#333] mx-1" />
|
<div className="w-px h-3.5 bg-[#333] mx-1" />
|
||||||
@@ -442,7 +442,7 @@ export default function Header() {
|
|||||||
<div className="w-px h-3.5 bg-[#333] mx-1" />
|
<div className="w-px h-3.5 bg-[#333] mx-1" />
|
||||||
{currentUserId ? (
|
{currentUserId ? (
|
||||||
<>
|
<>
|
||||||
<Link href="/account" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/account" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
My Profile
|
My Profile
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
@@ -452,33 +452,33 @@ export default function Header() {
|
|||||||
await supabase.auth.signOut();
|
await supabase.auth.signOut();
|
||||||
window.location.href = '/forum';
|
window.location.href = '/forum';
|
||||||
}}
|
}}
|
||||||
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider"
|
className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider"
|
||||||
>
|
>
|
||||||
Log Out
|
Log Out
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Link href="/forum/login" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/forum/login" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
Sign In
|
Sign In
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/forum/register" className="text-[#3b82f6] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/forum/register" className="text-[#E67E22] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
Join
|
Join
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<>
|
<>
|
||||||
<Link href="/admin/import" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/admin/import" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
WP Import
|
WP Import
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/admin/articles" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/admin/articles" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
Articles
|
Articles
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/dashboard/editorial" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/dashboard/editorial" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
Editorial
|
Editorial
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/admin/newsletter" className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/admin/newsletter" className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
Newsletter
|
Newsletter
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
@@ -487,7 +487,7 @@ export default function Header() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
className="text-[#999] hover:text-[#3b82f6] focus:text-[#3b82f6] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider inline-flex items-center gap-1">
|
className="text-[#999] hover:text-[#E67E22] focus:text-[#E67E22] focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider inline-flex items-center gap-1">
|
||||||
Tools
|
Tools
|
||||||
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
|
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
|
||||||
<polyline points="6 9 12 15 18 9" />
|
<polyline points="6 9 12 15 18 9" />
|
||||||
@@ -497,25 +497,25 @@ export default function Header() {
|
|||||||
role="menu"
|
role="menu"
|
||||||
aria-label="Admin tools"
|
aria-label="Admin tools"
|
||||||
className="absolute right-0 top-full min-w-[200px] bg-[#0d0d0d] border border-[#252525] shadow-xl rounded-sm py-1 opacity-0 invisible translate-y-1 group-hover/tools:opacity-100 group-hover/tools:visible group-hover/tools:translate-y-0 group-focus-within/tools:opacity-100 group-focus-within/tools:visible group-focus-within/tools:translate-y-0 transition-all duration-150 z-50">
|
className="absolute right-0 top-full min-w-[200px] bg-[#0d0d0d] border border-[#252525] shadow-xl rounded-sm py-1 opacity-0 invisible translate-y-1 group-hover/tools:opacity-100 group-hover/tools:visible group-hover/tools:translate-y-0 group-focus-within/tools:opacity-100 group-focus-within/tools:visible group-focus-within/tools:translate-y-0 transition-all duration-150 z-50">
|
||||||
<Link href="/admin/users" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Users</Link>
|
<Link href="/admin/users" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Users</Link>
|
||||||
<Link href="/admin/analytics" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Analytics</Link>
|
<Link href="/admin/analytics" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Analytics</Link>
|
||||||
<Link href="/admin/audit-log" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Audit Log</Link>
|
<Link href="/admin/audit-log" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Audit Log</Link>
|
||||||
<Link href="/admin/notifications" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Notifications</Link>
|
<Link href="/admin/notifications" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Notifications</Link>
|
||||||
<Link href="/admin/forum-seed" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Forum Seed</Link>
|
<Link href="/admin/forum-seed" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Forum Seed</Link>
|
||||||
<Link href="/admin/company-tracker" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Company Tracker</Link>
|
<Link href="/admin/company-tracker" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Company Tracker</Link>
|
||||||
<Link href="/dashboard/show-calendar" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Show Calendar</Link>
|
<Link href="/dashboard/show-calendar" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Show Calendar</Link>
|
||||||
<Link href="/dashboard/ai-console" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#3b82f6] hover:bg-[#1a1a1a] hover:text-blue-300 focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-blue-300 uppercase tracking-wider transition-colors">AI Console</Link>
|
<Link href="/dashboard/ai-console" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#E67E22] hover:bg-[#1a1a1a] hover:text-blue-300 focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-blue-300 uppercase tracking-wider transition-colors">AI Console</Link>
|
||||||
<Link href="/admin/banned-terms" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#3b82f6] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#3b82f6] uppercase tracking-wider transition-colors">Banned Terms</Link>
|
<Link href="/admin/banned-terms" role="menuitem" className="block px-4 py-2 text-xs font-body text-[#bbb] hover:bg-[#1a1a1a] hover:text-[#E67E22] focus:outline-none focus-visible:bg-[#1a1a1a] focus-visible:text-[#E67E22] uppercase tracking-wider transition-colors">Banned Terms</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Link href="/admin" className="text-[#3b82f6] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
<Link href="/admin" className="text-[#E67E22] hover:text-blue-300 focus:text-blue-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] transition-colors text-xs font-body font-bold uppercase tracking-wider">
|
||||||
Dashboard
|
Dashboard
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* Inline Email Subscription Form */}
|
{/* Inline Email Subscription Form */}
|
||||||
{subStatus === "success" ? (
|
{subStatus === "success" ? (
|
||||||
<span className="text-[#3b82f6] text-xs font-body font-bold whitespace-nowrap">{subMessage}</span>
|
<span className="text-[#E67E22] text-xs font-body font-bold whitespace-nowrap">{subMessage}</span>
|
||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubscribe} className="flex items-center gap-1" aria-label="Newsletter subscription">
|
<form onSubmit={handleSubscribe} className="flex items-center gap-1" aria-label="Newsletter subscription">
|
||||||
<input
|
<input
|
||||||
@@ -525,12 +525,12 @@ export default function Header() {
|
|||||||
placeholder="your@email.com"
|
placeholder="your@email.com"
|
||||||
aria-label="Email address for newsletter"
|
aria-label="Email address for newsletter"
|
||||||
disabled={subStatus === "loading"}
|
disabled={subStatus === "loading"}
|
||||||
className="h-5 px-2 text-[11px] bg-[#0d1520] border border-[#2a3a50] text-[#ccc] placeholder-[#555] rounded focus:outline-none focus:border-[#3b82f6] focus-visible:ring-1 focus-visible:ring-[#3b82f6] w-32 disabled:opacity-50 transition-colors"
|
className="h-5 px-2 text-[11px] bg-[#0d1520] border border-[#2a3a50] text-[#ccc] placeholder-[#555] rounded focus:outline-none focus:border-[#E67E22] focus-visible:ring-1 focus-visible:ring-[#E67E22] w-32 disabled:opacity-50 transition-colors"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={subStatus === "loading"}
|
disabled={subStatus === "loading"}
|
||||||
className="h-5 px-2.5 text-[11px] font-body font-bold uppercase tracking-wider bg-[#3b82f6] hover:bg-[#2563eb] text-white rounded disabled:opacity-50 transition-colors whitespace-nowrap focus:outline-none focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-1 focus-visible:ring-offset-[#3b82f6]"
|
className="h-5 px-2.5 text-[11px] font-body font-bold uppercase tracking-wider bg-[#E67E22] hover:bg-[#C8651B] text-white rounded disabled:opacity-50 transition-colors whitespace-nowrap focus:outline-none focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-1 focus-visible:ring-offset-[#E67E22]"
|
||||||
>
|
>
|
||||||
{subStatus === "loading" ? "..." : "Subscribe"}
|
{subStatus === "loading" ? "..." : "Subscribe"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export default function LanguageSwitcher({ currentLang, onLanguageChange, compac
|
|||||||
<div ref={dropdownRef} className="relative z-[100]">
|
<div ref={dropdownRef} className="relative z-[100]">
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
className="flex items-center gap-1.5 px-2 py-1 rounded text-xs text-[#888] hover:text-white hover:bg-[#1a1a1a] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]"
|
className="flex items-center gap-1.5 px-2 py-1 rounded text-xs text-[#888] hover:text-white hover:bg-[#1a1a1a] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]"
|
||||||
aria-label={`Language: ${currentLangData.name}. Click to change.`}
|
aria-label={`Language: ${currentLangData.name}. Click to change.`}
|
||||||
aria-expanded={isOpen}
|
aria-expanded={isOpen}
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
@@ -184,14 +184,14 @@ export default function LanguageSwitcher({ currentLang, onLanguageChange, compac
|
|||||||
onClick={() => handleSelect(lang.code)}
|
onClick={() => handleSelect(lang.code)}
|
||||||
className={`w-full flex items-center gap-2.5 px-3 py-2 text-xs transition-colors text-left ${
|
className={`w-full flex items-center gap-2.5 px-3 py-2 text-xs transition-colors text-left ${
|
||||||
activeLang === lang.code
|
activeLang === lang.code
|
||||||
? 'bg-[#3b82f6]/10 text-[#3b82f6]'
|
? 'bg-[#E67E22]/10 text-[#E67E22]'
|
||||||
: 'text-[#888] hover:text-white hover:bg-[#1a1a1a]'
|
: 'text-[#888] hover:text-white hover:bg-[#1a1a1a]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="text-sm">{lang.flag}</span>
|
<span className="text-sm">{lang.flag}</span>
|
||||||
<span>{lang.nativeName}</span>
|
<span>{lang.nativeName}</span>
|
||||||
{activeLang === lang.code && (
|
{activeLang === lang.code && (
|
||||||
<svg className="w-3 h-3 ml-auto text-[#3b82f6]" fill="currentColor" viewBox="0 0 20 20">
|
<svg className="w-3 h-3 ml-auto text-[#E67E22]" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ export default function NotificationCenter() {
|
|||||||
aria-label={`Notifications${unreadCount > 0 ? `, ${unreadCount} unread` : ""}`}
|
aria-label={`Notifications${unreadCount > 0 ? `, ${unreadCount} unread` : ""}`}
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
className="relative flex items-center justify-center w-7 h-7 text-[#888] hover:text-[#3b82f6] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6] focus-visible:ring-offset-1 focus-visible:ring-offset-[#111]"
|
className="relative flex items-center justify-center w-7 h-7 text-[#888] hover:text-[#E67E22] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22] focus-visible:ring-offset-1 focus-visible:ring-offset-[#111]"
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
||||||
@@ -202,7 +202,7 @@ export default function NotificationCenter() {
|
|||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="absolute -top-1 -right-1 min-w-[16px] h-4 px-0.5 flex items-center justify-center rounded-full bg-[#3b82f6] text-white text-[9px] font-bold leading-none"
|
className="absolute -top-1 -right-1 min-w-[16px] h-4 px-0.5 flex items-center justify-center rounded-full bg-[#E67E22] text-white text-[9px] font-bold leading-none"
|
||||||
>
|
>
|
||||||
{unreadCount > 99 ? "99+" : unreadCount}
|
{unreadCount > 99 ? "99+" : unreadCount}
|
||||||
</span>
|
</span>
|
||||||
@@ -225,7 +225,7 @@ export default function NotificationCenter() {
|
|||||||
{notifications.length > 0 && (
|
{notifications.length > 0 && (
|
||||||
<button
|
<button
|
||||||
onClick={dismissAll}
|
onClick={dismissAll}
|
||||||
className="text-[10px] text-[#666] hover:text-[#3b82f6] transition-colors uppercase tracking-wide font-bold focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]"
|
className="text-[10px] text-[#666] hover:text-[#E67E22] transition-colors uppercase tracking-wide font-bold focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]"
|
||||||
>
|
>
|
||||||
Clear all
|
Clear all
|
||||||
</button>
|
</button>
|
||||||
@@ -233,7 +233,7 @@ export default function NotificationCenter() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
aria-label="Close notifications"
|
aria-label="Close notifications"
|
||||||
className="text-[#555] hover:text-[#ccc] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]"
|
className="text-[#555] hover:text-[#ccc] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-[#E67E22]"
|
||||||
>
|
>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
|
||||||
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
||||||
@@ -287,9 +287,9 @@ function FilterTabs({ notifications }: { notifications: Notification[] }) {
|
|||||||
<button
|
<button
|
||||||
key={tab.key}
|
key={tab.key}
|
||||||
onClick={() => setActiveTab(tab.key)}
|
onClick={() => setActiveTab(tab.key)}
|
||||||
className={`flex-shrink-0 px-3 py-2 text-[10px] font-bold uppercase tracking-wide transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6] ${
|
className={`flex-shrink-0 px-3 py-2 text-[10px] font-bold uppercase tracking-wide transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22] ${
|
||||||
activeTab === tab.key
|
activeTab === tab.key
|
||||||
? "text-[#3b82f6] border-b-2 border-[#3b82f6]"
|
? "text-[#E67E22] border-b-2 border-[#E67E22]"
|
||||||
: "text-[#666] hover:text-[#aaa]"
|
: "text-[#666] hover:text-[#aaa]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -359,7 +359,7 @@ function NotificationItem({ notification: n }: { notification: Notification }) {
|
|||||||
<button
|
<button
|
||||||
onClick={handleDismiss}
|
onClick={handleDismiss}
|
||||||
aria-label="Dismiss notification"
|
aria-label="Dismiss notification"
|
||||||
className="flex-shrink-0 opacity-0 group-hover:opacity-100 text-[#444] hover:text-[#888] transition-all focus:outline-none focus:opacity-100 focus-visible:ring-1 focus-visible:ring-[#3b82f6]"
|
className="flex-shrink-0 opacity-0 group-hover:opacity-100 text-[#444] hover:text-[#888] transition-all focus:outline-none focus:opacity-100 focus-visible:ring-1 focus-visible:ring-[#E67E22]"
|
||||||
>
|
>
|
||||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
|
||||||
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
<line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
|
||||||
@@ -370,7 +370,7 @@ function NotificationItem({ notification: n }: { notification: Notification }) {
|
|||||||
<span className={`text-[9px] font-bold uppercase tracking-wide ${cfg.color}`}>{cfg.label}</span>
|
<span className={`text-[9px] font-bold uppercase tracking-wide ${cfg.color}`}>{cfg.label}</span>
|
||||||
<span className="text-[9px] text-[#444]">{timeAgo(n.created_at)}</span>
|
<span className="text-[9px] text-[#444]">{timeAgo(n.created_at)}</span>
|
||||||
{!n.is_read && (
|
{!n.is_read && (
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-[#3b82f6] flex-shrink-0" aria-label="Unread" />
|
<span className="w-1.5 h-1.5 rounded-full bg-[#E67E22] flex-shrink-0" aria-label="Unread" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -379,7 +379,7 @@ function NotificationItem({ notification: n }: { notification: Notification }) {
|
|||||||
|
|
||||||
if (n.link) {
|
if (n.link) {
|
||||||
return (
|
return (
|
||||||
<Link href={n.link} className="block focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#3b82f6]">
|
<Link href={n.link} className="block focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#E67E22]">
|
||||||
{content}
|
{content}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import { useEffect, useState } from "react";
|
|||||||
* On mount we assemble the address client-side and wrap in a real mailto.
|
* On mount we assemble the address client-side and wrap in a real mailto.
|
||||||
*
|
*
|
||||||
* Pre-mount HTML looks like:
|
* Pre-mount HTML looks like:
|
||||||
* <span data-u="ryan" data-d="broadcastbeat.com">[email protected]</span>
|
* <span data-u="ryan" data-d="avbeat.com">[email protected]</span>
|
||||||
*
|
*
|
||||||
* Post-mount (JS executed) becomes:
|
* Post-mount (JS executed) becomes:
|
||||||
* <a href="mailto:ryan@broadcastbeat.com">ryan@broadcastbeat.com</a>
|
* <a href="mailto:ryan@avbeat.com">ryan@avbeat.com</a>
|
||||||
*
|
*
|
||||||
* Plus the @ is rendered with an HTML entity so even rare bots reading the
|
* Plus the @ is rendered with an HTML entity so even rare bots reading the
|
||||||
* pre-JS DOM won't get a clean string.
|
* pre-JS DOM won't get a clean string.
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const ToolbarButton = ({
|
|||||||
title={title}
|
title={title}
|
||||||
className={`p-1.5 rounded transition-colors text-sm ${
|
className={`p-1.5 rounded transition-colors text-sm ${
|
||||||
active
|
active
|
||||||
? 'bg-[#3b82f6] text-white'
|
? 'bg-[#E67E22] text-white'
|
||||||
: 'text-[#888] hover:text-[#cccccc] hover:bg-[#252525]'
|
: 'text-[#888] hover:text-[#cccccc] hover:bg-[#252525]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -53,7 +53,7 @@ export default function RichTextEditor({ value, onChange, placeholder = 'Start w
|
|||||||
Color,
|
Color,
|
||||||
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||||
Image.configure({ inline: false, allowBase64: true }),
|
Image.configure({ inline: false, allowBase64: true }),
|
||||||
Link.configure({ openOnClick: false, HTMLAttributes: { class: 'text-[#3b82f6] underline' } }),
|
Link.configure({ openOnClick: false, HTMLAttributes: { class: 'text-[#E67E22] underline' } }),
|
||||||
Placeholder.configure({ placeholder }),
|
Placeholder.configure({ placeholder }),
|
||||||
],
|
],
|
||||||
content: value,
|
content: value,
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ export default async function SponsorLogoStrip() {
|
|||||||
return (
|
return (
|
||||||
<section className="max-w-container mx-auto px-4 py-6 border-t border-b border-[#1a1a1a]" aria-label="Content partners">
|
<section className="max-w-container mx-auto px-4 py-6 border-t border-b border-[#1a1a1a]" aria-label="Content partners">
|
||||||
<div className="flex items-center gap-4 mb-3">
|
<div className="flex items-center gap-4 mb-3">
|
||||||
<span className="text-[10px] uppercase tracking-widest text-[#3b82f6] font-bold">
|
<span className="text-[10px] uppercase tracking-widest text-[#E67E22] font-bold">
|
||||||
Content Partners
|
Content Partners
|
||||||
</span>
|
</span>
|
||||||
<div className="flex-1 h-px bg-[#1a1a1a]" />
|
<div className="flex-1 h-px bg-[#1a1a1a]" />
|
||||||
<Link href="/advertise" className="text-[10px] text-[#666] hover:text-[#3b82f6] font-mono">
|
<Link href="/advertise" className="text-[10px] text-[#666] hover:text-[#E67E22] font-mono">
|
||||||
Advertise →
|
Advertise →
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default function SocialAuthButtons({ next = "/forum" }: { next?: string }
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={!!busy}
|
disabled={!!busy}
|
||||||
onClick={() => oauth(p.id)}
|
onClick={() => oauth(p.id)}
|
||||||
className="bg-[#0d0d0d] border border-[#252525] hover:border-[#3b82f6] text-white text-xs font-semibold py-2 px-3 rounded-lg flex items-center justify-center gap-2 disabled:opacity-50 transition-colors"
|
className="bg-[#0d0d0d] border border-[#252525] hover:border-[#E67E22] text-white text-xs font-semibold py-2 px-3 rounded-lg flex items-center justify-center gap-2 disabled:opacity-50 transition-colors"
|
||||||
>
|
>
|
||||||
<span className="text-base leading-none">{p.icon}</span>
|
<span className="text-base leading-none">{p.icon}</span>
|
||||||
<span>{busy === p.id ? "Redirecting…" : p.label}</span>
|
<span>{busy === p.id ? "Redirecting…" : p.label}</span>
|
||||||
@@ -96,7 +96,7 @@ export default function SocialAuthButtons({ next = "/forum" }: { next?: string }
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={busy === "magic" || !email.trim()}
|
disabled={busy === "magic" || !email.trim()}
|
||||||
className="w-full bg-[#3b82f6] hover:bg-[#2563eb] text-white text-xs font-semibold py-1.5 rounded disabled:opacity-50"
|
className="w-full bg-[#E67E22] hover:bg-[#C8651B] text-white text-xs font-semibold py-1.5 rounded disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{busy === "magic" ? "Sending…" : "Send login link"}
|
{busy === "magic" ? "Sending…" : "Send login link"}
|
||||||
</button>
|
</button>
|
||||||
@@ -105,7 +105,7 @@ export default function SocialAuthButtons({ next = "/forum" }: { next?: string }
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowMagic(true)}
|
onClick={() => setShowMagic(true)}
|
||||||
className="w-full bg-[#0d0d0d] border border-dashed border-[#3b82f6]/50 hover:border-[#3b82f6] text-[#3b82f6] text-xs font-semibold py-2 rounded-lg transition-colors"
|
className="w-full bg-[#0d0d0d] border border-dashed border-[#E67E22]/50 hover:border-[#E67E22] text-[#E67E22] text-xs font-semibold py-2 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
📧 Email me a one-time login link (no password)
|
📧 Email me a one-time login link (no password)
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ export function createAdminClient(schema?: string) {
|
|||||||
}
|
}
|
||||||
return createClient(url, key, {
|
return createClient(url, key, {
|
||||||
auth: { persistSession: false, autoRefreshToken: false },
|
auth: { persistSession: false, autoRefreshToken: false },
|
||||||
db: { schema: schema || process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb" },
|
db: { schema: schema || process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "avb" },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export function createClient() {
|
|||||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||||
{
|
{
|
||||||
db: { schema: process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || 'bb' },
|
db: { schema: process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || 'avb' },
|
||||||
cookies: {
|
cookies: {
|
||||||
getAll() {
|
getAll() {
|
||||||
if (typeof document === 'undefined') return [];
|
if (typeof document === 'undefined') return [];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export async function createClient() {
|
|||||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||||
{
|
{
|
||||||
db: { schema: process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || 'bb' },
|
db: { schema: process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || 'avb' },
|
||||||
cookies: {
|
cookies: {
|
||||||
getAll() {
|
getAll() {
|
||||||
return cookieStore.getAll();
|
return cookieStore.getAll();
|
||||||
|
|||||||
@@ -3,21 +3,22 @@
|
|||||||
homepage components. Existing tokens kept intact (forward-only). */
|
homepage components. Existing tokens kept intact (forward-only). */
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* AI accent (blue) — same hue as --color-accent but exposed via the
|
/* AV Beat palette — orange-led, near-black canvas. The legacy
|
||||||
spec name --color-text-info so the redesign components can reference
|
`--color-text-info` and `*-strong` names retain their semantic
|
||||||
it without coupling to the existing --color-accent. */
|
meaning (informational accent) but now carry the AV orange so
|
||||||
--color-text-info: #60a5fa;
|
ticker dots, AI rings and inline accents read as AV Beat. */
|
||||||
--color-text-info-strong: #3b82f6;
|
--color-text-info: #E67E22;
|
||||||
|
--color-text-info-strong: #E67E22;
|
||||||
|
|
||||||
/* Backgrounds */
|
/* Backgrounds */
|
||||||
--color-background-primary: #0a0a0a;
|
--color-background-primary: #0F0E0E;
|
||||||
--color-background-secondary: #111111;
|
--color-background-secondary: #1D1A1A;
|
||||||
--color-background-tertiary: #1a1f2e;
|
--color-background-tertiary: #221c18;
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
--color-text-primary: #e8e8e8;
|
--color-text-primary: #F0EBE6;
|
||||||
--color-text-secondary: #b0b0b0;
|
--color-text-secondary: #b8b1aa;
|
||||||
--color-text-tertiary: #6b7280;
|
--color-text-tertiary: #6b6560;
|
||||||
|
|
||||||
/* Brand surfaces */
|
/* Brand surfaces */
|
||||||
--color-text-success: #10b981;
|
--color-text-success: #10b981;
|
||||||
|
|||||||
@@ -3,20 +3,21 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-primary: #0d0d0d;
|
/* AV Beat palette — orange/near-black, "Where AV Meets IT" */
|
||||||
--color-secondary: #161616;
|
--color-primary: #0F0E0E;
|
||||||
--color-accent: #3b82f6;
|
--color-secondary: #1D1A1A;
|
||||||
--color-accent-dark: #2563eb;
|
--color-accent: #E67E22;
|
||||||
--color-accent-muted: #1e3a5f;
|
--color-accent-dark: #C8651B;
|
||||||
--color-accent-red: #cc0000;
|
--color-accent-muted: #5a3413;
|
||||||
--color-accent-red-dark: #aa0000;
|
--color-accent-red: #E67E22;
|
||||||
--color-bg: #111111;
|
--color-accent-red-dark: #C8651B;
|
||||||
--color-card: #1a1a1a;
|
--color-bg: #0F0E0E;
|
||||||
--color-foreground: #e8e8e8;
|
--color-card: #1D1A1A;
|
||||||
--color-muted: #888888;
|
--color-foreground: #F0EBE6;
|
||||||
--color-border: #2a2a2a;
|
--color-muted: #8a847e;
|
||||||
--color-top-bar: #1a2535;
|
--color-border: #2a2624;
|
||||||
--color-sub-nav: #161c28;
|
--color-top-bar: #1D1A1A;
|
||||||
|
--color-sub-nav: #161313;
|
||||||
/* --font-serif and --font-sans are injected on <html> by next/font/google
|
/* --font-serif and --font-sans are injected on <html> by next/font/google
|
||||||
(Lora + Inter) in src/app/layout.tsx. The system-font tails are real
|
(Lora + Inter) in src/app/layout.tsx. The system-font tails are real
|
||||||
fallbacks for the brief window before the self-hosted face is ready. */
|
fallbacks for the brief window before the self-hosted face is ready. */
|
||||||
|
|||||||
@@ -8,23 +8,24 @@ module.exports = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
primary: '#0d0d0d',
|
// AV Beat palette — orange/near-black, "Where AV Meets IT"
|
||||||
secondary: '#161616',
|
primary: '#0F0E0E',
|
||||||
accent: '#3b82f6',
|
secondary: '#1D1A1A',
|
||||||
'accent-dark': '#2563eb',
|
accent: '#E67E22',
|
||||||
'accent-muted': '#1e3a5f',
|
'accent-dark': '#C8651B',
|
||||||
'accent-red': '#cc0000',
|
'accent-muted': '#5a3413',
|
||||||
'accent-red-dark': '#aa0000',
|
'accent-red': '#E67E22',
|
||||||
background: '#111111',
|
'accent-red-dark': '#C8651B',
|
||||||
card: '#1a1a1a',
|
background: '#0F0E0E',
|
||||||
foreground: '#e8e8e8',
|
card: '#1D1A1A',
|
||||||
muted: '#888888',
|
foreground: '#F0EBE6',
|
||||||
border: '#2a2a2a',
|
muted: '#8a847e',
|
||||||
'navy-mid': '#1e1e1e',
|
border: '#2a2624',
|
||||||
'navy-light': '#222222',
|
'navy-mid': '#1e1a18',
|
||||||
'ice-blue': '#1a2535',
|
'navy-light': '#221c18',
|
||||||
'top-bar': '#1a2535',
|
'ice-blue': '#1D1A1A',
|
||||||
'sub-nav': '#161c28',
|
'top-bar': '#1D1A1A',
|
||||||
|
'sub-nav': '#161313',
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
heading: ['Georgia', 'serif'],
|
heading: ['Georgia', 'serif'],
|
||||||
|
|||||||