{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "ImageTrail-JS-TW",
	"title": "ImageTrail",
	"description": "Cursor-based image trail with several built-in variants.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "ImageTrail/ImageTrail.jsx",
			"content": "import { useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\n\nfunction lerp(a, b, n) {\n  return (1 - n) * a + n * b;\n}\n\nfunction getLocalPointerPos(e, rect) {\n  let clientX = 0,\n    clientY = 0;\n  if (e.touches && e.touches.length > 0) {\n    clientX = e.touches[0].clientX;\n    clientY = e.touches[0].clientY;\n  } else {\n    clientX = e.clientX;\n    clientY = e.clientY;\n  }\n  return {\n    x: clientX - rect.left,\n    y: clientY - rect.top\n  };\n}\nfunction getMouseDistance(p1, p2) {\n  const dx = p1.x - p2.x;\n  const dy = p1.y - p2.y;\n  return Math.hypot(dx, dy);\n}\n\nclass ImageItem {\n  DOM = { el: null, inner: null };\n  defaultStyle = { scale: 1, x: 0, y: 0, opacity: 0 };\n  rect = null;\n\n  constructor(DOM_el) {\n    this.DOM.el = DOM_el;\n    this.DOM.inner = this.DOM.el.querySelector('.content__img-inner');\n    this.getRect();\n    this.initEvents();\n  }\n  initEvents() {\n    this.resize = () => {\n      gsap.set(this.DOM.el, this.defaultStyle);\n      this.getRect();\n    };\n    window.addEventListener('resize', this.resize);\n  }\n  getRect() {\n    this.rect = this.DOM.el.getBoundingClientRect();\n  }\n\n  destroy() {\n    window.removeEventListener('resize', this.resize);\n  }\n}\n\nclass ImageTrailVariant1 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...this.DOM.el.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    const handlePointerMove = ev => {\n      const rect = this.container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = this.container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n\n      this.rafId = requestAnimationFrame(() => this.render());\n\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.1);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.1);\n\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    if (this.isIdle && this.zIndexVal !== 1) {\n      this.zIndexVal = 1;\n    }\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n\n    gsap.killTweensOf(img.DOM.el);\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          scale: 1,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.4,\n          ease: 'power3',\n          opacity: 0,\n          scale: 0.2\n        },\n        0.4\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) {\n      this.isIdle = true;\n    }\n  }\n}\n\nclass ImageTrailVariant2 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n\n      this.rafId = requestAnimationFrame(() => this.render());\n\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.1);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.1);\n\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    if (this.isIdle && this.zIndexVal !== 1) {\n      this.zIndexVal = 1;\n    }\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n\n    gsap.killTweensOf(img.DOM.el);\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          scale: 0,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          scale: 1,\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2\n        },\n        0\n      )\n      .fromTo(\n        img.DOM.inner,\n        {\n          scale: 2.8,\n          filter: 'brightness(250%)'\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          scale: 1,\n          filter: 'brightness(100%)'\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.4,\n          ease: 'power2',\n          opacity: 0,\n          scale: 0.2\n        },\n        0.45\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) this.isIdle = true;\n  }\n}\n\nclass ImageTrailVariant3 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n\n      this.rafId = requestAnimationFrame(() => this.render());\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.1);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.1);\n\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    if (this.isIdle && this.zIndexVal !== 1) {\n      this.zIndexVal = 1;\n    }\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n    gsap.killTweensOf(img.DOM.el);\n\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          scale: 0,\n          zIndex: this.zIndexVal,\n          xPercent: 0,\n          yPercent: 0,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          scale: 1,\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2\n        },\n        0\n      )\n      .fromTo(\n        img.DOM.inner,\n        {\n          scale: 1.2\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          scale: 1\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.6,\n          ease: 'power2',\n          opacity: 0,\n          scale: 0.2,\n          xPercent: () => gsap.utils.random(-30, 30),\n          yPercent: -200\n        },\n        0.6\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) this.isIdle = true;\n  }\n}\n\nclass ImageTrailVariant4 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n      this.rafId = requestAnimationFrame(() => this.render());\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.1);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.1);\n\n    if (this.isIdle && this.zIndexVal !== 1) this.zIndexVal = 1;\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n    gsap.killTweensOf(img.DOM.el);\n\n    let dx = this.mousePos.x - this.cacheMousePos.x;\n    let dy = this.mousePos.y - this.cacheMousePos.y;\n    let distance = Math.sqrt(dx * dx + dy * dy);\n    if (distance !== 0) {\n      dx /= distance;\n      dy /= distance;\n    }\n    dx *= distance / 100;\n    dy *= distance / 100;\n\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          scale: 0,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          scale: 1,\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2\n        },\n        0\n      )\n      .fromTo(\n        img.DOM.inner,\n        {\n          scale: 2,\n          filter: `brightness(${Math.max((400 * distance) / 100, 100)}%) contrast(${Math.max((400 * distance) / 100, 100)}%)`\n        },\n        {\n          duration: 0.4,\n          ease: 'power1',\n          scale: 1,\n          filter: 'brightness(100%) contrast(100%)'\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.4,\n          ease: 'power3',\n          opacity: 0\n        },\n        0.4\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 1.5,\n          ease: 'power4',\n          x: `+=${dx * 110}`,\n          y: `+=${dy * 110}`\n        },\n        0.05\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) this.isIdle = true;\n  }\n}\n\nclass ImageTrailVariant5 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n    this.lastAngle = 0;\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n      this.rafId = requestAnimationFrame(() => this.render());\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.1);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.1);\n    if (this.isIdle && this.zIndexVal !== 1) this.zIndexVal = 1;\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    let dx = this.mousePos.x - this.cacheMousePos.x;\n    let dy = this.mousePos.y - this.cacheMousePos.y;\n    let angle = Math.atan2(dy, dx) * (180 / Math.PI);\n    if (angle < 0) angle += 360;\n    if (angle > 90 && angle <= 270) angle += 180;\n    const isMovingClockwise = angle >= this.lastAngle;\n    this.lastAngle = angle;\n    let startAngle = isMovingClockwise ? angle - 10 : angle + 10;\n    let distance = Math.sqrt(dx * dx + dy * dy);\n    if (distance !== 0) {\n      dx /= distance;\n      dy /= distance;\n    }\n    dx *= distance / 150;\n    dy *= distance / 150;\n\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n    gsap.killTweensOf(img.DOM.el);\n\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          filter: 'brightness(80%)',\n          scale: 0.1,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2,\n          rotation: startAngle\n        },\n        {\n          duration: 1,\n          ease: 'power2',\n          scale: 1,\n          filter: 'brightness(100%)',\n          x: this.mousePos.x - img.rect.width / 2 + dx * 70,\n          y: this.mousePos.y - img.rect.height / 2 + dy * 70,\n          rotation: this.lastAngle\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.4,\n          ease: 'expo',\n          opacity: 0\n        },\n        0.5\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 1.5,\n          ease: 'power4',\n          x: `+=${dx * 120}`,\n          y: `+=${dy * 120}`\n        },\n        0.05\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) this.isIdle = true;\n  }\n}\n\nclass ImageTrailVariant6 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n      this.rafId = requestAnimationFrame(() => this.render());\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.3);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.3);\n\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    if (this.isIdle && this.zIndexVal !== 1) {\n      this.zIndexVal = 1;\n    }\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  mapSpeedToSize(speed, minSize, maxSize) {\n    const maxSpeed = 200;\n    return minSize + (maxSize - minSize) * Math.min(speed / maxSpeed, 1);\n  }\n  mapSpeedToBrightness(speed, minB, maxB) {\n    const maxSpeed = 70;\n    return minB + (maxB - minB) * Math.min(speed / maxSpeed, 1);\n  }\n  mapSpeedToBlur(speed, minBlur, maxBlur) {\n    const maxSpeed = 90;\n    return minBlur + (maxBlur - minBlur) * Math.min(speed / maxSpeed, 1);\n  }\n  mapSpeedToGrayscale(speed, minG, maxG) {\n    const maxSpeed = 90;\n    return minG + (maxG - minG) * Math.min(speed / maxSpeed, 1);\n  }\n\n  showNextImage() {\n    let dx = this.mousePos.x - this.cacheMousePos.x;\n    let dy = this.mousePos.y - this.cacheMousePos.y;\n    let speed = Math.sqrt(dx * dx + dy * dy);\n\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n\n    let scaleFactor = this.mapSpeedToSize(speed, 0.3, 2);\n    let brightnessValue = this.mapSpeedToBrightness(speed, 0, 1.3);\n    let blurValue = this.mapSpeedToBlur(speed, 20, 0);\n    let grayscaleValue = this.mapSpeedToGrayscale(speed, 600, 0);\n\n    gsap.killTweensOf(img.DOM.el);\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          scale: 0,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2\n        },\n        {\n          duration: 0.8,\n          ease: 'power3',\n          scale: scaleFactor,\n          filter: `grayscale(${grayscaleValue * 100}%) brightness(${brightnessValue * 100}%) blur(${blurValue}px)`,\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2\n        },\n        0\n      )\n      .fromTo(\n        img.DOM.inner,\n        {\n          scale: 2\n        },\n        {\n          duration: 0.8,\n          ease: 'power3',\n          scale: 1\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.4,\n          ease: 'power3.in',\n          opacity: 0,\n          scale: 0.2\n        },\n        0.45\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) {\n      this.isIdle = true;\n    }\n  }\n}\n\nfunction getNewPosition(position, offset, arr) {\n  const realOffset = Math.abs(offset) % arr.length;\n  if (position - realOffset >= 0) {\n    return position - realOffset;\n  } else {\n    return arr.length - (realOffset - position);\n  }\n}\nclass ImageTrailVariant7 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    this.visibleImagesCount = 0;\n    this.visibleImagesTotal = 9;\n    this.visibleImagesTotal = Math.min(this.visibleImagesTotal, this.imagesTotal - 1);\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n      this.rafId = requestAnimationFrame(() => this.render());\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.3);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.3);\n\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    if (this.isIdle && this.zIndexVal !== 1) this.zIndexVal = 1;\n\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n    ++this.visibleImagesCount;\n\n    gsap.killTweensOf(img.DOM.el);\n    const scaleValue = gsap.utils.random(0.5, 1.6);\n\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .fromTo(\n        img.DOM.el,\n        {\n          scale: scaleValue - Math.max(gsap.utils.random(0.2, 0.6), 0),\n          rotationZ: 0,\n          opacity: 1,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2\n        },\n        {\n          duration: 0.4,\n          ease: 'power3',\n          scale: scaleValue,\n          rotationZ: gsap.utils.random(-3, 3),\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2\n        },\n        0\n      );\n\n    if (this.visibleImagesCount >= this.visibleImagesTotal) {\n      const lastInQueue = getNewPosition(this.imgPosition, this.visibleImagesTotal, this.images);\n      const oldImg = this.images[lastInQueue];\n      gsap.to(oldImg.DOM.el, {\n        duration: 0.4,\n        ease: 'power4',\n        opacity: 0,\n        scale: 1.3,\n        onComplete: () => {\n          if (this.activeImagesCount === 0) {\n            this.isIdle = true;\n          }\n        }\n      });\n    }\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n  }\n}\n\nclass ImageTrailVariant8 {\n  constructor(container) {\n    this.container = container;\n    this.rafId = null;\n    this.destroyed = false;\n    this.DOM = { el: container };\n    this.images = [...container.querySelectorAll('.content__img')].map(img => new ImageItem(img));\n    this.imagesTotal = this.images.length;\n    this.imgPosition = 0;\n    this.zIndexVal = 1;\n    this.activeImagesCount = 0;\n    this.isIdle = true;\n    this.threshold = 80;\n\n    this.mousePos = { x: 0, y: 0 };\n    this.lastMousePos = { x: 0, y: 0 };\n    this.cacheMousePos = { x: 0, y: 0 };\n\n    this.rotation = { x: 0, y: 0 };\n    this.cachedRotation = { x: 0, y: 0 };\n    this.zValue = 0;\n    this.cachedZValue = 0;\n\n    const handlePointerMove = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n    };\n    container.addEventListener('mousemove', handlePointerMove);\n    container.addEventListener('touchmove', handlePointerMove);\n\n    const initRender = ev => {\n      const rect = container.getBoundingClientRect();\n      this.mousePos = getLocalPointerPos(ev, rect);\n      this.cacheMousePos = { ...this.mousePos };\n      this.rafId = requestAnimationFrame(() => this.render());\n      container.removeEventListener('mousemove', initRender);\n      container.removeEventListener('touchmove', initRender);\n    };\n    container.addEventListener('mousemove', initRender);\n    container.addEventListener('touchmove', initRender);\n    this.handlePointerMove = handlePointerMove;\n    this.initRender = initRender;\n  }\n\n  render() {\n    if (this.destroyed) return;\n\n    let distance = getMouseDistance(this.mousePos, this.lastMousePos);\n    this.cacheMousePos.x = lerp(this.cacheMousePos.x, this.mousePos.x, 0.1);\n    this.cacheMousePos.y = lerp(this.cacheMousePos.y, this.mousePos.y, 0.1);\n\n    if (distance > this.threshold) {\n      this.showNextImage();\n      this.lastMousePos = { ...this.mousePos };\n    }\n    if (this.isIdle && this.zIndexVal !== 1) {\n      this.zIndexVal = 1;\n    }\n    this.rafId = requestAnimationFrame(() => this.render());\n  }\n\n  showNextImage() {\n    const rect = this.container.getBoundingClientRect();\n    const centerX = rect.width / 2;\n    const centerY = rect.height / 2;\n    const relX = this.mousePos.x - centerX;\n    const relY = this.mousePos.y - centerY;\n\n    this.rotation.x = -(relY / centerY) * 30;\n    this.rotation.y = (relX / centerX) * 30;\n    this.cachedRotation = { ...this.rotation };\n\n    const distanceFromCenter = Math.sqrt(relX * relX + relY * relY);\n    const maxDistance = Math.sqrt(centerX * centerX + centerY * centerY);\n    const proportion = distanceFromCenter / maxDistance;\n    this.zValue = proportion * 1200 - 600;\n    this.cachedZValue = this.zValue;\n    const normalizedZ = (this.zValue + 600) / 1200;\n    const brightness = 0.2 + normalizedZ * 2.3;\n\n    ++this.zIndexVal;\n    this.imgPosition = this.imgPosition < this.imagesTotal - 1 ? this.imgPosition + 1 : 0;\n    const img = this.images[this.imgPosition];\n    gsap.killTweensOf(img.DOM.el);\n\n    gsap\n      .timeline({\n        onStart: () => this.onImageActivated(),\n        onComplete: () => this.onImageDeactivated()\n      })\n      .set(this.DOM.el, { perspective: 1000 }, 0)\n      .fromTo(\n        img.DOM.el,\n        {\n          opacity: 1,\n          z: 0,\n          scale: 1 + this.cachedZValue / 1000,\n          zIndex: this.zIndexVal,\n          x: this.cacheMousePos.x - img.rect.width / 2,\n          y: this.cacheMousePos.y - img.rect.height / 2,\n          rotationX: this.cachedRotation.x,\n          rotationY: this.cachedRotation.y,\n          filter: `brightness(${brightness})`\n        },\n        {\n          duration: 1,\n          ease: 'expo',\n          scale: 1 + this.zValue / 1000,\n          x: this.mousePos.x - img.rect.width / 2,\n          y: this.mousePos.y - img.rect.height / 2,\n          rotationX: this.rotation.x,\n          rotationY: this.rotation.y\n        },\n        0\n      )\n      .to(\n        img.DOM.el,\n        {\n          duration: 0.4,\n          ease: 'power2',\n          opacity: 0,\n          z: -800\n        },\n        0.3\n      );\n  }\n\n  destroy() {\n    this.destroyed = true;\n    if (this.rafId !== null) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n    this.container.removeEventListener('mousemove', this.handlePointerMove);\n    this.container.removeEventListener('touchmove', this.handlePointerMove);\n    this.container.removeEventListener('mousemove', this.initRender);\n    this.container.removeEventListener('touchmove', this.initRender);\n    this.images.forEach(img => {\n      gsap.killTweensOf(img.DOM.el);\n      img.destroy();\n    });\n  }\n\n  onImageActivated() {\n    this.activeImagesCount++;\n    this.isIdle = false;\n  }\n  onImageDeactivated() {\n    this.activeImagesCount--;\n    if (this.activeImagesCount === 0) this.isIdle = true;\n  }\n}\n\nconst variantMap = {\n  1: ImageTrailVariant1,\n  2: ImageTrailVariant2,\n  3: ImageTrailVariant3,\n  4: ImageTrailVariant4,\n  5: ImageTrailVariant5,\n  6: ImageTrailVariant6,\n  7: ImageTrailVariant7,\n  8: ImageTrailVariant8\n};\n\nexport default function ImageTrail({ items = [], variant = 1 }) {\n  const containerRef = useRef(null);\n\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    const Cls = variantMap[variant] || variantMap[1];\n    const instance = new Cls(containerRef.current);\n\n    return () => {\n      instance.destroy();\n    };\n  }, [variant, items]);\n\n  return (\n    <div className=\"w-full h-full relative z-[100] rounded-lg bg-transparent overflow-visible\" ref={containerRef}>\n      {items.map((url, i) => (\n        <div\n          className=\"content__img w-[190px] aspect-[1.1] rounded-[15px] absolute top-0 left-0 opacity-0 overflow-hidden [will-change:transform,filter]\"\n          key={i}\n        >\n          <div\n            className=\"content__img-inner bg-center bg-cover w-[calc(100%+20px)] h-[calc(100%+20px)] absolute top-[-10px] left-[-10px]\"\n            style={{ backgroundImage: `url(${url})` }}\n          />\n        </div>\n      ))}\n    </div>\n  );\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"gsap@^3.13.0"
	]
}