// ─────────────────────────────────────────────────────────────
// app.jsx — Top-level Kidora demo orchestrator
// ─────────────────────────────────────────────────────────────

const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React;

const STAGE_W = 1360;
const STAGE_H = 920;

// ─── chapter index built from BEATS ────────────────────────
function buildChapters(beats, totalMs) {
  const chaps = beats.filter(b => b.kind === 'chapter');
  return chaps.map((c, i) => ({
    n: c.n,
    title: c.title,
    accent: c.accent,
    startT: c.t,
    endT: (chaps[i + 1] ? chaps[i + 1].t : totalMs),
  }));
}

// ─── canned sandbox replies ─────────────────────────────
function getCannedReply(chip) {
  const map = {
    pay: { delay: 700, messages: [
      { text: 'Tres familias pendientes esta semana — $755 en total. Los mensajes ya están redactados:' },
      { card: 'payments' },
    ]},
    ocfs: { delay: 700, messages: [
      { text: 'Tu carpeta OCFS está al día con una sola excepción:' },
      { alert: { accent: '#1a2d5a', icon: '!', head: 'CPR de Yarianny vence en 22 días',
        body: 'Vence el 15 de junio. Te programé un recordatorio para el martes. ¿Quieres que reserve el curso ya?' } },
    ]},
    inspector: { delay: 800, messages: [
      { text: 'Inspector aquí. Esto es lo que necesitas a mano, maestra:' },
      { bullets: [
        'Licencia FDC-554781 — vigente hasta 2027',
        'Roster: 8 niños · 5 presentes · 1 tarde · 2 ausentes con justificación',
        'Carpeta de incidentes 2026: 1 reporte menor (Mateo · curita)',
        'CPR: 2 al día, 1 vence en 22 días (Yarianny — ya agendada)',
        'Menús de la semana actualizados',
        'Plan de emergencia revisado el 12 de mayo',
      ] },
      { text: 'Te abrí la carpeta en pantalla. Todo está en orden, maestra.' },
    ]},
    note: { delay: 500, messages: [
      { text: 'Hecho:', bullets: [
        'Nota agregada al expediente de Yariel · "Contó hasta 10"',
        'Mensaje enviado a Johanny (mamá de Yariel)',
      ] },
    ]},
  };
  return map[chip.id];
}

function getCannedReplyFromText(text) {
  const t = text.toLowerCase();
  if (/paga|pago|debe|cobr/.test(t)) return getCannedReply({ id: 'pay' });
  if (/ocfs|estado|cumpl/.test(t)) return getCannedReply({ id: 'ocfs' });
  if (/inspector|papel|documento/.test(t)) return getCannedReply({ id: 'inspector' });
  if (/yariel|nota|contó|conto/.test(t)) return getCannedReply({ id: 'note' });
  return { delay: 700, messages: [
    { text: 'Te ayudo, maestra. Estoy lista para mensajes a padres, asistencia, cobros, reportes a OCFS, notas en expedientes, cumpleaños, alergias y más.' },
    { text: 'Para verme trabajando con los nombres reales de tus niños, deja tu WhatsApp aquí abajo. Cristina te configura una en menos de 24h.' },
  ]};
}

// ─── animated count-up hook ─────────────────────────────
function useCountUp(target, duration = 900) {
  const [val, setVal] = useState(0);
  const lastTargetRef = useRef(0);
  const valRef = useRef(0);
  const timerRef = useRef(null);

  useEffect(() => {
    if (target === lastTargetRef.current) return;
    const from = valRef.current;
    const to = target;
    lastTargetRef.current = target;
    const start = Date.now();
    if (timerRef.current) clearInterval(timerRef.current);
    timerRef.current = setInterval(() => {
      const e = Math.min(1, (Date.now() - start) / duration);
      const eased = 1 - Math.pow(1 - e, 3);
      const v = from + (to - from) * eased;
      valRef.current = v;
      setVal(v);
      if (e >= 1) {
        clearInterval(timerRef.current);
        timerRef.current = null;
      }
    }, 33);
    return () => { if (timerRef.current) clearInterval(timerRef.current); };
  }, [target, duration]);

  return val;
}

