/* PANEL SAAS · Orgullo Millonario — React + Tailwind · Liquid Glass
   Conectado a OM-DB: lo que se edita aquí se refleja EN VIVO en la página principal (site.html). */
const {useState,useEffect,useRef,useMemo,useSyncExternalStore,createContext,useContext}=React;
const useDB=()=>useSyncExternalStore(OMDB.subscribe,OMDB.get);

/* ============ ICONS ============ */
const I={
  dash:<path d="M3 3h7v9H3zM14 3h7v5h-7zM14 12h7v9h-7zM3 16h7v5H3z"/>,
  card:<><rect x="4" y="3" width="12" height="16" rx="2"/><path d="M8 21h12V8"/></>,
  squad:<><circle cx="9" cy="8" r="3"/><circle cx="17" cy="10" r="2.5"/><path d="M3 20a6 6 0 0 1 12 0M13 20a5 5 0 0 1 8-4"/></>,
  spark:<path d="m13 2-8 12h6l-2 8 8-12h-6l2-8Z"/>,
  gift:<><path d="M4 9h16v10H4z"/><path d="M4 9l2-5 6 3 6-3 2 5M12 9v10"/></>,
  heart:<path d="M12 21C7 17 3 13.5 3 9.5A4.5 4.5 0 0 1 12 7a4.5 4.5 0 0 1 9 2.5c0 4-4 7.5-9 11.5Z"/>,
  play:<><rect x="2" y="4" width="20" height="16" rx="3"/><path d="m10 8 6 4-6 4V8Z"/></>,
  chat:<path d="M21 12a8 8 0 0 1-8 8H4l2-3a8 8 0 1 1 15-5Z"/>,
  grid:<><rect x="3" y="3" width="8" height="8" rx="2"/><rect x="13" y="3" width="8" height="8" rx="2"/><rect x="3" y="13" width="8" height="8" rx="2"/><rect x="13" y="13" width="8" height="8" rx="2"/></>,
  bell:<path d="M6 9a6 6 0 1 1 12 0c0 5 2 6 2 6H4s2-1 2-6M10 19a2 2 0 0 0 4 0"/>,
  search:<><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></>,
  x:<path d="M6 6l12 12M18 6 6 18"/>,
  down:<path d="M4 4v12h12M8 10l4 4 6-8"/>,
  eye:<><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6-10-6-10-6Z"/><circle cx="12" cy="12" r="2.5"/></>,
  drag:<><circle cx="9" cy="6" r="1.4"/><circle cx="15" cy="6" r="1.4"/><circle cx="9" cy="12" r="1.4"/><circle cx="15" cy="12" r="1.4"/><circle cx="9" cy="18" r="1.4"/><circle cx="15" cy="18" r="1.4"/></>,
  users:<><circle cx="9" cy="8" r="3.2"/><path d="M3 20a6 6 0 0 1 12 0"/><path d="M16 5.5a3 3 0 0 1 0 5.8M21 20a5.5 5.5 0 0 0-4-5.3"/></>,
  trophy:<><path d="M7 4h10v4a5 5 0 0 1-10 0V4Z"/><path d="M7 6H4v1a3 3 0 0 0 3 3M17 6h3v1a3 3 0 0 1-3 3M9 20h6M12 14v6"/></>,
  video:<><rect x="2" y="5" width="14" height="14" rx="3"/><path d="m16 10 6-3v10l-6-3"/></>,
  plus:<path d="M12 5v14M5 12h14"/>,
  trash:<><path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 13h10l1-13"/></>,
};
const Ic=({n,s=18,c=""})=><svg viewBox="0 0 24 24" width={s} height={s} className={c} fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">{I[n]}</svg>;

/* ============ SHIRT 360 ============ */
function Shirt360({front,back,s=110}){
  const [ang,setAng]=useState(0);const drag=useRef(null);const auto=useRef(true);const resume=useRef();
  useEffect(()=>{let last=performance.now(),raf;const loop=t=>{const dt=t-last;last=t;if(auto.current)setAng(a=>(a+dt*0.05)%360);raf=requestAnimationFrame(loop);};raf=requestAnimationFrame(loop);return()=>cancelAnimationFrame(raf);},[]);
  const down=e=>{auto.current=false;clearTimeout(resume.current);drag.current={x:e.clientX,a:ang};e.currentTarget.setPointerCapture&&e.currentTarget.setPointerCapture(e.pointerId);};
  const move=e=>{if(drag.current)setAng(drag.current.a+(e.clientX-drag.current.x)*0.8);};
  const up=()=>{if(drag.current){drag.current=null;resume.current=setTimeout(()=>auto.current=true,2000);}};
  return <div className="flex flex-col items-center gap-1.5 select-none">
    <div onPointerDown={down} onPointerMove={move} onPointerUp={up} onPointerCancel={up} className="relative cursor-grab active:cursor-grabbing touch-none" style={{width:s,height:s*1.25,perspective:'600px'}}>
      <div className="absolute inset-0" style={{transformStyle:'preserve-3d',transform:`rotateY(${ang}deg)`}}>
        <img src={front} draggable="false" className="absolute inset-0 w-full h-full object-contain drop-shadow-[0_10px_20px_rgba(0,0,0,.6)]" style={{backfaceVisibility:'hidden'}}/>
        <img src={back||front} draggable="false" className="absolute inset-0 w-full h-full object-contain drop-shadow-[0_10px_20px_rgba(0,0,0,.6)]" style={{backfaceVisibility:'hidden',transform:'rotateY(180deg)'}}/>
      </div>
    </div>
    <span className="font-mono text-[7px] tracking-[1px] text-mut">↺ GIRA 360°</span>
  </div>;
}

/* ============ TOAST ============ */
const ToastCtx=createContext(()=>{});
function ToastHost({children}){
  const [msg,setMsg]=useState(null);const t=useRef();
  const toast=m=>{setMsg(m);clearTimeout(t.current);t.current=setTimeout(()=>setMsg(null),2400);};
  return <ToastCtx.Provider value={toast}>{children}
    <div className={`fixed left-1/2 -translate-x-1/2 bottom-24 md:bottom-8 z-[90] transition-all duration-300 ${msg?'opacity-100 translate-y-0':'opacity-0 translate-y-4 pointer-events-none'}`}>
      <div className="glass rounded-2xl px-5 py-3 text-[13px] font-semibold border-river/40" style={{borderColor:'rgba(224,30,47,.45)'}}>{msg}</div>
    </div>
  </ToastCtx.Provider>;
}

/* ============ SHARED ============ */
const Sec=({label,children,right})=>(
  <div className="flex items-center justify-between mb-3">
    <span className="font-mono text-[9px] tracking-[2px] text-mut">{label}</span>{right}
  </div>
);
const Bar=({pct,cls})=>(
  <div className="h-[7px] rounded-full bg-white/10 overflow-hidden">
    <i className={`block h-full rounded-full ${cls}`} style={{width:pct+'%',background:cls?undefined:'linear-gradient(90deg,#8d0917,#ff2e3e)'}}/>
  </div>
);

/* ============ MODULES ============ */
function KpiModule(){
  const db=useDB();const k=db.kpis;
  const kpis=[[k.viajes,"VIAJES PUBLICADOS","#2f7df6","🚌"],[k.reservas,"RESERVAS","#22c55e","📅"],[k.socios,"SOCIOS","#ffcf3e","👤"],[k.visitas,"VISITAS TOTALES","#ff2e3e","👁"]];
  return <div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
    {kpis.map(([n,l,c,e],i)=>(
      <div key={i} className="glass rounded-3xl p-4 press cursor-default overflow-hidden">
        <div className="flex items-start justify-between"><b className="font-disp text-[30px] leading-none pr-6" style={{color:c}}>{n}</b><span className="text-[15px] opacity-90">{e}</span></div>
        <small className="font-mono text-[8px] tracking-[1.5px] text-mut block mt-2 leading-relaxed">{l}</small>
        <div className="absolute inset-x-0 bottom-0 h-[3px]" style={{background:`linear-gradient(90deg,${c},transparent)`}}/>
      </div>))}
  </div>;
}

/* ============ SITIO & HERO — controla la página principal EN VIVO ============ */
function SiteModule(){
  const db=useDB();const toast=useContext(ToastCtx);const c=db.config;
  const heroRef=useRef();
  const [drag,setDrag]=useState(null);
  const secs=c.sections||[];
  const modular=!!c.modular;
  const [ak,setAk]=useState('');const [al,setAl]=useState('128');const [at,setAt]=useState('435');const [asn,setAsn]=useState('2025');
  const [live,setLive]=useState(()=>!!(window.OMDB&&OMDB.isLive&&OMDB.isLive()));
  const conectarAPI=async()=>{if(!ak.trim()){toast('Pegá tu API key de API-Football');return;}toast('Conectando con la API…');const ok=await OMDB.setAPI({key:ak.trim(),league:+al,season:+asn,team:+at});setLive(ok);toast(ok?'✔ Datos EN VIVO desde la API':'No se pudo conectar · sigue en DEMO');};
  const F=({label,val,onChange,mono})=>(
    <label className="block"><span className="font-mono text-[8.5px] tracking-[1.5px] text-mut block mb-1.5">{label}</span>
      <input value={val} onChange={e=>onChange(e.target.value)} className={`w-full bg-white/[.06] border border-white/10 rounded-xl px-3.5 py-2.5 text-[12px] outline-none focus:border-river/60 transition-colors ${mono?'font-mono':''}`}/></label>);
  const onHero=e=>{const f=e.target.files&&e.target.files[0];if(!f)return;if(f.size>4.5e6){toast('Imagen muy grande (máx 4.5MB)');return;}const r=new FileReader();r.onload=()=>{OMDB.set('config.hero_img',r.result);toast('Imagen del hero actualizada en el sitio');};r.readAsDataURL(f);};
  const move=(from,to)=>{if(from===to||to<0||to>=secs.length)return;OMDB.update(s=>{const a=s.config.sections;const [it]=a.splice(from,1);a.splice(to,0,it);});};
  const toggleSec=i=>OMDB.update(s=>{s.config.sections[i].on=!s.config.sections[i].on;});
  const toggleBlk=(i,j)=>OMDB.update(s=>{s.config.sections[i].blocks[j].on=!s.config.sections[i].blocks[j].on;});
  const allOn=v=>OMDB.update(s=>{s.config.sections.forEach(x=>{x.on=v;(x.blocks||[]).forEach(b=>b.on=v);});});
  return <>
    <Sec label="TODO ESTO SE REFLEJA EN VIVO EN LA PÁGINA" right={<a href="site.html" target="_blank" className="press font-mono text-[8.5px] tracking-[1px] text-cyanx border border-cyanx/40 rounded-lg px-3 py-1.5 hover:bg-cyanx/10">ABRIR SITIO ↗</a>}/>
    <div className="flex flex-col gap-3">
      <F label="TÍTULO DEL HERO" val={c.hero_titulo} onChange={v=>OMDB.set('config.hero_titulo',v)}/>
      <F label="SUBTÍTULO" val={c.hero_subtitulo} onChange={v=>OMDB.set('config.hero_subtitulo',v)}/>
      <div className="grid grid-cols-2 gap-3">
        <F label="WHATSAPP" val={c.whatsapp} onChange={v=>OMDB.set('config.whatsapp',v)} mono/>
        <F label="LINK MERCADO PAGO" val={c.mp_link} onChange={v=>OMDB.set('config.mp_link',v)} mono/>
      </div>
      <div>
        <span className="font-mono text-[8.5px] tracking-[1.5px] text-mut block mb-1.5">IMAGEN DEL HERO</span>
        <input ref={heroRef} type="file" accept="image/*" onChange={onHero} className="hidden"/>
        <div className="flex items-center gap-2.5">
          <img src={c.hero_img} alt="" className="w-14 h-14 rounded-xl object-cover border border-white/10 flex-none"/>
          <button onClick={()=>heroRef.current.click()} className="press flex-1 rounded-xl py-2.5 text-[11px] font-bold text-white flex items-center justify-center gap-2" style={{background:'linear-gradient(160deg,#2f7df6,#1b4fb0)',boxShadow:'0 8px 22px -8px rgba(47,125,246,.6)'}}>⬆ Subir imagen del hero</button>
        </div>
      </div>
      <div>
        <span className="font-mono text-[8px] tracking-[1.5px] text-mut block mb-1.5">TEMA DEL SITIO</span>
        <div className="grid grid-cols-2 gap-2">
          {[['dark','Oscuro'],['light','Claro']].map(([t,l])=>(
            <button key={t} onClick={()=>{OMDB.set('config.tema',t);toast('Tema: '+l);}} className={`press rounded-xl py-2.5 text-[12px] font-bold border transition-all ${c.tema===t?'text-white border-river':'text-mut border-white/10'}`} style={c.tema===t?{background:'linear-gradient(160deg,#ff2e3e,#8d0917)'}:{}}>{l}</button>))}
        </div>
      </div>

      {/* modularización y constructor viven en sus propias tarjetas */}

      {/* ===== DATOS REALES · API-FOOTBALL ===== */}
      <div className="pt-1">
        <Sec label="DATOS REALES · API-FOOTBALL (api-sports.io)" right={<span className={`font-mono text-[8px] tracking-[1px] px-2 py-1 rounded-lg ${live?'text-green-400 border border-green-400/40':'text-gold2 border border-gold2/40'}`}>{live?'● EN VIVO':'● DEMO'}</span>}/>
        <F label="API KEY" val={ak} onChange={setAk} mono/>
        <div className="grid grid-cols-3 gap-2 mt-2">
          <label className="block"><span className="font-mono text-[7px] tracking-[1px] text-mut block mb-1">LIGA</span><input value={al} onChange={e=>setAl(e.target.value)} className="w-full bg-white/[.06] border border-white/10 rounded-xl px-2 py-2 text-[11px] font-mono text-center outline-none focus:border-river/60"/></label>
          <label className="block"><span className="font-mono text-[7px] tracking-[1px] text-mut block mb-1">EQUIPO</span><input value={at} onChange={e=>setAt(e.target.value)} className="w-full bg-white/[.06] border border-white/10 rounded-xl px-2 py-2 text-[11px] font-mono text-center outline-none focus:border-river/60"/></label>
          <label className="block"><span className="font-mono text-[7px] tracking-[1px] text-mut block mb-1">TEMPORADA</span><input value={asn} onChange={e=>setAsn(e.target.value)} className="w-full bg-white/[.06] border border-white/10 rounded-xl px-2 py-2 text-[11px] font-mono text-center outline-none focus:border-river/60"/></label>
        </div>
        <button onClick={conectarAPI} className="press rounded-xl py-2.5 mt-2 w-full text-[12px] font-bold text-white" style={{background:'linear-gradient(160deg,#2f7df6,#1b4fb0)',boxShadow:'0 8px 22px -8px rgba(47,125,246,.6)'}}>Conectar datos en vivo</button>
        <p className="font-mono text-[7.5px] text-dim tracking-[.5px] mt-1.5">Liga 128 = Liga Profesional Argentina · Equipo 435 = River Plate · sin key funciona en DEMO</p>
      </div>

      {/* constructor movido a su propia tarjeta */}

      <button onClick={()=>{OMDB.reset();toast('Datos restablecidos a los valores de fábrica');}} className="press glass rounded-xl py-2.5 text-[11px] font-bold text-mut hover:text-ink mt-1">↺ Restablecer datos del sitio</button>
    </div>
  </>;
}

