/* Queues — index view (all queues at a glance) + single-queue triage dashboard */

const QUEUE_SPACE_MAP = { 1: [1, 5], 2: [3], 3: [2, 4] };

function getQueuesList(provided) {
  return provided || window.__liveQueues || D.queues;
}

function queueTasks(allTasks, queueId) {
  const allowed = new Set(QUEUE_SPACE_MAP[queueId] || []);
  return allTasks.filter(t => t.queueId === queueId || (t.queueId == null && allowed.has(t.spaceId)));
}

function QueuesIndex({ allTasks, queues, onView, onSelectTask, onOpenModal, onSetQueue, onCreateQueue }) {
  const list = getQueuesList(queues);
  const [creatingQueue, setCreatingQueue] = React.useState(false);
  const unrouted = allTasks.filter(t => !t.queueId && !Object.values(QUEUE_SPACE_MAP).flat().includes(t.spaceId) && t.statusId !== 6);

  return (
    <div style={{ flex: 1, overflowY: 'auto', background: 'rgb(var(--bg))' }}>
      <div style={{ maxWidth: 1280, margin: '0 auto', padding: '28px 32px 60px' }}>
        <header style={{ marginBottom: 22 }}>
          <p style={{ margin: 0, fontSize: 12, fontWeight: 600, letterSpacing: '0.04em', color: 'rgb(var(--accent))', textTransform: 'uppercase' }}>Queues</p>
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginTop: 4 }}>
            <div>
              <h1 style={{ margin: 0, fontSize: 28, fontWeight: 700, letterSpacing: '-0.025em' }}>Team inboxes</h1>
              <p style={{ margin: '6px 0 0', fontSize: 13.5, color: 'rgb(var(--muted))', maxWidth: 560 }}>
                Shared work, sorted into team-owned queues. Pick the queue you cover and claim what needs you next.
              </p>
            </div>
            <button className="btn btn-accent" style={{ fontSize: 12.5 }} onClick={() => setCreatingQueue(true)}>+ New queue</button>
          </div>
        </header>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 24 }}>
          {list.map(q => {
            const tasks = queueTasks(allTasks, q.id);
            const open  = tasks.filter(t => t.statusId !== 6);
            const unassigned = open.filter(t => t.assignees.length === 0);
            const mine = open.filter(t => t.assignees.includes(1));
            const overdue = open.filter(t => t.dueDate && dayDelta(t.dueDate) < 0);
            return (
              <div key={q.id} style={{
                background: 'rgb(var(--surface))',
                border: '1px solid rgb(var(--rule))',
                borderRadius: 14, padding: 18,
                boxShadow: 'var(--sh-sm)',
                position: 'relative', overflow: 'hidden',
                display: 'flex', flexDirection: 'column',
                cursor: 'pointer', transition: 'box-shadow .14s, border-color .14s',
              }}
                onClick={() => onView(`queue:${q.id}`)}
                onMouseEnter={e => { e.currentTarget.style.boxShadow = 'var(--sh-md)'; e.currentTarget.style.borderColor = q.colour + '60'; }}
                onMouseLeave={e => { e.currentTarget.style.boxShadow = 'var(--sh-sm)'; e.currentTarget.style.borderColor = 'rgb(var(--rule))'; }}>
                <span style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 4, background: q.colour }} />
                <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
                  <div style={{
                    width: 44, height: 44, borderRadius: 12,
                    background: q.colour + '14', color: q.colour,
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: 18, fontWeight: 700,
                  }}>{q.name.charAt(0)}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <h3 style={{ margin: 0, fontSize: 16, fontWeight: 700, letterSpacing: '-0.015em' }}>{q.name}</h3>
                    <p style={{ margin: '2px 0 0', fontSize: 11.5, color: 'rgb(var(--muted))' }}>{q.members} members</p>
                  </div>
                  <span style={{ fontSize: 16, color: 'rgb(var(--muted))' }}>›</span>
                </div>

                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 8, marginBottom: 12 }}>
                  <QStat label="Open"        n={open.length}        c="rgb(var(--ink))" />
                  <QStat label="Unassigned"  n={unassigned.length}  c="rgb(var(--accent))" highlight={unassigned.length > 0} />
                  <QStat label="Mine"        n={mine.length}        c="rgb(var(--forest))" />
                  <QStat label="Overdue"     n={overdue.length}     c="rgb(var(--brick))"  highlight={overdue.length > 0} />
                </div>

                <p style={{ margin: '4px 0 6px', fontSize: 10, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.06em', textTransform: 'uppercase' }}>Next up</p>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                  {sortByQueuePriority(open).slice(0, 3).map(t => (
                    <div key={t.id} onClick={(e) => { e.stopPropagation(); onSelectTask?.(t.id); }} style={{
                      display: 'flex', alignItems: 'center', gap: 6,
                      padding: '5px 8px',
                      background: 'rgb(var(--paper-2) / 0.5)', borderRadius: 6,
                      fontSize: 11.5,
                    }}>
                      <PriorityDot p={t.priority} />
                      <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'rgb(var(--ink-2))' }}>{t.title}</span>
                      {t.dueDate && <DueChip iso={t.dueDate} />}
                    </div>
                  ))}
                  {open.length === 0 && <p style={{ margin: 0, fontSize: 11.5, color: 'rgb(var(--muted))', fontStyle: 'italic', padding: 8 }}>This queue is empty.</p>}
                </div>
              </div>
            );
          })}
        </div>

        {/* Inbox — tickets not in any queue */}
        <section>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
            <h2 style={{ margin: 0, fontSize: 17, fontWeight: 700, letterSpacing: '-0.02em' }}>Inbox</h2>
            <span style={{ fontSize: 12, color: 'rgb(var(--muted))' }}>Tickets not in a queue — route them to a team.</span>
            <span style={{ flex: 1 }} />
            <span className="num" style={{
              fontSize: 11, fontWeight: 700, padding: '3px 10px', borderRadius: 999,
              background: unrouted.length > 0 ? 'rgb(var(--warm-soft))' : 'rgb(var(--paper-2))',
              color: unrouted.length > 0 ? 'rgb(var(--warm))' : 'rgb(var(--muted))',
            }}>{unrouted.length}</span>
          </div>
          <div style={{ background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))', borderRadius: 12, boxShadow: 'var(--sh-sm)', overflow: 'hidden' }}>
            {unrouted.length === 0 && (
              <div style={{ padding: 30, textAlign: 'center', color: 'rgb(var(--muted))' }}>
                <div style={{ fontSize: 24, marginBottom: 6 }}>✓</div>
                <p style={{ margin: 0, fontSize: 13, fontWeight: 600, color: 'rgb(var(--ink))' }}>Inbox zero.</p>
                <p style={{ margin: '4px 0 0', fontSize: 12 }}>Every ticket is routed to a team queue.</p>
              </div>
            )}
            {unrouted.slice(0, 8).map((t, i, arr) => (
              <InboxRow key={t.id} task={t} last={i === arr.length - 1}
                onClick={() => onSelectTask?.(t.id)}
                onOpen={() => onOpenModal?.(t.id)}
                onSetQueue={(qid) => onSetQueue?.(t.id, qid)}
                queues={list}
              />
            ))}
          </div>
        </section>
      </div>
      {creatingQueue && <NewQueueModal onClose={() => setCreatingQueue(false)} onCreate={(payload) => { onCreateQueue?.(payload); setCreatingQueue(false); }} />}
    </div>
  );
}

