/* ============================================================
   screens.jsx — title, select, reward, rest/event/shop, endings
   ============================================================ */
const { useState, useRef, useEffect } = React;


/* ---------------- TITLE ---------------- */
function TitleScreen({ onStart, onContinue, onMeta, onCodex, onLogros }) {
  const t2 = !!(window.GAME.loadProfile().season1Cleared);
  return (
    <div className="fill col center" style={{ background:'radial-gradient(120% 90% at 50% 20%, #16344b, #07131f 75%)', gap: 30 }}>
      <div className="col center" style={{ gap: 6 }}>
        <div className="pix" style={{ fontSize: 14, color:'#2ce8d8', letterSpacing: 6, textShadow:'2px 2px 0 #000' }}>EL TIBURÓN TUNTUN</div>
        <div className="pix" style={{ fontSize: 52, color:'#ff5a3c', textShadow:'5px 5px 0 #07131f, 0 0 30px rgba(255,90,60,.4)', letterSpacing: 4 }}>ARPONLIKE</div>
        <div className="pix" style={{ fontSize: 22, color:'#ffd23f', textShadow:'4px 4px 0 #07131f' }}>· LA VENGANZA ·</div>
      </div>
      <div className="bob"><Sprite name="c_tuntun" scale={9} /></div>
      <div className="pix blink" style={{ fontSize: 14, color:'#fff', textShadow:'2px 2px 0 #000', marginTop: 10 }}>▶ PULSA START</div>
      {onContinue && <Btn variant="btn-go" onClick={onContinue} style={{ fontSize: 14, padding:'14px 28px' }}>↺ CONTINUAR PARTIDA</Btn>}
      <Btn variant="btn-primary" onClick={onStart} style={{ fontSize: 16, padding:'16px 30px' }}>{onContinue ? 'NUEVA PARTIDA' : 'START'}</Btn>
      <div className="row" style={{ gap: 10 }}>
        <Btn onClick={onMeta} style={{ fontSize: 10, padding:'9px 18px' }}>◈ TESORO ABISAL</Btn>
        <Btn onClick={onCodex} style={{ fontSize: 10, padding:'9px 18px' }}>📖 BESTIARIO</Btn>
        <Btn onClick={onLogros} style={{ fontSize: 10, padding:'9px 18px' }}>★ LOGROS</Btn>
      </div>
      {/* TEMPORADAS — dos campañas; la T2 se desbloquea al ganar la T1 */}
      <div className="row center" style={{ gap: 10, marginTop: 2 }}>
        <div className="pix" style={{ fontSize: 8, lineHeight: 1.5, padding:'5px 12px', border:'2px solid #ffd23f', color:'#ffd23f', background:'rgba(255,210,63,.08)' }}>
          TEMPORADA 1 · EL DESCENSO <span style={{ color:'#9db8c9' }}>· actos I–VI</span>
        </div>
        <div className="pix" {...window.tipProps({ title:'Temporada 2 · Bancos Mutantes', color:'#1fd6c0', icon:'ic_drop',
          body: t2 ? 'Campaña desbloqueada (actos VII–X): el AGUA es parte de la build — 6 tipos de agua, 3 personajes nuevos (Brinco/Nácar/Salmuera) y 4 actos con mecánicas propias. Elígela en la pantalla de personaje.' : 'Bloqueada. Gana una run de la Temporada 1 para desbloquear la Temporada 2: agua activa, 3 personajes nuevos y 4 actos con mecánicas propias.' })}
          style={{ fontSize: 8, lineHeight: 1.5, padding:'5px 12px', cursor:'help',
            border:'2px solid '+(t2?'#1fd6c0':'#3a5a72'), color: t2?'#1fd6c0':'#5a7088', background: t2?'rgba(31,214,192,.08)':'rgba(58,90,114,.12)' }}>
          {t2 ? 'TEMPORADA 2 · BANCOS MUTANTES' : '🔒 TEMPORADA 2 · ???'} <span style={{ color:'#6f8aa0' }}>· actos VII–X</span>
        </div>
      </div>
      <div className="body" style={{ fontSize: 16, color:'#6f8aa0', marginTop: 6 }}>roguelike de arpón · pesca tu turno{t2 ? ' · 2 temporadas' : ''}</div>
    </div>
  );
}