/* ============ MODULARIZACIÓN (tarjeta separada) ============ */
function ModularModule(){
  const db=useDB();const toast=useContext(ToastCtx);const modular=!!db.config.modular;
  return <>
    <Sec label="ACTIVÁ O DESACTIVÁ LA MODULARIZACIÓN DEL SITIO"/>
    <button onClick={()=>{OMDB.set('config.modular',!modular);toast(modular?'Modularización DESACTIVADA · el sitio muestra todo':'Modularización ACTIVADA · ya podés ocultar secciones');}}
      className="press w-full flex items-center gap-3 rounded-2xl px-4 py-4 border transition-all" style={{borderColor:modular?'rgba(224,30,47,.55)':'rgba(255,255,255,.1)',background:modular?'linear-gradient(160deg,rgba(255,46,62,.16),rgba(255,255,255,.02))':'rgba(255,255,255,.04)'}}>
      <span className={modular?'text-river2':'text-dim'}><Ic n="grid" s={20}/></span>
      <div className="flex-1 text-left"><b className="text-[14px] block">MODULARIZACIÓN {modular?'ACTIVA':'DESACTIVADA'}</b><small className="font-mono text-[8px] text-mut">{modular?'Lo que apagues en el Constructor se oculta del sitio':'El sitio muestra todas las secciones'}</small></div>
      <span className={`w-12 h-7 rounded-full p-[3px] transition-colors flex-none ${modular?'bg-river':'bg-white/15'}`}><i className={`block w-[22px] h-[22px] rounded-full bg-white shadow transition-transform ${modular?'translate-x-5':''}`}/></span>
    </button>
    <div className="grid grid-cols-2 gap-2 mt-3 flex-1 items-stretch">
      <div className="rounded-2xl border border-white/[.08] bg-white/[.04] p-3 flex flex-col items-center justify-center"><b className="font-disp text-[24px] block leading-none" style={{color:modular?'#22c55e':'#8a8a97'}}>{modular?'ON':'OFF'}</b><small className="font-mono text-[7px] tracking-[1px] text-mut mt-1">ESTADO</small></div>
      <a href="site.html" target="_blank" className="press rounded-2xl border border-cyanx/40 text-cyanx grid place-items-center font-mono text-[10px] tracking-[1px]">ABRIR SITIO ↗</a>
    </div>
  </>;
}

/* ============ CONSTRUCTOR DEL SITIO (3 tarjetas) ============ */
function ConstructorModule(){
  const db=useDB();const toast=useContext(ToastCtx);
  const secs=db.config.sections||[];const modular=!!db.config.modular;
  const [drag,setDrag]=useState(null);
  const move=(from,to)=>{if(from===to||to<0||to>=secs.length)return;OMDB.update(s=>{const a=s.config.sections;const [it]=a.splice(from,1);a.splice(to,0,it);});};
  const toggleSec=i=>OMDB.update(s=>{s.config.sections[i].on=!s.config.sections[i].on;});
  const toggleBlk=(i,j)=>OMDB.update(s=>{s.config.sections[i].blocks[j].on=!s.config.sections[i].blocks[j].on;});
  const allOn=v=>OMDB.update(s=>{s.config.sections.forEach(x=>{x.on=v;(x.blocks||[]).forEach(b=>b.on=v);});});
  const per=Math.max(1,Math.ceil(secs.length/3));
  const groups=[[0,secs.slice(0,per)],[per,secs.slice(per,per*2)],[per*2,secs.slice(per*2)]];
  const row=(sc,i)=>(
    <div key={sc.id} className={`rounded-2xl border transition-all ${drag===i?'border-river bg-river/10':'border-white/[.08] bg-white/[.05]'} ${sc.on?'':'opacity-50'}`}>
      <div draggable onDragStart={()=>setDrag(i)} onDragEnd={()=>setDrag(null)} onDragOver={e=>{e.preventDefault();if(drag!==null&&drag!==i){move(drag,i);setDrag(i);}}} className="flex items-center gap-2 px-2.5 py-2 cursor-grab active:cursor-grabbing">
        <span className="text-dim flex-none"><Ic n="drag" s={15}/></span>
        <b className="text-[11.5px] flex-1 min-w-0 truncate">{sc.label}</b>
        {sc.blocks&&<span className="font-mono text-[7px] text-cyanx tracking-[.5px] hidden xl:block">{sc.blocks.filter(b=>b.on).length}/{sc.blocks.length}</span>}
        <button onClick={()=>toggleSec(i)} className={`press w-7 h-7 rounded-lg grid place-items-center flex-none ${sc.on?'text-cyanx':'text-dim'}`}><Ic n="eye" s={14}/></button>
        <div className="flex flex-col flex-none">
          <button onClick={()=>move(i,i-1)} className="press w-5 h-4 grid place-items-center text-dim hover:text-ink"><Ic n="down" s={10} c="rotate-180"/></button>
          <button onClick={()=>move(i,i+1)} className="press w-5 h-4 grid place-items-center text-dim hover:text-ink"><Ic n="down" s={10}/></button>
        </div>
      </div>
      {sc.on&&sc.blocks&&sc.blocks.length>0&&<div className="flex flex-col gap-1 px-2.5 pb-2 pl-7">
        {sc.blocks.map((b,j)=>(
          <div key={b.id} className={`flex items-center gap-2 rounded-xl px-2 py-1 bg-black/25 border border-white/[.05] ${b.on?'':'opacity-45'}`}>
            <span className="text-dim text-[10px] flex-none">↳</span>
            <span className="text-[10.5px] flex-1 min-w-0 truncate">{b.label}</span>
            <button onClick={()=>toggleBlk(i,j)} className={`press w-7 h-6 rounded-lg grid place-items-center flex-none ${b.on?'text-cyanx':'text-dim'}`}><Ic n="eye" s={12}/></button>
          </div>))}
      </div>}
    </div>);
  return <>
    <Sec label={modular?'ARRASTRÁ PARA ORDENAR · OJO = MOSTRAR/OCULTAR · SE ORDENA ENTRE LAS 3 TARJETAS':'ACTIVÁ LA MODULARIZACIÓN PARA QUE APLIQUE EN EL SITIO'} right={<div className="flex gap-1.5"><button onClick={()=>allOn(true)} className="press font-mono text-[8px] tracking-[1px] text-green-400 border border-green-400/40 rounded-lg px-2 py-1">TODO ON</button><button onClick={()=>allOn(false)} className="press font-mono text-[8px] tracking-[1px] text-mut border border-white/15 rounded-lg px-2 py-1">TODO OFF</button></div>}/>
    <div className={`grid grid-cols-1 md:grid-cols-3 gap-3 flex-1 ${modular?'':'opacity-60'}`}>
      {groups.map(([base,g],gi)=>(
        <div key={gi} className="rounded-2xl border border-white/[.06] bg-black/20 p-2.5 flex flex-col gap-1.5">
          <span className="font-mono text-[7.5px] tracking-[2px] text-cyanx px-1 mb-0.5">GRUPO {gi+1}</span>
          {g.map((sc,k)=>row(sc,base+k))}
        </div>))}
    </div>
  </>;
}

