/* Right detail pane — full task view */

function DetailPane({ task, allTasks, onClose, onStatusChange, onSetAssignees, onSetQueue, onSetReminder, onToggleFlag, onSetClients, onUpdateTitle, editTitleOnOpen, clearEditTitleOnOpen, onLog, onToggleLink, onOpenTask, presentation = 'pane' }) {
  const [tab, setTab] = React.useState('activity');
  const [composer, setComposer] = React.useState('');
  const [titleEditing, setTitleEditing] = React.useState(false);
  const [titleDraft, setTitleDraft] = React.useState(task?.title || '');
  const isModal = presentation === 'modal';

  React.useEffect(() => {
    if (editTitleOnOpen && task) {
      setTitleDraft(task.title);
      setTitleEditing(true);
      clearEditTitleOnOpen?.();
    }
  }, [editTitleOnOpen, task?.id]);

  React.useEffect(() => { setTitleDraft(task?.title || ''); }, [task?.id]);

  const commitTitle = () => {
    const v = titleDraft.trim();
    if (v && v !== task.title) onUpdateTitle?.(task.id, v);
    setTitleEditing(false);
  };

  // Local checklist state — initialise from task, persist per task in a ref
  const [localSubtasks, setLocalSubtasks] = React.useState([]);
  const [localChecklist, setLocalChecklist] = React.useState([]);
  const [newSubtask, setNewSubtask] = React.useState('');
  const [newChecklist, setNewChecklist] = React.useState('');

  React.useEffect(() => {
    setLocalSubtasks(task ? [...(task.subtasks || [])] : []);
    setLocalChecklist(task ? [...(task.checklist || [])] : []);
    setNewSubtask(''); setNewChecklist('');
  }, [task?.id]);

  const toggleSub  = (id) => {
    setLocalSubtasks(l => l.map(s => {
      if (s.id === id) {
        if (onLog) onLog(task.id, `${s.done ? 'unchecked' : 'checked'} subtask "${s.title}"`);
        return { ...s, done: !s.done };
      }
      return s;
    }));
  };
  const toggleCheck= (id) => {
    setLocalChecklist(l => l.map(s => {
      if (s.id === id) {
        if (onLog) onLog(task.id, `${s.done ? 'unchecked' : 'checked'} checklist item "${s.title}"`);
        return { ...s, done: !s.done };
      }
      return s;
    }));
  };
  const deleteSub  = (id) => {
    const item = localSubtasks.find(s => s.id === id);
    setLocalSubtasks (l => l.filter(s => s.id !== id));
    if (item && onLog) onLog(task.id, `removed subtask "${item.title}"`);
  };
  const deleteCheck= (id) => {
    const item = localChecklist.find(s => s.id === id);
    setLocalChecklist(l => l.filter(s => s.id !== id));
    if (item && onLog) onLog(task.id, `removed checklist item "${item.title}"`);
  };
  const addSub = () => {
    if (!newSubtask.trim()) return;
    const title = newSubtask.trim();
    setLocalSubtasks(l => [...l, { id: Date.now() + Math.random(), title, done: false }]);
    if (onLog) onLog(task.id, `added subtask "${title}"`);
    setNewSubtask('');
  };
  const addCheck = () => {
    if (!newChecklist.trim()) return;
    const title = newChecklist.trim();
    setLocalChecklist(l => [...l, { id: Date.now() + Math.random(), title, done: false }]);
    if (onLog) onLog(task.id, `added checklist item "${title}"`);
    setNewChecklist('');
  };

  if (!task) return <EmptyDetail onClose={onClose} />;

  const sp = spaceById(task.spaceId);
  const grp = groupById(task.groupId);
  const subs = localSubtasks;
  const checks = localChecklist;
  const subsDone = subs.filter(s => s.done).length;
  const checksDone = checks.filter(c => c.done).length;
  const totalSubs = subs.length + checks.length;
  const doneSubs = subsDone + checksDone;

  const created = new Date(task.createdAt).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
  const updated = new Date(task.updatedAt).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });

  // Compute "time in current status" from activity log
  const timeInStatus = (() => {
    const acts = task.activity || [];
    const lastChange = acts.find(a => /changed status/i.test(a.what || ''));
    const baseDateStr = task.updatedAt;
    let baseTime = new Date(baseDateStr + 'T00:00:00').getTime();
    if (lastChange?.at && /^\d{2}:\d{2}/.test(lastChange.at)) {
      const [h, m] = lastChange.at.split(':').map(Number);
      const d = new Date(TODAY); d.setHours(h, m, 0, 0);
      baseTime = d.getTime();
    }
    const diff = Date.now() - baseTime;
    const mins = Math.floor(diff / 60000);
    if (mins < 1) return 'just now';
    if (mins < 60) return `${mins}m`;
    const hrs = Math.floor(mins / 60);
    if (hrs < 24) return `${hrs}h ${mins % 60}m`;
    const days = Math.floor(hrs / 24);
    return `${days}d ${hrs % 24}h`;
  })();

  return (
    <div style={isModal ? {
      width: '100%', height: '100%',
      background: 'rgb(var(--surface))',
      display: 'flex', flexDirection: 'column',
      overflow: 'hidden',
    } : {
      width: 500, flexShrink: 0,
      borderLeft: '1px solid rgb(var(--rule))',
      background: 'rgb(var(--surface))',
      display: 'flex', flexDirection: 'column',
      overflow: 'hidden',
      boxShadow: '-12px 0 24px -16px rgba(15,23,42,0.08)',
    }} className={isModal ? 'fadein' : 'slide-in-r'}>

      {/* Top bar */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '12px 18px', borderBottom: '1px solid rgb(var(--rule))',
      }}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <span style={{ width: 8, height: 8, background: sp.colour, borderRadius: 2 }} />
          <span style={{ fontSize: 12, color: sp.colour, fontWeight: 600 }}>{sp.name}</span>
        </span>
        {grp && <>
          <span style={{ fontSize: 11, color: 'rgb(var(--muted-2))' }}>/</span>
          <span style={{ fontSize: 12, color: 'rgb(var(--muted))', fontWeight: 500 }}>{grp.name}</span>
        </>}
        <span style={{ flex: 1 }} />
        <span className="ticket-id">{task.ticket}</span>
        <IconBtn title="Copy link">⎘</IconBtn>
        <IconBtn title="More">⋯</IconBtn>
        <IconBtn onClick={onClose} title="Close" style={{ fontSize: 18, marginLeft: 2 }}>×</IconBtn>
      </div>

      {/* Body */}
      <div style={{ flex: 1, overflowY: 'auto' }}>

        {/* Title block */}
        <div style={{ padding: '22px 24px 18px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
            <PriorityDot p={task.priority} />
            <TypeBadge type={task.type} />
            <button onClick={() => onToggleFlag?.(task.id, 'important')}
              title={task.important ? 'Remove star' : 'Star task'}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                padding: '3px 10px', fontSize: 11.5, fontWeight: 600,
                background: task.important ? 'rgb(var(--warm-soft))' : 'transparent',
                color: task.important ? 'rgb(var(--warm))' : 'rgb(var(--muted))',
                border: '1px solid ' + (task.important ? 'rgb(var(--warm) / 0.3)' : 'rgb(var(--rule-2))'),
                borderRadius: 999,
                transition: 'all .12s',
              }}
              onMouseEnter={e => { if (!task.important) { e.currentTarget.style.color = 'rgb(var(--warm))'; e.currentTarget.style.borderColor = 'rgb(var(--warm))'; }}}
              onMouseLeave={e => { if (!task.important) { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}}>
              {task.important ? '★ Important' : '☆ Star'}
            </button>
            <button onClick={() => onToggleFlag?.(task.id, 'myDay')}
              title={task.myDay ? 'Remove from My Day' : 'Add to My Day'}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                padding: '3px 10px', fontSize: 11.5, fontWeight: 600,
                background: task.myDay ? 'rgb(var(--warm-soft))' : 'transparent',
                color: task.myDay ? 'rgb(var(--warm))' : 'rgb(var(--muted))',
                border: '1px solid ' + (task.myDay ? 'rgb(var(--warm) / 0.3)' : 'rgb(var(--rule-2))'),
                borderRadius: 999,
                transition: 'all .12s',
              }}
              onMouseEnter={e => { if (!task.myDay) { e.currentTarget.style.color = 'rgb(var(--warm))'; e.currentTarget.style.borderColor = 'rgb(var(--warm))'; }}}
              onMouseLeave={e => { if (!task.myDay) { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}}>
              {task.myDay ? '☀ My Day' : '+ My Day'}
            </button>
            {task.myDayUserIds && task.myDayUserIds.length > 0 && (
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }} title={task.myDayUserIds.map(id => userById(id)?.name).filter(Boolean).join(', ')}>
                <AvatarStack ids={task.myDayUserIds} size={18} max={4} />
                <span style={{ fontSize: 10.5, fontWeight: 700, color: 'rgb(var(--warm))', letterSpacing: '0.04em' }}>
                  {task.myDayUserIds.length}
                </span>
              </span>
            )}
          </div>
          {titleEditing ? (
            <input autoFocus value={titleDraft} onChange={e => setTitleDraft(e.target.value)}
              onBlur={commitTitle}
              onKeyDown={(e) => {
                if (e.key === 'Enter')  { e.preventDefault(); commitTitle(); }
                if (e.key === 'Escape') { e.preventDefault(); setTitleDraft(task.title); setTitleEditing(false); }
              }}
              style={{
                width: '100%', margin: 0,
                fontSize: 22, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.25,
                color: 'rgb(var(--ink))', fontFamily: 'inherit',
                border: '2px solid rgb(var(--accent))',
                borderRadius: 8, padding: '6px 10px',
                outline: 'none', background: 'rgb(var(--surface))',
                boxShadow: '0 0 0 4px rgb(var(--accent) / 0.15)',
              }}
            />
          ) : (
            <h1 onClick={() => { setTitleDraft(task.title); setTitleEditing(true); }}
              style={{
                margin: 0, fontSize: 22, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.25,
                color: 'rgb(var(--ink))', cursor: 'text',
                padding: '6px 10px', marginInline: -10,
                borderRadius: 8, transition: 'background-color .12s',
              }}
              onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--accent-soft) / 0.5)'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
              {task.title.split(/(\[[^\]]+\])/).map((part, i) =>
                /^\[([^\]]+)\]$/.test(part)
                  ? <span key={i} style={{ background: 'rgb(var(--warm-soft))', color: 'rgb(var(--warm))', padding: '0 4px', borderRadius: 4, fontWeight: 600 }}>{part}</span>
                  : part
              )}
            </h1>
          )}
          {/\[([^\]]+)\]/.test(task.title) && !titleEditing && (
            <p style={{ margin: '6px 0 0', fontSize: 11.5, color: 'rgb(var(--warm))', display: 'inline-flex', alignItems: 'center', gap: 4 }}>
              ⚡ This title has placeholders — add a patient below to auto-fill, or click the title to edit.
            </p>
          )}

          {/* Status + time row */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 16 }}>
            <StatusPicker task={task} onChange={onStatusChange} />
            <span title={`In ${statusById(task.statusId)?.title} for ${timeInStatus}`}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                fontSize: 9.5, fontWeight: 700, letterSpacing: '0.08em',
                color: 'rgb(var(--muted))', textTransform: 'uppercase',
              }}>
              <span>For</span>
              <span>{timeInStatus}</span>
            </span>
          </div>
        </div>

        {/* Properties grid */}
        <div style={{ padding: '0 24px 20px' }}>
          <div style={{
            display: 'grid', gridTemplateColumns: '110px 1fr',
            rowGap: 12, columnGap: 14,
            fontSize: 13,
          }}>
            <PropLabel>Assignees</PropLabel>
            <PropValue>
              <AssigneeEditor ids={task.assignees} onChange={(next) => onSetAssignees?.(task.id, next)} />
            </PropValue>

            <PropLabel>Queue</PropLabel>
            <PropValue>
              <QueueEditor queueId={task.queueId} onChange={(qid) => onSetQueue?.(task.id, qid)} />
            </PropValue>

            {(false) && (
              <>
                <PropLabel>On My Day</PropLabel>
                <PropValue>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                    <AvatarStack ids={task.myDayUserIds} size={20} max={5} />
                    <span style={{ fontSize: 12, color: 'rgb(var(--ink-2))' }}>
                      {task.myDayUserIds.map(id => userById(id)?.name.split(' ')[0]).join(', ')}
                    </span>
                    <span style={{ fontSize: 10.5, fontWeight: 600, color: 'rgb(var(--warm))', background: 'rgb(var(--warm-soft))', padding: '2px 8px', borderRadius: 999 }}>
                      ☀ {task.myDayUserIds.length}
                    </span>
                  </span>
                </PropValue>
              </>
            )}

            <PropLabel>Watchers</PropLabel>
            <PropValue>
              {task.watchers.length > 0
                ? <AvatarStack ids={task.watchers} size={20} />
                : <span className="ticket-id">none</span>}
            </PropValue>

            <PropLabel>Due &amp; reminder</PropLabel>
            <PropValue>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                <DueChip iso={task.dueDate} />
                <ReminderEditor reminder={task.reminder} dueDate={task.dueDate} onChange={(iso) => onSetReminder?.(task.id, iso)} />
              </span>
            </PropValue>

            <PropLabel>Estimate</PropLabel>
            <PropValue>
              {task.estimateMins > 0
                ? <span className="num" style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 12 }}>
                    {fmtMins(task.estimateMins)}
                    {task.actualMins > 0 && (
                      <span style={{ color: 'rgb(var(--muted))' }}>
                        {' · '}{fmtMins(task.actualMins)} logged
                        {task.actualMins > task.estimateMins && <span style={{ color: 'rgb(var(--brick))' }}> · +{fmtMins(task.actualMins - task.estimateMins)}</span>}
                      </span>
                    )}
                  </span>
                : <span className="ticket-id">no estimate</span>}
            </PropValue>

            {task.clients.length > 0 && (
              <>
                <PropLabel>Patients</PropLabel>
                <PropValue>
                  <PatientEditor ids={task.clients} onChange={(next) => onSetClients?.(task.id, next)} />
                </PropValue>
              </>
            )}
            {task.clients.length === 0 && (
              <>
                <PropLabel>Patients</PropLabel>
                <PropValue>
                  <PatientEditor ids={[]} onChange={(next) => onSetClients?.(task.id, next)} />
                </PropValue>
              </>
            )}

            {task.tags.length > 0 && (
              <>
                <PropLabel>Tags</PropLabel>
                <PropValue>
                  <span style={{ display: 'inline-flex', gap: 4, flexWrap: 'wrap' }}>
                    {task.tags.map(id => <TagChip key={id} tag={tagById(id)} />)}
                  </span>
                </PropValue>
              </>
            )}

            <PropLabel>Linked tickets</PropLabel>
            <PropValue>
              <LinkedTicketsEditor task={task} allTasks={allTasks} onToggle={onToggleLink} onOpen={onOpenTask} />
            </PropValue>
          </div>
        </div>

        {/* Description */}
        {task.description && (
          <div style={{ padding: '18px 24px 18px', borderTop: '1px solid rgb(var(--rule))' }}>
            <p className="eyebrow" style={{ marginBottom: 10 }}>Description</p>
            <div className="prose" dangerouslySetInnerHTML={{ __html: renderMd(task.description) }} />
          </div>
        )}

        {/* Checklist & Subtasks */}
        <div style={{ padding: '18px 24px 18px', borderTop: '1px solid rgb(var(--rule))' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
              <p className="eyebrow" style={{ margin: 0 }}>Subtasks &amp; checklist</p>
              {totalSubs > 0 && (
                <span className="num" style={{ fontSize: 11, fontWeight: 600, color: 'rgb(var(--muted))', padding: '2px 8px', background: 'rgb(var(--paper-2))', borderRadius: 999 }}>{doneSubs}/{totalSubs}</span>
              )}
              <span style={{ flex: 1 }} />
            </div>
            {totalSubs > 0 && (
              <div style={{ marginBottom: 14 }}>
                <ProgressBar value={(doneSubs/totalSubs)*100} height={5} colour={doneSubs === totalSubs ? 'rgb(var(--forest))' : 'rgb(var(--accent))'} />
              </div>
            )}
            {subs.length > 0 && (
              <div style={{ marginBottom: checks.length > 0 ? 10 : 8 }}>
                {subs.map(s => <ChecklistItem key={s.id} item={s} kind="subtask" onToggle={() => toggleSub(s.id)} onDelete={() => deleteSub(s.id)} />)}
              </div>
            )}
            <ChecklistAddRow value={newSubtask} onChange={setNewSubtask} onAdd={addSub} placeholder="Add subtask…" />
            {checks.length > 0 && (
              <div style={{ marginTop: 10 }}>
                {checks.map(c => <ChecklistItem key={c.id} item={c} kind="check" onToggle={() => toggleCheck(c.id)} onDelete={() => deleteCheck(c.id)} />)}
              </div>
            )}
            <div style={{ marginTop: 6 }}>
              <ChecklistAddRow value={newChecklist} onChange={setNewChecklist} onAdd={addCheck} placeholder="Add checklist item…" muted />
            </div>
          </div>

        {/* Activity tabs */}
        <div style={{ borderTop: '1px solid rgb(var(--rule))', padding: '14px 24px 0' }}>
          <div style={{ display: 'flex', gap: 2, marginBottom: 14, borderBottom: '1px solid rgb(var(--rule))', marginInline: -4 }}>
            {[
              ['activity',    `Comments`,    task.comments.length],
              ['attachments', `Files`,       task.attachments.length],
              ['history',     'Activity',    (task.activity || []).length + 1],
            ].map(([key, label, n]) => (
              <button key={key} onClick={() => setTab(key)}
                style={{
                  fontSize: 12.5, padding: '8px 12px',
                  fontWeight: 600, letterSpacing: '-0.005em',
                  color: tab === key ? 'rgb(var(--accent-2))' : 'rgb(var(--muted))',
                  borderBottom: '2px solid ' + (tab === key ? 'rgb(var(--accent))' : 'transparent'),
                  marginBottom: -1,
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                }}>
                {label}
                {n != null && n > 0 && <span style={{
                  fontSize: 10.5, fontWeight: 700,
                  padding: '0 6px', borderRadius: 999,
                  background: tab === key ? 'rgb(var(--accent))' : 'rgb(var(--paper-2))',
                  color: tab === key ? 'white' : 'rgb(var(--muted))',
                }}>{n}</span>}
              </button>
            ))}
          </div>
        </div>

        <div style={{ padding: '0 24px 24px' }}>
          {tab === 'activity' && (
            <>
              {task.comments.length === 0 && (
                <p style={{ color: 'rgb(var(--muted))', fontSize: 12.5, fontStyle: 'italic', padding: '12px 0' }}>No comments yet — start the conversation.</p>
              )}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
                {task.comments.map(c => <Comment key={c.id} comment={c} />)}
              </div>
              <CommentComposer value={composer} onChange={setComposer} />
            </>
          )}

          {tab === 'attachments' && (
            <>
              {task.attachments.length === 0 && (
                <p style={{ color: 'rgb(var(--muted))', fontSize: 12.5, fontStyle: 'italic', padding: '12px 0' }}>No files attached.</p>
              )}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {task.attachments.map(a => {
                  const u = userById(a.by);
                  return (
                    <div key={a.id} style={{
                      display: 'flex', alignItems: 'center', gap: 10,
                      padding: '8px 10px',
                      background: 'rgb(var(--paper-2) / 0.4)',
                      border: '1px solid rgb(var(--rule))',
                    }}>
                      <span style={{
                        width: 28, height: 32, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                        background: 'rgb(var(--paper))', border: '1px solid rgb(var(--rule-2))',
                        fontFamily: 'JetBrains Mono, monospace', fontSize: 9, fontWeight: 700, color: 'rgb(var(--muted))',
                        letterSpacing: '0.05em',
                      }}>{a.name.split('.').pop().toUpperCase()}</span>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 12.5, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{a.name}</div>
                        <div className="ticket-id">{a.size} · {u?.name} · {a.at}</div>
                      </div>
                      <button className="btn btn-ghost" style={{ fontSize: 11 }}>↓</button>
                    </div>
                  );
                })}
                <div style={{
                  padding: '10px', textAlign: 'center', fontSize: 11.5,
                  color: 'rgb(var(--muted))', border: '1px dashed rgb(var(--rule-2))',
                }}>Drop files to attach, or <span style={{ color: 'rgb(var(--accent))', textDecoration: 'underline' }}>browse</span></div>
              </div>
            </>
          )}

          {tab === 'history' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
              {(task.activity || []).map((a, i) => {
                const u = userById(a.who);
                return (
                  <div key={i} style={{
                    display: 'grid', gridTemplateColumns: '60px 28px 1fr',
                    gap: 10, alignItems: 'center',
                    padding: '8px 0', borderBottom: '1px solid rgb(var(--rule) / 0.5)',
                    fontSize: 12.5,
                  }}>
                    <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 11, color: 'rgb(var(--muted))' }}>{a.at}</span>
                    <Avatar user={u} size={22} />
                    <div>
                      <span style={{ fontWeight: 600, color: 'rgb(var(--ink))' }}>{u?.name?.split(' ')[0] || 'Someone'}</span>
                      <span style={{ color: 'rgb(var(--ink-2))' }}> {a.what}</span>
                    </div>
                  </div>
                );
              })}
              {/* Creation entry at the bottom */}
              <div style={{
                display: 'grid', gridTemplateColumns: '60px 28px 1fr',
                gap: 10, alignItems: 'center',
                padding: '10px 0 4px',
                fontSize: 12.5,
                marginTop: (task.activity || []).length > 0 ? 4 : 0,
              }}>
                <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 11, color: 'rgb(var(--muted))' }}>{created.split(' ')[0]} {created.split(' ')[1]}</span>
                <span style={{
                  width: 22, height: 22, borderRadius: '50%',
                  background: 'rgb(var(--forest-soft))', color: 'rgb(var(--forest))',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 12, fontWeight: 700,
                }}>✦</span>
                <div>
                  <span style={{ fontWeight: 600, color: 'rgb(var(--ink))' }}>Task created</span>
                  <span style={{ color: 'rgb(var(--ink-2))' }}> by <strong>{userById(task.assignees[0] || 1)?.name?.split(' ')[0] || 'someone'}</strong> on {created}</span>
                </div>
              </div>
              {(task.activity || []).length === 0 && (
                <p style={{ color: 'rgb(var(--muted))', fontSize: 12, fontStyle: 'italic', padding: '8px 0 0', borderTop: '1px solid rgb(var(--rule) / 0.5)' }}>No further changes recorded.</p>
              )}
            </div>
          )}
        </div>
      </div>

      {/* Footer meta */}
      <div style={{
        borderTop: '1px solid rgb(var(--rule))',
        padding: '10px 18px',
        display: 'flex', alignItems: 'center', gap: 10,
        background: 'rgb(var(--bg))',
        fontSize: 11, color: 'rgb(var(--muted))', fontFamily: 'JetBrains Mono, monospace',
      }}>
        <span>Created {created}</span>
        <span>·</span>
        <span>Updated {updated}</span>
        <span style={{ flex: 1 }} />
        <span>#{task.id}</span>
      </div>
    </div>
  );
}

