@drivexstudio/animations 1.0.0 → 1.0.1

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