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.
- package/README.md +3 -3
- package/dist/esm/{chunk-WPU33HDL.mjs → chunk-7WMNPIOZ.mjs} +1724 -240
- package/dist/esm/{chunk-LWS4OSLX.mjs → chunk-JT4PZL3A.mjs} +50 -7
- package/dist/esm/{chunk-5EVG7CIY.mjs → chunk-TJDDIQRG.mjs} +42 -16
- package/dist/esm/core/index.mjs +2 -2
- package/dist/esm/effects/index.mjs +2 -2
- package/dist/esm/index.mjs +3 -3
- package/dist/kuinetic.all.js +1813 -262
- package/dist/kuinetic.css +1491 -94
- package/dist/kuinetic.js +1812 -261
- package/dist/types/core/instances.d.ts +34 -3
- package/dist/types/core/path-morph.d.ts +19 -0
- package/dist/types/core/style-plan.d.ts +6 -1
- package/dist/types/core/types.d.ts +99 -1
- package/dist/types/effects/catalog/background-media.d.ts +152 -0
- package/dist/types/effects/catalog/index.d.ts +0 -6
- package/dist/types/effects/catalog/media-shared.d.ts +124 -0
- package/dist/types/effects/catalog/media.d.ts +4 -0
- package/dist/types/effects/catalog/numbers-shared.d.ts +5 -1
- package/dist/types/effects/catalog/shared.d.ts +2 -3
- package/dist/types/effects/catalog/subtree-capture.d.ts +23 -0
- package/dist/types/effects/catalog/text-shared.d.ts +11 -2
- package/dist/types/effects/gestures/index.d.ts +0 -1
- package/dist/types/effects/layout/index.d.ts +0 -2
- package/dist/types/effects/scroll-mechanics/index.d.ts +0 -4
- package/dist/types/effects/scroll-mechanics/scroll-spy.d.ts +26 -0
- package/dist/types/effects/scroll-mechanics/tracker.d.ts +61 -3
- package/dist/types/effects/shared.d.ts +40 -0
- package/dist/types/effects/step-marking.d.ts +99 -0
- package/dist/types/effects/three-d/index.d.ts +0 -7
- package/package.json +9 -2
package/dist/kuinetic.all.js
CHANGED
|
@@ -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
|
-
|
|
554
|
-
tracks.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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: {
|
|
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:
|
|
3306
|
+
parameters: { scale: { type: "number", default: "0.92", cssProperty: "--kui-from-scale" } }
|
|
2804
3307
|
})
|
|
2805
3308
|
];
|
|
2806
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4583
|
+
distance: distance2.distance,
|
|
3680
4584
|
angle: { type: "angle", default: "-120deg", cssProperty: "--kui-from-angle" }
|
|
3681
4585
|
}
|
|
3682
4586
|
}),
|
|
3683
|
-
|
|
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: {
|
|
3708
|
-
|
|
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: {
|
|
3714
|
-
|
|
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
|
-
|
|
4697
|
+
pIn("fade-in", "reveal", "kui-in"),
|
|
3742
4698
|
p("fade-out", "reveal", "kui-out"),
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
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
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
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
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
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
|
-
|
|
4726
|
+
pIn("zoom-in", "scale", "kui-zoom-in"),
|
|
3771
4727
|
p("zoom-out", "scale", "kui-zoom-out"),
|
|
3772
|
-
|
|
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
|
-
|
|
3775
|
-
|
|
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
|
-
|
|
3779
|
-
|
|
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
|
-
|
|
4740
|
+
pIn("rotate-in", "rotate", "kui-rotate-in"),
|
|
3785
4741
|
p("rotate-out", "rotate", "kui-rotate-out"),
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
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
|
-
|
|
4746
|
+
pIn("swing-in", "rotate", "kui-swing-in", { angle: "-15deg", ease: "back-out" })
|
|
3791
4747
|
];
|
|
3792
4748
|
var BLUR = [
|
|
3793
|
-
|
|
4749
|
+
pIn("blur-in", "blur", "kui-blur-in"),
|
|
3794
4750
|
p("blur-out", "blur", "kui-blur-out"),
|
|
3795
|
-
|
|
3796
|
-
|
|
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
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = () =>
|
|
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
|
-
|
|
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
|
-
{
|
|
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({
|
|
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
|
-
|
|
4280
|
-
runners.
|
|
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
|
-
|
|
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
|
|
4575
|
-
const from = `${node.getBoundingClientRect().height}px`;
|
|
5608
|
+
function heightEndpoints(node, previous) {
|
|
4576
5609
|
node.style.removeProperty("height");
|
|
4577
|
-
const to =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4729
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4849
|
-
|
|
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
|
|
4857
|
-
|
|
4858
|
-
|
|
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
|
-
|
|
4864
|
-
|
|
6113
|
+
self.restore();
|
|
6114
|
+
marker.restore();
|
|
6115
|
+
});
|
|
4865
6116
|
}
|
|
4866
6117
|
function prepareHorizontal(el, params, ctx) {
|
|
4867
|
-
const
|
|
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
|
-
|
|
4871
|
-
ctx.
|
|
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
|
|
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
|
|
4913
|
-
|
|
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
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
);
|
|
4967
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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)
|
|
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.
|
|
5108
|
-
|
|
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 (
|
|
5114
|
-
|
|
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
|
|
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
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
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
|
|
5205
|
-
|
|
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
|
|
5213
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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()
|