homepage: neon dual-speed ticker + emerald/cyan palette pass

- New DualSpeedTicker component (CSS keyframes, GPU-accelerated,
  pause on hover, 28px rows, 17s BEAT / 40s FORUM, refresh 5min)
- /api/ticker/beat-news: last 7 days from bb.native_articles
- /api/ticker/forum-posts: latest threads + replies interleaved
- Mounted at the bottom of Header so every page gets it
- Scoped .bb-neon palette on homepage root: section heads +
  text-shadow glow, neon-tinted card borders, emerald→cyan button
  gradient, cyan hover state on links, subscribe boxes neon-bordered
- CSS-only — no layout changes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ryan Salazar
2026-05-21 23:58:12 +00:00
parent 5eaf64925f
commit aee67ef9f6
6 changed files with 490 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
import { NextResponse } from 'next/server';
import { createClient } from '@supabase/supabase-js';
export const runtime = 'nodejs';
export const revalidate = 300; // 5 minutes
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const SUPABASE_ANON = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
export async function GET() {
const sb = createClient(SUPABASE_URL, SUPABASE_ANON, {
db: { schema: 'bb' },
auth: { persistSession: false },
});
const cutoff = new Date(Date.now() - 7 * 86400000).toISOString();
const { data, error } = await sb
.from('native_articles')
.select('id, slug, title, category, published_at')
.eq('status', 'published')
.gte('published_at', cutoff)
.order('published_at', { ascending: false })
.limit(30);
if (error) {
return NextResponse.json({ error: error.message, items: [] }, { status: 500 });
}
const items = (data || []).map((a) => ({
id: a.id,
href: `/articles/${a.slug}`,
title: a.title,
category: a.category || null,
published_at: a.published_at,
}));
return NextResponse.json(
{ items },
{
headers: {
'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
},
}
);
}