// components.jsx — Mi VIRAL design system
// Reusable primitives for the activation tracker PWA.

// ─────────────────────────────────────────────────────────────
// Icon — minimal line set, stroke 1.5, rounded
// ─────────────────────────────────────────────────────────────
function Icon({ name, size = 20, color = 'currentColor', strokeWidth = 1.5, style }) {
  const p = {
    width: size, height: size, viewBox: '0 0 24 24', fill: 'none',
    stroke: color, strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round',
    style,
  };
  switch (name) {
    case 'back':       return (<svg {...p}><path d="M15 18l-6-6 6-6"/></svg>);
    case 'chevron':    return (<svg {...p}><path d="M9 6l6 6-6 6"/></svg>);
    case 'chevron-d':  return (<svg {...p}><path d="M6 9l6 6 6-6"/></svg>);
    case 'check':      return (<svg {...p}><path d="M5 12l5 5L20 7"/></svg>);
    case 'clock':      return (<svg {...p}><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/></svg>);
    case 'alert':      return (<svg {...p}><path d="M12 3.5L22 21H2L12 3.5z"/><path d="M12 10v5"/><circle cx="12" cy="18" r=".6" fill={color} stroke="none"/></svg>);
    case 'qr':         return (<svg {...p}><rect x="3" y="3" width="7" height="7" rx="1.2"/><rect x="14" y="3" width="7" height="7" rx="1.2"/><rect x="3" y="14" width="7" height="7" rx="1.2"/><path d="M14 14h3v3M14 19v2M19 14v3M21 17v4M19 21h-2"/></svg>);
    case 'sim':        return (<svg {...p}><path d="M9 3h7l4 4v13a1.5 1.5 0 01-1.5 1.5h-13A1.5 1.5 0 014 20V8l5-5z"/><rect x="8" y="11" width="8" height="7" rx="1.5"/><path d="M12 11v7M8 14.5h8"/></svg>);
    case 'phone':      return (<svg {...p}><path d="M5 4h4l2 5-3 1.8a11 11 0 005.2 5.2L15 13l5 2v4a2 2 0 01-2 2A16 16 0 013 6a2 2 0 012-2z"/></svg>);
    case 'arrow-right':return (<svg {...p}><path d="M5 12h14M13 6l6 6-6 6"/></svg>);
    case 'copy':       return (<svg {...p}><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a1 1 0 011-1h10"/></svg>);
    case 'share':      return (<svg {...p}><path d="M12 3v12M7 8l5-5 5 5M5 14v5a2 2 0 002 2h10a2 2 0 002-2v-5"/></svg>);
    case 'refresh':    return (<svg {...p}><path d="M3 12a9 9 0 0115-6.7L21 8M21 3v5h-5M21 12a9 9 0 01-15 6.7L3 16M3 21v-5h5"/></svg>);
    case 'more':       return (<svg {...p}><circle cx="5" cy="12" r=".9" fill={color} stroke="none"/><circle cx="12" cy="12" r=".9" fill={color} stroke="none"/><circle cx="19" cy="12" r=".9" fill={color} stroke="none"/></svg>);
    case 'info':       return (<svg {...p}><circle cx="12" cy="12" r="9"/><path d="M12 11v5"/><circle cx="12" cy="8" r=".6" fill={color} stroke="none"/></svg>);
    case 'mail':       return (<svg {...p}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></svg>);
    case 'message':    return (<svg {...p}><path d="M21 12a8 8 0 01-12 7l-5 1 1-5a8 8 0 1116-3z"/></svg>);
    case 'support':    return (<svg {...p}><circle cx="12" cy="12" r="9"/><path d="M9 9.5a3 3 0 015.7 1.3c0 1.5-2.2 2-2.2 3.7M12 17.5h.01"/></svg>);
    case 'home':       return (<svg {...p}><path d="M3 12l9-9 9 9M5 10v10h14V10"/></svg>);
    case 'spark':      return (<svg {...p}><path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8L12 3z"/></svg>);
    case 'bell':       return (<svg {...p}><path d="M6 8a6 6 0 0112 0v5l2 3H4l2-3V8z"/><path d="M10 19a2 2 0 004 0"/></svg>);
    case 'card':       return (<svg {...p}><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18"/></svg>);
    case 'truck':      return (<svg {...p}><path d="M3 7h11v10H3zM14 10h4l3 3v4h-7"/><circle cx="7" cy="18" r="1.6"/><circle cx="17" cy="18" r="1.6"/></svg>);
    case 'sparkle-2':  return (<svg {...p}><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/></svg>);
    case 'signal':     return (<svg {...p}><path d="M3 17a13 13 0 0118 0M6 13.5a9 9 0 0112 0M9 10a5 5 0 016 0"/><circle cx="12" cy="18.5" r="1.2" fill={color} stroke="none"/></svg>);
    case 'x':          return (<svg {...p}><path d="M6 6l12 12M18 6L6 18"/></svg>);
    case 'send':       return (<svg {...p}><path d="M3 11l18-8-8 18-2.5-7.5L3 11z"/></svg>);
    default:           return null;
  }
}

// ─────────────────────────────────────────────────────────────
// StatusBadge — pill with tone + optional pulsing dot
// ─────────────────────────────────────────────────────────────
function StatusBadge({ tone = 'active', children, dot = true, size = 'md', pulse = null }) {
  const tones = {
    active:  { bg: 'rgba(252,90,0,0.16)',   fg: '#FC5A00', d: '#FC5A00', pulse: true },
    success: { bg: 'rgba(216,255,75,0.16)', fg: '#D8FF4B', d: '#D8FF4B' },
    error:   { bg: 'rgba(220,38,38,0.18)',  fg: '#F87171', d: '#DC2626' },
    neutral: { bg: 'rgba(255,255,255,0.08)', fg: 'rgba(255,255,255,0.72)', d: 'rgba(255,255,255,0.55)' },
    pending: { bg: 'rgba(255,255,255,0.05)', fg: 'rgba(255,255,255,0.55)', d: 'rgba(255,255,255,0.4)' },
    cream:   { bg: '#F5F5F0', fg: '#12152E', d: '#FC5A00' },
  };
  const t = tones[tone];
  const sz = size === 'sm'
    ? { pad: '3px 8px', fs: 11, gap: 5, dot: 5 }
    : size === 'lg'
    ? { pad: '7px 14px', fs: 13, gap: 7, dot: 7 }
    : { pad: '5px 10px', fs: 12, gap: 6, dot: 6 };
  const isPulse = pulse ?? t.pulse;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: sz.gap,
      padding: sz.pad, background: t.bg, color: t.fg,
      fontSize: sz.fs, fontWeight: 600, letterSpacing: 0.2,
      borderRadius: 999, lineHeight: 1, whiteSpace: 'nowrap',
    }}>
      {dot && (
        <span style={{
          width: sz.dot, height: sz.dot, borderRadius: 999, background: t.d,
          ...(isPulse && { animation: 'viral-pulse 1.6s ease-in-out infinite' }),
        }}/>
      )}
      {children}
    </span>
  );
}

