(function () {
  'use strict';

  const t = {
    bgPage: '#0f0f0f', bgCard: '#1a1a1a', bgCardAlt: '#141414', bgInput: '#111',
    bgHover: '#222', bgSunken: '#0a0a0a', bgOverlay: 'rgba(0,0,0,0.75)',
    border: '#2a2a2a', borderSubtle: '#1e1e1e',
    textPrimary: '#f5f5f5', textSecondary: '#aaaaaa', textMuted: '#666',
    textDisabled: '#444', accent: '#00ff00', accentText: '#000',
    error: '#ff4444',
  };

  const TRANSITION_TYPES = [
    { id: 'crossfade', name: 'Crossfade', category: 'basic' },
    { id: 'dissolve', name: 'Dissolve', category: 'basic' },
    { id: 'fade-black', name: 'Fade to Black', category: 'basic' },
    { id: 'fade-white', name: 'Fade to White', category: 'basic' },
    { id: 'wipe-left', name: 'Wipe Left', category: 'wipe' },
    { id: 'wipe-right', name: 'Wipe Right', category: 'wipe' },
    { id: 'wipe-up', name: 'Wipe Up', category: 'wipe' },
    { id: 'wipe-down', name: 'Wipe Down', category: 'wipe' },
    { id: 'slide-left', name: 'Slide Left', category: 'slide' },
    { id: 'slide-right', name: 'Slide Right', category: 'slide' },
    { id: 'push-left', name: 'Push Left', category: 'push' },
    { id: 'push-right', name: 'Push Right', category: 'push' },
    { id: 'zoom-in', name: 'Zoom In', category: 'zoom' },
    { id: 'zoom-out', name: 'Zoom Out', category: 'zoom' },
    { id: 'blur', name: 'Blur Transition', category: 'blur' },
    { id: 'glitch', name: 'Glitch', category: 'creative' },
    { id: 'cube', name: '3D Cube', category: '3d' },
    { id: 'flip', name: '3D Flip', category: '3d' },
    { id: 'fold', name: '3D Fold', category: '3d' },
    { id: 'morph', name: 'Morph', category: 'creative' },
    { id: 'light-leak', name: 'Light Leak', category: 'creative' },
    { id: 'lens-flare', name: 'Lens Flare', category: 'creative' },
    { id: 'spin', name: 'Spin', category: 'creative' },
    { id: 'luma-wipe', name: 'Luma Wipe', category: 'wipe' },
    { id: 'gradient-wipe', name: 'Gradient Wipe', category: 'wipe' },
    { id: 'film-burn', name: 'Film Burn', category: 'creative' },
    { id: 'slide-up', name: 'Slide Up', category: 'slide' },
    { id: 'slide-down', name: 'Slide Down', category: 'slide' },
    { id: 'push-up', name: 'Push Up', category: 'push' },
    { id: 'push-down', name: 'Push Down', category: 'push' },
    { id: 'wipe-circle', name: 'Circle Wipe', category: 'wipe' },
    { id: 'wipe-diagonal', name: 'Diagonal Wipe', category: 'wipe' },
    { id: 'barn-doors', name: 'Barn Doors', category: 'wipe' },
    { id: 'split-v', name: 'Split Vertical', category: 'wipe' },
    { id: 'flash', name: 'Flash', category: 'basic' },
    { id: 'dip-color', name: 'Dip to Color', category: 'basic' },
    { id: 'page-turn', name: 'Page Turn', category: '3d' },
    { id: 'swing', name: 'Door Swing', category: '3d' },
    { id: 'roll', name: 'Barrel Roll', category: '3d' },
    { id: 'zoom-blur', name: 'Zoom Blur', category: 'zoom' },
    { id: 'zoom-in-fade', name: 'Zoom In Fade', category: 'zoom' },
    { id: 'zoom-out-fade', name: 'Zoom Out Fade', category: 'zoom' },
    { id: 'dreamy', name: 'Dreamy', category: 'creative' },
    { id: 'ripple', name: 'Ripple', category: 'creative' },
    { id: 'shatter', name: 'Shatter', category: 'creative' },
    { id: 'glitch-rgb', name: 'RGB Glitch', category: 'creative' },
    { id: 'color-wash', name: 'Color Wash', category: 'creative' },
    { id: 'whip-pan', name: 'Whip Pan', category: 'creative' },
    { id: 'stretch-h', name: 'Stretch H', category: 'creative' },
    { id: 'stretch-v', name: 'Stretch V', category: 'creative' },
    { id: 'pixelate', name: 'Pixelate', category: 'creative' },
    { id: 'vhs', name: 'VHS', category: 'creative' },
    { id: 'prism', name: 'Prism', category: 'creative' },
    { id: 'squeeze', name: 'Squeeze', category: 'creative' },
    { id: 'ink-wash', name: 'Ink Wash', category: 'creative' },
    { id: 'tv-off', name: 'TV Off', category: 'creative' },
  ];

  const TRANSITION_META = {
    'crossfade': { description: 'Smooth fade between clips', defaultDuration: 1.0 },
    'dissolve': { description: 'Gradual blending of clips', defaultDuration: 1.0 },
    'fade-black': { description: 'Fade out to black then fade in', defaultDuration: 0.5 },
    'fade-white': { description: 'Fade out to white then fade in', defaultDuration: 0.5 },
    'wipe-left': { description: 'Next clip wipes from right to left', defaultDuration: 0.8 },
    'wipe-right': { description: 'Next clip wipes from left to right', defaultDuration: 0.8 },
    'wipe-up': { description: 'Next clip wipes from bottom to top', defaultDuration: 0.8 },
    'wipe-down': { description: 'Next clip wipes from top to bottom', defaultDuration: 0.8 },
    'slide-left': { description: 'Clips slide from right to left', defaultDuration: 0.8 },
    'slide-right': { description: 'Clips slide from left to right', defaultDuration: 0.8 },
    'push-left': { description: 'Outgoing pushes incoming left', defaultDuration: 0.8 },
    'push-right': { description: 'Outgoing pushes incoming right', defaultDuration: 0.8 },
    'zoom-in': { description: 'Next clip zooms in from centre', defaultDuration: 0.8 },
    'zoom-out': { description: 'Current clip zooms out', defaultDuration: 0.8 },
    'blur': { description: 'Blur out and blur in between clips', defaultDuration: 0.8 },
    'glitch': { description: 'Digital glitch effect', defaultDuration: 0.6 },
    'cube': { description: '3D cube rotation between clips', defaultDuration: 1.0 },
    'flip': { description: '3D flip between clips', defaultDuration: 0.8 },
    'fold': { description: '3D fold between clips', defaultDuration: 0.8 },
    'morph': { description: 'Smooth morphing between clips', defaultDuration: 1.0 },
    'light-leak': { description: 'Light leak effect between clips', defaultDuration: 1.0 },
    'lens-flare': { description: 'Lens flare effect between clips', defaultDuration: 0.8 },
    'spin': { description: 'Clockwise rotation between clips', defaultDuration: 0.8 },
    'luma-wipe': { description: 'Luminance-driven wipe reveal', defaultDuration: 1.0 },
    'gradient-wipe': { description: 'Custom gradient-driven transition reveal', defaultDuration: 1.0 },
    'film-burn': { description: 'Organic film burn between clips', defaultDuration: 1.2 },
    'slide-up': { description: 'Clips slide vertically upward', defaultDuration: 0.8 },
    'slide-down': { description: 'Clips slide vertically downward', defaultDuration: 0.8 },
    'push-up': { description: 'Outgoing clip pushes incoming upward', defaultDuration: 0.8 },
    'push-down': { description: 'Outgoing clip pushes incoming downward', defaultDuration: 0.8 },
    'wipe-circle': { description: 'Circular iris wipe reveal', defaultDuration: 0.8 },
    'wipe-diagonal': { description: 'Diagonal corner-to-corner wipe', defaultDuration: 0.8 },
    'barn-doors': { description: 'Barn doors open from centre outward', defaultDuration: 0.8 },
    'split-v': { description: 'Split reveal from horizontal centre', defaultDuration: 0.8 },
    'flash': { description: 'Sharp white flash between clips', defaultDuration: 0.4 },
    'dip-color': { description: 'Dip through a deep colour', defaultDuration: 0.8 },
    'page-turn': { description: 'Clip flips like a turning page', defaultDuration: 0.9 },
    'swing': { description: 'Clip swings open like a door', defaultDuration: 0.8 },
    'roll': { description: 'Full barrel roll between clips', defaultDuration: 1.0 },
    'zoom-blur': { description: 'Zoom in with radial motion blur', defaultDuration: 0.8 },
    'zoom-in-fade': { description: 'Zoom in while cross-fading', defaultDuration: 0.8 },
    'zoom-out-fade': { description: 'Zoom out while cross-fading', defaultDuration: 0.8 },
    'dreamy': { description: 'Soft focus dream-like transition', defaultDuration: 1.2 },
    'ripple': { description: 'Water ripple pulse between clips', defaultDuration: 1.0 },
    'shatter': { description: 'Clip shatters into pieces', defaultDuration: 0.8 },
    'glitch-rgb': { description: 'RGB channel-split glitch effect', defaultDuration: 0.6 },
    'color-wash': { description: 'Vivid colour sweep over the cut', defaultDuration: 1.0 },
    'whip-pan': { description: 'Fast horizontal motion-blur whip', defaultDuration: 0.5 },
    'stretch-h': { description: 'Horizontal stretch and squeeze', defaultDuration: 0.8 },
    'stretch-v': { description: 'Vertical stretch and squeeze', defaultDuration: 0.8 },
    'pixelate': { description: 'Blur-pixelate through the cut', defaultDuration: 0.8 },
    'vhs': { description: 'VHS tape tracking glitch', defaultDuration: 0.8 },
    'prism': { description: 'Rainbow hue-rotate prism effect', defaultDuration: 1.0 },
    'squeeze': { description: 'Squeeze clip to a line then expand', defaultDuration: 0.8 },
    'ink-wash': { description: 'Sepia ink-wash dissolve', defaultDuration: 1.2 },
    'tv-off': { description: 'Screen contracts like a TV turning off', defaultDuration: 0.6 },
  };

  function getTransitionPreviewStyle(transitionId) {
    switch (transitionId) {
      case 'crossfade': return { keyframes: '@keyframes tm-cf-a{0%,15%{opacity:1}85%,100%{opacity:0}}', clipA: { animation: 'tm-cf-a 2s ease-in-out infinite' } };
      case 'fade-black': return { keyframes: '@keyframes tm-fb{0%,10%{opacity:0}40%,60%{opacity:1}90%,100%{opacity:0}}', overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: '#000', zIndex: 3, animation: 'tm-fb 2s ease-in-out infinite' } };
      case 'fade-white': return { keyframes: '@keyframes tm-fw{0%,10%{opacity:0}40%,60%{opacity:1}90%,100%{opacity:0}}', overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: '#fff', zIndex: 3, animation: 'tm-fw 2s ease-in-out infinite' } };
      case 'wipe-left': return { keyframes: '@keyframes tm-wl{0%,10%{clip-path:inset(0 100% 0 0)}90%,100%{clip-path:inset(0 0% 0 0)}}', clipB: { animation: 'tm-wl 1.5s ease-in-out infinite', zIndex: 3 } };
      case 'wipe-right': return { keyframes: '@keyframes tm-wr{0%,10%{clip-path:inset(0 0 0 100%)}90%,100%{clip-path:inset(0 0 0 0%)}}', clipB: { animation: 'tm-wr 1.5s ease-in-out infinite', zIndex: 3 } };
      case 'wipe-up': return { keyframes: '@keyframes tm-wu{0%,10%{clip-path:inset(100% 0 0 0)}90%,100%{clip-path:inset(0 0 0 0)}}', clipB: { animation: 'tm-wu 1.5s ease-in-out infinite', zIndex: 3 } };
      case 'wipe-down': return { keyframes: '@keyframes tm-wd{0%,10%{clip-path:inset(0 0 100% 0)}90%,100%{clip-path:inset(0 0 0 0)}}', clipB: { animation: 'tm-wd 1.5s ease-in-out infinite', zIndex: 3 } };
      case 'slide-left': return { keyframes: '@keyframes tm-sla{0%,10%{transform:translateX(0)}90%,100%{transform:translateX(-100%)}} @keyframes tm-slb{0%,10%{transform:translateX(100%)}90%,100%{transform:translateX(0)}}', clipA: { animation: 'tm-sla 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-slb 1.8s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'slide-right': return { keyframes: '@keyframes tm-sra{0%,10%{transform:translateX(0)}90%,100%{transform:translateX(100%)}} @keyframes tm-srb{0%,10%{transform:translateX(-100%)}90%,100%{transform:translateX(0)}}', clipA: { animation: 'tm-sra 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-srb 1.8s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'zoom-in': return { keyframes: '@keyframes tm-zi{0%,10%{transform:scale(0);opacity:0}90%,100%{transform:scale(1);opacity:1}}', clipB: { animation: 'tm-zi 1.8s ease-in-out infinite', zIndex: 3 } };
      case 'zoom-out': return { keyframes: '@keyframes tm-zo{0%,10%{transform:scale(1);opacity:1}90%,100%{transform:scale(2.5);opacity:0}}', clipA: { animation: 'tm-zo 1.8s ease-in-out infinite' } };
      case 'dissolve': return { keyframes: '@keyframes tm-ds-a{0%,15%{opacity:1}85%,100%{opacity:0}}', clipA: { animation: 'tm-ds-a 1.5s linear infinite' } };
      case 'push-left': return { keyframes: '@keyframes tm-pla{0%,10%{transform:translateX(0)}90%,100%{transform:translateX(-100%)}} @keyframes tm-plb{0%,10%{transform:translateX(100%)}90%,100%{transform:translateX(0)}}', clipA: { animation: 'tm-pla 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-plb 1.8s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'push-right': return { keyframes: '@keyframes tm-pra{0%,10%{transform:translateX(0)}90%,100%{transform:translateX(100%)}} @keyframes tm-prb{0%,10%{transform:translateX(-100%)}90%,100%{transform:translateX(0)}}', clipA: { animation: 'tm-pra 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-prb 1.8s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'blur': return { keyframes: '@keyframes tm-bla{0%,10%{filter:blur(0px);opacity:1}90%,100%{filter:blur(12px);opacity:0}} @keyframes tm-blb{0%,10%{filter:blur(12px);opacity:0}90%,100%{filter:blur(0px);opacity:1}}', clipA: { animation: 'tm-bla 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-blb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'glitch': return { keyframes: '@keyframes tm-gla{0%{transform:skewX(0deg);opacity:1}10%{transform:skewX(-8deg);opacity:0}20%{transform:skewX(8deg);opacity:1}30%{transform:skewX(0deg);opacity:0}100%{transform:skewX(0deg);opacity:1}} @keyframes tm-glb{0%{opacity:0}80%{opacity:0}100%{opacity:1}}', clipA: { animation: 'tm-gla 0.4s linear infinite' }, clipB: { animation: 'tm-glb 0.4s linear infinite', zIndex: 2 } };
      case 'cube': return { keyframes: '@keyframes tm-cba{0%,10%{transform:perspective(400px) rotateY(0deg);opacity:1}90%,100%{transform:perspective(400px) rotateY(-90deg);opacity:0}} @keyframes tm-cbb{0%,10%{transform:perspective(400px) rotateY(90deg);opacity:0}90%,100%{transform:perspective(400px) rotateY(0deg);opacity:1}}', clipA: { animation: 'tm-cba 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-cbb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'flip': return { keyframes: '@keyframes tm-fla{0%,10%{transform:perspective(400px) rotateY(0deg);opacity:1}45%,100%{transform:perspective(400px) rotateY(90deg);opacity:0}} @keyframes tm-flb{0%,55%{transform:perspective(400px) rotateY(-90deg);opacity:0}90%,100%{transform:perspective(400px) rotateY(0deg);opacity:1}}', clipA: { animation: 'tm-fla 1.8s ease-in-out infinite' }, clipB: { animation: 'tm-flb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'fold': return { keyframes: '@keyframes tm-fda{0%,10%{transform:scaleY(1);opacity:1}90%,100%{transform:scaleY(0);opacity:0}} @keyframes tm-fdb{0%,10%{transform:scaleY(0);opacity:0}90%,100%{transform:scaleY(1);opacity:1}}', clipA: { animation: 'tm-fda 1.8s ease-in-out infinite', transformOrigin: 'bottom' }, clipB: { animation: 'tm-fdb 1.8s ease-in-out infinite', transformOrigin: 'top', zIndex: 2 } };
      case 'morph': return { keyframes: '@keyframes tm-mpa{0%,10%{transform:scale(1);opacity:1}90%,100%{transform:scale(1.08);opacity:0}} @keyframes tm-mpb{0%,10%{transform:scale(1.08);opacity:0}90%,100%{transform:scale(1);opacity:1}}', clipA: { animation: 'tm-mpa 2s ease-in-out infinite' }, clipB: { animation: 'tm-mpb 2s ease-in-out infinite', zIndex: 2 } };
      case 'light-leak': return { keyframes: '@keyframes tm-lla{0%,10%{opacity:1}90%,100%{opacity:0}} @keyframes tm-llb{0%,10%{opacity:0}90%,100%{opacity:1}} @keyframes tm-ll{0%,15%{background:transparent}40%,60%{background:rgba(255,220,100,0.85)}85%,100%{background:transparent}}', clipA: { animation: 'tm-lla 2s ease-in-out infinite' }, clipB: { animation: 'tm-llb 2s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 3, animation: 'tm-ll 2s ease-in-out infinite' } };
      case 'lens-flare': return { keyframes: '@keyframes tm-lfa{0%,10%{opacity:1}90%,100%{opacity:0}} @keyframes tm-lfb{0%,10%{opacity:0}90%,100%{opacity:1}} @keyframes tm-lf{0%,10%{background:radial-gradient(circle 15px at 50% 50%,rgba(255,255,255,0) 0%,transparent 100%);opacity:0}50%{background:radial-gradient(ellipse 100% 100% at 50% 50%,rgba(255,255,255,0.95) 0%,transparent 100%);opacity:1}90%,100%{background:radial-gradient(circle 15px at 50% 50%,rgba(255,255,255,0) 0%,transparent 100%);opacity:0}}', clipA: { animation: 'tm-lfa 2s ease-in-out infinite' }, clipB: { animation: 'tm-lfb 2s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 3, animation: 'tm-lf 2s ease-in-out infinite' } };
      case 'spin': return { keyframes: '@keyframes tm-spin-a{0%,10%{transform:perspective(300px) rotateY(0deg);opacity:1}90%,100%{transform:perspective(300px) rotateY(180deg);opacity:0}} @keyframes tm-spin-b{0%,10%{transform:perspective(300px) rotateY(-180deg);opacity:0}90%,100%{transform:perspective(300px) rotateY(0deg);opacity:1}}', clipA: { animation: 'tm-spin-a 1.5s ease-in-out infinite' }, clipB: { animation: 'tm-spin-b 1.5s ease-in-out infinite', zIndex: 2 } };
      case 'luma-wipe': return { keyframes: '@keyframes tm-lw{0%,10%{clip-path:inset(0 100% 100% 0)}90%,100%{clip-path:inset(0 0% 0% 0)}}', clipA: {}, clipB: { animation: 'tm-lw 1.8s ease-in-out infinite', zIndex: 3 }, containerStyle: { overflow: 'hidden' } };
      case 'gradient-wipe': return { keyframes: '@keyframes tm-gw{0%,10%{clip-path:circle(0% at 50% 50%)}90%,100%{clip-path:circle(150% at 50% 50%)}}', clipA: {}, clipB: { animation: 'tm-gw 2s ease-in-out infinite', zIndex: 3 }, containerStyle: { overflow: 'hidden' } };
      case 'film-burn': return { keyframes: '@keyframes tm-fb1{0%,10%{opacity:1}90%,100%{opacity:0}} @keyframes tm-fb2{0%,10%{opacity:0}90%,100%{opacity:1}} @keyframes tm-fb{0%{opacity:0;transform:scale(0.2)}50%{opacity:0.9;transform:scale(1)}100%{opacity:0;transform:scale(1.8)}}', clipA: { animation: 'tm-fb1 2s ease-in-out infinite' }, clipB: { animation: 'tm-fb2 2s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 3, animation: 'tm-fb 2s ease-in-out infinite', background: 'radial-gradient(circle, rgba(255,180,50,0.9) 0%, transparent 70%)' } };
      case 'slide-up': return { keyframes: '@keyframes tm2-sua{0%,10%{transform:translateY(0)}90%,100%{transform:translateY(-100%)}} @keyframes tm2-sub{0%,10%{transform:translateY(100%)}90%,100%{transform:translateY(0)}}', clipA: { animation: 'tm2-sua 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-sub 1.8s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'slide-down': return { keyframes: '@keyframes tm2-sda{0%,10%{transform:translateY(0)}90%,100%{transform:translateY(100%)}} @keyframes tm2-sdb{0%,10%{transform:translateY(-100%)}90%,100%{transform:translateY(0)}}', clipA: { animation: 'tm2-sda 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-sdb 1.8s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'push-up': return { keyframes: '@keyframes tm2-pua{0%,10%{transform:translateY(0)}90%,100%{transform:translateY(-100%)}} @keyframes tm2-pub{0%,10%{transform:translateY(100%)}90%,100%{transform:translateY(0)}}', clipA: { animation: 'tm2-pua 1.5s ease-in-out infinite' }, clipB: { animation: 'tm2-pub 1.5s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'push-down': return { keyframes: '@keyframes tm2-pda{0%,10%{transform:translateY(0)}90%,100%{transform:translateY(100%)}} @keyframes tm2-pdb{0%,10%{transform:translateY(-100%)}90%,100%{transform:translateY(0)}}', clipA: { animation: 'tm2-pda 1.5s ease-in-out infinite' }, clipB: { animation: 'tm2-pdb 1.5s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'wipe-circle': return { keyframes: '@keyframes tm2-wci{0%,10%{clip-path:circle(0% at 50% 50%)}90%,100%{clip-path:circle(150% at 50% 50%)}}', clipB: { animation: 'tm2-wci 2s ease-in-out infinite', zIndex: 3 }, containerStyle: { overflow: 'hidden' } };
      case 'wipe-diagonal': return { keyframes: '@keyframes tm2-wdi{0%,10%{clip-path:polygon(0 0,0 0,0 0)}50%{clip-path:polygon(0 0,100% 0,0 100%)}90%,100%{clip-path:polygon(0 0,100% 0,100% 100%,0 100%)}}', clipB: { animation: 'tm2-wdi 2s ease-in-out infinite', zIndex: 3 }, containerStyle: { overflow: 'hidden' } };
      case 'barn-doors': return { keyframes: '@keyframes tm2-bd{0%,10%{clip-path:inset(0 50% 0 50%)}90%,100%{clip-path:inset(0 0% 0 0%)}}', clipB: { animation: 'tm2-bd 1.8s ease-in-out infinite', zIndex: 3 }, containerStyle: { overflow: 'hidden' } };
      case 'split-v': return { keyframes: '@keyframes tm2-spv{0%,10%{clip-path:inset(50% 0 50% 0)}90%,100%{clip-path:inset(0% 0 0% 0)}}', clipB: { animation: 'tm2-spv 1.8s ease-in-out infinite', zIndex: 3 }, containerStyle: { overflow: 'hidden' } };
      case 'flash': return { keyframes: '@keyframes tm2-fla{0%,38%{opacity:1}50%,100%{opacity:0}} @keyframes tm2-flb{0%,50%{opacity:0}62%,100%{opacity:1}} @keyframes tm2-flo{0%,35%{opacity:0}43%,57%{opacity:1}65%,100%{opacity:0}}', clipA: { animation: 'tm2-fla 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-flb 1.8s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: '#fff', zIndex: 3, animation: 'tm2-flo 1.8s ease-in-out infinite' } };
      case 'dip-color': return { keyframes: '@keyframes tm2-dca{0%,10%{opacity:1}40%,100%{opacity:0}} @keyframes tm2-dcb{0%,60%{opacity:0}90%,100%{opacity:1}} @keyframes tm2-dcol{0%,5%{opacity:0}35%,65%{opacity:1}95%,100%{opacity:0}}', clipA: { animation: 'tm2-dca 2s ease-in-out infinite' }, clipB: { animation: 'tm2-dcb 2s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: '#1a0a3a', zIndex: 3, animation: 'tm2-dcol 2s ease-in-out infinite' } };
      case 'page-turn': return { keyframes: '@keyframes tm2-pta{0%,10%{transform:scaleX(1);opacity:1}45%,100%{transform:scaleX(0);opacity:0}} @keyframes tm2-ptb{0%,55%{transform:scaleX(0);opacity:0}90%,100%{transform:scaleX(1);opacity:1}}', clipA: { animation: 'tm2-pta 1.8s ease-in-out infinite', transformOrigin: 'right center' }, clipB: { animation: 'tm2-ptb 1.8s ease-in-out infinite', transformOrigin: 'left center', zIndex: 2 } };
      case 'swing': return { keyframes: '@keyframes tm2-swa{0%,10%{transform:perspective(300px) rotateY(0deg);opacity:1}90%,100%{transform:perspective(300px) rotateY(-90deg);opacity:0}} @keyframes tm2-swb{0%,10%{opacity:0}90%,100%{opacity:1}}', clipA: { animation: 'tm2-swa 1.8s ease-in-out infinite', transformOrigin: 'left center' }, clipB: { animation: 'tm2-swb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'roll': return { keyframes: '@keyframes tm2-rla{0%,10%{transform:rotate(0deg) scale(1);opacity:1}90%,100%{transform:rotate(360deg) scale(0);opacity:0}} @keyframes tm2-rlb{0%,10%{transform:rotate(-360deg) scale(0);opacity:0}90%,100%{transform:rotate(0deg) scale(1);opacity:1}}', clipA: { animation: 'tm2-rla 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-rlb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'zoom-blur': return { keyframes: '@keyframes tm2-zba{0%,10%{transform:scale(1);filter:blur(0px);opacity:1}90%,100%{transform:scale(2.5);filter:blur(20px);opacity:0}} @keyframes tm2-zbb{0%,10%{transform:scale(0.3);filter:blur(20px);opacity:0}90%,100%{transform:scale(1);filter:blur(0px);opacity:1}}', clipA: { animation: 'tm2-zba 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-zbb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'zoom-in-fade': return { keyframes: '@keyframes tm2-zifa{0%,10%{transform:scale(1);opacity:1}90%,100%{transform:scale(1.6);opacity:0}} @keyframes tm2-zifb{0%,10%{transform:scale(0.6);opacity:0}90%,100%{transform:scale(1);opacity:1}}', clipA: { animation: 'tm2-zifa 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-zifb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'zoom-out-fade': return { keyframes: '@keyframes tm2-zofa{0%,10%{transform:scale(1);opacity:1}90%,100%{transform:scale(0.4);opacity:0}} @keyframes tm2-zofb{0%,10%{transform:scale(1.6);opacity:0}90%,100%{transform:scale(1);opacity:1}}', clipA: { animation: 'tm2-zofa 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-zofb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'dreamy': return { keyframes: '@keyframes tm2-dra{0%,10%{filter:blur(0px) brightness(1);opacity:1}90%,100%{filter:blur(10px) brightness(2.5);opacity:0}} @keyframes tm2-drb{0%,10%{filter:blur(10px) brightness(2.5);opacity:0}90%,100%{filter:blur(0px) brightness(1);opacity:1}}', clipA: { animation: 'tm2-dra 2s ease-in-out infinite' }, clipB: { animation: 'tm2-drb 2s ease-in-out infinite', zIndex: 2 } };
      case 'ripple': return { keyframes: '@keyframes tm2-rpa{0%,10%{transform:scale(1);opacity:1}90%,100%{transform:scale(1.12);opacity:0}} @keyframes tm2-rpb{0%,10%{transform:scale(0.88);opacity:0}90%,100%{transform:scale(1);opacity:1}} @keyframes tm2-rpo{0%,20%{opacity:0}50%{background:radial-gradient(circle at 50% 50%,rgba(255,255,255,0.3) 20%,transparent 60%);opacity:1}80%,100%{opacity:0}}', clipA: { animation: 'tm2-rpa 2s ease-in-out infinite' }, clipB: { animation: 'tm2-rpb 2s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 3, animation: 'tm2-rpo 2s ease-in-out infinite' } };
      case 'shatter': return { keyframes: '@keyframes tm2-sha{0%,10%{transform:scale(1) skewX(0deg);opacity:1;filter:blur(0)}90%,100%{transform:scale(0.05) skewX(20deg);opacity:0;filter:blur(5px)}} @keyframes tm2-shb{0%,10%{transform:scale(3);opacity:0;filter:blur(3px)}90%,100%{transform:scale(1);opacity:1;filter:blur(0)}}', clipA: { animation: 'tm2-sha 1.5s ease-in-out infinite' }, clipB: { animation: 'tm2-shb 1.5s ease-in-out infinite', zIndex: 2 } };
      case 'glitch-rgb': return { keyframes: '@keyframes tm2-grga{0%{transform:translateX(0);opacity:1;filter:none}14%{transform:translateX(-6px);filter:hue-rotate(90deg) brightness(1.3)}16%{transform:translateX(6px);filter:hue-rotate(-90deg)}18%{transform:translateX(0);filter:none}50%{opacity:0.4}80%,100%{opacity:0}} @keyframes tm2-grgb{0%,50%{opacity:0}65%{transform:translateX(5px);filter:hue-rotate(90deg)}67%{transform:translateX(-5px);filter:hue-rotate(-90deg)}80%,100%{transform:translateX(0);opacity:1;filter:none}}', clipA: { animation: 'tm2-grga 1.5s linear infinite' }, clipB: { animation: 'tm2-grgb 1.5s linear infinite', zIndex: 2 } };
      case 'color-wash': return { keyframes: '@keyframes tm2-cwa{0%,10%{opacity:1}90%,100%{opacity:0}} @keyframes tm2-cwb{0%,10%{opacity:0}90%,100%{opacity:1}} @keyframes tm2-cwo{0%,5%{opacity:0}35%,65%{opacity:1}95%,100%{opacity:0}}', clipA: { animation: 'tm2-cwa 2s ease-in-out infinite' }, clipB: { animation: 'tm2-cwb 2s ease-in-out infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: 'linear-gradient(135deg,rgba(0,150,255,0.9),rgba(150,0,255,0.9))', zIndex: 3, animation: 'tm2-cwo 2s ease-in-out infinite' } };
      case 'whip-pan': return { keyframes: '@keyframes tm2-wpa{0%,10%{transform:translateX(0);filter:blur(0px);opacity:1}90%,100%{transform:translateX(-200%);filter:blur(25px);opacity:0}} @keyframes tm2-wpb{0%,10%{transform:translateX(200%);filter:blur(25px);opacity:0}90%,100%{transform:translateX(0);filter:blur(0px);opacity:1}}', clipA: { animation: 'tm2-wpa 1.2s ease-in-out infinite' }, clipB: { animation: 'tm2-wpb 1.2s ease-in-out infinite', zIndex: 2 }, containerStyle: { overflow: 'hidden' } };
      case 'stretch-h': return { keyframes: '@keyframes tm2-stha{0%,10%{transform:scaleX(1);opacity:1}90%,100%{transform:scaleX(2.5);opacity:0}} @keyframes tm2-sthb{0%,10%{transform:scaleX(0.4);opacity:0}90%,100%{transform:scaleX(1);opacity:1}}', clipA: { animation: 'tm2-stha 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-sthb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'stretch-v': return { keyframes: '@keyframes tm2-stva{0%,10%{transform:scaleY(1);opacity:1}90%,100%{transform:scaleY(2.5);opacity:0}} @keyframes tm2-stvb{0%,10%{transform:scaleY(0.4);opacity:0}90%,100%{transform:scaleY(1);opacity:1}}', clipA: { animation: 'tm2-stva 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-stvb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'pixelate': return { keyframes: '@keyframes tm2-pxa{0%,10%{filter:blur(0px) contrast(1);opacity:1}50%{filter:blur(5px) contrast(3);opacity:0.5}90%,100%{filter:blur(0px) contrast(1);opacity:0}} @keyframes tm2-pxb{0%,10%{filter:blur(0px) contrast(1);opacity:0}50%{filter:blur(5px) contrast(3);opacity:0.5}90%,100%{filter:blur(0px) contrast(1);opacity:1}}', clipA: { animation: 'tm2-pxa 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-pxb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'vhs': return { keyframes: '@keyframes tm2-vhsa{0%{opacity:1;transform:skewX(0)}15%{transform:skewX(-3deg);filter:brightness(1.5)}16%{transform:skewX(3deg)}30%{transform:skewX(0)}60%,100%{opacity:0}} @keyframes tm2-vhsb{0%,55%{opacity:0}70%{transform:skewX(-2deg);filter:brightness(1.4)}71%{transform:skewX(2deg)}85%{transform:skewX(0)}100%{opacity:1}} @keyframes tm2-vhso{0%,40%{opacity:0}48%{opacity:0.6}52%{opacity:0}60%{opacity:0.5}70%,100%{opacity:0}}', clipA: { animation: 'tm2-vhsa 1.8s linear infinite' }, clipB: { animation: 'tm2-vhsb 1.8s linear infinite', zIndex: 2 }, overlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, background: 'repeating-linear-gradient(to bottom,rgba(255,255,255,0.04) 0px,rgba(255,255,255,0.04) 2px,transparent 2px,transparent 4px)', zIndex: 3, animation: 'tm2-vhso 1.8s linear infinite' } };
      case 'prism': return { keyframes: '@keyframes tm2-pra{0%,10%{filter:hue-rotate(0deg) brightness(1);opacity:1}90%,100%{filter:hue-rotate(360deg) brightness(1.5);opacity:0}} @keyframes tm2-prb{0%,10%{filter:hue-rotate(-360deg) brightness(1.5);opacity:0}90%,100%{filter:hue-rotate(0deg) brightness(1);opacity:1}}', clipA: { animation: 'tm2-pra 2s ease-in-out infinite' }, clipB: { animation: 'tm2-prb 2s ease-in-out infinite', zIndex: 2 } };
      case 'squeeze': return { keyframes: '@keyframes tm2-sqa{0%,10%{transform:scaleX(1) scaleY(1);opacity:1}90%,100%{transform:scaleX(0) scaleY(3);opacity:0}} @keyframes tm2-sqb{0%,10%{transform:scaleX(0) scaleY(3);opacity:0}90%,100%{transform:scaleX(1) scaleY(1);opacity:1}}', clipA: { animation: 'tm2-sqa 1.8s ease-in-out infinite' }, clipB: { animation: 'tm2-sqb 1.8s ease-in-out infinite', zIndex: 2 } };
      case 'ink-wash': return { keyframes: '@keyframes tm2-iwa{0%,10%{filter:saturate(1) sepia(0) contrast(1);opacity:1}90%,100%{filter:saturate(0) sepia(0.8) contrast(2);opacity:0}} @keyframes tm2-iwb{0%,10%{filter:saturate(0) sepia(0.8) contrast(2);opacity:0}90%,100%{filter:saturate(1) sepia(0) contrast(1);opacity:1}}', clipA: { animation: 'tm2-iwa 2s ease-in-out infinite' }, clipB: { animation: 'tm2-iwb 2s ease-in-out infinite', zIndex: 2 } };
      case 'tv-off': return { keyframes: '@keyframes tm2-tva{0%,10%{transform:scaleY(1) scaleX(1);opacity:1;filter:brightness(1)}60%{transform:scaleY(0.02) scaleX(1.1);opacity:1;filter:brightness(3)}90%,100%{transform:scaleY(0.02) scaleX(0);opacity:0}} @keyframes tm2-tvb{0%,10%{opacity:0}90%,100%{opacity:1}}', clipA: { animation: 'tm2-tva 1.5s ease-in-out infinite' }, clipB: { animation: 'tm2-tvb 1.5s ease-in-out infinite', zIndex: 2 } };
      default: return { keyframes: '' };
    }
  }

  const CATEGORIES = ['all', 'basic', 'wipe', 'slide', 'push', 'zoom', '3d', 'creative', 'blur'];

  function TransitionMenuReal() {
    const [selectedTransition, setSelectedTransition] = React.useState(null);
    const [activeCategory, setActiveCategory] = React.useState('all');
    const selectedClips = ['demo-clip-1', 'demo-clip-2'];
    const clips = [
      { id: 'demo-clip-1', name: 'Interview.mp4', startTime: 0, endTime: 5.0, trackId: 'video-track-1' },
      { id: 'demo-clip-2', name: 'B-Roll.mp4', startTime: 5.0, endTime: 10.0, trackId: 'video-track-1' },
    ];
    const addTransition = (tr) => console.log('[demo] addTransition', tr.name);
    const updateClip = () => {};
    const selectTransitions = () => {};

    const transitions = activeCategory === 'all'
      ? TRANSITION_TYPES
      : TRANSITION_TYPES.filter(tr => tr.category === activeCategory);

    const keyframesCss = TRANSITION_TYPES
      .map(tr => getTransitionPreviewStyle(tr.id).keyframes)
      .filter(Boolean)
      .join(' ');

    const handleTransitionClick = (transition) => {
      setSelectedTransition(transition.id);
      if (selectedClips.length !== 2) {
        console.log('[demo] Select two adjacent clips first.');
        return;
      }
      const clipsMap = new Map(clips.map(c => [c.id, c]));
      const sortedSelected = selectedClips
        .map(id => clipsMap.get(id))
        .filter(Boolean)
        .sort((a, b) => (a.startTime ?? 0) - (b.startTime ?? 0));
      const clipA = sortedSelected[0];
      const clipB = sortedSelected[1];
      const meta = TRANSITION_META[transition.id];
      const duration = meta?.defaultDuration ?? 1.0;
      const aEnd = clipA.endTime ?? 5.0;
      const transitionObj = {
        id: `transition-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
        type: transition.id,
        name: transition.name,
        duration,
        startTime: aEnd - duration / 2,
        endTime: aEnd + duration / 2,
        fromClipId: clipA.id,
        toClipId: clipB.id,
        trackId: clipA.trackId,
        intensity: 1.0,
        easing: 'linear',
      };
      addTransition(transitionObj);
      console.log('[demo] Applied transition:', transition.name, 'duration:', duration + 's');
    };

    const css = `
      .vls-ed-tm-wrap { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: ${t.bgCard}; border-radius: 8px; overflow: hidden; }
      .vls-ed-tm-cats { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 14px; background: ${t.bgCardAlt}; border-bottom: 1px solid ${t.border}; }
      .vls-ed-tm-cat { padding: 4px 10px; border-radius: 12px; border: 1px solid ${t.border}; background: transparent; color: ${t.textMuted}; font-size: 11px; font-weight: 600; cursor: pointer; text-transform: capitalize; transition: all 0.15s; }
      .vls-ed-tm-cat.active { background: rgba(0,255,0,0.12); border-color: rgba(0,255,0,0.4); color: ${t.accent}; }
      .vls-ed-tm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 8px; padding: 14px; max-height: 320px; overflow-y: auto; }
      .vls-ed-tm-item { display: flex; flex-direction: column; align-items: stretch; box-sizing: border-box; height: 80px; padding: 0; background: #2a2a2a; border: 2px solid #3a3a3a; border-radius: 8px; color: #fff; cursor: pointer; transition: all 0.2s; overflow: hidden; flex-shrink: 0; }
      .vls-ed-tm-item:hover { background: #333; border-color: #555; box-shadow: 0 4px 8px rgba(0,0,0,0.3); }
      .vls-ed-tm-item.active { border-color: #00ff00; box-shadow: 0 0 0 1px #00ff00; }
      .vls-ed-tm-item.active .vls-ed-tm-name { color: #00ff00; }
      .vls-ed-tm-preview { position: relative; width: 100%; height: 55px; flex-shrink: 0; border-radius: 6px 6px 0 0; overflow: hidden; }
      .vls-ed-tm-clip-a { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 2; background: linear-gradient(135deg,#1a6b8a 0%,#2db5c8 100%); }
      .vls-ed-tm-clip-b { position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; background: linear-gradient(135deg,#7b2d8b 0%,#b54ca9 100%); }
      .vls-ed-tm-name { height: 25px; display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 500; text-align: center; line-height: 1.2; padding: 2px 4px; flex-shrink: 0; }
    `;

    return (
      <div className="vls-ed-tm-wrap">
        <style>{css}</style>
        <style>{keyframesCss}</style>
        <div className="vls-ed-tm-cats">
          {CATEGORIES.map(cat => (
            <button
              key={cat}
              className={`vls-ed-tm-cat${activeCategory === cat ? ' active' : ''}`}
              onClick={() => setActiveCategory(cat)}
            >
              {cat === 'all' ? 'All' : cat}
            </button>
          ))}
        </div>
        <div className="vls-ed-tm-grid">
          {transitions.map(transition => {
            const preview = getTransitionPreviewStyle(transition.id);
            return (
              <button
                key={transition.id}
                className={`vls-ed-tm-item${selectedTransition === transition.id ? ' active' : ''}`}
                onClick={() => handleTransitionClick(transition)}
                title={TRANSITION_META[transition.id]?.description ?? transition.name}
              >
                <div className="vls-ed-tm-preview" style={preview.containerStyle || {}}>
                  <div className="vls-ed-tm-clip-a" style={preview.clipA || {}} />
                  {preview.overlay && <div style={preview.overlay} />}
                  <div className="vls-ed-tm-clip-b" style={preview.clipB || {}} />
                </div>
                <div className="vls-ed-tm-name">{transition.name}</div>
              </button>
            );
          })}
        </div>
        <div style={{ padding: '8px 14px', fontSize: '11px', color: t.textMuted, borderTop: `1px solid ${t.border}` }}>
          {selectedTransition
            ? `Selected: ${TRANSITION_TYPES.find(tr => tr.id === selectedTransition)?.name} — ${TRANSITION_META[selectedTransition]?.description}`
            : 'Click a transition to apply between two clips'}
        </div>
      </div>
    );
  }

  window.TransitionMenuReal = TransitionMenuReal;
})();
