// cards-panel.jsx — Full-page CardsScreen, MyMind-style masonry grid.
// Exports window.CardsScreen.

const { useState: _qp_us, useEffect: _qp_ue, useRef: _qp_ur } = React;

const CARD_TYPES = [
  { type: "note",  label: "Note",  color: "var(--fg-2)" },
  { type: "image", label: "Image", color: "var(--c-pattern)" },
  { type: "quote", label: "Quote", color: "var(--c-perspective)" },
  { type: "link",  label: "Link",  color: "var(--c-function)" },
  { type: "video", label: "Video", color: "var(--c-essence)" },
];

const CARD_LEVEL_COLOR = {
  consist:     "var(--c-consist)",
  function:    "var(--c-function)",
  perspective: "var(--c-perspective)",
  pattern:     "var(--c-pattern)",
  essence:     "var(--c-essence)",
};

const CardIcons = {
  Note: (s = 15) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
      <polyline points="14 2 14 8 20 8"/>
      <line x1="16" y1="13" x2="8" y2="13"/>
      <line x1="16" y1="17" x2="8" y2="17"/>
    </svg>
  ),
  Image: (s = 15) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
      <circle cx="8.5" cy="8.5" r="1.5"/>
      <polyline points="21 15 16 10 5 21"/>
    </svg>
  ),
  Quote: (s = 15) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 21c3 0 7-1 7-8V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h2c0 4-2 6-3 7"/>
      <path d="M15 21c3 0 7-1 7-8V5c0-1.1-.9-2-2-2h-4c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h2c0 4-2 6-3 7"/>
    </svg>
  ),
  Link: (s = 15) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      <path d="M10 14a4 4 0 0 0 5.66 0l3-3a4 4 0 0 0-5.66-5.66l-1.4 1.4"/>
      <path d="M14 10a4 4 0 0 0-5.66 0l-3 3a4 4 0 1 0 5.66 5.66l1.4-1.4"/>
    </svg>
  ),
  Video: (s = 15) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      <polygon points="23 7 16 12 23 17 23 7"/>
      <rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
    </svg>
  ),
  Upload: (s = 15) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
      <polyline points="17 8 12 3 7 8"/>
      <line x1="12" y1="3" x2="12" y2="15"/>
    </svg>
  ),
  Edit: (s = 12) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
      <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
    </svg>
  ),
  Trash: (s = 12) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
      <path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-12"/>
    </svg>
  ),
};

function cardIcon(type, s) {
  const k = type.charAt(0).toUpperCase() + type.slice(1);
  return CardIcons[k]?.(s) || null;
}

// Shared field styles used in both Add and Edit forms
function fieldStyle() {
  return {
    width: "100%", display: "block", boxSizing: "border-box",
    background: "var(--bg-elev)", border: "none",
    borderRadius: 10, padding: "10px 14px",
    boxShadow: "0 1px 2px rgba(0,0,0,.04), 0 4px 20px -6px rgba(0,0,0,.08)",
    fontSize: 14, color: "var(--fg)", fontFamily: "var(--font-ui)",
    outline: "none", resize: "vertical",
    transition: "box-shadow 160ms",
  };
}
const LBL = {
  display: "block", marginBottom: 6,
  fontSize: 9.5, fontFamily: "var(--font-mono)", letterSpacing: ".12em",
  textTransform: "uppercase", color: "var(--fg-3)",
};

// ── CardInlineEdit — edits an existing card in-place ────────────

function CardInlineEdit({ card, onSave, onCancel }) {
  const isBase64 = (card.content || "").startsWith("data:");
  const [content, setContent]     = _qp_us(card.content  || "");
  const [source,  setSource]      = _qp_us(card.source   || "");
  const [caption, setCaption]     = _qp_us(card.caption  || "");
  const [imageMode, setImageMode] = _qp_us(isBase64 ? "upload" : "url");
  const fileRef = _qp_ur(null);
  const type = card.type;

  function handleFileUpload(e) {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (evt) => setContent(evt.target.result);
    reader.readAsDataURL(file);
  }

  function toEmbedUrl(url) {
    const m = url.match(/(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/)|youtu\.be\/)([A-Za-z0-9_-]{11})/);
    return m ? `https://www.youtube.com/embed/${m[1]}` : url;
  }

  function canSave() {
    if (type === "image") return !!(content || caption);
    return !!content.trim();
  }

  function handleSubmit() {
    if (!canSave()) return;
    const finalContent = type === "video" ? toEmbedUrl(content.trim()) : content.trim();
    onSave({ content: finalContent, source: source.trim(), caption: caption.trim() });
  }

  function handleKey(e) {
    if (e.key === "Escape") onCancel();
    if ((e.metaKey || e.ctrlKey) && e.key === "Enter") handleSubmit();
  }

  const fs = fieldStyle();

  return (
    <div style={{ padding: "16px 16px 14px" }} onKeyDown={handleKey}>
      <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>

        {type === "note" && (
          <textarea value={content} onChange={e => setContent(e.target.value)}
            rows={5} style={{ ...fs, fontSize: 15, lineHeight: 1.65 }} autoFocus />
        )}

        {type === "image" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <div style={{ display: "flex", gap: 5 }}>
              {["url", "upload"].map(m => (
                <button key={m} className="btn tiny"
                  style={{
                    fontSize: 10.5,
                    background: imageMode === m ? "var(--bg-sunk)" : "transparent",
                    border: `1px solid ${imageMode === m ? "var(--border)" : "var(--border-soft)"}`,
                    color: imageMode === m ? "var(--fg)" : "var(--fg-3)",
                  }}
                  onClick={() => setImageMode(m)}>
                  {m === "url" ? "URL" : "↑ Upload"}
                </button>
              ))}
            </div>
            {imageMode === "url"
              ? <input value={content} onChange={e => setContent(e.target.value)}
                  placeholder="https://…" style={fs} autoFocus />
              : <div>
                  <input ref={fileRef} type="file" accept="image/*"
                    onChange={handleFileUpload} style={{ display: "none" }} />
                  <button className="btn ghost"
                    style={{ width: "100%", justifyContent: "center", padding: "16px 0", borderStyle: "dashed", gap: 7, fontSize: 12 }}
                    onClick={() => fileRef.current?.click()}>
                    {CardIcons.Upload(13)}
                    {content ? "click to replace image" : "choose image file"}
                  </button>
                  {content && <img src={content} alt="" style={{ width: "100%", borderRadius: 8, marginTop: 7, maxHeight: 130, objectFit: "cover", display: "block" }} />}
                </div>
            }
            <div>
              <label style={LBL}>Caption</label>
              <input value={caption} onChange={e => setCaption(e.target.value)}
                placeholder="Optional…" style={fs} />
            </div>
          </div>
        )}

        {type === "quote" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <textarea value={content} onChange={e => setContent(e.target.value)}
              rows={4} placeholder="The exact words…"
              style={{ ...fs, fontFamily: "var(--font-serif)", fontStyle: "italic", fontSize: 16 }} autoFocus />
            <div>
              <label style={LBL}>Source</label>
              <input value={source} onChange={e => setSource(e.target.value)}
                placeholder="Author, book, URL…" style={fs} />
            </div>
          </div>
        )}

        {type === "link" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <div>
              <label style={LBL}>URL</label>
              <input value={content} onChange={e => setContent(e.target.value)}
                placeholder="https://…" style={fs} autoFocus />
            </div>
            <div>
              <label style={LBL}>Label</label>
              <input value={caption} onChange={e => setCaption(e.target.value)}
                placeholder="Optional display text…" style={fs} />
            </div>
          </div>
        )}

        {type === "video" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            <div>
              <label style={LBL}>Video URL</label>
              <input value={content} onChange={e => setContent(e.target.value)}
                placeholder="YouTube URL or embed URL…" style={fs} autoFocus />
            </div>
            <div>
              <label style={LBL}>Caption</label>
              <input value={caption} onChange={e => setCaption(e.target.value)}
                placeholder="Optional…" style={fs} />
            </div>
          </div>
        )}

        <div style={{ display: "flex", gap: 6, justifyContent: "flex-end", paddingTop: 2 }}>
          <span style={{ fontSize: 10, color: "var(--fg-3)", fontFamily: "var(--font-mono)", alignSelf: "center", marginRight: "auto", opacity: 0.7 }}>
            ⌘↵ save
          </span>
          <button className="btn ghost tiny" onClick={onCancel}>Cancel</button>
          <button className="btn tiny" onClick={handleSubmit} disabled={!canSave()}
            style={{
              background: canSave() ? "var(--fg)" : "var(--bg-sunk)",
              color: canSave() ? "var(--bg)" : "var(--fg-3)",
              borderColor: canSave() ? "var(--fg)" : "var(--border-soft)",
              cursor: canSave() ? "pointer" : "not-allowed",
            }}>
            Save
          </button>
        </div>
      </div>
    </div>
  );
}