function StatusPicker({ task, onChange }) {
  const [open, setOpen] = React.useState(false);
  const s = statusById(task.statusId);
  return (
    <div style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '6px 12px 6px 10px',
          background: s.colour + '14', color: s.colour,
          fontSize: 12, fontWeight: 700, letterSpacing: '-0.005em',
          borderRadius: 999,
          transition: 'background-color .12s',
        }}
        onMouseEnter={e => e.currentTarget.style.background = s.colour + '22'}
        onMouseLeave={e => e.currentTarget.style.background = s.colour + '14'}>
        <span className="pip" style={{ background: s.colour }} />
        {s.title}
        <span style={{ fontSize: 10, marginLeft: 2, opacity: 0.7 }}>▾</span>
      </button>
      {open && (
        <div className="shadow-pop fadein" style={{
          position: 'absolute', top: '100%', left: 0, marginTop: 6,
          background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
          minWidth: 180, padding: 6, zIndex: 30,
          borderRadius: 10,
        }}>
          {D.statuses.map(st => (
            <button key={st.id} onClick={() => { onChange(task.id, st.id); setOpen(false); }}
              style={{
                display: 'flex', alignItems: 'center', gap: 8, width: '100%',
                padding: '7px 10px', fontSize: 12.5, fontWeight: 500,
                color: 'rgb(var(--ink))', textAlign: 'left',
                borderRadius: 6,
              }}
              onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2))'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
              <span className="pip" style={{ background: st.colour }} />
              {st.title}
              {task.statusId === st.id && <span style={{ marginLeft: 'auto', color: 'rgb(var(--accent))', fontSize: 11 }}>✓</span>}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function PropLabel({ children }) {
  return <span style={{ paddingTop: 5, fontSize: 11, fontWeight: 500, color: 'rgb(var(--muted))', letterSpacing: '-0.005em' }}>{children}</span>;
}
function PropValue({ children }) {
  return <span style={{ minWidth: 0, paddingBlock: 2 }}>{children}</span>;
}

function ChecklistItem({ item, kind, onToggle, onDelete }) {
  return (
    <div className="checklist-item" style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '8px 0',
      borderBottom: '1px solid rgb(var(--rule) / 0.5)',
    }}>
      <Checkbox checked={item.done} onClick={(e) => { e.stopPropagation(); onToggle?.(); }} />
      <span style={{
        fontSize: 13, flex: 1,
        color: item.done ? 'rgb(var(--muted))' : 'rgb(var(--ink-2))',
        textDecoration: item.done ? 'line-through' : 'none',
      }}>
        {item.title}
      </span>
      {kind === 'check' && <span style={{ fontSize: 10, fontWeight: 600, color: 'rgb(var(--muted))', padding: '1px 7px', background: 'rgb(var(--paper-2))', borderRadius: 999 }}>checklist</span>}
      <button onClick={(e) => { e.stopPropagation(); onDelete?.(); }} title="Delete"
        style={{
          fontSize: 14, color: 'rgb(var(--muted))', padding: '0 4px', lineHeight: 1, opacity: 0.4,
          borderRadius: 4,
        }}
        onMouseEnter={e => { e.currentTarget.style.opacity = '1'; e.currentTarget.style.color = 'rgb(var(--brick))'; }}
        onMouseLeave={e => { e.currentTarget.style.opacity = '0.4'; e.currentTarget.style.color = 'rgb(var(--muted))'; }}
      >×</button>
    </div>
  );
}

function ChecklistAddRow({ value, onChange, onAdd, placeholder, muted = false }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '6px 0',
    }}>
      <span style={{
        width: 18, height: 18, borderRadius: 6,
        border: '1.5px dashed rgb(var(--rule-2))',
        flexShrink: 0,
      }} />
      <input value={value} onChange={e => onChange(e.target.value)}
        onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); onAdd(); } }}
        placeholder={placeholder}
        style={{
          flex: 1, padding: '4px 6px', fontSize: 13,
          border: 'none', background: 'transparent', outline: 'none',
          fontFamily: 'inherit', color: muted ? 'rgb(var(--muted))' : 'rgb(var(--ink-2))',
        }}
      />
      {value.trim() && (
        <button onClick={onAdd} className="btn btn-ghost" style={{ fontSize: 11 }}>Add</button>
      )}
    </div>
  );
}

