/* ─────────────────────────────────────────────────────────────────────────
   Menu Factory — Wizard (premium rebuild)
   Non-linear 5-step onboarding with sidebar nav + live preview pane.

   Adaptations from premium-updates/view-wizard.jsx:
   1. WIZARD_BRANDS local constant — window.MF_BRANDS stays the landing array
   2. MF_normalizeColor wired in acceptRefinement
   3. useEffect cleanup references WIZARD_BRANDS
   4. MFWizardView accepts onPublish(draft) prop
   5. saveToFirestore ported from old wizard
   6. window.FactoryWizard adapter keeps factory.jsx untouched
   7. window.factorySlug + window.MFWizardView exported
   ───────────────────────────────────────────────────────────────────────── */

const { useState: useW, useEffect: useEW, useRef: useRW } = React;

/* ── Gemini helpers (routed through Cloud Functions — API key never leaves the server) ── */
async function _geminiVision(base64, mimeType, prompt) {
  const result = await window._geminiVisionGenerate({ base64, mimeType, prompt });
  return result;
}

async function _geminiText(prompt) {
  const result = await window._geminiGenerate({ prompt, responseMimeType: 'application/json' });
  try {
    return JSON.parse(result);
  } catch {
    throw new Error('La IA devolvió una respuesta inválida. Intenta de nuevo.');
  }
}

function fileToBase64(file) {
  return new Promise((res, rej) => {
    const r = new FileReader();
    r.onload = () => res(r.result.split(',')[1]);
    r.onerror = rej;
    r.readAsDataURL(file);
  });
}

function wMapErr(code) {
  const m = {
    'auth/email-already-in-use':  'Ya existe una cuenta con ese correo. Ingresa tu contraseña para acceder.',
    'auth/wrong-password':        'Contraseña incorrecta.',
    'auth/invalid-credential':          'Correo o contraseña incorrectos.',
    'auth/invalid-login-credentials':   'Correo o contraseña incorrectos.',
    'auth/weak-password':               'La contraseña debe tener al menos 6 caracteres.',
    'auth/invalid-email':         'El correo no es válido.',
    'auth/too-many-requests':     'Demasiados intentos. Espera un momento.',
    'auth/popup-closed-by-user':  '',
    'auth/network-request-failed':'Error de conexión. Verifica tu internet.',
    'auth/operation-not-allowed': 'Este método de acceso no está habilitado.',
  };
  return m[code] ?? null;
}

function WizardGoogleIcon() {
  return (
    <svg width="18" height="18" viewBox="0 0 48 48" style={{ flexShrink: 0 }}>
      <path fill="#FFC107" d="M43.6 20.1H42V20H24v8h11.3c-1.6 4.7-6.1 8-11.3 8-6.6 0-12-5.4-12-12s5.4-12 12-12c3.1 0 5.8 1.2 7.9 3l5.7-5.7C34.2 6.1 29.4 4 24 4 12.9 4 4 12.9 4 24s8.9 20 20 20 20-8.9 20-20c0-1.3-.1-2.6-.4-3.9z"/>
      <path fill="#FF3D00" d="M6.3 14.7l6.6 4.8C14.6 16 18.9 13 24 13c3.1 0 5.8 1.2 7.9 3l5.7-5.7C34.2 6.1 29.4 4 24 4 16.3 4 9.7 8.3 6.3 14.7z"/>
      <path fill="#4CAF50" d="M24 44c5.2 0 9.9-2 13.5-5.2l-6.2-5.2C29.2 35.1 26.7 36 24 36c-5.2 0-9.6-3.3-11.3-8l-6.5 5C9.6 39.6 16.2 44 24 44z"/>
      <path fill="#1976D2" d="M43.6 20.1H42V20H24v8h11.3c-.8 2.2-2.1 4.1-3.9 5.6l6.2 5.2C41.2 35.9 44 30.5 44 24c0-1.3-.1-2.6-.4-3.9z"/>
    </svg>
  );
}

const MENU_EXTRACT_PROMPT = `Eres un experto en gastronomía. Analiza esta imagen o PDF de menú y extrae toda la información disponible.
Devuelve EXCLUSIVAMENTE un JSON con este formato (sin markdown, sin texto adicional):
{
  "categories": [{"id": "cat1", "name": "Entradas"}, {"id": "cat2", "name": "Platos fuertes"}],
  "items": [
    {"id": "item1", "cat": "cat1", "name": "Nombre del plato", "desc": "Descripción breve y apetitosa", "price": 150, "tags": []}
  ]
}
Reglas: IDs únicos (cat1, cat2... item1, item2...). Precios como número sin símbolo. Tags válidos: "v" (vegetariano), "gf" (sin gluten), "firma" (estrella de la casa), "spicy". Si falta descripción, crea una breve y apetitosa. Si el menú está en inglés, usa los nombres originales.`;

const MENU_GEN_PROMPT = (desc) => `Eres un experto en gastronomía. Crea un menú completo y auténtico para este restaurante:

"${desc}"

Devuelve EXCLUSIVAMENTE un JSON (sin markdown):
{
  "categories": [{"id": "cat1", "name": "..."}, ...],
  "items": [{"id": "item1", "cat": "cat1", "name": "...", "desc": "Descripción de 1-2 líneas muy apetitosa", "price": 180, "tags": []}, ...]
}
Crea 3-4 categorías y 4-6 platos por categoría. Precios realistas en MXN. Descripciones que hagan salivar.`;

function useMobileW() {
  const [m, setM] = useW(() => window.innerWidth < 768);
  useEW(() => {
    const fn = () => setM(window.innerWidth < 768);
    window.addEventListener('resize', fn);
    return () => window.removeEventListener('resize', fn);
  }, []);
  return m;
}

/* ── Local brand data — does NOT overwrite window.MF_BRANDS (the landing array) */
const WIZARD_BRANDS = {
  casa: {
    accent:      '#C8451F',
    accentDeep:  '#8A2E0F',
    accentSoft:  '#F5C4B0',
    accentTint:  '#FDF0EB',
    items: (window.MF_MENU?.items || []).map(function(it) {
      return {
        id:    it.id,
        name:  window.MF_T ? window.MF_T(it.name) : (it.name?.es || it.name || ''),
        desc:  window.MF_T ? window.MF_T(it.desc || {}) : (it.desc?.es || it.desc || ''),
        price: it.price,
        cat:   it.cat,
        img:   null,
      };
    }),
    categories: (window.MF_MENU?.categories || []).map(function(c) {
      return {
        id:   c.id,
        name: window.MF_T ? window.MF_T(c.name) : (c.name?.es || c.name || ''),
      };
    }),
  },
};

const WIZARD_STEPS = [
  { id: 'brand',    n: 'I',   t: 'Tu Logo',              hint: 'ADN cromático · design tokens' },
  { id: 'identity', n: 'II',  t: 'Identidad',            hint: 'Nombre · voz · lugar' },
  { id: 'menu',     n: 'III', t: 'Ingesta Gemini Vision', hint: 'PDF · foto · 60 segundos' },
  { id: 'tables',   n: 'IV',  t: 'Enlaza tu Cocina',     hint: 'QR · POS · webhooks' },
  { id: 'publish',  n: 'V',   t: 'Publicar',             hint: 'Cuenta · dominio' },
];

function MFWizardView({ brandId, goTo, onPublish }) {
  const isMobile = useMobileW();
  const initialBrand = WIZARD_BRANDS[brandId || 'casa'];
  const [step, setStep] = useW('brand');
  const [draft, setDraft] = useW(() => ({
    name:        '',
    tagline:     '',
    location:    '',
    founded:     '',
    accent:      initialBrand.accent,
    accentRaw:   '#DC2C2C',
    accentMode:  'extracted',
    logo:        null,
    menu:        initialBrand.items.slice(0, 8),
    categories:  initialBrand.categories.slice(0, 3),
    tables:      12,
    locations:   1,
    domain:      '',
    email:       '',
  }));

  // Live-apply accent + derived variants to the page during wizard session
  useEW(() => {
    const r = document.documentElement.style;
    r.setProperty('--accent',      draft.accent);
    r.setProperty('--accent-deep', darken(draft.accent, 0.25));
    r.setProperty('--accent-soft', lighten(draft.accent, 0.55));
    r.setProperty('--accent-tint', lighten(draft.accent, 0.72));
    return () => {
      // Restore brand defaults on unmount
      const b = WIZARD_BRANDS[window.__currentBrand || 'casa'];
      r.setProperty('--accent',      b.accent);
      r.setProperty('--accent-deep', b.accentDeep);
      r.setProperty('--accent-soft', b.accentSoft);
      r.setProperty('--accent-tint', b.accentTint);
    };
  }, [draft.accent]);

  const update = (key, val) => setDraft(d => ({ ...d, [key]: val }));

  if (isMobile) {
    return (
      <div style={{ minHeight: '100dvh', display: 'flex', flexDirection: 'column', background: 'var(--paper)' }}>
        <WizardMobileHeader step={step} setStep={setStep} draft={draft} goTo={goTo} />
        <div style={{ flex: 1, padding: '28px 20px 100px', overflowY: 'auto' }} key={step}>
          <div className="fade-in">
            {step === 'brand'    && <StepBrand    draft={draft} update={update} onNext={() => setStep('identity')} />}
            {step === 'identity' && <StepIdentity draft={draft} update={update} onBack={() => setStep('brand')}    onNext={() => setStep('menu')} />}
            {step === 'menu'     && <StepMenu     draft={draft} update={update} onBack={() => setStep('identity')} onNext={() => setStep('tables')} />}
            {step === 'tables'   && <StepTables   draft={draft} update={update} onBack={() => setStep('menu')}     onNext={() => setStep('publish')} />}
            {step === 'publish'  && (
              <StepPublish
                draft={draft} update={update}
                onBack={() => setStep('tables')}
                onPublish={async () => { await onPublish?.(draft); goTo('dashboard'); }}
              />
            )}
          </div>
        </div>
      </div>
    );
  }

  return (
    <div style={{
      height: '100vh',
      overflow: 'hidden',
      display: 'grid', gridTemplateColumns: '320px 1fr 1fr',
    }}>
      <WizardSidebar step={step} setStep={setStep} draft={draft} goTo={goTo} />

      <div style={{
        padding: '48px 56px 64px',
        borderLeft: '1px solid var(--line)',
        borderRight: '1px solid var(--line)',
        background: 'var(--paper)',
        height: '100vh',
        overflowY: 'auto',
      }} key={step}>
        <div className="fade-in">
          {step === 'brand'    && <StepBrand    draft={draft} update={update} onNext={() => setStep('identity')} />}
          {step === 'identity' && <StepIdentity draft={draft} update={update} onBack={() => setStep('brand')}    onNext={() => setStep('menu')} />}
          {step === 'menu'     && <StepMenu     draft={draft} update={update} onBack={() => setStep('identity')} onNext={() => setStep('tables')} />}
          {step === 'tables'   && <StepTables   draft={draft} update={update} onBack={() => setStep('menu')}     onNext={() => setStep('publish')} />}
          {step === 'publish'  && (
            <StepPublish
              draft={draft} update={update}
              onBack={() => setStep('tables')}
              onPublish={async () => { await onPublish?.(draft); goTo('dashboard'); }}
            />
          )}
        </div>
      </div>

      <WizardPreview draft={draft} step={step} />
    </div>
  );
}