// ── CardModal — popup overlay for a single card ────────────────

function CardModal({ card, onClose, onUpdate, onDelete, onOpenConcept }) {
  var linkMetaState = _qp_us(null);
  var linkMeta = linkMetaState[0], setLinkMeta = linkMetaState[1];
  var draftCaptionState = _qp_us(card.caption || '');
  var draftCaption = draftCaptionState[0], setDraftCaption = draftCaptionState[1];
  var draftSourceState = _qp_us(card.source || '');
  var draftSource = draftSourceState[0], setDraftSource = draftSourceState[1];
  var draftContentState = _qp_us(card.content || '');
  var draftContent = draftContentState[0], setDraftContent = draftContentState[1];

  _qp_ue(function() {
    function onKey(e) { if (e.key === 'Escape') onClose(); }
    document.addEventListener('keydown', onKey);
    return function() { document.removeEventListener('keydown', onKey); };
  }, [onClose]);

  _qp_ue(function() {
    if (card.type !== 'link' || !card.content) return;
    fetch('https://api.microlink.io?url=' + encodeURIComponent(card.content))
      .then(function(r) { return r.json(); })
      .then(function(data) { if (data.status === 'success') setLinkMeta(data.data); })
      .catch(function() {});
  }, [card.content]);

  var levelColor = CARD_LEVEL_COLOR[card.parent_type] || 'var(--fg-3)';

  function saveCaption() {
    if (draftCaption !== (card.caption || '')) onUpdate(card.id, { caption: draftCaption });
  }
  function saveSource() {
    if (draftSource !== (card.source || '')) onUpdate(card.id, { source: draftSource });
  }
  function saveContent() {
    if (draftContent !== (card.content || '')) onUpdate(card.id, { content: draftContent });
  }

  function renderLeft() {
    if (card.type === 'image' && card.content) {
      return (
        <img src={card.content} alt={card.caption || ''}
          style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 6 }} />
      );
    }
    if (card.type === 'video' && card.content) {
      return (
        <div style={{ width: '100%', position: 'relative', paddingBottom: '56.25%', borderRadius: 10, overflow: 'hidden' }}>
          <iframe src={card.content} title={card.caption || 'video'}
            allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
            allowFullScreen
            style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', border: 0 }} />
        </div>
      );
    }
    if (card.type === 'quote') {
      return (
        <div style={{ padding: '52px 56px', maxWidth: 520 }}>
          <div style={{ fontFamily: 'var(--font-serif)', fontSize: 88, lineHeight: 0.7, color: 'var(--c-perspective)', opacity: 0.12, marginBottom: 16, userSelect: 'none' }}>&ldquo;</div>
          <p style={{ margin: '0 0 22px', fontFamily: 'var(--font-serif)', fontStyle: 'italic', fontSize: 26, lineHeight: 1.55, color: 'var(--fg)' }}>
            {card.content}
          </p>
          {card.source && (
            <p style={{ margin: 0, fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-3)', letterSpacing: '.04em' }}>
              — {card.source}
            </p>
          )}
        </div>
      );
    }
    if (card.type === 'note') {
      return (
        <div style={{ padding: '52px 56px', maxWidth: 520 }}>
          <p style={{ margin: 0, fontSize: 20, lineHeight: 1.72, color: 'var(--fg)', whiteSpace: 'pre-wrap' }}>
            {card.content}
          </p>
        </div>
      );
    }
    if (card.type === 'link') {
      if (linkMeta && linkMeta.image && linkMeta.image.url) {
        return (
          <img src={linkMeta.image.url} alt=''
            style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 6 }} />
        );
      }
      return (
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14, color: 'var(--fg-3)' }}>
          {CardIcons.Link(40)}
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: 12, wordBreak: 'break-all', textAlign: 'center', maxWidth: 300 }}>
            {(function() { try { return new URL(card.content).hostname; } catch(ex) { return card.content; } }())}
          </span>
        </div>
      );
    }
    return null;
  }

  var LBL = { fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '.14em', textTransform: 'uppercase', color: 'var(--fg-3)', marginBottom: 6 };
  var FIELD_WRAP = { marginBottom: 18 };
  var FIELD_BASE = { display: 'block', width: '100%', margin: 0, padding: 0, background: 'transparent', border: 'none', outline: 'none', fontFamily: 'inherit', cursor: 'text', color: 'var(--fg)', lineHeight: 1.55 };

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 300,
      background: 'rgba(0,0,0,0.45)',
      backdropFilter: 'blur(16px)',
      WebkitBackdropFilter: 'blur(16px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 32,
    }}>
      <div onClick={function(e) { e.stopPropagation(); }} className='fade-up' style={{
        display: 'flex',
        width: '100%', maxWidth: 960,
        height: '80vh', maxHeight: 680,
        borderRadius: 18, overflow: 'hidden',
        background: 'var(--bg-sunk)',
        boxShadow: '0 24px 80px rgba(0,0,0,0.3)',
      }}>

        {/* Left: main content */}
        <div style={{
          flex: '1 1 60%',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          overflow: 'hidden',
          padding: 16,
        }}>
          {renderLeft()}
        </div>

        {/* Right: form card floating inside the main card */}
        <div style={{
          flex: '0 0 280px', minWidth: 0,
          padding: 10,
          display: 'flex',
        }}>
        <div style={{
          flex: 1,
          background: 'var(--bg-elev)',
          borderRadius: 12,
          display: 'flex', flexDirection: 'column',
          overflow: 'hidden',
          boxShadow: '0 2px 16px rgba(0,0,0,0.12)',
        }}>
          <div style={{ flex: 1, overflowY: 'auto', padding: '24px 20px' }}>

            <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 26 }}>
              <span style={{ width: 7, height: 7, borderRadius: 999, background: levelColor, flexShrink: 0 }} />
              <span style={{ fontFamily: 'var(--font-mono)', fontSize: 9.5, letterSpacing: '.14em', textTransform: 'uppercase', color: 'var(--fg-3)' }}>{card.parent_type}</span>
              <span style={{ opacity: 0.3 }}>·</span>
              <span style={{ fontFamily: 'var(--font-mono)', fontSize: 9.5, letterSpacing: '.14em', textTransform: 'uppercase', color: 'var(--fg-3)' }}>{card.type}</span>
            </div>

            {/* Content — editable main body */}
            <div style={FIELD_WRAP}>
              <div style={LBL}>{card.type === 'link' || card.type === 'image' || card.type === 'video' ? 'URL' : 'Content'}</div>
              {(card.type === 'note' || card.type === 'quote') ? (
                <textarea
                  value={draftContent}
                  onChange={function(e) { setDraftContent(e.target.value); }}
                  onBlur={saveContent}
                  placeholder={'Add ' + card.type + '…'}
                  rows={5}
                  style={{ ...FIELD_BASE, fontSize: 14, resize: 'none' }}
                />
              ) : (
                <input
                  type='text'
                  value={draftContent}
                  onChange={function(e) { setDraftContent(e.target.value); }}
                  onBlur={saveContent}
                  placeholder='https://…'
                  style={{ ...FIELD_BASE, fontSize: 12, fontFamily: 'var(--font-mono)', color: 'var(--fg-2)' }}
                />
              )}
            </div>

            {(card.type === 'image' || card.type === 'video' || card.type === 'link') && (
              <div style={FIELD_WRAP}>
                <div style={LBL}>Caption</div>
                <textarea
                  value={draftCaption}
                  onChange={function(e) { setDraftCaption(e.target.value); }}
                  onBlur={saveCaption}
                  placeholder='Add caption…'
                  rows={3}
                  style={{ ...FIELD_BASE, fontSize: 14, resize: 'none' }}
                />
              </div>
            )}

            {card.type === 'quote' && (
              <div style={FIELD_WRAP}>
                <div style={LBL}>Source</div>
                <input
                  type='text'
                  value={draftSource}
                  onChange={function(e) { setDraftSource(e.target.value); }}
                  onBlur={saveSource}
                  placeholder='Add source…'
                  style={{ ...FIELD_BASE, fontSize: 12.5, color: 'var(--fg-2)', fontFamily: 'var(--font-mono)' }}
                />
              </div>
            )}

            {card.type === 'link' && draftContent && (
              <div style={{ paddingLeft: 2, marginTop: -4, marginBottom: 10 }}>
                <a href={draftContent} target='_blank' rel='noopener noreferrer'
                  onClick={function(e) { e.stopPropagation(); }}
                  style={{ fontSize: 10.5, color: 'var(--c-function)', fontFamily: 'var(--font-mono)', textDecoration: 'none', opacity: 0.8 }}>
                  open →
                </a>
              </div>
            )}

            {card.type === 'link' && linkMeta && linkMeta.title && (
              <div style={FIELD_WRAP}>
                <div style={LBL}>Title</div>
                <p style={{ margin: 0, fontSize: 14, color: 'var(--fg)', lineHeight: 1.4, fontWeight: 500 }}>{linkMeta.title}</p>
              </div>
            )}

            {card.type === 'link' && linkMeta && linkMeta.description && (
              <div style={FIELD_WRAP}>
                <div style={LBL}>Description</div>
                <p style={{ margin: 0, fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.55 }}>{linkMeta.description}</p>
              </div>
            )}

          </div>

          <div style={{ padding: '13px 14px', display: 'flex', alignItems: 'center', gap: 6 }}>
            {onOpenConcept && (
              <button className='btn ghost tiny' onClick={function() { onOpenConcept(card.concept_id, 'edit', card.parent_type); onClose(); }}
                style={{ gap: 5, fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.04em' }}>
                Concept →
              </button>
            )}
            <div style={{ flex: 1 }} />
            <button className='btn ghost tiny' onClick={function() { onDelete(card.id); onClose(); }} style={{ gap: 5, color: 'var(--c-essence)' }}>
              {CardIcons.Trash(11)}
            </button>
            <button className='btn ghost tiny' onClick={onClose} style={{ color: 'var(--fg-3)' }}>✕</button>
          </div>
        </div>
        </div>

      </div>
    </div>
  );
}

