/* global React, GlossaryTooltip, VoiceCard, AudioPlayer, RubricMeter, Watermark, Banner, DisclaimerFooter, EpisodeHeader, StepRail, GlossaryRail, STEPS */
// P03 Episode Shell + slots S1 / S2 / S3 / S4 (interactive)
// Content: M01.E02 — Trailing vs forward

const { useState: useSh } = React;

// ============================================================================
// Content for M01.E02
// ============================================================================
const VOICE_SCRIPT = [
  { speaker: 'sofia', cue: '(música breve, tono serio)', text: 'La calculadora que vas a usar no es magia; cada celda esconde una decisión que alguien tomó. El P/E que ves en Bloomberg suele ser una mezcla rara: trailing reportado, forward consenso o blended. Cada uno cuenta una historia distinta.' },
  { speaker: 'mateo', cue: '(intervención conversacional)', text: 'Las calculadoras me dan falsa precisión. Aquí espero ver que cada número viene de una decisión, no de una fórmula mágica.' },
  { speaker: 'tutor', cue: '(sonido de panel interactivo)', text: 'La regla del episodio: en cíclica usar normalized (CAPE) o mid-cycle, nunca trailing en pico ni forward en valle. En growth, forward defendible si consenso tiene track record. En stalwart, cualquiera funciona con earnings estables. En turnaround, trailing engaña y forward depende de catalyst; usar normalized post-recovery.' },
  { speaker: 'sofia', cue: '(pausa didáctica)', text: 'Cuatro empresas: cíclica, growth, stalwart, turnaround. Calcula ambos P/E y elige justificadamente. Identifica el caso donde ambos engañan y cuál sería tu solución.' },
];

// 4-company synthetic dataset
const DATASET = [
  { ticker: 'CICL.MC', archetype: 'cíclica',    price: 42.30, eps_ttm: 5.20, eps_ntm: 3.40, calibration: -0.08, peak_warn: true },
  { ticker: 'GROW.MC', archetype: 'growth',     price: 168.90, eps_ttm: 3.80, eps_ntm: 5.10, calibration: -0.12, peak_warn: false },
  { ticker: 'STLW.MC', archetype: 'stalwart',   price: 96.50, eps_ttm: 6.40, eps_ntm: 6.65, calibration: 0.01, peak_warn: false },
  { ticker: 'TURN.MC', archetype: 'turnaround', price: 18.20, eps_ttm: -0.40, eps_ntm: 1.10, calibration: -0.22, peak_warn: false },
];

// ============================================================================
// S1 — story_panel
// ============================================================================
const Slot_S1 = ({ onComplete }) => {
  const [playingIdx, setPlayingIdx] = useSh(2);
  const [notes, setNotes] = useSh('');

  return (
    <div>
      <div className="row" style={{ gap: 8, marginBottom: 16 }}>
        <span className="badge" style={{ borderColor: 'var(--accent)', color: 'var(--accent)' }}>S1 · STORY PANEL</span>
        <span className="txt-xs txt-sec">Mientras escuchas, anota qué esperas encontrar y por qué.</span>
      </div>

      <h2 className="serif italic" style={{ fontSize: 'var(--size-2xl)', fontWeight: 400, lineHeight: 1.3, margin: '0 0 28px 0', maxWidth: 720, color: 'var(--text-primary)' }}>
        ¿Cuándo prefieres P/E forward —que depende de un consenso— sobre P/E trailing —que carga one-offs?
      </h2>

      <AudioPlayer played={0.42} duration="04:12" playing />

      <div className="row" style={{ gap: 8, margin: '14px 0' }}>
        <span className="chip">▸ 1×</span>
        <button className="chip">0.75×</button>
        <button className="chip">1.25×</button>
        <button className="chip">1.5×</button>
        <span style={{ flex: 1 }}/>
        <span className="chip chip-pass">CC captions ON</span>
        <button className="btn btn-ghost txt-sm">Ver transcripción</button>
      </div>

      <div style={{ marginTop: 14 }}>
        {VOICE_SCRIPT.map((v, i) => (
          <div key={i} onClick={() => setPlayingIdx(i)} style={{ cursor: 'pointer' }}>
            <VoiceCard {...v} playing={playingIdx === i}/>
          </div>
        ))}
      </div>

      <div className="card" style={{ padding: 16, marginTop: 24, borderStyle: 'dashed' }}>
        <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 8 }}>Tu expectativa (opcional, persiste)</div>
        <textarea
          className="textarea"
          style={{ minHeight: 80, fontSize: 'var(--size-md)' }}
          placeholder="Antes de calcular: qué esperas encontrar y por qué…"
          value={notes}
          onChange={e => setNotes(e.target.value)}
        />
      </div>

      <div className="row" style={{ justifyContent: 'space-between', marginTop: 24, padding: '14px 0', borderTop: '1px solid var(--border-subtle)' }}>
        <div className="row txt-sm txt-sec">
          <input type="checkbox" defaultChecked /> &nbsp; He escuchado la locución o leído la transcripción
        </div>
        <button className="btn btn-primary" onClick={onComplete}>
          Continuar a S2 · Concepto
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M5 3l5 5-5 5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
        </button>
      </div>
    </div>
  );
};

