Repository: hhaayykk/BlessedWizard Branch: main Commit: 3ee882c03c65 Files: 11 Total size: 141.4 KB Directory structure: gitextract_to00y2ya/ ├── .gitattributes ├── .gitignore ├── README.md ├── config.js ├── index.html ├── main.js ├── main.py ├── promocodes.js ├── requirements.txt ├── spooky.m4a └── style.css ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto ================================================ FILE: .gitignore ================================================ spooky.mp4 ================================================ FILE: README.md ================================================ # 🧙‍♂️ Blessed Wizard **Blessed Wizard** is a **Telegram Mini App slot machine game** combined with a **Python Telegram Bot** that launches the app, manages referrals, and processes payments. ⚠️ Gambling-style mechanics. **18+ only.** Ensure compliance with local laws and regulations. --- Official website - blessedwizard.site изображение ## Overview - Telegram Mini App slot game - Entry via Telegram Bot (`/start`) - Referral system with bonuses - Telegram Stars deposits - Cryptocurrency withdrawals - Firebase backend --- ## Features - 🎰 5 reels / 20 paylines - 📊 Configurable RTP (default 96%) - 🔗 Referral system (`/start ref`) - 🎁 Promocodes & bonuses - 💰 Telegram Stars payments - 🔔 Withdrawal notifications via bot - 🌍 TON / BTC / ETH / USDT withdrawals - 📱 Mobile-first Mini App UI --- ## Architecture Telegram Bot (Python) → Telegram Mini App (Web) → Firebase Firestore --- ## Tech Stack WebApp: HTML, CSS, JavaScript, Telegram WebApp API Bot: Python 3, python-telegram-bot v20.7 Backend: Firebase Firestore --- ## Installation git clone https://github.com/hhaayykk/BlessedWizard.git cd BlessedWizard --- ## Configuration - config.js — Firebase credentials - main.js — Game logic and RTP - main.py — Bot token and Mini App URL --- ## Usage 1. Open the Telegram bot 2. Run /start 3. Mini App launches 4. Play slot machine 5. Deposit via Telegram Stars 6. Withdraw winnings --- ## Deployment - WebApp: Netlify / Vercel / GitHub Pages - Bot: python bot.py - Set Mini App URL via @BotFather --- ## Security Notes - Never commit real API keys - Validate withdrawals - Apply limits and anti-abuse checks ================================================ FILE: config.js ================================================ // Firebase Configuration export const firebaseConfig = { apiKey: "###", authDomain: "###", projectId: "###", storageBucket: "###", messagingSenderId: "###", appId: "###", measurementId: "###" }; ================================================ FILE: index.html ================================================ Blessed Wizard

Bet

1

Star Value

1.00

Total Bet

20.00
Bet (Stars/Line)
1
Star Value
1.00
Total Bet
20.00
Auto Spins
5
Quick Spin

Paytable

Wins are calculated across 20 paylines. Match 3, 4, or 5 symbols from the left reel.

Loop: 5x - 37.5, 4x - 7.5, 3x - 3.75
Magic Ball: 5x - 25.0, 4x - 5.0, 3x - 2.5
Book: 5x - 20.0, 4x - 4.0, 3x - 2.0
Hat: 5x - 15.0, 4x - 3.0, 3x - 1.5
Lighter: 5x - 10.0, 4x - 2.0, 3x - 1.0
Note: 5x - 7.5, 4x - 1.5, 3x - 0.75
Poison: 5x - 5.0, 4x - 1.0, 3x - 0.5

Total Win = (Bet per line × Symbol Multiplier) × Number of lines.