function VideoCard({ embedUrl, thumb, caption }) {
  var playingState = _qp_us(false);
  var playing = playingState[0], setPlaying = playingState[1];
  var hovState = _qp_us(false);
  var hov = hovState[0], setHov = hovState[1];
  return (
    <div style={{ position: 'relative', paddingBottom: '56.25%', background: '#000' }}
      onMouseEnter={function() { setHov(true); }}
      onMouseLeave={function() { setHov(false); }}>
      {playing ? (
        <iframe src={embedUrl + '?autoplay=1'} title={caption || 'video'}
          allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'
          allowFullScreen
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', border: 0 }} />
      ) : (
        <>
          {thumb
            ? <img src={thumb} alt='' style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
            : <div style={{ position: 'absolute', inset: 0, background: 'var(--bg)' }} />
          }
          <div style={{
            position: 'absolute', bottom: 0, left: 0, right: 0, height: '60%',
            background: 'linear-gradient(to top, rgba(0,0,0,0.72) 0%, transparent 100%)',
            opacity: hov ? 1 : 0,
            transition: 'opacity 200ms ease',
            pointerEvents: 'none',
          }} />
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <div onClick={function(e) { e.stopPropagation(); setPlaying(true); }}
              style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
                width: 48, height: 48, borderRadius: 999,
                background: 'rgba(0,0,0,0.45)', backdropFilter: 'blur(4px)',
                transition: 'transform 150ms ease, background 150ms ease',
              }}
              onMouseEnter={function(e) { e.currentTarget.style.transform = 'scale(1.1)'; e.currentTarget.style.background = 'rgba(0,0,0,0.65)'; }}
              onMouseLeave={function(e) { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.background = 'rgba(0,0,0,0.45)'; }}>
              <svg width='18' height='18' viewBox='0 0 24 24' fill='rgba(255,255,255,0.9)' style={{ marginLeft: 2 }}>
                <polygon points='5,3 19,12 5,21' />
              </svg>
            </div>
          </div>
        </>
      )}
    </div>
  );
}


// ── CardCard ─────────────────────────────────────────────────────

