/* Brainstorm X — Name Nebula: shared helpers + presentational pieces.
   Loaded before flow-arena.jsx. Exposes window.BXNebula + window.BXheatFromVotes. */
(function () {
  const { useState, useEffect, useRef } = React;
  const DS = window.BrainstormXDesignSystem_f2a458;
  const { Button, Card, NameCard, KeywordOrb, Badge, Avatar, AvatarStack, IconButton, Input, Chip } = DS;
  const I = window.BXIcons;
  const clamp = (v, a, b) => Math.min(b, Math.max(a, v));

  const INVITED = (window.BXpeople ? window.BXpeople.length : 3) + 1;
  window.BXheatFromVotes = function (up = 0, down = 0) {
    const net = up - down;
    const hi = Math.ceil(INVITED * 1.5), lo = Math.ceil(INVITED * 0.8);
    if (net >= hi) return 'on_fire';
    if (net >= lo) return 'hot';
    if (net >= 1) return 'warm';
    if (net <= -hi) return 'frozen';
    if (net <= -lo) return 'cold';
    return 'neutral';
  };

  function hash(str) { let h = 0; for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) >>> 0; return h; }

  function deriveVoters(n) {
    const people = window.BXpeople || [];
    const c = n.counts || {};
    const tally = people.map((p) => ({ name: p.name, up: 0, down: 0 }));
    ['up', 'down'].forEach((type) => {
      const total = c[type] || 0;
      for (let i = 0; i < total; i++) tally[i % (people.length || 1)][type]++;
    });
    return tally.filter((t) => t.up || t.down);
  }

  // names related by shared keyword / style / wave
  function relatedIds(name, names) {
    if (!name) return new Set();
    const ks = new Set(name.keywords || []);
    const ids = new Set();
    names.forEach((m) => {
      if (m.id === name.id) return;
      const shareKw = (m.keywords || []).some((k) => ks.has(k));
      if (shareKw || m.style === name.style || (m.wave != null && m.wave === name.wave)) ids.add(m.id);
    });
    return ids;
  }

  function whyText(n, names) {
    const sameStyle = names.filter((m) => m.style === n.style && (m.counts ? (m.counts.up || 0) > (m.counts.down || 0) : false)).length;
    if (n.heat === 'on_fire' || n.heat === 'hot') {
      return `Rising — ${sameStyle > 1 ? `${sameStyle} teammates favour ${n.style.replace('_', ' ')} names` : 'the team keeps voting it up'}.`;
    }
    if (n.heat === 'frozen' || n.heat === 'cold') return 'Cooling — the team is moving away from this direction.';
    if (n.custom) return 'Added by your team — not yet generated by the AI.';
    return 'Undecided — needs a few more votes to find its place.';
  }

  function domainRows(name) {
    const root = name.toLowerCase().replace(/[^a-z0-9]/g, '');
    return [['.com', 0], ['.co.uk', 1], ['.io', 2], ['.app', 3]].map(([tld]) => {
      const taken = tld === '.com' ? hash(root) % 3 !== 0 : hash(root + tld) % 2 === 0;
      return { domain: root + tld, available: !taken };
    });
  }
  function legalRows(name) {
    const root = name.replace(/[^A-Za-z0-9]/g, '');
    const cands = [`${root}ly`, `${root} Co`, `${root.slice(0, Math.max(3, root.length - 1))}o`];
    const states = [
      { label: 'No exact mark found', tone: 'success' }, { label: 'Similar mark — review', tone: 'fire' },
      { label: 'Possible conflict', tone: 'fire' }, { label: 'Clear in your class', tone: 'success' },
    ];
    return cands.map((c, i) => ({ name: c, ...states[(hash(c) + i) % states.length] }));
  }

  // ---- one-off styles + keyframes --------------------------------------
  {
    const css = `
      @keyframes bxFloatY{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
      @keyframes bxFireGlow{0%,100%{box-shadow:0 0 16px 1px rgba(255,90,55,.55), inset 0 0 18px rgba(255,130,50,.32)}50%{box-shadow:0 0 32px 5px rgba(255,60,40,.85), inset 0 0 30px rgba(255,140,45,.55)}}
      @keyframes bxIceGlow{0%,100%{box-shadow:0 0 18px 1px rgba(120,200,255,.45), inset 0 0 22px rgba(185,232,255,.38)}50%{box-shadow:0 0 28px 4px rgba(150,215,255,.65), inset 0 0 32px rgba(205,242,255,.55)}}
      @keyframes bxFrost{0%,100%{opacity:.5}50%{opacity:.95}}
      @keyframes bxPortalPulse{0%,100%{box-shadow:0 0 22px rgba(45,226,230,.4)}50%{box-shadow:0 0 40px rgba(45,226,230,.75)}}
      @keyframes bxPortalCore{0%{transform:translate(-50%,-50%) scale(0.2);opacity:0}30%{opacity:1}100%{transform:translate(-50%,-50%) scale(2.4);opacity:0}}
      @keyframes bxSpark{0%{transform:translate(-50%,-50%) scale(0.2);opacity:1}100%{transform:translate(-50%,-50%) scale(2.6);opacity:0}}
      @keyframes bxSpin{to{transform:rotate(360deg)}}
      @keyframes bxBannerIn{0%{transform:transl(-50%,0) translateY(-14px);opacity:0}100%{opacity:1}}
      .bx-nc-mount [aria-label="Shortlist"]:not([data-overlay]){display:none !important}
      .bx-nc-mount [aria-label="Strong contender"]{display:none !important}
      .bx-nc-mount [aria-label="Move away from this"]{display:none !important}
      .bx-scroll{scrollbar-width:thin;scrollbar-color:rgba(160,150,255,0.45) transparent;}
      .bx-scroll::-webkit-scrollbar{width:9px;height:9px;}
      .bx-scroll::-webkit-scrollbar-track{background:transparent;margin:10px 0;}
      .bx-scroll::-webkit-scrollbar-thumb{background:linear-gradient(180deg,var(--neon-violet),var(--neon-cyan));border-radius:999px;border:2px solid transparent;background-clip:padding-box;}
      .bx-scroll::-webkit-scrollbar-thumb:hover{background:var(--neon-cyan);background-clip:padding-box;}`;
    let el = document.getElementById('bx-arena-extra');
    if (!el) { el = document.createElement('style'); el.id = 'bx-arena-extra'; document.head.appendChild(el); }
    el.textContent = css;
  }

  function Stars({ n = 70 }) {
    const stars = React.useMemo(() => Array.from({ length: n }, () => ({
      top: Math.random() * 100, left: Math.random() * 100, s: Math.random() * 2 + 1,
      o: Math.random() * 0.6 + 0.15, glow: Math.random() > 0.88,
    })), [n]);
    return <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none' }}>
      {stars.map((s, i) => <span key={i} style={{ position: 'absolute', top: s.top + '%', left: s.left + '%', width: s.s, height: s.s, borderRadius: '50%', background: '#fff', opacity: s.o, boxShadow: s.glow ? '0 0 6px var(--neon-cyan)' : 'none' }} />)}
    </div>;
  }

  function FireFX() {
    return (
      <div style={{ position: 'absolute', inset: -3, borderRadius: 'calc(var(--radius-xl) + 3px)', pointerEvents: 'none', zIndex: 3 }}>
        <div style={{ position: 'absolute', inset: 0, borderRadius: 'inherit', border: '1.5px solid rgba(255,150,70,0.75)', animation: 'bxFireGlow 1.3s var(--ease-in-out) infinite' }} />
      </div>
    );
  }
  function FrostFX() {
    return (
      <div style={{ position: 'absolute', inset: -3, borderRadius: 'calc(var(--radius-xl) + 3px)', pointerEvents: 'none', zIndex: 3, overflow: 'hidden' }}>
        <div style={{
          position: 'absolute', inset: 0, borderRadius: 'inherit', border: '1.5px solid rgba(190,235,255,0.7)',
          background: 'radial-gradient(130% 70% at 50% 0%, rgba(205,240,255,0.22), transparent 46%), radial-gradient(130% 70% at 50% 100%, rgba(170,220,255,0.20), transparent 46%), radial-gradient(80% 120% at 0% 50%, rgba(190,235,255,0.18), transparent 42%), radial-gradient(80% 120% at 100% 50%, rgba(190,235,255,0.18), transparent 42%)',
          animation: 'bxIceGlow 3s var(--ease-in-out) infinite',
        }} />
        <div style={{ position: 'absolute', inset: 0, borderRadius: 'inherit', background: 'linear-gradient(135deg, rgba(225,247,255,0.12), transparent 42%, rgba(200,238,255,0.10))', animation: 'bxFrost 3.4s var(--ease-in-out) infinite' }} />
      </div>
    );
  }

  function VoterAvatars({ voters, size = 22, popUp = true }) {
    const [open, setOpen] = useState(null);
    if (!voters.length) return null;
    return (
      <div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
        {voters.slice(0, 5).map((t, i) => (
          <span key={t.name} onClick={(e) => { e.stopPropagation(); setOpen(open === i ? null : i); }}
            title={`${t.name} — ${t.up} up · ${t.down} down`}
            style={{ marginLeft: i === 0 ? 0 : -size * 0.34, zIndex: open === i ? 30 : voters.length - i, cursor: 'pointer', position: 'relative' }}>
            <Avatar name={t.name} size={size} ring={open === i} style={{ border: '2px solid var(--surface-2)' }} />
            {open === i && (
              <div onClick={(e) => e.stopPropagation()} style={{
                position: 'absolute', [popUp ? 'bottom' : 'top']: 'calc(100% + 8px)', left: '50%', transform: 'translateX(-50%)',
                zIndex: 60, width: 150, padding: '10px 12px', borderRadius: 'var(--radius-md)',
                background: 'var(--surface-glass-strong)', border: '1px solid var(--line-2)',
                backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', boxShadow: 'var(--shadow-lg)',
              }}>
                <div style={{ fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 13, color: 'var(--text-1)', marginBottom: 8 }}>{t.name}</div>
                <div style={{ display: 'flex', gap: 14 }}>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--vote-up)' }}><I.ChevronUp size={15} />{t.up}</span>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--vote-down)' }}><I.ChevronDown size={15} />{t.down}</span>
                </div>
              </div>
            )}
          </span>
        ))}
      </div>
    );
  }

  function SectionLabel({ children, icon }) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 7, fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.1em', margin: '0 0 10px' }}>
        {icon}{children}
      </div>
    );
  }

  function Header({ wave, project = 'Pickle Paws', onGenerate, generating, goto, onInvite, onMoreNames, onAddCustom, sprint, onToggleSprint }) {
    return (
      <header style={{
        position: 'absolute', top: 0, left: 0, right: 0, zIndex: 30, display: 'flex', alignItems: 'center',
        justifyContent: 'space-between', padding: '14px 22px', gap: 16,
        background: 'rgba(10,10,24,0.5)', borderBottom: '1px solid var(--line-1)',
        backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <img src="../assets/mark-spark.svg" width="28" height="28" alt="" />
          <div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 15, color: 'var(--text-1)', lineHeight: 1.1 }}>{project}</div>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>Name Nebula · Wave {wave}</div>
          </div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <button onClick={onInvite} title="Invite collaborators" style={{ display: 'flex', alignItems: 'center', gap: 9, background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>
            <AvatarStack people={window.BXpeople} max={4} size={30} />
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--text-3)', fontFamily: 'var(--font-mono)' }}>
              <span style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--neon-lime)', boxShadow: '0 0 8px var(--neon-lime)' }} />{INVITED} live
            </span>
          </button>
          <Button variant={sprint ? 'fire' : 'secondary'} size="sm" onClick={onToggleSprint} iconLeft={<span style={{ fontSize: 13 }}>🔥</span>}>{sprint ? 'End sprint' : 'Fire round'}</Button>
          <Button variant="secondary" size="sm" onClick={onInvite} iconLeft={<I.Users size={16} />}>Invite</Button>
          <Button variant="secondary" size="sm" onClick={() => goto('shortlist')} iconLeft={<I.Star size={16} />}>Shortlist</Button>
          <Button variant="ghost" size="sm" onClick={onAddCustom} iconLeft={<I.Plus size={16} />}>Add custom</Button>
          <Button variant="secondary" size="md" onClick={onMoreNames} iconLeft={<I.Sparkles size={16} />}>Generate more</Button>
          <Button variant="fire" size="md" onClick={onGenerate} disabled={generating} iconLeft={<I.Zap size={18} />}>
            {generating ? 'Evolving…' : 'Generate next wave'}
          </Button>
        </div>
      </header>
    );
  }

  function ZoomControl({ zoom, setZoom, onReset }) {
    const btn = { width: 30, height: 30, borderRadius: 8, border: '1px solid var(--line-2)', background: 'var(--surface-2)', color: 'var(--text-1)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' };
    return (
      <div style={{ position: 'absolute', right: 18, top: 84, zIndex: 26, display: 'flex', alignItems: 'center', gap: 6, padding: 6, borderRadius: 'var(--radius-pill)', background: 'var(--surface-glass)', border: '1px solid var(--line-2)', backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)' }}>
        <button style={btn} aria-label="Zoom out" onClick={() => setZoom((z) => clamp(z - 0.15, 0.3, 1.7))}><span style={{ fontSize: 18, lineHeight: 1 }}>−</span></button>
        <button onClick={onReset} title="Reset view" style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--text-2)', width: 44, textAlign: 'center', background: 'none', border: 'none', cursor: 'pointer' }}>{Math.round(zoom * 100)}%</button>
        <button style={btn} aria-label="Zoom in" onClick={() => setZoom((z) => clamp(z + 0.15, 0.3, 1.7))}><I.Plus size={16} /></button>
      </div>
    );
  }

  function Welcome() {
    const [show, setShow] = useState(true);
    useEffect(() => { const t = setTimeout(() => setShow(false), 30000); return () => clearTimeout(t); }, []);
    if (!show) {
      return (
        <button onClick={() => setShow(true)} title="Show welcome" aria-label="Show welcome" style={{
          position: 'absolute', right: 18, bottom: 300, zIndex: 27, width: 40, height: 40, borderRadius: '50%',
          display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
          background: 'var(--surface-glass)', border: '1px solid var(--line-2)', color: 'var(--neon-cyan)',
          backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', boxShadow: 'var(--shadow-lg)',
        }}><I.Sparkles size={18} /></button>
      );
    }
    return (
      <div style={{
        position: 'absolute', right: 18, bottom: 300, zIndex: 27, width: 264, padding: '14px 16px',
        background: 'var(--surface-glass)', border: '1px solid var(--line-2)', borderRadius: 'var(--radius-lg)',
        backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', boxShadow: 'var(--shadow-lg)',
      }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8, marginBottom: 6 }}>
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.1em' }}>Welcome</span>
          <button onClick={() => setShow(false)} aria-label="Dismiss welcome" style={{ display: 'flex', background: 'none', border: 'none', color: 'var(--text-3)', cursor: 'pointer', padding: 0 }}><I.X size={14} /></button>
        </div>
        <span style={{ fontSize: 12.5, color: 'var(--text-2)', lineHeight: 1.5 }}>
          <strong style={{ color: 'var(--neon-cyan)' }}>This is your Name Nebula.</strong> Liked names grow and drift to the centre; rejected ones cool and drift out. Boost a keyword to pull its names closer. Hover a name to see its family. Drag a name into a portal to evolve it, or onto another to fuse them.
        </span>
      </div>
    );
  }

  function ActivityFeed({ events }) {
    return (
      <div style={{
        position: 'absolute', right: 18, bottom: 84, zIndex: 25, width: 264, maxHeight: 200, overflow: 'hidden',
        padding: '14px 16px', background: 'var(--surface-glass)', border: '1px solid var(--line-2)',
        borderRadius: 'var(--radius-lg)', backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', boxShadow: 'var(--shadow-lg)',
      }}>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 10 }}>Activity</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
          {events.slice(0, 5).map((e, i) => (
            <div key={e.k || i} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, color: 'var(--text-2)', opacity: i === 0 ? 1 : 1 - i * 0.13 }}>
              <Avatar name={e.who} size={20} />
              <span><strong style={{ color: 'var(--text-1)' }}>{e.who.split(' ')[0]}</strong> {e.what}</span>
            </div>
          ))}
        </div>
      </div>
    );
  }

  // Banner shown after a wave: AI explainability
  function WaveBanner({ text }) {
    if (!text) return null;
    return (
      <div style={{
        position: 'absolute', top: 78, left: '50%', transform: 'translateX(-50%)', zIndex: 36,
        display: 'flex', alignItems: 'center', gap: 10, padding: '10px 18px', borderRadius: 'var(--radius-pill)',
        background: 'var(--surface-glass-strong)', border: '1px solid color-mix(in srgb, var(--neon-cyan) 45%, transparent)',
        backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', boxShadow: 'var(--glow-cyan)',
        animation: 'bxBannerIn 0.4s var(--ease-out)',
      }}>
        <I.Sparkles size={16} style={{ color: 'var(--neon-cyan)' }} />
        <span style={{ fontSize: 13.5, color: 'var(--text-1)', fontWeight: 600 }}>{text}</span>
      </div>
    );
  }

  function SprintBar({ seconds }) {
    return (
      <div style={{
        position: 'absolute', top: 130, left: '50%', transform: 'translateX(-50%)', zIndex: 36,
        display: 'flex', alignItems: 'center', gap: 12, padding: '8px 16px', borderRadius: 'var(--radius-pill)',
        background: 'color-mix(in srgb, var(--heat-fire) 20%, var(--surface-glass-strong))',
        border: '1px solid color-mix(in srgb, var(--heat-fire) 55%, transparent)',
        backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)', boxShadow: 'var(--glow-fire)',
      }}>
        <span style={{ fontSize: 15 }}>🔥</span>
        <span style={{ fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 13, color: 'var(--text-1)' }}>Fire round — vote fast!</span>
        <span style={{ fontFamily: 'var(--font-mono)', fontSize: 15, fontWeight: 700, color: 'var(--heat-fire)' }}>0:{String(seconds).padStart(2, '0')}</span>
      </div>
    );
  }

  // Evolve portals — fade in while dragging a name
  const PORTALS = [
    { id: 'shorter', label: 'Shorter', ic: 'Minimize' },
    { id: 'premium', label: 'More premium', ic: 'Crown' },
    { id: 'playful', label: 'More playful', ic: 'Sparkles' },
    { id: 'unusual', label: 'More unusual', ic: 'Wand' },
    { id: 'british', label: 'More British', ic: 'Flag' },
    { id: 'like', label: 'More like this', ic: 'Copy' },
  ];
  function Portals({ active, hoverId }) {
    return (
      <div style={{
        position: 'absolute', top: 70, left: 0, right: 0, zIndex: 34, display: 'flex', justifyContent: 'center',
        gap: 10, padding: '12px', flexWrap: 'wrap', pointerEvents: 'none',
        opacity: active ? 1 : 0, transform: active ? 'translateY(0)' : 'translateY(-10px)',
        transition: 'opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out)',
      }}>
        {PORTALS.map((p) => {
          const on = hoverId === p.id;
          const Ic = I[p.ic] || I.Sparkles;
          return (
            <div key={p.id} data-portal={p.id} style={{
              display: 'flex', alignItems: 'center', gap: 8, padding: '10px 16px', borderRadius: 'var(--radius-pill)',
              background: on ? 'var(--neon-cyan)' : 'var(--surface-glass-strong)',
              color: on ? 'var(--accent-ink)' : 'var(--text-1)',
              border: `1.5px ${on ? 'solid' : 'dashed'} ${on ? 'var(--neon-cyan)' : 'color-mix(in srgb, var(--neon-cyan) 45%, transparent)'}`,
              backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)',
              boxShadow: on ? 'var(--glow-cyan)' : 'none', transform: on ? 'scale(1.08)' : 'scale(1)',
              transition: 'all var(--dur-fast) var(--ease-spring)', fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 13,
              animation: on ? 'none' : 'bxPortalPulse 2.4s var(--ease-in-out) infinite',
            }}>
              <Ic size={15} />{p.label}
            </div>
          );
        })}
      </div>
    );
  }

  function InviteModal({ onClose }) {
    const [emails, setEmails] = useState(['alex@studio.co.uk', 'priya@studio.co.uk']);
    const [val, setVal] = useState('');
    const [copied, setCopied] = useState(false);
    const [sent, setSent] = useState(false);
    const share = 'brainstorm-x.app/s/pickle-paws-9f3';
    const add = (e) => { e.preventDefault(); const v = val.trim(); if (v && !emails.includes(v)) setEmails([...emails, v]); setVal(''); };
    return (
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 80, background: 'rgba(6,6,14,0.65)', backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
        <Card glass glow="violet" onClick={(e) => e.stopPropagation()} style={{ width: 'min(520px,100%)', padding: '26px 28px', position: 'relative' }}>
          <div style={{ position: 'absolute', top: 18, right: 18 }}>
            <IconButton label="Close" variant="ghost" onClick={onClose}><I.X size={18} /></IconButton>
          </div>
          <span className="bx-eyebrow"><I.Users size={14} style={{ verticalAlign: 'middle', marginRight: 6 }} />Invite your team</span>
          <h2 style={{ fontSize: 24, margin: '10px 0 6px' }}>Bring people into this session</h2>
          <p style={{ fontSize: 14, color: 'var(--text-2)', marginBottom: 18 }}>They’ll get a secure link to vote, comment and shape the shortlist — no password needed. The more people vote, the sooner names catch fire.</p>
          <form onSubmit={add}><Input placeholder="name@company.co.uk — press enter to add" value={val} onChange={(e) => setVal(e.target.value)} iconLeft={<I.Mail size={18} />} /></form>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 12 }}>
            {emails.map((em) => <Chip key={em} onRemove={() => setEmails(emails.filter((x) => x !== em))} accent="var(--neon-violet)">{em}</Chip>)}
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 18, padding: '12px 14px', borderRadius: 'var(--radius-md)', background: 'var(--surface-inset)', border: '1px solid var(--line-1)' }}>
            <I.Link size={16} style={{ color: 'var(--neon-cyan)', flex: 'none' }} />
            <span style={{ flex: 1, fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--neon-cyan)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{share}</span>
            <Button variant="secondary" size="sm" onClick={() => setCopied(true)} iconLeft={<I.Copy size={15} />}>{copied ? 'Copied' : 'Copy'}</Button>
          </div>
          <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 18 }}>
            <Button onClick={() => setSent(true)} iconLeft={<I.Mail size={16} />}>{sent ? `Sent to ${emails.length} ✓` : `Send ${emails.length} invites`}</Button>
          </div>
        </Card>
      </div>
    );
  }

  function AddCustomModal({ onAdd, onClose }) {
    const [val, setVal] = useState('');
    const [styl, setStyl] = useState('brandable');
    const [added, setAdded] = useState([]);
    const STYLES = ['brandable', 'evocative', 'compound', 'real_word', 'short_phrase'];
    const submit = (e) => { e.preventDefault(); const v = val.trim(); if (!v) return; onAdd(v, styl); setAdded((a) => [v, ...a]); setVal(''); };
    return (
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 80, background: 'rgba(6,6,14,0.65)', backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
        <Card glass glow="cyan" onClick={(e) => e.stopPropagation()} style={{ width: 'min(460px,100%)', padding: '26px 28px', position: 'relative' }}>
          <div style={{ position: 'absolute', top: 18, right: 18 }}>
            <IconButton label="Close" variant="ghost" onClick={onClose}><I.X size={18} /></IconButton>
          </div>
          <span className="bx-eyebrow"><I.Sparkles size={14} style={{ verticalAlign: 'middle', marginRight: 6 }} />Add your own</span>
          <h2 style={{ fontSize: 24, margin: '10px 0 6px' }}>Add a custom name</h2>
          <p style={{ fontSize: 14, color: 'var(--text-2)', marginBottom: 18 }}>Got a name in mind? Drop it into the nebula to vote on it alongside the AI ideas.</p>
          <form onSubmit={submit}><Input placeholder="e.g. Pickle Paws" value={val} onChange={(e) => setVal(e.target.value)} iconLeft={<I.Sparkles size={18} />} /></form>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, margin: '14px 0 4px' }}>
            {STYLES.map((s) => <Chip key={s} size="sm" selected={styl === s} onClick={() => setStyl(s)}>{s.replace('_', ' ')}</Chip>)}
          </div>
          <div style={{ marginTop: 16 }}>
            <Button onClick={submit} fullWidth iconLeft={<I.Plus size={16} />}>Add to nebula</Button>
          </div>
          {added.length > 0 && (
            <div style={{ marginTop: 16 }}>
              <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8 }}>Added this session</div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
                {added.map((a, i) => <Badge key={i} tone="success" size="sm">{a}</Badge>)}
              </div>
            </div>
          )}
        </Card>
      </div>
    );
  }

  function DetailPanel({ name, names, onClose, onEvolve, onOpen, onVote }) {
    const variants = [['like', 'More like this'], ['shorter', 'Make it shorter'], ['premium', 'More premium'], ['playful', 'More playful']];
    const voters = deriveVoters(name);
    const domains = domainRows(name.name);
    const legal = legalRows(name.name);
    const related = [...relatedIds(name, names)].map((id) => names.find((m) => m.id === id)).filter(Boolean).slice(0, 6);
    return (
      <div className="bx-scroll" style={{
        position: 'absolute', top: 0, right: 0, bottom: 0, width: 360, zIndex: 40, padding: '78px 26px 26px',
        background: 'var(--surface-glass-strong)', borderLeft: '1px solid var(--line-2)',
        backdropFilter: 'blur(24px)', WebkitBackdropFilter: 'blur(24px)', boxShadow: 'var(--shadow-xl)', overflowY: 'auto',
      }}>
        <div style={{ position: 'absolute', top: 82, right: 22 }}>
          <IconButton label="Close" variant="ghost" onClick={onClose}><I.X size={18} /></IconButton>
        </div>
        <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.1em' }}>{name.style}</span>
        <h2 style={{ fontSize: 38, margin: '8px 0 4px' }}>{name.name}</h2>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--text-3)', marginBottom: 16 }}>{name.pronunciation}</div>
        <p style={{ fontSize: 15, color: 'var(--text-2)', lineHeight: 1.5, marginBottom: 14 }}>{name.reason}</p>
        {/* AI explainability */}
        <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start', padding: '10px 12px', marginBottom: 16, borderRadius: 'var(--radius-md)', background: 'color-mix(in srgb, var(--neon-cyan) 8%, transparent)', border: '1px solid color-mix(in srgb, var(--neon-cyan) 26%, transparent)' }}>
          <I.Sparkles size={15} style={{ color: 'var(--neon-cyan)', flexShrink: 0, marginTop: 2 }} />
          <span style={{ fontSize: 13, color: 'var(--text-2)', lineHeight: 1.45 }}>{whyText(name, names)}{name.wave ? ` · Wave ${name.wave}.` : ''}</span>
        </div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7, marginBottom: 18 }}>
          {name.keywords.map((k) => <Badge key={k} tone="cyan" mono>{k}</Badge>)}
        </div>
        <div style={{ display: 'flex', gap: 8, marginBottom: 20 }}>
          <div style={{ flex: 1, padding: '10px 12px', borderRadius: 'var(--radius-md)', background: 'var(--surface-inset)', border: '1px solid var(--line-1)' }}>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-3)' }}>SCORE</div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 24, color: 'var(--heat-fire)' }}>{name.score > 0 ? '+' + name.score : name.score}</div>
          </div>
          <div style={{ flex: 1, padding: '10px 12px', borderRadius: 'var(--radius-md)', background: 'var(--surface-inset)', border: '1px solid var(--line-1)' }}>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-3)' }}>CONFIDENCE</div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 24, color: 'var(--neon-cyan)' }}>{name.confidence != null ? name.confidence + '%' : '—'}</div>
          </div>
        </div>

        <SectionLabel icon={<I.Sparkles size={12} />}>Evolve this name</SectionLabel>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 22 }}>
          {variants.map(([dir, lbl]) => <Button key={dir} variant="secondary" size="sm" iconLeft={<I.Sparkles size={15} />} onClick={() => onEvolve && onEvolve(name.id, dir)}>{lbl}</Button>)}
        </div>

        {related.length > 0 && (
          <div style={{ marginBottom: 20 }}>
            <SectionLabel icon={<I.Share size={12} />}>Similar names</SectionLabel>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
              {related.map((m) => (
                <button key={m.id} onClick={() => onOpen && onOpen(m.id)} style={{ cursor: 'pointer', padding: '6px 11px', borderRadius: 'var(--radius-pill)', border: '1px solid var(--line-2)', background: 'var(--surface-2)', color: 'var(--text-1)', fontFamily: 'var(--font-sans)', fontWeight: 600, fontSize: 13 }}>{m.name}</button>
              ))}
            </div>
          </div>
        )}

        {voters.length > 0 && (
          <div style={{ marginBottom: 20 }}>
            <SectionLabel icon={<I.Users size={12} />}>Who voted</SectionLabel>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <VoterAvatars voters={voters} size={28} popUp />
              <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-3)' }}>tap for votes</span>
            </div>
          </div>
        )}

        <div style={{ marginBottom: 20 }}>
          <SectionLabel icon={<I.Globe size={12} />}>Domains</SectionLabel>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {domains.map((d) => (
              <div key={d.domain} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', borderRadius: 'var(--radius-sm)', background: 'var(--surface-inset)', border: '1px solid var(--line-1)' }}>
                <span style={{ fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--text-1)' }}>{d.domain}</span>
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontFamily: 'var(--font-mono)', fontSize: 11, color: d.available ? 'var(--success)' : 'var(--text-3)' }}>
                  <span style={{ width: 7, height: 7, borderRadius: '50%', background: d.available ? 'var(--success)' : 'var(--text-disabled)' }} />
                  {d.available ? 'Available' : 'Taken'}
                </span>
              </div>
            ))}
          </div>
          <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 7 }}>Indicative — live availability lookups coming soon.</div>
        </div>

        <div style={{ marginBottom: 22 }}>
          <SectionLabel icon={<I.Compass size={12} />}>Legal</SectionLabel>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {legal.map((l) => (
              <div key={l.name} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, padding: '8px 12px', borderRadius: 'var(--radius-sm)', background: 'var(--surface-inset)', border: '1px solid var(--line-1)' }}>
                <span style={{ fontSize: 13, color: 'var(--text-1)' }}>{l.name}</span>
                <Badge tone={l.tone} size="sm">{l.label}</Badge>
              </div>
            ))}
          </div>
          <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 7 }}>Indicative similar-mark scan — not legal advice. Run a formal trademark search before deciding.</div>
        </div>

        <SectionLabel icon={<I.MessageCircle size={12} />}>Comments</SectionLabel>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ display: 'flex', gap: 9 }}><Avatar name="Alex Roy" size={26} /><div style={{ fontSize: 13.5, color: 'var(--text-2)', background: 'var(--surface-2)', padding: '8px 12px', borderRadius: 'var(--radius-md)' }}>I like how short this is.</div></div>
          <div style={{ display: 'flex', gap: 9 }}><Avatar name="Priya N" size={26} /><div style={{ fontSize: 13.5, color: 'var(--text-2)', background: 'var(--surface-2)', padding: '8px 12px', borderRadius: 'var(--radius-md)' }}>Works for a .co.uk domain too 👍</div></div>
        </div>
      </div>
    );
  }

  window.BXNebula = {
    clamp, hash, deriveVoters, relatedIds, whyText,
    Stars, FireFX, FrostFX, VoterAvatars, SectionLabel,
    Header, ZoomControl, Welcome, ActivityFeed, WaveBanner, SprintBar, Portals,
    InviteModal, AddCustomModal, DetailPanel,
  };
})();
