@yewdiel21/motion-kit 0.9.1 → 0.10.0

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.
Files changed (129) hide show
  1. package/README.md +93 -83
  2. package/dist/auto/index.d.ts +22 -3
  3. package/dist/auto/index.js +41 -3
  4. package/dist/auto/index.js.map +1 -1
  5. package/dist/{chunk-BFC27F2G.js → chunk-IKQ5FODN.js} +2 -2
  6. package/dist/{chunk-JQMJQSXD.js → chunk-TFNRIZHU.js} +1614 -11
  7. package/dist/chunk-TFNRIZHU.js.map +1 -0
  8. package/dist/{chunk-XDWBVFHQ.js → chunk-XAKC2QY7.js} +1202 -3
  9. package/dist/chunk-XAKC2QY7.js.map +1 -0
  10. package/dist/css/components/calendar.css +159 -0
  11. package/dist/css/components/hover-card.css +14 -0
  12. package/dist/css/components/input-otp.css +10 -0
  13. package/dist/css/components/pagination.css +11 -0
  14. package/dist/css/components/quantity.css +16 -0
  15. package/dist/css/components/slot-picker.css +126 -0
  16. package/dist/css/components/studio-dock.css +35 -0
  17. package/dist/css/components/trailer.css +37 -0
  18. package/dist/css/controls.css +65 -0
  19. package/dist/elements/index.d.ts +72 -4
  20. package/dist/elements/index.js +170 -4
  21. package/dist/elements/index.js.map +1 -1
  22. package/dist/index.css +1017 -0
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +45 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/react/index.d.ts +105 -6
  27. package/dist/react/index.js +350 -3
  28. package/dist/react/index.js.map +1 -1
  29. package/dist/{reduced-B98f8LPs.d.ts → reduced-BmBzKH94.d.ts} +199 -2
  30. package/dist/{reveal-C0hCDcB_.d.ts → reveal-jsX-uwUz.d.ts} +211 -1
  31. package/dist/{store-pHiUvxP8.d.ts → store-DYb3tTsZ.d.ts} +1 -1
  32. package/package.json +1 -1
  33. package/registry.json +178 -104
  34. package/skills/motion-kit/SKILL.md +209 -189
  35. package/src/auto/auto-init.ts +38 -0
  36. package/src/components/calendar/README.md +82 -0
  37. package/src/components/calendar/calendar.css +158 -0
  38. package/src/components/calendar/element.ts +27 -0
  39. package/src/components/calendar/engine.ts +412 -0
  40. package/src/components/calendar/react.tsx +69 -0
  41. package/src/components/hover-card/README.md +16 -0
  42. package/src/components/hover-card/element.ts +8 -0
  43. package/src/components/hover-card/engine.ts +69 -0
  44. package/src/components/hover-card/hover-card.css +13 -0
  45. package/src/components/hover-card/react.tsx +19 -0
  46. package/src/components/input-otp/README.md +18 -0
  47. package/src/components/input-otp/element.ts +14 -0
  48. package/src/components/input-otp/engine.ts +106 -0
  49. package/src/components/input-otp/input-otp.css +9 -0
  50. package/src/components/input-otp/react.tsx +21 -0
  51. package/src/components/pagination/README.md +17 -0
  52. package/src/components/pagination/element.ts +14 -0
  53. package/src/components/pagination/engine.ts +102 -0
  54. package/src/components/pagination/pagination.css +10 -0
  55. package/src/components/pagination/react.tsx +22 -0
  56. package/src/components/quantity/README.md +20 -0
  57. package/src/components/quantity/element.ts +14 -0
  58. package/src/components/quantity/engine.ts +85 -0
  59. package/src/components/quantity/quantity.css +15 -0
  60. package/src/components/quantity/react.tsx +21 -0
  61. package/src/components/slot-picker/README.md +68 -0
  62. package/src/components/slot-picker/element.ts +27 -0
  63. package/src/components/slot-picker/engine.ts +412 -0
  64. package/src/components/slot-picker/react.tsx +76 -0
  65. package/src/components/slot-picker/slot-picker.css +125 -0
  66. package/src/components/studio-dock/README.md +44 -0
  67. package/src/components/studio-dock/element.ts +11 -0
  68. package/src/components/studio-dock/engine.ts +212 -0
  69. package/src/components/studio-dock/react.tsx +23 -0
  70. package/src/components/studio-dock/studio-dock.css +34 -0
  71. package/src/components/trailer/README.md +28 -0
  72. package/src/components/trailer/element.ts +13 -0
  73. package/src/components/trailer/engine.ts +189 -0
  74. package/src/components/trailer/react.tsx +16 -0
  75. package/src/components/trailer/trailer.css +36 -0
  76. package/src/core/dates.ts +98 -0
  77. package/src/css/controls/README.md +14 -0
  78. package/src/css/controls/status.css +63 -0
  79. package/src/elements/index.ts +24 -0
  80. package/src/index.ts +25 -0
  81. package/src/react/index.ts +21 -0
  82. package/src/react/use-action-button.ts +6 -0
  83. package/src/react/use-editorial-reveal.ts +7 -0
  84. package/src/react/use-film-hero.ts +6 -0
  85. package/src/react/use-font-cycle.ts +7 -0
  86. package/src/react/use-image-zoom.ts +5 -0
  87. package/src/react/use-joined-cta.ts +5 -0
  88. package/src/react/use-local-clock.ts +5 -0
  89. package/src/react/use-scroll-dock.ts +5 -0
  90. package/src/react/use-sticker.ts +5 -0
  91. package/src/react/use-studio-dock.ts +8 -0
  92. package/src/react/use-thumbnail-nav.ts +7 -0
  93. package/src/react/use-trailer.ts +6 -0
  94. package/src/react/use-weight-hover.ts +5 -0
  95. package/src/recipes/action-button/README.md +31 -0
  96. package/src/recipes/action-button/action-button.css +113 -0
  97. package/src/recipes/action-button/create-action-button.ts +56 -0
  98. package/src/recipes/editorial-reveal/README.md +35 -0
  99. package/src/recipes/editorial-reveal/create-editorial-reveal.ts +91 -0
  100. package/src/recipes/editorial-reveal/editorial-reveal.css +17 -0
  101. package/src/recipes/film-hero/README.md +23 -0
  102. package/src/recipes/film-hero/create-film-hero.ts +65 -0
  103. package/src/recipes/film-hero/film-hero.css +29 -0
  104. package/src/recipes/font-cycle/README.md +11 -0
  105. package/src/recipes/font-cycle/create-font-cycle.ts +126 -0
  106. package/src/recipes/font-cycle/font-cycle.css +36 -0
  107. package/src/recipes/image-zoom/README.md +16 -0
  108. package/src/recipes/image-zoom/create-image-zoom.ts +27 -0
  109. package/src/recipes/image-zoom/image-zoom.css +7 -0
  110. package/src/recipes/joined-cta/README.md +13 -0
  111. package/src/recipes/joined-cta/create-joined-cta.ts +22 -0
  112. package/src/recipes/joined-cta/joined-cta.css +17 -0
  113. package/src/recipes/local-clock/README.md +31 -0
  114. package/src/recipes/local-clock/create-local-clock.ts +61 -0
  115. package/src/recipes/scroll-dock/README.md +46 -0
  116. package/src/recipes/scroll-dock/create-scroll-dock.ts +72 -0
  117. package/src/recipes/scroll-dock/scroll-dock.css +70 -0
  118. package/src/recipes/sticker/README.md +59 -0
  119. package/src/recipes/sticker/create-sticker.ts +490 -0
  120. package/src/recipes/sticker/sticker.css +112 -0
  121. package/src/recipes/thumbnail-nav/README.md +16 -0
  122. package/src/recipes/thumbnail-nav/create-thumbnail-nav.ts +165 -0
  123. package/src/recipes/thumbnail-nav/thumbnail-nav.css +108 -0
  124. package/src/recipes/weight-hover/README.md +37 -0
  125. package/src/recipes/weight-hover/create-weight-hover.ts +136 -0
  126. package/src/recipes/weight-hover/weight-hover.css +7 -0
  127. package/dist/chunk-JQMJQSXD.js.map +0 -1
  128. package/dist/chunk-XDWBVFHQ.js.map +0 -1
  129. /package/dist/{chunk-BFC27F2G.js.map → chunk-IKQ5FODN.js.map} +0 -0
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CustomEase,
2
3
  EASE,
3
4
  ELASTIC,
4
5
  Flip,
@@ -16,7 +17,7 @@ import {
16
17
  revealFrom,
17
18
  splitGraphemes,
18
19
  takeMovement
19
- } from "./chunk-JQMJQSXD.js";
20
+ } from "./chunk-TFNRIZHU.js";
20
21
  import {
21
22
  clamp,
22
23
  damp,
@@ -3271,6 +3272,1193 @@ function createDockPill(root, options = {}) {
3271
3272
  }, destroy: () => scope.dispose() };
