<script>
    import LoginModal from "./LoginModal.svelte";
    import ManagedACL from "./ManagedACL.svelte";
    import {
        initializeNostrClient,
        fetchUserProfile,
        fetchAllEvents,
        fetchUserEvents,
        searchEvents,
        fetchEventById,
        fetchDeleteEventsByTarget,
        queryEvents,
        queryEventsFromDB,
        debugIndexedDB,
        nostrClient,
        NostrClient,
        Nip07Signer,
        PrivateKeySigner,
    } from "./nostr.js";
    import { publishEventWithAuth } from "./websocket-auth.js";

    // Expose debug function globally for console access
    if (typeof window !== "undefined") {
        window.debugIndexedDB = debugIndexedDB;
    }

    let isDarkTheme = false;
    let showLoginModal = false;
    let isLoggedIn = false;
    let userPubkey = "";
    let authMethod = "";
    let userProfile = null;
    let userRole = "";
    let userSigner = null;
    let showSettingsDrawer = false;
    let selectedTab = localStorage.getItem("selectedTab") || "export";
    let isSearchMode = false;
    let searchQuery = "";
    let searchTabs = [];
    let allEvents = [];
    let selectedFile = null;
    let expandedEvents = new Set();
    let isLoadingEvents = false;
    let hasMoreEvents = true;
    let eventsPerPage = 100;
    let oldestEventTimestamp = null; // For timestamp-based pagination
    let newestEventTimestamp = null; // For loading newer events
    let showPermissionMenu = false;
    let viewAsRole = "";

    // Search results state
    let searchResults = new Map(); // Map of searchTabId -> { events, isLoading, hasMore, oldestTimestamp }
    let isLoadingSearch = false;

    // Screen-filling events view state
    let eventsPerScreen = 20; // Default, will be calculated based on screen size

    // Global events cache system
    let globalEventsCache = []; // All events cache
    let globalCacheTimestamp = 0;
    const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes

    // Events filter toggle
    let showOnlyMyEvents = false;

    // My Events state
    let myEvents = [];
    let isLoadingMyEvents = false;
    let hasMoreMyEvents = true;
    let oldestMyEventTimestamp = null;
    let newestMyEventTimestamp = null;

    // Sprocket management state
    let sprocketScript = "";
    let sprocketStatus = null;
    let sprocketVersions = [];
    let isLoadingSprocket = false;
    let sprocketMessage = "";
    let sprocketMessageType = "info";
    let sprocketEnabled = false;
    let sprocketUploadFile = null;

    // ACL mode
    let aclMode = "";

    // Compose tab state
    let composeEventJson = "";

    // Recovery tab state
    let recoverySelectedKind = null;
    let recoveryCustomKind = "";
    let recoveryEvents = [];
    let isLoadingRecovery = false;
    let recoveryHasMore = true;
    let recoveryOldestTimestamp = null;
    let recoveryNewestTimestamp = null;

    // Replaceable kinds for the recovery dropdown
    // Based on NIP-01: kinds 0, 3, and 10000-19999 are replaceable
    // kinds 30000-39999 are addressable (parameterized replaceable)
    const replaceableKinds = [
        // Basic replaceable kinds (0, 3)
        { value: 0, label: "User Metadata (0)" },
        { value: 3, label: "Follows (3)" },

        // Replaceable range 10000-19999
        { value: 10000, label: "Mute list (10000)" },
        { value: 10001, label: "Pin list (10001)" },
        { value: 10002, label: "Relay List Metadata (10002)" },
        { value: 10003, label: "Bookmark list (10003)" },
        { value: 10004, label: "Communities list (10004)" },
        { value: 10005, label: "Public chats list (10005)" },
        { value: 10006, label: "Blocked relays list (10006)" },
        { value: 10007, label: "Search relays list (10007)" },
        { value: 10009, label: "User groups (10009)" },
        { value: 10012, label: "Favorite relays list (10012)" },
        { value: 10013, label: "Private event relay list (10013)" },
        { value: 10015, label: "Interests list (10015)" },
        { value: 10019, label: "Nutzap Mint Recommendation (10019)" },
        { value: 10020, label: "Media follows (10020)" },
        { value: 10030, label: "User emoji list (10030)" },
        { value: 10050, label: "Relay list to receive DMs (10050)" },
        { value: 10051, label: "KeyPackage Relays List (10051)" },
        { value: 10063, label: "User server list (10063)" },
        { value: 10096, label: "File storage server list (10096)" },
        { value: 10166, label: "Relay Monitor Announcement (10166)" },
        { value: 10312, label: "Room Presence (10312)" },
        { value: 10377, label: "Proxy Announcement (10377)" },
        { value: 11111, label: "Transport Method Announcement (11111)" },
        { value: 13194, label: "Wallet Info (13194)" },
        { value: 17375, label: "Cashu Wallet Event (17375)" },

        // Addressable range 30000-39999 (parameterized replaceable)
        { value: 30000, label: "Follow sets (30000)" },
        { value: 30001, label: "Generic lists (30001)" },
        { value: 30002, label: "Relay sets (30002)" },
        { value: 30003, label: "Bookmark sets (30003)" },
        { value: 30004, label: "Curation sets (30004)" },
        { value: 30005, label: "Video sets (30005)" },
        { value: 30007, label: "Kind mute sets (30007)" },
        { value: 30008, label: "Profile Badges (30008)" },
        { value: 30009, label: "Badge Definition (30009)" },
        { value: 30015, label: "Interest sets (30015)" },
        { value: 30017, label: "Create or update a stall (30017)" },
        { value: 30018, label: "Create or update a product (30018)" },
        { value: 30019, label: "Marketplace UI/UX (30019)" },
        { value: 30020, label: "Product sold as an auction (30020)" },
        { value: 30023, label: "Long-form Content (30023)" },
        { value: 30024, label: "Draft Long-form Content (30024)" },
        { value: 30030, label: "Emoji sets (30030)" },
        { value: 30040, label: "Curated Publication Index (30040)" },
        { value: 30041, label: "Curated Publication Content (30041)" },
        { value: 30063, label: "Release artifact sets (30063)" },
        { value: 30078, label: "Application-specific Data (30078)" },
        { value: 30166, label: "Relay Discovery (30166)" },
        { value: 30267, label: "App curation sets (30267)" },
        { value: 30311, label: "Live Event (30311)" },
        { value: 30312, label: "Interactive Room (30312)" },
        { value: 30313, label: "Conference Event (30313)" },
        { value: 30315, label: "User Statuses (30315)" },
        { value: 30388, label: "Slide Set (30388)" },
        { value: 30402, label: "Classified Listing (30402)" },
        { value: 30403, label: "Draft Classified Listing (30403)" },
        { value: 30617, label: "Repository announcements (30617)" },
        { value: 30618, label: "Repository state announcements (30618)" },
        { value: 30818, label: "Wiki article (30818)" },
        { value: 30819, label: "Redirects (30819)" },
        { value: 31234, label: "Draft Event (31234)" },
        { value: 31388, label: "Link Set (31388)" },
        { value: 31890, label: "Feed (31890)" },
        { value: 31922, label: "Date-Based Calendar Event (31922)" },
        { value: 31923, label: "Time-Based Calendar Event (31923)" },
        { value: 31924, label: "Calendar (31924)" },
        { value: 31925, label: "Calendar Event RSVP (31925)" },
        { value: 31989, label: "Handler recommendation (31989)" },
        { value: 31990, label: "Handler information (31990)" },
        { value: 32267, label: "Software Application (32267)" },
        { value: 34550, label: "Community Definition (34550)" },
        { value: 37516, label: "Geocache listing (37516)" },
        { value: 38172, label: "Cashu Mint Announcement (38172)" },
        { value: 38173, label: "Fedimint Announcement (38173)" },
        { value: 38383, label: "Peer-to-peer Order events (38383)" },
        { value: 39089, label: "Starter packs (39089)" },
        { value: 39092, label: "Media starter packs (39092)" },
        { value: 39701, label: "Web bookmarks (39701)" },
    ];

    // Kind name mapping based on NIP specification
    // Matches official Nostr event kinds from https://github.com/nostr-protocol/nips
    const kindNames = {
        0: "User Metadata",
        1: "Short Text Note",
        2: "Recommend Relay",
        3: "Follows",
        4: "Encrypted Direct Messages",
        5: "Event Deletion Request",
        6: "Repost",
        7: "Reaction",
        8: "Badge Award",
        9: "Chat Message",
        10: "Group Chat Threaded Reply",
        11: "Thread",
        12: "Group Thread Reply",
        13: "Seal",
        14: "Direct Message",
        15: "File Message",
        16: "Generic Repost",
        17: "Reaction to a website",
        20: "Picture",
        40: "Channel Creation",
        41: "Channel Metadata",
        42: "Channel Message",
        43: "Channel Hide Message",
        44: "Channel Mute User",
        1021: "Bid",
        1022: "Bid Confirmation",
        1040: "OpenTimestamps",
        1063: "File Metadata",
        1311: "Live Chat Message",
        1971: "Problem Tracker",
        1984: "Reporting",
        1985: "Label",
        4550: "Community Post Approval",
        5000: "Job Request",
        5999: "Job Request",
        6000: "Job Result",
        6999: "Job Result",
        7000: "Job Feedback",
        9041: "Zap Goal",
        9734: "Zap Request",
        9735: "Zap",
        9882: "Highlights",
        10000: "Mute list",
        10001: "Pin list",
        10002: "Relay List Metadata",
        10003: "Bookmarks list",
        10004: "Communities list",
        10005: "Public Chats list",
        10006: "Blocked Relays list",
        10007: "Search Relays list",
        10015: "Interests",
        10030: "User Emoji list",
        10050: "DM relays",
        10096: "File Storage Server List",
        13194: "Wallet Service Info",
        21000: "Lightning pub RPC",
        22242: "Client Authentication",
        23194: "Wallet Request",
        23195: "Wallet Response",
        23196: "Wallet Notification",
        23197: "Wallet Notification",
        24133: "Nostr Connect",
        27235: "HTTP Auth",
        30000: "Follow sets",
        30001: "Generic lists",
        30002: "Relay sets",
        30003: "Bookmark sets",
        30004: "Curation sets",
        30008: "Profile Badges",
        30009: "Badge Definition",
        30015: "Interest sets",
        30017: "Stall Definition",
        30018: "Product Definition",
        30019: "Marketplace UI/UX",
        30020: "Product sold as an auction",
        30023: "Long-form Content",
        30024: "Draft Long-form Content",
        30030: "Emoji sets",
        30078: "Application-specific Data",
        30311: "Live Event",
        30315: "User Statuses",
        30402: "Classified Listing",
        30403: "Draft Classified Listing",
        31922: "Date-Based Calendar Event",
        31923: "Time-Based Calendar Event",
        31924: "Calendar",
        31925: "Calendar Event RSVP",
        31989: "Handler recommendation",
        31990: "Handler information",
        34235: "Video Event Horizontal",
        34236: "Video Event Vertical",
        34550: "Community Definition",
    };

    function getKindName(kind) {
        return kindNames[kind] || `Kind ${kind}`;
    }

    function truncatePubkey(pubkey) {
        if (!pubkey) return "unknown";
        return pubkey.slice(0, 8) + "..." + pubkey.slice(-8);
    }

    function truncateContent(content, maxLength = 100) {
        if (!content) return "";
        return content.length > maxLength
            ? content.slice(0, maxLength) + "..."
            : content;
    }

    function formatTimestamp(timestamp) {
        if (!timestamp) return "";
        return new Date(timestamp * 1000).toLocaleString();
    }

    function toggleEventExpansion(eventId) {
        if (expandedEvents.has(eventId)) {
            expandedEvents.delete(eventId);
        } else {
            expandedEvents.add(eventId);
        }
        expandedEvents = expandedEvents; // Trigger reactivity
    }

    async function copyEventToClipboard(eventData, clickEvent) {
        try {
            // Create minified JSON (no indentation)
            const minifiedJson = JSON.stringify(eventData);
            await navigator.clipboard.writeText(minifiedJson);

            // Show temporary feedback
            const button = clickEvent.target.closest(".copy-json-btn");
            if (button) {
                const originalText = button.textContent;
                button.textContent = "✅";
                button.style.backgroundColor = "#4CAF50";
                setTimeout(() => {
                    button.textContent = originalText;
                    button.style.backgroundColor = "";
                }, 2000);
            }
        } catch (error) {
            console.error("Failed to copy to clipboard:", error);
            // Fallback for older browsers
            try {
                const textArea = document.createElement("textarea");
                textArea.value = JSON.stringify(eventData);
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand("copy");
                document.body.removeChild(textArea);

                const button = clickEvent.target.closest(".copy-json-btn");
                if (button) {
                    const originalText = button.textContent;
                    button.textContent = "✅";
                    button.style.backgroundColor = "#4CAF50";
                    setTimeout(() => {
                        button.textContent = originalText;
                        button.style.backgroundColor = "";
                    }, 2000);
                }
            } catch (fallbackError) {
                console.error("Fallback copy also failed:", fallbackError);
                alert("Failed to copy to clipboard. Please copy manually.");
            }
        }
    }

    async function handleToggleChange() {
        // Toggle state is already updated by bind:checked
        console.log("Toggle changed, showOnlyMyEvents:", showOnlyMyEvents);

        // Reload events with the new filter
        const authors =
            showOnlyMyEvents && isLoggedIn && userPubkey ? [userPubkey] : null;
        await loadAllEvents(true, authors);
    }

    // Events are filtered server-side, but add client-side filtering as backup
    // Sort events by created_at timestamp (newest first)
    $: filteredEvents = (
        showOnlyMyEvents && isLoggedIn && userPubkey
            ? allEvents.filter(
                  (event) => event.pubkey && event.pubkey === userPubkey,
              )
            : allEvents
    ).sort((a, b) => b.created_at - a.created_at);

    async function deleteEvent(eventId) {
        if (!isLoggedIn) {
            alert("Please log in first");
            return;
        }

        // Find the event to check if user can delete it
        const event = allEvents.find((e) => e.id === eventId);
        if (!event) {
            alert("Event not found");
            return;
        }

        // Check permissions: admin/owner can delete any event, write users can only delete their own events
        const canDelete =
            userRole === "admin" ||
            userRole === "owner" ||
            (userRole === "write" &&
                event.pubkey &&
                event.pubkey === userPubkey);

        if (!canDelete) {
            alert("You do not have permission to delete this event");
            return;
        }

        if (!confirm("Are you sure you want to delete this event?")) {
            return;
        }

        try {
            // Check if signer is available
            if (!userSigner) {
                throw new Error("Signer not available for signing");
            }

            // Create the delete event template (unsigned)
            const deleteEventTemplate = {
                kind: 5,
                created_at: Math.floor(Date.now() / 1000),
                tags: [["e", eventId]], // e-tag referencing the event to delete
                content: "",
                // Don't set pubkey - let the signer set it
            };

            console.log("Created delete event template:", deleteEventTemplate);
            console.log("User pubkey:", userPubkey);
            console.log("Target event:", event);
            console.log("Target event pubkey:", event.pubkey);

            // Sign the event using the signer
            const signedDeleteEvent =
                await userSigner.signEvent(deleteEventTemplate);
            console.log("Signed delete event:", signedDeleteEvent);
            console.log(
                "Signed delete event pubkey:",
                signedDeleteEvent.pubkey,
            );
            console.log("Delete event tags:", signedDeleteEvent.tags);

            // Publish to the ORLY relay using WebSocket authentication
            const relayUrl = `wss://${window.location.host}`;

            try {
                const result = await publishEventWithAuth(
                    relayUrl,
                    signedDeleteEvent,
                    userSigner,
                    userPubkey,
                );

                if (result.success) {
                    console.log(
                        "Delete event published successfully to ORLY relay",
                    );
                } else {
                    console.error(
                        "Failed to publish delete event:",
                        result.reason,
                    );
                }
            } catch (error) {
                console.error("Error publishing delete event:", error);
            }

            // Determine if we should publish to external relays
            // Only publish to external relays if:
            // 1. User is deleting their own event, OR
            // 2. User is admin/owner AND deleting their own event
            const isDeletingOwnEvent =
                event.pubkey && event.pubkey === userPubkey;
            const isAdminOrOwner = userRole === "admin" || userRole === "owner";
            const shouldPublishToExternalRelays = isDeletingOwnEvent;

            if (shouldPublishToExternalRelays) {
                // Publish the delete event to all relays (including external ones)
                const result = await nostrClient.publish(signedDeleteEvent);
                console.log("Delete event published:", result);

                if (result.success && result.okCount > 0) {
                    // Wait a moment for the deletion to propagate
                    await new Promise((resolve) => setTimeout(resolve, 2000));

                    // Verify the event was actually deleted by trying to fetch it
                    try {
                        const deletedEvent = await fetchEventById(eventId, {
                            timeout: 5000,
                        });
                        if (deletedEvent) {
                            console.warn(
                                "Event still exists after deletion attempt:",
                                deletedEvent,
                            );
                            alert(
                                `Warning: Delete event was accepted by ${result.okCount} relay(s), but the event still exists on the relay. This may indicate the relay does not properly handle delete events.`,
                            );
                        } else {
                            console.log(
                                "Event successfully deleted and verified",
                            );
                        }
                    } catch (fetchError) {
                        console.log(
                            "Could not fetch event after deletion (likely deleted):",
                            fetchError.message,
                        );
                    }

                    // Also verify that the delete event has been saved
                    try {
                        const deleteEvents = await fetchDeleteEventsByTarget(
                            eventId,
                            { timeout: 5000 },
                        );
                        if (deleteEvents.length > 0) {
                            console.log(
                                `Delete event verification: Found ${deleteEvents.length} delete event(s) targeting ${eventId}`,
                            );
                            // Check if our delete event is among them
                            const ourDeleteEvent = deleteEvents.find(
                                (de) => de.pubkey && de.pubkey === userPubkey,
                            );
                            if (ourDeleteEvent) {
                                console.log(
                                    "Our delete event found in database:",
                                    ourDeleteEvent.id,
                                );
                            } else {
                                console.warn(
                                    "Our delete event not found in database, but other delete events exist",
                                );
                            }
                        } else {
                            console.warn(
                                "No delete events found in database for target event:",
                                eventId,
                            );
                        }
                    } catch (deleteFetchError) {
                        console.log(
                            "Could not verify delete event in database:",
                            deleteFetchError.message,
                        );
                    }

                    // Remove from local lists
                    allEvents = allEvents.filter(
                        (event) => event.id !== eventId,
                    );
                    myEvents = myEvents.filter((event) => event.id !== eventId);

                    // Remove from global cache
                    globalEventsCache = globalEventsCache.filter(
                        (event) => event.id !== eventId,
                    );

                    // Remove from search results cache
                    for (const [tabId, searchResult] of searchResults) {
                        if (searchResult.events) {
                            searchResult.events = searchResult.events.filter(
                                (event) => event.id !== eventId,
                            );
                            searchResults.set(tabId, searchResult);
                        }
                    }

                    // Update persistent state
                    savePersistentState();

                    // Reload events to show the new delete event at the top
                    console.log("Reloading events to show delete event...");
                    const authors =
                        showOnlyMyEvents && isLoggedIn && userPubkey
                            ? [userPubkey]
                            : null;
                    await loadAllEvents(true, authors);

                    alert(
                        `Event deleted successfully (accepted by ${result.okCount} relay(s))`,
                    );
                } else {
                    throw new Error("No relays accepted the delete event");
                }
            } else {
                // Admin/owner deleting someone else's event - only publish to local relay
                // We need to publish only to the local relay, not external ones
                const localRelayUrl = `wss://${window.location.host}/`;

                // Create a modified client that only connects to the local relay
                const localClient = new NostrClient();
                await localClient.connectToRelay(localRelayUrl);

                const result = await localClient.publish(signedDeleteEvent);
                console.log(
                    "Delete event published to local relay only:",
                    result,
                );

                if (result.success && result.okCount > 0) {
                    // Wait a moment for the deletion to propagate
                    await new Promise((resolve) => setTimeout(resolve, 2000));

                    // Verify the event was actually deleted by trying to fetch it
                    try {
                        const deletedEvent = await fetchEventById(eventId, {
                            timeout: 5000,
                        });
                        if (deletedEvent) {
                            console.warn(
                                "Event still exists after deletion attempt:",
                                deletedEvent,
                            );
                            alert(
                                `Warning: Delete event was accepted by ${result.okCount} relay(s), but the event still exists on the relay. This may indicate the relay does not properly handle delete events.`,
                            );
                        } else {
                            console.log(
                                "Event successfully deleted and verified",
                            );
                        }
                    } catch (fetchError) {
                        console.log(
                            "Could not fetch event after deletion (likely deleted):",
                            fetchError.message,
                        );
                    }

                    // Also verify that the delete event has been saved
                    try {
                        const deleteEvents = await fetchDeleteEventsByTarget(
                            eventId,
                            { timeout: 5000 },
                        );
                        if (deleteEvents.length > 0) {
                            console.log(
                                `Delete event verification: Found ${deleteEvents.length} delete event(s) targeting ${eventId}`,
                            );
                            // Check if our delete event is among them
                            const ourDeleteEvent = deleteEvents.find(
                                (de) => de.pubkey && de.pubkey === userPubkey,
                            );
                            if (ourDeleteEvent) {
                                console.log(
                                    "Our delete event found in database:",
                                    ourDeleteEvent.id,
                                );
                            } else {
                                console.warn(
                                    "Our delete event not found in database, but other delete events exist",
                                );
                            }
                        } else {
                            console.warn(
                                "No delete events found in database for target event:",
                                eventId,
                            );
                        }
                    } catch (deleteFetchError) {
                        console.log(
                            "Could not verify delete event in database:",
                            deleteFetchError.message,
                        );
                    }

                    // Remove from local lists
                    allEvents = allEvents.filter(
                        (event) => event.id !== eventId,
                    );
                    myEvents = myEvents.filter((event) => event.id !== eventId);

                    // Remove from global cache
                    globalEventsCache = globalEventsCache.filter(
                        (event) => event.id !== eventId,
                    );

                    // Remove from search results cache
                    for (const [tabId, searchResult] of searchResults) {
                        if (searchResult.events) {
                            searchResult.events = searchResult.events.filter(
                                (event) => event.id !== eventId,
                            );
                            searchResults.set(tabId, searchResult);
                        }
                    }

                    // Update persistent state
                    savePersistentState();

                    // Reload events to show the new delete event at the top
                    console.log("Reloading events to show delete event...");
                    const authors =
                        showOnlyMyEvents && isLoggedIn && userPubkey
                            ? [userPubkey]
                            : null;
                    await loadAllEvents(true, authors);

                    alert(
                        `Event deleted successfully (local relay only - admin/owner deleting other user's event)`,
                    );
                } else {
                    throw new Error(
                        "Local relay did not accept the delete event",
                    );
                }
            }
        } catch (error) {
            console.error("Failed to delete event:", error);
            alert("Failed to delete event: " + error.message);
        }
    }

    // Safely render "about" text: convert double newlines to a single HTML line break
    function escapeHtml(str) {
        return String(str)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#39;");
    }

    // Recovery tab functions
    async function loadRecoveryEvents() {
        const kindToUse = recoveryCustomKind
            ? parseInt(recoveryCustomKind)
            : recoverySelectedKind;

        if (kindToUse === null || kindToUse === undefined || isNaN(kindToUse)) {
            console.log("No valid kind to load, kindToUse:", kindToUse);
            return;
        }

        if (!isLoggedIn) {
            console.log("Not logged in, cannot load recovery events");
            return;
        }

        console.log(
            "Loading recovery events for kind:",
            kindToUse,
            "user:",
            userPubkey,
        );
        isLoadingRecovery = true;
        try {
            // Fetch multiple versions using limit parameter
            // For replaceable events, limit > 1 returns multiple versions
            const filters = [
                {
                    kinds: [kindToUse],
                    authors: [userPubkey],
                    limit: 100, // Get up to 100 versions
                },
            ];

            if (recoveryOldestTimestamp) {
                filters[0].until = recoveryOldestTimestamp;
            }

            console.log("Recovery filters:", filters);

            // Use queryEvents which checks IndexedDB cache first, then relay
            const events = await queryEvents(filters, {
                timeout: 30000,
                cacheFirst: true, // Check cache first
            });

            console.log("Recovery events received:", events.length);
            console.log(
                "Recovery events kinds:",
                events.map((e) => e.kind),
            );

            if (recoveryOldestTimestamp) {
                // Append to existing events
                recoveryEvents = [...recoveryEvents, ...events];
            } else {
                // Replace events
                recoveryEvents = events;
            }

            if (events.length > 0) {
                recoveryOldestTimestamp = Math.min(
                    ...events.map((e) => e.created_at),
                );
                recoveryHasMore = events.length === 100;
            } else {
                recoveryHasMore = false;
            }
        } catch (error) {
            console.error("Failed to load recovery events:", error);
        } finally {
            isLoadingRecovery = false;
        }
    }

    async function repostEvent(event) {
        if (!confirm("Are you sure you want to repost this event?")) {
            return;
        }

        try {
            const localRelayUrl = `wss://${window.location.host}/`;
            console.log(
                "Reposting event to local relay:",
                localRelayUrl,
                event,
            );

            // Create a new event with updated timestamp
            const newEvent = { ...event };
            newEvent.created_at = Math.floor(Date.now() / 1000);
            newEvent.id = ""; // Clear the old ID so it gets recalculated
            newEvent.sig = ""; // Clear the old signature

            // For addressable events, ensure the d tag matches
            if (event.kind >= 30000 && event.kind <= 39999) {
                const dTag = event.tags.find((tag) => tag[0] === "d");
                if (dTag) {
                    newEvent.tags = newEvent.tags.filter(
                        (tag) => tag[0] !== "d",
                    );
                    newEvent.tags.push(dTag);
                }
            }

            // Sign the event before publishing
            if (userSigner) {
                const signedEvent = await userSigner.sign(newEvent);
                console.log("Signed event for repost:", signedEvent);

                const result = await nostrClient.publish(signedEvent, [
                    localRelayUrl,
                ]);
                console.log("Repost publish result:", result);

                if (result.success && result.okCount > 0) {
                    alert("Event reposted successfully!");
                    recoveryHasMore = false; // Reset to allow reloading
                    await loadRecoveryEvents(); // Reload the events to show the new version
                } else {
                    alert("Failed to repost event. Check console for details.");
                }
            } else {
                alert("No signer available. Please log in.");
            }
        } catch (error) {
            console.error("Error reposting event:", error);
            alert("Error reposting event: " + error.message);
        }
    }

    function selectRecoveryKind() {
        console.log(
            "selectRecoveryKind called, recoverySelectedKind:",
            recoverySelectedKind,
        );
        if (
            recoverySelectedKind === null ||
            recoverySelectedKind === undefined
        ) {
            console.log("No kind selected, skipping load");
            return;
        }
        recoveryCustomKind = ""; // Clear custom kind when selecting from dropdown
        recoveryEvents = [];
        recoveryOldestTimestamp = null;
        recoveryHasMore = true;
        loadRecoveryEvents();
    }

    function handleCustomKindInput() {
        console.log(
            "handleCustomKindInput called, recoveryCustomKind:",
            recoveryCustomKind,
        );
        // Check if a valid number was entered (including 0)
        const kindNum = parseInt(recoveryCustomKind);
        if (recoveryCustomKind !== "" && !isNaN(kindNum) && kindNum >= 0) {
            recoverySelectedKind = null; // Clear dropdown selection when using custom
            recoveryEvents = [];
            recoveryOldestTimestamp = null;
            recoveryHasMore = true;
            loadRecoveryEvents();
        }
    }

    function isCurrentVersion(event) {
        // Find all events with the same kind and pubkey
        const sameKindEvents = recoveryEvents.filter(
            (e) => e.kind === event.kind && e.pubkey === event.pubkey,
        );

        // Check if this event has the highest timestamp
        const maxTimestamp = Math.max(
            ...sameKindEvents.map((e) => e.created_at),
        );
        return event.created_at === maxTimestamp;
    }

    // Always show all versions - no filtering needed

    $: aboutHtml = userProfile?.about
        ? escapeHtml(userProfile.about).replace(/\n{2,}/g, "<br>")
        : "";

    // Load theme preference from localStorage on component initialization
    if (typeof localStorage !== "undefined") {
        const savedTheme = localStorage.getItem("isDarkTheme");
        if (savedTheme !== null) {
            isDarkTheme = JSON.parse(savedTheme);
        }

        // Check for existing authentication
        const storedAuthMethod = localStorage.getItem("nostr_auth_method");
        const storedPubkey = localStorage.getItem("nostr_pubkey");

        if (storedAuthMethod && storedPubkey) {
            isLoggedIn = true;
            userPubkey = storedPubkey;
            authMethod = storedAuthMethod;

            // Restore signer for extension method
            if (storedAuthMethod === "extension" && window.nostr) {
                userSigner = window.nostr;
            }

            // Fetch user role for already logged in users
            fetchUserRole();
            fetchACLMode();
        }

        // Load persistent app state
        loadPersistentState();

        // Load sprocket configuration
        loadSprocketConfig();
    }

    function savePersistentState() {
        if (typeof localStorage === "undefined") return;

        const state = {
            selectedTab,
            expandedEvents: Array.from(expandedEvents),
            globalEventsCache,
            globalCacheTimestamp,
            hasMoreEvents,
            oldestEventTimestamp,
        };

        localStorage.setItem("app_state", JSON.stringify(state));
    }

    function loadPersistentState() {
        if (typeof localStorage === "undefined") return;

        try {
            const savedState = localStorage.getItem("app_state");
            if (savedState) {
                const state = JSON.parse(savedState);

                // Restore tab state
                if (
                    state.selectedTab &&
                    baseTabs.some((tab) => tab.id === state.selectedTab)
                ) {
                    selectedTab = state.selectedTab;
                }

                // Restore expanded events
                if (state.expandedEvents) {
                    expandedEvents = new Set(state.expandedEvents);
                }

                // Restore cache data

                if (state.globalEventsCache) {
                    globalEventsCache = state.globalEventsCache;
                }

                if (state.globalCacheTimestamp) {
                    globalCacheTimestamp = state.globalCacheTimestamp;
                }

                if (state.hasMoreEvents !== undefined) {
                    hasMoreEvents = state.hasMoreEvents;
                }

                if (state.oldestEventTimestamp) {
                    oldestEventTimestamp = state.oldestEventTimestamp;
                }

                if (state.hasMoreMyEvents !== undefined) {
                    hasMoreMyEvents = state.hasMoreMyEvents;
                }

                if (state.oldestMyEventTimestamp) {
                    oldestMyEventTimestamp = state.oldestMyEventTimestamp;
                }

                // Restore events from cache
                restoreEventsFromCache();
            }
        } catch (error) {
            console.error("Failed to load persistent state:", error);
        }
    }

    function restoreEventsFromCache() {
        // Restore global events cache
        if (
            globalEventsCache.length > 0 &&
            isCacheValid(globalCacheTimestamp)
        ) {
            allEvents = globalEventsCache;
        }
    }

    function isCacheValid(timestamp) {
        if (!timestamp) return false;
        return Date.now() - timestamp < CACHE_DURATION;
    }

    function updateGlobalCache(events) {
        globalEventsCache = events.sort((a, b) => b.created_at - a.created_at);
        globalCacheTimestamp = Date.now();
        savePersistentState();
    }

    function clearCache() {
        globalEventsCache = [];
        globalCacheTimestamp = 0;
        savePersistentState();
    }

    // Sprocket management functions
    async function loadSprocketConfig() {
        try {
            const response = await fetch("/api/sprocket/config", {
                method: "GET",
                headers: {
                    "Content-Type": "application/json",
                },
            });

            if (response.ok) {
                const config = await response.json();
                sprocketEnabled = config.enabled;
            }
        } catch (error) {
            console.error("Error loading sprocket config:", error);
        }
    }

    async function loadSprocketStatus() {
        if (!isLoggedIn || userRole !== "owner" || !sprocketEnabled) return;

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/status", {
                method: "GET",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("GET", "/api/sprocket/status")}`,
                    "Content-Type": "application/json",
                },
            });

            if (response.ok) {
                sprocketStatus = await response.json();
            } else {
                showSprocketMessage("Failed to load sprocket status", "error");
            }
        } catch (error) {
            showSprocketMessage(
                `Error loading sprocket status: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    async function loadSprocket() {
        if (!isLoggedIn || userRole !== "owner") return;

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/status", {
                method: "GET",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("GET", "/api/sprocket/status")}`,
                    "Content-Type": "application/json",
                },
            });

            if (response.ok) {
                const status = await response.json();
                sprocketScript = status.script_content || "";
                sprocketStatus = status;
                showSprocketMessage("Script loaded successfully", "success");
            } else {
                showSprocketMessage("Failed to load script", "error");
            }
        } catch (error) {
            showSprocketMessage(
                `Error loading script: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    async function saveSprocket() {
        if (!isLoggedIn || userRole !== "owner") return;

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/update", {
                method: "POST",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("POST", "/api/sprocket/update")}`,
                    "Content-Type": "text/plain",
                },
                body: sprocketScript,
            });

            if (response.ok) {
                showSprocketMessage(
                    "Script saved and updated successfully",
                    "success",
                );
                await loadSprocketStatus();
                await loadVersions();
            } else {
                const errorText = await response.text();
                showSprocketMessage(
                    `Failed to save script: ${errorText}`,
                    "error",
                );
            }
        } catch (error) {
            showSprocketMessage(
                `Error saving script: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    async function restartSprocket() {
        if (!isLoggedIn || userRole !== "owner") return;

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/restart", {
                method: "POST",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("POST", "/api/sprocket/restart")}`,
                    "Content-Type": "application/json",
                },
            });

            if (response.ok) {
                showSprocketMessage(
                    "Sprocket restarted successfully",
                    "success",
                );
                await loadSprocketStatus();
            } else {
                const errorText = await response.text();
                showSprocketMessage(
                    `Failed to restart sprocket: ${errorText}`,
                    "error",
                );
            }
        } catch (error) {
            showSprocketMessage(
                `Error restarting sprocket: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    async function deleteSprocket() {
        if (!isLoggedIn || userRole !== "owner") return;

        if (
            !confirm(
                "Are you sure you want to delete the sprocket script? This will stop the current process.",
            )
        ) {
            return;
        }

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/update", {
                method: "POST",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("POST", "/api/sprocket/update")}`,
                    "Content-Type": "text/plain",
                },
                body: "", // Empty body deletes the script
            });

            if (response.ok) {
                sprocketScript = "";
                showSprocketMessage(
                    "Sprocket script deleted successfully",
                    "success",
                );
                await loadSprocketStatus();
                await loadVersions();
            } else {
                const errorText = await response.text();
                showSprocketMessage(
                    `Failed to delete script: ${errorText}`,
                    "error",
                );
            }
        } catch (error) {
            showSprocketMessage(
                `Error deleting script: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    async function loadVersions() {
        if (!isLoggedIn || userRole !== "owner") return;

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/versions", {
                method: "GET",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("GET", "/api/sprocket/versions")}`,
                    "Content-Type": "application/json",
                },
            });

            if (response.ok) {
                sprocketVersions = await response.json();
            } else {
                showSprocketMessage("Failed to load versions", "error");
            }
        } catch (error) {
            showSprocketMessage(
                `Error loading versions: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    async function loadVersion(version) {
        if (!isLoggedIn || userRole !== "owner") return;

        sprocketScript = version.content;
        showSprocketMessage(`Loaded version: ${version.name}`, "success");
    }

    async function deleteVersion(filename) {
        if (!isLoggedIn || userRole !== "owner") return;

        if (!confirm(`Are you sure you want to delete version ${filename}?`)) {
            return;
        }

        try {
            isLoadingSprocket = true;
            const response = await fetch("/api/sprocket/delete-version", {
                method: "POST",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("POST", "/api/sprocket/delete-version")}`,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ filename }),
            });

            if (response.ok) {
                showSprocketMessage(
                    `Version ${filename} deleted successfully`,
                    "success",
                );
                await loadVersions();
            } else {
                const errorText = await response.text();
                showSprocketMessage(
                    `Failed to delete version: ${errorText}`,
                    "error",
                );
            }
        } catch (error) {
            showSprocketMessage(
                `Error deleting version: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
        }
    }

    function showSprocketMessage(message, type = "info") {
        sprocketMessage = message;
        sprocketMessageType = type;

        // Auto-hide message after 5 seconds
        setTimeout(() => {
            sprocketMessage = "";
        }, 5000);
    }

    function handleSprocketFileSelect(event) {
        sprocketUploadFile = event.target.files[0];
    }

    async function uploadSprocketScript() {
        if (!isLoggedIn || userRole !== "owner" || !sprocketUploadFile) return;

        try {
            isLoadingSprocket = true;

            // Read the file content
            const fileContent = await sprocketUploadFile.text();

            // Upload the script
            const response = await fetch("/api/sprocket/update", {
                method: "POST",
                headers: {
                    Authorization: `Nostr ${await createNIP98Auth("POST", "/api/sprocket/update")}`,
                    "Content-Type": "text/plain",
                },
                body: fileContent,
            });

            if (response.ok) {
                sprocketScript = fileContent;
                showSprocketMessage(
                    "Script uploaded and updated successfully",
                    "success",
                );
                await loadSprocketStatus();
                await loadVersions();
            } else {
                const errorText = await response.text();
                showSprocketMessage(
                    `Failed to upload script: ${errorText}`,
                    "error",
                );
            }
        } catch (error) {
            showSprocketMessage(
                `Error uploading script: ${error.message}`,
                "error",
            );
        } finally {
            isLoadingSprocket = false;
            sprocketUploadFile = null;
            // Clear the file input
            const fileInput = document.getElementById("sprocket-upload-file");
            if (fileInput) {
                fileInput.value = "";
            }
        }
    }

    const baseTabs = [
        { id: "export", icon: "📤", label: "Export" },
        { id: "import", icon: "💾", label: "Import", requiresAdmin: true },
        { id: "events", icon: "📡", label: "Events" },
        { id: "compose", icon: "✏️", label: "Compose", requiresWrite: true },
        { id: "recovery", icon: "🔄", label: "Recovery" },
        {
            id: "managed-acl",
            icon: "🛡️",
            label: "Managed ACL",
            requiresOwner: true,
        },
        { id: "sprocket", icon: "⚙️", label: "Sprocket", requiresOwner: true },
    ];

    // Filter tabs based on current effective role (including view-as setting)
    $: filteredBaseTabs = baseTabs.filter((tab) => {
        const currentRole = currentEffectiveRole;

        if (
            tab.requiresAdmin &&
            (!isLoggedIn ||
                (currentRole !== "admin" && currentRole !== "owner"))
        ) {
            return false;
        }
        if (tab.requiresOwner && (!isLoggedIn || currentRole !== "owner")) {
            return false;
        }
        if (tab.requiresWrite && (!isLoggedIn || currentRole === "read")) {
            return false;
        }
        // Hide sprocket tab if not enabled
        if (tab.id === "sprocket" && !sprocketEnabled) {
            return false;
        }
        // Hide managed ACL tab if not in managed mode
        if (tab.id === "managed-acl" && aclMode !== "managed") {
            return false;
        }
        // Debug logging for tab filtering
        console.log(`Tab ${tab.id} filter check:`, {
            isLoggedIn,
            userRole,
            viewAsRole,
            currentRole,
            requiresAdmin: tab.requiresAdmin,
            requiresOwner: tab.requiresOwner,
            requiresWrite: tab.requiresWrite,
            visible: true,
        });
        return true;
    });

    $: tabs = [...filteredBaseTabs, ...searchTabs];

    // Debug logging for tabs
    $: console.log("Tabs debug:", {
        isLoggedIn,
        userRole,
        aclMode,
        filteredBaseTabs: filteredBaseTabs.map((t) => t.id),
        allTabs: tabs.map((t) => t.id),
    });

    function selectTab(tabId) {
        selectedTab = tabId;

        // Load sprocket data when switching to sprocket tab
        if (
            tabId === "sprocket" &&
            isLoggedIn &&
            userRole === "owner" &&
            sprocketEnabled
        ) {
            loadSprocketStatus();
            loadVersions();
        }

        savePersistentState();
    }

    function toggleTheme() {
        isDarkTheme = !isDarkTheme;
        // Save theme preference to localStorage
        if (typeof localStorage !== "undefined") {
            localStorage.setItem("isDarkTheme", JSON.stringify(isDarkTheme));
        }
    }

    function openLoginModal() {
        if (!isLoggedIn) {
            showLoginModal = true;
        }
    }

    async function handleLogin(event) {
        const { method, pubkey, privateKey, signer } = event.detail;
        isLoggedIn = true;
        userPubkey = pubkey;
        authMethod = method;
        userSigner = signer;
        showLoginModal = false;

        // Initialize Nostr client and fetch profile
        try {
            await initializeNostrClient();

            // Set up NDK signer based on authentication method
            if (method === "extension" && signer) {
                // Extension signer (NIP-07 compatible)
                nostrClient.setSigner(signer);
            } else if (method === "nsec" && privateKey) {
                // Private key signer for nsec
                const keySigner = new PrivateKeySigner(privateKey);
                nostrClient.setSigner(keySigner);
            }

            userProfile = await fetchUserProfile(pubkey);
            console.log("Profile loaded:", userProfile);
        } catch (error) {
            console.error("Failed to load profile:", error);
        }

        // Fetch user role/permissions
        await fetchUserRole();
        await fetchACLMode();
    }

    function handleLogout() {
        isLoggedIn = false;
        userPubkey = "";
        authMethod = "";
        userProfile = null;
        userRole = "";
        userSigner = null;
        showSettingsDrawer = false;

        // Clear events
        myEvents = [];
        allEvents = [];

        // Clear cache
        clearCache();

        // Clear stored authentication
        if (typeof localStorage !== "undefined") {
            localStorage.removeItem("nostr_auth_method");
            localStorage.removeItem("nostr_pubkey");
            localStorage.removeItem("nostr_privkey");
        }
    }

    function closeLoginModal() {
        showLoginModal = false;
    }

    function openSettingsDrawer() {
        showSettingsDrawer = true;
    }

    function closeSettingsDrawer() {
        showSettingsDrawer = false;
    }

    function toggleSearchMode() {
        isSearchMode = !isSearchMode;
        if (!isSearchMode) {
            searchQuery = "";
        }
    }

    function handleSearchKeydown(event) {
        if (event.key === "Enter" && searchQuery.trim()) {
            createSearchTab(searchQuery.trim());
            searchQuery = "";
            isSearchMode = false;
        } else if (event.key === "Escape") {
            isSearchMode = false;
            searchQuery = "";
        }
    }

    function createSearchTab(query) {
        const searchTabId = `search-${Date.now()}`;
        const newSearchTab = {
            id: searchTabId,
            icon: "🔍",
            label: query,
            isSearchTab: true,
            query: query,
        };
        searchTabs = [...searchTabs, newSearchTab];
        selectedTab = searchTabId;

        // Initialize search results for this tab
        searchResults.set(searchTabId, {
            events: [],
            isLoading: false,
            hasMore: true,
            oldestTimestamp: null,
        });

        // Start loading search results
        loadSearchResults(searchTabId, query);
    }

    function closeSearchTab(tabId) {
        searchTabs = searchTabs.filter((tab) => tab.id !== tabId);
        searchResults.delete(tabId); // Clean up search results
        if (selectedTab === tabId) {
            selectedTab = "export"; // Fall back to export tab
        }
    }

    async function loadSearchResults(searchTabId, query, reset = true) {
        const searchResult = searchResults.get(searchTabId);
        if (!searchResult || searchResult.isLoading) return;

        // Update loading state
        searchResult.isLoading = true;
        searchResults.set(searchTabId, searchResult);

        try {
            const options = {
                limit: reset ? 100 : 200,
                until: reset
                    ? Math.floor(Date.now() / 1000)
                    : searchResult.oldestTimestamp,
            };

            console.log(
                "Loading search results for query:",
                query,
                "with options:",
                options,
            );
            const events = await searchEvents(query, options);
            console.log("Received search results:", events.length, "events");

            if (reset) {
                searchResult.events = events.sort(
                    (a, b) => b.created_at - a.created_at,
                );
            } else {
                searchResult.events = [...searchResult.events, ...events].sort(
                    (a, b) => b.created_at - a.created_at,
                );
            }

            // Update oldest timestamp for next pagination
            if (events.length > 0) {
                const oldestInBatch = Math.min(
                    ...events.map((e) => e.created_at),
                );
                if (
                    !searchResult.oldestTimestamp ||
                    oldestInBatch < searchResult.oldestTimestamp
                ) {
                    searchResult.oldestTimestamp = oldestInBatch;
                }
            }

            searchResult.hasMore = events.length === (reset ? 100 : 200);
            searchResult.isLoading = false;
            searchResults.set(searchTabId, searchResult);
        } catch (error) {
            console.error("Failed to load search results:", error);
            searchResult.isLoading = false;
            searchResults.set(searchTabId, searchResult);
            alert("Failed to load search results: " + error.message);
        }
    }

    async function loadMoreSearchResults(searchTabId) {
        const searchTab = searchTabs.find((tab) => tab.id === searchTabId);
        if (searchTab) {
            await loadSearchResults(searchTabId, searchTab.query, false);
        }
    }

    function handleSearchScroll(event, searchTabId) {
        const { scrollTop, scrollHeight, clientHeight } = event.target;
        const threshold = 100; // Load more when 100px from bottom

        if (scrollHeight - scrollTop - clientHeight < threshold) {
            const searchResult = searchResults.get(searchTabId);
            if (
                searchResult &&
                !searchResult.isLoading &&
                searchResult.hasMore
            ) {
                loadMoreSearchResults(searchTabId);
            }
        }
    }

    $: if (typeof document !== "undefined") {
        if (isDarkTheme) {
            document.body.classList.add("dark-theme");
        } else {
            document.body.classList.remove("dark-theme");
        }
    }

    // Auto-fetch profile when user is logged in but profile is missing
    $: if (isLoggedIn && userPubkey && !userProfile) {
        fetchProfileIfMissing();
    }

    async function fetchProfileIfMissing() {
        if (!isLoggedIn || !userPubkey || userProfile) {
            return; // Don't fetch if not logged in, no pubkey, or profile already exists
        }

        try {
            console.log("Auto-fetching profile for:", userPubkey);
            await initializeNostrClient();
            userProfile = await fetchUserProfile(userPubkey);
            console.log("Profile auto-loaded:", userProfile);
        } catch (error) {
            console.error("Failed to auto-load profile:", error);
        }
    }

    async function fetchUserRole() {
        if (!isLoggedIn || !userPubkey) {
            userRole = "";
            return;
        }

        try {
            const response = await fetch(`/api/permissions/${userPubkey}`);
            if (response.ok) {
                const data = await response.json();
                userRole = data.permission || "";
                console.log("User role loaded:", userRole);
                console.log("Is owner?", userRole === "owner");
            } else {
                console.error("Failed to fetch user role:", response.status);
                userRole = "";
            }
        } catch (error) {
            console.error("Error fetching user role:", error);
            userRole = "";
        }
    }

    async function fetchACLMode() {
        try {
            const response = await fetch("/api/acl-mode");
            if (response.ok) {
                const data = await response.json();
                aclMode = data.acl_mode || "";
                console.log("ACL mode loaded:", aclMode);
            } else {
                console.error("Failed to fetch ACL mode:", response.status);
                aclMode = "";
            }
        } catch (error) {
            console.error("Error fetching ACL mode:", error);
            aclMode = "";
        }
    }

    // Export functionality
    async function exportEvents(pubkeys = []) {
        if (!isLoggedIn) {
            alert("Please log in first");
            return;
        }

        // Check permissions for exporting all events using current effective role
        if (
            pubkeys.length === 0 &&
            currentEffectiveRole !== "admin" &&
            currentEffectiveRole !== "owner"
        ) {
            alert("Admin or owner permission required to export all events");
            return;
        }

        try {
            const authHeader = await createNIP98AuthHeader(
                "/api/export",
                "POST",
            );
            const response = await fetch("/api/export", {
                method: "POST",
                headers: {
                    Authorization: authHeader,
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ pubkeys }),
            });

            if (!response.ok) {
                throw new Error(
                    `Export failed: ${response.status} ${response.statusText}`,
                );
            }

            const blob = await response.blob();
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.href = url;

            // Get filename from response headers or use default
            const contentDisposition = response.headers.get(
                "Content-Disposition",
            );
            let filename = "events.jsonl";
            if (contentDisposition) {
                const filenameMatch =
                    contentDisposition.match(/filename="([^"]+)"/);
                if (filenameMatch) {
                    filename = filenameMatch[1];
                }
            }

            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        } catch (error) {
            console.error("Export failed:", error);
            alert("Export failed: " + error.message);
        }
    }

    async function exportAllEvents() {
        await exportEvents([]); // Empty array means export all events
    }

    async function exportMyEvents() {
        await exportEvents([userPubkey]); // Export only current user's events
    }

    // Import functionality
    function handleFileSelect(event) {
        selectedFile = event.target.files[0];
    }

    async function importEvents() {
        if (!isLoggedIn || (userRole !== "admin" && userRole !== "owner")) {
            alert("Admin or owner permission required");
            return;
        }

        if (!selectedFile) {
            alert("Please select a file");
            return;
        }

        try {
            const authHeader = await createNIP98AuthHeader(
                "/api/import",
                "POST",
            );
            const formData = new FormData();
            formData.append("file", selectedFile);

            const response = await fetch("/api/import", {
                method: "POST",
                headers: {
                    Authorization: authHeader,
                },
                body: formData,
            });

            if (!response.ok) {
                throw new Error(
                    `Import failed: ${response.status} ${response.statusText}`,
                );
            }

            const result = await response.json();
            alert("Import started successfully");
            selectedFile = null;
            document.getElementById("import-file").value = "";
        } catch (error) {
            console.error("Import failed:", error);
            alert("Import failed: " + error.message);
        }
    }

    // Events loading functionality
    async function loadMyEvents(reset = false) {
        if (!isLoggedIn) {
            alert("Please log in first");
            return;
        }

        if (isLoadingMyEvents) return;

        // Always load fresh data when feed becomes visible (reset = true)
        // Skip cache check to ensure fresh data every time

        isLoadingMyEvents = true;

        // Reset timestamps when doing a fresh load
        if (reset) {
            oldestMyEventTimestamp = null;
            newestMyEventTimestamp = null;
        }

        try {
            // Use WebSocket REQ to fetch user events with timestamp-based pagination
            // Load 1000 events on initial load, otherwise use 200 for pagination
            const events = await fetchUserEvents(userPubkey, {
                limit: reset ? 1000 : 200,
                until: reset ? null : oldestMyEventTimestamp,
            });

            if (reset) {
                myEvents = events.sort((a, b) => b.created_at - a.created_at);
            } else {
                myEvents = [...myEvents, ...events].sort(
                    (a, b) => b.created_at - a.created_at,
                );
            }

            // Update oldest timestamp for next pagination
            if (events.length > 0) {
                const oldestInBatch = Math.min(
                    ...events.map((e) => e.created_at),
                );
                if (
                    !oldestMyEventTimestamp ||
                    oldestInBatch < oldestMyEventTimestamp
                ) {
                    oldestMyEventTimestamp = oldestInBatch;
                }
            }

            hasMoreMyEvents = events.length === (reset ? 1000 : 200);

            // Auto-load more events if content doesn't fill viewport and more events are available
            // Only do this on initial load (reset = true) to avoid interfering with scroll-based loading
            if (reset && hasMoreMyEvents) {
                setTimeout(() => {
                    // Only check viewport if we're currently on the My Events tab
                    if (selectedTab === "myevents") {
                        const eventsContainers = document.querySelectorAll(
                            ".events-view-content",
                        );
                        // The My Events container should be the first one (before All Events)
                        const myEventsContainer = eventsContainers[0];
                        if (
                            myEventsContainer &&
                            myEventsContainer.scrollHeight <=
                                myEventsContainer.clientHeight
                        ) {
                            // Content doesn't fill viewport, load more automatically
                            loadMoreMyEvents();
                        }
                    }
                }, 100); // Small delay to ensure DOM is updated
            }
        } catch (error) {
            console.error("Failed to load events:", error);
            alert("Failed to load events: " + error.message);
        } finally {
            isLoadingMyEvents = false;
        }
    }

    async function loadMoreMyEvents() {
        if (!isLoadingMyEvents && hasMoreMyEvents) {
            await loadMyEvents(false);
        }
    }

    function handleMyEventsScroll(event) {
        const { scrollTop, scrollHeight, clientHeight } = event.target;
        const threshold = 100; // Load more when 100px from bottom

        if (scrollHeight - scrollTop - clientHeight < threshold) {
            loadMoreMyEvents();
        }
    }

    async function loadAllEvents(reset = false, authors = null) {
        if (
            !isLoggedIn ||
            (userRole !== "write" &&
                userRole !== "admin" &&
                userRole !== "owner")
        ) {
            alert("Write, admin, or owner permission required");
            return;
        }

        if (isLoadingEvents) return;

        // Always load fresh data when feed becomes visible (reset = true)
        // Skip cache check to ensure fresh data every time

        isLoadingEvents = true;

        // Reset timestamps when doing a fresh load
        if (reset) {
            oldestEventTimestamp = null;
            newestEventTimestamp = null;
        }

        try {
            // Use Nostr WebSocket to fetch events with timestamp-based pagination
            // Load 100 events on initial load, otherwise use 200 for pagination
            console.log(
                "Loading events with authors filter:",
                authors,
                "including delete events",
            );
            const events = await fetchAllEvents({
                limit: reset ? 100 : 200,
                until: reset
                    ? Math.floor(Date.now() / 1000)
                    : oldestEventTimestamp,
                authors: authors,
            });
            console.log("Received events:", events.length, "events");
            if (authors && events.length > 0) {
                const nonUserEvents = events.filter(
                    (event) => event.pubkey && event.pubkey !== userPubkey,
                );
                if (nonUserEvents.length > 0) {
                    console.warn(
                        "Server returned non-user events:",
                        nonUserEvents.length,
                        "out of",
                        events.length,
                    );
                }
            }

            if (reset) {
                allEvents = events.sort((a, b) => b.created_at - a.created_at);
                // Update global cache
                updateGlobalCache(events);
            } else {
                allEvents = [...allEvents, ...events].sort(
                    (a, b) => b.created_at - a.created_at,
                );
                // Update global cache with all events
                updateGlobalCache(allEvents);
            }

            // Update oldest timestamp for next pagination
            if (events.length > 0) {
                const oldestInBatch = Math.min(
                    ...events.map((e) => e.created_at),
                );
                if (
                    !oldestEventTimestamp ||
                    oldestInBatch < oldestEventTimestamp
                ) {
                    oldestEventTimestamp = oldestInBatch;
                }
            }

            hasMoreEvents = events.length === (reset ? 1000 : 200);

            // Auto-load more events if content doesn't fill viewport and more events are available
            // Only do this on initial load (reset = true) to avoid interfering with scroll-based loading
            if (reset && hasMoreEvents) {
                setTimeout(() => {
                    // Only check viewport if we're currently on the All Events tab
                    if (selectedTab === "events") {
                        const eventsContainers = document.querySelectorAll(
                            ".events-view-content",
                        );
                        // The All Events container should be the first one (only container now)
                        const allEventsContainer = eventsContainers[0];
                        if (
                            allEventsContainer &&
                            allEventsContainer.scrollHeight <=
                                allEventsContainer.clientHeight
                        ) {
                            // Content doesn't fill viewport, load more automatically
                            loadMoreEvents();
                        }
                    }
                }, 100); // Small delay to ensure DOM is updated
            }
        } catch (error) {
            console.error("Failed to load events:", error);
            alert("Failed to load events: " + error.message);
        } finally {
            isLoadingEvents = false;
        }
    }

    async function loadMoreEvents() {
        await loadAllEvents(false);
    }

    function handleScroll(event) {
        const { scrollTop, scrollHeight, clientHeight } = event.target;
        const threshold = 100; // Load more when 100px from bottom

        if (scrollHeight - scrollTop - clientHeight < threshold) {
            loadMoreEvents();
        }
    }

    // Load events when events tab is selected (only if no events loaded yet)
    $: if (
        selectedTab === "events" &&
        isLoggedIn &&
        (userRole === "write" ||
            userRole === "admin" ||
            userRole === "owner") &&
        allEvents.length === 0
    ) {
        const authors = showOnlyMyEvents && userPubkey ? [userPubkey] : null;
        loadAllEvents(true, authors);
    }

    // NIP-98 authentication helper
    async function createNIP98AuthHeader(url, method) {
        if (!isLoggedIn || !userPubkey) {
            throw new Error("Not logged in");
        }

        // Create NIP-98 auth event
        const authEvent = {
            kind: 27235,
            created_at: Math.floor(Date.now() / 1000),
            tags: [
                ["u", window.location.origin + url],
                ["method", method.toUpperCase()],
            ],
            content: "",
            pubkey: userPubkey,
        };

        let signedEvent;

        if (userSigner && authMethod === "extension") {
            // Use the signer from the extension
            try {
                signedEvent = await userSigner.signEvent(authEvent);
            } catch (error) {
                throw new Error(
                    "Failed to sign with extension: " + error.message,
                );
            }
        } else if (authMethod === "nsec") {
            // For nsec method, we need to implement proper signing
            // For now, create a mock signature (in production, use proper crypto)
            authEvent.id = "mock-id-" + Date.now();
            authEvent.sig = "mock-signature-" + Date.now();
            signedEvent = authEvent;
        } else {
            throw new Error("No valid signer available");
        }

        // Encode as base64
        const eventJson = JSON.stringify(signedEvent);
        const base64Event = btoa(eventJson);

        return `Nostr ${base64Event}`;
    }

    // NIP-98 authentication helper (for sprocket functions)
    async function createNIP98Auth(method, url) {
        if (!isLoggedIn || !userPubkey) {
            throw new Error("Not logged in");
        }

        // Create NIP-98 auth event
        const authEvent = {
            kind: 27235,
            created_at: Math.floor(Date.now() / 1000),
            tags: [
                ["u", window.location.origin + url],
                ["method", method.toUpperCase()],
            ],
            content: "",
            pubkey: userPubkey,
        };

        let signedEvent;

        if (userSigner && authMethod === "extension") {
            // Use the signer from the extension
            try {
                signedEvent = await userSigner.signEvent(authEvent);
            } catch (error) {
                throw new Error(
                    "Failed to sign with extension: " + error.message,
                );
            }
        } else if (authMethod === "nsec") {
            // For nsec method, we need to implement proper signing
            // For now, create a mock signature (in production, use proper crypto)
            authEvent.id = "mock-id-" + Date.now();
            authEvent.sig = "mock-signature-" + Date.now();
            signedEvent = authEvent;
        } else {
            throw new Error("No valid signer available");
        }

        // Encode as base64
        const eventJson = JSON.stringify(signedEvent);
        const base64Event = btoa(eventJson);

        return base64Event;
    }

    // Compose tab functions
    function reformatJson() {
        try {
            if (!composeEventJson.trim()) {
                alert("Please enter some JSON to reformat");
                return;
            }
            const parsed = JSON.parse(composeEventJson);
            composeEventJson = JSON.stringify(parsed, null, 2);
        } catch (error) {
            alert("Invalid JSON: " + error.message);
        }
    }

    async function signEvent() {
        try {
            if (!composeEventJson.trim()) {
                alert("Please enter an event to sign");
                return;
            }

            if (!isLoggedIn || !userPubkey) {
                alert("Please log in to sign events");
                return;
            }

            if (!userSigner) {
                alert(
                    "No signer available. Please log in with a valid authentication method.",
                );
                return;
            }

            const event = JSON.parse(composeEventJson);

            // Update event with current user's pubkey and timestamp
            event.pubkey = userPubkey;
            event.created_at = Math.floor(Date.now() / 1000);

            // Remove any existing id and sig to ensure fresh signing
            delete event.id;
            delete event.sig;

            // Sign the event using the real signer
            const signedEvent = await userSigner.signEvent(event);

            // Update the compose area with the signed event
            composeEventJson = JSON.stringify(signedEvent, null, 2);

            // Show success feedback
            alert("Event signed successfully!");
        } catch (error) {
            console.error("Error signing event:", error);
            alert("Error signing event: " + error.message);
        }
    }

    async function publishEvent() {
        try {
            if (!composeEventJson.trim()) {
                alert("Please enter an event to publish");
                return;
            }

            if (!isLoggedIn) {
                alert("Please log in to publish events");
                return;
            }

            if (!userSigner) {
                alert(
                    "No signer available. Please log in with a valid authentication method.",
                );
                return;
            }

            const event = JSON.parse(composeEventJson);

            // Validate that the event has required fields
            if (!event.id || !event.sig) {
                alert(
                    'Event must be signed before publishing. Please click "Sign" first.',
                );
                return;
            }

            // Publish to the ORLY relay using WebSocket (same address as current page)
            const relayUrl = `wss://${window.location.host}`;

            // Use the authentication module to publish the event
            const result = await publishEventWithAuth(
                relayUrl,
                event,
                userSigner,
                userPubkey,
            );

            if (result.success) {
                alert("Event published successfully to ORLY relay!");
                // Optionally clear the editor after successful publish
                // composeEventJson = '';
            } else {
                alert(
                    `Event publishing failed: ${result.reason || "Unknown error"}`,
                );
            }
        } catch (error) {
            console.error("Error publishing event:", error);
            alert("Error publishing event: " + error.message);
        }
    }

    // Persist selected tab to local storage
    $: {
        localStorage.setItem("selectedTab", selectedTab);
    }

    // Handle permission view switching
    function setViewAsRole(role) {
        viewAsRole = role;
        localStorage.setItem("viewAsRole", role);
        console.log(
            "View as role changed to:",
            role,
            "Current effective role:",
            currentEffectiveRole,
        );
    }

    // Reactive statement for current effective role
    $: currentEffectiveRole =
        viewAsRole && viewAsRole !== "" ? viewAsRole : userRole;

    // Initialize viewAsRole from local storage if available
    viewAsRole = localStorage.getItem("viewAsRole") || "";

    // Get available roles based on user's actual role
    function getAvailableRoles() {
        const allRoles = ["owner", "admin", "write", "read"];
        const userRoleIndex = allRoles.indexOf(userRole);
        if (userRoleIndex === -1) return ["read"]; // Default to read if role not found
        return allRoles.slice(userRoleIndex); // Return current role and all lower roles
    }
