{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "team-1",
  "title": "Team 1: portrait grid with hover bios",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A grid of team portraits with name, role, location and links. On hover or keyboard focus a bio slides up over the photo; phones show it inline. Initial tiles when there is no photo.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/button.json",
    "https://ui.ballmac.com/r/media.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/team-1/team-1.tsx",
      "content": "// Ballmac UI: Team 1. https://ui.ballmac.com/blocks/team-1\nimport * as React from \"react\"\nimport { ArrowRight, Link2, MapPin } from \"lucide-react\"\n\nimport { buttonVariants } from \"@/components/ballmac/button\"\nimport { cn } from \"@/lib/utils\"\nimport { Media, type MediaSource } from \"@/components/ballmac/media\"\n\ntype Team1Link = {\n  /** Accessible name, e.g. \"Priya on LinkedIn\". */\n  label: string\n  /** Destination. */\n  href: string\n  /** Icon. Defaults to a link icon. */\n  icon?: React.ReactNode\n}\n\ntype Team1Member = {\n  name: string\n  role: string\n  /** One or two sentences, revealed over the photo on hover and focus (always visible on phones). */\n  bio?: string\n  /** City or country. */\n  location?: string\n  /** Photo: an image URL, an object with alt text and a dark-mode file, or your own element. Initials on a coloured tile are shown when omitted. */\n  image?: MediaSource\n  /** Describes `image` when it is a plain URL. The name is printed beside the photo, so the default is decorative (empty). */\n  imageAlt?: string\n  /** Social and contact links. */\n  links?: Team1Link[]\n}\n\ntype Team1Props = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  /** Label above the heading. */\n  eyebrow?: string\n  /** Section heading. */\n  title?: string\n  /** One or two sentences under the heading. */\n  description?: string\n  /** The people. Four or eight fill the grid evenly. */\n  members?: Team1Member[]\n  /** Link beside the heading, e.g. to open roles. Pass null to hide it. */\n  action?: { label: string; href: string } | null\n}\n\nconst tiles = [\n  \"from-chart-1/70 to-chart-1/20\",\n  \"from-chart-3/70 to-chart-3/20\",\n  \"from-chart-5/70 to-chart-5/20\",\n  \"from-chart-2/70 to-chart-2/20\",\n  \"from-chart-4/70 to-chart-4/20\",\n]\n\nconst defaults: Team1Member[] = [\n  { name: \"Priya Raman\", role: \"Co-founder, CEO\", bio: \"Spent a decade in finance ops before deciding the tools needed to be kinder.\", location: \"Lisbon\", links: [{ label: \"Priya’s website\", href: \"#\" }] },\n  { name: \"Marcus Webb\", role: \"Co-founder, CTO\", bio: \"Builds the boring, reliable parts so everyone else can move fast.\", location: \"Austin\", links: [{ label: \"Marcus’s website\", href: \"#\" }] },\n  { name: \"Elena Fischer\", role: \"Head of Design\", bio: \"Believes good software feels like a quiet room. Obsessed with small details.\", location: \"Berlin\", links: [{ label: \"Elena’s portfolio\", href: \"#\" }] },\n  { name: \"Tomás Herrera\", role: \"Head of Customer Success\", bio: \"Answers the hard questions first. Has read every support thread we have.\", location: \"Mexico City\", links: [{ label: \"Tomás’s website\", href: \"#\" }] },\n  { name: \"Amara Singh\", role: \"Staff Engineer\", bio: \"Makes the database fast, then makes it understandable.\", location: \"Toronto\", links: [{ label: \"Amara’s website\", href: \"#\" }] },\n  { name: \"Jonas Ortiz\", role: \"Product Manager\", bio: \"Turns messy customer calls into clear, shippable ideas.\", location: \"Madrid\", links: [{ label: \"Jonas’s website\", href: \"#\" }] },\n  { name: \"Yuki Tanaka\", role: \"Product Designer\", bio: \"Draws interfaces on paper first, and throws most of them away.\", location: \"Osaka\", links: [{ label: \"Yuki’s portfolio\", href: \"#\" }] },\n  { name: \"Ingrid Larsen\", role: \"Security Lead\", bio: \"Sleeps well because the audit log never lies.\", location: \"Oslo\", links: [{ label: \"Ingrid’s website\", href: \"#\" }] },\n]\n\nconst initials = (name: string) => name.split(\" \").map((w) => w[0]).join(\"\").slice(0, 2)\n\nfunction Team1({\n  eyebrow = \"Our team\",\n  title = \"The people behind the product.\",\n  description = \"A small, remote team that cares a lot about getting the details right.\",\n  members = defaults,\n  action = { label: \"See open roles\", href: \"#\" },\n  className,\n  ...props\n}: Team1Props) {\n  return (\n    <section data-slot=\"team-1\" className={cn(\"mx-auto max-w-6xl px-4 py-20 sm:px-6 md:py-28\", className)} {...props}>\n      <div className=\"flex flex-col gap-6 md:flex-row md:items-end md:justify-between\">\n        <div className=\"max-w-2xl\">\n          <p className=\"text-muted-foreground text-sm font-medium\">{eyebrow}</p>\n          <h2 className=\"mt-3 text-3xl font-semibold tracking-[-0.035em] text-balance sm:text-4xl lg:text-5xl\">{title}</h2>\n          <p className=\"text-muted-foreground mt-4 text-lg text-pretty\">{description}</p>\n        </div>\n        {action && (\n          <a href={action.href} className={buttonVariants({ variant: \"outline\", shape: \"pill\", className: \"w-fit\" })}>\n            {action.label} <ArrowRight  className=\"rtl:rotate-180\"/>\n          </a>\n        )}\n      </div>\n\n      <ul className=\"mt-12 grid gap-x-5 gap-y-10 sm:grid-cols-2 lg:grid-cols-4\">\n        {members.map((m, i) => (\n          <li key={m.name} data-slot=\"team-1-member\" className=\"group/member\">\n            <div className=\"relative aspect-[4/5] overflow-hidden rounded-2xl border\">\n              {m.image ? (\n                <Media media={m.image} alt={m.imageAlt ?? \"\"} fill className=\"absolute inset-0 transition-transform duration-500 group-hover/member:scale-105 motion-reduce:transition-none motion-reduce:group-hover/member:scale-100\" />\n              ) : (\n                <div aria-hidden=\"true\" className={cn(\"absolute inset-0 flex items-center justify-center bg-gradient-to-br\", tiles[i % tiles.length])}>\n                  <span className=\"text-foreground/80 text-6xl font-semibold tracking-tight select-none\">{initials(m.name)}</span>\n                  <div className=\"absolute inset-0 bg-[radial-gradient(color-mix(in_oklch,var(--foreground)_18%,transparent)_1px,transparent_1px)] [background-size:16px_16px] [mask-image:radial-gradient(80%_80%_at_50%_50%,transparent_30%,black)]\" />\n                </div>\n              )}\n              {m.bio && (\n                <div className=\"from-background via-background/95 absolute inset-x-0 bottom-0 hidden translate-y-3 bg-gradient-to-t to-transparent p-4 pt-14 text-sm opacity-0 transition-all duration-300 group-focus-within/member:translate-y-0 group-focus-within/member:opacity-100 group-hover/member:translate-y-0 group-hover/member:opacity-100 motion-reduce:transition-none md:block\">\n                  <p className=\"text-pretty\">{m.bio}</p>\n                </div>\n              )}\n            </div>\n            <div className=\"mt-4 flex items-start justify-between gap-3\">\n              <div className=\"min-w-0\">\n                <h3 className=\"font-semibold\">{m.name}</h3>\n                <p className=\"text-muted-foreground text-sm\">{m.role}</p>\n                {m.location && (\n                  <p className=\"text-muted-foreground mt-1 flex items-center gap-1 text-xs\">\n                    <MapPin className=\"size-3\" aria-hidden=\"true\" />\n                    {m.location}\n                  </p>\n                )}\n              </div>\n              {m.links && m.links.length > 0 && (\n                <ul className=\"flex shrink-0 gap-1\">\n                  {m.links.map((l) => (\n                    <li key={l.href + l.label}>\n                      <a\n                        href={l.href}\n                        aria-label={l.label}\n                        className=\"text-muted-foreground hover:text-foreground hover:bg-accent focus-visible:ring-ring/50 flex size-8 items-center justify-center rounded-full outline-none transition-colors focus-visible:ring-[3px] [&_svg]:size-4\"\n                      >\n                        {l.icon ?? <Link2 />}\n                      </a>\n                    </li>\n                  ))}\n                </ul>\n              )}\n            </div>\n            {m.bio && <p className=\"text-muted-foreground mt-3 text-sm text-pretty md:hidden\">{m.bio}</p>}\n          </li>\n        ))}\n      </ul>\n    </section>\n  )\n}\n\nexport { Team1, type Team1Props, type Team1Member, type Team1Link }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/team-1/team-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "team",
      "people",
      "about",
      "company",
      "portraits"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "An About-page team section. Pass members=[{ name, role, bio?, location?, image?, links? }].",
      "whenToUse": [
        "About and careers pages",
        "Showing the humans behind a product"
      ],
      "whenNotToUse": [
        "Long staff directories (use a table)"
      ],
      "composesWith": [
        "stats-1",
        "testimonials-2",
        "cta-1",
        "footer-1"
      ],
      "a11y": [
        {
          "keys": "Tab",
          "action": "Focusing a member's link reveals their bio, like hover"
        }
      ],
      "customization": [
        "members: { name, role, bio?, location?, image?, links?: { label, href, icon? }[] }[]",
        "action: link beside the heading (null to hide)"
      ]
    },
    "examples": [
      "team-1-demo",
      "team-1-small"
    ],
    "url": "https://ui.ballmac.com/components/team-1"
  },
  "categories": [
    "blocks",
    "team"
  ],
  "type": "registry:block"
}