// Nexora Cores — Website UI Kit · Products (Stripe-style visual cards)
const { Eyebrow, StatusTag, Button } = window.NexoraCoresDesignSystem_c8b55f;
const PROD_SP = ''; // sprite is inlined in the page → same-document #ico-* refs

/* ── Animated visuals (rAF-driven so they animate reliably) ── */

// Twinkling particle arc — CommunityOS (purple) energy swirl.
function ParticleField({ rgb }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext('2d');
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    let w, h, dpr, pts = [], raf, run = true, tick = 0;
    function resize() {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      const r = c.getBoundingClientRect(); w = r.width; h = r.height;
      c.width = w * dpr; c.height = h * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      const n = Math.max(90, Math.min(190, Math.round(w * h / 150)));
      const cx = w * 0.5, cy = h * 1.02, R = h * 0.98;
      pts = new Array(n).fill(0).map(() => {
        const a = Math.PI * (0.05 + Math.random() * 0.9);
        const rad = R * (0.4 + Math.random() * 0.7);
        return { a, rad, cx, cy, tw: Math.random() * Math.PI * 2, sp: 0.01 + Math.random() * 0.03, sz: Math.random() * 1.8 + 0.7, osc: Math.random() * 6 };
      });
    }
    function frame() {
      ctx.clearRect(0, 0, w, h); tick += 1;
      for (const p of pts) {
        p.tw += p.sp;
        const rad = p.rad + Math.sin(tick * 0.01 + p.osc) * 6;
        const x = p.cx + Math.cos(p.a) * rad;
        const y = p.cy - Math.sin(p.a) * rad;
        const o = 0.28 + 0.55 * (0.5 + 0.5 * Math.sin(p.tw));
        ctx.beginPath(); ctx.arc(x, y, p.sz, 0, Math.PI * 2);
        ctx.fillStyle = `rgba(${rgb},${o})`;
        ctx.shadowColor = `rgba(${rgb},0.9)`; ctx.shadowBlur = 6; ctx.fill(); ctx.shadowBlur = 0;
      }
      if (run && !reduce) raf = requestAnimationFrame(frame);
    }
    resize(); frame(); if (!reduce) raf = requestAnimationFrame(frame);
    const onR = () => resize(); window.addEventListener('resize', onR);
    const onV = () => { if (document.hidden) { run = false; cancelAnimationFrame(raf); } else if (!reduce) { run = true; raf = requestAnimationFrame(frame); } };
    document.addEventListener('visibilitychange', onV);
    return () => { run = false; cancelAnimationFrame(raf); window.removeEventListener('resize', onR); document.removeEventListener('visibilitychange', onV); };
  }, []);
  return <canvas ref={ref} aria-hidden="true" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />;
}