/* ---------------- CHARACTER SELECT ---------------- */
function CharacterSelect({ onChoose, onMeta, onBack }) {
  const chars = window.GAME.CHARS;
  const unlocked = window.GAME.getUnlocked();
  const isUnlocked = (id) => unlocked.includes(id);
  const firstUnlockedIdx = chars.findIndex(c => isUnlocked(c.id));
  const [sel, setSel] = useState(firstUnlockedIdx < 0 ? 0 : firstUnlockedIdx);
  const [tier, setTier] = useState(0);
  const season1Cleared = !!(window.GAME.loadProfile().season1Cleared);   // TEMPORADA 2 desbloqueada
  const [season, setSeason] = useState(1);
  const c = chars[sel];
  const selUnlocked = isUnlocked(c.id);
  const perlas = window.GAME.getPerlas();
  const best = window.GAME.getAmenaza(c.id);                 // highest cleared (-1 = none)
  const maxSel = window.GAME.maxAmenazaSel(c.id);            // can pick up to best+1
  const curTier = Math.min(tier, maxSel);
  const amInfo = window.GAME.amenaza(curTier);
  return (
    <div className="fill col" style={{ gap: 14, padding: 16, paddingBottom: 0, alignItems:'center', overflowY:'auto' }}>
      <div className="row center" style={{ gap: 16, alignItems:'center' }}>
        <div className="pix" style={{ fontSize: 20, color:'#ffd23f', textShadow:'3px 3px 0 #000' }}>ELIGE TU CAZADOR</div>
        <div onClick={onMeta} {...window.tipProps({ title:'Perlas Abisales', color:'#b06bff', icon:'ic_star', body:'Moneda que ganas al completar runs (más en Amenaza alta). Clic para gastarlas en el TESORO ABISAL (mejoras permanentes).' })}
          style={{ alignItems:'center', gap:6, background:'rgba(176,107,255,.12)', border:'2px solid #b06bff', padding:'3px 10px', cursor:'pointer', display:'flex' }}>
          <Glyph name="ic_star" scale={2.5} color="#b06bff" /><span className="pix" style={{ fontSize:11, color:'#b06bff' }}>{perlas}</span>
          <span className="pix" style={{ fontSize:7, color:'#b06bff', marginLeft:4 }}>◈ TESORO</span>
        </div>
      </div>
      <div className="row" style={{ gap: 14, flexWrap: 'wrap', justifyContent: 'center', maxWidth: '94vw' }}>
        {chars.map((ch, i) => {
          const unl = isUnlocked(ch.id);
          return (
          <div key={ch.id} onClick={() => { setSel(i); if (window.SFX) window.SFX.select(); }} className="col center panel"
            style={{ width: 180, padding: 14, gap: 8, cursor:'pointer', position:'relative',
              borderColor: sel===i ? '#ffd23f' : '#16242f',
              boxShadow: sel===i ? '0 0 0 4px #ffd23f, inset 3px 3px 0 rgba(255,255,255,.08)' : undefined,
              background: sel===i ? '#16344b' : '#13293d' }}>
            <div className={sel===i?'bob':''} style={{ filter: unl ? 'none' : 'grayscale(1) brightness(.35)' }}><Sprite name={ch.sprite || 'tuntun'} scale={3} hue={ch.hue} /></div>
            <div className="pix" style={{ fontSize: 15, color: unl?'#fff':'#5a7088', textShadow:'2px 2px 0 #000' }}>{unl ? ch.name : '???'}</div>
            <div className="pix" style={{ fontSize: 8, color:'#2ce8d8' }}>{unl ? ch.title : 'BLOQUEADO'}</div>
            <div className="row" style={{ alignItems:'center', gap:6 }}>
              <Glyph name="ic_heart" scale={2.5} color="#ff6b8b" />
              <span className="pix" style={{ fontSize:11, color:'#ff6b8b' }}>{ch.maxHp}</span>
            </div>
            {!unl && <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center' }}>
              <Glyph name="ic_anchor" scale={4} color="rgba(255,255,255,.25)" /></div>}
          </div>
        );})}
      </div>
      {/* detail */}
      <div className="panel col" style={{ width: 720, padding: 16, gap: 10, background:'#0b1d2e' }}>
        {selUnlocked ? <React.Fragment>
          <div className="body" style={{ fontSize: 22, color:'#eaf6ff', lineHeight:1.1 }}>{c.blurb}</div>
          <div className="pix" style={{ fontSize:8, color:'#9db8c9', lineHeight:1.6 }}>{c.style}</div>
          <div className="row" style={{ alignItems:'center', gap:8, background:'rgba(255,210,63,.1)', padding:'7px 10px', border:'3px solid #ffd23f' }}>
            <Glyph name="ic_star" scale={2.5} color="#ffd23f" />
            <span className="pix" style={{ fontSize:9, color:'#ffd23f', lineHeight:1.6 }}>{c.passive}</span>
          </div>
          {(() => { const h = window.GAME.harpoon(c.harpoon);
            const hasArt = c.harpoonArt && window.SPRITES && window.SPRITES[c.harpoonArt];
            return (
            <div className="row" style={{ alignItems:'center', gap:10, background:'rgba(44,232,216,.08)', padding:'8px 11px', border:'3px solid #2ce8d8' }}>
              {hasArt ? <Sprite name={c.harpoonArt} scale={2} /> : <Glyph name={h.icon} scale={2.5} color="#2ce8d8" />}
              <span className="pix" style={{ fontSize:9, color:'#2ce8d8', lineHeight:1.6 }}>ARPÓN · {c.harpoonName || h.name}: {c.harpoonDesc || h.desc}</span>
            </div>
          ); })()}
          <div className="row" style={{ gap: 7, flexWrap:'wrap' }}>
            {c.deck.map((id, i) => <ItemCard key={i} it={window.GAME.item(id)} size="sm" />)}
          </div>
        </React.Fragment> : (
          <div className="col center" style={{ gap:10, padding:14 }}>
            <Glyph name="ic_anchor" scale={4} color="#5a7088" />
            <div className="pix" style={{ fontSize:11, color:'#ff9f1c', lineHeight:1.6, textAlign:'center' }}>CÓMO DESBLOQUEAR</div>
            <div className="body" style={{ fontSize:22, color:'#eaf6ff', textAlign:'center' }}>{c.unlock}</div>
          </div>
        )}
      </div>
      {/* Amenaza (ascension) selector */}
      <div className="col center" style={{ gap: 6 }}>
        <div className="row center" style={{ gap: 8, alignItems:'center' }}>
          <span className="pix" style={{ fontSize:9, color:'#6f8aa0' }}>AMENAZA:</span>
          {window.GAME.AMENAZA.map((d, i) => {
            const locked = i > maxSel;
            return (
              <div key={i} onClick={() => { if (!locked) { setTier(i); if (window.SFX) window.SFX.select && window.SFX.select(); } }}
                className="col center" style={{ cursor: locked?'not-allowed':'pointer', gap:2, padding:'5px 11px', minWidth:34,
                  border:'3px solid '+(i===curTier?'#ff5a3c':'#16242f'), background: i===curTier?'#2a1414':'#13293d', opacity: locked?0.4:1 }}>
                <span className="pix" style={{ fontSize:12, color: i===curTier?'#ff5a3c':(locked?'#5a7088':'#9db8c9') }}>{locked?'🔒':i}</span>
                {i<=best && <span className="pix" style={{ fontSize:6, color:'#9ae600' }}>✓</span>}
              </div>
            );
          })}
        </div>
        <div className="row center" style={{ gap:8, alignItems:'center', maxWidth:640 }}>
          <span className="pix" style={{ fontSize:8, color:'#ff5a3c' }}>×{amInfo.mul.toFixed(2)} fuerza</span>
          <span className="pix" style={{ fontSize:9, color:'#eaf6ff', textShadow:'1px 1px 0 #000' }}>{amInfo.rule}</span>
          {best >= 0 && <span className="pix" style={{ fontSize:7, color:'#9ae600' }}>· máx superada: A{best}</span>}
        </div>
      </div>
      {season1Cleared && (
        <div className="row center" style={{ gap: 8, alignItems:'center' }}>
          {[{ s:1, label:'TEMPORADA 1 · El Descenso', color:'#ffd23f' }, { s:2, label:'TEMPORADA 2 · Bancos Mutantes', color:'#1fd6c0' }].map(o => (
            <div key={o.s} onClick={() => { setSeason(o.s); if (window.SFX) window.SFX.select && window.SFX.select(); }}
              className="pix" style={{ cursor:'pointer', fontSize:9, padding:'7px 14px', lineHeight:1.5,
                border:'3px solid '+(season===o.s?o.color:'#16242f'), background: season===o.s?'rgba(31,214,192,.12)':'#13293d',
                color: season===o.s?o.color:'#9db8c9' }}>{o.label}</div>
          ))}
        </div>
      )}
      {/* footer FIJO — el botón de jugar siempre visible aunque el contenido sea alto */}
      <div className="row center" style={{ gap: 12, alignItems:'center', position:'sticky', bottom:0, width:'100%',
        padding:'12px 0 14px', marginTop:'auto', zIndex:5,
        background:'linear-gradient(180deg, rgba(7,19,31,0) 0%, rgba(7,19,31,.92) 38%)' }}>
        {onBack && <Btn onClick={onBack} style={{ fontSize: 10, padding:'11px 18px' }}>◀ VOLVER</Btn>}
        {onMeta && <Btn onClick={onMeta} style={{ fontSize: 10, padding:'11px 18px' }}>◈ TESORO</Btn>}
        <Btn variant="btn-primary" disabled={!selUnlocked} onClick={() => onChoose(c, curTier, season)}
          style={{ fontSize: 17, padding:'15px 40px', boxShadow: selUnlocked ? '0 0 24px rgba(255,90,60,.5)' : undefined }}>
          {selUnlocked ? (season===2 ? '▶ SURCAR LOS BANCOS' : '▶ EMPEZAR EL DESCENSO') : '🔒 BLOQUEADO'}
        </Btn>
      </div>
    </div>
  );
}

/* ---------------- REWARD ---------------- */
function RewardScreen({ run, type, choices, gold, cardDesign, onTake, onSkip }) {
  const [taken, setTaken] = useState(false);
  const isRelic = type === 'relic';
  return (
    <div className="fill col center" style={{ gap: 22, background:'radial-gradient(120% 90% at 50% 0%, #16344b, #0a1b2a 75%)' }}>
      <div className="pix" style={{ fontSize: 22, color: isRelic?'#b06bff':'#ffd23f', textShadow:'3px 3px 0 #000' }}>{isRelic?'¡RELIQUIA!':'¡BOTÍN!'}</div>
      {gold>0 && <div className="row" style={{ alignItems:'center', gap:8 }}>
        <Glyph name="ic_coin" scale={3} color="#ffd23f" /><span className="pix" style={{ fontSize:12, color:'#ffd23f' }}>+{gold} ORO</span></div>}
      <div className="body" style={{ fontSize: 20, color:'#9db8c9' }}>{isRelic?'Engancha UNA reliquia':'Engancha UN objeto para tu baraja'}</div>
      <div className="row" style={{ gap: 22, alignItems:'stretch' }}>
        {choices.map((it, i) => (
          <div key={i} className="pop" style={{ opacity: taken?0.4:1, pointerEvents: taken?'none':'auto' }}>
            {isRelic
              ? <RelicCard rel={it} onClick={() => { setTaken(true); onTake(it); }} />
              : <ItemCard it={it} size="lg" design={cardDesign} onClick={() => { setTaken(true); onTake(it); }} />}
          </div>
        ))}
      </div>
      <Btn onClick={onSkip} disabled={taken} style={{ fontSize: 11 }}>SALTAR ▶</Btn>
    </div>
  );
}

