const { Icon } = window.PryzeDesignSystem_b2bad9;
const { USER, COMPANIES, ORBIT_ACTIVE_USERS, LEADERBOARD_PEERS, FILLER_PEERS } = window.PryzeData;

const RALLY_MAX = 40;
// Heat escalates with the stack — never cycles back.
const RALLY_COLORS = [
  { min: 2, c: 'var(--lime-500)' }, { min: 6, c: 'var(--orange-500)' }, { min: 13, c: 'var(--orange-600)' },
  { min: 22, c: 'var(--violet-300)' }, { min: 31, c: 'var(--violet-500)' },
];
function heatColor(n) { const m = RALLY_COLORS.filter(t => n >= t.min); return (m[m.length - 1] || RALLY_COLORS[0]).c; }
const RALLY_HEAT = [
  { min: 2, label: 'warming up' }, { min: 4, label: 'heating up' }, { min: 6, label: 'on fire' },
  { min: 9, label: 'unstoppable' }, { min: 13, label: 'no notes' }, { min: 22, label: 'certified menace' }, { min: 31, label: 'absolute unit' },
];
function heatFor(n) { const m = RALLY_HEAT.filter(t => n >= t.min); return (m[m.length - 1] || RALLY_HEAT[0]).label; }
const RALLY_PRAISE = [
  { min: 2, word: 'nice one' }, { min: 4, word: 'on a roll' }, { min: 6, word: 'spreading love' },
  { min: 9, word: 'hype machine' }, { min: 13, word: 'crew favorite' }, { min: 18, word: 'kudos legend' },
  { min: 24, word: 'menace to morale' }, { min: 32, word: 'shift MVP energy' }, { min: 38, word: 'the whole crew felt that' },
];
const KUDOS_EMOJIS = ['🎉','🙌','🥳','🎊','✨','🤩','💫','🏆'];
const MIN_RING_R = 84, RING_STEP = 50, RING_SIZES = [44, 40, 38], MAX_PER_RING = 14, MAX_RINGS = 3;
const STARS = [[6,12,2,.6,3.2,0],[14,78,1.5,.4,4.1,.4],[22,41,2.5,.7,2.8,.8],[30,88,1.5,.35,3.6,.2],[9,55,2,.5,4.4,1],[38,7,2,.55,3,.6],[47,93,1.5,.4,3.8,1.3],[52,28,2.5,.65,2.6,.3],[58,68,1.5,.35,4.6,.9],[63,4,2,.5,3.4,1.5],[70,84,2.5,.6,3.9,.5],[74,48,1.5,.3,4.2,1.1],[81,17,2,.45,3.1,.7],[87,72,1.5,.35,4.8,.2],[92,38,2.5,.55,2.9,1.4],[96,90,1.5,.3,3.7,.6],[18,62,1.5,.3,4.3,1.2],[34,33,1.5,.35,3.3,.9],[43,58,2,.45,4,.1],[66,25,1.5,.3,3.5,1.6],[78,60,2,.4,4.5,.8],[27,96,1.5,.3,3.2,.4],[3,34,2,.5,4.7,1.7],[89,8,1.5,.35,3.6,1]];
const MAX_PARTICLES = 96;
const HINT_KEY = 'pryze.orbit.hintSeen';

// Ring color encodes streak tier — not decoration.
const STREAK_TIERS = [
  { min: 10, color: 'var(--lime-500)', short: '10d+' },
  { min: 5, color: 'var(--lime-500)', short: '5–9d' },
  { min: 1, color: 'var(--lime-500)', short: '1–4d' },
  { min: 0, color: 'var(--lime-500)', short: 'None' },
];
function tierFor(streak) { return STREAK_TIERS.find(t => (streak || 0) >= t.min) || STREAK_TIERS[3]; }

function vibeStatement(count) {
  if (count >= 15) return "Whole store's in orbit rn 🔥";
  if (count >= 7) return "Store's fully locked in rn 🔥";
  if (count >= 4) return 'Decent crowd orbiting today 👀';
  if (count >= 1) return 'A few of you are locked in 🌌';
  return 'Quiet orbit right now — be the first ✨';
}