// Expanding signal rings — Connect (teal).
function RingsCanvas({ rgb }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext('2d');
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    let w, h, dpr, raf, run = true, t = 0;
    function resize() { dpr = Math.min(window.devicePixelRatio || 1, 2); const r = c.getBoundingClientRect(); w = r.width; h = r.height; c.width = w * dpr; c.height = h * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }
    function frame() {
      ctx.clearRect(0, 0, w, h); t += 0.006;
      const cx = w * 0.5, cy = h * 0.62, maxR = Math.min(w, h) * 0.52;
      for (let i = 0; i < 4; i++) {
        const p = (t + i / 4) % 1;
        const rad = p * maxR;
        const o = (1 - p) * 0.5;
        ctx.beginPath(); ctx.arc(cx, cy, rad, 0, Math.PI * 2);
        ctx.strokeStyle = `rgba(${rgb},${o})`; ctx.lineWidth = 1.5; ctx.stroke();
      }
      // center node
      ctx.beginPath(); ctx.arc(cx, cy, 5, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(${rgb},0.95)`; ctx.shadowColor = `rgba(${rgb},1)`; ctx.shadowBlur = 14; ctx.fill(); ctx.shadowBlur = 0;
      if (run && !reduce) raf = requestAnimationFrame(frame);
    }
    resize(); frame(); if (!reduce) raf = requestAnimationFrame(frame);
    const onR = () => resize(); window.addEventListener('resize', onR);
    const onV = () => { if (document.hidden) { run = false; cancelAnimationFrame(raf); } else if (!reduce) { run = true; raf = requestAnimationFrame(frame); } };
    document.addEventListener('visibilitychange', onV);
    return () => { run = false; cancelAnimationFrame(raf); window.removeEventListener('resize', onR); document.removeEventListener('visibilitychange', onV); };
  }, []);
  return <canvas ref={ref} aria-hidden="true" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />;
}

// Voice equalizer — Voice AI (green).
function BarsCanvas({ rgb }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const c = ref.current; if (!c) return;
    const ctx = c.getContext('2d');
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    let w, h, dpr, raf, run = true, t = 0, n = 0, ph = [];
    function resize() { dpr = Math.min(window.devicePixelRatio || 1, 2); const r = c.getBoundingClientRect(); w = r.width; h = r.height; c.width = w * dpr; c.height = h * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); n = Math.max(18, Math.round(w / 14)); ph = new Array(n).fill(0).map((_, i) => i * 0.5 + Math.random()); }
    function frame() {
      ctx.clearRect(0, 0, w, h); t += 0.05;
      const gap = 5, bw = (w - gap * (n - 1)) / n, cy = h * 0.62;
      for (let i = 0; i < n; i++) {
        const env = 0.35 + 0.65 * Math.abs(Math.sin(i / n * Math.PI)); // taller in middle
        const amp = (0.2 + 0.8 * (0.5 + 0.5 * Math.sin(t + ph[i]))) * env;
        const bh = Math.max(4, amp * h * 0.6);
        const x = i * (bw + gap), y = cy - bh / 2;
        ctx.fillStyle = `rgba(${rgb},${0.45 + amp * 0.5})`;
        const r = Math.min(bw / 2, 3);
        ctx.beginPath();
        if (ctx.roundRect) ctx.roundRect(x, y, bw, bh, r); else ctx.rect(x, y, bw, bh);
        ctx.fill();
      }
      if (run && !reduce) raf = requestAnimationFrame(frame);
    }
    resize(); frame(); if (!reduce) raf = requestAnimationFrame(frame);
    const onR = () => resize(); window.addEventListener('resize', onR);
    const onV = () => { if (document.hidden) { run = false; cancelAnimationFrame(raf); } else if (!reduce) { run = true; raf = requestAnimationFrame(frame); } };
    document.addEventListener('visibilitychange', onV);
    return () => { run = false; cancelAnimationFrame(raf); window.removeEventListener('resize', onR); document.removeEventListener('visibilitychange', onV); };
  }, []);
  return <canvas ref={ref} aria-hidden="true" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />;
}

function ExpandIcon({ tone }) {
  const c = { purple: 'var(--purple)', teal: 'var(--teal)', green: 'var(--success)', amber: 'var(--warning)' }[tone];
  return (
    <span style={{ width: 32, height: 32, borderRadius: 8, background: 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: c, flexShrink: 0 }}>
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
        <polyline points="15 3 21 3 21 9" /><polyline points="9 21 3 21 3 15" /><line x1="21" y1="3" x2="14" y2="10" /><line x1="3" y1="21" x2="10" y2="14" />
      </svg>
    </span>
  );
}

/* ── Floating overlay chips per product ── */
function CommunityChip() {
  return (
    <div style={{ position: 'absolute', left: 22, bottom: 56, right: 22, background: 'rgba(255,255,255,.82)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)', border: '1px solid var(--border)', borderRadius: 12, padding: '12px 14px', boxShadow: '0 8px 28px rgba(82,64,228,.14)', display: 'flex', alignItems: 'center', gap: 11 }}>
      <div style={{ display: 'flex' }}>
        {['NC', 'AR', 'SP'].map((x, i) => (
          <span key={x} style={{ marginLeft: i ? -8 : 0, width: 26, height: 26, borderRadius: '50%', border: '2px solid #fff', background: i === 0 ? 'var(--grad-brand)' : 'var(--surface-3)', color: i === 0 ? '#fff' : 'var(--ink-soft)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-display)', fontSize: 10, fontWeight: 700 }}>{x}</span>
        ))}
      </div>
      <div style={{ flex: 1 }}>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600, color: 'var(--ink)', letterSpacing: '-0.01em' }}>+247 members today</div>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--ink-muted)' }}>Free → Legend tiers</div>
      </div>
      <span style={{ fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 600, color: 'var(--success)' }}>↑ 12%</span>
    </div>
  );
}

function ConnectChip() {
  return (
    <div style={{ position: 'absolute', left: 22, bottom: 56, right: 22, display: 'flex', gap: 8 }}>
      {[['SMS', '99.9%'], ['eSIM', 'Live'], ['Voice', '99%']].map(([k, v]) => (
        <div key={k} style={{ flex: 1, background: 'rgba(255,255,255,.82)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)', border: '1px solid var(--border)', borderRadius: 10, padding: '9px 10px', textAlign: 'center' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 14, fontWeight: 700, color: 'var(--ink)', letterSpacing: '-0.02em' }}>{v}</div>
          <div style={{ fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--ink-muted)' }}>{k}</div>
        </div>
      ))}
    </div>
  );
}

function VoiceChip() {
  return (
    <div style={{ position: 'absolute', left: 22, bottom: 56, background: 'rgba(255,255,255,.82)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)', border: '1px solid var(--border)', borderRadius: 12, padding: '10px 14px', boxShadow: '0 8px 24px rgba(11,166,112,.14)', display: 'flex', alignItems: 'center', gap: 9 }}>
      <span style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--success)', boxShadow: '0 0 8px var(--success)' }} />
      <span style={{ fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>"Your reward is ready" — EN · TH</span>
    </div>
  );
}

function CoreAIChip() {
  return (
    <div style={{ position: 'absolute', left: 22, bottom: 56, right: 22, background: 'rgba(255,255,255,.82)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)', border: '1px solid var(--border)', borderRadius: 12, padding: '12px 14px', boxShadow: '0 8px 28px rgba(217,119,6,.14)', display: 'flex', alignItems: 'center', gap: 11 }}>
      <span style={{ width: 8, height: 8, borderRadius: '50%', background: 'var(--warning)', boxShadow: '0 0 8px var(--warning)' }} />
      <div style={{ flex: 1 }}>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600, color: 'var(--ink)', letterSpacing: '-0.01em' }}>Answered 42 calls today</div>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--ink-muted)' }}>Phone · SMS · Messenger</div>
      </div>
    </div>
  );
}

const PRODUCTS = [
  {
    label: 'Core AI', icon: 'building', tone: 'amber', status: 'dev',
    headline: 'Your AI employee for hospitality',
    desc: 'Answers calls, takes orders, and manages reservations — across every channel, 24/7.',
    longDesc: 'An AI employee for hospitality and community-driven businesses — answers phone calls, manages reservations, takes orders, and handles FAQs across SMS, Facebook Messenger, and Google Business Messages. Also runs automated engagement campaigns and multi-language conversational support, so your team never misses a guest or member.',
    pills: ['Voice answering', 'Reservations', 'From $299/mo', 'Multi-language'],
    features: ['Answers inbound phone calls, 24/7', 'Manages reservations and table/room bookings', 'Takes orders by phone, SMS, or chat', 'Answers guest &amp; member FAQs instantly', 'Sends confirmations and reminders by SMS', 'Handles Facebook Messenger + Google Business Messages', 'Automated engagement campaigns', 'Multi-language (EN + TH + more)'],
    highlights: [['24/7', 'Always on'], ['5 channels', 'Phone, SMS, chat +'], ['In design', 'Coming to partners']],
    href: 'core-ai.html',
    bg: 'linear-gradient(180deg, #FFFBF5 0%, #FCF1E2 100%)',
    rgb: '217,119,6', Visual: RingsCanvas, Chip: CoreAIChip,
  },
  {
    label: 'CommunityOS', icon: 'community', tone: 'purple', status: 'dev',
    headline: 'Convert followers into paying members',
    desc: 'Memberships, missions, referrals, and a white-label rewards marketplace.',
    longDesc: 'A complete community growth platform — memberships, engagement missions, referral programs, and a rewards marketplace, white-labeled for your brand. Every member action compounds into recurring revenue.',
    pills: ['Loyalty engine', 'Membership tiers', 'Rewards marketplace'],
    features: ['Loyalty engine + member engagement scoring', 'Membership tiers (Free → Legend)', 'Engagement engine — tasks, surveys, community challenges', 'Rewards marketplace — telecom, banking, digital rewards', 'Partner ecosystem API'],
    highlights: [['Free → Legend', 'Membership tiers'], ['White-label', 'For your brand'], ['Compounding', 'Recurring revenue']],
    href: 'communityos.html',
    bg: 'linear-gradient(180deg, #FBFAFF 0%, #EFEBFF 100%)',
    rgb: '124,111,240', Visual: ParticleField, Chip: CommunityChip,
  },
  {
    label: 'Connect', icon: 'signal', tone: 'teal', status: 'active',
    headline: 'Launch branded mobile — live in 60 days',
    desc: 'MVNO-as-a-Service: carrier-grade SMS, Voice, OTP & eSIM infrastructure.',
    longDesc: 'MVNO-as-a-Service infrastructure for brands and fintechs that want branded mobile — without building carrier infrastructure. Carrier-grade SMS, Voice, OTP and eSIM, compliant from day one.',
    pills: ['SIM + eSIM API', 'Subscriber dashboard', 'CALEA + E911'],
    features: ['Carrier wholesale access (US + SEA)', 'SIM + eSIM provisioning API', 'Subscriber management dashboard', 'CALEA + E911 compliance built-in', 'Production-ready in 60 days'],
    highlights: [['60 days', 'To production'], ['APAC + U.S.', 'Coverage'], ['3GPP R17', 'Protocol']],
    href: 'connect.html',
    bg: 'linear-gradient(180deg, #F8FCFF 0%, #E2F4FF 100%)',
    rgb: '14,165,233', Visual: RingsCanvas, Chip: ConnectChip,
  },
];

function ProductModal({ p, onClose }) {
  const [on, setOn] = React.useState(false);
  React.useEffect(() => { const r = requestAnimationFrame(() => setOn(true)); return () => cancelAnimationFrame(r); }, []);
  const toneColor = { purple: 'var(--purple)', teal: 'var(--teal)', green: 'var(--success)', amber: 'var(--warning)' }[p.tone];
  const tint = { purple: 'var(--purple-soft)', teal: 'var(--teal-soft)', green: 'var(--success-soft)', amber: 'var(--warning-soft)' }[p.tone];
  const Visual = p.Visual;
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 400, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, background: 'rgba(8,7,26,.55)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)' }}>
      <div onClick={e => e.stopPropagation()} className="pm-grid" style={{ position: 'relative', width: 'min(960px,100%)', maxHeight: '88vh', background: '#fff', borderRadius: 22, overflow: 'hidden', display: 'grid', gridTemplateColumns: 'minmax(0,42%) 1fr', boxShadow: '0 40px 100px rgba(8,7,26,.5)', transform: on ? 'none' : 'translateY(16px) scale(.985)', transition: 'transform .35s var(--ease-out)' }}>
        <button onClick={onClose} aria-label="Close" style={{ position: 'absolute', top: 16, right: 16, zIndex: 2, width: 34, height: 34, borderRadius: 9, border: '1px solid var(--border)', background: 'rgba(255,255,255,.9)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--ink-soft)' }}>
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>
        </button>
        <div className="pm-visual" style={{ position: 'relative', overflow: 'hidden', background: p.bg, minHeight: 320 }}>
          <Visual rgb={p.rgb} />
          <div style={{ position: 'absolute', top: 22, left: 22 }}><StatusTag status={p.status} /></div>
        </div>
        <div style={{ padding: '34px 36px', overflowY: 'auto' }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600, color: toneColor, letterSpacing: '.02em', marginBottom: 12 }}>
            <span style={{ width: 28, height: 28, borderRadius: 8, background: tint, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <svg width="16" height="16" style={{ color: toneColor }}><use href={`${PROD_SP}#ico-${p.icon}`} /></svg>
            </span>{p.label}
          </span>
          <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 30, fontWeight: 800, color: 'var(--ink)', letterSpacing: '-0.03em', lineHeight: 1.1, margin: '0 0 12px' }}>{p.headline}</h2>
          <p style={{ fontFamily: 'var(--font-body)', fontSize: 16, color: 'var(--ink-soft)', lineHeight: 1.7, margin: '0 0 22px', letterSpacing: '-0.01em' }}>{p.longDesc}</p>
          <div style={{ display: 'flex', gap: 10, marginBottom: 24 }}>
            {p.highlights.map(([v, l]) => (
              <div key={l} style={{ flex: 1, background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 10, padding: '12px' }}>
                <div style={{ fontFamily: 'var(--font-display)', fontSize: 16, fontWeight: 700, color: 'var(--ink)', letterSpacing: '-0.02em', lineHeight: 1.1 }}>{v}</div>
                <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--ink-muted)', marginTop: 3 }}>{l}</div>
              </div>
            ))}
          </div>
          <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.12em', color: 'var(--ink-muted)', marginBottom: 12 }}>What's included</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 26 }}>
            {p.features.map(f => (
              <div key={f} style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
                <span style={{ width: 20, height: 20, borderRadius: '50%', background: tint, color: toneColor, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, marginTop: 1 }}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>
                </span>
                <span style={{ fontFamily: 'var(--font-body)', fontSize: 15, color: 'var(--ink-soft)', lineHeight: 1.5, letterSpacing: '-0.01em' }}>{f}</span>
              </div>
            ))}
          </div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            <Button variant="primary" arrow href="#contact" onClick={onClose}>Book a Strategy Call</Button>
            {p.href ? <Button variant="outline" href={p.href}>View full page</Button> : null}
          </div>
        </div>
      </div>
    </div>
  );
}