/* ---------------- REST ---------------- */
function RestScreen({ run, onHeal, onRemove, onUpgrade, onDone }) {
  const [mode, setMode] = useState('menu');
  if (mode === 'remove') {
    return <DeckPicker run={run} title="QUITA UNA CARTA DE TU BARAJA" cardDesign="token"
      onPick={(idx) => { onRemove(idx); onDone(); }} onCancel={() => setMode('menu')} />;
  }
  if (mode === 'upgrade') {
    return <DeckPicker run={run} title="TEMPLA UNA CARTA · +1" cardDesign="token"
      filter={(card) => window.GAME.canUpgradeCard(card)}
      emptyText="No tienes cartas mejorables."
      tip={(card) => window.tipForUpgrade(card)}
      onPick={(idx) => { onUpgrade(idx); onDone(); }} onCancel={() => setMode('menu')} />;
  }
  const healAmt = Math.round(run.char.maxHp * 0.3 * ((run.ascMods && run.ascMods.healMul) || 1));
  const canUpgrade = (run.deck || []).some(c => window.GAME.canUpgradeCard(c));
  return (
    <div className="fill col center" style={{ gap: 26, background:'radial-gradient(120% 90% at 50% 30%, #2a1f12, #0a1b2a 75%)' }}>
      <div className="pix" style={{ fontSize: 20, color:'#ff9f1c', textShadow:'3px 3px 0 #000' }}>HOGUERA</div>
      <div className="bob"><Sprite name="hoguera" scale={5} /></div>
      <div className="body" style={{ fontSize: 22, color:'#eaf6ff' }}>El abismo está en calma. ¿Qué haces?</div>
      <div className="row" style={{ gap: 18 }}>
        <Btn variant="btn-go" onClick={() => { onHeal(healAmt); onDone(); }} style={{ fontSize:11 }}>DESCANSAR · +{healAmt} PV</Btn>
        <Btn variant={canUpgrade?'btn-go':''} disabled={!canUpgrade} onClick={() => setMode('upgrade')} style={{ fontSize:11 }}>TEMPLAR · MEJORAR CARTA</Btn>
        <Btn onClick={() => setMode('remove')} style={{ fontSize:11 }}>FORJAR · QUITAR CARTA</Btn>
      </div>
    </div>
  );
}

/* ---------------- EVENT ---------------- */
const EVENTS = [
  { text:'Un pecio hundido brilla en la oscuridad. Una caja fuerte oxidada te observa.',
    opts:[ {label:'FORZARLA (+40 oro, -6 PV)', eff:{gold:40, hp:-6}}, {label:'IGNORARLA', eff:{}} ] },
  { text:'Una anguila eléctrica ofrece un trato: poder a cambio de carne.',
    opts:[ {label:'ACEPTAR (+1 carta rara, -8 PV)', eff:{card:'rare', hp:-8}}, {label:'RECHAZAR (+15 oro)', eff:{gold:15}} ] },
  { text:'Encuentras un banco de peces curativos.',
    opts:[ {label:'COMER (+20 PV)', eff:{hp:20}}, {label:'GUARDAR (+25 oro)', eff:{gold:25}} ] },
  { text:'Un ídolo de coral late con poder antiguo. Exige carne a cambio de su don.',
    opts:[ {label:'OFRENDA (-5 PV máx, +1 RELIQUIA)', eff:{maxHp:-5, relic:true}}, {label:'RETIRARSE', eff:{}} ] },
  { text:'Una sirena abisal te ofrece un arma rara... pero deja una maldición pegada al casco.',
    opts:[ {label:'ACEPTAR (+1 carta RARA, +1 MALDICIÓN)', eff:{card:'rare', curse:true}}, {label:'RECHAZAR (+20 oro)', eff:{gold:20}} ] },
  { text:'Un pulpo armero te tienta: cambiará tu arpón por uno desconocido durante el resto del Acto.',
    opts:[ {label:'PROBAR (arpón ANCHO el resto del acto)', eff:{harpoon:'wide'}}, {label:'NO TOCAR NADA', eff:{}} ] },
  { text:'Un taller hundido conserva PUNTAS de arpón intactas entre el óxido.',
    opts:[ {label:'HURGAR (+1 MOD de arpón, -6 PV)', eff:{harpoonMod:true, hp:-6}}, {label:'LLEVARSE LA CHATARRA (+25 oro)', eff:{gold:25}} ] },
  { text:'Una corriente cálida arrastra cápsulas y artilugios abisales.',
    opts:[ {label:'PESCAR UN CONSUMIBLE', eff:{consumable:true}}, {label:'PESCAR UN ACTIVABLE', eff:{gadget:true}} ] },
  { text:'Un cardumen translúcido vibra al ritmo del agua; algunos brillan distinto.',
    opts:[ {label:'AFINARTE CON ELLOS (+1 carta, +10 oro)', eff:{card:'gen', gold:10}}, {label:'OBSERVAR (+1 consumible)', eff:{consumable:true}} ] },
  // SECRETO — desbloqueo oculto
  { secret:true, text:'Una compuerta sellada con runas de arpón. Algo late, paciente, detrás del metal.',
    opts:[ {label:'FORZAR EL SELLO (-12 PV, +RELIQUIA, ???)', eff:{hp:-12, relic:true, unlockAch:'sellado', perlas:15}}, {label:'DEJARLA EN PAZ', eff:{}} ] },
];
/* minijuegos de evento — TODOS usan la física real del arpón (TankEngine). Tu puntería decide el premio. */
const MINIGAMES = [
  { mini:'harpoon', cfg:'treasure', icon:'ic_coin', text:'Un pecio rebosa doblones y un cofre sellado... pero hay MINAS entre el botín. Arponéa lo que puedas.' },
  { mini:'harpoon', cfg:'pearls',   icon:'ic_drop', text:'Un banco de perlas abisales se desliza veloz por la corriente. Engánchalas antes de que huyan.' },
];

// token de tesoro = objeto-item completo (mismos campos que GAME.item) con extras gold/cofre que leemos al pescar
function treasureToken(name, cat, aff, icon, val, move, extra) {
  return Object.assign({ name, cat, aff, icon, val, move, rarity:'c', eff:{}, desc:'' }, extra || {});
}
const HARPOON_CFG = {
  treasure: { throws: 3, build: () => {
    const out = [];
    const vals = [8, 10, 12, 12, 15], moves = ['static', 'drift', 'drift', 'fast'];
    for (let i = 0; i < 6; i++) { const g = vals[Math.floor(Math.random()*vals.length)];
      out.push(treasureToken('Doblón', 'bomb', 'chatarra', 'ic_coin', g, moves[Math.floor(Math.random()*moves.length)], { gold: g })); }
    out.push(treasureToken('Cofre Sellado', 'wild', 'chatarra', 'ic_star', 0, 'static', { cofre: true, val: 0 }));
    out.push(window.GAME.junkItem('mine'), window.GAME.junkItem('mine'), window.GAME.junkItem('mine'));
    return out;
  } },
  pearls: { throws: 4, build: () => {
    const out = [];
    const vals = [6, 7, 8, 9];
    for (let i = 0; i < 9; i++) { const g = vals[Math.floor(Math.random()*vals.length)];
      out.push(treasureToken('Perla', 'defense', 'marea', 'ic_drop', g, i % 3 === 0 ? 'drift' : 'fast', { gold: g })); }
    out.push(window.GAME.junkItem('mine'));
    return out;
  } },
};