// ─── render a chat item ────────────────────────────────
// ctx carries live App-level callbacks/state for items whose props need to
// reflect current state (the SuggestionChips loading spinner especially).
function renderItem(item, key, ctx) {
  switch (item.type) {
    case 'banner':
      return <ChatBanner key={key} label={item.label} title={item.title} time={item.time} />;
    case 'msg':
      return (
        <Bubble key={key} kind={item.author} name={item.name} time={item.time}>
          {item.text}
          {item.bullets && <BulletList items={item.bullets} />}
        </Bubble>
      );
    case 'card-roster': return <RosterCard key={key} />;
    case 'card-payments': return <PaymentsCard key={key} />;
    case 'card-weekly': return <WeeklyCard key={key} />;
    case 'alert':
      return (
        <AlertPill key={key} accent={item.accent} icon={item.icon} head={item.head}>
          {item.body}
        </AlertPill>
      );
    case 'incident': return <IncidentReport key={key} />;
    case 'handoff': return <HandoffStrip key={key} minutes={item.minutes} />;
    case 'chips': return <SuggestionChips key={key} onPick={item.onPick} onRealApp={ctx && ctx.onRealApp} realAppLoading={ctx && ctx.realAppLoading} />;
    default: return null;
  }
}

// ─── lead form ──────────────────────────────────
function LeadForm({ open, onDismiss }) {
  const [name, setName] = useState('');
  const [whatsapp, setWhatsapp] = useState('');
  const [submitted, setSubmitted] = useState(false);
  const [sending, setSending] = useState(false);
  const [error, setError] = useState('');
  const submit = async (e) => {
    e.preventDefault();
    if (!name || !whatsapp || sending) return;
    setSending(true);
    setError('');
    try {
      const ref = new URLSearchParams(window.location.search).get('for') || '';
      const r = await fetch('/api/demo/lead', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          name: name.trim(),
          whatsapp: whatsapp.trim(),
          ref_param: ref,
          demo_session_id: null,
        }),
      });
      const data = await r.json().catch(() => ({}));
      if (!r.ok || !data.ok) throw new Error(data.error || 'No pudimos guardar — intenta de nuevo');
      setSubmitted(true);
    } catch (err) {
      setError(err.message || 'Algo falló — intenta de nuevo');
    } finally {
      setSending(false);
    }
  };
  const firstName = name.trim().split(' ')[0];
  return (
    <div className={`lead-overlay ${open ? 'open' : ''}`}>
      <div className="scrim" onClick={onDismiss}></div>
      <div className="lead-sheet">
        {!submitted ? (
          <>
            <div className="casita-wrap"><CasitaSVG size={64} /></div>
            <KidoraBlocks />
            <h2>¿Quieres esto en tu guardería?</h2>
            <div className="sub">
              Cristina (la fundadora) te escribe por WhatsApp en menos de 24h
              para configurártela con los nombres de tus niños.
            </div>
            <form onSubmit={submit}>
              <input type="text" placeholder="¿Cómo te llamas, maestra?"
                value={name} onChange={e => setName(e.target.value)} />
              <div className="submit-row">
                <input type="tel" placeholder="Tu WhatsApp"
                  value={whatsapp} onChange={e => setWhatsapp(e.target.value)} />
                <button type="submit" className="submit-btn" disabled={sending || !name.trim() || whatsapp.trim().length < 7}>
                  {sending ? 'Enviando…' : 'Enviar'}
                </button>
              </div>
              {error && <div className="lead-error">{error}</div>}
            </form>
            <div className="social">
              <div className="avatars">
                <div className="av" style={{ background: '#e84040' }}></div>
                <div className="av" style={{ background: '#f07d20' }}></div>
                <div className="av" style={{ background: '#2db54c' }}></div>
                <div className="av" style={{ background: '#20b8c8' }}></div>
              </div>
              <span>Ya somos 12 maestras en El Bronx, Queens y Washington Heights.</span>
            </div>
            <button className="dismiss" onClick={onDismiss}>Sigo explorando</button>
          </>
        ) : (
          <div className="lead-thanks">
            <div className="check">✓</div>
            <h3>Recibido, {firstName || 'maestra'}.</h3>
            <p>Cristina te escribe por WhatsApp en menos de 24h.<br/>Mientras tanto, sigue explorando.</p>
            <button className="dismiss" onClick={onDismiss} style={{ marginTop: 18 }}>Cerrar</button>
          </div>
        )}
      </div>
    </div>
  );
}

