Hero 11

A split-copy hero with a light product studio mock — issue feed, properties, and a floating agent panel that fades into the page.

hero-11.tsx

import { ArrowRight } from 'lucide-react'
import Balancer from 'react-wrap-balancer'

import { Hero11Studio } from './hero-11-studio'

export function Hero11() {
  return (
    <section className="relative isolate w-full overflow-hidden">
      <div className="relative z-10 mx-auto flex max-w-7xl flex-col gap-8 px-6 pt-20 pb-0 sm:gap-10 sm:pt-28">
        <div className="flex w-full flex-col gap-4 sm:gap-5">
          <h1 className="text-foreground max-w-xl font-serif text-3xl font-normal tracking-tight text-balance sm:text-4xl md:text-5xl lg:text-[3.25rem]">
            <Balancer>The interface system for teams and agents.</Balancer>
          </h1>

          <div className="flex w-full flex-col gap-3 sm:flex-row sm:items-end sm:justify-between sm:gap-8">
            <p className="text-muted-foreground max-w-md text-sm leading-relaxed sm:text-base">
              <Balancer>
                Purpose-built blocks for planning and shipping product UI.
                Designed for the AI era.
              </Balancer>
            </p>
            <a
              href="#"
              className="text-foreground group inline-flex shrink-0 items-center gap-2 text-sm font-medium transition-[opacity,transform] hover:opacity-80 active:scale-[0.96]"
            >
              <span>Compositions</span>
              <ArrowRight className="size-3.5 transition-transform group-hover:translate-x-0.5" />
            </a>
          </div>
        </div>

        <div className="relative mx-auto w-full max-w-7xl">
          <div className="relative w-full overflow-hidden mask-r-from-90% mask-r-to-100%">
            <Hero11Studio />
          </div>
        </div>
      </div>
    </section>
  )
}

hero-11-studio.tsx

import type * as React from 'react'
import {
  ArrowUp,
  Boxes,
  CircleDot,
  Component,
  FileText,
  Layers,
  LayoutTemplate,
  MoreHorizontal,
  Paperclip,
  Plus,
  Search,
  SignalHigh,
  Sparkles,
} from 'lucide-react'

import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { cn } from '@/lib/utils'

const nav = [
  { icon: Boxes, label: 'Catalog', count: '128' },
  { icon: LayoutTemplate, label: 'Blocks', count: '48' },
  { icon: Layers, label: 'Patterns' },
  { icon: Component, label: 'Compositions' },
]

const collections = [
  { label: 'Heroes' },
  { label: 'Forms' },
  { label: 'Illustrations' },
]

const recent = [
  { label: 'Split-copy hero', active: true },
  { label: 'Agent studio' },
  { label: 'Empty states' },
  { label: 'Auth screens' },
]

const activity = [
  {
    src: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=64&q=80',
    name: 'Flex',
    action: 'added to catalog',
    time: '2m',
    body: null as string | null,
  },
  {
    src: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=64&q=80',
    name: 'Maya',
    action: 'tagged',
    time: '6m',
    body: null,
    badge: 'Hero',
  },
  {
    src: 'https://images.unsplash.com/photo-1633332755192-727a05c4013d?w=64&q=80',
    name: 'Noah',
    action: 'commented',
    time: '4m',
    body: 'Keep the right edge soft — sidebar stays crisp against the page.',
  },
  {
    src: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=64&q=80',
    name: 'Ava',
    action: 'commented',
    time: '3m',
    body: 'Light-first. Soft shadows, concentric radii, tabular counts.',
  },
]

const changedFiles = [
  { name: 'studio-demo.tsx', add: 48, del: 12 },
  { name: 'hero-11.tsx', add: 16, del: 4 },
]