/* MINIJUEGO de gancho — monta un TankEngine real: apuntas y disparas con la misma física del combate */
function HarpoonGame({ cfg, onDone }) {
  const cvRef = useRef(null), engRef = useRef(null), doneRef = useRef(false);
  const goldRef = useRef(0), cofreRef = useRef(false), penRef = useRef(0), throwsRef = useRef(0);
  const [throwsLeft, setThrowsLeft] = useState(0);
  const [gold, setGold] = useState(0);
  useEffect(() => {
    const conf = HARPOON_CFG[cfg] || HARPOON_CFG.treasure;
    throwsRef.current = conf.throws; setThrowsLeft(conf.throws);
    const eng = new window.TankEngine(cvRef.current);
    engRef.current = eng;
    eng.setMode('free');
    eng.harpoon = window.GAME.harpoon('standard');
    eng.actTint = '#0e2a40';
    eng.onResolve = (items) => {
      items.forEach(({ it }) => {
        if (it.gold) goldRef.current += it.gold;
        if (it.cofre) cofreRef.current = true;
        if (it.eff && it.eff.selfHit) penRef.current += it.eff.selfHit;   // mina pescada
        if (it.hazard) penRef.current += (it.hazardDmg || 6);             // erizo rozado
      });
      setGold(goldRef.current);
      throwsRef.current -= 1; setThrowsLeft(throwsRef.current);
      if (throwsRef.current <= 0 || eng.objs.length === 0) finish();
      else setTimeout(() => eng.resume(), 420);
    };
    eng.spawn(conf.build(), { floor: 1 });
    eng.resume();   // desbloquea: sin esto el motor queda locked/idle y el arpón no apunta ni dispara
    const ro = () => eng.resize(); window.addEventListener('resize', ro);
    return () => { window.removeEventListener('resize', ro); eng.destroy(); };
  }, []);
  function finish() {
    if (doneRef.current) return; doneRef.current = true;
    const g = goldRef.current, pen = penRef.current, cofre = cofreRef.current;
    const eff = {}; if (g > 0) eff.gold = g; if (cofre) eff.relic = true; if (pen > 0) eff.hp = -pen;
    let msg = g > 0 ? `Recogiste ${g} oro` : 'No pescaste botín';
    if (cofre) msg += ' + un COFRE (¡RELIQUIA!)';
    if (pen > 0) msg += ` (−${pen} PV por minas)`;
    msg += '.';
    const color = cofre ? '#ffd23f' : (g > 0 ? '#9ae600' : '#ff5a3c');
    if (window.SFX) (g > 0 ? (window.SFX.win && window.SFX.win()) : (window.SFX.playerHit && window.SFX.playerHit()));
    setTimeout(() => onDone(eff, msg, color), 700);
  }
  return (
    <div className="col center" style={{ gap: 12 }}>
      <div className="pix" style={{ fontSize: 9, color:'#9db8c9' }}>APUNTA con el ratón · CLIC para lanzar el arpón · evita las MINAS</div>
      <canvas ref={cvRef} style={{ width: 600, height: 320, background:'#07131f', border:'3px solid #16242f', cursor:'crosshair', display:'block' }} />
      <div className="row" style={{ gap: 18, alignItems:'center' }}>
        <span className="pix" style={{ fontSize: 10, color:'#ffd23f' }}>BOTÍN: {gold} oro</span>
        <span className="pix" style={{ fontSize: 10, color:'#2ce8d8' }}>TIROS: {throwsLeft}</span>
      </div>
    </div>
  );
}

function EventScreen({ run, onResolve }) {
  const [ev] = useState(() => {
    const pool = [...EVENTS, ...MINIGAMES];
    return pool[Math.floor(Math.random() * pool.length)];
  });
  const [phase, setPhase] = useState('intro');     // intro | play | result
  const [result, setResult] = useState(null);      // {eff, msg, color}
  const isMini = !!ev.mini;

  const finishMini = (eff, msg, color) => { setResult({ eff, msg, color }); setPhase('result'); };

  return (
    <div className="fill col center" style={{ gap: 24, background:'radial-gradient(120% 90% at 50% 20%, #1d1233, #0a1b2a 75%)', padding: 30 }}>
      <div className="pix" style={{ fontSize: 18, color:'#b06bff', textShadow:'3px 3px 0 #000' }}>{isMini ? 'DESAFÍO' : 'EVENTO'}</div>
      {phase !== 'play' && <div className="bob"><Glyph name={ev.icon || 'ic_door'} scale={8} color="#b06bff" /></div>}

      {/* INTRO / texto */}
      {phase === 'intro' && <React.Fragment>
        <div className="body panel" style={{ fontSize: 24, color:'#eaf6ff', maxWidth: 620, padding: 20, textAlign:'center', lineHeight:1.1 }}>{ev.text}</div>
        {isMini
          ? <Btn variant="btn-primary" onClick={() => setPhase('play')} style={{ fontSize: 13, minWidth: 280 }}>EMPEZAR DESAFÍO ▶</Btn>
          : <div className="col" style={{ gap: 14 }}>
              {ev.opts.map((o, i) => (
                <Btn key={i} onClick={() => onResolve(o.eff)} style={{ fontSize:11, minWidth: 380 }}>{o.label}</Btn>
              ))}
            </div>}
      </React.Fragment>}

      {/* JUEGO */}
      {phase === 'play' && <HarpoonGame cfg={ev.cfg} onDone={finishMini} />}

      {/* RESULTADO */}
      {phase === 'result' && result && <React.Fragment>
        <div className="body panel" style={{ fontSize: 22, color: result.color, maxWidth: 560, padding: 18, textAlign:'center', lineHeight:1.2, borderColor: result.color }}>{result.msg}</div>
        <Btn variant="btn-primary" onClick={() => onResolve(result.eff)} style={{ fontSize: 12, minWidth: 240 }}>CONTINUAR ▶</Btn>
      </React.Fragment>}
    </div>
  );
}

/* ---------------- SHOP ---------------- */
function ConsumableCard({ con, onClick, label = 'CONSUMIBLE' }) {
  return (
    <div onClick={onClick} className="col center panel" {...window.tipProps({ title: con.name, color: con.color, icon: con.icon, body: con.desc })}
      style={{ width: 120, padding: 12, gap: 8, cursor: onClick ? 'pointer' : 'help', borderColor: con.color, background:'#0b1d2e' }}>
      <Glyph name={con.icon} scale={4} color={con.color} />
      <div className="pix" style={{ fontSize: 8, color: con.color, textAlign:'center', lineHeight:1.4 }}>{con.name}</div>
      <div className="pix" style={{ fontSize: 6, color:'#6f8aa0' }}>{label}</div>
    </div>
  );
}
const SHOP_REMOVE_COST = 25, SHOP_DUP_COST = 45;
function ShopScreen({ run, stock, cardDesign, onBuy, onRemove, onDuplicate, onLeave }) {
  const [bought, setBought] = useState({});
  const [mode, setMode] = useState('shop');         // shop | remove | duplicate
  const [usedRemove, setUsedRemove] = useState(false);
  const [usedDup, setUsedDup] = useState(false);

  if (mode === 'remove') {
    return <DeckPicker run={run} title={`FORJA · ELIMINA UNA CARTA (${SHOP_REMOVE_COST} ORO)`} cardDesign={cardDesign}
      onPick={(idx) => { onRemove(idx); setUsedRemove(true); setMode('shop'); }} onCancel={() => setMode('shop')} />;
  }
  if (mode === 'duplicate') {
    return <DeckPicker run={run} title={`FORJA · DUPLICA UNA CARTA (${SHOP_DUP_COST} ORO)`} cardDesign={cardDesign}
      onPick={(idx) => { onDuplicate(idx); setUsedDup(true); setMode('shop'); }} onCancel={() => setMode('shop')} />;
  }

  const canRemove = !usedRemove && run.gold >= SHOP_REMOVE_COST && run.deck.length > 4;
  const canDup = !usedDup && run.gold >= SHOP_DUP_COST;
  return (
    <div className="fill col" style={{ gap: 18, padding:'18px 16px 0', alignItems:'center', overflowY:'auto', background:'radial-gradient(120% 90% at 50% 10%, #2a2410, #0a1b2a 75%)' }}>
      <div className="pix" style={{ fontSize: 20, color:'#ffd23f', textShadow:'3px 3px 0 #000' }}>TIENDA DEL ABISMO</div>
      <div className="row" style={{ alignItems:'center', gap:8 }}>
        <Glyph name="ic_coin" scale={3} color="#ffd23f" /><span className="pix" style={{ fontSize:12, color:'#ffd23f' }}>{run.gold} ORO</span></div>
      <div className="row" style={{ gap: 26 }}>
        {stock.map((s, i) => {
          const owned = bought[i]; const canBuy = run.gold >= s.price && !owned;
          return (
            <div key={i} className="col center" style={{ gap: 8, opacity: owned?0.35:1 }}>
              {s.relic ? <RelicCard rel={s.relic} /> : s.consumable ? <ConsumableCard con={s.consumable} /> : s.harpoonMod ? <ConsumableCard con={s.harpoonMod} label="ARPÓN" /> : s.gadget ? <ConsumableCard con={s.gadget} label="ACTIVABLE" /> : <ItemCard it={s.it} size="lg" design={cardDesign} />}
              <Btn variant={canBuy?'btn-go':''} disabled={!canBuy}
                onClick={() => { setBought(b=>({...b,[i]:true})); onBuy(s); }} style={{ fontSize:10 }}>
                {owned ? 'COMPRADO' : `${s.price} ORO`}
              </Btn>
            </div>
          );
        })}
      </div>
      {/* forja: afinar la baraja (clave en deckbuilders tipo Dungeon Clawler) */}
      <div className="row center" style={{ gap:14, alignItems:'center', borderTop:'2px solid #16242f', paddingTop:14 }}>
        <span className="pix" style={{ fontSize:8, color:'#6f8aa0' }}>FORJA:</span>
        <Btn variant={canRemove?'btn-go':''} disabled={!canRemove} onClick={() => setMode('remove')} style={{ fontSize:9 }}>
          {usedRemove ? 'ELIMINADA' : `ELIMINAR CARTA · ${SHOP_REMOVE_COST}`}
        </Btn>
        <Btn variant={canDup?'btn-go':''} disabled={!canDup} onClick={() => setMode('duplicate')} style={{ fontSize:9 }}>
          {usedDup ? 'DUPLICADA' : `DUPLICAR CARTA · ${SHOP_DUP_COST}`}
        </Btn>
      </div>
      <div className="row center" style={{ position:'sticky', bottom:0, width:'100%', padding:'10px 0 14px', marginTop:'auto', zIndex:5, background:'linear-gradient(180deg, rgba(10,27,42,0) 0%, rgba(10,27,42,.94) 40%)' }}>
        <Btn onClick={onLeave} style={{ fontSize: 12 }}>SALIR ▶</Btn>
      </div>
    </div>
  );
}
window.SHOP_REMOVE_COST = SHOP_REMOVE_COST; window.SHOP_DUP_COST = SHOP_DUP_COST;