function NewQueueModal({ onClose, onCreate }) {
  const [name, setName] = React.useState('');
  const [colour, setColour] = React.useState('#6366F1');
  const [memberIds, setMemberIds] = React.useState([1]);
  const colours = ['#6366F1', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#3B82F6', '#64748B'];
  const submit = (e) => {
    e?.preventDefault();
    if (!name.trim()) return;
    onCreate({ name: name.trim(), colour, memberIds });
  };
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(15,23,42,0.45)', backdropFilter: 'blur(4px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 220, padding: 32,
    }}>
      <form onSubmit={submit} onClick={e => e.stopPropagation()} className="fadein shadow-pop" style={{
        background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
        borderRadius: 14, width: 480, maxHeight: '86vh', overflow: 'auto',
      }}>
        <div style={{ padding: '16px 22px', borderBottom: '1px solid rgb(var(--rule))' }}>
          <h3 style={{ margin: 0, fontSize: 17, fontWeight: 700 }}>New queue</h3>
          <p style={{ margin: '3px 0 0', fontSize: 12, color: 'rgb(var(--muted))' }}>Group a team around shared work — referrals, billing, escalations.</p>
        </div>
        <div style={{ padding: '18px 22px', display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <p style={{ margin: '0 0 6px', fontSize: 11, fontWeight: 600, letterSpacing: '0.04em', color: 'rgb(var(--muted))', textTransform: 'uppercase' }}>Name</p>
            <input autoFocus value={name} onChange={e => setName(e.target.value)} placeholder="e.g. After-hours triage" style={{ width: '100%', fontSize: 13.5, padding: '8px 12px' }} />
          </div>
          <div>
            <p style={{ margin: '0 0 6px', fontSize: 11, fontWeight: 600, letterSpacing: '0.04em', color: 'rgb(var(--muted))', textTransform: 'uppercase' }}>Colour</p>
            <div style={{ display: 'flex', gap: 6 }}>
              {colours.map(c => (
                <button key={c} type="button" onClick={() => setColour(c)} style={{
                  width: 30, height: 30, borderRadius: 8, background: c,
                  border: '2px solid ' + (colour === c ? 'rgb(var(--ink))' : 'transparent'),
                }} />
              ))}
            </div>
          </div>
          <div>
            <p style={{ margin: '0 0 6px', fontSize: 11, fontWeight: 600, letterSpacing: '0.04em', color: 'rgb(var(--muted))', textTransform: 'uppercase' }}>Initial members</p>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {D.users.map(u => {
                const on = memberIds.includes(u.id);
                return (
                  <button key={u.id} type="button" onClick={() => setMemberIds(on ? memberIds.filter(x => x !== u.id) : [...memberIds, u.id])}
                    style={{
                      display: 'inline-flex', alignItems: 'center', gap: 6,
                      padding: '4px 4px 4px 8px', borderRadius: 999,
                      background: on ? u.colour + '22' : 'rgb(var(--paper-2))',
                      border: '1px solid ' + (on ? u.colour + '60' : 'transparent'),
                      color: on ? u.colour : 'rgb(var(--ink-2))', fontWeight: 600, fontSize: 12,
                    }}>
                    {u.name.split(' ')[0]}
                    <Avatar user={u} size={20} />
                  </button>
                );
              })}
            </div>
          </div>
        </div>
        <div style={{ padding: '12px 22px', borderTop: '1px solid rgb(var(--rule))', background: 'rgb(var(--bg))', display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button type="button" onClick={onClose} className="btn btn-secondary" style={{ fontSize: 12.5 }}>Cancel</button>
          <button type="submit" className="btn btn-accent" style={{ fontSize: 12.5 }} disabled={!name.trim()}>Create queue</button>
        </div>
      </form>
    </div>
  );
}

function InboxRow({ task, last, onClick, onOpen, onSetQueue, queues }) {
  const sp = spaceById(task.spaceId);
  const list = getQueuesList(queues);
  const [open, setOpen] = React.useState(false);
  return (
    <article style={{
      display: 'grid', gridTemplateColumns: '14px 1fr auto auto auto',
      gap: 12, alignItems: 'center', padding: '11px 16px',
      borderBottom: last ? 'none' : '1px solid rgb(var(--rule) / 0.6)',
    }}>
      <PriorityDot p={task.priority} />
      <div onClick={onClick} onDoubleClick={(e) => { e.stopPropagation(); onOpen(); }} style={{ minWidth: 0, cursor: 'pointer' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 3 }}>
          <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10.5, color: 'rgb(var(--muted))' }}>{task.ticket}</span>
          <span style={{ width: 3, height: 3, background: 'rgb(var(--rule-2))', borderRadius: '50%' }} />
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 11, color: sp.colour, fontWeight: 600 }}>
            <span style={{ width: 5, height: 5, background: sp.colour, borderRadius: 2 }} />{sp.name}
          </span>
        </div>
        <div style={{ fontSize: 13, fontWeight: 600, color: 'rgb(var(--ink))', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: '-0.005em' }}>{task.title}</div>
      </div>
      {task.dueDate ? <DueChip iso={task.dueDate} /> : <span />}
      {task.assignees.length > 0 ? <AvatarStack ids={task.assignees} size={22} max={2} /> : <span style={{ fontSize: 10.5, color: 'rgb(var(--muted-2))', fontStyle: 'italic' }}>unassigned</span>}
      <span style={{ position: 'relative' }}>
        <button onClick={() => setOpen(o => !o)} className="btn btn-accent" style={{ fontSize: 11.5, padding: '5px 12px' }}>Route to →</button>
        {open && (
          <>
            <span onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
            <div className="shadow-pop fadein" style={{
              position: 'absolute', top: '100%', right: 0, marginTop: 4,
              background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
              borderRadius: 10, padding: 4, zIndex: 61, minWidth: 200,
            }}>
              {list.map(qu => (
                <button key={qu.id} onClick={() => { onSetQueue(qu.id); setOpen(false); }} style={{
                  display: 'flex', alignItems: 'center', gap: 9, width: '100%',
                  padding: '6px 10px', borderRadius: 6, textAlign: 'left',
                }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2) / 0.6)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                  <span style={{ width: 8, height: 8, background: qu.colour, borderRadius: '50%' }} />
                  <span style={{ fontSize: 12.5, fontWeight: 600, color: 'rgb(var(--ink))' }}>{qu.name}</span>
                </button>
              ))}
            </div>
          </>
        )}
      </span>
    </article>
  );
}