/* ── Mobile header — step indicator + progress ─────────────────────────── */
function WizardMobileHeader({ step, setStep, draft, goTo }) {
  const currentIdx = WIZARD_STEPS.findIndex(s => s.id === step);
  const current    = WIZARD_STEPS[currentIdx];
  const completion = Math.round(((currentIdx) / WIZARD_STEPS.length) * 100);

  return (
    <div style={{ borderBottom: '1px solid var(--line)', background: 'var(--paper-2)', padding: '16px 20px 0' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
        <button onClick={() => goTo('landing')} style={{ fontSize: 13, color: 'var(--mu)', display: 'flex', alignItems: 'center', gap: 6 }}>
          <window.MFIcon name="arrowL" size={13} /> Salir
        </button>
        <span style={{ fontSize: 11, color: 'var(--mu)', letterSpacing: '0.12em', textTransform: 'uppercase' }}>
          {currentIdx + 1} / {WIZARD_STEPS.length} · {current.t}
        </span>
        <div style={{ width: 40 }} />
      </div>
      <div style={{ display: 'flex', gap: 4, paddingBottom: 16 }}>
        {WIZARD_STEPS.map((s, i) => (
          <button key={s.id} onClick={() => setStep(s.id)} style={{
            flex: 1, height: 3, borderRadius: 2,
            background: i <= currentIdx ? 'var(--accent)' : 'var(--line-2)',
            transition: 'background 300ms',
          }} />
        ))}
      </div>
    </div>
  );
}

/* ── Sidebar ───────────────────────────────────────────────────────────── */
function WizardSidebar({ step, setStep, draft, goTo }) {
  const completion = (() => {
    let n = 0;
    if (draft.logo || draft.accent !== WIZARD_BRANDS.casa.accent) n++;
    if (draft.name) n++;
    if (draft.menu.length) n++;
    if (draft.tables) n++;
    if (draft.email) n++;
    return Math.round((n / 5) * 100);
  })();

  return (
    <aside style={{
      background: 'var(--paper-2)',
      padding: '48px 36px 36px',
      display: 'flex', flexDirection: 'column',
      height: '100vh',
      overflowY: 'auto',
    }}>
      <button onClick={() => goTo('landing')} style={{
        display: 'inline-flex', alignItems: 'center', gap: 6,
        fontSize: 12, color: 'var(--mu)', alignSelf: 'flex-start',
        marginBottom: 32,
      }}>
        <window.MFIcon name="arrowL" size={13} />
        Salir del onboarding
      </button>

      <div style={{ marginBottom: 36 }}>
        <window.MFMarker label="Onboarding" />
        <h2 className="serif" style={{ fontSize: 28, fontStyle: 'italic', fontWeight: 400, margin: '20px 0 8px', letterSpacing: '-0.01em' }}>
          Construyendo tu casa
        </h2>
        <p style={{ fontSize: 12, color: 'var(--mu)', lineHeight: 1.55, margin: 0 }}>
          Cinco pasos. Puedes saltar entre ellos cuando quieras — todo se guarda en vivo.
        </p>
      </div>

      <nav style={{ display: 'flex', flexDirection: 'column', gap: 2, flex: 1 }}>
        {WIZARD_STEPS.map((s, i) => {
          const active = s.id === step;
          const done   = WIZARD_STEPS.findIndex(x => x.id === step) > i;
          return (
            <button key={s.id} onClick={() => setStep(s.id)} style={{
              display: 'grid',
              gridTemplateColumns: '32px 1fr 14px',
              gap: 14,
              alignItems: 'center',
              padding: '14px 14px',
              borderRadius: 'var(--r-md)',
              background: active ? 'var(--paper)' : 'transparent',
              border: active ? '1px solid var(--line)' : '1px solid transparent',
              textAlign: 'left',
              transition: 'all 180ms',
            }}>
              <span className="serif-italic" style={{
                fontSize: 18, color: done ? 'var(--accent)' : active ? 'var(--ink)' : 'var(--mu-2)',
              }}>{done ? '✓' : s.n}.</span>
              <span>
                <div style={{ fontSize: 14, fontWeight: 500, color: active || done ? 'var(--ink)' : 'var(--mu)' }}>{s.t}</div>
                <div style={{ fontSize: 11, color: 'var(--mu-2)', marginTop: 2, letterSpacing: '0.02em' }}>{s.hint}</div>
              </span>
              {active && <window.MFIcon name="chevR" size={12} style={{ color: 'var(--mu)' }} />}
            </button>
          );
        })}
      </nav>

      <div style={{ marginTop: 36, paddingTop: 28, borderTop: '1px solid var(--line)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
          <span style={{ fontSize: 10, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)' }}>Progreso</span>
          <span className="mono" style={{ fontSize: 11, color: 'var(--mu)' }}>{completion}%</span>
        </div>
        <div style={{ height: 2, background: 'var(--line)', borderRadius: 2, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: `${completion}%`, background: 'var(--accent)', transition: 'width 600ms cubic-bezier(.2,.7,.3,1)' }} />
        </div>
      </div>

      <div style={{ marginTop: 24, padding: 16, background: 'var(--paper)', borderRadius: 'var(--r-md)', border: '1px solid var(--line)' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
          <window.MFIcon name="info" size={14} style={{ color: 'var(--accent)', marginTop: 2 }} />
          <div style={{ fontSize: 12, color: 'var(--mu)', lineHeight: 1.5 }}>
            Todo lo que escribas se guarda en vivo. Puedes volver mañana y continuar.
          </div>
        </div>
      </div>
    </aside>
  );
}

/* ── STEP I: BRAND ─────────────────────────────────────────────────────── */
function StepBrand({ draft, update, onNext }) {
  const [busy, setBusy]                     = useW(false);
  const [showRefinement, setShowRefinement] = useW(false);
  const inputRef                            = useRW(null);

  const handleLogo = (file) => {
    if (!file) return;
    setBusy(true);
    const url = URL.createObjectURL(file);
    update('logo', url);
    const img = new Image();
    img.onload = async () => {
      // Canvas extraction — instant fallback
      let raw = '#C8451F';
      try {
        if (window.FactoryPalette?.extractPalette) {
          const pal = await window.FactoryPalette.extractPalette(img);
          raw = pal.gold || raw;
        }
      } catch (_) {}
      // Gemini Vision — semantic brand color (overrides canvas if valid)
      try {
        const b64 = await fileToBase64(file);
        const result = await _geminiVision(
          b64, file.type || 'image/png',
          'Identify the single dominant brand accent color in this restaurant logo. Ignore white, black, and near-gray tones. Return only valid JSON: {"hex":"#RRGGBB"}'
        );
        if (result?.hex && /^#[0-9a-f]{6}$/i.test(result.hex)) raw = result.hex;
      } catch (_) {}
      update('accentRaw', raw);
      update('accent', raw);
      setBusy(false);
      setTimeout(() => setShowRefinement(true), 600);
    };
    img.onerror = () => { setBusy(false); };
    img.src = url;
  };

  const acceptRefinement = () => {
    // Wire MF_normalizeColor: the refined value is the editorial-safe version of the raw
    const refined = window.MF_normalizeColor
      ? window.MF_normalizeColor(draft.accentRaw || '#DC2C2C')
      : '#B5391A';
    update('accent', refined);
    update('accentMode', 'refined');
    setShowRefinement(false);
  };

  const keepRaw = () => {
    update('accentMode', 'raw');
    setShowRefinement(false);
  };

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
        <span className="serif-italic" style={{ fontSize: 22, color: 'var(--accent)' }}>I.</span>
        <window.MFMarker label="Paso 1 de 5" />
      </div>
      <h1 className="display-md" style={{ marginTop: 12, fontWeight: 400, letterSpacing: '-0.018em' }}>
        Empezamos con
        <br />
        <em style={{ color: 'var(--accent)' }}>tu logo.</em>
      </h1>
      <p style={{ fontSize: 15, color: 'var(--mu)', lineHeight: 1.6, marginTop: 20, maxWidth: 480 }}>
        Extraemos el ADN cromático de tu marca mediante algoritmos de
        cuantificación visual y compilamos tus design tokens exclusivos
        al instante. Cero plantillas. Verifica el resultado en vivo a la derecha.
      </p>

      <div style={{ marginTop: 36 }}>
        {!draft.logo ? (
          <div
            onClick={() => inputRef.current?.click()}
            onDragOver={e => e.preventDefault()}
            onDrop={e => { e.preventDefault(); handleLogo(e.dataTransfer.files[0]); }}
            style={{
              padding: '64px 32px',
              border: '1.5px dashed var(--line-3)',
              borderRadius: 'var(--r-lg)',
              textAlign: 'center',
              background: 'var(--paper-tint)',
              cursor: 'pointer',
              transition: 'all 220ms',
            }}
            onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--ink)'; e.currentTarget.style.background = 'var(--paper)'; }}
            onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--line-3)'; e.currentTarget.style.background = 'var(--paper-tint)'; }}
          >
            <div style={{ width: 56, height: 56, margin: '0 auto', borderRadius: '50%', background: 'var(--paper)', border: '1px solid var(--line-2)', display: 'grid', placeItems: 'center' }}>
              <window.MFIcon name="upload" size={20} style={{ color: 'var(--ink)' }} />
            </div>
            <div className="serif" style={{ fontSize: 22, fontStyle: 'italic', marginTop: 18, fontWeight: 400 }}>
              Suelta tu logo aquí
            </div>
            <p style={{ fontSize: 12, color: 'var(--mu)', marginTop: 8 }}>
              SVG, PNG o JPG · Idealmente con fondo transparente
            </p>
            <input ref={inputRef} type="file" accept="image/*" hidden onChange={e => handleLogo(e.target.files[0])} />
          </div>
        ) : (
          <div className="card" style={{ padding: 32 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 24 }}>
              <div style={{
                width: 100, height: 100, borderRadius: 'var(--r-md)',
                background: 'var(--paper-2)',
                border: '1px solid var(--line)',
                display: 'grid', placeItems: 'center',
                fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 36,
                color: 'var(--accent)',
              }}>
                {draft.name ? draft.name[0] : 'A'}
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)' }}>
                  Logo cargado
                </div>
                <div className="serif" style={{ fontSize: 20, fontStyle: 'italic', marginTop: 6, fontWeight: 400 }}>
                  {busy ? 'Analizando colores…' : 'Paleta extraída'}
                </div>
                {!busy && (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 12 }}>
                    <div style={{ width: 18, height: 18, borderRadius: 4, background: draft.accent, border: '1px solid var(--line-2)' }} />
                    <span className="mono" style={{ fontSize: 12, color: 'var(--mu)' }}>{draft.accent.toUpperCase()}</span>
                    <span style={{ fontSize: 11, color: 'var(--mu-2)', marginLeft: 6 }}>
                      · {draft.accentMode === 'refined' ? 'Refinado por IA' : draft.accentMode === 'raw' ? 'Tal como en tu logo' : 'Extraído'}
                    </span>
                  </div>
                )}
              </div>
              <button onClick={() => inputRef.current?.click()} className="btn ghost sm">
                Cambiar
              </button>
              <input ref={inputRef} type="file" accept="image/*" hidden onChange={e => handleLogo(e.target.files[0])} />
            </div>
          </div>
        )}
      </div>

      {/* AI refinement banner */}
      {showRefinement && (
        <div className="fade-in" style={{
          marginTop: 24,
          background: 'var(--ink)',
          color: 'var(--paper)',
          borderRadius: 'var(--r-lg)',
          padding: 28,
          position: 'relative', overflow: 'hidden',
        }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16 }}>
            <window.MFIcon name="sparkle" size={18} style={{ color: 'var(--accent)', marginTop: 2 }} />
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 600, color: 'rgba(250,249,245,0.5)' }}>
                Sugerencia editorial
              </div>
              <div className="serif" style={{ fontSize: 20, fontStyle: 'italic', marginTop: 6, color: 'var(--paper)' }}>
                Tu rojo es excelente. Lo bajamos un punto para que se sienta más editorial.
              </div>
              <p style={{ fontSize: 13, color: 'rgba(250,249,245,0.7)', marginTop: 12, lineHeight: 1.55 }}>
                Los rojos muy saturados compiten visualmente con el contenido.
                Reducimos la saturación y subimos la profundidad. El cambio es sutil
                pero hace que el menú se sienta de catálogo, no de promoción.
              </p>
              <div style={{ marginTop: 20, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                <RefSwatch label="Tu color" hex={draft.accentRaw} />
                <RefSwatch label="Refinado" hex={window.MF_normalizeColor ? window.MF_normalizeColor(draft.accentRaw) : '#B5391A'} recommended />
              </div>
              <div style={{ marginTop: 20, display: 'flex', gap: 10 }}>
                <button className="btn accent sm" onClick={acceptRefinement}>
                  Usar refinado
                </button>
                <button onClick={keepRaw} style={{ fontSize: 12, color: 'rgba(250,249,245,0.7)', padding: '0 8px' }}>
                  Conservar el original
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Color picker + curated swatches — always visible */}
      {!showRefinement && (
        <div style={{ marginTop: 32 }}>
          <div style={{ fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)', marginBottom: 14 }}>
            Color de acento
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 16 }}>
            <label style={{ display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer' }}>
              <div style={{ position: 'relative', width: 44, height: 44, borderRadius: 'var(--r-md)', background: draft.accent, border: '2px solid var(--line-2)', overflow: 'hidden', flexShrink: 0 }}>
                <input type="color" value={draft.accent}
                  onChange={e => {
                    update('accentRaw', e.target.value);
                    update('accent', e.target.value);
                    update('accentMode', 'manual');
                    setShowRefinement(true);
                  }}
                  style={{ position: 'absolute', inset: 0, opacity: 0, width: '100%', height: '100%', cursor: 'pointer', border: 'none', padding: 0 }}
                />
              </div>
              <div>
                <div style={{ fontSize: 11, color: 'var(--mu)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Color libre</div>
                <div className="mono" style={{ fontSize: 12, color: 'var(--ink)', marginTop: 2 }}>{draft.accent.toUpperCase()}</div>
              </div>
            </label>
            <div style={{ fontSize: 11, color: 'var(--mu-2)' }}>— o curado:</div>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 10 }}>
            {['#C8451F', '#2B4A6B', '#D98E1F', '#5A7A52', '#6B3F8E', '#0F0F0E'].map(c => (
              <button key={c} onClick={() => { update('accentRaw', c); update('accent', c); update('accentMode', 'curated'); }} style={{
                aspectRatio: '1',
                background: c,
                borderRadius: 'var(--r-md)',
                border: draft.accent === c ? '3px solid var(--paper)' : '1px solid var(--line-2)',
                outline: draft.accent === c ? `1px solid ${c}` : 'none',
                cursor: 'pointer',
                transition: 'transform 160ms',
              }}
                onMouseEnter={e => e.currentTarget.style.transform = 'scale(1.06)'}
                onMouseLeave={e => e.currentTarget.style.transform = 'scale(1)'}
              />
            ))}
          </div>
        </div>
      )}

      <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 56 }}>
        <button className="btn accent lg" disabled={!draft.logo} onClick={onNext}>
          Continuar a identidad
          <window.MFIcon name="arrow" size={16} />
        </button>
      </div>
    </div>
  );
}

function RefSwatch({ label, hex, recommended }) {
  return (
    <div style={{ padding: 14, borderRadius: 'var(--r-md)', background: 'rgba(255,255,255,0.04)', border: recommended ? '1px solid var(--accent)' : '1px solid rgba(255,255,255,0.08)', position: 'relative' }}>
      {recommended && (
        <span style={{ position: 'absolute', top: -8, right: 12, fontSize: 9, letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--accent)', background: 'var(--ink)', padding: '2px 8px', borderRadius: 999 }}>Recomendado</span>
      )}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{ width: 32, height: 32, borderRadius: 6, background: hex }} />
        <div>
          <div style={{ fontSize: 11, color: 'rgba(250,249,245,0.6)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>{label}</div>
          <div className="mono" style={{ fontSize: 13, color: 'var(--paper)' }}>{hex}</div>
        </div>
      </div>
    </div>
  );
}

/* ── STEP II: IDENTITY ─────────────────────────────────────────────────── */
function StepIdentity({ draft, update, onBack, onNext }) {
  const [taglineBusy, setTaglineBusy] = useW(false);
  const [taglineSuggestions, setTaglineSuggestions] = useW([]);
  const [taglineError, setTaglineError] = useW('');

  const suggestTaglines = async () => {
    if (!draft.name) return;
    setTaglineBusy(true);
    setTaglineError('');
    try {
      const prompt = `Eres un experto en branding de restaurantes. Para el restaurante llamado "${draft.name}"${draft.location ? ` en ${draft.location}` : ''}, sugiere 3 taglines cortos (máximo 5 palabras cada uno) en español. Que suenen auténticos, poéticos y memorables, no corporativos.

Devuelve EXCLUSIVAMENTE un JSON array: ["tagline 1", "tagline 2", "tagline 3"]`;
      const suggestions = await _geminiText(prompt);
      setTaglineSuggestions(Array.isArray(suggestions) ? suggestions : []);
    } catch (e) {
      setTaglineError('No se pudo conectar con Gemini. Verifica tu conexión.');
    } finally {
      setTaglineBusy(false);
    }
  };

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
        <span className="serif-italic" style={{ fontSize: 22, color: 'var(--accent)' }}>II.</span>
        <window.MFMarker label="Paso 2 de 5" />
      </div>
      <h1 className="display-md" style={{ marginTop: 12, fontWeight: 400, letterSpacing: '-0.018em' }}>
        La voz
        <br />
        <em style={{ color: 'var(--accent)' }}>de tu casa.</em>
      </h1>
      <p style={{ fontSize: 15, color: 'var(--mu)', lineHeight: 1.6, marginTop: 20, maxWidth: 480 }}>
        Define el tono y la historia de tu marca. Estos campos configuran el
        modelo de lenguaje del Asistente Digital nativo y aparecen como
        detalles tipográficos en cada pantalla.
      </p>

      <div style={{ marginTop: 40, display: 'flex', flexDirection: 'column', gap: 28 }}>
        <Field label="Nombre" hint="Como lo escribirías en el menú impreso.">
          <input className="input" value={draft.name} onChange={e => update('name', e.target.value)}
            placeholder="Casa Amaranta" autoFocus
            style={{ fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 22, height: 60 }} />
        </Field>
        <div>
          <Field label="Tagline" hint="Una idea, no una frase. Tres a cinco palabras.">
            <input className="input" value={draft.tagline} onChange={e => update('tagline', e.target.value)}
              placeholder="Cocina de raíz" />
          </Field>
          {taglineSuggestions.length > 0 && (
            <div style={{ marginTop: 10, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {taglineSuggestions.map((s, i) => (
                <button key={i} onClick={() => { update('tagline', s); setTaglineSuggestions([]); }} style={{
                  padding: '6px 14px', borderRadius: 999, fontSize: 13,
                  background: 'var(--accent-tint)', color: 'var(--accent-deep)',
                  border: '1px solid var(--accent-soft)', cursor: 'pointer',
                  fontFamily: 'var(--serif)', fontStyle: 'italic',
                  transition: 'all 150ms',
                }}>
                  {s}
                </button>
              ))}
            </div>
          )}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr', gap: 18 }}>
          <Field label="Ubicación" hint="Barrio + ciudad.">
            <input className="input" value={draft.location} onChange={e => update('location', e.target.value)}
              placeholder="Roma Norte · Ciudad de México" />
          </Field>
          <Field label="Año" hint="Romano si quieres.">
            <input className="input" value={draft.founded} onChange={e => update('founded', e.target.value)}
              placeholder="MMXIX" style={{ fontFamily: 'var(--serif)', fontStyle: 'italic' }} />
          </Field>
        </div>
      </div>

      <div style={{ marginTop: 40, padding: 18, background: 'var(--paper-2)', borderRadius: 'var(--r-md)', display: 'flex', gap: 12, alignItems: 'flex-start' }}>
        <window.MFIcon name="sparkle" size={14} style={{ color: 'var(--accent)', marginTop: 2, flexShrink: 0 }} />
        <div style={{ flex: 1, fontSize: 12, color: 'var(--mu)', lineHeight: 1.55 }}>
          {taglineError || '¿No encuentras la voz? Escribe el nombre y sugerimos tres taglines a juego con tu cocina.'}
        </div>
        <button className="btn ghost sm" style={{ alignSelf: 'flex-start', flexShrink: 0 }}
          onClick={suggestTaglines} disabled={!draft.name || taglineBusy}>
          {taglineBusy ? '✦ Pensando…' : 'Sugerir con IA'}
        </button>
      </div>

      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 56 }}>
        <button className="btn ghost" onClick={onBack}>
          <window.MFIcon name="arrowL" size={14} />
          Atrás
        </button>
        <button className="btn accent lg" onClick={onNext} disabled={!draft.name}>
          Continuar al menú
          <window.MFIcon name="arrow" size={16} />
        </button>
      </div>
    </div>
  );
}

/* ── STEP III: MENU ────────────────────────────────────────────────────── */
function StepMenu({ draft, update, onBack, onNext }) {
  const [mode, setMode]         = useW('pdf');
  const [scanning, setScanning] = useW(false);
  const [progress, setProgress] = useW(0);
  const [scanError, setScanError] = useW(null);
  const [aiDesc, setAiDesc]     = useW('');
  const [aiGenBusy, setAiGenBusy] = useW(false);
  const fileRef                 = useRW(null);

  // #4 — Camera-first onboarding (mobile)
  const videoRef     = useRW(null);
  const canvasRef    = useRW(null);
  const streamRef    = useRW(null);
  const [camMode, setCamMode]   = useW(false);   // is the camera viewfinder open?
  const [camSnap, setCamSnap]   = useW(null);    // base64 preview after capture
  const [camErr,  setCamErr]    = useW('');
  const isMobileCamera = typeof navigator !== 'undefined' && !!navigator.mediaDevices;

  const openCamera = async () => {
    setCamErr('');
    setCamSnap(null);
    setCamMode(true);
    // small delay lets the DOM mount the video element
    setTimeout(async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', width: { ideal: 1920 }, height: { ideal: 1080 } } });
        streamRef.current = stream;
        if (videoRef.current) { videoRef.current.srcObject = stream; videoRef.current.play(); }
      } catch (err) {
        setCamErr('No se pudo acceder a la cámara. Verifica los permisos del navegador.');
        setCamMode(false);
      }
    }, 80);
  };

  const closeCamera = () => {
    if (streamRef.current) { streamRef.current.getTracks().forEach(t => t.stop()); streamRef.current = null; }
    setCamMode(false);
    setCamSnap(null);
  };

  const capturePhoto = () => {
    const video  = videoRef.current;
    const canvas = canvasRef.current;
    if (!video || !canvas) return;
    canvas.width  = video.videoWidth  || 1280;
    canvas.height = video.videoHeight || 720;
    canvas.getContext('2d').drawImage(video, 0, 0);
    const dataUrl = canvas.toDataURL('image/jpeg', 0.92);
    setCamSnap(dataUrl);
    // stop live stream but keep preview
    if (streamRef.current) { streamRef.current.getTracks().forEach(t => t.stop()); streamRef.current = null; }
  };

  const confirmCapture = async () => {
    if (!camSnap) return;
    closeCamera();
    // Convert data-URL → Blob → File → existing handleFile pipeline
    const base64 = camSnap.split(',')[1];
    const binary  = atob(base64);
    const arr     = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) arr[i] = binary.charCodeAt(i);
    const file = new File([arr], 'foto-menu.jpg', { type: 'image/jpeg' });
    await handleFile(file);
  };

  // Safety: stop the camera stream if the step unmounts mid-capture
  useEW(() => () => {
    if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
  }, []);

  const applyExtracted = ({ categories, items }) => {
    const cats  = (categories || []).map((c, i) => ({ id: c.id || `cat${i+1}`, name: c.name }));
    const menu  = (items || []).map((it, i) => ({
      id:    it.id   || `item${i+1}`,
      cat:   it.cat,
      name:  it.name || 'Plato sin nombre',
      desc:  it.desc || '',
      price: Number(it.price) || 0,
      tags:  it.tags || [],
    }));
    update('categories', cats);
    update('menu', menu);
  };

  const handleFile = async (file) => {
    if (!file) return;
    setScanError(null);
    setScanning(true);
    setProgress(12);
    try {
      const mimeType = file.type || (file.name?.endsWith('.pdf') ? 'application/pdf' : 'image/jpeg');
      const base64   = await fileToBase64(file);
      setProgress(35);
      const result = await _geminiVision(base64, mimeType, MENU_EXTRACT_PROMPT);
      setProgress(90);
      applyExtracted(result);
      setProgress(100);
      setTimeout(() => { setScanning(false); setMode('imported'); }, 400);
    } catch (err) {
      setScanError(err.message || 'Error al analizar el archivo. Intenta con una imagen más clara o un PDF de texto.');
      setScanning(false);
      setProgress(0);
    }
  };

  const generateFromText = async () => {
    if (!aiDesc.trim()) return;
    setScanError(null);
    setAiGenBusy(true);
    try {
      const result = await _geminiText(MENU_GEN_PROMPT(aiDesc));
      applyExtracted(result);
      setMode('imported');
    } catch (err) {
      setScanError(err.message || 'Error al generar el menú. Intenta de nuevo.');
    } finally {
      setAiGenBusy(false);
    }
  };

  const useSample = () => {
    update('categories', draft.categories.length ? draft.categories : [{ id: 'ent', name: 'Entradas' }, { id: 'pf', name: 'Platos fuertes' }, { id: 'post', name: 'Postres' }]);
    if (!draft.menu.length) update('menu', WIZARD_BRANDS.casa.items);
    setMode('imported');
  };

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
        <span className="serif-italic" style={{ fontSize: 22, color: 'var(--accent)' }}>III.</span>
        <window.MFMarker label="Paso 3 de 5" />
      </div>
      <h1 className="display-md" style={{ marginTop: 12, fontWeight: 400, letterSpacing: '-0.018em' }}>
        Ingesta
        <br />
        <em style={{ color: 'var(--accent)' }}>Gemini Vision.</em>
      </h1>
      <p style={{ fontSize: 15, color: 'var(--mu)', lineHeight: 1.6, marginTop: 20, maxWidth: 480 }}>
        Sube una foto o PDF de tu menú actual. Nuestro pipeline de IA procesa,
        estructura y clasifica categorías, descripciones y precios directamente
        en tu base de datos en 60 segundos.
      </p>

      <div style={{
        display: 'flex', alignItems: 'flex-start', gap: 10,
        marginTop: 18, padding: '11px 16px',
        background: 'var(--paper-tint)',
        border: '1px solid var(--line-2)',
        borderRadius: 'var(--r-md)',
        maxWidth: 480,
      }}>
        <window.MFIcon name="image" size={14} style={{ color: 'var(--accent)', flexShrink: 0, marginTop: 1 }} />
        <span style={{ fontSize: 12.5, color: 'var(--ink-2)', lineHeight: 1.55 }}>
          Las imágenes del menú o los platillos se agregan desde la consola de tu perfil.
        </span>
      </div>

      <div className="seg" style={{ marginTop: 32 }}>
        <button className={mode === 'pdf'      ? 'active' : ''} onClick={() => { setMode('pdf'); setScanError(null); }}>Subir PDF / foto</button>
        <button className={mode === 'ai'       ? 'active' : ''} onClick={() => { setMode('ai'); setScanError(null); }}>Crear con IA</button>
        <button className={mode === 'imported' ? 'active' : ''} onClick={() => setMode('imported')}>
          {draft.menu?.length ? `Ver mis ${draft.menu.length} platos` : 'Empezar en blanco'}
        </button>
      </div>

      <div style={{ marginTop: 28 }}>
        {mode === 'pdf' && !scanning && (
          <>
            {/* #4 — Camera-first CTA (shown when camera API is available, i.e. mobile/https) */}
            {isMobileCamera && (
              <button
                onClick={openCamera}
                style={{
                  width: '100%', height: 64, borderRadius: 'var(--r-lg)',
                  background: 'var(--ink)', color: '#FAF9F5',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12,
                  fontSize: 16, fontWeight: 700, letterSpacing: '-0.01em',
                  marginBottom: 16,
                  boxShadow: '0 8px 32px -8px rgba(0,0,0,0.35)',
                  transition: 'transform 100ms, box-shadow 100ms',
                }}
                onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.boxShadow = '0 12px 36px -8px rgba(0,0,0,0.45)'; }}
                onMouseLeave={e => { e.currentTarget.style.transform = 'none'; e.currentTarget.style.boxShadow = '0 8px 32px -8px rgba(0,0,0,0.35)'; }}
              >
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
                  <circle cx="12" cy="13" r="4"/>
                </svg>
                Fotografiar mi menú físico
              </button>
            )}
            {camErr && (
              <div style={{ marginBottom: 12, padding: '10px 14px', background: 'rgba(220,38,38,0.10)', color: '#b91c1c', borderRadius: 'var(--r-sm)', fontSize: 13 }}>
                {camErr}
              </div>
            )}
            <div
              onClick={() => fileRef.current?.click()}
              onDragOver={e => e.preventDefault()}
              onDrop={e => { e.preventDefault(); handleFile(e.dataTransfer.files[0]); }}
              style={{
                padding: '56px 32px', border: '1.5px dashed var(--line-3)',
                borderRadius: 'var(--r-lg)', textAlign: 'center',
                background: 'var(--paper-tint)', cursor: 'pointer', transition: 'all 200ms',
              }}
              onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.background = 'var(--paper)'; }}
              onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--line-3)'; e.currentTarget.style.background = 'var(--paper-tint)'; }}
            >
              <input ref={fileRef} type="file" accept="image/*,.pdf" hidden onChange={e => handleFile(e.target.files[0])} />
              <div style={{ width: 48, height: 48, margin: '0 auto', borderRadius: '50%', background: 'var(--paper)', border: '1px solid var(--line-2)', display: 'grid', placeItems: 'center' }}>
                <window.MFIcon name="upload" size={18} />
              </div>
              <div className="serif" style={{ fontSize: 20, fontStyle: 'italic', marginTop: 16 }}>
                {isMobileCamera ? 'O sube un archivo' : 'Suelta tu menú aquí'}
              </div>
              <p style={{ fontSize: 12, color: 'var(--mu)', marginTop: 6 }}>PNG · JPG · PDF · Gemini Vision lee todo</p>
            </div>
            <div style={{ marginTop: 16, textAlign: 'center' }}>
              <button className="btn ghost sm" onClick={useSample} style={{ fontSize: 12, color: 'var(--mu)' }}>
                O usar menú de muestra →
              </button>
            </div>
          </>
        )}

        {/* #4 — Camera viewfinder overlay */}
        {camMode && (
          <div style={{ position: 'fixed', inset: 0, zIndex: 1000, background: '#070504', display: 'flex', flexDirection: 'column' }}>
            {/* Viewfinder or preview */}
            <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
              {!camSnap && (
                <video
                  ref={videoRef}
                  autoPlay playsInline muted
                  style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                />
              )}
              {camSnap && (
                <img src={camSnap} alt="Captura" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
              )}
              {/* Scan guide */}
              {!camSnap && (
                <div style={{
                  position: 'absolute', inset: '10%', border: '2px solid rgba(200,146,58,0.55)',
                  borderRadius: 16, pointerEvents: 'none',
                  boxShadow: '0 0 0 4000px rgba(0,0,0,0.35)',
                }}>
                  <div style={{ position: 'absolute', top: -24, left: 0, right: 0, textAlign: 'center', fontSize: 11, color: 'rgba(250,249,245,0.75)', letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 600 }}>
                    Apunta al menú físico
                  </div>
                </div>
              )}
            </div>

            {/* Camera controls */}
            <div style={{ padding: '24px 32px calc(32px + env(safe-area-inset-bottom, 0px))', background: '#0E0C09', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>
              <button onClick={closeCamera} title="Cancelar" style={{ width: 48, height: 48, borderRadius: '50%', background: 'rgba(255,255,255,0.1)', display: 'grid', placeItems: 'center', color: '#FAF9F5' }}>
                <window.MFIcon name="x" size={18} />
              </button>

              {!camSnap ? (
                <button
                  onClick={capturePhoto}
                  title="Capturar"
                  style={{
                    width: 72, height: 72, borderRadius: '50%',
                    background: '#FAF9F5', border: '4px solid rgba(255,255,255,0.4)',
                    boxShadow: '0 0 0 2px #FAF9F5',
                    transition: 'transform 100ms',
                  }}
                  onMouseDown={e => { e.currentTarget.style.transform = 'scale(0.93)'; }}
                  onMouseUp={e  => { e.currentTarget.style.transform = 'none'; }}
                />
              ) : (
                <button
                  onClick={confirmCapture}
                  style={{
                    height: 56, padding: '0 28px', borderRadius: 999,
                    background: 'var(--accent, #C8923A)', color: '#fff',
                    fontSize: 15, fontWeight: 700, display: 'flex', alignItems: 'center', gap: 10,
                  }}
                >
                  <window.MFIcon name="sparkle" size={16} />
                  Analizar con Gemini
                </button>
              )}

              {!camSnap ? (
                <div style={{ width: 48 }} /> /* spacer */
              ) : (
                <button onClick={() => setCamSnap(null)} title="Tomar otra foto" style={{ width: 48, height: 48, borderRadius: '50%', background: 'rgba(255,255,255,0.1)', display: 'grid', placeItems: 'center', color: '#FAF9F5' }}>
                  <window.MFIcon name="refresh" size={18} />
                </button>
              )}
            </div>

            {/* Hidden canvas for capture */}
            <canvas ref={canvasRef} style={{ display: 'none' }} />
          </div>
        )}

        {scanning && <ScanningCard progress={progress} />}

        {mode === 'ai' && !aiGenBusy && (
          <div className="card" style={{ padding: 28 }}>
            <Field label="Describe tu cocina" hint="Tipo de cocina, platos estrella, rango de precios, ambiente.">
              <textarea className="input" rows={5} value={aiDesc} onChange={e => setAiDesc(e.target.value)}
                style={{ height: 'auto', padding: 16, resize: 'vertical', lineHeight: 1.55 }}
                placeholder="Cocina mexicana de raíz. Especialidad: maíz nixtamalizado, salsas de molcajete, cordero al hoyo. Precio medio-alto, ambiente íntimo, 14 mesas." />
            </Field>
            <button className="btn accent" style={{ marginTop: 18, width: '100%', justifyContent: 'center' }}
              onClick={generateFromText} disabled={!aiDesc.trim()}>
              <window.MFIcon name="sparkle" size={14} />
              Generar menú completo con IA
            </button>
          </div>
        )}

        {mode === 'ai' && aiGenBusy && (
          <ScanningCard progress={50} label="Gemini está creando tu carta…" />
        )}

        {mode === 'imported' && (
          <MenuReview draft={draft} update={update} />
        )}

        {scanError && (
          <div style={{ marginTop: 16, padding: '12px 16px', background: '#FEF2F2', border: '1px solid #FECACA', borderRadius: 'var(--r-md)', fontSize: 13, color: '#DC2626', lineHeight: 1.5 }}>
            {scanError}
          </div>
        )}
      </div>

      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 48 }}>
        <button className="btn ghost" onClick={onBack}>
          <window.MFIcon name="arrowL" size={14} />
          Atrás
        </button>
        <button className="btn accent lg" onClick={onNext} disabled={!draft.menu?.length}>
          Continuar a mesas
          <window.MFIcon name="arrow" size={16} />
        </button>
      </div>
    </div>
  );
}

