# Hero Section v7

> Centered hero with serif headline, mono description, Monad-style flip pill CTAs, animated food-delivery demo, and authentication brands marquee.

A shadcn/ui block from the [@uiception](https://uiception.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiception/hero-section-v7).

- Page, with a live preview: https://shadcn.exchange/r/uiception/hero-section-v7
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uiception/hero-section-v7-light-ef547d349d7fd809.png
- Registry item: https://uiception.com/r/hero-section-v7.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uiception.com/r/hero-section-v7.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `gsap`, `lucide-react`
- Registry items: `https://uiception.com/r/brands-section-v6.json`

## Files

### `registry/new-york/blocks/hero-section-v7/page.tsx`

```tsx
import HeroSectionV7 from "./components/hero-section-v7"

export default function Page() {
  return <HeroSectionV7 />
}
```

### `registry/new-york/blocks/hero-section-v7/components/hero-section-v7.tsx`

```tsx
import { HeroV7Root } from "./hero-v7-root"

import type { HeroV7Props } from "../lib/config"

import "../styles/hero-section-v7.css"

export default function HeroSectionV7(props: HeroV7Props) {
  return (
    <section className="py-4 md:py-6 lg:py-8">
      <HeroV7Root {...props} />
    </section>
  )
}
```

### `registry/new-york/blocks/hero-section-v7/components/hero-v7-root.tsx`

```tsx
import type { HeroV7Props } from "../lib/config"
import HeroContent from "./hero-content"

export function HeroV7Root(props: HeroV7Props) {
  return (
    <div className="mx-auto max-w-6xl px-4">
      <HeroContent {...props} />
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v7/components/hero-content.tsx`

```tsx
import { Instrument_Serif } from "next/font/google"

import type { HeroV7Props } from "../lib/config"
import { resolveHeroV7Content } from "../lib/config"
import BrandsSectionV6 from "../../brands-section-v6/components/brands-section-v6"
import CtaButtons from "./cta-buttons"
import PipelineDemo from "./pipeline-demo"

const instrumentSerif = Instrument_Serif({
  subsets: ["latin"],
  weight: ["400"],
})

export default function HeroContent(props: HeroV7Props) {
  const content = resolveHeroV7Content(props)

  return (
    <div className="flex flex-col items-center gap-6 text-center">
      <h1 className={`${instrumentSerif.className} max-w-3xl text-3xl tracking-tight sm:text-4xl lg:text-5xl lg:leading-[1.15]`}>
        {content.title}
      </h1>

      <p className="max-w-lg font-mono text-base text-muted-foreground">
        {content.description}
      </p>

      <CtaButtons
        primaryCta={content.primaryCta}
        secondaryCta={content.secondaryCta}
      />

      <PipelineDemo />

      <BrandsSectionV6 />
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v7/components/cta-buttons.tsx`

```tsx
"use client"

import { useLayoutEffect, useRef } from "react"
import Link from "next/link"
import gsap from "gsap"

import { cn } from "@/lib/utils"

import type { HeroV7Cta } from "../lib/config"
import { heroV7Content } from "../lib/config"

const CHAR_STAGGER = 0.03
const DURATION = 0.45
const EASE = "power3.out"

function playIn(row1: HTMLElement[], row2: HTMLElement[]) {
  gsap.killTweensOf([...row1, ...row2])
  gsap.to(row1, {
    yPercent: -100,
    duration: DURATION,
    stagger: CHAR_STAGGER,
    ease: EASE,
    overwrite: "auto",
  })
  gsap.to(row2, {
    yPercent: 0,
    duration: DURATION,
    stagger: CHAR_STAGGER,
    ease: EASE,
    overwrite: "auto",
  })
}

function playOut(row1: HTMLElement[], row2: HTMLElement[]) {
  gsap.killTweensOf([...row1, ...row2])
  gsap.to(row1, {
    yPercent: 0,
    duration: DURATION,
    stagger: { each: CHAR_STAGGER, from: "end" },
    ease: EASE,
    overwrite: "auto",
  })
  gsap.to(row2, {
    yPercent: 100,
    duration: DURATION,
    stagger: { each: CHAR_STAGGER, from: "end" },
    ease: EASE,
    overwrite: "auto",
  })
}

function useStaggerCtaAnimation(label: string) {
  const linkRef = useRef<HTMLAnchorElement>(null)

  useLayoutEffect(() => {
    const link = linkRef.current
    if (!link) return

    const row1 = gsap.utils.toArray<HTMLElement>(
      ".hero-v7-cta__stagger-text:not(.hero-v7-cta__stagger-text--second) .hero-v7-cta__char",
      link
    )
    const row2 = gsap.utils.toArray<HTMLElement>(
      ".hero-v7-cta__stagger-text--second .hero-v7-cta__char",
      link
    )

    if (row1.length === 0 || row2.length === 0) return

    const reduced = window.matchMedia(
      "(prefers-reduced-motion: reduce)"
    ).matches
    if (reduced) {
      gsap.set([...row1, ...row2], { clearProps: "all" })
      return
    }

    gsap.set(row1, { yPercent: 0 })
    gsap.set(row2, { yPercent: 100 })

    let over = false
    let pressed = false

    const sync = () => {
      if (over || pressed) playIn(row1, row2)
      else playOut(row1, row2)
    }

    const onEnter = () => {
      over = true
      sync()
    }

    const onLeave = () => {
      over = false
      pressed = false
      sync()
    }

    const onDown = (event: PointerEvent) => {
      if (!event.isPrimary) return
      if (event.pointerType === "mouse" && event.button !== 0) return
      pressed = true
      if (event.pointerType === "touch" || event.pointerType === "pen") {
        try {
          link.setPointerCapture(event.pointerId)
        } catch {

        }
      }
      sync()
    }

    const onUp = (event: PointerEvent) => {
      pressed = false
      try {
        if (link.hasPointerCapture(event.pointerId)) {
          link.releasePointerCapture(event.pointerId)
        }
      } catch {

      }
      if (event.pointerType === "touch") over = false
      sync()
    }

    const onCancel = (event: PointerEvent) => {
      pressed = false
      try {
        if (link.hasPointerCapture(event.pointerId)) {
          link.releasePointerCapture(event.pointerId)
        }
      } catch {

      }
      sync()
    }

    link.addEventListener("pointerenter", onEnter)
    link.addEventListener("pointerleave", onLeave)
    link.addEventListener("pointerdown", onDown)
    link.addEventListener("pointerup", onUp)
    link.addEventListener("pointercancel", onCancel)

    return () => {
      link.removeEventListener("pointerenter", onEnter)
      link.removeEventListener("pointerleave", onLeave)
      link.removeEventListener("pointerdown", onDown)
      link.removeEventListener("pointerup", onUp)
      link.removeEventListener("pointercancel", onCancel)
      gsap.killTweensOf([...row1, ...row2])
    }
  }, [label])

  return linkRef
}

function StaggerChars({ chars }: { chars: string[] }) {
  return (
    <>
      {chars.map((char, i) => (
        <span
          key={`${char}-${i}`}
          className="hero-v7-cta__char inline-block will-change-transform"
        >
          {char === " " ? "\u00a0" : char}
        </span>
      ))}
    </>
  )
}

function FlipCtaButton({
  href,
  label,
  variant = "primary",
  className,
}: {
  href: string
  label: string
  variant?: "primary" | "secondary"
  className?: string
}) {
  const linkRef = useStaggerCtaAnimation(label)

  return (
    <Link
      ref={linkRef}
      href={href}
      className={cn(
        "group relative inline-flex min-h-10 items-center justify-center rounded-full border px-5.5 py-2 font-mono text-sm font-medium tracking-[0.12em] uppercase no-underline transition-[background-color,border-color,color] duration-250 ease-out outline-none",
        variant === "primary" &&
          "border-white/14 bg-[#141414] text-zinc-100 hover:border-blue-500/40 hover:bg-[#1f1f1f] focus-visible:shadow-[0_0_0_2px_color-mix(in_srgb,rgba(59,130,246,0.55)_55%,transparent)] active:border-blue-500/40 active:bg-[#1f1f1f]",
        variant === "secondary" &&
          "border-zinc-900/14 bg-zinc-100 text-zinc-900 hover:border-orange-500/40 hover:bg-zinc-50 focus-visible:shadow-[0_0_0_2px_color-mix(in_srgb,rgba(249,115,22,0.55)_55%,transparent)] active:border-orange-500/40 active:bg-zinc-50",
        className
      )}
      style={{ touchAction: "manipulation" }}
    >
      <span
        className={cn(
          "pointer-events-none absolute -inset-px rounded-[inherit] opacity-0 transition-opacity duration-350 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:opacity-100 group-focus-visible:opacity-100 group-active:opacity-100",
          variant === "primary" &&
            "shadow-[0_0_0_1px_rgba(59,130,246,0.65),0_0_16px_rgba(59,130,246,0.28),0_0_32px_color-mix(in_srgb,rgba(59,130,246,0.28)_65%,transparent)]",
          variant === "secondary" &&
            "shadow-[0_0_0_1px_rgba(249,115,22,0.65),0_0_16px_rgba(249,115,22,0.28),0_0_32px_color-mix(in_srgb,rgba(249,115,22,0.28)_65%,transparent)]"
        )}
        aria-hidden="true"
      />
      <span className="relative inline-block overflow-hidden leading-none">
        <span className="hero-v7-cta__stagger-text inline-flex flex-wrap justify-center gap-0">
          <StaggerChars chars={label.split("")} />
        </span>
        <span
          className="hero-v7-cta__stagger-text hero-v7-cta__stagger-text--second absolute inset-0 inline-flex justify-center"
          aria-hidden="true"
        >
          <StaggerChars chars={label.split("")} />
        </span>
      </span>
    </Link>
  )
}

export default function CtaButtons({
  primaryCta = heroV7Content.primaryCta,
  secondaryCta = heroV7Content.secondaryCta,
}: {
  primaryCta?: HeroV7Cta
  secondaryCta?: HeroV7Cta
}) {
  return (
    <div className="flex flex-row flex-wrap justify-center gap-3">
      <FlipCtaButton
        href={primaryCta.href}
        label={primaryCta.label}
        variant="primary"
        className="w-full sm:w-auto"
      />
      <FlipCtaButton
        href={secondaryCta.href}
        label={secondaryCta.label}
        variant="secondary"
        className="w-full sm:w-auto"
      />
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v7/components/pipeline-demo.tsx`

```tsx
"use client"

import { Bike, Circle, CookingPot, Receipt } from "lucide-react"

import { cn } from "@/lib/utils"
import { Doordash } from "@/components/ui/svgs/doordash"
import { UberEats } from "@/components/ui/svgs/ubereats"

import { usePipelineDemoAnimation } from "../hooks/use-pipeline-demo-animation"
import {
  DELIVERY_PLATFORMS,
  DELIVERY_STAGES,
  getDeliveryPlatformState,
  getFooterPlatforms,
  getFooterStepActive,
  type DeliveryPlatformState,
  type PipelineStage,
} from "../lib/pipeline-config"

const PLATFORM_LOGOS = {
  ubereats: UberEats,
  doordash: Doordash,
} as const

const STAGE_ICONS = {
  ingest: Receipt,
  transform: CookingPot,
  deliver: Bike,
} as const

type StageId = keyof typeof STAGE_ICONS

const PLATFORM_CHIP_STYLES: Record<DeliveryPlatformState, string> = {
  inactive: "[&_svg]:opacity-25",
  pending: "[&_svg]:opacity-40",
  queued:
    "border-blue-500/35 bg-blue-500/5 ring-2 ring-blue-500/15 [&_svg]:opacity-70",
  delivering:
    "border-orange-500/25 bg-orange-500/4 [&_svg]:opacity-50",
  delivered:
    "border-orange-500 bg-orange-500/8 ring-2 ring-orange-500/25 [&_svg]:opacity-100",
  complete:
    "border-orange-500/45 bg-orange-500/6 [&_svg]:opacity-100",
}

function StageNode({
  stageId,
  label,
  detail,
  active,
}: {
  stageId: StageId
  label: string
  detail: string
  active: boolean
}) {
  const Icon = STAGE_ICONS[stageId]

  return (
    <div
      className={cn(
        "flex min-h-24 flex-col items-center justify-center gap-1.5 rounded-xl border border-zinc-900/12 px-3 py-3 text-center transition-[border-color,box-shadow,color] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] dark:border-white/12",
        stageId === "ingest" &&
          active &&
          "border-zinc-900/28 dark:border-white/28 [&_.hero-v7-pipeline__node-icon]:text-zinc-600 dark:[&_.hero-v7-pipeline__node-icon]:text-zinc-300",
        stageId === "transform" &&
          active &&
          "border-blue-500 ring-2 ring-blue-500/25 [&_.hero-v7-pipeline__node-icon]:text-blue-500",
        stageId === "deliver" &&
          active &&
          "border-orange-500 ring-2 ring-orange-500/25 [&_.hero-v7-pipeline__node-icon]:text-orange-500"
      )}
    >
      <span
        className="hero-v7-pipeline__node-icon inline-flex text-zinc-400 transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]"
        aria-hidden
      >
        <Icon size={20} strokeWidth={1.75} />
      </span>
      <span
        className={cn(
          "text-sm font-medium tracking-tight text-foreground transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]",
          active && stageId === "ingest" && "font-semibold",
          active &&
            stageId === "transform" &&
            "font-semibold text-blue-600 dark:text-blue-400",
          active &&
            stageId === "deliver" &&
            "font-semibold text-orange-600 dark:text-orange-400"
        )}
      >
        {label}
      </span>
      <span
        className={cn(
          "max-w-39 text-pretty font-mono text-xs leading-relaxed tracking-tight text-muted-foreground transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]",
          active && "text-foreground/75"
        )}
      >
        {detail}
      </span>
    </div>
  )
}

function PlatformChip({
  chipId,
  label,
  state,
}: {
  chipId: keyof typeof PLATFORM_LOGOS
  label: string
  state: DeliveryPlatformState
}) {
  const Logo = PLATFORM_LOGOS[chipId]

  return (
    <span
      className={cn(
        "inline-flex items-center justify-center rounded-full border border-zinc-900/10 px-2.5 py-1.5 transition-[border-color,background-color,opacity,box-shadow] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] dark:border-white/10",
        PLATFORM_CHIP_STYLES[state]
      )}
    >
      <Logo className="block size-5.5 w-auto transition-opacity duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]" />
      <span className="sr-only">{label}</span>
    </span>
  )
}

function isStageActive(stage: PipelineStage, id: StageId) {
  return stage === id
}

export default function PipelineDemo() {
  const { run, stage, activePlatforms, lineARef, lineBRef } =
    usePipelineDemoAnimation()

  const footerPlatforms = getFooterPlatforms(stage, run.platforms)
  const footerActive = getFooterStepActive(stage, run.platforms)

  return (
    <div
      className="mx-auto mt-2 w-full max-w-3xl text-left"
      aria-label="Live food delivery demo"
    >
      <div className="overflow-hidden rounded-2xl border border-zinc-900/10 bg-zinc-50 shadow-[0_24px_48px_-28px_rgba(24,24,27,0.18)] dark:border-white/10 dark:bg-[#141414] dark:shadow-[0_24px_48px_-28px_rgba(0,0,0,0.45)]">
        <header className="flex items-center justify-between gap-4 border-b border-zinc-900/8 px-4 py-3 text-xs dark:border-white/8">
          <div className="flex min-w-0 items-center gap-3">
            <span className="font-mono text-2.75 tracking-wider text-muted-foreground">
              {run.id}
            </span>
            <span className="inline-flex items-center gap-1.5 text-zinc-600 dark:text-zinc-300">
              <Circle
                className={cn(
                  "text-zinc-400 transition-colors duration-250 ease-out",
                  stage !== "idle" && "text-green-500"
                )}
                size={8}
                fill="currentColor"
                aria-hidden
              />
              <span className="font-mono text-2.75 tracking-wider uppercase">
                {stage === "idle" ? "Delivered" : "In progress"}
              </span>
            </span>
          </div>
          <span className="font-mono text-3.25 text-foreground tabular-nums">
            {run.etaMin}
            <span className="text-muted-foreground"> min</span>
          </span>
        </header>

        <div className="flex flex-col gap-3 px-4 pt-5 pb-4">
          <div className="relative z-1 grid grid-cols-1 items-start gap-3 sm:grid-cols-[minmax(0,1fr)_2.5rem_minmax(0,1fr)_2.5rem_minmax(0,1fr)] sm:items-stretch sm:gap-0">
            <StageNode
              stageId="ingest"
              label={DELIVERY_STAGES[0].label}
              detail={DELIVERY_STAGES[0].detail}
              active={isStageActive(stage, "ingest")}
            />

            <div
              ref={lineARef}
              className="hero-v7-pipeline__line hero-v7-pipeline__line--transform relative hidden h-0.5 self-center overflow-hidden rounded-full bg-zinc-900/8 sm:block dark:bg-white/8"
              aria-hidden
            />

            <StageNode
              stageId="transform"
              label={DELIVERY_STAGES[1].label}
              detail={DELIVERY_STAGES[1].detail}
              active={isStageActive(stage, "transform")}
            />

            <div
              ref={lineBRef}
              className="hero-v7-pipeline__line hero-v7-pipeline__line--deliver relative hidden h-0.5 self-center overflow-hidden rounded-full bg-zinc-900/8 sm:block dark:bg-white/8"
              aria-hidden
            />

            <StageNode
              stageId="deliver"
              label={DELIVERY_STAGES[2].label}
              detail={DELIVERY_STAGES[2].detail}
              active={isStageActive(stage, "deliver")}
            />
          </div>

          <div
            className="flex flex-wrap justify-center gap-2 pt-1"
            aria-label="Delivery apps"
          >
            {DELIVERY_PLATFORMS.map((chip) => (
              <PlatformChip
                key={chip.id}
                chipId={chip.id}
                label={chip.label}
                state={getDeliveryPlatformState(
                  chip.label,
                  run.platforms,
                  stage,
                  activePlatforms
                )}
              />
            ))}
          </div>
        </div>

        <footer className="flex flex-wrap items-center justify-center gap-x-2 gap-y-1.5 border-t border-zinc-900/8 px-4 py-3 font-mono text-sm leading-snug dark:border-white/8">
          <span
            className={cn(
              "transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]",
              footerActive.order
                ? "font-medium text-foreground"
                : "text-muted-foreground"
            )}
          >
            {run.order}
          </span>
          <span className="text-zinc-400" aria-hidden>
            →
          </span>
          <span
            className={cn(
              "transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]",
              footerActive.prep
                ? "font-medium text-blue-600 dark:text-blue-400"
                : "text-muted-foreground"
            )}
          >
            {run.prep}
          </span>
          <span className="text-zinc-400" aria-hidden>
            →
          </span>
          <span
            className={cn(
              "transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]",
              footerActive.platforms
                ? "font-medium text-orange-600 dark:text-orange-400"
                : "text-muted-foreground"
            )}
          >
            {footerPlatforms.text}
          </span>
        </footer>
      </div>
    </div>
  )
}
```

### `registry/new-york/blocks/hero-section-v7/hooks/use-pipeline-demo-animation.ts`

```ts
"use client"

import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react"
import gsap from "gsap"

import { DELIVERY_EXAMPLES, type PipelineStage } from "../lib/pipeline-config"

function subscribeReducedMotion(onStoreChange: () => void) {
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)")
  mq.addEventListener("change", onStoreChange)
  return () => mq.removeEventListener("change", onStoreChange)
}

function getReducedMotion() {
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches
}

function getReducedMotionServer() {
  return false
}

const TIMING = {
  startDelay: 0.4,
  stageHold: 0.55,
  lineFill: 0.65,
  deliverPause: 0.15,
  betweenExamples: 0.85,
  resizeDebounce: 0.15,
} as const

export function usePipelineDemoAnimation() {
  const reducedMotion = useSyncExternalStore(
    subscribeReducedMotion,
    getReducedMotion,
    getReducedMotionServer
  )
  const lineARef = useRef<HTMLDivElement>(null)
  const lineBRef = useRef<HTMLDivElement>(null)
  const exampleIndexRef = useRef(0)
  const timelineRef = useRef<gsap.core.Timeline | null>(null)
  const delayedCallRef = useRef<gsap.core.Tween | null>(null)

  const [exampleIndex, setExampleIndex] = useState(0)
  const [stage, setStage] = useState<PipelineStage>("idle")
  const [activePlatforms, setActivePlatforms] = useState<string[]>([])

  const run = DELIVERY_EXAMPLES[exampleIndex]

  useLayoutEffect(() => {
    if (!reducedMotion) {
      return
    }

    const lineA = lineARef.current
    const lineB = lineBRef.current

    if (!lineA || !lineB) {
      return
    }

    gsap.set([lineA, lineB], { "--hero-v7-pipeline-line-progress": "100%" })
  }, [reducedMotion])

  useLayoutEffect(() => {
    if (reducedMotion) {
      return
    }

    const lineA = lineARef.current
    const lineB = lineBRef.current

    if (!lineA || !lineB) {
      return
    }

    let cancelled = false

    const stopAll = () => {
      timelineRef.current?.kill()
      timelineRef.current = null
      delayedCallRef.current?.kill()
      delayedCallRef.current = null
      gsap.killTweensOf([lineA, lineB])
    }

    const resetLines = () => {
      gsap.set([lineA, lineB], { "--hero-v7-pipeline-line-progress": "0%" })
    }

    const playExample = (index: number) => {
      if (cancelled) {
        return
      }

      stopAll()

      exampleIndexRef.current = index
      const current = DELIVERY_EXAMPLES[index]

      setExampleIndex(index)
      setStage("ingest")
      setActivePlatforms([])
      resetLines()

      const tl = gsap.timeline({
        onComplete: () => {
          if (cancelled) {
            return
          }
          const next = (index + 1) % DELIVERY_EXAMPLES.length
          delayedCallRef.current = gsap.delayedCall(
            TIMING.betweenExamples,
            () => playExample(next)
          )
        },
      })
      timelineRef.current = tl

      tl.call(() => setStage("ingest"))
        .to({}, { duration: TIMING.stageHold })
        .to(lineA, {
          "--hero-v7-pipeline-line-progress": "100%",
          duration: TIMING.lineFill,
          ease: "power2.out",
        })
        .call(() => setStage("transform"))
        .to({}, { duration: TIMING.stageHold })
        .to(lineB, {
          "--hero-v7-pipeline-line-progress": "100%",
          duration: TIMING.lineFill,
          ease: "power2.out",
        })
        .call(() => {
          setStage("deliver")
          setActivePlatforms([])
        })
        .to({}, { duration: TIMING.deliverPause })
        .call(() => setActivePlatforms(current.platforms))
        .to({}, { duration: TIMING.stageHold })
        .call(() => setStage("idle"))
    }

    delayedCallRef.current = gsap.delayedCall(TIMING.startDelay, () =>
      playExample(exampleIndexRef.current)
    )

    const onResize = () => {
      stopAll()
      delayedCallRef.current = gsap.delayedCall(TIMING.resizeDebounce, () =>
        playExample(exampleIndexRef.current)
      )
    }

    window.addEventListener("resize", onResize)

    return () => {
      cancelled = true
      stopAll()
      window.removeEventListener("resize", onResize)
    }
  }, [reducedMotion])

  if (reducedMotion) {
    return {
      run: DELIVERY_EXAMPLES[0],
      stage: "idle" as const,
      activePlatforms: DELIVERY_EXAMPLES[0].platforms,
      lineARef,
      lineBRef,
    }
  }

  return {
    run,
    stage,
    activePlatforms,
    lineARef,
    lineBRef,
  }
}
```

### `registry/new-york/blocks/hero-section-v7/lib/config.ts`

```ts
export type HeroV7Cta = {
  label: string
  href: string
}

export type HeroV7Content = {
  title: string
  description: string
  primaryCta: HeroV7Cta
  secondaryCta: HeroV7Cta
}

export const heroV7Content: HeroV7Content = {
  title: "Find your next delivery app partner for your business",
  description:
    "Take the order, prep the food, send it out on DoorDash or Uber Eats.",
  primaryCta: { label: "Get started", href: "#" },
  secondaryCta: { label: "View docs", href: "#" },
}

export type HeroV7Props = Partial<HeroV7Content>

export function resolveHeroV7Content(overrides?: HeroV7Props): HeroV7Content {
  return {
    title: overrides?.title ?? heroV7Content.title,
    description: overrides?.description ?? heroV7Content.description,
    primaryCta: overrides?.primaryCta ?? heroV7Content.primaryCta,
    secondaryCta: overrides?.secondaryCta ?? heroV7Content.secondaryCta,
  }
}
```

### `registry/new-york/blocks/hero-section-v7/lib/pipeline-config.ts`

```ts
export type DeliveryExample = {
  id: string
  order: string
  prep: string
  platforms: string[]
  etaMin: number
}

export type PipelineStage = "ingest" | "transform" | "deliver" | "idle"

export const DELIVERY_EXAMPLES: DeliveryExample[] = [
  {
    id: "ord_4821",
    order: "Pepperoni pizza",
    prep: "Take out box",
    platforms: ["DoorDash"],
    etaMin: 12,
  },
  {
    id: "ord_7394",
    order: "Chicken wings",
    prep: "Take out box",
    platforms: ["Uber Eats"],
    etaMin: 15,
  },
]

export const DELIVERY_STAGES = [
  {
    id: "ingest",
    label: "Order",
    detail: "Customer checks out",
  },
  {
    id: "transform",
    label: "Prep",
    detail: "Kitchen packs it",
  },
  {
    id: "deliver",
    label: "Deliver",
    detail: "Driver en route",
  },
] as const

export const DELIVERY_PLATFORMS = [
  { id: "ubereats", label: "Uber Eats" },
  { id: "doordash", label: "DoorDash" },
] as const

export type DeliveryPlatformId = (typeof DELIVERY_PLATFORMS)[number]["id"]

export type DeliveryPlatformState =
  | "inactive"
  | "pending"
  | "queued"
  | "delivering"
  | "delivered"
  | "complete"

export function getDeliveryPlatformState(
  platformLabel: string,
  orderPlatforms: string[],
  stage: PipelineStage,
  activePlatforms: string[]
): DeliveryPlatformState {
  if (!orderPlatforms.includes(platformLabel)) {
    return "inactive"
  }

  switch (stage) {
    case "idle":
      return "complete"
    case "ingest":
      return "pending"
    case "transform":
      return "queued"
    case "deliver":
      return activePlatforms.includes(platformLabel)
        ? "delivered"
        : "delivering"
    default:
      return "pending"
  }
}

export function getFooterPlatforms(
  stage: PipelineStage,
  orderPlatforms: string[]
) {
  const route = orderPlatforms.join(" + ")

  if (stage === "deliver" || stage === "idle") {
    return {
      text: route,
      highlighted: true,
    }
  }

  return {
    text: route,
    highlighted: false,
  }
}

export function getFooterStepActive(
  stage: PipelineStage,
  orderPlatforms: string[]
) {
  const platforms = getFooterPlatforms(stage, orderPlatforms)

  return {
    order: stage === "ingest",
    prep: stage === "transform",
    platforms: platforms.highlighted,
  }
}
```

### `components/ui/svgs/doordash.tsx`

```tsx
import type { SVGProps } from "react";

const Doordash = (props: SVGProps<SVGSVGElement>) => (
  <svg {...props} viewBox="0 0 24 24" role="img" aria-hidden>
    <path
      fill="currentColor"
      d="M23.071 8.409a6.09 6.09 0 00-5.396-3.228H.584A.589.589 0 00.17 6.184L3.894 9.93a1.752 1.752 0 001.242.516h12.049a1.554 1.554 0 11.031 3.108H8.91a.589.589 0 00-.415 1.003l3.725 3.747a1.75 1.75 0 001.242.516h3.757c4.887 0 8.584-5.225 5.852-10.413"
    />
  </svg>
);

export { Doordash };
```

### `components/ui/svgs/ubereats.tsx`

```tsx
import type { SVGProps } from "react";

const UberEats = (props: SVGProps<SVGSVGElement>) => (
  <svg {...props} viewBox="0 0 24 24" role="img" aria-hidden>
    <path
      fill="currentColor"
      d="M0 2.8645v4.9972c0 1.8834 1.3315 3.1297 3.0835 3.1297a2.9652 2.9652 0 0 0 2.1502-.876v.7425H6.445V2.8645H5.223v4.9339c0 1.2642-.8696 2.1198-1.9954 2.122-1.1386-.0023-1.997-.834-1.997-2.122V2.8645zm7.3625 0v7.9934h1.163v-.7318a2.9915 2.9915 0 0 0 2.1177.876c1.714.048 3.1295-1.3283 3.1295-3.0429s-1.4155-3.091-3.1295-3.0429a2.9674 2.9674 0 0 0-2.107.876V2.8645zm9.8857 2.0561c-1.6752-.0074-3.0369 1.3492-3.0356 3.0245 0 1.7366 1.3732 3.0373 3.1537 3.0373a3.123 3.123 0 0 0 2.5578-1.2438l-.8495-.6177a2.0498 2.0498 0 0 1-1.7083.8585c-.9763.0126-1.8147-.6915-1.971-1.6553h4.818v-.379c0-1.734-1.254-3.0238-2.9638-3.0245zm6.1632.0667a1.5943 1.5943 0 0 0-1.376.7657v-.7186h-1.163v5.8235h1.1741V7.5465c0-.9023.5581-1.4847 1.3268-1.4847h.4949V4.9886c-.1576.0013-.3186-.0009-.4568-.0013zm-6.2034.944a1.844 1.844 0 0 1 1.8337 1.486H15.424a1.844 1.844 0 0 1 1.784-1.486zm-6.6589.0056c1.1223-.0084 2.0365.8992 2.0364 2.0215-.0026 1.1203-.914 2.0258-2.0343 2.021a2.0151 2.0151 0 0 1-1.4159-.5987A2.0152 2.0152 0 0 1 8.55 7.9592a2.0152 2.0152 0 0 1 .5838-1.422 2.0152 2.0152 0 0 1 1.4153-.6003zM0 12.9864v7.9716h5.7222v-1.3666H1.5458v-1.971h4.0647v-1.314H1.5458v-1.9556h4.1764v-1.3644zm14.5608.4097v1.6861h-1.1519v1.338h1.1545v3.143c0 .7927.5712 1.4209 1.6005 1.4209h1.6425L17.8 19.646h-1.1412c-.3482 0-.5714-.1509-.5714-.464v-2.7683H17.8v-1.3316h-1.7062v-1.686zm-5.2974 1.5275c-1.7348-.0103-3.141 1.4035-3.1214 3.1382.0196 1.7346 1.4575 3.1163 3.1915 3.0668a2.9915 2.9915 0 0 0 1.912-.6655v.532h1.5175v-5.9129h-1.509v.5257a3.0047 3.0047 0 0 0-1.9205-.6835c-.0244-.0007-.0492-.0006-.0701-.0008zm11.771.0077c-1.5855 0-2.7002.6437-2.7002 1.8854 0 .8607.6132 1.4213 1.936 1.695l1.4478.3286c.5694.1095.7224.2585.7224.4906 0 .3701-.438.6022-1.1279.6022-.876 0-1.3774-.1907-1.5723-.8477h-1.533c.219 1.2307 1.1563 2.05 3.0484 2.05h.0022c1.752 0 2.7422-.819 2.7422-1.9534 0-.8059-.5847-1.4084-1.8089-1.6668l-1.2943-.2605c-.7511-.1358-.988-.2738-.988-.5454 0-.357.3616-.5757 1.0295-.5757.7227 0 1.2527.1925 1.406.8473h1.5175c-.0854-1.2286-.9899-2.0497-2.8273-2.0497zM9.467 16.1815c1.0092.0096 1.8188.8369 1.8067 1.8461.0014 1.0046-.8198 1.816-1.8243 1.8025-1.0075-.0048-1.8203-.8256-1.8155-1.833.0048-1.0076.8255-1.8204 1.833-1.8156z"
    />
  </svg>
);

export { UberEats };
```

### `registry/new-york/blocks/hero-section-v7/styles/hero-section-v7.css`

```css
.hero-v7-pipeline__line::after {
  content: "";
  position: absolute;
  inset: 0;
  width: var(--hero-v7-pipeline-line-progress, 0%);
  border-radius: inherit;
  background: #52525b;
  transition: width 0.05s linear;
}

.hero-v7-pipeline__line--transform::after {
  background: #3b82f6;
}

.hero-v7-pipeline__line--deliver::after {
  background: #f97316;
}

.dark .hero-v7-pipeline__line::after {
  background: #a1a1aa;
}

.dark .hero-v7-pipeline__line--transform::after {
  background: #60a5fa;
}

.dark .hero-v7-pipeline__line--deliver::after {
  background: #fb923c;
}

@media (prefers-reduced-motion: reduce) {
  .hero-v7-pipeline__line::after {
    width: 100%;
  }

  .hero-v7-cta__stagger-text--second {
    display: none;
  }

  .hero-v7-cta__char {
    will-change: auto;
  }
}
```
