From 6f7eccda3813d4a092554ec5b3f4b109b4e2b215 Mon Sep 17 00:00:00 2001 From: Steph Alinsug Date: Thu, 26 Mar 2026 09:48:15 -0400 Subject: [PATCH 01/12] v2 copy alignment: hero, ecosystem, community, nav, footer --- CLAUDE.md | 115 +++++--- app/page.tsx | 9 - components/home/BuiltOnLivepeer.tsx | 12 +- components/home/CommunityCTA.tsx | 40 +-- components/home/DeveloperCTA.tsx | 96 ++----- components/home/Hero.tsx | 18 +- components/home/WhatIsLivepeer.tsx | 417 +--------------------------- components/layout/Footer.tsx | 16 +- components/layout/Header.tsx | 37 ++- components/primer/PrimerContent.tsx | 254 +++++------------ lib/constants.ts | 14 +- 11 files changed, 215 insertions(+), 813 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a465d11..f28a4ff 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,7 +29,7 @@ ### `components/` -- **`home/`** — self-contained homepage sections. Render order in `app/page.tsx`: Hero, Capabilities, WhatIsLivepeer, WhyLivepeer, BuiltOnLivepeer, NetworkStats, DeveloperCTA, CommunityCTA. (`NetworkParticipants.tsx` exists but is not currently rendered.) +- **`home/`** — self-contained homepage sections. Render order in `app/page.tsx`: Hero, BuiltOnLivepeer, CommunityCTA. (`NetworkParticipants.tsx` exists but is not currently rendered.) - **`layout/`** — Header, Footer. Header has headroom behavior on `/primer` (hides on scroll down, reveals on scroll up). - **`ui/`** — shared primitives (Button, Card, Container, SectionHeader, Badge, ImageMask, GlowOverlay, EarlyAccessCTA, etc.). Reuse these; don't create new wrappers for the same purpose. Also contains canvas components: `GenerativeCanvas.tsx` (GLSL shader), `LiveNetwork.tsx` (Canvas 2D particle trails), `AiVideoHero.tsx` (Sobel edge detection on video texture). All canvas components follow `useEffect` + `useRef` + `requestAnimationFrame` + cleanup. - **`blog/`** — blog listing page (`BlogListingClient`, `BlogCategoryFilter`, `BlogPostCard`) and post detail (`BlogPostHeader`, `BlogPostContent`). @@ -114,70 +114,111 @@ Dark theme only — except `/primer`, which uses a light theme override with scr ## Messaging -### Positioning - -- **Canonical headline:** "Open infrastructure for real-time AI video" -- **Tagline (footer):** "The world's open video infrastructure." -- Lead with AI video, not transcoding. Transcoding is a capability, not the identity. -- The network IS the product. Daydream, Livepeer Studio, Streamplace, Embody are built ON it — they are ecosystem proof, not Livepeer products. -- **Network vs. Gateway:** The Livepeer Network is the open protocol (orchestrators, delegators, staking, distributed GPU compute). The Livepeer Gateway is the developer-facing product layer on top — API keys, request routing, billing, SLA enforcement. "Build on Livepeer" = use the Gateway. "Run the network" = be an orchestrator/delegator. -- **Differentiation:** real-time, frame-by-frame AI inference on live video — not batch processing, not generic GPU compute. Centralized GPU clouds are optimized for batch/static and structurally disadvantaged here. -- **Three demand drivers:** AI-generated worlds/games, real-time video analysis, AI-mediated avatars/agents. +### Positioning (v2 Thesis — March 2026) + +- **Thesis statement:** "Livepeer is the open network for GPU-powered video." +- **Canonical headline:** "The open network for GPU-powered video" +- **Tagline (footer):** "The open network for GPU-powered video." +- Lead with solutions (builds on the network), not raw protocol capabilities. +- The network provides GPU infrastructure. Solutions provide the product experience. +- **Three competitive variables (cost / capability / community):** + - **Cost:** 60–85% cheaper than centralized alternatives (AWS, RunPod, Fal). [FLAG: pending validation with Rick — do not scale to "10x" without confirmed data] + - **Capability:** Specialized for real-time, streaming video inference. Nine years of video processing optimization. BYOC flexibility for custom models and pipelines. No other network is built specifically for this. + - **Community:** The network is operated by independent orchestrators, expanded by builders building on builders, and open to permissionless participation. This is a structural property of the infrastructure — not a Discord server or governance forum. A centralized provider structurally cannot replicate this. +- Not a trilemma — cost, capability, and community interact. +- **External competitive frame:** Livepeer solutions (powered by community-operated GPU infrastructure) vs. centralized alternatives (AWS, RunPod, Fal, Replicate) for GPU-powered video. Name competitors explicitly — the target audience is comparison-shopping. +- **Internal only (not for website copy):** DePIN comparisons (Theta, Render, Akash). These are relevant for internal strategy but not for developer-facing positioning. +- **Narrative routing principle:** Every piece of external communication should route audiences to solutions on the network. The question the website answers: "What's being built on Livepeer, and how can I use it?" +- **Solutions on the network:** Daydream (real-time generative video), Frameworks (sovereign live streaming), Streamplace (decentralized social video), Embody (AI avatars). These are the ecosystem, not Livepeer products. +- **CTA pattern:** All CTAs point to Discord ("Build with Livepeer"). No email capture. ### Voice - Confident, technical but accessible, no-nonsense. -- Lead with what the network does and why it matters. Use concrete numbers (10x cost reduction, <1s latency, 100K+ GPUs). +- Lead with what builders can do on the network and why it matters. Use concrete numbers where validated. +- Name competitors directly — credibility comes from honest comparison, not avoidance. +- Be honest about constraints. The network has ~100 AI-capable GPUs, estimated 90–95% uptime, no formal SLAs. Don't oversell what doesn't exist yet. - Explain technical concepts simply on first reference. -- **Avoid:** "revolutionary," "game-changing," empty superlatives, hype language, "web3 bro" tone, lorem ipsum. +- **Avoid:** "revolutionary," "game-changing," empty superlatives, hype language, "web3 bro" tone, "decentralized" as a selling point, "the platform," lorem ipsum. ### Terminology | Use | Don't use | |-----|-----------| | "the network" | "the platform," "the service" | -| "open infrastructure" | "decentralized infrastructure" (too web3-coded) | +| "open network" | "decentralized infrastructure" (too web3-coded) | +| "solutions" | "builds" (internal only), "gateways," "tools," "DGEs" | | "GPU providers" (dev-facing) | "nodes" (too generic) | | "orchestrators" (protocol/network context) | "miners," "validators" (wrong mental model) | -| "gateway" | "API endpoint" (gateway is the product concept) | -| "delegators" | "stakers" (delegators is the protocol term) | -| "frames," "streams" | "requests," "calls" (video-native language) | | "inference" | "processing" (when referring to AI specifically) | -| "Livepeer Gateway" | "NaaP" (internal-only project name), "Livepeer platform" (too generic) | +| "GPU-powered video" | "real-time AI video" (previous headline, now retired) | + +### What the v2 thesis kills (do not use this framing) + +- ~~"The network is the product."~~ → The network is infrastructure. Solutions are the products. +- ~~"Livepeer is a generalized GPU compute network."~~ → It's specialized. GPU-powered video. +- ~~"We need to attract developers to the raw protocol."~~ → Route audiences to solutions. +- ~~"Enterprise is the near-term market."~~ → The edges are. +- ~~"Centralized vs. decentralized."~~ → The tension is centralized-proprietary vs. community-operated/open. +- ~~"10x cost reduction."~~ → 60–85% cheaper. [FLAG: pending Rick validation] +- ~~"100K+ GPUs."~~ → ~100 AI-capable GPUs currently. Don't inflate supply numbers. ## Strategic Context -This website is a redesign of livepeer.org to reflect Livepeer's repositioning from decentralized video transcoding to **infrastructure purpose-built for real-time AI video**, guided by the **Cascade vision**. As of late 2025, 70%+ of network fees come from AI inference (not transcoding), with ~3x YoY fee growth — AI is already the primary economic driver. Core strengths: low-latency video pipelines, distributed GPU operator network, open stake-coordinated execution, permissionless protocol on public blockchain. +This website reflects Livepeer's v2 positioning as a specialized GPU network for real-time video inference. The thesis organizes everything around a three-layer stack: supply (GPUs contributed by orchestrators), protocol (routing that matches workloads to GPUs), and demand (builds that package network capabilities for their audiences). -### Livepeer Gateway (internally "NaaP") +### The Stack -"NaaP" is an **internal project name** for the canonical open-source gateway and developer platform. **Do not surface "NaaP" as user-facing terminology on the website.** The user-facing name is **Livepeer Gateway**. +``` +┌─────────────────────────────────────────────┐ +│ DEMAND (top) │ +│ Builds. Products and businesses on the │ +│ protocol that serve their own audiences. │ +├─────────────────────────────────────────────┤ +│ PROTOCOL (middle) │ +│ Routing. Orchestrator discovery, job │ +│ routing, staking, delegation, payments. │ +├─────────────────────────────────────────────┤ +│ SUPPLY (bottom) │ +│ GPUs. Physical compute contributed by │ +│ independent orchestrators. │ +└─────────────────────────────────────────────┘ +``` -The 2026 focus is preparing the network for scalable demand. This platform is the developer-facing product layer — a "single pane of glass" for core network services. Stewarded by the Livepeer Foundation. +### Current Builds (the ecosystem) -**Product principles:** Observable (transparent network data, LLM/agent-readable), Performant (production-grade real-time execution), User-Centric (developers who drive demand). +- **Scope / Daydream** — Creative AI tools for real-time video. ~30-person creative technologist cohort. Demand R&D partner, not primary demand driver. +- **Frameworks (Marco)** — Live streaming infrastructure. Bare-metal video pipelines on the network. +- **Embody (George)** — Embodied AI avatars. ~70% dev time on integration infrastructure. +- **Streamplace (Eli)** — Open-source video infrastructure for decentralized social (AT Protocol). -**Priority personas:** -1. **App Developers** — build demand-generating apps. "Get an API key and go." -2. **Service Providers** — build plugins for app devs: billing, analytics, CDN. -3. **Orchestrators** — provide GPU compute by meeting SLAs. +### Target Audience: The Edges -### Daydream +The near-term audience is the edges — builders drawn to frontier, specialized infrastructure for emerging use cases. They tolerate constraints (reliability, polish) because cost, capability, or community alignment makes their work possible. This is not the enterprise market. -Flagship product and design partner. Translates the real-time AI video thesis into developer workflows and generates early production demand. -- **Scope**: open-source local tool for designing real-time AI video workflows (integrates with TouchDesigner, Unity, Unreal) -- **Daydream API**: remote inference on Livepeer GPU backends -- **Community Hub**: workflow discovery and collaboration +Three segments: creative technologists (enter through Scope), early-stage AI video builders (enter through whichever build fits), orchestrator-entrepreneurs (enter through the network itself). -### 2026 Roadmap Phases +### 2026 Foundation Priorities -1. **Now:** Drive network performance — observability, reliability, security; expand dev tooling; remove barriers to production use -2. **Next:** Accelerate network GTM — convert readiness into demand; design partners, onboarding, ecosystem support -3. **Beyond:** Scale enterprise adoption — higher demand, GPU supply expansion, enterprise-grade ops +1. Route audiences to builds — the ecosystem page is the website's primary routing surface +2. Validate edges audience motivations through builder discovery and audience research +3. Ship case studies proving cost, capability, and community claims before they become headlines +4. Grow the builder ecosystem — the network's demand story is the builds' story ### Website Design Direction -- **AI-first positioning** — lead with real-time AI video, not legacy transcoding +- **Specialized, not generic** — lead with video inference specialization, not "open infrastructure" - **Brand source of truth:** Holographic Agency brand guidelines (see `brand-tokens.md`) - **Design references:** Vercel, Linear, Stripe, Raycast — clean, developer-focused, premium feel -- **Target audience:** Developers building real-time AI/video applications +- **Primary audience:** Builders evaluating GPU infrastructure for real-time video AI applications +- **Ecosystem page is the primary routing surface** — every visitor should be able to answer "what's being built here and how do I use it?" +- **Honest about constraints.** Don't mock up product UI that doesn't exist. The CTA is "Build with Livepeer" (Discord) — match the copy to that reality. + +### Open Flags + +These claims require validation before shipping as headlines. Use [FLAG] markers in copy: + +- 60–85% cost claim — needs Rick validation against current network pricing +- "Agents can discover and pay directly" — pending Rick's architecture confirmation +- Framework case study — blocked on Marco discovery conversation +- Specific GPU/node counts — verify current numbers before publishing diff --git a/app/page.tsx b/app/page.tsx index 44a6180..83c54ad 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,21 +1,12 @@ import Hero from "@/components/home/Hero"; -import WhatIsLivepeer from "@/components/home/WhatIsLivepeer"; -import Capabilities from "@/components/home/Capabilities"; - -import WhyLivepeer from "@/components/home/WhyLivepeer"; import BuiltOnLivepeer from "@/components/home/BuiltOnLivepeer"; -import DeveloperCTA from "@/components/home/DeveloperCTA"; import CommunityCTA from "@/components/home/CommunityCTA"; export default function Home() { return ( <> - - - - ); diff --git a/components/home/BuiltOnLivepeer.tsx b/components/home/BuiltOnLivepeer.tsx index 6df1cf4..9c1dcea 100644 --- a/components/home/BuiltOnLivepeer.tsx +++ b/components/home/BuiltOnLivepeer.tsx @@ -421,7 +421,7 @@ const projects = [ Visual: DaydreamVisual, Logo: DaydreamLogo, description: - "APIs for building interactive AI experiences — from video to games and beyond.", + "Open-source engine for real-time generative video, audio, and live visuals.", href: "https://daydream.live", linkLabel: "Visit Daydream", }, @@ -429,7 +429,7 @@ const projects = [ Visual: StudioVisual, Logo: FrameworksLogo, description: - "Stream without the cloud. A next-generation video platform that delivers broadcast-quality streaming directly on Livepeer's open GPU network.", + "Sovereign live streaming platform with SaaS, hybrid, and fully self-hosted modes. No cloud lock-in.", href: "https://frameworks.network", linkLabel: "Visit Frameworks", }, @@ -437,7 +437,7 @@ const projects = [ Visual: StreamplaceVisual, Logo: StreamplaceLogo, description: - "The video layer for decentralized social networks. Open-source infrastructure for high-quality video on the AT Protocol.", + "Streaming video infrastructure for decentralized social networks. Built on the AT Protocol powering Bluesky.", href: "https://stream.place", linkLabel: "Visit Streamplace", }, @@ -445,7 +445,7 @@ const projects = [ Visual: EmbodyVisual, Logo: EmbodyLogo, description: - "Embodied AI avatars for real-time tutoring, telepresence, and branded content powered by Livepeer infrastructure.", + "Embodied AI avatars for real-time tutoring and telepresence.", href: "https://embody.zone", linkLabel: "Visit Embody", }, @@ -468,8 +468,8 @@ export default function BuiltOnLivepeer() { diff --git a/components/home/CommunityCTA.tsx b/components/home/CommunityCTA.tsx index 65e9aa3..39df36e 100644 --- a/components/home/CommunityCTA.tsx +++ b/components/home/CommunityCTA.tsx @@ -10,7 +10,7 @@ import { EXTERNAL_LINKS } from "@/lib/constants"; const channels = [ { name: "Discord", - description: "Join the conversation with developers and the core team.", + description: "Build in real time with frontier developers, engineers, and GPU providers.", href: EXTERNAL_LINKS.discord, icon: ( @@ -19,33 +19,22 @@ const channels = [ ), }, { - name: "Governance", - description: "Participate in proposals that shape the network's future.", - href: "https://explorer.livepeer.org/voting", - icon: ( - - - - ), - }, - { - name: "Treasury", - description: "Submit proposals for grants and funding from the Livepeer Treasury.", - href: "/blog/using-the-livepeer-community-treasury", + name: "Forum", + description: "Explore protocol design, upgrades, and long-range ideas.", + href: EXTERNAL_LINKS.forum, icon: ( - - + ), }, { - name: "Forum", - description: "Long-form discussion on protocol development and ideas.", - href: EXTERNAL_LINKS.forum, + name: "Roadmap", + description: "See where the network is today, and where it's headed.", + href: "https://roadmap.livepeer.org/roadmap", icon: ( - + ), }, @@ -99,21 +88,20 @@ export default function CommunityCTA() { Community

