// HeroAnimation: the centerpiece visual. Members participate from Slack,
// email, and their community while one shared thread stays current.

const { useState: useStateH, useEffect: useEffectH, useRef: useRefH, useMemo: useMemoH } = React;

// ---------- Shared sub-pieces ----------

function PlatformIcon({ kind, size = 44 }) {
  const s = size;
  if (kind === 'slack') {
    return (
      <svg width={s} height={s} viewBox="0 0 24 24" fill="none" aria-label="Slack">
        <rect width="24" height="24" rx="6" fill="#fff"/>
        <path d="M7.5 13.5a1.5 1.5 0 0 1-1.5 1.5 1.5 1.5 0 0 1-1.5-1.5A1.5 1.5 0 0 1 6 12h1.5v1.5zM8.25 13.5A1.5 1.5 0 0 1 9.75 12a1.5 1.5 0 0 1 1.5 1.5v3.75A1.5 1.5 0 0 1 9.75 18.75 1.5 1.5 0 0 1 8.25 17.25V13.5z" fill="#E01E5A"/>
        <path d="M9.75 7.5A1.5 1.5 0 0 1 8.25 6 1.5 1.5 0 0 1 9.75 4.5 1.5 1.5 0 0 1 11.25 6v1.5H9.75zM9.75 8.25A1.5 1.5 0 0 1 11.25 9.75 1.5 1.5 0 0 1 9.75 11.25H6A1.5 1.5 0 0 1 4.5 9.75 1.5 1.5 0 0 1 6 8.25h3.75z" fill="#36C5F0"/>
        <path d="M16.5 9.75A1.5 1.5 0 0 1 18 8.25 1.5 1.5 0 0 1 19.5 9.75 1.5 1.5 0 0 1 18 11.25h-1.5V9.75zM15.75 9.75A1.5 1.5 0 0 1 14.25 11.25 1.5 1.5 0 0 1 12.75 9.75V6A1.5 1.5 0 0 1 14.25 4.5 1.5 1.5 0 0 1 15.75 6v3.75z" fill="#2EB67D"/>
        <path d="M14.25 16.5A1.5 1.5 0 0 1 15.75 18A1.5 1.5 0 0 1 14.25 19.5A1.5 1.5 0 0 1 12.75 18v-1.5h1.5zM14.25 15.75A1.5 1.5 0 0 1 12.75 14.25 1.5 1.5 0 0 1 14.25 12.75H18A1.5 1.5 0 0 1 19.5 14.25 1.5 1.5 0 0 1 18 15.75h-3.75z" fill="#ECB22E"/>
      </svg>
    );
  }
  if (kind === 'email') {
    return (
      <span className="platform-email-icon" style={{ width: s, height: s }} role="img" aria-label="Email">
        <i aria-hidden="true"></i>
      </span>
    );
  }
  if (kind === 'circle') {
    return (
      <span className="platform-community-icon" style={{ width: s, height: s }} role="img" aria-label="Online community">
        <i aria-hidden="true"><b></b><b></b></i>
      </span>
    );
  }
  if (kind === 'enterprise') {
    return (
      <svg width={s} height={s} viewBox="0 0 24 24" fill="none" role="img" aria-label="Enterprise">
        <rect width="24" height="24" rx="6" fill="#F2F2FF" stroke="#C5C5E7"/>
        <path d="M12 4.5 18.2 7v4.8c0 3.8-2.5 6.3-6.2 7.7-3.7-1.4-6.2-3.9-6.2-7.7V7L12 4.5Z" fill="#fff" stroke="#4242F0" strokeWidth="1.4" strokeLinejoin="round"/>
        <path d="m9.1 12.1 1.8 1.8 4-4" stroke="#15996C" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    );
  }
  return null;
}

function Avatar({ name, color = '#4242F0', size = 28 }) {
  return <PortraitAvatar name={name} color={color} size={size}/>;
}

// ---------- MOTIF 1: FLOW (default) ----------
// Central hub with 3 platforms. Messages pulse along connecting paths.

function FlowMotif() {
  const [messages, setMessages] = useStateH([]);
  const idRef = useRefH(0);

  const SAMPLES = useMemoH(() => [
    { from: 'slack',  to: 'email',  author: 'Marisol Duarte', color: '#4242F0', text: "Who's rolled out Claude company-wide? Curious what stuck." },
    { from: 'email',  to: 'circle', author: 'James Okafor',   color: '#FF6A3D', text: "We did last quarter. Support team pairs it with Fin for roughly 42% deflection." },
    { from: 'circle', to: 'slack',  author: 'Priya Ramanathan', color: '#21C089', text: "Same here. The hard part wasn't the model, it was the prompt library." },
    { from: 'slack',  to: 'circle', author: 'Nick Sato',       color: '#8B5CF6', text: "Prompt library question → do you version them in Git or a Notion DB?" },
    { from: 'email',  to: 'slack',  author: 'Dave Kowalski',   color: '#F5B820', text: "Git. Treat prompts like code: PRs, reviews, the whole thing." },
    { from: 'circle', to: 'email',  author: 'Helen Tran',      color: '#2F7CFF', text: "+1. We broke prod once with a bad prompt change. Reviews now mandatory." },
    { from: 'slack',  to: 'email',  author: 'Andrew Weiss',    color: '#315EFB', text: "Any favorite evals tool? Trying to move past vibes." },
    { from: 'email',  to: 'circle', author: 'Marisol Duarte',  color: '#4242F0', text: "Braintrust has been great for us. Happy to do a quick show-and-tell next meetup." },
  ], []);

  useEffectH(() => {
    let t = 0;
    const tick = () => {
      const sample = SAMPLES[idRef.current % SAMPLES.length];
      const id = idRef.current++;
      setMessages(ms => [...ms.slice(-4), { id, ...sample, born: Date.now() }]);
      t = setTimeout(tick, 1800 + Math.random() * 900);
    };
    t = setTimeout(tick, 600);
    return () => clearTimeout(t);
  }, [SAMPLES]);

  // Cleanup old messages
  useEffectH(() => {
    const i = setInterval(() => {
      setMessages(ms => ms.filter(m => Date.now() - m.born < 5000));
    }, 1000);
    return () => clearInterval(i);
  }, []);

  // Positions of platform nodes: triangle layout
  const nodes = {
    slack:  { x: 90,  y: 80,  color: 'var(--c-slack)' },
    email:  { x: 500, y: 80,  color: 'var(--c-email)' },
    circle: { x: 295, y: 380, color: 'var(--c-circle)' },
  };
  const hub = { x: 295, y: 220 };

  // path from a platform through hub to another
  const pathFor = (from, to) => {
    const f = nodes[from], t = nodes[to];
    return `M ${f.x} ${f.y} Q ${hub.x} ${hub.y} ${t.x} ${t.y}`;
  };

  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: '590/500', maxWidth: 620 }}>
      <svg viewBox="0 0 590 500" style={{ width: '100%', height: '100%' }}>
        <defs>
          <radialGradient id="hubGlow" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="#4242F0" stopOpacity="0.35"/>
            <stop offset="100%" stopColor="#4242F0" stopOpacity="0"/>
          </radialGradient>
          <filter id="soft">
            <feGaussianBlur stdDeviation="0.6"/>
          </filter>
        </defs>

        {/* hub glow */}
        <circle cx={hub.x} cy={hub.y} r="170" fill="url(#hubGlow)"/>

        {/* connection paths */}
        <path d={pathFor('slack','email')}  stroke="#E8E3D6" strokeWidth="2" fill="none" strokeDasharray="4 6"/>
        <path d={pathFor('email','circle')} stroke="#E8E3D6" strokeWidth="2" fill="none" strokeDasharray="4 6"/>
        <path d={pathFor('circle','slack')} stroke="#E8E3D6" strokeWidth="2" fill="none" strokeDasharray="4 6"/>

        {/* Ambient always-on pulses: three continuously-looping dots, one per edge, staggered. */}
        {[
          { from: 'slack',  to: 'email',  delay: '0s',   dur: '3.2s' },
          { from: 'email',  to: 'circle', delay: '-1.1s', dur: '3.2s' },
          { from: 'circle', to: 'slack',  delay: '-2.2s', dur: '3.2s' },
        ].map((edge, i) => {
          const d = pathFor(edge.from, edge.to);
          return (
            <g key={'amb-'+i}>
              <circle r="4" fill="#4242F0" opacity="0.55" filter="url(#soft)">
                <animateMotion dur={edge.dur} path={d} repeatCount="indefinite" begin={edge.delay} rotate="auto"/>
                <animate attributeName="opacity" values="0;0.55;0.55;0" keyTimes="0;0.12;0.88;1" dur={edge.dur} repeatCount="indefinite" begin={edge.delay}/>
              </circle>
              {/* second, smaller trailing dot for richer texture */}
              <circle r="2.5" fill="#4242F0" opacity="0.35">
                <animateMotion dur={edge.dur} path={d} repeatCount="indefinite" begin={`${parseFloat(edge.delay) - 0.5}s`} rotate="auto"/>
                <animate attributeName="opacity" values="0;0.35;0.35;0" keyTimes="0;0.12;0.88;1" dur={edge.dur} repeatCount="indefinite" begin={`${parseFloat(edge.delay) - 0.5}s`}/>
              </circle>
            </g>
          );
        })}

        {/* message-triggered pulses (on top of ambient) */}
        {messages.map(m => {
          const from = nodes[m.from], to = nodes[m.to];
          const d = `M ${from.x} ${from.y} Q ${hub.x} ${hub.y} ${to.x} ${to.y}`;
          return (
            <g key={m.id}>
              <path d={d} stroke="#4242F0" strokeWidth="2.5" fill="none" opacity="0.3">
                <animate attributeName="stroke-dasharray" from="0 600" to="600 0" dur="1.8s" fill="freeze"/>
                <animate attributeName="opacity" values="0;0.6;0" keyTimes="0;0.5;1" dur="1.8s" fill="freeze"/>
              </path>
              {/* brighter, larger traveling dot to call out the actual message */}
              <circle r="6" fill="#4242F0" filter="url(#soft)">
                <animateMotion dur="1.8s" path={d} fill="freeze" rotate="auto"/>
                <animate attributeName="opacity" values="0;1;1;0" keyTimes="0;0.1;0.9;1" dur="1.8s" fill="freeze"/>
              </circle>
            </g>
          );
        })}

        {/* central hub */}
        <g>
          <circle cx={hub.x} cy={hub.y} r="42" fill="#fff" stroke="var(--site-line)" strokeWidth="1.5"/>
          <circle cx={hub.x} cy={hub.y} r="42" fill="none" stroke="#4242F0" strokeWidth="2">
            <animate attributeName="r" values="42;52;42" dur="3s" repeatCount="indefinite"/>
            <animate attributeName="opacity" values="1;0;1" dur="3s" repeatCount="indefinite"/>
          </circle>
          <image href="assets/logo-mark.svg" x={hub.x - 22} y={hub.y - 22} width="44" height="44"/>
        </g>

        {/* platform nodes */}
        {Object.entries(nodes).map(([k, n]) => (
          <g key={k}>
            <circle cx={n.x} cy={n.y} r="34" fill="#fff" stroke="var(--site-line)" strokeWidth="1.5"/>
          </g>
        ))}
      </svg>

      {/* Icons positioned over SVG */}
      {Object.entries(nodes).map(([k, n]) => (
        <div key={k} style={{
          position: 'absolute',
          left: `${(n.x / 590) * 100}%`,
          top: `${(n.y / 500) * 100}%`,
          transform: 'translate(-50%, -50%)',
          width: 68, height: 68, borderRadius: 16,
          background: '#fff',
          boxShadow: '0 6px 18px rgba(26,27,58,0.08), 0 0 0 1px var(--site-line)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexDirection: 'column', gap: 2,
        }}>
          <PlatformIcon kind={k} size={32} />
          <div style={{ fontSize: 10, fontWeight: 600, color: 'var(--site-ink-soft)', textTransform: 'capitalize' }}>{k === 'circle' ? 'Community' : k}</div>
        </div>
      ))}

      {/* floating message pills */}
      <FlowMessageFeed messages={messages} />
    </div>
  );
}