function QStat({ label, n, c, highlight }) {
  return (
    <div style={{
      padding: '8px 10px', borderRadius: 8,
      background: highlight ? c + '0c' : 'rgb(var(--paper-2) / 0.5)',
    }}>
      <div style={{ fontSize: 10, fontWeight: 600, color: 'rgb(var(--muted))', letterSpacing: '0.04em', textTransform: 'uppercase' }}>{label}</div>
      <div className="num" style={{ fontSize: 19, fontWeight: 700, color: c, letterSpacing: '-0.02em', lineHeight: 1, marginTop: 3 }}>{n}</div>
    </div>
  );
}

function sortByQueuePriority(tasks) {
  const rank = { urgent: 0, high: 1, medium: 2, low: 3 };
  return [...tasks].sort((a, b) => {
    // overdue first
    const aO = a.dueDate && dayDelta(a.dueDate) < 0 ? 0 : 1;
    const bO = b.dueDate && dayDelta(b.dueDate) < 0 ? 0 : 1;
    if (aO !== bO) return aO - bO;
    // then by priority
    if (rank[a.priority] !== rank[b.priority]) return rank[a.priority] - rank[b.priority];
    // then by due
    return (a.dueDate || '9999').localeCompare(b.dueDate || '9999');
  });
}

/* ─── Single queue — triage dashboard ─── */
function QueueDashboard({ queueId, queues, allTasks, onSelectTask, onOpenModal, onStatusChange, onView, onClaim, onToggleMember, onCreateTicket }) {
  const queueList = getQueuesList(queues);
  const q = queueList.find(x => x.id === queueId);
  if (!q) return <div style={{ padding: 40, color: 'rgb(var(--muted))' }}>Queue not found</div>;

  const [tab, setTab] = React.useState('next');
  const [membersOpen, setMembersOpen] = React.useState(false);
  const [addingTicket, setAddingTicket] = React.useState(false);
  const [newTicketTitle, setNewTicketTitle] = React.useState('');
  const tasks = queueTasks(allTasks, queueId);
  const open = tasks.filter(t => t.statusId !== 6);
  const memberIds = q.memberIds || [];
  const inQueue = memberIds.includes(1);
  const buckets = {
    next:       sortByQueuePriority(open),
    mine:       sortByQueuePriority(open.filter(t => t.assignees.includes(1))),
    unassigned: sortByQueuePriority(open.filter(t => t.assignees.length === 0)),
    overdue:    sortByQueuePriority(open.filter(t => t.dueDate && dayDelta(t.dueDate) < 0)),
    all:        sortByQueuePriority(open),
    done:       tasks.filter(t => t.statusId === 6),
  };

  const tabs = [
    ['next',       'Next up',     buckets.next.length,       'rgb(var(--accent))'],
    ['unassigned', 'Unassigned',  buckets.unassigned.length, 'rgb(var(--warm))'],
    ['mine',       'My tickets',  buckets.mine.length,       'rgb(var(--forest))'],
    ['overdue',    'Overdue',     buckets.overdue.length,    'rgb(var(--brick))'],
    ['all',        'All open',    buckets.all.length,        'rgb(var(--muted))'],
    ['done',       'Done',        buckets.done.length,       'rgb(var(--muted))'],
  ];
  const list = buckets[tab] || [];

  // SLA — using space SLA's average for the queue
  const spaceIds = QUEUE_SPACE_MAP[queueId] || [];
  const avgSla = (() => {
    const slas = spaceIds.map(id => spaceById(id)?.sla).filter(Boolean);
    return slas.length ? Math.round(slas.reduce((a, b) => a + b, 0) / slas.length) : null;
  })();
  const onTime = open.filter(t => !(t.dueDate && dayDelta(t.dueDate) < 0)).length;
  const health = open.length ? Math.round((onTime / open.length) * 100) : 100;

  return (
    <div style={{ flex: 1, overflowY: 'auto', background: 'rgb(var(--bg))' }}>
      <div style={{ maxWidth: 1280, margin: '0 auto', padding: '24px 32px 60px' }}>

        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16, marginBottom: 20 }}>
          <button onClick={() => onView('queues')} style={{
            fontSize: 12, color: 'rgb(var(--muted))', padding: '6px 10px', borderRadius: 7,
            border: '1px solid rgb(var(--rule))', background: 'rgb(var(--surface))',
            marginTop: 4,
          }}>‹ All queues</button>
          <div style={{
            width: 52, height: 52, borderRadius: 14,
            background: q.colour + '14', color: q.colour,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 22, fontWeight: 700, flexShrink: 0,
          }}>{q.name.charAt(0)}</div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <p style={{ margin: 0, fontSize: 11.5, fontWeight: 600, color: q.colour, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Queue</p>
            <h1 style={{ margin: '2px 0 0', fontSize: 26, fontWeight: 700, letterSpacing: '-0.025em' }}>{q.name}</h1>
            <button onClick={() => setMembersOpen(true)} style={{
              margin: '6px 0 0', fontSize: 12.5, color: 'rgb(var(--muted))',
              display: 'inline-flex', alignItems: 'center', gap: 8, padding: '4px 10px 4px 4px',
              borderRadius: 999, background: 'rgb(var(--paper-2) / 0.7)',
              transition: 'background-color .12s',
            }}
              onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2))'}
              onMouseLeave={e => e.currentTarget.style.background = 'rgb(var(--paper-2) / 0.7)'}>
              {memberIds.length > 0 && <AvatarStack ids={memberIds} size={20} max={4} />}
              <span style={{ color: 'rgb(var(--ink-2))', fontWeight: 500 }}>{memberIds.length} member{memberIds.length === 1 ? '' : 's'}</span>
              <span style={{ color: 'rgb(var(--muted))' }}>·</span>
              <span style={{ color: 'rgb(var(--ink-2))', fontWeight: 600 }}>{open.length} open</span>
              {avgSla && <><span style={{ color: 'rgb(var(--muted))' }}>·</span><span>SLA {avgSla}d</span></>}
              <span style={{ color: 'rgb(var(--accent))', marginLeft: 6, fontSize: 11, fontWeight: 600 }}>Manage →</span>
            </button>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={() => onToggleMember?.(queueId, 1)} className="btn btn-secondary" style={{ fontSize: 12 }}>
              {inQueue ? '✓ Joined' : '+ Join queue'}
            </button>
            <button onClick={() => setAddingTicket(true)} className="btn btn-accent" style={{ fontSize: 12 }}>+ Add ticket</button>
          </div>
        </div>

        {addingTicket && (
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            padding: '12px 14px', marginBottom: 14,
            background: 'rgb(var(--accent-soft) / 0.5)',
            border: '1px solid rgb(var(--accent) / 0.3)',
            borderRadius: 12,
          }}>
            <span style={{ width: 18, height: 18, borderRadius: 6, border: '1.5px dashed rgb(var(--accent))', flexShrink: 0 }} />
            <input autoFocus value={newTicketTitle} onChange={e => setNewTicketTitle(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === 'Enter' && newTicketTitle.trim()) { onCreateTicket?.(newTicketTitle.trim()); setNewTicketTitle(''); setAddingTicket(false); }
                if (e.key === 'Escape') { setAddingTicket(false); setNewTicketTitle(''); }
              }}
              placeholder={`New ticket in ${q.name}…`}
              style={{ flex: 1, fontSize: 13.5, padding: '5px 10px', border: 'none', background: 'rgb(var(--surface))' }} />
            <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10.5, color: 'rgb(var(--muted))', padding: '2px 7px', background: 'rgb(var(--surface))', borderRadius: 4 }}>Enter</span>
            <button onClick={() => { if (newTicketTitle.trim()) { onCreateTicket?.(newTicketTitle.trim()); setNewTicketTitle(''); setAddingTicket(false); } }}
              className="btn btn-accent" style={{ fontSize: 11.5, padding: '4px 12px' }}>Add</button>
            <button onClick={() => { setAddingTicket(false); setNewTicketTitle(''); }} style={{ fontSize: 16, color: 'rgb(var(--muted))', padding: '0 4px', borderRadius: 4 }}>×</button>
          </div>
        )}

        {/* KPI row */}
        <div style={{
          display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 18,
        }}>
          <QKpi label="Health"      value={`${health}%`} c={health >= 90 ? 'rgb(var(--forest))' : health >= 70 ? 'rgb(var(--warm))' : 'rgb(var(--brick))'} />
          <QKpi label="Unassigned"  value={buckets.unassigned.length} c="rgb(var(--accent))"  hint={buckets.unassigned.length > 0 ? 'needs an owner' : 'all owned'} />
          <QKpi label="Overdue"     value={buckets.overdue.length}    c="rgb(var(--brick))"   hint={buckets.overdue.length > 0 ? 'past SLA target' : 'on track'} />
          <QKpi label="My tickets"  value={buckets.mine.length}       c="rgb(var(--ink))"    hint="assigned to you" />
        </div>

        {/* Tabs */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 4, padding: 4, background: 'rgb(var(--paper-2))', borderRadius: 10, marginBottom: 14, overflowX: 'auto' }}>
          {tabs.map(([k, lbl, n, c]) => (
            <button key={k} onClick={() => setTab(k)} style={{
              display: 'inline-flex', alignItems: 'center', gap: 6,
              padding: '6px 12px', fontSize: 12.5, fontWeight: 600,
              background: tab === k ? 'rgb(var(--surface))' : 'transparent',
              color: tab === k ? 'rgb(var(--ink))' : 'rgb(var(--muted))',
              borderRadius: 7, boxShadow: tab === k ? 'var(--sh-sm)' : 'none',
              whiteSpace: 'nowrap',
            }}>
              {lbl}
              <span className="num" style={{
                fontSize: 10, fontWeight: 700, padding: '1px 6px', borderRadius: 999,
                background: tab === k ? c + '14' : 'rgb(var(--paper-3))',
                color: tab === k ? c : 'rgb(var(--muted))',
              }}>{n}</span>
            </button>
          ))}
        </div>

        {/* Ticket list */}
        <div style={{
          background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
          borderRadius: 12, overflow: 'hidden', boxShadow: 'var(--sh-sm)',
        }}>
          {list.length === 0 && (
            <div style={{ padding: '50px 20px', textAlign: 'center', color: 'rgb(var(--muted))' }}>
              <div style={{ fontSize: 28, marginBottom: 8 }}>🍵</div>
              <p style={{ margin: 0, fontSize: 14, fontWeight: 600, color: 'rgb(var(--ink))' }}>Nothing in this bucket.</p>
              <p style={{ margin: '4px 0 0', fontSize: 12.5 }}>Switch tabs or grab the next ticket.</p>
            </div>
          )}
          {list.map((t, i) => (
            <QueueRow key={t.id} task={t} last={i === list.length - 1}
              onClick={() => onSelectTask(t.id)}
              onOpen={() => onOpenModal(t.id)}
              onClaim={() => onClaim(t.id)}
              onStatusChange={(sid) => onStatusChange(t.id, sid)}
            />
          ))}
        </div>
      </div>
      {membersOpen && <QueueMembersModal queue={q} memberIds={memberIds} onClose={() => setMembersOpen(false)} onToggle={(uid) => onToggleMember?.(queueId, uid)} />}
    </div>
  );
}

