{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "app-card",
  "title": "AppCard",
  "description": "Tenant dashboard grid card composing the status vocabulary: name, badges, timestamp, one-link hover.",
  "dependencies": [
    "@hugeicons/react",
    "@hugeicons/core-free-icons"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.neon.com/r/neon-tokens.json",
    "https://ui.neon.com/r/status-badge.json"
  ],
  "files": [
    {
      "path": "src/components/app-card/app-card.tsx",
      "content": "\"use client\";\n\nimport { ArrowUpRight01Icon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport type { ComponentProps, ReactNode } from \"react\";\n\nimport { PlanBadge, StatusBadge } from \"@/components/status-badge/status-badge\";\nimport type {\n  AppPlan,\n  AppStatus,\n} from \"@/components/status-badge/status-badge\";\nimport { cn } from \"@/lib/utils\";\n\nexport type AppCardProps = Omit<ComponentProps<\"a\">, \"children\"> & {\n  /** App name, shown in mono. */\n  name: string;\n  /** Lifecycle state, rendered as a StatusBadge. */\n  status: AppStatus;\n  /** Billing plan, rendered as a PlanBadge. */\n  plan?: AppPlan;\n  /** One-line summary of what the app does. */\n  description?: string;\n  /** Last activity, already formatted, e.g. \"2h ago\". */\n  updatedAt?: string;\n  /**\n   * Status-colored grain rising from the bottom edge. Pass a ReactNode\n   * (e.g. a Paper Shaders GrainGradient) to replace the built-in CSS grain\n   * inside the same positioned, status-tinted slot.\n   */\n  wash?: boolean | ReactNode;\n};\n\n/** Wash tint per status: color only ever comes from the vocabulary. */\nconst STATUS_WASH: Record<AppStatus, string> = {\n  error: \"text-destructive\",\n  provisioning: \"text-primary\",\n  ready: \"text-primary\",\n  stopped: \"text-muted-foreground\",\n};\n\n/* ─────────────────────────────────────────────────────────\n * The dashboard grid card, on MetricCard's shell: hairline\n * border warming on hover with a neon underline sweeping in\n * under the name (transform-only, no layout shift). The\n * corner arrow inks in alongside; the status vocabulary\n * anchors the foot with the timestamp opposite. The whole\n * card is one link.\n * ───────────────────────────────────────────────────────── */\nexport const AppCard = ({\n  className,\n  description,\n  name,\n  plan,\n  status,\n  updatedAt,\n  wash = true,\n  ...props\n}: AppCardProps) => (\n  <a\n    className={cn(\n      \"group relative isolate flex min-h-[128px] cursor-pointer select-none flex-col overflow-hidden rounded-lg border border-border/60 bg-card p-4 no-underline shadow-none ring-0 transition-colors hover:border-border focus-visible:border-primary focus-visible:outline-none\",\n      className\n    )}\n    data-slot=\"app-card\"\n    data-status={status}\n    {...props}\n  >\n    {wash === true ? (\n      <div\n        aria-hidden=\"true\"\n        className={cn(\n          \"neon-card-wash -z-10 pointer-events-none absolute inset-x-0 bottom-0 h-24 opacity-[0.07] transition-opacity duration-500 group-hover:opacity-[0.22]\",\n          STATUS_WASH[status]\n        )}\n      />\n    ) : null}\n    {wash && wash !== true ? (\n      <div\n        aria-hidden=\"true\"\n        className={cn(\n          \"-z-10 pointer-events-none absolute inset-x-0 bottom-0 h-24 overflow-hidden\",\n          STATUS_WASH[status]\n        )}\n        data-slot=\"app-card-wash\"\n      >\n        {wash}\n      </div>\n    ) : null}\n    <div className=\"flex items-center gap-1.5\">\n      <p\n        title={name}\n        className=\"relative min-w-0 truncate font-mono font-semibold text-foreground text-sm after:absolute after:inset-x-0 after:bottom-0 after:h-px after:origin-left after:scale-x-0 after:bg-primary after:transition-transform after:duration-300 after:ease-out group-hover:after:scale-x-100 motion-reduce:after:transition-none\"\n      >\n        {name}\n      </p>\n      <HugeiconsIcon\n        aria-hidden=\"true\"\n        icon={ArrowUpRight01Icon}\n        strokeWidth={2}\n        className=\"size-3.5 shrink-0 text-muted-foreground/40 transition-colors group-hover:text-foreground\"\n      />\n    </div>\n    {description ? (\n      <p className=\"mt-1.5 line-clamp-2 max-w-[48ch] text-pretty text-muted-foreground/80 text-xs leading-5\">\n        {description}\n      </p>\n    ) : null}\n    <div className=\"mt-auto flex items-center gap-1.5 pt-4\">\n      <StatusBadge status={status} />\n      {plan ? <PlanBadge plan={plan} /> : null}\n      {updatedAt ? (\n        <span className=\"ml-auto whitespace-nowrap font-mono text-[10px] text-muted-foreground/70 tabular-nums\">\n          {updatedAt}\n        </span>\n      ) : null}\n    </div>\n  </a>\n);\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}