function ScanningCard({ progress, label }) {
  const stages = [
    { p: 20,  label: 'Identificando categorías' },
    { p: 45,  label: 'Leyendo nombres y descripciones' },
    { p: 70,  label: 'Procesando precios' },
    { p: 90,  label: 'Traduciendo y refinando' },
    { p: 100, label: 'Listo' },
  ];
  return (
    <div className="card" style={{ padding: 28 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
        <div style={{ width: 28, height: 28, borderRadius: '50%', border: '2px solid var(--line)', borderTopColor: 'var(--accent)' }} className="spin" />
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 10, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)' }}>
            Gemini Flash · Visión
          </div>
          <div className="serif" style={{ fontSize: 18, fontStyle: 'italic', marginTop: 4 }}>
            {label || stages.find(s => progress < s.p)?.label || 'Listo'}
          </div>
        </div>
        <div className="mono" style={{ fontSize: 16, color: 'var(--accent)' }}>{progress}%</div>
      </div>
      <div style={{ height: 2, background: 'var(--line)', marginTop: 22, borderRadius: 2, overflow: 'hidden' }}>
        <div style={{ height: '100%', width: `${progress}%`, background: 'var(--accent)', transition: 'width 200ms ease' }} />
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 18 }}>
        {stages.slice(0, -1).map(s => {
          const done = progress >= s.p;
          return (
            <span key={s.p} style={{
              padding: '4px 10px', fontSize: 11, borderRadius: 999,
              background: done ? 'var(--accent)' : 'transparent',
              color: done ? 'var(--accent-ink)' : 'var(--mu)',
              border: done ? 'none' : '1px solid var(--line-2)',
              fontWeight: 500,
              transition: 'all 280ms',
            }}>
              {done && '✓ '}{s.label}
            </span>
          );
        })}
      </div>
    </div>
  );
}