// ─── preroll ──────────────────────────────────────
function Preroll({ onPlay }) {
  return (
    <div className="preroll">
      <div className="preroll-main">
        <div className="preroll-wordmark">
          <KidoraBlocks size={22} />
        </div>

        <div className="preroll-art">
          <CasitaSVG size={140} />
        </div>

        <div className="eyebrow">
          <span className="dot"></span>
          <span>Demo en vivo · 2:40</span>
        </div>

        <h1>
          Tu mañana entera,<br/>
          en <em>un solo chat</em>.
        </h1>

        <p className="lede">
          Una mañana real en una guardería de Washington Heights.
          Mateo llega tarde, hay un raspón en el patio, dos alergias por
          verificar y $755 en cobros pendientes.
          <br/>
          Sabrina, tu asistente, lo resuelve todo. <b>Tú miras. Después te toca a ti.</b>
        </p>

        <button className="play" onClick={onPlay}>
          <PlayCircleIcon size={24} />
          <span className="play-label">Empezar el demo</span>
          <ArrowRightIcon size={15} color="white" />
        </button>
      </div>
    </div>
  );
}

// ─── ChatSurface — the chat content (header + scroll + composer)
// Used both inside iOS bezel (desktop) and full-bleed (phone)
function ChatSurface({ items, typingWho, dimmedComposer, handoffPulse, sandboxMode, onSend, composerValue, setComposerValue, native, sceneCaption, onRealApp, realAppLoading }) {
  const scrollRef = useRef(null);
  const inputRef = useRef(null);

  useEffect(() => {
    if (scrollRef.current) {
      setTimeout(() => {
        if (scrollRef.current) {
          scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
        }
      }, 30);
    }
  }, [items.length, typingWho]);

  // Prevent mobile keyboards from popping up at handoff. When the composer
  // transitions from disabled → enabled, iOS/Android can auto-focus the
  // newly-enabled input. Explicitly blur and ensure the active element isn't
  // the input. The visitor focuses it by tapping.
  useEffect(() => {
    if (sandboxMode && inputRef.current) {
      inputRef.current.blur();
      if (document.activeElement === inputRef.current && document.body) {
        document.body.focus?.();
      }
    }
  }, [sandboxMode]);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!composerValue.trim()) return;
    onSend(composerValue.trim());
    setComposerValue('');
  };

  return (
    <>
      <div className={`chat-header ${native ? 'native' : ''}`}>
        <SabrinaAvatar size={native ? 32 : 36} />
        <div className="who">
          <span className="name">Sabrina</span>
          <span className="sub">Asistente de Casita Sol</span>
        </div>
        <span className="menu-dots">⋯</span>
      </div>

      {sandboxMode && !native && (
        <div className="free-pill" style={{ top: 96 }}>
          <span className="dot"></span>
          Modo libre · tu turno
        </div>
      )}

      {sceneCaption && (
        <div className="scene-caption-strip" style={{ '--accent': sceneCaption.accent || '#1a2d5a' }}>
          <span className="dot"></span>
          <span className="lbl">Escena {sceneCaption.n} · {sceneCaption.title}</span>
        </div>
      )}

      <div className={`chat-scroll ${native ? 'native' : ''} ${sceneCaption ? 'with-caption' : ''}`} ref={scrollRef}>
        {items.map((it, i) => renderItem(it, `it-${i}`, { onRealApp, realAppLoading }))}
        {typingWho && <TypingBubble kind={typingWho} key={`typing-${items.length}`} />}
      </div>

      <div className={`composer ${native ? 'native' : ''} ${dimmedComposer ? 'dim' : ''} ${handoffPulse ? 'handoff' : ''}`}>
        <div className="composer-glow"></div>
        <form className="composer-row" onSubmit={handleSubmit}>
          <input
            ref={inputRef}
            className="composer-input"
            placeholder={sandboxMode ? 'Pregúntale a Sabrina…' : 'Sabrina te está poniendo al día…'}
            value={composerValue}
            onChange={e => setComposerValue(e.target.value)}
            disabled={!sandboxMode}
            autoFocus={false}
          />
          <button type="button" className="composer-mic" tabIndex={-1}>
            <MicIcon />
          </button>
          <button type="submit" className="composer-btn" disabled={!sandboxMode || !composerValue.trim()}>
            <SendIcon />
          </button>
        </form>
      </div>
    </>
  );
}

