// ScrollTextAnimator.js

// Initialize Lenis smooth scrolling
let lenis;

/**
 * @param {string|undefined} raw data-animation-start (e.g. "75%").
 * @return {number} Viewport percentage from top, 0–100.
 */
function dsmParseViewportStartPercent( raw ) {
	const n = parseFloat( String( raw ?? '100' ).replace( /%/g, '' ) );
	if ( ! Number.isFinite( n ) ) {
		return 100;
	}
	return Math.min( 100, Math.max( 0, n ) );
}

/**
 * @param {string} startStr ScrollTrigger start (trigger part + scroller part).
 * @param {number} viewportPercent Scroller line: 0% top of viewport, 100% bottom.
 * @return {string}
 */
function dsmApplyScrollStartViewportPercent( startStr, viewportPercent ) {
	const trimmed = String( startStr ).trim();
	const i = trimmed.indexOf( ' ' );
	if ( i === -1 ) {
		return trimmed;
	}
	return `${ trimmed.slice( 0, i ) } ${ viewportPercent }%`;
}

class ScrollTextAnimator {
	constructor( selector = '.dsm-on-scroll-header-animation-wrapper' ) {
		this._viewportStartPercent = 100;
		this.elements = document.querySelectorAll( selector );
		this.lettersAndSymbols = [
			'a',
			'b',
			'c',
			'd',
			'e',
			'f',
			'g',
			'h',
			'i',
			'j',
			'k',
			'l',
			'm',
			'n',
			'o',
			'p',
			'q',
			'r',
			's',
			't',
			'u',
			'v',
			'w',
			'x',
			'y',
			'z',
			'!',
			'@',
			'#',
			'$',
			'%',
			'^',
			'&',
			'*',
			'-',
			'_',
			'+',
			'=',
			';',
			':',
			'<',
			'>',
			',',
		];
		this.initLenis();
		this.init();
	}

	initLenis() {
		// Initialize Lenis smooth scroll
		lenis = new Lenis( {
			duration: 1.2,
			easing: ( t ) => Math.min( 1, 1.001 - Math.pow( 2, -10 * t ) ),
			orientation: 'vertical',
			gestureOrientation: 'vertical',
			smoothWheel: true,
			wheelMultiplier: 1,
			smoothTouch: false,
			touchMultiplier: 2,
			infinite: false,
		} );

		// Connect Lenis to GSAP ScrollTrigger
		lenis.on( 'scroll', ScrollTrigger.update );

		// Set up the RAF loop
		gsap.ticker.add( ( time ) => {
			lenis.raf( time * 1000 );
		} );

		// Override the default ScrollTrigger refresh behavior
		gsap.ticker.lagSmoothing( 0 );
	}

	init() {
		// Check if required libraries are loaded
		if ( ! this.checkRequiredLibraries() ) {
			return;
		}

		gsap.registerPlugin( ScrollTrigger );

		// Loop through each element to apply animation
		this.elements.forEach( ( wrapper ) => {
			const title = wrapper.querySelector( '.dsm-title' );
			if ( ! title ) return;

			this._viewportStartPercent = dsmParseViewportStartPercent(
				wrapper.dataset.animationStart
			);

			// Get animation effect type
			const effectType = wrapper.dataset.effect || 'effect_1';

			// Split text and prepare for animation
			const chars = this.splitTextIntoChars( title );
			if ( ! chars || chars.length === 0 ) return;

			// Apply animation based on effect type
			this.animateChars( chars, title, effectType );
		} );
	}

	checkRequiredLibraries() {
		if (
			typeof gsap === 'undefined' ||
			typeof ScrollTrigger === 'undefined' ||
			typeof Splitting === 'undefined' ||
			typeof Lenis === 'undefined'
		) {
			console.warn(
				'Required libraries (GSAP, ScrollTrigger, Splitting, or Lenis) are not loaded.'
			);
			return false;
		}
		return true;
	}