- Join the network + Build alongside the network

- Livepeer is built by a global community of developers, operators, - and token holders working together to create open video - infrastructure. + Livepeer is operated by global, independent GPU providers — builders, + developers, and experts in open video infrastructure.

-
+
{channels.map((channel) => ( - - - ), - }, - { - title: "10-Minute Primer", - description: "Learn how Livepeer works through storytelling, illustration, and data.", - href: "/primer", - icon: ( - - - - ), - }, - { - title: "Livepeer Treasury", - description: "Submit proposals for grants and funding from the community treasury.", - href: "/blog/using-the-livepeer-community-treasury", - icon: ( - - - - ), - }, -]; const fadeUp = { hidden: { opacity: 0, y: 12 }, @@ -47,7 +10,7 @@ const fadeUp = { export default function DeveloperCTA() { return ( -
+
@@ -63,54 +26,29 @@ export default function DeveloperCTA() { className="mx-auto max-w-3xl text-center" >

- Developers + Builders

- Be first to build + Start building

- Get early access to Livepeer — a single API for real-time AI - video inference, built for developers and AI agents alike. + Explore the codebase, read the primer, or jump into Discord and + talk to the builders and operators who run the network.

-
- -
-
- + - -
- {resources.map((resource) => { - const isExternal = resource.href.startsWith("http"); - const Tag = isExternal ? motion.a : motion(Link); - const extraProps = isExternal - ? { target: "_blank", rel: "noopener noreferrer" } - : {}; - - return ( - -
{resource.icon}
-

{resource.title}

-

- {resource.description} -

- - Learn more - -
- ); - })} -
diff --git a/components/home/Hero.tsx b/components/home/Hero.tsx index b87c24d..c0c6055 100644 --- a/components/home/Hero.tsx +++ b/components/home/Hero.tsx @@ -4,7 +4,6 @@ import { useEffect, useRef } from "react"; import { motion, useMotionValue, useMotionValueEvent, animate } from "framer-motion"; import Container from "@/components/ui/Container"; import ImageMask from "@/components/ui/ImageMask"; -import EarlyAccessCTA from "@/components/ui/EarlyAccessCTA"; /* 9×5 tile grid with square tiles. @@ -274,9 +273,9 @@ export default function Hero() { animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.7, delay: 0.1, ease: "easeOut" }} > - Open infrastructure + The open network for
- for real-time AI video + GPU-powered video - Generate, transform, and interpret live video streams - with low-latency AI inference on an open and permissionless elastic GPU network. + For less than the centralized cloud — no platform lock-in, and a + direct line to builders powering the network. - - - - diff --git a/components/home/WhatIsLivepeer.tsx b/components/home/WhatIsLivepeer.tsx index 8b997cf..be76063 100644 --- a/components/home/WhatIsLivepeer.tsx +++ b/components/home/WhatIsLivepeer.tsx @@ -9,341 +9,6 @@ const fadeUp = { visible: { opacity: 1, y: 0 }, }; -/* ── Panel 1: API Keys visual ── */ - -function SpotlightCard({ children }: { children: React.ReactNode }) { - return ( -
-
- {/* Spotlight + vignette + bottom fade — single coherent overlay */} - -
- ); -} - -function ApiKeysVisual() { - return ( - -
- {/* Header */} -
- - Developer API Manager - - -
- - {/* Key row */} -
-
- - - - - - -
-
production
-
lp_sk_1a2b•••c8d9
-
- - Active - -
-
- - {/* Billing provider */} -
-
- Billing Provider -
-
-
-
- Daydream - - - -
-
-
- Livepeer Studio -
-
-
- - {/* Bottom bar */} -
- 3 keys · 2 active - CDN: Enabled -
-
- - ); -} - -/* ── Panel 2: Models / Workflows visual ── */ - -function ModelsVisual() { - return ( - -
- {/* Header */} -
- Models -
- - - - - Search models... -
-
- - {/* Model rows */} - {[ - { name: "GameNGen", desc: "Real-time world generation", type: "world", color: "#34d399", latency: "12ms", price: "$0.006/min" }, - { name: "Depth Anything v2", desc: "Monocular depth estimation", type: "vision", color: "#60a5fa", latency: "8ms", price: "$0.003/min" }, - { name: "StreamDiffusion", desc: "Real-time image generation", type: "gen", color: "#a78bfa", latency: "24ms", price: "$0.008/min" }, - { name: "Whisper v3", desc: "Speech-to-text transcription", type: "audio", color: "#fbbf24", latency: "45ms", price: "$0.002/min" }, - ].map((m, i) => ( -
-
-
-
-
-
{m.name}
-
{m.desc}
-
-
- {m.price} - {m.latency} - - {m.type} - -
-
- ))} - - {/* Bottom bar */} -
- 12 models available - 4 regions -
-
- - ); -} - -/* ── Panel 3: Start streaming visual ── */ - -function StreamVisual() { - return ( - -
- {/* Header */} -
-
- Stream - - Live - -
- stream_8f3k2m -
- - {/* Request snippet */} -
-
- POST{" "} - /v1/stream/start -
- {"{"} workflow: - "world-model-v1" - {" }"} -
-
- - {/* Input/output flow */} -
-
-
-
Input
-
-
- {[0.10, 0.15, 0.08, 0.12, 0.18, 0.10].map((o, j) => ( -
- ))} -
- 30 fps -
-
- - - -
-
Output
-
-
- {[0.12, 0.18, 0.10, 0.15, 0.20, 0.12].map((o, j) => ( -
- ))} -
- 30 fps -
-
-
-
- - {/* Session stats */} -
- {[ - { label: "Latency", value: "14ms" }, - { label: "Uptime", value: "4m 32s" }, - { label: "Frames", value: "8,142" }, - ].map((s) => ( -
-
{s.label}
-
{s.value}
-
- ))} -
- - {/* Bottom bar */} -
- US East · GPU A100 - ● Connected -
-
- - ); -} - -/* ── Panel 4: Usage & SLAs visual ── */ - -const sparklinePoints = - "0,32 15,30 30,34 45,28 60,31 75,25 90,29 105,22 120,27 135,20 150,24 165,18 180,22 195,16 210,20 225,14 240,19 255,15 270,18 285,13 300,17"; - -function UsageVisual() { - return ( - -
- {/* Header */} -
- Usage & SLAs - - Last 24h - -
- - {/* Chart area */} -
-
- 14ms - avg latency - - ↓ 12% vs yesterday - -
- - - - - - - - - - -
- - {/* SLA rows */} - {[ - { label: "Uptime", value: "99.97%", sla: "99.9%" }, - { label: "Failure Rate", value: "0.02%", sla: "<0.1%" }, - { label: "Swap Rate", value: "1.2%", sla: "<5%" }, - ].map((m, i) => ( -
- {m.label} -
- {m.value} - {m.sla} - - - - - -
-
- ))} - - {/* Bottom bar */} -
- All SLAs passing - ● Healthy -
-
-
- ); -} - -/* ── Section ── */ - export default function WhatIsLivepeer() { return (
@@ -358,88 +23,12 @@ export default function WhatIsLivepeer() { > - - {/* Single surface — all 4 panels */} - -
- {[ - { - Visual: ApiKeysVisual, - title: "Authenticate & connect", - description: - "Create an API key and choose a billing provider. Your key authenticates requests, your provider handles routing and payments.", - step: "1", - }, - { - Visual: ModelsVisual, - title: "Pick a model", - description: - "Browse available models optimized for real-time inference. See latency, type, and health across the network.", - step: "2", - }, - { - Visual: StreamVisual, - title: "Start streaming", - description: - "Open a session, send frames, receive outputs. The gateway routes your stream to the best-performing GPU provider.", - step: "3", - }, - { - Visual: UsageVisual, - title: "Ship with confidence", - description: - "Published SLAs on latency, uptime, and failure rate. Track performance per workflow, region, and GPU provider.", - step: "4", - }, - ].map((panel, i) => ( -
- {/* Visual area — fixed height so all panels align */} -
-
- -
- {/* Bottom fade-out — sits above SpotlightCard to blend into panel bg */} -
-
- {/* Text */} -
-

- {panel.step}. - {panel.title} -

-

- {panel.description} -

-
-
- ))} -
-
diff --git a/components/layout/Footer.tsx b/components/layout/Footer.tsx index b88a1e6..729039a 100644 --- a/components/layout/Footer.tsx +++ b/components/layout/Footer.tsx @@ -7,19 +7,17 @@ const footerNav = [ { title: "Network", links: [ - { label: "Explorer", href: EXTERNAL_LINKS.explorer }, - { label: "Delegate", href: EXTERNAL_LINKS.staking }, - { label: "Provide Compute", href: "https://docs.livepeer.org/v1/orchestrators/guides/get-started" }, - { label: "Roadmap", href: "https://github.com/livepeer/catalyst/milestones" }, + { label: "Delegate LPT", href: EXTERNAL_LINKS.explorer }, + { label: "Provide GPUs", href: "https://docs.livepeer.org" }, + { label: "Roadmap", href: "https://roadmap.livepeer.org/roadmap" }, ], }, { title: "Resources", links: [ - { label: "10-Minute Primer", href: "/primer" }, - { label: "Foundation", href: "/foundation" }, - { label: "Blog", href: "/blog" }, - { label: "Brand", href: "/brand" }, + { label: "Livepeer Primer", href: "/primer" }, + { label: "Livepeer Blog", href: "/blog" }, + { label: "Livepeer Brand", href: "/brand" }, ], }, { @@ -83,7 +81,7 @@ export default function Footer() {

- The world's open video infrastructure. + The open network for GPU-powered video.

{socialLinks.map((social) => ( diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx index 2c2efe7..823d949 100644 --- a/components/layout/Header.tsx +++ b/components/layout/Header.tsx @@ -167,17 +167,15 @@ export default function Header() { {NAV_ITEMS.filter((item) => item.href !== "/").map((item) => ( ))} - - - {/* Desktop CTA */} -
- - Get Early Access - -
+ Build with Livepeer + + {/* Mobile hamburger */}
+ setMobileOpen(false)} + className="rounded-xl px-4 py-3 text-lg text-white/50 transition-colors hover:bg-white/5 hover:text-white" + > + Build with Livepeer +
)} diff --git a/components/primer/PrimerContent.tsx b/components/primer/PrimerContent.tsx index 101f262..d496481 100644 --- a/components/primer/PrimerContent.tsx +++ b/components/primer/PrimerContent.tsx @@ -4,7 +4,6 @@ import { useEffect, useRef, useState, useCallback } from "react"; import Link from "next/link"; import { raleway } from "@/lib/fonts"; import type { ProtocolStats } from "@/lib/subgraph"; -import InflationMeter from "@/components/primer/InflationMeter"; import MintingDiagram from "@/components/primer/MintingDiagram"; /** @@ -27,8 +26,7 @@ const CHAPTERS = [ { id: "orchestrators", label: "Orchestrators", bg: "#a6adeb" }, { id: "token", label: "Livepeer Token", bg: "#d4b9e4" }, { id: "delegators", label: "Delegators", bg: "#ffc37b" }, - { id: "rewarding", label: "Rewarding Participation", bg: "#ffa3a3" }, - { id: "rounds", label: "Rounds & Inflation", bg: "#97f2ef" }, + { id: "rewarding", label: "Why It's Cheaper", bg: "#ffa3a3" }, { id: "involved", label: "Get Involved", bg: "#95f58c" }, ] as const; @@ -327,8 +325,8 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) { - What if there were an open network purpose-built for - this? + Livepeer is an open network where independent builders deploy AI + solutions on community-operated GPU infrastructure.
@@ -345,15 +343,15 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) { - Livepeer is a specialized GPU network for real-time - video AI. It coordinates a global pool of GPU - providers to deliver low-latency AI inference on video streams - — frame-by-frame processing, not batch jobs. + Livepeer is the open network for GPU-powered + video. It coordinates a global pool of independent GPU + operators to power real-time video AI — style transfer, + object detection, generative video, live streaming, and more. - The network is open-source, built on Ethereum for coordination - and permissionless access, and 60–85% cheaper than - centralized cloud alternatives. + The network is community-operated, built on Ethereum for + coordination, and 60–85% cheaper than centralized cloud + alternatives like AWS, RunPod, and Fal. {/* eslint-disable-next-line @next/next/no-img-element */} @@ -401,81 +399,59 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) { - {/* Meet Alice — contained, image left, text right */} - - - Meet: Alice + + - Alice is a developer building an interactive world model - — an AI-generated environment that responds to user - input in real-time, rendering 30–60 frames per second - of generated video, continuously. + The network organizes around three layers: supply,{" "} + protocol, and demand. - {/* eslint-disable-next-line @next/next/no-img-element */} - Alice - + - {/* Meet Bob — contained, text left, image right */} - - - Meet: Bob + + + Supply: GPU Operators - Bob is a gamer. He’s exploring Alice’s world model - — an AI-generated environment that reacts to his every - move in real-time. + Independent operators contribute GPU hardware by running Livepeer + software. They earn fees for performing AI inference, transcoding, + and other video workloads. Anyone with capable hardware can join. - {/* eslint-disable-next-line @next/next/no-img-element */} - Bob - + - {/* Devices — image flush left, text right */} - - + + + Protocol: Routing & Coordination - When Bob plays, every input he makes triggers a new frame of - AI-generated video. Alice’s app sends these requests to - Livepeer, which routes the GPU-intensive work across the - network and returns generated frames in real-time — - inference, encoding, and delivery in one pipeline. + The protocol matches workloads to the best available GPU. It + handles orchestrator discovery, job routing, payment settlement, + and quality-of-service checks. Built on Ethereum for transparency + and permissionless participation. + + + + + + Demand: Solutions - Bob’s experience is seamless. But behind the scenes, - Alice needs massive GPU compute to keep up — and her - app is gaining users fast. How does Livepeer make this - affordable? + Solutions are the products and businesses built on the protocol + — Daydream for creative AI video, Frameworks for live + streaming, Embody for AI avatars, and more. They package network + capabilities into experiences their audiences use and pay for. - {/* eslint-disable-next-line @next/next/no-img-element */} - Multiple devices - + {/* Two key actors — text left, image right — contained */} - There are two key roles in the Livepeer protocol that make - this work:{" "} - Orchestrators (GPU providers who run the + Two roles keep the network running:{" "} + Orchestrators (GPU providers who perform the compute) and{" "} Delegators (token holders who help secure - the network). - - - Let’s start with Orchestrators. + the network by staking toward reliable operators). {/* eslint-disable-next-line @next/next/no-img-element */} @@ -623,7 +599,7 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) {
- Rewarding Participation + Why It’s Cheaper @@ -671,92 +647,12 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) { />
- - - - Neat right? Next, let’s go over how often new tokens - are minted. - - - -
- - {/* ════════════════════════════════════════════════ - ROUNDS & INFLATION - ════════════════════════════════════════════════ */} -
- {/* Text left, image flush right */} - - - - Rounds & Inflation - - Rounds - - In Livepeer, new tokens are minted every so-called{" "} - round. Rounds are measured in Ethereum - blocks, where one round is equal to 5,760{" "} - Ethereum blocks. In Ethereum, one block is mined on average - every 12.69 seconds, which means one Livepeer - round lasts roughly 20.31 hours. Assuming the - Orchestrator you’re staked to is doing its job, this is - how often you can expect to receive reward tokens. - - - Next, let’s go over the Livepeer inflation rate, or in - other words, the way by which the Livepeer protocol determines - how many new tokens to mint each round. - - - {/* eslint-disable-next-line @next/next/no-img-element */} - Rounds illustration - - - {/* Image left, text right — contained */} - - - Inflation - - The current rate of inflation as of today’s round is{" "} - {stats.inflationRate} and there are currently - a total of {stats.totalSupply} Livepeer tokens - in supply. So, if you do the math, a total of{" "} - {stats.mintableTokens} newly minted Livepeer - tokens will be rewarded to all participants during the next - round. - - - The cool thing about Livepeer is the inflation rate adjusts - automatically depending on how many tokens are staked out of - the total circulating supply. Currently, the total supply of - Livepeer tokens stands at {stats.totalSupply}{" "} - and of those, {stats.totalStaked} are staked. - Livepeer refers to this ratio ( - {stats.participationRate}) as its - ‘participation rate’. - - - - - - Livepeer presupposes that a target rate of 50% is a healthy - trade-off between network security and token liquidity, so in - order to hit this target, the protocol incentivizes - participation by increasing the inflation rate by{" "} - {stats.inflationChange} for every round the - participation rate is below 50% and decreasing it{" "} - {stats.inflationChange} for every round the - participation rate is above 50%. + This structural cost advantage is why builders choose Livepeer + over centralized GPU clouds for workloads where cost makes the + difference between viable and impossible. @@ -766,46 +662,22 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) { GET INVOLVED ════════════════════════════════════════════════ */}
- -
-
- - LIVEPEER IS GROWING! - -
+ + + Get Involved + - {/* Text left, image right — contained */} - - + + - Today, there are {stats.delegatorsCount} delegators securing - the network, with more and more participants joining every - day. + Four solutions are shipping on Livepeer today: Daydream for + creative AI video, Frameworks for live streaming, Embody for + AI avatars, and a growing set of emerging projects. The network + is open to builders, GPU operators, and anyone who wants to + participate. - {/* eslint-disable-next-line @next/next/no-img-element */} - Network growth - - - - - Livepeer is open GPU infrastructure for real-time video AI - — specialized, affordable, and built to scale. Here’s - how to get involved. - {/* Three columns */} @@ -816,13 +688,13 @@ export default function PrimerContent({ stats }: { stats: ProtocolStats }) { title="Want to build with Livepeer?" > - Build real-time AI video applications with affordable GPU - compute. Get an API key and start building with Livepeer. + Join the builders and operators who run the network. Ask + questions, share what you’re working on, and find + collaborators. Date: Tue, 31 Mar 2026 21:55:00 -0400 Subject: [PATCH 02/12] v2 copy round 2: headline, hero, ecosystem section with animated cards, builders & operators, nav and footer updates Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 4 +- components/home/BuiltOnLivepeer.tsx | 55 ++- components/home/CommunityCTA.tsx | 46 ++- components/home/Hero.tsx | 23 +- components/home/UseCases.tsx | 554 ++++++++++++++++++++++++++++ components/layout/Footer.tsx | 10 +- lib/constants.ts | 7 +- 7 files changed, 633 insertions(+), 66 deletions(-) create mode 100644 components/home/UseCases.tsx diff --git a/app/page.tsx b/app/page.tsx index 83c54ad..bea2078 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,12 +1,12 @@ import Hero from "@/components/home/Hero"; -import BuiltOnLivepeer from "@/components/home/BuiltOnLivepeer"; +import UseCases from "@/components/home/UseCases"; import CommunityCTA from "@/components/home/CommunityCTA"; export default function Home() { return ( <> - + ); diff --git a/components/home/BuiltOnLivepeer.tsx b/components/home/BuiltOnLivepeer.tsx index 9c1dcea..432ebc5 100644 --- a/components/home/BuiltOnLivepeer.tsx +++ b/components/home/BuiltOnLivepeer.tsx @@ -422,32 +422,24 @@ const projects = [ Logo: DaydreamLogo, description: "Open-source engine for real-time generative video, audio, and live visuals.", - href: "https://daydream.live", - linkLabel: "Visit Daydream", }, { Visual: StudioVisual, Logo: FrameworksLogo, description: "Sovereign live streaming platform with SaaS, hybrid, and fully self-hosted modes. No cloud lock-in.", - href: "https://frameworks.network", - linkLabel: "Visit Frameworks", }, { Visual: StreamplaceVisual, Logo: StreamplaceLogo, description: "Streaming video infrastructure for decentralized social networks. Built on the AT Protocol powering Bluesky.", - href: "https://stream.place", - linkLabel: "Visit Streamplace", }, { Visual: EmbodyVisual, Logo: EmbodyLogo, description: "Embodied AI avatars for real-time tutoring and telepresence.", - href: "https://embody.zone", - linkLabel: "Visit Embody", }, ]; @@ -468,8 +460,8 @@ export default function BuiltOnLivepeer() { @@ -482,11 +474,8 @@ export default function BuiltOnLivepeer() { transition={{ duration: 0.4 }} className="lg:row-span-3" > - {/* 3 smaller cards stacked on the right */} @@ -512,26 +497,36 @@ export default function BuiltOnLivepeer() { variants={fadeUp} transition={{ duration: 0.4 }} > -

{project.description}

- - {project.linkLabel} - -
-
+
))}
+ + + + Explore the Ecosystem + + +
diff --git a/components/home/CommunityCTA.tsx b/components/home/CommunityCTA.tsx index 39df36e..2a70e56 100644 --- a/components/home/CommunityCTA.tsx +++ b/components/home/CommunityCTA.tsx @@ -7,14 +7,15 @@ import ImageMask from "@/components/ui/ImageMask"; import GlowOverlay from "@/components/ui/GlowOverlay"; import { EXTERNAL_LINKS } from "@/lib/constants"; -const channels = [ +const resources = [ { - name: "Discord", - description: "Build in real time with frontier developers, engineers, and GPU providers.", - href: EXTERNAL_LINKS.discord, + name: "Documentation", + description: "Technical guides and API references for builders and GPU providers.", + href: EXTERNAL_LINKS.docs, icon: ( - - + + + ), }, @@ -85,44 +86,41 @@ export default function CommunityCTA() { className="mx-auto max-w-3xl text-center" >

- Community + The Network

- Build alongside the network + Powered by independent GPU providers

- Livepeer is operated by global, independent GPU providers — builders, - developers, and experts in open video infrastructure. + Livepeer is a global network of independent GPU providers, builders, + and engineers who run the network and shape its direction. Open-source. + Permissionless.

-
- {channels.map((channel) => ( + {resources.map((resource) => (
- {channel.icon} + {resource.icon}
-

{channel.name}

+

{resource.name}

- {channel.description} + {resource.description}

diff --git a/components/home/Hero.tsx b/components/home/Hero.tsx index c0c6055..2ed7d84 100644 --- a/components/home/Hero.tsx +++ b/components/home/Hero.tsx @@ -275,7 +275,7 @@ export default function Hero() { > The open network for
- GPU-powered video + real-time AI video - For less than the centralized cloud — no platform lock-in, and a - direct line to builders powering the network. + Generate, transform, and interpret live video streams on a + permissionless GPU network — for less than the centralized cloud, + with no platform lock-in. + + Build with Livepeer + + diff --git a/components/home/UseCases.tsx b/components/home/UseCases.tsx new file mode 100644 index 0000000..69b46db --- /dev/null +++ b/components/home/UseCases.tsx @@ -0,0 +1,554 @@ +"use client"; + +import { useState, useEffect, useRef } from "react"; +import { motion, AnimatePresence, useInView } from "framer-motion"; +import Container from "@/components/ui/Container"; +import SectionHeader from "@/components/ui/SectionHeader"; + +const fadeUp = { + hidden: { opacity: 0, y: 12 }, + visible: { opacity: 1, y: 0 }, +}; + +/* ── Illustration: AI-Generated World ── */ +function WorldsVisual() { + const ref = useRef(null); + const inView = useInView(ref, { once: false, margin: "-50px" }); + + return ( +
+ {/* Sky gradient */} +
+ + {/* Stars */} + {[ + [12, 8], [28, 14], [45, 6], [62, 18], [78, 10], [88, 22], + [18, 24], [52, 20], [70, 12], [35, 16], + ].map(([x, y], i) => ( +
+ ))} + + {/* Terrain layers — revealed L→R by cursor, plays once */} +
+ + {/* Far mountains */} + + {/* Mid hills */} + + {/* Foreground */} + + {/* Grid lines overlaying terrain */} + {[0, 50, 100, 150, 200, 250, 300, 350, 400].map((x) => ( + + ))} + {[0, 30, 60, 90, 120].map((y) => ( + + ))} + +
+ + {/* Generation cursor — sweeps once then disappears */} +
+ + {/* HUD */} +
+ GENERATING +
+
+ 60 FPS +
+
+ Frame 1,847 · 12ms +
+
+ ); +} + +/* ── Illustration: AI Avatars & Agents ── */ +function AvatarsVisual() { + const pts: [number, number][] = [ + [35, 28], [65, 28], [50, 23], + [30, 40], [42, 38], [58, 38], [70, 40], + [50, 48], [44, 46], [56, 46], + [38, 58], [50, 60], [62, 58], + [35, 48], [65, 48], [50, 68], + ]; + const lines: [number, number][] = [ + [0, 2], [2, 1], [0, 3], [3, 4], [4, 5], [5, 6], [6, 1], + [4, 8], [5, 9], [8, 7], [9, 7], + [3, 13], [6, 14], [10, 11], [11, 12], [13, 10], [14, 12], [11, 15], + ]; + return ( +
+
+ {/* Input */} +
+
+
+
+ + {lines.map(([a, b], i) => ( + + ))} + {pts.map(([x, y], i) => ( + + ))} + +
INPUT
+
+ + {/* Arrow */} + + + + + {/* Output */} +
+
+
+
+
+
+
+
+
OUTPUT
+
+
+ + {/* Bottom label */} +
+ Style: Anime · 22ms +
+
+ ); +} + +/* ── Tracking box — smooth drift + micro-jitter + detection lifecycle ── */ +interface Detection { + x: number; y: number; w: number; h: number; + label: string; color: string; conf: number; + drift: { freqX: number; freqY: number; amtX: number; amtY: number }; + sil: { left: string; top: string; w: string; h: string; opacity: number }; +} + +function TrackingBox({ d, i, active, entered }: { + d: Detection; i: number; active: boolean; entered: boolean; +}) { + const [pos, setPos] = useState({ x: 0, y: 0 }); + const [conf, setConf] = useState(d.conf); + const t0 = useRef(Date.now()); + + useEffect(() => { + const tick = setInterval(() => { + const t = (Date.now() - t0.current) / 1000; + + const dx = + Math.sin(t * d.drift.freqX) * d.drift.amtX + + Math.sin(t * d.drift.freqX * 2.3 + 1.2) * d.drift.amtX * 0.3; + const dy = + Math.sin(t * d.drift.freqY + 0.7) * d.drift.amtY + + Math.cos(t * d.drift.freqY * 1.7 + 0.4) * d.drift.amtY * 0.3; + + setPos({ x: dx, y: dy }); + setConf(Math.min(0.99, Math.max(0.68, d.conf + (Math.random() - 0.5) * 0.1))); + }, 200); + + return () => clearInterval(tick); + }, [d]); + + return ( + + {entered && ( + + {/* Silhouette */} +
+ {/* Border with glow */} +
+ {/* Label + confidence */} +
+ {d.label} + {conf.toFixed(2)} +
+ + )} + + ); +} + +/* ── Illustration: Real-Time Video Analysis ── */ +const analysisDetections: Detection[] = [ + { x: 10, y: 15, w: 20, h: 40, label: "person", color: "#34d399", conf: 0.94, + drift: { freqX: 0.3, freqY: 0.18, amtX: 8, amtY: 12 }, + sil: { left: "15%", top: "7.5%", w: "35%", h: "95%", opacity: 0.06 } }, + { x: 55, y: 25, w: 16, h: 32, label: "person", color: "#34d399", conf: 0.87, + drift: { freqX: 0.22, freqY: 0.28, amtX: 6, amtY: 10 }, + sil: { left: "12.5%", top: "9.4%", w: "37.5%", h: "87.5%", opacity: 0.045 } }, + { x: 35, y: 58, w: 30, h: 18, label: "vehicle", color: "#60a5fa", conf: 0.91, + drift: { freqX: 0.15, freqY: 0.35, amtX: 14, amtY: 3 }, + sil: { left: "6.7%", top: "11.1%", w: "86.7%", h: "66.7%", opacity: 0.03 } }, +]; + +function AnalysisVisual() { + const [active, setActive] = useState([true, true, true]); + const [entered, setEntered] = useState([false, false, false]); + + useEffect(() => { + const timers = analysisDetections.map((_, i) => + setTimeout(() => { + setEntered((prev) => { const n = [...prev]; n[i] = true; return n; }); + }, 400 + i * 500) + ); + + const flicker = setInterval(() => { + const idx = Math.floor(Math.random() * analysisDetections.length); + if (Math.random() < 0.22) { + setActive((prev) => { const n = [...prev]; n[idx] = false; return n; }); + setTimeout(() => { + setActive((prev) => { const n = [...prev]; n[idx] = true; return n; }); + }, 120 + Math.random() * 200); + } + }, 2800); + + return () => { timers.forEach(clearTimeout); clearInterval(flicker); }; + }, []); + + const personCount = analysisDetections.filter( + (d, i) => d.label === "person" && active[i] && entered[i] + ).length; + const vehicleCount = analysisDetections.filter( + (d, i) => d.label === "vehicle" && active[i] && entered[i] + ).length; + + return ( +
+
+ {analysisDetections.map((d, i) => ( + + ))} +
+ + LIVE +
+
+ YOLOv8 · 8ms +
+
+ {personCount > 0 && ( + + {personCount} {personCount === 1 ? "person" : "persons"} + + )} + {vehicleCount > 0 && ( + + {vehicleCount} {vehicleCount === 1 ? "vehicle" : "vehicles"} + + )} +
+
+ ); +} + +/* ── Illustration: Composable AI Pipelines (3 nodes) ── */ +function PipelinesVisual() { + const nodes = [ + { label: "Ingest", x: 20 }, + { label: "Model", x: 120, active: true }, + { label: "Output", x: 220 }, + ]; + const nodeW = 64; + const nodeH = 26; + const nodeY = 14; + return ( +
+
+ + {/* Pipeline node graph */} +
+ + {/* Connection lines + flowing data packets */} + {nodes.slice(0, -1).map((node, i) => { + const next = nodes[i + 1]; + const x1 = node.x + nodeW; + const x2 = next.x; + const cy = nodeY + nodeH / 2; + return ( + + + {/* Data packet */} + + + + + {/* Second staggered packet */} + + + + + + ); + })} + + {/* Nodes */} + {nodes.map((node) => ( + + + + {node.label} + + + ))} + +
+ + {/* Bottom HUD */} +
+ Pipeline: img2img · 3 stages +
+
+ Active +
+
+ ); +} + +/* ── Use Cases data ── */ +const useCases: { + title: string; + description: string; + attribution: string; + Visual: React.ComponentType; + colSpan: 2 | 4; +}[] = [ + { + title: "AI-Generated Worlds", + description: + "Live environments inferred in real time from user inputs — not pre-rendered, not pre-recorded. The world itself is the model's output.", + attribution: "Powered by Daydream on Livepeer.", + Visual: WorldsVisual, + colSpan: 4, + }, + { + title: "AI Avatars", + description: + "Video representations of AI agents that respond in real time — for tutoring, telepresence, and conversational interfaces.", + attribution: "Powered by Embody on Livepeer.", + Visual: AvatarsVisual, + colSpan: 2, + }, + { + title: "Live Video Intelligence", + description: + "Real-time analysis of live streams — sports broadcasts, security feeds, manufacturing lines. Processed during, not after.", + attribution: "Emerging on Livepeer.", + Visual: AnalysisVisual, + colSpan: 2, + }, + { + title: "Prompt-Driven Live Transformation", + description: + "A creator goes live and the stream adapts — lighting, environment, visual style — based on a text prompt, in real time.", + attribution: "Powered by Daydream on Livepeer.", + Visual: PipelinesVisual, + colSpan: 4, + }, +]; + +const colSpanClass: Record = { + 2: "lg:col-span-2", + 4: "lg:col-span-4", +}; + +export default function UseCases() { + return ( +
+
+ + + + + + + +
+ {useCases.map((useCase) => ( + +
+
+ +
+
+

{useCase.title}

+

+ {useCase.description} +

+

+ {useCase.attribution} +

+
+
+
+ ))} +
+ + + + Explore the Ecosystem + + + +
+
+
+ ); +} diff --git a/components/layout/Footer.tsx b/components/layout/Footer.tsx index 729039a..596c7ee 100644 --- a/components/layout/Footer.tsx +++ b/components/layout/Footer.tsx @@ -10,14 +10,15 @@ const footerNav = [ { label: "Delegate LPT", href: EXTERNAL_LINKS.explorer }, { label: "Provide GPUs", href: "https://docs.livepeer.org" }, { label: "Roadmap", href: "https://roadmap.livepeer.org/roadmap" }, + { label: "Ecosystem (beta)", href: "/ecosystem" }, ], }, { title: "Resources", links: [ - { label: "Livepeer Primer", href: "/primer" }, - { label: "Livepeer Blog", href: "/blog" }, - { label: "Livepeer Brand", href: "/brand" }, + { label: "Primer", href: "/primer" }, + { label: "Blog", href: "/blog" }, + { label: "Documentation", href: "https://docs.livepeer.org" }, ], }, { @@ -26,6 +27,7 @@ const footerNav = [ { label: "Discord", href: EXTERNAL_LINKS.discord }, { label: "X / Twitter", href: EXTERNAL_LINKS.twitter }, { label: "Forum", href: EXTERNAL_LINKS.forum }, + { label: "YouTube", href: "https://www.youtube.com/@LivepeerProject" }, ], }, ]; @@ -81,7 +83,7 @@ export default function Footer() {

- The open network for GPU-powered video. + The open network for real-time AI video.

{socialLinks.map((social) => ( diff --git a/lib/constants.ts b/lib/constants.ts index c7257fe..bf22321 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -19,15 +19,16 @@ export const NAV_ITEMS: NavItem[] = [ { label: "Delegate LPT", href: "https://explorer.livepeer.org", external: true }, { label: "Provide GPUs", href: "https://docs.livepeer.org", external: true }, { label: "Roadmap", href: "https://roadmap.livepeer.org/roadmap", external: true }, + { label: "Ecosystem (beta)", href: "/ecosystem" }, ], }, { label: "Resources", href: "/brand", children: [ - { label: "Livepeer Primer", href: "/primer" }, - { label: "Livepeer Blog", href: "/blog" }, - { label: "Livepeer Brand", href: "/brand" }, + { label: "Primer", href: "/primer" }, + { label: "Blog", href: "/blog" }, + { label: "Documentation", href: "https://docs.livepeer.org", external: true }, ], }, ]; From 78f530322eb0342eea16cd9d2312f9946a1acb04 Mon Sep 17 00:00:00 2001 From: Steph Alinsug Date: Thu, 2 Apr 2026 00:30:13 -0400 Subject: [PATCH 03/12] v2 copy round 3: token page, solution cards, nav updates, copy refinements - Add token page with TokenFlowVisualization, end-user copy, active voice - Update hero subtitle for real-time video inference positioning - Split ecosystem section: Use Cases + Solution Cards with "Live on Livepeer" headline - Rename solution cards (Daydream, Frameworks), add flower visual + Embody motion - Add "Ecosystem (coming soon)" to nav and footer, "Brand" to Resources dropdown - Update network headline, Discord subtitle, solutions subtitle copy - Add NetworkSpirits canvas component (not rendered on homepage) Co-Authored-By: Claude Opus 4.6 --- app/token/page.tsx | 425 +++++++++++++++++++++ components/home/CommunityCTA.tsx | 11 +- components/home/Hero.tsx | 7 +- components/home/NetworkSpirits.tsx | 592 ++++++++++++++++++++++++++++ components/home/UseCases.tsx | 593 ++++++++++++++++++++++++++++- components/layout/Footer.tsx | 5 +- components/layout/Header.tsx | 12 +- lib/constants.ts | 4 +- 8 files changed, 1620 insertions(+), 29 deletions(-) create mode 100644 app/token/page.tsx create mode 100644 components/home/NetworkSpirits.tsx diff --git a/app/token/page.tsx b/app/token/page.tsx new file mode 100644 index 0000000..e5bf603 --- /dev/null +++ b/app/token/page.tsx @@ -0,0 +1,425 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { motion } from "framer-motion"; +import Container from "@/components/ui/Container"; +import SectionHeader from "@/components/ui/SectionHeader"; +import Button from "@/components/ui/Button"; +import { EXTERNAL_LINKS } from "@/lib/constants"; + +const fadeUp = { + hidden: { opacity: 0, y: 12 }, + visible: { opacity: 1, y: 0 }, +}; + +/* ── Token Flow Visualization ── */ +const FC = { + cardBg: "rgba(255,255,255,0.04)", + cardBorder: "rgba(255,255,255,0.08)", + cardBorderHover: "rgba(255,255,255,0.15)", + green: "#00EB88", + greenDim: "rgba(0, 235, 136, 0.08)", + greenSoft: "rgba(0, 235, 136, 0.2)", + greenGlow: "rgba(0, 235, 136, 0.4)", + textPrimary: "rgba(255,255,255,0.92)", + textSecondary: "rgba(255,255,255,0.55)", + textTertiary: "rgba(255,255,255,0.3)", +}; + +function FlowNode({ x, y, label, sublabel, number, isActive, width = 150, height = 64 }: { + x: number; y: number; label: string; sublabel: string; number: string; isActive: boolean; width?: number; height?: number; +}) { + const r = 10; + return ( + + {isActive && ( + + + + )} + + + {number} + + + {label} + + + {sublabel} + + + ); +} + +function TokenPacket({ path, dur, delay, label, size = 3.5, color }: { + path: string; dur: string; delay: string; label?: string; size?: number; color?: string; +}) { + const c = color || FC.green; + return ( + + + + + + + + + + + + + + {label && ( + + + + {label} + + )} + + ); +} + +function FlowPath({ d, dashed = false }: { d: string; dashed?: boolean }) { + return ( + + ); +} + +function TokenFlowVisualization() { + const [activeNode, setActiveNode] = useState(0); + const [isPaused, setIsPaused] = useState(false); + + useEffect(() => { + if (isPaused) return; + const id = setInterval(() => setActiveNode((n) => (n + 1) % 5), 3000); + return () => clearInterval(id); + }, [isPaused]); + + const flowNodes = [ + { id: 0, label: "Applications", sublabel: "Request video compute jobs", number: "01", x: 130, y: 85 }, + { id: 1, label: "Gateway Nodes", sublabel: "Route jobs to orchestrators", number: "02", x: 400, y: 85 }, + { id: 2, label: "Orchestrator Nodes", sublabel: "GPU clusters process work", number: "03", x: 670, y: 85 }, + { id: 3, label: "Delegators", sublabel: "Stake LPT for network security", number: "04", x: 670, y: 275 }, + { id: 4, label: "Micropayments", sublabel: "Ticket-based payment settlement", number: "05", x: 400, y: 275 }, + ]; + + const descriptions = [ + "Apps send video compute requests through Livepeer network endpoints.", + "Gateways receive jobs from apps and route them to available orchestrators.", + "Orchestrators perform transcoding and AI processing on GPU clusters.", + "Delegators stake LPT to orchestrators, earning fees and rewards.", + "Gateways pay orchestrators via probabilistic micropayment tickets.", + ]; + + return ( +
+ {/* Active description bar */} +
+
+
+ {descriptions[activeNode]} +
+ +
+ + {/* SVG visualization */} +
+ + + + + + + + + + + + + + + + + {/* Paths */} + + + + + + + + + + + + + {/* Path labels */} + REQUESTS + JOBS + REWARDS + STAKE + TICKETS + SETTLEMENT + + {/* Center label */} + LPT COORDINATION + Token secures every layer + + {/* Animated packets */} + + + + + + + + + + + {/* Nodes */} + {flowNodes.map((n) => ( + + ))} + + {/* Ambient particles */} + {Array.from({ length: 10 }).map((_, i) => ( + + + + ))} + +
+ + {/* Node selector tabs */} +
+ {flowNodes.map((node, i) => ( + + ))} +
+
+ ); +} + +const exchanges = [ + { name: "Binance", href: "https://www.binance.com/en/trade/LPT_USDT" }, + { name: "Coinbase", href: "https://www.coinbase.com/price/livepeer" }, + { name: "Kraken", href: "https://www.kraken.com/prices/livepeer" }, + { name: "Uniswap", href: "https://app.uniswap.org/tokens/ethereum/0x58b6a8a3302369daec383334672404ee733ab239" }, + { name: "OKX", href: "https://www.okx.com/trade-spot/lpt-usdt" }, +]; + + +const tokenFacts = [ + "An ERC-20 token on Ethereum, available on major exchanges like Coinbase, Binance, and Kraken.", + "The protocol mints new LPT each round and distributes it to active participants, incentivizing ongoing network participation.", + "Inactive token holdings dilute over time, encouraging active delegation to keep the network secure.", +]; + +export default function TokenPage() { + return ( + <> + {/* Hero */} +
+ + + + Livepeer Token + + + The token that powers the network + + + Livepeer Token (LPT) is the coordination mechanism behind the + Livepeer network. It aligns incentives between the GPU providers + who do the work, the applications that need video processing, and + the stakeholders who help secure the network. + + + + + + +
+ + {/* What is LPT */} +
+
+ + + + + + + + +
+ + {/* Network Architecture */} +
+
+ + + + + + + {/* Interactive flow visualization */} + + + + + +
+ + {/* Exchanges */} +
+
+ + + + + + +
+ {exchanges.map((exchange) => ( + + + {exchange.name} + + + Explore + + + + + + ))} +
+ + {/* Delegate LPT */} + +
+

+ Delegate LPT +

+

+ Delegators back GPU providers with LPT and earn a share of + the fees those providers generate. +

+
+ +
+
+
+
+ + ); +} diff --git a/components/home/CommunityCTA.tsx b/components/home/CommunityCTA.tsx index 2a70e56..f962d8a 100644 --- a/components/home/CommunityCTA.tsx +++ b/components/home/CommunityCTA.tsx @@ -9,13 +9,12 @@ import { EXTERNAL_LINKS } from "@/lib/constants"; const resources = [ { - name: "Documentation", - description: "Technical guides and API references for builders and GPU providers.", - href: EXTERNAL_LINKS.docs, + name: "Discord", + description: "Join the operators, developers, and stakeholders building Livepeer.", + href: EXTERNAL_LINKS.discord, icon: ( - - - + + ), }, diff --git a/components/home/Hero.tsx b/components/home/Hero.tsx index 2ed7d84..c21f2b1 100644 --- a/components/home/Hero.tsx +++ b/components/home/Hero.tsx @@ -285,14 +285,11 @@ export default function Hero() { transition={{ duration: 0.6, delay: 0.4 }} > Generate, transform, and interpret live video streams on a - permissionless GPU network — for less than the centralized cloud, - with no platform lock-in. + permissionless GPU network built for real-time video inference. { + s = (s * 16807) % 2147483647; + return (s - 1) / 2147483646; + }; +} + +// ─── Types ──────────────────────────────────── + +interface NetworkNode { + x: number; + y: number; + size: number; + energy: number; + speed: number; + phase: number; + type: "hub" | "secondary" | "dust"; +} + +interface Connection { + a: NetworkNode; + b: NetworkNode; + strength: number; + speed: number; + offset: number; +} + +interface TrailPoint { + x: number; + y: number; +} + +interface TravelerSpirit { + type: "traveler"; + conn: Connection; + progress: number; + speed: number; + size: number; + bodyPhase: number; + wobbleAmp: number; + wobbleSpeed: number; + trail: TrailPoint[]; + trailLength: number; + eyeSpacing: number; + hasEars: boolean; + hasTail: boolean; + legPhase: number; + brightness: number; + pauseAt: number; + pauseTimer: number; + direction: number; +} + +interface FloaterSpirit { + type: "floater"; + hub: NetworkNode; + angle: number; + orbitSpeed: number; + orbitRadius: number; + size: number; + bobPhase: number; + bobAmp: number; + brightness: number; + trail: TrailPoint[]; + trailLength: number; +} + +type Spirit = TravelerSpirit | FloaterSpirit; + +// ─── NETWORK TOPOLOGY ──────────────────────── + +function buildNetwork() { + const rand = seeded(77); + const nodes: NetworkNode[] = []; + const connections: Connection[] = []; + const hubs: NetworkNode[] = []; + + const hubPositions: [number, number][] = [ + [120, 150], [300, 75], [500, 170], [720, 85], [940, 145], + [1080, 75], [200, 300], [650, 310], [860, 270], [420, 330], [1040, 310], + [380, 180], [780, 200], [160, 220], + ]; + + hubPositions.forEach(([x, y]) => { + const node: NetworkNode = { + x, y, + size: 3.5 + rand() * 3, + energy: 0.7 + rand() * 0.3, + speed: 1.5 + rand() * 2, + phase: rand() * Math.PI * 2, + type: "hub", + }; + hubs.push(node); + nodes.push(node); + }); + + for (let i = 0; i < 20; i++) { + nodes.push({ + x: 40 + rand() * 1120, + y: 30 + rand() * 380, + size: 1.8 + rand() * 2, + energy: 0.4 + rand() * 0.4, + speed: 2 + rand() * 2.5, + phase: rand() * Math.PI * 2, + type: "secondary", + }); + } + + for (let i = 0; i < 50; i++) { + nodes.push({ + x: rand() * 1200, + y: rand() * 440, + size: 0.5 + rand() * 1, + energy: 0.1 + rand() * 0.25, + speed: 3 + rand() * 4, + phase: rand() * Math.PI * 2, + type: "dust", + }); + } + + for (let i = 0; i < hubs.length; i++) { + for (let j = i + 1; j < hubs.length; j++) { + const d = Math.hypot(hubs[i].x - hubs[j].x, hubs[i].y - hubs[j].y); + if (d < 350) { + connections.push({ + a: hubs[i], b: hubs[j], + strength: 1 - d / 350, + speed: 0.2 + rand() * 0.4, + offset: rand() * 100, + }); + } + } + } + + const secs = nodes.filter(n => n.type === "secondary"); + hubs.forEach(hub => { + secs.forEach(sec => { + const d = Math.hypot(hub.x - sec.x, hub.y - sec.y); + if (d < 200) { + connections.push({ + a: hub, b: sec, + strength: (1 - d / 200) * 0.5, + speed: 0.3 + rand() * 0.3, + offset: rand() * 100, + }); + } + }); + }); + + return { nodes, connections, hubs }; +} + +// ─── SPIRIT CREATURES ──────────────────────── + +function createSpirits(connections: Connection[], hubs: NetworkNode[]): Spirit[] { + const rand = seeded(123); + const spirits: Spirit[] = []; + + const goodConns = connections.filter(c => c.strength > 0.3); + for (let i = 0; i < 12; i++) { + const conn = goodConns[Math.floor(rand() * goodConns.length)]; + spirits.push({ + type: "traveler", + conn, + progress: rand(), + speed: 0.03 + rand() * 0.05, + size: 4 + rand() * 5, + bodyPhase: rand() * Math.PI * 2, + wobbleAmp: 3 + rand() * 5, + wobbleSpeed: 2 + rand() * 3, + trail: [], + trailLength: 8 + Math.floor(rand() * 12), + eyeSpacing: 2 + rand() * 2, + hasEars: rand() > 0.4, + hasTail: rand() > 0.3, + legPhase: rand() * Math.PI * 2, + brightness: 0.7 + rand() * 0.3, + pauseAt: rand() > 0.6 ? 0.4 + rand() * 0.2 : -1, + pauseTimer: 0, + direction: rand() > 0.5 ? 1 : -1, + }); + } + + for (let i = 0; i < 6; i++) { + const hub = hubs[Math.floor(rand() * hubs.length)]; + spirits.push({ + type: "floater", + hub, + angle: rand() * Math.PI * 2, + orbitSpeed: 0.3 + rand() * 0.5, + orbitRadius: 20 + rand() * 30, + size: 3 + rand() * 3, + bobPhase: rand() * Math.PI * 2, + bobAmp: 4 + rand() * 4, + brightness: 0.5 + rand() * 0.3, + trail: [], + trailLength: 6, + }); + } + + return spirits; +} + +const NET = buildNetwork(); +const SPIRITS = createSpirits(NET.connections, NET.hubs); + +const BG = "#121212"; + +export default function NetworkSpirits() { + const canvasRef = useRef(null); + const animRef = useRef(0); + + const draw = useCallback(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; + const w = canvas.width; + const h = canvas.height; + const t = performance.now() * 0.001; + const dt = 0.016; + + ctx.clearRect(0, 0, w, h); + + // ─── ATMOSPHERE ─── + const g1 = ctx.createRadialGradient(w * 0.3, h * 0.35, 0, w * 0.3, h * 0.35, w * 0.4); + g1.addColorStop(0, "rgba(0, 235, 136, 0.035)"); + g1.addColorStop(1, "rgba(0, 0, 0, 0)"); + ctx.fillStyle = g1; + ctx.fillRect(0, 0, w, h); + + const g2 = ctx.createRadialGradient(w * 0.75, h * 0.6, 0, w * 0.75, h * 0.6, w * 0.35); + g2.addColorStop(0, "rgba(0, 235, 136, 0.02)"); + g2.addColorStop(1, "rgba(0, 0, 0, 0)"); + ctx.fillStyle = g2; + ctx.fillRect(0, 0, w, h); + + // ─── CONNECTIONS ─── + NET.connections.forEach(conn => { + const wave = Math.sin(t * conn.speed * 2 + conn.offset) * 0.5 + 0.5; + const alpha = conn.strength * (0.03 + wave * 0.05); + const mx = (conn.a.x + conn.b.x) / 2 + Math.sin(t * 0.7 + conn.offset) * 10; + const my = (conn.a.y + conn.b.y) / 2 + Math.cos(t * 0.5 + conn.offset) * 7; + + ctx.beginPath(); + ctx.moveTo(conn.a.x, conn.a.y); + ctx.quadraticCurveTo(mx, my, conn.b.x, conn.b.y); + ctx.strokeStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${alpha})`; + ctx.lineWidth = conn.strength > 0.5 ? 0.8 : 0.5; + ctx.stroke(); + + const pT = ((t * conn.speed * 0.6 + conn.offset * 0.1) % 1); + const pAlpha = Math.sin(pT * Math.PI) * conn.strength * 0.4; + if (pAlpha > 0.03) { + const u = pT; + const px = (1 - u) * (1 - u) * conn.a.x + 2 * (1 - u) * u * mx + u * u * conn.b.x; + const py = (1 - u) * (1 - u) * conn.a.y + 2 * (1 - u) * u * my + u * u * conn.b.y; + ctx.beginPath(); + ctx.arc(px, py, 1.2, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${pAlpha})`; + ctx.fill(); + } + }); + + // ─── NODES ─── + NET.nodes.forEach(node => { + const pulse = Math.sin(t * (Math.PI * 2) / node.speed + node.phase) * 0.5 + 0.5; + + if (node.type === "hub") { + ctx.beginPath(); + ctx.arc(node.x, node.y, node.size * 3.5 + pulse * node.size * 2, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.012 + pulse * 0.012})`; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(node.x, node.y, node.size + 2 + pulse, 0, Math.PI * 2); + ctx.strokeStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.08 + pulse * 0.1})`; + ctx.lineWidth = 0.5; + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(node.x, node.y, node.size, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.35 + pulse * 0.35})`; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(node.x, node.y, node.size * 0.3, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${0.2 + pulse * 0.3})`; + ctx.fill(); + } else if (node.type === "secondary") { + ctx.beginPath(); + ctx.arc(node.x, node.y, node.size, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.15 + pulse * 0.2})`; + ctx.fill(); + } else { + ctx.beginPath(); + ctx.arc(node.x, node.y, node.size, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.03 + pulse * node.energy * 0.12})`; + ctx.fill(); + } + }); + + // ─── SPIRIT CREATURES ─── + SPIRITS.forEach(spirit => { + if (spirit.type === "traveler") { + const conn = spirit.conn; + + if (spirit.pauseAt > 0 && Math.abs(spirit.progress - spirit.pauseAt) < 0.02) { + spirit.pauseTimer += dt; + if (spirit.pauseTimer >= 1.5) { + spirit.pauseTimer = 0; + spirit.pauseAt = -1; + } + } else { + spirit.progress += spirit.speed * dt * spirit.direction; + } + + if (spirit.progress > 1) { spirit.progress = 1; spirit.direction = -1; } + if (spirit.progress < 0) { spirit.progress = 0; spirit.direction = 1; } + + const mx = (conn.a.x + conn.b.x) / 2 + Math.sin(t * 0.7 + conn.offset) * 10; + const my = (conn.a.y + conn.b.y) / 2 + Math.cos(t * 0.5 + conn.offset) * 7; + const u = spirit.progress; + const baseX = (1 - u) * (1 - u) * conn.a.x + 2 * (1 - u) * u * mx + u * u * conn.b.x; + const baseY = (1 - u) * (1 - u) * conn.a.y + 2 * (1 - u) * u * my + u * u * conn.b.y; + + const wobble = Math.sin(t * spirit.wobbleSpeed + spirit.bodyPhase) * spirit.wobbleAmp; + const nextU = Math.min(1, u + 0.01); + const nx = (1 - nextU) * (1 - nextU) * conn.a.x + 2 * (1 - nextU) * nextU * mx + nextU * nextU * conn.b.x; + const ny = (1 - nextU) * (1 - nextU) * conn.a.y + 2 * (1 - nextU) * nextU * my + nextU * nextU * conn.b.y; + const ddx = nx - baseX; + const ddy = ny - baseY; + const len = Math.hypot(ddx, ddy) || 1; + const perpX = -ddy / len; + const perpY = ddx / len; + + const x = baseX + perpX * wobble; + const y = baseY + perpY * wobble; + + spirit.trail.unshift({ x, y }); + if (spirit.trail.length > spirit.trailLength) spirit.trail.pop(); + + spirit.trail.forEach((tp, i) => { + const fade = 1 - i / spirit.trail.length; + const trailSize = spirit.size * 0.35 * fade; + ctx.beginPath(); + ctx.arc(tp.x, tp.y, trailSize, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${fade * 0.15 * spirit.brightness})`; + ctx.fill(); + }); + + if (spirit.trail.length > 2) { + ctx.beginPath(); + ctx.moveTo(spirit.trail[0].x, spirit.trail[0].y); + for (let i = 1; i < spirit.trail.length; i++) { + ctx.lineTo(spirit.trail[i].x, spirit.trail[i].y); + } + ctx.strokeStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, 0.08)`; + ctx.lineWidth = spirit.size * 0.5; + ctx.lineCap = "round"; + ctx.stroke(); + } + + const s = spirit.size; + const breathe = Math.sin(t * 2 + spirit.bodyPhase) * 0.1 + 1; + const squish = 1 + Math.sin(t * spirit.wobbleSpeed * 2) * 0.05; + + ctx.beginPath(); + ctx.arc(x, y, s * 2.5 * breathe, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.04 * spirit.brightness})`; + ctx.fill(); + + ctx.save(); + ctx.translate(x, y); + ctx.scale(squish, 1 / squish); + ctx.beginPath(); + ctx.arc(0, 0, s * breathe, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.4 * spirit.brightness})`; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(0, 0, s * 0.6 * breathe, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.6 * spirit.brightness})`; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(0, 0, s * 0.25, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${0.5 * spirit.brightness})`; + ctx.fill(); + + const eyeY = -s * 0.15; + const blinkPhase = Math.sin(t * 0.5 + spirit.bodyPhase * 3); + const eyeOpen = blinkPhase > -0.9 ? 1 : 0.1; + ctx.beginPath(); + ctx.arc(-spirit.eyeSpacing, eyeY, s * 0.12 * eyeOpen, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${0.8 * spirit.brightness})`; + ctx.fill(); + ctx.beginPath(); + ctx.arc(spirit.eyeSpacing, eyeY, s * 0.12 * eyeOpen, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${0.8 * spirit.brightness})`; + ctx.fill(); + + if (spirit.hasEars) { + const earWave = Math.sin(t * 3 + spirit.bodyPhase) * 2; + ctx.beginPath(); + ctx.arc(-s * 0.4, -s * 0.8 + earWave, s * 0.15, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.5 * spirit.brightness})`; + ctx.fill(); + ctx.beginPath(); + ctx.arc(s * 0.4, -s * 0.8 - earWave, s * 0.15, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.5 * spirit.brightness})`; + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(-s * 0.3, -s * 0.5); + ctx.lineTo(-s * 0.4, -s * 0.8 + earWave); + ctx.strokeStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.2 * spirit.brightness})`; + ctx.lineWidth = 0.5; + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(s * 0.3, -s * 0.5); + ctx.lineTo(s * 0.4, -s * 0.8 - earWave); + ctx.stroke(); + } + + ctx.restore(); + + const legCycle = Math.sin(t * 8 + spirit.legPhase); + const legOffsetL = legCycle * 2; + const legOffsetR = -legCycle * 2; + ctx.beginPath(); + ctx.arc(x - s * 0.3 + legOffsetL, y + s * 0.9, s * 0.12, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.35 * spirit.brightness})`; + ctx.fill(); + ctx.beginPath(); + ctx.arc(x + s * 0.3 + legOffsetR, y + s * 0.9, s * 0.12, 0, Math.PI * 2); + ctx.fill(); + + if (spirit.hasTail) { + const tailDir = -spirit.direction; + ctx.beginPath(); + ctx.moveTo(x + tailDir * s * 0.6, y); + ctx.quadraticCurveTo( + x + tailDir * s * 1.5, y + Math.sin(t * 4 + spirit.bodyPhase) * 4, + x + tailDir * s * 2, y + Math.sin(t * 3 + spirit.bodyPhase) * 3 + ); + ctx.strokeStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.15 * spirit.brightness})`; + ctx.lineWidth = 1.2; + ctx.lineCap = "round"; + ctx.stroke(); + const tailTipX = x + tailDir * s * 2; + const tailTipY = y + Math.sin(t * 3 + spirit.bodyPhase) * 3; + ctx.beginPath(); + ctx.arc(tailTipX, tailTipY, s * 0.1, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.3 * spirit.brightness})`; + ctx.fill(); + } + + for (let p = 0; p < 3; p++) { + const sparkleAge = (t * 2 + p * 1.3 + spirit.bodyPhase) % 2; + if (sparkleAge < 1) { + const sparkleX = x + Math.cos(t * 3 + p * 2.1) * s * (1 + sparkleAge); + const sparkleY = y + Math.sin(t * 2.5 + p * 1.7) * s * (1 + sparkleAge) - sparkleAge * 8; + const sparkleAlpha = (1 - sparkleAge) * 0.35 * spirit.brightness; + ctx.beginPath(); + ctx.arc(sparkleX, sparkleY, 0.8, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${sparkleAlpha})`; + ctx.fill(); + } + } + + } else if (spirit.type === "floater") { + spirit.angle += spirit.orbitSpeed * dt; + const bob = Math.sin(t * 1.5 + spirit.bobPhase) * spirit.bobAmp; + const x = spirit.hub.x + Math.cos(spirit.angle) * spirit.orbitRadius; + const y = spirit.hub.y + Math.sin(spirit.angle) * spirit.orbitRadius * 0.6 + bob; + + spirit.trail.unshift({ x, y }); + if (spirit.trail.length > spirit.trailLength) spirit.trail.pop(); + + spirit.trail.forEach((tp, i) => { + const fade = 1 - i / spirit.trail.length; + ctx.beginPath(); + ctx.arc(tp.x, tp.y, spirit.size * 0.3 * fade, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${fade * 0.1 * spirit.brightness})`; + ctx.fill(); + }); + + ctx.beginPath(); + ctx.arc(x, y, spirit.size * 2, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.03 * spirit.brightness})`; + ctx.fill(); + + const breathe = Math.sin(t * 2.5 + spirit.bobPhase) * 0.1 + 1; + ctx.beginPath(); + ctx.arc(x, y, spirit.size * breathe, 0, Math.PI * 2); + ctx.fillStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${0.3 * spirit.brightness})`; + ctx.fill(); + + ctx.beginPath(); + ctx.arc(x, y, spirit.size * 0.4, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${0.35 * spirit.brightness})`; + ctx.fill(); + + const blink = Math.sin(t * 0.4 + spirit.bobPhase * 2) > -0.85 ? 1 : 0.1; + ctx.beginPath(); + ctx.arc(x - 1.5, y - spirit.size * 0.2, spirit.size * 0.1 * blink, 0, Math.PI * 2); + ctx.fillStyle = `rgba(255, 255, 255, ${0.7 * spirit.brightness})`; + ctx.fill(); + ctx.beginPath(); + ctx.arc(x + 1.5, y - spirit.size * 0.2, spirit.size * 0.1 * blink, 0, Math.PI * 2); + ctx.fill(); + } + }); + + // ─── ENERGY BURSTS ─── + const hubNodes = NET.hubs; + for (let i = 0; i < 2; i++) { + const burstT = (t * 0.1 + i * 0.5) % 1; + const hub = hubNodes[Math.floor((t * 0.25 + i * 4.1) % hubNodes.length)]; + if (hub && burstT < 0.5) { + const r = burstT * 120; + const a = (1 - burstT / 0.5) * 0.04; + ctx.beginPath(); + ctx.arc(hub.x, hub.y, r, 0, Math.PI * 2); + ctx.strokeStyle = `rgba(${GREEN[0]}, ${GREEN[1]}, ${GREEN[2]}, ${a})`; + ctx.lineWidth = 0.8; + ctx.stroke(); + } + } + + animRef.current = requestAnimationFrame(draw); + }, []); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + canvas.width = 1200; + canvas.height = 440; + animRef.current = requestAnimationFrame(draw); + return () => { + if (animRef.current) cancelAnimationFrame(animRef.current); + }; + }, [draw]); + + return ( +
+
+ +
+
+ ); +} diff --git a/components/home/UseCases.tsx b/components/home/UseCases.tsx index 69b46db..10ab576 100644 --- a/components/home/UseCases.tsx +++ b/components/home/UseCases.tsx @@ -437,6 +437,518 @@ function PipelinesVisual() { ); } +/* ── Illustration: Live Transcoding & Streaming (ABR Ladder) ── */ +function TranscodingVisual() { + const renditions = [ + { res: "1080p", fps: "60fps", width: "100%" }, + { res: "720p", fps: "30fps", width: "78%" }, + { res: "480p", fps: "30fps", width: "58%" }, + { res: "360p", fps: "30fps", width: "42%" }, + ]; + return ( +
+
+ + {/* Mini player chrome */} +
+ {/* Video area gradient */} +
+ + {/* LIVE badge */} +
+ + LIVE +
+ + {/* ABR Ladder */} +
+ {renditions.map((r, i) => ( +
+
+
+
+ + {r.res} {r.fps} + +
+ ))} +
+ +
+ + {/* Bottom stats */} +
+ Bitrate: 4.2 Mbps · Latency: 85ms +
+
+ ); +} + +/* ── Solution Visual: Daydream — Live Canvas ── */ +function DaydreamSolutionVisual() { + return ( +
+
+ + {/* Wireframe flower (left/raw side) */} + + + {/* Stem */} + + {/* Leaves */} + + + {/* Leaf veins */} + + + {/* Flower petals */} + + + + {/* Flower center */} + + + {/* Stamen dots */} + + + + + + + {/* AI-generated glow side — revealed by wipe */} +
+ + {/* Glowing AI flower */} + + {/* Stem */} + + {/* Leaves */} + + + {/* Leaf veins */} + + + {/* Flower petals */} + + + + {/* Flower center */} + + + {/* Glow particles — pollen/sparkles */} + {[ + [92, 28], [108, 30], [88, 45], [112, 42], [95, 50], + [105, 48], [85, 72], [115, 62], [90, 85], [110, 78], + ].map(([x, y], i) => ( + + ))} + {/* Ambient glow */} + + + +
+ + {/* Sweep line */} +
+ + {/* HUD */} +
+ LIVE CANVAS +
+
+ Real-time +
+
+ ); +} + +/* ── Solution Visual: Frameworks — Signal Flow ── */ +function FrameworksSolutionVisual() { + const endpoints = [ + { x: 240, y: 15, label: "1080p" }, + { x: 240, y: 37, label: "720p" }, + { x: 240, y: 59, label: "480p" }, + { x: 240, y: 81, label: "360p" }, + { x: 240, y: 103, label: "CDN" }, + ]; + + return ( +
+
+ +
+ + {/* Source node */} + + + Source + + + {/* Fork node */} + + + ABR + + + {/* Source → Fork connection */} + + + + + + + {/* Fork → Endpoints */} + {endpoints.map((ep, i) => ( + + + {/* Endpoint node */} + + + {ep.label} + + {/* Flowing packet */} + + + + + + + ))} + +
+ +
+ Adaptive bitrate +
+
+ Live +
+
+ ); +} + +/* ── Solution Visual: Streamplace — Open Layer ── */ +function StreamplaceSolutionVisual() { + const layers = [ + { label: "Ingest", y: 10, color: "rgba(222,145,166,0.5)", speed: "12s" }, + { label: "Encode", y: 35, color: "rgba(222,145,166,0.4)", speed: "16s" }, + { label: "Route", y: 60, color: "rgba(222,145,166,0.3)", speed: "20s" }, + { label: "Deliver", y: 85, color: "rgba(222,145,166,0.25)", speed: "24s" }, + ]; + + return ( +
+
+ + {/* Subtle brand glow */} +
+ + + {/* Infrastructure layers with parallax drift */} + {layers.map((layer, i) => ( + + {/* Layer bar */} + + {/* Layer label */} + + {layer.label} + + {/* Open source badge */} + + open + + {/* Data packet traveling through layer */} + + + + + {/* Second staggered packet */} + + + + + + ))} + + {/* Vertical connection lines */} + {[60, 100, 140].map((x) => ( + + ))} + + +
+ AT Protocol +
+
+ Open-source +
+
+ ); +} + +/* ── Solution Visual: Embody — Presence ── */ +function EmbodySolutionVisual() { + const facePts: [number, number][] = [ + [100, 35], [85, 42], [115, 42], // forehead, brow L, brow R + [90, 52], [110, 52], // eye L, eye R + [100, 58], // nose + [93, 68], [100, 72], [107, 68], // mouth L, chin, mouth R + [78, 48], [122, 48], // temple L, temple R + [82, 62], [118, 62], // cheek L, cheek R + [100, 42], // bridge + [88, 48], [112, 48], // inner brow + ]; + + const faceLines: [number, number][] = [ + [0, 1], [0, 2], [0, 13], + [1, 3], [1, 9], [1, 14], + [2, 4], [2, 10], [2, 15], + [3, 5], [3, 14], [3, 11], + [4, 5], [4, 15], [4, 12], + [5, 6], [5, 8], + [6, 7], [7, 8], + [9, 11], [10, 12], + [11, 6], [12, 8], + [13, 3], [13, 4], + ]; + + return ( +
+
+ + {/* Subtle glow */} +
+ + + {/* Whole face group — gentle head turn/nod */} + + + + {/* Face mesh lines */} + {faceLines.map(([a, b], i) => ( + + + + ))} + + {/* Face mesh points */} + {facePts.map(([x, y], i) => ( + + + + + ))} + + {/* Eye glow — blink */} + + + + + + + + {/* Eye pupils — track movement */} + + + + + + + + + + {/* Mouth animation — speaking */} + + + + + + {/* Scanning line — sweeps vertically */} + + + + + + + {/* Tracking corners — animated lock-on */} + + + {/* Top-left */} + + + + {/* Top-right */} + + + + {/* Bottom-left */} + + + + {/* Bottom-right */} + + + + + + {/* Outer presence ring — rotating */} + + + + + + + + {/* Data particles — float upward */} + {[[75, 90], [125, 85], [68, 65], [132, 70], [80, 38], [120, 35]].map(([x, y], i) => ( + + + + + ))} + + + {/* Agent indicator + waveform */} +
+ + Agent Active +
+
+ {[3, 6, 4, 8, 5, 7, 3, 5, 4].map((h, i) => ( +
+ ))} +
+
+ ); +} + /* ── Use Cases data ── */ const useCases: { title: string; @@ -448,7 +960,7 @@ const useCases: { { title: "AI-Generated Worlds", description: - "Live environments inferred in real time from user inputs — not pre-rendered, not pre-recorded. The world itself is the model's output.", + "Live environments inferred in real time from user inputs. Not pre-rendered, not pre-recorded. The world itself is the model's output.", attribution: "Powered by Daydream on Livepeer.", Visual: WorldsVisual, colSpan: 4, @@ -456,7 +968,7 @@ const useCases: { { title: "AI Avatars", description: - "Video representations of AI agents that respond in real time — for tutoring, telepresence, and conversational interfaces.", + "Video representations of AI agents that respond in real time, for tutoring, telepresence, and conversational interfaces.", attribution: "Powered by Embody on Livepeer.", Visual: AvatarsVisual, colSpan: 2, @@ -464,7 +976,7 @@ const useCases: { { title: "Live Video Intelligence", description: - "Real-time analysis of live streams — sports broadcasts, security feeds, manufacturing lines. Processed during, not after.", + "Real-time analysis of live streams: sports broadcasts, security feeds, manufacturing lines. Processed during, not after.", attribution: "Emerging on Livepeer.", Visual: AnalysisVisual, colSpan: 2, @@ -472,16 +984,25 @@ const useCases: { { title: "Prompt-Driven Live Transformation", description: - "A creator goes live and the stream adapts — lighting, environment, visual style — based on a text prompt, in real time.", + "A creator goes live and the stream adapts: lighting, environment, visual style, based on a text prompt, in real time.", attribution: "Powered by Daydream on Livepeer.", Visual: PipelinesVisual, colSpan: 4, }, + { + title: "Live Transcoding & Streaming", + description: + "Adaptive bitrate transcoding across a global GPU network with sub-second latency.", + attribution: "Live on Livepeer.", + Visual: TranscodingVisual, + colSpan: 6, + }, ]; const colSpanClass: Record = { 2: "lg:col-span-2", 4: "lg:col-span-4", + 6: "lg:col-span-6", }; export default function UseCases() { @@ -496,11 +1017,12 @@ export default function UseCases() { viewport={{ once: true, margin: "-100px" }} transition={{ staggerChildren: 0.06 }} > + {/* Sub-section A: Use Cases */} @@ -531,6 +1053,63 @@ export default function UseCases() { ))}
+ {/* Sub-section B: Solution Cards */} + +

+ Solutions +

+

+ Live on Livepeer +

+

+ Applications and emerging capabilities on Livepeer, from transcoding and streaming to real-time AI. +

+
+ +
+ {[ + { + name: "Daydream", + capability: "Turn a live camera feed into AI-generated video, in real time.", + Visual: DaydreamSolutionVisual, + }, + { + name: "Frameworks", + capability: "Stream to any device, any format, any scale, from a single source.", + Visual: FrameworksSolutionVisual, + }, + { + name: "Streamplace", + capability: "Open-source video infrastructure powering AT Protocol social apps.", + Visual: StreamplaceSolutionVisual, + }, + { + name: "Embody", + capability: "Deploy AI avatars that see, speak, and respond in real time.", + Visual: EmbodySolutionVisual, + }, + ].map((solution) => ( + +
+ +
+
+

+ {solution.name} +

+

+ {solution.capability} +

+
+
+ ))} +
+ ))} Build with Livepeer @@ -305,11 +303,9 @@ export default function Header() { })} setMobileOpen(false)} - className="rounded-xl px-4 py-3 text-lg text-white/50 transition-colors hover:bg-white/5 hover:text-white" + className="rounded-xl bg-white px-4 py-3 text-center text-lg font-medium text-dark transition-colors hover:bg-white/90" > Build with Livepeer diff --git a/lib/constants.ts b/lib/constants.ts index bf22321..fb1a525 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -16,10 +16,11 @@ export const NAV_ITEMS: NavItem[] = [ label: "Network", href: "https://explorer.livepeer.org", children: [ + { label: "Ecosystem (coming soon)", href: "/ecosystem" }, + { label: "Livepeer Token", href: "/token" }, { label: "Delegate LPT", href: "https://explorer.livepeer.org", external: true }, { label: "Provide GPUs", href: "https://docs.livepeer.org", external: true }, { label: "Roadmap", href: "https://roadmap.livepeer.org/roadmap", external: true }, - { label: "Ecosystem (beta)", href: "/ecosystem" }, ], }, { @@ -29,6 +30,7 @@ export const NAV_ITEMS: NavItem[] = [ { label: "Primer", href: "/primer" }, { label: "Blog", href: "/blog" }, { label: "Documentation", href: "https://docs.livepeer.org", external: true }, + { label: "Brand", href: "/brand" }, ], }, ]; From 02aa7f2b9376e4304a3deee7a543f506fbee73ff Mon Sep 17 00:00:00 2001 From: Steph Alinsug Date: Thu, 2 Apr 2026 00:57:28 -0400 Subject: [PATCH 04/12] Fix build: add colSpan 6 to type union, remove unused tokenFacts Co-Authored-By: Claude Opus 4.6 --- app/token/page.tsx | 7 ------- components/home/UseCases.tsx | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/token/page.tsx b/app/token/page.tsx index e5bf603..87de64e 100644 --- a/app/token/page.tsx +++ b/app/token/page.tsx @@ -240,13 +240,6 @@ const exchanges = [ { name: "OKX", href: "https://www.okx.com/trade-spot/lpt-usdt" }, ]; - -const tokenFacts = [ - "An ERC-20 token on Ethereum, available on major exchanges like Coinbase, Binance, and Kraken.", - "The protocol mints new LPT each round and distributes it to active participants, incentivizing ongoing network participation.", - "Inactive token holdings dilute over time, encouraging active delegation to keep the network secure.", -]; - export default function TokenPage() { return ( <> diff --git a/components/home/UseCases.tsx b/components/home/UseCases.tsx index 10ab576..a5659da 100644 --- a/components/home/UseCases.tsx +++ b/components/home/UseCases.tsx @@ -955,7 +955,7 @@ const useCases: { description: string; attribution: string; Visual: React.ComponentType; - colSpan: 2 | 4; + colSpan: 2 | 4 | 6; }[] = [ { title: "AI-Generated Worlds", From a92094487e4cc418d65acc23f7f0b5afde2bc6d3 Mon Sep 17 00:00:00 2001 From: adamsoffer Date: Thu, 2 Apr 2026 09:14:18 -0400 Subject: [PATCH 05/12] chore: run Prettier to match main branch formatting Pre-rebase formatting pass to minimize merge conflicts. Co-Authored-By: Claude Opus 4.6 (1M context) --- .prettierrc | 6 + CLAUDE.md | 21 +- README.md | 12 +- app/api/early-access/route.ts | 10 +- app/blog/layout.tsx | 6 +- app/blog/opengraph-image.tsx | 244 +++--- app/brand/page.tsx | 89 +-- app/foundation/page.tsx | 731 +++++++++++++++--- app/globals.css | 175 ++++- app/layout.tsx | 5 +- app/opengraph-image.tsx | 244 +++--- app/primer/opengraph-image.tsx | 186 ++--- app/token/page.tsx | 542 ++++++++++++-- brand-tokens.md | 151 ++-- components/blog/BlogPostHeader.tsx | 5 +- components/home/BuiltOnLivepeer.tsx | 344 +++++++-- components/home/Capabilities.tsx | 308 ++++++-- components/home/CommunityCTA.tsx | 34 +- components/home/DeveloperCTA.tsx | 3 +- components/home/Hero.tsx | 29 +- components/home/NetworkParticipants.tsx | 51 +- components/home/NetworkSpirits.tsx | 191 +++-- components/home/NetworkStats.tsx | 19 +- components/home/UseCases.tsx | 947 +++++++++++++++++++----- components/home/WhyLivepeer.tsx | 7 +- components/icons/LivepeerLogo.tsx | 40 +- components/layout/Header.tsx | 324 ++++---- components/primer/PrimerContent.tsx | 160 ++-- components/ui/AiVideoHero.tsx | 112 ++- components/ui/Button.tsx | 13 +- components/ui/EarlyAccessCTA.tsx | 31 +- components/ui/GenerativeCanvas.tsx | 6 +- components/ui/ImageMask.tsx | 16 +- components/ui/LiveNetwork.tsx | 176 ++++- components/ui/SectionHeader.tsx | 12 +- lib/constants.ts | 24 +- lib/subgraph.ts | 7 +- livepeer-website-brief.md | 11 +- plans/world-models.md | 2 - 39 files changed, 3894 insertions(+), 1400 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8f1866a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5" +} diff --git a/CLAUDE.md b/CLAUDE.md index f28a4ff..ddaf2a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,6 +3,7 @@ **Stack:** Next.js 15, React 19, TypeScript, Tailwind CSS v4, Framer Motion 11. Package manager: npm. No test framework. **Environment variables** (set in Vercel / `.env.local`): + - `MAILCHIMP_API_KEY` — Mailchimp API key (early access signups) - `MAILCHIMP_AUDIENCE_ID` — Mailchimp audience/list ID - `MAILCHIMP_TAG` — Mailchimp tag applied to new subscribers (default: "v2 Website Signups") @@ -80,6 +81,7 @@ The site's signature visual is a layered grid system that combines B&W video/imagery, geometric shapes, animated particle trails, and liquid glass effects. This creates an "outer space control room" aesthetic — technical, cinematic, and distinctly Livepeer. **Layer stack (bottom to top):** + 1. **Media layer** — B&W video or image with green tint, darkened (`ImageMask`) 2. **Tile grid** — 9-column square grid with 1px white borders, overlaid on the media 3. **Geometric shapes** — circles, crosshairs, and a starburst node positioned at grid intersections @@ -101,6 +103,7 @@ The site's signature visual is a layered grid system that combines B&W video/ima Full spec in `brand-tokens.md` — colors, typography, logo rules, greyscale ramp, gradients, graphic elements. **Theme tokens** (`globals.css` `@theme`): + - Colors: `green`, `green-light`, `green-dark`, `green-bright`, `green-subtle`, `blue`, `blue-light`, `blue-bright`, `blue-dark`, `blue-subtle`, `dark`, `dark-lighter`, `dark-card`, `dark-border` - Fonts: `--font-sans` (Favorit Pro — Light/Book/Regular/Medium/Bold), `--font-mono` (Favorit Mono — Regular/Medium/Bold) @@ -143,15 +146,15 @@ Dark theme only — except `/primer`, which uses a light theme override with scr ### Terminology -| Use | Don't use | -|-----|-----------| -| "the network" | "the platform," "the service" | -| "open network" | "decentralized infrastructure" (too web3-coded) | -| "solutions" | "builds" (internal only), "gateways," "tools," "DGEs" | -| "GPU providers" (dev-facing) | "nodes" (too generic) | -| "orchestrators" (protocol/network context) | "miners," "validators" (wrong mental model) | -| "inference" | "processing" (when referring to AI specifically) | -| "GPU-powered video" | "real-time AI video" (previous headline, now retired) | +| Use | Don't use | +| ------------------------------------------ | ----------------------------------------------------- | +| "the network" | "the platform," "the service" | +| "open network" | "decentralized infrastructure" (too web3-coded) | +| "solutions" | "builds" (internal only), "gateways," "tools," "DGEs" | +| "GPU providers" (dev-facing) | "nodes" (too generic) | +| "orchestrators" (protocol/network context) | "miners," "validators" (wrong mental model) | +| "inference" | "processing" (when referring to AI specifically) | +| "GPU-powered video" | "real-time AI video" (previous headline, now retired) | ### What the v2 thesis kills (do not use this framing) diff --git a/README.md b/README.md index 78fc862..f861bc1 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Open [http://localhost:3000](http://localhost:3000) in your browser. ## Scripts -| Command | Description | -| --------------- | ------------------------------------ | -| `npm run dev` | Start development server | -| `npm run build` | Create production build | -| `npm run start` | Serve production build | -| `npm run lint` | Run ESLint | +| Command | Description | +| --------------- | ------------------------ | +| `npm run dev` | Start development server | +| `npm run build` | Create production build | +| `npm run start` | Serve production build | +| `npm run lint` | Run ESLint | ## Project Structure diff --git a/app/api/early-access/route.ts b/app/api/early-access/route.ts index 57b9a07..9a0abda 100644 --- a/app/api/early-access/route.ts +++ b/app/api/early-access/route.ts @@ -12,10 +12,7 @@ export async function POST(request: Request) { const { email } = await request.json(); if (!email || typeof email !== "string") { - return NextResponse.json( - { error: "Email is required" }, - { status: 400 } - ); + return NextResponse.json({ error: "Email is required" }, { status: 400 }); } // Add member to audience (or update if already exists) @@ -50,9 +47,6 @@ export async function POST(request: Request) { return NextResponse.json({ success: true }); } catch (error) { console.error("Early access signup failed:", error); - return NextResponse.json( - { error: "Failed to subscribe" }, - { status: 500 } - ); + return NextResponse.json({ error: "Failed to subscribe" }, { status: 500 }); } } diff --git a/app/blog/layout.tsx b/app/blog/layout.tsx index 8e3ee9f..218cb31 100644 --- a/app/blog/layout.tsx +++ b/app/blog/layout.tsx @@ -17,6 +17,10 @@ export const metadata: Metadata = { }, }; -export default function BlogLayout({ children }: { children: React.ReactNode }) { +export default function BlogLayout({ + children, +}: { + children: React.ReactNode; +}) { return children; } diff --git a/app/blog/opengraph-image.tsx b/app/blog/opengraph-image.tsx index 56ba8ab..e8ecdad 100644 --- a/app/blog/opengraph-image.tsx +++ b/app/blog/opengraph-image.tsx @@ -12,126 +12,152 @@ export default async function OGImage() { ); return new ImageResponse( - ( +
+ {/* Centered radial glow */}
- {/* Centered radial glow */} -
+ /> - {/* Secondary glow — bottom right for depth */} -
+ {/* Secondary glow — bottom right for depth */} +
- {/* Subtle grid overlay */} -
+ {/* Subtle grid overlay */} +
- {/* Lockup (symbol + LIVEPEER text) — 560px wide */} - - - - - - - - - - - - - - - - + {/* Lockup (symbol + LIVEPEER text) — 560px wide */} + + + + + + + + + + + + + + + + - {/* Tagline */} -
- The latest in Livepeer -
+ {/* Tagline */} +
+ The latest in Livepeer +
- {/* Accent line */} -
+ {/* Accent line */} +
- {/* Bottom domain */} -
- livepeer.org -
+ {/* Bottom domain */} +
+ livepeer.org
- ), +
, { ...size, fonts: [ diff --git a/app/brand/page.tsx b/app/brand/page.tsx index c3aa12c..b79e02e 100644 --- a/app/brand/page.tsx +++ b/app/brand/page.tsx @@ -1,7 +1,12 @@ "use client"; import { useEffect, useRef, useState } from "react"; -import { motion, animate, useMotionValue, useMotionValueEvent } from "framer-motion"; +import { + motion, + animate, + useMotionValue, + useMotionValueEvent, +} from "framer-motion"; import Container from "@/components/ui/Container"; import ImageMask from "@/components/ui/ImageMask"; import { @@ -324,9 +329,7 @@ function ColorSwatch({ {name}

- {token && ( -

{token}

- )} + {token &&

{token}

}
); } @@ -586,8 +589,8 @@ export default function BrandPage() { Brand Guidelines

- Resources for presenting the Livepeer brand consistently - and professionally. + Resources for presenting the Livepeer brand consistently and + professionally.

@@ -600,7 +603,6 @@ export default function BrandPage() { background: "linear-gradient(to bottom, transparent, #121212)", }} /> - {/* Section 2: Logo */} @@ -641,7 +643,6 @@ export default function BrandPage() {
  • - Clear Space - {" "}— Minimum clear space equals the width of the symbol on all sides. + + Clear Space + {" "} + — Minimum clear space equals the width of the symbol on all + sides.
  • - Placement - {" "}— Primary: top-left or bottom-left. Secondary: top-right, bottom-right, or center. + + Placement + {" "} + — Primary: top-left or bottom-left. Secondary: top-right, + bottom-right, or center.
  • - Avatars - {" "}— Green gradient bg + white symbol, black bg + white symbol, or white bg + black symbol. + + Avatars + {" "} + — Green gradient bg + white symbol, black bg + white symbol, + or white bg + black symbol.
- @@ -905,10 +909,7 @@ export default function BrandPage() {
{greyscale.map((c) => (
-
+

{c.hex} @@ -1135,8 +1136,8 @@ export default function BrandPage() {

- The Livepeer network is open infrastructure for real-time - AI video. Build applications powered by decentralized + The Livepeer network is open infrastructure for real-time AI + video. Build applications powered by decentralized transcoding, streaming, and AI processing.

@@ -1150,8 +1151,8 @@ export default function BrandPage() {

- Supporting text, descriptions, and secondary content use - the smaller body size with reduced opacity. + Supporting text, descriptions, and secondary content use the + smaller body size with reduced opacity.

@@ -1183,9 +1184,7 @@ export default function BrandPage() {

93%

-

- Favorit Pro Bold -

+

Favorit Pro Bold

@@ -1194,9 +1193,7 @@ export default function BrandPage() {

120%

-

- Favorit Mono Bold -

+

Favorit Mono Bold

@@ -1205,9 +1202,7 @@ export default function BrandPage() {

100%

-

- Favorit Pro Medium -

+

Favorit Pro Medium

@@ -1232,12 +1227,19 @@ export default function BrandPage() { Holographik Grid System

- The site's signature visual combines B&W video/imagery, a 9-column square tile grid, geometric shapes, animated particle trails, and liquid glass effects into a layered “control room” aesthetic. + The site's signature visual combines B&W video/imagery, a + 9-column square tile grid, geometric shapes, animated particle + trails, and liquid glass effects into a layered “control + room” aesthetic.

{/* Layer stack — visual demo */} - +

Layer Stack (bottom to top)

@@ -1277,7 +1279,7 @@ export default function BrandPage() { />

- 1{" "}Media + 1 Media

@@ -1297,7 +1299,7 @@ export default function BrandPage() { />

- 2{" "}Tile Grid + 2 Tile Grid

@@ -1359,7 +1361,7 @@ export default function BrandPage() { ))}

- 3{" "}Shapes + 3 Shapes

@@ -1465,7 +1467,7 @@ export default function BrandPage() { />

- ={" "}Combined + = Combined

@@ -1560,7 +1562,6 @@ export default function BrandPage() { - ); } diff --git a/app/foundation/page.tsx b/app/foundation/page.tsx index ee21529..8efc44b 100644 --- a/app/foundation/page.tsx +++ b/app/foundation/page.tsx @@ -56,35 +56,134 @@ const VALUES = [ function IllustrationStrategy() { // Compass / convergence — multiple arrows pointing inward to a center target return ( - {num}}{word}{" "} + + {num && ( + + {num} + + )} + {word}{" "} ); } @@ -404,11 +917,7 @@ function GrainCanvas() { /* Tile grid with cutout hole + corner crosshairs */ /* ------------------------------------------------------------------ */ -function TileGrid({ - children, -}: { - children?: React.ReactNode; -}) { +function TileGrid({ children }: { children?: React.ReactNode }) { const gridRef = useRef(null); const [tile, setTile] = useState(0); const [gridOffsetX, setGridOffsetX] = useState(0); @@ -434,7 +943,17 @@ function TileGrid({ }, [calculate]); return ( -