// game-levels.jsx — Niveles 1-4: educational levels that build understanding
// before the final "A pronosticar" game (existing rounds, now Nivel 5).

// ───────────────────────────────────────────────────────────────
// Shared data — the 3 sources, used across levels
// ───────────────────────────────────────────────────────────────
const SOURCES = {
  cams: {
    id:'cams', icon:'🛰️', short:'CAMS',
    name:'CAMS · Satélites',
    full:'Servicio Copernicus de Vigilancia Atmosférica',
    where:'El cielo · 36.000 km',
    color:'#A78BFA',
    tagline:'Ve todo el planeta',
    sees:[
      'Polvo del Sahara cruzando el Atlántico',
      'Humo de incendios en el Amazonas',
      'Cenizas volcánicas, gases industriales',
      'Tendencias globales del aire',
    ],
    cannot:[
      'Detalle dentro del valle (40 km de píxel)',
      'Diferenciar barrios o calles',
      'Medir el aire que respiras tú',
    ],
    freq:'Cada 12 h · cobertura global',
  },
  wrf: {
    id:'wrf', icon:'🌬️', short:'WRF',
    name:'WRF · Modelo meteorológico',
    full:'Weather Research and Forecasting Model',
    where:'El valle · simulación 1 km',
    color:'#0DCCC0',
    tagline:'Simula el viento del valle',
    sees:[
      'Viento entre las montañas a cada hora',
      'Inversión térmica de madrugada',
      'Lluvia, humedad, temperatura',
      'Hacia dónde se va a mover el aire',
    ],
    cannot:[
      'PM2.5 directamente (no lo mide — lo estima de forma indirecta y con menos precisión)',
      'Quemas o emisiones inesperadas',
      'Eventos repentinos entre corrida y corrida',
    ],
    freq:'1 corrida/día · más de 72 h adelante',
  },
  sens: {
    id:'sens', icon:'🌡️', short:'Sensores',
    name:'Sensores en tierra',
    full:'Red SIATA · 20 estaciones del Valle',
    where:'Las estaciones · al nivel del suelo',
    color:'#84BD45',
    tagline:'Mide la realidad ahora mismo',
    sees:[
      'PM2.5 real, minuto a minuto',
      'Diferencias entre barrios',
      'Picos de tráfico o quemas locales',
      'Si el modelo se equivocó',
    ],
    cannot:[
      'Ver hacia el futuro',
      'Cubrir zonas sin estaciones',
      'Detectar polvo antes de que llegue',
    ],
    freq:'Cada minuto · 20 puntos del Valle',
  },
};

// ═══════════════════════════════════════════════════════════════
// NIVEL 1 — LAS FUENTES (Sources discovery + mini-quiz)
// ═══════════════════════════════════════════════════════════════
function Level1Sources({onNext}){
  const [flipped, setFlipped] = React.useState({});
  const [quizAns, setQuizAns] = React.useState({});

  const QUIZ = [
    {q:'¿Quién detecta polvo del Sahara antes de que llegue?', correct:'cams'},
    {q:'¿Quién mide el aire que respiras en este momento?', correct:'sens'},
    {q:'¿Quién simula hacia dónde va el viento del valle?', correct:'wrf'},
  ];

  const allFlipped = Object.keys(SOURCES).every(k => flipped[k]);
  const allAnswered = QUIZ.every((_,i) => quizAns[i]);
  const allCorrect = QUIZ.every((q,i) => quizAns[i] === q.correct);

  return (
    <div style={{maxWidth:1500, margin:'0 auto', padding:'56px 40px'}}>
      <LevelHead n="01" total="05" name="Las fuentes"
        title="Conoce a los 3 héroes del pronóstico"
        sub="El SIATA no usa un solo dato — combina tres fuentes muy distintas, cada una mirando el aire desde un ángulo diferente. Toca cada tarjeta para conocerlas." />

      <div style={{
        display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:24,
        marginTop:20,
      }}>
        {Object.values(SOURCES).map((s, i) => (
          <SourceFlipCard key={s.id} source={s}
            flipped={!!flipped[s.id]}
            onFlip={()=> setFlipped({...flipped, [s.id]:true})}
            delay={i} />
        ))}
      </div>

      {allFlipped && (
        <div className="fade-up" style={{marginTop:60}}>
          <div style={{
            fontFamily:'var(--mono)', fontSize:13, color:'var(--cyan)',
            letterSpacing:'0.24em', textTransform:'uppercase',
            textAlign:'center', marginBottom:20,
          }}>Pequeño reto · empareja</div>
          <h2 style={{
            textAlign:'center', fontSize:38, fontWeight:700,
            lineHeight:1.15, marginBottom:40, maxWidth:820, margin:'0 auto 40px',
            textWrap:'balance',
          }}>¿A quién le toca cada trabajo?</h2>

          <div style={{display:'flex', flexDirection:'column', gap:22}}>
            {QUIZ.map((qq, qi) => {
              const ans = quizAns[qi];
              const isCorrect = ans === qq.correct;
              return (
                <div key={qi} style={{
                  padding:'28px 32px',
                  background: ans
                    ? (isCorrect ? 'rgba(132,189,69,0.10)' : 'rgba(232,115,90,0.10)')
                    : 'rgba(255,255,255,0.025)',
                  border:'1px solid ' + (ans
                    ? (isCorrect ? 'rgba(132,189,69,0.45)' : 'rgba(232,115,90,0.45)')
                    : 'rgba(255,255,255,0.1)'),
                  borderRadius:14,
                  display:'flex', alignItems:'center', gap:28, flexWrap:'wrap',
                }}>
                  <div style={{fontSize:26, fontWeight:600, flex:'1 1 320px', minWidth:280, lineHeight:1.4}}>
                    {qq.q}
                  </div>
                  <div style={{display:'flex', gap:12}}>
                    {Object.values(SOURCES).map(s => {
                      const sel = ans === s.id;
                      const showResult = !!ans;
                      const isRight = s.id === qq.correct;
                      return (
                        <button key={s.id}
                          onClick={()=> !ans && setQuizAns({...quizAns, [qi]:s.id})}
                          disabled={!!ans}
                          style={{
                            cursor: ans?'default':'pointer',
                            padding:'16px 22px', borderRadius:12,
                            background: sel
                              ? (isCorrect ? 'rgba(132,189,69,0.25)' : 'rgba(232,115,90,0.25)')
                              : (showResult && isRight ? 'rgba(132,189,69,0.15)' : 'rgba(255,255,255,0.05)'),
                            border:'2px solid ' + (sel
                              ? (isCorrect ? '#84BD45' : '#E8735A')
                              : (showResult && isRight ? '#84BD45' : 'rgba(255,255,255,0.12)')),
                            color:'#fff', display:'flex', alignItems:'center', gap:10,
                            fontFamily:'var(--font)', fontSize:17, fontWeight:500,
                          }}>
                          <span style={{fontSize:32}}>{s.icon}</span>
                          {s.short}
                          {showResult && sel && (
                            <span style={{fontSize:20}}>{isCorrect?'✓':'✗'}</span>
                          )}
                          {showResult && !sel && isRight && (
                            <span style={{fontSize:18, color:'#84BD45'}}>★</span>
                          )}
                        </button>
                      );
                    })}
                  </div>
                </div>
              );
            })}
          </div>

          {allAnswered && (
            <div className="fade-up" style={{marginTop:40, textAlign:'center'}}>
              <PrimaryBtn onClick={onNext} variant="green">
                Siguiente nivel · qué significa cada uno →
              </PrimaryBtn>
            </div>
          )}
        </div>
      )}

      {!allFlipped && (
        <div style={{marginTop:40, textAlign:'center',
          fontFamily:'var(--mono)', fontSize:14, color:'var(--dim)',
          letterSpacing:'0.2em', textTransform:'uppercase'}}>
          ↑ Toca las 3 tarjetas para descubrir el reto
        </div>
      )}
    </div>
  );
}

