// Kayser v2 — Bottom composer + ⌘ command palette + Trust sheet + Reference sheet.

// Pending-attachment row used inside the composer. Shows a real
// thumbnail for image uploads, a file card for everything else, plus
// an upload-status indicator (uploading / ready / failed).
function KxV2PendingAttachmentRow({ att, onRemove, fmtBytes }) {
  // Free the object-URL when the row unmounts. Otherwise blob memory
  // sticks around until the page is reloaded.
  React.useEffect(() => {
    return () => {
      if (att.localPreview) {
        try { URL.revokeObjectURL(att.localPreview); } catch (_) {}
      }
    };
  }, [att.localPreview]);

  const failed = att.status === 'failed';
  const pending = att.status === 'uploading';
  const ready = att.status === 'ready';
  const isImage = (att.type || '').startsWith('image/');

  // Status pill in the top-right corner of the row.
  const statusPill = (
    <span style={{
      fontFamily: KX2.fontMono, fontSize: 10, letterSpacing: 0.4,
      color: failed ? KX2.reject : (pending ? KX2.ink3 : '#a7f3d0'),
      padding: '2px 6px', borderRadius: 6,
      background: failed
        ? 'rgba(248,113,113,.12)'
        : (pending ? 'rgba(255,255,255,.06)' : 'rgba(52,211,153,.12)'),
    }}>
      {failed ? 'FAILED' : (pending ? 'UPLOADING' : 'READY')}
    </span>
  );

  // Remove (×) button — same in every variant.
  const removeBtn = (
    <button onClick={onRemove} aria-label="Remove attachment" style={{
      width: 20, height: 20, padding: 0, border: 'none', borderRadius: 10,
      background: 'rgba(0,0,0,.45)', color: '#fff',
      cursor: 'pointer', fontSize: 12, lineHeight: 1,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>×</button>
  );

  if (isImage && att.localPreview) {
    return (
      <div style={{
        ...kx2GlassThin({ borderRadius: 12 }),
        padding: 4, position: 'relative', maxWidth: 240,
        border: failed ? `1px solid ${KX2.reject}` : undefined,
      }}>
        <img src={att.localPreview} alt={att.name}
          style={{
            display: 'block', width: '100%', maxHeight: 160,
            borderRadius: 8, objectFit: 'cover',
            opacity: failed ? 0.5 : (pending ? 0.85 : 1),
            transition: 'opacity 150ms',
          }} />
        <div style={{
          position: 'absolute', top: 8, right: 8, display: 'flex', gap: 6,
        }}>
          {statusPill}
          {removeBtn}
        </div>
        <div style={{
          padding: '6px 4px 2px', display: 'flex', alignItems: 'baseline', gap: 6,
        }}>
          <span title={att.name} style={{
            fontFamily: KX2.fontUI, fontSize: 11.5, color: KX2.ink, flex: 1,
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', minWidth: 0,
          }}>{att.name}</span>
          <span style={{
            fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3,
          }}>{failed ? att.error : fmtBytes(att.size)}</span>
        </div>
      </div>
    );
  }

  // Non-image (and image without a local preview — shouldn't happen,
  // but render gracefully): file-card shape.
  const badge = kxAttachmentBadge ? kxAttachmentBadge(att.type) : { label: 'FILE', bg: 'rgba(255,255,255,.10)', fg: KX2.ink2 };
  return (
    <div style={{
      ...kx2GlassThin({ borderRadius: 12 }),
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '8px 12px', maxWidth: 320,
      border: failed ? `1px solid ${KX2.reject}` : undefined,
    }}>
      <div style={{
        width: 36, height: 44, borderRadius: 6,
        background: badge.bg, color: badge.fg,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: KX2.fontMono, fontSize: 10, fontWeight: 700,
        letterSpacing: 0.3, flexShrink: 0,
      }}>{badge.label}</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div title={att.name} style={{
          fontFamily: KX2.fontUI, fontSize: 13, color: KX2.ink, fontWeight: 500,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>{att.name}</div>
        <div style={{
          fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3, marginTop: 2,
          display: 'flex', alignItems: 'center', gap: 6,
        }}>
          <span>{failed ? att.error : fmtBytes(att.size)}</span>
          <span style={{ opacity: 0.5 }}>·</span>
          {statusPill}
        </div>
      </div>
      {removeBtn}
    </div>
  );
}

// ============ COMPOSER ============
//
// Attachments
// -----------
// The composer maintains a local queue of attachment objects, each
// shaped { id?, name, size, type, status, file?, error? }. Files added
// via the +button / drag-drop / paste are uploaded eagerly to
// POST /uploads — the server returns an attachment id; the queue chip
// flips from "uploading" to "ready". On send, attachment ids ride
// alongside the text via the onSend signature
// onSend(text, { attachment_ids: number[] }).
//
// All UI is debounce-free: attachments upload as you pick them, send is
// disabled while any attachment is still uploading.
function KxV2Composer({ onCmd, onSend, onVoice, placeholder, threadSlug }) {
  const [val, setVal] = React.useState('');
  const [listening, setListening] = React.useState(false);
  const [waveTick, setWaveTick] = React.useState(0);
  const [attachments, setAttachments] = React.useState([]); // {tmpId, id?, name, size, type, status, error?}
  const [dragHover, setDragHover] = React.useState(false);
  const fileInputRef = React.useRef(null);

  React.useEffect(() => {
    if (!listening) return;
    let lastVoice = Date.now();
    let stopped = false;
    const finish = () => {
      if (stopped) return;
      stopped = true;
      setListening(false);
      onVoice?.("What's on for tomorrow?");
    };
    const id = setInterval(() => {
      setWaveTick(t => t + 1);
      // Mock voice activity — assume 70% of ticks are "speech"
      if (Math.random() < 0.7) lastVoice = Date.now();
      // Auto-stop on 10s of silence
      if (Date.now() - lastVoice > 10000) finish();
    }, 120);
    // Hard cap at 30s for the demo
    const cap = setTimeout(finish, 30000);
    return () => { clearInterval(id); clearTimeout(cap); };
  }, [listening, onVoice]);

  // Mirror server-side limit (atlas-api MAX_FILE_BYTES). Avoids round-
  // tripping a 25MB blob just to get a 413 back.
  const MAX_FILE_BYTES = 25 * 1024 * 1024;
  const fmtBytes = (n) => {
    if (n < 1024) return `${n} B`;
    if (n < 1024 * 1024) return `${(n / 1024).toFixed(0)} KB`;
    return `${(n / 1024 / 1024).toFixed(1)} MB`;
  };

  // Upload one file. Pushes the chip immediately (status='uploading'),
  // then flips to 'ready' with the server-assigned id or 'failed' with
  // the error.
  const uploadFile = React.useCallback(async (file) => {
    const tmpId = 'a_' + Math.random().toString(36).slice(2, 10);
    // For images we generate a local object-URL so the preview shows
    // instantly, before the upload completes. Revoked when the
    // attachment is removed (see KxV2PendingAttachmentRow's effect).
    const localPreview = (file.type || '').startsWith('image/')
      ? URL.createObjectURL(file)
      : null;
    const stub = {
      tmpId, name: file.name, size: file.size, type: file.type,
      file, localPreview,
      status: file.size > MAX_FILE_BYTES ? 'failed' : 'uploading',
      error: file.size > MAX_FILE_BYTES ? `Over ${fmtBytes(MAX_FILE_BYTES)}` : undefined,
    };
    setAttachments((prev) => [...prev, stub]);
    if (stub.status === 'failed') return;

    if (!window.kxApi) {
      // Mock mode — no real upload available.
      setAttachments((prev) => prev.map((a) =>
        a.tmpId === tmpId ? { ...a, status: 'failed', error: 'Demo mode — uploads disabled.' } : a,
      ));
      return;
    }

    try {
      const fd = new FormData();
      fd.append('file', file, file.name);
      const path = '/uploads' + (threadSlug ? `?thread=${encodeURIComponent(threadSlug)}` : '');
      const resp = await window.kxApi(path, { method: 'POST', body: fd });
      if (resp && resp.id != null) {
        setAttachments((prev) => prev.map((a) =>
          a.tmpId === tmpId ? { ...a, id: resp.id, status: 'ready' } : a,
        ));
      } else {
        setAttachments((prev) => prev.map((a) =>
          a.tmpId === tmpId ? { ...a, status: 'failed', error: 'Upload failed.' } : a,
        ));
      }
    } catch (e) {
      setAttachments((prev) => prev.map((a) =>
        a.tmpId === tmpId ? { ...a, status: 'failed', error: 'Upload failed.' } : a,
      ));
    }
  }, [threadSlug]);

  const handleFiles = React.useCallback((fileList) => {
    if (!fileList || !fileList.length) return;
    [...fileList].forEach((f) => uploadFile(f));
  }, [uploadFile]);

  // Paste handler — attach files dropped from clipboard (screenshots
  // pasted from the OS, files copied from another app, etc.).
  const onPaste = React.useCallback((e) => {
    const files = e.clipboardData?.files;
    if (files && files.length) {
      e.preventDefault();
      handleFiles(files);
    }
  }, [handleFiles]);

  const removeAttachment = (tmpId) => {
    setAttachments((prev) => prev.filter((a) => a.tmpId !== tmpId));
  };

  const hasPending = attachments.some((a) => a.status === 'uploading');
  const hasReady = attachments.some((a) => a.status === 'ready');
  const canSend = val.trim().length > 0 || hasReady;
  const sendBlocked = hasPending;

  const doSend = () => {
    if (sendBlocked || !canSend) return;
    const ids = attachments.filter((a) => a.status === 'ready' && a.id != null).map((a) => a.id);
    onSend?.(val, { attachment_ids: ids });
    setVal('');
    setAttachments([]);
  };

  // Drag-and-drop on the whole composer container.
  const onDragOver = (e) => { e.preventDefault(); setDragHover(true); };
  const onDragLeave = (e) => {
    // Only clear when the drag actually leaves the container (not when
    // crossing internal child boundaries).
    if (e.currentTarget.contains(e.relatedTarget)) return;
    setDragHover(false);
  };
  const onDrop = (e) => {
    e.preventDefault();
    setDragHover(false);
    handleFiles(e.dataTransfer?.files);
  };

  return (
    <div
      onDragOver={onDragOver}
      onDragLeave={onDragLeave}
      onDrop={onDrop}
      style={{
        flexShrink: 0, padding: '8px 12px 12px',
        background: 'linear-gradient(180deg, transparent 0%, rgba(15,10,37,.6) 100%)',
        position: 'relative',
      }}
    >
      {/* Drag overlay — appears whenever a file is being dragged over
          the composer (or its parent thread area when we lift this
          higher in a later iteration). */}
      {dragHover && (
        <div style={{
          position: 'absolute', inset: 4, zIndex: 5, borderRadius: 20,
          border: `2px dashed ${KX2.brand}`,
          background: 'rgba(124,58,237,.08)', pointerEvents: 'none',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontFamily: KX2.fontUI, fontSize: 13, color: KX2.ink, fontWeight: 600,
        }}>Drop to attach</div>
      )}

      {/* Hidden file input — clicked by the paperclip button below. */}
      <input
        ref={fileInputRef}
        type="file"
        multiple
        style={{ display: 'none' }}
        onChange={(e) => {
          handleFiles(e.target.files);
          // Reset so picking the same file twice still fires onChange.
          e.target.value = '';
        }}
      />

      {/* Attachment queue — one row per pending attachment, richer than
          a name-only chip so the user can see what they're sending. The
          thumbnail uses the local File object via ObjectURL (no server
          round-trip needed for the just-picked image). For non-images
          we render the same file-card shape we use in chat bubbles. */}
      {attachments.length > 0 && (
        <div style={{
          display: 'flex', flexDirection: 'column', gap: 6,
          padding: '0 4px 8px',
        }}>
          {attachments.map((a) => (
            <KxV2PendingAttachmentRow key={a.tmpId} att={a}
              onRemove={() => removeAttachment(a.tmpId)}
              fmtBytes={fmtBytes} />
          ))}
        </div>
      )}

      {listening ? (
        <div style={{
          ...kx2Glass({ borderRadius: 22 }),
          display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px',
          border: `1px solid ${KX2.brand}`,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 3, flex: 1, height: 24 }}>
            {[...Array(18)].map((_, i) => {
              const phase = (waveTick + i) * 0.5;
              const h = 4 + Math.abs(Math.sin(phase)) * 18;
              return <div key={i} style={{
                width: 3, height: h, borderRadius: 1.5, background: KX2.brand,
                opacity: 0.55 + Math.abs(Math.sin(phase)) * 0.45,
                transition: 'height 110ms',
              }} />;
            })}
          </div>
          <div style={{ fontFamily: KX2.fontMono, fontSize: 11, color: KX2.ink3, letterSpacing: 0.4 }}>LISTENING…</div>
          <button onClick={() => { setListening(false); onVoice?.("What's on for tomorrow?"); }} aria-label="Stop recording" style={{
            height: 32, padding: '0 12px', borderRadius: 16, border: 'none', cursor: 'pointer',
            background: KX2.reject, color: '#fff', display: 'flex',
            alignItems: 'center', justifyContent: 'center', gap: 6,
            fontFamily: KX2.fontUI, fontSize: 12, fontWeight: 600,
          }}>
            <svg width="10" height="10" viewBox="0 0 10 10"><rect width="10" height="10" rx="1" fill="currentColor"/></svg>
            Tap to stop
          </button>
        </div>
      ) : (
        <div style={{
          ...kx2Glass({ borderRadius: 22 }),
          display: 'flex', alignItems: 'center', gap: 6, padding: 6,
        }}>
          {/* Commands — palette */}
          <button onClick={onCmd} aria-label="Commands" style={{
            height: 36, padding: '0 12px', borderRadius: 18, border: 'none',
            background: KX2.chipBg, color: KX2.ink, cursor: 'pointer',
            display: 'flex', alignItems: 'center', gap: 6, flexShrink: 0,
            fontFamily: KX2.fontUI, fontSize: 12.5, fontWeight: 600,
          }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <polyline points="3 11 12 4 21 11"/><path d="M5 11v9h14v-9"/>
            </svg>
            Commands
          </button>

          <input
            value={val} onChange={(e) => setVal(e.target.value)}
            placeholder={placeholder || "Ask Kayser, or tap Commands"}
            onPaste={onPaste}
            style={{
              flex: 1, background: 'transparent', border: 'none', outline: 'none',
              color: KX2.ink, fontSize: 14, fontFamily: KX2.fontUI, padding: '0 4px',
              minWidth: 0,
            }}
            onKeyDown={(e) => {
              if (e.key === 'Enter' && !e.shiftKey && canSend && !sendBlocked) {
                e.preventDefault();
                doSend();
              }
            }}
          />

          <button
            onClick={() => fileInputRef.current?.click()}
            aria-label="Attach files"
            title="Attach files (drag, drop, or paste also works)"
            style={{
              width: 36, height: 36, borderRadius: 18, border: 'none',
              background: 'transparent', color: KX2.ink2, cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
            }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/>
            </svg>
          </button>

          <button
            onClick={() => {
              if (canSend) { doSend(); }
              else { setListening(true); }
            }}
            disabled={sendBlocked && canSend}
            aria-label={canSend ? 'Send' : 'Voice'}
            title={sendBlocked ? 'Waiting for upload…' : (canSend ? 'Send' : 'Voice')}
            style={{
              width: 36, height: 36, borderRadius: 18, border: 'none',
              background: canSend ? (sendBlocked ? KX2.chipBg : KX2.brand) : KX2.chipBg,
              color: '#fff',
              cursor: sendBlocked ? 'not-allowed' : 'pointer',
              flexShrink: 0, opacity: sendBlocked ? 0.7 : 1,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              transition: 'background 120ms',
            }}>
            {canSend
              ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l14-7-7 14-2-5-5-2z"/></svg>
              : <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 1a4 4 0 0 0-4 4v6a4 4 0 0 0 8 0V5a4 4 0 0 0-4-4z"/><path d="M19 10v1a7 7 0 0 1-14 0v-1M12 18v3"/></svg>}
          </button>
        </div>
      )}
    </div>
  );
}

// ============ SHEET WRAPPER ============
function KxV2Sheet({ open, onClose, title, children, peek }) {
  if (!open) return null;
  const isDesktop = !!window.__kxDesktop;
  // On desktop, render as a right-side drawer that floats above the whole window.
  if (isDesktop) {
    return (
      <div onClick={onClose} style={{
        position: 'fixed', inset: 0, zIndex: 200,
        background: 'rgba(0,0,0,.35)', backdropFilter: 'blur(2px)',
        display: 'flex', justifyContent: 'flex-end',
        animation: 'kxv2FadeIn 160ms ease-out',
      }}>
        <div onClick={(e) => e.stopPropagation()} style={{
          ...kx2Glass({ borderRadius: '20px 0 0 20px' }),
          background: KX2.glassSheet,
          width: 'min(480px, 50vw)', height: '100%',
          display: 'flex', flexDirection: 'column',
          animation: 'kxv2SlideInRight 220ms cubic-bezier(.2,.8,.2,1)',
          borderLeft: `1px solid ${KX2.glassLine}`, borderRight: 'none',
          borderTop: 'none', borderBottom: 'none',
        }}>
          <div style={{
            display: 'flex', alignItems: 'center', padding: '14px 18px 12px',
            borderBottom: `1px solid ${KX2.glassLine}`,
          }}>
            <div style={{ fontFamily: KX2.fontBrand, fontSize: 16, fontWeight: 600, color: KX2.ink }}>{title}</div>
            <span style={{ flex: 1 }} />
            <button onClick={onClose} aria-label="Close" style={{
              width: 28, height: 28, borderRadius: 14, border: 'none',
              background: KX2.chipBg, color: KX2.ink2,
              cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
            </button>
          </div>
          <div style={{ overflowY: 'auto', flex: 1 }}>{children}</div>
        </div>
      </div>
    );
  }
  // Mobile / tablet: slide up from bottom (original behavior)
  return (
    <div onClick={onClose} style={{
      position: 'absolute', inset: 0, zIndex: 30,
      background: 'rgba(0,0,0,.45)', backdropFilter: 'blur(3px)',
      display: 'flex', alignItems: 'flex-end',
      animation: 'kxv2FadeIn 160ms ease-out',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        ...kx2Glass({ borderRadius: '20px 20px 0 0' }),
        background: KX2.glassSheet,
        width: '100%', maxHeight: peek ? '54%' : '88%',
        display: 'flex', flexDirection: 'column',
        animation: 'kxv2SlideUp 220ms cubic-bezier(.2,.8,.2,1)',
      }}>
        <div style={{
          display: 'flex', alignItems: 'center', padding: '10px 14px 8px',
          borderBottom: `1px solid ${KX2.glassLine}`,
        }}>
          <div style={{
            width: 36, height: 4, borderRadius: 2, background: 'rgba(255,255,255,.18)',
            position: 'absolute', left: '50%', top: 6, transform: 'translateX(-50%)',
          }} />
          <div style={{ fontFamily: KX2.fontBrand, fontSize: 15, fontWeight: 600, color: KX2.ink, marginTop: 6 }}>{title}</div>
          <span style={{ flex: 1 }} />
          <button onClick={onClose} style={{
            background: 'transparent', border: 'none', color: KX2.ink2,
            cursor: 'pointer', fontSize: 18, padding: 4, marginTop: 6,
          }}>✕</button>
        </div>
        <div style={{ overflowY: 'auto', flex: 1 }}>{children}</div>
      </div>
    </div>
  );
}

// ============ COMMAND PALETTE ============
const KXV2_COMMANDS = [
  { group: 'Today',   icon: '◷', label: "Show today's schedule",        hint: 'today' },
  { group: 'Today',   icon: '◷', label: 'Show overdue invoices',        hint: 'overdue' },
  { group: 'Today',   icon: '◷', label: 'What needs my approval?',      hint: 'queue' },
  { group: 'Money',   icon: '$', label: 'Cashflow this month',          hint: 'cashflow' },
  { group: 'Money',   icon: '$', label: 'Outstanding by customer',      hint: 'aging' },
  { group: 'Money',   icon: '$', label: 'Send a payment reminder…',     hint: 'remind' },
  { group: 'People',  icon: '◉', label: 'Find a customer…',             hint: 'find' },
  { group: 'People',  icon: '◉', label: 'Pipeline & recent estimates',  hint: 'pipeline' },
  { group: 'Train',   icon: '↗', label: 'Teach Kayser a new automation', hint: 'teach' },
  { group: 'Train',   icon: '↗', label: 'Review what Kayser auto-handled', hint: 'auto' },
  { group: 'Train',   icon: '↗', label: 'Adjust trust thresholds',      hint: 'trust' },
  { group: 'Memory',  icon: '◈', label: 'What I know about you',        hint: 'memory' },
  { group: 'Memory',  icon: '◈', label: 'Forget something',             hint: 'forget' },
];

function KxV2Palette({ open, onClose, onPick }) {
  const [q, setQ] = React.useState('');
  // Hermes-side skills are fetched on first open and cached in state.
  // Each one becomes a real palette command — name + description — that
  // sends a prompt asking the agent to use the skill. In live mode they
  // appear under a "Skills" group above the built-in command set.
  const [skills, setSkills] = React.useState([]);
  React.useEffect(() => { if (open) setQ(''); }, [open]);
  React.useEffect(() => {
    if (!open) return;
    if (!window.KX_TOKEN || !window.kxApi) return;
    let alive = true;
    window.kxApi('/skills').then((data) => {
      if (alive && Array.isArray(data)) setSkills(data);
    }).catch(() => { /* palette still works without skills */ });
    return () => { alive = false; };
  }, [open]);

  // Adapt server-shaped skills into palette commands. The ``hint`` is
  // the actual prompt sent to the agent on pick — we ask Hermes to
  // load + use the skill, with a placeholder the user can edit
  // mid-conversation. The label/description come straight from
  // /v1/skills so the agent's own ``skill_view`` description doesn't
  // drift from what we render.
  const skillCommands = skills.map((s) => ({
    group: 'Skills',
    icon: '▣',
    label: (s.name || 'unnamed-skill').replace(/[-_]/g, ' '),
    sub: s.description || '',
    hint: 'skill:' + (s.name || ''),
    sendText: `Use your ${s.name} skill for: `,
  }));

  const allCommands = [...skillCommands, ...KXV2_COMMANDS];
  const filt = allCommands.filter(c =>
    !q
    || c.label.toLowerCase().includes(q.toLowerCase())
    || (c.sub || '').toLowerCase().includes(q.toLowerCase())
    || c.hint.includes(q.toLowerCase()));
  const groups = ['Skills', 'Today', 'Money', 'People', 'Train', 'Memory'];

  return (
    <KxV2Sheet open={open} onClose={onClose} title="Commands">
      <div style={{ padding: '10px 12px' }}>
        <div style={{
          ...kx2GlassThin({ borderRadius: 12 }),
          display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px',
        }}>
          <span style={{ fontFamily: KX2.fontMono, color: KX2.ink3, fontSize: 14 }}>⌘</span>
          <input autoFocus value={q} onChange={(e) => setQ(e.target.value)}
            placeholder="Type a command, skill, or task…"
            style={{
              flex: 1, background: 'transparent', border: 'none', outline: 'none',
              color: KX2.ink, fontSize: 14, fontFamily: KX2.fontUI,
            }} />
        </div>
      </div>
      <div style={{ padding: '0 8px 16px' }}>
        {groups.map(g => {
          const items = filt.filter(c => c.group === g);
          if (!items.length) return null;
          return (
            <div key={g} style={{ marginTop: 8 }}>
              <div style={{
                fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3,
                letterSpacing: 0.6, textTransform: 'uppercase',
                padding: '6px 10px',
              }}>{g}{g === 'Skills' && skills.length > 0 ? ` (${skills.length})` : ''}</div>
              {items.map(c => (
                <button key={c.label + c.hint} onClick={() => onPick?.(c)} style={{
                  width: '100%', display: 'flex', alignItems: 'flex-start', gap: 12,
                  padding: '10px 12px', borderRadius: 10, background: 'transparent',
                  border: 'none', cursor: 'pointer', textAlign: 'left',
                  color: KX2.ink, fontSize: 14, fontFamily: KX2.fontUI,
                }}
                  onMouseEnter={(e) => e.currentTarget.style.background = KX2.chipBg}
                  onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
                  <span style={{
                    width: 24, height: 24, borderRadius: 6, fontSize: 12,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    background: KX2.chipBg, color: KX2.ink2, fontFamily: KX2.fontMono,
                    flexShrink: 0, marginTop: 1,
                  }}>{c.icon}</span>
                  <span style={{ flex: 1, minWidth: 0 }}>
                    <div style={{
                      whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                      textTransform: c.group === 'Skills' ? 'capitalize' : 'none',
                    }}>{c.label}</div>
                    {c.sub && (
                      <div style={{
                        fontSize: 11.5, color: KX2.ink3, marginTop: 2, lineHeight: 1.35,
                        display: '-webkit-box', WebkitLineClamp: 2,
                        WebkitBoxOrient: 'vertical', overflow: 'hidden',
                      }}>{c.sub}</div>
                    )}
                  </span>
                  <span style={{ fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3,
                    flexShrink: 0, marginTop: 2,
                  }}>↵</span>
                </button>
              ))}
            </div>
          );
        })}
      </div>
    </KxV2Sheet>
  );
}

// ============ TRUST SHEET ============
const KXV2_TRUST_TIERS = [
  { lvl: 1, name: 'Apprentice',  range: [0, 25],  blurb: 'Asks before everything.' },
  { lvl: 2, name: 'Assistant',   range: [25, 50], blurb: 'Handles routine reschedules and reminders on its own.' },
  { lvl: 3, name: 'Operator',    range: [50, 75], blurb: 'Sends quotes, books appointments, manages collections autonomously.' },
  { lvl: 4, name: 'Partner',     range: [75, 100], blurb: 'Acts as a true co-pilot. You review the day, not every action.' },
];

const KXV2_TRUST_DOMAINS = [
  { label: 'Scheduling',  pct: 82, auto: true,  did: 'Reschedules, confirmations, calendar holds' },
  { label: 'Reminders',   pct: 64, auto: true,  did: 'Invoice nudges, follow-ups under $5k' },
  { label: 'Quotes',      pct: 38, auto: false, did: 'Drafts only — you approve every send' },
  { label: 'Collections', pct: 22, auto: false, did: 'Drafts only — phone calls require you' },
  { label: 'New leads',   pct: 56, auto: true,  did: 'Auto-replies to inbound under 4 hours' },
];

function KxV2TrustSheet({ open, onClose, pct, level, onTeach }) {
  const tier = KXV2_TRUST_TIERS.find(t => t.lvl === level) || KXV2_TRUST_TIERS[0];
  const next = KXV2_TRUST_TIERS.find(t => t.lvl === level + 1);
  const toNext = next ? next.range[0] - pct : 0;

  return (
    <KxV2Sheet open={open} onClose={onClose} title="Trust & autonomy">
      <div style={{ padding: '14px 16px 20px' }}>
        {/* Big gauge */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <div style={{ position: 'relative', width: 96, height: 96, flexShrink: 0 }}>
            <TrustRing pct={pct} size={96} stroke={6} />
            <div style={{
              position: 'absolute', inset: 0, display: 'flex',
              flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
            }}>
              <div style={{ fontFamily: KX2.fontMono, fontSize: 22, fontWeight: 700, color: KX2.ink, fontVariantNumeric: 'tabular-nums', lineHeight: 1 }}>{pct}%</div>
              <div style={{ fontSize: 10, color: KX2.ink3, fontFamily: KX2.fontMono, letterSpacing: 0.5, marginTop: 2 }}>LEVEL {level}</div>
            </div>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: KX2.fontBrand, fontSize: 18, fontWeight: 700, color: KX2.ink, letterSpacing: -0.3 }}>{tier.name}</div>
            <div style={{ fontSize: 12.5, color: KX2.ink2, marginTop: 4, lineHeight: 1.45 }}>{tier.blurb}</div>
            {next && (
              <div style={{
                ...kx2GlassThin({ borderRadius: 8 }),
                marginTop: 8, padding: '6px 8px',
                fontSize: 11, color: KX2.ink2, fontFamily: KX2.fontMono,
              }}>{toNext}% to <span style={{ color: KX2.brand, fontWeight: 600 }}>{next.name}</span></div>
            )}
          </div>
        </div>

        {/* Tier ladder */}
        <div style={{ marginTop: 20 }}>
          <div style={{
            fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3,
            letterSpacing: 0.6, textTransform: 'uppercase', marginBottom: 8,
          }}>The journey</div>
          <div style={{ display: 'grid', gap: 6 }}>
            {KXV2_TRUST_TIERS.map(t => {
              const reached = pct >= t.range[0];
              const current = level === t.lvl;
              return (
                <div key={t.lvl} style={{
                  ...kx2GlassThin({ borderRadius: 10 }),
                  padding: '8px 10px', display: 'flex', alignItems: 'center', gap: 10,
                  opacity: reached ? 1 : 0.55,
                  border: current ? `1px solid ${KX2.brand}` : `1px solid ${KX2.glassLine}`,
                }}>
                  <div style={{
                    width: 22, height: 22, borderRadius: 11,
                    background: reached ? KX2.brand : KX2.chipBg,
                    color: '#fff', fontFamily: KX2.fontMono, fontSize: 10, fontWeight: 700,
                    display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
                  }}>L{t.lvl}</div>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 13, fontWeight: 600, color: KX2.ink }}>{t.name}</div>
                    <div style={{ fontSize: 11, color: KX2.ink3, marginTop: 1 }}>{t.blurb}</div>
                  </div>
                  <div style={{ fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3 }}>{t.range[0]}–{t.range[1]}%</div>
                </div>
              );
            })}
          </div>
        </div>

        {/* Domain breakdown */}
        <div style={{ marginTop: 22 }}>
          <div style={{
            fontFamily: KX2.fontMono, fontSize: 10, color: KX2.ink3,
            letterSpacing: 0.6, textTransform: 'uppercase', marginBottom: 8,
          }}>By area · tap to train</div>
          <div style={{ display: 'grid', gap: 8 }}>
            {KXV2_TRUST_DOMAINS.map(d => (
              <button key={d.label} onClick={onTeach} style={{
                ...kx2GlassThin({ borderRadius: 10 }),
                padding: '10px 12px', textAlign: 'left', cursor: 'pointer',
                color: KX2.ink, fontFamily: KX2.fontUI, width: '100%',
              }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <div style={{ flex: 1, fontSize: 13.5, fontWeight: 600, color: KX2.ink }}>{d.label}</div>
                  <span style={{
                    fontFamily: KX2.fontMono, fontSize: 10, fontWeight: 600,
                    color: d.auto ? KX2.approve : KX2.warn, letterSpacing: 0.4,
                  }}>{d.auto ? 'AUTO' : 'DRAFTS'}</span>
                  <span style={{
                    fontFamily: KX2.fontMono, fontSize: 12, color: KX2.ink2,
                    fontVariantNumeric: 'tabular-nums', minWidth: 32, textAlign: 'right',
                  }}>{d.pct}%</span>
                </div>
                <div style={{
                  marginTop: 6, height: 3, borderRadius: 2,
                  background: 'rgba(255,255,255,.08)', overflow: 'hidden',
                }}>
                  <div style={{ width: `${d.pct}%`, height: '100%', background: KX2.brand }} />
                </div>
                <div style={{ fontSize: 11, color: KX2.ink3, marginTop: 6, lineHeight: 1.4 }}>{d.did}</div>
              </button>
            ))}
          </div>
        </div>

        <button onClick={onTeach} style={{
          marginTop: 18, width: '100%', padding: '11px 12px',
          borderRadius: 12, background: KX2.brand, color: '#fff',
          fontSize: 14, fontWeight: 600, fontFamily: KX2.fontUI, border: 'none',
          cursor: 'pointer', boxShadow: '0 6px 18px rgba(124,58,237,.4)',
        }}>+ Teach Kayser a new automation</button>
      </div>
    </KxV2Sheet>
  );
}

// ============ STATUS PEEK SHEET ============
function KxV2StatusSheet({ open, onClose, watching, handledToday, onPick }) {
  const items = [
    { kind: 'watch', label: '3 invoices · cashflow', meta: '$11,200 outstanding',
      ask: "Show me the cashflow plan for those 3 invoices." },
    { kind: 'watch', label: 'Williams estimate · 2 days quiet', meta: 'Will follow up tomorrow AM',
      ask: "What's the Williams estimate situation?" },
    { kind: 'watch', label: 'Patel weather window', meta: 'Re-checking forecast hourly',
      ask: "When does the Patel weather window open?" },
    { kind: 'done',  label: 'Rescheduled Patel · Wed → Thu', meta: '6 min ago',
      ask: "Why did you reschedule Patel?" },
    { kind: 'done',  label: 'Sent reminder · Garcia · #1031', meta: '14 min ago · you approved',
      ask: "Show me the Garcia reminder you sent." },
    { kind: 'done',  label: 'Logged Sherwin Williams receipt', meta: '1 hr ago · matched job 1822',
      ask: "What did you log for the Sherwin Williams receipt?" },
  ];
  return (
    <KxV2Sheet open={open} onClose={onClose} title="What I'm on" peek>
      <div style={{ padding: '8px 14px 18px' }}>
        <div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
          <div style={{ ...kx2GlassThin({ borderRadius: 10 }), flex: 1, padding: '8px 10px' }}>
            <div style={{ fontSize: 10, color: KX2.ink3, fontFamily: KX2.fontMono, letterSpacing: 0.5 }}>WATCHING</div>
            <div style={{ fontSize: 22, color: KX2.ink, fontFamily: KX2.fontMono, fontWeight: 700, marginTop: 2 }}>{watching}</div>
          </div>
          <div style={{ ...kx2GlassThin({ borderRadius: 10 }), flex: 1, padding: '8px 10px' }}>
            <div style={{ fontSize: 10, color: KX2.ink3, fontFamily: KX2.fontMono, letterSpacing: 0.5 }}>HANDLED TODAY</div>
            <div style={{ fontSize: 22, color: KX2.approve, fontFamily: KX2.fontMono, fontWeight: 700, marginTop: 2 }}>{handledToday}</div>
          </div>
        </div>
        <div style={{ display: 'grid', gap: 6 }}>
          {items.map((it, i) => (
            <button key={i} onClick={() => onPick?.(it.ask)} style={{
              ...kx2GlassThin({ borderRadius: 10 }),
              padding: '8px 12px', display: 'flex', alignItems: 'flex-start', gap: 10,
              cursor: 'pointer', textAlign: 'left', color: KX2.ink, fontFamily: KX2.fontUI, width: '100%',
            }}>
              <div style={{
                width: 6, height: 6, borderRadius: 3, marginTop: 6, flexShrink: 0,
                background: it.kind === 'watch' ? KX2.warn : KX2.approve,
              }} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: KX2.ink, lineHeight: 1.3 }}>{it.label}</div>
                <div style={{ fontSize: 11, color: KX2.ink3, marginTop: 1 }}>{it.meta}</div>
              </div>
              <span style={{ color: KX2.ink3, fontFamily: KX2.fontMono, fontSize: 11, marginTop: 2 }}>›</span>
            </button>
          ))}
        </div>
      </div>
    </KxV2Sheet>
  );
}

Object.assign(window, { KxV2Composer, KxV2Sheet, KxV2Palette, KxV2TrustSheet, KxV2StatusSheet });