function FlowMessageFeed({ messages }) {
  // Show latest 3. Anchor pills next to the platform nodes they came FROM,
  // so each pill visually belongs to one of the three platforms.
  // Node positions (as % of 590x500 viewBox):
  //   slack   ≈ (15%, 16%)   top-left
  //   email   ≈ (85%, 16%)   top-right
  //   circle  ≈ (50%, 76%)   bottom-center
  const latest = messages.slice(-3);
  const anchorFor = (from) => {
    if (from === 'slack')  return { left: '0%',  top: '26%', transform: 'translate(-4%, 0)' };
    if (from === 'email')  return { right: '0%', top: '26%', transform: 'translate(4%, 0)' };
    return                        { left: '50%', bottom: '-2%', transform: 'translate(-50%, 0)' };
  };
  return (
    <>
      {latest.map((m, idx) => {
        const pos = anchorFor(m.from);
        return (
          <div key={m.id}
            style={{
              position: 'absolute', ...pos,
              background: '#fff',
              border: '1px solid var(--site-line)',
              borderRadius: 14,
              padding: '10px 14px',
              fontSize: 12,
              boxShadow: '0 8px 20px rgba(26,27,58,0.06)',
              maxWidth: 220,
              animation: 'fade-in-up 0.5s cubic-bezier(.16,1,.3,1) both',
            }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
              <Avatar name={m.author} color={m.color} size={22}/>
              <span style={{ fontWeight: 600, fontSize: 11 }}>{m.author}</span>
              <span style={{ fontSize: 10, color: 'var(--site-ink-soft)', marginLeft: 'auto' }}>
                <PlatformBadge kind={m.from}/>
              </span>
            </div>
            <div style={{ color: 'var(--site-ink)', lineHeight: 1.4, fontSize: 11.5 }}>{m.text}</div>
          </div>
        );
      })}
      <style>{`
        @keyframes fade-in-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
      `}</style>
    </>
  );
}

function PlatformBadge({ kind }) {
  const colors = {
    slack:  { bg: '#F4EDE9', fg: '#611F69' },
    email:  { bg: '#EEF2FF', fg: '#315EFB' },
    circle: { bg: '#E7F0FF', fg: '#2F7CFF' },
  };
  const c = colors[kind] || colors.slack;
  return (
    <span style={{
      padding: '2px 7px', borderRadius: 4, fontSize: 9.5, fontWeight: 600,
      background: c.bg, color: c.fg, textTransform: 'capitalize', letterSpacing: '0.02em'
    }}>{kind === 'circle' ? 'Community' : kind}</span>
  );
}

// ---------- MOTIF 2: ORBIT ----------
function OrbitMotif() {
  const members = useMemoH(() => {
    const names = ['Marisol','James','Nick','Priya','Dave','Sven','Helen','Andrew','Jean','Melissa','Tom','Lucas'];
    const colors = ['#4242F0','#FF6A3D','#21C089','#8B5CF6','#F5B820','#2F7CFF','#315EFB','#611F69'];
    return names.map((n, i) => ({ name: n, color: colors[i % colors.length] }));
  }, []);

  return (
    <div style={{ position: 'relative', width: '100%', aspectRatio: '1', maxWidth: 560, margin: '0 auto' }}>
      <svg viewBox="0 0 560 560" style={{ width: '100%', height: '100%' }}>
        {/* 3 concentric orbit rings */}
        {[120, 200, 260].map((r, i) => (
          <circle key={r} cx="280" cy="280" r={r}
            fill="none" stroke="#E8E3D6" strokeWidth="1" strokeDasharray="2 6"/>
        ))}
        {/* platform-tagged rings */}
        {[
          { r: 120, color: '#4242F0', label: 'slack',  n: 4 },
          { r: 200, color: '#2F7CFF', label: 'circle', n: 6 },
          { r: 260, color: '#315EFB', label: 'email',  n: 8 },
        ].map((ring, ringIdx) => (
          <g key={ring.label}>
            {Array.from({ length: ring.n }).map((_, i) => {
              const ang = (i / ring.n) * 360;
              return (
                <g key={i} style={{
                  transformOrigin: '280px 280px',
                  animation: `orbit-${ringIdx} ${30 + ringIdx * 10}s linear infinite`,
                }}>
                  <circle cx={280 + ring.r} cy="280" r="14" fill={members[(ringIdx*5+i) % members.length].color}/>
                  <text x={280 + ring.r} y="284" textAnchor="middle" fill="#fff" fontSize="10" fontWeight="600"
                    fontFamily="Host Grotesk">
                    {members[(ringIdx*5+i) % members.length].name[0]}
                  </text>
                </g>
              );
            })}
          </g>
        ))}
        {/* center */}
        <circle cx="280" cy="280" r="56" fill="#1A1B3A"/>
        <circle cx="280" cy="280" r="56" fill="none" stroke="#4242F0" strokeWidth="2" opacity="0.6">
          <animate attributeName="r" values="56;76;56" dur="3s" repeatCount="indefinite"/>
          <animate attributeName="opacity" values="0.6;0;0.6" dur="3s" repeatCount="indefinite"/>
        </circle>
        <text x="280" y="292" textAnchor="middle" fill="#fff"
          fontFamily="Petrona" fontSize="40" fontStyle="italic">S</text>
      </svg>

      {/* labels */}
      <div style={{ position: 'absolute', top: 10, left: '50%', transform: 'translateX(-50%)' }}>
        <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--c-email)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Email readers</span>
      </div>
      <div style={{ position: 'absolute', top: 80, left: 80 }}>
        <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--c-circle)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Community active</span>
      </div>
      <div style={{ position: 'absolute', top: 180, right: 100 }}>
        <span style={{ fontSize: 11, fontWeight: 600, color: '#611F69', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Slack power users</span>
      </div>

      <style>{`
        @keyframes orbit-0 { to { transform: rotate(360deg); } }
        @keyframes orbit-1 { to { transform: rotate(-360deg); } }
        @keyframes orbit-2 { to { transform: rotate(360deg); } }
      `}</style>
    </div>
  );
}

// ---------- MOTIF 3: MIRROR ----------
// Same conversation, three native-looking surfaces.
function MirrorMotif({ variant = 'home' }) {
  const conversations = useMemoH(() => {
    const primaryPeople = variant === 'features' ? {
      maya: ['Amara Wilson', 'amara'], daniel: ['Kenji Ito', 'kenji'],
      simone: ['Sofia Mendes', 'sofia'], marcus: ['Rafael Costa', 'rafael'],
    } : {
      maya: ['Maya Torres', 'maya'], daniel: ['Daniel Cho', 'daniel'],
      simone: ['Simone Brooks', 'simone'], marcus: ['Marcus Reed', 'marcus'],
    };
    const secondaryPeople = variant === 'features' ? {
      owner: ['Rohan Mehta', 'rohan'], security: ['Naomi Chen', 'naomi'],
      product: ['Julian Park', 'julian'], finance: ['Erin Walsh', 'erin'],
    } : {
      owner: ['Helen Tran', 'helen'], security: ['Andrew Weiss', 'andrew'],
      product: ['Nick Sato', 'nick'], finance: ['Dave Kowalski', 'dave'],
    };
    return [
      {
        key: 'runway',
        slackChannel: 'member-asks',
        communitySpace: 'Member asks',
        memberCount: 184,
        subject: 'What belongs on a useful runway slide?',
        messages: [
          { author: primaryPeople.maya[0], handle: primaryPeople.maya[1], role: 'VP Product', color: '#4242F0', type: 'question',
            text: "What does your board actually want to see on a runway slide? I want ours to drive a decision, not just report a number.",
            time: '9:12 AM' },
          { author: primaryPeople.daniel[0], handle: primaryPeople.daniel[1], role: 'Fractional CFO', color: '#315EFB',
            text: "We use one page: runway at plan, downside runway, and the exact trigger for opening a raise.",
            time: '9:18 AM' },
          { author: primaryPeople.simone[0], handle: primaryPeople.simone[1], role: 'VP Finance', color: '#21C089',
            text: "I can share our board template. The scenario table is the part founders come back to.",
            time: '9:24 AM' },
          { author: primaryPeople.marcus[0], handle: primaryPeople.marcus[1], role: 'Chief of Staff', color: '#8B5CF6',
            text: "Same approach here. We also show the owner and decision date for each trigger.",
            time: '9:31 AM' },
        ],
        timeline: [
          { type: 'post', message: 0 },
          { type: 'reaction', message: 0, emoji: '❤️', count: 1 },
          { type: 'reaction', message: 0, emoji: '❤️', count: 2 },
          { type: 'post', message: 1 },
          { type: 'reaction', message: 1, emoji: '🙌', count: 1 },
          { type: 'reaction', message: 0, emoji: '❤️', count: 3 },
          { type: 'reaction', message: 1, emoji: '🙌', count: 2 },
          { type: 'post', message: 2 },
          { type: 'reaction', message: 1, emoji: '🙌', count: 3 },
          { type: 'reaction', message: 2, emoji: '👏', count: 1 },
          { type: 'post', message: 3 },
          { type: 'reaction', message: 2, emoji: '👏', count: 2 },
          { type: 'reaction', message: 1, emoji: '💡', count: 1 },
          { type: 'reaction', message: 1, emoji: '💡', count: 2 },
          { type: 'pause' }, { type: 'pause' },
        ],
      },
      {
        key: 'ai-notes',
        slackChannel: 'ai-practice',
        communitySpace: 'AI in practice',
        memberCount: 167,
        subject: 'What guardrails are you using for AI meeting notes?',
        messages: [
          { author: secondaryPeople.owner[0], handle: secondaryPeople.owner[1], role: 'VP Operations', color: '#21C089', type: 'question',
            text: "How are teams handling AI meeting notes on sensitive customer calls? We want the recap, but not another place for confidential context to live.",
            time: '11:04 AM' },
          { author: secondaryPeople.security[0], handle: secondaryPeople.security[1], role: 'Head of Security', color: '#8B5CF6',
            text: "We default recording off for customer calls and require an approved tool with a 30-day retention policy.",
            time: '11:09 AM' },
          { author: secondaryPeople.product[0], handle: secondaryPeople.product[1], role: 'VP Product', color: '#4242F0',
            text: "We added a verbal consent prompt and a short list of topics that should never be captured. Adoption actually improved once the rule was clear.",
            time: '11:16 AM' },
          { author: secondaryPeople.finance[0], handle: secondaryPeople.finance[1], role: 'VP Finance', color: '#315EFB',
            text: "The retention limit is the piece we were missing. I’m taking that back to our security review this week.",
            time: '11:22 AM' },
        ],
        timeline: [
          { type: 'post', message: 0 },
          { type: 'reaction', message: 0, emoji: '👀', count: 1 },
          { type: 'reaction', message: 0, emoji: '👀', count: 2 },
          { type: 'post', message: 1 },
          { type: 'reaction', message: 1, emoji: '✅', count: 1 },
          { type: 'reaction', message: 0, emoji: '👀', count: 3 },
          { type: 'post', message: 2 },
          { type: 'reaction', message: 2, emoji: '🛡️', count: 1 },
          { type: 'reaction', message: 1, emoji: '✅', count: 2 },
          { type: 'reaction', message: 2, emoji: '🛡️', count: 2 },
          { type: 'post', message: 3 },
          { type: 'reaction', message: 3, emoji: '🔒', count: 1 },
          { type: 'reaction', message: 2, emoji: '🛡️', count: 3 },
          { type: 'reaction', message: 1, emoji: '✅', count: 3 },
          { type: 'pause' }, { type: 'pause' },
        ],
      },
    ];
  }, [variant]);
  const [frame, setFrame] = useStateH({ conversation: 0, event: 0 });
  const reducedMotion = window.useReducedMotion();

  useEffectH(() => {
    if (reducedMotion) {
      setFrame({ conversation: 0, event: conversations[0].timeline.length - 1 });
      return undefined;
    }
    const t = setInterval(() => setFrame(current => {
      const eventCount = conversations[current.conversation].timeline.length;
      if (current.event < eventCount - 1) return { ...current, event: current.event + 1 };
      return { conversation: (current.conversation + 1) % conversations.length, event: 0 };
    }), 1100);
    return () => clearInterval(t);
  }, [conversations, reducedMotion]);

  const conversation = conversations[frame.conversation];
  const activeEvents = conversation.timeline.slice(0, frame.event + 1);
  const visibleCount = activeEvents.filter(event => event.type === 'post').length;
  const reactionCounts = activeEvents.reduce((counts, event) => {
    if (event.type !== 'reaction') return counts;
    if (!counts[event.message]) counts[event.message] = {};
    counts[event.message][event.emoji] = event.count;
    return counts;
  }, {});
  const visible = conversation.messages.slice(0, visibleCount).map((message, messageIndex) => ({
    ...message,
    reactions: Object.entries(reactionCounts[messageIndex] || {}).map(([emoji, count]) => ({ emoji, count })),
  }));

  return (
    <div className="mirror-surface-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12, width: '100%' }}>
      <SlackPane visible={visible} conversation={conversation}/>
      <EmailPane visible={visible} conversation={conversation}/>
      <CirclePane visible={visible} conversation={conversation}/>
      <style>{`
        @keyframes mirror-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
        @keyframes reaction-pop { 0% { opacity: 0; transform: scale(.72); } 70% { transform: scale(1.08); } 100% { opacity: 1; transform: scale(1); } }
      `}</style>
    </div>
  );
}

// --- Friendly silhouette avatar: tinted bg + head-and-shoulders in accent color
// --- User avatar with switchable styles (set globally via Tweaks).
//   styles: 'initials' | 'gradient' | 'solid' | 'ring' | 'silhouette'
function useAvatarStyle() {
  const [s, setS] = useStateH((typeof window !== 'undefined' && window.SYNC_AVATAR_STYLE) || 'silhouette');
  useEffectH(() => {
    const h = () => setS(window.SYNC_AVATAR_STYLE || 'silhouette');
    window.addEventListener('sync-avatar-style', h);
    return () => window.removeEventListener('sync-avatar-style', h);
  }, []);
  return s;
}

function initialsOf(name) {
  return (name || '?')
    .split(/[\s_\-]+/).filter(Boolean).slice(0, 2)
    .map(w => w[0]).join('').toUpperCase() || '?';
}

// lighten a hex color toward white by t (0..1)
function lighten(hex, t) {
  const m = hex.replace('#', '');
  const n = m.length === 3 ? m.split('').map(c => c + c).join('') : m;
  const r = parseInt(n.slice(0,2),16), g = parseInt(n.slice(2,4),16), b = parseInt(n.slice(4,6),16);
  const lr = Math.round(r + (255 - r) * t), lg = Math.round(g + (255 - g) * t), lb = Math.round(b + (255 - b) * t);
  return `rgb(${lr}, ${lg}, ${lb})`;
}

const AVATAR_SPRITE_POSITIONS = [
  '0% 0%', '25% 0%', '50% 0%', '75% 0%', '100% 0%',
  '0% 25%', '25% 25%', '50% 25%', '75% 25%', '100% 25%',
  '0% 50%', '25% 50%', '50% 50%', '75% 50%', '100% 50%',
  '0% 75%', '25% 75%', '50% 75%', '75% 75%', '100% 75%',
  '0% 100%', '25% 100%', '50% 100%', '75% 100%', '100% 100%',
];

// Every fictional member has a unique, gender-appropriate portrait from the
// two small-size-optimized production sheets. Aliases intentionally share the
// same portrait because they refer to the same person.
const NAMED_AVATAR_PROFILE = {
  'marisol duarte': ['mutedA', 1],
  'priya raman': ['mutedA', 3],
  'priya ramanathan': ['mutedA', 3],
  'james okafor': ['mutedA', 0],
  'talia chen': ['mutedA', 5],
  'talia': ['mutedA', 5],
  'lena park': ['mutedA', 7],
  'omar haddad': ['mutedA', 2],
  'nick sato': ['mutedA', 4],
  'dave kowalski': ['mutedA', 6],
  'helen tran': ['mutedA', 9],
  'andrew weiss': ['mutedA', 8],

  'maya torres': ['mutedA', 11],
  'daniel cho': ['mutedA', 10],
  'simone brooks': ['mutedA', 13],
  'marcus reed': ['mutedA', 12],
  'aisha patel': ['mutedA', 15],
  'ethan cole': ['mutedA', 14],

  'amara wilson': ['mutedA', 17],
  'kenji ito': ['mutedA', 16],
  'sofia mendes': ['mutedA', 19],
  'rafael costa': ['mutedA', 18],
  'nadia khan': ['mutedB', 0],
  'ben carter': ['mutedA', 20],

  'elena rossi': ['mutedB', 2],
  'kwame mensah': ['mutedB', 1],
  'leo hart': ['mutedB', 3],
  'martin vogel': ['mutedB', 5],
  'andre kim': ['mutedB', 7],
  'monica wells': ['mutedB', 4],
  'ravi desai': ['mutedB', 9],
  'victor l.': ['mutedB', 11],
  'caroline m.': ['mutedB', 6],
  'hassan k.': ['mutedB', 13],
  'gabriela s.': ['mutedB', 8],
  'michael a.': ['mutedB', 15],
  'yuna p.': ['mutedB', 10],

  'alyssa grant': ['mutedB', 12],
  'alyssa g.': ['mutedB', 12],
  'mei lin': ['mutedB', 14],
  'mei l.': ['mutedB', 14],
  'david ortiz': ['mutedB', 17],
  'david o.': ['mutedB', 17],
  'nora bennett': ['mutedB', 16],
  'nora b.': ['mutedB', 16],
  'cory brown': ['mutedB', 19],

  'leah morgan': ['mutedA', 21],
  'caleb foster': ['mutedA', 22],
  'mina alvarez': ['mutedA', 23],
  'jonah brooks': ['mutedA', 24],
  'rachel kim': ['mutedB', 18],
  'samuel ortiz': ['mutedB', 21],
  'clara wilson': ['mutedB', 20],
  'isaac mensah': ['mutedB', 23],

  'rohan mehta': ['mutedB', 19],
  'naomi chen': ['mutedB', 22],
  'julian park': ['mutedA', 10],
  'erin walsh': ['mutedB', 24],

  // Synchronize for Slack hero rotation.
  'amara ndlovu': ['mutedB', 22],
  'felix moreau': ['mutedB', 23],
  'yuki tanaka': ['mutedB', 24],
};

function avatarProfileFor(name) {
  const key = (name || '').toLowerCase();
  if (NAMED_AVATAR_PROFILE[key]) return NAMED_AVATAR_PROFILE[key];
  const index = key.split('').reduce((sum, char) => sum + char.charCodeAt(0), 0) % AVATAR_SPRITE_POSITIONS.length;
  return ['mutedA', index];
}

function PortraitAvatar({ name, size = 28, radius }) {
  const [sheet, index] = avatarProfileFor(name);
  const scale = size <= 18 ? 'micro' : size <= 28 ? 'small' : 'regular';
  return (
    <span
      className={`editorial-avatar avatar-sheet-${sheet} avatar-${scale}`}
      role="img"
      aria-label={name || 'Community member'}
      style={{
        width: size,
        height: size,
        borderRadius: radius != null ? radius : '50%',
        '--avatar-position': AVATAR_SPRITE_POSITIONS[index],
      }}
    />
  );
}

// --- Flat illustrated character avatar. Deterministic traits from the name.
function IllustratedAvatar({ name, color = '#4242F0', size = 28, radius, seed }) {
  const s = seed != null ? seed : (name || '').split('').reduce((a, c) => a + c.charCodeAt(0), 0);
  const r = radius ?? size / 2;
  const rx = r * 40 / size;
  const uid = `ia-${s}-${size}`;

  const SKIN = ['#F7D3BC', '#F0C0A0', '#DBA179', '#C08457', '#9C6540', '#7A4B30'];
  const HAIR = ['#241C18', '#3B2A1E', '#5A3E28', '#8A5A32', '#C9974B', '#B8B2AC', '#111111'];
  const BG   = ['#EEEDEA', '#E8ECF5', '#E8EEE9', '#F2EAE5', '#EEEAF2', '#F2EFE7', '#E9ECF4'];

  const skin = SKIN[s % SKIN.length];
  const hc   = HAIR[Math.floor(s / 6) % HAIR.length];
  const bg   = BG[Math.floor(s / 4) % BG.length];
  const top  = color;                       // clothing keeps the contextual color
  const hs   = Math.floor(s / 3) % 6;        // hairstyle
  const glasses = s % 4 === 0;
  const beard   = s % 5 === 0 && hs !== 1;   // no beard with long hair

  const cap = "M10.8 16.6 C10.5 8 14.6 5 20 5 C25.4 5 29.5 8 29.2 16.6 C27.4 12 24.4 10.2 20 10.2 C15.6 10.2 12.6 12 10.8 16.6 Z";

  return (
    <svg width={size} height={size} viewBox="0 0 40 40" style={{ flexShrink: 0, display: 'block' }}>
      <defs><clipPath id={`${uid}-clip`}><rect width="40" height="40" rx={rx} ry={rx}/></clipPath></defs>
      <g clipPath={`url(#${uid}-clip)`}>
        {/* background */}
        <rect width="40" height="40" fill={bg}/>

        {/* long hair back panels (behind everything) */}
        {hs === 1 && <g fill={hc} opacity="0.96">
          <path d="M10.5 15 C7 21 8 30 11 35 L14 35 C12.2 29 12 22 13.6 16 Z"/>
          <path d="M29.5 15 C33 21 32 30 29 35 L26 35 C27.8 29 28 22 26.4 16 Z"/>
        </g>}

        {/* shoulders / clothing */}
        <path d="M4 40 C4.8 31.2 11.8 27.2 20 27.2 C28.2 27.2 35.2 31.2 36 40 Z" fill={top}/>
        <path d="M15.8 27.6 Q20 30.2 24.2 27.6" stroke="rgba(255,255,255,0.34)" strokeWidth="1" fill="none"/>

        {/* neck */}
        <path d="M17.4 21 h5.2 v5 q-2.6 1.6 -5.2 0 Z" fill={skin}/>
        <path d="M17.4 24.4 q2.6 1.4 5.2 0 v1.4 q-2.6 1.5 -5.2 0 Z" fill="rgba(0,0,0,0.10)"/>

        {/* ears */}
        <circle cx="11.9" cy="17.2" r="1.9" fill={skin}/>
        <circle cx="28.1" cy="17.2" r="1.9" fill={skin}/>

        {/* head */}
        <ellipse cx="20" cy="16" rx="8.3" ry="9.2" fill={skin} stroke="rgba(42,38,36,.08)" strokeWidth=".5"/>

        {/* beard */}
        {beard && <path d="M11.9 16.5 C12 25 15 28 20 28 C25 28 28 25 28.1 16.5 C27 21 24 22.6 20 22.6 C16 22.6 13 21 11.9 16.5 Z" fill={hc}/>}

        {/* restrained facial features */}
        <circle cx="16.8" cy="16.5" r=".72" fill="#382F29"/>
        <circle cx="23.2" cy="16.5" r=".72" fill="#382F29"/>

        {/* brows */}
        <path d="M15.2 14 Q16.8 13.5 18.2 14" stroke={hc} strokeWidth=".72" fill="none" strokeLinecap="round" opacity=".78"/>
        <path d="M21.8 14 Q23.2 13.5 24.8 14" stroke={hc} strokeWidth=".72" fill="none" strokeLinecap="round" opacity=".78"/>

        {/* mouth */}
        <path d="M18 21.2 Q20 22 22 21.2" stroke="#8F5548" strokeWidth=".82" fill="none" strokeLinecap="round"/>

        {/* hair on top */}
        {(hs === 0 || hs === 5) && <path d={cap} fill={hc}/>}
        {hs === 4 && <path d={cap} fill={hc} opacity="0.55"/>}
        {hs === 1 && <path d={cap} fill={hc}/>}
        {hs === 2 && <g fill={hc}>
          <path d={cap}/>
          <circle cx="20" cy="6" r="3.2"/>
        </g>}
        {hs === 3 && <g fill={hc}>
          <circle cx="13" cy="12.5" r="3.5"/>
          <circle cx="12" cy="16.5" r="3.2"/>
          <circle cx="16" cy="8.5" r="3.5"/>
          <circle cx="20" cy="6.8" r="3.6"/>
          <circle cx="24" cy="8.5" r="3.5"/>
          <circle cx="28" cy="12.5" r="3.5"/>
          <circle cx="28.8" cy="16.5" r="3.2"/>
        </g>}
        {hs === 5 && <path d="M11.5 12.5 C16 7.5 25 8 28.8 12 C24.5 9.5 15 9.8 11.5 15 Z" fill={hc}/>}

        {/* glasses */}
        {glasses && <g stroke="#2B2B33" strokeWidth="0.9" fill="rgba(255,255,255,0.14)">
          <rect x="13.1" y="14.2" width="5.5" height="4.6" rx="2.3"/>
          <rect x="21.4" y="14.2" width="5.5" height="4.6" rx="2.3"/>
          <path d="M18.6 16.2 h2.8" strokeLinecap="round"/>
        </g>}
      </g>
    </svg>
  );
}

function ReactionChips({ reactions = [], compact = false }) {
  if (!reactions.length) return null;
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: compact ? 4 : 6 }}>
      {reactions.map(reaction => (
        <span key={`${reaction.emoji}-${reaction.count}`} style={{
          border: '1px solid #D8DEE8', borderRadius: 10,
          padding: compact ? '1px 5px' : '1px 6px', fontSize: compact ? 9.5 : 10,
          background: '#F5F8FC', color: '#334155', lineHeight: 1.45,
          animation: 'reaction-pop .32s ease both',
        }}>
          {reaction.emoji} {reaction.count}
        </span>
      ))}
    </div>
  );
}

