// App root — una variante a la vez, rueda flotante abajo-izquierda
const { useState, useRef, useEffect } = React;
const data = window.PORTFOLIO_DATA;

// ─── Mapa de variantes → archivo fuente y nombre global ────────────────────
const VARIANT_FILES = {
  v1:  ['src/v1-terminal.jsx',           'V1Terminal'],
  v2:  ['src/v2-ide.jsx',                'V2IDE'],
  v3:  ['src/v3-cyber.jsx',              'V3Cyber'],
  v4:  ['src/v4-nutri.jsx',              'V4Nutri'],
  v5:  ['src/v5-bar.jsx',                'V5Bar'],
  v6:  ['src/v6-cosmos.jsx',             'V6Cosmos'],
  v7:  ['src/v7-ocean.jsx',              'V7Ocean'],
  v8:  ['src/v8-aurora.jsx',             'V8Aurora'],
  v9:  ['src/v9-matrix.jsx',             'V9Matrix'],
  v10: ['src/v10-bento.jsx',             'V10Bento'],
  v11: ['src/v11-pantone.jsx',           'V11Pantone'],
  v12: ['src/v12-fluid.jsx',             'V12Fluid'],
  v13: ['src/v13-jupyter.jsx',           'V13Jupyter'],
  v14: ['src/v14-blueprint.jsx',         'V14Blueprint'],
  v15: ['src/v15-retro-os.jsx',          'V15RetroOS'],
  v16: ['src/v16-gameboy.jsx',           'V16Gameboy'],
  v17: ['src/v17-synthwave.jsx',         'V17Synthwave'],
  v18: ['src/v18-glitch.jsx',            'V18Glitch'],
  v19: ['src/v19-comic.jsx',             'V19Comic'],
  v20: ['src/v20-pipboy.jsx',            'V20PipBoy'],
  v21: ['src/v21-neural.jsx',            'V21Neural'],
  v22: ['src/v22-receipt.jsx',           'V22Receipt'],
  v23: ['src/v23-bios.jsx',              'V23Bios'],
  v24: ['src/v24-ecommerce.jsx',         'V24Ecommerce'],
  v25: ['src/v25-newspaper.jsx',         'V25Newspaper'],
  v26: ['src/v26-mecha.jsx',             'V26Mecha'],
  v27: ['src/v27-mac1984.jsx',           'V27Mac1984'],
  v28: ['src/v28-cinematic.jsx',         'V28Cinematic'],
  v29: ['src/v29-player.jsx',            'V29Player'],
  v30: ['src/v30-dnd.jsx',               'V30Dnd'],
  v31: ['src/v31-nodes.jsx',             'V31Nodes'],
  v32: ['src/v32-sonar.jsx',             'V32Sonar'],
  v33: ['src/v33-fractal.jsx',           'V33Fractal'],
  v34: ['src/v34-ascii.jsx',             'V34Ascii'],
  v35: ['src/v35-isometry.jsx',          'V35Isometry'],
  v36: ['src/v36-japan.jsx',             'V36Japan'],
  v37: ['src/v37-china.jsx',             'V37China'],
  v38: ['src/v38-jewelry-luxury.jsx',    'V38JewelryLuxury'],
  v39: ['src/v39-jewelry-boutique.jsx',  'V39JewelryBoutique'],
  v40: ['src/v40-jewelry-atelier.jsx',   'V40JewelryAtelier'],
  v41: ['src/v41-retro-pop.jsx',         'V41RetroPop'],
  v42: ['src/v42-yoga.jsx',              'V42Yoga'],
  v43: ['src/v43-gym.jsx',               'V43Gym'],
  v44: ['src/v44-fashion.jsx',           'V44Fashion'],
  v45: ['src/v45-shoes.jsx',             'V45Shoes'],
  v46: ['src/v46-bakery.jsx',            'V46Bakery'],
  v47: ['src/v47-funeral.jsx',           'V47Funeral'],
  v48: ['src/v48-makeup.jsx',            'V48Makeup'],
  v49: ['src/v49-aesthetic-clinic.jsx',  'V49AestheticClinic'],
  v50: ['src/v50-dental.jsx',            'V50Dental'],
  v51: ['src/v51-pets.jsx',              'V51Pets'],
  v52: ['src/v52-retro-pop-classic.jsx', 'V52RetroPopClassic'],
  v53: ['src/v53-retro-pop-disco.jsx',   'V53RetroPopDisco'],
  v54: ['src/v54-cyber-samurai.jsx',     'V54CyberSamurai'],
  v55: ['src/v55-orquidea.jsx',          'V55Orquidea'],
  v56: ['src/v56-barberia.jsx',          'V56Barberia'],
  v57: ['src/v57-tatuajes.jsx',          'V57Tatuajes'],
  v58: ['src/v58-cafeteria.jsx',         'V58Cafeteria'],
  v59: ['src/v59-fotografia.jsx',        'V59Fotografia'],
  v60: ['src/v60-inmobiliaria.jsx',      'V60Inmobiliaria'],
  v61: ['src/v61-vaporwave.jsx',         'V61Vaporwave'],
  v62: ['src/v62-memphis.jsx',           'V62Memphis'],
  v63: ['src/v63-dashboard.jsx',         'V63Dashboard'],
  v64: ['src/v64-estudio-musical.jsx',   'V64EstudioMusical'],
  v65: ['src/v65-acid.jsx',              'V65Acid'],
  v66: ['src/v66-holo.jsx',              'V66Holo'],
  v67: ['src/v67-bauhaus.jsx',           'V67Bauhaus'],
  v68: ['src/v68-rave.jsx',              'V68Rave'],
  v69: ['src/v69-lava.jsx',              'V69Lava'],
  v70: ['src/v70-mesh.jsx',              'V70Mesh'],
  v71: ['src/v71-brew-haven.jsx',        'V71BrewHaven'],
  v72: ['src/v72-brutalist.jsx',         'V72Brutalist'],
  v73: ['src/v73-tarot.jsx',             'V73Tarot'],
  v74: ['src/v74-aurora-glass.jsx',      'V74AuroraGlass'],
  v75: ['src/v75-ecoguard.jsx',          'V75EcoGuard'],
  v76: ['src/v76-aston-martin.jsx',      'V76AstonMartin'],
};

// ─── Categorías para el desplegable ────────────────────────────────────────
const CATEGORIES = [
  { id: 'restaurants', label: '🍽️  Restaurantes & Repostería', ids: ['v5', 'v36', 'v37', 'v46', 'v58', 'v71'] },
  { id: 'jewelry',     label: '💎  Joyerías',                  ids: ['v38', 'v39', 'v40'] },
  { id: 'fashion',     label: '🎀  Moda & Calzado',            ids: ['v44', 'v45'] },
  { id: 'beauty',      label: '💆  Belleza & Estética',        ids: ['v48', 'v49', 'v56', 'v57'] },
  { id: 'health',      label: '🏥  Salud & Bienestar',         ids: ['v42', 'v43', 'v50'] },
  { id: 'pets',        label: '🐾  Mascotas',                  ids: ['v51'] },
  { id: 'stores',      label: '🛍️  Tiendas & Negocios',       ids: ['v4', 'v22', 'v24', 'v47', 'v60'] },
  { id: 'portfolio',   label: '💻  Portafolios',               ids: ['v1', 'v2', 'v3', 'v13', 'v14', 'v20', 'v23', 'v25', 'v59', 'v61', 'v63'] },
  { id: 'creative',    label: '🎨  Diseño & Arte',             ids: ['v6', 'v7', 'v8', 'v11', 'v12', 'v17', 'v18', 'v19', 'v28', 'v29', 'v62', 'v64', 'v65', 'v66', 'v67', 'v68', 'v69', 'v70', 'v72', 'v73', 'v74'] },
  { id: 'gaming',      label: '🎮  Gaming & Retro',            ids: ['v9', 'v15', 'v16', 'v26', 'v27', 'v30', 'v33', 'v34'] },
  { id: 'experimental',label: '🔬  Experimental',              ids: ['v10', 'v21', 'v31', 'v32', 'v35'] },
  { id: '3d',          label: '🧊  3D / Interactivo',          ids: ['v54', 'v76'] },
];

