{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bento-live",
  "type": "registry:component",
  "title": "Bento live",
  "description": "A React bento feature grid where each tile runs a live demo: a rolling MRR counter, a typed AI answer, ranked alerts and an orbiting integrations ring.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/number-roll.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/blocks/bento-live.tsx",
      "type": "registry:ui",
      "target": "components/ui/bento-live.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { motion, useInView, useReducedMotion } from \"motion/react\"\nimport { BellIcon, CheckIcon, SparklesIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\nimport { NumberRoll } from \"@/components/ui/number-roll\"\n\n/*\n * BentoLive: a feature grid where every tile runs a small piece of the real\n * product instead of a screenshot. Tiles rise in as the grid enters view and\n * lift toward the light on hover.\n */\n\ntype BentoItem = {\n  title: string\n  body: string\n  visual?: React.ReactNode\n  /** Column span on large screens (of 6). */\n  span?: 2 | 3 | 4 | 6\n  /** Row span on large screens. */\n  tall?: boolean\n}\n\ntype BentoLiveProps = {\n  eyebrow?: string\n  title?: React.ReactNode\n  description?: React.ReactNode\n  items?: BentoItem[]\n  className?: string\n}\n\n/* ── default visuals ───────────────────────────────────────────────────── */\nfunction LiveMetric() {\n  const [v, setV] = React.useState(48290)\n  React.useEffect(() => {\n    const t = setInterval(() => setV((x) => x + Math.round(40 + Math.random() * 300)), 2400)\n    return () => clearInterval(t)\n  }, [])\n  const bars = [38, 52, 44, 61, 58, 72, 66, 84, 78, 92]\n  return (\n    <div className=\"w-full\">\n      <p className=\"text-[12px] text-fg-muted\">MRR, live</p>\n      <p className=\"mt-1 text-[40px] leading-[44px] font-medium tracking-[-0.04em] text-fg\">\n        <NumberRoll value={v} trend format={{ style: \"currency\", currency: \"USD\", maximumFractionDigits: 0 }} />\n      </p>\n      <div className=\"mt-5 flex h-24 items-end gap-1.5\">\n        {bars.map((h, i) => (\n          <span key={i} className={cn(\"flex-1 rounded-t-md\", i === bars.length - 1 ? \"bg-accent\" : \"bg-accent/20\")} style={{ height: `${h}%` }} />\n        ))}\n      </div>\n    </div>\n  )\n}\n\nfunction LiveAsk() {\n  const answer = \"Churn rose 0.4% after the price change on the Starter plan. 31 accounts, mostly monthly. Want me to draft a win-back email?\"\n  const [n, setN] = React.useState(0)\n  const ref = React.useRef<HTMLDivElement>(null)\n  const inView = useInView(ref, { once: false, amount: 0.6 })\n  React.useEffect(() => {\n    if (!inView) return\n    setN(0)\n    const t = setInterval(() => setN((x) => (x >= answer.length ? x : x + 2)), 28)\n    return () => clearInterval(t)\n  }, [inView])\n  return (\n    <div ref={ref} className=\"w-full space-y-3\">\n      <div className=\"ml-auto w-fit max-w-[85%] rounded-2xl rounded-br-md bg-sunken px-3.5 py-2 text-[13px] text-fg\">Why did churn go up this week?</div>\n      <div className=\"flex gap-2.5\">\n        <span className=\"grid size-7 shrink-0 place-items-center rounded-full bg-accent text-on-accent\">\n          <SparklesIcon className=\"size-3.5\" />\n        </span>\n        <p className=\"min-h-[60px] text-[13px] leading-[1.55] text-fg\">\n          {answer.slice(0, n)}\n          {n < answer.length ? <span className=\"ml-0.5 inline-block h-[1em] w-px translate-y-[2px] animate-[caret-blink_1s_steps(1)_infinite] bg-fg\" /> : null}\n        </p>\n      </div>\n    </div>\n  )\n}\n\nfunction LiveAlerts() {\n  const items = [\n    { t: \"Stripe payout landed\", d: \"$12,400 · 2m ago\", tone: \"bg-success\" },\n    { t: \"Trial ending: Acme\", d: \"3 days left · 12 seats\", tone: \"bg-warning\" },\n    { t: \"Anomaly in refunds\", d: \"+220% vs last week\", tone: \"bg-danger\" },\n  ]\n  return (\n    <div className=\"w-full space-y-2\">\n      {items.map((it, i) => (\n        <div key={it.t} className=\"flex items-center gap-3 rounded-xl border border-border bg-raised px-3 py-2.5 shadow-raised\" style={{ marginLeft: i * 10, opacity: 1 - i * 0.12 }}>\n          <span className={cn(\"size-2 shrink-0 rounded-full\", it.tone)} />\n          <div className=\"min-w-0 flex-1\">\n            <p className=\"truncate text-[12.5px] font-medium text-fg\">{it.t}</p>\n            <p className=\"truncate text-[11.5px] text-fg-muted\">{it.d}</p>\n          </div>\n          <BellIcon className=\"size-3.5 text-fg-subtle\" />\n        </div>\n      ))}\n    </div>\n  )\n}\n\nfunction LiveSources() {\n  const names = [\"Stripe\", \"HubSpot\", \"Postgres\", \"Slack\", \"Sheets\", \"Linear\"]\n  return (\n    <div className=\"relative mx-auto grid size-52 place-items-center\">\n      <span className=\"absolute inset-0 rounded-full border border-dashed border-border-strong\" />\n      <span className=\"absolute inset-8 rounded-full border border-border\" />\n      <span className=\"relative z-10 grid size-14 place-items-center rounded-2xl bg-ink text-on-ink shadow-ink\">\n        <SparklesIcon className=\"size-5\" />\n      </span>\n      <div className=\"absolute inset-0 animate-[spin_40s_linear_infinite] motion-reduce:animate-none\">\n        {names.map((n, i) => {\n          const a = (i / names.length) * Math.PI * 2\n          return (\n            <span\n              key={n}\n              className=\"absolute grid h-7 place-items-center rounded-full border border-border bg-raised px-2.5 text-[11px] font-medium whitespace-nowrap text-fg shadow-key\"\n              style={{ left: `calc(50% + ${Math.cos(a) * 104}px)`, top: `calc(50% + ${Math.sin(a) * 104}px)`, transform: \"translate(-50%,-50%)\" }}\n            >\n              <span className=\"inline-block animate-[spin_40s_linear_infinite_reverse] motion-reduce:animate-none\">{n}</span>\n            </span>\n          )\n        })}\n      </div>\n    </div>\n  )\n}\n\nfunction LiveChecks() {\n  const rows = [\"Row-level permissions\", \"SSO and SCIM\", \"Audit log on every answer\", \"EU or US data residency\"]\n  return (\n    <ul className=\"w-full space-y-2.5\">\n      {rows.map((r) => (\n        <li key={r} className=\"flex items-center gap-2.5 text-[13px] text-fg\">\n          <span className=\"grid size-5 place-items-center rounded-full bg-accent-soft text-accent-fg\">\n            <CheckIcon className=\"size-3\" strokeWidth={3} />\n          </span>\n          {r}\n        </li>\n      ))}\n    </ul>\n  )\n}\n\nconst DEFAULT_ITEMS: BentoItem[] = [\n  { title: \"Numbers that move when the business does\", body: \"Every metric is live. No refresh button, no stale Monday report.\", visual: <LiveMetric />, span: 4, tall: true },\n  { title: \"Ask in plain English\", body: \"Lumen reads your data and answers with the reason, not just the number.\", visual: <LiveAsk />, span: 2, tall: true },\n  { title: \"Alerts that matter\", body: \"Payouts, trials, anomalies. Ranked by money at stake.\", visual: <LiveAlerts />, span: 2 },\n  { title: \"Plugs into what you run\", body: \"Two-click connections to the tools your team already pays for.\", visual: <LiveSources />, span: 2 },\n  { title: \"Enterprise from day one\", body: \"The controls your security review asks about, already on.\", visual: <LiveChecks />, span: 2 },\n]\n\nconst SPAN: Record<number, string> = { 2: \"lg:col-span-2\", 3: \"lg:col-span-3\", 4: \"lg:col-span-4\", 6: \"lg:col-span-6\" }\n\nfunction BentoLive({\n  eyebrow = \"Product\",\n  title = \"Everything your finance team checks, checked for them.\",\n  description,\n  items = DEFAULT_ITEMS,\n  className,\n}: BentoLiveProps) {\n  const reduce = useReducedMotion()\n  return (\n    <section data-slot=\"bento-live\" className={cn(\"mx-auto max-w-7xl px-4 py-24 sm:px-6 sm:py-32 lg:px-8\", className)}>\n      <div className=\"max-w-2xl\">\n        {eyebrow ? <p className=\"font-mono text-[11px] tracking-[0.08em] text-accent-fg uppercase\">{eyebrow}</p> : null}\n        <h2 className=\"mt-3 text-4xl leading-[1.05] font-medium tracking-[-0.04em] text-balance text-fg sm:text-5xl\">{title}</h2>\n        {description ? <p className=\"mt-4 text-[1.0625rem] leading-[1.65] text-fg-muted\">{description}</p> : null}\n      </div>\n      <div className=\"mt-12 grid gap-4 sm:grid-cols-2 lg:auto-rows-[minmax(260px,auto)] lg:grid-cols-6\">\n        {items.map((it, i) => (\n          <motion.article\n            key={it.title}\n            initial={reduce ? false : { opacity: 0, y: 24 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true, amount: 0.3 }}\n            transition={{ duration: 0.7, delay: (i % 3) * 0.08, ease: [0.2, 0, 0, 1] }}\n            className={cn(\n              \"group/tile relative flex flex-col overflow-hidden rounded-3xl border border-border bg-surface shadow-raised\",\n              \"transition-[translate,box-shadow] duration-300 ease-hairline hover:-translate-y-1 hover:shadow-lg\",\n              SPAN[it.span ?? 2],\n              it.tall && \"lg:row-span-2\",\n              (it.span ?? 2) >= 4 && \"sm:col-span-2\"\n            )}\n          >\n            <div className=\"flex flex-1 items-center p-6 sm:p-7\">{it.visual}</div>\n            <div className=\"border-t border-border px-6 py-5 sm:px-7\">\n              <h3 className=\"text-[15px] font-medium tracking-[-0.01em] text-fg\">{it.title}</h3>\n              <p className=\"mt-1 text-[13.5px] leading-[1.55] text-fg-muted\">{it.body}</p>\n            </div>\n          </motion.article>\n        ))}\n      </div>\n    </section>\n  )\n}\n\nexport { BentoLive }\nexport type { BentoLiveProps, BentoItem }\n"
    }
  ]
}