</script>

<!-- Header -->
<header class="main-header" class:dark-theme={isDarkTheme}>
    <div class="header-content">
        <img src="/orly.png" alt="ORLY Logo" class="logo" />
        {#if isSearchMode}
            <div class="search-input-container">
                <input
                    type="text"
                    class="search-input"
                    bind:value={searchQuery}
                    on:keydown={handleSearchKeydown}
                    placeholder="Search..."
                />
            </div>
        {:else}
            <div class="header-title">
                <span class="app-title">
                    ORLY? dashboard
                    {#if isLoggedIn && userRole}
                        <span class="permission-badge"
                            >{currentEffectiveRole}</span
                        >
                    {/if}
                </span>
            </div>
        {/if}
        <button class="search-btn" on:click={toggleSearchMode}> 🔍 </button>
        <button class="theme-toggle-btn" on:click={toggleTheme}>
            {isDarkTheme ? "☀️" : "🌙"}
        </button>
        {#if isLoggedIn}
            <div class="user-info">
                <button class="user-profile-btn" on:click={openSettingsDrawer}>
                    {#if userProfile?.picture}
                        <img
                            src={userProfile.picture}
                            alt="User avatar"
                            class="user-avatar"
                        />
                    {:else}
                        <div class="user-avatar-placeholder">👤</div>
                    {/if}
                    <span class="user-name">
                        {userProfile?.name || userPubkey.slice(0, 8) + "..."}
                    </span>
                </button>
            </div>
        {:else}
            <button class="login-btn" on:click={openLoginModal}>Log in</button>
        {/if}
    </div>
</header>

<!-- Main Content Area -->
<div class="app-container" class:dark-theme={isDarkTheme}>
    <!-- Sidebar -->
    <aside class="sidebar" class:dark-theme={isDarkTheme}>
        <div class="sidebar-content">
            <div class="tabs">
                {#each tabs as tab}
                    <button
                        class="tab"
                        class:active={selectedTab === tab.id}
                        on:click={() => selectTab(tab.id)}
                    >
                        <span class="tab-icon">{tab.icon}</span>
                        <span class="tab-label">{tab.label}</span>
                        {#if tab.isSearchTab}
                            <span
                                class="tab-close-icon"
                                on:click|stopPropagation={() =>
                                    closeSearchTab(tab.id)}
                                on:keydown={(e) =>
                                    e.key === "Enter" && closeSearchTab(tab.id)}
                                role="button"
                                tabindex="0">✕</span
                            >
                        {/if}
                    </button>
                {/each}
            </div>
        </div>
    </aside>

    <!-- Main Content -->
    <main class="main-content">
        {#if selectedTab === "export"}
            {#if isLoggedIn}
                <div class="export-section">
                    <h3>Export My Events</h3>
                    <p>Download your personal events as a JSONL file.</p>
                    <button class="export-btn" on:click={exportMyEvents}>
                        📤 Export My Events
                    </button>
                </div>
                {#if currentEffectiveRole === "admin" || currentEffectiveRole === "owner"}
                    <div class="export-section">
                        <h3>Export All Events</h3>
                        <p>
                            Download the complete database as a JSONL file. This
                            includes all events from all users.
                        </p>
                        <button class="export-btn" on:click={exportAllEvents}>
                            📤 Export All Events
                        </button>
                    </div>
                {/if}
            {:else}
                <div class="login-prompt">
                    <p>Please log in to access export functionality.</p>
                    <button class="login-btn" on:click={openLoginModal}
                        >Log In</button
                    >
                </div>
            {/if}
        {:else if selectedTab === "import"}
            <div class="import-section">
                {#if isLoggedIn && (currentEffectiveRole === "admin" || currentEffectiveRole === "owner")}
                    <h3>Import Events</h3>
                    <p>
                        Upload a JSONL file to import events into the database.
                    </p>
                    <div class="recovery-controls-card">
                        <input
                            type="file"
                            id="import-file"
                            accept=".jsonl,.txt"
                            on:change={handleFileSelect}
                        />
                        <button
                            class="import-btn"
                            on:click={importEvents}
                            disabled={!selectedFile}
                        >
                            Import Events
                        </button>
                    </div>
                {:else if isLoggedIn}
                    <div class="permission-denied">
                        <h3 class="recovery-header">Import Events</h3>
                        <p class="recovery-description">
                            ❌ Admin or owner permission required for import
                            functionality.
                        </p>
                    </div>
                {:else}
                    <div class="login-prompt">
                        <h3 class="recovery-header">Import Events</h3>
                        <p class="recovery-description">
                            Please log in to access import functionality.
                        </p>
                        <button class="login-btn" on:click={openLoginModal}
                            >Log In</button
                        >
                    </div>
                {/if}
            </div>
        {:else if selectedTab === "events"}
            <div class="events-view-container">
                {#if isLoggedIn && (userRole === "write" || userRole === "admin" || userRole === "owner")}
                    <div class="events-view-content" on:scroll={handleScroll}>
                        {#if filteredEvents.length > 0}
                            {#each filteredEvents as event}
                                <div
                                    class="events-view-item"
                                    class:expanded={expandedEvents.has(
                                        event.id,
                                    )}
                                >
                                    <div
                                        class="events-view-row"
                                        on:click={() =>
                                            toggleEventExpansion(event.id)}
                                        on:keydown={(e) =>
                                            e.key === "Enter" &&
                                            toggleEventExpansion(event.id)}
                                        role="button"
                                        tabindex="0"
                                    >
                                        <div class="events-view-avatar">
                                            <div class="avatar-placeholder">
                                                👤
                                            </div>
                                        </div>
                                        <div class="events-view-info">
                                            <div class="events-view-author">
                                                {truncatePubkey(event.pubkey)}
                                            </div>
                                            <div class="events-view-kind">
                                                <span
                                                    class="kind-number"
                                                    class:delete-event={event.kind ===
                                                        5}>{event.kind}</span
                                                >
                                                <span class="kind-name"
                                                    >{getKindName(
                                                        event.kind,
                                                    )}</span
                                                >
                                            </div>
                                        </div>
                                        <div class="events-view-content">
                                            <div class="event-timestamp">
                                                {formatTimestamp(
                                                    event.created_at,
                                                )}
                                            </div>
                                            {#if event.kind === 5}
                                                <div class="delete-event-info">
                                                    <span
                                                        class="delete-event-label"
                                                        >🗑️ Delete Event</span
                                                    >
                                                    {#if event.tags && event.tags.length > 0}
                                                        <div
                                                            class="delete-targets"
                                                        >
                                                            {#each event.tags.filter((tag) => tag[0] === "e") as eTag}
                                                                <span
                                                                    class="delete-target"
                                                                    >Target: {eTag[1].slice(
                                                                        0,
                                                                        8,
                                                                    )}...{eTag[1].slice(
                                                                        -8,
                                                                    )}</span
                                                                >
                                                            {/each}
                                                        </div>
                                                    {/if}
                                                </div>
                                            {:else}
                                                <div
                                                    class="event-content-single-line"
                                                >
                                                    {truncateContent(
                                                        event.content,
                                                    )}
                                                </div>
                                            {/if}
                                        </div>
                                        {#if event.kind !== 5 && (userRole === "admin" || userRole === "owner" || (userRole === "write" && event.pubkey && event.pubkey === userPubkey))}
                                            <button
                                                class="delete-btn"
                                                on:click|stopPropagation={() =>
                                                    deleteEvent(event.id)}
                                            >
                                                🗑️
                                            </button>
                                        {/if}
                                    </div>
                                    {#if expandedEvents.has(event.id)}
                                        <div class="events-view-details">
                                            <div class="json-container">
                                                <pre
                                                    class="event-json">{JSON.stringify(
                                                        event,
                                                        null,
                                                        2,
                                                    )}</pre>
                                                <button
                                                    class="copy-json-btn"
                                                    on:click|stopPropagation={(
                                                        e,
                                                    ) =>
                                                        copyEventToClipboard(
                                                            event,
                                                            e,
                                                        )}
                                                    title="Copy minified JSON to clipboard"
                                                >
                                                    📋
                                                </button>
                                            </div>
                                        </div>
                                    {/if}
                                </div>
                            {/each}
                        {:else if !isLoadingEvents}
                            <div class="no-events">
                                <p>No events found.</p>
                            </div>
                        {/if}

                        {#if isLoadingEvents}
                            <div class="loading-events">
                                <div class="loading-spinner"></div>
                                <p>Loading events...</p>
                            </div>
                        {/if}

                        {#if !hasMoreEvents && allEvents.length > 0}
                            <div class="end-of-events">
                                <p>No more events to load.</p>
                            </div>
                        {/if}
                    </div>
                {:else}
                    <div class="permission-denied">
                        <p>
                            ❌ Write, admin, or owner permission required to
                            view all events.
                        </p>
                    </div>
                {/if}
                {#if isLoggedIn && (userRole === "write" || userRole === "admin" || userRole === "owner")}
                    <div class="events-view-header">
                        <div class="events-view-toggle">
                            <label class="toggle-container">
                                <input
                                    type="checkbox"
                                    bind:checked={showOnlyMyEvents}
                                    on:change={() => handleToggleChange()}
                                />
                                <span class="toggle-slider"></span>
                                <span class="toggle-label"
                                    >Only show my events</span
                                >
                            </label>
                        </div>
                        <div class="events-view-buttons">
                            <button
                                class="refresh-btn"
                                on:click={() => {
                                    const authors =
                                        showOnlyMyEvents && userPubkey
                                            ? [userPubkey]
                                            : null;
                                    loadAllEvents(false, authors);
                                }}
                                disabled={isLoadingEvents}
                            >
                                🔄 Load More
                            </button>
                            <button
                                class="reload-btn"
                                on:click={() => {
                                    const authors =
                                        showOnlyMyEvents && userPubkey
                                            ? [userPubkey]
                                            : null;
                                    loadAllEvents(true, authors);
                                }}
                                disabled={isLoadingEvents}
                            >
                                {#if isLoadingEvents}
                                    <div class="spinner"></div>
                                {:else}
                                    🔄
                                {/if}
                            </button>
                        </div>
                    </div>
                {/if}
            </div>
        {:else if selectedTab === "compose"}
            <div class="compose-view">
                <div class="compose-header">
                    <button
                        class="compose-btn reformat-btn"
                        on:click={reformatJson}>Reformat</button
                    >
                    <button class="compose-btn sign-btn" on:click={signEvent}
                        >Sign</button
                    >
                    <button
                        class="compose-btn publish-btn"
                        on:click={publishEvent}>Publish</button
                    >
                </div>
                <div class="compose-editor">
                    <textarea
                        bind:value={composeEventJson}
                        class="compose-textarea"
                        placeholder="Enter your Nostr event JSON here..."
                        spellcheck="false"
                    ></textarea>
                </div>
            </div>
        {:else if selectedTab === "managed-acl"}
            <div class="managed-acl-view">
                {#if aclMode !== "managed"}
                    <div class="acl-mode-warning">
                        <h3>⚠️ Managed ACL Mode Not Active</h3>
                        <p>
                            To use the Managed ACL interface, you need to set
                            the ACL mode to "managed" in your relay
                            configuration.
                        </p>
                        <p>
                            Current ACL mode: <strong
                                >{aclMode || "unknown"}</strong
                            >
                        </p>
                        <p>
                            Please set <code>ORLY_ACL_MODE=managed</code> in your
                            environment variables and restart the relay.
                        </p>
                    </div>
                {:else if isLoggedIn && userRole === "owner"}
                    <ManagedACL {userSigner} {userPubkey} />
                {:else}
                    <div class="access-denied">
                        <p>
                            Please log in with owner permissions to access
                            managed ACL configuration.
                        </p>
                        <button class="login-btn" on:click={openLoginModal}
                            >Log In</button
                        >
                    </div>
                {/if}
            </div>
        {:else if selectedTab === "sprocket"}
            <div class="sprocket-view">
                <h2>Sprocket Script Management</h2>
                {#if isLoggedIn && userRole === "owner"}
                    <div class="sprocket-section">
                        <div class="sprocket-header">
                            <h3>Script Editor</h3>
                            <div class="sprocket-controls">
                                <button
                                    class="sprocket-btn restart-btn"
                                    on:click={restartSprocket}
                                    disabled={isLoadingSprocket}
                                >
                                    🔄 Restart
                                </button>
                                <button
                                    class="sprocket-btn delete-btn"
                                    on:click={deleteSprocket}
                                    disabled={isLoadingSprocket ||
                                        !sprocketStatus?.script_exists}
                                >
                                    🗑️ Delete Script
                                </button>
                            </div>
                        </div>

                        <div class="sprocket-upload-section">
                            <h4>Upload Script</h4>
                            <div class="upload-controls">
                                <input
                                    type="file"
                                    id="sprocket-upload-file"
                                    accept=".sh,.bash"
                                    on:change={handleSprocketFileSelect}
                                    disabled={isLoadingSprocket}
                                />
                                <button
                                    class="sprocket-btn upload-btn"
                                    on:click={uploadSprocketScript}
                                    disabled={isLoadingSprocket ||
                                        !sprocketUploadFile}
                                >
                                    📤 Upload & Update
                                </button>
                            </div>
                        </div>

                        <div class="sprocket-status">
                            <div class="status-item">
                                <span class="status-label">Status:</span>
                                <span
                                    class="status-value"
                                    class:running={sprocketStatus?.is_running}
                                >
                                    {sprocketStatus?.is_running
                                        ? "🟢 Running"
                                        : "🔴 Stopped"}
                                </span>
                            </div>
                            {#if sprocketStatus?.pid}
                                <div class="status-item">
                                    <span class="status-label">PID:</span>
                                    <span class="status-value"
                                        >{sprocketStatus.pid}</span
                                    >
                                </div>
                            {/if}
                            <div class="status-item">
                                <span class="status-label">Script:</span>
                                <span class="status-value"
                                    >{sprocketStatus?.script_exists
                                        ? "✅ Exists"
                                        : "❌ Not found"}</span
                                >
                            </div>
                        </div>

                        <div class="script-editor-container">
                            <textarea
                                class="script-editor"
                                bind:value={sprocketScript}
                                placeholder="#!/bin/bash&#10;# Enter your sprocket script here..."
                                disabled={isLoadingSprocket}
                            ></textarea>
                        </div>

                        <div class="script-actions">
                            <button
                                class="sprocket-btn save-btn"
                                on:click={saveSprocket}
                                disabled={isLoadingSprocket}
                            >
                                💾 Save & Update
                            </button>
                            <button
                                class="sprocket-btn load-btn"
                                on:click={loadSprocket}
                                disabled={isLoadingSprocket}
                            >
                                📥 Load Current
                            </button>
                        </div>

                        {#if sprocketMessage}
                            <div
                                class="sprocket-message"
                                class:error={sprocketMessageType === "error"}
                            >
                                {sprocketMessage}
                            </div>
                        {/if}
                    </div>

                    <div class="sprocket-section">
                        <h3>Script Versions</h3>
                        <div class="versions-list">
                            {#each sprocketVersions as version}
                                <div
                                    class="version-item"
                                    class:current={version.is_current}
                                >
                                    <div class="version-info">
                                        <div class="version-name">
                                            {version.name}
                                        </div>
                                        <div class="version-date">
                                            {new Date(
                                                version.modified,
                                            ).toLocaleString()}
                                            {#if version.is_current}
                                                <span class="current-badge"
                                                    >Current</span
                                                >
                                            {/if}
                                        </div>
                                    </div>
                                    <div class="version-actions">
                                        <button
                                            class="version-btn load-btn"
                                            on:click={() =>
                                                loadVersion(version)}
                                            disabled={isLoadingSprocket}
                                        >
                                            📥 Load
                                        </button>
                                        {#if !version.is_current}
                                            <button
                                                class="version-btn delete-btn"
                                                on:click={() =>
                                                    deleteVersion(version.name)}
                                                disabled={isLoadingSprocket}
                                            >
                                                🗑️ Delete
                                            </button>
                                        {/if}
                                    </div>
                                </div>
                            {/each}
                        </div>

                        <button
                            class="sprocket-btn refresh-btn"
                            on:click={loadVersions}
                            disabled={isLoadingSprocket}
                        >
                            🔄 Refresh Versions
                        </button>
                    </div>
                {:else if isLoggedIn}
                    <div class="permission-denied">
                        <p>
                            ❌ Owner permission required for sprocket
                            management.
                        </p>
                        <p>
                            To enable sprocket functionality, set the <code
                                >ORLY_OWNERS</code
                            > environment variable with your npub when starting the
                            relay.
                        </p>
                        <p>
                            Current user role: <strong
                                >{userRole || "none"}</strong
                            >
                        </p>
                    </div>
                {:else}
                    <div class="login-prompt">
                        <p>Please log in to access sprocket management.</p>
                        <button class="login-btn" on:click={openLoginModal}
                            >Log In</button
                        >
                    </div>
                {/if}
            </div>
        {:else if selectedTab === "recovery"}
            <div class="recovery-tab">
                <div>
                    <h3>Event Recovery</h3>
                    <p>Search and recover old versions of replaceable events</p>
                </div>

                <div class="recovery-controls-card">
                    <div class="recovery-controls">
                        <div class="kind-selector">
                            <label for="recovery-kind">Select Event Kind:</label
                            >
                            <select
                                id="recovery-kind"
                                bind:value={recoverySelectedKind}
                                on:change={selectRecoveryKind}
                            >
                                <option value={null}
                                    >Choose a replaceable kind...</option
                                >
                                {#each replaceableKinds as kind}
                                    <option value={kind.value}
                                        >{kind.label}</option
                                    >
                                {/each}
                            </select>
                        </div>

                        <div class="custom-kind-input">
                            <label for="custom-kind"
                                >Or enter custom kind number:</label
                            >
                            <input
                                id="custom-kind"
                                type="number"
                                bind:value={recoveryCustomKind}
                                on:input={handleCustomKindInput}
                                placeholder="e.g., 10001"
                                min="0"
                            />
                        </div>
                    </div>
                </div>

                {#if (recoverySelectedKind !== null && recoverySelectedKind !== undefined && recoverySelectedKind >= 0) || (recoveryCustomKind !== "" && parseInt(recoveryCustomKind) >= 0)}
                    <div class="recovery-results">
                        {#if isLoadingRecovery}
                            <div class="loading">Loading events...</div>
                        {:else if recoveryEvents.length === 0}
                            <div class="no-events">
                                No events found for this kind
                            </div>
                        {:else}
                            <div class="events-list">
                                {#each recoveryEvents as event}
                                    {@const isCurrent = isCurrentVersion(event)}
                                    <div
                                        class="event-item"
                                        class:old-version={!isCurrent}
                                    >
                                        <div class="event-header">
                                            <div class="event-header-left">
                                                <span class="event-kind">
                                                    {#if isCurrent}
                                                        Current Version{/if}</span
                                                >
                                                <span class="event-timestamp">
                                                    {new Date(
                                                        event.created_at * 1000,
                                                    ).toLocaleString()}
                                                </span>
                                            </div>
                                            <div class="event-header-actions">
                                                {#if !isCurrent}
                                                    <button
                                                        class="repost-button"
                                                        on:click={() =>
                                                            repostEvent(event)}
                                                    >
                                                        🔄 Repost
                                                    </button>
                                                {/if}
                                                <button
                                                    class="copy-json-btn"
                                                    on:click|stopPropagation={(
                                                        e,
                                                    ) =>
                                                        copyEventToClipboard(
                                                            event,
                                                            e,
                                                        )}
                                                >
                                                    📋 Copy JSON
                                                </button>
                                            </div>
                                        </div>

                                        <div class="event-content">
                                            <pre
                                                class="event-json">{JSON.stringify(
                                                    event,
                                                    null,
                                                    2,
                                                )}</pre>
                                        </div>
                                    </div>
                                {/each}
                            </div>

                            {#if recoveryHasMore}
                                <button
                                    class="load-more"
                                    on:click={loadRecoveryEvents}
                                    disabled={isLoadingRecovery}
                                >
                                    Load More Events
                                </button>
                            {/if}
                        {/if}
                    </div>
                {/if}
            </div>
        {:else if searchTabs.some((tab) => tab.id === selectedTab)}
            {#each searchTabs as searchTab}
                {#if searchTab.id === selectedTab}
                    <div class="search-results-view">
                        <div class="search-results-header">
                            <h2>🔍 Search Results: "{searchTab.query}"</h2>
                            <button
                                class="refresh-btn"
                                on:click={() =>
                                    loadSearchResults(
                                        searchTab.id,
                                        searchTab.query,
                                        true,
                                    )}
                                disabled={searchResults.get(searchTab.id)
                                    ?.isLoading}
                            >
                                🔄 Refresh
                            </button>
                        </div>
                        <div
                            class="search-results-content"
                            on:scroll={(e) =>
                                handleSearchScroll(e, searchTab.id)}
                        >
                            {#if searchResults.get(searchTab.id)?.events?.length > 0}
                                {#each searchResults.get(searchTab.id).events as event}
                                    <div
                                        class="search-result-item"
                                        class:expanded={expandedEvents.has(
                                            event.id,
                                        )}
                                    >
                                        <div
                                            class="search-result-row"
                                            on:click={() =>
                                                toggleEventExpansion(event.id)}
                                            on:keydown={(e) =>
                                                e.key === "Enter" &&
                                                toggleEventExpansion(event.id)}
                                            role="button"
                                            tabindex="0"
                                        >
                                            <div class="search-result-avatar">
                                                <div class="avatar-placeholder">
                                                    👤
                                                </div>
                                            </div>
                                            <div class="search-result-info">
                                                <div
                                                    class="search-result-author"
                                                >
                                                    {truncatePubkey(
                                                        event.pubkey,
                                                    )}
                                                </div>
                                                <div class="search-result-kind">
                                                    <span class="kind-number"
                                                        >{event.kind}</span
                                                    >
                                                    <span class="kind-name"
                                                        >{getKindName(
                                                            event.kind,
                                                        )}</span
                                                    >
                                                </div>
                                            </div>
                                            <div class="search-result-content">
                                                <div class="event-timestamp">
                                                    {formatTimestamp(
                                                        event.created_at,
                                                    )}
                                                </div>
                                                <div
                                                    class="event-content-single-line"
                                                >
                                                    {truncateContent(
                                                        event.content,
                                                    )}
                                                </div>
                                            </div>
                                            {#if event.kind !== 5 && (userRole === "admin" || userRole === "owner" || (userRole === "write" && event.pubkey && event.pubkey === userPubkey))}
                                                <button
                                                    class="delete-btn"
                                                    on:click|stopPropagation={() =>
                                                        deleteEvent(event.id)}
                                                >
                                                    🗑️
                                                </button>
                                            {/if}
                                        </div>
                                        {#if expandedEvents.has(event.id)}
                                            <div class="search-result-details">
                                                <div class="json-container">
                                                    <pre
                                                        class="event-json">{JSON.stringify(
                                                            event,
                                                            null,
                                                            2,
                                                        )}</pre>
                                                    <button
                                                        class="copy-json-btn"
                                                        on:click|stopPropagation={(
                                                            e,
                                                        ) =>
                                                            copyEventToClipboard(
                                                                event,
                                                                e,
                                                            )}
                                                        title="Copy minified JSON to clipboard"
                                                    >
                                                        📋
                                                    </button>
                                                </div>
                                            </div>
                                        {/if}
                                    </div>
                                {/each}
                            {:else if !searchResults.get(searchTab.id)?.isLoading}
                                <div class="no-search-results">
                                    <p>
                                        No search results found for "{searchTab.query}".
                                    </p>
                                </div>
                            {/if}

                            {#if searchResults.get(searchTab.id)?.isLoading}
                                <div class="loading-search-results">
                                    <div class="loading-spinner"></div>
                                    <p>Searching...</p>
                                </div>
                            {/if}

                            {#if !searchResults.get(searchTab.id)?.hasMore && searchResults.get(searchTab.id)?.events?.length > 0}
                                <div class="end-of-search-results">
                                    <p>No more search results to load.</p>
                                </div>
                            {/if}
                        </div>
                    </div>
                {/if}
            {/each}
        {:else}
            <div class="welcome-message">
                {#if isLoggedIn}
                    <p>
                        Welcome {userProfile?.name ||
                            userPubkey.slice(0, 8) + "..."}
                    </p>
                {:else}
                    <p>Log in to access your user dashboard</p>
                {/if}
            </div>
        {/if}
    </main>
</div>

<!-- Settings Drawer -->
{#if showSettingsDrawer}
    <div
        class="drawer-overlay"
        on:click={closeSettingsDrawer}
        on:keydown={(e) => e.key === "Escape" && closeSettingsDrawer()}
        role="button"
        tabindex="0"
    >
        <div
            class="settings-drawer"
            class:dark-theme={isDarkTheme}
            on:click|stopPropagation
            on:keydown|stopPropagation
        >
            <div class="drawer-header">
                <h2>Settings</h2>
                <button class="close-btn" on:click={closeSettingsDrawer}
                    >✕</button
                >
            </div>
            <div class="drawer-content">
                {#if userProfile}
                    <div class="profile-section">
                        <div class="profile-hero">
                            {#if userProfile.banner}
                                <img
                                    src={userProfile.banner}
                                    alt="Profile banner"
                                    class="profile-banner"
                                />
                            {/if}
                            <!-- Logout button floating in top-right corner of banner -->
                            <button
                                class="logout-btn floating"
                                on:click={handleLogout}>Log out</button
                            >
                            <!-- Avatar overlaps the bottom edge of the banner by 50% -->
                            {#if userProfile.picture}
                                <img
                                    src={userProfile.picture}
                                    alt="User avatar"
                                    class="profile-avatar overlap"
                                />
                            {:else}
                                <div class="profile-avatar-placeholder overlap">
                                    👤
                                </div>
                            {/if}
                            <!-- Username and nip05 to the right of the avatar, above the bottom edge -->
                            <div class="name-row">
                                <h3 class="profile-username">
                                    {userProfile.name || "Unknown User"}
                                </h3>
                                {#if userProfile.nip05}
                                    <span class="profile-nip05-inline"
                                        >{userProfile.nip05}</span
                                    >
                                {/if}
                            </div>
                        </div>

                        <!-- About text in a box underneath, with avatar overlapping its top edge -->
                        {#if userProfile.about}
                            <div class="about-card">
                                <p class="profile-about">{@html aboutHtml}</p>
                            </div>
                        {/if}
                    </div>

                    <!-- View as section -->
                    {#if userRole && userRole !== "read"}
                        <div class="view-as-section">
                            <h3>View as Role</h3>
                            <p>
                                See the interface as it appears for different
                                permission levels:
                            </p>
                            <div class="radio-group">
                                {#each getAvailableRoles() as role}
                                    <label class="radio-label">
                                        <input
                                            type="radio"
                                            name="viewAsRole"
                                            value={role}
                                            checked={currentEffectiveRole ===
                                                role}
                                            on:change={() =>
                                                setViewAsRole(
                                                    role === userRole
                                                        ? ""
                                                        : role,
                                                )}
                                        />
                                        {role.charAt(0).toUpperCase() +
                                            role.slice(1)}{role === userRole
                                            ? " (Default)"
                                            : ""}
                                    </label>
                                {/each}
                            </div>
                        </div>
                    {/if}
                {:else if isLoggedIn && userPubkey}
                    <div class="profile-loading-section">
                        <h3>Profile Loading</h3>
                        <p>Your profile metadata is being loaded...</p>
                        <button
                            class="retry-profile-btn"
                            on:click={fetchProfileIfMissing}
                        >
                            Retry Loading Profile
                        </button>
                        <div class="user-pubkey-display">
                            <strong>Public Key:</strong>
                            {userPubkey.slice(0, 16)}...{userPubkey.slice(-8)}
                        </div>
                    </div>
                {/if}
                <!-- Additional settings can be added here -->
            </div>
        </div>
    </div>
{/if}

<!-- Login Modal -->
<LoginModal
    bind:showModal={showLoginModal}
    {isDarkTheme}
    on:login={handleLogin}
    on:close={closeLoginModal}
/>

<style>
    :global(body) {
        margin: 0;
        padding: 0;
        --bg-color: #ddd;
        --header-bg: #eee;
        --border-color: #dee2e6;
        --text-color: #444444;
        --input-border: #ccc;
        --button-bg: #ddd;
        --button-hover-bg: #eee;
        --primary: #00bcd4;
        --warning: #ff3e00;
        --tab-inactive-bg: #bbb;
    }

    :global(body.dark-theme) {
        --bg-color: #263238;
        --header-bg: #1e272c;
        --border-color: #404040;
        --text-color: #ffffff;
        --input-border: #555;
        --button-bg: #263238;
        --button-hover-bg: #1e272c;
        --primary: #00bcd4;
        --warning: #ff3e00;
        --tab-inactive-bg: #1a1a1a;
    }

    /* Header Styles */
    .main-header {
        height: 3em;
        background-color: var(--header-bg);
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 1000;
        color: var(--text-color);
    }

    .header-content {
        height: 100%;
        display: flex;
        align-items: center;
        padding: 0;
        gap: 0;
    }

    .logo {
        height: 2.5em;
        width: 2.5em;
        object-fit: contain;
        flex-shrink: 0;
        transition: opacity 0.2s ease;
    }

    .logo:hover {
        content: url("/favicon.png");
    }

    .header-title {
        flex: 1;
        height: 100%;
        display: flex;
        align-items: center;
        gap: 0;
        padding: 0 1rem;
    }

    .app-title {
        font-size: 1em;
        font-weight: 600;
        color: var(--text-color);
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .permission-badge {
        font-size: 0.7em;
        font-weight: 500;
        padding: 0.2em 0.5em;
        border-radius: 0.3em;
        background-color: var(--primary);
        color: white;
        text-transform: uppercase;
        letter-spacing: 0.05em;
    }

    .search-input-container {
        flex: 1;
        height: 100%;
        display: flex;
        align-items: center;
        padding: 0 1rem;
    }

    .search-input {
        width: 100%;
        height: 2em;
        padding: 0.5rem;
        border: 1px solid var(--input-border);
        border-radius: 4px;
        background: var(--bg-color);
        color: var(--text-color);
        font-size: 1em;
        outline: none;
    }

    .search-input:focus {
        border-color: var(--primary);
    }

    .search-btn {
        border: 0 none;
        border-radius: 0;
        display: flex;
        align-items: center;
        background-color: var(--button-hover-bg);
        cursor: pointer;
        color: var(--text-color);
        height: 3em;
        width: auto;
        min-width: 3em;
        flex-shrink: 0;
        line-height: 1;
        transition: background-color 0.2s;
        justify-content: center;
        padding: 1em 1em 1em 1em;
        margin: 0;
    }

    .search-btn:hover {
        background-color: var(--button-bg);
    }

    .theme-toggle-btn {
        border: 0 none;
        border-radius: 0;
        display: flex;
        align-items: center;
        background-color: var(--button-hover-bg);
        cursor: pointer;
        color: var(--text-color);
        height: 3em;
        width: auto;
        min-width: 3em;
        flex-shrink: 0;
        line-height: 1;
        transition: background-color 0.2s;
        justify-content: center;
        padding: 1em 1em 1em 1em;
        margin: 0;
    }

    .theme-toggle-btn:hover {
        background-color: var(--button-bg);
    }

    .login-btn {
        padding: 0.5em 1em;
        border: none;
        border-radius: 6px;
        background-color: #4caf50;
        color: white;
        cursor: pointer;
        font-size: 1rem;
        font-weight: 500;
        transition: background-color 0.2s;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        vertical-align: middle;
        margin: 0 auto;
        padding: 0.5em 1em;
    }

    .login-btn:hover {
        background-color: #45a049;
    }

    .acl-mode-warning {
        padding: 1em;
        background-color: #fff3cd;
        border: 1px solid #ffeaa7;
        border-radius: 8px;
        color: #856404;
        margin: 20px 0;
    }

    .acl-mode-warning h3 {
        margin: 0 0 15px 0;
        color: #856404;
    }

    .acl-mode-warning p {
        margin: 10px 0;
        line-height: 1.5;
    }

    .acl-mode-warning code {
        background-color: #f8f9fa;
        padding: 2px 6px;
        border-radius: 4px;
        font-family: monospace;
        color: #495057;
    }

    /* App Container */
    .app-container {
        display: flex;
        margin-top: 3em;
        height: calc(100vh - 3em);
    }

    /* Sidebar Styles */
    .sidebar {
        position: fixed;
        left: 0;
        top: 3em;
        bottom: 0;
        width: 200px;
        background-color: var(--header-bg);
        color: var(--text-color);
        z-index: 100;
    }

    .sidebar-content {
        height: 100%;
        display: flex;
        flex-direction: column;
        padding: 0;
    }

    .tabs {
        display: flex;
        flex-direction: column;
    }

    .tab {
        height: 3em;
        display: flex;
        align-items: center;
        padding: 0 1rem;
        cursor: pointer;
        border: none;
        background: transparent;
        color: var(--text-color);
        transition: background-color 0.2s ease;
        gap: 0.75rem;
        text-align: left;
        width: 100%;
    }

    .tab:hover {
        background-color: var(--bg-color);
    }

    .tab.active {
        background-color: var(--bg-color);
    }

    .tab-icon {
        font-size: 1.2em;
        flex-shrink: 0;
        width: 1.5em;
        text-align: center;
    }

    .tab-label {
        font-size: 0.9em;
        font-weight: 500;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        flex: 1;
    }

    .tab-close-icon {
        cursor: pointer;
        transition: opacity 0.2s;
        font-size: 0.8em;
        margin-left: auto;
        padding: 0.25rem;
        border-radius: 0.25rem;
        flex-shrink: 0;
    }

    .tab-close-icon:hover {
        opacity: 0.7;
        background-color: var(--warning);
        color: white;
    }

    /* Main Content */
    .main-content {
        position: fixed;
        left: 200px;
        top: 3em;
        right: 0;
        bottom: 0;
        padding: 1em;
        overflow-y: auto;
        background-color: var(--bg-color);
        color: var(--text-color);
        display: flex;
        align-items: flex-start;
        justify-content: flex-start;
        flex-direction: column;
        display: flex;
    }

    .welcome-message {
        text-align: center;
    }

    .welcome-message p {
        font-size: 1.2rem;
    }

    /* Sprocket Styles */
    .sprocket-view {
        width: 100%;
        max-width: 1200px;
        margin: 0;
        padding: 20px;
        background: var(--header-bg);
        color: var(--text-color);
        border-radius: 8px;
    }

    .sprocket-section {
        background-color: var(--card-bg);
        border-radius: 8px;
        padding: 1em;
        margin-bottom: 1.5rem;
        border: 1px solid var(--border-color);
        width: 32em;
    }

    .sprocket-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 1rem;
    }

    .sprocket-controls {
        display: flex;
        gap: 0.5rem;
    }

    .sprocket-upload-section {
        margin-bottom: 1rem;
        padding: 1rem;
        background-color: var(--bg-color);
        border-radius: 6px;
        border: 1px solid var(--border-color);
    }

    .sprocket-upload-section h4 {
        margin: 0 0 0.75rem 0;
        color: var(--text-color);
        font-size: 1rem;
        font-weight: 500;
    }

    .upload-controls {
        display: flex;
        gap: 0.5rem;
        align-items: center;
    }

    .upload-controls input[type="file"] {
        flex: 1;
        padding: 0.5rem;
        border: 1px solid var(--border-color);
        border-radius: 4px;
        background: var(--bg-color);
        color: var(--text-color);
        font-size: 0.9rem;
    }

    .sprocket-btn.upload-btn {
        background-color: #8b5cf6;
        color: white;
    }

    .sprocket-btn.upload-btn:hover:not(:disabled) {
        background-color: #7c3aed;
    }

    .sprocket-status {
        display: flex;
        gap: 1rem;
        margin-bottom: 1rem;
        padding: 0.75rem;
        background-color: var(--bg-color);
        border-radius: 6px;
        border: 1px solid var(--border-color);
    }

    .status-item {
        display: flex;
        flex-direction: column;
        gap: 0.25rem;
    }

    .status-label {
        font-size: 0.8rem;
        color: var(--text-muted);
        font-weight: 500;
    }

    .status-value {
        font-size: 0.9rem;
        font-weight: 600;
    }

    .status-value.running {
        color: #22c55e;
    }

    .script-editor-container {
        margin-bottom: 1rem;
    }

    .script-editor {
        width: 100%;
        height: 300px;
        padding: 1rem;
        border: 1px solid var(--border-color);
        border-radius: 6px;
        background-color: var(--bg-color);
        color: var(--text-color);
        font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
        font-size: 0.9rem;
        line-height: 1.4;
        resize: vertical;
        outline: none;
    }

    .script-editor:focus {
        border-color: var(--primary-color);
        box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
    }

    .script-editor:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .script-actions {
        display: flex;
        gap: 0.5rem;
        margin-bottom: 1rem;
    }

    .sprocket-btn {
        padding: 0.5rem 1rem;
        border: none;
        border-radius: 6px;
        font-size: 0.9rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .sprocket-btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .sprocket-btn.save-btn {
        background-color: #22c55e;
        color: white;
    }

    .sprocket-btn.save-btn:hover:not(:disabled) {
        background-color: #16a34a;
    }

    .sprocket-btn.load-btn {
        background-color: #3b82f6;
        color: white;
    }

    .sprocket-btn.load-btn:hover:not(:disabled) {
        background-color: #2563eb;
    }

    .sprocket-btn.restart-btn {
        background-color: #f59e0b;
        color: white;
    }

    .sprocket-btn.restart-btn:hover:not(:disabled) {
        background-color: #d97706;
    }

    .sprocket-btn.delete-btn {
        background-color: #ef4444;
        color: white;
    }

    .sprocket-btn.delete-btn:hover:not(:disabled) {
        background-color: #dc2626;
    }

    .sprocket-btn.refresh-btn {
        background-color: #6b7280;
        color: white;
    }

    .sprocket-btn.refresh-btn:hover:not(:disabled) {
        background-color: #4b5563;
    }

    .sprocket-message {
        padding: 0.75rem;
        border-radius: 6px;
        font-size: 0.9rem;
        font-weight: 500;
        background-color: #dbeafe;
        color: #1e40af;
        border: 1px solid #93c5fd;
    }

    .sprocket-message.error {
        background-color: #fee2e2;
        color: #dc2626;
        border-color: #fca5a5;
    }

    .versions-list {
        display: flex;
        flex-direction: column;
        gap: 0.75rem;
        margin-bottom: 1rem;
    }

    .version-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 1rem;
        background-color: var(--bg-color);
        border: 1px solid var(--border-color);
        border-radius: 6px;
        transition: all 0.2s ease;
    }

    .version-item.current {
        border-color: var(--primary-color);
        background-color: rgba(59, 130, 246, 0.05);
    }

    .version-item:hover {
        border-color: var(--primary-color);
    }

    .version-info {
        flex: 1;
    }

    .version-name {
        font-weight: 600;
        font-size: 0.9rem;
        margin-bottom: 0.25rem;
    }

    .version-date {
        font-size: 0.8rem;
        color: var(--text-muted);
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .current-badge {
        background-color: var(--primary-color);
        color: white;
        padding: 0.125rem 0.5rem;
        border-radius: 12px;
        font-size: 0.7rem;
        font-weight: 500;
    }

    .version-actions {
        display: flex;
        gap: 0.5rem;
    }

    .version-btn {
        padding: 0.375rem 0.75rem;
        border: none;
        border-radius: 4px;
        font-size: 0.8rem;
        font-weight: 500;
        cursor: pointer;
        transition: all 0.2s ease;
        display: flex;
        align-items: center;
        gap: 0.25rem;
    }

    .version-btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    .version-btn.load-btn {
        background-color: #3b82f6;
        color: white;
    }

    .version-btn.load-btn:hover:not(:disabled) {
        background-color: #2563eb;
    }

    .version-btn.delete-btn {
        background-color: #ef4444;
        color: white;
    }

    .version-btn.delete-btn:hover:not(:disabled) {
        background-color: #dc2626;
    }

    @media (max-width: 640px) {
        .header-content {
            padding: 0;
        }

        .sidebar {
            width: 160px;
        }

        .main-content {
            left: 160px;
            padding: 1rem;
        }
    }

    /* User Info Styles */
    .user-info {
        display: flex;
        align-items: flex-start;
        padding: 0;
        height: 3em;
    }

    .logout-btn {
        padding: 0.5rem 1rem;
        border: none;
        border-radius: 6px;
        background-color: var(--warning);
        color: white;
        cursor: pointer;
        font-size: 1rem;
        font-weight: 500;
        transition: background-color 0.2s;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.5rem;
    }

    .logout-btn:hover {
        background-color: #e53935;
    }

    .logout-btn.floating {
        position: absolute;
        top: 0.5em;
        right: 0.5em;
        z-index: 10;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    }

    /* User Profile Button */
    .user-profile-btn {
        border: 0 none;
        border-radius: 0;
        display: flex;
        align-items: center;
        background-color: var(--button-hover-bg);
        cursor: pointer;
        color: var(--text-color);
        height: 3em;
        width: auto;
        min-width: 3em;
        flex-shrink: 0;
        line-height: 1;
        transition: background-color 0.2s;
        justify-content: center;
        padding: 0;
        margin: 0;
    }

    .user-profile-btn:hover {
        background-color: var(--button-bg);
        padding: 0;
    }

    .user-avatar,
    .user-avatar-placeholder {
        width: 2.5em;
        height: 2.5em;
        object-fit: cover;
        border-radius: 50%;
    }

    .user-avatar-placeholder {
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 0.5em;
        padding: 0.5em;
    }

    .user-name {
        font-size: 1.2em;
        font-weight: 500;
        max-width: 100px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        padding: 0.5em;
    }

    /* Settings Drawer */
    .drawer-overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.5);
        z-index: 1000;
        display: flex;
        justify-content: flex-end;
    }

    .settings-drawer {
        width: 640px;
        height: 100%;
        background: var(--bg-color);
        /*border-left: 1px solid var(--border-color);*/
        overflow-y: auto;
        animation: slideIn 0.3s ease;
    }

    @keyframes slideIn {
        from {
            transform: translateX(100%);
        }
        to {
            transform: translateX(0);
        }
    }

    .drawer-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        background: var(--header-bg);
    }

    .drawer-header h2 {
        margin: 0;
        color: var(--text-color);
        font-size: 1em;
        padding: 1rem;
    }

    .close-btn {
        background: none;
        border: none;
        font-size: 1em;
        cursor: pointer;
        color: var(--text-color);
        padding: 0.5em;
        transition: background-color 0.2s;
        align-items: center;
    }

    .close-btn:hover {
        background: var(--button-hover-bg);
    }

    .profile-section {
        margin-bottom: 2rem;
    }

    .profile-hero {
        position: relative;
    }

    .profile-banner {
        width: 100%;
        height: 160px;
        object-fit: cover;
        border-radius: 0;
        display: block;
    }

    /* Avatar sits half over the bottom edge of the banner */
    .profile-avatar,
    .profile-avatar-placeholder {
        width: 72px;
        height: 72px;
        border-radius: 50%;
        object-fit: cover;
        flex-shrink: 0;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
        border: 2px solid var(--bg-color);
    }

    .overlap {
        position: absolute;
        left: 12px;
        bottom: -36px; /* half out of the banner */
        z-index: 2;
        background: var(--button-hover-bg);
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 1.5rem;
    }

    /* Username and nip05 on the banner, to the right of avatar */
    .name-row {
        position: absolute;
        left: calc(12px + 72px + 12px);
        bottom: 8px;
        right: 12px;
        display: flex;
        align-items: baseline;
        gap: 8px;
        z-index: 1;
    }

    .profile-username {
        margin: 0;
        font-size: 1.1rem;
        color: var(--text-color); /* contrasting over banner */
        text-shadow: 0 3px 6px rgba(255, 255, 255, 1);
    }

    .profile-nip05-inline {
        font-size: 0.85rem;
        color: var(--text-color); /* subtle but contrasting */
        font-family: monospace;
        opacity: 0.95;
        text-shadow: 0 3px 6px rgba(255, 255, 255, 1);
    }

    /* About box below with overlap space for avatar */
    .about-card {
        background: var(--header-bg);
        padding: 12px 12px 12px 96px; /* offset text from overlapping avatar */
        position: relative;
        word-break: auto-phrase;
    }

    .profile-about {
        margin: 0;
        color: var(--text-color);
        font-size: 0.9rem;
        line-height: 1.4;
    }

    .profile-loading-section {
        padding: 1rem;
        text-align: center;
    }

    .profile-loading-section h3 {
        margin: 0 0 1rem 0;
        color: var(--text-color);
        font-size: 1.1rem;
    }

    .profile-loading-section p {
        margin: 0 0 1rem 0;
        color: var(--text-color);
        opacity: 0.8;
    }

    .retry-profile-btn {
        padding: 0.5rem 1rem;
        background: var(--primary);
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9rem;
        margin-bottom: 1rem;
        transition: background-color 0.2s;
    }

    .retry-profile-btn:hover {
        background: #00acc1;
    }

    .user-pubkey-display {
        font-family: monospace;
        font-size: 0.8rem;
        color: var(--text-color);
        opacity: 0.7;
        background: var(--button-bg);
        padding: 0.5rem;
        border-radius: 4px;
        word-break: break-all;
    }

    /* Export/Import Views */
    .import-view {
        padding: 1em;
        max-width: 32em;
        margin: 0;
        background: transparent;
        color: var(--text-color);
        border-radius: 0;
    }

    /* Managed ACL View */
    .managed-acl-view {
        padding: 20px;
        max-width: 1200px;
        margin: 0;
        background: var(--header-bg);
        color: var(--text-color);
        border-radius: 8px;
    }

    .compose-view {
        position: fixed;
        top: 3em;
        left: 200px;
        right: 0;
        bottom: 0;
        display: flex;
        flex-direction: column;
        background: transparent;
    }

    .compose-header {
        display: flex;
        gap: 0.5em;
        padding: 0.5em;
        background: transparent;
        border-bottom: 1px solid var(--border-color);
    }

    .compose-btn {
        padding: 0.5em 1em;
        border: 1px solid var(--border-color);
        border-radius: 0.25rem;
        background: var(--button-bg);
        color: var(--button-text);
        cursor: pointer;
        font-size: 0.9rem;
        transition:
            background-color 0.2s,
            border-color 0.2s;
    }

    .compose-btn:hover {
        background: var(--button-hover-bg);
        border-color: var(--button-hover-border);
    }

    .publish-btn {
        background: var(--accent-color, #007bff);
        color: white;
        border-color: var(--accent-color, #007bff);
    }

    .publish-btn:hover {
        background: var(--accent-hover-color, #0056b3);
        border-color: var(--accent-hover-color, #0056b3);
    }

    .compose-editor {
        flex: 1;
        padding: 0.5em;
    }

    .compose-textarea {
        width: 100%;
        height: 100%;
        border: 1px solid var(--border-color);
        border-radius: 0.25rem;
        background: var(--bg-color);
        color: var(--text-color);
        font-family: "Courier New", monospace;
        font-size: 0.9rem;
        padding: 1rem;
        resize: none;
        outline: none;
    }

    .compose-textarea:focus {
        border-color: var(--accent-color, #007bff);
        box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
    }

    .import-view h3 {
        margin: 0 0 2rem 0;
        color: var(--text-color);
        font-size: 1.5rem;
        font-weight: 600;
    }

    .export-section,
    .import-section {
        background: transparent;
        padding: 1em;
        border-radius: 8px;
        margin-bottom: 1.5rem;
        width: 32em;
    }

    .export-section h3,
    .import-section h3 {
        margin: 0 0 10px 0;
        color: var(--text-color);
    }

    .export-section p,
    .import-section p {
        margin: 0;
        color: var(--text-color);
        opacity: 0.7;
        padding: 0.5em;
    }

    .events-view-buttons {
        display: flex;
        gap: 0.5rem;
        align-items: center;
    }

    .export-btn,
    .import-btn,
    .refresh-btn,
    .reload-btn {
        padding: 0.5rem 1rem;
        background: var(--primary);
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.875rem;
        font-weight: 500;
        transition: background-color 0.2s;
        display: inline-flex;
        align-items: center;
        gap: 0.25rem;
        height: 2em;
        margin: 1em;
    }

    .export-btn:hover,
    .import-btn:hover,
    .refresh-btn:hover,
    .reload-btn:hover {
        background: #00acc1;
    }

    .reload-btn {
        min-width: 2em;
        justify-content: center;
    }

    .spinner {
        width: 1em;
        height: 1em;
        border: 2px solid transparent;
        border-top: 2px solid currentColor;
        border-radius: 50%;
        animation: spin 1s linear infinite;
    }

    @keyframes spin {
        0% {
            transform: rotate(0deg);
        }
        100% {
            transform: rotate(360deg);
        }
    }

    .export-btn:disabled,
    .import-btn:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    #import-file {
        margin: 1rem 0;
        padding: 0.5rem;
        border: 1px solid var(--input-border);
        border-radius: 4px;
        background: var(--bg-color);
        color: var(--text-color);
        font-size: 1rem;
    }

    .login-prompt {
        text-align: center;
        padding: 1em;
        background: var(--header-bg);
        border-radius: 8px;
    }

    .login-prompt p {
        margin: 0 0 1rem 0;
        color: var(--text-color);
        font-size: 1.1rem;
    }

    .permission-denied {
        text-align: center;
        padding: 1em;
        background: var(--header-bg);
        border-radius: 8px;
        border: 2px solid var(--warning);
    }

    .permission-denied p {
        margin: 0;
        color: var(--warning);
        font-size: 1.1rem;
        font-weight: 500;
    }

    /* View as Section */
    .view-as-section {
        color: var(--text-color);
        padding: 1rem;
        border-radius: 8px;
        margin-bottom: 1rem;
    }

    .view-as-section h3 {
        margin-top: 0;
        margin-bottom: 0.5rem;
        font-size: 1rem;
        color: var(--primary);
    }

    .view-as-section p {
        margin: 0.5rem 0;
        font-size: 0.9rem;
        opacity: 0.8;
    }

    .radio-group {
        display: flex;
        flex-direction: column;
        gap: 0.5rem;
    }

    .radio-label {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        cursor: pointer;
        padding: 0.25rem;
        border-radius: 4px;
        transition: background 0.2s;
    }

    .radio-label:hover {
        background: rgba(255, 255, 255, 0.1);
    }

    .radio-label input {
        margin: 0;
    }

    /* Events View Container */
    .events-view-container {
        position: fixed;
        top: 3em;
        left: 200px;
        right: 0;
        bottom: 0;
        background: var(--bg-color);
        color: var(--text-color);
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .events-view-header {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        padding: 0.5rem 1rem;
        background: var(--header-bg);
        border-top: 1px solid var(--border-color);
        display: flex;
        justify-content: space-between;
        align-items: center;
        height: 2.5em;
        z-index: 10;
    }

    .events-view-toggle {
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .toggle-container {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        cursor: pointer;
        font-size: 0.875rem;
        color: var(--text-color);
    }

    .toggle-container input[type="checkbox"] {
        display: none;
    }

    .toggle-slider {
        position: relative;
        width: 2.5em;
        height: 1.25em;
        background: var(--border-color);
        border-radius: 1.25em;
        transition: background-color 0.3s;
    }

    .toggle-slider::before {
        content: "";
        position: absolute;
        top: 0.125em;
        left: 0.125em;
        width: 1em;
        height: 1em;
        background: white;
        border-radius: 50%;
        transition: transform 0.3s;
    }

    .toggle-container input[type="checkbox"]:checked + .toggle-slider {
        background: var(--primary);
    }

    .toggle-container input[type="checkbox"]:checked + .toggle-slider::before {
        transform: translateX(1.25em);
    }

    .toggle-label {
        font-size: 0.875rem;
        font-weight: 500;
        user-select: none;
    }

    .events-view-content {
        flex: 1;
        overflow-y: auto;
        padding: 0;
    }

    .events-view-item {
        border-bottom: 1px solid var(--border-color);
        transition: background-color 0.2s;
    }

    .events-view-item:hover {
        background: var(--button-hover-bg);
    }

    .events-view-item.expanded {
        background: var(--button-hover-bg);
    }

    .events-view-row {
        display: flex;
        align-items: center;
        padding: 0.4rem 1rem;
        cursor: pointer;
        gap: 0.75rem;
        min-height: 2rem;
    }

    .events-view-avatar {
        flex-shrink: 0;
        width: 1.5rem;
        height: 1.5rem;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .avatar-placeholder {
        width: 1.5rem;
        height: 1.5rem;
        border-radius: 50%;
        background: var(--button-bg);
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 0.7rem;
    }

    .events-view-info {
        flex-shrink: 0;
        width: 12rem;
        display: flex;
        flex-direction: column;
        gap: 0.1rem;
    }

    .events-view-author {
        font-family: monospace;
        font-size: 0.8rem;
        color: var(--text-color);
        opacity: 0.8;
    }

    .events-view-kind {
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .kind-number {
        background: var(--primary);
        color: white;
        padding: 0.125rem 0.375rem;
        border-radius: 0.25rem;
        font-size: 0.7rem;
        font-weight: 500;
        font-family: monospace;
    }

    .kind-name {
        font-size: 0.75rem;
        color: var(--text-color);
        opacity: 0.7;
        font-weight: 500;
    }

    .events-view-content {
        flex: 1;
        color: var(--text-color);
        font-size: 0.9rem;
        line-height: 1.3;
        word-break: break-word;
    }

    .event-timestamp {
        font-size: 0.75rem;
        color: var(--text-color);
        opacity: 0.7;
        margin-bottom: 0.25rem;
        font-weight: 500;
    }

    .event-content-single-line {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        line-height: 1.2;
    }

    .delete-btn {
        flex-shrink: 0;
        background: none;
        border: none;
        cursor: pointer;
        padding: 0.2rem;
        border-radius: 0.25rem;
        transition: background-color 0.2s;
        font-size: 1.6rem;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 1.5rem;
        height: 1.5rem;
    }

    .delete-btn:hover {
        background: var(--warning);
        color: white;
    }

    .kind-number.delete-event {
        background: var(--warning);
    }

    .delete-event-info {
        display: flex;
        flex-direction: column;
        gap: 0.25rem;
    }

    .delete-event-label {
        font-weight: 500;
        color: var(--warning);
    }

    .delete-targets {
        display: flex;
        flex-direction: column;
        gap: 0.125rem;
    }

    .delete-target {
        font-size: 0.75rem;
        font-family: monospace;
        color: var(--text-color);
        opacity: 0.7;
    }

    .events-view-details {
        border-top: 1px solid var(--border-color);
        background: var(--header-bg);
        padding: 1rem;
    }

    .json-container {
        position: relative;
    }

    .copy-json-btn {
        background: var(--active-tab-bg);
        color: var(--button-text);
        border: 1px solid var(--border-color);
        border-radius: 0.25rem;
        padding: 0.5rem 1rem;
        font-size: 1rem;
        cursor: pointer;
        transition:
            background-color 0.2s,
            border-color 0.2s;
        width: auto;
        height: auto;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .copy-json-btn:hover {
        background: var(--button-hover-bg);
        border-color: var(--button-hover-border);
    }

    .event-json {
        background: var(--bg-color);
        padding: 1rem;
        margin: 0;
        font-family: "Courier New", monospace;
        font-size: 0.8rem;
        line-height: 1.4;
        color: var(--text-color);
        white-space: pre-wrap;
        word-break: break-word;
        overflow-x: auto;
    }

    .no-events {
        padding: 2rem;
        text-align: center;
        color: var(--text-color);
        opacity: 0.7;
    }

    .no-events p {
        margin: 0;
        font-size: 1rem;
    }

    .loading-events {
        padding: 2rem;
        text-align: center;
        color: var(--text-color);
        opacity: 0.7;
    }

    .loading-spinner {
        width: 2rem;
        height: 2rem;
        border: 3px solid var(--border-color);
        border-top: 3px solid var(--primary);
        border-radius: 50%;
        animation: spin 1s linear infinite;
        margin: 0 auto 1rem auto;
    }

    @keyframes spin {
        0% {
            transform: rotate(0deg);
        }
        100% {
            transform: rotate(360deg);
        }
    }

    .loading-events p {
        margin: 0;
        font-size: 0.9rem;
    }

    .end-of-events {
        padding: 1rem;
        text-align: center;
        color: var(--text-color);
        opacity: 0.5;
        font-size: 0.8rem;
        border-top: 1px solid var(--border-color);
    }

    .end-of-events p {
        margin: 0;
    }

    /* Search Results Styles */
    .search-results-view {
        position: fixed;
        top: 3em;
        left: 200px;
        right: 0;
        bottom: 0;
        background: var(--bg-color);
        color: var(--text-color);
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .search-results-header {
        padding: 0.5rem 1rem;
        background: var(--header-bg);
        border-bottom: 1px solid var(--border-color);
        flex-shrink: 0;
        display: flex;
        justify-content: space-between;
        align-items: center;
        height: 2.5em;
    }

    .search-results-header h2 {
        margin: 0;
        font-size: 1rem;
        font-weight: 600;
        color: var(--text-color);
    }

    .search-results-content {
        flex: 1;
        overflow-y: auto;
        padding: 0;
    }

    .search-result-item {
        border-bottom: 1px solid var(--border-color);
        transition: background-color 0.2s;
    }

    .search-result-item:hover {
        background: var(--button-hover-bg);
    }

    .search-result-item.expanded {
        background: var(--button-hover-bg);
    }

    .search-result-row {
        display: flex;
        align-items: center;
        padding: 0.75rem 1rem;
        cursor: pointer;
        gap: 0.75rem;
        min-height: 3rem;
    }

    .search-result-avatar {
        flex-shrink: 0;
        width: 2rem;
        height: 2rem;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .search-result-info {
        flex-shrink: 0;
        width: 12rem;
        display: flex;
        flex-direction: column;
        gap: 0.25rem;
    }

    .search-result-author {
        font-family: monospace;
        font-size: 0.8rem;
        color: var(--text-color);
        opacity: 0.8;
    }

    .search-result-kind {
        display: flex;
        align-items: center;
        gap: 0.5rem;
    }

    .search-result-content {
        flex: 1;
        color: var(--text-color);
        font-size: 0.9rem;
        line-height: 1.3;
        word-break: break-word;
    }

    .search-result-content .event-timestamp {
        font-size: 0.75rem;
        color: var(--text-color);
        opacity: 0.7;
        margin-bottom: 0.25rem;
        font-weight: 500;
    }

    .search-result-content .event-content-single-line {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        line-height: 1.2;
    }

    .search-result-details {
        border-top: 1px solid var(--border-color);
        background: var(--header-bg);
        padding: 1rem;
    }

    .no-search-results {
        padding: 2rem;
        text-align: center;
        color: var(--text-color);
        opacity: 0.7;
    }

    .no-search-results p {
        margin: 0;
        font-size: 1rem;
    }

    .loading-search-results {
        padding: 2rem;
        text-align: center;
        color: var(--text-color);
        opacity: 0.7;
    }

    .loading-search-results p {
        margin: 0;
        font-size: 0.9rem;
    }

    .end-of-search-results {
        padding: 1rem;
        text-align: center;
        color: var(--text-color);
        opacity: 0.5;
        font-size: 0.8rem;
        border-top: 1px solid var(--border-color);
    }

    .end-of-search-results p {
        margin: 0;
    }

    @media (max-width: 1280px) {
        .sidebar {
            width: 60px;
        }

        .main-content {
            left: 60px;
        }

        .events-view-container {
            left: 60px;
        }

        .compose-view {
            left: 60px;
        }

        .search-results-view {
            left: 60px;
        }

        .tab-label {
            display: none;
        }

        .tab {
            justify-content: center;
            padding: 0 0.5rem;
        }

        .tab-icon {
            width: 2em;
            height: 2em;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 0.25rem;
            background-color: var(--bg-color);
        }

        .tab.active .tab-icon {
            background-color: var(--primary);
            color: white;
        }
    }

    @media (max-width: 640px) {
        .settings-drawer {
            width: 100%;
        }

        .name-row {
            left: calc(8px + 56px + 8px);
            bottom: 6px;
            right: 8px;
            gap: 6px;
        }

        .profile-username {
            font-size: 1rem;
            color: var(--text-color);
        }
        .profile-nip05-inline {
            font-size: 0.8rem;
            color: var(--text-color);
        }

        .import-view {
            padding: 1em;
        }

        .managed-acl-view {
            padding: 1rem;
        }

        .export-section,
        .import-section {
            padding: 1em;
            width: 32em;
        }

        .events-view-container {
            left: 160px;
        }

        .compose-view {
            left: 160px;
        }

        .events-view-info {
            width: 8rem;
        }

        .events-view-author {
            font-size: 0.7rem;
        }

        .kind-name {
            font-size: 0.7rem;
        }

        .events-view-content {
            font-size: 0.8rem;
        }

        .search-results-view {
            left: 160px;
        }

        .search-result-info {
            width: 8rem;
        }

        .search-result-author {
            font-size: 0.7rem;
        }

        .search-result-content {
            font-size: 0.8rem;
        }
    }

    /* Recovery Tab Styles */
    .recovery-tab {
        padding: 20px;
        max-width: 1200px;
        margin: 0;
    }

    .recovery-header {
        margin-bottom: 30px;
        text-align: left;
    }

    .recovery-tab h3 {
        margin: 0 0 10px 0;
        color: var(--text-color);
    }

    .recovery-tab p {
        margin: 0;
        color: var(--text-color);
        opacity: 0.7;
        padding: 0.5em;
    }

    .recovery-controls-card {
        background-color: transparent;
        border: none;
        border-radius: 0;
        padding: 0;
    }

    .recovery-controls {
        display: flex;
        gap: 20px;
        align-items: center;
        flex-wrap: wrap;
    }

    .kind-selector {
        display: flex;
        flex-direction: column;
        gap: 5px;
    }

    .kind-selector label {
        font-weight: 500;
        color: var(--text-color);
    }

    .kind-selector select {
        padding: 8px 12px;
        border: 1px solid var(--border-color);
        border-radius: 4px;
        background: var(--bg-color);
        color: var(--text-color);
        min-width: 300px;
    }

    .custom-kind-input {
        display: flex;
        flex-direction: column;
        gap: 5px;
    }

    .custom-kind-input label {
        font-weight: 500;
        color: var(--text-color);
    }

    .custom-kind-input input {
        padding: 8px 12px;
        border: 1px solid var(--border-color);
        border-radius: 4px;
        background: var(--bg-color);
        color: var(--text-color);
        min-width: 200px;
    }

    .custom-kind-input input::placeholder {
        color: var(--text-color);
        opacity: 0.6;
    }

    .recovery-results {
        margin-top: 20px;
    }

    .loading,
    .no-events {
        text-align: left;
        padding: 40px 20px;
        color: var(--text-color);
        opacity: 0.7;
    }

    .events-list {
        display: flex;
        flex-direction: column;
        gap: 15px;
    }

    .event-item {
        background: var(--surface-bg);
        border: 2px solid var(--primary);
        border-radius: 8px;
        padding: 20px;
        transition: all 0.2s ease;
        background: var(--header-bg);
    }

    .event-item.old-version {
        opacity: 0.85;
        border: none;
        background: var(--header-bg);
    }

    .event-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 15px;
        flex-wrap: wrap;
        gap: 10px;
    }

    .event-header-left {
        display: flex;
        align-items: center;
        gap: 15px;
        flex-wrap: wrap;
    }

    .event-header-actions {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .event-kind {
        font-weight: 600;
        color: var(--primary);
    }

    .event-timestamp {
        color: var(--text-color);
        font-size: 0.9em;
        opacity: 0.7;
    }

    .repost-button {
        background: var(--primary);
        color: white;
        border: none;
        padding: 6px 12px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9em;
        transition: background 0.2s ease;
    }

    .repost-button:hover {
        background: #00acc1;
    }

    .event-content {
        margin-bottom: 15px;
    }

    .profile-content pre,
    .generic-content pre {
        background: var(--bg-color);
        padding: 15px;
        border-radius: 4px;
        overflow-x: auto;
        font-size: 0.9em;
        margin: 0;
        border: 1px solid var(--border-color);
        white-space: pre-wrap;
        word-wrap: break-word;
    }

    .follow-list p {
        margin: 0 0 10px 0;
        font-weight: 500;
        color: var(--text-color);
    }

    .follow-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
    }

    .pubkey-tag {
        background: var(--bg-color);
        padding: 4px 8px;
        border-radius: 4px;
        font-family: monospace;
        font-size: 0.8em;
        border: 1px solid var(--border-color);
        color: var(--text-color);
    }

    .event-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
    }

    .tag {
        background: var(--bg-color);
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 0.8em;
        color: var(--text-color);
        opacity: 0.7;
        border: 1px solid var(--border-color);
    }

    .load-more {
        width: 100%;
        padding: 12px;
        background: var(--primary);
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 1em;
        margin-top: 20px;
        transition: background 0.2s ease;
    }

    .load-more:hover:not(:disabled) {
        background: #00acc1;
    }

    .load-more:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }

    /* Dark theme adjustments for recovery tab */
    :global(body.dark-theme) .event-item.old-version {
        background: var(--header-bg);
        border: none;
    }

    .tab-panel {
        display: none;
        padding: 1em;
        background-color: var(--panel-bg);
        border-radius: 0 0 8px 8px;
        color: var(--text-color);
        text-align: left;
    }

    .tab-panel.active {
        display: block;
    }

    .recovery-header {
        font-size: 1.5em;
        font-weight: bold;
        margin-bottom: 0.5em;
        color: var(--header-text-color);
    }

    .recovery-description {
        margin-bottom: 1.5em;
        color: var(--description-text-color);
    }

    .recovery-controls-card {
        background-color: transparent;
        border-radius: 8px;
        padding: 1em;
        margin-bottom: 1em;
        width: 100%;
    }

    .form-group {
        margin-bottom: 1em;
    }

    .form-group label {
        display: block;
        margin-bottom: 0.5em;
        font-weight: bold;
    }

    .form-group input,
    .form-group select {
        width: 100%;
        padding: 0.5em;
        border: 1px solid var(--border-color);
        border-radius: 4px;
        background-color: var(--input-bg);
        color: var(--input-text-color);
    }

    .form-group button {
        background-color: var(--primary);
        color: white;
        border: none;
        padding: 0.75em 1.5em;
        border-radius: 4px;
        cursor: pointer;
        font-weight: bold;
    }

    .form-group button:disabled {
        background-color: #6c757d;
        cursor: not-allowed;
    }

    .status-message {
        padding: 1em;
        border-radius: 4px;
        margin-top: 1em;
    }

    .status-message.success {
        background-color: var(--success-bg);
        color: var(--success-text);
    }

    .status-message.error {
        background-color: var(--error-bg);
        color: var(--error-text);
    }

    .export-results {
        margin-top: 2em;
    }

    .export-results h3 {
        margin-bottom: 1em;
    }

    .export-results button {
        background-color: var(--primary);
        color: white;
        border: none;
        padding: 0.75em 1.5em;
        border-radius: 4px;
        cursor: pointer;
        font-weight: bold;
        margin-bottom: 1em;
    }

    .events-preview {
        max-height: 400px;
        overflow-y: auto;
    }
</style>
