Aceable Autoanswer with Gemini AI

Auto-answer Aceable questions using Gemini AI, auto-play videos, auto-next, prevent idle refresh, works in background

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Aceable Autoanswer with Gemini AI
// @namespace    http://tampermonkey.net/
// @version      2.2
// @description  Auto-answer Aceable questions using Gemini AI, auto-play videos, auto-next, prevent idle refresh, works in background
// @author       Coolbossco (original author: DudeUnoob)
// @match        https://*.aceable.com/*
// @grant        GM_xmlhttpRequest
// @connect      generativelanguage.googleapis.com
// @connect      gitlab.com
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";

  // ========================================
  // CONFIGURATION - STORED IN TAMPERMONKEY STORAGE
  // ========================================
  function getConfig() {
    const defaults = {
      geminiApiKey: "YOUR_API_KEY_HERE",
      useAI: true,
      aiModel: "gemini-2.5-flash-lite",
      scriptEnabled: true,
      debugLogging: false,
      // Profile Data for Verification
      firstName: "",
      lastName: "",
      phoneNumber: "",
      email: "",
      address: "",
      city: "",
      state: "",
      zipCode: "",
      birthDate: "", // Format: Month DD, YYYY (e.g. August 15, 2005)
    };

    // Try GM storage first
    let stored = GM_getValue("aceableScriptConfig");
    
    // Migration: If not in GM but is in localStorage, migrate it
    if (!stored) {
        const local = localStorage.getItem("aceableScriptConfig");
        if (local) {
            try {
                stored = JSON.parse(local);
                GM_setValue("aceableScriptConfig", stored); // Save to GM
                console.log("[Aceable Script] Migrated settings from localStorage to GM storage");
            } catch (e) {
                console.error("[Aceable Script] Error migrating settings:", e);
            }
        }
    }

    const config = stored ? { ...defaults, ...stored } : defaults;
    // Always use gemini-2.5-flash-lite
    config.aiModel = "gemini-2.5-flash-lite";
    return config;
  }

  function saveConfig(config) {
    GM_setValue("aceableScriptConfig", config);
  }

  function getStoredQuestions() {
    let questions = GM_getValue("questions");
    if (!questions) {
        const local = localStorage.getItem("questions");
        if (local) {
             try {
                questions = JSON.parse(local);
                GM_setValue("questions", questions);
             } catch(e) {}
        }
    }
    return questions || {};
  }

  function saveStoredQuestions(questions) {
    GM_setValue("questions", questions);
  }

  let config = getConfig();

  // Wait for DOM to be ready
  function init() {
    // --- Break Detection State ---
    let isOnBreak = false;
    let breakEndTime = null;
    let breakCheckInterval = null;
    let breakLoggedOnce = false;

    // --- AI / Question Handling State ---
    let playFactProcessing = false;
    let testQuestionProcessing = false;
    let categoryQuestionProcessing = false;
    let lastFoafFact = "";
    let lastFoafFactTime = 0;

    // --- Version Check ---
    const UPDATE_URL = "https://gitlab.com/Coolbossco/Aceable/-/raw/main/userscript.js"; // TODO: Replace with actual URL
    let isCheckingUpdate = false;

    // --- Settings Menu ---
    let settingsMenuOpen = false;

    // Inject CSS for Settings Menu
    function injectSettingsCSS() {
      if (document.getElementById("aceable-settings-css")) return;

      const style = document.createElement("style");
      style.id = "aceable-settings-css";
      style.textContent = `
        .aceable-settings-overlay {
          position: fixed; top: 0; left: 0; width: 100%; height: 100%;
          background: rgba(0,0,0,0.8); z-index: 999999;
          display: flex; justify-content: center; align-items: center;
          backdrop-filter: blur(5px);
        }
        .aceable-settings-modal {
          background: #1a1a1a; color: #e0e0e0;
          width: 700px; height: 500px;
          border-radius: 12px; display: flex; overflow: hidden;
          box-shadow: 0 10px 40px rgba(0,0,0,0.6);
          border: 1px solid #333; font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
        }
        .aceable-settings-sidebar {
          width: 200px; background: #252525;
          border-right: 1px solid #333; display: flex; flex-direction: column;
        }
        .aceable-settings-sidebar-header {
          padding: 20px; border-bottom: 1px solid #333;
          font-size: 16px; font-weight: bold; color: #0f0;
          display: flex; align-items: center; gap: 10px;
        }
        .aceable-nav-item {
          padding: 15px 20px; cursor: pointer; transition: background 0.2s;
          border-left: 3px solid transparent; color: #aaa; font-size: 14px;
          background: none; border: none; text-align: left; width: 100%; outline: none;
        }
        .aceable-nav-item:hover { background: #2a2a2a; color: #fff; }
        .aceable-nav-item.active {
          background: #2a2a2a; color: #fff; border-left-color: #0f0;
        }
        .aceable-settings-content {
          flex: 1; display: flex; flex-direction: column;
        }
        .aceable-tab-content {
          flex: 1; padding: 30px; overflow-y: auto; display: none;
        }
        .aceable-tab-content.active { display: block; }
        .aceable-settings-footer {
          padding: 15px 30px; background: #252525; border-top: 1px solid #333;
          display: flex; justify-content: flex-end; gap: 10px;
        }
        
        /* Form Elements */
        .aceable-form-group { margin-bottom: 20px; }
        .aceable-label { display: block; margin-bottom: 8px; font-size: 13px; color: #aaa; }
        .aceable-input {
          width: 100%; padding: 10px; background: #333; border: 1px solid #444;
          color: #fff; border-radius: 6px; font-size: 14px; box-sizing: border-box;
          transition: border-color 0.2s;
        }
        .aceable-input:focus { border-color: #0f0; outline: none; }
        .aceable-helper { font-size: 12px; color: #666; margin-top: 5px; }
        
        /* Toggle Switch */
        .aceable-toggle-row {
          display: flex; justify-content: space-between; align-items: center;
          margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #333;
        }
        .aceable-toggle-label { font-weight: 500; font-size: 15px; color: #fff; }
        .aceable-switch {
          position: relative; display: inline-block; width: 50px; height: 26px;
        }
        .aceable-switch input { opacity: 0; width: 0; height: 0; }
        .aceable-slider {
          position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0;
          background-color: #444; transition: .4s; border-radius: 34px;
        }
        .aceable-slider:before {
          position: absolute; content: ""; height: 20px; width: 20px;
          left: 3px; bottom: 3px; background-color: white;
          transition: .4s; border-radius: 50%;
        }
        input:checked + .aceable-slider { background-color: #0f0; }
        input:checked + .aceable-slider:before { transform: translateX(24px); }
        
        /* Buttons */
        .aceable-btn {
          padding: 8px 20px; border-radius: 6px; font-weight: 600; cursor: pointer;
          border: none; font-size: 14px; transition: opacity 0.2s;
        }
        .aceable-btn:hover { opacity: 0.9; }
        .aceable-btn-primary { background: #0f0; color: #000; }
        .aceable-btn-secondary { background: #444; color: #fff; }
        
        /* Grid for Profile */
        .aceable-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }

        /* Toast Notifications */
        .aceable-toast-container {
          position: fixed; bottom: 20px; right: 20px; z-index: 1000000;
          display: flex; flex-direction: column; gap: 10px; pointer-events: none;
        }
        .aceable-toast {
          background: #1a1a1a; color: #fff; padding: 12px 20px;
          border-radius: 8px; border-left: 4px solid #444;
          box-shadow: 0 5px 15px rgba(0,0,0,0.5);
          font-family: 'Segoe UI', Roboto, sans-serif; font-size: 14px;
          display: flex; align-items: center; gap: 10px;
          opacity: 0; transform: translateX(20px);
          animation: aceable-toast-in 0.3s forwards;
          pointer-events: auto; min-width: 250px; max-width: 400px;
        }
        .aceable-toast.success { border-left-color: #0f0; }
        .aceable-toast.error { border-left-color: #f00; }
        .aceable-toast.info { border-left-color: #00f; }
        
        .aceable-toast-content { flex: 1; }
        .aceable-toast-action {
          background: #333; border: none; color: #0f0; padding: 5px 10px;
          border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: bold;
          margin-left: 10px;
        }
        .aceable-toast-action:hover { background: #444; }

        @keyframes aceable-toast-in {
          to { opacity: 1; transform: translateX(0); }
        }
        @keyframes aceable-toast-out {
          to { opacity: 0; transform: translateX(20px); }
        }
      `;
      document.head.appendChild(style);
    }

    function showToast(message, type = "info", action = null) {
      let container = document.getElementById("aceable-toast-container");
      if (!container) {
        container = document.createElement("div");
        container.id = "aceable-toast-container";
        container.className = "aceable-toast-container";
        document.body.appendChild(container);
      }

      const toast = document.createElement("div");
      toast.className = `aceable-toast ${type}`;
      
      let icon = "ℹ️";
      if (type === "success") icon = "✅";
      if (type === "error") icon = "❌";

      let html = `<span>${icon}</span><div class="aceable-toast-content">${message}</div>`;
      
      if (action) {
        html += `<button class="aceable-toast-action">${action.text}</button>`;
      }

      toast.innerHTML = html;
      container.appendChild(toast);

      if (action) {
        const btn = toast.querySelector(".aceable-toast-action");
        btn.addEventListener("click", () => {
          action.callback();
          removeToast();
        });
      }

      const removeToast = () => {
        toast.style.animation = "aceable-toast-out 0.3s forwards";
        setTimeout(() => toast.remove(), 300);
      };

      // Auto remove after 5 seconds unless it has an action
      if (!action) {
        setTimeout(removeToast, 5000);
      }
    }

    function checkForUpdates() {
      if (isCheckingUpdate) return;
      isCheckingUpdate = true;
      log("🔄 Checking for updates...", false, false);
      
      const btn = document.getElementById("settings-check-update");
      if (btn) {
          btn.disabled = true;
          btn.textContent = "Checking...";
      }

      GM_xmlhttpRequest({
        method: "GET",
        url: UPDATE_URL,
        onload: function(response) {
          isCheckingUpdate = false;
          if (btn) {
              btn.disabled = false;
              btn.textContent = "Check for Updates";
          }

          if (response.status !== 200) {
            log(`❌ Update check failed: HTTP ${response.status}`, true);
            showToast(`Update check failed (HTTP ${response.status})`, "error");
            return;
          }

          const remoteVersionMatch = response.responseText.match(/@version\s+([\d.]+)/);
          if (!remoteVersionMatch) {
            log("❌ Could not parse remote version", true);
            showToast("Could not parse remote version", "error");
            return;
          }

          const remoteVersion = remoteVersionMatch[1];
          const localVersion = GM_info.script.version;

          if (remoteVersion !== localVersion) {
             const msg = `Update available! v${remoteVersion}`;
             log(`✨ ${msg}`);
             showToast(msg, "success", {
               text: "Update Now",
               callback: () => window.open("https://greasyfork.org/en/scripts/556441-aceable-autoanswer-with-gemini-ai", '_blank')
             });
          } else {
            log("✅ You are on the latest version.");
            showToast(`You are on the latest version (${localVersion})`, "success");
          }
        },
        onerror: function(err) {
          isCheckingUpdate = false;
          if (btn) {
              btn.disabled = false;
              btn.textContent = "Check for Updates";
          }
          log("❌ Update check error", true);
          console.error(err);
          showToast("Network error checking for updates", "error");
        }
      });
    }

    function createSettingsMenu() {
      injectSettingsCSS();

      // Check if menu is already open
      const existingOverlay = document.getElementById("aceable-settings-overlay");
      if (existingOverlay) {
        existingOverlay.remove();
        settingsMenuOpen = false;
        return;
      }

      settingsMenuOpen = true;
      const overlay = document.createElement("div");
      overlay.id = "aceable-settings-overlay";
      overlay.className = "aceable-settings-overlay";

      overlay.innerHTML = `
        <div class="aceable-settings-modal">
          <div class="aceable-settings-sidebar">
            <div class="aceable-settings-sidebar-header">
              <span>⚙️ Settings</span>
            </div>
            <button class="aceable-nav-item active" data-tab="general">General</button>
            <button class="aceable-nav-item" data-tab="verification">Verification</button>
          </div>
          
          <div class="aceable-settings-content">
            <!-- General Tab -->
            <div id="tab-general" class="aceable-tab-content active">
              <div class="aceable-toggle-row">
                <div>
                  <div class="aceable-toggle-label">Script Enabled</div>
                  <div class="aceable-helper">Master switch to enable/disable all automation</div>
                </div>
                <label class="aceable-switch">
                  <input type="checkbox" id="setting-enabled" ${config.scriptEnabled ? "checked" : ""}>
                  <span class="aceable-slider"></span>
                </label>
              </div>

              <div class="aceable-toggle-row">
                <div>
                  <div class="aceable-toggle-label">Use AI for Answers</div>
                  <div class="aceable-helper">Enable Gemini AI to answer questions</div>
                </div>
                <label class="aceable-switch">
                  <input type="checkbox" id="setting-use-ai" ${config.useAI ? "checked" : ""}>
                  <span class="aceable-slider"></span>
                </label>
              </div>

              <div class="aceable-form-group">
                <label class="aceable-label">Gemini API Key</label>
                <input type="text" class="aceable-input" id="setting-api-key" 
                       value="${config.geminiApiKey}" placeholder="YOUR_API_KEY_HERE">
                <div class="aceable-helper">
                  Get free key at: <a href="https://aistudio.google.com/app/apikey" target="_blank" style="color: #0f0;">aistudio.google.com</a>
                </div>
              </div>

              <div class="aceable-form-group" style="margin-top: 20px; border-top: 1px solid #333; padding-top: 20px;">
                  <button id="settings-check-update" class="aceable-btn aceable-btn-secondary" style="width: 100%;">Check for Updates</button>
                  <div class="aceable-helper" style="text-align: center;">
                      Current Version: ${GM_info.script.version}
                  </div>
              </div>
            </div>

            <!-- Verification Tab -->
            <div id="tab-verification" class="aceable-tab-content">
              <div style="margin-bottom: 20px; padding: 10px; background: #333; border-radius: 6px; font-size: 13px; color: #ddd;">
                ℹ️ <strong>Identity Verification:</strong> Aceable may ask security questions based on your profile. Fill these out so the AI can auto-answer them.
              </div>
              
              <div class="aceable-grid-2">
                <div class="aceable-form-group">
                  <label class="aceable-label">First Name</label>
                  <input type="text" class="aceable-input" id="setting-first-name" value="${config.firstName || ''}">
                </div>
                <div class="aceable-form-group">
                  <label class="aceable-label">Last Name</label>
                  <input type="text" class="aceable-input" id="setting-last-name" value="${config.lastName || ''}">
                </div>
              </div>

              <div class="aceable-form-group">
                <label class="aceable-label">Phone Number</label>
                <input type="text" class="aceable-input" id="setting-phone" value="${config.phoneNumber || ''}" placeholder="555-555-5555">
              </div>

              <div class="aceable-form-group">
                <label class="aceable-label">Email</label>
                <input type="text" class="aceable-input" id="setting-email" value="${config.email || ''}">
              </div>

              <div class="aceable-form-group">
                <label class="aceable-label">Birth Date</label>
                <input type="text" class="aceable-input" id="setting-birthdate" value="${config.birthDate || ''}" placeholder="Month DD, YYYY">
              </div>

              <div class="aceable-form-group">
                <label class="aceable-label">Address</label>
                <input type="text" class="aceable-input" id="setting-address" value="${config.address || ''}">
              </div>

              <div class="aceable-grid-2">
                <div class="aceable-form-group">
                  <label class="aceable-label">City</label>
                  <input type="text" class="aceable-input" id="setting-city" value="${config.city || ''}">
                </div>
                <div class="aceable-form-group">
                  <label class="aceable-label">State</label>
                  <input type="text" class="aceable-input" id="setting-state" value="${config.state || ''}">
                </div>
              </div>

              <div class="aceable-form-group">
                <label class="aceable-label">Zip Code</label>
                <input type="text" class="aceable-input" id="setting-zip" value="${config.zipCode || ''}">
              </div>
            </div>

            <div class="aceable-settings-footer">
              <button id="settings-cancel" class="aceable-btn aceable-btn-secondary">Cancel</button>
              <button id="settings-save" class="aceable-btn aceable-btn-primary">Save Changes</button>
            </div>
          </div>
        </div>
      `;

      document.body.appendChild(overlay);

      // Tab Switching Logic
      const navItems = overlay.querySelectorAll('.aceable-nav-item');
      const tabContents = overlay.querySelectorAll('.aceable-tab-content');

      navItems.forEach(item => {
        item.addEventListener('click', () => {
          // Remove active class from all
          navItems.forEach(n => n.classList.remove('active'));
          tabContents.forEach(t => t.classList.remove('active'));

          // Add active class to clicked
          item.classList.add('active');
          const tabId = `tab-${item.dataset.tab}`;
          document.getElementById(tabId).classList.add('active');
        });
      });

      // Event handlers
      const closeMenu = () => {
        overlay.remove();
        settingsMenuOpen = false;
      };

      document.getElementById("settings-check-update").addEventListener("click", checkForUpdates);

      document.getElementById("settings-save").addEventListener("click", () => {
        config.scriptEnabled = document.getElementById("setting-enabled").checked;
        config.useAI = document.getElementById("setting-use-ai").checked;
        config.geminiApiKey = document.getElementById("setting-api-key").value.trim();
        config.aiModel = "gemini-2.5-flash-lite";

        // Save profile data
        config.firstName = document.getElementById("setting-first-name").value.trim();
        config.lastName = document.getElementById("setting-last-name").value.trim();
        config.phoneNumber = document.getElementById("setting-phone").value.trim();
        config.email = document.getElementById("setting-email").value.trim();
        config.address = document.getElementById("setting-address").value.trim();
        config.city = document.getElementById("setting-city").value.trim();
        config.state = document.getElementById("setting-state").value.trim();
        config.zipCode = document.getElementById("setting-zip").value.trim();
        config.birthDate = document.getElementById("setting-birthdate").value.trim();

        saveConfig(config);
        log("⚙️ Settings saved!", false, true);
        closeMenu();

        // Reload if script was re-enabled
        if (config.scriptEnabled) {
          log("✅ Script enabled - resuming automation");
        } else {
          log("⏸️ Script disabled - paused");
        }
      });

      document.getElementById("settings-cancel").addEventListener("click", closeMenu);

      // Close on overlay click
      overlay.addEventListener("click", (e) => {
        if (e.target === overlay) {
          closeMenu();
        }
      });

      // Close on Escape key
      const escapeHandler = (e) => {
        if (e.key === "Escape") {
          closeMenu();
          document.removeEventListener("keydown", escapeHandler);
        }
      };
      document.addEventListener("keydown", escapeHandler);
    }

    // Keyboard shortcut listener (Ctrl+Shift+A / Cmd+Shift+A)
    document.addEventListener("keydown", (e) => {
      const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
      const modifierKey = isMac ? e.metaKey : e.ctrlKey;

      if (modifierKey && e.shiftKey && e.key.toLowerCase() === "a") {
        e.preventDefault();
        createSettingsMenu();
      }
    });

    // --- Prevent Background Tab Throttling ---
    let audioContext;
    let oscillator;
    let gainNode;

    try {
      audioContext = new (window.AudioContext || window.webkitAudioContext)();
      oscillator = audioContext.createOscillator();
      gainNode = audioContext.createGain();
      gainNode.gain.value = 0; // Silent
      oscillator.connect(gainNode);
      gainNode.connect(audioContext.destination);
      
      // Try to start oscillator - may be suspended until user interaction
      try {
        oscillator.start();
        debug("[Aceable Script] Background mode active");
      } catch (startErr) {
        // Oscillator may not start if AudioContext is suspended
        // It will be resumed on first user interaction via keepAlive()
        debug("[Aceable Script] Background mode initialized (will activate on user interaction)");
      }
    } catch (err) {
      console.warn("[Aceable Script] Could not enable background mode:", err);
    }

    // --- Debug Panel ---
    const panel = document.createElement("div");
    panel.style.cssText = `
      position: fixed;
      bottom: 10px;
      right: 10px;
      width: 350px;
      max-height: 250px;
      overflow-y: auto;
      background: rgba(0,0,0,0.9);
      color: #0f0;
      font-family: monospace;
      font-size: 11px;
      padding: 10px;
      border-radius: 8px;
      z-index: 99999;
      white-space: pre-line;
      border: 2px solid #0f0;
    `;
    panel.innerText = `[Aceable AI Script v2.1]\nAI: ${config.useAI ? "ENABLED" : "DISABLED"}\n`;
    document.body.appendChild(panel);

    // Add click handler to panel to open settings
    panel.style.cursor = "pointer";
    panel.title =
      "Click to open settings (or press Ctrl+Shift+A / Cmd+Shift+A)";
    panel.addEventListener("click", createSettingsMenu);

    let logs = [];

    function debug(...args) {
      if (config.debugLogging) {
        console.log(...args);
      }
    }

    function log(msg, isError = false, isWarning = false) {
      const timestamp = Date.now();
      const timeStr = new Date(timestamp).toLocaleTimeString();
      const line = `[${timeStr}] ${msg}`;
      logs.push({
        time: timestamp,
        text: line,
        error: isError,
        warning: isWarning,
      });
      
      if (isError) {
        console.error("[Aceable Script]", msg);
      } else if (config.debugLogging) {
        console[isWarning ? "warn" : "log"]("[Aceable Script]", msg);
      }
      
      refreshPanel();
    }

    function refreshPanel() {
      const cutoff = Date.now() - 5 * 60 * 1000; // 5 min
      logs = logs.filter((l) => l.time >= cutoff);
      const statusLine = config.scriptEnabled
        ? `AI: ${config.useAI ? "ENABLED" : "DISABLED"}`
        : `SCRIPT DISABLED`;
      panel.innerText =
        `[Aceable AI Script v2.1]\n${statusLine}\n` +
        logs.map((l) => l.text).join("\n");
      panel.scrollTop = panel.scrollHeight;
    }

    // --- Break Detection System ---
    function detectBreakPopup() {
      // Look for the break notification popup
      const bodyText = document.body.innerText || "";

      // Check for daily limit lock (12 hours)
      const dailyLimitRegex =
        /course is locked.*daily limit.*unlock in (\d+) hour/i;
      const dailyMatch = bodyText.match(dailyLimitRegex);

      if (dailyMatch) {
        const hours = parseInt(dailyMatch[1]);
        if (!isOnBreak) {
          isOnBreak = true;
          breakEndTime = Date.now() + hours * 60 * 60 * 1000;
          breakLoggedOnce = false;
          log(
            `🔒 DAILY LIMIT REACHED: ${hours} hours until unlock`,
            false,
            true,
          );
          startBreakCountdown();
        }
        return true;
      }

      // Check for break/lock message (minutes)
      const breakRegex =
        /course is locked.*required to take a break.*resume in (\d+) minute/i;
      const match = bodyText.match(breakRegex);

      if (match) {
        const minutes = parseInt(match[1]);
        if (!isOnBreak) {
          isOnBreak = true;
          breakEndTime = Date.now() + minutes * 60 * 1000;
          breakLoggedOnce = false;
          log(`⏸️ BREAK DETECTED: ${minutes} minutes required`, false, true);
          startBreakCountdown();
        }
        return true;
      }

      // Check if break is over
      if (isOnBreak && breakEndTime && Date.now() >= breakEndTime) {
        isOnBreak = false;
        breakEndTime = null;
        breakLoggedOnce = false;
        log("✅ Break over - resuming automation", false, true);
        if (breakCheckInterval) {
          clearInterval(breakCheckInterval);
          breakCheckInterval = null;
        }
        // Click continue learning button after break
        handleContinueLearningButton();
      }

      return isOnBreak;
    }

    // --- Verification Popup System ---
    function detectVerificationPopup() {
        const verifyModal = document.querySelector('.gritDialogScreen--verifyMultichoice');
        if (!verifyModal) return false;

        const questionHeader = verifyModal.querySelector('h1[data-test="header"]');
        if (!questionHeader) return false;

        const questionText = questionHeader.innerText.trim();
        log(`🔒 Verification Question Detected: "${questionText}"`);

        // Define matchers for different profile fields
        // The question format is usually "Fill in the blank: My [field] is [value with blank]"
        // Example: "Fill in the blank: My phone number is 513-399-92_6"
        // Example: "Fill in the blank: My birth date is August 15, 20__."
        
        const matchers = [
            { field: 'phoneNumber', pattern: /phone number/i },
            { field: 'email', pattern: /email/i },
            { field: 'firstName', pattern: /first name/i },
            { field: 'lastName', pattern: /last name/i },
            { field: 'address', pattern: /address/i },
            { field: 'city', pattern: /city/i },
            { field: 'state', pattern: /state/i },
            { field: 'zipCode', pattern: /zip code/i },
            { field: 'birthDate', pattern: /birth date/i }
        ];

        let targetField = null;
        for (const m of matchers) {
            if (m.pattern.test(questionText)) {
                targetField = m.field;
                break;
            }
        }

        if (!targetField) {
            log("⚠️ Could not identify profile field for verification question", true);
            return false;
        }

        const userValue = config[targetField];
        if (!userValue) {
            log(`⚠️ Missing profile data for '${targetField}' - cannot auto-answer`, true);
            return false;
        }

        // Find the missing part in the question text if possible, or just look for the answer options
        // The question usually contains the partial value. We need to find which option completes it.
        // However, simply checking which option is contained in the user's full value is usually sufficient and robust.
        
        const options = Array.from(verifyModal.querySelectorAll('button[data-test="popupAnswer"]'));
        log(`Found ${options.length} options for verification`);

        for (const btn of options) {
            const optionText = btn.innerText.trim();
            // Check if the option text is part of the user's value
            // We handle case insensitivity
            if (userValue.toLowerCase().includes(optionText.toLowerCase())) {
                log(`✅ Found matching verification answer: "${optionText}" (matches "${userValue}")`);
                
                // Add a small delay to simulate human reaction
                setTimeout(() => {
                    btn.click();
                    log("Clicked verification answer");
                }, 1500 + Math.random() * 1000);
                
                return true;
            }
        }

        log(`⚠️ No option matched user value "${userValue}"`, true);
        return false;
    }

    function handleContinueLearningButton() {
      const continueBtn = document.querySelector(
        '[data-test="continueLearningButton"]',
      );
      if (continueBtn) {
        log("Clicking Continue Learning button");
        continueBtn.click();
        // Re-check for break timer after clicking
        setTimeout(() => {
          detectBreakPopup();
        }, 1000);
      }
    }

    function startBreakCountdown() {
      if (breakCheckInterval) {
        clearInterval(breakCheckInterval);
      }

      breakCheckInterval = setInterval(() => {
        if (!breakEndTime) {
          clearInterval(breakCheckInterval);
          breakCheckInterval = null;
          return;
        }

        const remaining = breakEndTime - Date.now();
        if (remaining <= 0) {
          isOnBreak = false;
          breakEndTime = null;
          clearInterval(breakCheckInterval);
          breakCheckInterval = null;
          log("✅ Break over - resuming automation", false, true);
        } else {
          const hours = Math.floor(remaining / 3600000);
          const mins = Math.floor((remaining % 3600000) / 60000);
          const secs = Math.floor((remaining % 60000) / 1000);

          if (hours > 0) {
            log(`⏳ Time remaining: ${hours}h ${mins}m`, false, true);
          } else {
            log(`⏳ Time remaining: ${mins}m ${secs}s`, false, true);
          }
        }
      }, 10000); // Update every 10 seconds
    }

    // --- Gemini AI Integration (FIXED WITH COMPREHENSIVE LOGGING) ---
    async function askGeminiForOrder(question, items) {
      debug("=== AI DEBUG: askGeminiForOrder called ===");
      debug("AI Config:", {
        useAI: config.useAI,
        hasApiKey: config.geminiApiKey !== "YOUR_API_KEY_HERE",
        apiKeyLength: config.geminiApiKey.length,
        model: config.aiModel,
      });

      if (!config.useAI) {
        debug("AI DEBUG: AI is disabled in config");
        log("AI disabled in settings", false, true);
        return null;
      }

      if (config.geminiApiKey === "YOUR_API_KEY_HERE" || !config.geminiApiKey) {
        debug("AI DEBUG: No API key configured");
        log("⚠️ No API key configured", true);
        return null;
      }

      debug("AI DEBUG: Question:", question);
      debug("AI DEBUG: Items to order:", items);

      const prompt = `You are helping with a driving education course question that requires ordering steps in the correct sequence.

Question: ${question}

Items to order (currently in random order):
${items.map((item, i) => `${i + 1}. ${item}`).join("\n")}

Please respond with ONLY a comma-separated list of numbers representing the correct order from top to bottom. For example, if the correct order is: item 3, then item 1, then item 4, then item 2, respond with: 3,1,4,2

Do not include any explanation or other text.`;

      debug("AI DEBUG: Full prompt sent to AI:", prompt);

      try {
        log("🤖 Asking Gemini AI for correct order...");
        debug("AI DEBUG: Making API request...");

        const response = await new Promise((resolve, reject) => {
          const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${config.aiModel}:generateContent`;
          debug("AI DEBUG: API URL:", apiUrl);

          const requestBody = {
            contents: [
              {
                parts: [{ text: prompt }],
              },
            ],
            tools: [
              {
                google_search: {},
              },
            ],
            generationConfig: {
              temperature: 0.1,
              maxOutputTokens: 2000,
            },
          };

          debug("AI DEBUG: Request body:", JSON.stringify(requestBody, null, 2));

          GM_xmlhttpRequest({
            method: "POST",
            url: apiUrl,
            headers: {
              "Content-Type": "application/json",
              "x-goog-api-key": config.geminiApiKey,
            },
            data: JSON.stringify(requestBody),
            onload: (response) => {
              debug("AI DEBUG: Response received");
              debug("AI DEBUG: Status:", response.status);
              debug("AI DEBUG: Response text:", response.responseText);
              resolve(response);
            },
            onerror: (error) => {
              console.error("AI DEBUG: Request error:", error);
              reject(error);
            },
            ontimeout: () => {
              console.error("AI DEBUG: Request timeout");
              reject(new Error("Request timeout"));
            },
            timeout: 15000,
          });
        });

        if (response.status !== 200) {
          console.error("AI DEBUG: Non-200 status:", response.status);
          console.error("AI DEBUG: Error response:", response.responseText);
          throw new Error(
            `API error: ${response.status} - ${response.responseText}`,
          );
        }

        const data = JSON.parse(response.responseText);
        debug("AI DEBUG: Parsed response data:", JSON.stringify(data, null, 2));

        const candidate = data.candidates?.[0];
        if (!candidate) {
          throw new Error("No candidate in AI response");
        }

        // Check if response was truncated
        if (candidate.finishReason === "MAX_TOKENS") {
          console.warn("AI DEBUG: Response was truncated (MAX_TOKENS)");
          log("⚠️ AI response was truncated - increasing token limit", false, true);
        }

        const aiResponse =
          candidate.content?.parts?.[0]?.text?.trim();

        debug("AI DEBUG: Extracted AI response:", aiResponse);

        if (!aiResponse) {
          console.error("AI DEBUG: No response text from AI");
          console.error("AI DEBUG: Finish reason:", candidate.finishReason);
          console.error("AI DEBUG: Data structure:", data);
          throw new Error(`No response from AI (finishReason: ${candidate.finishReason})`);
        }

        // Parse comma-separated order (e.g., "3,1,4,2" or "3, 1, 4, 2")
        const orderMatch = aiResponse.match(/[\d,\s]+/);
        if (!orderMatch) {
          throw new Error(`Could not parse order from AI response: ${aiResponse}`);
        }

        const order = orderMatch[0]
          .split(",")
          .map((n) => parseInt(n.trim()))
          .filter((n) => !isNaN(n) && n >= 1 && n <= items.length);

        debug("AI DEBUG: Parsed order:", order);

        if (order.length !== items.length) {
          throw new Error(`Invalid order length: ${order.length} (expected ${items.length})`);
        }

        // Validate that all numbers 1..items.length are present
        const sortedOrder = [...order].sort((a, b) => a - b);
        for (let i = 0; i < items.length; i++) {
          if (sortedOrder[i] !== i + 1) {
            throw new Error(`Invalid order: missing number ${i + 1}`);
          }
        }

        log(`✅ AI determined order: ${order.join(", ")}`);
        return order;
      } catch (err) {
        console.error("AI DEBUG: Exception caught:", err);
        console.error("AI DEBUG: Error stack:", err.stack);
        log(`❌ AI Error: ${err.message}`, true);
        return null;
      }
    }

    async function askGeminiForBlank(question, blankCount) {
      debug("=== AI DEBUG: askGeminiForBlank called ===");
      debug("AI Config:", {
        useAI: config.useAI,
        hasApiKey: config.geminiApiKey !== "YOUR_API_KEY_HERE",
        apiKeyLength: config.geminiApiKey.length,
        model: config.aiModel,
      });

      if (!config.useAI) {
        log("AI disabled in settings", false, true);
        return null;
      }

      if (config.geminiApiKey === "YOUR_API_KEY_HERE" || !config.geminiApiKey) {
        log("⚠️ No API key configured", true);
        return null;
      }

      const prompt = `You are helping with a driving education course fill-in-the-blank question.
Question: ${question}

Fill in the missing word${blankCount > 1 ? "s" : ""} where each blank is represented by "____".
${
  blankCount === 1
    ? `Respond with ONLY the missing word or short phrase that correctly completes the blank. Do not include quotes, punctuation, capitalization changes, or any explanation.`
    : `Respond with the missing words or short phrases for each of the ${blankCount} blanks, in order. Provide each answer on its own line with no numbering, punctuation, or extra text.`
}
Keep answers concise and directly usable in the blanks.`;

      debug("AI DEBUG: Full prompt sent to AI:", prompt);

      try {
        log("🤖 Asking Gemini AI for blank answer...");
        debug("AI DEBUG: Making API request...");

        const response = await new Promise((resolve, reject) => {
          const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${config.aiModel}:generateContent`;
          debug("AI DEBUG: API URL:", apiUrl);

          const requestBody = {
            contents: [
              {
                parts: [{ text: prompt }],
              },
            ],
            tools: [
              {
                google_search: {},
              },
            ],
            generationConfig: {
              temperature: 0.05,
              maxOutputTokens: Math.max(120, blankCount * 40),
            },
          };

          debug(
            "AI DEBUG: Request body:",
            JSON.stringify(requestBody, null, 2),
          );

          GM_xmlhttpRequest({
            method: "POST",
            url: apiUrl,
            headers: {
              "Content-Type": "application/json",
              "x-goog-api-key": config.geminiApiKey,
            },
            data: JSON.stringify(requestBody),
            onload: (response) => {
              debug("AI DEBUG: Response received");
              debug("AI DEBUG: Status:", response.status);
              debug("AI DEBUG: Response text:", response.responseText);
              resolve(response);
            },
            onerror: (error) => {
              console.error("AI DEBUG: Request error:", error);
              reject(error);
            },
            ontimeout: () => {
              console.error("AI DEBUG: Request timeout");
              reject(new Error("Request timeout"));
            },
            timeout: 15000,
          });
        });

        if (response.status !== 200) {
          console.error("AI DEBUG: Non-200 status:", response.status);
          console.error("AI DEBUG: Error response:", response.responseText);
          throw new Error(
            `API error: ${response.status} - ${response.responseText}`,
          );
        }

        const data = JSON.parse(response.responseText);
        debug(
          "AI DEBUG: Parsed response data:",
          JSON.stringify(data, null, 2),
        );

        const candidate = data.candidates?.[0];
        if (!candidate) {
          throw new Error("No candidate in AI response");
        }

        const aiResponse = candidate.content?.parts
          ?.map((part) => part.text?.trim())
          .filter(Boolean)
          .join("\n")
          .trim();

        debug("AI DEBUG: Extracted AI response:", aiResponse);

        if (!aiResponse) {
          throw new Error("No response text from AI");
        }

        const lines =
          aiResponse
            .split(/\r?\n/)
            .map((line) =>
              line
                .trim()
                .replace(/^[-*\d.)\s]+/, "")
                .replace(/^["'“”‘’]|["'“”‘’]$/g, "")
                .replace(/[.?!]+$/g, "")
                .trim(),
            )
            .filter(Boolean) || [];

        if (blankCount === 1) {
          const single =
            lines[0] || aiResponse.replace(/[.?!]+$/g, "").trim();
          if (!single) {
            throw new Error("AI did not return a usable answer");
          }
          return [single];
        }

        if (lines.length !== blankCount) {
          console.warn(
            "AI DEBUG: Expected",
            blankCount,
            "answers but received",
            lines.length,
          );
          throw new Error("AI returned unexpected number of blank answers");
        }

        return lines;
      } catch (err) {
        console.error("AI DEBUG: Exception caught:", err);
        console.error("AI DEBUG: Error stack:", err.stack);
        log(`❌ AI Error: ${err.message}`, true);
        return null;
      }
    }

    async function askGemini(question, answerOptions, options = {}) {
      const { expectMultiple = false } = options || {};
      debug("=== AI DEBUG: askGemini called ===");
      debug("AI Config:", {
        useAI: config.useAI,
        hasApiKey: config.geminiApiKey !== "YOUR_API_KEY_HERE",
        apiKeyLength: config.geminiApiKey.length,
        model: config.aiModel,
      });

      if (!config.useAI) {
        debug("AI DEBUG: AI is disabled in config");
        log("AI disabled in settings", false, true);
        return null;
      }

      if (config.geminiApiKey === "YOUR_API_KEY_HERE" || !config.geminiApiKey) {
        debug("AI DEBUG: No API key configured");
        log("⚠️ No API key configured", true);
        return null;
      }

      debug("AI DEBUG: Question:", question);
      debug("AI DEBUG: Answer options:", answerOptions);

      const prompt = (() => {
        const base = `You are helping with a driving education course question.
Question: ${question}

Available answers:
${answerOptions.map((opt, i) => `${i + 1}. ${opt}`).join("\n")}
`;
        if (expectMultiple) {
          return `${base}
One or more of the answers may be correct. Respond with ONLY the numbers of all correct answers, separated by commas in ascending order (for example: "1,3,4"). Do not include any explanation or other text. If no answers are correct, respond with "NONE".`;
        }
        return `${base}
Please respond with ONLY the number (1, 2, 3, or 4) of the correct answer. Do not include any explanation or other text.`;
      })();

      debug("AI DEBUG: Full prompt sent to AI:", prompt);

      try {
        log(
          expectMultiple
            ? "🤖 Asking Gemini AI for all correct answers..."
            : "🤖 Asking Gemini AI...",
        );
        debug("AI DEBUG: Making API request...");

        const response = await new Promise((resolve, reject) => {
          const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${config.aiModel}:generateContent`;
          debug("AI DEBUG: API URL:", apiUrl);

          const requestBody = {
            contents: [
              {
                parts: [{ text: prompt }],
              },
            ],
            tools: [
              {
                google_search: {},
              },
            ],
            generationConfig: {
              temperature: 0.1,
              maxOutputTokens: 1000,
            },
          };

          debug("AI DEBUG: Request body:", JSON.stringify(requestBody, null, 2));

          GM_xmlhttpRequest({
            method: "POST",
            url: apiUrl,
            headers: {
              "Content-Type": "application/json",
              "x-goog-api-key": config.geminiApiKey,
            },
            data: JSON.stringify(requestBody),
            onload: (response) => {
              debug("AI DEBUG: Response received");
              debug("AI DEBUG: Status:", response.status);
              debug("AI DEBUG: Response text:", response.responseText);
              resolve(response);
            },
            onerror: (error) => {
              console.error("AI DEBUG: Request error:", error);
              reject(error);
            },
            ontimeout: () => {
              console.error("AI DEBUG: Request timeout");
              reject(new Error("Request timeout"));
            },
            timeout: 15000, // Increased timeout to 15 seconds
          });
        });

        if (response.status !== 200) {
          console.error("AI DEBUG: Non-200 status:", response.status);
          console.error("AI DEBUG: Error response:", response.responseText);
          throw new Error(
            `API error: ${response.status} - ${response.responseText}`,
          );
        }

        const data = JSON.parse(response.responseText);
        debug("AI DEBUG: Parsed response data:", JSON.stringify(data, null, 2));

        const candidate = data.candidates?.[0];
        if (!candidate) {
          throw new Error("No candidate in AI response");
        }

        // Check if response was truncated
        if (candidate.finishReason === "MAX_TOKENS") {
          console.warn("AI DEBUG: Response was truncated (MAX_TOKENS)");
          log("⚠️ AI response was truncated - increasing token limit", false, true);
        }

        const aiResponse =
          candidate.content?.parts?.[0]?.text?.trim();

        debug("AI DEBUG: Extracted AI response:", aiResponse);

        if (!aiResponse) {
          console.error("AI DEBUG: No response text from AI");
          console.error("AI DEBUG: Finish reason:", candidate.finishReason);
          console.error("AI DEBUG: Data structure:", data);
          throw new Error(`No response from AI (finishReason: ${candidate.finishReason})`);
        }

        // Extract numbers from response - handle multiple answers if requested
        const numberMatches = Array.from(aiResponse.matchAll(/(\d+)/g)).map(
          (match) => parseInt(match[1], 10),
        );
        const uniqueNumbers = [...new Set(numberMatches)].filter(
          (num) => num >= 1 && num <= answerOptions.length,
        );
        debug("AI DEBUG: Number matches:", uniqueNumbers);

        function fallbackAnswersFromText() {
          const lowerResponse = aiResponse.toLowerCase();
          return answerOptions.filter((opt) =>
            lowerResponse.includes(opt.toLowerCase()),
          );
        }

        if (expectMultiple) {
          let selectedAnswers = uniqueNumbers.map(
            (num) => answerOptions[num - 1],
          );

          if (selectedAnswers.length === 0) {
            selectedAnswers = fallbackAnswersFromText();
          }

          if (
            selectedAnswers.length === 0 &&
            aiResponse.trim().toUpperCase() === "NONE"
          ) {
            log("⚠️ AI indicated no answers are correct", false, true);
            return [];
          }

          if (selectedAnswers.length === 0) {
            console.error(
              "AI DEBUG: Could not determine answers from response:",
              aiResponse,
            );
            throw new Error(
              `Could not parse multiple answers from AI response: ${aiResponse}`,
            );
          }

          const truncatedList = selectedAnswers
            .map((ans) => ans.substring(0, 50))
            .join(", ");
          log(
            `✅ AI selected ${selectedAnswers.length} answer${
              selectedAnswers.length === 1 ? "" : "s"
            }: ${truncatedList}...`,
          );
          return selectedAnswers;
        }

        const answerNum = uniqueNumbers[0];
        if (!answerNum) {
          const fallback = fallbackAnswersFromText()[0];
          if (fallback) {
            log(`✅ AI selected: ${fallback.substring(0, 50)}...`);
            return fallback;
          }
          console.error(
            "AI DEBUG: Could not extract number from response:",
            aiResponse,
          );
          throw new Error(
            `Could not parse number from AI response: ${aiResponse}`,
          );
        }

        const selectedAnswer = answerOptions[answerNum - 1];
        debug("AI DEBUG: Selected answer:", selectedAnswer);
        log(`✅ AI selected: ${answerNum}. ${selectedAnswer.substring(0, 50)}...`);
        return selectedAnswer;
      } catch (err) {
        console.error("AI DEBUG: Exception caught:", err);
        console.error("AI DEBUG: Error stack:", err.stack);
        log(`❌ AI Error: ${err.message}`, true);
        return null;
      }
    }

    // --- Main Loop ---
    setInterval(async () => {
      try {
        // Check if script is disabled
        if (!config.scriptEnabled) {
          return; // Skip all actions if disabled
        }

        // Check for break popup first
        if (detectBreakPopup()) {
          if (!breakLoggedOnce) {
            log("⏸️ On break - paused", false, true);
            breakLoggedOnce = true;
          }
          return; // Skip all actions during break
        }

        capturePopupQuestionResults();

        const nextArrow = document.getElementById("arrow-next");
        if (nextArrow && !nextArrow.disabled) {
          log("Clicking next arrow");
          nextArrow.click();
        }

        // // Video handling
        // const video = document.querySelector("video");
        // if (video && video.paused) {
        //   const playButton = document.querySelector(".vjs-big-play-button");
        //   if (playButton) {
        //     log("Playing video");
        //     playButton.click();
        //   }
        // }
        // Video handling
        const video = document.querySelector("video");
        if (video) {
          // Skip video by seeking to end minus 1 second, then play
          if (video.duration && !isNaN(video.duration) && video.duration > 0) {
            const targetTime = video.duration - 1;
            // If video hasn't been skipped yet, skip to near the end
            if (video.currentTime < targetTime - 0.5) {
              video.currentTime = targetTime;
              log("⏩ Video skipped to end (1 second remaining)");
            }
          }
          
          // Speed up video (2x speed)
          if (video.playbackRate !== 2) {
            video.playbackRate = 2;
            log("⚡ Video speed set to 2x");
          }
          
          // Play if paused
          if (video.paused) {
            const playButton = document.querySelector(".vjs-big-play-button");
            if (playButton) {
              log("Playing video");
              playButton.click();
            } else {
              video.play();
            }
          }
        }

        const playBtn = document.getElementById("play-btn");
        if (playBtn) {
          log("Clicking Play Facts");
          playBtn.click();
        }

        const continueButton = document.querySelector(
          ".continue-learning-button",
        );
        if (continueButton) {
          log("Clicking Continue");
          continueButton.click();
        }

        const nextChapterButton = document.querySelector(
          '[data-test="continueButton"]',
        );
        if (nextChapterButton) {
          const computedStyle = window.getComputedStyle(nextChapterButton);
          const isVisible =
            nextChapterButton.offsetParent !== null &&
            computedStyle.display !== "none" &&
            computedStyle.visibility !== "hidden" &&
            computedStyle.opacity !== "0";
          const isEnabled =
            !nextChapterButton.disabled &&
            !nextChapterButton.hasAttribute("disabled") &&
            !nextChapterButton.classList.contains("disabled");
          if (isVisible && isEnabled) {
            log("Clicking Next Chapter");
            nextChapterButton.click();
          }
        }

        // Also check for continue learning button (after break)
        const continueLearningBtn = document.querySelector(
          '[data-test="continueLearningButton"]',
        );
        if (continueLearningBtn) {
          log("Clicking Continue Learning");
          continueLearningBtn.click();
        }

        const resultsContinueBtn = document.getElementById(
          "results-continue-btn",
        );
        if (resultsContinueBtn) {
          log("Clicking Results Continue");
          resultsContinueBtn.click();
        }

        const assessmentButton = document.querySelector(
          "[data-test='startAssessmentButton']",
        );
        if (assessmentButton) {
          log("Starting Assessment");
          assessmentButton.click();
        }

        const continueModalButton = document.querySelector(
          "[data-test='modalPrimaryButton']",
        );
        if (continueModalButton) {
          log("Clicking modal Continue");
          continueModalButton.click();
        }

        const continueTestButton = document.querySelector(
          "[data-test='continueTest']",
        );
        if (continueTestButton) {
          log("Continuing Test");
          continueTestButton.click();
        }

        await autoAnswerCategoryQuestions();
        await autoAnswerPlayFactQuestions();
        await autoAnswerBlankQuestions();
        await autoAnswerTestQuestionsWithAI();
        await autoAnswerSortableQuestions();
        saveReviewQuestions();
      } catch (err) {
        console.error("Script error:", err);
        log(`Script error: ${err.message}`, true);
      }
    }, 500);

    // --- Keep Alive ---
    function keepAlive() {
        // Run verification check
        detectVerificationPopup();
        
        // ... existing keepAlive logic ...
      document.dispatchEvent(new MouseEvent("mousemove"));
      document.dispatchEvent(new KeyboardEvent("keydown", { key: "Shift" }));
      if (audioContext && audioContext.state === "suspended") {
        audioContext.resume();
      }
      log("Keep alive ping");
    }
    setInterval(keepAlive, 120000);

    // --- Question Handlers ---
    const factsOfAFeatherLibrary = [
      {
        matchText:
          "To ride these vehicles, training classes, a license, vehicle registration, and a helmet are required.",
        keywords: [
          "training classes",
          "license",
          "vehicle registration",
          "helmet",
        ],
        category: "motorcycles",
      },
    ];

    const normalizeFoafText = (text) =>
      (text || "").replace(/\s+/g, " ").trim().toLowerCase();
    const normalizeCategoryLabel = (text) =>
      (text || "").replace(/\s+/g, " ").trim().toLowerCase();

    async function autoAnswerCategoryQuestions() {
      if (categoryQuestionProcessing) {
        return;
      }

      const categoryList = document.querySelector(".category-list");
      if (!categoryList) return;

      const descriptorEl =
        categoryList.querySelector(".categorize__descriptor") ||
        document.querySelector(".categorize__descriptor");

      const factTextRaw = descriptorEl?.innerText || "";
      const factText = factTextRaw.replace(/\s+/g, " ").trim();

      if (!factText) {
        const buttons = categoryList.querySelectorAll("button");
        for (let button of buttons) {
          while (!areAllDotsComplete(button)) {
            log("Completing category dots");
            button.click();
            await waitFor(400);
          }
        }
        return;
      }

      const now = Date.now();
      if (factText === lastFoafFact && now - lastFoafFactTime < 800) {
        return;
      }

      lastFoafFact = factText;
      lastFoafFactTime = now;
      categoryQuestionProcessing = true;

      try {
        const buttons = Array.from(categoryList.querySelectorAll("button"));
        if (buttons.length === 0) {
          return;
        }

        const normalizedFact = normalizeFoafText(factText);
        const storedQuestions =
          getStoredQuestions();
        const storageKey = `foaf:${normalizedFact}`;
        let targetCategory = storedQuestions[storageKey];

        if (!targetCategory) {
          const libraryEntry = factsOfAFeatherLibrary.find((entry) => {
            if (
              entry.matchText &&
              normalizeFoafText(entry.matchText) === normalizedFact
            ) {
              return true;
            }
            if (entry.keywords && entry.keywords.length > 0) {
              return entry.keywords.every((keyword) =>
                normalizedFact.includes(keyword.toLowerCase()),
              );
            }
            return false;
          });

          if (libraryEntry) {
            targetCategory = libraryEntry.category;
          }
        }

        if (!targetCategory) {
          debug("No FOAF mapping found for fact:", factText);
          return;
        }

        const normalizedTarget = normalizeCategoryLabel(targetCategory);
        let buttonToClick =
          buttons.find(
            (btn) =>
              normalizeCategoryLabel(btn.innerText) === normalizedTarget,
          ) ||
          buttons.find((btn) =>
            normalizeCategoryLabel(btn.innerText).includes(normalizedTarget),
          ) ||
          buttons.find((btn) =>
            normalizedTarget.includes(normalizeCategoryLabel(btn.innerText)),
          );

        if (!buttonToClick) {
          console.warn(
            "[FoAF] Could not find category button for target:",
            targetCategory,
          );
          return;
        }

        if (areAllDotsComplete(buttonToClick)) {
          debug(
            "FOAF target category already complete:",
            buttonToClick.innerText.replace(/\s+/g, " ").trim(),
          );
          return;
        }

        const buttonLabel = buttonToClick.innerText.replace(/\s+/g, " ").trim();
        log(`🪶 Categorizing fact using "${buttonLabel}"`);
        buttonToClick.click();

        storedQuestions[storageKey] = buttonLabel;
        saveStoredQuestions(storedQuestions);
      } finally {
        categoryQuestionProcessing = false;
      }
    }

    function areAllDotsComplete(button) {
      return Array.from(button.querySelectorAll(".bucket-dot")).every((dot) =>
        dot.classList.contains("complete"),
      );
    }

    async function autoAnswerPlayFactQuestions() {
      if (playFactProcessing) {
        return;
      }

      const answerButtonList = document.querySelector(
        ".gritCourseflowNode__answerButtonList",
      );
      if (!answerButtonList) return;

      playFactProcessing = true;

      try {
        // Get all answer buttons
        const answerButtons = answerButtonList.querySelectorAll("button");
        if (answerButtons.length === 0) return;

        // Check if already answered (button has selected class or is disabled)
        const alreadyAnswered = Array.from(answerButtons).some(
          (btn) => btn.classList.contains("selected") || btn.disabled,
        );

        if (alreadyAnswered) {
          // Already answered, just proceed to check and next
          await waitUntilCheckAnswerButtonIsEnabled();
          const checkAnswerButton =
            document.querySelector("[courseflow-forward]");
          if (checkAnswerButton) {
            log("Checking answer");
            checkAnswerButton.click();
          }
          const nextArrow = document.getElementById("arrow-next");
          if (nextArrow) {
            await waitFor(300);
            nextArrow.click();
          }
          return;
        }

        // Extract question text - search within the PlayFact container only
        let questionText = "";
        const parentContainer = answerButtonList.closest(".gritCourseflowNode");

        if (!parentContainer) {
          debug("No parent container found for PlayFact question");
          return;
        }

        // Exclude UI elements (settings menu, debug panel, etc.)
        const excludedSelectors = [
          "#aceable-settings-overlay",
          "#aceable-settings-overlay *",
          "[id*='aceable']",
          ".aceable-script-panel",
        ];

        // Try specific question selectors within the container first
        const questionSelectors = [
          ".gritCourseflowNode__question",
          ".question",
          "[data-test='question']",
          ".paragraph__question",
          "h1",
          "h2",
          "h3",
          "p",
        ];

        for (const selector of questionSelectors) {
          const questionEl = parentContainer.querySelector(selector);
          if (questionEl) {
            // Check if element is not in excluded areas
            let isExcluded = false;
            for (const exclSelector of excludedSelectors) {
              if (questionEl.closest(exclSelector)) {
                isExcluded = true;
                break;
              }
            }

            if (!isExcluded && questionEl.innerText.trim()) {
              const text = questionEl.innerText.trim();
              // Filter out very short text (likely not a question) and UI text
              if (
                text.length > 10 &&
                !text.includes("⚙️") &&
                !text.includes("Settings") &&
                !text.includes("Aceable Script")
              ) {
                questionText = text;
                break;
              }
            }
          }
        }

        // If no question text found, try to get it from the parent container
        // by extracting text that's not in answer buttons or excluded elements
        if (!questionText || questionText.length < 10) {
          // Clone the container to work with
          const clone = parentContainer.cloneNode(true);

          // Remove answer button list from clone
          const answerListClone =
            clone.querySelector(".gritCourseflowNode__answerButtonList");
          if (answerListClone) {
            answerListClone.remove();
          }

          // Remove any excluded elements
          excludedSelectors.forEach((selector) => {
            const excluded = clone.querySelector(selector);
            if (excluded) excluded.remove();
          });

          // Get all text and clean it up
          let allText = clone.innerText.trim();

          // Remove answer texts that might still be in there
          const answerTexts = Array.from(answerButtons).map((btn) =>
            btn.innerText.trim(),
          );
          answerTexts.forEach((ans) => {
            allText = allText.replace(ans, "").trim();
          });

          // Clean up common UI text patterns
          allText = allText
            .replace(/⚙️.*Settings.*/g, "")
            .replace(/Aceable Script.*/g, "")
            .replace(/\s+/g, " ")
            .trim();

          // Filter out very short or suspicious text
          if (
            allText.length > 10 &&
            !allText.includes("⚙️") &&
            !allText.includes("Settings")
          ) {
            questionText = allText;
          }
        }

        // Final validation - if question is still suspicious, skip AI
        if (
          !questionText ||
          questionText.length < 10 ||
          questionText.includes("⚙️") ||
          questionText.includes("Aceable Script Settings")
        ) {
          debug(
            "Could not extract valid question text, skipping PlayFact question",
          );
          questionText = "";
        }

        // Extract answer options
        const answerTexts = Array.from(answerButtons).map((btn) =>
          btn.innerText.trim(),
        );

        debug("=== PLAYFACT QUESTION DETECTED ===");
        debug("Question:", questionText || "(could not extract)");
        debug("Answer options:", answerTexts);

        // If we couldn't extract a valid question, skip AI and use fallback
        if (!questionText) {
          log(
            "⚠️ Could not extract PlayFact question text - using fallback",
            false,
            true,
          );
          // Fallback: click first option
          answerButtons[0].click();
          await waitUntilCheckAnswerButtonIsEnabled();
          const checkAnswerButton =
            document.querySelector("[courseflow-forward]");
          if (checkAnswerButton) {
            log("Checking answer");
            checkAnswerButton.click();
          }
          const nextArrow = document.getElementById("arrow-next");
          if (nextArrow) {
            await waitFor(300);
            nextArrow.click();
          }
          return;
        }

        // Try local storage first
        const storedQuestions =
          getStoredQuestions();

        if (storedQuestions[questionText]) {
          const correctAnswer = storedQuestions[questionText];
          debug("Found stored answer:", correctAnswer);

          const correctBtn = Array.from(answerButtons).find(
            (btn) => btn.innerText.trim() === correctAnswer.trim(),
          );

          if (correctBtn) {
            log(`📚 Using stored answer for PlayFact`);
            correctBtn.click();
            await waitUntilCheckAnswerButtonIsEnabled();
            const checkAnswerButton = document.querySelector(
              "[courseflow-forward]",
            );
            if (checkAnswerButton) {
              log("Checking answer");
              checkAnswerButton.click();
            }
            const nextArrow = document.getElementById("arrow-next");
            if (nextArrow) {
              await waitFor(300);
              nextArrow.click();
            }
            return;
          }
        }

        // Use AI if enabled
        if (config.useAI && questionText) {
          debug("\n" + "=".repeat(60));
          debug("🤖 AI ENABLED - PROCESSING PLAYFACT QUESTION");
          debug("=".repeat(60));
          debug("📝 QUESTION DETECTED:");
          debug("   " + questionText);
          debug("\n📋 POSSIBLE ANSWERS:");
          answerTexts.forEach((answer, index) => {
            debug(`   ${index + 1}. ${answer}`);
          });
          debug("\n🔄 Calling AI...");

          const aiAnswer = await askGemini(questionText, answerTexts);

          const aiAnswers = Array.isArray(aiAnswer)
            ? aiAnswer
            : aiAnswer
            ? [aiAnswer]
            : [];

          debug("\n✅ AI RESPONSE RECEIVED:");
          if (aiAnswers.length > 0) {
            aiAnswers.forEach((ans, idx) => {
              debug(`   Selected Answer ${idx + 1}: ${ans}`);
            });
          } else {
            debug("   ❌ No answer received (error occurred)");
          }
          debug("=".repeat(60) + "\n");

          if (aiAnswers.length > 0) {
            let aiBtn = null;

            for (const candidateAnswer of aiAnswers) {
              if (!candidateAnswer) continue;
              aiBtn = Array.from(answerButtons).find(
                (btn) => btn.innerText.trim() === candidateAnswer.trim(),
              );

              if (!aiBtn) {
                debug("No exact match, trying case-insensitive...");
                aiBtn = Array.from(answerButtons).find(
                  (btn) =>
                    btn.innerText.trim().toLowerCase() ===
                    candidateAnswer.trim().toLowerCase(),
                );
              }

              if (!aiBtn) {
                debug("No case-insensitive match, trying partial...");
                aiBtn = Array.from(answerButtons).find(
                  (btn) =>
                    btn.innerText.trim().includes(candidateAnswer.trim()) ||
                    candidateAnswer.trim().includes(btn.innerText.trim()),
                );
              }

              if (aiBtn) {
                break;
              }
            }

            if (aiBtn) {
              log(`🤖 AI answered PlayFact question`);
              aiBtn.click();

              // Cache the answer for next time
              storedQuestions[questionText] = aiBtn.innerText.trim();
              saveStoredQuestions(storedQuestions);

              await waitUntilCheckAnswerButtonIsEnabled();
              const checkAnswerButton = document.querySelector(
                "[courseflow-forward]",
              );
              if (checkAnswerButton) {
                log("Checking answer");
                checkAnswerButton.click();
              }
              const nextArrow = document.getElementById("arrow-next");
              if (nextArrow) {
                await waitFor(300);
                nextArrow.click();
              }
              return;
            } else {
              console.error("AI answer not found in button options!");
              console.error("AI returned:", aiAnswers);
              console.error("Available options:", answerTexts);
              log("⚠️ AI answer not found in PlayFact options", false, true);
            }
          }
        }

        // Fallback: if AI is disabled or failed, click first option
        if (!config.useAI) {
          log("⚠️ AI disabled - clicking first PlayFact option", false, true);
        } else {
          log("⚠️ AI failed - using fallback (first option)", false, true);
        }
        answerButtons[0].click();
        await waitUntilCheckAnswerButtonIsEnabled();
        const checkAnswerButton =
          document.querySelector("[courseflow-forward]");
        if (checkAnswerButton) {
          log("Checking answer");
          checkAnswerButton.click();
        }
        const nextArrow = document.getElementById("arrow-next");
        if (nextArrow) {
          await waitFor(300);
          nextArrow.click();
        }
      } finally {
        playFactProcessing = false;
      }
    }

    async function autoAnswerBlankQuestions() {
      debug("=== CHECKING FOR BLANK QUESTIONS ===");

      const blankContainer = document.querySelector(
        ".gritCourseflowNode--blankitivity",
      );

      if (!blankContainer) {
        debug("No blank activity container found");
        return;
      }

      const blankElements = Array.from(
        blankContainer.querySelectorAll("ace-blank"),
      );

      if (blankElements.length === 0) {
        debug("No ace-blank elements found");
        return;
      }

      const existingValues = blankElements.map((blank) =>
        getBlankElementValue(blank),
      );

      if (existingValues.every((val) => val && val.trim().length > 0)) {
        debug("Blank activity already filled");
        return;
      }

      const markupContainer = blankContainer.querySelector(
        ".gritCourseflowNode__markupContainer",
      );

      let questionText = "";
      if (markupContainer) {
        const html = markupContainer.innerHTML;
        questionText = html
          .replace(/<ace-blank[^>]*><\/ace-blank>/gi, "____")
          .replace(/<ace-blank[^>]*\/>/gi, "____")
          .replace(/<\/?[^>]+>/g, " ")
          .replace(/\s+/g, " ")
          .trim();
      }

      if (!questionText) {
        const fallbackText = blankContainer.innerText
          .replace(/\s+/g, " ")
          .trim();
        if (fallbackText.length > 5) {
          questionText = fallbackText;
        }
      }

      if (!questionText) {
        debug("Could not extract question text for blank activity");
        return;
      }

      const blankCount = blankElements.length;
      const storageKey = `blank:${questionText}::${blankCount}`;
      const storedQuestions =
        getStoredQuestions();

      let answers = storedQuestions[storageKey];
      if (answers) {
        answers = Array.isArray(answers) ? answers : [answers];
        log(
          `📚 Using stored blank answer${
            answers.length === 1 ? "" : "s"
          } for activity`,
        );
      } else if (config.useAI) {
        debug("\n" + "=".repeat(60));
        debug("🤖 AI ENABLED - PROCESSING BLANK QUESTION");
        debug("=".repeat(60));
        debug("Question:", questionText);
        debug("Blanks:", blankCount);
        debug("🔄 Calling AI for blank answers...");

        answers = await askGeminiForBlank(questionText, blankCount);

        debug("\n✅ AI RESPONSE RECEIVED:");
        if (answers && answers.length > 0) {
          answers.forEach((ans, idx) => {
            debug(`   Blank ${idx + 1}: ${ans}`);
          });
        } else {
          debug("   ❌ No blank answers received (error occurred)");
        }
        debug("=".repeat(60) + "\n");

        if (answers && answers.length === blankCount) {
          storedQuestions[storageKey] =
            blankCount === 1 ? answers[0] : answers;
                        saveStoredQuestions(storedQuestions);
        }
      }

      if (!answers || answers.length !== blankCount) {
        log("⚠️ Could not determine blank answers", false, true);
        return;
      }

      for (let i = 0; i < blankElements.length; i++) {
        const blank = blankElements[i];
        const answer = answers[i];

        if (!answer) {
          continue;
        }

        const applied = setBlankElementValue(blank, answer);
        if (applied) {
          debug(`Filled blank ${i + 1} with "${answer}"`);
          blank.setAttribute("data-ace-filled", "true");
          await waitFor(100);
        } else {
          console.warn("Could not set value for blank element", blank);
        }
      }

      await waitFor(300);
      try {
        await waitUntilCheckAnswerButtonIsEnabled();
      } catch (err) {
        debug("Check answer button did not enable:", err?.message || err);
      }
      await clickContinueButton();
    }

    async function autoAnswerTestQuestionsWithAI() {
      if (testQuestionProcessing) {
        return;
      }

      testQuestionProcessing = true;

      try {
        debug("=== CHECKING FOR TEST QUESTIONS ===");

        const questionContainer = document.querySelector(
          "[data-test='questionContainer']",
        );

        if (!questionContainer) {
          debug("No question container found");
          return;
        }

        debug("Question container found!");

        const questionTitleEl = questionContainer.querySelector(
          "[data-test='questionTitle']",
        );

        if (!questionTitleEl) {
          debug("No question title element found");
          return;
        }

        const questionTitle = questionTitleEl.innerText.trim();
        if (!questionTitle) {
          debug("Question title text empty, skipping");
          return;
        }
        debug("Question found:", questionTitle);

        const answerButtons =
          questionContainer.querySelectorAll(
            ".grit.answerButton, [data-test='answer']",
          );

        debug("Answer buttons found:", answerButtons.length);

        if (answerButtons.length === 0) {
          debug("No answer buttons found");
          return;
        }

        const instructionText =
          questionContainer
            .querySelector("[data-test='selectAllThatApply']")
            ?.innerText?.trim() ||
          questionContainer
            .querySelector(".gritCourseflowNode__questionInstructions")
            ?.innerText?.trim() ||
          "";

        const isMultiSelect =
          !!questionContainer.querySelector("[data-test='selectAllThatApply']") ||
          /select all that apply/i.test(instructionText) ||
          /select all that apply/i.test(
            questionContainer.innerText ? questionContainer.innerText.slice(0, 300) : "",
          );

        debug("Is multi-select question:", isMultiSelect);

        const alreadyAnswered = Array.from(answerButtons).some(
          (btn) =>
            btn.classList.contains("selected") ||
            btn.classList.contains("correct") ||
            btn.classList.contains("missed") ||
            btn.disabled,
        );

        if (alreadyAnswered) {
          debug("Question already answered, skipping");
          return;
        }

        debug("Question not yet answered, proceeding...");

        const normalizeText = (text) =>
          (text || "").replace(/\s+/g, " ").trim();

        const answerTexts = Array.from(answerButtons).map((btn) =>
          normalizeText(btn.innerText),
        );

        debug("Answer options:", answerTexts);

        const storedQuestions =
          getStoredQuestions();

        debug(
          "Stored questions count:",
          Object.keys(storedQuestions).length,
        );

        async function deselectUnwantedAnswers(targetSet) {
          if (!isMultiSelect) {
            return;
          }
          for (const btn of answerButtons) {
            const btnText = normalizeText(btn.innerText).toLowerCase();
            if (
              btn.classList.contains("selected") &&
              !targetSet.has(btnText) &&
              !btn.disabled
            ) {
              debug("Deselecting non-target answer:", btn.innerText.trim());
              btn.click();
              await waitFor(100);
            }
          }
        }

        async function clickAnswersByText(answersToSelect) {
          const normalizedTargets = answersToSelect
            .map((ans) => normalizeText(ans))
            .filter(Boolean);
          if (normalizedTargets.length === 0) {
            return 0;
          }

          const targetSet = new Set(
            normalizedTargets.map((text) => text.toLowerCase()),
          );
          await deselectUnwantedAnswers(targetSet);

          let clickedCount = 0;
          for (const targetText of normalizedTargets) {
            const lowerTarget = targetText.toLowerCase();
            const button = Array.from(answerButtons).find((btn) => {
              const btnText = normalizeText(btn.innerText).toLowerCase();
              return btnText === lowerTarget;
            });

            if (!button) {
              console.warn(
                "[AI] Could not find button for answer:",
                targetText,
              );
              continue;
            }

            if (!button.classList.contains("selected")) {
              debug("Selecting answer:", button.innerText.trim());
              button.click();
              clickedCount++;
              await waitFor(150);
            }
          }

          return clickedCount;
        }

        async function submitSelectedAnswers() {
          await submitTestQuestion(questionContainer, questionTitle);
        }

        if (storedQuestions[questionTitle]) {
          const storedEntry = storedQuestions[questionTitle];
          const answersToSelect = Array.isArray(storedEntry)
            ? storedEntry
            : [storedEntry];
          debug("Found stored answers:", answersToSelect);

          const clicked = await clickAnswersByText(answersToSelect);
          if (clicked > 0) {
            log(
              `📚 Using stored ${
                isMultiSelect ? "answers" : "answer"
              } for test question`,
            );
            await submitSelectedAnswers();
            return;
          }

          debug("Stored answers were not found in the current options");
        }

        if (config.useAI) {
          debug("\n" + "=".repeat(60));
          debug("🤖 AI ENABLED - PROCESSING QUESTION");
          debug("=".repeat(60));
          debug("📝 QUESTION DETECTED:");
          debug("   " + questionTitle);
          debug("\n📋 POSSIBLE ANSWERS:");
          answerTexts.forEach((answer, index) => {
            debug(`   ${index + 1}. ${answer}`);
          });
          debug("\n🔄 Calling AI...");

          const aiAnswer = await askGemini(questionTitle, answerTexts, {
            expectMultiple: isMultiSelect,
          });

          const aiAnswers = Array.isArray(aiAnswer)
            ? aiAnswer
            : aiAnswer
            ? [aiAnswer]
            : [];

          debug("\n✅ AI RESPONSE RECEIVED:");
          if (aiAnswers.length > 0) {
            aiAnswers.forEach((ans, idx) => {
              debug(`   Selected Answer ${idx + 1}: ${ans}`);
            });
          } else {
            debug("   ❌ No answer received (error occurred)");
          }
          debug("=".repeat(60) + "\n");

          if (aiAnswers.length > 0) {
            const clicked = await clickAnswersByText(aiAnswers);
            if (clicked > 0) {
              storedQuestions[questionTitle] = isMultiSelect
                ? aiAnswers
                : aiAnswers[0];
              saveStoredQuestions(storedQuestions);
              await submitSelectedAnswers();
              return;
            }

            console.error(
              "AI answers not found in button options!",
              aiAnswers,
            );
            log("⚠️ AI answers not found in options", false, true);
          } else {
            debug("AI returned null (error or disabled)");
          }
        } else {
          debug("AI is disabled, using fallback");
        }

        log("⚠️ Using fallback (first option)", false, true);
        answerButtons[0].click();
        await submitSelectedAnswers();
      } finally {
        testQuestionProcessing = false;
      }
    }

    async function submitTestQuestion(questionContainer, previousQuestionTitle) {
      try {
        await waitUntilCheckAnswerButtonIsEnabled();
      } catch (err) {
        console.warn("Wait for check answer button failed:", err);
      }

      const checkAnswerButton =
        questionContainer.querySelector("[data-test='courseflow-forward']") ||
        questionContainer.querySelector("button[data-test='courseflow-forward']") ||
        questionContainer.querySelector("[courseflow-forward]") ||
        questionContainer.querySelector("[data-test='submitAnswer']") ||
        questionContainer.querySelector("button[data-test='submitAnswer']") ||
        questionContainer.querySelector("[data-test='continueButton']") ||
        questionContainer.querySelector("button[data-test='continueButton']") ||
        document.querySelector("[data-test='courseflow-forward']") ||
        document.querySelector("[data-test='submitAnswer']") ||
        document.querySelector("[data-test='continueButton']");

      if (checkAnswerButton && !checkAnswerButton.disabled) {
        log("Checking answer");
        checkAnswerButton.click();
        await waitFor(500);
      }

      const buttonDataTest = checkAnswerButton?.getAttribute("data-test");
      if (buttonDataTest === "submitAnswer") {
        await waitForNewTestQuestion(previousQuestionTitle);
        return;
      }

      // Attempt to proceed to the next screen
      const nextArrow = document.getElementById("arrow-next");
      if (
        nextArrow &&
        nextArrow !== checkAnswerButton &&
        !nextArrow.disabled
      ) {
        log("Clicking next arrow");
        
        // Try up to 3 times to click and wait for change
        for (let i = 0; i < 3; i++) {
          await robustClick(nextArrow);
          const changed = await waitForNewTestQuestion(previousQuestionTitle);
          if (changed) break;
          
          if (i < 2) {
            log(`⚠️ Page didn't change after click (attempt ${i+1}), retrying...`, false, true);
            await waitFor(1000);
          }
        }
        
        await waitFor(400);
      }

      await clickContinueButton();
    }

    function capturePopupQuestionResults() {
      const popupContainers = document.querySelectorAll(
        "[data-test='popupQuestionContainer']",
      );
      if (popupContainers.length === 0) {
        return;
      }

      const storedQuestions =
        getStoredQuestions();
      let hasChanges = false;

      popupContainers.forEach((container) => {
        if (container.dataset.aceableResultsSaved === "true") {
          return;
        }

        const answerButtons = container.querySelectorAll(".grit.answerButton");
        if (answerButtons.length === 0) {
          return;
        }

        const resultButtons = Array.from(answerButtons).filter(
          (btn) =>
            btn.classList.contains("correct") ||
            btn.classList.contains("missed"),
        );

        if (resultButtons.length === 0) {
          return;
        }

        let questionText = "";
        const questionSelectors = [
          "[data-test='questionTitle']",
          ".gritCourseflowNode__markupContainer",
          ".content__markup-new",
          ".gritCourseflowNode__question",
          ".gritCourseflowNode__questionText",
          "h1",
          "h2",
          "h3",
          "p",
        ];

        for (const selector of questionSelectors) {
          const el = container.querySelector(selector);
          const text = el?.innerText?.trim();
          if (text && text.length > 5) {
            questionText = text.replace(/\s+/g, " ").trim();
            break;
          }
        }

        if (!questionText || questionText.length < 5) {
          return;
        }

        const answers = resultButtons
          .map((btn) => btn.innerText?.trim())
          .filter((text) => text && text.length > 0);

        if (answers.length === 0) {
          return;
        }

        const valueToStore = answers.length === 1 ? answers[0] : answers;

        const existingEntry = storedQuestions[questionText];
        const normalizeList = (entry) => {
          if (!entry) return [];
          if (Array.isArray(entry)) {
            return entry.map((ans) => ans.trim().toLowerCase());
          }
          return [entry.trim().toLowerCase()];
        };

        const existingNormalized = normalizeList(existingEntry);
        const newNormalized = normalizeList(valueToStore);

        const sameAnswers =
          existingNormalized.length === newNormalized.length &&
          existingNormalized.every((ans) => newNormalized.includes(ans));

        if (!sameAnswers) {
          storedQuestions[questionText] = valueToStore;
          hasChanges = true;
          log(
            `💾 Saved ${answers.length} correct answer${
              answers.length === 1 ? "" : "s"
            } for "${questionText.substring(0, 60)}..."`,
          );
        }

        container.dataset.aceableResultsSaved = "true";
      });

      if (hasChanges) {
                      saveStoredQuestions(storedQuestions);
      }
    }

    async function autoAnswerSortableQuestions() {
      debug("=== CHECKING FOR SORTABLE QUESTIONS ===");

      const questionContainer = document.querySelector(
        "[data-test='popupQuestionContainer']",
      );

      if (!questionContainer) {
        debug("No sortable question container found");
        return;
      }

      // Check if this is a sortable/draggable question
      const answerButtonList = questionContainer.querySelector(
        ".gritCourseflowNode__answerButtonList",
      );

      if (!answerButtonList) {
        debug("No answer button list found");
        return;
      }

      // Get all potential draggable items
      const draggableItems = answerButtonList.querySelectorAll(
        ".gritCourseflowNode__answerButtonListItem",
      );

      if (draggableItems.length === 0) {
        debug("No draggable items found");
        return;
      }

      const hasSortableMarkers = Array.from(draggableItems).some((li) =>
        li.querySelector(".sortable-item, [data-test='dragItemText']"),
      );

      if (!hasSortableMarkers) {
        debug("Detected standard question (no sortable markers)");
        return;
      }

      // Check if items are already in correct order (by checking if they're disabled or have a "correct" class)
      const sortableItems = Array.from(draggableItems).map(
        (li) => li.querySelector(".sortable-item") || li,
      );

      // Check if already answered - look for disabled state or completion indicators
      const alreadyAnswered = Array.from(sortableItems).some(
        (item) =>
          item?.getAttribute("aria-disabled") === "true" ||
          item?.classList.contains("disabled") ||
          item?.closest(".gritCourseflowNode__answerButtonListItem")
            ?.classList.contains("correct"),
      );

      if (alreadyAnswered) {
        debug("Sortable question already answered, skipping");
        return;
      }

      debug("Sortable question found with", draggableItems.length, "items");

      // Extract question text
      const questionHeader = questionContainer.querySelector(
        "[data-test='questionHeader']",
      );
      let questionText = "";

      if (questionHeader) {
        questionText = questionHeader.innerText.trim();
      }

      if (!questionText || questionText.length < 10) {
        debug("Could not extract valid question text");
        return;
      }

      // Extract all draggable item texts
      const validItems = Array.from(draggableItems)
        .map((li) => {
          const dragItemText = li.querySelector("[data-test='dragItemText']");
          return dragItemText ? dragItemText.innerText.trim() : "";
        })
        .filter((text) => text.length > 0);

      if (validItems.length === 0) {
        debug("No valid item texts found");
        return;
      }

      debug("=== SORTABLE QUESTION DETECTED ===");
      debug("Question:", questionText);
      debug("Items to order:", validItems);

      // Try local storage first
      const storedQuestions =
        getStoredQuestions();

      const storageKey = `sortable:${questionText}`;
      if (storedQuestions[storageKey]) {
        const storedOrder = storedQuestions[storageKey];
        debug("Found stored order:", storedOrder);

        if (
          Array.isArray(storedOrder) &&
          storedOrder.length === validItems.length
        ) {
          log(`📚 Using stored order for sortable question`);
          await reorderItems(draggableItems, storedOrder);

          // Wait a bit for the UI to update and verify the order
          await waitFor(1000);
          
          // Try to find and click the continue button multiple times with delays
          await clickContinueButton();

          return;
        }
      }

      // Use AI if enabled
      if (config.useAI) {
        debug("\n" + "=".repeat(60));
        debug("🤖 AI ENABLED - PROCESSING SORTABLE QUESTION");
        debug("=".repeat(60));
        debug("📝 QUESTION DETECTED:");
        debug("   " + questionText);
        debug("\n📋 ITEMS TO ORDER:");
        validItems.forEach((item, index) => {
          debug(`   ${index + 1}. ${item}`);
        });
        debug("\n🔄 Calling AI...");

        const aiOrder = await askGeminiForOrder(questionText, validItems);

        debug("\n✅ AI RESPONSE RECEIVED:");
        if (aiOrder) {
          debug("   Order: " + aiOrder.join(", "));
        } else {
          debug("   ❌ No order received (error occurred)");
        }
        debug("=".repeat(60) + "\n");

        if (aiOrder && Array.isArray(aiOrder) && aiOrder.length === validItems.length) {
          log(`🤖 AI determined order for sortable question`);
          await reorderItems(draggableItems, aiOrder);

          // Save to local storage
          storedQuestions[storageKey] = aiOrder;
                        saveStoredQuestions(storedQuestions);

          // Wait a bit for the UI to update and verify the order
          await waitFor(1000);
          
          // Try to find and click the continue button multiple times with delays
          await clickContinueButton();

          return;
        } else {
          console.error("AI returned invalid order:", aiOrder);
          log("⚠️ AI returned invalid order for sortable question", false, true);
        }
      } else {
        debug("AI is disabled, skipping sortable question");
        log("⚠️ AI disabled - cannot answer sortable question", false, true);
      }
    }

    async function robustClick(element) {
      if (!element) return false;
      
      try {
        // Scroll into view
        element.scrollIntoView({ behavior: 'smooth', block: 'center' });
        await waitFor(200);
        
        // Native click
        element.click();
        
        // Dispatch events as backup
        const clickEvent = new MouseEvent("click", {
          bubbles: true,
          cancelable: true,
          view: window,
          button: 0
        });
        element.dispatchEvent(clickEvent);
        
        return true;
      } catch (err) {
        console.error("Robust click failed:", err);
        return false;
      }
    }

    async function clickContinueButton() {
      // Try to find and click the continue button multiple times with delays
      for (let attempt = 0; attempt < 15; attempt++) {
        await waitFor(500);
        
        // Try multiple selectors for the continue button
        // The button has data-test="courseflow-forward" attribute
        const checkAnswerButton = 
          document.querySelector('[data-test="courseflow-forward"]') ||
          document.querySelector('button[data-test="courseflow-forward"]') ||
          document.querySelector('[courseflow-forward]') ||
          document.querySelector('button[courseflow-forward]') ||
          document.querySelector('[data-test="submitAnswer"]') ||
          document.querySelector('button[data-test="submitAnswer"]') ||
          document.querySelector('#arrow-next') ||
          document.querySelector('button[aria-label="Next Page"]') ||
          document.querySelector('.continue-button') ||
          document.querySelector('[data-test="continueButton"]') ||
          document.querySelector('button[data-test="continueButton"]');
        
        if (checkAnswerButton) {
          const computedStyle = window.getComputedStyle(checkAnswerButton);
          const isVisible = checkAnswerButton.offsetParent !== null && 
                           computedStyle.display !== 'none' &&
                           computedStyle.visibility !== 'hidden' &&
                           computedStyle.opacity !== '0';
          const isEnabled = !checkAnswerButton.disabled && 
                           !checkAnswerButton.hasAttribute('disabled') &&
                           !checkAnswerButton.classList.contains('disabled');
          
          debug(`Continue button found (attempt ${attempt + 1}), disabled: ${checkAnswerButton.disabled}, visible: ${isVisible}, enabled: ${isEnabled}`);
          debug(`Button element:`, checkAnswerButton);
          debug(`Button classes:`, checkAnswerButton.className);
          debug(
            `Button attributes:`,
            Array.from(checkAnswerButton.attributes)
              .map((a) => `${a.name}="${a.value}"`)
              .join(", "),
          );
          
          // Check if button is visible and enabled
          if (isEnabled && isVisible) {
            log("Checking sortable answer");
            
            // Try multiple ways to click
            try {
              // Scroll button into view first
              checkAnswerButton.scrollIntoView({ behavior: 'smooth', block: 'center' });
              await waitFor(200);
              
              // Try native click first
              checkAnswerButton.click();
              debug("Button clicked using .click()");
            } catch (e) {
              debug("Click failed, trying dispatchEvent:", e);
              try {
                // Try MouseEvent
                const clickEvent = new MouseEvent("click", {
                  bubbles: true,
                  cancelable: true,
                  view: window,
                  button: 0
                });
                checkAnswerButton.dispatchEvent(clickEvent);
                debug("Button clicked using MouseEvent");
              } catch (e2) {
                debug("MouseEvent failed, trying pointer events:", e2);
                // Try pointer events
                const pointerDown = new PointerEvent("pointerdown", {
                  bubbles: true,
                  cancelable: true,
                  pointerId: 1,
                  button: 0
                });
                const pointerUp = new PointerEvent("pointerup", {
                  bubbles: true,
                  cancelable: true,
                  pointerId: 1,
                  button: 0
                });
                checkAnswerButton.dispatchEvent(pointerDown);
                await waitFor(50);
                checkAnswerButton.dispatchEvent(pointerUp);
                debug("Button clicked using PointerEvent");
              }
            }
            
            await waitFor(800);
            
            // Check if button is still there or if page changed
            const stillThere = document.querySelector('[data-test="courseflow-forward"]');
            if (!stillThere || stillThere.disabled || stillThere !== checkAnswerButton) {
              debug("Continue button clicked and processed - button changed or removed");
              return true;
            }
          } else {
            debug(`Continue button disabled or hidden (attempt ${attempt + 1}) - visible: ${isVisible}, enabled: ${isEnabled}`);
          }
        } else {
          debug(`Continue button not found (attempt ${attempt + 1})`);
          // Log all buttons with data-test attributes for debugging
          if (attempt === 0) {
            const allButtons = document.querySelectorAll('button[data-test]');
            debug(
              `Found ${allButtons.length} buttons with data-test:`,
              Array.from(allButtons).map(
                (b) =>
                  `${b.getAttribute("data-test")}: ${b.textContent
                    ?.trim()
                    ?.substring(0, 30)}`,
              ),
            );
          }
        }
      }
      log("⚠️ Failed to click continue button after all attempts", false, true);
      return false;
    }

    async function reorderItems(draggableItems, order) {
      // order is 1-based array (e.g., [3, 1, 4, 2] means: item 3, then item 1, then item 4, then item 2)
      // draggableItems is the current DOM order

      debug("Reordering items to:", order);
      debug("Current items count:", draggableItems.length);

      if (order.length !== draggableItems.length) {
        console.error("Order length mismatch:", order.length, "vs", draggableItems.length);
        return;
      }

      // Convert NodeList to array to avoid stale references
      const itemsArray = Array.from(draggableItems);

      // Get the parent list
      const parentList = itemsArray[0]?.parentElement;
      if (!parentList) {
        console.error("Could not find parent list");
        return;
      }

      // Create array of items in the desired order (convert from 1-based to 0-based)
      const reorderedItems = order.map((pos) => itemsArray[pos - 1]);

      debug(
        "Target order items:",
        reorderedItems.map((item, idx) => {
          const text =
            item
              ?.querySelector("[data-test='dragItemText']")
              ?.innerText?.trim() || "unknown";
          return `${idx + 1}: ${text.substring(0, 50)}...`;
        }),
      );

      // Method 1: Remove all items and re-add them in the correct order
      // This ensures items are in the exact order we want
      const fragment = document.createDocumentFragment();
      
      // Add items to fragment in the correct order
      reorderedItems.forEach((item) => {
        if (item) {
          fragment.appendChild(item);
        }
      });
      
      // Clear the parent list
      while (parentList.firstChild) {
        parentList.removeChild(parentList.firstChild);
      }
      
      // Add items back in the correct order
      parentList.appendChild(fragment);
      
      debug("Items reordered using fragment method");
      
      // Method 2: Also try using insertBefore one by one as a backup
      // This might be needed for some drag-and-drop libraries
      await waitFor(200);
      
      // Verify and fix order if needed
      const currentChildren = Array.from(parentList.children);
      for (let i = 0; i < reorderedItems.length; i++) {
        const targetItem = reorderedItems[i];
        if (!targetItem) continue;
        
        const currentIndex = currentChildren.indexOf(targetItem);
        const targetIndex = i;
        
        if (currentIndex !== targetIndex) {
          // Item is in wrong position, move it
          const referenceNode = parentList.children[targetIndex] || null;
          if (referenceNode && referenceNode !== targetItem) {
            parentList.insertBefore(targetItem, referenceNode);
            debug(`Fixed item ${i + 1} position: ${currentIndex} -> ${targetIndex}`);
          } else if (!referenceNode) {
            parentList.appendChild(targetItem);
            debug(`Fixed item ${i + 1} position: ${currentIndex} -> end`);
          }
          
          // Update currentChildren array
          currentChildren.splice(currentIndex, 1);
          currentChildren.splice(targetIndex, 0, targetItem);
          
          await waitFor(100);
        }
      }
      
      // Try to simulate drag events for all items to notify the library
      const sortableItems = parentList.querySelectorAll(".sortable-item");
      sortableItems.forEach((item, index) => {
        // Trigger various events that might be needed
        const events = ["change", "input", "blur", "focus"];
        events.forEach((eventType) => {
          const event = new Event(eventType, { bubbles: true });
          item.dispatchEvent(event);
        });
      });

      log(`✅ Reordered ${reorderedItems.length} items`);

      // Verify the order is correct
      await waitFor(300);
      const finalOrder = Array.from(parentList.children).map((li, idx) => {
        const text = li.querySelector("[data-test='dragItemText']")?.innerText?.trim() || "";
        return { index: idx, text: text.substring(0, 50) };
      });
      debug("Final order after reordering:", finalOrder);

      // Wait a bit for the UI to update
      await waitFor(500);

      // Try to trigger additional events that might be needed
      const allSortableItems = parentList.querySelectorAll(".sortable-item");
      allSortableItems.forEach((item, index) => {
        // Trigger change event
        const changeEvent = new Event("change", { bubbles: true });
        item.dispatchEvent(changeEvent);

        // Trigger input event
        const inputEvent = new Event("input", { bubbles: true });
        item.dispatchEvent(inputEvent);

        // Try to trigger React's onChange if it's a React app
        // Only do this for input elements, not sortable items
        if (item instanceof HTMLInputElement) {
          try {
            const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
              window.HTMLInputElement.prototype,
              "value"
            )?.set;
            if (nativeInputValueSetter) {
              nativeInputValueSetter.call(item, item.value || "");
              const reactEvent = new Event("input", { bubbles: true });
              item.dispatchEvent(reactEvent);
            }
          } catch (e) {
            // Ignore errors for non-input elements
            debug("Skipping React input trigger for non-input element");
          }
        }
      });

      // Try to find and trigger any drag-and-drop library callbacks
      // Look for common drag-and-drop library patterns
      const listElement = parentList;
      if (listElement) {
        // Trigger a custom event that might be listened to
        const reorderEvent = new CustomEvent("itemsReordered", {
          bubbles: true,
          detail: { order: order }
        });
        listElement.dispatchEvent(reorderEvent);

        // Try to find React state updates if it's a React app
        const reactInstance = listElement._reactInternalInstance || 
                             listElement.__reactInternalInstance ||
                             listElement._reactInternalFiber;
        if (reactInstance) {
          debug("Found React instance, trying to trigger update");
        }
      }

      debug("Items reordered successfully");
    }

    function saveReviewQuestions() {
      const reviewCards = document.querySelectorAll(
        ".grit.question-review-card",
      );
      if (reviewCards.length === 0) return;

      debug("Found review cards:", reviewCards.length);

      const storedQuestions =
        getStoredQuestions();
      let savedCount = 0;

      reviewCards.forEach((card) => {
        const q = card.querySelector(".paragraph__question");
        const a =
          card.querySelector(".grit.answerButton.selected.correct") ||
          card.querySelector(".grit.answerButton.missed");

        if (q && a) {
          const qText = q.innerText.trim();
          const aText = a.innerText.trim();

          debug("Review Q:", qText);
          debug("Review A:", aText);

          if (!storedQuestions[qText] || storedQuestions[qText] !== aText) {
            storedQuestions[qText] = aText;
            savedCount++;
          }
        }
      });

      if (savedCount > 0) {
                      saveStoredQuestions(storedQuestions);
        log(`💾 Saved ${savedCount} review answers`);
      }
    }

    function waitUntilCheckAnswerButtonIsEnabled() {
      return new Promise((resolve) => {
        const checkInterval = setInterval(() => {
          const btn =
            document.querySelector("[data-test='courseflow-forward']") ||
            document.querySelector("button[data-test='courseflow-forward']") ||
            document.querySelector("[courseflow-forward]") ||
            document.querySelector("button[courseflow-forward]") ||
            document.querySelector("[data-test='submitAnswer']") ||
            document.querySelector("button[data-test='submitAnswer']") ||
            document.querySelector("[data-test='continueButton']") ||
            document.querySelector("button[data-test='continueButton']");
          if (btn && !btn.disabled) {
            clearInterval(checkInterval);
            resolve();
          }
        }, 100);
      });
    }

    async function waitForNewTestQuestion(previousQuestionTitle) {
      if (!previousQuestionTitle) {
        await waitFor(600);
        return true;
      }

      for (let attempt = 0; attempt < 24; attempt++) {
        await waitFor(200);
        const container = document.querySelector(
          "[data-test='questionContainer']",
        );
        const currentTitle = container
          ?.querySelector("[data-test='questionTitle']")
          ?.innerText?.trim();

        if (!container || !currentTitle || currentTitle !== previousQuestionTitle) {
          return true;
        }
      }
      return false;
    }

    function findEditableTarget(element) {
      if (!element) return null;

      const selectors = [
        "input",
        "textarea",
        "select",
        "[contenteditable='true']",
        "[role='textbox']",
      ];

      const visited = new Set();
      const queue = [];

      const enqueue = (node) => {
        if (!node || visited.has(node)) return;
        visited.add(node);
        queue.push(node);
      };

      enqueue(element);
      if (element.shadowRoot) {
        enqueue(element.shadowRoot);
      }

      while (queue.length > 0) {
        const current = queue.shift();
        if (!current) continue;

        for (const selector of selectors) {
          if (typeof current.querySelector === "function") {
            const found = current.querySelector(selector);
            if (found) {
              return found;
            }
          }
        }

        const children =
          current instanceof Element || current instanceof DocumentFragment
            ? current.children || current.childNodes
            : [];

        for (const child of Array.from(children || [])) {
          if (child instanceof Element) {
            enqueue(child);
            if (child.shadowRoot) {
              enqueue(child.shadowRoot);
            }
          }
        }
      }

      if (typeof element.value === "string") {
        return element;
      }

      return null;
    }

    function setValueWithEvents(target, value) {
      if (!target) return false;
      const trimmed = value.trim();

      if (target.tagName) {
        const tag = target.tagName.toLowerCase();
        if (tag === "select") {
          let matched = false;
          for (const option of Array.from(target.options || [])) {
            if (
              option.value.toLowerCase() === trimmed.toLowerCase() ||
              option.text.toLowerCase() === trimmed.toLowerCase()
            ) {
              target.value = option.value;
              option.selected = true;
              matched = true;
              break;
            }
          }
          if (!matched) {
            target.value = trimmed;
          }
        } else if (tag === "input" || tag === "textarea") {
          const descriptor = Object.getOwnPropertyDescriptor(
            Object.getPrototypeOf(target),
            "value",
          );
          if (descriptor?.set) {
            descriptor.set.call(target, trimmed);
          } else {
            target.value = trimmed;
          }
        } else {
          target.textContent = trimmed;
        }
      } else if (target instanceof HTMLElement) {
        if (target.isContentEditable) {
          target.innerText = trimmed;
        } else {
          target.textContent = trimmed;
        }
      } else {
        target.textContent = trimmed;
      }

      const events = ["input", "change", "blur"];
      for (const eventName of events) {
        try {
          const event =
            eventName === "input"
              ? new InputEvent(eventName, { bubbles: true })
              : new Event(eventName, { bubbles: true });
          target.dispatchEvent(event);
        } catch (err) {
          target.dispatchEvent(new Event(eventName, { bubbles: true }));
        }
      }

      return true;
    }

    function getBlankElementValue(blankElement) {
      const target = findEditableTarget(blankElement);
      if (target) {
        if (target.tagName) {
          const tag = target.tagName.toLowerCase();
          if (tag === "input" || tag === "textarea" || tag === "select") {
            return target.value || "";
          }
        }
        if (target.isContentEditable) {
          return target.innerText || "";
        }
        return target.textContent || "";
      }

      const directValue =
        blankElement.getAttribute?.("value") ||
        blankElement.textContent ||
        blankElement.innerText;
      return directValue ? directValue.trim() : "";
    }

    function setBlankElementValue(blankElement, value) {
      if (!blankElement || !value) {
        return false;
      }

      const target = findEditableTarget(blankElement);
      if (target) {
        simulateHumanTyping(target, value);
        return true;
      }

      blankElement.textContent = value.trim();
      if (typeof blankElement.setAttribute === "function") {
        blankElement.setAttribute("value", value.trim());
      }
      blankElement.setAttribute("data-ace-filled", "true");
      return true;
    }

    function waitFor(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }

    async function simulateHumanTyping(target, value) {
      if (!target) return;
      const text = value.trim();

      target.focus?.();
      await waitFor(50);

      const descriptor = target.tagName
        ? Object.getOwnPropertyDescriptor(
            Object.getPrototypeOf(target),
            "value",
          )
        : null;

      if (descriptor?.set) {
        descriptor.set.call(target, "");
      } else if (target.tagName) {
        target.value = "";
      } else if (target.isContentEditable) {
        target.innerText = "";
      } else {
        target.textContent = "";
      }

      target.dispatchEvent(new Event("input", { bubbles: true }));

      const characters = text.split("");
      for (const char of characters) {
        const keyEventOptions = {
          key: char,
          char,
          keyCode: char.charCodeAt(0),
          which: char.charCodeAt(0),
          bubbles: true,
        };

        target.dispatchEvent(new KeyboardEvent("keydown", keyEventOptions));
        target.dispatchEvent(new KeyboardEvent("keypress", keyEventOptions));

        if (descriptor?.set) {
          descriptor.set.call(target, (target.value || "") + char);
        } else if (target.tagName) {
          target.value = (target.value || "") + char;
        } else if (target.isContentEditable) {
          target.innerText = (target.innerText || "") + char;
        } else {
          target.textContent = (target.textContent || "") + char;
        }

        target.dispatchEvent(new Event("input", { bubbles: true }));
        target.dispatchEvent(new KeyboardEvent("keyup", keyEventOptions));
        await waitFor(30 + Math.random() * 70);
      }

      target.dispatchEvent(
        new KeyboardEvent("keydown", {
          key: "Enter",
          code: "Enter",
          keyCode: 13,
          which: 13,
          bubbles: true,
        }),
      );
      target.dispatchEvent(
        new KeyboardEvent("keyup", {
          key: "Enter",
          code: "Enter",
          keyCode: 13,
          which: 13,
          bubbles: true,
        }),
      );

      target.dispatchEvent(new Event("change", { bubbles: true }));
      await waitFor(50);
      target.blur?.();
    }

    // Log startup
    log("✨ Script initialized!");
    log("Press Ctrl+Shift+A (Win) or Cmd+Shift+A (Mac) for settings");
    debug("=== Aceable Script Started ===");
    debug("Config:", config);
    
    if (config.useAI && config.geminiApiKey === "YOUR_API_KEY_HERE") {
      log("⚠️ Please set your Gemini API key in settings!", true);
      console.warn("No API key configured - AI will not work!");
    }
    if (!config.scriptEnabled) {
      log("⏸️ Script is DISABLED - enable in settings", false, true);
    }

    // Check for break on startup
    detectBreakPopup();
  }

  // Initialize when DOM is ready
  if (document.body) {
    init();
  } else {
    document.addEventListener("DOMContentLoaded", init);
  }
})();