function RevenueModule(){
  const [pts,setPts]=useState(()=>Array.from({length:56},(_,i)=>48+Math.sin(i/5)*13));
  const [val,setVal]=useState(24900);
  useEffect(()=>{let raf,last=performance.now(),acc=0;
    const tick=t=>{acc+=t-last;last=t;
      if(acc>110){acc=0;
        setPts(p=>{const nx=Math.max(12,Math.min(82,p[p.length-1]+(Math.random()-.48)*9));return[...p.slice(1),nx];});
        setVal(v=>Math.max(18500,Math.min(41000,v+Math.round((Math.random()-.44)*180))));}
      raf=requestAnimationFrame(tick);};
    raf=requestAnimationFrame(tick);return()=>cancelAnimationFrame(raf);},[]);
  const W=260,H=88,n=pts.length;
  const xy=i=>[(i/(n-1))*W,H-(pts[i]/94*H)];
  const line=pts.map((_,i)=>xy(i).join(',')).join(' ');
  const [hx,hy]=xy(n-1);
  const up=pts[n-1]>=pts[n-2];const col=up?'#22c55e':'#ff3b45';
  const users=(window.OMAuth&&typeof OMAuth.users==='function')?(OMAuth.users()||[]):[];
  const PLAN=[['VIP','#8b5cf6'],['Oro','#ffcf3e'],['Plata','#c0c0cb'],['Bronce','#c97b3a'],['Gratuito','#5b93ff']];
  const price={VIP:8490,ORO:4490,PLATA:2490,BRONCE:1290};const pp=x=>price[(x||'').toUpperCase()]||0;
  const dist=PLAN.map(([k,c])=>[k,c,users.filter(u=>(u.nivel||'').toUpperCase()===k.toUpperCase()).length]);
  let totU=dist.reduce((s,d)=>s+d[2],0);if(!totU){dist[0][2]=1;totU=1;}
  const R=26,CC=2*Math.PI*R;let off=0;
  const arcs=dist.filter(d=>d[2]>0).map(([k,c,v])=>{const dash=v/totU*CC;const seg={c,dash,off};off+=dash;return seg;});
  const payers=users.filter(u=>u.nivel&&!/GRAT/i.test(u.nivel));
  const fmt=x=>'$'+Math.round(x).toLocaleString('es-AR');
  return <>
    <Sec label="FACTURACIÓN · TIEMPO REAL" right={<b className="font-disp text-[16px]" style={{color:col}}>{fmt(val)}<span className="text-[10px] text-mut">/mes</span></b>}/>
    <div className="relative">
      <svg viewBox="0 0 260 88" className="w-full" preserveAspectRatio="none" style={{height:96}}>
        <defs><linearGradient id="fg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor={col} stopOpacity=".35"/><stop offset="1" stopColor={col} stopOpacity="0"/></linearGradient></defs>
        <line x1="0" y1="24" x2="260" y2="24" stroke="#22c55e" strokeWidth="1" strokeDasharray="4 4" opacity=".5"/>
        <polygon points={`0,88 ${line} 260,88`} fill="url(#fg)"/>
        <polyline points={line} fill="none" stroke={col} strokeWidth="2" strokeLinejoin="round" strokeLinecap="round"/>
        <circle cx={hx} cy={hy} r="3.4" fill={col}/>
        <circle cx={hx} cy={hy} r="6" fill="none" stroke={col}><animate attributeName="r" values="4;9;4" dur="1.4s" repeatCount="indefinite"/><animate attributeName="opacity" values=".6;0;.6" dur="1.4s" repeatCount="indefinite"/></circle>
      </svg>
      <span className="absolute top-1 left-1 font-mono text-[7px] tracking-[1px] px-1.5 py-0.5 rounded" style={{color:col,background:col+'22'}}>{up?'▲':'▼'} LIVE</span>
    </div>
    <div className="grid grid-cols-4 gap-2 mt-2 text-center">
      {[[fmt(val),"MES ACTUAL","text-gold2"],["$1.22M","META ANUAL","text-ink"],["20,41%","PROGRESO","text-green-400"],[String(payers.length||users.length),"OPERACIONES","text-ink"]].map(([b,s,cc],i)=>(
        <div key={i}><b className={`font-mono text-[11px] block ${cc}`}>{b}</b><small className="font-mono text-[6.5px] text-dim tracking-wide">{s}</small></div>))}
    </div>
    <div className="grid grid-cols-[auto_1fr] gap-4 items-center mt-4 pt-3 border-t border-white/10">
      <div className="relative w-[84px] h-[84px] flex-none">
        <svg viewBox="0 0 72 72" className="w-full h-full -rotate-90">
          <circle cx="36" cy="36" r="26" fill="none" stroke="rgba(255,255,255,.08)" strokeWidth="9"/>
          {arcs.map((a,i)=><circle key={i} cx="36" cy="36" r="26" fill="none" stroke={a.c} strokeWidth="9" strokeDasharray={`${a.dash} ${CC-a.dash}`} strokeDashoffset={-a.off}/>)}
        </svg>
        <div className="absolute inset-0 grid place-items-center"><div className="text-center"><b className="font-disp text-[18px] leading-none">{totU}</b><small className="font-mono text-[6px] text-mut block">SOCIOS</small></div></div>
      </div>
      <div className="min-w-0">
        <small className="font-mono text-[7px] tracking-[2px] text-mut block mb-1.5">SOCIOS POR PLAN</small>
        <div className="grid grid-cols-2 gap-x-3 gap-y-1">
          {dist.map(([k,c,v])=><div key={k} className="flex items-center gap-1.5"><i className="w-2 h-2 rounded-sm flex-none" style={{background:c}}/><span className="text-[10px] flex-1 truncate">{k}</span><b className="font-mono text-[10px]">{v}</b></div>)}
        </div>
      </div>
    </div>
    <div className="mt-3 flex-1 flex flex-col min-h-0">
      <small className="font-mono text-[7px] tracking-[2px] text-mut block mb-1.5">USUARIOS QUE GENERARON EL PAGO</small>
      <div className="flex flex-col gap-1 overflow-y-auto">
        {payers.length?payers.map((u,i)=>(
          <div key={i} className="flex items-center gap-2 rounded-xl px-2.5 py-1.5 bg-white/[.04] border border-white/[.06]">
            <span className="w-6 h-6 rounded-full grid place-items-center font-disp text-[10px] text-gold2 flex-none" style={{background:'radial-gradient(circle at 35% 30%,#3a3a44,#17171c)'}}>{(u.nombre||'?')[0]}</span>
            <span className="text-[11px] flex-1 truncate">{u.nombre||u.email}</span>
            <span className="font-mono text-[7px] text-mut">{u.nivel}</span>
            <b className="font-mono text-[10px] text-green-400">{fmt(pp(u.nivel))}</b>
          </div>)):<div className="text-mut text-[11px] px-1 py-2">Sin pagos registrados aún · aparecerán cuando un socio pague.</div>}
      </div>
    </div>
  </>;
}

function WorldModule(){
  const dots=useMemo(()=>{
    let seed=77;const rnd=()=>((seed=(seed*1103515245+12345)&0x7fffffff)/0x7fffffff);
    const zones=[[18,18,55,32,.5],[55,52,28,48,.6],[112,12,40,26,.5],[118,40,32,38,.55],[150,14,60,40,.5],[196,60,26,20,.45]];
    const a=[];zones.forEach(z=>{for(let i=0;i<z[2]/4;i++)for(let j=0;j<z[3]/4;j++)if(rnd()<z[4])a.push([z[0]+i*4+rnd()*2,z[1]+j*4+rnd()*2]);});return a;
  },[]);
  const hot=[[72,92,4],[70,86,2.5],[40,40,2.5],[135,32,2],[126,58,2],[170,30,2],[205,68,2]];
  return <>
    <Sec label="VISITAS EN EL MUNDO" right={<span className="flex items-center gap-2 font-mono text-[8.5px] text-green-400"><i className="w-[6px] h-[6px] rounded-full bg-green-400" style={{animation:'pulseDot 1.4s infinite'}}/>EN VIVO</span>}/>
    <svg viewBox="0 0 260 120" className="w-full">
      {dots.map((d,i)=><circle key={i} cx={d[0]} cy={d[1]} r="1" fill="rgba(255,255,255,.16)"/>)}
      {hot.map((p,i)=><g key={i}><circle cx={p[0]} cy={p[1]} r={p[2]} fill="#ff2e3e" style={{animation:'pulseDot 2s infinite'}}/><circle cx={p[0]} cy={p[1]} r={p[2]*2.2} fill="none" stroke="#ff2e3e" strokeWidth=".6" opacity=".4"/></g>)}
    </svg>
    <div className="flex justify-between items-end -mt-3">
      <div><b className="font-disp text-[26px] text-river2 leading-none">882</b><small className="font-mono text-[7px] tracking-[1px] text-mut block">VISITAS</small></div>
      <div className="text-right"><b className="font-disp text-[18px] text-cyanx leading-none">138</b><small className="font-mono text-[7px] text-mut block">HOY</small></div>
    </div>
  </>;
}

function ReferidosModule(){
  const db=useDB();const toast=useContext(ToastCtx);const r=db.referidos;
  const list=[...r.lista].sort((a,b)=>b.ganancia-a.ganancia);
  const total=list.reduce((s,x)=>s+x.ganancia,0);
  const invit=list.reduce((s,x)=>s+x.invitados,0);
  const activos=list.reduce((s,x)=>s+x.activos,0);
  const max=Math.max(...list.map(x=>x.ganancia),1);
  const fmt=n=>'$'+n.toLocaleString('es-AR');
  return <>
    <Sec label="GANANCIAS POR SOCIOS QUE INVITAN · COMISIÓN" right={<span className="font-mono text-[8.5px] text-gold2 tracking-[1px]">{r.comision_pct}% C/SOCIO</span>}/>
    <div className="grid grid-cols-3 gap-2 mb-4">
      {[[fmt(total),'GANANCIA TOTAL','text-green-400'],[invit,'INVITADOS','text-ink'],[activos,'SOCIOS ACTIVOS','text-gold2']].map(([b,s,cc],i)=>(
        <div key={i} className="bg-white/[.05] border border-white/[.07] rounded-2xl px-3 py-2.5 text-center"><b className={`font-disp text-[18px] block leading-none ${cc}`}>{b}</b><small className="font-mono text-[6.5px] tracking-[1px] text-mut block mt-1.5">{s}</small></div>))}
    </div>
    <div className="flex flex-col gap-2 flex-1">
      {list.map((x,i)=>(
        <div key={x.codigo} className="flex items-center gap-3 bg-white/[.05] border border-white/[.07] rounded-2xl px-3 py-2.5">
          <span className="w-8 h-8 rounded-full grid place-items-center font-disp font-bold text-[13px] flex-none" style={{background:i===0?'linear-gradient(150deg,#ffcf3e,#d4a437)':'radial-gradient(circle at 35% 30%,#3a3a44,#17171c)',color:i===0?'#1a1206':'#c0c0cb'}}>{i+1}</span>
          <div className="min-w-0 flex-1">
            <div className="flex items-center gap-2"><b className="text-[12px] truncate">{x.nombre}</b><span className="font-mono text-[7px] tracking-[1px] px-1.5 py-[2px] rounded bg-cyanx/10 text-cyanx flex-none">{x.codigo}</span></div>
            <div className="h-[5px] rounded-full bg-white/10 overflow-hidden mt-1.5"><i className="block h-full rounded-full" style={{width:x.ganancia/max*100+'%',background:'linear-gradient(90deg,#189e5b,#26d07c)'}}/></div>
          </div>
          <div className="text-right flex-none">
            <b className="font-mono text-[12px] text-green-400 block leading-none">{fmt(x.ganancia)}</b>
            <small className="font-mono text-[7px] text-dim">{x.invitados} inv · {x.activos} act</small>
          </div>
        </div>))}
    </div>
    <button onClick={()=>toast('Comisiones exportadas · listo para liquidar')} className="press glass rounded-2xl py-2.5 mt-3 text-[11px] font-bold text-mut hover:text-ink w-full">Liquidar comisiones del mes</button>
  </>;
}