const SECRET_CATEGORY = { id: 'secrets', label: '🔐  Secretos', ids: ['v41', 'v52', 'v53', 'v55', 'v75'] };

// ─── Lista completa de variantes ────────────────────────────────────────────
const VARIANTS = [
  // Restaurantes & Repostería
  { id: 'v5',  label: 'Bar',        hint: 'carta',           bg: '#17110c', color: '#d4a574' },
  { id: 'v36', label: 'Japan',      hint: 'kaiseki/omakase', bg: '#1a1614', color: '#c0392b' },
  { id: 'v37', label: 'China',      hint: 'dragón dorado',   bg: '#0d0200', color: '#d4a017' },
  { id: 'v46', label: 'Bakery',     hint: 'pastelería',      bg: '#fef9f0', color: '#5c3317' },
  { id: 'v55', label: 'Orquídea',  hint: 'gluten free pastry', bg: '#2b5c44', color: '#e8a0b4' },
  // Joyerías
  { id: 'v38', label: 'Luxury',     hint: 'alta joyería',    bg: '#080604', color: '#c9a84c' },
  { id: 'v39', label: 'Boutique',   hint: 'rose gold',       bg: '#fefefe', color: '#b76e79' },
  { id: 'v40', label: 'Atelier',    hint: 'art nouveau',     bg: '#fdf6e3', color: '#3d1020' },
  // Moda & Calzado
  { id: 'v41', label: 'RetroPop',   hint: '70s groovy',      bg: '#f5f0e6', color: '#df2d19' },
  { id: 'v52', label: 'Pop Classic',hint: 'mosaico original', bg: '#f5f0e6', color: '#df2d19' },
  { id: 'v53', label: 'Pop Disco',  hint: 'glam dark gold',  bg: '#100820', color: '#f0c23e' },
  { id: 'v44', label: 'Fashion',    hint: 'editorial',       bg: '#fafafa', color: '#c8a84c' },
  { id: 'v45', label: 'Shoes',      hint: 'zapatería',       bg: '#fdfaf6', color: '#c8773a' },
  // Belleza & Estética
  { id: 'v48', label: 'Makeup',     hint: 'rose gold glam',  bg: '#0c0808', color: '#c9607a' },
  { id: 'v49', label: 'Aesthetic',  hint: 'clínica estética',bg: '#fdfaf8', color: '#d4687e' },
  // Salud & Bienestar
  { id: 'v42', label: 'Yoga',       hint: 'bienestar',       bg: '#faf7f2', color: '#7a9e7e' },
  { id: 'v43', label: 'Gym',        hint: 'neon power',      bg: '#0d0d0d', color: '#e8f325' },
  { id: 'v50', label: 'Dental',     hint: 'clínica dental',  bg: '#f0f8ff', color: '#1b4f8a' },
  // Mascotas
  { id: 'v51', label: 'Pets',       hint: 'pantone + paws',  bg: '#fff9f5', color: '#FF6B6B' },
  // Tiendas & Negocios
  { id: 'v47', label: 'Funeral',    hint: 'serenidad',       bg: '#0a1628', color: '#c9a84c' },
  { id: 'v4',  label: 'Nutri',      hint: 'nutrición',       bg: '#faf7f0', color: '#3f6b4a' },
  { id: 'v22', label: 'Receipt',    hint: 'ticket',          bg: '#e4e4e4', color: '#1a1a1a' },
  { id: 'v24', label: 'Store',      hint: 'e-commerce',      bg: '#f3f4f6', color: '#f6ad55' },
  { id: 'v60', label: 'Inmobili.',  hint: 'swiss bauhaus RE', bg: '#ffffff',  color: '#E63946' },
  // Portafolios
  { id: 'v1',  label: 'Terminal',   hint: 'zsh',             bg: '#0a0a0a', color: '#7fb98c' },
  { id: 'v2',  label: 'IDE',        hint: 'vscode',          bg: '#1e1e1e', color: '#c586c0' },
  { id: 'v3',  label: 'Cyber',      hint: 'brutalist',       bg: '#050505', color: '#7CFFA5' },
  { id: 'v13', label: 'Jupyter',    hint: 'notebook',        bg: '#f8f9fa', color: '#1976d2' },
  { id: 'v14', label: 'Blueprint',  hint: 'cad/plano',       bg: '#053163', color: '#7db9f7' },
  { id: 'v20', label: 'Pip-Boy',    hint: 'robco os',        bg: '#091208', color: '#14e33a' },
  { id: 'v23', label: 'BIOS',       hint: 'setup utility',   bg: '#0000aa', color: '#ffff55' },
  { id: 'v25', label: 'News',       hint: 'newspaper',       bg: '#f4f1ea', color: '#1a1a1a' },
  { id: 'v61', label: 'Vaporwave',  hint: 'synthpop retro',  bg: '#0d0015',  color: '#ff2d78' },
  // Diseño & Arte
  { id: 'v6',  label: 'Cosmos',     hint: 'astronomía',      bg: '#02030a', color: '#9ab6ff' },
  { id: 'v7',  label: 'Ocean',      hint: 'inmersión',       bg: '#052544', color: '#5effc7' },
  { id: 'v8',  label: 'Aurora',     hint: 'gooey blobs',     bg: '#0a0615', color: '#ff6b9d' },
  { id: 'v11', label: 'Pantone',    hint: 'chroma',          bg: '#f2f0e8', color: '#ff005c' },
  { id: 'v12', label: 'Fluid',      hint: 'mouse flow',      bg: '#07060e', color: '#ff3d88' },
  { id: 'v17', label: 'Synthwave',  hint: '80s outrun',      bg: '#0b001a', color: '#e800ff' },
  { id: 'v18', label: 'Glitch',     hint: 'vhs noise',       bg: '#020202', color: '#0ff'    },
  { id: 'v19', label: 'Comic',      hint: 'manga/pop-art',   bg: '#f4f4f4', color: '#e30022' },
  { id: 'v28', label: 'Cinematic',  hint: 'dark fade',       bg: '#030303', color: '#ffffff' },
  { id: 'v29', label: 'Player',     hint: 'music app',       bg: '#121212', color: '#1db954' },
  // Gaming & Retro
  { id: 'v9',  label: 'Matrix',     hint: 'canvas rain',     bg: '#000000', color: '#00ff9d' },
  { id: 'v15', label: 'Win95',      hint: 'retro os',        bg: '#008080', color: '#ffffff' },
  { id: 'v16', label: 'GameBoy',    hint: '8-bit rpg',       bg: '#9bbc0f', color: '#0f380f' },
  { id: 'v26', label: 'Mecha',      hint: 'anime ui',        bg: '#090909', color: '#ff6600' },
  { id: 'v27', label: 'Mac1984',    hint: 'classic b&w',     bg: '#ffffff', color: '#000000' },
  { id: 'v30', label: 'Quest',      hint: 'rpg sheet',       bg: '#f4ecd8', color: '#8b0000' },
  { id: 'v33', label: 'Fractal',    hint: 'l-system',        bg: '#e8e4d9', color: '#8b4513' },
  { id: 'v34', label: 'ASCII',      hint: '3d torus',        bg: '#000000', color: '#00ff00' },
  // 3D / Interactivo
  { id: 'v54', label: 'CyberSamurai', hint: '3D model-viewer', bg: '#050812', color: '#00e5ff' },
  // Experimental
  { id: 'v10', label: 'Bento',      hint: '3d tilt',         bg: '#0e0e12', color: '#4d9fff' },
  { id: 'v21', label: 'Neural',     hint: 'force graph',     bg: '#030408', color: '#00f0ff' },
  { id: 'v31', label: 'Nodes',      hint: 'drag & drop',     bg: '#1e1e24', color: '#7b61ff' },
  { id: 'v32', label: 'Sonar',      hint: 'radar+audio',     bg: '#010a05', color: '#00ff66' },
  { id: 'v35', label: 'Isometry',   hint: 'css 3d city',     bg: '#2b2b36', color: '#00f0ff' },
  { id: 'v56', label: 'Gentleman',  hint: 'classic barbershop', bg: '#1a140f', color: '#c5a575' },
  { id: 'v57', label: 'Tatuajes',   hint: 'dark academia',   bg: '#0f0a06', color: '#c4a55a' },
  { id: 'v58', label: 'Café',       hint: 'glassmorphism',   bg: '#3d1c02', color: '#c26a2d' },
  { id: 'v59', label: 'Fotografía', hint: 'minimal dark',    bg: '#080808', color: '#e8a84c' },
  { id: 'v62', label: 'Memphis',    hint: '80s design',      bg: '#FFFFFF', color: '#E63946' },
  { id: 'v63', label: 'Dashboard',  hint: 'analytics ui',    bg: '#0d1117', color: '#4d9fff' },
  { id: 'v64', label: 'Estudio',    hint: 'music producer',  bg: '#080808', color: '#b700ff' },
  { id: 'v65', label: 'Acid',       hint: 'cyberdelic green',bg: '#000000', color: '#CCFF00' },
  { id: 'v66', label: 'Holo',       hint: 'iridescent glass',bg: '#ff007f', color: '#ffffff' },
  { id: 'v67', label: 'Bauhaus',    hint: 'primary colors',  bg: '#f4f1e9', color: '#e83f2c' },
  { id: 'v68', label: 'Rave',       hint: 'y2k club',        bg: '#050505', color: '#8BFF00' },
  { id: 'v69', label: 'Lava',       hint: 'fluid orbs',      bg: '#1a0b2e', color: '#ff3366' },
  { id: 'v70', label: 'Mesh',       hint: 'gradient mesh',   bg: '#001b44', color: '#00ffcc' },
  { id: 'v71', label: 'Brew Haven', hint: 'coffee shop warm',    bg: '#140a04', color: '#c49a3c' },
  { id: 'v72', label: 'Brutalist',  hint: 'raw grid exposure',   bg: '#f0ede6', color: '#000000' },
  { id: 'v73', label: 'Tarot',      hint: 'arcanum flip card',   bg: '#06040f', color: '#d4af37' },
  { id: 'v74', label: 'Aurora Glass', hint: 'glassmorphism aurora', bg: '#020b18', color: '#00e5a0' },
  { id: 'v75', label: 'EcoGuard',    hint: 'fundación ecológica',  bg: '#071a0e', color: '#7dc96e' },
  { id: 'v76', label: 'Aston Martin', hint: '3D supercar racing',  bg: '#080604', color: '#e8a020' },
];