// ─── Phone chat surface (desktop — wrapped in iOS bezel) ──────────────────
function PhoneChat(props) {
  return (
    <IOSDevice width={402} height={800}>
      <ChatSurface {...props} />
    </IOSDevice>
  );
}

// ─── Scale-to-fit container ─────────────────
function StageFit({ children }) {
  const [scale, setScale] = useState(1);
  useLayoutEffect(() => {
    const recompute = () => {
      const sw = window.innerWidth / STAGE_W;
      const sh = window.innerHeight / STAGE_H;
      setScale(Math.min(sw, sh, 1));
    };
    recompute();
    window.addEventListener('resize', recompute);
    return () => window.removeEventListener('resize', recompute);
  }, []);
  return (
    <div className="stage-fit">
      <div className="stage" style={{ transform: `scale(${scale})` }}>
        {children}
      </div>
    </div>
  );
}

// ─── Device detection ─────────────────
function detectDevice() {
  if (typeof window === 'undefined') return 'desktop';
  const w = window.innerWidth;
  const h = window.innerHeight;
  const ua = navigator.userAgent || '';
  const touch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;

  // Detect device type
  const isAndroid = /Android/i.test(ua);
  const isPhoneUA = /iPhone|iPod/i.test(ua) || (isAndroid && /Mobile/i.test(ua));
  const isTabletUA = /iPad|Tablet/i.test(ua) || (isAndroid && !/Mobile/i.test(ua));

  if (isPhoneUA) return 'phone';
  // Tablets: portrait → phone shell (taller chat, no side chrome); landscape → desktop chrome (scaled)
  if (isTabletUA) {
    return w > h ? 'desktop' : 'phone';
  }
  // PCs and unknown: by width. iPads up to 11" portrait (834) → phone shell.
  if (w < 1024) return 'phone';
  return 'desktop';
}

function useDevice() {
  const [device, setDevice] = useState(detectDevice);
  useEffect(() => {
    let raf;
    const onResize = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => setDevice(detectDevice()));
    };
    window.addEventListener('resize', onResize);
    window.addEventListener('orientationchange', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
      window.removeEventListener('orientationchange', onResize);
      cancelAnimationFrame(raf);
    };
  }, []);
  return device;
}