/* ---------------- DECK PICKER / VIEWER ---------------- */
function DeckPicker({ run, title, onPick, onCancel, cardDesign='token', filter, emptyText, tip }) {
  const entries = (run.deck || []).map((card, idx) => ({ card, idx })).filter(e => !filter || filter(e.card, e.idx));
  return (
    <div className="fill col center" style={{ gap: 18, background:'rgba(7,19,31,.97)', padding: 24 }}>
      <div className="pix" style={{ fontSize: 14, color:'#ffd23f', textShadow:'2px 2px 0 #000' }}>{title}</div>
      <div className="row" style={{ gap: 14, flexWrap:'wrap', justifyContent:'center', maxWidth: 1080, maxHeight: 560, overflowY:'auto' }}>
        {entries.map(({ card, idx }) => (
          <div key={idx} style={{ cursor: onPick?'pointer':'default' }}
            {...(tip ? window.tipProps(tip(card)) : {})}>
            <ItemCard it={window.GAME.item(card)} size="md" design={cardDesign} onClick={onPick ? () => onPick(idx) : undefined} />
          </div>
        ))}
        {!entries.length && <div className="body" style={{ fontSize:22, color:'#8aa6bf' }}>{emptyText || 'No hay cartas disponibles.'}</div>}
      </div>
      <Btn onClick={onCancel} style={{ fontSize: 11 }}>{onPick ? 'CANCELAR' : 'CERRAR'}</Btn>
    </div>
  );
}

/* ---------------- ACT INTRO (per act) ---------------- */
const ACT_INTRO = {
  2: { tag:'ACTO II', title:'EL ABISMO', titleColor:'#ff6b8b', bg:'radial-gradient(120% 90% at 50% 30%, #102a3a, #060d16 75%)',
       sprite:'kraken', scale:4, hue:150,
       body:(<span>El Kraken no era el final. Más abajo, las aguas giran solas... algo enorme las remueve desde el fondo. <span style={{color:'#2ce8d8'}}>EL MAELSTRÖM</span> te espera.</span>),
       cta:'DESCENDER AL ABISMO ▶' },
  3: { tag:'ACTO III', title:'EL CEMENTERIO DE ARPONES', titleColor:'#ff9f1c', bg:'radial-gradient(120% 90% at 50% 35%, #241018, #0a0608 75%)',
       sprite:'arponfago', scale:3, hue:0,
       body:(<span>Donde el Maelström se calma, el agua se vuelve un osario de arpones hundidos. Algo se mueve entre el hierro oxidado: la bestia que te dejó la cicatriz. <span style={{color:'#ff9f1c'}}>EL VIEJO ARPÓN</span> despierta.</span>),
       cta:'ENFRENTAR TU VENGANZA ▶' },
  4: { tag:'ACTO IV', title:'LA FOSA', titleColor:'#b06bff', bg:'radial-gradient(120% 90% at 50% 30%, #14102e, #04030c 78%)',
       sprite:'boss_tragaluz', scale:3, hue:0,
       body:(<span>El osario no tenía fondo. Sigues cayendo hasta una sima sin luz donde la presión aplasta y un único farol verde flota en la negrura. <span style={{color:'#b06bff'}}>EL TRAGALUZ</span> te ha estado esperando.</span>),
       cta:'HUNDIRTE EN LA FOSA ▶' },
  5: { tag:'ACTO V', title:'EL VELO NEGRO', titleColor:'#7fd6e6', bg:'radial-gradient(120% 90% at 50% 30%, #0a0820, #020108 80%)',
       sprite:'boss_nodriza', scale:3, hue:0,
       body:(<span>Bajo la Fosa la luz muere del todo. Solo laten medusas como faroles enfermos en una negrura que se cierra y se abre. En el centro del enjambre, <span style={{color:'#7fd6e6'}}>LA NODRIZA</span> respira. Aprende a disparar a ciegas.</span>),
       cta:'ENTRAR EN EL VELO ▶' },
  6: { tag:'ACTO VI', title:'LA CÁMARA DE RESONANCIA', titleColor:'#ffd23f', bg:'radial-gradient(120% 90% at 50% 30%, #241a33, #07040e 80%)',
       sprite:'boss_pearl_organ', scale:3, hue:0,
       body:(<span>Al otro lado del Velo se abre una sala antigua de conchas y tubos de coral, donde cada disparo resuena dos veces. Lo que afines, te servirá; lo que dejes vibrando, se corromperá. <span style={{color:'#ffd23f'}}>EL ÓRGANO DE PERLA</span> empieza a tocar.</span>),
       cta:'AFINAR EL ARPÓN ▶' },
  // TEMPORADA 2 — campaña nueva, gateada tras vencer la T1
  7: { tag:'TEMPORADA 2 · ACTO VII', title:'BANCOS MUTANTES', titleColor:'#1fd6c0', bg:'radial-gradient(120% 90% at 50% 30%, #07262b, #03100f 80%)',
       sprite:'boss_tideleviathan', scale:3, hue:0,
       body:(<span>Venciste a la Cámara, pero el océano ha cambiado. En los Bancos Mutantes el <span style={{color:'#7fc8ff'}}>agua misma</span> es parte del combate: zonas <span style={{color:'#7fc8ff'}}>claras</span> que afinan tu PERFECTO y corrientes <span style={{color:'#1fd6c0'}}>salinas</span> que empujan tus presas. Aprende a leer el medio. <span style={{color:'#1fd6c0'}}>EL LEVIATÁN DE MAREA</span> remueve el fondo.</span>),
       cta:'SURCAR LOS BANCOS ▶' },
  8: { tag:'TEMPORADA 2 · ACTO VIII', title:'LA SIMA PRESÓRICA', titleColor:'#ff6b6b', bg:'radial-gradient(120% 90% at 50% 30%, #15090c, #050203 82%)',
       sprite:'boss_crusher', scale:3, hue:0,
       body:(<span>Bajo los Bancos se abre una fosa sin fondo. La <span style={{color:'#ff6b6b'}}>PRESIÓN</span> aumenta cada turno: el tanque se estrecha y todo corre más rápido. Captura para <span style={{color:'#7fc8ff'}}>ventilar</span> o serás aplastado. <span style={{color:'#ff6b6b'}}>EL TRITURADOR</span> aguarda en el fondo.</span>),
       cta:'DESCENDER A LA SIMA ▶' },
  9: { tag:'TEMPORADA 2 · ACTO IX', title:'EL PECIO POLAR', titleColor:'#b06bff', bg:'radial-gradient(120% 90% at 50% 30%, #0a0a22, #030310 82%)',
       sprite:'boss_dynamo', scale:3, hue:0,
       body:(<span>Un cementerio de cascos imantados. Los objetos tienen <span style={{color:'#ff5050'}}>polo +</span> y <span style={{color:'#5a96ff'}}>polo −</span>; tu arpón lleva carga: los opuestos se ATRAEN al asta, los iguales la REPELEN. Planifica la curva. <span style={{color:'#b06bff'}}>EL DÍNAMO ABISAL</span> late en el pecio.</span>),
       cta:'CRUZAR EL PECIO ▶' },
  10:{ tag:'TEMPORADA 2 · ACTO X', title:'EL PRISMA ABISAL', titleColor:'#9ae6ff', bg:'radial-gradient(120% 90% at 50% 30%, #08161c, #02080c 82%)',
       sprite:'boss_abyssalembic', scale:3, hue:0,
       body:(<span>El último abismo es de cristal: <span style={{color:'#9ae6ff'}}>PRISMAS</span> que TUERCEN tu arpón como un billar de luz. Y en el centro, la criatura-máquina que <span style={{color:'#ffd23f'}}>destila el océano</span> y te obliga a dominar TODAS las aguas. <span style={{color:'#ffd23f'}}>EL ALAMBIQUE ABISAL</span> empieza su destilación.</span>),
       cta:'ROMPER EL PRISMA ▶' },
};
function ActIntro({ act = 2, onContinue }) {
  const a = ACT_INTRO[act] || ACT_INTRO[2];
  return (
    <div className="fill col center" style={{ gap: 24, background: a.bg }}>
      <div className="pix" style={{ fontSize: 14, color:'#2ce8d8', letterSpacing:6, textShadow:'2px 2px 0 #000' }}>{a.tag}</div>
      <div className="pix" style={{ fontSize: 38, color: a.titleColor, textShadow:'5px 5px 0 #07131f', textAlign:'center', lineHeight:1.1, maxWidth:900 }}>{a.title}</div>
      <div className="bob"><Sprite name={a.sprite} scale={a.scale} hue={a.hue} /></div>
      <div className="body panel" style={{ fontSize: 22, color:'#eaf6ff', maxWidth: 600, padding: 20, textAlign:'center', lineHeight:1.15, background:'#0b1d2e' }}>{a.body}</div>
      <Btn variant="btn-primary" onClick={onContinue} style={{ fontSize: 14, padding:'14px 30px' }}>{a.cta}</Btn>
    </div>
  );
}

