fix(contrast,brand): full audit sweep — white-on-white + cross-component renames

- forum/page.tsx: text-white → navy on TopThreads, Following feed, Discover feed cards, search input, select, category grid, Community Guidelines; greys lightened where applicable for new white surface
- forum/[slug]/page.tsx: same sweep for category page (h1, h2, thread rows, new-thread form, search/sort bar) + hover:bg-[#1e2a3a] → slate-200
- forum/user/[username]/page.tsx: text-[#cccccc]/[#bfbfbf]/[#e0e0e0] + bg-[#222] tag chip + #9ca3af labels → readable on white card
- authors/[slug]/page.tsx: profile contact/facts cards readable
- search/page.tsx: h1 + h2 navy + hover slate
- news/[slug]/NewsArticleDetailClient.tsx: toast color
- news/NewsPageClient.tsx: import cleanExcerpt, strip HTML from listing excerpts
- privacy/terms/page.tsx: h1 + h2 + body greys to readable navy/slate
- error.tsx: page-level text to navy + button border slate
- about/page.tsx: page body text + Stat/Block cards to white surface w/ navy text
- Header.tsx: mobile hamburger + dropdown links → readable navy + slate border
- NotificationCenter.tsx: hover + unread bg flipped to light blue
- AISuggestedArticles.tsx: hover bg → slate-200
- Footer.tsx: link greys to #475569 + #DCE6F2 borders
- AskBBAI.tsx: copy rename 'BB AI' → 'AV Beat AI' (greeting, button, header, aria)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 22:13:42 +00:00
parent 2826f0b566
commit 6e9a163cfd
16 changed files with 160 additions and 159 deletions

View File

@@ -11,7 +11,7 @@ export default function AboutPage() {
return ( return (
<div className="min-h-screen bg-background"> <div className="min-h-screen bg-background">
<Header /> <Header />
<div className="max-w-container mx-auto px-4 py-12 text-[#e5e7eb]"> <div className="max-w-container mx-auto px-4 py-12 text-[#1e293b]">
<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-[#DCE6F2] pb-6"> <header className="mb-8 border-b border-[#DCE6F2] pb-6">
@@ -19,7 +19,7 @@ export default function AboutPage() {
AV Beat AV Beat
</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-[#475569] text-base max-w-2xl">
An independent trade publication covering pro AV, live production, display tech, and signal-flow technology. An independent trade publication covering pro AV, live production, display tech, and signal-flow technology.
</p> </p>
</header> </header>
@@ -30,7 +30,7 @@ export default function AboutPage() {
<Stat label="Stories archived" value="29k+" /> <Stat label="Stories archived" value="29k+" />
</div> </div>
<section className="space-y-6 text-[#d1d5db] text-[15px] leading-relaxed"> <section className="space-y-6 text-[#1e293b] text-[15px] leading-relaxed">
<Block title="What we cover"> <Block title="What we cover">
AV Beat reports on the equipment, services, and people AV Beat reports on the equipment, services, and people
that move the professional video industry forward. We follow that move the professional video industry forward. We follow
@@ -79,9 +79,9 @@ export default function AboutPage() {
function Stat({ label, value }: { label: string; value: string }) { function Stat({ label, value }: { label: string; value: string }) {
return ( return (
<div className="rounded border border-[#DCE6F2] bg-[#0b0f17] p-4"> <div className="rounded border border-[#DCE6F2] bg-[#FFFFFF] p-4">
<div className="font-serif text-3xl text-white">{value}</div> <div className="font-serif text-3xl text-[#0F172A]">{value}</div>
<div className="mt-1 text-[10px] font-mono uppercase tracking-[0.16em] text-[#6b7280]"> <div className="mt-1 text-[10px] font-mono uppercase tracking-[0.16em] text-[#475569]">
{label} {label}
</div> </div>
</div> </div>
@@ -90,7 +90,7 @@ function Stat({ label, value }: { label: string; value: string }) {
function Block({ title, children }: { title: string; children: React.ReactNode }) { function Block({ title, children }: { title: string; children: React.ReactNode }) {
return ( return (
<div className="rounded border border-[#1e1e1e] bg-[#0b0f17]/70 p-5"> <div className="rounded border border-[#DCE6F2] bg-[#FFFFFF] p-5">
<h2 className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-2"> <h2 className="font-mono text-xs uppercase tracking-[0.18em] text-[var(--color-text-info,#60a5fa)] mb-2">
{title} {title}
</h2> </h2>

View File

@@ -290,7 +290,7 @@ export default function AuthorProfilePage() {
<span className="font-body text-[10px] text-[#555]">·</span> <span className="font-body text-[10px] text-[#555]">·</span>
<span className="font-body text-[10px] text-[#555]">{article.readTime}</span> <span className="font-body text-[10px] text-[#555]">{article.readTime}</span>
</div> </div>
<h3 className="font-heading text-sm font-bold text-[#e0e0e0] group-hover:text-[#1D4ED8] transition-colors leading-snug mb-2 line-clamp-2"> <h3 className="font-heading text-sm font-bold text-[#0F172A] group-hover:text-[#1D4ED8] transition-colors leading-snug mb-2 line-clamp-2">
{article.title} {article.title}
</h3> </h3>
<p className="font-body text-xs text-[#777] leading-relaxed line-clamp-2 mb-3"> <p className="font-body text-xs text-[#777] leading-relaxed line-clamp-2 mb-3">
@@ -311,7 +311,7 @@ export default function AuthorProfilePage() {
<div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-sm p-6"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-sm p-6">
<p className="section-label mb-4">Biography</p> <p className="section-label mb-4">Biography</p>
{profile.bio ? ( {profile.bio ? (
<p className="font-body text-sm text-[#aaaaaa] leading-relaxed whitespace-pre-line"> <p className="font-body text-sm text-[#1e293b] leading-relaxed whitespace-pre-line">
{profile.bio} {profile.bio}
</p> </p>
) : ( ) : (
@@ -336,7 +336,7 @@ export default function AuthorProfilePage() {
<p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1"> <p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1">
Address Address
</p> </p>
<address className="font-body text-sm text-[#cccccc] not-italic leading-relaxed"> <address className="font-body text-sm text-[#1e293b] not-italic leading-relaxed">
{addressLines.map((line, i) => ( {addressLines.map((line, i) => (
<span key={i}> <span key={i}>
{line} {line}
@@ -400,7 +400,7 @@ export default function AuthorProfilePage() {
<p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1"> <p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1">
Location Location
</p> </p>
<p className="font-body text-sm text-[#cccccc]">{locationLine}</p> <p className="font-body text-sm text-[#1e293b]">{locationLine}</p>
</div> </div>
)} )}
{profile.joined_label && ( {profile.joined_label && (
@@ -408,14 +408,14 @@ export default function AuthorProfilePage() {
<p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1"> <p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1">
Member Since Member Since
</p> </p>
<p className="font-body text-sm text-[#cccccc]">{profile.joined_label}</p> <p className="font-body text-sm text-[#1e293b]">{profile.joined_label}</p>
</div> </div>
)} )}
<div> <div>
<p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1"> <p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1">
Articles Published Articles Published
</p> </p>
<p className="font-body text-sm text-[#cccccc]"> <p className="font-body text-sm text-[#1e293b]">
{articleTotal.toLocaleString()} {articleTotal.toLocaleString()}
</p> </p>
</div> </div>
@@ -423,7 +423,7 @@ export default function AuthorProfilePage() {
<p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1"> <p className="font-body text-xs font-bold uppercase tracking-wider text-[#555] mb-1">
Role Role
</p> </p>
<p className="font-body text-sm text-[#cccccc]">{profile.role}</p> <p className="font-body text-sm text-[#1e293b]">{profile.role}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -13,14 +13,14 @@ export default function Error({
console.error("[route error]", error); console.error("[route error]", error);
}, [error]); }, [error]);
return ( return (
<div className="min-h-screen flex items-center justify-center bg-[#F8FAFC] text-[#e0e0e0] p-6"> <div className="min-h-screen flex items-center justify-center bg-[#F8FAFC] text-[#0F172A] p-6">
<div className="max-w-md text-center"> <div className="max-w-md text-center">
<h2 className="font-heading text-2xl mb-3">Something went wrong</h2> <h2 className="font-heading text-2xl mb-3">Something went wrong</h2>
<p className="text-[#888] text-sm mb-4 font-body"> <p className="text-[#475569] text-sm mb-4 font-body">
We hit an error rendering this page. Try again, or head back to the home page. We hit an error rendering this page. Try again, or head back to the home page.
</p> </p>
{error?.digest && ( {error?.digest && (
<p className="text-[#444] text-xs font-mono mb-4">ref: {error.digest}</p> <p className="text-[#666] text-xs font-mono mb-4">ref: {error.digest}</p>
)} )}
<div className="flex gap-3 justify-center"> <div className="flex gap-3 justify-center">
<button <button
@@ -31,7 +31,7 @@ export default function Error({
</button> </button>
<Link <Link
href="/home-page" href="/home-page"
className="px-4 py-2 border border-[#333] text-sm rounded-sm hover:border-[#1D4ED8] transition-colors" className="px-4 py-2 border border-[#DCE6F2] text-[#0F172A] text-sm rounded-sm hover:border-[#1D4ED8] transition-colors"
> >
Home Home
</Link> </Link>

View File

@@ -84,7 +84,7 @@ function TopThreadsWidget({ categoryId }: { categoryId: string }) {
<div className="flex items-center justify-between px-5 py-4 border-b border-[#DCE6F2]"> <div className="flex items-center justify-between px-5 py-4 border-b border-[#DCE6F2]">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-lg">🏆</span> <span className="text-lg">🏆</span>
<h2 className="text-white font-heading font-bold text-base">Top Performing Threads</h2> <h2 className="text-[#0F172A] font-heading font-bold text-base">Top Performing Threads</h2>
</div> </div>
{/* Metric Tabs */} {/* Metric Tabs */}
<div className="flex gap-1 bg-[#111] border border-[#DCE6F2] rounded-lg p-1"> <div className="flex gap-1 bg-[#111] border border-[#DCE6F2] rounded-lg p-1">
@@ -132,7 +132,7 @@ function TopThreadsWidget({ categoryId }: { categoryId: string }) {
<Link <Link
key={thread.id} key={thread.id}
href={`/forum/thread/${thread.id}`} href={`/forum/thread/${thread.id}`}
className="flex items-center gap-3 px-5 py-3 hover:bg-[#1e2a3a] transition-colors group" className="flex items-center gap-3 px-5 py-3 hover:bg-[#e2e8f0] transition-colors group"
> >
{/* Rank */} {/* Rank */}
<span className={`flex-shrink-0 w-6 text-center font-heading font-bold text-sm ${ <span className={`flex-shrink-0 w-6 text-center font-heading font-bold text-sm ${
@@ -142,7 +142,7 @@ 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-[#1D4ED8] transition-colors truncate leading-snug"> <p className="text-[#0F172A] font-body text-sm font-semibold group-hover:text-[#1D4ED8] 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">
@@ -284,15 +284,15 @@ export default function ForumCategoryPage() {
<nav className="text-sm font-body text-[#666] mb-3"> <nav className="text-sm font-body text-[#666] mb-3">
<Link href="/forum" className="hover:text-[#1D4ED8] transition-colors">Forum</Link> <Link href="/forum" className="hover:text-[#1D4ED8] transition-colors">Forum</Link>
<span className="mx-2"></span> <span className="mx-2"></span>
<span className="text-[#aab4c4]">{category?.name || slug}</span> <span className="text-[#0F172A]">{category?.name || slug}</span>
</nav> </nav>
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-2xl">{category?.icon || '💬'}</span> <span className="text-2xl">{category?.icon || '💬'}</span>
<div> <div>
<h1 className="text-2xl font-heading font-bold text-white">{category?.name || 'Loading...'}</h1> <h1 className="text-2xl font-heading font-bold text-[#0F172A]">{category?.name || 'Loading...'}</h1>
{category && ( {category && (
<p className="text-[#aab4c4] font-body text-sm mt-0.5">{category.thread_count} threads</p> <p className="text-[#475569] font-body text-sm mt-0.5">{category.thread_count} threads</p>
)} )}
</div> </div>
</div> </div>
@@ -318,8 +318,8 @@ export default function ForumCategoryPage() {
<div className="max-w-container mx-auto px-4 py-6"> <div className="max-w-container mx-auto px-4 py-6">
{/* New Thread Form — auth-gated */} {/* New Thread Form — auth-gated */}
{showNewThread && isAuthenticated && ( {showNewThread && isAuthenticated && (
<form onSubmit={handleNewThread} className="bg-[#FFFFFF] border border-[#2a3a50] rounded-lg p-5 mb-6"> <form onSubmit={handleNewThread} className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-lg p-5 mb-6">
<h3 className="text-white font-heading font-semibold mb-4">Start a New Thread</h3> <h3 className="text-[#0F172A] font-heading font-semibold mb-4">Start a New Thread</h3>
<div className="space-y-3"> <div className="space-y-3">
<input <input
type="text" type="text"
@@ -327,7 +327,7 @@ export default function ForumCategoryPage() {
value={newTitle} value={newTitle}
onChange={e => setNewTitle(e.target.value)} onChange={e => setNewTitle(e.target.value)}
required required
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-[#1D4ED8]" className="w-full bg-[#F8FAFC] border border-[#DCE6F2] rounded-lg px-3 py-2 text-[#0F172A] font-body text-sm placeholder-[#888] focus:outline-none focus:border-[#1D4ED8]"
/> />
<textarea <textarea
placeholder="What would you like to discuss? *" placeholder="What would you like to discuss? *"
@@ -335,7 +335,7 @@ export default function ForumCategoryPage() {
onChange={e => setNewBody(e.target.value)} onChange={e => setNewBody(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-[#1D4ED8] resize-none" className="w-full bg-[#F8FAFC] border border-[#DCE6F2] rounded-lg px-3 py-2 text-[#0F172A] font-body text-sm placeholder-[#888] focus:outline-none focus:border-[#1D4ED8] resize-none"
/> />
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ImageAttachButton onInsert={(md) => setNewBody((b) => (b || "") + md)} /> <ImageAttachButton onInsert={(md) => setNewBody((b) => (b || "") + md)} />
@@ -357,7 +357,7 @@ export default function ForumCategoryPage() {
<button <button
type="button" type="button"
onClick={() => setShowNewThread(false)} onClick={() => setShowNewThread(false)}
className="bg-[#DCE6F2] hover:bg-[#333] text-[#aaa] font-body text-sm px-5 py-2 rounded-lg transition-colors" className="bg-[#DCE6F2] hover:bg-[#cbd5e1] text-[#0F172A] font-body text-sm px-5 py-2 rounded-lg transition-colors"
> >
Cancel Cancel
</button> </button>
@@ -378,7 +378,7 @@ export default function ForumCategoryPage() {
placeholder="Search threads in this category..." placeholder="Search threads in this category..."
value={search} value={search}
onChange={e => setSearch(e.target.value)} onChange={e => setSearch(e.target.value)}
className="w-full bg-[#FFFFFF] border border-[#DCE6F2] focus:border-[#1D4ED8] 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-[#FFFFFF] border border-[#DCE6F2] focus:border-[#1D4ED8] rounded-lg pl-9 pr-4 py-2.5 text-[#0F172A] font-body text-sm placeholder-[#888] focus:outline-none transition-colors"
/> />
{search && ( {search && (
<button <button
@@ -402,7 +402,7 @@ export default function ForumCategoryPage() {
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-[#1D4ED8] text-white' ? 'bg-[#1D4ED8] text-white'
: 'text-[#888] hover:text-white' : 'text-[#888] hover:text-[#0F172A]'
}`} }`}
> >
{opt.label} {opt.label}
@@ -426,8 +426,8 @@ export default function ForumCategoryPage() {
)} )}
{!loading && !threadsLoading && !error && threads.length === 0 && ( {!loading && !threadsLoading && !error && threads.length === 0 && (
<div className="text-center py-16 text-[#555] font-body"> <div className="text-center py-16 text-[#475569] font-body">
<p className="text-lg mb-2"> <p className="text-lg mb-2 text-[#0F172A] font-semibold">
{search ? 'No threads match your search' : sort === 'unanswered' ? 'No unanswered threads' : 'No threads yet'} {search ? 'No threads match your search' : sort === 'unanswered' ? 'No unanswered threads' : 'No threads yet'}
</p> </p>
<p className="text-sm"> <p className="text-sm">
@@ -445,7 +445,7 @@ export default function ForumCategoryPage() {
<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-[#FFFFFF] hover:bg-[#1e2a3a] border border-[#DCE6F2] hover:border-[#1D4ED8] rounded-lg px-4 py-3 transition-all group" className="flex items-center gap-4 bg-[#FFFFFF] hover:bg-[#e2e8f0] border border-[#DCE6F2] hover:border-[#1D4ED8] rounded-lg px-4 py-3 transition-all group"
> >
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
@@ -458,7 +458,7 @@ export default function ForumCategoryPage() {
{thread.reply_count === 0 && ( {thread.reply_count === 0 && (
<span className="text-xs bg-[#f59e0b]/10 text-[#f59e0b] px-2 py-0.5 rounded font-body">UNANSWERED</span> <span className="text-xs bg-[#f59e0b]/10 text-[#f59e0b] px-2 py-0.5 rounded font-body">UNANSWERED</span>
)} )}
<h3 className="text-white font-body font-semibold text-sm group-hover:text-[#1D4ED8] transition-colors truncate"> <h3 className="text-[#0F172A] font-body font-semibold text-sm group-hover:text-[#1D4ED8] transition-colors truncate">
{thread.title} {thread.title}
</h3> </h3>
</div> </div>
@@ -468,13 +468,13 @@ export default function ForumCategoryPage() {
</p> </p>
</div> </div>
<div className="flex-shrink-0 text-right hidden sm:block"> <div className="flex-shrink-0 text-right hidden sm:block">
<div className="text-[#aaa] font-body text-sm font-semibold">{thread.reply_count}</div> <div className="text-[#0F172A] font-body text-sm font-semibold">{thread.reply_count}</div>
<div className="text-[#555] font-body text-xs">replies</div> <div className="text-[#475569] font-body text-xs">replies</div>
</div> </div>
{sort === 'top-voted' && ( {sort === 'top-voted' && (
<div className="flex-shrink-0 text-right hidden md:block"> <div className="flex-shrink-0 text-right hidden md:block">
<div className="text-[#22c55e] font-body text-sm font-semibold">+{thread.upvotes ?? 0}</div> <div className="text-[#22c55e] font-body text-sm font-semibold">+{thread.upvotes ?? 0}</div>
<div className="text-[#555] font-body text-xs">votes</div> <div className="text-[#475569] font-body text-xs">votes</div>
</div> </div>
)} )}
<div className="flex-shrink-0 text-right hidden md:block"> <div className="flex-shrink-0 text-right hidden md:block">

View File

@@ -151,7 +151,7 @@ function TopThreadsWidget({ categoryId }: { categoryId?: string }) {
</> </>
)} )}
{!loading && threads.length === 0 && ( {!loading && threads.length === 0 && (
<div className="px-5 py-8 text-center text-[#555] font-body text-sm"> <div className="px-5 py-8 text-center text-[#475569] font-body text-sm">
No threads yet. Be the first to start a discussion! No threads yet. Be the first to start a discussion!
</div> </div>
)} )}
@@ -161,7 +161,7 @@ function TopThreadsWidget({ categoryId }: { categoryId?: string }) {
<Link <Link
key={thread.id} key={thread.id}
href={`/forum/thread/${thread.id}`} href={`/forum/thread/${thread.id}`}
className="flex items-center gap-3 px-5 py-3 hover:bg-[#1e2a3a] transition-colors group" className="flex items-center gap-3 px-5 py-3 hover:bg-[#e2e8f0] transition-colors group"
> >
{/* Rank */} {/* Rank */}
<span className={`flex-shrink-0 w-6 text-center font-heading font-bold text-sm ${ <span className={`flex-shrink-0 w-6 text-center font-heading font-bold text-sm ${
@@ -171,11 +171,11 @@ 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-[#1D4ED8] transition-colors truncate leading-snug"> <p className="text-[#0F172A] font-body text-sm font-semibold group-hover:text-[#1D4ED8] 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-[#475569] font-body text-xs mt-0.5">
<Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-[#666] hover:text-[#1D4ED8] hover:underline" onClick={(e) => e.stopPropagation()}>{thread.author_name}</Link> <Link href={`/forum/user/${encodeURIComponent(thread.author_name)}`} className="text-[#475569] hover:text-[#1D4ED8] hover:underline" onClick={(e) => e.stopPropagation()}>{thread.author_name}</Link>
{thread.forum_categories && ( {thread.forum_categories && (
<> · <span className="text-[#1D4ED8]">{thread.forum_categories.name}</span></> <> · <span className="text-[#1D4ED8]">{thread.forum_categories.name}</span></>
)} )}
@@ -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-[#1D4ED8] text-white' : 'text-[#888] hover:text-white' feedMode === 'discover' ? 'bg-[#1D4ED8] text-white' : 'text-[#888] hover:text-[#0F172A]'
}`} }`}
> >
<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-[#1D4ED8] text-white' : 'text-[#888] hover:text-white' feedMode === 'following' ? 'bg-[#1D4ED8] text-white' : 'text-[#888] hover:text-[#0F172A]'
}`} }`}
> >
<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">
@@ -359,8 +359,8 @@ export default function ForumIndexPage() {
{feedMode === 'following' && ( {feedMode === 'following' && (
<div className="mb-8"> <div className="mb-8">
{!currentUserId && ( {!currentUserId && (
<div className="bg-[#FFFFFF] border border-[#2a3a50] rounded-lg p-8 text-center"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-lg p-8 text-center">
<p className="text-[#aab4c4] font-body text-sm mb-3">Sign in to see threads from people you follow.</p> <p className="text-[#475569] 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-[#1D4ED8] text-white font-body text-sm font-bold px-5 py-2 rounded-lg hover:bg-[#1E3A8A] transition-colors" className="inline-block bg-[#1D4ED8] text-white font-body text-sm font-bold px-5 py-2 rounded-lg hover:bg-[#1E3A8A] transition-colors"
@@ -379,8 +379,8 @@ export default function ForumIndexPage() {
{currentUserId && !followingLoading && followingThreads.length === 0 && ( {currentUserId && !followingLoading && followingThreads.length === 0 && (
<div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-lg p-10 text-center"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-lg p-10 text-center">
<div className="text-3xl mb-3">👥</div> <div className="text-3xl mb-3">👥</div>
<p className="text-[#aaa] font-body text-sm font-semibold mb-1">Your following feed is empty</p> <p className="text-[#0F172A] font-body text-sm font-semibold mb-1">Your following feed is empty</p>
<p className="text-[#555] font-body text-sm mb-4">Follow other members to see their threads here.</p> <p className="text-[#475569] font-body text-sm mb-4">Follow other members to see their threads here.</p>
<Link <Link
href="/forum" href="/forum"
onClick={() => setFeedMode('discover')} onClick={() => setFeedMode('discover')}
@@ -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-[#FFFFFF] hover:bg-[#1e2a3a] border border-[#DCE6F2] hover:border-[#1D4ED8] rounded-lg px-4 py-3 transition-all group" className="flex items-center gap-4 bg-[#FFFFFF] hover:bg-[#e2e8f0] border border-[#DCE6F2] hover:border-[#1D4ED8] 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-[#1D4ED8] transition-colors truncate"> <h3 className="text-[#0F172A] font-body font-semibold text-sm group-hover:text-[#1D4ED8] 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">
@@ -420,12 +420,12 @@ export default function ForumIndexPage() {
</p> </p>
</div> </div>
<div className="flex-shrink-0 text-right hidden sm:block"> <div className="flex-shrink-0 text-right hidden sm:block">
<div className="text-[#aaa] font-body text-sm font-semibold">{thread.reply_count}</div> <div className="text-[#0F172A] font-body text-sm font-semibold">{thread.reply_count}</div>
<div className="text-[#555] font-body text-xs">replies</div> <div className="text-[#475569] font-body text-xs">replies</div>
</div> </div>
<div className="flex-shrink-0 text-right hidden md:block"> <div className="flex-shrink-0 text-right hidden md:block">
<div className="text-[#aaa] font-body text-sm font-semibold">{thread.view_count}</div> <div className="text-[#0F172A] font-body text-sm font-semibold">{thread.view_count}</div>
<div className="text-[#555] font-body text-xs">views</div> <div className="text-[#475569] font-body text-xs">views</div>
</div> </div>
</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-[#FFFFFF] border border-[#DCE6F2] focus:border-[#1D4ED8] 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-[#FFFFFF] border border-[#DCE6F2] focus:border-[#1D4ED8] rounded-lg pl-9 pr-4 py-2.5 text-[#0F172A] font-body text-sm placeholder-[#888] 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-[#FFFFFF] border border-[#DCE6F2] focus:border-[#1D4ED8] 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-[#FFFFFF] border border-[#DCE6F2] focus:border-[#1D4ED8] rounded-lg pl-3 pr-8 py-2.5 text-sm font-body text-[#0F172A] 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 => (
@@ -491,7 +491,7 @@ export default function ForumIndexPage() {
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-[#1D4ED8] text-white' ? 'bg-[#1D4ED8] text-white'
: 'text-[#888] hover:text-white' : 'text-[#888] hover:text-[#0F172A]'
}`} }`}
> >
{opt.label} {opt.label}
@@ -511,8 +511,8 @@ export default function ForumIndexPage() {
</div> </div>
)} )}
{!threadsLoading && threads.length === 0 && ( {!threadsLoading && threads.length === 0 && (
<div className="text-center py-12 text-[#555] font-body"> <div className="text-center py-12 text-[#475569] font-body">
<p className="text-base mb-1">No threads found</p> <p className="text-base mb-1 text-[#0F172A] font-semibold">No threads found</p>
<p className="text-sm">Try adjusting your search or filters.</p> <p className="text-sm">Try adjusting your search or filters.</p>
</div> </div>
)} )}
@@ -523,10 +523,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-[#FFFFFF] hover:bg-[#1e2a3a] border border-[#DCE6F2] hover:border-[#1D4ED8] rounded-lg px-4 py-3 transition-all group" className="flex items-center gap-4 bg-[#FFFFFF] hover:bg-[#e2e8f0] border border-[#DCE6F2] hover:border-[#1D4ED8] 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-[#1D4ED8] transition-colors truncate"> <h3 className="text-[#0F172A] font-body font-semibold text-sm group-hover:text-[#1D4ED8] 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">
@@ -538,13 +538,13 @@ export default function ForumIndexPage() {
</p> </p>
</div> </div>
<div className="flex-shrink-0 text-right hidden sm:block"> <div className="flex-shrink-0 text-right hidden sm:block">
<div className="text-[#aaa] font-body text-sm font-semibold">{thread.reply_count}</div> <div className="text-[#0F172A] font-body text-sm font-semibold">{thread.reply_count}</div>
<div className="text-[#555] font-body text-xs">replies</div> <div className="text-[#475569] font-body text-xs">replies</div>
</div> </div>
{sort === 'top-voted' && ( {sort === 'top-voted' && (
<div className="flex-shrink-0 text-right hidden md:block"> <div className="flex-shrink-0 text-right hidden md:block">
<div className="text-[#22c55e] font-body text-sm font-semibold">+{thread.upvotes ?? 0}</div> <div className="text-[#22c55e] font-body text-sm font-semibold">+{thread.upvotes ?? 0}</div>
<div className="text-[#555] font-body text-xs">votes</div> <div className="text-[#475569] font-body text-xs">votes</div>
</div> </div>
)} )}
</Link> </Link>
@@ -584,30 +584,30 @@ 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-[#FFFFFF] hover:bg-[#1e2a3a] border border-[#DCE6F2] hover:border-[#1D4ED8] rounded-lg p-4 transition-all group" className="flex items-center gap-4 bg-[#FFFFFF] hover:bg-[#e2e8f0] border border-[#DCE6F2] hover:border-[#1D4ED8] 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-[#1D4ED8] transition-colors"> <h2 className="text-[#0F172A] font-heading font-semibold text-lg group-hover:text-[#1D4ED8] 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-[#475569] font-body text-sm mt-0.5 truncate">{cat.description}</p>
{cat.last_thread_title && ( {cat.last_thread_title && (
<p className="text-[10px] text-[#666] font-body mt-1.5 truncate"> <p className="text-[10px] text-[#475569] font-body mt-1.5 truncate">
<span className="text-[#555]">Last post:</span>{' '} <span className="text-[#666]">Last post:</span>{' '}
<span className="text-[#aaa]">{cat.last_thread_title}</span> <span className="text-[#1e293b]">{cat.last_thread_title}</span>
{cat.last_activity_at && ( {cat.last_activity_at && (
<span className="text-[#555]"> · {timeAgoFresh(cat.last_activity_at, cat.id)}</span> <span className="text-[#666]"> · {timeAgoFresh(cat.last_activity_at, cat.id)}</span>
)} )}
</p> </p>
)} )}
</div> </div>
<div className="flex-shrink-0 text-right hidden sm:block"> <div className="flex-shrink-0 text-right hidden sm:block">
<div className="text-white font-body font-semibold text-sm">{cat.thread_count}</div> <div className="text-[#0F172A] font-body font-semibold text-sm">{cat.thread_count}</div>
<div className="text-[#666] font-body text-xs">threads</div> <div className="text-[#666] font-body text-xs">threads</div>
</div> </div>
<div className="flex-shrink-0 text-right hidden md:block"> <div className="flex-shrink-0 text-right hidden md:block">
<div className="text-white font-body font-semibold text-sm">{cat.post_count}</div> <div className="text-[#0F172A] 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-[#1D4ED8] 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-[#1D4ED8] flex-shrink-0 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -624,9 +624,9 @@ export default function ForumIndexPage() {
)} )}
{/* Forum guidelines */} {/* Forum guidelines */}
<div className="mt-8 bg-[#FFFFFF] border border-[#2a3a50] rounded-lg p-5"> <div className="mt-8 bg-[#FFFFFF] border border-[#DCE6F2] rounded-lg p-5">
<h3 className="text-white font-heading font-semibold mb-2">Community Guidelines</h3> <h3 className="text-[#0F172A] font-heading font-semibold mb-2">Community Guidelines</h3>
<ul className="text-[#aab4c4] font-body text-sm space-y-1"> <ul className="text-[#475569] 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-[#1D4ED8]">AV Beat AI</span> assistant is available in threads to help answer questions it is clearly labeled</li> <li> The <span className="text-[#1D4ED8]">AV Beat AI</span> assistant is available in threads to help answer questions it is clearly labeled</li>

View File

@@ -182,7 +182,7 @@ export default function ForumUserPage() {
</span> </span>
)} )}
{profile.role_title && ( {profile.role_title && (
<span className="font-body text-[10px] font-bold uppercase tracking-wider text-[#9ca3af] bg-[#9ca3af]/10 border border-[#9ca3af]/30 px-2 py-1 rounded-sm"> <span className="font-body text-[10px] font-bold uppercase tracking-wider text-[#475569] bg-[#9ca3af]/10 border border-[#9ca3af]/30 px-2 py-1 rounded-sm">
{profile.role_title} {profile.role_title}
</span> </span>
)} )}
@@ -198,7 +198,7 @@ export default function ForumUserPage() {
</div> </div>
{profile.bio && ( {profile.bio && (
<p className="font-body text-sm text-[#bfbfbf] leading-relaxed mt-5 pt-5 border-t border-[#DCE6F2]"> <p className="font-body text-sm text-[#1e293b] leading-relaxed mt-5 pt-5 border-t border-[#DCE6F2]">
{profile.bio} {profile.bio}
</p> </p>
)} )}
@@ -206,7 +206,7 @@ export default function ForumUserPage() {
{profile.expertise_tags && profile.expertise_tags.length > 0 && ( {profile.expertise_tags && profile.expertise_tags.length > 0 && (
<div className="flex flex-wrap gap-2 mt-4"> <div className="flex flex-wrap gap-2 mt-4">
{profile.expertise_tags.map((tag) => ( {profile.expertise_tags.map((tag) => (
<span key={tag} className="font-body text-[11px] text-[#9ca3af] bg-[#222] border border-[#DCE6F2] px-2 py-0.5 rounded-sm"> <span key={tag} className="font-body text-[11px] text-[#475569] bg-[#F8FAFC] border border-[#DCE6F2] px-2 py-0.5 rounded-sm">
#{tag} #{tag}
</span> </span>
))} ))}
@@ -226,7 +226,7 @@ export default function ForumUserPage() {
<ul className="space-y-2 mb-8"> <ul className="space-y-2 mb-8">
{threads.map((t) => ( {threads.map((t) => (
<li key={t.id} className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-sm p-3 hover:border-[#1D4ED8]/50 transition-colors"> <li key={t.id} className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-sm p-3 hover:border-[#1D4ED8]/50 transition-colors">
<Link href={`/forum/thread/${t.id}`} className="font-body text-sm font-semibold text-[#e0e0e0] hover:text-[#1D4ED8] block"> <Link href={`/forum/thread/${t.id}`} className="font-body text-sm font-semibold text-[#0F172A] hover:text-[#1D4ED8] block">
{t.title} {t.title}
</Link> </Link>
<div className="flex items-center gap-3 mt-1 text-[11px] text-[#666]"> <div className="flex items-center gap-3 mt-1 text-[11px] text-[#666]">
@@ -255,7 +255,7 @@ export default function ForumUserPage() {
<Link href={`/forum/thread/${r.thread_id}`} className="font-body text-xs font-semibold text-[#1D4ED8] hover:underline"> <Link href={`/forum/thread/${r.thread_id}`} className="font-body text-xs font-semibold text-[#1D4ED8] hover:underline">
{r.forum_threads?.title || "(thread)"} {r.forum_threads?.title || "(thread)"}
</Link> </Link>
<p className="font-body text-sm text-[#bfbfbf] leading-relaxed mt-1 line-clamp-3">{r.body}</p> <p className="font-body text-sm text-[#1e293b] leading-relaxed mt-1 line-clamp-3">{r.body}</p>
<div className="mt-1 text-[11px] text-[#666]"> <div className="mt-1 text-[11px] text-[#666]">
<time>{new Date(r.created_at).toLocaleDateString()}</time> <time>{new Date(r.created_at).toLocaleDateString()}</time>
</div> </div>
@@ -269,12 +269,12 @@ export default function ForumUserPage() {
<div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-sm p-4 sticky top-4"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded-sm p-4 sticky top-4">
<h3 className="font-body text-xs font-bold uppercase tracking-wider text-[#888] mb-2">Profile facts</h3> <h3 className="font-body text-xs font-bold uppercase tracking-wider text-[#888] mb-2">Profile facts</h3>
<dl className="text-sm space-y-2"> <dl className="text-sm space-y-2">
<div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Role</dt><dd className="text-[#cccccc]">{profile.role_title || "—"}</dd></div> <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Role</dt><dd className="text-[#0F172A]">{profile.role_title || "—"}</dd></div>
{profile.company && <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Company</dt><dd className="text-[#cccccc]">{profile.company}</dd></div>} {profile.company && <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Company</dt><dd className="text-[#0F172A]">{profile.company}</dd></div>}
<div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Threads</dt><dd className="text-[#cccccc]">{profile.thread_count}</dd></div> <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Threads</dt><dd className="text-[#0F172A]">{profile.thread_count}</dd></div>
<div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Replies</dt><dd className="text-[#cccccc]">{profile.reply_count}</dd></div> <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Replies</dt><dd className="text-[#0F172A]">{profile.reply_count}</dd></div>
{profile.years_experience && <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Years in industry</dt><dd className="text-[#cccccc]">{profile.years_experience}</dd></div>} {profile.years_experience && <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Years in industry</dt><dd className="text-[#0F172A]">{profile.years_experience}</dd></div>}
<div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Member since</dt><dd className="text-[#cccccc]">{fmtDate(profile.join_date)}</dd></div> <div><dt className="text-[#666] text-[11px] uppercase tracking-wider">Member since</dt><dd className="text-[#0F172A]">{fmtDate(profile.join_date)}</dd></div>
</dl> </dl>
</div> </div>
</aside> </aside>

View File

@@ -9,6 +9,7 @@ import AppImage from "@/components/ui/AppImage";
import type { Article } from "@/lib/articles/types"; import type { Article } from "@/lib/articles/types";
import AISuggestedArticles from "@/components/AISuggestedArticles"; import AISuggestedArticles from "@/components/AISuggestedArticles";
import SidebarAdStack from "@/components/SidebarAdStack"; import SidebarAdStack from "@/components/SidebarAdStack";
import { cleanExcerpt } from "@/lib/articles/excerpt";
const CATEGORIES = ["All", "Cloud", "AI", "IP Workflows", "Live Production", "Streaming", "Audio", "Cameras", "NAB Show", "EAS", "Sports Broadcasting", "Ad Tech"]; const CATEGORIES = ["All", "Cloud", "AI", "IP Workflows", "Live Production", "Streaming", "Audio", "Cameras", "NAB Show", "EAS", "Sports Broadcasting", "Ad Tech"];
@@ -276,7 +277,7 @@ export default function NewsPage({
<h2 className="font-heading text-[#0F172A] text-base font-bold leading-snug mb-2 group-hover:text-[#1D4ED8] transition-colors line-clamp-2"> <h2 className="font-heading text-[#0F172A] text-base font-bold leading-snug mb-2 group-hover:text-[#1D4ED8] 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-[#475569] font-body text-sm leading-relaxed line-clamp-2">{cleanExcerpt(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 || "AV 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>

View File

@@ -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-[#FFFFFF] border border-[#1D4ED8] text-white px-4 py-3 rounded-sm text-sm font-body shadow-lg z-50"> className="fixed bottom-4 right-4 bg-[#FFFFFF] border border-[#1D4ED8] text-[#0F172A] px-4 py-3 rounded-sm text-sm font-body shadow-lg z-50">
{saveToast} {saveToast}
</div> </div>
)} )}

View File

@@ -17,40 +17,40 @@ export default function PrivacyPage() {
<Header /> <Header />
<div className="bg-[#111] border-b border-[#222] py-8"> <div className="bg-[#111] border-b border-[#222] py-8">
<div className="max-w-container mx-auto px-4"> <div className="max-w-container mx-auto px-4">
<h1 className="font-heading text-white text-3xl font-bold">Privacy Policy</h1> <h1 className="font-heading text-[#0F172A] text-3xl font-bold">Privacy Policy</h1>
<p className="text-[#888] font-body text-sm mt-2">Last updated: April 2026 Relevant Media Properties, LLC</p> <p className="text-[#475569] font-body text-sm mt-2">Last updated: April 2026 Relevant Media Properties, LLC</p>
</div> </div>
</div> </div>
<main id="main-content" className="max-w-container mx-auto px-4 py-10"> <main id="main-content" className="max-w-container mx-auto px-4 py-10">
<div className="max-w-3xl space-y-8 font-body text-[#aaa] leading-relaxed"> <div className="max-w-3xl space-y-8 font-body text-[#1e293b] leading-relaxed">
<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 AV Beat, Broadcast 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-[#0F172A]">Relevant Media Properties, LLC</strong> ("we," "us," or "our"), publisher of AV Beat, Broadcast 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>
<section aria-labelledby="section-1"> <section aria-labelledby="section-1">
<h2 id="section-1" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">1. Information We Collect</h2> <h2 id="section-1" className="font-heading text-[#0F172A] text-xl font-bold mb-3">1. Information We Collect</h2>
<h3 className="font-heading text-[#ccc] text-base font-semibold mb-2">Information You Provide</h3> <h3 className="font-heading text-[#1e293b] text-base font-semibold mb-2">Information You Provide</h3>
<p>We collect information you provide directly to us, such as when you subscribe to our newsletter, create an account, post in our forum, or contact us. This may include your name, email address, username, and professional information.</p> <p>We collect information you provide directly to us, such as when you subscribe to our newsletter, create an account, post in our forum, or contact us. This may include your name, email address, username, and professional information.</p>
<h3 className="font-heading text-[#ccc] text-base font-semibold mb-2 mt-4">Information Collected Automatically</h3> <h3 className="font-heading text-[#1e293b] text-base font-semibold mb-2 mt-4">Information Collected Automatically</h3>
<p className="mt-2">We also collect information automatically when you use our sites, including:</p> <p className="mt-2">We also collect information automatically when you use our sites, including:</p>
<ul className="list-disc list-inside mt-2 space-y-1 text-[#999]"> <ul className="list-disc list-inside mt-2 space-y-1 text-[#475569]">
<li>IP address and approximate geographic location</li> <li>IP address and approximate geographic location</li>
<li>Browser type, version, and operating system</li> <li>Browser type, version, and operating system</li>
<li>Pages visited, time spent, and referring URLs</li> <li>Pages visited, time spent, and referring URLs</li>
<li>Device identifiers and cookie data</li> <li>Device identifiers and cookie data</li>
<li>Interaction data (clicks, scrolls, video plays)</li> <li>Interaction data (clicks, scrolls, video plays)</li>
</ul> </ul>
<h3 className="font-heading text-[#ccc] text-base font-semibold mb-2 mt-4">Cookies and Tracking Technologies</h3> <h3 className="font-heading text-[#1e293b] text-base font-semibold mb-2 mt-4">Cookies and Tracking Technologies</h3>
<p className="mt-2">We use cookies, web beacons, and similar tracking technologies. You can control cookie preferences through our cookie consent banner or your browser settings. Essential cookies required for site functionality cannot be disabled.</p> <p className="mt-2">We use cookies, web beacons, and similar tracking technologies. You can control cookie preferences through our cookie consent banner or your browser settings. Essential cookies required for site functionality cannot be disabled.</p>
</section> </section>
<section aria-labelledby="section-2"> <section aria-labelledby="section-2">
<h2 id="section-2" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">2. How We Use Your Information</h2> <h2 id="section-2" className="font-heading text-[#0F172A] text-xl font-bold mb-3">2. How We Use Your Information</h2>
<p>We use the information we collect to:</p> <p>We use the information we collect to:</p>
<ul className="list-disc list-inside mt-2 space-y-1 text-[#999]"> <ul className="list-disc list-inside mt-2 space-y-1 text-[#475569]">
<li>Provide, maintain, and improve our editorial content and services</li> <li>Provide, maintain, and improve our editorial content and services</li>
<li>Send newsletters and editorial content you have subscribed to</li> <li>Send newsletters and editorial content you have subscribed to</li>
<li>Respond to your comments, questions, and support requests</li> <li>Respond to your comments, questions, and support requests</li>
@@ -59,73 +59,73 @@ export default function PrivacyPage() {
<li>Detect and prevent fraud, abuse, and security incidents</li> <li>Detect and prevent fraud, abuse, and security incidents</li>
<li>Comply with legal obligations</li> <li>Comply with legal obligations</li>
</ul> </ul>
<p className="mt-3"><strong className="text-[#ccc]">Legal basis (GDPR):</strong> We process your data based on consent (newsletter subscriptions, analytics cookies), legitimate interests (site security, fraud prevention), and contractual necessity (account management).</p> <p className="mt-3"><strong className="text-[#1e293b]">Legal basis (GDPR):</strong> We process your data based on consent (newsletter subscriptions, analytics cookies), legitimate interests (site security, fraud prevention), and contractual necessity (account management).</p>
</section> </section>
<section aria-labelledby="section-3"> <section aria-labelledby="section-3">
<h2 id="section-3" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">3. Information Sharing</h2> <h2 id="section-3" className="font-heading text-[#0F172A] text-xl font-bold mb-3">3. Information Sharing</h2>
<p>We do not sell your personal information. We may share information with:</p> <p>We do not sell your personal information. We may share information with:</p>
<ul className="list-disc list-inside mt-2 space-y-1 text-[#999]"> <ul className="list-disc list-inside mt-2 space-y-1 text-[#475569]">
<li><strong className="text-[#ccc]">Service providers</strong> who assist in operating our websites (hosting, email delivery, analytics)</li> <li><strong className="text-[#1e293b]">Service providers</strong> who assist in operating our websites (hosting, email delivery, analytics)</li>
<li><strong className="text-[#ccc]">Advertising partners</strong> for serving and measuring ad campaigns (subject to your consent)</li> <li><strong className="text-[#1e293b]">Advertising partners</strong> for serving and measuring ad campaigns (subject to your consent)</li>
<li><strong className="text-[#ccc]">Legal authorities</strong> when required by law or to protect our rights</li> <li><strong className="text-[#1e293b]">Legal authorities</strong> when required by law or to protect our rights</li>
<li><strong className="text-[#ccc]">Business successors</strong> in the event of a merger, acquisition, or sale of assets</li> <li><strong className="text-[#1e293b]">Business successors</strong> in the event of a merger, acquisition, or sale of assets</li>
</ul> </ul>
</section> </section>
<section aria-labelledby="section-4"> <section aria-labelledby="section-4">
<h2 id="section-4" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">4. Cookies Policy</h2> <h2 id="section-4" className="font-heading text-[#0F172A] text-xl font-bold mb-3">4. Cookies Policy</h2>
<p>We use the following categories of cookies:</p> <p>We use the following categories of cookies:</p>
<div className="mt-3 space-y-3"> <div className="mt-3 space-y-3">
<div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded p-3"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded p-3">
<p className="font-bold text-[#ccc] text-sm">Essential Cookies</p> <p className="font-bold text-[#1e293b] text-sm">Essential Cookies</p>
<p className="text-sm mt-1">Required for the website to function. Cannot be disabled. Includes session management and security tokens.</p> <p className="text-sm mt-1">Required for the website to function. Cannot be disabled. Includes session management and security tokens.</p>
</div> </div>
<div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded p-3"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded p-3">
<p className="font-bold text-[#ccc] text-sm">Analytics Cookies</p> <p className="font-bold text-[#1e293b] text-sm">Analytics Cookies</p>
<p className="text-sm mt-1">Help us understand how visitors interact with our site (Google Analytics). Requires your consent.</p> <p className="text-sm mt-1">Help us understand how visitors interact with our site (Google Analytics). Requires your consent.</p>
</div> </div>
<div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded p-3"> <div className="bg-[#FFFFFF] border border-[#DCE6F2] rounded p-3">
<p className="font-bold text-[#ccc] text-sm">Advertising Cookies</p> <p className="font-bold text-[#1e293b] text-sm">Advertising Cookies</p>
<p className="text-sm mt-1">Used to serve relevant ads and measure campaign performance. Requires your consent.</p> <p className="text-sm mt-1">Used to serve relevant ads and measure campaign performance. Requires your consent.</p>
</div> </div>
</div> </div>
</section> </section>
<section aria-labelledby="section-5"> <section aria-labelledby="section-5">
<h2 id="section-5" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">5. Your Rights</h2> <h2 id="section-5" className="font-heading text-[#0F172A] text-xl font-bold mb-3">5. Your Rights</h2>
<p>Depending on your location, you may have the following rights regarding your personal data:</p> <p>Depending on your location, you may have the following rights regarding your personal data:</p>
<ul className="list-disc list-inside mt-2 space-y-1 text-[#999]"> <ul className="list-disc list-inside mt-2 space-y-1 text-[#475569]">
<li><strong className="text-[#ccc]">Access:</strong> Request a copy of the personal data we hold about you</li> <li><strong className="text-[#1e293b]">Access:</strong> Request a copy of the personal data we hold about you</li>
<li><strong className="text-[#ccc]">Correction:</strong> Request correction of inaccurate or incomplete data</li> <li><strong className="text-[#1e293b]">Correction:</strong> Request correction of inaccurate or incomplete data</li>
<li><strong className="text-[#ccc]">Deletion:</strong> Request deletion of your personal data ("right to be forgotten")</li> <li><strong className="text-[#1e293b]">Deletion:</strong> Request deletion of your personal data ("right to be forgotten")</li>
<li><strong className="text-[#ccc]">Portability:</strong> Receive your data in a structured, machine-readable format</li> <li><strong className="text-[#1e293b]">Portability:</strong> Receive your data in a structured, machine-readable format</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-[#1e293b]">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-[#1e293b]">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@avbeat.com" className="text-[#1D4ED8] underline hover:text-blue-400 transition-colors">privacy@avbeat.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-[#1D4ED8] 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">
<h2 id="section-6" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">6. Data Retention</h2> <h2 id="section-6" className="font-heading text-[#0F172A] text-xl font-bold mb-3">6. Data Retention</h2>
<p>We retain personal data for as long as necessary to provide our services and comply with legal obligations. Newsletter subscriber data is retained until you unsubscribe. Account data is retained for the life of your account plus 90 days after deletion.</p> <p>We retain personal data for as long as necessary to provide our services and comply with legal obligations. Newsletter subscriber data is retained until you unsubscribe. Account data is retained for the life of your account plus 90 days after deletion.</p>
</section> </section>
<section aria-labelledby="section-7"> <section aria-labelledby="section-7">
<h2 id="section-7" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">7. Children's Privacy</h2> <h2 id="section-7" className="font-heading text-[#0F172A] text-xl font-bold mb-3">7. Children's Privacy</h2>
<p>Our services are not directed to children under 13 years of age. We do not knowingly collect personal information from children under 13. If you believe we have inadvertently collected such information, please contact us immediately.</p> <p>Our services are not directed to children under 13 years of age. We do not knowingly collect personal information from children under 13. If you believe we have inadvertently collected such information, please contact us immediately.</p>
</section> </section>
<section aria-labelledby="section-8"> <section aria-labelledby="section-8">
<h2 id="section-8" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">8. Changes to This Policy</h2> <h2 id="section-8" className="font-heading text-[#0F172A] text-xl font-bold mb-3">8. Changes to This Policy</h2>
<p>We may update this Privacy Policy from time to time. We will notify you of significant changes by posting a notice on our website or sending an email to newsletter subscribers. Your continued use of our services after changes take effect constitutes acceptance of the updated policy.</p> <p>We may update this Privacy Policy from time to time. We will notify you of significant changes by posting a notice on our website or sending an email to newsletter subscribers. Your continued use of our services after changes take effect constitutes acceptance of the updated policy.</p>
</section> </section>
<section aria-labelledby="section-9"> <section aria-labelledby="section-9">
<h2 id="section-9" className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">9. Contact Us</h2> <h2 id="section-9" className="font-heading text-[#0F172A] text-xl font-bold mb-3">9. Contact Us</h2>
<p>For privacy-related questions, data requests, or to report a concern:</p> <p>For privacy-related questions, data requests, or to report a concern:</p>
<address className="not-italic mt-3 space-y-1 text-[#999]"> <address className="not-italic mt-3 space-y-1 text-[#475569]">
<p><strong className="text-[#ccc]">Relevant Media Properties, LLC</strong></p> <p><strong className="text-[#1e293b]">Relevant Media Properties, LLC</strong></p>
<p>Privacy Officer</p> <p>Privacy Officer</p>
<p>Email: <a href="mailto:privacy@avbeat.com" className="text-[#1D4ED8] underline hover:text-blue-400 transition-colors">privacy@avbeat.com</a></p> <p>Email: <a href="mailto:privacy@avbeat.com" className="text-[#1D4ED8] underline hover:text-blue-400 transition-colors">privacy@avbeat.com</a></p>
</address> </address>

View File

@@ -35,7 +35,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
<span className="section-label">Search</span> <span className="section-label">Search</span>
<div className="flex-1 h-px bg-[#DCE6F2]" /> <div className="flex-1 h-px bg-[#DCE6F2]" />
</div> </div>
<h1 className="font-heading text-white text-3xl font-bold"> <h1 className="font-heading text-[#0F172A] text-3xl font-bold">
{query ? `Results for "${query}"` : "Search AV 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">
@@ -64,7 +64,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
<Link <Link
key={article.slug} key={article.slug}
href={`/news/${article.slug}`} href={`/news/${article.slug}`}
className="flex gap-4 py-5 border-b border-[#222] group hover:bg-[#111] transition-colors px-2 -mx-2" className="flex gap-4 py-5 border-b border-[#DCE6F2] group hover:bg-[#e2e8f0] transition-colors px-2 -mx-2 rounded-sm"
> >
<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
@@ -81,7 +81,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) {
{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-[#1D4ED8] transition-colors line-clamp-2"> <h2 className="font-heading text-[#0F172A] text-base font-bold leading-snug mb-2 group-hover:text-[#1D4ED8] 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">

View File

@@ -14,38 +14,38 @@ export default function TermsPage() {
<Header /> <Header />
<div className="bg-[#111] border-b border-[#222] py-8"> <div className="bg-[#111] border-b border-[#222] py-8">
<div className="max-w-container mx-auto px-4"> <div className="max-w-container mx-auto px-4">
<h1 className="font-heading text-white text-3xl font-bold">Terms of Service</h1> <h1 className="font-heading text-[#0F172A] text-3xl font-bold">Terms of Service</h1>
<p className="text-[#555] font-body text-sm mt-2">Last updated: March 2026</p> <p className="text-[#475569] font-body text-sm mt-2">Last updated: March 2026</p>
</div> </div>
</div> </div>
<main className="max-w-container mx-auto px-4 py-10"> <main className="max-w-container mx-auto px-4 py-10">
<div className="max-w-3xl space-y-8 font-body text-[#aaa] leading-relaxed"> <div className="max-w-3xl space-y-8 font-body text-[#1e293b] leading-relaxed">
<section> <section>
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">1. Acceptance of Terms</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">1. Acceptance of Terms</h2>
<p>By accessing and using AV Beat, you accept and agree to be bound by the terms and provisions of this agreement. If you do not agree to these terms, please do not use our service.</p> <p>By accessing and using AV Beat, you accept and agree to be bound by the terms and provisions of this agreement. If you do not agree to these terms, please do not use our service.</p>
</section> </section>
<section> <section>
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">2. Use of Content</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">2. Use of Content</h2>
<p>All content on AV Beat is the property of Relevant Media Properties or its content suppliers and is protected by applicable intellectual property laws. You may not reproduce, distribute, or create derivative works from our content without express written permission.</p> <p>All content on AV Beat is the property of Relevant Media Properties or its content suppliers and is protected by applicable intellectual property laws. You may not reproduce, distribute, or create derivative works from our content without express written permission.</p>
</section> </section>
<section> <section>
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">3. User Accounts</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">3. User Accounts</h2>
<p>If you create an account on AV Beat, you are responsible for maintaining the confidentiality of your account credentials and for all activities that occur under your account. You agree to notify us immediately of any unauthorized use of your account.</p> <p>If you create an account on AV Beat, you are responsible for maintaining the confidentiality of your account credentials and for all activities that occur under your account. You agree to notify us immediately of any unauthorized use of your account.</p>
</section> </section>
<section> <section>
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">4. Community Standards</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">4. Community Standards</h2>
<p>Users participating in our forum and community features agree to maintain respectful discourse. We reserve the right to remove content and suspend accounts that violate our community standards, including content that is abusive, defamatory, or otherwise harmful.</p> <p>Users participating in our forum and community features agree to maintain respectful discourse. We reserve the right to remove content and suspend accounts that violate our community standards, including content that is abusive, defamatory, or otherwise harmful.</p>
</section> </section>
<section> <section>
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">5. Disclaimer of Warranties</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">5. Disclaimer of Warranties</h2>
<p>AV Beat is provided "as is" without warranty of any kind. We do not warrant that the service will be uninterrupted or error-free. We make no warranties regarding the accuracy or completeness of any content on the site.</p> <p>AV Beat is provided "as is" without warranty of any kind. We do not warrant that the service will be uninterrupted or error-free. We make no warranties regarding the accuracy or completeness of any content on the site.</p>
</section> </section>
<section> <section>
<h2 className="font-heading text-[#e0e0e0] text-xl font-bold mb-3">6. Limitation of Liability</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">6. Limitation of Liability</h2>
<p>Relevant Media Properties shall not be liable for any indirect, incidental, special, consequential, or punitive damages resulting from your use of or inability to use the service.</p> <p>Relevant Media Properties shall not be liable for any indirect, incidental, special, consequential, or punitive damages resulting from your use of or inability to use the service.</p>
</section> </section>
<section> <section>
<h2 className="font-heading text-[#e0e0f0] text-xl font-bold mb-3">7. Contact</h2> <h2 className="font-heading text-[#0F172A] text-xl font-bold mb-3">7. Contact</h2>
<p>Questions about these Terms? Contact us at <a href="mailto:legal@avbeat.com" className="text-[#1D4ED8] hover:underline">legal@avbeat.com</a>.</p> <p>Questions about these Terms? Contact us at <a href="mailto:legal@avbeat.com" className="text-[#1D4ED8] hover:underline">legal@avbeat.com</a>.</p>
</section> </section>
</div> </div>

View File

@@ -168,7 +168,7 @@ export default function AISuggestedArticles({ variant = 'compact' }: Props) {
<Link <Link
key={article.slug} key={article.slug}
href={`/news/${article.slug}`} href={`/news/${article.slug}`}
className="flex gap-3 group hover:bg-[#111] transition-colors -mx-1 px-1 py-1 rounded-sm" className="flex gap-3 group hover:bg-[#e2e8f0] transition-colors -mx-1 px-1 py-1 rounded-sm"
> >
<div className="relative w-16 h-12 flex-shrink-0 overflow-hidden rounded-sm"> <div className="relative w-16 h-12 flex-shrink-0 overflow-hidden rounded-sm">
<AppImage <AppImage

View File

@@ -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 AV Beat archive.", "Hi — I'm AV Beat AI. Ask me anything about pro AV, live production, or display technology. I'll cite sources from the AV Beat archive.",
}, },
]); ]);
const [err, setErr] = useState<string | null>(null); const [err, setErr] = useState<string | null>(null);
@@ -68,13 +68,13 @@ export default function AskBBAI() {
type="button" type="button"
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
className="fixed bottom-16 right-6 z-40 bg-black text-white rounded-full pl-5 pr-5 py-3 shadow-2xl flex items-center gap-3 hover:bg-zinc-900 transition-colors" className="fixed bottom-16 right-6 z-40 bg-black text-white rounded-full pl-5 pr-5 py-3 shadow-2xl flex items-center gap-3 hover:bg-zinc-900 transition-colors"
aria-label="Ask BB AI" aria-label="Ask AV Beat 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,#60a5fa)]" />
<span className="bb-ring" /> <span className="bb-ring" />
</span> </span>
<span className="font-serif">Ask BB AI</span> <span className="font-serif">Ask AV Beat AI</span>
</button> </button>
)} )}
@@ -82,7 +82,7 @@ export default function AskBBAI() {
<div <div
ref={drawerRef} ref={drawerRef}
role="dialog" role="dialog"
aria-label="Ask BB AI" aria-label="Ask AV Beat AI"
className="fixed right-0 top-0 bottom-0 z-50 w-full sm:w-[420px] bg-[#0a0a0a] border-l border-[#DCE6F2] flex flex-col" className="fixed right-0 top-0 bottom-0 z-50 w-full sm:w-[420px] bg-[#0a0a0a] border-l border-[#DCE6F2] flex flex-col"
> >
<header className="flex items-center justify-between px-5 py-4 border-b border-[#DCE6F2]"> <header className="flex items-center justify-between px-5 py-4 border-b border-[#DCE6F2]">
@@ -91,7 +91,7 @@ export default function AskBBAI() {
<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,#60a5fa)]" />
<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">AV Beat AI</h2>
</div> </div>
<button <button
onClick={() => setOpen(false)} onClick={() => setOpen(false)}

View File

@@ -63,7 +63,7 @@ const footerColumns = [
export default function Footer() { export default function Footer() {
return ( return (
<footer className="bg-[#F8FAFC] border-t border-[#222]"> <footer className="bg-[#F8FAFC] border-t border-[#DCE6F2]">
{/* Footer Ad Row — site-wide 728x90 (rotates if multiple in pool) */} {/* Footer Ad Row — site-wide 728x90 (rotates if multiple in pool) */}
<div className="max-w-container mx-auto px-4 pt-8 pb-4"> <div className="max-w-container mx-auto px-4 pt-8 pb-4">
{pickAds("728x90", 1).length > 0 && ( {pickAds("728x90", 1).length > 0 && (
@@ -73,7 +73,7 @@ export default function Footer() {
)} )}
{/* Multi-column link section */} {/* Multi-column link section */}
<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-[#DCE6F2] 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-[#1D4ED8] uppercase tracking-widest mb-4 pb-2 border-b border-[#1D4ED8]/20"> <h4 className="font-body font-bold text-xs text-[#1D4ED8] uppercase tracking-widest mb-4 pb-2 border-b border-[#1D4ED8]/20">
@@ -87,13 +87,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-[#1D4ED8] transition-colors leading-relaxed inline-block"> className="footer-link font-body text-sm text-[#475569] hover:text-[#1D4ED8] 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-[#1D4ED8] transition-colors leading-relaxed inline-block"> className="footer-link font-body text-sm text-[#475569] hover:text-[#1D4ED8] transition-colors leading-relaxed inline-block">
{link?.label} {link?.label}
</Link> </Link>
} }
@@ -106,7 +106,7 @@ export default function Footer() {
{/* Footer Bottom */} {/* Footer Bottom */}
{/* DO NOT OVERRIDE — footer bottom bar layout */} {/* DO NOT OVERRIDE — footer bottom bar layout */}
<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-[#DCE6F2] 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="AV Beat — home" aria-label="AV Beat — home"
@@ -121,24 +121,24 @@ export default function Footer() {
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<a href="https://linkedin.com/company/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV 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-[#1D4ED8] transition-colors social-icon"> className="text-[#475569] hover:text-[#1D4ED8] transition-colors social-icon">
<LinkedInIcon size={17} /> <LinkedInIcon size={17} />
</a> </a>
<a href="https://instagram.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV 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-[#1D4ED8] transition-colors social-icon"> className="text-[#475569] hover:text-[#1D4ED8] transition-colors social-icon">
<InstagramIcon size={17} /> <InstagramIcon size={17} />
</a> </a>
<a href="https://twitter.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV 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-[#1D4ED8] transition-colors social-icon"> className="text-[#475569] hover:text-[#1D4ED8] transition-colors social-icon">
<TwitterXIcon size={17} /> <TwitterXIcon size={17} />
</a> </a>
<a href="https://facebook.com/avbeat" target="_blank" rel="noopener noreferrer" aria-label="AV 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-[#1D4ED8] transition-colors social-icon"> className="text-[#475569] hover:text-[#1D4ED8] 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-[#475569]">
<Link href="/privacy" className="hover:text-[#1D4ED8] transition-colors">Privacy Policy</Link> <Link href="/privacy" className="hover:text-[#1D4ED8] transition-colors">Privacy Policy</Link>
<Link href="/terms" className="hover:text-[#1D4ED8] transition-colors">Terms of Service</Link> <Link href="/terms" className="hover:text-[#1D4ED8] transition-colors">Terms of Service</Link>
<span> <span>

View File

@@ -298,7 +298,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-[#1D4ED8]" className="lg:hidden p-2 text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-[#1D4ED8]"
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}
@@ -340,7 +340,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-[#1D4ED8] hover:bg-[#FFFFFF] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#1D4ED8] flex items-center gap-2" className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#0F172A] hover:text-[#1D4ED8] hover:bg-[#FFFFFF] border-b border-[#DCE6F2] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#1D4ED8] 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" />
@@ -352,7 +352,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-[#1D4ED8] hover:bg-[#FFFFFF] border-b border-[#222] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#1D4ED8] flex items-center gap-2" className="px-2 py-2.5 text-sm font-body font-bold uppercase tracking-wide text-[#0F172A] hover:text-[#1D4ED8] hover:bg-[#FFFFFF] border-b border-[#DCE6F2] transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[#1D4ED8] 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" />

View File

@@ -338,7 +338,7 @@ function NotificationItem({ notification: n }: { notification: Notification }) {
if (dismissed) return null; if (dismissed) return null;
const content = ( const content = (
<div className={`group flex items-start gap-3 px-4 py-3 border-b border-[#1e1e1e] hover:bg-[#1e2a3a] transition-colors ${!n.is_read ? "bg-[#1a2030]" : ""}`}> <div className={`group flex items-start gap-3 px-4 py-3 border-b border-[#DCE6F2] hover:bg-[#e2e8f0] transition-colors ${!n.is_read ? "bg-[#EFF6FF]" : ""}`}>
{/* Type icon */} {/* Type icon */}
<div className={`flex-shrink-0 mt-0.5 ${cfg.color}`}>{cfg.icon}</div> <div className={`flex-shrink-0 mt-0.5 ${cfg.color}`}>{cfg.icon}</div>