// --- Slack pane: purple sidebar, channel header, threaded messages
function SlackPane({ visible, conversation }) {
  const scrollRef = useRefH(null);
  useEffectH(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });
    }
  }, [visible.length, conversation.key]);
  const channels = ['announcements','intros','member-asks','ai-practice','events','jobs'];
  return (
    <div style={{
      background: '#fff',
      border: '1px solid var(--site-line)',
      borderRadius: 14,
      overflow: 'hidden',
      display: 'grid', gridTemplateColumns: '86px 1fr',
      height: 440,
      boxShadow: '0 6px 18px rgba(26,27,58,0.05)',
      fontFamily: 'Host Grotesk, system-ui, sans-serif',
    }}>
      {/* Slack sidebar */}
      <div style={{ background: '#3F0E40', color: '#D1B3D1', padding: '12px 8px', fontSize: 11 }}>
        <div style={{ fontWeight: 700, color: '#fff', fontSize: 12, padding: '2px 4px 10px', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>Leadership Exchange</div>
        <div style={{ fontSize: 10, letterSpacing: '0.04em', textTransform: 'uppercase', opacity: 0.6, margin: '12px 4px 4px' }}>Channels</div>
        {channels.map(c => {
          const isActive = c === conversation.slackChannel;
          return (
          <div key={c} style={{
            padding: '4px 6px',
            borderRadius: 4,
            background: isActive ? '#1164A3' : 'transparent',
            color: isActive ? '#fff' : '#D1B3D1',
            fontWeight: isActive ? 600 : 400,
            marginBottom: 1,
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          }}>
            <span style={{ opacity: 0.6, marginRight: 3 }}>#</span>{c.replace('#','')}
          </div>
          );
        })}
      </div>
      {/* Channel */}
      <div style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
        <div style={{ padding: '9px 12px', borderBottom: '1px solid #EDEDED', display: 'flex', alignItems: 'center', gap: 7 }}>
          <PlatformIcon kind="slack" size={22}/>
          <span style={{ fontWeight: 700, fontSize: 13, color: '#1D1C1D' }}># {conversation.slackChannel}</span>
          <span style={{ fontSize: 10, color: '#616061', marginLeft: 'auto' }}>{conversation.memberCount} members</span>
        </div>
        <div ref={scrollRef} style={{ padding: '10px 12px', display: 'flex', flexDirection: 'column', gap: 10, flex: 1, overflowY: 'auto', scrollbarWidth: 'thin' }}>
          {visible.map((m, i) => (
            <div key={`${conversation.key}-${i}`} style={{ display: 'flex', gap: 8, animation: 'mirror-in 0.4s both' }}>
              <PortraitAvatar name={m.author} color={m.color} size={32} radius={6}/>
              <div style={{ minWidth: 0, flex: 1 }}>
                <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 1 }}>
                  <span style={{ fontWeight: 700, fontSize: 12, color: '#1D1C1D' }}>{m.author}</span>
                  <span style={{ fontSize: 10, color: '#616061' }}>{m.time}</span>
                </div>
                <div style={{ fontSize: 12, color: '#1D1C1D', lineHeight: 1.4 }}>{m.text}</div>
                <ReactionChips reactions={m.reactions}/>
              </div>
            </div>
          ))}
        </div>
        <div style={{ margin: 10, padding: '8px 10px', border: '1px solid #DDD', borderRadius: 6, fontSize: 11, color: '#ABABAD' }}>
          Message #{conversation.slackChannel}
        </div>
      </div>
    </div>
  );
}

