santycss 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -826,4 +826,182 @@
826
826
  }
827
827
  });
828
828
  }, { threshold: 0.15 }).observe(...document.querySelectorAll('[class*="animate-on-scroll"],[class*="scroll-reveal"]'));
829
- </script> */
829
+ </script> */
830
+
831
+
832
+ /* ═════════════════════════════════════════════════════════════════════════
833
+ SCROLL ANIMATIONS
834
+ Family 1: when-visible: — one-shot viewport-entry (+ santy-scroll.js)
835
+ Family 2: on-scroll: — scroll-progress (animation-timeline: scroll())
836
+ Family 3: scroll-{prop} — scroll-linked property utilities
837
+ Stagger: stagger-children / stagger-{n}
838
+ ═════════════════════════════════════════════════════════════════════════ */
839
+
840
+ /* ── Keyframes for when-visible: ── */
841
+ @keyframes wv-fade-in { from{opacity:0} to{opacity:1} }
842
+ @keyframes wv-slide-up { from{transform:translateY(24px);opacity:0} to{transform:translateY(0);opacity:1} }
843
+ @keyframes wv-slide-in-left { from{transform:translateX(-32px);opacity:0} to{transform:translateX(0);opacity:1} }
844
+ @keyframes wv-slide-in-right { from{transform:translateX(32px);opacity:0} to{transform:translateX(0);opacity:1} }
845
+ @keyframes wv-slide-in-bottom { from{transform:translateY(32px);opacity:0} to{transform:translateY(0);opacity:1} }
846
+ @keyframes wv-zoom-in { from{transform:scale(0.85);opacity:0} to{transform:scale(1);opacity:1} }
847
+ @keyframes wv-zoom-in-up { from{transform:scale(0.8) translateY(20px);opacity:0} to{transform:scale(1) translateY(0);opacity:1} }
848
+ @keyframes wv-zoom-bounce { from{transform:scale(0.7);opacity:0} to{transform:scale(1);opacity:1} }
849
+ @keyframes wv-bounce-in-bottom {
850
+ 0% {transform:translateY(40px);opacity:0}
851
+ 60% {transform:translateY(-8px);opacity:1}
852
+ 80% {transform:translateY(4px)}
853
+ 100%{transform:translateY(0)}
854
+ }
855
+ @keyframes wv-flip-in {
856
+ from{transform:perspective(600px) rotateX(-80deg);opacity:0}
857
+ to {transform:perspective(600px) rotateX(0);opacity:1}
858
+ }
859
+
860
+
861
+ /* ── when-visible: — viewport-entry animations ── */
862
+ /* Requires santy-scroll.js observer. Classes activate via .is-visible. */
863
+ .when-visible\:animate-fade-in { animation:none; animation-fill-mode:both; }
864
+ .when-visible\:animate-fade-in.is-visible { animation-name:wv-fade-in; animation-duration:0.6s; animation-timing-function:ease-out; }
865
+ .when-visible\:animate-slide-up { animation:none; animation-fill-mode:both; }
866
+ .when-visible\:animate-slide-up.is-visible { animation-name:wv-slide-up; animation-duration:0.5s; animation-timing-function:ease-out; }
867
+ .when-visible\:animate-slide-in-from-left { animation:none; animation-fill-mode:both; }
868
+ .when-visible\:animate-slide-in-from-left.is-visible { animation-name:wv-slide-in-left; animation-duration:0.5s; animation-timing-function:ease-out; }
869
+ .when-visible\:animate-slide-in-from-right { animation:none; animation-fill-mode:both; }
870
+ .when-visible\:animate-slide-in-from-right.is-visible { animation-name:wv-slide-in-right; animation-duration:0.5s; animation-timing-function:ease-out; }
871
+ .when-visible\:animate-slide-in-from-bottom { animation:none; animation-fill-mode:both; }
872
+ .when-visible\:animate-slide-in-from-bottom.is-visible { animation-name:wv-slide-in-bottom; animation-duration:0.5s; animation-timing-function:ease-out; }
873
+ .when-visible\:animate-zoom-in { animation:none; animation-fill-mode:both; }
874
+ .when-visible\:animate-zoom-in.is-visible { animation-name:wv-zoom-in; animation-duration:0.5s; animation-timing-function:ease-out; }
875
+ .when-visible\:animate-zoom-in-up { animation:none; animation-fill-mode:both; }
876
+ .when-visible\:animate-zoom-in-up.is-visible { animation-name:wv-zoom-in-up; animation-duration:0.5s; animation-timing-function:ease-out; }
877
+ .when-visible\:animate-zoom-bounce { animation:none; animation-fill-mode:both; }
878
+ .when-visible\:animate-zoom-bounce.is-visible { animation-name:wv-zoom-bounce; animation-duration:0.5s; animation-timing-function:cubic-bezier(0.34,1.56,0.64,1); }
879
+ .when-visible\:animate-bounce-in-from-bottom { animation:none; animation-fill-mode:both; }
880
+ .when-visible\:animate-bounce-in-from-bottom.is-visible { animation-name:wv-bounce-in-bottom; animation-duration:0.7s; animation-timing-function:ease-out; }
881
+ .when-visible\:animate-flip-in { animation:none; animation-fill-mode:both; }
882
+ .when-visible\:animate-flip-in.is-visible { animation-name:wv-flip-in; animation-duration:0.6s; animation-timing-function:ease-out; }
883
+ .when-visible\:animate-bounce { animation:none; animation-fill-mode:both; }
884
+ .when-visible\:animate-bounce.is-visible { animation-name:santy-bounce; animation-duration:1s; animation-timing-function:ease; }
885
+ .when-visible\:animate-pulse { animation:none; animation-fill-mode:both; }
886
+ .when-visible\:animate-pulse.is-visible { animation-name:santy-pulse; animation-duration:2s; animation-timing-function:cubic-bezier(0.4,0,0.6,1); }
887
+
888
+ /* ── enter-at-{n} — how much of element must be visible before triggering ── */
889
+ .enter-at-15 { --santy-enter-threshold:0.15; }
890
+ .enter-at-25 { --santy-enter-threshold:0.25; }
891
+ .enter-at-50 { --santy-enter-threshold:0.5; }
892
+ .enter-at-75 { --santy-enter-threshold:0.75; }
893
+ .enter-repeat { --santy-enter-repeat:1; }
894
+
895
+ /* ── on-scroll: — animations linked to scroll position ── */
896
+ @keyframes os-width-full { from{width:0%} to{width:100%} }
897
+ @keyframes os-rotate-180 { from{transform:rotate(0)} to{transform:rotate(180deg)} }
898
+ @keyframes os-rotate-360 { from{transform:rotate(0)} to{transform:rotate(360deg)} }
899
+ @keyframes os-fade-in { from{opacity:0} to{opacity:1} }
900
+ @keyframes os-fade-out { from{opacity:1} to{opacity:0} }
901
+ @keyframes os-scale-up { from{transform:scale(0.8)} to{transform:scale(1)} }
902
+ @keyframes os-slide-down { from{transform:translateY(-20px);opacity:0} to{transform:translateY(0);opacity:1} }
903
+ @keyframes os-translate-y-half { from{transform:translateY(0)} to{transform:translateY(-50%)} }
904
+
905
+ .on-scroll\:animate-width-full { animation:os-width-full linear; animation-timeline:scroll(); animation-fill-mode:both; }
906
+ .on-scroll\:rotate-180 { animation:os-rotate-180 linear; animation-timeline:scroll(); animation-fill-mode:both; }
907
+ .on-scroll\:rotate-360 { animation:os-rotate-360 linear; animation-timeline:scroll(); animation-fill-mode:both; }
908
+ .on-scroll\:fade-in { animation:os-fade-in linear; animation-timeline:scroll(); animation-fill-mode:both; }
909
+ .on-scroll\:fade-out { animation:os-fade-out linear; animation-timeline:scroll(); animation-fill-mode:both; }
910
+ .on-scroll\:scale-up { animation:os-scale-up linear; animation-timeline:scroll(); animation-fill-mode:both; }
911
+ .on-scroll\:slide-down { animation:os-slide-down linear; animation-timeline:scroll(); animation-fill-mode:both; }
912
+ .on-scroll\:translate-y-half { animation:os-translate-y-half linear; animation-timeline:scroll(); animation-fill-mode:both; }
913
+
914
+ /* ── scroll-{prop} — scroll-linked CSS property utilities ── */
915
+ @keyframes scroll-grow-width { from{width:0} to{width:100%} }
916
+ @keyframes scroll-opacity-out { from{opacity:1} to{opacity:0} }
917
+ @keyframes scroll-scale-down { from{transform:scale(1)} to{transform:scale(0.8)} }
918
+ @keyframes scroll-parallax-slow { from{transform:translateY(0)} to{transform:translateY(-30%)} }
919
+ @keyframes scroll-parallax-fast { from{transform:translateY(0)} to{transform:translateY(-60%)} }
920
+
921
+ .scroll-width { animation:scroll-grow-width linear both; animation-timeline:scroll(root); }
922
+ .scroll-opacity-out { animation:scroll-opacity-out linear both; animation-timeline:scroll(root); }
923
+ .scroll-scale-down { animation:scroll-scale-down linear both; animation-timeline:scroll(root); }
924
+ .scroll-parallax-slow { animation:scroll-parallax-slow linear both; animation-timeline:scroll(nearest); }
925
+ .scroll-parallax-fast { animation:scroll-parallax-fast linear both; animation-timeline:scroll(nearest); }
926
+
927
+
928
+ /* ── stagger-children — auto-delay nth-child for list animations ── */
929
+ .stagger-children > *:nth-child(1) { animation-delay:0ms; }
930
+ .stagger-children > *:nth-child(2) { animation-delay:100ms; }
931
+ .stagger-children > *:nth-child(3) { animation-delay:200ms; }
932
+ .stagger-children > *:nth-child(4) { animation-delay:300ms; }
933
+ .stagger-children > *:nth-child(5) { animation-delay:400ms; }
934
+ .stagger-children > *:nth-child(6) { animation-delay:500ms; }
935
+ .stagger-children > *:nth-child(7) { animation-delay:600ms; }
936
+ .stagger-children > *:nth-child(8) { animation-delay:700ms; }
937
+ .stagger-children > *:nth-child(9) { animation-delay:800ms; }
938
+ .stagger-children > *:nth-child(10) { animation-delay:900ms; }
939
+ .stagger-children > *:nth-child(11) { animation-delay:1000ms; }
940
+ .stagger-children > *:nth-child(12) { animation-delay:1100ms; }
941
+
942
+ /* stagger-50 */
943
+ .stagger-50 > *:nth-child(1) { animation-delay:0ms; }
944
+ .stagger-50 > *:nth-child(2) { animation-delay:50ms; }
945
+ .stagger-50 > *:nth-child(3) { animation-delay:100ms; }
946
+ .stagger-50 > *:nth-child(4) { animation-delay:150ms; }
947
+ .stagger-50 > *:nth-child(5) { animation-delay:200ms; }
948
+ .stagger-50 > *:nth-child(6) { animation-delay:250ms; }
949
+ .stagger-50 > *:nth-child(7) { animation-delay:300ms; }
950
+ .stagger-50 > *:nth-child(8) { animation-delay:350ms; }
951
+ .stagger-50 > *:nth-child(9) { animation-delay:400ms; }
952
+ .stagger-50 > *:nth-child(10) { animation-delay:450ms; }
953
+ .stagger-50 > *:nth-child(11) { animation-delay:500ms; }
954
+ .stagger-50 > *:nth-child(12) { animation-delay:550ms; }
955
+
956
+ /* stagger-150 */
957
+ .stagger-150 > *:nth-child(1) { animation-delay:0ms; }
958
+ .stagger-150 > *:nth-child(2) { animation-delay:150ms; }
959
+ .stagger-150 > *:nth-child(3) { animation-delay:300ms; }
960
+ .stagger-150 > *:nth-child(4) { animation-delay:450ms; }
961
+ .stagger-150 > *:nth-child(5) { animation-delay:600ms; }
962
+ .stagger-150 > *:nth-child(6) { animation-delay:750ms; }
963
+ .stagger-150 > *:nth-child(7) { animation-delay:900ms; }
964
+ .stagger-150 > *:nth-child(8) { animation-delay:1050ms; }
965
+ .stagger-150 > *:nth-child(9) { animation-delay:1200ms; }
966
+ .stagger-150 > *:nth-child(10) { animation-delay:1350ms; }
967
+ .stagger-150 > *:nth-child(11) { animation-delay:1500ms; }
968
+ .stagger-150 > *:nth-child(12) { animation-delay:1650ms; }
969
+
970
+ /* stagger-200 */
971
+ .stagger-200 > *:nth-child(1) { animation-delay:0ms; }
972
+ .stagger-200 > *:nth-child(2) { animation-delay:200ms; }
973
+ .stagger-200 > *:nth-child(3) { animation-delay:400ms; }
974
+ .stagger-200 > *:nth-child(4) { animation-delay:600ms; }
975
+ .stagger-200 > *:nth-child(5) { animation-delay:800ms; }
976
+ .stagger-200 > *:nth-child(6) { animation-delay:1000ms; }
977
+ .stagger-200 > *:nth-child(7) { animation-delay:1200ms; }
978
+ .stagger-200 > *:nth-child(8) { animation-delay:1400ms; }
979
+ .stagger-200 > *:nth-child(9) { animation-delay:1600ms; }
980
+ .stagger-200 > *:nth-child(10) { animation-delay:1800ms; }
981
+ .stagger-200 > *:nth-child(11) { animation-delay:2000ms; }
982
+ .stagger-200 > *:nth-child(12) { animation-delay:2200ms; }
983
+
984
+ /* stagger-300 */
985
+ .stagger-300 > *:nth-child(1) { animation-delay:0ms; }
986
+ .stagger-300 > *:nth-child(2) { animation-delay:300ms; }
987
+ .stagger-300 > *:nth-child(3) { animation-delay:600ms; }
988
+ .stagger-300 > *:nth-child(4) { animation-delay:900ms; }
989
+ .stagger-300 > *:nth-child(5) { animation-delay:1200ms; }
990
+ .stagger-300 > *:nth-child(6) { animation-delay:1500ms; }
991
+ .stagger-300 > *:nth-child(7) { animation-delay:1800ms; }
992
+ .stagger-300 > *:nth-child(8) { animation-delay:2100ms; }
993
+ .stagger-300 > *:nth-child(9) { animation-delay:2400ms; }
994
+ .stagger-300 > *:nth-child(10) { animation-delay:2700ms; }
995
+ .stagger-300 > *:nth-child(11) { animation-delay:3000ms; }
996
+ .stagger-300 > *:nth-child(12) { animation-delay:3300ms; }
997
+
998
+ /* ── Scroll animations — prefers-reduced-motion ── */
999
+ @media (prefers-reduced-motion: reduce) {
1000
+ [class*="when-visible:"],
1001
+ [class*="on-scroll:"],
1002
+ .scroll-width, .scroll-opacity-out, .scroll-scale-down,
1003
+ .scroll-parallax-slow, .scroll-parallax-fast {
1004
+ animation: none !important;
1005
+ transition: none !important;
1006
+ }
1007
+ }
@@ -1,5 +1,5 @@
1
1
  /* ============================================================
2
- SantyCSS v2.1.0 — Plain-English Utility CSS Framework
2
+ SantyCSS v2.2.0 — Plain-English Utility CSS Framework
3
3
  https://github.com/santybad/santy_css
4
4
  ============================================================ */
