{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "partner-spotlight",
  "title": "Partner Spotlight",
  "description": "Landing-page section that promotes a partnership: a logo lockup of the partners joined by a cross (logos on white tiles, or names as text), eyebrow, heading, supporting copy and a short checklist of what the reader gets, with link tiles shaped like app-store badges (caption, label or a logo standing in for it, optional icon; external links open in a new tab). Sits in a rounded card, dark by default with an optional cover photo under a legibility gradient, or muted; an optional picture is anchored to the card's bottom edge beside the copy. Presentational; hosts pass copy, swap the media for next/image via mediaContent and pass their router link through renderLink.",
  "registryDependencies": [
    "card"
  ],
  "files": [
    {
      "path": "default/partner-spotlight/partner-spotlight.tsx",
      "content": "import { Fragment, useId, type ReactNode } from \"react\"\n\nimport { Card, CardContent } from \"@/components/ui/card\"\n\nexport type PartnerSpotlightLinkProps = {\n  href: string\n  className?: string\n  /** True for a link that leaves the site; the default opens it in a new tab. */\n  external?: boolean\n  children: ReactNode\n}\n\nexport type PartnerSpotlightPartner = {\n  /** Shown as text when there is no logo, and as the logo's alt text. */\n  name: string\n  /** Logo URL. Sits on a white tile, so any colour logo reads on the dark card. */\n  logo?: string | null\n}\n\nexport type PartnerSpotlightAction = {\n  label: string\n  href: string\n  /** Small line above the label, e.g. \"Visit\" or \"Read on\". */\n  caption?: string | null\n  /** Decorative icon from the host's own icon set. */\n  icon?: ReactNode\n  /**\n   * Logo URL shown in place of the label, like the wordmark on an app-store\n   * badge. The label becomes its alt text, so the link keeps its name.\n   */\n  logo?: string | null\n  external?: boolean\n}\n\nexport type PartnerSpotlightMedia = {\n  src: string\n  /** Describes the image. Leave empty for a purely decorative picture. */\n  alt?: string\n}\n\nexport type PartnerSpotlightProps = {\n  /**\n   * The organisations in the partnership, host first. Rendered as a logo\n   * lockup joined by a cross, e.g. FarmSURE × africanfarming.com.\n   */\n  partners?: PartnerSpotlightPartner[]\n  /** Short line above the heading, e.g. \"Official partner\". */\n  eyebrow?: string | null\n  title: string\n  description?: string | null\n  /** What the partnership gives the reader, as a short checklist. */\n  highlights?: string[]\n  /** Link tiles in the shape of app-store badges. The first is the main one. */\n  actions?: PartnerSpotlightAction[]\n  /** Cover image behind the whole card. The card stays dark underneath it. */\n  backgroundImage?: string | null\n  /** Picture beside the copy, anchored to the bottom of the card. */\n  media?: PartnerSpotlightMedia | null\n  /** Replaces the default media <img>, e.g. with next/image. */\n  mediaContent?: ReactNode\n  /** \"dark\" is the black card; \"muted\" a light card on the muted surface. */\n  tone?: \"dark\" | \"muted\"\n  /** Swaps the plain anchor for the host's router link (next/link, i18n Link). */\n  renderLink?: (props: PartnerSpotlightLinkProps) => ReactNode\n  className?: string\n}\n\nconst tones = {\n  dark: {\n    card: \"bg-neutral-950 text-white\",\n    eyebrow: \"text-white/70\",\n    body: \"text-white/70\",\n    cross: \"text-white/50\",\n    check: \"bg-white/15 text-white\",\n  },\n  muted: {\n    card: \"bg-muted text-foreground\",\n    eyebrow: \"text-primary\",\n    body: \"text-muted-foreground\",\n    cross: \"text-muted-foreground\",\n    check: \"bg-primary/10 text-primary\",\n  },\n} as const\n\nconst actionTileClass =\n  \"flex min-w-[10rem] items-center gap-3 rounded-md bg-white px-4 py-2.5 text-left text-black shadow-sm ring-1 ring-black/5 transition-transform hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 sm:min-w-[12.5rem] sm:px-6\"\n\nfunction cx(...classes: Array<string | false | null | undefined>) {\n  return classes.filter(Boolean).join(\" \")\n}\n\nfunction defaultLink({\n  href,\n  className,\n  external,\n  children,\n}: PartnerSpotlightLinkProps) {\n  return (\n    <a\n      href={href}\n      className={className}\n      target={external ? \"_blank\" : undefined}\n      rel={external ? \"noopener noreferrer\" : undefined}\n    >\n      {children}\n    </a>\n  )\n}\n\nfunction CheckIcon() {\n  return (\n    <svg\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className=\"h-3 w-3\"\n    >\n      <path d=\"M3.5 8.5l3 3 6-7\" />\n    </svg>\n  )\n}\n\nfunction PartnerLockup({\n  partners,\n  crossClass,\n}: {\n  partners: PartnerSpotlightPartner[]\n  crossClass: string\n}) {\n  return (\n    <ul\n      data-slot=\"partner-spotlight-partners\"\n      aria-label=\"Partners\"\n      className=\"flex flex-wrap items-center gap-3 max-lg:justify-center\"\n    >\n      {partners.map((partner, index) => (\n        <li key={partner.name} className=\"flex items-center gap-3\">\n          {index > 0 ? (\n            <span aria-hidden=\"true\" className={cx(\"text-xl\", crossClass)}>\n              ×\n            </span>\n          ) : null}\n          {partner.logo ? (\n            <span className=\"flex h-12 items-center rounded-lg bg-white px-3\">\n              <img\n                src={partner.logo}\n                alt={partner.name}\n                className=\"h-8 w-auto max-w-[9rem] object-contain\"\n              />\n            </span>\n          ) : (\n            <span className=\"text-lg font-semibold tracking-tight\">\n              {partner.name}\n            </span>\n          )}\n        </li>\n      ))}\n    </ul>\n  )\n}\n\n/**\n * Promotes a partnership on a landing page: the partners' logos, a heading,\n * supporting copy and a short list of what the reader gets, with link tiles\n * shaped like app-store badges, beside a picture anchored to the bottom of a\n * rounded card. Presentational; the host passes copy, links and its router\n * link.\n */\nexport function PartnerSpotlight({\n  partners = [],\n  eyebrow,\n  title,\n  description,\n  highlights = [],\n  actions = [],\n  backgroundImage,\n  media,\n  mediaContent,\n  tone = \"dark\",\n  renderLink = defaultLink,\n  className,\n}: PartnerSpotlightProps) {\n  const headingId = useId()\n  const colors = tones[tone]\n  const visual =\n    mediaContent ??\n    (media ? (\n      <img\n        src={media.src}\n        alt={media.alt ?? \"\"}\n        className=\"w-auto sm:w-[28rem] lg:w-[32rem]\"\n      />\n    ) : null)\n  const hasVisual = visual != null\n\n  return (\n    <section\n      data-slot=\"partner-spotlight\"\n      aria-labelledby={headingId}\n      className={cx(\"py-8 sm:py-16 lg:py-24\", className)}\n    >\n      <Card\n        data-tone={tone}\n        className={cx(\n          \"relative overflow-hidden rounded-3xl border-none p-0 shadow-lg\",\n          colors.card\n        )}\n      >\n        {backgroundImage ? (\n          <>\n            <img\n              data-slot=\"partner-spotlight-background\"\n              src={backgroundImage}\n              alt=\"\"\n              aria-hidden=\"true\"\n              className=\"absolute inset-0 h-full w-full object-cover\"\n            />\n            {tone === \"dark\" ? (\n              // Keeps the white copy readable over any photograph.\n              <div\n                aria-hidden=\"true\"\n                className=\"absolute inset-0 bg-gradient-to-r from-black/85 via-black/60 to-black/20 max-lg:bg-gradient-to-b\"\n              />\n            ) : null}\n          </>\n        ) : null}\n\n        <CardContent\n          className={cx(\n            \"relative flex gap-12 px-6 pt-10 max-lg:flex-col max-lg:text-center sm:px-16 sm:pt-16 lg:px-12 xl:gap-24 xl:px-20\",\n            hasVisual ? \"pb-0\" : \"pb-10 sm:pb-16\"\n          )}\n        >\n          <div\n            className={cx(\n              \"flex flex-col gap-5 max-lg:items-center xl:flex-1\",\n              hasVisual && \"lg:pb-16\"\n            )}\n          >\n            {partners.length > 0 ? (\n              <PartnerLockup partners={partners} crossClass={colors.cross} />\n            ) : null}\n            {eyebrow ? (\n              <p\n                className={cx(\n                  \"text-sm font-medium uppercase tracking-wide\",\n                  colors.eyebrow\n                )}\n              >\n                {eyebrow}\n              </p>\n            ) : null}\n            <h2\n              id={headingId}\n              className=\"text-2xl font-semibold tracking-tight md:text-3xl lg:text-4xl\"\n            >\n              {title}\n            </h2>\n            {description ? (\n              <p className={cx(\"max-w-2xl text-base sm:text-xl\", colors.body)}>\n                {description}\n              </p>\n            ) : null}\n            {highlights.length > 0 ? (\n              <ul\n                data-slot=\"partner-spotlight-highlights\"\n                className=\"flex flex-col gap-2 text-left\"\n              >\n                {highlights.map((highlight) => (\n                  <li key={highlight} className=\"flex items-start gap-3\">\n                    <span\n                      aria-hidden=\"true\"\n                      className={cx(\n                        \"mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full\",\n                        colors.check\n                      )}\n                    >\n                      <CheckIcon />\n                    </span>\n                    <span className={colors.body}>{highlight}</span>\n                  </li>\n                ))}\n              </ul>\n            ) : null}\n            {actions.length > 0 ? (\n              <div className=\"mt-2 flex flex-wrap items-center gap-4 max-lg:justify-center\">\n                {actions.map((action) => (\n                  <Fragment key={action.href}>\n                    {renderLink({\n                      href: action.href,\n                      external: action.external,\n                      className: actionTileClass,\n                      children: (\n                        <>\n                          {action.icon ? (\n                            <span\n                              aria-hidden=\"true\"\n                              className=\"flex h-7 w-7 shrink-0 items-center justify-center\"\n                            >\n                              {action.icon}\n                            </span>\n                          ) : null}\n                          <span className=\"flex flex-col items-start\">\n                            {action.caption ? (\n                              <span className=\"text-xs leading-4 text-black/70\">\n                                {action.caption}\n                              </span>\n                            ) : null}\n                            {action.logo ? (\n                              <img\n                                src={action.logo}\n                                alt={action.label}\n                                className=\"mt-1 h-10 w-auto max-w-[13rem] object-contain\"\n                              />\n                            ) : (\n                              <span className=\"text-base font-medium leading-6 text-black/90\">\n                                {action.label}\n                              </span>\n                            )}\n                          </span>\n                          {action.external ? (\n                            <span className=\"sr-only\"> (opens in a new tab)</span>\n                          ) : null}\n                        </>\n                      ),\n                    })}\n                  </Fragment>\n                ))}\n              </div>\n            ) : null}\n          </div>\n\n          {hasVisual ? (\n            <div\n              data-slot=\"partner-spotlight-media\"\n              className=\"mt-auto flex items-end justify-center sm:min-w-[22.5rem]\"\n            >\n              {visual}\n            </div>\n          ) : null}\n        </CardContent>\n      </Card>\n    </section>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}