// --- Email pane: Gmail-style inbox list + opened thread
function EmailPane({ visible, conversation }) {
  const latest = visible[visible.length - 1];
  const scrollRef = useRefH(null);
  useEffectH(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTo({ top: 0, behavior: 'smooth' });
    }
  }, [visible.length, conversation.key]);
  return (
    <div style={{
      background: '#fff',
      border: '1px solid var(--site-line)',
      borderRadius: 14,
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
      height: 440,
      boxShadow: '0 6px 18px rgba(26,27,58,0.05)',
      fontFamily: 'Host Grotesk, system-ui, sans-serif',
    }}>
      {/* Generic email inbox header */}
      <div style={{ padding: '10px 12px', borderBottom: '1px solid #EEE', display: 'flex', alignItems: 'center', gap: 8 }}>
        <PlatformIcon kind="email" size={22}/>
        <span style={{ fontWeight: 600, fontSize: 12, color: '#202124' }}>Inbox</span>
        <span style={{ fontSize: 10, color: '#5F6368', marginLeft: 'auto' }}>1–{visible.length} of 12</span>
      </div>
      {/* inbox rows */}
      <div ref={scrollRef} style={{ flex: 1, overflowY: 'auto' }}>
        {visible.slice().reverse().map((m, i) => (
          <div key={`${conversation.key}-${m.time}`} style={{
            display: 'grid', gridTemplateColumns: '16px 22px 68px minmax(0, 1fr) 42px', gap: 5, alignItems: 'center',
            padding: '7px 10px',
            borderBottom: '1px solid #F4F4F4',
            fontSize: 11,
            background: i === 0 ? '#F8FAFD' : '#fff',
            animation: i === 0 ? 'mirror-in 0.4s both' : 'none',
            fontWeight: i === 0 ? 600 : 400,
          }}>
            <span style={{ color: i === 0 ? '#F5B820' : '#DADCE0' }}>★</span>
            <PortraitAvatar name={m.author} color={m.color} size={20}/>
            <span style={{ color: '#202124', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{m.author}</span>
            <span style={{ color: '#202124', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              <span style={{ color: '#5F6368', fontWeight: 400 }}>[Leadership Exchange] </span>{m.type === 'question' ? conversation.subject : `Re: ${conversation.subject}`}
              <span style={{ color: '#5F6368', fontWeight: 400 }}>: {m.text.slice(0, 36)}…</span>
            </span>
            <span style={{ color: '#5F6368', fontSize: 10, textAlign: 'right', whiteSpace: 'nowrap' }}>{m.time}</span>
          </div>
        ))}
      </div>
      {/* compose preview / reply box */}
      {latest && (
        <div style={{
          margin: '10px 10px 0', padding: 10, borderTop: '2px solid #EA4335',
          background: '#FFF8F7', borderRadius: '0 0 6px 6px', fontSize: 11,
        }}>
          <div style={{ fontSize: 10, color: '#5F6368', marginBottom: 4, letterSpacing: '0.04em' }}>{latest.type === 'question' ? 'NEW THREAD' : 'NEW REPLY'} · {latest.time}</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 4 }}>
            <PortraitAvatar name={latest.author} color={latest.color} size={22}/>
            <div style={{ fontWeight: 600, fontSize: 12, color: '#202124' }}>
              {latest.author} {latest.type === 'question' ? 'started a new thread' : 'replied'}
            </div>
          </div>
          <div style={{ fontSize: 10.5, color: '#5F6368', marginBottom: 4 }}>{latest.type === 'question' ? conversation.subject : `Re: ${conversation.subject}`}</div>
          <div style={{ color: '#3C4043', lineHeight: 1.4 }}>{latest.text}</div>
        </div>
      )}
      <div style={{ flex: 1 }}/>
      <div style={{ padding: '8px 12px', borderTop: '1px solid #EEE', display: 'flex', gap: 6, fontSize: 10, color: '#5F6368' }}>
        <span style={{ padding: '4px 10px', background: '#1A73E8', color: '#fff', borderRadius: 4, fontWeight: 600 }}>↩ Reply all</span>
        <span style={{ padding: '4px 10px', border: '1px solid #DADCE0', borderRadius: 4 }}>Forward</span>
      </div>
    </div>
  );
}

// --- Circle pane: forum-style post with nested replies & reactions
function CirclePane({ visible, conversation }) {
  const [op, ...replies] = visible;
  const scrollRef = useRefH(null);
  useEffectH(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });
    }
  }, [replies.length, conversation.key]);
  return (
    <div style={{
      background: '#fff',
      border: '1px solid var(--site-line)',
      borderRadius: 14,
      overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
      height: 440,
      boxShadow: '0 6px 18px rgba(26,27,58,0.05)',
      fontFamily: 'Host Grotesk, system-ui, sans-serif',
    }}>
      {/* Online community header */}
      <div style={{ padding: '10px 12px', borderBottom: '1px solid #EEF1F5', display: 'flex', alignItems: 'center', gap: 8, background: '#FAFBFD' }}>
        <PlatformIcon kind="circle" size={22}/>
        <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.1 }}>
          <span style={{ fontWeight: 700, fontSize: 12, color: '#0F172A' }}>Leadership Exchange Community</span>
          <span style={{ fontSize: 10, color: '#64748B' }}>{conversation.communitySpace}</span>
        </div>
      </div>
      {/* post */}
      <div style={{ padding: 12, borderBottom: '1px solid #EEF1F5', animation: op ? 'mirror-in 0.4s both' : 'none' }}>
        {op && (
          <>
            <div style={{ display: 'flex', gap: 8, marginBottom: 6 }}>
              <PortraitAvatar name={op.author} color={op.color} size={32}/>
              <div style={{ lineHeight: 1.2 }}>
                <div style={{ fontWeight: 600, fontSize: 12, color: '#0F172A' }}>{op.author}</div>
                <div style={{ fontSize: 10, color: '#64748B' }}>{op.time}</div>
              </div>
            </div>
            <div style={{ fontSize: 13, fontWeight: 600, color: '#0F172A', marginBottom: 4, lineHeight: 1.3 }}>
              {conversation.subject}
            </div>
            <div style={{ fontSize: 11.5, color: '#334155', lineHeight: 1.45 }}>{op.text}</div>
            <ReactionChips reactions={op.reactions}/>
            <div style={{ display: 'flex', gap: 12, marginTop: 7, alignItems: 'center', fontSize: 10, color: '#64748B' }}>
              <span>💬 {Math.max(0, replies.length)} replies</span>
            </div>
          </>
        )}
      </div>
      {/* replies */}
      <div ref={scrollRef} style={{ padding: '8px 12px', display: 'flex', flexDirection: 'column', gap: 10, flex: 1, overflowY: 'auto' }}>
        {replies.map((m, i) => (
          <div key={`${conversation.key}-${i}`} style={{
            display: 'flex', gap: 8, paddingLeft: 8, borderLeft: '2px solid #EEF1F5',
            animation: 'mirror-in 0.4s both',
          }}>
            <PortraitAvatar name={m.author} color={m.color} size={26}/>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 11, marginBottom: 1 }}>
                <span style={{ fontWeight: 600, color: '#0F172A' }}>{m.author}</span>
                <span style={{ color: '#64748B', marginLeft: 6, fontSize: 10 }}>· {m.time}</span>
              </div>
              <div style={{ fontSize: 11, color: '#334155', lineHeight: 1.4 }}>{m.text}</div>
              <ReactionChips reactions={m.reactions} compact/>
            </div>
          </div>
        ))}
      </div>
      <div style={{ padding: '8px 12px', borderTop: '1px solid #EEF1F5', fontSize: 11, color: '#94A3B8' }}>
        Write a reply…
      </div>
    </div>
  );
}