/* ---------------- TESORO ABISAL (spend Perlas on permanent boons) ---------------- */
function MetaScreen({ onBack }) {
  const [tick, setTick] = useState(0);
  const perlas = window.GAME.getPerlas();
  const owned = window.GAME.getMeta();
  const list = Object.values(window.GAME.META);
  return (
    <div className="fill col" style={{ gap: 16, padding: 24, paddingBottom: 0, alignItems:'center', overflowY:'auto', background:'radial-gradient(120% 90% at 50% 15%, #1d1233, #07061a 80%)' }}>
      <div className="pix" style={{ fontSize: 22, color:'#b06bff', textShadow:'3px 3px 0 #000' }}>TESORO ABISAL</div>
      <div className="row" style={{ alignItems:'center', gap:8, background:'rgba(176,107,255,.12)', border:'2px solid #b06bff', padding:'4px 12px' }}>
        <Glyph name="ic_star" scale={3} color="#b06bff" /><span className="pix" style={{ fontSize:13, color:'#b06bff' }}>{perlas} PERLAS</span>
      </div>
      <div className="body" style={{ fontSize:16, color:'#9db8c9' }}>Mejoras PERMANENTES que se aplican a todas tus runs.</div>
      <div className="row" style={{ gap: 16, flexWrap:'wrap', justifyContent:'center', maxWidth: 980 }}>
        {list.map(u => {
          const has = owned.includes(u.id); const can = !has && perlas >= u.cost;
          return (
            <div key={u.id} className="col center panel" style={{ width: 170, padding: 14, gap: 8, background:'#0b1d2e', borderColor: has?'#9ae600':u.color, opacity: has?0.7:1 }}>
              <Glyph name={u.icon} scale={4} color={u.color} />
              <div className="pix" style={{ fontSize: 9, color: u.color, textAlign:'center', lineHeight:1.5 }}>{u.name}</div>
              <div className="body" style={{ fontSize: 14, color:'#cfe2ee', textAlign:'center', lineHeight:1.1, minHeight: 54 }}>{u.desc}</div>
              <Btn variant={can?'btn-go':''} disabled={!can}
                onClick={() => { if (window.GAME.buyMeta(u.id)) { if (window.SFX) window.SFX.relic ? window.SFX.relic() : (window.SFX.gold&&window.SFX.gold()); setTick(t=>t+1); } }}
                style={{ fontSize: 9 }}>{has ? 'EN POSESIÓN' : `${u.cost} ◈`}</Btn>
            </div>
          );
        })}
      </div>
      <div className="row center" style={{ position:'sticky', bottom:0, width:'100%', padding:'12px 0 14px', marginTop:'auto', zIndex:5, background:'linear-gradient(180deg, rgba(7,6,26,0) 0%, rgba(7,6,26,.94) 40%)' }}>
        <Btn variant="btn-primary" onClick={onBack} style={{ fontSize: 13, padding:'12px 26px' }}>◀ VOLVER</Btn>
      </div>
    </div>
  );
}

/* ---------------- BESTIARIO / CÓDICE ---------------- */
function CodexScreen({ onBack }) {
  const codex = window.GAME.getCodex();
  const all = Object.values(window.GAME.ENEMY_T);
  const seenN = all.filter(e => codex[e.key]).length;
  return (
    <div className="fill col" style={{ gap: 12, padding: 22, paddingBottom: 0, alignItems:'center', overflowY:'auto', background:'radial-gradient(120% 90% at 50% 12%, #102a3a, #07131f 80%)' }}>
      <div className="pix" style={{ fontSize: 22, color:'#2ce8d8', textShadow:'3px 3px 0 #000' }}>BESTIARIO</div>
      <div className="pix" style={{ fontSize: 10, color:'#9db8c9' }}>{seenN} / {all.length} criaturas registradas</div>
      <div className="row" style={{ gap: 12, flexWrap:'wrap', justifyContent:'center', maxWidth: 1100, maxHeight: 540, overflowY:'auto' }}>
        {all.map(e => {
          const seen = !!codex[e.key];
          const tag = e.boss ? 'JEFE' : e.elite ? 'ÉLITE' : (e.mod ? e.mod.toUpperCase() : 'ENEMIGO');
          return (
            <div key={e.key} className="col center panel" style={{ width: 150, height: 150, padding: 8, gap: 4, justifyContent:'center',
              background:'#0b1d2e', borderColor: seen ? (e.boss?'#ff6b8b':'#16344b') : '#16242f' }}
              {...(seen ? window.tipProps({ title:e.name, color: e.boss?'#ff6b8b':'#2ce8d8', body: e.tankRule || ('Vida base '+e.maxHp+'.') }) : {})}>
              <div style={{ filter: seen ? 'none' : 'brightness(0) opacity(.45)' }}>
                <Sprite name={e.sprite} scale={seen && (e.boss) ? 2 : 3} hue={e.hue || 0} />
              </div>
              <div className="pix" style={{ fontSize: seen?8:9, color: seen?'#fff':'#5a7088', textAlign:'center', lineHeight:1.4 }}>{seen ? e.name : '???'}</div>
              {seen && <div className="pix" style={{ fontSize:6, color: e.boss?'#ff6b8b':'#6f8aa0' }}>{tag}</div>}
            </div>
          );
        })}
      </div>
      <div className="row center" style={{ position:'sticky', bottom:0, width:'100%', padding:'12px 0 14px', marginTop:'auto', zIndex:5, background:'linear-gradient(180deg, rgba(7,19,31,0) 0%, rgba(7,19,31,.94) 40%)' }}>
        <Btn variant="btn-primary" onClick={onBack} style={{ fontSize: 13, padding:'12px 26px' }}>◀ VOLVER</Btn>
      </div>
    </div>
  );
}