export function Hero11Studio() {
  return (
    <div className="relative w-full">
      <div
        className={cn(
          'bg-card w-full overflow-hidden rounded-xl border',
          'shadow-[10px_18px_40px_-18px_rgba(0,0,0,0.16)]',
          'dark:shadow-[10px_18px_40px_-18px_rgba(0,0,0,0.5)]',
        )}
      >
        <div className="flex min-h-[30rem]">
          {/* Sidebar */}
          <aside className="bg-card hidden w-52 shrink-0 flex-col border-r sm:flex">
            <div className="flex items-center gap-2 px-3 py-3">
              <div className="bg-foreground text-background flex size-5 items-center justify-center rounded-md text-[10px] font-semibold">
                F
              </div>
              <span className="truncate text-sm font-semibold tracking-tight">
                Flexnative
              </span>
              <div className="ml-auto flex items-center gap-0.5">
                <Button
                  size="icon-xs"
                  variant="ghost"
                  className="text-muted-foreground"
                  aria-label="Search"
                >
                  <Search className="size-3.5" />
                </Button>
                <Button
                  size="icon-xs"
                  variant="ghost"
                  className="text-muted-foreground"
                  aria-label="New block"
                >
                  <Plus className="size-3.5" />
                </Button>
              </div>
            </div>

            <nav className="flex flex-col gap-0.5 px-2">
              {nav.map(({ icon: Icon, label, count }) => (
                <div
                  key={label}
                  className="text-muted-foreground hover:bg-muted flex items-center gap-2 rounded-md px-2 py-1.5 text-xs"
                >
                  <Icon className="size-3.5 shrink-0" />
                  <span className="min-w-0 flex-1 truncate">{label}</span>
                  {count && (
                    <span className="text-muted-foreground/80 tabular-nums">
                      {count}
                    </span>
                  )}
                </div>
              ))}
            </nav>

            <div className="mt-4 px-2">
              <span className="text-muted-foreground px-2 text-[10px] font-medium tracking-wide uppercase">
                Collections
              </span>
              <div className="mt-1 flex flex-col gap-0.5">
                {collections.map(({ label }) => (
                  <div
                    key={label}
                    className="text-muted-foreground hover:bg-muted truncate rounded-md px-2 py-1.5 text-xs"
                  >
                    {label}
                  </div>
                ))}
              </div>
            </div>

            <div className="mt-4 flex-1 px-2 pb-3">
              <span className="text-muted-foreground px-2 text-[10px] font-medium tracking-wide uppercase">
                Recent
              </span>
              <div className="mt-1 flex flex-col gap-0.5">
                {recent.map(({ label, active }) => (
                  <div
                    key={label}
                    className={cn(
                      'truncate rounded-md px-2 py-1.5 text-xs',
                      active
                        ? 'bg-muted text-foreground font-medium'
                        : 'text-muted-foreground hover:bg-muted',
                    )}
                  >
                    {label}
                  </div>
                ))}
              </div>
            </div>
          </aside>

          {/* Main */}
          <div className="flex min-w-0 flex-1 flex-col">
            <header className="flex items-center gap-2 border-b px-3 py-2.5 sm:px-4">
              <span className="truncate text-sm font-medium tracking-tight">
                Split-copy hero
              </span>
              <div className="text-muted-foreground ml-auto flex items-center gap-1.5 text-[10px] tabular-nums">
                <span>02</span>
                <span className="text-muted-foreground/50">/</span>
                <span>128</span>
              </div>
            </header>

            <div className="flex min-h-0 flex-1">
              <div className="flex min-w-0 flex-1 flex-col gap-4 p-4 sm:p-5">
                <div className="space-y-2">
                  <h2 className="text-xl font-semibold tracking-tight sm:text-2xl">
                    Split-copy hero
                  </h2>
                  <p className="text-muted-foreground max-w-xl text-xs leading-relaxed text-pretty sm:text-[13px]">
                    A product studio mock with split copy, concentric radii, and
                    a soft{' '}
                    <code className="bg-muted rounded px-1 py-0.5 font-mono text-[11px]">
                      mask-*
                    </code>{' '}
                    fade so the interface dissolves into the page.
                  </p>
                </div>

                <Separator />

                <div className="flex flex-col gap-3">
                  {activity.map((item, i) => (
                    <div key={`${item.name}-${i}`} className="flex gap-2.5">
                      <Avatar className="mt-0.5 size-6 shrink-0">
                        <AvatarImage src={item.src} alt="" />
                        <AvatarFallback className="text-[9px]">
                          {item.name[0]}
                        </AvatarFallback>
                      </Avatar>
                      <div className="min-w-0 flex-1 space-y-1.5">
                        <div className="flex flex-wrap items-baseline gap-x-1.5 gap-y-0.5 text-xs">
                          <span className="font-medium">{item.name}</span>
                          <span className="text-muted-foreground">
                            {item.action}
                          </span>
                          {'badge' in item && item.badge && (
                            <Badge
                              variant="secondary"
                              className="h-4 px-1.5 text-[9px]"
                            >
                              {item.badge}
                            </Badge>
                          )}
                          <span className="text-muted-foreground ml-auto text-[10px] tabular-nums">
                            {item.time}
                          </span>
                        </div>
                        {item.body && (
                          <div className="bg-muted/50 rounded-lg px-3 py-2 text-xs leading-relaxed text-pretty">
                            {item.body}
                          </div>
                        )}
                      </div>
                    </div>
                  ))}

                  <div className="flex items-center gap-2.5 text-xs">
                    <div className="flex size-6 shrink-0 items-center justify-center rounded-full border border-amber-400/40 bg-amber-400/10">
                      <CircleDot className="size-3 text-amber-500" />
                    </div>
                    <span className="text-muted-foreground">
                      Moved from{' '}
                      <span className="text-foreground font-medium">Draft</span>{' '}
                      to{' '}
                      <span className="text-foreground font-medium">
                        In review
                      </span>
                    </span>
                    <span className="text-muted-foreground ml-auto text-[10px] tabular-nums">
                      1m
                    </span>
                  </div>
                </div>
              </div>

              {/* Properties */}
              <aside className="hidden w-40 shrink-0 flex-col gap-3 border-l p-3 md:flex lg:w-44">
                <Property
                  label="Status"
                  value={
                    <span className="flex items-center gap-1.5">
                      <span className="size-1.5 rounded-full bg-amber-400" />
                      In review
                    </span>
                  }
                />
                <Property
                  label="Priority"
                  value={
                    <span className="flex items-center gap-1.5">
                      <SignalHigh className="size-3.5 text-orange-500" />
                      High
                    </span>
                  }
                />
                <Property
                  label="Owner"
                  value={
                    <span className="flex items-center gap-1.5">
                      <Avatar className="size-4">
                        <AvatarImage
                          src="https://images.unsplash.com/photo-1633332755192-727a05c4013d?w=64&q=80"
                          alt=""
                        />
                        <AvatarFallback className="text-[8px]">
                          N
                        </AvatarFallback>
                      </Avatar>
                      Noah
                    </span>
                  }
                />
                <Property
                  label="Category"
                  value={
                    <span className="flex items-center gap-1.5">
                      <span className="bg-foreground/90 size-3.5 rounded-[4px]" />
                      Hero
                    </span>
                  }
                />
                <Property
                  label="Slug"
                  value={<span className="font-mono text-[11px]">hero-11</span>}
                />
              </aside>
            </div>
          </div>
        </div>
      </div>

      {/* Floating agent panel */}
      <div
        className={cn(
          'bg-card absolute right-2 bottom-3 z-10 w-[min(100%-1rem,17.5rem)] overflow-hidden rounded-xl sm:right-4 sm:w-72',
          'shadow-[0_10px_32px_-8px_rgba(0,0,0,0.2)]',
          'dark:shadow-[0_10px_32px_-8px_rgba(0,0,0,0.55)]',
        )}
      >
        <div className="flex items-center gap-2 border-b px-3 py-2">
          <Sparkles className="text-foreground size-3.5 shrink-0" />
          <span className="truncate text-xs font-medium tracking-tight">
            Flex Agent
          </span>
          <Badge
            variant="secondary"
            className="h-4 border-emerald-500/20 bg-emerald-500/10 px-1.5 text-[9px] text-emerald-700 dark:text-emerald-400"
          >
            Live
          </Badge>
          <Button
            size="icon-xs"
            variant="ghost"
            className="text-muted-foreground ml-auto"
            aria-label="More"
          >
            <MoreHorizontal className="size-3.5" />
          </Button>
        </div>

        <div className="space-y-2.5 px-3 py-2.5">
          <p className="text-muted-foreground text-[11px] leading-relaxed">
            Examining the hero mask path…
          </p>
          <p className="text-muted-foreground text-[10px] tabular-nums">
            Worked for 7s
          </p>
          <p className="text-[11px] leading-relaxed">
            Pushed and opened a draft PR.
          </p>

          <div className="bg-muted/60 space-y-1.5 rounded-lg p-2">
            <div className="flex items-center justify-between gap-2">
              <span className="text-[10px] font-medium">
                Changed 2 files{' '}
                <span className="text-emerald-600 tabular-nums dark:text-emerald-400">
                  +64
                </span>{' '}
                <span className="text-red-500 tabular-nums dark:text-red-400">
                  -16
                </span>
              </span>
              <Button
                size="xs"
                variant="outline"
                className="h-5 px-1.5 text-[9px]"
              >
                Preview
              </Button>
            </div>
            {changedFiles.map((f) => (
              <div
                key={f.name}
                className="text-muted-foreground flex items-center gap-1.5 text-[10px]"
              >
                <FileText className="size-3 shrink-0" />
                <span className="min-w-0 flex-1 truncate font-mono">
                  {f.name}
                </span>
                <span className="text-emerald-600 tabular-nums dark:text-emerald-400">
                  +{f.add}
                </span>
                <span className="text-red-500 tabular-nums dark:text-red-400">
                  -{f.del}
                </span>
              </div>
            ))}
          </div>
        </div>

        <div className="border-t p-2">
          <div className="bg-muted/50 flex items-center gap-1.5 rounded-lg px-2 py-1.5">
            <span className="text-muted-foreground min-w-0 flex-1 truncate text-[11px]">
              Tell Flex what to do next…
            </span>
            <Button
              size="icon-xs"
              variant="ghost"
              className="text-muted-foreground"
              aria-label="Attach"
            >
              <Paperclip className="size-3.5" />
            </Button>
            <Button
              size="icon-xs"
              variant="default"
              className="size-6 rounded-md active:scale-[0.96]"
              aria-label="Send"
            >
              <ArrowUp className="size-3.5" />
            </Button>
          </div>
        </div>
      </div>
    </div>
  )
}

function Property({
  label,
  value,
}: Readonly<{ label: string; value: React.ReactNode }>) {
  return (
    <div className="space-y-1">
      <span className="text-muted-foreground text-[10px]">{label}</span>
      <div className="text-foreground text-xs font-medium">{value}</div>
    </div>
  )
}