diff --git a/src/app/home-page/page.tsx b/src/app/home-page/page.tsx
index b179687..5c0d830 100644
--- a/src/app/home-page/page.tsx
+++ b/src/app/home-page/page.tsx
@@ -4,7 +4,7 @@ import Header from "@/components/Header";
import Footer from "@/components/Footer";
import FeaturedBentoFromDb from '@/components/FeaturedBentoFromDb';
import FeaturedCarouselServer from '@/components/FeaturedCarouselServer';
-import LiveWireTicker from '@/components/LiveWireTicker';
+import DoubleTicker from '@/components/DoubleTicker';
import NewsTicker from "./components/NewsTicker";
import FeaturedBento from "./components/FeaturedBento";
import SpotlightCarousel from "./components/SpotlightCarousel";
@@ -147,10 +147,10 @@ export default function HomePage() {
Broadcast Beat — Broadcast Engineering News & Insights
- {/* Scrolling news ticker */}
+ {/* Scrolling news + forum tickers */}
}>
-
+
diff --git a/src/components/DoubleTicker.tsx b/src/components/DoubleTicker.tsx
new file mode 100644
index 0000000..a76a06f
--- /dev/null
+++ b/src/components/DoubleTicker.tsx
@@ -0,0 +1,93 @@
+import Link from "next/link";
+import { createClient } from "@supabase/supabase-js";
+
+export const dynamic = "force-dynamic";
+
+interface TickerItem {
+ href: string;
+ title: string;
+}
+
+export default async function DoubleTicker() {
+ const sb = createClient(
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
+ {
+ db: { schema: (process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb") as "public" },
+ auth: { persistSession: false },
+ }
+ );
+
+ const [wireRes, forumRes] = await Promise.all([
+ sb
+ .from("wp_imported_posts")
+ .select("wp_slug, title")
+ .eq("status", "published")
+ .order("wp_published_at", { ascending: false, nullsFirst: false })
+ .limit(14),
+ sb
+ .from("forum_threads")
+ .select("id, title")
+ .order("last_reply_at", { ascending: false, nullsFirst: false })
+ .limit(14),
+ ]);
+
+ const wire: TickerItem[] = (wireRes.data || []).map((r: any) => ({
+ href: `/news/${r.wp_slug}`,
+ title: r.title,
+ }));
+ const forum: TickerItem[] = (forumRes.data || []).map((r: any) => ({
+ href: `/forum/thread/${r.id}`,
+ title: r.title,
+ }));
+
+ // Fallback to keep layout sensible if either feed is empty
+ const wireItems = wire.length > 0 ? wire : [{ href: "/news", title: "Latest broadcast industry news loading…" }];
+ const forumItems = forum.length > 0 ? forum : [{ href: "/forum", title: "Join the conversation in the forum" }];
+
+ return (
+
+ {/* LANE 1 — LIVE WIRE (red) */}
+
+
+
+ LIVE WIRE
+
+
+
+ {[...wireItems, ...wireItems].map((item, i) => (
+
+ ›
+ {item.title}
+
+ ))}
+
+
+
+
+ {/* LANE 2 — RECENT FORUM POSTS (teal-green) */}
+
+
+
+ RECENT FORUM POSTS
+
+
+
+ {[...forumItems, ...forumItems].map((item, i) => (
+
+ ›
+ {item.title}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/LiveWireTicker.tsx b/src/components/LiveWireTicker.tsx
deleted file mode 100644
index 11d2235..0000000
--- a/src/components/LiveWireTicker.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import Link from "next/link";
-import { createClient } from "@supabase/supabase-js";
-
-export const dynamic = "force-dynamic";
-
-interface Item {
- slug: string;
- title: string;
- published_at: string;
- source_code: string;
-}
-
-const SOURCE_CODES: { match: RegExp; code: string }[] = [
- { match: /blackmagic|davinci/i, code: "BMD" },
- { match: /magewell/i, code: "MGW" },
- { match: /aja/i, code: "AJA" },
- { match: /sony/i, code: "SONY" },
- { match: /matrox/i, code: "MTRX" },
- { match: /telestream/i, code: "TLST" },
- { match: /grass valley/i, code: "GV" },
- { match: /lawo/i, code: "LAWO" },
- { match: /calrec/i, code: "CLRC" },
- { match: /zixi/i, code: "ZIXI" },
- { match: /liveu/i, code: "LVU" },
- { match: /netflix/i, code: "NFLX" },
- { match: /youtube/i, code: "YT" },
- { match: /amazon|aws/i, code: "AWS" },
-];
-
-function codeFor(title: string): string {
- for (const { match, code } of SOURCE_CODES) if (match.test(title)) return code;
- return "WIRE";
-}
-
-function hhmm(iso: string): string {
- const d = new Date(iso);
- const h = String(d.getUTCHours()).padStart(2, "0");
- const m = String(d.getUTCMinutes()).padStart(2, "0");
- return `${h}:${m}`;
-}
-
-export default async function LiveWireTicker() {
- const sb = createClient(
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
- {
- db: { schema: (process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb") as "public" },
- auth: { persistSession: false },
- }
- );
-
- const [wp, ai] = await Promise.all([
- sb
- .from("wp_imported_posts")
- .select("wp_slug, title, wp_published_at")
- .eq("status", "published")
- .order("wp_published_at", { ascending: false })
- .limit(12),
- sb
- .from("ai_rewritten_articles")
- .select("slug, title, published_at")
- .eq("status", "published")
- .order("published_at", { ascending: false, nullsFirst: false })
- .limit(6),
- ]);
-
- const items: Item[] = [];
- for (const r of (wp.data || []) as any[]) {
- items.push({
- slug: r.wp_slug,
- title: r.title,
- published_at: r.wp_published_at,
- source_code: codeFor(r.title || ""),
- });
- }
- for (const r of (ai.data || []) as any[]) {
- items.push({
- slug: r.slug,
- title: r.title,
- published_at: r.published_at,
- source_code: codeFor(r.title || ""),
- });
- }
- items.sort(
- (a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime()
- );
-
- const display = items.slice(0, 14);
-
- return (
-
-
-
- Live wire
-
-
-
- {[...display, ...display].map((it, i) => (
-
- [{hhmm(it.published_at)}]
- [{it.source_code}]
- {it.title}
-
- ))}
-
-
-
-
- );
-}
diff --git a/src/styles/tailwind.css b/src/styles/tailwind.css
index 15f4bde..a07e74f 100644
--- a/src/styles/tailwind.css
+++ b/src/styles/tailwind.css
@@ -807,4 +807,197 @@ h1, h2, h3 {
.ProseMirror strong { font-weight: 700; color: #fff; }
.ProseMirror em { font-style: italic; }
.ProseMirror u { text-decoration: underline; }
-.ProseMirror s { text-decoration: line-through; }
\ No newline at end of file
+.ProseMirror s { text-decoration: line-through; }
+
+/* =====================
+ DOUBLE TICKER (LIVE WIRE + RECENT FORUM POSTS)
+ ===================== */
+.bb-ticker-stack {
+ width: 100%;
+ border-bottom: 1px solid #1a1a1a;
+}
+
+.bb-ticker {
+ display: flex;
+ align-items: stretch;
+ height: 34px;
+ width: 100%;
+ overflow: hidden;
+ position: relative;
+}
+
+.bb-ticker--wire {
+ background: linear-gradient(90deg, #0a0a0a 0%, #161616 50%, #0a0a0a 100%);
+ border-bottom: 1px solid #1f1212;
+}
+
+.bb-ticker--forum {
+ background: linear-gradient(90deg, #08120e 0%, #0f1d18 50%, #08120e 100%);
+}
+
+.bb-ticker-label {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ padding: 0 16px;
+ font-family: var(--font-body);
+ font-size: 11px;
+ font-weight: 800;
+ letter-spacing: 0.22em;
+ color: #ffffff;
+ white-space: nowrap;
+ position: relative;
+ z-index: 2;
+}
+
+.bb-ticker-label--red {
+ background: linear-gradient(90deg, #d40000 0%, #a30000 100%);
+ box-shadow: 0 0 18px rgba(212, 0, 0, 0.45), inset 0 -1px 0 rgba(255, 255, 255, 0.05);
+ clip-path: polygon(0 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
+ padding-right: 24px;
+}
+
+.bb-ticker-label--green {
+ background: linear-gradient(90deg, #0c8a63 0%, #086149 100%);
+ box-shadow: 0 0 18px rgba(12, 138, 99, 0.40), inset 0 -1px 0 rgba(255, 255, 255, 0.05);
+ clip-path: polygon(0 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
+ padding-right: 24px;
+}
+
+.bb-ticker-pulse {
+ display: inline-block;
+ width: 7px;
+ height: 7px;
+ border-radius: 999px;
+ background: #ffffff;
+ box-shadow: 0 0 8px rgba(255, 255, 255, 0.9);
+ animation: bb-ticker-pulse-anim 1.4s ease-in-out infinite;
+}
+
+.bb-ticker-pulse--green {
+ animation-duration: 1.8s;
+}
+
+@keyframes bb-ticker-pulse-anim {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.4; transform: scale(1.25); }
+}
+
+.bb-ticker-mask {
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ position: relative;
+ -webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
+ mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
+}
+
+.bb-ticker-track {
+ display: inline-flex;
+ align-items: center;
+ width: max-content;
+ white-space: nowrap;
+ animation-name: bb-ticker-scroll;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ will-change: transform;
+}
+
+/* Different speeds so the two lanes never sync up — feels alive */
+.bb-ticker-track--wire { animation-duration: 65s; }
+.bb-ticker-track--forum { animation-duration: 82s; }
+
+.bb-ticker-track:hover,
+.bb-ticker-track:focus-within {
+ animation-play-state: paused;
+}
+
+@keyframes bb-ticker-scroll {
+ 0% { transform: translate3d(0, 0, 0); }
+ 100% { transform: translate3d(-50%, 0, 0); }
+}
+
+.bb-ticker-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ padding: 0 22px;
+ height: 34px;
+ text-decoration: none;
+ font-family: var(--font-body);
+ font-size: 13px;
+ font-weight: 500;
+ letter-spacing: 0.005em;
+ color: #d8d8d8;
+ transition: color 0.18s ease;
+ border-right: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.bb-ticker-item--wire:hover { color: #ff6b6b; }
+.bb-ticker-item--forum:hover { color: #5eead4; }
+
+.bb-ticker-bullet {
+ flex-shrink: 0;
+ font-weight: 700;
+ color: rgba(255, 255, 255, 0.35);
+}
+
+.bb-ticker-item--wire:hover .bb-ticker-bullet { color: #ff6b6b; }
+.bb-ticker-item--forum:hover .bb-ticker-bullet { color: #5eead4; }
+
+/* =====================
+ BROWSE NAV BAR (sits below the 728x90 header banner)
+ ===================== */
+.bb-browse-bar {
+ background: linear-gradient(180deg, #0b0b0b 0%, #0e0e0e 100%);
+ border-top: 1px solid #1a1a1a;
+ border-bottom: 1px solid #1a1a1a;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+}
+
+.bb-browse-row {
+ display: flex;
+ align-items: stretch;
+ gap: 0;
+ height: 44px;
+}
+
+.bb-browse-item {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ padding: 0 16px;
+ font-family: var(--font-body);
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: #cccccc;
+ text-decoration: none;
+ border-right: 1px solid #1a1a1a;
+ transition: background 0.15s ease, color 0.15s ease;
+}
+
+.bb-browse-item:first-of-type {
+ border-left: 1px solid #1a1a1a;
+}
+
+.bb-browse-item:hover,
+.bb-browse-item[aria-expanded="true"] {
+ background: #161616;
+ color: var(--color-accent);
+}
+
+.bb-browse-item .bb-browse-caret {
+ display: inline-block;
+ margin-left: 6px;
+ font-size: 8px;
+ opacity: 0.7;
+ transform: translateY(-1px);
+ transition: transform 0.15s ease;
+}
+
+.bb-browse-item[aria-expanded="true"] .bb-browse-caret {
+ transform: rotate(180deg) translateY(1px);
+}
\ No newline at end of file