Duolingo Booster

Complete Duolingo farming tool with XP, Gems, Streak, Super, and Items

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Duolingo Booster
// @namespace    http://tampermonkey.net/
// @version      2.0.2
// @description  Complete Duolingo farming tool with XP, Gems, Streak, Super, and Items
// @author       LucaN
// @match        https://*.duolingo.com/*
// @icon         https://www.duolingo.com/favicon.ico
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  // =============================================================================
  // CONFIGURATION & CONSTANTS
  // =============================================================================

  const CONFIG = {
    apiBaseUrl: 'https://duoapi.smoteam.com/v1/duolingo',
    maxXP: 20000,
    maxGems: 10000,
    maxStreak: 1000,
    xpStep: 200,
    gemStep: 30,
  };

  const ENDPOINTS = {
    verifyLicense: `${CONFIG.apiBaseUrl}/verify-license`,
    addXp: `${CONFIG.apiBaseUrl}/add-xp`,
    farmXp: `${CONFIG.apiBaseUrl}/farm-xp`,
    claimGems: `${CONFIG.apiBaseUrl}/claim-gems`,
    farmGems: `${CONFIG.apiBaseUrl}/farm-gems`,
    farmGemsFast: `${CONFIG.apiBaseUrl}/farm-gems-fast`,
    farmStreak: `${CONFIG.apiBaseUrl}/farm-streak`,
    activateSuper: `${CONFIG.apiBaseUrl}/activate-super`,
    claimItem: `${CONFIG.apiBaseUrl}/claim-item`,
    userInfo: `${CONFIG.apiBaseUrl}/user-info`,
    items: `${CONFIG.apiBaseUrl}/items`,
  };

  // Shop items list
  const SHOP_ITEMS = [
    {
      id: 'society_streak_freeze',
      name: 'Streak Freeze',
      icon: '❄️',
      color: '#00D4FF',
    },
    {
      id: 'streak_repair',
      name: 'Streak Repair',
      icon: '🔧',
      color: '#FFA500',
    },
    {
      id: 'heart_segment',
      name: 'Heart Segment',
      icon: '❤️',
      color: '#FF6B6B',
    },
    {
      id: 'health_refill',
      name: 'Health Refill',
      icon: '💊',
      color: '#FF4444',
    },
    {
      id: 'xp_boost_stackable',
      name: 'XP Boost Stackable',
      icon: '⚡',
      color: '#FFD700',
    },
    {
      id: 'general_xp_boost',
      name: 'General XP Boost',
      icon: '🔥',
      color: '#FF8C00',
    },
    {
      id: 'xp_boost_15',
      name: 'XP Boost x2 (15 min)',
      icon: '⏱️',
      color: '#58CC02',
    },
    {
      id: 'xp_boost_60',
      name: 'XP Boost x2 (60 min)',
      icon: '⏰',
      color: '#45A800',
    },
    {
      id: 'xp_boost_refill',
      name: 'XP Boost x3 (15 min)',
      icon: '🚀',
      color: '#1CB0F6',
    },
    {
      id: 'early_bird_xp_boost',
      name: 'Early Bird XP Boost',
      icon: '🌅',
      color: '#FF9500',
    },
    {
      id: 'row_blaster_150',
      name: 'Row Blaster 150',
      icon: '💥',
      color: '#9B59B6',
    },
    {
      id: 'row_blaster_250',
      name: 'Row Blaster 250',
      icon: '💣',
      color: '#8E44AD',
    },
  ];

  // Tab definitions
  const TABS = [
    { id: 'xp', name: 'XP', icon: '⚡', color: '#58CC02' },
    { id: 'gems', name: 'Gems', icon: '💎', color: '#FFD700' },
    { id: 'streak', name: 'Streak', icon: '🔥', color: '#FF9500' },
    { id: 'super', name: 'Super', icon: '👑', color: '#9B59B6' },
    { id: 'items', name: 'Items', icon: '🎁', color: '#1CB0F6' },
  ];

  // =============================================================================
  // JWT TOKEN & USER INFO
  // =============================================================================

  const jwtToken = document.cookie
    .split('; ')
    .find((cookie) => cookie.startsWith('jwt_token='))
    ?.split('=')[1];

  if (!jwtToken) {
    console.error('[Duolingo Farm Pro] JWT token not found.');
    return;
  }

  /**
   * Decode user ID from JWT token
   */
  function getUserIdFromJwt(token) {
    try {
      const payload = token.split('.')[1];
      const decoded = decodeURIComponent(
        atob(payload.replace(/-/g, '+').replace(/_/g, '/'))
          .split('')
          .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
          .join('')
      );
      return JSON.parse(decoded).sub;
    } catch (error) {
      console.error('Failed to decode JWT:', error);
      return null;
    }
  }

  // =============================================================================
  // APPLICATION STATE
  // =============================================================================

  const state = {
    token: GM_getValue('auth_token', '') || `Bearer ${jwtToken}`,
    userId: getUserIdFromJwt(jwtToken).toString(),
    activeTab: 'xp',
    isRunning: false,
    progress: 0,
    currentAction: '',
    // Form values
    xpAmount: 1000,
    gemAmount: 300,
    streakDays: 10,
    gemMode: 'normal', // 'normal' or 'fast'
    // License info
    licenseKey: GM_getValue('license_key', ''),
    licenseValid: false,
    licensePlan: 'free',
    licenseUsername: '',
    licenseFeatures: [],
    licenseExpiresAt: null,
  };

  // =============================================================================
  // CSS STYLES
  // =============================================================================

  GM_addStyle(`
    @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

    /* Container */
    #duo-farm-container {
      position: fixed;
      top: 20px;
      right: 20px;
      z-index: 999999;
      font-family: 'Poppins', sans-serif;
    }

    /* Toggle Button */
    .duo-toggle-btn {
      width: 56px;
      height: 56px;
      border-radius: 50%;
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      border: none;
      cursor: pointer;
      box-shadow: 0 4px 20px rgba(88, 204, 2, 0.4);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 24px;
      transition: all 0.3s ease;
    }

    .duo-toggle-btn:hover {
      transform: scale(1.1);
      box-shadow: 0 6px 30px rgba(88, 204, 2, 0.6);
    }

    /* Main Panel */
    .duo-panel {
      width: 420px;
      max-height: 85vh;
      overflow-y: auto;
      background: rgba(19, 31, 36, 0.98);
      backdrop-filter: blur(20px);
      border-radius: 20px;
      padding: 0;
      box-shadow: 0 10px 50px rgba(0, 0, 0, 0.6);
      border: 1px solid rgba(255, 255, 255, 0.1);
      color: white;
      display: none;
      animation: slideIn 0.3s ease-out;
    }

    .duo-panel.show {
      display: block;
    }

    .duo-panel::-webkit-scrollbar {
      width: 6px;
    }

    .duo-panel::-webkit-scrollbar-track {
      background: rgba(255, 255, 255, 0.05);
      border-radius: 3px;
    }

    .duo-panel::-webkit-scrollbar-thumb {
      background: rgba(88, 204, 2, 0.5);
      border-radius: 3px;
    }

    @keyframes slideIn {
      from { opacity: 0; transform: translateY(-20px) scale(0.95); }
      to { opacity: 1; transform: translateY(0) scale(1); }
    }

    /* Header */
    .duo-header {
      padding: 20px 24px;
      background: linear-gradient(135deg, rgba(88, 204, 2, 0.1) 0%, rgba(28, 176, 246, 0.1) 100%);
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .duo-header-left {
      display: flex;
      align-items: center;
      gap: 12px;
    }

    .duo-logo {
      font-size: 28px;
    }

    .duo-title {
      font-size: 18px;
      font-weight: 700;
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      margin: 0;
    }

    .duo-version {
      font-size: 11px;
      color: #6B7280;
    }

    .duo-close-btn {
      width: 32px;
      height: 32px;
      border-radius: 50%;
      background: rgba(255, 255, 255, 0.1);
      border: none;
      color: white;
      font-size: 18px;
      cursor: pointer;
      transition: all 0.2s;
    }

    .duo-close-btn:hover {
      background: rgba(255, 255, 255, 0.2);
      transform: rotate(90deg);
    }

    /* Tabs */
    .duo-tabs {
      display: flex;
      padding: 0 16px;
      gap: 4px;
      background: rgba(0, 0, 0, 0.2);
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .duo-tab {
      flex: 1;
      padding: 12px 8px;
      background: none;
      border: none;
      color: #6B7280;
      font-size: 12px;
      font-weight: 600;
      font-family: 'Poppins', sans-serif;
      cursor: pointer;
      transition: all 0.2s;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 4px;
      border-bottom: 2px solid transparent;
    }

    .duo-tab:hover {
      color: #D1D5DB;
    }

    .duo-tab.active {
      color: var(--tab-color, #58CC02);
      border-bottom-color: var(--tab-color, #58CC02);
    }

    .duo-tab-icon {
      font-size: 18px;
    }

    /* Tab Content */
    .duo-content {
      padding: 20px 24px;
    }

    .duo-tab-panel {
      display: none;
    }

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

    /* Form Elements */
    .duo-form-group {
      margin-bottom: 20px;
    }

    .duo-label {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px;
      font-weight: 600;
      color: #D1D5DB;
      margin-bottom: 10px;
    }

    .duo-label-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: currentColor;
    }

    /* Input */
    .duo-input {
      width: 100%;
      padding: 12px 16px;
      background: rgba(31, 41, 55, 0.6);
      border: 1px solid rgba(255, 255, 255, 0.1);
      border-radius: 12px;
      color: white;
      font-size: 13px;
      font-family: 'Poppins', sans-serif;
      box-sizing: border-box;
      transition: all 0.2s;
    }

    .duo-input:focus {
      outline: none;
      border-color: #58CC02;
      box-shadow: 0 0 0 3px rgba(88, 204, 2, 0.1);
    }

    .duo-input::placeholder {
      color: #6B7280;
    }

    /* Slider */
    .duo-slider-wrapper {
      display: flex;
      gap: 16px;
      align-items: center;
    }

    .duo-slider {
      flex: 1;
      height: 6px;
      border-radius: 3px;
      background: rgba(31, 41, 55, 0.8);
      outline: none;
      -webkit-appearance: none;
    }

    .duo-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      cursor: pointer;
      box-shadow: 0 0 10px rgba(88, 204, 2, 0.5);
    }

    .duo-value-box {
      background: rgba(31, 41, 55, 0.6);
      border: 1px solid rgba(255, 255, 255, 0.1);
      border-radius: 12px;
      padding: 10px 16px;
      min-width: 100px;
      text-align: center;
    }

    .duo-value-number {
      display: block;
      font-size: 18px;
      font-weight: 700;
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    .duo-value-label {
      font-size: 10px;
      color: #9CA3AF;
    }

    .duo-slider-info {
      display: flex;
      justify-content: space-between;
      font-size: 11px;
      color: #6B7280;
      margin-top: 8px;
    }

    .duo-slider-info .highlight {
      color: #58CC02;
      font-weight: 600;
    }

    /* Buttons */
    .duo-btn {
      width: 100%;
      padding: 14px;
      border: none;
      border-radius: 12px;
      color: white;
      font-size: 14px;
      font-weight: 600;
      font-family: 'Poppins', sans-serif;
      cursor: pointer;
      transition: all 0.3s;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
    }

    .duo-btn:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }

    .duo-btn-primary {
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      box-shadow: 0 4px 15px rgba(88, 204, 2, 0.3);
    }

    .duo-btn-primary:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 6px 25px rgba(88, 204, 2, 0.4);
    }

    .duo-btn-gold {
      background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
      box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);
    }

    .duo-btn-gold:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 6px 25px rgba(255, 215, 0, 0.4);
    }

    .duo-btn-orange {
      background: linear-gradient(135deg, #FF9500 0%, #FF6B00 100%);
      box-shadow: 0 4px 15px rgba(255, 149, 0, 0.3);
    }

    .duo-btn-purple {
      background: linear-gradient(135deg, #9B59B6 0%, #8E44AD 100%);
      box-shadow: 0 4px 15px rgba(155, 89, 182, 0.3);
    }

    .duo-btn-blue {
      background: linear-gradient(135deg, #1CB0F6 0%, #0095E0 100%);
      box-shadow: 0 4px 15px rgba(28, 176, 246, 0.3);
    }

    .duo-btn-sm {
      padding: 10px 16px;
      font-size: 12px;
    }

    .duo-btn-group {
      display: flex;
      gap: 12px;
    }

    .duo-btn-group .duo-btn {
      flex: 1;
    }

    /* Toggle Switch */
    .duo-toggle-group {
      display: flex;
      background: rgba(31, 41, 55, 0.6);
      border-radius: 12px;
      padding: 4px;
      margin-bottom: 16px;
    }

    .duo-toggle-option {
      flex: 1;
      padding: 10px;
      border: none;
      background: none;
      color: #9CA3AF;
      font-size: 12px;
      font-weight: 600;
      font-family: 'Poppins', sans-serif;
      cursor: pointer;
      border-radius: 8px;
      transition: all 0.2s;
    }

    .duo-toggle-option.active {
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      color: white;
    }

    /* Progress */
    .duo-progress {
      margin-top: 16px;
      padding: 16px;
      background: rgba(31, 41, 55, 0.4);
      border-radius: 12px;
      display: none;
    }

    .duo-progress.show {
      display: block;
    }

    .duo-progress-header {
      display: flex;
      justify-content: space-between;
      margin-bottom: 10px;
      font-size: 12px;
    }

    .duo-progress-label {
      color: #D1D5DB;
      font-weight: 600;
    }

    .duo-progress-percent {
      color: #58CC02;
      font-weight: 700;
    }

    .duo-progress-bar-bg {
      height: 8px;
      background: rgba(31, 41, 55, 0.8);
      border-radius: 4px;
      overflow: hidden;
    }

    .duo-progress-bar {
      height: 100%;
      background: linear-gradient(90deg, #58CC02 0%, #1CB0F6 100%);
      border-radius: 4px;
      width: 0%;
      transition: width 0.3s ease;
    }

    .duo-progress-status {
      margin-top: 10px;
      font-size: 11px;
      color: #9CA3AF;
      text-align: center;
    }

    /* Result Card */
    .duo-result {
      margin-top: 16px;
      padding: 16px;
      border-radius: 12px;
      display: none;
    }

    .duo-result.show {
      display: block;
    }

    .duo-result.success {
      background: rgba(88, 204, 2, 0.1);
      border: 1px solid rgba(88, 204, 2, 0.3);
    }

    .duo-result.error {
      background: rgba(239, 68, 68, 0.1);
      border: 1px solid rgba(239, 68, 68, 0.3);
    }

    .duo-result-text {
      font-size: 13px;
      text-align: center;
    }

    .duo-result.success .duo-result-text {
      color: #58CC02;
    }

    .duo-result.error .duo-result-text {
      color: #EF4444;
    }

    /* Items Grid */
    .duo-items-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 12px;
    }

    .duo-item-card {
      background: rgba(31, 41, 55, 0.4);
      border: 1px solid rgba(255, 255, 255, 0.1);
      border-radius: 12px;
      padding: 12px;
      cursor: pointer;
      transition: all 0.2s;
      text-align: center;
    }

    .duo-item-card:hover {
      border-color: rgba(88, 204, 2, 0.5);
      background: rgba(31, 41, 55, 0.6);
    }

    .duo-item-card.loading {
      opacity: 0.5;
      pointer-events: none;
    }

    .duo-item-icon {
      font-size: 24px;
      margin-bottom: 6px;
    }

    .duo-item-name {
      font-size: 11px;
      color: #D1D5DB;
      font-weight: 500;
    }

    /* Section Divider */
    .duo-divider {
      height: 1px;
      background: rgba(255, 255, 255, 0.1);
      margin: 20px 0;
    }

    /* Info Box */
    .duo-info {
      background: rgba(31, 41, 55, 0.4);
      border-radius: 12px;
      padding: 12px 16px;
      font-size: 12px;
      color: #9CA3AF;
      margin-bottom: 16px;
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .duo-info-icon {
      font-size: 18px;
    }

    /* Warning Box */
    .duo-warning {
      background: rgba(255, 149, 0, 0.1);
      border: 1px solid rgba(255, 149, 0, 0.3);
      border-radius: 12px;
      padding: 12px 16px;
      font-size: 12px;
      color: #FF9500;
      margin-bottom: 16px;
    }

    /* Toast */
    .duo-toast {
      position: fixed;
      bottom: 20px;
      right: 20px;
      max-width: 320px;
      background: rgba(19, 31, 36, 0.98);
      backdrop-filter: blur(20px);
      border-radius: 12px;
      padding: 16px;
      box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
      border: 1px solid rgba(255, 255, 255, 0.1);
      z-index: 1000001;
      display: none;
      animation: slideUp 0.3s ease-out;
    }

    .duo-toast.show {
      display: flex;
      gap: 12px;
      align-items: center;
    }

    @keyframes slideUp {
      from { opacity: 0; transform: translateY(20px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .duo-toast-icon {
      font-size: 24px;
    }

    .duo-toast-content {
      flex: 1;
    }

    .duo-toast-title {
      font-size: 13px;
      font-weight: 600;
      color: white;
      margin: 0 0 4px 0;
    }

    .duo-toast-message {
      font-size: 11px;
      color: #D1D5DB;
      margin: 0;
    }

    /* Token input area */
    .duo-token-section {
      padding: 0 24px 16px;
      background: rgba(0, 0, 0, 0.2);
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .duo-token-toggle {
      padding: 12px;
      background: none;
      border: none;
      color: #6B7280;
      font-size: 12px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 8px;
      width: 100%;
      font-family: 'Poppins', sans-serif;
    }

    .duo-token-toggle:hover {
      color: #D1D5DB;
    }

    .duo-token-content {
      display: none;
      padding-top: 8px;
    }

    .duo-token-content.show {
      display: block;
    }

    .duo-input-wrapper {
      position: relative;
    }

    .duo-input-btn {
      position: absolute;
      right: 12px;
      top: 50%;
      transform: translateY(-50%);
      background: none;
      border: none;
      color: #6B7280;
      cursor: pointer;
      font-size: 16px;
    }

    .duo-input-btn:hover {
      color: #58CC02;
    }

    .duo-hint {
      font-size: 10px;
      color: #6B7280;
      margin-top: 6px;
    }

    /* License Activation Screen */
    .duo-license-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      background: rgba(0, 0, 0, 0.8);
      backdrop-filter: blur(10px);
      z-index: 1000000;
      display: flex;
      align-items: center;
      justify-content: center;
      animation: fadeIn 0.3s ease-out;
    }

    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }

    .duo-license-modal {
      width: 400px;
      background: rgba(19, 31, 36, 0.98);
      border-radius: 24px;
      padding: 32px;
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
      border: 1px solid rgba(255, 255, 255, 0.1);
      animation: scaleIn 0.3s ease-out;
    }

    @keyframes scaleIn {
      from { opacity: 0; transform: scale(0.9); }
      to { opacity: 1; transform: scale(1); }
    }

    .duo-license-header {
      text-align: center;
      margin-bottom: 24px;
    }

    .duo-license-icon {
      font-size: 64px;
      margin-bottom: 16px;
    }

    .duo-license-title {
      font-size: 24px;
      font-weight: 700;
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      margin: 0 0 8px 0;
    }

    .duo-license-subtitle {
      font-size: 14px;
      color: #9CA3AF;
      margin: 0;
    }

    .duo-license-form {
      margin-bottom: 16px;
    }

    .duo-license-input {
      width: 100%;
      padding: 16px;
      background: rgba(31, 41, 55, 0.6);
      border: 2px solid rgba(255, 255, 255, 0.1);
      border-radius: 12px;
      color: white;
      font-size: 16px;
      font-family: 'Poppins', sans-serif;
      text-align: center;
      letter-spacing: 2px;
      box-sizing: border-box;
      transition: all 0.2s;
    }

    .duo-license-input:focus {
      outline: none;
      border-color: #58CC02;
      box-shadow: 0 0 0 4px rgba(88, 204, 2, 0.1);
    }

    .duo-license-input::placeholder {
      color: #6B7280;
      letter-spacing: normal;
    }

    .duo-license-btn {
      width: 100%;
      padding: 16px;
      background: linear-gradient(135deg, #58CC02 0%, #1CB0F6 100%);
      border: none;
      border-radius: 12px;
      color: white;
      font-size: 16px;
      font-weight: 600;
      font-family: 'Poppins', sans-serif;
      cursor: pointer;
      transition: all 0.3s;
      box-shadow: 0 4px 15px rgba(88, 204, 2, 0.3);
    }

    .duo-license-btn:hover:not(:disabled) {
      transform: translateY(-2px);
      box-shadow: 0 6px 25px rgba(88, 204, 2, 0.4);
    }

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

    .duo-license-btn.loading {
      pointer-events: none;
    }

    .duo-license-error {
      margin-top: 16px;
      padding: 12px;
      background: rgba(239, 68, 68, 0.1);
      border: 1px solid rgba(239, 68, 68, 0.3);
      border-radius: 8px;
      color: #EF4444;
      font-size: 13px;
      text-align: center;
      display: none;
    }

    .duo-license-error.show {
      display: block;
    }

    .duo-license-footer {
      margin-top: 20px;
      text-align: center;
      font-size: 12px;
      color: #6B7280;
    }

    .duo-license-footer a {
      color: #58CC02;
      text-decoration: none;
    }

    /* License Badge in Header */
    .duo-license-badge {
      padding: 4px 10px;
      border-radius: 20px;
      font-size: 10px;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      cursor: pointer;
      transition: all 0.2s;
    }

    .duo-license-badge.free {
      background: rgba(107, 114, 128, 0.2);
      color: #9CA3AF;
      border: 1px solid rgba(107, 114, 128, 0.3);
    }

    .duo-license-badge.pro {
      background: linear-gradient(135deg, rgba(88, 204, 2, 0.2) 0%, rgba(28, 176, 246, 0.2) 100%);
      color: #58CC02;
      border: 1px solid rgba(88, 204, 2, 0.3);
    }

    .duo-license-badge:hover {
      transform: scale(1.05);
    }

    /* Icon Buttons (Logout & Close) */
    .duo-icon-btn {
      width: 28px;
      height: 28px;
      border-radius: 6px;
      background: transparent;
      border: none;
      color: #9CA3AF;
      cursor: pointer;
      transition: all 0.2s;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 0;
    }

    .duo-icon-btn:hover {
      background: rgba(255, 255, 255, 0.1);
      color: white;
      transform: translateY(-1px);
    }

    .duo-icon-btn.logout:hover {
      background: rgba(239, 68, 68, 0.15);
      color: #EF4444;
    }

    .duo-icon-btn svg {
      width: 16px;
      height: 16px;
      stroke-width: 2.5;
    }

    /* Pro Feature Lock */
    .duo-pro-lock {
      position: relative;
      opacity: 0.5;
      pointer-events: none;
    }

    .duo-pro-lock::after {
      content: '🔒 PRO';
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: rgba(0, 0, 0, 0.8);
      padding: 8px 16px;
      border-radius: 8px;
      font-size: 12px;
      font-weight: 600;
      color: #FFD700;
    }
  `);

  // =============================================================================
  // UI CREATION
  // =============================================================================

  function createUI() {
    const container = document.createElement('div');
    container.id = 'duo-farm-container';

    container.innerHTML = `
      <button class="duo-toggle-btn" id="duo-toggle">🚀</button>

      <div class="duo-panel" id="duo-panel">
        <!-- Header -->
        <div class="duo-header">
          <div class="duo-header-left">
            <span class="duo-logo">🦉</span>
            <div>
              <h1 class="duo-title">Duolingo Farm Pro</h1>
              <div style="display: flex; align-items: center; gap: 8px;">
                <span class="duo-version">v2.0.0</span>
                <span id="duo-license-days" style="font-size: 10px; color: #9CA3AF; display: none;"></span>
              </div>
            </div>
          </div>
          <div style="display: flex; align-items: center; gap: 12px;">
            <span class="duo-license-badge ${
              state.licensePlan
            }" id="duo-license-badge">
              ${state.licensePlan === 'pro' ? '👑 PRO' : '🔓 FREE'}
            </span>
            <button class="duo-icon-btn logout" id="duo-logout" title="Change License Key">
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
            </button>
            <button class="duo-icon-btn close" id="duo-close" title="Close Panel">
              <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
            </button>
          </div>
        </div>

        <!-- Token Section -->
        <div class="duo-token-section">
          <button class="duo-token-toggle" id="duo-token-toggle">
            🔐 Authorization Token <span style="margin-left:auto">▼</span>
          </button>
          <div class="duo-token-content" id="duo-token-content">
            <div class="duo-input-wrapper">
              <input type="password" class="duo-input" id="duo-token"
                placeholder="Bearer eyJhbGciOiJIUzI1..." value="${
                  state.token
                }"/>
              <button class="duo-input-btn" id="duo-token-visibility">👁️</button>
            </div>
            <div class="duo-hint">Auto-detected from cookies. Click 👁️ to show/hide.</div>
          </div>
        </div>

        <!-- Tabs -->
        <div class="duo-tabs" id="duo-tabs">
          ${TABS.map(
            (tab) => `
            <button class="duo-tab ${
              tab.id === state.activeTab ? 'active' : ''
            }"
              data-tab="${tab.id}" style="--tab-color: ${tab.color}">
              <span class="duo-tab-icon">${tab.icon}</span>
              ${tab.name}
            </button>
          `
          ).join('')}
        </div>

        <!-- Content -->
        <div class="duo-content">
          <!-- XP Tab -->
          <div class="duo-tab-panel ${
            state.activeTab === 'xp' ? 'active' : ''
          }" id="panel-xp">
            <div class="duo-form-group">
              <label class="duo-label">
                <span class="duo-label-dot" style="background:#58CC02"></span>
                XP Amount (Max: ${CONFIG.maxXP.toLocaleString()})
              </label>
              <div class="duo-slider-wrapper">
                <input type="range" class="duo-slider" id="xp-slider"
                  min="0" max="${CONFIG.maxXP}" step="${
      CONFIG.xpStep
    }" value="${state.xpAmount}"/>
                <div class="duo-value-box">
                  <span class="duo-value-number" id="xp-value">${state.xpAmount.toLocaleString()}</span>
                  <span class="duo-value-label">XP</span>
                </div>
              </div>
              <div class="duo-slider-info">
                <span>0</span>
                <span class="highlight" id="xp-requests">${Math.ceil(
                  state.xpAmount / 200
                )} requests</span>
                <span>${CONFIG.maxXP.toLocaleString()}</span>
              </div>
            </div>

            <button class="duo-btn duo-btn-primary" id="xp-start">
              ⚡ Start XP Farm
            </button>

            <div class="duo-progress" id="xp-progress">
              <div class="duo-progress-header">
                <span class="duo-progress-label">Farming XP...</span>
                <span class="duo-progress-percent" id="xp-percent">0%</span>
              </div>
              <div class="duo-progress-bar-bg">
                <div class="duo-progress-bar" id="xp-bar"></div>
              </div>
              <div class="duo-progress-status" id="xp-status">Starting...</div>
            </div>

            <div class="duo-result" id="xp-result">
              <p class="duo-result-text" id="xp-result-text"></p>
            </div>
          </div>

          <!-- Gems Tab -->
          <div class="duo-tab-panel ${
            state.activeTab === 'gems' ? 'active' : ''
          }" id="panel-gems">
            <div class="duo-toggle-group" id="gem-mode-toggle">
              <button class="duo-toggle-option ${
                state.gemMode === 'claim' ? 'active' : ''
              }" data-mode="claim">
                🎁 Claim Rewards
              </button>
              <button class="duo-toggle-option ${
                state.gemMode === 'normal' ? 'active' : ''
              }" data-mode="normal">
                💎 Farm Gems
              </button>
              <button class="duo-toggle-option ${
                state.gemMode === 'fast' ? 'active' : ''
              }" data-mode="fast">
                🚀 Fast Mode
              </button>
            </div>

            <div id="gem-claim-content" style="display:${
              state.gemMode === 'claim' ? 'block' : 'none'
            }">
              <div class="duo-info">
                <span class="duo-info-icon">ℹ️</span>
                Claim all unclaimed gem rewards from completed lessons
              </div>
              <button class="duo-btn duo-btn-gold" id="gem-claim-btn">
                🎁 Claim All Gems
              </button>
            </div>

            <div id="gem-farm-content" style="display:${
              state.gemMode !== 'claim' ? 'block' : 'none'
            }">
              <div id="gem-fast-warning" class="duo-warning" style="display:${
                state.gemMode === 'fast' ? 'block' : 'none'
              }">
                ⚠️ Fast mode uses concurrent requests. Higher risk of rate limiting!
              </div>

              <div class="duo-form-group">
                <label class="duo-label">
                  <span class="duo-label-dot" style="background:#FFD700"></span>
                  Gems Amount
                </label>
                <div class="duo-slider-wrapper">
                  <input type="range" class="duo-slider" id="gem-slider"
                    min="0" max="${CONFIG.maxGems}" step="${
      CONFIG.gemStep
    }" value="${state.gemAmount}"/>
                  <div class="duo-value-box">
                    <span class="duo-value-number" id="gem-value">${state.gemAmount.toLocaleString()}</span>
                    <span class="duo-value-label">Gems</span>
                  </div>
                </div>
                <div class="duo-slider-info">
                  <span>30</span>
                  <span class="highlight" id="gem-requests">${Math.ceil(
                    state.gemAmount / 30
                  )} requests</span>
                  <span>${CONFIG.maxGems.toLocaleString()}</span>
                </div>
              </div>

              <button class="duo-btn duo-btn-gold" id="gem-start">
                💎 Start Gem Farm
              </button>
            </div>

            <div class="duo-progress" id="gem-progress">
              <div class="duo-progress-header">
                <span class="duo-progress-label">Farming Gems...</span>
                <span class="duo-progress-percent" id="gem-percent">0%</span>
              </div>
              <div class="duo-progress-bar-bg">
                <div class="duo-progress-bar" id="gem-bar"></div>
              </div>
              <div class="duo-progress-status" id="gem-status">Starting...</div>
            </div>

            <div class="duo-result" id="gem-result">
              <p class="duo-result-text" id="gem-result-text"></p>
            </div>
          </div>

          <!-- Streak Tab -->
          <div class="duo-tab-panel ${
            state.activeTab === 'streak' ? 'active' : ''
          }" id="panel-streak">
            <div class="duo-info">
              <span class="duo-info-icon">🔥</span>
              Extend your streak by completing backdated practice sessions
            </div>

            <div class="duo-form-group">
              <label class="duo-label">
                <span class="duo-label-dot" style="background:#FF9500"></span>
                Streak Days
              </label>
              <div class="duo-slider-wrapper">
                <input type="range" class="duo-slider" id="streak-slider"
                  min="1" max="${CONFIG.maxStreak}" step="1" value="${
      state.streakDays
    }"/>
                <div class="duo-value-box">
                  <span class="duo-value-number" id="streak-value">${
                    state.streakDays
                  }</span>
                  <span class="duo-value-label">Days</span>
                </div>
              </div>
              <div class="duo-slider-info">
                <span>1</span>
                <span class="highlight">${state.streakDays} sessions</span>
                <span>${CONFIG.maxStreak}</span>
              </div>
            </div>

            <button class="duo-btn duo-btn-orange" id="streak-start">
              🔥 Start Streak Farm
            </button>

            <div class="duo-progress" id="streak-progress">
              <div class="duo-progress-header">
                <span class="duo-progress-label">Extending Streak...</span>
                <span class="duo-progress-percent" id="streak-percent">0%</span>
              </div>
              <div class="duo-progress-bar-bg">
                <div class="duo-progress-bar" id="streak-bar"></div>
              </div>
              <div class="duo-progress-status" id="streak-status">Starting...</div>
            </div>

            <div class="duo-result" id="streak-result">
              <p class="duo-result-text" id="streak-result-text"></p>
            </div>
          </div>

          <!-- Super Tab -->
          <div class="duo-tab-panel ${
            state.activeTab === 'super' ? 'active' : ''
          }" id="panel-super">
            <div class="duo-info">
              <span class="duo-info-icon">👑</span>
              Activate a 3-day Super Duolingo trial (may not work due to detection)
            </div>

            <div class="duo-warning">
              ⚠️ This feature has a high chance of not working. Duolingo has improved their detection.
            </div>

            <button class="duo-btn duo-btn-purple" id="super-activate">
              👑 Activate Super Duolingo
            </button>

            <div class="duo-result" id="super-result">
              <p class="duo-result-text" id="super-result-text"></p>
            </div>
          </div>

          <!-- Items Tab -->
          <div class="duo-tab-panel ${
            state.activeTab === 'items' ? 'active' : ''
          }" id="panel-items">
            <div class="duo-info">
              <span class="duo-info-icon">🎁</span>
              Click on any item to claim it for free
            </div>

            <div class="duo-items-grid" id="items-grid">
              ${SHOP_ITEMS.map(
                (item) => `
                <div class="duo-item-card" data-item-id="${item.id}" style="--item-color:${item.color}">
                  <div class="duo-item-icon">${item.icon}</div>
                  <div class="duo-item-name">${item.name}</div>
                </div>
              `
              ).join('')}
            </div>

            <div class="duo-result" id="item-result">
              <p class="duo-result-text" id="item-result-text"></p>
            </div>
          </div>
        </div>
      </div>

      <!-- Toast -->
      <div class="duo-toast" id="duo-toast">
        <span class="duo-toast-icon" id="toast-icon">✅</span>
        <div class="duo-toast-content">
          <p class="duo-toast-title" id="toast-title">Success!</p>
          <p class="duo-toast-message" id="toast-message">Action completed.</p>
        </div>
      </div>
    `;

    document.body.appendChild(container);
    bindEvents();
  }

  // =============================================================================
  // EVENT BINDINGS
  // =============================================================================

  function bindEvents() {
    // Toggle panel
    document.getElementById('duo-toggle').addEventListener('click', () => {
      const panel = document.getElementById('duo-panel');
      const toggle = document.getElementById('duo-toggle');
      panel.classList.toggle('show');
      toggle.style.display = panel.classList.contains('show') ? 'none' : 'flex';
    });

    document.getElementById('duo-close').addEventListener('click', () => {
      document.getElementById('duo-panel').classList.remove('show');
      document.getElementById('duo-toggle').style.display = 'flex';
    });

    // Token section toggle
    document
      .getElementById('duo-token-toggle')
      .addEventListener('click', () => {
        document.getElementById('duo-token-content').classList.toggle('show');
      });

    // Token visibility
    document
      .getElementById('duo-token-visibility')
      .addEventListener('click', () => {
        const input = document.getElementById('duo-token');
        input.type = input.type === 'password' ? 'text' : 'password';
      });

    // Token change
    document.getElementById('duo-token').addEventListener('change', (e) => {
      state.token = e.target.value;
      GM_setValue('auth_token', state.token);
    });

    // Logout (change license key)
    document.getElementById('duo-logout').addEventListener('click', () => {
      logoutLicense();
    });

    // Tabs
    document.getElementById('duo-tabs').addEventListener('click', (e) => {
      const tab = e.target.closest('.duo-tab');
      if (!tab) return;

      const tabId = tab.dataset.tab;
      state.activeTab = tabId;

      document
        .querySelectorAll('.duo-tab')
        .forEach((t) => t.classList.remove('active'));
      document
        .querySelectorAll('.duo-tab-panel')
        .forEach((p) => p.classList.remove('active'));

      tab.classList.add('active');
      document.getElementById(`panel-${tabId}`).classList.add('active');
    });

    // XP slider
    document.getElementById('xp-slider').addEventListener('input', (e) => {
      state.xpAmount = parseInt(e.target.value);
      document.getElementById('xp-value').textContent =
        state.xpAmount.toLocaleString();
      document.getElementById('xp-requests').textContent = `${Math.ceil(
        state.xpAmount / 200
      )} requests`;
    });

    // XP start
    document
      .getElementById('xp-start')
      .addEventListener('click', () => farmXP());

    // Gem mode toggle
    document
      .getElementById('gem-mode-toggle')
      .addEventListener('click', (e) => {
        const option = e.target.closest('.duo-toggle-option');
        if (!option) return;

        const mode = option.dataset.mode;
        state.gemMode = mode;

        document
          .querySelectorAll('#gem-mode-toggle .duo-toggle-option')
          .forEach((o) => o.classList.remove('active'));
        option.classList.add('active');

        document.getElementById('gem-claim-content').style.display =
          mode === 'claim' ? 'block' : 'none';
        document.getElementById('gem-farm-content').style.display =
          mode !== 'claim' ? 'block' : 'none';
        document.getElementById('gem-fast-warning').style.display =
          mode === 'fast' ? 'block' : 'none';
      });

    // Gem slider
    document.getElementById('gem-slider').addEventListener('input', (e) => {
      state.gemAmount = parseInt(e.target.value);
      document.getElementById('gem-value').textContent =
        state.gemAmount.toLocaleString();
      document.getElementById('gem-requests').textContent = `${Math.ceil(
        state.gemAmount / 30
      )} requests`;
    });

    // Gem claim
    document
      .getElementById('gem-claim-btn')
      .addEventListener('click', () => claimGems());

    // Gem start
    document
      .getElementById('gem-start')
      .addEventListener('click', () => farmGems());

    // Streak slider
    document.getElementById('streak-slider').addEventListener('input', (e) => {
      state.streakDays = parseInt(e.target.value);
      document.getElementById('streak-value').textContent = state.streakDays;
    });

    // Streak start
    document
      .getElementById('streak-start')
      .addEventListener('click', () => farmStreak());

    // Super activate
    document
      .getElementById('super-activate')
      .addEventListener('click', () => activateSuper());

    // Items
    document.getElementById('items-grid').addEventListener('click', (e) => {
      const card = e.target.closest('.duo-item-card');
      if (!card || card.classList.contains('loading')) return;
      claimItem(card.dataset.itemId, card);
    });
  }

  // =============================================================================
  // API FUNCTIONS
  // =============================================================================

  /**
   * Helper to handle SSE streaming (new format with { type, data, timestamp })
   */
  async function handleSSEStream(url, options, callbacks) {
    const { onStart, onProgress, onSuccess, onError } = callbacks;

    try {
      const response = await fetch(url, {
        ...options,
        headers: {
          Authorization: state.token,
          'Content-Type': 'application/json',
          'X-License-Key': state.licenseKey,
          ...options.headers,
        },
      });

      if (!response.ok) {
        if (response) {
          const errorData = await response.json();
          // New API format: { statusCode, message, data, date }
          throw new Error(
            errorData.message || errorData.error || 'Request failed'
          );
        }
        throw new Error('Something went wrong');
      }

      const reader = response.body.getReader();
      const decoder = new TextDecoder();

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value);
        const lines = chunk
          .split('\n')
          .filter((line) => line.startsWith('data: '));

        for (const line of lines) {
          try {
            // New SSE format: { type, data: {...}, timestamp }
            const event = JSON.parse(line.slice(6));
            const eventData = event.data || event;

            switch (event.type) {
              case 'start':
                onStart?.(eventData);
                break;
              case 'progress':
                onProgress?.(eventData);
                break;
              case 'success':
                onSuccess?.(eventData);
                break;
              case 'error':
                onError?.(eventData);
                break;
            }
          } catch (e) {
            console.error('Failed to parse SSE data:', e);
          }
        }
      }
    } catch (error) {
      console.error('Failed to handle SSE stream:', error);
      onError?.({ message: error.message });
    }
  }

  /**
   * Farm XP
   */
  async function farmXP() {
    if (state.isRunning) return;
    state.isRunning = true;

    const btn = document.getElementById('xp-start');
    const progress = document.getElementById('xp-progress');
    const result = document.getElementById('xp-result');

    btn.disabled = true;
    btn.innerHTML = '⏳ Farming...';
    progress.classList.add('show');
    result.classList.remove('show');

    await handleSSEStream(
      ENDPOINTS.addXp,
      {
        method: 'POST',
        body: JSON.stringify({ xp: state.xpAmount }),
      },
      {
        onStart: () => {
          document.getElementById('xp-status').textContent = 'Starting...';
        },
        onProgress: (data) => {
          const percent = data.progress || 0;
          document.getElementById('xp-percent').textContent = `${percent}%`;
          document.getElementById('xp-bar').style.width = `${percent}%`;
          document.getElementById(
            'xp-status'
          ).textContent = `Progress: ${percent}%`;
        },
        onSuccess: (data) => {
          result.classList.add('show', 'success');
          result.classList.remove('error');
          document.getElementById('xp-result-text').textContent = `✅ ${
            data.message || 'XP farmed successfully!'
          }`;
          showToast(
            'Success!',
            `Farmed ${state.xpAmount.toLocaleString()} XP`,
            '✅'
          );
        },
        onError: (data) => {
          result.classList.add('show', 'error');
          result.classList.remove('success');
          document.getElementById(
            'xp-result-text'
          ).textContent = `❌ ${data.message}`;
          showToast('Error', data.message, '❌');
        },
      }
    );

    btn.disabled = false;
    btn.innerHTML = '⚡ Start XP Farm';
    state.isRunning = false;
  }

  /**
   * Helper to check if API response is successful (new format)
   * API format: { statusCode, message, data, date }
   */
  function isApiSuccess(response) {
    return response.statusCode >= 200 && response.statusCode < 300;
  }

  /**
   * Claim Gems
   */
  async function claimGems() {
    if (state.isRunning) return;
    state.isRunning = true;

    const btn = document.getElementById('gem-claim-btn');
    const result = document.getElementById('gem-result');

    btn.disabled = true;
    btn.innerHTML = '⏳ Claiming...';
    result.classList.remove('show');

    try {
      const response = await fetch(
        `${ENDPOINTS.claimGems}?userId=${state.userId}`,
        {
          method: 'GET',
          headers: {
            Authorization: state.token,
            'X-License-Key': state.licenseKey,
          },
        }
      );

      const data = await response.json();

      // New API format: { statusCode, message, data, date }
      if (isApiSuccess(data)) {
        result.classList.add('show', 'success');
        result.classList.remove('error');
        document.getElementById(
          'gem-result-text'
        ).textContent = `✅ ${data.message}`;
        showToast('Success!', data.message, '✅');
      } else {
        throw new Error(data.message || 'Failed to claim gems');
      }
    } catch (error) {
      result.classList.add('show', 'error');
      result.classList.remove('success');
      document.getElementById(
        'gem-result-text'
      ).textContent = `❌ ${error.message}`;
      showToast('Error', error.message, '❌');
    }

    btn.disabled = false;
    btn.innerHTML = '🎁 Claim All Gems';
    state.isRunning = false;
  }

  /**
   * Farm Gems
   */
  async function farmGems() {
    if (state.isRunning) return;
    state.isRunning = true;

    const btn = document.getElementById('gem-start');
    const progress = document.getElementById('gem-progress');
    const result = document.getElementById('gem-result');

    btn.disabled = true;
    btn.innerHTML = '⏳ Farming...';
    progress.classList.add('show');
    result.classList.remove('show');

    const endpoint =
      state.gemMode === 'fast' ? ENDPOINTS.farmGemsFast : ENDPOINTS.farmGems;

    await handleSSEStream(
      endpoint,
      {
        method: 'POST',
        body: JSON.stringify({
          userId: state.userId,
          amount: state.gemAmount,
          ...(state.gemMode === 'fast' ? { concurrency: 10 } : {}),
        }),
      },
      {
        onStart: () => {
          document.getElementById('gem-status').textContent = 'Starting...';
        },
        onProgress: (data) => {
          const percent = data.progress || 0;
          document.getElementById('gem-percent').textContent = `${percent}%`;
          document.getElementById('gem-bar').style.width = `${percent}%`;
          document.getElementById('gem-status').textContent =
            data.message || `Progress: ${percent}%`;
        },
        onSuccess: (data) => {
          result.classList.add('show', 'success');
          result.classList.remove('error');
          document.getElementById(
            'gem-result-text'
          ).textContent = `✅ ${data.message}`;
          showToast('Success!', data.message, '💎');
        },
        onError: (data) => {
          result.classList.add('show', 'error');
          result.classList.remove('success');
          document.getElementById(
            'gem-result-text'
          ).textContent = `❌ ${data.message}`;
          showToast('Error', data.message, '❌');
        },
      }
    );

    btn.disabled = false;
    btn.innerHTML = '💎 Start Gem Farm';
    state.isRunning = false;
  }

  /**
   * Farm Streak
   */
  async function farmStreak() {
    if (state.isRunning) return;
    state.isRunning = true;

    const btn = document.getElementById('streak-start');
    const progress = document.getElementById('streak-progress');
    const result = document.getElementById('streak-result');

    btn.disabled = true;
    btn.innerHTML = '⏳ Farming...';
    progress.classList.add('show');
    result.classList.remove('show');

    await handleSSEStream(
      ENDPOINTS.farmStreak,
      {
        method: 'POST',
        body: JSON.stringify({ days: state.streakDays }),
      },
      {
        onStart: () => {
          document.getElementById('streak-status').textContent = 'Starting...';
        },
        onProgress: (data) => {
          const percent = data.progress || 0;
          document.getElementById('streak-percent').textContent = `${percent}%`;
          document.getElementById('streak-bar').style.width = `${percent}%`;
          document.getElementById('streak-status').textContent =
            data.message || `Progress: ${percent}%`;
        },
        onSuccess: (data) => {
          result.classList.add('show', 'success');
          result.classList.remove('error');
          document.getElementById(
            'streak-result-text'
          ).textContent = `✅ ${data.message}`;
          showToast('Success!', data.message, '🔥');
        },
        onError: (data) => {
          result.classList.add('show', 'error');
          result.classList.remove('success');
          document.getElementById(
            'streak-result-text'
          ).textContent = `❌ ${data.message}`;
          showToast('Error', data.message, '❌');
        },
      }
    );

    btn.disabled = false;
    btn.innerHTML = '🔥 Start Streak Farm';
    state.isRunning = false;
  }

  /**
   * Activate Super Duolingo
   */
  async function activateSuper() {
    if (state.isRunning) return;
    state.isRunning = true;

    const btn = document.getElementById('super-activate');
    const result = document.getElementById('super-result');

    btn.disabled = true;
    btn.innerHTML = '⏳ Activating...';
    result.classList.remove('show');

    try {
      const response = await fetch(ENDPOINTS.activateSuper, {
        method: 'POST',
        headers: {
          Authorization: state.token,
          'Content-Type': 'application/json',
          'X-License-Key': state.licenseKey,
        },
        body: JSON.stringify({ userId: state.userId }),
      });

      const data = await response.json();

      // New API format: { statusCode, message, data, date }
      if (isApiSuccess(data)) {
        result.classList.add('show', 'success');
        result.classList.remove('error');
        document.getElementById(
          'super-result-text'
        ).textContent = `✅ ${data.message}`;
        showToast('Success!', data.message, '👑');
      } else {
        throw new Error(data.message || 'Failed to activate Super');
      }
    } catch (error) {
      result.classList.add('show', 'error');
      result.classList.remove('success');
      document.getElementById(
        'super-result-text'
      ).textContent = `❌ ${error.message}`;
      showToast('Error', error.message, '❌');
    }

    btn.disabled = false;
    btn.innerHTML = '👑 Activate Super Duolingo';
    state.isRunning = false;
  }

  /**
   * Claim Shop Item
   */
  async function claimItem(itemId, cardElement) {
    if (state.isRunning) return;
    state.isRunning = true;

    const result = document.getElementById('item-result');
    cardElement.classList.add('loading');
    result.classList.remove('show');

    try {
      const response = await fetch(ENDPOINTS.claimItem, {
        method: 'POST',
        headers: {
          Authorization: state.token,
          'Content-Type': 'application/json',
          'X-License-Key': state.licenseKey,
        },
        body: JSON.stringify({ userId: state.userId, itemId }),
      });

      const data = await response.json();

      // New API format: { statusCode, message, data, date }
      if (isApiSuccess(data)) {
        result.classList.add('show', 'success');
        result.classList.remove('error');
        document.getElementById(
          'item-result-text'
        ).textContent = `✅ ${data.message}`;
        showToast('Success!', data.message, '🎁');
      } else {
        throw new Error(data.message || 'Failed to claim item');
      }
    } catch (error) {
      result.classList.add('show', 'error');
      result.classList.remove('success');
      document.getElementById(
        'item-result-text'
      ).textContent = `❌ ${error.message}`;
      showToast('Error', error.message, '❌');
    }

    cardElement.classList.remove('loading');
    state.isRunning = false;
  }

  /**
   * Show Toast Notification
   */
  function showToast(title, message, icon) {
    const toast = document.getElementById('duo-toast');
    document.getElementById('toast-icon').textContent = icon;
    document.getElementById('toast-title').textContent = title;
    document.getElementById('toast-message').textContent = message;

    toast.classList.add('show');

    setTimeout(() => {
      toast.classList.remove('show');
    }, 3000);
  }

  // =============================================================================
  // LICENSE FUNCTIONS
  // =============================================================================

  /**
   * Create License Activation UI
   */
  function createLicenseUI() {
    const overlay = document.createElement('div');
    overlay.className = 'duo-license-overlay';
    overlay.id = 'duo-license-overlay';

    overlay.innerHTML = `
      <div class="duo-license-modal">
        <div class="duo-license-header">
          <div class="duo-license-icon">🔑</div>
          <h2 class="duo-license-title">Activate License</h2>
          <p class="duo-license-subtitle">Enter your license key to unlock all features</p>
        </div>

        <div class="duo-license-form">
          <input type="text" class="duo-license-input" id="license-key-input"
            placeholder="Enter license key..." autocomplete="off" />
          <p style="font-size: 11px; color: #6B7280; margin-top: 6px; margin-left: 2px;">
            Enter <code style="background: rgba(88, 204, 2, 0.1); color: #58CC02; padding: 2px 4px; border-radius: 4px;">free_trial</code> for Free plan
          </p>
        </div>

        <button class="duo-license-btn" id="license-verify-btn">
          🚀 Activate License
        </button>

        <div class="duo-license-error" id="license-error"></div>

        <div class="duo-license-footer">
          <p>Don't have a key? <a href="#" id="license-skip">Continue with FREE plan</a></p>
        </div>
      </div>
    `;

    document.body.appendChild(overlay);
    bindLicenseEvents();
  }

  /**
   * Bind License UI Events
   */
  function bindLicenseEvents() {
    // Verify button click
    document
      .getElementById('license-verify-btn')
      .addEventListener('click', async () => {
        const input = document.getElementById('license-key-input');
        const key = input.value.trim();

        if (!key) {
          showLicenseError('Please enter a license key');
          return;
        }

        await verifyAndActivateLicense(key);
      });

    // Enter key submit
    document
      .getElementById('license-key-input')
      .addEventListener('keypress', async (e) => {
        if (e.key === 'Enter') {
          const key = e.target.value.trim();
          if (key) {
            await verifyAndActivateLicense(key);
          }
        }
      });

    // Skip to free plan
    // Skip to free plan (Activate free_trial key)
    document
      .getElementById('license-skip')
      .addEventListener('click', async (e) => {
        e.preventDefault();
        const input = document.getElementById('license-key-input');
        input.value = 'free_trial'; // Show it in input
        await verifyAndActivateLicense('free_trial');
      });
  }

  /**
   * Verify License Key with Backend
   * API format: { statusCode, message, data: { valid, plan, username, features, expiresAt }, date }
   */
  async function verifyAndActivateLicense(key) {
    const btn = document.getElementById('license-verify-btn');
    const originalText = btn.innerHTML;

    btn.disabled = true;
    btn.classList.add('loading');
    btn.innerHTML = '⏳ Verifying...';

    try {
      const response = await fetch(ENDPOINTS.verifyLicense, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ key }),
      });

      const apiResponse = await response.json();

      // New API format: { statusCode, message, data, date }
      if (isApiSuccess(apiResponse) && apiResponse.data?.license?.valid) {
        const license = apiResponse.data.license;

        // Save license info
        state.licenseKey = key;
        state.licenseValid = true;
        state.licensePlan = license.plan;
        state.licenseUsername = license.username || '';
        state.licenseFeatures = license.features || [];
        state.licenseExpiresAt = license.expiresAt || null;

        // Persist to storage
        GM_setValue('license_key', key);

        // Hide license UI and update main UI
        hideLicenseUI();
        updateUIForPlan();

        showToast(
          'License Activated!',
          `Welcome ${
            state.licenseUsername || 'User'
          }! You have ${state.licensePlan.toUpperCase()} plan`,
          state.licensePlan === 'pro' ? '👑' : '🔓'
        );
      } else {
        showLicenseError(apiResponse.message || 'Invalid license key');
      }
    } catch (error) {
      console.error('License verification failed:', error);
      showLicenseError('Failed to verify license. Please try again.');
    }

    btn.disabled = false;
    btn.classList.remove('loading');
    btn.innerHTML = originalText;
  }

  /**
   * Show License Error Message
   */
  function showLicenseError(message) {
    const errorEl = document.getElementById('license-error');
    errorEl.textContent = message;
    errorEl.classList.add('show');

    setTimeout(() => {
      errorEl.classList.remove('show');
    }, 5000);
  }

  /**
   * Hide License Overlay
   */
  function hideLicenseUI() {
    const overlay = document.getElementById('duo-license-overlay');
    if (overlay) {
      overlay.style.animation = 'fadeIn 0.3s ease-out reverse';
      setTimeout(() => {
        overlay.remove();
      }, 300);
    }
  }

  /**
   * Show License Overlay (for re-activation)
   */
  function showLicenseUI() {
    const existingOverlay = document.getElementById('duo-license-overlay');
    if (!existingOverlay) {
      createLicenseUI();
    }
  }

  /**
   * Logout and clear license key
   */
  function logoutLicense() {
    // Clear saved license
    GM_setValue('license_key', '');

    // Reset state
    state.licenseKey = '';
    state.licenseValid = false;
    state.licensePlan = 'free';
    state.licenseUsername = '';
    state.licenseFeatures = [];
    state.licenseExpiresAt = null;

    // Update UI
    updateUIForPlan();

    // Show license activation screen
    showLicenseUI();

    showToast('Logged Out', 'Please enter a new license key', '🔄');
  }

  /**
   * Update UI Based on License Plan
   */
  function updateUIForPlan() {
    // Update license badge
    const badge = document.getElementById('duo-license-badge');
    if (badge) {
      badge.className = `duo-license-badge ${state.licensePlan}`;
      badge.innerHTML = state.licensePlan === 'pro' ? '👑 PRO' : '🔓 FREE';
    }

    // Show expiration days
    const daysEl = document.getElementById('duo-license-days');
    if (daysEl) {
      if (state.licenseExpiresAt) {
        const expires = new Date(state.licenseExpiresAt);
        const now = new Date();
        const diffTime = expires - now;
        const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

        if (diffDays > 0) {
          daysEl.textContent = `• ${diffDays} days left`;
          daysEl.style.display = 'inline';
          daysEl.style.color = diffDays <= 3 ? '#EF4444' : '#9CA3AF'; // Red if <= 3 days
        } else {
          daysEl.style.display = 'none';
        }
      } else if (state.licensePlan === 'pro' || state.licensePlan === 'free') {
        // Lifetime
        daysEl.textContent = '• Lifetime';
        daysEl.style.display = 'inline';
        daysEl.style.color = '#58CC02';
      } else {
        daysEl.style.display = 'none';
      }
    }

    // Lock/unlock pro features based on plan
    if (state.licensePlan !== 'pro') {
      // Find tabs that require pro
      const proTabs = ['streak', 'super', 'items'];
      proTabs.forEach((tabId) => {
        const panel = document.getElementById(`panel-${tabId}`);
        if (panel) {
          panel.classList.add('duo-pro-lock');
        }
      });

      // Lock fast gems mode button
      const fastGemBtn = document.querySelector('[data-mode="fast"]');
      if (fastGemBtn) {
        fastGemBtn.classList.add('duo-pro-lock');
        fastGemBtn.disabled = true;
      }
    } else {
      // Unlock all for pro
      document.querySelectorAll('.duo-pro-lock').forEach((el) => {
        el.classList.remove('duo-pro-lock');
        if (el.disabled) el.disabled = false;
      });
    }
  }

  /**
   * Check Saved License on Startup
   * API format: { statusCode, message, data: { valid, plan, username, features, expiresAt }, date }
   */
  async function checkSavedLicense() {
    const savedKey = state.licenseKey;

    if (savedKey) {
      try {
        const response = await fetch(ENDPOINTS.verifyLicense, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ key: savedKey }),
        });

        const apiResponse = await response.json();

        // New API format: { statusCode, message, data, date }
        if (isApiSuccess(apiResponse) && apiResponse.data?.license?.valid) {
          const license = apiResponse.data.license;
          state.licenseValid = true;
          state.licensePlan = license.plan;
          state.licenseUsername = license.username || '';
          state.licenseFeatures = license.features || [];
          state.licenseExpiresAt = license.expiresAt || null;
          updateUIForPlan();
          console.log(
            `[Duolingo Farm Pro] License verified: ${state.licensePlan.toUpperCase()} plan`
          );
          return true;
        }
      } catch (error) {
        console.error(
          '[Duolingo Farm Pro] Failed to verify saved license:',
          error
        );
      }
    }

    // No valid license, show activation UI
    createLicenseUI();
    return false;
  }

  // =============================================================================
  // INITIALIZATION
  // =============================================================================

  createUI();
  checkSavedLicense();
  console.log('[Duolingo Farm Pro] v2.0.0 loaded successfully!');
})();