// ============================================================================
// S2 — annotated_statement_or_formula
// ============================================================================
const Slot_S2 = ({ onComplete }) => {
  const [consulted, setConsulted] = useSh(new Set(['pe_trailing']));
  const terms = ['pe_trailing', 'pe_forward', 'consenso_calibration'];

  const consult = (t) => {
    setConsulted(prev => {
      const n = new Set(prev);
      n.add(t);
      return n;
    });
  };

  return (
    <div>
      <div className="row" style={{ gap: 8, marginBottom: 16 }}>
        <span className="badge" style={{ borderColor: 'var(--accent)', color: 'var(--accent)' }}>S2 · CONCEPTO</span>
        <span className="txt-xs txt-sec">Toca los iconos i antes de usar una métrica. Una fórmula sin significado no cuenta como análisis.</span>
      </div>

      {/* Statement */}
      <div style={{ padding: '28px 32px', background: 'var(--surface-1)', border: '1px solid var(--border-default)', borderRadius: 'var(--r-lg)', marginBottom: 20 }}>
        <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 12 }}>Regla del episodio</div>
        <p className="serif" style={{ fontSize: 'var(--size-xl)', lineHeight: 1.55, margin: 0, color: 'var(--text-primary)' }}>
          En cíclica usar <em>normalized</em> (CAPE) o mid-cycle, nunca <span onClick={() => consult('pe_trailing')}><GlossaryTooltip termId="pe_trailing">P/E trailing</GlossaryTooltip></span> en pico ni <span onClick={() => consult('pe_forward')}><GlossaryTooltip termId="pe_forward">P/E forward</GlossaryTooltip></span> en valle. En growth, forward defendible si la <span onClick={() => consult('consenso_calibration')}><GlossaryTooltip termId="consenso_calibration">calibración del consenso</GlossaryTooltip></span> tiene track record. En stalwart, cualquiera funciona con earnings estables.
        </p>
      </div>

      {/* Two formulas side by side */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 24 }}>
        <div>
          <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 8 }}>P/E trailing</div>
          <div className="formula">
            P/E<sub>TTM</sub> <span className="op">=</span> <span className="var">Price</span> <span className="op">/</span> <span className="var">EPS</span><sub>últimos 12m</sub>
          </div>
          <div className="txt-xs txt-sec" style={{ marginTop: 10, lineHeight: 1.5 }}>
            Memoria contable: arrastra one-offs, plusvalías, restructuring. Objetivo pero no necesariamente representativo.
          </div>
        </div>
        <div>
          <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 8 }}>P/E forward</div>
          <div className="formula">
            P/E<sub>fwd</sub> <span className="op">=</span> <span className="var">Price</span> <span className="op">/</span> <span className="var">EPS</span><sub>NTM consenso</sub>
          </div>
          <div className="txt-xs txt-sec" style={{ marginTop: 10, lineHeight: 1.5 }}>
            Mira al futuro: depende de un consenso que puede estar sistemáticamente sesgado (tech ~+10–15%).
          </div>
        </div>
      </div>

      {/* Sectorial calibration table */}
      <div className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 24 }}>
        <div style={{ padding: 14, borderBottom: '1px solid var(--border-subtle)' }}>
          <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>Sesgo histórico del consenso sell-side</div>
          <div className="txt-sm txt-sec" style={{ marginTop: 4 }}>Mediana(EPS_realizado / EPS_consenso) en los últimos 5 años por arquetipo</div>
        </div>
        <table className="table">
          <thead>
            <tr>
              <th>Arquetipo</th>
              <th>Calibración</th>
              <th>Interpretación</th>
              <th>Forward fiable</th>
            </tr>
          </thead>
          <tbody>
            <tr><td>Tech growth</td><td className="num" style={{ color: 'var(--signal-warning)' }}>0,87×</td><td className="txt-sec">consenso ~13% optimista</td><td><span className="chip chip-warning">cuidado</span></td></tr>
            <tr><td>Banca</td><td className="num">1,02×</td><td className="txt-sec">calibrado</td><td><span className="chip chip-pass">sí</span></td></tr>
            <tr><td>Cíclica (auto, química)</td><td className="num" style={{ color: 'var(--signal-fail)' }}>0,71×</td><td className="txt-sec">sigue el ciclo, mala calibración</td><td><span className="chip chip-fail">no</span></td></tr>
            <tr><td>Stalwart consumer</td><td className="num">0,98×</td><td className="txt-sec">levemente optimista</td><td><span className="chip chip-pass">sí</span></td></tr>
          </tbody>
        </table>
      </div>

      {/* Consult tracker */}
      <div className="card" style={{ padding: 16, marginBottom: 24 }}>
        <div className="row" style={{ justifyContent: 'space-between', marginBottom: 12 }}>
          <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>Términos consultados · {consulted.size} / {terms.length}</div>
          <span className="txt-xs txt-sec">Al menos 1 para desbloquear S3</span>
        </div>
        <div className="row" style={{ gap: 8 }}>
          {terms.map(t => {
            const isC = consulted.has(t);
            return (
              <span key={t} className={`chip ${isC ? 'chip-pass' : ''}`}>
                {isC ? '✓' : '○'} {window.GLOSSARY[t].term}
              </span>
            );
          })}
        </div>
      </div>

      <div className="row" style={{ justifyContent: 'space-between', padding: '14px 0', borderTop: '1px solid var(--border-subtle)' }}>
        <div className="txt-xs txt-sec">El sistema propondrá flashcards de los términos que no consultes.</div>
        <button className="btn btn-primary" onClick={onComplete} disabled={consulted.size === 0}>
          Continuar a S3 · Lab CALC
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M5 3l5 5-5 5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
        </button>
      </div>
    </div>
  );
};

