{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "marketing-feature-bento",
  "title": "Marketing feature bento",
  "description": "Marketing feature bento is a production-ready blocks component from BJORK UI, packaged for shadcn registries.",
  "dependencies": [
    "clsx",
    "framer-motion",
    "lucide-react",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "components/bjork-ui/blocks/marketing-feature-bento.tsx",
      "content": "\"use client\";\n\nimport { Fragment, useId, type ReactNode } from \"react\";\nimport { motion } from \"framer-motion\";\nimport {\n  blockFrame,\n  blockToneProps,\n  CtaLink,\n  Reveal,\n  SectionHeader,\n  useBlockReducedMotion,\n  type BlockLink,\n  type BlockTone,\n} from \"@/components/bjork-ui/blocks/marketing-kit\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface BentoSpan {\n  label: string;\n  /** Start offset, in the same unit as `total`. */\n  start: number;\n  duration: number;\n  /** Marks the span as the slow one: accent bar and a duration tag. */\n  slow?: boolean;\n}\n\nexport type BentoVisual =\n  | { type: \"waterfall\"; spans: BentoSpan[]; total: number; unit?: string }\n  | { type: \"replay\"; markers: number[]; position: number; timecode: string; label: string }\n  | { type: \"alerts\"; alerts: { title: string; meta: string }[] }\n  | { type: \"cost\"; value: string; caption: string; bars: number[]; highlight?: number }\n  | { type: \"code\"; filename: string; lines: string[] }\n  | { type: \"keys\"; shortcuts: { keys: string[]; label: string }[] }\n  | { type: \"redact\"; segments: (string | { redacted: string })[]; badge: string }\n  | { type: \"custom\"; node: ReactNode };\n\nexport interface BentoFeature {\n  id: string;\n  title: string;\n  description: string;\n  visual: BentoVisual;\n  /** Width on the six-column grid: sm is a third, md a half, lg two thirds, full the whole row. */\n  size?: \"sm\" | \"md\" | \"lg\" | \"full\";\n  /** Turns the whole card into a link. */\n  href?: string;\n}\n\nexport interface MarketingFeatureBentoProps {\n  eyebrow?: string;\n  title: ReactNode;\n  description?: ReactNode;\n  cta?: BlockLink;\n  features: BentoFeature[];\n  tone?: BlockTone;\n  /** Play the in-view motion. Turn off for screenshots. */\n  animate?: boolean;\n  className?: string;\n}\n\nexport const MARKETING_BENTO_SAMPLE: MarketingFeatureBentoProps = {\n  eyebrow: \"Platform\",\n  title: \"Everything between the prompt and the answer.\",\n  description:\n    \"One trace per run, from the first token to the last tool call. Find the slow step, replay it, and fix it before the next deploy.\",\n  cta: { label: \"Explore the platform\", href: \"#platform\" },\n  features: [\n    {\n      id: \"traces\",\n      size: \"lg\",\n      title: \"Live traces, span by span\",\n      description: \"Every model call, tool and retry lands on one timeline while the run is still going.\",\n      visual: {\n        type: \"waterfall\",\n        total: 10,\n        unit: \"s\",\n        spans: [\n          { label: \"read_request\", start: 0, duration: 0.7 },\n          { label: \"lookup_order\", start: 0.7, duration: 1.2 },\n          { label: \"search_policy\", start: 1.9, duration: 1.6 },\n          { label: \"issue_refund\", start: 3.5, duration: 4.6, slow: true },\n          { label: \"draft_reply\", start: 8.1, duration: 1.2 },\n          { label: \"send_reply\", start: 9.3, duration: 0.6 },\n        ],\n      },\n    },\n    {\n      id: \"replay\",\n      size: \"sm\",\n      title: \"Replay from any step\",\n      description: \"Rewind a failed run to the step before it broke and run it again with a new prompt.\",\n      visual: { type: \"replay\", markers: [0, 0.08, 0.2, 0.36, 0.82, 0.94], position: 0.36, timecode: \"00:03.5\", label: \"issue_refund\" },\n    },\n    {\n      id: \"alerts\",\n      size: \"sm\",\n      title: \"Alerts that read like sentences\",\n      description: \"Thresholds on any span, routed to the people who own it.\",\n      visual: {\n        type: \"alerts\",\n        alerts: [\n          { title: \"issue_refund p95 crossed 4s\", meta: \"support-agent · 2m ago\" },\n          { title: \"Retry rate up 3x on lookup_order\", meta: \"support-agent · 18m ago\" },\n          { title: \"Token spend 40% over budget\", meta: \"research-agent · 1h ago\" },\n        ],\n      },\n    },\n    {\n      id: \"cost\",\n      size: \"sm\",\n      title: \"Cost per run, not per month\",\n      description: \"Tokens and tool fees priced on every trace, so a costly prompt shows up the day it ships.\",\n      visual: {\n        type: \"cost\",\n        value: \"$0.031\",\n        caption: \"median cost per run\",\n        bars: [3, 5, 8, 12, 15, 13, 9, 6, 4, 3, 2, 5],\n        highlight: 11,\n      },\n    },\n    {\n      id: \"install\",\n      size: \"sm\",\n      title: \"Two lines to install\",\n      description: \"Wraps the SDK you already use. OpenTelemetry in, OpenTelemetry out.\",\n      visual: {\n        type: \"code\",\n        filename: \"agent.ts\",\n        // The quote is escaped so registry tooling does not read this sample line as a real import.\n        lines: [\"import { trace } from \\u0022@tracewell/sdk\\u0022;\", \"\", 'trace.init({ project: \"support\" });'],\n      },\n    },\n    {\n      id: \"keys\",\n      size: \"md\",\n      title: \"Built for the keyboard\",\n      description: \"Jump between runs and steps without reaching for the mouse.\",\n      visual: {\n        type: \"keys\",\n        shortcuts: [\n          { keys: [\"⌘\", \"K\"], label: \"Jump to a run\" },\n          { keys: [\"J\"], label: \"Next step\" },\n          { keys: [\"K\"], label: \"Previous step\" },\n          { keys: [\"R\"], label: \"Replay from here\" },\n        ],\n      },\n    },\n    {\n      id: \"private\",\n      size: \"md\",\n      title: \"Private by default\",\n      description: \"Emails, card numbers and keys are masked in the SDK, before anything leaves your servers.\",\n      visual: {\n        type: \"redact\",\n        badge: \"3 fields masked\",\n        segments: [\n          \"Customer \",\n          { redacted: \"maya.okafor@\" },\n          \" asked to refund the order paid with the card ending \",\n          { redacted: \"4242\" },\n          \", and to ship the replacement to \",\n          { redacted: \"18 Alder Row\" },\n          \".\",\n        ],\n      },\n    },\n  ],\n};\n\nconst SIZE_CLASS: Record<NonNullable<BentoFeature[\"size\"]>, string> = {\n  sm: \"@2xl:col-span-1 @5xl:col-span-2\",\n  md: \"@2xl:col-span-1 @5xl:col-span-3\",\n  lg: \"@2xl:col-span-2 @5xl:col-span-4\",\n  full: \"@2xl:col-span-2 @5xl:col-span-6\",\n};\n\n/**\n * A feature section on a bento grid. Each card carries a small working illustration (trace waterfall,\n * replay scrubber, alert stack, cost histogram, install snippet, shortcuts, redaction) built from the\n * content you pass, so the grid explains the product instead of decorating it.\n */\nexport function MarketingFeatureBento({\n  eyebrow,\n  title,\n  description,\n  cta,\n  features,\n  tone = \"auto\",\n  animate = true,\n  className,\n}: MarketingFeatureBentoProps) {\n  const titleId = useId();\n  const toneProps = blockToneProps(tone);\n\n  return (\n    <section\n      aria-labelledby={titleId}\n      {...toneProps}\n      className={cn(\n        \"@container w-full bg-[var(--bjork-bg)] py-20 font-bjork-alpha text-[color:var(--bjork-text)] @3xl:py-28\",\n        toneProps.className,\n        className,\n      )}\n    >\n      <div className={blockFrame}>\n        <div className=\"grid gap-6 @4xl:grid-cols-[minmax(0,1.25fr)_minmax(0,1fr)] @4xl:items-end @4xl:gap-16\">\n          <SectionHeader id={titleId} eyebrow={eyebrow} title={title} />\n          <div className=\"flex flex-col items-start gap-6\">\n            {description ? (\n              <p className=\"max-w-[46ch] text-pretty text-[16px] leading-7 text-[color:var(--bjork-text-medium)] @xl:text-[17px]\">\n                {description}\n              </p>\n            ) : null}\n            {cta ? <CtaLink link={cta} variant=\"secondary\" arrow /> : null}\n          </div>\n        </div>\n\n        <ul className=\"mt-12 grid grid-cols-1 gap-3 @2xl:grid-cols-2 @3xl:mt-16 @5xl:grid-cols-6\">\n          {features.map((feature, index) => (\n            <FeatureCard key={feature.id} feature={feature} index={index} animate={animate} />\n          ))}\n        </ul>\n      </div>\n    </section>\n  );\n}\n\nfunction FeatureCard({ feature, index, animate }: { feature: BentoFeature; index: number; animate: boolean }) {\n  const titleId = useId();\n  const body = (\n    <>\n      <div aria-hidden className=\"relative flex min-h-[190px] flex-1 items-center justify-center overflow-hidden px-5 pt-5 @xl:min-h-[210px]\">\n        <Visual visual={feature.visual} animate={animate} />\n      </div>\n      <div className=\"px-6 pb-6 pt-5\">\n        <h3 id={titleId} className=\"text-[16px] font-semibold tracking-[-0.02em] text-[color:var(--bjork-text)]\">\n          {feature.title}\n        </h3>\n        <p className=\"mt-1.5 max-w-[48ch] text-[14px] leading-6 text-[color:var(--bjork-text-muted)]\">\n          {feature.description}\n        </p>\n      </div>\n    </>\n  );\n\n  const shell =\n    \"group/card relative flex h-full flex-col overflow-hidden rounded-[22px] border border-[color:var(--bjork-border)] bg-[var(--bjork-surface)] shadow-[var(--bjork-shadow-panel)] transition-colors duration-200\";\n\n  const content = feature.href ? (\n    <a\n      href={feature.href}\n      aria-labelledby={titleId}\n      className={cn(\n        shell,\n        \"hover:border-[color:var(--bjork-border-strong)] outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--bjork-accent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[color:var(--bjork-ring-offset)]\",\n      )}\n    >\n      {body}\n    </a>\n  ) : (\n    <article aria-labelledby={titleId} className={shell}>\n      {body}\n    </article>\n  );\n\n  const size = SIZE_CLASS[feature.size ?? \"sm\"];\n  if (!animate) return <li className={size}>{content}</li>;\n  return (\n    <Reveal as=\"li\" delay={Math.min(index, 4) * 0.05} className={size}>\n      {content}\n    </Reveal>\n  );\n}\n\nfunction Visual({ visual, animate }: { visual: BentoVisual; animate: boolean }) {\n  switch (visual.type) {\n    case \"waterfall\":\n      return <Waterfall {...visual} animate={animate} />;\n    case \"replay\":\n      return <Replay {...visual} />;\n    case \"alerts\":\n      return <Alerts alerts={visual.alerts} />;\n    case \"cost\":\n      return <Cost {...visual} animate={animate} />;\n    case \"code\":\n      return <Code filename={visual.filename} lines={visual.lines} />;\n    case \"keys\":\n      return <Keys shortcuts={visual.shortcuts} />;\n    case \"redact\":\n      return <Redact segments={visual.segments} badge={visual.badge} />;\n    case \"custom\":\n      return <>{visual.node}</>;\n  }\n}\n\n/* Visuals. All decorative (the card hides them from assistive tech); the card text carries the meaning. */\n\nfunction useGrow(animate: boolean) {\n  const reduce = useBlockReducedMotion();\n  return (delay: number) =>\n    animate && !reduce\n      ? {\n          initial: { scaleX: 0 },\n          whileInView: { scaleX: 1 },\n          viewport: { once: true, amount: 0.6 },\n          transition: { type: \"spring\" as const, stiffness: 180, damping: 26, mass: 0.9, delay },\n        }\n      : {};\n}\n\nfunction Waterfall({ spans, total, unit = \"s\", animate }: { spans: BentoSpan[]; total: number; unit?: string; animate: boolean }) {\n  const grow = useGrow(animate);\n  const ticks = [0, 0.25, 0.5, 0.75, 1];\n  return (\n    <div className=\"w-full max-w-[640px] self-stretch rounded-[14px] border border-[color:var(--bjork-border)] bg-[var(--bjork-panel)] p-4\">\n      <div className=\"grid grid-cols-[96px_minmax(0,1fr)] gap-x-3 @md:grid-cols-[116px_minmax(0,1fr)]\">\n        <span />\n        <div className=\"relative mb-2 h-4 font-mono text-[11px] text-[color:var(--bjork-text-soft)]\">\n          {ticks.map((tick) => (\n            <span\n              key={tick}\n              className=\"absolute top-0 -translate-x-1/2 tabular-nums first:translate-x-0 last:-translate-x-full\"\n              style={{ left: `${tick * 100}%` }}\n            >\n              {(tick * total).toFixed(tick * total % 1 ? 1 : 0)}\n              {unit}\n            </span>\n          ))}\n        </div>\n        {spans.map((span, index) => (\n          <Fragment key={span.label}>\n            <span\n              className={cn(\n                \"truncate py-[5px] font-mono text-[11px] leading-4\",\n                span.slow ? \"text-[color:var(--bjork-text)]\" : \"text-[color:var(--bjork-text-muted)]\",\n              )}\n            >\n              {span.label}\n            </span>\n            <div className=\"relative py-[5px]\">\n              <div\n                className=\"absolute inset-0\"\n                style={{\n                  backgroundImage: \"linear-gradient(to right, var(--bjork-border) 1px, transparent 1px)\",\n                  backgroundSize: \"25% 100%\",\n                }}\n              />\n              <div className=\"relative h-4\" style={{ marginLeft: `${(span.start / total) * 100}%`, width: `${(span.duration / total) * 100}%` }}>\n                <motion.div\n                  {...grow(0.1 + index * 0.08)}\n                  className={cn(\n                    \"h-full origin-left rounded-[4px]\",\n                    span.slow\n                      ? \"bg-[color:var(--bjork-accent)] shadow-[0_0_0_3px_var(--bjork-accent-soft)]\"\n                      : \"bg-[color:var(--bjork-text-faint)] transition-colors duration-200 group-hover/card:bg-[color:var(--bjork-text-soft)]\",\n                  )}\n                />\n                {span.slow ? (\n                  <span className=\"absolute left-full top-1/2 ml-2 -translate-y-1/2 whitespace-nowrap rounded-full bg-[var(--bjork-accent-soft)] px-1.5 py-px font-mono text-[10px] tabular-nums text-[color:var(--bjork-accent)]\">\n                    {span.duration}\n                    {unit}\n                  </span>\n                ) : null}\n              </div>\n            </div>\n          </Fragment>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nfunction Replay({ markers, position, timecode, label }: { markers: number[]; position: number; timecode: string; label: string }) {\n  return (\n    <div className=\"w-full max-w-[300px]\">\n      <div className=\"mb-4 flex items-center justify-between\">\n        <span className=\"flex items-center gap-2 rounded-full border border-[color:var(--bjork-border)] bg-[var(--bjork-panel)] py-1 pl-1 pr-2.5 font-mono text-[11px] text-[color:var(--bjork-text-medium)]\">\n          <span className=\"grid size-5 place-items-center rounded-full bg-[color:var(--bjork-text)] text-[color:var(--bjork-bg)]\">\n            <svg viewBox=\"0 0 10 10\" className=\"ml-px size-2.5\">\n              <path d=\"M2.5 1.5v7l6-3.5z\" fill=\"currentColor\" />\n            </svg>\n          </span>\n          Replay\n        </span>\n        <span className=\"font-mono text-[11px] tabular-nums text-[color:var(--bjork-text-muted)]\">{timecode}</span>\n      </div>\n      <div className=\"relative h-10\">\n        <div className=\"absolute inset-x-0 top-1/2 h-[3px] -translate-y-1/2 rounded-full bg-[color:var(--bjork-border-strong)]\" />\n        <div\n          className=\"absolute left-0 top-1/2 h-[3px] -translate-y-1/2 rounded-full bg-[color:var(--bjork-accent)]\"\n          style={{ width: `${position * 100}%` }}\n        />\n        {markers.map((marker) => (\n          <span\n            key={marker}\n            className={cn(\n              \"absolute top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-[color:var(--bjork-surface)]\",\n              marker <= position ? \"bg-[color:var(--bjork-accent)]\" : \"bg-[color:var(--bjork-text-faint)]\",\n            )}\n            style={{ left: `${marker * 100}%` }}\n          />\n        ))}\n        <div\n          className=\"absolute top-0 h-full transition-transform duration-500 ease-[cubic-bezier(0.77,0,0.175,1)] group-hover/card:translate-x-3 motion-reduce:transition-none\"\n          style={{ left: `${position * 100}%` }}\n        >\n          <span className=\"absolute left-0 top-0 h-full w-[2px] -translate-x-1/2 rounded-full bg-[color:var(--bjork-text)]\" />\n          <span className=\"absolute -top-6 left-0 -translate-x-1/2 whitespace-nowrap rounded-[6px] bg-[color:var(--bjork-text)] px-1.5 py-0.5 font-mono text-[11px] text-[color:var(--bjork-bg)]\">\n            {label}\n          </span>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction Alerts({ alerts }: { alerts: { title: string; meta: string }[] }) {\n  const stack = alerts.slice(0, 3);\n  return (\n    <div className=\"relative h-[104px] w-full max-w-[300px]\">\n      {stack.map((alert, index) => {\n        const depth = index;\n        return (\n          <div\n            key={alert.title}\n            className=\"absolute inset-x-0 top-0 rounded-[14px] border border-[color:var(--bjork-border)] bg-[var(--bjork-surface)] p-3 shadow-[var(--bjork-shadow-menu)] transition-transform duration-300 ease-[cubic-bezier(0.23,1,0.32,1)] [transform:translateY(calc(var(--depth)*14px))_scale(calc(1-var(--depth)*0.06))] group-hover/card:[transform:translateY(calc(var(--depth)*40px))_scale(calc(1-var(--depth)*0.03))] motion-reduce:transition-none\"\n            style={{\n              zIndex: stack.length - depth,\n              opacity: 1 - depth * 0.2,\n              background: depth === 0 ? undefined : \"var(--bjork-surface-active)\",\n              [\"--depth\" as string]: depth,\n            }}\n          >\n            <div\n              className={cn(\n                \"flex items-start gap-2.5\",\n                depth > 0 &&\n                  \"opacity-0 transition-opacity duration-300 group-hover/card:opacity-100 motion-reduce:transition-none\",\n              )}\n            >\n              <span\n                className={cn(\n                  \"mt-1 size-2 shrink-0 rounded-full\",\n                  depth === 0 ? \"bg-[color:var(--bjork-accent)]\" : \"bg-[color:var(--bjork-text-faint)]\",\n                )}\n              />\n              <span className=\"min-w-0\">\n                <span className=\"block truncate text-[12.5px] font-medium text-[color:var(--bjork-text)]\">{alert.title}</span>\n                <span className=\"block truncate font-mono text-[11px] text-[color:var(--bjork-text-soft)]\">{alert.meta}</span>\n              </span>\n            </div>\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\nfunction Cost({ value, caption, bars, highlight, animate }: { value: string; caption: string; bars: number[]; highlight?: number; animate: boolean }) {\n  const reduce = useBlockReducedMotion();\n  const max = Math.max(...bars);\n  return (\n    <div className=\"flex w-full max-w-[300px] flex-col gap-4\">\n      <div>\n        <p className=\"font-bjork-display text-[40px] font-semibold leading-none tracking-[-0.04em] tabular-nums text-[color:var(--bjork-text)]\">\n          {value}\n        </p>\n        <p className=\"mt-1.5 font-mono text-[11px] text-[color:var(--bjork-text-soft)]\">{caption}</p>\n      </div>\n      <div className=\"flex h-14 items-end gap-[3px]\">\n        {bars.map((bar, index) => (\n          <motion.span\n            key={index}\n            initial={animate && !reduce ? { scaleY: 0 } : false}\n            whileInView={{ scaleY: 1 }}\n            viewport={{ once: true, amount: 0.8 }}\n            transition={{ type: \"spring\", stiffness: 200, damping: 24, delay: 0.1 + index * 0.03 }}\n            className={cn(\n              \"flex-1 origin-bottom rounded-t-[3px]\",\n              index === highlight ? \"bg-[color:var(--bjork-accent)]\" : \"bg-[color:var(--bjork-border-strong)]\",\n            )}\n            style={{ height: `${(bar / max) * 100}%` }}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n\nconst CODE_TOKEN = /(\"[^\"]*\"|\\b(?:import|from|const|await|export)\\b)/g;\n\nfunction Code({ filename, lines }: { filename: string; lines: string[] }) {\n  return (\n    <div className=\"w-full max-w-[320px] overflow-hidden rounded-[14px] border border-[color:var(--bjork-border)] bg-[var(--bjork-panel)]\">\n      <div className=\"flex items-center justify-between border-b border-[color:var(--bjork-border)] px-3 py-2\">\n        <span className=\"font-mono text-[11px] text-[color:var(--bjork-text-soft)]\">{filename}</span>\n        <span className=\"flex gap-1\">\n          <span className=\"size-1.5 rounded-full bg-[color:var(--bjork-border-strong)]\" />\n          <span className=\"size-1.5 rounded-full bg-[color:var(--bjork-border-strong)]\" />\n        </span>\n      </div>\n      <pre className=\"overflow-hidden px-3 py-3 font-mono text-[11px] leading-[1.7] text-[color:var(--bjork-text-medium)]\">\n        {lines.map((line, index) => (\n          <div key={index} className=\"flex gap-3\">\n            <span className=\"w-3 select-none text-right tabular-nums text-[color:var(--bjork-text-faint)]\">{index + 1}</span>\n            <span className=\"whitespace-pre\">\n              {line.split(CODE_TOKEN).map((part, partIndex) =>\n                part.startsWith('\"') ? (\n                  <span key={partIndex} className=\"text-[color:var(--bjork-accent)]\">\n                    {part}\n                  </span>\n                ) : /^(import|from|const|await|export)$/.test(part) ? (\n                  <span key={partIndex} className=\"text-[color:var(--bjork-text-soft)]\">\n                    {part}\n                  </span>\n                ) : (\n                  <span key={partIndex}>{part}</span>\n                ),\n              )}\n            </span>\n          </div>\n        ))}\n      </pre>\n    </div>\n  );\n}\n\nfunction Keys({ shortcuts }: { shortcuts: { keys: string[]; label: string }[] }) {\n  return (\n    <div className=\"grid w-full max-w-[420px] grid-cols-1 gap-2 @5xl:grid-cols-2\">\n      {shortcuts.map((shortcut, index) => (\n        <div\n          key={shortcut.label}\n          className=\"flex items-center justify-between gap-3 rounded-[12px] border border-[color:var(--bjork-border)] bg-[var(--bjork-panel)] px-3 py-2.5\"\n        >\n          <span className=\"truncate text-[12.5px] text-[color:var(--bjork-text-medium)]\">{shortcut.label}</span>\n          <span className=\"flex gap-1\">\n            {shortcut.keys.map((key) => (\n              <kbd\n                key={key}\n                className={cn(\n                  \"grid h-6 min-w-6 place-items-center rounded-[6px] border border-b-2 border-[color:var(--bjork-border-strong)] bg-[var(--bjork-surface)] px-1.5 font-mono text-[11px] text-[color:var(--bjork-text)] transition-transform duration-150 motion-reduce:transition-none\",\n                  index === 0 && \"group-hover/card:translate-y-px group-hover/card:border-b\",\n                )}\n              >\n                {key}\n              </kbd>\n            ))}\n          </span>\n        </div>\n      ))}\n    </div>\n  );\n}\n\nfunction Redact({ segments, badge }: { segments: (string | { redacted: string })[]; badge: string }) {\n  return (\n    <div className=\"w-full max-w-[440px] rounded-[14px] border border-[color:var(--bjork-border)] bg-[var(--bjork-panel)] p-4\">\n      <div className=\"mb-3 flex items-center justify-between\">\n        <span className=\"font-mono text-[10px] uppercase tracking-[0.12em] text-[color:var(--bjork-text-soft)]\">span input</span>\n        <span className=\"flex items-center gap-1.5 rounded-full bg-[var(--bjork-accent-soft)] px-2 py-0.5 font-mono text-[11px] text-[color:var(--bjork-accent)]\">\n          <svg viewBox=\"0 0 12 12\" className=\"size-3\">\n            <rect x=\"2.5\" y=\"5.5\" width=\"7\" height=\"5\" rx=\"1.2\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.2\" />\n            <path d=\"M4 5.5V4a2 2 0 0 1 4 0v1.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.2\" />\n          </svg>\n          {badge}\n        </span>\n      </div>\n      <p className=\"text-[13px] leading-[1.9] text-[color:var(--bjork-text-medium)]\">\n        {segments.map((segment, index) =>\n          typeof segment === \"string\" ? (\n            <span key={index}>{segment}</span>\n          ) : (\n            <span\n              key={index}\n              className=\"relative mx-px inline-block rounded-[4px] align-middle leading-none\"\n            >\n              <span className=\"invisible px-1 font-mono text-[12px]\">{segment.redacted}</span>\n              <span\n                className=\"absolute inset-0 rounded-[4px] bg-[color:var(--bjork-text)] opacity-85\"\n                style={{\n                  backgroundImage:\n                    \"repeating-linear-gradient(135deg, transparent 0 4px, color-mix(in srgb, var(--bjork-bg) 18%, transparent) 4px 5px)\",\n                }}\n              />\n            </span>\n          ),\n        )}\n      </p>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/blocks/marketing-feature-bento.tsx"
    },
    {
      "path": "components/bjork-ui/blocks/marketing-kit.tsx",
      "content": "\"use client\";\n\nimport {\n  useSyncExternalStore,\n  type CSSProperties,\n  type MouseEventHandler,\n  type ReactNode,\n} from \"react\";\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Shared pieces for the marketing blocks: tone scoping, hydration-safe reduced motion,\n * the section heading, call-to-action links and the in-view reveal.\n */\n\nexport type BlockTone = \"light\" | \"dark\" | \"auto\";\n\nconst subscribeNoop = () => () => {};\n\n/** `true` once hydrated and the visitor asked for reduced motion. The server and first client render agree on `false`. */\nexport function useBlockReducedMotion(): boolean {\n  const hydrated = useSyncExternalStore(subscribeNoop, () => true, () => false);\n  const prefers = useReducedMotion();\n  return hydrated && prefers === true;\n}\n\n/**\n * Scopes the library tokens to one block. \"light\" and \"dark\" re-declare every `--bjork-*` token on the\n * section through the `.light` / `.dark` classes; \"auto\" inherits whatever the page sets.\n * Blocks only colour through these tokens, never through `dark:` variants, so a forced tone holds.\n */\nexport function blockToneProps(tone: BlockTone): { className?: string; style?: CSSProperties } {\n  if (tone === \"auto\") return {};\n  return { className: tone, style: { colorScheme: tone } };\n}\n\n/** Focus ring used by every interactive element in the blocks. */\nexport const focusRing =\n  \"outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--bjork-accent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[color:var(--bjork-ring-offset)]\";\n\n/** Section padding and width that every block shares, so stacked blocks line up. */\nexport const blockFrame = \"mx-auto w-full max-w-[1200px] px-5 @2xl:px-8 @5xl:px-10\";\n\nexport interface BlockLink {\n  label: string;\n  href: string;\n  /** Optional click handler; the link still navigates unless you call `preventDefault`. */\n  onClick?: MouseEventHandler<HTMLAnchorElement>;\n}\n\nexport function Eyebrow({ children, className }: { children: ReactNode; className?: string }) {\n  return (\n    <p\n      className={cn(\n        \"flex items-center gap-2 font-mono text-[11px] uppercase leading-4 tracking-[0.12em] text-[color:var(--bjork-text-muted)]\",\n        className,\n      )}\n    >\n      <span aria-hidden className=\"size-1.5 rounded-full bg-[color:var(--bjork-accent)]\" />\n      {children}\n    </p>\n  );\n}\n\nexport interface SectionHeaderProps {\n  id: string;\n  eyebrow?: ReactNode;\n  title: ReactNode;\n  description?: ReactNode;\n  align?: \"start\" | \"center\";\n  as?: \"h1\" | \"h2\" | \"h3\";\n  className?: string;\n  children?: ReactNode;\n}\n\nexport function SectionHeader({\n  id,\n  eyebrow,\n  title,\n  description,\n  align = \"start\",\n  as: Heading = \"h2\",\n  className,\n  children,\n}: SectionHeaderProps) {\n  return (\n    <div\n      className={cn(\n        \"flex flex-col gap-4\",\n        align === \"center\" ? \"items-center text-center\" : \"items-start text-left\",\n        className,\n      )}\n    >\n      {eyebrow ? <Eyebrow>{eyebrow}</Eyebrow> : null}\n      <Heading\n        id={id}\n        className=\"max-w-[20ch] text-balance font-bjork-display text-[34px] font-semibold leading-[1.02] tracking-[-0.035em] text-[color:var(--bjork-text)] @xl:text-[44px] @4xl:text-[52px]\"\n      >\n        {title}\n      </Heading>\n      {description ? (\n        <p className=\"max-w-[58ch] text-pretty text-[16px] leading-7 text-[color:var(--bjork-text-medium)] @xl:text-[17px]\">\n          {description}\n        </p>\n      ) : null}\n      {children}\n    </div>\n  );\n}\n\ntype CtaVariant = \"primary\" | \"secondary\" | \"ghost\";\n\nconst ctaBase =\n  \"group/cta relative inline-flex h-11 shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-[13px] px-[18px] text-[14px] font-medium tracking-[-0.01em] transition-[transform,background-color,border-color,color] duration-150 ease-out active:scale-[0.97] motion-reduce:transition-none motion-reduce:active:scale-100\";\n\nconst ctaVariants: Record<CtaVariant, string> = {\n  primary: \"bjork-layered-button-accent text-white\",\n  secondary:\n    \"border border-[color:var(--bjork-border-strong)] bg-[var(--bjork-surface)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-soft)] hover:border-[color:var(--bjork-text-faint)] hover:bg-[var(--bjork-surface-hover)]\",\n  ghost:\n    \"px-2 text-[color:var(--bjork-text-medium)] hover:text-[color:var(--bjork-text)]\",\n};\n\nexport function CtaLink({\n  link,\n  variant = \"primary\",\n  arrow = false,\n  className,\n}: {\n  link: BlockLink;\n  variant?: CtaVariant;\n  arrow?: boolean;\n  className?: string;\n}) {\n  return (\n    <a\n      href={link.href}\n      onClick={link.onClick}\n      className={cn(ctaBase, ctaVariants[variant], focusRing, className)}\n    >\n      <span>{link.label}</span>\n      {arrow ? (\n        <ArrowRight\n          aria-hidden\n          className=\"size-4 transition-transform duration-200 ease-out group-hover/cta:translate-x-0.5 motion-reduce:transition-none\"\n        />\n      ) : null}\n    </a>\n  );\n}\n\n/**\n * Fades and lifts its children the first time they scroll into view. Under reduced motion only the\n * fade runs, with no movement.\n */\nexport function Reveal({\n  children,\n  delay = 0,\n  className,\n  as = \"div\",\n}: {\n  children: ReactNode;\n  delay?: number;\n  className?: string;\n  as?: \"div\" | \"li\";\n}) {\n  const reduce = useBlockReducedMotion();\n  const Component = as === \"li\" ? motion.li : motion.div;\n  return (\n    <Component\n      className={className}\n      initial={{ opacity: 0, y: 14 }}\n      whileInView={{ opacity: 1, y: 0 }}\n      viewport={{ once: true, amount: 0.2 }}\n      transition={{\n        opacity: { duration: reduce ? 0.2 : 0.5, ease: [0.23, 1, 0.32, 1], delay: reduce ? 0 : delay },\n        y: reduce ? { duration: 0 } : { type: \"spring\", stiffness: 260, damping: 30, mass: 0.9, delay },\n      }}\n    >\n      {children}\n    </Component>\n  );\n}\n\n/** A small product mark used by the sample content. Swap it for your logo. */\nexport function SampleMark({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 24 24\" aria-hidden className={cn(\"size-6\", className)}>\n      <rect x=\"1\" y=\"1\" width=\"22\" height=\"22\" rx=\"7\" fill=\"var(--bjork-text)\" />\n      <path\n        d=\"M6.5 15.5h3l2-7 2 7h4\"\n        fill=\"none\"\n        stroke=\"var(--bjork-bg)\"\n        strokeWidth=\"1.9\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <circle cx=\"17.5\" cy=\"15.5\" r=\"1.6\" fill=\"var(--bjork-accent)\" />\n    </svg>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/bjork-ui/blocks/marketing-kit.tsx"
    },
    {
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n",
      "type": "registry:lib",
      "target": "@lib/utils.ts"
    }
  ],
  "cssVars": {
    "light": {
      "bjork-bg": "#f7f3ea",
      "bjork-surface": "#fffcf6",
      "bjork-surface-muted": "rgba(250, 246, 237, 0.94)",
      "bjork-surface-hover": "#f5efe3",
      "bjork-surface-active": "#efe7d8",
      "bjork-panel": "#f8f2e7",
      "bjork-menu": "rgba(255, 252, 246, 0.98)",
      "bjork-field": "rgba(255, 252, 246, 0.96)",
      "bjork-field-muted": "rgba(248, 242, 231, 0.82)",
      "bjork-field-inset": "rgba(239, 231, 216, 0.72)",
      "bjork-error-bg": "#fff0e8",
      "bjork-text": "#171717",
      "bjork-text-strong": "rgba(23, 23, 23, 0.9)",
      "bjork-text-medium": "rgba(23, 23, 23, 0.78)",
      "bjork-text-muted": "rgba(23, 23, 23, 0.68)",
      "bjork-text-soft": "rgba(23, 23, 23, 0.62)",
      "bjork-text-faint": "rgba(23, 23, 23, 0.22)",
      "bjork-inverted-text": "#fff8f0",
      "bjork-border": "#eee6db",
      "bjork-border-muted": "#f5ede2",
      "bjork-border-strong": "#e1d7c8",
      "bjork-thumb": "#a49b8e",
      "bjork-track": "#ded6ca",
      "bjork-accent": "#ec7d43",
      "bjork-accent-hover": "#f0935f",
      "bjork-accent-soft": "rgba(236, 125, 67, 0.1)",
      "bjork-accent-muted": "rgba(236, 125, 67, 0.2)",
      "bjork-accent-badge": "rgba(236, 125, 67, 0.16)",
      "bjork-accent-badge-foreground": "#8d421d",
      "bjork-accent-foreground": "#3f2112",
      "bjork-accent-fill": "#ec7d43",
      "bjork-accent-fill-hover": "#f0935f",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.92), inset 1px 0 0 rgba(88, 72, 49, 0.026), inset -1px 0 0 rgba(255, 255, 255, 0.68), 0 14px 22px -9px rgba(66, 52, 33, 0.11)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(88, 72, 49, 0.045), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.86)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.64), 0 18px 38px -30px rgba(66, 52, 33, 0.2)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(88, 72, 49, 0.04), inset 0 12px 24px rgba(88, 72, 49, 0.022), inset 1px 0 0 rgba(88, 72, 49, 0.024), inset -1px 0 0 rgba(255, 255, 255, 0.62), 0 22px 44px -26px rgba(66, 52, 33, 0.22)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(88, 72, 49, 0.12), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.72)",
      "bjork-ring-offset": "#f7f3ea"
    },
    "dark": {
      "bjork-bg": "#111111",
      "bjork-surface": "#121212",
      "bjork-surface-muted": "rgba(22, 22, 22, 0.92)",
      "bjork-surface-hover": "#161616",
      "bjork-surface-active": "#202020",
      "bjork-panel": "#0d0d0d",
      "bjork-menu": "rgba(18, 18, 18, 0.98)",
      "bjork-field": "rgba(18, 18, 18, 0.95)",
      "bjork-field-muted": "rgba(18, 18, 18, 0.55)",
      "bjork-field-inset": "rgba(9, 9, 9, 0.75)",
      "bjork-error-bg": "#130d0a",
      "bjork-text": "#ededed",
      "bjork-text-strong": "rgba(237, 237, 237, 0.9)",
      "bjork-text-medium": "rgba(237, 237, 237, 0.72)",
      "bjork-text-muted": "rgba(237, 237, 237, 0.6)",
      "bjork-text-soft": "rgba(237, 237, 237, 0.5)",
      "bjork-text-faint": "rgba(237, 237, 237, 0.22)",
      "bjork-inverted-text": "#080808",
      "bjork-border": "#232323",
      "bjork-border-muted": "#1c1c1c",
      "bjork-border-strong": "#343434",
      "bjork-thumb": "#626262",
      "bjork-track": "#090909",
      "bjork-accent": "#ec5c13",
      "bjork-accent-hover": "#f07832",
      "bjork-accent-soft": "rgba(236, 92, 19, 0.12)",
      "bjork-accent-muted": "rgba(236, 92, 19, 0.24)",
      "bjork-accent-badge": "#b84a12",
      "bjork-accent-badge-foreground": "#fff2ea",
      "bjork-accent-foreground": "#fff2ea",
      "bjork-accent-fill": "#b84a12",
      "bjork-accent-fill-hover": "#a84a14",
      "bjork-shadow-surface": "inset 0 7px 14px rgba(255, 255, 255, 0.03), inset 0 0.5px 0.5px rgba(255, 255, 255, 0.06), 0 14px 20px -6px rgba(0, 0, 0, 0.45)",
      "bjork-shadow-soft": "inset 0 1px 0 rgba(255, 255, 255, 0.045)",
      "bjork-shadow-panel": "inset 0 1px 0 rgba(255, 255, 255, 0.035), 0 18px 36px -28px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-menu": "inset 0 1px 0 rgba(255, 255, 255, 0.055), inset 0 12px 24px rgba(255, 255, 255, 0.018), inset 0 -18px 26px rgba(0, 0, 0, 0.24), 0 22px 42px -22px rgba(0, 0, 0, 0.9)",
      "bjork-shadow-inset": "inset 0 1px 10px rgba(0, 0, 0, 0.45)",
      "bjork-ring-offset": "#080808"
    }
  },
  "css": {
    ".bjork-range": {
      "box-shadow": "inset 0 1px 1px rgba(255, 255, 255, 0.04), 0 0 14px rgba(189, 69, 20, 0.06)"
    },
    ".bjork-range::-webkit-slider-thumb": {
      "appearance": "none",
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-range::-moz-range-thumb": {
      "width": "16px",
      "height": "8px",
      "border": "0",
      "border-radius": "999px",
      "background": "var(--bjork-thumb)",
      "box-shadow": "inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 0 0 1px rgba(0, 0, 0, 0.42)"
    },
    ".bjork-layered-button-accent, .bjork-layered-button-raised": {
      "position": "relative",
      "isolation": "isolate",
      "appearance": "none",
      "overflow": "hidden",
      "border": "0",
      "border-radius": "13px",
      "box-shadow": "inset 0 1px 0 #ffffff38, 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a",
      "transition": "filter 150ms ease-out, box-shadow 150ms ease-out"
    },
    ".bjork-layered-button-accent::after, .bjork-layered-button-raised::after": {
      "position": "absolute",
      "inset": "0",
      "pointer-events": "none",
      "content": "\"\"",
      "border": "1.5px solid transparent",
      "border-radius": "inherit",
      "background": "linear-gradient(#ffffffb8, #0000003d 41% 75%, #ffffff47) border-box",
      "mix-blend-mode": "overlay",
      "-webkit-mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "mask-image": "linear-gradient(#000, #000), linear-gradient(#000, #000)",
      "-webkit-mask-clip": "padding-box, border-box",
      "mask-clip": "padding-box, border-box",
      "-webkit-mask-origin": "padding-box, border-box",
      "mask-origin": "padding-box, border-box",
      "-webkit-mask-composite": "xor",
      "mask-composite": "exclude"
    },
    ".bjork-layered-button-accent": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000040",
      "background": "radial-gradient(ellipse at -20px top, rgba(255, 255, 255, 0.14), transparent), linear-gradient(180deg, #ec5c13 0%, #ec5c13 62%, color-mix(in oklab, #ec5c13 84%, black 16%) 100%)"
    },
    ".bjork-layered-button-raised": {
      "color": "oklch(100% 0 0)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, oklch(0.1881 0.006 265), oklch(0.5493 0.0081 265))"
    },
    ".dark .bjork-layered-button-raised": {
      "color": "oklch(0.2574 0.0056 265)",
      "text-shadow": "0 -1px #00000026",
      "background": "radial-gradient(ellipse at -20px top, #ffffff38, #fff0), linear-gradient(180deg, color-mix(in oklab, oklch(0.9674 0.0013 265) 80%, oklch(0.2574 0.0056 265) 20%), color-mix(in oklab, oklch(0.669 0.0107 265) 80%, oklch(0.2574 0.0056 265) 20%))"
    },
    "@media (hover: hover)": {
      ".bjork-layered-button-accent:hover": {
        "filter": "brightness(1.08) saturate(1.04)"
      },
      ".bjork-layered-button-raised:hover": {
        "filter": "brightness(1.08) contrast(0.9)"
      }
    }
  },
  "meta": {
    "collection": "Blocks",
    "registryUrl": "https://ui.isaiahbjork.com/marketing-feature-bento.json",
    "installCommand": "npx shadcn@latest add https://ui.isaiahbjork.com/marketing-feature-bento.json"
  },
  "type": "registry:block"
}