function MenuReview({ draft, update }) {
  const [openCat, setOpenCat]     = useW(draft.categories[0]?.id);
  const [improving, setImproving] = useW(false);
  const [improveErr, setImproveErr] = useW('');

  const improveDescriptions = async () => {
    setImproving(true);
    setImproveErr('');
    try {
      const prompt = `Eres un experto en marketing gastronómico. Mejora las descripciones de estos platos para que sean más apetitosas y evocadoras. NO cambies nombres ni precios. Devuelve EXACTAMENTE el mismo JSON con los mismos IDs y estructura, solo mejora el campo "desc":

${JSON.stringify(draft.menu, null, 2)}`;
      const improved = await _geminiText(prompt);
      if (Array.isArray(improved)) {
        update('menu', improved.map((it, i) => ({ ...draft.menu[i], ...it, id: draft.menu[i]?.id || it.id })));
      }
    } catch (e) {
      setImproveErr('Error al mejorar. Intenta de nuevo.');
    } finally {
      setImproving(false);
    }
  };

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 16 }}>
        <div style={{ fontSize: 13, color: 'var(--mu)' }}>
          <span style={{ color: 'var(--ink)', fontWeight: 500 }}>{draft.menu.length} platos</span> en {draft.categories.length} categorías
          {improveErr && <span style={{ color: '#DC2626', marginLeft: 8, fontSize: 12 }}>{improveErr}</span>}
        </div>
        <button className="btn ghost sm" onClick={improveDescriptions} disabled={improving || !draft.menu.length}>
          <window.MFIcon name="sparkle" size={12} />
          {improving ? 'Mejorando…' : 'Mejorar descripciones con IA'}
        </button>
      </div>

      <div className="card" style={{ overflow: 'hidden' }}>
        {draft.categories.map((c, i) => {
          const open     = openCat === c.id;
          const catItems = draft.menu.filter(it => it.cat === c.id);
          return (
            <div key={c.id} style={{ borderTop: i > 0 ? '1px solid var(--line)' : 'none' }}>
              <button onClick={() => setOpenCat(open ? null : c.id)} style={{
                width: '100%', padding: '18px 24px',
                display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                textAlign: 'left',
              }}>
                <div style={{ display: 'flex', alignItems: 'baseline', gap: 14 }}>
                  <span className="serif-italic" style={{ fontSize: 18 }}>{c.name}</span>
                  <span style={{ fontSize: 12, color: 'var(--mu)' }}>{catItems.length} platos</span>
                </div>
                <window.MFIcon name="chevD" size={14} style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 220ms' }} />
              </button>
              {open && (
                <div style={{ borderTop: '1px solid var(--line)' }}>
                  {catItems.map((it, idx) => (
                    <div key={it.id} style={{
                      padding: '16px 24px',
                      display: 'grid', gridTemplateColumns: '40px 1fr 70px 28px',
                      gap: 16, alignItems: 'center',
                      borderTop: idx > 0 ? '1px solid var(--line)' : 'none',
                    }}>
                      <div style={{ width: 36, height: 36, borderRadius: 6, background: `url(${it.img}) center/cover, var(--paper-2)` }} />
                      <div>
                        <div className="serif-italic" style={{ fontSize: 15, lineHeight: 1.2 }}>{it.name}</div>
                        <div style={{ fontSize: 11, color: 'var(--mu)', marginTop: 3, lineHeight: 1.4, display: '-webkit-box', WebkitLineClamp: 1, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{it.desc}</div>
                      </div>
                      <div className="serif tabular" style={{ fontSize: 14, color: 'var(--accent)', textAlign: 'right' }}>${it.price}</div>
                      <window.MFIcon name="edit" size={13} style={{ color: 'var(--mu-2)' }} />
                    </div>
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ── STEP IV: TABLES ───────────────────────────────────────────────────── */
function StepTables({ draft, update, onBack, onNext }) {
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
        <span className="serif-italic" style={{ fontSize: 22, color: 'var(--accent)' }}>IV.</span>
        <window.MFMarker label="Paso 4 de 5" />
      </div>
      <h1 className="display-md" style={{ marginTop: 12, fontWeight: 400, letterSpacing: '-0.018em' }}>
        Enlaza tu
        <br />
        <em style={{ color: 'var(--accent)' }}>Cocina.</em>
      </h1>
      <p style={{ fontSize: 15, color: 'var(--mu)', lineHeight: 1.6, marginTop: 20, maxWidth: 480 }}>
        Conecta los códigos QR de tus mesas directamente al POS de tu cocina
        vía webhooks de baja latencia. Automatiza la comanda, divide cuentas
        y elimina el error humano.
      </p>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, marginTop: 36 }}>
        <Field label="Número de mesas">
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <button className="btn icon" onClick={() => update('tables', Math.max(1, draft.tables - 1))}>
              <window.MFIcon name="minus" size={14} />
            </button>
            <input className="input tabular serif" value={draft.tables}
              onChange={e => update('tables', Number(e.target.value) || 0)}
              style={{ textAlign: 'center', fontSize: 24, fontStyle: 'italic' }} />
            <button className="btn icon" onClick={() => update('tables', draft.tables + 1)}>
              <window.MFIcon name="plus" size={14} />
            </button>
          </div>
        </Field>
        <Field label="Sucursales">
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <button className="btn icon" onClick={() => update('locations', Math.max(1, draft.locations - 1))}>
              <window.MFIcon name="minus" size={14} />
            </button>
            <input className="input tabular serif" value={draft.locations}
              onChange={e => update('locations', Number(e.target.value) || 0)}
              style={{ textAlign: 'center', fontSize: 24, fontStyle: 'italic' }} />
            <button className="btn icon" onClick={() => update('locations', draft.locations + 1)}>
              <window.MFIcon name="plus" size={14} />
            </button>
          </div>
        </Field>
      </div>

      {/* Mesa grid preview */}
      <div style={{ marginTop: 32, padding: 28, background: 'var(--paper-2)', borderRadius: 'var(--r-lg)' }}>
        <div style={{ fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)', marginBottom: 18 }}>
          Vista previa · {draft.tables} mesas
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.min(12, draft.tables)}, 1fr)`, gap: 8 }}>
          {Array.from({ length: Math.min(draft.tables, 60) }).map((_, i) => (
            <div key={i} style={{
              aspectRatio: '1',
              border: '1px dashed var(--line-2)',
              borderRadius: 6,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 9, color: 'var(--mu-2)',
              fontFamily: 'var(--mono)',
            }}>
              {String(i + 1).padStart(2, '0')}
            </div>
          ))}
        </div>
        <div style={{ marginTop: 16, fontSize: 12, color: 'var(--mu)', display: 'flex', alignItems: 'center', gap: 6 }}>
          <window.MFIcon name="qr" size={14} />
          Cada mesa con su URL única, ej. <span className="mono" style={{ color: 'var(--ink)' }}>{draft.domain || 'tu-marca'}.menu/M-07</span>
        </div>
      </div>

      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 56 }}>
        <button className="btn ghost" onClick={onBack}>
          <window.MFIcon name="arrowL" size={14} />
          Atrás
        </button>
        <button className="btn accent lg" onClick={onNext}>
          Continuar a publicar
          <window.MFIcon name="arrow" size={16} />
        </button>
      </div>
    </div>
  );
}

/* ── STEP V: PUBLISH ───────────────────────────────────────────────────── */
function StepPublish({ draft, update, onBack, onPublish }) {
  const [authTab, setAuthTab]     = useW('google');
  const [email, setEmail]         = useW(draft.email || '');
  const [password, setPassword]   = useW('');
  const [busy, setBusy]           = useW(false);
  const [authError, setAuthError] = useW('');
  const [saveError, setSaveError] = useW(null);

  const syncEmail = (v) => { setEmail(v); update('email', v); };

  const AUTH_TABS = [['google', 'Google'], ['email', 'Email']];

  const handlePublish = async () => {
    setBusy(true);
    setAuthError('');
    setSaveError(null);
    try {
      if (typeof firebase === 'undefined' || !firebase.auth) {
        throw new Error('Firebase no disponible. Verifica tu conexión.');
      }
      if (authTab === 'google') {
        const provider = new firebase.auth.GoogleAuthProvider();
        const cred = await firebase.auth().signInWithPopup(provider);
        if (cred.user.email) update('email', cred.user.email);
      } else if (authTab === 'email') {
        if (!email)    { setAuthError('Ingresa tu correo.');              setBusy(false); return; }
        if (!password) { setAuthError('Ingresa una contraseña.');         setBusy(false); return; }
        try {
          const cred = await firebase.auth().createUserWithEmailAndPassword(email, password);
          try { await cred.user.sendEmailVerification(); } catch (_) {}
        } catch (authErr) {
          if (authErr.code === 'auth/email-already-in-use') {
            setAuthError('Ya existe una cuenta con ese correo. Iniciando sesión…');
            await firebase.auth().signInWithEmailAndPassword(email, password);
            setAuthError('');
          } else throw authErr;
        }
      }
      await onPublish?.();
    } catch (e) {
      const msg = wMapErr(e.code) || e.message || 'Error al publicar. Verifica tu conexión.';
      if (e.code?.startsWith('auth/')) setAuthError(msg);
      else setSaveError(msg);
      setBusy(false);
    }
  };

  const canPublish = authTab === 'google' || (authTab === 'email' && email && password);

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
        <span className="serif-italic" style={{ fontSize: 22, color: 'var(--accent)' }}>V.</span>
        <window.MFMarker label="Paso 5 de 5 · Final" />
      </div>
      <h1 className="display-md" style={{ marginTop: 12, fontWeight: 400, letterSpacing: '-0.018em' }}>
        Lista para
        <br />
        <em style={{ color: 'var(--accent)' }}>recibir comensales.</em>
      </h1>
      <p style={{ fontSize: 15, color: 'var(--mu)', lineHeight: 1.6, marginTop: 20, maxWidth: 520 }}>
        Crea tu cuenta para publicar. Elige cómo quieres acceder a tu panel.
      </p>

      {/* Domain */}
      <div style={{ marginTop: 36 }}>
        <Field label="Tu dominio" hint="Puedes conectar uno propio después.">
          <div style={{ display: 'flex', alignItems: 'stretch', borderRadius: 'var(--r-md)', border: '1px solid var(--line-2)', background: 'var(--paper)', overflow: 'hidden' }}>
            <input className="input" value={draft.domain}
              onChange={e => update('domain', e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))}
              placeholder="casaamaranta"
              style={{ border: 'none', borderRadius: 0, flex: 1 }} />
            <div style={{ padding: '0 18px', display: 'flex', alignItems: 'center', fontSize: 14, color: 'var(--mu)', background: 'var(--paper-2)', borderLeft: '1px solid var(--line-2)' }}>
              .menu.app
            </div>
          </div>
        </Field>
      </div>

      {/* Inline account creation */}
      <div style={{ marginTop: 24, padding: '20px 22px', background: 'var(--paper-2)', border: '1px solid var(--line)', borderRadius: 'var(--r-lg)' }}>
        <div style={{ fontSize: 10, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)', marginBottom: 14 }}>
          Crea tu cuenta
        </div>

        {/* Auth tabs */}
        <div style={{ display: 'flex', background: 'var(--paper)', borderRadius: 10, padding: 3, marginBottom: 18 }}>
          {AUTH_TABS.map(([id, label]) => (
            <button key={id} onClick={() => { setAuthTab(id); setAuthError(''); }} style={{
              flex: 1, padding: '8px 4px', borderRadius: 8, fontSize: 12,
              background: authTab === id ? 'var(--ink)' : 'transparent',
              color: authTab === id ? '#fff' : 'var(--mu)',
              fontWeight: authTab === id ? 600 : 400,
              transition: 'all 150ms',
            }}>{label}</button>
          ))}
        </div>

        {authError && (
          <div style={{ padding: '9px 12px', background: '#fee2e2', color: '#b91c1c', borderRadius: 8, fontSize: 12, marginBottom: 14 }}>
            {authError}
          </div>
        )}

        {authTab === 'google' && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13, color: 'var(--mu)', padding: '4px 0' }}>
            <WizardGoogleIcon />
            Al publicar, Google te pedirá autenticarte.
          </div>
        )}

        {authTab === 'email' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            <input className="input" type="email" placeholder="tu@restaurante.com"
              value={email} onChange={e => syncEmail(e.target.value)} autoFocus />
            <input className="input" type="password" placeholder="Crea una contraseña (mín. 6 caracteres)"
              value={password} onChange={e => setPassword(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && canPublish && !busy && handlePublish()} />
            <div style={{ fontSize: 11, color: 'var(--mu)' }}>
              Te enviaremos un correo de verificación al publicar.
            </div>
          </div>
        )}

        {authTab === 'phone' && (
          <div style={{ fontSize: 13, color: 'var(--mu)', padding: '4px 0' }}>
            Autenticación por teléfono disponible próximamente.
          </div>
        )}
      </div>

      {/* Summary card */}
      <div className="card" style={{ padding: 28, marginTop: 24, background: 'var(--paper-2)' }}>
        <div style={{ fontSize: 11, letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)', marginBottom: 18 }}>
          Resumen final
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18 }}>
          <SummaryRow label="Nombre"  value={draft.name     || '—'} />
          <SummaryRow label="Tagline" value={draft.tagline  || '—'} />
          <SummaryRow label="Lugar"   value={draft.location || '—'} />
          <SummaryRow label="Año"     value={draft.founded  || '—'} />
          <SummaryRow label="Acento" value={
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
              <span style={{ width: 14, height: 14, borderRadius: 4, background: draft.accent, display: 'inline-block' }} />
              <span className="mono">{draft.accent.toUpperCase()}</span>
            </span>
          } />
          <SummaryRow label="Mesas" value={`${draft.tables} mesas · ${draft.locations} sucursal${draft.locations > 1 ? 'es' : ''}`} />
          <SummaryRow label="Carta" value={`${draft.menu.length} platos · ${draft.categories.length} cat.`} />
          <SummaryRow label="URL"   value={<span className="mono">{draft.domain || 'tu-marca'}.menu.app</span>} />
        </div>
      </div>

      {saveError && (
        <div style={{ marginTop: 24, padding: '12px 16px', background: '#FEF2F2', border: '1px solid #FECACA', borderRadius: 'var(--r-md)', fontSize: 13, color: '#DC2626', lineHeight: 1.5 }}>
          {saveError}
        </div>
      )}

      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 24 }}>
        <button className="btn ghost" onClick={onBack} disabled={busy}>
          <window.MFIcon name="arrowL" size={14} />
          Atrás
        </button>
        <button className="btn accent lg" onClick={handlePublish} disabled={!canPublish || busy}>
          {busy ? 'Publicando…' : authTab === 'google' ? 'Publicar con Google →' : 'Publicar mi menú →'}
          {!busy && <window.MFIcon name="sparkle" size={16} />}
        </button>
      </div>
    </div>
  );
}

function SummaryRow({ label, value }) {
  return (
    <div>
      <div style={{ fontSize: 10, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)' }}>{label}</div>
      <div style={{ fontSize: 14, marginTop: 4, color: 'var(--ink)' }}>{value}</div>
    </div>
  );
}

/* ── Live preview pane ─────────────────────────────────────────────────── */
function WizardPreview({ draft, step }) {
  const phoneAreaRef             = useRW(null);
  const [phoneScale, setPhoneScale] = useW(1);

  useEW(() => {
    const el = phoneAreaRef.current;
    if (!el) return;
    const update = () => setPhoneScale(Math.min(1, (el.clientHeight - 16) / 600));
    update();
    const ro = new ResizeObserver(update);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  const previewBrand = {
    ...WIZARD_BRANDS.casa,
    name:     draft.name     || 'Tu casa',
    tagline:  draft.tagline  || 'Cocina · cocina',
    location: draft.location || 'Ciudad de México',
    accent:   draft.accent,
    initials: (draft.name?.split(' ').slice(0, 2).map(w => w[0]).join('') || 'TC').toUpperCase().slice(0, 2),
  };

  return (
    <aside style={{
      background: 'var(--paper-2)',
      padding: '48px 36px 32px',
      height: '100vh',
      display: 'flex', flexDirection: 'column', alignItems: 'center',
      overflow: 'hidden',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', marginBottom: 28 }}>
        <window.MFMarker label="Vista en vivo" />
        <div className="seg">
          <button className="active">Móvil</button>
          <button>Sala</button>
        </div>
      </div>

      {/* Phone area — flex: 1 + minHeight: 0 allows it to shrink; ResizeObserver drives scale */}
      <div ref={phoneAreaRef} style={{ flex: 1, minHeight: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', overflow: 'hidden' }}>
        <div style={{
          transform: `scale(${phoneScale})`,
          transformOrigin: 'center center',
          marginTop:    phoneScale < 1 ? `${-(600 * (1 - phoneScale)) / 2}px` : 0,
          marginBottom: phoneScale < 1 ? `${-(600 * (1 - phoneScale)) / 2}px` : 0,
        }}>
          <WizardPreviewPhone brand={previewBrand} step={step} draft={draft} />
        </div>
      </div>

      <div style={{ width: '100%', paddingTop: 20, borderTop: '1px solid var(--line)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div>
            <div style={{ fontSize: 10, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 600, color: 'var(--mu)' }}>
              Acento
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
              <span style={{ width: 18, height: 18, borderRadius: 4, background: draft.accent, border: '1px solid var(--line-2)' }} />
              <span className="mono" style={{ fontSize: 13 }}>{draft.accent.toUpperCase()}</span>
            </div>
          </div>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
            <window.MFIcon name="eye" size={14} style={{ color: 'var(--mu)' }} />
            <span style={{ fontSize: 12, color: 'var(--mu)' }}>Tal y como lo verán</span>
          </div>
        </div>
      </div>
    </aside>
  );
}

function WizardPreviewPhone({ brand, step, draft }) {
  const item = draft.menu?.[0] || WIZARD_BRANDS.casa.items[0];
  return (
    <div style={{
      width: 300, height: 600,
      borderRadius: 42,
      background: '#0a0a0a',
      padding: 12,
      boxShadow: '0 60px 100px -50px rgba(15,15,14,0.35), 0 20px 40px -20px rgba(15,15,14,0.15)',
      position: 'relative',
    }}>
      <div style={{ position: 'absolute', top: 18, left: '50%', transform: 'translateX(-50%)', width: 90, height: 22, background: '#0a0a0a', borderRadius: 12, zIndex: 3 }} />
      <div style={{
        width: '100%', height: '100%', borderRadius: 32, overflow: 'hidden',
        background: 'var(--paper)', display: 'flex', flexDirection: 'column',
      }}>
        <div style={{ height: 44 }} />
        {/* Hero */}
        <div style={{ padding: '8px 22px 24px', borderBottom: '1px solid var(--line)' }}>
          <window.MFBrandLogo brand={brand} size="md" vertical />
          <div style={{ textAlign: 'center', marginTop: 14, fontSize: 10, color: 'var(--mu)', letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 600 }}>
            {brand.location}
          </div>
        </div>
        {/* Item hero */}
        <div style={{ padding: '20px 22px' }}>
          <div style={{ fontSize: 10, color: 'var(--accent)', letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 600 }}>
            {draft.categories?.[0]?.name || 'Especialidad'}
          </div>
          <div className="serif" style={{ fontSize: 22, fontStyle: 'italic', marginTop: 6, lineHeight: 1.15 }}>
            {item?.name || 'Plato firma'}
          </div>
          <p style={{ fontSize: 12, color: 'var(--mu)', marginTop: 8, lineHeight: 1.55,
            display: '-webkit-box', WebkitLineClamp: 3, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>
            {item?.desc || 'Descripción del plato'}
          </p>
          <div style={{ marginTop: 14, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span className="serif tabular" style={{ fontSize: 18, color: 'var(--accent)' }}>${item?.price || '—'}</span>
            <button style={{ height: 36, padding: '0 16px', borderRadius: 999, background: 'var(--accent)', color: 'var(--accent-ink)', fontSize: 12, fontWeight: 500 }}>
              + Agregar
            </button>
          </div>
        </div>
        {/* List preview */}
        <div style={{ flex: 1, padding: '0 22px 16px', overflow: 'hidden' }}>
          <div style={{ height: 1, background: 'var(--line)', marginBottom: 12 }} />
          {(draft.menu || []).slice(1, 4).map((it, i) => (
            <div key={it.id} style={{ display: 'flex', justifyContent: 'space-between', paddingBottom: 10, marginBottom: 10, borderBottom: i < 2 ? '1px dashed var(--line)' : 'none' }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div className="serif-italic" style={{ fontSize: 13, lineHeight: 1.2 }}>{it.name}</div>
                <div style={{ fontSize: 10, color: 'var(--mu)', marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{it.desc}</div>
              </div>
              <div className="serif tabular" style={{ fontSize: 12, color: 'var(--accent)', marginLeft: 8 }}>${it.price}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ── Shared form helpers ───────────────────────────────────────────────── */
function Field({ label, hint, children }) {
  return (
    <label style={{ display: 'block' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 8 }}>
        <span style={{ fontSize: 10, color: 'var(--ink)', letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 600 }}>
          {label}
        </span>
        {hint && <span style={{ fontSize: 11, color: 'var(--mu-2)' }}>{hint}</span>}
      </div>
      {children}
    </label>
  );
}

/* ── Color helpers (used for live accent derivation in useEffect) ───────── */
/* NOTE: these are prefixed _wiz* to avoid shadowing palette.jsx's hexToRgb (which
   returns an array) — both are global in Babel standalone's script context. */
function clamp(x, lo, hi) { return Math.max(lo, Math.min(hi, x)); }
function _wizHexToRgb(hex) {
  const h = hex.replace('#', '');
  return { r: parseInt(h.substr(0,2),16), g: parseInt(h.substr(2,2),16), b: parseInt(h.substr(4,2),16) };
}
function _wizRgbToHex(r, g, b) {
  return '#' + [r,g,b].map(x => clamp(Math.round(x),0,255).toString(16).padStart(2,'0')).join('');
}
function darken(hex, amount) {
  const {r,g,b} = _wizHexToRgb(hex);
  return _wizRgbToHex(r*(1-amount), g*(1-amount), b*(1-amount));
}
function lighten(hex, amount) {
  const {r,g,b} = _wizHexToRgb(hex);
  return _wizRgbToHex(r+(255-r)*amount, g+(255-g)*amount, b+(255-b)*amount);
}

/* ── Firestore save — ported from original wizard ──────────────────────── */
async function saveToFirestore(profile, restaurantId) {
  try {
    const db     = firebase.firestore();
    const docRef = db.collection('restaurants').doc(restaurantId);
    await docRef.set({
      brand:       profile.brand,
      menu:        profile.menu,
      owner:       { email: profile.user.email, provider: profile.user.provider, name: profile.user.name },
      ownerUid:    firebase.auth().currentUser?.uid || null,
      logoUrl:     profile.logoUrl || null,
      slug:        restaurantId,
      settings:    { displayDirection: 'editorial' },
      createdAt:   firebase.firestore.FieldValue.serverTimestamp(),
    });
    const items     = profile.menu?.items || [];
    const BATCH_MAX = 400;
    for (let i = 0; i < items.length; i += BATCH_MAX) {
      const batch = db.batch();
      items.slice(i, i + BATCH_MAX).forEach(item => {
        const ref = docRef.collection('menuItems').doc(String(item.id));
        batch.set(ref, { ...item, available: item.available !== false });
      });
      await batch.commit();
    }
    console.log(`[wizard] Restaurant "${restaurantId}" saved (${items.length} items)`);
    return restaurantId;
  } catch (e) {
    console.warn('[wizard] Firestore save failed:', e.message);
    throw e;
  }
}

function slug(s) {
  return (s || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
}

/* ── window.FactoryWizard — adapter keeps factory.jsx untouched ─────────── */
window.FactoryWizard = function FactoryWizard({ onComplete, onCancel }) {
  return (
    <MFWizardView
      brandId="casa"
      goTo={(screen) => {
        if (screen === 'landing') onCancel?.();
        // 'dashboard' is handled via onPublish completing and then goTo('dashboard')
      }}
      onPublish={async (draft) => {
        const nameEs       = draft.name || 'Mi Restaurante';
        const restaurantId = slug(nameEs) || 'mi-restaurante';
        const profile = {
          user: {
            name:     draft.email?.split('@')[0] || 'owner',
            email:    draft.email || '',
            avatar:   null,
            provider: 'email',
          },
          brand: {
            name:     { es: nameEs, en: nameEs },
            tagline:  { es: draft.tagline || 'Cocina de autor', en: draft.tagline || 'Signature cuisine' },
            subtitle: [draft.location, draft.founded].filter(Boolean).join(' · ') || 'Est. 2024',
            palette: {
              ...(window.FactoryPalette?.defaultPalette?.() || {}),
              gold:      draft.accent || '#C8451F',
              goldLight: lighten(draft.accent || '#C8451F', 0.30),
              goldPale:  lighten(draft.accent || '#C8451F', 0.55),
              goldDeep:  darken(draft.accent  || '#C8451F', 0.25),
            },
            accentMode: draft.accentMode || 'extracted',
          },
          menu:    { items: draft.menu || [], categories: draft.categories || [] },
          logoUrl: draft.logo || null,
        };
        const rid = await saveToFirestore(profile, restaurantId);
        profile.restaurantId = rid;
        onComplete?.(profile);
      }}
    />
  );
};

window.MFWizardView  = MFWizardView;
window.factorySlug   = slug;