function initialsOf(name) { return name.split(' ').map(n => n[0]).join('').slice(0, 2); }

/* Rings are derived from the space actually available, and each ring only seats as many
   people as fit at a legible arc spacing. Everyone beyond the seat count goes to the roster. */
function planRings(maxRadius) {
  const radii = [];
  for (let r = MIN_RING_R; radii.length < MAX_RINGS && r <= maxRadius + 1; r += RING_STEP) radii.push(r);
  if (!radii.length) radii.push(Math.max(60, maxRadius));
  radii[radii.length - 1] = Math.max(radii[radii.length - 1], maxRadius);
  return radii.map((radius, i) => {
    const size = RING_SIZES[Math.min(i, RING_SIZES.length - 1)];
    const cap = Math.max(2, Math.min(MAX_PER_RING, Math.floor((2 * Math.PI * radius) / (size + 24))));
    return { radius, size, cap, duration: 84 + i * 20, reverse: i % 2 === 1 };
  });
}

function useOrbitLayout(users, maxRadius) {
  return React.useMemo(() => {
    const rings = planRings(maxRadius);
    const seats = rings.reduce((s, r) => s + r.cap, 0);
    const shown = Math.min(users.length, seats);
    // Spread people across rings in proportion to each ring's capacity.
    const counts = rings.map(r => Math.min(r.cap, Math.floor((shown * r.cap) / seats)));
    let left = shown - counts.reduce((a, b) => a + b, 0);
    for (let i = 0; left > 0; i = (i + 1) % rings.length) { if (counts[i] < rings[i].cap) { counts[i]++; left--; } }
    const placed = [];
    let idx = 0;
    rings.forEach((ring, ri) => {
      for (let j = 0; j < counts[ri]; j++, idx++) {
        const startAngle = ((j * 360) / counts[ri] + (ri * 180) / Math.max(1, counts[ri])) % 360;
        placed.push({ ...users[idx], radius: ring.radius, duration: ring.duration, reverse: ring.reverse, startAngle, size: ring.size, tier: tierFor(users[idx].streak) });
      }
    });
    return { placed, overflow: users.slice(shown), radii: rings.map(r => r.radius) };
  }, [users, maxRadius]);
}

/* Shared bottom-sheet with real dialog semantics: focus capture, Esc, focus return. */
function OrbitSheet({ label, onClose, children }) {
  const cardRef = React.useRef(null);
  const returnRef = React.useRef(null);
  React.useEffect(() => {
    returnRef.current = document.activeElement;
    const card = cardRef.current;
    if (card) {
      const first = card.querySelector('[data-autofocus]') || card.querySelector('button, [href], input, [tabindex]:not([tabindex="-1"])');
      (first || card).focus();
    }
    function onKey(e) {
      if (e.key === 'Escape') { e.stopPropagation(); onClose(); return; }
      if (e.key !== 'Tab' || !card) return;
      const f = Array.from(card.querySelectorAll('button, [href], input, [tabindex]:not([tabindex="-1"])')).filter(el => el.offsetParent !== null);
      if (!f.length) return;
      const i = f.indexOf(document.activeElement);
      if (e.shiftKey && (i <= 0)) { e.preventDefault(); f[f.length - 1].focus(); }
      else if (!e.shiftKey && i === f.length - 1) { e.preventDefault(); f[0].focus(); }
    }
    document.addEventListener('keydown', onKey, true);
    return () => { document.removeEventListener('keydown', onKey, true); if (returnRef.current && returnRef.current.focus) returnRef.current.focus(); };
  }, [onClose]);
  return (
    <div className="orbit-info-overlay" onClick={onClose}>
      <div className="orbit-info-card" role="dialog" aria-modal="true" aria-label={label} tabIndex={-1} ref={cardRef} onClick={e => e.stopPropagation()}>
        <button className="orbit-info-close" onClick={onClose} aria-label="Close"><Icon name="x" weight="bold" size={16} color="var(--fg-primary)" /></button>
        {children}
      </div>
    </div>
  );
}