/* ---------------- LOGROS ---------------- */
function LogrosScreen({ onBack }) {
  const ach = window.GAME.getAch();
  const all = Object.values(window.GAME.ACH);
  const done = all.filter(a => ach[a.id]).length;
  return (
    <div className="fill col" style={{ gap: 12, padding: 22, paddingBottom: 0, alignItems:'center', overflowY:'auto', background:'radial-gradient(120% 90% at 50% 12%, #1d2410, #07131f 80%)' }}>
      <div className="pix" style={{ fontSize: 22, color:'#ffd23f', textShadow:'3px 3px 0 #000' }}>LOGROS</div>
      <div className="pix" style={{ fontSize: 10, color:'#9db8c9' }}>{done} / {all.length} conseguidos</div>
      <div className="col" style={{ gap: 8, maxWidth: 620, maxHeight: 540, overflowY:'auto' }}>
        {all.map(a => { const got = !!ach[a.id]; return (
          <div key={a.id} className="row" style={{ alignItems:'center', gap:12, padding:'8px 12px', background:'#0b1d2e',
            border:'3px solid '+(got?'#9ae600':'#16242f'), opacity: got?1:0.7 }}>
            <Glyph name={got?'ic_star':'ic_anchor'} scale={3} color={got?'#9ae600':'#5a7088'} />
            <div className="col" style={{ gap:2, flex:1 }}>
              <div className="pix" style={{ fontSize:9, color: got?'#fff':'#9db8c9' }}>{a.name}</div>
              <div className="body" style={{ fontSize:15, color:'#9db8c9', lineHeight:1.05 }}>{a.desc}</div>
            </div>
            <div className="row" style={{ alignItems:'center', gap:4 }}>
              <Glyph name="ic_star" scale={2} color="#b06bff" /><span className="pix" style={{ fontSize:9, color:'#b06bff' }}>{a.perlas}</span>
            </div>
          </div>
        ); })}
      </div>
      <div className="row center" style={{ position:'sticky', bottom:0, width:'100%', padding:'12px 0 14px', marginTop:'auto', zIndex:5, background:'linear-gradient(180deg, rgba(7,19,31,0) 0%, rgba(7,19,31,.94) 40%)' }}>
        <Btn variant="btn-primary" onClick={onBack} style={{ fontSize: 13, padding:'12px 26px' }}>◀ VOLVER</Btn>
      </div>
    </div>
  );
}

/* ---------------- ENDINGS ---------------- */
function EndScreen({ win, run, unlocked, onRestart }) {
  const newly = (unlocked || []).map(id => (window.GAME.CHARS.find(c => c.id === id) || {}).name).filter(Boolean);
  const loss = run.lossSummary || {};
  const min = Math.floor((loss.elapsedMs || 0) / 60000);
  const sec = Math.floor(((loss.elapsedMs || 0) % 60000) / 1000);
  const timeLabel = loss.elapsedMs ? `${min}:${String(sec).padStart(2, '0')}` : '—';
  return (
    <div className="fill col center" style={{ gap: 24,
      background: win ? 'radial-gradient(120% 90% at 50% 30%, #2a2410, #07131f 75%)' : 'radial-gradient(120% 90% at 50% 30%, #2a1212, #07131f 75%)' }}>
      <div className="pix" style={{ fontSize: 40, color: win?'#ffd23f':'#ff3b3b', textShadow:'5px 5px 0 #000' }}>
        {win ? '¡VENGANZA CUMPLIDA!' : 'GAME OVER'}
      </div>
      <div className={win?'bob':''} style={{ filter: win?'none':'grayscale(1) brightness(.6)' }}>
        <Sprite name={run.char.sprite || 'tuntun'} scale={7} hue={run.char.hue} />
      </div>
      <div className="body" style={{ fontSize: 22, color:'#9db8c9', maxWidth: 640, textAlign:'center', lineHeight:1.1 }}>
        {win ? 'El Viejo Arpón yace por fin en la fosa. La cicatriz deja de doler. La venganza está cumplida.' : `${run.char.name} cayó en el abismo. La última sacudida vino de ${loss.cause || 'las aguas negras'}.`}
      </div>
      {!win && (
        <div className="row center panel" style={{ gap:18, padding:'12px 18px', background:'#0b1d2e', borderColor:'#ff5a3c', flexWrap:'wrap', maxWidth:760 }}>
          {[
            ['ACTO', loss.act || run.act],
            ['PISO', loss.floor == null ? run.floor : loss.floor],
            ['ORO', loss.gold == null ? run.gold : loss.gold],
            ['BARAJA', loss.deckCount || run.deck.length],
            ['RELIQUIAS', loss.relicCount || (run.relics || []).length],
            ['TIEMPO', timeLabel],
          ].map(([k,v]) => (
            <div key={k} className="col center" style={{ gap:4, minWidth:76 }}>
              <div className="pix" style={{ fontSize:7, color:'#6f8aa0' }}>{k}</div>
              <div className="pix" style={{ fontSize:12, color:'#eaf6ff' }}>{v}</div>
            </div>
          ))}
        </div>
      )}
      {win && (
        <div className="row center" style={{ alignItems:'center', gap:10, background:'rgba(176,107,255,.12)', border:'3px solid #b06bff', padding:'8px 16px' }}>
          <Glyph name="ic_star" scale={3} color="#b06bff" />
          <span className="pix" style={{ fontSize:12, color:'#b06bff' }}>+{run.perlasEarned || 0} PERLAS ABISALES</span>
          <span className="pix" style={{ fontSize:8, color:'#9db8c9' }}>(AMENAZA {run.amenaza || 0})</span>
        </div>
      )}
      {win && newly.length > 0 && (
        <div className="col center panel" style={{ gap:8, padding:'14px 22px', background:'#16344b', border:'4px solid #ffd23f' }}>
          <div className="pix" style={{ fontSize:11, color:'#ffd23f', textShadow:'2px 2px 0 #000' }}>★ PERSONAJE DESBLOQUEADO ★</div>
          <div className="pix" style={{ fontSize:14, color:'#fff' }}>{newly.join(' · ')}</div>
        </div>
      )}
      <Btn variant="btn-primary" onClick={onRestart} style={{ fontSize: 14, padding:'14px 28px' }}>OTRA VEZ ▶</Btn>
    </div>
  );
}