// ============================================================================
// S3 — CALC lab (the heavyweight slot)
// ============================================================================
const Slot_S3 = ({ onComplete }) => {
  // each row: choice (trailing|forward|normalized), justification text, source filled, limitation filled
  const [rows, setRows] = useSh(DATASET.map(d => ({
    choice: '',
    source: '',
    limitation: '',
    justification: '',
  })));
  const [tasksDone, setTasksDone] = useSh([true, true, true, false, false]);

  const update = (i, key, val) => {
    setRows(prev => prev.map((r, idx) => idx === i ? { ...r, [key]: val } : r));
  };

  const calc = (row, type) => {
    if (type === 'trailing') {
      return row.eps_ttm > 0 ? (row.price / row.eps_ttm).toFixed(1) + '×' : 'n/a';
    }
    return (row.price / row.eps_ntm).toFixed(1) + '×';
  };

  // Pre-fill row 2 (stalwart) and row 0 (cyclical) with example data for demo
  React.useEffect(() => {
    setRows(prev => prev.map((r, i) => {
      if (i === 2) return { choice: 'either', source: 'consenso · histórico 5y', limitation: 'consumer base estable, margen 18%', justification: 'Stalwart con earnings estables; ambos P/E convergen y forward defendible.' };
      if (i === 0) return { choice: 'normalized', source: 'EPS_TTM en pico cíclico 2024', limitation: 'EPS forward asume descenso 35% por ciclo', justification: 'Cíclica: ni trailing (peak earnings) ni forward (consenso siguiendo ciclo). Usar mid-cycle.' };
      return r;
    }));
  }, []);

  const tasks = [
    'Calcular P/E trailing y forward para 4 empresas',
    'Identificar fuente del input para cada celda derivada',
    'Anotar limitación 1-línea de cada cálculo',
    'Elegir métrica (trailing / forward / normalized) por caso',
    'Justificar elección citando arquetipo y sesgo del consenso',
  ];

  const completeRows = rows.filter(r => r.choice && r.source && r.limitation && r.justification.length >= 20).length;
  const canSubmit = completeRows >= 3;

  return (
    <div>
      <div className="row" style={{ gap: 8, marginBottom: 16, flexWrap: 'wrap' }}>
        <span className="badge" style={{ borderColor: 'var(--accent)', color: 'var(--accent)' }}>S3 · LAB · CALC</span>
        <span className="badge">Calculadora con justificación obligatoria</span>
        <span className="txt-xs txt-sec">Calcula P/E trailing y forward para 4 empresas; decide cuál usar y por qué.</span>
      </div>

      {/* Student tasks checklist */}
      <div className="card" style={{ padding: 14, marginBottom: 20, background: 'var(--surface-1)' }}>
        <div className="row" style={{ justifyContent: 'space-between', marginBottom: 10 }}>
          <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>Tu encargo · {tasksDone.filter(Boolean).length} / {tasks.length}</div>
          <span className="txt-xs txt-sec">Las 5 tareas son las dimensiones de la rúbrica.</span>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 8 }}>
          {tasks.map((t, i) => (
            <label key={i} className="row" style={{ alignItems: 'flex-start', gap: 6, fontSize: 11, lineHeight: 1.35, cursor: 'pointer' }}>
              <input type="checkbox" checked={tasksDone[i]} onChange={() => setTasksDone(prev => prev.map((d, idx) => idx === i ? !d : d))} style={{ marginTop: 2, accentColor: 'var(--signal-pass)' }}/>
              <span style={{ color: tasksDone[i] ? 'var(--signal-pass)' : 'var(--text-secondary)' }}>
                <span className="mono txt-xs txt-ter" style={{ marginRight: 4 }}>0{i+1}</span>
                {t}
              </span>
            </label>
          ))}
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: 20 }}>
        {/* Main: rows */}
        <div className="col" style={{ gap: 12 }}>
          {DATASET.map((d, i) => {
            const row = rows[i] || {};
            const trailing = calc(d, 'trailing');
            const forward = calc(d, 'forward');
            const isPeakWarn = d.peak_warn;
            const filled = row.choice && row.source && row.limitation && row.justification.length >= 20;
            const partial = (row.choice || row.source || row.limitation || row.justification.length > 0) && !filled;

            return (
              <div key={d.ticker} className="card" style={{
                padding: 16,
                borderColor: filled ? 'var(--signal-pass)' : partial ? 'var(--signal-hypothesis)' : 'var(--border-default)',
                borderStyle: partial && !filled ? 'dashed' : 'solid',
                background: filled ? 'rgba(91,198,143,0.04)' : 'var(--surface-1)',
              }}>
                {/* row 1 — header + trio cards */}
                <div className="row" style={{ justifyContent: 'space-between', marginBottom: 14 }}>
                  <div className="row" style={{ gap: 12 }}>
                    <span className="mono" style={{ fontSize: 'var(--size-lg)', fontWeight: 600 }}>{d.ticker}</span>
                    <span className="badge">{d.archetype}</span>
                    {isPeakWarn && <span className="chip chip-warning">⚠ peak earnings</span>}
                  </div>
                  <div className="row mono txt-sm txt-sec" style={{ gap: 16 }}>
                    <span>Price <span style={{ color: 'var(--text-primary)' }}>{d.price.toFixed(2)}€</span></span>
                    <span>EPS_TTM <span style={{ color: 'var(--text-primary)' }}>{d.eps_ttm.toFixed(2)}</span></span>
                    <span>EPS_NTM <span style={{ color: 'var(--text-primary)' }}>{d.eps_ntm.toFixed(2)}</span></span>
                  </div>
                </div>

                {/* trio cards row */}
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 2fr', gap: 12, marginBottom: 14 }}>
                  <div className="trio-card">
                    <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>P/E trailing</div>
                    <div className="trio-value">{trailing}</div>
                    <div className="trio-delta">fuente: <span style={{ color: 'var(--text-secondary)' }}>EPS_TTM</span></div>
                    {trailing === 'n/a' && <div className="txt-xs" style={{ color: 'var(--signal-fail)' }}>⚠ pérdidas en TTM</div>}
                  </div>
                  <div className="trio-card">
                    <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>P/E forward</div>
                    <div className="trio-value">{forward}</div>
                    <div className="trio-delta">consenso: <span style={{ color: d.calibration < -0.05 ? 'var(--signal-warning)' : 'var(--text-secondary)' }}>cal. {(d.calibration*100).toFixed(0)}%</span></div>
                  </div>
                  <div>
                    <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 6 }}>Tu elección · qué métrica usar</div>
                    <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
                      {['trailing', 'forward', 'normalized', 'either'].map(opt => (
                        <button
                          key={opt}
                          className={`chip ${row.choice === opt ? 'chip-active' : ''}`}
                          onClick={() => update(i, 'choice', opt)}
                          style={{ cursor: 'pointer' }}
                        >
                          {opt}
                        </button>
                      ))}
                    </div>
                  </div>
                </div>

                {/* Inputs row */}
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1.2fr 2fr', gap: 12 }}>
                  <div>
                    <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 4 }}>Fuente del input <span style={{ color: 'var(--signal-warning)' }}>*</span></div>
                    <input
                      className="input"
                      placeholder="dataset.row[i].col[j]"
                      value={row.source}
                      onChange={e => update(i, 'source', e.target.value)}
                    />
                  </div>
                  <div>
                    <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 4 }}>Limitación 1-línea <span style={{ color: 'var(--signal-warning)' }}>*</span></div>
                    <input
                      className="input"
                      placeholder="consenso optimista ~10%, carga one-offs…"
                      value={row.limitation}
                      onChange={e => update(i, 'limitation', e.target.value)}
                      style={{ color: row.limitation ? 'var(--signal-hypothesis)' : undefined }}
                    />
                  </div>
                  <div>
                    <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 4 }}>
                      Justificación <span style={{ color: 'var(--signal-warning)' }}>*</span>
                      <span className="txt-ter" style={{ marginLeft: 6 }}>{row.justification.length} / 20 min</span>
                    </div>
                    <input
                      className="input serif"
                      placeholder="Conecta el arquetipo con la elección de métrica…"
                      style={{ fontFamily: 'var(--font-serif)' }}
                      value={row.justification}
                      onChange={e => update(i, 'justification', e.target.value)}
                    />
                  </div>
                </div>
              </div>
            );
          })}

          {/* Feedback banner */}
          <Banner
            tone="warning"
            icon="!"
            title="Banner contextual · feedback_bank.underclaim"
            text='Has visto el dato pero no explicas por qué importa. Conecta el ratio con el arquetipo (cíclica/growth/stalwart/turnaround) y el sesgo del consenso. Reformula como hipótesis verificable.'
            action={<button className="btn btn-ghost txt-xs">Ver glosario →</button>}
          />
        </div>

        {/* Right rail: dataset panel */}
        <div className="col" style={{ gap: 16, position: 'sticky', top: 90, alignSelf: 'flex-start' }}>
          <div className="card" style={{ padding: 14, position: 'relative', overflow: 'hidden' }}>
            <div className="row" style={{ justifyContent: 'space-between', marginBottom: 10 }}>
              <span className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>Dataset · M01.E02</span>
              <button className="btn btn-ghost" style={{ padding: '2px 6px', fontSize: 11 }}>⤢</button>
            </div>
            <table className="table" style={{ fontSize: 11 }}>
              <thead>
                <tr><th></th><th className="num">Price</th><th className="num">EPS_TTM</th><th className="num">EPS_NTM</th></tr>
              </thead>
              <tbody>
                {DATASET.map(d => (
                  <tr key={d.ticker}>
                    <td className="mono" style={{ fontSize: 11 }}>{d.ticker}</td>
                    <td className="num">{d.price.toFixed(2)}</td>
                    <td className="num" style={{ color: d.eps_ttm < 0 ? 'var(--signal-fail)' : undefined }}>{d.eps_ttm.toFixed(2)}</td>
                    <td className="num">{d.eps_ntm.toFixed(2)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
            <Watermark />
            <div style={{ height: 10 }}/>
          </div>

          <div className="card" style={{ padding: 14 }}>
            <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 10 }}>Rúbrica en vivo</div>
            <RubricMeter rubric={[
              { label: 'Precisión técnica', score: completeRows * 24 },
              { label: 'Cita de fuente',    score: rows.filter(r=>r.source).length * 25 },
              { label: 'Limitación reconocida', score: rows.filter(r=>r.limitation).length * 25 },
              { label: 'Lenguaje prudente', score: 90 },
              { label: 'Convergencia analítica', score: completeRows * 20 },
            ]}/>
          </div>
        </div>
      </div>

      <div className="row" style={{ justifyContent: 'space-between', marginTop: 24, padding: '14px 0', borderTop: '1px solid var(--border-subtle)' }}>
        <div className="row" style={{ gap: 14 }}>
          <span className="chip">Retries: <strong style={{ color: 'var(--text-primary)', marginLeft: 4 }}>2 disponibles</strong></span>
          <span className="txt-xs txt-sec">{completeRows} / 4 filas completas</span>
        </div>
        <div className="row" style={{ gap: 8 }}>
          <button className="btn btn-ghost">Guardar borrador</button>
          <button className="btn btn-primary" disabled={!canSubmit} onClick={onComplete}>
            Enviar y continuar a S4
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M5 3l5 5-5 5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
          </button>
        </div>
      </div>
    </div>
  );
};