// ---------- PRODUCT RELAY ----------
// A compact, product-like workspace used in the home hero. It shows one
// authentic thread and the delivery state of each connected surface.
function RelayWorkspace() {
  const [phase, setPhase] = useStateH(0);
  const replies = useMemoH(() => [
    { author: 'Priya Raman', role: 'Fractional CFO', via: 'email', color: '#315EFB', text: 'We use one page: runway at plan, downside runway, and the exact trigger for opening a raise.' },
    { author: 'James Okafor', role: 'VP Finance · Quay', via: 'circle', color: '#2F7CFF', text: 'I can share our board template. The scenario table has been the most useful part.' },
    { author: 'Helen Tran', role: 'Chief of Staff · Zego', via: 'slack', color: '#611F69', text: 'Same approach here. We also show the owner and decision date for each trigger.' },
  ], []);

  useEffectH(() => {
    const timer = setInterval(() => setPhase(p => (p + 1) % 4), 2300);
    return () => clearInterval(timer);
  }, []);

  const visibleReplies = replies.slice(0, phase);

  return (
    <div className="relay-workspace">
      <div className="relay-topbar">
        <div className="relay-brand"><img src="assets/logo-mark.svg" alt=""/><strong>Leadership Exchange</strong></div>
        <div className="relay-connected"><span></span> All surfaces connected</div>
      </div>
      <div className="relay-layout">
        <aside className="relay-sidebar">
          <span className="relay-sidebar-label">Community</span>
          {['Home', 'Member asks', 'Events', 'Directory'].map((item, i) => (
            <div className={i === 1 ? 'active' : ''} key={item}>{i === 1 ? '#' : '·'} {item}</div>
          ))}
          <span className="relay-sidebar-label relay-sidebar-label--second">Connected</span>
          {['slack', 'email', 'circle'].map(kind => (
            <div className="relay-surface-mini" key={kind}><PlatformIcon kind={kind} size={18}/>{kind === 'circle' ? 'Community' : kind[0].toUpperCase() + kind.slice(1)}</div>
          ))}
        </aside>

        <section className="relay-thread">
          <div className="relay-thread-head">
            <div><strong># member-asks</strong><span>Practical answers from community operators</span></div>
            <span>184 members</span>
          </div>
          <div className="relay-post">
            <PortraitAvatar name="Marisol Duarte" color="#4242F0" size={36}/>
            <div>
              <div className="relay-author"><strong>Marisol Duarte</strong><span>VP Product · Lumen</span><time>9:12</time></div>
              <p>What does your board actually want to see on a runway slide? I’m rebuilding ours for next Thursday and want it to drive a decision, not just report a number.</p>
              <div className="relay-meta"><span>3 replies</span><span>Saved by 8 members</span></div>
            </div>
          </div>
          <div className="relay-replies">
            {visibleReplies.map((reply, i) => (
              <div className="relay-reply" key={`${phase}-${reply.author}`}>
                <PortraitAvatar name={reply.author} color={reply.color} size={28}/>
                <div><div className="relay-author"><strong>{reply.author}</strong><span>{reply.role}</span><PlatformBadge kind={reply.via}/></div><p>{reply.text}</p></div>
              </div>
            ))}
            {phase === 0 && <div className="relay-syncing"><span></span> Delivering this thread to connected surfaces…</div>}
          </div>
        </section>

        <aside className="relay-delivery">
          <div className="relay-delivery-title"><span>Delivery</span><strong>184 members</strong></div>
          {[
            { kind: 'slack', label: 'Slack', detail: '82 active', at: 1 },
            { kind: 'email', label: 'Email', detail: '64 delivered', at: 2 },
            { kind: 'circle', label: 'Community', detail: '38 notified', at: 3 },
          ].map(item => (
            <div className={'relay-delivery-row' + (phase >= item.at ? ' complete' : '')} key={item.kind}>
              <PlatformIcon kind={item.kind} size={27}/>
              <div><strong>{item.label}</strong><span>{phase >= item.at ? item.detail : 'Queued'}</span></div>
              <i>{phase >= item.at ? '✓' : '·'}</i>
            </div>
          ))}
          <div className="relay-summary">
            <span>Thread health</span>
            <strong>{phase === 0 ? 'Sending' : phase === 1 ? '32% reached' : phase === 2 ? '71% reached' : '100% connected'}</strong>
            <div><i style={{ width: `${phase === 0 ? 8 : phase === 1 ? 32 : phase === 2 ? 71 : 100}%` }}></i></div>
          </div>
        </aside>
      </div>
    </div>
  );
}

