/* ============================================================
   Miss XV VIP — Order Flow (order-wizard.jsx)
   À la carte en USD · 5 pasos · preview + video demos en vivo
   Solo capa visual/UX. Sin backend real.
   ============================================================ */
const { useState, useMemo, useEffect, useRef } = React;

/* ---------- helpers de cliente (sin backend) ---------- */
// Copia texto al portapapeles con fallback a un input temporal + execCommand.
async function copyText(text) {
  try {
    if (navigator.clipboard && navigator.clipboard.writeText) {
      await navigator.clipboard.writeText(text);
      return true;
    }
  } catch (e) { /* cae al fallback */ }
  try {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.setAttribute('readonly', '');
    ta.style.position = 'fixed';
    ta.style.top = '-9999px';
    ta.style.opacity = '0';
    document.body.appendChild(ta);
    ta.focus(); ta.select();
    const ok = document.execCommand('copy');
    document.body.removeChild(ta);
    return ok;
  } catch (e) { return false; }
}
// Lee un File como data URL (para guardarlo en el borrador, sin servidor).
function fileToDataURL(file, cb) {
  const r = new FileReader();
  r.onload = (e) => cb(e.target.result);
  r.readAsDataURL(file);
}
// Botón "Copiar" con confirmación visual breve.
function CopyChip({ text, label = 'Copiar' }) {
  const [done, setDone] = useState(false);
  const onCopy = async (e) => {
    e.preventDefault();
    e.stopPropagation();
    const ok = await copyText(String(text));
    if (ok) { setDone(true); setTimeout(() => setDone(false), 1500); }
  };
  return (
    <button type="button" className={`ow-copy ${done ? 'is-done' : ''}`} onClick={onCopy}>
      {done ? '¡Copiado!' : label}
    </button>
  );
}

/* ---------- icons ---------- */
const P = {
  check: 'M20 6L9 17l-5-5', chevright: 'M9 18l6-6-6-6', chevleft: 'M15 18l-6-6 6-6',
  x: 'M18 6L6 18M6 6l12 12', plus: 'M12 5v14M5 12h14',
  eye: 'M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8zM12 15a3 3 0 100-6 3 3 0 000 6z',
  play: 'M6 4l14 8-14 8V4z', pause: 'M6 4h4v16H6zM14 4h4v16h-4z',
  sparkle: 'M12 2l1.8 5.5L19.5 9l-5.7 1.5L12 16l-1.8-5.5L4.5 9l5.7-1.5z',
  star: 'M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 22 12 18.27 5.82 22 7 14.14l-5-4.87 6.91-1.01z',
  qr: 'M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h2v2h-2zM18 14h3v3h-3zM14 18h2v3h-2zM18 19h3v2h-3z',
  image: 'M19 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2zM8.5 10a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM21 15l-5-5L5 21',
  film: 'M3 3h18v18H3zM7 3v18M17 3v18M3 8h4M3 16h4M17 8h4M17 16h4M3 12h18',
  users: 'M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2M8.5 11a4 4 0 100-8 4 4 0 000 8zM23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75',
  user: 'M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z',
  calendar: 'M19 4H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V6a2 2 0 00-2-2zM16 2v4M8 2v4M3 10h18',
  clock: 'M12 22a10 10 0 100-20 10 10 0 000 20zM12 6v6l4 2',
  mappin: 'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0zM12 13a3 3 0 100-6 3 3 0 000 6z',
  palette: 'M12 22a10 10 0 110-20 10 10 0 0110 8 4 4 0 01-4 4h-2a2 2 0 00-1 4 2 2 0 01-3 2zM6.5 12a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM10.5 8a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM15.5 8a1.5 1.5 0 100-3 1.5 1.5 0 000 3z',
  heart: 'M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z',
  edit: 'M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7M18.5 2.5a2.121 2.121 0 113 3L12 15l-4 1 1-4 9.5-9.5z',
  seat: 'M3 18v-6a3 3 0 013-3h12a3 3 0 013 3v6M3 18h18M5 18v2M19 18v2M6 9V7a3 3 0 013-3h6a3 3 0 013 3v2',
  lock: 'M19 11H5a2 2 0 00-2 2v7a2 2 0 002 2h14a2 2 0 002-2v-7a2 2 0 00-2-2zM7 11V7a5 5 0 0110 0v4',
  shield: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z',
  gift: 'M20 12v10H4V12M2 7h20v5H2zM12 22V7M12 7H7.5a2.5 2.5 0 010-5C11 2 12 7 12 7zM12 7h4.5a2.5 2.5 0 000-5C13 2 12 7 12 7z',
  zap: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z',
  crown: 'M3 18l2-11 5 6 2-8 2 8 5-6 2 11z',
  message: 'M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z',
  globe: 'M12 22a10 10 0 100-20 10 10 0 000 20zM2 12h20M12 2a15 15 0 010 20 15 15 0 010-20z',
  shirt: 'M16 3l4 3-3 3-1-1v11H8V8L7 9 4 6l4-3 2 2h4z',
  ticket: 'M3 8a2 2 0 012-2h14a2 2 0 012 2v2a2 2 0 000 4v2a2 2 0 01-2 2H5a2 2 0 01-2-2v-2a2 2 0 000-4zM12 6v2M12 11v2M12 16v2',
  music: 'M9 18V5l12-2v13M9 18a3 3 0 11-6 0 3 3 0 016 0zM21 16a3 3 0 11-6 0 3 3 0 016 0z',
  mail: 'M4 4h16a2 2 0 012 2v12a2 2 0 01-2 2H4a2 2 0 01-2-2V6a2 2 0 012-2zM22 6l-10 7L2 6',
  info: 'M12 22a10 10 0 100-20 10 10 0 000 20zM12 8h.01M11 12h1v4h1'
};
function Ic({ n, s = 16, style, cls }) {
  return (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor"
    strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" style={style} className={cls}>
      <path d={P[n] || P.info} />
    </svg>);

}
function GoogleG({ s = 18 }) {
  return (
    <svg viewBox="0 0 48 48" width={s} height={s} aria-hidden="true">
      <path fill="#FFC107" d="M43.6 20.5H42V20H24v8h11.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.9 1.2 8 3.1l5.7-5.7C34 6.5 29.3 4.5 24 4.5 13.2 4.5 4.5 13.2 4.5 24S13.2 43.5 24 43.5 43.5 34.8 43.5 24c0-1.2-.1-2.3-.4-3.5z" />
      <path fill="#FF3D00" d="M6.3 14.7l6.6 4.8C14.7 16 19 13 24 13c3.1 0 5.9 1.2 8 3.1l5.7-5.7C34 6.5 29.3 4.5 24 4.5 16.3 4.5 9.7 8.9 6.3 14.7z" />
      <path fill="#4CAF50" d="M24 43.5c5.2 0 9.9-2 13.4-5.2l-6.2-5.2c-2 1.5-4.6 2.4-7.2 2.4-5.2 0-9.6-3.3-11.3-8l-6.5 5C9.6 39 16.2 43.5 24 43.5z" />
      <path fill="#1976D2" d="M43.6 20.5H42V20H24v8h11.3c-.8 2.3-2.3 4.3-4.1 5.6l6.2 5.2C39.7 36.9 43.5 31.2 43.5 24c0-1.2-.1-2.3-.4-3.5z" />
    </svg>);

}

/* ---------- catálogo real (USD) ---------- */
const SERVICES = [
{ id: 'invitacion', base: true, name: 'Invitación Digital VIP Original', short: 'Tu invitación base, personalizada', price: 100, icon: 'sparkle', tint: '#f5c945', badge: 'Invitación', demoUrl: 'https://www.missxvvip.com/inv/xv-maria-fernanda-valdes', desc: 'La invitación digital animada y personalizada a tu medida, siempre incluida en tu orden.' },
{ id: 'savedate', name: 'Anuncia tu Fecha', short: 'Save the Date', price: 150, icon: 'calendar', tint: '#b347ff', badge: 'Save the Date', desc: 'Video animado para anunciar tu fecha y crear expectativa entre tus invitados.' },
{ id: 'historia', name: 'Cuenta tu Historia', short: 'Video Cronológico', price: 300, popular: true, icon: 'film', tint: '#fb923c', badge: 'Tu Historia', desc: 'Video cronológico que narra tu historia con fotos y momentos clave.' },
{ id: 'qrvip', name: 'Vive tu Fiesta', short: 'QR-VIP · álbum y photo wall', price: 75, icon: 'qr', tint: '#22d34a', badge: 'QR-VIP', desc: 'Álbum colaborativo y photo wall en vivo: tus invitados suben fotos con un QR.' },
{ id: 'seating', name: 'Organiza tus Mesas', short: 'SP-VIP · mapa de mesas', price: 75, icon: 'seat', tint: '#3b8bd6', badge: 'Seating', desc: 'Seating planner visual para acomodar tus mesas e invitados sin estrés.' },
{ id: 'slideshow', name: 'VIP Slideshow', short: '3 videos en 1', price: 100, was: 300, icon: 'image', tint: '#ec4899', badge: 'Slideshow', desc: 'Tres videos integrados en una sola pieza espectacular para tu pantalla.' }];

/* cargo por invitación bilingüe (EN/ES) */
const BILINGUAL_PRICE = 25;
const bilingualFee = (lang) => lang === 'bi' ? BILINGUAL_PRICE : 0;