3272
3273
  }
3273
3274
 
3275
+ // src/recipes/joined-cta/create-joined-cta.ts
3276
+ function createJoinedCta(root, options = {}) {
3277
+ if (!root) return inert({ expand() {
3278
+ }, collapse() {
3279
+ }, expanded: false });
3280
+ const scope = createScope();
3281
+ let expanded = false;
3282
+ let pointer2 = false;
3283
+ scope.attr(root, "style", root.getAttribute("style"));
3284
+ scope.attr(root, "data-mk-ready", "");
3285
+ if (Number.isFinite(options.duration)) root.style.setProperty("--mk-joined-duration", `${Math.max(0, options.duration)}s`);
3286
+ const set = (value) => {
3287
+ if (!scope.disposed) {
3288
+ expanded = value;
3289
+ scope.attr(root, "data-mk-expanded", value ? "" : null);
3290
+ }
3291
+ };
3292
+ scope.on(root, "pointerenter", () => {
3293
+ pointer2 = true;
3294
+ set(true);
3295
+ });
3296
+ scope.on(root, "pointerleave", () => {
3297
+ pointer2 = false;
3298
+ if (!root.contains(document.activeElement)) set(false);
3299
+ });
3300
+ scope.on(root, "focusin", () => set(true));
3301
+ scope.on(root, "focusout", (event) => {
3302
+ if (!pointer2 && !root.contains(event.relatedTarget)) set(false);
3303
+ });
3304
+ return { expand: () => set(true), collapse: () => set(false), get expanded() {
3305
+ return expanded;
3306
+ }, destroy: () => scope.dispose() };
3307
+ }
3308
+
3309
+ // src/recipes/image-zoom/create-image-zoom.ts
3310
+ function createImageZoom(root, options = {}) {
3311
+ if (!root) return inert({ open() {
3312
+ }, active: false });
3313
+ const scope = createScope();
3314
+ let active = false;
3315
+ let pointer2 = false;
3316
+ scope.attr(root, "style", root.getAttribute("style"));
3317
+ scope.attr(root, "data-mk-ready", "");
3318
+ const setVariable = (name, value, suffix = "") => {
3319
+ if (Number.isFinite(value)) root.style.setProperty(name, `${Math.max(0, value)}${suffix}`);
3320
+ };
3321
+ setVariable("--mk-zoom-duration", options.duration, "s");
3322
+ setVariable("--mk-zoom-frame-scale", options.frameScale);
3323
+ setVariable("--mk-zoom-image-scale", options.imageScale);
3324
+ const set = (on) => {
3325
+ if (!scope.disposed) {
3326
+ active = on;
3327
+ scope.attr(root, "data-mk-active", on ? "" : null);
3328
+ }
3329
+ };
3330
+ scope.on(root, "pointerenter", () => {
3331
+ pointer2 = true;
3332
+ set(true);
3333
+ });
3334
+ scope.on(root, "pointerleave", () => {
3335
+ pointer2 = false;
3336
+ if (!root.contains(document.activeElement)) set(false);
3337
+ });
3338
+ scope.on(root, "focusin", () => set(true));
3339
+ scope.on(root, "focusout", (event) => {
3340
+ if (!pointer2 && !root.contains(event.relatedTarget)) set(false);
3341
+ });
3342
+ return { open: set, get active() {
3343
+ return active;
3344
+ }, destroy: () => scope.dispose() };
3345
+ }
3346
+
3347
+ // src/recipes/font-cycle/create-font-cycle.ts
3348
+ function applyFace(element, face2) {
3349
+ if (face2.family) element.style.fontFamily = face2.family;
3350
+ if (face2.weight !== void 0) element.style.fontWeight = String(face2.weight);
3351
+ if (face2.style) element.style.fontStyle = face2.style;
3352
+ const axes = Object.entries(face2.axes ?? {}).filter(([tag, value]) => /^[\w ]{4}$/.test(tag) && Number.isFinite(value));
3353
+ if (axes.length) element.style.fontVariationSettings = axes.map(([tag, value]) => `"${tag}" ${value}`).join(", ");
3354
+ }
3355
+ function createFontCycle(root, options = {}) {
3356
+ if (!root || typeof document === "undefined") return inert({ play() {
3357
+ }, reset() {
3358
+ }, active: false });
3359
+ registerMotion();
3360
+ const scope = createScope();
3361
+ const original = [...root.childNodes];
3362
+ const text = options.text ?? root.textContent ?? "";
3363
+ const faces = options.faces ?? [
3364
+ { weight: 500, axes: { wght: 500, wdth: 75 } },
3365
+ { family: root.dataset.mkFontAlternate, weight: 900, axes: { wght: 900, wdth: 125 } }
3366
+ ];
3367
+ const duration = Math.max(0, options.duration ?? 0.6);
3368
+ const stagger = Math.max(0, options.stagger ?? 0.035);
3369
+ const tracks = [];
3370
+ let animation;
3371
+ let active = false;
3372
+ let hovered = false;
3373
+ let focused = false;
3374
+ const copy = document.createElement("span");
3375
+ copy.className = "mk-font-cycle__accessible";
3376
+ copy.textContent = text;
3377
+ const visual = document.createElement("span");
3378
+ visual.className = "mk-font-cycle__visual";
3379
+ visual.setAttribute("aria-hidden", "true");
3380
+ for (const [index, letter] of splitGraphemes(text, Number.MAX_SAFE_INTEGER).entries()) {
3381
+ const character = document.createElement("span");
3382
+ character.className = "mk-font-cycle__char";
3383
+ const track = document.createElement("span");
3384
+ track.className = "mk-font-cycle__track";
3385
+ for (let side = 0; side < 2; side++) {
3386
+ const face2 = document.createElement("span");
3387
+ face2.className = "mk-font-cycle__face";
3388
+ face2.textContent = letter === " " ? "\xA0" : letter;
3389
+ applyFace(face2, faces[(index + side) % 2]);
3390
+ const sizer = face2.cloneNode(true);
3391
+ sizer.className = "mk-font-cycle__sizer";
3392
+ character.append(sizer);
3393
+ track.append(face2);
3394
+ }
3395
+ character.append(track);
3396
+ visual.append(character);
3397
+ tracks.push(track);
3398
+ }
3399
+ root.replaceChildren(copy, visual);
3400
+ const overlaySrc = options.overlaySrc ?? root.dataset.mkFontOverlay;
3401
+ if (overlaySrc) {
3402
+ const overlay = document.createElement("img");
3403
+ overlay.className = "mk-font-cycle__overlay";
3404
+ overlay.alt = "";
3405
+ overlay.setAttribute("aria-hidden", "true");
3406
+ scope.attr(root, "data-mk-font-overlay-ready", null);
3407
+ const loaded = () => {
3408
+ if (!scope.disposed) scope.attr(root, "data-mk-font-overlay-ready", overlay.naturalWidth > 0 ? "" : null);
3409
+ };
3410
+ scope.on(overlay, "load", loaded);
3411
+ scope.on(overlay, "error", () => {
3412
+ if (!scope.disposed) scope.attr(root, "data-mk-font-overlay-ready", null);
3413
+ });
3414
+ overlay.src = overlaySrc;
3415
+ root.append(overlay);
3416
+ if (overlay.complete) loaded();
3417
+ }
3418
+ scope.add(() => root.replaceChildren(...original));
3419
+ scope.attr(root, "class", `${root.className} mk-font-cycle`.trim());
3420
+ scope.attr(root, "data-mk-ready", "");
3421
+ const setActive = (next) => {
3422
+ if (scope.disposed || next === active) return;
3423
+ active = next;
3424
+ scope.attr(root, "data-mk-active", next ? "" : null);
3425
+ animation?.kill();
3426
+ if (reduced() || duration === 0) gsap.set(tracks, { yPercent: next ? -50 : 0 });
3427
+ else animation = gsap.to(tracks, { yPercent: next ? -50 : 0, duration, stagger, ease: "power3.out", overwrite: true });
3428
+ };
3429
+ scope.on(root, "mouseenter", () => {
3430
+ hovered = true;
3431
+ setActive(true);
3432
+ });
3433
+ scope.on(root, "mouseleave", () => {
3434
+ hovered = false;
3435
+ setActive(focused);
3436
+ });
3437
+ scope.on(root, "focusin", () => {
3438
+ focused = true;
3439
+ setActive(true);
3440
+ });
3441
+ scope.on(root, "focusout", (event) => {
3442
+ if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
3443
+ focused = false;
3444
+ setActive(hovered);
3445
+ });
3446
+ scope.add(() => animation?.kill());
3447
+ return { play: () => setActive(true), reset: () => setActive(false), get active() {
3448
+ return active;
3449
+ }, destroy: () => scope.dispose() };
3450
+ }
3451
+
3452
+ // src/recipes/thumbnail-nav/create-thumbnail-nav.ts
3453
+ var panelSequence = 0;
3454
+ function createThumbnailNav(root, options = {}) {
3455
+ const items = root ? [...root.querySelectorAll("[data-mk-thumbnail-item]")] : [];
3456
+ if (!root || !items.length || typeof document === "undefined") return inert({ open() {
3457
+ }, close() {
3458
+ }, toggle() {
3459
+ }, select() {
3460
+ }, index: 0, expanded: false });
3461
+ registerMotion();
3462
+ const scope = createScope();
3463
+ const original = [...root.childNodes];
3464
+ const duration = Math.max(0, options.duration ?? 0.5);
3465
+ const disabled = (item) => item.matches(':disabled, [aria-disabled="true"]');
3466
+ const name = (item) => item.dataset.mkLabel ?? item.textContent?.trim() ?? "";
3467
+ let index = 0;
3468
+ let expanded = false;
3469
+ let animation;
3470
+ const button = document.createElement("button");
3471
+ button.type = "button";
3472
+ button.className = "mk-thumbnail-nav__toggle";
3473
+ button.setAttribute("aria-expanded", "false");
3474
+ const preview = document.createElement("span");
3475
+ preview.className = "mk-thumbnail-nav__preview";
3476
+ preview.setAttribute("aria-hidden", "true");
3477
+ const copy = document.createElement("span");
3478
+ copy.className = "mk-thumbnail-nav__copy";
3479
+ const kicker = document.createElement("span");
3480
+ kicker.className = "mk-thumbnail-nav__kicker";
3481
+ kicker.textContent = options.label ?? root.dataset.mkLabel ?? "Categor\xEDa";
3482
+ const label = document.createElement("span");
3483
+ label.className = "mk-thumbnail-nav__label";
3484
+ copy.append(kicker, label);
3485
+ const arrow = document.createElement("span");
3486
+ arrow.className = "mk-thumbnail-nav__chevron";
3487
+ arrow.setAttribute("aria-hidden", "true");
3488
+ arrow.textContent = "\u2304";
3489
+ button.append(preview, copy, arrow);
3490
+ const panel = document.createElement("div");
3491
+ panel.className = "mk-thumbnail-nav__panel";
3492
+ panel.id = `mk-thumbnail-panel-${++panelSequence}`;
3493
+ while (document.getElementById(panel.id)) panel.id = `mk-thumbnail-panel-${++panelSequence}`;
3494
+ panel.hidden = true;
3495
+ panel.inert = true;
3496
+ button.setAttribute("aria-controls", panel.id);
3497
+ panel.append(...original);
3498
+ root.replaceChildren(button, panel);
3499
+ scope.add(() => root.replaceChildren(...original));
3500
+ scope.attr(root, "class", `${root.className} mk-thumbnail-nav`.trim());
3501
+ scope.attr(root, "style", root.getAttribute("style"));
3502
+ root.style.setProperty("--mk-thumbnail-nav-duration", `${duration}s`);
3503
+ scope.attr(root, "data-mk-ready", "");
3504
+ const updatePreview = () => {
3505
+ label.textContent = name(items[index]);
3506
+ button.setAttribute("aria-label", `${kicker.textContent}: ${label.textContent}`);
3507
+ preview.replaceChildren();
3508
+ for (let offset = 0; offset < Math.min(3, items.length); offset++) {
3509
+ const item = items[wrap(index + offset, items.length)];
3510
+ const thumb = document.createElement("span");
3511
+ thumb.className = "mk-thumbnail-nav__thumb";
3512
+ thumb.style.setProperty("--mk-thumbnail-order", String(offset));
3513
+ const source = item.querySelector("img");
3514
+ if (source) {
3515
+ const image = source.cloneNode(false);
3516
+ image.removeAttribute("id");
3517
+ image.alt = "";
3518
+ thumb.append(image);
3519
+ } else thumb.textContent = name(item).slice(0, 1);
3520
+ preview.append(thumb);
3521
+ }
3522
+ };
3523
+ const focusItem = (candidate, direction = 1) => {
3524
+ for (let attempt = 0; attempt < items.length; attempt++) {
3525
+ const current = items[wrap(candidate, items.length)];
3526
+ if (!disabled(current)) {
3527
+ current.focus();
3528
+ return;
3529
+ }
3530
+ candidate += direction;
3531
+ }
3532
+ };
3533
+ const close = (restoreFocus = panel.contains(document.activeElement)) => {
3534
+ if (scope.disposed || !expanded) return;
3535
+ expanded = false;
3536
+ button.setAttribute("aria-expanded", "false");
3537
+ scope.attr(root, "data-mk-expanded", null);
3538
+ panel.inert = true;
3539
+ animation?.kill();
3540
+ if (restoreFocus) button.focus();
3541
+ if (reduced() || duration === 0) panel.hidden = true;
3542
+ else animation = gsap.to(panel, { autoAlpha: 0, y: 10, scale: 0.98, duration: duration * 0.6, ease: "power3.out", overwrite: true, onComplete: () => {
3543
+ panel.hidden = true;
3544
+ } });
3545
+ };
3546
+ const open = () => {
3547
+ if (scope.disposed || expanded) return;
3548
+ expanded = true;
3549
+ panel.hidden = false;
3550
+ panel.inert = false;
3551
+ button.setAttribute("aria-expanded", "true");
3552
+ scope.attr(root, "data-mk-expanded", "");
3553
+ animation?.kill();
3554
+ if (reduced() || duration === 0) gsap.set(panel, { autoAlpha: 1, y: 0, scale: 1 });
3555
+ else animation = gsap.fromTo(panel, { autoAlpha: 0, y: 12, scale: 0.96 }, { autoAlpha: 1, y: 0, scale: 1, duration, ease: "power3.out", overwrite: true });
3556
+ };
3557
+ const select = (next, notify = true) => {
3558
+ if (scope.disposed || !Number.isFinite(next)) return false;
3559
+ const candidate = wrap(Math.round(next), items.length);
3560
+ const item = items[candidate];
3561
+ if (disabled(item)) return false;
3562
+ if (notify && !root.dispatchEvent(new CustomEvent("mk:select", { bubbles: true, cancelable: true, detail: { index: candidate, item } }))) return false;
3563
+ index = candidate;
3564
+ items.forEach((entry, position) => scope.attr(entry, "aria-current", position === index ? "page" : null));
3565
+ updatePreview();
3566
+ if (notify) options.onSelect?.(index, item);
3567
+ return true;
3568
+ };
3569
+ scope.on(button, "click", () => {
3570
+ if (expanded) close();
3571
+ else open();
3572
+ });
3573
+ scope.on(root, "click", (event) => {
3574
+ const item = event.target.closest("[data-mk-thumbnail-item]");
3575
+ if (!item || !items.includes(item)) return;
3576
+ if (!select(items.indexOf(item))) {
3577
+ event.preventDefault();
3578
+ return;
3579
+ }
3580
+ close(true);
3581
+ });
3582
+ scope.on(root, "keydown", (event) => {
3583
+ if (event.key === "Escape" && expanded) {
3584
+ event.preventDefault();
3585
+ event.stopPropagation();
3586
+ close(true);
3587
+ return;
3588
+ }
3589
+ const current = items.indexOf(document.activeElement);
3590
+ const onTrigger = document.activeElement === button;
3591
+ const direction = event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : event.key === "ArrowUp" || event.key === "ArrowLeft" ? -1 : 0;
3592
+ if (!direction && event.key !== "Home" && event.key !== "End" || !onTrigger && current < 0) return;
3593
+ event.preventDefault();
3594
+ open();
3595
+ const candidate = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : onTrigger ? index : current + direction;
3596
+ focusItem(candidate, event.key === "End" ? -1 : direction || 1);
3597
+ });
3598
+ scope.on(root, "focusout", (event) => {
3599
+ if (event.relatedTarget instanceof Node && root.contains(event.relatedTarget)) return;
3600
+ scope.timeout(() => {
3601
+ if (!scope.disposed && !root.contains(document.activeElement)) close(false);
3602
+ }, 0);
3603
+ });
3604
+ scope.on(document, "pointerdown", (event) => {
3605
+ if (event.target instanceof Node && !root.contains(event.target)) close(false);
3606
+ });
3607
+ scope.add(() => animation?.kill());
3608
+ const initial = options.index ?? Math.max(0, items.findIndex((item) => item.getAttribute("aria-current") === "page"));
3609
+ const enabled = items.findIndex((item) => !disabled(item));
3610
+ if (!select(initial, false)) {
3611
+ if (enabled >= 0) select(enabled, false);
3612
+ else {
3613
+ updatePreview();
3614
+ button.disabled = true;
3615
+ }
3616
+ }
3617
+ return { open, close: () => close(), toggle: () => {
3618
+ if (expanded) close();
3619
+ else open();
3620
+ }, select: (next) => {
3621
+ select(next);
3622
+ }, get index() {
3623
+ return index;
3624
+ }, get expanded() {
3625
+ return expanded;
3626
+ }, destroy: () => scope.dispose() };
3627
+ }
3628
+
3629
+ // src/recipes/action-button/create-action-button.ts
3630
+ function createActionButton(root, options = {}) {
3631
+ if (!root) return inert({ setPressed() {
3632
+ }, pressed: false });
3633
+ const scope = createScope();
3634
+ const variant = options.variant ?? root.getAttribute("data-mk-action-variant") ?? "primary";
3635
+ scope.attr(root, "data-mk-action-variant", variant);
3636
+ scope.attr(root, "data-mk-action-ready", "");
3637
+ const label = root.querySelector("[data-mk-action-label]");
3638
+ if (label && variant === "film") {
3639
+ const nodes = [...label.childNodes];
3640
+ const line = document.createElement("span");
3641
+ line.className = "mk-action-button__line";
3642
+ line.append(...nodes);
3643
+ const copy = document.createElement("span");
3644
+ copy.className = "mk-action-button__copy";
3645
+ copy.setAttribute("aria-hidden", "true");
3646
+ copy.textContent = line.textContent;
3647
+ label.append(line, copy);
3648
+ scope.add(() => {
3649
+ label.replaceChildren(...nodes);
3650
+ });
3651
+ }
3652
+ let pressed = options.pressed ?? root.getAttribute("aria-pressed") === "true";
3653
+ if (options.toggle || options.pressed !== void 0) scope.attr(root, "aria-pressed", String(pressed));
3654
+ const setPressed = (next) => {
3655
+ if (scope.disposed || next === pressed) return;
3656
+ pressed = next;
3657
+ scope.attr(root, "aria-pressed", String(pressed));
3658
+ options.onPressedChange?.(pressed);
3659
+ root.dispatchEvent(new CustomEvent("mk:action-change", { bubbles: true, detail: { pressed } }));
3660
+ };
3661
+ scope.on(root, "click", (event) => {
3662
+ if (event.defaultPrevented) return;
3663
+ if (root.hasAttribute("disabled") || root.getAttribute("aria-disabled") === "true") {
3664
+ event.preventDefault();
3665
+ return;
3666
+ }
3667
+ if (options.toggle) setPressed(!pressed);
3668
+ });
3669
+ return { setPressed, get pressed() {
3670
+ return pressed;
3671
+ }, destroy: () => scope.dispose() };
3672
+ }
3673
+
3674
+ // src/recipes/film-hero/create-film-hero.ts
3675
+ function createFilmHero(root, options = {}) {
3676
+ if (!root) return inert({ replay() {
3677
+ }, refresh() {
3678
+ } });
3679
+ const scope = createScope();
3680
+ scope.attr(root, "data-mk-film-ready", "");
3681
+ scope.attr(root, "data-mk-film-edge", options.edge ?? "organic");
3682
+ scope.attr(root, "style", root.getAttribute("style"));
3683
+ const still = reduced();
3684
+ const lines = [...root.querySelectorAll("[data-mk-film-line]")];
3685
+ const rules = [...root.querySelectorAll("[data-mk-film-rule]")];
3686
+ [...lines, ...rules].forEach((element) => scope.attr(element, "style", element.getAttribute("style")));
3687
+ let timeline = null;
3688
+ const context = gsap.context(() => {
3689
+ }, root);
3690
+ const replay = () => {
3691
+ if (scope.disposed || !entrances()) return;
3692
+ registerMotion();
3693
+ context.add(() => {
3694
+ timeline?.kill();
3695
+ timeline = gsap.timeline();
3696
+ if (lines.length) timeline.fromTo(lines, { yPercent: 120 }, { yPercent: 0, duration: options.duration ?? 0.7, stagger: options.stagger ?? 0.08, ease: "power3.out" }, 0);
3697
+ if (rules.length) timeline.fromTo(rules, { scaleX: 0, transformOrigin: "left center" }, { scaleX: 1, duration: 0.9, stagger: 0.06, ease: "power3.out" }, 0.08);
3698
+ });
3699
+ };
3700
+ replay();
3701
+ const scroller = options.scroller ?? root.closest("[data-mk-scroll-root]") ?? window;
3702
+ let frame = 0;
3703
+ const refresh = () => {
3704
+ if (scope.disposed || still) return;
3705
+ const rect = root.getBoundingClientRect();
3706
+ const view = scroller instanceof HTMLElement ? scroller.getBoundingClientRect() : { top: 0, height: window.innerHeight };
3707
+ const progress = Math.max(0, Math.min(1, (view.top - rect.top) / Math.max(1, rect.height)));
3708
+ root.style.setProperty("--mk-film-shift", `${(progress * rect.height * (options.parallax ?? 0.12)).toFixed(2)}px`);
3709
+ root.style.setProperty("--mk-film-scale", String((options.scale ?? 1.08) - progress * Math.max(0, (options.scale ?? 1.08) - 1)));
3710
+ };
3711
+ if (!still) {
3712
+ const schedule = () => {
3713
+ if (frame) return;
3714
+ frame = requestAnimationFrame(() => {
3715
+ frame = 0;
3716
+ refresh();
3717
+ });
3718
+ };
3719
+ scope.on(scroller, "scroll", schedule, { passive: true });
3720
+ scope.on(window, "resize", schedule, { passive: true });
3721
+ if (typeof ResizeObserver === "function") scope.observe(new ResizeObserver(schedule)).observe(root);
3722
+ scope.add(() => cancelAnimationFrame(frame));
3723
+ refresh();
3724
+ }
3725
+ scope.add(() => {
3726
+ timeline?.kill();
3727
+ context.revert();
3728
+ });
3729
+ return { replay, refresh, destroy: () => scope.dispose() };
3730
+ }
3731
+
3732
+ // src/recipes/editorial-reveal/create-editorial-reveal.ts
3733
+ var nextScope = 0;
3734
+ function createEditorialReveal(root, options = {}) {
3735
+ if (!root) return inert({ play() {
3736
+ }, reset() {
3737
+ } });
3738
+ registerMotion();
3739
+ const scope = createScope();
3740
+ const context = gsap.context(() => {
3741
+ }, root);
3742
+ const trigger = options.trigger ?? "scroll";
3743
+ const duration = options.duration ?? 1.2;
3744
+ const stagger = options.stagger ?? 0.03;
3745
+ const hoverDuration = options.hoverDuration ?? 0.5;
3746
+ const ease = options.ease ?? "mk-editorial-reference";
3747
+ CustomEase.create("mk-editorial-reference", "M0,0 C0.6,0 0,1 1,1");
3748
+ const preserve = (el) => ["style", "data-mk-ready", "aria-label"].forEach((name) => scope.attr(el, name, el.getAttribute(name)));
3749
+ preserve(root);
3750
+ root.querySelectorAll("[data-mk-editorial-title],[data-mk-editorial-group],[data-mk-cascade],[data-mk-editorial-image],[data-mk-parallax-target],[data-mk-editorial-header],[data-mk-editorial-card-image],[data-mk-editorial-button-content]").forEach(preserve);
3751
+ const titles = [...root.querySelectorAll("[data-mk-editorial-title]")].map((el) => createMaskText(el, { by: "lines", trigger, once: true, y: options.y ?? 110, duration, stagger, ease, delay: 0 }));
3752
+ const groups = [...root.querySelectorAll("[data-mk-editorial-group]")].map((el) => createCascade(el, { trigger, duration, step: stagger, once: true }));
3753
+ const images = options.parallax === false ? [] : [...root.querySelectorAll("[data-mk-editorial-image]")].map((el) => createParallax(el, { amount: 0.25 }));
3754
+ const header = root.querySelector("[data-mk-editorial-header]");
3755
+ const sections = [...root.querySelectorAll("[data-mk-tone]")];
3756
+ const scopeId = String(++nextScope);
3757
+ sections.forEach((el) => scope.attr(el, "data-mk-editorial-scope", scopeId));
3758
+ const contrast = header ? createContrast(header, { sections: `[data-mk-editorial-scope="${scopeId}"]` }) : null;
3759
+ scope.add(() => {
3760
+ titles.forEach((instance) => instance.destroy());
3761
+ groups.forEach((instance) => instance.destroy());
3762
+ images.forEach((instance) => instance.destroy());
3763
+ contrast?.destroy();
3764
+ context.revert();
3765
+ });
3766
+ root.querySelectorAll("[data-mk-editorial-button]").forEach((button) => {
3767
+ const content = button.querySelector("[data-mk-editorial-button-content]");
3768
+ if (!content) return;
3769
+ let pointerInside = false;
3770
+ const render = () => {
3771
+ const active = pointerInside || button.contains(root.ownerDocument.activeElement);
3772
+ context.add(() => gsap.to(content, { x: active && !reduced() ? `${options.buttonShift ?? 2.25}em` : "0em", duration: reduced() ? 0 : hoverDuration, ease, overwrite: true }));
3773
+ };
3774
+ scope.on(button, "pointerenter", () => {
3775
+ pointerInside = true;
3776
+ render();
3777
+ });
3778
+ scope.on(button, "pointerleave", () => {
3779
+ pointerInside = false;
3780
+ render();
3781
+ });
3782
+ scope.on(button, "focusin", render);
3783
+ scope.on(button, "focusout", () => {
3784
+ scope.timeout(render, 0);
3785
+ });
3786
+ });
3787
+ const cursor = root.querySelector("[data-mk-editorial-cursor]");
3788
+ if (cursor) {
3789
+ preserve(cursor);
3790
+ scope.attr(cursor, "aria-hidden", "true");
3791
+ scope.attr(cursor, "hidden", "");
3792
+ }
3793
+ root.querySelectorAll("[data-mk-editorial-card]").forEach((card) => {
3794
+ const image = card.querySelector("[data-mk-editorial-card-image]");
3795
+ let pointerInside = false;
3796
+ const render = () => {
3797
+ const active = pointerInside || card.contains(root.ownerDocument.activeElement);
3798
+ scope.attr(card, "data-mk-editorial-active", active ? "" : null);
3799
+ if (image) context.add(() => gsap.to(image, { scale: active && !reduced() ? options.imageScale ?? 1.04 : 1, duration: reduced() ? 0 : hoverDuration, ease, overwrite: true }));
3800
+ };
3801
+ scope.on(card, "pointerenter", (event) => {
3802
+ pointerInside = true;
3803
+ render();
3804
+ if (cursor && !reduced() && event.pointerType !== "touch") scope.attr(cursor, "hidden", null);
3805
+ });
3806
+ scope.on(card, "pointerleave", () => {
3807
+ pointerInside = false;
3808
+ render();
3809
+ if (cursor) scope.attr(cursor, "hidden", "");
3810
+ });
3811
+ scope.on(card, "pointermove", (event) => {
3812
+ if (!cursor || reduced() || event.pointerType === "touch") return;
3813
+ const rect = root.getBoundingClientRect();
3814
+ cursor.style.transform = `translate(${event.clientX - rect.left + 16}px, ${event.clientY - rect.top + 16}px)`;
3815
+ });
3816
+ scope.on(card, "focusin", render);
3817
+ scope.on(card, "focusout", () => {
3818
+ scope.timeout(render, 0);
3819
+ });
3820
+ });
3821
+ scope.attr(root, "data-mk-ready", "");
3822
+ return {
3823
+ play() {
3824
+ if (!scope.disposed) {
3825
+ titles.forEach((instance) => instance.play());
3826
+ groups.forEach((instance) => instance.play());
3827
+ }
3828
+ },
3829
+ reset() {
3830
+ if (!scope.disposed) {
3831
+ titles.forEach((instance) => instance.hide());
3832
+ groups.forEach((instance) => instance.reset());
3833
+ }
3834
+ },
3835
+ destroy() {
3836
+ scope.dispose();
3837
+ }
3838
+ };
3839
+ }
3840
+
3841
+ // src/recipes/weight-hover/create-weight-hover.ts
3842
+ var FAR = -1e5;
3843
+ function createWeightHover(root, options = {}) {
3844
+ if (!root) return inert({ pointAt() {
3845
+ } });
3846
+ const scope = createScope();
3847
+ scope.attr(root, "data-mk-ready", "");
3848
+ if (reduced()) return { pointAt() {
3849
+ }, destroy: () => scope.dispose() };
3850
+ const num = (value, fallback) => Number.isFinite(value) ? value : fallback;
3851
+ const rest = num(options.rest, 900);
3852
+ const near = num(options.near, 200);
3853
+ const radius = Math.max(1, num(options.radius, 400));
3854
+ const lerp = Math.min(1, Math.max(0.01, num(options.lerp, 0.1)));
3855
+ const axis = /^[A-Za-z]{4}$/.test(options.axis ?? "") ? options.axis : "wght";
3856
+ const setting = (value) => `"${axis}" ${value.toFixed(1)}`;
3857
+ const original = [...root.childNodes];
3858
+ const copy = document.createElement("span");
3859
+ copy.className = "mk-sr-only";
3860
+ const spoken = root.cloneNode(true);
3861
+ spoken.querySelectorAll("br").forEach((br) => br.replaceWith(" "));
3862
+ copy.textContent = spoken.textContent?.replace(/\s+/g, " ").trim() ?? "";
3863
+ const visual = document.createElement("span");
3864
+ visual.className = "mk-weight-hover__visual";
3865
+ visual.setAttribute("aria-hidden", "true");
3866
+ visual.append(...original.map((node) => node.cloneNode(true)));
3867
+ const letters2 = [];
3868
+ const walker = document.createTreeWalker(visual, NodeFilter.SHOW_TEXT);
3869
+ const texts = [];
3870
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) texts.push(node);
3871
+ for (const text of texts) {
3872
+ const fragment = document.createDocumentFragment();
3873
+ for (const char of text.data) {
3874
+ if (/\s/.test(char)) {
3875
+ fragment.append(char);
3876
+ continue;
3877
+ }
3878
+ const span = document.createElement("span");
3879
+ span.dataset.mkWeightChar = "";
3880
+ span.textContent = char;
3881
+ span.style.fontVariationSettings = setting(rest);
3882
+ fragment.append(span);
3883
+ letters2.push({ span, value: rest, x: 0, y: 0 });
3884
+ }
3885
+ text.replaceWith(fragment);
3886
+ }
3887
+ root.replaceChildren(copy, visual);
3888
+ scope.add(() => root.replaceChildren(...original));
3889
+ const measure = () => {
3890
+ const box = root.getBoundingClientRect();
3891
+ for (const letter of letters2) {
3892
+ const r = letter.span.getBoundingClientRect();
3893
+ letter.x = r.left + r.width / 2 - box.left;
3894
+ letter.y = r.top + r.height / 2 - box.top;
3895
+ }
3896
+ };
3897
+ measure();
3898
+ if (typeof ResizeObserver === "function") scope.observe(new ResizeObserver(measure)).observe(root);
3899
+ document.fonts?.ready.then(() => {
3900
+ if (!scope.disposed) measure();
3901
+ });
3902
+ const pointer2 = { x: FAR, y: FAR };
3903
+ let frame = 0;
3904
+ const tick = () => {
3905
+ frame = 0;
3906
+ if (scope.disposed) return;
3907
+ const box = root.getBoundingClientRect();
3908
+ let moving = false;
3909
+ for (const letter of letters2) {
3910
+ const distance = Math.hypot(pointer2.x - (box.left + letter.x), pointer2.y - (box.top + letter.y));
3911
+ const d = Math.max(0, 1 - distance / radius);
3912
+ const target = rest + (near - rest) * d * d * (3 - 2 * d);
3913
+ letter.value += (target - letter.value) * lerp;
3914
+ if (Math.abs(target - letter.value) > 0.5) moving = true;
3915
+ else letter.value = target;
3916
+ letter.span.style.fontVariationSettings = setting(letter.value);
3917
+ }
3918
+ if (moving) frame = requestAnimationFrame(tick);
3919
+ };
3920
+ const wake = () => {
3921
+ if (!frame && !scope.disposed) frame = requestAnimationFrame(tick);
3922
+ };
3923
+ scope.add(() => cancelAnimationFrame(frame));
3924
+ scope.on(window, "pointermove", (event) => {
3925
+ const e = event;
3926
+ if (!options.anyPointer && e.pointerType && e.pointerType !== "mouse") return;
3927
+ pointer2.x = e.clientX;
3928
+ pointer2.y = e.clientY;
3929
+ wake();
3930
+ }, { passive: true });
3931
+ scope.on(document, "pointerleave", () => {
3932
+ pointer2.x = pointer2.y = FAR;
3933
+ wake();
3934
+ });
3935
+ return {
3936
+ pointAt(point) {
3937
+ if (scope.disposed) return;
3938
+ pointer2.x = point ? point.x : FAR;
3939
+ pointer2.y = point ? point.y : FAR;
3940
+ wake();
3941
+ },
3942
+ destroy: () => scope.dispose()
3943
+ };
3944
+ }
3945
+
3946
+ // src/recipes/sticker/create-sticker.ts
3947
+ var easeOutCubic = (t) => 1 - (1 - t) ** 3;
3948
+ var clamp2 = (v, min, max) => Math.min(max, Math.max(min, v));
3949
+ function clipHalfPlane(poly, m, n) {
3950
+ const side = (p) => (p.x - m.x) * n.x + (p.y - m.y) * n.y;
3951
+ const out = [];
3952
+ for (let i = 0; i < poly.length; i++) {
3953
+ const a = poly[i];
3954
+ const b = poly[(i + 1) % poly.length];
3955
+ const sa = side(a);
3956
+ const sb = side(b);
3957
+ if (sa >= 0) out.push(a);
3958
+ if (sa >= 0 !== sb >= 0) {
3959
+ const t = sa / (sa - sb);
3960
+ out.push({ x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t });
3961
+ }
3962
+ }
3963
+ return out;
3964
+ }
3965
+ var area = (poly) => {
3966
+ let sum = 0;
3967
+ for (let i = 0; i < poly.length; i++) {
3968
+ const a = poly[i];
3969
+ const b = poly[(i + 1) % poly.length];
3970
+ sum += a.x * b.y - b.x * a.y;
3971
+ }
3972
+ return Math.abs(sum) / 2;
3973
+ };
3974
+ var polygon = (poly) => poly.length < 3 ? "polygon(0 0, 0 0, 0 0)" : `polygon(${poly.map((p) => `${p.x.toFixed(2)}px ${p.y.toFixed(2)}px`).join(", ")})`;
3975
+ function createSticker(root, options = {}) {
3976
+ if (!root) return inert({ state: "idle" });
3977
+ const scope = createScope();
3978
+ const still = reduced();
3979
+ const detachAt = clamp2(Number.isFinite(options.detachAt) ? options.detachAt : 0.3, 0.05, 1);
3980
+ const returnDelay = Math.max(0, Number.isFinite(options.returnDelay) ? options.returnDelay : 2500);
3981
+ let state = "idle";
3982
+ const frames = /* @__PURE__ */ new Set();
3983
+ const raf = (fn) => {
3984
+ const id = requestAnimationFrame((time) => {
3985
+ frames.delete(id);
3986
+ fn(time);
3987
+ });
3988
+ frames.add(id);
3989
+ };
3990
+ scope.add(() => {
3991
+ frames.forEach(cancelAnimationFrame);
3992
+ frames.clear();
3993
+ });
3994
+ scope.attr(root, "data-mk-ready", "");
3995
+ scope.attr(root, "style", root.getAttribute("style"));
3996
+ if (options.holo !== false) scope.attr(root, "data-mk-holo", "");
3997
+ const original = [...root.childNodes];
3998
+ const peelBox = document.createElement("div");
3999
+ peelBox.className = "mk-sticker__peel";
4000
+ const top = document.createElement("div");
4001
+ top.className = "mk-sticker__top";
4002
+ top.append(...original);
4003
+ peelBox.append(top);
4004
+ root.append(peelBox);
4005
+ scope.add(() => root.replaceChildren(...original));
4006
+ let back = null;
4007
+ if (options.holo !== false) {
4008
+ const holo = { x: 0.5, y: 0.5 };
4009
+ let mouse = null;
4010
+ let visible = typeof IntersectionObserver !== "function";
4011
+ const hoverless = typeof matchMedia === "function" && matchMedia("(hover: none)").matches;
4012
+ root.style.setProperty("--mk-sticker-x", "0.5");
4013
+ root.style.setProperty("--mk-sticker-y", "0.5");
4014
+ if (!still) {
4015
+ scope.on(window, "pointermove", (event) => {
4016
+ const e = event;
4017
+ if (e.pointerType === "mouse") mouse = { x: e.clientX, y: e.clientY };
4018
+ }, { passive: true });
4019
+ scope.on(document, "pointerleave", () => {
4020
+ mouse = null;
4021
+ });
4022
+ if (typeof IntersectionObserver === "function") {
4023
+ scope.observe(new IntersectionObserver(([entry]) => {
4024
+ const was = visible;
4025
+ visible = entry.isIntersecting;
4026
+ if (visible && !was) raf(loop);
4027
+ })).observe(root);
4028
+ }
4029
+ const loop = (time) => {
4030
+ if (scope.disposed || !visible) return;
4031
+ const box = top.getBoundingClientRect();
4032
+ let x;
4033
+ let y;
4034
+ if (mouse && !hoverless && box.width && box.height) {
4035
+ x = clamp2((mouse.x - box.left) / box.width, -0.5, 1.5);
4036
+ y = clamp2((mouse.y - box.top) / box.height, -0.5, 1.5);
4037
+ } else {
4038
+ x = 0.5 + Math.sin(time / 1600 + box.left / 200) * 0.6;
4039
+ y = 0.5 + Math.cos(time / 2100 + box.top / 200) * 0.6;
4040
+ }
4041
+ holo.x += (x - holo.x) * 0.08;
4042
+ holo.y += (y - holo.y) * 0.08;
4043
+ root.style.setProperty("--mk-sticker-x", holo.x.toFixed(4));
4044
+ root.style.setProperty("--mk-sticker-y", holo.y.toFixed(4));
4045
+ raf(loop);
4046
+ };
4047
+ if (visible) raf(loop);
4048
+ }
4049
+ }
4050
+ if (options.peel === false) return { get state() {
4051
+ return state;
4052
+ }, destroy: () => scope.dispose() };
4053
+ let w = 0;
4054
+ let h = 0;
4055
+ let corner = { x: 0, y: 0 };
4056
+ let current = { x: 0, y: 0 };
4057
+ let grabbed = { x: 0, y: 0 };
4058
+ let lifted = 0;
4059
+ let anchor = { x: 0, y: 0 };
4060
+ let reach = 0;
4061
+ let outward = { x: 1, y: 0 };
4062
+ let detachDistance = 80;
4063
+ let pose = { angle: 0, scale: 1, prop: 1 };
4064
+ let frozen = null;
4065
+ const readPose = () => {
4066
+ const style = getComputedStyle(root);
4067
+ const prop = parseFloat(style.scale) || 1;
4068
+ const matrix = /^matrix\(([^)]+)\)$/.exec(style.transform || "");
4069
+ if (matrix) {
4070
+ const [a, b] = matrix[1].split(",").map(Number);
4071
+ return { angle: Math.atan2(b, a), scale: Math.hypot(a, b) || 1, prop, transform: style.transform };
4072
+ }
4073
+ const angle = (parseFloat(style.getPropertyValue("--mk-sticker-rot")) || 0) * (Math.PI / 180);
4074
+ return { angle, scale: 1, prop, transform: `rotate(${angle}rad)` };
4075
+ };
4076
+ const freeze = () => {
4077
+ const now = readPose();
4078
+ pose = now;
4079
+ if (!frozen) frozen = { transform: root.style.transform, transition: root.style.transition };
4080
+ root.style.transition = "none";
4081
+ root.style.transform = now.transform;
4082
+ };
4083
+ const unfreeze = () => {
4084
+ if (!frozen) return;
4085
+ root.style.transform = frozen.transform;
4086
+ root.style.transition = frozen.transition;
4087
+ frozen = null;
4088
+ };
4089
+ scope.add(unfreeze);
4090
+ const toLocal = (clientX, clientY) => {
4091
+ const box = peelBox.getBoundingClientRect();
4092
+ const a = -pose.angle;
4093
+ const s = pose.scale * pose.prop;
4094
+ const dx = (clientX - (box.left + box.width / 2)) / s;
4095
+ const dy = (clientY - (box.top + box.height / 2)) / s;
4096
+ return { x: dx * Math.cos(a) - dy * Math.sin(a) + w / 2, y: dx * Math.sin(a) + dy * Math.cos(a) + h / 2 };
4097
+ };
4098
+ const CORNERS = () => [{ x: 0, y: 0 }, { x: w, y: 0 }, { x: w, y: h }, { x: 0, y: h }];
4099
+ const nearBorder = (p) => Math.min(p.x, w - p.x, p.y, h - p.y) < Math.min(w, h) * 0.35;
4100
+ const closestCorner = (p) => CORNERS().reduce((a, b) => Math.hypot(b.x - p.x, b.y - p.y) < Math.hypot(a.x - p.x, a.y - p.y) ? b : a);
4101
+ const cornerAgainst = (d) => CORNERS().reduce((a, b) => (b.x - w / 2) * -d.x + (b.y - h / 2) * -d.y > (a.x - w / 2) * -d.x + (a.y - h / 2) * -d.y + 1e-6 ? b : a);
4102
+ let choosing = false;
4103
+ const useCorner = (c) => {
4104
+ choosing = false;
4105
+ corner = c;
4106
+ anchor = { x: w - c.x, y: h - c.y };
4107
+ reach = Math.hypot(c.x - anchor.x, c.y - anchor.y);
4108
+ const ox = c.x - w / 2;
4109
+ const oy = c.y - h / 2;
4110
+ outward = { x: ox / (Math.hypot(ox, oy) || 1), y: oy / (Math.hypot(ox, oy) || 1) };
4111
+ current = { ...c };
4112
+ };
4113
+ const ensureBack = () => {
4114
+ if (back) return back;
4115
+ back = document.createElement("div");
4116
+ back.className = "mk-sticker__back";
4117
+ back.setAttribute("aria-hidden", "true");
4118
+ const face2 = document.createElement("div");
4119
+ face2.className = "mk-sticker__back-face";
4120
+ const copy = top.cloneNode(true);
4121
+ copy.querySelectorAll("[id]").forEach((el) => el.removeAttribute("id"));
4122
+ face2.append(...copy.childNodes);
4123
+ back.append(face2);
4124
+ peelBox.append(back);
4125
+ return back;
4126
+ };
4127
+ const flat = () => {
4128
+ top.style.clipPath = "";
4129
+ if (back) back.style.visibility = "hidden";
4130
+ root.removeAttribute("data-mk-peeling");
4131
+ lifted = 0;
4132
+ unfreeze();
4133
+ };
4134
+ scope.add(() => {
4135
+ top.style.clipPath = "";
4136
+ back?.remove();
4137
+ back = null;
4138
+ });
4139
+ const fold = (p) => {
4140
+ current = p;
4141
+ const dx = p.x - corner.x;
4142
+ const dy = p.y - corner.y;
4143
+ const length = Math.hypot(dx, dy);
4144
+ if (length < 0.5) {
4145
+ top.style.clipPath = "";
4146
+ if (back) back.style.visibility = "hidden";
4147
+ lifted = 0;
4148
+ return;
4149
+ }
4150
+ const n = { x: dx / length, y: dy / length };
4151
+ const m = { x: (corner.x + p.x) / 2, y: (corner.y + p.y) / 2 };
4152
+ const rect = [{ x: 0, y: 0 }, { x: w, y: 0 }, { x: w, y: h }, { x: 0, y: h }];
4153
+ const stuck = clipHalfPlane(rect, m, n);
4154
+ const flap = clipHalfPlane(rect, m, { x: -n.x, y: -n.y });
4155
+ lifted = area(flap) / (w * h || 1);
4156
+ top.style.clipPath = polygon(stuck);
4157
+ const b = ensureBack();
4158
+ const d = m.x * n.x + m.y * n.y;
4159
+ b.style.transform = `matrix(${1 - 2 * n.x * n.x}, ${-2 * n.x * n.y}, ${-2 * n.x * n.y}, ${1 - 2 * n.y * n.y}, ${2 * d * n.x}, ${2 * d * n.y})`;
4160
+ b.firstElementChild.style.clipPath = polygon(flap);
4161
+ b.style.setProperty("--mk-sticker-fold", `${Math.atan2(-n.x, n.y) * 180 / Math.PI}deg`);
4162
+ b.style.visibility = flap.length >= 3 ? "visible" : "hidden";
4163
+ };
4164
+ const NUDGE = 8;
4165
+ const pull = (at) => {
4166
+ const dx = at.x - grabbed.x;
4167
+ const dy = at.y - grabbed.y;
4168
+ const depth = Math.hypot(dx, dy);
4169
+ const perp = { x: -outward.y, y: outward.x };
4170
+ const side = clamp2(dx * perp.x + dy * perp.y, -depth * 0.7, depth * 0.7) * 0.6;
4171
+ let p = {
4172
+ x: corner.x - outward.x * (NUDGE + depth) + perp.x * side,
4173
+ y: corner.y - outward.y * (NUDGE + depth) + perp.y * side
4174
+ };
4175
+ const ax = p.x - anchor.x;
4176
+ const ay = p.y - anchor.y;
4177
+ const length = Math.hypot(ax, ay);
4178
+ if (length > reach) p = { x: anchor.x + ax * reach / length, y: anchor.y + ay * reach / length };
4179
+ return p;
4180
+ };
4181
+ let generation = 0;
4182
+ const settle = (from, duration, done) => {
4183
+ const start = performance.now();
4184
+ const mine = generation;
4185
+ const step = () => {
4186
+ if (scope.disposed || mine !== generation) return;
4187
+ const t = Math.min(1, (performance.now() - start) / duration);
4188
+ const e = easeOutCubic(t);
4189
+ fold({ x: from.x + (corner.x - from.x) * e, y: from.y + (corner.y - from.y) * e });
4190
+ if (t < 1) raf(step);
4191
+ else {
4192
+ flat();
4193
+ done?.();
4194
+ }
4195
+ };
4196
+ raf(step);
4197
+ };
4198
+ const velocity = { x: 0, y: 0 };
4199
+ let last = { x: 0, y: 0, t: 0 };
4200
+ const track = (e) => {
4201
+ const now = performance.now();
4202
+ const frames60 = Math.max(1, (now - last.t) / 16);
4203
+ velocity.x = velocity.x * 0.5 + (e.clientX - last.x) / frames60 * 0.5;
4204
+ velocity.y = velocity.y * 0.5 + (e.clientY - last.y) / frames60 * 0.5;
4205
+ last = { x: e.clientX, y: e.clientY, t: now };
4206
+ };
4207
+ let home = null;
4208
+ const carry = { x: 0, y: 0, tx: 0, ty: 0, offX: 0, offY: 0, lift: 0, tilt: 0, base: 0, from: 1, to: 1.08 };
4209
+ const carryScale = () => carry.from + (carry.to - carry.from) * carry.lift;
4210
+ const paint = () => {
4211
+ root.style.left = `${carry.x}px`;
4212
+ root.style.top = `${carry.y}px`;
4213
+ root.style.transform = `rotate(${carry.base + carry.tilt}deg) scale(${carryScale()})`;
4214
+ root.style.filter = `drop-shadow(0 ${8 + 14 * carry.lift}px ${14 + 12 * carry.lift}px rgb(0 0 0 / ${0.16 + 0.14 * carry.lift}))`;
4215
+ };
4216
+ const follow = () => {
4217
+ if (state !== "carried" || scope.disposed) return;
4218
+ carry.x += (carry.tx - carry.x) * 0.22;
4219
+ carry.y += (carry.ty - carry.y) * 0.22;
4220
+ carry.lift += (1 - carry.lift) * 0.15;
4221
+ carry.tilt += (clamp2(velocity.x * 1.2, -16, 16) - carry.tilt) * 0.15;
4222
+ paint();
4223
+ raf(follow);
4224
+ };
4225
+ const detach = (e) => {
4226
+ state = "carried";
4227
+ root.removeAttribute("data-mk-peeling");
4228
+ root.setAttribute("data-mk-carried", "");
4229
+ settle({ ...current }, 160);
4230
+ const box = root.getBoundingClientRect();
4231
+ unfreeze();
4232
+ carry.base = pose.angle * (180 / Math.PI);
4233
+ carry.from = pose.scale;
4234
+ carry.to = Math.max(1, pose.scale) + 0.08;
4235
+ home = { parent: root.parentNode, next: root.nextSibling, style: root.getAttribute("style") };
4236
+ const width = root.offsetWidth;
4237
+ const height = root.offsetHeight;
4238
+ document.body.append(root);
4239
+ Object.assign(root.style, { position: "fixed", right: "auto", bottom: "auto", margin: "0", translate: "none", scale: String(pose.prop), zIndex: "999", animation: "none", transition: "none" });
4240
+ carry.x = box.left + box.width / 2 - width / 2;
4241
+ carry.y = box.top + box.height / 2 - height / 2;
4242
+ carry.offX = e.clientX - carry.x;
4243
+ carry.offY = e.clientY - carry.y;
4244
+ carry.tx = carry.x;
4245
+ carry.ty = carry.y;
4246
+ carry.lift = 0;
4247
+ carry.tilt = 0;
4248
+ paint();
4249
+ options.onPeel?.();
4250
+ raf(follow);
4251
+ };
4252
+ const goHome = () => {
4253
+ if (!home) return;
4254
+ const { parent, next, style } = home;
4255
+ home = null;
4256
+ parent.insertBefore(root, next && next.parentNode === parent ? next : null);
4257
+ if (style === null) root.removeAttribute("style");
4258
+ else root.setAttribute("style", style);
4259
+ root.style.animationDelay = "0s";
4260
+ root.removeAttribute("data-mk-carried");
4261
+ root.removeAttribute("data-mk-away");
4262
+ state = "idle";
4263
+ options.onReturn?.();
4264
+ };
4265
+ scope.add(goHome);
4266
+ const away = () => {
4267
+ state = "away";
4268
+ root.setAttribute("data-mk-away", "");
4269
+ scope.timeout(goHome, returnDelay);
4270
+ };
4271
+ const fall = () => {
4272
+ state = "falling";
4273
+ root.style.pointerEvents = "none";
4274
+ if (still) {
4275
+ away();
4276
+ return;
4277
+ }
4278
+ let x = 0;
4279
+ let y = 0;
4280
+ let angle = carry.base + carry.tilt;
4281
+ let vx = clamp2(velocity.x * 40, -900, 900);
4282
+ let vy = clamp2(velocity.y * 40, -500, 600) - 120;
4283
+ const spin = vx * 0.35 + (Math.random() - 0.5) * 120;
4284
+ const startY = carry.y;
4285
+ let then = performance.now();
4286
+ const step = () => {
4287
+ if (scope.disposed || state !== "falling") return;
4288
+ const time = performance.now();
4289
+ const dt = Math.min(0.05, Math.max(0, time - then) / 1e3);
4290
+ then = time;
4291
+ vy += 1600 * dt;
4292
+ x += vx * dt;
4293
+ y += vy * dt;
4294
+ vx *= 1 - 0.6 * dt;
4295
+ angle += spin * dt;
4296
+ root.style.transform = `translate(${x}px, ${y}px) rotate(${angle}deg) scale(${carryScale()})`;
4297
+ if (startY + y < innerHeight + 40) raf(step);
4298
+ else away();
4299
+ };
4300
+ raf(step);
4301
+ };
4302
+ let pointerId = null;
4303
+ scope.on(root, "pointerdown", (event) => {
4304
+ const e = event;
4305
+ if (state !== "idle" || e.button !== 0) return;
4306
+ e.preventDefault();
4307
+ generation++;
4308
+ freeze();
4309
+ state = "peeling";
4310
+ root.setAttribute("data-mk-peeling", "");
4311
+ pointerId = e.pointerId;
4312
+ root.setPointerCapture?.(e.pointerId);
4313
+ w = top.offsetWidth;
4314
+ h = top.offsetHeight;
4315
+ peelBox.style.width = `${w}px`;
4316
+ peelBox.style.height = `${h}px`;
4317
+ const at = toLocal(e.clientX, e.clientY);
4318
+ grabbed = at;
4319
+ detachDistance = Number.isFinite(options.detachDistance) ? Math.max(1, options.detachDistance) : Math.max(80, Math.max(w, h) / 2);
4320
+ last = { x: e.clientX, y: e.clientY, t: performance.now() };
4321
+ velocity.x = velocity.y = 0;
4322
+ if (nearBorder(at)) {
4323
+ useCorner(closestCorner(at));
4324
+ fold(pull(at));
4325
+ } else {
4326
+ choosing = true;
4327
+ corner = current = { ...at };
4328
+ }
4329
+ });
4330
+ scope.on(root, "pointermove", (event) => {
4331
+ const e = event;
4332
+ if (state !== "peeling" || e.pointerId !== pointerId) return;
4333
+ track(e);
4334
+ const at = toLocal(e.clientX, e.clientY);
4335
+ if (choosing) {
4336
+ const dx = at.x - grabbed.x;
4337
+ const dy = at.y - grabbed.y;
4338
+ const moved = Math.hypot(dx, dy);
4339
+ if (moved < 4) return;
4340
+ useCorner(cornerAgainst({ x: dx / moved, y: dy / moved }));
4341
+ }
4342
+ fold(pull(at));
4343
+ if (lifted > detachAt || Math.hypot(at.x - grabbed.x, at.y - grabbed.y) > detachDistance) detach(e);
4344
+ });
4345
+ scope.on(document, "pointermove", (event) => {
4346
+ const e = event;
4347
+ if (state !== "carried" || e.pointerId !== pointerId) return;
4348
+ track(e);
4349
+ carry.tx = e.clientX - carry.offX;
4350
+ carry.ty = e.clientY - carry.offY;
4351
+ });
4352
+ const release = (event) => {
4353
+ const e = event;
4354
+ if (e.pointerId !== pointerId) return;
4355
+ if (state === "peeling") {
4356
+ state = "idle";
4357
+ settle({ ...current }, 380);
4358
+ } else if (state === "carried") fall();
4359
+ };
4360
+ scope.on(document, "pointerup", release);
4361
+ scope.on(document, "pointercancel", release);
4362
+ scope.add(() => {
4363
+ peelBox.style.width = "";
4364
+ peelBox.style.height = "";
4365
+ });
4366
+ return {
4367
+ get state() {
4368
+ return state;
4369
+ },
4370
+ destroy: () => scope.dispose()
4371
+ };
4372
+ }
4373
+
4374
+ // src/recipes/scroll-dock/create-scroll-dock.ts
4375
+ function createScrollDock(root, options = {}) {
4376
+ if (!root) return inert({ visible: false, update() {
4377
+ } });
4378
+ const scope = createScope();
4379
+ const threshold = Number.isFinite(options.threshold) ? Math.max(0, options.threshold) : 0.6;
4380
+ const hideOffset = Number.isFinite(options.hideOffset) ? options.hideOffset : 40;
4381
+ const scroller = typeof options.scroller === "string" ? root.closest(options.scroller) ?? document.querySelector(options.scroller) : options.scroller ?? null;
4382
+ const hideTarget = () => typeof options.hideWhen === "string" ? document.querySelector(options.hideWhen) : options.hideWhen ?? null;
4383
+ let visible = false;
4384
+ let queued = false;
4385
+ scope.attr(root, "data-mk-ready", "");
4386
+ const update = () => {
4387
+ queued = false;
4388
+ if (scope.disposed) return;
4389
+ const offset = scroller ? scroller.scrollTop : window.scrollY;
4390
+ const viewport = scroller ? scroller.clientHeight : window.innerHeight;
4391
+ const bottom = scroller ? scroller.getBoundingClientRect().bottom : window.innerHeight;
4392
+ const end = hideTarget();
4393
+ const reachedEnd = end ? end.getBoundingClientRect().top < bottom - hideOffset : false;
4394
+ const next = offset > viewport * threshold && !reachedEnd;
4395
+ if (next === visible) return;
4396
+ visible = next;
4397
+ scope.attr(root, "data-mk-visible", next ? "" : null);
4398
+ options.onChange?.(next);
4399
+ root.dispatchEvent(new CustomEvent("mk:change", { bubbles: true, detail: { visible: next } }));
4400
+ };
4401
+ const queue = () => {
4402
+ if (queued) return;
4403
+ queued = true;
4404
+ requestAnimationFrame(update);
4405
+ };
4406
+ scope.on(scroller ?? window, "scroll", queue, { passive: true });
4407
+ scope.on(window, "resize", queue, { passive: true });
4408
+ update();
4409
+ return {
4410
+ get visible() {
4411
+ return visible;
4412
+ },
4413
+ update,
4414
+ destroy: () => scope.dispose()
4415
+ };
4416
+ }
4417
+
4418
+ // src/recipes/local-clock/create-local-clock.ts
4419
+ function createLocalClock(root, options = {}) {
4420
+ if (!root) return inert({ text: "" });
4421
+ const scope = createScope();
4422
+ const original = [...root.childNodes];
4423
+ scope.add(() => root.replaceChildren(...original));
4424
+ scope.attr(root, "data-mk-ready", "");
4425
+ let formatter;
4426
+ try {
4427
+ formatter = new Intl.DateTimeFormat(options.locale ?? "es", options.format ?? {
4428
+ hour: "2-digit",
4429
+ minute: "2-digit",
4430
+ ...options.seconds ? { second: "2-digit" } : {},
4431
+ hour12: options.hour12 ?? false,
4432
+ timeZone: options.timeZone
4433
+ });
4434
+ } catch {
4435
+ formatter = new Intl.DateTimeFormat(void 0, { hour: "2-digit", minute: "2-digit", hour12: options.hour12 ?? false });
4436
+ }
4437
+ let text = "";
4438
+ const isTime = root instanceof HTMLTimeElement;
4439
+ const render = () => {
4440
+ const now = /* @__PURE__ */ new Date();
4441
+ text = formatter.format(now);
4442
+ if (root.textContent !== text) root.textContent = text;
4443
+ if (isTime) scope.attr(root, "datetime", now.toISOString());
4444
+ };
4445
+ const step = options.seconds || options.format?.second ? 1e3 : 6e4;
4446
+ const schedule = () => {
4447
+ const wait = step - Date.now() % step + 20;
4448
+ scope.timeout(() => {
4449
+ if (!scope.disposed) {
4450
+ render();
4451
+ schedule();
4452
+ }
4453
+ }, wait);
4454
+ };
4455
+ render();
4456
+ schedule();
4457
+ return { get text() {
4458
+ return text;
4459
+ }, destroy: () => scope.dispose() };
4460
+ }
4461
+
3274
4462
  export {
3275
4463
  trackScroll,
3276
4464
  toggleDetails,
@@ -3315,6 +4503,17 @@ export {
3315
4503
  createRepelBento,
3316
4504
  createRotator,
3317
4505
  createDragRuler,
3318
- createDockPill
4506
+ createDockPill,
4507
+ createJoinedCta,
4508
+ createImageZoom,
4509
+ createFontCycle,
4510
+ createThumbnailNav,
4511
+ createActionButton,
4512
+ createFilmHero,
4513
+ createEditorialReveal,
4514
+ createWeightHover,
4515
+ createSticker,
4516
+ createScrollDock,
4517
+ createLocalClock
3319
4518
  };
3320
- //# sourceMappingURL=chunk-XDWBVFHQ.js.map
4519
+ //# sourceMappingURL=chunk-XAKC2QY7.js.map