/**
 * KnowledgeBase.jsx — WPSiteBeam In-App Wiki + Customer KB
 * Repo: wpsitebeam-app / design/KnowledgeBase.jsx
 * Version: v1.0.0 — 2026-05-26
 *
 * Two-mode panel driven by role:
 *   customer        → /kb endpoint (visibility='customer_visible' articles only)
 *   super_admin     → /internal-docs endpoint (all articles, full CRUD editor)
 *   internal_partner/dev_admin → /internal-docs (read their role-gated articles)
 *
 * Requires:
 *   - kb-visibility-migration.sql applied in Supabase
 *   - /kb + /kb/:slug endpoints live in server.js (kb-server-patch.js)
 *   - marked.min.js available via CDN for markdown rendering
 *   - window.WPSBD.getToken() for auth
 *   - window.WPSBD.apiBase for Railway URL
 *
 * Design: WCAG 2.1 AA, light + dark via CSS variables, 768/480 breakpoints,
 *         44px touch targets, aria + keyboard navigation, empty states.
 *
 * IIFE pattern: window.KnowledgeBase exposed for index.html module loader.
 */

(function () {
  'use strict';

  const VERSION = '1.0.0';
  console.log('[KnowledgeBase] v' + VERSION + ' loaded');

  /* ─── Constants ──────────────────────────────────────────────── */
  const CATEGORIES = {
    getting_started: 'Getting Started',
    scanner:         'Scanner & Audits',
    accessibility:   'Accessibility',
    seo:             'SEO & Rankings',
    migrations:      'Migrations',
    images:          'Image Tools',
    billing:         'Billing & Plans',
    api:             'API & Integrations',
    security:        'Security',
    faq:             'FAQ',
    dev:             'Dev Notes',
    internal:        'Internal Docs',
    architecture:    'Architecture',
    decisions:       'Decisions',
    onboarding:      'Onboarding',
  };

  const VISIBILITY_LABELS = {
    internal_only:      'Internal only',
    customer_visible:   'Customer visible',
    public:             'Public',
  };

  const ROLE_COLORS = {
    super_admin:      'var(--green)',
    dev_admin:        'var(--blue)',
    internal_partner: 'var(--warn)',
    customer:         'var(--dim)',
  };

  /* ─── Helpers ────────────────────────────────────────────────── */
  function getApiBase() {
    return (window.WPSBD && window.WPSBD.apiBase) || 'https://api.wpsitebeam.io';
  }

  function getToken() {
    return window.WPSBD && window.WPSBD.getToken ? window.WPSBD.getToken() : '';
  }

  function getRole() {
    return (window.WPSBD && window.WPSBD.state && window.WPSBD.state.role) || 'customer';
  }

  function isAdmin() {
    const r = getRole();
    return r === 'super_admin' || r === 'dev_admin' || r === 'internal_partner';
  }

  async function apiFetch(path, opts = {}) {
    const token = getToken();
    const res = await fetch(getApiBase() + path, {
      ...opts,
      headers: {
        'Content-Type': 'application/json',
        ...(token ? { Authorization: 'Bearer ' + token } : {}),
        ...(opts.headers || {}),
      },
    });
    if (!res.ok) {
      const body = await res.json().catch(() => ({}));
      throw new Error(body.error || 'Request failed ' + res.status);
    }
    return res.json();
  }

  function renderMarkdown(md) {
    if (window.marked) {
      return window.marked.parse(md || '', { breaks: true, gfm: true });
    }
    /* Fallback: basic escaping if marked not loaded */
    return '<pre style="white-space:pre-wrap">' +
      (md || '').replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</pre>';
  }

  function timeAgo(iso) {
    if (!iso) return '';
    const diff = Date.now() - new Date(iso).getTime();
    const m = Math.floor(diff / 60000);
    if (m < 1) return 'just now';
    if (m < 60) return m + 'm ago';
    const h = Math.floor(m / 60);
    if (h < 24) return h + 'h ago';
    const d = Math.floor(h / 24);
    if (d < 30) return d + 'd ago';
    return new Date(iso).toLocaleDateString();
  }

  function categoryLabel(cat) {
    return CATEGORIES[cat] || (cat ? cat.charAt(0).toUpperCase() + cat.slice(1).replace(/_/g, ' ') : 'General');
  }

  /* ─── State ──────────────────────────────────────────────────── */
  const state = {
    articles:     [],
    categories:   {},
    loading:      false,
    error:        null,
    activeSlug:   null,
    activeArticle: null,
    activeCategory: null,
    searchQuery:  '',
    editorOpen:   false,
    editorArticle: null,  // null = new, article = edit
    editorSaving: false,
    editorError:  null,
    revisions:    [],
    revisionsOpen: false,
  };

  /* ─── Event bus ──────────────────────────────────────────────── */
  let renderFn = null;
  function setState(patch) {
    Object.assign(state, patch);
    if (renderFn) renderFn();
  }

  /* ─── Data fetching ──────────────────────────────────────────── */
  async function loadArticleList() {
    setState({ loading: true, error: null });
    try {
      const endpoint = isAdmin() ? '/internal-docs' : '/kb';
      const data = await apiFetch(endpoint);
      setState({
        articles: data.articles || [],
        categories: data.categories || {},
        loading: false,
      });
    } catch (e) {
      setState({ loading: false, error: e.message });
    }
  }

  async function loadArticle(slug) {
    setState({ loading: true, error: null, activeArticle: null });
    try {
      const endpoint = isAdmin() ? '/internal-docs/' + slug : '/kb/' + slug;
      const article = await apiFetch(endpoint);
      setState({ loading: false, activeSlug: slug, activeArticle: article });
    } catch (e) {
      setState({ loading: false, error: e.message });
    }
  }

  async function loadRevisions(id) {
    try {
      const data = await apiFetch('/internal-docs/' + id + '/revisions');
      setState({ revisions: data.revisions || [], revisionsOpen: true });
    } catch (e) {
      if (window.toast) window.toast('Failed to load revisions', 'error');
    }
  }

  async function saveArticle(payload) {
    setState({ editorSaving: true, editorError: null });
    try {
      const isNew = !state.editorArticle;
      const data = await apiFetch(
        isNew ? '/internal-docs' : '/internal-docs/' + state.editorArticle.id,
        { method: isNew ? 'POST' : 'PUT', body: JSON.stringify(payload) }
      );
      setState({ editorOpen: false, editorSaving: false, editorArticle: null });
      await loadArticleList();
      if (window.toast) window.toast(isNew ? 'Article created' : 'Article updated', 'success');
      setState({ activeSlug: data.slug });
      await loadArticle(data.slug);
    } catch (e) {
      setState({ editorSaving: false, editorError: e.message });
    }
  }

  async function archiveArticle(id) {
    try {
      await apiFetch('/internal-docs/' + id, { method: 'DELETE' });
      setState({ activeSlug: null, activeArticle: null });
      await loadArticleList();
      if (window.toast) window.toast('Article archived', 'success');
    } catch (e) {
      if (window.toast) window.toast('Archive failed: ' + e.message, 'error');
    }
  }

  /* ─── Filtered articles ──────────────────────────────────────── */
  function getFiltered() {
    let list = state.articles;
    if (state.activeCategory) {
      list = list.filter(a => a.category === state.activeCategory);
    }
    if (state.searchQuery.trim()) {
      const q = state.searchQuery.toLowerCase();
      list = list.filter(a =>
        a.title.toLowerCase().includes(q) ||
        (a.category || '').toLowerCase().includes(q)
      );
    }
    return list;
  }

  /* ─── CSS ────────────────────────────────────────────────────── */
  const CSS = `
.kb-root {
  display: flex;
  height: calc(100vh - 120px);
  min-height: 500px;
  gap: 0;
  font-size: 14px;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
/* Sidebar */
.kb-sidebar {
  width: 240px;
  min-width: 200px;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  background: rgba(0,0,0,.03);
  flex-shrink: 0;
}
.kb-sidebar-header {
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--border);
}
.kb-sidebar-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .06em;
  margin: 0 0 8px;
}
.kb-search {
  width: 100%;
  padding: 6px 10px;
  font-size: 13px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  box-sizing: border-box;
}
.kb-search:focus { outline: 2px solid var(--blue); outline-offset: 1px; }
.kb-nav { flex: 1; overflow-y: auto; padding: 8px 0; }
.kb-cat-btn {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding: 7px 16px;
  font-size: 13px;
  color: var(--text);
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  border-radius: 0;
  min-height: 36px;
  gap: 8px;
}
.kb-cat-btn:hover { background: rgba(0,0,0,.05); }
.kb-cat-btn.active {
  background: rgba(6,182,212,.12);
  color: var(--cyan, #06b6d4);
  font-weight: 500;
}
.kb-cat-count {
  font-size: 11px;
  color: var(--dim);
  background: rgba(0,0,0,.08);
  border-radius: 10px;
  padding: 1px 6px;
  min-width: 20px;
  text-align: center;
}
.kb-sidebar-footer {
  border-top: 1px solid var(--border);
  padding: 10px 16px;
}
.kb-new-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 500;
  background: var(--cyan, #06b6d4);
  color: #fff;
  border: none;
  border-radius: 7px;
  cursor: pointer;
  min-height: 36px;
  justify-content: center;
}
.kb-new-btn:hover { opacity: .9; }

/* Article list pane */
.kb-list-pane {
  width: 280px;
  min-width: 220px;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}
.kb-list-header {
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.kb-list { flex: 1; overflow-y: auto; }
.kb-article-row {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid rgba(0,0,0,.05);
  min-height: 44px;
  justify-content: center;
}
.kb-article-row:hover { background: rgba(0,0,0,.04); }
.kb-article-row.active { background: rgba(6,182,212,.1); border-left: 3px solid var(--cyan,#06b6d4); padding-left: 11px; }
.kb-article-row-title { font-size: 13px; font-weight: 500; color: var(--text); }
.kb-article-row-meta { font-size: 11px; color: var(--dim); display: flex; gap: 8px; align-items: center; }
.kb-vis-badge {
  font-size: 10px;
  padding: 1px 5px;
  border-radius: 4px;
  font-weight: 500;
}
.kb-vis-internal { background: rgba(239,68,68,.12); color: var(--red,#ef4444); }
.kb-vis-customer { background: rgba(16,185,129,.12); color: var(--green,#10b981); }
.kb-vis-public   { background: rgba(6,182,212,.12);  color: var(--cyan,#06b6d4); }

/* Article detail pane */
.kb-detail-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}
.kb-detail-header {
  padding: 12px 20px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.kb-detail-title { font-size: 18px; font-weight: 600; flex: 1; }
.kb-detail-actions { display: flex; gap: 8px; flex-shrink: 0; }
.kb-action-btn {
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 500;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  min-height: 32px;
  white-space: nowrap;
}
.kb-action-btn:hover { background: rgba(0,0,0,.06); }
.kb-action-btn.danger { color: var(--red,#ef4444); border-color: var(--red,#ef4444); }
.kb-detail-meta {
  padding: 8px 20px;
  font-size: 12px;
  color: var(--dim);
  border-bottom: 1px solid var(--border);
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  align-items: center;
}
.kb-detail-body {
  flex: 1;
  overflow-y: auto;
  padding: 20px 24px;
  line-height: 1.7;
}
/* Markdown body */
.kb-md h1 { font-size: 22px; font-weight: 600; margin: 0 0 16px; }
.kb-md h2 { font-size: 17px; font-weight: 600; margin: 24px 0 10px; border-bottom: 1px solid var(--border); padding-bottom: 6px; }
.kb-md h3 { font-size: 15px; font-weight: 600; margin: 18px 0 8px; }
.kb-md p  { margin: 0 0 12px; }
.kb-md ul, .kb-md ol { margin: 0 0 12px; padding-left: 20px; }
.kb-md li { margin-bottom: 4px; }
.kb-md code { font-family: monospace; font-size: 13px; background: rgba(0,0,0,.07); padding: 1px 5px; border-radius: 4px; }
.kb-md pre { background: rgba(0,0,0,.07); padding: 12px 14px; border-radius: 8px; overflow-x: auto; margin: 0 0 14px; }
.kb-md pre code { background: none; padding: 0; }
.kb-md blockquote { border-left: 3px solid var(--cyan,#06b6d4); margin: 0 0 12px; padding: 8px 14px; background: rgba(6,182,212,.06); border-radius: 0 6px 6px 0; }
.kb-md table { width: 100%; border-collapse: collapse; margin: 0 0 14px; font-size: 13px; }
.kb-md th { background: rgba(0,0,0,.06); padding: 7px 10px; text-align: left; font-weight: 600; border-bottom: 1px solid var(--border); }
.kb-md td { padding: 7px 10px; border-bottom: 1px solid rgba(0,0,0,.05); }
.kb-md a { color: var(--cyan,#06b6d4); }

/* Empty state */
.kb-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  color: var(--dim);
  padding: 40px;
  text-align: center;
}
.kb-empty-icon { font-size: 40px; opacity: .4; }
.kb-empty-title { font-size: 16px; font-weight: 500; color: var(--text); }
.kb-empty-sub { font-size: 13px; max-width: 280px; line-height: 1.5; }

/* Loading spinner */
.kb-spinner {
  width: 28px; height: 28px;
  border: 3px solid var(--border);
  border-top-color: var(--cyan,#06b6d4);
  border-radius: 50%;
  animation: kb-spin .8s linear infinite;
}
@keyframes kb-spin { to { transform: rotate(360deg); } }

/* Editor overlay */
.kb-editor-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0,.5);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 32px 16px;
  z-index: 100;
  overflow-y: auto;
}
.kb-editor-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 100%;
  max-width: 780px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.kb-editor-title { font-size: 17px; font-weight: 600; }
.kb-field { display: flex; flex-direction: column; gap: 5px; }
.kb-label { font-size: 12px; font-weight: 600; color: var(--dim); text-transform: uppercase; letter-spacing: .04em; }
.kb-input {
  padding: 8px 10px; font-size: 13px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--text);
  width: 100%; box-sizing: border-box;
}
.kb-input:focus { outline: 2px solid var(--cyan,#06b6d4); outline-offset: 1px; }
.kb-textarea {
  padding: 10px 12px; font-size: 13px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--text);
  width: 100%; box-sizing: border-box;
  min-height: 320px; resize: vertical; font-family: monospace;
  line-height: 1.6;
}
.kb-textarea:focus { outline: 2px solid var(--cyan,#06b6d4); outline-offset: 1px; }
.kb-select {
  padding: 7px 10px; font-size: 13px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--text);
}
.kb-editor-row { display: flex; gap: 12px; }
.kb-editor-row .kb-field { flex: 1; }
.kb-editor-actions { display: flex; gap: 8px; justify-content: flex-end; }
.kb-btn-cancel { padding: 8px 16px; font-size: 13px; border: 1px solid var(--border); border-radius: 6px; background: transparent; color: var(--text); cursor: pointer; min-height: 36px; }
.kb-btn-save { padding: 8px 16px; font-size: 13px; font-weight: 500; border: none; border-radius: 6px; background: var(--cyan,#06b6d4); color: #fff; cursor: pointer; min-height: 36px; }
.kb-btn-save:disabled { opacity: .5; cursor: not-allowed; }
.kb-editor-error { font-size: 12px; color: var(--red,#ef4444); padding: 6px 10px; background: rgba(239,68,68,.08); border-radius: 6px; }

/* Revisions drawer */
.kb-revisions {
  position: absolute; right: 0; top: 0; bottom: 0;
  width: 360px;
  background: var(--panel);
  border-left: 1px solid var(--border);
  display: flex; flex-direction: column;
  z-index: 50;
  box-shadow: -4px 0 16px rgba(0,0,0,.1);
}
.kb-revisions-header { padding: 14px 16px; border-bottom: 1px solid var(--border); font-weight: 600; display: flex; justify-content: space-between; align-items: center; }
.kb-revisions-list { flex: 1; overflow-y: auto; padding: 8px 0; }
.kb-revision-row { padding: 10px 16px; border-bottom: 1px solid rgba(0,0,0,.05); font-size: 13px; }
.kb-revision-who { font-weight: 500; }
.kb-revision-when { color: var(--dim); font-size: 12px; }
.kb-revision-summary { color: var(--dim); font-size: 12px; margin-top: 3px; }
.kb-close-btn { background: none; border: none; cursor: pointer; color: var(--dim); font-size: 18px; padding: 4px; min-height: 32px; min-width: 32px; border-radius: 4px; }
.kb-close-btn:hover { background: rgba(0,0,0,.08); }

/* Responsive */
@media (max-width: 768px) {
  .kb-root { flex-direction: column; height: auto; min-height: 400px; }
  .kb-sidebar { width: 100%; border-right: none; border-bottom: 1px solid var(--border); max-height: 160px; }
  .kb-list-pane { width: 100%; border-right: none; border-bottom: 1px solid var(--border); max-height: 220px; }
  .kb-nav { display: flex; flex-wrap: wrap; padding: 6px; gap: 4px; }
  .kb-cat-btn { width: auto; padding: 5px 10px; border-radius: 16px; border: 1px solid var(--border); }
  .kb-cat-btn.active { border-color: var(--cyan,#06b6d4); }
}
@media (max-width: 480px) {
  .kb-detail-header { flex-direction: column; }
  .kb-detail-actions { flex-wrap: wrap; }
  .kb-detail-meta { gap: 8px; }
}
`;

  /* ─── Component: Sidebar ─────────────────────────────────────── */
  function renderSidebar() {
    const catEntries = Object.entries(state.categories)
      .sort((a, b) => a[0].localeCompare(b[0]));

    const catItems = catEntries.map(([cat, count]) =>
      React.createElement('button', {
        key: cat,
        className: 'kb-cat-btn' + (state.activeCategory === cat ? ' active' : ''),
        onClick: () => setState({ activeCategory: state.activeCategory === cat ? null : cat }),
        'aria-pressed': state.activeCategory === cat,
      },
        React.createElement('span', null, categoryLabel(cat)),
        React.createElement('span', { className: 'kb-cat-count', 'aria-hidden': 'true' }, count)
      )
    );

    return React.createElement('div', { className: 'kb-sidebar', role: 'navigation', 'aria-label': 'KB categories' },
      React.createElement('div', { className: 'kb-sidebar-header' },
        React.createElement('p', { className: 'kb-sidebar-title' }, 'Knowledge Base'),
        React.createElement('input', {
          type: 'search',
          className: 'kb-search',
          placeholder: 'Search articles\u2026',
          value: state.searchQuery,
          onChange: e => setState({ searchQuery: e.target.value }),
          'aria-label': 'Search knowledge base',
        })
      ),
      React.createElement('nav', { className: 'kb-nav' },
        React.createElement('button', {
          className: 'kb-cat-btn' + (!state.activeCategory ? ' active' : ''),
          onClick: () => setState({ activeCategory: null }),
          'aria-pressed': !state.activeCategory,
        },
          React.createElement('span', null, 'All articles'),
          React.createElement('span', { className: 'kb-cat-count', 'aria-hidden': 'true' }, state.articles.length)
        ),
        ...catItems
      ),
      isAdmin() && React.createElement('div', { className: 'kb-sidebar-footer' },
        React.createElement('button', {
          className: 'kb-new-btn',
          onClick: () => setState({ editorOpen: true, editorArticle: null }),
          'aria-label': 'Create new article',
        }, '+ New article')
      )
    );
  }

  /* ─── Component: Article list ────────────────────────────────── */
  function renderListPane() {
    const filtered = getFiltered();
    const heading = state.activeCategory
      ? categoryLabel(state.activeCategory)
      : 'All articles';

    const rows = filtered.length === 0
      ? React.createElement('div', { className: 'kb-empty', role: 'status' },
          React.createElement('div', { className: 'kb-empty-icon', 'aria-hidden': 'true' }, '\uD83D\uDCCB'),
          React.createElement('div', { className: 'kb-empty-title' }, 'No articles found'),
          React.createElement('div', { className: 'kb-empty-sub' },
            state.searchQuery ? 'Try a different search term.' : 'No articles in this category yet.'
          )
        )
      : filtered.map(a => {
          const visCls = a.visibility === 'customer_visible' ? 'kb-vis-customer'
                       : a.visibility === 'public'           ? 'kb-vis-public'
                       : 'kb-vis-internal';
          return React.createElement('div', {
            key: a.slug,
            className: 'kb-article-row' + (state.activeSlug === a.slug ? ' active' : ''),
            onClick: () => loadArticle(a.slug),
            tabIndex: 0,
            role: 'button',
            'aria-pressed': state.activeSlug === a.slug,
            onKeyDown: e => { if (e.key === 'Enter' || e.key === ' ') loadArticle(a.slug); },
          },
            React.createElement('div', { className: 'kb-article-row-title' }, a.title),
            React.createElement('div', { className: 'kb-article-row-meta' },
              React.createElement('span', null, categoryLabel(a.category)),
              isAdmin() && a.visibility && React.createElement('span', { className: 'kb-vis-badge ' + visCls },
                VISIBILITY_LABELS[a.visibility] || a.visibility
              ),
              a.updated_at && React.createElement('span', null, timeAgo(a.updated_at))
            )
          );
        });

    return React.createElement('div', { className: 'kb-list-pane', role: 'region', 'aria-label': 'Article list' },
      React.createElement('div', { className: 'kb-list-header' }, heading, ' (', filtered.length, ')'),
      React.createElement('div', { className: 'kb-list', role: 'list' }, rows)
    );
  }

  /* ─── Component: Article detail ──────────────────────────────── */
  function renderDetailPane() {
    if (state.loading && !state.activeArticle) {
      return React.createElement('div', { className: 'kb-detail-pane' },
        React.createElement('div', { className: 'kb-empty', role: 'status', 'aria-live': 'polite' },
          React.createElement('div', { className: 'kb-spinner', 'aria-label': 'Loading article' })
        )
      );
    }

    if (state.error) {
      return React.createElement('div', { className: 'kb-detail-pane' },
        React.createElement('div', { className: 'kb-empty', role: 'alert' },
          React.createElement('div', { className: 'kb-empty-icon', 'aria-hidden': 'true' }, '\u26A0\uFE0F'),
          React.createElement('div', { className: 'kb-empty-title' }, 'Error'),
          React.createElement('div', { className: 'kb-empty-sub' }, state.error),
          React.createElement('button', { className: 'kb-action-btn', onClick: loadArticleList }, 'Retry')
        )
      );
    }

    if (!state.activeArticle) {
      return React.createElement('div', { className: 'kb-detail-pane' },
        React.createElement('div', { className: 'kb-empty' },
          React.createElement('div', { className: 'kb-empty-icon', 'aria-hidden': 'true' }, '\uD83D\uDCDA'),
          React.createElement('div', { className: 'kb-empty-title' }, 'Select an article'),
          React.createElement('div', { className: 'kb-empty-sub' }, 'Choose an article from the list to read it here.')
        )
      );
    }

    const a = state.activeArticle;
    const visCls = a.visibility === 'customer_visible' ? 'kb-vis-customer'
                 : a.visibility === 'public'           ? 'kb-vis-public'
                 : 'kb-vis-internal';

    return React.createElement('div', { className: 'kb-detail-pane', role: 'main' },
      React.createElement('div', { className: 'kb-detail-header' },
        React.createElement('h1', { className: 'kb-detail-title' }, a.title),
        isAdmin() && React.createElement('div', { className: 'kb-detail-actions' },
          React.createElement('button', {
            className: 'kb-action-btn',
            onClick: () => loadRevisions(a.id),
            'aria-label': 'View edit history',
          }, 'History'),
          React.createElement('button', {
            className: 'kb-action-btn',
            onClick: () => setState({ editorOpen: true, editorArticle: a }),
            'aria-label': 'Edit article',
          }, 'Edit'),
          React.createElement('button', {
            className: 'kb-action-btn danger',
            onClick: () => {
              if (window.confirm('Archive "' + a.title + '"? It will be hidden but not deleted.')) {
                archiveArticle(a.id);
              }
            },
            'aria-label': 'Archive article',
          }, 'Archive')
        )
      ),
      React.createElement('div', { className: 'kb-detail-meta' },
        React.createElement('span', null, categoryLabel(a.category)),
        a.view_count != null && React.createElement('span', null, a.view_count + ' views'),
        a.published_at && React.createElement('span', null, 'Published ' + timeAgo(a.published_at)),
        a.updated_at && React.createElement('span', null, 'Updated ' + timeAgo(a.updated_at)),
        isAdmin() && a.visibility && React.createElement('span', { className: 'kb-vis-badge ' + visCls },
          VISIBILITY_LABELS[a.visibility] || a.visibility
        )
      ),
      React.createElement('div', {
        className: 'kb-detail-body kb-md',
        dangerouslySetInnerHTML: { __html: renderMarkdown(a.body_md) },
      })
    );
  }

  /* ─── Component: Editor ──────────────────────────────────────── */
  function renderEditor() {
    if (!state.editorOpen) return null;
    const isNew = !state.editorArticle;
    const a = state.editorArticle || {};

    let formSlug, formTitle, formCat, formVis, formRoles, formStatus, formBody;

    function handleSave() {
      const payload = {
        slug:           formSlug.value.trim().toLowerCase().replace(/\s+/g, '-'),
        title:          formTitle.value.trim(),
        category:       formCat.value,
        visibility:     formVis.value,
        required_roles: formRoles.value.split(',').map(r => r.trim()).filter(Boolean),
        status:         formStatus.value,
        body_md:        formBody.value,
      };
      if (!payload.slug || !payload.title || !payload.body_md) {
        setState({ editorError: 'Slug, title, and body are required.' });
        return;
      }
      saveArticle(payload);
    }

    const catOptions = Object.entries(CATEGORIES).map(([val, label]) =>
      React.createElement('option', { key: val, value: val }, label)
    );
    const visOptions = Object.entries(VISIBILITY_LABELS).map(([val, label]) =>
      React.createElement('option', { key: val, value: val }, label)
    );

    return React.createElement('div', {
      className: 'kb-editor-overlay',
      role: 'dialog',
      'aria-modal': 'true',
      'aria-label': isNew ? 'Create new article' : 'Edit article',
    },
      React.createElement('div', { className: 'kb-editor-card' },
        React.createElement('div', { className: 'kb-editor-title' }, isNew ? 'New article' : 'Edit: ' + a.title),

        React.createElement('div', { className: 'kb-editor-row' },
          React.createElement('div', { className: 'kb-field' },
            React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-title' }, 'Title'),
            React.createElement('input', {
              id: 'kb-ed-title', className: 'kb-input', defaultValue: a.title || '',
              ref: el => { formTitle = el; }, required: true,
            })
          ),
          React.createElement('div', { className: 'kb-field', style: { maxWidth: 200 } },
            React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-slug' }, 'Slug'),
            React.createElement('input', {
              id: 'kb-ed-slug', className: 'kb-input',
              defaultValue: a.slug || '',
              ref: el => { formSlug = el; },
              placeholder: 'auto-from-title',
            })
          )
        ),

        React.createElement('div', { className: 'kb-editor-row' },
          React.createElement('div', { className: 'kb-field' },
            React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-cat' }, 'Category'),
            React.createElement('select', {
              id: 'kb-ed-cat', className: 'kb-select kb-input',
              defaultValue: a.category || 'getting_started',
              ref: el => { formCat = el; },
            }, catOptions)
          ),
          React.createElement('div', { className: 'kb-field' },
            React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-vis' }, 'Visibility'),
            React.createElement('select', {
              id: 'kb-ed-vis', className: 'kb-select kb-input',
              defaultValue: a.visibility || 'internal_only',
              ref: el => { formVis = el; },
            }, visOptions)
          ),
          React.createElement('div', { className: 'kb-field' },
            React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-status' }, 'Status'),
            React.createElement('select', {
              id: 'kb-ed-status', className: 'kb-select kb-input',
              defaultValue: a.status || 'draft',
              ref: el => { formStatus = el; },
            },
              React.createElement('option', { value: 'draft' }, 'Draft'),
              React.createElement('option', { value: 'published' }, 'Published')
            )
          )
        ),

        React.createElement('div', { className: 'kb-field' },
          React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-roles' }, 'Required roles (comma-separated, super_admin always has access)'),
          React.createElement('input', {
            id: 'kb-ed-roles', className: 'kb-input',
            defaultValue: Array.isArray(a.required_roles) ? a.required_roles.join(', ') : 'super_admin',
            ref: el => { formRoles = el; },
            placeholder: 'super_admin, internal_partner',
          })
        ),

        React.createElement('div', { className: 'kb-field' },
          React.createElement('label', { className: 'kb-label', htmlFor: 'kb-ed-body' }, 'Body (Markdown)'),
          React.createElement('textarea', {
            id: 'kb-ed-body', className: 'kb-textarea',
            defaultValue: a.body_md || '',
            ref: el => { formBody = el; },
            placeholder: '# Article title\n\nWrite your article in Markdown\u2026',
          })
        ),

        state.editorError && React.createElement('div', { className: 'kb-editor-error', role: 'alert' }, state.editorError),

        React.createElement('div', { className: 'kb-editor-actions' },
          React.createElement('button', {
            className: 'kb-btn-cancel',
            onClick: () => setState({ editorOpen: false, editorError: null }),
          }, 'Cancel'),
          React.createElement('button', {
            className: 'kb-btn-save',
            onClick: handleSave,
            disabled: state.editorSaving,
          }, state.editorSaving ? 'Saving\u2026' : (isNew ? 'Create article' : 'Save changes'))
        )
      )
    );
  }

  /* ─── Component: Revisions drawer ───────────────────────────── */
  function renderRevisions() {
    if (!state.revisionsOpen) return null;
    return React.createElement('div', {
      className: 'kb-revisions',
      role: 'complementary',
      'aria-label': 'Edit history',
    },
      React.createElement('div', { className: 'kb-revisions-header' },
        React.createElement('span', null, 'Edit history'),
        React.createElement('button', {
          className: 'kb-close-btn',
          onClick: () => setState({ revisionsOpen: false }),
          'aria-label': 'Close history',
        }, '\u00D7')
      ),
      React.createElement('div', { className: 'kb-revisions-list' },
        state.revisions.length === 0
          ? React.createElement('div', { style: { padding: '20px 16px', color: 'var(--dim)', fontSize: 13 } }, 'No edits recorded yet.')
          : state.revisions.map((r, i) =>
              React.createElement('div', { key: r.id || i, className: 'kb-revision-row' },
                React.createElement('div', { className: 'kb-revision-who' }, r.edited_by || 'Unknown'),
                React.createElement('div', { className: 'kb-revision-when' }, timeAgo(r.created_at)),
                r.edit_summary && React.createElement('div', { className: 'kb-revision-summary' }, r.edit_summary)
              )
            )
      )
    );
  }

  /* ─── Root render ────────────────────────────────────────────── */
  function KnowledgeBase({ containerId }) {
    const [, forceUpdate] = React.useState(0);
    renderFn = () => forceUpdate(n => n + 1);

    React.useEffect(() => {
      /* Load marked.js if not already present */
      if (!window.marked) {
        const s = document.createElement('script');
        s.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
        s.onload = () => forceUpdate(n => n + 1);
        document.head.appendChild(s);
      }
      loadArticleList();
      return () => { renderFn = null; };
    }, []);

    return React.createElement(React.Fragment, null,
      /* Inject styles once */
      React.createElement('style', null, CSS),

      React.createElement('div', {
        className: 'kb-root',
        style: { position: 'relative' },
        role: 'application',
        'aria-label': 'Knowledge base',
      },
        renderSidebar(),
        renderListPane(),
        renderDetailPane(),
        state.revisionsOpen && renderRevisions(),
        renderEditor()
      )
    );
  }

  /* ─── Export ─────────────────────────────────────────────────── */
  window.KnowledgeBase = KnowledgeBase;

})();