const THEMES = [
{ id: 'esmeralda', name: 'Esmeralda Real', mood: 'Botánico · señorial', bg: ['#16341f', '#0a1d11'], text: '#FFF8E6', accent: '#E8C76B', dots: ['#1f5a33', '#E8C76B', '#FFF8E6'] },
{ id: 'rosa', name: 'Rosa Cuarzo', mood: 'Romántico · suave', bg: ['#3a1f2e', '#1c0e16'], text: '#FFEFF4', accent: '#F2A7BB', dots: ['#7a3450', '#F2A7BB', '#FFEFF4'] },
{ id: 'lavanda', name: 'Lavanda VIP', mood: 'Moderno · etéreo', bg: ['#241640', '#120a26'], text: '#F3ECFF', accent: '#d4a3ff', dots: ['#5a2e9e', '#d4a3ff', '#F3ECFF'] },
{ id: 'dorado', name: 'Dorado Clásico', mood: 'Atemporal · lujo', bg: ['#14161f', '#08080d'], text: '#FFF8E6', accent: '#f5c945', dots: ['#2a2a3a', '#f5c945', '#FFF8E6'] }];

const STEPS = ['Tus servicios', 'La festejada', 'Secciones', 'Temática', 'Resumen', 'Pago'];
/* secciones de la invitación VIP real (VIPINV_SECTIONS del portal INV) — core = siempre activa; req = servicio del que dependen */
const SECTIONS = [
{ id: 'video_intro', name: 'Video de intro / apertura', desc: 'Pantalla animada de bienvenida', icon: 'play' },
{ id: 'hero', name: 'Portada', desc: 'Nombre y fecha principal', icon: 'crown' },
{ id: 'countdown', name: 'Cuenta regresiva', desc: 'Días, horas y minutos al gran día', icon: 'clock' },
{ id: 'parents', name: 'Padres / Padrinos', desc: 'Con la bendición de…', icon: 'heart' },
{ id: 'ceremony', name: 'Ceremonia', desc: 'Misa o acto religioso', icon: 'sparkle' },
{ id: 'reception', name: 'Recepción', desc: 'Dirección, hora y mapa del salón', icon: 'mappin' },
{ id: 'itinerary', name: 'Itinerario', desc: 'Programa del evento', icon: 'calendar' },
{ id: 'dress_code', name: 'Vestimenta', desc: 'Código de vestimenta', icon: 'shirt' },
{ id: 'registry', name: 'Mesa de Regalos', desc: 'Links a tu registro o datos', icon: 'gift' },
{ id: 'corte', name: 'Corte de Honor', desc: 'Chambelanes y damas de honor', icon: 'users' },
{ id: 'gallery', name: 'Galería', desc: 'Hasta 6 fotos en tu invitación', icon: 'image' },
{ id: 'music', name: 'Música de fondo', desc: 'Activa una pista al abrir', icon: 'music' },
{ id: 'qrvip', name: 'Álbum de Fotos QR', desc: 'Muro de fotos colaborativo de invitados', icon: 'qr' },
{ id: 'rsvp', name: 'RSVP', desc: 'Confirmación de asistencia', icon: 'mail' },
{ id: 'guestbook', name: 'Libro de Visitas', desc: 'Mensajes y firmas de invitados', icon: 'message' },
{ id: 'thanks', name: 'Agradecimiento', desc: 'Mensaje de cierre', icon: 'sparkle' },
{ id: 'map', name: 'Mapa / Ubicación', desc: 'Cómo llegar al evento', icon: 'globe' }];

const REQ_LABEL = { slideshow: 'VIP Slideshow', qrvip: 'QR-VIP', savedate: 'Save the Date' };
/* las secciones son contenido libre — el cliente las enciende/apaga, no dependen de servicios */
function secAvailable() {return true;}
/* paquetes de inicio rápido — precios fijos de negocio (ids no-base) */
const PKGS = [
{ id: 'esencial', name: 'Esencial', icon: 'sparkle', price: 100, tagline: 'Tu invitación digital, lista para brillar.', items: [] },
{ id: 'premium', name: 'Premium', icon: 'star', popular: true, price: 200, tagline: 'Invitación + QR + Seating + VIP Slideshow.', items: ['qrvip', 'seating', 'slideshow'] },
{ id: 'lujo', name: 'Lujo VIP', icon: 'crown', price: 500, tagline: 'Todo incluido: video crono, save the date y slideshow.', items: ['qrvip', 'seating', 'savedate', 'historia', 'slideshow'] }];

function pkgActive(services, pkg) {
  if (services.size !== pkg.items.length) return false;
  return pkg.items.every((id) => services.has(id));
}
function findPkg(services) {return PKGS.find((p) => pkgActive(services, p)) || null;}
function pkgListValue(pkg) {return SERVICES.filter((s) => s.base || pkg.items.includes(s.id)).reduce((a, s) => a + s.price, 0);}
/* CARRILES SEPARADOS: paquete = precio fijo sin promos | à la carte = lista + promos por monto */
function orderState(services) {
  const pkg = findPkg(services);
  if (pkg) return { mode: 'pkg', pkg, total: pkg.price, gross: pkg.price, discount: 0, freeIds: new Set(), freeQS: false, freeSlide: false };
  return { mode: 'alc', pkg: null, ...calcTotals(services) };
}
const USD = (n) => '$' + n.toLocaleString('en-US');
const USD2 = (n) => '$' + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
/* ── COMISIÓN TARJETA (Stripe) ──────────────────────────────────
   Ajusta estos dos valores a la tasa real de tu cuenta Stripe.
   El recargo SOLO aplica a pagos con tarjeta; el cliente lo absorbe. */
const CARD_FEE_PCT = 0.029; // 2.9 %
const CARD_FEE_FIXED = 0.30; // $0.30 USD por transacción
/* Stripe activo: cuando es false, no se muestra el sello "Recomendado" en tarjeta */
const STRIPE_ACTIVE = false;
/* grossing-up: lo que cobramos para recibir `total` neto tras la comisión */
function cardCharge(total) {
  return Math.round((total + CARD_FEE_FIXED) / (1 - CARD_FEE_PCT) * 100) / 100;
}
const MESES = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];
const PAY = { zelle: { handle: '(818) 674-3792', titular: 'Manuel Acevedo' }, venmo: { handle: '@missxvvip' } };
/* número de WhatsApp del negocio (formato wa.me, sin + ni espacios). Edítalo aquí. */
const WHATSAPP_NUMBER = '18183106910';
function waLink(msg) {return `https://wa.me/${WHATSAPP_NUMBER}?text=${encodeURIComponent(msg)}`;}

function fmtDate(d) {
  if (!d) return null;
  const [y, m, day] = d.split('-').map(Number);
  if (!y || !m || !day) return null;
  return `${String(day).padStart(2, '0')} de ${MESES[m - 1]} de ${y}`;
}
const capMes = (m) => m.charAt(0).toUpperCase() + m.slice(1);
/* etiqueta de fecha flexible: exacta · mes/año · por confirmar */
function eventDateLabel(data) {
  const mode = data.dateMode || 'exact';
  if (mode === 'tbd') return 'Fecha por confirmar';
  if (mode === 'monthyear') {
    if (!data.dateMY) return null;
    const [y, m] = data.dateMY.split('-').map(Number);
    if (!y || !m) return null;
    return `${capMes(MESES[m - 1])} ${y}`;
  }
  return fmtDate(data.date);
}
/* ¿la fecha quedó pendiente de afinar? (tbd o mes/año o vacía) */
function dateIsPending(data) {
  const mode = data.dateMode || 'exact';
  if (mode === 'tbd') return true;
  if (mode === 'monthyear') return !data.dateMY;
  return !data.date;
}
/* folio único de orden para reconciliar pagos (Zelle/Venmo) */
function genFolio() {return 'MXV-' + (1000 + Math.floor(Math.random() * 9000));}
function countdown(d, t) {
  if (!d) return null;
  const diff = new Date(`${d}T${t || '20:00'}:00`) - new Date();
  if (isNaN(diff) || diff <= 0) return null;
  return { days: Math.floor(diff / 86400000), hrs: Math.floor(diff % 86400000 / 3600000), min: Math.floor(diff % 3600000 / 60000) };
}
/* promo-aware totals */
function calcTotals(services) {
  const chosen = SERVICES.filter((s) => s.base || services.has(s.id));
  const gross = chosen.reduce((a, s) => a + s.price, 0);
  const freeQS = gross >= 400,freeSlide = gross >= 700;
  const freeIds = new Set();
  let discount = 0;
  chosen.forEach((s) => {
    if (freeQS && (s.id === 'qrvip' || s.id === 'seating')) {discount += s.price;freeIds.add(s.id);}
    if (freeSlide && s.id === 'slideshow') {discount += s.price;freeIds.add(s.id);}
  });
  return { gross, discount, total: gross - discount, freeIds, freeQS, freeSlide };
}

/* ============================================================
   PANEL DERECHO — invitación en vivo
   ============================================================ */
