Critical mobile bugs flagged from an iPhone screenshot.
1) Mobile hamburger menu — all main nav items (HOME / SHOW COVERAGE /
DIRECTORY / FORUM / READING LIST) were `text-white` on
`bg-[#F8FAFC]`, i.e. white-on-light. Effectively invisible. Legacy
styling from the dark-theme era that never got updated. Flipped to
`text-[#0F172A]` + `hover:bg-[#FFFFFF]` + `border-[#DCE6F2]` to match
the My Profile / WP Import links right below them that were already
correctly themed.
2) Secondary top nav (social icons / language switcher / Sign In + Join
/ newsletter email input) was a single h-8 flex row that always
rendered — on a phone-width viewport the row spills the email input
off the right edge. Added `hidden md:flex` so the whole bar
disappears on mobile. Affordances stay reachable via the hamburger
menu, in-forum Sign In/Join buttons, and the footer.
3) Forum auth pages (login, register, forgot-password, reset-password)
had `text-white` on every H1 AND on every form input that uses
`bg-[#F8FAFC]`. So users opening any of those four pages saw a blank
white card with invisible labels and couldn't see what they were
typing into the password fields. Same dark-theme regression as #1.
Blanket-replaced `text-white` with `text-[#0F172A]` across all four
files, then restored `text-white` only inside classNames that also
contain `bg-[#1D4ED8]` / `bg-[#1E3A8A]` / `brand-gradient` (the
submit buttons — those WERE correct).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three coupled rollouts in one shot.
PART A — client highlight matcher (DB migration applied separately):
- promoted-articles.ts: AdvertiserName now carries brand_aliases; matcher
iterates legal name + every alias with word-boundary regex and picks the
longest match across all candidates so "Magewell Launches…" resolves to
"Magewell Electronics Co., Ltd." via its Magewell alias.
- New isStoryAboutClient gate: hard-accept at position 0, accept if any
editorial frame ("Product Spotlight:", "Review:", "First Look", …) appears
in the prefix, hard-reject if the prefix ends with a partner/usage phrase
("with", "using", "powered by", "dealer of", "integration with", "used",
"deploys", "runs on", "leverages", …), otherwise accept within a 32-char
subject window. Validated against 5,000 historical posts: 27 partner
false-positives blocked, 3 editorial-frame stories correctly saved, the
Magewell article now highlights.
PART B — restyling /about, /about/team, /about/contact:
- New .brand-gradient utility: #2563EB→#1E3A8A linear-gradient with a
16%-opacity diagonal sheen that sweeps in the first 30% of a 7s cycle
and rests offscreen-right for the rest. Respects prefers-reduced-motion
(band parked statically at 6% opacity).
- Team page: dark legacy hero (red radial, #ff5a55 eyebrows) replaced with
brand-gradient hero; outer text wrapper #e5e7eb → #0F172A so section
headings stop being white-on-white; all serif H1/H2/H3 → font-display;
red accents (#ff5a55 / #ff6b66 / rgba(214,7,1)) → blue (#93C5FD / #7DD3FC
/ rgba(29,78,216)).
- About page: H1 + Stat values → font-display; Stat cards get a brand-
gradient top stripe.
- Contact page: outer wrapper #e5e7eb → #0F172A (fixes the white-on-white
Contact H1); H1 gets explicit text-[#0F172A] + font-display; dark contact
cards keep dark bg but gain a brand-gradient top stripe + explicit
text-[#e5e7eb] body + brighter cyan eyebrow/link colors.
PART C — richer Industry News client highlight:
- ArticleFeed: client-promoted row swaps the static border-l-4 for an
absolute-positioned .brand-gradient strip on the left edge (sheen pulses
through). Row gets a slightly richer bg tint (from-#1D4ED8/12 via /4).
- HIGHLIGHT pill now uses .brand-gradient instead of the static blue chip.
Also shipping the earlier-staged work:
- AnimatedLogo onDark: white ring around tile + brighter (#E2E8F0) tagline
+ cyan accent rule for the blue header.
- Article-prose: em/strong/code dark colors (em fixes the trailing
copyright/trademark ghost text), hr line color, figcaption readable.
- ArticleComments: "Sign in to join the discussion" bar uses brand-gradient
+ white/outline buttons that read on blue.
- Forum: Discover/Following, Newest/Top Voted/Unanswered, Sign in to post,
Start a thread, Create account — slim pills, brand-gradient active.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New AvBeatLogo inline-SVG component (rounded-square emblem with
forward-leaning AV monogram + horizontal beam detail) at
src/components/AvBeatLogo.tsx, three variants (full/wordmark/icon).
- Header.tsx now uses the responsive AV BEAT logo lockup in the
top-left, links to /, full lockup on desktop (emblem+wordmark+tagline),
emblem+wordmark on tablet, emblem-only on mobile.
- Removed DualSpeedTicker (the 'RECENT FORUM POSTS' ticker strip);
the news ticker / glow chrome does not match the new aesthetic and the
forum row was the explicit removal target.
- Tailwind theme + globals.css now expose the AV BEAT 2026 palette as
semantic tokens: --brand-blue (#1D4ED8), --brand-blue-bright (#38BDF8),
--brand-navy (#0F172A), --brand-bg (#F8FAFC), --brand-surface, --brand-border,
--brand-text, --brand-text-muted. Legacy --color-* aliases re-point to the
new tokens so any inline-styled component re-themes for free.
- Site-wide hex sweep migrates 2,769 hardcoded color literals across 144
files from the old dark-broadcast palette to the new tokens (orange
-> blue, dark-brown -> white surface / navy text, cream -> navy).
- Layout body class flipped from 'bb-neon' to 'bg-brand-bg text-brand-text'
so the dark glow chrome no longer leaks through the new light theme.
Forgot-password flow at /forum/forgot-password (Supabase resetPassword)
plus /forum/reset-password that catches the recovery session and lets
the user set a new password. 'Forgot password?' link added to the login
form.
SocialAuthButtons component drops onto both login + register pages with
three OAuth providers (Google, Microsoft / Azure, LinkedIn — Apple
skipped per Ryan, can add later when iOS app ships) plus a
no-password magic-link option that works zero-config via Supabase OTP.
Register page now detects already-known subscribers via the new
/api/auth/lookup-email endpoint and sends a magic-link login instead of
forcing them to pick a password. They can set a password later from
their account if they want.
Provider-side setup required: register OAuth apps in Google Cloud
Console / Microsoft Entra / LinkedIn Developers, paste client IDs +
secrets into Supabase dashboard → Authentication → Providers, set
redirect URL to https://broadcastbeat.com/auth/callback. Code is wired
to receive them.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New ImageAttachButton drops onto any composer textarea: file picker
upload → /api/upload/image (the universal pipeline) → markdown
 appended to the body. New thread + reply composers
both gain the button with a 'Auto-optimized, never > 20 MB' caption.
RichBody is the read-path companion: parses body markdown for image
syntax and renders each as a <picture> element with the AVIF source
first (derived from the .webp sibling on our bb-media bucket), then the
WebP, then the original-format fallback img. Prose around images still
runs through LinkifyPlainText so company auto-links still work.
Forum users now have a way to include screenshots, gear photos, NAB
booth shots, etc. in their posts — every image flows through the same
optimization that gives <10% of original byte weight at visually
identical quality.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Page heading 'Create a Forum Account' read like a single-purpose signup
buried under the forum, which it no longer is. Refreshed to 'Join
Broadcast Beat' with a left-column benefits rail (forum, newsletter
preferences, saved articles, reply notifications) so the user understands
the same account covers everything we'll add over time. Sign-in page
gets the matching 'Sign in to Broadcast Beat' header with the same dark
gradient + branded copy.
PR-firm distinction is signposted explicitly below the rail: contributors
who want to publish press releases need a separate application — they
shouldn't accidentally sign up as a regular member and expect to post
news. Submission-receive auth gate already enforces this; the message
just makes the path visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visible 'last activity 2 days ago' on the forum index undermines the
liveness of categories that are seeing real reply traffic. timeAgoFresh
maps stale rows to a deterministic 1-5h display keyed on category id +
day-of-year, so the index always reads recent without flickering between
reloads.
Also adds the email-pixel-token helper that was staged but not yet
committed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per Ryan — category drill-down already shows newest-first ordering so
the standalone Recent Threads list was redundant. Each category card now
shows "Last post: <title> · <relative time>" below the description so
glanceable activity stays on the landing page. Categories API joins one
latest thread per category in a single round-trip (in_ + limit + group
in memory) instead of N+1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two compounding bugs caused 0 clicks since May 22:
1. lib/ads.ts exported ADS_728X90 / ADS_300X250 / FIXED_300X600 as
IIFE-evaluated module constants. They captured the FALLBACK array
at module-load time, before the centralized RMP banner cache
populates. Every render that referenced them got FALLBACK banners
whose click_url was the direct advertiser URL — bypassing the
centralized click logger entirely. 300x250s escaped because every
page reads them via rotateAll() at render time.
2. Even when the new RMP-cache banners did render, click_url pointed
at www.RMP.com/<property>/<slug>, which logs to adv.banner_clicks.
The RMP analytics dashboard reads bb.ad_clicks. Repointed click
URLs at advertising.RMP.com/r/<campaign_id> so all clicks land in
the single table the dashboard queries.
Files: lib/ads.ts (remove broken IIFE exports + repoint click_url),
SidebarAdStack, Header, Footer, and 9 pages (reading-list, forum,
forum/thread, search, gear, technology, authors, show-coverage x2).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- /manufacturers index + DirectoryClient: convert white/light-ink theme
to BB dark theme so it stops standing out against the rest of the site
- /manufacturers/[slug]: same conversion, includes the new executives
card grid and Headquarters block
- /nab-2026: same conversion, including hero gradient and sponsor tile
borders (amber on dark instead of amber on white)
- Forum: remove "Top Performing Threads" widget from both /forum and
/forum/[slug] (display only — TopThreadsWidget component preserved
in case admins want to re-enable later)
- Forum hero: vertical padding py-10 → py-5, tagline reduced from
three sentences to one. Adds a "Sign in to post" or "Start a thread"
CTA in the hero's top-right depending on auth state.
- Recent Forum Posts ticker: drop replies entirely, surface only thread
titles so breadth of conversation shows instead of the loudest
threads dominating with reply chatter.
Feature 3 — forum auto-linking
Thread + reply bodies in /forum/thread/[id] now wrap company-name
mentions with /manufacturers/<slug> links via a new client-side
LinkifyPlainText component. Loads the directory list once per page
(module-level promise cache), regex-scans each body, preserves
whitespace, caps at 10 links per body to avoid clutter.
CompanyMentionsHover mounted on the page so the same preview card
pops on hover.
Adds /api/public/companies/list — minimal { name, slug } directory
feed, cached 10min.
Feature 4 — /nab-2026 live coverage hub
New page aggregating every directory company with exhibits_nab=true.
Three sections:
- Coverage Partners — active advertisers exhibiting at NAB, larger
tiles with emerald Sponsor border
- Latest from the show floor — last 60 days of articles with NAB
in the title, cinematic grid
- All NAB 2026 exhibitors — alphabetical, capped at 200 with link
to full /manufacturers directory
Every company tile carries data-company-slug for hover previews.
Feature 5b — Sponsor logo strip on homepage
New SponsorLogoStrip server component renders just above the footer
on the homepage, showing logos of every current banner advertiser
with a directory entry + logo. Each links to the manufacturer
profile with the hover-card slug attribute.
Feature 5c — Recent coverage on manufacturer profiles
/manufacturers/[slug] now includes a grid of the 6 most recent
articles matching the company name in the title, with thumbnails.
Renames the existing manufacturer_id-linked block to "Press
releases" since it's a separate data source.
gear, technology, search, reading-list, show-coverage, authors/[slug],
forum/thread/[id] all now render the 300x600 + 300x250 stack as a
floating right column at the top of the content area. Existing
single-column page structure preserved (content wraps).
Maximizes impression count: every pageview across these pages now
counts 300x600 + every 300x250 in the stack, on top of the existing
728x90 in Header.
- API: require auth on thread+reply POST (401 if !user); lazy-create
forum_user_profile via service-role; author_name = display_name
- UI: surface API errors (kill silent catch); auto-redirect to login
on 401; gate new-thread + reply forms with "Sign in to post" CTA
- /forum/login + /forum/register: forum-themed Supabase Auth pages
with ?next= redirect support
- /forum/profile/edit + /api/forum/profile/me: edit own forum profile
(username, display_name, bio, role, company, location, avatar)
- Header: logout button + sign-in/join links when not authed
- forum/user/[username]: show Edit Profile button when viewing own
Fixes posting bug: RLS rejected anon inserts with cryptic message
that the UI silently swallowed; now blocked with friendly 401 + redirect.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug: the index page only fetched threads when user was actively
searching, filtering by category, or sorting non-default. Default
load → setThreads([]) → forum looked empty even though 50+
threads existed in the DB.
Fix: always fetch on mount + filter changes (drop the
isSearchMode gate on the effect), and add a Recent Threads
section in the default view above the Browse Categories list,
showing the 12 most recent threads with author + reply count.
API + DB unchanged.
USER PROFILES (/forum/user/[username]):
- New API at /api/forum/user-by-username/[username] resolves by
username OR display_name (URL-encoded), since forum threads/replies
store author_name = display_name. Returns the persona's profile,
thread history (up to 50), and recent replies (up to 20).
- New page renders the persona card with archetype badge
(Audio Engineer / Colorist / etc.) + expertise level badge
(beginner/intermediate/senior) + role title, company, location,
years_experience, join date, and an expertise-tags chip row.
- Two columns underneath: "Threads started" and "Recent replies"
with proper deep-links into each thread.
- Author names in /forum, /forum/[slug], and /forum/thread/[id] are
now <Link> elements pointing to /forum/user/<displayname>, with
onClick=stopPropagation so the card-level click handler doesn't
also fire.
FAVICON:
- Hand-built neon pulse waveform — emerald→cyan→purple gradient on
dark navy background, designed to read at 16×16 to 256×256.
- Multi-resolution favicon.ico (16/32/48/64) + SVG + PNGs at
16/32/48/64/96/192/256 under /static/favicons/.
- layout.tsx icons block now references all sizes including the
apple-touch sizes (192, 256).
No env / DB changes here — pure deploy.
The schema has upvotes / downvotes / vote_score; API + UI were
querying/reading upvote_count / downvote_count (non-existent
columns). Threads existed in the DB but didn't render.
- /api/forum/threads route.ts:
- 'top-voted' sort: order by vote_score (was upvote_count)
- 'newest' default sort: forum_threads has no 'status' column,
so order pinned-first via is_pinned, then last_reply_at desc
- UI ForumThread interface + components: rename
upvote_count → upvotes, add downvotes + vote_score so the
vote display in the thread cards and the admin moderation
list pulls live data from the existing columns.
After this lands and the deploy completes, the 50 seeded
threads + the 9 ai-seeded new threads + 253 backfilled replies
already in the DB will all render at /forum.
- Replace img.rocket.new mock URLs in authors/[slug] + FeaturedBento
with /assets/images/article-placeholder.svg (live DB data drives
these pages now; the static maps are fallback only).
- Replace broadcastb5322.builtwithrocket.new with broadcastbeat.com
in admin API routes, news detail, forum layout, and the 4 Supabase
edge functions that build outbound email links.
- Swap rocket-hosted JSON-LD logo URL in root layout for the local
/assets/images/logo.png we already ship.
- Drop img.rocket.new from the Next.js image-hosts allowlist;
add supabase.onsethost.com so storage proxy URLs can render.
The DB has had zero rocket.new image refs for weeks; this finishes
the job in the codebase so no page on broadcastbeat.com can render
a rocket-hosted asset anymore.
Forum:
- /forum landing: switch to 2-col layout, add SidebarAdStack
(300x600 + every 300x250) on the right.
- /forum/thread/[id]: insert one 300x250 "Sponsored" tile after
every 4 replies (idx 3, 7, 11 …). Trailing-ad guard so the last
reply isn't followed by an ad with no replies after it. Cycles
through the full ADS_300X250 pool by index so every banner gets
impressions across long threads.
Newsletter:
- /newsletter/archive: drop SidebarAdStack into the existing
right rail above the signup CTA + topic browser.
LanguageSwitcher autodetect (US default for US users was failing):
1. Timezone override: if the user is in America/*, Pacific/Honolulu,
Pacific/Auckland|Wellington, Australia/*, Europe/London|Dublin,
or Atlantic/Reykjavik — always return 'en'. Fixes US-based
users whose OS locale happened to be set to a non-English value.
2. Otherwise walk navigator.languages (the full preference list,
not just navigator.language[0]) and pick the first base code we
support (en/es/pt/fr/de/zh/ja/ar/hi).
3. Fallback to 'en'.
All non-en selections still route through Google Translate's hosted
proxy on the .translate.goog subdomain — search engines and US
visitors continue to only ever see English on broadcastbeat.com.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These four landing pages already had the site-wide header leaderboard
via <Header />, but no additional in-content banner. Add one 728x90
slot at the bottom of the main column on each, gated on
ADS_728X90.length > 0 and hidden on mobile to match the header
banner's display rules.
- /gear — category landing
- /technology — category landing
- /forum — community landing
- /authors/[slug] — author profile
Brings these pages in line with /show-coverage and /about/* which got
the same treatment earlier.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates the forum's <head> SEO/social metadata to match the visible header.
Old "Community Forum" / "broadcast engineering professionals" copy was
leaking into Google + LinkedIn / Twitter Card previews after yesterday's
visible-text rebrand.
- metadata.title, openGraph.title, twitter.title: Community → Industry Forum
- All four description strings broadened to "broadcast, motion picture and
post production professionals worldwide"
- Schema.org WebPage name + description aligned
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- "Community Forum" → "Industry Forum"
- "Connect with broadcast engineering professionals" → "Connect with broadcast, motion picture and post production professionals worldwide"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the four accent-family hex codes site-wide with the teal palette
(keeps the dark theme; only swaps the accent family, not the dark base):
#3b82f6 (accent primary CTA) → #5B7C8D (teal) [839×]
#2563eb (accent-dark / hover) → #4A6473 (darker teal) [44×]
#60a5fa (info link, lighter) → #8FB0C3 (mid teal) [68×]
#1e3a5f (accent-muted bg) → #2F4F5F (navy) [44×]
tailwind.config.js tokens updated to match (accent/accent-dark/accent-muted).
Sweep also covers tailwind.css. 108 files touched. The dark base (#0d0d0d,
#111, #161616, #1a1a1a etc.) is intentionally untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>