@drivexstudio/animations 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1862 @@
1
+ "use client";
2
+
3
+ // packages/animations/ScrambleText.jsx
4
+ import React4, { useEffect as useEffect4, useRef as useRef3 } from "react";
5
+
6
+ // libs/vendor.js
7
+ import gsap from "gsap";
8
+ import { useGSAP } from "@gsap/react";
9
+ import { ScrollTrigger } from "gsap/ScrollTrigger";
10
+ import { Flip } from "gsap/Flip";
11
+ import { SplitText } from "gsap/SplitText";
12
+ import { ScrambleTextPlugin } from "gsap/ScrambleTextPlugin";
13
+ import { InertiaPlugin } from "gsap/InertiaPlugin";
14
+ import { Draggable } from "gsap/Draggable";
15
+ import { cx as classVarianceCx } from "class-variance-authority";
16
+ import { cva } from "class-variance-authority";
17
+ gsap.registerPlugin(
18
+ ScrollTrigger,
19
+ ScrambleTextPlugin,
20
+ SplitText,
21
+ Flip,
22
+ Draggable,
23
+ InertiaPlugin
24
+ );
25
+ var cx = classVarianceCx;
26
+
27
+ // packages/animations/ScrambleGroup.jsx
28
+ import React3, { createContext as createContext2, useContext as useContext2, useRef as useRef2, useState as useState3, useCallback as useCallback3, useMemo as useMemo4 } from "react";
29
+
30
+ // libs/hooks/useIdleGSAP.js
31
+ import { useState as useState2, useEffect as useEffect3, useCallback as useCallback2, useMemo as useMemo3 } from "react";
32
+ import { useGSAP as useGSAP2 } from "@gsap/react";
33
+
34
+ // libs/hooks/usePageTransition.js
35
+ import { useMemo as useMemo2 } from "react";
36
+
37
+ // packages/providers/PageTransitionProvider.jsx
38
+ import React2, {
39
+ createContext,
40
+ useState,
41
+ useTransition,
42
+ useRef,
43
+ useCallback,
44
+ useEffect as useEffect2,
45
+ useMemo,
46
+ useContext
47
+ } from "react";
48
+
49
+ // packages/providers/LenisProvider.jsx
50
+ import React, { useEffect } from "react";
51
+ import { ReactLenis, useLenis } from "lenis/react";
52
+ import { jsx, jsxs } from "react/jsx-runtime";
53
+ var globalLenisInstance = null;
54
+ var isCssScrollLocked = false;
55
+ function getLenis() {
56
+ return globalLenisInstance;
57
+ }
58
+ function setCssScrollLocked(isLocked) {
59
+ if (isLocked !== isCssScrollLocked) {
60
+ isCssScrollLocked = isLocked;
61
+ if (isLocked) {
62
+ document.documentElement.classList.add("scroll-locked");
63
+ } else {
64
+ document.documentElement.classList.remove("scroll-locked");
65
+ }
66
+ }
67
+ }
68
+ function getCssScrollLocked() {
69
+ return isCssScrollLocked;
70
+ }
71
+ function scrollToTop(immediate = true) {
72
+ const lenis = globalLenisInstance;
73
+ if (lenis) {
74
+ lenis.scrollTo(0, { immediate });
75
+ } else {
76
+ window.scrollTo(0, 0);
77
+ }
78
+ }
79
+ function LenisTracker() {
80
+ const lenis = useLenis();
81
+ useEffect(() => {
82
+ globalLenisInstance = lenis ?? null;
83
+ return () => {
84
+ globalLenisInstance = null;
85
+ };
86
+ }, [lenis]);
87
+ return null;
88
+ }
89
+ function customEasing(t) {
90
+ return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
91
+ }
92
+ function Lenis(props) {
93
+ return /* @__PURE__ */ jsxs(
94
+ ReactLenis,
95
+ {
96
+ root: true,
97
+ options: {
98
+ anchors: {
99
+ duration: 1.2,
100
+ easing: customEasing
101
+ }
102
+ },
103
+ ...props,
104
+ children: [
105
+ /* @__PURE__ */ jsx(LenisTracker, {}),
106
+ props.children
107
+ ]
108
+ }
109
+ );
110
+ }
111
+
112
+ // packages/providers/PageTransitionProvider.jsx
113
+ import { jsx as jsx2 } from "react/jsx-runtime";
114
+ var PageTransitionContext = createContext(null);
115
+ var scrollTriggerModulePromise = null;
116
+ function getScrollTriggerModule() {
117
+ if (!scrollTriggerModulePromise) {
118
+ scrollTriggerModulePromise = import("gsap/ScrollTrigger");
119
+ }
120
+ return scrollTriggerModulePromise;
121
+ }
122
+ function clearScrollMemory() {
123
+ const modulePromise = getScrollTriggerModule();
124
+ if (modulePromise) {
125
+ modulePromise.then(({ ScrollTrigger: ScrollTrigger2 }) => {
126
+ ScrollTrigger2.clearScrollMemory();
127
+ });
128
+ }
129
+ }
130
+ function refreshScrollTrigger() {
131
+ const modulePromise = getScrollTriggerModule();
132
+ if (modulePromise) {
133
+ modulePromise.then(({ ScrollTrigger: ScrollTrigger2 }) => {
134
+ ScrollTrigger2.refresh(true);
135
+ });
136
+ }
137
+ }
138
+ function PageTransitionProvider({ children }) {
139
+ const [phase, setPhase] = useState("idle");
140
+ const [isPending, startTransitionReact] = useTransition();
141
+ const timerRef = useRef(null);
142
+ const pendingCallbackRef = useRef(null);
143
+ const startTimeRef = useRef(0);
144
+ const clearTimer = useCallback(() => {
145
+ if (timerRef.current) {
146
+ clearTimeout(timerRef.current);
147
+ timerRef.current = null;
148
+ }
149
+ }, []);
150
+ const handlePopState = useCallback(() => {
151
+ clearTimer();
152
+ if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
153
+ startTimeRef.current = 0;
154
+ pendingCallbackRef.current = null;
155
+ setPhase("holding");
156
+ clearScrollMemory();
157
+ timerRef.current = window.setTimeout(() => {
158
+ setPhase("exiting");
159
+ timerRef.current = window.setTimeout(() => {
160
+ setPhase("idle");
161
+ refreshScrollTrigger();
162
+ }, 1200);
163
+ }, 100);
164
+ }
165
+ }, [clearTimer]);
166
+ const startTransition = useCallback((callback) => {
167
+ clearTimer();
168
+ pendingCallbackRef.current = callback;
169
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
170
+ callback();
171
+ } else {
172
+ setPhase("entering");
173
+ timerRef.current = window.setTimeout(() => {
174
+ startTimeRef.current = Date.now();
175
+ setPhase("holding");
176
+ clearScrollMemory();
177
+ scrollToTop();
178
+ startTransitionReact(() => {
179
+ var _a;
180
+ (_a = pendingCallbackRef.current) == null ? void 0 : _a.call(pendingCallbackRef);
181
+ });
182
+ }, 1300);
183
+ }
184
+ }, [clearTimer, startTransitionReact]);
185
+ useEffect2(() => {
186
+ if (phase === "holding" && !isPending && startTimeRef.current > 0) {
187
+ const elapsed = Date.now() - startTimeRef.current;
188
+ const remainingDelay = Math.max(0, 100 - elapsed);
189
+ timerRef.current = window.setTimeout(() => {
190
+ setPhase("exiting");
191
+ startTimeRef.current = 0;
192
+ timerRef.current = window.setTimeout(() => {
193
+ setPhase("idle");
194
+ refreshScrollTrigger();
195
+ }, 1200);
196
+ }, remainingDelay);
197
+ }
198
+ }, [phase, isPending]);
199
+ useEffect2(() => {
200
+ const onPopState = () => {
201
+ handlePopState();
202
+ };
203
+ window.addEventListener("popstate", onPopState);
204
+ return () => {
205
+ window.removeEventListener("popstate", onPopState);
206
+ };
207
+ }, [handlePopState]);
208
+ useEffect2(() => {
209
+ return () => {
210
+ clearTimer();
211
+ };
212
+ }, [clearTimer]);
213
+ const contextValue = useMemo(() => ({
214
+ phase,
215
+ startTransition,
216
+ isPending
217
+ }), [phase, startTransition, isPending]);
218
+ return /* @__PURE__ */ jsx2(PageTransitionContext.Provider, { value: contextValue, children });
219
+ }
220
+ var defaultContextValue = {
221
+ phase: "idle",
222
+ startTransition: (t) => t(),
223
+ isPending: false
224
+ };
225
+ function usePageTransitionContext() {
226
+ return useContext(PageTransitionContext) ?? defaultContextValue;
227
+ }
228
+
229
+ // libs/hooks/usePageTransition.js
230
+ function usePageTransition() {
231
+ const {
232
+ startTransition,
233
+ phase,
234
+ isPending
235
+ } = usePageTransitionContext();
236
+ const isTransitioning = phase !== "idle";
237
+ const transitionState = useMemo2(() => ({
238
+ startTransition,
239
+ isTransitioning,
240
+ phase,
241
+ isPending
242
+ }), [isPending, phase, startTransition, isTransitioning]);
243
+ return transitionState;
244
+ }
245
+
246
+ // libs/hooks/useIdleGSAP.js
247
+ function useIdleGSAP(callback, config) {
248
+ const { phase } = usePageTransition();
249
+ const [isIdle, setIsIdle] = useState2(phase === "idle");
250
+ useEffect3(() => {
251
+ if (phase === "idle") {
252
+ setIsIdle(true);
253
+ } else if (phase === "holding") {
254
+ setIsIdle(false);
255
+ }
256
+ }, [phase]);
257
+ const wrappedCallback = useCallback2((context, contextSafe) => {
258
+ if (isIdle) {
259
+ return callback(context, contextSafe);
260
+ }
261
+ }, [callback, isIdle]);
262
+ const gsapConfig = useMemo3(() => {
263
+ const configDependencies = (config == null ? void 0 : config.dependencies) ?? [];
264
+ const combinedDependencies = [...configDependencies, isIdle];
265
+ return {
266
+ ...config,
267
+ dependencies: combinedDependencies
268
+ };
269
+ }, [config, isIdle]);
270
+ return useGSAP2(wrappedCallback, gsapConfig);
271
+ }
272
+
273
+ // packages/animations/ScrambleGroup.jsx
274
+ import { jsx as jsx3 } from "react/jsx-runtime";
275
+ var ScrambleContext = createContext2(null);
276
+ function ScrambleGroup({
277
+ children,
278
+ stagger: stagger2 = 0.1,
279
+ start = "top 80%",
280
+ markers = false,
281
+ manual = false,
282
+ className
283
+ }) {
284
+ const childrenMapRef = useRef2(/* @__PURE__ */ new Map());
285
+ const [hasTriggered, setHasTriggered] = useState3(false);
286
+ const isTriggering = useRef2(false);
287
+ const scrollTriggerRef = useRef2(null);
288
+ const containerRef = useRef2(null);
289
+ const register = useCallback3((id, callback) => {
290
+ childrenMapRef.current.set(id, callback);
291
+ }, []);
292
+ const unregister = useCallback3((id) => {
293
+ childrenMapRef.current.delete(id);
294
+ }, []);
295
+ const triggerAll = useCallback3((customStagger) => {
296
+ if (isTriggering.current) return;
297
+ isTriggering.current = true;
298
+ const callbacks = Array.from(childrenMapRef.current.values());
299
+ const staggerValue = customStagger ?? stagger2;
300
+ callbacks.forEach((callbackFn, index) => {
301
+ gsap.delayedCall(index * staggerValue, () => {
302
+ callbackFn();
303
+ });
304
+ });
305
+ setHasTriggered(true);
306
+ }, [stagger2]);
307
+ const setupScrollTrigger = useCallback3(() => {
308
+ if (manual || !containerRef.current) return;
309
+ isTriggering.current = false;
310
+ const tween = gsap.to(containerRef.current, {
311
+ scrollTrigger: {
312
+ trigger: containerRef.current,
313
+ start,
314
+ markers,
315
+ toggleActions: "play none none none"
316
+ },
317
+ onStart: () => {
318
+ triggerAll();
319
+ },
320
+ duration: 1e-3
321
+ });
322
+ scrollTriggerRef.current = tween.scrollTrigger ?? null;
323
+ return () => {
324
+ var _a;
325
+ tween.kill();
326
+ (_a = scrollTriggerRef.current) == null ? void 0 : _a.kill();
327
+ };
328
+ }, [manual, start, markers, triggerAll]);
329
+ useIdleGSAP(setupScrollTrigger, {
330
+ dependencies: [manual, start, markers, triggerAll]
331
+ });
332
+ const contextValue = useMemo4(() => ({
333
+ register,
334
+ unregister,
335
+ triggerAll,
336
+ hasTriggered
337
+ }), [register, unregister, triggerAll, hasTriggered]);
338
+ return /* @__PURE__ */ jsx3(ScrambleContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx3("div", { ref: containerRef, className, children }) });
339
+ }
340
+ function useScrambleGroup() {
341
+ return useContext2(ScrambleContext);
342
+ }
343
+
344
+ // libs/constants/chars.js
345
+ var DEFAULT_CHARS = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
346
+
347
+ // packages/animations/ScrambleText.jsx
348
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
349
+ function ScrambleText({
350
+ children,
351
+ className,
352
+ duration = 0.6,
353
+ chars = DEFAULT_CHARS,
354
+ dualLayer = true,
355
+ triggerOnHover = false,
356
+ revealMode = false,
357
+ theme = "dark",
358
+ firstColorClass,
359
+ secondColorClass,
360
+ onComplete,
361
+ onReady,
362
+ multiLine = false
363
+ }) {
364
+ const containerRef = useRef3(null);
365
+ const textElementRef = useRef3(null);
366
+ const timelineRef = useRef3(null);
367
+ const internalTextRef = useRef3("");
368
+ const isCompleteRef = useRef3(false);
369
+ const instanceIdRef = useRef3(`scramble-${Math.random().toString(36).slice(2, 9)}`);
370
+ const themeColors = theme === "brand" ? { firstColorClass: "scramble-white", secondColorClass: "scramble-foreground" } : { firstColorClass: "scramble-brand", secondColorClass: "scramble-foreground" };
371
+ const color1 = firstColorClass ?? themeColors.firstColorClass;
372
+ const color2 = secondColorClass ?? themeColors.secondColorClass;
373
+ const scrambleGroup = useScrambleGroup();
374
+ let childText;
375
+ if (typeof children === "string") {
376
+ childText = children;
377
+ } else if (typeof children === "number") {
378
+ childText = String(children);
379
+ } else {
380
+ childText = "";
381
+ }
382
+ useEffect4(() => {
383
+ internalTextRef.current = childText;
384
+ }, [childText]);
385
+ const killTimeline = () => {
386
+ if (timelineRef.current) {
387
+ timelineRef.current.kill();
388
+ timelineRef.current = null;
389
+ }
390
+ };
391
+ const triggerAnimation = () => {
392
+ if (!textElementRef.current) return null;
393
+ const targetEl = textElementRef.current;
394
+ const textToScramble = internalTextRef.current || childText;
395
+ if (!textToScramble || textToScramble.length === 0) return null;
396
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
397
+ targetEl.textContent = textToScramble;
398
+ targetEl.className = targetEl.className.replace(/\bscramble-\w+\b/g, "");
399
+ isCompleteRef.current = true;
400
+ onComplete == null ? void 0 : onComplete();
401
+ return null;
402
+ }
403
+ killTimeline();
404
+ timelineRef.current = gsap.timeline({
405
+ onComplete: () => {
406
+ timelineRef.current = null;
407
+ isCompleteRef.current = true;
408
+ onComplete == null ? void 0 : onComplete();
409
+ }
410
+ });
411
+ if (dualLayer) {
412
+ const generateScrambledString = (targetString, charset = DEFAULT_CHARS) => {
413
+ let result = "";
414
+ for (let t = 0; t < targetString.length; t++) {
415
+ let char = targetString[t];
416
+ if (char === " " || char === "\n" || char === "\r") {
417
+ result += char;
418
+ } else {
419
+ result += charset[Math.floor(Math.random() * charset.length)];
420
+ }
421
+ }
422
+ return result;
423
+ };
424
+ const scrambledText = generateScrambledString(textToScramble, chars);
425
+ const nonWhitespaceLength = textToScramble.replace(/\s/g, "").length;
426
+ const stepDuration = nonWhitespaceLength > 0 ? duration / nonWhitespaceLength : 0;
427
+ if (revealMode && !isCompleteRef.current) {
428
+ targetEl.textContent = textToScramble.replace(/[^\s\n\r]/g, "\xA0");
429
+ timelineRef.current.to(targetEl, {
430
+ duration,
431
+ scrambleText: {
432
+ text: scrambledText,
433
+ chars,
434
+ speed: 1,
435
+ revealDelay: 0.1,
436
+ oldClass: color1,
437
+ newClass: color1
438
+ },
439
+ ease: "none"
440
+ });
441
+ } else {
442
+ timelineRef.current.to(targetEl, {
443
+ duration,
444
+ scrambleText: {
445
+ text: scrambledText,
446
+ chars,
447
+ speed: 1,
448
+ revealDelay: 0.1,
449
+ oldClass: color2,
450
+ newClass: color1
451
+ },
452
+ ease: "none"
453
+ });
454
+ }
455
+ timelineRef.current.to(targetEl, {
456
+ duration,
457
+ scrambleText: {
458
+ text: textToScramble,
459
+ chars,
460
+ speed: 1,
461
+ revealDelay: 0.1,
462
+ oldClass: color1,
463
+ newClass: color2
464
+ },
465
+ ease: "none"
466
+ }, stepDuration);
467
+ } else {
468
+ timelineRef.current.to(targetEl, {
469
+ duration,
470
+ scrambleText: {
471
+ text: textToScramble,
472
+ chars,
473
+ speed: 1,
474
+ revealDelay: 0.2
475
+ },
476
+ ease: "none"
477
+ });
478
+ }
479
+ return timelineRef.current;
480
+ };
481
+ useEffect4(() => {
482
+ if (scrambleGroup) {
483
+ scrambleGroup.register(instanceIdRef.current, triggerAnimation);
484
+ return () => {
485
+ scrambleGroup.unregister(instanceIdRef.current);
486
+ };
487
+ }
488
+ }, [scrambleGroup, triggerAnimation]);
489
+ useEffect4(() => {
490
+ onReady == null ? void 0 : onReady(triggerAnimation);
491
+ }, [onReady, triggerAnimation]);
492
+ const handleMouseEnter = () => {
493
+ if (triggerOnHover) {
494
+ triggerAnimation();
495
+ }
496
+ };
497
+ useEffect4(() => {
498
+ return () => {
499
+ killTimeline();
500
+ };
501
+ }, []);
502
+ const initialDisplayText = revealMode ? childText.replace(/[^\s\n\r]/g, "\xA0") : childText;
503
+ const whiteSpaceValue = multiLine ? "normal" : "nowrap";
504
+ const displayValue = multiLine ? "inline" : "inline-block";
505
+ const containerStyle = {
506
+ position: "relative",
507
+ display: displayValue,
508
+ whiteSpace: whiteSpaceValue
509
+ };
510
+ const hiddenSpanStyle = {
511
+ visibility: "hidden",
512
+ whiteSpace: whiteSpaceValue
513
+ };
514
+ const animatedSpanStyle = {
515
+ position: "absolute",
516
+ top: 0,
517
+ left: 0,
518
+ whiteSpace: whiteSpaceValue,
519
+ ...multiLine ? { width: "100%" } : {}
520
+ };
521
+ return /* @__PURE__ */ jsxs2(
522
+ "span",
523
+ {
524
+ ref: containerRef,
525
+ className,
526
+ style: containerStyle,
527
+ onMouseEnter: triggerOnHover ? handleMouseEnter : void 0,
528
+ children: [
529
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: childText }),
530
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: hiddenSpanStyle, children: childText }),
531
+ /* @__PURE__ */ jsx4("span", { ref: textElementRef, "aria-hidden": "true", style: animatedSpanStyle, children: initialDisplayText })
532
+ ]
533
+ }
534
+ );
535
+ }
536
+
537
+ // packages/animations/AnimatedHeadline.jsx
538
+ import React5, { forwardRef, useRef as useRef4, useState as useState4, useLayoutEffect, useCallback as useCallback4, useImperativeHandle, Fragment } from "react";
539
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
540
+ var typographyClasses = {
541
+ display: "text-display",
542
+ h1: "text-h1",
543
+ h2: "text-h2",
544
+ h3: "text-h3",
545
+ h4: "text-h4",
546
+ h5: "text-h5",
547
+ h6: "text-h6"
548
+ };
549
+ var AnimatedHeadline = forwardRef(({
550
+ children,
551
+ as: Component = "h1",
552
+ displayAs,
553
+ className,
554
+ skip,
555
+ trigger = "manual",
556
+ wrapperClassName
557
+ }, ref) => {
558
+ const containerRef = useRef4(null);
559
+ const wrapperRef = useRef4(null);
560
+ const hasRevealedRef = useRef4(false);
561
+ const [lines, setLines] = useState4(null);
562
+ const shouldSkip = skip ?? window.matchMedia("(prefers-reduced-motion: reduce)").matches;
563
+ useLayoutEffect(() => {
564
+ if (!containerRef.current) return;
565
+ const wordNodes = containerRef.current.querySelectorAll("[data-word]");
566
+ if (wordNodes.length === 0) return;
567
+ const computedLines = [];
568
+ let currentLine = [];
569
+ let lastY = -Infinity;
570
+ let lastExplicitLine = -1;
571
+ for (const node of wordNodes) {
572
+ const top = node.getBoundingClientRect().top;
573
+ const explicitLine = Number(node.dataset.explicitLine ?? -1);
574
+ const isNewExplicit = explicitLine !== lastExplicitLine && lastExplicitLine !== -1;
575
+ if (lastY > -Infinity && top - lastY > 2 || isNewExplicit) {
576
+ computedLines.push(currentLine.join(" "));
577
+ currentLine = [];
578
+ }
579
+ currentLine.push(node.textContent || "");
580
+ lastY = top;
581
+ lastExplicitLine = explicitLine;
582
+ }
583
+ if (currentLine.length > 0) {
584
+ computedLines.push(currentLine.join(" "));
585
+ }
586
+ setLines(computedLines);
587
+ }, []);
588
+ useGSAP(() => {
589
+ if (shouldSkip || !containerRef.current || !lines) return;
590
+ const container = containerRef.current;
591
+ gsap.set(container.querySelectorAll("[data-line-inner]"), { opacity: 0 });
592
+ gsap.set(container.querySelectorAll("[data-brand-rect], [data-fg-rect]"), { scaleX: 0, transformOrigin: "left" });
593
+ }, { dependencies: [shouldSkip, lines] });
594
+ useIdleGSAP(() => {
595
+ if (trigger !== "scroll" || shouldSkip || !lines) return;
596
+ const triggerEl = wrapperRef.current || containerRef.current;
597
+ if (triggerEl) {
598
+ ScrollTrigger.create({
599
+ trigger: triggerEl,
600
+ start: "top bottom",
601
+ once: true,
602
+ onEnter: () => reveal()
603
+ });
604
+ }
605
+ }, { dependencies: [trigger, shouldSkip, lines] });
606
+ const reveal = useCallback4((delayOffset = 0) => {
607
+ if (hasRevealedRef.current || shouldSkip || !containerRef.current) return;
608
+ hasRevealedRef.current = true;
609
+ const lineNodes = containerRef.current.querySelectorAll("[data-line]");
610
+ for (let r = 0; r < lineNodes.length; r++) {
611
+ const lineNode = lineNodes[r];
612
+ if (!lineNode) continue;
613
+ const delay = delayOffset + 0.15 * r;
614
+ const lineInner = lineNode.querySelector("[data-line-inner]");
615
+ const brandRect = lineNode.querySelector("[data-brand-rect]");
616
+ const fgRect = lineNode.querySelector("[data-fg-rect]");
617
+ if (!lineInner || !brandRect || !fgRect) continue;
618
+ const rects = [brandRect, fgRect];
619
+ const tl = gsap.timeline({ delay });
620
+ tl.to(brandRect, { scaleX: 1, duration: 0.45, ease: "power3.inOut" }, 0);
621
+ tl.to(fgRect, { scaleX: 1, duration: 0.45, ease: "power3.inOut" }, 0.1);
622
+ tl.set(lineInner, { opacity: 1 }, 0.5);
623
+ tl.set(rects, { transformOrigin: "right" }, 0.5);
624
+ tl.to(fgRect, { scaleX: 0, duration: 0.45, ease: "power3.inOut" }, 0.5);
625
+ tl.to(brandRect, { scaleX: 0, duration: 0.45, ease: "power3.inOut" }, 0.6);
626
+ }
627
+ }, [shouldSkip]);
628
+ const reset = useCallback4(() => {
629
+ hasRevealedRef.current = false;
630
+ if (!containerRef.current) return;
631
+ const container = containerRef.current;
632
+ gsap.set(container.querySelectorAll("[data-line-inner]"), { opacity: 0 });
633
+ gsap.set(container.querySelectorAll("[data-brand-rect], [data-fg-rect]"), { scaleX: 0, transformOrigin: "left" });
634
+ }, []);
635
+ useImperativeHandle(ref, () => ({ reveal, reset }), [reveal, reset]);
636
+ const resolvedClassName = cx(typographyClasses[displayAs ?? Component], className);
637
+ if (!lines) {
638
+ const manualLines = children.split("\n");
639
+ const preRenderedContent = /* @__PURE__ */ jsx5(Component, { ref: containerRef, className: resolvedClassName, children: manualLines.map((lineStr, lineIndex) => /* @__PURE__ */ jsxs3(Fragment, { children: [
640
+ lineIndex > 0 && /* @__PURE__ */ jsx5("br", {}),
641
+ lineStr.split(/\s+/).filter(Boolean).map((word, wordIndex) => /* @__PURE__ */ jsxs3(Fragment, { children: [
642
+ wordIndex > 0 && " ",
643
+ /* @__PURE__ */ jsx5("span", { "data-word": true, "data-explicit-line": lineIndex, children: word })
644
+ ] }, wordIndex))
645
+ ] }, lineIndex)) });
646
+ return trigger === "scroll" ? /* @__PURE__ */ jsx5("div", { ref: wrapperRef, className: wrapperClassName, children: preRenderedContent }) : preRenderedContent;
647
+ }
648
+ const animatedContent = /* @__PURE__ */ jsx5(Component, { ref: containerRef, className: resolvedClassName, children: lines.map((lineStr, lineIndex) => /* @__PURE__ */ jsxs3(Fragment, { children: [
649
+ lineIndex > 0 && /* @__PURE__ */ jsx5("br", {}),
650
+ /* @__PURE__ */ jsxs3("div", { "data-line": true, className: "relative inline-block", children: [
651
+ /* @__PURE__ */ jsx5("span", { "data-line-inner": true, className: "block whitespace-nowrap", children: lineStr }),
652
+ !shouldSkip && /* @__PURE__ */ jsxs3(Fragment, { children: [
653
+ /* @__PURE__ */ jsx5("div", { "data-brand-rect": true, className: "absolute -inset-x-[0.1em] -inset-y-[0.1em] bg-brand" }),
654
+ /* @__PURE__ */ jsx5("div", { "data-fg-rect": true, className: "absolute -inset-x-[0.1em] -inset-y-[0.1em] bg-foreground" })
655
+ ] })
656
+ ] })
657
+ ] }, lineIndex)) });
658
+ return trigger === "scroll" ? /* @__PURE__ */ jsx5("div", { ref: wrapperRef, className: wrapperClassName, children: animatedContent }) : animatedContent;
659
+ });
660
+ var AnimatedHeadline_default = AnimatedHeadline;
661
+
662
+ // packages/animations/AnimatedButton.jsx
663
+ import React6, { useRef as useRef6 } from "react";
664
+ import { Slot, Slottable } from "@radix-ui/react-slot";
665
+
666
+ // libs/hooks/useDualLayerScramble.js
667
+ import { useRef as useRef5, useEffect as useEffect5, useCallback as useCallback5 } from "react";
668
+ var DEFAULT_CHARS2 = " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
669
+ function useDualLayerScramble(options) {
670
+ const elementRef = useRef5(null);
671
+ const timelineRef = useRef5(null);
672
+ const originalTextRef = useRef5("");
673
+ const originalHTMLRef = useRef5("");
674
+ const dimensionsRef = useRef5(null);
675
+ const linesDataRef = useRef5([]);
676
+ const spansRef = useRef5([]);
677
+ const isAnimatingRef = useRef5(false);
678
+ const isPreparedRef = useRef5(false);
679
+ useEffect5(() => {
680
+ if (!elementRef.current) return;
681
+ const el = elementRef.current;
682
+ const innerText = el.innerText ?? "";
683
+ if (innerText.trim().length > 0) {
684
+ originalTextRef.current = innerText;
685
+ originalHTMLRef.current = el.innerHTML;
686
+ dimensionsRef.current = { width: el.offsetWidth, height: el.offsetHeight };
687
+ }
688
+ }, []);
689
+ const killTimeline = useCallback5(() => {
690
+ if (timelineRef.current) {
691
+ timelineRef.current.kill();
692
+ timelineRef.current = null;
693
+ isAnimatingRef.current = false;
694
+ }
695
+ }, []);
696
+ const prepareDOM = useCallback5(() => {
697
+ var _a, _b;
698
+ if (!elementRef.current || isPreparedRef.current) return;
699
+ const el = elementRef.current;
700
+ if ((originalTextRef.current || el.innerText || "").trim().length === 0) return;
701
+ if (!dimensionsRef.current) {
702
+ dimensionsRef.current = { width: el.offsetWidth, height: el.offsetHeight };
703
+ }
704
+ const splitTextIntoLines = (element) => {
705
+ const text = element.innerText || "";
706
+ if (text.trim().length === 0) return [];
707
+ if (text.includes("\n")) return text.split("\n").filter((line) => line.length > 0);
708
+ const firstChild = element.firstChild;
709
+ if (!firstChild || firstChild.nodeType !== Node.TEXT_NODE) return [text];
710
+ const range = document.createRange();
711
+ const lines2 = [];
712
+ let currentLine = "";
713
+ let lastTop = null;
714
+ const length = firstChild.length;
715
+ for (let i = 0; i < text.length && i < length; i++) {
716
+ range.setStart(firstChild, i);
717
+ range.setEnd(firstChild, i + 1);
718
+ const rect = range.getBoundingClientRect();
719
+ if (lastTop !== null && Math.abs(rect.top - lastTop) > 2) {
720
+ if (currentLine.length > 0) lines2.push(currentLine);
721
+ currentLine = "";
722
+ }
723
+ currentLine += text[i];
724
+ lastTop = rect.top;
725
+ }
726
+ if (currentLine.length > 0) lines2.push(currentLine);
727
+ return lines2.length > 0 ? lines2 : [text];
728
+ };
729
+ const lines = splitTextIntoLines(el);
730
+ const testDiv = document.createElement("div");
731
+ testDiv.style.cssText = `
732
+ position: absolute;
733
+ visibility: hidden;
734
+ pointer-events: none;
735
+ white-space: nowrap;
736
+ `;
737
+ const computedStyle = window.getComputedStyle(el);
738
+ testDiv.style.font = computedStyle.font;
739
+ testDiv.style.fontSize = computedStyle.fontSize;
740
+ testDiv.style.fontFamily = computedStyle.fontFamily;
741
+ testDiv.style.fontWeight = computedStyle.fontWeight;
742
+ testDiv.style.letterSpacing = computedStyle.letterSpacing;
743
+ testDiv.style.textTransform = computedStyle.textTransform;
744
+ document.body.appendChild(testDiv);
745
+ linesDataRef.current = lines.map((line) => {
746
+ testDiv.textContent = line;
747
+ return { text: line, width: testDiv.offsetWidth, height: testDiv.offsetHeight };
748
+ });
749
+ document.body.removeChild(testDiv);
750
+ const maxWidth = Math.max(...linesDataRef.current.map((lineData) => lineData.width));
751
+ const totalHeight = linesDataRef.current.reduce((sum, lineData) => sum + lineData.height, 0);
752
+ const finalWidth = Math.max(((_a = dimensionsRef.current) == null ? void 0 : _a.width) ?? 0, maxWidth);
753
+ const finalHeight = Math.max(((_b = dimensionsRef.current) == null ? void 0 : _b.height) ?? 0, totalHeight);
754
+ isPreparedRef.current = true;
755
+ gsap.set(el, { width: finalWidth, height: finalHeight, display: "inline-block", overflow: "hidden" });
756
+ el.innerHTML = "";
757
+ spansRef.current = [];
758
+ linesDataRef.current.forEach((lineData) => {
759
+ const span = document.createElement("span");
760
+ span.style.cssText = `
761
+ display: block;
762
+ opacity: 0;
763
+ width: ${lineData.width}px;
764
+ height: ${lineData.height}px;
765
+ overflow: hidden;
766
+ white-space: nowrap;
767
+ `;
768
+ span.innerText = lineData.text;
769
+ el.appendChild(span);
770
+ spansRef.current.push(span);
771
+ });
772
+ gsap.set(el, { opacity: 1 });
773
+ }, []);
774
+ const playScramble = useCallback5((overrides) => {
775
+ var _a;
776
+ if (!elementRef.current) return null;
777
+ if (isAnimatingRef.current) return timelineRef.current;
778
+ prepareDOM();
779
+ const config = { ...options, ...overrides };
780
+ const duration = config.duration ?? 1;
781
+ const speed = config.speed ?? 1;
782
+ const chars = config.chars ?? DEFAULT_CHARS2;
783
+ const firstColorClass = config.firstColorClass ?? "scramble-brand";
784
+ const secondColorClass = config.secondColorClass ?? "scramble-foreground";
785
+ const stagger2 = config.stagger ?? 0.08;
786
+ const linesData = linesDataRef.current;
787
+ const spans = spansRef.current;
788
+ if (linesData.length === 0 || spans.length === 0) return null;
789
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
790
+ spans.forEach((span, index) => {
791
+ const lineData = linesData[index];
792
+ if (lineData) {
793
+ span.innerText = lineData.text;
794
+ }
795
+ span.style.opacity = "1";
796
+ span.className = span.className.replace(/\bscramble-\w+\b/g, "");
797
+ });
798
+ (_a = config.onComplete) == null ? void 0 : _a.call(config);
799
+ return null;
800
+ }
801
+ killTimeline();
802
+ isAnimatingRef.current = true;
803
+ timelineRef.current = gsap.timeline({
804
+ onComplete: () => {
805
+ var _a2;
806
+ isAnimatingRef.current = false;
807
+ timelineRef.current = null;
808
+ (_a2 = config.onComplete) == null ? void 0 : _a2.call(config);
809
+ }
810
+ });
811
+ spans.forEach((span, index) => {
812
+ var _a2, _b, _c;
813
+ const lineData = linesData[index];
814
+ if (!lineData) return;
815
+ const originalText = lineData.text;
816
+ const delay = index * stagger2;
817
+ const generateScrambled = (text, charSet = DEFAULT_CHARS2) => {
818
+ let scrambled = "";
819
+ for (let i = 0; i < text.length; i++) {
820
+ const char = text[i];
821
+ if (char === " ") {
822
+ scrambled += char;
823
+ } else {
824
+ scrambled += charSet[Math.floor(Math.random() * charSet.length)];
825
+ }
826
+ }
827
+ return scrambled;
828
+ };
829
+ const initialScrambledText = generateScrambled(originalText, chars);
830
+ const nonSpaceCount = originalText.replace(/\s/g, "").length;
831
+ const spacesOnly = originalText.replace(/[^\s]/g, " ");
832
+ (_a2 = timelineRef.current) == null ? void 0 : _a2.add(() => {
833
+ gsap.set(span, { opacity: 1 });
834
+ span.innerText = spacesOnly;
835
+ }, delay);
836
+ (_b = timelineRef.current) == null ? void 0 : _b.to(span, {
837
+ duration,
838
+ scrambleText: {
839
+ text: initialScrambledText,
840
+ chars,
841
+ speed,
842
+ revealDelay: 0.1,
843
+ oldClass: firstColorClass,
844
+ newClass: firstColorClass
845
+ },
846
+ ease: "none"
847
+ }, delay);
848
+ (_c = timelineRef.current) == null ? void 0 : _c.to(span, {
849
+ duration,
850
+ scrambleText: {
851
+ text: originalText,
852
+ chars,
853
+ speed,
854
+ revealDelay: 0.1,
855
+ oldClass: firstColorClass,
856
+ newClass: secondColorClass
857
+ },
858
+ ease: "none"
859
+ }, delay + (nonSpaceCount > 0 ? duration / nonSpaceCount : 0));
860
+ });
861
+ return timelineRef.current;
862
+ }, [options, killTimeline, prepareDOM]);
863
+ const resetDOM = useCallback5(() => {
864
+ killTimeline();
865
+ if (elementRef.current && isPreparedRef.current) {
866
+ elementRef.current.innerHTML = originalHTMLRef.current || originalTextRef.current;
867
+ gsap.set(elementRef.current, {
868
+ opacity: 0,
869
+ width: "auto",
870
+ height: "auto",
871
+ overflow: "visible"
872
+ });
873
+ spansRef.current = [];
874
+ linesDataRef.current = [];
875
+ isPreparedRef.current = false;
876
+ }
877
+ }, [killTimeline]);
878
+ useEffect5(() => {
879
+ return () => {
880
+ killTimeline();
881
+ spansRef.current = [];
882
+ linesDataRef.current = [];
883
+ isPreparedRef.current = false;
884
+ };
885
+ }, [killTimeline]);
886
+ return {
887
+ ref: elementRef,
888
+ scramble: playScramble,
889
+ kill: resetDOM,
890
+ isAnimating: isAnimatingRef.current
891
+ };
892
+ }
893
+
894
+ // packages/animations/AnimatedButton.jsx
895
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
896
+ var buttonVariants = cva(
897
+ [
898
+ "group inline-flex min-w-0 shrink-0 cursor-pointer items-center justify-center whitespace-nowrap",
899
+ "font-medium font-mono uppercase",
900
+ "outline-none focus-visible:ring-2 focus-visible:ring-brand/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
901
+ "disabled:pointer-events-none disabled:opacity-50"
902
+ ],
903
+ {
904
+ variants: {
905
+ variant: {
906
+ primary: []
907
+ },
908
+ size: {
909
+ xs: "text-body-sm",
910
+ sm: "text-body-sm",
911
+ default: "text-body-sm lg:text-body",
912
+ lg: "text-body lg:text-body-lg"
913
+ }
914
+ },
915
+ defaultVariants: {
916
+ variant: "primary",
917
+ size: "default"
918
+ }
919
+ }
920
+ );
921
+ var iconVariants = cva(
922
+ [
923
+ "flex items-center justify-center",
924
+ "transition-transform duration-700 [transition-timing-function:var(--ease-power4-in-out)]"
925
+ ],
926
+ {
927
+ variants: {
928
+ size: {
929
+ xs: "size-32",
930
+ sm: "size-32 lg:size-40",
931
+ default: "size-40 lg:size-48",
932
+ lg: "size-48 lg:size-56"
933
+ },
934
+ position: {
935
+ left: "origin-left -rotate-45 scale-0",
936
+ right: "absolute right-0 z-10 origin-right rotate-0 scale-100"
937
+ },
938
+ theme: {
939
+ light: "bg-foreground text-background",
940
+ dark: "bg-foreground text-background",
941
+ brand: "bg-brand text-black"
942
+ }
943
+ },
944
+ defaultVariants: {
945
+ size: "default",
946
+ theme: "light"
947
+ }
948
+ }
949
+ );
950
+ var textWrapperVariants = cva(
951
+ [
952
+ "flex w-full flex-1 items-center justify-center",
953
+ "transition-transform duration-700 [transition-timing-function:var(--ease-power4-in-out)]"
954
+ ],
955
+ {
956
+ variants: {
957
+ size: {
958
+ xs: "h-32 -translate-x-[calc(32px+6px)] px-8",
959
+ sm: "h-32 -translate-x-[calc(32px+6px)] px-8 lg:h-40 lg:-translate-x-[calc(40px+6px)] lg:px-12",
960
+ default: "h-40 -translate-x-[calc(40px+6px)] px-12 lg:h-48 lg:-translate-x-[calc(48px+6px)] lg:px-16",
961
+ lg: "h-48 -translate-x-[calc(48px+6px)] px-16 lg:h-56 lg:-translate-x-[calc(56px+6px)] lg:px-24"
962
+ },
963
+ theme: {
964
+ light: "bg-foreground text-background",
965
+ dark: "bg-foreground text-background",
966
+ brand: "bg-brand text-black"
967
+ }
968
+ },
969
+ defaultVariants: {
970
+ size: "default",
971
+ theme: "light"
972
+ }
973
+ }
974
+ );
975
+ function PlusIcon({ className }) {
976
+ return /* @__PURE__ */ jsx6(
977
+ "svg",
978
+ {
979
+ className: cx("size-[0.75em]", className),
980
+ viewBox: "0 0 12 12",
981
+ fill: "none",
982
+ "aria-hidden": "true",
983
+ children: /* @__PURE__ */ jsx6(
984
+ "path",
985
+ {
986
+ d: "M6 1v10M1 6h10",
987
+ stroke: "currentColor",
988
+ strokeWidth: "1.5",
989
+ strokeLinecap: "square"
990
+ }
991
+ )
992
+ }
993
+ );
994
+ }
995
+ function AnimatedButton(props) {
996
+ const {
997
+ children,
998
+ className,
999
+ asChild,
1000
+ variant,
1001
+ size,
1002
+ theme,
1003
+ type,
1004
+ ...rest
1005
+ } = props;
1006
+ const resolvedType = type === void 0 ? asChild ? void 0 : "button" : type;
1007
+ const Comp = asChild ? Slot : "button";
1008
+ const themeColors = theme === "brand" ? { firstColorClass: "scramble-white", secondColorClass: "scramble-inherit" } : { firstColorClass: "scramble-brand", secondColorClass: "scramble-inherit" };
1009
+ const scrambleConfig = {
1010
+ duration: 0.5,
1011
+ initiallyVisible: true,
1012
+ firstColorClass: themeColors.firstColorClass,
1013
+ secondColorClass: themeColors.secondColorClass
1014
+ };
1015
+ const scrambleResult = useDualLayerScramble(scrambleConfig);
1016
+ const isScramblingRef = useRef6(false);
1017
+ const handleMouseEnter = () => {
1018
+ if (!isScramblingRef.current) {
1019
+ isScramblingRef.current = true;
1020
+ scrambleResult.scramble();
1021
+ }
1022
+ };
1023
+ const handleMouseLeave = () => {
1024
+ isScramblingRef.current = false;
1025
+ };
1026
+ const buttonClasses = buttonVariants({ variant, size, className });
1027
+ const renderContent = (content) => /* @__PURE__ */ jsxs4("span", { className: "relative flex w-full items-center gap-6", children: [
1028
+ /* @__PURE__ */ jsx6(
1029
+ "span",
1030
+ {
1031
+ className: cx(
1032
+ iconVariants({ size, theme, position: "left" }),
1033
+ "group-hover:rotate-0 group-hover:scale-100"
1034
+ ),
1035
+ children: /* @__PURE__ */ jsx6(PlusIcon, {})
1036
+ }
1037
+ ),
1038
+ /* @__PURE__ */ jsx6(
1039
+ "span",
1040
+ {
1041
+ className: cx(
1042
+ textWrapperVariants({ size, theme }),
1043
+ "group-hover:translate-x-0"
1044
+ ),
1045
+ children: /* @__PURE__ */ jsx6("span", { ref: scrambleResult.ref, children: content })
1046
+ }
1047
+ ),
1048
+ /* @__PURE__ */ jsx6(
1049
+ "span",
1050
+ {
1051
+ className: cx(
1052
+ iconVariants({ size, theme, position: "right" }),
1053
+ "group-hover:-rotate-45 group-hover:scale-0"
1054
+ ),
1055
+ children: /* @__PURE__ */ jsx6(PlusIcon, {})
1056
+ }
1057
+ )
1058
+ ] });
1059
+ return /* @__PURE__ */ jsx6(
1060
+ Comp,
1061
+ {
1062
+ type: resolvedType,
1063
+ className: buttonClasses,
1064
+ onMouseEnter: handleMouseEnter,
1065
+ onMouseLeave: handleMouseLeave,
1066
+ ...rest,
1067
+ children: /* @__PURE__ */ jsx6(Slottable, { asChild, child: children, children: renderContent })
1068
+ }
1069
+ );
1070
+ }
1071
+
1072
+ // packages/animations/AnimatedLink.jsx
1073
+ import React7, { forwardRef as forwardRef2 } from "react";
1074
+ import { Slot as Slot2, Slottable as Slottable2 } from "@radix-ui/react-slot";
1075
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1076
+ var AnimatedLink = forwardRef2(function AnimatedLink2(props, ref) {
1077
+ const {
1078
+ children,
1079
+ className,
1080
+ asChild,
1081
+ indicator,
1082
+ ...restProps
1083
+ } = props;
1084
+ const Component = asChild ? Slot2 : "a";
1085
+ const displayClass = indicator ? "inline-flex items-center" : "inline-block";
1086
+ const combinedClassName = cx(
1087
+ "group relative w-fit cursor-pointer",
1088
+ displayClass,
1089
+ "outline-none focus-visible:ring-2 focus-visible:ring-brand/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1090
+ className
1091
+ );
1092
+ const renderChildren = (childContent) => /* @__PURE__ */ jsxs5(Fragment2, { children: [
1093
+ indicator && /* @__PURE__ */ jsx7(
1094
+ "span",
1095
+ {
1096
+ className: cx(
1097
+ "absolute left-0 size-8 bg-brand lg:size-12",
1098
+ "-rotate-90 scale-0",
1099
+ "transition-transform duration-500 [transition-timing-function:cubic-bezier(0.34,1.56,0.64,1)]",
1100
+ "group-hover:rotate-0 group-hover:scale-100",
1101
+ "group-focus-visible:rotate-0 group-focus-visible:scale-100"
1102
+ ),
1103
+ "aria-hidden": "true"
1104
+ }
1105
+ ),
1106
+ indicator ? /* @__PURE__ */ jsx7(
1107
+ "span",
1108
+ {
1109
+ className: cx(
1110
+ "transition-transform duration-500 [transition-timing-function:cubic-bezier(0.33,1,0.68,1)]",
1111
+ "group-hover:translate-x-24",
1112
+ "group-focus-visible:translate-x-24"
1113
+ ),
1114
+ children: childContent
1115
+ }
1116
+ ) : childContent,
1117
+ /* @__PURE__ */ jsxs5(
1118
+ "span",
1119
+ {
1120
+ className: cx(
1121
+ "pointer-events-none absolute inset-x-0 -bottom-1",
1122
+ indicator && [
1123
+ "transition-transform duration-500 [transition-timing-function:cubic-bezier(0.33,1,0.68,1)]",
1124
+ "group-hover:translate-x-24 group-focus-visible:translate-x-24"
1125
+ ]
1126
+ ),
1127
+ "aria-hidden": "true",
1128
+ children: [
1129
+ /* @__PURE__ */ jsx7(
1130
+ "span",
1131
+ {
1132
+ className: cx(
1133
+ "absolute inset-x-0 top-0 h-px bg-current",
1134
+ "origin-left scale-x-100",
1135
+ "transition-transform duration-700 [transition-timing-function:cubic-bezier(0.625,0.05,0,1)]",
1136
+ "delay-300 group-hover:origin-right group-hover:scale-x-0 group-hover:delay-0",
1137
+ "group-focus-visible:origin-right group-focus-visible:scale-x-0 group-focus-visible:delay-0"
1138
+ )
1139
+ }
1140
+ ),
1141
+ /* @__PURE__ */ jsx7(
1142
+ "span",
1143
+ {
1144
+ className: cx(
1145
+ "absolute inset-x-0 top-0 h-px bg-current",
1146
+ "origin-right scale-x-0",
1147
+ "transition-transform duration-700 [transition-timing-function:cubic-bezier(0.625,0.05,0,1)]",
1148
+ "delay-0 group-hover:origin-left group-hover:scale-x-100 group-hover:delay-300",
1149
+ "group-focus-visible:origin-left group-focus-visible:scale-x-100 group-focus-visible:delay-300"
1150
+ )
1151
+ }
1152
+ )
1153
+ ]
1154
+ }
1155
+ )
1156
+ ] });
1157
+ return /* @__PURE__ */ jsx7(Component, { ref, className: combinedClassName, ...restProps, children: /* @__PURE__ */ jsx7(Slottable2, { asChild, child: children, children: renderChildren }) });
1158
+ });
1159
+ var AnimatedLink_default = AnimatedLink;
1160
+
1161
+ // packages/animations/AnimatedProse.jsx
1162
+ import React8, { createContext as createContext3, useState as useState5, useRef as useRef7, useEffect as useEffect6 } from "react";
1163
+ import { motion, animate } from "framer-motion";
1164
+
1165
+ // libs/utils/easings.js
1166
+ function cubicBezier(x1, y1, x2, y2) {
1167
+ const cx2 = 3 * x1;
1168
+ const bx = 3 * (x2 - x1) - cx2;
1169
+ const ax = 1 - cx2 - bx;
1170
+ const cy = 3 * y1;
1171
+ const by = 3 * (y2 - y1) - cy;
1172
+ const ay = 1 - cy - by;
1173
+ function sampleCurveX(t) {
1174
+ return ((ax * t + bx) * t + cx2) * t;
1175
+ }
1176
+ function sampleCurveY(t) {
1177
+ return ((ay * t + by) * t + cy) * t;
1178
+ }
1179
+ function sampleCurveDerivativeX(t) {
1180
+ return (3 * ax * t + 2 * bx) * t + cx2;
1181
+ }
1182
+ function solveCurveX(x, epsilon = 1e-6) {
1183
+ let t = x;
1184
+ for (let i = 0; i < 8; i++) {
1185
+ const currentX = sampleCurveX(t) - x;
1186
+ if (Math.abs(currentX) < epsilon) return t;
1187
+ const currentSlope = sampleCurveDerivativeX(t);
1188
+ if (Math.abs(currentSlope) < epsilon) break;
1189
+ t -= currentX / currentSlope;
1190
+ }
1191
+ let lower = 0;
1192
+ let upper = 1;
1193
+ t = x;
1194
+ if (t < lower) return lower;
1195
+ if (t > upper) return upper;
1196
+ while (lower < upper) {
1197
+ const currentX = sampleCurveX(t);
1198
+ if (Math.abs(currentX - x) < epsilon) return t;
1199
+ if (x > currentX) {
1200
+ lower = t;
1201
+ } else {
1202
+ upper = t;
1203
+ }
1204
+ t = (upper - lower) / 2 + lower;
1205
+ }
1206
+ return t;
1207
+ }
1208
+ return function bezierEase(t) {
1209
+ if (t <= 0) return 0;
1210
+ if (t >= 1) return 1;
1211
+ return sampleCurveY(solveCurveX(t));
1212
+ };
1213
+ }
1214
+ var NAMED_EASINGS = {
1215
+ linear: (t) => t,
1216
+ easeIn: cubicBezier(0.42, 0, 1, 1),
1217
+ easeOut: cubicBezier(0, 0, 0.58, 1),
1218
+ easeInOut: cubicBezier(0.42, 0, 0.58, 1),
1219
+ circIn: cubicBezier(0.55, 0, 1, 0.45),
1220
+ circOut: cubicBezier(0, 0.55, 0.45, 1),
1221
+ circInOut: cubicBezier(0.85, 0, 0.15, 1),
1222
+ backIn: cubicBezier(0.36, 0, 0.66, -0.56),
1223
+ backOut: cubicBezier(0.34, 1.56, 0.64, 1),
1224
+ backInOut: cubicBezier(0.68, -0.6, 0.32, 1.6),
1225
+ anticipate: cubicBezier(0.36, 0, 0.66, -0.56)
1226
+ };
1227
+ function easingDefinitionToFunction(definition) {
1228
+ if (typeof definition === "function") {
1229
+ return definition;
1230
+ }
1231
+ if (Array.isArray(definition) && definition.length === 4) {
1232
+ const [x1, y1, x2, y2] = definition;
1233
+ return cubicBezier(x1, y1, x2, y2);
1234
+ }
1235
+ if (typeof definition === "string" && NAMED_EASINGS[definition]) {
1236
+ return NAMED_EASINGS[definition];
1237
+ }
1238
+ return NAMED_EASINGS.linear;
1239
+ }
1240
+
1241
+ // packages/animations/AnimatedProse.jsx
1242
+ import { jsx as jsx8 } from "react/jsx-runtime";
1243
+ function stagger(amount = 0.1, { startDelay = 0, from = 0, ease } = {}) {
1244
+ return (index, totalElements) => {
1245
+ let fromVal = typeof from === "number" ? from : (function(fromStr, total) {
1246
+ if (fromStr === "first") return 0;
1247
+ let lastIndex = total - 1;
1248
+ return fromStr === "last" ? lastIndex : lastIndex / 2;
1249
+ })(from, totalElements);
1250
+ let distance = amount * Math.abs(fromVal - index);
1251
+ if (ease) {
1252
+ let maxStagger = totalElements * amount;
1253
+ distance = easingDefinitionToFunction(ease)(distance / maxStagger) * maxStagger;
1254
+ }
1255
+ return startDelay + distance;
1256
+ };
1257
+ }
1258
+ var AnimatedProseContext = createContext3(false);
1259
+ function AnimatedProse({
1260
+ children,
1261
+ className,
1262
+ staggerDelay = 0.03,
1263
+ duration = 0.5,
1264
+ delay = 0,
1265
+ margin = "0px 0px -10% 0px"
1266
+ }) {
1267
+ const [isInView, setIsInView] = useState5(false);
1268
+ const [hasSplitLines, setHasSplitLines] = useState5(false);
1269
+ const containerRef = useRef7(null);
1270
+ const hasAnimatedRef = useRef7(false);
1271
+ useEffect6(() => {
1272
+ if (!containerRef.current) return;
1273
+ const checkSplitLines = () => {
1274
+ var _a;
1275
+ const lines = (_a = containerRef.current) == null ? void 0 : _a.querySelectorAll(".split-line");
1276
+ if (lines && lines.length > 0) {
1277
+ setHasSplitLines(true);
1278
+ return true;
1279
+ }
1280
+ return false;
1281
+ };
1282
+ if (checkSplitLines()) return;
1283
+ const intervalId = setInterval(() => {
1284
+ if (checkSplitLines()) {
1285
+ clearInterval(intervalId);
1286
+ }
1287
+ }, 50);
1288
+ const timeoutId = setTimeout(() => {
1289
+ clearInterval(intervalId);
1290
+ setHasSplitLines(true);
1291
+ }, 500);
1292
+ return () => {
1293
+ clearInterval(intervalId);
1294
+ clearTimeout(timeoutId);
1295
+ };
1296
+ }, []);
1297
+ useEffect6(() => {
1298
+ if (!isInView || !hasSplitLines || !containerRef.current || hasAnimatedRef.current) return;
1299
+ const lines = containerRef.current.querySelectorAll(".split-line");
1300
+ if (lines.length === 0) return;
1301
+ hasAnimatedRef.current = true;
1302
+ containerRef.current.querySelectorAll(".invisible").forEach(makeVisible);
1303
+ const animation = animate(
1304
+ Array.from(lines),
1305
+ { y: ["100%", "0%"] },
1306
+ {
1307
+ delay: stagger(staggerDelay, { startDelay: delay }),
1308
+ duration,
1309
+ ease: [0.33, 1, 0.68, 1]
1310
+ }
1311
+ );
1312
+ return () => {
1313
+ animation == null ? void 0 : animation.cancel();
1314
+ };
1315
+ }, [isInView, hasSplitLines, staggerDelay, duration, delay]);
1316
+ const handleViewportEnter = () => setIsInView(true);
1317
+ return /* @__PURE__ */ jsx8(AnimatedProseContext.Provider, { value: true, children: /* @__PURE__ */ jsx8(
1318
+ motion.div,
1319
+ {
1320
+ ref: containerRef,
1321
+ onViewportEnter: handleViewportEnter,
1322
+ viewport: { once: true, margin },
1323
+ className: cx(className),
1324
+ children
1325
+ }
1326
+ ) });
1327
+ }
1328
+ function makeVisible(el) {
1329
+ el.style.visibility = "visible";
1330
+ }
1331
+
1332
+ // packages/animations/AnimatedSubtext.jsx
1333
+ import React9, { forwardRef as forwardRef3, useRef as useRef8, useState as useState6, useLayoutEffect as useLayoutEffect2, useImperativeHandle as useImperativeHandle2 } from "react";
1334
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1335
+ var AnimatedSubtext = forwardRef3(({
1336
+ children,
1337
+ className,
1338
+ skip = false,
1339
+ staggerDelay = 0.05,
1340
+ duration = 0.8
1341
+ }, ref) => {
1342
+ const elementRef = useRef8(null);
1343
+ const splitTextRef = useRef8(null);
1344
+ const [isSplit, setIsSplit] = useState6(false);
1345
+ useLayoutEffect2(() => {
1346
+ if (elementRef.current && !skip) {
1347
+ setIsSplit(false);
1348
+ splitTextRef.current = new SplitText(elementRef.current, {
1349
+ type: "lines",
1350
+ autoSplit: true,
1351
+ aria: false,
1352
+ deepSlice: true,
1353
+ reduceWhiteSpace: false,
1354
+ mask: "lines",
1355
+ linesClass: "split-line",
1356
+ onSplit: () => setIsSplit(true)
1357
+ });
1358
+ return () => {
1359
+ var _a;
1360
+ (_a = splitTextRef.current) == null ? void 0 : _a.revert();
1361
+ splitTextRef.current = null;
1362
+ };
1363
+ }
1364
+ }, [skip]);
1365
+ useImperativeHandle2(ref, () => ({
1366
+ reveal: (delay = 0) => {
1367
+ if (!elementRef.current || !splitTextRef.current || skip) return;
1368
+ const lines = splitTextRef.current.lines ?? [];
1369
+ if (lines.length !== 0) {
1370
+ elementRef.current.style.visibility = "visible";
1371
+ gsap.fromTo(
1372
+ lines,
1373
+ { y: "100%" },
1374
+ {
1375
+ y: "0%",
1376
+ duration,
1377
+ ease: "power3.out",
1378
+ stagger: staggerDelay,
1379
+ delay
1380
+ }
1381
+ );
1382
+ }
1383
+ }
1384
+ }), [skip, duration, staggerDelay]);
1385
+ const renderWithLineBreaks = (text) => {
1386
+ const lines = text.split("\n");
1387
+ return lines.map((line, index) => /* @__PURE__ */ jsxs6("span", { children: [
1388
+ line,
1389
+ index < lines.length - 1 && /* @__PURE__ */ jsx9("br", {})
1390
+ ] }, index));
1391
+ };
1392
+ return skip ? /* @__PURE__ */ jsx9("p", { className, children: renderWithLineBreaks(children) }) : /* @__PURE__ */ jsx9("p", { ref: elementRef, className: cx("invisible", className), children: renderWithLineBreaks(children) });
1393
+ });
1394
+ var AnimatedSubtext_default = AnimatedSubtext;
1395
+
1396
+ // packages/animations/AnimatedText.jsx
1397
+ import React10, { useState as useState7, useRef as useRef9, useContext as useContext3, useLayoutEffect as useLayoutEffect3, useEffect as useEffect7 } from "react";
1398
+ import { motion as motion2, animate as animate2 } from "framer-motion";
1399
+ import { jsx as jsx10 } from "react/jsx-runtime";
1400
+ function AnimatedText({
1401
+ children,
1402
+ className,
1403
+ staggerDelay = 0.03,
1404
+ duration = 0.5,
1405
+ delay = 0,
1406
+ margin = "0px 0px -10% 0px",
1407
+ revert = false
1408
+ }) {
1409
+ const [isInView, setIsInView] = useState7(false);
1410
+ const [isSplit, setIsSplit] = useState7(false);
1411
+ const containerRef = useRef9(null);
1412
+ const splitTextRef = useRef9(null);
1413
+ const inAnimatedProse = useContext3(AnimatedProseContext);
1414
+ useLayoutEffect3(() => {
1415
+ if (containerRef.current) {
1416
+ setIsSplit(false);
1417
+ splitTextRef.current = new SplitText(containerRef.current, {
1418
+ type: "lines",
1419
+ autoSplit: true,
1420
+ aria: false,
1421
+ deepSlice: true,
1422
+ reduceWhiteSpace: false,
1423
+ mask: "lines",
1424
+ linesClass: "split-line",
1425
+ onSplit: () => setIsSplit(true)
1426
+ });
1427
+ }
1428
+ return () => {
1429
+ splitTextRef.current = null;
1430
+ };
1431
+ }, []);
1432
+ useEffect7(() => {
1433
+ if (inAnimatedProse || !isInView || !isSplit || !containerRef.current || !splitTextRef.current) {
1434
+ return;
1435
+ }
1436
+ const lines = splitTextRef.current.lines ?? [];
1437
+ if (lines.length === 0) return;
1438
+ containerRef.current.style.visibility = "visible";
1439
+ const animation = animate2(
1440
+ lines,
1441
+ { y: ["100%", "0%"] },
1442
+ {
1443
+ delay: stagger(staggerDelay, { startDelay: delay }),
1444
+ duration,
1445
+ ease: [0.33, 1, 0.68, 1]
1446
+ }
1447
+ );
1448
+ const handleRevert = async () => {
1449
+ var _a;
1450
+ try {
1451
+ await animation;
1452
+ if (revert) {
1453
+ (_a = splitTextRef.current) == null ? void 0 : _a.revert();
1454
+ }
1455
+ } catch (err) {
1456
+ }
1457
+ };
1458
+ handleRevert();
1459
+ return () => {
1460
+ animation == null ? void 0 : animation.cancel();
1461
+ };
1462
+ }, [isInView, isSplit, inAnimatedProse, staggerDelay, duration, delay, revert]);
1463
+ const handleViewportEnter = () => setIsInView(true);
1464
+ return /* @__PURE__ */ jsx10(
1465
+ motion2.span,
1466
+ {
1467
+ ref: containerRef,
1468
+ onViewportEnter: handleViewportEnter,
1469
+ viewport: { once: true, margin },
1470
+ className: cx("invisible", className),
1471
+ children
1472
+ }
1473
+ );
1474
+ }
1475
+
1476
+ // packages/animations/ScrollAnimatedHeadline.jsx
1477
+ import { jsx as jsx11 } from "react/jsx-runtime";
1478
+ function ScrollAnimatedHeadline({
1479
+ headline,
1480
+ displayAs,
1481
+ className,
1482
+ headlineClassName
1483
+ }) {
1484
+ if (!(headline == null ? void 0 : headline.text) || !(headline == null ? void 0 : headline.level)) return null;
1485
+ return /* @__PURE__ */ jsx11(
1486
+ AnimatedHeadline_default,
1487
+ {
1488
+ trigger: "scroll",
1489
+ as: headline.level,
1490
+ displayAs,
1491
+ className: cx(headlineClassName),
1492
+ wrapperClassName: className,
1493
+ children: headline.text
1494
+ }
1495
+ );
1496
+ }
1497
+
1498
+ // packages/animations/Preloader.jsx
1499
+ import React12, { useRef as useRef10, useState as useState9, useEffect as useEffect9 } from "react";
1500
+
1501
+ // packages/providers/PreloaderProvider.jsx
1502
+ import React11, { createContext as createContext4, useState as useState8, useEffect as useEffect8, useContext as useContext4, useMemo as useMemo5 } from "react";
1503
+ import { jsx as jsx12 } from "react/jsx-runtime";
1504
+ var PreloaderContext = createContext4(null);
1505
+ var defaultContextValue2 = {
1506
+ phase: "hidden",
1507
+ setPhase: () => {
1508
+ },
1509
+ isInitialLoad: false
1510
+ };
1511
+ function PreloaderProvider({ children }) {
1512
+ const [phase, setPhase] = useState8("loading");
1513
+ const [isInitialLoad, setIsInitialLoad] = useState8(true);
1514
+ useEffect8(() => {
1515
+ if (phase === "revealing") {
1516
+ const timer = setTimeout(() => {
1517
+ setPhase("hidden");
1518
+ setIsInitialLoad(false);
1519
+ }, 200);
1520
+ return () => clearTimeout(timer);
1521
+ }
1522
+ }, [phase]);
1523
+ const contextValue = useMemo5(() => ({
1524
+ phase,
1525
+ setPhase,
1526
+ isInitialLoad
1527
+ }), [phase, isInitialLoad]);
1528
+ return /* @__PURE__ */ jsx12(PreloaderContext.Provider, { value: contextValue, children });
1529
+ }
1530
+ function usePreloader() {
1531
+ return useContext4(PreloaderContext) ?? defaultContextValue2;
1532
+ }
1533
+
1534
+ // packages/animations/Preloader.jsx
1535
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1536
+ function Preloader() {
1537
+ const { phase, setPhase, isInitialLoad } = usePreloader();
1538
+ const containerRef = useRef10(null);
1539
+ const contentRef = useRef10(null);
1540
+ const squaresContainerRef = useRef10(null);
1541
+ const squaresRefs = useRef10([]);
1542
+ const onReadyRef = useRef10(null);
1543
+ const timelineRef = useRef10(null);
1544
+ const hasStartedRef = useRef10(false);
1545
+ const [isReducedMotion, setIsReducedMotion] = useState9(false);
1546
+ useEffect9(() => {
1547
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
1548
+ setIsReducedMotion(mediaQuery.matches);
1549
+ const handleChange = (e) => setIsReducedMotion(e.matches);
1550
+ mediaQuery.addEventListener("change", handleChange);
1551
+ return () => mediaQuery.removeEventListener("change", handleChange);
1552
+ }, []);
1553
+ const handleReady = (readyFn) => {
1554
+ onReadyRef.current = readyFn;
1555
+ };
1556
+ useGSAP(() => {
1557
+ if (!isInitialLoad || phase !== "loading" || hasStartedRef.current) return;
1558
+ if (isReducedMotion) {
1559
+ setPhase("complete");
1560
+ return;
1561
+ }
1562
+ const squares = squaresRefs.current.filter(Boolean);
1563
+ if (squares.length === 0) return;
1564
+ hasStartedRef.current = true;
1565
+ let timeoutId;
1566
+ const runAnimation = () => {
1567
+ var _a, _b;
1568
+ if (!onReadyRef.current) {
1569
+ requestAnimationFrame(runAnimation);
1570
+ return;
1571
+ }
1572
+ timelineRef.current = gsap.timeline();
1573
+ onReadyRef.current();
1574
+ squares.forEach((square, index) => {
1575
+ var _a2;
1576
+ (_a2 = timelineRef.current) == null ? void 0 : _a2.fromTo(
1577
+ square,
1578
+ {
1579
+ x: index === 0 ? -16 : (index - 1) * 18,
1580
+ rotate: 0
1581
+ },
1582
+ {
1583
+ x: index * 18 - 16,
1584
+ rotate: 90,
1585
+ duration: 0.7,
1586
+ ease: "expo.inOut",
1587
+ immediateRender: false
1588
+ },
1589
+ index === 0 ? 0 : ">-25%"
1590
+ );
1591
+ });
1592
+ const timeOffset = 2.2749999999999995;
1593
+ (_a = timelineRef.current) == null ? void 0 : _a.to(
1594
+ contentRef.current,
1595
+ { opacity: 0, duration: 0.4, ease: "power3.out" },
1596
+ timeOffset + 0.2
1597
+ );
1598
+ const clipState = { value: 0 };
1599
+ const eventState = { value: false };
1600
+ (_b = timelineRef.current) == null ? void 0 : _b.to(
1601
+ clipState,
1602
+ {
1603
+ value: 1,
1604
+ duration: 1.5,
1605
+ ease: "expo.inOut",
1606
+ onUpdate: () => {
1607
+ if (containerRef.current) {
1608
+ const val = clipState.value;
1609
+ containerRef.current.style.clipPath = val <= 0 ? "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)" : val >= 1 ? "polygon(0% 100%, 0% 100%, 0% 100%)" : val <= 0.5 ? `polygon(0% 100%, ${2 * val * 100}% 0%, 100% 0%, 100% 100%)` : `polygon(0% 100%, 100% ${(val - 0.5) * 200}%, 100% 100%)`;
1610
+ }
1611
+ if (!eventState.value && clipState.value >= 0.9) {
1612
+ eventState.value = true;
1613
+ setPhase("revealing");
1614
+ }
1615
+ }
1616
+ },
1617
+ timeOffset
1618
+ );
1619
+ };
1620
+ timeoutId = setTimeout(runAnimation, 100);
1621
+ return () => {
1622
+ var _a;
1623
+ clearTimeout(timeoutId);
1624
+ (_a = timelineRef.current) == null ? void 0 : _a.kill();
1625
+ };
1626
+ }, { dependencies: [isInitialLoad, isReducedMotion, setPhase] });
1627
+ if (!isInitialLoad || phase === "hidden") return null;
1628
+ const pointerEventsClass = phase === "complete" ? "pointer-events-none" : "";
1629
+ const containerClasses = cx("fixed inset-0 z-[10000] flex items-center justify-center bg-background", pointerEventsClass);
1630
+ return /* @__PURE__ */ jsx13("div", { ref: containerRef, "data-theme": "brand", className: containerClasses, children: /* @__PURE__ */ jsxs7("div", { ref: contentRef, className: "flex flex-col items-center gap-4", children: [
1631
+ /* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(
1632
+ "div",
1633
+ {
1634
+ ref: squaresContainerRef,
1635
+ className: "relative overflow-x-clip overflow-y-visible",
1636
+ style: { width: 70, height: 16 },
1637
+ children: [0, 1, 2, 3].map((index) => /* @__PURE__ */ jsx13(
1638
+ "div",
1639
+ {
1640
+ ref: (el) => {
1641
+ squaresRefs.current[index] = el;
1642
+ },
1643
+ className: "absolute top-0 left-0 bg-foreground",
1644
+ style: {
1645
+ width: 16,
1646
+ height: 16,
1647
+ transform: "translateX(-16px)",
1648
+ transformOrigin: "bottom right"
1649
+ }
1650
+ },
1651
+ index
1652
+ ))
1653
+ }
1654
+ ) }),
1655
+ /* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx13(ScrambleText, { revealMode: true, duration: 1, onReady: handleReady, children: "LOADING" }) }) })
1656
+ ] }) });
1657
+ }
1658
+
1659
+ // packages/providers/PageEnterProvider.jsx
1660
+ import React13, {
1661
+ createContext as createContext5,
1662
+ useState as useState10,
1663
+ useRef as useRef11,
1664
+ useEffect as useEffect10,
1665
+ useCallback as useCallback6,
1666
+ useMemo as useMemo6,
1667
+ useContext as useContext5
1668
+ } from "react";
1669
+ import { jsx as jsx14 } from "react/jsx-runtime";
1670
+ var PageEnterContext = createContext5(null);
1671
+ function sortByPriority(a, b) {
1672
+ return a.priority - b.priority;
1673
+ }
1674
+ function PageEnterProvider({ children }) {
1675
+ const { phase: pageTransitionPhase } = usePageTransition();
1676
+ const { phase: preloaderPhase } = usePreloader();
1677
+ const [phase, setPhase] = useState10("waiting");
1678
+ const [prefersReducedMotion, setPrefersReducedMotion] = useState10(false);
1679
+ const callbacksMapRef = useRef11(/* @__PURE__ */ new Map());
1680
+ const isTriggeringRef = useRef11(false);
1681
+ useEffect10(() => {
1682
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
1683
+ setPrefersReducedMotion(mediaQuery.matches);
1684
+ const handleMediaQueryChange = (e) => setPrefersReducedMotion(e.matches);
1685
+ mediaQuery.addEventListener("change", handleMediaQueryChange);
1686
+ return () => {
1687
+ mediaQuery.removeEventListener("change", handleMediaQueryChange);
1688
+ };
1689
+ }, []);
1690
+ const register = useCallback6((id, trigger, priority = 0) => {
1691
+ callbacksMapRef.current.set(id, { id, trigger, priority });
1692
+ }, []);
1693
+ const unregister = useCallback6((id) => {
1694
+ callbacksMapRef.current.delete(id);
1695
+ }, []);
1696
+ const triggerEnter = useCallback6(() => {
1697
+ if (isTriggeringRef.current) {
1698
+ return;
1699
+ }
1700
+ isTriggeringRef.current = true;
1701
+ setPhase("entering");
1702
+ const callbacks = Array.from(callbacksMapRef.current.values());
1703
+ callbacks.sort(sortByPriority);
1704
+ let lastPriority = null;
1705
+ let staggerIndex = 0;
1706
+ for (const item of callbacks) {
1707
+ if (lastPriority !== null && item.priority > lastPriority) {
1708
+ staggerIndex += 0.08;
1709
+ }
1710
+ lastPriority = item.priority;
1711
+ item.trigger(staggerIndex);
1712
+ }
1713
+ setTimeout(() => {
1714
+ setPhase("complete");
1715
+ }, 1e3 * (staggerIndex + 1));
1716
+ }, []);
1717
+ useEffect10(() => {
1718
+ const isPreloaderReady = preloaderPhase === "revealing" || preloaderPhase === "hidden";
1719
+ if ((pageTransitionPhase === "exiting" || pageTransitionPhase === "idle") && phase === "waiting" && isPreloaderReady) {
1720
+ const timerId = setTimeout(() => {
1721
+ triggerEnter();
1722
+ }, 250);
1723
+ return () => clearTimeout(timerId);
1724
+ }
1725
+ }, [pageTransitionPhase, preloaderPhase, phase, triggerEnter]);
1726
+ useEffect10(() => {
1727
+ if (pageTransitionPhase === "entering" || pageTransitionPhase === "holding") {
1728
+ setPhase("waiting");
1729
+ isTriggeringRef.current = false;
1730
+ }
1731
+ }, [pageTransitionPhase]);
1732
+ const contextValue = useMemo6(() => ({
1733
+ phase,
1734
+ register,
1735
+ unregister,
1736
+ prefersReducedMotion
1737
+ }), [phase, register, unregister, prefersReducedMotion]);
1738
+ return /* @__PURE__ */ jsx14(PageEnterContext.Provider, { value: contextValue, children });
1739
+ }
1740
+ var defaultContextValue3 = {
1741
+ phase: "complete",
1742
+ register: () => {
1743
+ },
1744
+ unregister: () => {
1745
+ },
1746
+ prefersReducedMotion: false
1747
+ };
1748
+ function usePageEnterContext() {
1749
+ return useContext5(PageEnterContext) ?? defaultContextValue3;
1750
+ }
1751
+
1752
+ // libs/hooks/usePageEnter.js
1753
+ import { useRef as useRef12, useEffect as useEffect11, useMemo as useMemo7 } from "react";
1754
+ function usePageEnter(callback, options = {}) {
1755
+ const { priority = 0, skip = false } = options;
1756
+ const {
1757
+ register,
1758
+ unregister,
1759
+ phase,
1760
+ prefersReducedMotion
1761
+ } = usePageEnterContext();
1762
+ const uuid = useMemo7(() => crypto.randomUUID(), []);
1763
+ const idRef = useRef12(uuid);
1764
+ useEffect11(() => {
1765
+ if (skip) {
1766
+ return;
1767
+ }
1768
+ const currentId = idRef.current;
1769
+ register(currentId, callback, priority);
1770
+ return () => {
1771
+ unregister(currentId);
1772
+ };
1773
+ }, [register, unregister, callback, priority, skip]);
1774
+ const isEntering = phase === "entering";
1775
+ const isComplete = phase === "complete";
1776
+ const returnedState = useMemo7(() => ({
1777
+ phase,
1778
+ prefersReducedMotion,
1779
+ isEntering,
1780
+ isComplete
1781
+ }), [phase, prefersReducedMotion, isEntering, isComplete]);
1782
+ return returnedState;
1783
+ }
1784
+
1785
+ // libs/hooks/useBreakpoint.js
1786
+ import { useEffect as useEffect12, useState as useState11 } from "react";
1787
+ var BREAKPOINTS = {
1788
+ sm: 640,
1789
+ md: 768,
1790
+ lg: 1024,
1791
+ xl: 1280,
1792
+ "2xl": 1536
1793
+ };
1794
+ function useBreakpoint(breakpoint = "md") {
1795
+ const minWidth = BREAKPOINTS[breakpoint] ?? BREAKPOINTS.md;
1796
+ const [matches, setMatches] = useState11(() => {
1797
+ if (typeof window === "undefined") return false;
1798
+ return window.matchMedia(`(min-width: ${minWidth}px)`).matches;
1799
+ });
1800
+ useEffect12(() => {
1801
+ const mediaQuery = window.matchMedia(`(min-width: ${minWidth}px)`);
1802
+ const handleChange = (event) => setMatches(event.matches);
1803
+ setMatches(mediaQuery.matches);
1804
+ mediaQuery.addEventListener("change", handleChange);
1805
+ return () => {
1806
+ mediaQuery.removeEventListener("change", handleChange);
1807
+ };
1808
+ }, [minWidth]);
1809
+ return matches;
1810
+ }
1811
+
1812
+ // libs/constants/timing.js
1813
+ var ASCII_REVEAL_DURATION = 1.2;
1814
+
1815
+ // libs/hooks/useAsciiDelay.js
1816
+ var asciiDelayBase = 0.1 * ASCII_REVEAL_DURATION;
1817
+ function useAsciiDelay() {
1818
+ const md = useBreakpoint("md");
1819
+ return md ? asciiDelayBase : 0;
1820
+ }
1821
+ export {
1822
+ AnimatedButton,
1823
+ AnimatedHeadline_default as AnimatedHeadline,
1824
+ AnimatedLink_default as AnimatedLink,
1825
+ AnimatedProse,
1826
+ AnimatedProseContext,
1827
+ AnimatedSubtext_default as AnimatedSubtext,
1828
+ AnimatedText,
1829
+ DEFAULT_CHARS,
1830
+ Lenis as LenisProvider,
1831
+ PageEnterProvider,
1832
+ PageTransitionProvider,
1833
+ Preloader,
1834
+ PreloaderProvider,
1835
+ ScrambleGroup,
1836
+ ScrambleText,
1837
+ ScrambleTextPlugin,
1838
+ ScrollAnimatedHeadline,
1839
+ ScrollTrigger,
1840
+ SplitText,
1841
+ cva,
1842
+ cx,
1843
+ easingDefinitionToFunction,
1844
+ getCssScrollLocked,
1845
+ getLenis,
1846
+ gsap,
1847
+ scrollToTop,
1848
+ setCssScrollLocked,
1849
+ stagger,
1850
+ useAsciiDelay,
1851
+ useDualLayerScramble,
1852
+ useGSAP,
1853
+ useIdleGSAP,
1854
+ useLenis,
1855
+ usePageEnter,
1856
+ usePageEnterContext,
1857
+ usePageTransition,
1858
+ usePageTransitionContext,
1859
+ usePreloader,
1860
+ useScrambleGroup
1861
+ };
1862
+ //# sourceMappingURL=index.js.map