{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "error-1",
  "title": "Error 1: friendly 404, 403 and 500 page",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "An error page with giant fading numerals whose zero is a ring with an orbiting moon, plain-language copy per status, home and back buttons, site search and helpful links.",
  "dependencies": [
    "lucide-react",
    "motion@^12"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/button.json",
    "https://ui.ballmac.com/r/search-field.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/blocks/error-1/error-1.tsx",
      "content": "// Ballmac UI: Error 1. https://ui.ballmac.com/blocks/error-1\n\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowLeft, ArrowUpRight, BookOpen, Home, LifeBuoy, Tag } from \"lucide-react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { Button, buttonVariants } from \"@/components/ballmac/button\"\nimport { SearchField } from \"@/components/ballmac/search-field\"\nimport { cn } from \"@/lib/utils\"\n\ntype Error1Status = \"404\" | \"403\" | \"500\"\n\ntype Error1Link = { label: string; description?: string; href: string; icon?: React.ReactNode }\n\ntype Error1Props = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  /** Which error this is. Sets the numerals and the default words. */\n  status?: Error1Status\n  /** Overrides the default headline. */\n  title?: string\n  /** Overrides the default explanation. */\n  description?: string\n  /** Helpful places to go next. Pass an empty array to hide them. */\n  links?: Error1Link[]\n  /** Show a search field. Pass false to hide it. */\n  search?: boolean\n  /** Called with the query when the visitor searches. */\n  onSearch?: (query: string) => void\n  /** Where the primary button goes. */\n  homeHref?: string\n  /** Small reference line under the buttons, e.g. a request id to quote to support. */\n  reference?: string\n}\n\nconst copy: Record<Error1Status, { title: string; description: string }> = {\n  \"404\": { title: \"This page took a wrong turn.\", description: \"The page you are looking for has moved, been renamed or never existed. Try a search, or head to one of these.\" },\n  \"403\": { title: \"You don’t have access to this.\", description: \"Your account doesn’t have permission to view this page. If you think that is a mistake, ask the owner to invite you.\" },\n  \"500\": { title: \"Something broke on our side.\", description: \"We hit an unexpected error and have been notified. Try again in a moment; if it keeps happening, tell us what you were doing.\" },\n}\n\nconst defaultLinks: Error1Link[] = [\n  { label: \"Home\", description: \"Back to the start\", href: \"#\", icon: <Home /> },\n  { label: \"Documentation\", description: \"Guides and API reference\", href: \"#\", icon: <BookOpen /> },\n  { label: \"Pricing\", description: \"Plans for every team\", href: \"#\", icon: <Tag /> },\n  { label: \"Contact support\", description: \"A real person replies\", href: \"#\", icon: <LifeBuoy /> },\n]\n\n/** A zero drawn as a ring with a small moon in orbit. */\nfunction Ring() {\n  const reduce = useReducedMotion()\n  return (\n    <span aria-hidden=\"true\" className=\"relative inline-block size-[0.72em] align-[-0.02em]\">\n      <svg viewBox=\"0 0 100 100\" className=\"absolute inset-0 size-full\">\n        <circle cx=\"50\" cy=\"50\" r=\"42\" fill=\"none\" strokeWidth=\"13\" className=\"stroke-current\" />\n      </svg>\n      <motion.span\n        className=\"absolute inset-0\"\n        animate={reduce ? undefined : { rotate: 360 }}\n        transition={{ duration: 14, repeat: Infinity, ease: \"linear\" }}\n      >\n        <span className=\"bg-chart-1 absolute top-[-5%] left-1/2 size-[18%] -translate-x-1/2 rounded-full shadow-[0_0_0_0.04em_var(--background)]\" />\n      </motion.span>\n    </span>\n  )\n}\n\nfunction Error1({\n  status = \"404\",\n  title,\n  description,\n  links = defaultLinks,\n  search = true,\n  onSearch,\n  homeHref = \"#\",\n  reference,\n  className,\n  ...props\n}: Error1Props) {\n  const [query, setQuery] = React.useState(\"\")\n  const words = copy[status]\n  return (\n    <section data-slot=\"error-1\" className={cn(\"relative isolate mx-auto flex max-w-4xl overflow-x-clip flex-col items-center px-4 py-16 text-center sm:px-6 md:py-24\", className)} {...props}>\n      <div aria-hidden=\"true\" className=\"bg-chart-1/10 pointer-events-none absolute top-0 left-1/2 -z-10 size-[28rem] -translate-x-1/2 rounded-full blur-[110px]\" />\n\n      <p\n        aria-hidden=\"true\"\n        className=\"text-foreground [mask-image:linear-gradient(to_bottom,black_45%,transparent_98%)] text-[clamp(7rem,26vw,16rem)] leading-[0.9] font-semibold tracking-[-0.06em] tabular-nums select-none\"\n      >\n        {status.split(\"\").map((ch, i) => (ch === \"0\" ? <Ring key={i} /> : <span key={i}>{ch}</span>))}\n      </p>\n\n      <h1 className=\"-mt-2 text-3xl font-semibold tracking-[-0.035em] text-balance sm:text-5xl\">\n        <span className=\"sr-only\">Error {status}: </span>\n        {title ?? words.title}\n      </h1>\n      <p className=\"text-muted-foreground mt-4 max-w-lg text-lg text-pretty\">{description ?? words.description}</p>\n\n      <div className=\"mt-8 flex flex-wrap justify-center gap-3\">\n        <a className={buttonVariants({ size: \"lg\", shape: \"pill\" })} href={homeHref}>\n          <Home /> Take me home\n        </a>\n        <Button variant=\"outline\" size=\"lg\" shape=\"pill\" onClick={() => window.history.back()}>\n          <ArrowLeft  className=\"rtl:rotate-180\"/> Go back\n        </Button>\n      </div>\n      {reference && <p className=\"text-muted-foreground mt-6 font-mono text-xs\">Reference: {reference}</p>}\n\n      {search && (\n        <form\n          role=\"search\"\n          onSubmit={(e) => {\n            e.preventDefault()\n            if (query.trim()) onSearch?.(query.trim())\n          }}\n          className=\"mt-12 w-full max-w-md\"\n        >\n          <SearchField label=\"Search the site\" placeholder=\"Search the site\" value={query} onValueChange={setQuery} onSearch={(v) => v.trim() && onSearch?.(v.trim())} />\n        </form>\n      )}\n\n      {links.length > 0 && (\n        <ul className=\"mt-8 grid w-full gap-3 text-start sm:grid-cols-2\">\n          {links.map((l) => (\n            <li key={l.label}>\n              <a href={l.href} className=\"group/link hover:bg-accent focus-visible:ring-ring/50 flex h-full items-center gap-4 rounded-2xl border p-4 outline-none transition-colors focus-visible:ring-[3px]\">\n                <span aria-hidden=\"true\" className=\"bg-muted flex size-10 shrink-0 items-center justify-center rounded-xl [&_svg]:size-5\">{l.icon ?? <ArrowUpRight  className=\"rtl:-scale-x-100\"/>}</span>\n                <span className=\"min-w-0 flex-1\">\n                  <span className=\"block text-sm font-medium\">{l.label}</span>\n                  {l.description && <span className=\"text-muted-foreground block text-sm\">{l.description}</span>}\n                </span>\n                <ArrowUpRight className=\"text-muted-foreground size-4 shrink-0 transition-transform group-hover/link:translate-x-0.5 group-hover/link:-translate-y-0.5 motion-reduce:transition-none rtl:-scale-x-100 rtl:group-hover/link:-translate-x-0.5\" aria-hidden=\"true\" />\n              </a>\n            </li>\n          ))}\n        </ul>\n      )}\n    </section>\n  )\n}\n\nexport { Error1, type Error1Props, type Error1Link, type Error1Status }\n",
      "type": "registry:component",
      "target": "@components/ballmac/blocks/error-1/error-1.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "error",
      "404",
      "not found",
      "500",
      "403",
      "page"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "Drop into not-found.tsx or error.tsx. Set status ('404' | '403' | '500'); override title and description if you like; pass onSearch to wire the search box, links for next steps and reference for a request id.",
      "whenToUse": [
        "Next.js not-found.tsx and error.tsx",
        "Any missing, forbidden or failed page"
      ],
      "whenNotToUse": [
        "Inline errors inside a form or panel (use alert or empty-state)"
      ],
      "composesWith": [
        "header-2",
        "footer-2",
        "contact-1"
      ],
      "a11y": [
        {
          "keys": "Page title",
          "action": "The heading is prefixed with 'Error 404:' for screen readers; the giant numerals are decorative"
        },
        {
          "keys": "Enter in the search field",
          "action": "Calls onSearch with the query"
        }
      ],
      "customization": [
        "status picks the numerals and default copy",
        "links: [] hides the cards, search: false hides the field",
        "The orbiting moon stops for reduced motion"
      ]
    },
    "examples": [
      "error-1-demo",
      "error-1-server"
    ],
    "url": "https://ui.ballmac.com/components/error-1"
  },
  "categories": [
    "blocks",
    "error"
  ],
  "type": "registry:block"
}