function Comment({ comment }) {
  const u = userById(comment.userId);
  return (
    <div style={{ display: 'flex', gap: 12 }}>
      <Avatar user={u} size={32} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
          <span style={{ fontSize: 13, fontWeight: 600 }}>{u.name}</span>
          <span style={{ fontSize: 11, color: 'rgb(var(--muted))' }}>{u.role}</span>
          <span style={{ flex: 1 }} />
          <span style={{ fontSize: 11, color: 'rgb(var(--muted))' }}>{comment.at}</span>
        </div>
        <div style={{
          marginTop: 6, padding: '10px 12px',
          background: 'rgb(var(--paper-2) / 0.7)', borderRadius: 10,
        }}>
          <div className="prose" style={{ fontSize: 13 }}
            dangerouslySetInnerHTML={{ __html: renderMd(comment.body, { mentions: true }) }} />
          {comment.reactions && Object.keys(comment.reactions).length > 0 && (
            <div style={{ display: 'flex', gap: 5, marginTop: 8 }}>
              {Object.entries(comment.reactions).map(([emoji, n]) => (
                <span key={emoji} style={{
                  fontSize: 12, padding: '2px 8px',
                  background: 'rgb(var(--surface))',
                  border: '1px solid rgb(var(--rule))',
                  display: 'inline-flex', alignItems: 'center', gap: 4,
                  borderRadius: 999,
                }}>
                  <span>{emoji}</span>
                  <span className="num" style={{ fontSize: 10.5, fontWeight: 600, color: 'rgb(var(--muted))' }}>{n}</span>
                </span>
              ))}
              <button style={{ fontSize: 12, padding: '2px 8px', color: 'rgb(var(--muted))', border: '1px dashed rgb(var(--rule-2))', borderRadius: 999, background: 'transparent' }}>+</button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function CommentComposer({ value, onChange }) {
  return (
    <div style={{
      marginTop: 16,
      border: '1px solid rgb(var(--rule))',
      background: 'rgb(var(--surface))',
      borderRadius: 12,
      overflow: 'hidden',
      transition: 'border-color .14s, box-shadow .14s',
    }}>
      <textarea
        value={value}
        onChange={e => onChange(e.target.value)}
        rows={2}
        placeholder="Add a comment, @mention someone, or type / for commands…"
        style={{
          width: '100%', resize: 'vertical', minHeight: 64,
          border: 'none', background: 'transparent',
          padding: '12px 14px',
          fontSize: 13, lineHeight: 1.55,
          borderRadius: 0,
          fontFamily: 'inherit',
        }}
      />
      <div style={{
        display: 'flex', alignItems: 'center', gap: 6,
        padding: '8px 12px',
        borderTop: '1px solid rgb(var(--rule))',
        background: 'rgb(var(--bg))',
      }}>
        <button className="btn-ghost" style={{ fontSize: 12, padding: '4px 8px' }} title="Mention">@</button>
        <button className="btn-ghost" style={{ fontSize: 12, padding: '4px 8px' }} title="Slash command">/</button>
        <button className="btn-ghost" style={{ fontSize: 12, padding: '4px 8px' }} title="Attach">📎</button>
        <button className="btn-ghost" style={{ fontSize: 12, padding: '4px 8px' }} title="Emoji">😀</button>
        <span style={{ flex: 1 }} />
        <button className="btn btn-accent" style={{ fontSize: 12.5, padding: '5px 14px' }}>Comment</button>
      </div>
    </div>
  );
}

function ReminderEditor({ reminder, dueDate, onChange }) {
  const [open, setOpen] = React.useState(false);
  const reminderLabel = () => {
    if (!reminder) return null;
    const r = new Date(reminder + 'T00:00:00');
    const today = new Date(TODAY); today.setHours(0,0,0,0);
    const diff = Math.round((r - today) / 86400000);
    const fmt = r.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });
    if (diff < 0) return `${fmt} · passed`;
    if (diff === 0) return `${fmt} · today`;
    if (diff === 1) return `${fmt} · tomorrow`;
    return `${fmt} · in ${diff}d`;
  };
  const lbl = reminderLabel();
  const presets = [
    ['Tomorrow',  () => { const d = new Date(TODAY); d.setDate(d.getDate()+1); return d.toISOString().slice(0,10); }],
    ['In 3 days', () => { const d = new Date(TODAY); d.setDate(d.getDate()+3); return d.toISOString().slice(0,10); }],
    ['Next Mon',  () => { const d = new Date(TODAY); d.setDate(d.getDate() + ((1 + 7 - d.getDay()) % 7 || 7)); return d.toISOString().slice(0,10); }],
  ];
  if (dueDate) {
    [1, 3, 7].forEach(n => {
      presets.push([`${n}d before due`, () => {
        const d = new Date(dueDate + 'T00:00:00');
        d.setDate(d.getDate() - n);
        return d.toISOString().slice(0, 10);
      }]);
    });
  }
  return (
    <span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
      {lbl ? (
        <span style={{
          display: 'inline-flex', alignItems: 'center', gap: 5,
          padding: '3px 10px', background: 'rgb(var(--warm-soft))', color: 'rgb(var(--warm))',
          fontSize: 11.5, fontWeight: 600, borderRadius: 999,
        }}>🔔 {lbl}</span>
      ) : null}
      <button onClick={() => setOpen(o => !o)} style={{
        fontSize: 11.5, color: 'rgb(var(--muted))', padding: '3px 9px',
        border: '1px dashed rgb(var(--rule-2))', borderRadius: 999,
      }}
        onMouseEnter={e => { e.currentTarget.style.color = 'rgb(var(--warm))'; e.currentTarget.style.borderColor = 'rgb(var(--warm))'; }}
        onMouseLeave={e => { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}>
        {reminder ? 'change' : '+ set reminder'}
      </button>
      {open && (
        <>
          <span onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
          <div className="shadow-pop fadein" style={{
            position: 'absolute', top: '100%', left: 0, marginTop: 6,
            background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
            borderRadius: 10, padding: 10, zIndex: 61, minWidth: 260,
          }}>
            <p style={{ margin: '0 0 6px', fontSize: 10, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.06em', textTransform: 'uppercase' }}>Custom date</p>
            <input type="date" defaultValue={reminder || ''}
              onChange={e => { if (e.target.value) { onChange?.(e.target.value); setOpen(false); } }}
              style={{ width: '100%', fontSize: 12, marginBottom: 10 }} />
            <p style={{ margin: '0 0 6px', fontSize: 10, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.06em', textTransform: 'uppercase' }}>Quick set</p>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
              {presets.map(([lbl, fn]) => (
                <button key={lbl} onClick={() => { onChange?.(fn()); setOpen(false); }}
                  style={{ fontSize: 11, padding: '4px 10px', background: 'rgb(var(--paper-2))', borderRadius: 999, color: 'rgb(var(--ink-2))', fontWeight: 500 }}>
                  {lbl}
                </button>
              ))}
            </div>
            {reminder && (
              <button onClick={() => { onChange?.(null); setOpen(false); }} style={{
                display: 'flex', alignItems: 'center', gap: 6, width: '100%',
                marginTop: 10, padding: '6px 10px', borderRadius: 8,
                fontSize: 11.5, color: 'rgb(var(--brick))', fontWeight: 500,
                background: 'rgb(var(--brick-soft))',
              }}>
                <span>×</span> Clear reminder
              </button>
            )}
          </div>
        </>
      )}
    </span>
  );
}

function LinkedTicketsEditor({ task, allTasks, onToggle, onOpen }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const linkedIds = task.linkedTaskIds || [];
  const candidates = (allTasks || []).filter(t => t.id !== task.id && !linkedIds.includes(t.id) && (!q.trim() || t.title.toLowerCase().includes(q.toLowerCase()) || t.ticket.toLowerCase().includes(q.toLowerCase()))).slice(0, 12);
  return (
    <span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
      {linkedIds.length === 0 && null}
      {linkedIds.map(id => {
        const l = (allTasks || []).find(t => t.id === id);
        if (!l) return null;
        const sp = spaceById(l.spaceId);
        const st = statusById(l.statusId);
        return (
          <span key={id} style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '3px 4px 3px 9px',
            background: 'rgb(var(--paper-2))', borderRadius: 999,
            border: '1px solid rgb(var(--rule))',
            fontSize: 11.5,
          }}>
            <span style={{ width: 5, height: 5, background: sp?.colour, borderRadius: 2 }} />
            <button onClick={() => onOpen?.(id)} style={{
              fontFamily: 'JetBrains Mono, monospace', fontSize: 10.5, color: 'rgb(var(--accent))',
              fontWeight: 600,
            }} title={l.title}>{l.ticket}</button>
            <span className="pip" style={{ background: st?.colour, width: 5, height: 5 }} title={st?.title} />
            <button onClick={() => onToggle?.(task.id, id)} title="Unlink" style={{
              width: 18, height: 18, borderRadius: '50%',
              fontSize: 12, lineHeight: 1, fontWeight: 700,
              color: 'rgb(var(--muted))',
            }}
              onMouseEnter={e => { e.currentTarget.style.background = 'rgb(var(--brick) / 0.15)'; e.currentTarget.style.color = 'rgb(var(--brick))'; }}
              onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'rgb(var(--muted))'; }}>×</button>
          </span>
        );
      })}
      <button onClick={() => setOpen(o => !o)} style={{
        fontSize: 11.5, color: 'rgb(var(--muted))', padding: '3px 10px',
        border: '1px dashed rgb(var(--rule-2))', borderRadius: 999,
      }}
        onMouseEnter={e => { e.currentTarget.style.color = 'rgb(var(--accent))'; e.currentTarget.style.borderColor = 'rgb(var(--accent))'; }}
        onMouseLeave={e => { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}>
        + link ticket
      </button>
      {open && (
        <>
          <span onClick={() => { setOpen(false); setQ(''); }} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
          <div className="shadow-pop fadein" style={{
            position: 'absolute', top: '100%', left: 0, marginTop: 6,
            background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
            borderRadius: 10, padding: 6, zIndex: 61, minWidth: 320, maxHeight: 360, overflowY: 'auto',
          }}>
            <input autoFocus value={q} onChange={e => setQ(e.target.value)}
              placeholder="Search by ticket or title…"
              style={{ width: '100%', fontSize: 12, padding: '6px 10px', marginBottom: 4 }} />
            {candidates.length === 0 && <p style={{ margin: 0, padding: '8px 10px', fontSize: 11.5, color: 'rgb(var(--muted))', fontStyle: 'italic' }}>No matches.</p>}
            {candidates.map(c => {
              const sp = spaceById(c.spaceId);
              const st = statusById(c.statusId);
              return (
                <button key={c.id} onClick={() => { onToggle?.(task.id, c.id); setOpen(false); setQ(''); }} style={{
                  display: 'grid', gridTemplateColumns: '8px 70px 1fr auto',
                  gap: 8, alignItems: 'center',
                  width: '100%', padding: '7px 10px', borderRadius: 6, textAlign: 'left',
                }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2))'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                  <span style={{ width: 6, height: 6, background: sp?.colour, borderRadius: 2 }} />
                  <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10.5, color: 'rgb(var(--muted))', fontWeight: 600 }}>{c.ticket}</span>
                  <span style={{ fontSize: 12, color: 'rgb(var(--ink-2))', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.title}</span>
                  <span className="pip" style={{ background: st?.colour }} />
                </button>
              );
            })}
          </div>
        </>
      )}
    </span>
  );
}

function PatientEditor({ ids, onChange }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const add = (id) => onChange?.([...new Set([...(ids || []), id])]);
  const remove = (id) => onChange?.((ids || []).filter(x => x !== id));
  const available = D.clients.filter(c => !(ids || []).includes(c.id) && (!q.trim() || c.name.toLowerCase().includes(q.toLowerCase()) || c.ref.toLowerCase().includes(q.toLowerCase())));
  return (
    <span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
      {(ids || []).map(id => {
        const c = clientById(id);
        if (!c) return null;
        return (
          <span key={id} style={{
            display: 'inline-flex', alignItems: 'center', gap: 5,
            padding: '3px 4px 3px 10px', background: 'rgb(var(--paper-2))',
            borderRadius: 999, fontSize: 12,
          }}>
            <span style={{ width: 6, height: 6, background: 'rgb(var(--accent))', borderRadius: '50%' }} />
            <span style={{ fontWeight: 500 }}>{c.name}</span>
            <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10.5, color: 'rgb(var(--muted))' }}>{c.ref}</span>
            <button onClick={() => remove(id)} title={`Remove ${c.name}`} style={{
              width: 18, height: 18, borderRadius: '50%',
              fontSize: 12, lineHeight: 1, fontWeight: 700, color: 'rgb(var(--muted))',
            }}
              onMouseEnter={e => { e.currentTarget.style.background = 'rgb(var(--brick) / 0.15)'; e.currentTarget.style.color = 'rgb(var(--brick))'; }}
              onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'rgb(var(--muted))'; }}>×</button>
          </span>
        );
      })}
      <button onClick={() => setOpen(o => !o)} style={{
        fontSize: 11.5, color: 'rgb(var(--muted))', padding: '3px 10px',
        border: '1px dashed rgb(var(--rule-2))', borderRadius: 999,
      }}
        onMouseEnter={e => { e.currentTarget.style.color = 'rgb(var(--accent))'; e.currentTarget.style.borderColor = 'rgb(var(--accent))'; }}
        onMouseLeave={e => { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}>+ patient</button>
      {open && (
        <>
          <span onClick={() => { setOpen(false); setQ(''); }} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
          <div className="shadow-pop fadein" style={{
            position: 'absolute', top: '100%', left: 0, marginTop: 6,
            background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
            borderRadius: 10, padding: 6, zIndex: 61, minWidth: 260, maxHeight: 320, overflowY: 'auto',
          }}>
            <input autoFocus value={q} onChange={e => setQ(e.target.value)}
              placeholder="Search patients by name or ref…"
              style={{ width: '100%', fontSize: 12, padding: '6px 10px', marginBottom: 4 }} />
            {available.length === 0 && <p style={{ margin: 0, padding: '8px 10px', fontSize: 11.5, color: 'rgb(var(--muted))', fontStyle: 'italic' }}>No matches.</p>}
            {available.map(c => (
              <button key={c.id} onClick={() => { add(c.id); setOpen(false); setQ(''); }} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                width: '100%', padding: '7px 10px', borderRadius: 6, textAlign: 'left',
              }}
                onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2))'}
                onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                <span style={{
                  width: 26, height: 26, borderRadius: '50%',
                  background: 'rgb(var(--accent-soft))', color: 'rgb(var(--accent))',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 11, fontWeight: 700,
                }}>{c.name.split(' ').map(p => p.charAt(0)).join('').slice(0, 2)}</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: 'rgb(var(--ink))' }}>{c.name}</div>
                  <div style={{ fontSize: 10.5, color: 'rgb(var(--muted))', fontFamily: 'JetBrains Mono, monospace' }}>{c.ref}</div>
                </div>
              </button>
            ))}
          </div>
        </>
      )}
    </span>
  );
}

