kuinetic 0.1.2 → 0.1.4

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 (31) hide show
  1. package/README.md +3 -3
  2. package/dist/esm/{chunk-WPU33HDL.mjs → chunk-7WMNPIOZ.mjs} +1724 -240
  3. package/dist/esm/{chunk-LWS4OSLX.mjs → chunk-JT4PZL3A.mjs} +50 -7
  4. package/dist/esm/{chunk-5EVG7CIY.mjs → chunk-TJDDIQRG.mjs} +42 -16
  5. package/dist/esm/core/index.mjs +2 -2
  6. package/dist/esm/effects/index.mjs +2 -2
  7. package/dist/esm/index.mjs +3 -3
  8. package/dist/kuinetic.all.js +1813 -262
  9. package/dist/kuinetic.css +1491 -94
  10. package/dist/kuinetic.js +1812 -261
  11. package/dist/types/core/instances.d.ts +34 -3
  12. package/dist/types/core/path-morph.d.ts +19 -0
  13. package/dist/types/core/style-plan.d.ts +6 -1
  14. package/dist/types/core/types.d.ts +99 -1
  15. package/dist/types/effects/catalog/background-media.d.ts +152 -0
  16. package/dist/types/effects/catalog/index.d.ts +0 -6
  17. package/dist/types/effects/catalog/media-shared.d.ts +124 -0
  18. package/dist/types/effects/catalog/media.d.ts +4 -0
  19. package/dist/types/effects/catalog/numbers-shared.d.ts +5 -1
  20. package/dist/types/effects/catalog/shared.d.ts +2 -3
  21. package/dist/types/effects/catalog/subtree-capture.d.ts +23 -0
  22. package/dist/types/effects/catalog/text-shared.d.ts +11 -2
  23. package/dist/types/effects/gestures/index.d.ts +0 -1
  24. package/dist/types/effects/layout/index.d.ts +0 -2
  25. package/dist/types/effects/scroll-mechanics/index.d.ts +0 -4
  26. package/dist/types/effects/scroll-mechanics/scroll-spy.d.ts +26 -0
  27. package/dist/types/effects/scroll-mechanics/tracker.d.ts +61 -3
  28. package/dist/types/effects/shared.d.ts +40 -0
  29. package/dist/types/effects/step-marking.d.ts +99 -0
  30. package/dist/types/effects/three-d/index.d.ts +0 -7
  31. package/package.json +9 -2
@@ -61,7 +61,7 @@ var kuinetic = (() => {
61
61
 
62
62
  // src/core/element-config.ts
63
63
  var ACTIVATIONS = /* @__PURE__ */ new Set(["load", "enter", "hover", "focus", "click", "manual"]);
64
- var TIMELINES = /* @__PURE__ */ new Set(["time", "view", "scroll", "pointer"]);
64
+ var TIMELINES = /* @__PURE__ */ new Set(["time", "view", "scroll", "pointer", "pin"]);
65
65
  function readAttributes(el) {
66
66
  return {
67
67
  source: el.getAttribute(ATTR.source) ?? "",
@@ -243,7 +243,7 @@ var kuinetic = (() => {
243
243
  var HEX_COLOR = /^#[0-9a-f]{3,8}$/i;
244
244
  var COLOR_FUNCTIONS = /^(?:rgba?|hsla?|okl(?:ch|ab)|l(?:ch|ab)|color)\([^()]*\)$/i;
245
245
  var COLOR_KEYWORD = /^[a-z]+$/i;
246
- var EASING_KEYWORD = /^(?:linear|ease|step-start|step-end|spring|[a-z]+-(?:in|out|in-out))$/i;
246
+ var EASING_KEYWORD = /^(?:linear|ease|step-start|step-end|spring|bounce|[a-z]+-(?:in|out|in-out))$/i;
247
247
  var EASING_FUNCTION = /^(?:cubic-bezier|steps|linear)\([^()]*\)$/i;
248
248
  var CALC_TYPES = /* @__PURE__ */ new Set(["length", "percentage", "number"]);
249
249
  var CALC_CHARACTER = /^[\d.\s+\-*/%a-z,]$/i;
@@ -530,7 +530,7 @@ var kuinetic = (() => {
530
530
  plan.vars,
531
531
  resolveParams(entry.spec.params, primitive.parameters, (m) => warnings.push(m))
532
532
  );
533
- if (primitive.renderer === "css-keyframes") pushTrack(tracks, entry);
533
+ if (primitive.renderer === "css-keyframes") pushTrack(tracks, entry, timeline);
534
534
  else plan.jsEffects.push(entry);
535
535
  }
536
536
  Object.assign(plan.declarations, declarationsFor(tracks));
@@ -546,12 +546,13 @@ var kuinetic = (() => {
546
546
  function iterationCountProperty(presetName) {
547
547
  return `--kui-fx-${presetName}-iterations`;
548
548
  }
549
- function pushTrack(tracks, entry) {
549
+ function pushTrack(tracks, entry, timeline) {
550
550
  const { spec, resolved } = entry;
551
551
  const id = resolved.primitive.id;
552
552
  tracks.names.push(resolved.preset.keyframes ?? `kui-${resolved.preset.name}`);
553
- tracks.durations.push(spec.duration ?? `var(${timingProperty(id, "duration")}, 600ms)`);
554
- tracks.delays.push(staggerDelay(spec.delay, id));
553
+ const duration = spec.duration ?? `var(${timingProperty(id, "duration")}, 600ms)`;
554
+ tracks.durations.push(duration);
555
+ tracks.delays.push(staggerDelay(spec.delay, id, timeline, duration));
555
556
  tracks.easings.push(easingValue(spec.easing, id));
556
557
  tracks.iterationCounts.push(`var(${iterationCountProperty(resolved.preset.name)}, 1)`);
557
558
  }
@@ -575,9 +576,12 @@ var kuinetic = (() => {
575
576
  `"${name}" does not support timeline "${timeline}" (supports: ${supported.join(", ")})`
576
577
  );
577
578
  }
578
- function staggerDelay(delay, primitiveId) {
579
+ function staggerDelay(delay, primitiveId, timeline, duration) {
579
580
  const base = delay ?? `var(${timingProperty(primitiveId, "delay")}, 0ms)`;
580
- return `calc(${base} + var(--kui-i, 0) * var(--kui-stagger, 0ms))`;
581
+ const staggered = `${base} + var(--kui-i, 0) * var(--kui-stagger, 0ms)`;
582
+ if (timeline !== "pin") return `calc(${staggered})`;
583
+ const span = `${duration} + (var(--kui-stagger-count, 1) - 1) * var(--kui-stagger, 0ms)`;
584
+ return `calc(${staggered} - var(--kui-progress, 0) * (${span}))`;
581
585
  }
582
586
  function easingValue(easing, primitiveId) {
583
587
  if (!easing) return `var(${timingProperty(primitiveId, "ease")}, ease-out)`;
@@ -860,7 +864,8 @@ var kuinetic = (() => {
860
864
  "back-in-out",
861
865
  "quart-out",
862
866
  "circ-out",
863
- "spring"
867
+ "spring",
868
+ "bounce"
864
869
  ]);
865
870
  function splitTopLevel(input, delimiter, warnings = []) {
866
871
  const parts = [];
@@ -1095,7 +1100,22 @@ var kuinetic = (() => {
1095
1100
  viewportLeft: 0
1096
1101
  }),
1097
1102
  onScroll: (handler) => listen(win, "scroll", handler),
1098
- onResize: (handler) => listen(win, "resize", handler)
1103
+ /*
1104
+ * The document's own height counts as a resize, not just the window's.
1105
+ *
1106
+ * `elementScrollRoot` has observed its element's size since it was written, for the reason
1107
+ * stated three doc comments down: "images loading, fonts swapping, a container changing size,
1108
+ * or content being inserted". Every one of those applies at least as strongly to the page
1109
+ * itself, and the page root was the one that only listened for `window.resize` — so on any
1110
+ * document with lazy-loaded media below the fold, every cached content offset silently drifted
1111
+ * by however much the page grew after it was measured, with no epoch bump to correct it.
1112
+ *
1113
+ * Measured on `demo/scroll.html` (32,000px of lazy screenshots): the horizontal track's stage
1114
+ * moved ~840px between page load and reaching it, which is a third of that effect's whole
1115
+ * scroll range. Nothing was wrong with the maths; the number it was doing the maths on was
1116
+ * taken before forty images existed.
1117
+ */
1118
+ onResize: (handler) => observeSize(win.document.documentElement, win, handler)
1099
1119
  };
1100
1120
  }
1101
1121
  function elementScrollRoot(el, win) {
@@ -1322,7 +1342,7 @@ var kuinetic = (() => {
1322
1342
  flushComputedAnimationName(el);
1323
1343
  ledger.set("animation-name", name);
1324
1344
  }
1325
- function createCssInstance(el, ledger, animationNames) {
1345
+ function createCssInstance(el, ledger, animationNames, scrubbed = false) {
1326
1346
  const ownedNames = new Set(animationNames);
1327
1347
  let settle2;
1328
1348
  let finished = new Promise((resolve) => {
@@ -1341,6 +1361,10 @@ var kuinetic = (() => {
1341
1361
  finished = new Promise((resolve) => {
1342
1362
  settle2 = resolve;
1343
1363
  });
1364
+ if (scrubbed) {
1365
+ settle2?.();
1366
+ return;
1367
+ }
1344
1368
  let animations = ownedAnimationsOf(el, ownedNames);
1345
1369
  if (!activatedBefore) {
1346
1370
  restartCssAnimation(el, ledger);
@@ -1373,6 +1397,9 @@ var kuinetic = (() => {
1373
1397
  }
1374
1398
  };
1375
1399
  }
1400
+ function continuousSetup(cleanup) {
1401
+ return { cleanup, continuous: true };
1402
+ }
1376
1403
  function cleanupOf(result) {
1377
1404
  if (result === void 0) return void 0;
1378
1405
  return typeof result === "function" ? result : result.cleanup;
@@ -1389,7 +1416,7 @@ var kuinetic = (() => {
1389
1416
  return createJsInstance({
1390
1417
  activate() {
1391
1418
  running = setup();
1392
- if (typeof running === "function") return;
1419
+ if (typeof running === "function" || "continuous" in running) return;
1393
1420
  const work = running.finished;
1394
1421
  finished = new Promise((resolve) => {
1395
1422
  settle2 = resolve;
@@ -1398,11 +1425,14 @@ var kuinetic = (() => {
1398
1425
  },
1399
1426
  cancel: teardown,
1400
1427
  finish() {
1401
- if (typeof running === "object") running.finish?.();
1428
+ if (typeof running === "object" && "finish" in running) running.finish?.();
1402
1429
  settle2?.();
1403
1430
  },
1404
1431
  destroy: teardown,
1405
- finished: () => finished
1432
+ finished: () => finished,
1433
+ // Only an explicit marker counts. A bare `Cleanup` deliberately does not — see
1434
+ // `ContinuousSetup` above for why the two cannot be told apart by shape.
1435
+ continuous: () => typeof running === "object" && "continuous" in running
1406
1436
  });
1407
1437
  }
1408
1438
  function deferPrepare(setup) {
@@ -1426,6 +1456,9 @@ var kuinetic = (() => {
1426
1456
  get finished() {
1427
1457
  return hooks.finished?.() ?? Promise.resolve();
1428
1458
  },
1459
+ get continuous() {
1460
+ return hooks.continuous?.() ?? false;
1461
+ },
1429
1462
  destroy() {
1430
1463
  active = false;
1431
1464
  hooks.destroy();
@@ -1437,6 +1470,7 @@ var kuinetic = (() => {
1437
1470
  function createStyleLedger(el) {
1438
1471
  const style = el.style;
1439
1472
  const previous = /* @__PURE__ */ new Map();
1473
+ const authoredStyleAttribute = el.hasAttribute("style");
1440
1474
  function remember(property) {
1441
1475
  if (previous.has(property)) return;
1442
1476
  const existing = style.getPropertyValue(property);
@@ -1454,6 +1488,8 @@ var kuinetic = (() => {
1454
1488
  else style.setProperty(property, value);
1455
1489
  }
1456
1490
  previous.clear();
1491
+ if (!authoredStyleAttribute && style.length === 0 && !el.getAttribute("style"))
1492
+ el.removeAttribute("style");
1457
1493
  },
1458
1494
  owned: () => [...previous.keys()]
1459
1495
  };
@@ -1487,6 +1523,8 @@ var kuinetic = (() => {
1487
1523
  index++;
1488
1524
  }
1489
1525
  }
1526
+ ;
1527
+ group.style.setProperty("--kui-stagger-count", String(Math.max(index, 1)));
1490
1528
  }
1491
1529
  function applyStagger(root) {
1492
1530
  const selector = `[${ATTR.stagger}]`;
@@ -1498,12 +1536,14 @@ var kuinetic = (() => {
1498
1536
  function planStyles(input) {
1499
1537
  const { plan, config, capabilities, respectReducedMotion } = input;
1500
1538
  const reduce = respectReducedMotion && capabilities.reducedMotion;
1501
- const useNativeTimeline = supportsTimeline(config.timeline, capabilities) && plan.supportedTimelines.includes(config.timeline);
1539
+ const scrubbed = config.timeline === "pin" && plan.supportedTimelines.includes("pin");
1540
+ const useNativeTimeline = !scrubbed && supportsTimeline(config.timeline, capabilities) && plan.supportedTimelines.includes(config.timeline);
1502
1541
  const properties = { ...plan.vars, ...plan.declarations };
1503
1542
  Object.assign(properties, timelineProperties(config, capabilities, useNativeTimeline));
1504
1543
  const hasCssAnimation = Object.keys(plan.declarations).length > 0;
1505
1544
  const gate = resolveGate({
1506
1545
  useNativeTimeline,
1546
+ scrubbed: scrubbed && hasCssAnimation,
1507
1547
  reduce,
1508
1548
  activation: config.activation,
1509
1549
  // JS effects are gated too. They emit no `animation` declaration, so only the play-state
@@ -1518,7 +1558,7 @@ var kuinetic = (() => {
1518
1558
  // already applied under reduced motion.
1519
1559
  unsupportedTransform: needsIndividualTransforms(plan.channels, capabilities)
1520
1560
  });
1521
- if (gate === "deferred") properties["animation-play-state"] = "paused";
1561
+ if (gate === "deferred" || gate === "scrubbed") properties["animation-play-state"] = "paused";
1522
1562
  return {
1523
1563
  properties,
1524
1564
  attributes: {
@@ -1542,6 +1582,7 @@ var kuinetic = (() => {
1542
1582
  }
1543
1583
  function resolveGate(input) {
1544
1584
  if (input.useNativeTimeline) return "native-timeline";
1585
+ if (input.scrubbed) return "scrubbed";
1545
1586
  if (!input.hasWork) return "immediate";
1546
1587
  if (input.reduce || input.activation === "load" || input.unsupportedTransform) return "immediate";
1547
1588
  return "deferred";
@@ -1722,7 +1763,9 @@ var kuinetic = (() => {
1722
1763
  this.liveElements.add(el);
1723
1764
  if (Object.keys(stylePlan.properties).some((property) => property.startsWith("animation-"))) {
1724
1765
  const animationNames = (plan.declarations["animation-name"] ?? "").split(",").map((name) => name.trim()).filter(Boolean);
1725
- state.instances.push(createCssInstance(el, ledger, animationNames));
1766
+ state.instances.push(
1767
+ createCssInstance(el, ledger, animationNames, stylePlan.gate === "scrubbed")
1768
+ );
1726
1769
  }
1727
1770
  state.instances.push(
1728
1771
  ...this.jsEffectPreparer.prepare({ el, plan, signal: controller.signal, ledger })
@@ -1752,10 +1795,20 @@ var kuinetic = (() => {
1752
1795
  this.activate(el);
1753
1796
  return;
1754
1797
  }
1755
- state.controller.signal.addEventListener(
1756
- "abort",
1757
- this.binder.bind(el, stylePlan.activation, config.threshold, () => this.activate(el))
1798
+ const releaseBinding = this.binder.bind(
1799
+ el,
1800
+ stylePlan.activation,
1801
+ config.threshold,
1802
+ () => this.activate(el)
1758
1803
  );
1804
+ let released = false;
1805
+ const releaseOnce = () => {
1806
+ if (released) return;
1807
+ released = true;
1808
+ releaseBinding();
1809
+ };
1810
+ state.controller.signal.addEventListener("abort", releaseOnce);
1811
+ if (stylePlan.activation === "enter") state.releaseActivation = releaseOnce;
1759
1812
  }
1760
1813
  /**
1761
1814
  * Start a deferred animation.
@@ -1775,6 +1828,8 @@ var kuinetic = (() => {
1775
1828
  activate(el) {
1776
1829
  const state = this.states.get(el);
1777
1830
  if (!state || state.status === "running") return;
1831
+ state.releaseActivation?.();
1832
+ state.releaseActivation = void 0;
1778
1833
  state.status = "running";
1779
1834
  state.attributes.set(ATTR.state, "running");
1780
1835
  const started = state.instances.filter((instance) => this.startInstance(instance, el));
@@ -1783,7 +1838,9 @@ var kuinetic = (() => {
1783
1838
  state.attributes.set(ATTR.state, "failed");
1784
1839
  return;
1785
1840
  }
1786
- void Promise.all(started.map((instance) => instance.finished)).then(() => {
1841
+ const timed = started.filter((instance) => !instance.continuous);
1842
+ if (timed.length === 0 && started.length > 0) return;
1843
+ void Promise.all(timed.map((instance) => instance.finished)).then(() => {
1787
1844
  if (this.states.get(el) !== state || state.status !== "running") return;
1788
1845
  state.status = "finished";
1789
1846
  state.attributes.set(ATTR.state, "finished");
@@ -1972,7 +2029,18 @@ var kuinetic = (() => {
1972
2029
  background: "background",
1973
2030
  color: "color",
1974
2031
  stroke: "stroke",
1975
- text: "text"
2032
+ text: "text",
2033
+ /**
2034
+ * Skew is the one transform CSS never gave an independent property to — there is no `skew:`
2035
+ * beside `translate:`/`rotate:`/`scale:`, so it can only be written through the `transform`
2036
+ * shorthand. That makes it its own channel: anything writing `transform` clobbers the whole
2037
+ * shorthand, so every primitive that does is on this channel, whatever the transform is for.
2038
+ * `scroll-skew` is one member; `flip-face` (`effects/three-d`) and `flip-3d`
2039
+ * (`effects/catalog/core`, the `flip-in-*`/`flip-out-*` family) are the other two — both need
2040
+ * the `perspective()` transform *function* for an element to have depth on itself, which
2041
+ * likewise only exists inside `transform`.
2042
+ */
2043
+ skew: "skew"
1976
2044
  };
1977
2045
  function inertInstance(destroy = () => {
1978
2046
  }) {
@@ -1989,9 +2057,13 @@ var kuinetic = (() => {
1989
2057
  }
1990
2058
 
1991
2059
  // src/effects/shared.ts
2060
+ var TRIGGER_DELAY_PARAM = {
2061
+ delay: { type: "time", default: "0ms", cssProperty: "--kui-delay" }
2062
+ };
2063
+ var TIMELINE_AGNOSTIC = ["time", "view", "scroll", "pin"];
1992
2064
  var COMMON = {
1993
2065
  duration: { type: "time", default: "600ms", cssProperty: "--kui-duration" },
1994
- delay: { type: "time", default: "0ms", cssProperty: "--kui-delay" },
2066
+ ...TRIGGER_DELAY_PARAM,
1995
2067
  ease: { type: "easing", default: "ease-out", cssProperty: "--kui-ease" },
1996
2068
  stagger: { type: "time", default: "0ms", cssProperty: "--kui-stagger" }
1997
2069
  };
@@ -2019,13 +2091,27 @@ var kuinetic = (() => {
2019
2091
  // src/effects/catalog/ambient.ts
2020
2092
  var drift = {
2021
2093
  duration: { type: "time", default: "10s", cssProperty: "--kui-duration" },
2022
- ease: { type: "easing", default: "ease-in-out", cssProperty: "--kui-ease" }
2094
+ ease: { type: "easing", default: "ease-in-out", cssProperty: "--kui-ease" },
2095
+ from: { type: "color", default: "", cssProperty: "--kui-ambient-c1" },
2096
+ to: { type: "color", default: "", cssProperty: "--kui-ambient-c2" }
2097
+ };
2098
+ var tint = {
2099
+ duration: { type: "time", default: "10s", cssProperty: "--kui-duration" },
2100
+ ease: { type: "easing", default: "ease-in-out", cssProperty: "--kui-ease" },
2101
+ from: { type: "color", default: "", cssProperty: "--kui-ambient-c1" }
2023
2102
  };
2024
2103
  var float = {
2025
2104
  duration: { type: "time", default: "4s", cssProperty: "--kui-duration" },
2026
2105
  ease: { type: "easing", default: "ease-in-out", cssProperty: "--kui-ease" },
2027
2106
  distance: { type: "length", default: "14px", cssProperty: "--kui-distance" }
2028
2107
  };
2108
+ var orbit = {
2109
+ duration: { type: "time", default: "3.5s", cssProperty: "--kui-duration" },
2110
+ // `linear` by default, unlike every other ambient primitive: a continuous rotation that eases
2111
+ // visibly stutters once per revolution, because the ease restarts at each iteration boundary.
2112
+ ease: { type: "easing", default: "linear", cssProperty: "--kui-ease" },
2113
+ angle: { type: "angle", default: "360deg", cssProperty: "--kui-to-angle" }
2114
+ };
2029
2115
  var pulse = {
2030
2116
  duration: { type: "time", default: "2.2s", cssProperty: "--kui-duration" },
2031
2117
  ease: { type: "easing", default: "ease-in-out", cssProperty: "--kui-ease" },
@@ -2038,12 +2124,46 @@ var kuinetic = (() => {
2038
2124
  reducedMotion: "disable",
2039
2125
  perfClass: "continuous"
2040
2126
  }),
2127
+ /*
2128
+ * `gradient-rotate-border` and `gradient-border` are not background fills, and a second
2129
+ * primitive is how this catalog says so — the same reason `ambient-tint` exists beside
2130
+ * `ambient-gradient` above. Channels are per-primitive, so a different channel set means a
2131
+ * different primitive; folding these into `ambient-gradient` would make `gradient-mesh` and
2132
+ * `aurora` claim a mask and a box they never touch, and `aurora, pin` would start reporting a
2133
+ * conflict that isn't there.
2134
+ *
2135
+ * What the ring rules actually write (`ambient.css`) beyond the gradient: `mask` +
2136
+ * `mask-composite`, which subtract the element's own content box to leave a ring — the same
2137
+ * physical property `media-mask` claims under the `'mask'` channel — and `position: relative`
2138
+ * plus a `padding` that *is* the ring's thickness, which is a claim on the host's box in the
2139
+ * sense `pin` and `background-media` already use `'layout'` for. Declared only as
2140
+ * `background`, a `gradient-border, pin-section` pair composed silently while both decided the
2141
+ * host's `position`, and `gradient-border, mask-reveal` while both wrote `mask`.
2142
+ */
2143
+ cssPrimitive("ambient-gradient-ring", [CHANNEL.background, "mask", "layout"], {
2144
+ parameters: drift,
2145
+ defaultActivation: "load",
2146
+ reducedMotion: "disable",
2147
+ perfClass: "continuous"
2148
+ }),
2149
+ cssPrimitive("ambient-tint", [CHANNEL.background], {
2150
+ parameters: tint,
2151
+ defaultActivation: "load",
2152
+ reducedMotion: "disable",
2153
+ perfClass: "continuous"
2154
+ }),
2041
2155
  cssPrimitive("ambient-float", [CHANNEL.translate], {
2042
2156
  parameters: float,
2043
2157
  defaultActivation: "load",
2044
2158
  reducedMotion: "disable",
2045
2159
  perfClass: "continuous"
2046
2160
  }),
2161
+ cssPrimitive("ambient-orbit", [CHANNEL.rotate], {
2162
+ parameters: orbit,
2163
+ defaultActivation: "load",
2164
+ reducedMotion: "disable",
2165
+ perfClass: "continuous"
2166
+ }),
2047
2167
  cssPrimitive("ambient-pulse", [CHANNEL.scale, CHANNEL.opacity], {
2048
2168
  parameters: pulse,
2049
2169
  defaultActivation: "load",
@@ -2056,49 +2176,58 @@ var kuinetic = (() => {
2056
2176
  { name: "aurora", primitive: "ambient-gradient", keyframes: "kui-aurora" },
2057
2177
  {
2058
2178
  name: "gradient-rotate-border",
2059
- primitive: "ambient-gradient",
2179
+ primitive: "ambient-gradient-ring",
2060
2180
  keyframes: "kui-gradient-rotate-border",
2061
2181
  params: { duration: "6s", ease: "linear" }
2062
2182
  },
2183
+ {
2184
+ // Not `gradient`: this rule masks its own content box away (`mask-composite: exclude`) to leave
2185
+ // a ring, so putting the name on real content deletes the content. `-border` says that out loud,
2186
+ // matching `gradient-rotate-border` and `beam-border`.
2187
+ name: "gradient-border",
2188
+ primitive: "ambient-gradient-ring",
2189
+ keyframes: "kui-gradient-border",
2190
+ params: { duration: "6s", ease: "linear" }
2191
+ },
2063
2192
  {
2064
2193
  name: "noise-overlay",
2065
- primitive: "ambient-gradient",
2194
+ primitive: "ambient-tint",
2066
2195
  keyframes: "kui-noise-overlay",
2067
2196
  params: { duration: "650ms", ease: "steps(6)" }
2068
2197
  },
2069
2198
  {
2070
2199
  name: "scanline",
2071
- primitive: "ambient-gradient",
2200
+ primitive: "ambient-tint",
2072
2201
  keyframes: "kui-scanline",
2073
2202
  params: { duration: "3.5s", ease: "linear" }
2074
2203
  },
2075
2204
  {
2076
2205
  name: "dot-grid-drift",
2077
- primitive: "ambient-gradient",
2206
+ primitive: "ambient-tint",
2078
2207
  keyframes: "kui-dot-grid-drift",
2079
2208
  params: { duration: "16s", ease: "linear" }
2080
2209
  },
2081
2210
  {
2082
2211
  name: "line-grid-drift",
2083
- primitive: "ambient-gradient",
2212
+ primitive: "ambient-tint",
2084
2213
  keyframes: "kui-line-grid-drift",
2085
2214
  params: { duration: "16s", ease: "linear" }
2086
2215
  },
2087
2216
  {
2088
2217
  name: "starfield",
2089
- primitive: "ambient-gradient",
2218
+ primitive: "ambient-tint",
2090
2219
  keyframes: "kui-starfield",
2091
2220
  params: { duration: "40s", ease: "linear" }
2092
2221
  },
2093
2222
  {
2094
2223
  name: "spotlight-follow",
2095
- primitive: "ambient-gradient",
2224
+ primitive: "ambient-tint",
2096
2225
  keyframes: "kui-spotlight-follow",
2097
2226
  params: { duration: "9s" }
2098
2227
  },
2099
2228
  {
2100
2229
  name: "wave-blob",
2101
- primitive: "ambient-gradient",
2230
+ primitive: "ambient-tint",
2102
2231
  keyframes: "kui-wave-blob",
2103
2232
  params: { duration: "12s" }
2104
2233
  },
@@ -2115,6 +2244,7 @@ var kuinetic = (() => {
2115
2244
  keyframes: "kui-floating-shapes",
2116
2245
  params: { duration: "6s", distance: "10px" }
2117
2246
  },
2247
+ { name: "orbit", primitive: "ambient-orbit", keyframes: "kui-orbit" },
2118
2248
  { name: "glow-pulse", primitive: "ambient-pulse", keyframes: "kui-glow-pulse" }
2119
2249
  ];
2120
2250
  function registerAmbient(registry) {
@@ -2431,6 +2561,13 @@ var kuinetic = (() => {
2431
2561
  var liftParams = {
2432
2562
  distance: { type: "length", default: "6px", cssProperty: "--kui-lift-distance" }
2433
2563
  };
2564
+ var popParams = {
2565
+ scale: { type: "number", default: "1.06", cssProperty: "--kui-pop-scale", finite: true, minimum: 0 }
2566
+ };
2567
+ var beamParams = {
2568
+ color: { type: "color", default: "", cssProperty: "--kui-beam-border-c1" },
2569
+ outset: { type: "length", default: "", cssProperty: "--kui-beam-border-outset" }
2570
+ };
2434
2571
  function hoverPrimitive(id, channels, extraParams = {}) {
2435
2572
  return {
2436
2573
  id,
@@ -2452,12 +2589,13 @@ var kuinetic = (() => {
2452
2589
  }
2453
2590
  var HOVER_PRIMITIVES = [
2454
2591
  hoverPrimitive("lift", ["translate"], liftParams),
2592
+ hoverPrimitive("pop", ["scale"], popParams),
2455
2593
  hoverPrimitive("lift-shadow", ["translate", "shadow"], liftParams),
2456
2594
  hoverPrimitive("shine-sweep", ["sweep"]),
2457
2595
  hoverPrimitive("split-flap", ["rotate"]),
2458
2596
  hoverPrimitive("border-draw", ["border"]),
2459
2597
  hoverPrimitive("border-glow", ["shadow"]),
2460
- hoverPrimitive("beam-border", ["border"]),
2598
+ hoverPrimitive("beam-border", ["border"], beamParams),
2461
2599
  hoverPrimitive("underline-slide", ["scale"]),
2462
2600
  hoverPrimitive("underline-center", ["scale"]),
2463
2601
  hoverPrimitive("icon-wiggle", ["rotate"]),
@@ -2473,7 +2611,7 @@ var kuinetic = (() => {
2473
2611
  id: "beam-border-auto",
2474
2612
  renderer: "javascript",
2475
2613
  channels: ["border"],
2476
- parameters: hoverTiming,
2614
+ parameters: { ...hoverTiming, ...beamParams },
2477
2615
  supportedTimelines: ["time"],
2478
2616
  supportedActivations: ["load"],
2479
2617
  defaultActivation: "load",
@@ -2762,12 +2900,344 @@ var kuinetic = (() => {
2762
2900
  return registry.registerPrimitives(INTERACTION_PRIMITIVES).registerPresets(INTERACTION_PRESETS);
2763
2901
  }
2764
2902
 
2903
+ // src/effects/catalog/background-media.ts
2904
+ var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "m4v", "ogv"]);
2905
+ var VIDEO_PAGE_HOSTS = /* @__PURE__ */ new Set([
2906
+ "youtube.com",
2907
+ "www.youtube.com",
2908
+ "m.youtube.com",
2909
+ "youtube-nocookie.com",
2910
+ "www.youtube-nocookie.com",
2911
+ "youtu.be",
2912
+ "www.youtu.be"
2913
+ ]);
2914
+ function normalizeUrl(value) {
2915
+ const stripped = value.replace(/[\t\n\r]/g, "");
2916
+ let start = 0;
2917
+ while (start < stripped.length && stripped.charCodeAt(start) <= 32) start += 1;
2918
+ return stripped.slice(start);
2919
+ }
2920
+ function hostOf(value) {
2921
+ const withoutScheme = normalizeUrl(value).replace(/^[a-z][a-z0-9+.-]*:/i, "").replace(/^\/\//, "");
2922
+ const end = withoutScheme.search(/[/?#]/);
2923
+ const authority = end === -1 ? withoutScheme : withoutScheme.slice(0, end);
2924
+ return authority.slice(authority.lastIndexOf("@") + 1).toLowerCase();
2925
+ }
2926
+ function isVideoPageUrl(value) {
2927
+ return VIDEO_PAGE_HOSTS.has(hostOf(value));
2928
+ }
2929
+ var FOCAL_POINTS = {
2930
+ center: "50% 50%",
2931
+ top: "50% 0%",
2932
+ bottom: "50% 100%",
2933
+ left: "0% 50%",
2934
+ right: "100% 50%",
2935
+ "top-left": "0% 0%",
2936
+ "top-right": "100% 0%",
2937
+ "bottom-left": "0% 100%",
2938
+ "bottom-right": "100% 100%"
2939
+ };
2940
+ var FOCAL_POINT_NAMES = Object.keys(FOCAL_POINTS);
2941
+ function focalPosition(focus) {
2942
+ return FOCAL_POINTS[focus] ?? FOCAL_POINTS.center;
2943
+ }
2944
+ function paintsOverlay(options) {
2945
+ return options.overlay !== "transparent" && options.overlayOpacity > 0;
2946
+ }
2947
+ function isVideoSource(src) {
2948
+ const path = src.split("?")[0].split("#")[0];
2949
+ const dot = path.lastIndexOf(".");
2950
+ if (dot <= path.lastIndexOf("/")) return false;
2951
+ return VIDEO_EXTENSIONS.has(path.slice(dot + 1).toLowerCase());
2952
+ }
2953
+ var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http", "https"]);
2954
+ function schemeOf(value) {
2955
+ const match = /^([a-z][a-z0-9+.-]*):/i.exec(normalizeUrl(value));
2956
+ return match ? match[1].toLowerCase() : "";
2957
+ }
2958
+ function mediaSource(authored, name, ctx) {
2959
+ if (!authored) return authored;
2960
+ if (isVideoPageUrl(authored)) {
2961
+ ctx.warn(
2962
+ `background-media "${name}": "${authored}" is a video *page*, not a media file \u2014 a YouTube URL cannot be played by <video> and needs an iframe embed instead. Point "${name}" at an .mp4/.webm file, or use a YouTube facade alongside this effect.`
2963
+ );
2964
+ return "";
2965
+ }
2966
+ const scheme = schemeOf(authored);
2967
+ if (!scheme || ALLOWED_SCHEMES.has(scheme)) return authored;
2968
+ ctx.warn(
2969
+ `background-media "${name}": "${scheme}:" URLs are not allowed \u2014 use https:, http:, or a path such as "/media/hero.mp4".`
2970
+ );
2971
+ return "";
2972
+ }
2973
+ function styleLayer(node) {
2974
+ const { style } = node;
2975
+ style.setProperty("position", "absolute");
2976
+ style.setProperty("top", "0");
2977
+ style.setProperty("left", "0");
2978
+ style.setProperty("width", "100%");
2979
+ style.setProperty("height", "100%");
2980
+ style.setProperty("border-radius", "inherit");
2981
+ style.setProperty("pointer-events", "none");
2982
+ style.setProperty("z-index", "-1");
2983
+ }
2984
+ function createOverlay(doc, options) {
2985
+ const scrim = doc.createElement("div");
2986
+ styleLayer(scrim);
2987
+ scrim.style.setProperty("background", options.overlay);
2988
+ scrim.style.setProperty("opacity", String(options.overlayOpacity));
2989
+ scrim.setAttribute("aria-hidden", "true");
2990
+ return scrim;
2991
+ }
2992
+ function createVideo(doc, options) {
2993
+ const video = doc.createElement("video");
2994
+ video.muted = true;
2995
+ video.setAttribute("muted", "");
2996
+ video.playsInline = true;
2997
+ video.setAttribute("playsinline", "");
2998
+ video.loop = options.loop;
2999
+ video.defaultPlaybackRate = options.rate;
3000
+ video.playbackRate = options.rate;
3001
+ video.preload = "metadata";
3002
+ if (options.poster) video.poster = options.poster;
3003
+ video.src = options.src;
3004
+ return video;
3005
+ }
3006
+ function play2(video) {
3007
+ const started = video.play();
3008
+ if (started) void started.catch(() => {
3009
+ });
3010
+ }
3011
+ function startPlayback(video, win, options) {
3012
+ if (options.reducedMotion || options.autoplay === "never") return () => {
3013
+ };
3014
+ if (options.autoplay === "always") {
3015
+ play2(video);
3016
+ return () => {
3017
+ if (!video.paused) video.pause();
3018
+ };
3019
+ }
3020
+ return autoplayInView(video, win);
3021
+ }
3022
+ function createImage(doc, options) {
3023
+ const image = doc.createElement("img");
3024
+ image.alt = "";
3025
+ image.decoding = "async";
3026
+ image.src = options.src;
3027
+ return image;
3028
+ }
3029
+ function autoplayInView(video, win) {
3030
+ const Observer = win.IntersectionObserver;
3031
+ if (!Observer) return () => {
3032
+ };
3033
+ const observer = new Observer(
3034
+ (entries) => {
3035
+ for (const entry of entries) {
3036
+ if (entry.isIntersecting) play2(video);
3037
+ else if (!video.paused) video.pause();
3038
+ }
3039
+ },
3040
+ { threshold: 0 }
3041
+ );
3042
+ observer.observe(video);
3043
+ return () => {
3044
+ observer.disconnect();
3045
+ if (!video.paused) video.pause();
3046
+ };
3047
+ }
3048
+ function installBackgroundMedia(el, ctx, options) {
3049
+ const doc = el.ownerDocument;
3050
+ const video = isVideoSource(options.src) ? createVideo(doc, options) : null;
3051
+ const node = video ?? createImage(doc, options);
3052
+ styleLayer(node);
3053
+ node.style.setProperty("object-fit", options.fit);
3054
+ node.style.setProperty("object-position", options.position);
3055
+ node.setAttribute("aria-hidden", "true");
3056
+ node.setAttribute("data-kui-background", "");
3057
+ el.append(node);
3058
+ const overlay = paintsOverlay(options) ? createOverlay(doc, options) : null;
3059
+ if (overlay) {
3060
+ overlay.setAttribute("data-kui-background-overlay", "");
3061
+ el.append(overlay);
3062
+ }
3063
+ const stopPlayback = video ? startPlayback(video, ctx.win, options) : () => {
3064
+ };
3065
+ return {
3066
+ remove: () => {
3067
+ stopPlayback();
3068
+ node.remove();
3069
+ overlay?.remove();
3070
+ }
3071
+ };
3072
+ }
3073
+
3074
+ // src/effects/catalog/media-shared.ts
3075
+ var AXIS_DEGREES = { vertical: 0, horizontal: 90 };
3076
+ var UNIT_DEGREES = { deg: 1, grad: 0.9, rad: 180 / Math.PI, turn: 360 };
3077
+ var BAND_OVERLAP_PX = 1;
3078
+ var STAGE_CLASS = "kui-slat-stage";
3079
+ var SLAT_CLASS = "kui-slat-item";
3080
+ var GOLDEN_RATIO_CONJUGATE = 0.6180339887498949;
3081
+ function slatOrder(index, count, from) {
3082
+ if (count <= 1) return 0;
3083
+ switch (from) {
3084
+ case "start":
3085
+ return index;
3086
+ case "end":
3087
+ return count - 1 - index;
3088
+ case "edges":
3089
+ return Math.min(index, count - 1 - index);
3090
+ case "random-ish":
3091
+ return Math.floor(index * GOLDEN_RATIO_CONJUGATE % 1 * count);
3092
+ case "alternate":
3093
+ default:
3094
+ return zigzagRank(index, count);
3095
+ }
3096
+ }
3097
+ function zigzagRank(index, count) {
3098
+ const fromStart = index;
3099
+ const fromEnd = count - 1 - index;
3100
+ const pair = Math.min(fromStart, fromEnd);
3101
+ return fromStart <= fromEnd ? pair * 2 : pair * 2 + 1;
3102
+ }
3103
+ function slatAngleDegrees(authored, axis) {
3104
+ const trimmed = authored.trim();
3105
+ if (!trimmed) return AXIS_DEGREES[axis];
3106
+ const match = /^(-?(?:\d+(?:\.\d+)?|\.\d+))(deg|rad|grad|turn)?$/.exec(trimmed);
3107
+ if (!match) return AXIS_DEGREES[axis];
3108
+ const value = Number(match[1]);
3109
+ if (!Number.isFinite(value)) return AXIS_DEGREES[axis];
3110
+ const unit = match[2] ?? "deg";
3111
+ const degrees = value * UNIT_DEGREES[unit];
3112
+ return (degrees % 180 + 180) % 180;
3113
+ }
3114
+ function slatTravelVector(angleDegrees) {
3115
+ const theta = angleDegrees * Math.PI / 180;
3116
+ return { x: -Math.sin(theta), y: Math.cos(theta) };
3117
+ }
3118
+ function slatBandClip(index, count, angleDegrees, box) {
3119
+ const { width, height } = box;
3120
+ const theta = angleDegrees * Math.PI / 180;
3121
+ const nx = Math.cos(theta);
3122
+ const ny = Math.sin(theta);
3123
+ const { x: dx, y: dy } = slatTravelVector(angleDegrees);
3124
+ const span = Math.abs(nx) * width + Math.abs(ny) * height;
3125
+ const centreX = width / 2;
3126
+ const centreY = height / 2;
3127
+ const step = span / count;
3128
+ const near = index * step - span / 2 - BAND_OVERLAP_PX;
3129
+ const far = (index + 1) * step - span / 2 + BAND_OVERLAP_PX;
3130
+ const reach = width + height;
3131
+ const corner = (along, across) => `${(centreX + along * nx + across * dx).toFixed(2)}px ${(centreY + along * ny + across * dy).toFixed(2)}px`;
3132
+ return `polygon(${corner(near, reach)}, ${corner(near, -reach)}, ${corner(far, -reach)}, ${corner(far, reach)})`;
3133
+ }
3134
+ function axisLabel(angleDegrees) {
3135
+ if (angleDegrees === 0) return "vertical";
3136
+ if (angleDegrees === 90) return "horizontal";
3137
+ return "diagonal";
3138
+ }
3139
+ function syncStageToImage(stage, img, bands) {
3140
+ const { slats, angleDegrees } = bands;
3141
+ const width = img.offsetWidth;
3142
+ const height = img.offsetHeight;
3143
+ stage.style.top = `${img.offsetTop}px`;
3144
+ stage.style.left = `${img.offsetLeft}px`;
3145
+ stage.style.width = `${width}px`;
3146
+ stage.style.height = `${height}px`;
3147
+ slats.forEach((slat, index) => {
3148
+ slat.style.clipPath = slatBandClip(index, slats.length, angleDegrees, { width, height });
3149
+ });
3150
+ }
3151
+ function watchImageBox(stage, img, win, bands) {
3152
+ const handler = () => syncStageToImage(stage, img, bands);
3153
+ win.addEventListener("resize", handler, { passive: true });
3154
+ const stopWindow = () => win.removeEventListener("resize", handler);
3155
+ const ResizeObserverCtor = win.ResizeObserver;
3156
+ if (!ResizeObserverCtor) return stopWindow;
3157
+ const observer = new ResizeObserverCtor(handler);
3158
+ observer.observe(img);
3159
+ return () => {
3160
+ stopWindow();
3161
+ observer.disconnect();
3162
+ };
3163
+ }
3164
+ function imagePaintStyle(img, win) {
3165
+ const computed = win.getComputedStyle(img);
3166
+ const position = computed.objectPosition || "50% 50%";
3167
+ const fit = computed.objectFit;
3168
+ if (fit === "cover") return { size: "cover", position };
3169
+ if (fit === "contain" || fit === "scale-down") return { size: "contain", position };
3170
+ if (fit === "none") return { size: "auto", position };
3171
+ return { size: "100% 100%", position };
3172
+ }
3173
+ function installSlatStage(el, doc, win, options) {
3174
+ const img = el.querySelector("img");
3175
+ if (!img) return null;
3176
+ const url = img.currentSrc || img.getAttribute("src") || "";
3177
+ if (!url) return null;
3178
+ const { count, angleDegrees, from, fold } = options;
3179
+ const travel = slatTravelVector(angleDegrees);
3180
+ const stage = doc.createElement("div");
3181
+ stage.className = STAGE_CLASS;
3182
+ stage.setAttribute("aria-hidden", "true");
3183
+ stage.dataset.kuiSlatAxis = axisLabel(angleDegrees);
3184
+ stage.dataset.kuiSlatAngle = String(angleDegrees);
3185
+ stage.dataset.kuiSlatFold = String(fold);
3186
+ stage.style.setProperty("--kui-slat-count", String(count));
3187
+ stage.style.setProperty("--kui-slat-dx", travel.x.toFixed(4));
3188
+ stage.style.setProperty("--kui-slat-dy", travel.y.toFixed(4));
3189
+ const paint = imagePaintStyle(img, win);
3190
+ const slats = [];
3191
+ for (let index = 0; index < count; index++) {
3192
+ const slat = doc.createElement("div");
3193
+ slat.className = SLAT_CLASS;
3194
+ slat.style.setProperty("--kui-slat-index", String(index));
3195
+ slat.style.setProperty("--kui-i", String(slatOrder(index, count, from)));
3196
+ slat.style.backgroundImage = `url("${url}")`;
3197
+ slat.style.backgroundSize = paint.size;
3198
+ slat.style.backgroundPosition = paint.position;
3199
+ stage.append(slat);
3200
+ slats.push(slat);
3201
+ }
3202
+ el.append(stage);
3203
+ const bands = { slats, angleDegrees };
3204
+ syncStageToImage(stage, img, bands);
3205
+ const stopWatching = watchImageBox(stage, img, win, bands);
3206
+ const imageStyles = createStyleLedger(img);
3207
+ imageStyles.set("visibility", "hidden");
3208
+ return {
3209
+ stage,
3210
+ slats,
3211
+ restore: () => {
3212
+ stopWatching();
3213
+ imageStyles.restore();
3214
+ stage.remove();
3215
+ }
3216
+ };
3217
+ }
3218
+ function applySlatTimingVars(stage, params) {
3219
+ const { durationMs, delayMs, easing } = params.timing;
3220
+ const duration = durationMs === void 0 ? params.text("duration", "500ms") : `${durationMs}ms`;
3221
+ const delay = delayMs === void 0 ? params.text("delay", "0ms") : `${delayMs}ms`;
3222
+ stage.style.setProperty("--kui-duration", duration);
3223
+ stage.style.setProperty("--kui-delay", delay);
3224
+ stage.style.setProperty("--kui-ease", easing ?? params.text("ease", "ease-out"));
3225
+ stage.style.setProperty("--kui-stagger", params.text("stagger", "60ms"));
3226
+ }
3227
+ function slatAssembleFinishMs(params, count) {
3228
+ if (count === 0) return 0;
3229
+ const durationMs = params.timing.durationMs ?? params.ms("duration", 500);
3230
+ const delayMs = params.timing.delayMs ?? params.ms("delay", 0);
3231
+ const staggerMs = params.ms("stagger", 60);
3232
+ return delayMs + (count - 1) * staggerMs + durationMs;
3233
+ }
3234
+
2765
3235
  // src/effects/catalog/media.ts
2766
3236
  var geometry = {
2767
3237
  distance: { type: "length", default: "24px", cssProperty: "--kui-distance" },
2768
3238
  scale: { type: "number", default: "1.12", cssProperty: "--kui-to-scale" }
2769
3239
  };
2770
- var MEDIA_PRIMITIVES = [
3240
+ var MEDIA_CSS_PRIMITIVES = [
2771
3241
  cssPrimitive("media-wipe", [CHANNEL.clip]),
2772
3242
  cssPrimitive("media-mask", ["mask"], { perfClass: "paint" }),
2773
3243
  // Not `reducedMotion: 'disable'` — that policy means "no finite duration would make sense,
@@ -2788,33 +3258,66 @@ var kuinetic = (() => {
2788
3258
  defaultActivation: "hover",
2789
3259
  perfClass: "paint"
2790
3260
  }),
3261
+ // `geometry.distance` only, not `...geometry`: `kui-blur-up` (media.css) reads `--kui-distance`
3262
+ // and `--kui-blur` but never `--kui-to-scale` — this primitive doesn't even declare
3263
+ // `CHANNEL.scale`. Spreading the whole shared object used to expose `scale:` as an
3264
+ // apparently-valid, silently-inert parameter, the same shape `flip-3d`'s dead `perspective`
3265
+ // parameter was (`entrance.css`'s comment on `kui-flip-in-x`).
2791
3266
  cssPrimitive("media-blur-up", [CHANNEL.translate, CHANNEL.filter], {
2792
- parameters: { ...geometry, blur: { type: "length", default: "16px", cssProperty: "--kui-blur" } },
3267
+ parameters: {
3268
+ distance: geometry.distance,
3269
+ blur: { type: "length", default: "16px", cssProperty: "--kui-blur" }
3270
+ },
2793
3271
  perfClass: "paint"
2794
3272
  }),
3273
+ // No `defaultActivation` — same convention `core.ts`'s `parallax`/`parallax-scale`/
3274
+ // `parallax-rotate`/`scroll-fade`/`desaturate`/`skew`/`progress`/`progress-stroke` already use
3275
+ // for every other `timelines: ['view', 'scroll', ...]` primitive. `resolveActivation`
3276
+ // (`animator.ts`) only consults `defaultActivation` when the author named no activation, and
3277
+ // falls through to `element-config.ts`'s hardcoded `'enter'` when a primitive declares none.
3278
+ // Setting it to `'manual'` here (matching `activations: ['manual']`) looked like the obviously
3279
+ // correct pairing, but it is what actually broke the effect: `effectiveActivation`
3280
+ // (`style-plan.ts`) only converts a stuck `'manual'` into `'enter'` when `config.timeline !==
3281
+ // 'time'` — i.e. only once the author has actually written `timeline:view`/`timeline:scroll`.
3282
+ // Authored bare (no `timeline:`, the sweep's own probe and the likely first thing anyone
3283
+ // tries), `config.timeline` stays the default `'time'`, that conversion never fires, and the
3284
+ // element sits at `data-kui-state="ready"` forever — which is also the state
3285
+ // `entrance.css`'s `[data-kui-state='ready'] { --kui-distance: 0px !important; }` targets, so
3286
+ // every sample read the same permanently-zeroed `--kui-distance`: not a paused animation, a
3287
+ // zeroed one. `timeline:view`/`timeline:scroll` usage is unaffected either way, since a native
3288
+ // timeline resolves to the `'native-timeline'` gate before activation is even consulted.
3289
+ // `geometry.distance` only: `kui-image-parallax-frame` never reads `--kui-to-scale` and this
3290
+ // primitive doesn't declare `CHANNEL.scale` — same dead-parameter shape as `media-blur-up` above.
2795
3291
  cssPrimitive("media-parallax-frame", [CHANNEL.translate], {
2796
- parameters: geometry,
3292
+ parameters: { distance: geometry.distance },
2797
3293
  timelines: ["view", "scroll"],
2798
3294
  activations: ["manual"],
2799
- defaultActivation: "manual",
2800
3295
  reducedMotion: "disable"
2801
3296
  }),
3297
+ // Its own `scale` parameter, not `geometry`: `kui-lightbox-open` opens from a fixed 0.92, never
3298
+ // reading `--kui-to-scale` (or any `distance`, since this primitive doesn't animate position at
3299
+ // all) — the whole shared object was dead weight here. Unlike `media-blur-up`/
3300
+ // `media-parallax-frame`, this primitive *does* declare `CHANNEL.scale`, so the fix is to wire
3301
+ // the keyframe up to a real parameter rather than remove the promise: `--kui-from-scale`, the
3302
+ // same name and "starts at this scale, animates to 1" meaning `scale`/`scale-move`
3303
+ // (`catalog/core.ts`) already use, so `lightbox-open scale:0.8` now does what it looks like it
3304
+ // should. See `kui-lightbox-open` in `media.css`.
2802
3305
  cssPrimitive("media-lightbox", [CHANNEL.opacity, CHANNEL.scale], {
2803
- parameters: geometry
3306
+ parameters: { scale: { type: "number", default: "0.92", cssProperty: "--kui-from-scale" } }
2804
3307
  })
2805
3308
  ];
2806
- var MEDIA_PRESETS = [
2807
- { name: "wipe-up", primitive: "media-wipe", keyframes: "kui-wipe-up" },
2808
- { name: "wipe-down", primitive: "media-wipe", keyframes: "kui-wipe-down" },
2809
- { name: "wipe-left", primitive: "media-wipe", keyframes: "kui-wipe-left" },
2810
- { name: "wipe-right", primitive: "media-wipe", keyframes: "kui-wipe-right" },
2811
- { name: "wipe-circle", primitive: "media-wipe", keyframes: "kui-wipe-circle" },
2812
- { name: "wipe-diagonal", primitive: "media-wipe", keyframes: "kui-wipe-diagonal" },
2813
- { name: "mask-reveal", primitive: "media-mask", keyframes: "kui-mask-reveal" },
2814
- { name: "curtain-reveal", primitive: "media-wipe", keyframes: "kui-curtain-reveal" },
3309
+ var MEDIA_CSS_PRESETS = [
3310
+ { name: "wipe-up", primitive: "media-wipe", keyframes: "kui-wipe-up", cloak: true },
3311
+ { name: "wipe-down", primitive: "media-wipe", keyframes: "kui-wipe-down", cloak: true },
3312
+ { name: "wipe-left", primitive: "media-wipe", keyframes: "kui-wipe-left", cloak: true },
3313
+ { name: "wipe-right", primitive: "media-wipe", keyframes: "kui-wipe-right", cloak: true },
3314
+ { name: "wipe-circle", primitive: "media-wipe", keyframes: "kui-wipe-circle", cloak: true },
3315
+ { name: "wipe-diagonal", primitive: "media-wipe", keyframes: "kui-wipe-diagonal", cloak: true },
3316
+ { name: "mask-reveal", primitive: "media-mask", keyframes: "kui-mask-reveal", cloak: true },
3317
+ { name: "curtain-reveal", primitive: "media-wipe", keyframes: "kui-curtain-reveal", cloak: true },
2815
3318
  { name: "ken-burns", primitive: "media-ken-burns", keyframes: "kui-ken-burns" },
2816
3319
  { name: "ken-burns-out", primitive: "media-ken-burns", keyframes: "kui-ken-burns-out" },
2817
- { name: "blur-up", primitive: "media-blur-up", keyframes: "kui-blur-up" },
3320
+ { name: "blur-up", primitive: "media-blur-up", keyframes: "kui-blur-up", cloak: true },
2818
3321
  { name: "duotone-hover", primitive: "media-filter", keyframes: "kui-duotone-hover" },
2819
3322
  { name: "grayscale-hover", primitive: "media-filter", keyframes: "kui-grayscale-hover" },
2820
3323
  { name: "saturate-hover", primitive: "media-filter", keyframes: "kui-saturate-hover" },
@@ -2826,14 +3329,316 @@ var kuinetic = (() => {
2826
3329
  { name: "before-after-wipe", primitive: "media-wipe", keyframes: "kui-before-after-wipe" },
2827
3330
  { name: "lightbox-open", primitive: "media-lightbox", keyframes: "kui-lightbox-open" }
2828
3331
  ];
3332
+ var slatParams = {
3333
+ slats: {
3334
+ type: "number",
3335
+ default: "8",
3336
+ cssProperty: "--kui-slats",
3337
+ minimum: 2,
3338
+ maximum: 24,
3339
+ integer: true
3340
+ },
3341
+ axis: {
3342
+ type: "keyword",
3343
+ default: "vertical",
3344
+ cssProperty: "--kui-axis",
3345
+ values: ["vertical", "horizontal"]
3346
+ },
3347
+ /*
3348
+ * The general form of `axis:`, in degrees: `0deg` is `axis:vertical`, `90deg` is
3349
+ * `axis:horizontal`, and anything between cuts the picture into diagonal bands. An authored
3350
+ * angle wins; leaving it off reads `axis:`, so every existing attribute keeps its meaning.
3351
+ *
3352
+ * `text`, not `angle`, for one reason: `readParams` pre-fills every declared parameter with its
3353
+ * schema default, so a typed default is indistinguishable from an authored value and there would
3354
+ * be no way to tell `angle:0deg` from "no angle, use the axis". An empty default is only possible
3355
+ * on a type that is never written to a stylesheet, which is exactly what `text` is for — and this
3356
+ * value never reaches CSS anyway. `slatAngleDegrees` does the parsing and the range clamp.
3357
+ */
3358
+ angle: { type: "text", default: "", cssProperty: "--kui-slat-angle" },
3359
+ from: {
3360
+ type: "keyword",
3361
+ default: "alternate",
3362
+ cssProperty: "--kui-from",
3363
+ values: ["alternate", "start", "end", "edges", "random-ish"]
3364
+ },
3365
+ fold: { type: "keyword", default: "false", cssProperty: "--kui-fold", values: ["true", "false"] },
3366
+ duration: { type: "time", default: "500ms", cssProperty: "--kui-duration" },
3367
+ ...TRIGGER_DELAY_PARAM,
3368
+ ease: { type: "easing", default: "ease-out", cssProperty: "--kui-ease" },
3369
+ stagger: { type: "time", default: "60ms", cssProperty: "--kui-stagger" }
3370
+ };
3371
+ function prepareSlatAssemble(el, params, ctx) {
3372
+ const doc = el.ownerDocument;
3373
+ const count = Math.min(24, Math.max(2, Math.round(params.num("slats", 8))));
3374
+ const axis = params.text("axis", "vertical");
3375
+ const angleDegrees = slatAngleDegrees(params.text("angle", ""), axis);
3376
+ const from = params.text("from", "alternate");
3377
+ const fold = params.is("fold");
3378
+ const node = el;
3379
+ if (ctx.win.getComputedStyle(node).position === "static") ctx.style.set("position", "relative");
3380
+ const built = installSlatStage(el, doc, ctx.win, { count, angleDegrees, from, fold });
3381
+ if (!built) return () => {
3382
+ };
3383
+ const { stage } = built;
3384
+ applySlatTimingVars(stage, params);
3385
+ stage.classList.add("kui-slat-animating");
3386
+ let settle2;
3387
+ const finished = new Promise((resolve) => {
3388
+ settle2 = resolve;
3389
+ });
3390
+ let landed = false;
3391
+ const land = () => {
3392
+ if (landed) return;
3393
+ landed = true;
3394
+ stage.classList.remove("kui-slat-animating");
3395
+ built.restore();
3396
+ settle2();
3397
+ };
3398
+ const timer = ctx.win.setTimeout(land, slatAssembleFinishMs(params, count));
3399
+ return {
3400
+ cleanup: () => {
3401
+ ctx.win.clearTimeout(timer);
3402
+ if (landed) return;
3403
+ landed = true;
3404
+ built.restore();
3405
+ },
3406
+ finished,
3407
+ finish: () => {
3408
+ ctx.win.clearTimeout(timer);
3409
+ land();
3410
+ }
3411
+ };
3412
+ }
3413
+ var backgroundMediaParams = {
3414
+ /*
3415
+ * `text`, and same-origin-checked at the point of use rather than by the type — identical
3416
+ * shape and identical reasoning to `media-scrub`'s own `src` (`scroll-mechanics/primitives.ts`).
3417
+ * A URL has no lexical shape to validate against, and `type: 'text'` is the one type that never
3418
+ * reaches a stylesheet, which is what makes accepting arbitrary path characters safe.
3419
+ */
3420
+ src: { type: "text", default: "", cssProperty: "--kui-src" },
3421
+ /*
3422
+ * The still a `<video>` shows before its first frame decodes. Not optional polish: without it a
3423
+ * background clip paints as an empty box for as long as the network takes, and that box is the
3424
+ * backdrop to the author's text — the one place on the page where a flash of nothing is most
3425
+ * visible. Every background video in this repo's own demo pages is authored with one.
3426
+ */
3427
+ poster: { type: "text", default: "", cssProperty: "--kui-poster" },
3428
+ /*
3429
+ * `fill`, `none` and `scale-down` are deliberately absent. `fill` is the only `object-fit` value
3430
+ * that distorts — it stretches the picture to the box rather than cropping it — and the standing
3431
+ * rule for imagery in this project is to crop, never stretch. The other two leave the media at
3432
+ * its intrinsic size inside a box sized to something else, which for a *backdrop* is a gap, not
3433
+ * a layout. Adding them would be offering three ways to get a broken background.
3434
+ */
3435
+ fit: {
3436
+ type: "keyword",
3437
+ default: "cover",
3438
+ cssProperty: "--kui-fit",
3439
+ values: ["cover", "contain"]
3440
+ },
3441
+ /*
3442
+ * Which part of the picture a `cover` crop keeps. Nine named points rather than a free
3443
+ * `object-position` string, because a free string would have to be `type: 'text'` — the one type
3444
+ * that is explicitly never written to a stylesheet (see `core/params.ts`) — and this value is
3445
+ * written to one. A keyword list is validated against its own `values`, so the author gets real
3446
+ * focal control and the CSS surface stays closed.
3447
+ */
3448
+ focus: {
3449
+ type: "keyword",
3450
+ default: "center",
3451
+ cssProperty: "--kui-focus",
3452
+ values: FOCAL_POINT_NAMES
3453
+ },
3454
+ /*
3455
+ * The scrim. This is the parameter that makes the whole effect usable, because the point of a
3456
+ * backdrop here is animated text on top of it, and text over unmodified footage is illegible
3457
+ * about half the time — a light frame arrives and the headline vanishes for those seconds.
3458
+ *
3459
+ * `type: 'color'` so it goes through the same validator every other colour does. `transparent`
3460
+ * as the default rather than an empty string for the same reason: `''` is not a colour, and a
3461
+ * default that its own type would reject is a lie the schema cannot catch. It is also the honest
3462
+ * spelling of "no scrim", and no scrim node is created for it.
3463
+ */
3464
+ overlay: { type: "color", default: "transparent", cssProperty: "--kui-overlay" },
3465
+ /*
3466
+ * Separate from the colour rather than folded into it. `overlay:rgb(0 0 0 / 45%)` does parse —
3467
+ * the tokenizer is paren-aware — but `overlay:black overlay-opacity:45%` is the spelling someone
3468
+ * reaches for while tuning legibility, and tuning is exactly what this value is for.
3469
+ */
3470
+ "overlay-opacity": { type: "percentage", default: "100%", cssProperty: "--kui-overlay-opacity" },
3471
+ /*
3472
+ * The opt-out for the play-while-visible behaviour. `in-view` pairs the clip with the viewport
3473
+ * and is right for a long section. `always` is for a short hero clip that must never be caught
3474
+ * mid-stall by a visibility heuristic. `never` installs the clip and leaves it on its poster,
3475
+ * which is also where any mode lands under a reduced-motion preference.
3476
+ */
3477
+ autoplay: {
3478
+ type: "keyword",
3479
+ default: "in-view",
3480
+ cssProperty: "--kui-autoplay",
3481
+ values: ["in-view", "always", "never"]
3482
+ },
3483
+ /*
3484
+ * Bounded at both ends: `0` is a clip that is loaded, decoding, and permanently frozen — worse
3485
+ * than `autoplay:never`, which at least says so — and browsers stop honouring rates past roughly
3486
+ * 4 anyway, so a larger number is a silent no-op rather than a faster clip.
3487
+ */
3488
+ rate: {
3489
+ type: "number",
3490
+ default: "1",
3491
+ cssProperty: "--kui-rate",
3492
+ finite: true,
3493
+ minimum: 0.25,
3494
+ maximum: 4
3495
+ },
3496
+ loop: { type: "keyword", default: "true", cssProperty: "--kui-loop", values: ["true", "false"] }
3497
+ /*
3498
+ * There is deliberately no `controls:`. The layer this primitive builds paints at `z-index: -1`
3499
+ * behind the author's own children, so a native control bar there is focusable by keyboard and
3500
+ * occluded by whatever the page happens to put over it — a player you can tab into and cannot
3501
+ * see. A clip meant to be controlled is a content `<video controls>` the author writes, not a
3502
+ * background one.
3503
+ */
3504
+ };
3505
+ function prepareBackgroundMedia(el, params, ctx) {
3506
+ const authored = params.text("src");
3507
+ if (!authored) {
3508
+ ctx.warn('background-media needs a "src:" \u2014 nothing installed');
3509
+ return () => {
3510
+ };
3511
+ }
3512
+ const src = mediaSource(authored, "src", ctx);
3513
+ if (!src) return () => {
3514
+ };
3515
+ const node = el;
3516
+ if (ctx.win.getComputedStyle(node).position === "static") ctx.style.set("position", "relative");
3517
+ ctx.style.set("isolation", "isolate");
3518
+ const layer = installBackgroundMedia(el, ctx, {
3519
+ src,
3520
+ poster: mediaSource(params.text("poster"), "poster", ctx),
3521
+ fit: params.is("fit", "contain") ? "contain" : "cover",
3522
+ position: focalPosition(params.text("focus", "center")),
3523
+ overlay: params.text("overlay", "transparent"),
3524
+ // `num` returns a percentage as a 0–1 ratio, which is exactly what `opacity` takes.
3525
+ overlayOpacity: Math.min(1, Math.max(0, params.num("overlay-opacity", 1))),
3526
+ autoplay: params.text("autoplay", "in-view"),
3527
+ rate: params.num("rate", 1),
3528
+ // `!is('loop', 'false')`, not `is('loop')`. Every other read here names its own fallback, and
3529
+ // this one has to as well: `is()` takes no fallback argument, so a bare `is('loop')` is only
3530
+ // true when something already filled the schema default in. That holds on the animator's path
3531
+ // (`readEffectParams` pre-fills every declared parameter) and not on `createParams`, so the
3532
+ // positive spelling silently defaulted a true-by-default parameter to false for any caller
3533
+ // handing over raw values. Reading it as "loop unless explicitly told not to" states the
3534
+ // default at the point of use, where it cannot drift.
3535
+ loop: !params.is("loop", "false"),
3536
+ reducedMotion: ctx.reducedMotion
3537
+ });
3538
+ return continuousSetup(layer.remove);
3539
+ }
3540
+ var MEDIA_JS_PRIMITIVES = [
3541
+ {
3542
+ id: "slat-assemble",
3543
+ renderer: "javascript",
3544
+ channels: [CHANNEL.opacity, CHANNEL.translate, CHANNEL.rotate],
3545
+ parameters: slatParams,
3546
+ supportedTimelines: ["time"],
3547
+ supportedActivations: ["load", "enter", "hover", "focus", "click", "manual"],
3548
+ defaultActivation: "enter",
3549
+ perfClass: "dom-transform",
3550
+ // Same reasoning as every JS-rendered primitive in `text.ts`: nothing here declares a CSS
3551
+ // `animation-duration` the reduced-motion policy layer could shorten, and `disable` is what
3552
+ // stops `installSlatStage`'s DOM surgery from ever running at all under reduced motion — the
3553
+ // animator never calls `activate()`, so the wrapped `<img>` is simply left exactly as authored.
3554
+ reducedMotion: "disable",
3555
+ // Land()ing hands the picture back to the real `<img>` and tears down every slat — see
3556
+ // `prepareSlatAssemble`'s `land()`. Declared, not assumed: see `restoresOnFinish`'s own comment
3557
+ // in `core/types.ts` for why the catalog's default is the opposite of this.
3558
+ restoresOnFinish: true,
3559
+ prepare: deferPrepare(prepareSlatAssemble)
3560
+ },
3561
+ {
3562
+ id: "background-media",
3563
+ /*
3564
+ * `media` is the same word `media-scrub` uses for "this effect owns what the element shows",
3565
+ * and it is what makes `background-media, video-scrub` on one element a reported conflict
3566
+ * rather than two effects silently fighting over the same picture.
3567
+ *
3568
+ * `layout` is claimed for the same reason `pin` claims it: preparation writes `position` and
3569
+ * `isolation` on the *host*, which is a stacking-context claim on someone else's element. Left
3570
+ * undeclared, `background-media, pin-section` composed silently while both decided what
3571
+ * `position` the host has — the conflict detector cannot report a claim it was never told about.
3572
+ */
3573
+ channels: ["media", "layout"],
3574
+ renderer: "javascript",
3575
+ parameters: backgroundMediaParams,
3576
+ // Not a claim to support four timelines — an abstention. A backdrop is not driven by progress
3577
+ // of any kind and this primitive never reads `Timeline`; the list exists only so that
3578
+ // `data-kui="background-media src:/hero.mp4, parallax"` plus a `timeline:view` survives
3579
+ // `compile.ts`'s `intersect`. See `TIMELINE_AGNOSTIC` (`effects/shared.ts`), shared with the
3580
+ // scroll-mechanics drivers, which abstain for the same reason.
3581
+ supportedTimelines: TIMELINE_AGNOSTIC,
3582
+ supportedActivations: ["load", "enter", "manual"],
3583
+ /*
3584
+ * `'load'`, not the catalog's usual `'enter'`, and this is the difference between working and
3585
+ * not. A backdrop is the element's appearance, so gating it on an IntersectionObserver means a
3586
+ * section that is already on screen at page load, one in a background tab (no IO callbacks
3587
+ * fire at all until the tab is foregrounded), or one whose own box is still zero-area waits an
3588
+ * unbounded time to have any background — and unlike a missed reveal, that is a visibly broken
3589
+ * page. An author who *wants* a heavy clip deferred can still write `on:enter`.
3590
+ */
3591
+ defaultActivation: "load",
3592
+ perfClass: "paint",
3593
+ /*
3594
+ * `'shorten'`, unlike every other JS-rendered primitive in this file and in `text.ts`, and
3595
+ * deliberately so. `'disable'` means the animator never calls `activate()` under a reduced
3596
+ * motion preference, which for an animation is exactly right and for this is not: it would
3597
+ * leave the element with no backdrop at all rather than a calmer one. There is no CSS duration
3598
+ * here for `'shorten'` to shorten, so the policy is inert and the effect installs normally;
3599
+ * `ctx.reducedMotion` is then read inside, where it suppresses the one genuinely motion-y part
3600
+ * — a clip's autoplay — and leaves the poster frame standing. See `autoplayInView`.
3601
+ */
3602
+ reducedMotion: "shorten",
3603
+ prepare: deferPrepare(prepareBackgroundMedia)
3604
+ }
3605
+ ];
3606
+ var MEDIA_JS_PRESETS = [
3607
+ { name: "slat-assemble", primitive: "slat-assemble", cloak: true },
3608
+ /*
3609
+ * Two names, one primitive — the alias shape the catalog already uses everywhere (`pin-until`,
3610
+ * `pin-spacer` and `stacking-cards` are three names over the one `pin` primitive; six `wipe-*`
3611
+ * names share `media-wipe`). A preset row is the alias mechanism, so a second spelling costs a
3612
+ * table entry and nothing else: no duplicated implementation to keep in sync, and both names
3613
+ * resolve to the same `prepare`.
3614
+ *
3615
+ * No `cloak` on either: the pre-JS cloak rule hides an element until the runtime installs the
3616
+ * effect's from-state, and this element is the author's own content. Cloaking it would blank
3617
+ * their text for as long as the bundle takes to arrive, to hide a backdrop that has no
3618
+ * from-state at all.
3619
+ */
3620
+ { name: "bg", primitive: "background-media" },
3621
+ { name: "background", primitive: "background-media" }
3622
+ ];
3623
+ var MEDIA_PRIMITIVES = [...MEDIA_CSS_PRIMITIVES, ...MEDIA_JS_PRIMITIVES];
3624
+ var MEDIA_PRESETS = [...MEDIA_CSS_PRESETS, ...MEDIA_JS_PRESETS];
2829
3625
  function registerMedia(registry) {
2830
3626
  return registry.registerPrimitives(MEDIA_PRIMITIVES).registerPresets(MEDIA_PRESETS);
2831
3627
  }
2832
3628
 
3629
+ // src/effects/catalog/subtree-capture.ts
3630
+ function captureChildren(el) {
3631
+ const authored = Array.from(el.childNodes);
3632
+ return () => {
3633
+ el.replaceChildren(...authored);
3634
+ };
3635
+ }
3636
+
2833
3637
  // src/effects/catalog/numbers-shared.ts
2834
3638
  var SR_ONLY_CLASS = "kui-sr-only";
2835
3639
  var DECORATIVE_CLASS = "kui-count-decorative";
2836
3640
  function installCountLayers(el, doc) {
3641
+ const restoreChildren = captureChildren(el);
2837
3642
  const decorative = doc.createElement("span");
2838
3643
  decorative.setAttribute("aria-hidden", "true");
2839
3644
  decorative.className = DECORATIVE_CLASS;
@@ -2845,9 +3650,7 @@ var kuinetic = (() => {
2845
3650
  return {
2846
3651
  decorative,
2847
3652
  srOnly,
2848
- restore: () => {
2849
- el.textContent = srOnly.textContent;
2850
- }
3653
+ restore: restoreChildren
2851
3654
  };
2852
3655
  }
2853
3656
  function easeOutCubic(t) {
@@ -2958,6 +3761,7 @@ var kuinetic = (() => {
2958
3761
  var REDUCED_MOTION_DURATION_MS = 1;
2959
3762
  var countParams = {
2960
3763
  duration: { type: "time", default: "1600ms", cssProperty: "--kui-duration" },
3764
+ ...TRIGGER_DELAY_PARAM,
2961
3765
  from: { type: "number", default: "0", cssProperty: "--kui-from" },
2962
3766
  to: { type: "number", default: "100", cssProperty: "--kui-to" },
2963
3767
  decimals: {
@@ -2978,6 +3782,7 @@ var kuinetic = (() => {
2978
3782
  };
2979
3783
  var odometerParams = {
2980
3784
  duration: { type: "time", default: "1600ms", cssProperty: "--kui-duration" },
3785
+ ...TRIGGER_DELAY_PARAM,
2981
3786
  from: { type: "number", default: "0", cssProperty: "--kui-from" },
2982
3787
  to: { type: "number", default: "100", cssProperty: "--kui-to" }
2983
3788
  };
@@ -3004,7 +3809,9 @@ var kuinetic = (() => {
3004
3809
  function tweenTimingFor(params, ctx) {
3005
3810
  return {
3006
3811
  durationMs: tweenDurationMs(params, ctx),
3007
- delayMs: Math.max(0, params.timing.delayMs ?? 0),
3812
+ // Positional first, then the same-named parameter — the two-spellings rule `effectDurationMs`
3813
+ // applies to `duration` just above, now applied to `delay` as well.
3814
+ delayMs: Math.max(0, params.timing.delayMs ?? params.ms("delay", 0)),
3008
3815
  easing: resolveEasing(params.timing.easing, ctx.warn)
3009
3816
  };
3010
3817
  }
@@ -3157,7 +3964,12 @@ var kuinetic = (() => {
3157
3964
  ];
3158
3965
  var METER_PRIMITIVES = [
3159
3966
  cssPrimitive("stroke-sweep", [CHANNEL.stroke]),
3160
- cssPrimitive("meter-bar", [CHANNEL.scale]),
3967
+ // `from` gives `progress-bar` a real knob on its start scale — it had none before. `--kui-bar-from`
3968
+ // is also what the `[data-kui-fx~='progress-bar'][data-kui-state='ready']` gate in numbers.css
3969
+ // neutralizes; see that rule's comment for the on:enter fix this parameter doubles as.
3970
+ cssPrimitive("meter-bar", [CHANNEL.scale], {
3971
+ parameters: { from: { type: "number", default: "0", cssProperty: "--kui-bar-from" } }
3972
+ }),
3161
3973
  cssPrimitive("meter-segments", [CHANNEL.opacity]),
3162
3974
  cssPrimitive("meter-stars", [CHANNEL.clip])
3163
3975
  ];
@@ -3166,7 +3978,13 @@ var kuinetic = (() => {
3166
3978
  { name: "gauge-sweep", primitive: "stroke-sweep", keyframes: "kui-gauge-sweep" },
3167
3979
  { name: "donut-sweep", primitive: "stroke-sweep", keyframes: "kui-donut-sweep" },
3168
3980
  { name: "sparkline-draw", primitive: "stroke-sweep", keyframes: "kui-sparkline-draw" },
3169
- { name: "progress-bar", primitive: "meter-bar", keyframes: "kui-progress-bar" },
3981
+ // `cloak: true`: `kui-progress-bar`'s `from { scale: 0 1 }` (numbers.css) is a zero-width box
3982
+ // while paused, not just an invisible one — so while it waits it occupies no space in layout at
3983
+ // all. Not, despite the tidier story, because an observer refuses to fire on it: Chromium was
3984
+ // measured resolving a zero-area target's `intersectionRatio` to `1`. See numbers.css's
3985
+ // `[data-kui-fx~='progress-bar'][data-kui-state='ready']` rule for the geometry half of the fix;
3986
+ // `cloak` keeps the pre-JS and post-JS "waiting" look the same (invisible) either side of it.
3987
+ { name: "progress-bar", primitive: "meter-bar", keyframes: "kui-progress-bar", cloak: true },
3170
3988
  { name: "progress-segments", primitive: "meter-segments", keyframes: "kui-progress-segments" },
3171
3989
  { name: "star-rating-fill", primitive: "meter-stars", keyframes: "kui-star-rating-fill" }
3172
3990
  ];
@@ -3181,6 +3999,7 @@ var kuinetic = (() => {
3181
3999
  var DECORATIVE_CLASS2 = "kui-split-decorative";
3182
4000
  function installSplitLayers(el, doc) {
3183
4001
  const originalText = el.textContent.trim();
4002
+ const restoreChildren = captureChildren(el);
3184
4003
  const decorative = doc.createElement("span");
3185
4004
  decorative.setAttribute("aria-hidden", "true");
3186
4005
  decorative.className = DECORATIVE_CLASS2;
@@ -3192,9 +4011,7 @@ var kuinetic = (() => {
3192
4011
  return {
3193
4012
  decorative,
3194
4013
  originalText,
3195
- restore: () => {
3196
- el.textContent = originalText;
3197
- }
4014
+ restore: restoreChildren
3198
4015
  };
3199
4016
  }
3200
4017
  function segmentGraphemes(text) {
@@ -3254,15 +4071,22 @@ var kuinetic = (() => {
3254
4071
  function appendCharSpans(container, doc, text) {
3255
4072
  const spans = [];
3256
4073
  let index = 0;
4074
+ let wordWrapper = null;
3257
4075
  for (const grapheme of segmentGraphemes(text)) {
3258
4076
  if (grapheme.trim() === "") {
3259
4077
  container.append(doc.createTextNode(grapheme));
4078
+ wordWrapper = null;
3260
4079
  continue;
3261
4080
  }
4081
+ if (!wordWrapper) {
4082
+ wordWrapper = doc.createElement("span");
4083
+ wordWrapper.className = "kui-split-word";
4084
+ container.append(wordWrapper);
4085
+ }
3262
4086
  const span = doc.createElement("span");
3263
4087
  markItem(span, index);
3264
4088
  span.textContent = grapheme;
3265
- container.append(span);
4089
+ wordWrapper.append(span);
3266
4090
  spans.push(span);
3267
4091
  index++;
3268
4092
  }
@@ -3309,7 +4133,13 @@ var kuinetic = (() => {
3309
4133
  return buckets.map((nodes, index) => {
3310
4134
  const line = doc.createElement("span");
3311
4135
  markItem(line, index, "kui-split-line");
3312
- for (const node of nodes) line.append(node);
4136
+ for (const node of nodes) {
4137
+ if (node instanceof HTMLElement) {
4138
+ node.removeAttribute("class");
4139
+ node.style.removeProperty("--kui-i");
4140
+ }
4141
+ line.append(node);
4142
+ }
3313
4143
  container.append(line);
3314
4144
  return line;
3315
4145
  });
@@ -3362,14 +4192,43 @@ var kuinetic = (() => {
3362
4192
  distance: { type: "length", default: "32px", cssProperty: "--kui-distance" }
3363
4193
  };
3364
4194
  var TEXT_CSS_PRIMITIVES = [
3365
- cssPrimitive("text-shimmer", [CHANNEL.background], { reducedMotion: "disable" }),
4195
+ // `color`, alongside `background`: both presets' unconditional rule sets `-webkit-text-fill-color:
4196
+ // transparent` so the `background-image` gradient shows through the glyphs (the standard
4197
+ // gradient-text technique). That is a real claim on the glyph fill, the same physical property
4198
+ // `text-outline-fill` animates on its own `color` channel below — without this, the two looked
4199
+ // disjoint to the compiler (`background` vs `stroke`+`color`) and composing them would let
4200
+ // whichever applied last silently win the glyph fill instead of being flagged as a conflict.
4201
+ cssPrimitive("text-shimmer", [CHANNEL.background, CHANNEL.color], { reducedMotion: "disable" }),
4202
+ // `gradient-sweep` is the only one of the three `text-sweep`-family presets whose keyframe
4203
+ // touches `-webkit-text-fill-color` (see text.css) — `highlight-sweep` and `underline-draw` only
4204
+ // paint a `background-image`. Claiming `color` for all three made the compiler reject compositions
4205
+ // like `underline-draw, text-outline-fill` as a glyph-fill conflict even though they touch disjoint
4206
+ // properties. Split so only the preset that actually claims the fill declares the channel.
4207
+ cssPrimitive("text-gradient-sweep", [CHANNEL.background, CHANNEL.color], { parameters: textSweepParams }),
3366
4208
  cssPrimitive("text-sweep", [CHANNEL.background], { parameters: textSweepParams }),
3367
4209
  cssPrimitive("text-outline-fill", [CHANNEL.stroke, CHANNEL.color]),
3368
4210
  cssPrimitive("var-weight", ["font"], { parameters: fontWeightParams }),
3369
4211
  cssPrimitive("var-width", ["font"], { parameters: fontWidthParams }),
3370
4212
  cssPrimitive("var-slant", ["font"], { parameters: fontSlantParams }),
4213
+ /*
4214
+ * `defaultActivation: 'load'` for the same reason every ambient primitive declares it, and it
4215
+ * was missing here: a marquee is continuous motion, so `reducedMotion: 'disable'` is only half
4216
+ * the rule `ambient.ts` spells out — the other half is starting on `load` rather than waiting on
4217
+ * a scroll-triggered `enter`.
4218
+ *
4219
+ * Without it, a bare `data-kui="marquee 42s"` resolved to `enter`, which `style-plan.ts`'s
4220
+ * `resolveGate` sends down the `deferred` path and stamps `animation-play-state: paused`. It
4221
+ * compiled correctly, reported `data-kui-state="ready"`, and never ran — measured on a marquee
4222
+ * fully in view, so this was not an observer that had simply not fired yet. Every page carrying
4223
+ * one had to know to write `on:load`, which is exactly the kind of thing an author cannot be
4224
+ * expected to guess.
4225
+ *
4226
+ * `marquee-scroll-linked` is unaffected: its position comes from `animation-timeline: scroll()`,
4227
+ * not from an activation.
4228
+ */
3371
4229
  cssPrimitive("text-marquee", [CHANNEL.translate], {
3372
4230
  timelines: ["time", "scroll"],
4231
+ defaultActivation: "load",
3373
4232
  reducedMotion: "disable"
3374
4233
  }),
3375
4234
  cssPrimitive("redaction-reveal", [CHANNEL.clip]),
@@ -3377,7 +4236,7 @@ var kuinetic = (() => {
3377
4236
  ];
3378
4237
  var TEXT_CSS_PRESETS = [
3379
4238
  { name: "gradient-shimmer", primitive: "text-shimmer", keyframes: "kui-gradient-shimmer" },
3380
- { name: "gradient-sweep", primitive: "text-sweep", keyframes: "kui-gradient-sweep" },
4239
+ { name: "gradient-sweep", primitive: "text-gradient-sweep", keyframes: "kui-gradient-sweep" },
3381
4240
  {
3382
4241
  name: "highlight-sweep",
3383
4242
  primitive: "text-sweep",
@@ -3414,7 +4273,7 @@ var kuinetic = (() => {
3414
4273
  }
3415
4274
  var splitTiming = {
3416
4275
  duration: { type: "time", default: "500ms", cssProperty: "--kui-duration" },
3417
- delay: { type: "time", default: "0ms", cssProperty: "--kui-delay" },
4276
+ ...TRIGGER_DELAY_PARAM,
3418
4277
  ease: { type: "easing", default: "ease-out", cssProperty: "--kui-ease" },
3419
4278
  stagger: { type: "time", default: "30ms", cssProperty: "--kui-stagger" },
3420
4279
  unit: {
@@ -3432,6 +4291,11 @@ var kuinetic = (() => {
3432
4291
  };
3433
4292
  var motionParams = {
3434
4293
  stagger: { type: "time", default: "40ms", cssProperty: "--kui-stagger" },
4294
+ // `duration`/`ease` are deliberately *not* declared beside the delay: text.css pins both for
4295
+ // wave and jitter on a higher-specificity `[data-kui-split-fx='wave'] .kui-split-item` rule, so
4296
+ // declaring them would advertise two knobs that the stylesheet then overrides. `animation-delay`
4297
+ // is the one the phase-start rule leaves alone, which is what lets `applyStaggerVars` honour it.
4298
+ ...TRIGGER_DELAY_PARAM,
3435
4299
  motion: {
3436
4300
  type: "keyword",
3437
4301
  default: "wave",
@@ -3441,10 +4305,15 @@ var kuinetic = (() => {
3441
4305
  };
3442
4306
  var typewriterParams = {
3443
4307
  step: { type: "time", default: "55ms", cssProperty: "--kui-step" },
3444
- loop: { type: "keyword", default: "false", cssProperty: "--kui-loop", values: ["true", "false"] }
4308
+ loop: { type: "keyword", default: "false", cssProperty: "--kui-loop", values: ["true", "false"] },
4309
+ ...TRIGGER_DELAY_PARAM
3445
4310
  };
3446
4311
  var scrambleParams = {
3447
4312
  step: { type: "time", default: "40ms", cssProperty: "--kui-step" },
4313
+ // `duration` gets no such shared declaration: `stepMsFor` reads its authored-or-not distinction
4314
+ // to decide between a whole-effect time and a per-tick `step:`, and a schema default would erase
4315
+ // that distinction. A `0ms` delay default has no equivalent problem.
4316
+ ...TRIGGER_DELAY_PARAM,
3448
4317
  revealEvery: {
3449
4318
  type: "number",
3450
4319
  default: "2",
@@ -3461,7 +4330,11 @@ var kuinetic = (() => {
3461
4330
  };
3462
4331
  var wordCyclerParams = {
3463
4332
  words: { type: "text", default: "", cssProperty: "--kui-words" },
3464
- interval: { type: "time", default: "2200ms", cssProperty: "--kui-interval" }
4333
+ interval: { type: "time", default: "2200ms", cssProperty: "--kui-interval" },
4334
+ // Load-bearing here, not just for symmetry: a cycler has no authored `duration` — `interval:`
4335
+ // paces it — so the positional "duration then delay" slot only reached a delay when the author
4336
+ // wrote a throwaway first value, `word-cycler 0ms 300ms`.
4337
+ ...TRIGGER_DELAY_PARAM
3465
4338
  };
3466
4339
  function prepareSplitText(el, params, ctx) {
3467
4340
  const doc = el.ownerDocument;
@@ -3507,7 +4380,7 @@ var kuinetic = (() => {
3507
4380
  layers.decorative.textContent = graphemes.slice(0, count).join("");
3508
4381
  };
3509
4382
  const run = createStepRunner(ctx.win, {
3510
- delayMs: params.timing.delayMs ?? 0,
4383
+ delayMs: params.timing.delayMs ?? params.ms("delay", 0),
3511
4384
  stepMs: stepMsFor(params, graphemes.length, 55),
3512
4385
  tick: () => {
3513
4386
  const step = nextTypeState(state, graphemes.length, loop2);
@@ -3531,6 +4404,16 @@ var kuinetic = (() => {
3531
4404
  function prepareScramble(el, params, ctx) {
3532
4405
  const charset = SCRAMBLE_CHARSETS[params.text("charset", "upper")];
3533
4406
  const revealEvery = Math.max(1, Math.round(params.num("revealEvery", 2)));
4407
+ const node = el;
4408
+ const authoredMinWidth = node.style.getPropertyValue("min-width");
4409
+ const authoredMinHeight = node.style.getPropertyValue("min-height");
4410
+ const restRect = el.getBoundingClientRect();
4411
+ ctx.style.set("min-width", `${restRect.width}px`);
4412
+ ctx.style.set("min-height", `${restRect.height}px`);
4413
+ const releaseSizeLock = () => {
4414
+ ctx.style.set("min-width", authoredMinWidth);
4415
+ ctx.style.set("min-height", authoredMinHeight);
4416
+ };
3534
4417
  const layers = installSplitLayers(el, el.ownerDocument);
3535
4418
  layers.decorative.classList.add("kui-scramble");
3536
4419
  const graphemes = segmentGraphemes(layers.originalText);
@@ -3540,26 +4423,31 @@ var kuinetic = (() => {
3540
4423
  layers.decorative.textContent = scrambledFrame(graphemes, resolved, charset, Math.random);
3541
4424
  };
3542
4425
  render();
4426
+ const totalTicks = Math.max(1, graphemes.length * revealEvery);
3543
4427
  const run = createStepRunner(ctx.win, {
3544
- delayMs: params.timing.delayMs ?? 0,
3545
- stepMs: stepMsFor(params, graphemes.length * revealEvery, 40),
4428
+ delayMs: params.timing.delayMs ?? params.ms("delay", 0),
4429
+ stepMs: stepMsFor(params, totalTicks, Math.max(1, 700 / totalTicks)),
3546
4430
  tick: () => {
3547
4431
  ticks++;
3548
4432
  if (ticks % revealEvery === 0) resolved++;
3549
4433
  render();
3550
- return resolved >= graphemes.length;
4434
+ const done = resolved >= graphemes.length;
4435
+ if (done) releaseSizeLock();
4436
+ return done;
3551
4437
  }
3552
4438
  });
3553
4439
  return {
3554
4440
  cleanup: () => {
3555
4441
  run.stop();
3556
4442
  layers.restore();
4443
+ releaseSizeLock();
3557
4444
  },
3558
4445
  finished: run.finished,
3559
4446
  finish: () => {
3560
4447
  run.stop();
3561
4448
  resolved = graphemes.length;
3562
4449
  render();
4450
+ releaseSizeLock();
3563
4451
  }
3564
4452
  };
3565
4453
  }
@@ -3567,12 +4455,12 @@ var kuinetic = (() => {
3567
4455
  const words = params.text("words", "").split("|").map((word) => word.trim()).filter(Boolean);
3568
4456
  if (words.length === 0) return () => {
3569
4457
  };
3570
- const original = el.textContent;
4458
+ const restoreChildren = captureChildren(el);
3571
4459
  const swapMs = 150;
3572
4460
  let index = 0;
3573
4461
  el.textContent = words[0];
3574
4462
  const run = createStepRunner(ctx.win, {
3575
- delayMs: params.timing.delayMs ?? 0,
4463
+ delayMs: params.timing.delayMs ?? params.ms("delay", 0),
3576
4464
  stepMs: params.ms("interval", 2200),
3577
4465
  tick: () => {
3578
4466
  el.classList.add("kui-word-cycler-swap");
@@ -3588,7 +4476,7 @@ var kuinetic = (() => {
3588
4476
  cleanup: () => {
3589
4477
  run.stop();
3590
4478
  el.classList.remove("kui-word-cycler-swap");
3591
- el.textContent = original;
4479
+ restoreChildren();
3592
4480
  },
3593
4481
  finished: run.finished,
3594
4482
  finish: () => run.stop()
@@ -3620,13 +4508,16 @@ var kuinetic = (() => {
3620
4508
  perfClass: "continuous"
3621
4509
  })
3622
4510
  ];
4511
+ var CHARS_STAGGER = "30ms";
4512
+ var WORDS_STAGGER = "90ms";
4513
+ var LINES_STAGGER = "160ms";
3623
4514
  var TEXT_JS_PRESETS = [
3624
- { name: "split-chars", primitive: "split-text", params: { unit: "chars", direction: "fade" } },
3625
- { name: "split-words", primitive: "split-text", params: { unit: "words", direction: "fade" } },
3626
- { name: "split-lines", primitive: "split-text", params: { unit: "lines", direction: "fade" } },
3627
- { name: "text-reveal-up", primitive: "split-text", params: { unit: "words", direction: "up" } },
3628
- { name: "text-reveal-down", primitive: "split-text", params: { unit: "words", direction: "down" } },
3629
- { name: "text-reveal-mask", primitive: "split-text", params: { unit: "lines", direction: "mask" } },
4515
+ { name: "split-chars", primitive: "split-text", params: { unit: "chars", direction: "fade", stagger: CHARS_STAGGER } },
4516
+ { name: "split-words", primitive: "split-text", params: { unit: "words", direction: "fade", stagger: WORDS_STAGGER } },
4517
+ { name: "split-lines", primitive: "split-text", params: { unit: "lines", direction: "fade", stagger: LINES_STAGGER } },
4518
+ { name: "text-reveal-up", primitive: "split-text", params: { unit: "words", direction: "up", stagger: WORDS_STAGGER }, cloak: true },
4519
+ { name: "text-reveal-down", primitive: "split-text", params: { unit: "words", direction: "down", stagger: WORDS_STAGGER }, cloak: true },
4520
+ { name: "text-reveal-mask", primitive: "split-text", params: { unit: "lines", direction: "mask", stagger: LINES_STAGGER }, cloak: true },
3630
4521
  { name: "text-wave", primitive: "split-text-motion", params: { motion: "wave" } },
3631
4522
  { name: "text-jitter", primitive: "split-text-motion", params: { motion: "jitter" } },
3632
4523
  { name: "typewriter", primitive: "typewriter", params: { loop: "false" } },
@@ -3658,66 +4549,130 @@ var kuinetic = (() => {
3658
4549
  distance: { type: "length", default: "24px", cssProperty: "--kui-distance" },
3659
4550
  opacity: { type: "number", default: "0", cssProperty: "--kui-from-opacity" }
3660
4551
  };
4552
+ var ENTRANCE_TIMELINES = ["time", "view", "scroll", "pin"];
3661
4553
  var PRIMITIVES = [
3662
4554
  // --- entrance / exit -------------------------------------------------------------------
3663
- cssPrimitive("reveal", [CHANNEL.opacity, CHANNEL.translate], { parameters: distance2 }),
4555
+ cssPrimitive("reveal", [CHANNEL.opacity, CHANNEL.translate], { timelines: ENTRANCE_TIMELINES, parameters: distance2 }),
3664
4556
  cssPrimitive("scale", [CHANNEL.scale], {
4557
+ timelines: ENTRANCE_TIMELINES,
3665
4558
  parameters: { scale: { type: "number", default: "0.92", cssProperty: "--kui-from-scale" } }
3666
4559
  }),
3667
4560
  // Separate from `scale` because it claims translate as well and so composes differently.
4561
+ // `distance.distance` only, not `...distance`: `kui-zoom-in-up`/`-down` (entrance.css) read
4562
+ // `--kui-distance` and `--kui-from-scale` but never `--kui-from-opacity` — this primitive
4563
+ // doesn't declare `CHANNEL.opacity`. Spreading the whole shared `distance` object used to expose
4564
+ // `opacity:` as an apparently-valid, silently-inert parameter (same dead-parameter shape as
4565
+ // `flip-3d`'s old `perspective`, fixed above).
3668
4566
  cssPrimitive("scale-move", [CHANNEL.scale, CHANNEL.translate], {
4567
+ timelines: ENTRANCE_TIMELINES,
3669
4568
  parameters: {
3670
- ...distance2,
4569
+ distance: distance2.distance,
3671
4570
  scale: { type: "number", default: "0.92", cssProperty: "--kui-from-scale" }
3672
4571
  }
3673
4572
  }),
3674
4573
  cssPrimitive("rotate", [CHANNEL.rotate], {
4574
+ timelines: ENTRANCE_TIMELINES,
3675
4575
  parameters: { angle: { type: "angle", default: "-8deg", cssProperty: "--kui-from-angle" } }
3676
4576
  }),
4577
+ // `distance.distance` only, not `...distance`: `kui-roll-in`/`-out` (entrance.css) write
4578
+ // `rotate`/`translate`, never `opacity` — this primitive doesn't declare `CHANNEL.opacity`. Same
4579
+ // dead-parameter shape as `scale-move` above.
3677
4580
  cssPrimitive("roll", [CHANNEL.rotate, CHANNEL.translate], {
4581
+ timelines: ENTRANCE_TIMELINES,
3678
4582
  parameters: {
3679
- ...distance2,
4583
+ distance: distance2.distance,
3680
4584
  angle: { type: "angle", default: "-120deg", cssProperty: "--kui-from-angle" }
3681
4585
  }
3682
4586
  }),
3683
- cssPrimitive("flip-3d", [CHANNEL.rotate], {
4587
+ // `skew`, not `rotate`: entrance.css's keyframes write `transform: perspective(...)
4588
+ // rotateX/Y(...)`, not the individual `rotate:` property — the `perspective` parameter below
4589
+ // used to compile cleanly and do nothing, because nothing read it. See entrance.css's own
4590
+ // comment on `kui-flip-in-x` for the fix; `CHANNEL.skew` is this catalog's name for "claims the
4591
+ // whole `transform` shorthand" (`core/types.ts`), shared with `scroll-skew` and `flip-face`.
4592
+ cssPrimitive("flip-3d", [CHANNEL.skew], {
4593
+ timelines: ENTRANCE_TIMELINES,
3684
4594
  parameters: {
3685
4595
  angle: { type: "angle", default: "90deg", cssProperty: "--kui-from-angle" },
3686
4596
  perspective: { type: "length", default: "1200px", cssProperty: "--kui-perspective" }
3687
4597
  }
3688
4598
  }),
3689
4599
  cssPrimitive("blur", [CHANNEL.filter], {
4600
+ timelines: ENTRANCE_TIMELINES,
3690
4601
  parameters: { blur: { type: "length", default: "12px", cssProperty: "--kui-blur" } }
3691
4602
  }),
3692
4603
  // Purpose-built combination: one keyframe, so opacity is written once instead of twice.
3693
4604
  cssPrimitive("reveal-blur", [CHANNEL.opacity, CHANNEL.translate, CHANNEL.filter], {
4605
+ timelines: ENTRANCE_TIMELINES,
3694
4606
  parameters: { ...distance2, blur: { type: "length", default: "12px", cssProperty: "--kui-blur" } }
3695
4607
  }),
3696
4608
  // --- scroll-linked ---------------------------------------------------------------------
3697
4609
  // These are progress-linked, not time-triggered: they reverse as the user scrolls back.
3698
4610
  // That is by design and is why `timeline:` is a different axis from `on:`.
4611
+ // `distance.distance` only, not the whole `distance` object: `kui-parallax-y`/`-x` (scroll.css)
4612
+ // write only `translate` — this primitive doesn't declare `CHANNEL.opacity`. Same dead-parameter
4613
+ // shape as `scale-move`/`roll` above; `parallax-y`/`parallax-x`/`depth-layer` never read
4614
+ // `--kui-from-opacity`.
3699
4615
  cssPrimitive("parallax", [CHANNEL.translate], {
3700
- parameters: distance2,
3701
- timelines: ["view", "scroll"],
4616
+ parameters: { distance: distance2.distance },
4617
+ timelines: ["view", "scroll", "pin"],
3702
4618
  activations: ["manual"],
3703
4619
  reducedMotion: "disable",
3704
4620
  perfClass: "compositor"
3705
4621
  }),
4622
+ // `from` exists because the resting end was hardcoded (scale 1 / rotate 0deg), which fixed
4623
+ // these to "grow slightly" and "tilt slightly" — a scroll-driven element that should sweep in
4624
+ // from a quarter-size or from half a turn away had no way to say so. Same `--kui-from-*`
4625
+ // properties the entrance primitives already use, so the two stay spellable the same way.
3706
4626
  cssPrimitive("parallax-scale", [CHANNEL.scale], {
3707
- parameters: { scale: { type: "number", default: "1.2", cssProperty: "--kui-to-scale" } },
3708
- timelines: ["view", "scroll"],
4627
+ parameters: {
4628
+ scale: { type: "number", default: "1.2", cssProperty: "--kui-to-scale" },
4629
+ from: { type: "number", default: "1", cssProperty: "--kui-from-scale" }
4630
+ },
4631
+ timelines: ["view", "scroll", "pin"],
3709
4632
  activations: ["manual"],
3710
4633
  reducedMotion: "disable"
3711
4634
  }),
3712
4635
  cssPrimitive("parallax-rotate", [CHANNEL.rotate], {
3713
- parameters: { angle: { type: "angle", default: "12deg", cssProperty: "--kui-to-angle" } },
3714
- timelines: ["view", "scroll"],
4636
+ parameters: {
4637
+ angle: { type: "angle", default: "12deg", cssProperty: "--kui-to-angle" },
4638
+ from: { type: "angle", default: "0deg", cssProperty: "--kui-from-angle" }
4639
+ },
4640
+ timelines: ["view", "scroll", "pin"],
3715
4641
  activations: ["manual"],
3716
4642
  reducedMotion: "disable"
3717
4643
  }),
3718
4644
  cssPrimitive("scroll-fade", [CHANNEL.opacity], {
3719
4645
  parameters: { opacity: { type: "number", default: "0", cssProperty: "--kui-from-opacity" } },
3720
- timelines: ["view", "scroll"],
4646
+ timelines: ["view", "scroll", "pin"],
4647
+ activations: ["manual"],
4648
+ reducedMotion: "disable"
4649
+ }),
4650
+ // `filter`, not `opacity` — it collides with `blur`, and declaring the real channel is what
4651
+ // makes `channels.ts` say so instead of letting the two silently overwrite each other's
4652
+ // `filter` declaration.
4653
+ cssPrimitive("desaturate", [CHANNEL.filter], {
4654
+ parameters: {
4655
+ from: { type: "percentage", default: "100%", cssProperty: "--kui-from-grayscale" },
4656
+ to: { type: "percentage", default: "0%", cssProperty: "--kui-to-grayscale" }
4657
+ },
4658
+ timelines: ["view", "scroll", "pin"],
4659
+ activations: ["manual"],
4660
+ reducedMotion: "disable",
4661
+ perfClass: "paint"
4662
+ }),
4663
+ /*
4664
+ * `transform`, not one of the independent transform properties, because CSS never shipped a
4665
+ * standalone `skew:`. That is also why `skew` is its own channel rather than folded in with
4666
+ * `rotate`: writing `transform` replaces the entire shorthand, so a skew composed with anything
4667
+ * else that wrote `transform` would silently win. Nothing else in the catalog does — every other
4668
+ * transform in the library goes through `translate`/`rotate`/`scale` — so the shorthand is free.
4669
+ */
4670
+ cssPrimitive("skew", [CHANNEL.skew], {
4671
+ parameters: {
4672
+ from: { type: "angle", default: "8deg", cssProperty: "--kui-from-skew" },
4673
+ to: { type: "angle", default: "0deg", cssProperty: "--kui-to-skew" }
4674
+ },
4675
+ timelines: ["view", "scroll", "pin"],
3721
4676
  activations: ["manual"],
3722
4677
  reducedMotion: "disable"
3723
4678
  }),
@@ -3737,13 +4692,14 @@ var kuinetic = (() => {
3737
4692
  })
3738
4693
  ];
3739
4694
  var p = (name, primitive, keyframes, params) => ({ name, primitive, keyframes, ...params ? { params } : {} });
4695
+ var pIn = (name, primitive, keyframes, params) => ({ ...p(name, primitive, keyframes, params), cloak: true });
3740
4696
  var FADE = [
3741
- p("fade-in", "reveal", "kui-in"),
4697
+ pIn("fade-in", "reveal", "kui-in"),
3742
4698
  p("fade-out", "reveal", "kui-out"),
3743
- p("fade-up", "reveal", "kui-in-up"),
3744
- p("fade-down", "reveal", "kui-in-down"),
3745
- p("fade-left", "reveal", "kui-in-left"),
3746
- p("fade-right", "reveal", "kui-in-right"),
4699
+ pIn("fade-up", "reveal", "kui-in-up"),
4700
+ pIn("fade-down", "reveal", "kui-in-down"),
4701
+ pIn("fade-left", "reveal", "kui-in-left"),
4702
+ pIn("fade-right", "reveal", "kui-in-right"),
3747
4703
  p("fade-out-up", "reveal", "kui-out-up"),
3748
4704
  p("fade-out-down", "reveal", "kui-out-down"),
3749
4705
  p("fade-out-left", "reveal", "kui-out-left"),
@@ -3751,56 +4707,56 @@ var kuinetic = (() => {
3751
4707
  ];
3752
4708
  var SLIDE_PARAMS = { distance: "100px", opacity: "1" };
3753
4709
  var SLIDE = [
3754
- p("slide-up", "reveal", "kui-in-up", SLIDE_PARAMS),
3755
- p("slide-down", "reveal", "kui-in-down", SLIDE_PARAMS),
3756
- p("slide-left", "reveal", "kui-in-left", SLIDE_PARAMS),
3757
- p("slide-right", "reveal", "kui-in-right", SLIDE_PARAMS),
4710
+ pIn("slide-up", "reveal", "kui-in-up", SLIDE_PARAMS),
4711
+ pIn("slide-down", "reveal", "kui-in-down", SLIDE_PARAMS),
4712
+ pIn("slide-left", "reveal", "kui-in-left", SLIDE_PARAMS),
4713
+ pIn("slide-right", "reveal", "kui-in-right", SLIDE_PARAMS),
3758
4714
  p("slide-out-up", "reveal", "kui-out-up", SLIDE_PARAMS),
3759
4715
  p("slide-out-down", "reveal", "kui-out-down", SLIDE_PARAMS),
3760
4716
  p("slide-out-left", "reveal", "kui-out-left", SLIDE_PARAMS),
3761
4717
  p("slide-out-right", "reveal", "kui-out-right", SLIDE_PARAMS)
3762
4718
  ];
3763
4719
  var LOGICAL = [
3764
- p("slide-inline-start", "reveal", "kui-in-inline-start", SLIDE_PARAMS),
3765
- p("slide-inline-end", "reveal", "kui-in-inline-end", SLIDE_PARAMS),
3766
- p("slide-block-start", "reveal", "kui-in-up", SLIDE_PARAMS),
3767
- p("slide-block-end", "reveal", "kui-in-down", SLIDE_PARAMS)
4720
+ pIn("slide-inline-start", "reveal", "kui-in-inline-start", SLIDE_PARAMS),
4721
+ pIn("slide-inline-end", "reveal", "kui-in-inline-end", SLIDE_PARAMS),
4722
+ pIn("slide-block-start", "reveal", "kui-in-up", SLIDE_PARAMS),
4723
+ pIn("slide-block-end", "reveal", "kui-in-down", SLIDE_PARAMS)
3768
4724
  ];
3769
4725
  var ZOOM = [
3770
- p("zoom-in", "scale", "kui-zoom-in"),
4726
+ pIn("zoom-in", "scale", "kui-zoom-in"),
3771
4727
  p("zoom-out", "scale", "kui-zoom-out"),
3772
- p("pop-in", "scale", "kui-zoom-in", { scale: "0.6", ease: "back-out" }),
4728
+ pIn("pop-in", "scale", "kui-zoom-in", { scale: "0.6", ease: "back-out" }),
3773
4729
  p("pop-out", "scale", "kui-zoom-out", { scale: "0.6", ease: "back-in" }),
3774
- p("zoom-in-up", "scale-move", "kui-zoom-in-up"),
3775
- p("zoom-in-down", "scale-move", "kui-zoom-in-down")
4730
+ pIn("zoom-in-up", "scale-move", "kui-zoom-in-up"),
4731
+ pIn("zoom-in-down", "scale-move", "kui-zoom-in-down")
3776
4732
  ];
3777
4733
  var FLIP = [
3778
- p("flip-in-x", "flip-3d", "kui-flip-in-x"),
3779
- p("flip-in-y", "flip-3d", "kui-flip-in-y"),
4734
+ pIn("flip-in-x", "flip-3d", "kui-flip-in-x"),
4735
+ pIn("flip-in-y", "flip-3d", "kui-flip-in-y"),
3780
4736
  p("flip-out-x", "flip-3d", "kui-flip-out-x"),
3781
4737
  p("flip-out-y", "flip-3d", "kui-flip-out-y")
3782
4738
  ];
3783
4739
  var ROTATE = [
3784
- p("rotate-in", "rotate", "kui-rotate-in"),
4740
+ pIn("rotate-in", "rotate", "kui-rotate-in"),
3785
4741
  p("rotate-out", "rotate", "kui-rotate-out"),
3786
- p("rotate-in-left", "rotate", "kui-rotate-in", { angle: "-45deg" }),
3787
- p("rotate-in-right", "rotate", "kui-rotate-in", { angle: "45deg" }),
3788
- p("roll-in", "roll", "kui-roll-in"),
4742
+ pIn("rotate-in-left", "rotate", "kui-rotate-in", { angle: "-45deg" }),
4743
+ pIn("rotate-in-right", "rotate", "kui-rotate-in", { angle: "45deg" }),
4744
+ pIn("roll-in", "roll", "kui-roll-in"),
3789
4745
  p("roll-out", "roll", "kui-roll-out"),
3790
- p("swing-in", "rotate", "kui-swing-in", { angle: "-15deg", ease: "back-out" })
4746
+ pIn("swing-in", "rotate", "kui-swing-in", { angle: "-15deg", ease: "back-out" })
3791
4747
  ];
3792
4748
  var BLUR = [
3793
- p("blur-in", "blur", "kui-blur-in"),
4749
+ pIn("blur-in", "blur", "kui-blur-in"),
3794
4750
  p("blur-out", "blur", "kui-blur-out"),
3795
- p("fade-blur-up", "reveal-blur", "kui-fade-blur-up"),
3796
- p("fade-blur-in", "reveal-blur", "kui-fade-blur-in")
4751
+ pIn("fade-blur-up", "reveal-blur", "kui-fade-blur-up"),
4752
+ pIn("fade-blur-in", "reveal-blur", "kui-fade-blur-in")
3797
4753
  ];
3798
4754
  var CHARACTER = [
3799
- p("bounce-in", "scale", "kui-zoom-in", { scale: "0.3", ease: "back-out" }),
3800
- p("bounce-in-up", "reveal", "kui-in-up", { distance: "60px", ease: "back-out" }),
3801
- p("bounce-in-down", "reveal", "kui-in-down", { distance: "60px", ease: "back-out" }),
3802
- p("back-in-up", "reveal", "kui-in-up", { distance: "120px", ease: "expo-out" }),
3803
- p("back-in-down", "reveal", "kui-in-down", { distance: "120px", ease: "expo-out" })
4755
+ pIn("bounce-in", "scale", "kui-zoom-in", { scale: "0.3", ease: "bounce" }),
4756
+ pIn("bounce-in-up", "reveal", "kui-in-up", { distance: "60px", ease: "back-out" }),
4757
+ pIn("bounce-in-down", "reveal", "kui-in-down", { distance: "60px", ease: "back-out" }),
4758
+ pIn("back-in-up", "reveal", "kui-in-up", { distance: "120px", ease: "expo-out" }),
4759
+ pIn("back-in-down", "reveal", "kui-in-down", { distance: "120px", ease: "expo-out" })
3804
4760
  ];
3805
4761
  var SCROLL = [
3806
4762
  p("parallax-y", "parallax", "kui-parallax-y"),
@@ -3809,11 +4765,14 @@ var kuinetic = (() => {
3809
4765
  p("parallax-rotate", "parallax-rotate", "kui-parallax-rotate"),
3810
4766
  p("depth-layer", "parallax", "kui-parallax-y", { distance: "200px" }),
3811
4767
  p("scroll-fade", "scroll-fade", "kui-scroll-fade"),
4768
+ p("scroll-desaturate", "desaturate", "kui-desaturate"),
4769
+ p("scroll-skew", "skew", "kui-scroll-skew"),
3812
4770
  p("scroll-progress-bar", "progress", "kui-progress-x"),
4771
+ p("scroll-progress-bar-y", "progress", "kui-progress-y"),
3813
4772
  p("scroll-progress-ring", "progress-stroke", "kui-progress-ring"),
3814
4773
  // `reveal-repeat` was removed: it was byte-identical to `reveal-once`, and the activation
3815
4774
  // binder unobserves after first entry, so a repeating reveal is not implementable yet.
3816
- p("reveal-once", "reveal", "kui-in-up")
4775
+ pIn("reveal-once", "reveal", "kui-in-up")
3817
4776
  ];
3818
4777
  var PRESETS = [
3819
4778
  ...FADE,
@@ -3836,10 +4795,63 @@ var kuinetic = (() => {
3836
4795
  return registry;
3837
4796
  }
3838
4797
 
4798
+ // src/effects/step-marking.ts
4799
+ var STEP_STATE_ATTR = "data-kui-step-state";
4800
+ function selectorBreadth(selector, doc) {
4801
+ try {
4802
+ if (doc.documentElement.matches(selector)) return "document-wide";
4803
+ if (doc.body?.matches(selector)) return "document-wide";
4804
+ return "ok";
4805
+ } catch {
4806
+ return "invalid";
4807
+ }
4808
+ }
4809
+ function resolveTarget(selector, ctx, effect) {
4810
+ if (!selector) return selector;
4811
+ const breadth = selectorBreadth(selector, ctx.doc);
4812
+ if (breadth === "invalid") {
4813
+ ctx.warn(`${effect} target "${selector}" is not a valid selector and will be ignored`);
4814
+ return "";
4815
+ }
4816
+ if (breadth === "document-wide") {
4817
+ ctx.warn(`${effect} target "${selector}" matches the whole document and will be ignored`);
4818
+ return "";
4819
+ }
4820
+ return selector;
4821
+ }
4822
+ function createStepMarker(resolve) {
4823
+ const ledgers = /* @__PURE__ */ new Map();
4824
+ return {
4825
+ mark(index) {
4826
+ const seen = /* @__PURE__ */ new Map();
4827
+ for (const node of resolve()) {
4828
+ const parent = node.parentElement;
4829
+ const position = seen.get(parent) ?? 0;
4830
+ seen.set(parent, position + 1);
4831
+ let ledger = ledgers.get(node);
4832
+ if (!ledger) {
4833
+ ledger = createAttributeLedger(node);
4834
+ ledgers.set(node, ledger);
4835
+ }
4836
+ ledger.set(STEP_STATE_ATTR, stepStateFor(position, index));
4837
+ }
4838
+ },
4839
+ restore() {
4840
+ for (const ledger of ledgers.values()) ledger.restore();
4841
+ ledgers.clear();
4842
+ }
4843
+ };
4844
+ }
4845
+ function stepStateFor(position, index) {
4846
+ if (position < index) return "before";
4847
+ if (position === index) return "active";
4848
+ return "after";
4849
+ }
4850
+
3839
4851
  // src/effects/forms/primitives.ts
3840
4852
  var timing = {
3841
4853
  duration: { type: "time", default: "400ms", cssProperty: "--kui-duration" },
3842
- delay: { type: "time", default: "0ms", cssProperty: "--kui-delay" },
4854
+ ...TRIGGER_DELAY_PARAM,
3843
4855
  ease: { type: "easing", default: "ease-out", cssProperty: "--kui-ease" }
3844
4856
  };
3845
4857
  var NATIVE_STATE_PRIMITIVE = {
@@ -3954,10 +4966,18 @@ var kuinetic = (() => {
3954
4966
  function nextStep(step, total) {
3955
4967
  return total > 0 ? (step + 1) % total : 0;
3956
4968
  }
3957
- function prepareStepProgress(el, params) {
4969
+ function prepareStepProgress(el, params, ctx) {
3958
4970
  const total = Math.max(1, Math.round(params.num("steps", 4)));
4971
+ const selector = resolveTarget(params.text("target"), ctx, "step-progress");
4972
+ const marker = createStepMarker(
4973
+ () => selector ? ctx.doc.querySelectorAll(selector) : el.children
4974
+ );
4975
+ const self = createAttributeLedger(el);
3959
4976
  let step = 0;
3960
- const render = () => el.setAttribute("data-kui-step", String(step));
4977
+ const render = () => {
4978
+ self.set("data-kui-step", String(step));
4979
+ marker.mark(step);
4980
+ };
3961
4981
  const advance = () => {
3962
4982
  step = nextStep(step, total);
3963
4983
  render();
@@ -3966,13 +4986,17 @@ var kuinetic = (() => {
3966
4986
  render();
3967
4987
  return () => {
3968
4988
  el.removeEventListener("click", advance);
3969
- el.removeAttribute("data-kui-step");
4989
+ self.restore();
4990
+ marker.restore();
3970
4991
  };
3971
4992
  }
3972
4993
  var STEP_PROGRESS_PRIMITIVE = jsInputPrimitive(
3973
4994
  "step-progress",
3974
4995
  ["state"],
3975
- { steps: { type: "number", default: "4", cssProperty: "--kui-steps", minimum: 1, maximum: 20, integer: true } },
4996
+ {
4997
+ steps: { type: "number", default: "4", cssProperty: "--kui-steps", minimum: 1, maximum: 20, integer: true },
4998
+ target: { type: "text", default: "", cssProperty: "--kui-target" }
4999
+ },
3976
5000
  deferPrepare(prepareStepProgress)
3977
5001
  );
3978
5002
  function nextSubmitStage(stage) {
@@ -4229,6 +5253,7 @@ var kuinetic = (() => {
4229
5253
  const resistance = params.num("resistance", 0.55);
4230
5254
  const momentum = params.num("momentum", 0.2);
4231
5255
  const offset = { x: 0, y: 0 };
5256
+ const pickup = { x: 0, y: 0 };
4232
5257
  const deps = springDeps(ctx);
4233
5258
  const runners = {
4234
5259
  x: createSpringRunner(config, (value) => write({ ...offset, x: value }), deps),
@@ -4245,10 +5270,15 @@ var kuinetic = (() => {
4245
5270
  onStart() {
4246
5271
  runners.x.stop();
4247
5272
  runners.y.stop();
5273
+ pickup.x = offset.x;
5274
+ pickup.y = offset.y;
4248
5275
  el.setAttribute("data-kui-dragging", "true");
4249
5276
  },
4250
5277
  onMove(vector) {
4251
- write({ x: resist(vector.dx, bounds, resistance), y: resist(vector.dy, bounds, resistance) });
5278
+ write({
5279
+ x: resist(pickup.x + vector.dx, bounds, resistance),
5280
+ y: resist(pickup.y + vector.dy, bounds, resistance)
5281
+ });
4252
5282
  },
4253
5283
  onEnd(vector) {
4254
5284
  el.setAttribute("data-kui-dragging", "false");
@@ -4276,8 +5306,9 @@ var kuinetic = (() => {
4276
5306
  runners.y.to(0);
4277
5307
  return;
4278
5308
  }
4279
- runners.x.to(offset.x + vector.vx * mode.momentum);
4280
- runners.y.to(offset.y + vector.vy * mode.momentum);
5309
+ const carry = mode.inertia ? mode.momentum : 0;
5310
+ runners.x.to(offset.x + vector.vx * carry);
5311
+ runners.y.to(offset.y + vector.vy * carry);
4281
5312
  }
4282
5313
  function prepareSwipeable(el, params) {
4283
5314
  const stop = recognise(
@@ -4561,9 +5592,12 @@ var kuinetic = (() => {
4561
5592
  ctx.style.claim("height");
4562
5593
  const duration = effectDurationMs(params, 400);
4563
5594
  let animation = null;
5595
+ let previous = node.getBoundingClientRect().height;
4564
5596
  const observer = watchAttribute(node, params.text("attribute"), () => {
4565
5597
  animation?.cancel();
4566
- animation = animateHeight(node, duration, params.text("ease"));
5598
+ const endpoints = heightEndpoints(node, previous);
5599
+ previous = endpoints.to;
5600
+ animation = animateHeight(node, endpoints, duration, params.text("ease"));
4567
5601
  });
4568
5602
  ctx.invalidate();
4569
5603
  return () => {
@@ -4571,13 +5605,19 @@ var kuinetic = (() => {
4571
5605
  animation?.cancel();
4572
5606
  };
4573
5607
  }
4574
- function animateHeight(node, duration, easing) {
4575
- const from = `${node.getBoundingClientRect().height}px`;
5608
+ function heightEndpoints(node, previous) {
4576
5609
  node.style.removeProperty("height");
4577
- const to = `${node.scrollHeight}px`;
5610
+ const to = node.getBoundingClientRect().height;
5611
+ return { from: previous, to };
5612
+ }
5613
+ function animateHeight(node, endpoints, duration, easing) {
4578
5614
  const animate = node.animate;
4579
5615
  if (typeof animate !== "function") return null;
4580
- return animate.call(node, [{ height: from }, { height: to }], { duration, easing, fill: "none" });
5616
+ return animate.call(
5617
+ node,
5618
+ [{ height: `${endpoints.from}px` }, { height: `${endpoints.to}px` }],
5619
+ { duration, easing, fill: "none" }
5620
+ );
4581
5621
  }
4582
5622
  function prepareIndicator(el, params, ctx) {
4583
5623
  const node = el;
@@ -4707,27 +5747,42 @@ var kuinetic = (() => {
4707
5747
  }
4708
5748
  function prepareHeaderShrink(el, params, ctx) {
4709
5749
  const offset = params.num("offset", 120);
4710
- return subscribeScrollTop(el, ctx, (top) => {
5750
+ const state = createAttributeLedger(el);
5751
+ const unsubscribe = subscribeScrollTop(el, ctx, (top) => {
4711
5752
  const progress = offset > 0 ? Math.min(1, Math.max(0, top / offset)) : 1;
4712
5753
  ctx.style.set("--kui-shrink", progress.toFixed(4));
4713
- el.setAttribute("data-kui-shrunk", String(progress >= 1));
5754
+ state.set("data-kui-shrunk", String(progress >= 1));
4714
5755
  });
5756
+ return () => {
5757
+ unsubscribe();
5758
+ state.restore();
5759
+ };
4715
5760
  }
4716
5761
  function prepareHeaderHide(el, params, ctx) {
4717
5762
  const minDelta = params.num("offset", 8);
4718
5763
  let last = 0;
4719
- return subscribeScrollTop(el, ctx, (top) => {
5764
+ const state = createAttributeLedger(el);
5765
+ const unsubscribe = subscribeScrollTop(el, ctx, (top) => {
4720
5766
  const delta = top - last;
4721
5767
  if (Math.abs(delta) < minDelta) return;
4722
- el.setAttribute("data-kui-hidden", String(delta > 0 && top > minDelta));
5768
+ state.set("data-kui-hidden", String(delta > 0 && top > minDelta));
4723
5769
  last = top;
4724
5770
  });
5771
+ return () => {
5772
+ unsubscribe();
5773
+ state.restore();
5774
+ };
4725
5775
  }
4726
5776
  function prepareBackToTop(el, params, ctx) {
4727
5777
  const offset = params.num("offset", 400);
4728
- return subscribeScrollTop(el, ctx, (top) => {
4729
- el.setAttribute("data-kui-visible", String(top > offset));
5778
+ const state = createAttributeLedger(el);
5779
+ const unsubscribe = subscribeScrollTop(el, ctx, (top) => {
5780
+ state.set("data-kui-visible", String(top > offset));
4730
5781
  });
5782
+ return () => {
5783
+ unsubscribe();
5784
+ state.restore();
5785
+ };
4731
5786
  }
4732
5787
  var NAV_JS_PRIMITIVES = [
4733
5788
  navPrimitive(
@@ -4765,13 +5820,38 @@ var kuinetic = (() => {
4765
5820
  const rect = el.getBoundingClientRect();
4766
5821
  return { top: rect.top, height: rect.height };
4767
5822
  };
5823
+ var domPosition = (el) => {
5824
+ const view = el.ownerDocument?.defaultView;
5825
+ return view ? view.getComputedStyle(el).position : "static";
5826
+ };
5827
+ var domOffsetTop = (el) => {
5828
+ const view = el.ownerDocument?.defaultView;
5829
+ if (!view) return 0;
5830
+ const parsed = Number.parseFloat(view.getComputedStyle(el).top);
5831
+ return Number.isFinite(parsed) ? parsed : 0;
5832
+ };
5833
+ function geometrySource(el, positionOf) {
5834
+ let outermostSticky = null;
5835
+ for (let node = el; node; node = node.parentElement) {
5836
+ if (positionOf(node) === "sticky") outermostSticky = node;
5837
+ }
5838
+ return outermostSticky?.parentElement ?? el;
5839
+ }
5840
+ function sourceTop(el, box, measure, positionOf) {
5841
+ const source = geometrySource(el, positionOf);
5842
+ return source === el ? box.top : measure(source).top;
5843
+ }
4768
5844
  function trackProgress(el, ctx, options, onProgress) {
4769
5845
  const measure = options.measure ?? domGeometry;
5846
+ const positionOf = options.positionOf ?? domPosition;
5847
+ const offsetOf = options.offsetOf ?? domOffsetTop;
4770
5848
  let scrollTop = 0;
4771
5849
  let scrollportTop = 0;
4772
5850
  const geometry2 = createMeasureCache(() => {
4773
5851
  const box = measure(el);
4774
- return { contentTop: box.top - scrollportTop + scrollTop, height: box.height };
5852
+ const top = options.contentAnchor ? measure(options.contentAnchor).top - box.height : sourceTop(el, box, measure, positionOf);
5853
+ const stickyOffset = options.stickyEl ? offsetOf(options.stickyEl) : 0;
5854
+ return { contentTop: top - stickyOffset - scrollportTop + scrollTop, height: box.height };
4775
5855
  });
4776
5856
  return ctx.scheduler.subscribe(ctx.rootFor(el), (frame) => {
4777
5857
  scrollTop = frame.metrics.scrollTop;
@@ -4797,11 +5877,167 @@ var kuinetic = (() => {
4797
5877
  return clamp01(-top / span);
4798
5878
  }
4799
5879
 
5880
+ // src/effects/scroll-mechanics/scroll-spy.ts
5881
+ function prepareScrollSpy(el, params, ctx) {
5882
+ const sectionsAuthored = params.text("sections");
5883
+ if (sectionsAuthored) {
5884
+ const sectionsSelector = resolveTarget(sectionsAuthored, ctx, "scroll-spy sections");
5885
+ return prepareScrollSpyContainer(el, params, ctx, sectionsSelector);
5886
+ }
5887
+ return prepareScrollSpySingle(el, params, ctx);
5888
+ }
5889
+ function prepareScrollSpySingle(el, params, ctx) {
5890
+ if (params.text("offset-top", "0px") !== "0px") {
5891
+ ctx.warn('scroll-spy "offset-top" has no effect without sections: and is ignored here');
5892
+ }
5893
+ const selector = resolveTarget(params.text("target"), ctx, "scroll-spy");
5894
+ const links = /* @__PURE__ */ new Map();
5895
+ const self = createAttributeLedger(el);
5896
+ let last;
5897
+ const untrack = trackProgress(el, ctx, { distance: params.text("distance") }, (progress) => {
5898
+ const active = progress > 0 && progress < 1;
5899
+ if (active === last) return;
5900
+ last = active;
5901
+ self.set("data-kui-active", String(active));
5902
+ if (selector) markLinks(ctx.doc, selector, active, links);
5903
+ });
5904
+ return continuousSetup(() => {
5905
+ untrack();
5906
+ self.restore();
5907
+ for (const ledger of links.values()) ledger.restore();
5908
+ });
5909
+ }
5910
+ function markLinks(doc, selector, active, links) {
5911
+ for (const link of doc.querySelectorAll(selector)) {
5912
+ let ledger = links.get(link);
5913
+ if (!ledger) {
5914
+ ledger = createAttributeLedger(link);
5915
+ links.set(link, ledger);
5916
+ }
5917
+ ledger.set("data-kui-active", String(active));
5918
+ }
5919
+ }
5920
+ function hrefHash(link) {
5921
+ const href = link.getAttribute("href") ?? "";
5922
+ const at = href.indexOf("#");
5923
+ return at === -1 ? "" : href.slice(at);
5924
+ }
5925
+ function pairSectionsWithLinks(sections, links, ctx, sectionsSelector) {
5926
+ const linksByHash = /* @__PURE__ */ new Map();
5927
+ for (const link of links) {
5928
+ const hash = hrefHash(link);
5929
+ if (hash) linksByHash.set(hash, link);
5930
+ }
5931
+ const claimed = /* @__PURE__ */ new Set();
5932
+ const pairs = sections.map((section) => {
5933
+ if (!section.id) {
5934
+ ctx.warn(
5935
+ `scroll-spy: a section matched by sections:"${sectionsSelector}" has no id and cannot be paired with a link`
5936
+ );
5937
+ return { section, link: null };
5938
+ }
5939
+ const hash = `#${section.id}`;
5940
+ const link = linksByHash.get(hash) ?? null;
5941
+ if (link) claimed.add(hash);
5942
+ return { section, link };
5943
+ });
5944
+ for (const [hash, link] of linksByHash) {
5945
+ if (!claimed.has(hash)) {
5946
+ ctx.warn(`scroll-spy: link "${link.getAttribute("href")}" matches no section id in sections:"${sectionsSelector}"`);
5947
+ }
5948
+ }
5949
+ return pairs;
5950
+ }
5951
+ function offsetTopPixels(authored, frame) {
5952
+ return toPixels(
5953
+ authored,
5954
+ {
5955
+ viewportWidth: frame.metrics.viewportWidth,
5956
+ viewportHeight: frame.metrics.viewportHeight,
5957
+ percentBasis: 0,
5958
+ fontSize: 16,
5959
+ rootFontSize: 16
5960
+ },
5961
+ 0
5962
+ );
5963
+ }
5964
+ function highestReachedIndex(tops, scrollTop, line) {
5965
+ let index = -1;
5966
+ for (let i = 0; i < tops.length; i++) {
5967
+ if (tops[i] - scrollTop - line <= 0) index = i;
5968
+ }
5969
+ return index;
5970
+ }
5971
+ function prepareScrollSpyContainer(el, params, ctx, sectionsSelector) {
5972
+ if (params.text("distance", "100vh") !== "100vh") {
5973
+ ctx.warn('scroll-spy "distance" has no effect with sections: \u2014 each section measures its own height');
5974
+ }
5975
+ const linksSelector = resolveTarget(params.text("target"), ctx, "scroll-spy target");
5976
+ const sections = sectionsSelector ? [...el.querySelectorAll(sectionsSelector)] : [];
5977
+ if (sectionsSelector && sections.length === 0) {
5978
+ ctx.warn(`scroll-spy sections:"${sectionsSelector}" matched nothing inside this element`);
5979
+ }
5980
+ const links = linksSelector ? [...el.querySelectorAll(linksSelector)] : [];
5981
+ const pairs = pairSectionsWithLinks(sections, links, ctx, sectionsSelector);
5982
+ const sectionLedgers = pairs.map((pair) => createAttributeLedger(pair.section));
5983
+ const linkLedgers = /* @__PURE__ */ new Map();
5984
+ for (const { link } of pairs) {
5985
+ if (link && !linkLedgers.has(link)) linkLedgers.set(link, createAttributeLedger(link));
5986
+ }
5987
+ const offsetAuthored = params.text("offset-top", "0px");
5988
+ let scrollTop = 0;
5989
+ let scrollportTop = 0;
5990
+ const contentTops = createMeasureCache(
5991
+ () => pairs.map(({ section }) => domGeometry(section).top - scrollportTop + scrollTop)
5992
+ );
5993
+ let active = -1;
5994
+ function setActive(index, value) {
5995
+ const pair = pairs[index];
5996
+ sectionLedgers[index].set("data-kui-active", String(value));
5997
+ if (pair.link) linkLedgers.get(pair.link).set("data-kui-active", String(value));
5998
+ }
5999
+ const untrack = ctx.scheduler.subscribe(ctx.rootFor(el), (frame) => {
6000
+ scrollTop = frame.metrics.scrollTop;
6001
+ scrollportTop = frame.metrics.viewportTop;
6002
+ const tops = contentTops.read(frame.epoch);
6003
+ const line = offsetTopPixels(offsetAuthored, frame);
6004
+ const next = highestReachedIndex(tops, scrollTop, line);
6005
+ if (next === active) return;
6006
+ if (active !== -1) setActive(active, false);
6007
+ if (next !== -1) setActive(next, true);
6008
+ active = next;
6009
+ });
6010
+ return continuousSetup(() => {
6011
+ untrack();
6012
+ for (const ledger of sectionLedgers) ledger.restore();
6013
+ for (const ledger of linkLedgers.values()) ledger.restore();
6014
+ });
6015
+ }
6016
+
4800
6017
  // src/effects/scroll-mechanics/primitives.ts
4801
6018
  var PROGRESS_VAR = "--kui-progress";
4802
6019
  var distanceParam = {
4803
6020
  distance: { type: "length", default: "100vh", cssProperty: "--kui-distance" }
4804
6021
  };
6022
+ var stickyParams = {
6023
+ "offset-top": {
6024
+ type: "length",
6025
+ default: "var(--kui-pin-offset, 0px)",
6026
+ cssProperty: "--kui-offset-top"
6027
+ },
6028
+ spacer: {
6029
+ type: "keyword",
6030
+ default: "false",
6031
+ cssProperty: "--kui-spacer",
6032
+ values: ["true", "false"]
6033
+ }
6034
+ };
6035
+ function installSticky(node, params, ctx) {
6036
+ ctx.style.set("position", "sticky");
6037
+ ctx.style.set("top", params.text("offset-top", "var(--kui-pin-offset, 0px)"));
6038
+ const inserted = params.is("spacer") ? insertSpacer(node, params.text("distance"), ctx) : null;
6039
+ return { spacer: inserted?.spacer ?? null, dispose: () => inserted?.remove() };
6040
+ }
4805
6041
  function scrollPrimitive(spec) {
4806
6042
  const { id, channels, parameters, prepare, perfClass = "compositor" } = spec;
4807
6043
  return {
@@ -4809,7 +6045,13 @@ var kuinetic = (() => {
4809
6045
  renderer: "javascript",
4810
6046
  channels,
4811
6047
  parameters,
4812
- supportedTimelines: ["time", "view", "scroll"],
6048
+ // Accepted, never read: these primitives read scroll position themselves and are never driven
6049
+ // by an `animation-timeline`. The list exists so that composing the driver with the effects it
6050
+ // drives — `data-kui="pin-section distance:200vh, parallax-rotate ... "` plus `timeline:pin` —
6051
+ // survives `compile.ts`'s `intersect`. Without it the intersection empties, `style-plan.ts`
6052
+ // refuses the timeline, and the scrub silently degrades to a one-shot. See `TIMELINE_AGNOSTIC`
6053
+ // (`effects/shared.ts`) for why the name says abstention rather than support.
6054
+ supportedTimelines: TIMELINE_AGNOSTIC,
4813
6055
  supportedActivations: ["manual", "load", "enter"],
4814
6056
  defaultActivation: "load",
4815
6057
  perfClass,
@@ -4822,19 +6064,17 @@ var kuinetic = (() => {
4822
6064
  }
4823
6065
  function preparePin(el, params, ctx) {
4824
6066
  const node = el;
4825
- ctx.style.set("position", "sticky");
4826
- ctx.style.set("top", params.text("offset", "0px"));
4827
- const removeSpacer = params.is("spacer") ? insertSpacer(node, params.text("distance"), ctx) : null;
6067
+ const { dispose: unstick } = installSticky(node, params, ctx);
4828
6068
  const tracked = node.parentElement ?? el;
4829
- const untrack = trackProgress(tracked, ctx, { distance: params.text("distance") }, (progress) => {
6069
+ const untrack = trackProgress(tracked, ctx, { distance: params.text("distance"), stickyEl: node }, (progress) => {
4830
6070
  writeProgress(ctx, progress);
4831
6071
  el.setAttribute("data-kui-pinned", progress > 0 && progress < 1 ? "true" : "false");
4832
6072
  });
4833
- return () => {
6073
+ return continuousSetup(() => {
4834
6074
  untrack();
4835
- removeSpacer?.();
6075
+ unstick();
4836
6076
  el.removeAttribute("data-kui-pinned");
4837
- };
6077
+ });
4838
6078
  }
4839
6079
  function insertSpacer(node, distance3, ctx) {
4840
6080
  const spacer = ctx.doc.createElement("div");
@@ -4844,34 +6084,81 @@ var kuinetic = (() => {
4844
6084
  spacer.style.pointerEvents = "none";
4845
6085
  node.after(spacer);
4846
6086
  ctx.invalidate();
4847
- return () => {
4848
- spacer.remove();
4849
- ctx.invalidate();
6087
+ return {
6088
+ spacer,
6089
+ remove: () => {
6090
+ spacer.remove();
6091
+ ctx.invalidate();
6092
+ }
4850
6093
  };
4851
6094
  }
4852
6095
  function prepareProgress(el, params, ctx) {
4853
6096
  const steps = Math.max(0, Math.round(params.num("steps", 0)));
6097
+ const selector = resolveTarget(params.text("target"), ctx, "scrollytelling-step");
6098
+ const marker = createStepMarker(() => ctx.doc.querySelectorAll(selector));
6099
+ const self = createAttributeLedger(el);
6100
+ let lastIndex;
4854
6101
  const untrack = trackProgress(el, ctx, { distance: params.text("distance") }, (progress) => {
4855
6102
  writeProgress(ctx, progress);
4856
- if (steps > 0) {
4857
- const index = Math.min(steps - 1, Math.floor(progress * steps));
4858
- el.setAttribute("data-kui-step", String(index));
4859
- }
6103
+ if (steps === 0) return;
6104
+ const index = Math.min(steps - 1, Math.floor(progress * steps));
6105
+ if (index === lastIndex) return;
6106
+ lastIndex = index;
6107
+ self.set("data-kui-step", String(index));
6108
+ ctx.style.set("--kui-step", String(index));
6109
+ if (selector) marker.mark(index);
4860
6110
  });
4861
- return () => {
6111
+ return continuousSetup(() => {
4862
6112
  untrack();
4863
- el.removeAttribute("data-kui-step");
4864
- };
6113
+ self.restore();
6114
+ marker.restore();
6115
+ });
4865
6116
  }
4866
6117
  function prepareHorizontal(el, params, ctx) {
4867
- const node = el;
6118
+ const selector = resolveTarget(params.text("target"), ctx, "horizontal-scroll");
6119
+ if (!selector) return prepareBareTrack(el, params, ctx);
6120
+ const track = el.querySelector(selector);
6121
+ if (!track) {
6122
+ ctx.warn(`horizontal-scroll target "${selector}" matched nothing inside this element`);
6123
+ return () => {
6124
+ };
6125
+ }
6126
+ return prepareManagedTrack(el, track, params, ctx);
6127
+ }
6128
+ function prepareBareTrack(node, params, ctx) {
4868
6129
  const authored = params.text("travel", "auto");
4869
6130
  const travel = createMeasureCache(() => trackTravel(node, authored, node.ownerDocument));
4870
- const untrack = trackProgress(el, ctx, { distance: params.text("distance") }, (progress, frame) => {
4871
- ctx.style.set("translate", `${-progress * travel.read(frame.epoch)}px 0`);
6131
+ return continuousSetup(
6132
+ trackProgress(node, ctx, { distance: params.text("distance") }, (progress, frame) => {
6133
+ ctx.style.set("translate", `${-progress * travel.read(frame.epoch)}px 0`);
6134
+ writeProgress(ctx, progress);
6135
+ })
6136
+ );
6137
+ }
6138
+ function prepareManagedTrack(host, track, params, ctx) {
6139
+ const offsetTop = params.text("offset-top", "var(--kui-pin-offset, 0px)");
6140
+ ctx.style.set("position", "sticky");
6141
+ ctx.style.set("top", offsetTop);
6142
+ ctx.style.set("height", `calc(100vh - ${offsetTop})`);
6143
+ ctx.style.set("overflow", "hidden");
6144
+ ctx.style.set("display", "grid");
6145
+ ctx.style.set("align-content", "center");
6146
+ const { remove: removeSpacer } = insertSpacer(host, params.text("distance"), ctx);
6147
+ const rail = createStyleLedger(track);
6148
+ rail.set("display", "flex");
6149
+ rail.set("width", "max-content");
6150
+ const authored = params.text("travel", "auto");
6151
+ const travel = createMeasureCache(() => trackTravel(track, authored, track.ownerDocument));
6152
+ const tracked = host.parentElement ?? host;
6153
+ const untrack = trackProgress(tracked, ctx, { distance: params.text("distance"), stickyEl: host }, (progress, frame) => {
6154
+ rail.set("translate", `${-progress * travel.read(frame.epoch)}px 0`);
4872
6155
  writeProgress(ctx, progress);
4873
6156
  });
4874
- return untrack;
6157
+ return continuousSetup(() => {
6158
+ untrack();
6159
+ rail.restore();
6160
+ removeSpacer();
6161
+ });
4875
6162
  }
4876
6163
  function trackTravel(node, authored, doc) {
4877
6164
  if (authored && authored !== "auto") return toPixels(authored, ABSOLUTE_BASIS, 0);
@@ -4881,11 +6168,41 @@ var kuinetic = (() => {
4881
6168
  return Math.max(0, node.scrollWidth - viewportWidth);
4882
6169
  }
4883
6170
  function prepareMediaScrub(el, params, ctx) {
6171
+ const managed = params.is("spacer") ? installSticky(el, params, ctx) : null;
6172
+ const contentAnchor = managed?.spacer ?? void 0;
6173
+ const stickyEl = managed ? el : void 0;
6174
+ const selector = resolveTarget(params.text("target"), ctx, "media-scrub");
6175
+ const scrub = selector ? prepareTargetScrub(el, params, ctx, { selector, contentAnchor, stickyEl }) : prepareSrcScrub(el, params, ctx, { contentAnchor, stickyEl });
6176
+ return continuousSetup(() => {
6177
+ scrub();
6178
+ managed?.dispose();
6179
+ });
6180
+ }
6181
+ function prepareTargetScrub(el, params, ctx, authored) {
6182
+ const { selector, contentAnchor, stickyEl } = authored;
6183
+ const marker = createStepMarker(() => ctx.doc.querySelectorAll(selector));
6184
+ const frames = Math.max(1, ctx.doc.querySelectorAll(selector).length);
6185
+ let lastIndex;
6186
+ const untrack = trackProgress(el, ctx, { distance: params.text("distance"), contentAnchor, stickyEl }, (progress) => {
6187
+ writeProgress(ctx, progress);
6188
+ const index = Math.min(frames - 1, Math.floor(progress * frames));
6189
+ if (index === lastIndex) return;
6190
+ lastIndex = index;
6191
+ marker.mark(index);
6192
+ });
6193
+ marker.mark(0);
6194
+ return () => {
6195
+ untrack();
6196
+ marker.restore();
6197
+ };
6198
+ }
6199
+ function prepareSrcScrub(el, params, ctx, anchors) {
6200
+ const { contentAnchor, stickyEl } = anchors;
4884
6201
  const frames = Math.max(1, Math.round(params.num("frames", 1)));
4885
6202
  const pattern = mediaSrcPattern(params.text("src"), ctx);
4886
6203
  const media = el;
4887
6204
  let lastIndex = -1;
4888
- const untrack = trackProgress(el, ctx, { distance: params.text("distance") }, (progress) => {
6205
+ const untrack = trackProgress(el, ctx, { distance: params.text("distance"), contentAnchor, stickyEl }, (progress) => {
4889
6206
  writeProgress(ctx, progress);
4890
6207
  const index = Math.min(frames - 1, Math.floor(progress * frames));
4891
6208
  if (index === lastIndex) return;
@@ -4909,80 +6226,37 @@ var kuinetic = (() => {
4909
6226
  if (media.tagName !== "IMG") return;
4910
6227
  if (pattern) media.src = pattern.replace("{i}", String(index).padStart(String(frames).length, "0"));
4911
6228
  }
4912
- function prepareScrollSpy(el, params, ctx) {
4913
- const selector = spyTarget(params.text("target"), ctx);
4914
- const links = /* @__PURE__ */ new Map();
4915
- const self = createAttributeLedger(el);
4916
- let last;
4917
- const untrack = trackProgress(el, ctx, { distance: params.text("distance") }, (progress) => {
4918
- const active = progress > 0 && progress < 1;
4919
- if (active === last) return;
4920
- last = active;
4921
- self.set("data-kui-active", String(active));
4922
- if (selector) markLinks(ctx.doc, selector, active, links);
4923
- });
6229
+ function prepareSmoothScroll(el, params, ctx) {
6230
+ ctx.style.set("scroll-behavior", params.text("behavior", "smooth"));
4924
6231
  return () => {
4925
- untrack();
4926
- self.restore();
4927
- for (const ledger of links.values()) ledger.restore();
4928
6232
  };
4929
6233
  }
4930
- function spyTarget(selector, ctx) {
4931
- if (!selector) return selector;
4932
- const breadth = selectorBreadth(selector, ctx.doc);
4933
- if (breadth === "invalid") {
4934
- ctx.warn(`scroll-spy target "${selector}" is not a valid selector and will be ignored`);
4935
- return "";
4936
- }
4937
- if (breadth === "document-wide") {
4938
- ctx.warn(`scroll-spy target "${selector}" matches the whole document and will be ignored`);
4939
- return "";
4940
- }
4941
- return selector;
4942
- }
4943
- function selectorBreadth(selector, doc) {
4944
- try {
4945
- if (doc.documentElement.matches(selector)) return "document-wide";
4946
- if (doc.body?.matches(selector)) return "document-wide";
4947
- return "ok";
4948
- } catch {
4949
- return "invalid";
4950
- }
4951
- }
4952
- function markLinks(doc, selector, active, links) {
4953
- for (const link of doc.querySelectorAll(selector)) {
4954
- let ledger = links.get(link);
4955
- if (!ledger) {
4956
- ledger = createAttributeLedger(link);
4957
- links.set(link, ledger);
4958
- }
4959
- ledger.set("data-kui-active", String(active));
4960
- }
4961
- }
4962
6234
  function prepareSnap(el, params, ctx) {
4963
- ctx.style.set(
4964
- "scroll-snap-type",
4965
- `${params.is("axis", "x") ? "x" : "y"} ${params.text("strictness", "mandatory")}`
4966
- );
4967
- const childLedgers = [...el.children].map((child) => createStyleLedger(child));
6235
+ const axis = params.is("axis", "x") ? "x" : "y";
6236
+ ctx.style.set("scroll-snap-type", `${axis} ${params.text("strictness", "mandatory")}`);
6237
+ const selector = resolveTarget(params.text("target"), ctx, "scroll-snap");
6238
+ const items = selector ? [...el.querySelectorAll(selector)] : [...el.children];
6239
+ if (selector && items.length === 0) {
6240
+ ctx.warn(`scroll-snap target "${selector}" matched nothing inside this element`);
6241
+ }
6242
+ if (selector) installSnapContainer(axis, ctx);
6243
+ const childLedgers = items.map((child) => createStyleLedger(child));
4968
6244
  for (const ledger of childLedgers) ledger.set("scroll-snap-align", params.text("align", "start"));
4969
6245
  return () => {
4970
6246
  for (const ledger of childLedgers) ledger.restore();
4971
6247
  };
4972
6248
  }
6249
+ function installSnapContainer(axis, ctx) {
6250
+ ctx.style.set(axis === "x" ? "overflow-x" : "overflow-y", "auto");
6251
+ if (axis === "x") ctx.style.set("display", "flex");
6252
+ }
4973
6253
  var SCROLL_PRIMITIVES = [
4974
6254
  scrollPrimitive({
4975
6255
  id: "pin",
4976
6256
  channels: ["layout", "progress"],
4977
6257
  parameters: {
4978
6258
  ...distanceParam,
4979
- offset: { type: "length", default: "0px", cssProperty: "--kui-offset" },
4980
- spacer: {
4981
- type: "keyword",
4982
- default: "false",
4983
- cssProperty: "--kui-spacer",
4984
- values: ["true", "false"]
4985
- }
6259
+ ...stickyParams
4986
6260
  },
4987
6261
  prepare: deferPrepare(preparePin),
4988
6262
  perfClass: "layout"
@@ -4992,7 +6266,10 @@ var kuinetic = (() => {
4992
6266
  channels: ["progress"],
4993
6267
  parameters: {
4994
6268
  ...distanceParam,
4995
- steps: { type: "number", default: "0", cssProperty: "--kui-steps" }
6269
+ steps: { type: "number", default: "0", cssProperty: "--kui-steps" },
6270
+ // Same name, same shape and the same validation as scroll-spy's: one `target:` convention
6271
+ // across the library rather than a second word for "the elements this effect marks".
6272
+ target: { type: "text", default: "", cssProperty: "--kui-target" }
4996
6273
  },
4997
6274
  prepare: deferPrepare(prepareProgress)
4998
6275
  }),
@@ -5001,7 +6278,16 @@ var kuinetic = (() => {
5001
6278
  channels: ["translate", "progress"],
5002
6279
  parameters: {
5003
6280
  ...distanceParam,
5004
- travel: { type: "text", default: "auto", cssProperty: "--kui-travel" }
6281
+ travel: { type: "text", default: "auto", cssProperty: "--kui-travel" },
6282
+ // Same name, same shape and the same validation as scroll-spy's and media-scrub's: one
6283
+ // `target:` convention across the library. Naming the row that moves is also what opts this
6284
+ // primitive into owning the stage, the sticky window and the row's layout itself.
6285
+ target: { type: "text", default: "", cssProperty: "--kui-target" },
6286
+ "offset-top": {
6287
+ type: "length",
6288
+ default: "var(--kui-pin-offset, 0px)",
6289
+ cssProperty: "--kui-offset-top"
6290
+ }
5005
6291
  },
5006
6292
  prepare: deferPrepare(prepareHorizontal)
5007
6293
  }),
@@ -5010,8 +6296,13 @@ var kuinetic = (() => {
5010
6296
  channels: ["media", "progress"],
5011
6297
  parameters: {
5012
6298
  ...distanceParam,
6299
+ // A scrub is a hold, so it needs the same two knobs a pin does. Declaring them here is what
6300
+ // lets `sequence-scrub` become one attribute with no wrapper at all.
6301
+ ...stickyParams,
5013
6302
  frames: { type: "number", default: "1", cssProperty: "--kui-frames" },
5014
- src: { type: "text", default: "", cssProperty: "--kui-src" }
6303
+ src: { type: "text", default: "", cssProperty: "--kui-src" },
6304
+ // The preferred form. `frames:`/`src:` remain for sequences too long to author as tags.
6305
+ target: { type: "text", default: "", cssProperty: "--kui-target" }
5015
6306
  },
5016
6307
  prepare: deferPrepare(prepareMediaScrub),
5017
6308
  perfClass: "paint"
@@ -5020,11 +6311,44 @@ var kuinetic = (() => {
5020
6311
  id: "scroll-spy",
5021
6312
  channels: ["state"],
5022
6313
  parameters: {
6314
+ // `distance`: the per-section form only. `offset-top`: the container form only. Each is a
6315
+ // no-op — warned, not silent — in the other; see `prepareScrollSpySingle` and
6316
+ // `prepareScrollSpyContainer`.
5023
6317
  ...distanceParam,
5024
- target: { type: "text", default: "", cssProperty: "--kui-target" }
6318
+ // Same name and meaning in both forms: the link(s) this instance marks. Per-section, the
6319
+ // one link this section names; with `sections:`, every link `target:` matches, each paired
6320
+ // to its own section by `href`. See `prepareScrollSpyContainer`.
6321
+ target: { type: "text", default: "", cssProperty: "--kui-target" },
6322
+ // Presence, not value, selects the container form: authoring this at all switches
6323
+ // `prepareScrollSpy` from one-section-per-instance to one-instance-on-the-shared-ancestor.
6324
+ sections: { type: "text", default: "", cssProperty: "--kui-sections" },
6325
+ "offset-top": { type: "length", default: "0px", cssProperty: "--kui-offset-top" }
5025
6326
  },
5026
6327
  prepare: deferPrepare(prepareScrollSpy)
5027
6328
  }),
6329
+ scrollPrimitive({
6330
+ id: "smooth-scroll",
6331
+ /*
6332
+ * Its own channel, not the `'layout'` it used to share with `pin`, `stacking-cards` and
6333
+ * `scroll-snap`. The channel model exists to stop two effects fighting over the same CSS
6334
+ * property, and this one writes exactly `scroll-behavior` — a property that describes how a
6335
+ * *user-or-script-initiated* scroll is performed, and that no other primitive touches.
6336
+ *
6337
+ * On `'layout'` it made a legitimate pairing impossible. Both `smooth-scroll` and
6338
+ * `scroll-snap` have to sit on the document element to have any effect at all — neither
6339
+ * `scroll-behavior` nor `scroll-snap-type` is propagated to the viewport from `<body>` — so
6340
+ * "apply them to nested elements", the advice the conflict message gives, has no valid
6341
+ * nesting to offer here. `data-kui="smooth-scroll-to, scroll-snap-y"` on `<html>` is the
6342
+ * ordinary way to ask for smooth anchor jumps on a page that also snaps, and it was refused
6343
+ * for a collision that cannot happen: the two write disjoint properties.
6344
+ */
6345
+ channels: ["scroll-behavior"],
6346
+ parameters: {
6347
+ behavior: { type: "keyword", default: "smooth", cssProperty: "--kui-scroll-behavior", values: ["smooth", "auto"] }
6348
+ },
6349
+ prepare: deferPrepare(prepareSmoothScroll),
6350
+ perfClass: "layout"
6351
+ }),
5028
6352
  scrollPrimitive({
5029
6353
  id: "scroll-snap",
5030
6354
  channels: ["layout"],
@@ -5041,7 +6365,11 @@ var kuinetic = (() => {
5041
6365
  default: "start",
5042
6366
  cssProperty: "--kui-snap-align",
5043
6367
  values: ["start", "center", "end"]
5044
- }
6368
+ },
6369
+ // Names the snap items. Also what opts this primitive into owning the scroll container
6370
+ // itself — see `installSnapContainer`. Without it, the direct children are the items and
6371
+ // the page keeps its own `overflow`, exactly as before.
6372
+ target: { type: "text", default: "", cssProperty: "--kui-target" }
5045
6373
  },
5046
6374
  prepare: deferPrepare(prepareSnap),
5047
6375
  perfClass: "layout"
@@ -5064,11 +6392,28 @@ var kuinetic = (() => {
5064
6392
  // --- travel ------------------------------------------------------------------------------
5065
6393
  { name: "horizontal-scroll", primitive: "horizontal-track" },
5066
6394
  // --- media -------------------------------------------------------------------------------
5067
- { name: "sequence-scrub", primitive: "media-scrub" },
6395
+ /*
6396
+ * `spacer:true` is what deletes the `.scrub-stage` wrapper a page used to hand-write.
6397
+ *
6398
+ * It could not be switched on until the tracker stopped measuring against the parent. A scrub
6399
+ * makes itself sticky, and `geometrySource` escapes a sticky subtree by taking its parent — so
6400
+ * with the wrapper gone the scrub was measured against whatever section contained it. Measured
6401
+ * on `demo/scroll.html`: the parent started 926px above the scrub against a 1817px distance, so
6402
+ * progress reached 51% before the element had even stuck and half the sequence played off
6403
+ * screen. The wrapper was not ceremony; it was the tight box that made the parent honest.
6404
+ *
6405
+ * `trackProgress`'s `contentAnchor` is the fix. Progress is read from the spacer, which the
6406
+ * library inserts, is exactly `distance` tall, is never sticky, and moves with the content — so
6407
+ * there is no wrapper to write and nothing to disagree with.
6408
+ *
6409
+ * `video-scrub` stays off: a video positioned by the page is not asking the library for a box.
6410
+ */
6411
+ { name: "sequence-scrub", primitive: "media-scrub", params: { spacer: "true" } },
5068
6412
  { name: "video-scrub", primitive: "media-scrub" },
5069
6413
  // --- navigation --------------------------------------------------------------------------
5070
6414
  { name: "scroll-spy", primitive: "scroll-spy" },
5071
6415
  // --- native CSS passthroughs ---------------------------------------------------------------
6416
+ { name: "smooth-scroll-to", primitive: "smooth-scroll" },
5072
6417
  { name: "scroll-snap-x", primitive: "scroll-snap", params: { axis: "x" } },
5073
6418
  { name: "scroll-snap-y", primitive: "scroll-snap", params: { axis: "y" } }
5074
6419
  ];
@@ -5083,11 +6428,16 @@ var kuinetic = (() => {
5083
6428
  var UNSUPPORTED = /[AaSsQqTt]/;
5084
6429
  function parsePath(d) {
5085
6430
  if (UNSUPPORTED.test(d)) {
5086
- return { segments: [], reason: "arc and shorthand commands (A S Q T) are not supported" };
6431
+ return {
6432
+ segments: [],
6433
+ subpaths: [],
6434
+ reason: "arc and shorthand commands (A S Q T) are not supported"
6435
+ };
5087
6436
  }
5088
6437
  const tokens = [...d.matchAll(COMMAND)].map((m) => m[1] ?? m[2]);
5089
6438
  const state = {
5090
- segments: [],
6439
+ subpaths: [],
6440
+ open: void 0,
5091
6441
  current: { x: 0, y: 0 },
5092
6442
  start: { x: 0, y: 0 },
5093
6443
  command: ""
@@ -5101,18 +6451,33 @@ var kuinetic = (() => {
5101
6451
  index++;
5102
6452
  continue;
5103
6453
  }
5104
- if (!state.command) return { segments: [], reason: "path must start with a command letter" };
6454
+ if (!state.command) {
6455
+ return { segments: [], subpaths: [], reason: "path must start with a command letter" };
6456
+ }
5105
6457
  index = consume(tokens, index, state);
5106
6458
  }
5107
- if (state.segments.length === 0) return { segments: [], reason: "no drawable segments" };
5108
- return { segments: state.segments };
6459
+ if (state.subpaths.length === 0) {
6460
+ return { segments: [], subpaths: [], reason: "no drawable segments" };
6461
+ }
6462
+ return { segments: state.subpaths.flatMap((sub) => sub.segments), subpaths: state.subpaths };
6463
+ }
6464
+ function pushSegment(state, segment) {
6465
+ if (!state.open) {
6466
+ state.open = { segments: [], closed: false };
6467
+ state.subpaths.push(state.open);
6468
+ }
6469
+ state.open.segments.push(segment);
5109
6470
  }
5110
6471
  var ARITY = { m: 2, l: 2, h: 1, v: 1, c: 6, z: 0 };
5111
6472
  function closeSubpath(state) {
5112
- const { current, start } = state;
5113
- if (Math.abs(current.x - start.x) < 1e-6 && Math.abs(current.y - start.y) < 1e-6) return;
5114
- state.segments.push(lineToCubic(current, start));
6473
+ const { current, start, open } = state;
6474
+ if (!open) return;
6475
+ if (Math.abs(current.x - start.x) >= 1e-6 || Math.abs(current.y - start.y) >= 1e-6) {
6476
+ open.segments.push(lineToCubic(current, start));
6477
+ }
6478
+ open.closed = true;
5115
6479
  state.current = { ...start };
6480
+ state.open = void 0;
5116
6481
  }
5117
6482
  function consume(tokens, index, state) {
5118
6483
  const key = state.command.toLowerCase();
@@ -5124,10 +6489,11 @@ var kuinetic = (() => {
5124
6489
  if (key === "m") {
5125
6490
  state.current = next;
5126
6491
  state.start = next;
6492
+ state.open = void 0;
5127
6493
  state.command = relative ? "l" : "L";
5128
6494
  return index + arity;
5129
6495
  }
5130
- state.segments.push(straightOrCubic({ key, args, from: state.current, relative }, next));
6496
+ pushSegment(state, straightOrCubic({ key, args, from: state.current, relative }, next));
5131
6497
  state.current = next;
5132
6498
  return index + arity;
5133
6499
  }
@@ -5190,34 +6556,78 @@ var kuinetic = (() => {
5190
6556
  function chordLength(segment) {
5191
6557
  return Math.hypot(segment.to.x - segment.from.x, segment.to.y - segment.from.y);
5192
6558
  }
5193
- function toPathData(segments) {
5194
- if (segments.length === 0) return "";
5195
- const head = segments[0];
5196
- const parts = [`M${round(head.from.x)},${round(head.from.y)}`];
5197
- for (const s of segments) {
5198
- parts.push(
5199
- `C${round(s.c1.x)},${round(s.c1.y)} ${round(s.c2.x)},${round(s.c2.y)} ${round(s.to.x)},${round(s.to.y)}`
5200
- );
6559
+ function round(value) {
6560
+ return Math.round(value * 100) / 100;
6561
+ }
6562
+ function subpathsToPathData(subpaths) {
6563
+ const parts = [];
6564
+ for (const sub of subpaths) {
6565
+ const head = sub.segments[0];
6566
+ parts.push(`M${round(head.from.x)},${round(head.from.y)}`);
6567
+ for (const s of sub.segments) {
6568
+ parts.push(
6569
+ `C${round(s.c1.x)},${round(s.c1.y)} ${round(s.c2.x)},${round(s.c2.y)} ${round(s.to.x)},${round(s.to.y)}`
6570
+ );
6571
+ }
6572
+ if (sub.closed) parts.push("Z");
5201
6573
  }
5202
6574
  return parts.join(" ");
5203
6575
  }
5204
- function round(value) {
5205
- return Math.round(value * 100) / 100;
6576
+ function centroidOf(subpath) {
6577
+ let x = 0;
6578
+ let y = 0;
6579
+ for (const s of subpath.segments) {
6580
+ x += s.from.x;
6581
+ y += s.from.y;
6582
+ }
6583
+ return { x: x / subpath.segments.length, y: y / subpath.segments.length };
6584
+ }
6585
+ function collapsedLike(partner) {
6586
+ const at = centroidOf(partner);
6587
+ return {
6588
+ segments: partner.segments.map(() => ({
6589
+ from: { ...at },
6590
+ c1: { ...at },
6591
+ c2: { ...at },
6592
+ to: { ...at }
6593
+ })),
6594
+ closed: partner.closed
6595
+ };
6596
+ }
6597
+ function normaliseSubpaths(a, b) {
6598
+ const count = Math.max(a.length, b.length);
6599
+ const from = [];
6600
+ const to = [];
6601
+ for (let i = 0; i < count; i++) {
6602
+ const left = a[i] ?? collapsedLike(b[i]);
6603
+ const right = b[i] ?? collapsedLike(a[i]);
6604
+ const target = Math.max(left.segments.length, right.segments.length);
6605
+ const closed = left.closed && right.closed;
6606
+ from.push({ segments: normaliseCount(left.segments, target), closed });
6607
+ to.push({ segments: normaliseCount(right.segments, target), closed });
6608
+ }
6609
+ return { from, to };
5206
6610
  }
5207
6611
  function createMorph(fromPath, toPath) {
5208
6612
  const a = parsePath(fromPath);
5209
6613
  const b = parsePath(toPath);
5210
6614
  if (a.reason) return { reason: `start path: ${a.reason}` };
5211
6615
  if (b.reason) return { reason: `end path: ${b.reason}` };
5212
- const count = Math.max(a.segments.length, b.segments.length);
5213
- const from = normaliseCount(a.segments, count);
5214
- const to = normaliseCount(b.segments, count);
6616
+ const { from, to } = normaliseSubpaths(a.subpaths, b.subpaths);
6617
+ const count = from.reduce((total, sub) => total + sub.segments.length, 0);
5215
6618
  return {
5216
6619
  morph: {
5217
6620
  segmentCount: count,
5218
6621
  at(t) {
5219
6622
  const clamped = Math.min(1, Math.max(0, t));
5220
- return toPathData(from.map((segment, i) => lerpCubic(segment, to[i], clamped)));
6623
+ return subpathsToPathData(
6624
+ from.map((sub, i) => ({
6625
+ segments: sub.segments.map(
6626
+ (segment, j) => lerpCubic(segment, to[i].segments[j], clamped)
6627
+ ),
6628
+ closed: sub.closed
6629
+ }))
6630
+ );
5221
6631
  }
5222
6632
  }
5223
6633
  };
@@ -5274,6 +6684,35 @@ var kuinetic = (() => {
5274
6684
  path.setAttribute("d", startPath);
5275
6685
  };
5276
6686
  }
6687
+ var PATH_DRAW_PRIMITIVE = cssPrimitive("path-draw", [CHANNEL.stroke], {
6688
+ parameters: {
6689
+ length: { type: "number", default: "100", cssProperty: "--kui-path-length", finite: true }
6690
+ }
6691
+ });
6692
+ var SHAPE_FILL_PRIMITIVE = cssPrimitive("shape-fill", [CHANNEL.clip]);
6693
+ var BAR_GROW_PRIMITIVE = cssPrimitive("bar-grow", [CHANNEL.scale], {
6694
+ parameters: { from: { type: "number", default: "0", cssProperty: "--kui-bar-from" } }
6695
+ });
6696
+ var LOGO_BUILD_PRIMITIVE = cssPrimitive("logo-assemble", [
6697
+ CHANNEL.opacity,
6698
+ CHANNEL.scale,
6699
+ CHANNEL.rotate
6700
+ ]);
6701
+ var ICON_TOGGLE_PRIMITIVE = {
6702
+ id: "icon-toggle",
6703
+ renderer: "javascript",
6704
+ channels: [CHANNEL.translate, CHANNEL.rotate, CHANNEL.scale, CHANNEL.opacity, CHANNEL.clip],
6705
+ parameters: {
6706
+ duration: { type: "time", default: "260ms", cssProperty: "--kui-duration" },
6707
+ ease: { type: "easing", default: "ease-out", cssProperty: "--kui-ease" }
6708
+ },
6709
+ supportedTimelines: ["time"],
6710
+ supportedActivations: ["load"],
6711
+ defaultActivation: "load",
6712
+ perfClass: "compositor",
6713
+ reducedMotion: "disable",
6714
+ prepare: () => inertInstance()
6715
+ };
5277
6716
  var SVG_PRIMITIVES = [
5278
6717
  {
5279
6718
  id: "path-morph",
@@ -5293,19 +6732,109 @@ var kuinetic = (() => {
5293
6732
  perfClass: "paint",
5294
6733
  reducedMotion: "disable",
5295
6734
  prepare: deferPrepare(prepareMorph)
5296
- }
6735
+ },
6736
+ PATH_DRAW_PRIMITIVE,
6737
+ SHAPE_FILL_PRIMITIVE,
6738
+ BAR_GROW_PRIMITIVE,
6739
+ LOGO_BUILD_PRIMITIVE,
6740
+ ICON_TOGGLE_PRIMITIVE
5297
6741
  ];
5298
6742
  var SVG_PRESETS = [
5299
6743
  { name: "icon-morph", primitive: "path-morph" },
5300
- { name: "blob-morph", primitive: "path-morph", params: { duration: "800ms" } }
6744
+ { name: "blob-morph", primitive: "path-morph", params: { duration: "800ms" } },
6745
+ // Stroke draws. One keyframe block each rather than one shared block, matching the
6746
+ // progress-ring/gauge-sweep/donut-sweep/sparkline-draw group in numbers.ts: identical bodies
6747
+ // today, but each name is free to diverge and a consumer can restyle one without the others.
6748
+ { name: "draw-stroke", primitive: "path-draw", keyframes: "kui-draw-stroke", params: { duration: "800ms", ease: "ease-in-out" } },
6749
+ { name: "draw-signature", primitive: "path-draw", keyframes: "kui-draw-signature", params: { duration: "1600ms", ease: "ease-in-out" } },
6750
+ { name: "draw-underline", primitive: "path-draw", keyframes: "kui-draw-underline", params: { duration: "420ms" } },
6751
+ { name: "checkmark-draw", primitive: "path-draw", keyframes: "kui-checkmark-draw", params: { duration: "320ms" } },
6752
+ { name: "cross-draw", primitive: "path-draw", keyframes: "kui-cross-draw", params: { duration: "260ms" } },
6753
+ { name: "chart-line-draw", primitive: "path-draw", keyframes: "kui-chart-line-draw", params: { duration: "1200ms", ease: "ease-in-out" } },
6754
+ { name: "gradient-stroke", primitive: "path-draw", keyframes: "kui-gradient-stroke", params: { duration: "2400ms", ease: "ease-in-out" } },
6755
+ // Fills.
6756
+ { name: "heart-fill", primitive: "shape-fill", keyframes: "kui-heart-fill", params: { duration: "420ms" } },
6757
+ { name: "bookmark-fill", primitive: "shape-fill", keyframes: "kui-bookmark-fill", params: { duration: "360ms" } },
6758
+ { name: "chart-area-fill", primitive: "shape-fill", keyframes: "kui-chart-area-fill", params: { duration: "900ms" } },
6759
+ // `cloak: true`: `kui-chart-bar-grow`'s `from { scale: 1 0 }` (svg.css) is a zero-height box
6760
+ // while paused, not just an invisible one — so it occupies no space in layout for the whole
6761
+ // wait. Same defect `fold-panel` has, and the same fix shape: see svg.css's
6762
+ // `[data-kui-fx~='chart-bar-grow'][data-kui-state='ready']` rule.
6763
+ {
6764
+ name: "chart-bar-grow",
6765
+ primitive: "bar-grow",
6766
+ keyframes: "kui-chart-bar-grow",
6767
+ params: { duration: "700ms", ease: "back-out" },
6768
+ cloak: true
6769
+ },
6770
+ { name: "logo-build", primitive: "logo-assemble", keyframes: "kui-logo-build", params: { duration: "520ms", ease: "back-out" } },
6771
+ // Icon toggles — no `keyframes`, because their motion is a CSS transition in svg.css keyed off
6772
+ // aria state, not a compiled animation. Same shape as forms.ts's native-state presets.
6773
+ { name: "hamburger-to-x", primitive: "icon-toggle" },
6774
+ { name: "play-to-pause", primitive: "icon-toggle" },
6775
+ { name: "plus-to-minus", primitive: "icon-toggle" }
5301
6776
  ];
5302
6777
  function registerSvg(registry) {
5303
6778
  return registry.registerPrimitives(SVG_PRIMITIVES).registerPresets(SVG_PRESETS);
5304
6779
  }
5305
6780
 
5306
6781
  // src/effects/three-d/index.ts
6782
+ var FLIP_CONTROL_SELECTOR = ":scope > .kui-flip-control";
6783
+ function prepareCardToggle(el, params, ctx) {
6784
+ const trigger = params.text("trigger", "click");
6785
+ if (trigger === "click") return () => {
6786
+ };
6787
+ if (!supportsFineHover(ctx.win)) return () => {
6788
+ };
6789
+ const control = el.querySelector(FLIP_CONTROL_SELECTOR);
6790
+ if (!control) {
6791
+ ctx.warn(`flip-card trigger:${trigger} found no direct-child .kui-flip-control \u2014 the card will not flip`);
6792
+ return () => {
6793
+ };
6794
+ }
6795
+ const set = (flipped) => control.setAttribute("aria-pressed", String(flipped));
6796
+ const isFlipped = () => control.getAttribute("aria-pressed") === "true";
6797
+ const onEnter = () => {
6798
+ set(trigger === "hover-toggle" ? !isFlipped() : true);
6799
+ };
6800
+ const onLeave = () => set(false);
6801
+ el.addEventListener("pointerenter", onEnter, { passive: true });
6802
+ if (trigger === "hover") el.addEventListener("pointerleave", onLeave, { passive: true });
6803
+ return () => {
6804
+ el.removeEventListener("pointerenter", onEnter);
6805
+ el.removeEventListener("pointerleave", onLeave);
6806
+ };
6807
+ }
6808
+ var CARD_TOGGLE_PRIMITIVE = {
6809
+ id: "card-toggle",
6810
+ renderer: "javascript",
6811
+ channels: [CHANNEL.rotate],
6812
+ parameters: {
6813
+ duration: { type: "time", default: "700ms", cssProperty: "--kui-duration" },
6814
+ ease: { type: "easing", default: "ease-in-out", cssProperty: "--kui-ease" },
6815
+ perspective: { type: "length", default: "1600px", cssProperty: "--kui-perspective" },
6816
+ trigger: {
6817
+ type: "keyword",
6818
+ default: "click",
6819
+ cssProperty: "--kui-flip-trigger",
6820
+ values: ["click", "hover", "hover-latch", "hover-toggle"]
6821
+ }
6822
+ },
6823
+ supportedTimelines: ["time"],
6824
+ supportedActivations: ["load"],
6825
+ defaultActivation: "load",
6826
+ perfClass: "compositor",
6827
+ reducedMotion: "disable",
6828
+ prepare: deferPrepare(prepareCardToggle)
6829
+ };
5307
6830
  var THREE_D_PRIMITIVES = [
5308
- cssPrimitive("flip-face", [CHANNEL.rotate], {
6831
+ // `skew`, not `rotate`: the keyframes in three-d.css write `transform: perspective(...)
6832
+ // rotateX/Y(...)`, not the individual `rotate:` property — `perspective` only creates depth for
6833
+ // an element's *children*, so giving one of these effects its own depth means reaching for the
6834
+ // `perspective()` transform *function*, which only exists inside the `transform` shorthand.
6835
+ // `CHANNEL.skew` is this catalog's name for "claims the whole `transform` shorthand"; see the
6836
+ // comment on it in `core/types.ts`.
6837
+ cssPrimitive("flip-face", [CHANNEL.skew], {
5309
6838
  parameters: {
5310
6839
  angle: { type: "angle", default: "180deg", cssProperty: "--kui-from-angle" },
5311
6840
  perspective: { type: "length", default: "1200px", cssProperty: "--kui-perspective" }
@@ -5315,12 +6844,19 @@ var kuinetic = (() => {
5315
6844
  parameters: { distance: { type: "length", default: "40px", cssProperty: "--kui-distance" } }
5316
6845
  }),
5317
6846
  cssPrimitive("wipe", [CHANNEL.clip]),
5318
- cssPrimitive("bar", [CHANNEL.scale])
6847
+ // `from` gives `loading-bar` a real knob on its start scale — it had none before, unlike
6848
+ // `flip-face`'s `angle:` two rows up. `--kui-bar-from` is also what the fold-panel-style
6849
+ // `[data-kui-fx~='loading-bar'][data-kui-state='ready']` gate neutralizes in three-d.css; see
6850
+ // that rule's comment for the on:enter fix this parameter doubles as.
6851
+ cssPrimitive("bar", [CHANNEL.scale], {
6852
+ parameters: { from: { type: "number", default: "0", cssProperty: "--kui-bar-from" } }
6853
+ }),
6854
+ CARD_TOGGLE_PRIMITIVE
5319
6855
  ];
5320
6856
  var THREE_D_PRESETS = [
5321
6857
  // --- 3D & perspective ---
5322
- { name: "card-flip-y", primitive: "flip-face", keyframes: "kui-card-flip-y" },
5323
- { name: "card-flip-x", primitive: "flip-face", keyframes: "kui-card-flip-x" },
6858
+ { name: "card-flip-y", primitive: "flip-face", keyframes: "kui-card-flip-y", cloak: true },
6859
+ { name: "card-flip-x", primitive: "flip-face", keyframes: "kui-card-flip-x", cloak: true },
5324
6860
  { name: "cube-rotate", primitive: "flip-face", keyframes: "kui-cube-rotate", params: { angle: "90deg" } },
5325
6861
  {
5326
6862
  name: "book-page-turn",
@@ -5328,17 +6864,32 @@ var kuinetic = (() => {
5328
6864
  keyframes: "kui-book-page-turn",
5329
6865
  params: { angle: "-160deg", duration: "900ms" }
5330
6866
  },
6867
+ // `cloak: true`, unlike its `flip-face` siblings above: those are `to`-only keyframes, so their
6868
+ // paused/waiting box is the ordinary, untransformed rest state. `fold-panel` is `from`-only —
6869
+ // its `rotateX(-90deg)` (three-d.css) *is* the paused box, edge-on and zero-height, so it holds
6870
+ // no space in layout for the whole wait; see the
6871
+ // `[data-kui-fx~='fold-panel'][data-kui-state='ready']` rule in three-d.css for the other half
6872
+ // of that fix. `cloak` only ever hid the pre-JS flash, not this, but adding it here keeps the
6873
+ // pre-JS and post-JS "ready" appearances the same (invisible) instead of trading one flash for
6874
+ // the other.
5331
6875
  {
5332
6876
  name: "fold-panel",
5333
6877
  primitive: "flip-face",
5334
6878
  keyframes: "kui-fold-panel",
5335
- params: { angle: "-90deg" }
6879
+ params: { angle: "-90deg" },
6880
+ cloak: true
5336
6881
  },
5337
6882
  // --- page transitions ---
5338
6883
  { name: "page-fade", primitive: "page-reveal", keyframes: "kui-page-fade" },
5339
6884
  { name: "page-slide", primitive: "page-reveal", keyframes: "kui-page-slide" },
5340
6885
  { name: "curtain-wipe", primitive: "wipe", keyframes: "kui-curtain-wipe", params: { duration: "800ms" } },
5341
- { name: "loading-bar", primitive: "bar", keyframes: "kui-loading-bar" }
6886
+ // `cloak: true` for the same reason as `fold-panel`: `kui-loading-bar`'s `from { scale: 0 1 }`
6887
+ // (three-d.css) is a zero-width box, not just an invisible one — see that file's
6888
+ // `[data-kui-fx~='loading-bar'][data-kui-state='ready']` rule.
6889
+ { name: "loading-bar", primitive: "bar", keyframes: "kui-loading-bar", cloak: true },
6890
+ // No `keyframes`: its motion is a CSS transition in three-d.css keyed off the control's
6891
+ // aria-pressed, not a compiled animation. Same shape as the icon toggles in svg.ts.
6892
+ { name: "flip-card", primitive: "card-toggle" }
5342
6893
  ];
5343
6894
  function registerThreeD(registry) {
5344
6895
  return registry.registerPrimitives(THREE_D_PRIMITIVES).registerPresets(THREE_D_PRESETS);
@@ -5371,7 +6922,7 @@ var kuinetic = (() => {
5371
6922
  if (!document.getElementById('kuinetic-styles')) {
5372
6923
  var style = document.createElement('style')
5373
6924
  style.id = 'kuinetic-styles'
5374
- style.textContent = "/* src/css/base.css */\n@layer kui.tokens, kui.presets, kui.effects, kui.policy;\n@layer kui.tokens {\n :root {\n --kui-ease-expo-in: cubic-bezier(0.7, 0, 0.84, 0);\n --kui-ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);\n --kui-ease-expo-in-out: cubic-bezier(0.87, 0, 0.13, 1);\n --kui-ease-back-in: cubic-bezier(0.36, 0, 0.66, -0.56);\n --kui-ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);\n --kui-ease-back-in-out: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n --kui-ease-quart-out: cubic-bezier(0.25, 1, 0.5, 1);\n --kui-ease-circ-out: cubic-bezier(0, 0.55, 0.45, 1);\n --kui-ease-spring: linear(0, 0.4 12%, 0.9 25%, 1.06 38%, 1.01 62%, 1);\n }\n [data-kui-fx] {\n --kui-dir: 1;\n }\n :dir(rtl) [data-kui-fx] {\n --kui-dir: -1;\n }\n}\n@layer kui.policy {\n html[data-kui-cloak] [data-kui][data-kui-reveal]:not([data-kui-state]) {\n opacity: 0 !important;\n }\n @media (prefers-reduced-motion: reduce) {\n [data-kui-rm],\n [data-kui-rm]::before,\n [data-kui-rm]::after,\n [data-kui-rm] .kui-odometer-strip,\n [data-kui-rm][data-kui-fx~=label-float] ~ label,\n [data-kui-rm][data-kui-fx~=input-underline-grow] ~ .kui-underline,\n [data-kui-rm][data-kui-fx~=toggle-morph] ~ .kui-track,\n [data-kui-rm][data-kui-fx~=toggle-morph] ~ .kui-track .kui-thumb,\n [data-kui-rm][data-kui-fx~=checkbox-draw] ~ svg path,\n [data-kui-rm][data-kui-fx~=radio-fill] ~ .kui-dot,\n [data-kui-rm][data-kui-fx~=strength-meter] ~ .kui-meter > * {\n transition-duration: 1ms !important;\n transition-delay: 0ms !important;\n }\n [data-kui-rm=shorten],\n [data-kui-rm=shorten]::before,\n [data-kui-rm=shorten]::after {\n animation-duration: 1ms !important;\n animation-delay: 0ms !important;\n }\n [data-kui-rm=crossfade] {\n animation-name: kui-in !important;\n animation-duration: 200ms !important;\n }\n [data-kui-rm=disable] {\n animation: none !important;\n opacity: 1 !important;\n translate: none !important;\n scale: none !important;\n rotate: none !important;\n filter: none !important;\n }\n }\n @media print {\n [data-kui-fx],\n [data-kui-reveal] {\n animation: none !important;\n opacity: 1 !important;\n translate: none !important;\n scale: none !important;\n rotate: none !important;\n filter: none !important;\n }\n }\n}\n\n/* src/css/presets.generated.css */\n@layer kui.presets {\n [data-kui-fx~=back-in-down] {\n --kui-distance: 120px;\n --kui-reveal-ease: expo-out;\n }\n [data-kui-fx~=back-in-up] {\n --kui-distance: 120px;\n --kui-reveal-ease: expo-out;\n }\n [data-kui-fx~=blob-morph] {\n --kui-path-morph-duration: 800ms;\n }\n [data-kui-fx~=bob] {\n --kui-ambient-float-duration: 2s;\n --kui-distance: 8px;\n }\n [data-kui-fx~=book-page-turn] {\n --kui-from-angle: -160deg;\n --kui-flip-face-duration: 900ms;\n }\n [data-kui-fx~=bounce-in] {\n --kui-from-scale: 0.3;\n --kui-scale-ease: back-out;\n }\n [data-kui-fx~=bounce-in-down] {\n --kui-distance: 60px;\n --kui-reveal-ease: back-out;\n }\n [data-kui-fx~=bounce-in-up] {\n --kui-distance: 60px;\n --kui-reveal-ease: back-out;\n }\n [data-kui-fx~=confetti-burst] {\n --kui-feedback-burst-duration: 900ms;\n --kui-pop-scale: 1.15;\n }\n [data-kui-fx~=count-bump] {\n --kui-feedback-pop-duration: 280ms;\n --kui-pop-scale: 1.3;\n }\n [data-kui-fx~=count-compact] {\n --kui-from: 0;\n --kui-to: 128400;\n --kui-format: compact;\n }\n [data-kui-fx~=count-currency] {\n --kui-from: 0;\n --kui-to: 4820;\n --kui-format: currency;\n --kui-decimals: 0;\n }\n [data-kui-fx~=count-down] {\n --kui-from: 100;\n --kui-to: 0;\n }\n [data-kui-fx~=count-percent] {\n --kui-from: 0;\n --kui-to: 0.82;\n --kui-format: percent;\n --kui-decimals: 0;\n }\n [data-kui-fx~=count-up] {\n --kui-from: 0;\n --kui-to: 100;\n }\n [data-kui-fx~=cube-rotate] {\n --kui-from-angle: 90deg;\n }\n [data-kui-fx~=cursor-follow] {\n --kui-stiffness: 300;\n --kui-damping: 30;\n }\n [data-kui-fx~=cursor-invert] {\n --kui-stiffness: 260;\n --kui-damping: 26;\n }\n [data-kui-fx~=cursor-label] {\n --kui-stiffness: 260;\n --kui-damping: 26;\n }\n [data-kui-fx~=cursor-lag] {\n --kui-stiffness: 80;\n --kui-damping: 14;\n }\n [data-kui-fx~=curtain-wipe] {\n --kui-wipe-duration: 800ms;\n }\n [data-kui-fx~=decode] {\n --kui-charset: binary;\n }\n [data-kui-fx~=depth-layer] {\n --kui-distance: 200px;\n }\n [data-kui-fx~=dot-grid-drift] {\n --kui-ambient-gradient-duration: 16s;\n --kui-ambient-gradient-ease: linear;\n }\n [data-kui-fx~=drag-inertia] {\n --kui-inertia: true;\n }\n [data-kui-fx~=drag-x] {\n --kui-axis: x;\n }\n [data-kui-fx~=drag-y] {\n --kui-axis: y;\n }\n [data-kui-fx~=elastic-pull] {\n --kui-return: true;\n --kui-bounds: 80;\n }\n [data-kui-fx~=expand-to-modal] {\n --kui-flip-scale: true;\n --kui-flip-container-duration: 500ms;\n }\n [data-kui-fx~=floating-shapes] {\n --kui-ambient-float-duration: 6s;\n --kui-distance: 10px;\n }\n [data-kui-fx~=fold-panel] {\n --kui-from-angle: -90deg;\n }\n [data-kui-fx~=glitch] {\n --kui-charset: symbols;\n }\n [data-kui-fx~=gradient-rotate-border] {\n --kui-ambient-gradient-duration: 6s;\n --kui-ambient-gradient-ease: linear;\n }\n [data-kui-fx~=grid-to-list] {\n --kui-flip-scale: true;\n }\n [data-kui-fx~=heart-burst] {\n --kui-feedback-burst-duration: 700ms;\n --kui-pop-scale: 1.4;\n }\n [data-kui-fx~=highlight-sweep] {\n --kui-sweep-color: gold;\n }\n [data-kui-fx~=line-grid-drift] {\n --kui-ambient-gradient-duration: 16s;\n --kui-ambient-gradient-ease: linear;\n }\n [data-kui-fx~=magnetic-snap] {\n --kui-strength: 0.6;\n --kui-radius: 160;\n }\n [data-kui-fx~=mega-menu-drop] {\n --kui-panel-reveal-duration: 550ms;\n }\n [data-kui-fx~=noise-overlay] {\n --kui-ambient-gradient-duration: 650ms;\n --kui-ambient-gradient-ease: steps(6);\n }\n [data-kui-fx~=odometer-roll] {\n --kui-from: 0;\n --kui-to: 4820;\n }\n [data-kui-fx~=pin-section] {\n --kui-distance: 100vh;\n --kui-spacer: true;\n }\n [data-kui-fx~=pin-spacer] {\n --kui-spacer: true;\n }\n [data-kui-fx~=pin-until] {\n --kui-spacer: false;\n }\n [data-kui-fx~=pop-in] {\n --kui-from-scale: 0.6;\n --kui-scale-ease: back-out;\n }\n [data-kui-fx~=pop-out] {\n --kui-from-scale: 0.6;\n --kui-scale-ease: back-in;\n }\n [data-kui-fx~=pull-to-refresh] {\n --kui-feedback-pull-duration: 900ms;\n --kui-feedback-pull-ease: ease-out;\n }\n [data-kui-fx~=rotate-in-left] {\n --kui-from-angle: -45deg;\n }\n [data-kui-fx~=rotate-in-right] {\n --kui-from-angle: 45deg;\n }\n [data-kui-fx~=rubber-band] {\n --kui-return: true;\n --kui-bounds: 120;\n }\n [data-kui-fx~=scanline] {\n --kui-ambient-gradient-duration: 3.5s;\n --kui-ambient-gradient-ease: linear;\n }\n [data-kui-fx~=scramble] {\n --kui-charset: upper;\n }\n [data-kui-fx~=scroll-snap-x] {\n --kui-axis: x;\n }\n [data-kui-fx~=scroll-snap-y] {\n --kui-axis: y;\n }\n [data-kui-fx~=scrollytelling-step] {\n --kui-steps: 4;\n }\n [data-kui-fx~=slide-block-end] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-block-start] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-down] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-inline-end] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-inline-start] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-left] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-out-down] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-out-left] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-out-right] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-out-up] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-right] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=slide-up] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n }\n [data-kui-fx~=snap-back] {\n --kui-return: true;\n --kui-stiffness: 260;\n }\n [data-kui-fx~=split-chars] {\n --kui-unit: chars;\n --kui-direction: fade;\n }\n [data-kui-fx~=split-lines] {\n --kui-unit: lines;\n --kui-direction: fade;\n }\n [data-kui-fx~=split-words] {\n --kui-unit: words;\n --kui-direction: fade;\n }\n [data-kui-fx~=spotlight-follow] {\n --kui-ambient-gradient-duration: 9s;\n }\n [data-kui-fx~=stacking-cards] {\n --kui-spacer: false;\n }\n [data-kui-fx~=starfield] {\n --kui-ambient-gradient-duration: 40s;\n --kui-ambient-gradient-ease: linear;\n }\n [data-kui-fx~=swing-in] {\n --kui-from-angle: -15deg;\n --kui-rotate-ease: back-out;\n }\n [data-kui-fx~=swipe-x] {\n --kui-axis: x;\n }\n [data-kui-fx~=tab-indicator-slide] {\n --kui-flip-indicator-duration: 300ms;\n }\n [data-kui-fx~=text-jitter] {\n --kui-motion: jitter;\n }\n [data-kui-fx~=text-reveal-down] {\n --kui-unit: words;\n --kui-direction: down;\n }\n [data-kui-fx~=text-reveal-mask] {\n --kui-unit: lines;\n --kui-direction: mask;\n }\n [data-kui-fx~=text-reveal-up] {\n --kui-unit: words;\n --kui-direction: up;\n }\n [data-kui-fx~=text-wave] {\n --kui-motion: wave;\n }\n [data-kui-fx~=throwable] {\n --kui-inertia: true;\n --kui-damping: 18;\n }\n [data-kui-fx~=toast-slide-out] {\n --kui-feedback-toast-ease: ease-in;\n }\n [data-kui-fx~=typewriter] {\n --kui-loop: false;\n }\n [data-kui-fx~=typewriter-loop] {\n --kui-loop: true;\n }\n [data-kui-fx~=wave-blob] {\n --kui-ambient-gradient-duration: 12s;\n }\n [data-kui-fx~=wobble] {\n --kui-feedback-wobble-duration: 600ms;\n --kui-feedback-wobble-ease: ease-in-out;\n }\n}\n\n/* src/css/entrance.css */\n@layer kui.effects {\n @keyframes kui-in {\n from {\n opacity: var(--kui-from-opacity, 0);\n }\n }\n @keyframes kui-out {\n to {\n opacity: 0;\n }\n }\n @keyframes kui-in-up {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-in-down {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n }\n @keyframes kui-in-left {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: var(--kui-distance, 24px) 0;\n }\n }\n @keyframes kui-in-right {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n }\n @keyframes kui-out-up {\n to {\n opacity: 0;\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n }\n @keyframes kui-out-down {\n to {\n opacity: 0;\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-out-left {\n to {\n opacity: 0;\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n }\n @keyframes kui-out-right {\n to {\n opacity: 0;\n translate: var(--kui-distance, 24px) 0;\n }\n }\n @keyframes kui-in-inline-start {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: calc(var(--kui-distance, 24px) * var(--kui-dir, 1) * -1) 0;\n }\n }\n @keyframes kui-in-inline-end {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: calc(var(--kui-distance, 24px) * var(--kui-dir, 1)) 0;\n }\n }\n @keyframes kui-zoom-in {\n from {\n scale: var(--kui-from-scale, 0.92);\n }\n }\n @keyframes kui-zoom-out {\n to {\n scale: var(--kui-from-scale, 0.92);\n }\n }\n @keyframes kui-zoom-in-up {\n from {\n scale: var(--kui-from-scale, 0.92);\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-zoom-in-down {\n from {\n scale: var(--kui-from-scale, 0.92);\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n }\n @keyframes kui-flip-in-x {\n from {\n rotate: x var(--kui-from-angle, 90deg);\n }\n }\n @keyframes kui-flip-in-y {\n from {\n rotate: y var(--kui-from-angle, 90deg);\n }\n }\n @keyframes kui-flip-out-x {\n to {\n rotate: x var(--kui-from-angle, 90deg);\n }\n }\n @keyframes kui-flip-out-y {\n to {\n rotate: y var(--kui-from-angle, 90deg);\n }\n }\n @keyframes kui-rotate-in {\n from {\n rotate: var(--kui-from-angle, -8deg);\n }\n }\n @keyframes kui-rotate-out {\n to {\n rotate: var(--kui-from-angle, -8deg);\n }\n }\n @keyframes kui-swing-in {\n from {\n rotate: var(--kui-from-angle, -15deg);\n }\n }\n @keyframes kui-roll-in {\n from {\n rotate: var(--kui-from-angle, -120deg);\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n }\n @keyframes kui-roll-out {\n to {\n rotate: var(--kui-from-angle, -120deg);\n translate: var(--kui-distance, 24px) 0;\n }\n }\n @keyframes kui-blur-in {\n from {\n filter: blur(var(--kui-blur, 12px));\n }\n }\n @keyframes kui-blur-out {\n to {\n filter: blur(var(--kui-blur, 12px));\n }\n }\n @keyframes kui-fade-blur-up {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: 0 var(--kui-distance, 24px);\n filter: blur(var(--kui-blur, 12px));\n }\n }\n @keyframes kui-fade-blur-in {\n from {\n opacity: var(--kui-from-opacity, 0);\n filter: blur(var(--kui-blur, 12px));\n }\n }\n [data-kui-fx*=flip-] {\n transform-style: preserve-3d;\n }\n}\n\n/* src/css/scroll.css */\n@layer kui.effects {\n @keyframes kui-parallax-y {\n from {\n translate: 0 calc(var(--kui-distance, 100px) * -1);\n }\n to {\n translate: 0 var(--kui-distance, 100px);\n }\n }\n @keyframes kui-parallax-x {\n from {\n translate: calc(var(--kui-distance, 100px) * -1) 0;\n }\n to {\n translate: var(--kui-distance, 100px) 0;\n }\n }\n @keyframes kui-parallax-scale {\n from {\n scale: 1;\n }\n to {\n scale: var(--kui-to-scale, 1.2);\n }\n }\n @keyframes kui-parallax-rotate {\n from {\n rotate: 0deg;\n }\n to {\n rotate: var(--kui-to-angle, 12deg);\n }\n }\n @keyframes kui-scroll-fade {\n from {\n opacity: var(--kui-from-opacity, 0);\n }\n to {\n opacity: 1;\n }\n }\n @keyframes kui-progress-x {\n from {\n scale: 0 1;\n }\n to {\n scale: 1 1;\n }\n }\n @keyframes kui-progress-ring {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=scroll-progress-bar] {\n transform-origin: left center;\n }\n [data-kui-fx~=scroll-progress-ring] {\n stroke-dasharray: var(--kui-path-length, 100);\n }\n @supports (animation-timeline: view()) {\n [data-kui-timeline] {\n animation-range: var(--kui-range, entry 0% cover 60%);\n }\n }\n}\n\n/* src/css/three-d.css */\n@layer kui.effects {\n @keyframes kui-card-flip-y {\n to {\n rotate: y var(--kui-from-angle, 180deg);\n }\n }\n @keyframes kui-card-flip-x {\n to {\n rotate: x var(--kui-from-angle, 180deg);\n }\n }\n @keyframes kui-cube-rotate {\n to {\n rotate: y var(--kui-from-angle, 90deg);\n }\n }\n @keyframes kui-book-page-turn {\n to {\n rotate: y var(--kui-from-angle, -160deg);\n }\n }\n @keyframes kui-fold-panel {\n from {\n rotate: x var(--kui-from-angle, -90deg);\n }\n }\n [data-kui-fx~=card-flip-x],\n [data-kui-fx~=card-flip-y],\n [data-kui-fx~=cube-rotate],\n [data-kui-fx~=book-page-turn],\n [data-kui-fx~=fold-panel] {\n transform-style: preserve-3d;\n perspective: var(--kui-perspective, 1200px);\n }\n [data-kui-fx~=book-page-turn] {\n transform-origin: left center;\n }\n [data-kui-fx~=fold-panel] {\n transform-origin: top center;\n }\n @keyframes kui-page-fade {\n from {\n opacity: 0;\n }\n }\n @keyframes kui-page-slide {\n from {\n opacity: 0;\n translate: 0 var(--kui-distance, 40px);\n }\n }\n @keyframes kui-curtain-wipe {\n from {\n clip-path: inset(0 0 100% 0);\n }\n }\n @keyframes kui-loading-bar {\n from {\n scale: 0 1;\n }\n }\n [data-kui-fx~=loading-bar] {\n transform-origin: left center;\n }\n}\n\n/* src/css/media.css */\n@layer kui.effects {\n @keyframes kui-wipe-up {\n from {\n clip-path: inset(100% 0 0);\n }\n }\n @keyframes kui-wipe-down {\n from {\n clip-path: inset(0 0 100%);\n }\n }\n @keyframes kui-wipe-left {\n from {\n clip-path: inset(0 100% 0 0);\n }\n }\n @keyframes kui-wipe-right {\n from {\n clip-path: inset(0 0 0 100%);\n }\n }\n @keyframes kui-wipe-circle {\n from {\n clip-path: circle(0 at 50% 50%);\n }\n }\n @keyframes kui-wipe-diagonal {\n from {\n clip-path: polygon(0 0, 0 0, 0 0);\n }\n to {\n clip-path: polygon(0 0, 200% 0, 0 200%);\n }\n }\n @keyframes kui-curtain-reveal {\n from {\n clip-path: inset(0 50%);\n }\n }\n @keyframes kui-before-after-wipe {\n from {\n clip-path: inset(0 100% 0 0);\n }\n }\n @keyframes kui-mask-reveal {\n from {\n mask-position: 100% 0;\n }\n to {\n mask-position: 0 0;\n }\n }\n [data-kui-fx~=mask-reveal] {\n mask-image:\n linear-gradient(\n 90deg,\n #000 0 45%,\n transparent 55% 100%);\n mask-size: 220% 100%;\n }\n @keyframes kui-ken-burns {\n from {\n scale: 1;\n translate: 0 0;\n }\n to {\n scale: var(--kui-to-scale, 1.12);\n translate: var(--kui-distance, 24px) calc(var(--kui-distance, 24px) * -0.5);\n }\n }\n @keyframes kui-ken-burns-out {\n from {\n scale: var(--kui-to-scale, 1.12);\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n to {\n scale: 1;\n translate: 0 0;\n }\n }\n @keyframes kui-blur-up {\n from {\n translate: 0 var(--kui-distance, 24px);\n filter: blur(var(--kui-blur, 16px));\n }\n to {\n translate: 0 0;\n filter: blur(0);\n }\n }\n @keyframes kui-duotone-hover {\n from {\n filter: grayscale(1) sepia(0.7) hue-rotate(330deg) saturate(2);\n }\n to {\n filter: none;\n }\n }\n @keyframes kui-grayscale-hover {\n from {\n filter: grayscale(1);\n }\n to {\n filter: grayscale(0);\n }\n }\n @keyframes kui-saturate-hover {\n from {\n filter: saturate(0.45);\n }\n to {\n filter: saturate(1.35);\n }\n }\n @keyframes kui-image-parallax-frame {\n from {\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n to {\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-lightbox-open {\n from {\n opacity: 0;\n scale: 0.92;\n }\n to {\n opacity: 1;\n scale: 1;\n }\n }\n @media (pointer: coarse) {\n [data-kui-fx~=duotone-hover]:active,\n [data-kui-fx~=grayscale-hover]:active,\n [data-kui-fx~=saturate-hover]:active {\n filter: none;\n }\n }\n}\n\n/* src/css/text.css */\n@layer kui.effects {\n .kui-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n .kui-split-decorative {\n display: inline;\n }\n .kui-split-item {\n display: inline-block;\n animation-duration: var(--kui-duration, 500ms);\n animation-delay: calc(var(--kui-delay, 0ms) + var(--kui-i, 0) * var(--kui-stagger, 30ms));\n animation-timing-function: var(--kui-ease, ease-out);\n animation-fill-mode: both;\n }\n .kui-split-line {\n display: block;\n }\n [data-kui-split-fx=fade] .kui-split-item {\n animation-name: kui-split-reveal-fade;\n }\n [data-kui-split-fx=up] .kui-split-item {\n animation-name: kui-split-reveal-up;\n }\n [data-kui-split-fx=down] .kui-split-item {\n animation-name: kui-split-reveal-down;\n }\n [data-kui-split-fx=mask] .kui-split-item {\n animation-name: kui-split-reveal-mask;\n }\n @keyframes kui-split-reveal-fade {\n from {\n opacity: 0;\n }\n }\n @keyframes kui-split-reveal-up {\n from {\n opacity: 0;\n translate: 0 0.6em;\n }\n }\n @keyframes kui-split-reveal-down {\n from {\n opacity: 0;\n translate: 0 -0.6em;\n }\n }\n @keyframes kui-split-reveal-mask {\n from {\n opacity: 0;\n clip-path: inset(0 0 100% 0);\n }\n }\n [data-kui-split-fx=wave] .kui-split-item {\n animation-name: kui-split-wave;\n animation-duration: 1400ms;\n animation-iteration-count: infinite;\n animation-timing-function: ease-in-out;\n }\n [data-kui-split-fx=jitter] .kui-split-item {\n animation-name: kui-split-jitter;\n animation-duration: 220ms;\n animation-iteration-count: infinite;\n animation-timing-function: ease-in-out;\n animation-direction: alternate;\n }\n @keyframes kui-split-wave {\n 0%, 100% {\n translate: 0 0;\n }\n 50% {\n translate: 0 -0.35em;\n }\n }\n @keyframes kui-split-jitter {\n from {\n translate: 0 0;\n rotate: 0deg;\n }\n to {\n translate: 0 0.08em;\n rotate: 3deg;\n }\n }\n .kui-typewriter::after {\n content: \"\";\n display: inline-block;\n width: 0.08em;\n margin-inline-start: 0.05em;\n height: 1em;\n vertical-align: -0.15em;\n background: currentColor;\n animation: kui-caret-blink 1s steps(1) infinite;\n }\n @keyframes kui-caret-blink {\n 50% {\n opacity: 0;\n }\n }\n .kui-scramble {\n font-variant-numeric: tabular-nums;\n }\n [data-kui-fx~=word-cycler] {\n display: inline-block;\n transition: opacity 150ms ease-out;\n }\n [data-kui-fx~=word-cycler].kui-word-cycler-swap {\n opacity: 0;\n }\n [data-kui-fx~=gradient-shimmer] {\n background-image:\n linear-gradient(\n 100deg,\n currentColor 40%,\n color-mix(in srgb, currentColor 35%, transparent) 50%,\n currentColor 60%);\n background-size: 220% 100%;\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n --kui-fx-gradient-shimmer-iterations: infinite;\n }\n @keyframes kui-gradient-shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n [data-kui-fx~=gradient-sweep] {\n background-image:\n linear-gradient(\n 100deg,\n transparent 30%,\n color-mix(in srgb, var(--kui-sweep-color, currentColor) 70%, transparent) 50%,\n transparent 70%);\n background-size: 260% 100%;\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n }\n @keyframes kui-gradient-sweep {\n from {\n background-position: 220% 0;\n }\n to {\n background-position: -60% 0;\n }\n }\n [data-kui-fx~=highlight-sweep] {\n background-image: linear-gradient(90deg, color-mix(in srgb, var(--kui-sweep-color, currentColor) 55%, transparent) 0 100%);\n background-repeat: no-repeat;\n background-size: 0% 100%;\n padding: 0 0.05em;\n }\n @keyframes kui-highlight-sweep {\n to {\n background-size: 100% 100%;\n }\n }\n [data-kui-fx~=underline-draw] {\n background-image: linear-gradient(var(--kui-sweep-color, currentColor), var(--kui-sweep-color, currentColor));\n background-repeat: no-repeat;\n background-position: 0 100%;\n background-size: 0% 2px;\n padding-bottom: 0.15em;\n }\n @keyframes kui-underline-draw {\n to {\n background-size: 100% 2px;\n }\n }\n [data-kui-fx~=text-outline-fill] {\n -webkit-text-stroke: 1px currentColor;\n color: transparent;\n }\n @keyframes kui-text-outline-fill {\n to {\n color: currentColor;\n }\n }\n @keyframes kui-var-weight {\n from {\n font-weight: var(--kui-from-weight, 100);\n }\n to {\n font-weight: var(--kui-to-weight, 800);\n }\n }\n @keyframes kui-var-width {\n from {\n font-stretch: var(--kui-from-width, 75%);\n }\n to {\n font-stretch: var(--kui-to-width, 125%);\n }\n }\n @keyframes kui-var-slant {\n from {\n font-style: oblique var(--kui-from-slant, 0deg);\n }\n to {\n font-style: oblique var(--kui-to-slant, 10deg);\n }\n }\n [data-kui-fx~=marquee],\n [data-kui-fx~=marquee-scroll-linked] {\n display: flex;\n width: max-content;\n will-change: translate;\n }\n [data-kui-fx~=marquee] {\n --kui-fx-marquee-iterations: infinite;\n }\n @keyframes kui-marquee {\n to {\n translate: -50% 0;\n }\n }\n @property --kui-redaction-x { syntax: \"<percentage>\"; inherits: true; initial-value: 0%; }\n [data-kui-fx~=redaction-reveal] {\n position: relative;\n display: inline-block;\n }\n [data-kui-fx~=redaction-reveal]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background: var(--kui-bar-color, #111);\n clip-path: inset(0 0 0 var(--kui-redaction-x));\n }\n @keyframes kui-redaction-reveal {\n to {\n --kui-redaction-x: 100%;\n }\n }\n [data-kui-fx~=text-3d-extrude] {\n display: inline-block;\n text-shadow:\n 1px 1px 0 color-mix(in srgb, currentColor 70%, transparent),\n 2px 2px 0 color-mix(in srgb, currentColor 60%, transparent),\n 3px 3px 0 color-mix(in srgb, currentColor 50%, transparent),\n 4px 4px 0 color-mix(in srgb, currentColor 40%, transparent),\n 5px 5px 0 color-mix(in srgb, currentColor 30%, transparent),\n 6px 6px 8px rgb(0 0 0 / 0.35);\n }\n @keyframes kui-text-3d-extrude {\n from {\n rotate: var(--kui-from-angle, -20deg);\n translate: 0 var(--kui-distance, 32px);\n }\n to {\n rotate: 0deg;\n translate: 0 0;\n }\n }\n}\n\n/* src/css/navigation.css */\n@layer kui.effects {\n @keyframes kui-nav-reveal {\n from {\n opacity: 0;\n translate: 0 var(--kui-distance, 16px);\n }\n }\n @keyframes kui-menu-fullscreen {\n from {\n opacity: 0;\n clip-path: circle(0% at var(--kui-origin, 100% 0%));\n }\n to {\n opacity: 1;\n clip-path: circle(150% at var(--kui-origin, 100% 0%));\n }\n }\n @keyframes kui-panel-reveal {\n from {\n opacity: 0;\n translate: 0 calc(var(--kui-distance, 10px) * -1);\n }\n }\n @keyframes kui-drawer-slide-right {\n from {\n translate: 100% 0;\n }\n }\n [data-kui-fx~=header-shrink] {\n --kui-shrink: 0;\n padding-block: calc(1.4rem - 0.7rem * var(--kui-shrink, 0));\n font-size: calc(1rem - 0.15rem * var(--kui-shrink, 0));\n transition:\n padding-block 200ms ease-out,\n font-size 200ms ease-out,\n box-shadow 200ms ease-out;\n }\n [data-kui-fx~=header-shrink][data-kui-shrunk=true] {\n box-shadow: 0 2px 10px rgb(0 0 0 / 0.12);\n }\n [data-kui-fx~=header-hide-on-scroll] {\n transition: translate 220ms ease-out;\n }\n [data-kui-fx~=header-hide-on-scroll][data-kui-hidden=true] {\n translate: 0 -100%;\n }\n [data-kui-fx~=back-to-top-fade] {\n opacity: 0;\n translate: 0 12px;\n pointer-events: none;\n transition: opacity 200ms ease-out, translate 200ms ease-out;\n }\n [data-kui-fx~=back-to-top-fade][data-kui-visible=true] {\n opacity: 1;\n translate: 0 0;\n pointer-events: auto;\n }\n}\n\n/* src/css/forms.css */\n@layer kui.effects {\n [data-kui-fx~=label-float] ~ label {\n transition: translate 180ms ease-out, scale 180ms ease-out;\n transform-origin: left center;\n }\n [data-kui-fx~=label-float]:focus ~ label,\n [data-kui-fx~=label-float]:not(:placeholder-shown) ~ label {\n translate: 0 -1.35rem;\n scale: 0.82;\n }\n [data-kui-fx~=input-underline-grow] ~ .kui-underline {\n display: block;\n height: 2px;\n background: currentColor;\n scale: 0 1;\n transform-origin: left center;\n transition: scale 220ms ease-out;\n }\n [data-kui-fx~=input-underline-grow]:focus ~ .kui-underline {\n scale: 1 1;\n }\n @keyframes kui-focus-ring-grow {\n from {\n box-shadow: 0 0 0 0 var(--kui-ring-color, rgb(210 105 30 / 0.55));\n }\n to {\n box-shadow: 0 0 0 5px var(--kui-ring-color, rgb(210 105 30 / 0.55));\n }\n }\n @keyframes kui-validate-shake {\n 10%, 90% {\n translate: -1px 0;\n }\n 20%, 80% {\n translate: 2px 0;\n }\n 30%, 50%, 70% {\n translate: -4px 0;\n }\n 40%, 60% {\n translate: 4px 0;\n }\n }\n [data-kui-fx~=validate-check] {\n stroke-dasharray: 24;\n stroke-dashoffset: 24;\n }\n @keyframes kui-validate-check {\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=strength-meter] ~ .kui-meter > * {\n background: var(--kui-meter-off, currentColor);\n opacity: 0.2;\n transition: opacity 200ms ease-out, background-color 200ms ease-out;\n }\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"1\"] ~ .kui-meter > *:nth-child(-n+1),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"2\"] ~ .kui-meter > *:nth-child(-n+2),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"3\"] ~ .kui-meter > *:nth-child(-n+3),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"4\"] ~ .kui-meter > *:nth-child(-n+4),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"5\"] ~ .kui-meter > *:nth-child(-n+5),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"6\"] ~ .kui-meter > *:nth-child(-n+6),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"7\"] ~ .kui-meter > *:nth-child(-n+7),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"8\"] ~ .kui-meter > *:nth-child(-n+8),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"9\"] ~ .kui-meter > *:nth-child(-n+9),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"10\"] ~ .kui-meter > *:nth-child(-n+10),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"11\"] ~ .kui-meter > *:nth-child(-n+11),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"12\"] ~ .kui-meter > *:nth-child(-n+12),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"13\"] ~ .kui-meter > *:nth-child(-n+13),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"14\"] ~ .kui-meter > *:nth-child(-n+14),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"15\"] ~ .kui-meter > *:nth-child(-n+15),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"16\"] ~ .kui-meter > *:nth-child(-n+16),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"17\"] ~ .kui-meter > *:nth-child(-n+17),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"18\"] ~ .kui-meter > *:nth-child(-n+18),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"19\"] ~ .kui-meter > *:nth-child(-n+19),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"20\"] ~ .kui-meter > *:nth-child(-n+20) {\n opacity: 1;\n background: var(--kui-meter-on, #2e9e5b);\n }\n [data-kui-fx~=toggle-morph] ~ .kui-track {\n display: inline-block;\n width: calc(40px * var(--kui-toggle-scale, 1));\n height: calc(22px * var(--kui-toggle-scale, 1));\n border-radius: 999px;\n background: var(--dim, #9a8d80);\n transition: background-color 200ms ease-out;\n position: relative;\n }\n [data-kui-fx~=toggle-morph] ~ .kui-track .kui-thumb {\n position: absolute;\n top: calc(2px * var(--kui-toggle-scale, 1));\n left: calc(2px * var(--kui-toggle-scale, 1));\n width: calc(18px * var(--kui-toggle-scale, 1));\n height: calc(18px * var(--kui-toggle-scale, 1));\n border-radius: 50%;\n background: var(--kui-thumb-color, #fff);\n translate: 0 0;\n transition: translate 200ms ease-out;\n }\n [data-kui-fx~=toggle-morph]:checked ~ .kui-track {\n background: var(--accent, #d2691e);\n }\n [data-kui-fx~=toggle-morph]:checked ~ .kui-track .kui-thumb {\n translate: calc(18px * var(--kui-toggle-scale, 1)) 0;\n }\n [data-kui-fx~=checkbox-draw] ~ svg path {\n stroke-dasharray: 24;\n stroke-dashoffset: 24;\n transition: stroke-dashoffset 220ms ease-out;\n }\n [data-kui-fx~=checkbox-draw]:checked ~ svg path {\n stroke-dashoffset: 0;\n }\n [data-kui-fx~=radio-fill] ~ .kui-dot {\n display: inline-block;\n width: calc(10px * var(--kui-radio-scale, 1));\n height: calc(10px * var(--kui-radio-scale, 1));\n border-radius: 50%;\n background: var(--accent, #d2691e);\n scale: 0;\n transition: scale 180ms ease-out;\n }\n [data-kui-fx~=radio-fill]:checked ~ .kui-dot {\n scale: 1;\n }\n [data-kui-fx~=range-fill] {\n --kui-fill: 0%;\n accent-color: var(--accent, #d2691e);\n background:\n linear-gradient(\n to right,\n var(--accent, #d2691e) var(--kui-fill, 0%),\n var(--line, #2a2422) var(--kui-fill, 0%));\n height: 4px;\n border-radius: 999px;\n appearance: none;\n outline: none;\n }\n [data-kui-fx~=submit-to-spinner-to-check] .kui-stage-idle,\n [data-kui-fx~=submit-to-spinner-to-check] .kui-stage-loading,\n [data-kui-fx~=submit-to-spinner-to-check] .kui-stage-done {\n display: none;\n }\n [data-kui-fx~=submit-to-spinner-to-check][data-kui-stage=idle] .kui-stage-idle,\n [data-kui-fx~=submit-to-spinner-to-check][data-kui-stage=loading] .kui-stage-loading,\n [data-kui-fx~=submit-to-spinner-to-check][data-kui-stage=done] .kui-stage-done {\n display: inline-flex;\n }\n [data-kui-fx~=submit-to-spinner-to-check] .kui-spinner {\n width: 1em;\n height: 1em;\n border-radius: 50%;\n border: 2px solid currentColor;\n border-top-color: transparent;\n animation: kui-spin 700ms linear infinite;\n }\n @keyframes kui-spin {\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=step-progress] {\n cursor: pointer;\n }\n [data-kui-fx~=step-progress] > * {\n background: var(--dim, #9a8d80);\n opacity: 0.35;\n transition: opacity 200ms ease-out, background-color 200ms ease-out;\n }\n [data-kui-fx~=step-progress][data-kui-step=\"0\"] > *:nth-child(-n+1),\n [data-kui-fx~=step-progress][data-kui-step=\"1\"] > *:nth-child(-n+2),\n [data-kui-fx~=step-progress][data-kui-step=\"2\"] > *:nth-child(-n+3),\n [data-kui-fx~=step-progress][data-kui-step=\"3\"] > *:nth-child(-n+4),\n [data-kui-fx~=step-progress][data-kui-step=\"4\"] > *:nth-child(-n+5),\n [data-kui-fx~=step-progress][data-kui-step=\"5\"] > *:nth-child(-n+6),\n [data-kui-fx~=step-progress][data-kui-step=\"6\"] > *:nth-child(-n+7),\n [data-kui-fx~=step-progress][data-kui-step=\"7\"] > *:nth-child(-n+8),\n [data-kui-fx~=step-progress][data-kui-step=\"8\"] > *:nth-child(-n+9),\n [data-kui-fx~=step-progress][data-kui-step=\"9\"] > *:nth-child(-n+10),\n [data-kui-fx~=step-progress][data-kui-step=\"10\"] > *:nth-child(-n+11),\n [data-kui-fx~=step-progress][data-kui-step=\"11\"] > *:nth-child(-n+12),\n [data-kui-fx~=step-progress][data-kui-step=\"12\"] > *:nth-child(-n+13),\n [data-kui-fx~=step-progress][data-kui-step=\"13\"] > *:nth-child(-n+14),\n [data-kui-fx~=step-progress][data-kui-step=\"14\"] > *:nth-child(-n+15),\n [data-kui-fx~=step-progress][data-kui-step=\"15\"] > *:nth-child(-n+16),\n [data-kui-fx~=step-progress][data-kui-step=\"16\"] > *:nth-child(-n+17),\n [data-kui-fx~=step-progress][data-kui-step=\"17\"] > *:nth-child(-n+18),\n [data-kui-fx~=step-progress][data-kui-step=\"18\"] > *:nth-child(-n+19),\n [data-kui-fx~=step-progress][data-kui-step=\"19\"] > *:nth-child(-n+20) {\n opacity: 1;\n background: var(--accent, #d2691e);\n }\n}\n\n/* src/css/ambient.css */\n@layer kui.effects {\n [data-kui-fx~=gradient-mesh] {\n background-image:\n radial-gradient(\n at 15% 20%,\n color-mix(in srgb, var(--kui-c1, #7c3aed) 55%, transparent),\n transparent 55%),\n radial-gradient(\n at 85% 15%,\n color-mix(in srgb, var(--kui-c2, #06b6d4) 50%, transparent),\n transparent 55%),\n radial-gradient(\n at 50% 85%,\n color-mix(in srgb, var(--kui-c3, #f97316) 45%, transparent),\n transparent 55%);\n background-size: 200% 200%;\n --kui-fx-gradient-mesh-iterations: infinite;\n }\n @keyframes kui-gradient-mesh {\n 0%, 100% {\n background-position:\n 0% 0%,\n 100% 0%,\n 50% 100%;\n }\n 50% {\n background-position:\n 25% 35%,\n 75% 30%,\n 35% 70%;\n }\n }\n [data-kui-fx~=aurora] {\n background-image:\n linear-gradient(\n 120deg,\n color-mix(in srgb, var(--kui-c1, #34d399) 45%, transparent),\n transparent 45%),\n linear-gradient(\n 240deg,\n color-mix(in srgb, var(--kui-c2, #60a5fa) 45%, transparent),\n transparent 45%),\n linear-gradient(\n 0deg,\n color-mix(in srgb, var(--kui-c3, #c084fc) 40%, transparent),\n transparent 50%);\n background-size: 260% 260%;\n --kui-fx-aurora-iterations: infinite;\n }\n @keyframes kui-aurora {\n 0%, 100% {\n background-position:\n 0% 50%,\n 100% 50%,\n 50% 0%;\n }\n 50% {\n background-position:\n 60% 30%,\n 40% 70%,\n 55% 60%;\n }\n }\n [data-kui-fx~=wave-blob] {\n background-image:\n radial-gradient(\n circle,\n color-mix(in srgb, var(--kui-c1, #22d3ee) 50%, transparent),\n transparent 60%);\n background-size: 140% 140%;\n background-repeat: no-repeat;\n --kui-fx-wave-blob-iterations: infinite;\n }\n @keyframes kui-wave-blob {\n 0%, 100% {\n background-position: 20% 30%;\n background-size: 140% 140%;\n }\n 33% {\n background-position: 70% 20%;\n background-size: 160% 160%;\n }\n 66% {\n background-position: 50% 75%;\n background-size: 120% 120%;\n }\n }\n [data-kui-fx~=spotlight-follow] {\n background-image:\n radial-gradient(\n circle,\n color-mix(in srgb, var(--kui-c1, #fde68a) 65%, transparent),\n transparent 65%);\n background-size: 55% 55%;\n background-repeat: no-repeat;\n --kui-fx-spotlight-follow-iterations: infinite;\n }\n @keyframes kui-spotlight-follow {\n 0%, 100% {\n background-position: 10% 15%;\n }\n 25% {\n background-position: 85% 20%;\n }\n 50% {\n background-position: 80% 80%;\n }\n 75% {\n background-position: 15% 75%;\n }\n }\n [data-kui-fx~=gradient-rotate-border] {\n position: relative;\n background-image:\n linear-gradient(\n 90deg,\n var(--kui-c1, #f472b6),\n var(--kui-c2, #60a5fa),\n var(--kui-c3, #34d399),\n var(--kui-c4, #fbbf24),\n var(--kui-c1, #f472b6));\n background-size: 300% 100%;\n padding: 3px;\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask-composite: xor;\n --kui-fx-gradient-rotate-border-iterations: infinite;\n }\n @keyframes kui-gradient-rotate-border {\n from {\n background-position: 0% 50%;\n }\n to {\n background-position: 300% 50%;\n }\n }\n [data-kui-fx~=noise-overlay] {\n background-image:\n repeating-radial-gradient(\n circle at 0 0,\n color-mix(in srgb, var(--kui-c1, currentColor) 30%, transparent) 0,\n transparent 1.4px,\n transparent 3px);\n background-size: 5px 5px;\n --kui-fx-noise-overlay-iterations: infinite;\n }\n @keyframes kui-noise-overlay {\n 0% {\n background-position: 0 0;\n }\n 20% {\n background-position: -3px 1px;\n }\n 40% {\n background-position: 2px -2px;\n }\n 60% {\n background-position: -1px 3px;\n }\n 80% {\n background-position: 3px 2px;\n }\n 100% {\n background-position: 0 0;\n }\n }\n [data-kui-fx~=scanline] {\n background-image:\n linear-gradient(\n to bottom,\n transparent,\n color-mix(in srgb, var(--kui-c1, currentColor) 35%, transparent) 50%,\n transparent);\n background-size: 100% 220%;\n background-repeat: no-repeat;\n --kui-fx-scanline-iterations: infinite;\n }\n @keyframes kui-scanline {\n from {\n background-position: 0 -120%;\n }\n to {\n background-position: 0 120%;\n }\n }\n [data-kui-fx~=dot-grid-drift] {\n background-image:\n radial-gradient(\n circle,\n color-mix(in srgb, var(--kui-c1, currentColor) 45%, transparent) 1.5px,\n transparent 1.5px);\n background-size: 22px 22px;\n --kui-fx-dot-grid-drift-iterations: infinite;\n }\n @keyframes kui-dot-grid-drift {\n from {\n background-position: 0 0;\n }\n to {\n background-position: 22px 22px;\n }\n }\n [data-kui-fx~=line-grid-drift] {\n background-image:\n repeating-linear-gradient(\n 0deg,\n color-mix(in srgb, var(--kui-c1, currentColor) 30%, transparent) 0 1px,\n transparent 1px 26px),\n repeating-linear-gradient(\n 90deg,\n color-mix(in srgb, var(--kui-c1, currentColor) 30%, transparent) 0 1px,\n transparent 1px 26px);\n --kui-fx-line-grid-drift-iterations: infinite;\n }\n @keyframes kui-line-grid-drift {\n from {\n background-position: 0 0, 0 0;\n }\n to {\n background-position: 0 26px, 26px 0;\n }\n }\n [data-kui-fx~=starfield] {\n background-image:\n radial-gradient(\n 1px 1px at 10% 20%,\n color-mix(in srgb, var(--kui-c1, #ffffff) 90%, transparent),\n transparent),\n radial-gradient(\n 1px 1px at 70% 60%,\n color-mix(in srgb, var(--kui-c1, #ffffff) 70%, transparent),\n transparent),\n radial-gradient(\n 1.5px 1.5px at 40% 80%,\n color-mix(in srgb, var(--kui-c1, #ffffff) 85%, transparent),\n transparent),\n radial-gradient(\n 1px 1px at 90% 30%,\n color-mix(in srgb, var(--kui-c1, #ffffff) 60%, transparent),\n transparent),\n radial-gradient(\n 1.5px 1.5px at 25% 55%,\n color-mix(in srgb, var(--kui-c1, #ffffff) 80%, transparent),\n transparent);\n background-size: 220px 220px;\n background-repeat: repeat;\n --kui-fx-starfield-iterations: infinite;\n }\n @keyframes kui-starfield {\n from {\n background-position:\n 0 0,\n 0 0,\n 0 0,\n 0 0,\n 0 0;\n }\n to {\n background-position:\n 220px 90px,\n -160px 220px,\n 90px -140px,\n -220px -90px,\n 140px 160px;\n }\n }\n [data-kui-fx~=float] {\n --kui-fx-float-iterations: infinite;\n }\n @keyframes kui-float {\n 0%, 100% {\n translate: 0 0;\n }\n 50% {\n translate: 0 calc(var(--kui-distance, 14px) * -1);\n }\n }\n [data-kui-fx~=bob] {\n --kui-fx-bob-iterations: infinite;\n }\n @keyframes kui-bob {\n 0%, 100% {\n translate: 0 0;\n }\n 50% {\n translate: 0 calc(var(--kui-distance, 8px) * -1);\n }\n }\n [data-kui-fx~=floating-shapes] {\n --kui-fx-floating-shapes-iterations: infinite;\n }\n @keyframes kui-floating-shapes {\n 0%, 100% {\n translate: 0 0;\n }\n 33% {\n translate: calc(var(--kui-distance, 10px) * 0.6) calc(var(--kui-distance, 10px) * -1);\n }\n 66% {\n translate: calc(var(--kui-distance, 10px) * -0.6) calc(var(--kui-distance, 10px) * -0.4);\n }\n }\n [data-kui-fx~=glow-pulse] {\n --kui-fx-glow-pulse-iterations: infinite;\n }\n @keyframes kui-glow-pulse {\n 0%, 100% {\n scale: 1;\n opacity: 0.6;\n }\n 50% {\n scale: var(--kui-pulse-scale, 1.15);\n opacity: 1;\n }\n }\n}\n\n/* src/css/feedback.css */\n@layer kui.effects {\n [data-kui-fx~=skeleton-shimmer] {\n background-color: var(--kui-skeleton-base, #2a2422);\n background-image:\n linear-gradient(\n 100deg,\n transparent 30%,\n color-mix(in srgb, var(--kui-skeleton-highlight, #ffffff) 18%, transparent) 50%,\n transparent 70%);\n background-size: 200% 100%;\n --kui-fx-skeleton-shimmer-iterations: infinite;\n }\n @keyframes kui-skeleton-shimmer {\n from {\n background-position: 150% 0;\n }\n to {\n background-position: -50% 0;\n }\n }\n @keyframes kui-skeleton-to-content {\n from {\n opacity: 0;\n }\n }\n [data-kui-fx~=spinner] {\n display: inline-block;\n width: var(--kui-spinner-size, 28px);\n height: var(--kui-spinner-size, 28px);\n border-radius: 50%;\n border: 3px solid color-mix(in srgb, currentColor 20%, transparent);\n border-top-color: currentColor;\n --kui-fx-spinner-iterations: infinite;\n }\n @keyframes kui-spinner-spin {\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=spinner-ring] {\n display: inline-block;\n width: var(--kui-spinner-size, 28px);\n height: var(--kui-spinner-size, 28px);\n border-radius: 50%;\n border: 3px dashed color-mix(in srgb, currentColor 55%, transparent);\n --kui-fx-spinner-ring-iterations: infinite;\n }\n @keyframes kui-spinner-ring-spin {\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=spinner-dots] {\n display: inline-block;\n width: var(--kui-dot-size, 8px);\n height: var(--kui-dot-size, 8px);\n border-radius: 50%;\n background: currentColor;\n box-shadow: calc(var(--kui-dot-size, 8px) * -2) 0 currentColor, calc(var(--kui-dot-size, 8px) * 2) 0 currentColor;\n --kui-fx-spinner-dots-iterations: infinite;\n }\n @keyframes kui-spinner-dots {\n 0%, 80%, 100% {\n scale: 0.7;\n opacity: 0.5;\n }\n 40% {\n scale: 1.15;\n opacity: 1;\n }\n }\n [data-kui-fx~=progress-indeterminate] {\n transform-origin: 0% 50%;\n background: currentColor;\n border-radius: inherit;\n --kui-fx-progress-indeterminate-iterations: infinite;\n }\n @keyframes kui-progress-indeterminate {\n 0% {\n translate: -100% 0;\n scale: 0.4 1;\n }\n 50% {\n scale: 1 1;\n }\n 100% {\n translate: 100% 0;\n scale: 0.4 1;\n }\n }\n @keyframes kui-toast-slide-in {\n from {\n opacity: 0;\n translate: 0 var(--kui-distance, 24px);\n }\n to {\n opacity: 1;\n translate: 0 0;\n }\n }\n @keyframes kui-toast-slide-out {\n from {\n opacity: 1;\n translate: 0 0;\n }\n to {\n opacity: 0;\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-shake-error {\n 10%, 90% {\n translate: -1px 0;\n }\n 20%, 80% {\n translate: 2px 0;\n }\n 30%, 50%, 70% {\n translate: -4px 0;\n }\n 40%, 60% {\n translate: 4px 0;\n }\n }\n @keyframes kui-wobble {\n 0% {\n translate: 0 0;\n rotate: 0deg;\n }\n 15% {\n translate: -6px 0;\n rotate: -6deg;\n }\n 30% {\n translate: 4px 0;\n rotate: 4deg;\n }\n 45% {\n translate: -3px 0;\n rotate: -3deg;\n }\n 60% {\n translate: 2px 0;\n rotate: 2deg;\n }\n 75% {\n translate: -1px 0;\n rotate: -1deg;\n }\n 100% {\n translate: 0 0;\n rotate: 0deg;\n }\n }\n [data-kui-fx~=ripple] {\n border-radius: 50%;\n background: currentColor;\n transform-origin: center;\n }\n @keyframes kui-ripple {\n from {\n scale: 0.2;\n opacity: 0.6;\n }\n to {\n scale: var(--kui-ripple-scale, 4);\n opacity: 0;\n }\n }\n @keyframes kui-badge-pop {\n 0% {\n scale: 0.4;\n }\n 60% {\n scale: var(--kui-pop-scale, 1.18);\n }\n 100% {\n scale: 1;\n }\n }\n @keyframes kui-count-bump {\n 0%, 100% {\n scale: 1;\n }\n 50% {\n scale: var(--kui-pop-scale, 1.18);\n }\n }\n [data-kui-fx~=heart-burst] {\n color: var(--kui-heart-color, #f43f5e);\n }\n @keyframes kui-heart-burst {\n 0% {\n scale: 1;\n }\n 30% {\n scale: var(--kui-pop-scale, 1.18);\n }\n 55% {\n scale: 0.9;\n }\n 100% {\n scale: 1;\n }\n }\n [data-kui-fx~=confetti-burst] {\n background-image:\n radial-gradient(\n circle at 20% 20%,\n var(--kui-c1, #f43f5e) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 75% 15%,\n var(--kui-c2, #fbbf24) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 50% 75%,\n var(--kui-c3, #34d399) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 85% 65%,\n var(--kui-c4, #60a5fa) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 15% 65%,\n var(--kui-c5, #c084fc) 0 3px,\n transparent 3px);\n background-repeat: no-repeat;\n }\n @keyframes kui-confetti-burst {\n 0% {\n scale: 0.6;\n opacity: 0;\n }\n 40% {\n scale: var(--kui-pop-scale, 1.18);\n opacity: 1;\n }\n 100% {\n scale: 1;\n opacity: 1;\n }\n }\n @keyframes kui-copy-confirm {\n 0% {\n opacity: 0;\n }\n 15% {\n opacity: 1;\n }\n 85% {\n opacity: 1;\n }\n 100% {\n opacity: 0;\n }\n }\n @keyframes kui-pull-to-refresh {\n 0% {\n translate: 0 0;\n }\n 45% {\n translate: 0 var(--kui-pull-distance, 36px);\n }\n 100% {\n translate: 0 0;\n }\n }\n}\n\n/* src/css/numbers.css */\n@layer kui.effects {\n @keyframes kui-progress-ring {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-gauge-sweep {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-donut-sweep {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-sparkline-draw {\n from {\n stroke-dashoffset: var(--kui-path-length, 1000);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=progress-ring],\n [data-kui-fx~=gauge-sweep],\n [data-kui-fx~=donut-sweep],\n [data-kui-fx~=sparkline-draw] {\n stroke-dasharray: var(--kui-path-length, 100);\n fill: none;\n }\n @keyframes kui-progress-bar {\n from {\n scale: 0 1;\n }\n to {\n scale: 1 1;\n }\n }\n [data-kui-fx~=progress-bar] {\n transform-origin: left center;\n }\n @keyframes kui-progress-segments {\n from {\n opacity: 0.18;\n }\n to {\n opacity: 1;\n }\n }\n @keyframes kui-star-rating-fill {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 calc(100% - var(--kui-fill, 100%)) 0 0);\n }\n }\n .kui-count-decorative {\n display: inline-flex;\n align-items: baseline;\n }\n .kui-odometer-col {\n display: inline-block;\n overflow: hidden;\n height: 1em;\n line-height: 1;\n vertical-align: baseline;\n }\n .kui-odometer-strip {\n display: flex;\n flex-direction: column;\n translate: 0 calc(var(--kui-o, 0) * -1em);\n transition: translate 220ms ease-out;\n }\n .kui-odometer-strip > span {\n height: 1em;\n line-height: 1;\n }\n}\n\n/* src/css/interaction.css */\n@layer kui.effects {\n @property --kui-border-angle { syntax: \"<angle>\"; inherits: false; initial-value: 0deg; }\n @property --kui-border-pct { syntax: \"<percentage>\"; inherits: false; initial-value: 0%; }\n [data-kui-fx~=lift] {\n transition: translate var(--kui-lift-duration, 220ms) var(--kui-lift-ease, ease-out);\n }\n [data-kui-fx~=lift]:focus-visible {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n }\n [data-kui-fx~=lift-shadow] {\n transition: translate var(--kui-lift-shadow-duration, 220ms) var(--kui-lift-shadow-ease, ease-out), box-shadow var(--kui-lift-shadow-duration, 220ms) var(--kui-lift-shadow-ease, ease-out);\n }\n [data-kui-fx~=lift-shadow]:focus-visible {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n box-shadow: 0 14px 28px -12px rgb(0 0 0 / 0.45);\n }\n [data-kui-fx~=shine-sweep] {\n position: relative;\n overflow: hidden;\n }\n [data-kui-fx~=shine-sweep]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 115deg,\n transparent 40%,\n var(--kui-c1, rgb(255 255 255 / 0.35)) 50%,\n transparent 60%);\n background-size: 220% 100%;\n background-position: 120% 0;\n pointer-events: none;\n }\n [data-kui-fx~=shine-sweep]:focus-visible::after {\n animation: kui-shine-sweep var(--kui-shine-sweep-duration, 700ms) var(--kui-shine-sweep-ease, ease-out);\n }\n @keyframes kui-shine-sweep {\n from {\n background-position: 120% 0;\n }\n to {\n background-position: -20% 0;\n }\n }\n [data-kui-fx~=split-flap] {\n display: inline-block;\n perspective: 600px;\n backface-visibility: hidden;\n }\n [data-kui-fx~=split-flap]:focus-visible {\n animation: kui-split-flap var(--kui-split-flap-duration, 600ms) var(--kui-split-flap-ease, ease-in-out);\n }\n @keyframes kui-split-flap {\n from {\n rotate: x 0deg;\n }\n to {\n rotate: x 360deg;\n }\n }\n [data-kui-fx~=border-draw] {\n border: 2px solid transparent;\n border-image-slice: 1;\n border-image-source:\n conic-gradient(\n from -90deg,\n var(--kui-border-draw-color, var(--accent, #d2691e)) 0 var(--kui-border-pct, 0%),\n transparent 0);\n transition: --kui-border-pct var(--kui-border-draw-duration, 500ms) var(--kui-border-draw-ease, ease-out);\n }\n [data-kui-fx~=border-draw]:focus-visible {\n --kui-border-pct: 100%;\n }\n [data-kui-fx~=border-glow] {\n transition: box-shadow var(--kui-border-glow-duration, 260ms) var(--kui-border-glow-ease, ease-out);\n }\n [data-kui-fx~=border-glow]:focus-visible {\n box-shadow: 0 0 0 3px var(--kui-border-glow-color, var(--accent, #d2691e));\n }\n [data-kui-fx~=beam-border],\n [data-kui-fx~=beam-border-auto] {\n position: relative;\n }\n [data-kui-fx~=beam-border]::before,\n [data-kui-fx~=beam-border-auto]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: var(--kui-beam-border-width, 3px);\n background:\n conic-gradient(\n from var(--kui-border-angle, 0deg),\n transparent 0deg,\n transparent 260deg,\n var(--kui-beam-border-c1, #ff5f6d) 282deg,\n var(--kui-beam-border-c2, #ffc371) 306deg,\n var(--kui-beam-border-c3, #4facfe) 330deg,\n var(--kui-beam-border-c4, #a855f7) 354deg,\n transparent 360deg);\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--kui-beam-border-fade-duration, 200ms) ease-out;\n }\n [data-kui-fx~=beam-border]:focus-visible::before {\n opacity: 1;\n animation: kui-beam-rotate var(--kui-beam-border-duration, 4260ms) linear infinite;\n }\n [data-kui-fx~=beam-border-auto]::before {\n opacity: 1;\n animation: kui-beam-rotate var(--kui-beam-border-duration, 4260ms) linear infinite;\n }\n @keyframes kui-beam-rotate {\n to {\n --kui-border-angle: 360deg;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n [data-kui-fx~=beam-border]:focus-visible::before,\n [data-kui-fx~=beam-border-auto]::before {\n animation: none;\n --kui-border-angle: 45deg;\n }\n }\n [data-kui-fx~=underline-slide] {\n position: relative;\n text-decoration: none;\n }\n [data-kui-fx~=underline-slide]::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: -0.15em;\n height: 0.08em;\n background: currentcolor;\n scale: 0 1;\n transform-origin: left center;\n transition: scale var(--kui-underline-slide-duration, 260ms) var(--kui-underline-slide-ease, ease-out);\n }\n [data-kui-fx~=underline-slide]:focus-visible::after {\n scale: 1 1;\n }\n [data-kui-fx~=underline-center] {\n position: relative;\n text-decoration: none;\n }\n [data-kui-fx~=underline-center]::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: -0.15em;\n height: 0.08em;\n background: currentcolor;\n scale: 0 1;\n transform-origin: center;\n transition: scale var(--kui-underline-center-duration, 260ms) var(--kui-underline-center-ease, ease-out);\n }\n [data-kui-fx~=underline-center]:focus-visible::after {\n scale: 1 1;\n }\n [data-kui-fx~=icon-wiggle]:focus-visible {\n animation: kui-icon-wiggle var(--kui-icon-wiggle-duration, 500ms) var(--kui-icon-wiggle-ease, ease-in-out);\n }\n @keyframes kui-icon-wiggle {\n 0%, 100% {\n rotate: 0deg;\n }\n 20% {\n rotate: -12deg;\n }\n 40% {\n rotate: 10deg;\n }\n 60% {\n rotate: -6deg;\n }\n 80% {\n rotate: 4deg;\n }\n }\n [data-kui-fx~=icon-spin]:focus-visible {\n animation: kui-icon-spin var(--kui-icon-spin-duration, 700ms) linear;\n }\n @keyframes kui-icon-spin {\n from {\n rotate: 0deg;\n }\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=icon-bounce]:focus-visible {\n animation: kui-icon-bounce var(--kui-icon-bounce-duration, 500ms) var(--kui-icon-bounce-ease, ease-out);\n }\n @keyframes kui-icon-bounce {\n 0%, 100% {\n translate: 0 0;\n }\n 30% {\n translate: 0 -7px;\n }\n 55% {\n translate: 0 1px;\n }\n 75% {\n translate: 0 -3px;\n }\n }\n @media (hover: hover) and (pointer: fine) {\n [data-kui-fx~=lift]:hover {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n }\n [data-kui-fx~=lift-shadow]:hover {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n box-shadow: 0 14px 28px -12px rgb(0 0 0 / 0.45);\n }\n [data-kui-fx~=shine-sweep]:hover::after {\n animation: kui-shine-sweep var(--kui-shine-sweep-duration, 700ms) var(--kui-shine-sweep-ease, ease-out);\n }\n [data-kui-fx~=split-flap]:hover {\n animation: kui-split-flap var(--kui-split-flap-duration, 600ms) var(--kui-split-flap-ease, ease-in-out);\n }\n [data-kui-fx~=border-draw]:hover {\n --kui-border-pct: 100%;\n }\n [data-kui-fx~=border-glow]:hover {\n box-shadow: 0 0 0 3px var(--kui-border-glow-color, var(--accent, #d2691e));\n }\n [data-kui-fx~=beam-border]:hover::before {\n opacity: 1;\n animation: kui-beam-rotate var(--kui-beam-border-duration, 4260ms) linear infinite;\n }\n [data-kui-fx~=underline-slide]:hover::after {\n scale: 1 1;\n }\n [data-kui-fx~=underline-center]:hover::after {\n scale: 1 1;\n }\n [data-kui-fx~=icon-wiggle]:hover {\n animation: kui-icon-wiggle var(--kui-icon-wiggle-duration, 500ms) var(--kui-icon-wiggle-ease, ease-in-out);\n }\n [data-kui-fx~=icon-spin]:hover {\n animation: kui-icon-spin var(--kui-icon-spin-duration, 700ms) linear;\n }\n [data-kui-fx~=icon-bounce]:hover {\n animation: kui-icon-bounce var(--kui-icon-bounce-duration, 500ms) var(--kui-icon-bounce-ease, ease-out);\n }\n }\n [data-kui-fx~=tilt-3d],\n [data-kui-fx~=tilt-parallax] {\n transform-style: preserve-3d;\n }\n [data-kui-fx~=cursor-spotlight]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n opacity: var(--kui-spotlight-opacity, 0);\n transition: opacity 200ms ease-out;\n background:\n radial-gradient(\n circle 160px at var(--kui-x, 50%) var(--kui-y, 50%),\n color-mix(in srgb, var(--accent, #d2691e) 30%, transparent),\n transparent 70%);\n }\n .kui-cursor-dot {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 9999;\n display: grid;\n place-items: center;\n pointer-events: none;\n opacity: 0;\n transition: opacity 160ms ease-out;\n }\n .kui-cursor-dot-active {\n opacity: 1;\n }\n .kui-cursor-dot-follow,\n .kui-cursor-dot-lag,\n .kui-cursor-dot-invert {\n width: 14px;\n height: 14px;\n margin: -7px 0 0 -7px;\n border-radius: 50%;\n background: var(--accent, #d2691e);\n }\n .kui-cursor-dot-invert {\n background: var(--kui-invert-color, #fff);\n mix-blend-mode: difference;\n }\n .kui-cursor-dot-label {\n margin: -1.4em 0 0 -1.4em;\n padding: 0.4em 0.8em;\n border-radius: 999px;\n background: var(--accent, #d2691e);\n color: var(--kui-label-color, #fff);\n font-size: 0.75rem;\n font-family:\n ui-monospace,\n Menlo,\n monospace;\n white-space: nowrap;\n }\n @media (pointer: coarse) {\n .kui-cursor-dot {\n display: none;\n }\n }\n}\n\n/* src/css/index.css */\n"
6925
+ style.textContent = "/* src/css/base.css */\n@layer kui.tokens, kui.presets, kui.effects, kui.policy, kui.cloak;\n@layer kui.tokens {\n :root {\n --kui-ease-expo-in: cubic-bezier(0.7, 0, 0.84, 0);\n --kui-ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);\n --kui-ease-expo-in-out: cubic-bezier(0.87, 0, 0.13, 1);\n --kui-ease-back-in: cubic-bezier(0.36, 0, 0.66, -0.56);\n --kui-ease-back-out: cubic-bezier(0.34, 1.56, 0.64, 1);\n --kui-ease-back-in-out: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n --kui-ease-quart-out: cubic-bezier(0.25, 1, 0.5, 1);\n --kui-ease-circ-out: cubic-bezier(0, 0.55, 0.45, 1);\n --kui-ease-spring: linear(0, 0.4 12%, 0.9 25%, 1.06 38%, 1.01 62%, 1);\n --kui-ease-bounce: linear(0, 0.5 10%, 1.2 30%, 0.9 50%, 1.08 68%, 0.97 82%, 1.02 92%, 1);\n }\n [data-kui-fx] {\n --kui-dir: 1;\n --kui-i: 0;\n }\n :dir(rtl) [data-kui-fx] {\n --kui-dir: -1;\n }\n}\n@layer kui.policy {\n html[data-kui-cloak] [data-kui][data-kui-reveal]:not([data-kui-state]) {\n opacity: 0 !important;\n }\n @media (prefers-reduced-motion: reduce) {\n [data-kui-rm],\n [data-kui-rm]::before,\n [data-kui-rm]::after,\n [data-kui-rm] .kui-odometer-strip,\n [data-kui-rm][data-kui-fx~=label-float] ~ label,\n [data-kui-rm][data-kui-fx~=input-underline-grow] ~ .kui-underline,\n [data-kui-rm][data-kui-fx~=toggle-morph] ~ .kui-track,\n [data-kui-rm][data-kui-fx~=toggle-morph] ~ .kui-track .kui-thumb,\n [data-kui-rm][data-kui-fx~=checkbox-draw] ~ svg path,\n [data-kui-rm][data-kui-fx~=radio-fill] ~ .kui-dot,\n [data-kui-rm][data-kui-fx~=strength-meter] ~ .kui-meter > *,\n [data-kui-rm][data-kui-fx~=hamburger-to-x] .kui-bar,\n [data-kui-rm][data-kui-fx~=play-to-pause] .kui-bar,\n [data-kui-rm][data-kui-fx~=plus-to-minus] .kui-bar,\n [data-kui-rm][data-kui-fx~=flip-card] > .kui-face-front,\n [data-kui-rm][data-kui-fx~=flip-card] > .kui-face-back {\n transition-duration: 1ms !important;\n transition-delay: 0ms !important;\n }\n [data-kui-rm=shorten],\n [data-kui-rm=shorten]::before,\n [data-kui-rm=shorten]::after {\n animation-duration: 1ms !important;\n animation-delay: 0ms !important;\n }\n [data-kui-rm=crossfade] {\n animation-name: kui-in !important;\n animation-duration: 200ms !important;\n }\n [data-kui-rm=disable] {\n animation: none !important;\n opacity: 1 !important;\n translate: none !important;\n scale: none !important;\n rotate: none !important;\n filter: none !important;\n }\n }\n @media print {\n [data-kui-fx],\n [data-kui-reveal] {\n animation: none !important;\n opacity: 1 !important;\n translate: none !important;\n scale: none !important;\n rotate: none !important;\n filter: none !important;\n }\n }\n}\n\n/* src/css/presets.generated.css */\n@layer kui.presets {\n [data-kui-fx~=accordion-height] {\n --kui-auto-height-duration: 400ms;\n --kui-auto-height-ease: ease-out;\n }\n [data-kui-fx~=aurora] {\n --kui-ambient-gradient-duration: 10s;\n --kui-ambient-gradient-delay: 0ms;\n --kui-ambient-gradient-ease: ease-in-out;\n }\n [data-kui-fx~=back-in-down] {\n --kui-distance: 120px;\n --kui-reveal-ease: var(--kui-ease-expo-out, ease-out);\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n }\n [data-kui-fx~=back-in-up] {\n --kui-distance: 120px;\n --kui-reveal-ease: var(--kui-ease-expo-out, ease-out);\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n }\n [data-kui-fx~=badge-pop] {\n --kui-feedback-pop-duration: 420ms;\n --kui-feedback-pop-delay: 0ms;\n --kui-feedback-pop-ease: var(--kui-ease-back-out, ease-out);\n }\n [data-kui-fx~=beam-border] {\n --kui-beam-border-duration: 220ms;\n --kui-beam-border-ease: ease-out;\n }\n [data-kui-fx~=beam-border-auto] {\n --kui-beam-border-auto-duration: 220ms;\n --kui-beam-border-auto-ease: ease-out;\n }\n [data-kui-fx~=before-after-wipe] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=blob-morph] {\n --kui-path-morph-duration: 800ms;\n }\n [data-kui-fx~=blur-in] {\n --kui-blur-duration: 600ms;\n --kui-blur-delay: 0ms;\n --kui-blur-ease: ease-out;\n }\n [data-kui-fx~=blur-out] {\n --kui-blur-duration: 600ms;\n --kui-blur-delay: 0ms;\n --kui-blur-ease: ease-out;\n }\n [data-kui-fx~=blur-up] {\n --kui-media-blur-up-duration: 600ms;\n --kui-media-blur-up-delay: 0ms;\n --kui-media-blur-up-ease: ease-out;\n }\n [data-kui-fx~=bob] {\n --kui-ambient-float-duration: 2s;\n --kui-distance: 8px;\n --kui-ambient-float-delay: 0ms;\n --kui-ambient-float-ease: ease-in-out;\n }\n [data-kui-fx~=book-page-turn] {\n --kui-from-angle: -160deg;\n --kui-flip-face-duration: 900ms;\n --kui-flip-face-delay: 0ms;\n --kui-flip-face-ease: ease-out;\n }\n [data-kui-fx~=bookmark-fill] {\n --kui-shape-fill-duration: 360ms;\n --kui-shape-fill-delay: 0ms;\n --kui-shape-fill-ease: ease-out;\n }\n [data-kui-fx~=border-draw] {\n --kui-border-draw-duration: 220ms;\n --kui-border-draw-ease: ease-out;\n }\n [data-kui-fx~=border-glow] {\n --kui-border-glow-duration: 220ms;\n --kui-border-glow-ease: ease-out;\n }\n [data-kui-fx~=bounce-in] {\n --kui-from-scale: 0.3;\n --kui-scale-ease: var(--kui-ease-bounce, ease-out);\n --kui-scale-duration: 600ms;\n --kui-scale-delay: 0ms;\n }\n [data-kui-fx~=bounce-in-down] {\n --kui-distance: 60px;\n --kui-reveal-ease: var(--kui-ease-back-out, ease-out);\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n }\n [data-kui-fx~=bounce-in-up] {\n --kui-distance: 60px;\n --kui-reveal-ease: var(--kui-ease-back-out, ease-out);\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n }\n [data-kui-fx~=card-flip-x] {\n --kui-flip-face-duration: 600ms;\n --kui-flip-face-delay: 0ms;\n --kui-flip-face-ease: ease-out;\n }\n [data-kui-fx~=card-flip-y] {\n --kui-flip-face-duration: 600ms;\n --kui-flip-face-delay: 0ms;\n --kui-flip-face-ease: ease-out;\n }\n [data-kui-fx~=chart-area-fill] {\n --kui-shape-fill-duration: 900ms;\n --kui-shape-fill-delay: 0ms;\n --kui-shape-fill-ease: ease-out;\n }\n [data-kui-fx~=chart-bar-grow] {\n --kui-bar-grow-duration: 700ms;\n --kui-bar-grow-ease: var(--kui-ease-back-out, ease-out);\n --kui-bar-grow-delay: 0ms;\n }\n [data-kui-fx~=chart-line-draw] {\n --kui-path-draw-duration: 1200ms;\n --kui-path-draw-ease: ease-in-out;\n --kui-path-draw-delay: 0ms;\n }\n [data-kui-fx~=checkmark-draw] {\n --kui-path-draw-duration: 320ms;\n --kui-path-draw-delay: 0ms;\n --kui-path-draw-ease: ease-out;\n }\n [data-kui-fx~=confetti-burst] {\n --kui-feedback-burst-duration: 900ms;\n --kui-pop-scale: 1.15;\n --kui-feedback-burst-delay: 0ms;\n --kui-feedback-burst-ease: var(--kui-ease-back-out, ease-out);\n }\n [data-kui-fx~=copy-confirm] {\n --kui-feedback-confirm-duration: 1400ms;\n --kui-feedback-confirm-delay: 0ms;\n --kui-feedback-confirm-ease: linear;\n }\n [data-kui-fx~=count-bump] {\n --kui-feedback-pop-duration: 280ms;\n --kui-pop-scale: 1.3;\n --kui-feedback-pop-delay: 0ms;\n --kui-feedback-pop-ease: var(--kui-ease-back-out, ease-out);\n }\n [data-kui-fx~=count-compact] {\n --kui-from: 0;\n --kui-to: 128400;\n --kui-format: compact;\n --kui-count-duration: 1600ms;\n --kui-count-delay: 0ms;\n }\n [data-kui-fx~=count-currency] {\n --kui-from: 0;\n --kui-to: 4820;\n --kui-format: currency;\n --kui-decimals: 0;\n --kui-count-duration: 1600ms;\n --kui-count-delay: 0ms;\n }\n [data-kui-fx~=count-down] {\n --kui-from: 100;\n --kui-to: 0;\n --kui-count-duration: 1600ms;\n --kui-count-delay: 0ms;\n }\n [data-kui-fx~=count-percent] {\n --kui-from: 0;\n --kui-to: 0.82;\n --kui-format: percent;\n --kui-decimals: 0;\n --kui-count-duration: 1600ms;\n --kui-count-delay: 0ms;\n }\n [data-kui-fx~=count-up] {\n --kui-from: 0;\n --kui-to: 100;\n --kui-count-duration: 1600ms;\n --kui-count-delay: 0ms;\n }\n [data-kui-fx~=cross-draw] {\n --kui-path-draw-duration: 260ms;\n --kui-path-draw-delay: 0ms;\n --kui-path-draw-ease: ease-out;\n }\n [data-kui-fx~=cube-rotate] {\n --kui-from-angle: 90deg;\n --kui-flip-face-duration: 600ms;\n --kui-flip-face-delay: 0ms;\n --kui-flip-face-ease: ease-out;\n }\n [data-kui-fx~=cursor-follow] {\n --kui-stiffness: 300;\n --kui-damping: 30;\n }\n [data-kui-fx~=cursor-invert] {\n --kui-stiffness: 260;\n --kui-damping: 26;\n }\n [data-kui-fx~=cursor-label] {\n --kui-stiffness: 260;\n --kui-damping: 26;\n }\n [data-kui-fx~=cursor-lag] {\n --kui-stiffness: 80;\n --kui-damping: 14;\n }\n [data-kui-fx~=curtain-reveal] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=curtain-wipe] {\n --kui-wipe-duration: 800ms;\n --kui-wipe-delay: 0ms;\n --kui-wipe-ease: ease-out;\n }\n [data-kui-fx~=decode] {\n --kui-charset: binary;\n --kui-scramble-text-delay: 0ms;\n }\n [data-kui-fx~=depth-layer] {\n --kui-distance: 200px;\n --kui-parallax-duration: 600ms;\n --kui-parallax-delay: 0ms;\n --kui-parallax-ease: ease-out;\n }\n [data-kui-fx~=donut-sweep] {\n --kui-stroke-sweep-duration: 600ms;\n --kui-stroke-sweep-delay: 0ms;\n --kui-stroke-sweep-ease: ease-out;\n }\n [data-kui-fx~=dot-grid-drift] {\n --kui-ambient-tint-duration: 16s;\n --kui-ambient-tint-ease: linear;\n --kui-ambient-tint-delay: 0ms;\n }\n [data-kui-fx~=drag-inertia] {\n --kui-inertia: true;\n }\n [data-kui-fx~=drag-x] {\n --kui-axis: x;\n }\n [data-kui-fx~=drag-y] {\n --kui-axis: y;\n }\n [data-kui-fx~=draw-signature] {\n --kui-path-draw-duration: 1600ms;\n --kui-path-draw-ease: ease-in-out;\n --kui-path-draw-delay: 0ms;\n }\n [data-kui-fx~=draw-stroke] {\n --kui-path-draw-duration: 800ms;\n --kui-path-draw-ease: ease-in-out;\n --kui-path-draw-delay: 0ms;\n }\n [data-kui-fx~=draw-underline] {\n --kui-path-draw-duration: 420ms;\n --kui-path-draw-delay: 0ms;\n --kui-path-draw-ease: ease-out;\n }\n [data-kui-fx~=drawer-slide] {\n --kui-drawer-slide-duration: 600ms;\n --kui-drawer-slide-delay: 0ms;\n --kui-drawer-slide-ease: ease-out;\n }\n [data-kui-fx~=dropdown-open] {\n --kui-panel-reveal-duration: 600ms;\n --kui-panel-reveal-delay: 0ms;\n --kui-panel-reveal-ease: ease-out;\n }\n [data-kui-fx~=duotone-hover] {\n --kui-media-filter-duration: 600ms;\n --kui-media-filter-delay: 0ms;\n --kui-media-filter-ease: ease-out;\n }\n [data-kui-fx~=elastic-pull] {\n --kui-return: true;\n --kui-bounds: 80;\n }\n [data-kui-fx~=expand-to-modal] {\n --kui-flip-scale: true;\n --kui-flip-container-duration: 500ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=fade-blur-in] {\n --kui-reveal-blur-duration: 600ms;\n --kui-reveal-blur-delay: 0ms;\n --kui-reveal-blur-ease: ease-out;\n }\n [data-kui-fx~=fade-blur-up] {\n --kui-reveal-blur-duration: 600ms;\n --kui-reveal-blur-delay: 0ms;\n --kui-reveal-blur-ease: ease-out;\n }\n [data-kui-fx~=fade-down] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-in] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-left] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-out] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-out-down] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-out-left] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-out-right] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-out-up] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-right] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=fade-up] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=flip-card] {\n --kui-card-toggle-duration: 700ms;\n --kui-card-toggle-ease: ease-in-out;\n }\n [data-kui-fx~=flip-filter] {\n --kui-flip-container-duration: 400ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=flip-in-x] {\n --kui-flip-3d-duration: 600ms;\n --kui-flip-3d-delay: 0ms;\n --kui-flip-3d-ease: ease-out;\n }\n [data-kui-fx~=flip-in-y] {\n --kui-flip-3d-duration: 600ms;\n --kui-flip-3d-delay: 0ms;\n --kui-flip-3d-ease: ease-out;\n }\n [data-kui-fx~=flip-out-x] {\n --kui-flip-3d-duration: 600ms;\n --kui-flip-3d-delay: 0ms;\n --kui-flip-3d-ease: ease-out;\n }\n [data-kui-fx~=flip-out-y] {\n --kui-flip-3d-duration: 600ms;\n --kui-flip-3d-delay: 0ms;\n --kui-flip-3d-ease: ease-out;\n }\n [data-kui-fx~=flip-reorder] {\n --kui-flip-container-duration: 400ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=flip-shuffle] {\n --kui-flip-container-duration: 400ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=flip-sort] {\n --kui-flip-container-duration: 400ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=float] {\n --kui-ambient-float-duration: 4s;\n --kui-ambient-float-delay: 0ms;\n --kui-ambient-float-ease: ease-in-out;\n }\n [data-kui-fx~=floating-shapes] {\n --kui-ambient-float-duration: 6s;\n --kui-distance: 10px;\n --kui-ambient-float-delay: 0ms;\n --kui-ambient-float-ease: ease-in-out;\n }\n [data-kui-fx~=focus-ring-grow] {\n --kui-focus-ring-duration: 600ms;\n --kui-focus-ring-delay: 0ms;\n --kui-focus-ring-ease: ease-out;\n }\n [data-kui-fx~=fold-panel] {\n --kui-from-angle: -90deg;\n --kui-flip-face-duration: 600ms;\n --kui-flip-face-delay: 0ms;\n --kui-flip-face-ease: ease-out;\n }\n [data-kui-fx~=gauge-sweep] {\n --kui-stroke-sweep-duration: 600ms;\n --kui-stroke-sweep-delay: 0ms;\n --kui-stroke-sweep-ease: ease-out;\n }\n [data-kui-fx~=glitch] {\n --kui-charset: symbols;\n --kui-scramble-text-delay: 0ms;\n }\n [data-kui-fx~=glow-pulse] {\n --kui-ambient-pulse-duration: 2.2s;\n --kui-ambient-pulse-delay: 0ms;\n --kui-ambient-pulse-ease: ease-in-out;\n }\n [data-kui-fx~=gradient-border] {\n --kui-ambient-gradient-ring-duration: 6s;\n --kui-ambient-gradient-ring-ease: linear;\n --kui-ambient-gradient-ring-delay: 0ms;\n }\n [data-kui-fx~=gradient-mesh] {\n --kui-ambient-gradient-duration: 10s;\n --kui-ambient-gradient-delay: 0ms;\n --kui-ambient-gradient-ease: ease-in-out;\n }\n [data-kui-fx~=gradient-rotate-border] {\n --kui-ambient-gradient-ring-duration: 6s;\n --kui-ambient-gradient-ring-ease: linear;\n --kui-ambient-gradient-ring-delay: 0ms;\n }\n [data-kui-fx~=gradient-shimmer] {\n --kui-text-shimmer-duration: 600ms;\n --kui-text-shimmer-delay: 0ms;\n --kui-text-shimmer-ease: ease-out;\n }\n [data-kui-fx~=gradient-stroke] {\n --kui-path-draw-duration: 2400ms;\n --kui-path-draw-ease: ease-in-out;\n --kui-path-draw-delay: 0ms;\n }\n [data-kui-fx~=gradient-sweep] {\n --kui-text-gradient-sweep-duration: 600ms;\n --kui-text-gradient-sweep-delay: 0ms;\n --kui-text-gradient-sweep-ease: ease-out;\n }\n [data-kui-fx~=grayscale-hover] {\n --kui-media-filter-duration: 600ms;\n --kui-media-filter-delay: 0ms;\n --kui-media-filter-ease: ease-out;\n }\n [data-kui-fx~=grid-to-list] {\n --kui-flip-scale: true;\n --kui-flip-container-duration: 400ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=hamburger-to-x] {\n --kui-icon-toggle-duration: 260ms;\n --kui-icon-toggle-ease: ease-out;\n }\n [data-kui-fx~=heart-burst] {\n --kui-feedback-burst-duration: 700ms;\n --kui-pop-scale: 1.4;\n --kui-feedback-burst-delay: 0ms;\n --kui-feedback-burst-ease: var(--kui-ease-back-out, ease-out);\n }\n [data-kui-fx~=heart-fill] {\n --kui-shape-fill-duration: 420ms;\n --kui-shape-fill-delay: 0ms;\n --kui-shape-fill-ease: ease-out;\n }\n [data-kui-fx~=highlight-sweep] {\n --kui-sweep-color: gold;\n --kui-text-sweep-duration: 600ms;\n --kui-text-sweep-delay: 0ms;\n --kui-text-sweep-ease: ease-out;\n }\n [data-kui-fx~=icon-bounce] {\n --kui-icon-bounce-duration: 220ms;\n --kui-icon-bounce-ease: ease-out;\n }\n [data-kui-fx~=icon-morph] {\n --kui-path-morph-duration: 300ms;\n }\n [data-kui-fx~=icon-spin] {\n --kui-icon-spin-duration: 220ms;\n --kui-icon-spin-ease: ease-out;\n }\n [data-kui-fx~=icon-wiggle] {\n --kui-icon-wiggle-duration: 220ms;\n --kui-icon-wiggle-ease: ease-out;\n }\n [data-kui-fx~=image-parallax-frame] {\n --kui-media-parallax-frame-duration: 600ms;\n --kui-media-parallax-frame-delay: 0ms;\n --kui-media-parallax-frame-ease: ease-out;\n }\n [data-kui-fx~=ken-burns] {\n --kui-media-ken-burns-duration: 600ms;\n --kui-media-ken-burns-delay: 0ms;\n --kui-media-ken-burns-ease: ease-out;\n }\n [data-kui-fx~=ken-burns-out] {\n --kui-media-ken-burns-duration: 600ms;\n --kui-media-ken-burns-delay: 0ms;\n --kui-media-ken-burns-ease: ease-out;\n }\n [data-kui-fx~=lift] {\n --kui-lift-duration: 220ms;\n --kui-lift-ease: ease-out;\n }\n [data-kui-fx~=lift-shadow] {\n --kui-lift-shadow-duration: 220ms;\n --kui-lift-shadow-ease: ease-out;\n }\n [data-kui-fx~=lightbox-open] {\n --kui-media-lightbox-duration: 600ms;\n --kui-media-lightbox-delay: 0ms;\n --kui-media-lightbox-ease: ease-out;\n }\n [data-kui-fx~=line-grid-drift] {\n --kui-ambient-tint-duration: 16s;\n --kui-ambient-tint-ease: linear;\n --kui-ambient-tint-delay: 0ms;\n }\n [data-kui-fx~=loading-bar] {\n --kui-bar-duration: 600ms;\n --kui-bar-delay: 0ms;\n --kui-bar-ease: ease-out;\n }\n [data-kui-fx~=logo-build] {\n --kui-logo-assemble-duration: 520ms;\n --kui-logo-assemble-ease: var(--kui-ease-back-out, ease-out);\n --kui-logo-assemble-delay: 0ms;\n }\n [data-kui-fx~=long-press] {\n --kui-pressable-duration: 500ms;\n }\n [data-kui-fx~=magnetic-snap] {\n --kui-strength: 0.6;\n --kui-radius: 160;\n }\n [data-kui-fx~=marquee] {\n --kui-text-marquee-duration: 600ms;\n --kui-text-marquee-delay: 0ms;\n --kui-text-marquee-ease: ease-out;\n }\n [data-kui-fx~=marquee-scroll-linked] {\n --kui-text-marquee-duration: 600ms;\n --kui-text-marquee-delay: 0ms;\n --kui-text-marquee-ease: ease-out;\n }\n [data-kui-fx~=mask-reveal] {\n --kui-media-mask-duration: 600ms;\n --kui-media-mask-delay: 0ms;\n --kui-media-mask-ease: ease-out;\n }\n [data-kui-fx~=masonry-reflow] {\n --kui-flip-container-duration: 400ms;\n --kui-flip-container-ease: ease-out;\n }\n [data-kui-fx~=mega-menu-drop] {\n --kui-panel-reveal-duration: 550ms;\n --kui-panel-reveal-delay: 0ms;\n --kui-panel-reveal-ease: ease-out;\n }\n [data-kui-fx~=menu-fullscreen] {\n --kui-menu-fullscreen-duration: 600ms;\n --kui-menu-fullscreen-delay: 0ms;\n --kui-menu-fullscreen-ease: ease-out;\n }\n [data-kui-fx~=menu-stagger-open] {\n --kui-nav-reveal-duration: 600ms;\n --kui-nav-reveal-delay: 0ms;\n --kui-nav-reveal-ease: ease-out;\n }\n [data-kui-fx~=noise-overlay] {\n --kui-ambient-tint-duration: 650ms;\n --kui-ambient-tint-ease: steps(6);\n --kui-ambient-tint-delay: 0ms;\n }\n [data-kui-fx~=odometer-roll] {\n --kui-from: 0;\n --kui-to: 4820;\n --kui-count-odometer-duration: 1600ms;\n --kui-count-odometer-delay: 0ms;\n }\n [data-kui-fx~=orbit] {\n --kui-ambient-orbit-duration: 3.5s;\n --kui-ambient-orbit-delay: 0ms;\n --kui-ambient-orbit-ease: linear;\n }\n [data-kui-fx~=page-fade] {\n --kui-page-reveal-duration: 600ms;\n --kui-page-reveal-delay: 0ms;\n --kui-page-reveal-ease: ease-out;\n }\n [data-kui-fx~=page-slide] {\n --kui-page-reveal-duration: 600ms;\n --kui-page-reveal-delay: 0ms;\n --kui-page-reveal-ease: ease-out;\n }\n [data-kui-fx~=parallax-rotate] {\n --kui-parallax-rotate-duration: 600ms;\n --kui-parallax-rotate-delay: 0ms;\n --kui-parallax-rotate-ease: ease-out;\n }\n [data-kui-fx~=parallax-scale] {\n --kui-parallax-scale-duration: 600ms;\n --kui-parallax-scale-delay: 0ms;\n --kui-parallax-scale-ease: ease-out;\n }\n [data-kui-fx~=parallax-x] {\n --kui-parallax-duration: 600ms;\n --kui-parallax-delay: 0ms;\n --kui-parallax-ease: ease-out;\n }\n [data-kui-fx~=parallax-y] {\n --kui-parallax-duration: 600ms;\n --kui-parallax-delay: 0ms;\n --kui-parallax-ease: ease-out;\n }\n [data-kui-fx~=pin-section] {\n --kui-distance: 100vh;\n --kui-spacer: true;\n }\n [data-kui-fx~=pin-spacer] {\n --kui-spacer: true;\n }\n [data-kui-fx~=pin-until] {\n --kui-spacer: false;\n }\n [data-kui-fx~=play-to-pause] {\n --kui-icon-toggle-duration: 260ms;\n --kui-icon-toggle-ease: ease-out;\n }\n [data-kui-fx~=plus-to-minus] {\n --kui-icon-toggle-duration: 260ms;\n --kui-icon-toggle-ease: ease-out;\n }\n [data-kui-fx~=pop] {\n --kui-pop-duration: 220ms;\n --kui-pop-ease: ease-out;\n }\n [data-kui-fx~=pop-in] {\n --kui-from-scale: 0.6;\n --kui-scale-ease: var(--kui-ease-back-out, ease-out);\n --kui-scale-duration: 600ms;\n --kui-scale-delay: 0ms;\n }\n [data-kui-fx~=pop-out] {\n --kui-from-scale: 0.6;\n --kui-scale-ease: var(--kui-ease-back-in, ease-out);\n --kui-scale-duration: 600ms;\n --kui-scale-delay: 0ms;\n }\n [data-kui-fx~=progress-bar] {\n --kui-meter-bar-duration: 600ms;\n --kui-meter-bar-delay: 0ms;\n --kui-meter-bar-ease: ease-out;\n }\n [data-kui-fx~=progress-indeterminate] {\n --kui-feedback-progress-track-duration: 1.4s;\n --kui-feedback-progress-track-delay: 0ms;\n --kui-feedback-progress-track-ease: ease-in-out;\n }\n [data-kui-fx~=progress-ring] {\n --kui-stroke-sweep-duration: 600ms;\n --kui-stroke-sweep-delay: 0ms;\n --kui-stroke-sweep-ease: ease-out;\n }\n [data-kui-fx~=progress-segments] {\n --kui-meter-segments-duration: 600ms;\n --kui-meter-segments-delay: 0ms;\n --kui-meter-segments-ease: ease-out;\n }\n [data-kui-fx~=pull-to-refresh] {\n --kui-feedback-pull-duration: 900ms;\n --kui-feedback-pull-ease: ease-out;\n --kui-feedback-pull-delay: 0ms;\n }\n [data-kui-fx~=range-fill] {\n --kui-range-fill-duration: 400ms;\n --kui-range-fill-delay: 0ms;\n --kui-range-fill-ease: ease-out;\n }\n [data-kui-fx~=redaction-reveal] {\n --kui-redaction-reveal-duration: 600ms;\n --kui-redaction-reveal-delay: 0ms;\n --kui-redaction-reveal-ease: ease-out;\n }\n [data-kui-fx~=reveal-once] {\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=ripple] {\n --kui-feedback-ripple-duration: 600ms;\n --kui-feedback-ripple-delay: 0ms;\n --kui-feedback-ripple-ease: ease-out;\n }\n [data-kui-fx~=roll-in] {\n --kui-roll-duration: 600ms;\n --kui-roll-delay: 0ms;\n --kui-roll-ease: ease-out;\n }\n [data-kui-fx~=roll-out] {\n --kui-roll-duration: 600ms;\n --kui-roll-delay: 0ms;\n --kui-roll-ease: ease-out;\n }\n [data-kui-fx~=rotate-in] {\n --kui-rotate-duration: 600ms;\n --kui-rotate-delay: 0ms;\n --kui-rotate-ease: ease-out;\n }\n [data-kui-fx~=rotate-in-left] {\n --kui-from-angle: -45deg;\n --kui-rotate-duration: 600ms;\n --kui-rotate-delay: 0ms;\n --kui-rotate-ease: ease-out;\n }\n [data-kui-fx~=rotate-in-right] {\n --kui-from-angle: 45deg;\n --kui-rotate-duration: 600ms;\n --kui-rotate-delay: 0ms;\n --kui-rotate-ease: ease-out;\n }\n [data-kui-fx~=rotate-out] {\n --kui-rotate-duration: 600ms;\n --kui-rotate-delay: 0ms;\n --kui-rotate-ease: ease-out;\n }\n [data-kui-fx~=rubber-band] {\n --kui-return: true;\n --kui-bounds: 120;\n }\n [data-kui-fx~=saturate-hover] {\n --kui-media-filter-duration: 600ms;\n --kui-media-filter-delay: 0ms;\n --kui-media-filter-ease: ease-out;\n }\n [data-kui-fx~=scanline] {\n --kui-ambient-tint-duration: 3.5s;\n --kui-ambient-tint-ease: linear;\n --kui-ambient-tint-delay: 0ms;\n }\n [data-kui-fx~=scramble] {\n --kui-charset: upper;\n --kui-scramble-text-delay: 0ms;\n }\n [data-kui-fx~=scroll-desaturate] {\n --kui-desaturate-duration: 600ms;\n --kui-desaturate-delay: 0ms;\n --kui-desaturate-ease: ease-out;\n }\n [data-kui-fx~=scroll-fade] {\n --kui-scroll-fade-duration: 600ms;\n --kui-scroll-fade-delay: 0ms;\n --kui-scroll-fade-ease: ease-out;\n }\n [data-kui-fx~=scroll-progress-bar] {\n --kui-progress-duration: 600ms;\n --kui-progress-delay: 0ms;\n --kui-progress-ease: ease-out;\n }\n [data-kui-fx~=scroll-progress-bar-y] {\n --kui-progress-duration: 600ms;\n --kui-progress-delay: 0ms;\n --kui-progress-ease: ease-out;\n }\n [data-kui-fx~=scroll-progress-ring] {\n --kui-progress-stroke-duration: 600ms;\n --kui-progress-stroke-delay: 0ms;\n --kui-progress-stroke-ease: ease-out;\n }\n [data-kui-fx~=scroll-skew] {\n --kui-skew-duration: 600ms;\n --kui-skew-delay: 0ms;\n --kui-skew-ease: ease-out;\n }\n [data-kui-fx~=scroll-snap-x] {\n --kui-axis: x;\n }\n [data-kui-fx~=scroll-snap-y] {\n --kui-axis: y;\n }\n [data-kui-fx~=scrollytelling-step] {\n --kui-steps: 4;\n }\n [data-kui-fx~=sequence-scrub] {\n --kui-spacer: true;\n }\n [data-kui-fx~=shake-error] {\n --kui-feedback-shake-duration: 500ms;\n --kui-feedback-shake-delay: 0ms;\n --kui-feedback-shake-ease: linear;\n }\n [data-kui-fx~=shine-sweep] {\n --kui-shine-sweep-duration: 220ms;\n --kui-shine-sweep-ease: ease-out;\n }\n [data-kui-fx~=skeleton-shimmer] {\n --kui-feedback-shimmer-duration: 1.6s;\n --kui-feedback-shimmer-delay: 0ms;\n --kui-feedback-shimmer-ease: linear;\n }\n [data-kui-fx~=skeleton-to-content] {\n --kui-feedback-fade-duration: 600ms;\n --kui-feedback-fade-delay: 0ms;\n --kui-feedback-fade-ease: ease-out;\n }\n [data-kui-fx~=slat-assemble] {\n --kui-slat-assemble-duration: 500ms;\n --kui-slat-assemble-delay: 0ms;\n --kui-slat-assemble-ease: ease-out;\n }\n [data-kui-fx~=slide-block-end] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-block-start] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-down] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-inline-end] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-inline-start] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-left] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-out-down] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-out-left] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-out-right] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-out-up] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-right] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=slide-up] {\n --kui-distance: 100px;\n --kui-from-opacity: 1;\n --kui-reveal-duration: 600ms;\n --kui-reveal-delay: 0ms;\n --kui-reveal-ease: ease-out;\n }\n [data-kui-fx~=snap-back] {\n --kui-return: true;\n --kui-stiffness: 260;\n }\n [data-kui-fx~=sparkline-draw] {\n --kui-stroke-sweep-duration: 600ms;\n --kui-stroke-sweep-delay: 0ms;\n --kui-stroke-sweep-ease: ease-out;\n }\n [data-kui-fx~=spinner] {\n --kui-feedback-spin-duration: 900ms;\n --kui-feedback-spin-delay: 0ms;\n --kui-feedback-spin-ease: linear;\n }\n [data-kui-fx~=spinner-dots] {\n --kui-feedback-dot-pulse-duration: 1.2s;\n --kui-feedback-dot-pulse-delay: 0ms;\n --kui-feedback-dot-pulse-ease: ease-in-out;\n }\n [data-kui-fx~=spinner-ring] {\n --kui-feedback-spin-duration: 900ms;\n --kui-feedback-spin-delay: 0ms;\n --kui-feedback-spin-ease: linear;\n }\n [data-kui-fx~=split-chars] {\n --kui-unit: chars;\n --kui-direction: fade;\n --kui-stagger: 30ms;\n --kui-split-text-duration: 500ms;\n --kui-split-text-delay: 0ms;\n --kui-split-text-ease: ease-out;\n }\n [data-kui-fx~=split-flap] {\n --kui-split-flap-duration: 220ms;\n --kui-split-flap-ease: ease-out;\n }\n [data-kui-fx~=split-lines] {\n --kui-unit: lines;\n --kui-direction: fade;\n --kui-stagger: 160ms;\n --kui-split-text-duration: 500ms;\n --kui-split-text-delay: 0ms;\n --kui-split-text-ease: ease-out;\n }\n [data-kui-fx~=split-words] {\n --kui-unit: words;\n --kui-direction: fade;\n --kui-stagger: 90ms;\n --kui-split-text-duration: 500ms;\n --kui-split-text-delay: 0ms;\n --kui-split-text-ease: ease-out;\n }\n [data-kui-fx~=spotlight-follow] {\n --kui-ambient-tint-duration: 9s;\n --kui-ambient-tint-delay: 0ms;\n --kui-ambient-tint-ease: ease-in-out;\n }\n [data-kui-fx~=stacking-cards] {\n --kui-spacer: false;\n }\n [data-kui-fx~=star-rating-fill] {\n --kui-meter-stars-duration: 600ms;\n --kui-meter-stars-delay: 0ms;\n --kui-meter-stars-ease: ease-out;\n }\n [data-kui-fx~=starfield] {\n --kui-ambient-tint-duration: 40s;\n --kui-ambient-tint-ease: linear;\n --kui-ambient-tint-delay: 0ms;\n }\n [data-kui-fx~=step-progress] {\n --kui-step-progress-duration: 400ms;\n --kui-step-progress-delay: 0ms;\n --kui-step-progress-ease: ease-out;\n }\n [data-kui-fx~=strength-meter] {\n --kui-strength-meter-duration: 400ms;\n --kui-strength-meter-delay: 0ms;\n --kui-strength-meter-ease: ease-out;\n }\n [data-kui-fx~=submit-to-spinner-to-check] {\n --kui-submit-flow-duration: 400ms;\n --kui-submit-flow-delay: 0ms;\n --kui-submit-flow-ease: ease-out;\n }\n [data-kui-fx~=swing-in] {\n --kui-from-angle: -15deg;\n --kui-rotate-ease: var(--kui-ease-back-out, ease-out);\n --kui-rotate-duration: 600ms;\n --kui-rotate-delay: 0ms;\n }\n [data-kui-fx~=swipe-x] {\n --kui-axis: x;\n }\n [data-kui-fx~=tab-indicator-slide] {\n --kui-flip-indicator-duration: 300ms;\n --kui-flip-indicator-ease: ease-out;\n }\n [data-kui-fx~=text-3d-extrude] {\n --kui-text-3d-extrude-duration: 600ms;\n --kui-text-3d-extrude-delay: 0ms;\n --kui-text-3d-extrude-ease: ease-out;\n }\n [data-kui-fx~=text-jitter] {\n --kui-motion: jitter;\n --kui-split-text-motion-delay: 0ms;\n }\n [data-kui-fx~=text-outline-fill] {\n --kui-text-outline-fill-duration: 600ms;\n --kui-text-outline-fill-delay: 0ms;\n --kui-text-outline-fill-ease: ease-out;\n }\n [data-kui-fx~=text-reveal-down] {\n --kui-unit: words;\n --kui-direction: down;\n --kui-stagger: 90ms;\n --kui-split-text-duration: 500ms;\n --kui-split-text-delay: 0ms;\n --kui-split-text-ease: ease-out;\n }\n [data-kui-fx~=text-reveal-mask] {\n --kui-unit: lines;\n --kui-direction: mask;\n --kui-stagger: 160ms;\n --kui-split-text-duration: 500ms;\n --kui-split-text-delay: 0ms;\n --kui-split-text-ease: ease-out;\n }\n [data-kui-fx~=text-reveal-up] {\n --kui-unit: words;\n --kui-direction: up;\n --kui-stagger: 90ms;\n --kui-split-text-duration: 500ms;\n --kui-split-text-delay: 0ms;\n --kui-split-text-ease: ease-out;\n }\n [data-kui-fx~=text-wave] {\n --kui-motion: wave;\n --kui-split-text-motion-delay: 0ms;\n }\n [data-kui-fx~=throwable] {\n --kui-inertia: true;\n --kui-damping: 18;\n }\n [data-kui-fx~=toast-slide-in] {\n --kui-feedback-toast-duration: 420ms;\n --kui-feedback-toast-delay: 0ms;\n --kui-feedback-toast-ease: var(--kui-ease-back-out, ease-out);\n }\n [data-kui-fx~=toast-slide-out] {\n --kui-feedback-toast-ease: ease-in;\n --kui-feedback-toast-duration: 420ms;\n --kui-feedback-toast-delay: 0ms;\n }\n [data-kui-fx~=typewriter] {\n --kui-loop: false;\n --kui-typewriter-delay: 0ms;\n }\n [data-kui-fx~=typewriter-loop] {\n --kui-loop: true;\n --kui-typewriter-delay: 0ms;\n }\n [data-kui-fx~=underline-center] {\n --kui-underline-center-duration: 220ms;\n --kui-underline-center-ease: ease-out;\n }\n [data-kui-fx~=underline-draw] {\n --kui-text-sweep-duration: 600ms;\n --kui-text-sweep-delay: 0ms;\n --kui-text-sweep-ease: ease-out;\n }\n [data-kui-fx~=underline-slide] {\n --kui-underline-slide-duration: 220ms;\n --kui-underline-slide-ease: ease-out;\n }\n [data-kui-fx~=validate-check] {\n --kui-validate-check-duration: 600ms;\n --kui-validate-check-delay: 0ms;\n --kui-validate-check-ease: ease-out;\n }\n [data-kui-fx~=validate-shake] {\n --kui-validate-shake-duration: 600ms;\n --kui-validate-shake-delay: 0ms;\n --kui-validate-shake-ease: ease-out;\n }\n [data-kui-fx~=var-slant] {\n --kui-var-slant-duration: 600ms;\n --kui-var-slant-delay: 0ms;\n --kui-var-slant-ease: ease-out;\n }\n [data-kui-fx~=var-weight] {\n --kui-var-weight-duration: 600ms;\n --kui-var-weight-delay: 0ms;\n --kui-var-weight-ease: ease-out;\n }\n [data-kui-fx~=var-width] {\n --kui-var-width-duration: 600ms;\n --kui-var-width-delay: 0ms;\n --kui-var-width-ease: ease-out;\n }\n [data-kui-fx~=wave-blob] {\n --kui-ambient-tint-duration: 12s;\n --kui-ambient-tint-delay: 0ms;\n --kui-ambient-tint-ease: ease-in-out;\n }\n [data-kui-fx~=wipe-circle] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=wipe-diagonal] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=wipe-down] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=wipe-left] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=wipe-right] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=wipe-up] {\n --kui-media-wipe-duration: 600ms;\n --kui-media-wipe-delay: 0ms;\n --kui-media-wipe-ease: ease-out;\n }\n [data-kui-fx~=wobble] {\n --kui-feedback-wobble-duration: 600ms;\n --kui-feedback-wobble-ease: ease-in-out;\n --kui-feedback-wobble-delay: 0ms;\n }\n [data-kui-fx~=word-cycler] {\n --kui-word-cycler-delay: 0ms;\n }\n [data-kui-fx~=zoom-in] {\n --kui-scale-duration: 600ms;\n --kui-scale-delay: 0ms;\n --kui-scale-ease: ease-out;\n }\n [data-kui-fx~=zoom-in-down] {\n --kui-scale-move-duration: 600ms;\n --kui-scale-move-delay: 0ms;\n --kui-scale-move-ease: ease-out;\n }\n [data-kui-fx~=zoom-in-up] {\n --kui-scale-move-duration: 600ms;\n --kui-scale-move-delay: 0ms;\n --kui-scale-move-ease: ease-out;\n }\n [data-kui-fx~=zoom-out] {\n --kui-scale-duration: 600ms;\n --kui-scale-delay: 0ms;\n --kui-scale-ease: ease-out;\n }\n}\n@layer kui.cloak {\n html[data-kui-cloak] [data-kui~=back-in-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=back-in-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=blur-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=blur-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=bounce-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=bounce-in-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=bounce-in-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=card-flip-x]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=card-flip-y]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=chart-bar-grow]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=curtain-reveal]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-blur-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-blur-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=flip-in-x]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=flip-in-y]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fold-panel]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=loading-bar]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=mask-reveal]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=pop-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=progress-bar]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=reveal-once]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=roll-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=rotate-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=rotate-in-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=rotate-in-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slat-assemble]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-block-end]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-block-start]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-inline-end]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-inline-start]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=swing-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=text-reveal-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=text-reveal-mask]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=text-reveal-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-circle]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-diagonal]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=zoom-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=zoom-in-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=zoom-in-up]:not([data-kui-state]) {\n opacity: 0;\n animation: kui-cloak-release 1ms linear 2s forwards;\n }\n @keyframes kui-cloak-release {\n to {\n opacity: 1;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n html[data-kui-cloak] [data-kui~=back-in-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=back-in-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=blur-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=blur-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=bounce-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=bounce-in-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=bounce-in-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=card-flip-x]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=card-flip-y]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=chart-bar-grow]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=curtain-reveal]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-blur-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-blur-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fade-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=flip-in-x]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=flip-in-y]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=fold-panel]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=loading-bar]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=mask-reveal]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=pop-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=progress-bar]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=reveal-once]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=roll-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=rotate-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=rotate-in-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=rotate-in-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slat-assemble]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-block-end]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-block-start]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-inline-end]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-inline-start]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=slide-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=swing-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=text-reveal-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=text-reveal-mask]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=text-reveal-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-circle]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-diagonal]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-left]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-right]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=wipe-up]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=zoom-in]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=zoom-in-down]:not([data-kui-state]),\n html[data-kui-cloak] [data-kui~=zoom-in-up]:not([data-kui-state]) {\n opacity: 1;\n animation: none;\n }\n }\n}\n\n/* src/css/entrance.css */\n@layer kui.effects {\n @keyframes kui-in {\n from {\n opacity: var(--kui-from-opacity, 0);\n }\n }\n @keyframes kui-out {\n to {\n opacity: 0;\n }\n }\n @keyframes kui-in-up {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-in-down {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n }\n @keyframes kui-in-left {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: var(--kui-distance, 24px) 0;\n }\n }\n @keyframes kui-in-right {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n }\n @keyframes kui-out-up {\n to {\n opacity: 0;\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n }\n @keyframes kui-out-down {\n to {\n opacity: 0;\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-out-left {\n to {\n opacity: 0;\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n }\n @keyframes kui-out-right {\n to {\n opacity: 0;\n translate: var(--kui-distance, 24px) 0;\n }\n }\n @keyframes kui-in-inline-start {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: calc(var(--kui-distance, 24px) * var(--kui-dir, 1)) 0;\n }\n }\n @keyframes kui-in-inline-end {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: calc(var(--kui-distance, 24px) * var(--kui-dir, 1) * -1) 0;\n }\n }\n [data-kui-state=ready] {\n --kui-distance: 0px !important;\n }\n @keyframes kui-zoom-in {\n from {\n scale: var(--kui-from-scale, 0.92);\n }\n }\n @keyframes kui-zoom-out {\n to {\n scale: var(--kui-from-scale, 0.92);\n }\n }\n @keyframes kui-zoom-in-up {\n from {\n scale: var(--kui-from-scale, 0.92);\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-zoom-in-down {\n from {\n scale: var(--kui-from-scale, 0.92);\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n }\n @keyframes kui-flip-in-x {\n from {\n transform: perspective(var(--kui-perspective, 1200px)) rotateX(var(--kui-from-angle, 90deg));\n }\n }\n @keyframes kui-flip-in-y {\n from {\n transform: perspective(var(--kui-perspective, 1200px)) rotateY(var(--kui-from-angle, 90deg));\n }\n }\n @keyframes kui-flip-out-x {\n to {\n transform: perspective(var(--kui-perspective, 1200px)) rotateX(var(--kui-from-angle, 90deg));\n }\n }\n @keyframes kui-flip-out-y {\n to {\n transform: perspective(var(--kui-perspective, 1200px)) rotateY(var(--kui-from-angle, 90deg));\n }\n }\n @keyframes kui-rotate-in {\n from {\n rotate: var(--kui-from-angle, -8deg);\n }\n }\n @keyframes kui-rotate-out {\n to {\n rotate: var(--kui-from-angle, -8deg);\n }\n }\n @keyframes kui-swing-in {\n from {\n rotate: var(--kui-from-angle, -15deg);\n }\n }\n @keyframes kui-roll-in {\n from {\n rotate: var(--kui-from-angle, -120deg);\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n }\n @keyframes kui-roll-out {\n to {\n rotate: var(--kui-from-angle, -120deg);\n translate: var(--kui-distance, 24px) 0;\n }\n }\n @keyframes kui-blur-in {\n from {\n filter: blur(var(--kui-blur, 12px));\n }\n }\n @keyframes kui-blur-out {\n to {\n filter: blur(var(--kui-blur, 12px));\n }\n }\n @keyframes kui-fade-blur-up {\n from {\n opacity: var(--kui-from-opacity, 0);\n translate: 0 var(--kui-distance, 24px);\n filter: blur(var(--kui-blur, 12px));\n }\n }\n @keyframes kui-fade-blur-in {\n from {\n opacity: var(--kui-from-opacity, 0);\n filter: blur(var(--kui-blur, 12px));\n }\n }\n [data-kui-fx*=flip-] {\n transform-style: preserve-3d;\n }\n [data-kui-fx~=flip-in-x][data-kui-state=ready],\n [data-kui-fx~=flip-in-y][data-kui-state=ready] {\n --kui-from-angle: 0deg !important;\n opacity: 0;\n }\n [data-kui-fx~=rotate-in][data-kui-state=ready],\n [data-kui-fx~=rotate-in-left][data-kui-state=ready],\n [data-kui-fx~=rotate-in-right][data-kui-state=ready],\n [data-kui-fx~=swing-in][data-kui-state=ready],\n [data-kui-fx~=roll-in][data-kui-state=ready] {\n --kui-from-angle: 0deg !important;\n opacity: 0;\n }\n}\n\n/* src/css/scroll.css */\n@layer kui.effects {\n @keyframes kui-parallax-y {\n from {\n translate: 0 calc(var(--kui-distance, 100px) * -1);\n }\n to {\n translate: 0 var(--kui-distance, 100px);\n }\n }\n @keyframes kui-parallax-x {\n from {\n translate: calc(var(--kui-distance, 100px) * -1) 0;\n }\n to {\n translate: var(--kui-distance, 100px) 0;\n }\n }\n @keyframes kui-parallax-scale {\n from {\n scale: var(--kui-from-scale, 1);\n }\n to {\n scale: var(--kui-to-scale, 1.2);\n }\n }\n @keyframes kui-parallax-rotate {\n from {\n rotate: var(--kui-from-angle, 0deg);\n }\n to {\n rotate: var(--kui-to-angle, 12deg);\n }\n }\n @keyframes kui-scroll-fade {\n from {\n opacity: var(--kui-from-opacity, 0);\n }\n to {\n opacity: 1;\n }\n }\n @keyframes kui-desaturate {\n from {\n filter: grayscale(var(--kui-from-grayscale, 100%));\n }\n to {\n filter: grayscale(var(--kui-to-grayscale, 0%));\n }\n }\n @keyframes kui-scroll-skew {\n from {\n transform: skewY(var(--kui-from-skew, 8deg));\n }\n to {\n transform: skewY(var(--kui-to-skew, 0deg));\n }\n }\n @keyframes kui-progress-x {\n from {\n scale: 0 1;\n }\n to {\n scale: 1 1;\n }\n }\n @keyframes kui-progress-y {\n from {\n scale: 1 0;\n }\n to {\n scale: 1 1;\n }\n }\n @keyframes kui-progress-ring {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=scroll-progress-bar] {\n transform-origin: left center;\n }\n [data-kui-fx~=scroll-progress-ring] {\n stroke-dasharray: var(--kui-path-length, 100);\n }\n [data-kui-fx~=sequence-scrub]:has(> [data-kui-step-state]),\n [data-kui-fx~=video-scrub]:has(> [data-kui-step-state]),\n .kui-frame-stack {\n position: relative;\n }\n [data-kui-fx~=sequence-scrub] > [data-kui-step-state],\n [data-kui-fx~=video-scrub] > [data-kui-step-state],\n .kui-frame-stack > [data-kui-step-state] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n opacity: 0;\n }\n [data-kui-fx~=sequence-scrub] > [data-kui-step-state=active],\n [data-kui-fx~=video-scrub] > [data-kui-step-state=active],\n .kui-frame-stack > [data-kui-step-state=active] {\n opacity: 1;\n }\n @supports (animation-timeline: view()) {\n [data-kui-timeline] {\n animation-range: var(--kui-range, entry 0% cover 60%);\n }\n }\n}\n\n/* src/css/three-d.css */\n@layer kui.effects {\n @keyframes kui-card-flip-y {\n to {\n transform: perspective(var(--kui-perspective, 1200px)) rotateY(var(--kui-from-angle, 180deg));\n }\n }\n @keyframes kui-card-flip-x {\n to {\n transform: perspective(var(--kui-perspective, 1200px)) rotateX(var(--kui-from-angle, 180deg));\n }\n }\n @keyframes kui-cube-rotate {\n to {\n transform: perspective(var(--kui-perspective, 1200px)) rotateY(var(--kui-from-angle, 90deg));\n }\n }\n @keyframes kui-book-page-turn {\n to {\n transform: perspective(var(--kui-perspective, 1200px)) rotateY(var(--kui-from-angle, -160deg));\n }\n }\n @keyframes kui-fold-panel {\n from {\n transform: perspective(var(--kui-perspective, 1200px)) rotateX(var(--kui-from-angle, -90deg));\n }\n }\n [data-kui-fx~=card-flip-x],\n [data-kui-fx~=card-flip-y],\n [data-kui-fx~=cube-rotate],\n [data-kui-fx~=book-page-turn],\n [data-kui-fx~=fold-panel] {\n transform-style: preserve-3d;\n perspective: var(--kui-perspective, 1200px);\n }\n [data-kui-fx~=card-flip-x]:not(:has(> :nth-child(2))),\n [data-kui-fx~=card-flip-y]:not(:has(> :nth-child(2))) {\n --kui-from-angle: 360deg;\n }\n [data-kui-fx~=card-flip-x]:has(> :nth-child(2)),\n [data-kui-fx~=card-flip-y]:has(> :nth-child(2)) {\n display: grid;\n }\n [data-kui-fx~=card-flip-x]:has(> :nth-child(2)) > :first-child,\n [data-kui-fx~=card-flip-x]:has(> :nth-child(2)) > :nth-child(2),\n [data-kui-fx~=card-flip-y]:has(> :nth-child(2)) > :first-child,\n [data-kui-fx~=card-flip-y]:has(> :nth-child(2)) > :nth-child(2) {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n }\n [data-kui-fx~=card-flip-x]:has(> :nth-child(2)) > :nth-child(2) {\n rotate: x 180deg;\n }\n [data-kui-fx~=card-flip-y]:has(> :nth-child(2)) > :nth-child(2) {\n rotate: y 180deg;\n }\n [data-kui-fx~=book-page-turn] {\n transform-origin: left center;\n }\n [data-kui-fx~=fold-panel] {\n transform-origin: top center;\n }\n [data-kui-fx~=fold-panel][data-kui-state=ready] {\n --kui-from-angle: 0deg !important;\n opacity: 0;\n }\n @keyframes kui-page-fade {\n from {\n opacity: 0;\n }\n }\n @keyframes kui-page-slide {\n from {\n opacity: 0;\n translate: 0 var(--kui-distance, 40px);\n }\n }\n @keyframes kui-curtain-wipe {\n from {\n clip-path: inset(0 0 100% 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-loading-bar {\n from {\n scale: var(--kui-bar-from, 0) 1;\n }\n }\n [data-kui-fx~=loading-bar] {\n transform-origin: left center;\n }\n [data-kui-fx~=loading-bar][data-kui-state=ready] {\n --kui-bar-from: 1 !important;\n opacity: 0;\n }\n [data-kui-fx~=flip-card] {\n display: grid;\n perspective: var(--kui-perspective, 1600px);\n }\n [data-kui-fx~=flip-card] > .kui-face-front,\n [data-kui-fx~=flip-card] > .kui-face-back {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n transition: rotate var(--kui-card-toggle-duration, 700ms) var(--kui-card-toggle-ease, ease-in-out);\n }\n [data-kui-fx~=flip-card] > .kui-face-front {\n rotate: y 0deg;\n }\n [data-kui-fx~=flip-card] > .kui-face-back {\n rotate: y 180deg;\n }\n [data-kui-fx~=flip-card]:has(> .kui-flip-control[aria-pressed=true]) > .kui-face-front {\n rotate: y -180deg;\n }\n [data-kui-fx~=flip-card]:has(> .kui-flip-control[aria-pressed=true]) > .kui-face-back {\n rotate: y 0deg;\n }\n [data-kui~=flip-card] {\n display: grid;\n perspective: var(--kui-perspective, 1600px);\n }\n [data-kui~=flip-card] > .kui-face-front,\n [data-kui~=flip-card] > .kui-face-back {\n grid-area: 1 / 1;\n backface-visibility: hidden;\n }\n [data-kui~=flip-card] > .kui-face-front {\n rotate: y 0deg;\n }\n [data-kui~=flip-card] > .kui-face-back {\n rotate: y 180deg;\n }\n}\n\n/* src/css/media.css */\n@layer kui.effects {\n @keyframes kui-wipe-up {\n from {\n clip-path: inset(100% 0 0);\n }\n to {\n clip-path: inset(0 0 0);\n }\n }\n @keyframes kui-wipe-down {\n from {\n clip-path: inset(0 0 100%);\n }\n to {\n clip-path: inset(0 0 0);\n }\n }\n @keyframes kui-wipe-left {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-wipe-right {\n from {\n clip-path: inset(0 0 0 100%);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-wipe-circle {\n from {\n clip-path: circle(0 at 50% 50%);\n }\n to {\n clip-path: circle(75% at 50% 50%);\n }\n }\n @keyframes kui-wipe-diagonal {\n from {\n clip-path: polygon(0 0, 0 0, 0 0);\n }\n to {\n clip-path: polygon(0 0, 200% 0, 0 200%);\n }\n }\n @keyframes kui-curtain-reveal {\n from {\n clip-path: inset(0 50%);\n }\n to {\n clip-path: inset(0 0);\n }\n }\n @keyframes kui-before-after-wipe {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-mask-reveal {\n from {\n mask-position: 100% 0;\n }\n to {\n mask-position: 0 0;\n }\n }\n [data-kui-fx~=mask-reveal] {\n mask-image:\n linear-gradient(\n 90deg,\n #000 0 45%,\n transparent 55% 100%);\n mask-size: 220% 100%;\n }\n @keyframes kui-ken-burns {\n from {\n scale: 1;\n translate: 0 0;\n }\n to {\n scale: var(--kui-to-scale, 1.12);\n translate: var(--kui-distance, 24px) calc(var(--kui-distance, 24px) * -0.5);\n }\n }\n @keyframes kui-ken-burns-out {\n from {\n scale: var(--kui-to-scale, 1.12);\n translate: calc(var(--kui-distance, 24px) * -1) 0;\n }\n to {\n scale: 1;\n translate: 0 0;\n }\n }\n @keyframes kui-blur-up {\n from {\n translate: 0 var(--kui-distance, 24px);\n filter: blur(var(--kui-blur, 16px));\n }\n to {\n translate: 0 0;\n filter: blur(0);\n }\n }\n @keyframes kui-duotone-hover {\n from {\n filter: grayscale(1) sepia(0.7) hue-rotate(330deg) saturate(2);\n }\n to {\n filter: none;\n }\n }\n @keyframes kui-grayscale-hover {\n from {\n filter: grayscale(1);\n }\n to {\n filter: grayscale(0);\n }\n }\n @keyframes kui-saturate-hover {\n from {\n filter: saturate(0.45);\n }\n to {\n filter: saturate(1.35);\n }\n }\n @keyframes kui-image-parallax-frame {\n from {\n translate: 0 calc(var(--kui-distance, 24px) * -1);\n }\n to {\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-lightbox-open {\n from {\n opacity: 0;\n scale: var(--kui-from-scale, 0.92);\n }\n to {\n opacity: 1;\n scale: 1;\n }\n }\n @media (pointer: coarse) {\n [data-kui-fx~=duotone-hover]:active,\n [data-kui-fx~=grayscale-hover]:active,\n [data-kui-fx~=saturate-hover]:active {\n filter: none;\n }\n }\n .kui-slat-stage {\n position: absolute;\n overflow: clip;\n }\n .kui-slat-item {\n position: absolute;\n inset: 0;\n background-repeat: no-repeat;\n background-size: 100% 100%;\n animation-duration: var(--kui-duration, 500ms);\n animation-delay: calc(var(--kui-delay, 0ms) + var(--kui-i, 0) * var(--kui-stagger, 60ms));\n animation-timing-function: var(--kui-ease, ease-out);\n animation-fill-mode: both;\n }\n .kui-slat-stage.kui-slat-animating .kui-slat-item {\n will-change: transform;\n }\n .kui-slat-item:nth-child(odd) {\n --kui-slat-sign: -1;\n }\n .kui-slat-item:nth-child(even) {\n --kui-slat-sign: 1;\n }\n .kui-slat-stage[data-kui-slat-fold=true] {\n perspective: 1200px;\n }\n .kui-slat-stage:not([data-kui-slat-fold=true]) .kui-slat-item {\n animation-name: kui-slat-assemble;\n }\n .kui-slat-stage[data-kui-slat-fold=true] .kui-slat-item {\n animation-name: kui-slat-assemble-fold;\n }\n @keyframes kui-slat-assemble {\n from {\n opacity: 0;\n translate: calc(var(--kui-slat-sign, 1) * var(--kui-slat-dx, 0) * 38%) calc(var(--kui-slat-sign, 1) * var(--kui-slat-dy, 1) * 38%);\n }\n 12% {\n opacity: 1;\n }\n to {\n opacity: 1;\n translate: 0 0;\n }\n }\n @keyframes kui-slat-assemble-fold {\n from {\n opacity: 0;\n translate: calc(var(--kui-slat-sign, 1) * var(--kui-slat-dx, 0) * 38%) calc(var(--kui-slat-sign, 1) * var(--kui-slat-dy, 1) * 38%);\n rotate: var(--kui-slat-dx, 0) var(--kui-slat-dy, 1) 0 calc(var(--kui-slat-sign, 1) * 38deg);\n }\n 12% {\n opacity: 1;\n }\n to {\n opacity: 1;\n translate: 0 0;\n rotate: var(--kui-slat-dx, 0) var(--kui-slat-dy, 1) 0 0deg;\n }\n }\n}\n\n/* src/css/text.css */\n@layer kui.effects {\n .kui-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n .kui-split-decorative {\n display: inline;\n }\n .kui-split-item {\n display: inline-block;\n animation-duration: var(--kui-duration, 500ms);\n animation-delay: calc(var(--kui-delay, 0ms) + var(--kui-i, 0) * var(--kui-stagger, 30ms));\n animation-timing-function: var(--kui-ease, ease-out);\n animation-fill-mode: both;\n }\n .kui-split-word {\n display: inline-block;\n }\n .kui-split-line {\n display: block;\n }\n [data-kui-split-fx=fade] .kui-split-item {\n animation-name: kui-split-reveal-fade;\n }\n [data-kui-split-fx=up] .kui-split-item {\n animation-name: kui-split-reveal-up;\n }\n [data-kui-split-fx=down] .kui-split-item {\n animation-name: kui-split-reveal-down;\n }\n [data-kui-split-fx=mask] .kui-split-item {\n animation-name: kui-split-reveal-mask;\n padding-block: var(--kui-mask-bleed, 0px);\n margin-block: calc(-1 * var(--kui-mask-bleed, 0px));\n }\n @keyframes kui-split-reveal-fade {\n from {\n opacity: 0;\n }\n }\n @keyframes kui-split-reveal-up {\n from {\n opacity: 0;\n translate: 0 0.6em;\n }\n }\n @keyframes kui-split-reveal-down {\n from {\n opacity: 0;\n translate: 0 -0.6em;\n }\n }\n @keyframes kui-split-reveal-mask {\n from {\n opacity: 0;\n clip-path: inset(0 0 100% 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n [data-kui-split-fx=wave] .kui-split-item {\n animation-name: kui-split-wave;\n animation-duration: 1400ms;\n animation-iteration-count: infinite;\n animation-timing-function: ease-in-out;\n }\n [data-kui-split-fx=jitter] .kui-split-item {\n animation-name: kui-split-jitter;\n animation-duration: 220ms;\n animation-iteration-count: infinite;\n animation-timing-function: ease-in-out;\n animation-direction: alternate;\n }\n @keyframes kui-split-wave {\n 0%, 100% {\n translate: 0 0;\n }\n 50% {\n translate: 0 -0.35em;\n }\n }\n @keyframes kui-split-jitter {\n from {\n translate: 0 0;\n rotate: 0deg;\n }\n to {\n translate: 0 0.08em;\n rotate: 3deg;\n }\n }\n .kui-typewriter::after {\n content: \"\";\n display: inline-block;\n width: 0.08em;\n margin-inline-start: 0.05em;\n height: 1em;\n vertical-align: -0.15em;\n background: currentColor;\n animation: kui-caret-blink 1s steps(1) infinite;\n }\n @keyframes kui-caret-blink {\n 50% {\n opacity: 0;\n }\n }\n .kui-scramble {\n font-variant-numeric: tabular-nums;\n }\n [data-kui-fx~=word-cycler] {\n display: inline-block;\n transition: opacity 150ms ease-out;\n }\n [data-kui-fx~=word-cycler].kui-word-cycler-swap {\n opacity: 0;\n }\n [data-kui-fx~=gradient-shimmer] {\n background-image:\n linear-gradient(\n 100deg,\n currentColor 40%,\n color-mix(in srgb, currentColor 35%, transparent) 50%,\n currentColor 60%);\n background-size: 220% 100%;\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n --kui-fx-gradient-shimmer-iterations: infinite;\n }\n @keyframes kui-gradient-shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n [data-kui-fx~=gradient-sweep] {\n background-image:\n linear-gradient(\n 100deg,\n transparent 30%,\n color-mix(in srgb, var(--kui-sweep-color, currentColor) 70%, transparent) 50%,\n transparent 70%);\n background-size: 260% 100%;\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n }\n @keyframes kui-gradient-sweep {\n from {\n background-position: 220% 0;\n }\n to {\n background-position: -60% 0;\n }\n }\n [data-kui-fx~=highlight-sweep] {\n background-image: linear-gradient(90deg, color-mix(in srgb, var(--kui-sweep-color, currentColor) 55%, transparent) 0 100%);\n background-repeat: no-repeat;\n background-size: 0% 100%;\n padding: 0 0.05em;\n }\n @keyframes kui-highlight-sweep {\n to {\n background-size: 100% 100%;\n }\n }\n [data-kui-fx~=underline-draw] {\n background-image: linear-gradient(var(--kui-sweep-color, currentColor), var(--kui-sweep-color, currentColor));\n background-repeat: no-repeat;\n background-position: 0 100%;\n background-size: 0% 2px;\n padding-bottom: 0.15em;\n }\n @keyframes kui-underline-draw {\n to {\n background-size: 100% 2px;\n }\n }\n [data-kui-fx~=text-outline-fill] {\n -webkit-text-stroke: 1px currentColor;\n -webkit-text-fill-color: transparent;\n }\n @keyframes kui-text-outline-fill {\n to {\n -webkit-text-fill-color: currentColor;\n }\n }\n @keyframes kui-var-weight {\n from {\n font-weight: var(--kui-from-weight, 100);\n }\n to {\n font-weight: var(--kui-to-weight, 800);\n }\n }\n @keyframes kui-var-width {\n from {\n font-stretch: var(--kui-from-width, 75%);\n }\n to {\n font-stretch: var(--kui-to-width, 125%);\n }\n }\n @keyframes kui-var-slant {\n from {\n font-style: oblique var(--kui-from-slant, 0deg);\n }\n to {\n font-style: oblique var(--kui-to-slant, 10deg);\n }\n }\n [data-kui-fx~=marquee],\n [data-kui-fx~=marquee-scroll-linked] {\n display: flex;\n width: max-content;\n will-change: translate;\n }\n [data-kui-fx~=marquee] {\n --kui-fx-marquee-iterations: infinite;\n }\n @keyframes kui-marquee {\n to {\n translate: -50% 0;\n }\n }\n @property --kui-redaction-x { syntax: \"<percentage>\"; inherits: true; initial-value: 0%; }\n [data-kui-fx~=redaction-reveal] {\n position: relative;\n display: inline-block;\n }\n [data-kui-fx~=redaction-reveal]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background: var(--kui-bar-color, #111);\n clip-path: inset(0 0 0 var(--kui-redaction-x));\n }\n @keyframes kui-redaction-reveal {\n to {\n --kui-redaction-x: 100%;\n }\n }\n [data-kui-fx~=text-3d-extrude] {\n display: inline-block;\n text-shadow:\n 1px 1px 0 color-mix(in srgb, currentColor 70%, transparent),\n 2px 2px 0 color-mix(in srgb, currentColor 60%, transparent),\n 3px 3px 0 color-mix(in srgb, currentColor 50%, transparent),\n 4px 4px 0 color-mix(in srgb, currentColor 40%, transparent),\n 5px 5px 0 color-mix(in srgb, currentColor 30%, transparent),\n 6px 6px 8px rgb(0 0 0 / 0.35);\n }\n @keyframes kui-text-3d-extrude {\n from {\n rotate: var(--kui-from-angle, -20deg);\n translate: 0 var(--kui-distance, 32px);\n }\n to {\n rotate: 0deg;\n translate: 0 0;\n }\n }\n}\n\n/* src/css/navigation.css */\n@layer kui.effects {\n @keyframes kui-nav-reveal {\n from {\n opacity: 0;\n translate: 0 var(--kui-distance, 16px);\n }\n }\n @keyframes kui-menu-fullscreen {\n from {\n opacity: 0;\n clip-path: circle(0% at var(--kui-origin, 100% 0%));\n }\n to {\n opacity: 1;\n clip-path: circle(150% at var(--kui-origin, 100% 0%));\n }\n }\n @keyframes kui-panel-reveal {\n from {\n opacity: 0;\n translate: 0 calc(var(--kui-distance, 10px) * -1);\n }\n }\n @keyframes kui-drawer-slide-right {\n from {\n translate: 100% 0;\n }\n }\n [data-kui-fx~=header-shrink] {\n --kui-shrink: 0;\n padding-block: calc(1.4rem - 0.7rem * var(--kui-shrink, 0));\n font-size: calc(1rem - 0.15rem * var(--kui-shrink, 0));\n transition:\n padding-block 200ms ease-out,\n font-size 200ms ease-out,\n box-shadow 200ms ease-out;\n }\n [data-kui-fx~=header-shrink][data-kui-shrunk=true] {\n box-shadow: 0 2px 10px rgb(0 0 0 / 0.12);\n }\n [data-kui-fx~=header-hide-on-scroll] {\n transition: translate 220ms ease-out;\n }\n [data-kui-fx~=header-hide-on-scroll][data-kui-hidden=true] {\n translate: 0 -100%;\n }\n [data-kui-fx~=back-to-top-fade] {\n opacity: 0;\n translate: 0 12px;\n pointer-events: none;\n transition: opacity 200ms ease-out, translate 200ms ease-out;\n }\n [data-kui-fx~=back-to-top-fade][data-kui-visible=true] {\n opacity: 1;\n translate: 0 0;\n pointer-events: auto;\n }\n}\n\n/* src/css/forms.css */\n@layer kui.effects {\n [data-kui-fx~=label-float] ~ label {\n transition: translate 180ms ease-out, scale 180ms ease-out;\n transform-origin: left center;\n }\n [data-kui-fx~=label-float]:focus ~ label,\n [data-kui-fx~=label-float]:not(:placeholder-shown) ~ label {\n translate: 0 -1.35rem;\n scale: 0.82;\n }\n [data-kui-fx~=input-underline-grow] ~ .kui-underline {\n display: block;\n height: 2px;\n background: currentColor;\n scale: 0 1;\n transform-origin: left center;\n transition: scale 220ms ease-out;\n }\n [data-kui-fx~=input-underline-grow]:focus ~ .kui-underline {\n scale: 1 1;\n }\n @keyframes kui-focus-ring-grow {\n from {\n box-shadow: 0 0 0 0 var(--kui-ring-color, rgb(210 105 30 / 0.55));\n }\n to {\n box-shadow: 0 0 0 5px var(--kui-ring-color, rgb(210 105 30 / 0.55));\n }\n }\n @keyframes kui-validate-shake {\n 10%, 90% {\n translate: -1px 0;\n }\n 20%, 80% {\n translate: 2px 0;\n }\n 30%, 50%, 70% {\n translate: -4px 0;\n }\n 40%, 60% {\n translate: 4px 0;\n }\n }\n [data-kui-fx~=validate-check] {\n stroke-dasharray: 24;\n stroke-dashoffset: 24;\n }\n @keyframes kui-validate-check {\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=strength-meter] ~ .kui-meter > * {\n background: var(--kui-meter-off, currentColor);\n opacity: 0.2;\n transition: opacity 200ms ease-out, background-color 200ms ease-out;\n }\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"1\"] ~ .kui-meter > *:nth-child(-n+1),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"2\"] ~ .kui-meter > *:nth-child(-n+2),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"3\"] ~ .kui-meter > *:nth-child(-n+3),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"4\"] ~ .kui-meter > *:nth-child(-n+4),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"5\"] ~ .kui-meter > *:nth-child(-n+5),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"6\"] ~ .kui-meter > *:nth-child(-n+6),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"7\"] ~ .kui-meter > *:nth-child(-n+7),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"8\"] ~ .kui-meter > *:nth-child(-n+8),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"9\"] ~ .kui-meter > *:nth-child(-n+9),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"10\"] ~ .kui-meter > *:nth-child(-n+10),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"11\"] ~ .kui-meter > *:nth-child(-n+11),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"12\"] ~ .kui-meter > *:nth-child(-n+12),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"13\"] ~ .kui-meter > *:nth-child(-n+13),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"14\"] ~ .kui-meter > *:nth-child(-n+14),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"15\"] ~ .kui-meter > *:nth-child(-n+15),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"16\"] ~ .kui-meter > *:nth-child(-n+16),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"17\"] ~ .kui-meter > *:nth-child(-n+17),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"18\"] ~ .kui-meter > *:nth-child(-n+18),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"19\"] ~ .kui-meter > *:nth-child(-n+19),\n [data-kui-fx~=strength-meter][data-kui-strength-level=\"20\"] ~ .kui-meter > *:nth-child(-n+20) {\n opacity: 1;\n background: var(--kui-meter-on, #2e9e5b);\n }\n [data-kui-fx~=toggle-morph] ~ .kui-track {\n display: inline-block;\n width: calc(40px * var(--kui-toggle-scale, 1));\n height: calc(22px * var(--kui-toggle-scale, 1));\n border-radius: 999px;\n background: var(--muted, #9a8d80);\n transition: background-color 200ms ease-out;\n position: relative;\n }\n [data-kui-fx~=toggle-morph] ~ .kui-track .kui-thumb {\n position: absolute;\n top: calc(2px * var(--kui-toggle-scale, 1));\n left: calc(2px * var(--kui-toggle-scale, 1));\n width: calc(18px * var(--kui-toggle-scale, 1));\n height: calc(18px * var(--kui-toggle-scale, 1));\n border-radius: 50%;\n background: var(--kui-thumb-color, #fff);\n translate: 0 0;\n transition: translate 200ms ease-out;\n }\n [data-kui-fx~=toggle-morph]:checked ~ .kui-track {\n background: var(--accent, #d2691e);\n }\n [data-kui-fx~=toggle-morph]:checked ~ .kui-track .kui-thumb {\n translate: calc(18px * var(--kui-toggle-scale, 1)) 0;\n }\n [data-kui-fx~=checkbox-draw] ~ svg path {\n stroke-dasharray: 24;\n stroke-dashoffset: 24;\n transition: stroke-dashoffset 220ms ease-out;\n }\n [data-kui-fx~=checkbox-draw]:checked ~ svg path {\n stroke-dashoffset: 0;\n }\n [data-kui-fx~=radio-fill] ~ .kui-dot {\n display: inline-block;\n width: calc(10px * var(--kui-radio-scale, 1));\n height: calc(10px * var(--kui-radio-scale, 1));\n border-radius: 50%;\n background: var(--accent, #d2691e);\n scale: 0;\n transition: scale 180ms ease-out;\n }\n [data-kui-fx~=radio-fill]:checked ~ .kui-dot {\n scale: 1;\n }\n [data-kui-fx~=range-fill] {\n --kui-fill: 0%;\n accent-color: var(--accent, #d2691e);\n background:\n linear-gradient(\n to right,\n var(--accent, #d2691e) var(--kui-fill, 0%),\n var(--border, #2a2422) var(--kui-fill, 0%));\n height: 4px;\n border-radius: 999px;\n appearance: none;\n outline: none;\n }\n [data-kui-fx~=submit-to-spinner-to-check] .kui-stage-idle,\n [data-kui-fx~=submit-to-spinner-to-check] .kui-stage-loading,\n [data-kui-fx~=submit-to-spinner-to-check] .kui-stage-done {\n display: none;\n }\n [data-kui-fx~=submit-to-spinner-to-check][data-kui-stage=idle] .kui-stage-idle,\n [data-kui-fx~=submit-to-spinner-to-check][data-kui-stage=loading] .kui-stage-loading,\n [data-kui-fx~=submit-to-spinner-to-check][data-kui-stage=done] .kui-stage-done {\n display: inline-flex;\n }\n [data-kui-fx~=submit-to-spinner-to-check] .kui-spinner {\n width: 1em;\n height: 1em;\n border-radius: 50%;\n border: 2px solid currentColor;\n border-top-color: transparent;\n animation: kui-spin 700ms linear infinite;\n }\n @keyframes kui-spin {\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=step-progress] {\n cursor: pointer;\n }\n [data-kui-fx~=step-progress] > [data-kui-step-state],\n .kui-step-track > [data-kui-step-state] {\n background: var(--muted, #9a8d80);\n opacity: 0.35;\n transition: opacity 200ms ease-out, background-color 200ms ease-out;\n }\n [data-kui-fx~=step-progress] > [data-kui-step-state=before],\n [data-kui-fx~=step-progress] > [data-kui-step-state=active],\n .kui-step-track > [data-kui-step-state=before],\n .kui-step-track > [data-kui-step-state=active] {\n opacity: 1;\n background: var(--accent, #d2691e);\n }\n}\n\n/* src/css/ambient.css */\n@layer kui.effects {\n [data-kui-fx~=gradient-mesh] {\n background-image:\n radial-gradient(\n at 15% 20%,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #7c3aed)) 55%, transparent),\n transparent 55%),\n radial-gradient(\n at 85% 15%,\n color-mix(in srgb, var(--kui-ambient-c2, var(--kui-c2, #06b6d4)) 50%, transparent),\n transparent 55%),\n radial-gradient(\n at 50% 85%,\n color-mix(in srgb, var(--kui-ambient-c3, var(--kui-c3, #f97316)) 45%, transparent),\n transparent 55%);\n background-size: 200% 200%;\n --kui-fx-gradient-mesh-iterations: infinite;\n }\n @keyframes kui-gradient-mesh {\n 0%, 100% {\n background-position:\n 0% 0%,\n 100% 0%,\n 50% 100%;\n }\n 50% {\n background-position:\n 25% 35%,\n 75% 30%,\n 35% 70%;\n }\n }\n [data-kui-fx~=aurora] {\n background-image:\n linear-gradient(\n 120deg,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #34d399)) 45%, transparent),\n transparent 45%),\n linear-gradient(\n 240deg,\n color-mix(in srgb, var(--kui-ambient-c2, var(--kui-c2, #60a5fa)) 45%, transparent),\n transparent 45%),\n linear-gradient(\n 0deg,\n color-mix(in srgb, var(--kui-ambient-c3, var(--kui-c3, #c084fc)) 40%, transparent),\n transparent 50%);\n background-size: 260% 260%;\n --kui-fx-aurora-iterations: infinite;\n }\n @keyframes kui-aurora {\n 0%, 100% {\n background-position:\n 0% 50%,\n 100% 50%,\n 50% 0%;\n }\n 50% {\n background-position:\n 60% 30%,\n 40% 70%,\n 55% 60%;\n }\n }\n [data-kui-fx~=wave-blob] {\n background-image:\n radial-gradient(\n circle,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #22d3ee)) 50%, transparent),\n transparent 60%);\n background-size: 140% 140%;\n background-repeat: no-repeat;\n --kui-fx-wave-blob-iterations: infinite;\n }\n @keyframes kui-wave-blob {\n 0%, 100% {\n background-position: 20% 30%;\n background-size: 140% 140%;\n }\n 33% {\n background-position: 70% 20%;\n background-size: 160% 160%;\n }\n 66% {\n background-position: 50% 75%;\n background-size: 120% 120%;\n }\n }\n [data-kui-fx~=spotlight-follow] {\n background-image:\n radial-gradient(\n circle,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #fde68a)) 65%, transparent),\n transparent 65%);\n background-size: 55% 55%;\n background-repeat: no-repeat;\n --kui-fx-spotlight-follow-iterations: infinite;\n }\n @keyframes kui-spotlight-follow {\n 0%, 100% {\n background-position: 10% 15%;\n }\n 25% {\n background-position: 85% 20%;\n }\n 50% {\n background-position: 80% 80%;\n }\n 75% {\n background-position: 15% 75%;\n }\n }\n [data-kui-fx~=gradient-rotate-border] {\n position: relative;\n background-image:\n linear-gradient(\n 90deg,\n var(--kui-ambient-c1, var(--kui-c1, #f472b6)),\n var(--kui-ambient-c2, var(--kui-c2, #60a5fa)),\n var(--kui-ambient-c3, var(--kui-c3, #34d399)),\n var(--kui-ambient-c4, var(--kui-c4, #fbbf24)),\n var(--kui-ambient-c1, var(--kui-c1, #f472b6)));\n background-size: 300% 100%;\n padding: 3px;\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask-composite: xor;\n --kui-fx-gradient-rotate-border-iterations: infinite;\n }\n @keyframes kui-gradient-rotate-border {\n from {\n background-position: 0% 50%;\n }\n to {\n background-position: 300% 50%;\n }\n }\n [data-kui-fx~=gradient-border] {\n position: relative;\n background-image:\n linear-gradient(\n 90deg,\n var(--kui-ambient-c1, var(--kui-c1, #7c3aed)),\n var(--kui-ambient-c2, var(--kui-c2, #fbbf24)),\n var(--kui-ambient-c1, var(--kui-c1, #7c3aed)));\n background-size: 300% 100%;\n padding: var(--kui-gradient-border-width, 3px);\n mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n mask-composite: exclude;\n -webkit-mask-composite: xor;\n --kui-fx-gradient-border-iterations: infinite;\n }\n @keyframes kui-gradient-border {\n from {\n background-position: 0% 50%;\n }\n to {\n background-position: 300% 50%;\n }\n }\n [data-kui-fx~=noise-overlay] {\n background-image:\n repeating-radial-gradient(\n circle at 0 0,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, currentColor)) 30%, transparent) 0,\n transparent 1.4px,\n transparent 3px);\n background-size: 5px 5px;\n --kui-fx-noise-overlay-iterations: infinite;\n }\n @keyframes kui-noise-overlay {\n 0% {\n background-position: 0 0;\n }\n 20% {\n background-position: -3px 1px;\n }\n 40% {\n background-position: 2px -2px;\n }\n 60% {\n background-position: -1px 3px;\n }\n 80% {\n background-position: 3px 2px;\n }\n 100% {\n background-position: 0 0;\n }\n }\n [data-kui-fx~=scanline] {\n background-image:\n linear-gradient(\n to bottom,\n transparent,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, currentColor)) 35%, transparent) 50%,\n transparent);\n background-size: 100% 220%;\n background-repeat: no-repeat;\n --kui-fx-scanline-iterations: infinite;\n }\n @keyframes kui-scanline {\n from {\n background-position: 0 -120%;\n }\n to {\n background-position: 0 120%;\n }\n }\n [data-kui-fx~=dot-grid-drift] {\n background-image:\n radial-gradient(\n circle,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, currentColor)) 45%, transparent) 1.5px,\n transparent 1.5px);\n background-size: 22px 22px;\n --kui-fx-dot-grid-drift-iterations: infinite;\n }\n @keyframes kui-dot-grid-drift {\n from {\n background-position: 0 0;\n }\n to {\n background-position: 22px 22px;\n }\n }\n [data-kui-fx~=line-grid-drift] {\n background-image:\n repeating-linear-gradient(\n 0deg,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, currentColor)) 30%, transparent) 0 1px,\n transparent 1px 26px),\n repeating-linear-gradient(\n 90deg,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, currentColor)) 30%, transparent) 0 1px,\n transparent 1px 26px);\n --kui-fx-line-grid-drift-iterations: infinite;\n }\n @keyframes kui-line-grid-drift {\n from {\n background-position: 0 0, 0 0;\n }\n to {\n background-position: 0 26px, 26px 0;\n }\n }\n [data-kui-fx~=starfield] {\n background-image:\n radial-gradient(\n 1px 1px at 10% 20%,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #ffffff)) 90%, transparent),\n transparent),\n radial-gradient(\n 1px 1px at 70% 60%,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #ffffff)) 70%, transparent),\n transparent),\n radial-gradient(\n 1.5px 1.5px at 40% 80%,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #ffffff)) 85%, transparent),\n transparent),\n radial-gradient(\n 1px 1px at 90% 30%,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #ffffff)) 60%, transparent),\n transparent),\n radial-gradient(\n 1.5px 1.5px at 25% 55%,\n color-mix(in srgb, var(--kui-ambient-c1, var(--kui-c1, #ffffff)) 80%, transparent),\n transparent);\n background-size: 220px 220px;\n background-repeat: repeat;\n --kui-fx-starfield-iterations: infinite;\n }\n @keyframes kui-starfield {\n from {\n background-position:\n 0 0,\n 0 0,\n 0 0,\n 0 0,\n 0 0;\n }\n to {\n background-position:\n 220px 90px,\n -160px 220px,\n 90px -140px,\n -220px -90px,\n 140px 160px;\n }\n }\n [data-kui-fx~=orbit] {\n --kui-fx-orbit-iterations: infinite;\n }\n @keyframes kui-orbit {\n to {\n rotate: var(--kui-to-angle, 360deg);\n }\n }\n [data-kui-fx~=float] {\n --kui-fx-float-iterations: infinite;\n }\n @keyframes kui-float {\n 0%, 100% {\n translate: 0 0;\n }\n 50% {\n translate: 0 calc(var(--kui-distance, 14px) * -1);\n }\n }\n [data-kui-fx~=bob] {\n --kui-fx-bob-iterations: infinite;\n }\n @keyframes kui-bob {\n 0%, 100% {\n translate: 0 0;\n }\n 50% {\n translate: 0 calc(var(--kui-distance, 8px) * -1);\n }\n }\n [data-kui-fx~=floating-shapes] {\n --kui-fx-floating-shapes-iterations: infinite;\n }\n @keyframes kui-floating-shapes {\n 0%, 100% {\n translate: 0 0;\n }\n 33% {\n translate: calc(var(--kui-distance, 10px) * 0.6) calc(var(--kui-distance, 10px) * -1);\n }\n 66% {\n translate: calc(var(--kui-distance, 10px) * -0.6) calc(var(--kui-distance, 10px) * -0.4);\n }\n }\n [data-kui-fx~=glow-pulse] {\n --kui-fx-glow-pulse-iterations: infinite;\n }\n @keyframes kui-glow-pulse {\n 0%, 100% {\n scale: 1;\n opacity: 0.6;\n }\n 50% {\n scale: var(--kui-pulse-scale, 1.15);\n opacity: 1;\n }\n }\n}\n\n/* src/css/feedback.css */\n@layer kui.effects {\n [data-kui-fx~=skeleton-shimmer] {\n background-color: var(--kui-skeleton-base, #2a2422);\n background-image:\n linear-gradient(\n 100deg,\n transparent 30%,\n color-mix(in srgb, var(--kui-skeleton-highlight, #ffffff) 18%, transparent) 50%,\n transparent 70%);\n background-size: 200% 100%;\n --kui-fx-skeleton-shimmer-iterations: infinite;\n }\n @keyframes kui-skeleton-shimmer {\n from {\n background-position: 150% 0;\n }\n to {\n background-position: -50% 0;\n }\n }\n @keyframes kui-skeleton-to-content {\n from {\n opacity: 0;\n }\n }\n [data-kui-fx~=spinner] {\n display: inline-block;\n width: var(--kui-spinner-size, 28px);\n height: var(--kui-spinner-size, 28px);\n border-radius: 50%;\n border: 3px solid color-mix(in srgb, currentColor 20%, transparent);\n border-top-color: currentColor;\n --kui-fx-spinner-iterations: infinite;\n }\n @keyframes kui-spinner-spin {\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=spinner-ring] {\n display: inline-block;\n width: var(--kui-spinner-size, 28px);\n height: var(--kui-spinner-size, 28px);\n border-radius: 50%;\n border: 3px dashed color-mix(in srgb, currentColor 55%, transparent);\n --kui-fx-spinner-ring-iterations: infinite;\n }\n @keyframes kui-spinner-ring-spin {\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=spinner-dots] {\n display: inline-block;\n width: var(--kui-dot-size, 8px);\n height: var(--kui-dot-size, 8px);\n border-radius: 50%;\n background: currentColor;\n box-shadow: calc(var(--kui-dot-size, 8px) * -2) 0 currentColor, calc(var(--kui-dot-size, 8px) * 2) 0 currentColor;\n --kui-fx-spinner-dots-iterations: infinite;\n }\n @keyframes kui-spinner-dots {\n 0%, 80%, 100% {\n scale: 0.7;\n opacity: 0.5;\n }\n 40% {\n scale: 1.15;\n opacity: 1;\n }\n }\n [data-kui-fx~=progress-indeterminate] {\n transform-origin: 0% 50%;\n background: currentColor;\n border-radius: inherit;\n --kui-fx-progress-indeterminate-iterations: infinite;\n }\n @keyframes kui-progress-indeterminate {\n 0% {\n translate: -100% 0;\n scale: 0.4 1;\n }\n 50% {\n scale: 1 1;\n }\n 100% {\n translate: 100% 0;\n scale: 0.4 1;\n }\n }\n @keyframes kui-toast-slide-in {\n from {\n opacity: 0;\n translate: 0 var(--kui-distance, 24px);\n }\n to {\n opacity: 1;\n translate: 0 0;\n }\n }\n @keyframes kui-toast-slide-out {\n from {\n opacity: 1;\n translate: 0 0;\n }\n to {\n opacity: 0;\n translate: 0 var(--kui-distance, 24px);\n }\n }\n @keyframes kui-shake-error {\n 10%, 90% {\n translate: -1px 0;\n }\n 20%, 80% {\n translate: 2px 0;\n }\n 30%, 50%, 70% {\n translate: -4px 0;\n }\n 40%, 60% {\n translate: 4px 0;\n }\n }\n @keyframes kui-wobble {\n 0% {\n translate: 0 0;\n rotate: 0deg;\n }\n 15% {\n translate: -6px 0;\n rotate: -6deg;\n }\n 30% {\n translate: 4px 0;\n rotate: 4deg;\n }\n 45% {\n translate: -3px 0;\n rotate: -3deg;\n }\n 60% {\n translate: 2px 0;\n rotate: 2deg;\n }\n 75% {\n translate: -1px 0;\n rotate: -1deg;\n }\n 100% {\n translate: 0 0;\n rotate: 0deg;\n }\n }\n [data-kui-fx~=ripple] {\n border-radius: 50%;\n background: currentColor;\n transform-origin: center;\n }\n @keyframes kui-ripple {\n from {\n scale: 0.2;\n opacity: 0.6;\n }\n to {\n scale: var(--kui-ripple-scale, 4);\n opacity: 0;\n }\n }\n @keyframes kui-badge-pop {\n 0% {\n scale: 0.4;\n }\n 60% {\n scale: var(--kui-pop-scale, 1.18);\n }\n 100% {\n scale: 1;\n }\n }\n @keyframes kui-count-bump {\n 0%, 100% {\n scale: 1;\n }\n 50% {\n scale: var(--kui-pop-scale, 1.18);\n }\n }\n [data-kui-fx~=heart-burst] {\n color: var(--kui-heart-color, #f43f5e);\n }\n @keyframes kui-heart-burst {\n 0% {\n scale: 1;\n }\n 30% {\n scale: var(--kui-pop-scale, 1.18);\n }\n 55% {\n scale: 0.9;\n }\n 100% {\n scale: 1;\n }\n }\n [data-kui-fx~=confetti-burst] {\n background-image:\n radial-gradient(\n circle at 20% 20%,\n var(--kui-c1, #f43f5e) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 75% 15%,\n var(--kui-c2, #fbbf24) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 50% 75%,\n var(--kui-c3, #34d399) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 85% 65%,\n var(--kui-c4, #60a5fa) 0 3px,\n transparent 3px),\n radial-gradient(\n circle at 15% 65%,\n var(--kui-c5, #c084fc) 0 3px,\n transparent 3px);\n background-repeat: no-repeat;\n }\n @keyframes kui-confetti-burst {\n 0% {\n scale: 0.6;\n opacity: 0;\n }\n 40% {\n scale: var(--kui-pop-scale, 1.18);\n opacity: 1;\n }\n 100% {\n scale: 1;\n opacity: 1;\n }\n }\n @keyframes kui-copy-confirm {\n 0% {\n opacity: 0;\n }\n 15% {\n opacity: 1;\n }\n 85% {\n opacity: 1;\n }\n 100% {\n opacity: 0;\n }\n }\n @keyframes kui-pull-to-refresh {\n 0% {\n translate: 0 0;\n }\n 45% {\n translate: 0 var(--kui-pull-distance, 36px);\n }\n 100% {\n translate: 0 0;\n }\n }\n}\n\n/* src/css/numbers.css */\n@layer kui.effects {\n @keyframes kui-progress-ring {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-gauge-sweep {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-donut-sweep {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-sparkline-draw {\n from {\n stroke-dashoffset: var(--kui-path-length, 1000);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=progress-ring],\n [data-kui-fx~=gauge-sweep],\n [data-kui-fx~=donut-sweep],\n [data-kui-fx~=sparkline-draw] {\n stroke-dasharray: var(--kui-path-length, 100);\n fill: none;\n }\n @keyframes kui-progress-bar {\n from {\n scale: var(--kui-bar-from, 0) 1;\n }\n to {\n scale: 1 1;\n }\n }\n [data-kui-fx~=progress-bar] {\n transform-origin: left center;\n }\n [data-kui-fx~=progress-bar][data-kui-state=ready] {\n --kui-bar-from: 1 !important;\n opacity: 0;\n }\n @keyframes kui-progress-segments {\n from {\n opacity: 0.18;\n }\n to {\n opacity: 1;\n }\n }\n @keyframes kui-star-rating-fill {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 calc(100% - var(--kui-fill, 100%)) 0 0);\n }\n }\n .kui-count-decorative {\n display: inline-flex;\n align-items: baseline;\n }\n .kui-odometer-col {\n display: inline-block;\n overflow: hidden;\n height: 1em;\n line-height: 1;\n vertical-align: baseline;\n }\n .kui-odometer-strip {\n display: flex;\n flex-direction: column;\n translate: 0 calc(var(--kui-o, 0) * -1em);\n transition: translate 220ms ease-out;\n }\n .kui-odometer-strip > span {\n height: 1em;\n line-height: 1;\n }\n}\n\n/* src/css/interaction.css */\n@layer kui.effects {\n @property --kui-border-angle { syntax: \"<angle>\"; inherits: false; initial-value: 0deg; }\n @property --kui-border-pct { syntax: \"<percentage>\"; inherits: false; initial-value: 0%; }\n [data-kui-fx~=lift] {\n transition: translate var(--kui-lift-duration, 220ms) var(--kui-lift-ease, ease-out);\n }\n [data-kui-fx~=lift]:focus-visible {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n }\n [data-kui-fx~=pop] {\n transition: scale var(--kui-pop-duration, 220ms) var(--kui-pop-ease, ease-out);\n }\n [data-kui-fx~=pop]:focus-visible {\n scale: var(--kui-pop-scale, 1.06);\n }\n [data-kui-fx~=lift-shadow] {\n transition: translate var(--kui-lift-shadow-duration, 220ms) var(--kui-lift-shadow-ease, ease-out), box-shadow var(--kui-lift-shadow-duration, 220ms) var(--kui-lift-shadow-ease, ease-out);\n }\n [data-kui-fx~=lift-shadow]:focus-visible {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n box-shadow: 0 14px 28px -12px rgb(0 0 0 / 0.45);\n }\n [data-kui-fx~=shine-sweep] {\n position: relative;\n overflow: hidden;\n }\n [data-kui-fx~=shine-sweep]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n linear-gradient(\n 115deg,\n transparent 40%,\n var(--kui-c1, rgb(255 255 255 / 0.35)) 50%,\n transparent 60%);\n background-size: 220% 100%;\n background-position: 120% 0;\n pointer-events: none;\n }\n [data-kui-fx~=shine-sweep]:focus-visible::after {\n animation: kui-shine-sweep var(--kui-shine-sweep-duration, 700ms) var(--kui-shine-sweep-ease, ease-out);\n }\n @keyframes kui-shine-sweep {\n from {\n background-position: 120% 0;\n }\n to {\n background-position: -20% 0;\n }\n }\n [data-kui-fx~=split-flap] {\n display: inline-block;\n perspective: 600px;\n backface-visibility: hidden;\n }\n [data-kui-fx~=split-flap]:focus-visible {\n animation: kui-split-flap var(--kui-split-flap-duration, 600ms) var(--kui-split-flap-ease, ease-in-out);\n }\n @keyframes kui-split-flap {\n from {\n rotate: x 0deg;\n }\n to {\n rotate: x 360deg;\n }\n }\n [data-kui-fx~=border-draw] {\n border: 2px solid transparent;\n border-image-slice: 1;\n border-image-source:\n conic-gradient(\n from -90deg,\n var(--kui-border-draw-color, var(--accent, #d2691e)) 0 var(--kui-border-pct, 0%),\n transparent 0);\n transition: --kui-border-pct var(--kui-border-draw-duration, 500ms) var(--kui-border-draw-ease, ease-out);\n }\n [data-kui-fx~=border-draw]:focus-visible {\n --kui-border-pct: 100%;\n }\n [data-kui-fx~=border-glow] {\n transition: box-shadow var(--kui-border-glow-duration, 260ms) var(--kui-border-glow-ease, ease-out);\n }\n [data-kui-fx~=border-glow]:focus-visible {\n box-shadow: 0 0 0 3px var(--kui-border-glow-color, var(--accent, #d2691e));\n }\n [data-kui-fx~=beam-border],\n [data-kui-fx~=beam-border-auto] {\n position: relative;\n }\n [data-kui-fx~=beam-border]::before,\n [data-kui-fx~=beam-border-auto]::before {\n content: \"\";\n position: absolute;\n inset: calc(-1 * var(--kui-beam-border-outset, 0px));\n border-radius: inherit;\n padding: var(--kui-beam-border-width, 3px);\n background:\n conic-gradient(\n from var(--kui-border-angle, 0deg),\n transparent 0deg,\n transparent 260deg,\n var(--kui-beam-border-c1, #ff5f6d) 282deg,\n var(--kui-beam-border-c2, var(--kui-beam-border-c1, #ffc371)) 306deg,\n var(--kui-beam-border-c3, var(--kui-beam-border-c1, #4facfe)) 330deg,\n var(--kui-beam-border-c4, var(--kui-beam-border-c1, #a855f7)) 354deg,\n transparent 360deg);\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--kui-beam-border-fade-duration, 200ms) ease-out;\n }\n [data-kui-fx~=beam-border]:focus-visible::before {\n opacity: 1;\n animation: kui-beam-rotate var(--kui-beam-border-duration, 4260ms) linear infinite;\n }\n [data-kui-fx~=beam-border-auto]::before {\n opacity: 1;\n animation: kui-beam-rotate var(--kui-beam-border-auto-duration, 4260ms) linear infinite;\n }\n @keyframes kui-beam-rotate {\n to {\n --kui-border-angle: 360deg;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n [data-kui-fx~=beam-border]:focus-visible::before,\n [data-kui-fx~=beam-border-auto]::before {\n animation: none;\n --kui-border-angle: 45deg;\n }\n }\n [data-kui-fx~=underline-slide] {\n position: relative;\n text-decoration: none;\n }\n [data-kui-fx~=underline-slide]::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: -0.15em;\n height: 0.08em;\n background: currentcolor;\n scale: 0 1;\n transform-origin: left center;\n transition: scale var(--kui-underline-slide-duration, 260ms) var(--kui-underline-slide-ease, ease-out);\n }\n [data-kui-fx~=underline-slide]:focus-visible::after {\n scale: 1 1;\n }\n [data-kui-fx~=underline-center] {\n position: relative;\n text-decoration: none;\n }\n [data-kui-fx~=underline-center]::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: -0.15em;\n height: 0.08em;\n background: currentcolor;\n scale: 0 1;\n transform-origin: center;\n transition: scale var(--kui-underline-center-duration, 260ms) var(--kui-underline-center-ease, ease-out);\n }\n [data-kui-fx~=underline-center]:focus-visible::after {\n scale: 1 1;\n }\n [data-kui-fx~=icon-wiggle]:focus-visible {\n animation: kui-icon-wiggle var(--kui-icon-wiggle-duration, 500ms) var(--kui-icon-wiggle-ease, ease-in-out);\n }\n @keyframes kui-icon-wiggle {\n 0%, 100% {\n rotate: 0deg;\n }\n 20% {\n rotate: -12deg;\n }\n 40% {\n rotate: 10deg;\n }\n 60% {\n rotate: -6deg;\n }\n 80% {\n rotate: 4deg;\n }\n }\n [data-kui-fx~=icon-spin]:focus-visible {\n animation: kui-icon-spin var(--kui-icon-spin-duration, 700ms) linear;\n }\n @keyframes kui-icon-spin {\n from {\n rotate: 0deg;\n }\n to {\n rotate: 360deg;\n }\n }\n [data-kui-fx~=icon-bounce]:focus-visible {\n animation: kui-icon-bounce var(--kui-icon-bounce-duration, 500ms) var(--kui-icon-bounce-ease, ease-out);\n }\n @keyframes kui-icon-bounce {\n 0%, 100% {\n translate: 0 0;\n }\n 30% {\n translate: 0 -7px;\n }\n 55% {\n translate: 0 1px;\n }\n 75% {\n translate: 0 -3px;\n }\n }\n @media (hover: hover) and (pointer: fine) {\n [data-kui-fx~=lift]:hover {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n }\n [data-kui-fx~=pop]:hover {\n scale: var(--kui-pop-scale, 1.06);\n }\n [data-kui-fx~=lift-shadow]:hover {\n translate: 0 calc(var(--kui-lift-distance, 6px) * -1);\n box-shadow: 0 14px 28px -12px rgb(0 0 0 / 0.45);\n }\n [data-kui-fx~=shine-sweep]:hover::after {\n animation: kui-shine-sweep var(--kui-shine-sweep-duration, 700ms) var(--kui-shine-sweep-ease, ease-out);\n }\n [data-kui-fx~=split-flap]:hover {\n animation: kui-split-flap var(--kui-split-flap-duration, 600ms) var(--kui-split-flap-ease, ease-in-out);\n }\n [data-kui-fx~=border-draw]:hover {\n --kui-border-pct: 100%;\n }\n [data-kui-fx~=border-glow]:hover {\n box-shadow: 0 0 0 3px var(--kui-border-glow-color, var(--accent, #d2691e));\n }\n [data-kui-fx~=beam-border]:hover::before {\n opacity: 1;\n animation: kui-beam-rotate var(--kui-beam-border-duration, 4260ms) linear infinite;\n }\n [data-kui-fx~=underline-slide]:hover::after {\n scale: 1 1;\n }\n [data-kui-fx~=underline-center]:hover::after {\n scale: 1 1;\n }\n [data-kui-fx~=icon-wiggle]:hover {\n animation: kui-icon-wiggle var(--kui-icon-wiggle-duration, 500ms) var(--kui-icon-wiggle-ease, ease-in-out);\n }\n [data-kui-fx~=icon-spin]:hover {\n animation: kui-icon-spin var(--kui-icon-spin-duration, 700ms) linear;\n }\n [data-kui-fx~=icon-bounce]:hover {\n animation: kui-icon-bounce var(--kui-icon-bounce-duration, 500ms) var(--kui-icon-bounce-ease, ease-out);\n }\n }\n [data-kui-fx~=tilt-3d],\n [data-kui-fx~=tilt-parallax] {\n transform-style: preserve-3d;\n }\n [data-kui-fx~=cursor-spotlight]::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n opacity: var(--kui-spotlight-opacity, 0);\n transition: opacity 200ms ease-out;\n background:\n radial-gradient(\n circle 160px at var(--kui-x, 50%) var(--kui-y, 50%),\n color-mix(in srgb, var(--accent, #d2691e) 30%, transparent),\n transparent 70%);\n }\n .kui-cursor-dot {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 9999;\n display: grid;\n place-items: center;\n pointer-events: none;\n opacity: 0;\n transition: opacity 160ms ease-out;\n }\n .kui-cursor-dot-active {\n opacity: 1;\n }\n .kui-cursor-dot-follow,\n .kui-cursor-dot-lag,\n .kui-cursor-dot-invert {\n width: 14px;\n height: 14px;\n margin: -7px 0 0 -7px;\n border-radius: 50%;\n background: var(--accent, #d2691e);\n }\n .kui-cursor-dot-invert {\n background: var(--kui-invert-color, #fff);\n mix-blend-mode: difference;\n }\n .kui-cursor-dot-label {\n margin: -1.4em 0 0 -1.4em;\n padding: 0.4em 0.8em;\n border-radius: 999px;\n background: var(--accent, #d2691e);\n color: var(--kui-label-color, #fff);\n font-size: 0.75rem;\n font-family:\n ui-monospace,\n Menlo,\n monospace;\n white-space: nowrap;\n }\n @media (pointer: coarse) {\n .kui-cursor-dot {\n display: none;\n }\n }\n}\n\n/* src/css/layout.css */\n@layer kui.effects {\n [data-kui-fx~=accordion-height] {\n overflow: hidden;\n }\n [data-kui-fx~=accordion-height]:not([data-open]) {\n height: 0;\n }\n}\n\n/* src/css/svg.css */\n@layer kui.effects {\n @keyframes kui-draw-stroke {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-draw-signature {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-draw-underline {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-checkmark-draw {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-cross-draw {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n @keyframes kui-chart-line-draw {\n from {\n stroke-dashoffset: var(--kui-path-length, 100);\n }\n to {\n stroke-dashoffset: 0;\n }\n }\n [data-kui-fx~=draw-stroke],\n [data-kui-fx~=draw-signature],\n [data-kui-fx~=draw-underline],\n [data-kui-fx~=checkmark-draw],\n [data-kui-fx~=cross-draw],\n [data-kui-fx~=chart-line-draw] {\n stroke-dasharray: var(--kui-path-length, 100);\n fill: none;\n }\n @keyframes kui-gradient-stroke {\n from {\n stroke: var(--kui-stroke-from, currentColor);\n }\n 50% {\n stroke: var(--kui-stroke-via, currentColor);\n }\n to {\n stroke: var(--kui-stroke-to, currentColor);\n }\n }\n [data-kui-fx~=gradient-stroke] {\n --kui-fx-gradient-stroke-iterations: infinite;\n }\n @keyframes kui-heart-fill {\n from {\n clip-path: inset(100% 0 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-bookmark-fill {\n from {\n clip-path: inset(100% 0 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-chart-area-fill {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n @keyframes kui-chart-bar-grow {\n from {\n scale: 1 var(--kui-bar-from, 0);\n }\n to {\n scale: 1 1;\n }\n }\n [data-kui-fx~=chart-bar-grow] {\n transform-origin: bottom center;\n }\n [data-kui-fx~=chart-bar-grow][data-kui-state=ready] {\n --kui-bar-from: 1 !important;\n opacity: 0;\n }\n @keyframes kui-logo-build {\n from {\n opacity: 0;\n scale: 0.62;\n rotate: -8deg;\n }\n to {\n opacity: 1;\n scale: 1;\n rotate: 0deg;\n }\n }\n [data-kui-fx~=hamburger-to-x] .kui-bar {\n transition:\n translate var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out),\n rotate var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out),\n opacity var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out);\n }\n [data-kui-fx~=hamburger-to-x][aria-expanded=true] .kui-bar:nth-child(1) {\n translate: 0 var(--kui-bar-gap, 6px);\n rotate: 45deg;\n }\n [data-kui-fx~=hamburger-to-x][aria-expanded=true] .kui-bar:nth-child(2) {\n opacity: 0;\n }\n [data-kui-fx~=hamburger-to-x][aria-expanded=true] .kui-bar:nth-child(3) {\n translate: 0 calc(var(--kui-bar-gap, 6px) * -1);\n rotate: -45deg;\n }\n [data-kui-fx~=play-to-pause] .kui-bar {\n transition: translate var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out), clip-path var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out);\n }\n [data-kui-fx~=play-to-pause]:not([aria-pressed=true]) .kui-bar:nth-child(1) {\n translate: var(--kui-bar-gap, 4px) 0;\n clip-path: polygon(0 0, 100% 25%, 100% 75%, 0 100%);\n }\n [data-kui-fx~=play-to-pause]:not([aria-pressed=true]) .kui-bar:nth-child(2) {\n translate: calc(var(--kui-bar-gap, 4px) * -1) 0;\n clip-path: polygon(0 25%, 100% 50%, 100% 50%, 0 75%);\n }\n [data-kui-fx~=plus-to-minus] {\n transition: rotate var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out);\n }\n [data-kui-fx~=plus-to-minus] .kui-bar {\n transition: scale var(--kui-icon-toggle-duration, 260ms) var(--kui-icon-toggle-ease, ease-out);\n }\n [data-kui-fx~=plus-to-minus] .kui-bar:nth-child(2) {\n rotate: 90deg;\n }\n [data-kui-fx~=plus-to-minus][aria-expanded=true] {\n rotate: 90deg;\n }\n [data-kui-fx~=plus-to-minus][aria-expanded=true] .kui-bar:nth-child(1) {\n scale: 0;\n }\n}\n\n/* src/css/index.css */\n"
5375
6926
  document.head.appendChild(style)
5376
6927
  }
5377
6928
  window.__kuinetic = kuinetic.kuinetic({ observe: true }).start()