            //
            //
            // region MEMBERS
            //
            //

            // load members
            var members = [];
            var profiles = new Map();
            var activityCounts = {};
            let membersPage = 1;
            const membersLimit = 400;
            var hideProfileImages = false;
            var memberInterests = new Map();   // skool_id → [{category, weight, count}]
            var labelsLoaded = false;

            var loadMembers = async (skipAnimation = false) => {
                if (!skipAnimation) lib.showLoading();
                const PAGE_SIZE = 100;
                let allUsers = [];
                let allProfiles = [];
                let allCounts = {};
                let totalUserCount = 0;
                let totalFilterErrors = 0;
                let page = 1;
                let totalPages = 1;
                try {
                    do {
                        const rawRes = await fetch('/api/user/filter', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ ...filterState, page, pageSize: PAGE_SIZE })
                        });
                        if (!rawRes.ok) {
                            if (!skipAnimation) lib.hideLoading();
                            lib.showSectionError('members-view', 'Members', 'loadMembers', 'members-filter');
                            return;
                        }
                        const res = await rawRes.json();
                        allUsers = allUsers.concat(res.users || []);
                        for (const p of res.profiles || []) allProfiles.push(p);
                        Object.assign(allCounts, res.counts || {});
                        totalUserCount = res.total || 0;
                        totalFilterErrors += res.filter_errors || 0;
                        totalPages = res.total_pages || 1;
                        page++;
                    } while (page <= totalPages);
                } catch (e) {
                    if (!skipAnimation) lib.hideLoading();
                    lib.showSectionError('members-view', 'Members', 'loadMembers', 'members-filter');
                    return;
                }
                if (!skipAnimation) lib.hideLoading();

                if (totalFilterErrors > 0) {
                    console.warn('[Members] filter_errors:', totalFilterErrors, '— some members may be excluded from filter results');
                }

                // Success: reset retry counter
                lib.retryCounters['members-filter'] = 0;
                members = allUsers;
                for (const p of allProfiles) profiles.set(p.skool_id, p);
                activityCounts = allCounts;
                membersPage = 1;
                document.getElementById("memberAmount").innerText = members.length.toString();
                if (totalUserCount !== undefined) document.getElementById("memberTotal").innerText = totalUserCount.toString();
                renderMembersView();
                if (!initialLoading) renderSecondaryView();
                updateActionButtonCounts();
                // Load member traits in background (non-blocking)
                if (!labelsLoaded) loadMemberInterests();
            };

            let setMembersPage = (page) => {
                membersPage = page;
                renderMembersView();
                document.getElementById("members-view").scrollIntoView({behavior: 'smooth'});
            };

            let renderMembersPagination = () => {
                const total = members.length;
                const pages = Math.ceil(total / membersLimit);
                if (pages <= 1) return '';
                const start = (membersPage - 1) * membersLimit + 1;
                const end = Math.min(membersPage * membersLimit, total);
                let html = `<div style="margin:10px 0;display:flex;gap:5px;align-items:center;flex-wrap:wrap;">`;
                html += `<span>${start}-${end} von ${total} | Seite ${membersPage}/${pages}</span>`;
                html += `<button onclick="setMembersPage(1)" ${membersPage===1?'disabled':''}>«</button>`;
                html += `<button onclick="setMembersPage(${membersPage-1})" ${membersPage===1?'disabled':''}>‹</button>`;
                const pStart = Math.max(1, membersPage - 2);
                const pEnd = Math.min(pages, membersPage + 2);
                for (let i = pStart; i <= pEnd; i++) {
                    html += `<button onclick="setMembersPage(${i})" ${i===membersPage?'style="font-weight:bold"':''}>${i}</button>`;
                }
                html += `<button onclick="setMembersPage(${membersPage+1})" ${membersPage===pages?'disabled':''}>›</button>`;
                html += `<button onclick="setMembersPage(${pages})" ${membersPage===pages?'disabled':''}>»</button>`;
                html += `</div>`;
                return html;
            };

            const formatDate = (ts) => ts ? new Date(ts * 1000).toLocaleDateString() : '-';

            const formatAge = (ts) => {
                if (!ts) return '-';
                const now = Math.floor(Date.now() / 1000);
                const diff = now - ts;
                if (diff < 60) return 'just now';
                const mins = Math.floor(diff / 60);
                if (diff < 3600) return mins + (mins === 1 ? ' minute' : ' minutes');
                const hrs = Math.floor(diff / 3600);
                if (diff < 86400) return hrs + (hrs === 1 ? ' hour' : ' hours');
                const days = Math.floor(diff / 86400);
                return days + (days === 1 ? ' day' : ' days');
            };

            const renderMemberCard = (member, showDetails = false) => {
                let imgHtml = '';
                if (hideProfileImages) {
                    const initial = (member.first_name || member.name || '?')[0].toUpperCase();
                    const hue = (member.skool_id || '').split('').reduce((a, c) => a + c.charCodeAt(0), 0) % 360;
                    imgHtml = `<div class="vcard-avatar" style="background:hsl(${hue},55%,45%);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:bold;font-size:20px;border-radius:50%;">${initial}</div>`;
                } else {
                    const imgSrc = member.skool_id ? `/api/image/${member.skool_id}` : '';
                    imgHtml = imgSrc ? `<img src="${imgSrc}" class="vcard-avatar" onerror="this.style.display='none'">` : '';
                }
                const hasProfileFetched = profiles.has(member.skool_id);
                const fetchedAge = formatAge(member.fetched_at);
                const hasProfileFetchedHtml = hasProfileFetched
                    ? `<span title="Fetched: ${fetchedAge} ago" style="color:green;font-weight:bold;cursor:help">●</span>`
                    : `<span title="Profile details not fetched" style="color:red;font-weight:bold;cursor:help">●</span>`;
                const c = activityCounts[member.skool_id] || {posts: 0, comments: 0, likes: 0};
                // for performance reasons dont print the meta data...
                member.metadata = undefined;
                member.member_metadata = undefined;
                let details = "";
                if (showDetails) {
                    details = `<pre style="margin:4px 0 0;white-space:pre-wrap;font-size:0.9em">${member.bio}</pre>`;
                }
                return `
                    <div class="vcard" onclick="loadAndRenderProfileDetails(${member.id})">
                        ${imgHtml}
                        <div class="vcard-body">
                            <div class="vcard-name">${member.first_name} ${member.last_name} ${hasProfileFetchedHtml}</div>
                            <div class="vcard-meta">
                                <span class="vcard-handle">@${member.name}</span>
                            </div>
                            <div class="vcard-meta">
                                <span>Active: ${formatDate(member.last_active)}</span>
                                <span>Joined: ${formatDate(member.member_created_at)}</span>
                            </div>
                            <div class="vcard-meta">
                                <span>Posts: <b>${c.posts}</b></span>
                                <span>Comments: <b>${c.comments}</b></span>
                                <span>Likes: <b>${c.likes}</b></span>
                            </div>
                            ${renderInterestPills(member.skool_id)}
                            ${details}
                        </div>
                    </div>
                `;
            };

            const renderMembersView = async () => {
                const hideVal = await ConfigEntry.get("hide_profile_images");
                hideProfileImages = hideVal === "1";
                const membersView = document.getElementById("members-view");
                membersView.innerHTML= "";
                const secondaryViewActions = document.getElementById("secondary-views");
                if (currentView === 'graph' || currentView === 'timeline') secondaryViewActions.style.display = 'none';
                else secondaryViewActions.style.display = 'inline';

                // Paginated slice
                const offset = (membersPage - 1) * membersLimit;
                const pageMembers = members.slice(offset, offset + membersLimit);

                switch(currentView){
                    case "list": {
                        if (secondaryViewVisible) {
                            membersView.innerHTML = `
                                <div style="flex: 1; min-width: 0; height: 100%; overflow-y: auto; padding: 4px;" id="members-list"></div>
                                <div style="flex: 1; min-width: 0; height: 100%; overflow-y: auto; padding: 4px;" id='secondary-view'></div>
                            `;
                        } else {
                            membersView.innerHTML = `
                                <div style="flex: 1; min-width: 0; height: 100%; overflow-y: auto; padding: 4px;" id="members-list"></div>
                            `;
                        }
                        const membersList = document.getElementById("members-list");
                        const gridClass = secondaryViewVisible ? 'members-grid' : 'members-grid members-grid-wide';
                        let gridHtml = `<div class="${gridClass}">`;
                        for (let member of pageMembers) {
                            gridHtml += renderMemberCard(member, false);
                        }
                        gridHtml += `<div class="pagination-row">${renderMembersPagination()}</div>`;
                        gridHtml += '</div>';
                        membersList.innerHTML = gridHtml;
                    }break;

                    case "graph": {
                        membersView.innerHTML = `<div id="graph-container" style="width: 100%; height: 100%; border: 1px solid #ccc; display: flex; flex-direction: column;"></div>`;
                        const container = document.getElementById("graph-container");
                        MembersConnectionGraph.loadAndRenderInteractionGraph(container, members);
                    } break;

                    case "timeline": {
                        membersView.innerHTML = `<div id="timeline-container" style="width: 100%; height: 100%; border: 1px solid #444; border-radius: 6px;"></div>`;
                        const timelineContainer = document.getElementById("timeline-container");
                        MembersTimeline.loadAndRender(timelineContainer, members);
                    } break;

                    default:
                        membersView.innerHTML= "UNKNOWN VIEW -> this is a bug";
                        break;
                }
            }

            // ---- Member Interests ----

            const categoryHueMap = {
                'Health & Fitness': 120,
                'Marketing & Sales': 30,
                'Business & Entrepreneurship': 45,
                'Personal Development': 270,
                'Technology & Software': 210,
                'Creative Arts & Design': 330,
                'Education & Learning': 200,
                'Finance & Investing': 60,
                'Spirituality & Mindfulness': 280,
                'Relationships & Dating': 350,
                'Career & Professional': 190,
                'Real Estate': 80,
                'E-Commerce': 40,
                'Content Creation': 300,
                'Coaching & Consulting': 160,
                'Other': 0,
            };

            const categoryToHue = (category) => {
                return categoryHueMap[category] ?? 0;
            };

            const renderInterestPills = (skoolId) => {
                const interests = memberInterests.get(skoolId);
                if (!interests || interests.length === 0) return '';
                const maxPills = 3;
                let html = '<div class="vcard-meta" style="gap:4px;flex-wrap:wrap;">';
                for (let i = 0; i < Math.min(interests.length, maxPills); i++) {
                    const t = interests[i];
                    const hue = categoryToHue(t.category);
                    const pct = Math.round(t.weight * 100);
                    html += `<span title="${t.category}: ${pct}% (${t.count} communities)"
                        style="padding:1px 6px;border-radius:3px;font-size:0.75em;
                        background:hsl(${hue},50%,25%);color:hsl(${hue},80%,80%);
                        border:1px solid hsl(${hue},50%,35%);">${t.category}</span>`;
                }
                html += '</div>';
                return html;
            };

            var loadMemberInterests = async () => {
                try {
                    // Check if labels are stale
                    const labelsRes = await fetch('/api/community-labels');
                    if (!labelsRes.ok) return;
                    const labelsData = await labelsRes.json();

                    // If stale, trigger refresh (fire-and-forget)
                    if (labelsData.stale) {
                        console.log('[CommunityLabels] Cache stale, triggering refresh...');
                        fetch('/api/community-labels/refresh').then(r => {
                            if (r.ok) {
                                console.log('[CommunityLabels] Refresh complete, reloading interests...');
                                loadMemberInterestsData();
                            }
                        }).catch(() => {});
                    }

                    // If we have labels, load interests for visible members
                    if (labelsData.count > 0) {
                        await loadMemberInterestsData();
                    }

                    labelsLoaded = true;
                } catch (e) {
                    console.warn('[CommunityLabels] Failed to load labels:', e);
                }
            };

            var loadMemberInterestsData = async () => {
                try {
                    const skoolIds = members.map(m => m.skool_id).filter(Boolean);
                    if (skoolIds.length === 0) return;

                    const res = await fetch('/api/member-interests/batch', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ skool_ids: skoolIds })
                    });
                    if (!res.ok) return;
                    const data = await res.json();

                    memberInterests.clear();
                    if (data.interests) {
                        for (const [skoolId, interests] of Object.entries(data.interests)) {
                            memberInterests.set(skoolId, interests);
                        }
                    }

                    // Re-render to show pills
                    if (memberInterests.size > 0) {
                        renderMembersView();
                    }
                } catch (e) {
                    console.warn('[CommunityLabels] Failed to load member interests:', e);
                }
            };