function CardCard({ card, onDelete, onUpdate, isDragging, onDragStart, onDragOver, onDragEnd, animDelay, conceptInfo, onOpen }) {
  const [editing, setEditing] = _qp_us(false);
  const [linkMeta, setLinkMeta] = _qp_us(null);
  const info = CARD_TYPES.find(t => t.type === card.type);

  _qp_ue(function() {
    if (card.type !== 'link' || !card.content) return;
    var url = card.content;
    fetch('https://api.microlink.io?url=' + encodeURIComponent(url))
      .then(function(r) { return r.json(); })
      .then(function(data) { if (data.status === 'success') setLinkMeta(data.data); })
      .catch(function() {});
  }, [card.content]);

  function handleSave(changes) {
    onUpdate(card.id, changes);
    setEditing(false);
  }

  return (
    <div
      className={`qk-card fade-up${editing ? " qk-editing" : ""}${isDragging ? " qk-dragging" : ""}`}
      style={{ animationDelay: animDelay ? `${animDelay}ms` : "0ms", cursor: onOpen && !editing ? "pointer" : "default" }}
      onClick={() => !editing && onOpen && onOpen(card)}
      draggable={!editing && !!onDragStart}
      onDragStart={(e) => {
        e.dataTransfer.effectAllowed = "move";
        e.dataTransfer.setData("text/plain", card.id);
        onDragStart && onDragStart();
      }}
      onDragOver={(e) => {
        e.preventDefault();
        e.stopPropagation();
        const rect = e.currentTarget.getBoundingClientRect();
        const isTopHalf = e.clientY < rect.top + rect.height / 2;
        onDragOver && onDragOver(card.id, isTopHalf);
      }}
      onDragEnd={() => { onDragEnd && onDragEnd(); }}
    >

      {/* ── Edit mode ── */}
      {editing && (
        <CardInlineEdit
          card={card}
          onSave={handleSave}
          onCancel={() => setEditing(false)}
        />
      )}

      {/* ── View: NOTE ── */}
      {!editing && card.type === "note" && (
        <div style={{ padding: "20px 20px 34px" }}>
          <p style={{ margin: 0, fontSize: 14.5, lineHeight: 1.72, color: "var(--fg)", whiteSpace: "pre-wrap" }}>
            {card.content}
          </p>
        </div>
      )}

      {/* ── View: IMAGE ── */}
      {!editing && card.type === "image" && (
        <div>
          {card.content
            ? <img src={card.content} alt={card.caption || ""}
                style={{ width: "100%", display: "block", maxHeight: 420, objectFit: "cover" }} />
            : <window.StripePlaceholder label="image" h={180} />
          }
        </div>
      )}

      {/* ── View: QUOTE ── */}
      {!editing && card.type === "quote" && (
        <div style={{ padding: "22px 20px 34px" }}>
          <p style={{ margin: "0 0 16px", fontFamily: "var(--font-serif)", fontStyle: "italic", fontSize: 17.5, lineHeight: 1.65, color: "var(--fg)" }}>
            {card.content}
          </p>
          {card.source && (
            <span style={{ fontSize: 11.5, color: "var(--fg-3)", fontFamily: "var(--font-mono)", letterSpacing: ".04em" }}>
              — {card.source}
            </span>
          )}
        </div>
      )}

      {/* ── View: LINK ── */}
      {!editing && card.type === 'link' && (
        <div>
          {linkMeta && linkMeta.image && linkMeta.image.url && (
            <img src={linkMeta.image.url} alt=""
              style={{ width: '100%', display: 'block', maxHeight: 180, objectFit: 'cover' }} />
          )}
          <div style={{ padding: '14px 18px 34px' }}>
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 9 }}>
              <img
                src={'https://www.google.com/s2/favicons?domain=' + (function() { try { return new URL(card.content).hostname; } catch(ex) { return ''; } }()) + '&sz=32'}
                alt="" onError={function(e) { e.target.style.display = 'none'; }}
                style={{ width: 15, height: 15, borderRadius: 3, flexShrink: 0, marginTop: 3, objectFit: 'contain' }}
              />
              <div style={{ minWidth: 0, flex: 1 }}>
                <a href={card.content} target="_blank" rel="noopener noreferrer"
                  onClick={function(e) { e.stopPropagation(); }}
                  style={{ display: 'block', fontSize: 14, fontWeight: 500, color: 'var(--fg)', textDecoration: 'none', lineHeight: 1.35, marginBottom: 3, wordBreak: 'break-word' }}>
                  {card.caption || (linkMeta && linkMeta.title) || card.content}
                </a>

              </div>
            </div>
          </div>
        </div>
      )}

      {/* ── View: VIDEO ── */}
      {!editing && card.type === 'video' && (function() {
        var embedUrl = card.content || '';
        var vidId = '';
        var m = embedUrl.match(/\/embed\/([A-Za-z0-9_-]{11})/);
        if (m) vidId = m[1];
        var thumb = vidId ? 'https://img.youtube.com/vi/' + vidId + '/hqdefault.jpg' : '';
        return (
          <VideoCard embedUrl={embedUrl} thumb={thumb} caption={card.caption} />
        );
      })()}

      {/* ── Hover overlay (image only) ── */}
      {!editing && card.type === "image" && (
        <div className="qk-overlay-image">
          {card.caption && <div style={{ fontSize: 12, fontStyle: "italic", lineHeight: 1.4, color: "rgba(255,255,255,0.9)", marginBottom: 5 }}>{card.caption}</div>}
          <div style={{ display: "flex", alignItems: "center", gap: 5, fontFamily: "var(--font-mono)", fontSize: 9, letterSpacing: ".1em", textTransform: "uppercase", color: "rgba(255,255,255,0.6)", flexWrap: "wrap" }}>
            {conceptInfo && (
              <>
                <span>{conceptInfo}</span>
                <span style={{ opacity: 0.4 }}>·</span>
              </>
            )}
            <span>{card.type}</span>
          </div>
        </div>
      )}

      {/* ── Info (non-image cards) ── */}
      {!editing && card.type !== "image" && (
        <div className="qk-info">
          <div style={{ display: "flex", alignItems: "center", gap: 5, fontFamily: "var(--font-mono)", fontSize: 9, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--fg-3)", flexWrap: "wrap" }}>
            {conceptInfo && (
              <>
                <span>{conceptInfo}</span>
                <span style={{ opacity: 0.4 }}>·</span>
              </>
            )}
            <span>{card.type}</span>
          </div>
        </div>
      )}
    </div>
  );
}

// ── AddCardForm ──────────────────────────────────────────────────