function OrbitBubble({ user, kudos, tick, onOpen, onKudos }) {
  const btnRef = React.useRef(null);
  const press = React.useRef({ timer: null, long: false, x: 0, y: 0 });
  const [combo, setCombo] = React.useState(0);
  const comboTimer = React.useRef(null);
  React.useEffect(() => () => { clearTimeout(press.current.timer); clearTimeout(comboTimer.current); }, []);
  const fireKudos = () => {
    const r = btnRef.current.getBoundingClientRect();
    onKudos(user, r.left + r.width / 2, r.top);
    setCombo(c => c + 1);
    clearTimeout(comboTimer.current);
    comboTimer.current = setTimeout(() => setCombo(0), 1100);
  };
  const onPointerDown = e => {
    const p = press.current;
    p.long = false; p.x = e.clientX; p.y = e.clientY;
    clearTimeout(p.timer);
    p.timer = setTimeout(() => { p.long = true; if (navigator.vibrate) navigator.vibrate(12); onOpen(user); }, 400);
  };
  const onPointerMove = e => {
    const p = press.current;
    if (p.timer && (Math.abs(e.clientX - p.x) > 8 || Math.abs(e.clientY - p.y) > 8)) { clearTimeout(p.timer); p.timer = null; }
  };
  const onPointerUp = () => {
    const p = press.current;
    clearTimeout(p.timer); p.timer = null;
    if (p.long) { p.long = false; return; }
    fireKudos();
  };
  const onPointerLeave = () => { const p = press.current; clearTimeout(p.timer); p.timer = null; p.long = false; };
  return (
    <div className="orbit-ring" style={{ width: user.radius * 2, height: user.radius * 2 }} aria-hidden="true">
      <div className="orbit-pivot" style={{ transform: `rotate(${user.startAngle}deg)`, animationDuration: user.duration + 's', animationDelay: -(user.startAngle / 360 * user.duration) + 's', animationDirection: user.reverse ? 'reverse' : 'normal' }}>
        <div className="orbit-anchor" style={{ transform: `translateX(${user.radius}px)` }}>
          <div className="orbit-bubble-wrap" style={{ width: user.size, height: user.size, animationDuration: user.duration + 's', animationDelay: -(user.startAngle / 360 * user.duration) + 's', animationDirection: user.reverse ? 'reverse' : 'normal', transform: `translate(-50%,-50%) rotate(${-user.startAngle}deg)`, '--ring-color': user.tier.color }}>
            <button ref={btnRef} className={'orbit-bubble' + (combo ? ' is-combo' : '')} tabIndex={-1}
              onContextMenu={e => e.preventDefault()}
              onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} onPointerLeave={onPointerLeave} onPointerCancel={onPointerLeave}>
              <span className="orbit-bubble-glow" />
              {user.photo ? <img src={user.photo} alt="" /> : <span className="orbit-bubble-initials">{initialsOf(user.name)}</span>}
            </button>
            <span className="orbit-kudos-badge" aria-hidden="true">
              <Icon name="heart" weight="fill" size={9} color="var(--neutral-950)" />
            </span>
            {combo > 0 && <span key={combo} className="orbit-combo">+{combo}</span>}
            {tick && !combo ? <span key={tick} className="orbit-kudos-tick">+1</span> : null}
            <span className="orbit-bubble-name">{user.name.split(' ')[0]}{user.name.split(' ')[1] ? ' ' + user.name.split(' ')[1][0] + '.' : ''}</span>
          </div>
        </div>
      </div>
    </div>
  );
}