function FiguritasModule(){
  const toast=useContext(ToastCtx);
  const RZ=[["COMÚN","#9a9aa6"],["PLATA","#c0c0cb"],["ORO","#ffcf3e"],["LEYENDA","#ff2e3e"],["HOLOGRÁFICA","#8b5cf6"],["ULTRA PREMIUM","#22d3ee"]];
  const CLUBS=[["River","assets/river.png"],["Boca","assets/boca.png"],["Barracas","assets/barracas.png"],["Racing","assets/racing.png"],["Talleres","assets/talleres.png"],["Argentinos","assets/argentinos.png"],["Gimnasia","assets/gimnasia.png"],["Rosario","assets/rosario.png"]];
  const FLAGS=[["Argentina","ar"],["Brasil","br"],["Uruguay","uy"],["Colombia","co"],["Chile","cl"],["Paraguay","py"],["Venezuela","ve"],["Perú","pe"]];
  const flagUrl=(cc,w=80)=>`https://flagcdn.com/w${w}/${cc}.png`;
  const [rz,setRz]=useState(4);
  const [name,setName]=useState("Franco Armani");
  const [num,setNum]=useState("23");
  const [pos,setPos]=useState("ARQ");
  const [club,setClub]=useState(0);
  const [flag,setFlag]=useState(0);
  const [photo,setPhoto]=useState("assets/armani.png");
  const fileRef=useRef();const addRef=useRef();const cardRef=useRef();const drag=useRef(null);
  const [sel,setSel]=useState('foto');
  const [layers,setLayers]=useState(()=>[
    {id:'foto',  label:'Foto del jugador', tipo:'foto',   x:50, y:60, w:84, on:true},
    {id:'escudo',label:'Escudo del club',  tipo:'escudo', x:15, y:11, w:20, on:true},
    {id:'flag',  label:'Bandera + año',    tipo:'flag',   x:85, y:10, w:22, on:true},
    {id:'pitch', label:'Cancha / posición',tipo:'pitch',  x:15, y:35, w:16, on:true},
  ]);
  const patch=(id,p)=>setLayers(ls=>ls.map(l=>l.id===id?{...l,...p}:l));
  const zOf=t=>t==='foto'?10:t==='custom'?24:30;
  const startDrag=(e,id)=>{e.stopPropagation();setSel(id);const r=cardRef.current.getBoundingClientRect();const L=layers.find(l=>l.id===id);drag.current={id,sx:e.clientX,sy:e.clientY,ox:L.x,oy:L.y,rw:r.width,rh:r.height};
    const mv=ev=>{const d=drag.current;if(!d)return;patch(d.id,{x:Math.max(2,Math.min(98,d.ox+(ev.clientX-d.sx)/d.rw*100)),y:Math.max(2,Math.min(98,d.oy+(ev.clientY-d.sy)/d.rh*100))});};
    const up=()=>{drag.current=null;window.removeEventListener('pointermove',mv);window.removeEventListener('pointerup',up);};
    window.addEventListener('pointermove',mv);window.addEventListener('pointerup',up);};
  const addImage=e=>{const f=e.target.files&&e.target.files[0];if(!f)return;if(f.size>4.5e6){toast('Imagen muy grande (máx 4.5MB)');return;}const r=new FileReader();r.onload=()=>{const id='u'+Date.now();setLayers(ls=>[...ls,{id,label:'Imagen '+(ls.filter(l=>l.tipo==='custom').length+1),tipo:'custom',src:r.result,x:50,y:45,w:34,on:true}]);setSel(id);toast('Imagen agregada · arrastrala en la tarjeta');};r.readAsDataURL(f);e.target.value='';};
  const removeLayer=id=>{setLayers(ls=>ls.filter(l=>l.id!==id));if(sel===id)setSel(null);};
  const srcOf=l=>l.tipo==='foto'?photo:l.tipo==='escudo'?CLUBS[club][1]:l.tipo==='flag'?flagUrl(FLAGS[flag][1],160):l.src;
  const selLayer=layers.find(l=>l.id===sel);
  const [year,setYear]=useState('2001');const [alt,setAlt]=useState('1,70');const [peso,setPeso]=useState('67');
  const [nac,setNac]=useState('03-11-1979');const [sigla,setSigla]=useState('CARP');const [posab,setPosab]=useState('AM');
  const [enr,setEnr]=useState('EN RIVER PLATE: 1996 - 2000');
  const TEX=['comun','plata','oro','leyenda','holografica','premium'];
  const tex='assets/rareza/'+TEX[rz]+'.png';
  const dark=[1,2,3].includes(rz);const tcol=dark?'#2a1c08':'#f4f4f6';const tsh=dark?'0 1px 0 rgba(255,255,255,.35)':'0 2px 6px rgba(0,0,0,.8)';
  const c=RZ[rz][1];
  const onFile=e=>{const f=e.target.files&&e.target.files[0];if(!f)return;if(f.size>4.5e6){toast('Imagen muy grande (máx 4.5MB)');return;}const r=new FileReader();r.onload=()=>{setPhoto(r.result);toast('Imagen del jugador cargada');};r.readAsDataURL(f);};
  const crear=()=>{
    if(!name.trim()){toast('Poné un nombre');return;}
    OMDB.update(s=>{s.figuritas=[{clave:'c'+Date.now(),nombre:name.trim(),tier:RZ[rz][0],img:photo,tenida:true,cant:1,num,pos,flag:FLAGS[flag][1],club:CLUBS[club][1]},...s.figuritas].slice(0,20);});
    toast(`✔ ${name} (${RZ[rz][0]}) publicada en el álbum del sitio`);
  };
  const exportPNG=async()=>{
    const W=520,H=728,cv=document.createElement('canvas');cv.width=W;cv.height=H;const x=cv.getContext('2d');
    const rr=(a,b,w,h,r)=>{x.beginPath();x.moveTo(a+r,b);x.arcTo(a+w,b,a+w,b+h,r);x.arcTo(a+w,b+h,a,b+h,r);x.arcTo(a,b+h,a,b,r);x.arcTo(a,b,a+w,b,r);x.closePath();};
    rr(0,0,W,H,40);x.clip();
    const g=x.createLinearGradient(0,0,W,H);g.addColorStop(0,c+'55');g.addColorStop(.5,'#1a1226');g.addColorStop(1,'#0b0714');x.fillStyle=g;x.fillRect(0,0,W,H);
    const load=src=>new Promise(res=>{const im=new Image();im.crossOrigin='anonymous';im.onload=()=>res(im);im.onerror=()=>res(null);im.src=src;});
    const drawImgLayer=async l=>{const cx=W*l.x/100,cy=H*l.y/100,w=W*l.w/100;const im=await load(srcOf(l));if(!im)return;const h=w*(im.height/im.width);x.drawImage(im,cx-w/2,cy-h/2,w,h);return{cx,cy,w,h};};
    const drawPitch=l=>{const cx=W*l.x/100,cy=H*l.y/100,w=W*l.w/100,h=w*4/3;x.save();rr(cx-w/2,cy-h/2,w,h,8);x.clip();const pg=x.createLinearGradient(0,cy-h/2,0,cy+h/2);pg.addColorStop(0,'#2f7a2f');pg.addColorStop(1,'#1c4d1c');x.fillStyle=pg;x.fillRect(cx-w/2,cy-h/2,w,h);x.strokeStyle='rgba(255,255,255,.4)';x.lineWidth=1.5;x.beginPath();x.moveTo(cx-w/2,cy-h/2+5);x.lineTo(cx+w/2,cy-h/2+5);x.moveTo(cx-w/2,cy+h/2-5);x.lineTo(cx+w/2,cy+h/2-5);x.stroke();x.restore();x.fillStyle='#fff';x.textAlign='center';x.textBaseline='middle';x.font='700 '+(w*0.42)+'px Oswald,sans-serif';x.shadowColor='#000';x.shadowBlur=4;x.fillText(posab,cx,cy);x.shadowBlur=0;x.textBaseline='alphabetic';};
    const vis=layers.filter(l=>l.on);
    for(const l of vis.filter(l=>zOf(l.tipo)<20)) await drawImgLayer(l);
    // dark gradient + iridiscente + streak (rareza / holográfico)
    const ov=x.createLinearGradient(0,H*0.45,0,H);ov.addColorStop(0,'rgba(0,0,0,0)');ov.addColorStop(1,'rgba(0,0,0,.95)');x.fillStyle=ov;x.fillRect(0,H*0.45,W,H*0.55);
    x.save();x.globalCompositeOperation='overlay';x.globalAlpha=.45;x.translate(W/2,H*0.42);x.rotate(-0.9);const cols=['#ff0078','#00c8ff','#78ff50','#ffdc00'];for(let i=-34;i<34;i++){x.fillStyle=cols[((i%4)+4)%4];x.fillRect(i*24,-H,12,H*2);}x.restore();
    x.save();x.globalCompositeOperation='color-dodge';x.globalAlpha=.32;x.translate(W/2,H*0.42);x.rotate(-0.9);for(let i=-34;i<34;i++){x.fillStyle=(i%2?c:'#ffffff');x.fillRect(i*18,-H,7,H*2);}x.restore();
    const hs=x.createLinearGradient(0,0,W,H);hs.addColorStop(.3,'rgba(255,255,255,0)');hs.addColorStop(.46,'rgba(255,255,255,.5)');hs.addColorStop(.5,c);hs.addColorStop(.54,'rgba(255,255,255,.4)');hs.addColorStop(.68,'rgba(255,255,255,0)');x.fillStyle=hs;x.fillRect(0,0,W,H);
    // chrome: capas superiores (escudo/flag/pitch/custom)
    for(const l of vis.filter(l=>zOf(l.tipo)>=20)){
      if(l.tipo==='pitch'){drawPitch(l);continue;}
      const d=await drawImgLayer(l);if(!d)continue;
      if(l.tipo==='flag'){x.strokeStyle='rgba(255,255,255,.7)';x.lineWidth=2.5;rr(d.cx-d.w/2,d.cy-d.h/2,d.w,d.h,8);x.stroke();x.fillStyle=tcol;x.textAlign='center';x.font='700 28px Oswald,sans-serif';x.fillText(year,d.cx,d.cy+d.h/2+26);}
      if(l.tipo==='escudo'){x.fillStyle=tcol;x.textAlign='center';x.font='700 20px Oswald,sans-serif';x.fillText(sigla,d.cx,d.cy+d.h/2+20);}
    }
    x.fillStyle='rgba(255,255,255,.14)';x.font='900 130px Oswald, sans-serif';x.textAlign='right';x.fillText(num,W-30,158);
    x.textAlign='center';x.fillStyle='#fff';x.font='700 46px Oswald, sans-serif';x.fillText(name.toUpperCase().slice(0,16),W/2,H-92);
    x.fillStyle=c;x.font='700 22px "JetBrains Mono", monospace';x.fillText(RZ[rz][0]+' · #'+num,W/2,H-54);
    x.strokeStyle=c;x.lineWidth=8;rr(4,4,W-8,H-8,36);x.stroke();
    const a=document.createElement('a');a.download=`figurita-${name.toLowerCase().replace(/\s+/g,'-')||'cromo'}.png`;a.href=cv.toDataURL('image/png');a.click();
    toast('PNG descargado ✔');
  };
  return <>
    <div className="grid grid-cols-1 lg:grid-cols-[1fr_minmax(380px,560px)] gap-5">
      <div className="flex flex-col gap-4 min-w-0">
        <div>
          <Sec label="DATOS DEL JUGADOR"/>
          <input value={name} onChange={e=>setName(e.target.value)} className="w-full bg-white/[.06] border border-white/10 rounded-2xl px-4 py-3 text-[13px] font-semibold outline-none focus:border-river/60 transition-colors" placeholder="Nombre del jugador"/>
          <div className="grid grid-cols-[1fr_1fr] gap-2 mt-2">
            <input value={num} onChange={e=>setNum(e.target.value.slice(0,3))} className="bg-white/[.06] border border-white/10 rounded-xl px-3 py-2 text-[11px] font-mono text-center outline-none focus:border-river/60 min-w-0" placeholder="N°"/>
            <select value={pos} onChange={e=>setPos(e.target.value)} className="bg-white/[.06] border border-white/10 rounded-xl px-3 py-2 text-[11px] font-mono text-center outline-none focus:border-river/60 min-w-0">{["ARQ","DEF","MED","DEL","DT"].map(p=><option key={p} className="bg-[#15151a]">{p}</option>)}</select>
          </div>
        </div>
        <div>
          <Sec label="FOTO DEL JUGADOR"/>
          <input ref={fileRef} type="file" accept="image/*" onChange={onFile} className="hidden"/>
          <div className="grid grid-cols-2 gap-2">
            <button onClick={()=>fileRef.current.click()} className="press rounded-xl py-2.5 text-[11px] font-bold text-white flex items-center justify-center gap-2" style={{background:'linear-gradient(160deg,#2f7df6,#1b4fb0)',boxShadow:'0 8px 22px -8px rgba(47,125,246,.6)'}}>⬆ Subir imagen</button>
            <button onClick={()=>{setPhoto('assets/armani.png');toast('Foto de ejemplo');}} className="press glass rounded-xl py-2.5 text-[11px] font-bold text-mut hover:text-ink">Ejemplo</button>
          </div>
          <p className="font-mono text-[7.5px] text-dim tracking-[.5px] mt-1.5">PNG recortado del jugador · fondo transparente ideal · máx 4.5MB</p>
        </div>
        <div>
          <Sec label="RAREZA / EDICIÓN"/>
          <div className="flex flex-wrap gap-2">
            {RZ.map(([r,cc],i)=>(
              <button key={r} onClick={()=>{setRz(i);}}
                className={`press rounded-full px-4 py-2 text-[10px] font-mono tracking-[1px] border transition-all ${i===rz?'text-white':'text-mut border-white/10 hover:text-ink'}`}
                style={i===rz?{borderColor:cc,background:`color-mix(in srgb, ${cc} 22%, transparent)`,boxShadow:`0 0 20px ${cc}44`}:{}}>{r}</button>))}
          </div>
        </div>
        <div className="grid grid-cols-2 gap-3">
          <div><Sec label="ESCUDO"/><div className="flex flex-wrap gap-1.5">{CLUBS.map(([n,src],i)=><button key={n} onClick={()=>setClub(i)} className={`press w-9 h-9 rounded-lg grid place-items-center bg-white/[.05] border ${i===club?'border-river':'border-white/10'}`}><img src={src} alt={n} className="w-6 h-6 object-contain"/></button>)}</div></div>
          <div><Sec label="BANDERA"/><div className="flex flex-wrap gap-1.5">{FLAGS.map((f,i)=><button key={i} onClick={()=>setFlag(i)} title={f[0]} className={`press w-9 h-9 rounded-lg grid place-items-center overflow-hidden bg-white/[.05] border ${i===flag?'border-river':'border-white/10'}`}><img src={flagUrl(f[1],40)} alt={f[0]} className="w-7 h-5 object-cover rounded-sm"/></button>)}</div></div>
        </div>
        <div>
          <Sec label="CAPAS · ARRASTRÁ EN LA TARJETA · MOSTRAR / QUITAR" right={<button onClick={()=>addRef.current.click()} className="press font-mono text-[8px] tracking-[1px] text-cyanx border border-cyanx/40 rounded-lg px-2.5 py-1 hover:bg-cyanx/10">＋ IMAGEN</button>}/>
          <input ref={addRef} type="file" accept="image/*" onChange={addImage} className="hidden"/>
          <div className="flex flex-col gap-1.5">
            {layers.map(l=>(
              <div key={l.id} onClick={()=>setSel(l.id)} className={`flex items-center gap-2 rounded-xl px-2.5 py-2 border cursor-pointer transition-colors ${sel===l.id?'border-river bg-river/10':'border-white/[.08] bg-white/[.05]'} ${l.on?'':'opacity-45'}`}>
                <span className="w-7 h-7 rounded-lg overflow-hidden grid place-items-center flex-none bg-black/30">{l.tipo==='pitch'?<span className="text-[12px]">🟩</span>:<img src={srcOf(l)} alt="" className="w-full h-full object-contain"/>}</span>
                <b className="text-[11px] flex-1 truncate">{l.label}</b>
                <button onClick={e=>{e.stopPropagation();patch(l.id,{on:!l.on});}} className={`press w-7 h-7 rounded-lg grid place-items-center flex-none ${l.on?'text-cyanx':'text-dim'}`}><Ic n="eye" s={14}/></button>
                {l.tipo==='custom'&&<button onClick={e=>{e.stopPropagation();removeLayer(l.id);}} className="press w-7 h-7 rounded-lg grid place-items-center text-dim hover:text-river2 flex-none"><Ic n="trash" s={14}/></button>}
              </div>))}
          </div>
          {selLayer&&<div className="mt-2 rounded-xl border border-white/[.08] bg-white/[.03] p-3">
            <div className="font-mono text-[8px] tracking-[1.5px] text-gold2 mb-2">EDITAR · {selLayer.label.toUpperCase()}</div>
            {[['x','X ◄ ►',0,100],['y','Y ▲ ▼',0,100],['w','TAMAÑO',6,120]].map(([k,lb,mn,mx])=>(
              <div key={k} className="flex items-center gap-3 mb-1.5"><span className="font-mono text-[8px] text-mut w-16">{lb}</span><input type="range" min={mn} max={mx} value={selLayer[k]} onChange={e=>patch(selLayer.id,{[k]:+e.target.value})} className="flex-1"/><output className="font-mono text-[9px] text-gold2 w-9 text-right">{Math.round(selLayer[k])}</output></div>))}
            <p className="font-mono text-[7px] text-dim mt-1">Tip: también podés arrastrar la imagen directamente sobre la tarjeta.</p>
          </div>}
        </div>
        <div className="grid grid-cols-2 gap-2 mt-auto">
          <button onClick={crear} className="press rounded-2xl py-3 text-[12px] font-bold text-white" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)',boxShadow:'0 10px 30px -8px rgba(224,30,47,.6)'}}>＋ Crear y publicar</button>
          <button onClick={exportPNG} className="press glass rounded-2xl py-3 text-[12px] font-bold text-mut hover:text-ink">⬇ Descargar PNG</button>
        </div>
      </div>
      <div className="flex flex-col items-center justify-center gap-3 mx-auto w-full h-full">
        <span className="font-mono text-[8px] tracking-[2px] text-mut">VISTA PREVIA EN VIVO</span>
        <div className="relative w-full max-w-[460px] aspect-[3/4.25] rounded-[18px] overflow-hidden press cursor-pointer" style={{containerType:'inline-size',boxShadow:`0 30px 70px -18px ${c}88, inset 0 1px 0 rgba(255,255,255,.3)`}}>
          <img src={tex} alt="" className="absolute inset-0 w-full h-full object-cover z-0"/>
          <div className="absolute inset-0 z-10" onPointerDown={()=>setSel(null)}/>
          {layers.filter(l=>l.on).map(l=>(
            <div key={l.id} onPointerDown={e=>startDrag(e,l.id)}
              className={`absolute cursor-grab active:cursor-grabbing ${sel===l.id?'ring-1 ring-river2/80 ring-offset-0':''}`}
              style={{left:l.x+'%',top:l.y+'%',width:l.w+'%',transform:'translate(-50%,-50%)',zIndex:zOf(l.tipo),touchAction:'none'}}>
              {l.tipo==='pitch'
                ? <div className="relative rounded-md overflow-hidden w-full" style={{aspectRatio:'3/4',background:'linear-gradient(160deg,#2f7a2f,#1c4d1c)',border:'1px solid rgba(255,255,255,.35)'}}>
                    <span className="absolute inset-0 grid place-items-center font-disp text-[12px] font-bold text-white" style={{textShadow:'0 1px 3px #000'}}>{posab}</span>
                    <span className="absolute inset-x-0 top-1 h-px bg-white/40"/><span className="absolute inset-x-0 bottom-1 h-px bg-white/40"/>
                  </div>
                : <img src={srcOf(l)} alt={l.label} draggable="false" className={`w-full object-contain pointer-events-none drop-shadow-[0_2px_6px_rgba(0,0,0,.55)] ${l.tipo==='flag'?'rounded-md border border-white/60 shadow-lg':''}`} style={l.tipo==='flag'?{aspectRatio:'3/2',objectFit:'cover'}:{}}/>}
              {l.tipo==='escudo'&&<b className="absolute left-1/2 -translate-x-1/2 font-disp text-[13px] leading-none" style={{top:'100%',color:tcol,textShadow:tsh}}>{sigla}</b>}
              {l.tipo==='flag'&&<b className="absolute left-1/2 -translate-x-1/2 font-disp text-[19px] leading-none mt-0.5" style={{top:'100%',color:tcol,textShadow:tsh}}>{year}</b>}
            </div>))}
          {/* sheen + holo */}
          <span className="absolute inset-0 pointer-events-none z-20" style={{background:`linear-gradient(115deg,transparent 40%,rgba(255,255,255,.5) 49%,transparent 58%)`,animation:'holoShine 3.4s linear infinite'}}/>
          {rz>=4&&<span className="absolute inset-0 pointer-events-none mix-blend-color-dodge opacity-70 z-20" style={{background:'repeating-linear-gradient(70deg,rgba(255,0,120,.2) 0 8px,rgba(0,200,255,.2) 8px 16px,rgba(120,255,80,.16) 16px 24px,rgba(255,220,0,.16) 24px 32px)',animation:'holoShine 7s linear infinite reverse'}}/>}
          {/* escudo + sigla */}
          {/* pitch + posición */}
          {/* flag + año */}
          {/* stats altura/peso */}
          <div className="absolute z-30 flex gap-1" style={{top:'27%',right:'5%',width:'23%'}}>
            {[['↥',alt],['KG',peso]].map(([k,v],i)=>(
              <div key={i} className="flex-1 rounded-md py-1 grid place-items-center" style={{background:dark?'rgba(0,0,0,.18)':'rgba(0,0,0,.4)',border:`1px solid ${dark?'rgba(0,0,0,.3)':'rgba(255,255,255,.25)'}`}}>
                <span className="font-mono text-[7px] leading-none" style={{color:tcol,opacity:.8}}>{k}</span>
                <b className="font-disp text-[13px] leading-none mt-0.5" style={{color:tcol}}>{v}</b>
              </div>))}
          </div>
          {/* nameplate + ribbon + footer */}
          <div className="absolute inset-x-0 bottom-0 z-30 px-[6%] pb-[3%] text-center">
            <b className="font-disp font-bold tracking-[.5px] block leading-[.9]" style={{fontSize:'clamp(14px,8cqw,34px)',color:tcol,textShadow:tsh,WebkitTextStroke:dark?'0':'0.5px rgba(0,0,0,.5)'}}>{(name||'—').toUpperCase()}</b>
            <span className="font-disp text-[13px] tracking-[1px] block leading-none mt-0.5" style={{color:tcol,opacity:.9}}>{nac}</span>
            <div className="mt-1.5 py-1 rounded-sm" style={{background:dark?'rgba(0,0,0,.22)':'rgba(0,0,0,.55)',border:`1px solid ${dark?'rgba(0,0,0,.35)':'rgba(255,255,255,.2)'}`}}>
              <span className="font-disp text-[10px] tracking-[.5px] font-bold" style={{color:tcol}}>{enr}</span>
            </div>
            <span className="font-mono text-[6px] tracking-[1px] block mt-1" style={{color:tcol,opacity:.7}}>CARP Official Collection</span>
          </div>
        </div>
      </div>
    </div>
  </>;
}