function QueueEditor({ queueId, onChange }) {
  const [open, setOpen] = React.useState(false);
  const q = queueId ? queueById(queueId) : null;
  return (
    <span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
      {q ? (
        <span style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '3px 11px', background: q.colour + '14', color: q.colour,
          fontSize: 12, fontWeight: 600, borderRadius: 999,
        }}>
          <span style={{ width: 7, height: 7, background: q.colour, borderRadius: '50%' }} />
          {q.name}
        </span>
      ) : (
        <span style={{ fontSize: 11.5, color: 'rgb(var(--muted))' }}>—</span>
      )}
      <button onClick={() => setOpen(o => !o)} style={{
        fontSize: 11.5, color: 'rgb(var(--muted))', padding: '3px 9px',
        border: '1px dashed rgb(var(--rule-2))', borderRadius: 999,
      }}
        onMouseEnter={e => { e.currentTarget.style.color = 'rgb(var(--accent))'; e.currentTarget.style.borderColor = 'rgb(var(--accent))'; }}
        onMouseLeave={e => { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}>
        {q ? 'change' : '+ pick queue'}
      </button>
      {open && (
        <>
          <span onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
          <div className="shadow-pop fadein" style={{
            position: 'absolute', top: '100%', left: 0, marginTop: 6,
            background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
            borderRadius: 10, padding: 4, zIndex: 61, minWidth: 220,
          }}>
            <p style={{ margin: 0, padding: '6px 10px 4px', fontSize: 10, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.06em', textTransform: 'uppercase' }}>Move to queue</p>
            {D.queues.map(qu => (
              <button key={qu.id} onClick={() => { onChange?.(qu.id); setOpen(false); }} style={{
                display: 'flex', alignItems: 'center', gap: 9, width: '100%',
                padding: '6px 10px', borderRadius: 6, textAlign: 'left',
                background: queueId === qu.id ? 'rgb(var(--paper-2))' : 'transparent',
              }}
                onMouseEnter={e => { if (queueId !== qu.id) e.currentTarget.style.background = 'rgb(var(--paper-2) / 0.6)'; }}
                onMouseLeave={e => { if (queueId !== qu.id) e.currentTarget.style.background = 'transparent'; }}>
                <span style={{ width: 8, height: 8, background: qu.colour, borderRadius: '50%' }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: 'rgb(var(--ink))' }}>{qu.name}</div>
                  <div style={{ fontSize: 10.5, color: 'rgb(var(--muted))' }}>{qu.members} members</div>
                </div>
                {queueId === qu.id && <span style={{ color: 'rgb(var(--accent))', fontSize: 11 }}>✓</span>}
              </button>
            ))}
            {queueId && (
              <>
                <div style={{ height: 1, background: 'rgb(var(--rule))', margin: '5px 6px' }} />
                <button onClick={() => { onChange?.(null); setOpen(false); }} style={{
                  display: 'flex', alignItems: 'center', gap: 9, width: '100%',
                  padding: '6px 10px', borderRadius: 6, textAlign: 'left',
                  color: 'rgb(var(--brick))', fontSize: 12.5, fontWeight: 500,
                }}
                  onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--brick-soft) / 0.6)'}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                  <span style={{ width: 18, textAlign: 'center' }}>×</span>
                  Remove from queue
                </button>
              </>
            )}
          </div>
        </>
      )}
    </span>
  );
}