function OrbitInfoBody({ user, kudos, onKudos }) {
  const btnRef = React.useRef(null);
  return (
    <React.Fragment>
      <div className="orbit-info-head">
        {user.photo ? <img className="orbit-info-avatar" src={user.photo} alt="" style={{ borderColor: user.tier ? user.tier.color : 'var(--color-primary)' }} /> : <span className="orbit-info-avatar orbit-info-avatar-fallback">{initialsOf(user.name)}</span>}
        <div className="orbit-info-headtext">
          <p className="orbit-info-name">{user.name}</p>
          <span className="orbit-info-status">{user.status || user.bio}</span>
        </div>
      </div>
      <button ref={btnRef} data-autofocus className="orbit-info-kudos-btn" onClick={() => { const r = btnRef.current.getBoundingClientRect(); onKudos(user, r.left + r.width / 2, r.top); }}>
        <Icon name="heart" weight="fill" size={18} color="var(--neutral-950)" />
        Send kudos to {user.name.split(' ')[0]}
      </button>
      <div className="orbit-info-stats">
        <div className="orbit-info-stat"><span className="orbit-info-stat-value">{kudos.toLocaleString()}</span><span className="orbit-info-stat-label">Kudos</span></div>
        <div className="orbit-info-stat"><span className="orbit-info-stat-value">{2 + (user.points % 5)}</span><span className="orbit-info-stat-label">Goals crushed</span></div>
        <div className="orbit-info-stat"><span className="orbit-info-stat-value">{user.streak || 0}d</span><span className="orbit-info-stat-label">Streak</span></div>
      </div>
    </React.Fragment>
  );
}

