{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "border-beam",
  "type": "registry:ui",
  "title": "Border Beam",
  "description": "Animated light beam that travels around its parent's border — a quiet “an agent is working in here” signal for composers, cards and panels.",
  "dependencies": [],
  "registryDependencies": [
    "https://kuro.abstergo.fyi/r/kura-theme.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/kura/border-beam/border-beam.tsx",
      "type": "registry:ui",
      "target": "components/kura/border-beam/border-beam.tsx",
      "content": "import type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n/*\n * The beam is one conic gradient whose start angle is a registered custom property, so the\n * browser can interpolate it in a keyframe. A content-box/border-box mask cuts the gradient down\n * to a ring exactly `width` px thick that inherits the parent's border radius.\n */\nconst CSS = `\n@property --kura-beam-angle { syntax: \"<angle>\"; inherits: false; initial-value: 0deg; }\n@keyframes kura-beam-spin { to { --kura-beam-angle: 360deg; } }\n[data-slot=\"border-beam-ring\"] {\n  padding: var(--kura-beam-width);\n  background: conic-gradient(from var(--kura-beam-angle), var(--kura-beam-stops));\n  -webkit-mask: linear-gradient(black, black) content-box, linear-gradient(black, black);\n  -webkit-mask-composite: xor;\n  mask: linear-gradient(black, black) content-box exclude, linear-gradient(black, black);\n  animation: kura-beam-spin var(--kura-beam-duration) linear infinite;\n  animation-delay: var(--kura-beam-delay);\n  animation-direction: var(--kura-beam-direction);\n  animation-play-state: var(--kura-beam-play);\n}\n@media (prefers-reduced-motion: reduce) {\n  [data-slot=\"border-beam-ring\"] {\n    animation: none;\n    background: var(--kura-beam-static);\n  }\n}\n`;\n\nexport interface BorderBeamProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n  /** Show the beam. Toggling fades it in/out (and pauses the animation when hidden). Default true. */\n  active?: boolean;\n  /** Seconds per lap. Default 6. */\n  duration?: number;\n  /** Beam length as a share of the lap, 0.05–0.9. Default 0.25. */\n  size?: number;\n  /** Ring thickness in px. Default 1. */\n  width?: number;\n  /** CSS colours from tail to head, e.g. `[\"transparent\", \"var(--kura-accent)\"]`. Default: a jade tail fading in to a bright head. */\n  colors?: readonly string[];\n  /** Add a soft blurred glow around the beam. Default false. */\n  glow?: boolean;\n  /** Travel counter-clockwise. Default false. */\n  reverse?: boolean;\n  /** Start position as a share of the lap, 0–1. Use 0.5 on a second beam to run them opposite. Default 0. */\n  offset?: number;\n}\n\nconst DEFAULT_COLORS = [\n  \"transparent\",\n  \"color-mix(in oklab, var(--kura-accent) 45%, transparent)\",\n  \"var(--kura-accent)\",\n  \"var(--kura-accent-text)\",\n];\n\nfunction stops(colors: readonly string[], size: number): string {\n  const len = Math.min(0.9, Math.max(0.05, size)) * 360;\n  const start = 360 - len;\n  const head = 356;\n  const list = colors.length > 1 ? colors : [\"transparent\", colors[0] ?? \"var(--kura-accent)\"];\n  const step = (head - start) / (list.length - 1);\n  const parts = list.map((c, i) => `${c} ${(start + i * step).toFixed(2)}deg`);\n  return `transparent 0deg, transparent ${start.toFixed(2)}deg, ${parts.join(\", \")}, transparent 360deg`;\n}\n\n/**\n * An animated light beam that travels around its parent's border — the \"an agent is working in\n * here\" signal for composers, cards and panels. Place it inside a `relative` element with a\n * border radius; it inherits the radius and never affects layout. Pure CSS (server-safe).\n */\nexport function BorderBeam({\n  active = true,\n  duration = 6,\n  size = 0.25,\n  width = 1,\n  colors = DEFAULT_COLORS,\n  glow = false,\n  reverse = false,\n  offset = 0,\n  className,\n  style,\n  ...props\n}: BorderBeamProps) {\n  const vars = {\n    \"--kura-beam-duration\": `${duration}s`,\n    \"--kura-beam-width\": `${width}px`,\n    \"--kura-beam-stops\": stops(colors, size),\n    \"--kura-beam-delay\": `${-Math.min(1, Math.max(0, offset)) * duration}s`,\n    \"--kura-beam-direction\": reverse ? \"reverse\" : \"normal\",\n    \"--kura-beam-play\": active ? \"running\" : \"paused\",\n    \"--kura-beam-static\": `color-mix(in oklab, ${colors[colors.length - 1] ?? \"var(--kura-accent)\"} 45%, transparent)`,\n  } as React.CSSProperties;\n  return (\n    <div\n      aria-hidden\n      data-slot=\"border-beam\"\n      data-active={active}\n      className={cn(\n        \"pointer-events-none absolute inset-0 rounded-[inherit] transition-opacity duration-(--kura-duration-slower) ease-kura-out\",\n        active ? \"opacity-100\" : \"opacity-0\",\n        className,\n      )}\n      style={{ ...vars, ...style }}\n      {...props}\n    >\n      <style href=\"kura-border-beam\" precedence=\"kura\">\n        {CSS}\n      </style>\n      {glow && (\n        <div\n          data-slot=\"border-beam-glow\"\n          className=\"absolute inset-0 rounded-[inherit] opacity-55 blur-[5px] motion-reduce:opacity-30\"\n        >\n          <div\n            data-slot=\"border-beam-ring\"\n            className=\"absolute inset-0 rounded-[inherit]\"\n            style={{ \"--kura-beam-width\": `${width + 2}px` } as React.CSSProperties}\n          />\n        </div>\n      )}\n      <div data-slot=\"border-beam-ring\" className=\"absolute inset-0 rounded-[inherit]\" />\n    </div>\n  );\n}\n"
    },
    {
      "path": "registry/kura/border-beam/index.ts",
      "type": "registry:ui",
      "target": "components/kura/border-beam/index.ts",
      "content": "export { BorderBeam, type BorderBeamProps } from \"./border-beam\";\n"
    }
  ],
  "categories": [
    "effects",
    "border",
    "beam",
    "glow",
    "loading"
  ],
  "docs": "Docs: https://kuro.abstergo.fyi/components/border-beam · Markdown for agents: https://kuro.abstergo.fyi/components/border-beam.md",
  "meta": {
    "kura": {
      "status": "beta",
      "whenToUse": [
        "A composer, card or panel whose content an agent is currently editing or generating.",
        "A running tool call or background task row that should read as live without a spinner.",
        "Highlighting the one container on screen that is waiting for or producing AI output."
      ],
      "whenNotToUse": [
        "Small inline 'thinking' feedback — use thinking-indicator or shimmer-text.",
        "Filling a background with colour — use mesh-gradient or aurora.",
        "Focus or selection states — use the focus ring (shadow-kura-focus), not a beam.",
        "Several beams on one screen at once: it is a signal; more than one or two becomes noise."
      ]
    }
  }
}