// ─────────────────────────────────────────────────────────────
// ProgressCircle — circular % indicator
// ─────────────────────────────────────────────────────────────
function ProgressCircle({ value = 0, total = 100, size = 88, strokeWidth = 6, accent = 'orange', children, track = 'rgba(255,255,255,0.08)' }) {
  const r = (size - strokeWidth) / 2;
  const c = 2 * Math.PI * r;
  const pct = Math.max(0, Math.min(1, value / total));
  const colors = { orange: '#FC5A00', lime: '#D8FF4B', red: '#DC2626' };
  // Glow sutil del color del accent — agrega profundidad sin gritar.
  const glow = { orange: 'rgba(252,90,0,0.18)', lime: 'rgba(216,255,75,0.18)', red: 'rgba(220,38,38,0.18)' };
  return (
    <div style={{
      position: 'relative', width: size, height: size, flexShrink: 0,
      // Glow ambient — drop-shadow es leve, no satura.
      filter: pct > 0.05 ? `drop-shadow(0 0 12px ${glow[accent]})` : 'none',
    }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
        <circle cx={size/2} cy={size/2} r={r} stroke={track} strokeWidth={strokeWidth} fill="none"/>
        <circle cx={size/2} cy={size/2} r={r}
          stroke={colors[accent]} strokeWidth={strokeWidth}
          strokeDasharray={`${c * pct} ${c}`} strokeLinecap="round" fill="none"
          style={{ transition: 'stroke-dasharray 0.7s cubic-bezier(0.22, 1, 0.36, 1)' }}/>
      </svg>
      <div style={{
        position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column',
        alignItems: 'center', justifyContent: 'center', textAlign: 'center', lineHeight: 1,
      }}>{children}</div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// TimelineStep — vertical timeline node
// ─────────────────────────────────────────────────────────────
function TimelineStep({
  status = 'pending',  // completed | active | pending | error
  idx, title, time, detail, action, isLast = false, onClick,
}) {
  const tones = {
    completed: { bg: '#D8FF4B', fg: '#0A0C28', line: '#D8FF4B', tc: '#D8FF4B' },
    active:    { bg: '#FC5A00', fg: '#fff',    line: 'rgba(255,255,255,0.14)', tc: '#FC5A00' },
    pending:   { bg: 'rgba(255,255,255,0.05)', fg: 'rgba(255,255,255,0.45)',
                 line: 'rgba(255,255,255,0.14)', tc: 'rgba(255,255,255,0.4)' },
    error:     { bg: '#DC2626', fg: '#fff',    line: 'rgba(255,255,255,0.14)', tc: '#F87171' },
  };
  const c = tones[status];
  const lineBg = status === 'completed'
    ? c.line
    : `repeating-linear-gradient(to bottom, ${c.line} 0 4px, transparent 4px 8px)`;
  return (
    <div style={{ display: 'flex', gap: 14, paddingBottom: isLast ? 0 : 22, position: 'relative' }}>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0 }}>
        <div style={{
          width: 28, height: 28, borderRadius: 999, background: c.bg, color: c.fg,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 12, fontWeight: 700, fontVariantNumeric: 'tabular-nums',
          boxShadow: status === 'pending' ? 'inset 0 0 0 1px rgba(255,255,255,0.1)' : 'none',
          ...(status === 'active' && { animation: 'viral-pulse 1.6s ease-in-out infinite' }),
        }}>
          {status === 'completed' ? <Icon name="check" size={14} color="#0A0C28" strokeWidth={2.6}/>
           : status === 'error' ? '!'
           : (idx ?? '')}
        </div>
        {!isLast && (
          <div style={{ width: 2, flex: 1, marginTop: 4, marginBottom: 4, background: lineBg, minHeight: 22 }}/>
        )}
      </div>
      <div onClick={onClick} style={{
        flex: 1, paddingTop: 3, cursor: onClick ? 'pointer' : 'default',
        animation: status === 'active' ? 'viral-fade-in-up 0.4s ease' : 'none',
      }}>
        <div style={{
          fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em',
          color: status === 'pending' ? 'rgba(255,255,255,0.5)' : '#fff',
          marginBottom: 3,
        }}>{title}</div>
        {time && (
          <div style={{
            fontSize: 13, fontWeight: 500, color: c.tc,
            fontVariantNumeric: 'tabular-nums',
            marginBottom: detail || action ? 6 : 0,
          }}>{time}</div>
        )}
        {detail && (
          <div style={{
            fontSize: 13.5, color: 'rgba(255,255,255,0.62)', lineHeight: 1.45,
            marginBottom: action ? 10 : 0,
          }}>{detail}</div>
        )}
        {action && <div style={{ marginTop: 6 }}>{action}</div>}
      </div>
      {onClick && (
        <Icon name="chevron" size={16} color="rgba(255,255,255,0.3)" strokeWidth={2}
          style={{ alignSelf: 'flex-start', marginTop: 8 }}/>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// ActionCard — tappable row card
// ─────────────────────────────────────────────────────────────
function ActionCard({ icon, iconTone = 'navy', title, subtitle, badge, onClick, variant = 'default', chevron = true }) {
  // Variant `default` ahora usa surface-card (gradient sutil) en vez del navy plano.
  // Las variantes con color sólido (primary/lime/cream) quedan iguales.
  const variants = {
    default: { bg: 'linear-gradient(180deg, rgba(255,255,255,0.045) 0%, rgba(255,255,255,0.015) 100%), #1A1D52',
               fg: '#fff', border: 'rgba(255,255,255,0.07)' },
    cream:   { bg: '#F5F5F0', fg: '#12152E', border: 'rgba(18,21,46,0.06)' },
    primary: { bg: '#FC5A00', fg: '#fff',    border: null },
    lime:    { bg: '#D8FF4B', fg: '#0A0C28', border: null },
  };
  const v = variants[variant];
  return (
    <button onClick={onClick} className="viral-action-card" style={{
      width: '100%', display: 'flex', alignItems: 'center', gap: 14,
      padding: '14px 16px', background: v.bg, color: v.fg,
      border: 'none', borderRadius: 18, textAlign: 'left',
      cursor: 'pointer', fontFamily: 'inherit',
      ...(v.border && { boxShadow: `inset 0 0 0 1px ${v.border}` }),
    }}>
      {icon && <IconCircle name={icon} tone={iconTone} size={40}/>}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em', marginBottom: subtitle ? 2 : 0 }}>{title}</div>
        {subtitle && <div style={{ fontSize: 13, opacity: 0.65, lineHeight: 1.35 }}>{subtitle}</div>}
      </div>
      {badge}
      {chevron && <Icon name="chevron" size={18} color={v.fg} strokeWidth={2} style={{ opacity: 0.5 }}/>}
    </button>
  );
}

// ─────────────────────────────────────────────────────────────
// NotificationPreview — lock-screen push
// ─────────────────────────────────────────────────────────────
function NotificationPreview({ time = 'ahora', title, body, accent = 'orange' }) {
  const accents = { orange: '#FC5A00', lime: '#D8FF4B', red: '#DC2626' };
  const isLime = accent === 'lime';
  return (
    <div style={{
      display: 'flex', gap: 12, padding: 13,
      background: 'rgba(40, 44, 90, 0.55)',
      backdropFilter: 'blur(24px) saturate(180%)',
      WebkitBackdropFilter: 'blur(24px) saturate(180%)',
      borderRadius: 18,
      boxShadow: 'inset 0 0 0 0.5px rgba(255,255,255,0.14)',
    }}>
      <div style={{
        width: 38, height: 38, borderRadius: 9.5,
        background: accents[accent], flexShrink: 0,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontWeight: 800, fontSize: 17, color: isLime ? '#0A0C28' : '#fff',
        letterSpacing: '-0.05em',
      }}>v.</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 2 }}>
          <span style={{ fontSize: 12.5, fontWeight: 600, color: '#fff', letterSpacing: 0.4, textTransform: 'uppercase' }}>VIRAL</span>
          <span style={{ fontSize: 12, color: 'rgba(255,255,255,0.55)', fontVariantNumeric: 'tabular-nums' }}>{time}</span>
        </div>
        <div style={{ fontSize: 14, fontWeight: 600, color: '#fff', marginBottom: 1, letterSpacing: '-0.01em' }}>{title}</div>
        <div style={{ fontSize: 13.5, color: 'rgba(255,255,255,0.78)', lineHeight: 1.35 }}>{body}</div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────
function MetaRow({ label, value, mono = false, hairline = true }) {
  return (
    <div style={{
      display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
      padding: '12px 0', gap: 16,
      borderBottom: hairline ? '1px solid rgba(255,255,255,0.06)' : 'none',
    }}>
      <span style={{ fontSize: 13, color: 'rgba(255,255,255,0.5)' }}>{label}</span>
      <span style={{
        fontSize: 14, fontWeight: 500, color: '#fff', textAlign: 'right',
        ...(mono && { fontVariantNumeric: 'tabular-nums', fontFeatureSettings: '"tnum"' }),
      }}>{value}</span>
    </div>
  );
}

function IconCircle({ name, size = 40, tone = 'navy' }) {
  const tones = {
    navy:   { bg: 'rgba(255,255,255,0.06)', fg: '#fff' },
    orange: { bg: 'rgba(252,90,0,0.18)',    fg: '#FC5A00' },
    lime:   { bg: 'rgba(216,255,75,0.18)',  fg: '#D8FF4B' },
    red:    { bg: 'rgba(220,38,38,0.18)',   fg: '#F87171' },
    cream:  { bg: '#F5F5F0',                fg: '#12152E' },
    inkOnCream: { bg: 'rgba(18,21,46,0.06)', fg: '#12152E' },
  };
  const t = tones[tone];
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.32,
      background: t.bg, color: t.fg, flexShrink: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <Icon name={name} size={size * 0.5}/>
    </div>
  );
}

function Button({ children, variant = 'primary', icon, fullWidth = false, onClick, size = 'md', iconLeft }) {
  // Cada variant: bg puede ser color sólido o gradient.
  // shadow: sombra propia del color para variants brand (primary/lime).
  const vs = {
    primary:   {
      bg: 'linear-gradient(180deg, #FC6A0F 0%, #FC5A00 100%)',
      fg: '#fff',
      shadow: '0 4px 14px rgba(252, 90, 0, 0.35), inset 0 1px 0 rgba(255,255,255,0.18)',
    },
    secondary: { bg: 'rgba(255,255,255,0.08)', fg: '#fff', shadow: 'inset 0 0 0 1px rgba(255,255,255,0.10)' },
    cream:     { bg: '#F5F5F0', fg: '#12152E', shadow: '0 1px 2px rgba(0,0,0,0.18)' },
    lime:      {
      bg: 'linear-gradient(180deg, #E2FF6B 0%, #D8FF4B 100%)',
      fg: '#0A0C28',
      shadow: '0 4px 14px rgba(216, 255, 75, 0.30), inset 0 1px 0 rgba(255,255,255,0.5)',
    },
    ghost:     { bg: 'transparent', fg: '#FC5A00', shadow: 'none' },
  };
  const v = vs[variant];
  const sz = size === 'sm'
    ? { h: 38, fs: 14, px: 16, gap: 8, r: 12 }
    : { h: 52, fs: 16, px: 22, gap: 10, r: 16 };
  return (
    <button onClick={onClick} className="viral-btn" style={{
      height: sz.h, padding: `0 ${sz.px}px`, background: v.bg, color: v.fg,
      border: 'none', borderRadius: sz.r,
      fontFamily: 'inherit', fontSize: sz.fs, fontWeight: 600,
      letterSpacing: '-0.01em', width: fullWidth ? '100%' : 'auto',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: sz.gap,
      cursor: 'pointer',
      boxShadow: v.shadow,
    }}>
      {iconLeft && <Icon name={iconLeft} size={18} strokeWidth={2}/>}
      {children}
      {icon && <Icon name={icon} size={18} strokeWidth={2}/>}
    </button>
  );
}

function Hairline({ tone = 'dark' }) {
  return <div style={{ height: 1, background: tone === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(18,21,46,0.08)' }}/>;
}

function ScreenHeader({ eyebrow, title, onBack, action, dark = true }) {
  const fg = dark ? '#fff' : '#12152E';
  const btnBg = dark ? 'rgba(255,255,255,0.06)' : 'rgba(18,21,46,0.06)';
  const Btn = ({ icon, onClick }) => (
    <button onClick={onClick} style={{
      width: 36, height: 36, borderRadius: 999, background: btnBg, border: 'none',
      color: fg, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
    }}><Icon name={icon} size={18}/></button>
  );
  // No default "more" button — keep top clean. Only show row if onBack or explicit action exists.
  const hasButtons = onBack || action;
  return (
    <div style={{ padding: '0 20px' }}>
      {hasButtons && (
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: 36 }}>
          {onBack ? <Btn icon="back" onClick={onBack}/> : <div/>}
          {action || <div/>}
        </div>
      )}
      {(eyebrow || title) && (
        <div style={{ marginTop: hasButtons ? 10 : 0 }}>
          {eyebrow && (
            <div style={{
              fontSize: 11.5, fontWeight: 700, letterSpacing: 1.4,
              textTransform: 'uppercase', color: '#FC5A00', marginBottom: 4,
            }}>{eyebrow}</div>
          )}
          {title && (
            <div style={{
              fontSize: 28, fontWeight: 700, color: fg,
              letterSpacing: '-0.025em', lineHeight: 1.15,
            }}>{title}</div>
          )}
        </div>
      )}
    </div>
  );
}

function BrandMark({ size = 22, dark = true }) {
  // BrandMark uses the actual VIRAL logo PNG. `size` is the rendered height.
  return (
    <img
      src="logo_viral.png"
      alt="VIRAL"
      style={{
        height: size * 1.4, width: 'auto',
        display: 'inline-block',
        verticalAlign: 'baseline',
        userSelect: 'none', pointerEvents: 'none',
        ...(dark ? {} : { filter: 'invert(0)' }),
      }}
    />
  );
}

// ─────────────────────────────────────────────────────────────
// Empty placeholder for unbuilt screens
// ─────────────────────────────────────────────────────────────
function ScreenPlaceholder({ kind, label, hint }) {
  return (
    <div className="viral-app viral-screen-bg" style={{
      width: '100%', height: '100%', display: 'flex',
      flexDirection: 'column', padding: '60px 24px 40px',
    }}>
      <div style={{ paddingTop: 8, opacity: 0.4 }}>
        <BrandMark size={20}/>
      </div>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 14 }}>
        <div style={{
          width: 56, height: 56, borderRadius: 16,
          background: 'rgba(255,255,255,0.04)',
          boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.08)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <span style={{ fontSize: 18, fontWeight: 700, color: 'rgba(255,255,255,0.4)', fontVariantNumeric: 'tabular-nums' }}>{kind}</span>
        </div>
        <div style={{ fontSize: 22, fontWeight: 700, color: '#fff', letterSpacing: '-0.02em', lineHeight: 1.15 }}>{label}</div>
        {hint && <div style={{ fontSize: 14, color: 'rgba(255,255,255,0.55)', lineHeight: 1.5 }}>{hint}</div>}
        <div style={{
          marginTop: 18,
          display: 'inline-flex', alignSelf: 'flex-start', alignItems: 'center', gap: 8,
          padding: '6px 12px', borderRadius: 999,
          background: 'rgba(252,90,0,0.14)', color: '#FC5A00',
          fontSize: 11.5, fontWeight: 700, letterSpacing: 0.6, textTransform: 'uppercase',
        }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: '#FC5A00', animation: 'viral-pulse 1.6s ease-in-out infinite' }}/>
          Pendiente de diseño
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Skeleton — placeholder con shimmer, para usar mientras data carga.
// Uso: <Skeleton w="60%" h={20} /> o <Skeleton style={{ marginBottom: 12 }} />
// CSS de la animación vive en tokens.css (.viral-skeleton).
// ─────────────────────────────────────────────────────────────
function Skeleton({ w = '100%', h = 14, radius = 8, style }) {
  return (
    <span
      className="viral-skeleton"
      style={{
        '--skeleton-w': typeof w === 'number' ? w + 'px' : w,
        '--skeleton-h': typeof h === 'number' ? h + 'px' : h,
        borderRadius: radius,
        ...style,
      }}
    />
  );
}

// SkeletonGroup — N líneas con anchos descendentes, look "loading text".
function SkeletonGroup({ lines = 3, gap = 8 }) {
  const widths = ['78%', '92%', '64%', '85%', '55%'];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap }}>
      {Array.from({ length: lines }, (_, i) => (
        <Skeleton key={i} w={widths[i % widths.length]} h={14} />
      ))}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MiLineaSkeleton — placeholder mientras carga datos de línea.
// Replica la estructura visual de MiLineaScreen: progress circle + meta + 3 stats.
// ─────────────────────────────────────────────────────────────
function MiLineaSkeleton() {
  return (
    <div style={{ padding: '8px 20px 24px' }}>
      {/* Card principal: progress circle + plan info */}
      <div className="viral-card" style={{
        padding: 24, marginBottom: 14,
        display: 'flex', alignItems: 'center', gap: 18,
      }}>
        <Skeleton w={88} h={88} radius={999} />
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 8 }}>
          <Skeleton w="60%" h={16} />
          <Skeleton w="80%" h={12} />
          <Skeleton w="45%" h={11} />
        </div>
      </div>
      {/* Stats grid 3 columnas: GB, Min, SMS */}
      <div className="viral-card" style={{
        padding: 18,
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12,
      }}>
        {[0,1,2].map(i => (
          <div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
            <Skeleton w={36} h={36} radius={999} />
            <Skeleton w="70%" h={11} />
            <Skeleton w="50%" h={14} />
          </div>
        ))}
      </div>
      {/* CTA placeholder */}
      <div style={{ marginTop: 14 }}>
        <Skeleton w="100%" h={52} radius={16} />
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// MiCuentaSkeleton — placeholder para Mi Cuenta (persona + créditos + lineas).
// ─────────────────────────────────────────────────────────────
function MiCuentaSkeleton() {
  return (
    <div style={{ padding: '8px 20px 24px' }}>
      {/* Persona card */}
      <div className="viral-card" style={{ padding: 18, marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 14 }}>
          <Skeleton w={48} h={48} radius={999} />
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6 }}>
            <Skeleton w="55%" h={15} />
            <Skeleton w="75%" h={12} />
          </div>
        </div>
        <SkeletonGroup lines={3} gap={10} />
      </div>
      {/* Créditos card 3 stats */}
      <div className="viral-card" style={{ padding: 18, marginBottom: 12 }}>
        <Skeleton w="40%" h={11} style={{ marginBottom: 14 }} />
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16 }}>
          {[0,1,2].map(i => (
            <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              <Skeleton w="65%" h={10} />
              <Skeleton w="80%" h={18} />
            </div>
          ))}
        </div>
      </div>
      {/* List rows (ActionCards) */}
      {[0,1,2].map(i => (
        <div key={i} className="viral-card" style={{
          padding: 14, marginBottom: 10,
          display: 'flex', alignItems: 'center', gap: 14,
        }}>
          <Skeleton w={40} h={40} radius={12} />
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6 }}>
            <Skeleton w="60%" h={14} />
            <Skeleton w="85%" h={12} />
          </div>
          <Skeleton w={16} h={16} radius={999} />
        </div>
      ))}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// EditarPerfilSkeleton — placeholder para formulario de perfil.