5
5
 
@@ -0,0 +1,45 @@
1
+ /*! santy-scroll.js — SantyCSS Scroll Observer v2.1
2
+ * Activates when-visible: viewport-entry animations via IntersectionObserver.
3
+ *
4
+ * CDN: <script src="https://cdn.jsdelivr.net/npm/santycss/dist/santy-scroll.js"></script>
5
+ *
6
+ * Modifiers read from element classes:
7
+ * enter-at-{15|25|50|75} — threshold (default: 0.15)
8
+ * enter-repeat — re-trigger on every viewport entry
9
+ */
10
+ (function () {
11
+ 'use strict';
12
+
13
+ function getThreshold(el) {
14
+ if (el.classList.contains('enter-at-75')) return 0.75;
15
+ if (el.classList.contains('enter-at-50')) return 0.50;
16
+ if (el.classList.contains('enter-at-25')) return 0.25;
17
+ return 0.15;
18
+ }
19
+
20
+ function makeObserver(threshold, repeat) {
21
+ return new IntersectionObserver(function (entries) {
22
+ entries.forEach(function (entry) {
23
+ if (entry.isIntersecting) {
24
+ entry.target.classList.add('is-visible');
25
+ if (!repeat) this.unobserve(entry.target);
26
+ } else if (repeat) {
27
+ entry.target.classList.remove('is-visible');
28
+ }
29
+ }.bind(this));
30
+ }, { threshold: threshold });
31
+ }
32
+
33
+ function init() {
34
+ document.querySelectorAll('[class*="when-visible:"]').forEach(function (el) {
35
+ var obs = makeObserver(getThreshold(el), el.classList.contains('enter-repeat'));
36
+ obs.observe(el);
37
+ });
38
+ }
39
+
40
+ if (document.readyState === 'loading') {
41
+ document.addEventListener('DOMContentLoaded', init);
42
+ } else {
43
+ init();
44
+ }
45
+ }());
@@ -5,7 +5,7 @@
5
5
  https://santycss.santy.in */
