// app.jsx — Hola Taxi Avatar prototype
// 4 scenes: welcome → conversation → seatbelt → ride (dynamic map + cards)
// + global LangSwitcher overlay
// + speech orchestration + caption modes + trivia rotation.

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

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "layout": "companion",
  "lang": "en",
  "theme": "light",
  "font": "serif",
  "autoplay": false,
  "speechOn": true,
  "captionsOn": true,
  "volume": 0.85,
  "brightness": 1,
  "elevenLabsKey": "",
  "didApiKey": "",
  "didSourceUrl": ""
}/*EDITMODE-END*/;

const SCENES = [
  { id: 'welcome',      label: 'Bienvenida' },
  { id: 'conversation', label: 'Conversación' },
  { id: 'seatbelt',     label: 'Cinturón' },
  { id: 'ride',         label: 'En ruta · Leo' },
  { id: 'trip_end',     label: 'Fin de trayecto' },
];

function getSpokenLine(sceneId, lang) {
  switch (sceneId) {
    case 'welcome':      return { lang: 'es', text: t('welcomeSpeech', 'es') };
    case 'conversation': return { lang,       text: t('whereTo', lang) };
    case 'seatbelt':     return { lang,       text: t('seatbelt', lang) };
    case 'ride':         return null; // RideExperience manages its own audio
    default:             return null;
  }
}

const _params = new URLSearchParams(window.location.search);
const KIOSK   = _params.has('kiosk');

// Apply kiosk class to body immediately (before React renders)
if (KIOSK) document.body.classList.add('kiosk-mode');
window.KIOSK = KIOSK;

// ElevenLabs key priority: URL param → existing window (from config.js) → localStorage
const _eleven = _params.get('eleven');
if (_eleven) {
  try { localStorage.setItem('hola_eleven_key', _eleven); } catch(e) {}
  window.__ELEVEN_KEY = _eleven;
} else if (!window.__ELEVEN_KEY) {
  // Only fall back to localStorage if config.js didn't set it
  try { window.__ELEVEN_KEY = localStorage.getItem('hola_eleven_key') || ''; } catch(e) {}
}

