{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "weld",
  "type": "registry:ui",
  "title": "Weld",
  "description": "A light beam traveling the border in a continuous loop like a welding arc: a blinding white-hot head with a cool blue-white tail, corner sparks, and a fading heat discoloration. Pure CSS, corner-radius aware via offset-path.",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/buttons/weld/weld.tsx",
      "type": "registry:ui",
      "target": "components/crucible/weld.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface WeldProps extends React.ComponentPropsWithoutRef<\"button\"> {\n  /** Color of the blinding beam head. Any CSS color. @default white */\n  headColor?: string;\n  /** Color of the tail trailing the head. @default cool blue-white */\n  tailColor?: string;\n  /** Seconds for one full lap of the border. @default 3.2 */\n  duration?: number;\n  /** Border radius in px — the beam's path is corner-radius aware and matches this exactly. @default 10 */\n  radius?: number;\n  /** Travel counterclockwise instead of clockwise. @default false */\n  reverse?: boolean;\n  /** Seconds to wait before the first lap starts. @default 0 */\n  delay?: number;\n  /** Freeze the animation, holding the current frame. @default false */\n  paused?: boolean;\n}\n\n/**\n * Weld — a light beam traveling the border in a continuous loop, like a\n * welding arc tracing the seam: a blinding white-hot head with a cool\n * blue-white tail, corner sparks, and a faint heat-discoloration on the rim\n * that fades before the next lap. Pure CSS, corner-radius aware via\n * `offset-path`. Honors prefers-reduced-motion by freezing the beam in place.\n */\nexport function Weld({\n  headColor = \"#ffffff\",\n  tailColor = \"#93c5fd\",\n  duration = 3.2,\n  radius = 10,\n  reverse = false,\n  delay = 0,\n  paused = false,\n  className,\n  style,\n  disabled,\n  children,\n  ...rest\n}: WeldProps) {\n  return (\n    <button\n      data-crucible=\"weld\"\n      className={cn(\n        \"relative isolate inline-flex items-center justify-center overflow-visible\",\n        \"border border-neutral-800 bg-neutral-900 px-6 py-3 text-sm font-medium text-white\",\n        \"select-none\",\n        \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-neutral-950\",\n        \"focus-visible:ring-[color:var(--weld-head)]\",\n        \"disabled:cursor-not-allowed disabled:opacity-50\",\n        className\n      )}\n      style={\n        {\n          \"--weld-head\": headColor,\n          \"--weld-tail\": tailColor,\n          \"--weld-duration\": `${duration}s`,\n          \"--weld-delay\": `${delay}s`,\n          \"--weld-direction\": reverse ? \"reverse\" : \"normal\",\n          \"--weld-play\": paused ? \"paused\" : \"running\",\n          \"--weld-radius\": `${radius}px`,\n          borderRadius: radius,\n          ...style,\n        } as React.CSSProperties\n      }\n      disabled={disabled}\n      {...rest}\n    >\n      <style>{`\n        @keyframes crucible-weld-travel {\n          to { offset-distance: 100%; }\n        }\n        @keyframes crucible-weld-spark {\n          0%, 92%, 100% { opacity: 0; transform: scale(0.4); }\n          95% { opacity: 1; transform: scale(1); }\n        }\n        @keyframes crucible-weld-heat {\n          0%   { opacity: 0; }\n          6%   { opacity: 0.9; }\n          30%  { opacity: 0; }\n          100% { opacity: 0; }\n        }\n        [data-crucible=\"weld\"] .weld-track {\n          position: absolute;\n          inset: 0;\n          offset-path: inset(0 round var(--weld-radius));\n          offset-distance: 0%;\n          animation: crucible-weld-travel var(--weld-duration) linear infinite;\n          animation-delay: var(--weld-delay);\n          animation-direction: var(--weld-direction);\n          animation-play-state: var(--weld-play);\n        }\n        /* The track box is exactly container-sized so its own perimeter IS the\n           button's border; offset-distance rides that perimeter by moving the\n           box's center anchor. Centering the head/tail on that anchor makes\n           them ride the path while the (invisible) track box itself overflows. */\n        [data-crucible=\"weld\"] .weld-track { offset-rotate: auto; }\n        [data-crucible=\"weld\"] .weld-head,\n        [data-crucible=\"weld\"] .weld-trail-dot {\n          position: absolute;\n          top: 50%;\n          left: 50%;\n          transform: translate(-50%, -50%);\n          border-radius: 999px;\n        }\n        [data-crucible=\"weld\"] .weld-head {\n          width: 7px;\n          height: 7px;\n          /* A tight white-hot core so the beam reads as a point riding the\n             seam, not a loose orb hovering off the edge. */\n          background: radial-gradient(circle, #fff 0%, var(--weld-head) 30%, var(--weld-tail) 62%, transparent 80%);\n          filter: blur(0.3px);\n          box-shadow: 0 0 5px 1px color-mix(in oklab, var(--weld-head) 85%, transparent),\n            0 0 11px 2px color-mix(in oklab, var(--weld-tail) 45%, transparent);\n        }\n        /* The tail is drip dots on their own tracks, phase-lagged behind the\n           head via negative animation-delays — they ride the same offset-path,\n           so the trail bends around corners instead of flying off tangentially\n           like a straight gradient bar would. */\n        [data-crucible=\"weld\"] .weld-trail-dot {\n          background: var(--weld-tail);\n          filter: blur(0.4px);\n          box-shadow: 0 0 4px 1px color-mix(in oklab, var(--weld-tail) 55%, transparent);\n        }\n        [data-crucible=\"weld\"] .weld-corner {\n          position: absolute;\n          width: 4px;\n          height: 4px;\n          border-radius: 999px;\n          background: var(--weld-head);\n          box-shadow: 0 0 5px 1px color-mix(in oklab, var(--weld-tail) 60%, transparent);\n          animation: crucible-weld-spark var(--weld-duration) linear infinite;\n          /* backwards: adopt the 0% keyframe (opacity 0) during the staggered\n             start delay, so delayed corners stay dark on mount instead of\n             flashing at their base opacity 1 until their animation begins. */\n          animation-fill-mode: backwards;\n          animation-play-state: var(--weld-play);\n          transform: translate(-50%, -50%);\n        }\n        [data-crucible=\"weld\"] .weld-heat {\n          position: absolute;\n          inset: 0;\n          border-radius: inherit;\n          box-shadow: inset 0 0 0 1.5px color-mix(in oklab, var(--weld-tail) 55%, transparent);\n          animation: crucible-weld-heat var(--weld-duration) ease-out infinite;\n          animation-delay: var(--weld-delay);\n          /* backwards so a non-zero delay holds the rim at the 0% keyframe\n             (opacity 0) rather than its base opacity 1 during the wait. */\n          animation-fill-mode: backwards;\n          animation-play-state: var(--weld-play);\n        }\n        [data-crucible=\"weld\"]:disabled .weld-track,\n        [data-crucible=\"weld\"]:disabled .weld-corner,\n        [data-crucible=\"weld\"]:disabled .weld-heat {\n          animation-play-state: paused;\n        }\n        @media (prefers-reduced-motion: reduce) {\n          [data-crucible=\"weld\"] .weld-track,\n          [data-crucible=\"weld\"] .weld-corner,\n          [data-crucible=\"weld\"] .weld-heat {\n            animation: none;\n          }\n          [data-crucible=\"weld\"] .weld-track { offset-distance: 0%; }\n          [data-crucible=\"weld\"] .weld-heat { opacity: 0.25; }\n          [data-crucible=\"weld\"] .weld-corner { opacity: 0; }\n        }\n      `}</style>\n\n      {/* Faint heat discoloration that blooms after the beam passes and fades before the next lap. */}\n      <span aria-hidden className=\"weld-heat pointer-events-none\" />\n\n      {/* Corner sparks, roughly phase-locked to the beam reaching each corner. */}\n      {/* Counterclockwise laps reach the top-right / bottom-left corners in the\n          opposite order, so their phases swap when reversed. */}\n      <span aria-hidden className=\"weld-corner pointer-events-none\" style={{ top: 0, left: 0, animationDelay: `calc(var(--weld-delay) + var(--weld-duration) * 0.0)` }} />\n      <span aria-hidden className=\"weld-corner pointer-events-none\" style={{ top: 0, right: 0, animationDelay: `calc(var(--weld-delay) + var(--weld-duration) * ${reverse ? 0.75 : 0.25})` }} />\n      <span aria-hidden className=\"weld-corner pointer-events-none\" style={{ bottom: 0, right: 0, animationDelay: `calc(var(--weld-delay) + var(--weld-duration) * 0.5)` }} />\n      <span aria-hidden className=\"weld-corner pointer-events-none\" style={{ bottom: 0, left: 0, animationDelay: `calc(var(--weld-delay) + var(--weld-duration) * ${reverse ? 0.25 : 0.75})` }} />\n\n      {/* The beam itself: a bright head trailed by drip dots, all riding the\n          border path. Negative delays put each dot mid-lap immediately (no\n          start-corner pileup) at a phase just behind the head. */}\n      {[\n        { lag: 0.008, size: 5, opacity: 0.85 },\n        { lag: 0.018, size: 4, opacity: 0.6 },\n        { lag: 0.03, size: 3.5, opacity: 0.4 },\n        { lag: 0.044, size: 3, opacity: 0.22 },\n      ].map(({ lag, size, opacity }) => (\n        <span\n          key={lag}\n          aria-hidden\n          className=\"weld-track pointer-events-none\"\n          style={{ animationDelay: `calc(var(--weld-delay) - var(--weld-duration) * ${1 - lag})` }}\n        >\n          <span className=\"weld-trail-dot\" style={{ width: size, height: size, opacity }} />\n        </span>\n      ))}\n      <span aria-hidden className=\"weld-track pointer-events-none\">\n        <span className=\"weld-head\" />\n      </span>\n\n      <span className=\"relative z-10\">{children}</span>\n    </button>\n  );\n}\n"
    }
  ]
}