/* ---------------- REVELADO DE RECOMPENSA (cofre / botín de evento) ---------------- */
function RewardReveal({ reveal, onDone }) {
  const [opened, setOpened] = useState(!reveal.chest);
  useEffect(() => {
    if (reveal.chest) { const id = setTimeout(() => { setOpened(true); if (window.SFX) (window.SFX.relic||window.SFX.win||function(){})(); }, 950); return () => clearTimeout(id); }
    if (window.SFX && window.SFX.gold) window.SFX.gold();
  }, []);
  const G = window.GAME;
  const items = [];
  if (reveal.relic) items.push(<RelicCard key="rel" rel={G.relic(reveal.relic)} />);
  if (reveal.card) items.push(<ItemCard key="card" it={G.item(reveal.card)} size="lg" design="token" />);
  if (reveal.harpoonMod) items.push(<ConsumableCard key="hm" con={G.harpoonMod(reveal.harpoonMod)} label="ARPÓN" />);
  if (reveal.consumable) items.push(<ConsumableCard key="co" con={G.consumable(reveal.consumable)} />);
  if (reveal.gadget) items.push(<ConsumableCard key="ga" con={G.gadget(reveal.gadget)} label="ACTIVABLE" />);
  const lines = [];
  if (reveal.gold) lines.push(['ic_coin','#ffd23f', (reveal.gold>0?'+':'')+reveal.gold+' ORO']);
  if (reveal.hp) lines.push(['ic_heart','#ff6b8b', (reveal.hp>0?'+':'')+reveal.hp+' PV']);
  if (reveal.maxHp) lines.push(['ic_heart','#ff4fa3', (reveal.maxHp>0?'+':'')+reveal.maxHp+' PV MÁX']);
  if (reveal.curse) lines.push(['ic_junk','#9db8c9','CARTA MALDITA']);
  return (
    <div className="fill col center" style={{ gap:18, background:'radial-gradient(120% 90% at 50% 25%, #1d1a0e, #07061a 80%)' }}>
      {reveal.chest && !opened ? (
        <div className="col center" style={{ gap:14 }}>
          <div className="pix" style={{ fontSize:14, color:'#ffd23f', textShadow:'2px 2px 0 #000' }}>¡UN COFRE!</div>
          <div className="bob" style={{ position:'relative', width:120, height:96 }}>
            <div style={{ position:'absolute', left:0, bottom:0, width:120, height:64, background:'#7a4a1c', border:'4px solid #3a2410', borderRadius:'4px', boxShadow:'inset 0 -8px 0 rgba(0,0,0,.3)' }} />
            <div style={{ position:'absolute', left:54, bottom:24, width:14, height:18, background:'#ffd23f', border:'3px solid #3a2410' }} />
            <div className="chest-lid" style={{ position:'absolute', left:0, bottom:60, width:120, height:30, background:'#8a5520', border:'4px solid #3a2410', borderRadius:'8px 8px 0 0', transformOrigin:'bottom center', animation:'chestRattle .5s infinite' }} />
          </div>
          <div className="pix blink" style={{ fontSize:10, color:'#9db8c9' }}>abriendo…</div>
        </div>
      ) : (
        <React.Fragment>
          <div className="pix" style={{ fontSize:16, color:'#9ae600', textShadow:'3px 3px 0 #000' }}>{reveal.chest ? '¡RELIQUIA OBTENIDA!' : 'BOTÍN'}</div>
          {items.length > 0 && <div className="row center pop" style={{ gap:18, flexWrap:'wrap' }}>{items}</div>}
          {lines.length > 0 && (
            <div className="row center" style={{ gap:16, flexWrap:'wrap' }}>
              {lines.map((l,i) => (
                <div key={i} className="row" style={{ alignItems:'center', gap:6, border:'2px solid '+l[1], padding:'5px 12px', background:'#0b1d2e' }}>
                  <Glyph name={l[0]} scale={3} color={l[1]} /><span className="pix" style={{ fontSize:10, color:l[1] }}>{l[2]}</span>
                </div>
              ))}
            </div>
          )}
          <Btn variant="btn-primary" onClick={onDone} style={{ fontSize:13, padding:'12px 30px' }}>CONTINUAR ▶</Btn>
        </React.Fragment>
      )}
    </div>
  );
}

/* ---------------- WIKI / AYUDA ---------------- */
const WIKI = [
  { t:'PESCA', c:'#ffd23f', icon:'ic_bolt', d:'Apunta y dispara el arpón para PESCAR los objetos (cartas) del tanque. Cada objeto pescado aplica su efecto. Tienes varios LANZAMIENTOS por turno.' },
  { t:'¡PERFECTO!', c:'#ffd23f', icon:'ic_star', d:'Si enganchas con el NÚCLEO central del objeto, es ¡PERFECTO!: efecto x1.5 (o x2 con reliquia) y un efecto físico extra. Objetos estáticos son fáciles; los rápidos, difíciles.' },
  { t:'AFINIDAD', c:'#2ce8d8', icon:'ic_drop', d:'Cada objeto es de una afinidad: FILO, TOXINA, MAREA, BESTIA o CHATARRA (su color). Define sinergias y Resonancia.' },
  { t:'RESONANCIA', c:'#ff4fa3', icon:'ic_swirl', d:'Encadenar objetos de la MISMA afinidad en un mismo tiro da un bono creciente de daño/bloqueo. Premia planear la ruta del arpón.' },
  { t:'ACTIVABLES (enemigo PREPARA)', c:'#b06bff', icon:'ic_skull', d:'El enemigo telegrafía algo que se resuelve en TU próximo turno (anclas, corrientes, niebla, agua...). Lee el aviso y planea alrededor.' },
  { t:'RUTAS DE ARPÓN', c:'#9ae600', icon:'ic_swirl', d:'Recto, Rebote, Zigzag, Espiral... Cámbialas con el botón "ARPÓN ⚙" junto al lanzador. Cada cazador tiene además su propio arpón especial.' },
  { t:'TIPOS DE AGUA (Temp. 2)', c:'#7fc8ff', icon:'ic_drop', d:'CLARA: amplía el PERFECTO. SALINA: empuja objetos. NEGRA: pescas a ciegas pero das +oro. TERMAL: el arpón corre más y quema. VIVA: cura al pescar dentro. MUERTA: apaga Resonancia y neutrales.' },
  { t:'RELIQUIAS', c:'#ffd23f', icon:'ic_star', d:'Mejoras pasivas que duran toda la run (las ganas en élites/eventos). Definen tu build.' },
  { t:'BLOQUEO / ESTADOS', c:'#2ce8d8', icon:'ic_shield', d:'El BLOQUEO absorbe daño ese turno (se reinicia). VENENO/QUEMADURA dañan al final del turno; DÉBIL/VULNERABLE/FRÁGIL alteran daño y bloqueo.' },
  { t:'AMENAZA', c:'#ff5a3c', icon:'ic_up', d:'Niveles de dificultad ascendente: enemigos más duros y reglas extra, a cambio de más recompensa (Perlas).' },
  { t:'TEMPORADAS', c:'#1fd6c0', icon:'ic_anchor', d:'Temporada 1: actos I–VI. Temporada 2 "Bancos Mutantes" (actos VII–X): el AGUA es parte de la build. Se desbloquea al ganar la T1.' },
  { t:'TESORO ABISAL / PERLAS', c:'#b06bff', icon:'ic_coin', d:'Al completar runs ganas PERLAS para comprar mejoras PERMANENTES en el Tesoro Abisal (entre runs).' },
];
function WikiModal({ onClose }) {
  return (
    <div onClick={onClose} style={{ position:'fixed', inset:0, zIndex:10000, background:'rgba(4,9,16,.8)', display:'flex', alignItems:'center', justifyContent:'center', padding:16 }}>
      <div onClick={e => e.stopPropagation()} className="col" style={{ gap:10, background:'#0b1d2e', border:'3px solid #2ce8d8', borderRadius:10, padding:18, width:'min(680px,94vw)', maxHeight:'88%', overflowY:'auto' }}>
        <div className="row" style={{ justifyContent:'space-between', alignItems:'center' }}>
          <span className="pix" style={{ fontSize:14, color:'#2ce8d8' }}>📖 GUÍA RÁPIDA</span>
          <span onClick={onClose} className="pix" style={{ fontSize:12, color:'#9db8c9', cursor:'pointer' }}>✕ CERRAR</span>
        </div>
        <div className="body" style={{ fontSize:13, color:'#6f8aa0' }}>Conceptos clave del juego. Pulsa fuera o ✕ para cerrar.</div>
        {WIKI.map((w,i) => (
          <div key={i} className="row" style={{ alignItems:'flex-start', gap:10, padding:'8px 10px', border:'2px solid '+w.c, background:'#0e2336' }}>
            <Glyph name={w.icon} scale={3} color={w.c} />
            <div className="col" style={{ gap:3, flex:1 }}>
              <span className="pix" style={{ fontSize:9, color:w.c, lineHeight:1.4 }}>{w.t}</span>
              <span className="body" style={{ fontSize:14, color:'#cfe2ee', lineHeight:1.2 }}>{w.d}</span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { TitleScreen, CharacterSelect, RewardScreen, RestScreen, EventScreen, ShopScreen, DeckPicker, EndScreen, ActIntro, MetaScreen, CodexScreen, LogrosScreen, WikiModal, RewardReveal });