function Preview({ data, totals, step }) {
  const th = THEMES.find((t) => t.id === data.theme) || THEMES[0];
  const cd = (data.dateMode || 'exact') === 'exact' ? countdown(data.date, data.time) : null;
  const dateStr = eventDateLabel(data);
  const badges = SERVICES.filter((s) => !s.base && data.services.has(s.id)).map((s) => s.badge);
  const sec = data.sections;
  const pills = ['Eligiendo tus servicios', 'Capturando los datos', 'Eligiendo secciones', 'Aplicando la temática', 'Revisando tu orden', 'Lista para publicar'];
  const nameStyle = data.style === 'moderno' ?
  { fontFamily: "'Poppins', sans-serif", fontStyle: 'normal', fontWeight: 800, letterSpacing: '-0.02em', textTransform: 'uppercase', fontSize: 33 } : {};
  return (
    <div className="ow-screen" style={{ background: `radial-gradient(120% 80% at 50% 0%, ${th.accent}22, transparent 55%), linear-gradient(180deg, ${th.bg[0]}, ${th.bg[1]})` }}>
      <div className="ow-inv" style={{ color: th.text }}>
        {sec.hero && <div className="ow-inv-eyebrow" style={{ color: th.accent }}>Mis XV Años</div>}
        {sec.hero && <div className="ow-inv-name" style={{ color: th.text, ...nameStyle }}>{data.honoree || 'Tu Festejada'}</div>}
        {sec.hero && <div className="ow-inv-rule" style={{ background: th.accent }} />}
        {data.welcome && <div className="ow-inv-welcome" style={{ color: th.text }}>{data.welcome}</div>}
        {sec.hero && <div className="ow-inv-date" style={{ color: th.accent }}>{dateStr || 'Fecha por definir'}</div>}
        {sec.countdown && cd &&
        <div className="ow-inv-count">
            {[['días', cd.days], ['hrs', cd.hrs], ['min', cd.min]].map(([l, v]) =>
          <div className="ow-inv-count-u" key={l}>
                <div className="ow-inv-count-n" style={{ color: th.accent }}>{String(v).padStart(2, '0')}</div>
                <div className="ow-inv-count-l">{l}</div>
              </div>
          )}
          </div>
        }
        {sec.reception && (data.venue || data.city) &&
        <div style={{ marginTop: sec.countdown && cd ? 14 : 22 }}>
            {data.venue && <div className="ow-inv-venue" style={{ color: th.accent }}>{data.venue}</div>}
            {data.city && <div className="ow-inv-city">{data.city}</div>}
            {data.recepTime && <div className="ow-inv-city" style={{ marginTop: 2 }}>Recepción · {data.recepTime}</div>}
          </div>
        }
        {sec.parents && (data.mom || data.dad || data.padrinos) &&
        <div className="ow-inv-parents">
            <div style={{ fontSize: 9.5, letterSpacing: '0.2em', textTransform: 'uppercase', opacity: 0.7, marginBottom: 4 }}>Con la bendición de</div>
            {data.mom && <div className="nm">{data.mom}</div>}
            {data.mom && data.dad && <div style={{ opacity: 0.5, fontSize: 10 }}>&</div>}
            {data.dad && <div className="nm">{data.dad}</div>}
            {data.padrinos && <div style={{ fontSize: 10.5, opacity: 0.78, marginTop: data.mom || data.dad ? 6 : 0 }}>{data.padrinos}</div>}
          </div>
        }
        {sec.corte && data.corteNames &&
        <div className="ow-inv-parents" style={{ marginTop: 18 }}>
            <div style={{ fontSize: 9.5, letterSpacing: '0.2em', textTransform: 'uppercase', opacity: 0.7, marginBottom: 4 }}>Corte de Honor</div>
            <div style={{ whiteSpace: 'pre-line', fontSize: 11, opacity: 0.82 }}>{data.corteNames}</div>
          </div>
        }
        {sec.dress_code && data.dressCode && <div className="ow-inv-dress" style={{ color: th.accent, borderColor: th.accent }}>{data.dressCode}</div>}
        {badges.length > 0 &&
        <div className="ow-inv-badges" style={{ color: th.accent }}>
            {badges.map((b) => <span className="ow-inv-badge" key={b}>{b}</span>)}
          </div>
        }
        {sec.thanks && <div className="ow-inv-foot" style={{ color: th.text }}>Miss XV VIP</div>}
      </div>
      <div className="ow-inv-pill"><span className="ow-live-dot" />{pills[step]}</div>
    </div>);

}

/* ============================================================
   PANEL DERECHO — video demos (paso 1)
   ============================================================ */