function PlantillaModule(){
  const toast=useContext(ToastCtx);const db=useDB();const f=db.formacion;
  const PRESETS={'4-3-3':{def:4,med:3,del:3},'4-4-2':{def:4,med:4,del:2},'3-5-2':{def:3,med:5,del:2},'4-2-3-1':{def:4,med:5,del:1},'3-4-3':{def:3,med:4,del:3}};
  const setEsquema=e=>OMDB.update(s=>{const p=PRESETS[e];s.formacion.esquema=e;['def','med','del'].forEach(k=>{const cur=s.formacion[k]||[];const arr=[];for(let i=0;i<p[k];i++)arr.push(cur[i]||{n:'Jugador',foto:''});s.formacion[k]=arr;});});
  const setName=(line,i,v)=>OMDB.update(s=>{if(line==='arq')s.formacion.arq.n=v;else s.formacion[line][i].n=v;});
  const upPhoto=(line,i,file)=>{if(!file)return;if(file.size>4.5e6){toast('Imagen muy grande (máx 4.5MB)');return;}const r=new FileReader();r.onload=()=>{OMDB.update(s=>{if(line==='arq')s.formacion.arq.foto=r.result;else s.formacion[line][i].foto=r.result;});toast('Rostro actualizado ✔');};r.readAsDataURL(file);};
  const pick=(line,i)=>{const inp=document.createElement('input');inp.type='file';inp.accept='image/*';inp.onchange=e=>upPhoto(line,i,e.target.files[0]);inp.click();};
  const Line=({line,label,arr,gk})=>(<div>
    <div className="font-mono text-[7.5px] tracking-[1.5px] text-river2 mt-2 mb-1">{label}</div>
    <div className="flex flex-col gap-1.5">{arr.map((p,i)=>(
      <div key={i} className="flex items-center gap-2 bg-white/[.05] border border-white/[.07] rounded-xl px-2 py-1.5">
        <button onClick={()=>pick(line,i)} className="press w-9 h-9 rounded-full overflow-hidden grid place-items-center flex-none" style={{border:gk?'2px solid #ffcf3e':'1px solid rgba(255,255,255,.18)',background:'radial-gradient(circle at 40% 30%,#2a2a33,#111116)'}}>{p.foto?<img src={p.foto} alt={p.n} className="w-full h-full object-cover object-top"/>:<Ic n="plus" s={13} c="text-dim"/>}</button>
        <input value={p.n} onChange={e=>setName(line,i,e.target.value)} className="flex-1 bg-transparent text-[11.5px] font-semibold outline-none min-w-0" placeholder="Nombre"/>
        <button onClick={()=>pick(line,i)} className="press font-mono text-[7px] tracking-[1px] text-cyanx border border-cyanx/40 rounded-lg px-2 py-1 flex-none">FOTO</button>
      </div>))}</div>
  </div>);
  return <>
    <Sec label="FORMACIÓN · SE REFLEJA EN EL SITIO EN VIVO" right={<b className="font-disp text-[18px] text-gold2">{f.esquema}</b>}/>
    <div className="flex flex-wrap gap-1.5 mb-1">{Object.keys(PRESETS).map(e=><button key={e} onClick={()=>setEsquema(e)} className={`press rounded-full px-3 py-1.5 text-[10px] font-mono border transition-all ${f.esquema===e?'text-white border-river bg-river/15':'text-mut border-white/10 hover:text-ink'}`}>{e}</button>)}</div>
    <div className="flex flex-col gap-1 flex-1 overflow-y-auto max-h-[400px] pr-1">
      <Line line="del" label="DELANTEROS" arr={f.del}/>
      <Line line="med" label="MEDIOCAMPO" arr={f.med}/>
      <Line line="def" label="DEFENSA" arr={f.def}/>
      <Line line="arq" label="ARQUERO" arr={[f.arq]} gk/>
    </div>
    <p className="font-mono text-[7.5px] text-dim tracking-[.5px] mt-2">Tocá el círculo o “FOTO” para subir el rostro · editá el nombre · cambiá el esquema arriba</p>
  </>;
}