function AddCardForm({ type, onSubmit, onCancel }) {
  const [content, setContent]     = _qp_us("");
  const [source, setSource]       = _qp_us("");
  const [caption, setCaption]     = _qp_us("");
  const [imageMode, setImageMode] = _qp_us("url");
  const fileRef = _qp_ur(null);

  const info = CARD_TYPES.find(t => t.type === type);

  function handleFileUpload(e) {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (evt) => setContent(evt.target.result);
    reader.readAsDataURL(file);
  }

  function toEmbedUrl(url) {
    const m = url.match(/(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/)|youtu\.be\/)([A-Za-z0-9_-]{11})/);
    return m ? `https://www.youtube.com/embed/${m[1]}` : url;
  }

  function canSubmit() {
    if (type === "image") return !!(content || caption);
    return !!content.trim();
  }

  function handleSubmit() {
    if (!canSubmit()) return;
    const finalContent = type === "video" ? toEmbedUrl(content.trim()) : content.trim();
    onSubmit({ type, content: finalContent, source: source.trim(), caption: caption.trim() });
  }

  function handleKey(e) {
    if (e.key === "Escape") onCancel();
    if ((e.metaKey || e.ctrlKey) && e.key === "Enter") handleSubmit();
  }

  const fs = fieldStyle();

  return (
    <div className="surface fade-up"
      style={{ padding: "20px 22px", marginBottom: 24, borderRadius: 16 }}
      onKeyDown={handleKey}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 16 }}>
        <span style={{ color: info?.color }}>{cardIcon(type, 14)}</span>
        <span style={{ fontSize: 11, fontFamily: "var(--font-mono)", letterSpacing: ".12em", textTransform: "uppercase", color: "var(--fg-3)", flex: 1 }}>
          Add {type}
        </span>
        <span style={{ fontSize: 10, color: "var(--fg-3)", fontFamily: "var(--font-mono)", opacity: 0.7 }}>⌘↵ save</span>
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {type === "note" && (
          <textarea value={content} onChange={e => setContent(e.target.value)}
            placeholder="Write your note…" rows={5}
            style={{ ...fs, fontSize: 15, lineHeight: 1.65 }} autoFocus />
        )}

        {type === "image" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <div style={{ display: "flex", gap: 6 }}>
              {["url", "upload"].map(m => (
                <button key={m} className="btn tiny"
                  style={{
                    fontSize: 11,
                    background: imageMode === m ? "var(--bg-sunk)" : "transparent",
                    border: `1px solid ${imageMode === m ? "var(--border)" : "var(--border-soft)"}`,
                    color: imageMode === m ? "var(--fg)" : "var(--fg-3)",
                  }}
                  onClick={() => setImageMode(m)}>
                  {m === "url" ? "URL" : "↑ Upload"}
                </button>
              ))}
            </div>
            {imageMode === "url"
              ? <input value={content} onChange={e => setContent(e.target.value)}
                  placeholder="https://…" style={fs} autoFocus />
              : <div>
                  <input ref={fileRef} type="file" accept="image/*"
                    onChange={handleFileUpload} style={{ display: "none" }} />
                  <button className="btn ghost"
                    style={{ width: "100%", justifyContent: "center", padding: "24px 0", borderStyle: "dashed", gap: 8 }}
                    onClick={() => fileRef.current?.click()}>
                    {CardIcons.Upload(14)}
                    {content ? "image loaded — click to replace" : "choose image file"}
                  </button>
                  {content && <img src={content} alt="" style={{ width: "100%", borderRadius: 8, marginTop: 8, maxHeight: 140, objectFit: "cover", display: "block" }} />}
                </div>
            }
            <div>
              <label style={LBL}>Caption</label>
              <input value={caption} onChange={e => setCaption(e.target.value)}
                placeholder="Optional…" style={fs} />
            </div>
          </div>
        )}

        {type === "quote" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <textarea value={content} onChange={e => setContent(e.target.value)}
              placeholder="The exact words…" rows={4}
              style={{ ...fs, fontFamily: "var(--font-serif)", fontStyle: "italic", fontSize: 16 }} autoFocus />
            <div>
              <label style={LBL}>Source</label>
              <input value={source} onChange={e => setSource(e.target.value)}
                placeholder="Author, book, URL…" style={fs} />
            </div>
          </div>
        )}

        {type === "link" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <input value={content} onChange={e => setContent(e.target.value)}
              placeholder="https://…" style={fs} autoFocus />
            <div>
              <label style={LBL}>Label</label>
              <input value={caption} onChange={e => setCaption(e.target.value)}
                placeholder="Optional display text…" style={fs} />
            </div>
          </div>
        )}

        {type === "video" && (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <input value={content} onChange={e => setContent(e.target.value)}
              placeholder="YouTube URL or embed URL…" style={fs} autoFocus />
            <span style={{ fontSize: 11, color: "var(--fg-3)", fontFamily: "var(--font-mono)", marginTop: -4 }}>
              YouTube links auto-convert to embeds.
            </span>
            <div>
              <label style={LBL}>Caption</label>
              <input value={caption} onChange={e => setCaption(e.target.value)}
                placeholder="Optional…" style={fs} />
            </div>
          </div>
        )}

        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", paddingTop: 2 }}>
          <button className="btn ghost" onClick={onCancel}>Cancel</button>
          <button className="btn" onClick={handleSubmit} disabled={!canSubmit()}
            style={{
              background: canSubmit() ? "var(--fg)" : "var(--bg-sunk)",
              color: canSubmit() ? "var(--bg)" : "var(--fg-3)",
              borderColor: canSubmit() ? "var(--fg)" : "var(--border-soft)",
              cursor: canSubmit() ? "pointer" : "not-allowed",
            }}>
            Add {type}
          </button>
        </div>
      </div>
    </div>
  );
}

// ── CardsScreen — full page, masonry ────────────────────────────

function CardsScreen({ conceptId, parentType, parentId, label, conceptTitle, onBack, onOpenConcept }) {
  const [cards, setCards]           = _qp_us([]);
  const [adding, setAdding]           = _qp_us(null);
  const [draggedId, setDraggedId]     = _qp_us(null);
  const [dropBeforeId, setDropBeforeId] = _qp_us(null);
  const [selectedCardId, setSelectedCardId] = _qp_us(null);
  const selectedCard = selectedCardId ? cards.find(q => q.id === selectedCardId) : null;

  _qp_ue(() => {
    if (window.cardsService) {
      window.cardsService.getCards(parentType, parentId).then(rows => {
        setCards(rows.slice().sort((a, b) => {
          if (a.sort_order != null && b.sort_order != null) return a.sort_order - b.sort_order;
          if (a.sort_order != null) return -1;
          if (b.sort_order != null) return 1;
          return a.created_at < b.created_at ? -1 : 1;
        }));
      });
    }
    setAdding(null);
  }, [parentType, parentId]);

  async function handleAdd(card) {
    const now = new Date().toISOString();
    const newCard = {
      id:          "qk" + Date.now() + Math.random().toString(36).slice(2, 6),
      parent_type: parentType,
      parent_id:   parentId,
      concept_id:  conceptId,
      sort_order:  (cards.length + 1) * 1000,
      created_at:  now,
      updated_at:  now,
      ...card,
    };
    await window.cardsService.addCard(newCard);
    setCards(qs => [...qs, newCard]);
    setAdding(null);
  }

  async function handleUpdate(id, changes) {
    await window.cardsService.updateCard(id, changes);
    setCards(qs => qs.map(q => q.id === id
      ? { ...q, ...changes, updated_at: new Date().toISOString() }
      : q
    ));
  }

  async function handleDelete(id) {
    await window.cardsService.deleteCard(id);
    setCards(qs => qs.filter(q => q.id !== id));
  }

  function handleDragStart(id) {
    setDraggedId(id);
    setDropBeforeId(null);
  }

  function handleDragOver(targetId, isTopHalf) {
    if (targetId === draggedId) return;
    if (isTopHalf) {
      setDropBeforeId(targetId);
    } else {
      const idx = cards.findIndex(q => q.id === targetId);
      const next = cards[idx + 1];
      setDropBeforeId(next ? next.id : null);
    }
  }

  function handleDragEnd() {
    setDraggedId(null);
    setDropBeforeId(null);
  }

  async function handleDrop() {
    if (!draggedId) { handleDragEnd(); return; }
    const dragged = cards.find(q => q.id === draggedId);
    if (!dragged) { handleDragEnd(); return; }

    const rest = cards.filter(q => q.id !== draggedId);
    let newOrder;
    if (dropBeforeId === null) {
      newOrder = [...rest, dragged];
    } else {
      const insertIdx = rest.findIndex(q => q.id === dropBeforeId);
      newOrder = insertIdx === -1
        ? [...rest, dragged]
        : [...rest.slice(0, insertIdx), dragged, ...rest.slice(insertIdx)];
    }

    const updated = newOrder.map((q, i) => ({ ...q, sort_order: (i + 1) * 1000 }));
    setCards(updated);
    for (const q of updated) {
      await window.cardsService.updateCard(q.id, { sort_order: q.sort_order });
    }
    setDraggedId(null);
    setDropBeforeId(null);
  }

  const levelColor = CARD_LEVEL_COLOR[parentType] || "var(--fg-2)";

  return (
    <div className="page" data-screen-label="Cards" style={{ maxWidth: 1080 }}>

      {/* Toolbar */}
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 40 }}>
        <button className="btn ghost tiny" onClick={onBack} style={{ gap: 6 }}>
          {window.Icons.ArrowL(13)} {conceptTitle || "Concept"}
        </button>
        <div style={{ flex: 1 }} />
        {cards.length > 0 && (
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 10.5, letterSpacing: ".08em", color: "var(--fg-3)" }}>
            {cards.length} card{cards.length !== 1 ? "s" : ""}
          </span>
        )}
      </div>

      {/* Hero */}
      <div style={{ marginBottom: 36 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
          <span style={{ width: 8, height: 8, borderRadius: 999, background: levelColor, flexShrink: 0 }} />
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: ".16em", textTransform: "uppercase", color: "var(--fg-3)" }}>
            {parentType}
          </span>
        </div>
        {label && (
          <h1 className="concept-title" style={{ fontSize: 48, margin: 0, lineHeight: 1.05 }}>
            {label}
          </h1>
        )}
      </div>

      {/* Add type buttons */}
      <div style={{ display: "flex", gap: 8, marginBottom: 28 }}>
        {CARD_TYPES.map(qt => {
          const active = adding === qt.type;
          return (
            <button key={qt.type}
              onClick={() => setAdding(active ? null : qt.type)}
              style={{
                flex: 1,
                display: "flex", flexDirection: "column", alignItems: "center", gap: 7,
                padding: "14px 8px 12px",
                borderRadius: 12,
                background: active ? qt.color : "var(--bg-elev)",
                boxShadow: active ? "none" : "0 1px 2px rgba(0,0,0,.04), 0 4px 20px -6px rgba(0,0,0,.08)",
                color: active ? "rgba(0,0,0,0.75)" : "var(--fg-3)",
                cursor: "pointer",
                transition: "background 150ms, box-shadow 150ms",
              }}>
              <span style={{ color: active ? "rgba(0,0,0,0.7)" : qt.color, display: "flex" }}>{cardIcon(qt.type, 20)}</span>
              <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: ".08em", textTransform: "uppercase" }}>{qt.label}</span>
            </button>
          );
        })}
      </div>

      {/* Add form */}
      {adding && (
        <AddCardForm type={adding} onSubmit={handleAdd} onCancel={() => setAdding(null)} />
      )}

      {/* Empty state */}
      {cards.length === 0 && !adding && (
        <div style={{
          padding: "100px 0", textAlign: "center",
          display: "flex", flexDirection: "column", alignItems: "center", gap: 14,
          color: "var(--fg-3)",
        }}>
          <svg width="52" height="52" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="0.9" style={{ opacity: 0.15 }}>
            <circle cx="12" cy="5.5" r="3.5"/>
            <circle cx="5"  cy="18" r="3.5"/>
            <circle cx="19" cy="18" r="3.5"/>
          </svg>
          <p style={{ margin: 0, fontFamily: "var(--font-serif)", fontStyle: "italic", fontSize: 17 }}>
            Nothing here yet.
          </p>
          <p style={{ margin: 0, fontSize: 13 }}>
            Attach a note, image, quote, link, or video to this {parentType}.
          </p>
        </div>
      )}

      {/* Masonry grid */}
      {cards.length > 0 && (() => {
        const items = [];
        cards.forEach((q, i) => {
          if (draggedId && dropBeforeId === q.id && draggedId !== q.id) {
            items.push(<div key="__drop_ph__" className="qk-drop-placeholder" />);
          }
          items.push(
            <CardCard
              key={q.id}
              card={q}
              isDragging={draggedId === q.id}
              onDragStart={() => handleDragStart(q.id)}
              onDragOver={handleDragOver}
              onDragEnd={handleDragEnd}
              onDelete={handleDelete}
              onUpdate={handleUpdate}
              animDelay={draggedId ? 0 : i * 35}
              onOpen={(q) => setSelectedCardId(q.id)}
            />
          );
        });
        if (draggedId && dropBeforeId === null) {
          items.push(<div key="__drop_ph__" className="qk-drop-placeholder" />);
        }
        return (
          <div className="qk-grid"
            onDragOver={(e) => e.preventDefault()}
            onDrop={(e) => { e.preventDefault(); handleDrop(); }}>
            {items}
          </div>
        );
      })()}

      <div style={{ height: 80 }} />

      {selectedCard && (
        <CardModal
          card={selectedCard}
          onClose={() => setSelectedCardId(null)}
          onUpdate={handleUpdate}
          onDelete={(id) => { handleDelete(id); setSelectedCardId(null); }}
          onOpenConcept={onOpenConcept}
        />
      )}
    </div>
  );
}