// ─────────────────────────────────────────────────────────────
function EditarPerfilSkeleton() {
  return (
    <div style={{ padding: '8px 20px 24px' }}>
      <div className="viral-card" style={{ padding: 18 }}>
        {Array.from({ length: 7 }, (_, i) => (
          <div key={i} style={{ marginBottom: 16 }}>
            <Skeleton w={['40%','30%','45%','35%','38%','30%','42%'][i]} h={11} style={{ marginBottom: 8 }} />
            <Skeleton w="100%" h={42} radius={12} />
          </div>
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// EmptyState — placeholder para cuando NO hay data (cero líneas, cero órdenes, etc).
// Reemplaza los "vacío deprimente" por algo accionable.
// ─────────────────────────────────────────────────────────────
function EmptyState({ icon = 'spark', title, hint, action }) {
  return (
    <div style={{
      padding: '36px 22px',
      display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', gap: 14,
    }}>
      <div style={{
        width: 64, height: 64, borderRadius: 18,
        background: 'rgba(255,255,255,0.04)',
        boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.08)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'rgba(255,255,255,0.45)',
      }}>
        <Icon name={icon} size={28} strokeWidth={1.8}/>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxWidth: 280 }}>
        <div style={{
          fontSize: 16, fontWeight: 600, color: '#fff', letterSpacing: '-0.01em',
        }}>{title}</div>
        {hint && (
          <div style={{
            fontSize: 13.5, color: 'rgba(255,255,255,0.55)', lineHeight: 1.45,
          }}>{hint}</div>
        )}
      </div>
      {action}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// ErrorState — para errores de red / fetch. Action típica: retry button.
// ─────────────────────────────────────────────────────────────
function ErrorState({ title = 'No pudimos cargar tus datos', hint, onRetry }) {
  return (
    <div style={{
      margin: '12px 20px',
      padding: 20,
      borderRadius: 16,
      background: 'rgba(220,38,38,0.10)',
      boxShadow: 'inset 0 0 0 1px rgba(220,38,38,0.30)',
      display: 'flex', flexDirection: 'column', gap: 12,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <div style={{
          width: 36, height: 36, borderRadius: 10,
          background: 'rgba(220,38,38,0.20)',
          color: '#F87171',
          display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
        }}>
          <Icon name="alert" size={20} strokeWidth={2}/>
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 14.5, fontWeight: 600, color: '#fff', marginBottom: 2 }}>{title}</div>
          {hint && (
            <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.65)', lineHeight: 1.4 }}>{hint}</div>
          )}
        </div>
      </div>
      {onRetry && (
        <button onClick={onRetry} className="viral-btn" style={{
          alignSelf: 'flex-start',
          background: 'rgba(255,255,255,0.08)', color: '#fff',
          border: '1px solid rgba(255,255,255,0.15)',
          padding: '8px 16px', borderRadius: 10,
          fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: 'inherit',
        }}>
          Reintentar
        </button>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// animateCount — anima un número de "from" a "to" durante "duration" ms
// con ease-out cubic. Llamar pasando una ref al elemento que muestra el número.
// Uso típico: useEffect(() => animateCount(ref.current, 0, value, 700, fmtInt))
// ─────────────────────────────────────────────────────────────
function animateCount(el, from, to, duration, formatFn) {
  if (!el || typeof to !== 'number' || isNaN(to)) return;
  if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    el.textContent = formatFn ? formatFn(to) : String(to);
    return;
  }
  const start = performance.now();
  const range = to - from;
  function step(now) {
    const t = Math.min(1, (now - start) / duration);
    const eased = 1 - Math.pow(1 - t, 3); // ease-out cubic
    const current = from + range * eased;
    el.textContent = formatFn ? formatFn(current) : String(Math.round(current));
    if (t < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

// ─────────────────────────────────────────────────────────────
// ChatPanel — sheet de chat conectado al webhook chatTrigger de n8n.
// El webhook vive en: {N8N_HOST}/webhook/{webhookId}/chat
// Payload: { action, chatInput, sessionId }  →  respuesta { output }
// SessionId se persiste por dispositivo en localStorage.
// ─────────────────────────────────────────────────────────────
const VIRAL_CHAT_ENDPOINT =
  'https://n8n-n8n.s1kkr7.easypanel.host/webhook/bc3d8f7c-22ef-45cf-8f3f-4187fc7721ae/chat';
const VIRAL_CHAT_SESSION_KEY = 'viral_chat_session_v1';

function ChatPanel({ open, onClose }) {
  const [messages, setMessages] = React.useState([]);
  const [input, setInput] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const scrollRef = React.useRef(null);
  const inputRef = React.useRef(null);
  const sessionId = React.useRef(null);

  // Init sessionId (persistido por dispositivo)
  React.useEffect(() => {
    try {
      let sid = localStorage.getItem(VIRAL_CHAT_SESSION_KEY);
      if (!sid) {
        sid = 'pwa-' + Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
        localStorage.setItem(VIRAL_CHAT_SESSION_KEY, sid);
      }
      sessionId.current = sid;
    } catch (_) {
      sessionId.current = 'pwa-fallback-' + Date.now();
    }
  }, []);

  // Autoscroll al final cuando llega un mensaje nuevo o aparece el typing.
  React.useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [messages, loading]);

  // Focus al input al abrir.
  React.useEffect(() => {
    if (open) setTimeout(() => inputRef.current && inputRef.current.focus(), 200);
  }, [open]);

  async function sendMessage() {
    const text = input.trim();
    if (!text || loading) return;
    setMessages(m => [...m, { role: 'user', content: text }]);
    setInput('');
    setLoading(true);
    try {
      const res = await fetch(VIRAL_CHAT_ENDPOINT, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          action: 'sendMessage',
          chatInput: text,
          sessionId: sessionId.current,
        }),
      });
      const data = await res.json().catch(() => ({}));
      const reply = data.output || data.text || data.message || 'Sin respuesta del agente.';
      setMessages(m => [...m, { role: 'agent', content: reply }]);
    } catch (_) {
      setMessages(m => [...m, { role: 'agent', content: 'No pudimos conectar con el agente. Intenta de nuevo en un momento.' }]);
    } finally {
      setLoading(false);
    }
  }

  function onKeyDown(e) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
  }

  return (
    <React.Fragment>
      <div
        className={'viral-chat-backdrop' + (open ? ' open' : '')}
        onClick={onClose}
      />
      <aside
        className={'viral-chat-panel' + (open ? ' open' : '')}
        aria-hidden={!open}
        role="dialog"
        aria-label="Chat con asistente VIRAL"
      >
        <header className="viral-chat-header">
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <div className="viral-chat-avatar">
              <Icon name="message" size={18} strokeWidth={2} color="#fff"/>
            </div>
            <div>
              <div className="viral-chat-title">Asistente VIRAL</div>
              <div className="viral-chat-sub">
                <span className="viral-chat-dot"/> En línea
              </div>
            </div>
          </div>
          <button className="viral-chat-close" onClick={onClose} aria-label="Cerrar chat">
            <Icon name="x" size={18} strokeWidth={2.4}/>
          </button>
        </header>

        <div className="viral-chat-messages" ref={scrollRef}>
          {messages.length === 0 && (
            <div className="viral-chat-empty">
              <div className="viral-chat-empty-icon">
                <Icon name="message" size={26} strokeWidth={1.8}/>
              </div>
              <div className="viral-chat-empty-title">¿En qué te ayudamos?</div>
              <div className="viral-chat-empty-hint">
                Pregunta sobre tu línea, plan, recargas, portabilidad o reemplazo de SIM.
              </div>
            </div>
          )}
          {messages.map((m, i) => (
            <div key={i} className={'viral-chat-msg viral-chat-msg-' + m.role}>
              {m.content}
            </div>
          ))}
          {loading && (
            <div className="viral-chat-msg viral-chat-msg-agent viral-chat-typing">
              <span/><span/><span/>
            </div>
          )}
        </div>

        <div className="viral-chat-input-row">
          <input
            ref={inputRef}
            type="text"
            placeholder="Escribe tu mensaje…"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            onKeyDown={onKeyDown}
            disabled={loading}
          />
          <button
            className="viral-chat-send"
            onClick={sendMessage}
            disabled={!input.trim() || loading}
            aria-label="Enviar mensaje"
          >
            <Icon name="send" size={18} strokeWidth={2} color="#fff"/>
          </button>
        </div>
      </aside>
    </React.Fragment>
  );
}

Object.assign(window, {
  Icon, StatusBadge, ProgressCircle, TimelineStep, ActionCard, NotificationPreview,
  MetaRow, IconCircle, Button, Hairline, ScreenHeader, BrandMark, ScreenPlaceholder,
  Skeleton, SkeletonGroup,
  MiLineaSkeleton, MiCuentaSkeleton, EditarPerfilSkeleton,
  EmptyState, ErrorState,
  animateCount,
  ChatPanel,
});
