{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "faq92",
  "title": "Answer Router",
  "description": "A help section that asks two questions instead of offering a search box: the answers pick one written answer from a list of routes, an image tile beside them floats the answer as a live assistant card and changes with it, and a soft panel underneath keeps a way through to a person.",
  "dependencies": [
    "clsx",
    "tailwind-merge",
    "lucide-react"
  ],
  "registryDependencies": [
    "questionnaire",
    "button"
  ],
  "files": [
    {
      "path": "registry/faq92/faq92.tsx",
      "content": "\"use client\";\n\nimport { ArrowUpRight, RotateCcw } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { type FormEvent, type ReactNode, useState } from \"react\";\nimport { Badge23 } from \"@/components/beste/component/badge23\";\nimport { Button21 } from \"@/components/beste/component/button21\";\nimport { Chat34 } from \"@/components/beste/piece/chat34\";\nimport {\n  Questionnaire,\n  QuestionnaireActions,\n  QuestionnaireChoice,\n  QuestionnaireChoiceDescription,\n  QuestionnaireChoices,\n  QuestionnaireDescription,\n  QuestionnaireError,\n  QuestionnaireItem,\n  QuestionnaireNext,\n  QuestionnairePrevious,\n  QuestionnaireProgress,\n  QuestionnaireSubmit,\n  QuestionnaireTitle,\n} from \"@/components/ui/questionnaire\";\nimport { cn } from \"@/lib/utils\";\n\ninterface Badge {\n  label: string;\n}\n\ninterface ActionLink {\n  label: string;\n  href: string;\n}\n\ninterface RouterImage {\n  src: string;\n  alt: string;\n}\n\ninterface RouterChoice {\n  value: string;\n  label: string;\n  description?: string;\n}\n\ninterface RouterQuestion {\n  /** Form field name. Must be unique across the flow. */\n  name: string;\n  title: string;\n  description?: string;\n  choices?: RouterChoice[];\n}\n\ninterface Answer {\n  /**\n   * Field name to answer value. An answer is only shown when every pair\n   * matches. Leave it out on the last entry, which is the catch-all.\n   */\n  match?: Record<string, string>;\n  title: string;\n  description: string;\n  link?: ActionLink;\n  /** Replaces the standing photograph once this answer is shown */\n  image?: RouterImage;\n  /** Piece floated over the photograph once this answer is shown */\n  media?: ReactNode;\n  caption?: string;\n}\n\ninterface Contact {\n  title: string;\n  description: string;\n  link: ActionLink;\n}\n\ninterface Faq92Labels {\n  previous?: string;\n  next?: string;\n  submit?: string;\n  restart?: string;\n  answerTitle?: string;\n}\n\ninterface Faq92Props {\n  badge?: Badge;\n  heading?: string;\n  description?: string;\n  questions?: RouterQuestion[];\n  /** Prints a scoped shortcut on every choice and binds it while the question is active */\n  shortcuts?: \"letters\" | \"numbers\";\n  /** Checked in order; the first one whose `match` fits the answers is shown. */\n  answers?: Answer[];\n  /** Standing photograph, shown until an answer is found */\n  image?: RouterImage;\n  /** Piece floated over the photograph before an answer is found */\n  media?: ReactNode;\n  caption?: string;\n  /** Always shown under the photograph, for the times the routing was not enough */\n  contact?: Contact;\n  labels?: Faq92Labels;\n  className?: string;\n}\n\nexport const faq92Demo: Faq92Props = {\n  badge: { label: \"Help\" },\n  heading: \"Two questions instead of a search box\",\n  description:\n    \"Most support tickets are one of a dozen questions wearing different words. Tell us where you are and what happened, and we will point at the page that answers it.\",\n  shortcuts: \"numbers\",\n  image: {\n    src: \"https://images.unsplash.com/photo-1579508750794-1959a573f5e6?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\n    alt: \"An open notebook with a pen and a pair of glasses resting on it\",\n  },\n  media: (\n    <Chat34\n      question=\"How do I change the card on the account?\"\n      answer=\"Any admin can update it in billing settings. The change applies to the next invoice rather than the one already issued.\"\n      sourcesLabel=\"Answered from\"\n      sources={[\"Billing\", \"Roles\"]}\n    />\n  ),\n  caption: \"Four people answer these, and they wrote the pages the router points at.\",\n  labels: {\n    submit: \"Show me the answer\",\n    restart: \"Ask about something else\",\n    answerTitle: \"Start here\",\n  },\n  questions: [\n    {\n      name: \"area\",\n      title: \"What is this about?\",\n      description: \"Pick the closest one. Nothing here creates a ticket.\",\n      choices: [\n        {\n          value: \"billing\",\n          label: \"Billing and invoices\",\n          description: \"Charges, receipts, plans, and refunds.\",\n        },\n        {\n          value: \"access\",\n          label: \"Getting into the account\",\n          description: \"Passwords, devices, and team access.\",\n        },\n        {\n          value: \"data\",\n          label: \"Data going in or out\",\n          description: \"Imports, exports, and integrations.\",\n        },\n      ],\n    },\n    {\n      name: \"state\",\n      title: \"Where did it stop working?\",\n      choices: [\n        { value: \"before\", label: \"Before I could start\" },\n        { value: \"during\", label: \"Halfway through, with an error\" },\n        { value: \"after\", label: \"It finished, but the result is wrong\" },\n      ],\n    },\n  ],\n  answers: [\n    {\n      match: { area: \"billing\", state: \"after\" },\n      title: \"The invoice does not match what you expected\",\n      description:\n        \"Mid-cycle plan changes are prorated to the day, so the next invoice carries both the credit and the new rate. The breakdown on the invoice shows each line separately.\",\n      link: { label: \"Read how proration is calculated\", href: \"https://beste.co\" },\n      image: {\n        src: \"https://images.unsplash.com/photo-1554224155-6726b3ff858f?w=1200&h=1400&fit=crop\",\n        alt: \"Printed forms spread across a desk with a pen and a calculator\",\n      },\n      media: (\n        <Chat34\n          question=\"Why is this invoice higher than the plan price?\"\n          answer=\"You changed plan on the 14th, so this one carries nine days at the old rate and the rest at the new one.\"\n          sourcesLabel=\"Answered from\"\n          sources={[\"Invoices\", \"Proration\"]}\n        />\n      ),\n      caption: \"Every invoice line is itemised, including the credit from the day you switched.\",\n    },\n    {\n      match: { area: \"billing\" },\n      title: \"Payments, plans, and receipts\",\n      description:\n        \"Card updates, VAT details, and past receipts all live in the billing settings, and any admin on the workspace can reach them.\",\n      link: { label: \"Open the billing guide\", href: \"https://beste.co\" },\n    },\n    {\n      match: { area: \"access\", state: \"before\" },\n      title: \"You cannot get past the sign-in screen\",\n      description:\n        \"Reset links expire after fifteen minutes and only work once. If yours has run out, ask for a new one and open it in the same browser.\",\n      link: { label: \"Reset your password\", href: \"https://beste.co\" },\n      image: {\n        src: \"https://images.unsplash.com/photo-1555421689-d68471e189f2?w=1200&h=1400&fit=crop\",\n        alt: \"Somebody sitting at a desktop computer, waiting on the screen\",\n      },\n      media: (\n        <Chat34\n          question=\"My reset link says it has already been used.\"\n          answer=\"They last fifteen minutes and work once. Ask for a new one and open it in the browser you asked from.\"\n          sourcesLabel=\"Answered from\"\n          sources={[\"Sign-in\", \"Sessions\"]}\n        />\n      ),\n      caption: \"Reset links are single use, which is why the second click never works.\",\n    },\n    {\n      match: { area: \"access\" },\n      title: \"Access, devices, and who can see what\",\n      description:\n        \"Roles decide what each person can open. An admin can change a role at any time, and the change applies the next time that person loads the page.\",\n      link: { label: \"See the roles table\", href: \"https://beste.co\" },\n    },\n    {\n      match: { area: \"data\", state: \"during\" },\n      title: \"An import stopped partway\",\n      description:\n        \"Nothing is written until the whole file validates, so a failed import leaves your data untouched. The error names the first row it could not read.\",\n      link: { label: \"Fix a rejected import\", href: \"https://beste.co\" },\n      image: {\n        src: \"https://images.unsplash.com/photo-1543286386-713bdd548da4?w=1200&h=1400&fit=crop\",\n        alt: \"A line chart drawn on graph paper beside a ruler and two pens\",\n      },\n      media: (\n        <Chat34\n          question=\"The import stopped on row 412.\"\n          answer=\"Nothing was written. Row 412 carries a date in a format we do not read, and the whole file goes through once it is fixed.\"\n          sourcesLabel=\"Answered from\"\n          sources={[\"Imports\", \"Field mapping\"]}\n        />\n      ),\n      caption: \"A rejected file changes nothing. The row number in the error is where to look.\",\n    },\n    {\n      title: \"Moving data in and out\",\n      description:\n        \"Exports run in the background and arrive by email as a signed link. Imports are checked against your existing records before anything is created.\",\n      link: { label: \"Read the data guide\", href: \"https://beste.co\" },\n    },\n  ],\n  contact: {\n    title: \"Still not it?\",\n    description: \"Send us the answers you just gave and a sentence about what you expected.\",\n    link: { label: \"Write to support\", href: \"https://beste.co\" },\n  },\n};\n\nexport function Faq92({\n  badge,\n  heading,\n  description,\n  questions = [],\n  shortcuts,\n  answers = [],\n  image,\n  media,\n  caption,\n  contact,\n  labels = {},\n  className,\n}: Faq92Props) {\n  const {\n    previous: previousLabel,\n    next: nextLabel,\n    submit: submitLabel,\n    restart: restartLabel,\n    answerTitle,\n  } = labels;\n\n  const [answer, setAnswer] = useState<Answer | null>(null);\n\n  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    const data = new FormData(event.currentTarget);\n    const given: Record<string, string> = {};\n    for (const question of questions) {\n      given[question.name] = String(data.get(question.name) ?? \"\");\n    }\n\n    // The last entry stands in when nothing fits, so a submit never dead-ends.\n    const matched = answers.find((entry) =>\n      Object.entries(entry.match ?? {}).every(([name, value]) => given[name] === value)\n    );\n    setAnswer(matched ?? answers[answers.length - 1] ?? null);\n  };\n\n  const restart = () => setAnswer(null);\n\n  const shownImage = answer?.image ?? image;\n  const shownMedia = answer?.media ?? media;\n  const shownCaption = answer?.caption ?? caption;\n\n  return (\n    <section className={cn(\"w-full bg-background py-16 md:py-24\", className)}>\n      <div className=\"mx-auto max-w-6xl 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-12 grid gap-8 md:mt-16 lg:grid-cols-[minmax(0,1.1fr)_minmax(0,1fr)] lg:gap-12\">\n          <div>\n            {answer ? (\n              <>\n                {answerTitle && <p className=\"text-sm text-muted-foreground\">{answerTitle}</p>}\n                <p className=\"mt-3 text-2xl font-light tracking-tight text-foreground md:text-3xl\">\n                  {answer.title}\n                </p>\n                <p className=\"mt-4 text-base leading-relaxed text-muted-foreground\">\n                  {answer.description}\n                </p>\n\n                {answer.link && (\n                  <Link\n                    href={answer.link.href}\n                    className=\"group/faq92 mt-6 inline-flex items-center gap-2 border-b border-primary/40 pb-1 text-base text-foreground transition-colors hover:text-primary\"\n                  >\n                    {answer.link.label}\n                    <ArrowUpRight\n                      className=\"size-4 transition-transform motion-safe:group-hover/faq92:-translate-y-0.5 motion-safe:group-hover/faq92:translate-x-0.5\"\n                      aria-hidden=\"true\"\n                    />\n                  </Link>\n                )}\n\n                <div className=\"mt-8\">\n                  <Button21\n                    label={restartLabel ?? \"Ask about something else\"}\n                    tone=\"outline\"\n                    icon={RotateCcw}\n                    onClick={restart}\n                  />\n                </div>\n              </>\n            ) : (\n              <Questionnaire\n                defaultItem={questions[0]?.name}\n                shortcuts={shortcuts}\n                onSubmit={handleSubmit}\n                className=\"gap-6\"\n              >\n                {questions.length > 0 && (\n                  <QuestionnaireProgress className=\"text-sm font-normal tracking-normal\" />\n                )}\n\n                {questions.map((question, index) => (\n                  <QuestionnaireItem key={index} name={question.name} required>\n                    <QuestionnaireTitle className=\"text-xl font-medium text-foreground\">\n                      {question.title}\n                    </QuestionnaireTitle>\n                    {question.description && (\n                      <QuestionnaireDescription className=\"text-base leading-relaxed\">\n                        {question.description}\n                      </QuestionnaireDescription>\n                    )}\n                    <QuestionnaireChoices>\n                      {question.choices?.map((choice, choiceIndex) => (\n                        <QuestionnaireChoice\n                          key={choiceIndex}\n                          value={choice.value}\n                          className=\"rounded-md border-border p-4 text-base\"\n                        >\n                          <span className=\"font-medium text-foreground\">{choice.label}</span>\n                          {choice.description && (\n                            <QuestionnaireChoiceDescription className=\"text-sm\">\n                              {choice.description}\n                            </QuestionnaireChoiceDescription>\n                          )}\n                        </QuestionnaireChoice>\n                      ))}\n                    </QuestionnaireChoices>\n                    <QuestionnaireError className=\"text-sm\" />\n                  </QuestionnaireItem>\n                ))}\n\n                <QuestionnaireActions>\n                  <QuestionnairePrevious className=\"cursor-pointer rounded-md border-border text-sm font-medium\">\n                    {previousLabel}\n                  </QuestionnairePrevious>\n                  <QuestionnaireNext className=\"cursor-pointer rounded-md bg-primary text-sm font-medium text-primary-foreground hover:bg-primary/90\">\n                    {nextLabel}\n                  </QuestionnaireNext>\n                  <QuestionnaireSubmit className=\"cursor-pointer rounded-md bg-primary text-sm font-medium text-primary-foreground hover:bg-primary/90\">\n                    {submitLabel}\n                  </QuestionnaireSubmit>\n                </QuestionnaireActions>\n              </Questionnaire>\n            )}\n          </div>\n\n          <div>\n            <div className=\"relative flex h-72 items-center justify-center overflow-hidden rounded-md bg-muted lg:h-[24rem]\">\n              {shownImage && (\n                <img\n                  className=\"absolute inset-0 size-full object-cover\"\n                  src={shownImage.src}\n                  alt={shownImage.alt}\n                />\n              )}\n              <div className=\"relative z-10 size-full\">{shownMedia}</div>\n            </div>\n\n            {shownCaption && (\n              <p className=\"mt-5 border-t border-border pt-5 text-sm leading-relaxed text-muted-foreground\">\n                {shownCaption}\n              </p>\n            )}\n          </div>\n        </div>\n\n        {contact && (\n          <div className=\"mt-10 flex flex-col gap-4 rounded-md bg-muted p-6 sm:flex-row sm:items-center sm:justify-between md:p-8\">\n            <div className=\"max-w-xl\">\n              <p className=\"text-lg font-medium text-foreground\">{contact.title}</p>\n              <p className=\"mt-2 text-base leading-relaxed text-muted-foreground\">\n                {contact.description}\n              </p>\n            </div>\n            <Button21 asChild label={contact.link.label} tone=\"outline\">\n              <Link href={contact.link.href} />\n            </Button21>\n          </div>\n        )}\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/beste/block/faq92.tsx"
    },
    {
      "path": "registry-pieces/chat34/chat34.tsx",
      "content": "\"use client\";\n\nimport { Sparkles } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface Chat34Props {\n  question?: string;\n  answer?: string;\n  sourcesLabel?: string;\n  sources?: string[];\n  className?: string;\n}\n\nexport const chat34Demo: Chat34Props = {\n  question: \"How many rooms were unused last Tuesday?\",\n  answer:\n    \"Three, all at Kingsway between 13:00 and 16:00. Two were held for a clinic that was cancelled on the Friday before.\",\n  sourcesLabel: \"Answered from\",\n  sources: [\"Rota\", \"Bookings\", \"Cancellations\"],\n};\n\nexport function Chat34({ question, answer, sourcesLabel, sources = [], className }: Chat34Props) {\n  return (\n    <div className={cn(\"relative flex size-full items-center justify-center p-4\", className)}>\n      <div className=\"w-full max-w-96 rounded-md border border-border bg-card p-5 shadow-xl\">\n        {question && (\n          <p className=\"ml-auto w-fit max-w-[85%] rounded-md bg-muted px-3 py-2 text-sm text-card-foreground\">\n            {question}\n          </p>\n        )}\n\n        <div className=\"mt-3 flex gap-2.5\">\n          <span\n            className=\"flex size-7 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary\"\n            aria-hidden=\"true\"\n          >\n            <Sparkles className=\"size-3.5\" />\n          </span>\n          {answer && (\n            <p className=\"text-sm leading-relaxed text-card-foreground\">{answer}</p>\n          )}\n        </div>\n\n        {sources.length > 0 && (\n          <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-border pt-3\">\n            {sourcesLabel && (\n              <span className=\"text-sm text-muted-foreground\">{sourcesLabel}</span>\n            )}\n            {sources.map((source, index) => (\n              <span\n                key={index}\n                className=\"rounded-full border border-border px-2 py-0.5 text-xs text-muted-foreground\"\n              >\n                {source}\n              </span>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/beste/piece/chat34.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"
}