function SourceFlipCard({source: s, flipped, onFlip, delay}){
  return (
    <div className={'fade-up delay-' + ((delay+1)%5)}
      onClick={onFlip}
      style={{
        cursor: flipped ? 'default' : 'pointer',
        minHeight: 560,
        perspective:'1200px',
      }}>
      <div style={{
        position:'relative', width:'100%', height:'100%', minHeight:560,
        transformStyle:'preserve-3d',
        transition:'transform 0.7s cubic-bezier(.2,.7,.2,1)',
        transform: flipped ? 'rotateY(180deg)' : 'rotateY(0)',
      }}>
        {/* FRONT */}
        <div style={{
          position:'absolute', inset:0, backfaceVisibility:'hidden',
          borderRadius:14, padding:'48px 36px',
          background:'linear-gradient(135deg, ' + s.color + '22, ' + s.color + '06)',
          border:'2px solid ' + s.color + '55',
          display:'flex', flexDirection:'column', alignItems:'center',
          justifyContent:'center', textAlign:'center',
        }}>
          <div style={{
            width:160, height:160, borderRadius:'50%',
            background: s.color + '20', border:'2px solid ' + s.color,
            display:'flex', alignItems:'center', justifyContent:'center',
            fontSize:96, marginBottom:30,
            boxShadow:'0 0 60px ' + s.color + '40',
          }}>{s.icon}</div>
          <div style={{
            fontFamily:'var(--mono)', fontSize:17, color: s.color,
            letterSpacing:'0.22em', textTransform:'uppercase', fontWeight:600,
          }}>Fuente · {s.short}</div>
          <div style={{fontSize:42, fontWeight:700, marginTop:18, textWrap:'balance', lineHeight:1.1, letterSpacing:'-0.01em'}}>{s.tagline}</div>
          <div style={{
            marginTop:34, padding:'14px 26px',
            background:'rgba(255,255,255,0.08)', borderRadius:30,
            fontSize:16, fontFamily:'var(--mono)', color:'var(--gray)',
            letterSpacing:'0.18em', textTransform:'uppercase',
          }}>Toca para conocer →</div>
        </div>

        {/* BACK */}
        <div style={{
          position:'absolute', inset:0, backfaceVisibility:'hidden',
          transform:'rotateY(180deg)',
          borderRadius:14, padding:'34px 32px',
          background:'linear-gradient(180deg, rgba(16,53,94,0.7), rgba(14,44,77,0.95))',
          border:'2px solid ' + s.color,
          boxShadow:'0 12px 40px ' + s.color + '30',
          overflowY:'auto',
        }}>
          <div style={{display:'flex', alignItems:'center', gap:20, marginBottom:28}}>
            <div style={{
              width:96, height:96, borderRadius:18,
              background: s.color, fontSize:54,
              display:'flex', alignItems:'center', justifyContent:'center',
              flexShrink:0,
            }}>{s.icon}</div>
            <div style={{flex:1, minWidth:0}}>
              <div style={{fontSize:30, fontWeight:700, lineHeight:1.15}}>{s.name}</div>
              <div style={{fontSize:19, color:'var(--gray)', marginTop:8, lineHeight:1.4}}>{s.full}</div>
            </div>
          </div>

          <div style={{
            fontSize:18, color: s.color, fontFamily:'var(--mono)',
            letterSpacing:'0.16em', textTransform:'uppercase', marginBottom:16,
            fontWeight:700,
          }}>✓ Lo que ve</div>
          <ul style={{listStyle:'none', display:'flex', flexDirection:'column', gap:16, marginBottom:28}}>
            {s.sees.map((t,i)=>(
              <li key={i} style={{
                fontSize:22, color:'#fff', lineHeight:1.5,
                display:'flex', gap:14,
              }}>
                <span style={{color: s.color, fontSize:26, lineHeight:1.1, flexShrink:0}}>•</span>
                <span>{t}</span>
              </li>
            ))}
          </ul>

          <div style={{
            fontSize:18, color:'#E8735A', fontFamily:'var(--mono)',
            letterSpacing:'0.16em', textTransform:'uppercase', marginBottom:16,
            fontWeight:700,
          }}>✗ Lo que NO ve</div>
          <ul style={{listStyle:'none', display:'flex', flexDirection:'column', gap:16, marginBottom:28}}>
            {s.cannot.map((t,i)=>(
              <li key={i} style={{
                fontSize:21, color:'var(--gray)', lineHeight:1.5,
                display:'flex', gap:14,
              }}>
                <span style={{color:'#E8735A', fontSize:26, lineHeight:1.1, flexShrink:0}}>•</span>
                <span>{t}</span>
              </li>
            ))}
          </ul>

          <div style={{
            padding:'16px 22px', borderRadius:12,
            background: s.color + '20', border:'1px solid ' + s.color + '50',
            fontFamily:'var(--mono)', fontSize:17, color:'#fff',
            letterSpacing:'0.12em', textTransform:'uppercase',
            fontWeight:600,
          }}>🕒 {s.freq}</div>
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// NIVEL 2 — QUÉ SIGNIFICA CADA UNO (Scenario sorting)
// ═══════════════════════════════════════════════════════════════
const SCENARIOS = [
  {
    id:'sahara', emoji:'🏜️',
    text:'Una nube de polvo del Sahara va a cruzar el Atlántico y llegar a Colombia en 5 días.',
    best:'cams',
    why:'Solo los satélites globales ven el polvo viajando sobre el océano. Los modelos locales y los sensores no lo detectan hasta que ya llegó.',
  },
  {
    id:'inversion', emoji:'🌫️',
    text:'Mañana a las 6 a.m. va a haber inversión térmica fuerte: el aire frío se queda atrapado abajo.',
    best:'wrf',
    why:'El modelo meteorológico simula la atmósfera y predice cuándo y dónde se va a formar la inversión — los satélites no lo ven, y los sensores se enteran cuando ya está pasando.',
  },
  {
    id:'leña', emoji:'🔥',
    text:'En el barrio Manrique alguien está quemando leña ahora mismo y el aire local subió.',
    best:'sens',
    why:'Solo los sensores en tierra detectan eventos puntuales y locales en tiempo real. Satélites y modelos no tienen esa resolución.',
  },
  {
    id:'incendio', emoji:'🌳',
    text:'Hay incendios forestales en el Amazonas brasilero y el humo se mueve hacia el norte.',
    best:'cams',
    why:'CAMS detecta plumas de humo a escala continental. WRF puede simular hacia dónde irá, pero CAMS lo ve primero.',
  },
  {
    id:'lluvia', emoji:'🌧️',
    text:'Mañana en la tarde va a llover fuerte y eso va a lavar el aire del valle.',
    best:'wrf',
    why:'El modelo meteorológico predice la lluvia y dónde caerá. Los sensores la miden cuando ya cae; los satélites la ven pero no la pronostican.',
  },
  {
    id:'rio', emoji:'📊',
    text:'Queremos saber si el aire de Bello tiene más PM2.5 que el aire del Poblado ahora mismo.',
    best:'sens',
    why:'Comparar barrios es lo que mejor hacen los sensores: cada estación es un punto fijo midiendo en su zona. Los modelos suavizan; los satélites ni siquiera distinguen barrios.',
  },
];

function Level2Meaning({onNext}){
  const [picks, setPicks] = React.useState({});
  const allDone = SCENARIOS.every(s => picks[s.id]);
  const correctCount = SCENARIOS.filter(s => picks[s.id] === s.best).length;

  return (
    <div style={{maxWidth:1500, margin:'0 auto', padding:'56px 40px'}}>
      <LevelHead n="02" total="05" name="Qué significa cada uno"
        title="Ya conoces las 3 fuentes. ¿Sabes cuándo usar cada una?"
        sub="Cada situación necesita la fuente correcta. Lee el escenario y escoge quién es el héroe del caso. Si te equivocas, te explico por qué." />

      <div style={{
        display:'grid', gridTemplateColumns:'repeat(2, 1fr)', gap:22,
        marginTop:12,
      }}>
        {SCENARIOS.map((sc, i) => (
          <ScenarioCard key={sc.id} sc={sc}
            picked={picks[sc.id]}
            onPick={(sid)=> setPicks({...picks, [sc.id]:sid})}
            delay={i} />
        ))}
      </div>

      <div style={{marginTop:40, textAlign:'center'}}>
        {allDone && (
          <div className="fade-up" style={{
            marginBottom:24, fontSize:18, color:'var(--gray)',
          }}>
            <strong style={{color: correctCount >= 4 ? '#84BD45' : '#FFB300', fontSize:24}}>
              {correctCount} / {SCENARIOS.length}
            </strong> escenarios acertados
          </div>
        )}
        <PrimaryBtn onClick={onNext} disabled={!allDone} variant={allDone?'green':'cyan'}>
          {allDone ? 'Siguiente · las matemáticas →' : 'Clasifica los 6 escenarios para continuar'}
        </PrimaryBtn>
      </div>
    </div>
  );
}

function ScenarioCard({sc, picked, onPick, delay}){
  const correct = picked === sc.best;
  return (
    <div className={'fade-up delay-' + ((delay%5)+1)} style={{
      padding:'26px 28px',
      borderRadius:14,
      background: picked
        ? (correct ? 'rgba(132,189,69,0.08)' : 'rgba(232,115,90,0.06)')
        : 'rgba(255,255,255,0.025)',
      border:'1px solid ' + (picked
        ? (correct ? 'rgba(132,189,69,0.5)' : 'rgba(232,115,90,0.45)')
        : 'rgba(255,255,255,0.1)'),
    }}>
      <div style={{display:'flex', gap:22, alignItems:'flex-start'}}>
        <div style={{fontSize:76, lineHeight:1, flexShrink:0}}>{sc.emoji}</div>
        <div style={{flex:1, fontSize:22, lineHeight:1.45, color:'#fff', textWrap:'pretty'}}>
          {sc.text}
        </div>
      </div>

      <div style={{display:'flex', gap:12, marginTop:24}}>
        {Object.values(SOURCES).map(s => {
          const sel = picked === s.id;
          const showResult = !!picked;
          const isRight = s.id === sc.best;
          return (
            <button key={s.id}
              onClick={()=> !picked && onPick(s.id)}
              disabled={!!picked}
              style={{
                flex:1, cursor: picked?'default':'pointer',
                padding:'16px 12px', borderRadius:12,
                background: sel
                  ? (correct ? 'rgba(132,189,69,0.25)' : 'rgba(232,115,90,0.25)')
                  : (showResult && isRight ? 'rgba(132,189,69,0.15)' : 'rgba(255,255,255,0.04)'),
                border:'2px solid ' + (sel
                  ? (correct ? '#84BD45' : '#E8735A')
                  : (showResult && isRight ? '#84BD45' : 'rgba(255,255,255,0.1)')),
                color:'#fff', fontSize:16, fontWeight:500,
                display:'flex', flexDirection:'column', alignItems:'center', gap:8,
              }}>
              <span style={{fontSize:36}}>{s.icon}</span>
              <span>{s.short}</span>
              {showResult && sel && <span style={{fontSize:18}}>{correct?'✓':'✗'}</span>}
              {showResult && !sel && isRight && <span style={{fontSize:16, color:'#84BD45'}}>★</span>}
            </button>
          );
        })}
      </div>

      {picked && (
        <div className="fade-up" style={{
          marginTop:22, padding:'22px 26px', borderRadius:12,
          background: correct ? 'rgba(132,189,69,0.12)' : 'rgba(255,255,255,0.04)',
          fontSize:19, lineHeight:1.6, textWrap:'pretty',
          color: correct ? '#a8e072' : 'var(--gray)',
        }}>
          <strong style={{color: correct?'#84BD45':'#FFB300'}}>
            {correct ? '¡Correcto!' : 'En realidad lo hace ' + SOURCES[sc.best].short + '.'}
          </strong> {sc.why}
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// NIVEL 3 — LAS MATEMÁTICAS (3 interactive equations)
// ═══════════════════════════════════════════════════════════════
function Level3Math({onNext}){
  return (
    <div style={{maxWidth:1400, margin:'0 auto', padding:'56px 40px'}}>
      <LevelHead n="03" total="05" name="Las matemáticas"
        title="Tres ecuaciones que mueven todo"
        sub="No te asustes — son simples. Cada una resuelve una pregunta concreta: cuánto hay, qué creer, hacia dónde va. Mueve los deslizadores y mira los números cambiar." />

      <div style={{display:'flex', flexDirection:'column', gap:32, marginTop:12}}>
        <EqAverage />
        <EqAssimilation />
        <EqEvolution />
      </div>

      <div style={{
        marginTop:40, padding:'30px 36px',
        background:'rgba(13,204,192,0.06)',
        border:'1px solid rgba(13,204,192,0.25)',
        borderRadius:12, textAlign:'center',
      }}>
        <div style={{fontSize:18, fontWeight:600, color:'#fff', marginBottom:8}}>
          Esto es lo que hace el computador del SIATA cada 6 horas
        </div>
        <div style={{fontSize:15, color:'var(--gray)', lineHeight:1.6, maxWidth:680, margin:'0 auto'}}>
          No es magia. Son sumas, promedios ponderados y diferencias. La inteligencia está en cómo se combinan miles de millones de veces sobre todo el valle.
        </div>
      </div>

      <div style={{marginTop:40, textAlign:'center'}}>
        <PrimaryBtn onClick={onNext} variant="cyan">
          Siguiente · test de comprensión →
        </PrimaryBtn>
      </div>
    </div>
  );
}

// Equation card shell
function EqCard({n, title, sub, formula, children, footer}){
  return (
    <div style={{
      background:'linear-gradient(180deg, rgba(16,53,94,0.55), rgba(14,44,77,0.85))',
      border:'1px solid rgba(255,255,255,0.08)',
      borderRadius:14, padding:'36px 40px',
    }}>
      <div style={{display:'flex', alignItems:'baseline', gap:18, marginBottom:10, flexWrap:'wrap'}}>
        <div style={{
          fontFamily:'var(--mono)', fontSize:15, color:'var(--cyan)',
          letterSpacing:'0.22em', fontWeight:600,
        }}>Ecuación {n}</div>
        <div style={{fontSize:32, fontWeight:700}}>{title}</div>
      </div>
      <div style={{fontSize:22, color:'var(--gray)', lineHeight:1.55, marginBottom:32, maxWidth:880}}>
        {sub}
      </div>

      <div style={{
        padding:'30px 36px', borderRadius:12,
        background:'rgba(0,0,0,0.35)',
        border:'1px solid rgba(13,204,192,0.25)',
        fontFamily:'var(--mono)', fontSize:32,
        color:'#fff', textAlign:'center',
        marginBottom:36, letterSpacing:'0.02em', lineHeight:1.4,
      }}>{formula}</div>

      {children}

      {footer && (
        <div style={{
          marginTop:28, padding:'22px 26px', borderRadius:12,
          background:'rgba(132,189,69,0.08)',
          border:'1px solid rgba(132,189,69,0.3)',
          fontSize:19, color:'#a8e072', lineHeight:1.6,
        }}>💡 {footer}</div>
      )}
    </div>
  );
}

// ─── Eq 1: spatial average ──────────────────────────────────────
function EqAverage(){
  const [highCount, setHighCount] = React.useState(2);
  const total = 20;
  const lowVal = 18;
  const highVal = 90;
  const avg = ((total - highCount) * lowVal + highCount * highVal) / total;

  return (
    <EqCard n="1" title="Promedio del valle"
      sub="El SIATA tiene unas 20 estaciones repartidas en el Valle de Aburrá. ¿Qué pasa con el promedio si unas pocas estaciones miden muy alto (por ejemplo, cerca de una quema)?"
      formula={<span>PM2.5<sub>valle</sub> = (sensor<sub>1</sub> + sensor<sub>2</sub> + … + sensor<sub>20</sub>) / 20</span>}
      footer="Pocas mediciones altas no mueven mucho el promedio — por eso necesitamos ver cada estación, no solo el número global."
    >
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:36, alignItems:'center'}}>
        <div>
          <div style={{
            fontFamily:'var(--mono)', fontSize:14, color:'var(--gray)',
            letterSpacing:'0.18em', textTransform:'uppercase', marginBottom:14,
          }}>Estaciones con valor alto ({'>'}55 μg)</div>
          <input type="range" className="pm-slider"
            min={0} max={10} step={1} value={highCount}
            onChange={e=>setHighCount(Number(e.target.value))} />
          <div style={{
            display:'flex', justifyContent:'space-between',
            marginTop:14, fontFamily:'var(--mono)', fontSize:15, color:'var(--gray)',
          }}>
            <span>0</span>
            <span style={{color:'#FF8A3D', fontSize:22, fontWeight:700}}>{highCount} de 20</span>
            <span>10</span>
          </div>

          {/* Dots viz: 20 stations in 10x2 grid */}
          <div style={{
            marginTop:24, display:'grid',
            gridTemplateColumns:'repeat(10, 1fr)', gap:8,
          }}>
            {Array.from({length:total}).map((_,i)=>(
              <div key={i} style={{
                width:'100%', paddingBottom:'100%', position:'relative',
              }}>
                <div style={{
                  position:'absolute', inset:0, borderRadius:'50%',
                  background: i < highCount ? '#FF8A3D' : '#84BD45',
                  opacity: i < highCount ? 1 : 0.5,
                  boxShadow: i < highCount ? '0 0 14px #FF8A3D80' : 'none',
                  transition:'all 0.2s ease',
                }} />
              </div>
            ))}
          </div>
        </div>

        <div style={{textAlign:'center'}}>
          <div style={{
            fontFamily:'var(--mono)', fontSize:12, color:'var(--gray)',
            letterSpacing:'0.18em', textTransform:'uppercase',
          }}>Promedio del valle</div>
          <div style={{
            fontSize:120, fontWeight:800, lineHeight:1, marginTop:10,
            color: pmColor(avg), fontVariantNumeric:'tabular-nums',
            letterSpacing:'-0.03em',
            textShadow:'0 6px 30px ' + pmColor(avg) + '40',
          }}>
            <AnimNum value={avg} decimals={1} duration={200} />
          </div>
          <div style={{fontSize:16, color:'var(--gray)', marginTop:8}}>
            μg/m³ · aire <strong style={{color:pmColor(avg)}}>{pmLabel(avg)}</strong>
          </div>
        </div>
      </div>
    </EqCard>
  );
}

// ─── Eq 2: data assimilation (model + obs weighting) ────────────
function tempColor(t){
  if (t <= 16) return '#7DD3FC';
  if (t <= 20) return '#0DCCC0';
  if (t <= 24) return '#84BD45';
  if (t <= 28) return '#FFB300';
  return '#FF8A3D';
}
function tempLabel(t){
  if (t <= 16) return 'frío';
  if (t <= 20) return 'fresco';
  if (t <= 24) return 'templado';
  if (t <= 28) return 'cálido';
  return 'caluroso';
}

function EqAssimilation(){
  const [alpha, setAlpha] = React.useState(0.5);
  const model = 22;   // °C predicho por WRF
  const obs = 26;     // °C medido por sensores
  const result = model * (1-alpha) + obs * alpha;

  return (
    <EqCard n="2" title="Combinar modelo y medición"
      sub="El modelo WRF simula que mañana al mediodía hará 22 °C. Las estaciones están midiendo 26 °C ahora mismo. ¿A quién creemos? El SIATA combina ambos con un peso α (alfa) — esto se llama asimilación de datos."
      formula={<span>Temperatura = Modelo · (1 − α)  +  Medición · α</span>}
      footer="Cuando los sensores son confiables, subimos α (les damos más peso). Cuando hay pocos sensores o el modelo simula bien la física, bajamos α y confiamos más en el modelo. Lo mismo aplica para viento, humedad y demás variables meteorológicas."
    >
      <div style={{display:'grid', gridTemplateColumns:'1fr 1.2fr', gap:36, alignItems:'center'}}>
        <div>
          <div style={{display:'flex', justifyContent:'space-between', marginBottom:14}}>
            <div>
              <div style={{fontFamily:'var(--mono)', fontSize:11, color:'#0DCCC0', letterSpacing:'0.16em', textTransform:'uppercase'}}>Modelo dice</div>
              <div style={{fontSize:32, fontWeight:700, color:'#0DCCC0', marginTop:4}}>{model}°</div>
              <div style={{fontSize:12, color:'var(--gray)'}}>°C · WRF</div>
            </div>
            <div style={{textAlign:'right'}}>
              <div style={{fontFamily:'var(--mono)', fontSize:11, color:'#84BD45', letterSpacing:'0.16em', textTransform:'uppercase'}}>Sensores miden</div>
              <div style={{fontSize:32, fontWeight:700, color:'#84BD45', marginTop:4}}>{obs}°</div>
              <div style={{fontSize:12, color:'var(--gray)'}}>°C · estaciones</div>
            </div>
          </div>

          <div style={{marginTop:10}}>
            <div style={{
              fontFamily:'var(--mono)', fontSize:12, color:'var(--gray)',
              letterSpacing:'0.18em', textTransform:'uppercase', marginBottom:10,
            }}>Peso de la medición · α = {alpha.toFixed(2)}</div>
            <input type="range" className="pm-slider"
              min={0} max={1} step={0.01} value={alpha}
              onChange={e=>setAlpha(Number(e.target.value))} />
            <div style={{
              display:'flex', justifyContent:'space-between',
              marginTop:10, fontFamily:'var(--mono)', fontSize:12, color:'var(--dim)',
            }}>
              <span>0 · creer al modelo</span>
              <span>1 · creer al sensor</span>
            </div>
          </div>

          {/* Mix bar */}
          <div style={{
            marginTop:24, height:18, borderRadius:9, display:'flex', overflow:'hidden',
            boxShadow:'inset 0 0 0 1px rgba(255,255,255,0.1)',
          }}>
            <div style={{
              width: ((1-alpha)*100) + '%', background:'#0DCCC0',
              transition:'width 0.2s ease',
            }} />
            <div style={{
              width: (alpha*100) + '%', background:'#84BD45',
              transition:'width 0.2s ease',
            }} />
          </div>
        </div>

        <div style={{textAlign:'center'}}>
          <div style={{
            fontFamily:'var(--mono)', fontSize:12, color:'var(--gray)',
            letterSpacing:'0.18em', textTransform:'uppercase',
          }}>Temperatura final</div>
          <div style={{
            fontSize:120, fontWeight:800, lineHeight:1, marginTop:10,
            color: tempColor(result), fontVariantNumeric:'tabular-nums',
            letterSpacing:'-0.03em',
            textShadow:'0 6px 30px ' + tempColor(result) + '40',
          }}>
            <AnimNum value={result} decimals={1} duration={150} />°
          </div>
          <div style={{fontSize:16, color:'var(--gray)', marginTop:8}}>
            °C · ambiente <strong style={{color:tempColor(result)}}>{tempLabel(result)}</strong>
          </div>
        </div>
      </div>
    </EqCard>
  );
}

// ─── Eq 3: evolution over time ──────────────────────────────────
function EqEvolution(){
  const [pm, setPm] = React.useState(30);
  const [emis, setEmis] = React.useState(10);
  const [wind, setWind] = React.useState(5);
  // hour-ahead
  const next = Math.max(2, pm + emis - wind*2);

  return (
    <EqCard n="3" title="¿Y la próxima hora?"
      sub="El aire de la próxima hora depende del aire de ahora más lo que se está emitiendo (tráfico, leña) menos lo que se dispersa (viento, lluvia)."
      formula={<span>PM2.5<sub>próxima hora</sub> = PM2.5<sub>ahora</sub> + Emisión − Dispersión</span>}
      footer="Por eso el viento es tu aliado: cada km/h de viento puede llevarse 2 μg/m³ por hora. Por eso los días de calma + inversión térmica son los peores."
    >
      <div style={{display:'grid', gridTemplateColumns:'1.2fr 1fr', gap:36, alignItems:'center'}}>
        <div style={{display:'flex', flexDirection:'column', gap:18}}>
          <EvolSlider label="PM2.5 ahora mismo" value={pm} setValue={setPm}
            min={5} max={80} step={1} unit="μg/m³" color="#A78BFA" />
          <EvolSlider label="Emisión esta hora (tráfico, leña)" value={emis} setValue={setEmis}
            min={0} max={30} step={1} unit="μg/m³ extra" color="#FF8A3D" />
          <EvolSlider label="Viento dispersando (km/h)" value={wind} setValue={setWind}
            min={0} max={20} step={1} unit="km/h" color="#0DCCC0" />
        </div>

        <div style={{textAlign:'center'}}>
          <div style={{
            display:'flex', alignItems:'center', justifyContent:'center', gap:12,
            marginBottom:18,
          }}>
            <div>
              <div style={{
                fontFamily:'var(--mono)', fontSize:11, color:'var(--gray)',
                letterSpacing:'0.18em', textTransform:'uppercase',
              }}>Ahora</div>
              <div style={{fontSize:42, fontWeight:700, color:pmColor(pm), marginTop:4}}>{pm}</div>
            </div>
            <div style={{fontSize:32, color:'var(--dim)'}}>→</div>
            <div>
              <div style={{
                fontFamily:'var(--mono)', fontSize:11, color:'#0DCCC0',
                letterSpacing:'0.18em', textTransform:'uppercase',
              }}>En 1 hora</div>
              <div style={{
                fontSize:80, fontWeight:800, lineHeight:1, marginTop:4,
                color: pmColor(next), fontVariantNumeric:'tabular-nums',
              }}>
                <AnimNum value={next} duration={150} />
              </div>
            </div>
          </div>
          <div style={{
            fontSize:16, color: next > pm ? '#FF8A3D' : '#84BD45', fontWeight:600,
          }}>
            {next > pm ? '↑ Empeorando' : next < pm ? '↓ Mejorando' : '= Sin cambio'}
            {' · '}
            <span style={{color:'var(--gray)', fontWeight:400}}>
              cambia {Math.abs(next-pm).toFixed(0)} μg/m³
            </span>
          </div>
        </div>
      </div>
    </EqCard>
  );
}

function EvolSlider({label, value, setValue, min, max, step, unit, color}){
  return (
    <div>
      <div style={{
        display:'flex', justifyContent:'space-between', alignItems:'baseline',
        marginBottom:12,
      }}>
        <div style={{fontSize:19, color:'var(--gray)'}}>{label}</div>
        <div style={{
          fontFamily:'var(--mono)', fontSize:26, fontWeight:700, color: color,
          fontVariantNumeric:'tabular-nums',
        }}>{value} <span style={{fontSize:15, color:'var(--dim)'}}>{unit}</span></div>
      </div>
      <input type="range" className="pm-slider"
        min={min} max={max} step={step} value={value}
        onChange={e=>setValue(Number(e.target.value))} />
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════
// NIVEL 4 — TEST DE COMPRENSIÓN (5-question quiz)
// ═══════════════════════════════════════════════════════════════
const QUIZ_Q = [
  {
    q:'Si llega una nube de polvo del Sahara hacia Colombia, ¿qué fuente te avisa primero?',
    opts:[
      {id:'a', t:'Un sensor en el centro de Medellín'},
      {id:'b', t:'CAMS · los satélites globales'},
      {id:'c', t:'El modelo meteorológico WRF'},
    ],
    correct:'b',
    why:'Los satélites de CAMS son los únicos que ven el aire del planeta entero. Detectan el polvo cuando aún está cruzando el Atlántico, días antes de que llegue.',
  },
  {
    q:'Una mañana hay inversión térmica fuerte sobre el valle. ¿Qué le pasa al PM2.5?',
    opts:[
      {id:'a', t:'Se queda atrapado y sube'},
      {id:'b', t:'Sube al cielo y se dispersa'},
      {id:'c', t:'No le pasa nada'},
    ],
    correct:'a',
    why:'La inversión térmica es como una "tapa" de aire caliente encima del aire frío. El material particulado no puede subir y se queda atrapado entre las montañas — por eso las mañanas son la peor hora.',
  },
  {
    q:'¿Por qué necesitamos sensores en tierra si ya tenemos satélites y modelos?',
    opts:[
      {id:'a', t:'Para tener más logos en la página'},
      {id:'b', t:'Porque sensores, modelos y satélites compiten'},
      {id:'c', t:'Porque sólo los sensores miden el aire que respiramos de verdad'},
    ],
    correct:'c',
    why:'Satélites y modelos hacen estimaciones. Los sensores son la "verdad" sobre el suelo: corrigen los modelos cuando se equivocan y miden lo que de verdad estás respirando ahora mismo.',
  },
  {
    q:'El pronóstico dice 50 μg/m³ ± 8 μg/m³. ¿Qué significa el "± 8"?',
    opts:[
      {id:'a', t:'Que el aire va a oscilar entre 42 y 58 a lo largo del día'},
      {id:'b', t:'La incertidumbre: probablemente esté entre 42 y 58 μg/m³'},
      {id:'c', t:'Que hay 8 sensores funcionando'},
    ],
    correct:'b',
    why:'Ningún pronóstico es exacto. El "±" indica el margen de error: el SIATA está bastante seguro de que el valor real estará dentro de ese rango. Mientras más datos, menor el ±.',
  },
  {
    q:'Es viernes a las 6 a.m. y el pronóstico de hoy dice PM2.5 alto en la mañana, bajo en la tarde. ¿Cuál es la mejor decisión para hacer ejercicio?',
    opts:[
      {id:'a', t:'Salir a trotar ya mismo, antes de que empeore'},
      {id:'b', t:'Esperar a la tarde, cuando baje'},
      {id:'c', t:'No ejercitarse hoy'},
    ],
    correct:'b',
    why:'El pronóstico te da exactamente esto: saber CUÁNDO el aire estará mejor. Esperar unas horas convierte un día "malo" en una ventana saludable — esa es la idea de pronosticar.',
  },
];

function Level4Quiz({onNext, store, setStore}){
  const [answers, setAnswers] = React.useState(store.quiz || {});
  const [shown, setShown] = React.useState({});

  const pick = (qi, aid) => {
    if(answers[qi]) return;
    const next = {...answers, [qi]: aid};
    setAnswers(next);
    setStore({...store, quiz: next});
    setTimeout(()=> setShown({...shown, [qi]: true}), 200);
  };

  const allDone = QUIZ_Q.every((_,i) => answers[i]);
  const score = QUIZ_Q.filter((q,i) => answers[i] === q.correct).length;

  return (
    <div style={{maxWidth:1280, margin:'0 auto', padding:'56px 40px'}}>
      <LevelHead n="04" total="05" name="Test de comprensión"
        title="Cinco preguntas. ¿Estás listo para pronosticar?"
        sub="Esto no es para sacar nota — es para que confirmes que entiendes cómo funciona el sistema. Si fallas, te explico por qué." />

      <div style={{display:'flex', flexDirection:'column', gap:22, marginTop:20}}>
        {QUIZ_Q.map((q, i) => (
          <QuizQuestion key={i} num={i+1} q={q}
            answer={answers[i]}
            onPick={(aid)=>pick(i, aid)} />
        ))}
      </div>

      {allDone && (
        <div className="fade-up" style={{
          marginTop:44, padding:'36px 40px', borderRadius:14, textAlign:'center',
          background: score >= 4
            ? 'linear-gradient(135deg, rgba(132,189,69,0.15), rgba(13,204,192,0.08))'
            : 'linear-gradient(135deg, rgba(255,179,0,0.12), rgba(232,115,90,0.05))',
          border:'1px solid ' + (score >= 4 ? 'rgba(132,189,69,0.5)' : 'rgba(255,179,0,0.5)'),
        }}>
          <div style={{
            fontFamily:'var(--mono)', fontSize:13, color: score>=4?'#84BD45':'#FFB300',
            letterSpacing:'0.24em', textTransform:'uppercase',
          }}>
            {score >= 4 ? 'Aprobaste' : 'Casi'}
          </div>
          <div style={{
            fontSize:96, fontWeight:800, lineHeight:1, marginTop:8,
            color: score>=4?'#84BD45':'#FFB300', letterSpacing:'-0.03em',
          }}>{score} / 5</div>
          <div style={{fontSize:18, color:'var(--gray)', marginTop:14, maxWidth:520, margin:'14px auto 0'}}>
            {score === 5 && '¡Perfecto! Tienes una comprensión completa del sistema.'}
            {score === 4 && 'Muy bien. Entiendes lo esencial — listo para el último nivel.'}
            {score === 3 && 'Bien. Revisa los niveles anteriores si quieres afinar, pero puedes seguir.'}
            {score <= 2 && 'Vale la pena volver a los niveles anteriores. El último nivel funciona mejor si tienes claros los conceptos.'}
          </div>

          <div style={{marginTop:32, display:'flex', justifyContent:'center', gap:14, flexWrap:'wrap'}}>
            <PrimaryBtn onClick={onNext} variant="green">
              Último nivel · ¡a pronosticar! →
            </PrimaryBtn>
          </div>
        </div>
      )}
    </div>
  );
}

function QuizQuestion({num, q, answer, onPick}){
  const showResult = !!answer;
  const isCorrect = answer === q.correct;
  return (
    <div style={{
      padding:'28px 32px', borderRadius:14,
      background:'rgba(255,255,255,0.025)',
      border:'1px solid ' + (showResult
        ? (isCorrect ? 'rgba(132,189,69,0.4)' : 'rgba(232,115,90,0.4)')
        : 'rgba(255,255,255,0.1)'),
    }}>
      <div style={{display:'flex', alignItems:'flex-start', gap:22}}>
        <div style={{
          fontFamily:'var(--mono)', fontSize:18, color:'var(--cyan)',
          letterSpacing:'0.2em', fontWeight:700, paddingTop:4,
        }}>0{num}</div>
        <div style={{flex:1}}>
          <div style={{fontSize:22, fontWeight:600, lineHeight:1.4, marginBottom:24, textWrap:'pretty'}}>
            {q.q}
          </div>

          <div style={{display:'flex', flexDirection:'column', gap:12}}>
            {q.opts.map(o => {
              const sel = answer === o.id;
              const isRight = o.id === q.correct;
              return (
                <button key={o.id}
                  onClick={()=>onPick(o.id)}
                  disabled={showResult}
                  style={{
                    textAlign:'left', cursor: showResult?'default':'pointer',
                    padding:'20px 24px', borderRadius:12,
                    background: sel
                      ? (isCorrect ? 'rgba(132,189,69,0.2)' : 'rgba(232,115,90,0.2)')
                      : (showResult && isRight ? 'rgba(132,189,69,0.1)' : 'rgba(255,255,255,0.04)'),
                    border:'2px solid ' + (sel
                      ? (isCorrect ? '#84BD45' : '#E8735A')
                      : (showResult && isRight ? '#84BD45' : 'rgba(255,255,255,0.1)')),
                    color:'#fff', fontSize:20,
                    display:'flex', alignItems:'center', gap:16,
                    lineHeight:1.4,
                  }}>
                  <span style={{
                    width:32, height:32, borderRadius:'50%',
                    border:'2px solid currentColor',
                    display:'inline-flex', alignItems:'center', justifyContent:'center',
                    fontSize:18, flexShrink:0,
                    color: sel ? (isCorrect?'#84BD45':'#E8735A') : (showResult&&isRight?'#84BD45':'var(--dim)'),
                  }}>{showResult ? (sel ? (isCorrect?'✓':'✗') : (isRight?'★':'')) : ''}</span>
                  {o.t}
                </button>
              );
            })}
          </div>

          {showResult && (
            <div className="fade-up" style={{
              marginTop:22, padding:'22px 26px', borderRadius:12,
              background: isCorrect ? 'rgba(132,189,69,0.1)' : 'rgba(255,179,0,0.08)',
              fontSize:19, color: isCorrect?'#a8e072':'var(--gray)', lineHeight:1.6,
              textWrap:'pretty',
            }}>
              <strong style={{color: isCorrect?'#84BD45':'#FFB300'}}>
                {isCorrect ? '✓ Correcto.' : 'Era ' + q.opts.find(o=>o.id===q.correct).t + '.'}
              </strong> {q.why}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────
// Shared LevelHead
// ───────────────────────────────────────────────────────────────
function LevelHead({n, total, name, title, sub}){
  return (
    <div style={{maxWidth:1040, margin:'0 auto 48px', textAlign:'center'}}>
      <div className="fade-up" style={{
        display:'inline-flex', alignItems:'center', gap:14,
        padding:'12px 26px', borderRadius:30,
        background:'rgba(13,204,192,0.1)',
        border:'1px solid rgba(13,204,192,0.4)',
        fontFamily:'var(--mono)', fontSize:16, color:'var(--cyan)',
        letterSpacing:'0.22em', textTransform:'uppercase',
      }}>
        Nivel {n} <span style={{color:'var(--dim)'}}>/ {total}</span>
        <span style={{
          width:5, height:5, borderRadius:'50%', background:'var(--cyan)',
        }} />
        {name}
      </div>
      <h1 className="fade-up delay-1" style={{
        fontSize:'clamp(40px, 5.5vw, 72px)', fontWeight:700,
        lineHeight:1.08, marginTop:26, textWrap:'balance',
        letterSpacing:'-0.02em',
      }}>{title}</h1>
      {sub && (
        <p className="fade-up delay-2" style={{
          fontSize:24, color:'var(--gray)', marginTop:26,
          lineHeight:1.5, textWrap:'pretty',
          maxWidth:860, margin:'26px auto 0',
        }}>{sub}</p>
      )}
    </div>
  );
}

Object.assign(window, {
  Level1Sources, Level2Meaning, Level3Math, Level4Quiz,
  LevelHead, SOURCES,
});