function QueueMembersModal({ queue, memberIds, onClose, onToggle }) {
  const members  = D.users.filter(u => memberIds.includes(u.id));
  const available = D.users.filter(u => !memberIds.includes(u.id));
  // Predefined "groups" (role-based bundles) for one-click membership
  const groups = [
    { name: 'Clinicians',   ids: D.users.filter(u => /clinician|therapist|lead/i.test(u.role)).map(u => u.id), glyph: '✚' },
    { name: 'Front desk',   ids: D.users.filter(u => /reception|coordinator/i.test(u.role)).map(u => u.id),   glyph: '☰' },
    { name: 'Admin/Billing',ids: D.users.filter(u => /billing|admin/i.test(u.role)).map(u => u.id),           glyph: '£' },
  ];
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(15,23,42,0.45)', backdropFilter: 'blur(4px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 220, padding: 28,
    }}>
      <div onClick={e => e.stopPropagation()} className="fadein shadow-pop" style={{
        background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
        borderRadius: 14, width: 520, maxHeight: '86vh', display: 'flex', flexDirection: 'column',
      }}>
        <div style={{ padding: '16px 22px', borderBottom: '1px solid rgb(var(--rule))', display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ width: 30, height: 30, borderRadius: 9, background: queue.colour + '14', color: queue.colour, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, fontWeight: 700 }}>{queue.name.charAt(0)}</span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <h3 style={{ margin: 0, fontSize: 16, fontWeight: 700 }}>{queue.name} · members</h3>
            <p style={{ margin: '2px 0 0', fontSize: 11.5, color: 'rgb(var(--muted))' }}>{members.length} member{members.length === 1 ? '' : 's'} cover this queue</p>
          </div>
          <button onClick={onClose} style={{ fontSize: 20, color: 'rgb(var(--muted))', padding: '0 6px', borderRadius: 6, lineHeight: 1 }}>×</button>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', padding: '14px 22px' }}>
          {/* Quick add by group */}
          <p style={{ margin: '0 0 8px', fontSize: 11, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.04em', textTransform: 'uppercase' }}>Quick add a group</p>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 18 }}>
            {groups.map(g => (
              <button key={g.name} onClick={() => g.ids.forEach(uid => !memberIds.includes(uid) && onToggle(uid))}
                style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                  padding: '5px 12px', fontSize: 12, fontWeight: 600,
                  background: 'rgb(var(--paper-2))', color: 'rgb(var(--ink-2))',
                  border: '1px solid rgb(var(--rule))', borderRadius: 999,
                }}
                onMouseEnter={e => { e.currentTarget.style.background = 'rgb(var(--accent-soft))'; e.currentTarget.style.color = 'rgb(var(--accent-2))'; e.currentTarget.style.borderColor = 'rgb(var(--accent) / 0.4)'; }}
                onMouseLeave={e => { e.currentTarget.style.background = 'rgb(var(--paper-2))'; e.currentTarget.style.color = 'rgb(var(--ink-2))'; e.currentTarget.style.borderColor = 'rgb(var(--rule))'; }}>
                <span style={{ color: 'rgb(var(--muted))' }}>{g.glyph}</span>
                {g.name}
                <span style={{ color: 'rgb(var(--muted))', fontWeight: 500 }}>+{g.ids.filter(id => !memberIds.includes(id)).length}</span>
              </button>
            ))}
          </div>

          <p style={{ margin: '0 0 8px', fontSize: 11, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.04em', textTransform: 'uppercase' }}>Members ({members.length})</p>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 18 }}>
            {members.length === 0 && <p style={{ margin: 0, padding: 12, fontSize: 12.5, color: 'rgb(var(--muted))', fontStyle: 'italic', textAlign: 'center', border: '1px dashed rgb(var(--rule-2))', borderRadius: 8 }}>No members yet — pick people below.</p>}
            {members.map(u => <MemberRow key={u.id} user={u} onAction={() => onToggle(u.id)} action="Remove" tone="danger" />)}
          </div>

          <p style={{ margin: '0 0 8px', fontSize: 11, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.04em', textTransform: 'uppercase' }}>Add team members</p>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {available.length === 0 && <p style={{ margin: 0, padding: 12, fontSize: 12.5, color: 'rgb(var(--muted))', fontStyle: 'italic', textAlign: 'center' }}>Everyone's already a member.</p>}
            {available.map(u => <MemberRow key={u.id} user={u} onAction={() => onToggle(u.id)} action="Add" tone="primary" />)}
          </div>
        </div>

        <div style={{ padding: '12px 22px', borderTop: '1px solid rgb(var(--rule))', background: 'rgb(var(--bg))', display: 'flex', justifyContent: 'flex-end' }}>
          <button onClick={onClose} className="btn btn-primary" style={{ fontSize: 12.5 }}>Done</button>
        </div>
      </div>
    </div>
  );
}

function MemberRow({ user, onAction, action, tone }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: '8px 12px', background: 'rgb(var(--surface))',
      border: '1px solid rgb(var(--rule))', borderRadius: 10,
    }}>
      <Avatar user={user} size={28} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: 'rgb(var(--ink))' }}>{user.name}</div>
        <div style={{ fontSize: 11.5, color: 'rgb(var(--muted))' }}>{user.role}</div>
      </div>
      <button onClick={onAction} className={tone === 'danger' ? 'btn btn-secondary' : 'btn btn-accent'}
        style={{ fontSize: 11.5, padding: '4px 12px', color: tone === 'danger' ? 'rgb(var(--brick))' : undefined }}>
        {action}
      </button>
    </div>
  );
}