// ── CardsHubScreen — full-screen interactive card feed ─────────

function _QhSlider({ label, value, min, max, step, onChange }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
      <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--fg-3)", fontFamily: "var(--font-mono)", letterSpacing: ".04em" }}>
        <span>{label}</span><span>{value}</span>
      </div>
      <input type="range" min={min} max={max} step={step} value={value}
        onChange={e => onChange(Number(e.target.value))}
        style={{ width: "100%", cursor: "pointer" }} />
    </div>
  );
}

function CardsHubScreen({ concepts, initialConceptId, onBack, onOpenConcept, onOpenSolar, onOpenGraph, onOpenGroup }) {
  const _qs = (() => { try { return JSON.parse(localStorage.getItem('disene_qhub_settings')) || {}; } catch { return {}; } })();

  const [allCards, setAllCards]         = _qp_us([]);
  const [conceptFilter, setConceptFilter] = _qp_us(() => {
    if (!initialConceptId) return null;
    const c = concepts.find(c => c.id === initialConceptId);
    return c ? c.title : null;
  });
  const [query, setQuery]                 = _qp_us(() => {
    if (!initialConceptId) return '';
    const c = concepts.find(c => c.id === initialConceptId);
    return c ? c.title : '';
  });
  const [dropOpen, setDropOpen]           = _qp_us(false);
  const [catFilter, setCatFilter]         = _qp_us(null);
  const [typeFilter, setTypeFilter]       = _qp_us(null);
  const [cols, setCols]                   = _qp_us(_qs.cols ?? 3);
  const [showSettings, setShowSettings]   = _qp_us(false);
  const [selectedCardId, setSelectedCardId] = _qp_us(null);
  const selectedCard = selectedCardId ? allCards.find(q => q.id === selectedCardId) : null;

  _qp_ue(() => {
    localStorage.setItem('disene_qhub_settings', JSON.stringify({ cols }));
  }, [cols]);

  _qp_ue(() => {
    if (!window.cardsService) return;
    window.cardsService.getAllCards().then(rows => {
      const s = rows.slice();
      for (let i = s.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [s[i], s[j]] = [s[j], s[i]];
      }
      setAllCards(s);
    });
  }, []);

  const conceptMap = {};
  concepts.forEach(c => { conceptMap[c.id] = c; });

  const allCategories = [...new Set(concepts.map(c => c.category).filter(Boolean))].sort();
  const q = query.trim().toLowerCase();
  const conceptSuggestions = q ? concepts.filter(c => c.title.toLowerCase().includes(q)).slice(0, 7) : [];
  const catSuggestions     = q ? allCategories.filter(cat => cat.toLowerCase().includes(q)).slice(0, 7) : [];

  const activeConceptId  = conceptFilter ? (concepts.find(c => c.title === conceptFilter) || {}).id : null;
  const selectedConcept  = activeConceptId ? concepts.find(c => c.id === activeConceptId) : null;

  const filtered = allCards.filter(qk => {
    const c = conceptMap[qk.concept_id];
    if (conceptFilter && (!c || c.title !== conceptFilter))           return false;
    if (catFilter    && (!c || c.category !== catFilter))             return false;
    if (typeFilter   && qk.type !== typeFilter)                       return false;
    return true;
  });

  const hasFilter = !!(conceptFilter || catFilter);
  const showDrop  = dropOpen && q && (conceptSuggestions.length > 0 || catSuggestions.length > 0);

  async function handleUpdate(id, changes) {
    await window.cardsService.updateCard(id, changes);
    setAllCards(qs => qs.map(qk => qk.id === id ? { ...qk, ...changes, updated_at: new Date().toISOString() } : qk));
  }
  async function handleDelete(id) {
    await window.cardsService.deleteCard(id);
    setAllCards(qs => qs.filter(qk => qk.id !== id));
  }

  function pickConcept(c) { setConceptFilter(c.title); setQuery(c.title); setDropOpen(false); }
  function pickCategory(cat) { setCatFilter(cat); setQuery(cat); setDropOpen(false); }
  function clearFilters() { setConceptFilter(null); setCatFilter(null); setQuery(''); }

  const navDim    = { border: 0, background: 'transparent', color: 'var(--fg-3)', opacity: selectedConcept ? 1 : 0.35 };
  const navActive = { border: 0, background: 'var(--c-nucleus)', boxShadow: '0 1px 3px rgba(0,0,0,.15)', color: 'rgba(0,0,0,0.8)' };

  return (
    <div style={{ position: 'relative', height: '100vh', overflow: 'hidden', background: 'var(--bg)' }}>

      {/* Floating toolbar */}
      <div className="view-toolbar" style={{ position: 'absolute', top: 14, left: 98, right: 14, display: 'flex', alignItems: 'center', gap: 12, zIndex: 10, background: 'var(--bg)', backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)', borderRadius: 12, padding: '6px 10px' }}>
        <button className="btn ghost tiny" onClick={onBack}>{window.Icons.ArrowL(13)} Library</button>

        {/* Search — absolutely centered */}
        <div style={{ position: 'absolute', left: '28%', transform: 'translateX(-50%)', zIndex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
            <input
              value={query}
              onChange={e => { setQuery(e.target.value); setConceptFilter(null); setCatFilter(null); setDropOpen(true); }}
              onFocus={() => { if (q) setDropOpen(true); }}
              onBlur={() => setTimeout(() => setDropOpen(false), 160)}
              onKeyDown={e => { if (e.key === 'Escape') { clearFilters(); setDropOpen(false); } }}
              placeholder="search…"
              size={Math.max((query || 'search…').length + 1, 8)}
              style={{
                background: 'transparent', border: 'none', outline: 'none',
                fontSize: 26, fontFamily: 'var(--font-serif)', fontStyle: 'italic',
                color: 'var(--fg)', padding: '2px 0',
              }}
            />
            {hasFilter && (
              <button onClick={clearFilters}
                style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--fg-3)', padding: '0 2px', fontSize: 13, lineHeight: 1, opacity: 0.6, display: 'flex' }}>
                ✕
              </button>
            )}
          </div>

          {/* Two-column dropdown */}
          {showDrop && (
            <div style={{
              position: 'absolute', top: 'calc(100% + 10px)',
              left: '50%', transform: 'translateX(-50%)',
              zIndex: 200,
              background: 'var(--bg-elev)', border: '1px solid var(--border)',
              borderRadius: 12, boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
              overflow: 'hidden', display: 'grid', gridTemplateColumns: '1fr 1fr',
              minWidth: 400,
            }}>
              <div style={{ borderRight: '1px solid var(--border-soft)', padding: '8px 0' }}>
                <div style={{ padding: '4px 14px 7px', fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '.14em', textTransform: 'uppercase', color: 'var(--fg-3)' }}>Concepts</div>
                {conceptSuggestions.length === 0
                  ? <div style={{ padding: '5px 14px', fontSize: 12, color: 'var(--fg-3)', fontStyle: 'italic' }}>no matches</div>
                  : conceptSuggestions.map(c => (
                      <div key={c.id} onMouseDown={() => pickConcept(c)}
                        onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-sunk)'}
                        onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                        style={{ padding: '7px 14px', cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 1 }}>
                        <span style={{ fontSize: 13, fontWeight: 500, color: 'var(--fg)' }}>{c.title}</span>
                        {c.category && <span style={{ fontSize: 10, color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', letterSpacing: '.04em' }}>{c.category}</span>}
                      </div>
                    ))
                }
              </div>
              <div style={{ padding: '8px 0' }}>
                <div style={{ padding: '4px 14px 7px', fontSize: 9, fontFamily: 'var(--font-mono)', letterSpacing: '.14em', textTransform: 'uppercase', color: 'var(--fg-3)' }}>Categories</div>
                {catSuggestions.length === 0
                  ? <div style={{ padding: '5px 14px', fontSize: 12, color: 'var(--fg-3)', fontStyle: 'italic' }}>no matches</div>
                  : catSuggestions.map(cat => (
                      <div key={cat} onMouseDown={() => pickCategory(cat)}
                        onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-sunk)'}
                        onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                        style={{ padding: '7px 14px', cursor: 'pointer' }}>
                        <span style={{ fontSize: 13, color: 'var(--fg)' }}>{cat}</span>
                      </div>
                    ))
                }
              </div>
            </div>
          )}
        </div>

        <div style={{ flex: 1 }} />

        <div className="mode-pill">
          <button className="btn tiny" style={navDim} onClick={() => selectedConcept && onOpenConcept && onOpenConcept(selectedConcept.id, 'edit')}>{window.Icons.Word(13)} Word</button>
          <button className="btn tiny" data-active="true" style={navActive}>{window.Icons.Cards(13)} Card</button>
          <button className="btn tiny" style={navDim} onClick={() => selectedConcept && onOpenSolar && onOpenSolar(selectedConcept.id)}>{window.Icons.Planet(13)} Solar</button>
          <button className="btn tiny" style={{ ...navDim, opacity: 1 }} onClick={() => onOpenGraph && onOpenGraph(selectedConcept ? selectedConcept.id : null)}>{window.Icons.Graph(13)} Graph</button>
          <button className="btn tiny" style={{ ...navDim, opacity: 1 }} onClick={() => onOpenGroup && onOpenGroup(selectedConcept ? selectedConcept.id : null)}>{window.Icons.Atom(13)} Group</button>
        </div>
      </div>

      {/* Scrollable grid */}
      <div style={{ position: 'absolute', top: 0, left: 84, right: 0, bottom: 0, overflowY: 'auto', paddingTop: 66 }}>
        {allCards.length === 0 && (
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, color: 'var(--fg-3)' }}>
            <p style={{ margin: 0, fontFamily: 'var(--font-serif)', fontStyle: 'italic', fontSize: 17 }}>No cards yet.</p>
            <p style={{ margin: 0, fontSize: 13 }}>Open a concept and attach notes, images, quotes, links or videos to its levels.</p>
          </div>
        )}
        {allCards.length > 0 && filtered.length === 0 && (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--fg-3)' }}>
            <p style={{ margin: 0, fontFamily: 'var(--font-serif)', fontStyle: 'italic', fontSize: 17 }}>No cards match this filter.</p>
          </div>
        )}
        {filtered.length > 0 && (
          <div style={{ columnCount: cols, columnGap: 10, padding: '10px' }}>
            {filtered.map((qk, i) => {
              const c = conceptMap[qk.concept_id];
              const conceptInfo = c ? c.title + ' · ' + qk.parent_type : qk.parent_type;
              return (
                <CardCard
                  key={qk.id}
                  card={qk}
                  conceptInfo={conceptInfo}
                  onDelete={handleDelete}
                  onUpdate={handleUpdate}
                  animDelay={i * 15}
                  onOpen={(qk) => setSelectedCardId(qk.id)}
                />
              );
            })}
          </div>
        )}
      </div>

      {/* Type filter — bottom left */}
      <div style={{ position: 'absolute', bottom: 14, left: 98, zIndex: 20 }}>
        <div className="mode-pill" style={{ padding: '6px 4px' }}>
          {[{ type: null, label: 'All' }, ...CARD_TYPES].map(qt => {
            const active = typeFilter === qt.type;
            return (
              <button key={qt.type || 'all'}
                onClick={() => setTypeFilter(qt.type)}
                className="btn tiny"
                style={{
                  border: 0,
                  background: active ? 'var(--c-nucleus)' : 'transparent',
                  boxShadow: active ? '0 1px 3px rgba(0,0,0,.15)' : 'none',
                  color: active ? 'rgba(0,0,0,0.8)' : 'var(--fg-3)',
                  display: 'flex', alignItems: 'center', gap: 5,
                }}>
                {qt.type && <span style={{ color: active ? 'rgba(0,0,0,0.6)' : qt.color, display: 'flex' }}>{cardIcon(qt.type, 11)}</span>}
                {qt.label}
              </button>
            );
          })}
        </div>
      </div>

      {/* Settings button */}
      <button className="btn icon" onClick={() => setShowSettings(s => !s)} title="Grid settings"
        style={{ position: 'absolute', bottom: 14, right: 14, zIndex: 20,
          borderRadius: 8, padding: 7, width: 32, height: 32, justifyContent: 'center',
          background: showSettings ? 'var(--bg-sunk)' : 'var(--bg-elev)',
          border: 'none', boxShadow: showSettings ? '0 1px 2px rgba(0,0,0,.04)' : '0 1px 2px rgba(0,0,0,.06), 0 4px 16px -4px rgba(0,0,0,.1)' }}>
        {window.Icons.Sliders(16)}
      </button>

      {showSettings && (
        <div style={{
          position: 'absolute', bottom: 54, right: 14, zIndex: 20,
          background: 'var(--bg-elev)',
          borderRadius: 14, padding: '14px 16px',
          boxShadow: '0 4px 24px rgba(0,0,0,.12), 0 1px 4px rgba(0,0,0,.06)',
          minWidth: 180, display: 'flex', flexDirection: 'column', gap: 12,
        }}>
          <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', letterSpacing: '.08em', textTransform: 'uppercase', color: 'var(--fg-3)', paddingBottom: 2 }}>Display</div>
          <_QhSlider label="Columns" value={cols} min={1} max={6} step={1} onChange={setCols} />
        </div>
      )}

      {selectedCard && (
        <CardModal
          card={selectedCard}
          onClose={() => setSelectedCardId(null)}
          onUpdate={handleUpdate}
          onDelete={(id) => { handleDelete(id); setSelectedCardId(null); }}
          onOpenConcept={onOpenConcept}
        />
      )}
    </div>
  );
}

