
(function () {
  'use strict';

  var SPRING_IN  = 'linear(0, 0.23 8%, 0.74 17%, 1 28%, 1.17 40%, 1.04 50%, 0.95 61%, 1.012 72%, 0.997 84%, 1)';
  var SPRING_OUT = 'cubic-bezier(0.32, 0, 0.6, -0.45)';

  var MOTIONS = {
    
    'zoom-in': {
      initial: function (ix) { return 'transform:scale(0);opacity:0'; },
      kf: function (ix) {
        var ov = 1 + 0.06 * ix; 
        return [
          { transform: 'scale(0)',                 opacity: 0 },
          { transform: 'scale(' + ov.toFixed(2) + ')', opacity: 1, offset: 0.7 },
          { transform: 'scale(1)',                 opacity: 1 }
        ];
      },
      duration: 700,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },
    'rise': {
      initial: function (ix) {
        var p = Math.round(125 * ix);
        return 'transform:translateY(' + p + '%);opacity:0';
      },
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateY(' + p + '%)', opacity: 0 },
          { transform: 'translateY(0)',          opacity: 1 }
        ];
      },
      duration: 650,
      easing: SPRING_IN
    },
    'drop': {
      initial: function (ix) {
        var p = Math.round(125 * ix);
        return 'transform:translateY(-' + p + '%);opacity:0';
      },
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateY(-' + p + '%)', opacity: 0 },
          { transform: 'translateY(0)',           opacity: 1 }
        ];
      },
      duration: 650,
      easing: SPRING_IN
    },
    'glide-from-l': {
      initial: function (ix) {
        var p = Math.round(125 * ix);
        return 'transform:translateX(-' + p + '%);opacity:0';
      },
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateX(-' + p + '%)', opacity: 0 },
          { transform: 'translateX(0)',           opacity: 1 }
        ];
      },
      duration: 650,
      easing: SPRING_IN
    },
    'glide-from-r': {
      initial: function (ix) {
        var p = Math.round(125 * ix);
        return 'transform:translateX(' + p + '%);opacity:0';
      },
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateX(' + p + '%)', opacity: 0 },
          { transform: 'translateX(0)',          opacity: 1 }
        ];
      },
      duration: 650,
      easing: SPRING_IN
    },
    'fade-in': {
      initial: 'opacity:0',
      kf: [ { opacity: 0 }, { opacity: 1 } ],
      duration: 500,
      easing: 'ease-in-out'
    },
    'blur-in': {
      initial: function (ix) {
        var b = Math.round(20 * ix);
        return 'opacity:0;filter:blur(' + b + 'px)';
      },
      kf: function (ix) {
        var b = Math.round(20 * ix);
        return [
          { opacity: 0, filter: 'blur(' + b + 'px)' },
          { opacity: 1, filter: 'blur(0)' }
        ];
      },
      duration: 700,
      easing: 'ease-out'
    },
    'spacing-in': {
      initial: function (ix) {
        var em = (0.5 * ix).toFixed(2);
        return 'opacity:0;letter-spacing:' + em + 'em';
      },
      kf: function (ix) {
        var em = (0.5 * ix).toFixed(2);
        return [
          { opacity: 0, letterSpacing: em + 'em' },
          { opacity: 1, letterSpacing: '-0.04em', offset: 0.7 },
          { opacity: 1, letterSpacing: '0em'    }
        ];
      },
      duration: 700,
      easing: SPRING_IN
    },

    'bounce-slide-split': {
      initial: function (ix, i, n) {
        var fromLeft = i < Math.ceil(n / 2);
        var p = Math.round(100 * ix);
        var x = fromLeft ? -p : p;
        return 'transform:translateX(' + x + '%);opacity:0';
      },
      kf: function (ix, i, n) {
        var fromLeft = i < Math.ceil(n / 2);
        var p = Math.round(100 * ix);
        var x = fromLeft ? -p : p;
        return [
          { transform: 'translateX(' + x + '%)', opacity: 0 },
          { transform: 'translateX(0)',           opacity: 1 }
        ];
      },
      duration: 700,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },
    'bounce-slide-l': {
      initial: function (ix) {
        var p = Math.round(100 * ix);
        return 'transform:translateX(-' + p + '%);opacity:0';
      },
      kf: function (ix) {
        var p = Math.round(100 * ix);
        return [
          { transform: 'translateX(-' + p + '%)', opacity: 0 },
          { transform: 'translateX(0)',           opacity: 1 }
        ];
      },
      duration: 700,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },
    'bounce-rise': {
      initial: function (ix) {
        var p = Math.round(100 * ix);
        return 'transform:translateY(' + p + '%);opacity:0';
      },
      kf: function (ix) {
        var p = Math.round(100 * ix);
        return [
          { transform: 'translateY(' + p + '%)', opacity: 0 },
          { transform: 'translateY(0)',          opacity: 1 }
        ];
      },
      duration: 750,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },

    'letter-kick-in': {
      initial: 'opacity:0;letter-spacing:0.35em',
      kf: function (ix) {
        var em = (0.35 * ix).toFixed(2);
        return [
          { opacity: 0, letterSpacing: em + 'em',  transform: 'translateY(20%)' },
          { opacity: 1, letterSpacing: '-0.05em', transform: 'translateY(0)', offset: 0.55 },
          { opacity: 1, letterSpacing: '0.02em',                              offset: 0.78 },
          { opacity: 1, letterSpacing: '0em' }
        ];
      },
      duration: 700,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },

    'scatter-in': {
      
      initial: function (ix, i, n) {
        var r1 = _rand(i * 7 + 1), r2 = _rand(i * 11 + 3), r3 = _rand(i * 13 + 5);
        var angle = r1 * Math.PI * 2;
        var dist  = (60 + r2 * 140) * ix;
        var dx = Math.cos(angle) * dist;
        var dy = Math.sin(angle) * dist;
        var rot = (r3 * 80 - 40) * ix;
        return 'transform:translate(' + dx.toFixed(0) + 'px,' + dy.toFixed(0) + 'px) rotate(' + rot.toFixed(1) + 'deg) scale(0.35);opacity:0';
      },
      kf: function (ix, i, n) {
        var r1 = _rand(i * 7 + 1), r2 = _rand(i * 11 + 3), r3 = _rand(i * 13 + 5);
        var angle = r1 * Math.PI * 2;
        var dist  = (60 + r2 * 140) * ix;
        var dx = Math.cos(angle) * dist;
        var dy = Math.sin(angle) * dist;
        var rot = (r3 * 80 - 40) * ix;
        return [
          { transform: 'translate(' + dx.toFixed(0) + 'px,' + dy.toFixed(0) + 'px) rotate(' + rot.toFixed(1) + 'deg) scale(0.35)', opacity: 0 },
          { transform: 'translate(0,0) rotate(0deg) scale(1)', opacity: 1 }
        ];
      },
      duration: 900,
      easing: SPRING_IN
    },
    'shake-in': {
      
      initial: 'opacity:0',
      kf: function (ix) {
        var a = 9 * ix;
        return [
          { transform: 'translate(0,0)',                                                                    opacity: 0 },
          { transform: 'translate(' + a.toFixed(0) + 'px,-' + (a*0.7).toFixed(0) + 'px)',                   opacity: 0.5, offset: 0.18 },
          { transform: 'translate(-' + (a*0.8).toFixed(0) + 'px,' + (a*0.5).toFixed(0) + 'px)',             opacity: 0.8, offset: 0.42 },
          { transform: 'translate(' + (a*0.5).toFixed(0) + 'px,' + (a*0.3).toFixed(0) + 'px)',              opacity: 1,   offset: 0.65 },
          { transform: 'translate(-' + (a*0.2).toFixed(0) + 'px,-' + (a*0.15).toFixed(0) + 'px)',           opacity: 1,   offset: 0.85 },
          { transform: 'translate(0,0)',                                                                    opacity: 1 }
        ];
      },
      duration: 700,
      easing: 'cubic-bezier(0.34, 1.2, 0.64, 1)'
    },
    'noise-flicker': {
      
      initial: 'opacity:0',
      kf: function (ix) {
        var lo = 0.05;
        var hi = Math.min(1, 0.55 * ix);
        return [
          { opacity: 0 },
          { opacity: hi,         offset: 0.08 },
          { opacity: lo,         offset: 0.16 },
          { opacity: hi * 1.4,   offset: 0.24 },
          { opacity: lo * 2,     offset: 0.36 },
          { opacity: 0.75,       offset: 0.48 },
          { opacity: 0.15,       offset: 0.58 },
          { opacity: 0.92,       offset: 0.72 },
          { opacity: 0.35,       offset: 0.82 },
          { opacity: 1 }
        ];
      },
      duration: 850,
      easing: 'steps(18, end)'
    },

    'flip-up-in': {
      initial: 'opacity:0;transform-origin:50% 100%;transform:rotateX(-90deg)',
      kf: [ { transform: 'rotateX(-90deg)', opacity: 0 }, { transform: 'rotateX(0deg)', opacity: 1 } ],
      duration: 1100, easing: SPRING_IN
    },
    'flip-down-in': {
      initial: 'opacity:0;transform-origin:50% 0%;transform:rotateX(90deg)',
      kf: [ { transform: 'rotateX(90deg)', opacity: 0 }, { transform: 'rotateX(0deg)', opacity: 1 } ],
      duration: 1100, easing: SPRING_IN
    },
    'spin-in': {
      initial: 'opacity:0;transform-origin:50% 50%;transform:rotateY(-105deg)',
      kf: [ { transform: 'rotateY(-105deg)', opacity: 0 }, { transform: 'rotateY(0deg)', opacity: 1 } ],
      duration: 1300, easing: SPRING_IN
    },
    'swing-in': {
      initial: 'opacity:0;transform-origin:0% 50%;transform:rotateY(78deg)',
      kf: [ { transform: 'rotateY(78deg)', opacity: 0 },
            { transform: 'rotateY(-14deg)', opacity: 1, offset: 0.62 },
            { transform: 'rotateY(0deg)', opacity: 1 } ],
      duration: 1500, easing: 'cubic-bezier(0.33, 1, 0.68, 1)'
    },
    'unfold-in': {
      initial: 'opacity:0;transform-origin:50% 0%;transform:rotateX(88deg) translateY(-6%)',
      kf: [ { transform: 'rotateX(88deg) translateY(-6%)', opacity: 0 },
            { transform: 'rotateX(0deg) translateY(0)', opacity: 1 } ],
      duration: 1200, easing: SPRING_IN
    },
    'curtain-in': {
      initial: 'opacity:0;transform-origin:0% 50%;transform:scaleX(0.02)',
      kf: [ { transform: 'scaleX(0.02)', opacity: 0.35 }, { transform: 'scaleX(1)', opacity: 1 } ],
      duration: 1000, easing: SPRING_IN
    },
    'depth-in': {
      initial: function (ix) { var z = Math.round(700 * ix); return 'opacity:0;transform:translateZ(-' + z + 'px)'; },
      kf: function (ix) { var z = Math.round(700 * ix); return [ { transform: 'translateZ(-' + z + 'px)', opacity: 0 }, { transform: 'translateZ(0)', opacity: 1 } ]; },
      duration: 1200, easing: SPRING_IN
    },
    'depth-fly-in': {
      
      initial: 'opacity:0;transform:translateZ(-5000px);filter:blur(12px)',
      kf: [
        { transform: 'translateZ(-5000px)', opacity: 0, filter: 'blur(12px)' },
        { transform: 'translateZ(-1400px)', opacity: 1, filter: 'blur(3px)', offset: 0.55 },
        { transform: 'translateZ(0px)',      opacity: 1, filter: 'blur(0px)' }
      ],
      duration: 1300, easing: SPRING_IN
    },
    'tumble-in': {
      initial: function (ix) { var d = Math.round(55 * ix); return 'opacity:0;transform:translateY(-' + d + '%) rotateX(-70deg) rotateZ(-16deg)'; },
      kf: function (ix) { var d = Math.round(55 * ix); return [ { transform: 'translateY(-' + d + '%) rotateX(-70deg) rotateZ(-16deg)', opacity: 0 }, { transform: 'translateY(0) rotateX(0deg) rotateZ(0deg)', opacity: 1 } ]; },
      duration: 1300, easing: SPRING_IN
    },
    
    'flip-up-out': {
      initial: 'transform-origin:50% 0%',
      kf: [ { transform: 'rotateX(0deg)', opacity: 1 }, { transform: 'rotateX(90deg)', opacity: 0 } ],
      duration: 520, easing: SPRING_OUT
    },
    'fold-out': {
      initial: 'transform-origin:50% 100%',
      kf: [ { transform: 'rotateX(0deg)', opacity: 1 }, { transform: 'rotateX(-90deg)', opacity: 0 } ],
      duration: 520, easing: SPRING_OUT
    },
    'spin-out': {
      initial: 'transform-origin:50% 50%',
      kf: [ { transform: 'rotateY(0deg)', opacity: 1 }, { transform: 'rotateY(105deg)', opacity: 0 } ],
      duration: 560, easing: SPRING_OUT
    },
    'curtain-out': {
      initial: 'transform-origin:100% 50%',
      kf: [ { transform: 'scaleX(1)', opacity: 1 }, { transform: 'scaleX(0.02)', opacity: 0 } ],
      duration: 520, easing: SPRING_OUT
    },
    'depth-out': {
      initial: '',
      kf: function (ix) { var z = Math.round(500 * ix); return [ { transform: 'translateZ(0)', opacity: 1 }, { transform: 'translateZ(' + z + 'px)', opacity: 0 } ]; },
      duration: 560, easing: SPRING_OUT
    },
    'depth-fly-out': {
      
      initial: '',
      kf: [
        { transform: 'translateZ(0px)',     opacity: 1, filter: 'blur(0px)' },
        { transform: 'translateZ(-3000px)', opacity: 0, filter: 'blur(10px)' }
      ],
      duration: 660, easing: SPRING_OUT
    },

    'zoom-out': {
      initial: '',
      kf: [
        { transform: 'scale(1)', opacity: 1 },
        { transform: 'scale(0)', opacity: 0 }
      ],
      duration: 500,
      easing: SPRING_OUT
    },
    'rise-out': {
      initial: '',
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateY(0)',           opacity: 1 },
          { transform: 'translateY(-' + p + '%)', opacity: 0 }
        ];
      },
      duration: 550,
      easing: SPRING_OUT
    },
    'drop-out': {
      initial: '',
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateY(0)',          opacity: 1 },
          { transform: 'translateY(' + p + '%)', opacity: 0 }
        ];
      },
      duration: 550,
      easing: SPRING_OUT
    },
    'glide-to-l': {
      initial: '',
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateX(0)',           opacity: 1 },
          { transform: 'translateX(-' + p + '%)', opacity: 0 }
        ];
      },
      duration: 550,
      easing: SPRING_OUT
    },
    'glide-to-r': {
      initial: '',
      kf: function (ix) {
        var p = Math.round(125 * ix);
        return [
          { transform: 'translateX(0)',          opacity: 1 },
          { transform: 'translateX(' + p + '%)', opacity: 0 }
        ];
      },
      duration: 550,
      easing: SPRING_OUT
    },
    'fade-out': {
      initial: '',
      kf: [ { opacity: 1 }, { opacity: 0 } ],
      duration: 500,
      easing: 'ease-in-out'
    },
    'blur-out': {
      initial: '',
      kf: function (ix) {
        var b = Math.round(20 * ix);
        return [
          { opacity: 1, filter: 'blur(0)' },
          { opacity: 0, filter: 'blur(' + b + 'px)' }
        ];
      },
      duration: 550,
      easing: 'ease-in'
    },
    'spacing-out': {
      initial: '',
      kf: function (ix) {
        var em = (0.6 * ix).toFixed(2);
        return [
          { opacity: 1, letterSpacing: '0em'   },
          { opacity: 0, letterSpacing: em + 'em' }
        ];
      },
      duration: 550,
      easing: SPRING_OUT
    },
    'scatter-out': {
      initial: '',
      kf: function (ix, i, n) {
        var r1 = _rand(i * 17 + 2), r2 = _rand(i * 19 + 4), r3 = _rand(i * 23 + 6);
        var angle = r1 * Math.PI * 2;
        var dist  = (80 + r2 * 160) * ix;
        var dx = Math.cos(angle) * dist;
        var dy = Math.sin(angle) * dist;
        var rot = (r3 * 100 - 50) * ix;
        return [
          { transform: 'translate(0,0) rotate(0deg) scale(1)', opacity: 1 },
          { transform: 'translate(' + dx.toFixed(0) + 'px,' + dy.toFixed(0) + 'px) rotate(' + rot.toFixed(1) + 'deg) scale(0.4)', opacity: 0 }
        ];
      },
      duration: 600,
      easing: SPRING_OUT
    },

    'circle-reveal': {
      initial: 'opacity:0;clip-path:circle(0% at 50% 50%);-webkit-clip-path:circle(0% at 50% 50%)',
      kf: function (ix) {
        var to = Math.round(75 * ix); 
        return [
          { opacity: 0, clipPath: 'circle(0% at 50% 50%)',  webkitClipPath: 'circle(0% at 50% 50%)'  },
          { opacity: 1, clipPath: 'circle(' + to + '% at 50% 50%)', webkitClipPath: 'circle(' + to + '% at 50% 50%)' }
        ];
      },
      duration: 700,
      easing: SPRING_IN
    },
    'circle-close': {
      initial: '',
      kf: [
        { opacity: 1, clipPath: 'circle(75% at 50% 50%)', webkitClipPath: 'circle(75% at 50% 50%)' },
        { opacity: 0, clipPath: 'circle(0% at 50% 50%)',  webkitClipPath: 'circle(0% at 50% 50%)'  }
      ],
      duration: 500,
      easing: SPRING_OUT
    },
    'wipe-right': {
      initial: 'opacity:0;clip-path:inset(0 100% 0 0);-webkit-clip-path:inset(0 100% 0 0)',
      kf: [
        { opacity: 0, clipPath: 'inset(0 100% 0 0)', webkitClipPath: 'inset(0 100% 0 0)' },
        { opacity: 1, clipPath: 'inset(0 0% 0 0)',   webkitClipPath: 'inset(0 0% 0 0)' }
      ],
      duration: 700,
      easing: SPRING_IN
    },
    'wipe-left': {
      initial: 'opacity:0;clip-path:inset(0 0 0 100%);-webkit-clip-path:inset(0 0 0 100%)',
      kf: [
        { opacity: 0, clipPath: 'inset(0 0 0 100%)', webkitClipPath: 'inset(0 0 0 100%)' },
        { opacity: 1, clipPath: 'inset(0 0 0 0%)',   webkitClipPath: 'inset(0 0 0 0%)' }
      ],
      duration: 700,
      easing: SPRING_IN
    },
    'wipe-right-out': {
      initial: '',
      kf: [
        { opacity: 1, clipPath: 'inset(0 0 0 0%)',   webkitClipPath: 'inset(0 0 0 0%)' },
        { opacity: 0, clipPath: 'inset(0 0 0 100%)', webkitClipPath: 'inset(0 0 0 100%)' }
      ],
      duration: 550,
      easing: SPRING_OUT
    },
    'slit-open': {
      
      initial: 'opacity:0;clip-path:inset(50% 0 50% 0);-webkit-clip-path:inset(50% 0 50% 0)',
      kf: [
        { opacity: 0, clipPath: 'inset(50% 0 50% 0)', webkitClipPath: 'inset(50% 0 50% 0)' },
        { opacity: 1, clipPath: 'inset(0 0 0 0)',     webkitClipPath: 'inset(0 0 0 0)' }
      ],
      duration: 800,
      easing: SPRING_IN
    },
    'slit-close': {
      initial: '',
      kf: [
        { opacity: 1, clipPath: 'inset(0 0 0 0)',     webkitClipPath: 'inset(0 0 0 0)' },
        { opacity: 0, clipPath: 'inset(50% 0 50% 0)', webkitClipPath: 'inset(50% 0 50% 0)' }
      ],
      duration: 550,
      easing: SPRING_OUT
    },

    'weight-morph-in': {
      initial: 'opacity:0;font-variation-settings:"wght" 120;font-weight:200',
      kf: [
        { opacity: 0, fontVariationSettings: '"wght" 120', fontWeight: '200' },
        { opacity: 1, fontVariationSettings: '"wght" 800', fontWeight: '800' }
      ],
      duration: 820,
      easing: SPRING_IN
    },
    'weight-morph-out': {
      initial: '',
      kf: [
        { opacity: 1, fontVariationSettings: '"wght" 800', fontWeight: '800' },
        { opacity: 0, fontVariationSettings: '"wght" 120', fontWeight: '200' }
      ],
      duration: 520,
      easing: SPRING_OUT
    },

    'box-in': {
      initial: 'opacity:0;clip-path:inset(50% 50% 50% 50%);-webkit-clip-path:inset(50% 50% 50% 50%)',
      kf: [
        { opacity: 0, clipPath: 'inset(50% 50% 50% 50%)', webkitClipPath: 'inset(50% 50% 50% 50%)' },
        { opacity: 1, clipPath: 'inset(0 0 0 0)',          webkitClipPath: 'inset(0 0 0 0)' }
      ],
      duration: 720,
      easing: SPRING_IN
    },
    'box-out': {
      initial: '',
      kf: [
        { opacity: 1, clipPath: 'inset(0 0 0 0)',          webkitClipPath: 'inset(0 0 0 0)' },
        { opacity: 0, clipPath: 'inset(50% 50% 50% 50%)', webkitClipPath: 'inset(50% 50% 50% 50%)' }
      ],
      duration: 520,
      easing: SPRING_OUT
    },
    'wipe-up': {
      initial: 'opacity:0;clip-path:inset(100% 0 0 0);-webkit-clip-path:inset(100% 0 0 0)',
      kf: [
        { opacity: 0, clipPath: 'inset(100% 0 0 0)', webkitClipPath: 'inset(100% 0 0 0)' },
        { opacity: 1, clipPath: 'inset(0 0 0 0)',    webkitClipPath: 'inset(0 0 0 0)' }
      ],
      duration: 700,
      easing: SPRING_IN
    },
    'wipe-down': {
      initial: 'opacity:0;clip-path:inset(0 0 100% 0);-webkit-clip-path:inset(0 0 100% 0)',
      kf: [
        { opacity: 0, clipPath: 'inset(0 0 100% 0)', webkitClipPath: 'inset(0 0 100% 0)' },
        { opacity: 1, clipPath: 'inset(0 0 0 0)',    webkitClipPath: 'inset(0 0 0 0)' }
      ],
      duration: 700,
      easing: SPRING_IN
    },

    'skew-in': {
      initial: function (ix) { var s = Math.round(26 * ix), x = Math.round(10 * ix); return 'opacity:0;transform:skewX(' + s + 'deg) translateX(-' + x + '%)'; },
      kf: function (ix) { var s = Math.round(26 * ix), x = Math.round(10 * ix); return [
        { opacity: 0, transform: 'skewX(' + s + 'deg) translateX(-' + x + '%)' },
        { opacity: 1, transform: 'skewX(0deg) translateX(0)' }
      ]; },
      duration: 680,
      easing: SPRING_IN
    },
    'skew-out': {
      initial: '',
      kf: function (ix) { var s = Math.round(22 * ix), x = Math.round(10 * ix); return [
        { opacity: 1, transform: 'skewX(0deg) translateX(0)' },
        { opacity: 0, transform: 'skewX(-' + s + 'deg) translateX(' + x + '%)' }
      ]; },
      duration: 520,
      easing: SPRING_OUT
    },

    'glitch-in': {
      initial: 'opacity:0',
      kf: function (ix) {
        var ox = Math.round(5 * ix);
        var jx = Math.round(8 * ix);
        return [
          { opacity: 0, transform: 'translate(' + jx + 'px,0)', textShadow: '-' + ox + 'px 0 0 #f0f,' + ox + 'px 0 0 #0ff' },
          { opacity: 0.6, transform: 'translate(-' + Math.round(jx*0.8) + 'px,2px)', textShadow: '-' + Math.round(ox*0.7) + 'px 0 0 #f0f,' + Math.round(ox*0.7) + 'px 0 0 #0ff', offset: 0.20 },
          { opacity: 0.4, transform: 'translate(' + Math.round(jx*0.5) + 'px,-1px)', textShadow: '-' + Math.round(ox*1.2) + 'px 0 0 #f0f,' + Math.round(ox*1.2) + 'px 0 0 #0ff', offset: 0.40 },
          { opacity: 0.85, transform: 'translate(-' + Math.round(jx*0.3) + 'px,1px)', textShadow: '-' + Math.round(ox*0.4) + 'px 0 0 #f0f,' + Math.round(ox*0.4) + 'px 0 0 #0ff', offset: 0.70 },
          { opacity: 1,   transform: 'translate(0,0)',                                 textShadow: 'none' }
        ];
      },
      duration: 650,
      easing: 'steps(24, end)'
    },
    'glitch-out': {
      initial: '',
      kf: function (ix) {
        var ox = Math.round(5 * ix);
        var jx = Math.round(10 * ix);
        return [
          { opacity: 1, transform: 'translate(0,0)', textShadow: 'none' },
          { opacity: 0.6, transform: 'translate(' + Math.round(jx*0.5) + 'px,1px)', textShadow: '-' + Math.round(ox*0.7) + 'px 0 0 #f0f,' + Math.round(ox*0.7) + 'px 0 0 #0ff', offset: 0.40 },
          { opacity: 0.3, transform: 'translate(-' + jx + 'px,-2px)', textShadow: '-' + Math.round(ox*1.5) + 'px 0 0 #f0f,' + Math.round(ox*1.5) + 'px 0 0 #0ff', offset: 0.70 },
          { opacity: 0, transform: 'translate(' + jx + 'px,0)', textShadow: '-' + ox + 'px 0 0 #f0f,' + ox + 'px 0 0 #0ff' }
        ];
      },
      duration: 500,
      easing: 'steps(16, end)'
    },
    'distort-in': {
      
      initial: function (ix) {
        var b = Math.round(12 * ix);
        return 'opacity:0;filter:blur(' + b + 'px) contrast(0.3)';
      },
      kf: function (ix) {
        var b = 12 * ix;
        return [
          { opacity: 0,   filter: 'blur(' + b.toFixed(0) + 'px) contrast(0.3)' },
          { opacity: 0.5, filter: 'blur(' + (b*0.5).toFixed(0) + 'px) contrast(2)',   offset: 0.30 },
          { opacity: 0.3, filter: 'blur(' + (b*0.8).toFixed(0) + 'px) contrast(0.4)', offset: 0.50 },
          { opacity: 0.9, filter: 'blur(2px) contrast(1.5)',                            offset: 0.78 },
          { opacity: 1,   filter: 'blur(0) contrast(1)' }
        ];
      },
      duration: 800,
      easing: 'steps(20, end)'
    },
    'distort-out': {
      initial: '',
      kf: function (ix) {
        var b = 14 * ix;
        return [
          { opacity: 1, filter: 'blur(0) contrast(1)' },
          { opacity: 0.7, filter: 'blur(3px) contrast(1.6)',                            offset: 0.35 },
          { opacity: 0.4, filter: 'blur(' + (b*0.6).toFixed(0) + 'px) contrast(0.4)', offset: 0.60 },
          { opacity: 0,   filter: 'blur(' + b.toFixed(0) + 'px) contrast(0.2)' }
        ];
      },
      duration: 600,
      easing: 'steps(16, end)'
    }
  };

  function _resolve(field, intensity, i, n) {
    return (typeof field === 'function') ? field(intensity, i, n) : field;
  }

  var AUTO_EXIT_PAIRS = {
    'rise':                'rise-out',
    'drop':                'drop-out',
    'glide-from-l':        'glide-to-l',
    'glide-from-r':        'glide-to-r',
    'bounce-slide-l':      'glide-to-l',
    'bounce-slide-split':  'glide-to-r',
    'bounce-rise':         'drop-out',
    'zoom-in':             'zoom-out',
    'fade-in':             'fade-out',
    'blur-in':             'blur-out',
    'spacing-in':          'spacing-out',
    'letter-kick-in':      'spacing-out',
    'scatter-in':          'scatter-out',
    'shake-in':            'fade-out',
    'noise-flicker':       'fade-out',
    'glitch-in':           'glitch-out',
    'distort-in':          'distort-out',
    'circle-reveal':       'circle-close',
    'slit-open':           'slit-close',
    'wipe-right':          'wipe-right-out',
    'wipe-left':           'fade-out',
    
    'flip-up-in':          'flip-up-out',
    'flip-down-in':        'fold-out',
    'spin-in':             'spin-out',
    'swing-in':            'spin-out',
    'unfold-in':           'fold-out',
    'curtain-in':          'curtain-out',
    'depth-in':            'depth-out',
    'depth-fly-in':        'depth-fly-out',
    'tumble-in':           'fade-out',
    
    'weight-morph-in':     'weight-morph-out',
    'box-in':              'box-out',
    'wipe-up':             'fade-out',
    'wipe-down':           'fade-out',
    'skew-in':             'skew-out'
  };

  var AUTO_TARGET = {
    'rise':                'letter',
    'drop':                'letter',
    'glide-from-l':        'word',
    'glide-from-r':        'word',
    'bounce-slide-l':      'word',
    'bounce-slide-split':  'word',
    'bounce-rise':         'letter',
    'zoom-in':             'word',
    'fade-in':             'letter',
    'blur-in':             'whole',
    'spacing-in':          'whole',
    'letter-kick-in':      'letter',
    'scatter-in':          'letter',
    'shake-in':            'letter',
    'noise-flicker':       'letter',
    'glitch-in':           'letter',
    'distort-in':          'word',
    'circle-reveal':       'letter',
    'slit-open':           'letter',
    'wipe-right':          'word',
    'wipe-left':           'word',
    
    'flip-up-in':          'whole',
    'flip-down-in':        'whole',
    'spin-in':             'whole',
    'swing-in':            'whole',
    'unfold-in':           'whole',
    'curtain-in':          'whole',
    'depth-in':            'whole',
    'depth-fly-in':        'whole',
    'tumble-in':           'whole',
    'weight-morph-in':     'letter',
    'box-in':              'word',
    'wipe-up':             'word',
    'wipe-down':           'word',
    'skew-in':             'word'
  };

  var DECOR_SHAPES = {
    'underline-line': {
      inner: '<line x1="2" y1="5" x2="98" y2="5" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 10',
      widthMult: 1.05, heightPx: 14,
      placement: 'below', offset: 6,
      drawn: true
    },
    'underline-wavy': {
      inner: '<path d="M3,7 Q12,1 22,7 T42,7 T62,7 T82,7 T97,7" stroke="currentColor" stroke-width="5" fill="none" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 14',
      widthMult: 1.05, heightPx: 22,
      placement: 'below', offset: 6,
      drawn: true
    },
    'strike': {
      inner: '<line x1="2" y1="5" x2="98" y2="5" stroke="currentColor" stroke-width="7" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 10',
      widthMult: 1.08, heightPx: 12,
      placement: 'overlay-mid', offset: 0,
      drawn: true
    },
    'circle': {
      inner: '<ellipse cx="50" cy="50" rx="46" ry="42" stroke="currentColor" stroke-width="5" fill="none" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 1.25, heightMult: 1.5,
      placement: 'overlay', offset: 0,
      drawn: true
    },
    'brackets': {
      
      inner: '<path d="M22,5 L8,5 L8,95 L22,95 M78,5 L92,5 L92,95 L78,95" stroke="currentColor" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 1.18, heightMult: 1.25,
      placement: 'overlay', offset: 0,
      drawn: true
    },
    'bracket-left': {
      
      inner: '<path d="M85,8 L20,8 L20,92 L85,92" stroke="currentColor" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 0.22, heightMult: 1.2,
      placement: 'left-of', offset: 16,
      drawn: true
    },
    'bracket-right': {
      
      inner: '<path d="M15,8 L80,8 L80,92 L15,92" stroke="currentColor" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 0.22, heightMult: 1.2,
      placement: 'right-of', offset: 16,
      drawn: true
    },
    'splash': {
      
      inner: '<path d="M50,3 Q72,5 75,22 Q92,30 82,52 Q90,72 65,73 Q52,90 38,75 Q15,78 18,55 Q3,40 18,25 Q24,4 50,3 Z" fill="currentColor" />',
      viewBox: '0 0 100 80',
      widthMult: 1.35, heightMult: 1.6,
      placement: 'overlay', offset: 0,
      opacity: 0.18,
      drawn: false,
      behind: true
    },
    'ticks': {
      
      inner: '<line x1="5" y1="15" x2="5" y2="85" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="95" y1="15" x2="95" y2="85" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 1.22, heightMult: 1.1,
      placement: 'overlay', offset: 0,
      drawn: true
    },
    'streaks': {
      
      inner: '<line x1="0" y1="5" x2="20" y2="5" stroke="currentColor" stroke-width="5" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="80" y1="5" x2="100" y2="5" stroke="currentColor" stroke-width="5" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 10',
      widthMult: 1.35, heightPx: 12,
      placement: 'overlay-mid', offset: 0,
      drawn: true
    },
    'lines-from-center': {
      
      inner: '<line x1="50" y1="5" x2="2" y2="5" stroke="currentColor" stroke-width="7" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="5" x2="98" y2="5" stroke="currentColor" stroke-width="7" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 10',
      widthMult: 1.4, heightPx: 14,
      placement: 'overlay-mid', offset: 0,
      drawn: true
    },
    'brush-stroke': {
      
      inner: '<path d="M2,40 Q10,26 22,33 Q35,22 48,36 Q60,26 72,36 Q85,28 98,38 Q92,54 78,48 Q60,58 45,52 Q28,58 12,52 Q5,50 2,40 Z" fill="currentColor" />',
      viewBox: '0 0 100 80',
      widthMult: 1.25, heightMult: 1.35,
      placement: 'overlay', offset: 0,
      opacity: 0.22,
      drawn: false,
      behind: true
    },
    'arrow-down': {
      inner: '<path d="M50,5 L50,75 M30,55 L50,77 L70,55" stroke="currentColor" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 0.25, heightMult: 0.85,
      placement: 'above', offset: 14,
      drawn: true
    },
    'arrow-right': {
      inner: '<path d="M5,50 L85,50 M65,30 L88,50 L65,70" stroke="currentColor" stroke-width="7" fill="none" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 0.5, heightMult: 0.55,
      placement: 'left-of', offset: 24,
      drawn: true
    },
    'asterisk-burst': {
      
      inner: '<g>' +
             '<line x1="50" y1="50" x2="50" y2="3"  stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="50" y2="97" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="3"  y2="50" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="97" y2="50" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="14" y2="14" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="86" y2="14" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="14" y2="86" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="50" x2="86" y2="86" stroke="currentColor" stroke-width="6" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '</g>',
      viewBox: '0 0 100 100',
      widthMult: 1.5, heightMult: 1.9,
      placement: 'overlay', offset: 0,
      opacity: 0.85,
      drawn: true
    },
    'frame-rect': {
      
      inner: '<rect x="3" y="3" width="94" height="94" stroke="currentColor" stroke-width="4" fill="none" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      widthMult: 1.15, heightMult: 1.25,
      placement: 'overlay', offset: 0,
      drawn: true
    },
    'viewfinder': {
      
      inner: '<path d="M5,20 L5,5 L20,5 M80,5 L95,5 L95,20 M95,80 L95,95 L80,95 M20,95 L5,95 L5,80" stroke="currentColor" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 100',
      
      widthMult: 1.14, heightMult: 1.16,
      placement: 'overlay', offset: 0,
      drawn: true
    },
    'scribble': {
      
      inner: '<path d="M5,10 Q12,2 18,12 Q24,18 30,8 Q36,1 42,11 Q49,17 55,7 Q61,2 67,13 Q73,17 79,9 Q86,3 95,12" stroke="currentColor" stroke-width="5" fill="none" stroke-linecap="round" vector-effect="non-scaling-stroke" />',
      viewBox: '0 0 100 20',
      widthMult: 1.08, heightPx: 22,
      placement: 'below', offset: 6,
      drawn: true
    },
    'drip': {
      
      inner: '<line x1="20" y1="0" x2="20" y2="80" stroke="currentColor" stroke-width="4" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="50" y1="0" x2="50" y2="65" stroke="currentColor" stroke-width="4" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<line x1="80" y1="0" x2="80" y2="75" stroke="currentColor" stroke-width="4" stroke-linecap="round" vector-effect="non-scaling-stroke" />' +
             '<circle cx="20" cy="83" r="5" fill="currentColor" />' +
             '<circle cx="50" cy="68" r="5" fill="currentColor" />' +
             '<circle cx="80" cy="78" r="5" fill="currentColor" />',
      viewBox: '0 0 100 100',
      widthMult: 0.6, heightMult: 0.9,
      placement: 'above', offset: 4,
      drawn: true
    }
  };

  var DECOR_MOTIONS = {
    'draw-on':  { drawOn: true, duration: 800, easing: 'cubic-bezier(0.65, 0, 0.35, 1)' },
    'draw-off': { drawOff: true, duration: 500, easing: 'cubic-bezier(0.45, 0, 0.55, 1)' },
    'scale-pop': {
      initial: 'transform:scale(0);opacity:0;transform-origin:50% 50%',
      kf: [
        { transform: 'scale(0)',    opacity: 0 },
        { transform: 'scale(1.12)', opacity: 1, offset: 0.7 },
        { transform: 'scale(1)',    opacity: 1 }
      ],
      duration: 600,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },
    'fade-in': {
      initial: 'opacity:0',
      kf: [ { opacity: 0 }, { opacity: 1 } ],
      duration: 500,
      easing: 'ease-out'
    },
    'fade-out': {
      initial: '',
      kf: [ { opacity: 1 }, { opacity: 0 } ],
      duration: 500,
      easing: 'ease-in'
    },
    'scale-out': {
      initial: '',
      kf: [
        { transform: 'scale(1)',    opacity: 1 },
        { transform: 'scale(1.15)', opacity: 0 }
      ],
      duration: 500,
      easing: SPRING_OUT
    },
    'rotate-in': {
      initial: 'transform:rotate(-180deg) scale(0);opacity:0;transform-origin:50% 50%',
      kf: [
        { transform: 'rotate(-180deg) scale(0)', opacity: 0 },
        { transform: 'rotate(0deg) scale(1.1)',  opacity: 1, offset: 0.75 },
        { transform: 'rotate(0deg) scale(1)',    opacity: 1 }
      ],
      duration: 700,
      easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)'
    },
    'rotate-out': {
      initial: '',
      kf: [
        { transform: 'rotate(0deg) scale(1)',     opacity: 1 },
        { transform: 'rotate(180deg) scale(0.4)', opacity: 0 }
      ],
      duration: 550,
      easing: SPRING_OUT
    },
    
    'bracket-emerge-l': {
      initial: 'transform:translateX(500%);opacity:0',
      kf: [
        { transform: 'translateX(500%)', opacity: 0 },
        { transform: 'translateX(500%)', opacity: 1, offset: 0.18 },
        { transform: 'translateX(0)',    opacity: 1 }
      ],
      duration: 800,
      easing: SPRING_IN
    },
    'bracket-emerge-r': {
      initial: 'transform:translateX(-500%);opacity:0',
      kf: [
        { transform: 'translateX(-500%)', opacity: 0 },
        { transform: 'translateX(-500%)', opacity: 1, offset: 0.18 },
        { transform: 'translateX(0)',     opacity: 1 }
      ],
      duration: 800,
      easing: SPRING_IN
    },
    'bracket-collapse-l': {
      
      initial: '',
      kf: [
        { transform: 'translateX(0)',    opacity: 1 },
        { transform: 'translateX(500%)', opacity: 1, offset: 0.75 },
        { transform: 'translateX(500%)', opacity: 0 }
      ],
      duration: 800,
      easing: SPRING_OUT
    },
    'bracket-collapse-r': {
      initial: '',
      kf: [
        { transform: 'translateX(0)',     opacity: 1 },
        { transform: 'translateX(-500%)', opacity: 1, offset: 0.75 },
        { transform: 'translateX(-500%)', opacity: 0 }
      ],
      duration: 800,
      easing: SPRING_OUT
    }
  };

  var STAGGERS = {
    'simultaneous': function (i, n, dur) { return 0; },
    'sequential':   function (i, n, dur) {
      return n <= 1 ? 0 : (i / (n - 1)) * dur * 0.55;
    },
    'reverse':      function (i, n, dur) {
      return n <= 1 ? 0 : ((n - 1 - i) / (n - 1)) * dur * 0.55;
    },
    'wave':         function (i, n, dur) {
      
      return n <= 1 ? 0 : Math.sin((i / (n - 1)) * Math.PI) * dur * 0.6;
    },
    'random':       function (i, n, dur) {
      
      var v = (i * 0.6180339887 + 0.5) % 1;
      return v * dur * 0.55;
    },
    'center-out':   function (i, n, dur) {
      if (n <= 1) return 0;
      var mid = (n - 1) / 2;
      return (Math.abs(i - mid) / Math.max(0.5, mid)) * dur * 0.5;
    },
    'edges-in':     function (i, n, dur) {
      if (n <= 1) return 0;
      var mid = (n - 1) / 2;
      return (1 - Math.abs(i - mid) / Math.max(0.5, mid)) * dur * 0.5;
    }
  };

  var COMPOSITIONS = {

    'cylinder': {
      name: 'Cylinder',
      description: 'Lines wrap a horizontal drum that rolls around the X axis.',
      continuous: true,
      duration: 8000,
      minLines: 1, maxLines: 4,
      lines: [
        { role: 'hero', scale: 1.0,
          enter: { motion: 'fade-in', target: 'whole', stagger: 'simultaneous', at: 0, dur: 600 },
          exit:  { motion: 'fade-out', target: 'whole', stagger: 'simultaneous', at: 7400, dur: 600 } }
      ]
    },

    
    'sweep': {
      name: 'Bar Sweep',
      description: 'A skewed bar sweeps across and reveals the text in its wake, then wipes it out.',
      continuous: true,
      duration: 5000,
      minLines: 1, maxLines: 4,
      lines: [
        { role: 'hero', scale: 1.0,
          enter: { motion: 'fade-in', target: 'whole', stagger: 'simultaneous', at: 0, dur: 600 },
          exit:  { motion: 'fade-out', target: 'whole', stagger: 'simultaneous', at: 4400, dur: 600 } }
      ]
    },

    'brutal': {
      name: 'Extruded',
      description: 'Bold extruded letters slide in horizontally with a hard depth shadow.',
      continuous: true,
      duration: 5000,
      minLines: 1, maxLines: 4,
      lines: [
        { role: 'hero', scale: 1.0,
          enter: { motion: 'fade-in', target: 'whole', stagger: 'simultaneous', at: 0, dur: 600 },
          exit:  { motion: 'fade-out', target: 'whole', stagger: 'simultaneous', at: 4400, dur: 600 } }
      ]
    },

    'arc': {
      name: 'Camera Arc',
      description: 'Letters blast in from the camera, assemble, then blast back out.',
      continuous: true,
      duration: 5000,
      minLines: 1, maxLines: 4,
      lines: [
        { role: 'hero', scale: 1.0,
          enter: { motion: 'fade-in', target: 'whole', stagger: 'simultaneous', at: 0, dur: 600 },
          exit:  { motion: 'fade-out', target: 'whole', stagger: 'simultaneous', at: 4400, dur: 600 } }
      ]
    },

    'block-slam': {
      name: 'Block Slam',
      description: 'Hero rises through a tight mask, subtitle bounce-slides from alternating sides. Both fade out.',
      duration: 5800,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'rise',     target: 'letter', stagger: 'sequential',   at: 0,    dur: 700 },
          exit:  { motion: 'rise-out', target: 'letter', stagger: 'reverse',      at: 4700, dur: 600 }
        },
        {
          role: 'sub', scale: 0.42,
          enter: { motion: 'bounce-slide-split', target: 'word', stagger: 'sequential', at: 900,  dur: 700 },
          exit:  { motion: 'glide-to-r',          target: 'word', stagger: 'sequential', at: 4700, dur: 500 }
        }
      ]
    },

    'triple-cascade': {
      name: 'Triple Cascade',
      description: 'Hero zooms with bounce, sub bounce-slides from sides, label rises. Distinct exits.',
      duration: 7500,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'zoom-in',  target: 'word', stagger: 'sequential', at: 0,    dur: 700 },
          exit:  { motion: 'zoom-out', target: 'word', stagger: 'sequential', at: 4500, dur: 600 }
        },
        {
          role: 'sub', scale: 0.55,
          enter: { motion: 'bounce-slide-split', target: 'word', stagger: 'sequential', at: 900,  dur: 750 },
          exit:  { motion: 'glide-to-r',          target: 'word', stagger: 'reverse',    at: 6000, dur: 700 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'rise',     target: 'letter', stagger: 'sequential', at: 1800, dur: 600 },
          exit:  { motion: 'rise-out', target: 'word',   stagger: 'sequential', at: 5400, dur: 500 }
        }
      ]
    },

    'wave-build': {
      name: 'Wave Build',
      description: 'Each line waves in via a different motion: rise → drop → zoom → blur, all sine-staggered.',
      duration: 6500,
      minLines: 1, maxLines: 4,
      lines: [
        {
          role: 'hero', scale: 0.95,
          enter: { motion: 'rise',     target: 'letter', stagger: 'wave', staggerScale: 1.5, intensity: 0.9, at: 0,    dur: 900 },
          exit:  { motion: 'fade-out', target: 'whole',  stagger: 'simultaneous', at: 5400, dur: 600 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'drop',     target: 'letter', stagger: 'wave', staggerScale: 1.5, intensity: 0.9, at: 1000, dur: 800 },
          exit:  { motion: 'fade-out', target: 'whole',  stagger: 'simultaneous', at: 5300, dur: 600 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'zoom-in',  target: 'letter', stagger: 'wave', staggerScale: 1.5, intensity: 0.8, at: 1900, dur: 700 },
          exit:  { motion: 'fade-out', target: 'whole',  stagger: 'simultaneous', at: 5200, dur: 500 }
        },
        {
          role: 'label', scale: 0.26,
          enter: { motion: 'blur-in',  target: 'letter', stagger: 'wave', staggerScale: 1.5, intensity: 0.7, at: 2700, dur: 600 },
          exit:  { motion: 'fade-out', target: 'whole',  stagger: 'simultaneous', at: 5100, dur: 500 }
        }
      ]
    },

    'spotlight': {
      name: 'Spotlight',
      description: 'Hero blurs in dramatically. Subtitle letter-spaces in below. Both fade out.',
      duration: 6500,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.2,
          enter: { motion: 'blur-in',  target: 'whole', stagger: 'simultaneous', at: 0,    dur: 1200 },
          exit:  { motion: 'blur-out', target: 'whole', stagger: 'simultaneous', at: 5000, dur: 800 }
        },
        {
          role: 'sub', scale: 0.4,
          enter: { motion: 'spacing-in',  target: 'word', stagger: 'sequential', at: 1500, dur: 800 },
          exit:  { motion: 'spacing-out', target: 'word', stagger: 'reverse',    at: 4800, dur: 700 }
        }
      ]
    },

    'reverse-cascade': {
      name: 'Up Cascade',
      description: 'Bottom label glides in, sub bounce-slides, hero rises. Hero exits first via drop.',
      duration: 7200,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'rise',           target: 'word', stagger: 'sequential', at: 1600, dur: 700 },
          exit:  { motion: 'drop-out',       target: 'word', stagger: 'sequential', at: 4800, dur: 600 }
        },
        {
          role: 'sub', scale: 0.55,
          enter: { motion: 'bounce-slide-split', target: 'word', stagger: 'sequential', at: 800, dur: 650 },
          exit:  { motion: 'glide-to-r',          target: 'word', stagger: 'sequential', at: 5300, dur: 600 }
        },
        {
          role: 'label', scale: 0.35,
          enter: { motion: 'glide-from-l',   target: 'word', stagger: 'sequential', at: 0,    dur: 600 },
          exit:  { motion: 'glide-to-l',     target: 'word', stagger: 'sequential', at: 5800, dur: 500 }
        }
      ]
    },

    'slide-stack': {
      name: 'Slide Stack',
      description: 'Lines build from alternating sides with bounce and per-letter kick, staggered into a layered stack.',
      duration: 7200,
      minLines: 1, maxLines: 4,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'bounce-slide-split', target: 'word',   stagger: 'sequential', at: 0,    dur: 760 },
          exit:  { motion: 'glide-to-l',         target: 'word',   stagger: 'sequential', at: 5400, dur: 600 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'glide-from-r',       target: 'letter', stagger: 'sequential', at: 650,  dur: 720 },
          exit:  { motion: 'spacing-out',        target: 'whole',  stagger: 'simultaneous', at: 5600, dur: 560 }
        },
        {
          role: 'label', scale: 0.35,
          enter: { motion: 'letter-kick-in',     target: 'letter', stagger: 'sequential', at: 1300, dur: 640 },
          exit:  { motion: 'drop-out',           target: 'letter', stagger: 'sequential', at: 5800, dur: 560 }
        },
        {
          role: 'label', scale: 0.28,
          enter: { motion: 'bounce-slide-l',     target: 'word',   stagger: 'sequential', at: 1950, dur: 640 },
          exit:  { motion: 'glide-to-r',         target: 'word',   stagger: 'sequential', at: 6000, dur: 560 }
        }
      ]
    },

    'pulse-zoom': {
      name: 'Pulse Zoom',
      description: 'Hero zooms with bounce. Sub letter-spaces with kick. Label spring-rises. Each line a different motion.',
      duration: 6000,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'zoom-in',         target: 'whole',  stagger: 'simultaneous', at: 0,    dur: 800 },
          exit:  { motion: 'zoom-out',        target: 'whole',  stagger: 'simultaneous', at: 4700, dur: 500 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'letter-kick-in',  target: 'letter', stagger: 'sequential',   at: 1000, dur: 700 },
          exit:  { motion: 'spacing-out',     target: 'whole',  stagger: 'simultaneous', at: 4500, dur: 500 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'bounce-rise',     target: 'word',   stagger: 'sequential',   at: 1800, dur: 600 },
          exit:  { motion: 'drop-out',        target: 'word',   stagger: 'sequential',   at: 4300, dur: 500 }
        }
      ]
    },

    'typewriter': {
      name: 'Typewriter',
      description: 'Hero strikes in letter-by-letter. Sub drops in. Label glides in. Then the whole thing un-types, erasing backward letter by letter.',
      duration: 8200,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'rise',     target: 'letter', stagger: 'sequential', staggerScale: 2.5, at: 0,    dur: 220 },
          exit:  { motion: 'drop-out', target: 'letter', stagger: 'reverse',    staggerScale: 1.6, at: 6300, dur: 200 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'drop',         target: 'letter', stagger: 'sequential', staggerScale: 2.0, at: 2200, dur: 200 },
          exit:  { motion: 'spacing-out',  target: 'letter', stagger: 'reverse',    staggerScale: 1.4, at: 6650, dur: 200 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'glide-from-l', target: 'letter', stagger: 'sequential', staggerScale: 2.2, at: 4200, dur: 180 },
          exit:  { motion: 'glide-to-r',   target: 'letter', stagger: 'reverse',    staggerScale: 1.4, at: 6950, dur: 190 }
        }
      ]
    },

    'edge-burst': {
      name: 'Edge Burst',
      description: 'Letters converge from the edges to the centre. Hold, then fly out the same way.',
      duration: 6200,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'zoom-in',  target: 'letter', stagger: 'edges-in',   staggerScale: 1.4, at: 0,    dur: 600 },
          exit:  { motion: 'zoom-out', target: 'letter', stagger: 'center-out', staggerScale: 1.4, at: 4800, dur: 500 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'fade-in',  target: 'word', stagger: 'sequential', at: 1400, dur: 600 },
          exit:  { motion: 'fade-out', target: 'word', stagger: 'sequential', at: 4900, dur: 500 }
        }
      ]
    },

    'center-burst': {
      name: 'Center Burst',
      description: 'Letters explode outward from the centre. Pull back to centre on exit.',
      duration: 5800,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'fade-in',  target: 'letter', stagger: 'center-out', staggerScale: 1.4, at: 0,    dur: 600 },
          exit:  { motion: 'fade-out', target: 'letter', stagger: 'edges-in',   staggerScale: 1.4, at: 4500, dur: 500 }
        },
        {
          role: 'sub', scale: 0.4,
          enter: { motion: 'spacing-in',  target: 'whole', stagger: 'simultaneous', at: 1400, dur: 800 },
          exit:  { motion: 'spacing-out', target: 'whole', stagger: 'simultaneous', at: 4500, dur: 600 }
        }
      ]
    },

    'random-rain': {
      name: 'Random Rain',
      description: 'Hero letters drop randomly. Sub letters rise randomly from below. Label blurs in randomly. Chaotic build.',
      duration: 6800,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'drop',     target: 'letter', stagger: 'random', staggerScale: 1.5, at: 0,    dur: 600 },
          exit:  { motion: 'fade-out', target: 'letter', stagger: 'random', staggerScale: 1.5, at: 5000, dur: 500 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'rise',     target: 'letter', stagger: 'random', staggerScale: 1.5, at: 1100, dur: 550 },
          exit:  { motion: 'fade-out', target: 'letter', stagger: 'random', staggerScale: 1.5, at: 5100, dur: 500 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'blur-in',  target: 'letter', stagger: 'random', staggerScale: 1.5, at: 2100, dur: 500 },
          exit:  { motion: 'fade-out', target: 'letter', stagger: 'random', staggerScale: 1.5, at: 5200, dur: 500 }
        }
      ]
    },

    'quick-flash': {
      name: 'Quick Flash',
      description: 'Snappy zoom in, sub kicks in with letter-spacing pulse. Snappy zoom out.',
      duration: 3500,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'zoom-in',  target: 'whole', stagger: 'simultaneous', at: 0,    dur: 380 },
          exit:  { motion: 'zoom-out', target: 'whole', stagger: 'simultaneous', at: 2700, dur: 280 }
        },
        {
          role: 'sub', scale: 0.4,
          enter: { motion: 'letter-kick-in', target: 'letter', stagger: 'sequential',   at: 400, dur: 400 },
          exit:  { motion: 'spacing-out',    target: 'whole',  stagger: 'simultaneous', at: 2600, dur: 300 }
        }
      ]
    },

    'slow-burn': {
      name: 'Slow Burn',
      description: 'Hero blurs in dramatically. Sub spacing-in slow. Cinematic and meditative.',
      duration: 9500,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'blur-in',     target: 'whole', stagger: 'simultaneous', at: 0,    dur: 1800 },
          exit:  { motion: 'blur-out',    target: 'whole', stagger: 'simultaneous', at: 7000, dur: 1700 }
        },
        {
          role: 'sub', scale: 0.4,
          enter: { motion: 'spacing-in',  target: 'whole', stagger: 'simultaneous', at: 1500, dur: 1500 },
          exit:  { motion: 'spacing-out', target: 'whole', stagger: 'simultaneous', at: 7200, dur: 1400 }
        }
      ]
    },

    'side-step': {
      name: 'Side Step',
      description: 'Lines glide in from alternating sides. Exit out the OPPOSITE side they came from.',
      duration: 6500,
      minLines: 1, maxLines: 4,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'glide-from-l', target: 'whole', stagger: 'simultaneous', at: 0,    dur: 650 },
          exit:  { motion: 'glide-to-r',   target: 'whole', stagger: 'simultaneous', at: 5200, dur: 550 }
        },
        {
          role: 'sub', scale: 0.55,
          enter: { motion: 'glide-from-r', target: 'whole', stagger: 'simultaneous', at: 800, dur: 650 },
          exit:  { motion: 'glide-to-l',   target: 'whole', stagger: 'simultaneous', at: 5000, dur: 550 }
        },
        {
          role: 'label', scale: 0.35,
          enter: { motion: 'glide-from-l', target: 'whole', stagger: 'simultaneous', at: 1600, dur: 600 },
          exit:  { motion: 'glide-to-r',   target: 'whole', stagger: 'simultaneous', at: 4800, dur: 500 }
        },
        {
          role: 'label', scale: 0.28,
          enter: { motion: 'glide-from-r', target: 'whole', stagger: 'simultaneous', at: 2400, dur: 600 },
          exit:  { motion: 'glide-to-l',   target: 'whole', stagger: 'simultaneous', at: 4600, dur: 500 }
        }
      ]
    },

    'letter-blur': {
      name: 'Letter Blur',
      description: 'Hero letters blur in, sub letter-spaces in, label letters distort in. Different distortion per line.',
      duration: 6500,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'blur-in',     target: 'letter', stagger: 'sequential', at: 0,    dur: 600 },
          exit:  { motion: 'blur-out',    target: 'letter', stagger: 'sequential', at: 5000, dur: 500 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'spacing-in',  target: 'whole',  stagger: 'simultaneous', at: 900, dur: 700 },
          exit:  { motion: 'spacing-out', target: 'whole',  stagger: 'simultaneous', at: 5100, dur: 500 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'distort-in',  target: 'letter', stagger: 'sequential', intensity: 0.8, at: 1800, dur: 500 },
          exit:  { motion: 'distort-out', target: 'letter', stagger: 'sequential', intensity: 0.8, at: 5200, dur: 500 }
        }
      ]
    },

    'rise-fall': {
      name: 'Rise Fall',
      description: 'Hero rises in dramatically, falls out via drop. Sub bounce-slides from sides. Letter-kick label optional.',
      duration: 6500,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'rise',               target: 'whole', stagger: 'simultaneous', at: 0,    dur: 900 },
          exit:  { motion: 'drop-out',           target: 'whole', stagger: 'simultaneous', at: 5000, dur: 700 }
        },
        {
          role: 'sub', scale: 0.4,
          enter: { motion: 'bounce-slide-split', target: 'word',  stagger: 'sequential',   at: 1200, dur: 650 },
          exit:  { motion: 'glide-to-r',         target: 'word',  stagger: 'sequential',   at: 4800, dur: 500 }
        }
      ]
    },

    'mixed-cascade': {
      name: 'Mixed Cascade',
      description: 'Hero scatter-bursts. Sub bounce-slides from sides. Label blurs in with kick. All blur out.',
      duration: 7200,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'scatter-in', target: 'letter', stagger: 'center-out', staggerScale: 1.2, intensity: 1.3, at: 0, dur: 800 },
          exit:  { motion: 'blur-out',   target: 'whole',  stagger: 'simultaneous', at: 5500, dur: 700 }
        },
        {
          role: 'sub', scale: 0.55,
          enter: { motion: 'bounce-slide-split', target: 'word',   stagger: 'sequential', at: 900,  dur: 700 },
          exit:  { motion: 'blur-out',           target: 'whole',  stagger: 'simultaneous', at: 5500, dur: 700 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'letter-kick-in', target: 'letter', stagger: 'sequential', at: 2000, dur: 600 },
          exit:  { motion: 'blur-out',       target: 'whole',  stagger: 'simultaneous', at: 5500, dur: 700 }
        }
      ]
    },

    'explosion': {
      name: 'Explosion',
      description: 'Letters scatter into place from random angles at full intensity. Sub bounces. Hero blasts out via scatter.',
      duration: 6200,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'scatter-in',  target: 'letter', stagger: 'random', staggerScale: 1.8, intensity: 2.0, at: 0,    dur: 800 },
          exit:  { motion: 'scatter-out', target: 'letter', stagger: 'random', staggerScale: 1.8, intensity: 2.0, at: 4700, dur: 700 }
        },
        {
          role: 'sub', scale: 0.45,
          enter: { motion: 'bounce-slide-split', target: 'word',  stagger: 'sequential',   at: 1100, dur: 650 },
          exit:  { motion: 'glide-to-r',          target: 'word',  stagger: 'sequential',   at: 4800, dur: 500 }
        }
      ]
    },

    'tremor': {
      name: 'Tremor',
      description: 'Hero shakes into place. Sub bounce-slides from sides. Label glitches in. Distinct per line.',
      duration: 5800,
      minLines: 1, maxLines: 3,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'shake-in',           target: 'letter', stagger: 'random',     staggerScale: 0.8, intensity: 1.3, at: 0,    dur: 700 },
          exit:  { motion: 'glitch-out',         target: 'whole',  stagger: 'simultaneous',                   intensity: 0.8, at: 4500, dur: 500 }
        },
        {
          role: 'sub', scale: 0.5,
          enter: { motion: 'bounce-slide-split', target: 'word',   stagger: 'sequential', at: 900, dur: 650 },
          exit:  { motion: 'glide-to-r',         target: 'word',   stagger: 'sequential', at: 4600, dur: 500 }
        },
        {
          role: 'label', scale: 0.32,
          enter: { motion: 'glitch-in',          target: 'whole',  stagger: 'simultaneous', intensity: 0.7, at: 1700, dur: 550 },
          exit:  { motion: 'fade-out',           target: 'whole',  stagger: 'simultaneous', at: 4700, dur: 500 }
        }
      ]
    },

    'static': {
      name: 'Static',
      description: 'Hero noise-flickers in like a broken neon sign. Sub glitches in with chromatic aberration. Holds, fades.',
      duration: 5500,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'noise-flicker', target: 'letter', stagger: 'random', staggerScale: 0.6, intensity: 1.2, at: 0,    dur: 850 },
          exit:  { motion: 'glitch-out',    target: 'whole',  stagger: 'simultaneous',                intensity: 1.0, at: 4300, dur: 700 }
        },
        {
          role: 'sub', scale: 0.4,
          enter: { motion: 'glitch-in',     target: 'word',   stagger: 'random', staggerScale: 0.8, intensity: 1.0, at: 1100, dur: 600 },
          exit:  { motion: 'fade-out',      target: 'whole',  stagger: 'simultaneous',                                 at: 4400, dur: 500 }
        }
      ]
    },

    

    

    

    

    

    

    'glitch': {
      name: 'Glitch',
      description: 'Hero glitches in with chromatic aberration. Sub distorts in through blur-contrast noise. Both fade with glitch-out.',
      duration: 5800,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'glitch-in',  target: 'letter', stagger: 'random', staggerScale: 1.2, intensity: 1.5, at: 0,    dur: 650 },
          exit:  { motion: 'glitch-out', target: 'whole',  stagger: 'simultaneous',                intensity: 1.5, at: 4500, dur: 600 }
        },
        {
          role: 'sub', scale: 0.45,
          enter: { motion: 'distort-in',  target: 'word',  stagger: 'sequential',   intensity: 1.0, at: 1100, dur: 700 },
          exit:  { motion: 'distort-out', target: 'word',  stagger: 'sequential',   intensity: 0.8, at: 4600, dur: 500 }
        }
      ]
    },

    'distortion': {
      name: 'Distortion',
      description: 'Hero resolves out of heavy blur and contrast noise — analog video tuning in. Sub kicks in. Both fade through blur.',
      duration: 6500,
      minLines: 1, maxLines: 2,
      lines: [
        {
          role: 'hero', scale: 1.0,
          enter: { motion: 'distort-in',  target: 'word',  stagger: 'simultaneous', intensity: 1.4, at: 0,    dur: 900 },
          exit:  { motion: 'distort-out', target: 'whole', stagger: 'simultaneous', intensity: 1.4, at: 5000, dur: 700 }
        },
        {
          role: 'sub', scale: 0.45,
          enter: { motion: 'letter-kick-in', target: 'letter', stagger: 'sequential', at: 1400, dur: 600 },
          exit:  { motion: 'spacing-out',    target: 'whole',  stagger: 'simultaneous', at: 5100, dur: 600 }
        }
      ]
    }

  };

  function _isEditor() {
    try { return window.parent !== window; } catch (e) { return false; }
  }
  function _isScreenMode() {
    return document.body.getAttribute('data-screen-mode') === '1'
        || !!document.querySelector('.impulse-screen-wrapper');
  }
  function _ms(base, speed) {
    return Math.max(0, Math.round(base / (speed || 1)));
  }
  
  function _rand(seed) {
    var x = Math.sin(seed * 12.9898 + 78.233) * 43758.5453;
    return x - Math.floor(x);
  }
  
  var _kBrRe = /<br\s*\/?>/gi;
  
  var _kWordRe = /[^\s\u00A0]+(?:\u00A0[^\s\u00A0]+)*/g;
  
  function _parseText(raw) {
    raw = (raw || '').replace(/\r/g, '');
    return raw.split('\n')
      .map(function (s) { return s.trim(); })
      .filter(Boolean)
      .map(function (line) { return { text: line, words: (line.match(_kWordRe) || []) }; });
  }

  var _kDecodeEl = null;
  function _kDecodeEntities(str) {
    if (str == null) return '';
    str = String(str);
    if (str.indexOf('&') === -1) return str;
    if (!_kDecodeEl) _kDecodeEl = document.createElement('textarea');
    _kDecodeEl.innerHTML = str;
    return _kDecodeEl.value;
  }

  var _kMeasCanvas = null, _kMeasCtx = null;
  function _kDescentEm(lineText, family, weight) {
    if (!lineText) return 0.03;
    try {
      if (!_kMeasCtx) { _kMeasCanvas = document.createElement('canvas'); _kMeasCtx = _kMeasCanvas.getContext('2d'); }
      _kMeasCtx.font = (weight || '800') + ' 100px ' + (family || 'sans-serif');
      var m = _kMeasCtx.measureText(lineText);
      if (m && typeof m.actualBoundingBoxDescent === 'number') return m.actualBoundingBoxDescent / 100;
    } catch (e) {}
    return 0.22; 
  }

  function _tierScale(tier, li) {
    if (tier === 'equal') return 1;
    if (tier === 'label') {
      if (li === 0) return 0.34;
      if (li === 1) return 1;
      return 0.42;
    }
    return li === 0 ? 1 : 0.42; 
  }

  function KineticBlock(el) {
    this.el = el;
    this._destroyed = false;
    this._running   = false;
    this._timers    = [];
    this._anims     = [];
    this._io        = null;

    this.composition = el.getAttribute('data-composition') || 'block-slam';
    this.tier        = el.getAttribute('data-tier') || 'hero'; 
    this.lineGap     = parseFloat(el.getAttribute('data-line-gap') || '0'); 
    if (!isFinite(this.lineGap)) this.lineGap = 0;

    this.maxWidthD = parseFloat(el.getAttribute('data-max-width') || '0');
    if (!isFinite(this.maxWidthD) || this.maxWidthD < 0) this.maxWidthD = 0;
    var _mwT = el.getAttribute('data-max-width-tablet');
    this.maxWidthT = (_mwT != null && _mwT !== '') ? parseFloat(_mwT) : null;
    var _mwM = el.getAttribute('data-max-width-mobile');
    this.maxWidthM = (_mwM != null && _mwM !== '') ? parseFloat(_mwM) : null;
    this.maxWidth  = this._pickMaxWidth();
    this.cylDiameter = parseFloat(el.getAttribute('data-cyl-diameter') || '70'); 
    if (!isFinite(this.cylDiameter) || this.cylDiameter <= 0) this.cylDiameter = 70;
    this.accent      = el.getAttribute('data-accent') || 'none'; 
    var _accTxt      = el.getAttribute('data-accent-text') || '';
    try { _accTxt = decodeURIComponent(_accTxt); } catch (e) {}
    this.accentText  = _accTxt;
    this.color       = el.getAttribute('data-color') || '#ffffff';
    
    this.fill        = (el.getAttribute('data-fill') || 'solid').toLowerCase();
    this.gradMode    = (el.getAttribute('data-grad-mode') || 'travel').toLowerCase();
    this.gradScope   = (el.getAttribute('data-grad-scope') || 'line').toLowerCase(); 
    this.gradC1      = el.getAttribute('data-grad-c1') || '#ff2d55';
    this.gradC2      = el.getAttribute('data-grad-c2') || '#5e5cff';
    this.gradSpeed   = parseFloat(el.getAttribute('data-grad-speed') || '1');
    if (!isFinite(this.gradSpeed) || this.gradSpeed <= 0) this.gradSpeed = 1;
    if (this.fill === 'gradient') _kInjectGradCss();
    this.speed       = parseFloat(el.getAttribute('data-speed') || '1');
    this.intensity   = parseFloat(el.getAttribute('data-intensity') || '1');
    if (!isFinite(this.intensity) || this.intensity <= 0) this.intensity = 1;
    this.loop        = el.getAttribute('data-loop') !== '0';
    
    this.cycle       = el.getAttribute('data-cycle') === '1';
    
    var _alignRaw    = (el.getAttribute('data-align') || 'center').toLowerCase();
    this.align       = (_alignRaw === 'left' || _alignRaw === 'right') ? _alignRaw : 'center';
    
    this.scrollExit  = (el.getAttribute('data-scroll-exit') || 'off').toLowerCase();

    
    
    this.leadScreen  = el.getAttribute('data-lead-screen') !== '0'
                       && !(typeof window !== 'undefined' && window._impulseEditorPreview);
    
    this.heightAuto  = el.getAttribute('data-height-auto') === '1';
    
    this._settleMs   = 1500;

    this.customMode = el.getAttribute('data-custom-mode') === '1';
    var customRaw   = el.getAttribute('data-custom-lines') || '[]';
    try { customRaw = decodeURIComponent(customRaw); } catch (e) {}
    try { this.customLines = JSON.parse(customRaw); }
    catch (e) { this.customLines = []; }
    if (Array.isArray(this.customLines)) {
      
      this.customLines.forEach(function (cl) {
        if (cl && typeof cl.text === 'string') cl.text = _kDecodeEntities(cl.text.replace(_kBrRe, '\n'));
      });
      
      var _kExpanded = [];
      this.customLines.forEach(function (cl) {
        if (!cl || typeof cl.text !== 'string' || cl.text.indexOf('\n') === -1) { _kExpanded.push(cl); return; }
        cl.text.split('\n').forEach(function (part) {
          if (part.trim() === '') return; 
          var copy = {}; for (var _ck in cl) copy[_ck] = cl[_ck];
          copy.text = part;
          _kExpanded.push(copy);
        });
      });
      this.customLines = _kExpanded;
    }

    var raw = el.getAttribute('data-text') || '';
    try { raw = decodeURIComponent(raw); } catch (e) {}
    raw = raw.replace(_kBrRe, '\n'); 
    raw = _kDecodeEntities(raw);
    this.lines = _parseText(raw);

    this._setup();
    this._startObserver();
    this._startResizeObserver();
  }

  KineticBlock.prototype._pickMaxWidth = function () {
    var w = window.innerWidth || 9999;
    var v;
    if (w < 768 && this.maxWidthM != null && isFinite(this.maxWidthM)) v = this.maxWidthM;
    else if (w < 1024 && this.maxWidthT != null && isFinite(this.maxWidthT)) v = this.maxWidthT;
    else v = this.maxWidthD;
    return (isFinite(v) && v > 0) ? v : 0;
  };

  KineticBlock.prototype._startResizeObserver = function () {
    var self = this;
    if (!window.ResizeObserver) return;
    var debounce = null;
    try {
      self._ro = new ResizeObserver(function () {
        
        if (self._destroyed || (!self._running && !self._shown)) return;
        
        var w = self.el.offsetWidth, h = self.el.offsetHeight;
        if (Math.abs(w - (self._lastW || 0)) < 3 && Math.abs(h - (self._lastH || 0)) < 3) return;
        if (debounce) clearTimeout(debounce);
        debounce = setTimeout(function () {
          if (self._destroyed || (!self._running && !self._shown)) return;
          var w2 = self.el.offsetWidth, h2 = self.el.offsetHeight;
          if (Math.abs(w2 - (self._lastW || 0)) < 3 && Math.abs(h2 - (self._lastH || 0)) < 3) return;
          var wasShown = self._shown;
          self._clearAll();   
          self._playOnce();
          
          if (wasShown) self._shown = true;
        }, 150); 
      });
      self._ro.observe(self.el);
    } catch (e) {}
  };

  KineticBlock.prototype._fillContainer = function () {
    if (this.el.style.height !== '100%') return; 
    
    var node = this.el;
    for (var i = 0; i < 4 && node && node.parentElement; i++) {
      var parent = node.parentElement;
      var pd;
      try { pd = (node.ownerDocument.defaultView || window).getComputedStyle(parent); }
      catch (e) { break; }
      node.style.flex = '1 1 0';
      node.style.minHeight = '0';
      node.style.alignSelf = 'stretch';
      if (pd.display.indexOf('flex') > -1 && pd.flexDirection === 'column') {
        break; 
      }
      node = parent;
    }
  };

  KineticBlock.prototype._setup = function () {
    this._fillContainer();
    
    this._stage = document.createElement('div');
    this._stage.className = 'impulse-kinetic-stage';
    var _alignItems = this.align === 'left' ? 'flex-start' : (this.align === 'right' ? 'flex-end' : 'center');
    
    this._stage.style.cssText = this.heightAuto
      ? ('position:relative;width:100%;' +
         'display:flex;flex-direction:column;' +
         'align-items:' + _alignItems + ';justify-content:center;' +
         'padding:0;box-sizing:border-box;' +
         'direction:ltr;text-align:' + this.align + ';' +
         'pointer-events:none;overflow:visible;' +
         'color:' + this.color + ';' +
         'font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, sans-serif;' +
         'font-weight:800;line-height:1;')
      : ('position:absolute;inset:0;' +
         'display:flex;flex-direction:column;' +
         'align-items:' + _alignItems + ';justify-content:center;' +
         'padding:0;box-sizing:border-box;' +
         'direction:ltr;text-align:' + this.align + ';' +
         'pointer-events:none;overflow:hidden;' +
         'color:' + this.color + ';' +
         'font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, sans-serif;' +
         'font-weight:800;line-height:1;');
    this.el.appendChild(this._stage);
  };

  function _kInjectGradCss() {
    if (typeof document === 'undefined') return;
    if (document.getElementById('impulse-kinetic-grad-css')) return;
    var st = document.createElement('style');
    st.id = 'impulse-kinetic-grad-css';
    
    st.textContent =
      '@property --kg-angle{syntax:"<angle>";inherits:true;initial-value:0deg;}' +
      '@property --kg{syntax:"<number>";inherits:true;initial-value:0;}' +
      '@keyframes kg-spin{to{--kg-angle:360deg;}}' +
      '@keyframes kg-roll{to{--kg:1;}}';
    (document.head || document.documentElement).appendChild(st);
  }

  function _kScreenEl(el) {
    if (!el || !el.closest) return null;
    var scr = el.closest('.impulse-screen');
    if (scr) return scr;
    var sec = el.closest('.impulse-el[data-type="section"]');
    while (sec) {
      var up = sec.parentElement && sec.parentElement.closest('.impulse-el[data-type="section"]');
      if (!up) break;
      sec = up;
    }
    return sec || null;
  }

  KineticBlock.prototype._classicSafetyNet = function (delay) {
    var self = this;
    if (self._safetyArmed) return;          
    self._safetyArmed = true;

    var tryRun = function () {
      if (self._destroyed || self._shown || self._exiting || self._running) return false;
      var el = self.el;

      

      

      var vis = (typeof el.checkVisibility === 'function')
        ? el.checkVisibility({ visibilityProperty: true })
        : (el.offsetParent !== null);
      if (!vis) return false;
      var r = el.getBoundingClientRect();
      var vh = window.innerHeight || 0, vw = window.innerWidth || 0;
      
      if (!(r.bottom > 1 && r.top < vh && r.right > 1 && r.left < vw && r.width > 1)) return false;
      if (el.offsetWidth > 0 && (r.width / el.offsetWidth) < 0.85) return false;
      self._run();
      return true;
    };

    var stop = function () {
      if (self._safetyPoll) { clearTimeout(self._safetyPoll); self._safetyPoll = null; }
      if (self._safetyIO) { try { self._safetyIO.disconnect(); } catch (e) {} self._safetyIO = null; }
    };
    self._safetyStop = stop;

    var attempts = 0, maxAttempts = 16; 
    var poll = function () {
      self._safetyPoll = null;
      if (tryRun()) { stop(); return; }
      if (self._destroyed || self._shown) { stop(); return; }
      if (++attempts < maxAttempts) self._safetyPoll = setTimeout(poll, 500);
    };

    if (window.IntersectionObserver) {
      self._safetyIO = new IntersectionObserver(function (entries) {
        if (self._shown || self._destroyed) { stop(); return; }
        if (entries[0] && entries[0].isIntersecting && tryRun()) stop();
      }, { threshold: 0.15 });
      self._safetyIO.observe(self.el);
    }

    self._safetyPoll = setTimeout(poll, delay || 700);
  };

  KineticBlock.prototype._startObserver = function () {
    var self = this;
    if (_isScreenMode()) {
      var sc = _kScreenEl(self.el);
      self._screenEl = sc || null;
      
      var _runtimeScreens = !!document.querySelector('.impulse-screen-wrapper');
      if (_runtimeScreens && sc) {
        if (sc.classList.contains('impulse-screen-active')) self._run();
        else self._classicSafetyNet(700); 
        return;
      }
      
      if (sc && window.MutationObserver) {
        
        var _entering = function () { return sc.classList.contains('impulse-screen-entering'); };
        var _active   = function () { return sc.classList.contains('impulse-screen-active'); };
        if (_entering() || _active()) { if (_active()) self._wasActive = true; self._run(); }
        self._screenMO = new MutationObserver(function () {
          if (_active()) self._wasActive = true;
          if (_entering() || _active()) {
            if (!self._shown && !self._exiting) self._run();
          } else if (self._wasActive && self._shown && !self._exiting) {
            
            self._pause();
            self._wasActive = false;
          }
        });
        self._screenMO.observe(sc, { attributes: true, attributeFilter: ['class'] });
        self._classicSafetyNet(600); 
        return;
      }
      
    }
    if (!window.IntersectionObserver) { self._run(); return; }
    self._io = new IntersectionObserver(function (entries) {
      var e = entries[0];
      if (e.isIntersecting && !self._shown && !self._exiting) self._run();
      else if (!e.isIntersecting && self._shown && !self._exiting) {
        if (self.scrollExit === 'spring' || self.scrollExit === 'fade') self._scrollExitAnim(self.scrollExit);
        else self._pause();
      }
    }, { threshold: 0.15 });
    self._io.observe(self.el);
  };

  KineticBlock.prototype._scrollExitAnim = function (style) {
    var self = this;
    if (self._exiting || !self._stage) { self._pause(); return; }
    self._exiting = true;
    var dur = _ms(style === 'spring' ? 520 : 620, self.speed);
    var kf  = (style === 'spring')
      ? [{ transform: 'translateY(0) scale(1)', opacity: 1 }, { transform: 'translateY(-14%) scale(0.9)', opacity: 0 }]
      : [{ transform: 'translateY(0)', opacity: 1 },          { transform: 'translateY(-8%)', opacity: 0 }];
    var ease = (style === 'spring') ? SPRING_OUT : 'ease-in-out';
    try { self._anims.push(self._stage.animate(kf, { duration: dur, easing: ease, fill: 'forwards' })); } catch (e) {}
    var t = setTimeout(function () {
      self._exiting = false;
      self._pause(); 
      
      if (self._screenEl && self._screenEl.classList.contains('impulse-screen-active')) self._run();
    }, dur + 40);
    self._timers.push(t);
  };

  function _ensureLeadCss() {
    if (_ensureLeadCss._done) return;
    var doc = document;
    if (doc.getElementById('impulse-kinetic-lead-css')) { _ensureLeadCss._done = true; return; }
    var st = doc.createElement('style');
    st.id = 'impulse-kinetic-lead-css';
    st.textContent =
      
      '.impulse-kinetic-lead .impulse-el:not(:has(.impulse-kinetic-block)):not(:has(section.impulse-frosted)){opacity:0 !important;transition:none;}' +
      
      '.impulse-kinetic-lead.impulse-kinetic-revealed .impulse-el:not(:has(.impulse-kinetic-block)):not(:has(section.impulse-frosted)){opacity:1 !important;transition:opacity 480ms ease-in-out;}';
    (doc.head || doc.documentElement).appendChild(st);
    _ensureLeadCss._done = true;
  }

  KineticBlock.prototype._leadGate = function () {
    var sc = this._screenEl;
    if (!sc || !this.leadScreen) return;
    _ensureLeadCss();
    if (this._leadTimer) { clearTimeout(this._leadTimer); this._leadTimer = null; }
    
    sc.classList.remove('impulse-kinetic-revealed');
    sc.classList.add('impulse-kinetic-lead');
  };
  
  KineticBlock.prototype._leadScheduleRelease = function () {
    if (!this._screenEl || !this.leadScreen) return;
    var self = this;
    if (this._leadTimer) clearTimeout(this._leadTimer);
    this._leadTimer = setTimeout(function () { self._leadRelease(); },
                                 Math.max(300, this._settleMs || 1500));
    this._timers.push(this._leadTimer);
  };
  KineticBlock.prototype._leadRelease = function () {
    var sc = this._screenEl;
    if (!sc) return;
    
    sc.classList.add('impulse-kinetic-revealed');
    var self = this;
    var t = setTimeout(function () {
      sc.classList.remove('impulse-kinetic-lead');
      sc.classList.remove('impulse-kinetic-revealed');
    }, 540);
    this._timers.push(t);
  };

  KineticBlock.prototype._run = function () {
    if (this._destroyed) return;
    
    if (this._running || this._shown) return;
    this._running = true;
    if (this.leadScreen) this._leadGate();   
    this._playOnce();      
    this._shown   = true;  
    if (this.leadScreen) this._leadScheduleRelease();
  };
  KineticBlock.prototype._pause = function () {
    this._running = false;
    this._exiting = false;
    this._clearAll();
    
    if (this.leadScreen) this._leadGate();
  };
  KineticBlock.prototype._clearAll = function () {
    this._shown = false;
    this._anims.forEach(function (a) { try { a.cancel(); } catch (e) {} });
    this._anims = [];
    this._timers.forEach(clearTimeout);
    this._timers = [];
    if (this._stage) {
      this._stage.innerHTML = '';
      
      this._stage.style.perspective = '';
      this._stage.style.perspectiveOrigin = '';
      this._stage.style.clipPath = '';
      this._stage.style.webkitClipPath = '';
      
      this._stage.style.webkitMaskImage = '';
      this._stage.style.maskImage = '';
      
      this._stage.style.transform = '';
      this._stage.style.opacity = '';
    }
  };

  KineticBlock.prototype._playOnce = function () {
    var self = this;
    self._clearAll();
    self.maxWidth = self._pickMaxWidth(); 
    
    self._lastW = self.el.offsetWidth;
    self._lastH = self.el.offsetHeight;

    var comp = COMPOSITIONS[self.composition] || COMPOSITIONS['block-slam'];

    var useCustom = !!(self.customMode && self.customLines && self.customLines.length &&
                       self.customLines.some(function(cl){ return cl && (cl.text || '').trim(); }));

    if (!useCustom && comp && comp.continuous && self.composition === 'cylinder' &&
        typeof self._playCylinder === 'function') {
      self._playCylinder();
      return;
    }
    if (!useCustom && self.composition === 'arc' && typeof self._playArc === 'function') {
      self._playArc();
      return;
    }
    if (!useCustom && self.composition === 'slide' && typeof self._playSlide === 'function') {
      self._playSlide();
      return;
    }
    if (!useCustom && self.composition === 'brutal' && typeof self._playBrutal === 'function') {
      self._playBrutal();
      return;
    }
    if (!useCustom && self.composition === 'sweep' && typeof self._playSweep === 'function') {
      self._playSweep();
      return;
    }
    
    var hasCustomText = self.customMode &&
                        Array.isArray(self.customLines) &&
                        self.customLines.some(function(cl){ return cl && (cl.text || '').trim(); });
    if (!hasCustomText && (!self.lines.length || !comp.lines.length)) {
      self._stage.innerHTML =
        '<span style="font-size:64px;opacity:0.18;letter-spacing:0.02em">Kinetic</span>';
      self._running = false;
      return;
    }

    var AUTO_EXTEND_POOL = [
      { enter: 'bounce-slide-split', exit: 'glide-to-r',   target: 'word',   stagger: 'sequential' },
      { enter: 'letter-kick-in',     exit: 'spacing-out',  target: 'letter', stagger: 'sequential' },
      { enter: 'rise',               exit: 'rise-out',     target: 'letter', stagger: 'sequential' },
      { enter: 'drop',               exit: 'drop-out',     target: 'letter', stagger: 'sequential' },
      { enter: 'glide-from-l',       exit: 'glide-to-l',   target: 'word',   stagger: 'sequential' },
      { enter: 'glide-from-r',       exit: 'glide-to-r',   target: 'word',   stagger: 'sequential' },
      { enter: 'zoom-in',            exit: 'zoom-out',     target: 'whole',  stagger: 'simultaneous' },
      { enter: 'blur-in',            exit: 'blur-out',     target: 'whole',  stagger: 'simultaneous' },
      { enter: 'spacing-in',         exit: 'spacing-out',  target: 'whole',  stagger: 'simultaneous' },
      { enter: 'wipe-right',         exit: 'wipe-right-out',target:'word',   stagger: 'sequential' }
    ];

    var maxN, slots, texts, compSlots, compSlotCount;

    self._useCustom = useCustom;
    if (useCustom) {
      var customN = Math.min(4, self.customLines.length);
      maxN = customN;
      slots = [];
      texts = [];
      var compDuration = 5500;
      var entryStaggerMs = 700;
      var exitStaggerMs  = 100;
      var exitAtBase     = compDuration - 800;
      for (var ci = 0; ci < customN; ci++) {
        var cl = self.customLines[ci] || {};
        var motion = cl.motion || 'rise';
        var exitMotion = cl.exit_motion || AUTO_EXIT_PAIRS[motion] || 'fade-out';
        var tgt    = cl.target  || AUTO_TARGET[motion] || 'letter';
        
        var _enterDur = (MOTIONS[motion] && MOTIONS[motion].duration) || 650;
        var _exitDur  = (MOTIONS[exitMotion] && MOTIONS[exitMotion].duration) || 500;
        var slScale = parseFloat(cl.scale);
        if (!isFinite(slScale) || slScale <= 0) slScale = 1.0;
        var slPx = parseFloat(cl.px);
        if (!isFinite(slPx) || slPx <= 0) slPx = 0; 
        slots.push({
          role:  'custom',
          scale: slScale,
          px:    slPx,
          font:  cl.font   || '',
          weight: cl.weight || '',
          color: cl.color  || '',
          enter: {
            motion:  motion,
            target:  tgt,
            stagger: cl.stagger || 'sequential',
            at:      (cl.at  != null) ? cl.at  : ci * entryStaggerMs,
            dur:     cl.dur != null ? cl.dur : _enterDur
          },
          exit: {
            motion:  exitMotion,
            target:  tgt,
            stagger: cl.stagger || 'sequential',
            at:      (cl.exit_at != null) ? cl.exit_at : exitAtBase + ci * exitStaggerMs,
            dur:     cl.exit_dur != null ? cl.exit_dur : _exitDur
          }
        });
        var txt = (cl.text || '').trim();
        texts.push({ text: txt, words: txt.match(_kWordRe) || [] });
      }
      compSlots     = [];
      compSlotCount = 0;
    } else {
      maxN          = Math.min(4, self.lines.length);
      compSlots     = comp.lines || [];
      compSlotCount = compSlots.length;
      slots         = [];
    }

    if (!useCustom) {
      var usedMotionsForPool = {};
      for (var cii = 0; cii < Math.min(compSlotCount, maxN); cii++) {
        if (compSlots[cii].enter && compSlots[cii].enter.motion) {
          usedMotionsForPool[compSlots[cii].enter.motion] = true;
        }
      }

      for (var si = 0; si < maxN; si++) {
        if (si < compSlotCount) {
          slots.push(compSlots[si]);
        } else {
          var lastDefined = compSlots[compSlotCount - 1] || { enter: { at: 0 }, exit: { at: 4500 } };
          var stepsBeyond = si - compSlotCount + 1;
          var available = AUTO_EXTEND_POOL.filter(function (m) {
            return !usedMotionsForPool[m.enter];
          });
          if (!available.length) available = AUTO_EXTEND_POOL;
          var pick = available[0];

          var baseEnterAt = (lastDefined.enter && lastDefined.enter.at) || 0;
          var baseExitAt  = (lastDefined.exit  && lastDefined.exit.at)  || 4500;
          slots.push({
            role:  'label',
            scale: 1.0,
            enter: {
              motion:  pick.enter,
              target:  pick.target,
              stagger: pick.stagger,
              at:      baseEnterAt + stepsBeyond * 800,
              dur:     600
            },
            exit: {
              motion:  pick.exit,
              target:  pick.target,
              stagger: pick.stagger,
              at:      Math.max(0, baseExitAt - stepsBeyond * 100),
              dur:     500
            }
          });
          usedMotionsForPool[pick.enter] = true;
        }
      }
      texts = self.lines.slice(0, maxN);
    }

    var stageH    = self._stage.offsetHeight || self.el.offsetHeight || 300;
    var stageW    = self._stage.offsetWidth  || self.el.offsetWidth  || 600;
    
    self._stage.style.perspective = Math.round(stageW * 1.4) + 'px';
    self._stage.style.perspectiveOrigin = '50% 50%';
    var divisor, heroFont;
    if (self.heightAuto) {
      
      divisor  = 1;
      heroFont = Math.min(64, stageW * 0.16);
    } else if (useCustom) {
      
      var sumScales = 0, _pxBudgetH = 0;
      for (var ss = 0; ss < slots.length; ss++) {
        var _sp = parseFloat(slots[ss].px);
        if (isFinite(_sp) && _sp > 0) { _pxBudgetH += _sp * 1.15; continue; }
        var s_scale = parseFloat(slots[ss].scale);
        if (!isFinite(s_scale) || s_scale <= 0) s_scale = 1;
        sumScales += s_scale;
      }
      divisor  = sumScales > 0 ? sumScales : 1;
      var _relBudgetH = Math.max(40, stageH * 0.9 - _pxBudgetH);
      heroFont = Math.min(_relBudgetH / (divisor * 1.15), stageW * 0.16);
    } else {
      divisor  = slots.length || 1; 
      heroFont = Math.min(stageH * 0.9 / (divisor * 1.15), stageW * 0.16);
    }

    var lineEls = [];
    slots.forEach(function (slot, idx) {
      var tx = texts[idx];
      if (!tx) return;
      var le = self._buildLine(tx, slot, heroFont);
      self._stage.appendChild(le);
      lineEls.push({ el: le, slot: slot });
    });

    var _gapPx = Math.round((self.lineGap / 100) * heroFont * 0.6);
    if (_gapPx) {
      for (var _gi = 1; _gi < lineEls.length; _gi++) {
        lineEls[_gi].el.style.marginTop = _gapPx + 'px';
      }
    }

    if (lineEls.length) {
      void self._stage.offsetWidth; 
      var _pxH = 0, _relH = 0, _relWidest = 0, _relEls = [];
      
      var _gbScale = (function () {
        var sr = self._stage.getBoundingClientRect();
        var ow = self._stage.offsetWidth || 0;
        var s = (ow > 0) ? (sr.width / ow) : 1;
        return (s > 0) ? s : 1;
      })();
      lineEls.forEach(function (le) {
        var h = le.el.offsetHeight;
        if (le.slot && parseFloat(le.slot.px) > 0) { _pxH += h; return; }
        _relH += h;
        _relEls.push(le);
        
        var kids = le.el.children;
        if (kids.length) {
          var w = (kids[kids.length - 1].getBoundingClientRect().right - kids[0].getBoundingClientRect().left) / _gbScale;
          if (w > _relWidest) _relWidest = w;
        }
      });
      if (_relEls.length) {
        var _gaps   = _gapPx * Math.max(0, lineEls.length - 1);
        var budgetH = stageH * 0.90;
        var budgetW = stageW * 0.92;
        var _relRoom = Math.max(20, budgetH - _pxH - _gaps); 
        
        var fitH = self.heightAuto ? 1 : ((_relH > _relRoom && _relH > 0) ? _relRoom / _relH : 1);
        
        var fitW = self.maxWidth > 0 ? 1 : ((_relWidest > budgetW && _relWidest > 0) ? budgetW / _relWidest : 1);
        var fit  = Math.min(fitH, fitW);
        if (fit < 1) {
          _relEls.forEach(function (le) {
            var cur = parseFloat(le.el.style.fontSize) || 0;
            if (cur > 0) le.el.style.fontSize = Math.max(8, Math.round(cur * fit)) + 'px';
          });
        }
      }

      var _safeW = stageW * 0.92;
      void self._stage.offsetWidth; 
      
      if (!self.maxWidth) lineEls.forEach(function (le) {
        if (le.slot && parseFloat(le.slot.px) > 0) {
          
          var _cw = le.el.clientWidth, _sw = le.el.scrollWidth;
          if (_sw > _cw + 1 && _cw > 0) {
            var _cp = parseFloat(le.el.style.fontSize) || 0;
            if (_cp > 0) le.el.style.fontSize = Math.max(8, Math.floor(_cp * (_cw / _sw))) + 'px';
          }
          return;
        }
        var kids = le.el.children;
        if (!kids.length) return;
        var w = (kids[kids.length - 1].getBoundingClientRect().right -
                kids[0].getBoundingClientRect().left) / _gbScale;
        if (w > _safeW && w > 0) {
          var cur = parseFloat(le.el.style.fontSize) || 0;
          if (cur > 0) le.el.style.fontSize = Math.max(8, Math.floor(cur * (_safeW / w))) + 'px';
        }
      });
      
      if (self.heightAuto) {
        self._lastW = self.el.offsetWidth;
        self._lastH = self.el.offsetHeight;
      }
    }

    if (self.fill === 'gradient' && lineEls.length) self._applyGradientScope(lineEls);

    var maxEnd = 0;
    var maxEnterEnd = 0;   
    var _kCycle = (useCustom && self.cycle && lineEls.length > 1);
    if (_kCycle) {
      
      self._stage.style.display    = 'grid';
      
      self._stage.style.alignItems   = 'center';
      var _hAlign = self.align === 'left' ? 'start'
                  : (self.align === 'right' ? 'end' : 'center');
      self._stage.style.justifyItems = _hAlign;
      
      self._stage.style.justifyContent = _hAlign;
      var _holdBase = 2200;   
      var _tBase = 0;
      lineEls.forEach(function (le, i) {
        le.el.style.gridArea = '1 / 1';
        var _en = le.slot.enter || {}, _ex = le.slot.exit || {};
        var _enterDurBase = _en.dur || ((MOTIONS[_en.motion] || {}).duration) || 650;
        var _exitDurBase  = _ex.dur || ((MOTIONS[_ex.motion] || {}).duration) || 500;
        self._schedule(le.el, { motion: _en.motion, target: _en.target, stagger: _en.stagger, dur: _en.dur, at: _tBase });
        if (i === 0) maxEnterEnd = _ms(_tBase + _enterDurBase, self.speed); 
        var _exitAt = _tBase + _enterDurBase + _holdBase;
        self._schedule(le.el, { motion: _ex.motion, target: _ex.target, stagger: _ex.stagger, dur: _ex.dur, at: _exitAt });
        _tBase = _exitAt + _exitDurBase + 300; 
      });
      maxEnd = _ms(_tBase, self.speed);
    } else {
      lineEls.forEach(function (le) {
        var endEnter = self._schedule(le.el, le.slot.enter);
        if (endEnter > maxEnd) maxEnd = endEnter;
        if (endEnter > maxEnterEnd) maxEnterEnd = endEnter;
        
        if (self.loop) {
          var endExit = self._schedule(le.el, le.slot.exit);
          if (endExit > maxEnd) maxEnd = endExit;
        }
      });
    }

    if (comp.decor && comp.decor.length) {
      
      void self._stage.offsetWidth;
      var stageRect = self._stage.getBoundingClientRect();
      comp.decor.forEach(function (dc) {
        if (!dc || !dc.shape) return;
        var shape = DECOR_SHAPES[dc.shape];
        if (!shape) return;
        
        var attachRect;
        if (dc.attach === 'stage' || dc.attach === 'all') {
          attachRect = stageRect;
        } else {
          var lineIdx = (dc.attach != null) ? dc.attach : 0;
          var lineEntry = lineEls[lineIdx];
          if (!lineEntry) return;
          attachRect = lineEntry.el.getBoundingClientRect();
        }
        var decorEl  = self._buildDecor(shape, attachRect, stageRect, dc);
        if (!decorEl) return;
        
        self._applyDecorInitial(decorEl, shape, dc.enter);
        self._stage.appendChild(decorEl);
        var dEnd  = self._scheduleDecor(decorEl, shape, dc.enter);
        if (dEnd  > maxEnd) maxEnd = dEnd;
        if (dEnd  > maxEnterEnd) maxEnterEnd = dEnd;
        
        if (self.loop) {
          var dExit = self._scheduleDecor(decorEl, shape, dc.exit);
          if (dExit > maxEnd) maxEnd = dExit;
        }
      });
    }

    var totalDur = Math.max(_ms(comp.duration, self.speed), maxEnd);

    self._settleMs = Math.max(300, maxEnterEnd + 120);

    var tDone = setTimeout(function () {
      if (self._destroyed) return;
      if (self.loop || _kCycle) self._playOnce();
      else self._running = false;
    }, totalDur + 200);
    self._timers.push(tDone);
  };

  KineticBlock.prototype._playCylinder = function () {
    var self = this;

    var srcLines = (self.lines || []).map(function (l) { return l.text; }).filter(Boolean);
    if (self.customMode && Array.isArray(self.customLines) && self.customLines.length) {
      var cl = self.customLines
        .map(function (c) { return (c && c.text || '').trim(); })
        .filter(Boolean);
      if (cl.length) srcLines = cl;
    }
    srcLines = srcLines.slice(0, 4); 
    if (!srcLines.length) {
      self._stage.innerHTML =
        '<span style="font-size:64px;opacity:0.18;letter-spacing:0.02em">Kinetic</span>';
      self._running = false;
      return;
    }

    var numLines = srcLines.length;
    var repeats  = Math.ceil(6 / numLines);
    var N        = numLines * repeats; 

    var stageH = self._stage.offsetHeight || self.el.offsetHeight || 300;
    var stageW = self._stage.offsetWidth  || self.el.offsetWidth  || 600;
    var ix     = self.intensity || 1;

    var refDim   = Math.min(stageW, stageH);
    var diaPct   = (this.cylDiameter > 0 ? this.cylDiameter : 70) / 100;
    var R        = refDim * (diaPct / 2) * (0.96 + ix * 0.04);    
    var h        = Math.max(16, Math.round(2 * R * Math.sin(Math.PI / N))); 
    var apothem  = Math.round(R * Math.cos(Math.PI / N));         
    
    var fontSize = Math.max(12, Math.round(h * 0.82));
    var _budgetW = stageW * 0.86;
    var _meas = document.createElement('span');
    _meas.style.cssText = 'position:absolute;left:-9999px;top:-9999px;white-space:nowrap;' +
      'visibility:hidden;font-weight:800;line-height:1;font-size:' + fontSize + 'px;';
    self._stage.appendChild(_meas);
    var _widest = 0;
    for (var _li = 0; _li < srcLines.length; _li++) {
      _meas.textContent = srcLines[_li];
      if (_meas.offsetWidth > _widest) _widest = _meas.offsetWidth;
    }
    if (_meas.parentNode) _meas.parentNode.removeChild(_meas);
    if (_widest > _budgetW && _widest > 0) {
      fontSize = Math.max(12, Math.floor(fontSize * (_budgetW / _widest)));
    }

    self._stage.style.perspective = Math.round(stageW * 1.2) + 'px';
    self._stage.style.perspectiveOrigin = '50% 24%';

    var _cylFade = 'linear-gradient(to bottom,'
                 + 'rgba(0,0,0,0) 5%,'
                 + 'rgba(0,0,0,0.30) 20%,'
                 + 'rgba(0,0,0,0.60) 33%,'
                 + 'rgba(0,0,0,1) 43%,'
                 + 'rgba(0,0,0,1) 57%,'
                 + 'rgba(0,0,0,0.60) 67%,'
                 + 'rgba(0,0,0,0.30) 80%,'
                 + 'rgba(0,0,0,0) 95%)';
    self._stage.style.webkitMaskImage = _cylFade;
    self._stage.style.maskImage = _cylFade;

    var scaleWrap = document.createElement('div');
    scaleWrap.style.cssText =
      'position:absolute;inset:0;transform-style:preserve-3d;will-change:transform,opacity;';
    var ring = document.createElement('div');
    ring.style.cssText =
      'position:absolute;inset:0;transform-style:preserve-3d;will-change:transform;';

    for (var i = 0; i < N; i++) {
      var face = document.createElement('div');
      
      var angle = -(360 / N) * i;
      face.textContent = srcLines[i % numLines];
      face.style.cssText =
        'position:absolute;left:0;width:100%;top:50%;height:' + h + 'px;' +
        'display:flex;align-items:center;justify-content:center;' +
        'transform:translateY(-50%) rotateX(' + angle + 'deg) translateZ(' + apothem + 'px);' +
        'backface-visibility:hidden;white-space:nowrap;overflow:hidden;' +
        'font-size:' + fontSize + 'px;line-height:1;font-weight:800;' +
        'color:' + self.color + ';will-change:transform;';
      ring.appendChild(face);
    }

    scaleWrap.appendChild(ring);
    self._stage.appendChild(scaleWrap);

    var dur      = _ms((COMPOSITIONS.cylinder && COMPOSITIONS.cylinder.duration) || 8000, self.speed); 
    var spinUpMs = _ms(1400, self.speed);
    var scaleMs  = _ms(1100, self.speed);

    var restAngle = self.loop ? 0 : (360 / N);
    try {
      scaleWrap.style.transform = 'scale(1.9)';
      scaleWrap.style.opacity = '0';
      self._anims.push(scaleWrap.animate(
        [{ transform: 'scale(1.9)', opacity: 0 },
         { opacity: 1, offset: 0.35 },
         { transform: 'scale(1)', opacity: 1 }],
        { duration: scaleMs, easing: 'cubic-bezier(.16,1,.3,1)', fill: 'forwards' }
      ));
    } catch (e) {}
    try {
      
      self._anims.push(ring.animate(
        [{ transform: 'rotateX(' + (restAngle - 540) + 'deg)' },
         { transform: 'rotateX(' + restAngle + 'deg)' }],
        { duration: spinUpMs, easing: 'cubic-bezier(.1,.85,.2,1)', fill: 'forwards' }
      ));
    } catch (e) {}
    
    if (self.loop) {
      var _loopT = setTimeout(function () {
        if (self._destroyed || !self._running) return;
        try {
          self._anims.push(ring.animate(
            [{ transform: 'rotateX(0deg)' }, { transform: 'rotateX(360deg)' }],
            { duration: dur, iterations: Infinity, easing: 'linear' }
          ));
        } catch (e) {}
      }, spinUpMs);
      self._timers.push(_loopT);
    }

    self._running = true;
  };

  KineticBlock.prototype._drawAccents = function () {
    var self = this;
    var a = self.accent;
    if (!a || a === 'none') return;
    var stage = self._stage;
    var W = stage.offsetWidth  || self.el.offsetWidth  || 600;
    var H = stage.offsetHeight || self.el.offsetHeight || 300;
    var col = self.color;
    var dur = _ms(650, self.speed);
    function mk(css) { var d = document.createElement('div'); d.style.cssText = 'position:absolute;will-change:opacity;' + css; return d; }
    function fade(el, to) {
      el.style.opacity = '0'; stage.appendChild(el);
      try { self._anims.push(el.animate([{ opacity: 0 }, { opacity: to }], { duration: dur, easing: 'ease-in-out', fill: 'forwards' })); } catch (e) {}
    }
    if (a === 'slash' || a === 'backslash') {
      var sk = (a === 'backslash') ? 12 : -12;
      var s = mk('left:7%;top:15%;font-size:' + Math.round(H * 0.13) + 'px;font-weight:800;line-height:1;color:' + col + ';letter-spacing:0.04em;transform:skewX(' + sk + 'deg);');
      s.textContent = (a === 'backslash') ? '\\\\' : '//';
      fade(s, 1);
    } else if (a === 'brackets') {
      var bw = Math.max(10, Math.round(W * 0.025)), bh = Math.round(H * 0.5), th = Math.max(3, Math.round(H * 0.014));
      fade(mk('left:5%;top:25%;width:' + bw + 'px;height:' + bh + 'px;border-left:' + th + 'px solid ' + col + ';border-top:' + th + 'px solid ' + col + ';border-bottom:' + th + 'px solid ' + col + ';'), 1);
      fade(mk('right:5%;top:25%;width:' + bw + 'px;height:' + bh + 'px;border-right:' + th + 'px solid ' + col + ';border-top:' + th + 'px solid ' + col + ';border-bottom:' + th + 'px solid ' + col + ';'), 1);
    } else if (a === 'frame') {
      fade(mk('left:5%;right:5%;top:7%;bottom:7%;box-sizing:border-box;border:' + Math.max(2, Math.round(H * 0.011)) + 'px solid ' + col + ';'), 1);
    } else if (a === 'numeral') {
      var t = (self.accentText || '').trim() || '01';
      var nm = mk('left:0;right:0;top:50%;transform:translateY(-50%);text-align:center;font-size:' + Math.round(H * 0.62) + 'px;font-weight:900;line-height:1;color:' + col + ';');
      nm.textContent = t;
      nm.style.opacity = '0';
      stage.insertBefore(nm, stage.firstChild); 
      try { self._anims.push(nm.animate([{ opacity: 0 }, { opacity: 0.12 }], { duration: dur, easing: 'ease-in-out', fill: 'forwards' })); } catch (e) {}
    }
  };

  KineticBlock.prototype._playArc = function () {
    var self = this;
    self._clearAll(); 

    var srcLines = (self.lines || []).map(function (l) { return l.text; }).filter(Boolean);
    if (self.customMode && Array.isArray(self.customLines) && self.customLines.length) {
      var cl = self.customLines
        .map(function (c) { return (c && c.text || '').trim(); })
        .filter(Boolean);
      if (cl.length) srcLines = cl;
    }
    srcLines = srcLines.slice(0, 4);
    if (!srcLines.length) {
      self._stage.innerHTML =
        '<span style="font-size:64px;opacity:0.18;letter-spacing:0.02em">Kinetic</span>';
      self._running = false;
      return;
    }

    var stageH = self._stage.offsetHeight || self.el.offsetHeight || 300;
    var stageW = self._stage.offsetWidth  || self.el.offsetWidth  || 600;
    var ix     = self.intensity || 1;

    var persp = Math.round(stageW * 1.15);
    self._stage.style.perspective = persp + 'px';
    self._stage.style.perspectiveOrigin = '50% 50%';

    var fontSize = Math.max(16, Math.round(Math.min(
      stageH * 0.82 / (srcLines.length * 1.3),
      stageW * 0.14
    )));

    var rig = document.createElement('div');
    rig.style.cssText =
      'position:absolute;inset:0;display:flex;flex-direction:column;' +
      'align-items:center;justify-content:center;gap:' + Math.round(fontSize * 0.16) + 'px;' +
      'transform-style:preserve-3d;will-change:transform;';

    var flyers = [];
    srcLines.forEach(function (text, li) {
      var lineEl = document.createElement('div');
      lineEl.style.cssText =
        'display:flex;justify-content:center;align-items:baseline;' +
        'white-space:nowrap;transform-style:preserve-3d;' +
        'font-size:' + Math.round(fontSize * _tierScale(self.tier, li)) + 'px;line-height:1.1;font-weight:800;' +
        'color:' + self.color + ';';
      [].forEach.call(text, function (ch) {
        var span = document.createElement('span');
        var isSpace = (ch === ' ');
        span.textContent = isSpace ? ' ' : ch; 
        span.style.cssText =
          'display:inline-block;transform-style:preserve-3d;' +
          'will-change:transform,opacity;backface-visibility:hidden;';
        if (!isSpace) {
          var s = flyers.length;
          
          var zin  = Math.round(persp * (0.80 + 0.12 * _rand(s * 3 + 1)));   
          var ryin = Math.round((_rand(s * 3 + 2) * 2 - 1) * 52 * ix);       
          var rzin = Math.round((_rand(s * 3 + 3) * 2 - 1) * 18);
          var dxin = Math.round((_rand(s * 5 + 1) * 2 - 1) * stageW * 0.16);
          var zout = Math.round(persp * (0.80 + 0.12 * _rand(s * 7 + 4)));
          var ryout= Math.round((_rand(s * 7 + 5) * 2 - 1) * 52 * ix);
          var dxout= Math.round((_rand(s * 7 + 6) * 2 - 1) * stageW * 0.12);
          var poseIn  = 'translateZ(' + zin  + 'px) translateX(' + dxin  + 'px) rotateY(' + ryin  + 'deg) rotateZ(' + rzin + 'deg)';
          var poseOut = 'translateZ(' + zout + 'px) translateX(' + dxout + 'px) rotateY(' + ryout + 'deg)';
          var poseRest= 'translateZ(0px) translateX(0px) rotateY(0deg) rotateZ(0deg)';
          
          span.style.transform = poseIn;
          span.style.opacity = '0';
          flyers.push({ el: span, poseIn: poseIn, poseOut: poseOut, poseRest: poseRest });
        }
        lineEl.appendChild(span);
      });
      rig.appendChild(lineEl);
    });
    self._stage.appendChild(rig);

    var SNAP     = SPRING_IN ; 
    var n        = flyers.length;
    var perIn    = _ms(42, self.speed);
    var flyDur   = _ms(620, self.speed);
    var inEnd    = (n > 0 ? (n - 1) : 0) * perIn + flyDur;
    var holdMs   = _ms(1500, self.speed);
    var outStart = inEnd + holdMs;
    var perOut   = _ms(32, self.speed);
    var outDur   = _ms(520, self.speed);
    var outEnd   = outStart + (n > 0 ? (n - 1) : 0) * perOut + outDur;
    var totalMs  = outEnd + _ms(250, self.speed);

    var amp = Math.round(13 * (0.6 + ix * 0.4));
    try {
      self._anims.push(rig.animate(
        [{ transform: 'rotateY(-' + amp + 'deg)' },
         { transform: 'rotateY('  + amp + 'deg)' }],
        { duration: totalMs, easing: 'ease-in-out', fill: 'forwards' }
      ));
    } catch (e) {}

    flyers.forEach(function (o, idx) {
      var t = setTimeout(function () {
        if (self._destroyed) return;
        try {
          self._anims.push(o.el.animate(
            [{ transform: o.poseIn,   opacity: 0 },
             { opacity: 1, offset: 0.15 },   
             { transform: o.poseRest, opacity: 1 }],
            { duration: flyDur, easing: SNAP, fill: 'forwards' }
          ));
        } catch (e) {}
      }, idx * perIn);
      self._timers.push(t);
    });

    if (self.loop) {
      flyers.forEach(function (o, idx) {
        var t = setTimeout(function () {
          if (self._destroyed) return;
          try {
            self._anims.push(o.el.animate(
              [{ transform: o.poseRest, opacity: 1 },
               { opacity: 1, offset: 0.6 },     
               { transform: o.poseOut,  opacity: 0 }],
              { duration: outDur, easing: SNAP, fill: 'forwards' }
            ));
          } catch (e) {}
        }, outStart + idx * perOut);
        self._timers.push(t);
      });

      var loopT = setTimeout(function () {
        if (self._destroyed || !self._running) return;
        self._playArc();
      }, totalMs);
      self._timers.push(loopT);
    }

    self._drawAccents();
    self._running = true;
  };

  KineticBlock.prototype._playSlide = function () {
    var self = this;
    self._clearAll(); 

    var srcLines = (self.lines || []).map(function (l) { return l.text; }).filter(Boolean);
    if (self.customMode && Array.isArray(self.customLines) && self.customLines.length) {
      var cl = self.customLines
        .map(function (c) { return (c && c.text || '').trim(); })
        .filter(Boolean);
      if (cl.length) srcLines = cl;
    }
    srcLines = srcLines.slice(0, 4);
    if (!srcLines.length) {
      self._stage.innerHTML =
        '<span style="font-size:64px;opacity:0.18;letter-spacing:0.02em">Kinetic</span>';
      self._running = false;
      return;
    }

    var stageH = self._stage.offsetHeight || self.el.offsetHeight || 300;
    var stageW = self._stage.offsetWidth  || self.el.offsetWidth  || 600;
    var ix     = self.intensity || 1;

    var fontSize = Math.max(16, Math.round(Math.min(
      stageH * 0.80 / (srcLines.length * 1.32),
      stageW * 0.15
    )));

    var SNAP = SPRING_IN ;
    var dxIn  = Math.round(stageW * (0.10 + 0.05 * ix)); 
    var dxOut = Math.round(stageW * (0.14 + 0.05 * ix)); 

    var group = document.createElement('div');
    group.style.cssText =
      'display:flex;flex-direction:column;align-items:flex-start;' +
      'gap:' + Math.round(fontSize * 0.12) + 'px;';

    var flyers = [];

    var accent = document.createElement('div');
    accent.textContent = '//';
    accent.style.cssText =
      'font-size:' + Math.round(fontSize * 0.5) + 'px;font-weight:800;line-height:1;' +
      'letter-spacing:' + Math.round(fontSize * 0.04) + 'px;' +
      'transform:skewX(-12deg);color:' + self.color + ';opacity:0;' +
      'margin-bottom:' + Math.round(fontSize * 0.06) + 'px;will-change:transform,opacity;';
    group.appendChild(accent);
    flyers.push({ el: accent, poseIn: 'translateX(-' + Math.round(stageW * 0.06) + 'px) skewX(-12deg)',
                  poseRest: 'translateX(0) skewX(-12deg)',
                  poseOut: 'translateX(-' + dxOut + 'px) skewX(-12deg)' });

    srcLines.forEach(function (text, li) {
      var lineEl = document.createElement('div');
      lineEl.style.cssText =
        'display:flex;justify-content:flex-start;align-items:baseline;white-space:nowrap;' +
        'font-size:' + Math.round(fontSize * _tierScale(self.tier, li)) + 'px;' +
        'line-height:1.04;font-weight:800;color:' + self.color + ';' +
        'letter-spacing:' + (li === 0 ? '0' : '0.12em') + ';';
      [].forEach.call(text, function (ch) {
        var span = document.createElement('span');
        var isSpace = (ch === ' ');
        span.textContent = isSpace ? ' ' : ch; 
        span.style.cssText = 'display:inline-block;will-change:transform,opacity;';
        if (!isSpace) {
          span.style.transform = 'translateX(' + dxIn + 'px)';
          span.style.opacity = '0';
          flyers.push({ el: span,
                        poseIn: 'translateX(' + dxIn + 'px)',
                        poseRest: 'translateX(0)',
                        poseOut: 'translateX(-' + dxOut + 'px)' });
        }
        lineEl.appendChild(span);
      });
      group.appendChild(lineEl);
    });
    self._stage.appendChild(group);

    var n        = flyers.length;
    var perIn    = _ms(34, self.speed);
    var slideDur = _ms(520, self.speed);
    var inEnd    = (n > 0 ? (n - 1) : 0) * perIn + slideDur;
    var holdMs   = _ms(1500, self.speed);
    var outStart = inEnd + holdMs;
    var perOut   = _ms(26, self.speed);
    var outDur   = _ms(420, self.speed);
    var outEnd   = outStart + (n > 0 ? (n - 1) : 0) * perOut + outDur;
    var totalMs  = outEnd + _ms(250, self.speed);

    flyers.forEach(function (o, idx) {
      var t = setTimeout(function () {
        if (self._destroyed) return;
        try {
          self._anims.push(o.el.animate(
            [{ transform: o.poseIn, opacity: 0 },
             { opacity: 1, offset: 0.35 },
             { transform: o.poseRest, opacity: 1 }],
            { duration: slideDur, easing: SNAP, fill: 'forwards' }
          ));
        } catch (e) {}
      }, idx * perIn);
      self._timers.push(t);
    });

    if (self.loop) {
      flyers.forEach(function (o, idx) {
        var t = setTimeout(function () {
          if (self._destroyed) return;
          try {
            self._anims.push(o.el.animate(
              [{ transform: o.poseRest, opacity: 1 },
               { transform: o.poseOut, opacity: 0 }],
              { duration: outDur, easing: SNAP, fill: 'forwards' }
            ));
          } catch (e) {}
        }, outStart + idx * perOut);
        self._timers.push(t);
      });

      var loopT = setTimeout(function () {
        if (self._destroyed || !self._running) return;
        self._playSlide();
      }, totalMs);
      self._timers.push(loopT);
    }

    self._drawAccents();
    self._running = true;
  };

  KineticBlock.prototype._playBrutal = function () {
    var self = this;
    self._clearAll();

    var srcLines = (self.lines || []).map(function (l) { return l.text; }).filter(Boolean);
    if (self.customMode && Array.isArray(self.customLines) && self.customLines.length) {
      var cl = self.customLines.map(function (c) { return (c && c.text || '').trim(); }).filter(Boolean);
      if (cl.length) srcLines = cl;
    }
    srcLines = srcLines.slice(0, 4);
    if (!srcLines.length) {
      self._stage.innerHTML = '<span style="font-size:64px;opacity:0.18;letter-spacing:0.02em">Kinetic</span>';
      self._running = false; return;
    }

    var stageH = self._stage.offsetHeight || self.el.offsetHeight || 300;
    var stageW = self._stage.offsetWidth  || self.el.offsetWidth  || 600;
    var ix     = self.intensity || 1;
    var fontSize = Math.max(16, Math.round(Math.min(stageH * 0.80 / (srcLines.length * 1.32), stageW * 0.16)));

    function _darken(c, f) {
      var h = (c || '').replace('#', '');
      if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(h)) return 'rgba(0,0,0,0.55)';
      if (h.length === 3) h = h.split('').map(function (x) { return x + x; }).join('');
      var r = Math.round(parseInt(h.substr(0,2),16) * f);
      var g = Math.round(parseInt(h.substr(2,2),16) * f);
      var b = Math.round(parseInt(h.substr(4,2),16) * f);
      return 'rgb(' + r + ',' + g + ',' + b + ')';
    }
    var dark = _darken(self.color, 0.42);
    function extrude(px) {
      var s = [];
      for (var i = 1; i <= px; i++) s.push(i + 'px ' + i + 'px 0 ' + dark);
      return s.join(',');
    }

    var SNAP  = SPRING_IN ;
    var dxIn  = Math.round(stageW * (0.14 + 0.05 * ix));
    var dxOut = Math.round(stageW * (0.18 + 0.05 * ix));

    var group = document.createElement('div');
    group.style.cssText = 'display:flex;flex-direction:column;align-items:flex-start;gap:' + Math.round(fontSize * 0.12) + 'px;';

    var flyers = [];
    srcLines.forEach(function (text, li) {
      var lineFs = Math.round(fontSize * _tierScale(self.tier, li));
      var depth  = Math.max(2, Math.round(lineFs * 0.08));
      var lineEl = document.createElement('div');
      lineEl.style.cssText =
        'display:flex;justify-content:flex-start;align-items:baseline;white-space:nowrap;' +
        'font-size:' + lineFs + 'px;line-height:1.04;font-weight:900;color:' + self.color + ';' +
        'letter-spacing:' + (li === 0 ? '0' : '0.08em') + ';text-shadow:' + extrude(depth) + ';';
      [].forEach.call(text, function (ch) {
        var span = document.createElement('span');
        var isSpace = (ch === ' ');
        span.textContent = isSpace ? ' ' : ch; 
        span.style.cssText = 'display:inline-block;will-change:transform,opacity;';
        if (!isSpace) {
          span.style.transform = 'translateX(' + dxIn + 'px) skewX(-8deg)';
          span.style.opacity = '0';
          flyers.push({ el: span,
            poseIn:  'translateX(' + dxIn + 'px) skewX(-8deg)',
            poseRest:'translateX(0) skewX(0deg)',
            poseOut: 'translateX(-' + dxOut + 'px) skewX(-8deg)' });
        }
        lineEl.appendChild(span);
      });
      group.appendChild(lineEl);
    });
    self._stage.appendChild(group);

    var n = flyers.length;
    var perIn = _ms(32, self.speed), inDur = _ms(500, self.speed);
    var inEnd = (n > 0 ? (n - 1) : 0) * perIn + inDur;
    var holdMs = _ms(1500, self.speed), outStart = inEnd + holdMs;
    var perOut = _ms(24, self.speed), outDur = _ms(400, self.speed);
    var totalMs = outStart + (n > 0 ? (n - 1) : 0) * perOut + outDur + _ms(250, self.speed);

    flyers.forEach(function (o, idx) {
      var t = setTimeout(function () {
        if (self._destroyed) return;
        try { self._anims.push(o.el.animate(
          [{ transform: o.poseIn, opacity: 0 }, { opacity: 1, offset: 0.35 }, { transform: o.poseRest, opacity: 1 }],
          { duration: inDur, easing: SNAP, fill: 'forwards' })); } catch (e) {}
      }, idx * perIn);
      self._timers.push(t);
    });
    
    if (self.loop) {
      flyers.forEach(function (o, idx) {
        var t = setTimeout(function () {
          if (self._destroyed) return;
          try { self._anims.push(o.el.animate(
            [{ transform: o.poseRest, opacity: 1 }, { transform: o.poseOut, opacity: 0 }],
            { duration: outDur, easing: SNAP, fill: 'forwards' })); } catch (e) {}
        }, outStart + idx * perOut);
        self._timers.push(t);
      });
      var loopT = setTimeout(function () { if (!self._destroyed && self._running) self._playBrutal(); }, totalMs);
      self._timers.push(loopT);
    }
    self._drawAccents();
    self._running = true;
  };

  KineticBlock.prototype._playSweep = function () {
    var self = this;
    self._clearAll();

    var srcLines = (self.lines || []).map(function (l) { return l.text; }).filter(Boolean);
    if (self.customMode && Array.isArray(self.customLines) && self.customLines.length) {
      var cl = self.customLines.map(function (c) { return (c && c.text || '').trim(); }).filter(Boolean);
      if (cl.length) srcLines = cl;
    }
    srcLines = srcLines.slice(0, 4);
    if (!srcLines.length) {
      self._stage.innerHTML = '<span style="font-size:64px;opacity:0.18;letter-spacing:0.02em">Kinetic</span>';
      self._running = false; return;
    }

    var stageH = self._stage.offsetHeight || self.el.offsetHeight || 300;
    var stageW = self._stage.offsetWidth  || self.el.offsetWidth  || 600;
    var fontSize = Math.max(16, Math.round(Math.min(stageH * 0.80 / (srcLines.length * 1.3), stageW * 0.15)));

    var pos = document.createElement('div');
    pos.style.cssText = 'position:relative;display:inline-block;';

    var textWrap = document.createElement('div');
    textWrap.style.cssText =
      'display:flex;flex-direction:column;align-items:flex-start;' +
      'gap:' + Math.round(fontSize * 0.1) + 'px;clip-path:inset(0 100% 0 0);will-change:clip-path;';
    srcLines.forEach(function (text, li) {
      var lineEl = document.createElement('div');
      lineEl.textContent = text;
      lineEl.style.cssText =
        'white-space:nowrap;font-size:' + Math.round(fontSize * _tierScale(self.tier, li)) + 'px;' +
        'line-height:1.05;font-weight:800;color:' + self.color + ';' +
        'letter-spacing:' + (li === 0 ? '0' : '0.1em') + ';';
      textWrap.appendChild(lineEl);
    });
    pos.appendChild(textWrap);

    var bar = document.createElement('div');
    bar.style.cssText =
      'position:absolute;top:-6%;left:0;height:112%;width:' + Math.max(4, Math.round(fontSize * 0.12)) + 'px;' +
      'background:' + self.color + ';transform:translateX(0) skewX(-12deg);will-change:transform;opacity:0;';
    pos.appendChild(bar);

    self._stage.appendChild(pos);

    void pos.offsetWidth;
    var w = pos.offsetWidth || Math.round(stageW * 0.6);

    var revealMs = _ms(900, self.speed);
    var holdMs   = _ms(1700, self.speed);
    var outMs    = _ms(800, self.speed);
    var totalMs  = revealMs + holdMs + outMs + _ms(250, self.speed);

    try {
      self._anims.push(textWrap.animate(
        [{ clipPath: 'inset(0 100% 0 0)' }, { clipPath: 'inset(0 0 0 0)' }],
        { duration: revealMs, easing: 'ease-in-out', fill: 'forwards' }));
      self._anims.push(bar.animate(
        [{ transform: 'translateX(0) skewX(-12deg)', opacity: 1 },
         { transform: 'translateX(' + w + 'px) skewX(-12deg)', opacity: 1 }],
        { duration: revealMs, easing: 'ease-in-out', fill: 'forwards' }));
    } catch (e) {}

    if (self.loop) {
      
      var outT = setTimeout(function () {
        if (self._destroyed) return;
        try {
          self._anims.push(textWrap.animate(
            [{ clipPath: 'inset(0 0 0 0)' }, { clipPath: 'inset(0 0 0 100%)' }],
            { duration: outMs, easing: 'ease-in-out', fill: 'forwards' }));
          self._anims.push(bar.animate(
            [{ transform: 'translateX(0) skewX(-12deg)', opacity: 1 },
             { transform: 'translateX(' + w + 'px) skewX(-12deg)', opacity: 1 }],
            { duration: outMs, easing: 'ease-in-out', fill: 'forwards' }));
        } catch (e) {}
      }, revealMs + holdMs);
      self._timers.push(outT);

      var loopT = setTimeout(function () { if (!self._destroyed && self._running) self._playSweep(); }, totalMs);
      self._timers.push(loopT);
    }
    self._drawAccents();
    self._running = true;
  };

  
  KineticBlock.prototype._gradLetterCss = function () {
    if (this.fill !== 'gradient') return '';
    return '-webkit-background-clip:text;background-clip:text;' +
           '-webkit-text-fill-color:transparent;background-repeat:no-repeat;';
  };

  KineticBlock.prototype._applyGradientScope = function (lineEls) {
    if (this.fill !== 'gradient' || !this._stage) return;
    var self  = this;
    var stage = this._stage;
    var c1 = this.gradC1, c2 = this.gradC2;
    var scope = (this.gradScope === 'line' || this.gradScope === 'letter') ? this.gradScope : 'block';
    var dur = (this.gradMode === 'spin' ? 5 : 3.5) / this.gradSpeed;
    dur = Math.max(0.3, Math.round(dur * 100) / 100);
    
    stage.style.animation = (this.gradMode === 'spin' ? 'kg-spin ' : 'kg-roll ') + dur + 's linear infinite';
    var sW = stage.offsetWidth || 1, sH = stage.offsetHeight || 1;
    var lin = 'linear-gradient(90deg,' + c1 + ',' + c2 + ',' + c1 + ')';
    lineEls.forEach(function (le) {
      var lx = le.el.offsetLeft, ly = le.el.offsetTop;
      var lw = le.el.offsetWidth || 1, lh = le.el.offsetHeight || 1;
      var chars = le.el.querySelectorAll('.kp-char');
      [].forEach.call(chars, function (ci) {
        
        var cx = ci.offsetLeft, cy = ci.offsetTop;
        var cw = ci.offsetWidth || 1, ch = ci.offsetHeight || 1;
        var refL, refT, refW, refH;
        if (scope === 'block')      { refL = 0;  refT = 0;  refW = sW; refH = sH; }
        else if (scope === 'line')  { refL = lx; refT = ly; refW = lw; refH = lh; }
        else                        { refL = cx; refT = cy; refW = cw; refH = ch; }
        if (self.gradMode === 'spin') {
          var atX = (refL + refW / 2 - cx).toFixed(1);
          var atY = (refT + refH / 2 - cy).toFixed(1);
          ci.style.backgroundImage = 'conic-gradient(from var(--kg-angle,0deg) at ' + atX + 'px ' + atY + 'px,' + c1 + ',' + c2 + ',' + c1 + ')';
          ci.style.backgroundSize = '';
        } else {
          var relX = (cx - refL).toFixed(1);
          var w = Math.max(1, refW).toFixed(1);
          ci.style.backgroundImage = lin;
          ci.style.backgroundSize = w + 'px 100%';
          ci.style.backgroundRepeat = 'repeat';
          ci.style.backgroundPosition = 'calc(' + (-relX) + 'px - var(--kg,0) * ' + w + 'px) 0';
        }
      });
    });
  };

  KineticBlock.prototype._buildLine = function (textLine, slot, heroFont) {
    var lineEl = document.createElement('div');
    
    var slotScale = 1;
    if (this._useCustom) {
      var s = parseFloat(slot.scale);
      if (isFinite(s) && s > 0) slotScale = s;
    }
    
    var fontSize;
    if (this._useCustom && slot.px > 0) {
      fontSize = Math.max(12, Math.round(slot.px)); 
    } else {
      fontSize = Math.max(12, Math.round(heroFont * slotScale));
    }
    
    var famCss = (this._useCustom && slot.font)   ? 'font-family:' + slot.font + ';' : '';
    var wtCss  = (this._useCustom && slot.weight) ? 'font-weight:' + slot.weight + ';' : '';
    
    var _lineColor = (this._useCustom && slot.color) ? slot.color : this.color;
    var colCss = 'color:' + _lineColor + ';';

    var _pxWrap = (this._useCustom && slot.px > 0);
    var _kWrap = this.maxWidth > 0 || _pxWrap;
    var _kWrapPct = this.maxWidth > 0 ? this.maxWidth : 90;
    var _kJust = this.align === 'left' ? 'flex-start' : (this.align === 'right' ? 'flex-end' : 'center');
    lineEl.style.cssText =
      'display:flex;flex-wrap:' + (_kWrap ? 'wrap' : 'nowrap') + ';justify-content:' + (_kWrap ? _kJust : 'center') + ';align-items:baseline;' +
      (_kWrap ? ('max-width:' + _kWrapPct + '%;') : '') +
      
      'gap:0.32em;' +
      'font-size:' + fontSize + 'px;line-height:1;' + famCss + wtCss + colCss +
      'white-space:nowrap;direction:ltr;overflow:visible;' +
      'will-change:transform,opacity,filter';
    lineEl.setAttribute('data-role', slot.role || '');

    var MASK_BASE =
      'display:inline-block;line-height:1;overflow:hidden;vertical-align:baseline;';
    
    var _kFam = (this._useCustom && slot.font) ? slot.font : 'sans-serif';
    var _kWt  = (this._useCustom && slot.weight) ? slot.weight : '800';
    var _descEm = _kDescentEm(textLine.words.join(' '), _kFam, _kWt);
    var _padEm  = Math.max(0.03, _descEm + 0.04);
    var MASK_CSS = MASK_BASE + 'padding-bottom:' + (Math.round(_padEm * 1000) / 1000) + 'em;';
    var INNER_CSS_W =
      'display:inline-block;line-height:1;' +
      'will-change:transform,opacity,filter,letter-spacing';
    var INNER_CSS_C =
      'display:inline-block;line-height:1;' +
      'will-change:transform,opacity,filter';
    
    if (this.fill === 'gradient') INNER_CSS_C += ';' + this._gradLetterCss();

    textLine.words.forEach(function (w) {
      var wordMask = document.createElement('span');
      wordMask.className = 'kp-word-mask';
      wordMask.style.cssText = MASK_CSS;

      var wordInner = document.createElement('span');
      wordInner.className = 'kp-word';
      wordInner.style.cssText = INNER_CSS_W;

      [].forEach.call(w, function (ch) {
        var charMask = document.createElement('span');
        charMask.className = 'kp-char-mask';
        charMask.style.cssText = MASK_CSS;

        var charInner = document.createElement('span');
        charInner.className = 'kp-char';
        charInner.textContent = ch;
        charInner.style.cssText = INNER_CSS_C;

        charMask.appendChild(charInner);
        wordInner.appendChild(charMask);
      });

      wordMask.appendChild(wordInner);
      lineEl.appendChild(wordMask);
    });

    var enterMotion = slot.enter && MOTIONS[slot.enter.motion];
    if (enterMotion && enterMotion.initial) {
      var evtIx = (slot.enter.intensity != null) ? slot.enter.intensity : 1;
      var ix    = evtIx * (this.intensity || 1);
      var targets = this._getTargets(lineEl, (slot.enter.target || 'word'));
      var n = targets.length;
      targets.forEach(function (t, i) {
        var init = _resolve(enterMotion.initial, ix, i, n);
        if (init) t.style.cssText += ';' + init;
      });
    }
    return lineEl;
  };

  KineticBlock.prototype._getTargets = function (lineEl, target) {
    if (target === 'whole' || target === 'line') return [lineEl];
    if (target === 'word')   return [].slice.call(lineEl.querySelectorAll('.kp-word'));
    if (target === 'letter') return [].slice.call(lineEl.querySelectorAll('.kp-char'));
    return [lineEl];
  };

  KineticBlock.prototype._schedule = function (lineEl, evt) {
    var self = this;
    if (!evt || !evt.motion) return 0;
    var motion = MOTIONS[evt.motion];
    if (!motion) return 0;
    var startOffset  = _ms(evt.at || 0, self.speed);
    var dur          = _ms(evt.dur || motion.duration, self.speed);
    var staggerFn    = STAGGERS[evt.stagger || 'simultaneous'] || STAGGERS.simultaneous;
    var staggerScale = (evt.staggerScale != null) ? evt.staggerScale : 1;
    var evtIx        = (evt.intensity != null) ? evt.intensity : 1;
    var ix           = evtIx * (self.intensity || 1);
    var targets      = self._getTargets(lineEl, evt.target || 'word');
    var n            = targets.length;
    if (!n || !dur) return 0;

    var maxDelay = 0;
    targets.forEach(function (target, i) {
      var delay = staggerFn(i, n, dur) * staggerScale;
      var t = setTimeout(function () {
        if (self._destroyed) return;
        try {
          var kfData = _resolve(motion.kf, ix, i, n);
          var a = target.animate(kfData, {
            duration: dur,
            easing:   motion.easing,
            fill:     'forwards'
          });
          self._anims.push(a);
        } catch (e) {}
      }, startOffset + delay);
      self._timers.push(t);
      if (delay > maxDelay) maxDelay = delay;
    });
    return startOffset + maxDelay + dur;
  };

  KineticBlock.prototype._buildDecor = function (shape, attachRect, stageRect, dc) {
    var widthMult  = (dc && dc.widthMult  != null) ? dc.widthMult  : shape.widthMult;
    var heightMult = (dc && dc.heightMult != null) ? dc.heightMult : shape.heightMult;
    var heightPx   = (dc && dc.heightPx   != null) ? dc.heightPx   : shape.heightPx;
    var placementOverride = (dc && dc.placement) ? dc.placement : null;
    var offsetOverride    = (dc && dc.offset != null) ? dc.offset : null;

    var w = (widthMult != null) ? attachRect.width * widthMult : null;
    var h;
    if (heightPx != null) {
      h = heightPx;
    } else if (heightMult != null) {
      h = attachRect.height * heightMult;
    } else {
      h = attachRect.height;
    }

    var top = attachRect.top - stageRect.top;
    var left = attachRect.left - stageRect.left;
    var placement = placementOverride || shape.placement || 'overlay';
    var offset = (offsetOverride != null) ? offsetOverride : (shape.offset || 0);
    var y, x;
    if (placement === 'below') {
      y = top + attachRect.height + offset;
      x = left + (attachRect.width - w) / 2;
    } else if (placement === 'above') {
      y = top - h - offset;
      x = left + (attachRect.width - w) / 2;
    } else if (placement === 'left-of') {
      y = top + (attachRect.height - h) / 2;
      x = left - w - offset;
    } else if (placement === 'right-of') {
      y = top + (attachRect.height - h) / 2;
      x = left + attachRect.width + offset;
    } else if (placement === 'overlay-mid') {
      
      y = top + (attachRect.height - h) / 2;
      x = left + (attachRect.width - w) / 2;
    } else {
      
      y = top + (attachRect.height - h) / 2;
      x = left + (attachRect.width - w) / 2;
    }

    var wrap = document.createElement('div');
    wrap.style.cssText =
      'position:absolute;left:' + Math.round(x) + 'px;top:' + Math.round(y) + 'px;' +
      'width:' + Math.round(w) + 'px;height:' + Math.round(h) + 'px;' +
      'pointer-events:none;color:' + this.color + ';' +
      (shape.behind ? 'z-index:-1;' : 'z-index:1;') +
      (shape.opacity != null ? 'opacity:' + shape.opacity + ';' : '');

    var svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="' + shape.viewBox + '" preserveAspectRatio="none" style="display:block;width:100%;height:100%;overflow:visible">' + shape.inner + '</svg>';
    wrap.innerHTML = svg;

    if (shape.drawn && dc && dc.enter && dc.enter.motion === 'draw-on') {
      var strokes = wrap.querySelectorAll('path, line, polyline, polygon, ellipse, circle, rect');
      [].forEach.call(strokes, function (el) {
        el.style.strokeDasharray  = '99999';
        el.style.strokeDashoffset = '99999';
      });
    }
    return wrap;
  };

  KineticBlock.prototype._scheduleDecor = function (decorEl, shape, evt) {
    var self = this;
    if (!evt || !evt.motion) return 0;
    var motion = DECOR_MOTIONS[evt.motion];
    if (!motion) return 0;
    var startOffset = _ms(evt.at || 0, self.speed);
    var dur = _ms(evt.dur || motion.duration, self.speed);

    var t = setTimeout(function () {
      if (self._destroyed) return;
      try {
        if (motion.drawOn) {
          
          var strokes = decorEl.querySelectorAll('path, line, polyline, polygon, ellipse, circle, rect');
          [].forEach.call(strokes, function (el) {
            var len = (el.getTotalLength) ? el.getTotalLength() : 200;
            if (!len) len = 200;
            el.style.strokeDasharray = len + '';
            el._drawLength = len;
            var a = el.animate(
              [ { strokeDashoffset: len }, { strokeDashoffset: 0 } ],
              { duration: dur, easing: motion.easing, fill: 'forwards' }
            );
            
            a.onfinish = function () { el.style.strokeDashoffset = '0'; };
            self._anims.push(a);
          });
        } else if (motion.drawOff) {
          var strokesOff = decorEl.querySelectorAll('path, line, polyline, polygon, ellipse, circle, rect');
          [].forEach.call(strokesOff, function (el) {
            var len = el._drawLength;
            if (!len) {
              len = (el.getTotalLength) ? el.getTotalLength() : 200;
              if (!len) len = 200;
              el.style.strokeDasharray = len + '';
              el._drawLength = len;
            }
            var a = el.animate(
              [ { strokeDashoffset: 0 }, { strokeDashoffset: len } ],
              { duration: dur, easing: motion.easing, fill: 'forwards' }
            );
            
            a.onfinish = function () { el.style.strokeDashoffset = len + ''; };
            self._anims.push(a);
          });
        } else if (motion.kf) {
          var a = decorEl.animate(motion.kf, {
            duration: dur,
            easing: motion.easing,
            fill: 'forwards'
          });
          self._anims.push(a);
        }
      } catch (e) {}
    }, startOffset);
    self._timers.push(t);
    return startOffset + dur;
  };

  KineticBlock.prototype._applyDecorInitial = function (decorEl, shape, enterEvt) {
    if (!enterEvt || !enterEvt.motion) return;
    var motion = DECOR_MOTIONS[enterEvt.motion];
    if (!motion || !motion.initial) return;
    decorEl.style.cssText += ';' + motion.initial;
  };

  KineticBlock.prototype.destroy = function () {
    this._destroyed = true;
    this._running   = false;
    if (this._io) { this._io.disconnect(); this._io = null; }
    if (this._ro) { try { this._ro.disconnect(); } catch (e) {} this._ro = null; }
    if (this._screenMO) { try { this._screenMO.disconnect(); } catch (e) {} this._screenMO = null; }
    if (this._safetyStop) { try { this._safetyStop(); } catch (e) {} }
    this._clearAll();
  };

  function initAll() {
    var blocks = document.querySelectorAll('.impulse-kinetic-block:not([data-kinetic])');
    [].forEach.call(blocks, function (el) {
      el.setAttribute('data-kinetic', '1');
      el._kineticInst = new KineticBlock(el);
    });
    if (_isScreenMode()) {
      var seen = document.querySelectorAll('.impulse-kinetic-block[data-kinetic]');
      [].forEach.call(seen, function (el) {
        var inst = el._kineticInst;
        if (!inst || inst._running) return;
        var sc = _kScreenEl(el);
        if (sc && sc.classList.contains('impulse-screen-active')) inst._run();
      });
    }
  }

  var ENTRY_MOTIONS_FOR_PANEL = [
    { group: 'Translate', motions: [
      { value: 'rise',                label: 'Rise' },
      { value: 'drop',                label: 'Drop' },
      { value: 'glide-from-l',        label: 'Glide from left' },
      { value: 'glide-from-r',        label: 'Glide from right' },
      { value: 'bounce-slide-l',      label: 'Bounce-slide left' },
      { value: 'bounce-slide-split',  label: 'Bounce-slide split' },
      { value: 'bounce-rise',         label: 'Bounce-rise' },
      { value: 'skew-in',             label: 'Skew' }
    ]},
    { group: 'Scale', motions: [
      { value: 'zoom-in',             label: 'Zoom' },
      { value: 'scatter-in',          label: 'Scatter' }
    ]},
    { group: 'Fade / blur', motions: [
      { value: 'fade-in',             label: 'Fade in' },
      { value: 'blur-in',             label: 'Blur in' },
      { value: 'spacing-in',          label: 'Letter-spacing in' },
      { value: 'letter-kick-in',      label: 'Letter kick' }
    ]},
    { group: 'Clip-mask', motions: [
      { value: 'circle-reveal',       label: 'Circle reveal' },
      { value: 'slit-open',           label: 'Slit open' },
      { value: 'box-in',              label: 'Box open' },
      { value: 'wipe-right',          label: 'Wipe right' },
      { value: 'wipe-left',           label: 'Wipe left' },
      { value: 'wipe-up',             label: 'Wipe up' },
      { value: 'wipe-down',           label: 'Wipe down' }
    ]},
    { group: 'Glitch / noise', motions: [
      { value: 'glitch-in',           label: 'Glitch' },
      { value: 'distort-in',          label: 'Distort' },
      { value: 'shake-in',            label: 'Shake' },
      { value: 'noise-flicker',       label: 'Static flicker' }
    ]},
    { group: '3D (procedural)', motions: [
      { value: 'flip-up-in',          label: 'Flip up' },
      { value: 'flip-down-in',        label: 'Flip down' },
      { value: 'spin-in',             label: 'Spin' },
      { value: 'swing-in',            label: 'Swing' },
      { value: 'unfold-in',           label: 'Unfold' },
      { value: 'curtain-in',          label: 'Curtain' },
      { value: 'depth-in',            label: 'Depth' },
      { value: 'depth-fly-in',        label: 'Depth fly' },
      { value: 'tumble-in',           label: 'Tumble' }
    ]},
    { group: 'Variable font', motions: [
      { value: 'weight-morph-in',     label: 'Weight morph' }
    ]}
  ];

  window.ImpulseKinetic = {
    init: initAll,
    initEl: function (el) {
      if (el._kineticInst) {
        try { el._kineticInst.destroy(); } catch (e) {}
        delete el._kineticInst;
        el.removeAttribute('data-kinetic');
      }
      el.setAttribute('data-kinetic', '1');
      el._kineticInst = new KineticBlock(el);
    },
    destroyEl: function (el) {
      if (el._kineticInst) { el._kineticInst.destroy(); delete el._kineticInst; }
      el.removeAttribute('data-kinetic');
    },
    
    compositions:      COMPOSITIONS,
    motions:           MOTIONS,
    staggers:          STAGGERS,
    entryMotionGroups: ENTRY_MOTIONS_FOR_PANEL
  };

  (function () {
    
    var _pw = window.impulsePreWarmScreen;
    window.impulsePreWarmScreen = function (screenEl) {
      if (_pw) _pw(screenEl);
      if (!screenEl || !screenEl.querySelectorAll) return;
      screenEl.querySelectorAll('.impulse-kinetic-block').forEach(function (el) {
        var inst = el._kineticInst;
        if (!inst || inst._shown || inst._running || inst._exiting) return;
        inst._run();
      });
    };
    var _pf = window.impulseOnScreenFocus;
    window.impulseOnScreenFocus = function (screenEl) {
      if (_pf) _pf(screenEl);
      screenEl.querySelectorAll('.impulse-kinetic-block').forEach(function (el) {
        var inst = el._kineticInst;
        if (!inst) return;
        
        if (!inst._shown && !inst._running && !inst._exiting) inst._run();
      });
    };
    
    var _pl = window.impulseOnScreenLeave;
    window.impulseOnScreenLeave = function (screenEl) {
      if (_pl) _pl(screenEl);
      if (!screenEl || !screenEl.querySelectorAll) return;
      screenEl.querySelectorAll('.impulse-kinetic-block').forEach(function (el) {
        var inst = el._kineticInst;
        if (!inst || !inst._shown) return;
        if (inst.scrollExit === 'spring' || inst.scrollExit === 'fade') inst._scrollExitAnim(inst.scrollExit);
      });
    };
    var _pr = window.impulseAnimatorReset;
    window.impulseAnimatorReset = function (screenEl) {
      if (_pr) _pr(screenEl);
      screenEl.querySelectorAll('.impulse-kinetic-block').forEach(function (el) {
        var inst = el._kineticInst;
        if (!inst) return;
        
        if (inst._exiting) return;
        inst._pause();
      });
    };
  })();

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initAll);
  } else {
    initAll();
  }
})();