function App() {
  const [tweaks, setTweak] = useTweaks({
    ...TWEAK_DEFAULTS,
    elevenLabsKey: window.__ELEVEN_KEY || '',
  });
  const [sceneIdx, setSceneIdx] = useState(0);
  const [swap, setSwap] = useState(false);
  const [micActive, setMicActive] = useState(false);
  const [isSpeaking, setIsSpeaking] = useState(false);
  const [hasInteracted, setHasInteracted] = useState(false);

  const lang = tweaks.lang;
  const layout = tweaks.layout;
  const theme = tweaks.theme;
  const font = tweaks.font;
  const autoplay = tweaks.autoplay;
  const voiceOn = tweaks.speechOn;
  const captionsOn = tweaks.captionsOn;
  const volume = typeof tweaks.volume === 'number' ? tweaks.volume : 0.85;
  const brightness = typeof tweaks.brightness === 'number' ? tweaks.brightness : 1;
  const captionMode = voiceOn && captionsOn ? 'both' : (voiceOn ? 'voice' : 'subtitles');
  const didApiKey   = tweaks.didApiKey   || '';
  const didSourceUrl = tweaks.didSourceUrl || '';

  // Sync API keys to window globals
  useEffect(() => {
    window.__DID_API_KEY    = didApiKey;
    window.__DID_SOURCE_URL = didSourceUrl || DID_DEFAULT_IMAGE;
  }, [didApiKey, didSourceUrl]);

  useEffect(() => {
    const k = tweaks.elevenLabsKey || '';
    if (k) window.__ELEVEN_KEY = k; // never overwrite with empty
  }, [tweaks.elevenLabsKey]);

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.setAttribute('data-font', font);
  }, [theme, font]);

  useEffect(() => {
    const onAny = () => setHasInteracted(true);
    window.addEventListener('pointerdown', onAny, { once: true });
    window.addEventListener('keydown', onAny, { once: true });
    return () => {
      window.removeEventListener('pointerdown', onAny);
      window.removeEventListener('keydown', onAny);
    };
  }, []);


  const speakLine = useCallback(async (line, lng) => {
    if (!line) return;

    // D-ID streaming (paid plan)
    if (window.__didStatus === 'connected' && window.__didSpeak) {
      stopSpeaking();
      setIsSpeaking(true);
      const ok = await window.__didSpeak(line, lng);
      if (ok) {
        setTimeout(() => setIsSpeaking(false), Math.max(3000, line.length * 65));
      } else {
        setIsSpeaking(false);
      }
      return;
    }

    stopSpeaking();
    const result = speak(line, lng, { volume });
    if (!result) return;

    setIsSpeaking(true);

    // ElevenLabs returns a Promise; browser TTS returns a SpeechSynthesisUtterance
    if (result && typeof result.then === 'function') {
      result.finally(() => setIsSpeaking(false));
    } else {
      result.onend   = () => setIsSpeaking(false);
      result.onerror = () => setIsSpeaking(false);
    }
  }, [volume]);

  // Auto-speak on scene/trivia change
  useEffect(() => {
    if (!voiceOn) {
      stopSpeaking();
      setIsSpeaking(false);
      return;
    }
    if (!hasInteracted) return;
    const line = getSpokenLine(SCENES[sceneIdx].id, lang);
    if (line) speakLine(line.text, line.lang);
    return () => { stopSpeaking(); };
  }, [sceneIdx, lang, voiceOn, hasInteracted, speakLine]);

  // Auto-advance in kiosk
  // Auto-advance seatbelt → ride IF driver already pressed "Iniciar Viaje"
  const seatbeltRef = useRef(null);
  useEffect(() => {
    if (!KIOSK || sceneIdx !== 2) return;
    if (seatbeltRef.current) clearTimeout(seatbeltRef.current);
    if (window.__rideReady) {
      // Driver pressed first — advance after Leo finishes speaking (~10s)
      seatbeltRef.current = setTimeout(() => goto(3), 10000);
    }
    return () => clearTimeout(seatbeltRef.current);
  }, [sceneIdx]);

  // Listen for driver "Finalizar viaje" → show end screen with fare
  useEffect(() => {
    const handler = (e) => {
      if (KIOSK) {
        window.__rideFare = e.detail?.fare || null;
        goto(4);
      }
    };
    window.addEventListener('ride_end', handler);
    return () => window.removeEventListener('ride_end', handler);
  }, []);

  // Listen for driver "Iniciar viaje" → advance to ride scene + store destination
  useEffect(() => {
    const handler = (e) => {
      if (!KIOSK) return;
      if (e.detail?.destination) window.__rideDestination = e.detail.destination;
      window.__rideSession = Date.now().toString(36);

      if (sceneIdx < 2) {
        // Passenger hasn't confirmed destination yet — go to seatbelt first
        window.__rideReady = true; // driver already pressed, auto-advance after seatbelt
        goto(2);
      } else if (sceneIdx === 2) {
        // Already on seatbelt — advance to ride now
        goto(3);
      }
      // sceneIdx === 3 → already in ride, ignore
    };
    window.addEventListener('ride_start', handler);
    return () => window.removeEventListener('ride_start', handler);
  }, []);


  // Design-mode autoplay (non-kiosk)
  const timeoutRef = useRef(null);
  useEffect(() => {
    if (!autoplay) { if (timeoutRef.current) clearTimeout(timeoutRef.current); return; }
    const dwell = [4500, 9000, 6500, 6000];
    timeoutRef.current = setTimeout(() => {
      setSceneIdx(i => (i + 1) % SCENES.length);
    }, dwell[sceneIdx] || 5000);
    return () => timeoutRef.current && clearTimeout(timeoutRef.current);
  }, [autoplay, sceneIdx]);


  const goto = (idx) => { setSceneIdx(idx); setSwap(false); };

  // Expose navigation actions for scenes
  useEffect(() => {
    window.__onDestinationConfirmed = () => {
      window.__rideReady = false; // driver hasn't pressed yet
      goto(2); // → seatbelt, wait for driver
    };
    window.__onRideStart = () => goto(3); // seatbelt → ride (unused directly)
    return () => { delete window.__onDestinationConfirmed; delete window.__onRideStart; };
  }, []);
  const next = () => goto((sceneIdx + 1) % SCENES.length);
  const prev = () => goto((sceneIdx - 1 + SCENES.length) % SCENES.length);

  const playCurrent = () => {
    const line = getSpokenLine(SCENES[sceneIdx].id, lang);
    if (line) speakLine(line.text, line.lang);
  };

  const handleLangChange = (newLang) => {
    setTweak('lang', newLang);
    if (voiceOn) setTimeout(() => speakLine(t('greeting', newLang), newLang), 80);
    if (window.notifyLangSelected) window.notifyLangSelected(newLang);
  };

  const avatarMode = micActive ? 'listening' : (!voiceOn ? 'idle' : (isSpeaking ? 'speaking' : 'idle'));

  const sceneId = SCENES[sceneIdx].id;
  const driverStatus = useMemo(() => {
    if (sceneId === 'welcome')      return { kind: 'waiting',     langCode: null, bubble: null };
    if (sceneId === 'conversation') return { kind: 'translating', langCode: lang, bubble: { role: 'CONVERSACIÓN ACTIVA', text: 'Hola está traduciendo en ambos sentidos. Habla normal, lee la traducción aquí.' } };
    if (sceneId === 'seatbelt')     return { kind: 'rolling',     langCode: lang, bubble: { role: 'HOLA → PASAJERO', text: 'Leo da la bienvenida en ' + langName(lang).toLowerCase() + '.' } };
    if (sceneId === 'ride')         return { kind: 'rolling', langCode: lang, bubble: { role: 'HOLA · EN RUTA', text: 'Leo gestiona el trayecto dinámicamente.' } };
    return { kind: 'rolling', langCode: lang, bubble: null };
  }, [sceneId, lang]);

  // ── Kiosk mode: full-screen passenger view only ────────────────────────────
  if (KIOSK) {
    return (
      <div style={{
        position: 'fixed', inset: 0, background: 'var(--bg)',
        overflow: 'hidden', touchAction: 'none',
      }}>
        <div className="tablet-screen" style={{
          width: '100%', height: '100%',
          filter: `brightness(${brightness})`,
        }}>
          <WelcomeScene
            active={sceneIdx === 0} lang={lang} voiceOn={voiceOn}
            avatarMode={avatarMode} isSpeaking={isSpeaking} onPlay={playCurrent}
            onPickLang={(c) => {
              setTweak('lang', c);
              if (voiceOn) setTimeout(() => speakLine(t('greeting', c), c), 50);
              if (window.notifyLangSelected) window.notifyLangSelected(c);
              next();
            }}
          />
          <ConversationScene
            active={sceneIdx === 1} lang={lang} voiceOn={voiceOn} captionMode={captionMode}
            avatarMode={avatarMode} isSpeaking={isSpeaking}
            micActive={micActive} onMic={setMicActive} onPlay={playCurrent}
          />
          <SeatbeltScene
            active={sceneIdx === 2} lang={lang} voiceOn={voiceOn} captionMode={captionMode}
            avatarMode={avatarMode} isSpeaking={isSpeaking} onPlay={playCurrent}
          />
          <RideExperience
            active={sceneIdx === 3}
            lang={lang} voiceOn={voiceOn}
            isSpeaking={isSpeaking} onPlay={playCurrent}
          />
          <TripEndScene
            active={sceneIdx === 4}
            lang={lang} voiceOn={voiceOn}
            onReset={() => { goto(0); setTweak('lang', 'en'); }}
          />
          {sceneIdx !== 0 && sceneIdx !== 3 && sceneIdx !== 4 && <LangSwitcher lang={lang} onChange={handleLangChange} />}
          {sceneIdx !== 3 && sceneIdx !== 4 && <ControlsPanel
            voiceOn={voiceOn} volume={volume} brightness={brightness}
            onToggleVoice={() => setTweak('speechOn', !voiceOn)}
            onVolume={(v) => setTweak('volume', v)}
            onBrightness={(b) => setTweak('brightness', b)}
          />}
          {sceneIdx !== 0 && sceneIdx !== 3 && <FloatingMic lang={lang} voiceOn={voiceOn} />}
          {sceneIdx !== 0 && sceneIdx !== 3 && <ReplayBtn onPlay={playCurrent} isSpeaking={isSpeaking} />}
        </div>
        <TweaksPanel title="Ajustes Leo">
          <TweakSection label="Idioma del pasajero" />
          <TweakSelect label="Idioma" value={lang}
            options={LANGUAGES.map(l => ({ value: l.code, label: `${l.name} · ${l.native}` }))}
            onChange={handleLangChange}
          />
          <TweakSection label="🎙️ Voz (ElevenLabs)" />
          <TweakText label="API Key" value={tweaks.elevenLabsKey || ''}
            placeholder="ElevenLabs API Key"
            onChange={(v) => setTweak('elevenLabsKey', v.trim())}
          />
          <TweakSection label="Escena" />
          <TweakRadio label="Ir a escena" value={String(sceneIdx)}
            options={SCENES.map((s, i) => ({ value: String(i), label: s.label }))}
            onChange={(v) => goto(Number(v))}
          />
          <TweakToggle label="Auto-play" value={autoplay}
            onChange={(v) => setTweak('autoplay', v)} />
        </TweaksPanel>
      </div>
    );
  }

  // ── Design / demo view (desktop) ───────────────────────────────────────────
  return (
    <>
      <header className="page-head">
        <div className="brand">
          <div className="brand-mark">h</div>
          <div>
            <div className="brand-name">Hola <span style={{ color: 'var(--fg-mute)', fontStyle: 'italic' }}>· Taxi Avatar</span></div>
            <div className="brand-sub">MVP · Málaga pilot · Tablet 10–13"</div>
          </div>
        </div>
        <div className="head-meta">
          <span className="dot"></span>
          <span>session #M-2406 · in progress</span>
          <span style={{ opacity: .4 }}>·</span>
          <span>landscape 16:10</span>
          <button
            onClick={() => window.postMessage({ type: '__activate_edit_mode' }, '*')}
            style={{
              appearance: 'none', border: '1px solid var(--line)', borderRadius: 8,
              background: 'var(--surface)', color: 'var(--fg)', cursor: 'pointer',
              padding: '5px 12px', fontFamily: 'var(--font-mono)', fontSize: 11,
              letterSpacing: '0.06em', display: 'flex', alignItems: 'center', gap: 6,
              boxShadow: '0 2px 6px -3px rgba(0,0,0,.12)', transition: 'all .15s',
            }}
            title="Abrir panel de ajustes"
          >
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
            </svg>
            Tweaks
          </button>
        </div>
      </header>

      <main className="stage">
        <div className="tablet-wrap">
          <div className="tablet">
            <div className="tablet-screen" style={{ filter: `brightness(${brightness})` }}>
              <WelcomeScene
                active={sceneIdx === 0} lang={lang} voiceOn={voiceOn}
                avatarMode={avatarMode} isSpeaking={isSpeaking}
                onPlay={playCurrent}
                onPickLang={(c) => {
                  setTweak('lang', c);
                  if (voiceOn) setTimeout(() => speakLine(t('greeting', c), c), 50);
                  next();
                }}
              />
              <ConversationScene
                active={sceneIdx === 1} lang={lang} voiceOn={voiceOn} captionMode={captionMode}
                avatarMode={avatarMode} isSpeaking={isSpeaking}
                micActive={micActive} onMic={setMicActive}
                onPlay={playCurrent}
              />
              <SeatbeltScene
                active={sceneIdx === 2} lang={lang} voiceOn={voiceOn} captionMode={captionMode}
                avatarMode={avatarMode} isSpeaking={isSpeaking}
                onPlay={playCurrent}
              />
              <RideExperience
                active={sceneIdx === 3}
                lang={lang} voiceOn={voiceOn}
                isSpeaking={isSpeaking} onPlay={playCurrent}
              />

              {sceneIdx !== 0 && sceneIdx !== 3 && <LangSwitcher lang={lang} onChange={handleLangChange} />}
              {sceneIdx !== 0 && sceneIdx !== 3 && <FloatingMic lang={lang} voiceOn={voiceOn} />}
              {sceneIdx !== 0 && sceneIdx !== 3 && <ReplayBtn onPlay={playCurrent} isSpeaking={isSpeaking} />}

              {sceneIdx !== 3 && <ControlsPanel
                voiceOn={voiceOn} volume={volume} brightness={brightness}
                onToggleVoice={() => setTweak('speechOn', !voiceOn)}
                onVolume={(v) => setTweak('volume', v)}
                onBrightness={(b) => setTweak('brightness', b)}
              />}
            </div>
          </div>

          <div className="scene-nav">
            <button className="nav-btn" onClick={prev} title="Anterior">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="15 18 9 12 15 6"/></svg>
            </button>
            <div className="scene-chips">
              {SCENES.map((s, i) => (
                <button key={s.id} className="scene-chip" aria-current={sceneIdx === i} onClick={() => goto(i)}>
                  <span className="idx">{String(i + 1).padStart(2, '0')}</span>
                  {s.label}
                </button>
              ))}
            </div>
            <button className="nav-btn" onClick={next} title="Siguiente">
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="9 18 15 12 9 6"/></svg>
            </button>
            <button className="nav-btn primary" onClick={() => setTweak('autoplay', !autoplay)} title="Auto-play">
              {autoplay
                ? <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14"/><rect x="14" y="5" width="4" height="14"/></svg>
                : <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><polygon points="6 4 20 12 6 20 6 4"/></svg>}
            </button>
          </div>

          <section className="notes">
            <h3>Sistema y notas de diseño</h3>
            <p>
              <strong>Leo</strong> es el asesor a bordo: en producción, su retrato será un avatar de vídeo realista
              (D-ID / HeyGen / Synthesia / Tavus) — aquí lo representamos con una fotografía.
              Puedes arrastrar otra foto sobre el retrato y persistirá entre recargas.
            </p>
            <p>
              <strong>Voz:</strong> el prototipo usa la síntesis de voz del navegador, que suena algo robótica.
              En producción la voz de Leo será una <em>voz neuronal clonada</em> (ElevenLabs, Azure Neural, Google WaveNet o similar), consistente y natural en cada idioma. Pulsa <em>Reproducir saludo</em>.
            </p>
            <p>
              <strong>Modo silencio:</strong> en el panel Tweaks puedes apagar la voz y dejar solo los subtítulos, o al revés.
              <strong>Cambia de idioma</strong> en cualquier escena tocando la bandera arriba a la derecha — sin volver al inicio.
              <strong>Habla con el conductor</strong> en la escena de Conversación: mantén el micro pulsado y Ryan traduce
              en ambos sentidos (cualquier pregunta, no solo el destino).
            </p>
            <div className="notes-grid">
              <div>
                <h4>3 layouts del viaje</h4>
                <p><strong>Companion</strong> · avatar protagonista, mapa flotante. <strong>Split</strong> · 50/50. <strong>Stage</strong> · avatar teatral con dock.</p>
              </div>
              <div>
                <h4>Curiosidades durante el viaje</h4>
                <p>Ryan va rotando datos turísticos por el recorrido — Calle Larios, La Manquita, Mercado de Atarazanas, La Malagueta… personalizables por trayecto.</p>
              </div>
              <div>
                <h4>Próximos pasos</h4>
                <p>App del conductor (Android), backend de sesiones + QR, integración Maps mirroring, motor de avatar 3D, panel de merchants.</p>
              </div>
            </div>
          </section>
        </div>

        <aside className="driver-panel">
          <div className="driver-head">
            <div className="driver-head-title">Vista del conductor</div>
            <div className="driver-head-sub">Móvil · conectado por bluetooth</div>
          </div>
          <div className="driver-body">
            <div className="driver-status">
              {driverStatus.langCode
                ? <Flag code={driverStatus.langCode} className="lang-flag" />
                : <div className="lang-flag empty"></div>}
              <div className="driver-status-text">
                <div className="driver-status-label">Idioma del cliente</div>
                <div className="driver-status-value">
                  {driverStatus.langCode ? langName(driverStatus.langCode) : 'Esperando selección…'}
                </div>
              </div>
            </div>

            {driverStatus.bubble && (
              <div className={`driver-bubble ${driverStatus.kind === 'translating' ? 'incoming' : ''} fade-in`}>
                <div className="bubble-meta">
                  {driverStatus.kind === 'translating' && <span className="pulse"></span>}
                  {driverStatus.bubble.role}
                </div>
                {driverStatus.bubble.text}
              </div>
            )}

            <div className="driver-tip">
              El conductor solo habla español. Hola escucha al pasajero, traduce en ambos sentidos
              y lo lee al conductor por el altavoz del móvil. Frases básicas funcionan offline.
            </div>
          </div>
        </aside>
      </main>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Idioma del pasajero" />
        <TweakSelect
          label="Idioma activo"
          value={lang}
          options={LANGUAGES.map(l => ({ value: l.code, label: `${l.name}  ·  ${l.native}` }))}
          onChange={handleLangChange}
        />

        <TweakSection label="Voz y subtítulos" />
        <TweakToggle
          label="Voz del avatar"
          value={voiceOn}
          onChange={(v) => { setTweak('speechOn', v); if (!v) stopSpeaking(); }}
        />
        <TweakToggle
          label="Subtítulos"
          value={captionsOn}
          onChange={(v) => setTweak('captionsOn', v)}
        />

        <TweakSection label="Layout principal del viaje" />
        <TweakRadio
          label="Variación"
          value={layout}
          options={[
            { value: 'companion', label: 'Companion' },
            { value: 'split',     label: 'Split' },
            { value: 'stage',     label: 'Stage' },
          ]}
          onChange={(v) => setTweak('layout', v)}
        />

        <TweakSection label="Tema y tipografía" />
        <TweakRadio
          label="Tema"
          value={theme}
          options={[
            { value: 'light', label: 'Día' },
            { value: 'dark',  label: 'Noche' },
          ]}
          onChange={(v) => setTweak('theme', v)}
        />
        <TweakRadio
          label="Tipografía"
          value={font}
          options={[
            { value: 'serif',   label: 'Serif' },
            { value: 'sans',    label: 'Sans' },
            { value: 'grotesk', label: 'Grotesk' },
          ]}
          onChange={(v) => setTweak('font', v)}
        />

        <TweakSection label="Reproducción" />
        <TweakToggle
          label="Auto-play del trayecto"
          value={autoplay}
          onChange={(v) => setTweak('autoplay', v)}
        />

        <TweakSection label="🎙️ Voz natural (ElevenLabs)" />
        <TweakText
          label="API Key"
          value={tweaks.elevenLabsKey || ''}
          placeholder="Pega tu ElevenLabs API Key"
          onChange={(v) => setTweak('elevenLabsKey', v.trim())}
        />
        {tweaks.elevenLabsKey ? (
          <div style={{ fontSize: 11, color: 'var(--olive-500)', fontFamily: 'var(--font-mono)', padding: '2px 0' }}>
            ✓ ElevenLabs activo · voz Leo (Adam)
          </div>
        ) : (
          <div style={{ fontSize: 10.5, color: 'rgba(41,38,27,.5)', lineHeight: 1.45, padding: '2px 0' }}>
            Gratis en elevenlabs.io · 10.000 chars/mes · sin tarjeta
          </div>
        )}

        <TweakSection label="🎭 Avatar en vivo (D-ID)" />
        <TweakText
          label="API Key"
          value={didApiKey}
          placeholder="Pega tu D-ID API Key aquí"
          onChange={(v) => setTweak('didApiKey', v.trim())}
        />
        <TweakText
          label="Imagen (URL pública)"
          value={didSourceUrl}
          placeholder="https://… (dejar vacío = demo)"
          onChange={(v) => setTweak('didSourceUrl', v.trim())}
        />
        {didApiKey ? (
          <div style={{ fontSize: 11, color: 'var(--olive-500)', fontFamily: 'var(--font-mono)', padding: '4px 0' }}>
            ✓ D-ID activo · {window.__didStatus || 'inicializando'}
          </div>
        ) : (
          <div style={{ fontSize: 10.5, color: 'rgba(41,38,27,.5)', lineHeight: 1.45, padding: '2px 0' }}>
            Obtén tu API Key gratis en d-id.com — trial incluye ~20 min de avatar en vivo.
          </div>
        )}
      </TweaksPanel>
    </>
  );
}

function langName(code) {
  return LANGUAGES.find(l => l.code === code)?.name || code;
}

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