<script lang="ts">
	// Titre cinétique : révélation mot à mot au scroll, cadence syncopée gwoka.
	// Split par MOTS (jamais par caractères — apostrophes). Le texte reste intégral
	// pour les lecteurs d'écran ; l'animation ne s'active que sous html.has-js + no-preference.
	import { syncopatedDelay } from '$lib/motion/tokens';
	import type { Action } from 'svelte/action';

	interface Props {
		text: string;
		as?: 'h1' | 'h2' | 'h3';
		id?: string;
		class?: string;
	}
	let { text, as = 'h2', id, class: cls = '' }: Props = $props();
	const mots = $derived(text.split(' '));

	const kinetic: Action = (node) => {
		const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
		if (reduce) {
			node.classList.add('is-in');
			return;
		}
		const io = new IntersectionObserver(
			(entries) => {
				for (const e of entries) {
					if (e.isIntersecting) {
						node.classList.add('is-in');
						io.unobserve(node);
					}
				}
			},
			{ threshold: 0.4 }
		);
		io.observe(node);
		return { destroy: () => io.disconnect() };
	};
</script>

<svelte:element this={as} {id} class="kinetic {cls}" use:kinetic>
	{#each mots as mot, i (i)}
		<span class="kinetic__w"><span class="kinetic__i" style:--d="{syncopatedDelay(i)}ms">{mot}</span></span>{' '}
	{/each}
</svelte:element>

<style>
	.kinetic__w {
		display: inline-block;
		overflow: hidden;
		vertical-align: top;
	}
	.kinetic__i {
		display: inline-block;
	}
	:global(html.has-js) .kinetic:not(.is-in) .kinetic__i {
		transform: translateY(105%);
	}
	.kinetic.is-in .kinetic__i {
		transform: translateY(0);
		transition: transform var(--dur-mesure) var(--ease-ka);
		transition-delay: var(--d, 0ms);
	}
	@media (prefers-reduced-motion: reduce) {
		:global(html.has-js) .kinetic .kinetic__i {
			transform: none;
		}
	}
</style>
