WhatsApp Channel Join Now
Telegram Channel Join Now

AI के ज़रिए ख़ुद की EARNING Website कैसे शुरू करें

5/5 - (1 vote)

आज के समय में AI इतना कमाल कर रहा है कि सब लोग अपने काम AI के ज़रिए करवा रहे हैं और ऐसे में Web Developers की मानो नौकरी खतरे में है, क्योंकि आजकल तो लोग YouTuber की videos देखकर AI की सहायता से ही अपनी website का coding करवा रहे हैं।

ऐसे में यदि आप भी अपनी एक website शुरू करना चाहते हैं, तो यही सुनहरा मौका है। आज मैं आपको detail में बताऊंगा कि कैसे आप बिना किसी coding जानकारी के मात्र 20 minutes में ख़ुद की earning website AI की मदद से बना सकते हैं।

वैसे तो काफ़ी AI tools मौजूद हैं, पर अब best AI tool कौनसा होगा, वह भी समझ लेते हैं और उसके लिए हम Google AI Studio का use करेंगे, क्योंकि यह Grok और ChatGPT से थोड़ा बेहतर काम करता है।

मैं आपको नीचे कुछ files जैसे HTML, API और prompt share कर रहा हूँ। आप उनका इस्तेमाल करके अपने लिए एक earning website Google Studio AI के ज़रिए बनवा सकते हैं।

सबसे पहले YouTuber पर मैंने इस topic पर एक video डाल रखा है, उसको देख लें, तभी आपको site integration समझ आएगा। फिर आप चाहें तो इस blog में दी गई सभी files और prompt का use करके site बना सकते हैं।