function ProductCard({ p, onOpen }) {
  const [hover, setHover] = React.useState(false);
  const toneColor = { purple: 'var(--purple)', teal: 'var(--teal)', green: 'var(--success)', amber: 'var(--warning)' }[p.tone];
  const tint = { purple: 'var(--purple-soft)', teal: 'var(--teal-soft)', green: 'var(--success-soft)', amber: 'var(--warning-soft)' }[p.tone];
  const Visual = p.Visual, Chip = p.Chip;
  return (
    <div
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      onClick={onOpen}
      style={{
        display: 'flex', flexDirection: 'column', background: '#fff', border: `1px solid ${hover ? 'var(--border-strong)' : 'var(--border)'}`,
        borderRadius: 20, overflow: 'hidden', minHeight: 500, height: '100%', position: 'relative', cursor: 'pointer',
        boxShadow: hover ? 'var(--shadow-glow)' : 'var(--shadow-card)', transform: hover ? 'translateY(-4px)' : 'none',
        transition: 'transform .3s var(--ease-out), box-shadow .3s, border-color .3s',
      }}
    >
      <div style={{ padding: '26px 26px 18px' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 16 }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600, color: toneColor, letterSpacing: '.02em' }}>
            <span style={{ width: 26, height: 26, borderRadius: 7, background: tint, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <svg width="15" height="15" style={{ color: toneColor }}><use href={`${PROD_SP}#ico-${p.icon}`} /></svg>
            </span>
            {p.label}
          </span>
          <button onClick={(e) => { e.stopPropagation(); onOpen && onOpen(); }} aria-label={`Open ${p.label} details`} style={{ border: 'none', background: 'none', padding: 0, cursor: 'pointer' }}>
            <ExpandIcon tone={p.tone} />
          </button>
        </div>
        <h3 style={{ fontFamily: 'var(--font-display)', fontSize: 25, fontWeight: 800, color: 'var(--ink)', letterSpacing: '-0.03em', lineHeight: 1.12, margin: '0 0 10px', textWrap: 'balance' }}>{p.headline}</h3>
        <p style={{ fontFamily: 'var(--font-body)', fontSize: 15, color: 'var(--ink-soft)', lineHeight: 1.6, margin: '0 0 14px', letterSpacing: '-0.01em' }}>{p.desc}</p>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
          {p.pills.map(t => (
            <span key={t} style={{ fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 500, color: 'var(--ink-soft)', background: 'var(--surface-2)', border: '1px solid var(--border)', padding: '4px 10px', borderRadius: 999, letterSpacing: '-0.01em', whiteSpace: 'nowrap' }}>{t}</span>
          ))}
        </div>
      </div>
      <div style={{ position: 'relative', flex: 1, minHeight: 210, overflow: 'hidden', background: p.bg, borderTop: '1px solid var(--border)' }}>
        <Visual rgb={p.rgb} />
        <Chip />
        <div style={{ position: 'absolute', left: 22, bottom: 18 }}><StatusTag status={p.status} /></div>
      </div>
    </div>
  );
}

function Products() {
  const { Reveal } = window;
  const Wrap = Reveal || (({ children }) => <div>{children}</div>);
  const [openIdx, setOpenIdx] = React.useState(null);
  React.useEffect(() => {
    const onKey = e => { if (e.key === 'Escape') setOpenIdx(null); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);
  React.useEffect(() => {
    document.body.style.overflow = openIdx != null ? 'hidden' : '';
    return () => { document.body.style.overflow = ''; };
  }, [openIdx]);
  return (
    <section id="products" style={{ padding: '100px 5%' }}>
      <div style={{ maxWidth: 'var(--container)', margin: '0 auto' }}>
        <Eyebrow>Products</Eyebrow>
        <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 50, fontWeight: 800, letterSpacing: '-0.04em', color: 'var(--ink)', margin: '10px 0 14px', lineHeight: 1.07 }}>One platform.<br />Infinite growth.</h2>
        <p style={{ fontFamily: 'var(--font-body)', fontSize: 21, color: 'var(--ink-soft)', maxWidth: 520, lineHeight: 1.7, margin: '0 0 52px', letterSpacing: '-0.01em' }}>Three integrated products. One engagement infrastructure. Built for organizations operating at scale.</p>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 22, alignItems: 'stretch' }} className="prod-grid">
          {PRODUCTS.map((p, i) => <Wrap key={p.label} delay={i * 0.08} style={{ display: 'flex' }}><ProductCard p={p} onOpen={() => setOpenIdx(i)} /></Wrap>)}
        </div>
        {openIdx != null ? ReactDOM.createPortal(<ProductModal p={PRODUCTS[openIdx]} onClose={() => setOpenIdx(null)} />, document.body) : null}
      </div>
    </section>
  );
}
window.Products = Products;