function VideoShowcase({ data, set, toggleSvc, focus, setFocus, totals, activePkg }) {
  const [playing, setPlaying] = useState(false);
  const svc = SERVICES.find((s) => s.id === focus) || SERVICES[0];
  const isFree = totals.freeIds.has(svc.id);
  const on = svc.base || data.services.has(svc.id);
  const inPkg = !!activePkg && on && !svc.base;
  const toggle = () => {if (!svc.base) toggleSvc(svc.id);};
  React.useEffect(() => {setPlaying(false);}, [focus]);
  return (
    <div className="ow-video-wrap">
      <div className="ow-player">
        <div className="ow-player-poster" style={{ background: `radial-gradient(120% 100% at 50% 0%, ${svc.tint}55, transparent 60%), linear-gradient(160deg, ${svc.tint}26, #070a1e 82%)` }} />
        <div className="ow-player-badge">{svc.demoUrl ? 'En vivo ↑' : `Demo · ${svc.badge}`}</div>
        <div className="ow-player-price">{inPkg ? <span style={{ color: '#5bf07c' }}>Incluido</span> : isFree ? <span style={{ color: '#5bf07c' }}>GRATIS</span> : <>{svc.was && <span className="was">{USD(svc.was)}</span>}{USD(svc.price)}</>}</div>
        <div className="ow-player-controls">
          <Ic n={playing ? 'pause' : 'play'} s={13} style={{ color: '#fff' }} />
          <div className="barwrap"><i className={playing ? 'play' : ''} style={playing ? {} : { width: '0%' }} /></div>
          <span className="ow-player-time">{svc.demoUrl ? 'Invitación real · en vivo' : playing ? '0:04 / 0:18' : '0:18'}</span>
        </div>
      </div>

      <div>
        <div className="ow-vcap-name">{svc.name}</div>
        <div className="ow-vcap-desc">{svc.desc}</div>
        <div className="ow-vcap-row">
          <div className="ow-vcap-price">{inPkg ? <span className="free">Incluido en paquete</span> : isFree ? <span className="free">GRATIS · promo</span> : USD(svc.price)}</div>
          {svc.demoUrl &&
          <a className="ow-vcap-add on" href={svc.demoUrl} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none' }}><Ic n="play" s={15} />Ver invitación real</a>}
          <button className={`ow-vcap-add ${svc.base ? 'base' : on ? 'on' : 'off'}`} onClick={toggle}>
            {svc.base ? <><Ic n="check" s={15} />Incluida</> : on ? <><Ic n="check" s={15} />Agregado</> : <><Ic n="plus" s={15} />Agregar</>}
          </button>
        </div>
      </div>

      <div className="ow-thumbs">
        {SERVICES.map((s) => {
          const added = s.base || data.services.has(s.id);
          return (
            <button key={s.id} className={`ow-thumb ${focus === s.id ? 'active' : ''}`} onClick={() => setFocus(s.id)}
            style={{ background: `linear-gradient(150deg, ${s.tint}40, #0a0e2a)` }}>
              <Ic n={s.icon} s={16} />
              {added && <span className="chk"><Ic n="check" s={9} /></span>}
            </button>);

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

}

/* ============================================================
   helpers de form
   ============================================================ */
function Field({ label, opt, hint, children, full }) {
  return (
    <div className={`ow-field ${full ? 'col-2' : ''}`}>
      <label className="ow-label">{label}{opt && <span className="opt">· opcional</span>}</label>
      {children}
      {hint && <span className="ow-field-hint">{hint}</span>}
    </div>);

}
function AccountGate({ data, set }) {
  const emailOk = /.+@.+\..+/.test(data.cEmail);
  const passOk = data.cPass.length >= 8;
  if (data.auth) {
    const initial = (data.cName || data.cEmail || 'U').trim().charAt(0).toUpperCase();
    return (
      <div className="ow-acct">
        <div className="ow-acct-done">
          <div className="ow-acct-avatar">{initial}</div>
          <div className="ow-acct-done-body">
            <div className="ow-acct-done-name">{data.cName || 'Tu cuenta Miss XV VIP'}</div>
            <div className="ow-acct-done-mail">{data.cEmail}</div>
          </div>
          <span className="ow-acct-saved"><Ic n="check" s={12} />Progreso guardado</span>
          <button className="ow-acct-change" onClick={() => set({ auth: false, authMethod: null })}>Cambiar</button>
        </div>
      </div>);

  }
  return (
    <div className="ow-acct">
      <div className="ow-acct-top">
        <div className="ow-acct-ico"><Ic n="lock" s={20} /></div>
        <div>
          <div className="ow-acct-title">Crea tu cuenta para guardar tu progreso</div>
          <div className="ow-acct-desc">Continúa cuando quieras y, con la misma cuenta, accede a tu <strong>RSVP, invitados y álbum QR</strong>.</div>
        </div>
      </div>
      <button className="ow-google" onClick={() => set({ auth: true, authMethod: 'google', cEmail: data.cEmail || 'tucuenta@gmail.com', cName: data.cName || 'María Fernanda' })}>
        <GoogleG s={18} />Continuar con Google
      </button>
      <div className="ow-or">o</div>
      <div className="ow-acct-form">
        <div className="ow-field col-2">
          <label className="ow-label">Correo</label>
          <input className="ow-input" type="email" placeholder="tu@correo.com" value={data.cEmail} onChange={(e) => set({ cEmail: e.target.value })} />
        </div>
        <div className="ow-field col-2">
          <label className="ow-label">Contraseña <span className="opt">· mín. 8 caracteres</span></label>
          <input className="ow-input" type="password" placeholder="Crea tu contraseña" value={data.cPass} onChange={(e) => set({ cPass: e.target.value })} />
        </div>
        <button className="ow-acct-submit" disabled={!(emailOk && passOk)} onClick={() => set({ auth: true, authMethod: 'email' })}>
          <Ic n="check" s={15} />Crear cuenta y guardar progreso
        </button>
      </div>
      <div className="ow-acct-mini">¿Ya tienes cuenta? <span className="lk" onClick={() => set({ auth: true, authMethod: 'email', cEmail: data.cEmail || 'cliente@correo.com' })}>Inicia sesión</span></div>
    </div>);

}

/* ============================================================
   PASO 1 — cuenta + servicios à la carte + promos
   ============================================================ */
function StepServices({ data, set, totals, toggleSvc, focus, setFocus }) {
  const toggle = (s) => {if (!s.base) toggleSvc(s.id);};
  const toGo400 = Math.max(0, 400 - totals.gross);
  const toGo700 = Math.max(0, 700 - totals.gross);
  const activePkg = findPkg(data.services);
  return (
    <div className="ow-step">
      <h1 className="ow-step-title">Bienvenida, <em>Miss XV VIP</em> <span className="ow-spark">✨</span></h1>
      <p className="ow-step-sub">Aquí empieza tu historia. Elige y personaliza tu invitación paso a paso, a tu manera, con una vista previa en vivo mientras decides. Al confirmar tu orden, nuestro equipo produce la versión final —fondos, estilos y animación de intro— y te la entrega en 1 a 3 días. Nosotros nos encargamos del resto. ♡</p>

      <AccountGate data={data} set={set} />

      <p className="ow-section-label"><Ic n="zap" s={14} style={{ color: 'var(--gold)' }} />Empieza con un paquete <span className="hint">— o personaliza abajo</span></p>
      <div className="ow-pkgrow">
        {PKGS.map((p) => {
          const active = pkgActive(data.services, p);
          const t = p.price;
          const list = pkgListValue(p);
          const save = list - t;
          return (
            <button key={p.id} className={`ow-pkgcard ${active ? 'sel' : ''} ${p.popular ? 'pop' : ''}`} onClick={() => set({ services: new Set(p.items) })}>
              {p.popular && <span className="ow-pkgcard-pop">Más popular</span>}
              {active && <span className="ow-pkgcard-chk"><Ic n="check" s={13} /></span>}
              <div className="ow-pkgcard-ico"><Ic n={p.icon} s={20} /></div>
              <div className="ow-pkgcard-name">{p.name}</div>
              <div className="ow-pkgcard-tag">{p.tagline}</div>
              <div className="ow-pkgcard-price">{USD(t)}<small>USD</small>{save > 0 && <span className="ow-pkgcard-was">{USD(list)}</span>}</div>
              {save > 0 ? <div className="ow-pkgcard-save"><Ic n="zap" s={11} />Ahorras {USD(save)}</div> : <div className="ow-pkgcard-save flat">Precio base</div>}
            </button>);

        })}
      </div>

      {activePkg ?
      <div className="ow-promos">
          <div className="ow-promo unlocked">
            <div className="ow-promo-ico"><Ic n="check" s={17} /></div>
            <div className="ow-promo-body">
              <div className="ow-promo-txt"><strong>Paquete {activePkg.name} seleccionado</strong> · {activePkg.items.length + 1} {activePkg.items.length + 1 === 1 ? 'servicio' : 'servicios'} por <strong>{USD(activePkg.price)} USD</strong>. Agrega o quita abajo para personalizarlo.</div>
            </div>
          </div>
        </div> :

      <div className="ow-promos">
        <div className={`ow-promo ${totals.freeQS ? 'unlocked' : 'locked'}`}>
          <div className="ow-promo-ico"><Ic n={totals.freeQS ? 'check' : 'gift'} s={17} /></div>
          <div className="ow-promo-body">
            <div className="ow-promo-txt">{totals.freeQS ?
              <><strong>¡Desbloqueado!</strong> QR-VIP y Seating <strong>GRATIS</strong> en tu orden.</> :
              <>Agrega <strong>{USD(toGo400)}</strong> más y llévate <strong>QR-VIP + Seating gratis</strong> ($400+).</>}</div>
            {!totals.freeQS && <div className="ow-promo-bar"><i style={{ width: `${Math.min(100, totals.gross / 400 * 100)}%` }} /></div>}
          </div>
        </div>
        <div className={`ow-promo ${totals.freeSlide ? 'unlocked' : 'locked'}`}>
          <div className="ow-promo-ico"><Ic n={totals.freeSlide ? 'check' : 'zap'} s={17} /></div>
          <div className="ow-promo-body">
            <div className="ow-promo-txt">{totals.freeSlide ?
              <><strong>¡Desbloqueado!</strong> VIP Slideshow <strong>GRATIS</strong> en tu orden.</> :
              <>Llega a <strong>$700</strong> y el <strong>VIP Slideshow</strong> es gratis — te faltan <strong>{USD(toGo700)}</strong>.</>}</div>
            {!totals.freeSlide && <div className="ow-promo-bar"><i style={{ width: `${Math.min(100, totals.gross / 700 * 100)}%` }} /></div>}
          </div>
        </div>
      </div>
      }

      <p className="ow-section-label"><Ic n="sparkle" s={14} style={{ color: 'var(--gold)' }} />O personaliza servicio por servicio <span className="hint">— toca para ver el demo, + para agregar</span></p>
      <div className="ow-svclist">
        {SERVICES.map((s) => {
          const on = s.base || data.services.has(s.id);
          const free = totals.freeIds.has(s.id);
          return (
            <div key={s.id} className={`ow-svc ${on ? 'on' : ''} ${s.base ? 'base' : ''} ${focus === s.id ? 'focus' : ''}`}
            onMouseEnter={() => setFocus(s.id)} onClick={() => setFocus(s.id)}>
              <div className="ow-svc-ico"><Ic n={s.icon} s={19} /></div>
              <div className="ow-svc-body">
                <div className="ow-svc-name">{s.name}{s.popular && <span className="ow-svc-pop">Más popular</span>}<span className="ow-svc-play"><Ic n="play" s={9} />Demo</span></div>
                <div className="ow-svc-desc">{s.short} · {s.base ? 'siempre incluida' : s.desc}</div>
              </div>
              <div className="ow-svc-right">
                <div className="ow-svc-price">
                  {activePkg && on ? <span className="free">Incluido</span> : <>
                    {s.was && !free && <span className="was">{USD(s.was)}</span>}
                    {free ? <span className="free">GRATIS</span> : USD(s.price)}
                  </>}
                </div>
                <button className="ow-svc-add" onClick={(e) => {e.stopPropagation();toggle(s);}} aria-label="Agregar">
                  <Ic n={on ? 'check' : 'plus'} s={15} />
                </button>
              </div>
            </div>);

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

}

function StepHonoree({ data, set }) {
  const mode = data.dateMode || 'exact';
  return (
    <div className="ow-step">
      <h1 className="ow-step-title">Datos del <em>evento</em></h1>
      <p className="ow-step-sub">Cuéntanos lo esencial para comenzar a crear tu invitación.</p>
      <div className="ow-grid ow-grid-2">
        <Field label="Nombre de la festejada" full><input className="ow-input display" placeholder="Ej. María Fernanda" value={data.honoree} onChange={(e) => set({ honoree: e.target.value })} /></Field>
        <Field label="Fecha del evento" full hint="¿Aún no confirmas el día? Elige solo mes y año, o déjala por confirmar — no es obligatoria.">
          <div className="ow-datemode">
            {[['exact', 'Fecha exacta'], ['monthyear', 'Mes y año'], ['tbd', 'Por confirmar']].map(([m, lbl]) =>
            <button key={m} type="button" className={`ow-dm ${mode === m ? 'on' : ''}`} onClick={() => set({ dateMode: m })}>{lbl}</button>
            )}
          </div>
          {mode === 'exact' &&
          <div className="ow-date-row">
              <input className="ow-input" type="date" value={data.date} onChange={(e) => set({ date: e.target.value })} />
              <input className="ow-input" type="time" value={data.time} onChange={(e) => set({ time: e.target.value })} aria-label="Hora" />
            </div>
          }
          {mode === 'monthyear' &&
          <input className="ow-input" type="month" value={data.dateMY} onChange={(e) => set({ dateMY: e.target.value })} />
          }
          {mode === 'tbd' &&
          <div className="ow-date-tbd"><Ic n="clock" s={15} />Tu invitación dirá <strong>“Fecha por confirmar”</strong>. Podrás fijar el día después desde tu panel.</div>
          }
        </Field>
        <Field label="Lugar / Salón" opt><input className="ow-input" placeholder="Ej. The Grand Ballroom" value={data.venue} onChange={(e) => set({ venue: e.target.value })} /></Field>
        <Field label="Ciudad" opt><input className="ow-input" placeholder="Ej. Houston, TX" value={data.city} onChange={(e) => set({ city: e.target.value })} /></Field>
      </div>
      <div className="ow-divider" />
      <p className="ow-subhead"><Ic n="heart" s={15} />Papás y padrinos <span style={{ color: 'var(--text-4)', fontWeight: 500 }}>· opcional</span></p>
      <div className="ow-grid ow-grid-2">
        <Field label="Mamá" opt><input className="ow-input" placeholder="Nombre de mamá" value={data.mom} onChange={(e) => set({ mom: e.target.value })} /></Field>
        <Field label="Papá" opt><input className="ow-input" placeholder="Nombre de papá" value={data.dad} onChange={(e) => set({ dad: e.target.value })} /></Field>
        <Field label="Padrinos" opt full><input className="ow-input" placeholder="Ej. Eduardo Valdés y Lucía Méndez" value={data.padrinos} onChange={(e) => set({ padrinos: e.target.value })} /></Field>
      </div>
    </div>);

}

/* secciones cuyo contenido se arma después en el panel (no se captura en la compra) */
const SEC_PANEL_NOTE = {
  itinerary: 'Arma el programa del día (horarios y momentos) en tu panel.',
  gallery: 'Sube tus fotos (hasta 6) desde tu panel después de comprar.',
  music: 'Elige la pista de la biblioteca o sube la tuya en tu panel.',
  qrvip: 'El álbum colaborativo se activa y configura en tu panel.',
  video_intro: 'La animación de apertura es preparada por nuestro equipo según el estilo de tu evento.'
};

function SecInlineFields({ id, data, set }) {
  if (id === 'reception') return (
    <div className="ow-grid ow-grid-2">
      <Field label="Dirección del salón" full><input className="ow-input" placeholder="Ej. 2700 SW Freeway, Houston, TX 77098" value={data.recepAddr} onChange={(e) => set({ recepAddr: e.target.value })} /></Field>
      <Field label="Hora de recepción"><input className="ow-input" type="time" value={data.recepTime} onChange={(e) => set({ recepTime: e.target.value })} /></Field>
      <Field label="Enlace de Google Maps" opt><input className="ow-input" placeholder="https://maps.app.goo.gl/…" value={data.recepMaps} onChange={(e) => set({ recepMaps: e.target.value })} /></Field>
    </div>);

  if (id === 'registry') return (
    <Field label="Datos o links de tu registro" full><textarea className="ow-input" placeholder="Ej. Liverpool #1234567 · Amazon: https://…&#10;Transferencia: CLABE 0123…" value={data.registry} onChange={(e) => set({ registry: e.target.value })} /></Field>);

  if (id === 'corte') return (
    <Field label="Nombres de chambelanes y damas" full><textarea className="ow-input" placeholder="Ej. Damas: Sofía, Ana, Regina…&#10;Chambelanes: Diego, Luis, Mateo…" value={data.corteNames} onChange={(e) => set({ corteNames: e.target.value })} /></Field>);

  if (id === 'dress_code') return (
    <Field label="Código de vestimenta" full><input className="ow-input" placeholder="Ej. Etiqueta formal · Vestido largo" value={data.dressCode} onChange={(e) => set({ dressCode: e.target.value })} /></Field>);

  if (id === 'rsvp') return (
    <Field label="Fecha límite de confirmación" opt full><input className="ow-input" type="date" value={data.rsvpDeadline} onChange={(e) => set({ rsvpDeadline: e.target.value })} /></Field>);

  if (id === 'hero') return (
    <Field label="Frase de bienvenida" opt full><input className="ow-input" placeholder="Ej. Acompáñame a celebrar mis XV años" value={data.welcome} onChange={(e) => set({ welcome: e.target.value })} /></Field>);

  if (id === 'map') return (
    <Field label="Enlace de Google Maps" opt full><input className="ow-input" placeholder="https://maps.app.goo.gl/…" value={data.recepMaps} onChange={(e) => set({ recepMaps: e.target.value })} /></Field>);

  if (SEC_PANEL_NOTE[id]) return (
    <div className="ow-sec-note"><Ic n="info" s={14} />{SEC_PANEL_NOTE[id]}</div>);

  return null;
}
const SEC_HAS_FIELDS = (id) => ['reception', 'registry', 'corte', 'dress_code', 'rsvp', 'hero', 'map'].includes(id) || !!SEC_PANEL_NOTE[id];

function StepSections({ data, set, toggleSec }) {
  const lang = data.lang;
  const langOpts = [['es', 'Español'], ['en', 'Inglés'], ['bi', 'Bilingüe ES/EN']];
  return (
    <div className="ow-step">
      <h1 className="ow-step-title">Secciones de <em>tu invitación</em></h1>
      <p className="ow-step-sub">Elige qué deseas incluir. Puedes completar los detalles ahora o después desde tu panel.</p>

      <div className="ow-seclist">
        {SECTIONS.map((sec) => {
          const on = data.sections[sec.id];
          const expand = on && SEC_HAS_FIELDS(sec.id);
          return (
            <div key={sec.id} className={`ow-sec-wrap ${on ? 'on' : ''}`}>
              <button className={`ow-sec ${on ? 'on' : ''}`} onClick={() => toggleSec(sec.id)}>
                <div className="ow-sec-ico"><Ic n={sec.icon} s={17} /></div>
                <div className="ow-sec-body">
                  <div className="ow-sec-name">{sec.name}</div>
                  <div className="ow-sec-desc">{sec.desc}</div>
                </div>
                <span className="ow-tgl" />
              </button>
              {expand &&
              <div className="ow-sec-panel">
                  <SecInlineFields id={sec.id} data={data} set={set} />
                </div>
              }
            </div>);

        })}
      </div>

      <div className="ow-divider" />
      <Field label="Idioma de la invitación">
        <div className="ow-lang">
          {langOpts.map(([v, l]) =>
          <button key={v} className={`ow-lang-opt ${lang === v ? 'on' : ''}`} onClick={() => set({ lang: v })}><Ic n="globe" s={13} />{l}</button>
          )}
        </div>
      </Field>
    </div>);

}

function StepTheme({ data, set }) {
  return (
    <div className="ow-step">
      <h1 className="ow-step-title">Elige el estilo de <em>tu invitación</em></h1>
      <p className="ow-step-sub">Selecciona la opción que mejor combine con tu evento. Verás una vista previa del estilo elegido.</p>
      <div className="ow-themes">
        {THEMES.map((t) =>
        <button key={t.id} className={`ow-theme ${data.theme === t.id ? 'sel' : ''}`} onClick={() => set({ theme: t.id })}>
            <div className="ow-theme-check"><Ic n="check" s={14} /></div>
            <div className="ow-theme-swatch" style={{ background: `radial-gradient(120% 90% at 50% 0%, ${t.accent}33, transparent 55%), linear-gradient(180deg, ${t.bg[0]}, ${t.bg[1]})` }}>
              <span style={{ color: t.accent }}>{(data.honoree || 'Aa').slice(0, 2)}</span>
            </div>
            <div className="ow-theme-info">
              <div><div className="ow-theme-name">{t.name}</div><div className="ow-theme-mood">{t.mood}</div></div>
              <div className="ow-theme-dots">{t.dots.map((c, i) => <span className="ow-theme-dot" key={i} style={{ background: c }} />)}</div>
            </div>
          </button>
        )}
      </div>
      <p className="ow-section-label" style={{ marginTop: 22 }}><Ic n="edit" s={14} style={{ color: 'var(--gold)' }} />Estilo de tipografía</p>
      <div className="ow-style-row" style={{ marginTop: 0 }}>
        <button className={`ow-chip ${data.style === 'clasico' ? 'on' : ''}`} onClick={() => set({ style: 'clasico' })}>Clásico<small>Serif elegante</small></button>
        <button className={`ow-chip ${data.style === 'moderno' ? 'on' : ''}`} onClick={() => set({ style: 'moderno' })}>Moderno<small>Sans en altas</small></button>
      </div>
    </div>);

}

function StepSummary({ data, totals, goTo, activePkg, orderTotal }) {
  const th = THEMES.find((t) => t.id === data.theme);
  const lines = SERVICES.filter((s) => s.base || data.services.has(s.id));
  const recap = [
  { lbl: 'Festejada', ico: 'user', val: data.honoree },
  { lbl: 'Fecha', ico: 'calendar', val: eventDateLabel(data), pending: dateIsPending(data) },
  { lbl: 'Lugar', ico: 'mappin', val: data.venue && data.city ? `${data.venue} · ${data.city}` : data.venue || data.city },
  { lbl: 'Temática', ico: 'palette', val: th && th.name }];

  const langName = { es: 'Español', en: 'Inglés', bi: 'Bilingüe ES/EN' }[data.lang];
  const activeSecs = SECTIONS.filter((s) => secAvailable(s, data.services) && data.sections[s.id]);
  const pendingData = {
    reception: !(data.recepAddr || data.venue),
    registry: !data.registry,
    corte: !data.corteNames,
    dress_code: !data.dressCode,
    parents: !(data.mom || data.dad || data.padrinos),
    map: !data.recepMaps,
    itinerary: true, gallery: true, music: true, video_intro: true
  };
  const secPending = (id) => !!pendingData[id];
  const pendingCount = activeSecs.filter((s) => secPending(s.id)).length;
  return (
    <div className="ow-step">
      <h1 className="ow-step-title">Resumen de <em>tu orden</em></h1>
      <p className="ow-step-sub">Revisa la información antes de pagar. Puedes volver a cualquier paso para hacer ajustes.</p>
      <div className="ow-event-recap">
        {recap.map((r) =>
        <div className={`ow-recap-card ${r.pending && r.val ? 'pending' : ''}`} key={r.lbl}>
            <div className="ow-recap-lbl"><Ic n={r.pending && r.val ? 'clock' : r.ico} s={12} />{r.lbl}</div>
            <div className={`ow-recap-val ${r.val ? '' : 'empty'}`}>{r.val || 'Por definir'}</div>
            {r.pending && r.val && <div className="ow-recap-note">Pendiente de afinar — lo actualizas en tu panel</div>}
          </div>
        )}
      </div>
      <div className="ow-recap-secs">
        <div className="ow-recap-lbl"><Ic n="sparkle" s={12} />Secciones de la invitación · <span style={{ textTransform: 'none', letterSpacing: 0, color: 'var(--text-2)' }}>Idioma: {langName}</span></div>
        <div className="ow-recap-chips">
          {activeSecs.map((s) => {
            const pend = secPending(s.id);
            return <span className={`ow-recap-chip ${pend ? 'pending' : ''}`} key={s.id}><Ic n={pend ? 'clock' : s.icon} s={11} />{s.name}</span>;
          })}
        </div>
        {pendingCount > 0 &&
        <div className="ow-recap-pending"><Ic n="info" s={13} />{pendingCount} {pendingCount === 1 ? 'sección encendida' : 'secciones encendidas'} con datos pendientes — los podrás completar o actualizar después en tu panel. No bloquea tu compra.</div>
        }
      </div>
      <div className="ow-summary">
        {activePkg &&
        <div className="ow-line">
            <div className="ow-line-ico gold"><Ic n={activePkg.icon} s={17} /></div>
            <div className="ow-line-body">
              <div className="ow-line-name">Paquete {activePkg.name}</div>
              <div className="ow-line-meta">{lines.length} {lines.length === 1 ? 'servicio incluido' : 'servicios incluidos'}</div>
            </div>
            <div className="ow-line-price">{USD(activePkg.price)}</div>
          </div>
        }
        {lines.map((s) => {
          const free = totals.freeIds.has(s.id);
          if (activePkg) {
            return (
              <div className="ow-line" key={s.id}>
                <div className={`ow-line-ico ${s.base ? 'gold' : ''}`}><Ic n={s.icon} s={17} /></div>
                <div className="ow-line-body">
                  <div className="ow-line-name">{s.name}</div>
                  <div className="ow-line-meta">{s.short}</div>
                </div>
                <div className="ow-line-price free">Incluido</div>
              </div>);

          }
          return (
            <div className="ow-line" key={s.id}>
              <div className={`ow-line-ico ${s.base ? 'gold' : ''}`}><Ic n={s.icon} s={17} /></div>
              <div className="ow-line-body">
                <div className="ow-line-name">{s.name}</div>
                <div className="ow-line-meta">{s.base ? 'Invitación base · incluida' : free ? 'Servicio · gratis por promoción' : s.short}</div>
              </div>
              <div className={`ow-line-price ${free ? 'free' : ''}`}>{free ? 'GRATIS' : USD(s.price)}</div>
            </div>);

        })}
        <div className="ow-totals">
          {!activePkg && <div className="ow-total-row"><span>Subtotal</span><span className="num">{USD(totals.gross)}</span></div>}
          {!activePkg && totals.discount > 0 && <div className="ow-total-row" style={{ color: 'var(--success-2)' }}><span>Descuento promociones</span><span className="num">− {USD(totals.discount)}</span></div>}
          {data.lang === 'bi' && <div className="ow-total-row"><span>Bilingüe EN/ES</span><span className="num">+ {USD(BILINGUAL_PRICE)}</span></div>}
          <div className="ow-total-row grand"><span>Total</span><span className="num">{USD(orderTotal)} <span style={{ fontSize: 12, color: 'var(--text-3)', fontWeight: 500 }}>USD</span></span></div>
          {activePkg && pkgListValue(activePkg) - activePkg.price > 0 &&
          <div className="ow-total-row" style={{ color: 'var(--success-2)', fontWeight: 700, marginTop: 8 }}><span>Ahorras vs. precio de lista</span><span className="num">{USD(pkgListValue(activePkg) - activePkg.price)}</span></div>
          }
        </div>
      </div>
      <button className="ow-edit-link" onClick={() => goTo(0)}><Ic n="edit" s={13} />Editar servicios</button>
    </div>);

}

function StepPay({ data, set, totals, orderTotal }) {
  const detail = data.payMethod === 'zelle' || data.payMethod === 'venmo' ? PAY[data.payMethod] : null;
  const isCard = data.payMethod === 'card';
  const charge = cardCharge(orderTotal);
  const fee = Math.round((charge - orderTotal) * 100) / 100;
  // Comprobante como data URL en el borrador (sin backend), con preview.
  const onReceipt = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    fileToDataURL(f, (url) => set({ receiptName: f.name, receiptData: url }));
    e.target.value = '';
  };
  return (
    <div className="ow-step">
      <h1 className="ow-step-title">Confirma tu <em>orden</em></h1>
      <p className="ow-step-sub">Elige tu método de pago para completar la orden. Nuestro equipo preparará tu invitación en 1 a 3 días hábiles.</p>

      <div className="ow-pay-note">
        <Ic n="check" s={18} />
        <div className="ow-pay-note-txt"><strong>Sesión iniciada{data.cEmail ? <> como {data.cEmail}</> : ''}.</strong> Tu progreso está guardado — vuelve a tu panel de RSVP, invitados y álbum QR cuando quieras.</div>
      </div>

      <p className="ow-subhead"><Ic n="shield" s={15} />Elige tu método de pago</p>
      <div className="ow-pay-methods col">
        <button className={`ow-pm featured ${isCard ? 'on' : ''}`} onClick={() => set({ payMethod: 'card' })}>
          <div className="ow-pm-logo card"><Ic n="card" s={18} /></div>
          <div className="ow-pm-main"><div className="ow-pm-name">Tarjeta de crédito o débito{STRIPE_ACTIVE && <span className="ow-pm-reco">Recomendado</span>}</div><div className="ow-pm-sub ok">Pago en línea seguro · confirmación inmediata</div></div>
          <span className="ow-pm-check"><Ic n="check" s={13} /></span>
        </button>
        <div className="ow-pm-pair">
          <button className={`ow-pm ${data.payMethod === 'zelle' ? 'on' : ''}`} onClick={() => set({ payMethod: 'zelle' })}>
            <div className="ow-pm-logo zelle">Z</div>
            <div className="ow-pm-main"><div className="ow-pm-name">Zelle{!STRIPE_ACTIVE && <span className="ow-pm-reco">Recomendado</span>}</div><div className="ow-pm-sub ok">Sin comisión ni impuestos · confirmación después de verificar tu pago</div></div>
          </button>
          <button className={`ow-pm ${data.payMethod === 'venmo' ? 'on' : ''}`} onClick={() => set({ payMethod: 'venmo' })}>
            <div className="ow-pm-logo venmo">V</div>
            <div className="ow-pm-main"><div className="ow-pm-name">Venmo</div><div className="ow-pm-sub">Sin comisión · confirmación después de verificar tu pago</div></div>
          </button>
        </div>
      </div>

      {isCard &&
      <>
          <div className="ow-pay-detail">
            <div className="ow-pay-detail-row"><span className="ow-pay-detail-k">Subtotal</span><span className="ow-pay-detail-v">{USD2(orderTotal)}</span></div>
            <div className="ow-pay-detail-row"><span className="ow-pay-detail-k">Comisión de procesamiento</span><span className="ow-pay-detail-v">{USD2(fee)}</span></div>
            <div className="ow-pay-detail-row"><span className="ow-pay-detail-k">Total con tarjeta</span><span className="ow-pay-detail-v big">{USD2(charge)} USD</span></div>
          </div>
          <div className="ow-pay-feenote"><Ic n="info" s={13} />Las transferencias (Zelle/Venmo) no tienen comisión y mantienen el total en {USD2(orderTotal)}.</div>
          <div className="ow-stripe-note"><Ic n="lock" s={14} />Te llevamos al checkout seguro de <strong>Stripe</strong> para completar tu pago. No guardamos los datos de tu tarjeta.</div>
          <a className="ow-wa-btn secondary" href={waLink(`¡Hola! Ya pagué mi Orden #${data.orderId} con tarjeta. Quedo al pendiente de los siguientes pasos.`)} target="_blank" rel="noopener noreferrer">
            <Ic n="message" s={16} />Escríbenos por WhatsApp
          </a>
        </>
      }

      {detail &&
      <>
          <div className="ow-pay-detail">
            <div className="ow-pay-detail-row">
              <span className="ow-pay-detail-k">Envía a ({data.payMethod === 'zelle' ? 'Zelle' : 'Venmo'})</span>
              <span className="ow-pay-detail-v">{detail.handle}<CopyChip text={detail.handle} /></span>
            </div>
            {data.payMethod === 'zelle' && detail.titular &&
            <div className="ow-pay-detail-row">
              <span className="ow-pay-detail-k">Titular Zelle</span>
              <span className="ow-pay-detail-v">{detail.titular}</span>
            </div>}
            <div className="ow-pay-detail-row">
              <span className="ow-pay-detail-k">Monto exacto</span>
              <span className="ow-pay-detail-v big">{USD(orderTotal)} USD</span>
            </div>
            <div className="ow-pay-detail-row">
              <span className="ow-pay-detail-k">Concepto / referencia</span>
              <span className="ow-pay-detail-v">Orden #{data.orderId}<CopyChip text={`Orden #${data.orderId}`} /></span>
            </div>
          </div>

          <div className="ow-grid ow-grid-2">
            <Field label="Nombre del titular" full><input className="ow-input" placeholder="¿A nombre de quién va la orden?" value={data.cName} onChange={(e) => set({ cName: e.target.value })} /></Field>
            <Field label="WhatsApp" full hint="Te escribiremos por WhatsApp para confirmar tu pago. Incluye el código de país (ej. +52, +1)."><input className="ow-input" type="tel" placeholder="+1 305 123 4567" value={data.cPhone} onChange={(e) => set({ cPhone: e.target.value })} /></Field>
          </div>

          <div className="ow-separator-lbl"><span>Sube tu comprobante</span></div>
          {data.receiptData ? (
          <div className="ow-receipt done">
            {data.receiptData.startsWith('data:image')
              ? <img src={data.receiptData} alt="Comprobante" className="ow-receipt-thumb" />
              : <div className="ow-receipt-ico done"><Ic n="check" s={18} /></div>}
            <div className="ow-receipt-body">
              <div className="ow-receipt-t">Comprobante adjunto</div>
              <div className="ow-receipt-d">{data.receiptName}</div>
            </div>
            <div className="ow-receipt-actions">
              <label className="ow-receipt-act">
                <input type="file" accept="image/*,application/pdf" style={{ display: 'none' }} onChange={onReceipt} />
                Reemplazar
              </label>
              <button type="button" className="ow-receipt-act danger" onClick={() => set({ receiptName: '', receiptData: '' })}>Quitar</button>
            </div>
          </div>
          ) : (
          <label className="ow-receipt">
            <input type="file" accept="image/*,application/pdf" style={{ display: 'none' }} onChange={onReceipt} />
            <div className="ow-receipt-ico"><Ic n="image" s={18} /></div>
            <div className="ow-receipt-body"><div className="ow-receipt-t">Adjunta tu comprobante de Zelle o Venmo</div><div className="ow-receipt-d">Opcional · ayuda a verificar tu pago más rápido</div></div>
          </label>
          )}

          <a className="ow-wa-btn" href={waLink(`¡Hola! Acabo de pagar mi Orden #${data.orderId} por ${USD(orderTotal)} USD vía ${data.payMethod === 'venmo' ? 'Venmo' : 'Zelle'}. Aquí les envío mi comprobante.`)} target="_blank" rel="noopener noreferrer">
            <Ic n="message" s={17} />Enviar comprobante por WhatsApp
          </a>

          <div className="ow-wa-note">
            <Ic n="message" s={18} />
            <div className="ow-wa-note-txt"><strong>Verificaremos tu pago y te confirmaremos por WhatsApp.</strong> Después, nuestro equipo comenzará a preparar tu invitación.</div>
          </div>
        </>
      }
    </div>);

}

function Paid({ data, totals, orderTotal }) {
  const isCard = data.payMethod === 'card';
  const paidAmount = isCard ? cardCharge(orderTotal) : orderTotal;
  const payDetail = (data.payMethod === 'zelle' || data.payMethod === 'venmo') ? PAY[data.payMethod] : null;
  const payLabel = data.payMethod === 'venmo' ? 'Venmo' : 'Zelle';
  const cuestionarioUrl = (typeof window !== 'undefined' && window.__OW_CUESTIONARIO_URL) ? window.__OW_CUESTIONARIO_URL : '/cuestionario/';
  const missing = [
  { ok: !dateIsPending(data), label: 'Fecha exacta del evento' },
  { ok: !!(data.venue && data.recepAddr), label: 'Lugar y dirección de recepción' },
  { ok: !!(data.mom || data.dad || data.padrinos), label: 'Nombres de papás / padrinos' },
  { ok: !data.sections.dress_code || !!data.dressCode, label: 'Código de vestimenta' },
  { ok: !data.sections.corte || !!data.corteNames, label: 'Corte de honor' }];

  const pend = missing.filter((m) => !m.ok);
  return (
    <div className="ow-step" style={{ textAlign: 'center', maxWidth: 470, margin: '20px auto 0' }}>
      <div className="ow-acct-avatar" style={{ margin: '0 auto 18px', width: 64, height: 64, fontSize: 28 }}><Ic n={isCard ? 'check' : 'clock'} s={30} /></div>
      <h1 className="ow-step-title" style={{ fontSize: 30 }}>{isCard ? <>¡Pago <em>confirmado</em>!</> : <>¡Orden <em>recibida</em>!</>}</h1>
      <div className="ow-folio-chip">Orden #{data.orderId}</div>
      <p className="ow-step-sub" style={{ margin: '10px auto 18px' }}>
        {isCard ?
        <>Gracias{data.cName ? `, ${data.cName.split(' ')[0]}` : ''}. Tu pago de <strong style={{ color: 'var(--gold-2)' }}>{USD2(paidAmount)} USD</strong> con tarjeta quedó confirmado. Nuestro equipo comenzará a preparar tu invitación y te la enviamos por WhatsApp{data.cPhone ? <> al {data.cPhone}</> : ''} en <strong style={{ color: 'var(--text)' }}>1 a 3 días hábiles</strong>.</> :
        <>Gracias{data.cName ? `, ${data.cName.split(' ')[0]}` : ''}. Registramos tu orden con <strong style={{ color: 'var(--text)' }}>{payLabel}</strong> como método de pago. Sigue los pasos de abajo para que tu orden avance.</>}
      </p>
      {!isCard && payDetail &&
      <div className="ow-pay-detail" style={{ textAlign: 'left', marginBottom: 16 }}>
        <div className="ow-pay-detail-row" style={{ alignItems: 'flex-start' }}>
          <span className="ow-pay-detail-k">Monto a enviar</span>
          <span className="ow-pay-detail-v"><strong style={{ color: 'var(--gold-2)', fontSize: 17 }}>{USD2(orderTotal)} USD</strong></span>
        </div>
        <div className="ow-pay-detail-row">
          <span className="ow-pay-detail-k">Envía a ({payLabel})</span>
          <span className="ow-pay-detail-v">{payDetail.handle}<CopyChip text={payDetail.handle} /></span>
        </div>
        {data.payMethod === 'zelle' && payDetail.titular &&
        <div className="ow-pay-detail-row">
          <span className="ow-pay-detail-k">Titular Zelle</span>
          <span className="ow-pay-detail-v">{payDetail.titular}</span>
        </div>}
        <div className="ow-wa-note" style={{ textAlign: 'left', marginTop: 12 }}>
          <Ic n="info" s={18} />
          <div className="ow-wa-note-txt"><strong>¿Ya enviaste tu pago?</strong> Si todavía no, envía <strong style={{ color: 'var(--gold-2)' }}>{USD2(orderTotal)} USD</strong> por {payLabel} ahora — tu orden no avanza hasta recibir el pago.</div>
        </div>
      </div>}

      {/* CTA principal — above the fold, elemento dominante: empieza el formulario */}
      <a className="ow-paid-cta" href={cuestionarioUrl}>
        <span className="ow-paid-cta-main"><Ic n="edit" s={18} />Empieza el formulario de tu invitación</span>
        <span className="ow-paid-cta-sub">{pend.length > 0 ? `Te faltan ${pend.length} ${pend.length === 1 ? 'dato' : 'datos'} por completar` : 'Llena los datos de tu invitación'} <Ic n="chevright" s={16} /></span>
      </a>

      {!isCard &&
      <div className="ow-checklist" style={{ textAlign: 'left', marginBottom: 16 }}>
        <div className="ow-checklist-head"><Ic n="info" s={15} /><span>Próximos pasos</span></div>
        <ol className="ow-next-steps" style={{ margin: 0, padding: '4px 0 0', listStyle: 'none', counterReset: 'ows' }}>
          {[`Envía tu pago por ${payLabel} si todavía no lo hiciste`,
            'Empieza el formulario de tu invitación (botón de arriba)',
            'Verificamos tu pago y te confirmamos por WhatsApp',
            'Preparamos y entregamos tu invitación en 1 a 3 días'].map((t, i) =>
          <li key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: 10, padding: '6px 0' }}>
            <span style={{ flex: '0 0 auto', width: 22, height: 22, borderRadius: '50%', background: 'var(--gold-soft)', color: 'var(--gold-2)', border: '1px solid var(--gold-line)', display: 'grid', placeItems: 'center', fontSize: 12, fontWeight: 700 }}>{i + 1}</span>
            <span style={{ fontSize: 13.5, lineHeight: 1.4, color: 'var(--text-2)', paddingTop: 2 }}>{t}</span>
          </li>)}
        </ol>
      </div>}
      {isCard &&
      <div className="ow-wa-note" style={{ textAlign: 'left' }}>
        <Ic n="message" s={18} />
        <div className="ow-wa-note-txt">Recibirás el acceso a tu panel {data.cEmail ? <>en <strong>{data.cEmail}</strong></> : ''} para ver el avance, tus RSVP y tu álbum QR.</div>
      </div>}
      {pend.length > 0 &&
      <div className="ow-checklist">
          <div className="ow-checklist-head"><Ic n="info" s={15} /><span>{isCard ? <>Datos que faltan para que quede perfecta</> : <>Datos pendientes de tu invitación</>}</span></div>
          <div className="ow-checklist-items">
            {missing.map((m) =>
          <div className={`ow-checklist-item ${m.ok ? 'ok' : ''}`} key={m.label}>
                <span className="ow-checklist-mark"><Ic n={m.ok ? 'check' : 'plus'} s={12} /></span>{m.label}
              </div>
          )}
          </div>
        </div>
      }
    </div>);

}

/* ============================================================
   APP
   ============================================================ */
const DRAFT_KEY = 'vipinv_wizard_draft';
// TOS-ORD-013: id de sesion por pestana (sessionStorage). El draft se restaura SOLO si su sid
// coincide con el de esta sesion -> una visita/persona distinta en el mismo navegador no
// autocompleta el borrador de otra. Refresh de la misma pestana si resume (sessionStorage persiste).
function wizSid() {
  try {
    let s = sessionStorage.getItem('vipinv_wizard_sid');
    if (!s) { s = Date.now().toString(36) + Math.random().toString(36).slice(2); sessionStorage.setItem('vipinv_wizard_sid', s); }
    return s;
  } catch (e) { return ''; }
}
const DEFAULT_DATA = {
  services: new Set(),
  honoree: '', date: '', time: '20:00', dateMode: 'exact', dateMY: '', venue: '', city: '', mom: '', dad: '', padrinos: '',
  theme: 'esmeralda', style: 'clasico', orderId: genFolio(),
  sections: { video_intro: true, hero: true, countdown: true, parents: true, ceremony: true, reception: true, itinerary: false, dress_code: true, registry: false, corte: false, gallery: false, music: false, qrvip: true, rsvp: true, guestbook: true, thanks: true, map: true },
  recepAddr: '', recepTime: '', recepMaps: '', registry: '', corteNames: '', dressCode: '', welcome: '', rsvpDeadline: '', lang: 'es',
  cName: '', cEmail: '', cPass: '', cPhone: '', receiptName: '', receiptData: '',
  auth: false, authMethod: null, payMethod: null
};
function loadDraft() {
  try {
    const raw = localStorage.getItem(DRAFT_KEY);
    if (!raw) return null;
    const p = JSON.parse(raw);
    if (!p || !p.data) return null;
    if (p.sid && p.sid !== wizSid()) return null; // TOS-ORD-013: borrador de otra sesion/persona -> no autocompletar
    const merged = { ...DEFAULT_DATA, ...p.data, sections: { ...DEFAULT_DATA.sections, ...(p.data.sections || {}) } };
    merged.services = new Set(Array.isArray(p.data.services) ? p.data.services : []);
    return { step: typeof p.step === 'number' ? p.step : 0, data: merged };
  } catch (e) {return null;}
}

function App() {
  const draft = useMemo(() => loadDraft(), []);
  const [step, setStep] = useState(draft ? draft.step : 0);
  const [paid, setPaid] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState('');
  const submittingRef = useRef(false);
  const [showPrev, setShowPrev] = useState(false);
  const [focus, setFocus] = useState('historia');
  const [saved, setSaved] = useState(false);
  const [data, setData] = useState(draft ? draft.data : DEFAULT_DATA);
  const set = (patch) => setData((d) => ({ ...d, ...patch }));
  const toggleSec = (id) => setData((d) => ({ ...d, sections: { ...d.sections, [id]: !d.sections[id] } }));
  const toggleSvc = (id) => setData((d) => {const s = new Set(d.services);s.has(id) ? s.delete(id) : s.add(id);return { ...d, services: s };});

  // auto-guardado en localStorage (solo este navegador)
  const mounted = useRef(false);
  useEffect(() => {
    try {
      localStorage.setItem(DRAFT_KEY, JSON.stringify({ step, sid: wizSid(), data: { ...data, services: [...data.services] } }));
    } catch (e) {}
    if (mounted.current) {
      setSaved(true);
      const t = setTimeout(() => setSaved(false), 1900);
      return () => clearTimeout(t);
    }
    mounted.current = true;
  }, [data, step]);

  const order = useMemo(() => orderState(data.services), [data.services]);
  const totals = order;
  const activePkg = order.pkg;
  const orderTotal = order.total + bilingualFee(data.lang);

  const last = STEPS.length - 1;
  const phoneOk = data.cPhone.replace(/\D/g, '').length >= 8;
  const payChargeNow = data.payMethod === 'card' ? cardCharge(orderTotal) : orderTotal;
  const canNext =
  step === 0 ? true /* RMR-635 / TOS-WIZARD-025: Paso 1 SIN gate de cuenta (restaura WIZARD-017). AccountGate opcional; guest avanza. NO revertir a !!data.auth en rebuilds. */ :
  step === 1 ? !!data.honoree.trim() :
  step === last ? data.payMethod === 'card' ? true : !!(data.payMethod && data.cName.trim() && phoneOk) :
  true;
  const next = () => {
    if (step < last) { setStep(step + 1); return; }
    if (submitting || submittingRef.current) return; // guard anti doble-cobro (ref sync + state)
    // Paso Pago: si el backend expuso el hook, delegamos la creación de orden +
    // cobro real (Stripe / Zelle / Venmo). El shim llama onDone() cuando toca.
    if (typeof window.__onOrderSubmit === 'function') {
      submittingRef.current = true;
      setSubmitError('');
      setSubmitting(true);
      // red de seguridad: si el backend no resuelve ni redirige, re-habilita el botón
      const safety = setTimeout(() => { submittingRef.current = false; setSubmitting(false); setSubmitError('Esto está tardando más de lo normal. Revisa tu conexión e inténtalo de nuevo.'); }, 20000);
      try {
        window.__onOrderSubmit({
          services: Array.from(data.services),
          honoree: data.honoree,
          date: data.dateMode === 'exact' ? data.date : '',
          venue: data.venue,
          city: data.city,
          theme: data.theme,
          lang: data.lang,
          payMethod: data.payMethod,
          cName: data.cName,
          cEmail: data.cEmail,
          cPhone: data.cPhone,
          orderId: data.orderId,
          total: orderTotal,
          onDone: () => { clearTimeout(safety); submittingRef.current = false; try { localStorage.removeItem(DRAFT_KEY); } catch (e) {} setSubmitting(false); setPaid(true); },
        });
      } catch (e) {
        clearTimeout(safety);
        submittingRef.current = false;
        setSubmitting(false);
        setSubmitError('No pudimos procesar tu orden. Intenta de nuevo.');
      }
      return;
    }
    setPaid(true); // fallback demo (sin backend)
  };
  const goTo = (i) => {setStep(i);setPaid(false);};

  const mode = 'video';
  const showDemos = step === 0 && !paid;

  return (
    <div className={`ow ${showDemos ? '' : 'ow-single'} ${showPrev ? 'show-prev' : ''}`}>
      {showDemos && <button className="ow-prev-toggle" onClick={() => setShowPrev(true)}><Ic n="play" s={15} />Ver demos</button>}

      <div className="ow-left">
        <div className="ow-head">
          <div className="ow-brand">
            <img className="ow-brand-logo" src="assets/missxv-logo.png" alt="Miss XV VIP" />
            <div className="ow-brand-name">Miss<em>XV</em>VIP<span className="sub">Orden de invitación</span></div>
            <div className={`ow-secure-pill ${data.auth ? 'saved' : ''}`}>
              {data.auth ? <><Ic n="check" s={13} />Progreso guardado</> : <><Ic n="lock" s={13} />Compra segura</>}
            </div>
          </div>
          <div className="ow-progress">
            <div className="ow-progress-bars">
              {STEPS.map((_, i) => <div key={i} className={`ow-progress-seg ${paid || i < step ? 'done' : i === step ? 'active' : ''}`} />)}
            </div>
            <div className="ow-progress-meta">
              <span className="ow-step-kicker">{paid ? 'Completado' : STEPS[step]}</span>
              <span className="ow-step-count">{paid ? `${STEPS.length} de ${STEPS.length}` : `Paso ${step + 1} de ${STEPS.length}`}</span>
            </div>
          </div>
        </div>

        <div className="ow-body">
          {paid ? <Paid data={data} totals={totals} orderTotal={orderTotal} /> :
          step === 0 ? <StepServices data={data} set={set} totals={totals} toggleSvc={toggleSvc} focus={focus} setFocus={setFocus} /> :
          step === 1 ? <StepHonoree data={data} set={set} /> :
          step === 2 ? <StepSections data={data} set={set} toggleSec={toggleSec} /> :
          step === 3 ? <StepTheme data={data} set={set} /> :
          step === 4 ? <StepSummary data={data} totals={totals} goTo={goTo} activePkg={activePkg} orderTotal={orderTotal} /> :
          <StepPay data={data} set={set} totals={totals} orderTotal={orderTotal} />}
        </div>

        <div className={`ow-autosave ${saved ? 'show' : ''}`}><Ic n="check" s={12} />Borrador guardado automáticamente</div>

        {!paid &&
        <>
        {submitError && <div className="ow-pay-err" role="alert" style={{ color: '#fb7185', fontSize: 12.5, fontWeight: 600, textAlign: 'center', padding: '2px 0 8px' }}>{submitError}</div>}
        <div className="ow-foot">
            {step !== last &&
            <div className="ow-foot-total">
              <span className="lbl">Total de la orden</span>
              <span className="val">{USD(orderTotal)}<small>USD</small></span>
            </div>}
            {step > 0 && <button className="ow-btn-ghost" onClick={() => setStep(step - 1)}><Ic n="chevleft" s={16} />Atrás</button>}
            <button className={`ow-btn ${step === last ? 'ow-btn-pay' : 'ow-btn-cta'}`} disabled={!canNext || submitting} onClick={next} style={{ flex: step === last ? '1 1 auto' : '0 0 auto', minWidth: 0 }}>
              {submitting ? <>Procesando…</> :
            step === last ?
            data.payMethod === 'card' ?
            <><Ic n="card" s={16} />Pagar con tarjeta {USD2(payChargeNow)}</> :
            <><Ic n="check" s={16} />Enviar mi orden</> :
            <>Continuar<Ic n="chevright" s={16} /></>}
            </button>
          </div>
        </>
        }
      </div>

      {showDemos &&
      <div className="ow-right">
        <button className="ow-prev-close" onClick={() => setShowPrev(false)}><Ic n="x" s={18} /></button>
        <div className="ow-right-head">
          <div className="ow-live-chip"><span className="ow-live-dot" />Mira cada servicio en acción</div>
        </div>
        <VideoShowcase data={data} set={set} toggleSvc={toggleSvc} focus={focus} setFocus={setFocus} totals={totals} activePkg={activePkg} />
        <div className="ow-right-foot">Conoce nuestros servicios premium y mira cada demo antes de agregarla a tu orden.</div>
      </div>}
    </div>);

}

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