initial commit: rocket.new export of broadcastbeat

This commit is contained in:
Ryan Salazar
2026-05-07 16:39:17 +00:00
commit 70aa1ad46e
302 changed files with 60710 additions and 0 deletions

View File

@@ -0,0 +1,270 @@
"use client";
import React, { useState, useEffect, useCallback } from "react";
import AppImage from "@/components/ui/AppImage";
import { ArrowRightIcon } from "@/components/ui/Icons";
import Link from "next/link";
// Fixed hero — only changes when manually updated
const hero = {
title: "Sony BRC-AM7 + HXC-FZ90: Better Together in the Field",
category: "BROADCAST",
excerpt: "Sony's latest camera pairing delivers unprecedented flexibility for field production teams, combining 4K HDR capture with compact form factors designed for the realities of live broadcast.",
image: "https://www.broadcastbeat.com/wp-content/uploads/BRC-AM7_012-Mid.png",
alt: "Sony BRC-AM7 broadcast camera in field setting",
slug: "sony-brc-am7-hxc-fz90",
};
// Last 15 stories from the homepage feed — these rotate in the secondary cards
const rotatingStories = [
{
title: "Sony\'s BVM-HX1710: The Compact Powerhouse Changing How Broadcast & Post See Their Work",
category: "POST PRODUCTION",
image: "https://www.broadcastbeat.com/wp-content/uploads/bvmhx1710_3q_250131_02-copy-scaled.png",
alt: "Sony BVM-HX1710 broadcast monitor three-quarter view",
slug: "sony-bvm-hx1710",
},
{
title: "The Top 5 Challenges in MCRs & NOCs—And How to Stay Ahead",
category: "BROADCAST",
image: "https://www.broadcastbeat.com/wp-content/uploads/data-insight-ip-monitoring-1024x597.pngw3_.webp",
alt: "IP monitoring dashboard showing data insights for MCR operations",
slug: "top-5-challenges-mcrs-nocs",
},
{
title: "TESSERA SQ200: THE NEW PACE-SETTER IN LED PROCESSING",
category: "FEATURED",
image: "https://www.broadcastbeat.com/wp-content/uploads/Hero-Image_SQ200-Gen-3_Sep2025_CMYK.jpg",
alt: "Tessera SQ200 Gen 3 LED processor hero image",
slug: "tessera-sq200-led-processing",
},
{
title: "Backlight adds real-time collaboration to Iconik, AI-powered livestreams to Wildmoka at IBC2025",
category: "BROADCAST",
image: "https://www.broadcastbeat.com/wp-content/uploads/Iconik-scaled.jpeg",
alt: "Iconik media asset management platform interface",
slug: "backlight-iconik-wildmoka-ibc2025",
},
{
title: "Telestream Expands Its Cloud Services with the Introduction of UP",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_11ff3d2ff-1773287949660.png",
alt: "Cloud services infrastructure for broadcast production environments",
slug: "telestream-expands-cloud-services-up",
},
{
title: "Operative Launches AOS Configuration for Digital-First Monetization",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_1805d1575-1773287951103.png",
alt: "Digital monetization analytics dashboard for broadcast advertising",
slug: "operative-launches-aos-configuration",
},
{
title: "Calrec Redefines Broadcast Workflows at NAB 2026 with its Most Powerful Audio Lineup Yet",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_10468e182-1773287953747.png",
alt: "Calrec audio mixing console for broadcast production at NAB 2026",
slug: "calrec-redefines-broadcast-workflows-nab-2026",
},
{
title: "Ease Live Powers Interactive Premier Padel Experiences on Red Bull TV as Viewership Surges 30%",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_1747232a3-1773287951222.png",
alt: "Interactive sports broadcast experience on streaming platform",
slug: "ease-live-premier-padel-red-bull-tv",
},
{
title: "From Metadata to Meaning: Why Semantic Intelligence Is Media\'s Next Competitive Advantage",
category: "FEATURED",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_1c523ae5f-1773287954372.png",
alt: "Media metadata intelligence and content analytics visualization",
slug: "from-metadata-to-meaning-semantic-intelligence",
},
{
title: "Mediagenix Title Management Accelerates Content Monetization Through Advanced Semantic Intelligence",
category: "POST PRODUCTION",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_19f6e3fe6-1769744394519.png",
alt: "Content management system for media title management and monetization",
slug: "mediagenix-title-management",
},
{
title: "Emergent Launches Fusion: The \'Interactive Anything\' Platform Transforming Data-Driven Storytelling",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_188669b2b-1773287951738.png",
alt: "Interactive data storytelling platform for broadcast and enterprise use",
slug: "emergent-launches-fusion-platform",
},
{
title: "Matrox Video Enables the Next Era of Software-Defined Media Production at NAB 2026",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_17266f6cf-1773287953058.png",
alt: "Matrox video production hardware and software at NAB 2026 exhibition",
slug: "matrox-video-software-defined-nab-2026",
},
{
title: "Video is King: 2026 Iconik Media Stats Report Finds Video Consumes 64% of Storage Needs",
category: "FEATURED",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_140568f68-1773287953452.png",
alt: "Video storage statistics and media asset management data visualization",
slug: "video-is-king-2026-iconik-media-stats",
},
{
title: "QuickLink\'s Latest StudioEdge Models to Make North American Debut at NAB 2026",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_19c4ba922-1773287953682.png",
alt: "QuickLink StudioEdge broadcast production equipment at NAB exhibition",
slug: "quicklink-studioedge-nab-2026",
},
{
title: "Utah Scientific Expands Technology Partner Program With Integrations From Audinate, Bitfocus, and Skaarhoj",
category: "BROADCAST",
image: "https://img.rocket.new/generatedImages/rocket_gen_img_1ea12f02f-1768375990494.png",
alt: "Broadcast routing and control system technology partner integration",
slug: "utah-scientific-partner-program",
},
];
// Number of secondary cards visible at once
const CARDS_PER_PAGE = 4;
// Auto-rotate interval in ms
const AUTO_ROTATE_MS = 5000;
export default function FeaturedBento() {
// offset = index of first visible secondary card
const [offset, setOffset] = useState(0);
const [paused, setPaused] = useState(false);
const maxOffset = rotatingStories?.length - CARDS_PER_PAGE;
const goNext = useCallback(() => {
setOffset((prev) => (prev >= maxOffset ? 0 : prev + 1));
}, [maxOffset]);
const goPrev = useCallback(() => {
setOffset((prev) => (prev <= 0 ? maxOffset : prev - 1));
}, [maxOffset]);
// Auto-rotate
useEffect(() => {
if (paused) return;
const timer = setInterval(goNext, AUTO_ROTATE_MS);
return () => clearInterval(timer);
}, [paused, goNext]);
const visibleCards = rotatingStories?.slice(offset, offset + CARDS_PER_PAGE);
return (
<section className="max-w-container mx-auto px-4 py-4 md:py-6">
{/* Section header */}
<div className="flex items-center gap-3 mb-4 md:mb-5">
<span className="section-label">Featured</span>
<div className="flex-1 h-px bg-[#2a2a2a]" />
{/* Navigation arrows */}
<div className="flex items-center gap-1.5">
<button
onClick={() => { goPrev(); setPaused(true); }}
aria-label="Previous stories"
className="w-7 h-7 flex items-center justify-center bg-[#1a1a1a] border border-[#2a2a2a] hover:border-[#3b82f6] hover:text-[#3b82f6] text-[#666] transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
<polyline points="15 18 9 12 15 6" />
</svg>
</button>
<button
onClick={() => { goNext(); setPaused(true); }}
aria-label="Next stories"
className="w-7 h-7 flex items-center justify-center bg-[#1a1a1a] border border-[#2a2a2a] hover:border-[#3b82f6] hover:text-[#3b82f6] text-[#666] transition-colors rounded-sm focus:outline-none focus-visible:ring-1 focus-visible:ring-[#3b82f6]">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
<polyline points="9 18 15 12 9 6" />
</svg>
</button>
{/* Position indicator */}
<span className="font-body text-[11px] text-[#555] ml-1">
{offset + 1}{Math.min(offset + CARDS_PER_PAGE, rotatingStories?.length)}/{rotatingStories?.length}
</span>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-3 md:gap-4">
{/* Hero Article — FIXED, only changes when manually updated */}
<Link
href={`/articles/${hero?.slug}`}
className="lg:col-span-7 img-zoom rounded-sm overflow-hidden relative group cursor-pointer block"
style={{ minHeight: 260 }}>
<div className="relative w-full h-[260px] sm:h-[320px] md:h-[380px] lg:h-full lg:min-h-[400px]">
<AppImage
src={hero?.image}
alt={hero?.alt}
fill
className="object-cover w-full h-full"
priority={true}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 100vw, 58vw"
/>
<div className="hero-overlay absolute inset-0" />
<div className="absolute inset-0 flex flex-col justify-end p-3 sm:p-4 md:p-5">
<span className="inline-block bg-[#3b82f6] text-white font-body text-[10px] font-bold px-2 py-0.5 tracking-wider uppercase mb-2 w-fit">
{hero?.category}
</span>
<h2 className="font-heading text-white text-lg sm:text-xl md:text-2xl font-bold leading-tight mb-2 max-w-xl">
{hero?.title}
</h2>
<p className="text-white/80 font-body text-sm leading-relaxed max-w-lg hidden sm:block mb-3">
{hero?.excerpt}
</p>
<span className="inline-flex items-center gap-1.5 text-white font-body font-bold text-xs uppercase tracking-wider group-hover:text-[#93c5fd] transition-colors">
Read More
<ArrowRightIcon size={12} />
</span>
</div>
</div>
</Link>
{/* Secondary 2x2 Grid — rotating stories */}
<div
className="lg:col-span-5 grid grid-cols-2 gap-2 md:gap-3"
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}>
{visibleCards?.map((article, i) => (
<Link
key={`${article?.slug}-${offset}-${i}`}
href={`/articles/${article?.slug}`}
className="bg-[#1a1a1a] border border-[#2a2a2a] overflow-hidden group cursor-pointer card-accent-hover flex flex-col hover:border-[#3b82f6] transition-colors">
<div className="relative overflow-hidden img-zoom h-[90px] sm:h-[110px] md:h-[120px]">
<AppImage
src={article?.image}
alt={article?.alt}
fill
className="object-cover w-full h-full"
sizes="(max-width: 640px) 50vw, (max-width: 1024px) 50vw, 22vw"
/>
<div className="absolute top-0 left-0">
<span className="bg-[#3b82f6] text-white font-body text-[9px] font-bold px-1.5 py-0.5 tracking-wider uppercase">
{article?.category}
</span>
</div>
</div>
<div className="p-2 md:p-2.5 flex-1 flex flex-col justify-between">
<h3 className="font-heading text-[#e0e0e0] text-[12px] md:text-[13px] font-bold leading-snug line-clamp-3 group-hover:text-[#3b82f6] transition-colors duration-200 mb-1.5 md:mb-2">
{article?.title}
</h3>
<span className="read-more inline-block text-[11px]">Read More »</span>
</div>
</Link>
))}
</div>
</div>
{/* Dot indicators */}
<div className="flex justify-end items-center gap-1 mt-3">
{Array.from({ length: maxOffset + 1 })?.map((_, i) => (
<button
key={i}
onClick={() => { setOffset(i); setPaused(true); }}
aria-label={`Go to position ${i + 1}`}
className={`rounded-full transition-all focus:outline-none ${
i === offset
? "w-4 h-1.5 bg-[#3b82f6]"
: "w-1.5 h-1.5 bg-[#333] hover:bg-[#555]"
}`}
/>
))}
</div>
</section>
);
}