function SponsorsModule(){
  const toast=useContext(ToastCtx);
  const tiers=[["ORO","text-gold2","rgba(212,164,55,.35)",[<img key="b" src="assets/betano.png" alt="Betano" className="h-6 object-contain"/>,"CODERE","GATORADE"]],
    ["PLATA","text-[#c0c0cb]","rgba(192,192,203,.25)",["STANLEY","KONAMI","MOSTAZA"]],
    ["BRONCE","text-[#b0763a]","rgba(176,118,58,.3)",["TIENDA RIVER","QUILMES","ASSIST CARD"]]];
  return <>
    <Sec label="MULTI-TIER"/>
    <div className="flex flex-col gap-3 flex-1">
      {tiers.map(([t,tc,bc,logos])=>(
        <div key={t}>
          <small className={`font-mono text-[8px] tracking-[2px] block mb-1.5 ${tc}`}>{t}</small>
          <div className="grid grid-cols-3 gap-2">
            {logos.map((l,i)=>(
              <span key={i} className="press h-11 rounded-2xl grid place-items-center font-disp text-[10.5px] tracking-[1px] text-mut bg-white/[.05] cursor-pointer hover:bg-white/[.09] transition-colors px-2 text-center" style={{border:`1px solid ${bc}`}}>{l}</span>))}
          </div>
        </div>))}
    </div>
    <button onClick={()=>toast('Abriendo gestor de sponsors…')} className="press glass rounded-2xl py-2.5 mt-3 text-[11px] font-bold text-mut hover:text-ink w-full">Gestionar sponsors</button>
  </>;
}

function HistoriasModule(){
  const toast=useContext(ToastCtx);
  const viajes=[["Boca Juniors vs River Plate","La Bombonera · 25/05",["BUS","ENTRADA"]],["River Plate vs Talleres","Más Monumental · 01/06",["BUS","COMIDA"]],["River Plate vs Independiente","Más Monumental · 08/06",["BUS"]]];
  return <>
    <Sec label="AI TELEGRAM PARSER" right={<span className="font-mono text-[8px] text-cyanx tracking-[1px]">⚡ IA</span>}/>
    <div className="flex flex-col gap-2">
      {viajes.map(([t,s,tags])=>(
        <div key={t} className="press bg-white/[.05] border border-white/[.07] rounded-2xl px-3.5 py-2.5 cursor-pointer hover:bg-white/[.09] transition-colors">
          <b className="text-[12px] block">{t}</b><small className="font-mono text-[8px] text-dim">{s}</small>
          <div className="flex gap-1.5 mt-1.5">{tags.map(g=><span key={g} className="font-mono text-[7px] tracking-[.5px] px-2 py-[3px] rounded-md bg-cyanx/10 text-cyanx">{g}</span>)}</div>
        </div>))}
    </div>
    <div className="font-mono text-[8.5px] leading-[1.8] text-mut bg-black/40 border border-white/[.07] rounded-2xl p-3.5 mt-3 whitespace-pre-line max-h-32 overflow-y-auto">{`VAMOS A LA BOMBONERA\nBOCA JRS VS RIVER PLATE\nVIERNES 25/05 · BUS 10:00 HS + ENTRADA\nDESDE EL OBELISCO · RESERVA AL 11 2234-5678\n¡CUPOS LIMITADOS!`}</div>
    <button onClick={()=>toast('🤖 Mensaje analizado: viaje detectado y publicado')} className="press rounded-2xl py-3 mt-3 text-[12px] font-bold text-white w-full" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)',boxShadow:'0 10px 30px -8px rgba(224,30,47,.6)'}}>Analizar mensaje</button>
  </>;
}

function SorteosModule(){
  const toast=useContext(ToastCtx);
  const db=useDB();const s=db.sorteo;const sold=useMemo(()=>new Set(s.sold),[s.sold]);
  const toggle=n=>OMDB.update(st=>{const set=new Set(st.sorteo.sold);set.has(n)?set.delete(n):set.add(n);st.sorteo.sold=[...set].sort((a,b)=>a-b);});
  return <>
    <Sec label="CLIC PARA MARCAR VENDIDO · SE ACTUALIZA EN EL SITIO" right={<span className="font-mono text-[8px] text-gold2 tracking-[1px]">${s.precio} C/U</span>}/>
    <div className="grid grid-cols-[auto_1fr] gap-4 items-center">
      <Shirt360 front={s.img} back={s.img_dorso} s={132}/>
      <div className="grid grid-cols-10 gap-1.5">
        {Array.from({length:s.total},(_,n)=>(
          <button key={n} onClick={()=>toggle(n)} className={`press aspect-square rounded-md grid place-items-center font-mono text-[10px] font-bold ${sold.has(n)?'bg-crimson text-white':'bg-white/[.06] text-mut border border-white/[.08]'}`}>{String(n).padStart(2,'0')}</button>))}
      </div>
    </div>
    <div className="flex justify-between font-mono text-[8px] text-mut mt-3"><span>■ VENDIDOS <b className="text-gold2">{sold.size}</b></span><span>□ DISPONIBLES <b className="text-gold2">{s.total-sold.size}</b></span></div>
  </>;
}

function ReelsModule(){
  const db=useDB();const toast=useContext(ToastCtx);
  const [txt,setTxt]=useState('');const [url,setUrl]=useState('');const [plat,setPlat]=useState('YOUTUBE');
  const add=()=>{
    if(!txt.trim()){toast('Poné un título');return;}
    if(!url.trim()){toast('Pegá el link de YouTube o TikTok');return;}
    OMDB.update(s=>{s.reels=[{titulo:txt.trim(),vistas:'0',plataforma:plat,url:url.trim()},...s.reels];});
    setTxt('');setUrl('');toast('Video publicado en el sitio');
  };
  const del=i=>OMDB.update(s=>{s.reels.splice(i,1);});
  return <>
    <Sec label="VIDEOS · TIKTOK / YOUTUBE CMS"/>
    <div className="flex flex-col gap-2 mb-3">
      <input value={txt} onChange={e=>setTxt(e.target.value)} placeholder="Título del video…" className="bg-white/[.06] border border-white/10 rounded-xl px-3 py-2 text-[11px] outline-none focus:border-river/60 min-w-0"/>
      <div className="flex gap-2">
        <div className="flex gap-1 flex-none">
          {['YOUTUBE','TIKTOK'].map(p=>(
            <button key={p} onClick={()=>setPlat(p)} className={`press rounded-xl px-2.5 text-[9px] font-mono tracking-[.5px] border ${plat===p?'text-white border-river bg-river/15':'text-mut border-white/10'}`}>{p==='YOUTUBE'?'▶ YT':'♪ TT'}</button>))}
        </div>
        <input value={url} onChange={e=>setUrl(e.target.value)} onKeyDown={e=>e.key==='Enter'&&add()} placeholder="Pegá el link…" className="flex-1 bg-white/[.06] border border-white/10 rounded-xl px-3 py-2 text-[10px] font-mono outline-none focus:border-river/60 min-w-0"/>
        <button onClick={add} className="press rounded-xl px-4 text-[16px] font-bold text-white flex-none" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)'}}>＋</button>
      </div>
    </div>
    <div className="flex flex-col gap-2 flex-1">
      {db.reels.map((r,i)=>(
        <div key={i} className="flex items-center gap-3 bg-white/[.05] border border-white/[.07] rounded-2xl px-3 py-2.5">
          <span className="w-9 h-11 rounded-xl grid place-items-center text-river2 flex-none" style={{background:'linear-gradient(160deg,#2a2a33,#101014)'}}><Ic n="video" s={16}/></span>
          <div className="min-w-0 flex-1"><b className="text-[11.5px] block leading-tight truncate">{r.titulo}</b><small className="font-mono text-[7px] text-dim">{r.plataforma} · {r.url?r.url.slice(0,26)+'…':r.vistas+' vistas'}</small></div>
          <button onClick={()=>del(i)} className="press w-7 h-7 rounded-lg grid place-items-center text-dim hover:text-river2 flex-none"><Ic n="trash" s={14}/></button>
        </div>))}
    </div>
  </>;
}

