Grok Rate Limit Display

Displays remaining queries on grok.com (UPDATED for 4.3 & Imagine)

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Grok Rate Limit Display
// @namespace    http://tampermonkey.net/
// @version      5.6.5
// @description  Displays remaining queries on grok.com (UPDATED for 4.3 & Imagine)
// @author       Blankspeaker, Originally ported from CursedAtom's chrome extension
// @match        https://grok.com/*
// @icon         https://img.icons8.com/color/1200/grok--v2.jpg
// @license      MIT
// @run-at       document-end
// ==/UserScript==

// Grok Rate Limit Display Chrome Extension
// Default model updated to Grok 4
// Version 1.6.5

(function () {
    'use strict';

    // Inlined Interceptor function for Userscript MAIN world injection
    function runInterceptor() {
        // Grok 4.20 Request Interceptor
        // Injected into the MAIN world to see request bodies
        
        (function () {
            function notifyUsage(model, url) {
                window.dispatchEvent(new CustomEvent('GROK_USAGE_DETECTED_EVENT', {
                    detail: {
                        model: model,
                        url: url,
                        timestamp: Date.now()
                    }
                }));
            }
        
            function notifyRateLimited(model) {
                window.dispatchEvent(new CustomEvent('GROK_RATE_LIMITED_EVENT', {
                    detail: {
                        model: model,
                        timestamp: Date.now()
                    }
                }));
            }
        
            function getModelFromBody(body) {
                if (!body) return null;
                
                // Normalize checking against different possible fields
                const modeId = body.modeId || body.modelName || body.metadata?.request_metadata?.mode || 
                               body.metadata?.requestMetadata?.mode || body.metadata?.modelName || body.metadata?.model_name;
                const modelMode = body.modelMode;
        
                if (modeId === 'grok-420' || modeId === 'grok-420-computer-use-sa' || modelMode === 'MODEL_MODE_GROK_420') {
                    return modeId;
                }
                if (modeId === 'heavy' || modelMode === 'MODEL_MODE_HEAVY' || modeId === 'grok-4-heavy') {
                    return 'grok-4-heavy';
                }
                if (modeId === 'fast' || modelMode === 'MODEL_MODE_FAST' || modeId === 'grok-3') {
                    return 'grok-3';
                }
                if (modeId === 'expert' || modelMode === 'MODEL_MODE_EXPERT' || modeId === 'grok-4') {
                    return 'grok-4';
                }
                if (modeId === 'auto' || modelMode === 'MODEL_MODE_AUTO' || modeId === 'grok-4-auto') {
                    return 'auto';
                }
                return null;
            }
        
            function wrapFetch() {
                if (window.fetch._isWrapped) return;
        
                const originalFetch = window.fetch;
                window.fetch = async function (...args) {
                    const urlOrRequest = args[0];
                    const options = args[1] || {};
        
                    const url = (urlOrRequest instanceof Request) ? urlOrRequest.url : String(urlOrRequest);
        
                    // Execute original fetch
                    const result = originalFetch.apply(this, args);
        
                    try {
                        if (url.includes('/rest/app-chat/conversations/new') || (url.includes('/responses') && url.includes('/rest/app-chat/conversations/'))) {
                            let body = null;
                            if (options.body) {
                                try {
                                    if (typeof options.body === 'string') {
                                        body = JSON.parse(options.body);
                                    } else if (typeof options.body === 'object' && !(options.body instanceof ReadableStream)) {
                                        body = options.body;
                                    }
                                } catch (e) { }
                            }
        
                            if (!body && urlOrRequest instanceof Request) {
                                try {
                                    body = await urlOrRequest.clone().json();
                                } catch (e) { }
                            }
        
                            const model = getModelFromBody(body);
                            if (model) {
                                if (model === 'auto') {
                                    result.then(async (response) => {
                                        if (response.ok) {
                                            try {
                                                const clone = response.clone();
                                                const reader = clone.body.getReader();
                                                const decoder = new TextDecoder();
                                                let buffer = '';
                                                while (true) {
                                                    const { done, value } = await reader.read();
                                                    if (done) break;
                                                    buffer += decoder.decode(value, { stream: true });
                                                    if (buffer.includes('"effort":"high"') || buffer.includes('"effort": "high"') || 
                                                        buffer.includes('"effortLevel":"high"') || buffer.includes('"is_high_effort":true')) {
                                                        notifyUsage('grok-4', url);
                                                        break;
                                                    } else if (buffer.includes('"effort":"low"') || buffer.includes('"effort": "low"') || 
                                                               buffer.includes('"effortLevel":"low"') || buffer.includes('"is_high_effort":false')) {
                                                        notifyUsage('grok-3', url);
                                                        break;
                                                    }
                                                    if (buffer.length > 50000) buffer = buffer.slice(-10000);
                                                }
                                            } catch(e) {}
                                        }
                                    }).catch(() => {});
                                } else {
                                    notifyUsage(model, url);
                                }
        
                                // Also monitor the response for "Too many requests" errors
                                result.then(async (response) => {
                                    if (response.status === 429 || !response.ok) {
                                        try {
                                            const respClone = response.clone();
                                            const respData = await respClone.json();
                                            if (respData?.error?.message === "Too many requests" || respData?.error?.code === 8) {
                                                notifyRateLimited(model === 'auto' ? 'grok-4' : model);
                                            }
                                        } catch (e) { }
                                    }
                                }).catch(() => { });
                            }
                        }
                    } catch (e) {
                    }
        
                    return result;
                };
                window.fetch._isWrapped = true;
            }
        
            function wrapXHR() {
                if (XMLHttpRequest.prototype.send._isWrapped) return;
        
                const originalOpen = XMLHttpRequest.prototype.open;
                XMLHttpRequest.prototype.open = function (method, url) {
                    this._url = url;
                    return originalOpen.apply(this, arguments);
                };
        
                const originalSend = XMLHttpRequest.prototype.send;
                XMLHttpRequest.prototype.send = function (body) {
                    try {
                        if (this._url && (this._url.includes('/conversations/new') || this._url.includes('/responses'))) {
                            let parsedBody = null;
                            if (typeof body === 'string') {
                                try {
                                    parsedBody = JSON.parse(body);
                                } catch (e) { }
                            }
        
                            const model = getModelFromBody(parsedBody);
                            if (model) {
                                if (model !== 'auto') {
                                    notifyUsage(model, this._url);
                                }
        
                                // For XHR, we listen to load/error events
                                this.addEventListener('load', () => {
                                    try {
                                        if (model === 'auto' && this.status >= 200 && this.status < 300) {
                                            if (this.responseText.includes('"effort":"high"') || this.responseText.includes('"effort": "high"')) {
                                                notifyUsage('grok-4', this._url);
                                            } else if (this.responseText.includes('"effort":"low"') || this.responseText.includes('"effort": "low"')) {
                                                notifyUsage('grok-3', this._url);
                                            }
                                        }
        
                                        if (this.status === 429) {
                                            notifyRateLimited(model === 'auto' ? 'grok-4' : model);
                                        } else {
                                            const respData = JSON.parse(this.responseText);
                                            if (respData?.error?.message === "Too many requests" || respData?.error?.code === 8) {
                                                notifyRateLimited(model === 'auto' ? 'grok-4' : model);
                                            }
                                        }
                                    } catch (e) { }
                                });
                            }
                        }
                    } catch (e) { }
        
                    return originalSend.apply(this, arguments);
                };
                XMLHttpRequest.prototype.send._isWrapped = true;
            }
        
            // Wrap immediately
            wrapFetch();
            wrapXHR();
        
            // Re-wrap on delays to catch late overrides from site scripts
            setTimeout(wrapFetch, 1000);
            setTimeout(wrapFetch, 3000);
            setTimeout(wrapFetch, 10000);
        })();
        
    }

    console.log('Grok Rate Limit Userscript loaded');

    // Inject interceptor into MAIN world (inline for userscript compatibility)
    try {
        const s = document.createElement('script');
        s.textContent = `(${runInterceptor.toString()})();`;
        s.onload = function () { this.remove(); };
        (document.head || document.documentElement).appendChild(s);
    } catch (e) {
        console.error('Grok Rate Limit Userscript: Failed to inject interceptor:', e);
    }

    // Listen for usage detected by interceptor
    window.addEventListener('GROK_USAGE_DETECTED_EVENT', (event) => {
        const { model } = event.detail;
        console.log('Grok Rate Limit Extension: Usage detected for', model);
        try {
            if (chrome.runtime?.id) {
                chrome.runtime.sendMessage({ type: 'GROK_USAGE_DETECTED', model: model });
            }
        } catch (e) {
            console.warn('Grok Rate Limit Extension: Extension context invalidated. Please refresh the page.');
        }
    });

    // Listen for rate limit errors detected by interceptor
    window.addEventListener('GROK_RATE_LIMITED_EVENT', (event) => {
        const { model } = event.detail;
        console.warn('Grok Rate Limit Extension: UI detected rate limit hit for', model);
        try {
            if (chrome.runtime?.id) {
                chrome.runtime.sendMessage({ type: 'RATE_LIMIT_HIT', model: model });
            }
        } catch (e) { }
    });
    // Listen for refresh messages from background script (extension context only)
    if (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {
        chrome.runtime.onMessage.addListener((message) => {
            if (message.type === 'REFRESH_UI') {
                if (lastQueryBar) {
                    // Poll up to 3 times if count hasn't changed yet
                    fetchAndUpdateRateLimit(lastQueryBar, true, 3);
                }
            }
        });
    }

    let lastHigh = { remaining: null, wait: null };
    let lastLow = { remaining: null, wait: null };
    let lastBoth = { high: null, low: null, wait: null };
    let lastImagine = { remaining: null, wait: null };
    let lastMulti = null;
    let grok420Fetches = 0;
    let grok420HasDropped = false;

    const MODEL_MAP = {
        "Grok 4.3 (beta)": "grok-420-computer-use-sa",
        "Grok 4.20 (Beta)": "grok-420",
        "Grok 420": "grok-420",
        "Grok 4": "grok-4",
        "Grok 3": "grok-3",
        "Grok 4 Heavy": "grok-4-heavy",
        "Grok 4 With Effort Decider": "grok-4-auto",
        "Auto": "grok-4-auto",
        "Fast": "grok-3",
        "Expert": "grok-4",
        "Heavy": "grok-4-heavy",
        "Grok 4 Fast": "grok-4-mini-thinking-tahoe",
        "Grok 4.1": "grok-4-1-non-thinking-w-tool",
        "Grok 4.1 Thinking": "grok-4-1-thinking-1129",
        "Grok 2": "grok-2",
        "Grok 2 Mini": "grok-2-mini",
    };

    const DEFAULT_KIND = "DEFAULT";
    const DEFAULT_MODEL = "grok-4-auto";
    const MODEL_SELECTOR = "button[aria-label='Model select']";
    const QUERY_BAR_SELECTOR = ".query-bar";
    const ELEMENT_WAIT_TIMEOUT_MS = 5000;

    const RATE_LIMIT_CONTAINER_ID = "grok-rate-limit";

    const cachedRateLimits = {};

    let countdownTimer = null;
    let isCountingDown = false;
    let lastQueryBar = null;
    let lastModelObserver = null;
    let lastThinkObserver = null;
    let lastSearchObserver = null;
    let lastInputElement = null;
    let lastSubmitButton = null;
    let lastModelName = null;

    // State for overlap checking
    let overlapCheckInterval = null;
    let isHiddenDueToOverlap = false;

    const commonFinderConfigs = {
        thinkButton: {
            selector: "button",
            ariaLabel: "Think",
            svgPartialD: "M19 9C19 12.866",
        },
        deepSearchButton: {
            selector: "button",
            ariaLabelRegex: /Deep(er)?Search/i,
        },
        attachButton: {
            selector: "button",
            classContains: ["group/attach-button"],
        },
        submitButton: {
            selector: "button",
            svgPartialD: "M6 11L12 5M12 5L18 11M12 5V19",
        }
    };

    // Function to check if current page is under /imagine
    function isImaginePage() {
        return window.location.pathname.startsWith('/imagine');
    }

    // Debounce function
    function debounce(func, delay) {
        let timeout;
        return (...args) => {
            clearTimeout(timeout);
            timeout = setTimeout(() => func(...args), delay);
        };
    }

    // Function to find element based on config (OR logic for conditions)
    function findElement(config, root = document) {
        const elements = root.querySelectorAll(config.selector);
        for (const el of elements) {
            let satisfied = 0;

            if (config.ariaLabel) {
                if (el.getAttribute('aria-label') === config.ariaLabel) satisfied++;
            }

            if (config.ariaLabelRegex) {
                const aria = el.getAttribute('aria-label');
                if (aria && config.ariaLabelRegex.test(aria)) satisfied++;
            }

            if (config.svgPartialD) {
                const path = el.querySelector('path');
                if (path && path.getAttribute('d')?.includes(config.svgPartialD)) satisfied++;
            }

            if (config.classContains) {
                if (config.classContains.some(cls => el.classList.contains(cls))) satisfied++;
            }

            if (satisfied > 0) {
                return el;
            }
        }
        return null;
    }

    // Function to format timer for display (H:MM:SS or MM:SS)
    function formatTimer(seconds) {
        const hours = Math.floor(seconds / 3600);
        const minutes = Math.floor((seconds % 3600) / 60);
        const secs = seconds % 60;
        if (hours > 0) {
            return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        } else {
            return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
        }
    }

    // Function to check if text content overlaps with rate limit display
    function checkTextOverlap(queryBar) {
        const rateLimitContainer = document.getElementById(RATE_LIMIT_CONTAINER_ID);
        if (!rateLimitContainer) return;

        // Look for both mobile textarea and desktop contenteditable
        const contentEditable = queryBar.querySelector('div[contenteditable="true"]');
        const textArea = queryBar.querySelector('textarea[aria-label*="Ask Grok"]');
        const inputElement = contentEditable || textArea;

        if (!inputElement) return;

        // Get the text content from either element
        const textContent = inputElement.value || inputElement.textContent || '';
        const textLength = textContent.trim().length;

        // Calculate available space more accurately
        const queryBarWidth = queryBar.offsetWidth;
        const rateLimitWidth = rateLimitContainer.offsetWidth;
        const availableSpace = queryBarWidth - rateLimitWidth - 100; // 100px buffer

        // More aggressive detection for small screens
        const isSmallScreen = window.innerWidth < 900 ||
            availableSpace < 200 ||
            window.screen?.width < 500 ||
            document.documentElement.clientWidth < 500;

        // Very conservative approach: on small screens hide immediately when typing
        // On larger screens, give more room
        const characterLimit = isSmallScreen ? 0 : 28;

        const shouldHide = textLength > characterLimit;

        if (shouldHide && !isHiddenDueToOverlap) {
            // Add slide-right animation to match model picker
            rateLimitContainer.style.transition = 'transform 0.2s ease-out, opacity 0.2s ease-out';
            rateLimitContainer.style.transform = 'translateX(100%)';
            rateLimitContainer.style.opacity = '0';

            // After animation, hide completely
            setTimeout(() => {
                if (isHiddenDueToOverlap) {
                    rateLimitContainer.style.display = 'none';
                }
            }, 200);

            isHiddenDueToOverlap = true;
        } else if (!shouldHide && isHiddenDueToOverlap) {
            // Show and slide back in
            rateLimitContainer.style.display = '';
            rateLimitContainer.style.transition = 'transform 0.2s ease-out, opacity 0.2s ease-out';

            // Force a reflow to ensure display change takes effect
            rateLimitContainer.offsetHeight;

            rateLimitContainer.style.transform = 'translateX(0)';
            rateLimitContainer.style.opacity = '0.8';

            isHiddenDueToOverlap = false;
        }
    }

    // Function to start overlap checking for a query bar
    function startOverlapChecking(queryBar) {
        // Clear any existing interval
        if (overlapCheckInterval) {
            clearInterval(overlapCheckInterval);
        }

        // Check for overlap less frequently to prevent flashing
        overlapCheckInterval = setInterval(() => {
            if (document.body.contains(queryBar)) {
                checkTextOverlap(queryBar);
            } else {
                clearInterval(overlapCheckInterval);
                overlapCheckInterval = null;
            }
        }, 500);
    }

    // Function to stop overlap checking
    function stopOverlapChecking() {
        if (overlapCheckInterval) {
            clearInterval(overlapCheckInterval);
            overlapCheckInterval = null;
        }
        isHiddenDueToOverlap = false;
    }

    // Function to remove any existing rate limit display
    function removeExistingRateLimit() {
        const existing = document.getElementById(RATE_LIMIT_CONTAINER_ID);
        if (existing) {
            existing.remove();
        }
    }

    // Function to determine model key from SVG or text
    function getCurrentModelKey(queryBar) {
        if (isImaginePage()) {
            const isPostPage = window.location.pathname.startsWith('/imagine/post');
            
            // Multilingual matching for "Video" and "Image" radio tabs
            const videoLabels = /Video|视频|影片|動画|비디오|Vidéo|Videoclipe/i;
            const imageLabels = /Image|图片|图像|画像|이미지|Photo|Foto/i;

            const radioButtons = Array.from(queryBar.querySelectorAll('button[role="radio"]'));
            let videoBtn = null;
            let imageBtn = null;

            if (radioButtons.length >= 2) {
                const videoIndex = radioButtons.findIndex(b => {
                    const text = b?.textContent || '';
                    const aria = b?.getAttribute('aria-label') || '';
                    return videoLabels.test(text) || videoLabels.test(aria);
                });
                if (videoIndex !== -1) {
                    videoBtn = radioButtons[videoIndex];
                    imageBtn = radioButtons[videoIndex === 0 ? 1 : 0];
                } else {
                    // Fallback to position: first is Image, second is Video
                    imageBtn = radioButtons[0];
                    videoBtn = radioButtons[1];
                }
            } else {
                videoBtn = radioButtons.find(b => {
                    const text = b?.textContent || '';
                    const aria = b?.getAttribute('aria-label') || '';
                    return videoLabels.test(text) || videoLabels.test(aria);
                }) || queryBar.querySelector('button[role="radio"][aria-label="Video"]');

                imageBtn = radioButtons.find(b => {
                    const text = b?.textContent || '';
                    const aria = b?.getAttribute('aria-label') || '';
                    return imageLabels.test(text) || imageLabels.test(aria);
                }) || queryBar.querySelector('button[role="radio"][aria-label="Image"]');
            }

            // Video specifiers (480p, 720p) are technical constants and are not translated
            const hasVideoControls = Array.from(queryBar.querySelectorAll('button')).some(b => b?.textContent?.includes('480p') || b?.textContent?.includes('720p'));

            // Multilingual matching for "Quality" and "Speed" controls
            const qualityLabels = /Quality|画质|质量|品质|画質|화질|품질|Calidad|Qualité|Qualität|Qualità|Qualidade|Качество/i;
            const speedLabels = /Speed|速度|快速|高速|속도|Velocidad|Rápido|Vitesse|Rapide|Geschwindigkeit|Schnell|Velocità|Velocidade|Быстро/i;

            const hasImageControls = Array.from(queryBar.querySelectorAll('button')).some(b => {
                const text = b?.textContent || '';
                return qualityLabels.test(text) || speedLabels.test(text);
            });

            // If controls are hidden (e.g., during generation), retain the last known mode
            if (!videoBtn && !imageBtn && !hasVideoControls && !hasImageControls && lastModelName && ['image', 'imagePro', 'imageEdit', 'video', 'video720p'].includes(lastModelName)) {
                return lastModelName;
            }

            let isVideoRadio = videoBtn && videoBtn.classList.contains('text-primary');
            let isImageRadio = imageBtn && imageBtn.classList.contains('text-primary');

            let isVideo = isVideoRadio || hasVideoControls;
            let isImage = isImageRadio || (!isVideo);

            // Default to image if none explicitly active
            if (!isVideo && !isImage) {
                isImage = true;
            }

            if (isVideo) {
                const is720p = Array.from(queryBar.querySelectorAll('button')).some(b => b?.textContent?.includes('720p') && b.classList.contains('text-primary'));
                return is720p ? 'video720p' : 'video';
            } else {
                if (isPostPage) {
                    return 'imageEdit';
                } else {
                    const isQuality = Array.from(queryBar.querySelectorAll('button')).some(b => {
                        const text = b?.textContent || '';
                        return qualityLabels.test(text) && b.classList.contains('text-primary');
                    });
                    return isQuality ? 'imagePro' : 'image';
                }
            }
        }

        const modelButton = queryBar.querySelector(MODEL_SELECTOR);
        if (!modelButton) return DEFAULT_MODEL;

        // Check for text span first (updated selector for new UI)
        const textElement = modelButton.querySelector('span.font-semibold');
        if (textElement) {
            const modelText = textElement.textContent.trim();
            return MODEL_MAP[modelText] || DEFAULT_MODEL;
        }

        // Fallback to old chooser text span
        const oldTextElement = modelButton.querySelector('span.inline-block');
        if (oldTextElement) {
            const modelText = oldTextElement.textContent.trim();
            return MODEL_MAP[modelText] || DEFAULT_MODEL;
        }

        // New chooser: check SVG icon
        const svg = modelButton.querySelector('svg');
        if (svg) {
            const pathsD = Array.from(svg.querySelectorAll('path'))
                .map(p => p.getAttribute('d') || '')
                .filter(d => d.length > 0)
                .join(' ');

            const hasBrainFill = svg.querySelector('path[class*="fill-yellow-100"]') !== null;

            if (pathsD.includes('M6.5 12.5L11.5 17.5')) {
                return 'grok-4-auto'; // Auto
            } else if (pathsD.includes('M5 14.25L14 4')) {
                return 'grok-3'; // Fast
            } else if (hasBrainFill || pathsD.includes('M19 9C19 12.866')) {
                return 'grok-4'; // Expert
            } else if (pathsD.includes('M12 3a6 6 0 0 0 9 9')) {
                return 'grok-4-mini-thinking-tahoe'; // Grok 4 Fast
            } else if (pathsD.includes('M11 18H10C7.79086 18 6 16.2091 6 14V13')) {
                return 'grok-4-heavy'; // Heavy
            }
        }

        return DEFAULT_MODEL;
    }

    // Function to determine effort level based on model
    function getEffortLevel(modelName) {
        if (['image', 'imagePro', 'imageEdit', 'video', 'video720p'].includes(modelName)) {
            return 'imagine';
        } else if (modelName === 'grok-4-auto') {
            return 'both';
        } else if (modelName === 'grok-3') {
            return 'low';
        } else if (modelName === 'grok-4-1-non-thinking-w-tool') {
            return 'low';
        } else if (modelName === 'grok-4-1-thinking-1129') {
            return 'high';
        } else if (modelName === 'grok-420' || modelName === 'grok-420-computer-use-sa') {
            return 'high';
        } else {
            // Grok 4, Heavy, and Grok 4.1 Thinking fall here
            return 'high';
        }
    }

    // Function to update or inject the rate limit display
    function updateRateLimitDisplay(queryBar, response, effort, modelName, imagineData) {
        let rateLimitContainer = document.getElementById(RATE_LIMIT_CONTAINER_ID);

        if (!rateLimitContainer) {
            rateLimitContainer = document.createElement('div');
            rateLimitContainer.id = RATE_LIMIT_CONTAINER_ID;
            rateLimitContainer.className = 'inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-60 disabled:cursor-not-allowed [&_svg]:duration-100 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:-mx-0.5 select-none text-fg-primary hover:bg-button-ghost-hover hover:border-border-l2 disabled:hover:bg-transparent h-10 px-3.5 py-2 text-sm rounded-full group/rate-limit transition-colors duration-100 relative overflow-hidden border border-transparent cursor-pointer';
            rateLimitContainer.style.opacity = '0.8';
            rateLimitContainer.style.transition = 'opacity 0.1s ease-in-out';
            rateLimitContainer.style.zIndex = '20';

            let customTooltip = document.getElementById('grok-rate-limit-custom-tooltip');
            if (!customTooltip) {
                customTooltip = document.createElement('div');
                customTooltip.id = 'grok-rate-limit-custom-tooltip';
                customTooltip.className = 'z-50 p-4 rounded-xl border shadow-[0_8px_30px_rgb(0,0,0,0.12)] text-left font-sans cursor-default';
                customTooltip.style.position = 'fixed';
                customTooltip.style.display = 'none';
                customTooltip.style.minWidth = '240px';
                document.body.appendChild(customTooltip);
            }

            const showCustomTooltip = () => {
                if (!rateLimitContainer._tooltipText) return;
                
                const isDark = document.documentElement.classList.contains('dark') || document.body.style.backgroundColor === 'rgb(0, 0, 0)';
                customTooltip.style.backgroundColor = isDark ? '#1a1a1a' : '#ffffff';
                customTooltip.style.borderColor = isDark ? '#333333' : '#e5e5e5';
                customTooltip.style.color = isDark ? '#e5e5e5' : '#1a1a1a';

                customTooltip.innerHTML = rateLimitContainer._tooltipText;
                customTooltip.style.display = 'block';
                const rect = rateLimitContainer.getBoundingClientRect();
                customTooltip.style.left = `${Math.max(10, rect.left + rect.width / 2 - customTooltip.offsetWidth / 2)}px`;
                customTooltip.style.top = `${Math.max(10, rect.top - customTooltip.offsetHeight - 8)}px`;
            };

            const hideCustomTooltip = () => {
                customTooltip.style.display = 'none';
            };

            if (!customTooltip._hasGlobalClick) {
                document.addEventListener('click', (e) => {
                    if (customTooltip.style.display === 'block' && !customTooltip.contains(e.target)) {
                        const rateLimits = document.querySelectorAll('#' + RATE_LIMIT_CONTAINER_ID);
                        let clickedRateLimit = false;
                        rateLimits.forEach(rl => {
                            if (rl.contains(e.target)) clickedRateLimit = true;
                        });
                        if (!clickedRateLimit) {
                            customTooltip.style.display = 'none';
                            rateLimits.forEach(rl => rl._isClicked = false);
                        }
                    }
                });
                customTooltip._hasGlobalClick = true;
            }

            rateLimitContainer.addEventListener('mouseenter', () => { 
                rateLimitContainer._isHovered = true; 
            });
            let pressTimer;
            rateLimitContainer.addEventListener('mousedown', (e) => {
                if (isImaginePage()) return;
                if (e.button !== 0) return;
                pressTimer = setTimeout(() => {
                    rateLimitContainer._isClicked = false;
                    hideCustomTooltip();
                    showResetMenu(rateLimitContainer, modelName, queryBar);
                }, 2000);
            });
            rateLimitContainer.addEventListener('mouseup', () => clearTimeout(pressTimer));
            rateLimitContainer.addEventListener('mouseleave', () => {
                rateLimitContainer._isHovered = false;
                clearTimeout(pressTimer);
            });
            rateLimitContainer.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                rateLimitContainer._isClicked = true;
                showCustomTooltip();
            });
            rateLimitContainer.addEventListener('click', (e) => {
                if (pressTimer) clearTimeout(pressTimer);
                
                if (rateLimitContainer._isClicked) {
                    rateLimitContainer._isClicked = false;
                    hideCustomTooltip();
                } else {
                    rateLimitContainer._isClicked = true;
                    showCustomTooltip();
                }

                if (!document.getElementById('grok-rate-limit-reset-menu')) {
                    fetchAndUpdateRateLimit(queryBar, true);
                }
            });

            const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
            svg.setAttribute('width', '18');
            svg.setAttribute('height', '18');
            svg.setAttribute('viewBox', '0 0 24 24');
            svg.setAttribute('fill', 'none');
            svg.setAttribute('stroke', 'currentColor');
            svg.setAttribute('stroke-width', '2');
            svg.setAttribute('stroke-linecap', 'round');
            svg.setAttribute('stroke-linejoin', 'round');
            svg.setAttribute('class', 'lucide lucide-gauge stroke-[2] text-fg-secondary transition-colors duration-100');
            svg.setAttribute('aria-hidden', 'true');

            const contentDiv = document.createElement('div');
            contentDiv.className = 'flex items-center';

            rateLimitContainer.appendChild(svg);
            rateLimitContainer.appendChild(contentDiv);

            let inserted = false;
            if (isImaginePage()) {
                const actionLabels = /Submit|Edit|Make video|提交|发送|编辑|制作视频|生成视频|動画作成|영상 만들기|影片/i;
                const submitBtn = Array.from(queryBar.querySelectorAll('button')).find(b => {
                    const aria = b.getAttribute('aria-label') || '';
                    const text = b.textContent || '';
                    if (actionLabels.test(aria) || actionLabels.test(text)) return true;
                    // Fallback to identifying by SVG arrow-up path
                    const path = b.querySelector('path');
                    if (path && path.getAttribute('d')?.includes('M6 11L12 5')) return true;
                    return false;
                });
                if (submitBtn && submitBtn.parentNode && submitBtn.parentNode.parentNode) {
                    submitBtn.parentNode.parentNode.insertBefore(rateLimitContainer, submitBtn.parentNode);
                    inserted = true;
                }
            }

            if (!inserted) {
                const modelSelector = queryBar.querySelector('#model-select-trigger')?.closest('.z-20') || 
                                     queryBar.querySelector('button[aria-label="Model select"]')?.closest('.z-20') ||
                                     queryBar.querySelector('button[aria-label="Model select"]');
                const toolsContainer = queryBar.querySelector('div.ms-auto.flex.flex-row.items-end');

                if (modelSelector && modelSelector.parentNode) {
                    modelSelector.parentNode.insertBefore(rateLimitContainer, modelSelector);
                } else if (toolsContainer) {
                    toolsContainer.prepend(rateLimitContainer);
                } else {
                    const bottomBar = queryBar.querySelector('div.absolute.inset-x-0.bottom-0');
                    if (bottomBar) {
                        bottomBar.appendChild(rateLimitContainer);
                    } else {
                        rateLimitContainer.remove();
                        rateLimitContainer = null;
                        return;
                    }
                }
            }
        }

        const contentDiv = rateLimitContainer.lastChild;
        const svg = rateLimitContainer.querySelector('svg');

        contentDiv.innerHTML = '';
        const isBoth = effort === 'both';

        const buildImagineSection = (full) => {
            if (!full || full.error) return '';
            const hours = Math.round((full.image?.windowSizeSeconds || 64800) / 3600);
            return `
                <div class="font-bold mb-3 mt-4 text-base border-b border-border-l2 pb-2">Imagine Gens Remaining</div>
                <div class="flex justify-between items-center gap-6 mb-1.5">
                    <span class="text-fg-secondary">Speed Images:</span>
                    <span class="font-mono text-primary font-bold">${full.image?.remainingQueries ?? 0}</span>
                </div>
                <div class="flex justify-between items-center gap-6 mb-1.5">
                    <span class="text-fg-secondary">Quality Images:</span>
                    <span class="font-mono text-primary font-bold">${full.imagePro?.remainingQueries ?? 0}</span>
                </div>
                <div class="flex justify-between items-center gap-6 mb-1.5">
                    <span class="text-fg-secondary">Image Edits:</span>
                    <span class="font-mono text-primary font-bold">${full.imageEdit?.remainingQueries ?? 0}</span>
                </div>
                <div class="flex justify-between items-center gap-6 mb-1.5">
                    <span class="text-fg-secondary">480p Videos:</span>
                    <span class="font-mono text-primary font-bold">${full.video?.remainingQueries ?? 0}</span>
                </div>
                <div class="flex justify-between items-center gap-6 mb-3">
                    <span class="text-fg-secondary">720p Videos:</span>
                    <span class="font-mono text-primary font-bold">${full.video720p?.remainingQueries ?? 0}</span>
                </div>
                <div class="mt-2 pt-2 border-t border-border-l2 text-xs text-fg-secondary text-center">
                    Resets after ${hours} hrs
                </div>
            `;
        };

        if (response.error) {
            if (isBoth) {
                if (lastBoth.high !== null) {
                    appendNumberSpan(contentDiv, lastBoth.high, '');
                    rateLimitContainer._tooltipText = `High: ${lastBoth.high} | Low: ${lastBoth.low ?? 'Unknown'} queries remaining`;
                    setGaugeSVG(svg);
                } else {
                    appendNumberSpan(contentDiv, 'Unavailable', '');
                    rateLimitContainer._tooltipText = 'Unavailable';
                    setGaugeSVG(svg);
                }
            } else {
                const lastForEffort = effort === 'imagine' ? lastImagine : ((effort === 'high') ? lastHigh : lastLow);
                if (lastForEffort.remaining !== null) {
                    appendNumberSpan(contentDiv, lastForEffort.remaining, '');
                    rateLimitContainer._tooltipText = `${lastForEffort.remaining} queries remaining`;
                    setGaugeSVG(svg);
                } else {
                    appendNumberSpan(contentDiv, 'Unavailable', '');
                    rateLimitContainer._tooltipText = 'Unavailable';
                    setGaugeSVG(svg);
                }
            }
        } else {
            if (countdownTimer) {
                clearInterval(countdownTimer);
                countdownTimer = null;
            }

            if (isBoth) {
                lastBoth.high = response.highRemaining;
                lastBoth.low = response.lowRemaining;
                lastBoth.wait = response.waitTimeSeconds;

                const high = lastBoth.high;
                const low = lastBoth.low;
                const waitTimeSeconds = lastBoth.wait;

                let currentCountdown = waitTimeSeconds;
                let timerSpan = null;

                if (high > 0) {
                    appendNumberSpan(contentDiv, high, '');
                    setGaugeSVG(svg);
                } else if (waitTimeSeconds > 0) {
                    timerSpan = appendNumberSpan(contentDiv, formatTimer(currentCountdown), '#ff6347');
                    setClockSVG(svg);
                } else {
                    appendNumberSpan(contentDiv, '0', '#ff6347');
                    setGaugeSVG(svg);
                }

                const updateTooltip = () => {
                    const hasHeavy = document.body.innerText.includes('Heavy') || (lastMulti?.heavy && lastMulti.heavy.remaining !== 'Unknown');
                    let titleHtml = "";
                    let chatSection = `
                        <div class="font-bold mb-3 text-base border-b border-border-l2 pb-2">Chat Queries Remaining</div>
                        <div class="flex justify-between items-center gap-6 mb-1.5">
                            <span class="text-fg-secondary">Auto:</span>
                            <span class="font-mono text-primary font-bold">${lastMulti?.auto?.remaining ?? 'Unknown'}</span>
                        </div>
                        <div class="flex justify-between items-center gap-6 mb-1.5">
                            <span class="text-fg-secondary">Grok 4.3 (Beta):</span>
                            <span class="font-mono text-primary font-bold">${lastMulti?.grok43?.remaining ?? 'Unknown'}</span>
                        </div>
                        ${hasHeavy ? `
                        <div class="flex justify-between items-center gap-6 mb-1.5">
                            <span class="text-fg-secondary">Heavy:</span>
                            <span class="font-mono text-primary font-bold">${lastMulti?.heavy?.remaining ?? 'Unknown'}</span>
                        </div>
                        ` : ''}
                        <div class="flex justify-between items-center gap-6 mb-1.5">
                            <span class="text-fg-secondary">Expert:</span>
                            <span class="font-mono text-primary font-bold">${lastMulti?.expert?.remaining ?? 'Unknown'}</span>
                        </div>
                        <div class="flex justify-between items-center gap-6 mb-3">
                            <span class="text-fg-secondary">Fast:</span>
                            <span class="font-mono text-primary font-bold">${lastMulti?.fast?.remaining ?? 'Unknown'}</span>
                        </div>
                        <div class="mt-2 pt-2 border-t border-border-l2 text-xs text-fg-secondary text-center">
                            Resets after ${lastMulti?.fast?.window || 2} hrs
                        </div>
                    `;
                    
                    if (currentCountdown > 0) {
                        chatSection += `
                            <div class="mt-2 pt-2 border-t border-border-l2 text-xs text-[#ff6347] text-center font-bold">
                                Reset in ${formatTimer(currentCountdown)}
                            </div>
                        `;
                    }
                    titleHtml = chatSection + buildImagineSection(imagineData);
                    
                    rateLimitContainer._tooltipText = titleHtml;
                    if (rateLimitContainer._isClicked) {
                        const customTooltip = document.getElementById('grok-rate-limit-custom-tooltip');
                        if (customTooltip) {
                            const isDark = document.documentElement.classList.contains('dark') || document.body.style.backgroundColor === 'rgb(0, 0, 0)';
                            customTooltip.style.backgroundColor = isDark ? '#1a1a1a' : '#ffffff';
                            customTooltip.style.borderColor = isDark ? '#333333' : '#e5e5e5';
                            customTooltip.style.color = isDark ? '#e5e5e5' : '#1a1a1a';
                            
                            customTooltip.innerHTML = titleHtml;
                            customTooltip.style.display = 'block';
                            const rect = rateLimitContainer.getBoundingClientRect();
                            customTooltip.style.left = `${Math.max(10, rect.left + rect.width / 2 - customTooltip.offsetWidth / 2)}px`;
                            customTooltip.style.top = `${Math.max(10, rect.top - customTooltip.offsetHeight - 8)}px`;
                        }
                    }
                };

                updateTooltip();

                if (waitTimeSeconds > 0) {
                    const isOutOfQueries = (high <= 0);
                    if (isOutOfQueries) {
                        isCountingDown = true;
                    }

                    countdownTimer = setInterval(() => {
                        currentCountdown--;
                        if (currentCountdown <= 0) {
                            clearInterval(countdownTimer);
                            countdownTimer = null;
                            if (isOutOfQueries) isCountingDown = false;
                            fetchAndUpdateRateLimit(queryBar, true);
                        } else {
                            if (timerSpan) timerSpan.textContent = formatTimer(currentCountdown);
                            updateTooltip();
                        }
                    }, 1000);
                }
            } else {
                const lastForEffort = effort === 'imagine' ? lastImagine : ((effort === 'high') ? lastHigh : lastLow);
                lastForEffort.remaining = response.remainingQueries;
                lastForEffort.wait = response.waitTimeSeconds;

                const remaining = lastForEffort.remaining;
                const waitTimeSeconds = lastForEffort.wait;

                let currentCountdown = waitTimeSeconds;
                let timerSpan = null;

                if (remaining > 0) {
                    let displayText = remaining;
                    appendNumberSpan(contentDiv, displayText, '');
                    setGaugeSVG(svg);
                } else if (waitTimeSeconds > 0) {
                    timerSpan = appendNumberSpan(contentDiv, formatTimer(currentCountdown), '#ff6347');
                    setClockSVG(svg);
                } else {
                    appendNumberSpan(contentDiv, '0', '#ff6347');
                    setGaugeSVG(svg);
                }

                const updateTooltip = () => {
                    const hasHeavy = document.body.innerText.includes('Heavy') || (lastMulti?.heavy && lastMulti.heavy.remaining !== 'Unknown');
                    let titleHtml = "";
                    if (isImaginePage() && effort === 'imagine') {
                        const full = response.fullImagineData;
                        if (full) {
                            titleHtml = buildImagineSection(full).replace('mt-4 ', '');
                        } else {
                            titleHtml = `${lastForEffort.remaining} queries remaining`;
                        }
                    } else {
                        let chatSection = `
                            <div class="font-bold mb-3 text-base border-b border-border-l2 pb-2">Chat Queries Remaining</div>
                            <div class="flex justify-between items-center gap-6 mb-1.5">
                                <span class="text-fg-secondary">Auto:</span>
                                <span class="font-mono text-primary font-bold">${lastMulti?.auto?.remaining ?? 'Unknown'}</span>
                            </div>
                            <div class="flex justify-between items-center gap-6 mb-1.5">
                                <span class="text-fg-secondary">Grok 4.3 (Beta):</span>
                                <span class="font-mono text-primary font-bold">${lastMulti?.grok43?.remaining ?? 'Unknown'}</span>
                            </div>
                            ${hasHeavy ? `
                            <div class="flex justify-between items-center gap-6 mb-1.5">
                                <span class="text-fg-secondary">Heavy:</span>
                                <span class="font-mono text-primary font-bold">${lastMulti?.heavy?.remaining ?? 'Unknown'}</span>
                            </div>
                            ` : ''}
                            <div class="flex justify-between items-center gap-6 mb-1.5">
                                <span class="text-fg-secondary">Expert:</span>
                                <span class="font-mono text-primary font-bold">${lastMulti?.expert?.remaining ?? 'Unknown'}</span>
                            </div>
                            <div class="flex justify-between items-center gap-6 mb-3">
                                <span class="text-fg-secondary">Fast:</span>
                                <span class="font-mono text-primary font-bold">${lastMulti?.fast?.remaining ?? 'Unknown'}</span>
                            </div>
                            <div class="mt-2 pt-2 border-t border-border-l2 text-xs text-fg-secondary text-center">
                                Resets after ${lastMulti?.fast?.window || 2} hrs
                            </div>
                        `;
                        
                        if (currentCountdown > 0) {
                            chatSection += `
                                <div class="mt-2 pt-2 border-t border-border-l2 text-xs text-[#ff6347] text-center font-bold">
                                    Reset in ${formatTimer(currentCountdown)}
                                </div>
                            `;
                        }
                        titleHtml = chatSection + buildImagineSection(imagineData);
                    }

                    rateLimitContainer._tooltipText = titleHtml;
                    if (rateLimitContainer._isClicked) {
                        const customTooltip = document.getElementById('grok-rate-limit-custom-tooltip');
                        if (customTooltip) {
                            const isDark = document.documentElement.classList.contains('dark') || document.body.style.backgroundColor === 'rgb(0, 0, 0)';
                            customTooltip.style.backgroundColor = isDark ? '#1a1a1a' : '#ffffff';
                            customTooltip.style.borderColor = isDark ? '#333333' : '#e5e5e5';
                            customTooltip.style.color = isDark ? '#e5e5e5' : '#1a1a1a';

                            customTooltip.innerHTML = titleHtml;
                            customTooltip.style.display = 'block';
                            const rect = rateLimitContainer.getBoundingClientRect();
                            customTooltip.style.left = `${Math.max(10, rect.left + rect.width / 2 - customTooltip.offsetWidth / 2)}px`;
                            customTooltip.style.top = `${Math.max(10, rect.top - customTooltip.offsetHeight - 8)}px`;
                        }
                    }
                };

                updateTooltip();

                if (waitTimeSeconds > 0) {
                    const isOutOfQueries = (remaining <= 0);
                    if (isOutOfQueries) {
                        isCountingDown = true;
                    }

                    countdownTimer = setInterval(() => {
                        currentCountdown--;
                        if (currentCountdown <= 0) {
                            clearInterval(countdownTimer);
                            countdownTimer = null;
                            if (isOutOfQueries) isCountingDown = false;
                            fetchAndUpdateRateLimit(queryBar, true);
                        } else {
                            if (timerSpan) timerSpan.textContent = formatTimer(currentCountdown);
                            updateTooltip();
                        }
                    }, 1000);
                }
            }
        }
    }

    function appendNumberSpan(parent, text, color) {
        const span = document.createElement('span');
        span.textContent = text;
        if (color) span.style.color = color;
        parent.appendChild(span);
        return span;
    }

    function appendDivider(parent) {
        const span = document.createElement('span');
        span.textContent = '|';
        span.style.margin = '0 8px';
        span.style.color = '#9a9a9a';
        parent.appendChild(span);
    }

    function setGaugeSVG(svg) {
        if (svg) {
            while (svg.firstChild) {
                svg.removeChild(svg.firstChild);
            }
            const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path1.setAttribute('d', 'm12 14 4-4');
            const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path2.setAttribute('d', 'M3.34 19a10 10 0 1 1 17.32 0');
            svg.appendChild(path1);
            svg.appendChild(path2);
            svg.setAttribute('class', 'lucide lucide-gauge stroke-[2] text-fg-secondary transition-colors duration-100');
        }
    }

    function setClockSVG(svg) {
        if (svg) {
            while (svg.firstChild) {
                svg.removeChild(svg.firstChild);
            }
            const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
            circle.setAttribute('cx', '12');
            circle.setAttribute('cy', '12');
            circle.setAttribute('r', '8');
            circle.setAttribute('stroke', 'currentColor');
            circle.setAttribute('stroke-width', '2');
            const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute('d', 'M12 12L12 6');
            path.setAttribute('stroke', 'currentColor');
            path.setAttribute('stroke-width', '2');
            path.setAttribute('stroke-linecap', 'round');
            svg.appendChild(circle);
            svg.appendChild(path);
            svg.setAttribute('class', 'stroke-[2] text-fg-secondary group-hover/rate-limit:text-fg-primary transition-colors duration-100');
        }
    }

    function showResetMenu(anchorElement, modelName, queryBar) {
        let existingMenu = document.getElementById('grok-rate-limit-reset-menu');
        if (existingMenu) existingMenu.remove();

        const menu = document.createElement('div');
        menu.id = 'grok-rate-limit-reset-menu';
        
        menu.className = 'flex flex-col gap-1 p-2 rounded-xl border z-[9999] shadow-lg';
        menu.style.position = 'fixed';
        menu.style.backgroundColor = '#1a1a1a';
        menu.style.borderColor = '#333333';
        menu.style.color = '#e5e5e5';
        menu.style.minWidth = '140px';
        menu.style.fontSize = '14px';

        const createButton = (text, onClick, isCancel = false) => {
            const btn = document.createElement('button');
            btn.textContent = text;
            btn.className = 'w-full text-left px-3 py-2 rounded-lg hover:bg-[#333333] transition-colors focus:outline-none';
            if (isCancel) {
                btn.style.color = '#9a9a9a';
                btn.style.textAlign = 'center';
                btn.style.marginTop = '4px';
            }
            btn.addEventListener('click', (e) => {
                e.stopPropagation();
                onClick();
                menu.remove();
            });
            return btn;
        };

        const resetCurrentBtn = createButton('Reset Current', () => {
            if (chrome.runtime?.id) {
                chrome.runtime.sendMessage({ type: 'RESET_COUNTERS', model: modelName, action: 'current' }, () => {
                    fetchAndUpdateRateLimit(queryBar, true);
                });
            }
        });

        const resetAllBtn = createButton('Reset All', () => {
            if (chrome.runtime?.id) {
                chrome.runtime.sendMessage({ type: 'RESET_COUNTERS', model: modelName, action: 'all' }, () => {
                    fetchAndUpdateRateLimit(queryBar, true);
                });
            }
        });

        const cancelBtn = createButton('Cancel', () => {}, true);

        menu.appendChild(resetCurrentBtn);
        menu.appendChild(resetAllBtn);
        menu.appendChild(cancelBtn);

        document.body.appendChild(menu);

        const rect = anchorElement.getBoundingClientRect();
        const menuRect = menu.getBoundingClientRect();
        
        let targetLeft = rect.left + (rect.width / 2) - (menuRect.width / 2);
        if (targetLeft < 10) targetLeft = 10; // Prevent falling off left edge constraint
        
        menu.style.left = `${targetLeft}px`;
        menu.style.top = `${rect.top - menuRect.height - 8}px`;

        const closeMenuOnOutsideClick = (e) => {
            if (!menu.contains(e.target) && !anchorElement.contains(e.target)) {
                menu.remove();
                document.removeEventListener('mousedown', closeMenuOnOutsideClick);
            }
        };
        setTimeout(() => document.addEventListener('mousedown', closeMenuOnOutsideClick), 0);
    }

    async function fetchImagineRateLimit(force = false) {
        if (!force && cachedRateLimits['imagine_info'] !== undefined) {
            return cachedRateLimits['imagine_info'];
        }
        try {
            const response = await fetch(window.location.origin + '/rest/media/imagine/quota_info', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({}),
                credentials: 'include',
            });
            if (!response.ok) throw new Error(`HTTP error: Status ${response.status}`);
            const data = await response.json();
            cachedRateLimits['imagine_info'] = data;
            return data;
        } catch (error) {
            console.error(`Failed to fetch imagine rate limit:`, error);
            return { error: true };
        }
    }

    // Function to fetch rate limit
    async function fetchRateLimit(modelName, requestKind, force = false) {
        if (!force) {
            const cached = cachedRateLimits[modelName]?.[requestKind];
            if (cached !== undefined) {
                return cached;
            }
        }

        try {
            const response = await fetch(window.location.origin + '/rest/rate-limits', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    requestKind,
                    modelName,
                }),
                credentials: 'include',
            });

            if (!response.ok) {
                throw new Error(`HTTP error: Status ${response.status}`);
            }

            const data = await response.json();
            if (!cachedRateLimits[modelName]) {
                cachedRateLimits[modelName] = {};
            }
            cachedRateLimits[modelName][requestKind] = data;
            return data;
        } catch (error) {
            console.error(`Failed to fetch rate limit:`, error);
            if (!cachedRateLimits[modelName]) {
                cachedRateLimits[modelName] = {};
            }
            cachedRateLimits[modelName][requestKind] = undefined;
            return { error: true };
        }
    }

    // Function to process the rate limit data based on effort level
    function processRateLimitData(data, effortLevel, modelName) {
        if (data.error) {
            return data;
        }

        if (effortLevel === 'imagine') {
            const imagineData = data[modelName];
            if (!imagineData) return { error: true };
            return {
                remainingQueries: imagineData.remainingQueries,
                windowSizeSeconds: imagineData.windowSizeSeconds,
                waitTimeSeconds: 0, // Assume no wait time for imagine limits based on JSON schema
                fullImagineData: data
            };
        }

        const getWaitTime = (obj) => {
            if (!obj) return 0;
            // Check prioritize waitTimeSeconds, then resetsAt (timestamp), then resetTime
            if (obj.waitTimeSeconds) return obj.waitTimeSeconds;
            if (obj.resetsAt) {
                const wait = Math.round((obj.resetsAt - Date.now()) / 1000);
                return wait > 0 ? wait : 0;
            }
            if (obj.resetTime) return obj.resetTime;
            return 0;
        };

        if (effortLevel === 'both') {
            const high = data.highEffortRateLimits?.remainingQueries;
            const low = data.lowEffortRateLimits?.remainingQueries;
            const waitTimeSeconds = Math.max(
                getWaitTime(data.highEffortRateLimits),
                getWaitTime(data.lowEffortRateLimits),
                getWaitTime(data)
            );

            if (high !== undefined && low !== undefined && high !== null && low !== null) {
                return {
                    highRemaining: high,
                    lowRemaining: low,
                    waitTimeSeconds: waitTimeSeconds
                };
            } else if (data.remainingQueries !== undefined) {
                // If breakdown is missing (e.g. unauthenticated), fallback to top-level
                return {
                    highRemaining: data.remainingQueries,
                    lowRemaining: data.remainingQueries, // Or maybe null? Let's show the same for both if we don't know
                    waitTimeSeconds: waitTimeSeconds
                };
            } else {
                return { error: true };
            }
        } else {
            let rateLimitsKey = effortLevel === 'high' ? 'highEffortRateLimits' : 'lowEffortRateLimits';
            let remaining = data[rateLimitsKey]?.remainingQueries ?? data.remainingQueries;

            if (remaining !== undefined) {
                return {
                    remainingQueries: remaining,
                    waitTimeSeconds: getWaitTime(data[rateLimitsKey]) || getWaitTime(data)
                };
            } else {
                return { error: true };
            }
        }
    }

    // Function to fetch and update rate limit
    async function fetchAndUpdateRateLimit(queryBar, force = false, pollUntilChange = false) {
        if (!queryBar || !document.body.contains(queryBar)) {
            return;
        }
        const modelName = getCurrentModelKey(queryBar);

        if (modelName !== lastModelName) {
            force = true;
        }

        if (isCountingDown && !force) {
            return;
        }

        const effortLevel = getEffortLevel(modelName);

        let requestKind = DEFAULT_KIND;
        if (modelName === 'grok-3') {
            const thinkButton = findElement(commonFinderConfigs.thinkButton, queryBar);
            const searchButton = findElement(commonFinderConfigs.deepSearchButton, queryBar);

            if (thinkButton && thinkButton.getAttribute('aria-pressed') === 'true') {
                requestKind = 'REASONING';
            } else if (searchButton && searchButton.getAttribute('aria-pressed') === 'true') {
                const searchAria = searchButton.getAttribute('aria-label') || '';
                if (/deeper/i.test(searchAria)) {
                    requestKind = 'DEEPERSEARCH';
                } else if (/deep/i.test(searchAria)) {
                    requestKind = 'DEEPSEARCH';
                }
            }
        }

        let data, imagineData, multiData;
        if (isImaginePage()) {
            data = await fetchImagineRateLimit(force);
            imagineData = null; 
        } else {
            const rateLimitContainer = document.getElementById(RATE_LIMIT_CONTAINER_ID);
            const shouldFetchAll = rateLimitContainer && rateLimitContainer._isClicked;

            if (shouldFetchAll) {
                const modelsToFetch = [
                    { key: 'grok-4-auto', name: 'auto' },
                    { key: 'grok-420-computer-use-sa', name: 'grok43' },
                    { key: 'grok-4-heavy', name: 'heavy' },
                    { key: 'grok-4', name: 'expert' },
                    { key: 'grok-3', name: 'fast' }
                ];
                const promises = [
                    fetchRateLimit(modelName, requestKind, force),
                    fetchImagineRateLimit(force)
                ];
                for (const m of modelsToFetch) {
                    promises.push(fetchRateLimit(m.key, DEFAULT_KIND, force));
                }
                const results = await Promise.all(promises);
                data = results[0];
                imagineData = results[1];
                multiData = {
                    auto: results[2],
                    grok43: results[3],
                    heavy: results[4],
                    expert: results[5],
                    fast: results[6]
                };
            } else {
                data = await fetchRateLimit(modelName, requestKind, force);
                imagineData = null;
                multiData = null;
            }
        }

        if (chrome.runtime?.id) {
            const syncWithBackground = (bucket, remaining, total, windowSize) => {
                return new Promise((resolve, reject) => {
                    chrome.runtime.sendMessage({
                        type: 'SYNC_AND_GET_REMAINING',
                        model: bucket,
                        remainingQueries: remaining,
                        totalQueries: total,
                        windowSizeSeconds: windowSize
                    }, (response) => {
                        if (chrome.runtime.lastError) reject(chrome.runtime.lastError);
                        else resolve(response);
                    });
                });
            };

            const getFromBackground = (bucket) => {
                return new Promise((resolve, reject) => {
                    chrome.runtime.sendMessage({
                        type: 'GET_REMAINING',
                        model: bucket
                    }, (response) => {
                        if (chrome.runtime.lastError) reject(chrome.runtime.lastError);
                        else resolve(response);
                    });
                });
            };

            if (!data.error) {
                // API SUCCESS: Use API data but sync to background for redundancy
                try {
                    if (effortLevel === 'both') {
                        let highRemaining = data.highEffortRateLimits?.remainingQueries ?? null;
                        let highTotal = data.highEffortRateLimits?.totalQueries ?? null;
                        let lowRemaining = data.lowEffortRateLimits?.remainingQueries ?? null;
                        let lowTotal = data.lowEffortRateLimits?.totalQueries ?? null;
                        
                        // Check if we should poll for change (max 3 times, 2s apart)
                        if (pollUntilChange && typeof pollUntilChange === 'number' && pollUntilChange > 0) {
                            if (highRemaining === lastBoth.high && highRemaining !== null) {
                                console.log(`Grok Rate Limit Extension: Count unchanged (${highRemaining}), polling... (${pollUntilChange} retries left)`);
                                setTimeout(() => fetchAndUpdateRateLimit(queryBar, true, pollUntilChange - 1), 2000);
                            }
                        }

                        await syncWithBackground('grok-4', highRemaining, highTotal, data.windowSizeSeconds);
                        await syncWithBackground('grok-3', lowRemaining, lowTotal, data.windowSizeSeconds);
                    } else {
                        let rateLimitsKey = effortLevel === 'high' ? 'highEffortRateLimits' : 'lowEffortRateLimits';
                        let apiRemaining = data[rateLimitsKey]?.remainingQueries ?? data.remainingQueries;
                        let apiTotal = data[rateLimitsKey]?.totalQueries ?? data.totalQueries;

                        // Check if we should poll for change (max 3 times, 2s apart)
                        if (pollUntilChange && typeof pollUntilChange === 'number' && pollUntilChange > 0) {
                            const lastRemaining = effortLevel === 'high' ? lastHigh.remaining : lastLow.remaining;
                            if (apiRemaining === lastRemaining && apiRemaining !== null) {
                                console.log(`Grok Rate Limit Extension: Count unchanged (${apiRemaining}), polling... (${pollUntilChange} retries left)`);
                                setTimeout(() => fetchAndUpdateRateLimit(queryBar, true, pollUntilChange - 1), 2000);
                            }
                        }

                        await syncWithBackground(modelName, apiRemaining, apiTotal, data.windowSizeSeconds);
                    }
                } catch (e) {
                    console.warn('Grok Rate Limit Extension: Failed to sync API data to background tracker.');
                }
            } else if (!isImaginePage()) {
                // API FAILURE: Fallback to local background tracking (not available for imagine)
                console.log('Grok Rate Limit Extension: API failed, falling back to local tracking.');
                try {
                    if (effortLevel === 'both') {
                        const bgDataHigh = await getFromBackground('grok-4');
                        const bgDataLow = await getFromBackground('grok-3');

                        data = {
                            ...data,
                            error: false, // Clear error since we have fallback
                            highEffortRateLimits: {
                                remainingQueries: bgDataHigh.remaining,
                                waitTimeSeconds: bgDataHigh.nextResetSeconds
                            },
                            lowEffortRateLimits: {
                                remainingQueries: bgDataLow.remaining,
                                waitTimeSeconds: bgDataLow.nextResetSeconds
                            },
                            windowSizeSeconds: bgDataHigh.windowSizeSeconds
                        };
                    } else {
                        const bgData = await getFromBackground(modelName);
                        let rateLimitsKey = effortLevel === 'high' ? 'highEffortRateLimits' : 'lowEffortRateLimits';

                        data = {
                            ...data,
                            error: false,
                            remainingQueries: bgData.remaining,
                            waitTimeSeconds: bgData.nextResetSeconds,
                            windowSizeSeconds: bgData.windowSizeSeconds
                        };

                        // Ensure the specific effort level object is also populated
                        data[rateLimitsKey] = {
                            remainingQueries: bgData.remaining,
                            waitTimeSeconds: bgData.nextResetSeconds
                        };
                    }
                } catch (e) {
                    console.error('Grok Rate Limit Extension: Both API and level fallback failed.', e);
                }
            }
        }

        if (multiData) {
            lastMulti = {};
            for (const [key, res] of Object.entries(multiData)) {
                if (!res || res.error) {
                    lastMulti[key] = { remaining: 'Unknown', window: null };
                } else {
                    let rem = res.remainingQueries;
                    if (rem === undefined && res.highEffortRateLimits) {
                        rem = res.highEffortRateLimits.remainingQueries;
                    }
                    if (rem === undefined && res.lowEffortRateLimits) {
                        rem = res.lowEffortRateLimits.remainingQueries;
                    }
                    
                    lastMulti[key] = {
                        remaining: rem !== undefined ? rem : 'Unknown',
                        window: res.windowSizeSeconds ? Math.round(res.windowSizeSeconds / 3600) : null
                    };
                }
            }
        } else {
            if (!lastMulti) {
                lastMulti = {
                    auto: { remaining: 'Unknown', window: null },
                    grok43: { remaining: 'Unknown', window: null },
                    heavy: { remaining: 'Unknown', window: null },
                    expert: { remaining: 'Unknown', window: null },
                    fast: { remaining: 'Unknown', window: null }
                };
            }

            if (data && !data.error) {
                const modelToNameMap = {
                    'grok-4-auto': 'auto',
                    'grok-420-computer-use-sa': 'grok43',
                    'grok-420': 'grok43',
                    'grok-4-heavy': 'heavy',
                    'grok-4': 'expert',
                    'grok-3': 'fast'
                };
                const mappedName = modelToNameMap[modelName];
                if (mappedName) {
                    let rem = data.remainingQueries;
                    if (rem === undefined && data.highEffortRateLimits) {
                        rem = data.highEffortRateLimits.remainingQueries;
                    }
                    if (rem === undefined && data.lowEffortRateLimits) {
                        rem = data.lowEffortRateLimits.remainingQueries;
                    }
                    lastMulti[mappedName] = {
                        remaining: rem !== undefined ? rem : 'Unknown',
                        window: data.windowSizeSeconds ? Math.round(data.windowSizeSeconds / 3600) : null
                    };
                }
            }
        }

        const processedData = processRateLimitData(data, effortLevel, modelName);
        updateRateLimitDisplay(queryBar, processedData, effortLevel, modelName, imagineData);

        lastModelName = modelName;
    }

    // Function to observe the DOM for the query bar
    function observeDOM() {
        const handleVisibilityChange = () => {
            if (document.visibilityState === 'visible' && lastQueryBar) {
                fetchAndUpdateRateLimit(lastQueryBar, true);
            }
        };

        // Add resize listener to handle mobile/desktop mode switches
        const handleResize = debounce(() => {
            if (lastQueryBar) {
                checkTextOverlap(lastQueryBar);
            }
        }, 300);

        document.addEventListener('visibilitychange', handleVisibilityChange);
        window.addEventListener('resize', handleResize);

        const initialQueryBar = document.querySelector(QUERY_BAR_SELECTOR);
        if (initialQueryBar) {
            removeExistingRateLimit();
            fetchAndUpdateRateLimit(initialQueryBar);
            lastQueryBar = initialQueryBar;

            setupQueryBarObserver(initialQueryBar);
            setupGrok3Observers(initialQueryBar);
            setupSubmissionListeners(initialQueryBar);

            // Start overlap checking
            startOverlapChecking(initialQueryBar);
            setTimeout(() => checkTextOverlap(initialQueryBar), 100);
        }

        const observer = new MutationObserver(() => {
            const queryBar = document.querySelector(QUERY_BAR_SELECTOR);
            if (queryBar && queryBar !== lastQueryBar) {
                removeExistingRateLimit();
                fetchAndUpdateRateLimit(queryBar);
                if (lastModelObserver) {
                    lastModelObserver.disconnect();
                }
                if (lastThinkObserver) {
                    lastThinkObserver.disconnect();
                }
                if (lastSearchObserver) {
                    lastSearchObserver.disconnect();
                }

                setupQueryBarObserver(queryBar);
                setupGrok3Observers(queryBar);
                setupSubmissionListeners(queryBar);

                // Start overlap checking
                startOverlapChecking(queryBar);
                setTimeout(() => checkTextOverlap(queryBar), 100);

                // Removed redundant polling
                lastQueryBar = queryBar;
            } else if (!queryBar && lastQueryBar) {
                removeExistingRateLimit();
                stopOverlapChecking();
                if (lastModelObserver) {
                    lastModelObserver.disconnect();
                }
                if (lastThinkObserver) {
                    lastThinkObserver.disconnect();
                }
                if (lastSearchObserver) {
                    lastSearchObserver.disconnect();
                }
                lastQueryBar = null;
                lastModelObserver = null;
                lastThinkObserver = null;
                lastSearchObserver = null;
                lastInputElement = null;
                lastSubmitButton = null;
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
        });

        function setupQueryBarObserver(queryBar) {
            const debouncedUpdate = debounce(() => {
                fetchAndUpdateRateLimit(queryBar);
                setupGrok3Observers(queryBar);
            }, 300);

            lastModelObserver = new MutationObserver(debouncedUpdate);
            lastModelObserver.observe(queryBar, { childList: true, subtree: true, attributes: true, characterData: true });
        }

        function setupGrok3Observers(queryBar) {
            const currentModel = getCurrentModelKey(queryBar);
            if (currentModel === 'grok-3') {
                const thinkButton = findElement(commonFinderConfigs.thinkButton, queryBar);
                if (thinkButton) {
                    if (lastThinkObserver) lastThinkObserver.disconnect();
                    lastThinkObserver = new MutationObserver(() => {
                        fetchAndUpdateRateLimit(queryBar);
                    });
                    lastThinkObserver.observe(thinkButton, { attributes: true, attributeFilter: ['aria-pressed', 'class'] });
                }
                const searchButton = findElement(commonFinderConfigs.deepSearchButton, queryBar);
                if (searchButton) {
                    if (lastSearchObserver) lastSearchObserver.disconnect();
                    lastSearchObserver = new MutationObserver(() => {
                        fetchAndUpdateRateLimit(queryBar);
                    });
                    lastSearchObserver.observe(searchButton, { attributes: true, attributeFilter: ['aria-pressed', 'class'], childList: true, subtree: true, characterData: true });
                }
            } else {
                if (lastThinkObserver) {
                    lastThinkObserver.disconnect();
                    lastThinkObserver = null;
                }
                if (lastSearchObserver) {
                    lastSearchObserver.disconnect();
                    lastSearchObserver = null;
                }
            }
        }

        function setupSubmissionListeners(queryBar) {
            const inputElement = queryBar.querySelector('div[contenteditable="true"]');
            if (inputElement && inputElement !== lastInputElement) {
                lastInputElement = inputElement;

                const debouncedOverlapCheck = debounce(() => {
                    checkTextOverlap(queryBar);
                }, 300);

                inputElement.addEventListener('keydown', (e) => {
                    const modelName = getCurrentModelKey(queryBar);
                    if (e.key === 'Enter' && !e.shiftKey) {
                        // For locally tracked models, we rely on the interceptor, but we still want to refresh the UI
                        const locallyTrackedModels = ['grok-420', 'grok-420-computer-use-sa', 'grok-4-heavy', 'grok-3', 'grok-4', 'grok-4-auto'];
                        const delay = locallyTrackedModels.includes(modelName) ? 1000 : 3000;
                        setTimeout(() => fetchAndUpdateRateLimit(queryBar, true, 3), delay);
                    }
                });

                inputElement.addEventListener('input', debouncedOverlapCheck);
                inputElement.addEventListener('focus', debouncedOverlapCheck);
                inputElement.addEventListener('blur', () => {
                    setTimeout(() => {
                        checkTextOverlap(queryBar);
                    }, 200);
                });
            }

            const bottomBar = queryBar.querySelector('div.absolute.inset-x-0.bottom-0');
            const submitButton = bottomBar ? findElement(commonFinderConfigs.submitButton, bottomBar) : findElement(commonFinderConfigs.submitButton, queryBar);
            if (submitButton && submitButton !== lastSubmitButton) {
                lastSubmitButton = submitButton;
                submitButton.addEventListener('click', () => {
                    const modelName = getCurrentModelKey(queryBar);
                    const locallyTrackedModels = ['grok-420', 'grok-420-computer-use-sa', 'grok-4-heavy', 'grok-3', 'grok-4', 'grok-4-auto'];
                    const delay = locallyTrackedModels.includes(modelName) ? 1000 : 3000;
                    setTimeout(() => fetchAndUpdateRateLimit(queryBar, true, 3), delay);
                });
            }
        }
    }

    // Start observing the DOM for changes
    observeDOM();

    function showNagScreen() {
        if (document.getElementById('grok-nag-screen')) return;

        // Ensure font is injected
        if (!document.getElementById('grok-nag-font')) {
            const fontLink = document.createElement('link');
            fontLink.id = 'grok-nag-font';
            fontLink.rel = 'stylesheet';
            fontLink.href = 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap';
            document.head.appendChild(fontLink);
        }

        // Add Stylesheet scoped tightly to prevent pollution or resets from Grok.com
        const style = document.createElement('style');
        style.id = 'grok-nag-styles';
        style.textContent = `
            .grok-nag-overlay {
                all: initial;
                position: fixed;
                inset: 0;
                background: rgba(8, 8, 9, 0.75);
                backdrop-filter: blur(12px);
                -webkit-backdrop-filter: blur(12px);
                z-index: 999999999;
                display: flex;
                align-items: center;
                justify-content: center;
                opacity: 0;
                transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
                pointer-events: auto;
                font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                box-sizing: border-box;
            }
            .grok-nag-overlay * {
                box-sizing: border-box;
            }
            .grok-nag-overlay.active {
                opacity: 1;
            }
            .grok-nag-dialog {
                background: #0b0b0c;
                border: 1px solid #27272a;
                border-radius: 24px;
                padding: 32px 28px;
                width: 90%;
                max-width: 400px;
                box-shadow: 0 24px 48px rgba(0, 0, 0, 0.6);
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 20px;
                transform: scale(0.9) translateY(20px);
                transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
            }
            .grok-nag-overlay.active .grok-nag-dialog {
                transform: scale(1) translateY(0);
            }
            .grok-nag-title-group {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 6px;
                text-align: center;
            }
            .grok-nag-title {
                color: #ffffff;
                font-size: 20px;
                font-weight: 700;
                margin: 0;
                line-height: 1.3;
                letter-spacing: -0.02em;
                font-family: 'Plus Jakarta Sans', sans-serif;
            }
            .grok-nag-author {
                color: #a1a1aa;
                font-size: 13px;
                font-family: 'Plus Jakarta Sans', sans-serif;
            }
            .grok-nag-author-link {
                color: #38bdf8;
                text-decoration: none;
                font-weight: 500;
            }
            .grok-nag-author-link:hover {
                text-decoration: underline;
            }
            .grok-nag-body {
                color: #e4e4e7;
                font-size: 14px;
                line-height: 1.5;
                margin: 0;
                text-align: center;
                font-weight: 450;
                font-family: 'Plus Jakarta Sans', sans-serif;
            }
            .grok-nag-buttons {
                display: flex;
                flex-direction: column;
                gap: 10px;
                width: 100%;
            }
            .grok-nag-btn {
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 8px;
                width: 100%;
                height: 44px;
                border-radius: 12px;
                font-size: 14px;
                font-weight: 600;
                text-decoration: none;
                cursor: pointer;
                transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
                font-family: 'Plus Jakarta Sans', sans-serif;
            }
            .grok-nag-btn-donate {
                background: linear-gradient(135deg, #FFDD00 0%, #FFB000 100%);
                color: #000000 !important;
                border: none;
                box-shadow: 0 4px 12px rgba(255, 221, 0, 0.15);
            }
            .grok-nag-btn-donate:hover {
                transform: translateY(-1.5px);
                box-shadow: 0 6px 16px rgba(255, 221, 0, 0.25);
                background: linear-gradient(135deg, #ffe333 0%, #ffbb1a 100%);
            }
            .grok-nag-btn-subscribe {
                background: #1c1c1f;
                color: #ffffff !important;
                border: 1px solid #3f3f46;
            }
            .grok-nag-btn-subscribe:hover {
                transform: translateY(-1.5px);
                background: #27272a;
                border-color: #52525b;
                box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
            }
            .grok-nag-btn:active {
                transform: translateY(0);
            }
            .grok-nag-close {
                background: transparent;
                color: #71717a;
                border: none;
                font-size: 13px;
                font-weight: 500;
                cursor: pointer;
                text-decoration: none;
                margin-top: 4px;
                transition: color 0.2s;
                font-family: 'Plus Jakarta Sans', sans-serif;
                align-self: center;
            }
            .grok-nag-close:hover {
                color: #a1a1aa;
                text-decoration: underline;
            }
            .grok-nag-icon-container {
                background: rgba(255, 255, 255, 0.03);
                border: 1px solid rgba(255, 255, 255, 0.08);
                border-radius: 50%;
                width: 68px;
                height: 68px;
                display: flex;
                align-items: center;
                justify-content: center;
                margin-bottom: 4px;
            }
            .grok-nag-btn-icon {
                width: 16px;
                height: 16px;
            }
        `;
        document.head.appendChild(style);

        // Create elements
        const overlay = document.createElement('div');
        overlay.id = 'grok-nag-screen';
        overlay.className = 'grok-nag-overlay';

        overlay.innerHTML = `
            <div class="grok-nag-dialog">
                <div class="grok-nag-icon-container">
                    <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <path d="m12 14 4-4"></path>
                        <path d="M3.34 19a10 10 0 1 1 17.32 0"></path>
                    </svg>
                </div>
                <div class="grok-nag-title-group">
                    <h2 class="grok-nag-title">Grok Rate Limit Display</h2>
                    <div class="grok-nag-author">by <a href="https://x.com/blankspeaker" target="_blank" class="grok-nag-author-link">@blankspeaker</a></div>
                </div>
                <p class="grok-nag-body">
                    Thank you for using Grok Rate Limit Display!<br><br>
                    This project is funded by your donations. If this extension makes your experience better, please consider donating or subscribing to help keep the project updated.
                </p>
                <div class="grok-nag-buttons">
                    <a href="https://buymeacoffee.com/blank_speaker" target="_blank" class="grok-nag-btn grok-nag-btn-donate">
                        <svg class="grok-nag-btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <path d="M17 8h1a4 4 0 1 1 0 8h-1"></path>
                            <path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"></path>
                            <line x1="6" y1="2" x2="6" y2="4"></line>
                            <line x1="10" y1="2" x2="10" y2="4"></line>
                            <line x1="14" y1="2" x2="14" y2="4"></line>
                        </svg>
                        Donate
                    </a>
                    <a href="https://x.com/blankspeaker/creator-subscriptions/subscribe" target="_blank" class="grok-nag-btn grok-nag-btn-subscribe">
                        <svg class="grok-nag-btn-icon" viewBox="0 0 24 24" fill="currentColor">
                            <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
                        </svg>
                        Subscribe
                    </a>
                    <button class="grok-nag-close" id="grok-nag-close-btn">Close &amp; Let's Go</button>
                </div>
            </div>
        `;

        document.body.appendChild(overlay);

        // Animate in
        setTimeout(() => {
            overlay.classList.add('active');
        }, 50);

        // Dismiss logic
        const dismiss = () => {
            overlay.classList.remove('active');
            setTimeout(() => {
                overlay.remove();
                style.remove();
            }, 400);

            // Save state
            if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
                try {
                    chrome.storage.local.set({ hasShownNag: true });
                } catch (e) {
                    localStorage.setItem('grok_hasShownNag', 'true');
                }
            } else {
                if (typeof GM_setValue !== 'undefined') {
                    GM_setValue('hasShownNag', true);
                } else {
                    localStorage.setItem('grok_hasShownNag', 'true');
                }
            }
        };

        document.getElementById('grok-nag-close-btn').addEventListener('click', dismiss);
    }

    // Check and show welcome nag screen on first launch of the extension
    try {
        if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
            chrome.storage.local.get(['hasShownNag'], (result) => {
                if (!result.hasShownNag) {
                    if (document.body) {
                        showNagScreen();
                    } else {
                        document.addEventListener('DOMContentLoaded', showNagScreen);
                    }
                }
            });
        } else {
            const hasShown = typeof GM_getValue !== 'undefined' ? GM_getValue('hasShownNag', false) : localStorage.getItem('grok_hasShownNag');
            if (!hasShown) {
                if (document.body) {
                    showNagScreen();
                } else {
                    document.addEventListener('DOMContentLoaded', showNagScreen);
                }
            }
        }
    } catch (e) {
        console.warn('Grok Rate Limit Extension: Failed to check or show welcome nag screen:', e);
    }

})();