UI Prompt: Spin & Earn Web Application
Overall Layout & Theme:
Theme: A modern, clean web application interface using a blue primary color (--primary-color), white surfaces (--surface-color), and clear typography (Inter font). Uses Font Awesome icons extensively.
Structure (Mobile/Tablet < 992px):
Fixed Header at the top containing a menu toggle button (hamburger icon) on the left, the app logo ("SpinEarn") centered (only visible on smaller screens), and a user profile icon on the right.
Slide-out Sidebar on the left, initially hidden. Activated by the menu toggle. Covers the content when open.
Main Content Area takes up the rest of the screen below the header.
Semi-transparent Overlay appears behind the sidebar when it's open, dimming the main content. Clicking it closes the sidebar.
Structure (Desktop >= 992px):
A two-column grid layout.
Fixed Sidebar permanently visible on the left.
Header at the top-right, containing only the user profile icon and greeting. The menu toggle and mobile logo are hidden.
Main Content Area occupies the main section to the right of the sidebar and below the header.
Header:
Menu Toggle: (Mobile/Tablet only) A button with a hamburger icon (fas fa-bars) to open/close the sidebar.
Logo: (Mobile/Tablet only) Text "SpinEarn".
User Profile: An icon (fas fa-user-circle). On wider screens (>= 768px), it also displays a greeting like "Hi, Guest!" or "Hi, [Username]!".
Sidebar:
Appearance: Dark background (--dark-color) with light text.
Navigation: A vertical list of links (<a> tags).
Each link has a Font Awesome icon and text (e.g., <i class="fas fa-tachometer-alt"></i> Dashboard).
The currently active page's link is highlighted (background color, left border).
Conditional Links (Login State):
Logged Out: Shows "Login" and "Register" links. Hides all other app links and "Logout".
Logged In: Hides "Login" and "Register". Shows app links ("Dashboard", "Spin Wheel", "Slot Machine", "Scratch Cards", "Daily Bonus", "Refer & Earn", "Redeem Points", "History", "Profile") and the "Logout" link.
Main Content Area:
Displays one "page" (.page-content) at a time, corresponding to the selected sidebar link. Pages fade in (fadeIn animation).
Default View: Login page (#login-content) if logged out, Dashboard (#dashboard-content) if logged in.
Specific Page Views:
Login (#login-content):
A centered card (.auth-page) with a "Login" title.
Form with "Username" and "Password" input fields and labels.
A "Login" button (shows spinner when processing).
An area for displaying login error messages (#login-error-message).
A link to the Register page.
Register (#register-content):
Similar card layout with a "Register" title.
Form with "Username", "Password", and "Confirm Password" fields and labels, including input validation hints (length, pattern).
A "Register" button (shows spinner when processing).
Areas for error (#register-error-message) and success (#register-success-message) messages.
A link to the Login page.
Dashboard (#dashboard-content):
Welcome section: "Welcome Back, [Username]!" message.
Stats Panel: A grid (1 column mobile, 2 tablet, 4 desktop) of statistic cards (.stat-card). Each card shows:
A colored circular icon (e.g., fas fa-coins).
A label (e.g., "Balance").
A value (e.g., "1,234 pts", fetched dynamically). Includes Balance, Spins Left, Slot Tokens, Scratch Cards.
Spin Action Section: A card encouraging users to spin, with a prominent "Go to Spin Wheel" button.
Feature Grid: A grid (1 column mobile, 2 tablet/desktop, 4 large desktop) of feature cards (.feature-card). Each card represents a game/feature and includes:
A colored square icon (e.g., fas fa-calendar-check).
A title (e.g., "Daily Bonus").
A short description.
A button linking to the respective feature page (e.g., "Go to Daily Bonus").
Activity Log Section: Shows the 5 most recent user activities in a list (.activity-list). Each list item displays:
A colored circular icon representing the activity type (e.g., spin win, bonus claim).
A description of the activity (e.g., "You won 50 points from the wheel").
A relative timestamp (e.g., "5 min ago").
A "View All Activity" link navigating to the History page.
Spin Wheel (#spin-wheel-content):
Title: "Spin the Wheel!".
A container (.spin-wheel-container) featuring:
A visual representation of a circular spinning wheel (.wheel) composed of colored segments, each with a prize value label. Segments are generated dynamically by JS based on spinWheelSegments.
A static pointer (.wheel-pointer) positioned on the left side of the wheel, indicating the winning segment.
A decorative center dot (.wheel-center).
Text displaying "Spins Left: [Number]".
A "Spin Now" button (Green, shows spinner during spin). Disabled if no spins are left or already spinning.
A text area (#spin-result) to display the outcome ("Spinning...", "You won X points!", "Try Again!", etc.).
Slot Machine (#slot-machine-content):
Title: "Slot Machine".
A container (.slots-container) featuring:
Info text: "Cost: 1 Token | Your Tokens: [Number]".
Three vertical reels (.reel) inside a frame (.slots-reels). Each reel displays a vertical strip of symbols (.reel-symbol, e.g., '🍒', '7') that scroll vertically during play. Only 3 symbols per reel are fully visible at rest.
A "Play (1 Token)" button (Red, shows spinner during spin). Disabled if no tokens are left or already spinning.
A text area (#slots-result) to display the outcome ("Spinning reels...", "Match!", "No Win", etc.).
Scratch Cards (#scratch-cards-content):
Title: "Scratch & Win".
A container (.scratch-card-container) featuring:
Info text: "Cards Left Today: [Number]".
A rectangular scratch area (.scratch-card-area). Initially covered by a gray gradient overlay (.scratch-overlay) saying "Click to Scratch!". Clicking/tapping simulates scratching.
Underneath the overlay is the result (.scratch-result), initially hidden, showing the prize (e.g., "50 Pts!", "No Win") once revealed.
A "Scratch Card" button (Yellow/Orange, shows spinner during scratch). Disabled if no cards left or already scratching. Also triggers the scratch action.
A text area (#scratch-status) displaying instructions or results ("Click the card above!", "Scratching...", "You won X points!", etc.).
Daily Bonus (#daily-bonus-content):
A placeholder-style page (.placeholder-page) with:
A large calendar icon (fas fa-calendar-check).
Title: "Daily Bonus".
A message indicating eligibility (#daily-bonus-message).
A "Claim Today's Bonus" button. Disabled and text changes if already claimed. Shows spinner when claiming.
A status text area (#daily-bonus-status) for feedback.
Refer & Earn (#refer-earn-content):
A placeholder-style page with:
A large user-plus icon (fas fa-user-plus).
Title: "Refer & Earn".
Descriptive text about the referral program.
The user's unique referral code (displayed prominently).
A "Copy Code" button.
A status message (#copy-status) indicating if the code was copied.
A note indicating referral tracking is a simulation in this demo.
Redeem Points (#redeem-content):
Title: "Redeem Points (Withdrawal Simulation)".
Displays the user's current point balance (#redeem-points-balance).
A form (#withdraw-form) for submitting a withdrawal request (clearly marked as simulation):
Amount (Points) input (numeric, min 100).
Withdrawal Method dropdown (PayPal, Bank, Voucher).
Details input (e.g., email/account - with a warning not to enter real info).
A "Request Withdrawal" button (shows spinner when processing).
Areas for displaying form validation errors (#withdraw-error-message) or success messages (#withdraw-success-message).
History (#history-content):
Title: "Activity History".
A description.
A comprehensive list (#full-activity-list) of all user activities, formatted identically to the dashboard activity log (icon, description, time ago). Populated dynamically when the page is viewed.
Profile (#profile-content):
A placeholder-style page with:
A large user-edit icon (fas fa-user-edit).
Title: "Profile".
Displays the Username.
Displays the "Member Since" date.
A non-functional "Change Password" button.
Common UI Elements & Behaviors:
Buttons: Consistent styling, clear text, often include icons. Show a loading spinner (.spinner) and become disabled (.loading, disabled) during asynchronous operations (API calls, animations). Have hover and active states.
Forms: Standard input fields with labels, clear submit buttons. Validation feedback often provided via error messages.
Error/Success Messages: Distinctively styled message boxes (.error-message, .success-message) used for feedback after actions or form submissions.
Loading Indicators: Spinners used within buttons to indicate processing. Initial data load might show placeholder text ("---") in stats/info areas.
Interactivity: Relies heavily on JavaScript for navigation, API calls, dynamic content updates (points, stats, activity logs), game logic (spinning, scratching), form handling, and UI state management (login status, button disabling).

Index.html File

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SpinEarn - Spin & Earn</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
    <style>
        :root {
            --primary-color: #007bff; /* Blue */
            --primary-color-dark: #0056b3;
            --primary-color-light: #4da3ff;
            --surface-color: #ffffff; /* White */
            --dark-color: #343a40;    /* Dark grey for sidebar */
            --text-color-on-dark: #f8f9fa;
            --text-color-on-light: #212529;
            --light-gray: #f8f9fa;
            --medium-gray: #e9ecef;
            --dark-gray: #6c757d;
            --success-color: #28a745;
            --danger-color: #dc3545;
            --warning-color: #ffc107;
            --info-color: #17a2b8;
            --font-family: 'Inter', sans-serif;
            --header-height-mobile: 60px;
            --sidebar-width-desktop: 250px;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: var(--font-family);
            background-color: var(--light-gray);
            color: var(--text-color-on-light);
            line-height: 1.6;
            overflow-x: hidden; /* Prevent horizontal scroll from sidebar transitions */
        }

        .app-container {
            display: flex;
            flex-direction: column;
            min-height: 100vh;
        }

        /* Header */
        .app-header {
            background-color: var(--primary-color);
            color: var(--surface-color);
            padding: 0 15px;
            height: var(--header-height-mobile);
            display: flex;
            align-items: center;
            justify-content: space-between;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            z-index: 1000;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .menu-toggle-btn {
            background: none;
            border: none;
            color: var(--surface-color);
            font-size: 1.5rem;
            cursor: pointer;
            padding: 10px;
        }

        .logo-mobile {
            font-size: 1.5rem;
            font-weight: 600;
        }

        .user-profile {
            display: flex;
            align-items: center;
        }

        .user-greeting {
            display: none; /* Hidden by default, shown on wider screens */
            margin-right: 10px;
            font-size: 0.9rem;
        }

        .user-profile-icon {
            font-size: 1.8rem;
        }

        /* Sidebar */
        .sidebar {
            background-color: var(--dark-color);
            color: var(--text-color-on-dark);
            width: 250px;
            position: fixed;
            top: 0;
            left: -250px; /* Initially hidden */
            height: 100%;
            padding-top: 20px;
            transition: left 0.3s ease-in-out;
            z-index: 1001;
            display: flex;
            flex-direction: column;
            box-shadow: 2px 0 5px rgba(0,0,0,0.1);
        }

        .sidebar.open {
            left: 0;
        }

        .sidebar-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 0 15px 15px 15px;
            border-bottom: 1px solid #495057; /* Slightly lighter than dark-color */
        }

        .sidebar-logo {
            font-size: 1.5rem;
            font-weight: 600;
            color: var(--surface-color);
        }

        .sidebar-close-btn {
            background: none;
            border: none;
            color: var(--text-color-on-dark);
            font-size: 1.2rem;
            cursor: pointer;
        }

        .sidebar-nav ul {
            list-style: none;
            padding: 0;
            margin-top: 15px;
        }

        .sidebar-nav .nav-item a {
            display: flex;
            align-items: center;
            padding: 12px 20px;
            color: var(--text-color-on-dark);
            text-decoration: none;
            transition: background-color 0.2s ease, color 0.2s ease;
        }

        .sidebar-nav .nav-item a i {
            margin-right: 15px;
            width: 20px; /* Ensure icons align */
            text-align: center;
        }

        .sidebar-nav .nav-item a:hover {
            background-color: #495057; /* Slightly lighter than dark-color */
        }

        .sidebar-nav .nav-item.active a {
            background-color: var(--primary-color);
            color: var(--surface-color);
            border-left: 4px solid var(--primary-color-light);
            font-weight: 500;
        }

        /* Sidebar Overlay */
        .sidebar-overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 1000; /* Behind sidebar, above content */
        }

        .sidebar-overlay.active {
            display: block;
        }

        /* Main Content Area */
        .main-content {
            margin-top: var(--header-height-mobile);
            padding: 20px;
            width: 100%;
            flex-grow: 1; /* Takes remaining space */
        }

        .page-content {
            background-color: var(--surface-color);
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            animation: fadeIn 0.5s ease-in-out;
        }

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

        /* --- Page Specific Styles --- */

        /* Auth Pages (Login/Register) */
        .auth-page {
            max-width: 400px;
            margin: 20px auto;
            padding: 25px;
            background-color: var(--surface-color);
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .auth-page h2 {
            text-align: center;
            margin-bottom: 20px;
            color: var(--primary-color);
        }
        .auth-switch {
            text-align: center;
            margin-top: 15px;
            font-size: 0.9rem;
        }
        .auth-switch a {
            color: var(--primary-color);
            text-decoration: none;
            font-weight: 500;
        }
        .auth-switch a:hover {
            text-decoration: underline;
        }

        /* Dashboard */
        #dashboardWelcome {
            margin-bottom: 20px;
            color: var(--dark-color);
        }
        #dashboardWelcome #dashboardUsername {
            color: var(--primary-color);
        }

        .stats-panel {
            display: grid;
            grid-template-columns: 1fr;
            gap: 15px;
            margin-bottom: 25px;
        }
        .stat-card {
            background-color: var(--surface-color);
            padding: 15px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
        }
        .stat-icon {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 1.2rem;
            margin-right: 15px;
        }
        .stat-label {
            font-size: 0.9rem;
            color: var(--dark-gray);
            margin-bottom: 2px;
        }
        .stat-value {
            font-size: 1.3rem;
            font-weight: 600;
            color: var(--dark-color);
        }

        .spin-action-card {
            background-color: var(--primary-color-light);
            color: white;
            padding: 20px;
            border-radius: 8px;
            text-align: center;
            margin-bottom: 25px;
        }
        .spin-action-card h3 { margin-bottom: 10px; }
        .spin-action-card p { margin-bottom: 15px; opacity: 0.9; }

        .feature-grid {
            display: grid;
            grid-template-columns: 1fr;
            gap: 15px;
            margin-bottom: 25px;
        }
        .feature-card {
            background-color: var(--surface-color);
            padding: 20px;
            border-radius: 8px;
            text-align: center;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .feature-icon {
            width: 50px;
            height: 50px;
            border-radius: 8px; /* Square icon */
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 1.5rem;
            margin-bottom: 10px;
        }
        .feature-card h4 {
            margin-bottom: 8px;
            color: var(--dark-color);
        }
        .feature-card p {
            font-size: 0.9rem;
            color: var(--dark-gray);
            margin-bottom: 15px;
            flex-grow: 1;
        }

        .activity-log-section h3 {
            margin-bottom: 10px;
            color: var(--dark-color);
        }
        .activity-list {
            list-style: none;
            padding: 0;
            margin-bottom: 15px;
        }
        .activity-list li {
            display: flex;
            align-items: center;
            padding: 10px 0;
            border-bottom: 1px solid var(--medium-gray);
            font-size: 0.9rem;
        }
        .activity-list li:last-child {
            border-bottom: none;
        }
        .activity-list .activity-item-icon {
            width: 30px;
            height: 30px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 0.9rem;
            margin-right: 10px;
            flex-shrink: 0;
        }
        .activity-list .activity-description {
            flex-grow: 1;
            color: var(--dark-color);
        }
        .activity-list .activity-timestamp {
            font-size: 0.8rem;
            color: var(--dark-gray);
            margin-left: 10px;
            white-space: nowrap;
        }
        .view-all-activity-link {
            display: inline-block;
            color: var(--primary-color);
            text-decoration: none;
            font-weight: 500;
        }
        .view-all-activity-link:hover {
            text-decoration: underline;
        }
        .empty-history {
            text-align: center;
            padding: 20px;
            color: var(--dark-gray);
        }


        /* Spin Wheel Page */
        .spin-wheel-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
        }
        .wheel-area {
            position: relative;
            width: 280px;
            height: 280px;
            margin-bottom: 20px;
        }
        .wheel {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            border: 5px solid var(--dark-color);
            position: relative;
            overflow: hidden;
            transition: transform 5s cubic-bezier(0.25, 0.1, 0.25, 1); /* Spin animation */
        }
        .wheel-segment {
            position: absolute;
            width: 50%;
            height: 50%;
            transform-origin: 100% 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 0.8rem;
            font-weight: bold;
            color: var(--text-color-on-dark); /* Default, can be overridden by segment color */
            writing-mode: vertical-rl;
            text-orientation: mixed;
            padding-left: 25px; /* Adjust for text position */
            clip-path: polygon(0 0, 100% 0, 100% 100%, 0 0); /* Creates triangle segment */
        }
        .wheel-segment span {
            transform: rotate(45deg) translateX(-10px) translateY(-10px); /* Position text better */
        }
        .wheel-pointer {
            position: absolute;
            left: -10px; /* Points from left */
            top: 50%;
            transform: translateY(-50%);
            font-size: 2.5rem;
            color: var(--primary-color);
            z-index: 10;
        }
        .wheel-center {
            position: absolute;
            width: 50px;
            height: 50px;
            background-color: var(--dark-color);
            border: 5px solid var(--surface-color);
            border-radius: 50%;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 5;
        }
        .info-text {
            font-size: 1.1rem;
            margin-bottom: 15px;
        }
        .info-text span {
            font-weight: bold;
            color: var(--primary-color);
        }
        .game-result-text {
            margin-top: 15px;
            font-size: 1.1rem;
            font-weight: 500;
            min-height: 1.5em; /* Prevent layout shift */
        }

        /* Slot Machine Page */
        .slots-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
        }
        .slots-reels {
            display: flex;
            justify-content: center;
            gap: 10px;
            margin-bottom: 20px;
            background-color: var(--dark-gray);
            padding: 15px;
            border-radius: 8px;
            border: 3px solid var(--dark-color);
            width: fit-content;
        }
        .reel {
            width: 70px;
            height: 210px; /* Shows 3 symbols, each 70px high */
            background-color: var(--surface-color);
            border: 2px solid var(--medium-gray);
            border-radius: 5px;
            overflow: hidden;
            position: relative;
        }
        .reel-symbols-strip {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            transition: top 0.3s ease-out; /* Slot spinning animation */
        }
        .reel-symbol {
            width: 100%;
            height: 70px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 2.5rem;
            border-bottom: 1px dashed var(--medium-gray);
        }
        .reel-symbol:last-child {
            border-bottom: none;
        }

        /* Scratch Card Page */
        .scratch-card-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
        }
        .scratch-card-area {
            width: 280px;
            height: 180px;
            background-color: var(--medium-gray);
            border-radius: 8px;
            position: relative;
            margin-bottom: 20px;
            border: 2px solid var(--dark-gray);
            overflow: hidden; /* Important for overlay */
            cursor: pointer;
        }
        .scratch-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: linear-gradient(45deg, #c0c0c0, #a9a9a9); /* Silver/gray gradient */
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.2rem;
            font-weight: 500;
            color: var(--dark-color);
            transition: opacity 0.5s ease-out;
            z-index: 1;
        }
        .scratch-overlay.scratched {
            opacity: 0;
            pointer-events: none; /* Allow clicking through if needed, though not for this simple version */
        }
        .scratch-result {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 2rem;
            font-weight: bold;
            color: var(--primary-color);
            background-color: var(--surface-color); /* Ensure it's visible */
        }

        /* Placeholder Style Pages (Daily Bonus, Refer, Profile) */
        .placeholder-page {
            text-align: center;
            padding: 40px 20px;
        }
        .placeholder-icon {
            font-size: 4rem;
            color: var(--primary-color);
            margin-bottom: 20px;
        }
        .placeholder-page h2 {
            margin-bottom: 15px;
        }
        .placeholder-page p {
            margin-bottom: 20px;
            color: var(--dark-gray);
            max-width: 500px;
            margin-left: auto;
            margin-right: auto;
        }
        .referral-code-box {
            background-color: var(--medium-gray);
            padding: 10px 15px;
            border-radius: 5px;
            display: inline-block;
            margin-bottom: 15px;
            border: 1px dashed var(--dark-gray);
        }
        #referralCodeDisplay {
            font-size: 1.2rem;
            font-weight: bold;
            color: var(--dark-color);
        }
        #copy-status {
            margin-top: 10px;
            font-size: 0.9rem;
        }

        /* Redeem Page */
        .form-card { /* Re-using a similar style to auth-page but more generic */
            max-width: 500px;
            margin: 20px auto;
            padding: 25px;
            background-color: var(--surface-color);
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        #redeem-content h2 small { font-size: 0.8rem; color: var(--dark-gray); }
        #redeem-points-balance { color: var(--primary-color); }
        .warning-text {
            color: var(--danger-color);
            font-weight: 500;
        }

        /* Common UI Elements */
        .btn {
            padding: 10px 15px;
            font-size: 1rem;
            border-radius: 5px;
            cursor: pointer;
            text-decoration: none;
            border: 1px solid transparent;
            transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 8px; /* Space between text and icon */
        }
        .btn-text { display: inline-block; }
        .btn-spinner { display: none; } /* Hidden by default */
        .btn.loading .btn-text { display: none; }
        .btn.loading .btn-spinner { display: inline-block; }

        .btn-primary {
            background-color: var(--primary-color);
            color: white;
        }
        .btn-primary:hover {
            background-color: var(--primary-color-dark);
        }
        .btn-primary:disabled {
            background-color: var(--medium-gray);
            color: var(--dark-gray);
            cursor: not-allowed;
        }

        .btn-secondary {
            background-color: var(--dark-gray);
            color: white;
        }
        .btn-secondary:hover {
            background-color: #5a6268;
        }

        .btn-success {
            background-color: var(--success-color);
            color: white;
        }
        .btn-success:hover { background-color: #1e7e34; }

        .btn-danger {
            background-color: var(--danger-color);
            color: white;
        }
        .btn-danger:hover { background-color: #b21f2d; }

        .btn-warning {
            background-color: var(--warning-color);
            color: var(--text-color-on-light); /* Dark text for yellow */
        }
        .btn-warning:hover { background-color: #d39e00; }


        .btn-outline-primary {
            background-color: transparent;
            color: var(--primary-color);
            border-color: var(--primary-color);
        }
        .btn-outline-primary:hover {
            background-color: var(--primary-color);
            color: white;
        }

        .btn-block {
            display: block;
            width: 100%;
        }
        .btn-lg {
            padding: 12px 25px;
            font-size: 1.1rem;
        }

        .form-group {
            margin-bottom: 15px;
        }
        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            font-size: 0.9rem;
        }
        .form-group input[type="text"],
        .form-group input[type="password"],
        .form-group input[type="email"],
        .form-group input[type="number"],
        .form-group select {
            width: 100%;
            padding: 10px;
            border: 1px solid var(--medium-gray);
            border-radius: 5px;
            font-size: 1rem;
            font-family: var(--font-family);
        }
        .form-group input:focus,
        .form-group select:focus {
            outline: none;
            border-color: var(--primary-color);
            box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
        }
        .form-group small {
            display: block;
            margin-top: 5px;
            font-size: 0.8rem;
            color: var(--dark-gray);
        }

        .error-message {
            background-color: #f8d7da;
            color: #721c24;
            padding: 10px;
            border: 1px solid #f5c6cb;
            border-radius: 5px;
            margin-bottom: 15px;
            font-size: 0.9rem;
            display: none; /* Hidden by default */
        }
        .success-message {
            background-color: #d4edda;
            color: #155724;
            padding: 10px;
            border: 1px solid #c3e6cb;
            border-radius: 5px;
            margin-bottom: 15px;
            font-size: 0.9rem;
            display: none; /* Hidden by default */
        }

        /* Desktop Layout (>= 992px) */
        @media (min-width: 992px) {
            .app-container {
                display: grid;
                grid-template-columns: var(--sidebar-width-desktop) 1fr;
                grid-template-rows: var(--header-height-mobile) 1fr;
                grid-template-areas:
                    "sidebar header"
                    "sidebar main";
                height: 100vh;
            }

            .app-header {
                grid-area: header;
                position: static; /* No longer fixed */
                justify-content: flex-end; /* Only user profile on right */
                background-color: var(--surface-color); /* White header on desktop */
                color: var(--text-color-on-light);
                border-bottom: 1px solid var(--medium-gray);
            }
            .menu-toggle-btn, .logo-mobile, .sidebar-close-btn {
                display: none;
            }
            .user-greeting {
                display: inline; /* Show greeting */
            }

            .sidebar {
                grid-area: sidebar;
                position: static; /* No longer fixed, part of grid */
                left: 0; /* Always visible */
                height: 100%;
                padding-top: 0; /* Remove top padding, header is separate */
                box-shadow: 2px 0 5px rgba(0,0,0,0.05);
            }
            .sidebar-header {
                height: var(--header-height-mobile); /* Match header height */
                border-bottom: 1px solid #495057;
                padding-left: 20px;
            }
            .sidebar-overlay {
                display: none !important; /* Never show overlay on desktop */
            }

            .main-content {
                grid-area: main;
                margin-top: 0;
                padding: 25px;
                overflow-y: auto; /* Allow scrolling within main content */
            }

            .stats-panel {
                grid-template-columns: repeat(4, 1fr);
            }
            .feature-grid {
                grid-template-columns: repeat(2, 1fr);
            }
        }

        /* Tablet specific adjustments (optional, but good practice) */
        @media (min-width: 768px) and (max-width: 991.98px) {
            .user-greeting {
                display: inline; /* Show greeting on tablets too */
            }
            .stats-panel {
                grid-template-columns: repeat(2, 1fr);
            }
            .feature-grid {
                grid-template-columns: repeat(2, 1fr);
            }
        }

        /* Large Desktop for Feature Grid (optional) */
        @media (min-width: 1200px) {
            .feature-grid {
                grid-template-columns: repeat(4, 1fr);
            }
        }
    </style>
</head>
<body>
    <div class="app-container">
        <!-- Header -->
        <header class="app-header">
            <button class="menu-toggle-btn" id="menuToggleBtn" aria-label="Toggle Menu">
                <i class="fas fa-bars"></i>
            </button>
            <div class="logo-mobile">SpinEarn</div>
            <div class="user-profile">
                <span class="user-greeting" id="userGreeting">Hi, Guest!</span>
                <i class="fas fa-user-circle user-profile-icon"></i>
            </div>
        </header>

        <!-- Sidebar -->
        <aside class="sidebar" id="sidebar">
            <div class="sidebar-header">
                <span class="sidebar-logo">SpinEarn</span>
                <!-- Close button for mobile, though overlay click also works -->
                <button class="sidebar-close-btn" id="sidebarCloseBtn" aria-label="Close Menu">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <nav class="sidebar-nav">
                <ul id="sidebarNavLinks">
                    <!-- Logged Out Links -->
                    <li data-page="login-content" class="nav-item nav-item-logged-out">
                        <a href="#login"><i class="fas fa-sign-in-alt"></i> Login</a>
                    </li>
                    <li data-page="register-content" class="nav-item nav-item-logged-out">
                        <a href="#register"><i class="fas fa-user-plus"></i> Register</a>
                    </li>
                    <!-- Logged In Links -->
                    <li data-page="dashboard-content" class="nav-item nav-item-logged-in">
                        <a href="#dashboard"><i class="fas fa-tachometer-alt"></i> Dashboard</a>
                    </li>
                    <li data-page="spin-wheel-content" class="nav-item nav-item-logged-in">
                        <a href="#spin-wheel"><i class="fas fa-dharmachakra"></i> Spin Wheel</a>
                    </li>
                    <li data-page="slot-machine-content" class="nav-item nav-item-logged-in">
                        <a href="#slot-machine"><i class="fas fa-dice-three"></i> Slot Machine</a>
                    </li>
                    <li data-page="scratch-cards-content" class="nav-item nav-item-logged-in">
                        <a href="#scratch-cards"><i class="fas fa-layer-group"></i> Scratch Cards</a>
                    </li>
                    <li data-page="daily-bonus-content" class="nav-item nav-item-logged-in">
                        <a href="#daily-bonus"><i class="fas fa-calendar-check"></i> Daily Bonus</a>
                    </li>
                    <li data-page="refer-earn-content" class="nav-item nav-item-logged-in">
                        <a href="#refer-earn"><i class="fas fa-user-plus"></i> Refer & Earn</a>
                    </li>
                    <li data-page="redeem-content" class="nav-item nav-item-logged-in">
                        <a href="#redeem"><i class="fas fa-gift"></i> Redeem Points</a>
                    </li>
                    <li data-page="history-content" class="nav-item nav-item-logged-in">
                        <a href="#history"><i class="fas fa-history"></i> History</a>
                    </li>
                    <li data-page="profile-content" class="nav-item nav-item-logged-in">
                        <a href="#profile"><i class="fas fa-user-cog"></i> Profile</a>
                    </li>
                    <li class="nav-item nav-item-logged-in" id="logoutNavItem">
                        <a href="#" id="logoutLink"><i class="fas fa-sign-out-alt"></i> Logout</a>
                    </li>
                </ul>
            </nav>
        </aside>
        <div class="sidebar-overlay" id="sidebarOverlay"></div>

        <!-- Main Content Area -->
        <main class="main-content">
            <!-- Login Page -->
            <div id="login-content" class="page-content">
                <div class="auth-page">
                    <h2>Login</h2>
                    <form id="loginForm">
                        <div class="form-group">
                            <label for="loginUsername">Username</label>
                            <input type="text" id="loginUsername" name="username" required>
                        </div>
                        <div class="form-group">
                            <label for="loginPassword">Password</label>
                            <input type="password" id="loginPassword" name="password" required>
                        </div>
                        <div id="login-error-message" class="error-message"></div>
                        <button type="submit" class="btn btn-primary btn-block">
                            <span class="btn-text">Login</span>
                            <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                        </button>
                    </form>
                    <p class="auth-switch">Don't have an account? <a href="#register" data-page="register-content">Register here</a></p>
                </div>
            </div>

            <!-- Register Page -->
            <div id="register-content" class="page-content" style="display: none;">
                <div class="auth-page">
                    <h2>Register</h2>
                    <form id="registerForm">
                        <div class="form-group">
                            <label for="registerUsername">Username</label>
                            <input type="text" id="registerUsername" name="username" required minlength="3" pattern="^[a-zA-Z0-9_]+$">
                            <small>Min 3 chars, letters, numbers, underscores only.</small>
                        </div>
                        <div class="form-group">
                            <label for="registerPassword">Password</label>
                            <input type="password" id="registerPassword" name="password" required minlength="6">
                            <small>Min 6 characters.</small>
                        </div>
                        <div class="form-group">
                            <label for="registerConfirmPassword">Confirm Password</label>
                            <input type="password" id="registerConfirmPassword" name="confirmPassword" required>
                        </div>
                        <div id="register-error-message" class="error-message"></div>
                        <div id="register-success-message" class="success-message"></div>
                        <button type="submit" class="btn btn-primary btn-block">
                            <span class="btn-text">Register</span>
                            <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                        </button>
                    </form>
                    <p class="auth-switch">Already have an account? <a href="#login" data-page="login-content">Login here</a></p>
                </div>
            </div>

            <!-- Dashboard Page -->
            <div id="dashboard-content" class="page-content" style="display: none;">
                <h2 id="dashboardWelcome">Welcome Back, <span id="dashboardUsername">User</span>!</h2>

                <section class="stats-panel">
                    <div class="stat-card">
                        <div class="stat-icon" style="background-color: #28a745;"><i class="fas fa-coins"></i></div>
                        <div class="stat-label">Balance</div>
                        <div class="stat-value" id="statBalance">--- pts</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-icon" style="background-color: #17a2b8;"><i class="fas fa-dharmachakra"></i></div>
                        <div class="stat-label">Spins Left</div>
                        <div class="stat-value" id="statSpinsLeft">---</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-icon" style="background-color: #ffc107;"><i class="fas fa-dice-three"></i></div>
                        <div class="stat-label">Slot Tokens</div>
                        <div class="stat-value" id="statSlotTokens">---</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-icon" style="background-color: #dc3545;"><i class="fas fa-layer-group"></i></div>
                        <div class="stat-label">Scratch Cards</div>
                        <div class="stat-value" id="statScratchCards">---</div>
                    </div>
                </section>

                <section class="spin-action-card">
                    <h3>Ready for More Fun?</h3>
                    <p>Try your luck on the Spin Wheel now!</p>
                    <button class="btn btn-success go-to-page-btn" data-page="spin-wheel-content">
                        <i class="fas fa-dharmachakra"></i> Go to Spin Wheel
                    </button>
                </section>

                <section class="feature-grid">
                    <div class="feature-card">
                        <div class="feature-icon" style="background-color: #6f42c1;"><i class="fas fa-calendar-check"></i></div>
                        <h4>Daily Bonus</h4>
                        <p>Claim your free daily rewards.</p>
                        <button class="btn btn-outline-primary go-to-page-btn" data-page="daily-bonus-content">Go to Daily Bonus</button>
                    </div>
                    <div class="feature-card">
                        <div class="feature-icon" style="background-color: #fd7e14;"><i class="fas fa-user-plus"></i></div>
                        <h4>Refer & Earn</h4>
                        <p>Invite friends and earn bonuses.</p>
                        <button class="btn btn-outline-primary go-to-page-btn" data-page="refer-earn-content">Go to Refer & Earn</button>
                    </div>
                     <div class="feature-card">
                        <div class="feature-icon" style="background-color: #20c997;"><i class="fas fa-dice-three"></i></div>
                        <h4>Slot Machine</h4>
                        <p>Test your luck with classic slots.</p>
                        <button class="btn btn-outline-primary go-to-page-btn" data-page="slot-machine-content">Play Slots</button>
                    </div>
                    <div class="feature-card">
                        <div class="feature-icon" style="background-color: #e83e8c;"><i class="fas fa-layer-group"></i></div>
                        <h4>Scratch Cards</h4>
                        <p>Scratch and win instant prizes.</p>
                        <button class="btn btn-outline-primary go-to-page-btn" data-page="scratch-cards-content">Scratch Now</button>
                    </div>
                </section>

                <section class="activity-log-section">
                    <h3>Recent Activity</h3>
                    <ul class="activity-list" id="dashboardActivityLog">
                        <!-- Activity items will be populated by JS -->
                    </ul>
                    <a href="#history" class="view-all-activity-link go-to-page-btn" data-page="history-content">View All Activity</a>
                </section>
            </div>

            <!-- Spin Wheel Page -->
            <div id="spin-wheel-content" class="page-content" style="display: none;">
                <h2>Spin the Wheel!</h2>
                <div class="spin-wheel-container">
                    <div class="wheel-area">
                        <div class="wheel" id="spinWheelElement">
                            <!-- Segments generated by JS -->
                        </div>
                        <div class="wheel-pointer"><i class="fas fa-caret-left"></i></div>
                        <div class="wheel-center"></div>
                    </div>
                    <p class="info-text">Spins Left: <span id="spinWheelSpinsLeft">--</span></p>
                    <button id="spinNowButton" class="btn btn-success btn-lg">
                        <span class="btn-text">Spin Now</span>
                        <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                    </button>
                    <p id="spin-result" class="game-result-text"></p>
                </div>
            </div>

            <!-- Slot Machine Page -->
            <div id="slot-machine-content" class="page-content" style="display: none;">
                <h2>Slot Machine</h2>
                <div class="slots-container">
                    <p class="info-text">Cost: 1 Token | Your Tokens: <span id="slotMachineTokens">--</span></p>
                    <div class="slots-reels">
                        <div class="reel" id="reel1"></div>
                        <div class="reel" id="reel2"></div>
                        <div class="reel" id="reel3"></div>
                    </div>
                    <button id="slotPlayButton" class="btn btn-danger btn-lg">
                        <span class="btn-text">Play (1 Token)</span>
                        <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                    </button>
                    <p id="slots-result" class="game-result-text"></p>
                </div>
            </div>

            <!-- Scratch Cards Page -->
            <div id="scratch-cards-content" class="page-content" style="display: none;">
                <h2>Scratch & Win</h2>
                <div class="scratch-card-container">
                    <p class="info-text">Cards Left Today: <span id="scratchCardsLeft">--</span></p>
                    <div class="scratch-card-area" id="scratchCardArea">
                        <div class="scratch-overlay" id="scratchOverlay">Click to Scratch!</div>
                        <div class="scratch-result" id="scratchCardResult"></div>
                    </div>
                    <button id="scratchCardButton" class="btn btn-warning btn-lg">
                         <span class="btn-text">Scratch Card</span>
                         <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                    </button>
                    <p id="scratch-status" class="game-result-text">Click the card above or the button!</p>
                </div>
            </div>

            <!-- Daily Bonus Page -->
            <div id="daily-bonus-content" class="page-content placeholder-page" style="display: none;">
                <i class="fas fa-calendar-check placeholder-icon"></i>
                <h2>Daily Bonus</h2>
                <p id="daily-bonus-message">Check your eligibility for today's bonus!</p>
                <button id="claimDailyBonusButton" class="btn btn-primary btn-lg">
                    <span class="btn-text">Claim Today's Bonus</span>
                    <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                </button>
                <p id="daily-bonus-status" class="game-result-text"></p>
            </div>

            <!-- Refer & Earn Page -->
            <div id="refer-earn-content" class="page-content placeholder-page" style="display: none;">
                <i class="fas fa-user-plus placeholder-icon"></i>
                <h2>Refer & Earn</h2>
                <p>Invite your friends to SpinEarn and get rewarded! Share your unique referral code:</p>
                <div class="referral-code-box">
                    <strong id="referralCodeDisplay">---------</strong>
                </div>
                <button id="copyReferralCodeButton" class="btn btn-secondary">
                    <i class="fas fa-copy"></i> Copy Code
                </button>
                <p id="copy-status" class="success-message" style="display: none;"></p>
                <small>Note: Referral tracking is a simulation in this demo.</small>
            </div>

            <!-- Redeem Points Page -->
            <div id="redeem-content" class="page-content" style="display: none;">
                 <h2>Redeem Points <small>(Withdrawal Simulation)</small></h2>
                 <p>Your current balance: <strong id="redeem-points-balance">--- pts</strong></p>
                 <form id="withdrawForm" class="form-card">
                    <div class="form-group">
                        <label for="withdrawAmount">Amount (Points)</label>
                        <input type="number" id="withdrawAmount" name="amount" required min="100">
                        <small>Minimum 100 points.</small>
                    </div>
                    <div class="form-group">
                        <label for="withdrawMethod">Withdrawal Method</label>
                        <select id="withdrawMethod" name="method" required>
                            <option value="">Select Method</option>
                            <option value="paypal">PayPal</option>
                            <option value="bank">Bank Transfer</option>
                            <option value="voucher">Gift Voucher</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="withdrawDetails">Details (Email/Account No.)</label>
                        <input type="text" id="withdrawDetails" name="details" required>
                        <small class="warning-text">Do NOT enter real financial information. This is a simulation.</small>
                    </div>
                    <div id="withdraw-error-message" class="error-message"></div>
                    <div id="withdraw-success-message" class="success-message"></div>
                    <button type="submit" class="btn btn-primary btn-block">
                        <span class="btn-text">Request Withdrawal</span>
                        <i class="fas fa-spinner fa-spin btn-spinner" style="display: none;"></i>
                    </button>
                 </form>
            </div>

            <!-- History Page -->
            <div id="history-content" class="page-content" style="display: none;">
                <h2>Activity History</h2>
                <p>Here is a log of all your recent activities on SpinEarn.</p>
                <ul class="activity-list" id="fullActivityList">
                    <!-- Full activity items will be populated by JS -->
                     <li class="empty-history" style="display:none;">No activity yet. Go play some games!</li>
                </ul>
            </div>

            <!-- Profile Page -->
            <div id="profile-content" class="page-content placeholder-page" style="display: none;">
                <i class="fas fa-user-edit placeholder-icon"></i>
                <h2>Profile</h2>
                <p>Username: <strong id="profileUsernameDisplay">---</strong></p>
                <p>Member Since: <strong id="profileMemberSinceDisplay">---</strong></p>
                <button class="btn btn-secondary" disabled>Change Password (Not Implemented)</button>
            </div>

        </main>
    </div>

    <script>
        // Inside the <script> tag in index.html

// const API_BASE_URL = 'api/'; // We will construct the full path in fetchApi

async function fetchApi(action, method = 'GET', body = null) {
    // Action will be 'login', 'register', 'get_user_data', etc.
    let url = `api/api.php?action=${encodeURIComponent(action)}`; // Base URL for the single API file

    const options = {
        method: method,
        headers: {
            'Accept': 'application/json' // Expect JSON response
        }
    };

    if (body && (method === 'POST' || method === 'PUT')) {
        options.body = JSON.stringify(body);
        options.headers['Content-Type'] = 'application/json'; // Send JSON body
    } else if (body && method === 'GET') {
        // For GET requests with body (less common, usually params in URL)
        // If you intend to send parameters for GET, they should be in the URL
        // This example keeps it simple, assuming body is for POST/PUT
    }


    try {
        const response = await fetch(url, options);
        if (!response.ok) {
            let errorData;
            try {
                errorData = await response.json();
            } catch (e) { /* Not a JSON error response */ }
            const errorMessage = errorData?.error || response.statusText || `HTTP error! status: ${response.status}`;

            if (response.status === 401 && action !== 'login' && action !== 'register') {
                console.warn('Unauthorized access, logging out (api.php).');
                await handleLogout(false);
                return { error: 'Session expired. Please login again.', status: 401 };
            }
            return { error: errorMessage, status: response.status };
        }
        return await response.json();
    } catch (error) {
        console.error(`API Fetch Error (${action}):`, error);
        return { error: 'Network error or server is unreachable.' };
    }
}

// Now, when you call fetchApi, you pass the action name:
// Example: const response = await fetchApi('login', 'POST', { username, password });
// Example: const data = await fetchApi('get_user_data');
        document.addEventListener('DOMContentLoaded', () => {
            // --- STATE ---
            let isLoggedIn = false;
            let currentUser = null; // { username, points, spins, tokens, scratchCards, joinDate, activityLog: [], dailyBonusClaimedDate }
            let users = loadUsers() || []; // Load from localStorage or initialize

            // --- UI ELEMENTS ---
            const menuToggleBtn = document.getElementById('menuToggleBtn');
            const sidebar = document.getElementById('sidebar');
            const sidebarOverlay = document.getElementById('sidebarOverlay');
            const sidebarCloseBtn = document.getElementById('sidebarCloseBtn');
            const sidebarNavLinksContainer = document.getElementById('sidebarNavLinks');
            const allPageContents = document.querySelectorAll('.page-content');
            const userGreeting = document.getElementById('userGreeting');

            // Auth Forms
            const loginForm = document.getElementById('loginForm');
            const registerForm = document.getElementById('registerForm');
            const loginErrorMessage = document.getElementById('login-error-message');
            const registerErrorMessage = document.getElementById('register-error-message');
            const registerSuccessMessage = document.getElementById('register-success-message');

            // Dashboard Elements
            const dashboardWelcome = document.getElementById('dashboardWelcome');
            const dashboardUsername = document.getElementById('dashboardUsername');
            const statBalance = document.getElementById('statBalance');
            const statSpinsLeft = document.getElementById('statSpinsLeft');
            const statSlotTokens = document.getElementById('statSlotTokens');
            const statScratchCards = document.getElementById('statScratchCards');
            const dashboardActivityLog = document.getElementById('dashboardActivityLog');

            // Spin Wheel Elements
            const spinWheelElement = document.getElementById('spinWheelElement');
            const spinWheelSpinsLeft = document.getElementById('spinWheelSpinsLeft');
            const spinNowButton = document.getElementById('spinNowButton');
            const spinResultText = document.getElementById('spin-result');
            const spinWheelSegments = [
                { label: "50 Pts", value: 50, color: "#FFC107" }, { label: "Try Again", value: 0, color: "#6c757d" },
                { label: "100 Pts", value: 100, color: "#28a745" }, { label: "1 Spin", value: "1_spin", color: "#17a2b8" },
                { label: "20 Pts", value: 20, color: "#fd7e14" }, { label: "Try Again", value: 0, color: "#6c757d" },
                { label: "250 Pts", value: 250, color: "#dc3545" }, { label: "5 Spins", value: "5_spins", color: "#6f42c1" }
            ];
            let isSpinningWheel = false;

            // Slot Machine Elements
            const slotMachineTokens = document.getElementById('slotMachineTokens');
            const reel1 = document.getElementById('reel1');
            const reel2 = document.getElementById('reel2');
            const reel3 = document.getElementById('reel3');
            const slotPlayButton = document.getElementById('slotPlayButton');
            const slotsResultText = document.getElementById('slots-result');
            const slotSymbols = ['🍒', '🍋', '🍊', '🍉', '⭐', '7', '💎'];
            const SYMBOLS_PER_REEL = 30; // For visual effect
            let isSpinningSlots = false;

            // Scratch Card Elements
            const scratchCardsLeft = document.getElementById('scratchCardsLeft');
            const scratchCardArea = document.getElementById('scratchCardArea');
            const scratchOverlay = document.getElementById('scratchOverlay');
            const scratchCardResult = document.getElementById('scratchCardResult');
            const scratchCardButton = document.getElementById('scratchCardButton');
            const scratchStatusText = document.getElementById('scratch-status');
            const scratchPrizes = [
                { label: "10 Pts", value: 10 }, { label: "No Win", value: 0 },
                { label: "25 Pts", value: 25 }, { label: "50 Pts", value: 50 },
                { label: "1 Card", value: "1_card" }, { label: "No Win", value: 0 }
            ];
            let isScratching = false;
            let currentScratchCardRevealed = false;

            // Daily Bonus Elements
            const dailyBonusMessage = document.getElementById('daily-bonus-message');
            const claimDailyBonusButton = document.getElementById('claimDailyBonusButton');
            const dailyBonusStatusText = document.getElementById('daily-bonus-status');
            const DAILY_BONUS_POINTS = 100;
            const DAILY_BONUS_SPINS = 5;

            // Refer & Earn Elements
            const referralCodeDisplay = document.getElementById('referralCodeDisplay');
            const copyReferralCodeButton = document.getElementById('copyReferralCodeButton');
            const copyStatusMessage = document.getElementById('copy-status');

            // Redeem Points Elements
            const redeemPointsBalance = document.getElementById('redeem-points-balance');
            const withdrawForm = document.getElementById('withdrawForm');
            const withdrawErrorMessage = document.getElementById('withdraw-error-message');
            const withdrawSuccessMessage = document.getElementById('withdraw-success-message');

            // History Page Elements
            const fullActivityList = document.getElementById('fullActivityList');
            const emptyHistoryMessage = fullActivityList.querySelector('.empty-history');


            // Profile Page Elements
            const profileUsernameDisplay = document.getElementById('profileUsernameDisplay');
            const profileMemberSinceDisplay = document.getElementById('profileMemberSinceDisplay');


            // --- HELPER FUNCTIONS ---
            function saveUsers() {
                localStorage.setItem('spinEarnUsers', JSON.stringify(users));
            }

            function loadUsers() {
                const storedUsers = localStorage.getItem('spinEarnUsers');
                return storedUsers ? JSON.parse(storedUsers) : [];
            }

            function showButtonLoading(button) {
                button.classList.add('loading');
                button.disabled = true;
                const spinner = button.querySelector('.btn-spinner');
                if (spinner) spinner.style.display = 'inline-block';
                const btnText = button.querySelector('.btn-text');
                if(btnText) btnText.style.display = 'none';
            }

            function hideButtonLoading(button) {
                button.classList.remove('loading');
                button.disabled = false; // Re-enable based on context later if needed
                const spinner = button.querySelector('.btn-spinner');
                if (spinner) spinner.style.display = 'none';
                const btnText = button.querySelector('.btn-text');
                if(btnText) btnText.style.display = 'inline-block';
            }

            function displayMessage(element, message, type = 'error') {
                element.textContent = message;
                element.className = type === 'error' ? 'error-message' : 'success-message';
                element.style.display = 'block';
                setTimeout(() => { element.style.display = 'none'; }, type === 'success' ? 3000 : 5000);
            }
            
            function clearMessage(element) {
                element.textContent = '';
                element.style.display = 'none';
            }

            function formatPoints(points) {
                return points.toLocaleString() + " pts";
            }

            function timeSince(date) {
                if (!date) return "Just now";
                const seconds = Math.floor((new Date() - new Date(date)) / 1000);
                let interval = seconds / 31536000;
                if (interval > 1) return Math.floor(interval) + " years ago";
                interval = seconds / 2592000;
                if (interval > 1) return Math.floor(interval) + " months ago";
                interval = seconds / 86400;
                if (interval > 1) return Math.floor(interval) + " days ago";
                interval = seconds / 3600;
                if (interval > 1) return Math.floor(interval) + " hours ago";
                interval = seconds / 60;
                if (interval > 1) return Math.floor(interval) + " min ago";
                return Math.floor(seconds) < 5 ? "Just now" : Math.floor(seconds) + " sec ago";
            }

            function addActivityLog(iconClass, description, pointsChange = 0, type = "general") {
                if (!currentUser) return;
                const activity = {
                    icon: iconClass,
                    description: description,
                    timestamp: new Date().toISOString(),
                    pointsChange: pointsChange,
                    type: type
                };
                currentUser.activityLog.unshift(activity); // Add to beginning
                if (currentUser.activityLog.length > 50) { // Keep log manageable
                    currentUser.activityLog.pop();
                }
                saveUsers(); // Save after updating user data
                updateDashboardActivityLog(); // Update dashboard immediately
                if(document.getElementById('history-content').style.display !== 'none') {
                    renderFullActivityLog(); // If history page is active, update it
                }
            }

            // --- NAVIGATION ---
            function showPage(pageId) {
                allPageContents.forEach(page => {
                    page.style.display = page.id === pageId ? 'block' : 'none';
                });
                updateActiveNavLink(pageId);
                // Scroll to top of page
                document.querySelector('.main-content').scrollTop = 0;

                // Specific page load functions
                if (isLoggedIn) {
                    if (pageId === 'dashboard-content') loadDashboardData();
                    else if (pageId === 'spin-wheel-content') initSpinWheelPage();
                    else if (pageId === 'slot-machine-content') initSlotMachinePage();
                    else if (pageId === 'scratch-cards-content') initScratchCardPage();
                    else if (pageId === 'daily-bonus-content') initDailyBonusPage();
                    else if (pageId === 'refer-earn-content') initReferEarnPage();
                    else if (pageId === 'redeem-content') initRedeemPage();
                    else if (pageId === 'history-content') renderFullActivityLog();
                    else if (pageId === 'profile-content') initProfilePage();
                }
                closeSidebar(); // Close sidebar after navigation on mobile
            }

            function updateActiveNavLink(pageId) {
                sidebarNavLinksContainer.querySelectorAll('.nav-item').forEach(item => {
                    item.classList.remove('active');
                    if (item.dataset.page === pageId) {
                        item.classList.add('active');
                    }
                });
            }

            function handleNavigation(event) {
                let target = event.target;
                // Allow clicks on <i> inside <a>
                if (target.tagName === 'I' && target.parentElement.tagName === 'A') {
                    target = target.parentElement;
                }
                if (target.tagName === 'A' && target.closest('.nav-item')) {
                    event.preventDefault();
                    const pageId = target.closest('.nav-item').dataset.page;
                    if (pageId) {
                        window.location.hash = target.getAttribute('href'); // Update hash for deep linking
                    }
                } else if (target.classList.contains('go-to-page-btn') || target.closest('.go-to-page-btn')) {
                    event.preventDefault(); // Prevent default if it's an <a>
                    const button = target.closest('.go-to-page-btn');
                    const pageId = button.dataset.page;
                    if (pageId) {
                        window.location.hash = `#${pageId.replace('-content', '')}`;
                    }
                } else if (target.tagName === 'A' && target.dataset.page) { // For auth switch links
                    event.preventDefault();
                    window.location.hash = target.getAttribute('href');
                }
            }

            function handleHashChange() {
                const hash = window.location.hash.substring(1); // Remove #
                let targetPageId = '';

                if (hash) {
                    targetPageId = `${hash}-content`;
                    if (!document.getElementById(targetPageId)) { 
                        targetPageId = isLoggedIn ? 'dashboard-content' : 'login-content';
                    }
                } else {
                    targetPageId = isLoggedIn ? 'dashboard-content' : 'login-content';
                }
                
                if (!isLoggedIn && !['login-content', 'register-content'].includes(targetPageId)) {
                    targetPageId = 'login-content';
                    if (window.location.hash !== '#login') window.location.hash = '#login'; 
                }
                if (isLoggedIn && ['login-content', 'register-content'].includes(targetPageId)) {
                    targetPageId = 'dashboard-content';
                     if (window.location.hash !== '#dashboard') window.location.hash = '#dashboard';
                }
                
                showPage(targetPageId);
            }

            // Sidebar Toggle
            function toggleSidebar() {
                sidebar.classList.toggle('open');
                sidebarOverlay.classList.toggle('active');
            }
            function closeSidebar() {
                sidebar.classList.remove('open');
                sidebarOverlay.classList.remove('active');
            }

            // --- AUTHENTICATION ---
            function updateUIForLoginState() {
                const loggedInItems = sidebarNavLinksContainer.querySelectorAll('.nav-item-logged-in');
                const loggedOutItems = sidebarNavLinksContainer.querySelectorAll('.nav-item-logged-out');

                if (isLoggedIn && currentUser) {
                    loggedInItems.forEach(item => item.style.display = 'block');
                    loggedOutItems.forEach(item => item.style.display = 'none');
                    userGreeting.textContent = `Hi, ${currentUser.username}!`;
                } else {
                    loggedInItems.forEach(item => item.style.display = 'none');
                    loggedOutItems.forEach(item => item.style.display = 'block');
                    userGreeting.textContent = 'Hi, Guest!';
                }
                handleHashChange(); 
            }

            loginForm.addEventListener('submit', (e) => {
                e.preventDefault();
                const submitBtn = loginForm.querySelector('button[type="submit"]');
                showButtonLoading(submitBtn);
                clearMessage(loginErrorMessage);

                const username = loginForm.loginUsername.value.trim();
                const password = loginForm.loginPassword.value;

                setTimeout(() => { 
                    const foundUser = users.find(user => user.username === username && user.password === password); 
                    if (foundUser) {
                        isLoggedIn = true;
                        currentUser = foundUser;
                        localStorage.setItem('spinEarnCurrentUser', username); 
                        updateUIForLoginState();
                         // No explicit hash change here, updateUIForLoginState calls handleHashChange
                    } else {
                        displayMessage(loginErrorMessage, 'Invalid username or password.');
                    }
                    hideButtonLoading(submitBtn);
                }, 1000);
            });

            registerForm.addEventListener('submit', (e) => {
                e.preventDefault();
                const submitBtn = registerForm.querySelector('button[type="submit"]');
                showButtonLoading(submitBtn);
                clearMessage(registerErrorMessage);
                clearMessage(registerSuccessMessage);

                const username = registerForm.registerUsername.value.trim();
                const password = registerForm.registerPassword.value;
                const confirmPassword = registerForm.registerConfirmPassword.value;

                if (password !== confirmPassword) {
                    displayMessage(registerErrorMessage, 'Passwords do not match.');
                    hideButtonLoading(submitBtn);
                    return;
                }
                if (!username.match(/^[a-zA-Z0-9_]+$/) || username.length < 3) {
                     displayMessage(registerErrorMessage, 'Invalid username: Min 3 chars, letters, numbers, underscores only.');
                     hideButtonLoading(submitBtn);
                     return;
                }
                 if (password.length < 6) {
                     displayMessage(registerErrorMessage, 'Password must be at least 6 characters.');
                     hideButtonLoading(submitBtn);
                     return;
                }

                setTimeout(() => { 
                    if (users.find(user => user.username === username)) {
                        displayMessage(registerErrorMessage, 'Username already exists.');
                    } else {
                        const newUser = {
                            username: username,
                            password: password, 
                            points: 1000, 
                            spins: 10,    
                            tokens: 5,    
                            scratchCards: 3, 
                            joinDate: new Date().toISOString(),
                            activityLog: [{ icon: 'fas fa-gift', description: 'Welcome bonus! +1000 points, 10 spins, 5 tokens, 3 scratch cards.', timestamp: new Date().toISOString(), type: 'bonus' }],
                            dailyBonusClaimedDate: null, 
                            referralCode: `SPIN${username.toUpperCase().slice(0,5)}${Math.floor(100 + Math.random() * 900)}`
                        };
                        users.push(newUser);
                        saveUsers();
                        displayMessage(registerSuccessMessage, 'Registration successful! Please login.');
                        registerForm.reset();
                    }
                    hideButtonLoading(submitBtn);
                }, 1000);
            });

            document.getElementById('logoutLink').addEventListener('click', (e) => {
                e.preventDefault();
                isLoggedIn = false;
                currentUser = null;
                localStorage.removeItem('spinEarnCurrentUser');
                updateUIForLoginState(); 
                // No explicit hash change here, updateUIForLoginState calls handleHashChange which will go to #login
            });

            // --- DASHBOARD ---
            function loadDashboardData() {
                if (!currentUser) return;
                if(dashboardUsername) dashboardUsername.textContent = currentUser.username;
                if(statBalance) statBalance.textContent = formatPoints(currentUser.points);
                if(statSpinsLeft) statSpinsLeft.textContent = currentUser.spins;
                if(statSlotTokens) statSlotTokens.textContent = currentUser.tokens;
                if(statScratchCards) statScratchCards.textContent = currentUser.scratchCards;
                updateDashboardActivityLog();
            }

            function updateDashboardActivityLog() {
                if (!currentUser || !dashboardActivityLog) return;
                dashboardActivityLog.innerHTML = ''; 
                const recentActivities = currentUser.activityLog.slice(0, 5);
                if (recentActivities.length === 0) {
                    dashboardActivityLog.innerHTML = '<li>No recent activity.</li>';
                    return;
                }
                recentActivities.forEach(activity => {
                    const li = document.createElement('li');
                    const iconColor = getActivityIconColor(activity.type);
                    li.innerHTML = `
                        <div class="activity-item-icon" style="background-color: ${iconColor};"><i class="${activity.icon}"></i></div>
                        <span class="activity-description">${activity.description}</span>
                        <span class="activity-timestamp">${timeSince(activity.timestamp)}</span>
                    `;
                    dashboardActivityLog.appendChild(li);
                });
            }
            
            function getActivityIconColor(type) {
                switch(type) {
                    case 'spin_win': return 'var(--success-color)';
                    case 'slot_win': return 'var(--success-color)';
                    case 'scratch_win': return 'var(--success-color)';
                    case 'bonus_claim':
                    case 'bonus': return 'var(--info-color)';
                    case 'referral': return 'var(--warning-color)';
                    case 'redeem': return 'var(--primary-color)';
                    default: return 'var(--dark-gray)';
                }
            }


            // --- SPIN WHEEL ---
            function initSpinWheelPage() {
                if (!currentUser || !spinWheelElement) return;
                renderSpinWheelSegments();
                spinWheelSpinsLeft.textContent = currentUser.spins;
                spinNowButton.disabled = currentUser.spins <= 0 || isSpinningWheel;
                hideButtonLoading(spinNowButton); // Ensure it's reset
                spinResultText.textContent = '';
            }

            function renderSpinWheelSegments() {
                spinWheelElement.innerHTML = '';
                const angleStep = 360 / spinWheelSegments.length;
                spinWheelSegments.forEach((segment, index) => {
                    const segmentDiv = document.createElement('div');
                    segmentDiv.classList.add('wheel-segment');
                    segmentDiv.style.backgroundColor = segment.color;
                    segmentDiv.style.transform = `rotate(${index * angleStep}deg)`;
                    if (segment.color === "#FFC107" || segment.color === "#fd7e14" || segment.color === "#17a2b8") { 
                         segmentDiv.style.color = 'var(--text-color-on-light)';
                    }
                    segmentDiv.innerHTML = `<span>${segment.label}</span>`;
                    spinWheelElement.appendChild(segmentDiv);
                });
            }
            
            spinNowButton.addEventListener('click', () => {
                if (currentUser.spins <= 0 || isSpinningWheel) return;

                isSpinningWheel = true;
                currentUser.spins--;
                spinWheelSpinsLeft.textContent = currentUser.spins;
                showButtonLoading(spinNowButton);
                spinResultText.textContent = 'Spinning...';
                spinResultText.style.color = 'var(--dark-gray)';

                const totalRotations = 3 + Math.floor(Math.random() * 3); 
                const randomIndex = Math.floor(Math.random() * spinWheelSegments.length);
                const winningSegment = spinWheelSegments[randomIndex];
                const anglePerSegment = 360 / spinWheelSegments.length;
                const finalRotation = (totalRotations * 360) - (randomIndex * anglePerSegment) - (anglePerSegment / 2) + (360/spinWheelSegments.length * 2.25); 

                spinWheelElement.style.transition = 'transform 5s cubic-bezier(0.25, 0.1, 0.25, 1)';
                spinWheelElement.style.transform = `rotate(${finalRotation}deg)`;

                setTimeout(() => {
                    isSpinningWheel = false;
                    hideButtonLoading(spinNowButton);
                    spinNowButton.disabled = currentUser.spins <= 0;

                    let outcomeText = '';
                    let pointsWon = 0;
                    let activityIcon = 'fas fa-dharmachakra';
                    let activityType = 'spin_general';

                    if (winningSegment.value === 0) {
                        outcomeText = "So close! Try Again!";
                        spinResultText.style.color = 'var(--danger-color)';
                    } else if (typeof winningSegment.value === 'string' && winningSegment.value.includes('_spin')) {
                        const spinsWon = parseInt(winningSegment.value.split('_')[0]);
                        currentUser.spins += spinsWon;
                        outcomeText = `You won ${spinsWon} extra spin${spinsWon > 1 ? 's' : ''}!`;
                        spinResultText.style.color = 'var(--info-color)';
                        activityIcon = 'fas fa-redo';
                        activityType = 'spin_bonus';
                    } else {
                        pointsWon = winningSegment.value;
                        currentUser.points += pointsWon;
                        outcomeText = `Congratulations! You won ${formatPoints(pointsWon)}!`;
                        spinResultText.style.color = 'var(--success-color)';
                        activityType = 'spin_win';
                    }
                    spinResultText.textContent = outcomeText;
                    addActivityLog(activityIcon, outcomeText, pointsWon, activityType);
                    
                    if (statBalance) statBalance.textContent = formatPoints(currentUser.points);
                    if (statSpinsLeft) statSpinsLeft.textContent = currentUser.spins;
                    spinWheelSpinsLeft.textContent = currentUser.spins; 
                    saveUsers();
                }, 5200); 
            });

            // --- SLOT MACHINE ---
            function initSlotMachinePage() {
                if (!currentUser || !reel1) return;
                slotMachineTokens.textContent = currentUser.tokens;
                slotPlayButton.disabled = currentUser.tokens <= 0 || isSpinningSlots;
                hideButtonLoading(slotPlayButton);
                slotsResultText.textContent = '';
                [reel1, reel2, reel3].forEach(r => createReelSymbols(r));
            }

            function createReelSymbols(reelElement) {
                reelElement.innerHTML = ''; 
                const strip = document.createElement('div');
                strip.classList.add('reel-symbols-strip');
                for (let i = 0; i < SYMBOLS_PER_REEL; i++) {
                    const symbolDiv = document.createElement('div');
                    symbolDiv.classList.add('reel-symbol');
                    symbolDiv.textContent = slotSymbols[Math.floor(Math.random() * slotSymbols.length)];
                    strip.appendChild(symbolDiv);
                }
                reelElement.appendChild(strip);
                const symbolHeight = 70;
                strip.style.top = `-${Math.floor(Math.random() * (SYMBOLS_PER_REEL - 3)) * symbolHeight}px`;
            }

            function spinReel(reelElement, finalSymbolIndex) {
                const strip = reelElement.querySelector('.reel-symbols-strip');
                const symbolHeight = 70; 
                let targetTop = -((finalSymbolIndex -1 + SYMBOLS_PER_REEL) % SYMBOLS_PER_REEL) * symbolHeight;
                targetTop -= (3 * SYMBOLS_PER_REEL * symbolHeight); 

                return new Promise(resolve => {
                    strip.style.transition = 'top 1s cubic-bezier(0.25, 0.1, 0.25, 1)'; 
                    strip.style.top = `${targetTop}px`;
                    setTimeout(() => {
                        strip.style.transition = 'none';
                        const finalPosition = -((finalSymbolIndex -1 + SYMBOLS_PER_REEL) % SYMBOLS_PER_REEL) * symbolHeight;
                        strip.style.top = `${finalPosition}px`;
                        resolve();
                    }, 1000); 
                });
            }
            
            function getReelSymbolAtIndex(reelElement, index) {
                const strip = reelElement.querySelector('.reel-symbols-strip');
                const currentTopOffset = parseInt(strip.style.top || '0px');
                const topSymbolVisualIndex = Math.abs(Math.round(currentTopOffset / 70));
                return strip.children[(topSymbolVisualIndex + index + SYMBOLS_PER_REEL) % SYMBOLS_PER_REEL].textContent;
            }


            slotPlayButton.addEventListener('click', async () => {
                if (currentUser.tokens <= 0 || isSpinningSlots) return;

                isSpinningSlots = true;
                currentUser.tokens--;
                slotMachineTokens.textContent = currentUser.tokens;
                showButtonLoading(slotPlayButton);
                slotsResultText.textContent = 'Spinning reels...';
                slotsResultText.style.color = 'var(--dark-gray)';

                const results = [
                    Math.floor(Math.random() * slotSymbols.length),
                    Math.floor(Math.random() * slotSymbols.length),
                    Math.floor(Math.random() * slotSymbols.length)
                ];
                
                const finalSymbolIndices = [
                    Math.floor(Math.random() * (SYMBOLS_PER_REEL - 6)) + 3, 
                    Math.floor(Math.random() * (SYMBOLS_PER_REEL - 6)) + 3,
                    Math.floor(Math.random() * (SYMBOLS_PER_REEL - 6)) + 3,
                ];
                
                [reel1, reel2, reel3].forEach((r, i) => {
                    // Ensure the symbol at the *target visible middle position* is the one we want.
                    // The strip's children are indexed 0 to SYMBOLS_PER_REEL-1.
                    // finalSymbolIndices[i] will be the index of the symbol that appears in the *top* visible slot of the reel.
                    // So, the symbol at finalSymbolIndices[i] + 1 will be in the middle.
                    r.querySelector('.reel-symbols-strip').children[(finalSymbolIndices[i] + 1 + SYMBOLS_PER_REEL) % SYMBOLS_PER_REEL].textContent = slotSymbols[results[i]];
                });

                await spinReel(reel1, finalSymbolIndices[0]);
                await new Promise(r => setTimeout(r, 150)); 
                await spinReel(reel2, finalSymbolIndices[1]);
                await new Promise(r => setTimeout(r, 150));
                await spinReel(reel3, finalSymbolIndices[2]);

                const s1 = getReelSymbolAtIndex(reel1, 1);
                const s2 = getReelSymbolAtIndex(reel2, 1);
                const s3 = getReelSymbolAtIndex(reel3, 1);
                
                let pointsWon = 0;
                let outcomeText = '';
                let activityIcon = 'fas fa-dice-three';
                let activityType = 'slot_general';

                if (s1 === s2 && s2 === s3) { 
                    if (s1 === '7') pointsWon = 500;
                    else if (s1 === '💎') pointsWon = 250;
                    else if (s1 === '⭐') pointsWon = 100;
                    else pointsWon = 50;
                    outcomeText = `JACKPOT! ${s1}${s2}${s3} - You won ${formatPoints(pointsWon)}!`;
                    slotsResultText.style.color = 'var(--success-color)';
                    activityType = 'slot_win';
                } else if (s1 === s2 || s2 === s3 || s1 === s3) { 
                    if ((s1 === s2 && s1 === '7') || (s2 === s3 && s2 === '7') || (s1 === s3 && s1 === '7')) pointsWon = 50;
                    else if ((s1 === s2 && s1 === '💎') || (s2 === s3 && s2 === '💎') || (s1 === s3 && s1 === '💎')) pointsWon = 25;
                    else pointsWon = 10;
                    outcomeText = `Match! ${s1}${s2}${s3} - You won ${formatPoints(pointsWon)}!`;
                    slotsResultText.style.color = 'var(--primary-color)';
                    activityType = 'slot_win';
                } else {
                    outcomeText = `No Win. ${s1}${s2}${s3} - Better luck next time!`;
                    slotsResultText.style.color = 'var(--danger-color)';
                }

                if (pointsWon > 0) {
                    currentUser.points += pointsWon;
                }
                slotsResultText.textContent = outcomeText;
                addActivityLog(activityIcon, outcomeText, pointsWon, activityType);

                isSpinningSlots = false;
                hideButtonLoading(slotPlayButton);
                slotPlayButton.disabled = currentUser.tokens <= 0;

                if(statBalance) statBalance.textContent = formatPoints(currentUser.points);
                if(statSlotTokens) statSlotTokens.textContent = currentUser.tokens;
                slotMachineTokens.textContent = currentUser.tokens;
                saveUsers();
            });


            // --- SCRATCH CARDS ---
            function initScratchCardPage() {
                if (!currentUser || !scratchCardArea) return;
                scratchCardsLeft.textContent = currentUser.scratchCards;
                resetScratchCard(); // This will also handle button state
                scratchStatusText.textContent = currentUser.scratchCards > 0 ? 'Click the card above or the button!' : 'No scratch cards left today.';
            }

            function resetScratchCard() {
                scratchOverlay.classList.remove('scratched');
                scratchOverlay.style.opacity = 1;
                scratchCardResult.textContent = ''; 
                currentScratchCardRevealed = false;
                scratchStatusText.textContent = 'Click the card or button to scratch!';
                scratchStatusText.style.color = 'var(--dark-gray)';
                
                hideButtonLoading(scratchCardButton);
                scratchCardButton.disabled = currentUser.scratchCards <= 0 || isScratching;
                const btnText = scratchCardButton.querySelector('.btn-text');
                if(btnText) btnText.textContent = "Scratch Card";
            }

            function revealScratchCard(prize) {
                scratchCardResult.textContent = prize.label;
                scratchOverlay.classList.add('scratched'); 
                currentScratchCardRevealed = true;
                
                let outcomeText = '';
                let pointsWon = 0;
                let activityIcon = 'fas fa-layer-group';
                let activityType = 'scratch_general';

                if (prize.value === 0) {
                    outcomeText = `Sorry, no win this time.`;
                    scratchStatusText.style.color = 'var(--danger-color)';
                } else if (typeof prize.value === 'string' && prize.value.includes('_card')) {
                    const cardsWon = parseInt(prize.value.split('_')[0]);
                    currentUser.scratchCards += cardsWon;
                    outcomeText = `You won ${cardsWon} extra scratch card${cardsWon > 1 ? 's' : ''}!`;
                    scratchStatusText.style.color = 'var(--info-color)';
                    activityIcon = 'fas fa-redo';
                    activityType = 'scratch_bonus';
                } else {
                    pointsWon = prize.value;
                    currentUser.points += pointsWon;
                    outcomeText = `Congratulations! You won ${formatPoints(pointsWon)}!`;
                    scratchStatusText.style.color = 'var(--success-color)';
                    activityType = 'scratch_win';
                }
                scratchStatusText.textContent = outcomeText;
                addActivityLog(activityIcon, outcomeText, pointsWon, activityType);

                if(statBalance) statBalance.textContent = formatPoints(currentUser.points);
                if(statScratchCards) statScratchCards.textContent = currentUser.scratchCards;
                scratchCardsLeft.textContent = currentUser.scratchCards; 
                saveUsers();

                const btnText = scratchCardButton.querySelector('.btn-text');
                if(btnText) btnText.textContent = "Get New Card";
                // Button disabled state will be handled by next click or initScratchCardPage
                scratchCardButton.disabled = isScratching; // Keep disabled if in process, else enable
            }

            function handleScratchAction() {
                if (isScratching) return;

                if (currentScratchCardRevealed) { 
                    if (currentUser.scratchCards > 0) {
                        resetScratchCard();
                    } else {
                        scratchStatusText.textContent = 'No scratch cards left today.';
                        scratchCardButton.disabled = true;
                    }
                    return;
                }
                
                if (currentUser.scratchCards <= 0) {
                     scratchStatusText.textContent = 'No scratch cards left today.';
                     scratchCardButton.disabled = true;
                     return;
                }

                isScratching = true;
                currentUser.scratchCards--; // Decrement before showing, to reflect usage
                scratchCardsLeft.textContent = currentUser.scratchCards;
                showButtonLoading(scratchCardButton);
                scratchStatusText.textContent = 'Scratching...';
                scratchStatusText.style.color = 'var(--dark-gray)';

                setTimeout(() => {
                    const prize = scratchPrizes[Math.floor(Math.random() * scratchPrizes.length)];
                    revealScratchCard(prize);
                    isScratching = false;
                    hideButtonLoading(scratchCardButton);
                    // After revealing, if no cards left, disable "Get New Card"
                    if (currentUser.scratchCards <= 0) {
                        scratchCardButton.disabled = true;
                         const btnText = scratchCardButton.querySelector('.btn-text');
                         if(btnText) btnText.textContent = "No Cards Left";
                    } else {
                         scratchCardButton.disabled = false; // Enable "Get New Card"
                    }
                }, 1000); 
            }

            if (scratchCardArea) scratchCardArea.addEventListener('click', () => {
                if (!currentScratchCardRevealed && currentUser.scratchCards > 0 && !isScratching) {
                    handleScratchAction();
                } else if (currentScratchCardRevealed && currentUser.scratchCards > 0 && !isScratching) {
                    handleScratchAction(); // This will trigger resetScratchCard
                }
            });
           if (scratchCardButton) scratchCardButton.addEventListener('click', handleScratchAction);


            // --- DAILY BONUS ---
            function initDailyBonusPage() {
                if (!currentUser || !dailyBonusMessage) return;
                const todayStr = new Date().toISOString().split('T')[0]; 
                
                dailyBonusStatusText.textContent = '';
                hideButtonLoading(claimDailyBonusButton);
                if (currentUser.dailyBonusClaimedDate === todayStr) {
                    dailyBonusMessage.textContent = `You have already claimed your bonus for today. Come back tomorrow!`;
                    claimDailyBonusButton.disabled = true;
                    const btnText = claimDailyBonusButton.querySelector('.btn-text');
                    if(btnText) btnText.textContent = "Claimed Today";
                } else {
                    dailyBonusMessage.textContent = `You are eligible for today's bonus of ${DAILY_BONUS_POINTS} points and ${DAILY_BONUS_SPINS} spins!`;
                    claimDailyBonusButton.disabled = false;
                    const btnText = claimDailyBonusButton.querySelector('.btn-text');
                    if(btnText) btnText.textContent = "Claim Today's Bonus";
                }
            }
            
            if(claimDailyBonusButton) claimDailyBonusButton.addEventListener('click', () => {
                if (!currentUser) return;
                const todayStr = new Date().toISOString().split('T')[0];
                if (currentUser.dailyBonusClaimedDate === todayStr) {
                    dailyBonusStatusText.textContent = 'Bonus already claimed today.';
                    dailyBonusStatusText.style.color = 'var(--info-color)';
                    return;
                }

                showButtonLoading(claimDailyBonusButton);
                dailyBonusStatusText.textContent = 'Claiming...';
                dailyBonusStatusText.style.color = 'var(--dark-gray)';

                setTimeout(() => {
                    currentUser.points += DAILY_BONUS_POINTS;
                    currentUser.spins += DAILY_BONUS_SPINS;
                    currentUser.dailyBonusClaimedDate = todayStr;

                    const message = `Bonus Claimed! +${DAILY_BONUS_POINTS} points and +${DAILY_BONUS_SPINS} spins.`;
                    dailyBonusStatusText.textContent = message;
                    dailyBonusStatusText.style.color = 'var(--success-color)';
                    
                    addActivityLog('fas fa-gift', message, DAILY_BONUS_POINTS, 'bonus_claim');
                    
                    hideButtonLoading(claimDailyBonusButton);
                    initDailyBonusPage(); 

                    if(statBalance) statBalance.textContent = formatPoints(currentUser.points);
                    if(statSpinsLeft) statSpinsLeft.textContent = currentUser.spins;
                    saveUsers();
                }, 1000);
            });

            // --- REFER & EARN ---
            function initReferEarnPage() {
                if (!currentUser || !referralCodeDisplay) return;
                referralCodeDisplay.textContent = currentUser.referralCode || 'N/A';
                copyStatusMessage.style.display = 'none';
            }

            if(copyReferralCodeButton) copyReferralCodeButton.addEventListener('click', () => {
                if (!currentUser || !currentUser.referralCode) return;
                navigator.clipboard.writeText(currentUser.referralCode)
                    .then(() => {
                        copyStatusMessage.textContent = 'Referral code copied to clipboard!';
                        copyStatusMessage.className = 'success-message'; // Ensure correct class
                        copyStatusMessage.style.display = 'block';
                        setTimeout(() => { copyStatusMessage.style.display = 'none'; }, 2000);
                    })
                    .catch(err => {
                        copyStatusMessage.textContent = 'Failed to copy code. Try manually.';
                        copyStatusMessage.className = 'error-message'; // Ensure correct class
                        copyStatusMessage.style.display = 'block';
                        console.error('Failed to copy text: ', err);
                    });
            });


            // --- REDEEM POINTS ---
            function initRedeemPage() {
                if (!currentUser || !withdrawForm) return;
                redeemPointsBalance.textContent = formatPoints(currentUser.points);
                withdrawForm.reset();
                clearMessage(withdrawErrorMessage);
                clearMessage(withdrawSuccessMessage);
                const submitBtn = withdrawForm.querySelector('button[type="submit"]');
                hideButtonLoading(submitBtn);
                submitBtn.disabled = false;
            }

            if(withdrawForm) withdrawForm.addEventListener('submit', (e) => {
                e.preventDefault();
                if (!currentUser) return;

                const amount = parseInt(withdrawForm.withdrawAmount.value);
                const method = withdrawForm.withdrawMethod.value;
                const details = withdrawForm.withdrawDetails.value.trim();
                const submitBtn = withdrawForm.querySelector('button[type="submit"]');

                clearMessage(withdrawErrorMessage);
                clearMessage(withdrawSuccessMessage);

                if (isNaN(amount) || amount < 100) {
                    displayMessage(withdrawErrorMessage, 'Minimum withdrawal amount is 100 points.');
                    return;
                }
                if (amount > currentUser.points) {
                    displayMessage(withdrawErrorMessage, 'Insufficient points balance.');
                    return;
                }
                if (!method) {
                    displayMessage(withdrawErrorMessage, 'Please select a withdrawal method.');
                    return;
                }
                if (!details) {
                    displayMessage(withdrawErrorMessage, 'Please provide withdrawal details.');
                    return;
                }

                showButtonLoading(submitBtn);
                setTimeout(() => { 
                    currentUser.points -= amount;
                    addActivityLog('fas fa-paper-plane', `Withdrawal request for ${formatPoints(amount)} (${method}).`, -amount, 'redeem');
                    
                    displayMessage(withdrawSuccessMessage, `Withdrawal request for ${formatPoints(amount)} submitted successfully! (Simulation)`);
                    
                    hideButtonLoading(submitBtn);
                    initRedeemPage(); 
                    if(statBalance) statBalance.textContent = formatPoints(currentUser.points); 
                    saveUsers();
                }, 1500);
            });


            // --- HISTORY PAGE ---
            function renderFullActivityLog() {
                if (!currentUser || !fullActivityList) return;
                fullActivityList.innerHTML = ''; 
                if (currentUser.activityLog.length === 0) {
                    if(emptyHistoryMessage) emptyHistoryMessage.style.display = 'list-item';
                    return;
                }
                if(emptyHistoryMessage) emptyHistoryMessage.style.display = 'none';

                currentUser.activityLog.forEach(activity => {
                    const li = document.createElement('li');
                    const iconColor = getActivityIconColor(activity.type);
                    li.innerHTML = `
                        <div class="activity-item-icon" style="background-color: ${iconColor};"><i class="${activity.icon}"></i></div>
                        <span class="activity-description">${activity.description}</span>
                        <span class="activity-timestamp">${timeSince(activity.timestamp)}</span>
                    `;
                    fullActivityList.appendChild(li);
                });
            }

            // --- PROFILE PAGE ---
            function initProfilePage() {
                if (!currentUser || !profileUsernameDisplay) return;
                profileUsernameDisplay.textContent = currentUser.username;
                profileMemberSinceDisplay.textContent = new Date(currentUser.joinDate).toLocaleDateString();
            }

            // --- INITIALIZATION ---
            function checkPersistedLogin() {
                const lastUser = localStorage.getItem('spinEarnCurrentUser');
                if (lastUser) {
                    const foundUser = users.find(u => u.username === lastUser);
                    if (foundUser) {
                        isLoggedIn = true;
                        currentUser = foundUser;
                    } else {
                        localStorage.removeItem('spinEarnCurrentUser'); // Clean up if user not found
                    }
                }
            }

            function initApp() {
                checkPersistedLogin();
                updateUIForLoginState(); // This also calls handleHashChange which shows the initial page

                // Event Listeners
                if(menuToggleBtn) menuToggleBtn.addEventListener('click', toggleSidebar);
                if(sidebarOverlay) sidebarOverlay.addEventListener('click', closeSidebar);
                if (sidebarCloseBtn) sidebarCloseBtn.addEventListener('click', closeSidebar); 
                
                if(sidebarNavLinksContainer) sidebarNavLinksContainer.addEventListener('click', handleNavigation);
                
                const mainContentArea = document.querySelector('.main-content');
                if (mainContentArea) mainContentArea.addEventListener('click', handleNavigation); 
                
                window.addEventListener('hashchange', handleHashChange);
            }

            initApp();
        });
    </script>
</body>
</html>

API.PHP File

<?php
// api/api.php - Combined version with config and DB logic

// --- ERROR REPORTING (FOR DEVELOPMENT) ---
// In production, set display_errors to 0 and rely on error_log.
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// --- DATABASE CREDENTIALS ---
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'u124970017_spinandearn');
define('DB_PASSWORD', 'I5yZWQP/;'); // REMEMBER TO CHANGE THIS FOR PRODUCTION
define('DB_NAME', 'u124970017_spinandearn');

// --- SESSION MANAGEMENT ---
if (session_status() == PHP_SESSION_NONE) {
    if (!headers_sent()) { // Check if headers already sent
        session_start();
    } else {
        error_log("API: Session could not be started because headers were already sent.");
        // Potentially output an error, but this is tricky if headers are sent
    }
}

// --- GLOBAL HELPER FUNCTIONS ---

/**
 * Sends a JSON response to the client.
 * @param mixed $data Data to be JSON encoded.
 * @param int $statusCode HTTP status code.
 */
function json_response($data, $statusCode = 200) {
    if (!headers_sent()) {
        http_response_code($statusCode);
        header('Content-Type: application/json; charset=utf-8');
    } else {
        error_log("API: json_response called but headers already sent. Status: {$statusCode}, Data: " . json_encode($data));
    }
    echo json_encode($data);
    exit;
}

/**
 * Adds an activity log entry to the database.
 * @param PDO $pdo The PDO database connection object.
 * @param int $user_id The ID of the user performing the activity.
 * @param string $icon_class Font Awesome icon class.
 * @param string $description Description of the activity.
 * @param int $points_change Points change associated with the activity.
 * @param string $activity_type Type of activity (e.g., 'spin_win', 'login').
 * @return bool True on success, false on failure.
 */
function add_activity_to_db($pdo, $user_id, $icon_class, $description, $points_change = 0, $activity_type = "general") {
    if (!$pdo) {
        error_log("add_activity_to_db: PDO object is null for user_id {$user_id}.");
        return false;
    }
    $sql = "INSERT INTO activity_log (user_id, icon_class, description, points_change, activity_type)
            VALUES (:user_id, :icon_class, :description, :points_change, :activity_type)";
    try {
        $stmt = $pdo->prepare($sql);
        $executed = $stmt->execute([
            ':user_id' => $user_id,
            ':icon_class' => $icon_class,
            ':description' => $description,
            ':points_change' => $points_change,
            ':activity_type' => $activity_type
        ]);
        if (!$executed) {
            error_log("add_activity_to_db execute failed. ErrorInfo: " . print_r($stmt->errorInfo(), true));
            return false;
        }
        return true;
    } catch (PDOException $e) {
        error_log("PDOException in add_activity_to_db for user_id {$user_id}: " . $e->getMessage() . " | SQL: " . $sql . " | Params: " . print_r(get_defined_vars(), true));
        return false;
    }
}

/**
 * Establishes and returns a PDO database connection.
 * This function uses a static variable to ensure the connection is made only once per request.
 * @return PDO|null PDO connection object on success, exits on critical failure.
 */
function get_db_connection() {
    static $pdo_connection = null;

    if ($pdo_connection === null) {
        $dsn = "mysql:host=" . DB_SERVER . ";dbname=" . DB_NAME . ";charset=utf8mb4";
        $options = [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
        ];
        try {
            $pdo_connection = new PDO($dsn, DB_USERNAME, DB_PASSWORD, $options);
            error_log("API: Database connection established successfully."); // Log success
        } catch (PDOException $e) {
            error_log("FATAL: Database Connection Error in get_db_connection(): " . $e->getMessage());
            json_response(['error' => 'Critical server error: Unable to connect to the database. Check server logs.'], 503);
        }
    }
    return $pdo_connection;
}

// --- API ROUTING LOGIC ---

$action = $_GET['action'] ?? '';
$request_method = $_SERVER['REQUEST_METHOD'];
$request_data = [];

if ($request_method === 'POST' || $request_method === 'PUT') {
    $json_input = file_get_contents('php://input');
    if ($json_input) {
        $request_data = json_decode($json_input, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            error_log("API: Invalid JSON received: " . json_last_error_msg() . " | Input: " . $json_input);
            json_response(['error' => 'Invalid JSON payload.'], 400);
        }
    }
}

// If action was in POST JSON body for some reason (though usually in GET for this setup)
if (empty($action) && isset($request_data['action'])) {
    $action = $request_data['action'];
}

error_log("API: Received action '{$action}' via {$request_method}"); // Log received action

switch ($action) {
    case 'register':            handle_register($request_data); break;
    case 'login':               handle_login($request_data); break;
    case 'logout':              handle_logout(); break;
    case 'get_user_data':       handle_get_user_data(); break;
    case 'spin_wheel':          handle_spin_wheel($request_data); break; // Pass data if needed by POST
    case 'play_slot_machine':   handle_play_slot_machine($request_data); break;
    case 'scratch_card':        handle_scratch_card($request_data); break;
    case 'claim_daily_bonus':   handle_claim_daily_bonus($request_data); break;
    case 'redeem_points':       handle_redeem_points($request_data); break;
    case 'get_history':         handle_get_history(); break;
    default:
        error_log("API: Invalid action '{$action}' requested.");
        json_response(['error' => 'Invalid API action specified: ' . htmlspecialchars($action)], 400);
}

// --- ACTION HANDLER FUNCTIONS ---

function handle_register($data) { // Accept $data as parameter
    $pdo = get_db_connection();

    if (empty($data['username']) || empty($data['password']) || empty($data['confirmPassword'])) {
        json_response(['error' => 'All registration fields are required.'], 400);
    }
    $username = trim($data['username']);
    $password = $data['password'];
    $confirmPassword = $data['confirmPassword'];

    if (strlen($username) < 3 || !preg_match('/^[a-zA-Z0-9_]+$/', $username)) {
        json_response(['error' => 'Invalid username: Min 3 chars, letters, numbers, underscores only.'], 400);
    }
    if (strlen($password) < 6) {
        json_response(['error' => 'Password must be at least 6 characters.'], 400);
    }
    if ($password !== $confirmPassword) {
        json_response(['error' => 'Passwords do not match.'], 400);
    }

    $sql_check = "SELECT id FROM users WHERE username = :username";
    $sql_insert = "INSERT INTO users (username, password_hash, role, points, spins_left, slot_tokens, scratch_cards_left, referral_code)
                   VALUES (:username, :password_hash, :role, :points, :spins_left, :slot_tokens, :scratch_cards_left, :referral_code)";
    try {
        $stmt_check_user = $pdo->prepare($sql_check);
        $stmt_check_user->execute([':username' => $username]);
        if ($stmt_check_user->fetch()) {
            json_response(['error' => 'Username already exists.'], 409);
        }

        $password_hash = password_hash($password, PASSWORD_DEFAULT);
        $referral_code_base = "SPIN" . strtoupper(substr(preg_replace("/[^A-Za-z0-9]/", '', $username), 0, 5));
        $is_unique_code = false; $referral_code = ''; $attempts = 0;
        while (!$is_unique_code && $attempts < 10) {
            $referral_code = $referral_code_base . random_int(100, 9999);
            $stmt_ref_check = $pdo->prepare("SELECT id FROM users WHERE referral_code = :referral_code");
            $stmt_ref_check->execute([':referral_code' => $referral_code]);
            if (!$stmt_ref_check->fetch()) { $is_unique_code = true; }
            $attempts++;
        }
        if (!$is_unique_code) { $referral_code = "SPIN" . bin2hex(random_bytes(4)); }

        $default_points = 1000; $default_spins = 10; $default_tokens = 5; $default_scratch_cards = 3; $default_role = 'user';

        $stmt = $pdo->prepare($sql_insert);
        $executed = $stmt->execute([
            ':username' => $username, ':password_hash' => $password_hash, ':role' => $default_role,
            ':points' => $default_points, ':spins_left' => $default_spins, ':slot_tokens' => $default_tokens,
            ':scratch_cards_left' => $default_scratch_cards, ':referral_code' => $referral_code
        ]);

        if ($executed) {
            $user_id = $pdo->lastInsertId();
            if ($user_id) {
                error_log("API Register: User '{$username}' (ID: {$user_id}) created successfully.");
                $welcome_desc = "Welcome bonus! +{$default_points} pts, {$default_spins} spins, {$default_tokens} tokens, {$default_scratch_cards} cards.";
                add_activity_to_db($pdo, $user_id, 'fas fa-gift', $welcome_desc, $default_points, 'bonus');
                json_response(['success' => 'Registration successful! Please login.']);
            } else {
                error_log("API Register: User '{$username}' INSERT executed but lastInsertId is not set. RowCount: " . $stmt->rowCount());
                json_response(['error' => 'Registration failed: Could not confirm user creation.'], 500);
            }
        } else {
            error_log("API Register: User '{$username}' INSERT failed. ErrorInfo: " . print_r($stmt->errorInfo(), true));
            json_response(['error' => 'Registration failed: Database operation error.'], 500);
        }
    } catch (PDOException $e) {
        error_log("PDOException in handle_register for user '{$username}': " . $e->getMessage() . " | SQL attempted: " . ($stmt_check_user->queryString ?? $sql_insert ?? 'N/A'));
        json_response(['error' => 'Registration failed due to a server database error. Check logs.'], 500);
    }
}

function handle_login($data) { // Accept $data
    $pdo = get_db_connection();

    if (empty($data['username']) || empty($data['password'])) {
        json_response(['error' => 'Username and password are required.'], 400);
    }
    $username = $data['username']; $password = $data['password'];

    try {
        $stmt = $pdo->prepare("SELECT id, username, password_hash, role FROM users WHERE username = :username");
        $stmt->execute([':username' => $username]);
        $user = $stmt->fetch();

        if ($user && password_verify($password, $user['password_hash'])) {
            if (!headers_sent()) { // Check before regenerating ID
                 session_regenerate_id(true);
            } else {
                error_log("API Login: Could not regenerate session ID for user '{$username}' because headers were already sent.");
            }
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $user['username'];
            $_SESSION['role'] = $user['role'];
            error_log("API Login: User '{$username}' (ID: {$user['id']}, Role: {$user['role']}) logged in. Session ID: " . session_id());


            try {
                $stmt_update_login = $pdo->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = :id");
                $stmt_update_login->execute([':id' => $user['id']]);
            } catch (PDOException $e_login_update) {
                error_log("API Login: Failed to update last_login for user_id {$user['id']}: " . $e_login_update->getMessage());
            }
            json_response(['success' => 'Login successful.', 'username' => $user['username'], 'role' => $user['role']]);
        } else {
            error_log("API Login: Invalid login attempt for username '{$username}'.");
            json_response(['error' => 'Invalid username or password.'], 401);
        }
    } catch (PDOException $e) {
        error_log("PDOException in handle_login for username '{$username}': " . $e->getMessage());
        json_response(['error' => 'Login failed due to a server database error. Check logs.'], 500);
    }
}

// ... (handle_logout, require_auth, handle_get_user_data remain largely the same, just ensure they call get_db_connection())

// Ensure all game handlers also get $pdo via get_db_connection()
// and have more robust error logging and checking of $stmt->rowCount()

// Example of an updated game handler:
function handle_spin_wheel($request_data) { // Pass $request_data even if not used by this POST
    $user_id = require_auth();
    $pdo = get_db_connection();

    $spinWheelSegments = [
        ["label" => "50 Pts", "value" => 50, "color" => "#FFC107", "type" => "points"],
        ["label" => "Try Again", "value" => 0, "color" => "#6c757d", "type" => "points"],
        ["label" => "100 Pts", "value" => 100, "color" => "#28a745", "type" => "points"],
        ["label" => "1 Spin", "value" => 1, "color" => "#17a2b8", "type" => "extra_spin"],
        ["label" => "20 Pts", "value" => 20, "color" => "#fd7e14", "type" => "points"],
        ["label" => "No Win", "value" => 0, "color" => "#6c757d", "type" => "points"],
        ["label" => "250 Pts", "value" => 250, "color" => "#dc3545", "type" => "points"],
        ["label" => "5 Spins", "value" => 5, "color" => "#6f42c1", "type" => "extra_spin"]
   ];
   $sql_user_select = "SELECT spins_left, points FROM users WHERE id = :user_id FOR UPDATE";
   $sql_user_update = "UPDATE users SET points = :points, spins_left = :spins_left WHERE id = :user_id";

   try {
       $pdo->beginTransaction();
       $stmt_user = $pdo->prepare($sql_user_select);
       $stmt_user->execute([':user_id' => $user_id]);
       $user = $stmt_user->fetch();

       if (!$user) { $pdo->rollBack(); error_log("API Spin: User not found for ID {$user_id}"); json_response(['error' => 'User not found.'], 404); }
       if ($user['spins_left'] <= 0) { $pdo->rollBack(); error_log("API Spin: No spins left for user ID {$user_id}"); json_response(['error' => 'No spins left.', 'spins_left' => 0], 400); }

       $new_spins_left = $user['spins_left'] - 1;
       $current_points = $user['points'];
       $randomIndex = array_rand($spinWheelSegments);
       $winningSegment = $spinWheelSegments[$randomIndex];
       $points_won_this_spin = 0; $extra_spins_won_this_spin = 0; $outcome_text = "";
       $activity_icon = 'fas fa-dharmachakra'; $activity_type = 'spin_general';

       switch ($winningSegment['type']) {
           // ... (switch logic as before) ...
           case 'points':
               $points_won_this_spin = (int)$winningSegment['value'];
               if ($points_won_this_spin > 0) {
                   $current_points += $points_won_this_spin;
                   $outcome_text = "Congratulations! You won {$points_won_this_spin} points!";
                   $activity_type = 'spin_win';
               } else {
                   $outcome_text = "{$winningSegment['label']}! Better luck next time."; $activity_type = 'spin_loss';
               }
               break;
           case 'extra_spin':
               $extra_spins_won_this_spin = (int)$winningSegment['value'];
               $new_spins_left += $extra_spins_won_this_spin;
               $outcome_text = "Awesome! You won {$extra_spins_won_this_spin} extra spin" . ($extra_spins_won_this_spin > 1 ? 's' : '') . "!";
               $activity_icon = 'fas fa-redo'; $activity_type = 'spin_bonus';
               break;
       }

       $stmt_update = $pdo->prepare($sql_user_update);
       $update_success = $stmt_update->execute([':points' => $current_points, ':spins_left' => $new_spins_left, ':user_id' => $user_id]);
       $rows_affected = $stmt_update->rowCount();

       if($update_success && $rows_affected > 0){
           error_log("API Spin: User ID {$user_id} updated. Points: {$current_points}, Spins: {$new_spins_left}. Rows: {$rows_affected}");
           add_activity_to_db($pdo, $user_id, $activity_icon, $outcome_text, $points_won_this_spin, $activity_type);
           $pdo->commit();
           json_response([
               'success' => true, 'outcome_text' => $outcome_text, 'winning_segment_index' => $randomIndex,
               'winning_segment_label' => $winningSegment['label'], 'points_won' => $points_won_this_spin,
               'extra_spins_won' => $extra_spins_won_this_spin, 'new_points_balance' => $current_points,
               'new_spins_left' => $new_spins_left
           ]);
       } else {
            $pdo->rollBack();
            error_log("API Spin: User update FAILED or affected 0 rows for user_id: {$user_id}. UpdateSuccess: " . ($update_success ? 'true' : 'false') . ", RowCount: {$rows_affected}. SQL: {$sql_user_update}");
            json_response(['error' => 'Spin wheel action failed to update user data. Check server logs.'], 500);
       }

   } catch (PDOException $e) {
       if ($pdo->inTransaction()) { $pdo->rollBack(); }
       error_log("PDOException in handle_spin_wheel for user_id {$user_id}: " . $e->getMessage());
       json_response(['error' => 'Spin wheel action failed due to a server database error. Check logs.'], 500);
   }
}

// --- Implement ALL other handler functions (play_slot_machine, scratch_card, etc.)
// --- in a similar way, calling get_db_connection() and adding robust error logging
// --- and checking rowCount() for UPDATE/INSERT/DELETE.

// ... (The rest of your handler functions from the previous "complete api.php" response,
//      but ensure each one starts with `$pdo = get_db_connection();`
//      and has similar detailed error logging and rowCount checks)

// It's too long to repeat every single handler here, but apply the pattern shown in
// handle_register, handle_login, and the more detailed handle_spin_wheel.

// Make sure the constants for Daily Bonus are defined if used in its handler:
// if (!defined('DAILY_BONUS_POINTS_AMOUNT')) define('DAILY_BONUS_POINTS_AMOUNT', 100);
// if (!defined('DAILY_BONUS_SPINS_AMOUNT')) define('DAILY_BONUS_SPINS_AMOUNT', 5);

// (Paste the rest of your handle_play_slot_machine, handle_scratch_card,
// handle_claim_daily_bonus, handle_redeem_points, handle_get_history functions here,
// ensuring they call `$pdo = get_db_connection();` at the start of each)


// --- Placeholder for other handlers to be fully implemented with logging and rowCount checks ---
function handle_play_slot_machine($request_data) {
    $user_id = require_auth(); $pdo = get_db_connection();
    error_log("API: play_slot_machine called by user_id {$user_id}");
    // Full implementation with try/catch, beginTransaction, commit/rollback, rowCount checks
    // For brevity, a placeholder response
    json_response(['error' => 'play_slot_machine not fully implemented with new error checks yet.'], 501);
}
function handle_scratch_card($request_data) {
    $user_id = require_auth(); $pdo = get_db_connection();
    error_log("API: scratch_card called by user_id {$user_id}");
    // Full implementation ...
    json_response(['error' => 'scratch_card not fully implemented with new error checks yet.'], 501);
}
function handle_claim_daily_bonus($request_data) {
    $user_id = require_auth(); $pdo = get_db_connection();
    error_log("API: claim_daily_bonus called by user_id {$user_id}");
     // Using constants defined at the top or define them here if not already
    if (!defined('DAILY_BONUS_POINTS_AMOUNT')) define('DAILY_BONUS_POINTS_AMOUNT', 100);
    if (!defined('DAILY_BONUS_SPINS_AMOUNT')) define('DAILY_BONUS_SPINS_AMOUNT', 5);
    // Full implementation ...
    json_response(['error' => 'claim_daily_bonus not fully implemented with new error checks yet.'], 501);
}
function handle_redeem_points($request_data) {
    $user_id = require_auth(); $pdo = get_db_connection();
    error_log("API: redeem_points called by user_id {$user_id} with data: " . print_r($request_data, true));
    // Full implementation ...
    json_response(['error' => 'redeem_points not fully implemented with new error checks yet.'], 501);
}
function handle_get_history() {
    $user_id = require_auth(); $pdo = get_db_connection();
    error_log("API: get_history called by user_id {$user_id}");
    // Full implementation ...
    json_response(['error' => 'get_history not fully implemented with new error checks yet.'], 501);
}

?>

अब आपको पहले तो Google Studio पर जाना है और उसे यह prompt देना होगा, फिर 2 से 3 मिनट का समय लेकर वह आपके लिए एक बढ़िया earning website बना देगा।

Leave a Comment

Your email address will not be published. Required fields are marked *