{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "faq89",
  "title": "Filterable Glossary",
  "description": "A jargon glossary whose letter filter is derived from the terms themselves, narrowing a two-column hairline definition list with an empty state and a suggest-a-term action.",
  "dependencies": [
    "clsx",
    "tailwind-merge",
    "lucide-react"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "registry/faq89/faq89.tsx",
      "content": "\"use client\";\n\nimport { Badge23 } from \"@/components/beste/component/badge23\";\nimport { Button21 } from \"@/components/beste/component/button21\";\nimport { cn } from \"@/lib/utils\";\nimport Link from \"next/link\";\nimport { useState } from \"react\";\n\ninterface Badge {\n  label: string;\n}\n\ninterface ActionLink {\n  label: string;\n  href: string;\n}\n\ninterface Term {\n  term: string;\n  definition: string;\n}\n\ninterface Faq89Props {\n  badge?: Badge;\n  heading?: string;\n  description?: string;\n  allLabel?: string;\n  terms?: Term[];\n  emptyLabel?: string;\n  closing?: string;\n  button?: ActionLink;\n  className?: string;\n}\n\nexport const faq89Demo: Faq89Props = {\n  badge: { label: \"Plain English\" },\n  heading: \"The words we use, and what we actually mean by them\",\n  description:\n    \"Clinical software collects vocabulary. Here is ours, defined by someone who has to explain it on calls.\",\n  allLabel: \"All\",\n  terms: [\n    {\n      term: \"Backfill\",\n      definition:\n        \"When a cancelled slot is offered to the waiting list automatically and taken by whoever confirms first. Nobody rings round.\",\n    },\n    {\n      term: \"Capacity\",\n      definition:\n        \"Rooms multiplied by opening hours, minus what is already booked. It is measured against rooms rather than clinicians, because rooms are the thing you cannot add on a Tuesday.\",\n    },\n    {\n      term: \"Care plan\",\n      definition:\n        \"A repeating series of appointments attached to one member, billed as a block rather than one visit at a time.\",\n    },\n    {\n      term: \"Dry run\",\n      definition:\n        \"Your real data loaded into a private workspace nobody outside your practice can reach, so your team can break it without consequence.\",\n    },\n    {\n      term: \"Field mapping\",\n      definition:\n        \"Agreeing what each column in your old export becomes in the new record. One hour, once, with your practice lead.\",\n    },\n    {\n      term: \"Member\",\n      definition:\n        \"The person receiving care. We avoid 'patient' because half the practices on the platform do not use it.\",\n    },\n    {\n      term: \"Role\",\n      definition:\n        \"A named job with an access level attached, like Reception or Locum. There are six and none of them are a checkbox grid.\",\n    },\n    {\n      term: \"Seat\",\n      definition:\n        \"One person with a login. Seats move up and down mid-month and we prorate both directions.\",\n    },\n    {\n      term: \"Site\",\n      definition:\n        \"One physical clinic. Sites share a waiting list and capacity but keep their own rooms, hours, and rules.\",\n    },\n    {\n      term: \"Utilisation\",\n      definition:\n        \"The share of available room hours that were actually booked. The number most practices are trying to move.\",\n    },\n  ],\n  emptyLabel: \"Nothing filed under that letter.\",\n  closing: \"Missing a word you keep having to explain to us? Tell us and it goes on the list.\",\n  button: { label: \"Suggest a term\", href: \"https://beste.co\" },\n};\n\nexport function Faq89({\n  badge,\n  heading,\n  description,\n  allLabel = \"All\",\n  terms = [],\n  emptyLabel,\n  closing,\n  button,\n  className,\n}: Faq89Props) {\n  const [letter, setLetter] = useState<string | null>(null);\n\n  const letters = Array.from(new Set(terms.map((term) => term.term.charAt(0).toUpperCase()))).sort();\n  const visible = letter\n    ? terms.filter((term) => term.term.charAt(0).toUpperCase() === letter)\n    : terms;\n\n  return (\n    <section className={cn(\"bg-background py-16 md:py-24 w-full\", className)}>\n      <div className=\"mx-auto max-w-7xl px-4 md:px-6\">\n        {badge && <Badge23 label={badge.label} />}\n\n        <div className=\"mt-6 border-t border-border pt-8 md:pt-10\">\n          <div className=\"grid gap-6 md:grid-cols-2 md:gap-12\">\n            {heading && (\n              <h2 className=\"text-3xl font-light leading-[1.1] tracking-tight text-foreground md:text-5xl\">\n                {heading}\n              </h2>\n            )}\n            {description && (\n              <div className=\"flex md:justify-end\">\n                <p className=\"max-w-md text-base leading-relaxed text-muted-foreground\">\n                  {description}\n                </p>\n              </div>\n            )}\n          </div>\n        </div>\n\n        <div className=\"mt-10 flex flex-wrap gap-2\">\n          <button\n            type=\"button\"\n            onClick={() => setLetter(null)}\n            aria-pressed={letter === null}\n            className={cn(\n              \"cursor-pointer rounded-md border px-3 py-1.5 text-sm transition-colors\",\n              letter === null\n                ? \"border-foreground bg-foreground text-background\"\n                : \"border-border bg-transparent text-muted-foreground hover:text-foreground\"\n            )}\n          >\n            {allLabel}\n          </button>\n          {letters.map((entry, index) => {\n            const isActive = letter === entry;\n            return (\n              <button\n                key={index}\n                type=\"button\"\n                onClick={() => setLetter(entry)}\n                aria-pressed={isActive}\n                className={cn(\n                  \"size-9 cursor-pointer rounded-md border text-sm transition-colors\",\n                  isActive\n                    ? \"border-foreground bg-foreground text-background\"\n                    : \"border-border bg-transparent text-muted-foreground hover:text-foreground\"\n                )}\n              >\n                {entry}\n              </button>\n            );\n          })}\n        </div>\n\n        <dl className=\"mt-10\">\n          {visible.map((term, index) => (\n            <div\n              key={index}\n              className=\"grid gap-2 border-t border-border py-5 md:grid-cols-[minmax(0,1fr)_minmax(0,2.4fr)] md:gap-12\"\n            >\n              <dt className=\"text-lg font-medium text-foreground\">{term.term}</dt>\n              <dd className=\"text-base leading-relaxed text-muted-foreground\">\n                {term.definition}\n              </dd>\n            </div>\n          ))}\n        </dl>\n\n        {visible.length === 0 && emptyLabel && (\n          <p className=\"border-t border-border py-6 text-base text-muted-foreground\">\n            {emptyLabel}\n          </p>\n        )}\n\n        <div className=\"mt-8 flex flex-col items-start gap-4 border-t border-border pt-8 sm:flex-row sm:items-center sm:justify-between\">\n          {closing && <p className=\"text-base text-muted-foreground\">{closing}</p>}\n          {button && (\n            <Button21 asChild label={button.label} tone=\"outline\">\n              <Link href={button.href} />\n            </Button21>\n          )}\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/beste/block/faq89.tsx"
    },
    {
      "path": "registry-components/badge23/badge23.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype Tone = \"muted\" | \"foreground\" | \"primary\";\n\ninterface Badge23Props {\n  /** Eyebrow label (rendered uppercase) */\n  label: string;\n  /** Text/border tone */\n  tone?: Tone;\n  /** Additional classes merged onto the root */\n  className?: string;\n}\n\nconst toneStyles: Record<Tone, string> = {\n  muted: \"border-border text-muted-foreground\",\n  foreground: \"border-foreground/30 text-foreground\",\n  primary: \"border-primary/40 text-primary\",\n};\n\nexport const badge23Demo: Badge23Props = {\n  label: \"Product\",\n};\n\nexport function Badge23({ label, tone = \"muted\", className }: Badge23Props) {\n  return (\n    <span\n      className={cn(\n        \"inline-flex items-center rounded-md border bg-transparent px-2.5 py-1 font-mono text-sm uppercase leading-none tracking-widest\",\n        toneStyles[tone],\n        className\n      )}\n    >\n      {label}\n    </span>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/beste/component/badge23.tsx"
    },
    {
      "path": "registry-components/button21/button21.tsx",
      "content": "\"use client\";\n\nimport type { LucideIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\ntype Tone = \"primary\" | \"neutral\" | \"outline\";\n\ninterface Button21Props {\n  /** Button label */\n  label: string;\n  /**\n   * Compose the rendered element shadcn-style (radix asChild): your element\n   * (e.g. a router Link) becomes the root and the button content is injected\n   * as its children. Pass the element without children of its own.\n   */\n  asChild?: boolean;\n  /** The element to render when `asChild` is set (e.g. your framework's Link) */\n  children?: React.ReactElement;\n  /**\n   * Fallback link: renders a plain `<a>`. Prefer `asChild` with your\n   * framework's Link component for client-side navigation.\n   */\n  href?: string;\n  /** Optional trailing icon */\n  icon?: LucideIcon;\n  /** Surface tone: solid accent (default), soft neutral pill, or hairline outline */\n  tone?: Tone;\n  /** Click handler (used when there is no href and no asChild) */\n  onClick?: React.MouseEventHandler<HTMLButtonElement>;\n  /** Additional classes merged onto the button */\n  className?: string;\n}\n\nconst toneStyles: Record<Tone, string> = {\n  primary: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n  neutral: \"bg-muted text-foreground hover:bg-muted/70\",\n  outline: \"border border-border bg-transparent text-foreground hover:bg-muted\",\n};\n\nexport const button21Demo: Button21Props = {\n  label: \"Book a demo\",\n};\n\nexport function Button21({\n  label,\n  asChild = false,\n  children,\n  href,\n  icon: Icon,\n  tone = \"primary\",\n  onClick,\n  className,\n}: Button21Props) {\n  const classes = cn(\n    \"group/button21 h-auto w-fit gap-2 rounded-md px-5 py-2.5 text-sm font-medium tracking-tight transition-colors\",\n    toneStyles[tone],\n    className\n  );\n\n  const inner = (\n    <>\n      {label}\n      {Icon && (\n        <Icon className=\"size-4 transition-transform motion-safe:group-hover/button21:translate-x-0.5\" />\n      )}\n    </>\n  );\n\n  if (asChild && React.isValidElement(children)) {\n    return (\n      <Button asChild className={classes}>\n        {React.cloneElement(children, undefined, inner)}\n      </Button>\n    );\n  }\n\n  if (href) {\n    return (\n      <Button asChild className={classes}>\n        <a href={href}>{inner}</a>\n      </Button>\n    );\n  }\n\n  return (\n    <Button type=\"button\" className={classes} onClick={onClick}>\n      {inner}\n    </Button>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/beste/component/button21.tsx"
    }
  ],
  "type": "registry:block"
}