// ---------- SELECTED HERO: MEMBERS MEET IN THE MIDDLE ----------
const HERO_COMMUNITY_MEMBERS = {
  marisol: { name: 'Marisol Duarte', short: 'Marisol', role: 'VP Product · Lumen', via: 'slack' },
  priya: { name: 'Priya Raman', short: 'Priya', role: 'VP Finance · Northstar', via: 'email' },
  james: { name: 'James Okafor', short: 'James', role: 'CTO · Quay', via: 'circle' },
  talia: { name: 'Talia Chen', short: 'Talia', role: 'CPO · Arcwell', via: 'circle' },
  lena: { name: 'Lena Park', short: 'Lena', role: 'COO · Fieldwork', via: 'slack' },
  omar: { name: 'Omar Haddad', short: 'Omar', role: 'CEO · Slope', via: 'email' },
};

const HERO_COMMUNITY_THREADS = [
  {
    id: 'ai-training',
    title: 'What’s working for AI training?',
    subtitle: 'Team enablement · Executive peer community',
    events: [
      { type: 'message', id: 'training-1', actor: 'marisol', via: 'slack', text: 'We’ve given everyone access to AI tools. What’s actually getting people past the first-week curiosity?' },
      { type: 'message', id: 'training-2', actor: 'priya', via: 'email', text: 'Small role-based sessions. Finance practiced on vendor reviews and board commentary, not generic prompt tips.' },
      { type: 'reaction', id: 'training-r1', actor: 'marisol', messageId: 'training-2', emoji: '👍' },
      { type: 'message', id: 'training-3', actor: 'james', via: 'circle', text: 'Same here. Our best session used three real workflows, then each team published one reusable example.' },
      { type: 'message', id: 'training-4', actor: 'marisol', via: 'slack', text: 'Did you run those centrally, or let each function own them?' },
      { type: 'message', id: 'training-5', actor: 'lena', via: 'slack', text: 'Central kickoff, then monthly office hours led by volunteers. Peer demos are doing more than formal training ever did.' },
      { type: 'reaction', id: 'training-r2', actor: 'priya', messageId: 'training-5', emoji: '👏' },
      { type: 'reaction', id: 'training-r3', actor: 'talia', messageId: 'training-5', emoji: '💡' },
      { type: 'reaction', id: 'training-r4', actor: 'omar', messageId: 'training-5', emoji: '🙌' },
      { type: 'message', id: 'training-6', actor: 'priya', via: 'email', text: 'That feels sustainable. We can pair office hours with our existing manager forum.' },
      { type: 'message', id: 'training-7', actor: 'omar', via: 'email', text: 'We track one adopted workflow per team, not attendance. Happy to share the simple scorecard.' },
      { type: 'reaction', id: 'training-r5', actor: 'james', messageId: 'training-7', emoji: '👀' },
      { type: 'reaction', id: 'training-r6', actor: 'lena', messageId: 'training-7', emoji: '✅' },
      { type: 'message', id: 'training-8', actor: 'talia', via: 'circle', text: 'Please do. That’s the first metric I’ve heard that doesn’t turn this into compliance theatre.' },
      { type: 'reaction', id: 'training-r7', actor: 'marisol', messageId: 'training-8', emoji: '🙌' },
    ],
  },
  {
    id: 'security-review',
    title: 'How are you reviewing new AI tools?',
    subtitle: 'Security & procurement · Executive peer community',
    events: [
      { type: 'message', id: 'security-1', actor: 'omar', via: 'email', text: 'We have fourteen AI tools somewhere in review. Has anyone made this faster without lowering the security bar?' },
      { type: 'message', id: 'security-2', actor: 'talia', via: 'circle', text: 'We created a lightweight lane for tools with no customer data, no training on inputs, and SSO from day one.' },
      { type: 'reaction', id: 'security-r1', actor: 'omar', messageId: 'security-2', emoji: '✅' },
      { type: 'message', id: 'security-3', actor: 'james', via: 'circle', text: 'The biggest unlock was one shared questionnaire. Vendors answer it once; security and legal review the same record.' },
      { type: 'message', id: 'security-4', actor: 'marisol', via: 'slack', text: 'Do you require a named business owner before the review starts?' },
      { type: 'reaction', id: 'security-r2', actor: 'priya', messageId: 'security-4', emoji: '💡' },
      { type: 'message', id: 'security-5', actor: 'lena', via: 'slack', text: 'Yes, along with a thirty-day pilot plan. It stopped “interesting tool” requests with no real workflow behind them.' },
      { type: 'reaction', id: 'security-r3', actor: 'talia', messageId: 'security-5', emoji: '👏' },
      { type: 'reaction', id: 'security-r4', actor: 'james', messageId: 'security-5', emoji: '👍' },
      { type: 'message', id: 'security-6', actor: 'omar', via: 'email', text: 'That may solve half our queue. I’ll combine the owner and pilot questions into intake.' },
      { type: 'message', id: 'security-7', actor: 'priya', via: 'email', text: 'We also ask what existing tool it replaces. That makes the budget conversation much easier.' },
      { type: 'reaction', id: 'security-r5', actor: 'marisol', messageId: 'security-7', emoji: '👀' },
      { type: 'reaction', id: 'security-r6', actor: 'lena', messageId: 'security-7', emoji: '✅' },
      { type: 'message', id: 'security-8', actor: 'james', via: 'circle', text: 'I’ll post our questionnaire and risk tiers after this. They’re short enough to adapt.' },
    ],
  },
  {
    id: 'tool-sprawl',
    title: 'How are you avoiding AI tool sprawl?',
    subtitle: 'Technology strategy · Executive peer community',
    events: [
      { type: 'message', id: 'sprawl-1', actor: 'lena', via: 'slack', text: 'Every team has a favorite AI tool now. How are you consolidating without killing useful experiments?' },
      { type: 'message', id: 'sprawl-2', actor: 'james', via: 'circle', text: 'We standardize the foundation models and stay flexible at the workflow layer. Fewer contracts, plenty of room to test.' },
      { type: 'reaction', id: 'sprawl-r1', actor: 'lena', messageId: 'sprawl-2', emoji: '👍' },
      { type: 'message', id: 'sprawl-3', actor: 'priya', via: 'email', text: 'Finance reviews overlapping spend quarterly, but teams keep anything with a clear owner and active usage.' },
      { type: 'message', id: 'sprawl-4', actor: 'talia', via: 'circle', text: 'We publish an approved toolbox with one sentence on what each product is actually best at.' },
      { type: 'reaction', id: 'sprawl-r2', actor: 'marisol', messageId: 'sprawl-4', emoji: '💡' },
      { type: 'message', id: 'sprawl-5', actor: 'marisol', via: 'slack', text: 'Love that. Does the list include experiments, or only tools cleared for broad use?' },
      { type: 'message', id: 'sprawl-6', actor: 'talia', via: 'circle', text: 'Both. “Approved,” “limited pilot,” and “do not use with company data.” The plain language matters.' },
      { type: 'reaction', id: 'sprawl-r3', actor: 'omar', messageId: 'sprawl-6', emoji: '👏' },
      { type: 'reaction', id: 'sprawl-r4', actor: 'james', messageId: 'sprawl-6', emoji: '✅' },
      { type: 'message', id: 'sprawl-7', actor: 'omar', via: 'email', text: 'We’re missing the middle state. Everything is either approved forever or blocked. I’m borrowing those labels.' },
      { type: 'reaction', id: 'sprawl-r5', actor: 'talia', messageId: 'sprawl-7', emoji: '🙌' },
      { type: 'message', id: 'sprawl-8', actor: 'lena', via: 'slack', text: 'This is exactly what I needed. I’ll share our inventory template once we add the three states.' },
      { type: 'reaction', id: 'sprawl-r6', actor: 'priya', messageId: 'sprawl-8', emoji: '👀' },
    ],
  },
];