// ============================================================================
// S4 — mini_memo with prohibited language linter
// ============================================================================
const PROHIBITED = ['compra', 'compras', 'vende', 'vender', 'venta', 'garantía', 'garantizo', 'fraude', 'barata', 'cara', 'carísim', 'baratísim'];

const renderMemo = (text) => {
  if (!text) return null;
  // tokenize keeping whitespace and punctuation
  const parts = text.split(/(\b)/);
  return parts.map((p, i) => {
    const lower = p.toLowerCase();
    const hit = PROHIBITED.find(w => lower.includes(w));
    if (hit) return <mark key={i} className="prohibited" title={`Lenguaje prohibido: "${hit}" — usa "señal de", "indicador de", "múltiplo aparente"`}>{p}</mark>;
    return <span key={i}>{p}</span>;
  });
};

const Slot_S4 = () => {
  const [memo, setMemo] = useSh(
    'Tras analizar las 4 empresas con P/E trailing y forward, identifico dos casos donde el múltiplo aparente engaña: CICL.MC en pico cíclico (EPS_TTM infla el denominador y el P/E forward sigue el ciclo) y TURN.MC con earnings negativos en TTM que invalidan el ratio. La empresa STLW.MC es la única donde ambos P/E convergen y el consenso está bien calibrado: la decisión metodológica es secundaria. Para GROW.MC, P/E forward 33× se justifica si el consenso —sobre-optimista ~12% históricamente— se corrige; el múltiplo es señal de premium de crecimiento, no compra automática.'
  );

  const prohibitedHits = PROHIBITED.filter(p => memo.toLowerCase().includes(p));
  const wordCount = memo.trim().split(/\s+/).filter(Boolean).length;

  return (
    <div>
      <div className="row" style={{ gap: 8, marginBottom: 16 }}>
        <span className="badge" style={{ borderColor: 'var(--accent)', color: 'var(--accent)' }}>S4 · TRANSFERENCIA</span>
        <span className="txt-xs txt-sec">Redacta como si lo enviases a un comité: breve, trazable y prudente.</span>
      </div>

      <h2 className="serif" style={{ fontSize: 'var(--size-xl)', fontWeight: 500, lineHeight: 1.3, margin: '0 0 20px 0' }}>
        Memo: ¿Cuándo P/E forward, cuándo trailing, cuándo normalized?
      </h2>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 20 }}>
        <div>
          {/* Memo editor */}
          <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
            <div style={{ padding: '10px 14px', background: 'var(--surface-2)', borderBottom: '1px solid var(--border-subtle)', display: 'flex', justifyContent: 'space-between' }}>
              <div className="row" style={{ gap: 8 }}>
                <button className="btn btn-ghost" style={{ padding: '2px 8px', fontSize: 11 }}>B</button>
                <button className="btn btn-ghost" style={{ padding: '2px 8px', fontSize: 11, fontStyle: 'italic' }}>I</button>
                <button className="btn btn-ghost" style={{ padding: '2px 8px', fontSize: 11 }}>U</button>
                <span style={{ width: 1, height: 14, background: 'var(--border-default)' }}/>
                <span className="txt-xs txt-sec">Cmd+K: sugerencia · Cmd+Enter: enviar</span>
              </div>
              <span className="mono txt-xs txt-sec">{wordCount} palabras · objetivo 150–250</span>
            </div>
            <div style={{ position: 'relative' }}>
              <textarea
                className="textarea"
                style={{ border: 0, minHeight: 280, fontSize: 'var(--size-lg)' }}
                value={memo}
                onChange={e => setMemo(e.target.value)}
              />
              {/* Overlay version showing prohibited */}
              <div
                aria-hidden="true"
                style={{
                  position: 'absolute', inset: 0,
                  padding: '12px 14px',
                  fontFamily: 'var(--font-serif)',
                  fontSize: 'var(--size-lg)',
                  lineHeight: 'var(--lh-prose)',
                  color: 'transparent',
                  pointerEvents: 'none',
                  whiteSpace: 'pre-wrap',
                  wordBreak: 'break-word',
                }}
              >
                {renderMemo(memo)}
              </div>
            </div>
          </div>

          {/* Linter feedback */}
          {prohibitedHits.length > 0
            ? <div style={{ marginTop: 14 }}>
                <Banner
                  tone="fail"
                  icon="!"
                  title={`Linter · ${prohibitedHits.length} término${prohibitedHits.length>1?'s':''} prohibido${prohibitedHits.length>1?'s':''}`}
                  text={
                    <span>
                      Detectados: {prohibitedHits.map((w, i) => <span key={w}><span className="mono" style={{ color: 'var(--signal-fail)' }}>"{w}"</span>{i < prohibitedHits.length - 1 ? ', ' : ''}</span>)}.
                      Reemplazos sugeridos: <em>“señal de”</em>, <em>“indicador potencial de”</em>, <em>“múltiplo aparente por debajo de”</em>.
                    </span>
                  }
                />
              </div>
            : <div style={{ marginTop: 14 }}>
                <Banner tone="pass" icon="✓" title="Linter · 0 términos prohibidos" text="Tu memo usa lenguaje prudente. Recuerda: barata/cara/garantía no son análisis, son atajos."/>
              </div>
          }

          {/* Evidence slot */}
          <div className="card" style={{ padding: 16, marginTop: 14, borderStyle: 'dashed' }}>
            <div className="row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
              <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>Evidencia pendiente · opcional pero recomendado</div>
              <span className="chip chip-hypothesis">Regla Finaz</span>
            </div>
            <input
              className="input serif"
              defaultValue="Verificar margen operativo histórico de GROW.MC en últimas 3 caídas de consenso para calibrar fiabilidad del P/E forward."
              style={{ fontFamily: 'var(--font-serif)' }}
            />
          </div>
        </div>

        {/* Right rail: rubric in real time + threshold */}
        <div className="col" style={{ gap: 16, position: 'sticky', top: 90, alignSelf: 'flex-start' }}>
          <div className="card" style={{ padding: 16 }}>
            <div className="row" style={{ justifyContent: 'space-between', marginBottom: 12 }}>
              <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase' }}>Rúbrica multidimensional</div>
              <span className="chip chip-active">vivo</span>
            </div>
            <RubricMeter rubric={[
              { label: 'Precisión técnica', score: 92 },
              { label: 'Cita de fuente',    score: 84 },
              { label: 'Lenguaje prudente', score: prohibitedHits.length === 0 ? 95 : 35 },
              { label: 'Convergencia',      score: 78 },
              { label: 'Utilidad valoración', score: 81 },
            ]}/>
            <div className="divider"></div>
            <div className="row" style={{ justifyContent: 'space-between', fontSize: 'var(--size-sm)' }}>
              <span className="txt-sec">Mastery threshold</span>
              <span className="mono">75 / 100</span>
            </div>
          </div>

          <div className="card" style={{ padding: 16 }}>
            <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 10 }}>Lenguaje prohibido</div>
            <div className="col" style={{ gap: 4 }}>
              {['compra', 'vende', 'garantía', 'fraude', 'barata', 'cara'].map(w => (
                <div key={w} className="row" style={{ justifyContent: 'space-between' }}>
                  <span className="mono txt-sm" style={{ color: memo.toLowerCase().includes(w) ? 'var(--signal-fail)' : 'var(--text-secondary)' }}>"{w}"</span>
                  <span className="txt-xs txt-ter">{memo.toLowerCase().includes(w) ? '1 hit' : '—'}</span>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>

      <div className="row" style={{ justifyContent: 'space-between', marginTop: 24, padding: '14px 0', borderTop: '1px solid var(--border-subtle)' }}>
        <div className="row" style={{ gap: 10 }}>
          <span className="chip chip-pass">Borrador guardado</span>
          <span className="txt-xs txt-ter mono">hace 12s</span>
        </div>
        <div className="row" style={{ gap: 8 }}>
          <button className="btn">Revisar antes de enviar</button>
          <button className="btn btn-primary" disabled={prohibitedHits.length > 0}>
            Enviar memo
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M5 8h6m0 0l-3-3m3 3l-3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
          </button>
        </div>
      </div>
    </div>
  );
};

// ============================================================================
// P03 — Episode Shell (the container)
// ============================================================================
const P03_Shell = ({ initialStep = 'S3_lab', showTutor = false, showFreeze = false }) => {
  const [step, setStep] = useSh(initialStep);
  const [completed, setCompleted] = useSh(['S1_hook', 'S2_concept']);
  const [tutorOpen, setTutorOpen] = useSh(showTutor);
  const [freezeOpen, setFreezeOpen] = useSh(showFreeze);
  const [activeTerm, setActiveTerm] = useSh('pe_forward');

  const advance = () => {
    setCompleted(prev => Array.from(new Set([...prev, step])));
    const order = ['S1_hook', 'S2_concept', 'S3_lab', 'S4_transfer'];
    const next = order[order.indexOf(step) + 1];
    if (next) setStep(next);
  };

  return (
    <div className="artboard-root">
      <EpisodeHeader
        breadcrumb="C04-A · M01 · E02"
        title="Trailing vs forward: ¿qué foto miras?"
        bigQuestion="¿Cuándo prefieres P/E forward sobre P/E trailing?"
        durationDone={step === 'S3_lab' ? 18 : step === 'S2_concept' ? 9 : step === 'S4_transfer' ? 27 : 4}
        durationTotal={30}
        onPause={() => setFreezeOpen(true)}
      />

      {/* freeze modal */}
      {freezeOpen && (
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.65)', zIndex: 100, display: 'grid', placeItems: 'center' }}>
          <div className="card" style={{ width: 480, padding: 24, background: 'var(--surface-1)' }}>
            <div className="row" style={{ justifyContent: 'space-between', marginBottom: 14 }}>
              <span className="chip chip-active">Pausa · scene_resume_exact</span>
              <button className="btn btn-ghost" onClick={() => setFreezeOpen(false)}>✕</button>
            </div>
            <h3 className="serif" style={{ fontSize: 'var(--size-xl)', margin: '0 0 8px 0', fontWeight: 500 }}>Vas a pausar el episodio</h3>
            <p className="txt-sec" style={{ marginTop: 0 }}>Volverás <strong>exactamente aquí</strong>. Guardamos tu progreso en este dispositivo y en tu cuenta.</p>
            <div className="card" style={{ padding: 14, marginBottom: 16, background: 'var(--surface-2)' }}>
              <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 8 }}>Mini-resumen autogenerado</div>
              <ul className="col" style={{ gap: 4, fontSize: 'var(--size-sm)' }}>
                <li>✓ S1 completado · escuchaste la locución de Sofía/Mateo/Tutor</li>
                <li>✓ S2 completado · consultaste 3 términos del glosario</li>
                <li style={{ color: 'var(--accent)' }}>▸ S3 en curso · 2 / 4 filas con justificación completa</li>
              </ul>
            </div>
            <div className="row" style={{ justifyContent: 'space-between' }}>
              <button className="btn btn-ghost" onClick={() => setFreezeOpen(false)}>Cancelar</button>
              <button className="btn btn-primary" onClick={() => setFreezeOpen(false)}>Pausar y salir</button>
            </div>
          </div>
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: '220px 1fr 280px', maxWidth: 1400, margin: '0 auto', minHeight: 'calc(100% - 80px)' }}>
        {/* Left: step rail */}
        <aside style={{ padding: '24px 16px', borderRight: '1px solid var(--border-subtle)', position: 'sticky', top: 80, alignSelf: 'flex-start', height: 'fit-content' }}>
          <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 10, padding: '0 8px' }}>Pasos del episodio</div>
          <StepRail current={step} completed={completed} onChange={setStep}/>
          <div className="divider"></div>
          <div className="txt-xs txt-sec" style={{ padding: '0 8px', lineHeight: 1.5 }}>
            <span className="mono">J/K</span> entre pasos · <span className="mono">G</span> glosario · <span className="mono">T</span> tutor · <span className="mono">Space</span> pausa
          </div>
        </aside>

        {/* Center: slot content */}
        <main style={{ padding: '24px 28px' }}>
          {step === 'S1_hook'     && <Slot_S1 onComplete={advance}/>}
          {step === 'S2_concept'  && <Slot_S2 onComplete={advance}/>}
          {step === 'S3_lab'      && <Slot_S3 onComplete={advance}/>}
          {step === 'S4_transfer' && <Slot_S4 />}
        </main>

        {/* Right: glossary + tutor */}
        <aside style={{ padding: '24px 16px', borderLeft: '1px solid var(--border-subtle)', position: 'sticky', top: 80, alignSelf: 'flex-start', height: 'fit-content', display: 'flex', flexDirection: 'column', gap: 16 }}>
          <GlossaryRail
            terms={['pe_trailing', 'pe_forward', 'consenso_calibration']}
            active={activeTerm}
          />
          <div className="divider"></div>
          <button
            className="btn btn-primary"
            style={{ justifyContent: 'center', padding: '12px 14px' }}
            onClick={() => setTutorOpen(o => !o)}
          >
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
              <circle cx="8" cy="8" r="6" stroke="currentColor"/>
              <path d="M6 7c0-1 .8-2 2-2s2 .8 2 1.7c0 .8-.6 1.2-1.2 1.5-.5.3-.8.5-.8 1M8 11.5h.01" stroke="currentColor" strokeLinecap="round"/>
            </svg>
            Tutor Finaz
          </button>
          <div className="card" style={{ padding: 12 }}>
            <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 8 }}>Notas del lab</div>
            <div className="txt-xs txt-sec" style={{ lineHeight: 1.5 }}>
              Cíclica en pico: ni trailing (peak EPS) ni forward (consenso sigue ciclo). Plantea normalized.
            </div>
          </div>
        </aside>

        {/* Tutor drawer */}
        {tutorOpen && (
          <div style={{
            position: 'fixed', top: 0, right: 0, bottom: 0,
            width: 380, background: 'var(--surface-1)',
            borderLeft: '1px solid var(--border-strong)',
            zIndex: 50, padding: 20,
            display: 'flex', flexDirection: 'column', gap: 14,
            boxShadow: '-12px 0 32px rgba(0,0,0,0.4)',
          }}>
            <div className="row" style={{ justifyContent: 'space-between' }}>
              <div className="row" style={{ gap: 10 }}>
                <span className="voice-glyph tutor" style={{ width: 28, height: 28 }}/>
                <div>
                  <div className="bold">Tutor Finaz</div>
                  <div className="txt-xs txt-ter">scope: ask_socratic, explain_formula, show_hint, map_to_assertion</div>
                </div>
              </div>
              <button className="btn btn-ghost" onClick={() => setTutorOpen(false)}>✕</button>
            </div>
            <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
              <span className="chip chip-active">Explica fórmula</span>
              <span className="chip">Pista</span>
              <span className="chip">Pregúntame socráticamente</span>
              <span className="chip">Abrir glosario</span>
              <span className="chip">Mapear a afirmación</span>
              <span className="chip">Sugiere remediación</span>
            </div>
            <div className="divider" style={{ margin: '4px 0' }}/>
            <div className="col" style={{ gap: 10, flex: 1, overflowY: 'auto' }}>
              <div className="voice-card voice-tutor" style={{ background: 'var(--surface-2)' }}>
                <div className="voice-avatar"><span className="voice-glyph tutor"/></div>
                <div>
                  <div className="voice-name">Tutor Finaz</div>
                  <div className="voice-text" style={{ fontSize: 'var(--size-sm)', fontFamily: 'var(--font-text)' }}>
                    Te has decantado por <em>forward</em> en CICL.MC. Antes de avanzar: ¿qué nos dice la calibración del consenso histórico para una cíclica? Y si EPS_NTM cae 30% el próximo año, ¿qué le pasa al múltiplo aparente?
                  </div>
                </div>
              </div>
              <div className="card" style={{ padding: 12, borderLeft: '3px solid var(--signal-warning)' }}>
                <div className="txt-xs txt-ter mono" style={{ textTransform: 'uppercase', marginBottom: 6 }}>Forbidden detectado</div>
                <div className="txt-sm txt-sec">No respondo a "compra X" ni "vende Y". Si quieres, te explico la fórmula o te propongo una hipótesis falsable.</div>
              </div>
            </div>
            <div style={{ position: 'relative' }}>
              <input className="input" placeholder="Pregúntame sobre P/E forward, calibración, normalized…" style={{ paddingRight: 40 }}/>
              <button className="btn btn-ghost" style={{ position: 'absolute', right: 4, top: 4, padding: 4 }}>
                <svg width="14" height="14" viewBox="0 0 16 16" fill="none"><path d="M2 8l12-6-4 14-2-6-6-2z" stroke="currentColor" strokeLinejoin="round"/></svg>
              </button>
            </div>
            <div className="txt-xs txt-ter" style={{ borderTop: '1px solid var(--border-subtle)', paddingTop: 10 }}>
              El tutor no da consejo de inversión. Es asistente pedagógico.
            </div>
          </div>
        )}
      </div>

      <DisclaimerFooter />
    </div>
  );
};

Object.assign(window, { P03_Shell, Slot_S1, Slot_S2, Slot_S3, Slot_S4 });