	splitTextIntoChars( element ) {
		// First split into words
		Splitting( {
			target: element,
			by: 'words',
			whitespace: true,
		} );

		// Get all words after splitting
		const words = element.querySelectorAll( '.word' );
		if ( ! words.length ) {
			console.warn( 'No words found after splitting:', element );
			return null;
		}

		// Process each word and split into characters
		words.forEach( ( word, wordIndex ) => {
			this.processWord( word, wordIndex );
		} );

		// Return all characters for animation
		return element.querySelectorAll( '.char' );
	}

	processWord( word, wordIndex ) {
		// Set word attributes and data
		word.setAttribute( 'data-word', word.textContent );
		word.style.setProperty( '--word-index', wordIndex );

		// Split the word into characters
		const chars = word.textContent
			.split( '' )
			.map( ( char, charIndex ) => {
				return `<span class="char" data-char="${ char }" style="--char-index: ${ charIndex };">${ char }</span>`;
			} )
			.join( '' );

		word.innerHTML = chars;
	}

	// Utility function to wrap elements
	wrapElements( elements, wrapperTag, className ) {
		elements.forEach( ( el ) => {
			const wrapper = document.createElement( wrapperTag );
			wrapper.className = className;
			el.parentNode.insertBefore( wrapper, el );
			wrapper.appendChild( el );
		} );
	}

