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>
P0-2 (Adsanity iframe path removed):
- AdImage.tsx: deleted the iframe branch and the ADSANITY_CAMPAIGN_MAP
fallback. AdImage now renders local images only; banners without a
src render nothing.
- ads.ts: removed Ad.adsanity_id from the interface. Pruned AJA, Zixi,
Lectrosonics, Opengear, Studio Hero, Magewell 300x250 and Blackmagic
300x600 — they relied on ads.broadcastbeat.com which does not resolve.
Surviving live banners: LiveU 728x90, LiveU PAYG 300x250, Telycam 300x250.
- SidebarAdStack: key no longer references the removed field.
- No banner_analytics rows reference ads.broadcastbeat.com.
Phase A (SEO baseline):
- robots.ts: fallback base URL now broadcastbeat.com (was rocket staging URL).
- sitemap.ts: fallback base URL now broadcastbeat.com (was bb-staging).
- news/[slug]/page.tsx and articles/[slug]/page.tsx: same fallback fix.
JSON-LD now uses ISO 8601 for datePublished, absolute image URL via
SITE_URL prefix, and publisher.logo points at /assets/images/logo.png
instead of the 404-ing /legacy/site/broadcastbeat-logo.png.
- layout.tsx: removed the /en /es /pt /fr /de /zh /ja /ar /hi hreflang
alternates - those routes 404 today; Phase D will reinstate. Replaced
rocket.new Organization schema URL with broadcastbeat.com. Set
robots index/follow=false until Phase B-D land and Rich Results passes.
Featured-image fallback hardening:
- legacy-source: rowToArticle now treats BB_Gray, no_image and placeholder
URLs as null and falls back to the new placeholder so dead images do not
leak through to the page.
- public/assets/images/article-placeholder.svg: new clean dark gradient
with BroadcastBeat wordmark, NOT no_image.png style.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploy-1 build failed because NewsPageClient now calls useSearchParams.
- src/app/news/page.tsx: wrap NewsPageClient in <Suspense> so static
generation can bail out to client rendering cleanly.
P0-6 (Featured carousel arrows):
- FeaturedBento: brighten arrow buttons (text-[#ccc] over bg-[#1a1a1a]
with #3a3a3a border; previously near-invisible #666 over the same
background), enlarge to w-8 h-8, hover fills to blue. Wrap the
arrow group with role/aria + Left/Right keyboard nav.
P0-7 (728x90 reposition):
- Header: move the leaderboard block from above the main nav to AFTER
the sticky nav, so it sits between the nav and The Beat ticker.
P0-8 (remove Latest search box):
- ArticleFeed: drop the in-section search input + clear button. The
top-right header search is now the sole search UI. searchQuery state
remains since the active-filter chip code still references it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
P0-3 (category page crash):
- Add src/app/error.tsx and src/app/global-error.tsx so any future render
error shows a real ref id instead of the bare client-exception toast.
- NewsPageClient: null-guard every field accessed during search/category/
sort filtering and article render (title, excerpt, author, tags,
category, date, image, alt). The user-visible link
/news?category=live-production now also hydrates filter state from
?category= and ?search= search-params, so the dropdown actually filters.
P0-4 (/articles/[slug] 404s):
- /articles/[slug] and /news/[slug]: on slug miss, redirect("/news")
instead of notFound(). Dead links from the hardcoded NewsTicker /
FeaturedBento / ArticleFeed arrays now land on the news index instead
of dead-ending on 404.
- legacy-source: add searchLegacyArticles() (title/excerpt/author ilike).
P0-5 (header search):
- Header: wire Enter on the desktop and mobile search inputs and make
both search icons clickable buttons. Submits to /search?q=<query>.
- New /search route: server component that runs searchLegacyArticles
and renders results in the same card style as /news.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Banner spec from Ryan:
- 728x90 header (every page): pool is now LiveU only; rotates when more
728x90 entries are added to ads.ts.
- 728x90 below main menu on article pages: removed (was the article-top
AdSlot stub).
- 728x90 footer (every page): replaces the old 300x250 rocket.new
placeholder in Footer.tsx. Rotates from the same ADS_728X90 pool.
- 300x250 sidebar: now renders EVERY entry in ADS_300X250 stacked
vertically, shuffled per page-view. Pool: AJA, Zixi, Telycam, LiveU,
Lectrosonics, Opengear, Studio Hero, Magewell. Adding more entries to
ads.ts auto-grows the stack.
- 300x600 fixed slot: Blackmagic (Adsanity ad-210571), top of sidebar,
never rotated.
Implementation:
- ads.ts: Ad interface now allows either src (local image) or adsanity_id
(iframe). New rotateAll() returns ALL ads of a size shuffled; pickAds
kept for ArticleBody bounded slots.
- AdImage.tsx: dropped the legacy ADSANITY_CAMPAIGN_MAP label fallback —
ads.ts is now the single source of truth, so Telycam (no adsanity_id)
renders its local GIF instead of the iframe.
- SidebarAdStack.tsx: rotateAll(300x250) instead of pickAds with a
count cap. count prop removed; callers updated.
- NewsArticleDetailClient.tsx: removed article-top 728x90 stub.
- Footer.tsx: 728x90 AdImage in place of the 300x250 placeholder.
- LeaderboardAd.tsx: deleted (was unused after the earlier home-page
cleanup).
- PENDING_ads.md: rewritten — Adsanity IDs to verify (Studio Hero,
Blackmagic) and optional local-file upgrades.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the placeholder Unsplash banner array with the live AdSanity
inventory served from /legacy/ads/ (same 27 active creatives currently
on broadcastbeat.com).
New:
- src/lib/ads.ts: ad inventory (1 fixed Blackmagic 300x600, 22 rotating
300x250s, 4 leaderboard 728x90s) + Fisher-Yates shuffle + pickAds()
helper that draws N unique ads excluding any caller-supplied src set.
- src/components/AdImage.tsx: renders one ad as <a target=_blank rel=
sponsored noopener noreferrer><AppImage>.
- src/components/SidebarAdStack.tsx: Blackmagic 300x600 fixed on top,
then count rotating 300x250s. Memoizes the rotation per render so a
refresh re-shuffles. Accepts excludeSrcs to coordinate with other
slots on the same page.
- src/components/ArticleBody.tsx: splits article HTML at </p>
boundaries, splices a 300x250 ad after every Nth paragraph (default
4). Same exclude-set pattern.
Wiring:
- ArticleFeed.tsx: remove the inline sidebarAds array (was 5 Unsplash
placeholders + 1 LiveU 300x250). Sidebar now <SidebarAdStack count=5/>.
- NewsArticleDetailClient.tsx: sidebar 300x600 + 300x250 AdSlot
placeholders replaced with <SidebarAdStack count=4/>. Article body
innerHTML replaced with <ArticleBody html=... everyN=4/> so
300x250 ads slot in between paragraphs.
Same banners as the live broadcastbeat.com (sourced from the existing
WP AdSanity install we cached locally during Phase 2). Each <a> opens
in a new tab and uses rel='sponsored noopener noreferrer'.
Eliminates 72 hardcoded rocket.new image refs in one shot. Section pages
(/gear, /technology, /show-coverage, /news) now render from
bb.wp_imported_posts via getLegacyArticlesBySection(section), which maps
each non-news section to a curated tag set (e.g. gear -> Audio, Cameras,
lighting, Blackmagic Design, monitoring; technology -> AI, Streaming,
OTT, IP, Cloud, AV; show-coverage -> NAB, IBC, BroadcastAsia).
Also:
- src/lib/articles/types.ts: Article interface lifted out of the deleted
seed file. Type-only consumers (NewsArticleDetailClient,
ArticleDetailClient, legacy-source) re-imported from here.
- /news (client component) now wrapped: server fetches articles from DB
and passes to NewsPageClient. Filter UI unchanged.
- generateStaticParams on /news/[slug] and /articles/[slug] no longer
references seed slugs; pre-renders 50 most-recent imported slugs and
relies on dynamicParams + revalidate=3600 for the rest.
- sitemap.ts now sources article URLs from
getLegacyRecentSitemapEntries() (up to 5000 most-recent imported posts).
Default BASE_URL fallback updated from the rocket.new placeholder to
bb-staging.onsethost.com.
- src/app/api/ai/article-suggestions/route.ts now pulls candidates from
the DB (top 100 recent news) and resolves AI-returned slugs via DB
lookup if not in the candidate window.
Inline rocket.new image refs in homepage components (ArticleFeed,
FeaturedBento) are unchanged in this commit; those are inline seed
arrays in the components, not imports of sampleArticles.
- new src/lib/articles/legacy-source.ts: server-side Supabase reader
(bb schema, anon key, RLS public_read policy) that maps imported rows
to the existing Article shape, applies rewriteLegacyImageUrl for the
featured image and rewriteLegacyImageUrlsInHtml across the body.
- both [slug]/page.tsx routes now: try seed sampleArticles first; on
miss fall back to getLegacyArticleBySlug. Related articles match via
same-category recent posts when imported.
- generateStaticParams pre-renders seed slugs + 50 most recent imported
posts. dynamicParams=true means anything else server-renders on first
request and ISR caches it for 1h (revalidate=3600). Avoids a 4577-page
build.
- Hardcoded builtwithrocket.new URLs in JSON-LD replaced with
process.env.NEXT_PUBLIC_SITE_URL.
Adds <AdSlot zone size /> server component that renders a labeled
placeholder for now (size + zone exposed as data attrs for the future
bb.ad_placements lookup).
Inserted on news/[slug] article template:
- 728×90 leaderboard above the <article>
- 300×600 half-page at top of sidebar
- 300×250 mid-rectangle in sidebar (replaces the old hand-rolled
placeholder div)
- 728×90 leaderboard below the </article>, before related-articles
Same scheme should be applied to articles/[slug]/page.tsx in a follow-up
once that template's body is finalized.
Drops the rendered date span from:
- ArticleFeed.tsx (homepage feed)
- news/page.tsx (news list — also drops the orphan separator)
- news/[slug]/NewsArticleDetailClient.tsx (article header + related-articles row, drops paired separators)
- AISuggestedArticles.tsx (sidebar — keeps readTime alone)
JSON-LD datePublished/publishedTime in news/[slug]/page.tsx and
articles/[slug]/page.tsx is unchanged so search engines still see the
publication date.