Revert "home: replace single LiveWire ticker with stacked dual ticker"

This reverts commit a52f8adb21.
This commit is contained in:
Ryan Salazar
2026-05-20 03:53:31 +00:00
parent 8890e61c2e
commit b1378de6d6
4 changed files with 122 additions and 290 deletions

View File

@@ -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 DoubleTicker from '@/components/DoubleTicker';
import LiveWireTicker from '@/components/LiveWireTicker';
import NewsTicker from "./components/NewsTicker";
import FeaturedBento from "./components/FeaturedBento";
import SpotlightCarousel from "./components/SpotlightCarousel";
@@ -147,10 +147,10 @@ export default function HomePage() {
<h1>Broadcast Beat Broadcast Engineering News & Insights</h1>
</div>
{/* Scrolling news + forum tickers */}
{/* Scrolling news ticker */}
<div className="section-enter section-enter-1">
<Suspense fallback={<TickerSkeleton />}>
<DoubleTicker />
<LiveWireTicker />
</Suspense>
</div>

View File

@@ -1,93 +0,0 @@
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 (
<div className="bb-ticker-stack" aria-label="Site activity tickers">
{/* LANE 1 — LIVE WIRE (red) */}
<div className="bb-ticker bb-ticker--wire" role="region" aria-label="Live wire — latest articles">
<span className="bb-ticker-label bb-ticker-label--red">
<span className="bb-ticker-pulse" aria-hidden="true" />
LIVE WIRE
</span>
<div className="bb-ticker-mask">
<div className="bb-ticker-track bb-ticker-track--wire">
{[...wireItems, ...wireItems].map((item, i) => (
<Link
key={`wire-${i}`}
href={item.href}
className="bb-ticker-item bb-ticker-item--wire">
<span className="bb-ticker-bullet" aria-hidden="true"></span>
<span>{item.title}</span>
</Link>
))}
</div>
</div>
</div>
{/* LANE 2 — RECENT FORUM POSTS (teal-green) */}
<div className="bb-ticker bb-ticker--forum" role="region" aria-label="Recent forum posts">
<span className="bb-ticker-label bb-ticker-label--green">
<span className="bb-ticker-pulse bb-ticker-pulse--green" aria-hidden="true" />
RECENT FORUM POSTS
</span>
<div className="bb-ticker-mask">
<div className="bb-ticker-track bb-ticker-track--forum">
{[...forumItems, ...forumItems].map((item, i) => (
<Link
key={`forum-${i}`}
href={item.href}
className="bb-ticker-item bb-ticker-item--forum">
<span className="bb-ticker-bullet" aria-hidden="true"></span>
<span>{item.title}</span>
</Link>
))}
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,118 @@
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 (
<div
className="w-full bg-[var(--color-background-secondary,#111)] border-b border-[#252525] overflow-hidden"
role="region"
aria-label="Live wire ticker"
>
<div className="max-w-container mx-auto px-4 h-[36px] flex items-center gap-4 text-[12px]">
<span className="inline-flex items-center gap-1.5 font-mono uppercase tracking-wider text-[var(--color-text-danger,#ef4444)] shrink-0">
<span className="bb-pulse-dot bb-pulse-dot--red" /> Live wire
</span>
<div className="overflow-hidden flex-1">
<div className="bb-marquee">
{[...display, ...display].map((it, i) => (
<Link
key={`${it.slug}-${i}`}
href={`/news/${it.slug}`}
className="inline-flex items-center gap-2 px-4 text-[#d1d5db] hover:text-[var(--color-text-info,#60a5fa)] whitespace-nowrap"
>
<span className="font-mono text-[#6b7280]">[{hhmm(it.published_at)}]</span>
<span className="font-mono text-[var(--color-text-info,#60a5fa)]">[{it.source_code}]</span>
<span>{it.title}</span>
</Link>
))}
</div>
</div>
</div>
</div>
);
}

View File

@@ -807,197 +807,4 @@ 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; }
/* =====================
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);
}
.ProseMirror s { text-decoration: line-through; }