	getAnimationConfig( effectType ) {
		const st = ( s ) =>
			dsmApplyScrollStartViewportPercent( s, this._viewportStartPercent );

		// Basic configurations that don't need special handling
		const basicConfigs = {
			twist_reveal: {
				from: {
					'will-change': 'opacity, transform',
					opacity: 0,
					scale: 0.6,
					rotationZ: () => gsap.utils.random( -20, 20 ),
				},
				to: {
					ease: 'power4',
					opacity: 1,
					scale: 1,
					rotation: 0,
					stagger: 0.4,
					scrollTrigger: {
						start: st('center+=20% bottom'),
						end: '+=50%',
						scrub: true,
					},
				},
			},
			stretch_up: {
				from: {
					'will-change': 'opacity, transform',
					opacity: 0,
					yPercent: 120,
					scaleY: 2.3,
					scaleX: 0.7,
					transformOrigin: '50% 0%',
				},
				to: {
					duration: 1,
					ease: 'back.inOut(2)',
					opacity: 1,
					yPercent: 0,
					scaleY: 1,
					scaleX: 1,
					stagger: 0.03,
					scrollTrigger: {
						start: st('center bottom+=50%'),
						end: 'bottom top+=40%',
						scrub: true,
					},
				},
			},
			stretch_down: {
				from: {
					'will-change': 'transform',
					transformOrigin: '50% 0%',
					scaleY: 0,
				},
				to: {
					ease: 'back',
					opacity: 1,
					scaleY: 1,
					yPercent: 0,
					stagger: 0.03,
					scrollTrigger: {
						start: st('center bottom-=5%'),
						end: 'top top-=20%',
						scrub: true,
					},
				},
			},
			split_merge: {
				from: {
					'will-change': 'opacity, transform',
					x: ( position, _, arr ) =>
						150 * ( position - arr.length / 2 ),
				},
				to: {
					ease: 'power1.inOut',
					x: 0,
					stagger: {
						grid: 'auto',
						from: 'center',
					},
					scrollTrigger: {
						start: st('center bottom+=30%'),
						end: 'top top+=15%',
						scrub: true,
					},
				},
			},
			letter_cross_in: {
				from: {
					'will-change': 'opacity, transform',
					opacity: 0,
					xPercent: () => gsap.utils.random( -200, 200 ),
					yPercent: () => gsap.utils.random( -150, 150 ),
				},
				to: {
					ease: 'power1.inOut',
					opacity: 1,
					xPercent: 0,
					yPercent: 0,
					stagger: {
						each: 0.05,
						grid: 'auto',
						from: 'random',
					},
					scrollTrigger: {
						start: st('center bottom+=10%'),
						end: 'bottom center',
						scrub: 0.9,
					},
				},
			},
			flip_rise: {
				from: {
					'will-change': 'opacity, transform',
					opacity: 0,
					rotationX: -90,
					yPercent: 50,
				},
				to: {
					ease: 'power1.inOut',
					opacity: 1,
					rotationX: 0,
					yPercent: 0,
					stagger: {
						each: 0.03,
						from: 0,
					},
					scrollTrigger: {
						start: st('center bottom+=40%'),
						end: 'bottom center-=30%',
						scrub: 0.9,
					},
				},
			},
			random_blur_reveal: {
				from: {
					'will-change': 'opacity',
					opacity: 0,
					filter: 'blur(20px)',
				},
				to: {
					duration: 0.25,
					ease: 'power1.inOut',
					opacity: 1,
					filter: 'blur(0px)',
					stagger: {
						each: 0.05,
						from: 'random',
					},
					scrollTrigger: {
						start: st('top bottom'),
						end: 'center center',
						toggleActions: 'play resume resume reset',
					},
				},
			},
		};

		// Special effect handlers that need custom logic
		const specialEffects = {
			word_by_word_corner_scale: ( chars, trigger ) => {
				const words = [ ...trigger.querySelectorAll( '.word' ) ];

				for ( const [ pos, word ] of words.entries() ) {
					const chars = word.querySelectorAll( '.char' );

					gsap.fromTo(
						chars,
						{
							'will-change': 'transform',
							transformOrigin: `${ pos % 2 ? 0 : 100 }% ${
								pos % 2 ? 100 : 0
							}%`,
							scale: 0,
						},
						{
							ease: 'power4',
							scale: 1,
							stagger: {
								each: 0.03,
								from: pos % 2 ? 'end' : 'start',
							},
							scrollTrigger: {
								trigger: word,
								start: st('top bottom-=10%'),
								end: 'top top',
								scrub: true,
							},
						}
					);
				}
			},
			character_wave: ( chars, trigger ) => {
				const words = [ ...trigger.querySelectorAll( '.word' ) ];

				for ( const word of words ) {
					const chars = word.querySelectorAll( '.char' );
					const charsTotal = chars.length;

					gsap.fromTo(
						chars,
						{
							'will-change': 'transform, filter',
							transformOrigin: '50% 100%',
							scale: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return gsap.utils.mapRange(
									0,
									Math.ceil( charsTotal / 2 ),
									0.5,
									2.1,
									factor
								);
							},
							y: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return gsap.utils.mapRange(
									0,
									Math.ceil( charsTotal / 2 ),
									0,
									60,
									factor
								);
							},
							rotation: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return position < charsTotal / 2
									? gsap.utils.mapRange(
											0,
											Math.ceil( charsTotal / 2 ),
											-4,
											0,
											factor
									  )
									: gsap.utils.mapRange(
											0,
											Math.ceil( charsTotal / 2 ),
											0,
											4,
											factor
									  );
							},
							filter: 'blur(12px) opacity(0)',
						},
						{
							ease: 'power2.inOut',
							y: 0,
							rotation: 0,
							scale: 1,
							filter: 'blur(0px) opacity(1)',
							scrollTrigger: {
								trigger: word,
								start: st('top bottom+=40%'),
								end: 'top top+=15%',
								scrub: true,
							},
							stagger: {
								amount: 0.15,
								from: 'center',
							},
						}
					);
				}
			},
			elastic_reveal: ( chars, trigger ) => {
				const charsTotal = chars.length;
				gsap.fromTo(
					chars,
					{
						'will-change': 'transform',
						y: ( position ) => {
							const factor =
								position < Math.ceil( charsTotal / 2 )
									? position
									: Math.ceil( charsTotal / 2 ) -
									  Math.abs(
											Math.floor( charsTotal / 2 ) -
												position
									  ) -
									  1;
							return ( charsTotal / 2 - factor + 6 ) * 130;
						},
					},
					{
						ease: 'elastic.out(.4)',
						y: 0,
						stagger: {
							amount: 0.1,
							from: 'center',
						},
						scrollTrigger: {
							trigger: trigger,
							start: st('top bottom'),
							end: 'bottom top+=10%',
							scrub: true,
						},
					}
				);
			},

			reverse_flip: ( chars, trigger ) => {
				const words = trigger.querySelectorAll( '.word' );
				for ( const word of words ) {
					const wordChars = word.querySelectorAll( '.char' );
					wordChars.forEach( ( char ) =>
						gsap.set( char.parentNode, { perspective: 2000 } )
					);

					gsap.fromTo(
						wordChars,
						{
							'will-change': 'opacity, transform',
							transformOrigin: '100% 50%',
							opacity: 0,
							rotationY: -90,
							z: -300,
						},
						{
							ease: 'expo',
							opacity: 1,
							rotationY: 0,
							z: 0,
							stagger: { each: 0.06, from: 'end' },
							scrollTrigger: {
								trigger: word,
								start: st('bottom bottom+=20%'),
								end: 'bottom top',
								scrub: 1,
							},
						}
					);
				}
			},

			word_by_word_scale_in: ( chars, trigger ) => {
				const words = [ ...trigger.querySelectorAll( '.word' ) ];

				for ( const [ wordPosition, word ] of words.entries() ) {
					gsap.fromTo(
						word.querySelectorAll( '.char' ),
						{
							'will-change': 'transform',
							scale: 0.01,
							x: ( pos, _, arr ) => {
								return wordPosition % 2
									? pos * 50
									: ( arr.length - pos - 1 ) * -50;
							},
						},
						{
							ease: 'power4',
							scale: 1,
							x: 0,
							scrollTrigger: {
								trigger: word,
								start: st('center bottom-=20%'),
								end: 'bottom top+=20%',
								scrub: true,
							},
						}
					);
				}
			},

			word_by_word_curve_reveal: ( chars, trigger ) => {
				const words = [ ...trigger.querySelectorAll( '.word' ) ];

				for ( const word of words ) {
					const chars = word.querySelectorAll( '.char' );
					const charsTotal = chars.length;

					chars.forEach( ( char ) =>
						gsap.set( char.parentNode, { perspective: 1000 } )
					);

					gsap.fromTo(
						chars,
						{
							'will-change': 'transform',
							x: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return (
									( charsTotal % 2
										? Math.abs(
												Math.ceil( charsTotal / 2 ) -
													1 -
													factor
										  )
										: Math.abs(
												Math.ceil( charsTotal / 2 ) -
													factor
										  ) ) *
									200 *
									( position < charsTotal / 2 ? -1 : 1 )
								);
							},
							y: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return factor * 60;
							},
							rotationY: -270,
							rotationZ: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return position < charsTotal / 2
									? Math.abs( factor - charsTotal / 2 ) * 8
									: -1 *
											Math.abs(
												factor - charsTotal / 2
											) *
											8;
							},
						},
						{
							ease: 'power2.inOut',
							x: 0,
							y: 0,
							rotationZ: 0,
							rotationY: 0,
							scale: 1,
							scrollTrigger: {
								trigger: word,
								start: st('top bottom-=10%'),
								end: 'top top+=45%',
								scrub: true,
							},
						}
					);
				}
			},

			word_by_word_3d_reveal: ( chars, trigger ) => {
				const words = [ ...trigger.querySelectorAll( '.word' ) ];

				for ( const word of words ) {
					const chars = word.querySelectorAll( '.char' );

					chars.forEach( ( char ) =>
						gsap.set( char.parentNode, { perspective: 2000 } )
					);

					gsap.fromTo(
						chars,
						{
							'will-change': 'opacity, transform',
							opacity: 0,
							y: ( position, _, arr ) =>
								-40 * Math.abs( position - arr.length / 2 ),
							z: () => gsap.utils.random( -1500, -600 ),
							rotationX: () => gsap.utils.random( -500, -200 ),
						},
						{
							ease: 'power1.inOut',
							opacity: 1,
							y: 0,
							z: 0,
							rotationX: 0,
							stagger: {
								each: 0.06,
								from: 'center',
							},
							scrollTrigger: {
								trigger: word,
								start: st('top bottom'),
								end: 'top top+=45%',
								scrub: true,
							},
						}
					);
				}
			},

			word_by_word_curve_reveal: ( chars, trigger ) => {
				const words = [ ...trigger.querySelectorAll( '.word' ) ];

				for ( const word of words ) {
					const chars = word.querySelectorAll( '.char' );
					const charsTotal = chars.length;

					chars.forEach( ( char ) =>
						gsap.set( char.parentNode, { perspective: 1000 } )
					);

					gsap.fromTo(
						chars,
						{
							'will-change': 'transform',
							x: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return (
									( charsTotal % 2
										? Math.abs(
												Math.ceil( charsTotal / 2 ) -
													1 -
													factor
										  )
										: Math.abs(
												Math.ceil( charsTotal / 2 ) -
													factor
										  ) ) *
									200 *
									( position < charsTotal / 2 ? -1 : 1 )
								);
							},
							y: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return factor * 60;
							},
							rotationY: -270,
							rotationZ: ( position ) => {
								const factor =
									position < Math.ceil( charsTotal / 2 )
										? position
										: Math.ceil( charsTotal / 2 ) -
										  Math.abs(
												Math.floor( charsTotal / 2 ) -
													position
										  ) -
										  1;
								return position < charsTotal / 2
									? Math.abs( factor - charsTotal / 2 ) * 8
									: -1 *
											Math.abs(
												factor - charsTotal / 2
											) *
											8;
							},
						},
						{
							ease: 'power2.inOut',
							x: 0,
							y: 0,
							rotationZ: 0,
							rotationY: 0,
							scale: 1,
							scrollTrigger: {
								trigger: word,
								start: st('top bottom-=10%'),
								end: 'top top+=45%',
								scrub: true,
							},
						}
					);
				}
			},

			random_flip_up_reveal: ( chars, trigger ) => {
				chars.forEach( ( char ) =>
					gsap.set( char.parentNode, { perspective: 1000 } )
				);

				gsap.fromTo(
					chars,
					{
						'will-change': 'opacity, transform',
						transformOrigin: '50% 100%',
						opacity: 0,
						rotationX: 90,
					},
					{
						ease: 'power4',
						opacity: 1,
						stagger: {
							each: 0.03,
							from: 'random',
						},
						rotationX: 0,
						scrollTrigger: {
							trigger: trigger,
							start: st('center bottom'),
							end: 'bottom top+=20%',
							scrub: true,
						},
					}
				);
			},

			zoom_z_reveal: ( chars, trigger ) => {
				chars.forEach( ( char ) =>
					gsap.set( char.parentNode, { perspective: 1000 } )
				);
				gsap.fromTo(
					chars,
					{
						'will-change': 'opacity, transform',
						opacity: 0.2,
						z: -800,
					},
					{
						ease: 'back.out(1.2)',
						opacity: 1,
						z: 0,
						stagger: 0.04,
						scrollTrigger: {
							trigger: trigger,
							start: st('top bottom'),
							end: 'bottom top',
							scrub: true,
						},
					}
				);
			},

			random_rotateX_reveal: ( chars, trigger ) => {
				chars.forEach( ( char ) =>
					gsap.set( char.parentNode, { perspective: 1000 } )
				);
				gsap.fromTo(
					chars,
					{
						'will-change': 'opacity, transform',
						opacity: 0,
						rotateX: () => gsap.utils.random( -120, 120 ),
						z: () => gsap.utils.random( -200, 200 ),
					},
					{
						ease: 'none',
						opacity: 1,
						rotateX: 0,
						z: 0,
						stagger: 0.02,
						scrollTrigger: {
							trigger: trigger,
							start: st('top bottom'),
							end: 'bottom top+=30%',
							scrub: true,
						},
					}
				);
			},

			rotate_fade: ( chars, trigger ) => {
				// First animation for the entire trigger element
				gsap.fromTo(
					trigger,
					{
						transformOrigin: '0% 50%',
						rotate: 3,
					},
					{
						ease: 'none',
						rotate: 0,
						scrollTrigger: {
							trigger: trigger,
							start: st('top bottom'),
							end: 'top top',
							scrub: true,
						},
					}
				);

				// Second animation for words
				gsap.fromTo(
					trigger.querySelectorAll( '.word' ),
					{
						'will-change': 'opacity',
						opacity: 0.1,
					},
					{
						ease: 'none',
						opacity: 1,
						stagger: 0.05,
						scrollTrigger: {
							trigger: trigger,
							start: st('top bottom-=20%'),
							end: 'center top+=40%',
							scrub: true,
						},
					}
				);
			},

			shuffle_reveal: ( chars, trigger ) => {
				chars.forEach( ( char, position ) => {
					const initialHTML = char.innerHTML;
					gsap.fromTo(
						char,
						{ opacity: 0 },
						{
							duration: 0.03,
							innerHTML: () =>
								this.lettersAndSymbols[
									Math.floor(
										Math.random() *
											this.lettersAndSymbols.length
									)
								],
							repeat: 1,
							repeatRefresh: true,
							opacity: 1,
							repeatDelay: 0.03,
							delay: ( position + 1 ) * 0.18,
							onComplete: () =>
								gsap.set( char, {
									innerHTML: initialHTML,
									delay: 0.03,
								} ),
							scrollTrigger: {
								trigger: trigger,
								start: st('top bottom'),
								end: 'bottom center',
								toggleActions: 'play resume resume reset',
								onEnter: () => gsap.set( char, { opacity: 0 } ),
							},
						}
					);
				} );
			},

			shrink_left_reveal: ( chars, trigger ) => {
				const words = trigger.querySelectorAll( '.word' );
				for ( const word of words ) {
					const wordChars = word.querySelectorAll( '.char' );
					gsap.fromTo(
						wordChars,
						{
							'will-change': 'transform',
							scaleX: 0,
							x: ( _, target ) =>
								window.innerWidth / 2 -
								target.offsetLeft -
								target.offsetWidth / 2,
						},
						{
							ease: 'power1.inOut',
							scaleX: 1,
							x: 0,
							scrollTrigger: {
								trigger: word,
								start: st('top bottom'),
								end: 'top top',
								scrub: true,
							},
						}
					);
				}
			},

			slide_rush: ( chars, trigger ) => {
				// First wrap each character in an additional wrapper
				this.wrapElements( chars, 'span', 'char-wrap' );

				gsap.fromTo(
					chars,
					{
						'will-change': 'transform',
						transformOrigin: '0% 50%',
						xPercent: 105,
					},
					{
						duration: 1,
						ease: 'expo',
						xPercent: 0,
						stagger: 0.042,
						scrollTrigger: {
							trigger: trigger,
							start: st('top bottom'),
							end: 'top top+=10%',
							toggleActions: 'play resume resume reset',
						},
					}
				);
			},

			twist_lift: ( chars, trigger ) => {
				// Set perspective on each character's parent
				chars.forEach( ( char ) =>
					gsap.set( char.parentNode, { perspective: 2000 } )
				);

				gsap.fromTo(
					chars,
					{
						'will-change': 'opacity, transform',
						opacity: 0,
						rotationY: 180,
						xPercent: -40,
						yPercent: 100,
					},
					{
						ease: 'power4.inOut()',
						opacity: 1,
						rotationY: 0,
						xPercent: 0,
						yPercent: 0,
						stagger: {
							each: -0.03,
							from: 0,
						},
						scrollTrigger: {
							trigger: trigger,
							start: st('center bottom'),
							end: 'bottom center-=30%',
							scrub: 0.9,
						},
					}
				);
			},
		};

		return {
			config: basicConfigs[ effectType ],
			specialHandler: specialEffects[ effectType ],
		};
	}

	animateChars( chars, trigger, effectType ) {
		const { config, specialHandler } =
			this.getAnimationConfig( effectType );

		// If this effect has a special handler, use it
		if ( specialHandler ) {
			specialHandler( chars, trigger );
			return;
		}

		// For basic effects, use the standard configuration
		if ( config ) {
			const scrollTriggerConfig = {
				...( config.to.scrollTrigger || {} ),
				trigger: trigger,
			};

			gsap.fromTo( chars, config.from, {
				...config.to,
				scrollTrigger: scrollTriggerConfig,
			} );
		}
	}
}

// Initialize when DOM is ready
document.addEventListener( 'DOMContentLoaded', () => {
	new ScrollTextAnimator();
} );