function AssigneeEditor({ ids, onChange }) {
  const [open, setOpen] = React.useState(false);
  const add = (id) => onChange?.([...new Set([...(ids || []), id])]);
  const remove = (id) => onChange?.((ids || []).filter(x => x !== id));
  const available = D.users.filter(u => !(ids || []).includes(u.id));
  return (
    <span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
      {(ids || []).map(id => {
        const u = userById(id);
        if (!u) return null;
        return (
          <span key={id} style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '2px 4px 2px 2px', background: 'rgb(var(--paper-2))',
            borderRadius: 999,
          }}>
            <Avatar user={u} size={20} />
            <span style={{ fontSize: 12, fontWeight: 500 }}>{u.name}</span>
            <button onClick={() => remove(id)} title={`Remove ${u.name}`} style={{
              width: 18, height: 18, borderRadius: '50%',
              fontSize: 12, lineHeight: 1, fontWeight: 700,
              color: 'rgb(var(--muted))',
              transition: 'background-color .12s, color .12s',
            }}
              onMouseEnter={e => { e.currentTarget.style.background = 'rgb(var(--brick) / 0.15)'; e.currentTarget.style.color = 'rgb(var(--brick))'; }}
              onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; e.currentTarget.style.color = 'rgb(var(--muted))'; }}>×</button>
          </span>
        );
      })}
      <button onClick={() => setOpen(o => !o)} style={{
        fontSize: 11.5, color: 'rgb(var(--muted))', padding: '3px 10px',
        border: '1px dashed rgb(var(--rule-2))', borderRadius: 999,
      }}
        onMouseEnter={e => { e.currentTarget.style.color = 'rgb(var(--accent))'; e.currentTarget.style.borderColor = 'rgb(var(--accent))'; }}
        onMouseLeave={e => { e.currentTarget.style.color = 'rgb(var(--muted))'; e.currentTarget.style.borderColor = 'rgb(var(--rule-2))'; }}>+ add</button>
      {open && (
        <>
          <span onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 60 }} />
          <div className="shadow-pop fadein" style={{
            position: 'absolute', top: '100%', left: 0, marginTop: 6,
            background: 'rgb(var(--surface))', border: '1px solid rgb(var(--rule))',
            borderRadius: 10, padding: 4, zIndex: 61, minWidth: 220, maxHeight: 260, overflowY: 'auto',
          }}>
            <p style={{ margin: 0, padding: '6px 10px 4px', fontSize: 10, fontWeight: 700, color: 'rgb(var(--muted))', letterSpacing: '0.06em', textTransform: 'uppercase' }}>Assign to</p>
            {available.length === 0 && <p style={{ margin: 0, padding: '8px 10px', fontSize: 12, color: 'rgb(var(--muted))', fontStyle: 'italic' }}>Everyone is already assigned.</p>}
            {available.map(u => (
              <button key={u.id} onClick={() => { add(u.id); setOpen(false); }} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                width: '100%', padding: '6px 10px', borderRadius: 6,
                textAlign: 'left',
              }}
                onMouseEnter={e => e.currentTarget.style.background = 'rgb(var(--paper-2))'}
                onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
                <Avatar user={u} size={22} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: 'rgb(var(--ink))' }}>{u.name}</div>
                  <div style={{ fontSize: 10.5, color: 'rgb(var(--muted))' }}>{u.role}</div>
                </div>
              </button>
            ))}
          </div>
        </>
      )}
    </span>
  );
}