6
6
 
7
7
  /* ============================================================
8
- SantyCSS v2.1.0 — Plain-English Utility CSS Framework
8
+ SantyCSS v2.2.0 — Plain-English Utility CSS Framework
9
9
  https://github.com/santybad/santy_css
10
10
  ============================================================ */
11
11
 
package/dist/santy.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ============================================================
2
- SantyCSS v2.1.0 — Plain-English Utility CSS Framework
2
+ SantyCSS v2.2.0 — Plain-English Utility CSS Framework
3
3
  https://github.com/santybad/santy_css
4
4
  ============================================================ */
5
5
 
@@ -12231,6 +12231,184 @@
12231
12231
  </script> */
12232
12232
 
12233
12233
 
12234
+ /* ═════════════════════════════════════════════════════════════════════════
12235
+ SCROLL ANIMATIONS
12236
+ Family 1: when-visible: — one-shot viewport-entry (+ santy-scroll.js)
12237
+ Family 2: on-scroll: — scroll-progress (animation-timeline: scroll())
12238
+ Family 3: scroll-{prop} — scroll-linked property utilities
12239
+ Stagger: stagger-children / stagger-{n}
12240
+ ═════════════════════════════════════════════════════════════════════════ */
12241
+
12242
+ /* ── Keyframes for when-visible: ── */
12243
+ @keyframes wv-fade-in { from{opacity:0} to{opacity:1} }
12244
+ @keyframes wv-slide-up { from{transform:translateY(24px);opacity:0} to{transform:translateY(0);opacity:1} }
12245
+ @keyframes wv-slide-in-left { from{transform:translateX(-32px);opacity:0} to{transform:translateX(0);opacity:1} }
12246
+ @keyframes wv-slide-in-right { from{transform:translateX(32px);opacity:0} to{transform:translateX(0);opacity:1} }
12247
+ @keyframes wv-slide-in-bottom { from{transform:translateY(32px);opacity:0} to{transform:translateY(0);opacity:1} }
12248
+ @keyframes wv-zoom-in { from{transform:scale(0.85);opacity:0} to{transform:scale(1);opacity:1} }
12249
+ @keyframes wv-zoom-in-up { from{transform:scale(0.8) translateY(20px);opacity:0} to{transform:scale(1) translateY(0);opacity:1} }
12250
+ @keyframes wv-zoom-bounce { from{transform:scale(0.7);opacity:0} to{transform:scale(1);opacity:1} }
12251
+ @keyframes wv-bounce-in-bottom {
12252
+ 0% {transform:translateY(40px);opacity:0}
12253
+ 60% {transform:translateY(-8px);opacity:1}
12254
+ 80% {transform:translateY(4px)}
12255
+ 100%{transform:translateY(0)}
12256
+ }
12257
+ @keyframes wv-flip-in {
12258
+ from{transform:perspective(600px) rotateX(-80deg);opacity:0}
12259
+ to {transform:perspective(600px) rotateX(0);opacity:1}
12260
+ }
12261
+
12262
+
12263
+ /* ── when-visible: — viewport-entry animations ── */
12264
+ /* Requires santy-scroll.js observer. Classes activate via .is-visible. */
12265
+ .when-visible\:animate-fade-in { animation:none; animation-fill-mode:both; }
12266
+ .when-visible\:animate-fade-in.is-visible { animation-name:wv-fade-in; animation-duration:0.6s; animation-timing-function:ease-out; }
12267
+ .when-visible\:animate-slide-up { animation:none; animation-fill-mode:both; }
12268
+ .when-visible\:animate-slide-up.is-visible { animation-name:wv-slide-up; animation-duration:0.5s; animation-timing-function:ease-out; }
12269
+ .when-visible\:animate-slide-in-from-left { animation:none; animation-fill-mode:both; }
12270
+ .when-visible\:animate-slide-in-from-left.is-visible { animation-name:wv-slide-in-left; animation-duration:0.5s; animation-timing-function:ease-out; }
12271
+ .when-visible\:animate-slide-in-from-right { animation:none; animation-fill-mode:both; }
12272
+ .when-visible\:animate-slide-in-from-right.is-visible { animation-name:wv-slide-in-right; animation-duration:0.5s; animation-timing-function:ease-out; }
12273
+ .when-visible\:animate-slide-in-from-bottom { animation:none; animation-fill-mode:both; }
12274
+ .when-visible\:animate-slide-in-from-bottom.is-visible { animation-name:wv-slide-in-bottom; animation-duration:0.5s; animation-timing-function:ease-out; }
12275
+ .when-visible\:animate-zoom-in { animation:none; animation-fill-mode:both; }
12276
+ .when-visible\:animate-zoom-in.is-visible { animation-name:wv-zoom-in; animation-duration:0.5s; animation-timing-function:ease-out; }
12277
+ .when-visible\:animate-zoom-in-up { animation:none; animation-fill-mode:both; }
12278
+ .when-visible\:animate-zoom-in-up.is-visible { animation-name:wv-zoom-in-up; animation-duration:0.5s; animation-timing-function:ease-out; }
12279
+ .when-visible\:animate-zoom-bounce { animation:none; animation-fill-mode:both; }
12280
+ .when-visible\:animate-zoom-bounce.is-visible { animation-name:wv-zoom-bounce; animation-duration:0.5s; animation-timing-function:cubic-bezier(0.34,1.56,0.64,1); }
12281
+ .when-visible\:animate-bounce-in-from-bottom { animation:none; animation-fill-mode:both; }
12282
+ .when-visible\:animate-bounce-in-from-bottom.is-visible { animation-name:wv-bounce-in-bottom; animation-duration:0.7s; animation-timing-function:ease-out; }
12283
+ .when-visible\:animate-flip-in { animation:none; animation-fill-mode:both; }
12284
+ .when-visible\:animate-flip-in.is-visible { animation-name:wv-flip-in; animation-duration:0.6s; animation-timing-function:ease-out; }
12285
+ .when-visible\:animate-bounce { animation:none; animation-fill-mode:both; }
12286
+ .when-visible\:animate-bounce.is-visible { animation-name:santy-bounce; animation-duration:1s; animation-timing-function:ease; }
12287
+ .when-visible\:animate-pulse { animation:none; animation-fill-mode:both; }
12288
+ .when-visible\:animate-pulse.is-visible { animation-name:santy-pulse; animation-duration:2s; animation-timing-function:cubic-bezier(0.4,0,0.6,1); }
12289
+
12290
+ /* ── enter-at-{n} — how much of element must be visible before triggering ── */
12291
+ .enter-at-15 { --santy-enter-threshold:0.15; }
12292
+ .enter-at-25 { --santy-enter-threshold:0.25; }
12293
+ .enter-at-50 { --santy-enter-threshold:0.5; }
12294
+ .enter-at-75 { --santy-enter-threshold:0.75; }
12295
+ .enter-repeat { --santy-enter-repeat:1; }
12296
+
12297
+ /* ── on-scroll: — animations linked to scroll position ── */
12298
+ @keyframes os-width-full { from{width:0%} to{width:100%} }
12299
+ @keyframes os-rotate-180 { from{transform:rotate(0)} to{transform:rotate(180deg)} }
12300
+ @keyframes os-rotate-360 { from{transform:rotate(0)} to{transform:rotate(360deg)} }
12301
+ @keyframes os-fade-in { from{opacity:0} to{opacity:1} }
12302
+ @keyframes os-fade-out { from{opacity:1} to{opacity:0} }
12303
+ @keyframes os-scale-up { from{transform:scale(0.8)} to{transform:scale(1)} }
12304
+ @keyframes os-slide-down { from{transform:translateY(-20px);opacity:0} to{transform:translateY(0);opacity:1} }
12305
+ @keyframes os-translate-y-half { from{transform:translateY(0)} to{transform:translateY(-50%)} }
12306
+
12307
+ .on-scroll\:animate-width-full { animation:os-width-full linear; animation-timeline:scroll(); animation-fill-mode:both; }
12308
+ .on-scroll\:rotate-180 { animation:os-rotate-180 linear; animation-timeline:scroll(); animation-fill-mode:both; }
12309
+ .on-scroll\:rotate-360 { animation:os-rotate-360 linear; animation-timeline:scroll(); animation-fill-mode:both; }
12310
+ .on-scroll\:fade-in { animation:os-fade-in linear; animation-timeline:scroll(); animation-fill-mode:both; }
12311
+ .on-scroll\:fade-out { animation:os-fade-out linear; animation-timeline:scroll(); animation-fill-mode:both; }
12312
+ .on-scroll\:scale-up { animation:os-scale-up linear; animation-timeline:scroll(); animation-fill-mode:both; }
12313
+ .on-scroll\:slide-down { animation:os-slide-down linear; animation-timeline:scroll(); animation-fill-mode:both; }
12314
+ .on-scroll\:translate-y-half { animation:os-translate-y-half linear; animation-timeline:scroll(); animation-fill-mode:both; }
12315
+
12316
+ /* ── scroll-{prop} — scroll-linked CSS property utilities ── */
12317
+ @keyframes scroll-grow-width { from{width:0} to{width:100%} }
12318
+ @keyframes scroll-opacity-out { from{opacity:1} to{opacity:0} }
12319
+ @keyframes scroll-scale-down { from{transform:scale(1)} to{transform:scale(0.8)} }
12320
+ @keyframes scroll-parallax-slow { from{transform:translateY(0)} to{transform:translateY(-30%)} }
12321
+ @keyframes scroll-parallax-fast { from{transform:translateY(0)} to{transform:translateY(-60%)} }
12322
+
12323
+ .scroll-width { animation:scroll-grow-width linear both; animation-timeline:scroll(root); }
12324
+ .scroll-opacity-out { animation:scroll-opacity-out linear both; animation-timeline:scroll(root); }
12325
+ .scroll-scale-down { animation:scroll-scale-down linear both; animation-timeline:scroll(root); }
12326
+ .scroll-parallax-slow { animation:scroll-parallax-slow linear both; animation-timeline:scroll(nearest); }
12327
+ .scroll-parallax-fast { animation:scroll-parallax-fast linear both; animation-timeline:scroll(nearest); }
12328
+
12329
+
12330
+ /* ── stagger-children — auto-delay nth-child for list animations ── */
12331
+ .stagger-children > *:nth-child(1) { animation-delay:0ms; }
12332
+ .stagger-children > *:nth-child(2) { animation-delay:100ms; }
12333
+ .stagger-children > *:nth-child(3) { animation-delay:200ms; }
12334
+ .stagger-children > *:nth-child(4) { animation-delay:300ms; }
12335
+ .stagger-children > *:nth-child(5) { animation-delay:400ms; }
12336
+ .stagger-children > *:nth-child(6) { animation-delay:500ms; }
12337
+ .stagger-children > *:nth-child(7) { animation-delay:600ms; }
12338
+ .stagger-children > *:nth-child(8) { animation-delay:700ms; }
12339
+ .stagger-children > *:nth-child(9) { animation-delay:800ms; }
12340
+ .stagger-children > *:nth-child(10) { animation-delay:900ms; }
12341
+ .stagger-children > *:nth-child(11) { animation-delay:1000ms; }
12342
+ .stagger-children > *:nth-child(12) { animation-delay:1100ms; }
12343
+
12344
+ /* stagger-50 */
12345
+ .stagger-50 > *:nth-child(1) { animation-delay:0ms; }
12346
+ .stagger-50 > *:nth-child(2) { animation-delay:50ms; }
12347
+ .stagger-50 > *:nth-child(3) { animation-delay:100ms; }
12348
+ .stagger-50 > *:nth-child(4) { animation-delay:150ms; }
12349
+ .stagger-50 > *:nth-child(5) { animation-delay:200ms; }
12350
+ .stagger-50 > *:nth-child(6) { animation-delay:250ms; }
12351
+ .stagger-50 > *:nth-child(7) { animation-delay:300ms; }
12352
+ .stagger-50 > *:nth-child(8) { animation-delay:350ms; }
12353
+ .stagger-50 > *:nth-child(9) { animation-delay:400ms; }
12354
+ .stagger-50 > *:nth-child(10) { animation-delay:450ms; }
12355
+ .stagger-50 > *:nth-child(11) { animation-delay:500ms; }
12356
+ .stagger-50 > *:nth-child(12) { animation-delay:550ms; }
12357
+
12358
+ /* stagger-150 */
12359
+ .stagger-150 > *:nth-child(1) { animation-delay:0ms; }
12360
+ .stagger-150 > *:nth-child(2) { animation-delay:150ms; }
12361
+ .stagger-150 > *:nth-child(3) { animation-delay:300ms; }
12362
+ .stagger-150 > *:nth-child(4) { animation-delay:450ms; }
12363
+ .stagger-150 > *:nth-child(5) { animation-delay:600ms; }
12364
+ .stagger-150 > *:nth-child(6) { animation-delay:750ms; }
12365
+ .stagger-150 > *:nth-child(7) { animation-delay:900ms; }
12366
+ .stagger-150 > *:nth-child(8) { animation-delay:1050ms; }
12367
+ .stagger-150 > *:nth-child(9) { animation-delay:1200ms; }
12368
+ .stagger-150 > *:nth-child(10) { animation-delay:1350ms; }
12369
+ .stagger-150 > *:nth-child(11) { animation-delay:1500ms; }
12370
+ .stagger-150 > *:nth-child(12) { animation-delay:1650ms; }
12371
+
12372
+ /* stagger-200 */
12373
+ .stagger-200 > *:nth-child(1) { animation-delay:0ms; }
12374
+ .stagger-200 > *:nth-child(2) { animation-delay:200ms; }
12375
+ .stagger-200 > *:nth-child(3) { animation-delay:400ms; }
12376
+ .stagger-200 > *:nth-child(4) { animation-delay:600ms; }
12377
+ .stagger-200 > *:nth-child(5) { animation-delay:800ms; }
12378
+ .stagger-200 > *:nth-child(6) { animation-delay:1000ms; }
12379
+ .stagger-200 > *:nth-child(7) { animation-delay:1200ms; }
12380
+ .stagger-200 > *:nth-child(8) { animation-delay:1400ms; }
12381
+ .stagger-200 > *:nth-child(9) { animation-delay:1600ms; }
12382
+ .stagger-200 > *:nth-child(10) { animation-delay:1800ms; }
12383
+ .stagger-200 > *:nth-child(11) { animation-delay:2000ms; }
12384
+ .stagger-200 > *:nth-child(12) { animation-delay:2200ms; }
12385
+
12386
+ /* stagger-300 */
12387
+ .stagger-300 > *:nth-child(1) { animation-delay:0ms; }
12388
+ .stagger-300 > *:nth-child(2) { animation-delay:300ms; }
12389
+ .stagger-300 > *:nth-child(3) { animation-delay:600ms; }
12390
+ .stagger-300 > *:nth-child(4) { animation-delay:900ms; }
12391
+ .stagger-300 > *:nth-child(5) { animation-delay:1200ms; }
12392
+ .stagger-300 > *:nth-child(6) { animation-delay:1500ms; }
12393
+ .stagger-300 > *:nth-child(7) { animation-delay:1800ms; }
12394
+ .stagger-300 > *:nth-child(8) { animation-delay:2100ms; }
12395
+ .stagger-300 > *:nth-child(9) { animation-delay:2400ms; }
12396
+ .stagger-300 > *:nth-child(10) { animation-delay:2700ms; }
12397
+ .stagger-300 > *:nth-child(11) { animation-delay:3000ms; }
12398
+ .stagger-300 > *:nth-child(12) { animation-delay:3300ms; }
12399
+
12400
+ /* ── Scroll animations — prefers-reduced-motion ── */
12401
+ @media (prefers-reduced-motion: reduce) {
12402
+ [class*="when-visible:"],
12403
+ [class*="on-scroll:"],
12404
+ .scroll-width, .scroll-opacity-out, .scroll-scale-down,
12405
+ .scroll-parallax-slow, .scroll-parallax-fast {
12406
+ animation: none !important;
12407
+ transition: none !important;
12408
+ }
12409
+ }
12410
+
12411
+
12234
12412
  /* ═══ SANTY COMPONENTS ═══ */
12235
12413
 
12236
12414
  /* ═══════════════════════════════════════════════════════════════