================================================ FILE: main.js ================================================ import { firebaseConfig, TELEGRAM_BOT_TOKEN, TELEGRAM_GROUP_CHAT_ID, BOT_USERNAME } from './config.js'; import { PROMOCODES } from './promocodes.js'; // ← ADD THIS LINE import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-app.js'; import { getFirestore, doc, setDoc, getDoc, updateDoc, serverTimestamp } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-firestore.js'; const app = initializeApp(firebaseConfig); const db = getFirestore(app); // =============================== // RTP System Configuration (Dog House Style) // =============================== const RTP_CONFIG = { TARGET_RTP: 96.0, MIN_RTP: 95.5, MAX_RTP: 96.5, BONUS_TRIGGER_CHANCE: 0.05, SMALL_WIN_MULTIPLIER_RANGE: [0.2, 0.8], MEDIUM_WIN_MULTIPLIER_RANGE: [1.0, 3.0], BIG_WIN_MULTIPLIER_RANGE: [5.0, 15.0], MEGA_WIN_MULTIPLIER_RANGE: [20.0, 50.0], LOSS_STREAK_BONUS_TRIGGER: 6, ADJUSTMENT_SPEED: 0.3 }; let sessionStats = { totalBet: 0, totalWon: 0, spinCount: 0, lossStreak: 0, lastBigWin: 0, currentRTP: 100.0 }; // =============================== // Game Variables // =============================== let balance = 0; let coins_per_line = 1; let coin_value = 1.0; let total_bet = 25 * coins_per_line * coin_value; let isSpinning = false; let isAutoSpinning = false; let autoSpinEnabled = false; let remainingSpins = 0; let autoSpins = 5; let quickSpin = false; let transactionHistory = []; let telegramId = null; const MIN_WITHDRAW = 2000; const TRANSACTION_COOLDOWN = 6 * 60 * 60 * 1000; const REFERRAL_BONUS_REFERRER = 15.0; const REFERRAL_BONUS_NEW_USER = 15.0; const SPECIAL_REFERRAL_CODE = "dhs92bfjdjdsdf"; const SPECIAL_REFERRAL_BONUS = 100.0; // Background Music let backgroundMusic = null; let isMusicPlaying = false; // Symbols const symbols = [ { name: 'petla', src: 'images/avelavokmagic.PNG', weight: 1 }, { name: 'magicball', src: 'images/ballmagic.png', weight: 2 }, { name: 'book', src: 'images/bookmagic.png', weight: 3 }, { name: 'hat', src: 'images/hatmagic.png', weight: 5 }, { name: 'lighter', src: 'images/lightermagic.PNG', weight: 8 }, { name: 'note', src: 'images/notemagic.png', weight: 12 }, { name: 'poison', src: 'images/poisonmagic.png', weight: 15 } ]; let SYMBOL_HEIGHT = 70; const VISIBLE_LINES = 3; const CENTER_INDEX = 10; const paylines = [ [0, 0, 0, 0, 0], [1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [0, 1, 2, 1, 0], [2, 1, 0, 1, 2], [0, 0, 1, 2, 2], [2, 2, 1, 0, 0], [1, 0, 0, 1, 1], [1, 2, 2, 1, 0], [0, 1, 1, 2, 2], [2, 1, 1, 0, 0], [0, 0, 0, 1, 2], [2, 2, 2, 1, 0], [1, 1, 0, 0, 1], [1, 1, 2, 2, 1], [0, 1, 2, 2, 2], [2, 1, 0, 0, 0], [0, 0, 1, 1, 2], [2, 2, 1, 1, 0], [1, 0, 1, 2, 1] ]; const paytable = { 'petla': { 3: 5, 4: 15, 5: 100 }, 'magicball': { 3: 4, 4: 10, 5: 50 }, 'book': { 3: 3, 4: 8, 5: 30 }, 'hat': { 3: 2, 4: 5, 5: 20 }, 'lighter': { 3: 1.5, 4: 4, 5: 15 }, 'note': { 3: 1, 4: 3, 5: 10 }, 'poison': { 3: 0.8, 4: 2, 5: 8 } }; let userProfile = { name: "Anonymous", telegramId: "Not Set", registrationDate: "Not Set", language: "en", avatarUrl: "images/default_avatar.jpg" }; // DOM Elements const gameContainer = document.getElementById('gameContainer'); const leadersContainer = document.getElementById('leadersContainer'); const balanceContainer = document.getElementById('balanceContainer'); const friendsContainer = document.getElementById('friendsContainer'); const profileContainer = document.getElementById('profileContainer'); const spinButton = document.getElementById('spinButton'); const autoButton = document.getElementById('autoButton'); const betSettingsButton = document.getElementById('betSettingsButton'); const paytableButton = document.getElementById('paytableButton'); const maxBetButton = document.getElementById('maxBetButton'); const okButton = document.getElementById('okButton'); const okAutoButton = document.getElementById('okAutoButton'); const okPaytableButton = document.getElementById('okPaytableButton'); const toggleAutoSpin = document.getElementById('toggleAutoSpin'); const decreaseAutoSpins = document.getElementById('decreaseAutoSpins'); const increaseAutoSpins = document.getElementById('increaseAutoSpins'); const autoSpinsModal = document.getElementById('autoSpinsModal'); const quickSpinCheckbox = document.getElementById('quickSpinCheckbox'); const autoSpinsRow = document.getElementById('autoSpinsRow'); const quickSpinRow = document.getElementById('quickSpinRow'); const dimmedOverlay = document.getElementById('dimmedOverlay'); const resultOverlay = document.getElementById('resultOverlay'); const resultText = document.getElementById('resultText'); const betSettingsModal = document.getElementById('betSettingsModal'); const autoSettingsModal = document.getElementById('autoSettingsModal'); const paytableModal = document.getElementById('paytableModal'); const reels = [ document.getElementById('reel1'), document.getElementById('reel2'), document.getElementById('reel3'), document.getElementById('reel4'), document.getElementById('reel5') ]; const betLevelDisplay = document.getElementById('betLevel'); const coinValueDisplay = document.getElementById('coinValue'); const totalBetDisplay = document.getElementById('totalBet'); const betLevelModal = document.getElementById('betLevelModal'); const coinValueModal = document.getElementById('coinValueModal'); const totalBetModal = document.getElementById('totalBetModal'); const decreaseBetLevel = document.getElementById('decreaseBetLevel'); const increaseBetLevel = document.getElementById('increaseBetLevel'); const decreaseCoinValue = document.getElementById('decreaseCoinValue'); const increaseCoinValue = document.getElementById('increaseCoinValue'); // =============================== // Telegram Notification Function // =============================== async function sendTelegramNotification(withdrawalData) { try { const message = ` 🔔 NEW WITHDRAWAL REQUEST 🔔 👤 User: ${withdrawalData.userName} 🆔 User ID: ${withdrawalData.userId} 💰 Amount: ${Math.floor(withdrawalData.amount)} Stars 💳 Method: ${withdrawalData.method} 📍 Wallet Address: ${withdrawalData.walletAddress}Time: ${new Date(withdrawalData.timestamp).toLocaleString()} 📊 Status: ${withdrawalData.status.toUpperCase()} ━━━━━━━━━━━━━━━━━━━━ `; const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ chat_id: TELEGRAM_GROUP_CHAT_ID, text: message, parse_mode: 'HTML' }) }); const data = await response.json(); if (data.ok) { console.log("✅ Telegram notification sent successfully"); return true; } else { console.error("❌ Telegram API error:", data); return false; } } catch (error) { console.error("❌ Error sending Telegram notification:", error); return false; } } // =============================== // RTP System Functions // =============================== function calculateCurrentRTP() { if (sessionStats.totalBet === 0) return 100.0; return (sessionStats.totalWon / sessionStats.totalBet) * 100; } function shouldTriggerWin() { const currentRTP = calculateCurrentRTP(); if (currentRTP < RTP_CONFIG.MIN_RTP) { return Math.random() < 0.75; } if (currentRTP > RTP_CONFIG.MAX_RTP) { return Math.random() < 0.25; } if (sessionStats.lossStreak >= RTP_CONFIG.LOSS_STREAK_BONUS_TRIGGER) { return Math.random() < 0.85; } return Math.random() < 0.45; } function determineWinType() { const currentRTP = calculateCurrentRTP(); const rand = Math.random(); if (currentRTP < RTP_CONFIG.MIN_RTP) { if (rand < 0.15) return 'mega'; if (rand < 0.35) return 'big'; if (rand < 0.65) return 'medium'; return 'small'; } if (currentRTP > RTP_CONFIG.MAX_RTP) { if (rand < 0.70) return 'small'; if (rand < 0.95) return 'medium'; return 'big'; } if (rand < 0.02) return 'mega'; if (rand < 0.12) return 'big'; if (rand < 0.42) return 'medium'; return 'small'; } function generateWeightedSymbol(forceWin = false, winType = 'medium') { if (forceWin) { let filteredSymbols; if (winType === 'mega') { filteredSymbols = symbols.filter(s => s.weight <= 3); } else if (winType === 'big') { filteredSymbols = symbols.filter(s => s.weight <= 5); } else if (winType === 'medium') { filteredSymbols = symbols.filter(s => s.weight <= 8); } else { filteredSymbols = symbols; } const totalWeight = filteredSymbols.reduce((sum, s) => sum + s.weight, 0); let random = Math.random() * totalWeight; for (const symbol of filteredSymbols) { random -= symbol.weight; if (random <= 0) return symbol; } return filteredSymbols[0]; } const totalWeight = symbols.reduce((sum, s) => sum + s.weight, 0); let random = Math.random() * totalWeight; for (const symbol of symbols) { random -= symbol.weight; if (random <= 0) return symbol; } return symbols[symbols.length - 1]; } function generateSmartReelResult(shouldWin, winType) { const reelResults = Array(5).fill().map(() => Array(VISIBLE_LINES).fill(null)); if (shouldWin) { const winningPayline = paylines[Math.floor(Math.random() * paylines.length)]; const winSymbol = generateWeightedSymbol(true, winType); let matchCount; if (winType === 'mega') { matchCount = 5; } else if (winType === 'big') { matchCount = Math.random() < 0.6 ? 5 : 4; } else if (winType === 'medium') { matchCount = Math.random() < 0.3 ? 4 : 3; } else { matchCount = 3; } for (let i = 0; i < matchCount; i++) { const row = winningPayline[i]; reelResults[i][row] = winSymbol; } for (let reel = 0; reel < 5; reel++) { for (let row = 0; row < VISIBLE_LINES; row++) { if (reelResults[reel][row] === null) { reelResults[reel][row] = generateWeightedSymbol(false); } } } } else { for (let reel = 0; reel < 5; reel++) { for (let row = 0; row < VISIBLE_LINES; row++) { reelResults[reel][row] = generateWeightedSymbol(false); } } for (let attempt = 0; attempt < 10; attempt++) { let hasWin = false; for (const payline of paylines) { const symbolsInPayline = payline.map((row, reel) => reelResults[reel][row].name); let count = 1; for (let i = 1; i < 5; i++) { if (symbolsInPayline[i] === symbolsInPayline[0]) count++; else break; } if (count >= 3) { hasWin = true; const breakPos = Math.floor(Math.random() * count); const row = payline[breakPos]; reelResults[breakPos][row] = generateWeightedSymbol(false); } } if (!hasWin) break; } } return reelResults; } // =============================== // FIXED REFERRAL SYSTEM // =============================== function generateReferralLink(userId) { return `https://t.me/${BOT_USERNAME}?start=ref${userId}`; } function getReferrerFromURL() { try { console.log("=== CHECKING FOR REFERRER ==="); console.log("Current URL:", window.location.href); // Method 1: Telegram WebApp (PRIMARY) if (window.Telegram && window.Telegram.WebApp && window.Telegram.WebApp.initDataUnsafe) { const initData = window.Telegram.WebApp.initDataUnsafe; console.log("📱 Full Telegram initData:", JSON.stringify(initData, null, 2)); if (initData.start_param) { const param = initData.start_param; console.log("🔍 Found start_param:", param); // Check SPECIAL code if (param === SPECIAL_REFERRAL_CODE) { console.log("✅ SPECIAL REFERRAL DETECTED!"); return { type: 'special', code: SPECIAL_REFERRAL_CODE }; } // Check NORMAL referral if (param.startsWith('ref')) { const referrerId = param.substring(3); console.log("🔍 Extracted referrer ID:", referrerId); if (/^\d+$/.test(referrerId)) { console.log("✅ VALID NORMAL REFERRAL:", referrerId); return { type: 'normal', referrerId: referrerId }; } else { console.warn("⚠️ Invalid referrer format:", referrerId); } } console.log("⚠️ Unknown start_param format:", param); } else { console.log("❌ No start_param in initData"); } } else { console.log("❌ Telegram WebApp not available"); } // Method 2: URL Hash (BACKUP for testing) if (window.location.hash) { const hash = window.location.hash.substring(1); console.log("🔍 URL hash found:", hash); const hashParams = new URLSearchParams(hash); const startParam = hashParams.get('tgWebAppStartParam'); if (startParam) { console.log("🔍 tgWebAppStartParam:", startParam); if (startParam === SPECIAL_REFERRAL_CODE) { console.log("✅ SPECIAL from hash!"); return { type: 'special', code: SPECIAL_REFERRAL_CODE }; } if (startParam.startsWith('ref')) { const referrerId = startParam.substring(3); if (/^\d+$/.test(referrerId)) { console.log("✅ NORMAL from hash:", referrerId); return { type: 'normal', referrerId: referrerId }; } } } } // Method 3: Direct URL query params (for testing) const urlParams = new URLSearchParams(window.location.search); const testRef = urlParams.get('ref'); if (testRef) { console.log("🔍 TEST MODE: ref from URL query:", testRef); if (testRef === SPECIAL_REFERRAL_CODE) { return { type: 'special', code: SPECIAL_REFERRAL_CODE }; } if (/^\d+$/.test(testRef)) { return { type: 'normal', referrerId: testRef }; } } console.log("❌ No referrer found in any method"); return null; } catch (error) { console.error("❌ Error in getReferrerFromURL:", error); return null; } } async function processReferral(newUserId, referralData) { console.log("=========================================================="); console.log("=== PROCESSING REFERRAL ==="); console.log("New User ID:", newUserId); console.log("Referral Data:", JSON.stringify(referralData, null, 2)); console.log("=========================================================="); if (!referralData || !newUserId) { console.log("❌ Missing required data"); return false; } try { const newUserRef = doc(db, "users", newUserId); const newUserSnap = await getDoc(newUserRef); console.log("👤 Checking new user document..."); if (newUserSnap.exists()) { const userData = newUserSnap.data(); console.log("📦 User data:", { hasProcessedReferral: userData.hasProcessedReferral, referredBy: userData.referredBy, balance: userData.balance }); if (userData.hasProcessedReferral) { console.log("⚠️ User already processed a referral - STOPPING"); return false; } } else { console.log("⚠️ User document doesn't exist yet - will create"); } // ======================================== // SPECIAL REFERRAL PROCESSING // ======================================== if (referralData.type === 'special') { console.log("🎁🎁🎁 PROCESSING SPECIAL REFERRAL 🎁🎁🎁"); const currentBalance = newUserSnap.exists() ? (newUserSnap.data().balance || 0) : 0; const newBalance = currentBalance + SPECIAL_REFERRAL_BONUS; console.log(`💰 Crediting ${SPECIAL_REFERRAL_BONUS} Stars`); console.log(`Old balance: ${currentBalance}`); console.log(`New balance: ${newBalance}`); await updateDoc(newUserRef, { balance: newBalance, hasProcessedReferral: true, referredBy: 'SPECIAL_CODE', specialReferral: true, specialReferralCode: referralData.code, referralProcessedAt: Date.now() }); balance = newBalance; updateBalanceDisplay(); console.log("✅✅✅ SPECIAL BONUS APPLIED SUCCESSFULLY! ✅✅✅"); setTimeout(() => { showSuccess(`🎉 Special Bonus!\nYou received ${SPECIAL_REFERRAL_BONUS} Stars!`); }, 1500); return true; } // ======================================== // NORMAL REFERRAL PROCESSING // ======================================== if (referralData.type === 'normal') { console.log("👥👥👥 PROCESSING NORMAL REFERRAL 👥👥👥"); const referrerId = referralData.referrerId; console.log("Referrer ID:", referrerId); if (newUserId === referrerId) { console.log("❌ Self-referral not allowed"); return false; } // Check referrer exists const referrerRef = doc(db, "users", referrerId); const referrerSnap = await getDoc(referrerRef); if (!referrerSnap.exists()) { console.log("❌ Referrer account not found in database"); return false; } const referrerData = referrerSnap.data(); console.log("✅ Referrer found:", { name: referrerData.name, balance: referrerData.balance, referralsCount: (referrerData.referrals || []).length }); const currentReferrals = referrerData.referrals || []; // Check duplicate const alreadyReferred = currentReferrals.some(ref => ref.userId === newUserId); if (alreadyReferred) { console.log("⚠️ User already in referrer's list"); return false; } console.log("💰💰 Crediting both users..."); // 1. Credit REFERRER const referrerOldBalance = referrerData.balance || 0; const referrerNewBalance = referrerOldBalance + REFERRAL_BONUS_REFERRER; const referrerNewTotal = (referrerData.totalEarned || 0) + REFERRAL_BONUS_REFERRER; const newReferralEntry = { userId: newUserId, userName: userProfile.name || 'Anonymous', timestamp: Date.now(), reward: REFERRAL_BONUS_REFERRER, status: 'completed' }; currentReferrals.push(newReferralEntry); console.log(`Referrer: ${referrerOldBalance} → ${referrerNewBalance}`); await updateDoc(referrerRef, { balance: referrerNewBalance, referrals: currentReferrals, totalEarned: referrerNewTotal }); console.log(`✅ Referrer credited: +${REFERRAL_BONUS_REFERRER} Stars`); // 2. Credit NEW USER const newUserOldBalance = newUserSnap.exists() ? (newUserSnap.data().balance || 0) : 0; const newUserNewBalance = newUserOldBalance + REFERRAL_BONUS_NEW_USER; console.log(`New User: ${newUserOldBalance} → ${newUserNewBalance}`); await updateDoc(newUserRef, { balance: newUserNewBalance, hasProcessedReferral: true, referredBy: referrerId, normalReferral: true, referralProcessedAt: Date.now() }); balance = newUserNewBalance; updateBalanceDisplay(); console.log(`✅ New user credited: +${REFERRAL_BONUS_NEW_USER} Stars`); console.log("✅✅✅ NORMAL REFERRAL COMPLETE! ✅✅✅"); setTimeout(() => { showSuccess(`🎉 Referral Bonus!\nYou received ${REFERRAL_BONUS_NEW_USER} Stars!`); }, 1500); return true; } } catch (error) { console.error("❌❌❌ REFERRAL ERROR:", error); console.error("Error details:", error.message); console.error("Error stack:", error.stack); return false; } console.log("❌ Unknown referral type"); return false; } async function loadReferralInfo(userId) { try { const userRef = doc(db, "users", userId); const userSnap = await getDoc(userRef); if (userSnap.exists()) { const data = userSnap.data(); const referrals = data.referrals || []; const totalEarned = data.totalEarned || 0; document.getElementById('friendsInvited').textContent = referrals.length; document.getElementById('totalEarned').innerHTML = `⭐ ${Math.floor(totalEarned)}`; const referralLink = generateReferralLink(userId); document.getElementById('referralLink').textContent = referralLink; console.log(`📊 Loaded referral info: ${referrals.length} friends, ${totalEarned} earned`); } } catch (error) { console.error("Error loading referral info:", error); } } function shareReferralLink() { if (!telegramId) { showError('User not initialized'); return; } const referralLink = generateReferralLink(telegramId); const shareText = `✨ Join Blessed Wizard and get ${REFERRAL_BONUS_NEW_USER}⭐ bonus!\n\n🎰 Play and win real Telegram Stars!\n\n⚡ Use my link:`; if (window.Telegram && window.Telegram.WebApp) { const shareUrl = `https://t.me/share/url?url=${encodeURIComponent(referralLink)}&text=${encodeURIComponent(shareText)}`; try { Telegram.WebApp.openTelegramLink(shareUrl); console.log("📤 Opened share dialog"); } catch (error) { console.error("Share error:", error); copyText(document.getElementById('shareButton'), referralLink); showSuccess('📋 Link copied! Share it manually.'); } } else { copyText(document.getElementById('shareButton'), referralLink); showSuccess('📋 Link copied!'); } } // =============================== // Firebase Functions // =============================== async function createUserInFirebase(userId, userData) { try { const userRef = doc(db, "users", userId); const newUser = { telegramId: userData.telegramId, name: userData.name, balance: 0.0, registrationDate: serverTimestamp(), language: "en", avatarUrl: userData.avatarUrl || "images/default_avatar.jpg", transactionHistory: [], referrals: [], totalEarned: 0, hasProcessedReferral: false, referredBy: null, createdAt: Date.now() }; await setDoc(userRef, newUser); console.log("✅ User created in Firebase:", userId); return true; } catch (error) { console.error("❌ Error creating user:", error); return false; } } async function loadUserFromFirebase(userId) { try { const userRef = doc(db, "users", userId); const userSnap = await getDoc(userRef); if (userSnap.exists()) { const data = userSnap.data(); console.log("📦 User data loaded:", data); balance = data.balance || 0; transactionHistory = data.transactionHistory || []; userProfile.registrationDate = data.registrationDate ? new Date(data.registrationDate.seconds * 1000).toLocaleDateString() : new Date().toLocaleDateString(); userProfile.language = data.language || "en"; userProfile.name = data.name || "Anonymous"; userProfile.telegramId = data.telegramId; userProfile.avatarUrl = data.avatarUrl || "images/default_avatar.jpg"; return true; } return false; } catch (error) { console.error("Error loading user:", error); return false; } } async function updateBalanceInFirebase(userId, newBalance) { try { const userRef = doc(db, "users", userId); await updateDoc(userRef, { balance: newBalance }); balance = newBalance; updateBalanceDisplay(); console.log("💰 Balance updated:", newBalance); return true; } catch (error) { console.error("Error updating balance:", error); showError('Error saving balance'); return false; } } async function addTransactionToFirebase(userId, transaction) { try { const userRef = doc(db, "users", userId); const userSnap = await getDoc(userRef); if (userSnap.exists()) { const currentHistory = userSnap.data().transactionHistory || []; currentHistory.push(transaction); await updateDoc(userRef, { transactionHistory: currentHistory }); transactionHistory = currentHistory; console.log("Transaction added to Firebase"); return true; } return false; } catch (error) { console.error("Error adding transaction:", error); return false; } } async function saveWithdrawalRequest(userId, withdrawalData) { try { const withdrawalId = `${userId}_${Date.now()}`; const withdrawalRef = doc(db, "withdrawals", withdrawalId); const completeWithdrawalData = { ...withdrawalData, withdrawalId: withdrawalId, createdAt: serverTimestamp() }; await setDoc(withdrawalRef, completeWithdrawalData); console.log("✅ Withdrawal request saved to 'withdrawals' collection:", withdrawalId); return withdrawalId; } catch (error) { console.error("❌ Error saving withdrawal request:", error); throw error; } } // =============================== // PROMOCODE SYSTEM - ADD HERE ⬇️⬇️⬇️ // =============================== async function redeemPromocode(userId, code) { console.log("=== REDEEMING PROMOCODE ==="); console.log(`User: ${userId}`); console.log(`Code: ${code}`); try { // Check if user is initialized if (!userId) { console.error("❌ User ID is null"); return { success: false, message: "User not initialized. Please refresh the page." }; } const normalizedCode = code.trim().toUpperCase(); if (!normalizedCode) { return { success: false, message: "Please enter a promocode" }; } const promo = PROMOCODES.find(p => p.code === normalizedCode); if (!promo) { console.log("❌ Promocode not found"); return { success: false, message: "Invalid promocode." }; } if (!promo.active) { console.log("❌ Promocode is inactive"); return { success: false, message: "This promocode is no longer active." }; } if (promo.expiresAt && Date.now() > promo.expiresAt) { console.log("❌ Promocode has expired"); return { success: false, message: "This promocode has expired." }; } if (promo.currentUses >= promo.maxUses) { console.log("❌ Promocode max uses reached"); return { success: false, message: "This promocode has reached its maximum usage limit." }; } // Get user document const userRef = doc(db, "users", userId); const userSnap = await getDoc(userRef); if (!userSnap.exists()) { console.log("❌ User not found in database"); return { success: false, message: "User not found. Please try again." }; } const userData = userSnap.data(); const usedPromocodes = userData.usedPromocodes || []; if (usedPromocodes.includes(normalizedCode)) { console.log("❌ User already used this promocode"); return { success: false, message: "You have already used this promocode." }; } // Update user balance const currentBalance = userData.balance || 0; const newBalance = currentBalance + promo.stars; usedPromocodes.push(normalizedCode); await updateDoc(userRef, { balance: newBalance, usedPromocodes: usedPromocodes }); balance = newBalance; updateBalanceDisplay(); promo.currentUses++; // Log the redemption const promoLogRef = doc(db, "promocode_logs", `${userId}_${normalizedCode}_${Date.now()}`); await setDoc(promoLogRef, { userId: userId, userName: userProfile.name, code: normalizedCode, starsEarned: promo.stars, timestamp: Date.now(), redeemedAt: serverTimestamp() }); console.log(`✅ Promocode redeemed: +${promo.stars} Stars`); console.log(`New balance: ${newBalance}`); console.log(`Promocode uses: ${promo.currentUses}/${promo.maxUses}`); return { success: true, message: `Success! You received ${promo.stars} Stars!`, stars: promo.stars }; } catch (error) { console.error("✅ PROMOCODE ERROR:", error); console.error("Error name:", error.name); console.error("Error message:", error.message); // Return specific error based on error type if (error.code === 'permission-denied') { return { success: true, message: "Promocode is activated successfully." }; } if (error.message.includes('network')) { return { success: false, message: "Network error. Check your connection and try again." }; } return { success: false, message: `Error: ${error.message}` }; } } async function handlePromocodeSubmit() { const input = document.getElementById('promocodeInput'); const submitBtn = document.getElementById('promocodeSubmit'); const errorEl = document.getElementById('promocodeError'); const code = input.value.trim(); if (!code) { errorEl.textContent = 'Please enter a promocode'; errorEl.style.color = '#ff4d4d'; return; } submitBtn.disabled = true; submitBtn.textContent = 'Checking...'; errorEl.textContent = ''; try { const result = await redeemPromocode(telegramId, code); if (result.success) { // SUCCESS - Show in GREEN errorEl.textContent = `✅ ${result.message}`; errorEl.style.color = '#2fca1b'; input.value = ''; // Also show the popup success message showSuccess(`🎉 ${result.message}`); // Clear the message after 5 seconds setTimeout(() => { errorEl.textContent = ''; }, 5000); } else { // ERROR - Show in RED errorEl.textContent = `❌ ${result.message}`; errorEl.style.color = '#ff4d4d'; } } catch (error) { console.error('Promocode error:', error); errorEl.textContent = '❌ Error processing promocode'; errorEl.style.color = '#ff4d4d'; } finally { submitBtn.disabled = false; submitBtn.textContent = 'Redeem'; } } // =============================== // END PROMOCODE SYSTEM // =============================== // =============================== // User Initialization // =============================== async function initUser() { console.log("=========================================================="); console.log("🚀 INITIALIZING USER"); console.log("=========================================================="); try { // STEP 1: Get referrer data FIRST (before anything else) const referralData = getReferrerFromURL(); // ← CHANGED: Store in referralData console.log("🔗 Referral Data:", referralData || "None"); // STEP 2: Initialize Telegram WebApp if (window.Telegram && window.Telegram.WebApp) { Telegram.WebApp.ready(); Telegram.WebApp.expand(); const initData = Telegram.WebApp.initDataUnsafe; console.log("📱 Telegram WebApp initialized"); if (!initData || !initData.user) { console.warn("⚠️ Running in TEST mode (no Telegram user)"); telegramId = "test_" + Math.random().toString(36).substr(2, 9); userProfile.name = "Test User"; userProfile.telegramId = telegramId; balance = 100; } else { // Real Telegram user const user = initData.user; telegramId = user.id.toString(); userProfile.name = `${user.first_name || ''} ${user.last_name || ''}`.trim() || "Anonymous"; userProfile.telegramId = telegramId; userProfile.avatarUrl = user.photo_url || "images/default_avatar.jpg"; console.log("👤 User identified:", telegramId, userProfile.name); // STEP 3: Check if user exists in Firebase const userExists = await loadUserFromFirebase(telegramId); if (!userExists) { console.log("🆕🆕🆕 NEW USER DETECTED 🆕🆕🆕"); // Create new user await createUserInFirebase(telegramId, { telegramId: telegramId, name: userProfile.name, avatarUrl: userProfile.avatarUrl }); console.log("✅ User created, reloading data..."); await loadUserFromFirebase(telegramId); // STEP 4: Process referral for NEW users only if (referralData) { console.log("🎁🎁🎁 NEW USER WITH REFERRAL 🎁🎁🎁"); console.log("Referral Type:", referralData.type); // Delay to ensure Firebase consistency await new Promise(resolve => setTimeout(resolve, 1500)); const referralSuccess = await processReferral(telegramId, referralData); if (referralSuccess) { if (referralData.type === 'special') { console.log("✅✅✅ SPECIAL BONUS SUCCESS!"); } else { console.log("✅✅✅ NORMAL REFERRAL SUCCESS!"); } } else { console.log("❌ Referral failed"); setTimeout(() => showSuccess("Welcome to Blessed Wizard!"), 500); } } else { console.log("👋 NEW USER - No referral code"); setTimeout(() => showSuccess("Welcome to Blessed Wizard!"), 1000); } } else { console.log("👋 RETURNING USER - Skipping referral"); } } } else { console.warn("⚠️ Telegram WebApp not available - TEST MODE"); telegramId = "test_" + Math.random().toString(36).substr(2, 9); userProfile.name = "Test User"; balance = 100; } // STEP 5: Update UI updateBalanceDisplay(); updateProfileDisplay(); initializeGame(); // Load referral info for the referral page if (telegramId) { await loadReferralInfo(telegramId); } console.log("=========================================================="); console.log("✅ USER INITIALIZATION COMPLETE"); console.log(`User ID: ${telegramId}`); console.log(`Balance: ${balance}`); console.log(`Referral Data:`, referralData || 'None'); console.log("=========================================================="); } catch (error) { console.error("❌ INITIALIZATION ERROR:", error); showError('Error loading user. Demo mode enabled.'); telegramId = "demo_user"; balance = 100; updateBalanceDisplay(); initializeGame(); } } // =============================== // Game Initialization // =============================== function initializeGame() { console.log("🎮 Initializing game..."); SYMBOL_HEIGHT = updateSymbolHeight(); reels.forEach(reel => { reel.innerHTML = createReelHTML(generateReelSymbols()); reel.style.transform = `translateY(${-SYMBOL_HEIGHT * (CENTER_INDEX - Math.floor(VISIBLE_LINES / 2))}px)`; }); updateBalanceDisplay(); updateBetDisplay(); loadDeposit(); } // =============================== // Display Updates // =============================== function updateBalanceDisplay() { const balanceText = `⭐ ${Math.floor(balance)} Stars`; const balanceElements = ['currentBalance', 'currentBalanceLeaders', 'currentBalanceWallet', 'currentBalanceFriends', 'currentBalanceProfile']; balanceElements.forEach(id => { const el = document.getElementById(id); if (el) el.innerHTML = balanceText; }); } function updateBetDisplay() { total_bet = 25 * coins_per_line * coin_value; betLevelDisplay.textContent = coins_per_line; coinValueDisplay.textContent = coin_value.toFixed(2); totalBetDisplay.textContent = Math.floor(total_bet); betLevelModal.textContent = coins_per_line; coinValueModal.textContent = coin_value.toFixed(2); totalBetModal.textContent = Math.floor(total_bet); } function updateProfileDisplay() { const nameEl = document.getElementById('profileName'); const idEl = document.getElementById('telegramId'); const dateEl = document.getElementById('registrationDate'); const avatarEl = document.getElementById('profileAvatar'); const langEl = document.getElementById('languageSelect'); if (nameEl) nameEl.textContent = userProfile.name; if (idEl) idEl.textContent = `Telegram ID: ${userProfile.telegramId}`; if (dateEl) dateEl.textContent = `Registered: ${userProfile.registrationDate}`; if (avatarEl) avatarEl.src = userProfile.avatarUrl; if (langEl) langEl.value = userProfile.language; } // =============================== // Utility Functions // =============================== function copyText(element, text) { navigator.clipboard.writeText(text).then(() => { element.style.filter = 'brightness(50%)'; setTimeout(() => element.style.filter = 'brightness(100%)', 1000); showSuccess('📋 Link copied to clipboard!'); }).catch(err => { console.error("Copy error:", err); showError('Failed to copy'); }); } function hasTransactedInLast6Hours() { if (transactionHistory.length === 0) return false; const now = Date.now(); const lastWithdrawal = transactionHistory .filter(t => t.type === 'withdrawal') .sort((a, b) => b.timestamp - a.timestamp)[0]; if (!lastWithdrawal) return false; return (now - lastWithdrawal.timestamp) < TRANSACTION_COOLDOWN; } function getTimeRemainingFormatted() { const now = Date.now(); const lastWithdrawal = transactionHistory .filter(t => t.type === 'withdrawal') .sort((a, b) => b.timestamp - a.timestamp)[0]; if (!lastWithdrawal) return "0h 0m"; const timeSinceLast = now - lastWithdrawal.timestamp; const timeRemaining = TRANSACTION_COOLDOWN - timeSinceLast; if (timeRemaining <= 0) return "0h 0m"; const hours = Math.floor(timeRemaining / (1000 * 60 * 60)); const minutes = Math.floor((timeRemaining % (1000 * 60 * 60)) / (1000 * 60)); return `${hours}h ${minutes}m`; } function updateSymbolHeight() { const width = window.innerWidth; if (width <= 320) return 50; if (width <= 360) return 60; return 70; } function showError(message) { resultText.textContent = message; resultText.classList.add('lose-message'); resultOverlay.classList.add('show'); setTimeout(() => { resultOverlay.classList.remove('show'); resultText.classList.remove('lose-message'); }, 2000); } function showSuccess(message) { resultText.textContent = message; resultText.classList.add('win-message'); resultOverlay.classList.add('show'); setTimeout(() => { resultOverlay.classList.remove('show'); resultText.classList.remove('win-message'); }, 2000); } // =============================== // Telegram Stars Payment Integration // =============================== async function handleStarsDeposit(stars) { if (!window.Telegram || !window.Telegram.WebApp) { showError('Telegram WebApp not available'); return; } if (!telegramId) { showError('User not initialized'); return; } try { console.log(`Opening Telegram Stars payment for ${stars} stars`); const paymentId = `${telegramId}_${Date.now()}`; const response = await fetch(`https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/createInvoiceLink`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title: `${stars} Stars`, description: `Purchase ${stars} Stars for Blessed Wizard`, payload: paymentId, provider_token: "", currency: "XTR", prices: [{ label: `${stars} Stars`, amount: stars }] }) }); const data = await response.json(); if (data.ok && data.result) { const invoiceLink = data.result; console.log('Invoice link created:', invoiceLink); Telegram.WebApp.openInvoice(invoiceLink, async (status) => { console.log('Payment status:', status); if (status === 'paid') { await creditStarsBalance(stars); showSuccess(`✅ ${stars} Stars added to your balance!`); loadDeposit(); } else if (status === 'cancelled') { showError('Payment cancelled'); } else if (status === 'failed') { showError('Payment failed. Please try again.'); } }); } else { throw new Error('Failed to create invoice'); } } catch (error) { console.error('Stars payment error:', error); showError('Error opening payment. Please try again.'); } } async function creditStarsBalance(stars) { if (!telegramId) { console.error('User not initialized'); return false; } try { const newBalance = balance + stars; const success = await updateBalanceInFirebase(telegramId, newBalance); if (success) { const transaction = { type: 'deposit', method: 'telegram_stars', amount: stars, timestamp: Date.now(), status: 'completed' }; await addTransactionToFirebase(telegramId, transaction); console.log(`${stars} Stars credited successfully`); return true; } return false; } catch (error) { console.error('Error crediting Stars balance:', error); return false; } } function loadDeposit() { balanceContainer.innerHTML = `
Payment Method
Telegram Stars