function OrbitScreen() {
  const stageRef = React.useRef(null);
  const [maxRadius, setMaxRadius] = React.useState(120);
  const [paused, setPaused] = React.useState(false);
  React.useEffect(() => {
    const el = stageRef.current;
    if (!el) return;
    function measure(entry) {
      // offsetWidth/Height are scale-independent; getBoundingClientRect() is not.
      const w = el.offsetWidth, ht = el.offsetHeight;
      if (w && ht) setMaxRadius(Math.max(60, Math.min(w, ht) / 2 - 34));
    }
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  const layout = useOrbitLayout(ORBIT_ACTIVE_USERS, maxRadius);
  const users = layout.placed;
  const overflow = layout.overflow;
  const ringRadii = layout.radii;

  const [kudosLifetime, setKudosLifetime] = React.useState(() => Object.fromEntries(ORBIT_ACTIVE_USERS.map(u => [u.id, u.kudosLifetime])));
  const [ticks, setTicks] = React.useState({});
  const [rally, setRally] = React.useState(null);
  const [praise, setPraise] = React.useState(null);
  const rallyN = React.useRef(0);
  const rallyTimer = React.useRef(null);
  const rallyWho = React.useRef(new Set());
  const burstTimers = React.useRef(new Set());
  const burstBatch = React.useRef(0);
  React.useEffect(() => () => { clearTimeout(rallyTimer.current); burstTimers.current.forEach(clearTimeout); }, []);
  const [openUser, setOpenUser] = React.useState(null);
  const [sheet, setSheet] = React.useState(null); // 'more' | 'store'
  const [query, setQuery] = React.useState('');
  const [bursts, setBursts] = React.useState([]);
  const [showHint, setShowHint] = React.useState(() => { try { return !localStorage.getItem(HINT_KEY); } catch (e) { return true; } });
  const company = COMPANIES[USER.company];
  const dateLabel = React.useMemo(() => new Date().toLocaleDateString(undefined, { month: 'short', day: 'numeric' }), []);
  const storeToday = React.useMemo(() => ORBIT_ACTIVE_USERS.reduce((s, u) => s + 20 + (u.points % 70), 0), []);
  const storeRank = React.useMemo(() => {
    const totals = {};
    [...LEADERBOARD_PEERS, ...FILLER_PEERS].forEach(p => { if (p.store) totals[p.store] = (totals[p.store] || 0) + p.points; });
    totals[USER.store] = (totals[USER.store] || 0) + USER.points;
    const order = Object.keys(totals).sort((a, b) => totals[b] - totals[a]);
    return { rank: order.indexOf(USER.store) + 1, of: order.length };
  }, []);
  const teamMetrics = React.useMemo(() => {
    const loops = ORBIT_ACTIVE_USERS.reduce((s, u) => s + 2 + (u.points % 5), 0);
    const lifetimeHours = ORBIT_ACTIVE_USERS.reduce((s, u) => s + 40 + (u.points % 380), 0);
    return [
      { key: 'rank', icon: 'trophy', color: 'var(--lime-500)', value: '#' + storeRank.rank, label: 'Store rank' },
      { key: 'hours', icon: 'users-three', color: 'var(--violet-300)', value: lifetimeHours.toLocaleString(), unit: 'hrs', label: 'Time together' },
      { key: 'points', icon: 'check-circle', color: 'var(--orange-500)', value: String(loops), label: 'Goals crushed' },
    ];
  }, [storeRank]);
  const topOrbiter = React.useMemo(() => ORBIT_ACTIVE_USERS.slice().sort((a, b) => b.points - a.points)[0], []);
  const anySheet = !!openUser || !!sheet;
  const roster = React.useMemo(() => {
    const q = query.trim().toLowerCase();
    return q ? ORBIT_ACTIVE_USERS.filter(u => u.name.toLowerCase().includes(q)) : ORBIT_ACTIVE_USERS;
  }, [query]);

  function dismissHint() {
    if (!showHint) return;
    setShowHint(false);
    try { localStorage.setItem(HINT_KEY, '1'); } catch (e) {}
  }

  function shortName(name) { const p = name.split(' '); return p[1] ? p[0] + ' ' + p[1][0] + '.' : p[0]; }

  function bumpRally(user) {
    rallyN.current = Math.min(RALLY_MAX, rallyN.current + 1);
    if (user) rallyWho.current.add(shortName(user.name));
    const n = rallyN.current;
    const who = Array.from(rallyWho.current);
    const target = who.length === 1 ? who[0] : who.length + ' teammates';
    if (n >= 1) setRally({ n, target });
    clearTimeout(rallyTimer.current);
    rallyTimer.current = setTimeout(() => {
      if (rallyN.current >= 2) {
        const tiers = RALLY_PRAISE.filter(t => rallyN.current >= t.min);
        const t = tiers[tiers.length - 1];
        const who2 = Array.from(rallyWho.current);
        setPraise({ id: Date.now(), word: t.word, n: rallyN.current, target: who2.length === 1 ? who2[0] : 'the crew' });
        setTimeout(() => setPraise(p => p && { ...p, leaving: true }), 3400);
        setTimeout(() => setPraise(null), 4900);
      }
      rallyN.current = 0;
      rallyWho.current.clear();
      setRally(null);
    }, 1150);
  }

  function sendKudos(user, clientX, clientY) {
    bumpRally(user);
    setKudosLifetime(k => ({ ...k, [user.id]: (k[user.id] || 0) + 1 }));
    setTicks(t => ({ ...t, [user.id]: Date.now() }));
    dismissHint();
    const screenRect = stageRef.current.closest('.orbit-screen').getBoundingClientRect();
    const x = (clientX != null ? clientX : screenRect.left + screenRect.width / 2) - screenRect.left;
    const y = (clientY != null ? clientY : screenRect.top + screenRect.height / 2) - screenRect.top;
    const W = screenRect.width, H = screenRect.height;
    const batch = ++burstBatch.current;
    // Waves keep arriving after the tap — the stream never quite stops, but each
    // particle drifts slowly so the effect reads as unstoppable, not frantic.
    const heavy = rallyN.current <= 3;
    const waves = heavy ? 3 : 2, perWave = heavy ? 8 : 6;
    const spawn = (wave) => {
      const parts = Array.from({ length: perWave }, (_, i) => {
        const fromTap = wave === 0 && i < 5;
        const sx = fromTap ? x + (Math.random() - 0.5) * 46 : 18 + Math.random() * (W - 36);
        const sy = fromTap ? y : H - Math.random() * H * 0.3;
        return {
          id: Date.now() + Math.random(), batch, emoji: KUDOS_EMOJIS[Math.floor(Math.random() * KUDOS_EMOJIS.length)],
          x: sx, y: sy, drift: (Math.random() - 0.5) * 64, rise: -(sy + 90),
          delay: Math.random() * 380, size: 22 + Math.random() * 16, spin: (Math.random() - 0.5) * 14,
          dur: 3 + Math.random() * 1.2, sway: 2.2 + Math.random() * 1.4,
        };
      });
      setBursts(b => {
        const next = [...b, ...parts];
        return next.length > MAX_PARTICLES ? next.slice(next.length - MAX_PARTICLES) : next;
      });
    };
    spawn(0);
    for (let w = 1; w < waves; w++) {
      const wt = setTimeout(() => { burstTimers.current.delete(wt); spawn(w); }, w * 420);
      burstTimers.current.add(wt);
    }
    const t = setTimeout(() => {
      burstTimers.current.delete(t);
      setBursts(b => b.filter(p => p.batch !== batch));
    }, 5200);
    burstTimers.current.add(t);
  }

  return (
    <div className="orbit-screen">
      <div className="orbit-cosmos" aria-hidden="true">
        {STARS.map((s, i) => <span key={i} style={{ top: s[0] + '%', left: s[1] + '%', width: s[2], height: s[2], opacity: s[3], animationDuration: s[4] + 's', animationDelay: s[5] + 's' }}></span>)}
        {STARS.slice(0, 14).map((s, i) => <span key={'b' + i} style={{ top: (100 - s[0]) + '%', left: (100 - s[1]) + '%', width: s[2], height: s[2], opacity: s[3] * .8, animationDuration: (s[4] + 1.4) + 's', animationDelay: (s[5] + .7) + 's' }}></span>)}
        <i className="orbit-nebula orbit-nebula-a"></i><i className="orbit-nebula orbit-nebula-b"></i><i className="orbit-nebula orbit-nebula-c"></i>
        <i className="orbit-vignette"></i>
      </div>
      <div className="orbit-header">
        <div className="orbit-title-row">
          <p className="orbit-header-title">Orbit</p>
          <button className="orbit-info-btn" onClick={() => setSheet('about')} aria-label="What is Orbit?"><Icon name="info" weight="fill" size={16} color="var(--violet-300)" /></button>
        </div>
        <div className="orbit-header-meta">
          {company && company.logo && <img src={company.logo} alt="" />}
          <span>{(company && company.name) || 'Pryze'} · {USER.store} · {dateLabel}</span>
        </div>
      </div>
      <div className={'orbit-metrics-wrap' + (rally || praise ? ' is-rallying' : '')}>
        <div className="orbit-rally-slot" aria-live="polite">
          {rally && !praise && <div className="orbit-rally" key="r" style={{ '--grow': Math.pow(Math.min(1, (rally.n - 1) / 11), 0.55), '--heat': heatColor(rally.n) }}>
            <span className="orbit-rally-bub">
              <i className="orbit-rally-pulse" key={rally.n}></i>
              <span className="orbit-rally-n" key={rally.n}>{rally.n}</span>
            </span>
            <span className="orbit-rally-copy">
              <span className="orbit-rally-heat" key={heatFor(rally.n)}>{heatFor(rally.n)}</span>
              <span className="orbit-rally-label">kudos to {rally.target}</span>
            </span>
          </div>}
          {praise && <div className={'orbit-rally-pop' + (praise.leaving ? ' is-leaving' : '')} key={praise.id}>
            <i className="orbit-rally-flash"></i><i className="orbit-rally-rays"></i><i className="orbit-rally-star"></i>{[0,1,2,3,4,5,6,7].map(i => <i key={i} className="orbit-rally-shard" style={{ '--a': (i * 45 + 12) + 'deg', '--d': (i % 3 === 0 ? 150 : i % 2 === 0 ? 116 : 92) + 'px', '--sd': (i * 26) + 'ms' }}></i>)}<i className="orbit-rally-bloom"></i><i className="orbit-rally-sweep"></i>
            <span className="orbit-rally-pop-word">{praise.word}</span>
            <span className="orbit-rally-pop-sub">{praise.n} kudos to {praise.target}</span>
          </div>}
        </div>
        <p className="orbit-metrics-title">Team effort</p>
        <div className="orbit-metrics">
          {teamMetrics.map(m => (
            <div className="orbit-metric" key={m.key}>
              <span className="orbit-metric-orb"><Icon name={m.icon} weight="fill" size={16} color={m.color} /></span>
              <span className="orbit-metric-value">{m.value}{m.unit && <em>{m.unit}</em>}</span>
              <span className="orbit-metric-label">{m.label}</span>
            </div>
          ))}
        </div>
      </div>
      <p className="orbit-vibe">{vibeStatement(ORBIT_ACTIVE_USERS.length)}</p>
      <div className={'orbit-stage' + (paused || anySheet ? ' is-paused' : '')} ref={stageRef}
        onPointerEnter={() => setPaused(true)} onPointerLeave={() => setPaused(false)}
        onPointerDown={() => setPaused(true)} onPointerUp={() => setPaused(false)}>
        <div className="orbit-paths" aria-hidden="true">{ringRadii.map(r => <i key={r} style={{ width: r * 2, height: r * 2 }} />)}</div>
        <button className="orbit-center" style={{ '--brand-color': (company && company.bg) || 'var(--color-primary)' }} onClick={() => setSheet('store')} aria-label={`${USER.store} today: ${storeToday.toLocaleString()} points earned together. Open store summary.`}>
          {company && company.logo ? <img src={company.logo} alt="" /> : <span className="orbit-center-fallback">{(company && company.initials) || 'P'}</span>}
        </button>
        {users.map(u => <OrbitBubble key={u.id} user={u} kudos={kudosLifetime[u.id]} tick={ticks[u.id]} onOpen={setOpenUser} onKudos={sendKudos} />)}
        {/* Accessible equivalent of the orbit field — lists everyone, not just the seated bubbles */}
        <div className="orbit-sr-list">
          <p>{ORBIT_ACTIVE_USERS.length} coworkers in orbit today at {USER.store}.</p>
          {ORBIT_ACTIVE_USERS.map(u => (
            <div key={u.id}>
              <button onClick={() => setOpenUser({ ...u, tier: tierFor(u.streak) })}>{u.name}, {u.streak || 0} day streak, {kudosLifetime[u.id]} kudos. Open profile.</button>
              <button onClick={() => sendKudos(u)}>Send kudos to {u.name}</button>
            </div>
          ))}
        </div>
      </div>
      {bursts.map(b => <span key={b.id} className="orbit-emoji-burst" style={{ left: b.x, top: b.y, fontSize: b.size, '--rise': b.rise + 'px', animationDelay: b.delay + 'ms', animationDuration: b.dur + 's' }} aria-hidden="true"><span className="orbit-emoji-inner" style={{ '--drift': b.drift + 'px', '--spin': b.spin + 'deg', animationDuration: b.sway + 's' }}>{b.emoji}</span></span>)}
      <div className="orbit-footer">
        <button className="orbit-more-chip" onClick={() => setSheet('more')}>{overflow.length > 0 ? `See all ${ORBIT_ACTIVE_USERS.length} in orbit · +${overflow.length} off-screen` : `See all ${ORBIT_ACTIVE_USERS.length} in orbit`}</button>
        {showHint && <p className="orbit-hint">Double-tap anyone to send kudos · press &amp; hold to view profile<button className="orbit-hint-dismiss" onClick={dismissHint} aria-label="Dismiss tip"><Icon name="x" weight="bold" size={11} color="var(--fg-muted)" /></button></p>}
      </div>

      {openUser && <OrbitSheet label={openUser.name} onClose={() => setOpenUser(null)}>
        <OrbitInfoBody user={openUser} kudos={kudosLifetime[openUser.id]} onKudos={sendKudos} />
      </OrbitSheet>}

      {sheet === 'about' && (
        <div className="orbit-about-overlay" onClick={() => setSheet(null)} role="dialog" aria-modal="true" aria-label="What is Orbit?">
          <div className="orbit-about-card" onClick={e => e.stopPropagation()}>
            <button className="orbit-about-close" onClick={() => setSheet(null)} aria-label="Close"><Icon name="x" weight="bold" size={14} color="currentColor" /></button>
            <div className="orbit-about-viz" aria-hidden="true">
              <i className="oav-ring"></i>
              <span className="oav-sun">{company && company.logo ? <img src={company.logo} alt="" /> : <b>{(company && company.initials) || 'P'}</b>}</span>
              <span className="oav-dot oav-d1"></span><span className="oav-dot oav-d2"></span><span className="oav-dot oav-d3"></span>
              <span className="oav-emoji oav-e1">🎉</span>
            </div>
            <p className="orbit-about-title">This is Orbit</p>
            <p className="orbit-about-body">Your store, live. Everyone on shift today circles the sun.</p>
            <div className="orbit-about-list">
              <div className="orbit-about-row"><span className="oar-icon"><Icon name="heart" weight="fill" size={15} color="var(--lime-500)" /></span><span>Double-tap a face to send kudos — keep tapping to stack</span></div>
              <div className="orbit-about-row"><span className="oar-icon"><Icon name="hand-tap" weight="fill" size={15} color="var(--violet-300)" /></span><span>Press &amp; hold to view their profile</span></div>
              <div className="orbit-about-row"><span className="oar-icon"><Icon name="storefront" weight="fill" size={15} color="var(--orange-500)" /></span><span>Tap the sun for store totals</span></div>
            </div>
          </div>
        </div>
      )}

      {sheet === 'more' && <OrbitSheet label="Everyone in orbit today" onClose={() => setSheet(null)}>
        <p className="orbit-sheet-title">In orbit today · {ORBIT_ACTIVE_USERS.length}</p>
        {ORBIT_ACTIVE_USERS.length > 10 && <input className="orbit-search" type="search" placeholder="Search coworkers" aria-label="Search coworkers in orbit" value={query} onChange={e => setQuery(e.target.value)} />}
        <div className="orbit-more-list">
          {roster.map(u => {
            const t = tierFor(u.streak);
            return (
              <div className="orbit-more-row" key={u.id}>
                <button className="orbit-more-main" onClick={() => { setSheet(null); setOpenUser({ ...u, tier: t }); }}>
                  {u.photo ? <img src={u.photo} alt="" style={{ borderColor: t.color }} /> : <span className="orbit-more-fallback">{initialsOf(u.name)}</span>}
                  <span className="orbit-more-text"><span className="orbit-more-name">{u.name}</span><span className="orbit-more-meta">{u.streak || 0}d streak · {kudosLifetime[u.id].toLocaleString()} kudos</span></span>
                </button>
                <button className="orbit-more-kudos" onClick={e => { const r = e.currentTarget.getBoundingClientRect(); sendKudos(u, r.left + r.width / 2, r.top); }} aria-label={`Send kudos to ${u.name}`}>
                  <Icon name="heart" weight="fill" size={15} color="var(--neutral-950)" />
                </button>
              </div>
            );
          })}
        </div>
      </OrbitSheet>}

      {sheet === 'store' && <OrbitSheet label={`${USER.store} today`} onClose={() => setSheet(null)}>
        <div className="orbit-store-head">
          {company && company.logo ? <img src={company.logo} alt="" /> : <span className="orbit-more-fallback">{(company && company.initials) || 'P'}</span>}
          <div><p className="orbit-info-name">{USER.store}</p><p className="orbit-info-bio">{(company && company.name) || 'Pryze'} · {dateLabel}</p></div>
        </div>
        <div className="orbit-info-stats">
          <div className="orbit-info-stat"><span className="orbit-info-stat-value">{storeToday.toLocaleString()}</span><span className="orbit-info-stat-label">Points today</span></div>
          <div className="orbit-info-stat"><span className="orbit-info-stat-value">{ORBIT_ACTIVE_USERS.length}</span><span className="orbit-info-stat-label">On shift</span></div>
          <div className="orbit-info-stat"><span className="orbit-info-stat-value">{Math.round(storeToday / Math.max(1, ORBIT_ACTIVE_USERS.length))}</span><span className="orbit-info-stat-label">Avg / person</span></div>
        </div>
        {topOrbiter && <p className="orbit-info-caption">Leading the store today — {topOrbiter.name}</p>}
      </OrbitSheet>}
    </div>
  );
}

window.PryzeOrbit = { OrbitScreen };
