{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "team-section",
  "title": "Team Section",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/team-section/team-section.component.ts",
      "content": "// Marketing team section: eyebrow + headline + lede over a responsive member card\n// grid (initials avatar, role badge, one-line bio, ghost social buttons). Members\n// with a department field group under uppercase headers; variant=\"compact\" drops\n// bios for tighter cards. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input } from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\n\nexport interface TeamMember {\n  name: string\n  role: string\n  bio?: string\n  /** When any member has a department, the grid groups under uppercase headers. */\n  department?: string\n  twitter?: string\n  github?: string\n  linkedin?: string\n}\n\n/** Stub roster — swap for your data source after installing, or pass `members`. */\nconst DEFAULT_MEMBERS: TeamMember[] = [\n  {\n    name: 'Amara Okafor',\n    role: 'Co-founder & CEO',\n    bio: 'Sets the vision and still reviews every release note before it ships.',\n    twitter: 'https://x.com/amaraokafor',\n    linkedin: 'https://linkedin.com/in/amaraokafor',\n  },\n  {\n    name: 'Jonas Lindqvist',\n    role: 'Co-founder & CTO',\n    bio: 'Owns architecture and keeps p95 latency under 80ms in every region.',\n    github: 'https://github.com/jonaslindqvist',\n    linkedin: 'https://linkedin.com/in/jonaslindqvist',\n  },\n  {\n    name: 'Priya Raghavan',\n    role: 'Staff Engineer',\n    bio: 'Leads the platform team and maintains our public REST and GraphQL APIs.',\n    github: 'https://github.com/priyaraghavan',\n  },\n  {\n    name: 'Marco Deluca',\n    role: 'Product Designer',\n    bio: 'Turns messy workflows into calm interfaces and runs our weekly design critique.',\n    twitter: 'https://x.com/marcodeluca',\n    github: 'https://github.com/marcodeluca',\n    linkedin: 'https://linkedin.com/in/marcodeluca',\n  },\n  {\n    name: 'Tomoko Saito',\n    role: 'Head of People',\n    bio: 'Builds hiring loops that respect candidates and onboarding that actually sticks.',\n    linkedin: 'https://linkedin.com/in/tomokosaito',\n  },\n  {\n    name: 'Felix Adeyemi',\n    role: 'Growth Engineer',\n    bio: 'Experiments across onboarding and pricing pages; ships behind flags daily.',\n    twitter: 'https://x.com/felixadeyemi',\n  },\n]\n\nexport interface MemberSection {\n  department?: string\n  members: TeamMember[]\n}\n\nexport function initialsFor(name: string): string {\n  return name\n    .split(/\\s+/)\n    .filter(Boolean)\n    .slice(0, 2)\n    .map((part) => part.charAt(0).toUpperCase())\n    .join('')\n}\n\nexport function getSections(members: TeamMember[]): MemberSection[] {\n  if (!members.some((member) => member.department)) return [{ members }]\n  const grouped = new Map<string, TeamMember[]>()\n  for (const member of members) {\n    const key = member.department ?? ''\n    if (!grouped.has(key)) grouped.set(key, [])\n    grouped.get(key)!.push(member)\n  }\n  return Array.from(grouped.entries()).map(([department, groupedMembers]) => ({\n    department: department || undefined,\n    members: groupedMembers,\n  }))\n}\n\nconst X_SVG_PATH =\n  'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z'\nconst GITHUB_SVG_PATH =\n  'M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.55v-2.15c-3.2.69-3.87-1.36-3.87-1.36-.52-1.33-1.28-1.68-1.28-1.68-1.04-.71.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.02 1.76 2.69 1.25 3.35.96.1-.75.4-1.25.72-1.54-2.55-.29-5.24-1.28-5.24-5.68 0-1.26.45-2.28 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11.1 11.1 0 0 1 5.78 0c2.21-1.49 3.18-1.18 3.18-1.18.63 1.59.23 2.76.11 3.05.74.81 1.19 1.83 1.19 3.09 0 4.41-2.69 5.38-5.26 5.66.41.35.78 1.05.78 2.12v3.14c0 .31.21.67.8.55A11.51 11.51 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z'\nconst LINKEDIN_SVG_PATH =\n  'M20.45 20.45h-3.55v-5.57c0-1.33-.03-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.36V9h3.41v1.56h.05c.47-.9 1.63-1.85 3.36-1.85 3.6 0 4.27 2.37 4.27 5.46zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.55C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.73V1.72C24 .77 23.2 0 22.22 0z'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-team-section, [ui-team-section]',\n  standalone: true,\n  // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n  ],\n  template: `\n    <section data-slot=\"team-section\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-6xl px-6 py-24\">\n        @if (showHeader) {\n          <div data-slot=\"team-section-header\" class=\"mx-auto max-w-2xl text-center\">\n            @if (eyebrow) {\n              <p class=\"text-primary text-sm font-medium tracking-widest uppercase\">{{ eyebrow }}</p>\n            }\n            @if (title) {\n              <h2 class=\"mt-2 text-3xl font-semibold tracking-tight sm:text-4xl\">{{ title }}</h2>\n            }\n            @if (description) {\n              <p class=\"text-muted-foreground mt-3 text-lg leading-relaxed\">{{ description }}</p>\n            }\n          </div>\n        }\n\n        @for (section of sections; track section.department ?? 'team'; let i = $index) {\n          <div data-slot=\"team-section-group\" [class]=\"i > 0 || showHeader ? 'mt-12' : undefined\">\n            @if (section.department) {\n              <h3 class=\"text-muted-foreground text-xs font-semibold tracking-widest uppercase\">\n                {{ section.department }}\n              </h3>\n            }\n            <div data-slot=\"team-section-grid\" [class]=\"gridClass(section.department)\">\n              @for (member of section.members; track member.name) {\n                <div ui-card class=\"text-center\">\n                  <div ui-card-content [class]=\"cardContentClass\">\n                    <ui-avatar [class]=\"compact ? 'size-10' : 'size-16'\">\n                      <ui-avatar-fallback [class]=\"compact ? 'text-sm' : 'text-lg'\">\n                        {{ initialsFor(member.name) }}\n                      </ui-avatar-fallback>\n                    </ui-avatar>\n                    <div class=\"space-y-1\">\n                      <p class=\"text-sm font-semibold\">{{ member.name }}</p>\n                      <span ui-badge variant=\"secondary\">{{ member.role }}</span>\n                      @if (!compact && member.bio) {\n                        <p class=\"text-muted-foreground line-clamp-2 text-xs leading-relaxed\">{{ member.bio }}</p>\n                      }\n                    </div>\n                    @if (member.twitter || member.github || member.linkedin) {\n                      <div class=\"flex items-center gap-0.5\">\n                        @if (member.twitter) {\n                          <a\n                            ui-button\n                            variant=\"ghost\"\n                            size=\"icon-sm\"\n                            [href]=\"member.twitter\"\n                            target=\"_blank\"\n                            rel=\"noreferrer\"\n                            [attr.aria-label]=\"member.name + ' on X (Twitter)'\"\n                          >\n                            <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"size-4\" aria-hidden=\"true\">\n                              <path [attr.d]=\"xSvgPath\" />\n                            </svg>\n                          </a>\n                        }\n                        @if (member.github) {\n                          <a\n                            ui-button\n                            variant=\"ghost\"\n                            size=\"icon-sm\"\n                            [href]=\"member.github\"\n                            target=\"_blank\"\n                            rel=\"noreferrer\"\n                            [attr.aria-label]=\"member.name + ' on GitHub'\"\n                          >\n                            <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"size-4\" aria-hidden=\"true\">\n                              <path [attr.d]=\"githubSvgPath\" />\n                            </svg>\n                          </a>\n                        }\n                        @if (member.linkedin) {\n                          <a\n                            ui-button\n                            variant=\"ghost\"\n                            size=\"icon-sm\"\n                            [href]=\"member.linkedin\"\n                            target=\"_blank\"\n                            rel=\"noreferrer\"\n                            [attr.aria-label]=\"member.name + ' on LinkedIn'\"\n                          >\n                            <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"size-4\" aria-hidden=\"true\">\n                              <path [attr.d]=\"linkedinSvgPath\" />\n                            </svg>\n                          </a>\n                        }\n                      </div>\n                    }\n                  </div>\n                </div>\n              }\n            </div>\n          </div>\n        }\n      </div>\n    </section>\n  `,\n})\nexport class UiTeamSectionComponent {\n  @Input() eyebrow = 'Our team'\n  @Input() title = 'The people behind the product'\n  @Input() description =\n    'A small team with strong opinions about craft. We design, build and support everything ourselves — no outsourcing, no black boxes.'\n  @Input() members: TeamMember[] = DEFAULT_MEMBERS\n  /** 'grid' shows full cards with bios; 'compact' tightens cards and drops them. */\n  @Input() variant: 'grid' | 'compact' = 'grid'\n  @Input('class') className?: string\n\n  protected readonly xSvgPath = X_SVG_PATH\n  protected readonly githubSvgPath = GITHUB_SVG_PATH\n  protected readonly linkedinSvgPath = LINKEDIN_SVG_PATH\n\n  get compact(): boolean {\n    return this.variant === 'compact'\n  }\n\n  get showHeader(): boolean {\n    return Boolean(this.eyebrow || this.title || this.description)\n  }\n\n  get sections(): MemberSection[] {\n    return getSections(this.members ?? DEFAULT_MEMBERS)\n  }\n\n  get rootClass(): string {\n    return cn('bg-background', this.className)\n  }\n\n  get cardContentClass(): string {\n    return cn('flex flex-col items-center', this.compact ? 'gap-3 p-4' : 'gap-4 p-6')\n  }\n\n  gridClass(department: string | undefined): string {\n    return cn('grid gap-6 sm:grid-cols-2 lg:grid-cols-3', this.compact && 'gap-4', department && 'mt-6')\n  }\n\n  initialsFor(name: string): string {\n    return initialsFor(name)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/team-section/team-section.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/team-section/index.ts",
      "content": "export {\n  UiTeamSectionComponent,\n  getSections,\n  initialsFor,\n  type MemberSection,\n  type TeamMember,\n} from './team-section.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/team-section/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json"
  ],
  "description": "Centered marketing team section. Eyebrow + headline + lede over a responsive member card grid (initials avatar, role badge, one-line bio, ghost social buttons). Members with a department field group under uppercase headers; variant=\"compact\" drops bios for tighter cards. Roster is stub data — edit DEFAULT_MEMBERS or pass members.",
  "categories": [
    "marketing"
  ]
}