// ─── Main App ─────────────────────────────
function App() {
  const chapters = useMemo(() => buildChapters(BEATS, TOTAL_MS), []);

  // Phase: preroll → cinema → handoff → sandbox
  const [phase, setPhase] = useState('preroll');
  // Whether to mount preroll (separate so we can fade-out then unmount)
  const [prerollMounted, setPrerollMounted] = useState(true);
  const [prerollFading, setPrerollFading] = useState(false);

  // Chrome visibility
  const [chromeMounted, setChromeMounted] = useState(true);
  const [chromeFading, setChromeFading] = useState(false);

  const [items, setItems] = useState([]);
  const [typingWho, setTypingWho] = useState(null);
  const [savedTarget, setSavedTarget] = useState(0);
  const savedDisplay = useCountUp(savedTarget, 900);
  const [savedTick, setSavedTick] = useState(null);
  const [spotlight, setSpotlight] = useState(null);
  const [currentChapter, setCurrentChapter] = useState(null);
  const [elapsed, setElapsed] = useState(0);
  const [handoffPulse, setHandoffPulse] = useState(false);
  const [composerValue, setComposerValue] = useState('');
  const [userSentCount, setUserSentCount] = useState(0);
  const [showLead, setShowLead] = useState(false);
  const [realAppLoading, setRealAppLoading] = useState(false);

  const timersRef = useRef([]);
  const elapsedRafRef = useRef(null);
  const startTimeRef = useRef(0);
  const idleRef = useRef(null);

  const push = useCallback((item) => {
    setItems(prev => [...prev, item]);
  }, []);

  // Dispatch one beat
  const dispatchBeat = useCallback((b) => {
    switch (b.kind) {
      case 'banner':
        push({ type: 'banner', label: b.label, title: b.title, time: b.time });
        break;
      case 'chapter':
        setCurrentChapter({ n: b.n, title: b.title, accent: b.accent });
        break;
      case 'spotlight':
        setSpotlight({ eyebrow: b.eyebrow, text: b.text, key: b.t });
        break;
      case 'msg':
        setTypingWho(null);
        push({ type: 'msg', author: b.author, name: b.name, time: b.time, text: b.text, bullets: b.bullets });
        break;
      case 'typing':
        setTypingWho(b.who);
        break;
      case 'untyping':
        setTypingWho(null);
        break;
      case 'card':
        if (b.card === 'roster') push({ type: 'card-roster' });
        else if (b.card === 'payments') push({ type: 'card-payments' });
        else if (b.card === 'weekly') push({ type: 'card-weekly' });
        break;
      case 'alert':
        push({ type: 'alert', accent: b.accent, icon: b.icon, head: b.head, body: b.body });
        break;
      case 'incident':
        push({ type: 'incident' });
        break;
      case 'time':
        setSavedTarget(prev => prev + b.mins);
        setSavedTick({ mins: b.mins, label: b.label, key: b.t });
        setTimeout(() => setSavedTick(cur => (cur && cur.key === b.t ? null : cur)), 1300);
        break;
      case 'handoff':
        runHandoff();
        break;
    }
  }, [push]);

  // Start cinema
  const startCinema = useCallback(() => {
    setPrerollFading(true);
    setTimeout(() => setPrerollMounted(false), 700);
    setPhase('cinema');
    setItems([]);
    setSavedTarget(0);
    startTimeRef.current = Date.now();

    // setInterval at 100ms — drives progress rail. 60fps RAF causes too many re-renders.
    elapsedRafRef.current = setInterval(() => {
      const e = Date.now() - startTimeRef.current;
      setElapsed(e);
      if (e >= TOTAL_MS + 200) {
        clearInterval(elapsedRafRef.current);
        elapsedRafRef.current = null;
      }
    }, 100);

    BEATS.forEach((b) => {
      const id = setTimeout(() => dispatchBeat(b), b.t);
      timersRef.current.push(id);
    });
  }, [dispatchBeat]);

  const armIdleLeadTimer = useCallback((ms) => {
    if (idleRef.current) clearTimeout(idleRef.current);
    idleRef.current = setTimeout(() => setShowLead(true), ms);
  }, []);

  const respondCanned = useCallback((reply) => {
    setTimeout(() => setTypingWho('sabrina'), 250);
    setTimeout(() => {
      setTypingWho(null);
      reply.messages.forEach((m, i) => {
        setTimeout(() => {
          if (m.card === 'payments') push({ type: 'card-payments' });
          else if (m.card === 'weekly') push({ type: 'card-weekly' });
          else if (m.card === 'roster') push({ type: 'card-roster' });
          else if (m.alert) push({ type: 'alert', ...m.alert });
          else push({ type: 'msg', author: 'sabrina', time: 'Ahora', text: m.text, bullets: m.bullets });
        }, i * 700);
      });
    }, reply.delay);
  }, [push]);

  const handleChipPick = useCallback((chip) => {
    push({ type: 'msg', author: 'owner', time: 'Ahora', text: chip.label });
    respondCanned(getCannedReply(chip));
    setUserSentCount(c => {
      const next = c + 1;
      armIdleLeadTimer(next === 1 ? 30000 : 60000);
      return next;
    });
  }, [push, respondCanned, armIdleLeadTimer]);

  const handleSend = useCallback((text) => {
    push({ type: 'msg', author: 'owner', time: 'Ahora', text });
    respondCanned(getCannedReplyFromText(text));
    setUserSentCount(c => {
      const next = c + 1;
      armIdleLeadTimer(next === 1 ? 30000 : 60000);
      return next;
    });
  }, [push, respondCanned, armIdleLeadTimer]);

  // Hand off to the real KIDORA app: auto-login as demo@kidora.app, stash
  // tokens via Supabase, redirect to /?demo=1. Legacy tour.js then runs its
  // in-app chip tour over the real product (per user choice — kidora_demo_tour
  // = 'pending').
  const enterRealApp = useCallback(async () => {
    if (realAppLoading) return;
    setRealAppLoading(true);
    try {
      const ref = new URLSearchParams(window.location.search).get('for') || '';
      const [cfg, init] = await Promise.all([
        fetch('/config').then(r => r.json()),
        fetch('/api/demo/init', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ for: ref }),
        }).then(r => r.json()),
      ]);
      if (!init.access_token) throw new Error(init.error || 'no token');
      if (!window.supabase) throw new Error('supabase-js not loaded');
      const sb = window.supabase.createClient(cfg.supabaseUrl, cfg.supabaseAnonKey);
      await sb.auth.setSession({
        access_token: init.access_token,
        refresh_token: init.refresh_token,
      });
      try {
        localStorage.setItem('kidora_lang', 'es');
        localStorage.setItem('kidora_voice_lang', 'es');
        localStorage.setItem('kidora_demo_tour', 'pending');
        if (init.demo_session_id) localStorage.setItem('kidora_demo_session_id', init.demo_session_id);
        if (ref) localStorage.setItem('kidora_demo_for', ref);
      } catch (_) {}
      window.location.replace('/?demo=1');
    } catch (err) {
      console.error('[demo] real-app handoff failed:', err);
      setRealAppLoading(false);
      alert('No pudimos abrir Casita Sol. Intenta de nuevo o escríbele a Cristina por WhatsApp.');
    }
  }, [realAppLoading]);

  const runHandoff = useCallback(() => {
    setSpotlight(null);
    setCurrentChapter(null);
    setPhase('handoff');
    setSavedTarget(109);

    // Clear cinema chat and seed sandbox
    setTimeout(() => {
      setItems([
        { type: 'handoff', minutes: 109 },
      ]);
    }, 600);

    setTimeout(() => push({ type: 'msg', author: 'sabrina', time: 'Ahora',
      text: 'Tu turno, maestra. Toca una o escríbeme tú:' }), 1300);
    setTimeout(() => push({ type: 'chips', onPick: handleChipPick }), 1600);

    // Fade out chrome, then unmount
    setTimeout(() => setChromeFading(true), 1700);
    setTimeout(() => {
      setChromeMounted(false);
      setPhase('sandbox');
      setHandoffPulse(true);
      armIdleLeadTimer(60000);
    }, 2500);
    setTimeout(() => setHandoffPulse(false), 5000);
  }, [push, handleChipPick, armIdleLeadTimer]);

  // Skip = play through to end instantly
  const skipToEnd = useCallback(() => {
    timersRef.current.forEach(id => clearTimeout(id));
    timersRef.current = [];
    if (elapsedRafRef.current) clearInterval(elapsedRafRef.current);

    setElapsed(TOTAL_MS);
    setSpotlight(null);
    setItems([]);
    setTypingWho(null);
    setSavedTarget(99); // sum of all minute beats; handoff snaps it to 109

    BEATS.forEach((b) => {
      if (b.kind === 'chapter') setCurrentChapter({ n: b.n, title: b.title, accent: b.accent });
      if (b.kind === 'banner') push({ type: 'banner', label: b.label, title: b.title, time: b.time });
      if (b.kind === 'msg') push({ type: 'msg', author: b.author, name: b.name, time: b.time, text: b.text, bullets: b.bullets });
      if (b.kind === 'card') {
        if (b.card === 'roster') push({ type: 'card-roster' });
        else if (b.card === 'payments') push({ type: 'card-payments' });
        else if (b.card === 'weekly') push({ type: 'card-weekly' });
      }
      if (b.kind === 'alert') push({ type: 'alert', accent: b.accent, icon: b.icon, head: b.head, body: b.body });
      if (b.kind === 'incident') push({ type: 'incident' });
    });
    runHandoff();
  }, [push, runHandoff]);

  useEffect(() => () => {
    timersRef.current.forEach(id => clearTimeout(id));
    if (elapsedRafRef.current) clearInterval(elapsedRafRef.current);
    if (idleRef.current) clearTimeout(idleRef.current);
  }, []);

  const dimmedComposer = phase === 'preroll' || phase === 'cinema' || phase === 'handoff';
  const sandboxMode = phase === 'sandbox';
  const device = useDevice();

  const surfaceProps = {
    items, typingWho,
    dimmedComposer, handoffPulse, sandboxMode,
    composerValue, setComposerValue,
    onSend: handleSend,
    onRealApp: enterRealApp,
    realAppLoading,
  };

  // ────────── PHONE / TABLET-PORTRAIT SHELL ──────────
  if (device === 'phone') {
    return (
      <div className="phone-shell">
        {(phase === 'cinema' || phase === 'handoff') && (
          <div className="phone-topbar">
            <div className="phone-topbar-brand"><BrandMark /></div>
            <div className="phone-topbar-saved">
              <span className="dot"></span>
              <span><b>{Math.round(savedDisplay)}</b><span className="unit">min ahorrados</span></span>
              {savedTick && <span key={savedTick.key} className="tick-mini">+{savedTick.mins}</span>}
            </div>
            {phase === 'cinema' && (
              <button className="phone-topbar-skip" onClick={skipToEnd}>
                <span>Saltar</span>
                <ArrowRightIcon size={11} color="currentColor"/>
              </button>
            )}
          </div>
        )}

        <div className="phone-body">
          <ChatSurface
            native
            sceneCaption={(phase === 'cinema') ? currentChapter : null}
            {...surfaceProps}
          />
        </div>

        {sandboxMode && (
          <div className="phone-restart">
            <button className="restart" onClick={() => window.location.reload()}>
              <RestartIcon size={13} color="currentColor" />
              <span>Reiniciar demo</span>
            </button>
          </div>
        )}

        <LeadForm open={showLead} onDismiss={() => { setShowLead(false); armIdleLeadTimer(120000); }} />

        {prerollMounted && <Preroll onPlay={startCinema} />}
      </div>
    );
  }

  // ────────── DESKTOP / TABLET-LANDSCAPE SHELL ──────────
  return (
    <StageFit>
      <div className="scene-wrap">
        {chromeMounted && (
          <>
            <div className={`top-rail ${chromeFading ? 'fading' : ''}`}>
              <BrandMark />
              <div className="now-playing">
                <span className="rec-dot"></span>
                <span>Demo en curso</span>
              </div>
              <ProgressRail chapters={chapters} currentChapter={currentChapter} elapsed={elapsed} />
              {phase === 'cinema' && (
                <button className="skip-btn" onClick={skipToEnd}>
                  <span>Saltar al modo libre</span>
                  <ArrowRightIcon size={12} color="currentColor"/>
                </button>
              )}
            </div>

            <div className={`left-col ${chromeFading ? 'fading' : ''}`}>
              <SceneChip chapter={currentChapter} />
              <TimeHUD savedMins={savedDisplay} tick={savedTick} />
            </div>

            <div className={`right-col ${chromeFading ? 'fading' : ''}`}>
              <Spotlight spotlight={spotlight} />
            </div>

            <div className={`bottom-bar ${chromeFading ? 'fading' : ''}`}>
              <BottomCaption mode={phase === 'cinema' ? 'playing' : phase} />
            </div>
          </>
        )}

        {/* Center column always rendered */}
        <div className="center-col" style={
          !chromeMounted ? { gridColumn: '1 / 4', gridRow: '1 / 4' } : {}
        }>
          <div style={{ position: 'relative', width: 402, height: 800, borderRadius: 48, overflow: 'hidden' }}>
            <PhoneChat {...surfaceProps} />
            <LeadForm open={showLead} onDismiss={() => { setShowLead(false); armIdleLeadTimer(120000); }} />
          </div>
        </div>

        {sandboxMode && (
          <div className="free-sidebar visible">
            <div className="avatar"><SabrinaAvatar size={28} /></div>
            <div className="text">
              Modo libre — <b>pregunta lo que quieras</b>.<br/>
              Sabrina ya está conectada a Casita Sol.
            </div>
            <button className="restart" onClick={() => window.location.reload()}>
              <RestartIcon size={13} color="currentColor" />
              <span>Reiniciar</span>
            </button>
          </div>
        )}
      </div>

      {prerollMounted && (
        <Preroll onPlay={startCinema} />
      )}

      {prerollMounted && (
        <style dangerouslySetInnerHTML={{ __html: prerollFading ? `.preroll { animation: preroll-out 700ms forwards; } @keyframes preroll-out { to { opacity: 0; transform: scale(1.04); } }` : '' }} />
      )}
    </StageFit>
  );
}

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