{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mission-hero",
  "title": "Mission Hero",
  "description": "Landing-page hero that leads with an organisation's mission rather than its shop: eyebrow, mission statement as the page h1, supporting copy and up to two actions beside a picture or muted looping video masked to the Landbou24 hero shape (or a plain rounded frame), with an optional tagline set in the shape's bottom-right notch. Below it, an optional grid of services (title, description, optional link and host icon), so a storefront reads as one service of the mission. Presentational; actions use the host's buttonVariants so they follow its theme; hosts pass copy, swap the media for next/image via mediaContent and pass their router link through renderLink.",
  "registryDependencies": [
    "button",
    "card"
  ],
  "files": [
    {
      "path": "default/mission-hero/mission-hero.tsx",
      "content": "import { useId, type ReactNode } from \"react\"\n\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\n\nexport type MissionHeroLinkProps = {\n  href: string\n  className?: string\n  children: ReactNode\n}\n\nexport type MissionHeroAction = {\n  label: string\n  href: string\n}\n\nexport type MissionHeroMedia = {\n  /** Image or video URL. */\n  src: string\n  /** Describes the image. Leave empty for a purely decorative picture. */\n  alt?: string\n  type?: \"image\" | \"video\"\n  /** Still frame shown before a video plays. */\n  poster?: string\n}\n\nexport type MissionHeroService = {\n  /** Stable key; defaults to the title. */\n  id?: string\n  title: string\n  description?: string\n  /** Where the service lives. Omit for a service with no page yet. */\n  href?: string | null\n  /** Link text. Defaults to \"Learn more\". */\n  linkLabel?: string\n  /** Decorative icon from the host's own icon set. */\n  icon?: ReactNode\n}\n\nexport type MissionHeroProps = {\n  /** Short line above the heading, e.g. the brand name. */\n  eyebrow?: string | null\n  /** The mission statement. Rendered as the page's h1. */\n  title: string\n  description?: string | null\n  primaryAction?: MissionHeroAction | null\n  secondaryAction?: MissionHeroAction | null\n  media?: MissionHeroMedia | null\n  /**\n   * Replaces the default <img>/<video> inside the media frame, e.g. with\n   * next/image. It should fill its parent (absolute inset-0, object-cover).\n   */\n  mediaContent?: ReactNode\n  /**\n   * \"organic\" masks the media to the Landbou24 hero shape, with a notch in the\n   * bottom-right corner for `mediaBadge`. \"rounded\" is a plain rounded frame.\n   */\n  mediaShape?: \"organic\" | \"rounded\"\n  /** A few words set in the notch of the organic shape, e.g. a tagline. */\n  mediaBadge?: string | null\n  servicesTitle?: string | null\n  /**\n   * What the organisation offers. The storefront is one of these, so a shop\n   * reads as a service of the mission rather than the whole of it.\n   */\n  services?: MissionHeroService[]\n  /** Swaps the plain anchor for the host's router link (next/link, i18n Link). */\n  renderLink?: (props: MissionHeroLinkProps) => ReactNode\n  className?: string\n}\n\n// The Landbou24 hero shape (Figma node 54-1477): rounded rectangle with a\n// stepped cut-out top-left and a notch bottom-right, at 762x384.\nconst ORGANIC_MASK = `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 762 384.125' preserveAspectRatio='none'%3E%3Cpath d='M722.409 0C744.275 0 762 17.7254 762 39.5908V272.534C762 294.4 744.275 312.125 722.409 312.125H583.002C563.43 312.125 547.506 327.743 547.014 347.196L546.99 349.054C546.498 368.507 530.574 384.125 511.002 384.125H39.5908C17.7254 384.125 0 366.4 0 344.534V183.779C0 169.658 11.4477 158.21 25.5691 158.21H57.4824C78.6947 158.21 95.954 141.283 96.4883 120.199L96.5137 118.186C97.0477 97.1019 114.306 80.174 135.519 80.1738H200.431C224.636 80.1737 244.258 60.5517 244.258 36.3467C244.258 24.0261 249.342 12.8933 257.526 4.93107C261.182 1.37502 266.314 0 271.413 0H722.409Z' fill='white'/%3E%3C/svg%3E\")`\n\nconst organicMaskStyle = {\n  maskImage: ORGANIC_MASK,\n  maskSize: \"100% 100%\",\n  maskRepeat: \"no-repeat\",\n  WebkitMaskImage: ORGANIC_MASK,\n  WebkitMaskSize: \"100% 100%\",\n  WebkitMaskRepeat: \"no-repeat\",\n} as const\n\n// Actions take the host's own Button styles, so they match its theme (radius,\n// shadow, focus ring) rather than a look baked into the block.\nconst primaryLinkClass = buttonVariants({ variant: \"default\", size: \"lg\" })\n\nconst secondaryLinkClass = buttonVariants({ variant: \"outline\", size: \"lg\" })\n\nconst textLinkClass =\n  \"text-sm font-medium text-primary underline-offset-4 hover:underline\"\n\nfunction cx(...classes: Array<string | false | null | undefined>) {\n  return classes.filter(Boolean).join(\" \")\n}\n\nfunction defaultLink({ href, className, children }: MissionHeroLinkProps) {\n  return (\n    <a href={href} className={className}>\n      {children}\n    </a>\n  )\n}\n\nfunction MediaFrame({\n  media,\n  mediaContent,\n  shape,\n  badge,\n}: {\n  media?: MissionHeroMedia | null\n  mediaContent?: ReactNode\n  shape: \"organic\" | \"rounded\"\n  badge?: string | null\n}) {\n  const organic = shape === \"organic\"\n  const content =\n    mediaContent ??\n    (media ? (\n      media.type === \"video\" ? (\n        <video\n          autoPlay\n          loop\n          muted\n          playsInline\n          preload=\"metadata\"\n          poster={media.poster}\n          aria-label={media.alt || undefined}\n          aria-hidden={media.alt ? undefined : true}\n          className=\"absolute inset-0 h-full w-full object-cover\"\n        >\n          <source src={media.src} />\n        </video>\n      ) : (\n        <img\n          src={media.src}\n          alt={media.alt ?? \"\"}\n          className=\"absolute inset-0 h-full w-full object-cover\"\n        />\n      )\n    ) : null)\n\n  if (!content) return null\n\n  return (\n    <div data-slot=\"mission-hero-media\" className=\"relative w-full\">\n      <div\n        data-shape={shape}\n        className={cx(\n          \"relative aspect-[762/384] w-full overflow-hidden bg-muted\",\n          !organic && \"rounded-2xl\"\n        )}\n        style={organic ? organicMaskStyle : undefined}\n      >\n        {content}\n      </div>\n      {organic && badge ? (\n        // Sits in the notch the mask cuts out of the bottom-right corner:\n        // x 547/762 (72%) to the edge, y 312/384 (81%) to the bottom.\n        <p\n          data-slot=\"mission-hero-badge\"\n          className=\"absolute bottom-0 right-0 flex h-[19%] w-[28%] items-center justify-center text-center text-xs font-bold leading-tight tracking-tight text-foreground sm:text-base lg:text-lg\"\n        >\n          {badge}\n        </p>\n      ) : null}\n    </div>\n  )\n}\n\n/**\n * Landing-page hero that leads with an organisation's mission: the statement,\n * one or two actions and a picture in the Landbou24 hero shape, then the\n * services that deliver on it. Presentational; the host passes copy, links\n * and its router link.\n */\nexport function MissionHero({\n  eyebrow,\n  title,\n  description,\n  primaryAction,\n  secondaryAction,\n  media,\n  mediaContent,\n  mediaShape = \"organic\",\n  mediaBadge,\n  servicesTitle,\n  services = [],\n  renderLink = defaultLink,\n  className,\n}: MissionHeroProps) {\n  const headingId = useId()\n  const servicesHeadingId = useId()\n\n  return (\n    <section\n      data-slot=\"mission-hero\"\n      aria-labelledby={headingId}\n      className={cx(\"flex flex-col gap-12 py-8 lg:gap-16 lg:py-12\", className)}\n    >\n      <div className=\"grid grid-cols-1 items-center gap-8 lg:grid-cols-2 xl:gap-12\">\n        <div className=\"flex flex-col items-start gap-6\">\n          {eyebrow ? (\n            <p className=\"text-sm font-medium uppercase tracking-wide text-primary\">\n              {eyebrow}\n            </p>\n          ) : null}\n          <h1\n            id={headingId}\n            className=\"text-4xl font-bold leading-tight tracking-tight text-foreground md:text-5xl xl:text-6xl\"\n          >\n            {title}\n          </h1>\n          {description ? (\n            <p className=\"max-w-xl text-base text-muted-foreground md:text-lg\">\n              {description}\n            </p>\n          ) : null}\n          {primaryAction || secondaryAction ? (\n            <div className=\"flex w-full flex-col gap-3 sm:w-auto sm:flex-row\">\n              {primaryAction\n                ? renderLink({\n                    href: primaryAction.href,\n                    className: primaryLinkClass,\n                    children: primaryAction.label,\n                  })\n                : null}\n              {secondaryAction\n                ? renderLink({\n                    href: secondaryAction.href,\n                    className: secondaryLinkClass,\n                    children: secondaryAction.label,\n                  })\n                : null}\n            </div>\n          ) : null}\n        </div>\n\n        <MediaFrame\n          media={media}\n          mediaContent={mediaContent}\n          shape={mediaShape}\n          badge={mediaBadge}\n        />\n      </div>\n\n      {services.length > 0 ? (\n        <div\n          data-slot=\"mission-hero-services\"\n          role={servicesTitle ? \"group\" : undefined}\n          aria-labelledby={servicesTitle ? servicesHeadingId : undefined}\n          className=\"flex flex-col gap-6\"\n        >\n          {servicesTitle ? (\n            <h2\n              id={servicesHeadingId}\n              className=\"text-2xl font-semibold tracking-tight\"\n            >\n              {servicesTitle}\n            </h2>\n          ) : null}\n          <ul\n            className={cx(\n              \"grid grid-cols-1 gap-6 sm:grid-cols-2\",\n              services.length >= 4\n                ? \"lg:grid-cols-4\"\n                : services.length === 3\n                  ? \"lg:grid-cols-3\"\n                  : null\n            )}\n          >\n            {services.map((service) => (\n              <li key={service.id ?? service.title} className=\"flex\">\n                <Card\n                  data-slot=\"mission-hero-service\"\n                  className=\"flex w-full flex-col\"\n                >\n                  <CardHeader className=\"flex flex-1 flex-col gap-3\">\n                    {service.icon ? (\n                      <span\n                        aria-hidden=\"true\"\n                        className=\"flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary\"\n                      >\n                        {service.icon}\n                      </span>\n                    ) : null}\n                    <CardTitle>{service.title}</CardTitle>\n                    {service.description ? (\n                      <CardDescription>{service.description}</CardDescription>\n                    ) : null}\n                  </CardHeader>\n                  {service.href ? (\n                    <CardFooter>\n                      {renderLink({\n                        href: service.href,\n                        className: textLinkClass,\n                        children: service.linkLabel ?? (\n                          <>\n                            Learn more\n                            <span className=\"sr-only\"> about {service.title}</span>\n                          </>\n                        ),\n                      })}\n                    </CardFooter>\n                  ) : null}\n                </Card>\n              </li>\n            ))}\n          </ul>\n        </div>\n      ) : null}\n    </section>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}