// ─── MiniPreview ────────────────────────────────────────────────────────────
function MiniPreview({ v }) {
  const box = { width: 36, height: 36, borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, position: 'relative', overflow: 'hidden' };

  switch (v.id) {
    case 'v1':  return <div style={{...box, background:'#000', border:`1px solid #333`, color:v.color, fontSize:14, fontWeight:'bold'}}>_</div>;
    case 'v2':  return <div style={{...box, background:'#1e1e1e', border:'1px solid #444', borderLeft:`6px solid ${v.color}`}} />;
    case 'v3':  return <div style={{...box, background:'#000', border:`2px solid ${v.color}`, boxShadow:`0 0 8px ${v.color}80`}}><div style={{width:10,height:10,background:v.color}}/></div>;
    case 'v4':  return <div style={{...box, background:'#fff', border:'2px solid #000', borderTop:'8px solid #000', flexDirection:'column', gap:3}}><div style={{width:'70%',height:2,background:'#000'}}/><div style={{width:'40%',height:2,background:'#000'}}/></div>;
    case 'v5':  return <div style={{...box, background:'#17110c', border:`2px double ${v.color}`, color:v.color, fontFamily:'serif', fontStyle:'italic', fontSize:16}}>V</div>;
    case 'v6':  return <div style={{...box, background:'#02030a'}}><div style={{width:8,height:8,background:'#fff',borderRadius:'50%',boxShadow:'0 0 10px #fff'}}/></div>;
    case 'v7':  return <div style={{...box, background:`linear-gradient(to bottom, #7fd4ed, #052544)`}} />;
    case 'v8':  return <div style={{...box, background:'#0a0615'}}><div style={{position:'absolute',top:4,left:4,width:20,height:20,background:v.color,borderRadius:'50%',filter:'blur(5px)'}}/></div>;
    case 'v9':  return <div style={{...box, background:'#000', color:v.color, fontSize:12, borderTop:`4px solid ${v.color}`, paddingTop:4}}>01</div>;
    case 'v10': return <div style={{...box, display:'grid', gridTemplateColumns:'1fr 1fr', gap:3, padding:6}}><div style={{background:v.color,borderRadius:2}}/><div style={{background:'#333',borderRadius:2}}/><div style={{background:'#333',borderRadius:2,gridColumn:'span 2'}}/></div>;
    case 'v11': return <div style={{...box, background:'#fff', border:'1px solid #ccc', display:'flex', flexDirection:'column'}}><div style={{flex:1,background:v.color,width:'100%'}}/><div style={{height:10}}/></div>;
    case 'v12': return <div style={{...box, background:'#07060e'}}><div style={{position:'absolute',bottom:-4,right:-4,width:24,height:24,background:v.color,borderRadius:'50%',filter:'blur(6px)'}}/></div>;
    case 'v13': return <div style={{...box, background:'#fff', border:'1px solid #ddd', borderLeft:`6px solid ${v.color}`}} />;
    case 'v14': return <div style={{...box, background:'#053163', backgroundImage:`linear-gradient(rgba(255,255,255,0.2) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.2) 1px, transparent 1px)`, backgroundSize:'8px 8px'}} />;
    case 'v15': return <div style={{...box, background:'#008080'}}><div style={{width:20,height:20,background:'#c0c0c0',borderTop:'2px solid #fff',borderLeft:'2px solid #fff',borderBottom:'2px solid #000',borderRight:'2px solid #000'}}/></div>;
    case 'v16': return <div style={{...box, background:'#d6d6d6', borderRadius:'4px 4px 10px 4px'}}><div style={{width:18,height:14,background:'#9bbc0f',border:'2px solid #4f4f4f'}}/></div>;
    case 'v17': return <div style={{...box, background:'#0b001a', borderBottom:`8px solid ${v.color}`}}><div style={{position:'absolute',top:8,left:10,width:16,height:16,background:'linear-gradient(#ffea00, #ff007f)',borderRadius:'50%'}}/></div>;
    case 'v18': return <div style={{...box, background:'#020202', color:'#fff', fontSize:16, fontWeight:'bold', textShadow:'-2px 0 cyan, 2px 0 magenta'}}>R</div>;
    case 'v19': return <div style={{...box, background:'#ffde00', border:'2px solid #000', boxShadow:'2px 2px 0 #000', color:'#e30022', fontSize:18, fontWeight:'bold', fontFamily:'serif'}}>!</div>;
    case 'v20': return <div style={{...box, background:'#091208', border:`2px solid ${v.color}`, color:v.color, textShadow:`0 0 4px ${v.color}`, fontSize:10, fontWeight:'bold'}}>_&gt;</div>;
    case 'v21': return <div style={{...box, background:'#030408'}}><div style={{width:6,height:6,borderRadius:'50%',background:v.color,boxShadow:`0 0 10px ${v.color}`,position:'absolute',top:'50%',left:'50%',transform:'translate(-50%,-50%)'}}/><div style={{width:1,height:16,background:v.color,position:'absolute',top:6,left:18,transform:'rotate(45deg)'}}/></div>;
    case 'v22': return <div style={{...box, background:'#e4e4e4'}}><div style={{width:20,height:26,background:'#fff',borderTop:'1px dashed #999',borderBottom:'1px dashed #999',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:2}}><div style={{width:14,height:1,background:'#333'}}/><div style={{width:10,height:1,background:'#333'}}/><div style={{width:14,height:1,background:'#333'}}/></div></div>;
    case 'v23': return <div style={{...box, background:'#0000aa', border:'3px double #aaaaaa', color:'#ffff55', fontSize:11, fontWeight:'bold'}}>A:</div>;
    case 'v24': return <div style={{...box, background:'#fff', border:'1px solid #ccc', display:'flex', flexDirection:'column', gap:2}}><div style={{width:'100%',height:6,background:'#131921'}}/><div style={{width:'60%',height:4,background:v.color,alignSelf:'flex-start',marginLeft:4,marginTop:4,borderRadius:2}}/></div>;
    case 'v25': return <div style={{...box, background:'#f4f1ea', border:'1px solid #1a1a1a', fontFamily:'serif', fontSize:18, fontWeight:'bold', color:'#1a1a1a'}}>N</div>;
    case 'v26': return <div style={{...box, background:'#000', border:`2px solid ${v.color}`, display:'flex', flexDirection:'column'}}><div style={{background:'#e60000',height:8,width:'100%'}}/><div style={{margin:'auto',color:v.color,fontSize:9,fontWeight:900}}>EVA</div></div>;
    case 'v27': return <div style={{...box, background:'#fff', border:'2px solid #000', boxShadow:'2px 2px 0 #000', display:'flex', flexDirection:'column'}}><div style={{width:'100%',height:6,borderBottom:'2px solid #000',background:'repeating-linear-gradient(to bottom,#000 0,#000 1px,#fff 1px,#fff 2px)'}}/></div>;
    case 'v28': return <div style={{...box, background:'linear-gradient(to bottom, #444 0%, #000 100%)'}}><div style={{position:'absolute',bottom:6,left:6,width:14,height:2,background:'#fff'}}/></div>;
    case 'v29': return <div style={{...box, background:'#121212', border:'1px solid #333'}}><div style={{width:0,height:0,borderTop:'8px solid transparent',borderBottom:'8px solid transparent',borderLeft:`12px solid ${v.color}`,marginLeft:4}}/></div>;
    case 'v30': return <div style={{...box, background:'#f4ecd8', border:'2px double #2b1b17', color:'#8b0000', fontFamily:'serif', fontWeight:'bold', fontSize:14}}>D20</div>;
    case 'v31': return <div style={{...box, background:'#1e1e24', border:'1px solid #3f3f4e'}}><div style={{width:6,height:6,background:v.color,borderRadius:2,position:'absolute',top:8,left:8}}/><div style={{width:6,height:6,background:'#00d2ff',borderRadius:2,position:'absolute',bottom:8,right:8}}/><svg style={{position:'absolute',inset:0}}><path d="M14 11 C20 11, 16 25, 22 25" stroke={v.color} fill="none"/></svg></div>;
    case 'v32': return <div style={{...box, background:'#010a05', border:'1px solid #003319', borderRadius:'50%'}}><div style={{width:'50%',height:'50%',borderRight:`2px solid ${v.color}`,borderTop:`2px solid ${v.color}`,borderRadius:'0 100% 0 0',position:'absolute',bottom:'50%',left:'50%'}}/></div>;
    case 'v33': return <div style={{...box, background:'#e8e4d9', border:'1px solid #d0c8b0'}}><svg width="20" height="20"><path d="M10 20 L10 12 M10 12 L5 5 M10 12 L15 5" stroke={v.color} fill="none" strokeWidth="2"/></svg></div>;
    case 'v34': return <div style={{...box, background:'#000'}}><div style={{color:v.color,fontFamily:'monospace',fontSize:16,fontWeight:'bold'}}>#</div></div>;
    case 'v35': return <div style={{...box, background:'#2b2b36', perspective:100}}><div style={{width:14,height:14,background:v.color,transform:'rotateX(60deg) rotateZ(-45deg)'}}/></div>;
    case 'v36': return <div style={{...box, background:'#1a1614', overflow:'hidden'}}><div style={{position:'absolute',top:0,left:0,right:0,height:6,background:'#c0392b'}}/><div style={{position:'absolute',top:8,left:'50%',transform:'translateX(-50%)',width:10,height:10,borderRadius:'50%',border:'2px solid #c0392b',opacity:0.7}}/></div>;
    case 'v37': return <div style={{...box, background:'#0d0200', border:`1px solid rgba(212,160,23,0.4)`}}><div style={{position:'absolute',top:4,left:4,width:10,height:16,background:'radial-gradient(ellipse,#cc1100,#550000)',borderRadius:'40% 40% 50% 50%'}}/><div style={{position:'absolute',bottom:5,right:5,fontSize:11,color:'#d4a017',fontWeight:'bold',lineHeight:1}}>金</div></div>;
    // Moda & Calzado
    case 'v41': return <div style={{...box, background:'#f5f0e6', border:'2px solid #1a1a1a', padding:3, display:'grid', gridTemplateColumns:'1fr 1fr', gap:2}}><div style={{background:'#ffb3cc',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>🌸</div><div style={{background:'#ffd600',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>⚡</div><div style={{background:'#f96b8c',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>💋</div><div style={{background:'#c9a9ff',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>⭐</div></div>;
    case 'v52': return <div style={{...box, background:'#ffb3cc', border:'2px solid #1a1a1a', padding:0, overflow:'hidden', display:'grid', gridTemplateColumns:'1fr 2fr', gridTemplateRows:'1fr 1fr'}}><div style={{background:'#ffb3cc',display:'flex',alignItems:'center',justifyContent:'center',fontSize:10,borderRight:'2px solid #1a1a1a',borderBottom:'2px solid #1a1a1a'}}>🌸</div><div style={{background:'#fdf8f0',display:'flex',alignItems:'center',padding:'0 4px',borderBottom:'2px solid #1a1a1a',fontFamily:'Fredoka One,cursive',fontSize:8,color:'#df2d19',WebkitTextStroke:'0.5px #c9a9ff',paintOrder:'stroke fill'}}>Pop</div><div style={{background:'#ffd600',display:'flex',alignItems:'center',justifyContent:'center',fontSize:10,borderRight:'2px solid #1a1a1a'}}>⚡</div><div style={{background:'#f96b8c',display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>💋</div></div>;
    case 'v53': return <div style={{...box, background:'#100820', border:'2px solid #f0c23e40', padding:3, display:'grid', gridTemplateColumns:'1fr 1fr', gap:2}}><div style={{background:'#2a104e',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>🪩</div><div style={{background:'#3d0b30',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>💫</div><div style={{background:'#0a2040',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>🕺</div><div style={{background:'#3d2800',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>✨</div></div>;
    case 'v44': return <div style={{...box, display:'grid', gridTemplateColumns:'1fr 1fr'}}><div style={{background:'#111',display:'flex',alignItems:'center',justifyContent:'center',color:'#c8a84c',fontFamily:'serif',fontSize:14,fontStyle:'italic'}}>S</div><div style={{background:'#f7f3ee',display:'flex',alignItems:'center',justifyContent:'center',fontSize:14,opacity:.5}}>👗</div></div>;
    case 'v45': return <div style={{...box, backgroundImage:'repeating-linear-gradient(125deg,transparent 0,transparent 8px,rgba(180,160,140,.1) 8px,rgba(180,160,140,.1) 9px)', backgroundColor:'#f5f0ea', border:`1px solid rgba(200,119,58,.3)`}}><div style={{fontSize:18,opacity:.7}}>👠</div></div>;
    // Joyerías
    case 'v38': return <div style={{...box, background:'#080604', border:`1px solid rgba(201,168,76,0.3)`}}><div style={{position:'absolute',inset:4,border:`1px solid rgba(201,168,76,0.2)`,borderRadius:2}}/><div style={{fontSize:16,opacity:0.7}}>💍</div></div>;
    case 'v39': return <div style={{...box, background:'#fefefe', border:'1px solid #ece5e2'}}><div style={{width:18,height:18,borderRadius:'50%',border:`3px solid #b76e79`,background:'rgba(183,110,121,0.08)'}}/></div>;
    case 'v40': return <div style={{...box, background:'#fdf6e3', border:`2px solid rgba(184,151,90,0.6)`}}><div style={{position:'absolute',top:2,left:2,width:8,height:8,borderTop:`1px solid #b8975a`,borderLeft:`1px solid #b8975a`}}/><div style={{position:'absolute',bottom:2,right:2,width:8,height:8,borderBottom:`1px solid #b8975a`,borderRight:`1px solid #b8975a`}}/><div style={{fontSize:14,color:'#3d1020',fontWeight:'bold',fontFamily:'Cinzel,serif'}}>✦</div></div>;
    // Restaurantes
    case 'v46': return <div style={{...box, background:'#fef9f0', border:`2px solid #f9b8c4`}}><div style={{position:'absolute',top:4,left:8,fontSize:14}}>🎂</div><div style={{position:'absolute',bottom:4,right:6,fontSize:11}}>🧁</div></div>;
    case 'v55': return <div style={{...box, background:'#2b5c44', position:'relative', overflow:'hidden'}}><div style={{position:'absolute',inset:0,backgroundImage:'radial-gradient(circle,rgba(232,160,180,.12) 1px,transparent 1px)',backgroundSize:'8px 8px'}}/><div style={{position:'absolute',top:'50%',left:'50%',transform:'translate(-50%,-50%)'}}><svg width="22" height="22" viewBox="0 0 22 22" fill="none"><ellipse cx="11" cy="11" rx="3" ry="9" stroke="#e8a0b4" strokeWidth="1" opacity=".7"/><ellipse cx="11" cy="11" rx="3" ry="9" stroke="#e8a0b4" strokeWidth="1" opacity=".6" transform="rotate(60 11 11)"/><ellipse cx="11" cy="11" rx="3" ry="9" stroke="#e8a0b4" strokeWidth="1" opacity=".6" transform="rotate(120 11 11)"/><circle cx="11" cy="11" r="2.5" fill="#e8a0b4" opacity=".8"/></svg></div></div>;
    // Belleza
    case 'v48': return <div style={{...box, background:'#0c0808'}}><div style={{position:'absolute',inset:0,background:'linear-gradient(135deg,rgba(201,96,122,.2),rgba(212,170,112,.15))'}}/><div style={{fontSize:16}}>💄</div></div>;
    case 'v49': return <div style={{...box, background:'#fdfaf8', border:`1px solid rgba(212,104,126,.2)`}}><div style={{position:'absolute',inset:0,background:'radial-gradient(circle at 30% 30%,rgba(247,224,228,.8),transparent)'}}/><div style={{fontSize:16}}>💆</div></div>;
    // Salud
    case 'v42': return <div style={{...box, background:'#faf7f2'}}><div style={{width:22,height:22,borderRadius:'50%',border:`2px solid #7a9e7e`,display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>🌿</div></div>;
    case 'v43': return <div style={{...box, background:'#0d0d0d', border:`2px solid #e8f325`}}><div style={{color:'#e8f325',fontFamily:'system-ui',fontWeight:900,fontSize:11,letterSpacing:-1}}>GYM</div></div>;
    case 'v50': return <div style={{...box, background:'#f0f8ff', border:`1px solid rgba(27,79,138,.2)`}}><div style={{width:20,height:20,borderRadius:6,background:'linear-gradient(135deg,#1b4f8a,#4a9fd4)',display:'flex',alignItems:'center',justifyContent:'center',fontSize:10}}>🦷</div></div>;
    // Mascotas
    case 'v51': return <div style={{...box, background:'#fff9f5', border:`2px solid #FF6B6B`, padding:3, display:'grid', gridTemplateColumns:'1fr 1fr', gap:2}}><div style={{background:'#FF6B6B30',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>🐶</div><div style={{background:'#00AEEF30',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>🐱</div><div style={{background:'#FFE04A50',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>🐾</div><div style={{background:'#6DD67D30',borderRadius:2,display:'flex',alignItems:'center',justifyContent:'center',fontSize:9}}>🦴</div></div>;
    // Negocios
    case 'v47': return <div style={{...box, background:'#0a1628', border:`1px solid rgba(201,168,76,.25)`}}><div style={{position:'absolute',top:4,left:4,width:8,height:8,borderTop:'1px solid #c9a84c',borderLeft:'1px solid #c9a84c'}}/><div style={{position:'absolute',bottom:4,right:4,width:8,height:8,borderBottom:'1px solid #c9a84c',borderRight:'1px solid #c9a84c'}}/><div style={{fontSize:12,opacity:.6}}>✦</div></div>;
    // 3D
    case 'v54': return <div style={{...box, background:'#050812', border:`1px solid rgba(0,229,255,.3)`, overflow:'hidden', position:'relative'}}>
      <div style={{position:'absolute',inset:0,backgroundImage:'linear-gradient(rgba(0,229,255,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(0,229,255,.05) 1px,transparent 1px)',backgroundSize:'8px 8px'}}/>
      <div style={{position:'absolute',top:'50%',left:'50%',transform:'translate(-50%,-50%)',width:18,height:18,border:'1px solid #00e5ff',opacity:.6,display:'flex',alignItems:'center',justifyContent:'center'}}>
        <span style={{fontFamily:'Orbitron,monospace',fontSize:7,color:'#00e5ff',fontWeight:700}}>3D</span>
      </div>
      <div style={{position:'absolute',top:2,right:2,width:5,height:5,borderTop:'1px solid #00e5ff',borderRight:'1px solid #00e5ff'}}/>
      <div style={{position:'absolute',bottom:2,left:2,width:5,height:5,borderBottom:'1px solid #00e5ff',borderLeft:'1px solid #00e5ff'}}/>
    </div>;
    // Inmobiliaria Swiss
    case 'v60': return <div style={{...box, background:'#ffffff', border:'3px solid #111', borderTop:'3px solid #111'}}>
      <div style={{position:'absolute',top:0,left:0,right:0,height:'4px',background:'#E63946'}}/>
      <div style={{position:'absolute',bottom:6,left:4,right:4,display:'flex',flexDirection:'column',gap:2}}>
        <div style={{height:2,background:'#111',width:'100%'}}/>
        <div style={{height:2,background:'#E63946',width:'70%'}}/>
        <div style={{height:2,background:'#ccc',width:'50%'}}/>
      </div>
    </div>;
    // Vaporwave
    case 'v61': return <div style={{...box, background:'#0d0015', overflow:'hidden', position:'relative'}}>
      <div style={{position:'absolute',inset:0,background:'linear-gradient(to bottom,transparent 50%,rgba(183,0,255,.2) 70%,rgba(255,45,120,.3) 85%,rgba(0,245,255,.15) 100%)'}}/>
      <div style={{position:'absolute',bottom:0,left:0,right:0,height:'50%',backgroundImage:'linear-gradient(rgba(255,45,120,.5) 1px,transparent 1px),linear-gradient(90deg,rgba(255,45,120,.5) 1px,transparent 1px)',backgroundSize:'8px 8px',transform:'perspective(20px) rotateX(8deg)',transformOrigin:'50% 0%'}}/>
      <div style={{position:'relative',zIndex:1,fontFamily:'monospace',fontSize:8,color:'#ff2d78',fontWeight:700,letterSpacing:1,textShadow:'0 0 6px #ff2d78'}}>VW</div>
    </div>;
    case 'v56': return <div style={{...box, background:v.bg, border:`1px solid #4a382a`}}><div style={{width:'100%',height:'100%',background:`repeating-linear-gradient(45deg,transparent 0,transparent 4px,${v.color}22 4px,${v.color}22 5px)`}}/></div>;
    case 'v57': return <div style={{...box, background:'#0f0a06', border:`1px solid #2a1e10`}}><div style={{fontSize:16,color:'#c4a55a'}}>✦</div></div>;
    case 'v58': return <div style={{...box, background:'linear-gradient(160deg, #3d1c02, #c26a2d)'}}><div style={{width:20,height:20,borderRadius:'50%',background:'rgba(255,255,255,0.1)',backdropFilter:'blur(2px)'}}/></div>;
    case 'v59': return <div style={{...box, background:'#080808', border:`1px solid #333`}}><div style={{width:1,height:12,background:v.color,margin:'0 auto'}}/></div>;
    case 'v62': return <div style={{...box, background:v.bg, border:'2px solid #111', padding:3, display:'grid', gridTemplateColumns:'1fr 1fr', gap:2}}><div style={{background:v.color,borderRadius:2}}/><div style={{background:'#00CFC8',borderRadius:2}}/><div style={{background:'#0050FF',borderRadius:2,gridColumn:'span 2'}}/></div>;
    case 'v63': return <div style={{...box, background:'#0d1117', border:'1px solid #30363d', padding:6, display:'flex', alignItems:'flex-end', gap:2}}><div style={{flex:1,height:'40%',background:v.color}}/><div style={{flex:1,height:'70%',background:v.color}}/><div style={{flex:1,height:'55%',background:v.color}}/></div>;
    case 'v64': return <div style={{...box, background:'#080808', border:'1px solid #333', padding:6, display:'flex', alignItems:'flex-end', gap:2}}><div style={{flex:1,height:'30%',background:v.color}}/><div style={{flex:1,height:'80%',background:v.color}}/><div style={{flex:1,height:'50%',background:v.color}}/></div>;
    case 'v65': return <div style={{...box, background:'#000', border:`2px solid ${v.color}`, display:'flex', alignItems:'center', justifyContent:'center'}}><div style={{width:'100%',height:6,background:'#ff0099',transform:'rotate(-15deg)'}}/></div>;
    case 'v66': return <div style={{...box, background:'linear-gradient(45deg,#ff007f,#00ffff,#ffff00)'}}><div style={{width:'60%',height:'60%',background:'rgba(255,255,255,0.4)',backdropFilter:'blur(2px)',borderRadius:4}}/></div>;
    case 'v67': return <div style={{...box, background:'#f4f1e9', border:'1px solid #111', overflow:'hidden'}}><div style={{position:'absolute',top:-4,left:-4,width:20,height:20,borderRadius:'50%',background:'#e83f2c',mixBlendMode:'multiply'}}/><div style={{position:'absolute',bottom:-4,right:-4,width:20,height:20,background:'#2864c6',mixBlendMode:'multiply'}}/></div>;
    case 'v68': return <div style={{...box, background:'#050505', border:`2px solid #FF00FF`, boxShadow:`0 0 10px #FF00FF`}}><div style={{width:10,height:10,background:'#8BFF00'}}/></div>;
    case 'v69': return <div style={{...box, background:'#1a0b2e'}}><div style={{width:18,height:18,borderRadius:'50%',background:'#ff3366',filter:'blur(4px)'}}/></div>;
    case 'v70': return <div style={{...box, background:'radial-gradient(circle at 100% 0%,#00ffcc,transparent), radial-gradient(circle at 0% 100%,#ff00ff,transparent), #001b44'}} />
    case 'v71': return <div style={{...box, background:'#140a04', border:`1px solid #c49a3c40`}}><div style={{fontSize:16}}>☕</div></div>;
    case 'v72': return <div style={{...box, background:'#f0ede6', border:'3px solid #000', position:'relative', overflow:'hidden'}}>
      <div style={{position:'absolute',inset:0,backgroundImage:'linear-gradient(to right,rgba(0,0,0,0.15) 1px,transparent 1px),linear-gradient(to bottom,rgba(0,0,0,0.15) 1px,transparent 1px)',backgroundSize:'9px 9px'}}/>
      <div style={{fontFamily:'sans-serif',fontWeight:900,fontSize:11,color:'#000',letterSpacing:'-0.02em',position:'relative',zIndex:1}}>BR</div>
    </div>;
    case 'v73': return <div style={{...box, background:'#06040f', border:`1px solid rgba(212,175,55,0.3)`, position:'relative', overflow:'hidden'}}>
      <div style={{position:'absolute',inset:0,backgroundImage:'linear-gradient(45deg,rgba(212,175,55,0.03) 25%,transparent 25%),linear-gradient(-45deg,rgba(212,175,55,0.03) 25%,transparent 25%)',backgroundSize:'8px 8px'}}/>
      <div style={{fontFamily:'serif',fontSize:14,color:'rgba(212,175,55,0.7)',position:'relative',zIndex:1}}>✦</div>
    </div>;
    case 'v74': return <div style={{...box, background:'linear-gradient(135deg,rgba(0,229,160,0.3),rgba(139,92,246,0.3),#020b18)', border:'1px solid rgba(0,229,160,0.2)', position:'relative', overflow:'hidden'}}>
      <div style={{position:'absolute',top:6,left:6,width:14,height:14,borderRadius:'50%',background:'rgba(0,229,160,0.5)',filter:'blur(5px)'}}/>
      <div style={{position:'absolute',bottom:6,right:6,width:10,height:10,borderRadius:'50%',background:'rgba(139,92,246,0.6)',filter:'blur(4px)'}}/>
    </div>;
    case 'v75': return <div style={{...box, background:'#071a0e', border:'1px solid rgba(125,201,110,0.25)', position:'relative', overflow:'hidden'}}>
      <div style={{position:'absolute',inset:0,background:'radial-gradient(circle at 70% 30%,rgba(45,125,74,0.4),transparent)'}}/>
      <div style={{position:'absolute',top:'50%',left:'50%',transform:'translate(-50%,-50%)',fontSize:16}}>🌿</div>
    </div>;
    case 'v76': return <div style={{...box, background:'#080604', border:'1px solid rgba(232,160,32,0.35)', position:'relative', overflow:'hidden'}}>
      <div style={{position:'absolute',inset:0,background:'radial-gradient(ellipse at 60% 40%,rgba(200,112,0,0.45),transparent)'}}/>
      <div style={{position:'absolute',bottom:0,left:0,right:0,height:'2px',background:'linear-gradient(to right,#8b4800,#e8a020,#f5b840)'}}/>
      <div style={{fontFamily:'serif',fontStyle:'italic',fontSize:9,color:'rgba(245,184,64,0.9)',fontWeight:700,position:'relative',letterSpacing:'0.05em'}}>V12</div>
    </div>;
    default: return <div style={{...box, background: v.bg, border: `1px solid ${v.color}`, color: v.color, fontSize: 14, fontWeight: 'bold'}}>?</div>;
  }
}

// ─── Callout apuntando al botón ─────────────────────────────────────────────
function ThemeCallout({ visible }) {
  return (
    <div style={{
      position: 'fixed', bottom: 76, left: 20, zIndex: 9998,
      maxWidth: 250,
      opacity: visible ? 1 : 0,
      transform: visible ? 'translateY(0) scale(1)' : 'translateY(6px) scale(0.97)',
      transition: 'opacity 0.4s ease, transform 0.4s ease',
      pointerEvents: 'none',
    }}>
      <div style={{
        background: '#1a1a2e', color: '#fff',
        borderRadius: 14, padding: '13px 16px',
        fontSize: 12, lineHeight: 1.55,
        boxShadow: '0 8px 32px rgba(0,0,0,0.45)',
        border: '1px solid rgba(255,255,255,0.1)',
        position: 'relative',
        fontFamily: '"JetBrains Mono", ui-monospace, monospace',
      }}>
        <span style={{ marginRight: 6 }}>👆</span>
        ¡Mira las distintas opciones web que tenemos para ti!
        <div style={{ position:'absolute', bottom:-8, left:28, width:0, height:0, borderLeft:'8px solid transparent', borderRight:'8px solid transparent', borderTop:'8px solid #1a1a2e' }} />
      </div>
    </div>
  );
}

// ─── ClipCutRedirect — registra la tarjeta ClipCut y la hace clickeable ─────
// Busca en el DOM la tarjeta que contiene "clipcut.xyz" y sólo ESA tarjeta
// redirige al hacer click. El resto de tarjetas de proyectos queda intacto.
function useClipCutRedirect() {
  useEffect(() => {
    const MARKER = 'clipcut.xyz';
    const URL    = 'https://clipcut.xyz';
    const cards  = new Set();
    let scanTimer = null;

    // a partir del elemento más estrecho que contiene "clipcut.xyz",
    // sube hasta el límite de la tarjeta (cuando el padre crece mucho = hermanos).
    const findCardBoundary = (leaf) => {
      let cur = leaf;
      while (cur && cur.parentElement && cur.parentElement !== document.body) {
        const parent   = cur.parentElement;
        const curLen   = (cur.innerText || '').length;
        const parentLen = (parent.innerText || '').length;
        // si el padre es >1.6x más grande, cur es la tarjeta
        if (parentLen > curLen * 1.6 + 80) break;
        // si el padre ya es demasiado grande (sección con varias tarjetas), paramos
        if (parentLen > 800) break;
        cur = parent;
      }
      return cur;
    };

    const clearPointers = () => {
      cards.forEach(el => {
        try {
          el.style.cursor = '';
          el.removeAttribute('title');
          delete el.dataset._ccCard;
        } catch (_) {}
      });
      cards.clear();
    };

    const scan = () => {
      clearPointers();
      const all = document.body.querySelectorAll('*:not(script):not(style)');
      const leaves = [];
      all.forEach(el => {
        const txt = el.innerText || '';
        if (!txt.includes(MARKER)) return;
        // sólo el contenedor más estrecho (ningún hijo lo contiene)
        const child = Array.from(el.children).find(c => (c.innerText || '').includes(MARKER));
        if (child) return;
        leaves.push(el);
      });
      leaves.forEach(leaf => {
        const card = findCardBoundary(leaf);
        if (!card || cards.has(card)) return;
        cards.add(card);
        card.dataset._ccCard = '1';
        card.style.cursor = 'pointer';
        card.title = 'Abrir clipcut.xyz — Editor de video online gratuito';
      });
    };

    const handler = (e) => {
      // buscamos si el click está dentro de alguna de las tarjetas registradas
      let inCard = null;
      for (const card of cards) {
        if (card.contains(e.target)) { inCard = card; break; }
      }
      if (!inCard) return;
      // respetar elementos interactivos reales dentro de la tarjeta
      let n = e.target;
      while (n && n !== inCard) {
        if ((n.tagName === 'A' && n.href) || n.tagName === 'BUTTON' || n.tagName === 'INPUT' || n.tagName === 'SELECT' || n.tagName === 'TEXTAREA') return;
        n = n.parentElement;
      }
      e.preventDefault();
      e.stopPropagation();
      window.open(URL, '_blank', 'noopener,noreferrer');
    };

    const scheduleScan = () => {
      if (scanTimer) return;
      scanTimer = setTimeout(() => { scanTimer = null; scan(); }, 120);
    };

    const mo = new MutationObserver(scheduleScan);
    mo.observe(document.body, { childList: true, subtree: true, characterData: true });
    scan();
    document.addEventListener('click', handler, true);

    return () => {
      document.removeEventListener('click', handler, true);
      mo.disconnect();
      if (scanTimer) clearTimeout(scanTimer);
      clearPointers();
    };
  }, []);
}

// ─── ThemeSwitcher con categorías ───────────────────────────────────────────
function ThemeSwitcher({ variants, current, setVariant, onOpenChange, categories, secretsUnlocked }) {
  const [open, setOpen] = useState(false);
  const wrapperRef = useRef(null);
  const varMap = Object.fromEntries(variants.map(v => [v.id, v]));
  const currentVar = varMap[current] || variants[0];

  useEffect(() => {
    if (!open) return;
    const handleClick = (e) => {
      if (!wrapperRef.current?.contains(e.target)) {
        setOpen(false);
        onOpenChange?.(false);
      }
    };
    document.addEventListener('mousedown', handleClick);
    return () => document.removeEventListener('mousedown', handleClick);
  }, [open]);

  const toggle = () => {
    const next = !open;
    setOpen(next);
    onOpenChange?.(next);
  };

  return (
    <div ref={wrapperRef} style={{ position:'fixed', bottom:20, left:20, zIndex:9999, fontFamily:'"JetBrains Mono", ui-monospace, monospace' }}>
      <style>{`
        .sw-scroll::-webkit-scrollbar { width: 4px; }
        .sw-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 4px; }
        .sw-panel {
          position: absolute; bottom: calc(100% + 12px); left: 0;
          transition: opacity 0.2s ease, transform 0.3s cubic-bezier(0.175,0.885,0.32,1.275);
          transform-origin: bottom left;
        }
        @media (max-width: 600px) {
          .sw-panel { width: calc(100vw - 40px) !important; }
          .sw-grid  { grid-template-columns: repeat(4, 1fr) !important; }
        }
        @media (max-width: 400px) {
          .sw-grid  { grid-template-columns: repeat(3, 1fr) !important; }
        }
        @keyframes swBadgePop {
          0%   { transform: scale(0) rotate(-20deg); opacity:0 }
          60%  { transform: scale(1.3) rotate(8deg);  opacity:1 }
          100% { transform: scale(1) rotate(0deg);    opacity:1 }
        }
        @keyframes swSecretGlow {
          0%,100% { opacity:.7 } 50% { opacity:1 }
        }
        .sw-secret-badge {
          display:inline-flex; align-items:center; justify-content:center;
          width:17px; height:17px; border-radius:50%;
          background:#ff2070; color:#fff; font-size:11px; font-weight:900;
          animation: swBadgePop .4s cubic-bezier(.175,.885,.32,1.275) both;
          box-shadow: 0 0 8px #ff207080;
          flex-shrink:0;
        }
        .sw-secrets-header { animation: swSecretGlow 2s ease infinite; }
      `}</style>

      {/* Panel */}
      <div className="sw-panel" style={{ opacity: open ? 1 : 0, pointerEvents: open ? 'auto' : 'none', transform: open ? 'translateY(0) scale(1)' : 'translateY(10px) scale(0.95)' }}>
        <div className="sw-scroll" style={{ background:'rgba(12,12,16,0.97)', backdropFilter:'blur(18px)', WebkitBackdropFilter:'blur(18px)', border:'1px solid rgba(255,255,255,0.13)', borderRadius:16, width:360, maxHeight:'80vh', overflowY:'auto', overflowX:'hidden', boxShadow:'0 12px 44px rgba(0,0,0,0.55)', paddingBottom:8 }}>

          <div style={{ padding:'14px 16px 6px', color:'#777', fontSize:9, letterSpacing:2, textTransform:'uppercase', borderBottom:'1px solid rgba(255,255,255,0.06)', marginBottom:4 }}>
            Seleccionar temática — {categories.reduce((n,c) => n + c.ids.length, 0)} opciones
          </div>

          {categories.map(cat => {
            const isSecret = cat.id === 'secrets';
            const catVars = cat.ids.map(id => varMap[id]).filter(Boolean);
            return (
              <div key={cat.id} style={ isSecret ? { borderTop:'1px solid rgba(255,32,112,.2)', marginTop:4, background:'rgba(255,32,112,.03)' } : {} }>
                {/* Category header */}
                <div style={{ padding:'10px 16px 6px', display:'flex', alignItems:'center', gap:10 }}>
                  <div style={{ flex:1, height:1, background: isSecret ? 'rgba(255,32,112,.2)' : 'rgba(255,255,255,0.05)' }} />
                  <span className={isSecret ? 'sw-secrets-header' : ''} style={{ fontSize:9, letterSpacing:1, color: isSecret ? '#ff2070' : '#555', whiteSpace:'nowrap', textTransform:'uppercase' }}>{cat.label}</span>
                  <div style={{ flex:1, height:1, background: isSecret ? 'rgba(255,32,112,.2)' : 'rgba(255,255,255,0.05)' }} />
                </div>

                {/* Variant grid */}
                <div className="sw-grid" style={{ padding:'0 12px 4px', display:'grid', gridTemplateColumns:'repeat(5, 1fr)', gap:6 }}>
                  {catVars.map(v => (
                    <button
                      key={v.id}
                      title={`${v.label} — ${v.hint}`}
                      onClick={() => { setVariant(v.id); setOpen(false); onOpenChange?.(false); }}
                      style={{
                        aspectRatio:'1 / 1',
                        background: v.bg, color: v.color,
                        border: current === v.id ? `2px solid ${v.color}` : '1px solid rgba(255,255,255,0.08)',
                        borderRadius:8, padding:4, cursor:'pointer',
                        display:'flex', alignItems:'center', justifyContent:'center',
                        transition:'transform 0.15s, box-shadow 0.15s',
                        boxShadow: current === v.id ? `0 0 14px ${v.color}50` : 'none',
                      }}
                      onMouseEnter={e => e.currentTarget.style.transform = 'translateY(-3px)'}
                      onMouseLeave={e => e.currentTarget.style.transform = 'translateY(0)'}
                    >
                      <MiniPreview v={v} />
                    </button>
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {/* Main button */}
      <button
        onClick={toggle}
        style={{
          background: currentVar.bg, color: currentVar.color,
          border: `1px solid ${currentVar.color}60`,
          borderRadius:30, padding:'10px 20px', cursor:'pointer',
          display:'flex', alignItems:'center', gap:10,
          boxShadow:'0 4px 15px rgba(0,0,0,0.3)',
          transition:'all 0.3s', fontSize:13, fontWeight:600,
        }}
      >
        <div style={{ width:10, height:10, borderRadius:'50%', background:currentVar.color, boxShadow:`0 0 8px ${currentVar.color}` }} />
        {currentVar.label}
        <span style={{ opacity:0.6, fontSize:10 }}>({currentVar.id})</span>
        {secretsUnlocked && <span className="sw-secret-badge">!</span>}
      </button>
    </div>
  );
}

// ─── App ────────────────────────────────────────────────────────────────────
function App() {
  const [lang, setLang] = useState('es');
  const [variant, setVariant] = useState('v19');
  const [calloutActive, setCalloutActive] = useState(true);
  const [dropdownOpen, setDropdownOpen] = useState(false);
  const [secretsUnlocked, setSecretsUnlocked] = useState(false);
  const [variantLoading, setVariantLoading] = useState(false);

  useClipCutRedirect();

  // Reinicia el timer de 20s cada vez que cambia la versión
  useEffect(() => {
    setCalloutActive(true);
    const t = setTimeout(() => setCalloutActive(false), 7000);
    return () => clearTimeout(t);
  }, [variant]);

  // Konami code: ↑ ↑ ↓ ↓ ← → ← →
  useEffect(() => {
    const KONAMI = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight'];
    let pos = 0;
    const handler = (e) => {
      if (e.key === KONAMI[pos]) {
        pos++;
        if (pos === KONAMI.length) {
          pos = 0;
          if (!secretsUnlocked) {
            setSecretsUnlocked(true);
          }
        }
      } else {
        pos = e.key === KONAMI[0] ? 1 : 0;
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [secretsUnlocked]);

  const activeCategories = secretsUnlocked ? [...CATEGORIES, SECRET_CATEGORY] : CATEGORIES;
  const showCallout = calloutActive && !dropdownOpen;

  // Lazy-load variant on demand usando el loader definido en index.html
  const handleSetVariant = (id) => {
    const entry = VARIANT_FILES[id];
    if (!entry) return;
    const [src, globalName] = entry;
    if (window[globalName]) { setVariant(id); return; }
    setVariantLoading(true);
    window.loadVariantFile(src).then(() => {
      setVariantLoading(false);
      setVariant(id);
    });
  };

  const Current = window[VARIANT_FILES[variant]?.[1]] || window.V19Comic;

  return (
    <div style={{ position: 'relative' }}>
      <div style={{ minHeight: '100vh', overflow: 'auto' }}>
        <Current data={data} lang={lang} setLang={setLang} />
      </div>
      {variantLoading && (
        <div style={{ position:'fixed', inset:0, zIndex:9998, display:'flex', alignItems:'center', justifyContent:'center', background:'rgba(0,0,0,.25)', backdropFilter:'blur(3px)' }}>
          <div style={{ width:30, height:30, border:'3px solid rgba(255,255,255,.15)', borderTop:'3px solid rgba(255,255,255,.85)', borderRadius:'50%', animation:'_vspin .65s linear infinite' }}/>
        </div>
      )}
      <ThemeCallout visible={showCallout} />
      <ThemeSwitcher
        variants={VARIANTS}
        current={variant}
        setVariant={handleSetVariant}
        onOpenChange={setDropdownOpen}
        categories={activeCategories}
        secretsUnlocked={secretsUnlocked}
      />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