/* ============ PUBLICIDADES · BANNERS DEL SITIO ============ */
function PublicidadesModule(){
  const db=useDB();const toast=useContext(ToastCtx);
  const ads=db.config.banners||[];
  const [drag,setDrag]=useState(null);
  const setImg=(i,file)=>{if(!file)return;if(!/^image\//.test(file.type)){toast('Solo imágenes');return;}if(file.size>4.5e6){toast('Máx 4.5MB');return;}const r=new FileReader();r.onload=()=>{OMDB.update(s=>{s.config.banners[i].img=r.result;});toast('Banner #'+(i+1)+' actualizado en el sitio');};r.readAsDataURL(file);};
  const setUrl=(i,v)=>OMDB.update(s=>{s.config.banners[i].img=v;});
  const setLink=(i,v)=>OMDB.update(s=>{s.config.banners[i].link=v;});
  return <>
    <Sec label="TODAS LAS PUBLICIDADES DEL SITIO · ARRASTRÁ IMAGEN O PEGÁ URL + LINK" right={<span className="font-mono text-[8px] text-gold2 tracking-[1px]">{ads.length} SLOTS</span>}/>
    <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3 flex-1">
      {ads.map((a,i)=>(
        <div key={a.id} className="rounded-2xl border border-white/[.08] bg-white/[.04] p-3 flex flex-col gap-2">
          <div className="flex items-center gap-2"><span className="font-disp text-[12px] w-6 h-6 rounded-lg grid place-items-center bg-river/20 text-river2 flex-none">{i+1}</span><b className="text-[11px] flex-1 leading-tight">{a.pos}</b></div>
          <label onDragOver={e=>{e.preventDefault();setDrag(i);}} onDragLeave={()=>setDrag(null)} onDrop={e=>{e.preventDefault();setDrag(null);setImg(i,e.dataTransfer.files&&e.dataTransfer.files[0]);}}
            className={`relative rounded-xl border-2 border-dashed grid place-items-center overflow-hidden cursor-pointer transition-colors ${drag===i?'border-river bg-river/10':'border-white/15 hover:border-white/30'}`} style={{aspectRatio:'300/160',background:'rgba(255,255,255,.03)'}}>
            <input type="file" accept="image/*" className="hidden" onChange={e=>setImg(i,e.target.files&&e.target.files[0])}/>
            {a.img?<img src={a.img} alt="" className="absolute inset-0 w-full h-full object-contain"/>:<span className="font-mono text-[8px] text-dim tracking-[1px] text-center px-2 leading-relaxed">⬇ ARRASTRÁ UNA IMAGEN<br/>O TOCÁ PARA SUBIR</span>}
          </label>
          <input value={a.img&&/^https?:/.test(a.img)?a.img:''} onChange={e=>setUrl(i,e.target.value)} placeholder="…o pegá la URL de la imagen" className="bg-white/[.06] border border-white/10 rounded-lg px-2.5 py-1.5 text-[9.5px] font-mono outline-none focus:border-river/60"/>
          <input value={a.link||''} onChange={e=>setLink(i,e.target.value)} placeholder="Link de destino (https://…)" className="bg-white/[.06] border border-white/10 rounded-lg px-2.5 py-1.5 text-[9.5px] font-mono outline-none focus:border-river/60"/>
        </div>))}
    </div>
  </>;
}

/* ============ MODULE REGISTRY (hiper modular) ============ */
const MODULES=[
  {id:'kpi',   title:'Dashboard & Métricas', icon:'dash',  span:'lg:col-span-6', bare:true, C:KpiModule},
  {id:'site',  title:'Sitio & Hero',         icon:'eye',   span:'lg:col-span-3', C:SiteModule},
  {id:'modular',title:'Modularización',      icon:'grid',  span:'lg:col-span-3', C:ModularModule},
  {id:'constr',title:'Constructor del Sitio', icon:'drag',  span:'lg:col-span-6', C:ConstructorModule},
  {id:'ads',   title:'Publicidades · Banners',icon:'card',  span:'lg:col-span-6', C:PublicidadesModule},
  {id:'rev',   title:'Facturación',          icon:'down',  span:'lg:col-span-3', C:RevenueModule},
  {id:'world', title:'Visitas',              icon:'eye',   span:'lg:col-span-3', C:WorldModule},
  {id:'refs',  title:'Referidos · Socios',   icon:'users', span:'lg:col-span-3', C:ReferidosModule},
  {id:'figus', title:'Figuritas Builder',    icon:'card',  span:'lg:col-span-6', C:FiguritasModule},
  {id:'squad', title:'Plantilla',            icon:'squad', span:'lg:col-span-2 lg:row-span-2', C:PlantillaModule},
  {id:'hist',  title:'Historias · Telegram', icon:'chat',  span:'lg:col-span-2', C:HistoriasModule},
  {id:'sorteo',title:'Sorteos',              icon:'gift',  span:'lg:col-span-4', C:SorteosModule},
  {id:'spons', title:'Sponsors',             icon:'heart', span:'lg:col-span-2', C:SponsorsModule},
  {id:'reels', title:'Reels TikTok',         icon:'play',  span:'lg:col-span-2', C:ReelsModule},
];
const LS_KEY='om-panel-modules-v1';

/* ============ MODULE MANAGER SHEET ============ */
function ModuleSheet({open,onClose,enabled,setEnabled}){
  if(!open)return null;
  return <div className="fixed inset-0 z-[80] flex items-end md:items-center justify-center" onClick={onClose}>
    <div className="absolute inset-0 bg-black/60 backdrop-blur-sm"/>
    <div onClick={e=>e.stopPropagation()} className="glass-deep relative rounded-t-glass md:rounded-glass w-full md:w-[420px] p-6 safe-b" style={{animation:'sheetUp .35s cubic-bezier(.22,1,.36,1) both'}}>
      <div className="w-10 h-1 rounded-full bg-white/20 mx-auto mb-4 md:hidden"/>
      <div className="flex items-center justify-between mb-4">
        <b className="font-disp text-[16px] tracking-[1px]">MÓDULOS DEL PANEL</b>
        <button onClick={onClose} className="press w-8 h-8 rounded-full glass grid place-items-center text-mut hover:text-ink"><Ic n="x" s={14}/></button>
      </div>
      <div className="flex flex-col gap-2 max-h-[55vh] overflow-y-auto">
        {MODULES.map(m=>{
          const on=enabled.includes(m.id);
          return <button key={m.id} onClick={()=>setEnabled(on?enabled.filter(x=>x!==m.id):[...enabled,m.id])}
            className="press flex items-center gap-3 bg-white/[.05] border border-white/[.08] rounded-2xl px-4 py-3 text-left">
            <span className={on?'text-river2':'text-dim'}><Ic n={m.icon} s={16}/></span>
            <b className={`text-[12.5px] flex-1 ${on?'':'text-dim'}`}>{m.title}</b>
            <span className={`w-11 h-6 rounded-full p-[3px] transition-colors ${on?'bg-river':'bg-white/15'}`}>
              <i className={`block w-[18px] h-[18px] rounded-full bg-white shadow transition-transform ${on?'translate-x-5':''}`}/>
            </span>
          </button>;})}
      </div>
      <p className="font-mono text-[8px] text-dim tracking-[1px] mt-4 text-center">TU CONFIGURACIÓN SE GUARDA AUTOMÁTICAMENTE</p>
    </div>
  </div>;
}

/* ============ SEARCH FULLSCREEN ============ */
function SearchOverlay({open,onClose,onGo}){
  const [q,setQ]=useState('');
  const inRef=useRef();
  useEffect(()=>{if(open){setQ('');setTimeout(()=>inRef.current&&inRef.current.focus(),80);}},[open]);
  if(!open)return null;
  const ql=q.trim().toLowerCase();
  const res=MODULES.filter(m=>!ql||m.title.toLowerCase().includes(ql));
  return <div className="fixed inset-0 z-[95] flex flex-col" style={{animation:'sheetUp .3s cubic-bezier(.22,1,.36,1) both'}}>
    <div className="absolute inset-0 bg-black/80 backdrop-blur-xl" onClick={onClose}/>
    <div className="relative flex flex-col h-full px-4 md:px-8 pt-6 md:pt-16 pb-10 max-w-[820px] w-full mx-auto safe-b">
      <div className="glass-deep rounded-glass flex items-center gap-3 px-5 py-4">
        <span className="text-river2"><Ic n="search" s={20}/></span>
        <input ref={inRef} value={q} onChange={e=>setQ(e.target.value)} placeholder="Buscar módulos, secciones, herramientas…" className="flex-1 bg-transparent text-[17px] md:text-[20px] font-semibold outline-none placeholder:text-dim min-w-0"/>
        <button onClick={onClose} className="press w-9 h-9 rounded-full glass grid place-items-center text-mut hover:text-ink flex-none"><Ic n="x" s={16}/></button>
      </div>
      <div className="mt-4 flex flex-col gap-2 overflow-y-auto">
        <span className="font-mono text-[9px] tracking-[2px] text-mut px-1 mt-2">{ql?`${res.length} RESULTADO(S)`:'TODOS LOS MÓDULOS'}</span>
        {res.map(m=>(
          <button key={m.id} onClick={()=>{onGo(m.id);onClose();}} className="press glass rounded-2xl flex items-center gap-3.5 px-4 py-3.5 text-left">
            <span className="w-10 h-10 rounded-xl grid place-items-center text-river2 flex-none" style={{background:'rgba(224,30,47,.12)',border:'1px solid rgba(224,30,47,.3)'}}><Ic n={m.icon} s={17}/></span>
            <b className="text-[14px] flex-1">{m.title}</b>
            <span className="font-mono text-[9px] text-dim tracking-[1px]">IR ↗</span>
          </button>))}
        {res.length===0&&<div className="glass rounded-2xl p-8 text-center text-mut text-[13px]">Sin resultados para “{q}”.</div>}
      </div>
    </div>
  </div>;
}

/* ============ APP ============ */
function App(){
  const toast=useContext(ToastCtx);
  const [enabled,setEnabledRaw]=useState(()=>{const all=MODULES.map(m=>m.id);try{const v=JSON.parse(localStorage.getItem(LS_KEY));if(!Array.isArray(v))return all;return[...v.filter(id=>all.includes(id)),...all.filter(id=>!v.includes(id))];}catch(e){return all;}});
  const setEnabled=v=>{setEnabledRaw(v);try{localStorage.setItem(LS_KEY,JSON.stringify(v));}catch(e){}};
  const [sheet,setSheet]=useState(false);
  const [search,setSearch]=useState(false);
  const [notif,setNotif]=useState(false);
  const [dragId,setDragId]=useState(null);
  const [clock,setClock]=useState('');
  useEffect(()=>{const f=()=>{const d=new Date(),p=n=>String(n).padStart(2,'0');setClock(`${p(d.getDate())}/${p(d.getMonth()+1)}/${d.getFullYear()} · ${p(d.getHours())}:${p(d.getMinutes())} HS`);};f();const t=setInterval(f,30000);return()=>clearInterval(t);},[]);
  const mods=enabled.map(id=>MODULES.find(m=>m.id===id)).filter(Boolean);
  const reorder=(toId)=>{if(!dragId||dragId===toId)return;const a=[...enabled];const from=a.indexOf(dragId),to=a.indexOf(toId);if(from<0||to<0)return;a.splice(from,1);a.splice(to,0,dragId);setEnabled(a);};
  const navGo=id=>{const el=document.getElementById('mod-'+id);if(el){const y=el.getBoundingClientRect().top+window.scrollY-84;window.scrollTo({top:y,behavior:'smooth'});}else toast('Activá ese módulo desde “Módulos”');};
  return <>
    {/* fondo liquid */}
    <div className="blob w-[520px] h-[520px] -top-40 -left-32" style={{background:'radial-gradient(circle,#8d0917 0%,transparent 70%)',opacity:.55,animation:'drift1 18s ease-in-out infinite'}}/>
    <div className="blob w-[420px] h-[420px] top-1/3 -right-32" style={{background:'radial-gradient(circle,#3b1d6e 0%,transparent 70%)',opacity:.5,animation:'drift2 22s ease-in-out infinite'}}/>
    <div className="blob w-[380px] h-[380px] bottom-0 left-1/3" style={{background:'radial-gradient(circle,#5a4008 0%,transparent 70%)',opacity:.4,animation:'drift1 26s ease-in-out infinite'}}/>

    {/* topbar glass */}
    <header className="sticky top-0 z-[60] px-3 md:px-6 pt-3">
      <div className="glass-deep rounded-glass flex items-center gap-3 px-4 md:px-5 py-2.5 max-w-[1440px] mx-auto">
        <img src="assets/logo-om.png" alt="OM" className="w-8 h-9 object-contain"/>
        <div className="leading-tight mr-2">
          <b className="font-disp text-[13px] tracking-[1px] block">PANEL SAAS</b>
          <small className="font-mono text-[7px] tracking-[2px] text-river2">ORGULLO MILLONARIO</small>
        </div>
        <nav className="hidden lg:flex items-center gap-1 flex-1 justify-center">
          {MODULES.slice(0,7).map(m=>(
            <button key={m.id} onClick={()=>navGo(m.id)} className="press flex items-center gap-2 px-3.5 py-2 rounded-full text-[11px] font-semibold text-mut hover:text-ink hover:bg-white/[.07] transition-colors">
              <Ic n={m.icon} s={13}/>{m.title.split(' ')[0]}
            </button>))}
        </nav>
        <div className="flex items-center gap-2 ml-auto">
          <button onClick={()=>setSearch(true)} className="press w-9 h-9 rounded-full glass grid place-items-center text-mut hover:text-ink"><Ic n="search" s={15}/></button>
          <div className="relative">
            <button onClick={()=>setNotif(v=>!v)} className="press w-9 h-9 rounded-full glass grid place-items-center text-mut hover:text-ink relative"><Ic n="bell" s={15}/><i className="absolute top-1.5 right-2 w-[6px] h-[6px] rounded-full bg-river2"/></button>
            {notif&&<><div className="fixed inset-0 z-[70]" onClick={()=>setNotif(false)}/><div className="glass-deep rounded-glass absolute right-0 mt-2 w-[280px] p-3 z-[71]" style={{animation:'sheetUp .25s cubic-bezier(.22,1,.36,1) both'}}>
              <div className="flex items-center justify-between mb-2 px-1"><b className="font-disp text-[12px] tracking-[1px]">NOTIFICACIONES</b><span className="font-mono text-[7px] text-green-400 tracking-[1px]">EN VIVO</span></div>
              <div className="flex flex-col gap-1.5">
                {(()=>{const u=(window.OMAuth&&OMAuth.users&&OMAuth.users())||[];const k=(window.OMDB&&OMDB.get&&OMDB.get().kpis)||{};return [
                  ['users','Socios registrados',(u.length||k.socios||0)+' en total','#8b5cf6'],
                  ['ticket','Reservas de viaje',(k.reservas||0)+' activas','#22c55e'],
                  ['eye','Visitas al sitio',(k.visitas||0)+' totales','#5b93ff'],
                  ['gift','Sorteo en curso','Camiseta firmada','#ffcf3e'],
                ].map(([ic,t,s,cc],i)=>(<div key={i} className="flex items-center gap-2.5 rounded-xl px-2.5 py-2 bg-white/[.04] border border-white/[.06]"><span className="w-7 h-7 rounded-lg grid place-items-center flex-none" style={{color:cc,background:cc+'1e'}}><Ic n={ic} s={14}/></span><div className="min-w-0 flex-1"><b className="text-[11px] block truncate">{t}</b><small className="font-mono text-[8px] text-mut">{s}</small></div></div>));})()}
              </div>
            </div></>}
          </div>
          <button onClick={()=>setSheet(true)} className="press hidden md:flex items-center gap-2 px-4 py-2 rounded-full text-[11px] font-bold text-white" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)',boxShadow:'0 8px 24px -6px rgba(224,30,47,.6)'}}><Ic n="grid" s={13}/>Módulos</button>
          <button onClick={()=>{OMAdmin.logout();location.reload();}} title="Cerrar sesión de admin" className="press w-9 h-9 rounded-full grid place-items-center font-disp font-bold text-[14px] text-gold2 border-2 border-river" style={{background:'radial-gradient(circle at 35% 30%,#3a3a44,#17171c)'}}>A</button>
        </div>
      </div>
    </header>

    {/* grid de módulos */}
    <main className="relative z-10 px-3 md:px-6 py-4 pb-40 md:pb-24 max-w-[1440px] mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-3 md:gap-4 items-stretch auto-rows-min [grid-auto-flow:row_dense]">
      {mods.map((m,i)=>m.bare
        ?<div key={m.id} id={'mod-'+m.id} draggable onDragStart={()=>setDragId(m.id)} onDragEnd={()=>setDragId(null)} onDragOver={e=>{e.preventDefault();reorder(m.id);}} className={`${m.span} md:col-span-2 rise ${dragId===m.id?'opacity-50':''}`} style={{animationDelay:i*60+'ms'}}><m.C/></div>
        :<section key={m.id} id={'mod-'+m.id} draggable onDragStart={()=>setDragId(m.id)} onDragEnd={()=>setDragId(null)} onDragOver={e=>{e.preventDefault();reorder(m.id);}} className={`glass rounded-glass p-5 flex flex-col h-full ${m.span} rise ${dragId===m.id?'opacity-50 ring-2 ring-river':''}`} style={{animationDelay:i*60+'ms'}}>
          <div className="flex items-center gap-2.5 mb-4 cursor-grab active:cursor-grabbing">
            <span className="w-8 h-8 rounded-xl grid place-items-center text-river2" style={{background:'rgba(224,30,47,.12)',border:'1px solid rgba(224,30,47,.3)'}}><Ic n={m.icon} s={15}/></span>
            <b className="font-disp text-[14px] tracking-[1.5px] flex-1">{m.title.toUpperCase()}</b>
            <span className="text-dim"><Ic n="drag" s={15}/></span>
          </div>
          <m.C/>
        </section>)}
      {mods.length===0&&<div className="col-span-full glass rounded-glass p-10 text-center text-mut text-[13px]">No hay módulos activos. Tocá <b className="text-ink">Módulos</b> para activarlos.</div>}
    </main>

    {/* footer */}
    <footer className="relative z-10 hidden md:flex items-center gap-5 px-6 pb-4 max-w-[1440px] mx-auto font-mono text-[8px] tracking-[1px] text-dim">
      <span>ORGULLO MILLONARIO ® 2026</span><span>PANEL SAAS v3.0 · LIQUID</span>
      <span className="text-green-400">SERVIDORES: ONLINE ●</span><span className="ml-auto">ACTUALIZADO: {clock}</span>
    </footer>

    {/* tab bar móvil */}
    <nav className="lg:hidden fixed bottom-3 inset-x-3 z-[70] safe-b">
      <div className="glass-deep rounded-glass flex items-center justify-around px-2 py-2">
        {[['kpi','dash','Inicio'],['figus','card','Figus'],['squad','squad','Plantel'],['sorteo','gift','Sorteos']].map(([id,ic,l])=>(
          <button key={id} onClick={()=>navGo(id)} className="press flex flex-col items-center gap-1 px-4 py-1.5 rounded-2xl text-mut hover:text-ink">
            <Ic n={ic} s={19}/><small className="font-mono text-[7px] tracking-[1px]">{l}</small>
          </button>))}
        <button onClick={()=>setSheet(true)} className="press flex flex-col items-center gap-1 px-4 py-1.5 rounded-2xl text-white">
          <span className="w-[26px] h-[26px] -my-[3px] rounded-full grid place-items-center" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)',boxShadow:'0 6px 18px -4px rgba(224,30,47,.7)'}}><Ic n="grid" s={13}/></span>
          <small className="font-mono text-[7px] tracking-[1px]">Módulos</small>
        </button>
      </div>
    </nav>

    <ModuleSheet open={sheet} onClose={()=>setSheet(false)} enabled={enabled} setEnabled={setEnabled}/>
    <SearchOverlay open={search} onClose={()=>setSearch(false)} onGo={navGo}/>
  </>;
}