function EmptyDetail({ onClose }) {
  return (
    <div style={{
      width: 500, flexShrink: 0,
      borderLeft: '1px solid rgb(var(--rule))',
      background: 'rgb(var(--surface))',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 32, textAlign: 'center',
    }}>
      <div>
        <div style={{
          width: 56, height: 56, margin: '0 auto 14px',
          background: 'rgb(var(--accent-soft))',
          borderRadius: '50%',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 24, color: 'rgb(var(--accent))',
        }}>→</div>
        <p style={{ fontSize: 15, fontWeight: 600, marginBottom: 6, color: 'rgb(var(--ink))' }}>Select a task</p>
        <p style={{ color: 'rgb(var(--muted))', fontSize: 13, maxWidth: 260, margin: '0 auto', lineHeight: 1.55 }}>
          Choose a task on the left to see its details, comments, files and activity.
        </p>
      </div>
    </div>
  );
}

/* tiny markdown renderer for descriptions / comments */
function renderMd(text, opts = {}) {
  let s = (text || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  s = s.replace(/`([^`]+)`/g, '<code>$1</code>');
  s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
  s = s.replace(/\*([^*]+)\*/g, '<em>$1</em>');
  if (opts.mentions) {
    s = s.replace(/@([\w.]+)/g, '<span style="color:rgb(var(--accent));font-weight:600;background:rgb(var(--accent) / 0.08);padding:0 3px">@$1</span>');
  }
  s = s.split(/\n\n/).map(p => `<p>${p.replace(/\n/g, '<br>')}</p>`).join('');
  return s;
}

window.DetailPane = DetailPane;
window.TaskDetailModal = TaskDetailModal;

function TaskDetailModal({ task, allTasks, onClose, onStatusChange, onSetAssignees, onSetQueue, onSetReminder, onToggleFlag, onSetClients, onUpdateTitle, editTitleOnOpen, clearEditTitleOnOpen, onLog, onToggleLink, onOpenTask }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose?.(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  if (!task) return null;
  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',
      padding: 24, zIndex: 210,
    }}>
      <div onClick={e => e.stopPropagation()} className="fadein" style={{
        background: 'rgb(var(--surface))',
        border: '1px solid rgb(var(--rule))',
        borderRadius: 16, overflow: 'hidden',
        boxShadow: 'var(--sh-pop)',
        width: 640, maxWidth: 'calc(100vw - 48px)',
        height: '88vh', maxHeight: 920,
        display: 'flex', flexDirection: 'column',
      }}>
        <DetailPane task={task} allTasks={allTasks} onClose={onClose} onStatusChange={onStatusChange} onSetAssignees={onSetAssignees} onSetQueue={onSetQueue} onSetReminder={onSetReminder} onToggleFlag={onToggleFlag} onSetClients={onSetClients} onUpdateTitle={onUpdateTitle} editTitleOnOpen={editTitleOnOpen} clearEditTitleOnOpen={clearEditTitleOnOpen} onLog={onLog} onToggleLink={onToggleLink} onOpenTask={onOpenTask} presentation="modal" />
      </div>
    </div>
  );
}