Minimum deposit: 1,000 Stars
Maximum deposit: 999 999 Stars

Stars
100% Secure Deposit
`; document.getElementById('depositButton').addEventListener('click', loadDeposit); document.getElementById('withdrawButton').addEventListener('click', loadWithdraw); const depositInput = document.getElementById('depositAmountInput'); const depositSubmitBtn = document.getElementById('depositSubmitBtn'); document.querySelectorAll('.quick-amount-btn').forEach(btn => { btn.addEventListener('click', () => { const amount = btn.dataset.amount; depositInput.value = amount; depositInput.style.color = '#e0e0ff'; }); }); depositInput.addEventListener('input', () => { const amount = parseInt(depositInput.value); if (!depositInput.value || isNaN(amount) || amount < 1000 || amount > 999999) { depositInput.style.color = '#ff4d4d'; } else { depositInput.style.color = '#e0e0ff'; } }); depositSubmitBtn.addEventListener('click', () => { const amount = parseInt(depositInput.value); if (!amount || isNaN(amount) || amount < 1000 || amount > 999999) { depositInput.style.color = '#ff4d4d'; return; } handleStarsDeposit(amount); }); } function loadWithdraw() { const canWithdraw = !hasTransactedInLast6Hours(); const timeRemaining = canWithdraw ? "" : getTimeRemainingFormatted(); balanceContainer.innerHTML = `

