+ return `
${processedImages.join('\n ')}
`;
};
diff --git a/src/assets/css/global/utilities/gsap-animations.css b/src/assets/css/local/gsap-animations.css
similarity index 53%
rename from src/assets/css/global/utilities/gsap-animations.css
rename to src/assets/css/local/gsap-animations.css
index a017f51..45cc436 100644
--- a/src/assets/css/global/utilities/gsap-animations.css
+++ b/src/assets/css/local/gsap-animations.css
@@ -1,6 +1,6 @@
/**
* GSAP Scroll Animation Utilities
- * CSS classes for GSAP scroll-driven animations triggered by shortcodes
+ * Punch animations: default constrained layout, optional spillingInto variant for breakout
*/
/* Container for GSAP scroll animations */
@@ -24,13 +24,43 @@
display: block;
width: 100%;
height: auto;
- /* GSAP will add will-change when animating */
transform: translateZ(0);
}
-/* Initial state - GSAP will handle visibility/opacity */
-.gsap-container[data-gsap-scroll-anim] .gsap-item {
- transform: translateZ(0);
+/* Punch animation container: constrained by default */
+.gsap-container[data-gsap-scroll-anim*='punch'] {
+ overflow: hidden;
+ position: relative;
+}
+
+.gsap-container[data-gsap-scroll-anim*='punch'] .gsap-image-wrapper {
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.gsap-container[data-gsap-scroll-anim*='punch'] .gsap-image {
+ position: relative;
+ width: 100%;
+ height: auto;
+ display: block;
+ image-rendering: -webkit-optimize-contrast;
+ image-rendering: crisp-edges;
+}
+
+/* spillingInto variant: responsive bleed breaking out of prose margins */
+.spillingInto {
+ --wrapper-width: 100%;
+}
+
+/* spillingInto-bleed: full-width edge-to-edge */
+.spillingInto-bleed {
+ margin-left: calc(-50vw + 50%);
+ margin-right: calc(-50vw + 50%);
+ padding-left: calc(50vw - 50%);
+ padding-right: calc(50vw - 50%);
+ width: 100vw;
}
/* Responsive spacing */
@@ -38,30 +68,7 @@
margin-top: var(--space-m, 1.5rem);
}
-/* Zoom animation specific styles */
-.gsap-container[data-gsap-scroll-anim*='zoom'] {
- overflow: hidden;
- position: relative;
-}
-
-.gsap-container[data-gsap-scroll-anim*='zoom'] .gsap-image-wrapper {
- overflow: hidden;
- position: relative;
- width: 100%;
- height: 100%;
-}
-
-.gsap-container[data-gsap-scroll-anim*='zoom'] .gsap-image {
- position: relative;
- width: 100%;
- height: auto;
- display: block;
- /* Ensure smooth scaling */
- image-rendering: -webkit-optimize-contrast;
- image-rendering: crisp-edges;
-}
-
-/* When animations are disabled (prefers-reduced-motion) */
+/* Accessibility: respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
.gsap-image,
.gsap-item {
@@ -70,19 +77,3 @@
opacity: 1 !important;
}
}
-
-/* Loading state */
-.gsap-container[data-gsap-loading] {
- opacity: 0.5;
-}
-
-/* Debug mode - shows animation markers */
-.gsap-container[data-gsap-debug] {
- outline: 2px dashed red;
- outline-offset: 4px;
-}
-
-.gsap-container[data-gsap-debug] .gsap-item {
- outline: 1px dashed blue;
- outline-offset: 2px;
-}
diff --git a/src/assets/scripts/bundle/gsap-animations.js b/src/assets/scripts/bundle/gsap-animations.js
new file mode 100644
index 0000000..2318681
--- /dev/null
+++ b/src/assets/scripts/bundle/gsap-animations.js
@@ -0,0 +1,73 @@
+/**
+ * GSAP Animations Library (zoom-only)
+ * Reusable presets with defaults that can be overridden via shortcode config
+ */
+import gsap from 'gsap';
+
+/**
+ * Check if user prefers reduced motion
+ */
+export const shouldAnimate = () =>
+ !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+
+/**
+ * Zoom defaults
+ */
+const zoomDefaults = {
+ focalX: 50, // 0-100 (% from left)
+ focalY: 50, // 0-100 (% from top)
+ startZoom: 1, // scale
+ endZoom: 2.5, // scale
+ ease: 'power2.inOut'
+};
+
+/**
+ * Zoom In: Start with full image visible, zoom into focal point
+ * config keys: focalX, focalY, startZoom, endZoom, ease
+ */
+export const punchIn = (element, config = {}) => {
+ const final = { ...zoomDefaults, ...config };
+ const { focalX, focalY, startZoom, endZoom, ease } = final;
+ return {
+ from: {
+ scale: startZoom,
+ transformOrigin: `${focalX}% ${focalY}%`,
+ x: 0,
+ y: 0
+ },
+ to: {
+ scale: endZoom,
+ transformOrigin: `${focalX}% ${focalY}%`,
+ x: 0,
+ y: 0,
+ ease
+ }
+ };
+};
+
+/**
+ * Zoom Out: Start zoomed into focal point, pull back to show full image
+ * config keys: focalX, focalY, startZoom, endZoom, ease
+ */
+export const punchOut = (element, config = {}) => {
+ const defaults = { ...zoomDefaults, startZoom: 2.5, endZoom: 1 };
+ const final = { ...defaults, ...config };
+ const { focalX, focalY, startZoom, endZoom, ease } = final;
+ return {
+ from: {
+ scale: startZoom,
+ transformOrigin: `${focalX}% ${focalY}%`,
+ x: 0,
+ y: 0
+ },
+ to: {
+ scale: endZoom,
+ transformOrigin: `${focalX}% ${focalY}%`,
+ x: 0,
+ y: 0,
+ ease
+ }
+ };
+};
+
+export default { shouldAnimate, punchIn, punchOut };
diff --git a/src/assets/scripts/bundle/gsap-effects.js b/src/assets/scripts/bundle/gsap-effects.js
deleted file mode 100644
index 0c8368d..0000000
--- a/src/assets/scripts/bundle/gsap-effects.js
+++ /dev/null
@@ -1,494 +0,0 @@
-/**
- * GSAP Effects Library
- * Shared animation effects for both shortcodes and UI components
- */
-import gsap from 'gsap';
-
-/**
- * Base animation effects
- * Each returns { from, to } objects for GSAP
- */
-export const effects = {
- fadeIn: (element, config = {}) => ({
- from: {
- opacity: 0,
- y: 50
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out',
- ...config
- }
- }),
-
- fadeInUp: (element, config = {}) => ({
- from: {
- opacity: 0,
- y: 100
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out',
- ...config
- }
- }),
-
- fadeInDown: (element, config = {}) => ({
- from: {
- opacity: 0,
- y: -100
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out',
- ...config
- }
- }),
-
- scaleIn: (element, config = {}) => ({
- from: {
- opacity: 0,
- scale: 0.8
- },
- to: {
- opacity: 1,
- scale: 1,
- ease: 'back.out(1.7)',
- ...config
- }
- }),
-
- slideInLeft: (element, config = {}) => ({
- from: {
- opacity: 0,
- x: -100
- },
- to: {
- opacity: 1,
- x: 0,
- ease: 'power2.out',
- ...config
- }
- }),
-
- slideInRight: (element, config = {}) => ({
- from: {
- opacity: 0,
- x: 100
- },
- to: {
- opacity: 1,
- x: 0,
- ease: 'power2.out',
- ...config
- }
- }),
-
- parallax: (element, config = {}) => ({
- from: {
- y: -100
- },
- to: {
- y: 100,
- ease: 'none',
- ...config
- }
- }),
-
- stagger: (element, config = {}) => ({
- from: {
- opacity: 0,
- y: 50
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out',
- stagger: 0.2,
- ...config
- }
- }),
-
- shake: (element, config = {}) => ({
- from: {},
- to: {
- x: 0,
- duration: 0.1,
- repeat: 5,
- yoyo: true,
- ease: 'power1.inOut',
- keyframes: {
- x: [-5, 5, -4, 4, -3, 3, -2, 2, -1, 0]
- },
- ...config
- }
- }),
-
- tremble: (element, config = {}) => ({
- from: {},
- to: {
- rotation: 0,
- duration: 0.05,
- repeat: -1,
- yoyo: true,
- ease: 'none',
- keyframes: {
- rotation: [-1, 1, -1, 1]
- },
- ...config
- }
- }),
-
- pulse: (element, config = {}) => ({
- from: {},
- to: {
- scale: 1,
- duration: 0.8,
- repeat: -1,
- yoyo: true,
- ease: 'power1.inOut',
- keyframes: {
- scale: [1, 1.05, 1]
- },
- ...config
- }
- }),
-
- wobble: (element, config = {}) => ({
- from: {},
- to: {
- rotation: 0,
- duration: 0.3,
- repeat: 3,
- yoyo: true,
- ease: 'power1.inOut',
- keyframes: {
- rotation: [-5, 5, -3, 3, -1, 0]
- },
- ...config
- }
- }),
-
- zoomIn: (element, config = {}) => {
- const {
- focalX = 50,
- focalY = 50,
- startZoom = 1,
- endZoom = 2.5
- } = config || {};
-
- return {
- from: {
- scale: startZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0
- },
- to: {
- scale: endZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0,
- ease: 'power2.inOut',
- ...config
- }
- };
- },
-
- zoomOut: (element, config = {}) => {
- const {
- focalX = 50,
- focalY = 50,
- startZoom = 2.5,
- endZoom = 1
- } = config || {};
-
- return {
- from: {
- scale: startZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0
- },
- to: {
- scale: endZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0,
- ease: 'power2.inOut',
- ...config
- }
- };
- },
-
- vibrate: (target, config = {}) => {
- return {
- to: {
- x: () => gsap.utils.random(-5, 5),
- y: () => gsap.utils.random(-5, 5),
- duration: 0.05,
- repeat: config.repeat ?? 10,
- ease: 'none',
- ...config
- }
- };
- }
-};
-
-/**
- * animation combos, express emtions, tell a story
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
-export const emotions = {
-/**
- * jumpscare
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
- jumpscare: (element, config = {}) => {
- const tl = gsap.timeline();
- tl.from(element, {
- scale: 0.5,
- opacity: 0,
- duration: 0.1,
- ease: 'power4.out'
- })
- .to(element, {
- x: -15,
- y: -15,
- rotation: 30,
- duration: 1,
- repeat: 3,
- yoyo: true
- })
- .to(element, {
- rotation: -2,
- duration: 0.05,
- repeat: -1,
- yoyo: true
- });
- return tl;
- },
-
-/**
- * anticipation
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
- anticipation: (element, config = {}) => {
- const tl = gsap.timeline();
- tl.to(element, {
- scale: 0.95,
- duration: 0.3,
- ease: 'power2.in'
- })
- .to(element, {
- scale: 1.1,
- duration: 0.2,
- ease: 'back.out(4)'
- })
- .to(element, {
- scale: 1,
- duration: 0.2,
- ease: 'power1.out'
- });
- return tl;
- },
-
-/**
- * dread, alledgedly
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
- dread: (element, config = {}) => {
- const tl = gsap.timeline();
- tl.from(element, {
- opacity: 0,
- scale: 1.2,
- duration: 2,
- ease: 'power1.in'
- })
- .to(element, {
- rotation: -1,
- duration: 0.1,
- repeat: -1,
- yoyo: true,
- ease: 'none'
- }, '-=1.5');
- return tl;
- },
-
-/**
- * relief, alledgedly
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
- relief: (element, config = {}) => {
- const tl = gsap.timeline();
- tl.from(element, {
- opacity: 0,
- y: -30,
- duration: 1,
- ease: 'power2.out'
- })
- .to(element, {
- y: 5,
- duration: 0.4,
- ease: 'power1.inOut'
- })
- .to(element, {
- y: 0,
- duration: 0.3,
- ease: 'power1.out'
- });
- return tl;
- },
-
-/**
- * tension
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
- tension: (element, config = {}) => {
- const tl = gsap.timeline();
- tl.from(element, {
- scale: 1,
- duration: 3,
- ease: 'none'
- })
- .to(element, {
- scale: 1.15,
- duration: 3,
- ease: 'power1.in'
- }, 0)
- .to(element, {
- x: -1,
- duration: 0.1,
- repeat: -1,
- yoyo: true,
- ease: 'none'
- }, 1);
- return tl;
- },
-
-/**
- * excitement
- * ****************************************************
- * ****************************************************
- * ****************************************************
- *****************************************************/
- excitement: (element, config = {}) => {
- const tl = gsap.timeline();
- tl.from(element, {
- opacity: 0,
- scale: 0,
- duration: 0.3,
- ease: 'back.out(3)'
- })
- .to(element, {
- y: -10,
- duration: 0.3,
- ease: 'power2.out'
- })
- .to(element, {
- y: 0,
- duration: 0.3,
- ease: 'bounce.out'
- });
- return tl;
- },
-
- /**
- * Image Swap: Swap image source at scroll position and vibrate
- */
- imageSwap: (element, config = {}) => {
- const tl = gsap.timeline({
- scrollTrigger: {
- trigger: element,
- start: 'center center',
- toggleActions: 'play none none none',
- once: true,
- markers: config.markers || false,
- ...config.scrollTrigger
- }
- });
-
- // Get both images from config or data attributes
- const img = element.querySelector('img');
- const secondSrc = config.secondImage || element.dataset.secondImage;
-
- if (!secondSrc || !img) {
- console.warn('imageSwap: No second image specified or img element not found', element);
- return tl;
- }
-
- // Swap image and vibrate simultaneously
- tl.call(() => {
- img.src = secondSrc;
- // Also update srcset if it exists
- if (img.srcset) {
- img.srcset = secondSrc;
- }
- })
- .to(element, {
- x: () => gsap.utils.random(-5, 5),
- y: () => gsap.utils.random(-5, 5),
- duration: 0.05,
- repeat: config.vibrateRepeats ?? 15,
- ease: 'none'
- }, 0); // 0 means start immediately
-
- return tl;
- }
-};
-
-/**
- * Check if user prefers reduced motion
- */
-export const shouldAnimate = () =>
- !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
-
-/**
- * Apply multiple effects to an element
- * @param {Element} element - Target element
- * @param {Array} effectNames - Array of effect names to apply
- * @param {Object} config - Configuration for effects
- */
-export const composeEffects = (element, effectNames, config = {}) => {
- if (!shouldAnimate()) return gsap.timeline();
-
- const tl = gsap.timeline();
-
- effectNames.forEach((effectName, index) => {
- const effect = effects[effectName];
- if (effect) {
- const { from, to } = effect(element, config[effectName] || {});
-
- if (index === 0 && Object.keys(from).length > 0) {
- tl.from(element, from);
- }
- if (Object.keys(to).length > 0) {
- // If this is a looping effect (repeat: -1), add it at the start
- if (to.repeat === -1) {
- tl.to(element, to, 0);
- } else {
- tl.to(element, to);
- }
- }
- }
- });
-
- return tl;
-};
-
-export default { effects, emotions, shouldAnimate, composeEffects };
diff --git a/src/assets/scripts/bundle/gsap-shortcode-init.js b/src/assets/scripts/bundle/gsap-shortcode-init.js
index 843a755..554e30e 100644
--- a/src/assets/scripts/bundle/gsap-shortcode-init.js
+++ b/src/assets/scripts/bundle/gsap-shortcode-init.js
@@ -1,178 +1,30 @@
/**
- * GSAP Scroll Animation Initializer
- * Handles scroll-driven animations triggered by gsapScrollAnim shortcode
- * Compatible with Hotwired Turbo navigation
- */
+ Initialize GSAP on pages with Turbo frames */
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
-import { effects, emotions, shouldAnimate, composeEffects } from './gsap-effects.js';
+import { shouldAnimate, punchIn, punchOut } from './gsap-animations.js';
// Register GSAP plugins
gsap.registerPlugin(ScrollTrigger);
+// Register GSDevTools in development only (esbuild inlines ELEVENTY_ENV at build time)
+if (process.env.ELEVENTY_ENV === 'development') {
+ try {
+ // Dynamic import wrapped in try-catch; GSDevTools package must be installed
+ import('gsap/GSDevTools').then(module => {
+ gsap.registerPlugin(module.default);
+ }).catch(() => {
+ // Silently continue if GSDevTools is not available
+ });
+ } catch (e) {
+ // Fallback for any import errors
+ }
+}
+
// Store active contexts for cleanup
const activeContexts = new Map();
-/**
- * Animation presets (use effects from gsap-effects.js, but kept for backward compatibility)
- * Each returns GSAP animation properties for the given element(s)
- */
-const animations = {
- fadeIn: (element) => ({
- from: {
- opacity: 0,
- y: 50
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out'
- }
- }),
-
- fadeInUp: (element) => ({
- from: {
- opacity: 0,
- y: 100
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out'
- }
- }),
-
- fadeInDown: (element) => ({
- from: {
- opacity: 0,
- y: -100
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out'
- }
- }),
-
- scaleIn: (element) => ({
- from: {
- opacity: 0,
- scale: 0.8
- },
- to: {
- opacity: 1,
- scale: 1,
- ease: 'back.out(1.7)'
- }
- }),
-
- slideInLeft: (element) => ({
- from: {
- opacity: 0,
- x: -100
- },
- to: {
- opacity: 1,
- x: 0,
- ease: 'power2.out'
- }
- }),
-
- slideInRight: (element) => ({
- from: {
- opacity: 0,
- x: 100
- },
- to: {
- opacity: 1,
- x: 0,
- ease: 'power2.out'
- }
- }),
-
- parallax: (element) => ({
- from: {
- y: -100
- },
- to: {
- y: 100,
- ease: 'none'
- }
- }),
-
- stagger: (element) => ({
- from: {
- opacity: 0,
- y: 50
- },
- to: {
- opacity: 1,
- y: 0,
- ease: 'power2.out',
- stagger: 0.2
- }
- }),
-
- /**
- * Zoom In: Start with full image visible, zoom into focal point
- * Requires: focalX, focalY (0-100%), startZoom, endZoom
- */
- zoomIn: (element, config) => {
- const {
- focalX = 50,
- focalY = 50,
- startZoom = 1,
- endZoom = 2.5
- } = config || {};
-
- return {
- from: {
- scale: startZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0
- },
- to: {
- scale: endZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0,
- ease: 'power2.inOut'
- }
- };
- },
-
- /**
- * Zoom Out: Start zoomed into focal point, pull back to show full image
- * Requires: focalX, focalY (0-100%), startZoom, endZoom
- */
- zoomOut: (element, config) => {
- const {
- focalX = 50,
- focalY = 50,
- startZoom = 2.5,
- endZoom = 1
- } = config || {};
-
- return {
- from: {
- scale: startZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0
- },
- to: {
- scale: endZoom,
- transformOrigin: `${focalX}% ${focalY}%`,
- x: 0,
- y: 0,
- ease: 'power2.inOut'
- }
- };
- }
-};
-
/**
* Initialize GSAP animations for all containers
*/
@@ -195,12 +47,10 @@ function initGsapAnimations() {
// Extract configuration with defaults
const {
animationType,
- emotion,
- effects: effectsList,
scrollStart = 'top 80%',
scrollEnd = 'bottom 20%',
scrub = true,
- pin = false,
+ pin = true,
markers = false
} = config;
@@ -211,70 +61,14 @@ function initGsapAnimations() {
// Create GSAP context for this container
const ctx = gsap.context(() => {
let timeline;
-
- // Handle emotional presets (take priority)
- if (emotion && emotions[emotion]) {
- timeline = gsap.timeline({
- scrollTrigger: {
- trigger: container,
- start: scrollStart,
- end: scrollEnd,
- scrub: scrub ? 1 : false,
- markers: markers,
- pin: pin,
- toggleActions: scrub ? undefined : 'play reverse play reverse',
- onEnter: () => container.dataset.gsapActive = 'true',
- onLeave: () => container.dataset.gsapActive = 'false',
- onEnterBack: () => container.dataset.gsapActive = 'true',
- onLeaveBack: () => container.dataset.gsapActive = 'false'
- }
- });
-
- // Apply emotional preset to each item
- items.forEach(item => {
- const emotionTl = emotions[emotion](item, config);
- timeline.add(emotionTl, 0); // Add at start
- });
- }
- // Handle effect composition (multiple effects)
- else if (effectsList && Array.isArray(effectsList)) {
- timeline = gsap.timeline({
- scrollTrigger: {
- trigger: container,
- start: scrollStart,
- end: scrollEnd,
- scrub: scrub ? 1 : false,
- markers: markers,
- pin: pin,
- toggleActions: scrub ? undefined : 'play reverse play reverse',
- onEnter: () => container.dataset.gsapActive = 'true',
- onLeave: () => container.dataset.gsapActive = 'false',
- onEnterBack: () => container.dataset.gsapActive = 'true',
- onLeaveBack: () => container.dataset.gsapActive = 'false'
- }
- });
-
- // Apply each effect in sequence
- items.forEach(item => {
- const composedTl = composeEffects(item, effectsList, config);
- timeline.add(composedTl, 0);
- });
- }
- // Handle single animation type (original behavior)
- else if (animationType) {
- const animationPreset = animations[animationType];
- if (!animationPreset) {
- console.warn(`Unknown animation type: ${animationType}`);
- return;
- }
-
- // For zoom animations, apply to images directly
- const targetElements = (animationType === 'zoomIn' || animationType === 'zoomOut')
- ? container.querySelectorAll('.gsap-image')
- : items;
-
- const anim = animationPreset(targetElements, config);
-
+
+ if (animationType === 'punchIn' || animationType === 'punchOut') {
+ const targetElements = container.querySelectorAll('.gsap-image');
+
+ const anim = animationType === 'punchIn'
+ ? punchIn(targetElements, config)
+ : punchOut(targetElements, config);
+
// Create timeline with ScrollTrigger
timeline = gsap.timeline({
scrollTrigger: {
@@ -291,7 +85,7 @@ function initGsapAnimations() {
onLeaveBack: () => container.dataset.gsapActive = 'false'
}
});
-
+
// Apply animation
if (anim.from && anim.to) {
timeline.fromTo(targetElements, anim.from, anim.to);
@@ -301,7 +95,7 @@ function initGsapAnimations() {
timeline.to(targetElements, anim.to);
}
} else {
- console.warn('No animation type, emotion, or effects specified', config);
+ console.warn('Unsupported animation type. Only zoomIn/zoomOut are available.', config);
return;
}
}, container);
@@ -368,4 +162,4 @@ window.addEventListener('resize', () => {
});
// Export for manual control if needed
-export { initGsapAnimations, cleanupGsapAnimations, refreshScrollTrigger, animations };
+export { initGsapAnimations, cleanupGsapAnimations, refreshScrollTrigger };
diff --git a/src/assets/scripts/bundle/mix-nav-animations.js b/src/assets/scripts/bundle/mix-nav-animations.js
index 3ea1f4a..d0ce7fd 100644
--- a/src/assets/scripts/bundle/mix-nav-animations.js
+++ b/src/assets/scripts/bundle/mix-nav-animations.js
@@ -3,7 +3,7 @@
* GSAP animations for mix track navigation UI elements
*/
import gsap from 'gsap';
-import { shouldAnimate } from './gsap-effects.js';
+import { shouldAnimate } from './gsap-animations.js';
/**
* Initialize mix navigation animations
diff --git a/src/pages/gsap-animations.md b/src/pages/gsap-animations.md
deleted file mode 100644
index 9b046ab..0000000
--- a/src/pages/gsap-animations.md
+++ /dev/null
@@ -1,352 +0,0 @@
----
-title: GSAP Animation Reference
-description: Visual guide to all available GSAP animations and how to use them
-layout: post
-permalink: 'docs/animations/index.html'
----
-
-# GSAP Animation Reference
-
-A visual reference for all available scroll-driven animations. Scroll down to see each effect in action!
-
-## Quick Start
-
-```markdown
-{% raw %}{% gsapScrollAnim { "animationType": "fadeIn" } %}
-[{ "src": "/path/to/image.jpg", "alt": "My image" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
----
-
-## Basic Effects
-
-### Fade In
-Gentle fade and slide up entrance.
-
-{% gsapScrollAnim { "animationType": "fadeIn", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/gorillaz-cracker-island.jpg", "alt": "Fade In Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "animationType": "fadeIn" } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Fade In Up
-Strong upward entrance.
-
-{% gsapScrollAnim { "animationType": "fadeInUp", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg", "alt": "Fade In Up Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "animationType": "fadeInUp" } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Fade In Down
-Drops in from above.
-
-{% gsapScrollAnim { "animationType": "fadeInDown", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/the-clash-london-calling.jpeg", "alt": "Fade In Down Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "animationType": "fadeInDown" } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Scale In
-Grows from center with bounce.
-
-{% gsapScrollAnim { "animationType": "scaleIn", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": false } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/james-whiplash.jpg", "alt": "Scale In Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "animationType": "scaleIn", "scrub": false } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Slide In Left
-
-{% gsapScrollAnim { "animationType": "slideInLeft", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/rtj-rtj4.jpg", "alt": "Slide In Left Demo" }]
-{% endgsapScrollAnim %}
-
-### Slide In Right
-
-{% gsapScrollAnim { "animationType": "slideInRight", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/ride-nowhere.jpg", "alt": "Slide In Right Demo" }]
-{% endgsapScrollAnim %}
-
----
-
-## Zoom Effects
-
-### Zoom In
-Slow zoom into the image as you scroll.
-
-{% gsapScrollAnim { "animationType": "zoomIn", "scrollStart": "top 80%", "scrollEnd": "middle middle", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/modest-mouse-we-were-dead.jpg", "alt": "Zoom In Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim {
- "animationType": "zoomIn",
- "focalX": 50,
- "focalY": 50,
- "startZoom": 1,
- "endZoom": 2.5,
- "scrub": true
-} %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Zoom Out
-Reverse zoom effect.
-
-{% gsapScrollAnim { "animationType": "zoomOut", "scrollStart": "top 80%", "scrollEnd": "bottom 20%", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/morphine-yes.jpg", "alt": "Zoom Out Demo" }]
-{% endgsapScrollAnim %}
-
----
-
-## Emotional Presets
-
-These animations tell stories, not just numbers.
-
-### Jumpscare 💥
-Sudden, intense appearance like an arrow hitting its mark.
-
-{% gsapScrollAnim { "emotion": "jumpscare", "scrub": false } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/james-whiplash.jpg", "alt": "Jumpscare Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "emotion": "jumpscare", "scrub": false } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Anticipation ⏳
-Wind up before the punch - builds tension.
-
-{% gsapScrollAnim { "emotion": "anticipation", "scrub": false } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/gorillaz-cracker-island.jpg", "alt": "Anticipation Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "emotion": "anticipation", "scrub": false } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Dread 😰
-Something ominous slowly approaches.
-
-{% gsapScrollAnim { "emotion": "dread", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg", "alt": "Dread Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "emotion": "dread", "scrub": true } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Relief 😌
-Everything's going to be okay - gentle, calming.
-
-{% gsapScrollAnim { "emotion": "relief", "scrub": false } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/the-clash-london-calling.jpeg", "alt": "Relief Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim { "emotion": "relief", "scrub": false } %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Tension 😬
-Slow zoom with subtle shake - something's wrong.
-
-{% gsapScrollAnim { "emotion": "tension", "scrub": true } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/bjork-all-is-full-of-love.jpg", "alt": "Tension Demo" }]
-{% endgsapScrollAnim %}
-
-### Excitement 🎉
-Bouncy, energetic entrance.
-
-{% gsapScrollAnim { "emotion": "excitement", "scrub": false } %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/parquet-courts-wide-awake.png", "alt": "Excitement Demo" }]
-{% endgsapScrollAnim %}
-
----
-
-## Advanced: Image Swap 🔄
-
-### Emotion Preset (Easier)
-Swap images when scrolled halfway past, with vibration effect.
-
-{% gsapScrollAnim {
- "emotion": "imageSwap",
- "secondImage": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg",
- "vibrateRepeats": 20
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/gorillaz-cracker-island.jpg",
- "alt": "Image Swap Emotion Demo",
- "data-second-image": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg"
-}]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim {
- "emotion": "imageSwap",
- "secondImage": "/path/to/second-image.jpg",
- "vibrateRepeats": 20
-} %}
-[{
- "src": "/path/to/first-image.jpg",
- "alt": "Demo",
- "data-second-image": "/path/to/second-image.jpg"
-}]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Effects Composition (More Control)
-Combine vibrate with other effects for custom animations.
-
-{% gsapScrollAnim {
- "effects": ["fadeIn", "vibrate"],
- "scrollStart": "top 80%",
- "scrollEnd": "bottom 20%",
- "scrub": false
-} %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/operation-ivy-energy.jpg", "alt": "Vibrate Effect Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim {
- "effects": ["fadeIn", "vibrate"],
- "scrub": false
-} %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
----
-
-## Effect Composition
-
-Combine multiple effects to create unique animations.
-
-### Fade + Shake
-
-{% gsapScrollAnim {
- "effects": ["fadeIn", "shake"],
- "scrollStart": "top 80%",
- "scrollEnd": "bottom 20%",
- "scrub": false
-} %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/ramones-mania.jpg", "alt": "Fade + Shake Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim {
- "effects": ["fadeIn", "shake"],
- "scrub": false
-} %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
-### Scale + Wobble + Pulse
-
-{% gsapScrollAnim {
- "effects": ["scaleIn", "wobble", "pulse"],
- "scrollStart": "top 80%",
- "scrollEnd": "bottom 20%",
- "scrub": false
-} %}
-[{ "src": "/pages/projects/mixes/tomorrowsbacon/van-halen-van-halen.jpg", "alt": "Triple Combo Demo" }]
-{% endgsapScrollAnim %}
-
-```markdown
-{% raw %}{% gsapScrollAnim {
- "effects": ["scaleIn", "wobble", "pulse"],
- "scrub": false
-} %}
-[{ "src": "/image.jpg", "alt": "Demo" }]
-{% endgsapScrollAnim %}{% endraw %}
-```
-
----
-
-## Configuration Options
-
-### Scrub
-- `"scrub": true` - Animation progress tied to scroll position (smooth)
-- `"scrub": false` - Animation plays once when triggered
-
-### Scroll Triggers
-- `"scrollStart": "top 80%"` - When to start (element position + viewport position)
-- `"scrollEnd": "bottom 20%"` - When to end
-- Common values: `"top center"`, `"center center"`, `"bottom top"`
-
-### Pin
-- `"pin": true` - Pin element in place while animating
-- `"pin": false` - Element scrolls normally
-
-### Markers
-- `"markers": true` - Show debug markers (for development)
-- `"markers": false` - Hide markers (for production)
-
----
-
-## Tips & Best Practices
-
-1. **Use emotions for storytelling** - They're designed to evoke feelings
-2. **Scrub for cinematic effects** - Ties animation to scroll for precise control
-3. **No scrub for surprise** - Let animations play independently
-4. **Compose effects carefully** - Too many can be overwhelming
-5. **Test on mobile** - Animations may need adjustment for smaller screens
-6. **Respect reduced motion** - All animations respect `prefers-reduced-motion` setting
-
----
-
-## All Available Effects
-
-**Base Effects:**
-- `fadeIn`, `fadeInUp`, `fadeInDown`
-- `scaleIn`
-- `slideInLeft`, `slideInRight`
-- `parallax`
-- `stagger`
-- `shake`, `tremble`, `pulse`, `wobble`
-- `zoomIn`, `zoomOut`
-- `vibrate`
-
-**Emotional Presets:**
-- `jumpscare` - Sudden impact
-- `anticipation` - Building tension
-- `dread` - Ominous approach
-- `relief` - Calming resolution
-- `tension` - Slow building stress
-- `excitement` - Bouncy energy
-- `imageSwap` - Image replacement with vibration
-
-**Composition:**
-- Combine any effects using `"effects": ["effect1", "effect2"]`
-- Effects can be layered for unique results
-
----
-
-
View this page's source code to see exactly how each animation is configured.
diff --git a/src/posts/2025/testing/emotional-animations.md b/src/posts/2025/testing/emotional-animations.md
deleted file mode 100644
index 03975a5..0000000
--- a/src/posts/2025/testing/emotional-animations.md
+++ /dev/null
@@ -1,123 +0,0 @@
----
-title: 'GSAP Emotional Animation Demo'
-description: "Demonstrating the new emotional animation presets"
-date: 2026-01-05
-tags: ['test', 'gsap']
----
-
-## Emotional Presets Demo
-
-Testing the new emotional animation system that lets you animate feelings, not just numbers.
-
-### Jumpscare
-
-Like an arrow hitting its mark - sudden appearance with impact:
-
-{% gsapScrollAnim {
- "emotion": "jumpscare",
- "scrub": true
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/james-whiplash.jpg",
- "alt": "Sudden impact!"
-}]
-{% endgsapScrollAnim %}
-
-### Anticipation
-
-Wind up before the punch:
-
-{% gsapScrollAnim {
- "emotion": "anticipation",
- "scrub": false,
- "scrollStart": "top 75%"
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/gorillaz-cracker-island.jpg",
- "alt": "Building up..."
-}]
-{% endgsapScrollAnim %}
-
-### Dread
-
-Something ominous approaches:
-
-{% gsapScrollAnim {
- "emotion": "dread",
- "scrub": true
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg",
- "alt": "Unsettling feeling"
-}]
-{% endgsapScrollAnim %}
-
-### Relief
-
-Everything's going to be okay:
-
-{% gsapScrollAnim {
- "emotion": "relief",
- "scrub": false
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/the-clash-london-calling.jpeg",
- "alt": "Phew, safe now"
-}]
-{% endgsapScrollAnim %}
-
-### Excitement
-
-Bouncy, energetic entrance:
-
-{% gsapScrollAnim {
- "emotion": "excitement",
- "scrub": false
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/james-whiplash.jpg",
- "alt": "So exciting!"
-}]
-{% endgsapScrollAnim %}
-
-## Effect Composition
-
-Combining multiple effects to create custom emotions:
-
-{% gsapScrollAnim {
- "effects": ["fadeIn", "shake"],
- "scrub": true
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/gorillaz-cracker-island.jpg",
- "alt": "Fade in + shake combo"
-}]
-{% endgsapScrollAnim %}
-
-### Triple Combo
-
-{% gsapScrollAnim {
- "effects": ["scaleIn", "wobble", "pulse"],
- "scrub": false
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg",
- "alt": "Scale + wobble + pulse"
-}]
-{% endgsapScrollAnim %}
-
----
-
-## Simple Animations Still Work
-
-The original simple syntax still works perfectly:
-
-{% gsapScrollAnim {
- "animationType": "fadeIn",
- "scrub": true
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/the-clash-london-calling.jpeg",
- "alt": "Classic fade in"
-}]
-{% endgsapScrollAnim %}
diff --git a/src/posts/2025/testing/swanpoint.jpg b/src/posts/2025/testing/swanpoint.jpg
new file mode 100644
index 0000000..90c1681
Binary files /dev/null and b/src/posts/2025/testing/swanpoint.jpg differ
diff --git a/src/posts/2025/testing/testing.md b/src/posts/2025/testing/testing.md
index 42d8984..8ee4b2e 100644
--- a/src/posts/2025/testing/testing.md
+++ b/src/posts/2025/testing/testing.md
@@ -1,80 +1,37 @@
---
title: 'Testing'
description: "Test animations and such here."
-date: 2026-01-04
+date: 2026-12-12
tags: ['test']
---
-## GSAP Scroll Animations
+## GSAP Proving Ground
-Testing the `gsapScrollAnim` shortcode with scroll-driven animations:
-
-{% gsapScrollAnim {
- "animationType": "fadeIn",
- "scrollStart": "top 80%",
- "scrub": true
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/james-whiplash.jpg",
- "alt": "Miles Teller as Andrew Neiman from Whiplash",
- "caption": "Testing scroll-driven fade in animation"
-}]
-{% endgsapScrollAnim %}
+Putting the `gsapScrollAnim` shortcode through its paces.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a sem ultrices, congue dui vitae, sodales augue. Mauris congue libero vitae nisi ullamcorper, nec laoreet sapien commodo. Vivamus dignissim urna et metus fermentum porta. Vivamus tempor tortor turpis, in pellentesque nisl viverra eget. Phasellus sed ligula quis massa commodo sagittis. Duis eu rhoncus augue. Vestibulum pretium convallis velit eget pharetra. Nam dapibus lacus eu cursus eleifend. Proin condimentum eros et est volutpat, vitae ullamcorper nulla vulputate. Pellentesque facilisis sem id nulla sodales, eu fermentum erat finibus.
-{% gsapScrollAnim {
- "animationType": "slideInRight",
- "scrollStart": "top 70%",
- "scrub": true,
- "containerClass": "gsap-container custom-spacing"
-} %}
-[{
- "src": "/pages/projects/mixes/tomorrowsbacon/gorillaz-cracker-island.jpg",
- "alt": "Gorillaz Cracker Island album cover",
- "caption": "Image 1 - Stagger animation"
-}, {
- "src": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg",
- "alt": "Radiohead OK Computer album cover",
- "caption": "Image 2 - Stagger animation"
-}, {
- "src": "/pages/projects/mixes/tomorrowsbacon/the-clash-london-calling.jpeg",
- "alt": "The Clash London Calling album cover",
- "caption": "Image 3 - Stagger animation"
-}]
-{% endgsapScrollAnim %}
-
-Proin id risus venenatis arcu sollicitudin venenatis. Ut quam nisl, commodo non urna ac, aliquam ullamcorper justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean sed dignissim massa, ac pulvinar sem. Etiam elementum justo lectus, nec blandit tortor varius sollicitudin. Nam tincidunt eros non sodales gravida. Donec pellentesque diam ante, sit amet ullamcorper urna efficitur ut. Quisque ut felis id nisi condimentum rhoncus non non eros. Mauris dapibus id magna et hendrerit. Phasellus in imperdiet quam. Vestibulum euismod leo at augue aliquam venenatis. Nullam quis nisi laoreet nisi laoreet ultricies. Morbi ut facilisis libero. Aliquam odio nunc, sagittis vel elit nec, finibus tristique velit. Nunc suscipit venenatis magna ut aliquet. Praesent sodales orci gravida facilisis finibus.
-
-Donec non pretium lectus. Aliquam pulvinar mattis egestas. Phasellus sit amet magna maximus velit dictum convallis. Nam mollis porttitor libero eu ornare. Cras sit amet leo ac mauris lacinia sodales. Maecenas pretium eu sapien sit amet interdum. Mauris sodales accumsan nibh vitae facilisis. Duis leo massa, placerat ut luctus quis, condimentum id dolor. Ut ut velit a ipsum mattis maximus. Praesent porta justo a lectus pretium iaculis.
-
-
-Nullam congue lectus a convallis dictum. Nunc rhoncus, ante id porta tempor, lorem ex molestie justo, eget egestas risus urna sit amet ipsum. Etiam dapibus eu eros vitae pharetra. Mauris dapibus erat sit amet quam semper laoreet. Suspendisse potenti. Curabitur et dolor quis nulla ultrices varius. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed auctor faucibus finibus. Integer porttitor, mauris sit amet interdum imperdiet, sapien tellus fermentum metus, non accumsan augue sapien nec ligula.
-
-Suspendisse eu rutrum dolor. Mauris semper, eros quis dapibus euismod, ante mi tempor libero, vel mollis ipsum dolor vel nulla. Sed cursus aliquam luctus. Suspendisse egestas erat ante, fringilla aliquam urna ullamcorper ac. Aliquam facilisis sed magna et bibendum. Nam sed justo a sapien vulputate dignissim vitae imperdiet enim. Integer mi ex, imperdiet porttitor posuere ut, placerat id magna. Pellentesque imperdiet porta blandit. Mauris eget rutrum sapien, luctus consequat ligula. Nullam sed ipsum in velit commodo rutrum. Aenean eu massa in dolor accumsan commodo. Sed vitae fermentum dolor, non vestibulum mi. Sed ac orci ac metus porta finibus.
-
-## Zoom Animation Test
-
-Testing the `zoomIn` effect to draw attention to album artwork details:
{% gsapScrollAnim {
- "animationType": "zoomIn",
- "focalX": 50,
- "focalY": 50,
+ "animationType": "punchIn",
+ "focalX": 90,
+ "focalY": 60,
"startZoom": 1,
"endZoom": 2.5,
- "scrollStart": "top top",
+ "scrollStart": "middle middle",
"scrollEnd": "+=100%",
"scrub": true,
- "pin": true
+ "pin": true,
+ "debug": true
} %}
[{
- "src": "/pages/projects/mixes/tomorrowsbacon/radiohead-ok-computer.jpg",
- "alt": "Radiohead OK Computer album cover - zoom to center detail"
+ "src": "/posts/2025/testing/swanpoint.jpg",
+ "alt": "alt text",
+ "caption": "This is a caption. Have we assigned it a class?"
}]
{% endgsapScrollAnim %}
-Mauris dignissim nisl et sem condimentum, eu molestie enim cursus. Proin varius tincidunt odio, ac varius ex faucibus quis. Cras ultrices dolor in mauris varius, eu vehicula nisi fringilla.
+Suspendisse eu rutrum dolor. Mauris semper, eros quis dapibus euismod, ante mi tempor libero, vel mollis ipsum dolor vel nulla. Sed cursus aliquam luctus. Suspendisse egestas erat ante, fringilla aliquam urna ullamcorper ac. Aliquam facilisis sed magna et bibendum. Nam sed justo a sapien vulputate dignissim vitae imperdiet enim. Integer mi ex, imperdiet porttitor posuere ut, placerat id magna. Pellentesque imperdiet porta blandit. Mauris eget rutrum sapien, luctus consequat ligula. Nullam sed ipsum in velit commodo rutrum. Aenean eu massa in dolor accumsan commodo. Sed vitae fermentum dolor, non vestibulum mi. Sed ac orci ac metus porta finibus.
{% gsapScrollAnim {
"animationType": "zoomOut",
@@ -95,3 +52,9 @@ Mauris dignissim nisl et sem condimentum, eu molestie enim cursus. Proin varius
Nullam quis facilisis mi. Sed dignissim tellus ut nisi tempor, eu ultrices libero consectetur. Integer et augue vitae nunc consequat elementum.
+Proin id risus venenatis arcu sollicitudin venenatis. Ut quam nisl, commodo non urna ac, aliquam ullamcorper justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean sed dignissim massa, ac pulvinar sem. Etiam elementum justo lectus, nec blandit tortor varius sollicitudin. Nam tincidunt eros non sodales gravida. Donec pellentesque diam ante, sit amet ullamcorper urna efficitur ut. Quisque ut felis id nisi condimentum rhoncus non non eros. Mauris dapibus id magna et hendrerit. Phasellus in imperdiet quam. Vestibulum euismod leo at augue aliquam venenatis. Nullam quis nisi laoreet nisi laoreet ultricies. Morbi ut facilisis libero. Aliquam odio nunc, sagittis vel elit nec, finibus tristique velit. Nunc suscipit venenatis magna ut aliquet. Praesent sodales orci gravida facilisis finibus.
+
+Donec non pretium lectus. Aliquam pulvinar mattis egestas. Phasellus sit amet magna maximus velit dictum convallis. Nam mollis porttitor libero eu ornare. Cras sit amet leo ac mauris lacinia sodales. Maecenas pretium eu sapien sit amet interdum. Mauris sodales accumsan nibh vitae facilisis. Duis leo massa, placerat ut luctus quis, condimentum id dolor. Ut ut velit a ipsum mattis maximus. Praesent porta justo a lectus pretium iaculis.
+
+
+Nullam congue lectus a convallis dictum. Nunc rhoncus, ante id porta tempor, lorem ex molestie justo, eget egestas risus urna sit amet ipsum. Etiam dapibus eu eros vitae pharetra. Mauris dapibus erat sit amet quam semper laoreet. Suspendisse potenti. Curabitur et dolor quis nulla ultrices varius. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed auctor faucibus finibus. Integer porttitor, mauris sit amet interdum imperdiet, sapien tellus fermentum metus, non accumsan augue sapien nec ligula.