// ── CardsSidePanel — compact right-rail panel for solar/graph views ─

function CardsSidePanel({ conceptId, parentType, parentId, label, onBack }) {
  const [cards, setCards] = _qp_us([]);
  const [adding, setAdding] = _qp_us(null);
  const [selectedCardId, setSelectedCardId] = _qp_us(null);
  const selectedCard = selectedCardId ? cards.find(q => q.id === selectedCardId) : null;

  _qp_ue(() => {
    if (window.cardsService) {
      window.cardsService.getCards(parentType, parentId).then(rows => {
        setCards(rows.slice().sort((a, b) => {
          if (a.sort_order != null && b.sort_order != null) return a.sort_order - b.sort_order;
          if (a.sort_order != null) return -1;
          if (b.sort_order != null) return 1;
          return a.created_at < b.created_at ? -1 : 1;
        }));
      });
    }
    setAdding(null);
  }, [parentType, parentId]);

  async function handleAdd(card) {
    const now = new Date().toISOString();
    const newCard = {
      id:          "qk" + Date.now() + Math.random().toString(36).slice(2, 6),
      parent_type: parentType,
      parent_id:   parentId,
      concept_id:  conceptId,
      sort_order:  (cards.length + 1) * 1000,
      created_at:  now,
      updated_at:  now,
      ...card,
    };
    await window.cardsService.addCard(newCard);
    setCards(qs => [...qs, newCard]);
    setAdding(null);
  }

  async function handleUpdate(id, changes) {
    await window.cardsService.updateCard(id, changes);
    setCards(qs => qs.map(q => q.id === id
      ? { ...q, ...changes, updated_at: new Date().toISOString() }
      : q
    ));
  }

  async function handleDelete(id) {
    await window.cardsService.deleteCard(id);
    setCards(qs => qs.filter(q => q.id !== id));
  }

  const levelColor = CARD_LEVEL_COLOR[parentType] || "var(--fg-2)";

  return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column", overflow: "hidden" }}>

      {/* Header */}
      <div style={{ padding: "20px 20px 14px", flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 6 }}>
          {onBack && (
            <button onClick={onBack} className="btn ghost tiny" style={{ padding: "2px 6px", marginRight: 2 }}>
              {window.Icons.ArrowL(11)}
            </button>
          )}
          <span style={{ width: 6, height: 6, borderRadius: 999, background: levelColor, flexShrink: 0 }} />
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 9.5, letterSpacing: ".14em", textTransform: "uppercase", color: "var(--fg-3)" }}>
            {parentType}
          </span>
          {cards.length > 0 && (
            <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--fg-3)" }}>
              {cards.length}
            </span>
          )}
        </div>
        <div style={{ fontSize: 22, fontFamily: "var(--font-serif)", fontStyle: "italic", color: "var(--fg)", lineHeight: 1.35, marginBottom: 14, marginTop: 28 }}>
          {label || parentType}
        </div>
        <div style={{ display: "flex", gap: 6, marginTop: 4 }}>
          {CARD_TYPES.map(qt => {
            const active = adding === qt.type;
            return (
              <button key={qt.type}
                onClick={() => setAdding(active ? null : qt.type)}
                style={{
                  flex: 1,
                  display: "flex", flexDirection: "column", alignItems: "center", gap: 6,
                  padding: "12px 6px 10px",
                  borderRadius: 12,
                  background: active ? qt.color : "var(--bg-elev)",
                  boxShadow: active ? "none" : "0 1px 2px rgba(0,0,0,.04), 0 4px 20px -6px rgba(0,0,0,.08)",
                  color: active ? "rgba(0,0,0,0.75)" : "var(--fg-3)",
                  cursor: "pointer",
                  transition: "background 150ms, box-shadow 150ms",
                }}>
                <span style={{ color: active ? "rgba(0,0,0,0.7)" : qt.color, display: "flex" }}>{cardIcon(qt.type, 18)}</span>
                <span style={{ fontFamily: "var(--font-mono)", fontSize: 9, letterSpacing: ".08em", textTransform: "uppercase" }}>{qt.label}</span>
              </button>
            );
          })}
        </div>
      </div>

      {/* Scrollable content */}
      <div style={{ flex: 1, overflowY: "auto", padding: "14px 14px 40px" }}>
        {adding && (
          <div style={{ marginBottom: 14 }}>
            <AddCardForm type={adding} onSubmit={handleAdd} onCancel={() => setAdding(null)} />
          </div>
        )}
        {cards.length === 0 && !adding && (
          <div style={{ padding: "48px 0", textAlign: "center", color: "var(--fg-3)" }}>
            <p style={{ margin: 0, fontFamily: "var(--font-serif)", fontStyle: "italic", fontSize: 15 }}>
              Nothing here yet.
            </p>
            <p style={{ margin: "8px 0 0", fontSize: 12, lineHeight: 1.5 }}>
              Attach a note, image, quote,<br />link, or video.
            </p>
          </div>
        )}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {cards.map((q, i) => (
            <CardCard
              key={q.id}
              card={q}
              onDelete={handleDelete}
              onUpdate={handleUpdate}
              animDelay={i * 30}
              onOpen={(q) => setSelectedCardId(q.id)}
            />
          ))}
        </div>
      </div>

      {selectedCard && (
        <CardModal
          card={selectedCard}
          onClose={() => setSelectedCardId(null)}
          onUpdate={handleUpdate}
          onDelete={(id) => { handleDelete(id); setSelectedCardId(null); }}
        />
      )}
    </div>
  );
}

window.CardsScreen    = CardsScreen;
window.CardsHubScreen = CardsHubScreen;
window.CardsSidePanel = CardsSidePanel;