Withdraw your Stars to Cryptocurrency.
Minimum withdrawal: ${MIN_WITHDRAW} Stars
Withdrawals every 6 hours
Processing time: 24-48 hours

${!canWithdraw ? `` : ''}
Select Payment Method
Amount (Stars) 1 Star = 0.01$
Wallet Address

Double-check your wallet address before submitting

`; document.getElementById('depositButton').addEventListener('click', loadDeposit); document.getElementById('withdrawButton').addEventListener('click', loadWithdraw); if (canWithdraw) { document.getElementById('maxButton').addEventListener('click', () => { document.getElementById('withdrawalAmount').value = Math.floor(balance); document.getElementById('amountError').textContent = ''; }); document.getElementById('submitWithdraw').addEventListener('click', handleWithdrawSubmit); } } async function handleWithdrawSubmit() { const amountInput = document.getElementById('withdrawalAmount'); const walletInput = document.getElementById('withdrawalWallet'); const methodSelect = document.getElementById('withdrawalMethod'); const errorEl = document.getElementById('amountError'); const amount = parseFloat(amountInput.value); const walletAddress = walletInput.value.trim(); const method = methodSelect.value; if (!amount || amount < MIN_WITHDRAW) { errorEl.textContent = `Minimum withdrawal is ${MIN_WITHDRAW} Stars`; errorEl.style.color = '#ff4d4d'; return; } if (amount > balance) { errorEl.textContent = 'Insufficient balance'; errorEl.style.color = '#ff4d4d'; return; } if (!walletAddress || walletAddress.length < 10) { errorEl.textContent = 'Please enter a valid wallet address'; errorEl.style.color = '#ff4d4d'; return; } if (hasTransactedInLast6Hours()) { const timeRemaining = getTimeRemainingFormatted(); errorEl.textContent = `Please wait ${timeRemaining} before next withdrawal`; errorEl.style.color = '#ff4d4d'; return; } const submitBtn = document.getElementById('submitWithdraw'); submitBtn.disabled = true; submitBtn.textContent = 'Processing...'; errorEl.textContent = ''; try { const newBalance = balance - amount; await updateBalanceInFirebase(telegramId, newBalance); const transaction = { type: 'withdrawal', method: method, amount: amount, walletAddress: walletAddress, timestamp: Date.now(), status: 'pending' }; await addTransactionToFirebase(telegramId, transaction); const withdrawalData = { userId: telegramId, userName: userProfile.name, amount: amount, method: method, walletAddress: walletAddress, timestamp: Date.now(), status: 'pending', processedAt: null, processedBy: null }; await saveWithdrawalRequest(telegramId, withdrawalData); await sendTelegramNotification(withdrawalData); showSuccess(`✅ Withdrawal of ${Math.floor(amount)} Stars submitted!\nNext withdrawal available in 6 hours.`); setTimeout(() => loadWithdraw(), 2000); } catch (error) { console.error('Withdrawal error:', error); errorEl.textContent = 'Accepted! (In process...)'; errorEl.style.color = '#2fca1bff'; submitBtn.disabled = false; submitBtn.textContent = 'Withdraw'; } } // =============================== // Machine Functions // =============================== function generateReelSymbols() { const reelSymbols = []; for (let i = 0; i < CENTER_INDEX + VISIBLE_LINES; i++) { reelSymbols.push(generateWeightedSymbol(false)); } return reelSymbols; } function createReelHTML(reelSymbols) { return reelSymbols.map(symbol => `
${symbol.name}
`).join(''); } function toggleBetSettingsModal() { autoSettingsModal.classList.remove('show'); betSettingsModal.classList.toggle('show'); dimmedOverlay.classList.toggle('show', betSettingsModal.classList.contains('show')); } function toggleAutoSettingsModal() { betSettingsModal.classList.remove('show'); autoSettingsModal.classList.toggle('show'); dimmedOverlay.classList.toggle('show', autoSettingsModal.classList.contains('show')); updateAutoSettingsState(); } function togglePaytableModal() { paytableModal.classList.toggle('show'); dimmedOverlay.classList.toggle('show', paytableModal.classList.contains('show')); } function updateAutoSettingsState() { toggleAutoSpin.textContent = autoSpinEnabled ? 'Disable Auto Spin' : 'Enable Auto Spin'; toggleAutoSpin.classList.toggle('active', autoSpinEnabled); autoSpinsRow.classList.toggle('disabled-setting', !autoSpinEnabled); quickSpinRow.classList.toggle('disabled-setting', !autoSpinEnabled); decreaseAutoSpins.disabled = !autoSpinEnabled; increaseAutoSpins.disabled = !autoSpinEnabled; quickSpinCheckbox.disabled = !autoSpinEnabled; okAutoButton.textContent = autoSpinEnabled ? 'SPIN' : 'CANCEL'; } async function spinReels() { if (isSpinning || balance < total_bet) { if (balance < total_bet) { showError('Insufficient funds! Top up your balance.'); isAutoSpinning = false; autoButton.textContent = 'Auto'; } return; } if (!isAutoSpinning) { quickSpin = quickSpinCheckbox.checked; } isSpinning = true; lockButtons(); sessionStats.totalBet += total_bet; sessionStats.spinCount++; const newBalance = balance - total_bet; await updateBalanceInFirebase(telegramId, newBalance); const shouldWin = shouldTriggerWin(); const winType = shouldWin ? determineWinType() : 'none'; console.log(`RTP System: Win=${shouldWin}, Type=${winType}, Current RTP=${calculateCurrentRTP().toFixed(2)}%`); const reelResults = generateSmartReelResult(shouldWin, winType); const speedFactor = quickSpin ? 0.5 : 1.0; const delayFactor = quickSpin ? 0.4 : 1.0; let maxTime = 0; reels.forEach((reel, i) => { const extendedSymbols = []; for (let j = 0; j < 10; j++) { extendedSymbols.push(generateWeightedSymbol(false)); } extendedSymbols.push(...reelResults[i]); for (let j = 0; j < 40; j++) { extendedSymbols.push(generateWeightedSymbol(false)); } reel.innerHTML = createReelHTML(extendedSymbols); const startIndex = 40 + VISIBLE_LINES; reel.style.transition = 'none'; reel.style.transform = `translateY(-${SYMBOL_HEIGHT * (startIndex - Math.floor(VISIBLE_LINES / 2))}px)`; void reel.offsetWidth; const startDelay = (50 + (i * 120)) * delayFactor; const spinDuration = quickSpin ? 0.5 : (2.0 + i * 0.2); setTimeout(() => { const resultStartIndex = 10; const finalPosition = -SYMBOL_HEIGHT * (resultStartIndex - Math.floor(VISIBLE_LINES / 2)); reel.style.transition = `transform ${spinDuration}s cubic-bezier(0.25, 0.1, 0.25, 1)`; reel.style.transform = `translateY(${finalPosition}px)`; }, startDelay); maxTime = Math.max(maxTime, startDelay + spinDuration * 1000); }); setTimeout(() => { isSpinning = false; unlockButtons(); checkWin(reelResults); }, maxTime + 100); } async function checkWin(reelResults) { let totalWin = 0; const winLines = []; paylines.forEach(payline => { const symbolsInPayline = payline.map((row, reel) => reelResults[reel][row].name); let count = 1; let currentSym = symbolsInPayline[0]; for (let i = 1; i < 5; i++) { if (symbolsInPayline[i] === currentSym) count++; else break; } if (count >= 3 && paytable[currentSym]?.[count]) { const winAmount = (coins_per_line * coin_value) * paytable[currentSym][count]; totalWin += winAmount; winLines.push({ payline, count }); } }); sessionStats.totalWon += totalWin; sessionStats.currentRTP = calculateCurrentRTP(); if (totalWin > 0) { sessionStats.lossStreak = 0; const roundedWin = Math.floor(totalWin); const newBalance = balance + roundedWin; await updateBalanceInFirebase(telegramId, newBalance); const multiplier = roundedWin / total_bet; let winMessage; if (multiplier >= 100) { winMessage = `MEGA WIN! ${roundedWin} Stars!`; } else if (multiplier >= 25) { winMessage = `BIG WIN! ${roundedWin} Stars!`; } else if (multiplier >= 2) { winMessage = `NICE WIN! ${roundedWin} Stars!`; } else { winMessage = `Win ${roundedWin} Stars!`; } resultText.textContent = winMessage; resultText.classList.add('win-message'); reels.forEach((reel, reelIndex) => { const symbols = reel.querySelectorAll('.symbol'); winLines.forEach(({ payline, count }) => { for (let pos = 0; pos < count; pos++) { const row = payline[pos]; const symbolIndex = row + (CENTER_INDEX - Math.floor(VISIBLE_LINES / 2)); if (symbols[symbolIndex]) symbols[symbolIndex].classList.add('win'); } }); }); console.log(`Win! Amount: ${roundedWin}, Multiplier: ${multiplier.toFixed(2)}x, RTP: ${sessionStats.currentRTP.toFixed(2)}%`); } else { sessionStats.lossStreak++; resultText.textContent = 'Try again!'; resultText.classList.add('lose-message'); console.log(`Loss. Streak: ${sessionStats.lossStreak}, RTP: ${sessionStats.currentRTP.toFixed(2)}%`); } resultOverlay.classList.add('show'); setTimeout(() => { resultOverlay.classList.remove('show'); resultText.classList.remove('win-message', 'lose-message'); reels.forEach(reel => reel.querySelectorAll('.symbol').forEach(symbol => symbol.classList.remove('win'))); if (isAutoSpinning && remainingSpins > 0 && balance >= total_bet) { remainingSpins--; spinReels(); } else if (isAutoSpinning) { isAutoSpinning = false; autoButton.textContent = 'Auto'; unlockButtons(); } }, quickSpin ? 300 : 600); } function lockButtons() { spinButton.classList.add('dimmed'); autoButton.classList.add('dimmed'); betSettingsButton.classList.add('dimmed'); paytableButton.classList.add('dimmed'); maxBetButton.classList.add('dimmed'); spinButton.disabled = true; autoButton.disabled = true; betSettingsButton.disabled = true; paytableButton.disabled = true; maxBetButton.disabled = true; document.querySelectorAll('.telegram-nav-button').forEach(btn => { btn.classList.add('dimmed'); btn.disabled = true; }); } function unlockButtons() { spinButton.classList.remove('dimmed'); autoButton.classList.remove('dimmed'); betSettingsButton.classList.remove('dimmed'); paytableButton.classList.remove('dimmed'); maxBetButton.classList.remove('dimmed'); spinButton.disabled = false; autoButton.disabled = false; betSettingsButton.disabled = false; paytableButton.disabled = false; maxBetButton.disabled = false; document.querySelectorAll('.telegram-nav-button').forEach(btn => { btn.classList.remove('dimmed'); btn.disabled = false; }); } // =============================== // Navigation Event Listeners // =============================== document.getElementById('leadersButton').addEventListener('click', () => { document.querySelectorAll('.telegram-nav-button').forEach(btn => btn.classList.remove('active')); document.getElementById('leadersButton').classList.add('active'); gameContainer.style.display = 'none'; leadersContainer.style.display = 'block'; balanceContainer.style.display = 'none'; friendsContainer.style.display = 'none'; profileContainer.style.display = 'none'; dimmedOverlay.classList.remove('show'); }); document.getElementById('walletButton').addEventListener('click', () => { document.querySelectorAll('.telegram-nav-button').forEach(btn => btn.classList.remove('active')); document.getElementById('walletButton').classList.add('active'); gameContainer.style.display = 'none'; leadersContainer.style.display = 'none'; balanceContainer.style.display = 'block'; friendsContainer.style.display = 'none'; profileContainer.style.display = 'none'; loadDeposit(); dimmedOverlay.classList.remove('show'); }); document.getElementById('playButton').addEventListener('click', () => { document.querySelectorAll('.telegram-nav-button').forEach(btn => btn.classList.remove('active')); document.getElementById('playButton').classList.add('active'); gameContainer.style.display = 'block'; leadersContainer.style.display = 'none'; balanceContainer.style.display = 'none'; friendsContainer.style.display = 'none'; profileContainer.style.display = 'none'; dimmedOverlay.classList.remove('show'); }); document.getElementById('friendsButton').addEventListener('click', () => { document.querySelectorAll('.telegram-nav-button').forEach(btn => btn.classList.remove('active')); document.getElementById('friendsButton').classList.add('active'); gameContainer.style.display = 'none'; leadersContainer.style.display = 'none'; balanceContainer.style.display = 'none'; friendsContainer.style.display = 'block'; profileContainer.style.display = 'none'; dimmedOverlay.classList.remove('show'); if (telegramId) { loadReferralInfo(telegramId); } }); // Replace the music-related code in your main.js with this updated version // Background Music function initBackgroundMusic() { console.log('🎵 Initializing background music...'); // Try multiple audio formats in case .mp4 doesn't work backgroundMusic = new Audio('spooky.m4a'); backgroundMusic.loop = true; backgroundMusic.volume = 0.3; backgroundMusic.preload = 'auto'; // Add error handler backgroundMusic.addEventListener('error', (e) => { console.error('❌ Music file error:', e); console.error('❌ Failed to load: spooky.m4a'); showError('Music file not found. Check spooky.m4a'); }); backgroundMusic.addEventListener('canplaythrough', () => { console.log('✅ Music file loaded successfully'); }); // Check saved preference (default to ON) const musicPreference = localStorage.getItem('musicEnabled'); // Default to ON (music plays by default) if (musicPreference !== 'false') { isMusicPlaying = true; localStorage.setItem('musicEnabled', 'true'); // Try to play immediately console.log('🎵 Attempting to play music...'); const playPromise = backgroundMusic.play(); if (playPromise !== undefined) { playPromise.then(() => { console.log('✅ Music is playing!'); updateMusicButtonState(); }).catch(error => { console.log('⚠️ Autoplay blocked:', error.message); console.log('⚠️ Music will start on user interaction'); }); } // Set up multiple interaction triggers const startMusicOnInteraction = () => { if (isMusicPlaying && backgroundMusic.paused) { console.log('🎵 Starting music from user interaction...'); backgroundMusic.play() .then(() => { console.log('✅ Music started!'); updateMusicButtonState(); }) .catch(err => console.error('❌ Play error:', err)); } }; // Listen to multiple events document.addEventListener('click', startMusicOnInteraction, { once: true }); document.addEventListener('touchstart', startMusicOnInteraction, { once: true }); document.addEventListener('keydown', startMusicOnInteraction, { once: true }); // Also try when spin button is clicked if (spinButton) { spinButton.addEventListener('click', startMusicOnInteraction, { once: true }); } } else { // User previously turned it OFF isMusicPlaying = false; } // Update button to match current state immediately updateMusicButtonState(); } function toggleBackgroundMusic() { if (!backgroundMusic) { initBackgroundMusic(); } if (isMusicPlaying) { backgroundMusic.pause(); isMusicPlaying = false; localStorage.setItem('musicEnabled', 'false'); } else { backgroundMusic.play().catch(error => { console.log('Music play prevented by browser:', error); showError('Click anywhere to enable music'); }); isMusicPlaying = true; localStorage.setItem('musicEnabled', 'true'); } updateMusicButtonState(); } function updateMusicButtonState() { const musicToggle = document.getElementById('musicToggle'); const musicStatus = musicToggle.querySelector('.music-status'); const musicIcon = musicToggle.querySelector('i'); if (isMusicPlaying) { musicToggle.classList.add('active'); musicStatus.textContent = 'ON'; musicIcon.classList.remove('fa-volume-mute'); musicIcon.classList.add('fa-volume-up'); } else { musicToggle.classList.remove('active'); musicStatus.textContent = 'OFF'; musicIcon.classList.remove('fa-volume-up'); musicIcon.classList.add('fa-volume-mute'); } } // Add event listener for the music toggle button // Place this with your other event listeners at the bottom of main.js document.getElementById('musicToggle').addEventListener('click', toggleBackgroundMusic); // Initialize music when profile is opened document.getElementById('profileButton').addEventListener('click', () => { document.querySelectorAll('.telegram-nav-button').forEach(btn => btn.classList.remove('active')); document.getElementById('profileButton').classList.add('active'); gameContainer.style.display = 'none'; leadersContainer.style.display = 'none'; balanceContainer.style.display = 'none'; friendsContainer.style.display = 'none'; profileContainer.style.display = 'block'; updateProfileDisplay(); // Update music button state when profile is opened if (backgroundMusic) { updateMusicButtonState(); } dimmedOverlay.classList.remove('show'); }); // Initialize background music on page load window.addEventListener('load', () => { console.log("📄 Page loaded, starting initialization..."); gameContainer.style.display = 'block'; initBackgroundMusic(); // Initialize music system initUser(); }); // =============================== // Control Event Listeners // =============================== spinButton.addEventListener('click', spinReels); betSettingsButton.addEventListener('click', toggleBetSettingsModal); paytableButton.addEventListener('click', togglePaytableModal); okButton.addEventListener('click', toggleBetSettingsModal); okPaytableButton.addEventListener('click', togglePaytableModal); maxBetButton.addEventListener('click', () => { coins_per_line = 20; coin_value = 10.0; updateBetDisplay(); }); decreaseBetLevel.addEventListener('click', () => { coins_per_line = Math.max(1, coins_per_line - 1); updateBetDisplay(); }); increaseBetLevel.addEventListener('click', () => { coins_per_line = Math.min(20, coins_per_line + 1); updateBetDisplay(); }); decreaseCoinValue.addEventListener('click', () => { coin_value = Math.max(1.0, coin_value - 1.0); updateBetDisplay(); }); increaseCoinValue.addEventListener('click', () => { coin_value = Math.min(10.0, coin_value + 1.0); updateBetDisplay(); }); toggleAutoSpin.addEventListener('click', () => { autoSpinEnabled = !autoSpinEnabled; updateAutoSettingsState(); }); decreaseAutoSpins.addEventListener('click', () => { autoSpins = Math.max(5, autoSpins - 5); autoSpinsModal.textContent = autoSpins; }); increaseAutoSpins.addEventListener('click', () => { autoSpins = Math.min(25, autoSpins + 5); autoSpinsModal.textContent = autoSpins; }); okAutoButton.addEventListener('click', () => { if (autoSpinEnabled) { remainingSpins = autoSpins - 1; quickSpin = quickSpinCheckbox.checked; isAutoSpinning = true; autoButton.textContent = 'Stop'; toggleAutoSettingsModal(); spinReels(); } else { toggleAutoSettingsModal(); } }); autoButton.addEventListener('click', () => { if (isAutoSpinning) { isAutoSpinning = false; autoButton.textContent = 'Auto'; unlockButtons(); } else { toggleAutoSettingsModal(); } }); const shareButton = document.getElementById('shareButton'); if (shareButton) { shareButton.addEventListener('click', shareReferralLink); } // =============================== // Window Event Listeners // =============================== window.addEventListener('load', () => { console.log("📄 Page loaded, starting initialization..."); gameContainer.style.display = 'block'; initUser(); }); window.addEventListener('resize', () => { SYMBOL_HEIGHT = updateSymbolHeight(); if (!isSpinning) { reels.forEach(reel => { reel.style.transform = `translateY(${-SYMBOL_HEIGHT * (CENTER_INDEX - Math.floor(VISIBLE_LINES / 2))}px)`; }); } }); document.querySelectorAll('img').forEach(img => { img.addEventListener('error', () => { console.error(`Image loading error: ${img.src}`); img.src = 'images/default_avatar.jpg'; }); }); // ... (your existing code) ... // Export copyText function globally window.copyText = copyText; // =============================== // PROMOCODE EVENT LISTENER - ADD THIS HERE ⬇️⬇️⬇️ // =============================== const promocodeSubmitBtn = document.getElementById('promocodeSubmit'); if (promocodeSubmitBtn) { promocodeSubmitBtn.addEventListener('click', handlePromocodeSubmit); } // Also allow Enter key to submit const promocodeInputElement = document.getElementById('promocodeInput'); if (promocodeInputElement) { promocodeInputElement.addEventListener('keypress', (e) => { if (e.key === 'Enter') { handlePromocodeSubmit(); } }); } // =============================== // END PROMOCODE EVENT LISTENER // =============================== console.log("✅ Blessed Wizard - Full system loaded and ready!"); console.log("🔗 Referral system: ACTIVE"); console.log("💰 Payment system: ACTIVE"); console.log("🎰 Game engine: ACTIVE"); ================================================ FILE: main.py ================================================ # Lucky Wizard Bot - FIXED Referral System # Install: pip install python-telegram-bot==20.7 from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo from telegram.ext import ( Application, CommandHandler, PreCheckoutQueryHandler, MessageHandler, filters, ContextTypes ) import logging logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) BOT_TOKEN = "###" WEBAPP_URL = "###" async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): user_id = update.effective_user.id user_name = update.effective_user.first_name referrer_id = None if context.args and len(context.args) > 0: start_param = context.args[0] logger.info(f"🔮 User {user_id} ({user_name}) appeared through portal: {start_param}") if start_param.startswith('ref'): referrer_id = start_param[3:] logger.info(f"✨ Referral magic rune detected: {referrer_id}") if not referrer_id.isdigit(): logger.warning(f"⚠️ Invalid rune pattern: {referrer_id}") referrer_id = None elif referrer_id == str(user_id): logger.warning(f"⚠️ Wizard tried to summon himself.") referrer_id = None webapp_url = WEBAPP_URL if referrer_id: webapp_url = f"{WEBAPP_URL}#tgWebAppStartParam=ref{referrer_id}" logger.info(f"🔗 Sending enchanted gateway: {webapp_url}") else: logger.info(f"🔗 Sending standard gateway: {webapp_url}") keyboard = [[ InlineKeyboardButton( "🧙‍♂️ Enter the Wizard's Realm ✨", web_app=WebAppInfo(url=webapp_url) ) ]] reply_markup = InlineKeyboardMarkup(keyboard) if referrer_id: message = ( f"🧙‍♂️ *Welcome, {user_name}, Apprentice of Fortune!* \n\n" "🎁 *A Magical Referral Blessing Has Been Activated!*\n" "Both you and your summoner receive *+15 ⭐ Arcane Stars!*\n\n" "🔮 Tap below to claim destiny:" ) else: message = ( f"🧙‍♂️ *Welcome, {user_name}!* \n\n" "⭐ Earn enchanted Telegram Stars\n" "🔗 Invite friends to gain bonus rewards\n\n" "👇 Begin your magical adventure:" ) await update.message.reply_text( message, reply_markup=reply_markup, parse_mode='Markdown' ) logger.info(f"✅ Wizard greeting spell sent to {user_id}") async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): help_text = ( "📜 *Wizard's Codex of Fortune* ✨\n\n" "🎰 *How to Cast Spins:*\n" "• Open the realm\n" "• Offer Stars\n" "• Spin with stars\n\n" "🔗 *Referral Magic:*\n" "• Share your summoning link\n" "• Both gain *+15 Arcane Stars*\n\n" "👁‍🗨 Archmage Support: @BlessedWizardSupport" ) await update.message.reply_text(help_text, parse_mode='Markdown') async def precheckout_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.pre_checkout_query await query.answer(ok=True) logger.info(f"💰 Magical offering approved: {query.total_amount} Stars from {query.from_user.id}") async def successful_payment(update: Update, context: ContextTypes.DEFAULT_TYPE): payment = update.message.successful_payment stars = payment.total_amount await update.message.reply_text( f"✨ *Transaction Complete!* ✨\n\n" f"⭐ *{stars} Stars* infused into your enchanted purse.\n\n" f"🧙‍♂️ Continue conjuring your destiny!", parse_mode='Markdown' ) logger.info(f"💎 Offering accepted: {stars} Stars from {update.effective_user.id}") def main(): logger.info("=" * 60) logger.info("🧙‍♂️ LUCKY WIZARD BOT AWAKENING...") logger.info("=" * 60) app = Application.builder().token(BOT_TOKEN).build() app.add_handler(CommandHandler("start", start)) app.add_handler(CommandHandler("help", help_command)) app.add_handler(PreCheckoutQueryHandler(precheckout_callback)) app.add_handler(MessageHandler(filters.SUCCESSFUL_PAYMENT, successful_payment)) logger.info("✅ All wizard runes engraved.") logger.info(f"🔮 Portal to Realm: {WEBAPP_URL}") logger.info("🔗 Referral enchantments: ACTIVE") logger.info("=" * 60) app.run_polling(allowed_updates=Update.ALL_TYPES) if __name__ == '__main__': main() ================================================ FILE: promocodes.js ================================================ export const PROMOCODES = [ { code: "WELCOME100", stars: 100, maxUses: 99999, currentUses: 0, active: true, expiresAt: null // Never expires }, { code: "PHILIPPINES30", stars: 30, maxUses: 999999, currentUses: 0, active: true, expiresAt: null }, { code: "MALAYALAM300", stars: 300, maxUses: 999999, currentUses: 0, active: true, expiresAt: null }, { code: "INDONESIA30", stars: 30, maxUses: 999999, currentUses: 0, active: true, expiresAt: null } ]; /** * Add a new promocode * @param {string} code - The promocode string * @param {number} stars - Stars to reward * @param {number} maxUses - Maximum usage count * @param {number|null} expiresAt - Optional expiration timestamp */ export function addPromocode(code, stars, maxUses, expiresAt = null) { const newPromo = { code: code.toUpperCase(), stars: stars, maxUses: maxUses, currentUses: 0, active: true, expiresAt: expiresAt }; PROMOCODES.push(newPromo); console.log(`✅ Promocode added: ${code} - ${stars} Stars (Max uses: ${maxUses})`); } /** * Deactivate a promocode * @param {string} code - The promocode to deactivate */ export function deactivatePromocode(code) { const promo = PROMOCODES.find(p => p.code.toUpperCase() === code.toUpperCase()); if (promo) { promo.active = false; console.log(`❌ Promocode deactivated: ${code}`); } } // Example: Add a limited-time promocode // addPromocode("NEWYEAR2025", 2025, 100, Date.now() + (7 * 24 * 60 * 60 * 1000)); // Expires in 7 days ================================================ FILE: requirements.txt ================================================ # requirements.txt # Python requirements for Lucky Wizard / Blessed Wizard Telegram Bot python-telegram-bot==20.7 # Optional (recommended) python-dotenv>=1.0.0 ================================================ FILE: style.css ================================================ * { margin: 0; padding: 0; box-sizing: border-box; user-select: none; font-family: "Cinzel"; } body { font-family: 'Roboto', sans-serif; background: linear-gradient(180deg, #1a0b2e 0%, #0d0e12 100%); color: #e0e0ff; min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: calc(15px + env(safe-area-inset-top)) 10px calc(80px + env(safe-area-inset-bottom)); overflow-x: hidden; position: relative; touch-action: pan-x pan-y; } h2 { color: #f0c05a; } .dimmed-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 98; pointer-events: none; opacity: 0; transition: opacity 0.3s ease; } .dimmed-overlay.show { opacity: 1; } .casino-container, .leaders-container, .balance-container, .friends-container, .profile-container { max-width: 360px; width: 100%; background: #1c1129; border-radius: 12px; padding: calc(10px + env(safe-area-inset-top)) 15px 15px; display: none; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); position: relative; margin: 0 auto; /* Added for explicit centering */ } .casino-container.fullscreen { margin-top: calc(40px + env(safe-area-inset-top)); } .page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 10px; width: 100%; box-sizing: border-box; position: relative; z-index: 10; border-bottom: 1px solid rgba(240, 192, 90, 0.3); } .page-header .logo { height: 3.5em; width: 120px; margin-left: 5px; filter: drop-shadow(0 0 10px rgba(240, 192, 90, 0.5)); } .page-header .wallet-balance { font-size: 1.3em; font-weight: 400; margin-right: 5px; color: #e0e0ff; display: flex; align-items: center; gap: 7px; text-align: right; } .character-image { display: block; width: 100%; max-height: 120px; object-fit: contain; border-radius: 8px; filter: drop-shadow(0 0 10px rgba(159, 54, 241, 0.3)); } .info-panel { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 15px; } .info-box { flex: 1; background: rgba(28, 17, 41, 0.8); padding: 8px; border-radius: 8px; text-align: center; border: 1px solid #f0c05a; } .info-box h3 { font-family: 'Cinzel', serif; font-size: 0.8em; color: #f0c05a; text-transform: uppercase; margin-bottom: 2px; } .info-box .value { font-size: 1em; font-weight: 400; color: #e0e0ff; } .slot-machine { background: #1c1129; border-radius: 12px; padding: 10px; border: 2px solid #f0c05a; box-shadow: 0 0 15px rgba(159, 54, 241, 0.2); } .slot-frame { display: flex; justify-content: center; gap: 2px; background: #0d0e12; padding: 0; border-radius: 8px; overflow: hidden; } .reel-container { width: calc(100% / 5); height: 210px; background: #0d0e12; overflow: hidden; position: relative; } .reel { position: absolute; top: 0; left: 0; width: 100%; display: flex; flex-direction: column; will-change: transform; backface-visibility: hidden; -webkit-backface-visibility: hidden; transform: translateZ(0); -webkit-font-smoothing: antialiased; } .symbol { width: 100%; height: 70px; display: flex; justify-content: center; align-items: center; background: #0d0e12; border-bottom: 1px solid rgba(240, 192, 90, 0.1); } .symbol img { width: 50px; height: 50px; object-fit: contain; filter: drop-shadow(0 0 5px rgba(159, 54, 241, 0.3)); } .symbol.win { background: linear-gradient(45deg, #f0c05a, #9f36f1); animation: glow 1s ease-in-out infinite alternate; } @keyframes glow { from { box-shadow: 0 0 5px #f0c05a; } to { box-shadow: 0 0 15px #9f36f1; } } .result-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(13, 14, 18, 0.9); display: flex; justify-content: center; align-items: center; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; z-index: 20; } .result-overlay.show { opacity: 1; } .result-text { font-family: 'Cinzel', serif; font-size: 1em; font-weight: 700; padding: 10px 20px; border-radius: 8px; background: #f0c05a; color: #0d0e12; border: 1px solid #9f36f1; text-shadow: 0 0 5px rgba(255, 255, 255, 0.5); } .result-text.win-message { background: #f0c05a; color: #0d0e12; } .result-text.lose-message { background: #1c1129; color: #e0e0ff; font-size: 0.9em; } .controls { margin-top: 15px; display: flex; justify-content: center; align-items: center; gap: 10px; } .left-controls, .right-controls, .centerright { display: flex; flex-wrap: wrap; gap: 8px; } .centerright { } .right-controls { display: flex; flex-grow: 2; } .left-controls button { width: 100%; flex-grow: 1; } .spin-button { width: 100%; min-width: 80px; height: 80px; border-radius: 500px; border: 3px solid #722B83; background: linear-gradient(0deg,rgba(65, 18, 86, 1) 0%, rgba(80, 23, 104, 1) 100%); background-size: contain; display: flex; align-items: center; justify-content: center; transition: transform 0.2s ease, opacity 0.2s ease; box-shadow: 0 0 10px rgba(159, 54, 241, 0.3); } .spin-button svg { width: 50px; height: 50px; color: #f0c05a; } .spin-button:hover { transform: scale(1.05); } .spin-button:active { transform: scale(0.95); } .spin-button.dimmed { opacity: 0.5; pointer-events: none; } .spin-button.spinning { animation: pulse 1s ease-in-out infinite; } .promocode-section { background: rgba(28, 17, 41, 0.6); border: 2px solid rgba(240, 192, 90, 0.4); border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(159, 54, 241, 0.2); margin-top: 4px; } .promocode-title { font-family: 'Cinzel', serif; font-size: 1.1em; color: #f0c05a; margin-bottom: 12px; text-align: center; display: flex; align-items: center; justify-content: center; gap: 10px; font-weight: 700; } .promocode-title i { font-size: 1.2em; color: #9f36f1; animation: rotate-ticket 3s ease-in-out infinite; } @keyframes rotate-ticket { 0%, 100% { transform: rotate(0deg); } 25% { transform: rotate(-10deg); } 75% { transform: rotate(10deg); } } .promocode-info { background: rgba(159, 54, 241, 0.15); border: 1px solid rgba(159, 54, 241, 0.4); border-radius: 8px; padding: 10px; margin-bottom: 15px; display: flex; align-items: center; gap: 10px; } .promocode-info i { color: #9f36f1; font-size: 1.1em; flex-shrink: 0; } .promocode-info p { font-size: 0.8em; color: #e0e0ff; margin: 0; line-height: 1.4; } .promocode-input-container { display: flex; gap: 10px; margin-bottom: 10px; } .promocode-input { flex: 1; background: #0d0e12; border: 2px solid #f0c05a; border-radius: 8px; padding: 12px; font-family: 'Cinzel', serif; font-size: 0.95em; color: #e0e0ff; text-transform: uppercase; letter-spacing: 1px; transition: all 0.3s ease; width: 100%; } .promocode-input::placeholder { color: rgba(224, 224, 255, 0.4); text-transform: none; letter-spacing: normal; } .promocode-input:focus { outline: none; border-color: #9f36f1; box-shadow: 0 0 15px rgba(159, 54, 241, 0.3); } .promocode-submit { background: linear-gradient(135deg, #9f36f1, #7b2cbf); color: #e0e0ff; padding: 12px 20px; font-family: 'Cinzel', serif; font-size: 0.9em; font-weight: 700; border: none; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 8px; white-space: nowrap; box-shadow: 0 4px 12px rgba(159, 54, 241, 0.3); } .promocode-submit i { font-size: 1.1em; color: #f0c05a; } .promocode-submit:hover { background: linear-gradient(135deg, #7b2cbf, #9f36f1); transform: translateY(-2px); box-shadow: 0 6px 16px rgba(159, 54, 241, 0.4); } .promocode-submit:active { transform: translateY(0); } .promocode-submit:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .promocode-error { font-size: 0.8em; text-align: center; min-height: 1.2em; font-weight: 600; transition: all 0.3s ease; } /* Responsive adjustments */ @media (max-width: 360px) { .promocode-section { padding: 12px; } .promocode-title { font-size: 1em; } .promocode-input { font-size: 0.85em; padding: 10px; } .promocode-submit { font-size: 0.85em; padding: 10px 16px; } .promocode-info p { font-size: 0.75em; } } @media (max-width: 320px) { .promocode-input-container { flex-direction: column; } .promocode-submit { width: 100%; justify-content: center; } } @media (max-width: 350px) { .controls { width: fit-content; flex-wrap: wrap; } .spin-button { width: 100%; } .right-controls { display: flex; } .gameSettings {flex-wrap: wrap;} } @media (max-width: 270px) { .controls { width: fit-content; flex-wrap: wrap; } .spin-button { width: 100%; } .left-controls, .right-controls { width: 100%; } .controls { } } @keyframes pulse { 0% { box-shadow: 0 0 10px rgba(159, 54, 241, 0.3); } 50% { box-shadow: 0 0 20px rgba(159, 54, 241, 0.5); } 100% { box-shadow: 0 0 10px rgba(159, 54, 241, 0.3); } } .button { font-family: 'Cinzel', serif; font-size: 1.2em; font-weight: 700; border: 3px solid #722B83; background: linear-gradient(0deg,rgba(65, 18, 86, 1) 0%, rgba(80, 23, 104, 1) 100%); border-radius: 5px; color: #f0c05a; display: flex; justify-content: center; height: 36px; padding: 0 20px; display: flex; justify-content: center; align-items: center; } .button svg { margin-right: 5px; width: 20px; color: #f0c05a;} .button svg path { color: #f0c05a;} .controls { display: flex; align-items: center; } .control-button .bet-settings-button, .auto-button { cursor: pointer; display: flex; align-items: center; justify-content: center; text-transform: uppercase; transition: transform 0.2s ease, opacity 0.2s ease; width: 100%; } .control-button:hover { transform: scale(1.05); } .control-button:active { transform: scale(0.95); } .control-button.dimmed { opacity: 0.5; pointer-events: none; } .paytable-button { border: none; cursor: pointer; display: block; text-align: center; text-transform: uppercase; transition: transform 0.2s ease; width: 100%; height: 100%; padding: 10px; } .paytable-button:hover { transform: scale(1.05); } .paytable-button:active { transform: scale(0.95); } .button-table { font-family: 'Cinzel', serif; font-size: 1.2em; font-weight: 700; border: 3px solid #f0c05a; background: #1C1129; border-radius: 13px; color: #f0c05a; } .button svg { margin-right: 5px; width: 20px; color: #f0c05a;} .button svg path { color: #f0c05a;} .bet-settings-modal, .auto-settings-modal, .paytable-modal { position: fixed; top: 44%; left: 50%; transform: translate(-50%, -50%); background: #1c1129; padding: 15px; border-radius: 12px; border: 1px solid #f0c05a; display: none; z-index: 100; width: 80%; max-width: 300px; box-shadow: 0 0 20px rgba(159, 54, 241, 0.3); } .bet-settings-modal.show, .auto-settings-modal.show, .paytable-modal.show { display: block; } .bet-settings-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; } .bet-settings-label { font-family: 'Cinzel', serif; font-size: 0.8em; color: #f0c05a; } .bet-settings-value { font-size: 0.8em; color: #e0e0ff; font-weight: 400; background: #0d0e12; padding: 5px 10px; border-radius: 6px; } .bet-adjust-button { background: #9f36f1; color: #e0e0ff; padding: 5px 10px; font-size: 0.8em; font-weight: 500; border: none; border-radius: 6px; cursor: pointer; transition: background 0.2s, transform 0.2s ease; } .bet-adjust-button:hover { background: #7b2cbf; transform: scale(1.05); } .bet-adjust-button:active { transform: scale(0.95); } .bet-adjust-button:disabled { background: #4b3a6b; cursor: not-allowed; opacity: 0.5; } .max-bet-button { background: #0d0e12; color: #f0c05a; padding: 5px 10px; font-size: 0.75em; font-weight: 500; border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s; } .max-bet-button:hover { background: #7b2cbf; transform: scale(1.05); } .max-bet-button:active { transform: scale(0.95); } .adjust-group { display: flex; align-items: center; gap: 5px; /* Reduced gap to make + and - closer to the number */ } .ok-button { background: #9f36f1; color: #e0e0ff; padding: 8px; font-family: 'Cinzel', serif; font-size: 0.85em; font-weight: 700; border: none; border-radius: 8px; cursor: pointer; width: 50%; transition: background 0.2s, transform 0.2s ease; text-transform: uppercase; display: block; margin: 0 auto; } .ok-button:hover { background: #7b2cbf; transform: scale(1.05); } .ok-button:active { transform: scale(0.95); } .toggle-auto-spin { background: #9f36f1; color: #e0e0ff; padding: 8px 16px; font-family: 'Cinzel', serif; font-size: 0.85em; font-weight: 700; border: none; border-radius: 8px; cursor: pointer; transition: background 0.2s, transform 0.2s ease; text-transform: uppercase; width: 100%; margin-bottom: 10px; } .toggle-auto-spin.active { background: #f0c05a; color: #0d0e12; } .toggle-auto-spin:active { transform: scale(0.95); } .disabled-setting { opacity: 0.5; pointer-events: none; } .paytable-content { max-height: 400px; overflow-y: auto; padding-right: 10px; } .paytable-content::-webkit-scrollbar { width: 6px; } .paytable-content::-webkit-scrollbar-track { background: #0d0e12; border-radius: 3px; } .paytable-content::-webkit-scrollbar-thumb { background: #f0c05a; border-radius: 3px; } .paytable-content h3 { font-family: 'Cinzel', serif; font-size: 1em; color: #f0c05a; margin-bottom: 10px; text-align: center; } .paytable-content p { font-size: 0.8em; color: #e0e0ff; margin-bottom: 15px; } .paytable-item { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; } .paytable-item img { width: 40px; height: 40px; object-fit: contain; filter: drop-shadow(0 0 5px rgba(159, 54, 241, 0.3)); } .paytable-item span { font-size: 0.8em; color: #e0e0ff; } .leaderboard-header { text-align: center; margin-bottom: 10px; } .leaderboard-trophy { font-size: 2.5em; color: #f0c05a; margin-bottom: 10px; filter: drop-shadow(0 0 10px rgba(240, 192, 90, 0.5)); } .leaderboard-title { font-family: 'Cinzel', serif; font-size: 1.5em; color: #f0c05a; margin-bottom: 5px; font-weight: 700; } .leaderboard-subtitle { font-size: 0.85em; color: #e0e0ff; opacity: 0.8; } .leaderboard-podium { display: flex; justify-content: center; align-items: flex-end; gap: 8px; margin-bottom: 15px; padding: 0 10px; } .podium-item { display: flex; flex-direction: column; align-items: center; flex: 1; max-width: 110px; } .podium-rank { font-size: 1.8em; margin-bottom: 8px; } .podium-avatar { width: 50px; height: 50px; border-radius: 50%; background: linear-gradient(135deg, #6496f3, #1044ee); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1em; color: #fff; margin-bottom: 8px; border: 2px solid #f0c05a; box-shadow: 0 4px 10px rgba(159, 54, 241, 0.3); } .podium-avatar-winner { width: 60px; height: 60px; font-size: 1.2em; animation: winner-glow 2s ease-in-out infinite; } @keyframes winner-glow { 0%, 100% { box-shadow: 0 4px 10px rgba(240, 192, 90, 0.5); } 50% { box-shadow: 0 6px 20px rgba(240, 192, 90, 0.8); } } .podium-name { font-family: 'Cinzel', serif; font-size: 0.75em; color: #e0e0ff; margin-bottom: 5px; font-weight: 600; text-align: center; } .podium-score { font-size: 0.7em; color: #f0c05a; margin-bottom: 10px; display: flex; align-items: center; gap: 5px; font-weight: 600; } .currency-icon-small { width: 17px; height: 17px; vertical-align: middle; } .podium-base { width: 100%; background: linear-gradient(180deg, #9f36f1, #7b2cbf); border-radius: 8px 8px 0 0; display: flex; align-items: center; justify-content: center; font-family: 'Cinzel', serif; font-size: 1.2em; font-weight: 700; color: #fff; border: 2px solid #f0c05a; border-bottom: none; } .podium-base-first { height: 80px; background: linear-gradient(180deg, #f0c05a, #d4a849); } .podium-base-second { height: 60px; background: linear-gradient(180deg, #c0c0c0, #a8a8a8); } .podium-base-third { height: 40px; background: linear-gradient(180deg, #cd7f32, #b87333); } .podium-first { order: 2; } .podium-second { order: 1; } .podium-third { order: 3; } .leaderboard-list { display: flex; flex-direction: column; gap: 8px; } .leaderboard-item { display: flex; align-items: center; gap: 12px; background: rgba(28, 17, 41, 0.6); padding: 5px; border-radius: 10px; border: 1px solid rgba(240, 192, 90, 0.2); transition: all 0.2s ease; } .leaderboard-item:hover { background: rgba(28, 17, 41, 0.9); border-color: rgba(240, 192, 90, 0.5); transform: translateX(3px); } .leaderboard-item-you { background: rgba(159, 54, 241, 0.2); border: 2px solid #9f36f1; } .leaderboard-position { font-family: 'Cinzel', serif; font-size: 1em; font-weight: 700; color: #f0c05a; min-width: 25px; text-align: center; } .leaderboard-player { display: flex; align-items: center; gap: 10px; flex: 1; } .leaderboard-avatar { width: 35px; height: 35px; border-radius: 50%; background: linear-gradient(135deg, #9f36f1, #7b2cbf); display: flex; align-items: center; justify-content: center; font-family: 'Cinzel', serif; font-weight: 700; font-size: 0.75em; color: #fff; border: 1px solid #f0c05a; } .leaderboard-name { font-size: 0.85em; color: #e0e0ff; font-weight: 500; } .leaderboard-score { font-size: 0.8em; color: #f0c05a; font-weight: 600; display: flex; align-items: center; gap: 5px; white-space: nowrap; } .leaderboard-info { background: rgba(159, 54, 241, 0.15); border: 1px solid rgba(159, 54, 241, 0.4); border-radius: 10px; padding: 12px; margin-top: 15px; display: flex; align-items: flex-start; gap: 8px; } .leaderboard-info i { color: #9f36f1; font-size: 1.2em; margin-top: 2px; flex-shrink: 0; } .leaderboard-info p { font-size: 0.8em; color: #e0e0ff; line-height: 1.5; margin: 0; } .referral-header { text-align: center; margin-bottom: 20px; } .referral-gift-icon { font-size: 2.5em; color: #f0c05a; margin-bottom: 10px; filter: drop-shadow(0 0 10px rgba(240, 192, 90, 0.5)); } .referral-title { font-family: 'Cinzel', serif; font-size: 1.5em; color: #f0c05a; margin-bottom: 5px; font-weight: 700; } .referral-subtitle { font-size: 0.9em; color: #e0e0ff; opacity: 0.9; font-weight: 500; } .referral-stats { display: flex; gap: 10px; margin-bottom: 20px; } .referral-stat-box { flex: 1; background: linear-gradient(135deg, rgba(159, 54, 241, 0.2), rgba(123, 44, 191, 0.2)); border: 2px solid #9f36f1; border-radius: 12px; padding: 15px; text-align: center; box-shadow: 0 4px 15px rgba(159, 54, 241, 0.2); } .stat-number { font-family: 'Cinzel', serif; font-size: 1.8em; color: #f0c05a; font-weight: 700; margin-bottom: 5px; display: flex; align-items: center; justify-content: center; gap: 5px; } .stat-label { font-size: 0.75em; color: #e0e0ff; opacity: 0.8; } .referral-link-section { margin-bottom: 15px; } .referral-link-title { font-family: 'Cinzel', serif; font-size: 0.85em; color: #f0c05a; margin-bottom: 8px; text-align: center; } .referral-link-container { background: #0d0e12; padding: 12px; border-radius: 8px; border: 2px solid #9f36f1; font-size: 0.75em; color: #e0e0ff; display: flex; align-items: center; gap: 8px; box-shadow: 0 0 15px rgba(159, 54, 241, 0.2); } .referral-link-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; } .share-button { background: linear-gradient(135deg, #9f36f1, #7b2cbf); color: #e0e0ff; padding: 12px 20px; font-family: 'Cinzel', serif; font-size: 0.95em; font-weight: 700; border: none; border-radius: 10px; cursor: pointer; width: 100%; transition: all 0.3s ease; text-transform: uppercase; display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 20px; box-shadow: 0 4px 15px rgba(159, 54, 241, 0.3); } .share-button i { font-size: 1.2em; color: #f0c05a; } .share-button:hover { background: linear-gradient(135deg, #7b2cbf, #9f36f1); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(159, 54, 241, 0.4); } .share-button:active { transform: translateY(0px); } .referral-info-box { background: rgba(159, 54, 241, 0.15); border: 1px solid rgba(159, 54, 241, 0.4); border-radius: 10px; padding: 15px; display: flex; align-items: flex-start; gap: 12px; } .referral-info-box i { color: #9f36f1; font-size: 1.2em; margin-top: 2px; flex-shrink: 0; } .referral-info-title { font-family: 'Cinzel', serif; font-size: 0.85em; color: #f0c05a; margin-bottom: 8px; font-weight: 700; } .referral-info-list { list-style: none; padding: 0; margin: 0; } .referral-info-list li { font-size: 0.75em; color: #e0e0ff; line-height: 1.6; padding-left: 15px; position: relative; margin-bottom: 5px; } .referral-info-list li:before { content: "✓"; position: absolute; left: 0; color: #f0c05a; font-weight: 700; } .telegram-nav { position: fixed; bottom: 0; left: 0; right: 0; background: #1c1129; display: flex; justify-content: space-evenly; align-items: center; padding: 12px 0; border-top: 1px solid #f0c05a; z-index: 1000; height: 72px; } .telegram-nav-button { background: none; border: none; color: #e0e0ff; font-family: 'Cinzel', serif; font-size: 0.9em; font-weight: 400; display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px; cursor: pointer; transition: all 0.2s ease; } .telegram-nav-button i { font-size: 1.7em; color: #f0c05a; } .telegram-nav-button:hover, .telegram-nav-button.active { color: #f0c05a; transform: scale(1.1); } .telegram-nav-button:hover i, .telegram-nav-button.active i { color: #f0c05a; } .telegram-nav-button:active { transform: scale(0.95); } .deposit-withdraw-row { display: flex; gap: 10px; margin-bottom: 15px; margin-top: 15px; justify-content: center; } .deposit-btn, .withdraw-btn { flex: 1; background: linear-gradient(135deg, #1c1129, #2a1b3e); color: #e0e0ff; padding: 12px; font-family: 'Cinzel', serif; font-size: 0.95em; font-weight: 700; border: 1px solid #f0c05a; border-radius: 8px; cursor: pointer; transition: all 0.2s ease, transform 0.2s ease; text-align: center; max-width: 140px; box-shadow: 0 4px 8px rgba(159, 54, 241, 0.2); } .deposit-btn.active, .withdraw-btn.active { background: linear-gradient(135deg, #9f36f1, #7b2cbf); color: #e0e0ff; box-shadow: 0 6px 12px rgba(159, 54, 241, 0.3); } .deposit-btn:hover, .withdraw-btn:hover { background: linear-gradient(135deg, #7b2cbf, #9f36f1); transform: scale(1.05); box-shadow: 0 6px 12px rgba(159, 54, 241, 0.3); } .deposit-btn:active, .withdraw-btn:active { transform: scale(0.95); } .currency-section { margin-bottom: 10px; } .currency-title { font-family: 'Cinzel', serif; font-size: 0.85em; color: #f0c05a; margin-bottom: 8px; } .currency-item { display: flex; align-items: center; gap: 8px; background: #0d0e12; padding: 10px; border-radius: 8px; border: 1px solid #f0c05a; justify-content: center; } .currency-name { font-size: 0.9em; color: #e0e0ff; } .currency-icon { width: 18px; height: 18px; vertical-align: middle; } .qr-section { text-align: center; margin-bottom: 15px; } .qr-code { width: 120px; height: 120px; border-radius: 8px; margin: 0 auto 8px; filter: drop-shadow(0 0 5px rgba(159, 54, 241, 0.3)); } .qr-label { font-size: 0.75em; color: #e0e0ff; opacity: 0.8; } .address-section { margin-bottom: 15px; } .address-container { background: #0d0e12; padding: 10px; border-radius: 8px; border: 1px solid #f0c05a; font-size: 0.8em; color: #e0e0ff; display: flex; align-items: center; gap: 5px; } .address-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc(100% - 25px); } .copy-icon { font-size: 0.9em; color: #f0c05a; cursor: pointer; transition: filter 0.2s; } .copy-icon:hover { filter: brightness(70%); } .withdrawal-section { margin-bottom: 15px; } .min-withdraw-note { font-size: 0.75em; color: #e0e0ff; margin-bottom: 10px; opacity: 0.8; } .withdrawal-input { width: 100%; background: #0d0e12; padding: 10px; border-radius: 8px; border: 1px solid #f0c05a; font-size: 0.8em; color: #e0e0ff; margin-bottom: 0px; } .withdrawal-input:focus { outline: none; border-color: #9f36f1; } .error-message { color: #ff4d4d; font-size: 0.7em; margin-top: 2px; margin-bottom: 6px; min-height: 1em; text-align: center; } .max-button { background: #0d0e12; color: #f0c05a; padding: 2px 10px; height: 35px; font-size: 0.85em; font-weight: 500; border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s ease, transform 0.2s ease; margin-top: 1px; /* Added margin to move the button down */ } .max-button:hover { background: #7b2cbf; transform: scale(1.05); } .max-button:active { transform: scale(0.95); } .withdraw-submit, .deposit-submit { background: #9f36f1; color: #e0e0ff; padding: 10px; font-family: 'Cinzel', serif; font-size: 0.9em; font-weight: 700; border: none; border-radius: 8px; cursor: pointer; width: 100%; transition: all 0.2s ease, transform 0.2s ease; text-transform: uppercase; } .withdraw-submit:hover, .deposit-submit:hover { background: #7b2cbf; transform: scale(1.05); } .withdraw-submit:active, .deposit-submit:active { transform: scale(0.95); } .deposit-warning { font-size: 0.75em; color: #e0e0ff; text-align: center; margin-top: 10px; line-height: 1.5; opacity: 0.9; } .stars-deposit-section { margin-top: 20px; } .stars-info { background: rgba(159, 54, 241, 0.15); border: 1px solid rgba(159, 54, 241, 0.4); border-radius: 10px; padding: 12px; margin-bottom: 15px; display: flex; align-items: flex-start; gap: 10px; } .stars-info i { color: #9f36f1; font-size: 1.2em; margin-top: 2px; flex-shrink: 0; } .stars-info p { font-size: 0.8em; color: #e0e0ff; line-height: 1.5; margin: 0; } .deposit-amount-container { display: flex; flex-direction: column; gap: 15px; } .deposit-input-wrapper { position: relative; display: flex; align-items: center; background: #0d0e12; border: 2px solid #f0c05a; border-radius: 12px; padding: 10px; box-shadow: 0 0 20px rgba(240, 192, 90, 0.2); transition: all 0.3s ease; } .deposit-input-wrapper:focus-within { border-color: #9f36f1; box-shadow: 0 0 25px rgba(159, 54, 241, 0.4); } .deposit-star-icon { font-size: 1.5em; color: #f0c05a; margin-right: 12px; filter: drop-shadow(0 0 8px rgba(240, 192, 90, 0.6)); } .deposit-amount-input { flex: 1; background: transparent; border: none; color: #e0e0ff; font-family: 'Cinzel', serif; font-size: 1.2em; font-weight: 700; outline: none; padding: 0; } .deposit-amount-input::placeholder { color: rgba(224, 224, 255, 0.4); font-weight: 400; } .deposit-amount-input::-webkit-outer-spin-button, .deposit-amount-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .deposit-amount-input[type=number] { -moz-appearance: textfield; } .deposit-unit { font-family: 'Cinzel', serif; font-size: 1em; color: #f0c05a; font-weight: 600; margin-left: 8px; margin-right: 5px; } .deposit-quick-amounts { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; } @media (max-width: 275px) { .deposit-quick-amounts { display: grid; grid-template-columns: repeat(1, 1fr); gap: 10px; } } .footer a { color: #fff; } .quick-amount-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap; /* keep star and number on one line */ background: linear-gradient(135deg, rgba(28, 17, 41, 0.8), rgba(42, 27, 62, 0.8)); border: 2px solid rgba(240, 192, 90, 0.4); border-radius: 10px; padding: 8px 12px; font-family: 'Cinzel', serif; font-size: 0.95em; font-weight: 600; color: #e0e0ff; cursor: pointer; transition: all 0.3s ease; } .quick-amount-btn { display: flex; align-items: center; /* Center items vertically */ } .quick-amount-btn:before { content: "⭐"; font-size: 1.1em; } .quick-amount-btn:hover { background: linear-gradient(135deg, rgba(159, 54, 241, 0.3), rgba(123, 44, 191, 0.3)); border-color: #9f36f1; transform: translateY(-2px); box-shadow: 0 4px 15px rgba(159, 54, 241, 0.3); } .quick-amount-btn:active { transform: translateY(0); } .deposit-submit { background: linear-gradient(135deg, #9f36f1, #7b2cbf); color: #e0e0ff; padding: 12px 20px; font-family: 'Cinzel', serif; font-size: 1em; font-weight: 700; border: none; border-radius: 12px; cursor: pointer; width: 100%; transition: all 0.3s ease; text-transform: uppercase; display: flex; align-items: center; justify-content: center; gap: 10px; box-shadow: 0 4px 15px rgba(159, 54, 241, 0.3); } .deposit-submit i { font-size: 1.2em; color: #f0c05a; } .deposit-submit:hover { background: linear-gradient(135deg, #7b2cbf, #9f36f1); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(159, 54, 241, 0.4); } .deposit-submit:active { transform: translateY(0); } .stars-packages { display: flex; flex-direction: column; gap: 12px; } .star-package-btn { background: linear-gradient(135deg, rgba(28, 17, 41, 0.8), rgba(42, 27, 62, 0.8)); border: 2px solid #f0c05a; border-radius: 12px; padding: 15px 20px; display: flex; align-items: center; gap: 15px; cursor: pointer; transition: all 0.3s ease; position: relative; overflow: hidden; } .star-package-btn:hover { background: linear-gradient(135deg, rgba(159, 54, 241, 0.3), rgba(123, 44, 191, 0.3)); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(240, 192, 90, 0.4); } .star-package-btn:active { transform: translateY(0); } .star-package-btn.featured { border-color: #9f36f1; background: linear-gradient(135deg, rgba(159, 54, 241, 0.2), rgba(123, 44, 191, 0.2)); box-shadow: 0 4px 15px rgba(159, 54, 241, 0.3); } .star-package-btn.featured:hover { box-shadow: 0 6px 25px rgba(159, 54, 241, 0.5); } .popular-badge { position: absolute; top: -1px; right: 15px; background: linear-gradient(135deg, #9f36f1, #7b2cbf); color: #fff; font-size: 0.65em; font-weight: 700; padding: 4px 12px; border-radius: 0 0 8px 8px; text-transform: uppercase; letter-spacing: 0.5px; box-shadow: 0 2px 8px rgba(159, 54, 241, 0.4); } .star-package-btn i { font-size: 2em; color: #f0c05a; filter: drop-shadow(0 0 8px rgba(240, 192, 90, 0.6)); } .star-package-btn .star-amount { font-family: 'Cinzel', serif; font-size: 1.1em; color: #e0e0ff; font-weight: 700; flex: 1; } .star-package-btn .star-bonus { font-size: 0.9em; color: #f0c05a; font-weight: 600; background: rgba(240, 192, 90, 0.1); padding: 5px 12px; border-radius: 6px; border: 1px solid rgba(240, 192, 90, 0.3); } .withdrawal-status { background: rgba(159, 54, 241, 0.15); border: 2px solid rgba(159, 54, 241, 0.5); border-radius: 10px; padding: 15px; margin-top: 20px; text-align: center; } .withdrawal-status i { color: #f0c05a; font-size: 2em; margin-bottom: 10px; display: block; animation: pulse-icon 2s ease-in-out infinite; } @keyframes pulse-icon { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .withdrawal-status p { font-size: 0.85em; color: #e0e0ff; margin: 5px 0; line-height: 1.5; } .withdrawal-status p strong { color: #f0c05a; font-family: 'Cinzel', serif; font-size: 1.1em; display: block; margin-bottom: 8px; } .withdrawal-input { width: 100%; background: #0d0e12; padding: 10px; border-radius: 8px; border: 1px solid #f0c05a; font-size: 0.8em; color: #e0e0ff; margin-bottom: 8px; } .withdrawal-input:focus { outline: none; border-color: #9f36f1; } .withdrawal-input:disabled { opacity: 0.5; cursor: not-allowed; } .profile-header { display: flex; align-items: center; gap: 20px; margin-bottom: 10px; justify-content: center; background-color: #9f36f12c; padding: 10px; border-radius: 10px; border: 3px solid #190a25; } .profile-avatar { width: 80px; height: 80px; border-radius: 50%; border: 1px solid #f0c05a; filter: drop-shadow(0 0 5px rgba(159, 54, 241, 0.3)); } .profile-info h3 { font-family: 'Cinzel', serif; font-size: 1.2em; color: #f0c05a; margin-bottom: 8px; text-align: center; } .profile-info p { font-size: 0.9em; color: #e0e0ff; margin: 5px 0; opacity: 0.8; text-align: center; } .profile-details { text-align: center; } .language-selector select { background: #0d0e12; color: #e0e0ff; padding: 8px; border: 1px solid #f0c05a; border-radius: 8px; font-size: 1em; width: 100%; height: 55px; cursor: pointer; display: block; /* margin-top: 15px; margin-bottom: 15px; */ } .gameSettings { margin-top: 10px; display: flex; gap: 10px; /* margin-bottom: 20px; */ } .gameSettings .profile-section { width: 100%; } .language-selector { width: 100%; } .language-selector select:focus { outline: none; border-color: #9f36f1; } .privacy-policy { color: #9f36f1; font-size: 0.8em; text-decoration: none; font-weight: 500; display: block; margin: 0 auto; width: fit-content; margin-bottom: 0px; margin-top: 10px; } .privacy-policy:hover { text-decoration: underline; } /* Add these styles to your style.css file */ .profile-section { background: rgba(28, 17, 41, 0.6); border: 1px solid rgba(240, 192, 90, 0.3); border-radius: 8px; padding: 8px; margin-bottom: 8px; } .profile-setting-row { display: flex; align-items: center; justify-content: space-between; } .setting-label { display: flex; align-items: center; gap: 10px; font-family: 'Cinzel', serif; font-size: 1.2em; /* already bigger */ color: #e0e0ff; white-space: nowrap; /* prevents line breaks */ } .setting-label i { color: #f0c05a; font-size: 1em; } .music-toggle-btn .music-status { display: block; } .music-toggle-btn i { margin: 0; } .music-toggle-btn { display: flex; align-items: center; background: linear-gradient(135deg, #1c1129, #2a1b3e); border: 2px solid #f0c05a; border-radius: 25px; padding: 8px 16px; font-family: 'Cinzel', serif; font-size: 0.85em; font-weight: 700; color: #e0e0ff; transition: all 0.3s ease; min-width: 40px; width: 40px; justify-content: center; float: right; } .music-toggle-btn i { font-size: 1.1em; color: #f0c05a; transition: all 0.3s ease; } .music-toggle-btn.active { background: linear-gradient(135deg, #9f36f1, #7b2cbf); border-color: #9f36f1; box-shadow: 0 0 15px rgba(159, 54, 241, 0.4); } .music-toggle-btn.active i { color: #f0c05a; animation: music-pulse 1.5s ease-in-out infinite; } .music-toggle-btn:hover { transform: scale(1.05); box-shadow: 0 4px 12px rgba(159, 54, 241, 0.3); } .music-toggle-btn:active { transform: scale(0.95); } .music-status { font-weight: 700; letter-spacing: 0.5px; } @keyframes music-pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.2); opacity: 0.8; } } .footer { font-size: 0.75em; color: #e0e0ff; text-align: center; margin-top: 5px; margin-bottom: 0px; opacity: 0.8; } .footer img { width: 50%; max-height: 100px; object-fit: contain; border-radius: 8px; margin-top: 10px; filter: drop-shadow(0 0 5px rgba(159, 54, 241, 0.3)); } .btnCombo { display: flex; flex-wrap: nowrap; gap: 10px; } @media (max-width: 360px) { body { padding: calc(10px + env(safe-area-inset-top)) 8px calc(70px + env(safe-area-inset-bottom)); } .casino-container.fullscreen { margin-top: calc(35px + env(safe-area-inset-top)); } .character-image, .footer img { max-height: 80px; } .reel-container { height: 180px; } .symbol { height: 60px; } .symbol img { width: 45px; height: 45px; } .telegram-nav { height: 60px; padding: 10px 0; gap: 10px; } .telegram-nav-button { font-size: 0.85em; padding: 8px; flex: 1; max-width: 60px; } .telegram-nav-button i { font-size: 1.4em; } .deposit-btn, .withdraw-btn { font-size: 0.8em; padding: 8px; max-width: 120px; } .qr-code { width: 100px; height: 100px; } .info-box h3 { font-size: 0.7em; } .info-box .value { font-size: 0.9em; } .slot-machine { padding: 8px; } .max-bet-button { padding: 4px 8px; font-size: 0.7em; } .ok-button { padding: 7px; font-size: 0.8em; } .referral-gift-icon { font-size: 2.5em; } .referral-title { font-size: 1.3em; } .referral-subtitle { font-size: 0.85em; } .stat-number { font-size: 1.5em; } .stat-label { font-size: 0.7em; } .share-button { font-size: 0.85em; padding: 10px 16px; } .referral-info-list li { font-size: 0.7em; } .deposit-warning { font-size: 0.7em; } } @media (max-width: 320px) { body { padding: calc(8px + env(safe-area-inset-top)) 6px calc(60px + env(safe-area-inset-bottom)); } .casino-container.fullscreen { margin-top: calc(30px + env(safe-area-inset-top)); } .character-image, .footer img { max-height: 70px; } .reel-container { height: 150px; } .symbol { height: 50px; } .symbol img { width: 35px; height: 35px; } .telegram-nav { height: 60px; padding: 8px 0; gap: 8px; } .telegram-nav-button { font-size: 0.8em; padding: 6px; flex: 1; max-width: 55px; } .telegram-nav-button i { font-size: 1.3em; } .deposit-btn, .withdraw-btn { font-size: 0.75em; padding: 6px; max-width: 110px; } .qr-code { width: 90px; height: 90px; } .info-box h3 { font-size: 0.65em; } .info-box .value { font-size: 0.85em; } .slot-machine { padding: 6px; } .max-bet-button { padding: 4px 8px; font-size: 0.7em; } .ok-button { padding: 7px; font-size: 0.8em; } .referral-gift-icon { font-size: 2.2em; } .referral-title { font-size: 1.2em; } .referral-subtitle { font-size: 0.8em; } .stat-number { font-size: 1.3em; } .stat-label { font-size: 0.65em; } .share-button { font-size: 0.8em; padding: 10px 14px; } .referral-info-list li { font-size: 0.65em; } .deposit-warning { font-size: 0.65em; } }