function shuffledThreadOrder(lastThread) {
  const order = [0, 1, 2].sort(() => Math.random() - .5);
  if (lastThread != null && order[0] === lastThread) [order[0], order[1]] = [order[1], order[0]];
  return order;
}

function HeroOriginBadge({ kind }) {
  return (
    <span className={`hero-origin hero-origin-${kind}`}>
      <PlatformIcon kind={kind} size={11}/>
      {kind === 'circle' ? 'Community' : kind[0].toUpperCase() + kind.slice(1)}
    </span>
  );
}

function CommunityThreadsHero() {
  const [threadOrder, setThreadOrder] = useStateH(() => shuffledThreadOrder());
  const [threadCursor, setThreadCursor] = useStateH(0);
  const [eventCount, setEventCount] = useStateH(1);
  const [cycle, setCycle] = useStateH(0);
  const reducedMotion = window.useReducedMotion();
  const threadIndex = threadOrder[threadCursor];
  const thread = HERO_COMMUNITY_THREADS[threadIndex];
  const shownEvents = thread.events.slice(0, eventCount);
  const currentEvent = shownEvents[shownEvents.length - 1];

  useEffectH(() => {
    if (reducedMotion) {
      setEventCount(thread.events.length);
      return undefined;
    }
    const complete = eventCount >= thread.events.length;
    const nextEvent = thread.events[eventCount];
    const delay = complete ? 3000 : (nextEvent?.type === 'reaction' ? 850 : 1350) + Math.round(Math.random() * 420);
    const timer = setTimeout(() => {
      if (!complete) {
        setEventCount(count => count + 1);
        return;
      }
      if (threadCursor < threadOrder.length - 1) {
        setThreadCursor(cursor => cursor + 1);
      } else {
        setThreadOrder(shuffledThreadOrder(threadIndex));
        setThreadCursor(0);
      }
      setEventCount(1);
      setCycle(value => value + 1);
    }, delay);
    return () => clearTimeout(timer);
  }, [eventCount, thread.events, threadCursor, threadOrder, threadIndex, reducedMotion]);

  const reactions = {};
  shownEvents.forEach(event => {
    if (event.type !== 'reaction') return;
    reactions[event.messageId] ||= {};
    reactions[event.messageId][event.emoji] ||= [];
    reactions[event.messageId][event.emoji].push(event.actor);
  });
  const visibleMessages = shownEvents.filter(event => event.type === 'message').slice(-4);

  return (
    <div className="community-hero-stage">
      <div className="community-hero-meta">
        <div className="community-hero-meta-copy">
          <span><i></i>Watch Synchronize in action</span>
          <strong>One conversation stays live across your whole community</strong>
        </div>
        <div className="community-hero-surfaces" aria-label="Connected surfaces">
          {['slack', 'email', 'circle'].map(kind => (
            <span key={kind}>
              <PlatformIcon kind={kind} size={20}/>
              {kind === 'circle' ? 'Community' : kind[0].toUpperCase() + kind.slice(1)}
            </span>
          ))}
        </div>
      </div>

      {Object.entries(HERO_COMMUNITY_MEMBERS).map(([key, member]) => (
        <div className={`hero-member hero-member-${key} ${currentEvent?.actor === key ? 'is-active' : ''}`} key={key}>
          <PortraitAvatar name={member.name} size={46}/>
          <strong>{member.short}</strong>
          <small>{member.role}</small>
          <span className="hero-member-platform"><PlatformIcon kind={member.via} size={15}/>{member.via === 'circle' ? 'Community' : member.via[0].toUpperCase() + member.via.slice(1)}</span>
        </div>
      ))}

      {Object.keys(HERO_COMMUNITY_MEMBERS).map(key => <span className={`hero-connection hero-connection-${key}`} key={`line-${key}`}></span>)}

      {currentEvent && (
        <span key={`${thread.id}-${currentEvent.id}`} className={`hero-network-flight hero-flight-${currentEvent.actor} ${currentEvent.type === 'reaction' ? 'is-reaction' : 'is-message'}`}>
          {currentEvent.type === 'reaction' ? currentEvent.emoji : ''}
        </span>
      )}

      <section className="hero-shared-thread" key={`${thread.id}-${cycle}`}>
        <header>
          <img src="assets/logo-mark.svg" alt=""/>
          <div><strong>{thread.title}</strong><span>{thread.subtitle}</span></div>
          <i></i>
        </header>
        <div className="hero-thread-body">
          {visibleMessages.map(message => {
            const member = HERO_COMMUNITY_MEMBERS[message.actor];
            const messageReactions = reactions[message.id] || {};
            return (
              <article className="hero-thread-message" key={message.id}>
                <PortraitAvatar name={member.name} size={28} radius={7}/>
                <div>
                  <div className="hero-thread-author"><strong>{member.name}</strong><HeroOriginBadge kind={message.via}/></div>
                  <p>{message.text}</p>
                  {Object.keys(messageReactions).length > 0 && (
                    <div className="hero-thread-reactions">
                      {Object.entries(messageReactions).map(([emoji, actors]) => (
                        <span className="hero-thread-reaction" key={emoji}>
                          <b>{emoji}</b><strong>{actors.length}</strong>
                          <i>{actors.map(actor => <PortraitAvatar key={actor} name={HERO_COMMUNITY_MEMBERS[actor].name} size={15}/>)}</i>
                        </span>
                      ))}
                    </div>
                  )}
                </div>
              </article>
            );
          })}
        </div>
        <footer>
          <span>Every post, reply and reaction stays connected</span>
          <div>{HERO_COMMUNITY_THREADS.map((_, index) => <i className={index === threadIndex ? 'active' : ''} key={index}></i>)}</div>
        </footer>
      </section>
    </div>
  );
}

// ---------- Dispatcher ----------
function HeroAnimation() {
  return <div className="hero-animation-frame"><CommunityThreadsHero/></div>;
}

Object.assign(window, { HeroAnimation, CommunityThreadsHero, RelayWorkspace, FlowMotif, OrbitMotif, MirrorMotif, SlackPane, EmailPane, CirclePane, PlatformIcon, Avatar, PortraitAvatar, PlatformBadge });
