{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"column-drift","type":"registry:ui","title":"Column Drift","description":"A tilted wall of image columns drifting up and down at different speeds, pausing on hover.","dependencies":[],"registryDependencies":["utils"],"files":[{"path":"registry/ui/column-drift.tsx","type":"registry:ui","content":"\"use client\";\n\nimport type { CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nconst css = `\n@keyframes drift-wall-up{from{transform:translate3d(0,0,0)}to{transform:translate3d(0,-50%,0)}}\n@keyframes drift-wall-down{from{transform:translate3d(0,-50%,0)}to{transform:translate3d(0,0,0)}}\n.drift-wall-col{animation:var(--dw-name) var(--dw-dur) linear infinite;will-change:transform}\n.column-drift[data-pause=\"true\"]:hover .drift-wall-col{animation-play-state:paused}\n.drift-wall-tile{transition:transform .5s cubic-bezier(.2,.7,.2,1),filter .5s}\n.column-drift[data-pause=\"true\"]:hover .drift-wall-tile{filter:saturate(.7) brightness(.8)}\n.column-drift[data-pause=\"true\"] .drift-wall-tile:hover{transform:scale(1.06);filter:none;z-index:1}\n@media (prefers-reduced-motion:reduce){.drift-wall-col{animation:none}}\n`;\n\nexport interface ColumnDriftProps {\n  images: string[];\n  /** Number of image columns. */\n  columns?: number;\n  /** Drift speed in px per second. */\n  speed?: number;\n  /** Wall tilt in degrees. */\n  tilt?: number;\n  /** Px between tiles. */\n  gap?: number;\n  /** Freeze the wall while hovered. */\n  pauseOnHover?: boolean;\n  /** Fade the wall into the background at the edges. */\n  fade?: boolean;\n  className?: string;\n}\n\n// Rough tile height (px) used to turn speed into an animation duration.\nconst TILE = 300;\n\nexport function ColumnDrift({\n  images,\n  columns = 7,\n  speed = 40,\n  tilt = 16,\n  gap = 16,\n  pauseOnHover = true,\n  fade = true,\n  className,\n}: ColumnDriftProps) {\n  const cols = Math.max(1, Math.round(columns));\n  const perCol = Math.max(4, Math.ceil(images.length / cols) + 2);\n\n  return (\n    <div\n      className={cn(\"column-drift relative h-full w-full overflow-hidden\", className)}\n      data-pause={pauseOnHover}\n      style={{ perspective: 1600 }}\n    >\n      <style href=\"column-drift\" precedence=\"default\">\n        {css}\n      </style>\n      <div\n        className=\"absolute left-1/2 top-1/2 flex\"\n        style={{\n          gap,\n          width: \"150%\",\n          height: \"220%\",\n          transform: `translate(-50%,-50%) rotateX(${tilt * 1.4}deg) rotateZ(${-tilt}deg)`,\n          transformStyle: \"preserve-3d\",\n        }}\n      >\n        {Array.from({ length: cols }, (_, c) => {\n          const list = Array.from({ length: perCol }, (_, j) => images[(j * cols + c) % images.length]);\n          // Each column gets its own direction and pace so the wall feels alive.\n          const pace = 1 + ((c * 7) % 5) * 0.18;\n          const dur = speed > 0 ? (perCol * TILE) / (speed * pace) : 0;\n          return (\n            <div key={c} className=\"min-w-0 flex-1\" style={{ marginTop: c % 2 ? -TILE / 2 : 0 }}>\n              <div\n                className=\"drift-wall-col flex flex-col\"\n                style={\n                  {\n                    // Margin (not flex gap) keeps -50% an exact loop point.\n                    \"--dw-name\": dur ? (c % 2 ? \"drift-wall-down\" : \"drift-wall-up\") : \"none\",\n                    \"--dw-dur\": `${dur}s`,\n                  } as CSSProperties\n                }\n              >\n                {[...list, ...list].map((src, j) => (\n                  // eslint-disable-next-line @next/next/no-img-element\n                  <img\n                    key={j}\n                    src={src}\n                    alt=\"\"\n                    draggable={false}\n                    style={{ marginBottom: gap }}\n                    className=\"drift-wall-tile relative block aspect-[4/5] w-full rounded-xl bg-muted object-cover shadow-lg\"\n                  />\n                ))}\n              </div>\n            </div>\n          );\n        })}\n      </div>\n      {fade && (\n        <div\n          aria-hidden\n          className=\"pointer-events-none absolute inset-0\"\n          style={{\n            background:\n              \"radial-gradient(ellipse 75% 70% at 50% 50%, transparent 45%, var(--background) 100%)\",\n          }}\n        />\n      )}\n    </div>\n  );\n}\n"}]}