/* ============ ADMIN LOGIN (privado) ============ */
function AdminLogin({onOk}){
  const [email,setEmail]=useState('');const [pass,setPass]=useState('');const [show,setShow]=useState(false);
  const [err,setErr]=useState('');const [loading,setLoading]=useState(false);
  const submit=async()=>{setErr('');setLoading(true);const r=await OMAdmin.login(email,pass);if(r.ok){onOk();}else{setErr(r.error);setLoading(false);}};
  return <div className="relative min-h-[100dvh] grid place-items-center px-4">
    <div className="blob w-[520px] h-[520px] -top-40 -left-32" style={{background:'radial-gradient(circle,#8d0917,transparent 70%)',opacity:.5,animation:'drift1 18s ease-in-out infinite'}}/>
    <div className="blob w-[420px] h-[420px] bottom-0 -right-32" style={{background:'radial-gradient(circle,#3b1d6e,transparent 70%)',opacity:.45,animation:'drift2 22s ease-in-out infinite'}}/>
    <div className="glass-deep relative rounded-glass w-full max-w-[400px] p-7 md:p-8 rise">
      <div className="flex flex-col items-center text-center mb-6">
        <img src="assets/logo-om.png" alt="OM" className="w-12 h-14 object-contain mb-3"/>
        <b className="font-disp text-[22px] tracking-[1px]">PANEL SAAS · PRIVADO</b>
        <small className="font-mono text-[8px] tracking-[2px] text-river2 mt-1">ACCESO SOLO ADMINISTRADORES</small>
      </div>
      <div className="flex flex-col gap-3">
        <label className="flex items-center gap-3 bg-white/[.06] border border-white/12 rounded-2xl px-4 py-3.5 focus-within:border-river/60 transition-colors">
          <span className="text-mut flex-none"><Ic n="eye" s={16}/></span>
          <input type="email" autoComplete="username" value={email} onChange={e=>setEmail(e.target.value)} onKeyDown={e=>e.key==='Enter'&&submit()} placeholder="Correo de administrador" className="flex-1 bg-transparent text-[14px] outline-none placeholder:text-dim min-w-0"/>
        </label>
        <label className="flex items-center gap-3 bg-white/[.06] border border-white/12 rounded-2xl px-4 py-3.5 focus-within:border-river/60 transition-colors">
          <span className="text-mut flex-none"><Ic n="grid" s={16}/></span>
          <input type={show?'text':'password'} autoComplete="current-password" value={pass} onChange={e=>setPass(e.target.value)} onKeyDown={e=>e.key==='Enter'&&submit()} placeholder="Contraseña" className="flex-1 bg-transparent text-[14px] outline-none placeholder:text-dim min-w-0"/>
          <button type="button" onClick={()=>setShow(!show)} className="press text-mut hover:text-ink flex-none text-[11px] font-mono">{show?'OCULTAR':'VER'}</button>
        </label>
        {err&&<div className="text-[12px] text-river2 bg-river/10 border border-river/30 rounded-xl px-3 py-2">{err}</div>}
        <button onClick={submit} disabled={loading} className="press rounded-2xl py-3.5 text-[14px] font-bold text-white mt-1" style={{background:'linear-gradient(160deg,#ff2e3e,#8d0917)',boxShadow:'0 12px 30px -8px rgba(224,30,47,.6)'}}>{loading?'Verificando…':'INGRESAR AL PANEL'}</button>
      </div>
      <p className="font-mono text-[8px] text-dim tracking-[1px] mt-5 text-center">🔒 Sesión cifrada · las credenciales nunca se almacenan en claro</p>
    </div>
  </div>;
}
function AdminGate(){
  const [authed,setAuthed]=useState(()=>!!(window.OMAdmin&&OMAdmin.isAuthed()));
  if(!authed) return <AdminLogin onOk={()=>setAuthed(true)}/>;
  return <App/>;
}

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