// Notequick — main app. Supabase-backed, realtime, markdown, context menus.

const TWEAK_DEFAULTS = {
  accent: '#1d4ed8', font: 'sans', density: 'comfortable',
  sideW: 240, listW: 320, layout: 'three', dark: false,
};

const ACCENTS = ['#1d4ed8', '#c2410c', '#15803d', '#7c3aed', '#0f172a'];
const FONTS   = [
  { value: 'sans',  label: 'Sans'  },
  { value: 'serif', label: 'Serif' },
  { value: 'mono',  label: 'Mono'  },
];

// Upsert-by-id helper for realtime merges.
function upsertById(list, row) {
  const i = list.findIndex(x => x.id === row.id);
  if (i === -1) return [...list, row];
  const next = list.slice();
  next[i] = { ...next[i], ...row };
  return next;
}

function NotequickApp() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [notes,     setNotes]     = React.useState([]);
  const [notebooks, setNotebooks] = React.useState(DEFAULT_NOTEBOOKS);
  const [tags,      setTags]      = React.useState(DEFAULT_TAGS);
  const [loading,   setLoading]   = React.useState(true);
  const [loadError, setLoadError] = React.useState(null);
  const [filter,    setFilter]    = React.useState({ type: 'all' });
  const [query,     setQuery]     = React.useState('');
  const [sort,      setSort]      = React.useState('updated');
  const [selectedId,setSelectedId]= React.useState(null);
  const [signingOut, setSigningOut] = React.useState(false);
  const saveTimers = React.useRef({});
  const ctx = useContextMenu();
  const [importState, setImportState] = React.useState(null);
  const enexInputRef = React.useRef(null);

  // ── idle auto-sign-out ───────────────────────────────────────────────────────
  // Only armed while the authenticated app is mounted. On idle, signOut() flips
  // the auth state and AuthGate swaps back to the sign-in screen.
  React.useEffect(() => startIdleSignout(), []);

  // ── initial load ───────────────────────────────────────────────────────────
  React.useEffect(() => {
    (async () => {
      try {
        // Seed defaults for brand-new users so they don't open an empty app.
        try { await seedDefaultsForUser(); } catch (e) { console.warn('seed defaults failed', e); }
        const data = await fetchAll();
        setNotebooks(data.notebooks.length ? data.notebooks : DEFAULT_NOTEBOOKS);
        setTags(data.tags);
        setNotes(data.notes);
        if (data.notes[0]) setSelectedId(data.notes[0].id);
      } catch (err) {
        console.error(err);
        setLoadError(err.message || String(err));
      } finally {
        setLoading(false);
      }
    })();
  }, []);

  // ── realtime ───────────────────────────────────────────────────────────────
  React.useEffect(() => {
    const unsub = subscribeRealtime({
      notebook: (p) => {
        if (p.eventType === 'DELETE') {
          setNotebooks(ns => ns.filter(x => x.id !== p.old.id));
        } else {
          setNotebooks(ns => upsertById(ns, p.new));
        }
      },
      tag: (p) => {
        if (p.eventType === 'DELETE') {
          setTags(ts => ts.filter(x => x.id !== p.old.id));
        } else {
          setTags(ts => upsertById(ts, p.new));
        }
      },
      note: (p) => {
        if (p.eventType === 'DELETE') {
          setNotes(ns => ns.filter(x => x.id !== p.old.id));
        } else if (p.eventType === 'INSERT') {
          setNotes(ns => {
            if (ns.some(x => x.id === p.new.id)) return ns;
            return [mapNote(p.new, [], []), ...ns];
          });
        } else { // UPDATE — preserve entries/attachments arrays
          setNotes(ns => ns.map(n => n.id === p.new.id
            ? { ...n,
                title: p.new.title || '',
                summary: p.new.body || '',
                notebook: p.new.notebook || 'inbox',
                tags: p.new.tags || [],
                starred: !!p.new.starred,
                updated: p.new.updated_at }
            : n));
        }
      },
      comment: (p) => {
        if (p.eventType === 'DELETE') {
          setNotes(ns => ns.map(n => ({ ...n, entries: n.entries.filter(e => e.id !== p.old.id) })));
        } else {
          const c = mapComment(p.new);
          setNotes(ns => ns.map(n => n.id === c.note_id
            ? { ...n, entries: upsertById(n.entries, c) } : n));
        }
      },
      attachment: (p) => {
        if (p.eventType === 'DELETE') {
          setNotes(ns => ns.map(n => ({ ...n, attachments: n.attachments.filter(a => a.id !== p.old.id) })));
        } else {
          const a = mapAttachment(p.new);
          setNotes(ns => ns.map(n => n.id === a.note_id
            ? { ...n, attachments: upsertById(n.attachments, a) } : n));
        }
      },
    });
    return unsub;
  }, []);

  // ── derived: filtered + sorted notes ───────────────────────────────────────
  const visible = React.useMemo(() => {
    let v = notes;
    if (filter.type === 'starred')       v = v.filter(n => n.starred);
    else if (filter.type === 'notebook') v = v.filter(n => n.notebook===filter.id);
    else if (filter.type === 'tag')      v = v.filter(n => (n.tags||[]).includes(filter.id));
    const q = query.trim().toLowerCase();
    if (q) {
      v = v.filter(n =>
        (n.title||'').toLowerCase().includes(q) ||
        (n.summary||'').toLowerCase().includes(q) ||
        (n.tags||[]).some(x => x.toLowerCase().includes(q)),
      );
    }
    if (sort === 'updated')      v = [...v].sort((a,b) => new Date(b.updated)-new Date(a.updated));
    else if (sort === 'alpha')   v = [...v].sort((a,b) => (a.title||'').localeCompare(b.title||''));
    else if (sort === 'created') v = [...v].sort((a,b) => new Date(a.updated)-new Date(b.updated));
    return v;
  }, [notes, filter, query, sort]);

  const listTitle = React.useMemo(() => {
    if (filter.type === 'all')      return 'All notes';
    if (filter.type === 'starred')  return 'Starred';
    if (filter.type === 'notebook') return notebooks.find(b=>b.id===filter.id)?.name || 'Notebook';
    if (filter.type === 'tag')      return `#${filter.id}`;
    return 'Notes';
  }, [filter, notebooks]);

  React.useEffect(() => {
    if (visible.length && !visible.find(n => n.id===selectedId)) {
      setSelectedId(visible[0].id);
    }
  }, [visible, selectedId]);

  const selected = notes.find(n => n.id===selectedId) || null;

  // ── note/entry mutations (optimistic) ──────────────────────────────────────
  const patchLocal = (id, patch) => {
    setNotes(ns => ns.map(n => n.id===id
      ? { ...n, ...patch, updated: patch.updated || new Date().toISOString() }
      : n));
  };

  const onChange = (id, patch) => {
    patchLocal(id, patch);
    if ('title' in patch || 'summary' in patch) {
      clearTimeout(saveTimers.current[id]);
      saveTimers.current[id] = setTimeout(() => {
        updateNote(id, patch).catch(err => console.error('save failed', err));
      }, 400);
    } else {
      updateNote(id, patch).catch(err => console.error('save failed', err));
    }
  };

  const onStar = (id) => {
    const n = notes.find(x => x.id===id);
    if (!n) return;
    const next = !n.starred;
    patchLocal(id, { starred: next });
    toggleStar(id, next).catch(err => console.error('star failed', err));
  };

  const onDelete = async (id) => {
    const n = notes.find(x => x.id===id);
    if (!n) return;
    if (!confirm(`Delete "${n.title || 'Untitled note'}"? This also removes its entries and attachments.`)) return;
    setNotes(ns => ns.filter(x => x.id !== id));
    if (selectedId === id) setSelectedId(null);
    try { await deleteNoteFull(id); }
    catch (err) { alert('Delete failed: ' + err.message); }
  };

  const onAddAttachment = async (id, file) => {
    const att = await uploadAttachment(id, file);
    setNotes(ns => ns.map(n => n.id===id
      ? { ...n, attachments: [att, ...n.attachments], updated: new Date().toISOString() } : n));
  };

  const onRemoveAttachment = async (id, att) => {
    if (!confirm(`Remove "${att.name}"?`)) return;
    setNotes(ns => ns.map(n => n.id===id
      ? { ...n, attachments: n.attachments.filter(a => a.id!==att.id) } : n));
    try { await removeAttachment(att); }
    catch (err) { console.error('remove attachment failed', err); }
  };

  const openImportPicker = () => {
    enexInputRef.current?.click();
  };

  const onENEXChosen = async (e) => {
    const file = e.target.files?.[0];
    e.target.value = '';
    if (!file) return;
    setImportState({ current: 0, total: 0, title: 'Reading file…', phase: 'note' });
    try {
      const result = await importENEX(file, (p) => setImportState(s => ({ ...s, ...p })));
      // Reload from the server so all notes/notebooks/tags reflect the import.
      try {
        const data = await fetchAll();
        setNotebooks(data.notebooks.length ? data.notebooks : DEFAULT_NOTEBOOKS);
        setTags(data.tags);
        setNotes(data.notes);
      } catch (err) { console.error('Post-import refresh failed:', err); }
      setImportState({ done: true, ...result });
      // Jump to the imported notebook.
      if (result.notebookId) setFilter({ type: 'notebook', id: result.notebookId });
    } catch (err) {
      console.error('ENEX import failed:', err);
      setImportState({ done: true, imported: 0, errors: 1,
        total: 0, notebookName: '—',
        message: err.message || String(err) });
    }
  };

  const newNote = async (opts = {}) => {
    const nbId = opts.notebook || (filter.type==='notebook' ? filter.id : 'inbox');
    try {
      const note = await createNote({ notebook: nbId });
      setNotes(ns => [note, ...ns]);
      setSelectedId(note.id);
      setTimeout(() => document.querySelector('.nq-ed-title')?.focus(), 50);
    } catch (err) {
      alert('Could not create note: ' + err.message);
    }
  };

  // ── notebook/tag mutations ─────────────────────────────────────────────────
  const onNewNotebook = async () => {
    const name = prompt('New notebook name:')?.trim();
    if (!name) return;
    try {
      const nb = await createNotebook(name, null, notebooks);
      setNotebooks(ns => upsertById(ns, nb));
    } catch (err) { alert('Could not create notebook: ' + err.message); }
  };
  const onRenameNotebook = async (nb) => {
    const name = prompt('Rename notebook:', nb.name)?.trim();
    if (!name || name === nb.name) return;
    try {
      const updated = await renameNotebook(nb.id, name);
      setNotebooks(ns => ns.map(x => x.id===nb.id ? updated : x));
    } catch (err) { alert('Rename failed: ' + err.message); }
  };
  const onDeleteNotebook = async (nb) => {
    if (nb.id === 'inbox') { alert('Inbox cannot be deleted.'); return; }
    if (!confirm(`Delete notebook "${nb.name}"? Notes in it will move to Inbox.`)) return;
    try {
      await deleteNotebookById(nb.id);
      setNotebooks(ns => ns.filter(x => x.id !== nb.id));
      setNotes(ns => ns.map(n => n.notebook===nb.id ? { ...n, notebook:'inbox' } : n));
      if (filter.type==='notebook' && filter.id===nb.id) setFilter({ type:'all' });
    } catch (err) { alert('Delete failed: ' + err.message); }
  };
  const onNewTag = async () => {
    const name = prompt('New tag name (no spaces, lowercase):')?.trim();
    if (!name) return;
    try {
      const tag = await createTag(name);
      setTags(ts => upsertById(ts, tag));
    } catch (err) { alert('Could not create tag: ' + err.message); }
  };
  const onRenameTag = async (tag) => {
    const name = prompt('Rename tag:', tag.name)?.trim();
    if (!name || name === tag.name) return;
    try {
      const updated = await renameTag(tag.id, name);
      setTags(ts => ts.map(x => x.id===tag.id ? updated : x));
    } catch (err) { alert('Rename failed: ' + err.message); }
  };
  const onDeleteTag = async (tag) => {
    if (!confirm(`Delete tag "#${tag.name}"? It will be removed from all notes.`)) return;
    try {
      await deleteTagById(tag.id);
      setTags(ts => ts.filter(x => x.id !== tag.id));
      setNotes(ns => ns.map(n => ({ ...n, tags: (n.tags||[]).filter(t => t !== tag.id) })));
      if (filter.type==='tag' && filter.id===tag.id) setFilter({ type:'all' });
    } catch (err) { alert('Delete failed: ' + err.message); }
  };

  const onDuplicate = async (note) => {
    try {
      const newId = await duplicateNote(note);
      // Realtime will deliver the new note + its entries shortly.
      setSelectedId(newId);
    } catch (err) { alert('Duplicate failed: ' + err.message); }
  };

  // ── context-menu builders ──────────────────────────────────────────────────
  const showCtxNotebook = (e, nb) => ctx.open(e, [
    { label: `New note in "${nb.name}"`, icon: <IconPencil size={13}/>,
      onClick: () => newNote({ notebook: nb.id }) },
    { divider: true },
    { label: 'Rename notebook…', icon: <IconPencil size={13}/>,
      onClick: () => onRenameNotebook(nb) },
    { label: 'Delete notebook', icon: <IconTrash size={13}/>, danger: true,
      disabled: nb.id === 'inbox',
      onClick: () => onDeleteNotebook(nb) },
  ]);
  const showCtxNotebookHeader = (e) => ctx.open(e, [
    { label: 'New notebook…', icon: <IconPlus size={13}/>, onClick: onNewNotebook },
    { divider: true },
    { label: 'Import from Evernote…', icon: <IconImport size={13}/>, onClick: openImportPicker },
  ]);
  const showCtxTag = (e, tag) => ctx.open(e, [
    { label: 'Rename tag…', icon: <IconPencil size={13}/>, onClick: () => onRenameTag(tag) },
    { label: 'Delete tag', icon: <IconTrash size={13}/>, danger: true,
      onClick: () => onDeleteTag(tag) },
  ]);
  const showCtxTagHeader = (e) => ctx.open(e, [
    { label: 'New tag…', icon: <IconPlus size={13}/>, onClick: onNewTag },
  ]);
  const showCtxCard = (e, n) => {
    const items = [
      { label: n.starred ? 'Unstar' : 'Star',
        icon: <IconStar size={13}/>, onClick: () => onStar(n.id) },
      { label: 'Duplicate', icon: <IconPlus size={13}/>, onClick: () => onDuplicate(n) },
      { divider: true },
      { header: 'Move to notebook' },
      ...notebooks.map(nb => ({
        label: nb.name,
        icon: <span className="nq-sb-dot" style={{background:nb.color}}/>,
        disabled: nb.id === n.notebook,
        onClick: () => onChange(n.id, { notebook: nb.id }),
      })),
      { divider: true },
      { label: 'Delete note', icon: <IconTrash size={13}/>, danger: true,
        onClick: () => onDelete(n.id) },
    ];
    ctx.open(e, items);
  };
  const showCtxEditor = (e) => {
    const tag = (e.target.tagName||'').toUpperCase();
    if (['INPUT','TEXTAREA','BUTTON','A'].includes(tag)) return;
    if (e.target.closest('.nq-att, .nq-fmt-btn, .nq-comp-btn, .nq-chip, .nq-fmt-toggle')) return;
    ctx.open(e, [
      { label: 'New note', icon: <IconPencil size={13}/>, kbd: '⌘N', onClick: () => newNote() },
      { label: 'New notebook…', icon: <IconPlus size={13}/>, onClick: onNewNotebook },
      { label: 'New tag…',      icon: <IconTag size={13}/>,  onClick: onNewTag },
    ]);
  };

  // ── keyboard shortcuts ─────────────────────────────────────────────────────
  React.useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase()==='n') {
        e.preventDefault(); newNote();
      }
      if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase()==='k') {
        e.preventDefault();
        document.querySelector('.nq-search input')?.focus();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  });

  // ── theme / layout vars ────────────────────────────────────────────────────
  const fontVar = t.font==='serif' ? 'var(--font-serif)'
                : t.font==='mono'  ? 'var(--font-mono)'
                :                    'var(--font-sans)';
  const rootStyle = {
    '--accent':   t.accent,
    '--accent-2': hexA(t.accent, .08),
    '--accent-3': hexA(t.accent, .18),
    '--font-body': fontVar,
    '--side-w':   t.sideW + 'px',
    '--list-w':   t.listW + 'px',
  };

  return (
    <div className="nq-app" data-theme={t.dark ? 'dark' : 'light'}
         data-density={t.density} data-layout={t.layout}
         style={rootStyle}>
      {t.layout === 'three' && (
        <Sidebar notebooks={notebooks} tags={tags} notes={notes}
                 filter={filter} setFilter={setFilter}
                 onNewNote={() => newNote()}
                 onImport={openImportPicker}
                 importing={!!(importState && !importState.done)}
                 sideW={t.sideW} density={t.density}
                 onCtxNotebook={showCtxNotebook}
                 onCtxNotebookHeader={showCtxNotebookHeader}
                 onCtxTag={showCtxTag}
                 onCtxTagHeader={showCtxTagHeader}/>
      )}
      <NotesList notes={visible} notebooks={notebooks}
                 query={query} setQuery={setQuery}
                 selectedId={selectedId} onSelect={setSelectedId}
                 title={listTitle} sort={sort} setSort={setSort}
                 listW={t.listW} density={t.density}
                 onCtxCard={showCtxCard}/>
      <Editor note={selected} notebooks={notebooks} tagsAll={tags}
              onChange={onChange}
              onAddAttachment={onAddAttachment}
              onRemoveAttachment={onRemoveAttachment}
              onStar={onStar}
              onDelete={onDelete}
              accent={t.accent}
              onCtxEditor={showCtxEditor}/>

      <ContextMenu menu={ctx.menu} onClose={ctx.close}/>

      <input ref={enexInputRef} type="file" accept=".enex,application/xml,text/xml"
             style={{display:'none'}} onChange={onENEXChosen}/>
      <ImportProgress state={importState} onClose={()=>setImportState(null)}/>

      {loading && <div className="nq-toast">Loading notes…</div>}
      {loadError && <div className="nq-toast error">Connection error: {loadError}</div>}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Account">
          <button className="nq-signout-btn" disabled={signingOut}
                  onClick={async () => {
                    setSigningOut(true);
                    try { await signOut(); } finally { setSigningOut(false); }
                  }}>
            {signingOut ? 'Signing out…' : 'Sign out'}
          </button>
        </TweakSection>
        <TweakSection label="Appearance">
          <TweakToggle label="Dark mode" value={t.dark}
                       onChange={v=>setTweak('dark', v)}/>
          <TweakColor label="Accent" value={t.accent} options={ACCENTS}
                      onChange={v=>setTweak('accent', v)}/>
        </TweakSection>
        <TweakSection label="Typography">
          <TweakRadio label="Font" value={t.font} options={FONTS}
                      onChange={v=>setTweak('font', v)}/>
        </TweakSection>
        <TweakSection label="Layout">
          <TweakRadio label="Density" value={t.density}
                      options={[{value:'compact',label:'Compact'},
                                {value:'comfortable',label:'Comfy'}]}
                      onChange={v=>setTweak('density', v)}/>
          <TweakRadio label="Panes" value={t.layout}
                      options={[{value:'three',label:'Three'},
                                {value:'two',label:'Two'}]}
                      onChange={v=>setTweak('layout', v)}/>
          <TweakSlider label="Sidebar width" value={t.sideW}
                       min={200} max={320} step={4} unit="px"
                       onChange={v=>setTweak('sideW', v)}/>
          <TweakSlider label="Notes list width" value={t.listW}
                       min={260} max={420} step={4} unit="px"
                       onChange={v=>setTweak('listW', v)}/>
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

function hexA(hex, a) {
  const h = hex.replace('#','');
  const x = h.length===3 ? h.replace(/./g,c=>c+c) : h;
  const n = parseInt(x,16);
  return `rgba(${(n>>16)&255},${(n>>8)&255},${n&255},${a})`;
}

function App() {
  // Read just the dark-mode preference up here so the auth screens (which
  // render before NotequickApp mounts) still respect the user's theme.
  const dark = React.useMemo(() => {
    try {
      const s = localStorage.getItem('nq.tweaks.v1');
      if (s) return !!JSON.parse(s).dark;
    } catch (_) {}
    return false;
  }, []);
  return (
    <div data-theme={dark ? 'dark' : 'light'} style={{ height: '100%' }}>
      <Styles />
      <AuthGate>
        <NotequickApp />
      </AuthGate>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);

// ─── Inline CSS ─────────────────────────────────────────────────────────────
function Styles(){ return <style>{`
  .nq-app{display:grid;height:100vh;
    grid-template-columns: var(--side-w) var(--list-w) 1fr;
    background:var(--bg);color:var(--ink);
    transition:background .25s ease,color .25s ease}
  .nq-app[data-layout="two"]{grid-template-columns: var(--list-w) 1fr}

  /* ───── Sidebar ───── */
  .nq-sb{border-right:1px solid var(--line);
    background:linear-gradient(180deg,var(--panel-2) 0%,var(--bg) 100%);
    padding:14px 10px 16px;display:flex;flex-direction:column;
    overflow:auto;min-height:0}
  .nq-brand{display:flex;align-items:center;gap:9px;padding:4px 6px 14px}
  .nq-brand-mark{width:24px;height:24px;border-radius:7px;
    background:linear-gradient(135deg,var(--accent) 0%,var(--accent-violet) 100%);
    display:grid;place-items:center;position:relative;flex-shrink:0;
    box-shadow:0 2px 6px rgba(37,99,235,.30),inset 0 1px 0 rgba(255,255,255,.25)}
  .nq-brand-mark span{display:block;width:10px;height:10px;border-radius:2px;
    background:#fff;opacity:.95}
  .nq-brand-name{font-weight:600;letter-spacing:-0.01em;font-size:14px;
    background:linear-gradient(120deg,var(--ink) 0%,var(--accent-deep) 100%);
    -webkit-background-clip:text;background-clip:text;
    -webkit-text-fill-color:transparent}
  [data-theme="dark"] .nq-brand-name{
    background:linear-gradient(120deg,var(--ink) 0%,var(--accent) 100%);
    -webkit-background-clip:text;background-clip:text;
    -webkit-text-fill-color:transparent}

  .nq-new{display:flex;align-items:center;gap:8px;width:100%;
    padding:8px 11px;border-radius:8px;border:1px solid var(--accent-3);
    background:linear-gradient(180deg,var(--panel) 0%,var(--accent-2) 130%);
    color:var(--accent);
    margin:0 0 16px;cursor:pointer;text-align:left;
    box-shadow:var(--shadow-1);white-space:nowrap;
    transition:transform .1s,box-shadow .12s,border-color .12s}
  .nq-new:hover{border-color:var(--accent);box-shadow:var(--shadow-2);
    transform:translateY(-1px)}
  .nq-new:active{transform:translateY(0)}
  .nq-new svg{color:var(--accent)}
  .nq-new span{font-weight:600;font-size:13px;color:var(--accent)}

  .nq-import-sb{display:flex;align-items:center;gap:8px;width:100%;
    padding:6px 10px;border-radius:7px;border:1px dashed var(--line-2);
    background:transparent;color:var(--ink-2);
    margin:0 0 14px;cursor:pointer;text-align:left;
    font-size:12.5px;white-space:nowrap;
    transition:border-color .12s,color .12s,background .12s}
  .nq-import-sb:hover:not(:disabled){border-color:var(--accent);
    color:var(--accent);background:var(--accent-2)}
  .nq-import-sb:disabled{opacity:.55;cursor:wait}
  .nq-import-sb svg{color:var(--ink-3);flex-shrink:0}
  .nq-import-sb:hover:not(:disabled) svg{color:var(--accent)}

  /* Import modal */
  .nq-import-modal{position:fixed;inset:0;z-index:90;
    background:rgba(20,20,40,.45);
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
    display:grid;place-items:center;padding:20px}
  .nq-import-card{width:min(440px,100%);background:var(--panel);
    border:1px solid var(--line-2);border-radius:12px;padding:20px;
    box-shadow:var(--shadow-3);
    display:flex;flex-direction:column;gap:12px}
  .nq-import-h{display:flex;align-items:center;gap:8px;
    font-size:14px;font-weight:600;color:var(--ink)}
  .nq-import-h svg{color:var(--accent)}
  .nq-import-bar{height:6px;border-radius:99px;background:var(--panel-2);
    overflow:hidden}
  .nq-import-bar > div{height:100%;border-radius:99px;
    background:linear-gradient(90deg,var(--accent) 0%,var(--accent-violet) 100%);
    transition:width .25s}
  .nq-import-meta{font-size:11.5px;color:var(--ink-3)}
  .nq-import-now{font-size:12.5px;color:var(--ink-2);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .nq-import-summary{font-size:13px;color:var(--ink-2);line-height:1.5}
  .nq-import-err{color:var(--danger);margin-top:6px;font-size:12.5px}
  .nq-import-detail{font-family:var(--font-mono);font-size:11px;line-height:1.5;
    background:var(--panel-2);border:1px solid var(--line-2);border-radius:6px;
    padding:8px 10px;margin:6px 0 0;max-height:220px;overflow:auto;
    white-space:pre-wrap;word-break:break-word;color:var(--ink-2)}
  .nq-new .nq-kbd{margin-left:auto;color:var(--ink-3);font-size:10.5px;
    padding:2px 6px;border-radius:4px;background:var(--panel-2);
    border:1px solid var(--line)}

  .nq-sb-h{display:flex;align-items:center;justify-content:space-between;
    font-size:10.5px;font-weight:600;letter-spacing:.08em;
    text-transform:uppercase;color:var(--ink-3);padding:14px 8px 6px}
  .nq-sb-h-add{border:0;background:transparent;color:var(--ink-3);
    cursor:pointer;padding:2px;border-radius:4px;display:inline-flex;
    opacity:0;transition:opacity .12s}
  .nq-sb-h:hover .nq-sb-h-add{opacity:1}
  .nq-sb-h-add:hover{color:var(--accent);background:var(--accent-2)}
  .nq-sb-group{display:flex;flex-direction:column;gap:1px}
  .nq-sb-row{display:flex;align-items:center;gap:10px;padding:6px 10px;
    border-radius:7px;border:0;background:transparent;color:var(--ink-2);
    cursor:pointer;text-align:left;width:100%;font-size:13px;line-height:1.2}
  .nq-sb-row:hover{background:rgba(0,0,0,.04);color:var(--ink)}
  [data-theme="dark"] .nq-sb-row:hover{background:rgba(255,255,255,.05)}
  .nq-sb-row[data-active="1"]{background:var(--accent-2);color:var(--accent);
    font-weight:500}
  .nq-sb-row[data-active="1"] .nq-sb-cnt{color:var(--accent)}
  .nq-sb-icon{display:inline-flex;width:18px;align-items:center;justify-content:flex-start;
    color:var(--ink-3)}
  .nq-sb-row[data-active="1"] .nq-sb-icon{color:var(--accent)}
  .nq-sb-dot{display:inline-block;width:9px;height:9px;border-radius:3px;
    background:var(--ink-3)}
  .nq-sb-lbl{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .nq-sb-cnt{color:var(--ink-3);font-size:11px}
  .nq-sb-spacer{flex:1}
  .nq-sb[data-density="compact"] .nq-sb-row{padding:4px 10px;font-size:12.5px}
  .nq-sb[data-density="compact"] .nq-sb-h{padding-top:10px}

  /* ───── Notes list ───── */
  .nq-list{background:var(--panel);border-right:1px solid var(--line);
    display:flex;flex-direction:column;min-height:0;overflow:hidden}
  .nq-list-hd{padding:14px 16px 10px;border-bottom:1px solid var(--line);
    display:flex;flex-direction:column;gap:10px;background:var(--panel)}
  .nq-list-title{display:flex;align-items:baseline;justify-content:space-between;
    gap:8px}
  .nq-list-title>span:first-child{font-size:17px;font-weight:600;
    letter-spacing:-0.01em}
  .nq-list-count{color:var(--ink-3);font-size:12px}

  .nq-search{display:flex;align-items:center;gap:7px;padding:6px 10px;
    border:1px solid var(--line-2);border-radius:7px;background:var(--bg);
    color:var(--ink-3)}
  .nq-search:focus-within{border-color:var(--accent);background:var(--panel);
    box-shadow:0 0 0 3px var(--accent-2)}
  .nq-search input{flex:1;border:0;background:transparent;outline:none;
    font-size:13px;color:var(--ink);min-width:0}
  .nq-search input::placeholder{color:var(--ink-3)}
  .nq-search-x{border:0;background:transparent;color:var(--ink-3);cursor:pointer;
    padding:2px;display:inline-flex;align-items:center}
  .nq-search-x:hover{color:var(--ink)}

  .nq-list-tools{display:flex;gap:4px}
  .nq-sort{border:0;background:transparent;color:var(--ink-3);
    padding:3px 8px;border-radius:5px;font-size:12px;cursor:pointer;white-space:nowrap}
  .nq-sort:hover{color:var(--ink);background:rgba(0,0,0,.04)}
  [data-theme="dark"] .nq-sort:hover{background:rgba(255,255,255,.06)}
  .nq-sort.is-on{background:var(--accent-2);color:var(--accent);font-weight:500}

  .nq-list-body{flex:1;min-height:0;overflow:auto;padding:6px 8px 8px}

  .nq-card{display:flex;flex-direction:column;gap:6px;width:100%;
    padding:11px 12px;border:0;background:transparent;border-radius:8px;
    text-align:left;cursor:pointer;color:var(--ink);
    border:1px solid transparent;position:relative}
  .nq-card+.nq-card{margin-top:2px}
  .nq-card:hover{background:rgba(0,0,0,.025)}
  [data-theme="dark"] .nq-card:hover{background:rgba(255,255,255,.04)}
  .nq-card[data-active="1"]{background:var(--accent-2);
    border-color:var(--accent-3);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.4),var(--shadow-1)}
  [data-theme="dark"] .nq-card[data-active="1"]{box-shadow:none}
  .nq-card[data-active="1"]::before{content:"";position:absolute;
    left:-1px;top:12px;bottom:12px;width:3px;border-radius:3px;
    background:linear-gradient(180deg,var(--accent) 0%,var(--accent-violet) 100%);
    box-shadow:0 0 8px rgba(37,99,235,.35)}
  .nq-card-top{display:flex;align-items:center;justify-content:space-between;gap:8px}
  .nq-card-nb{display:inline-flex;align-items:center;gap:6px;color:var(--ink-3);
    font-size:11.5px;font-weight:500}
  .nq-card-time{color:var(--ink-3);font-size:11px}
  .nq-card-title{display:flex;align-items:center;gap:6px;
    font-weight:600;font-size:13.5px;letter-spacing:-0.005em;color:var(--ink);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .nq-card-title svg{color:var(--warm);flex-shrink:0}
  .nq-card-snippet{color:var(--ink-2);font-size:12.5px;line-height:1.45;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
    overflow:hidden;text-wrap:pretty}
  .nq-card-bot{display:flex;align-items:center;justify-content:space-between;
    gap:8px;margin-top:2px}
  .nq-card-tags{display:flex;gap:4px;flex-wrap:wrap;min-width:0;overflow:hidden}
  .nq-card-meta{display:flex;align-items:center;gap:8px;color:var(--ink-3);
    font-size:11px}
  .nq-card-meta>span{display:inline-flex;align-items:center;gap:3px}
  .nq-list[data-density="compact"] .nq-card{padding:8px 12px;gap:3px}
  .nq-list[data-density="compact"] .nq-card-snippet{-webkit-line-clamp:1}

  .nq-chip{display:inline-flex;align-items:center;gap:4px;
    padding:1px 8px;border-radius:999px;border:1px solid var(--line-2);
    color:var(--accent-deep);font-size:11px;font-weight:500;
    background:var(--accent-2)}
  [data-theme="dark"] .nq-chip{color:var(--accent)}
  .nq-chip-soft{border-color:var(--accent-3);color:var(--accent);
    background:var(--accent-2)}
  .nq-chip-rm{cursor:pointer;border:1px dashed var(--line-2)}
  .nq-chip-rm:hover{border-color:var(--danger);color:var(--danger)}
  .nq-chip-add{cursor:pointer;color:var(--ink-3);
    border:1px dashed var(--line-2);background:transparent}
  .nq-chip-add:hover{color:var(--ink);border-color:var(--ink-4)}

  /* ───── Editor ───── */
  .nq-ed{display:flex;flex-direction:column;min-height:0;min-width:0;background:var(--bg);
    position:relative}
  .nq-ed-hd{display:flex;align-items:center;justify-content:space-between;
    padding:14px 28px;border-bottom:1px solid var(--line);
    background:var(--bg)}
  .nq-ed-crumb{display:flex;align-items:center;gap:10px;color:var(--ink-3);
    font-size:12.5px}
  .nq-ed-nb{display:inline-flex;align-items:center;gap:6px;border:0;
    background:transparent;color:var(--ink-2);font-weight:500;font-size:12.5px;
    padding:2px 6px;border-radius:5px;cursor:pointer}
  .nq-ed-nb:hover{background:rgba(0,0,0,.05);color:var(--ink)}
  [data-theme="dark"] .nq-ed-nb:hover{background:rgba(255,255,255,.06)}
  .nq-crumb-sep{color:var(--ink-4)}
  .nq-ed-time{font-size:11.5px}
  .nq-ed-actions{display:flex;gap:2px}
  .nq-ic{border:0;background:transparent;color:var(--ink-3);
    padding:6px;border-radius:6px;cursor:pointer;display:inline-flex}
  .nq-ic:hover{background:rgba(0,0,0,.05);color:var(--ink)}
  [data-theme="dark"] .nq-ic:hover{background:rgba(255,255,255,.06)}
  .nq-ic.is-on{color:var(--warm);background:var(--warm-2)}

  .nq-ed-body{flex:1;min-height:0;overflow:auto;padding:24px 28px 28px;
    display:flex;flex-direction:column;gap:14px;
    max-width:920px;width:100%;margin:0 auto;min-width:0}
  .nq-ed-title{border:0;background:transparent;color:var(--ink);outline:none;
    font:600 28px/1.2 var(--font-body);letter-spacing:-0.015em;
    padding:0;margin:0;width:100%;text-overflow:ellipsis}
  .nq-ed-title::placeholder{color:var(--ink-4)}

  .nq-ed-tags{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
  .nq-tagadd{position:relative}
  .nq-tagpop{position:absolute;top:calc(100% + 4px);left:0;z-index:20;
    background:var(--panel);border:1px solid var(--line-2);border-radius:8px;
    box-shadow:0 8px 24px rgba(0,0,0,.10);padding:4px;min-width:160px;
    display:flex;flex-direction:column;gap:1px}
  .nq-tagpop-up{top:auto;bottom:calc(100% + 4px)}
  .nq-tagpop-row{display:flex;align-items:center;border:0;background:transparent;color:var(--ink);text-align:left;
    padding:6px 10px;font-size:12.5px;border-radius:5px;cursor:pointer}
  .nq-tagpop-row:hover{background:var(--accent-2);color:var(--accent)}
  .nq-tagpop-empty{padding:6px 10px;color:var(--ink-3);font-size:12px}

  .nq-section-h{display:flex;align-items:center;gap:6px;
    font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
    color:var(--ink-3);margin-top:6px}
  .nq-section-h>.mono{margin-left:2px;color:var(--ink-4)}

  /* Attachments */
  .nq-atts-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
    gap:8px}
  .nq-att{display:flex;align-items:center;gap:10px;
    padding:10px 12px;border:1px solid var(--line-2);border-radius:8px;
    background:var(--panel);position:relative;cursor:pointer;
    transition:border-color .12s}
  .nq-att:hover{border-color:var(--ink-4)}
  .nq-att-mark{width:32px;height:32px;border-radius:6px;display:grid;place-items:center;
    color:var(--ink);background:var(--panel-2);flex-shrink:0}
  .nq-att-pdf .nq-att-mark{background:rgba(220,38,38,.08);color:#b91c1c}
  .nq-att-image .nq-att-mark{background:rgba(20,150,80,.08);color:#15803d}
  .nq-att-video .nq-att-mark{background:rgba(124,58,237,.08);color:#7c3aed}
  [data-theme="dark"] .nq-att-pdf .nq-att-mark{background:rgba(239,68,68,.15);color:#ef6868}
  [data-theme="dark"] .nq-att-image .nq-att-mark{background:rgba(34,197,94,.15);color:#86efac}
  [data-theme="dark"] .nq-att-video .nq-att-mark{background:rgba(167,139,250,.18);color:#c4b5fd}
  .nq-att-meta{flex:1;min-width:0}
  .nq-att-name{font-size:12.5px;font-weight:500;color:var(--ink);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .nq-att-sub{font-size:10.5px;color:var(--ink-3);letter-spacing:.04em}
  .nq-att-x{position:absolute;top:6px;right:6px;border:0;background:transparent;
    color:var(--ink-4);padding:3px;border-radius:4px;cursor:pointer;
    opacity:0;transition:opacity .12s}
  .nq-att:hover .nq-att-x{opacity:1}
  .nq-att-x:hover{color:var(--danger);background:rgba(0,0,0,.05)}

  /* Timeline */
  .nq-tl{position:relative;padding:2px 0 8px 18px;display:flex;flex-direction:column;gap:8px}
  .nq-tl-rail{position:absolute;left:22px;top:8px;bottom:8px;width:1px;
    background:linear-gradient(to bottom,
      transparent 0, var(--line-2) 12px, var(--line-2) calc(100% - 12px), transparent 100%)}
  .nq-tl-empty{color:var(--ink-3);font-size:13px;padding:8px 12px}
  .nq-tl-day{display:flex;align-items:center;gap:12px;padding:6px 0 2px;position:relative}
  .nq-tl-day-l{font-size:11px;font-weight:600;letter-spacing:.06em;
    text-transform:uppercase;color:var(--ink-3)}
  .nq-tl-dot{position:relative;width:9px;height:9px;border-radius:50%;
    background:var(--panel);border:1.5px solid var(--ink-4);flex-shrink:0;z-index:1}
  .nq-tl-dot-day{background:var(--accent);border-color:var(--accent);width:8px;height:8px}
  .nq-tl-entry{display:grid;grid-template-columns:9px 56px 1fr 20px;gap:12px;
    align-items:start;padding:6px 4px 6px 0;position:relative;border-radius:6px}
  .nq-tl-entry:hover{background:rgba(0,0,0,.025)}
  [data-theme="dark"] .nq-tl-entry:hover{background:rgba(255,255,255,.04)}
  .nq-tl-entry>.nq-tl-dot{margin-top:6px}
  .nq-tl-time{font-size:11.5px;color:var(--ink-3);padding-top:3px;line-height:1.4}
  .nq-tl-body{display:flex;flex-direction:column;gap:5px;align-items:flex-start;min-width:0;width:100%}
  .nq-tl-text{font-size:14px;color:var(--ink);line-height:1.55;word-break:break-word;width:100%}
  .nq-tl-x{border:0;background:transparent;color:var(--ink-4);cursor:pointer;
    padding:3px;border-radius:4px;opacity:0;transition:opacity .12s;margin-top:4px}
  .nq-tl-entry:hover .nq-tl-x{opacity:1}
  .nq-tl-x:hover{color:var(--danger);background:rgba(0,0,0,.05)}

  /* Inline-edit textarea inside a timeline entry */
  .nq-tl-edit{width:100%;border:1px solid var(--accent-3);border-radius:8px;
    background:var(--panel);padding:9px 11px;font:14px/1.5 var(--font-body);
    color:var(--ink);outline:none;resize:vertical;min-height:60px}
  .nq-tl-edit:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-2)}
  .nq-tl-edit-bar{display:flex;gap:6px;margin-top:4px;align-self:flex-end}

  /* Markdown body */
  .nq-md p{margin:0 0 .5em}
  .nq-md p:last-child{margin-bottom:0}
  .nq-md h1,.nq-md h2,.nq-md h3{margin:.4em 0 .3em;font-weight:600;letter-spacing:-0.01em}
  .nq-md h1{font-size:1.4em}
  .nq-md h2{font-size:1.2em}
  .nq-md h3{font-size:1.05em}
  .nq-md ul,.nq-md ol{margin:0 0 .5em;padding-left:1.4em}
  .nq-md li{margin:.1em 0}
  .nq-md code{font-family:var(--font-mono);font-size:.92em;
    background:var(--panel-2);padding:1px 5px;border-radius:4px}
  .nq-md pre{background:var(--panel-2);padding:10px 12px;border-radius:6px;
    overflow-x:auto;margin:.4em 0}
  .nq-md pre code{background:transparent;padding:0;border-radius:0}
  .nq-md blockquote{margin:.3em 0;padding:2px 0 2px 10px;
    border-left:2px solid var(--line-2);color:var(--ink-2)}
  .nq-md a{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
  .nq-md a:hover{filter:brightness(1.1)}
  .nq-md strong{font-weight:600}
  .nq-md hr{border:0;border-top:1px solid var(--line);margin:.6em 0}
  .nq-md img{max-width:100%;border-radius:6px}
  .nq-md table{border-collapse:collapse;margin:.4em 0}
  .nq-md th,.nq-md td{border:1px solid var(--line);padding:4px 8px}

  /* Format toolbar */
  .nq-fmt{display:flex;align-items:center;gap:1px;
    border:1px solid var(--line-2);background:var(--panel);border-radius:8px;
    padding:3px;align-self:flex-start;flex-wrap:wrap}
  .nq-fmt-btn{display:inline-flex;align-items:center;justify-content:center;
    min-width:26px;height:24px;padding:0 7px;border:0;background:transparent;
    color:var(--ink-2);font-size:12px;border-radius:5px;cursor:pointer;line-height:1}
  .nq-fmt-btn:hover{background:var(--accent-2);color:var(--accent)}
  .nq-fmt-btn b{font-weight:700}
  .nq-fmt-btn i{font-style:italic}
  .nq-fmt-btn s{text-decoration:line-through}
  .nq-fmt-div{width:1px;height:14px;background:var(--line);margin:0 3px}

  /* Body editor */
  .nq-body-toolbar{display:flex;align-items:center;justify-content:space-between;
    gap:8px;flex-wrap:wrap}
  .nq-body-tools{display:flex;align-items:center;gap:6px}
  .nq-comment-btn{display:inline-flex;align-items:center;gap:6px;
    padding:6px 13px;border:0;color:#fff;
    background:linear-gradient(180deg,var(--accent) 0%,var(--accent-deep) 100%);
    font-size:12px;font-weight:600;border-radius:7px;cursor:pointer;
    line-height:1;white-space:nowrap;letter-spacing:.01em;
    box-shadow:0 1px 0 rgba(255,255,255,.2) inset,0 2px 6px rgba(37,99,235,.28);
    transition:transform .12s,box-shadow .12s}
  .nq-comment-btn:hover{transform:translateY(-1px);
    box-shadow:0 1px 0 rgba(255,255,255,.2) inset,0 6px 14px rgba(37,99,235,.38)}
  .nq-comment-btn:active{transform:translateY(0)}
  .nq-comment-btn svg{color:#fff}
  .nq-fmt-toggle{display:inline-flex;align-items:center;
    padding:5px 11px;border:1px solid var(--line-2);background:var(--panel);
    color:var(--ink-2);font-size:12px;border-radius:6px;cursor:pointer;
    font-weight:500}
  .nq-fmt-toggle:hover{border-color:var(--ink-4);color:var(--ink)}
  .nq-fmt-toggle.is-on{background:var(--accent-2);color:var(--accent);
    border-color:var(--accent-3)}
  .nq-body-ta{width:100%;flex:1;min-height:340px;
    border:1px solid var(--line-2);border-radius:10px;background:var(--panel);
    padding:14px 16px;font:14.5px/1.6 var(--font-body);color:var(--ink);
    outline:none;resize:vertical}
  .nq-body-ta:focus{border-color:var(--accent);
    box-shadow:0 0 0 3px var(--accent-2)}
  .nq-body-ta::placeholder{color:var(--ink-3)}
  .nq-body-preview{width:100%;min-height:340px;padding:14px 16px;
    border:1px solid var(--line-2);border-radius:10px;background:var(--panel);
    font-size:14.5px;line-height:1.6}

  /* Composer */
  .nq-ed-comp{border-top:1px solid var(--line);background:var(--panel);
    padding:10px 28px 16px;display:flex;flex-direction:column;gap:8px}
  .nq-comp-ta{width:100%;border:1px solid var(--line-2);border-radius:10px;
    background:var(--bg);padding:11px 13px;font:14px/1.5 var(--font-body);
    resize:vertical;min-height:54px;max-height:240px;outline:none;color:var(--ink)}
  .nq-comp-ta:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-2)}
  .nq-comp-ta::placeholder{color:var(--ink-3)}
  .nq-comp-bar{display:flex;align-items:center;justify-content:space-between;gap:8px}
  .nq-comp-left{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
  .nq-comp-btn{display:inline-flex;align-items:center;gap:5px;
    padding:5px 10px;border-radius:6px;border:1px solid var(--line-2);
    background:var(--panel);color:var(--ink-2);font-size:12px;cursor:pointer;white-space:nowrap}
  .nq-comp-btn:hover{border-color:var(--ink-4);color:var(--ink)}
  .nq-comp-divider{width:1px;height:14px;background:var(--line-2);margin:0 2px}
  .nq-comp-stamp{display:inline-flex;align-items:center;gap:5px;
    color:var(--ink-3);font-size:11px;white-space:nowrap}
  .nq-comp-send{display:inline-flex;align-items:center;gap:6px;
    padding:7px 14px;border-radius:7px;border:0;white-space:nowrap;
    background:linear-gradient(180deg,var(--accent) 0%,var(--accent-deep) 100%);
    color:#fff;font-weight:600;font-size:12.5px;cursor:pointer;
    box-shadow:0 1px 0 rgba(255,255,255,.18) inset,var(--shadow-1);
    transition:transform .12s,box-shadow .12s,opacity .15s}
  .nq-comp-send:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
  .nq-comp-send:not(:disabled):hover{transform:translateY(-1px);box-shadow:var(--shadow-2)}
  .nq-comp-tagdrop details{position:relative}
  .nq-comp-tagdrop summary{list-style:none;cursor:pointer}
  .nq-comp-tagdrop summary::-webkit-details-marker{display:none}
  .nq-comp-tagdrop details[open] .nq-tagpop{display:flex}

  /* Drop overlay */
  .nq-drop{position:absolute;inset:0;background:var(--accent-2);
    display:grid;place-items:center;border:2px dashed var(--accent);
    border-radius:0;pointer-events:none;z-index:30}
  .nq-drop-card{display:flex;flex-direction:column;align-items:center;gap:8px;
    background:var(--panel);color:var(--accent);padding:18px 22px;border-radius:10px;
    font-weight:600;font-size:13.5px;
    box-shadow:0 8px 30px rgba(0,0,0,.12)}

  /* Empty editor */
  .nq-ed-empty{align-items:center;justify-content:center;color:var(--ink-3);
    display:flex;gap:8px;flex-direction:column}
  .nq-empty{padding:32px 16px;text-align:center;color:var(--ink-3)}
  .nq-empty-mark{width:36px;height:36px;border-radius:50%;
    border:1.5px dashed var(--line-2);margin:0 auto 10px}
  .nq-empty-mark.big{width:48px;height:48px;border-color:var(--ink-4)}
  .nq-empty-t{font-weight:600;color:var(--ink-2);font-size:13.5px;margin-bottom:2px}
  .nq-empty-s{font-size:12.5px}

  /* Toast */
  .nq-toast{position:fixed;left:50%;bottom:18px;transform:translateX(-50%);
    background:var(--ink);color:var(--bg);padding:6px 14px;border-radius:20px;
    font-size:12px;z-index:50;box-shadow:0 4px 14px rgba(0,0,0,.18)}
  .nq-toast.error{background:var(--danger);color:#fff}

  /* Attachment viewer */
  .nq-viewer{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.78);
    display:flex;flex-direction:column;
    -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
  .nq-viewer-bar{display:flex;align-items:center;gap:14px;padding:10px 14px;
    color:#fff;background:rgba(0,0,0,.4);border-bottom:1px solid rgba(255,255,255,.08)}
  .nq-viewer-name{flex:1;font-size:13px;font-weight:500;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .nq-viewer-link{color:rgba(255,255,255,.8);text-decoration:none;font-size:12px}
  .nq-viewer-link:hover{color:#fff}
  .nq-viewer-x{border:0;background:transparent;color:rgba(255,255,255,.85);
    padding:6px;border-radius:6px;cursor:pointer;display:inline-flex}
  .nq-viewer-x:hover{background:rgba(255,255,255,.12);color:#fff}
  .nq-viewer-stage{flex:1;display:flex;align-items:center;justify-content:center;
    overflow:hidden;padding:10px}
  .nq-viewer-stage img,.nq-viewer-stage video{max-width:100%;max-height:100%;
    object-fit:contain;border-radius:6px;background:#000}
  .nq-viewer-stage iframe{width:100%;height:100%;border:0;background:#fff;border-radius:6px}
  .nq-viewer-fallback{color:#fff;text-align:center;display:flex;flex-direction:column;
    gap:10px;align-items:center}
  .nq-viewer-fallback a{color:#9ec5ff}

  /* ───── Context menu ───── */
  .nq-ctx{position:fixed;z-index:80;min-width:210px;max-width:280px;
    background:var(--panel);border:1px solid var(--line-2);border-radius:9px;
    box-shadow:0 12px 32px rgba(0,0,0,.14),0 2px 6px rgba(0,0,0,.06);
    padding:4px;display:flex;flex-direction:column;
    font-size:13px}
  .nq-ctx-h{padding:6px 10px 2px;font-size:10.5px;font-weight:600;
    letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3)}
  .nq-ctx-div{height:1px;background:var(--line);margin:4px 6px}
  .nq-ctx-item{display:flex;align-items:center;gap:9px;
    padding:6px 10px;border-radius:6px;border:0;background:transparent;
    color:var(--ink);text-align:left;cursor:pointer;width:100%;
    font:inherit}
  .nq-ctx-item:hover:not(:disabled){background:var(--accent-2);color:var(--accent)}
  .nq-ctx-item:disabled{opacity:.45;cursor:not-allowed}
  .nq-ctx-item.danger{color:var(--danger)}
  .nq-ctx-item.danger:hover:not(:disabled){background:rgba(220,38,38,.08);color:var(--danger)}
  [data-theme="dark"] .nq-ctx-item.danger:hover:not(:disabled){background:rgba(239,68,68,.12)}
  .nq-ctx-icon{display:inline-flex;width:15px;align-items:center;justify-content:center;
    color:var(--ink-3);flex-shrink:0}
  .nq-ctx-item:hover:not(:disabled) .nq-ctx-icon{color:var(--accent)}
  .nq-ctx-item.danger .nq-ctx-icon{color:var(--danger)}
  .nq-ctx-lbl{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .nq-ctx-kbd{color:var(--ink-3);font-size:11px;padding:1px 5px;border-radius:3px;
    background:var(--panel-2);border:1px solid var(--line)}

  /* Responsive guard */
  @media (max-width: 900px){
    .nq-app{grid-template-columns: var(--list-w) 1fr}
    .nq-sb{display:none}
  }

  /* ───── Auth screens ───── */
  .nq-auth{position:fixed;inset:0;display:grid;place-items:center;
    background:radial-gradient(circle at 20% 0%, rgba(37,99,235,.08) 0%, transparent 50%),
               radial-gradient(circle at 80% 100%, rgba(124,58,237,.08) 0%, transparent 50%),
               var(--bg);
    padding:20px;overflow:auto;z-index:100}
  .nq-auth-card{width:min(420px,100%);background:var(--panel);
    border:1px solid var(--line-2);border-radius:14px;padding:28px 28px 22px;
    box-shadow:var(--shadow-3);
    display:flex;flex-direction:column;gap:14px}
  .nq-auth-brand{display:flex;align-items:center;gap:10px;margin-bottom:4px}
  .nq-auth-brand .nq-brand-name{font-size:15px}
  .nq-auth-h{font-size:20px;font-weight:600;letter-spacing:-0.01em;color:var(--ink);
    margin:0}
  .nq-auth-sub{font-size:13px;color:var(--ink-3);line-height:1.5;margin:0}
  .nq-auth-sub b{color:var(--ink-2);font-weight:600}
  .nq-auth-form{display:flex;flex-direction:column;gap:10px;margin-top:4px}
  .nq-auth-lbl{display:flex;flex-direction:column;gap:5px;font-size:12px;
    font-weight:500;color:var(--ink-2)}
  .nq-auth-in{border:1px solid var(--line-2);background:var(--bg);
    color:var(--ink);padding:10px 12px;border-radius:8px;font-size:14px;outline:none;
    transition:border-color .12s,box-shadow .12s}
  .nq-auth-in:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-2)}
  .nq-auth-otp{letter-spacing:.4em;font-size:18px;text-align:center}
  .nq-auth-hint{font-size:11.5px;color:var(--ink-3);line-height:1.4;margin-top:2px}
  .nq-auth-btn{margin-top:4px;padding:11px 14px;border:0;border-radius:8px;
    color:#fff;font-weight:600;font-size:13.5px;cursor:pointer;
    background:linear-gradient(180deg,var(--accent) 0%,var(--accent-deep) 100%);
    box-shadow:0 1px 0 rgba(255,255,255,.2) inset,0 4px 12px rgba(37,99,235,.30);
    transition:transform .1s,box-shadow .12s,opacity .15s}
  .nq-auth-btn:not(:disabled):hover{transform:translateY(-1px);
    box-shadow:0 1px 0 rgba(255,255,255,.2) inset,0 8px 18px rgba(37,99,235,.38)}
  .nq-auth-btn:disabled{opacity:.5;cursor:not-allowed}
  .nq-auth-err{color:var(--danger);font-size:12.5px;
    background:rgba(220,38,38,.08);border:1px solid rgba(220,38,38,.18);
    padding:8px 11px;border-radius:7px;line-height:1.4}
  [data-theme="dark"] .nq-auth-err{background:rgba(239,68,68,.12)}
  .nq-auth-notice{color:var(--success);font-size:12.5px;
    background:rgba(22,163,74,.08);border:1px solid rgba(22,163,74,.18);
    padding:8px 11px;border-radius:7px;line-height:1.4}
  .nq-auth-link{align-self:center;border:0;background:transparent;color:var(--accent);
    font-size:12.5px;cursor:pointer;padding:4px 8px;border-radius:5px}
  .nq-auth-link:hover{background:var(--accent-2)}
  .nq-auth-link-muted{color:var(--ink-3);margin-top:2px}
  .nq-auth-link-muted:hover{color:var(--danger);background:rgba(220,38,38,.08)}
  .nq-auth-loading{color:var(--ink-3);font-size:13px;text-align:center;padding:12px}
  .nq-auth-steps{margin:0;padding-left:20px;color:var(--ink-2);font-size:13px;
    line-height:1.55;display:flex;flex-direction:column;gap:2px}
  .nq-auth-qr{display:grid;place-items:center;padding:14px;background:#fff;
    border:1px solid var(--line-2);border-radius:10px;align-self:center}
  .nq-auth-qr svg{display:block;width:180px;height:180px}
  .nq-auth-secret{font-size:13px;text-align:center;color:var(--ink-2);
    padding:8px 10px;background:var(--panel-2);border:1px solid var(--line-2);
    border-radius:6px;word-break:break-all;letter-spacing:.05em}

  /* Sign-out button inside Tweaks */
  .nq-signout-btn{padding:7px 12px;border-radius:7px;border:1px solid var(--line-2);
    background:var(--panel);color:var(--ink-2);font-size:12.5px;cursor:pointer;
    transition:border-color .12s,color .12s,background .12s}
  .nq-signout-btn:hover:not(:disabled){border-color:var(--danger);color:var(--danger);
    background:rgba(220,38,38,.06)}
  [data-theme="dark"] .nq-signout-btn:hover:not(:disabled){background:rgba(239,68,68,.10)}
  .nq-signout-btn:disabled{opacity:.6;cursor:wait}
`}</style>; }