function QKpi({ label, value, c, hint }) {
  return (
    <div style={{
      padding: '14px 16px', background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
      borderRadius: 12, boxShadow: 'var(--sh-sm)', position: 'relative', overflow: 'hidden',
    }}>
      <span style={{ position: 'absolute', top: 0, left: 0, width: 24, height: 3, background: c }} />
      <p style={{ margin: 0, fontSize: 11, fontWeight: 600, color: 'rgb(var(--muted))', letterSpacing: '0.04em', textTransform: 'uppercase' }}>{label}</p>
      <p className="num" style={{ margin: '4px 0 0', fontSize: 22, fontWeight: 700, color: c, letterSpacing: '-0.02em', lineHeight: 1 }}>{value}</p>
      {hint && <p style={{ margin: '4px 0 0', fontSize: 11, color: 'rgb(var(--muted))' }}>{hint}</p>}
    </div>
  );
}

function QueueRow({ task, last, onClick, onOpen, onClaim, onStatusChange }) {
  const sp = spaceById(task.spaceId);
  const st = statusById(task.statusId);
  const unassigned = task.assignees.length === 0;
  const overdue = task.dueDate && dayDelta(task.dueDate) < 0 && task.statusId !== 6;
  return (
    <article
      onClick={onClick}
      onDoubleClick={(e) => { e.stopPropagation(); onOpen(); }}
      style={{
        display: 'grid', gridTemplateColumns: '14px 1fr auto auto auto auto',
        gap: 12, alignItems: 'center',
        padding: '12px 16px',
        borderBottom: last ? 'none' : '1px solid rgb(var(--rule) / 0.6)',
        cursor: 'pointer',
        transition: 'background-color .12s',
      }}
      onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2) / 0.4)'}
      onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
      <PriorityDot p={task.priority} />
      <div style={{ minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 3 }}>
          <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10.5, color: 'rgb(var(--muted))' }}>{task.ticket}</span>
          <span style={{ width: 3, height: 3, background: 'rgb(var(--rule-2))', borderRadius: '50%' }} />
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 11, color: sp.colour, fontWeight: 600 }}>
            <span style={{ width: 5, height: 5, background: sp.colour, borderRadius: 2 }} />{sp.name}
          </span>
          {overdue && <span style={{ fontSize: 10, fontWeight: 700, color: 'rgb(var(--brick))', background: 'rgb(var(--brick-soft))', padding: '0 6px', borderRadius: 999 }}>OVERDUE</span>}
        </div>
        <div style={{ fontSize: 13.5, fontWeight: 600, color: 'rgb(var(--ink))', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: '-0.005em' }}>{task.title}</div>
      </div>
      <span style={{
        display: 'inline-flex', alignItems: 'center', gap: 5,
        padding: '2px 9px', background: st.colour + '14', color: st.colour,
        fontSize: 10.5, fontWeight: 700, borderRadius: 999,
      }}>
        <span style={{ width: 5, height: 5, background: st.colour, borderRadius: '50%' }} />{st.title}
      </span>
      {task.dueDate ? <DueChip iso={task.dueDate} /> : <span style={{ fontSize: 11, color: 'rgb(var(--muted-2))', fontFamily: 'JetBrains Mono, monospace' }}>—</span>}
      {task.assignees.length > 0
        ? <AvatarStack ids={task.assignees} size={22} max={2} />
        : <span style={{ fontSize: 10.5, fontWeight: 700, color: 'rgb(var(--accent))', background: 'rgb(var(--accent-soft))', padding: '2px 9px', borderRadius: 999, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Open</span>}
      <button onClick={(e) => { e.stopPropagation(); unassigned ? onClaim() : onOpen(); }}
        className={unassigned ? "btn btn-accent" : "btn btn-secondary"}
        style={{ fontSize: 11.5, padding: '4px 12px' }}>
        {unassigned ? 'Claim →' : 'Open'}
      </button>
    </article>
  );
}

Object.assign(window, { QueuesIndex, QueueDashboard });
